import dayjs from "dayjs"; import { APPROVAL_MODULE_KEYS, APPROVAL_MODULE_REGISTRY, getModuleMatchingBusinessTypes, } from "./approvalModuleRegistry.js"; import { matchBusinessTypeValue } from "./approvalTemplateType.js"; import { parseDatetimerangeValue } from "./approvalFormField.js"; /** 人员下拉字段识别(与 Web SELECT_OPTION_SOURCE.USER 等价) */ export function isUserSelectField(field) { const src = String(field?.optionSource ?? "").toLowerCase(); return ( src === "user" || src === "personnel" || src === "userlist" || (field?.type === "select" && String(field?.label || "").includes("申请人")) ); } export function findApplicantTemplateField(fields = []) { return ( fields.find(f => String(f?.label || "").includes("申请人")) || fields.find(f => isUserSelectField(f)) || null ); } /* ---------- 请假 ---------- */ export function isLeaveBalanceField(field) { const label = String(field?.label || ""); return label.includes("假期余额") || field?.key === "leaveBalanceDays"; } export function isLeaveDurationField(field) { const label = String(field?.label || ""); return label.includes("请假时长") || field?.key === "leaveDurationDays"; } export function displayLeaveTemplateFields(fields = []) { return (fields || []).filter( f => !isLeaveBalanceField(f) && !isLeaveDurationField(f) ); } export function findLeaveTimeTemplateField(fields = []) { return ( fields.find( f => f?.type === "datetimerange" && String(f?.label || "").includes("请假时间") ) || fields.find(f => f?.type === "datetimerange" && f?.key === "dateRange") || fields.find(f => f?.type === "datetimerange") || null ); } export function resolveTimeRangeFromPayload(payload, timeField) { if (!timeField?.key) return { start: "", end: "" }; const val = payload?.[timeField.key]; if (Array.isArray(val) && val.length >= 2) { return { start: val[0] || "", end: val[1] || "" }; } return parseDatetimerangeValue(val); } export function computeLeaveDays(startStr, endStr) { if (!startStr || !endStr) return null; const t0 = dayjs(startStr); const t1 = dayjs(endStr); if (!t0.isValid() || !t1.isValid() || !t1.isAfter(t0)) return null; const days = t1.diff(t0, "millisecond") / (24 * 60 * 60 * 1000); return Math.round(days * 100) / 100; } export function computeLeaveDurationDisplay(fields, formPayload) { const timeField = findLeaveTimeTemplateField(fields); const { start, end } = resolveTimeRangeFromPayload(formPayload, timeField); const d = computeLeaveDays(start, end); return d == null ? "" : String(d); } export function validateLeaveBeforeSubmit(fields, formPayload) { const timeField = findLeaveTimeTemplateField(fields); const { start, end } = resolveTimeRangeFromPayload(formPayload, timeField); if (computeLeaveDays(start, end) == null) { return "请检查模板中的请假时间,结束时间须晚于开始时间"; } return ""; } /* ---------- 加班 ---------- */ export function isOvertimeDurationField(field) { const label = String(field?.label || ""); return label.includes("加班时长") || field?.key === "overtimeHours"; } export function displayOvertimeTemplateFields(fields = []) { return (fields || []).filter(f => !isOvertimeDurationField(f)); } export function findOvertimeTimeTemplateField(fields = []) { return ( fields.find( f => f?.type === "datetimerange" && String(f?.label || "").includes("加班时间") ) || fields.find(f => f?.type === "datetimerange") || null ); } export function computeOvertimeHours(startStr, endStr) { if (!startStr || !endStr) return null; const t0 = dayjs(startStr); const t1 = dayjs(endStr); if (!t0.isValid() || !t1.isValid() || !t1.isAfter(t0)) return null; return Math.round((t1.diff(t0, "millisecond") / 3600000) * 100) / 100; } export function computeOvertimeHoursDisplay(fields, formPayload) { const field = findOvertimeTimeTemplateField(fields); const { start, end } = resolveTimeRangeFromPayload(formPayload, field); const h = computeOvertimeHours(start, end); return h == null ? "" : String(h); } export function validateOvertimeBeforeSubmit(fields, formPayload) { const field = findOvertimeTimeTemplateField(fields); const { start, end } = resolveTimeRangeFromPayload(formPayload, field); if (computeOvertimeHours(start, end) == null) { return "请检查模板中的加班时间,结束时间须晚于开始时间"; } return ""; } /* ---------- 调岗 ---------- */ export function isOriginalPostField(field) { const label = String(field?.label || ""); return ( label.includes("原岗位") || field?.key === "originalPost" || field?.key === "originalPostName" || field?.key === "originalPostId" ); } export function displayTransferTemplateFields(fields = []) { return (fields || []).filter(f => !isOriginalPostField(f)); } export function unwrapUserArray(payload) { if (Array.isArray(payload)) return payload; if (payload?.data && Array.isArray(payload.data)) return payload.data; if (payload?.rows && Array.isArray(payload.rows)) return payload.rows; return []; } export function isActiveUser(u) { if (u?.delFlag === "2" || u?.delFlag === 2) return false; if (u?.status == null) return true; return String(u.status) === "0"; } export function firstPostId(user) { if (!user) return undefined; if (Array.isArray(user.postIds) && user.postIds.length) return user.postIds[0]; if (user.postId != null && user.postId !== "") return user.postId; return undefined; } export function buildPostIdToNameMap(postRows = []) { const m = {}; for (const p of postRows) { const id = p.postId ?? p.value ?? p.id; if (id != null && id !== "") { m[String(id)] = p.postName ?? p.label ?? p.name ?? ""; } } return m; } export function resolveOriginalPostName(user, postIdToName = {}) { if (!user) return ""; const nameStr = (user.postName ?? user.postname ?? "").toString().trim(); if (nameStr) return nameStr; if (Array.isArray(user.posts) && user.posts.length) { return (user.posts[0].postName ?? "").toString() || "未命名岗位"; } const pid = firstPostId(user); if (pid != null && pid !== "") { const n = postIdToName[String(pid)] || ""; return n || "当前岗位(未在岗位字典中)"; } return "未分配岗位"; } export function userById(users, id) { if (id == null || id === "") return undefined; return (users || []).find(u => String(u.userId ?? u.id) === String(id)); } /** 按 moduleKey 过滤模板填报项 */ export function displayTemplateFieldsByModule(moduleKey, fields = []) { if (moduleKey === APPROVAL_MODULE_KEYS.LEAVE) { return displayLeaveTemplateFields(fields); } if (moduleKey === APPROVAL_MODULE_KEYS.OVERTIME) { return displayOvertimeTemplateFields(fields); } if (moduleKey === APPROVAL_MODULE_KEYS.TRANSFER) { return displayTransferTemplateFields(fields); } return fields || []; } /** 保存前将业务扩展字段写入 formValues */ export function syncModuleExtrasToFormValues(moduleKey, formValues, extras, fields) { if (!moduleKey || !formValues) return; if (moduleKey === APPROVAL_MODULE_KEYS.LEAVE) { if (extras.leaveBalanceDays != null && extras.leaveBalanceDays !== "") { formValues.leaveBalanceDays = extras.leaveBalanceDays; } const days = computeLeaveDurationDisplay(fields, formValues); if (days) formValues.leaveDurationDays = days; } if (moduleKey === APPROVAL_MODULE_KEYS.OVERTIME) { const hours = computeOvertimeHoursDisplay(fields, formValues); if (hours) formValues.overtimeHours = hours; } if (moduleKey === APPROVAL_MODULE_KEYS.TRANSFER && extras.originalPostName) { formValues.originalPostName = extras.originalPostName; formValues.originalPost = extras.originalPostName; } } /** 业务扩展校验 */ export function validateModuleExtras(moduleKey, fields, formPayload, extras) { if (moduleKey === APPROVAL_MODULE_KEYS.LEAVE) { if ( extras.leaveBalanceDays == null || extras.leaveBalanceDays === "" || Number.isNaN(Number(extras.leaveBalanceDays)) ) { return "请填写假期余额"; } const msg = validateLeaveBeforeSubmit(fields, formPayload); if (msg) return msg; } if (moduleKey === APPROVAL_MODULE_KEYS.OVERTIME) { const msg = validateOvertimeBeforeSubmit(fields, formPayload); if (msg) return msg; } return ""; } /** 从实例 businessType 推断 moduleKey(编辑入口未带 moduleKey 时) */ export function inferModuleKeyFromRow(row, typeOptions = []) { const bt = row?.businessType; if (bt == null || bt === "") return ""; for (const key of Object.values(APPROVAL_MODULE_KEYS)) { const types = getModuleMatchingBusinessTypes(key, typeOptions); if (types.some(t => matchBusinessTypeValue(t, bt))) return key; const cfg = APPROVAL_MODULE_REGISTRY[key]; if (cfg && matchBusinessTypeValue(cfg.approvalType, bt)) return key; } return ""; } /** 编辑回显:从实例行恢复扩展字段 */ export function loadModuleExtrasFromRow(moduleKey, row, formPayload) { const extras = { leaveBalanceDays: undefined, originalPostName: "", }; if (!moduleKey || !row) return extras; const payload = formPayload || {}; if (moduleKey === APPROVAL_MODULE_KEYS.LEAVE) { const v = payload.leaveBalanceDays ?? row.leaveBalanceDays; extras.leaveBalanceDays = v != null && v !== "" ? Number(v) : undefined; } if (moduleKey === APPROVAL_MODULE_KEYS.TRANSFER) { extras.originalPostName = payload.originalPostName || payload.originalPost || row.originalPostName || ""; } return extras; }