helixlife-v5-cli 1.1.3 → 1.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helixlife-v5-cli",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "Helix(vip.helixlife.cn)精细化浏览器自动化命令行工具。",
5
5
  "main": "helix-cli.js",
6
6
  "bin": {
@@ -31,6 +31,7 @@
31
31
  - **直播分类(必辨路由)**:
32
32
  - `pathname` 为 `/edu/search`(顶栏搜索打开的页面):仅在 `main` 内点 `getByText('直播')`(与"课程"并列的结果 Tab),见 S-18 第 3 步;URL 常追加 `activeTab=live`。**禁止**改点侧栏 `to='/edu/courses'` 或套用 S-14。
33
33
  - `pathname` 为 `/edu/courses` 或其它学习中心列表(非 `/edu/search`):「直播」指 S-14 顶栏「直播」 + 子类 → `/edu/lives?category=…`。
34
+ - **学习中心 · 子类消歧(必辨父 Tab)**:顶栏「课程 / 训练营 / 直播」为**一级 Tab**,各自下拉内有**独立子类**;**子类名可跨 Tab 重名**(实测「三十六策」同时存在于**课程**与**直播**下拉)。用户说「**课程**下的三十六策」→ **必须先点「课程」Tab 展开下拉**,再点该下拉内的「三十六策」→ 验收 `pathname` 仍为 `/edu/courses` 且带 `category=`、`title` 以 `课程-` 开头;**禁止**因子类名命中直播枚举而跳过「课程」Tab 直接去 `/edu/lives`。细则见「学习中心 · 一级 Tab 与子类消歧」。
34
35
  - **播放页内切节(同课程:视频 / PDF / 作业等)**:用户说「下一个作业 / 下一节 / 下一份 PDF / 下一套小测」等,意图为**同一门课、同一大纲内**换到另一节时:
35
36
  1. 先用 `eval`(如 `location.pathname`)或 `snapshot` 确认当前路由。
36
37
  2. 若已在 `/edu/study/courses/{courseId}`:**必须优先**在本页 `main` 左侧「目录」(必要时先 `click` → `getByText('查看全部')` 展开)中,用 `getByText('<节完整标题>', { exact: true })` 直接 `click` 切节。视频、PDF、作业/小测等**均在本页完成**。
@@ -162,7 +163,7 @@
162
163
  | 数据分析 · 列表 / 主·子分类 / 拼图 / 全局设置 / 卡片收藏 | S-13 |
163
164
  | 数据分析 · 拼图工具子页(ABC 标注 · 生信工具 · 拖图 · 参考线 · TIFF/PDF 导出) | S-27(细则见「数据分析 · 拼图工具页」) |
164
165
  | 数据分析 · 工具详情页(上传 / 验证 / 参数重置保存 / 确认出结果 / 顶栏文档) | S-28(细则见「数据分析 · 工具详情页」) |
165
- | 学习中心 · 进入与 Tab/子类 | S-14 |
166
+ | 学习中心 · 进入与 Tab/子类(含跨 Tab 重名消歧) | S-14 |
166
167
  | 学习中心 · 课程排序/个人筛选 | S-17 |
167
168
  | 学习中心 · 顶栏关键字搜索(新标签) | S-18 |
168
169
  | 搜索页 · 同关键词下切「课程 / 直播」结果 | S-18 第 3 步(非 S-14) |
@@ -469,7 +470,7 @@ div.side-menu--item[to='/edu/courses']
469
470
 
470
471
  #### 数据分析 `/analysis/`(列表 · 拼图 · 全局设置)
471
472
 
472
- > **核验记录**(2026-05-13 / 2026-05-15 / 2026-05-21):全局设置弹窗、顶区**主分类下拉**(如「分析工具」→「交互网络」;URL `primary=`)与「拼图工具」形态 Tab、搜索框 + 搜索按钮、**子分类**标签条「展开 ⇄ 收起」(URL `subcategory=` / `isExpanded`)、工具卡片「收藏 / 已收藏」互斥文案均已核验。主分类项(如「交互网络」)**不在**标签条内,须展开顶栏下拉才可见——见下文「分类体系」。工具详情(`/analysis/{toolId}`,D-1~D-11)、拼图工具子页(`/analysis/jigsaw`)见 S-28 / S-27。
473
+ > **核验记录**(2026-05-13 / 2026-05-15 / 2026-05-21):全局设置弹窗、顶区**主分类下拉**(固定 7 项:全部工具 / 基础绘图 / 表达差异 / 功能聚类 / 交互网络 / 临床意义 / 其他;URL `primary=`)与「拼图工具」形态 Tab、搜索框 + 搜索按钮、**子分类**标签条「展开 ⇄ 收起」(URL `subcategory=` / `isExpanded`)、工具卡片「收藏 / 已收藏」互斥文案均已核验。主分类项**不在**标签条内——见「分类体系」。工具详情(`/analysis/{toolId}`,D-1~D-11)、拼图工具子页(`/analysis/jigsaw`)见 S-28 / S-27。
473
474
 
474
475
  ##### URL / 路由形态
475
476
 
@@ -609,33 +610,48 @@ helixlife-v5-cli run-code --filename=scripts/analysis-detail-upload-file.js
609
610
 
610
611
  ##### 数据分析 · 分类体系(主分类 vs 子分类 · Agent 必读)
611
612
 
612
- > **核验记录**(2026-05-21):列表页顶栏**最左侧**为**主分类下拉**(带下箭头;选中后 Tab 文案变为该主分类名,如「交互网络」)。「交互网络」等主分类项**不在**搜索框下方标签条内,须先 `click` 展开当前主分类名(默认「分析工具」)后才可见。URL `primary=` 对应该下拉;`subcategory=` 对应标签条。
613
+ > **核验记录**(2026-05-21):列表页顶栏**最左侧**为**主分类下拉**(带下箭头;选中后 Tab 文案变为该主分类名)。下拉内主分类为**固定 7 项**(见下表),**均不在**搜索框下方标签条内。URL `primary=` 对应该下拉;`subcategory=` 对应标签条。顶栏未切换时可能仍显示「分析工具」——这是模块入口文案,**勿**与主分类枚举混淆;切换主分类须**展开下拉**点选 7 项之一。
614
+
615
+ ###### 主分类枚举(固定 7 项 · 优先匹配)
616
+
617
+ | # | 主分类名 | 说明 |
618
+ | --- | --- | --- |
619
+ | 1 | **全部工具** | 全量工具列表 |
620
+ | 2 | **基础绘图** | |
621
+ | 3 | **表达差异** | |
622
+ | 4 | **功能聚类** | |
623
+ | 5 | **交互网络** | |
624
+ | 6 | **临床意义** | |
625
+ | 7 | **其他** | |
626
+
627
+ > 用户说「分析工具下的 XX」「切到 XX 分类」且 **XX 命中上表** → **一律按主分类处理**,**禁止**先去标签条找。仅当 XX **不在**上表、且出现在搜索框下方标签条时,才按子分类处理。
613
628
 
614
629
  ###### 页面分区
615
630
 
616
631
  ```
617
632
  顶栏左侧 [主分类 ▼] [拼图工具] 顶栏右侧 [全局设置]
618
- ↓ primary=
633
+ ↓ primary= (下拉内:全部工具 / 基础绘图 / … / 其他)
619
634
  搜索框 [请输入搜索关键词] [搜索]
620
635
  标签条 [全部] [类别比较] [差异分析] … [展开 | 收起]
621
- ↓ subcategory=
636
+ ↓ subcategory= (随当前主分类变化;≠ 上表 7 项)
622
637
  工具卡片列表
623
638
  ```
624
639
 
625
- | 层级 | UI 位置 | URL 参数 | 典型选项 | 用户常见说法 |
640
+ | 层级 | UI 位置 | URL 参数 | 选项来源 | 用户常见说法 |
626
641
  | --- | --- | --- | --- | --- |
627
- | **主分类** | 顶栏**最左侧**带下箭头的文案(选中后显示为该主分类名) | `primary=` | 分析工具、交互网络、…(**以展开下拉 snapshot 为准**,勿写死枚举) | 「分析工具下的交互网络」「切到交互网络」 |
628
- | **子分类** | 搜索框**下方**横排可点标签 | `subcategory=` | 全部、差异分析、网络图相关、互作分析、…(**随当前主分类变化**) | 「切到差异分析」「网络图相关分类」 |
642
+ | **主分类** | 顶栏**最左侧**带下箭头的文案 | `primary=` | **上表固定 7 项**(仅在下拉菜单内) | 「切到交互网络」「分析工具下的功能聚类」 |
643
+ | **子分类** | 搜索框**下方**横排标签 | `subcategory=` | 随主分类变化(如差异分析、网络图相关、互作分析…) | 「切到差异分析」「网络图相关」 |
629
644
  | **形态 Tab** | 主分类右侧「拼图工具」 | 路由 → `/analysis/jigsaw` | 拼图工具 | 「去拼图工具」 |
630
645
 
631
- > **易混点**:「网络图相关」「互作分析」是**子分类标签**,与主分类「交互网络」**不是同一控件**;勿因语义相近而误点标签条或搜索。
646
+ > **易混点**:「网络图相关」「互作分析」是**子分类**;「交互网络」是**主分类**——同名/近义也**不得**先查标签条。
632
647
 
633
648
  ###### 用户话术 → 操作映射
634
649
 
635
650
  | 用户说法 | 操作层 | 推荐操作 |
636
651
  | --- | --- | --- |
637
- | 「分析工具下的 XX」「切到 XX 主分类」 | 主分类 | 展开顶栏主分类下拉 → `click` → `getByText('XX', { exact: true })` |
638
- | 「XX 分类」「筛选 XX」且 XX 在标签条可见 | 子分类 | `click` 标签条 `getByText('XX', { exact: true })` |
652
+ | XX 为**主分类枚举 7 项之一** | 主分类 | 展开顶栏下拉 → `click` → `getByText('XX', { exact: true })` |
653
+ | 「分析工具下的 XX」且 XX 7 项内 | 主分类 | 同上(「分析工具」是模块说法,不是下拉里的第 8 项) |
654
+ | XX **不在** 7 项内,且 snapshot 标签条可见 XX | 子分类 | `click` → 标签条 `getByText('XX', { exact: true })` |
639
655
  | 「去拼图工具」 | 形态 Tab | `click` → `getByText('拼图工具', { exact: true })` |
640
656
  | 「找 Limma / 配对图 / 某工具名」 | 工具卡片 | 搜索或 `click` 卡片主标题(S-13 步骤 7) |
641
657
 
@@ -643,33 +659,36 @@ helixlife-v5-cli run-code --filename=scripts/analysis-detail-upload-file.js
643
659
 
644
660
  1. **`snapshot`(必做)**。
645
661
  2. 目标是**具体工具名**(卡片标题)→ 搜索或点卡片;**勿**当分类处理。
646
- 3. 目标名是否出现在**搜索框下方标签条**(当前可见,或点「展开」后可见)→ 是:`click` 该标签 验收 URL `subcategory=` 变化。
662
+ 3. **将目标名与「主分类枚举 7 项」做精确匹配**(含用户说的「分析工具下的 XX」→ XX 匹配):
663
+ - **命中** → **直接走主分类**(步骤 4~5),**跳过**标签条检索。
664
+ - **未命中** → 继续步骤 6(子分类)。
647
665
  4. 顶栏主分类文案**已是**目标名 → 已选中,结束。
648
- 5. 否则:`click` **顶栏当前主分类名**(带下箭头;以 snapshot 顶栏左侧块为准)展开下拉若菜单含目标名:`click` → 验收 URL `primary=` 变化且顶栏文案更新为目标名。
649
- 6. 下拉与标签条**均无**目标名**最后手段**用搜索找工具(非切换主分类);仍无则向用户确认是「分类名」还是「工具名」。
650
- 7. **切换主分类后**:若搜索框有残留关键词,先清空(搜索区清除钮或 `fill` 空串再搜),再 `snapshot` 验收列表。
666
+ 5. **主分类切换**:`click` 顶栏当前主分类块(带下箭头)展开下拉 `click` → `getByText('<目标主分类>', { exact: true })` → 验收 URL `primary=` 变化且顶栏文案更新 → **清空搜索框残留** → `snapshot` 验收列表。
667
+ 6. **子分类**(仅当步骤 3 未命中主分类枚举):目标名是否在搜索框下方标签条(不可见则先点「展开」)→ 是:`click` 该标签 验收 `subcategory=` 变化。
668
+ 7. 主分类下拉与标签条**均无**目标名 最后手段用搜索找**工具**;仍无则向用户确认是「主分类 / 子分类 / 工具名」。
651
669
 
652
670
  **何时才向用户追问**
653
671
 
654
672
  | 情况 | 处理 |
655
673
  | --- | --- |
656
- | 下拉与标签条**都有**同名项 | 问:「是顶栏主分类还是下方标签?」 |
657
- | 两处都找不到目标名 | 问:「是分类名还是具体工具名?」 |
658
- | 用户只说「切换分类」且语义不明 | 可先 `snapshot` 列出当前可见主分类/子分类供其选择 |
674
+ | 目标名**同时**出现在主分类 7 项与标签条(极少见) | 问:「是顶栏主分类还是下方标签?」 |
675
+ | 不在 7 项、也不在标签条 | 问:「是主分类、子分类还是具体工具名?」 |
676
+ | 用户只说「切换分类」且未给名称 | `snapshot` 后列出 7 个主分类 + 当前可见子分类供选 |
659
677
 
660
678
  **反模式**
661
679
 
662
- - **禁止**未展开顶栏主分类下拉就假定目标在标签条。
663
- - **禁止**用搜索框切换**主分类**(搜索仅筛工具卡片)。
680
+ - **禁止**对主分类 7 项中的名称**先查标签条**(这是上次误点的根因)。
681
+ - **禁止**未展开顶栏下拉就假定目标在标签条。
682
+ - **禁止**用搜索框切换主分类。
664
683
  - **禁止**因语义相近误点子分类(如用「网络图相关」代替「交互网络」)。
665
- - **禁止**把「展开/收起」当作切换主分类——它仅控制标签条 `isExpanded` 显示更多子分类。
684
+ - **禁止**把标签条「展开/收起」当作切换主分类。
666
685
 
667
686
  ###### 分类 · 稳定定位器速查
668
687
 
669
688
  | 控件 | 推荐定位器 |
670
689
  | --- | --- |
671
- | 展开主分类下拉 | `click` snapshot 顶栏左侧主分类块(当前主分类名 + 箭头);勿与标签条同名项混淆 |
672
- | 主分类菜单项 | 展开后 `getByText('<目标主分类>', { exact: true })` |
690
+ | 展开主分类下拉 | `click` snapshot 顶栏左侧主分类块(当前显示名 + 箭头) |
691
+ | 主分类菜单项 | 展开后 `getByText('<目标主分类>', { exact: true })`(7 项之一) |
673
692
  | 子分类标签 | 搜索框下方 `getByText('<子分类名>', { exact: true })` |
674
693
  | 拼图工具 Tab | `getByText('拼图工具', { exact: true })` |
675
694
  | 标签条展开/收起 | `getByText('展开', { exact: true })` / `getByText('收起', { exact: true })` |
@@ -679,7 +698,7 @@ helixlife-v5-cli run-code --filename=scripts/analysis-detail-upload-file.js
679
698
  | # | 语义 | 推荐操作 | 验收 / 备注 |
680
699
  | --- | --- | --- | --- |
681
700
  | 1 | **全局设置**(顶区右侧文案按钮) | `click` → `locator('main').getByText('全局设置')`(必须加 `main`) | 出现 `dialog`,可访问名为 `全局设置`。弹窗内可见「配色方案」「全局字体」「颜色面板」三块:配色为下拉 + 预览色块(示例 `NPG方案`);全局字体、颜色面板各带 `combobox`(示例 `Arial`、`随机颜色`)。底部 button:「重置」「保存」。关闭:`click` → `getByRole('dialog', { name: '全局设置' }).getByRole('button', { name: 'Close' })` |
682
- | 2 | **顶区 · 主分类下拉** | `click` 顶栏当前主分类名展开菜单 → `click` → `getByText('<主分类名>', { exact: true })`。默认项为「分析工具」;选中后顶栏文案变为该主分类名(如「交互网络」) | URL `primary=` 变化;顶栏文案更新。细则见「分类体系 · Agent 决策树」 |
701
+ | 2 | **顶区 · 主分类下拉** | `click` 顶栏当前主分类块展开菜单 → `click` → `getByText('<主分类名>', { exact: true })`。主分类**仅**为固定 7 项:全部工具、基础绘图、表达差异、功能聚类、交互网络、临床意义、其他 | URL `primary=` 变化;顶栏文案更新为目标主分类名。细则见「分类体系 · Agent 决策树」 |
683
702
  | 3 | **顶区 · 形态 Tab:拼图工具** | `click` → `getByText('拼图工具', { exact: true })`;从拼图回列表:`click` → `getByText('分析工具', { exact: true })` 或 `div.side-menu--item[to='/analysis']` | 拼图:`pathname === '/analysis/jigsaw'`,`title` 以 `拼图工具-` 开头。列表:`pathname` 为 `/analysis`/`/analysis/`,`title` 以 `数据分析-` 开头 |
684
703
  | 4 | **搜索(找工具,非切主分类)** | `fill` → `getByRole('textbox', { name: '请输入搜索关键词' })`;`click` → `getByRole('button', { name: '搜索' })` | 列表随关键字刷新(以 `snapshot`/卡片集合变化验收)。**勿**用搜索代替主分类切换 |
685
704
  | 5 | **子分类标签 + 展开 / 收起** | 搜索框下方为**子分类**可点文案(如「全部」「类别比较」「差异分析」「网络图相关」…),**仅在当前主分类下生效**。分类行右侧「展开」/「收起」:`click` → `getByText('展开', { exact: true })` → URL 中 `isExpanded=true`;再点 `getByText('收起', { exact: true })` → `isExpanded=false` | URL `subcategory=` / `isExpanded` 与选中标签、展开态一致 |
@@ -756,19 +775,73 @@ helixlife-v5-cli run-code --filename=scripts/analysis-detail-upload-file.js
756
775
 
757
776
  **说明**:`/edu/courses` 上切换顶栏「课程/训练营/直播」时,只有「课程」保持上述路径;点选训练营/直播子类会 push 到 `/edu/trainings?…`、`/edu/lives?…`(`category` 由后端配置,勿硬编码)。需确定回到课程列表时优先 `click` → `div.side-menu--item[to='/edu/courses']`,比顶栏 `getByText('课程')` 更稳。
758
777
 
778
+ #### 学习中心 · 一级 Tab 与子类消歧(Agent 必读)
779
+
780
+ > **核验记录**(2026-05-22):「课程」「训练营」「直播」均为**带下箭头的一级 Tab**;子类仅出现在**对应 Tab 展开的下拉菜单**内。子类名**可跨 Tab 重名**——实测 **「三十六策」** 同时存在于「课程」与「直播」下拉,但路由不同(`/edu/courses?category=…` vs `/edu/lives?category=…`)。
781
+
782
+ ###### 一级 Tab 与子类枚举
783
+
784
+ | 一级 Tab | 路由(选子类后) | 子类(下拉内 · 2026-05-22 实测) |
785
+ | --- | --- | --- |
786
+ | **课程** | `/edu/courses` + `?category=…` | 全部课程、**三十六策**、套路体系、基金申请、基础科研、生信分析、临床研究、文献阅读、科研规划 |
787
+ | **训练营** | `/edu/trainings` + `?category=…` | 全部训练营、7天主题营、14天成长营、21天讲席营、30天陪读营 |
788
+ | **直播** | `/edu/lives` + `?category=…` | 全部直播、直播季、名师直播、陪读营、**三十六策**、直播大课 |
789
+
790
+ ###### 跨 Tab 重名子类(必记)
791
+
792
+ | 子类名 | 出现的 Tab | 验收 |
793
+ | --- | --- | --- |
794
+ | **三十六策** | 课程、直播 | 课程:`pathname === '/edu/courses'` 且 `title` 以 `课程-` 开头;直播:`pathname === '/edu/lives'` 且 `title` 以 `直播-` 开头 |
795
+
796
+ > 运营后续若新增跨 Tab 重名,按同样格式追加本表;**勿**假设子类名全局唯一。
797
+
798
+ ###### 用户话术 → 操作映射
799
+
800
+ | 用户说法 | 解析 | 操作 |
801
+ | --- | --- | --- |
802
+ | 「**课程**下的 XX / 切到课程 XX」 | 父 Tab = **课程** | `click` 顶栏「课程」Tab 块展开下拉 → 在下拉内 `click` → `getByText('XX', { exact: true })` |
803
+ | 「**直播**下的 XX / 切到直播 XX」 | 父 Tab = **直播** | 同上,父 Tab 换「直播」 |
804
+ | 「**训练营**下的 XX」 | 父 Tab = **训练营** | 同上,父 Tab 换「训练营」 |
805
+ | 仅说「三十六策」等**重名子类**、未给父 Tab | 歧义 | **向用户确认**「课程还是直播?」;或 `snapshot` 列出两 Tab 供选。**禁止**默认走直播 |
806
+
807
+ ###### Agent 决策树(切换学习中心子类时必走)
808
+
809
+ 1. **`snapshot`(必做)**。
810
+ 2. 从用户句中解析**父 Tab 关键词**(课程 / 训练营 / 直播):
811
+ - **已明确**(如「课程下的三十六策」)→ 锁定该 Tab,**禁止**因子类名出现在其它 Tab 枚举而改道。
812
+ - **未明确**且子类名在「跨 Tab 重名」表 → **追问用户**或列出选项。
813
+ - **未明确**且子类名**仅**出现在某一 Tab 的子类枚举 → 可推断该 Tab(如「7天主题营」→ 训练营)。
814
+ 3. **`click` 对应一级 Tab**(带下箭头的块,非整页裸 `getByText`)**展开下拉**。
815
+ 4. **在该 Tab 的下拉菜单作用域内** `click` 目标子类(推荐先缩到 Tab 块再点,避免 strict / 误触另一 Tab 同名项):
816
+
817
+ ```js
818
+ const tab = page.locator('main').locator('div').filter({ hasText: /^课程$/ }).first();
819
+ await tab.click();
820
+ await tab.getByText('三十六策', { exact: true }).click();
821
+ ```
822
+
823
+ 5. **验收**(必做):`pathname` 与 `document.title` 前缀须与**父 Tab**一致(见上表);`category=` 由后端配置,勿硬编码 UUID。
824
+
825
+ **反模式**
826
+
827
+ - **禁止**未确定父 Tab 就对全页 `getByText('三十六策')`(`.first()` 可能点到错误 Tab 或 strict 失败)。
828
+ - **禁止**用户已说「课程下的…」仍因文档/live 枚举含该名而跳转到 `/edu/lives`。
829
+ - **禁止**用搜索页 S-18 的「课程/直播」结果 Tab 代替列表页 S-14 的一级 Tab 下拉(除非当前已在 `/edu/search`)。
830
+
759
831
  #### 信息架构与功能区块
760
832
 
761
833
  | 层级 | 区块 | 功能要点 |
762
834
  | --- | --- | --- |
763
835
  | 全局 | 左侧主导航 | `div.side-menu--item[to='/edu/courses']`(S-07) |
764
836
  | 列表公共顶栏 | 搜索 | 见 S-18;与应用/数据分析同一占位「请输入搜索关键词」+ 按钮「搜索」。数据分析同页内搜索见 S-13 |
765
- | 列表 | 一级 Tab | 「课程」「训练营」「直播」:`getByText('课程')` |
837
+ | 列表 | 一级 Tab | 「课程」「训练营」「直播」:均为**带下箭头下拉**的一级 Tab;`getByText('课程')` 等须配合展开后再点子类 |
838
+ | 课程 | 子类 | 全部课程、三十六策、套路体系、基金申请、基础科研、生信分析、临床研究、文献阅读、科研规划 → `/edu/courses?category=…`(**「三十六策」与直播下拉重名**,须先确定父 Tab,见「一级 Tab 与子类消歧」) |
766
839
  | 课程列表 | 排序与筛选 | 「最新」「最热」(`paragraph` 可点);`getByRole('checkbox', { name: '只显示我学习的' })` |
767
840
  | 课程列表 | 课程卡片 | 难度、已学人数、时长、已学进度(如「已学4%」);点击进入 `/edu/courses/…/learn`(S-19) |
768
841
  | 训练营 | 子类 | 全部训练营、7天主题营、14天成长营、21天讲席营、30天陪读营 → `/edu/trainings?category=…` |
769
842
  | 训练营列表 | 筛选 | 「全部」;「最新」「最热」;`getByRole('checkbox', { name: '只显示我报名的' })` |
770
843
  | 训练营列表 | 卡片 | 营名、营类型、人数/状态(如「待开营」)、时长;点击进入 `/edu/trainings/{id}`(S-20) |
771
- | 直播 | 子类 | 全部直播、直播季、名师直播、陪读营、三十六策、直播大课 → `/edu/lives?category=…` |
844
+ | 直播 | 子类 | 全部直播、直播季、名师直播、陪读营、三十六策、直播大课 → `/edu/lives?category=…`(**「三十六策」与课程下拉重名**,须先确定父 Tab) |
772
845
  | 直播列表 | 讲师筛选 | 全部、酸菜、老谈、雪球、虾仁、猫大、AW、大空熠等;`getByText('雪球')` 等 |
773
846
  | 直播列表 | 个人筛选 | `getByRole('checkbox', { name: '只显示我预约的' })` |
774
847
  | 直播列表 | 卡片 | 时间、标题、讲师·预约人数;状态标签「预约中」「回放」;推荐直接点标题进入详情(S-21) |
@@ -936,9 +1009,10 @@ async page => {
936
1009
  | 进入学习中心 | `div.side-menu--item[to='/edu/courses']` | 与全局侧栏表一致 |
937
1010
  | 顶栏搜索框 | `getByRole('textbox', { name: '请输入搜索关键词' })` | 列表/搜索页通用 |
938
1011
  | 顶栏搜索按钮 | `getByRole('button', { name: '搜索' })` | 点击后常**新开标签**(S-18) |
939
- | 一级 Tab | `getByText('课程')`、`getByText('训练营')`、`getByText('直播')` | 必要时 `.first()` 或缩小到 `main` |
940
- | 训练营子类 | `getByText('7天主题营')` 等 | 须先展开训练营 Tab |
941
- | 直播子类 | `getByText('名师直播')` 等 | 须先展开直播 Tab |
1012
+ | 一级 Tab | `getByText('课程')`、`getByText('训练营')`、`getByText('直播')` | 切换子类须先 `click` Tab 块**展开下拉**,再点下拉内子类;跨 Tab 重名见「一级 Tab 与子类消歧」 |
1013
+ | 课程子类 | `getByText('三十六策')` 等 | **须**先展开「课程」Tab,并在该 Tab 作用域内点击 |
1014
+ | 训练营子类 | `getByText('7天主题营')` 等 | 须先展开「训练营」Tab |
1015
+ | 直播子类 | `getByText('名师直播')` 等 | 须先展开「直播」Tab;「三十六策」与课程重名,**禁止**全页裸点 |
942
1016
  | 课程 · 最新/最热 | `getByText('最新')`、`getByText('最热')` | 与直播/训练营列表同文案 |
943
1017
  | 课程 · 仅看我的学习 | `getByRole('checkbox', { name: '只显示我学习的' })` | |
944
1018
  | 训练营 · 仅看我的报名 | `getByRole('checkbox', { name: '只显示我报名的' })` | |
@@ -1052,8 +1126,8 @@ async page => {
1052
1126
  3. **(可选)形态 Tab**:`click` → `getByText('拼图工具', { exact: true })` → 验收 `/analysis/jigsaw`。回列表优先 `click` → `div.side-menu--item[to='/analysis']` 或顶栏主分类「分析工具」。
1053
1127
  4. **(可选)搜索**:`fill` → `getByRole('textbox', { name: '请输入搜索关键词' })` → `click` → `getByRole('button', { name: '搜索' })`(找工具,非切主分类)。
1054
1128
  5. **(可选)切换分类**(细则见「分类体系 · Agent 决策树」):
1055
- - **子分类**:`click` 标签条文案 → 验收 `subcategory=`;按需点「展开」/「收起」验收 `isExpanded`。
1056
- - **主分类**:`click` 顶栏当前主分类名展开下拉 → `click` 目标 → 验收 `primary=` 且顶栏文案更新;**清空搜索框残留**后再验收列表。
1129
+ - **主分类(优先)**:目标名命中 7 项枚举 → 展开顶栏下拉 → `click` 目标 → 验收 `primary=` 且顶栏文案更新;**清空搜索框残留**后再验收列表。
1130
+ - **子分类(仅当未命中 7 项)**:`click` 标签条文案 → 验收 `subcategory=`;按需点「展开」/「收起」验收 `isExpanded`。
1057
1131
  6. **(可选)收藏/取消收藏**:目标卡片上 `click` → `收藏` → 验收 `已收藏`;再 `click` → `已收藏` → 回 `收藏`。
1058
1132
  7. **进入某一工具详情**:`main` 内 `click` → `getByText('<卡片主标题>', { exact: true })`。验收 `pathname` 为 `/analysis/{toolId}`;面包屑「数据分析 / {工具名}」。后续 → S-28(D-1~D-11)。
1059
1133
 
@@ -1084,11 +1158,11 @@ async page => {
1084
1158
 
1085
1159
  1. S-07 → `click` → `div.side-menu--item[to='/edu/courses']`。
1086
1160
  2. **验收**:`location.pathname` 为 `/edu/courses`;`title` 以 `课程-` 开头;`main` 内可见顶栏「搜索」与「课程/训练营/直播」。
1087
- 3. **切换一级形态**(按需,均禁止对 `/edu/**` 使用 `goto`):
1088
- - **课程**:`click` → `getByText('课程')`(若当前在 `/edu/trainings` `/edu/lives`,更稳为 `click` 侧栏 `to='/edu/courses'`)。
1089
- - **训练营**:`click` → `getByText('训练营')` → 展开后 `click` → `getByText('<子类名>')`(如 7天主题营)。验收:`pathname` 为 `/edu/trainings` 且带 `category=`;`title` 以 `训练营-` 开头。
1090
- - **直播**:`click` `getByText('直播')` → `click` → `getByText('<子类名>')`(如名师直播)。验收:`pathname` 为 `/edu/lives` 且带 `category=`;`title` 以 `直播-` 开头。
1091
- 4. **优化**:子类名、讲师名以运营配置为准;失效时用 `snapshot` 更新本文枚举,不要猜 `category` UUID
1161
+ 3. **切换一级形态**(按需,均禁止对 `/edu/**` 使用 `goto`;**子类切换细则见「一级 Tab 与子类消歧」**):
1162
+ - **课程 · 子类**:`click` 顶栏「课程」Tab 块展开下拉 → `click` → `getByText('<子类名>', { exact: true })`(须在该 Tab 作用域内)。验收:`pathname` `/edu/courses` 且带 `category=`;`title` `课程-` 开头。例:「课程下的三十六策」→ 课程 Tab → 三十六策,**勿**走直播。
1163
+ - **训练营 · 子类**:`click` → `getByText('训练营')` → 展开后 `click` → `getByText('<子类名>')`(如 7天主题营)。验收:`pathname` 为 `/edu/trainings` 且带 `category=`;`title` 以 `训练营-` 开头。
1164
+ - **直播 · 子类**:`click` 顶栏「直播」Tab 块展开下拉 → `click` → `getByText('<子类名>', { exact: true })`(须在该 Tab 作用域内)。验收:`pathname` 为 `/edu/lives` 且带 `category=`;`title` 以 `直播-` 开头。
1165
+ 4. **优化**:子类名、讲师名以运营配置为准;失效时用 `snapshot` 更新本文枚举,不要猜 `category` UUID。跨 Tab 重名子类须回写「跨 Tab 重名子类」表。
1092
1166
 
1093
1167
  #### S-17:学习中心 · 课程列表排序与个人课单筛选
1094
1168