feat(tsk_PbcHccuPmwxV): 任务取消/删除 UI 入口(带确认)

## 后端
- Store.deleteTask():BFS 收集子孙 id,清理 events,删根节点(子孙/runs/approvals 由 FK CASCADE 自动清除),广播 task.updated{kind:'delete'}
- API POST /api/tasks/:id/cancel:transition → cancelled(复用状态机守卫)
- API DELETE /api/tasks/:id:级联删除任务及子孙

## 前端
- 任务行(task-row):鼠标悬停后右侧出现「✕取消」「🗑删除」浮动按钮,点击弹 confirm() 二次确认(含子孙数提示),stopPropagation 防止触发行展开
- 任务详情(detail):底部行内确认区——「取消任务」显示确认条(确认取消 / 算了),「删除」显示含级联提示的高亮确认条,折叠/切换项目时自动清除确认状态
- 归档详情对话框:底部增加「🗑 删除归档」按钮,confirm() 确认后删除并关闭弹窗
- CSS:.task-row-cancel/.task-row-delete(悬停淡入)、.detail-actions / .detail-action-confirm / .detail-confirm-msg、.archive-modal-actions

## 测试
- 新增 4 个 store 测试:deleteTask 叶子、deleteTask 级联、deleteTask 不存在、transition→cancelled
- 全部 78 条 pass

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-13 03:37:01 +08:00
parent 740d2c2637
commit b60225779c
5 changed files with 279 additions and 3 deletions
+12
View File
@@ -203,6 +203,18 @@ export function buildServer(opts: ApiOptions): FastifyInstance {
return store.transition(id, b.to as TaskStatus, b.meta ?? {});
});
// 取消任务(transition → cancelled;状态机已守卫合法流转)
app.post('/api/tasks/:id/cancel', (req) => {
const { id } = req.params as { id: string };
return store.transition(id, 'cancelled', { by: 'user' });
});
// 彻底删除任务及其子孙(不可恢复,cascade)
app.delete('/api/tasks/:id', (req) => {
const { id } = req.params as { id: string };
return store.deleteTask(id);
});
// 审批闸:accept / rejectreject 必带 reason
// exec 闸的 accept = 通过并合并(PR 闭环):先 merge 再 decidemerge 失败 → 400,任务保留在审核闸。
app.post('/api/tasks/:id/decide', async (req) => {