diff --git a/src/Hua.Todo.Web/src/components/TaskItem.vue b/src/Hua.Todo.Web/src/components/TaskItem.vue index 4e3b7bb..8d1f900 100644 --- a/src/Hua.Todo.Web/src/components/TaskItem.vue +++ b/src/Hua.Todo.Web/src/components/TaskItem.vue @@ -201,6 +201,11 @@ const handleSubTaskUpdated = (updatedTask: Task) => { if (props.task.subTasks) { const index = props.task.subTasks.findIndex(t => t.id === updatedTask.id); if (index !== -1) { + // 保留现有子任务:API 返回的 DTO 是平面的(subTasks=[]),直接替换会丢失该任务已有的所有后代 + const existing = props.task.subTasks[index]; + if (existing.subTasks?.length && !updatedTask.subTasks?.length) { + updatedTask.subTasks = existing.subTasks; + } props.task.subTasks[index] = updatedTask; emit('updated', props.task); } diff --git a/src/Hua.Todo.Web/src/components/TaskList.vue b/src/Hua.Todo.Web/src/components/TaskList.vue index 8f464c2..8997ff7 100644 --- a/src/Hua.Todo.Web/src/components/TaskList.vue +++ b/src/Hua.Todo.Web/src/components/TaskList.vue @@ -402,6 +402,10 @@ const handleTaskUpdated = (updatedTask: Task) => { const updateTaskInTree = (tasks: Task[], updatedTask: Task): boolean => { for (let i = 0; i < tasks.length; i++) { if (tasks[i].id === updatedTask.id) { + // 保留现有子任务:API 返回的 DTO 是平面的(subTasks=[]),直接替换会丢失该任务已有的所有后代 + if (tasks[i].subTasks?.length && !updatedTask.subTasks?.length) { + updatedTask.subTasks = tasks[i].subTasks; + } tasks[i] = updatedTask; return true; } @@ -434,8 +438,22 @@ const deleteTaskFromTree = (tasks: Task[], taskId: string): boolean => { return false; }; -const handleSubTaskCreated = (subTask: Task) => { - addSubTaskToTree(tasks.value, subTask); +const handleSubTaskCreated = async (subTask: Task) => { + try { + // 直接从 API 重新加载完整任务树,保证所有层级的嵌套关系正确且响应式链路完整。 + // 深层嵌套(3+层)时,原地修改树(addSubTaskToTree)无法可靠触发 TaskItem 的 re-render, + // 因为 filteredTasks 计算属性中的浅拷贝({ ...task })在替换 subTasks 引用时会断开追踪。 + const response = await taskApi.getTasks(); + if (response.success && response.data) { + tasks.value = response.data; + } else { + console.warn('[handleSubTaskCreated] API 返回异常,尝试就地添加'); + addSubTaskToTree(tasks.value, subTask); + } + } catch (error) { + console.error('[handleSubTaskCreated] 加载任务失败,使用就地添加:', error); + addSubTaskToTree(tasks.value, subTask); + } lastSyncTime.value = Date.now(); }; @@ -445,7 +463,8 @@ const addSubTaskToTree = (tasks: Task[], subTask: Task): boolean => { if (!tasks[i].subTasks) { tasks[i].subTasks = []; } - tasks[i].subTasks.push(subTask); + // 使用新数组引用替换而非 push,确保 Vue 3 响应式系统无论嵌套多深都能检测到变更 + tasks[i].subTasks = [...tasks[i].subTasks, subTask]; return true; } if (tasks[i].subTasks && tasks[i].subTasks.length > 0) { @@ -454,6 +473,7 @@ const addSubTaskToTree = (tasks: Task[], subTask: Task): boolean => { } } } + console.warn(`[addSubTaskToTree] 未找到父任务 id="${subTask.parentTaskId}",子任务 id="${subTask.id}" 未能加入树中`); return false; };