练习结果页不是一个简单的“显示分数”页面。题库应用里用户完成一次限时挑战后需要马上知道三件事这次表现如何、错在哪里、下一步怎么继续练。结果页如果只给一个大分数用户无法定位薄弱题如果只列题目细节页面又会变得冗长如果结果页直接重新判题还容易和练习页的记录口径不一致。笔下生辉的ExamResultPage.ets采用了更稳的闭环PracticePage在考试结束时把bankId、score、total、correct、durationSec和records作为路由参数传给结果页结果页只负责接收、展示、保存历史并提供两个继续动作进入错题解析或重新开始一次限时挑战。这样结果页不重新判题避免统计口径分裂。本文基于本地工程D:\huawei\one17-11的真实源码编写包名标记为com.jiaweikang.one17。这里需要明确边界队列标题里的“薄弱项”在当前源码中不是智能诊断算法而是由答题网格、错题数量和“错题解析”入口承接源码没有按知识点聚类、AI 诊断或自动生成补弱建议。本文重点解决五个工程问题如何从路由参数恢复考试结果避免结果页重复判题。如何展示分数、正确率、用时和错题数。如何用答题网格让用户快速看到正确、错误和未答状态。如何保存挑战历史并避免重复写入。如何提供“错题解析”和“再来一次”两个继续学习动作。一、结果页接收的是结算结果不是原始题库结果页的输入参数很明确。interface ExamResultParams { bankId: string score: number total: number correct: number durationSec: number records: string }这些参数来自PracticePage。考试结束时练习页已经完成了判题、计分和答题记录序列化。router.replaceUrl({ url: pages/ExamResultPage, params: { bankId: this.bankId, score: score, total: this.questions.length, correct: correctCount, durationSec: this.timerSec, records: JSON.stringify(this.records), } })这条边界很重要结果页不是判题页。它不需要重新读取题目并再次计算对错只要按照练习页传来的结果展示。这样能避免两处代码分别算分导致不一致。二、aboutToAppear 负责恢复结果状态ExamResultPage进入时读取路由参数把分数、题量、正确数、耗时和答题记录恢复到页面状态。aboutToAppear(): void { const params router.getParams() as ExamResultParams | undefined if (params) { this.bankId params.bankId this.score params.score this.total params.total this.correct params.correct this.durationSec params.durationSec try { this.records JSON.parse(params.records) as AnswerRecord[] } catch (_) {} } this.rankInfo getRankByScore(this.score) }records是字符串传参因此解析失败时不能让页面崩溃。源码用try/catch兜底失败时保留空数组。结果页仍能显示分数、正确率和用时只是答题网格无法还原每题状态。三、分数等级由 ThemeConstants 统一映射结果页不是直接在页面里写“90 分优秀、75 分良好”。它通过getRankByScore()获取等级、印章和颜色。export function getRankByScore(score: number): RankInfo { if (score 90) return { label: 优秀, stamp: $r(app.media.img_exam_stamp_excellent), color: Colors.ERROR } if (score 75) return { label: 良好, stamp: $r(app.media.img_exam_stamp_good), color: Colors.ACCENT } if (score 60) return { label: 及格, stamp: $r(app.media.img_exam_stamp_pass), color: Colors.SUCCESS } return { label: 继续努力, stamp: $r(app.media.img_exam_stamp_fail), color: Colors.TEXT_HINT } }这样做的收益是视觉规则集中。以后如果产品要调整等级阈值、印章素材或等级颜色不需要改结果页布局只改常量函数。四、核心指标只展示三项结果页的指标区展示正确率、用时和错题数。Row() { this.MetricItem(${Math.round(this.correct / Math.max(this.total, 1) * 100)}%, 正确率) this.MetricDivider() this.MetricItem(this.formatDuration(this.durationSec), 用时) this.MetricDivider() this.MetricItem(${Math.max(this.total - this.correct, 0)}, 错题) }这里有两个防御点Math.max(this.total, 1)避免total0时除零。Math.max(this.total - this.correct, 0)避免异常参数导致错题数为负。指标选择也很克制。分数已经在顶部大卡片展示下面就不再重复堆“总题数、正确数、错误数、排名”等过多数据而是把用户最关心的正确率、耗时和错题数放在一行。五、答题网格承接薄弱项入口当前源码没有知识点维度的薄弱项算法但有答题网格。它可以让用户快速看到哪些题答错、哪些题未答。private makeAnswerGrid(): GridItem[] { const result: GridItem[] [] for (let i 0; i this.total; i) { let state: string empty if (i this.records.length) { state this.records[i].correct ? correct : wrong } result.push({ index: i 1, state }) } return result }颜色映射同样独立成方法。private stateColor(state: string): string { if (state correct) return Colors.SUCCESS if (state wrong) return Colors.ERROR return Colors.DIVIDER }这就是当前源码可复核的“薄弱项”边界结果页通过错题数和红色网格提示薄弱题位真正的逐题复盘交给“错题解析”动作。六、图例降低网格理解成本结果页没有假设用户天然理解颜色含义而是提供图例。Row({ space: 16 }) { this.Legend(Colors.SUCCESS, 正确) this.Legend(Colors.ERROR, 错误) this.Legend(Colors.DIVIDER, 未答) }Legend是一个很小的 Builder。Builder Legend(color: string, label: string) { Row({ space: 4 }) { Column() .width(12) .height(12) .borderRadius(3) .backgroundColor(color) Text(label) .fontSize(Sizes.SMALL_FONT) .fontColor(Colors.TEXT_HINT) } }对结果页来说图例比长文字解释更有效。用户扫一眼就能理解红、绿、灰分别代表什么。七、挑战历史只写一次结果页会把当前考试写入examHistory。State historySaved: boolean false StorageLink(examHistory) examHistory: ExamHistory[] [] if (!this.historySaved this.total 0) { this.examHistory UserDataManager.addExamHistory( this.examHistory, this.bankId, this.score, this.total, this.correct, this.durationSec ) this.historySaved true }historySaved的注释说明了真实问题从错题解析返回结果页时aboutToAppear()可能再次触发。如果不加保护同一场考试会被重复写入历史。持久化方法在UserDataManager中。static addExamHistory(records: ExamHistory[], bankId: string, score: number, total: number, correct: number, durationSec: number): ExamHistory[] { const result: ExamHistory[] [{ bankId, score, total, correct, durationSec, finishedAt: nowStr() }, ...records] UserDataManager.persist(UserDataManager.K_EXAM, result) return result }这里使用新记录前置方便历史列表按最近一次优先展示。八、错题解析动作回到 PracticePage结果页底部有两个动作。第一个是错题解析它不会在结果页内展开所有错题而是跳回PracticePage的wrongAnalysis模式。GreenButton({ text: 错题解析, isOutline: true, onTap: () { router.pushUrl({ url: pages/PracticePage, params: { bankId: this.bankId, mode: wrongAnalysis, records: JSON.stringify(this.records) } }) } })这个动作保持了职责清晰结果页负责总结练习页负责逐题解析。错题解析仍然复用练习页已有的题目展示、选项状态和解析卡片不需要结果页再实现一份类似 UI。九、再来一次使用 replaceUrl第二个动作是重新开始一次考试。GreenButton({ text: 再来一次, onTap: () { router.replaceUrl({ url: pages/PracticePage, params: { bankId: this.bankId, mode: exam } }) } })这里使用replaceUrl()而不是pushUrl()可以避免页面栈里累积多个结果页。用户点击再来一次后新考试成为当前任务返回路径也更干净。十、结果页不是学习统计页源码里还有LearningStatsPage、ExamTab等页面会展示长期统计ExamResultPage只服务于一次考试结束后的闭环。它保存历史但不承担长期趋势分析。这种边界能避免结果页变胖。一次考试结束时用户只需要任务结果页是否负责查看本次分数负责查看正确率、用时、错题数负责查看每题状态负责进入错题解析提供入口再来一次提供入口统计近 5 次均分不负责展示长期题库进度不负责长期数据应该放到统计页或挑战记录页避免结果页成为所有学习数据的堆放区。十一、结果页的安全区处理底部按钮区域使用navigationIndicatorHeightPx计算安全 padding。private bottomSafePadding(): number { return Math.max( Sizes.BOTTOM_NAV_MIN_PADDING, this.getUIContext().px2vp(this.navigationIndicatorHeightPx) ) }底部按钮区使用这个 padding.padding({ left: Sizes.PADDING_LARGE, right: Sizes.PADDING_LARGE, top: 10, bottom: 16 this.bottomSafePadding() })结果页通常在考试结束后出现底部操作是核心路径。如果按钮被系统导航条遮住用户就无法继续解析或重练。这个安全区处理是结果页可用性的一部分。十二、验证清单基于源码可以按下面步骤验证结果页从PracticePage的exam模式交卷确认跳转到ExamResultPage。检查路由参数包含bankId、score、total、correct、durationSec和records。检查顶部大卡片显示分数和对应印章。检查指标区正确率、用时、错题数和传参一致。检查答题网格中正确题、错误题、未答题颜色不同。点击“错题解析”确认进入PracticePage且modewrongAnalysis。点击“再来一次”确认replaceUrl()打开新的exam模式。从错题解析返回结果页确认examHistory不重复写入同一场考试。十三、常见问题排查问题优先检查分数显示正确但网格为空records参数是否为空或 JSON 解析失败正确率异常correct和total是否来自同一轮考试历史记录重复historySaved是否在本页生命周期内保持为 true错题解析没有题records中是否存在correctfalse项再来一次后返回路径混乱是否误用pushUrl()而不是replaceUrl()底部按钮被挡住navigationIndicatorHeightPx和bottomSafePadding()是否生效十四、总结笔下生辉的练习结果页把一次考试的闭环拆得很清楚PracticePage负责判题和传参ExamResultPage负责恢复结果、展示分数、正确率、用时、错题网格、保存挑战历史并提供错题解析和再来一次。它没有把长期统计、智能薄弱项诊断和逐题解析全部塞进结果页而是通过路由把职责交给更合适的页面。对 HarmonyOS 5.0 ArkTS 应用来说这种结构值得复用结果页应以“可信结果展示 后续动作”为核心不重新判题不复制练习页 UI不伪造诊断能力。只要输入参数、结果展示、历史保存和继续学习动作四个边界清楚结果页就能稳定承接练习流程并为后续统计页或错题页留下清晰扩展空间。部分内容由AI辅助生成。