From b9a45b4112a5852ecf63fd4eaedbcaa8072d2439 Mon Sep 17 00:00:00 2001 From: yibo Date: Mon, 31 Aug 2026 11:01:32 +0800 Subject: [PATCH] feat(task-decomposition): 看板展示子任务(43 号文档 Phase 4) --- web/src/components/graph-console/GoalTaskPane.vue | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------- web/src/components/task-board/TaskTable.vue | 10 ++++++++++ 2 files changed, 86 insertions(+), 50 deletions(-) diff --git a/web/src/components/graph-console/GoalTaskPane.vue b/web/src/components/graph-console/GoalTaskPane.vue index b0d6b77..4912918 100644 --- a/web/src/components/graph-console/GoalTaskPane.vue +++ b/web/src/components/graph-console/GoalTaskPane.vue @@ -76,58 +76,70 @@

这个目标下还没有任务。

@@ -173,6 +185,14 @@ let clock = null; onMounted(() => { clock = setInterval(() => { now.value = Date.now(); }, 30000); }); onBeforeUnmount(() => clearInterval(clock)); +/** 43 号文档:g.tasks 是摊平的(父任务 + 子任务同一个数组),这里按 parentTaskId 分两层 */ +function topLevel(tasks) { + return (tasks || []).filter((t) => !t.parentTaskId); +} +function childrenOf(tasks, parentTaskId) { + return (tasks || []).filter((t) => t.parentTaskId === parentTaskId); +} + function toggle(goalId) { const next = new Set(collapsed.value); if (next.has(goalId)) next.delete(goalId); else next.add(goalId); @@ -378,6 +398,12 @@ function onDispatched(d) { .dot.pulse { animation: pulse 1.6s ease-in-out infinite; } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } } +.subtasks { list-style: none; margin: 0 0 2px; padding: 0 0 0 27px; } +.subtask { display: flex; align-items: center; gap: 5px; padding: 2px 6px; font-size: 11px; } +.subtask .dot.sm { margin-top: 0; width: 6px; height: 6px; } +.st-title { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #555; } +.st-status { font-size: 10px; font-weight: 600; white-space: nowrap; } + .t-body { min-width: 0; flex: 1; } .t-title { font-size: 12px; line-height: 1.4; color: #1a1a1a; diff --git a/web/src/components/task-board/TaskTable.vue b/web/src/components/task-board/TaskTable.vue index df96297..ed34338 100644 --- a/web/src/components/task-board/TaskTable.vue +++ b/web/src/components/task-board/TaskTable.vue @@ -50,6 +50,9 @@ {{ queueBadge(t).text }} + +
↳ 子任务 · 属于「{{ parentTitle(t) }}」
{{ t.title || '(无标题)' }}
{{ t.description }}
{{ blockedLabel(t, siblingsOf(t)) || '挂起' }}
@@ -95,6 +98,12 @@ function siblingsOf(t) { return (g && g.tasks) || []; } +/** 43 号文档:子任务行指回父任务标题——父任务恒在同一个 goal 下,查兄弟列表就够了 */ +function parentTitle(t) { + const parent = siblingsOf(t).find((x) => x.taskId === t.parentTaskId); + return (parent && (parent.title || parent.taskId)) || t.parentTaskId; +} + function dotStyle(s) { return s.hollow ? { background: 'transparent', borderColor: s.color } : { background: s.color, borderColor: s.color }; } @@ -208,6 +217,7 @@ tbody td { padding: 8px 10px; border-bottom: 1px solid #f4f4f6; vertical-align: .title { font-weight: 600; color: #1a1a1a; } .desc { margin-top: 2px; color: #888; font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .blocked-line { margin-top: 2px; color: #d33; font-size: 11px; } +.parent-line { color: #8a5cd6; font-size: 10px; margin-bottom: 2px; } .c-goal, .c-assignee { color: #555; white-space: nowrap; } .c-priority { white-space: nowrap; } .c-time { white-space: nowrap; color: #666; } -- libgit2 0.21.4