fix(diagram): 嵌入页补滚动容器(裸渲染页被全局 body overflow:hidden 裁剪无滚动条)+ 流程图 PNG 按逻辑尺寸显示(SCALE=2 图按自然尺寸渲染整体放大一倍,四处 PNG 消费点统一走 fitDiagram)
build / build (push) Successful in 3m46s

This commit is contained in:
chenxing
2026-10-09 22:09:48 +08:00
parent 053eb0047b
commit 1a07c2f39a
3 changed files with 41 additions and 6 deletions
+12
View File
@@ -0,0 +1,12 @@
/**
* 流程示意图 PNG 显示口径(2026-10-10 用户反馈:嵌入页图过大):服务端 DiagramImageService
* 按 SCALE=2 出图(视网膜清晰度),页面若按自然尺寸渲染等于整体放大一倍。
* 须按逻辑尺寸(自然宽一半)显示;>3 分支等超宽长图再收缩到容器宽。
* 用法:<img ref="diagramEl" :src="..." @load="onDiagramLoad">,onMounted 里对 complete 的图补调一次。
*/
export function fitDiagram(el: HTMLImageElement | null | undefined): void {
if (!el || !el.naturalWidth) return;
const want = el.naturalWidth / 2;
const cap = (el.parentElement?.clientWidth || 0) - 8; // Card body 左右衬
el.style.width = Math.round(Math.min(want, cap > 120 ? cap : want)) + "px";
}
+16 -4
View File
@@ -3,11 +3,12 @@
// 展示"进度 + 流程示意图",零样式复制、单点维护。全程零 CAS 交互——数据走 /open/flow/embed/data
// 免签端点(只认 token),示意图 token 两用(/open/flow/diagram/img 同一 token 出图)。
// 本页只读(方案 §9):不做任何审批动作;办理入口仍是待办站内信 → /task/:taskId 既有链路。
import { ref, computed, onMounted } from "vue";
import { ref, computed, onMounted, nextTick } from "vue";
import { useRoute } from "vue-router";
import { Tag } from "view-ui-plus";
import { INSTANCE_STATUS, INSTANCE_STATUS_COLOR, TASK_STATUS } from "@/api/module/flow";
import TraceTimeline from "@/views/flow/components/TraceTimeline.vue";
import { fitDiagram } from "@/utils/diagramImg";
import config from "@/assets/config/host.config";
const route = useRoute();
@@ -29,6 +30,10 @@ const token = computed(() => String(route.query.token || ""));
const diagramSrc = computed(
() => `${config.serviceHost}/open/flow/diagram/img?token=${token.value}`,
);
const diagramEl = ref<HTMLImageElement | null>(null);
function onDiagramLoad() {
fitDiagram(diagramEl.value);
}
const snapshotRows = computed(() => {
const raw = detail.value?.bizSnapshot;
@@ -186,6 +191,11 @@ onMounted(async () => {
} finally {
loading.value = false;
}
// 缓存命中的图可能在监听器挂上之前就 complete,补一次适配
await nextTick();
if (diagramEl.value?.complete) {
fitDiagram(diagramEl.value);
}
});
</script>
@@ -217,7 +227,7 @@ onMounted(async () => {
<Card class="block" :bordered="false" dis-hover>
<template #title>流程示意图</template>
<img class="diagram-img" :src="diagramSrc" alt="流程示意图" />
<img class="diagram-img" ref="diagramEl" :src="diagramSrc" alt="流程示意图" @load="onDiagramLoad" />
</Card>
<Card class="block" :bordered="false" dis-hover>
@@ -255,7 +265,9 @@ onMounted(async () => {
<style scoped>
/* 根类不用 .page:UnoCSS 原子类同名会以更高优先级盖掉 scoped 的 padding(裸渲染无 layout 壳时暴露) */
.embed-page { padding: 12px 16px; }
/* 嵌入页自身即滚动容器:全局 html/body overflow:hidden(App.vue 壳用),裸渲染页无 layout 滚动壳,
不在此定高滚动的话内容超一屏被裁且无滚动条(2026-10-10 用户反馈) */
.embed-page { height: 100vh; overflow-y: auto; padding: 12px 16px; box-sizing: border-box; }
.head { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
.title { font-size: 16px; font-weight: 600; }
.block { margin-bottom: 12px; }
@@ -264,7 +276,7 @@ onMounted(async () => {
.v { white-space: pre-wrap; word-break: break-all; }
.muted { color: #999; }
.expired { padding: 48px 16px; text-align: center; }
.diagram-img { max-width: 100%; }
.diagram-img { max-width: 100%; height: auto; display: block; margin: 0 auto; }
.trace-mode { float: right; font-weight: normal; margin-right: 16px; }
/* 类名带 record- 前缀:防 UnoCSS 原子类撞名(嵌入页裸渲染无 layout 壳,撞名实测会盖样式) */
.record-tip {
+13 -2
View File
@@ -2,10 +2,11 @@
// 办件详情:状态 + 业务摘要 + 流程轨迹时间线 + 分支会签进度(概览 §9.4 进度图数据源)
// 轨迹两档显示(2026-09-18):mode=user(人员,缺省)|dept(科室),写进 query 便于分享/刷新保持,
// 并随"流程示意图"跳转带给图示页(两边口径一致)。
import { ref, computed, onMounted } from "vue";
import { ref, computed, onMounted, nextTick } from "vue";
import { useRoute, useRouter } from "vue-router";
import { flowApi, INSTANCE_STATUS } from "@/api/module/flow";
import TraceTimeline from "@/views/flow/components/TraceTimeline.vue";
import { fitDiagram } from "@/utils/diagramImg";
import config from "@/assets/config/host.config";
const route = useRoute();
@@ -72,6 +73,12 @@ const diagramSrc = computed(
() => `${config.serviceHost}/api/flow/diagram.png?instanceId=${route.params.instanceId}&mode=${mode.value}`,
);
/** PNG 按 SCALE=2 出图:显示逻辑尺寸(自然宽一半),超宽收缩到容器(src 随口径切换会重新 load) */
const diagramEl = ref<HTMLImageElement | null>(null);
function onDiagramLoad() {
fitDiagram(diagramEl.value);
}
/** 跳流程图时带上当前口径(图示页读 mode,缺省人员) */
function toDiagram() {
router.push({ path: "/diagram", query: { instanceId: String(route.params.instanceId), mode: mode.value } });
@@ -92,6 +99,10 @@ onMounted(async () => {
} finally {
loading.value = false;
}
await nextTick();
if (diagramEl.value?.complete) {
fitDiagram(diagramEl.value);
}
});
</script>
@@ -139,7 +150,7 @@ onMounted(async () => {
流程示意图
<a class="diagram-more" @click="toDiagram()">查看大图</a>
</template>
<img class="diagram-img" :src="diagramSrc" alt="流程示意图" />
<img class="diagram-img" ref="diagramEl" :src="diagramSrc" alt="流程示意图" @load="onDiagramLoad" />
</Card>
<Card class="block" :bordered="false" dis-hover>