123 lines
7.9 KiB
Markdown
123 lines
7.9 KiB
Markdown
# 完成页「统一分页架构」— 零影响审查报告(禁止应用)
|
||
|
||
**审查目标**:若应用此方案,确保**其他页面、其他功能、其他逻辑一点也不受影响**。
|
||
**结论**:仅审查,不修改仓库内任何文件。下文列出所有受影响点及达成「零影响」的**必要条件**。
|
||
|
||
---
|
||
|
||
## 一、方案会动到的文件与接口
|
||
|
||
| 文件 | 方案中的改动 |
|
||
|------|----------------|
|
||
| **CompletionView.swift** | 整文件替换:去掉 courseId;仅保留 courseTitle、completedLessonCount;纯 UI + switchToGrowthTab |
|
||
| **VerticalScreenPlayerView.swift** | 数据源改为 allItems (PlayerItem);去掉占位页与 onChange;init 去掉 navigationPath、isLastNode、courseTitle;handleBack 仅 dismiss();LessonPageView 传参可能变 |
|
||
|
||
---
|
||
|
||
## 二、依赖关系与「零影响」条件
|
||
|
||
### 2.1 谁调用 VerticalScreenPlayerView?
|
||
|
||
| 调用方 | 当前传参 | 方案中 init 若改为 3 参会怎样 |
|
||
|--------|----------|------------------------------|
|
||
| **GrowthView** (.player) | courseId, nodeId, initialScrollIndex: nil, **navigationPath: $growthPath**, **isLastNode**, **courseTitle** | ❌ 编译失败(多传 3 个参数) |
|
||
| **ProfileView** (.player) | 同上,navigationPath: $profilePath | ❌ 同上 |
|
||
| **DiscoveryView** (.player) | 同上,navigationPath: $homePath | ❌ 同上 |
|
||
| **NoteTreeView** (.player) | courseId, nodeId, initialScrollIndex(无 path / isLastNode / courseTitle) | ✅ 仍兼容 3 参 init |
|
||
|
||
**零影响条件 1**:
|
||
**VerticalScreenPlayerView 的 init 必须保留现有 6 参签名**(courseId, nodeId, initialScrollIndex, navigationPath?, isLastNode?, courseTitle?),且默认值不变。内部可改用 allItems + PlayerItem,但对外接口不变,这样 GrowthView / ProfileView / DiscoveryView **无需改一行**。
|
||
|
||
---
|
||
|
||
### 2.2 谁使用 CourseNavigation 与 CompletionView?
|
||
|
||
| 位置 | 当前行为 | 方案若不再 push .completion 会怎样 |
|
||
|------|----------|-------------------------------------|
|
||
| **GrowthView** | navigationDestination(.completion) → CompletionView(courseId, courseTitle, completedLessonCount) | 从「课程流」进完成页时不再走此分支(完成页在 TabView 内);若保留此分支,深链/通知若 push .completion 仍能展示 |
|
||
| **ProfileView** | 同上 | 同上 |
|
||
| **DiscoveryView** | 同上 | 同上 |
|
||
| **MapView** | 只 append .player,不直接 append .completion | ✅ 无影响 |
|
||
| **VerticalScreenPlayerView**(当前) | 滑到占位页时 append .completion | 方案中不再 append,完成页在 TabView 内 |
|
||
|
||
**零影响条件 2**:
|
||
- **保留** `CourseNavigation.completion` 与三处 `.completion` 的 navigationDestination。
|
||
- **CompletionView 仍保留三参**:courseId, courseTitle, completedLessonCount。这样:
|
||
- 从课程流进入时,完成页由播放器内 TabView 展示,不经过 navigationDestination;
|
||
- 若有深链/通知/其他入口直接 push .completion,三处 destination 仍能正确展示 CompletionView,且接口一致。
|
||
|
||
---
|
||
|
||
### 2.3 LessonPageView 的传参
|
||
|
||
| 当前传参 | 方案中若改为不传 navigationPath / 不传 courseTitle 会怎样 |
|
||
|----------|-----------------------------------------------------------|
|
||
| courseId, nodeId, currentGlobalNodeId, initialScrollIndex, headerConfig, **courseTitle**, **navigationPath** | LessonPageView 中两者均为可选;不传则 nil。若内部有逻辑依赖 path 或 courseTitle,可能受影响。 |
|
||
|
||
**零影响条件 3**:
|
||
在「统一分页」的播放器内部,对 **LessonPageView** 的调用仍传入 **courseTitle**、**navigationPath**(用 VerticalScreenPlayerView 持有的 courseTitle、navigationPath),与当前一致。即:仅改数据源与最后一页渲染方式,不减少对 LessonPageView 的传参。
|
||
|
||
---
|
||
|
||
### 2.4 播放器内部逻辑(其他功能)
|
||
|
||
| 当前能力 | 方案中若省略会怎样 |
|
||
|----------|--------------------|
|
||
| **handleBack** 用 navigationPath.removeLast() 或 dismiss() | 若改为仅 dismiss(),在 NavigationStack 内效果通常等价(pop 一层)。为保险起见,建议保留:有 path 且非空时 removeLast(),否则 dismiss()。 |
|
||
| **loadError / 错误态 UI** | 方案片段中未写,若删除则错误态消失。 |
|
||
| **showToast / toastMessage** | 同上,若删除则 toast 能力消失。 |
|
||
| **hideTabBar / showTabBar(onAppear / onDisappear)** | 若删除则播放器出现时 TabBar 可能仍显示。 |
|
||
| **currentPositionProgress**(不含占位页) | 方案中有「进度计算忽略完结页」,逻辑等价即可。 |
|
||
| **GeometryReader 等布局** | 若删除可能影响布局,建议保留与当前一致。 |
|
||
|
||
**零影响条件 4**:
|
||
播放器内**保留**:loadError / 错误态 UI、showToast / toastMessage、hideTabBar / showTabBar、handleBack 的 path 优先逻辑(若保留 navigationPath 参数则一并保留)、以及现有布局与进度计算方式(仅把「占位页」换成虚拟 completion 页,进度仍只按真实小节算)。
|
||
|
||
---
|
||
|
||
### 2.5 笔记流(NoteTreeView / NoteListView)
|
||
|
||
| 当前 | 说明 |
|
||
|------|------|
|
||
| 使用 NoteNavigationDestination.player,传 courseId, nodeId, initialScrollIndex | 不传 navigationPath、isLastNode、courseTitle;当前 init 中这些为可选且带默认值。 |
|
||
|
||
**零影响条件 5**:
|
||
保持 VerticalScreenPlayerView 的 init 中 **navigationPath、isLastNode、courseTitle 为可选且默认 nil**。这样 NoteTreeView / NoteListView **无需任何修改**,行为不变(不传 path 则不会 push 完成页;统一分页下完成页在 TabView 内,笔记流仍不涉及 .completion 路由)。
|
||
|
||
---
|
||
|
||
### 2.6 其他引用
|
||
|
||
| 引用 | 影响 |
|
||
|------|------|
|
||
| **ContentBlockBuilder** 注释「与 VerticalScreenPlayerView 保持一致」 | 仅注释,不依赖接口或类型。 |
|
||
| **CourseNavigation** 枚举 | 保留 .map / .player / .completion 三 case,见零影响条件 2。 |
|
||
| **MapView** 中 append CourseNavigation.player(courseId, nodeId, isLastNode, courseTitle) | 不依赖播放器 init 是否接收这些参数,只要枚举不变即无影响。 |
|
||
|
||
---
|
||
|
||
## 三、零影响检查清单(应用前必达)
|
||
|
||
若要在「其他页面、其他功能、其他逻辑一点也不受影响」的前提下应用统一分页方案,需同时满足:
|
||
|
||
| # | 条件 | 说明 |
|
||
|---|------|------|
|
||
| 1 | VerticalScreenPlayerView **init 保持 6 参**(courseId, nodeId, initialScrollIndex, navigationPath?, isLastNode?, courseTitle?) | GrowthView / ProfileView / DiscoveryView 不改动 |
|
||
| 2 | 保留 **CourseNavigation.completion** 及三处 **.completion** 的 navigationDestination | 深链/其他入口 push .completion 仍可用 |
|
||
| 3 | **CompletionView 保留三参**(courseId, courseTitle, completedLessonCount) | 与现有 destination 及枚举一致 |
|
||
| 4 | 播放器内对 **LessonPageView** 仍传 **courseTitle**、**navigationPath**(用播放器自身属性) | LessonPageView 行为不变 |
|
||
| 5 | 播放器内保留 **loadError、toast、hideTabBar/showTabBar、handleBack(path 优先)** 及现有布局/进度逻辑 | 错误、提示、TabBar、返回、进度均不受影响 |
|
||
| 6 | **NoteTreeView / NoteListView** 不修改 | 依赖 init 可选参数,已满足则零影响 |
|
||
|
||
---
|
||
|
||
## 四、总结
|
||
|
||
- **按方案原文直接替换**(init 改为 3 参、CompletionView 去掉 courseId、播放器去掉 path/错误/toast/tabBar 等):**会**影响 GrowthView / ProfileView / DiscoveryView(编译或行为)、以及错误态/toast/TabBar/返回等逻辑。
|
||
- **在满足上述 6 条零影响条件的前提下**,再引入 allItems + PlayerItem、TabView 最后一页渲染 CompletionView、不再 push .completion,则:
|
||
- 其他页面(含三 Tab、MapView、笔记流)**无需改**;
|
||
- 其他功能(错误、toast、TabBar、返回、进度、深链 .completion)**不受影响**;
|
||
- 其他逻辑(完课统计、LessonPageView、CourseNavigation)**保持一致**。
|
||
|
||
**未对仓库内任何文件进行修改。**
|