import { APPROVAL_MODULE_KEYS, APPROVAL_MODULE_REGISTRY, getApprovalModuleConfig, getModuleMatchingBusinessTypes, } from "./approvalModuleRegistry.js"; import { parseApprovalFormConfig, parseDatetimerangeValue, } from "./approvalFormField.js"; import { matchBusinessTypeValue } from "./approvalTemplateType.js"; /** 与 Web leave-apply LEAVE_TYPE_OPTIONS 一致 */ export const LEAVE_TYPE_OPTIONS = [ { label: "年假", value: "annual" }, { label: "病假", value: "sick" }, { label: "事假", value: "personal" }, { label: "婚假", value: "marriage" }, { label: "产假", value: "maternity" }, { label: "哺乳假", value: "nursing" }, { label: "慰唁假", value: "condolence" }, { label: "调休", value: "compensatory" }, ]; /** 与 Web overtime-apply OVERTIME_TYPE_OPTIONS 一致 */ export const OVERTIME_TYPE_OPTIONS = [ { label: "工作日加班", value: "weekday" }, { label: "休息日加班", value: "weekend" }, { label: "法定节假日加班", value: "holiday" }, ]; export const HANDOVER_STATUS_OPTIONS = [ { label: "进行中", value: "in_progress" }, { label: "已完成", value: "completed" }, { label: "已退回", value: "returned" }, ]; export const HANDOVER_TYPE_OPTIONS = [ { label: "离职交接", value: "resignation" }, { label: "调岗交接", value: "transfer" }, ]; function buildFormPayloadFromFields(fields = []) { const payload = {}; for (const f of fields) { if (!f?.key) continue; const val = f.value ?? f.defaultValue; if (val !== undefined && val !== null && val !== "") { payload[f.key] = val; } } return payload; } function guessFieldTypeFromValue(val) { if (Array.isArray(val)) return "datetimerange"; if (typeof val === "number") return "number"; return "text"; } /** 解析实例 formConfig / formPayload(与 Web resolveInstanceFormFields 对齐) */ export function resolveInstanceFormPayload(row) { const cfg = parseApprovalFormConfig(row?.formConfig); let fields = (row?.formFieldDefs?.length ? row.formFieldDefs : cfg.fields) || []; const formPayload = { ...(fields.length ? buildFormPayloadFromFields(fields) : {}), ...cfg.formPayload, ...(row?.formPayload || {}), }; if (!fields.length && Object.keys(formPayload).length) { fields = Object.keys(formPayload) .filter(k => k && k !== "summary") .map(k => ({ key: k, label: k, type: guessFieldTypeFromValue(formPayload[k]), required: false, options: [], })); } fields = fields.map(field => ({ ...field, value: formPayload[field.key] ?? field.value ?? field.defaultValue ?? "", })); return { fields, formPayload }; } /** 已知下拉字段 value → 展示文案(模板未带 options 时兜底) */ export function formatKnownSelectLabel(prop, val) { if (val === undefined || val === null || val === "") return ""; const maps = { leaveType: LEAVE_TYPE_OPTIONS, overtimeType: OVERTIME_TYPE_OPTIONS, handoverStatus: HANDOVER_STATUS_OPTIONS, handoverType: HANDOVER_TYPE_OPTIONS, }; const options = maps[prop]; if (!options) return ""; const hit = options.find(o => String(o.value) === String(val)); return hit?.label || ""; } export function getRowPayloadValue(row, keys) { const keyList = Array.isArray(keys) ? keys : [keys]; const { formPayload } = resolveInstanceFormPayload(row); for (const k of keyList) { if (row?.[k] != null && row[k] !== "") return row[k]; if (formPayload[k] != null && formPayload[k] !== "") return formPayload[k]; } return ""; } const DATETIME_RANGE_KEYS = [ "dateRange", "leaveTime", "overtimeTime", "timeRange", ]; function pickDatetimerangeRaw(formPayload, fields = []) { for (const key of DATETIME_RANGE_KEYS) { const v = formPayload?.[key]; if (v != null && v !== "") return v; const field = (fields || []).find(f => f?.key === key); const fv = field?.value ?? field?.defaultValue; if (fv != null && fv !== "") return fv; } const rangeField = (fields || []).find( f => String(f?.type || "").toLowerCase() === "datetimerange" ); if (rangeField?.key) { const v = formPayload?.[rangeField.key] ?? rangeField.value ?? rangeField.defaultValue; if (v != null && v !== "") return v; } return null; } function splitRangeValue(val) { if (val === undefined || val === null || val === "") { return { start: "", end: "" }; } if (Array.isArray(val)) { return { start: val[0] || "", end: val[1] || "" }; } return parseDatetimerangeValue(val); } /** * 列表列 prop 与 formPayload 对齐(请假 startTime/endTime 来自 dateRange) */ export function resolveListFieldRawValue(prop, row, fields = [], formPayload = {}) { const payload = formPayload || {}; const direct = payload[prop] ?? row?.[prop]; if (prop === "startTime" || prop === "endTime") { if (direct != null && direct !== "") return direct; const altStart = payload.start ?? payload.startDate ?? payload.beginTime ?? row?.startTime; const altEnd = payload.end ?? payload.endDate ?? payload.finishTime ?? row?.endTime; if (prop === "startTime" && altStart) return altStart; if (prop === "endTime" && altEnd) return altEnd; const { start, end } = splitRangeValue(pickDatetimerangeRaw(payload, fields)); return prop === "startTime" ? start : end; } if (prop === "overtimeDate") { const d = payload.overtimeDate ?? payload.date ?? direct; if (d != null && d !== "") return Array.isArray(d) ? d[0] || "" : d; const { start } = splitRangeValue(pickDatetimerangeRaw(payload, fields)); return start || ""; } if (direct != null && direct !== "") return direct; const hit = (fields || []).find(f => f?.key === prop); return hit?.value ?? hit?.defaultValue ?? ""; } function pickDateRange(searchForm) { const range = searchForm?.createTimeRange ?? searchForm?.applyDateRange ?? searchForm?.transferDateRange; if (!Array.isArray(range) || !range[0]) return {}; const out = { createTimeStart: range[0] }; if (range[1]) out.createTimeEnd = range[1]; return out; } /** 各模块默认查询表单(与 Web searchForm 字段一致) */ export function createModuleSearchForm(moduleKey) { switch (moduleKey) { case APPROVAL_MODULE_KEYS.REGULAR: return { applicantName: "", applyDateRange: null }; case APPROVAL_MODULE_KEYS.TRANSFER: return { applicantId: "", transferDateRange: null }; case APPROVAL_MODULE_KEYS.WORK_HANDOVER: return { applicantId: "", handoverStatus: "", handoverType: "" }; case APPROVAL_MODULE_KEYS.LEAVE: return { applicantKeyword: "", leaveType: "" }; case APPROVAL_MODULE_KEYS.OVERTIME: return { applicantKeyword: "", overtimeType: "" }; case APPROVAL_MODULE_KEYS.TRAVEL_REIMBURSE: case APPROVAL_MODULE_KEYS.COST_REIMBURSE: case APPROVAL_MODULE_KEYS.ENTERPRISE_NEWS: return { applicantKeyword: "" }; default: return {}; } } /** 服务端 listPage DTO 片段(与 Web buildExtraListParams + buildApprovalInstanceListParams 一致) */ export function buildModuleListDto(moduleKey, searchForm = {}) { const sf = searchForm || {}; const dto = { ...pickDateRange(sf) }; switch (moduleKey) { case APPROVAL_MODULE_KEYS.REGULAR: { const name = (sf.applicantName || "").trim(); if (name) dto.applicantName = name; break; } case APPROVAL_MODULE_KEYS.TRANSFER: break; case APPROVAL_MODULE_KEYS.WORK_HANDOVER: break; case APPROVAL_MODULE_KEYS.LEAVE: case APPROVAL_MODULE_KEYS.OVERTIME: break; default: break; } return dto; } function matchApplicantKeyword(row, keyword) { const kw = (keyword || "").trim().toLowerCase(); if (!kw) return true; const parts = [ row?.applicantName, row?.applicantNo, row?.applicantId, getRowPayloadValue(row, ["applicant", "applicantName", "applicantId"]), ] .filter(v => v != null && v !== "") .map(v => String(v).toLowerCase()); return parts.some(p => p.includes(kw)); } function matchSelectValue(row, keys, expected) { if (!expected) return true; const raw = getRowPayloadValue(row, keys); return String(raw) === String(expected); } function matchApplicantId(row, applicantId) { if (!applicantId) return true; const id = String(applicantId); if (row?.applicantId != null && String(row.applicantId) === id) return true; const payloadApplicant = getRowPayloadValue(row, [ "applicant", "applicantId", "applicantUserId", ]); return String(payloadApplicant) === id; } /** 按模块 businessType / 标题归属过滤(服务端未生效时的兜底) */ export function filterRowsByModuleBusinessType(moduleKey, rows, typeOptions = []) { const cfg = getApprovalModuleConfig(moduleKey); if (!cfg) return rows; const types = getModuleMatchingBusinessTypes(moduleKey, typeOptions); const myLabels = [cfg.label, ...(cfg.typeLabels || [])].filter(Boolean); return (rows || []).filter(row => { if (types.length && row?.businessType != null && row.businessType !== "") { if (types.some(t => matchBusinessTypeValue(row.businessType, t))) { return true; } } const title = String(row?.title || row?.templateName || "").trim(); if (title) { if (myLabels.some(l => title === l || title.includes(l))) return true; for (const [key, other] of Object.entries(APPROVAL_MODULE_REGISTRY)) { if (key === moduleKey) continue; const otherLabels = [other.label, ...(other.typeLabels || [])].filter(Boolean); if (otherLabels.some(l => title === l || (l.length > 2 && title.includes(l)))) { return false; } } } return types.length === 0; }); } /** 前端筛选(Web 未下发接口的字段与 Web 行为一致) */ export function filterRowsByModuleSearch(moduleKey, rows, searchForm = {}) { const sf = searchForm || {}; const list = Array.isArray(rows) ? rows : []; switch (moduleKey) { case APPROVAL_MODULE_KEYS.TRANSFER: return list.filter( row => matchApplicantId(row, sf.applicantId) && matchApplicantKeyword(row, sf.applicantKeyword) ); case APPROVAL_MODULE_KEYS.WORK_HANDOVER: return list.filter( row => matchApplicantId(row, sf.applicantId) && matchSelectValue(row, ["handoverStatus", "交接状态"], sf.handoverStatus) && matchSelectValue(row, ["handoverType", "交接类型"], sf.handoverType) ); case APPROVAL_MODULE_KEYS.LEAVE: return list.filter( row => matchApplicantKeyword(row, sf.applicantKeyword) && matchSelectValue(row, ["leaveType", "请假类型"], sf.leaveType) ); case APPROVAL_MODULE_KEYS.OVERTIME: return list.filter( row => matchApplicantKeyword(row, sf.applicantKeyword) && matchSelectValue(row, ["overtimeType", "加班类型"], sf.overtimeType) ); case APPROVAL_MODULE_KEYS.TRAVEL_REIMBURSE: case APPROVAL_MODULE_KEYS.COST_REIMBURSE: case APPROVAL_MODULE_KEYS.ENTERPRISE_NEWS: return list.filter(row => matchApplicantKeyword(row, sf.applicantKeyword)); default: return list; } } /** 模块筛选 UI 配置 */ export function getModuleSearchMeta(moduleKey) { switch (moduleKey) { case APPROVAL_MODULE_KEYS.REGULAR: return { fields: [ { key: "applicantName", type: "input", label: "申请人", placeholder: "请输入申请人" }, { key: "applyDateRange", type: "daterange", label: "申请日期" }, ], }; case APPROVAL_MODULE_KEYS.TRANSFER: return { fields: [ { key: "applicantId", type: "user", label: "申请人", placeholder: "请选择申请人" }, { key: "transferDateRange", type: "daterange", label: "转岗时间" }, ], }; case APPROVAL_MODULE_KEYS.WORK_HANDOVER: return { fields: [ { key: "applicantId", type: "user", label: "申请人", placeholder: "请选择申请人" }, { key: "handoverStatus", type: "select", label: "交接状态", options: HANDOVER_STATUS_OPTIONS, }, { key: "handoverType", type: "select", label: "交接类型", options: HANDOVER_TYPE_OPTIONS, }, ], }; case APPROVAL_MODULE_KEYS.LEAVE: return { fields: [ { key: "applicantKeyword", type: "input", label: "申请人", placeholder: "姓名或编号", }, { key: "leaveType", type: "select", label: "请假类型", options: LEAVE_TYPE_OPTIONS, }, ], }; case APPROVAL_MODULE_KEYS.OVERTIME: return { fields: [ { key: "applicantKeyword", type: "input", label: "申请人", placeholder: "姓名或编号", }, { key: "overtimeType", type: "select", label: "加班类型", options: OVERTIME_TYPE_OPTIONS, }, ], }; case APPROVAL_MODULE_KEYS.TRAVEL_REIMBURSE: case APPROVAL_MODULE_KEYS.COST_REIMBURSE: case APPROVAL_MODULE_KEYS.ENTERPRISE_NEWS: return { fields: [ { key: "applicantKeyword", type: "input", label: "申请人", placeholder: "姓名或编号", }, ], }; default: return { fields: [] }; } } export function resetModuleSearchForm(moduleKey, target) { const defaults = createModuleSearchForm(moduleKey); Object.keys(target).forEach(k => { if (!(k in defaults)) delete target[k]; }); Object.assign(target, defaults); } export function formatDateRangeLabel(range) { if (!Array.isArray(range) || !range[0]) return ""; if (range[1]) return `${range[0]} 至 ${range[1]}`; return range[0]; } export function userSelectLabel(u) { const nick = u?.nickName || ""; const name = u?.userName || ""; if (nick && name && nick !== name) return `${nick}(${name})`; return nick || name || `用户${u?.userId ?? u?.id ?? ""}`; }