You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dianshang-qianduan/.workbuddy/memory/2026-10-14.md

62 lines
8.6 KiB
Markdown

fix(task-review): 评阅预览改用最新学生页并修复新版样式门控失配 1. previewMap 三处指向已废弃的旧页面,改为 router 实际引用的最新页面: industry-demand-analysis -> demand/industry-demand-analysis.vue competitive-environment-analysis -> demand/competitive-environment-analysis.vue market-opportunity-selection -> product/market-opportunity-selection.vue 并在文件头补充契约注释(必须与 router 一致,不得指向废弃页面)。 2. 修复评阅预览"看起来仍在用废弃页面"的根因,两重原因叠加: a) /task-review-preview 是脱离 Layout 的独立路由,DOM 里没有 .studentLayout 与 section.app-main,导致学生页新版皮肤的门控选择器 `#app .studentLayout section.app-main:has(.xxx-page) …` 集体失配, 页面静默回落全局旧版样式(胶囊满宽、步骤说明小字重新出现、按钮配色不一致)。 模板外层补两级空壳祖先容器,只补壳、不渲染顶栏侧栏。 b) 预览 iframe 宽约 1000px,命中 student-training.scss 的 @media (max-width: 1100px):`#app .student-training-shell .step-tab { width: 100% }`(特异性 1,2,0)压过预览自身 :deep() 覆盖(0,4,0), 步骤胶囊被撑成整行、只剩第一个可见并出现横向滚动条。 新增非 scoped 样式用更高特异性复位宽度,并压掉布局级背景与内边距。 实测(真实 Chrome,1000px 视口,与预览 iframe 同宽): 修复前 胶囊宽 952px x4、导航区 scrollWidth 3874/952(溢出); 修复后 214/169/199/200px、952/952(无溢出),与学生端外观一致。
7 hours ago
# 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)` 等门控块,是现新页的前身)。