DAG画布:节点拖不动根因修复 + 缩放比例显示/一键100%

- 根因:dagLayout 画布宽(1100-1360px)超出视口可用宽度(约1060px),加上保存的偏移视图可能把节点推到视口外 → 节点不可见/不可点,表现为'拖不动'
- 修复:打开画布时若无保存视图、或节点全部在视口外,自动适配缩放(≤100%)并居中,全部节点始终可见可拖
- 新增右上角缩放控件:实时显示缩放百分比(滚轮/适配/拖动联动) + ⤢适配按钮 + 100%一键回原始视图
- 实测:真实鼠标拖动在 0.82x 缩放下精确跟随;滚轮 112%→100% 显示联动
This commit is contained in:
2026-08-12 23:55:57 +08:00
parent 9c45a143b9
commit dfdcd78f1a
2 changed files with 52 additions and 2 deletions
+48 -2
View File
@@ -323,6 +323,32 @@ function dagSize() {
return dagLayout(tasks, idMap);
}
function dagFitView() {
// 画布适配视口:缩放并居中,让全部节点可见
const s = dagState;
const box = $('#dag-box');
const {width, height} = dagSize();
if (!box || !s) return;
const bw = box.clientWidth, bh = box.clientHeight;
const scale = Math.max(0.25, Math.min(1, (bw - 60) / width, (bh - 60) / height));
s.scale = scale;
s.tx = (bw - width * scale) / 2;
s.ty = (bh - height * scale) / 2;
dagApplyView();
saveDagState();
}
function dagZoomTo(scale) {
// 一键回到指定比例(100% = 原始视图)
const s = dagState;
if (!s) return;
s.scale = scale;
s.tx = 0;
s.ty = 0;
dagApplyView();
saveDagState();
}
function saveDagState() {
if (!dagState) return;
try {
@@ -340,6 +366,8 @@ function dagApplyView() {
if (!svg) return;
svg.style.transform = `translate(${s.tx}px, ${s.ty}px) scale(${s.scale})`;
svg.style.transformOrigin = '0 0';
const pct = $('#dag-zoom-pct');
if (pct) pct.textContent = Math.round(s.scale * 100) + '%';
}
function drawDag() {
@@ -364,6 +392,10 @@ function drawDag() {
<button class="btn success" ${ready.length ? '' : 'disabled'} onclick="runWorkflow()">▶ 执行整个工作流(${ready.length}</button>
</div>
<div class="dag-box" id="dag-box">
<div class="dag-zoom" id="dag-zoom">🔍 <b id="dag-zoom-pct">100%</b>
<button onclick="dagFitView()" title="缩放至适配整个画布">⤢ 适配</button>
<button onclick="dagZoomTo(1)" title="一键回到 100% 原始视图">100%</button>
</div>
<div class="dag-trash" id="dag-trash" onclick="openDagTrash()" title="点击查看回收站">🗑️ 删除区<span class="cnt" id="dag-trash-cnt">0</span></div>
<svg id="dag-svg" width="${width}" height="${Math.max(480, height)}" viewBox="0 0 ${width} ${height}" style="transform-origin:0 0">
<defs><marker id="arrow" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" fill="#4a5878"/></marker></defs>
@@ -627,9 +659,23 @@ function renderDag() {
ty: savedView?.ty || 0,
drag: null, moved: false,
};
const box = $('#tab-body');
box.innerHTML = '';
const body2 = $('#tab-body');
body2.innerHTML = '';
drawDag();
// 节点是否在视口内?不在则自动适配(防止节点被平移到视口外无法操作)
const box = $('#dag-box');
const bb = box ? box.getBoundingClientRect() : null;
let anyVisible = false;
if (bb) {
tasks.forEach(t => {
const p = dagState.pos[t.id];
if (!p) return;
const vx = bb.left + dagState.tx + p.x * dagState.scale;
const vy = bb.top + dagState.ty + p.y * dagState.scale;
if (vx < bb.right && vx > bb.left && vy < bb.bottom && vy > bb.top) anyVisible = true;
});
}
if (!anyVisible || !savedView) dagFitView();
}
async function runWorkflow() {
+4
View File
@@ -143,6 +143,10 @@ tr:hover td{background:var(--panel2)}
.dag-trash{position:absolute;left:14px;bottom:14px;background:rgba(255,107,107,.1);border:1.5px dashed var(--danger);color:var(--danger);border-radius:10px;padding:8px 14px;font-size:12px;cursor:pointer;z-index:5;display:flex;align-items:center;gap:6px;backdrop-filter:blur(2px)}
.dag-trash:hover,.dag-trash.hover{background:rgba(255,107,107,.25);border-style:solid}
.dag-trash .cnt{background:var(--danger);color:#fff;border-radius:8px;padding:0 6px;font-size:11px}
.dag-zoom{position:absolute;right:14px;top:14px;background:rgba(23,30,46,.88);border:1px solid var(--border);border-radius:8px;padding:4px 10px;font-size:12px;color:var(--muted);display:flex;align-items:center;gap:6px;z-index:6}
.dag-zoom b{color:var(--text);font-weight:600;min-width:42px;text-align:center}
.dag-zoom button{background:var(--panel2);border:1px solid var(--border);color:var(--text);border-radius:5px;padding:2px 8px;font-size:11px;cursor:pointer}
.dag-zoom button:hover{border-color:var(--accent);color:var(--accent)}
.alert-row{border-left:3px solid var(--border);padding:12px 14px;margin-bottom:8px;background:var(--panel2);border-radius:0 10px 10px 0;cursor:pointer}
.alert-row.unread{border-left-color:var(--warn)}
.alert-row.critical{border-left-color:var(--danger)}