# 学习通页面事实集(实测) > 这份文档记录的是**实测确认过的**事实,不是猜的、不是文档抄的。 > 每一条都标注了它是怎么来的。 > > **为什么要写它**:这些隐性知识过去散在代码注释里、散在我脑子里, > 每次重构都要重新踩一遍。写下来之后,人和大模型都能直接用。 --- ## 零、最重要的一条 ### 完成状态只看 `aria-label` ```html
← 权威标记在这里 ``` **`ans-job-icon-clear` 这个类名跟"完成"没有任何关系**(它是样式类)。 这个坑我踩过,而且是**双向都会错**: | 场景 | `clear` 类判断 | `aria-label` 实际 | 后果 | |---|---|---|---| | 马原 章节测验 | 无 clear | 任务点已完成 | **漏判** → 会重做已得 100 分的测验 | | 医学英语 视频 | 有 clear | 任务点未完成 | **误判** → 视频全被跳过,进度 0/66 | 而且"马原那边验证通过"是**巧合** —— 用一次巧合去验证判据,等于没验证。 **正确的验证方式是:至少要有一个"已完成"样本和一个"未完成"样本对照。** --- ## 一、页面模型:课程 → 小节 → 页面(tab) → 任务点 ``` 课程(course) └ 单元(unit) ← 侧栏可折叠分组 └ 小节(section) ← .chapter_item[id],id 里含 chapterId └ 页面(tab) ← #prev_tab li,有 title + cardid └ 任务点 ← [class*=ans-job-icon],**可以有多个!** ``` ### ★ 一个页面可以有多个任务点 实测(医学英语 Unit 1 小节 2): | tab | 任务点 | |---|---| | `Video 3` | 视频(已完成)+ 章节测验「Unit 1 video 3」4 题(未完成) | | `Video 4` | 视频(已完成)+ 章节测验「Unit 1 video 4」3 题(未完成) | | `Listening` | 音频(未完成)+ 听力练习 4 题(未完成) | | `Discussion` | **0 个**(纯讨论帖,不计进度) | **所以「切过去 → 处理一个 → 翻页」是错的。必须先枚举这一页的所有任务点。** (这正是 `inventory()` 存在的理由) ### 侧栏徽章 `.chapter_item` 的文字末尾带一个数字(如 `①` `⑤`)或 ✔,代表**该小节剩余任务点数**。 实测可读,但它是**页面加载时**的快照,可能滞后。 **权威进度**永远是章节页头部的「**已完成任务点: X/Y**」。 (课程列表页的 `.btm-cover` 进度条**不可靠** —— 25 张卡片里只渲染 3~4 个;对应接口已 404) --- ## 二、frame 结构(三层,容易搞错) ``` 主 frame: /mycourse/studentstudy?chapterId=...&courseId=... ├ 知识卡片: /mooc-ans/knowledge/cards?clazzid=...&knowledgeid=...&num=N │ ← 任务点图标在这里;num 就是 tab 序号 ├ 模块外壳: /ananas/modules/video|audio|work|pdf|insertbbs/index.html │ ← **常常是空的!**(innerText 长度 0) └ 模块内容: /mooc-ans/work/doHomeWorkNew ← 题目在这里 /mooc-ans/work/selectWorkQuestionYiPiYue ← 已批阅的回顾视图 /mooc-ans/bbscircle/chapter ← 讨论帖内容在这里 ``` ### 踩过的坑 **坑 1:`find(modules/work) ?? find(doHomeWorkNew)` —— `??` 永远不执行。** 因为外壳 frame 一定存在(只是空的),所以 `??` 永远轮不到后备分支。 读到空内容,报「没有题目」。**实测:`doHomeWorkNew` 有 471 字符,`modules/work` 是 0 字符。** **坑 2:`mooc-ans/work/` 下不止一种页面。** | 页面 | 含义 | |---|---| | `doHomeWorkNew` | 待作答(有提交按钮) | | `selectWorkQuestionYiPiYue` | **已批阅的回顾视图**(有成绩「本次成绩100分」、「我的答案:A」,**没有提交按钮**) | 只认第一种,会把一个**已得满分**的测验报成「模块加载失败」。 --- ## 三、切页面(tab):`cardId` 才是判据 ### 怎么做 tab 的 `onclick` 是站点自己的函数,直接执行: ```js changeDisplayContent(idx, total, chapterId, courseId, clazzid, '') ``` **合成鼠标点击 `.chapter_item` 无效**(必须调用站点自己的 `toOld()`)。 ### 怎么判断"真的切过去了" **等 `knowledge/cards` 里的 `#cardId` 变成目标 tab 的 cardid。** 实测时间线(点击「章节测验」后): ``` ~500ms cards frame: num=1 cardId=1209451486 ← 已经切好了 模块 frame: video → work ~1500ms 出现 doHomeWorkNew ``` ### 踩过的坑 旧实现只等"某个模块 frame 就绪"—— 而**旧的 video 模块本来就已就绪**, 于是函数立刻返回,读到的全是**上一个 tab 的陈旧数据**。 结果:`cx_scan` 把「章节测验」识别成了 `type: "video"`,时长还抄了视频的 1973 秒。 **这是"章节测验被完全漏掉"的直接原因。** **判据必须是:`#cardId === 目标 cardid`,不是"某个模块能读"。** --- ## 四、题目 DOM ### 通用结构 ```html
← 每道题
1 ← 题号
← ★ 题干被字体混淆 【多选题】马撰思撱义...
提交 ``` ### 题型的三种形态 | 题型 | 选项容器 | `data` 的值 | onclick | |---|---|---|---| | 单选题 | `li.before-after-radio` | `A` `B` `C` `D` | `addSingleChoice(this)` | | 多选题 | `li.before-after-checkbox` | `A` `B` `C` `D` | `addMultipleChoice(this)` | | **判断题** | `li.font-cxsecret.before-after` `role=radio` | **`true` / `false`** | `addChoice(this)` | | **填空题** | **没有 li** | — | 是 `input[type=text]` / `textarea` | **判断题的 `data` 是 `"true"/"false"` 而不是字母** —— 用字母匹配会一个都抓不到。 **填空题没有选项,只有输入框** —— 需要写值并触发 `input`/`change` 事件。 ### 选中状态 **看 `aria-checked`**,不要看 class: ``` 点击前: li[aria-checked=null] span.class = "num_option_dx choice411484618" 点击后: li[aria-checked=true] span.class = "num_option_dx choice411484618 check_answer_dx" ``` **坑**:用 `/cur|check|selected/` 匹配 className 判定"已选中"会**全部误判** —— 因为选项的基础类名就叫 `before-after-checkbox`,**里面含 "check"**。 结果:每个选项都被当成"已选中",一个都没点。 ### ★ 题干有字体混淆 | | 内容 | |---|---| | **DOM 文本** | `马撰思撱义毳撴不撮分割的三个撱譒撯成部分是指()。` | | **截图里** | `马克思主义理论不可分割的三个主要组成部分是指()。` | 混淆标记是 `.font-cxsecret`(自定义字体)。 **选项文字不受影响,正常可读。** → **要读题干,必须截图。** 这是硬约束,不是可以绕过的。 --- ## 五、提交确认框 提交按钮点下去会弹确认框,**而确认框在主 frame 里,不在 work frame 里**: ```html
← 主 frame

确认提交?

提交 ← ★ 确认按钮
``` **坑**:用"找文字是『提交』的按钮"来点确认,会命中**表单底部那个提交按钮**, 结果确认框一直开着、提交根本没发生 —— 而代码看起来"成功"了。 **必须按 `#popok` 精确定位。** --- ## 六、播放器:两种形态 | 形态 | 位置 | 依赖 | |---|---|---| | **视频** | `/ananas/modules/video/index.html` | videojs(`.vjs-big-play-button`,`videojs.getPlayers()`) | | **音频** | `/ananas/modules/audio/index_new.html` | **原生 `