feat(workbench): 我的办件并入工作台为「我提交的」页签
build / build (push) Successful in 1m58s

- Workbench.vue 新增第4个页签「我提交的」,迁移原 MyInstances 列表(状态筛选/业务类型/详情·撤回·催办),惰性加载,默认页签仍为我的待办
- 删除独立路由 /my (modId 101) 及 MyInstances.vue
- 提示:UserBase 侧 app_module 的 modId=101(我的办件)需另行清理
This commit is contained in:
chenxing
2026-10-12 10:13:31 +08:00
parent ff1c50931a
commit fdc661f509
3 changed files with 78 additions and 103 deletions
+1 -7
View File
@@ -9,7 +9,7 @@ import type { RouteRecordRaw } from "vue-router";
* 办理页 /task/:taskId 为 UserMsg jumpPath 落地页(不挂菜单)。
* <p>
* 详情/深链页(不挂菜单)通过 meta.parentId 关联父级菜单(UserBase 同款范式):左侧菜单保持
* 父级高亮、面包屑补全"父页>本页"。同一详情页可能被多个列表进入(如办件详情既可从"我的办件"
* 父级高亮、面包屑补全"父页>本页"。同一详情页可能被多个列表进入(如办件详情既可从"我提交的"
* 也可从"办件记录"),此时列表页跳转带 ?pid=<父菜单modId> 动态覆盖 meta.parentId 静态默认。
*/
const routes: RouteRecordRaw[] = [
@@ -19,12 +19,6 @@ const routes: RouteRecordRaw[] = [
component: () => import("@/views/flow/Workbench.vue"),
meta: { title: "工作台", modId: 100 },
},
{
path: "/my",
name: "flow_my",
component: () => import("@/views/flow/MyInstances.vue"),
meta: { title: "我的办件", modId: 101 },
},
{
path: "/task/:taskId",
name: "flow_task_handle",
-91
View File
@@ -1,91 +0,0 @@
<script setup lang="ts">
// 我的办件(提交人视角):列表 + 撤回/催办/详情
import { ref } from "vue";
import { Message, Tag } from "view-ui-plus";
import MyConfirm from "@/components/MyConfirm";
import TablePageAlovaNew from "@/components/TablePageAlovaNew.vue";
import InstanceDrawer from "@/views/flow/components/InstanceDrawer.vue";
import { OperatorEnum } from "@/types";
import { flowApi, INSTANCE_STATUS, INSTANCE_STATUS_COLOR } from "@/api/module/flow";
import { actionColumn } from "@/utils/tableActions";
const tableRef = ref<InstanceType<typeof TablePageAlovaNew> | null>(null);
const dataParams = ref<{ status?: number }>({ status: undefined });
const drawerVisible = ref(false);
const drawerInstanceId = ref<string | number>("");
const columns = [
{ title: "序号", type: "index", width: 70, align: "center" },
{ title: "办件标题", key: "bizTitle", minWidth: 220 },
{ title: "业务类型", key: "bizType", width: 110 },
{ 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", " ") || "-") },
actionColumn(
[
{ key: "detail", label: "详情", icon: "icon-browse", on: (r: any) => openDetail(r.instanceId) },
{ key: "withdraw", label: "撤回", icon: "icon-yaohao", on: (r: any) => onWithdraw(r), disabledWhen: (r: any) => r.status !== 10, danger: true },
{ key: "urge", label: "催办", icon: "icon-fasong", on: (r: any) => onUrge(r), disabledWhen: (r: any) => r.status !== 10 },
],
{ mode: "text", width: 180, fixed: "right" },
),
];
// 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();
}
function openDetail(instanceId: string | number) {
drawerInstanceId.value = instanceId;
drawerVisible.value = true;
}
async function onWithdraw(row: any) {
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) {
try {
await flowApi.urge(row.instanceId).send();
Message.success("已催办");
} catch (e: any) {
Message.error(e?.message || "催办失败");
}
}
</script>
<template>
<div class="page-wrapper yh-fill">
<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" />
</div>
<InstanceDrawer v-model="drawerVisible" :instance-id="drawerInstanceId" />
</div>
</template>
+77 -5
View File
@@ -1,22 +1,25 @@
<script setup lang="ts">
// 工作台:我的待办 / 我的已办(概览 §9.4 主路径查询,走 assignee 索引)。
// 工作台:我的待办 / 我的已办 / 抄送我的 / 我提交的(概览 §9.4 主路径查询,走 assignee 索引)。
// 规范骨架(bsch应用页规范 §1):tabs-wrapper + Tabs type=card,Tab 切换只 v-show 内容区,分页状态各自独立
import { ref } from "vue";
import { useRouter } from "vue-router";
import { Tag } from "view-ui-plus";
import { Message, Tag } from "view-ui-plus";
import MyConfirm from "@/components/MyConfirm";
import TablePageAlovaNew from "@/components/TablePageAlovaNew.vue";
import InstanceDrawer from "@/views/flow/components/InstanceDrawer.vue";
import { OperatorEnum } from "@/types";
import { flowApi, TASK_STATUS, TASK_STATUS_COLOR, INSTANCE_STATUS } from "@/api/module/flow";
import { flowApi, TASK_STATUS, TASK_STATUS_COLOR, INSTANCE_STATUS, INSTANCE_STATUS_COLOR } from "@/api/module/flow";
import { actionColumn } from "@/utils/tableActions";
const router = useRouter();
const tab = ref<"todo" | "done" | "cc">("todo");
const tab = ref<"my" | "todo" | "done" | "cc">("todo");
const drawerVisible = ref(false);
const drawerInstanceId = ref<string | number>("");
const todoTableRef = ref<InstanceType<typeof TablePageAlovaNew> | null>(null);
const doneTableRef = ref<InstanceType<typeof TablePageAlovaNew> | null>(null);
const ccTableRef = ref<InstanceType<typeof TablePageAlovaNew> | null>(null);
const myTableRef = ref<InstanceType<typeof TablePageAlovaNew> | null>(null);
const myParams = ref<{ status?: number }>({ status: undefined });
const todoColumns = [
{ title: "序号", type: "index", width: 70, align: "center" },
@@ -59,10 +62,62 @@ const ccColumns = [
actionColumn([{ key: "detail", label: "详情", icon: "icon-browse", on: (r: any) => openDetail(r.instanceId) }], { mode: "text", width: 100, fixed: "right" }),
];
// 我提交的(提交人视角):2026-10-12 由独立页 /my 并入本页签,口径不变
const myColumns = [
{ title: "序号", type: "index", width: 70, align: "center" },
{ title: "办件标题", key: "bizTitle", minWidth: 220 },
{ title: "业务类型", key: "bizType", width: 110 },
{ 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", " ") || "-") },
actionColumn(
[
{ key: "detail", label: "详情", icon: "icon-browse", on: (r: any) => openDetail(r.instanceId) },
{ key: "withdraw", label: "撤回", icon: "icon-yaohao", on: (r: any) => onWithdraw(r), disabledWhen: (r: any) => r.status !== 10, danger: true },
{ key: "urge", label: "催办", icon: "icon-fasong", on: (r: any) => onUrge(r), disabledWhen: (r: any) => r.status !== 10 },
],
{ mode: "text", width: 180, fixed: "right" },
),
];
// dataApi 返回 Method(alova usePagination 负责发送与解包),不能调 .send()
const todoApi = (p: { pageIndex: number; pageSize: number }) => flowApi.todo({ page: p.pageIndex, size: p.pageSize });
const doneApi = (p: { pageIndex: number; pageSize: number }) => flowApi.done({ page: p.pageIndex, size: p.pageSize });
const ccApi = (p: { pageIndex: number; pageSize: number }) => flowApi.ccMy({ page: p.pageIndex, size: p.pageSize });
const myApi = (p: { pageIndex: number; pageSize: number }) =>
flowApi.my({ status: myParams.value.status || undefined, page: p.pageIndex, size: p.pageSize });
function onMySearch() {
myTableRef.value?.refreshData();
}
async function onWithdraw(row: any) {
await MyConfirm({
title: "撤回",
msg: `确定撤回办件「${row.bizTitle}」吗?`,
info: "撤回后流程终止,不可恢复",
async onOk() {
try {
await flowApi.withdraw(row.instanceId).send();
Message.success("已撤回");
myTableRef.value?.reloadData(OperatorEnum.edit);
} catch (e: any) {
Message.error(e?.message || "撤回失败(可能已有任务被办理)");
}
},
});
}
async function onUrge(row: any) {
try {
await flowApi.urge(row.instanceId).send();
Message.success("已催办");
} catch (e: any) {
Message.error(e?.message || "催办失败");
}
}
function goHandle(row: any) {
router.push(`/task/${row.taskId}?pid=100`);
@@ -73,9 +128,10 @@ function openDetail(instanceId: string | number) {
drawerVisible.value = true;
}
// 两个内容区 v-show 共存,已办/抄送首次切换才拉取(保持原惰性加载行为)
// 各内容区 v-show 共存,非首个页签首次切换才拉取(保持原惰性加载行为)
const doneLoaded = ref(false);
const ccLoaded = ref(false);
const myLoaded = ref(false);
function switchTab(name: string) {
tab.value = name as any;
if (name === "done" && !doneLoaded.value) {
@@ -86,6 +142,10 @@ function switchTab(name: string) {
ccLoaded.value = true;
ccTableRef.value?.reloadData(OperatorEnum.sel);
}
if (name === "my" && !myLoaded.value) {
myLoaded.value = true;
myTableRef.value?.reloadData(OperatorEnum.sel);
}
}
</script>
@@ -96,6 +156,7 @@ function switchTab(name: string) {
<TabPane label="我的待办" name="todo" />
<TabPane label="我的已办" name="done" />
<TabPane label="抄送我的" name="cc" />
<TabPane label="我提交的" name="my" />
</Tabs>
<div class="tab-body" v-show="tab === 'todo'">
@@ -107,6 +168,17 @@ function switchTab(name: string) {
<div class="tab-body" v-show="tab === 'cc'">
<TablePageAlovaNew ref="ccTableRef" :dataApi="ccApi" :columns="ccColumns" :autoLoad="false" />
</div>
<div class="tab-body" v-show="tab === 'my'">
<div class="search-wrap fix">
<Select v-model="myParams.status" class="search-wrap_sel" clearable placeholder="状态" @on-change="onMySearch">
<Option :value="10" label="审批中" />
<Option :value="20" label="已完成" />
<Option :value="30" label="已拒绝" />
<Option :value="40" label="已撤回" />
</Select>
</div>
<TablePageAlovaNew ref="myTableRef" :dataApi="myApi" :columns="myColumns" :autoLoad="false" />
</div>
</div>
<InstanceDrawer v-model="drawerVisible" :instance-id="drawerInstanceId" />
</div>