跳转至

SPEC-006 — task delete 二选一 confirm UI

1. 背景

来源:[[2026-05-26-task-delete-cascade-cleanup-tbd|2026-05-26-task-delete级联清理待确认]] — v0.10.47 第三轮独立 agent 跨模块发现。

现状task-manager.ts:319-326 controlTask('delete'): - ✅ 调 removeTask(id) — 清 task store - ✅ 调 clearTaskProgress(id) — 清进度 - ❌ 不删 MapTaskData 表里 taskId == 被删 task 的商家行

后果: - 删了任务后,该 taskId 的商家数据变孤儿 - TaskFilterPicker 只列存在的 task → 用户无法再按这个 taskId 过滤 - DataView 默认显示全部 → 孤儿数据仍可见但无法溯源

是 bug 还是设计:可能是设计意图(保留爬取结果),也可能是忘记级联删。没文档说明

2. 决策:二选一 confirm UI

3 方案权衡:

方案 优点 缺点
A. 保留 + 文档化 最省事 孤儿数据 + TaskFilterPicker 问题没解
B. 级联删(默认行为) 数据干净 已删任务的商家数据会丢,无挽回
C. 二选一 confirm UI ✅ UX 最佳,用户决定 多一个 dialog 步骤

选 C: - 删任务前弹 Dialog - 显示「该任务共采集到 N 条 商家数据,是否一并删除?」 - 按钮:「仅删任务(保留数据)」「全部删除」「取消」

3. 目标 / 非目标

目标

  • 删任务时让用户明确决定商家数据去留
  • 不删数据时仍保留可溯源能力(见 §6)
  • 全部删除时一致清掉:task / progress / MapTaskData 行

非目标

  • 不批量删(多任务一次性删)— 本期仅单任务
  • 不做"软删除"(标记删除但保留)— 用户选了删就真删

4. UX 设计

4.1 触发点

  • task-view.tsx 任务卡上点删除按钮
  • TaskTableActions 删除按钮

4.2 Dialog 设计

┌────────────────────────────────────────┐
│  删除任务「{任务名}」?                  │
│                                         │
│  该任务共采集到 N 条商家数据。           │
│  请选择数据处理方式:                    │
│                                         │
│  ⚪ 仅删任务,保留商家数据(推荐)       │
│      数据可在「商家列表」中继续查看      │
│  ⚪ 同时删除该任务的全部 N 条商家数据    │
│      数据无法恢复                        │
│                                         │
│         [取消]  [确认删除]              │
└────────────────────────────────────────┘

默认选项:保留数据(安全默认,符合最小破坏原则)。

4.3 边缘情况

  • N = 0:不弹 dialog,直接走原 delete 流程
  • N > 10000:dialog 显示「N 条数据,删除可能耗时」+ loading 状态

5. 数据流改动

5.1 API 层

controlTask('delete') 签名扩展:

export async function controlTask(
  id: string,
  action: 'pause' | 'resume' | 'stop' | 'delete' | 'rename',
  payload?: any  // { deleteData?: boolean } for action=delete
)

5.2 实现

if (action === 'delete') {
  if (isBatchActive(id)) await stopBatch(id);
  const cur = await storage.getItem<string>(CURRENT_TASK_KEY);
  if (cur === id) await storage.setItem(CURRENT_TASK_KEY, '');
  await removeTask(id);
  await clearTaskProgress(id);
  if (payload?.deleteData === true) {
    await removeByQuery('MapTaskData', { taskId: id });  // 新增
  }
  await pumpTasks();
}

5.3 UI 层

新组件 src/components/task/task-delete-confirm-dialog.tsx: - props: { taskId, taskName, open, onClose, onConfirm } - mount 时调 countByQuery('MapTaskData', { taskId }) 拿 N - onConfirm 调 controlTask(taskId, 'delete', { deleteData: chosen })

6. 保留数据时的溯源(关键)

如果用户选「保留数据」,孤儿数据要能溯源:

方案
a. TaskFilterPicker 加"已删任务"分组 用户能筛 要查所有 taskId 跟 task store 对比,慢
b. 删任务时给 MapTaskData 行加 taskDeleted: true 字段 加字段,要写迁移
c. 不管 — 用户筛"全部" + 按时间排序找 零成本 UX 差

推荐 a:TaskFilterPicker 异步算"孤儿 taskId 集合" → 加 "已删任务" 分组(可折叠)。可接受性能开销(只在 dropdown 打开时算一次)。

7. 批量删任务(2026-05-28 决策追加)

用户选「做」。多任务一次性删时: - Dialog 显示「选中 M 个任务,共 N 条商家数据,请选择数据处理方式」 - N = sum(countByQuery({ taskId }) for each task) - 实现:controlTask 单次调用 → 新建 controlTasksBatch(ids, action, payload) 包装

UI:TaskTableToolbar 加批量删按钮(仅 selection.length > 1 时显示)。

// 批量删 API(新增)
export async function controlTasksBatch(
  ids: string[],
  action: 'delete' | 'stop' | 'pause' | 'resume',
  payload?: any
) {
  // 串行,避免 jsstore 并发冲突
  for (const id of ids) {
    await controlTask(id, action, payload);
  }
}

性能注意:N 条 MapTaskData 删除可能慢(10w+ 时几百 ms),UI 期间显示 loading。

8. 实施计划

Step 工作 估时
1 task-delete-confirm-dialog.tsx 组件(含批量模式 props) 1.5h
2 task-manager.ts controlTaskdeleteData payload 0.5h
3 controlTasksBatch API + background message handler 0.5h
4 task-view / TaskTableActions 单删 trigger 点替换 0.5h
5 TaskTableToolbar 批量删按钮(多选时显示) 0.5h
6 TaskFilterPicker 加"已删任务"分组(方案 a) 1h
7 测试 + sanity check(单删 / 批量删 / 0 数据 / 10w 数据) 1h
合计 ~5.5h

9. 决策记录(2026-05-28 用户拍板)

# 决策点 用户选择
1 默认选项 保留数据(安全默认)
2 TaskFilterPicker 孤儿分组 (方案 a)
3 批量删任务 (本期一并实现)

相关

  • [[2026-05-26-task-delete-cascade-cleanup-tbd|2026-05-26-task-delete级联清理待确认]] — 原始发现
  • 共享队列架构 — task store / progress / MapTaskData 三层关系