4.8 KiB
4.8 KiB
完成页「最终交付 · 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 内保留与当前一致的写法:
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/传参)不变。✅ |
| 其他页面 / 功能 | 接口与调用方式未改,其他页面、其他功能不受影响。✅ |
未对仓库内任何文件进行修改。