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