import { adaptBpmTask, adaptCcProcess, approveTask, getCcProcessPage, getDoneTaskPage, getProcessInstance, getTodoTaskPage, rejectTask, } from "@/api/hr/bpm"; import { loadResignationDetail } from "@/api/hr/resign"; export const APPROVAL_TAB_TODO = "待我审批"; export const APPROVAL_TAB_DONE = "我已审批"; export const APPROVAL_TAB_CC = "抄送我的"; export const APPROVAL_TABS = [APPROVAL_TAB_TODO, APPROVAL_TAB_DONE, APPROVAL_TAB_CC]; export const RESIGN_BUSINESS_TYPE = "离职申请"; // 缓存最近一次列表结果,详情页按 id 查找时优先命中,避免重复请求 const listCache = new Map(); function isResignType(text) { return String(text || "").includes("离职"); } // 服务端任务不一定回填 businessType,这里把流程名 / 移动端路由一起作为兜底判断 function isResignItem(item = {}) { return [item.type, item.businessName, item.processName, item.viewRouter].some( (text) => isResignType(text) || String(text || "").toLowerCase().includes("resign") ); } function withStatus(item) { const status = (item.result || {}).status || "待审批"; return { ...item, statusLabel: status, statusClass: status === "已同意" ? "status-approved" : status === "待审批" || status === "已抄送" ? "status-pending" : "status-rejected", }; } // 任务上没带业务主键时,回流程实例里找(PC 端发起流程时写入 businessId / 流程变量) async function resolveBusiness(item) { if (item.businessId && item.applicant !== "—") return item; if (!item.processInstanceId) return item; try { const instance = (await getProcessInstance(item.processInstanceId, false)) || {}; const variables = instance.variables || {}; const businessId = item.businessId || instance.businessId || variables.businessId || ""; const applicant = item.applicant && item.applicant !== "—" ? item.applicant : instance.startUserNickname || variables.businessName || "—"; return { ...item, businessId, processName: item.processName || instance.name || variables.businessName || "", businessCode: item.businessCode || instance.businessCode || variables.businessCode || "", applicant, avatar: String(applicant || "审").charAt(0), dept: item.dept || variables.departmentName || "", position: item.position || variables.positionName || "", type: isResignType(item.type) || !variables.businessType ? item.type : variables.businessType, }; } catch (error) { return item; } } // 离职类审批:补全离职单信息,便于审批人在手机上直接看单据 async function withResignation(item) { if (!isResignItem(item) || !item.businessId) return item; try { const resignation = await loadResignationDetail(item.businessId); if (!resignation) return item; const applicant = item.applicant && item.applicant !== "—" ? item.applicant : resignation.name || "—"; return { ...item, resignation, applicant, avatar: String(applicant || "离").charAt(0), dept: item.dept || resignation.department || "", position: item.position || resignation.position || "", businessCode: resignation.resignationNo || item.businessCode || "", processName: item.processName || resignation.type || RESIGN_BUSINESS_TYPE, summary: `${resignation.type || RESIGN_BUSINESS_TYPE} · 预计离职 ${ resignation.expectedLeaveDate || "—" }`, reason: resignation.reason || item.reason, days: item.days || `${(resignation.handover && resignation.handover.items ? resignation.handover.items.length : 0)} 项交接`, }; } catch (error) { return item; } } async function toServerItem(vo, done) { const task = adaptBpmTask(vo); const base = { id: task.taskId, source: "server", taskId: task.taskId, processInstanceId: task.processInstanceId, processName: task.processName, businessId: task.businessId, businessCode: task.businessCode, businessName: task.businessName, type: task.businessType || task.processName, applicant: task.applicant || "—", avatar: String(task.applicant || "审").charAt(0), dept: task.department, position: task.position, taskName: task.name, viewRouter: task.viewRouter, handleRouter: task.handleRouter, summary: [task.businessType, task.businessCode || task.businessName].filter(Boolean).join(" · "), reason: "", days: "", color: "#157a2c", time: task.createTime, result: done ? { status: task.resultLabel || "已同意", comment: task.reason || "", handledAt: task.handledAt, } : null, raw: vo, }; return withStatus(await withResignation(await resolveBusiness(base))); } // 抄送我的是只读列表,不带审批动作 function toCcItem(vo) { const cc = adaptCcProcess(vo); return withStatus({ id: `cc-${cc.id}`, source: "server", ccId: cc.id, processInstanceId: cc.processInstanceId, processName: cc.processName, businessId: "", businessCode: "", type: cc.processName || "流程抄送", applicant: cc.applicant || cc.creatorName || "—", avatar: String(cc.applicant || cc.creatorName || "抄").charAt(0), dept: "", position: "", taskName: cc.taskName, summary: [cc.processName, cc.taskName].filter(Boolean).join(" · "), reason: cc.reason, days: "", color: "#157a2c", time: cc.createTime, readonly: true, result: { status: "已抄送", comment: cc.reason || "", handledAt: cc.createTime }, raw: vo, }); } async function serverApprovalItems(tab) { if (tab === APPROVAL_TAB_CC) { const page = await getCcProcessPage(); return (page.list || []).map((vo) => toCcItem(vo)); } const page = tab === APPROVAL_TAB_TODO ? await getTodoTaskPage() : await getDoneTaskPage(); const done = tab !== APPROVAL_TAB_TODO; return Promise.all((page.list || []).map((vo) => toServerItem(vo, done))); } /** * 审批列表统一入口 */ export async function loadApprovalList(tab = APPROVAL_TAB_TODO) { const list = await serverApprovalItems(tab); listCache.set(tab, list); return { list, source: "api" }; } /** * 审批详情:优先命中缓存,未命中则待办 / 已办各拉一次再匹配 */ export async function loadApprovalDetail(id) { if (!id) return null; const cachedList = Array.from(listCache.values()); for (let i = 0; i < cachedList.length; i++) { const hit = (cachedList[i] || []).find((x) => String(x.id) === String(id)); if (hit) return hit; } const results = await Promise.all([ serverApprovalItems(APPROVAL_TAB_TODO), serverApprovalItems(APPROVAL_TAB_DONE), ]); const all = results[0].concat(results[1]); return all.find((x) => String(x.id) === String(id)) || null; } /** * 审批通过 */ export async function actApproveApproval(item, comment = "") { if (!item || !item.id) throw new Error("缺少审批主键"); const reason = String(comment || "").trim() || "同意"; await approveTask({ id: item.taskId || item.id, reason }); return true; } /** * 审批驳回 */ export async function actRejectApproval(item, comment = "") { if (!item || !item.id) throw new Error("缺少审批主键"); const reason = String(comment || "").trim() || "申请信息不完整"; await rejectTask({ id: item.taskId || item.id, reason }); return true; } /** 待办数量(首页 / 工作台 / TabBar 角标) */ export async function fetchPendingApprovalCount() { try { const page = await getTodoTaskPage({ pageSize: 1 }, false); return page.total || 0; } catch (error) { return 0; } }