feat(ui): 应用页规范全量重构 + 工作台与流程轨迹优化
build / build (push) Successful in 3m17s

应用页规范(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 色映射统一导出
This commit is contained in:
chenxing
2026-09-18 16:52:40 +08:00
parent 0083246039
commit cdf04f0930
16 changed files with 1172 additions and 481 deletions
+46 -57
View File
@@ -1,58 +1,61 @@
<script setup lang="ts">
// 我的办件(提交人视角):列表 + 撤回/催办/详情
import { ref, onMounted } from "vue";
import { ref } from "vue";
import { useRouter } from "vue-router";
import { Message } from "view-ui-plus";
import { flowApi, INSTANCE_STATUS } from "@/api/module/flow";
import MyConfirm from "@/components/MyConfirm";
import TablePageAlovaNew from "@/components/TablePageAlovaNew.vue";
import { OperatorEnum } from "@/types";
import { flowApi, INSTANCE_STATUS, INSTANCE_STATUS_COLOR } from "@/api/module/flow";
import Utils from "@/utils";
const router = useRouter();
const loading = ref(false);
const page = ref(1);
const size = ref(10);
const total = ref(0);
const status = ref<number | undefined>(undefined);
const rows = ref<any[]>([]);
const tableRef = ref<InstanceType<typeof TablePageAlovaNew> | null>(null);
const tableHeight = ref(Utils.useTableHeight({ isSearch: true, isPage: true }));
const dataParams = ref<{ status?: number }>({ status: undefined });
const columns = [
{ title: "序号", type: "index", width: 70, align: "center" },
{ title: "办件标题", key: "bizTitle", minWidth: 220 },
{ title: "业务类型", key: "bizType", width: 110 },
{ title: "状态", width: 100, render: (h: any, p: any) => {
const color = p.row.status === 10 ? "blue" : p.row.status === 20 ? "green" : p.row.status === 30 ? "red" : "gray";
return h("Tag", { color }, () => INSTANCE_STATUS[p.row.status] || p.row.status);
} },
{ title: "状态", width: 100, align: "center", render: (h: any, p: any) =>
h("Tag", { color: INSTANCE_STATUS_COLOR[p.row.status] || "gray" }, () => INSTANCE_STATUS[p.row.status] || p.row.status) },
{ title: "当前节点", key: "currentNodes", width: 150 },
{ title: "提交时间", width: 170, render: (h: any, p: any) => h("span", (p.row.submitTime || "").replace("T", " ")) },
{ title: "办结时间", width: 170, render: (h: any, p: any) => h("span", (p.row.finishTime || "").replace("T", " ") || "-") },
{ title: "操作", width: 150, render: (h: any, p: any) => {
const btns = [Utils.operatingTextHandle(h, "详情", "", () => router.push(`/instance/${p.row.instanceId}?pid=101`))];
{ title: "操作", width: 180, align: "center", fixed: "right", render: (h: any, p: any) => {
const btns = [Utils.operatingHandle(h, "详情", "icon-browse", () => router.push(`/instance/${p.row.instanceId}?pid=101`))];
if (p.row.status === 10) {
btns.push(Utils.operatingTextHandle(h, "撤回", "", () => onWithdraw(p.row)));
btns.push(Utils.operatingTextHandle(h, "催办", "", () => onUrge(p.row)));
btns.push(Utils.operatingHandle(h, "撤回", "icon-yaohao", () => onWithdraw(p.row)));
btns.push(Utils.operatingHandle(h, "催办", "icon-fasong", () => onUrge(p.row)));
}
return btns;
} },
];
async function load() {
loading.value = true;
try {
const data: any = await flowApi.my({ status: status.value, page: page.value, size: size.value }).send();
rows.value = data?.content || [];
total.value = data?.total || 0;
} finally {
loading.value = false;
}
// dataApi 返回 Method(alova usePagination 负责发送与解包),不能调 .send()
const tableApi = (p: { pageIndex: number; pageSize: number }) =>
flowApi.my({ status: dataParams.value.status || undefined, page: p.pageIndex, size: p.pageSize });
function onSearch() {
tableRef.value?.refreshData();
}
async function onWithdraw(row: any) {
try {
await flowApi.withdraw(row.instanceId).send();
Message.success("已撤回");
load();
} catch (e: any) {
Message.error(e?.message || "撤回失败(可能已有任务被办理)");
}
await MyConfirm({
title: "撤回",
msg: `确定撤回办件「${row.bizTitle}」吗?`,
info: "撤回后流程终止,不可恢复",
async onOk() {
try {
await flowApi.withdraw(row.instanceId).send();
Message.success("已撤回");
tableRef.value?.reloadData(OperatorEnum.edit);
} catch (e: any) {
Message.error(e?.message || "撤回失败(可能已有任务被办理)");
}
},
});
}
async function onUrge(row: any) {
@@ -63,34 +66,20 @@ async function onUrge(row: any) {
Message.error(e?.message || "催办失败");
}
}
function onPage(p: number) {
page.value = p;
load();
}
onMounted(load);
</script>
<template>
<div class="page">
<div class="filter">
<Select v-model="status" clearable placeholder="状态" style="width: 140px" @on-change="load">
<Option :value="10" label="审批中" />
<Option :value="20" label="已完成" />
<Option :value="30" label="已拒绝" />
<Option :value="40" label="已撤回" />
</Select>
</div>
<Table :columns="columns" :data="rows" :loading="loading" stripe />
<div class="pager">
<Page :total="total" :current="page" :page-size="size" show-total @on-change="onPage" />
<div class="page-wrapper">
<div class="main-container">
<div class="search-wrap fix">
<Select v-model="dataParams.status" class="search-wrap_sel" clearable placeholder="状态" @on-change="onSearch">
<Option :value="10" label="审批中" />
<Option :value="20" label="已完成" />
<Option :value="30" label="已拒绝" />
<Option :value="40" label="已撤回" />
</Select>
</div>
<TablePageAlovaNew ref="tableRef" :dataApi="tableApi" :columns="columns" :tableHeight="tableHeight" />
</div>
</div>
</template>
<style scoped>
.page { padding: 12px 16px; }
.filter { margin-bottom: 10px; }
.pager { margin-top: 12px; text-align: right; }
</style>