diff --git a/webui/graph.js b/webui/graph.js index c7bee24..ef80fff 100644 --- a/webui/graph.js +++ b/webui/graph.js @@ -153,9 +153,11 @@ const yBand = LEVEL_BANDS[b].y; const jl = G.nodes.filter((n) => n.type === "Job" && levelBandOf(n) === b) .sort((x, y) => String(x.title || x.id).localeCompare(String(y.title || y.id), "zh")); + // 岗位带内多行排列(一行排开会横向过长,fitView 按比例缩成横带) + const jPerRow = Math.max(3, Math.ceil(Math.sqrt(jl.length) * 1.8)); jl.forEach((n, i) => { - n.x = (i - (jl.length - 1) / 2) * 200; - n.y = yBand + 170; + n.x = ((i % jPerRow) - (jPerRow - 1) / 2) * 190; + n.y = yBand + 150 - Math.floor(i / jPerRow) * 85; n.vx = 0; n.vy = 0; }); const sl = G.nodes.filter((n) => n.type !== "Job" && levelBandOf(n) === b) @@ -218,7 +220,12 @@ G.activeNodes = null; G.activeEdges = null; } else if (mode === "tech") applyTechLayout(); else if (mode === "level") applyLevelLayout(); - else if (mode === "timeline") setupTimeline(); + else if (mode === "timeline") { + // 时序视图沿用全景力导向布局(否则继承上一个视图的特化布局会被压扁) + if (G._overviewPos) G._overviewPos.forEach((p, i) => { G.nodes[i].x = p.x; G.nodes[i].y = p.y; G.nodes[i].vx = 0; G.nodes[i].vy = 0; }); + else layout(); + setupTimeline(); + } if (G.soloJob) { applySoloLayout(); fitSoloView(); } else fitView(); requestDraw(); drawMinimap(); @@ -229,7 +236,11 @@ function relayoutByView() { if (G.viewMode === "tech") applyTechLayout(); else if (G.viewMode === "level") applyLevelLayout(); - else if (G.viewMode === "timeline") setupTimeline(); + else if (G.viewMode === "timeline") { + if (G._overviewPos) G._overviewPos.forEach((p, i) => { G.nodes[i].x = p.x; G.nodes[i].y = p.y; G.nodes[i].vx = 0; G.nodes[i].vy = 0; }); + else layout(); + setupTimeline(); + } } /* ================= 数据装载 ================= */