# 完成页导航方案 — 实现后行为与影响说明 说明:实现方案后**会变成什么样**、**会不会影响原有逻辑和展示**、**会不会导致其他地方出问题**。不修改代码,仅作说明。 --- ## 一、实现后会变成什么样 ### 1. 进入完成页(两种方式,二选一或并存) | 方式 | 当前 | 实现后(若保留占位页 + 加手势) | |------|------|----------------------------------| | **左滑到「下一页」** | 最后一节再左滑 → 进入占位页(空白)→ 0.1s 后 push 完成页 | 不变:仍可滑到占位页,再由 `onChange` push | | **在最后一节左滑** | 无 | 新增:在最后一节直接左滑(手势识别)→ 可设为「切到占位页再 push」或「直接 push 完成页」 | 若方案采用「删除占位页、仅手势」: - 最后一节左滑 → 直接 push 完成页,**不再出现空白占位页那一屏**。 - TabView 只有真实课程页,不再有「多一页」的占位。 **展示上**:完成页本身 UI 不变(仍是当前 CompletionView 的 3D 卡片、打字机等);变的只是「怎么进」和「怎么回」。 --- ### 2. 从完成页返回(核心变化) | 操作 | 当前 | 实现后 | |------|------|--------| | **顶部返回 (chevron)** | `dismiss()` → 回到播放器(最后一节或占位页) | 可保持不变:仍 `dismiss()`,回到播放器 | | **底部「继续学习」** | `navStore.switchToGrowthTab()` + `dismiss()` | `handleReturnToMap()`:`navigationPath.removeLast(2)` | **「继续学习」行为对比**: - **当前**: 1. `dismiss()` → 栈 pop 一层,回到 **VerticalScreenPlayerView**(当前 Tab 可能是占位页,`onAppear` 里会切回最后一节)。 2. `switchToGrowthTab()` → 切到「技能」Tab,且 **清空 `growthPath`**(`growthPath = NavigationPath()`)。 3. 用户最终看到的是 **技能 Tab 的根界面(课程列表)**,不是地图。 - **实现后**: 1. `removeLast(2)` → 一次 pop 掉「完成页」和「播放器」。 2. 栈变成:**[MapView]**(或更短,视当前 path 而定)。 3. **不**调用 `switchToGrowthTab()`,所以**不会清空 path**,也**不一定会切 Tab**。 4. 用户最终看到的是 **当前 Tab 下的地图页**(从哪个 Tab 进的,就还在哪个 Tab)。 **总结**: - 实现后,点「继续学习」会**直接回到地图**,且**保留在当前 Tab**(发现 / 技能 / 我的)。 - 当前是**回到课程列表**(且强制在技能 Tab)。这是**产品行为上的明确变化**。 --- ### 3. 三条入口分别会怎样 | 入口 | 当前栈(到完成页时) | 当前「继续学习」后 | 实现后「继续学习」后 | |------|----------------------|--------------------|------------------------| | **技能 Tab** | growthPath: [Map, Player, Completion] | 切到技能 Tab + 清空栈 → **课程列表** | 仍技能 Tab,栈 [Map] → **地图** | | **发现 Tab** | homePath: [Map, Player, Completion] | dismiss 回播放器;switchToGrowthTab 切到技能并清空 → **课程列表** | 仍发现 Tab,栈 [Map] → **地图** | | **我的 Tab** | profilePath: [Map, Player, Completion] | 同上 → **课程列表** | 仍我的 Tab,栈 [Map] → **地图** | --- ## 二、会不会影响原有逻辑和展示 ### 1. 会改变的部分(有意为之) - **底部按钮语义**:「继续学习」从「回课程列表(并切到技能)」变为「回地图(且留在当前 Tab)」。 - **是否清空栈**:不再在完成页里清空 `growthPath`,所以不会出现「从完成页一点就回到空白课程列表」。 - **是否切 Tab**:不再强制切到技能 Tab;用户会留在发现 / 技能 / 我的中的当前 Tab。 若产品期望就是「完成课后回到地图、留在当前 Tab」,则与方案一致;若期望是「回到课程列表、且一定在技能 Tab」,则与**当前**一致,与**实现后**不一致,需要产品确认。 ### 2. 可保持不变的部分 - **完成页 UI**:3D 翻转卡片、打字机、顶部返回、按钮样式等都可以不改。 - **顶部返回**:继续用 `dismiss()`,仍回到播放器,逻辑和展示都不变。 - **进入完成页的方式**:若保留占位页,现有「滑到占位页再 push」仍可用;只是多了一种「最后一节左滑」的触发方式(若加手势)。 ### 3. 依赖「当前行为」的地方 - **NavigationStore.switchToGrowthTab()**:当前只在 CompletionView 的「继续学习」里被调用。实现后 CompletionView 不再调用它,**不会影响其他使用处**(因为别处没有用这个方法)。 - **growthPath 被清空**:当前只有「完成页点继续学习」会清空 growthPath。若没有「从完成页返回后依赖 growthPath 为空」的逻辑,则实现后**不会破坏其它功能**。 --- ## 三、会不会导致其他地方出问题 ### 1. 三个 Tab 的 navigationDestination(必须改) - **GrowthView / ProfileView / DiscoveryView** 里 `.completion` 分支都要给 CompletionView 传 **Binding**: `navigationPath: $navStore.growthPath` / `$navStore.profilePath` / `$navStore.homePath`。 - **漏传或错传**: - 漏传 → 编译不通过(CompletionView 多了必选参数)。 - 错传(例如发现进的完成页却传了 `growthPath`)→ 点「继续学习」会 pop 错栈,可能白屏或回到错误 Tab。 - **结论**:三处都必须改,且必须传**当前栈对应的 path**,否则会出问题。 ### 2. 笔记流(NoteTreeView / NoteListView)— 不受影响 - 笔记流用的是 **NoteNavigationDestination.player**,打开的是 **VerticalScreenPlayerView**,且**没有传 navigationPath**(或传的是笔记自己的 path)。 - 在这些地方打开的播放器里,`navigationPath` 为 nil,现有逻辑里 `onChange` 里会 `guard let path = navigationPath else { return }`,**不会 push CourseNavigation.completion**。 - 因此:从笔记进的播放器,**不会**出现「滑到完成页」的 push(除非你后续在笔记流里也接 Course 的 completion)。 - **结论**:实现完成页方案**不会影响笔记流**,也不会从笔记流误进完成页。 ### 3. 从完成页返回时栈深度不足 - 正常流程栈至少为 [Map, Player, Completion],`removeLast(2)` 安全。 - 若将来有「深链、通知、分享」等直接打开 CompletionView,栈可能只有 [Completion],此时 `path.count >= 2` 为假,应走兜底 `dismiss()`。 - 方案里已建议保留 `if navigationPath.count >= 2 { removeLast(2) } else { dismiss() }`,**不会因为栈浅而崩溃**,最多退回单层 pop。 ### 4. 手势与 TabView 滑动 - 在最后一节加「左滑进完成页」时,与 TabView 自带的左滑翻页**共用同一方向**,可能冲突(例如:想翻页却触发了完成页,或想进完成页却只翻到占位页)。 - 若用「边缘左滑」或阈值(如 -60pt)、防抖,可减轻误触;需在真机多测。 - **结论**:可能带来**体验上的小问题**(误触/难触),不是逻辑错误,可通过手势参数和测试收敛。 ### 5. 其他使用 VerticalScreenPlayerView / CompletionView 的地方 - **VerticalScreenPlayerView**:除 GrowthView / ProfileView / DiscoveryView 外,仅在 **NoteTreeView / NoteListView** 出现,且走的是 NoteNavigationDestination,不涉及 CourseNavigation.completion,**不受影响**。 - **CompletionView**:只在这三个 Tab 的 `navigationDestination(for: CourseNavigation.self)` 的 `.completion` 分支出现,**没有其它调用点**。 - **结论**:只要三处传参正确,**不会导致其它页面逻辑错误**。 --- ## 四、简要结论表 | 问题 | 结论 | |------|------| | 实现后会变成什么样? | 进入完成页可增加「最后一节左滑」触发;「继续学习」从「回课程列表 + 切技能 Tab」变为「直接回地图 + 保留当前 Tab」;可选去掉占位页。 | | 会不会影响原有逻辑和展示? | 会:底部按钮语义和最终停留页(地图 vs 课程列表)、是否清栈/切 Tab 会变;顶部返回和完成页 UI 可保持不变。 | | 会不会导致其他地方出问题? | 三处传 Binding 必须正确,否则会 pop 错栈;笔记流、其它使用点不受影响;栈浅时用 dismiss 兜底;手势可能与 TabView 滑动冲突,需真机调参。 | **建议**:实现前与产品确认「继续学习」的预期是「回地图」还是「回课程列表」;若确认为回地图且保留当前 Tab,再按方案改并保证三处 path 传参一致。