|
|
# 2026-10-14
|
|
|
|
|
|
## 摸清教师端「任务评阅」页面结构(是否复用学生端组件)
|
|
|
结论:**不是复制页面,而是同一个学生端 SFC 被动态复用**(源码同一份,非副本)。
|
|
|
|
|
|
链路:
|
|
|
- `views/teacherEnd/score/TaskReviewPanel.vue`(左侧班级/任务/学生列表 + 打分)右侧用 `<iframe src="...#/task-review-preview?taskKey=..&reviewPreviewKey=..">`
|
|
|
- 隐藏教师路由 `/task-review-preview`(roles:["teacher"])→ `views/teacherEnd/taskReview/StudentTaskReadonlyPreview.vue`
|
|
|
- 该组件用 `<component :is>` 动态加载 `studentTaskPreviewMap.js`:25 个 taskKey → 真实学生页组件(`@/views/foundation|demand|product|channel|test|assessment/**.vue`);仅 comprehensive-case-training 通过 props 传 previewTaskKey
|
|
|
|
|
|
数据注入(不走 props,走 sessionStorage + API 层拦截):
|
|
|
- `utils/trainingReviewPreview.js`:写 `teacher-task-review-preview:<previewKey>` = {taskKey, teachingClassId, taskConfig, answer}
|
|
|
- `api/studentTrainingAnswer.js#getStudentTrainingAnswer` 命中 preview 直接返回 preview.answer
|
|
|
- `api/trainingTask.js#getTrainingTaskByKey` 返回该班生效 taskConfig(教师覆盖优先,否则管理员案例)
|
|
|
- `utils/request.js` 拦截 POST/PUT/PATCH/DELETE(答案/学习时长/上传)直接 reject「只读模式」
|
|
|
|
|
|
只读保障三层:①点击/按键捕获 guardPreviewInteraction(拦 input/select/contenteditable/按钮,放行步骤导航)②scoped `:deep` 隐藏顶部 chrome、AI 侧栏、底部动作区,表单 pointer-events:none ③网络层拦截写请求
|
|
|
iframe 高度:子页 ResizeObserver + MutationObserver 测量 → postMessage(`teacher-task-review-preview-height`)→ 父页设置高度
|
|
|
|
|
|
风险点:学生页任何类名/结构改动会波及评阅预览(大量 :deep 依赖命名契约);学生页新增写接口需同步加入 `isTrainingReviewPreviewRequest` 名单。
|
|
|
|
|
|
### 追加:覆盖率与差异排查(重要发现)
|
|
|
- taskKey 全覆盖(25 个),不会出现"不支持预览";但**3 个 key 的预览加载的是废弃旧页面文件**(仅 previewMap 引用,router 已无引用):
|
|
|
| taskKey | 学生端实际文件 | 预览加载文件 |
|
|
|
|---|---|---|
|
|
|
| industry-demand-analysis | demand/industry-demand-analysis.vue | demand/index.vue |
|
|
|
| competitive-environment-analysis | demand/competitive-environment-analysis.vue | demand/environment.vue |
|
|
|
| market-opportunity-selection | product/market-opportunity-selection.vue | product/index.vue |
|
|
|
证据:旧页面模板头带 `TrainingTaskToolbar` + `TrainingTaskBrief` + `training-core`;新页面是自定义 `task-header/task-brief-toggle` + `xxx-core`
|
|
|
- 另有 5 个 key 存在"一个 taskKey 对应多个路由页面",previewMap 只映射其一:/demand/user(user.vue 旧6KB) 与 /demand/people;/test/channelas(test/channel.vue 3KB) 与 /test/index;/test/marketing(marketing.vue 旧) 与 /test/promotion;/channel/purchase(purchase.vue) 与 /channel/index;/assessment/index(index.vue 旧16KB)+five/kano/market/traffic 与 /assessment/diagnosis
|
|
|
- 有意差异(只读外壳):隐藏顶部标题/brief/各 toolbar、AI 侧栏与 AI 评测、底部动作区(55% 透明+灰度+不可点)、浮层弹窗;`student-training-shell` 改单列;步骤导航强制横排胶囊(132~220px);表单 pointer-events:none;数据走 sessionStorage 注入(该班生效 taskConfig + 学生 answer);iframe 无应用布局(学生端有顶栏/TagsView/侧边菜单)
|
|
|
- 待办建议:修正 previewMap 三条指向新版文件;评估废弃重复路由(/demand/user、/test/channelas、/test/marketing、/channel/purchase、/assessment/index 等)
|
|
|
|
|
|
### 已执行:previewMap 全部改指最新页面
|
|
|
- `src/views/teacherEnd/taskReview/studentTaskPreviewMap.js`:三条改为 demand/industry-demand-analysis.vue、demand/competitive-environment-analysis.vue、product/market-opportunity-selection.vue;顶部加注释声明"必须与 router 保持一致,不得指向废弃页面"
|
|
|
- `src/views/teacherEnd/taskReview/StudentTaskReadonlyPreview.vue`:补两个类名 —— 隐藏列表加 `.task-action-strip`(否则工具栏被隐藏后残留 10px 空带)、白卡规则加 `.industry-step-card`(行业页步骤容器类名不同)
|
|
|
- 校验:node 脚本交叉核对 = 映射 24 条、文件全部存在、router 全部引用、废弃页面残留引用 0;@vue/compiler-sfc parse + compileStyle 无错误
|
|
|
- 遗留风险(previewMap 换新页后新增):新版页面采用 localStorage 草稿(loadDraft/persistDraft,键含当前用户 id),评阅预览同源会读写教师自己的草稿,理论上可能先闪出教师草稿;涉及 5 个页面(new-product-survey、product-development-factors、product-development-process、industry-demand-analysis、competitive-environment-analysis),可在这些页面用 getTrainingReviewPreviewKey() 判断预览态跳过草稿读写。待用户确认是否执行
|
|
|
|
|
|
### 已修复:评阅预览"看起来还在用废弃页面"的根因(两个叠加原因)
|
|
|
用户反馈截图里步骤导航是一条满宽蓝色大条,误判为废弃页面。实测根因 **不是页面文件错了**,而是样式失配:
|
|
|
1. **学生页"新版皮肤"全部由布局门控选择器承载**:`#app .studentLayout section.app-main:has(.xxx-page) …`(非 scoped 全局块,写在每个实训页底部)。
|
|
|
而 `/task-review-preview` 路由**脱离 Layout**(`component` 直接指向组件,无 Layout 包裹)→ 没有 `.studentLayout`、没有 `section.app-main` → 这些新样式全部失配,页面静默退化到全局旧版样式(步骤说明 `small`「当前步骤」重新出现、胶囊样式丢失、按钮配色不一致)。
|
|
|
`studentLayout` 类由 `layout/index.vue` 的 `classObj` 计算(仅 role 4 挂载);`.app-main` 由 `layout/components/AppMain.vue` 渲染且样式是 scoped。
|
|
|
2. **预览 iframe 宽度约 1000px**(`TaskReviewPanel.vue` 左栏 `minmax(440px,520px)` + 右栏自适应),命中 `student-training.scss` 的 `@media (max-width: 1100px)`:
|
|
|
`.student-training-shell .step-indicator { flex-direction: column }` + `#app .student-training-shell .step-tab { width: 100% !important }`。
|
|
|
后者带 `#app`(ID 级特异性 1,2,0),压过预览自身 `:deep(.step-tab){max-width:220px}`(0,4,0)→ 每个步骤胶囊被撑成整行、只剩第一个可见并出现横向滚动条。
|
|
|
|
|
|
修复(`src/views/teacherEnd/taskReview/StudentTaskReadonlyPreview.vue`,单文件):
|
|
|
- 模板外层补空壳祖先:`<div class="studentLayout student-task-preview-root"><section class="app-main">…`,让门控样式集体生效
|
|
|
- 新增一段非 scoped `<style>`:用 `#app .studentLayout.student-task-preview-root …`(1,3,0/1,4,0)压掉布局级背景与内边距,并复位 `.step-tab/.flow-pill/.flow-step/.training-action` 的 `width: 100%`
|
|
|
- 保留原有兜底:`:deep()` 里 `flex-direction: row`、隐藏 chrome、白卡等规则不变
|
|
|
|
|
|
验证方式(可复用):用本地 `sass` 编译 `index.scss` + 页面非 scoped 样式块 + 预览组件样式块,拼成静态 HTML(`.studentLayout > section.app-main > main.student-task-readonly-preview` + 4 个 `.step-tab`),用本机 Chrome `--headless=new --window-size=1000,700 --screenshot=xxx.png` 出图,页面内探针把 computed style 写进 `<pre>` 一起截进图里。
|
|
|
结果:修复前 tabWidths=952px×4(=容器满宽)、navScroll=3874/952 溢出、small=inline(与用户截图完全一致);修复后 tabWidths=214/169/199/200px 自然宽、navScroll=952/952 不溢出、small=none,与图二学生端一致。
|
|
|
注:Chrome for Windows 为 GUI 子系统,`--dump-dom` 不写 stdout,只能靠 `--screenshot` 落盘取证。
|
|
|
|
|
|
### 学生端菜单/页面全貌(roleId=4)
|
|
|
菜单不是硬编码:`dynamicRoutes` 里大量 `hidden:true`,真正侧边菜单由 `utils/studentTrainingNavigation.js#buildStudentTaskSections()` 按后端「课程分配」动态生成(按 projectName 分组),路径查 `knownTaskPaths`。
|
|
|
静态模块:`/foundation`(3) / `/demand`(8: index、environment、people、user、business-feasibility、consumer-behavior、consumer-scenario、feature-conversion) / `/product`(7: index、price、differentiation、structure、after-sales-service、requirements-doc、hardware-cost) / `/test`(4: index、promotion、channelas、marketing) / `/channel`(3: index、purchase、supplier) / `/assessment`(3: index、diagnosis、optimization) / `/comprehensive` / `/training/task/:pageKey` / `/training-step/:taskKey/:stepId` / `/assistant` / `/ai/learning-situation-*` / `/analysis` / `/report` / `/bi` / `/examination`(index、take/:id、result/:id) / `/student-demo`。
|
|
|
遗留:5 处重复 pageKey(一个 taskKey 对多个路由入口);`demand/index.vue`、`demand/environment.vue`、`product/index.vue` 三个文件已零引用但仍在磁盘(其内部仍保留 `:has(.industry-demand-page)` 等门控块,是现新页的前身)。
|