从首页入口、功能路由、拍照与录音保存,到记录页和统计页的真实数据回流,一次读清。

图1|第三章封面:把首页、拍照、录音与记录闭环放在同一张图里。
|
栏目 |
内容 |
|
章节定位 |
先把 `Index.ets`、`Routes.ets`、`WorkClockService` 和四个功能页串成一条真实数据链。 |
|
核心文件 |
`Index.ets`、`Routes.ets`、`WorkClockService.ets`、`FeatureCapturePage.ets`、`FeatureVoicePage.ets`。 |
|
读完收获 |
知道首页为什么会刷新、拍照和录音如何回写记录、统计页为什么能直接看真实数据。 |
|
本章看点:这一章不再拆单个页面效果,而是把首页、路由、拍照、录音、记录和统计放到同一条闭环里。你会看到 `Index.ets` 如何在页面可见时重新读服务层数据,`Routes.ets` 如何把功能入口统一到页面名,`WorkClockService` 又如何把保存后的 `WorkRecord` 重新喂回记录页和统计页。 |
一、首页不是静态页,而是数据入口
留痕的首页不是单纯展示几张卡片,它负责把本地状态、服务层数据和页面切换串起来。只要记录发生变化,首页就要重新把概览、最近记录和功能入口刷新出来,这样用户从首页返回时看到的才是最新结果。
|
aboutToAppear(): void { onPageShow(): void { private handleWorkClockVersionChanged(): void { private refreshAllData(resetCalendarDate: boolean): void { this.overview = this.service.getOverviewSnapshot(); |
|
触发点 |
读到的数据 |
页面效果 |
|
aboutToAppear() |
首次进入时读取 `MAIN_TAB` 和首页数据 |
首页初始化为上次停留的模块 |
|
onPageShow() |
页面再次可见时重读服务层数据 |
拍照或录音后返回首页,内容立即更新 |
|
handleWorkClockVersionChanged() |
`WORKCLOCK_VERSION` 变化时重新刷新 |
记录保存后,首页、记录页和统计页一起回流 |
首页的关键,不是把数据写死在页面里,而是把“页面可见”和“数据可读”绑在一起。这样一来,用户在拍照页、录音页或者记录页完成一次保存后,回到首页看到的永远都是同一份真实数据。
二、Tab 与路由把页面串成一条线
底部 Tab 只是表面上的五个按钮,实际作用是把首页、记录、相机、统计和我的页面统一到同一条导航链里。`AppTabs` 提供稳定的内部 key,`Routes` 负责把功能入口映射到具体页面,这样首页上的功能卡片和底部栏就不会各写各的路径。
|
export class AppTabs { |
|
export class Routes { static resolveFeatureRoute(featureId: string): string | undefined { static resolveSettingRoute(settingId: string): string | undefined { |

图2|源码闭环图:从首页入口到功能页,再回到服务层和数据层。
|
入口 key |
对应页面 |
典型场景 |
|
capture |
FeatureCapturePage |
现场拍照并生成带水印的照片 |
|
voice |
FeatureVoicePage |
现场录音并保存成记录 |
|
record |
FeatureRecordPage |
查看全部记录、图片和录音 |
|
stats |
FeatureStatsPage |
按时间范围查看统计结果 |
|
setting-record |
FeatureRecordPage |
从设置页回看记录 |
|
setting-voice |
FeatureVoicePage |
从设置页回看录音 |
三、拍照和录音怎样把现场数据写回记录
拍照页和录音页真正重要的地方,不是“能不能拍”或者“能不能录”,而是保存完成以后有没有把现场信息变成 `WorkRecord`。只要这一步做对了,记录页、统计页和首页的刷新就都有了同一个数据源。
|
private buildWatermarkedRecord(mediaUri: string): WorkRecord { const finalUri: string = await this.createWatermarkedPhoto(abilityContext); |
|
private saveVoiceRecord(): void { const record: WorkRecord = { this.service.saveRecord(record); |
|
字段 |
来自哪里 |
被谁使用 |
|
title |
拍照页标题 / 录音页标题 |
记录页卡片、首页最近记录 |
|
projectName |
项目分布输入 |
项目页、统计页、首页概览 |
|
captureDate / captureTime |
拍照或录音保存时自动写入 |
记录页、日历页、统计范围 |
|
location |
拍照/录音时自动采集 |
水印、记录页、首页最新地点 |
|
mediaUri |
拍照后生成的本地图片路径 |
记录页图片预览 |
|
audioUri |
录音后生成的本地音频路径 |
记录页播放按钮 |
这也是为什么拍照和录音页会把时间、地点和项目分布自动写进记录:它们不是额外的装饰字段,而是整个闭环里的关键索引。少了这些字段,记录页和统计页就只能看见一堆孤立文件,无法回到业务语义。
四、记录页和统计页为什么能直接看真实数据
记录页和统计页看起来像两个不同的功能页,但它们最终都依赖同一份 `WorkRecord`。记录页负责筛选、播放和预览,统计页负责按时间范围重新计算指标、趋势和项目分布,所以一条记录写进去后,两个页面都会跟着变。

图3|从首页到记录页:保存、展示、统计、刷新形成一条完整回路。
|
private getFilteredRecords(): WorkRecord[] { private getAttachmentStatus(record: WorkRecord): string { |
|
private refreshStats(range: string): void { |
|
页面 |
调用的服务 |
用户看到的结果 |
|
FeatureRecordPage |
`getRecords()` / `getFilteredRecords()` |
查看全部、拍照、录音三种列表,并支持预览和播放 |
|
FeatureStatsPage |
`getStatsSummary(range)` |
看到按周、按月、按年重算后的统计卡片和趋势图 |
|
Index.ets |
`getOverviewSnapshot()` / `getRecentRecords()` |
首页概览和最近记录跟着保存结果同步变化 |
五、本章小结
这一章其实只做了一件事:把应用里最容易被误解的部分拆开看清楚。首页不是静态首页,Tab 不是单纯的按钮组,拍照和录音也不是单独的孤岛页面。它们最终都要回到同一套服务层和同一批 `WorkRecord` 数据上。
下一章会继续往前看 `EntryAbility` 和启动流程,看看应用是如何在进入首页之前先把服务、存储和首屏数据准备好的。那一段看清以后,整个“留痕”项目的入口链路就完整了。
今日实操
|
动作 |
检查点 |
|
打开 `Index.ets` |
找到 `refreshAllData()`、`switchTab()` 和 `openFeature()`,确认首页为什么会刷新。 |
|
打开 `Routes.ets` |
把 `featureId` 和页面路径一一对应起来,确认入口映射没有散落。 |
|
打开 `WorkClockService.ets` |
找到 `saveRecord()`、`persist()` 和 `getStatsSummary()`,理解数据怎样回流到首页、记录页和统计页。 |



