fix(frontend): TreeCanvas 坐标+连线三重修复 — 根治箭头悬空

1. calcSubtreeHeight: Math.max(CARD_H+GAP, totalChildrenHeight)
   → 首子与父平齐,父级高度取 max 而非无脑叠加

2. placeChildren: startY = parentNode.y (水平对齐)
   → 移除初始化 GAP,累加改为 startY += subtreeH

3. lines(): 动态追踪 Y 坐标
   → y1 = isLeft? n.y+40 : parent.y+40
   → y2 = isLeft? parent.y+40 : n.y+40
   → lineStyle 的 Math.atan2 自动画出完美对角线
This commit is contained in:
2026-08-11 16:42:14 +08:00
parent 0a1c2f4dcf
commit 5b5f4a6b0e

View File

@ -121,22 +121,22 @@ export default {
allTasks.forEach(t => { if (isMainFn(t)) mains.push(t); });
mains.sort((a, b) => new Date(a.created_at) - new Date(b.created_at));
// 🚀 预计算每个节点的子树高度(兄弟累加,用于防重叠占位
// 🚀 预计算每个节点的子树高度(兄弟累加取 max首子与父平齐
const calcSubtreeHeight = (nodeId) => {
const children = childMap[nodeId] || [];
if (children.length === 0) return CARD_H + VERTICAL_GAP;
const totalChildrenHeight = children.reduce((sum, c) => sum + calcSubtreeHeight(c.id), 0);
return CARD_H + VERTICAL_GAP + totalChildrenHeight;
return Math.max(CARD_H + VERTICAL_GAP, totalChildrenHeight);
};
const nodes = [];
// 🚀 递归放置算法:childNode.y = parentNode.y + CARD_H + VERTICAL_GAP
// 🚀 递归放置算法:首个子节点与父节点水平平齐
// 同级子节点按 index 错开 Y 轴,各占其子树高度防止重叠
const placeChildren = (parentNode, currentSide) => {
const children = childMap[parentNode.id] || [];
// 🚀 首个子节点 Y 起始:parent.y 正下方
let startY = parentNode.y + CARD_H + VERTICAL_GAP;
// 🚀 首个子节点 Y 起始:与父节点水平平齐
let startY = parentNode.y;
children.forEach((child, index) => {
const subtreeH = calcSubtreeHeight(child.id);
@ -152,7 +152,7 @@ export default {
? parentNode.x + CARD_W + GAP_X
: parentNode.x - CARD_W - GAP_X;
// 🚀 Y 轴:严格基于父节点向下延伸,同级按 index 错开
// 🚀 Y 轴:首子与父平齐,后续兄弟向下累加
const childY = startY;
const childNode = {
@ -168,8 +168,8 @@ export default {
// 递归放置孙子节点(沿相同方向)
placeChildren(childNode, side);
// 🚀 下一个兄弟节点跳到当前子树高度之后,严禁 Y 坐标重叠
startY += subtreeH + VERTICAL_GAP;
// 🚀 下一个兄弟节点跳到当前子树高度之后calcSubtreeHeight 已含 GAP
startY += subtreeH;
});
};
@ -194,7 +194,7 @@ export default {
// ============================================================
// 🚀 连线计算:中央 → 分支
// ============================================================
// 🔧 连线:卡片边缘到边缘,杜绝穿模
// 🔧 连线:卡片边缘到边缘,动态追踪 Y 坐标,杜绝箭头悬空
lines() {
const result = [];
const flatMap = {};
@ -207,9 +207,12 @@ export default {
const isLeft = n.x < parent.x;
const startX = isLeft ? n.x + 160 : parent.x + 160;
const endX = isLeft ? parent.x : n.x;
// 🚀 动态计算真实的 Y 坐标对接点
const startY = isLeft ? n.y + 40 : parent.y + 40;
const endY = isLeft ? parent.y + 40 : n.y + 40;
result.push({
x1: startX, y1: parent.y + 40,
x2: endX, y2: parent.y + 40,
x1: startX, y1: startY,
x2: endX, y2: endY,
dashed: parent.status === 'COMPLETED' || parent.status === 'ARCHIVED',
});
});