7.9 KiB
完成页「统一分页架构」— 零影响审查报告(禁止应用)
审查目标:若应用此方案,确保其他页面、其他功能、其他逻辑一点也不受影响。
结论:仅审查,不修改仓库内任何文件。下文列出所有受影响点及达成「零影响」的必要条件。
一、方案会动到的文件与接口
| 文件 | 方案中的改动 |
|---|---|
| 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)保持一致。
未对仓库内任何文件进行修改。