第 43 课:任务详情抽屉里的批量处理闭环与删除联动

这一课我们继续沿着"任务管理页主线"往前推进。

上一课你已经做到:

  • 在任务详情抽屉里直接建立批量选择上下文
  • 选中当前任务
  • 选中同状态任务
  • 选中同优先级任务
  • 清空当前批量选择

但那时候还差最后半步。

因为用户虽然已经能在详情里"选一批任务",却还不能在详情里"直接把这批任务处理完"。

所以这节课真正要补上的就是:

让任务详情抽屉从"建立批量上下文的入口",继续升级成"完成批量处理的闭环入口"。

也就是说,用户现在可以直接在详情抽屉里:

  • 批量改状态
  • 批量删除
  • 并且当当前详情任务也被批量删掉时,自动切到相邻仍然存在的任务

这就更像真实后台系统了。


这一课一句话在做什么?

这一课本质上是在做:

把"看一条任务 -> 选一批任务 -> 处理这一批任务"完整闭环起来。

重点不是多加几个按钮。

重点是把三件事真正接起来:

  1. 详情抽屉继续只负责发起意图
  2. 页面层继续复用已有批量处理逻辑
  3. 删除当前详情任务时,详情上下文还能自然跳到相邻任务

为什么这一步非常像真实后台?

因为真实后台里,用户很少只"看完一条就结束"。

更常见的路径其实是:

  1. 打开一条任务详情
  2. 发现它代表一类任务
  3. 直接把这一类任务一起推进

例如:

  • 看一条 待开始 任务后,想把所有 待开始 任务一起改成 进行中
  • 看一条中优先级任务后,想把同类任务一起删掉

如果这时候用户必须:

  • 先回到列表顶部
  • 再去找批量操作栏
  • 再重复执行动作

体验就会断裂。

所以这一课的核心价值是:

让详情抽屉不只是"上下文查看器",还成为真正的工作流执行入口。


这节课最关键的设计结论

1. 详情抽屉仍然是展示层,不直接掌握业务状态

这一课里,TaskDetailDrawer.vue 新增了两个事件:

  • change-selected-status
  • delete-selected

但它依然没有直接去改:

  • selectedTaskIds
  • 任务列表数据
  • 当前详情 id

它只是把意图抛给页面层。

这说明一个非常重要的原则:

即使功能越来越强,展示组件也不应该顺手接管跨区域业务状态。

2. 新入口最好复用旧逻辑,而不是复制一套逻辑

这一课没有新写一套"详情专用批量改状态逻辑"。

也没有新写一套"详情专用批量删除逻辑"。

而是继续复用页面层已有的:

  • handleUpdateSelectedTasksStatus
  • handleDeleteSelectedTasks

这很重要。

因为真正可靠的工程写法通常不是:

  • 每加一个入口,就复制一套业务逻辑

而是:

  • 新入口接到同一套页面级业务能力上

这样后面无论从哪里发起批量处理:

  • 批量操作栏
  • 详情抽屉

都会走同一套规则。

3. "当前详情任务也参与了批量处理"需要单独考虑

这一课里,页面层在批量改状态前会先记录:

  • 当前详情任务是否也在已选集合里

这样处理成功后,提示文案就可以更准确地告诉用户:

  • 当前详情上下文也已经同步更新

这是一种很典型的后台交互细节。

因为用户不只关心:

  • 改了多少条

还关心:

  • 我现在正在看的这条有没有一起更新

4. 批量删除时,"详情切到哪条"值得单独抽成纯函数

这一课新抽了一个纯函数:

  • getTaskDetailBatchDeleteFallbackId

它专门处理这个问题:

如果当前详情任务正好包含在这次批量删除里,删除后详情应该切到哪一条?

规则是:

  1. 先沿着当前详情位置往后找还活着的任务
  2. 后面没有,再往前找
  3. 如果前后都没有,就关闭详情

这类逻辑很适合抽成纯函数。

因为它:

  • 决策清晰
  • 不依赖组件实例
  • 非常适合单元测试

5. E2E 测试必须验证"当前代码构建产物",不能误测旧 dist

这一课顺手修了一个很关键的测试工程问题:

  • playwright.config.ts

之前 CI=1 时只启动 vite preview,如果本地 dist 不是最新的,就可能出现:

  • 你改了源码
  • 但 E2E 跑的还是旧构建产物

现在已经改成:

  • npm run build && npm run preview

这说明一个很重要的工程意识:

端到端测试不只是"会点页面",还必须保证自己验证的是当前真实代码。


这次主要改了哪些文件?

这一课主要涉及这些文件:

  1. src/components/tasks/TaskDetailDrawer.vue
  2. src/views/TasksView.vue
  3. src/utils/taskDetailLinkage.ts
  4. src/utils/__tests__/taskDetailLinkage.spec.ts
  5. src/components/tasks/__tests__/taskDetailDrawer.spec.ts
  6. e2e/pages/TasksPage.ts
  7. e2e/app.spec.ts
  8. playwright.config.ts
  9. docs/README.md

另外新增了本节文档:

  • docs/43-task-detail-drawer-batch-actions-and-delete-linkage.md

TaskDetailDrawer.vue 里学什么?

这一课给详情抽屉新增了一个真正执行动作的 批量处理 区块。

1. "批量上下文"和"批量处理"要分成两个区块

这节课非常值得你注意的一点是:

  • 上一课的区块叫 批量上下文
  • 这一课新增的区块叫 批量处理

它们不是一回事。

前者解决的是:

  • 我要把哪些任务纳入处理范围

后者解决的是:

  • 我准备对这批任务做什么

这就是很典型的后台页面分层:

  1. 先组织对象
  2. 再执行动作

2. 批量改状态按钮继续复用系统里的全部合法状态

这一区块没有手写四个死按钮值。

而是继续遍历:

  • props.statusOptions

这样详情抽屉里的批量改状态能力,会自动和整套任务状态定义保持一致。

这是很好的习惯。

因为展示入口最好不要偷偷复制业务枚举。

3. 按钮禁用条件要和页面真实能力保持一致

这一课里,这组按钮在下面几种情况下会禁用:

  • 页面正在加载
  • 当前视图不支持批量处理
  • 当前根本没有已选任务

也就是说,详情抽屉不能制造一组"页面层实际上接不住"的假入口。

这再次说明:

展示层入口必须尊重页面真实业务能力边界。


TasksView.vue 里学什么?

这一课最核心的协调逻辑继续放在页面层。

1. 批量改状态现在要考虑"当前详情是否一起变了"

handleUpdateSelectedTasksStatus 这次新增了一步:

  • 在真正批量更新前,先记录当前详情任务是否也在已选集合里

这样更新成功后就可以区分两种反馈:

  • 普通批量更新成功
  • 当前详情上下文也已经同步更新

这个细节会让页面提示更贴近用户正在做的事。

2. 批量删除要在真正删除前先快照上下文

handleDeleteSelectedTasks 里这次先保存了:

  • 删除前的已选 id 列表
  • 删除前的已选数量
  • 当前详情任务是否包含在这次删除里
  • 删除后应该跳到哪条任务

注意这里非常关键:

这些信息都必须在真正删除之前先算好。

因为一旦删除完成:

  • 选择状态会被清空
  • 原始任务可能已经不存在

如果这时候才开始判断,就拿不到完整上下文了。

3. 当前详情任务被批量删除后,要么切相邻任务,要么关闭详情

删除成功后,页面层现在会这样处理:

  1. 如果当前详情任务不在删除范围里,详情继续保留
  2. 如果在删除范围里,并且还能找到相邻存活任务,就自动切过去
  3. 如果找不到,就关闭详情

这就是很真实的后台体验。

因为详情抽屉不应该留在一条已经不存在的记录上。


taskDetailLinkage.ts 里学什么?

这一课新增的纯函数是:

  • getTaskDetailBatchDeleteFallbackId

它本质上是在回答一个非常具体的问题:

一批任务删掉之后,当前详情还能自然停留在什么地方?

这个函数的价值不只是"代码能跑"。

更重要的是它把这类复杂联动拆成了:

  • 可独立理解
  • 可独立测试
  • 可被页面层复用

这就是你之后写复杂页面时要重点训练的能力:

把复杂联动里的"决策部分"抽成纯函数。


单元测试这次补了什么?

1. taskDetailDrawer.spec.ts

这一课在抽屉组件测试里补了:

  • 批量处理 区块是否渲染
  • 批量改为待评审 是否会抛出 change-selected-status
  • 批量删除已选 是否会抛出 delete-selected

这说明展示层组件测试仍然要抓住两件事:

  1. 渲染是否符合设计
  2. 事件是否把正确意图抛了出去

2. taskDetailLinkage.spec.ts

这一课给新纯函数补了几类关键场景:

  • 删除当前详情后优先切到后一条存活任务
  • 如果后面都删掉了,就回退到前一条存活任务
  • 如果前后都没有,就返回 null
  • 如果当前详情任务根本不在上下文里,也返回 null

这类测试特别有价值。

因为它把"最容易写错的边界联动"锁得很死。


E2E 这次补了什么?

1. Page Object 补了详情抽屉里的批量处理入口

文件:

  • e2e/pages/TasksPage.ts

新增方法:

  • changeSelectedTasksStatusFromDetailDrawer
  • deleteSelectedTasksFromDetailDrawer

另外还顺手让删除确认框和确认按钮同时兼容:

  • 单条删除
  • 批量删除

2. 新增了两条真正闭环的端到端测试

文件:

  • e2e/app.spec.ts

新增测试:

  • user can batch update selected tasks from detail drawer and keep detail synced
  • user can batch delete selected tasks from detail drawer and switch detail to adjacent survivor

它们分别验证:

批量改状态链路
  1. 从详情抽屉选中当前任务
  2. 选中同状态任务
  3. 在详情抽屉里直接批量改成 进行中
  4. 当前详情状态立刻同步
  5. 通过详情抽屉继续导航到下一条 / 下一条
  6. 确认跨页任务也已经一起更新
批量删除链路
  1. 从详情抽屉选中当前任务
  2. 选中同状态任务
  3. 直接从详情抽屉里发起批量删除
  4. 确认批量删除确认框
  5. 删除成功后自动切到仍然存活的相邻任务
  6. 地址栏 taskId 和详情位置摘要一起同步更新

这里你要特别注意:

这两条测试测的不是"按钮能不能点"。

它们测的是整条联动链:

  • 详情抽屉
  • 批量选择
  • 页面级批量处理
  • 详情同步
  • 删除后的上下文恢复
  • 地址栏联动

这已经是比较完整的真实后台场景了。


你现在真正应该学会什么?

学完这一课,你最应该记住的是下面 6 件事:

1. 一个入口可以既服务单条上下文,也服务批量工作流

详情抽屉不是只能"看详情"。

它也可以成为组织和执行批量工作的入口。

2. "建立批量上下文"和"执行批量处理"最好分成两步

不要把:

  • 选哪些对象
  • 做什么动作

混成一个区块。

3. 新入口最好的接法,是复用旧业务逻辑

不要因为入口换了,就重新复制一套业务实现。

4. 复杂删除联动最好先快照上下文,再执行真正删除

否则删除后很多判断条件都会消失。

5. 复杂页面里的"下一条切哪里"很适合抽成纯函数

这样更容易:

  • 思考
  • 测试
  • 复用

6. E2E 测试不只要测功能,还要保证测的是最新构建结果

这是工程可靠性的一部分,不只是脚本技巧。

相关推荐
likerhood1 小时前
Java 访问修饰符:public、protected、private讲解
java·开发语言·javascript
刀法如飞1 小时前
JavaScript 数组去重的 20 种实现方式,学会用不同思路解决问题
前端·javascript·算法
Ting-yu1 小时前
SpringCloud快速入门(5)---- 均衡负载
java·spring·spring cloud
学不思则罔1 小时前
ParallelStream并发陷阱解析
java·开发语言·windows
小江的记录本1 小时前
【AI大模型选型指南】《2026年5月(最新版)国内外主流AI大模型选型指南》(个人版)
前端·人工智能·后端·ai·aigc·ai编程·ai写作
认真的小羽❅1 小时前
【Java并发编程】volatile关键字深度解析:从内存语义到实际应用
java·开发语言
jayson.h2 小时前
可视化界面
开发语言·python
@PHARAOH2 小时前
HOW - 前端输入场景支持拼音匹配
前端