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(() => {