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:
@ -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 = {};
|
||||
@ -206,10 +206,13 @@ export default {
|
||||
if (!parent) return;
|
||||
const isLeft = n.x < parent.x;
|
||||
const startX = isLeft ? n.x + 160 : parent.x + 160;
|
||||
const endX = isLeft ? parent.x : n.x;
|
||||
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',
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user