Files
flow-center-front/src/views/flow/components/TraceTimeline.vue
T
chenxing cdf04f0930
build / build (push) Successful in 3m17s
feat(ui): 应用页规范全量重构 + 工作台与流程轨迹优化
应用页规范(bsch)重构:
- 6 个列表页迁规范骨架: page-wrapper/tabs-wrapper + search-wrap 搜索区(关键字 200px 即输即搜, 去查询按钮) + link-table table-bottom-box 定高表格 + page-panel 分页栏
- 分页列表统一接 TablePageAlovaNew(页长 [10,20,30,40]/customPageCount 记忆/增删智能翻页); dataApi 须返回 Method, 调 .send() 会导致表格静默空数据
- 操作列统一图标式 operatingHandle(Tooltip+fixed right 居中); 各列表补序号列; 危险操作统一 MyConfirm 二次确认
- 弹窗表单必填红星 + 统一提示「请填写红色星号内容!」
- TablePageAlovaNew 补页长记忆与重载后清空勾选; 新增 src/types OperatorEnum

工作台与流程轨迹优化:
- 已办列表去重: 每办件一行, doneCount>1 显示「共办理 N 次」标签
- 新增 TraceTimeline 轨迹时间流(时间升序/kind/轮次, 按人员/科室切换写回 URL), 接入详情/办理页
- 新增流程源码只读抽屉 SourceDrawer, 接入定义列表与图示页
- 办理人映射/流程定义新增「启用」回路(/admin/map/enable, /admin/def/enable)
- 图示视图口径统一 dept/user 两档, 缺省人员视图; 状态列 Tag 色映射统一导出
2026-09-18 16:52:40 +08:00

128 lines
4.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
// 流程轨迹时间线(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";
const props = defineProps<{
/** 后端 detail.trace(已按时间升序,含 kind/time/round/科室富化字段) */
trace: any[];
/** user=按人员(缺省)|dept=按科室 */
mode?: string;
}>();
/** 事件类型 → 时间线圆点颜色 */
const KIND_DOT: Record<string, string> = {
SUBMIT: "green",
RESUBMIT: "blue",
WITHDRAW: "orange",
APPROVE: "green",
REJECT: "red",
RETURN: "orange",
TRANSFER: "cyan",
INVALID: "gray",
PENDING: "blue",
};
/** 事件类型 → 动作标签主题色(标签文案取 TASK_STATUS,避免两处口径漂移) */
const KIND_TAG: Record<string, string> = {
SUBMIT: "green",
RESUBMIT: "green",
WITHDRAW: "orange",
APPROVE: "green",
REJECT: "red",
RETURN: "orange",
TRANSFER: "cyan",
INVALID: "default",
PENDING: "blue",
};
const rows = computed(() => props.trace || []);
/** 多轮才有轮次分隔(单轮不打扰) */
const multiRound = computed(() => rows.value.some((t: any) => (t.round || 1) > 1));
/** 轮次分隔标题:第 N 轮 + 该轮重提时间 */
function roundTitle(round: number): string {
let at = "";
for (const t of rows.value) {
if ((t.round || 1) === round && t.kind === "RESUBMIT") {
at = fmtTime(t.time);
break;
}
}
return at ? `第 ${round} 轮审批(重新提交于 ${at})` : `第 ${round} 轮审批`;
}
/** 操作者:按人员=姓名;按科室=科室名/指定办理人/提交人(不暴露具体人名) */
function actor(t: any): string {
if (props.mode !== "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 || "-");
}
/** 动作标签:失效行标"失效"而不是笼统的"提交" */
function actionText(t: any): string {
return TASK_STATUS[t.status] || String(t.status ?? "");
}
function fmtTime(v: string | null | undefined): string {
if (!v) return "";
return String(v).replace("T", " ").replace(/\.\d+$/, "");
}
</script>
<template>
<div>
<Timeline>
<template v-for="(t, i) in rows" :key="t.rowKey || i">
<div v-if="multiRound && (t.round || 1) !== (rows[i - 1]?.round || 1)" class="round">
{{ roundTitle(t.round || 1) }}
</div>
<TimelineItem :color="KIND_DOT[t.kind] || 'gray'">
<div class="line">
<span class="node">{{ t.nodeName || t.nodeId }}</span>
<Tag size="small">{{ actor(t) }}</Tag>
<Tag size="small" :color="KIND_TAG[t.kind] || 'default'">{{ actionText(t) }}</Tag>
<span class="opinion" v-if="t.opinion">「{{ t.opinion }}」</span>
<span class="opinion" v-if="t.edgeName">去向:{{ t.edgeName }}</span>
</div>
<div class="time">{{ fmtTime(t.time || t.handleTime || t.arriveTime) }}</div>
</TimelineItem>
</template>
</Timeline>
<div class="muted" v-if="!rows.length">暂无轨迹</div>
</div>
</template>
<style scoped>
.round {
margin: 4px 0 10px 6px;
padding: 2px 8px;
display: inline-block;
font-size: 12px;
color: #2d8cf0;
background: #f0f7ff;
border-left: 3px solid #2d8cf0;
border-radius: 2px;
}
.line { display: flex; align-items: center; flex-wrap: wrap; gap: 4px; }
.node { font-weight: 600; }
.opinion { color: #666; }
.time { color: #aaa; font-size: 12px; }
.muted { color: #999; }
</style>