-
-
⇢{{ t.title || t.taskId }}
+
+
+
+
+
+ ⇢{{ t.title || t.taskId }}
+
+
+
+ {{ queueBadge(t).text }}
+ {{ taskStyle(t.status).label }}
+ · {{ taskSubtitle(t, now) }}
+
+
+
+ ⚠ {{ blockedLabel(t, g.tasks || []) }}
+
-
-
- {{ queueBadge(t).text }}
- {{ taskStyle(t.status).label }}
- · {{ taskSubtitle(t, now) }}
-
-
-
- ⚠ {{ blockedLabel(t, g.tasks || []) }}
-
-
-
-
-
-
-
+ v-if="taskActions(t).length"
+ class="more t-more"
+ title="更多"
+ @click.stop="menu = menu === t.taskId ? '' : t.taskId"
+ >⋯
+
+
+
+
+
+
+
+ -
+
+ {{ st.title || st.taskId }}
+ {{ taskStyle(st.status).label }}
+
+
+
这个目标下还没有任务。
@@ -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
|