From 5b83fd7144a370c2d54940b07ead83bf3078669d Mon Sep 17 00:00:00 2001 From: chenxing Date: Sat, 3 Oct 2026 23:52:11 +0800 Subject: [PATCH] =?UTF-8?q?feat(embed):=20=E5=B5=8C=E5=85=A5=E9=A1=B5?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AE=A1=E6=89=B9=E8=AE=B0=E5=BD=95=E5=AE=8C?= =?UTF-8?q?=E6=95=B4=E6=B5=81=E6=B0=B4=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/flow/EmbedInstance.vue | 110 ++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 1 deletion(-) diff --git a/src/views/flow/EmbedInstance.vue b/src/views/flow/EmbedInstance.vue index 0610d58..57bfbe3 100644 --- a/src/views/flow/EmbedInstance.vue +++ b/src/views/flow/EmbedInstance.vue @@ -5,7 +5,8 @@ // 本页只读(方案 §9):不做任何审批动作;办理入口仍是待办站内信 → /task/:taskId 既有链路。 import { ref, computed, onMounted } from "vue"; import { useRoute } from "vue-router"; -import { INSTANCE_STATUS, INSTANCE_STATUS_COLOR } from "@/api/module/flow"; +import { Tag } from "view-ui-plus"; +import { INSTANCE_STATUS, INSTANCE_STATUS_COLOR, TASK_STATUS } from "@/api/module/flow"; import TraceTimeline from "@/views/flow/components/TraceTimeline.vue"; import config from "@/assets/config/host.config"; @@ -66,6 +67,97 @@ const formRows = computed(() => { return [{ k: "表单原文", v: String(raw) }]; }); +// ===== 审批记录流水表(整个流程周期,与"流程轨迹"时间线互补:时间线给进度感,表格给逐条可查流水) ===== + +/** 事件类型 → 动作标签主题色(逐字照抄 TraceTimeline.vue 的 KIND_TAG:两处口径,改一处必须同步另一处) */ +const RECORD_KIND_TAG: Record = { + SUBMIT: "green", + RESUBMIT: "green", + WITHDRAW: "orange", + APPROVE: "green", + REJECT: "red", + RETURN: "orange", + TRANSFER: "cyan", + INVALID: "default", + PENDING: "blue", + CC: "cyan", + ROUTE: "purple", +}; + +/** 多轮才显示"轮次"列(单轮不打扰,口径同 TraceTimeline 的 multiRound) */ +const multiRound = computed(() => + (detail.value?.trace || []).some((t: any) => (t.round || 1) > 1)); + +function fmtTime(v: string | null | undefined) { + return v ? String(v).replace("T", " ").replace(/\.\d+$/, "") : ""; +} + +/** 操作者(口径=TraceTimeline.actor:dept 档不泄露人名,科室节点显科室、指定人显"指定办理人") */ +function recordActor(t: any): string { + if (t.kind === "ROUTE") return "流程引擎"; + if (traceMode.value !== "dept") { + return t.assigneeName || t.assigneeId || "-"; + } + if (t.assigneeType === "DEPT_KEY") { + return t.deptName || t.deptKey || t.assigneeName || "-"; + } + if (t.assigneeType === "USERS") { + return "指定办理人"; + } + return t.kind === "SUBMIT" || t.kind === "RESUBMIT" || t.kind === "WITHDRAW" + ? "提交人" + : (t.assigneeName || "-"); +} + +/** 动作标签(口径=TraceTimeline.actionText:CC/ROUTE 专用文案,任务行走 TASK_STATUS 码表) */ +function recordAction(t: any): string { + if (t.kind === "CC") return "已抄送"; + if (t.kind === "ROUTE") return "系统判断"; + return TASK_STATUS[t.status] || String(t.status ?? ""); +} + +/** 表格列:Drawer iframe 宽约 828px,列宽按此设计;轮次列仅多轮时出现 */ +const recordColumns = computed(() => { + const cols: any[] = [ + { title: "时间", width: 150, render: (h: any, p: any) => h("span", fmtTime(p.row.time)) }, + ]; + if (multiRound.value) { + cols.push({ + title: "轮次", width: 80, align: "center", + render: (h: any, p: any) => h("span", `第 ${p.row.round || 1} 轮`), + }); + } + cols.push( + { + title: "节点", minWidth: 120, + render: (h: any, p: any) => h("span", p.row.nodeName || p.row.nodeId || "-"), + }, + { title: "办理人", width: 110, render: (h: any, p: any) => h("span", recordActor(p.row)) }, + { + title: "动作", width: 90, align: "center", + render: (h: any, p: any) => + h(Tag, { size: "small", color: RECORD_KIND_TAG[p.row.kind] || "default" }, + () => recordAction(p.row)), + }, + { + title: "意见 / 去向", minWidth: 180, + render: (h: any, p: any) => { + const parts: any[] = []; + if (p.row.opinion) parts.push(h("span", `「${p.row.opinion}」`)); + if (p.row.edgeName) parts.push(h("span", `去向:${p.row.edgeName}`)); + if (p.row.kind === "ROUTE") { + // outlet 空=判断失败挂起(同 TraceTimeline 的 route-hung 文案),等管理端重跑路由 + parts.push(p.row.outlet + ? h("span", `→ ${p.row.outlet}`) + : h("span", { style: "color:#ed4014" }, `判断失败已挂起(第 ${p.row.attempt || 1} 次)`)); + } + return parts.length ? h("span", parts) : h("span", { style: "color:#999" }, "-"); + }, + }, + ); + return cols; +}); + function statusName(s: number) { return INSTANCE_STATUS[s] || s; } @@ -139,6 +231,14 @@ onMounted(async () => { + + + + +
@@ -166,4 +266,12 @@ onMounted(async () => { .expired { padding: 48px 16px; text-align: center; } .diagram-img { max-width: 100%; } .trace-mode { float: right; font-weight: normal; margin-right: 16px; } +/* 类名带 record- 前缀:防 UnoCSS 原子类撞名(嵌入页裸渲染无 layout 壳,撞名实测会盖样式) */ +.record-tip { + float: right; + font-size: 12px; + font-weight: normal; + margin-right: 16px; + color: #999; +}