fix(trace): 科室视图抄送行遮蔽——新增 utils/flowTrace.ts(collapseCcRows 按 nodeId#round 折叠 CC 行记 ccCount、ccActorText 科室显 deptName‖短码/其余显"抄送对象"、不回落姓名;user/缺省档原引用直返);TraceTimeline/InstanceBody 三视图接折叠与 CC 分支(时间线由组件内折叠、节点/列表走 displayTrace 防二次折叠);EmbedInstance 头部补流程 nodeSummary。真机三视图走查并入全流程重走 P2-E
build / build (push) Successful in 1m57s
build / build (push) Successful in 1m57s
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 轨迹口径工具(三视图 + 嵌入页共用,2026-10-12):
|
||||
* 抄送(CC)在库里与轨迹里是"逐接收人一条"(FlowEngine.notifyReceivers 逐人落 flw_cc)。
|
||||
* 人员视图下正需如此(能看出抄送给了谁);但科室视图不列姓名,N 个接收人若逐行渲染就是
|
||||
* N 行一模一样的文案,故 dept 档按「节点 + 轮次」折叠为一行并记人数——口径对齐
|
||||
* /oapi/flow/progress 的 ccSteps(本就按 nodeId 聚合)。
|
||||
*/
|
||||
|
||||
/** dept 档折叠抄送行:同 nodeId + 同轮次的 CC 行合并为一行(ccCount 记人数);其余行原样、顺序不变。 */
|
||||
export function collapseCcRows(trace: any[], mode?: string): any[] {
|
||||
if (mode !== "dept") return trace;
|
||||
const out: any[] = [];
|
||||
const at: Record<string, number> = {};
|
||||
for (const r of trace) {
|
||||
if (r.kind !== "CC") {
|
||||
out.push(r);
|
||||
continue;
|
||||
}
|
||||
const key = `${r.nodeId}#${r.round || 1}`;
|
||||
const i = at[key];
|
||||
if (i === undefined) {
|
||||
at[key] = out.length;
|
||||
out.push({ ...r, ccCount: 1 });
|
||||
} else {
|
||||
out[i].ccCount += 1;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** dept 档抄送行角色词:科室抄送显科室名(解析不出退短码)、指定人抄送显"抄送对象",多人加人数后缀。
|
||||
* ⚠️ 刻意不回落 assigneeName——本函数存在的意义就是不出现人名。 */
|
||||
export function ccActorText(row: any): string {
|
||||
const base = row.assigneeType === "DEPT_KEY"
|
||||
? (row.deptName || row.deptKey || "抄送对象")
|
||||
: "抄送对象";
|
||||
return (row.ccCount || 1) > 1 ? `${base} ${row.ccCount} 人` : base;
|
||||
}
|
||||
@@ -60,8 +60,13 @@ onMounted(async () => {
|
||||
<template>
|
||||
<div class="embed-page" v-if="detail">
|
||||
<div class="head">
|
||||
<span class="title">{{ detail.bizTitle }}</span>
|
||||
<Tag :color="statusColor(detail.status)">{{ statusName(detail.status) }}</Tag>
|
||||
<div class="head-row">
|
||||
<span class="title">{{ detail.bizTitle }}</span>
|
||||
<Tag :color="statusColor(detail.status)">{{ statusName(detail.status) }}</Tag>
|
||||
</div>
|
||||
<!-- 流程简略信息(detail.nodeSummary=APPROVAL 节点名串联,后端早已下发):
|
||||
不展开"流程示意图"也能一眼看清走向,口径同 Notice 详情页的"流程:"摘要行 -->
|
||||
<div class="head-flow" v-if="detail.nodeSummary">流程:{{ detail.nodeSummary }}</div>
|
||||
</div>
|
||||
|
||||
<InstanceBody :detail="detail" :diagram-src="diagramSrc" />
|
||||
@@ -78,9 +83,10 @@ onMounted(async () => {
|
||||
/* 标题区吸顶(贴合设计基线):左右负外边距让白色背景铺满滚动容器,滚动时内容不从两侧留白露出 */
|
||||
.head {
|
||||
position: sticky; top: 0; z-index: 5; background: #fff;
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
margin: 0 -20px 16px; padding: 14px 20px 12px; border-bottom: 1px solid #f2f4f7;
|
||||
}
|
||||
.head-row { display: flex; align-items: center; gap: 8px; }
|
||||
.head-flow { margin-top: 6px; font-size: 12px; line-height: 1.5; color: #8a94a6; word-break: break-all; }
|
||||
.title { font-size: 16px; font-weight: 600; flex: 1; min-width: 0; }
|
||||
.muted { color: #999; }
|
||||
.expired { padding: 48px 16px; text-align: center; }
|
||||
|
||||
@@ -10,6 +10,7 @@ import { ref, computed, onMounted, nextTick } from "vue";
|
||||
import { Tag } from "view-ui-plus";
|
||||
import TraceTimeline from "@/views/flow/components/TraceTimeline.vue";
|
||||
import { fitDiagram } from "@/utils/diagramImg";
|
||||
import { collapseCcRows, ccActorText } from "@/utils/flowTrace";
|
||||
|
||||
const props = defineProps<{
|
||||
/** InstanceDetail 形状对象(两页同源) */
|
||||
@@ -140,12 +141,17 @@ function rowTime(r: any): string {
|
||||
return r.time || r.handleTime || r.arriveTime || "";
|
||||
}
|
||||
|
||||
/** 操作者(口径=TraceTimeline.actor:dept 档不泄露人名,科室节点显科室、指定人显"指定办理人") */
|
||||
/** 操作者(口径=TraceTimeline.actor:dept 档不泄露人名,科室节点显科室、指定人显"指定办理人"、
|
||||
* 抄送显"抄送对象";发起/撤回/重提显"提交人") */
|
||||
function recordActor(t: any): string {
|
||||
if (t.kind === "ROUTE") return "流程引擎";
|
||||
if (traceMode.value !== "dept") {
|
||||
return t.assigneeName || t.assigneeId || "-";
|
||||
}
|
||||
// 抄送行先判:科室抄送显科室名、指定人抄送显"抄送对象",均不回落人名
|
||||
if (t.kind === "CC") {
|
||||
return ccActorText(t);
|
||||
}
|
||||
if (t.assigneeType === "DEPT_KEY") {
|
||||
return t.deptName || t.deptKey || t.assigneeName || "-";
|
||||
}
|
||||
@@ -193,12 +199,16 @@ function labelTagClass(label: string): string {
|
||||
return "default";
|
||||
}
|
||||
|
||||
/** dept 档下抄送行折叠为一行(N 个接收人 → "抄送对象 N 人",不出现人名);user 档原样。
|
||||
* 节点视图/列表视图共用;时间线视图由 TraceTimeline 内部折叠(口径同源) */
|
||||
const displayTrace = computed(() => collapseCcRows(props.detail?.trace || [], traceMode.value));
|
||||
|
||||
/**
|
||||
* trace 扁平行 → 步骤序列:同 nodeId 的审批行归一步,留痕事件行独立成步,步骤按时间升序。
|
||||
* ⚠️ 步内办理人行序 = 后端返回顺序(任务 id 序),不得按时间重排。
|
||||
*/
|
||||
const nodeSteps = computed(() => {
|
||||
const trace: any[] = props.detail?.trace || [];
|
||||
const trace: any[] = displayTrace.value;
|
||||
const act: string[] = String(props.detail?.currentNodes || "").split(",").filter(Boolean);
|
||||
const rows = trace.map((r) => ({ ...r, _t: rowTime(r) }));
|
||||
const steps: any[] = [];
|
||||
@@ -402,8 +412,8 @@ onMounted(async () => {
|
||||
<TraceTimeline v-else-if="viewMode === 'timeline'" :trace="detail.trace || []" :mode="traceMode"
|
||||
:status-text="TRACE_STATUS_TEXT" />
|
||||
|
||||
<!-- ③ 列表:整个流程周期的完整事件流水 -->
|
||||
<Table v-else size="small" :columns="recordColumns" :data="detail.trace || []" />
|
||||
<!-- ③ 列表:整个流程周期的完整事件流水(dept 档抄送行已折叠,见 displayTrace) -->
|
||||
<Table v-else size="small" :columns="recordColumns" :data="displayTrace" />
|
||||
</div>
|
||||
|
||||
<details class="embed-fold" @toggle="onDiagramToggle">
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
// 流程轨迹时间线(2026-09-18 新增,办件详情页与办理页共用一个实现):
|
||||
// ① 时间流:后端已按"该行实际发生的时间"升序返回(time 字段),此处不再排序,只按 round 分轮展示;
|
||||
// ② 两档显示:user=按人员(默认,显示办理人姓名);dept=按科室(DEPT_KEY 节点显科室名、USERS 节点显
|
||||
// "指定办理人"、发起/撤回/重提显"提交人"),口径与流程示意图的科室视图一致;
|
||||
// "指定办理人"、发起/撤回/重提显"提交人"、抄送显"抄送对象"),口径与流程示意图的科室视图一致;
|
||||
// ③ 撤回重走留痕:kind=WITHDRAW/RESUBMIT 的行由后端留痕事件行提供,重提处插入"第 N 轮"分隔。
|
||||
import { computed } from "vue";
|
||||
import { TASK_STATUS } from "@/api/module/flow";
|
||||
import { collapseCcRows, ccActorText } from "@/utils/flowTrace";
|
||||
|
||||
const props = defineProps<{
|
||||
/** 后端 detail.trace(已按时间升序,含 kind/time/round/科室富化字段) */
|
||||
@@ -46,7 +47,8 @@ const KIND_TAG: Record<string, string> = {
|
||||
ROUTE: "purple",
|
||||
};
|
||||
|
||||
const rows = computed(() => props.trace || []);
|
||||
/** dept 档下抄送行折叠为一行(N 个接收人 → "抄送对象 N 人");user 档原样 */
|
||||
const rows = computed(() => collapseCcRows(props.trace || [], props.mode));
|
||||
|
||||
/** 多轮才有轮次分隔(单轮不打扰) */
|
||||
const multiRound = computed(() => rows.value.some((t: any) => (t.round || 1) > 1));
|
||||
@@ -63,12 +65,16 @@ function roundTitle(round: number): string {
|
||||
return at ? `第 ${round} 轮审批(重新提交于 ${at})` : `第 ${round} 轮审批`;
|
||||
}
|
||||
|
||||
/** 操作者:按人员=姓名;按科室=科室名/指定办理人/提交人(不暴露具体人名) */
|
||||
/** 操作者:按人员=姓名;按科室=科室名/指定办理人/提交人/抄送对象(不暴露具体人名) */
|
||||
function actor(t: any): string {
|
||||
if (t.kind === "ROUTE") return "流程引擎";
|
||||
if (props.mode !== "dept") {
|
||||
return t.assigneeName || t.assigneeId || "-";
|
||||
}
|
||||
// 抄送行先判:科室抄送显科室名、指定人抄送显"抄送对象",均不回落人名
|
||||
if (t.kind === "CC") {
|
||||
return ccActorText(t);
|
||||
}
|
||||
if (t.assigneeType === "DEPT_KEY") {
|
||||
return t.deptName || t.deptKey || t.assigneeName || "-";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user