6.3 KiB
6.3 KiB
完成页「统一分页 + 零影响」代码审查报告(禁止应用)
审查对象:严格遵守 6 条零影响条件的 CompletionView + VerticalScreenPlayerView 完整代码(内部重构,外部兼容)。
结论:仅审查,不修改仓库内任何文件。对照 6 条逐项核对,并列出需补全/修正的细节以达「绝对零影响」。
一、6 条零影响条件对照
| # | 条件 | 本版代码 | 结论 |
|---|---|---|---|
| 1 | VerticalScreenPlayerView init 保持 6 参 | init(courseId, nodeId, initialScrollIndex, navigationPath?, isLastNode?, courseTitle?) 完整保留 |
✅ 满足 |
| 2 | 保留 CourseNavigation.completion 及三处 destination | 未改枚举与三 Tab;CompletionView 三参,destination 调用不变 | ✅ 满足 |
| 3 | CompletionView 保留三参 | courseId, courseTitle, completedLessonCount 均有 |
✅ 满足 |
| 4 | LessonPageView 仍传 courseTitle、navigationPath | 传 courseTitle: mapData?.courseTitle、navigationPath: navigationPath |
⚠️ 见下 4.1 |
| 5 | 保留 loadError、toast、hideTabBar/showTabBar、handleBack(path 优先) | 错误态、toast、tabBar、handleBack 均保留 | ⚠️ 见下 5.1、5.2 |
| 6 | NoteTreeView / NoteListView 不修改 | init 未改,笔记流仍只传 3 参 | ✅ 满足 |
二、CompletionView 审查
- 接口:courseId / courseTitle / completedLessonCount 全保留,与 CourseNavigation.completion 及三处 destination 一致。✅
- 内部:纯 UI(粉紫勋章)+ navStore.switchToGrowthTab(),无 navigationPath、无侧滑 Hack。✅
- 视觉:顶部「已完成」、底部「回到我的内容」淡蓝、无返回按钮。与需求一致。✅
- 无遗漏:无依赖 dismiss、无依赖 path,作为 TabView 一页或作为 push 目标均可。✅
结论:CompletionView 满足零影响条件,无需改动。
三、VerticalScreenPlayerView 审查
3.1 已满足项
- Init:6 参完整,与现有 GrowthView / ProfileView / DiscoveryView / NoteTreeView / NoteListView 调用兼容。✅
- PlayerItem:lesson(MapNode) + completion,id 分别为 node.id 与
"COMPLETION_PAGE"。✅ - loadMapData:realNodes + append(.completion),allItems 构造正确;loadError = nil 与 catch 内 set loadError 均有。✅
- currentPositionProgress:仅用 lesson 项计算,完结页时返回 1.0,逻辑正确。✅
- handleBack:path 非空则 removeLast(),否则 dismiss()。✅
- hideTabBar / showTabBar / toast:保留。✅
- LessonPageView:传 courseId, nodeId, currentGlobalNodeId, initialScrollIndex, headerConfig, courseTitle, navigationPath。✅
- CompletionView(内嵌):传 courseId, courseTitle, completedLessonCount。✅
3.2 需补全或修正的细节(达「绝对零影响」)
4.1 LessonPageView 的 courseTitle 传参(对应条件 4)
- 本版:
courseTitle: mapData?.courseTitle(仅用加载后的 mapData)。 - 当前实现:
courseTitle: courseTitle(用调用方传入的 courseTitle,如 MapView 的 data.courseTitle)。 - 差异:加载完成前 mapData 为 nil,本版会传 nil;当前实现一进入就有值。若希望与现有行为完全一致,建议传
courseTitle ?? mapData?.courseTitle,优先用传入值,再回退到加载结果。
5.1 loadMapData 失败时的 Toast(对应条件 5)
- 当前实现:catch 中除 set loadError 外,还调用
showToastMessage("加载失败")。 - 本版:catch 中只 set loadError,未调用 showToastMessage。
- 建议:在 catch 的 MainActor.run 内补上
showToastMessage("加载失败"),与现有体验一致。
5.2 isFirstNodeInChapter 实现(对应条件 5:逻辑不变)
- 当前实现:在 chapter 内取
validNodes = chapter.nodes.filter(locked).sorted { $0.order < $1.order },再validNodes.first?.id == nodeId(即按 order 排序后的「第一章第一节」)。 - 本版:
chapter.nodes.filter(locked).first,未按 order 排序,相当于用数组顺序的「第一个」。 - 风险:若后端/本地 chapter.nodes 顺序与 order 不一致,本版可能与当前表现不同。
- 建议:与当前保持一致,使用
validNodes = chapter.nodes.filter { $0.status != .locked }.sorted { $0.order < $1.order },再validNodes.first?.id == nodeId。
5.3 其他可选一致性(非必须)
- 空状态文案:当前为「暂无内容」+「该课程还没有可用的学习内容」;本版为「暂无内容」+ 单行。若需完全一致可补副标题,否则可保留本版简化。
- GeometryReader:当前 body 最外层包了一层 GeometryReader;本版未包。若当前无依赖 geo 的布局,可不再加;若有,需保留或等价处理。
- Import:hideTabBar/showTabBar 使用 UIApplication,需 import UIKit。若文件当前已含 UIKit 则无需改;否则需补。
四、对外暴露与调用方
| 调用方 / 入口 | 是否需改 | 说明 |
|---|---|---|
| GrowthView / ProfileView / DiscoveryView(.player / .completion) | 否 | init 与 CompletionView 三参未变 |
| MapView(append .player) | 否 | 枚举与参数不变 |
| NoteTreeView / NoteListView(.player) | 否 | 仍只传 3 参,可选参默认 nil |
| CourseNavigation 枚举 | 否 | 未改 |
| navigationDestination(.completion) | 否 | 仍用 CompletionView(courseId, courseTitle, completedLessonCount) |
结论:在补全 4.1、5.1、5.2 后,对外接口与所有调用方均可保持零改动、零行为差异。
五、审查结论汇总
| 项目 | 结论 |
|---|---|
| 6 条零影响 | 条件 1、2、3、6 已满足;条件 4、5 在按 3.2 补全后可达「绝对零影响」。 |
| CompletionView | 可直接采用,无需改。 |
| VerticalScreenPlayerView | 建议补全:① LessonPageView 传 courseTitle ?? mapData?.courseTitle;② loadMapData 失败时 showToastMessage("加载失败");③ isFirstNodeInChapter 按 order 排序取 first。 |
| 其他页面 / 功能 / 逻辑 | 在以上补全前提下,其他页面、其他功能、其他逻辑均不受影响。 |
未对仓库内任何文件进行修改。