| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319 |
- import { deleteApi, get, postJ, putJ } from "@/utils/request";
- import { processInstanceCreateAPI, processInstancePage } from "@/api/wt/index.js";
- import { dateOnly, getMyResignIds, rememberResignId } from "@/utils/hr/storage";
- import Vue from "vue";
- // ============ 枚举 ============
- // M02 后端枚举 → 中文
- export const RESIGNATION_TYPE_LABELS = {
- VOLUNTARY: "主动离职",
- NEGOTIATED: "协商解除",
- PROBATION_FAILED: "试用不合格",
- PERFORMANCE_FAILED: "绩效不合格",
- TERMINATED: "辞退",
- DISMISSED: "辞退",
- CONTRACT_EXPIRED: "合同到期",
- RETIRE: "退休",
- RETIREMENT: "退休",
- OTHER: "其他",
- };
- export const RESIGN_APPROVAL_STATUS_LABELS = {
- DRAFT: "草稿",
- PENDING: "审批中",
- APPROVING: "审批中",
- APPROVED: "已通过",
- REJECTED: "已驳回",
- WITHDRAWN: "已撤回",
- CANCELLED: "已取消",
- };
- export const HANDOVER_STATUS_LABELS = {
- NOT_STARTED: "未开始",
- PENDING: "待交接",
- IN_PROGRESS: "交接中",
- PROCESSING: "交接中",
- COMPLETED: "已完成",
- FORCE_COMPLETED: "强制办结",
- };
- export const RESIGN_EFFECTIVE_STATUS_LABELS = {
- PENDING: "待生效",
- SCHEDULED: "已排期",
- EFFECTIVE: "已生效",
- CANCELLED: "已取消",
- };
- export const ARCHIVE_STATUS_LABELS = {
- NOT_ARCHIVED: "未归档",
- PENDING: "待归档",
- ARCHIVED: "已归档",
- };
- export const HANDOVER_CATEGORY_LABELS = {
- WORK: "工作交接",
- ASSET: "资产交接",
- FINANCE: "财务交接",
- HR: "HR交接",
- };
- /** 交接方式:ONLINE-线上文档、OFFLINE-线下附件(对齐 PC 端 resignation.js) */
- export const HANDOVER_TYPE_LABELS = {
- ONLINE: "线上文档",
- OFFLINE: "线下附件",
- };
- export const HANDOVER_TYPE_OPTIONS = [
- { value: "ONLINE", label: "线上文档", desc: "由服务端创建线上交接文档,无需上传附件" },
- { value: "OFFLINE", label: "线下附件", desc: "必须上传交接附件(可多选)" },
- ];
- /** 交接事项分类选项 */
- export const HANDOVER_ITEM_CATEGORY_OPTIONS = [
- { value: "WORK", label: "工作交接" },
- { value: "ASSET", label: "资产交接" },
- { value: "FINANCE", label: "财务交接" },
- { value: "HR", label: "HR交接" },
- ];
- /** 交接事项状态 */
- export const HANDOVER_ITEM_STATUS_LABELS = {
- PENDING: "待交接",
- PROCESSING: "交接中",
- IN_PROGRESS: "交接中",
- COMPLETED: "已完成",
- DONE: "已完成",
- };
- export const RESIGN_DISPLAY_STATUS = {
- draft: { label: "草稿", class: "status-pending" },
- pending: { label: "审批中", class: "status-pending" },
- rejected: { label: "已驳回", class: "status-rejected" },
- withdrawn: { label: "已撤回", class: "status-rejected" },
- cancelled: { label: "已取消", class: "status-rejected" },
- overdue: { label: "已逾期", class: "status-rejected" },
- approved: { label: "已通过", class: "status-approved" },
- handover: { label: "交接中", class: "status-processing" },
- completed: { label: "已完成", class: "status-approved" },
- };
- export const RESIGN_FLOW_STAGES = [
- { key: "submit", label: "申请提交", desc: "员工提交离职申请并填写预计离职日" },
- { key: "approval", label: "离职审批", desc: "直属主管、部门负责人与 HRBP 审批" },
- { key: "handover", label: "工作交接", desc: "交接人确认工作、资产与文档移交" },
- { key: "archive", label: "归档生效", desc: "交接完成后归档,劳动关系解除" },
- ];
- export const RESIGN_STATUS_FILTERS = [
- { value: "all", label: "全部状态" },
- { value: "pending", label: "审批中" },
- { value: "handover", label: "交接中" },
- { value: "completed", label: "已完成" },
- { value: "rejected", label: "已驳回" },
- { value: "withdrawn", label: "已撤回" },
- ];
- /**
- * 创建态默认事项:与 PC 端 HandoverGenerateDialog 的 defaultItems() 完全一致 ——
- * 按四个交接分类各预置一行,**只自动填充「交接分类」,事项名称 / 说明 / 接收人一律留空**,
- * 由申请人自行填写(PC 提示「共 4 项;分类与名称必填」)。
- */
- export const DEFAULT_HANDOVER_ITEMS = HANDOVER_ITEM_CATEGORY_OPTIONS.map((option) => ({
- category: option.value,
- name: "",
- detail: "",
- }));
- // ============ 工具函数 ============
- function labelOf(map, raw, fallback = "") {
- const text = String(raw || "").trim();
- if (!text) return fallback;
- return map[text] || map[text.toUpperCase()] || text;
- }
- function asList(data) {
- if (Array.isArray(data)) return data;
- if (data && Array.isArray(data.list)) return data.list;
- if (data && Array.isArray(data.records)) return data.records;
- if (data && typeof data === "object" && data.id != null) return [data];
- return [];
- }
- function isDoneItemStatus(status) {
- return ["COMPLETED", "DONE", "FINISHED"].indexOf(String(status || "").toUpperCase()) >= 0;
- }
- function approvalCode(raw = {}) {
- return String(raw.approvalStatus || raw.statusCode || "").toUpperCase();
- }
- // ============ 状态推导与操作可用性 ============
- export function deriveResignationDisplayStatus(raw = {}) {
- const archive = String(raw.archiveStatus || "").toUpperCase();
- const effective = String(raw.effectiveStatus || "").toUpperCase();
- const handover = String(raw.handoverStatus || "").toUpperCase();
- const approval = String(raw.approvalStatus || "").toUpperCase();
- if (approval === "CANCELLED") return "cancelled";
- if (archive === "ARCHIVED" || effective === "EFFECTIVE") return "completed";
- if (approval === "APPROVED") {
- if (!handover || ["IN_PROGRESS", "PROCESSING", "PENDING", "NOT_STARTED"].indexOf(handover) >= 0) {
- return "handover";
- }
- return "approved";
- }
- if (approval === "REJECTED") return "rejected";
- if (approval === "PENDING" || approval === "APPROVING") return "pending";
- if (approval === "WITHDRAWN") return "withdrawn";
- return "draft";
- }
- export function canSubmitResignation(row = {}) {
- if (!row.id) return false;
- const code = approvalCode(row);
- return code === "DRAFT" || code === "REJECTED" || code === "";
- }
- export function canWithdrawResignation(row = {}) {
- const code = approvalCode(row);
- return code === "PENDING" || code === "APPROVING" || row.displayStatus === "pending";
- }
- export function canCancelResignation(row = {}) {
- if (!row.id) return false;
- if (approvalCode(row) === "CANCELLED") return false;
- return row.displayStatus !== "completed";
- }
- export function canGenerateHandover(row = {}) {
- if (!row.id || row.handover) return false;
- return approvalCode(row) === "APPROVED";
- }
- export function canArchiveResignation(row = {}) {
- if (!row.id) return false;
- if (!row.handover || row.handover.status !== "completed") return false;
- return String(row.archiveStatus || "").toUpperCase() !== "ARCHIVED";
- }
- export function canForceCompleteResignation(row = {}) {
- if (!row.id) return false;
- return String(row.archiveStatus || "").toUpperCase() !== "ARCHIVED" && row.displayStatus !== "completed";
- }
- export function matchDisplayStatus(row = {}, filter = "") {
- if (!filter || filter === "all") return true;
- if (filter === "rejected") return ["rejected", "cancelled"].indexOf(row.displayStatus) >= 0;
- if (filter === "withdrawn") return ["withdrawn", "cancelled"].indexOf(row.displayStatus) >= 0;
- return row.displayStatus === filter;
- }
- // ============ VO → 页面形状映射 ============
- export function adaptHandoverItem(raw = {}) {
- const done = isDoneItemStatus(raw.itemStatus || raw.status);
- const category = raw.itemCategory || raw.category || "WORK";
- const statusCode = String(raw.itemStatus || raw.status || "").toUpperCase();
- return {
- id: raw.id,
- name: raw.itemName || raw.name || "",
- itemName: raw.itemName || raw.name || "",
- detail: raw.itemContent || raw.detail || raw.completionRemark || "",
- itemContent: raw.itemContent || raw.detail || "",
- category,
- itemCategory: category,
- categoryLabel: labelOf(HANDOVER_CATEGORY_LABELS, category),
- receiverUserId: raw.receiverUserId,
- receiverName: raw.receiverName || raw.receiverUserName || "",
- status: raw.itemStatus || raw.status || "",
- itemStatus: raw.itemStatus || raw.status || "",
- // 待交接 / 交接中 / 已完成
- itemStatusLabel: labelOf(HANDOVER_ITEM_STATUS_LABELS, statusCode, done ? "已完成" : "待交接"),
- completionRemark: raw.completionRemark || "",
- done,
- doneAt: dateOnly(raw.completeTime || raw.doneAt),
- };
- }
- /** 资产归还清单行(详情 / 编辑态展示用) */
- export function adaptAssetReturn(raw = {}) {
- const statusCode = String(raw.returnStatus || "").toUpperCase();
- return {
- id: raw.id,
- handoverId: raw.handoverId,
- itemName: raw.itemName || raw.assetName || raw.returnName || "",
- assetCode: raw.assetCode || "",
- returnName: raw.returnName || raw.itemName || raw.assetName || "",
- returnStatus: statusCode || "PENDING",
- returnStatusLabel: statusCode === "RETURNED" ? "已归还" : "未归还",
- returned: statusCode === "RETURNED",
- returnRemark: raw.returnRemark || "",
- returnedTime: raw.returnedTime || "",
- };
- }
- export function adaptHandover(raw = {}) {
- if (!raw || typeof raw !== "object") return null;
- const items = ((raw.items || [])).map(adaptHandoverItem);
- const assetReturns = ((raw.assetReturns || raw.assetReturnList || [])).map(adaptAssetReturn);
- const doneCount = items.filter((item) => item.done).length;
- const itemsProgress = items.length ? Math.round((doneCount / items.length) * 100) : 0;
- const code = String(raw.handoverStatus || "").toUpperCase();
- const completed = ["COMPLETED", "FORCE_COMPLETED"].indexOf(code) >= 0 || (items.length > 0 && doneCount === items.length);
- const progress = completed ? 100 : itemsProgress;
- const receiverItem = items.find((item) => item.receiverName);
- // 附件:后端返回逗号分隔字符串
- const attachmentIdText = raw.attachmentIds ? String(raw.attachmentIds) : "";
- const attachmentIdList = attachmentIdText
- ? attachmentIdText.split(",").map((text) => text.trim()).filter(Boolean)
- : [];
- return {
- id: raw.id,
- resignationId: raw.resignationId,
- resignationNo: raw.resignationNo || "",
- documentCode: raw.documentCode || "",
- userId: raw.userId,
- employee: raw.employeeName || raw.userName || "",
- employeeNo: raw.employeeNo || "",
- department: raw.departmentName || raw.deptName || "",
- deptId: raw.deptId,
- deptName: raw.deptName || raw.departmentName || "",
- position: raw.positionName || "",
- person: raw.handoverPerson || raw.handoverUserName || (receiverItem ? receiverItem.receiverName : "") || "",
- handoverUserId: raw.handoverUserId,
- handoverUserName: raw.handoverUserName || "",
- handoverType: String(raw.handoverType || "").toUpperCase(),
- handoverTypeLabel: labelOf(HANDOVER_TYPE_LABELS, raw.handoverType),
- attachmentIds: attachmentIdText,
- attachmentIdList,
- handoverDate: dateOnly(raw.handoverDate),
- deadline: dateOnly(raw.deadline || raw.handoverDate),
- remark: raw.remark || "",
- status: completed ? "completed" : progress > 0 ? "processing" : "not_started",
- statusLabel: completed ? "已完成" : progress > 0 ? "交接中" : "待交接",
- statusCode: raw.handoverStatus || "",
- statusCodeLabel: labelOf(HANDOVER_STATUS_LABELS, raw.handoverStatus),
- approvalStatus: raw.approvalStatus || "",
- approvalStatusLabel: labelOf(RESIGN_APPROVAL_STATUS_LABELS, raw.approvalStatus),
- processInstanceId: raw.processInstanceId || raw.approvalInstanceId || "",
- progress,
- items,
- assetReturns,
- createTime: raw.createTime || "",
- updated: dateOnly(raw.handoverDate || raw.createTime),
- };
- }
- /**
- * 交接单是否可编辑(对齐 PC 端 resignation.js 的 canEditHandover):
- * 仅「审批草稿(DRAFT / 空)+ 交接未办结」可修改;
- * 审批中 / 已通过 / 已驳回 / 已撤回 / 已取消,或已完成 / 强制办结均不可改。
- */
- export function canEditHandover(row = {}) {
- if (!row || row.id === undefined || row.id === null) return false;
- const approval = String(row.approvalStatus || "").toUpperCase();
- if (approval && approval !== "DRAFT") return false;
- const code = String(row.statusCode || "").toUpperCase();
- return ["COMPLETED", "FORCE_COMPLETED"].indexOf(code) < 0;
- }
- function stageProgressOf(stageKey, stages, handover) {
- const index = stages.findIndex((stage) => stage.key === stageKey);
- if (index < 0) return 0;
- if (stageKey === "handover" && handover) {
- return Math.min(99, 50 + Math.round((handover.progress || 0) / 2));
- }
- return Math.round(((index + 1) / stages.length) * 100);
- }
- function progressTipOf(displayStatus, handover) {
- switch (displayStatus) {
- case "draft":
- return "草稿未提交,请提交审批后进入离职流程。";
- case "pending":
- return "离职审批中,请等待审批结果。";
- case "rejected":
- return "审批已驳回,可修改后重新提交。";
- case "withdrawn":
- return "本次离职申请已撤回。";
- case "cancelled":
- return "本次离职申请已取消。";
- case "approved":
- return "审批已通过,待生成离职交接单。";
- case "handover":
- return handover ? `交接进行中,事项已完成 ${handover.progress}%。` : "审批已通过,待生成离职交接单。";
- case "completed":
- return "离职流程已全部完成。";
- case "overdue":
- return "已超过最后工作日,请尽快推进交接与结算。";
- default:
- return "";
- }
- }
- export function adaptResignation(vo = {}, extras = {}) {
- if (!vo || typeof vo !== "object") return null;
- const displayStatus = deriveResignationDisplayStatus(vo);
- const handover = vo.handover ? adaptHandover(vo.handover) : null;
- const statusMeta = RESIGN_DISPLAY_STATUS[displayStatus] || RESIGN_DISPLAY_STATUS.pending;
- const stages = RESIGN_FLOW_STAGES;
- const stageKey =
- displayStatus === "completed"
- ? "archive"
- : displayStatus === "handover" || displayStatus === "approved"
- ? "handover"
- : displayStatus === "pending"
- ? "approval"
- : "submit";
- const name = extras.name || vo.employeeName || vo.userName || "";
- return {
- id: vo.id,
- source: "server",
- resignationNo: vo.resignationNo || "",
- userId: vo.userId,
- name,
- avatar: String(name).charAt(0),
- employeeNo: extras.employeeNo || vo.employeeNo || "",
- department: extras.department || vo.departmentName || vo.deptName || "",
- position: extras.position || vo.positionName || "",
- entryDate: dateOnly(extras.hireDate || vo.hireDate),
- expectedLeaveDate: dateOnly(vo.plannedLastWorkDate || vo.effectiveDate),
- lastWorkDate: dateOnly(vo.effectiveDate),
- applyDate: dateOnly(vo.applyDate || vo.createTime),
- effectiveDate: dateOnly(vo.effectiveDate),
- type: labelOf(RESIGNATION_TYPE_LABELS, vo.resignationType),
- typeCode: vo.resignationType || "",
- reason: vo.reason || "",
- userComment: vo.userComment || "",
- hrComment: vo.hrComment || "",
- approvalStatus: vo.approvalStatus || "",
- approvalStatusLabel: labelOf(RESIGN_APPROVAL_STATUS_LABELS, vo.approvalStatus),
- handoverStatus: vo.handoverStatus || "",
- handoverStatusLabel: labelOf(HANDOVER_STATUS_LABELS, vo.handoverStatus),
- effectiveStatus: vo.effectiveStatus || "",
- effectiveStatusLabel: labelOf(RESIGN_EFFECTIVE_STATUS_LABELS, vo.effectiveStatus),
- archiveStatus: vo.archiveStatus || "",
- archiveStatusLabel: labelOf(ARCHIVE_STATUS_LABELS, vo.archiveStatus),
- displayStatus,
- statusLabel: statusMeta.label,
- statusClass: statusMeta.class,
- stageKey,
- stages,
- progress: displayStatus === "completed" ? 100 : stageProgressOf(stageKey, stages, handover),
- progressTip: progressTipOf(displayStatus, handover),
- handover,
- onlineSign: Boolean(vo.approvalDocumentFileId || vo.onlineDocumentId),
- createTime: vo.createTime || "",
- // ===== PC「新建离职申请」表单回填所需的组织 / 岗位快照字段 =====
- phone: extras.phone || vo.phone || vo.mobile || "",
- groupId: pickBig(vo.groupId, vo.legalId, extras.groupId, extras.legalId),
- company: extras.company || vo.company || vo.groupName || vo.legalName || "",
- deptId: pickBig(vo.deptId, extras.deptId),
- deptName: extras.deptName || vo.deptName || vo.departmentName || "",
- positionId: pickBig(vo.positionId, extras.positionId),
- positionName: extras.position || vo.positionName || "",
- positionSequenceId: pickBig(vo.positionSequenceId, extras.positionSequenceId),
- positionSequence: extras.positionSequence || vo.positionSequence || "",
- positionLevelId: pickBig(vo.positionLevelId, extras.positionLevelId),
- positionLevel: extras.positionLevel || vo.positionLevel || "",
- positionType: extras.positionType || vo.positionType || "",
- positionTypeName: extras.positionTypeName || vo.positionTypeName || "",
- employmentPeriodId: pickBig(vo.employmentPeriodId, extras.employmentPeriodId),
- handlingMode:
- String(vo.handlingMode || vo.resignHandlingMode || extras.handlingMode || "")
- .trim()
- .toUpperCase() === "ONLINE"
- ? "ONLINE"
- : normalizeResignHandlingMode(vo),
- approvalDocumentFileId: firstFileIdOf([
- vo.approvalDocumentFileId,
- vo.onlineDocumentId,
- extras.approvalDocumentFileId,
- ]),
- raw: vo,
- };
- }
- /** 取第一个非空 ID(兼容大整数,统一转字符串) */
- function pickBig() {
- for (let i = 0; i < arguments.length; i += 1) {
- const value = arguments[i];
- if (value === undefined || value === null || value === "") continue;
- return String(value);
- }
- return "";
- }
- // ============ 员工信息补全 ============
- const employeeCache = {};
- async function fetchEmployeeBrief(userId) {
- if (userId === undefined || userId === null || userId === "") return null;
- const key = String(userId);
- if (key in employeeCache) return employeeCache[key];
- try {
- const res = await get(Vue.prototype.apiUrl + `/main/user/getById/${key}`, {}, false);
- const vo = (res && res.data) || {};
- const brief = {
- name: vo.name || vo.userName || "",
- employeeNo: vo.jobNumber || "",
- department: vo.deptName || "",
- position: vo.postName || vo.position || "",
- hireDate: dateOnly(vo.joinDate || vo.entryDate),
- };
- employeeCache[key] = brief;
- return brief;
- } catch (error) {
- employeeCache[key] = null;
- return null;
- }
- }
- function extrasOf(brief) {
- if (!brief) return {};
- return {
- name: brief.name,
- employeeNo: brief.employeeNo,
- department: brief.department,
- position: brief.position,
- hireDate: brief.hireDate,
- };
- }
- /**
- * 离职单详情
- * GET /main/resignations/{id}
- */
- export async function getResignationById(id, loding = true) {
- if (!id) return null;
- const res = await get(Vue.prototype.apiUrl + `/main/resignations/${id}`, {}, loding);
- if (Number(res.code) !== 0) return null;
- return res.data || null;
- }
- /**
- * 离职单详情(归一化后,失败返回 null,不阻断上层)
- */
- export async function loadResignationDetail(id) {
- if (!id) return null;
- try {
- const vo = await getResignationById(id, false);
- if (!vo) return null;
- rememberResignId(String(id));
- const brief = await fetchEmployeeBrief(vo.userId);
- return adaptResignation(vo, extrasOf(brief));
- } catch (error) {
- return null;
- }
- }
- /**
- * 离职单列表(归一化后)
- * POST /main/resignations/page
- */
- export async function loadResignationList(params = {}, loding = false) {
- const { keyword = "", status = "all", pageNum = 1, size = 50 } = params;
- let rows = [];
- try {
- const res = await postJ(
- Vue.prototype.apiUrl + "/main/resignations/page",
- { pageNum, size, resignationNo: keyword || undefined },
- loding
- );
- rows = res && Number(res.code) === 0 ? asList(res.data) : [];
- } catch (error) {
- rows = [];
- }
- if (!rows.length) {
- const details = await Promise.all(
- getMyResignIds().map(async (id) => {
- try {
- return await getResignationById(id, false);
- } catch (error) {
- return null;
- }
- })
- );
- rows = details.filter(Boolean);
- }
- const list = [];
- for (const vo of rows) {
- if (!vo || vo.id == null) continue;
- const brief = await fetchEmployeeBrief(vo.userId);
- const row = adaptResignation(vo, extrasOf(brief));
- if (!row) continue;
- rememberResignId(String(row.id));
- if (!matchDisplayStatus(row, status)) continue;
- if (keyword && !`${row.name}${row.employeeNo}${row.resignationNo}${row.department}`.includes(keyword)) continue;
- list.push(row);
- }
- return { list, count: list.length, source: "api" };
- }
- /** 交接单列表 POST /main/handovers/page */
- export async function loadHandoverList(params = {}) {
- const { keyword = "", status = "all" } = params;
- const page = await getHandoverPage({ keyword }, false);
- const list = page.list
- .map((item) => adaptHandover(item))
- .filter(Boolean)
- .filter((item) => (status === "all" ? true : item.status === status))
- .filter((item) => (keyword ? String(item.resignationNo || "").includes(keyword) : true));
- return { list, count: page.count || list.length, source: "api" };
- }
- export async function loadHandoverDetail(id) {
- if (!id) return null;
- try {
- const vo = await getHandoverDetail(id, false);
- return adaptHandover(vo);
- } catch (error) {
- return null;
- }
- }
- /**
- * 查询「我的离职交接单」。
- *
- * 员工自助场景下一个人只有一张交接单,所以这里按登录人过滤后取最新一条:
- * POST /main/handovers/page(userId 由当前登录用户传入,服务端仍会做归属校验)
- * @returns {Promise<object|null>} adaptHandover 适配后的交接单,未创建过返回 null
- */
- export async function loadMyHandover(userId, loding = false) {
- const params = { pageNum: 1, size: 20, orderBy: "desc", sortName: "createTime" };
- if (userId !== undefined && userId !== null && userId !== "") params.userId = userId;
- try {
- const res = await postJ(Vue.prototype.apiUrl + "/main/handovers/page", params, loding);
- if (!res || Number(res.code) !== 0) return null;
- const rows = asList(res.data).map((item) => adaptHandover(item)).filter(Boolean);
- if (!rows.length) return null;
- // 列表 VO 不含事项明细,拿详情补齐(失败时退回列表行)
- const detail = await loadHandoverDetail(rows[0].id);
- return detail || rows[0];
- } catch (error) {
- return null;
- }
- }
- /* ---------------- 交接单发起 / 编辑 ---------------- */
- function compactHandover(object = {}) {
- return Object.keys(object).reduce((acc, key) => {
- const value = object[key];
- if (value !== "" && value !== null && value !== undefined) acc[key] = value;
- return acc;
- }, {});
- }
- /**
- * 组装新建 / 修改离职交接单参数,严格对齐后端 PO(与 PC 端 serializeHandover 一致):
- * - handoverType 必填;ONLINE 不得传 attachmentIds,OFFLINE 必须传(英文逗号分隔的文件 id)
- * - items 必填:创建时每项不得带 id,修改时回传已有事项 id(未回传的事项会被删除)
- * - assetReturns(入职领用物品归还清单)移动端暂不提交
- */
- export function buildHandoverPayload(form = {}) {
- const handoverType = form.handoverType ? String(form.handoverType).toUpperCase() : "ONLINE";
- const isOffline = handoverType === "OFFLINE";
- let attachmentIds = form.attachmentIds;
- if (Array.isArray(attachmentIds)) attachmentIds = attachmentIds.filter(Boolean).join(",");
- attachmentIds = attachmentIds ? String(attachmentIds) : undefined;
- if (!isOffline) attachmentIds = undefined;
- const items = (Array.isArray(form.items) ? form.items : [])
- .filter((item) => item && String(item.itemName || "").trim())
- .map((item) =>
- compactHandover({
- id: item.id,
- itemCategory: item.itemCategory || item.category || "WORK",
- itemName: String(item.itemName || item.name).trim(),
- itemContent: item.itemContent ? String(item.itemContent).trim() : undefined,
- receiverUserId: item.receiverUserId,
- })
- );
- // 资产归还清单:创建时按「归还名称 + 状态」新增(不得带主键);
- // 编辑时仅回传已进入清单(有主键)的行,修改接口不接受新增明细(与 PC 一致)
- const assetReturns = (Array.isArray(form.assetReturns) ? form.assetReturns : [])
- .map((row) => {
- const name = String(row.returnName || row.itemName || "").trim();
- if (!name) return null;
- const status = String(row.returnStatus || "PENDING").toUpperCase() === "RETURNED" ? "RETURNED" : "PENDING";
- return compactHandover({
- id: row.id,
- returnName: name,
- returnStatus: status,
- returnRemark: row.returnRemark ? String(row.returnRemark).trim() : undefined,
- });
- })
- .filter(Boolean);
- return compactHandover({
- handoverType,
- attachmentIds,
- handoverDate: dateOnly(form.handoverDate) || undefined,
- handoverUserId: form.handoverUserId,
- items: items.length ? items : undefined,
- assetReturns: assetReturns.length ? assetReturns : undefined,
- remark: form.remark ? String(form.remark).trim() : undefined,
- resignationId: form.resignationId,
- });
- }
- /** 创建离职交接单 POST /main/handovers(data 为新建交接单主键) */
- export async function createHandover(form, loding = true) {
- const payload = buildHandoverPayload(form);
- if (!payload.resignationId) return Promise.reject(new Error("缺少关联的离职申请"));
- if (!payload.handoverDate) return Promise.reject(new Error("请选择交接日期"));
- if (!payload.handoverUserId) return Promise.reject(new Error("请选择交接人"));
- if (payload.handoverType === "OFFLINE" && !payload.attachmentIds) {
- return Promise.reject(new Error("线下交接必须上传交接附件"));
- }
- if (!payload.items || !payload.items.length) {
- return Promise.reject(new Error("请至少填写一条交接事项"));
- }
- const res = await postJ(Vue.prototype.apiUrl + "/main/handovers", payload, loding);
- if (res && res.code == 0) return res.data;
- return Promise.reject(new Error((res && res.message) || "交接单创建失败"));
- }
- /** 修改离职交接单 PUT /main/handovers/{id}(items 采用全量回传,未回传的事项会被删除) */
- export async function updateHandover(id, form, loding = true) {
- if (!id) return Promise.reject(new Error("缺少交接单主键"));
- const payload = buildHandoverPayload(form);
- if (!payload.handoverDate) return Promise.reject(new Error("请选择交接日期"));
- if (!payload.handoverUserId) return Promise.reject(new Error("请选择交接人"));
- if (payload.handoverType === "OFFLINE" && !payload.attachmentIds) {
- return Promise.reject(new Error("线下交接必须上传交接附件"));
- }
- if (!payload.items || !payload.items.length) {
- return Promise.reject(new Error("请至少填写一条交接事项"));
- }
- const res = await putJ(Vue.prototype.apiUrl + `/main/handovers/${id}`, payload, loding);
- if (res && res.code == 0) return true;
- return Promise.reject(new Error((res && res.message) || "交接单保存失败"));
- }
- // —— 流程动作 ——
- export async function actSubmitResignation(row) {
- if (!row || !row.id) throw new Error("缺少离职单主键");
- return submitResignation(row.id);
- }
- export async function actWithdrawResignation(row, reason = "") {
- if (!row || !row.id) throw new Error("缺少离职单主键");
- return withdrawResignation(row.id, reason);
- }
- export async function actCancelResignation(row, reason = "") {
- if (!row || !row.id) throw new Error("缺少离职单主键");
- return cancelResignation(row.id, reason);
- }
- export async function actGenerateHandover(row) {
- if (!row || !row.id) throw new Error("缺少离职单主键");
- return generateResignationHandover(row.id);
- }
- export async function actCompleteHandoverItem(resignationId, itemId, remark = "") {
- return completeResignationHandoverItem(resignationId, itemId, remark);
- }
- export async function actArchiveResignation(row) {
- if (!row || !row.id) throw new Error("缺少离职单主键");
- return archiveResignation(row.id);
- }
- export async function actForceCompleteResignation(row, reason) {
- if (!row || !row.id) throw new Error("缺少离职单主键");
- return forceCompleteResignation(row.id, reason);
- }
- /** 提交离职单 POST /main/resignations/{id}/submit */
- export async function submitResignation(id, loding = true) {
- const res = await postJ(Vue.prototype.apiUrl + `/main/resignations/${id}/submit`, {}, loding);
- if (res.code == 0) return res.data;
- return Promise.reject(new Error(res.message));
- }
- /** 撤回离职单 POST /main/resignations/{id}/withdraw */
- export async function withdrawResignation(id, reason = "", loding = true) {
- const res = await postJ(
- Vue.prototype.apiUrl + `/main/resignations/${id}/withdraw`,
- { reason },
- loding
- );
- if (res.code == 0) return res.data;
- return Promise.reject(new Error(res.message));
- }
- /** 撤销离职单 POST /main/resignations/cancel/{id} */
- export async function cancelResignation(id, reason = "", loding = true) {
- const res = await postJ(
- Vue.prototype.apiUrl + `/main/resignations/cancel/${id}`,
- { reason },
- loding
- );
- if (res.code == 0) return res.data;
- return Promise.reject(new Error(res.message));
- }
- /** 归档离职单 POST /main/resignations/{id}/archive */
- export async function archiveResignation(id, loding = true) {
- const res = await postJ(Vue.prototype.apiUrl + `/main/resignations/${id}/archive`, {}, loding);
- if (res.code == 0) return res.data;
- return Promise.reject(new Error(res.message));
- }
- /** 强制完成离职 POST /main/resignations/{id}/force-complete */
- export async function forceCompleteResignation(id, reason = "", loding = true) {
- const res = await postJ(
- Vue.prototype.apiUrl + `/main/resignations/${id}/force-complete`,
- { reason },
- loding
- );
- if (res.code == 0) return res.data;
- return Promise.reject(new Error(res.message));
- }
- /** 生成交接单 POST /main/resignations/{id}/generate-handover */
- export async function generateResignationHandover(id, loding = true) {
- const res = await postJ(
- Vue.prototype.apiUrl + `/main/resignations/${id}/generate-handover`,
- {},
- loding
- );
- if (res.code == 0) return res.data;
- return Promise.reject(new Error(res.message));
- }
- /** 完成交接项 PUT /main/resignations/{resignationId}/handover/items/{itemId}/complete */
- export async function completeResignationHandoverItem(resignationId, itemId, loding = true) {
- const res = await putJ(
- Vue.prototype.apiUrl + `/main/resignations/${resignationId}/handover/items/${itemId}/complete`,
- {},
- loding
- );
- if (res.code == 0) return res.data;
- return Promise.reject(new Error(res.message));
- }
- /** 回退交接项 PUT /main/handovers/{handoverId}/items/{itemId}/rollback */
- export async function rollbackResignationHandoverItem(handoverId, itemId, reason = "", loding = true) {
- if (!handoverId || !itemId) throw new Error("缺少交接单或事项主键");
- const res = await putJ(
- Vue.prototype.apiUrl + `/main/handovers/${handoverId}/items/${itemId}/rollback`,
- { rollbackReason: reason },
- loding
- );
- if (res.code == 0) return res.data;
- return Promise.reject(new Error(res.message));
- }
- /** 回退交接项(流程动作封装,handoverId 取自离职详情 handover.id) */
- export async function actRollbackHandoverItem(handoverId, itemId, reason = "") {
- return rollbackResignationHandoverItem(handoverId, itemId, reason);
- }
- /** 删除交接单 DELETE /main/handovers/{id} */
- export async function deleteHandover(id, loding = true) {
- const res = await deleteApi(Vue.prototype.apiUrl + `/main/handovers/${id}`, {}, loding);
- if (res.code == 0) return res.data;
- return Promise.reject(new Error(res.message));
- }
- /** 删除交接单(流程动作封装) */
- export async function actDeleteHandover(id) {
- if (!id) throw new Error("缺少交接单主键");
- return deleteHandover(id);
- }
- /** 交接单分页 POST /main/handovers/page */
- export async function getHandoverPage(params = {}, loding = true) {
- const res = await postJ(Vue.prototype.apiUrl + "/main/handovers/page", params, loding);
- if (res.code == 0) return res.data;
- return Promise.reject(new Error(res.message));
- }
- /** 交接单详情 GET /main/handovers/{id} */
- export async function getHandoverDetail(id, loding = true) {
- const res = await get(Vue.prototype.apiUrl + `/main/handovers/${id}`, {}, loding);
- if (res.code == 0) return res.data;
- return Promise.reject(new Error(res.message));
- }
- // ============ 员工端离职申请(对齐 PC 端 /main/resignations)============
- /** 离职申请 BPM 流程标识(与 PC 端 RESIGNATION_APPLY_BUSINESS_KEY 一致) */
- export const RESIGNATION_APPROVAL_BUSINESS_KEY = "main_resignation_approval";
- /** 离职交接单 BPM 流程标识(与 PC 端 HANDOVER_APPROVAL_BUSINESS_KEY 一致) */
- export const HANDOVER_APPROVAL_BUSINESS_KEY = "main_handover_approval";
- /**
- * 办理方式:ONLINE 线上模板 / OFFLINE 线下附件。
- * 移动端页面不再提供切换,统一按 OFFLINE(线下附件)提交;此表仅用于回显旧数据。
- */
- export const RESIGN_HANDLING_MODE_LABELS = {
- ONLINE: "线上模板",
- OFFLINE: "线下附件",
- };
- /** 离职申请表单 → 后端 resignationType 枚举(表单直接存码,此处仅做兜底映射) */
- export const RESIGN_TYPE_CODE_LABELS = {
- VOLUNTARY: "主动离职",
- NEGOTIATED: "协商解除",
- PROBATION_FAILED: "试用不合格",
- PERFORMANCE_FAILED: "绩效不合格",
- TERMINATED: "辞退",
- DISMISSED: "辞退",
- CONTRACT_EXPIRED: "合同到期",
- RETIRE: "退休",
- RETIREMENT: "退休",
- OTHER: "其他",
- };
- /** 需要展示在下拉里的离职类型(对齐 PC 端 RESIGNATION_TYPE_OPTIONS,去掉重复的辞退/退休别名) */
- export const RESIGN_TYPE_OPTIONS = Object.keys(RESIGNATION_TYPE_LABELS)
- .filter((code) => code !== "TERMINATED" && code !== "RETIRE")
- .map((code) => ({ value: code, label: RESIGNATION_TYPE_LABELS[code] }));
- function ok(res) {
- return res && Number(res.code) === 0;
- }
- function compact(object = {}) {
- return Object.keys(object).reduce((result, key) => {
- const value = object[key];
- if (value !== "" && value !== null && value !== undefined) result[key] = value;
- return result;
- }, {});
- }
- function keepBigIntId(value) {
- if (value === undefined || value === null || value === "") return undefined;
- const text = String(value).trim();
- if (!/^-?\d+$/.test(text)) return value;
- if (!Number.isSafeInteger(Number(text))) return text;
- return Number(text);
- }
- function firstFileIdOf(value) {
- if (value == null || value === "") return undefined;
- if (Array.isArray(value)) return firstFileIdOf(value.find((item) => item !== undefined && item !== null && item !== ""));
- if (typeof value === "object") return value.id != null ? value.id : value.fileId;
- return value;
- }
- function todayIso() {
- const d = new Date();
- const pad = (n) => String(n).padStart(2, "0");
- return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
- }
- function extractId(result) {
- if (result == null) return null;
- if (typeof result === "object") {
- return result.id != null ? result.id : result.resignationId != null ? result.resignationId : null;
- }
- return result;
- }
- /** 办理方式规范化:老数据无该字段时按线上单据字段推断,默认线下 */
- export function normalizeResignHandlingMode(raw = {}) {
- const mode = String(raw.handlingMode || raw.handlingType || "").trim().toUpperCase();
- if (mode === "ONLINE" || mode === "OFFLINE") return mode;
- return raw.onlineDocumentTemplateId != null && raw.onlineDocumentTemplateId !== ""
- ? "ONLINE"
- : raw.onlineDocumentId
- ? "ONLINE"
- : "OFFLINE";
- }
- /**
- * 组装新建 / 修改离职单参数,严格对齐 PC 端 buildResignationPayload(POST /main/resignations PO):
- * - 机构 / 部门 / 岗位序列 / 岗位名称 / 岗位层级 主键 + 名称快照
- * - 办理方式:ONLINE 传模板主键;OFFLINE 传附件主键(取表单文件数组首个)
- */
- export function buildResignationPayload(form = {}) {
- const source = form || {};
- const handlingMode = normalizeResignHandlingMode(source);
- const online = handlingMode === "ONLINE";
- return compact({
- id: keepBigIntId(source.resignationId || source.id),
- userId: keepBigIntId(source.userId || source.employeeId),
- employmentPeriodId: keepBigIntId(source.employmentPeriodId),
- // 机构 / 部门 / 岗位主键
- groupId: keepBigIntId(source.groupId),
- deptId: keepBigIntId(source.deptId),
- positionId: keepBigIntId(source.positionId),
- positionName: source.position || source.positionName || undefined,
- positionSequenceId: keepBigIntId(source.positionSequenceId),
- positionSequence: source.positionSequence || undefined,
- positionLevelId: keepBigIntId(source.positionLevelId),
- positionLevel: source.positionLevel || undefined,
- positionType: source.positionType || undefined,
- positionTypeName: source.positionTypeName || undefined,
- phone: source.phone || undefined,
- applyDate: dateOnly(source.applyDate) || todayIso(),
- plannedLastWorkDate: dateOnly(source.plannedLastWorkDate || source.expectedLeaveDate),
- effectiveDate: dateOnly(source.effectiveDate),
- reason: source.reason,
- resignationType: source.resignationType || "VOLUNTARY",
- userComment: source.userComment || undefined,
- hrComment: source.hrComment || undefined,
- // 办理方式:ONLINE 线上模板 / OFFLINE 线下附件
- handlingMode,
- // 线下附件:接口字段为单个 fileId,取表单文件数组首个
- approvalDocumentFileId: online
- ? undefined
- : keepBigIntId(source.approvalDocumentFileId || firstFileIdOf(source.attachmentIds)),
- // 线上模板:仅线上办理时传模板主键
- onlineDocumentTemplateId: online ? keepBigIntId(source.onlineDocumentTemplateId) : undefined,
- onlineDocumentContent: source.onlineDocumentContent,
- onlineDocumentVariableValuesJson: source.onlineDocumentVariableValuesJson,
- });
- }
- /** 新建离职草稿 POST /main/resignations(返回离职单主键) */
- export async function createResignation(payload, loding = true) {
- const res = await postJ(Vue.prototype.apiUrl + "/main/resignations", payload || {}, loding);
- if (!ok(res)) throw new Error(res.message || "离职申请创建失败");
- const id = extractId(res.data);
- if (id != null) rememberResignId(String(id));
- return id;
- }
- /** 修改离职草稿 PUT /main/resignations */
- export async function updateResignation(payload, loding = true) {
- const id = payload && (payload.id || payload.resignationId);
- if (id === undefined || id === null || id === "") throw new Error("缺少离职单主键");
- const res = await putJ(Vue.prototype.apiUrl + "/main/resignations", payload || {}, loding);
- if (!ok(res)) throw new Error(res.message || "离职申请修改失败");
- rememberResignId(String(id));
- return extractId(res.data) != null ? extractId(res.data) : id;
- }
- /**
- * 保存离职单(新建 / 修改统一入口)。
- * @returns {Promise<number|string>} 离职单主键
- */
- export async function saveResignationDraft(form, loding = true) {
- const payload = buildResignationPayload(form);
- if (!payload.userId) throw new Error("请选择离职人员");
- if (!payload.resignationType) throw new Error("请选择离职类型");
- if (!payload.plannedLastWorkDate) throw new Error("请选择计划最后工作日");
- if (!payload.reason) throw new Error("请填写离职原因");
- if (payload.handlingMode === "OFFLINE" && !payload.approvalDocumentFileId) {
- throw new Error("请上传离职申请附件");
- }
- if (payload.id != null && payload.id !== "") {
- await updateResignation(payload, loding);
- return payload.id;
- }
- return createResignation(payload, loding);
- }
- /** 审批流程 variables(与 PC 端 processSubmitDialog 的入参形状一致) */
- export function buildResignationVariables(form = {}, resignationId = null) {
- const row = form || {};
- const id = resignationId != null && resignationId !== "" ? resignationId : row.resignationId || row.id;
- return compact({
- businessCode: row.resignationNo || row.businessCode || (id != null ? String(id) : ""),
- businessName: row.name || row.employeeName || row.userName || "",
- businessType: "离职申请",
- userId: row.userId != null ? row.userId : row.employeeId,
- groupId: row.groupId || "",
- groupName: row.company || "",
- deptId: row.deptId || "",
- departmentName: row.department || "",
- positionId: row.positionId || "",
- positionName: row.position || "",
- adjustHeadcount: true,
- });
- }
- /**
- * 发起离职申请审批流程。
- * businessKey 固定 main_resignation_approval,businessId 为离职单主键。
- */
- export async function startResignationApproval(businessId, variables = {}, loding = false) {
- if (businessId == null || businessId === "") throw new Error("缺少离职单主键,无法发起审批");
- const page = await processInstancePage(
- { pageNo: 1, pageSize: 1, reset: true, key: RESIGNATION_APPROVAL_BUSINESS_KEY },
- loding
- );
- const first = page && page.list && page.list[0];
- const processDefinitionId = first && first.processDefinition ? first.processDefinition.id : "";
- if (!processDefinitionId) throw new Error("未匹配到离职审批流程,请联系管理员配置流程定义");
- return processInstanceCreateAPI(
- {
- businessId,
- businessKey: RESIGNATION_APPROVAL_BUSINESS_KEY,
- formCreateUserId: variables.userId || "",
- processDefinitionId,
- variables,
- },
- false
- );
- }
- /**
- * 提交离职申请:创建(或更新)离职单后直接发起审批。
- * 审批发起失败不阻断主流程(单据已落库,避免重复创建)。
- * @returns {Promise<{id:number|string, approvalStarted:boolean, approvalError:string}>}
- */
- export async function submitResignationFlow({ form, existingId = null, loding = true } = {}) {
- if (!form) throw new Error("申请参数为空");
- const id0 = existingId != null && existingId !== "" ? existingId : form.resignationId || form.id || null;
- const payload = buildResignationPayload(Object.assign({}, form, { id: id0 }));
- let resignationId = id0;
- if (id0 != null && id0 !== "") {
- await updateResignation(Object.assign({}, payload, { id: resignationId }), loding);
- } else {
- resignationId = await createResignation(payload, loding);
- if (resignationId == null) throw new Error("离职申请已提交但未返回主键,请联系管理员");
- }
- const variables = buildResignationVariables(Object.assign({}, form, { id: resignationId }), resignationId);
- let approvalStarted = false;
- let approvalError = "";
- try {
- await startResignationApproval(resignationId, variables);
- approvalStarted = true;
- } catch (error) {
- approvalError = (error && error.message) || "审批流程发起失败";
- }
- return { id: resignationId, approvalStarted, approvalError };
- }
- /**
- * 离职单 → 离职申请表单回填(只读详情 / 编辑态共用)
- * @param {object} raw GET /main/resignations/{id} 的原始 VO
- * @param {object} extras 列表行 / 员工档案补出的姓名、工号等展示字段
- */
- export function hydrateResignationForm(raw = {}, extras = {}) {
- const vo = raw || {};
- const ext = extras || {};
- const handlingMode = normalizeResignHandlingMode(vo);
- const attachmentId = vo.approvalDocumentFileId;
- return {
- resignationId: vo.id != null ? vo.id : null,
- userId: vo.userId != null ? String(vo.userId) : ext.userId != null ? String(ext.userId) : "",
- name: ext.name || ext.employeeName || vo.employeeName || vo.userName || "",
- employeeNo: ext.employeeNo || vo.employeeNo || "",
- phone: vo.phone || ext.phone || "",
- hireDate: dateOnly(vo.hireDate || vo.joinDate || ext.hireDate),
- // —— 员工信息(级联字段)——
- groupId: vo.groupId != null ? String(vo.groupId) : "",
- company: vo.groupName || vo.companyName || vo.legalEntityName || ext.company || "",
- deptId: vo.deptId != null ? String(vo.deptId) : "",
- department: vo.departmentName || vo.deptName || ext.department || "",
- positionId: vo.positionId != null ? String(vo.positionId) : "",
- position: vo.positionName || ext.position || "",
- positionSequenceId: vo.positionSequenceId != null ? String(vo.positionSequenceId) : "",
- positionSequence: vo.positionSequence || vo.positionSequenceName || "",
- positionLevelId: vo.positionLevelId != null ? String(vo.positionLevelId) : "",
- positionLevel: vo.positionLevel || "",
- positionType: vo.positionType != null ? String(vo.positionType) : "",
- positionTypeName: vo.positionTypeName || "",
- employmentPeriodId: vo.employmentPeriodId != null ? vo.employmentPeriodId : null,
- // —— 离职信息 ——
- resignationType: String(vo.resignationType || "VOLUNTARY").toUpperCase(),
- applyDate: dateOnly(vo.applyDate) || dateOnly(vo.createTime) || todayIso(),
- plannedLastWorkDate: dateOnly(vo.plannedLastWorkDate) || dateOnly(vo.leaveDate),
- effectiveDate: dateOnly(vo.effectiveDate),
- handlingMode,
- onlineDocumentTemplateId: vo.onlineDocumentTemplateId != null ? vo.onlineDocumentTemplateId : null,
- // 附件主键可能是逗号拼接的多附件字符串
- attachmentIds:
- attachmentId != null && attachmentId !== ""
- ? String(attachmentId)
- .split(",")
- .map((id) => id.trim())
- .filter(Boolean)
- : [],
- reason: vo.reason || "",
- userComment: vo.userComment || "",
- hrComment: vo.hrComment || "",
- // —— 状态(只读详情展示用)——
- resignationNo: vo.resignationNo || "",
- approvalStatus: vo.approvalStatus || "",
- createTime: vo.createTime || "",
- };
- }
- /* ---------------- 离职交接单:物品归还清单 / 审批 ---------------- */
- /** 领用单状态 */
- export const RECEIPT_STATUS_LABELS = {
- DRAFT: "草稿",
- APPROVING: "审批中",
- COMPLETED: "已完成",
- };
- /** 入职领用明细 → 交接单可编辑行(默认全部按「已归还」预选) */
- export function decorateReceiptItems(receipt) {
- const details = Array.isArray(receipt && receipt.details) ? receipt.details : [];
- return details.map((item) => ({
- id: item.id,
- itemName: item.itemName || "",
- itemId: item.itemId != null ? String(item.itemId) : "",
- itemTypeText: item.itemTypeText || item.itemType || "",
- model: item.model || "",
- specification: item.specification || "",
- quantity: item.quantity != null ? item.quantity : "",
- // 逐行标记的归还状态会随交接单一并提交为物品归还清单
- returnStatus: "RETURNED",
- returnRemark: item.remark || "",
- substanceId: item.substanceId,
- }));
- }
- /**
- * 按员工取最新入职物品领用单 GET /main/onboarding-item-receipts/latest/{employeeId}
- * 后端可能返回 null / {}(该员工无领用单),此时视为「无记录」。
- */
- export async function getLatestItemReceipt(employeeId, loding = false) {
- if (employeeId === undefined || employeeId === null || employeeId === "") return null;
- try {
- const res = await get(
- Vue.prototype.apiUrl + `/main/onboarding-item-receipts/latest/${encodeURIComponent(String(employeeId))}`,
- {},
- loding
- );
- if (!ok(res)) return null;
- const data = res.data;
- if (!data || typeof data !== "object") return null;
- const hasId = data.id != null && data.id !== "";
- const details = Array.isArray(data.details) ? data.details : [];
- if (!hasId && !details.length) return null;
- return {
- id: data.id != null ? data.id : "",
- documentCode: data.documentCode || "",
- receiptStatus: String(data.receiptStatus || "DRAFT").toUpperCase(),
- employeeId: data.employeeId,
- details: details.map((item) => ({
- id: item.id,
- itemName: item.itemName || "",
- itemId: item.itemId != null ? String(item.itemId) : "",
- itemTypeText: item.itemType || item.categoryName || "",
- model: item.itemModel || item.model || "",
- specification: item.specifications || item.specification || "",
- quantity: item.quantity != null ? Number(item.quantity) : 1,
- remark: item.remark || "",
- substanceId: item.substanceId != null && item.substanceId !== "" ? item.substanceId : undefined,
- })),
- };
- } catch (error) {
- return null;
- }
- }
- /**
- * 可发起交接单的离职申请:审批通过 + 未归档 + 尚未生成交接单
- * POST /main/resignations/page
- */
- export async function loadApprovedResignations(params = {}, loding = false) {
- const res = await postJ(
- Vue.prototype.apiUrl + "/main/resignations/page",
- compact({
- pageNum: params.pageNum || 1,
- size: params.size || 100,
- approvalStatus: "APPROVED",
- archiveStatus: "NOT_ARCHIVED",
- }),
- loding
- );
- if (!ok(res)) return [];
- const seen = new Set();
- const rows = [];
- // /main/resignations/page 的 VO 常不带 employeeName / departmentName / 计划最后工作日,
- // 直接 adapt 会得到「离职单号 · · 」这种空白选项(PC 端用 withEmployeeInfo 补全,这里对齐)
- for (const vo of asList(res.data)) {
- const brief = await fetchEmployeeBrief(vo && vo.userId);
- const row = adaptResignation(vo, extrasOf(brief));
- if (row) rows.push(row);
- }
- return rows
- .filter((row) => {
- const code = String(row.handoverStatus || "").toUpperCase();
- const created = row.handover && row.handover.id ? true : Boolean(code) && code !== "NOT_CREATED";
- return !created;
- })
- .filter((row) => {
- const key = String(row.resignationNo || row.id || "");
- if (!key) return true;
- if (seen.has(key)) return false;
- seen.add(key);
- return true;
- });
- }
- /** 交接单审批流程 variables(对齐 PC 端 launchApproval 入参) */
- export function buildHandoverVariables(form = {}, handoverId = null) {
- const row = form || {};
- const id = handoverId != null && handoverId !== "" ? handoverId : row.handoverId || row.id;
- return compact({
- businessCode: row.documentCode || row.resignationNo || (id != null ? String(id) : ""),
- businessName: row.person || row.handoverUserName || row.employee || "",
- businessType: "离职交接单",
- userId: row.userId,
- groupId: row.groupId || row.deptId || "",
- groupName: row.deptName || row.department || "",
- });
- }
- /** 发起离职交接单审批流程(businessKey 固定 main_handover_approval) */
- export async function startHandoverApproval(businessId, variables = {}, loding = false) {
- if (businessId == null || businessId === "") throw new Error("缺少交接单主键,无法发起审批");
- const page = await processInstancePage(
- { pageNo: 1, pageSize: 1, reset: true, key: HANDOVER_APPROVAL_BUSINESS_KEY },
- loding
- );
- const first = page && page.list && page.list[0];
- const processDefinitionId = first && first.processDefinition ? first.processDefinition.id : "";
- if (!processDefinitionId) throw new Error("未匹配到离职交接单审批流程,请联系管理员配置流程定义");
- return processInstanceCreateAPI(
- {
- businessId,
- businessKey: HANDOVER_APPROVAL_BUSINESS_KEY,
- formCreateUserId: variables.userId || "",
- processDefinitionId,
- variables,
- },
- false
- );
- }
- /**
- * 提交离职交接单:创建(或更新)交接单后直接发起审批。
- * @returns {Promise<{id:number|string, approvalStarted:boolean, approvalError:string}>}
- */
- export async function submitHandoverFlow({ form, existingId = null } = {}) {
- if (!form) throw new Error("交接单参数为空");
- const id0 = existingId != null && existingId !== "" ? existingId : form.handoverId || null;
- let handoverId = id0;
- if (id0 != null && id0 !== "") {
- await updateHandover(id0, form, true);
- } else {
- handoverId = await createHandover(form, true);
- if (handoverId == null) throw new Error("交接单已创建但未返回主键,请联系管理员");
- }
- const variables = buildHandoverVariables(
- Object.assign({}, form, { handoverId, documentCode: form.documentCode, resignationNo: form.resignationNo }),
- handoverId
- );
- let approvalStarted = false;
- let approvalError = "";
- try {
- await startHandoverApproval(handoverId, variables);
- approvalStarted = true;
- } catch (error) {
- approvalError = (error && error.message) || "审批流程发起失败";
- }
- return { id: handoverId, approvalStarted, approvalError };
- }
|