From d3def15bb3c785e0381d9cd157ac687a2036132c Mon Sep 17 00:00:00 2001 From: duxingchen Date: Tue, 11 Aug 2026 10:02:06 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20TaskFlowView=20=E9=80=92=E5=BD=92?= =?UTF-8?q?=E6=B8=B2=E6=9F=93=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 废弃拍平算法,构建全局childMap保留真实树结构 - 新增renderBranch递归渲染函数,子子孙孙向外延伸 - 焦点/全景模式统一应用递归算法,连线从真实父节点引出 --- .../src/components/TaskTree/TaskFlowView.tsx | 127 +++++++----------- 1 file changed, 48 insertions(+), 79 deletions(-) diff --git a/frontend/src/components/TaskTree/TaskFlowView.tsx b/frontend/src/components/TaskTree/TaskFlowView.tsx index 95d7f9f..4447d40 100644 --- a/frontend/src/components/TaskTree/TaskFlowView.tsx +++ b/frontend/src/components/TaskTree/TaskFlowView.tsx @@ -109,7 +109,7 @@ export const TaskFlowView = memo(function TaskFlowView({ tasks, onAction, curren const [recordsTask, setRecordsTask] = useState(null); // 数据分类 — 🚀 虚拟扁平化:三层+嵌套协助统一挂在视觉主干下 - const { activeMain, historicalMain, subsByRootMain, rootMainMap } = useMemo(() => { + const { activeMain, historicalMain, childMap, rootMainMap } = useMemo(() => { ALL_TASKS.clear(); if (tasks.length) collectAll(tasks); const all = Array.from(ALL_TASKS); // 拍平映射 @@ -136,34 +136,42 @@ export const TaskFlowView = memo(function TaskFlowView({ tasks, onAction, curren // 排序 am.sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()); hm.sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()); - // 🚀 层级排序:直接子节点优先,嵌套子节点跟在父节点之后 - for (const key of Object.keys(srm)) { - const subs = srm[key]; - // 构建父子索引 - const childMap: Record = {}; - for (const s of subs) { - const pid = s.parent_task_id || ''; - if (!childMap[pid]) childMap[pid] = []; - childMap[pid].push(s); - } - Object.values(childMap).forEach(arr => arr.sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime())); - // 递归收集:父 → 子 → 孙 - const ordered: TaskResponse[] = []; - const collect = (parentId: string) => { - const kids = childMap[parentId] || []; - for (const k of kids) { - ordered.push(k); - collect(k.id); - } - }; - collect(key); // 从根主干开始收集 - srm[key] = ordered; + // 🚀 构建全局 childMap(按 parent_task_id 索引直接子节点,保留真实树结构) + const childMap: Record = {}; + for (const t of all) { + const pid = t.parent_task_id || ''; + if (!childMap[pid]) childMap[pid] = []; + childMap[pid].push(t); } - return { activeMain: am, historicalMain: hm, subsByRootMain: srm, rootMainMap: rootMap }; + Object.values(childMap).forEach(arr => arr.sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime())); + return { activeMain: am, historicalMain: hm, childMap, rootMainMap: rootMap }; }, [tasks]); const allMains = [...historicalMain, ...activeMain]; + // 🚀 递归渲染分支节点 — 每个节点从自己的 childMap 获取直系子孙,保持树结构不断裂 + const renderBranch = (node: TaskResponse, side: 'left' | 'right', isLegacy: boolean, rootMainId: string): JSX.Element => { + const kids = childMap[node.id] || []; + const arrow = side === 'left' + ? (
) + : (
); + const card = ; + const kidsContainer = kids.length > 0 ? ( +
+ {kids.map(k => renderBranch(k, side, isLegacy, rootMainId))} +
+ ) : null; + return ( +
+ {side === 'left' && kidsContainer} + {side === 'left' && card} + {arrow} + {side === 'right' && card} + {side === 'right' && kidsContainer} +
+ ); + }; + return (
{/* 模式切换 */} @@ -178,44 +186,23 @@ export const TaskFlowView = memo(function TaskFlowView({ tasks, onAction, curren {!showFullMap && (
{activeMain.map(mainTask => { - const children = subsByRootMain[mainTask.id] || []; - const leftSubs = children.filter((_, i) => i % 2 === 0); - const rightSubs = children.filter((_, i) => i % 2 === 1); + const directChildren = childMap[mainTask.id] || []; + const leftDirect = directChildren.filter((_, i) => i % 2 === 0); + const rightDirect = directChildren.filter((_, i) => i % 2 === 1); + const isLegacy = !active(mainTask.status); return (
- {/* 左翼 */} + {/* 左翼 — 递归渲染,子子孙孙向外延伸 */}
- {leftSubs.map(t => { - const isLegacy = !active(mainTask.status); - return ( -
- -
- -
-
-
- ); - })} + {leftDirect.map(t => renderBranch(t, 'left', isLegacy, mainTask.id))}
{/* 中央主干 */}
- {/* 右翼 */} + {/* 右翼 — 递归渲染,子子孙孙向外延伸 */}
- {rightSubs.map(t => { - const isLegacy = !active(mainTask.status); - return ( -
-
-
- -
- -
- ); - })} + {rightDirect.map(t => renderBranch(t, 'right', isLegacy, mainTask.id))}
); @@ -228,28 +215,19 @@ export const TaskFlowView = memo(function TaskFlowView({ tasks, onAction, curren {showFullMap && (
{allMains.map(mainTask => { - const children = subsByRootMain[mainTask.id] || []; - const hasLegacyActive = children.some(c => !isMain(c) && !active(mainTask.status)); - const leftChildren = children.filter((_, i) => i % 2 === 0); - const rightChildren = children.filter((_, i) => i % 2 === 1); + const directChildren = childMap[mainTask.id] || []; + const hasLegacyActive = directChildren.some(c => !isMain(c) && !active(mainTask.status)); + const leftDirect = directChildren.filter((_, i) => i % 2 === 0); + const rightDirect = directChildren.filter((_, i) => i % 2 === 1); const lineStyle = hasLegacyActive && !active(mainTask.status); return (
- {/* 左翼 */} + {/* 左翼 — 递归渲染,子子孙孙向外延伸 */}
- {leftChildren.map(c => ( -
- -
- -
-
-
- ))} + {leftDirect.map(c => renderBranch(c, 'left', lineStyle, mainTask.id))}
{/* 中央 */}
@@ -259,18 +237,9 @@ export const TaskFlowView = memo(function TaskFlowView({ tasks, onAction, curren
)}
- {/* 右翼 */} + {/* 右翼 — 递归渲染,子子孙孙向外延伸 */}
- {rightChildren.map(c => ( -
-
-
- -
- -
- ))} + {rightDirect.map(c => renderBranch(c, 'right', lineStyle, mainTask.id))}