import dayjs from "dayjs"; /** 审批类型(与后端字段 approvalType 对齐,后期可同步) */ export const APPROVAL_TYPE_OPTIONS = [ { value: "cost_reimburse", label: "费用报销申请", cellBg: "#e8f8ef", cellColor: "#1a7f4b" }, { value: "travel_reimburse", label: "差旅报销申请", cellBg: "#f0f2f5", cellColor: "#606266" }, { value: "overtime", label: "加班申请", cellBg: "#fdf3e8", cellColor: "#c45c26" }, { value: "leave", label: "请假申请", cellBg: "#fce8f0", cellColor: "#b84d7a" }, { value: "work_handover", label: "工作交接申请", cellBg: "#f0e8fc", cellColor: "#6b4d9e" }, { value: "regular", label: "转正申请", cellBg: "#e8f4fc", cellColor: "#2b6cb0" }, { value: "resign", label: "离职申请", cellBg: "#ffffff", cellColor: "#303133", border: "1px solid #e4e7ed" }, { value: "transfer", label: "调岗申请", cellBg: "#ffffff", cellColor: "#303133", border: "1px solid #e4e7ed" }, { value: "out_office", label: "公出申请", cellBg: "#e8f4ff", cellColor: "#409eff" }, { value: "business_trip", label: "出差申请", cellBg: "#fdf6ec", cellColor: "#e6a23c" }, { value: "procurement", label: "采购审批", cellBg: "#f4f4f5", cellColor: "#909399" }, { value: "quotation", label: "报价审批", cellBg: "#f4ecfc", cellColor: "#9b59b6" }, { value: "shipment", label: "发货审批", cellBg: "#e8faf6", cellColor: "#1abc9c" }, ]; /** 审批状态 approvalStatus */ export const APPROVAL_STATUS_OPTIONS = [ { value: "pending", label: "审核中" }, { value: "approved", label: "已通过" }, { value: "rejected", label: "已驳回" }, { value: "cancelled", label: "已撤销" }, ]; /** 审批方式 approvalMode */ export const APPROVAL_MODE_OPTIONS = [ { value: "parallel", label: "与签" }, { value: "or_sign", label: "或签" }, ]; /** * 提交审批模板(按类型一键填报,字段后期与后端模板同步) */ export const SUBMIT_TEMPLATES = { cost_reimburse: { approvalType: "cost_reimburse", label: "费用报销", summaryPlaceholder: "请填写报销事由、金额等", fields: [ { key: "summary", label: "申请事由", type: "textarea", required: true, rows: 3 }, { key: "amount", label: "报销金额(元)", type: "number", required: true, min: 0, precision: 2 }, ], approvalMode: "parallel", }, travel_reimburse: { approvalType: "travel_reimburse", label: "差旅报销", summaryPlaceholder: "出差行程与费用说明", fields: [ { key: "summary", label: "差旅说明", type: "textarea", required: true, rows: 3 }, { key: "amount", label: "报销金额(元)", type: "number", required: true, min: 0, precision: 2 }, { key: "tripDays", label: "出差天数", type: "number", required: false, min: 0, precision: 0 }, ], approvalMode: "parallel", }, overtime: { approvalType: "overtime", label: "加班申请", fields: [ { key: "summary", label: "加班事由", type: "textarea", required: true, rows: 3 }, { key: "overtimeDate", label: "加班日期", type: "date", required: true }, { key: "hours", label: "加班时长(小时)", type: "number", required: true, min: 0.5, precision: 1 }, ], approvalMode: "parallel", }, leave: { approvalType: "leave", label: "请假申请", fields: [ { key: "leaveType", label: "请假类型", type: "select", required: true, options: [ { label: "年假", value: "annual" }, { label: "病假", value: "sick" }, { label: "事假", value: "personal" }, { label: "调休", value: "compensatory" }, ] }, { key: "summary", label: "请假事由", type: "textarea", required: true, rows: 2 }, { key: "dateRange", label: "请假时间", type: "datetimerange", required: true }, ], approvalMode: "parallel", }, work_handover: { approvalType: "work_handover", label: "工作交接", fields: [ { key: "summary", label: "交接说明", type: "textarea", required: true, rows: 3 }, { key: "handoverTo", label: "交接对象", type: "text", required: true }, ], approvalMode: "parallel", }, regular: { approvalType: "regular", label: "转正申请", fields: [ { key: "summary", label: "转正说明", type: "textarea", required: true, rows: 3 }, { key: "regularDate", label: "拟转正日期", type: "date", required: true }, ], approvalMode: "parallel", }, resign: { approvalType: "resign", label: "离职申请", fields: [ { key: "summary", label: "离职原因", type: "textarea", required: true, rows: 3 }, { key: "lastWorkDay", label: "最后工作日", type: "date", required: true }, ], approvalMode: "or_sign", }, transfer: { approvalType: "transfer", label: "调岗申请", fields: [ { key: "summary", label: "调岗说明", type: "textarea", required: true, rows: 2 }, { key: "targetDept", label: "目标部门", type: "text", required: true }, { key: "targetPost", label: "目标岗位", type: "text", required: true }, ], approvalMode: "parallel", }, }; export const STORAGE_KEY = "oa_unified_approve_list_v1"; export function approvalTypeLabel(v) { return APPROVAL_TYPE_OPTIONS.find((x) => x.value === v)?.label || "—"; } export function approvalTypeStyle(v) { const hit = APPROVAL_TYPE_OPTIONS.find((x) => x.value === v); if (!hit) return {}; return { backgroundColor: hit.cellBg, color: hit.cellColor, border: hit.border || "none", }; } export function approvalStatusLabel(v) { return APPROVAL_STATUS_OPTIONS.find((x) => x.value === v)?.label || "—"; } export function approvalStatusTagType(v) { if (v === "approved") return "success"; if (v === "rejected") return "danger"; if (v === "cancelled") return "info"; return "primary"; } export function approvalModeLabel(v) { if (v === "countersign") return "或签"; return APPROVAL_MODE_OPTIONS.find((x) => x.value === v)?.label || "与签"; } export function unreadLabel(v) { return v ? "是" : "否"; } export function buildDefaultFlowNodes() { return [ { approverId: "mock_supervisor", approverName: "直属上级", sortOrder: 1, nodeOrder: 1, nodeStatus: "process", approveOpinion: "", approveTime: "", }, { approverId: "mock_manager", approverName: "部门经理", sortOrder: 2, nodeOrder: 2, nodeStatus: "wait", approveOpinion: "", approveTime: "", }, ]; } function demoRow(partial) { const now = dayjs().format("YYYY-MM-DD HH:mm:ss"); return { id: partial.id, bizId: partial.bizId || partial.id, applicantNo: partial.applicantNo, applicantName: partial.applicantName, approvalType: partial.approvalType, approvalMode: partial.approvalMode || "parallel", unread: partial.unread ?? false, approvalStatus: partial.approvalStatus || "pending", createTime: partial.createTime || now, summary: partial.summary || "", formPayload: partial.formPayload || {}, approvalFlowNodes: partial.approvalFlowNodes || buildDefaultFlowNodes(), currentNodeIndex: partial.currentNodeIndex ?? 0, approvalRecords: partial.approvalRecords || [], rejectReason: partial.rejectReason || "", sourceRoute: partial.sourceRoute || "", }; } /** 初始演示数据(共 22 条,与原型数量一致) */ export function createInitialMockRows() { const types = [ "cost_reimburse", "travel_reimburse", "overtime", "leave", "work_handover", "regular", "resign", "transfer", "cost_reimburse", "leave", "overtime", "travel_reimburse", "work_handover", "regular", "cost_reimburse", "leave", "transfer", "resign", "overtime", "travel_reimburse", "cost_reimburse", "leave", ]; const applicants = [ { no: "007", name: "苹果" }, { no: "Guest001", name: "外部用户" }, { no: "0056", name: "王五" }, { no: "0042", name: "李四" }, { no: "0088", name: "猫猫" }, { no: "0012", name: "张三" }, { no: "0033", name: "赵六" }, ]; const summaries = [ "办公用品采购报销", "上海出差差旅费", "周末项目加班", "年假 3 天", "离职工作交接", "试用期转正申请", "个人原因离职", "调至销售部", "客户接待餐费", "病假 1 天", "节假日值班加班", "北京培训差旅", "项目文档交接", "研发岗转正", "通讯费报销", "事假半天", "调岗至市场部", "协商离职", "工作日延时加班", "成都展会差旅", "交通费报销", "调休 1 天", ]; const statuses = ["pending", "pending", "pending", "approved", "pending", "pending", "rejected", "pending"]; return types.map((approvalType, i) => { const ap = applicants[i % applicants.length]; const daysAgo = i % 14; return demoRow({ id: `mock_${i + 1}`, bizId: `BIZ${String(2025031400 + i)}`, applicantNo: ap.no, applicantName: ap.name, approvalType, approvalMode: i % 5 === 0 ? "or_sign" : "parallel", unread: i % 3 === 0, approvalStatus: statuses[i % statuses.length], createTime: dayjs().subtract(daysAgo, "day").hour(9 + (i % 8)).minute((i * 7) % 60).second(0).format("YYYY-MM-DD HH:mm:ss"), summary: summaries[i], formPayload: { summary: summaries[i] }, }); }); } export function loadStoredRows() { try { const raw = localStorage.getItem(STORAGE_KEY); if (!raw) return null; const parsed = JSON.parse(raw); return Array.isArray(parsed) ? parsed : null; } catch { return null; } } export function saveStoredRows(rows) { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(rows)); } catch { /* ignore quota */ } } export function createEmptySubmitForm(templateKey) { const tpl = SUBMIT_TEMPLATES[templateKey]; const payload = { summary: "" }; (tpl?.fields || []).forEach((f) => { if (f.type === "number") payload[f.key] = undefined; else if (f.type === "datetimerange") payload[f.key] = []; else payload[f.key] = ""; }); return { templateKey: templateKey || "", approvalMode: tpl?.approvalMode || "parallel", formPayload: payload, approvalFlowNodes: buildDefaultFlowNodes(), }; }