8.6 KiB
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.answerapi/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() 判断预览态跳过草稿读写。待用户确认是否执行
已修复:评阅预览"看起来还在用废弃页面"的根因(两个叠加原因)
用户反馈截图里步骤导航是一条满宽蓝色大条,误判为废弃页面。实测根因 不是页面文件错了,而是样式失配:
- 学生页"新版皮肤"全部由布局门控选择器承载:
#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。 - 预览 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) 等门控块,是现新页的前身)。