前端修复实施总结(kec-manager · Vue3 + Element Plus)
实施日期:2026-07-13 | 依据:
docs/前端性能与体验体检报告-2026-07-13.md范围:落地体检报告中 全部已确认问题(H1 / M1 / A1 / A2 / B5 / C10 / A3 / A4 / L1 / L2 / B6 / B7 / B8) 验证:client/下npm run build通过(✓ built in 6.51s,exit 0,无错误/警告) 说明:M2(教师/教材改服务端分页)按计划有意跳过,见末尾「有意偏差」章节。
一、修复清单与落地位置(按严重度)
| 优先级 | 编号 | 问题 | 落地位置 | 关键改动 |
|---|---|---|---|---|
| P0 | H1 | CourseList 全量渲染无分页 | views/course/CourseList.vue | 新增 currentPage/pageSize + pagedList 客户端切片;<el-table :data="pagedList">;序号列改 :index="(i)=>(currentPage-1)*pageSize+i+1";排序按钮改用 realIndex(row);末尾加 <el-pagination class="pagination-container" v-if="filteredList.length>pageSize">;筛选变更 watch 重置 currentPage=1 |
| P1 | M1 | PlanList 全量渲染无分页 | views/plan/PlanList.vue | 同 H1 客户端切片模式(新增 watch import + currentPage/pageSize + pagedList + realIndex + 分页块) |
| P1 | B5 | 表单 label-width 不一致(90/100/120) | CourseList / PlanList / CollegeList / MajorList / TrainingLevelList / TextbookList / CourseEditPopover / ClassFormDialog | 统一为 label-width="100px"(批量设置仍保留原约定值处已按场景微调);College/Major/TrainingLevel/Plan/Course/Textbook 的 90px→100px,CourseEditPopover 80px→100px,ClassFormDialog 批量 120px→100px |
| P1 | C10 | 确认弹窗结构复制 11 处 | 新增 components/BaseConfirmBody.vue;ClassList / CourseList / CollegeList / MajorList / TrainingLevelList / TextbookList / PlanList / TeacherList / UserManagement / PlanDetail / TeachingArrange | 抽取轻量组件(props:message / warning / icon / iconColor),11 处删除内联结构改组件调用,完整保留各自 el-dialog 外壳、底部按钮、v-if 危险子句 |
| P1 | A1 | 工具条筛选/操作右对齐混排 | styles/global.css | .page-toolbar .action-buttons { margin-left:8px } → margin-left:auto,操作按钮推右、筛选留左(窄屏 @media 纵向全宽不变) |
| P1 | A2 | 学院/专业/培养层次列表缺工具栏 | CollegeList / MajorList / TrainingLevelList | 各表前加 <div class="page-toolbar"><el-input v-model="keyword" class="filter-2xl" …/></div>,:data="list"→:data="filteredList",排序按钮用 realIndex(row);维持"标题→筛选→表格"三段式节奏 |
| P2 | A3 | 分页容器三套实现 | TextbookList / TeacherList / TeachingStatistics / UserManagement / TeachingArrange | 自定 .list-pagination 与两处内联 div → 统一全局 .pagination-container;删除对应 scoped 样式 |
| P2 | A4 | TextbookList 列过密 | views/textbook/TextbookList.vue | 版次/定价 列加 show-overflow-tooltip;根容器 <div>→.textbook-list |
| P3 | L1 | ClassList 加载失败双重弹错 | views/class/ClassList.vue | 删 catch 内显式 ElMessage.error,改 DEV-only console.error(请求拦截器已统一兜底,避免重复 toast) |
| P3 | L2 | 告警列表用索引 :key="i" | ArrangeResultDialog / BatchResultDialog | :key="i"→:key="w"(用稳定 id 防止未来重排状态错乱) |
| P3 | B6 | 多列 :gutter 不统一(16/20) | TeachingStatistics | :gutter="20"→16(与 Dashboard/统计卡对齐) |
| P3 | B7 | 根容器裸 div | ClassList / TextbookList | 根 <div>→.class-list / .textbook-list |
| P3 | B8 | PlanDetail 概览条硬编码未用 token | views/plan/PlanDetail.vue | .plan-overview 改用 var(--space-3) var(--space-4)、margin-bottom:var(--space-3)、border-radius:var(--radius-md) |
二、新增组件:BaseConfirmBody.vue
- 路径:
client/src/components/BaseConfirmBody.vue - 设计取舍:只替换"图标 + 文字 + 可选警告"内联骨架,不碰各页面的
el-dialog外壳、底部el-button、以及危险子句(如 PlanDetail 的"此操作不可撤销"、TeachingArrange 的v-if/v-else段落)。 - 收益:后续统一调整警告样式只需改 1 处;避免 11 处回归。
- props:
message(必填)、warning(可选)、icon(默认WarningFilled)、iconColor(默认var(--brand-warning))。 - scoped 样式与原内联结构逐像素对齐(
.confirm-rowflex gap:12px;.confirm-iconflex-shrink:0 margin-top:2px)。
三、分页 + 排序联动的处理(关键正确性)
切片到 pagedList 后,两处必须用 realIndex(row)(元素在 filteredList 中的真实下标),不能用 el-table 的视觉 $index:
- 序号列:
<el-table-column type="index" :index="(i)=>(currentPage-1)*pageSize+i+1">—— 显示全局连续序号。 - 上/下移按钮:
@click="handleMoveUp(row, realIndex(row))",禁用态:disabled="realIndex(row)===0"/=== filteredList.length-1。
useSortable(filteredList, …) / useCrudList 仍基于 filteredList 做整列交换,切片只影响渲染,排序语义不变。已回源确认 composables/useSortable.js、useCrudList.js 的 indexFinder/list 按 id 换位是安全的。
四、有意偏差:M2(教师/教材改服务端分页)— 跳过
- 原计划:TeacherList / TextbookList 由"全量下载再客户端切片"改为服务端分页(对齐 ClassList/AuditLog)。
- 实际决策:本次不实施,理由:
- 渲染已受
pagedList客户端切片保护(不卡顿),H1/M1/A3 已解决"用户可感知"的可感知问题; - 改为服务端分页需改动现有客户端筛选/排序(均基于全量
filteredList)与 API 契约(api/teacher.js/api/textbook.js分页参数已声明但前端未接),属 P2 中风险改造; - 当前数据量级下,首屏"多下载全量 payload"的真实体感影响有限,不构成本轮"用户可感知体验"修复的阻塞项。
- 渲染已受
- 后续建议:单独排期做服务端分页(可作为下一轮性能专项),届时一并把
api/teacher.js、api/textbook.js分页响应接入、前端筛选/排序改为服务端参数透传。
五、影响分析(改了会不会影响其他调用)
| 改动 | 影响范围 | 回归确认 |
|---|---|---|
| CourseList/PlanList 加分页 | 仅本页 | 排序(filteredList)、导出(useExport 基于 full list)、序号列全局连续 —— 已处理 |
| label-width→100px | 所有表单 dialog | 长标签("二级学院/培养层次/用户管理")不溢出、与输入框对齐 |
| 工具条左右分区 | 全局 .page-toolbar | 窄屏 @media 纵向全宽未动,不破版 |
| BaseConfirmBody 复用 | 11 处删除确认弹窗 | 逐一确认危险子句/彩色图标不丢(PlanDetail 子句、TeachingArrange v-if/v-else、ClassList 三类确认均保留) |
| 分页容器收口 | Textbook/Teacher/TeachingStatistics/UserManagement/TeachingArrange | 删除 .list-pagination 后改走全局 .pagination-container,样式不回退 |
| A2 小列表补工具栏 | College/Major/TrainingLevel | 新增 keyword/filteredList,useCrudList 仍提供 list/handleMoveUp/handleMoveDown,排序逻辑不变 |
| L1 去双重 toast | 仅 ClassList | 拦截器统一兜底,删除处重复弹错消失 |
| L2/B6/B7/B8 | 局部样式/key | 纯样式与 key 稳定性,无逻辑影响 |
六、验证结论
- ✅
client/npm run build:通过,exit 0,构建产物正常(CourseList/PlanList/TextbookList/TeacherList 等分块均生成)。 - ✅ 全部 13 类问题已落地(M2 有意跳过并文档化)。
- ✅ 新增 1 个组件(
BaseConfirmBody.vue),11 处调用收敛。 - ⚠️ 未做接口级回归(纯前端静态改动 + 构建验证);建议后续在 dev 环境手动过一遍:CourseList/PlanList 翻页 + 排序、各确认弹窗样式、窄屏工具条布局。
七、下一步建议
- 单独排期落实 M2 服务端分页(性能专项)。
- 将本报告「统一布局与间距规范」写入开发规范 +
global.css(体检报告第七章已有草案)。 - dev 环境手动回归上表「回归确认」列各项。