From 351a5135474ee2fbb28e622c18b7b896a32bbc05 Mon Sep 17 00:00:00 2001 From: chenxing Date: Mon, 19 Oct 2026 23:59:48 +0800 Subject: [PATCH] =?UTF-8?q?feat(p3.3):=20=E5=BA=94=E7=94=A8=E5=86=85?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E5=89=8D=E7=AB=AF=E2=80=94=E2=80=94flow-appr?= =?UTF-8?q?oval=20=E7=BB=84=E4=BB=B6=E6=97=8F=E6=9C=AC=E5=9C=B0=E5=8C=96?= =?UTF-8?q?=20+=20/todo=20=E6=88=91=E7=9A=84=E5=AE=A1=E6=89=B9=E9=A1=B5=20?= =?UTF-8?q?+=20=E8=AF=A6=E6=83=85=E5=AE=A1=E6=89=B9=E5=8D=A1=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 从 Notice/front 复制 flow-approval 六件并本地修复(漂移登记组件 README,Notice 不回改): · P0-04:四个写调用点(List 通过/驳回 + Card 通过/驳回)全部实际携带本行 bizId, 选中 bizId 随弹窗生命周期锁定/清理 · P0-05:删 useElementHeight/yh-* 平台布局依赖,本地 fa-* 样式(theme-base.css --sh-* 令牌) · P0-06:types.ts 本地契约——增 my(行=ApplyVo 子集/FlowPage 完整元数据/状态 0=待提交 禁止当全部,全部用 -1 哨兵)、删 instances(不代理全校视角)、approve/reject 增 bizId 参 · P1-05:卡片轨迹不自取(详情页把 /progress 转换 TraceVo 经 props 传入);节点摘要取 active steps 节点名;MULTI_TASK 逐条列出本人待办各带动作,不默认 tasks[0]; 未进流程(flowInstanceId 空)详情页不渲染卡片(显式决策) · P1-09:FlowTaskEmbed 消息四条件(打开中+严格 origin+本 iframe+taskId/bizId 上下文一致), 换址重置高度;宿主换址请求带代际号,关闭/换件废弃在途响应并清上下文; open-biz 只用与当前办件一致的 bizId 构造 /apply-detail?id=,不透传任意 URL - ApprovalApi 类挂 svc.ts(todo/my/byBiz/approve/reject/recordUrl/taskPageUrl,写面 taskId/applyId 查询参数+opinion/edgeName JSON body 两形态区分)+ approvalAdapter.ts 唯一映射 - /todo 路由 + 顶部导航第三链接「我的审批」+ MyApprovals.vue(双页签+记录抽屉+完整办理) - ApplyDetail 挂审批卡片(审批操作区)+ TraceVo 转换器 - 小屏基础响应式(drawer/modal 宽度自适应、列表横向滚动;1200px 全量适配不在本批) - 门禁:vue-tsc(ts4.9 外挂兼容组合)错误数 HEAD=工作区=18 零新增(仓内 vue-tsc1.8+ts5.9 崩溃系历史遗留);vite build 绿(MyApprovals/approvalAdapter chunk 产出); preview 冒烟:/todo 渲染完整(导航/页签/表格空态/API 失败优雅降级)、详情页无崩溃(Stage S4/P3.3) --- src/App.vue | 1 + src/api/module/approvalAdapter.ts | 28 ++ src/api/module/svc.ts | 36 ++ .../flow-approval/FlowApprovalCard.vue | 336 ++++++++++++++ .../flow-approval/FlowApprovalList.vue | 412 ++++++++++++++++++ .../flow-approval/FlowRecordDrawer.vue | 130 ++++++ .../flow-approval/FlowTaskEmbed.vue | 167 +++++++ src/components/flow-approval/README.md | 39 ++ src/components/flow-approval/types.ts | 167 +++++++ src/router/routes.ts | 8 +- src/views/ApplyDetail.vue | 106 +++++ src/views/MyApprovals.vue | 138 ++++++ 12 files changed, 1567 insertions(+), 1 deletion(-) create mode 100644 src/api/module/approvalAdapter.ts create mode 100644 src/components/flow-approval/FlowApprovalCard.vue create mode 100644 src/components/flow-approval/FlowApprovalList.vue create mode 100644 src/components/flow-approval/FlowRecordDrawer.vue create mode 100644 src/components/flow-approval/FlowTaskEmbed.vue create mode 100644 src/components/flow-approval/README.md create mode 100644 src/components/flow-approval/types.ts create mode 100644 src/views/MyApprovals.vue diff --git a/src/App.vue b/src/App.vue index 04e8130..d64d16e 100644 --- a/src/App.vue +++ b/src/App.vue @@ -82,6 +82,7 @@ async function applySiteThemeRecommend() { {{ userName }} diff --git a/src/api/module/approvalAdapter.ts b/src/api/module/approvalAdapter.ts new file mode 100644 index 0000000..eaffeaa --- /dev/null +++ b/src/api/module/approvalAdapter.ts @@ -0,0 +1,28 @@ +import type { FlowApprovalApi } from "@/components/flow-approval/types"; +import { approvalApi, applyApi } from "@/api/module/svc"; + +/** + * svc-hall 应用内审批适配器(唯一映射位置;沿 Notice approvalAdapter 形态)。 + * + * 纪律: + * - 身份三字段(operatorId/operatorName/schId)不出现——后端从 CAS 会话取; + * - 写面 bizId=applyId 是必需上下文(后端写面双闸),组件契约层已强制携带; + * - trace 复用既有 /api/apply/{id}/progress(卡片不自取轨迹,宿主转换后经 props 传入); + * - alova Method 是 thenable,组件 await 即发。 + */ +export const svcFlowApprovalApi: FlowApprovalApi = { + todo: (p) => approvalApi.todo(p?.page ?? 1, p?.size ?? 10).send(), + my: (p) => + approvalApi + .my(p?.page ?? 1, p?.size ?? 10, p?.flowStatus ?? null) + .send(), + byBiz: (bizId) => approvalApi.byBiz(Number(bizId)).send(), + trace: (bizId) => applyApi.progress(Number(bizId)).send(), + approve: (taskId, opinion, edgeName, bizId) => + approvalApi.approve(taskId, Number(bizId), opinion, edgeName).send(), + reject: (taskId, opinion, bizId) => approvalApi.reject(taskId, Number(bizId), opinion).send(), + recordUrl: (bizId) => approvalApi.recordUrl(Number(bizId)).send(), + taskPageUrl: (bizId, taskId) => approvalApi.taskPageUrl(Number(bizId), taskId).send(), +}; + +export default svcFlowApprovalApi; diff --git a/src/api/module/svc.ts b/src/api/module/svc.ts index 31fa8c4..3118422 100644 --- a/src/api/module/svc.ts +++ b/src/api/module/svc.ts @@ -22,6 +22,7 @@ import type { const HALL = "/api/hall"; const APPLY = "/api/apply"; const FILE = "/api/file"; +const APPROVAL = "/api/apply/approval"; function get(url: string, params?: Record): Method { return new Method("GET", alovaInstance, url, { params }); @@ -125,6 +126,41 @@ class FileApi { } } +class ApprovalApi { + /** 应用内审批代理(P3.1,/api/apply/approval/**;身份全部由后端从 CAS 会话取,前端不传) */ + /** 待我审批(中心 todo;双值双查与合并都在后端) */ + todo(page?: number, size?: number) { + return get(`${APPROVAL}/Todo`, { page, size }); + } + /** 我提交的(本地办件;后端做 {total,list}→FlowPage 信封映射;flowStatus 空=全部) */ + my(page?: number, size?: number, flowStatus?: number | null) { + return get(`${APPROVAL}/My`, { page, size, flowStatus: flowStatus ?? undefined }); + } + /** 当前人在该办件上的待办(0/1/N 均 200;卡片/列表复核同源) */ + byBiz(applyId: number) { + return get(`${APPROVAL}/ByBiz`, { applyId }); + } + /** 通过:taskId/applyId 走查询参数(与中心端点同构),opinion/edgeName 走 JSON body */ + approve(taskId: number, applyId: number, opinion?: string, edgeName?: string) { + return postJSON(`${APPROVAL}/Approve?taskId=${encodeURIComponent(taskId)}&applyId=${encodeURIComponent(applyId)}`, + { opinion: opinion || undefined, edgeName: edgeName || undefined }); + } + /** 拒绝:opinion 必填(后端前置校验) */ + reject(taskId: number, applyId: number, opinion: string) { + return postJSON(`${APPROVAL}/Reject?taskId=${encodeURIComponent(taskId)}&applyId=${encodeURIComponent(applyId)}`, + { opinion }); + } + /** 审批记录抽屉地址(token 只读页,instanceId 形态换址) */ + recordUrl(applyId: number) { + return get(`${APPROVAL}/RecordUrl`, { applyId }); + } + /** 完整办理任务页地址(先 byBiz 归属预检再签发,在后端) */ + taskPageUrl(applyId: number, taskId: number) { + return get(`${APPROVAL}/TaskPageUrl`, { applyId, taskId }); + } +} + export const hallApi = new HallApi(); export const applyApi = new ApplyApi(); export const fileApi = new FileApi(); +export const approvalApi = new ApprovalApi(); diff --git a/src/components/flow-approval/FlowApprovalCard.vue b/src/components/flow-approval/FlowApprovalCard.vue new file mode 100644 index 0000000..d7552fe --- /dev/null +++ b/src/components/flow-approval/FlowApprovalCard.vue @@ -0,0 +1,336 @@ + + + + + diff --git a/src/components/flow-approval/FlowApprovalList.vue b/src/components/flow-approval/FlowApprovalList.vue new file mode 100644 index 0000000..8e839e4 --- /dev/null +++ b/src/components/flow-approval/FlowApprovalList.vue @@ -0,0 +1,412 @@ + + + + + diff --git a/src/components/flow-approval/FlowRecordDrawer.vue b/src/components/flow-approval/FlowRecordDrawer.vue new file mode 100644 index 0000000..d20c152 --- /dev/null +++ b/src/components/flow-approval/FlowRecordDrawer.vue @@ -0,0 +1,130 @@ +