欢迎光临
我们一直在努力

首页到记录页:数据闭环全解析

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

图1|第三章封面:把首页、拍照、录音与记录闭环放在同一张图里。

栏目

内容

章节定位

先把 `Index.ets`、`Routes.ets`、`WorkClockService` 和四个功能页串成一条真实数据链。

核心文件

`Index.ets`、`Routes.ets`、`WorkClockService.ets`、`FeatureCapturePage.ets`、`FeatureVoicePage.ets`。

读完收获

知道首页为什么会刷新、拍照和录音如何回写记录、统计页为什么能直接看真实数据。

本章看点:这一章不再拆单个页面效果,而是把首页、路由、拍照、录音、记录和统计放到同一条闭环里。你会看到 `Index.ets` 如何在页面可见时重新读服务层数据,`Routes.ets` 如何把功能入口统一到页面名,`WorkClockService` 又如何把保存后的 `WorkRecord` 重新喂回记录页和统计页。

一、首页不是静态页,而是数据入口

留痕的首页不是单纯展示几张卡片,它负责把本地状态、服务层数据和页面切换串起来。只要记录发生变化,首页就要重新把概览、最近记录和功能入口刷新出来,这样用户从首页返回时看到的才是最新结果。

aboutToAppear(): void {
  this.refreshAllData(true);
}

onPageShow(): void {
  this.refreshAllData(false);
}

private handleWorkClockVersionChanged(): void {
  this.refreshAllData(false);
}

private refreshAllData(resetCalendarDate: boolean): void {
  const storedTab: string | undefined = AppStorage.get(AppStorageKeys.MAIN_TAB) as string | undefined;
  if (storedTab && storedTab.length > 0) {
    this.currentTab = storedTab;
  }

  this.overview = this.service.getOverviewSnapshot();
  this.actions = this.service.getHomeActions();
  this.records = this.service.getRecords();
  this.recentRecords = this.service.getRecentRecords();
}

触发点

读到的数据

页面效果

aboutToAppear()

首次进入时读取 `MAIN_TAB` 和首页数据

首页初始化为上次停留的模块

onPageShow()

页面再次可见时重读服务层数据

拍照或录音后返回首页,内容立即更新

handleWorkClockVersionChanged()

`WORKCLOCK_VERSION` 变化时重新刷新

记录保存后,首页、记录页和统计页一起回流

首页的关键,不是把数据写死在页面里,而是把“页面可见”和“数据可读”绑在一起。这样一来,用户在拍照页、录音页或者记录页完成一次保存后,回到首页看到的永远都是同一份真实数据。

二、Tab 与路由把页面串成一条线

底部 Tab 只是表面上的五个按钮,实际作用是把首页、记录、相机、统计和我的页面统一到同一条导航链里。`AppTabs` 提供稳定的内部 key,`Routes` 负责把功能入口映射到具体页面,这样首页上的功能卡片和底部栏就不会各写各的路径。

export class AppTabs {
  static readonly HOME: string = 'home';
  static readonly RECORDS: string = 'records';
  static readonly CAMERA: string = 'camera';
  static readonly STATS: string = 'stats';
  static readonly PROFILE: string = 'profile';
}

export class Routes {
  static readonly FEATURE_CAPTURE: string = 'pages/FeatureCapturePage';
  static readonly FEATURE_VOICE: string = 'pages/FeatureVoicePage';
  static readonly FEATURE_RECORD: string = 'pages/FeatureRecordPage';
  static readonly FEATURE_STATS: string = 'pages/FeatureStatsPage';

  static resolveFeatureRoute(featureId: string): string | undefined {
    switch (featureId) {
      case 'capture':
        return Routes.FEATURE_CAPTURE;
      case 'voice':
        return Routes.FEATURE_VOICE;
      case 'record':
        return Routes.FEATURE_RECORD;
      case 'stats':
        return Routes.FEATURE_STATS;
      default:
        return undefined;
    }
  }

  static resolveSettingRoute(settingId: string): string | undefined {
    switch (settingId) {
      case 'setting-record':
        return Routes.FEATURE_RECORD;
      case 'setting-voice':
        return Routes.FEATURE_VOICE;
      default:
        return undefined;
    }
  }
}

图2|源码闭环图:从首页入口到功能页,再回到服务层和数据层。

入口 key

对应页面

典型场景

capture

FeatureCapturePage

现场拍照并生成带水印的照片

voice

FeatureVoicePage

现场录音并保存成记录

record

FeatureRecordPage

查看全部记录、图片和录音

stats

FeatureStatsPage

按时间范围查看统计结果

setting-record

FeatureRecordPage

从设置页回看记录

setting-voice

FeatureVoicePage

从设置页回看录音

三、拍照和录音怎样把现场数据写回记录

拍照页和录音页真正重要的地方,不是“能不能拍”或者“能不能录”,而是保存完成以后有没有把现场信息变成 `WorkRecord`。只要这一步做对了,记录页、统计页和首页的刷新就都有了同一个数据源。

private buildWatermarkedRecord(mediaUri: string): WorkRecord {
  return {
    id: `capture-${Date.now()}`,
    title: this.getWatermarkTitle(),
    projectName: this.getSelectedProject(),
    captureDate: this.latestCaptureDate,
    captureTime: this.latestCaptureTime,
    durationLabel: '00:00',
    durationMinutes: 0,
    location: this.getWatermarkLocation(),
    weather: '–',
    temperature: '–',
    note: this.getWatermarkNote(),
    category: '拍照记录',
    mediaUri
  };
}

const finalUri: string = await this.createWatermarkedPhoto(abilityContext);
await this.savePhotoToSystemAlbum(abilityContext, finalUri);
this.service.saveRecord(this.buildWatermarkedRecord(finalUri));

private saveVoiceRecord(): void {
  if (this.hasActiveRecording || this.isPreparing) {
    this.showToast('请先停止录音再保存');
    return;
  }

  const record: WorkRecord = {
    id: `voice-${Date.now()}`,
    title: this.getVoiceTitle(),
    projectName: this.getVoiceProjectName(),
    captureDate: this.getVoiceCaptureDate(new Date()),
    captureTime: this.getVoiceCaptureTime(new Date()),
    durationLabel: this.formatDuration(this.elapsedSeconds),
    durationMinutes: this.elapsedSeconds,
    location: this.getVoiceLocation(),
    weather: '–',
    temperature: '–',
    note: this.getVoiceNote(),
    category: '录音记录',
    audioUri: this.currentAudioUri
  };

  this.service.saveRecord(record);
}

字段

来自哪里

被谁使用

title

拍照页标题 / 录音页标题

记录页卡片、首页最近记录

projectName

项目分布输入

项目页、统计页、首页概览

captureDate / captureTime

拍照或录音保存时自动写入

记录页、日历页、统计范围

location

拍照/录音时自动采集

水印、记录页、首页最新地点

mediaUri

拍照后生成的本地图片路径

记录页图片预览

audioUri

录音后生成的本地音频路径

记录页播放按钮

这也是为什么拍照和录音页会把时间、地点和项目分布自动写进记录:它们不是额外的装饰字段,而是整个闭环里的关键索引。少了这些字段,记录页和统计页就只能看见一堆孤立文件,无法回到业务语义。

四、记录页和统计页为什么能直接看真实数据

记录页和统计页看起来像两个不同的功能页,但它们最终都依赖同一份 `WorkRecord`。记录页负责筛选、播放和预览,统计页负责按时间范围重新计算指标、趋势和项目分布,所以一条记录写进去后,两个页面都会跟着变。

图3|从首页到记录页:保存、展示、统计、刷新形成一条完整回路。

private getFilteredRecords(): WorkRecord[] {
  if (this.filter === 'all') {
    return this.records.slice();
  }
  if (this.filter === 'photo') {
    return this.records.filter((item: WorkRecord) =>
      !!item.mediaUri && item.mediaUri.length > 0 && this.isPhotoMediaUri(item.mediaUri)
    );
  }
  return this.records.filter((item: WorkRecord) => !!item.audioUri && item.audioUri.length > 0);
}

private getAttachmentStatus(record: WorkRecord): string {
  if (record.mediaUri && record.mediaUri.length > 0 && this.isPhotoMediaUri(record.mediaUri)) {
    return '已关联现场照片,可点击查看';
  }
  if (record.audioUri && record.audioUri.length > 0) {
    return '已关联录音文件,可点击播放';
  }
  return '仅文字记录';
}

private refreshStats(range: string): void {
  this.currentRange = range;
  this.summary = this.service.getStatsSummary(range);
}

页面

调用的服务

用户看到的结果

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()`,理解数据怎样回流到首页、记录页和统计页。

赞(0)
未经允许不得转载:171主机测评 » 首页到记录页:数据闭环全解析
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址