一、功能概述

很多人白天忙于工作,真正记得喝水的时间往往集中在上午某个时段或晚间休息时。如果应用能够按“时间段”统计喝水行为,就可以帮助用户看清自己的日内节奏,从而有针对性地调整习惯。本篇文章围绕“时间段统计”模块展开,介绍如何在 Cordova Web 层 按小时或时间段聚合数据,并通过 OpenHarmony ArkTS 插件 将这些结果同步到原生侧,用于构建更直观的日内节奏视图。

我们将继续采用“一段代码一段说明”的形式,结合 HTML/JavaScript 和 ArkTS 示例,完成从 IndexedDB 记录到时间段分布视图的完整链路。

二、Web 端时间段统计页面结构

<div id="time-slot-page" class="page page-time-slot">
  <h1>时间段喝水统计</h1>

  <table class="data-table" id="table-time-slot">
    <thead>
      <tr>
        <th>时间段</th>
        <th>次数</th>
        <th>总饮水量 (ml)</th>
      </tr>
    </thead>
    <tbody id="table-time-slot-body"></tbody>
  </table>
</div>

这段 HTML 定义了时间段统计页面的基础结构。time-slot-page 是页面根容器,内部的表格包含三列:时间段(如 06:00-09:00)、喝水次数和总饮水量。真实的统计数据将由 JavaScript 在运行时写入 tbody,因此这里只需要一个空的 tbody 占位元素 table-time-slot-body。统一的 data-table 样式可以和其他统计页面共用,从而降低样式维护成本。

.page-time-slot {
  padding: 16px 24px;
}

#table-time-slot td:nth-child(2),
#table-time-slot td:nth-child(3) {
  text-align: right;
}

CSS 为页面增加内边距,并将“次数”和“总饮水量”两列右对齐,以便在纵向比较不同时间段时更直观。这里沿用了之前统计表格的视觉规范,保证用户在不同统计页面之间切换时,体验保持一致。

三、将时间映射到时间段

function getTimeSlotKey(dateString) {
  const d = new Date(dateString);
  const hour = d.getHours();

  if (hour < 6) return '00:00-06:00';
  if (hour < 12) return '06:00-12:00';
  if (hour < 18) return '12:00-18:00';
  return '18:00-24:00';
}

getTimeSlotKey 是时间段统计最关键的辅助函数之一。它将喝水记录的时间(ISO 字符串)转换为四个粗粒度时间段之一:凌晨、上午、下午和晚上。实际项目中,你可以根据需求细分为更多时间段,这里为了示例简单只划分为四段。通过这样的映射,我们可以在聚合时以时间段字符串为键,统计每个时间段内的喝水次数和总饮水量。

四、按时间段聚合统计数据

async function loadTimeSlotStats() {
  const records = await db.getAllDrinkRecords();
  const map = new Map();

  records.forEach((r) => {
    const slot = getTimeSlotKey(r.date);
    const current = map.get(slot) || { count: 0, total: 0 };
    current.count += 1;
    current.total += r.amount;
    map.set(slot, current);
  });

  renderTimeSlotTable(map);
  syncTimeSlotStatsToNative(map);
}

loadTimeSlotStats 用于从 IndexedDB 中读取所有喝水记录,并按时间段进行聚合。它遍历每条记录,使用前面定义的 getTimeSlotKey 得到时间段键值,然后在 Map 中累加对应的次数和总饮水量。聚合完成后,一方面调用 renderTimeSlotTable 渲染表格,另一方面通过 syncTimeSlotStatsToNative 将相同的数据发送给原生侧,实现数据一次计算、多端复用。

function renderTimeSlotTable(map) {
  const tbody = document.getElementById('table-time-slot-body');
  if (!tbody) return;

  tbody.innerHTML = '';

  const orderedSlots = ['00:00-06:00', '06:00-12:00', '12:00-18:00', '18:00-24:00'];

  orderedSlots.forEach((slot) => {
    const stat = map.get(slot) || { count: 0, total: 0 };
    const tr = document.createElement('tr');

    const tdSlot = document.createElement('td');
    const tdCount = document.createElement('td');
    const tdTotal = document.createElement('td');

    tdSlot.textContent = slot;
    tdCount.textContent = String(stat.count);
    tdTotal.textContent = String(stat.total);

    tr.appendChild(tdSlot);
    tr.appendChild(tdCount);
    tr.appendChild(tdTotal);

    tbody.appendChild(tr);
  });
}

renderTimeSlotTable 负责将聚合结果渲染到表格中。为了保证时间段的顺序固定,函数定义了一个 orderedSlots 数组,并按预设顺序依次取出对应统计值。这可以避免 Map 的遍历顺序带来的不确定性。对于不存在记录的时间段,使用 { count: 0, total: 0 } 作为默认值,让用户清晰地看到“没有在这个时间段喝过水”。通过这种方式,日内节奏的整体轮廓在一张表中就能完整呈现。

五、将时间段统计同步到原生

function syncTimeSlotStatsToNative(map) {
  if (!window.cordova) {
    console.warn('[TimeSlot] cordova not ready, skip native sync');
    return;
  }

  const items = [];
  map.forEach((stat, slot) => {
    items.push({
      slot,
      count: stat.count,
      total: stat.total,
    });
  });

  cordova.exec(
    () => {
      console.info('[TimeSlot] sync stats success');
    },
    (err) => {
      console.error('[TimeSlot] sync stats failed', err);
    },
    'WaterTrackerTimeSlot',
    'setStats',
    [items]
  );
}

syncTimeSlotStatsToNativeMap 结构转换为数组,并通过 Cordova 的 exec 接口发送给 ArkTS 插件。每个数组元素包含 slot(时间段字符串)、counttotal 三个字段。原生侧收到后可以直接在 ArkUI 中构建时间段分布视图,例如使用条形图、雷达图或者简单列表。与之前其他统计模块类似,函数在调用前检查 window.cordova 是否存在,以避免在 Cordova 尚未注入时发生错误。

document.addEventListener('DOMContentLoaded', () => {
  loadTimeSlotStats();
});

DOMContentLoaded 事件中调用 loadTimeSlotStats,确保 DOM 结构准备就绪后再进行统计和渲染。用户每次打开时间段统计页面时,都能看到基于当前所有记录计算出的最新结果。

六、OpenHarmony ArkTS 插件与时间段统计存储

// entry/src/main/ets/plugins/WaterTrackerTimeSlotPlugin.ets
import common from '@ohos.app.ability.common';

export interface TimeSlotStatItem {
  slot: string;
  count: number;
  total: number;
}

export class TimeSlotStatsStore {
  private static _items: TimeSlotStatItem[] = [];

  static setItems(items: TimeSlotStatItem[]) {
    this._items = items;
  }

  static get items() {
    return this._items;
  }
}

export default class WaterTrackerTimeSlotPlugin {
  context: common.UIAbilityContext;

  constructor(ctx: common.UIAbilityContext) {
    this.context = ctx;
  }

  setStats(args: Array<Object>, callbackId: number) {
    const items = args[0] as TimeSlotStatItem[];
    TimeSlotStatsStore.setItems(items);
    console.info(`[TimeSlotPlugin] receive ${items.length} slots`);
  }
}

这段 ArkTS 代码定义了时间段统计插件 WaterTrackerTimeSlotPlugin 及其数据存储 TimeSlotStatsStoreTimeSlotStatItem 接口描述了每个时间段统计项包含的字段:时间段字符串、次数和总饮水量。TimeSlotStatsStore 使用静态数组 _items 缓存最新的时间段统计结果,供 ArkUI 组件读取。插件在 setStats 方法中解析来自 Web 层的数组,将其存入存储类,并输出日志说明接收到多少个时间段的数据。

七、ArkUI 中展示日内节奏视图

// entry/src/main/ets/pages/TimeSlotStatsPage.ets
import { TimeSlotStatsStore } from '../plugins/WaterTrackerTimeSlotPlugin';

@Component
struct TimeSlotStatsView {
  build() {
    Column() {
      Text('日内喝水节奏')
        .fontSize(18)
        .margin({ bottom: 8 });

      TimeSlotStatsStore.items.forEach((item) => {
        Row() {
          Text(item.slot)
            .fontSize(14);

          Text(`次数:${item.count}`)
            .fontSize(14)
            .margin({ left: 8 });

          Text(`总量:${item.total} ml`)
            .fontSize(14)
            .margin({ left: 8 });
        }
        .margin({ bottom: 4 });
      });
    }
    .padding(16)
  }
}

TimeSlotStatsView 是一个示例性的 ArkUI 组件,用于在原生界面中展示日内喝水节奏。组件遍历 TimeSlotStatsStore.items,为每个时间段渲染一行包含时间段名称、喝水次数和总饮水量的文本行。虽然这里只是基础列表,但已经可以帮助用户快速识别出自己在哪些时间段喝水最多,哪些时间段几乎没有喝水。在实际项目中,可以根据这些数据生成折线图或雷达图,以更加形象的方式呈现日内节奏。

八、小结

本篇文章以“时间段统计”为主题,展示了如何在 Cordova&openharmony 混合应用中,从时间维度挖掘出用户日内喝水节奏。Web 层通过 getTimeSlotKey 将记录时间映射到预设时间段,loadTimeSlotStats 对所有记录按时间段聚合,renderTimeSlotTable 将结果渲染到表格,syncTimeSlotStatsToNative 则将同一份数据同步给 ArkTS 插件。原生侧通过 TimeSlotStatsStoreWaterTrackerTimeSlotPlugin 缓存数据,ArkUI 组件 TimeSlotStatsView 则将这些信息以原生界面形式呈现给用户。

通过“一段代码一段说明”的写作方式,我们把整个数据流拆解得足够细致,便于在阅读和调试时快速定位每一步的职责。你可以在此基础上进一步细化时间段(例如每 2 小时一段)、增加过滤条件(例如只看最近 7 天的数据),或者与提醒系统联动,在用户最容易忘记喝水的时间段增加提醒频率,从而真正做到对日内节奏的有针对性优化。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐