| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import { createEmptyNode, formatDisplayTime, mapNodesFromApi, mapSignModeFromApi, mapSignModeToApi, normalizeFlowNodes, nodeSignModeLabel } from "../approve-template/approveTemplateConstants.js"; |
| | | import { buildFormPayloadFromFields, parseFormConfigToData } from "../approve-template/formConfigUtils.js"; |
| | | import { isDynamicOptionSource, resolveSelectDisplayLabel } from "../approve-template/selectOptionSource.js"; |
| | | import { appendDotNotationQuery, buildApprovalInstanceSearchDto } from "../approve-shared/approvalInstanceListSearch.js"; |
| | | |
| | | /** 审æ¹ç±»åï¼ä¸åç«¯åæ®µ 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" }, |
| | | { value: "enterprise_news", label: "ä¼ä¸æ°é»", cellBg: "#ecf5ff", cellColor: "#409eff" }, |
| | | ]; |
| | | |
| | | /** å表æ¥è¯¢ï¼å®¡æ¹ç¶æï¼ä¸å端 status æä¸¾ä¸è´ï¼ */ |
| | | export const APPROVAL_STATUS_SEARCH_OPTIONS = [ |
| | | { value: "DRAFT", label: "è稿" }, |
| | | { value: "PENDING", label: "å¾
审æ¹" }, |
| | | { value: "APPROVED", label: "å·²éè¿" }, |
| | | { value: "REJECTED", label: "已驳å" }, |
| | | ]; |
| | | |
| | | /** |
| | | * 审æ¹ç¶æå±ç¤ºï¼ä¸å端 status æä¸¾ä¸è´ï¼ |
| | | * DRAFTâè稿 PENDINGâå¾
审æ¹/è¿è¡ä¸ APPROVEDâå·²éè¿/已宿 REJECTEDâ已驳å |
| | | */ |
| | | export const APPROVAL_STATUS_OPTIONS = [ |
| | | { value: "draft", api: "DRAFT", label: "è稿" }, |
| | | { value: "pending", api: "PENDING", label: "å¾
审æ¹" }, |
| | | { value: "approved", api: "APPROVED", label: "å·²éè¿" }, |
| | | { value: "rejected", api: "REJECTED", label: "已驳å" }, |
| | | { value: "cancelled", api: "CANCELLED", label: "å·²æ¤é" }, |
| | | ]; |
| | | |
| | | /** æ°åç¶æç ï¼é¨ååç«¯ç¨ 0/1/2ï¼ */ |
| | | const STATUS_NUMERIC_MAP = { |
| | | 0: "pending", |
| | | 1: "approved", |
| | | 2: "rejected", |
| | | 3: "cancelled", |
| | | 4: "cancelled", |
| | | }; |
| | | |
| | | /** å端 status / é¡µé¢ approvalStatus â ç»ä¸é¡µé¢ keyï¼pending | approved | rejected | cancelledï¼ */ |
| | | export function normalizeApprovalStatusKey(v) { |
| | | if (v == null || v === "") return "pending"; |
| | | if (typeof v === "number" || (typeof v === "string" && /^\d+$/.test(v.trim()))) { |
| | | const numKey = STATUS_NUMERIC_MAP[Number(v)]; |
| | | if (numKey) return numKey; |
| | | } |
| | | const s = String(v).trim(); |
| | | if (!s) return "pending"; |
| | | const upper = s.toUpperCase(); |
| | | if (upper === "DRAFT") return "draft"; |
| | | if (upper === "PUBLISHED") return "approved"; |
| | | if (upper === "OFFLINE") return "cancelled"; |
| | | if (upper === "APPROVED" || upper === "APPROVE" || upper === "PASS" || upper === "AGREE") { |
| | | return "approved"; |
| | | } |
| | | if (upper === "REJECTED" || upper === "REJECT" || upper === "REFUSE" || upper === "REFUSED" || upper === "DENIED") { |
| | | return "rejected"; |
| | | } |
| | | if (upper === "CANCELLED" || upper === "CANCEL" || upper === "REVOKED") return "cancelled"; |
| | | if (upper === "PENDING" || upper === "IN_PROGRESS" || upper === "PROCESSING" || upper === "RUNNING" || upper === "WAIT" || upper === "WAITING") { |
| | | return "pending"; |
| | | } |
| | | if (s.includes("è稿")) return "draft"; |
| | | if (s.includes("驳å") || s.includes("æç»")) return "rejected"; |
| | | if (s.includes("ä¸çº¿")) return "cancelled"; |
| | | if (s.includes("æ¤é")) return "cancelled"; |
| | | if (s.includes("åå¸") || s.includes("éè¿") || s.includes("宿")) return "approved"; |
| | | if (s.includes("å¾
审") || s.includes("è¿è¡ä¸") || s.includes("审æ¹ä¸")) return "pending"; |
| | | const lower = s.toLowerCase(); |
| | | if (["draft", "pending", "approved", "rejected", "cancelled"].includes(lower)) return lower; |
| | | return "pending"; |
| | | } |
| | | |
| | | /** ä»å表/详æ
è¡è§£æå端åå§ç¶æï¼å
¼å®¹å¤å段å½åï¼ */ |
| | | export function resolveInstanceStatusRaw(row) { |
| | | if (!row || typeof row !== "object") return ""; |
| | | const candidates = [ |
| | | row.status, |
| | | row.statusRaw, |
| | | row.approvalStatus, |
| | | row.statusName, |
| | | row.statusLabel, |
| | | row.approvalStatusName, |
| | | row.statusDesc, |
| | | row.instanceStatus, |
| | | row.approvalInstanceStatus, |
| | | row.approveStatus, |
| | | row.auditStatus, |
| | | row.approvalInstance?.status, |
| | | row.approvalInstanceVo?.status, |
| | | ]; |
| | | for (const c of candidates) { |
| | | if (c != null && c !== "") return c; |
| | | } |
| | | const tasks = row.tasks; |
| | | if (Array.isArray(tasks) && tasks.length) { |
| | | const rejected = tasks.some(t => normalizeApprovalStatusKey(t?.status ?? t?.taskStatus) === "rejected"); |
| | | if (rejected) return "REJECTED"; |
| | | const allApproved = tasks.every(t => normalizeApprovalStatusKey(t?.status ?? t?.taskStatus) === "approved"); |
| | | if (allApproved) return "APPROVED"; |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | /** æäº¤å¼¹çªï¼æ¨¡æ¿å¡çï¼æ¥èªå端åè¡¨ï¼ */ |
| | | export function mapSubmitTemplateCard(row) { |
| | | const cfg = parseFormConfigToData(row?.formConfig); |
| | | return { |
| | | id: row?.id, |
| | | key: String(row?.id ?? ""), |
| | | businessType: row?.businessType ?? cfg.approvalType ?? row?.approvalType ?? "", |
| | | approvalType: cfg.approvalType || row?.approvalType || "", |
| | | label: row?.templateName || "â", |
| | | summaryPlaceholder: (row?.description || "").trim() || cfg.summaryPlaceholder || "ç¹å»å¡«åå¹¶æäº¤", |
| | | }; |
| | | } |
| | | |
| | | export function matchBusinessTypeValue(a, b) { |
| | | if (a == null || a === "" || b == null || b === "") return false; |
| | | return a === b || a === Number(b) || Number(a) === b || String(a) === String(b); |
| | | } |
| | | |
| | | /** 审æ¹è®°å½ approveAction â é¡µé¢ result */ |
| | | export function mapRecordResultFromApi(action) { |
| | | const s = String(action || "").toUpperCase(); |
| | | if (s === "APPROVED" || s === "APPROVE" || s === "PASS") return "approved"; |
| | | if (s === "REJECTED" || s === "REJECT" || s === "REFUSE") return "rejected"; |
| | | return "pending"; |
| | | } |
| | | |
| | | /** å端 records â æ¶é´çº¿å±ç¤ºç»æ */ |
| | | export function mapRecordsFromApi(records) { |
| | | const list = Array.isArray(records) ? records : []; |
| | | return list.map(r => ({ |
| | | id: r.id, |
| | | operatorName: r.approverName || r.operatorName || r.createUserName || "", |
| | | result: mapRecordResultFromApi(r.approveAction ?? r.action ?? r.status), |
| | | opinion: r.approveComment || r.comment || r.opinion || "", |
| | | time: formatDisplayTime(r.approveTime || r.createTime || r.time || ""), |
| | | raw: r, |
| | | })); |
| | | } |
| | | |
| | | export function mapTaskStatusLabel(status) { |
| | | return approvalStatusLabel(status); |
| | | } |
| | | |
| | | export function mapTaskStatusTagType(status) { |
| | | return approvalStatusTagType(status); |
| | | } |
| | | |
| | | /** å端 tasks â é¡µé¢ flowNodesï¼æ levelNo åç»ï¼ä¾æµç¨ç¼è¾/å±ç¤ºï¼ */ |
| | | export function mapTasksToFlowNodes(tasks) { |
| | | const list = Array.isArray(tasks) ? tasks : []; |
| | | if (!list.length) return []; |
| | | const byLevel = new Map(); |
| | | list.forEach(t => { |
| | | const level = Number(t.levelNo ?? t.taskLevel ?? t.nodeOrder ?? 1); |
| | | if (!byLevel.has(level)) { |
| | | byLevel.set(level, { |
| | | id: t.nodeId, |
| | | templateId: t.templateId, |
| | | nodeOrder: level, |
| | | signMode: mapSignModeFromApi(t.approveType), |
| | | approvers: [], |
| | | tasks: [], |
| | | }); |
| | | } |
| | | const node = byLevel.get(level); |
| | | node.approvers.push({ |
| | | id: t.id, |
| | | nodeId: t.nodeId, |
| | | templateId: t.templateId, |
| | | approverId: t.approverId, |
| | | approverName: t.approverName || "", |
| | | status: t.status, |
| | | approveComment: t.approveComment, |
| | | approveTime: t.approveTime, |
| | | }); |
| | | node.tasks.push(t); |
| | | if (t.approveType != null) { |
| | | node.signMode = mapSignModeFromApi(t.approveType); |
| | | } |
| | | }); |
| | | return [...byLevel.entries()].sort(([a], [b]) => a - b).map(([, node]) => node); |
| | | } |
| | | |
| | | /** é¡µé¢ flowNodes â å端 tasks */ |
| | | export function mapFlowNodesToTasks(flowNodes, { instanceId, templateId } = {}) { |
| | | const nodes = normalizeFlowNodes(flowNodes); |
| | | const tasks = []; |
| | | nodes.forEach(n => { |
| | | const levelNo = n.nodeOrder ?? 1; |
| | | const approveType = mapSignModeToApi(n.signMode); |
| | | n.approvers.forEach((a, idx) => { |
| | | const task = { |
| | | levelNo, |
| | | approveType, |
| | | approverId: a.approverId, |
| | | approverName: a.approverName || "", |
| | | sortNo: a.sortNo ?? idx + 1, |
| | | }; |
| | | if (a.id != null) task.id = a.id; |
| | | if (a.nodeId != null) task.nodeId = a.nodeId; |
| | | if (a.templateId != null) task.templateId = a.templateId; |
| | | else if (templateId) task.templateId = templateId; |
| | | if (instanceId) task.instanceId = instanceId; |
| | | if (a.status != null) task.status = a.status; |
| | | tasks.push(task); |
| | | }); |
| | | }); |
| | | return tasks; |
| | | } |
| | | |
| | | function guessFieldTypeFromValue(val) { |
| | | if (Array.isArray(val) && val.length === 2) return "datetimerange"; |
| | | if (typeof val === "number") return "number"; |
| | | if (typeof val === "string" && /^\d{4}-\d{2}-\d{2}$/.test(val)) return "date"; |
| | | if (typeof val === "string" && val.length > 100) return "textarea"; |
| | | return "text"; |
| | | } |
| | | |
| | | /** |
| | | * ååæ®µå±ç¤ºå¼ï¼è¯¦æ
åªè¯»ãåè¡¨ä¸»è¡¨ï¼ |
| | | * @param {object} [caches] 人å/é¨é¨ä¸æç¼åï¼ç¨äºè§£æã人åå表ãç±»åæ®µä¸ºå§å |
| | | */ |
| | | export function formatFieldDisplayValue(field, val, caches) { |
| | | if (val == null || val === "" || (Array.isArray(val) && !val.length)) return "â"; |
| | | if (field?.type === "select" && isDynamicOptionSource(field.optionSource)) { |
| | | const label = resolveSelectDisplayLabel(field, val, caches || {}); |
| | | if (label && label !== "â") return label; |
| | | return String(val); |
| | | } |
| | | if (field?.type === "select" && field.options?.length) { |
| | | const hit = field.options.find(o => String(o.value) === String(val)); |
| | | return hit?.label || String(val); |
| | | } |
| | | if (Array.isArray(val)) return val.join(" è³ "); |
| | | return String(val); |
| | | } |
| | | |
| | | /** |
| | | * ä»è¡æ°æ® / formConfig è§£æå¡«æ¥å段å®ä¹ä¸ formPayloadï¼ä¸æ°å¢æäº¤ç»æä¸è´ï¼ |
| | | */ |
| | | export function resolveInstanceFormFields(row) { |
| | | const cfg = parseInstanceFormConfig(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, |
| | | rows: 3, |
| | | min: 0, |
| | | precision: 0, |
| | | options: [], |
| | | })); |
| | | } |
| | | const templateSnapshot = { |
| | | label: row?.templateName || row?.title || "审æ¹", |
| | | approvalType: cfg.approvalType || row?.approvalType || "", |
| | | summaryPlaceholder: cfg.summaryPlaceholder || "", |
| | | templateId: row?.templateId, |
| | | fields, |
| | | }; |
| | | return { fields, formPayload, templateSnapshot, formConfigData: cfg }; |
| | | } |
| | | |
| | | /** è§£æå®ä¾ formConfig */ |
| | | export function parseInstanceFormConfig(formConfig) { |
| | | let raw = {}; |
| | | if (formConfig) { |
| | | if (typeof formConfig === "object") raw = formConfig; |
| | | else { |
| | | try { |
| | | raw = JSON.parse(formConfig); |
| | | } catch { |
| | | raw = {}; |
| | | } |
| | | } |
| | | } |
| | | const data = parseFormConfigToData(formConfig); |
| | | const payload = raw.formPayload; |
| | | return { |
| | | summaryPlaceholder: raw.summaryPlaceholder || data.summaryPlaceholder || "", |
| | | approvalType: raw.approvalType || "", |
| | | fields: data.fields || [], |
| | | formPayload: payload && typeof payload === "object" ? payload : {}, |
| | | }; |
| | | } |
| | | |
| | | export function unwrapInstanceDetail(res) { |
| | | const data = res?.data ?? res; |
| | | if (!data || typeof data !== "object") return {}; |
| | | if (data.id != null || data.instanceNo) return data; |
| | | if (data.approvalInstanceVo) return data.approvalInstanceVo; |
| | | return data; |
| | | } |
| | | |
| | | /** å¡«æ¥å
容 + 模æ¿å段å®ä¹ â formConfig JSON */ |
| | | export function buildInstanceFormConfigJson(templateSnapshot, formPayload) { |
| | | const payload = formPayload || {}; |
| | | return JSON.stringify({ |
| | | summaryPlaceholder: templateSnapshot?.summaryPlaceholder || "", |
| | | approvalType: templateSnapshot?.approvalType || "", |
| | | fields: templateSnapshot?.fields || [], |
| | | formPayload: payload, |
| | | }); |
| | | } |
| | | |
| | | /** ç»è£
ä¿å/æ´æ°å®¡æ¹ DTO */ |
| | | export function buildInstanceDto({ submitForm, activeTemplate, userStore, flowNodes, existingRow }) { |
| | | const payload = submitForm?.formPayload || {}; |
| | | const tpl = activeTemplate || {}; |
| | | const title = String(payload.summary || payload.title || "").trim() || tpl.label || submitForm?.templateName || "审æ¹ç³è¯·"; |
| | | const templateId = submitForm?.templateId || tpl.templateId; |
| | | const instanceId = existingRow?.id ?? submitForm?.instanceId; |
| | | const taskList = mapFlowNodesToTasks(flowNodes || submitForm?.flowNodes, { |
| | | instanceId, |
| | | templateId, |
| | | }); |
| | | const isUpdate = Boolean(instanceId); |
| | | |
| | | const dto = { |
| | | templateId, |
| | | templateName: submitForm?.templateName || tpl.label || "", |
| | | businessType: tpl.businessType ?? submitForm?.businessType ?? "", |
| | | title, |
| | | formConfig: buildInstanceFormConfigJson({ ...tpl, fields: tpl.fields || submitForm?.formFieldDefs }, payload), |
| | | tasks: taskList, |
| | | }; |
| | | |
| | | const attachments = (Array.isArray(submitForm?.storageBlobDTOs) && submitForm.storageBlobDTOs.length ? submitForm.storageBlobDTOs : null) || tpl.storageBlobDTOs; |
| | | if (attachments?.length) dto.storageBlobDTOs = attachments; |
| | | |
| | | if (isUpdate) { |
| | | dto.id = existingRow?.id ?? submitForm?.instanceId; |
| | | dto.instanceNo = existingRow?.instanceNo ?? submitForm?.instanceNo ?? ""; |
| | | dto.status = submitForm?.saveStatusApi || existingRow?.statusRaw || mapInstanceStatusToApi(existingRow?.approvalStatus) || "PENDING"; |
| | | dto.currentLevel = existingRow?.currentLevel ?? submitForm?.currentLevel ?? 1; |
| | | dto.applicantId = existingRow?.applicantId ?? existingRow?.applicantNo; |
| | | dto.applicantName = existingRow?.applicantName || ""; |
| | | } else { |
| | | dto.status = submitForm?.saveStatusApi || "PENDING"; |
| | | dto.currentLevel = 1; |
| | | dto.applicantId = userStore?.id; |
| | | dto.applicantName = userStore?.nickName || userStore?.name || ""; |
| | | } |
| | | return dto; |
| | | } |
| | | |
| | | /** æ ¡éªæäº¤å®¡æ¹æµç¨ï¼ä¸æ¨¡æ¿é¡µè§åä¸è´ï¼ */ |
| | | export function validateSubmitFlowNodes(flowNodes) { |
| | | const nodes = normalizeFlowNodes(flowNodes); |
| | | if (!nodes.length) return { ok: false, message: "请è³å°é
ç½®ä¸ä¸ªå®¡æ¹èç¹" }; |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | if (!nodes[i].approvers.length) { |
| | | return { ok: false, message: `请为第 ${i + 1} 个èç¹éæ©è³å°ä¸å审æ¹äºº` }; |
| | | } |
| | | } |
| | | return { ok: true, nodes }; |
| | | } |
| | | |
| | | /** å端 status â é¡µé¢ approvalStatus */ |
| | | export function mapInstanceStatusFromApi(status) { |
| | | return normalizeApprovalStatusKey(status); |
| | | } |
| | | |
| | | /** å表/详æ
è¡ â é¡µé¢ approvalStatus key */ |
| | | export function mapInstanceApprovalStatusFromRow(row) { |
| | | const raw = resolveInstanceStatusRaw(row); |
| | | return normalizeApprovalStatusKey(raw); |
| | | } |
| | | |
| | | /** é¡µé¢ approvalStatus â å端 status */ |
| | | export function mapInstanceStatusToApi(approvalStatus) { |
| | | const key = normalizeApprovalStatusKey(approvalStatus); |
| | | const hit = APPROVAL_STATUS_OPTIONS.find(x => x.value === key); |
| | | return hit?.api || "PENDING"; |
| | | } |
| | | |
| | | export function unwrapInstancePage(res) { |
| | | const data = res?.data ?? res; |
| | | return { |
| | | records: Array.isArray(data?.records) ? data.records : [], |
| | | total: Number(data?.total ?? 0), |
| | | }; |
| | | } |
| | | |
| | | /** å页å表项 â è¡¨æ ¼è¡ */ |
| | | export function mapInstanceFromApi(row) { |
| | | if (!row) return {}; |
| | | const statusRaw = resolveInstanceStatusRaw(row); |
| | | const approvalStatus = normalizeApprovalStatusKey(statusRaw); |
| | | const createTime = formatDisplayTime(row.createTime ?? row.applyTime ?? ""); |
| | | const applyTime = formatDisplayTime(row.applyTime ?? ""); |
| | | const finishTime = formatDisplayTime(row.finishTime ?? ""); |
| | | const resolved = resolveInstanceFormFields(row); |
| | | const { fields, formPayload, templateSnapshot } = resolved; |
| | | const tasks = Array.isArray(row.tasks) ? row.tasks : []; |
| | | const flowNodes = tasks.length ? mapTasksToFlowNodes(tasks) : mapNodesFromApi(row.nodes || row.flowNodes); |
| | | const approvalRecords = mapRecordsFromApi(row.records); |
| | | return { |
| | | id: row.id, |
| | | bizId: row.instanceNo || String(row.id ?? ""), |
| | | instanceNo: row.instanceNo || "", |
| | | templateId: row.templateId, |
| | | templateName: row.templateName || "", |
| | | businessId: row.businessId, |
| | | businessType: row.businessType, |
| | | businessName: row.businessName || "", |
| | | applicantId: row.applicantId, |
| | | applicantNo: row.applicantId != null ? String(row.applicantId) : "", |
| | | applicantName: row.applicantName || "", |
| | | approvalType: row.approvalType || row.templateName || "", |
| | | unread: Boolean(row.isApprove) && approvalStatus === "pending", |
| | | isApprove: Boolean(row.isApprove), |
| | | approvalStatus, |
| | | statusRaw: statusRaw || row.status, |
| | | createTime, |
| | | applyTime: applyTime === "â" ? "" : applyTime, |
| | | finishTime: finishTime === "â" ? "" : finishTime, |
| | | title: row.title || "", |
| | | summary: row.title || row.templateName || "", |
| | | currentLevel: row.currentLevel, |
| | | formConfig: row.formConfig, |
| | | formPayload, |
| | | formFieldDefs: fields, |
| | | templateSnapshot, |
| | | tasks, |
| | | records: Array.isArray(row.records) ? row.records : [], |
| | | storageBlobVOList: row.storageBlobVOList || [], |
| | | storageBlobDTOs: row.storageBlobVOList || row.storageBlobDTOs || [], |
| | | flowNodes, |
| | | approvalFlowNodes: [], |
| | | currentNodeIndex: 0, |
| | | approvalRecords, |
| | | rejectReason: approvalRecords.find(r => r.result === "rejected")?.opinion || "", |
| | | purchaseContractNumber: row.purchaseContractNumber || "", |
| | | quotationNo: row.quotationNo || "", |
| | | shippingNo: row.shippingNo || "", |
| | | }; |
| | | } |
| | | |
| | | /** å®¡æ¹æä½ï¼ä¸å端 status æä¸¾ä¸è´ */ |
| | | export const APPROVE_ACTION_APPROVED = "APPROVED"; |
| | | export const APPROVE_ACTION_REJECTED = "REJECTED"; |
| | | |
| | | /** é¡µé¢æä½ â approveAction */ |
| | | export function mapApproveActionToApi(uiResult) { |
| | | return uiResult === "rejected" ? APPROVE_ACTION_REJECTED : APPROVE_ACTION_APPROVED; |
| | | } |
| | | |
| | | /** ç»è£
å®¡æ¹æäº¤ DTO */ |
| | | export function buildApproveInstanceDto(row, uiResult, comment) { |
| | | const opinion = (comment || "").trim(); |
| | | return { |
| | | id: row?.id, |
| | | approveAction: mapApproveActionToApi(uiResult), |
| | | approveComment: opinion || (uiResult === "approved" ? "åæ" : ""), |
| | | }; |
| | | } |
| | | |
| | | export function buildApprovalInstanceListParams({ page, searchForm, businessType, extraParams }) { |
| | | const dto = buildApprovalInstanceSearchDto(searchForm, extraParams); |
| | | const bizType = businessType ?? searchForm?.businessType; |
| | | if (bizType != null && bizType !== "") { |
| | | dto.businessType = bizType; |
| | | } |
| | | |
| | | const params = { |
| | | current: page.current, |
| | | size: page.size, |
| | | "page.current": page.current, |
| | | "page.size": page.size, |
| | | ...dto, |
| | | }; |
| | | appendDotNotationQuery(params, "approvalInstanceDto", dto); |
| | | return params; |
| | | } |
| | | |
| | | export function approvalTypeLabel(v) { |
| | | return APPROVAL_TYPE_OPTIONS.find(x => x.value === v)?.label || v || "â"; |
| | | } |
| | | |
| | | 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) { |
| | | const key = normalizeApprovalStatusKey(v); |
| | | return APPROVAL_STATUS_OPTIONS.find(x => x.value === key)?.label || "â"; |
| | | } |
| | | |
| | | /** ä¸å¡ç³è¯·é¡µç¶æææ¡ï¼PENDINGâè¿è¡ä¸ APPROVEDâ已宿 REJECTEDâ已驳å */ |
| | | export function businessApprovalStatusLabel(v) { |
| | | const key = normalizeApprovalStatusKey(v); |
| | | if (key === "draft") return "è稿"; |
| | | if (key === "pending") return "è¿è¡ä¸"; |
| | | if (key === "approved") return "已宿"; |
| | | if (key === "rejected") return "已驳å"; |
| | | if (key === "cancelled") return "å·²æ¤é"; |
| | | return "â"; |
| | | } |
| | | |
| | | /** |
| | | * ä¸å¡ç³è¯·é¡µæ¯å¦å
许修æ¹ï¼äºä¸ªç³è¯·é¡µï¼ |
| | | * è¿è¡ä¸(PENDING)ã已宿(APPROVED) ä¸å¯ä¿®æ¹ï¼å·²é©³åãå·²æ¤éçå¯ä¿®æ¹ |
| | | */ |
| | | export function canEditBusinessInstanceRow(row) { |
| | | const key = normalizeApprovalStatusKey(row?.approvalStatus ?? row?.statusRaw ?? row?.status); |
| | | return key !== "pending" && key !== "approved"; |
| | | } |
| | | |
| | | export function businessApprovalStatusTagType(v) { |
| | | const key = normalizeApprovalStatusKey(v); |
| | | if (key === "draft") return "info"; |
| | | if (key === "approved") return "success"; |
| | | if (key === "rejected") return "danger"; |
| | | if (key === "cancelled") return "info"; |
| | | return "warning"; |
| | | } |
| | | |
| | | export function approvalStatusTagType(v) { |
| | | const key = normalizeApprovalStatusKey(v); |
| | | if (key === "draft") return "info"; |
| | | if (key === "approved") return "success"; |
| | | if (key === "rejected") return "danger"; |
| | | if (key === "cancelled") return "info"; |
| | | return "warning"; |
| | | } |
| | | |
| | | /** åè¡¨è¡ â ç¼è¾è¡¨åï¼ä»
ç¨è¡æ°æ®åæ¾ï¼ */ |
| | | export function buildEditFormFromInstanceRow(row) { |
| | | const { fields, formPayload, templateSnapshot } = resolveInstanceFormFields(row); |
| | | const normalized = normalizeFlowNodes(row?.flowNodes?.length ? row.flowNodes : mapTasksToFlowNodes(row?.tasks)); |
| | | const flowNodes = normalized.length ? JSON.parse(JSON.stringify(normalized)) : [createEmptyNode(1)]; |
| | | |
| | | return { |
| | | templateKey: String(row?.templateId || ""), |
| | | templateId: row?.templateId, |
| | | templateName: row?.templateName || templateSnapshot.label, |
| | | instanceId: row?.id, |
| | | instanceNo: row?.instanceNo || "", |
| | | statusRaw: row?.statusRaw || row?.status || "PENDING", |
| | | currentLevel: row?.currentLevel ?? 1, |
| | | applicantId: row?.applicantId, |
| | | applicantName: row?.applicantName || "", |
| | | templateSnapshot, |
| | | formFieldDefs: fields, |
| | | formPayload, |
| | | flowNodes, |
| | | templateAttachments: initTemplateAttachmentsFromSnapshot(templateSnapshot), |
| | | storageBlobDTOs: (row?.storageBlobDTOs?.length ? row.storageBlobDTOs : row?.storageBlobVOList || []).map(f => JSON.parse(JSON.stringify(f))), |
| | | }; |
| | | } |
| | | |
| | | export function createEmptySubmitForm(templateKey, templateOverride, flowNodesOverride) { |
| | | const tpl = templateOverride || null; |
| | | const payload = tpl?.fields?.length ? buildFormPayloadFromFields(tpl.fields) : { summary: "" }; |
| | | const normalized = normalizeFlowNodes(flowNodesOverride); |
| | | const flowNodes = normalized.length ? JSON.parse(JSON.stringify(normalized)) : [createEmptyNode(1)]; |
| | | return { |
| | | templateKey: templateKey || "", |
| | | templateId: tpl?.templateId || "", |
| | | templateName: tpl?.label || "", |
| | | instanceId: "", |
| | | instanceNo: "", |
| | | statusRaw: "", |
| | | currentLevel: 1, |
| | | applicantId: null, |
| | | applicantName: "", |
| | | templateSnapshot: templateOverride || null, |
| | | formFieldDefs: tpl?.fields || [], |
| | | formPayload: payload, |
| | | flowNodes, |
| | | templateAttachments: tpl?.storageBlobDTOs ? JSON.parse(JSON.stringify(tpl.storageBlobDTOs)) : [], |
| | | storageBlobDTOs: [], |
| | | }; |
| | | } |
| | | |
| | | export function initTemplateAttachmentsFromSnapshot(templateSnapshot) { |
| | | const list = templateSnapshot?.storageBlobDTOs; |
| | | return list?.length ? JSON.parse(JSON.stringify(list)) : []; |
| | | } |