diff --git a/src/api/module/notice.ts b/src/api/module/notice.ts index 0874f5a..bf63268 100644 --- a/src/api/module/notice.ts +++ b/src/api/module/notice.ts @@ -123,6 +123,10 @@ class NoticeApi { flowTrace(serid: number) { return get(`${NOTICE}/flow/Trace`, { serid }); } + /** 撤回审核(2026-09-17:后端经 SDK 托管件中转,仅创建人本人+审批中;替代原先前端直调 flow-center) */ + flowWithdraw(serid: number) { + return postForm(`${NOTICE}/flow/Withdraw`, { serid }); + } /** 应用级流程绑定(应用设置 > 流程绑定;发通知页据此判断「提交审核」还是「发送」) */ getFlowBinding() { return get(`${NOTICE}/flow/Binding`); diff --git a/src/types/notice.ts b/src/types/notice.ts index 4b48f40..fa3fc77 100644 --- a/src/types/notice.ts +++ b/src/types/notice.ts @@ -118,6 +118,23 @@ export interface FlowTraceVo { finishTime?: string | null; /** 办理轨迹(traceJson 解析后的数组) */ trace?: any[] | null; + /** 结构化步骤(2026-09-17 接入:steps[0] 固定"发起申请",同节点多任务每人一行) */ + steps?: FlowProgressStepVo[] | null; +} + +/** 流程步骤(SDK FlowProgressStep 同构:后端 /flow/Trace 的 steps 元素) */ +export interface FlowProgressStepVo { + nodeId: string; + nodeName: string | null; + label: string | null; + active: boolean; + assignees?: { + userName?: string | null; + status?: number | null; + statusLabel?: string | null; + opinion?: string | null; + handleTime?: string | null; + }[] | null; } // ============================ 通知详情 ============================ diff --git a/src/views/notice/NoticeDetails.vue b/src/views/notice/NoticeDetails.vue index 8098739..f6e34eb 100644 --- a/src/views/notice/NoticeDetails.vue +++ b/src/views/notice/NoticeDetails.vue @@ -36,35 +36,59 @@ async function loadTrace() { } } -/** 撤回/催办直调 flow-center 用户面(同主域 CAS 免二次登录;本地 dev 走 flowHost CORS) */ +/** + * 撤回:走本应用后端(2026-09-17 接入:后端经 SDK 托管件中转并复核创建人, + * 不再前端直调 flow-center——跨应用 CAS 票据 + CORS 双重外溢且"谁能撤"落在前端)。 + * 催办:仍是"人"的动作,走 flow-center 用户面(同主域 CAS 免二次登录)。 + */ async function flowAction(action: "withdraw" | "urge") { const instanceId = detail.value?.flowInstanceId; - if (!instanceId || !config.flowHost) return; + if (!instanceId) return; flowActing.value = true; try { - const resp = await fetch(`${config.flowHost}/api/flow/instance/${instanceId}/${action}`, { - method: "POST", - credentials: "include", - }); - const res = await resp.json().catch(() => null); - if (resp.ok && res && res.code === 0) { - Message.success(action === "withdraw" ? "已撤回审核,可编辑后重新提交" : "已发送催办提醒"); - load(); // 撤回后状态刷新 - loadTrace(); + if (action === "withdraw") { + await noticeApi.flowWithdraw(serid.value).send(); + Message.success("已撤回审核,可编辑后重新提交"); + await load(); + await loadTrace(); } else { - Message.error(res?.msg || (action === "withdraw" ? "撤回失败(已有人办理时不可撤回)" : "催办失败")); + if (!config.flowHost) return; + const resp = await fetch(`${config.flowHost}/api/flow/instance/${instanceId}/urge`, { + method: "POST", + credentials: "include", + }); + const res = await resp.json().catch(() => null); + if (resp.ok && res && res.code === 0) { + Message.success("已发送催办提醒"); + } else { + Message.error(res?.msg || "催办失败"); + } } } catch (e: any) { - Message.error("流程中心不可达:" + (e.message || e)); + Message.error( + action === "withdraw" + ? "撤回失败(已有人办理时不可撤回):" + (e?.msg || e?.message || e) + : "流程中心不可达:" + (e?.message || e), + ); } finally { flowActing.value = false; } } -/** 查看流程图:flow-center 示意图深链(绑流程时;Notice 指定到人场景用人员视图) */ +/* 流程图弹窗(2026-09-17:后端同源出图 /flow/Diagram,img 免 token 免跨域,不再深链 flow-center) */ +const diagramVisible = ref(false); function openFlowDiagram() { - if (!detail.value?.flowGuid || !config.flowHost) return; - window.open(`${config.flowHost}/#/diagram?flowGuid=${detail.value.flowGuid}&mode=user`, "_blank"); + if (!detail.value?.flowGuid || !detail.value?.flowInstanceId) return; + diagramVisible.value = true; +} +const diagramSrc = computed(() => + diagramVisible.value && detail.value?.flowInstanceId + ? `${config.serviceHost}/api/notice/flow/Diagram?serid=${serid.value}` + : "", +); +function onDiagramError() { + diagramVisible.value = false; + Message.error("流程图加载失败(可能已过期,请刷新页面重试)"); } const readGroups = ref([]); const showRead = ref(false); @@ -264,8 +288,26 @@ function openPreview(item: { name?: string; filename?: string; fileid?: number; 办理意见:{{ flowTrace.flowResult.opinion }} - -
+ +
+
+ {{ s.label || s.nodeName || `节点${i + 1}` }} + +
+
+ +
{{ t.nodeName || t.node || t.taskName || `节点${i + 1}` }} {{ t.userName || t.assigneeName || t.handlerName || "" }} @@ -275,6 +317,13 @@ function openPreview(item: { name?: string; filename?: string; fileid?: number;
+ + +
+ 流程示意图 +
+
+
发文单位:{{ detail.DeptName || "—" }} 发布人:{{ detail.TeaName || "—" }} @@ -410,6 +459,25 @@ function openPreview(item: { name?: string; filename?: string; fileid?: number; .flow-step-op { color: #ed4014; } +.flow-step-active .flow-step-node { + color: #2d8cf0; + font-weight: 600; +} +.flow-step-status { + font-style: normal; + color: #808695; + margin-left: 4px; +} +.flow-step .flow-step-op { + font-style: normal; +} +.diagram-wrap { + text-align: center; + min-height: 200px; +} +.diagram-wrap img { + max-width: 100%; +} .meta { display: flex; flex-wrap: wrap;