Files
coursehomework-front/classwork-frontend-m5-plan.md

368 lines
40 KiB
Markdown
Raw Permalink 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/`。
---
## 8. M5 主体执行结果回填(2026-09-16 完成)
### 8.1 提交序列(每阶段一组语义化 commit)
| commit | 阶段 | 内容 |
|---|---|---|
| e6639f7 | 基线(前会话成果入库) | P0 契约层(types/homeworkV2.ts + 28 端点三客户端)+ §6/§7 下线 + 新增 TeacherHomeworks/ReviewModal/AttachmentUpload 组件(未挂路由) |
| 39824fb | P1 教师端发布链路 | /list 挂 TeacherHomeworks(已发布+草稿箱);NewHomework 切 V2(班级树/create→publish·schedule 三段式/AttachmentUpload/仅查阅互斥;转发保留 V1 GetDetail);HomeworkList 摘伪接口 remind;路由:/accept→/stu/today、新增 /stu/detail、/stu/redo initTab=todo |
| 23d6716 | P2 教师端批阅链路 | ReviewWorkbench 从 ReviewModal 抽出(版本切换/批阅/打回共用);Submissions 切 V2 矩阵(stats 汇总卡+classId/submitStatus/pendingGrade 筛选+未交占位行定向提醒);Grade 切 V2 批改中心(pendingGrade 排序+队列+内嵌工作台批完前移);HomeworkDetails 双模式(/list/detail 走 V2 详情+统计+分班进度表,/homeworklist/detail 保留 V1——V2 详情有 requireOwned 校验看不了他人作业) |
| 6258ebb | P3 学生端闭环 | StudentHomeworks 切 V2(四 tab 服务端分页+五态徽标+逾期角标+倒计时变色+todo 桶 RETURNED 置顶强化+快捷提交弹窗);HomeworkAccept 改造为学生详情页(3 秒阅读上报 onUnmounted 清理/我的提交/批阅结果/历史版本各版评语/submitType 动态表单/修改提交生成新版本/截止前撤回/打回重交);菜单与别名列齐 V2 |
| 44ace7c | P4 清理+mock+走查 | 见 §8.3 |
### 8.2 关键实现口径(与计划的差异说明)
1. **页 2/3 载体**:原计划「HomeworkList.vue 增加 草稿箱 段」,实际采用前会话已建好的独立页 TeacherHomeworks.vue 挂 /list(已发布/草稿箱双 Tab),HomeworkList.vue 仅保留给 /homeworklist(全部作业 V1 冻结)。功能映射不变,页面文件归属有变。
2. **HomeworkDetails 双模式**(计划外新增):后端 V2 `detail()` 有 `requireOwned` 归属校验,而 /homeworklist 管理页要看他人作业 → 同一组件按路由分支:`/list/detail` 走 V2(详情+stats),`/homeworklist/detail` 走 V1 冻结 Detail。
3. **学生端 redo 桶**(§4 风险 7 落地):采用「initTab 改 todo + 页内 RETURNED 红色强化置顶(当前页内重排)」;未做服务端 redo 过滤(V2 无此桶)。
4. **修改提交语义**:后端 `doSubmit` 对「已交未批」无拦截,POST 直接生成新版本——修改提交无需先撤回;撤回(withdraw)是独立动作(截止前,生成 NOT_SUBMITTED 空行后重新 POST)。
5. **已截止且 allowLate=0 的 RETURNED**:前端在列表与详情均禁重交入口(后端 doSubmit 必拒,前后端口径一致)。
6. **vite 代理**:发现工作区实际指向远程测试环境,按计划改回 `http://localhost:8102`(远程地址保留注释可切回)。
### 8.3 P4 清理与 mock
- **V1 客户端收敛**(api/module/homework.ts):仅保留仍被消费的 9 方法(sendList/modify/getHomeworkDetails/getNextPrevHomework/orgizeModify/deleteHomework/statistics/fileDownUrl/allFileDownUrl);删除伪接口方法(Submissions/SubmissionDetail/Grade/Remind/MyHomeworks/MySubmission/Submit)与孤儿方法(list/initializeRed/create/editHomework/receiveUsers/userPull/setTopmost/expedite + baseApi.getClassTree + postForm 辅助)。
- **types/homework.ts**:收敛为 V1 冻结契约,删 11 个孤儿类型(HomeworkPageResult/InitializeRedVo/HomeworkRequest/SubmitStatus/SubmissionBrief/SubmissionGroup/SubmissionsResultVo/GradeResult/SubmissionDetailVo/GradeRequest/ClassTreeNode/v2 学生端 6 类型等)。
- **删除被切换孤儿化的组件**:HomeworkUpload(消费者全部改 AttachmentUpload)、ClassCheckInput、UserCheckModal(ClassCheckInput 唯一消费者)。模板遗留死组件(BschImport/MyTree/TablePage 系等 M5 前已死)未动,留待统一清理。
- **mock 整体重写**(.backup/20260916-m5-v2-mock/ 有改动前快照):「V2 单一存储 + V1 适配层」——V2 端点 28 个直连存储(教师 19/学生 6/通知 3),V1 冻结 7 端点(SendList/Detail/GetDetail/GetNextPrev/ReadDetail/Delete/Statis)由同一存储派生(hwStatus 派生映射),与后端「同一张 homework 表」一致;提交内核对齐后端 doSubmit(版本翻转、逾期 LATE_SUBMITTED 分流、image/attach 必附附件、dataURL 拒绝、仅查阅拒绝、归档拒交),submit/action 返回 `{submissionId}`(走查中发现裸 id 契约偏差后修正,教师 create/copy 为裸 Integer 与后端一致);种子 12 份作业覆盖五态(含 DRAFT_SCHEDULED/ARCHIVED),演示学生定制七种提交态(含 hw12 双版本)。
### 8.4 验收实测
1. **dev:mock 冒烟(node fetch 逐端点,.backup/20260916-m5-v2-mock/smoke.mjs)**:**58/58 PASS**——教师 23(创建→编辑→定时→取消→发布→详情→统计→矩阵→筛选→提醒(含定向)→延长→复制→删除→批阅→版本→打回→导出占位)、学生 16(四 tab/详情/read/纯文本与带附件提交/withdraw/resubmit/版本递增/仅查阅拒绝/打回态)、通知 4(含 read-all 后 unreadCount=0)、V1 冻结 7、已删伪接口 7 端点全部 404。
2. **构建**:`npm run build` EXIT=0(约 40s)。`npx tsc --noEmit` 47 条全部为存量(基线 65 条 alova 泛型等,见 §6.4-4;vue-tsc 工具链不可用为既有问题),**零条引用已删符号**。
3. **浏览器走查(dev:mock @8077,IAB)**:9 页面渲染与数据流全过——/list(14 列统计聚合+草稿箱+定时标签)、/list/new(V2 表单)、/list/detail(统计卡+分班表)、/submissions(矩阵+筛选+未交占位)、/grade(队列 31 份+内嵌工作台)、/homeworklist 与其 detail、/stats(V1 face 由 V2 存储派生)、/stu/today(五态徽标+倒计时+身份自动切换)、/stu/detail(提交表单/双版本历史/打回原因)。**交互闭环**:批阅弹窗填 95 分+评语→保存→toast→矩阵行变「已批阅 95」;/stu/detail 停留 >3 秒→教师矩阵 isRead 0→1(firstReadAt=当前时间)。
4. **走查发现并修复 3 个缺陷**(均在 44ace7c):
- TeacherHomeworks 操作列 Dropdown 在 render 函数下插槽失效(view-ui-plus 需模板插槽),菜单项内联常显 → 改 column.slot + template #action;
- ReviewModal 残留 Vue2 写法 `:value/@input`(view-ui-plus Modal 是 modelValue),弹窗打不开 → 修正后批阅闭环通过;
- StudentHomeworks「重新提交」按钮缺 canSubmit 门控(已截止且不允许补交时后端必拒)→ 补门控。
5. **真实后端(8102)E2E 未执行**:本地后端未启动(MySQL/v3_seed 名册环境未就绪),风险 1 的种子账号联调(stu01~stu06)留待后端联调时进行;vite 代理已指向 8102,冒烟与走查均在 mock 模式完成。
### 8.5 遗留与后续
- 真实后端全链路联调(P1~P3 验收的 8102 实测部分):启动 ClassHomeWork + v3_seed.sql 后按 §3 表逐项补测。
- 通知中心页面(/notify 轻量页)为可选项未实施;notificationApi 3 端点已就绪并 mock 覆盖。
- 模板遗留死组件与 `vuedraggable` 依赖统一清理(§7.6 遗留合并处理)。
- 工作区新增用户文档 `bug检测与修复.md`(并行检测计划,44ace7c 随快照入库);`.gitignore` 增补 `.backup/` 忽略。