98 lines
6.3 KiB
Markdown
98 lines
6.3 KiB
Markdown
|
|
# 完成页「统一分页 + 零影响」代码审查报告(禁止应用)
|
|||
|
|
|
|||
|
|
**审查对象**:严格遵守 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。 |
|
|||
|
|
| **其他页面 / 功能 / 逻辑** | 在以上补全前提下,其他页面、其他功能、其他逻辑均不受影响。 |
|
|||
|
|
|
|||
|
|
**未对仓库内任何文件进行修改。**
|