77 lines
4.8 KiB
Markdown
77 lines
4.8 KiB
Markdown
# 完成页「最终交付 · UI 聚焦」审查报告(禁止应用)
|
||
|
||
**审查对象**:仅实现完结页 UI + 统一分页,不碰数据排序与 isFirstNodeInChapter 判定逻辑的最终交付代码。
|
||
**结论**:仅审查,不修改仓库内任何文件。核对「仅 UI、其他不变」及与当前仓库逻辑的一致性。
|
||
|
||
---
|
||
|
||
## 一、交付方承诺的「不变」项核对
|
||
|
||
| 承诺 | 本版代码 | 与当前仓库对比 | 结论 |
|
||
|------|----------|----------------|------|
|
||
| **loadMapData 不全局重排** | `realNodes = data.chapters.flatMap { $0.nodes }.filter { $0.status != .locked }`,无 `.sorted` | 当前:`allCourseNodes = data.chapters.flatMap { $0.nodes }.filter { $0.status != .locked }`,无排序 | ✅ 一致 |
|
||
| **isFirstNodeInChapter 保持原有逻辑** | `chapter.nodes.filter({ $0.status != .locked }).first`,无排序 | 当前:`validNodes = chapter.nodes.filter(...).sorted { $0.order < $1.order }`,再 `validNodes.first?.id == nodeId` | ⚠️ 见下 1.1 |
|
||
| **仅统一分页 + 完结页 UI** | 数据源改为 allItems (PlayerItem),最后一页渲染 CompletionView;其余 init/错误/toast/tabBar/handleBack 等保留 | — | ✅ 符合 |
|
||
|
||
### 1.1 isFirstNodeInChapter 与「严格保持原有逻辑」的差异
|
||
|
||
- **当前仓库**:在 chapter 内先取 `validNodes = chapter.nodes.filter { $0.status != .locked }.sorted { $0.order < $1.order }`,再判断 `validNodes.first?.id == nodeId`,即「**按 order 排序后的第一章第一节**」。
|
||
- **本版交付**:`chapter.nodes.filter({ $0.status != .locked }).first`,即「**数组顺序下的第一个未锁定节点**」,无 `.sorted { $0.order < $1.order }`。
|
||
|
||
因此:本版 **改动了** isFirstNodeInChapter 的判定规则(从「按 order 的首节」变为「按数组顺序的首节」)。若后端或产品依赖「按 order 的首节」显示章节标题,本版可能与现有行为不一致。
|
||
|
||
**建议**:若需 **严格保持原有判定规则**,应在 isFirstNodeInChapter 内保留与当前一致的写法:
|
||
|
||
```swift
|
||
if chapter.nodes.contains(where: { $0.id == nodeId }) {
|
||
let validNodes = chapter.nodes
|
||
.filter { $0.status != .locked }
|
||
.sorted { $0.order < $1.order }
|
||
return validNodes.first?.id == nodeId
|
||
}
|
||
```
|
||
|
||
其余(loadMapData 不排序、仅统一分页与完结页 UI)本版已满足。
|
||
|
||
---
|
||
|
||
## 二、CompletionView 审查
|
||
|
||
- **职责**:仅展示(粉紫勋章、共完成 N 节、底部「回到我的内容」)+ `navStore.switchToGrowthTab()`,无 navigationPath、无数据处理。✅
|
||
- **接口**:courseId / courseTitle / completedLessonCount 三参保留,与 CourseNavigation.completion 及三处 destination 兼容。✅
|
||
- **替换方式**:全量替换 `Views/CompletionView.swift` 即可。✅
|
||
|
||
**结论**:CompletionView 符合「仅完结页 UI、其他不变」。
|
||
|
||
---
|
||
|
||
## 三、VerticalScreenPlayerView 审查
|
||
|
||
### 3.1 已符合「仅 UI + 统一分页、其他不变」
|
||
|
||
- **Init**:6 参未改,外部调用零影响。✅
|
||
- **loadMapData**:flatMap + filter,无 sort;allItems = realNodes.map(.lesson) + append(.completion)。与当前「章节顺序 + 数组顺序」一致。✅
|
||
- **错误态 / toast / hideTabBar / showTabBar / handleBack(path 优先)**:均保留。✅
|
||
- **LessonPageView**:传 `self.courseTitle ?? mapData?.courseTitle`、navigationPath;headerConfig 仍用 isFirstNodeInChapter / getChapterTitle。✅
|
||
- **CompletionView(内嵌)**:courseId、courseTitle、completedLessonCount。✅
|
||
- **currentPositionProgress**:仅按 lesson 项计算,忽略完结页。✅
|
||
- **合并范围**:说明中已注明仅替换主视图 Struct,保留 HeaderConfig、CourseProgressNavBar、LessonPageView 等。✅
|
||
|
||
### 3.2 唯一需确认处:isFirstNodeInChapter
|
||
|
||
如上 1.1:当前仓库使用 **sorted { $0.order < $1.order }** 再取 first;本版使用 **.filter().first**(无排序)。若要求「严格保持原有逻辑、不修改判定规则」,需在替换时保留当前 isFirstNodeInChapter 实现(含 .sorted { $0.order < $1.order })。
|
||
|
||
---
|
||
|
||
## 四、审查结论汇总
|
||
|
||
| 项目 | 结论 |
|
||
|------|------|
|
||
| **loadMapData** | 无全局排序,与当前「章节顺序 + 数组顺序」一致。✅ |
|
||
| **isFirstNodeInChapter** | 本版为「数组顺序首节点」;当前为「按 order 排序后首节点」。若需与现有行为完全一致,需保留当前的 .sorted { $0.order < $1.order } 写法。⚠️ |
|
||
| **CompletionView** | 仅 UI + 退出导航,可全量替换。✅ |
|
||
| **VerticalScreenPlayerView** | 仅替换主视图 Struct,保留同文件其余类型;其他逻辑(错误/toast/tabBar/handleBack/传参)不变。✅ |
|
||
| **其他页面 / 功能** | 接口与调用方式未改,其他页面、其他功能不受影响。✅ |
|
||
|
||
**未对仓库内任何文件进行修改。**
|