Files
coursehomework-front/classwork-frontend-m5-plan.md
T
wanghu e6639f7925 feat(M5): P0 契约层 + 审批/应用设置下线
- types/homeworkV2.ts:V2 全量类型契约(五态/枚举/矩阵/版本/通知)
- teacherHomework/studentHomework/notification 三客户端(28 端点)
- §6 审批功能下线:删 ApprovalList/ApprovalSetting/ApprovalFlowEditor,
  types/api/menu/menuRoute/App/mock 同步清理,DROPPED_MOD_IDS={5,6}
- §7 应用设置下线:删 HomeworkSetting/TypeSetBox/UserCheckInput,
  SettingApi 与 dict/contacts 死代码清理,DROPPED_MOD_IDS 扩 {21}
- 新增 V2 组件:TeacherHomeworks(已发布+草稿箱)、ReviewModal(批阅弹窗)、
  AttachmentUpload(附件上传)——尚未挂路由,页面切换随后续阶段
2026-09-16 02:29:32 +08:00

320 lines
33 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 课堂作业前端 M5 切换计划 —— 对齐后端 V2 接口完成页面
| 项 | 内容 |
|---|---|
| 项目 | 前端 `E:\YHXT\view\CourseHomework`(Vue3 + Vite + TS + view-ui-plus + alova) |
| 依据 | 后端 `E:\YHXT\java\ClassHomeWork` 已实现接口(M1~M4 完成,V3.2 运行验证通过)+ `classwork-homework-plan-v3.md` §11 前端适配计划 / §15.5 遗留待办第 1 条 |
| 目标 | 前端 9 个核心页面全部切换到 V2 接口面,打通「发布→统计→批阅→学生提交→结果」全链路联调 |
| 后端地址 | 本地 `http://localhost:8102`(vite proxy 已配置,无需改动) |
| 文档版本 | V1.0(2026-09-15) |
---
## 1. 背景与现状差距
### 1.1 后端权威接口面(已实现,本计划的对齐目标)
后端按 PRD §18 完成了 V2 接口(28 个新端点),同时保留 V1 冻结接口(17 个)。统一响应 `{code,msg,data}`,分页 `BasePageResult`,时间格式全局 `yyyy-MM-dd HH:mm:ss`(无 "T" 分隔符)。
**教师端 `/api/teacher/homeworks`(19 端点,PRD 页 1~6):**
| 方法 | 路径 | 用途 |
|---|---|---|
| POST | `` | 创建作业(一律落草稿 DRAFT_UNPUBLISHED) |
| PUT | `/{id}` | 编辑作业(仅草稿两态) |
| DELETE | `/{id}` | 删除作业(仅草稿两态) |
| POST | `/{id}/publish` | 立即发布 |
| POST | `/{id}/schedule` | 定时发布 / 取消定时(`{scheduledAt}` 或 `{cancel:true}`) |
| GET | `/drafts` | 草稿箱分页(`subject`/`hwStatus`/`keyword` 筛选) |
| GET | `/published` | 已发布列表分页(含应交/已读/已交/未读未交聚合) |
| POST | `/{id}/remind` | 提醒未读/未交(`{type: NOT_READ\|NOT_SUBMITTED, studentIds?}`,返回 `{reminded}`) |
| POST | `/{id}/extend` | 延长截止(`{deadlineAt}` 须晚于当前值) |
| POST | `/{id}/copy` | 复制为新草稿,返回新 id |
| POST | `/{id}/archive` | 归档(只读,屏蔽提醒) |
| GET | `/{id}/export` | 导出学生矩阵 Excel(文件流) |
| GET | `/{id}` | 作业详情 |
| GET | `/{id}/stats` | 汇总统计卡片(总进度 + 分班进度) |
| GET | `/{id}/students` | 学生矩阵分页(`classId`/`submitStatus`/`reviewStatus`/`pendingGrade` 筛选) |
| POST | `/{id}/submissions/{studentId}/review` | 保存批阅(`{score?, comment?, markedImages?}`,score/comment 至少一项) |
| POST | `/{id}/submissions/{studentId}/return` | 打回(`{comment}` 必填) |
| GET | `/{id}/submissions/{studentId}/versions` | 历史提交版本列表 |
| GET | `/classes` | 年级→班级树(叶子 id=classId,含 studentCount) |
| POST | `/classes/sync` | 手动触发名册同步 |
**学生端 `/api/student/homeworks`(6 端点,PRD 学生页 1~3):**
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | `` | 我的作业列表(`tab=all/todo/done/closed` + 分页/keyword) |
| GET | `/{id}` | 作业详情(含 description/attachments/myAttachments/myText) |
| POST | `/{id}/read` | 阅读上报(前端停留 ≥3 秒后调用,幂等) |
| POST | `/{id}/submissions` | 提交作业(`{text?, attachments[]}`),返回 `{submissionId}` |
| PUT | `/{id}/submissions/{submissionId}` | 撤回/打回重交(`{action: withdraw\|resubmit, text?, attachments?}`) |
| GET | `/{id}/result` | 批阅结果(当前版本 + 历史版本含各版评语) |
**通知 `/api/notifications`(3 端点)**:GET 分页(含未读数)、`POST /{id}/read`、`POST /read-all`。
**辅助域(已存在,继续沿用)**:文件 `/api/file/Upload|UploadImage|Down|Preview|Delete|List|Bind`;字典 `/api/dict/List|ManageList|Add|Update|Delete|Sort|Sync`;联系组 `/api/contacts/*`;`/api/base/GetUserInfo|GetUserMod|GetSchoolAll|GetGroupUser|FileUpload|Logout`。
**V1 冻结接口 `/api/homework/*`(17 个)**:继续服务未切换页面(如「已收到」收件箱、「全部作业」管理端、「作业统计」),M5 完成后评估退役。
### 1.2 前端现状盘点与技术债
页面与数据源核对结果(`homeworkApi.*` 实际调用 vs 后端真伪):
| 页面 | 路由 | 现状数据源 | 判定 |
|---|---|---|---|
| NewHomework.vue | /list/new | 旧 Create/Edit/GetDetail | 真实接口,需切 V2 |
| HomeworkList.vue | /list、/homeworklist | 旧 SendList + **remind(伪)** | 部分伪接口 |
| HomeworkDetails.vue | /list/detail | 旧 Detail/GetNextPrev/ReadDetail | 真实接口,需切 V2 |
| Submissions.vue | /submissions | **Submissions/SubmissionDetail/grade(伪)** | 全部伪接口 |
| Grade.vue | /grade | **sendList + Submissions/SubmissionDetail/grade(伪)** | 部分伪接口 |
| StudentHomeworks.vue | /stu/today、/stu/redo、/par/kids | **MyHomeworks/parentNudge(伪)** | 全部伪接口 |
| HomeworkAccept.vue | /accept | 旧 List/Detail(v1 收件箱) | 真实接口,需改造为学生详情页 |
| HomeworkStats.vue | /stats | 旧 Statis | 真实接口,保留 |
| HomeworkSetting.vue | /setting | dict/contacts | 真实接口,保留 |
| ApprovalList.vue | /approval | **approvalList/approve/reject(伪)** | 后端无审批域 |
| ApprovalSetting.vue | /approval-setting | **approvers 系(伪)** | 后端无审批域 |
**「伪接口」清单**(后端不存在,真实环境必 404,仅 mock 模式可跑):`/api/homework/` 下的 `Submissions、SubmissionDetail、Grade、Remind、MyHomeworks、MySubmission、Submit、ParentNudge、ApprovalList、ApprovalCount、Approve、Reject`;`/api/dict/` 下的 `ApproverPool、Approvers、AddApprover、UpdateApprover、DelApprover`;`/api/base/SetRole`。
**类型契约差距**:`types/homework.ts` 为 V1 契约(hwStatus 四态 `draft/collecting/closed/done`、submitStatus 小写五值);V2 要求 hwStatus 五态 `DRAFT_UNPUBLISHED/DRAFT_SCHEDULED/PUBLISHED/CLOSED/ARCHIVED`、submitStatus 五态 `NOT_SUBMITTED/SUBMITTED/LATE_SUBMITTED/RETURNED/REVIEWED`、reviewStatus 三态 `PENDING/REVIEWED/RETURNED`。
**工作区未提交改动**:Grade/StudentHomeworks/Submissions 三页的图片预览/上传优化 + mock/env 配置微调(与本次切换方向一致,保留并在其上继续)。
## 2. 总体方案
三层推进:**契约层(类型+API 客户端)→ 教师端页面 → 学生端页面**,最后清理菜单路由与 mock。旧 V1 页面(已收到/全部作业/统计/设置)不动,继续走冻结接口。
### 2.1 契约层(P0)
**新增 `src/types/homeworkV2.ts`**(V2 契约独立成文件,不污染 V1 类型;与后端 `model/v2/*.java` 一一对齐):
```ts
/** 作业五态(PRD §17) */
export type HwStatusV2 = "DRAFT_UNPUBLISHED" | "DRAFT_SCHEDULED" | "PUBLISHED" | "CLOSED" | "ARCHIVED";
/** 提交五态 */
export type SubmitStatusV2 = "NOT_SUBMITTED" | "SUBMITTED" | "LATE_SUBMITTED" | "RETURNED" | "REVIEWED";
/** 批阅三态 */
export type ReviewStatus = "PENDING" | "REVIEWED" | "RETURNED";
export interface AttachmentItem { fileId?: number; fileUrl: string; fileType?: "image" | "file"; fileName?: string; sort?: number; }
export interface ClassItem { classId: string; className: string; }
export interface ProgressVo { should: number; read: number; submitted: number; graded: number; pendingGrade: number; unsubmitted: number; unread: number; unreadUndone: number; readRate: number; submitRate: number; gradeRate: number; }
export interface HomeworkV2Vo { id; title; subject; description; hwStatus: HwStatusV2; hwStatusDesc; scheduledAt; publishedAt; deadlineAt; archivedAt; submitType: string[]; allowLate; remind1d; remind1h; classes: ClassItem[]; attachments: AttachmentItem[]; publisherId; publisherName; createTime; updateTime; progress?: ProgressVo | null; }
export interface HomeworkV2Request { title: string; subject?; description?; classIds: string[]; attachments?: AttachmentItem[]; deadlineAt?; submitType: string[]; allowLate?; remind1d?; remind1h?; }
export interface TeacherHomeworkQuery { pageIndex?; pageSize?; keyword?; subject?; hwStatus?; }
export interface StudentMatrixVo / StudentMatrixQuery / HomeworkStatsVo / ClassTreeNodeV2(id: string)/ SubmissionVersionVo / StudentHomeworkV2Vo(含 MySubmission)/ StudentHomeworkDetailV2Vo / StudentSubmitRequest / StudentResultVo / NotificationVo …
```
**新增 3 个 API 客户端**(风格沿 `api/module/homework.ts` 的 `get/postJSON` 封装):
- `src/api/module/teacherHomework.ts` → `teacherApi`:19 端点全覆盖 + `exportUrl(id)`(Excel 直链,浏览器跳转下载)
- `src/api/module/studentHomework.ts` → `studentApi`:6 端点
- `src/api/module/notification.ts` → `notificationApi`:3 端点
**修正 `api/module/homework.ts`**:`baseApi.setRole` 标注仅 mock 可用(后端无此端点,真实环境学生身份由 CAS 登录账号 ↔ class_student 映射,联调用种子账号 stu01~stu06);`baseApi.getClassTree` 的调用方改走 `teacherApi.classTree()`。
### 2.2 页面改造映射(P1~P3)
| PRD 页面 | 前端页面 | 目标接口 | 关键改造点 |
|---|---|---|---|
| 页 1 发布作业 | NewHomework.vue | POST/PUT + /classes + publish/schedule + file/Upload | ① 表单重构:classIds(年级→班级树多选,数据源换 `/teacher/homeworks/classes`)② 新增:截止时间(精确到分钟,空=仅查阅型)、submitType(图片/文件/两者/文本)、allowLate、remind1d/remind1h、定时发布时间 ③ 保存语义拆分:「存草稿」(POST/PUT)与「立即发布」(POST 后调 publish)/「定时发布」(POST 后调 schedule)④ 附件走 file/Upload,组装 AttachmentItem[] ⑤ 编辑回显改 GET /teacher/homeworks/{id}(旧 GetDetail 保留给转发场景) |
| 页 2 草稿箱 | HomeworkList.vue(/list 增加「草稿箱」段) | GET /drafts | 草稿两态(未发布/定时待发)标签展示;操作:编辑、发布、取消定时(schedule cancel)、删除 |
| 页 3 已发布列表 | HomeworkList.vue(/list 主段) | GET /published + remind/extend/copy/archive/export | ① 统计列:应交/已读/已交/**未读未交高亮** + 阅读率/提交率(progress)② 状态筛选 PUBLISHED/CLOSED/ARCHIVED ③ 操作组:提醒(NOT_READ/NOT_SUBMITTED,返回 reminded 提示)、延长截止(日期选择,须晚于当前)、复制为新草稿、归档、导出 Excel(直链)④ 行点击进详情 |
| 页 4 详情/统计 | HomeworkDetails.vue | GET /{id} + GET /{id}/stats | 作业信息卡(五态标签、学科、截止、提交方式、班级、附件下载)+ 统计卡片(总进度 + 分班进度:阅读率/提交率/批阅率、待批阅数) |
| 页 5 学生矩阵 | Submissions.vue | GET /{id}/students | ① 列:学生/班级/阅读状态(含首末阅读时间)/提交状态(含逾期角标)/提交时间/评分/批阅状态/附件缩略图 ② 筛选:classId、submitStatus、reviewStatus、只看待批阅(pendingGrade)③ 未交学生占位行(submissionId=0)④ 行操作:批阅、打回、提醒单个学生(remind 定向) |
| 页 6 批阅/打回/版本 | Grade.vue(批改中心) | /published(pendingGrade 作业列表) + versions + review/return | ① 待批作业列表改 GET /published(按 progress.pendingGrade 排序/展示)② 选中学生(矩阵 pendingGrade=true)→ 右侧工作台 ③ 版本切换(GET versions,默认最新)④ 批阅保存(score+comment 至少一项)⑤ 打回(comment 必填,二次确认)⑥ 提交内容展示:text + 附件/图片(复用已有缩略图放大预览) |
| 学生页 1 列表 | StudentHomeworks.vue | GET /student/homeworks?tab= | ① tab 改 all/todo/done/closed(对齐 meta.initTab=redo→todo 语义调整)② 倒计时(deadlineAt)③ my 提交状态徽标(五态 + 逾期角标)④ 分页 + keyword |
| 学生页 2 详情/结果 | HomeworkAccept.vue(改造为学生详情页,挂 /stu/detail?serid=) | GET /{id} + read + result | ① 作业说明富文本 + 附件预览/下载 ② **进入页 3 秒定时器调 read(onUnmounted 清理)** ③ 我的提交区(当前版本 text/附件/评分/评语)④ 历史版本 + 各版评语(GET result)⑤ 提交/撤回/重交入口 |
| 学生页 3 提交 | HomeworkAccept.vue 内提交区(+列表页快捷弹窗保留) | POST submissions + PUT withdraw/resubmit | ① 按 submitType 动态渲染:文本框 / 图片上传(file/UploadImage 产物→AttachmentItem)/文件上传 ② 截止校验:截止后 allowLate=0 禁交 ③ 撤回(截止前)→ 重新填写重交 ④ 打回(RETURNED)→ 显示订正原因 + 重交 |
**不动页面**:HomeworkStats(旧 Statis)、HomeworkSetting(dict/contacts)、/accept 的 v1 收件箱语义由 HomeworkAccept 改造承接(路由 `/accept` 重定向到 `/stu/today`)。
### 2.3 路由与菜单调整(P3)
| 项 | 处置 | 理由 |
|---|---|---|
| 新增 `/stu/detail`(HomeworkAccept 改造后) | meta `{ role: "student", title: "作业详情", modId: 33 }` | PRD 学生页 2/3 载体 |
| `/accept` | 重定向 `/stu/today` | v1 收件箱语义被学生列表替代 |
| `/approval`、`/approval-setting` + 菜单 modId 5/6 | **路由与菜单摘除**(页面文件保留不引) | 后端无审批域(plan-v3 §4.6 顺延),伪接口联调必挂 |
| `/par/kids` 家长端 | 保留路由但隐藏入口 | 后端无家长接口(plan-v3 §4.6 顺延),parentNudge 为伪接口;页面内调用加守卫 |
| `/homeworklist` 全部作业、`/stats` 统计、`/setting` 设置 | 保留走 V1 冻结接口 | 冻结接口可用,M5 后评估退役 |
| 通知中心(可选) | 视排期加 `/notify` 轻量页(notificationApi 3 端点,未读角标) | PRD §18 未列页面,plan-v3 §7.3 补充接口;列为可选项 |
### 2.4 mock 同步(P4)
`src/assets/mock/homeworkapi.ts` 增补 V2 端点 mock(teacher 19 + student 6 + notifications 3),数据模型对齐 `types/homeworkV2.ts`,保证 `npm run dev:mock` 全页面可演示;同时清理伪接口 mock(Submissions/Grade/MyHomeworks/审批系),避免继续误导联调。
## 3. 实施顺序与验收
| 阶段 | 内容 | 验收标准 |
|---|---|---|
| P0 契约层 | types/homeworkV2.ts + teacherHomework/studentHomework/notification 三客户端 + homework.ts 修正 | `vue-tsc` 通过;接口路径/参数与本文 §1.1 表逐项一致 |
| P1 教师端发布链路 | NewHomework + HomeworkList(草稿箱+已发布) | 真实后端(8102):建作业→存草稿→草稿箱可见→立即发布/定时发布→已发布列表出统计聚合 |
| P2 教师端批阅链路 | HomeworkDetails(stats)+ Submissions(矩阵)+ Grade(批阅/打回/版本) | 矩阵筛选/缩略图/占位行正确;批阅→学生端可见结果;打回→重交→版本递增 |
| P3 学生端闭环 | StudentHomeworks + HomeworkAccept + 路由菜单调整 | 用种子账号(stu01~stu06):列表 tab→详情 3 秒已读→提交→倒计时/逾期分流→撤回留版本→打回重交→查结果 |
| P4 清理与全量走查 | mock 更新、审批/家长端摘除、全页面回归 | dev(dev) + dev:mock 双模式走查;PRD §18 九页面映射逐格核对 |
每阶段一组语义化 commit;P1 起需要后端 8102 运行 + `sql/v3_seed.sql` 名册种子(库名 `class_homework`,utf8mb4)。
## 4. 风险与开放问题
| # | 事项 | 影响 | 处置建议 |
|---|---|---|---|
| 1 | 学生身份联调:后端无 SetRole,学生接口按 CAS 登录账号 ↔ class_student 映射 | 高(P3 阻塞) | 与后端确认本地登录方案(种子账号 stu01~stu06 走 CAS 或本地放行);mock 模式保留 SetRole 演示用 |
| 2 | 审批域下线需产品确认 | 中 | 本计划先摘菜单/路由不删代码;产品若要保留需后端补接口 |
| 3 | 提交附件契约:v2 只收 file/Upload 产物(fileId/fileUrl),拒绝 dataURL | 中 | HomeworkUpload 组件改造为真实上传后再组装 AttachmentItem;改造前 dataURL 直交必被后端拒 |
| 4 | 学生端入口与菜单结构(/stu/* 不在现有静态菜单) | 低 | 沿用现有约定,学生视角由平台角色决定入口;具体待 GetUserMod 对接方案 |
| 5 | Excel 导出/export 直链需带登录态 cookie | 低 | 同源代理下 `location.href` 直跳即可(与 AllFileDown 同模式) |
| 6 | V1 页面(已收到/全部作业/统计)与新数据并存的两套状态口径 | 低 | 双轨期 hwStatus 派生映射(后端已做),前端不做转换;M5 后评估 V1 退役 |
| 7 | tab=redo(待订正)语义:V2 无独立 redo 桶,RETURNED 归入 todo | 低 | /stu/redo 路由 initTab 改 todo 并在页内强化 RETURNED 过滤,或改路由为 /stu/todo |
## 5. 交付物清单
1. `src/types/homeworkV2.ts` —— V2 全量类型契约
2. `src/api/module/teacherHomework.ts` / `studentHomework.ts` / `notification.ts` —— 28 端点客户端
3. 改造后 7 个页面:NewHomework / HomeworkList / HomeworkDetails / Submissions / Grade / StudentHomeworks / HomeworkAccept
4. `src/router/routes.ts` + `src/assets/config/menu.ts` 调整
5. `src/assets/mock/homeworkapi.ts` V2 增补与伪接口清理
---
## 6. 审核(审批)功能下线(2026-09-15)
### 6.1 裁决变化
§2.3 与 §4 风险 2 原口径为「摘菜单/路由、页面文件保留不引,等产品确认」。本次确认**彻底删除审批(审核)功能**:产品定位为任课教师直接布置,不存在送审环节;后端 `ClassHomeWork` 从未实现审批域(无 `approval_*` 列、无 `ApprovalList/Approve/Reject`、无审批人池接口),而前端两页调用的 `homeworkApi.approvalList/approve/reject` 与整个 `approvalSettingApi` **在 API 层根本不存在**,属编译期即悬空的死代码。
注意边界:本节只删「审批/审核」(approval),**不动「批改/批阅」(grade/review)**——`Grade.vue`、`Submissions.vue`、`ReviewModal.vue` 是作业核心链路。
### 6.2 牵连面盘点(grep `approval|审核|审批|approver|canApprove|vote|flow` 全仓)
| # | 文件 | 现状 | 处置 |
|---|---|---|---|
| 1 | `src/views/homework/ApprovalList.vue` | 审批列表页,未挂路由,调用不存在的 `homeworkApi.approvalList/approve/reject` | 删除(git 跟踪,可 `git checkout` 恢复) |
| 2 | `src/views/homework/ApprovalSetting.vue` | 审批人池设置页,未挂路由,调用不存在的 `approvalSettingApi` | 删除(同上) |
| 3 | `src/components/ApprovalFlowEditor.vue` | 工作区已处于删除态(`git status` 显示 ` D`) | 保持删除,不恢复 |
| 4 | `src/types/homework.ts` | `HomeworkVo` 81-95、`ApprovalStatus` 103、`HomeworkDetailVo` 146-174(含 `approvalFlow`/`canApprove`)、`HomeworkRequest` 393-398(`approvers`/`vote`/`flow`)、`ApprovalQuery` 489、`ApprovalAction` 500、`ApproverVo` 509、分节标题 476 | 全部删除;分节标题改「班级」 |
| 5 | `src/api/module/homework.ts` | 23-24 `import { ApprovalQuery, ApproverVo }`(导入后零使用) | 删两行导入 |
| 6 | `src/assets/config/menu.ts` | 12-13 静态菜单 `id/modId 5 审批列表 /approval`、`6 审核设置 /approval-setting`(指向不存在的路由) | 删两行 |
| 7 | `src/utils/menuRoute.ts` | 无审批别名;但 `resolveMenuPage` 对无法识别的模块只 `console.warn` 并返回 `path:""`,而 `layout.vue:192` 直接渲染 `modList`(不过滤空 path)→ 平台 `GetUserMod` 若仍下发 modId 5/6,侧栏会出现点了没反应的死菜单 | 新增 `DROPPED_MOD_IDS={5,6}` + `dropDroppedModules(list)`(顶层 + children,按 `modId ?? id`,不改入参),`App.vue` 赋值 `layoutData.modList` 前调用 |
| 8 | `src/assets/mock/homeworkapi.ts` | 审批引擎与数据:头注释 7/15-16、审批人池 41-52、`buildFlowFromInput` 62-72、`pendingApprovers` 75-79、`canAct` 81-84、`flowProgressText` 86-92、种子 `approval*` 字段 155-313(7 处)、`seedFlows` 318-343、复位循环 345-353、`isPublished` 639-641、`APPROVAL_DESC` 648、列表映射 682-689、详情映射 728-753、Create 1026-1032、Edit 1059-1092 | 全删;`isPublished` 简化为「非草稿即可见」;种子行不再带 `approval*` 键 |
| 9 | 其余 `src/views`、`src/components`、`src/router`、`App.vue` | grep **零命中** | 无需改动 |
后端 `E:\YHXT\java\ClassHomeWork`:审批域从未落地,无可删代码;仅需把 `classwork-homework-plan-v4.md` 的 §0 排除说明与 Q-08 由「待确认」改为「已裁决下线」。
### 6.3 执行步骤与验收
1. 备份:`types/homework.ts`、`api/module/homework.ts`、`assets/mock/homeworkapi.ts`、`assets/config/menu.ts`、`utils/menuRoute.ts`、`App.vue` 快照到 `.backup/20260915-drop-approval/`(这些文件工作区相对 HEAD 已有 M5 未提交改动,`git restore` 会连带丢改动,故单独快照)。
2. 删 2 个页面文件(`git rm` 语义,历史可查)。
3. 按 6.2 顺序改 types → api → menu → menuRoute+App → mock(自底向上,先断引用再清数据)。
4. 验收(实测见 §6.4):① `grep -rniE "approval|approver|canApprove" src` 命中数为 0;② `npm run build` 成功;③ `dev:mock` 下逐端点冒烟正常、审批端点 404、侧栏无审批入口。
> 原定的 `npx vue-tsc --noEmit` 门禁在本工程**跑不起来**:`vue-tsc@1.8.27` 与 `typescript@5.9.3` 不兼容会直接崩溃,且工程 tsc 基线本就有 65 条存量报错。故类型侧只作「不得新增引用已删符号的报错」的对照检查,见 §6.4 第 4 条。
5. 结果回填 §6.4。
### 6.4 执行结果回填(2026-09-15 完成)
**改动清单**
| 文件 | 动作 |
|---|---|
| `src/views/homework/ApprovalList.vue`、`ApprovalSetting.vue` | `git rm`(索引内 `D`,历史可查) |
| `src/components/ApprovalFlowEditor.vue` | 保持工作区已删除状态,未恢复 |
| `src/types/homework.ts` | 删 `HomeworkVo` 8 个 approval 字段、`ApprovalStatus`、`HomeworkDetailVo` 9 个 approval 字段(含 `approvalFlow`/`canApprove`)、`HomeworkRequest` 的 `approvers/vote/flow`、`ApprovalQuery`/`ApprovalAction`/`ApproverVo`;分节标题「班级 / 审批」→「班级」 |
| `src/api/module/homework.ts` | 删 `ApprovalQuery`、`ApproverVo` 两个死导入 |
| `src/assets/config/menu.ts` | 删 modId 5「审批列表」/6「审核设置」两项,头注释记录「平台若仍下发则由 DROPPED_MOD_IDS 剔除」 |
| `src/utils/menuRoute.ts` | 新增 `DROPPED_MOD_IDS={5,6}`、`DROPPED_NAMES`(审批/审核 7 个叫法)、`dropDroppedModules()`(顶层+children,按 `modId ?? id` 或名称命中,返回新数组不改入参) |
| `src/App.vue` | `GetUserMod` 结果入壳层前经 `dropDroppedModules` 过滤(侧栏渲染、默认跳转、console.table 共用同一份) |
| `src/assets/mock/homeworkapi.ts` | 删审批引擎(审批人池 `approvers/approverSeq/approverName/approverPoolAccounts`、`FlowLevel`、`buildFlowFromInput`、`pendingApprovers`、`canAct`、`flowProgressText`、`APPROVAL_DESC`)、`seedFlows()` 与其后的复位 forEach、种子 8 处 approval 属性、`toVo`/详情两处 8 行映射与 `approvalFlow` 块、Create 新对象的 7 行 approval 初始化、Edit 的 flow 重建整段(35 行);`isPublished()` 由「审批通过或免审」改为 `hwStatus !== "draft"`;头注释与存储注释同步改写 |
**验收实测**
1. **残留归零**:`grep -rniE "approval|approver|canapprove|flowprogress|buildflow" src` → **0 命中**(中文「审批/审核」仅存于注释与 `DROPPED_NAMES` 名单,属有意保留)。
2. **构建通过**:`npm run build`(`vite build --mode base`)**EXIT=0**,18.82s。
3. **mock 冒烟**(`npm run dev:mock` @8077,node fetch 逐端点,全部 `code==0`):
- `List` 5/7 条、`SendList?scope=user` 3/3 条、`Detail?serid=1&type=0` 出参键仍为 `serid/Contents/DeptID/...ChaoSend/tlist` 且**不再含任何 approval 键**、`ReadDetail` 按班级分组(`account=1301`/`teaname=初一(1)班(45人)`)、`MyHomeworks` buckets `{todo:3,submitted:1,graded:2,redo:0,all:7}`、`Statis` 11 字段齐全、`GetClassTree` 年级→班级带 `studentCount`。
- 审批端点确认消失:`/api/homework/ApprovalList`、`/api/dict/ApproverPool` 均 **HTTP 404**。
- 临时服务已按 PID 校验(命令行确为本工程 `vite.js --mode mock`)后 taskkill,8077 已释放。
4. **类型检查工具链本身不可用(既有问题,非本次引入)**:`vue-tsc@1.8.27` 在 `typescript@5.9.3` 下直接崩溃(`Search string not found: "/supportedTSExtensions = .*(?=;)/"`,vue-tsc 1.x 的 monkey-patch 与 TS 5.5+ 不兼容)。退化用 `npx tsc --noEmit` 得 **65 个报错,全部为存量**:alova 泛型 `TS2344 Type 'T' does not satisfy the constraint 'AlovaGenerics'`(连未改动的 `notification.ts`/`teacherHomework.ts`/`studentHomework.ts` 同样报)、mock 种子数组属性推断 `TS2339`、`menu.ts` 的 `IModule` 未从 `@/store` 导出、`teacherNames` 未使用等;**其中无一条引用被删除的审批符号**(已 grep 验证)。→ 建议后续单列一项:升级 `vue-tsc` 到 2.x 或锁 `typescript<=5.4`,否则类型门禁形同虚设。
**行为变化(预期内)**:原先 `pending`/`rejected` 的演示种子不再被门控,收件箱从 6 条变 7 条、`Statis.publishedCount` 7/8;这符合「无送审环节,发布即可见」的产品口径。
**回退方式**:`git checkout HEAD -- src/views/homework/ApprovalList.vue src/views/homework/ApprovalSetting.vue` 可复原页面;types/api/menu/menuRoute/App/mock 六个文件的改动前快照在 `.backup/20260915-drop-approval/`(因工作区相对 HEAD 另有未提交的 M5 改动,直接 `git restore` 会连带丢 M5,故用快照)。
---
## 7. 应用设置功能下线(2026-09-15)
### 7.1 裁决
「应用设置」(路由 `/setting`,modId 21)承载两块能力:**发文部门字典**(`dict/ManageList·Add·Update·Delete·Sort`)与**自定义联系组**(`contacts/GetGroup·AddGroup·EditGroup·DelGroup·GetContactsTea·SetContactsTea·GetGroupTree`),任课教师布置作业不依赖它们。裁决:
- **只删前端**:后端 `DictController`/`ContactsController` 及其 `sys_dict`/`contacts_group`/`contacts_group_user` 表**保留不动**(`HomeworkService.resolveDeptName` 仍要查 `sys_dict`;端点无人调用不影响运行)。
- **仅该页使用的组件与死 API 一并清掉**(与 §6 同一口径)。
### 7.2 牵连面盘点
| 文件 | 现状 | 处置 |
|---|---|---|
| `src/views/homework/HomeworkSetting.vue` | 应用设置页(部门拖拽排序 + 联系组及组员维护) | 删除(git 跟踪) |
| `src/components/TypeSetBox.vue` | 仅被 HomeworkSetting 引用(全仓 grep 零其他消费者) | 删除 |
| `src/components/UserCheckInput.vue` | 零消费者(其 `loader` 用 `baseApi.getSchoolAll` + `settingApi.getGroupTree`) | 删除 |
| `src/router/routes.ts` | `/setting` 路由(86-91)+ 旧路径重定向 `/homework/setting`(121)+ 头注释 `21=应用设置` | 删两处路由与注释项 |
| `src/assets/config/menu.ts` | `{ id: 21, modId: 21, name: "应用设置", page: "/setting" }` | 删除 |
| `src/api/module/homework.ts` | `class SettingApi`(12 个方法)+ `export const settingApi`;`BaseApi.getSchoolAll`;`HomeworkApi.getSysDic`(dict/List)、`getSchModuleIsSms`(dict/Sync) 自 §6 后零调用 | 全部删除 |
| `src/types/homework.ts` | `DeptManageItem`、`DictOption`、`ContactGroupPage`、`ContactUserPage`、`ContactGroupItem`、`ContactUser` | 删除 |
| `src/assets/mock/homeworkapi.ts` | `/api/base/GetSchoolAll`(682)、`/api/dict/*`(692-741)、`/api/contacts/*`(1173-1250) 三组桩;种子 `groups/groupSeq`(412-418)、`idNames`(420)、`teacherNames`(425,本就是死码)、`schoolTree`(431)、`deptSeq`(31) | 全部删除;头注释端点清单同步改写 |
### 7.3 明确保留项(勿顺手删)
| 保留 | 理由 |
|---|---|
| mock 的 `deptDict`(21-30) | Create(830)/Edit(867) 仍用 `deptDict.find(d => d.CodeID === b.deptId)` 解析部门名,是作业域数据来源,不是设置页专属 |
| `src/components/UserCheckModal.vue`、`ClassCheckInput.vue` | 发作业选班级仍在用 |
| `src/components/MyConfirm` | HomeworkList / TeacherHomeworks 仍在用 |
| 类型 `SchoolTreeNode` | `BaseApi.getUserInfo` 的返回类型仍引用它 |
| `vuedraggable` 依赖 | 删除后成未用依赖,但改 `package.json`/lock 属额外风险,本次不动(后续清依赖时一并处理) |
| 后端 `dict/*`、`contacts/*` 端点与三张表 | 按 7.1 裁决保留 |
### 7.4 对后端 V4 方案的连带影响(需同步改 `classwork-homework-plan-v4.md`)
1. **E-01 修法调整**:原计划「`deptId` 为空时回填字典默认发文部门」失去维护入口,改为**从当前教师主组/`BoaUser.getCurrentRole()` 推导** `deptId/deptName`,`sys_dict` 仅作为已有数据的名称解析(`resolveDeptName` 保持)。
2. **E-05 面收窄但过滤器仍必需**:`postForm` 的 14 处调用点里,dict 4 处 + contacts 6 处随本功能下线归零,剩余 `Remind`(N-04 新门面)、`ReceiveUsers`、`UserPull`、`SetTopmost`、`Expedite`、`InitializeRed`(无参) —— `SingleValueParameterFilter` 照旧要做,只是回归面变小。
### 7.5 执行步骤与验收
1. 快照受影响文件到 `.backup/20260915-drop-app-setting/`。
2. `git rm` 三个文件 → 删路由/菜单 → 删 SettingApi 与死方法 → 删 types → 清 mock(端点 + 专属种子)。
3. 验收:① `grep -rniE "settingApi|DeptManageItem|ContactGroup|ContactUser|DictOption|getSchoolAll|getGroupTree|TypeSetBox|UserCheckInput|homework_setting" src` 命中 0;② `npm run build` EXIT=0;③ `dev:mock` 冒烟:`List/SendList/Detail/ReadDetail/MyHomeworks/Statis/GetClassTree` 仍全 `code=0`,`/api/dict/*`、`/api/contacts/*`、`/api/base/GetSchoolAll` 均 404,`Create` 仍能解析 `deptName`(回归 §7.3 第一条);④ 侧栏无「应用设置」。
4. 结果回填 §7.6。
### 7.6 执行结果回填(2026-09-15 完成)
**改动清单**
| 文件 | 动作 |
|---|---|
| `src/views/homework/HomeworkSetting.vue`、`src/components/TypeSetBox.vue`、`src/components/UserCheckInput.vue` | `git rm` |
| `src/router/routes.ts` | 删 `/setting` 路由 + `/homework/setting` 重定向(目标已不存在,留着会把用户导向未知路由);头注释 modId 对照去掉 `21=应用设置` |
| `src/assets/config/menu.ts` | 删 `{ id: 21, modId: 21, 应用设置 }`,头注释改为「不做审批、不开放应用设置」 |
| `src/utils/menuRoute.ts` | `DROPPED_MOD_IDS` 扩为 `{5, 6, 21}`,`DROPPED_NAMES` 增「应用设置 / 作业设置」——平台 `GetUserMod` 若仍下发 modId 21,侧栏不会出现死菜单 |
| `src/api/module/homework.ts` | 删 `class SettingApi`(12 方法)与 `settingApi` 导出、`BaseApi.getSchoolAll`、`HomeworkApi.getSysDic`/`getSchModuleIsSms`、`DICT`/`CONTACTS` 前缀常量、4 个类型导入;头注释第 3 条改写 |
| `src/types/homework.ts` | 删 `DictOption`、`DeptManageItem`、`ContactGroupItem`、`ContactGroupPage`、`ContactUser`、`ContactUserPage`;分节改名为「基础平台」(保留 `SchoolTreeNode`) |
| `src/assets/mock/homeworkapi.ts` | 删 `dict` 6 桩 + `contacts` 7 桩 + `GetSchoolAll` 桩;删种子 `groups/groupSeq`、`idNames`、`teacherNames`(本就是死码)、`schoolTree`、`deptSeq`;头注释与 `deptDict` 注释同步改写。**`deptDict` 按 §7.3 保留** |
**验收实测**
1. **残留归零**:手写代码中 `settingApi|DeptManageItem|ContactGroup|ContactUser|DictOption|getSchoolAll|getGroupTree|TypeSetBox|UserCheckInput|homework_setting|deptSeq` **0 命中**。另有 12 处 grep 命中经逐条核对均非残留:`src/api/apiDefinitions.ts` 与 `globals.d.ts` 是 `npm run api:gen` 生成的 swagger 快照(镜像后端现状,后端端点仍在,属正确内容);其余为 `SchoolTreeNode` 被大小写不敏感的 `schoolTree` 模式误命中。
2. **构建通过**:`npm run build` **EXIT=0**,16.79s。
3. **mock 冒烟**(`dev:mock` @8077):保留面 9 个端点全 `code=0`(`List` 5/7、`SendList` 3/3、`Detail`、`ReadDetail` 3 组、`MyHomeworks` buckets `{todo:3,submitted:1,graded:2,redo:0,all:7}`、`Statis`、`GetClassTree`、`GetUserInfo`、`GetUserMod`);**§7.3 保留项回归**:`Create(deptId:"D002")` → `GetDetail` 仍解析出 `DeptName="数学教研组"`、`DeptID="D002"`,说明 `deptDict` 未误删;已删端点 6/6 全部 **404**(`dict/List`、`dict/ManageList`、`dict/Sync`、`contacts/GetGroup`、`contacts/GetGroupTree`、`base/GetSchoolAll`)。临时服务按 PID 命令行确认归属后 taskkill,8077 已释放。
4. **类型侧**:沿用 §6.4 第 4 条结论(`vue-tsc@1.8.27` 与 `typescript@5.9.3` 不兼容、工程 tsc 基线 65 条存量错),本次只核对「无引用已删符号的新报错」。
**遗留(有意不动)**:`vuedraggable` 依赖失去唯一使用者,留待统一清依赖时处理;`apiDefinitions.ts`/`globals.d.ts` 待下次 `npm run api:gen` 自然刷新;后端 `dict/*`、`contacts/*` 与三张表按裁决保留。
**回退方式**:`git checkout HEAD -- src/views/homework/HomeworkSetting.vue src/components/TypeSetBox.vue src/components/UserCheckInput.vue` 复原三文件;routes/menu/menuRoute/api/types/mock 六个文件的改动前快照在 `.backup/20260915-drop-app-setting/`。