第 40 课:任务详情抽屉里的编辑 / 删除联动强化
这一课我们继续沿着"任务管理页主线"往前推,把上一课已经更像真实后台的 任务详情抽屉 再补上两个非常常见的后台交互能力:
- 从详情抽屉直接进入编辑
- 删除当前详情任务后,自动切到相邻任务
同时还要保证:
- 编辑保存后,详情上下文不要丢
- 删除后,详情抽屉不要留在一条已经不存在的任务上
- 这些联动逻辑可以被单元测试稳定验证
- 页面层、E2E 和文档一起跟上
这一课一句话在做什么?
这一课本质上是在做:
让任务详情抽屉从"只能看"的上下文面板,升级成"能继续推进编辑与删除流程"的真实后台工作入口。
这一步很关键。
因为真实后台里,用户不会只打开详情看一眼就回列表。
更常见的是:
- 先在列表里打开详情
- 在详情里发现字段要改
- 直接点编辑
- 或者确认这条记录应该删掉
- 操作完成后,页面还要保持连贯
所以这节课练的不是单个按钮,而是:
详情上下文和页面级增删改流程之间,怎样保持体验连续。
这次要解决的两个真实后台问题
1. 从详情里去编辑,保存后详情不能丢
如果用户是在详情抽屉里点了 编辑任务:
- 编辑弹窗打开前,通常要先把详情抽屉关掉
- 不然两个覆盖层会叠在一起
但如果只是简单关掉详情,你会遇到一个体验问题:
- 编辑保存成功了
- 用户却回到了"没有详情上下文"的列表页
这很割裂。
更合理的体验是:
- 如果编辑入口来自当前详情任务
- 保存后就自动把详情抽屉重新打开到这条任务
这就是这一课新增的第一条联动。
2. 删除当前详情任务后,详情不能悬空
如果用户正在看第 5 条任务详情,然后直接在抽屉里点 删除任务,删除成功后会有两个风险:
- 抽屉里还残留刚被删掉的内容
- 地址栏和页面状态还指向已经不存在的任务
真实后台通常会这样处理:
- 优先切到当前上下文里的下一条任务
- 如果没有下一条,就退到上一条
- 如果前后都没有了,就关闭详情抽屉
这就是这一课新增的第二条联动。
这节课最关键的设计结论
1. 详情联动决策可以先抽成纯函数
这次没有把所有判断都直接堆在 TasksView.vue 里。
而是先抽出了两个纯函数:
getTaskDetailIdToRestoreAfterEditgetTaskDetailDeleteFallbackId
它们分别只做一件事:
- 判断这次编辑保存后要不要恢复详情
- 判断当前详情任务删除后应该跳到哪一条
这很重要。
因为页面层最容易越来越长、越来越难测。
把"纯判断"先抽出来以后:
- 单元测试更直接
- 页面层代码更清楚
- 后面改规则时也更安全
2. 页面层负责协调,不让展示层接管业务状态
这一课的联动最终仍然放在:
src/views/TasksView.vue
而不是塞回:
TaskDetailDrawer.vue
原因很简单:
- 抽屉组件只知道"用户点了编辑 / 删除"
- 真正的页面上下文在页面层
- 编辑弹窗状态也在页面层
- 当前详情任务状态也在页面层
所以真正适合做协调的是页面层。
这体现的是一个很重要的后台前端原则:
入口可以在组件里,但跨区域联动应该交给页面层统一调度。
3. 删除后的跳转,应该基于当前筛选 + 排序上下文
这次删除当前详情任务后,不是随便找一条任务顶上来。
而是基于:
- 当前筛选结果
- 当前排序结果
也就是当前详情导航真正使用的那份上下文。
所以规则才会是:
- 当前上下文中优先下一条
- 没有下一条就上一条
- 都没有就关闭
这说明一个关键点:
详情抽屉的联动,应该跟详情导航使用同一份上下文。
这次主要改了哪些文件?
这一课核心涉及这些文件:
src/utils/taskDetailLinkage.tssrc/utils/__tests__/taskDetailLinkage.spec.tssrc/views/TasksView.vuee2e/pages/TasksPage.tse2e/app.spec.tsdocs/README.md
另外新增了本节文档:
docs/40-task-detail-drawer-edit-delete-linkage.md
在 src/utils/taskDetailLinkage.ts 里学什么?
这是这一课最值得你反复看的一个小文件。
它体现的是:
先把联动规则写清楚,再把它接回页面。
getTaskDetailIdToRestoreAfterEdit
这个函数只回答一个问题:
当前这次编辑保存后,要不要把详情抽屉恢复回来?
规则非常明确:
- 如果当前详情任务 id 和编辑目标任务 id 相同
- 就返回这条任务 id
- 否则返回
null
也就是说:
- 从当前详情里进入编辑,保存后恢复
- 从列表别处进入编辑,不恢复当前详情
getTaskDetailDeleteFallbackId
这个函数只回答另一个问题:
当前详情任务删掉后,详情抽屉应该跳到哪一条?
规则是:
- 在当前导航上下文里先找到要删除的任务位置
- 优先返回下一条
- 没有下一条再返回上一条
- 如果前后都没有,就返回
null
这两个函数本质上都在表达:
页面联动先做成"输入明确、输出明确"的纯决策。
在 src/utils/__tests__/taskDetailLinkage.spec.ts 里学什么?
这一课的单元测试不是测页面,不是测 DOM,而是直接测规则。
主要覆盖了两类场景:
1. 编辑恢复详情
验证:
- 当前详情任务和编辑任务相同时,会返回对应 id
- 两者不同时,会返回
null - 当前根本没有详情时,也会返回
null
2. 删除后的回退目标
验证:
- 删除中间项时优先跳到下一条
- 删除最后一项时退到上一条
- 只剩一项时返回
null - 删除目标不在当前上下文里时也返回
null
你要重点学会的是:
复杂联动不一定一上来就写组件测试,很多时候先把规则拆成纯函数测试,成本更低、稳定性更高。
在 src/views/TasksView.vue 里学什么?
这一课页面层主要多了 4 组思路。
1. 记录"编辑后是否恢复详情"的临时状态
页面层新增了:
taskDetailIdToRestoreAfterEdit
它的作用不是长期状态,而是:
- 这一次编辑流程里的临时上下文
这说明:
不是所有状态都要进 composable 或 store。
有些只服务于当前一次交互流程的状态,放页面层 ref 就够了。
2. 打开编辑前先判断是否需要恢复详情
在 handleOpenEditTask 里:
- 先调用
getTaskDetailIdToRestoreAfterEdit - 再关闭当前详情抽屉
- 最后打开编辑弹窗
这一步做的是:
先记住要不要恢复,再切换 UI。
顺序不能反。
如果先关详情再判断,就会丢掉上下文依据。
3. 保存编辑后按条件恢复详情
在 handleUpdateTask 里:
- 先暂存本次是否需要恢复详情
- 再执行真正的更新
- 如果需要恢复,就去最新任务数组里重新找这条任务
- 找到后重新打开详情抽屉
- 最后清空临时状态
这里有个很重要的细节:
恢复详情时,不能偷懒复用旧对象引用,而要从最新任务数组里重新取。
这样才能保证抽屉拿到的是更新后的最新任务数据。
4. 删除当前详情任务后维护详情上下文
在 handleDeleteTask 里:
- 先判断删的是不是当前详情任务
- 如果是,就先根据
sortedTasks预计算删除后的目标任务 id - 删除成功后,再决定:
- 切到相邻任务
- 或关闭详情抽屉
这里最值得记住的是:
先算回退目标,再删数据。
因为删完以后,原来的位置信息可能就没有了。
为什么这里用 sortedTasks,而不是 paginatedTasks?
这是这一课非常值得你理解的一点。
这次删除后的相邻跳转,使用的是:
sortedTasks
也就是:
- 当前筛选结果
- 当前排序结果
- 但还没有被分页切片
为什么不用 paginatedTasks?
因为详情导航本来就不是"当前页上一条 / 下一条"。
前面几课已经确定过:
详情导航上下文,应该基于完整筛选结果,而不是当前分页切片。
所以这节课删除联动沿用同一套上下文,逻辑才一致。
E2E 这次补了什么?
这一课在 e2e/pages/TasksPage.ts 里新增了两个页面对象方法:
openEditTaskFromDetailDraweropenDeleteDialogFromTaskDetail
这样测试代码就能直接表达:
- 从详情里编辑
- 从详情里删除
而不是每次都重新写底层按钮定位。
在 e2e/app.spec.ts 里新增了两条测试:
1. user can edit a task from detail drawer and reopen same detail after save
这条测试验证:
- 打开第 4 条任务详情
- 从详情里进入编辑
- 修改标题并保存
- 详情抽屉自动回到同一条任务
- 地址栏里的
taskId仍然保持正确 - 列表里的任务标题也同步更新
2. user can delete current detail task and jump to adjacent detail within current filtered results
这条测试验证:
- 先筛到
待开始任务子集 - 打开中间那条第 5 条任务详情
- 从详情里直接删除它
- 第 5 条任务从列表消失
- 详情抽屉自动切到第 6 条任务
- 位置摘要从
第 2 / 3 条变成第 2 / 2 条 status和taskId查询参数都保持正确
这两条 E2E 很像真实后台测试。
因为它们测的不只是一个按钮,而是:
- 当前详情上下文
- 弹窗流程切换
- 地址栏同步
- 删除后的页面回退
你现在真正应该学会什么?
学完这一课,你不应该只记住"详情里多了编辑和删除入口"。
更重要的是理解下面 4 件事:
1. 页面联动要先把规则说清楚
先抽纯函数,再接 UI。
这是把复杂交互做稳的关键。
2. 跨区域协调适合放页面层
详情抽屉、编辑弹窗、任务列表、地址栏同步,这些都横跨多个区域。
真正适合统一协调的,是页面层。
3. 删除后的回退目标必须依赖当前上下文
不能"随便找下一条",而要用和详情导航一致的结果集。
4. 临时交互状态也需要认真设计
像"这次编辑保存后要不要恢复详情"这种状态虽然短暂,但如果不设计清楚,体验就会断。