第 38 课:任务列表里高亮当前正在查看详情的任务
第 38 课任务列表里高亮当前正在查看详情的任务这一课我们继续沿着“任务管理页主线”往下推进把上一课已经做好的“任务详情抽屉上下文导航”再向真实后台体验推进一步。这次的目标很明确当你打开某条任务详情时列表里对应任务要同步高亮这个高亮不只在表格里生效还要在卡片视图、看板视图里都成立当你在详情抽屉里切到上一条 / 下一条时高亮要跟着切换当你关闭详情抽屉时高亮要立刻消失补上单元测试、E2E 测试和课程文档这一课一句话在做什么这一课本质上是在做一件事让“当前正在查看哪条任务详情”这个页面上下文反向影响列表的视觉反馈。也就是说详情抽屉不再只是“右边弹出来一块内容”。它会开始和左边列表形成联动详情在看谁列表就高亮谁详情切到谁列表就跟着切到谁详情关闭列表就清掉高亮这是一种很典型的后台系统体验增强。为什么真实后台里经常需要这种联动因为用户在后台里很少是“只看详情不看列表”。更常见的是先在列表里筛出一批任务打开其中一条详情处理一会儿后切到下一条同时希望眼睛还能快速知道“当前处理的是列表里的哪一条”如果没有高亮用户很容易出现这几个问题看着详情不知道自己正在处理列表中的哪条记录在详情里切到下一条后列表没有视觉跟进列表、详情像两套互不认识的系统所以这节课练的不只是“加一个背景色”。真正练的是页面上下文如何共享同一份状态如何驱动多个视图为什么后台里的“联动反馈”比“单点组件功能”更重要这节课最关键的设计结论1. 高亮状态不应该分散在三个视图里各自乱算这次我们新增了一个共享工具文件src/components/tasks/taskDetailActiveHighlight.ts里面只做两件事isTaskDetailActive(taskId, activeTaskId)getTaskTableRowClassName(taskId, activeTaskId)这说明一个重要原则同一条业务规则如果会被多个视图同时使用就应该优先抽成共享函数。这样做的好处很直接表格、卡片、看板用的是同一套“是否高亮”规则后面修规则时不会三个地方各改一遍单元测试也可以直接先测纯函数再测组件渲染2. 页面状态继续留在页面层而不是塞进子组件内部这次三种列表视图都没有自己维护“我当前高亮谁”。它们都只是接收activeTaskId然后根据这个 id 去决定我该不该高亮这一行我该不该高亮这张卡片这说明子组件负责消费状态页面层负责拥有状态。这里的状态来源仍然是useTasksPage.tsTasksView.vue这和前面几课一直强调的分层原则是完全一致的。3. 三种视图的高亮表现可以不同但判定标准必须相同这一课里表格视图用行 class 高亮卡片视图用卡片 class 和data-detail-active看板视图也用卡片 class 和data-detail-active它们的视觉实现不同是合理的。因为三种视图的 DOM 结构本来就不同。但是它们判定“当前是不是详情任务”的标准必须一样。这也是为什么这次要抽共享判断函数。这次主要改了哪些文件这一课主要改了这些地方src/components/tasks/taskDetailActiveHighlight.tssrc/views/TasksView.vuesrc/components/tasks/TaskTable.vuesrc/components/tasks/TaskCardList.vuesrc/components/tasks/TaskKanbanBoard.vuesrc/components/tasks/__tests__/taskDetailActiveHighlight.spec.tssrc/components/tasks/__tests__/taskDetailActiveViews.spec.tse2e/pages/TasksPage.tse2e/app.spec.tsdocs/README.md另外新增了本节文档docs/38-task-list-highlight-active-detail-task.md在TasksView.vue里学什么文件src/views/TasksView.vue这次页面层做的事情不复杂但非常关键继续从useTasksPage()拿到activeTaskDetailId把它分别传给三个列表组件让三种视图都共享同一个“当前详情任务 id”也就是说这次页面层做的不是“写新逻辑”而是“把已有页面上下文继续分发给更多视图”。这很像真实项目里的迭代方式先把状态设计对再让更多组件接入这份状态这比每次新功能都重新发明一套状态结构健康得多。在TaskTable.vue里学什么文件src/components/tasks/TaskTable.vue这一课里表格最重要的变化是接收activeTaskId行 class 改成通过共享函数统一生成现在表格行 class 不再是只拼task-table-rowtask-table-row--task-id-xxx而是会在命中当前详情任务时额外补上task-table-row--active-detail这节课很适合你理解一个后台前端的常见技巧样式 class 不只是给 CSS 用的它也经常是测试稳定定位和状态表达的一部分。这里的task-table-row--task-id-xxx方便 E2E 精准定位。而task-table-row--active-detail则负责表达“当前详情高亮中”。在TaskCardList.vue和TaskKanbanBoard.vue里学什么文件src/components/tasks/TaskCardList.vuesrc/components/tasks/TaskKanbanBoard.vue这两个组件这次都做了同一类升级接收activeTaskId复用共享高亮判断函数把当前详情任务映射成 class 和data-detail-active其中data-detail-active很值得你记住。因为它代表一种很实用的工程习惯当视觉状态需要被测试稳定读取时可以用>单元测试这次测了什么文件src/components/tasks/__tests__/taskDetailActiveHighlight.spec.tssrc/components/tasks/__tests__/taskDetailActiveViews.spec.ts这次单元测试分成两层。1. 先测纯函数规则在taskDetailActiveHighlight.spec.ts里主要验证了taskId 和 activeTaskId 相等时会返回高亮activeTaskId 为空时不会误判表格行 class 会始终带稳定基础 class命中当前详情任务时会额外带上高亮 class这类测试的价值是规则变化时能最早发现问题不依赖 DOM失败时定位很直接2. 再测组件是否把规则变成真实 DOM 状态在taskDetailActiveViews.spec.ts里主要验证了卡片视图会给当前详情任务卡片加高亮 class卡片视图会同步输出data-detail-active看板视图会给当前详情任务卡片加高亮 class当activeTaskId变化或清空时DOM 状态会跟着更新这类测试练的是props 驱动视图DOM 状态如何反映业务状态为什么要把“规则测试”和“渲染测试”分开E2E 这次测了什么文件e2e/pages/TasksPage.tse2e/app.spec.ts这次我们给页面对象补了 6 个断言方法expectTaskRowActiveByIdexpectTaskRowInactiveByIdexpectTaskCardActiveByIdexpectTaskCardInactiveByIdexpectTaskKanbanCardActiveByIdexpectTaskKanbanCardInactiveById然后补了一条新的完整 E2Euser can highlight current detail task across task list views这条测试链路会验证打开第 4 条任务详情表格行高亮生效切到卡片视图后对应卡片继续高亮切到看板视图后对应看板卡片继续高亮在详情抽屉里切到下一条任务高亮从第 4 条迁移到第 5 条关闭详情抽屉高亮彻底消失切回表格视图后不残留旧高亮这条 E2E 很有代表性。因为它测的已经不只是“一个组件有没有渲染出来”而是页面上下文多视图联动详情切换关闭后的状态清理这就是越来越像真实后台系统测试的地方。你现在真正应该学会什么学完这一课你不应该只记住“加了高亮样式”。更重要的是理解下面 4 件事1. 详情状态本身就是一种页面上下文activeTaskDetailId不只是“右边抽屉显示谁”。它还可以驱动左边列表的反馈。这说明页面状态不是只能服务单个组件。它经常会跨组件、跨区域复用。2. 同一规则最好只有一个来源这次把“当前详情任务是否命中”抽成共享函数目的是避免表格自己算一套卡片自己算一套看板自己再算一套一旦规则分散后面就很容易出现“一个视图亮一个视图不亮”的问题。3. 测试最好分层这次的测试结构很典型纯函数测规则组件测渲染E2E 测真实交互链路这是非常值得你建立的工程习惯。4. 真正的后台体验来自联动而不是单点功能堆叠前几课我们做了列表筛选视图切换详情抽屉上一条 / 下一条这一课开始把这些能力真正连起来。所以这节课的价值不在“功能数量增加了多少”。而在“已有功能之间开始互相配合”。这一课完成后任务页更像真实后台了吗是的而且更像了很多。现在这个任务页已经不只是能筛选能分页能切换视图能看详情而是开始具备一种更真实的后台工作感你在详情里处理哪条任务列表就明确告诉你它是谁你切到下一条列表就跟着迁移视觉焦点你关闭详情列表就回到普通浏览状态这已经很接近真实后台里的“列表 - 详情联动体验”了。