diff --git a/src/views/flow/TaskHandle.vue b/src/views/flow/TaskHandle.vue index 260760c..0efb158 100644 --- a/src/views/flow/TaskHandle.vue +++ b/src/views/flow/TaskHandle.vue @@ -47,6 +47,18 @@ const showTransfer = ref(false); const transferUser = ref(""); const transferName = ref(""); +/** + * 业务信息行:唯一内容 = 约定键 bizSnapshot.summaryFields(子应用决定展示哪些字段、用什么中文 label, + * 口径同 InstanceBody「业务信息」)。不再逐字平铺 bizSnapshot 原始键——那会把 title/summaryFields 这类 + * 英文 key 直接摆给办理人看,对象值还会渲染成 [object Object]。 + * 无 summaryFields(老数据/未接入约定的子应用)只回退已知的人类可读键,未知键一律不外露。 + */ +const SUMMARY_FALLBACK_LABELS: Array<[string, string]> = [ + ["title", "标题"], + ["summary", "摘要"], + ["attachmentNames", "附件"], +]; + const snapshotRows = computed(() => { const raw = detail.value?.bizSnapshot; if (!raw) return []; @@ -56,10 +68,22 @@ const snapshotRows = computed(() => { } catch { return [{ k: "摘要", v: String(raw) }]; } - if (obj && typeof obj === "object") { - return Object.entries(obj).map(([k, v]) => ({ k, v: v == null ? "-" : String(v) })); + if (!obj || typeof obj !== "object") return []; + const sf = obj.summaryFields; + if (Array.isArray(sf) && sf.length) { + return sf + .filter((x: any) => x && x.label !== null && x.label !== undefined) + .slice(0, 6) + .map((x: any) => ({ + k: String(x.label), + v: Array.isArray(x.value) + ? (x.value.join("、") || "-") + : (x.value === null || x.value === undefined || x.value === "" ? "-" : String(x.value)), + })); } - return []; + return SUMMARY_FALLBACK_LABELS + .filter(([key]) => obj[key] !== null && obj[key] !== undefined && obj[key] !== "") + .map(([key, label]) => ({ k: label, v: formValueText(obj[key]) })); }); /** 业务表单数据(§3.4):formDataJson 逐键值只读渲染;数组值按文件名/项连接;解析失败降级一行原文 */ @@ -100,15 +124,22 @@ async function load() { } } +/** 业务详情绝对地址(后端按 bizUrl + applyAppCode 查应用注册表 pc_url 拼出)。 + * bizUrl 本身是裸相对路径,流内直开时没有宿主页面可借基址,直接 window.open 会落到本应用 origin 上。 */ +const bizUrlAbs = computed(() => detail.value?.bizUrlAbs || ""); +/** 入口可见性:bare 由父页面按自己路由接管(原样传裸路径),独立打开必须拿到绝对地址 */ +const canOpenBiz = computed(() => !!detail.value?.bizUrl && (isBare.value || !!bizUrlAbs.value)); + function openBizUrl() { - const url = detail.value?.bizUrl; - if (!url) return; - // bare:由父页面决定怎么开(它才知道自家业务详情的路由),iframe 里 window.open 会白跳一个中心站地址 if (isBare.value) { - postToParent({ type: "open-biz", bizId: detail.value?.bizId ?? null, bizUrl: url }); + postToParent({ type: "open-biz", bizId: detail.value?.bizId ?? null, bizUrl: detail.value?.bizUrl || "" }); return; } - window.open(url, "_blank"); + if (!bizUrlAbs.value) { + Message.warning("业务详情地址暂不可用,请在业务系统内查看"); + return; + } + window.open(bizUrlAbs.value, "_blank"); } /** 流程图仍要能看,但 bare 下不能用 router.push——/diagram 不是 /task/ 前缀,会在 iframe 里长出菜单壳 */ @@ -162,17 +193,33 @@ async function submit(approved: boolean) { } } +/** + * 二次确认:独立打开用 Modal.confirm(原生体验);bare 态一律行内确认。 + * iframe 内弹 Modal 会在父页面弹窗之上再叠一层遮罩——父页遮罩盖不到 iframe,视觉上是"弹窗套弹窗", + * 且弹出位置只在本页顶部(长表单下离按钮很远)。行内确认条没有这个问题。 + */ +const confirming = ref(false); + function confirmApprove() { if (outlets.value.length > 1 && !chosenEdge.value) { Message.warning("该节点有多个办理去向,请先选择"); return; } + if (isBare.value) { + confirming.value = true; + return; + } Modal.confirm({ title: "同意该任务?", onOk: () => submit(true), }); } +function doApprove() { + confirming.value = false; + submit(true); +} + // ===== 退回 ===== function openReturn() { const targets = detail.value?.returnTargets || []; @@ -266,17 +313,18 @@ onBeforeUnmount(() => { 待本人办理 - + - -
(业务方未提供摘要)
+ +
(业务方未提供业务信息)
{{ row.k }} {{ row.v }}
-
@@ -306,16 +354,49 @@ onBeforeUnmount(() => {
- - - - + +
当前任务不可办理(不存在/非本人/已办理)
+ + +
+ + +
+ +
+ + +
- - + + - - + + -