跳转到内容

前端体检报告第七节建议①执行总结:教师/教材服务端分页(M2)

日期:2026-07-14 对应文档:docs/前端性能与体验体检报告-2026-07-13.md 第七节建议① 决策:用户选择「完整 M2」——给 /teachers/textbooks 后端都加服务端筛选+排序+分页,前端改为消费 {items,total}

一、核查中发现的关键事实(修正了体检报告的误判)

体检报告称「后端接口已就绪」,核查后不成立

端点体检报告假设实际代码结论
GET /teachers已支持分页+筛选+排序listTeachers 仅支持 page/page_sizewhere={} 为空(无服务端筛选/排序),返回 {items,total}分页形态已就绪,筛选/排序缺
GET /textbooks已支持分页listTextbooks findMany 全量返回数组,无分页/筛选/排序完全缺失,需破坏性改造
前端 getTextbooks()不接收 params实际 () => request.get('/textbooks'),无参需改签名

此外,getTextbooks()TextbookList.vue 外还有 2 个消费者UnifiedTextbookQuery.vuePlanDetail.vue),它们期望返回数组。因此 /textbooks 改返回结构属于破坏性契约变更,必须三处一起改。

二、改动清单

后端(破坏性 + 功能新增)

  • server/src/controllers/teacher.controller.jslistTeachers

    • 新增服务端筛选:name(模糊 contains)、personnel_typestatusaffiliated_college_id,以及通过关联表过滤的 course_id(courses.some)、college_id(scheduling_colleges.some)、training_level_id(scheduling_levels.some)。
    • 新增排序参数:白名单 ['name','personnel_type','status','sort_order','created_at','updated_at'],默认 sort_order asc,非法列回退默认(防注入)。
    • 维持 {items,total} 返回与 PII 脱敏逻辑不变。
  • server/src/controllers/textbook.controller.jslistTextbooks

    • 新增分页:page/page_size(仅显式传入时分页,否则返回全量,兼容查询页/方案页取全量)。
    • 新增服务端筛选:title(模糊)、category(精确)、publisher(精确)。
    • 新增排序参数:白名单(含 price/publish_date 等)。
    • 返回结构从 数组 改为 { items, total, publishers }publishers 由后端聚合(distinct,不随筛选变化),供前端筛选下拉使用。

前端

  • client/src/api/textbook.jsgetTextbooks(params) 支持传参。
  • client/src/views/teaching/TeacherList.vue:移除客户端 filteredList/pagedListload() 传全部筛选参数并消费 {items,total};筛选变化 watch + 防抖回到第 1 页重载;分页 @current-change/@size-change 触发服务端重载。
  • client/src/views/textbook/TextbookList.vue:同上;publishers 改为后端聚合 ref(原从 list 派生,分页后 list 仅一页会导致下拉不全);useSortable 改为作用于当前页 list(连续 sort_order 切片,页内拖拽排序全局正确,跨页拖拽不在本页能力范围);序号改为 (currentPage-1)*pageSize + 页内下标 + 1
  • client/src/views/query/UnifiedTextbookQuery.vueclient/src/views/plan/PlanDetail.vue:消费改为 res.data?.items(破坏性变更同步)。
  • client/src/styles/theme.css / global.css:统一间距 token 与工具类(第七节建议②,已于本次一并落地)。

测试

  • server/src/controllers/__tests__/textbook.controller.test.js:更新 listTextbooks 断言至 {items,total,publishers} 形态;新增分页(skip/take)、筛选(where 构建)、排序(白名单/回退)用例。补 count mock。
  • server/src/controllers/__tests__/teacher-list.test.js:新增「筛选/排序/分页」describe,覆盖 where 构建、skip/takeorderBy 白名单。

三、验证结果

验证项命令/方式结果
后端控制器语法node --check✅ teacher / textbook 均通过
后端单元测试vitest run textbook.controller.test.js teacher-list.test.js✅ 83 通过(73 + 10)
前端构建client/ npm run build✅ exit 0,无错误/警告

四、影响矩阵

改动影响范围风险
/textbooks 返回结构TextbookList / UnifiedTextbookQuery / PlanDetail 三处消费者,已全部同步中(已闭环,无遗漏)
/teachers 新增筛选参数仅 TeacherList 消费,返回结构不变
教材出版社下拉数据源由「前端从 list 派生」改为「后端聚合」低(更正确:分页后仍完整)
教材拖拽排序仅页内有效,跨页排序不可用低(与原全量体验略有差异,可接受)

五、dev 环境手动回归清单(建议用户点一遍)

  1. 教师页:输入姓名/选人员类别/选学科/意向学院/层次/归属学院/状态 → 列表实时按服务端筛选;翻页、改每页大小正常;序号连续;新增/编辑/删除/状态切换后停留当前页。
  2. 教材页:按书名/类别/出版社筛选(出版社下拉为全量);翻页+改每页大小;页内上移/下移排序生效且刷新后保持;批量设置出版社/作者/类别、批量删除、导入后列表正确。
  3. 查询页(UnifiedTextbookQuery)与方案详情(PlanDetail):教材下拉/列表仍正常(已改 .items)。
  4. 注意 /textbooksdistinct:['publisher'] 聚合在真实 SQLite 上的行为(单测已 mock,未在真实库跑过)——首次 dev 启动后点一次教材筛选即可确认。

六、结论

M2 完整落地:教师/教材列表均由「全量下载+客户端切片」升级为「服务端筛选+排序+分页」,并修复了 TeacherList 原先 getTeachers() 不传参、只拿到默认 20 条的隐性缺陷。破坏性契约变更(/textbooks 返回结构)已三处消费者同步、单测覆盖、构建验证通过。