快速记录喝水-Cordova&openharmony表单交互实践
本文介绍了喝水记录应用中“快速记录喝水”功能的实现方法。文章分为五个部分:首先概述功能需求,随后展示Web表单的HTML结构和CSS样式,接着说明表单提交时的数据收集与本地IndexedDB存储,然后通过Cordova将记录同步到原生层,最后展示OpenHarmony ArkTS插件接收处理数据的代码。整个流程采用Web层与原生层分离的架构,通过Cordova桥接实现数据同步,既保证了用户体验的流

一、功能概述
在喝水记录应用中,用户最常用的操作是“快速记一杯水”。本篇文章围绕“快速记录喝水”页面展开,展示如何在 Cordova Web 层 使用表单收集数据、写入本地 IndexedDB,再通过 OpenHarmony ArkTS 插件 将本次饮水事件同步给原生层,用于触发通知、动画或小组件更新。全文依旧采用 一段代码 + 一段文字说明 的方式进行拆解,避免出现大段代码影响阅读。
二、Web 表单结构与基础样式
<form id="quick-drink-form" class="quick-form">
<label class="form-item">
<span>本次饮水量 (ml)</span>
<input type="number" id="input-amount" min="50" step="50" value="250" required />
</label>
<label class="form-item">
<span>饮水类型</span>
<select id="select-type">
<option value="water">白开水</option>
<option value="tea">茶水</option>
<option value="juice">果汁</option>
</select>
</label>
<button type="submit" class="btn-primary">立即记录</button>
</form>
这段 HTML 定义了一个最小可用的“快速记录喝水”表单。quick-drink-form 是整个表单容器,内部包含饮水量输入框、饮水类型下拉选择以及一个提交按钮。饮水量使用 type="number",并设置 min 和 step 来约束输入范围和步长,使得用户在移动端和 PC 端都能方便地调整数值。下拉框 select-type 提供几种常见饮水类型,后续可以扩展为从数据库动态加载。通过在每个表单项外部包裹一个 label.form-item,可以让标题和控件在 CSS 中统一布局和对齐。
.quick-form {
display: flex;
flex-direction: column;
gap: 12px;
max-width: 320px;
}
.form-item span {
display: block;
margin-bottom: 4px;
font-size: 14px;
}
.form-item input,
.form-item select {
width: 100%;
padding: 8px;
border-radius: 4px;
border: 1px solid #4b5563;
background-color: #111827;
color: #e5e7eb;
}
这段 CSS 为快速记录表单提供了简单而实用的视觉样式。.quick-form 使用纵向 flex 布局,让每个表单项顺序排列并保持统一间距;max-width 控制表单整体宽度,既适合在 PC 上显示,又不会在窄屏设备上显得过于拥挤。每个 span 标签作为字段标题,使用 display: block 让标题和输入控件分行显示;输入框和下拉框统一设置了圆角、边框和背景色,与仪表板的深色主题保持一致。通过分离结构和样式,表单逻辑可以在不改动 HTML 的情况下灵活调整视觉效果。
三、表单提交与本地数据库写入
function bindQuickDrinkForm() {
const form = document.getElementById('quick-drink-form');
if (!form) return;
form.addEventListener('submit', async (event) => {
event.preventDefault();
const amountInput = document.getElementById('input-amount') as HTMLInputElement | null;
const typeSelect = document.getElementById('select-type') as HTMLSelectElement | null;
const amount = Number(amountInput?.value || 0);
const drinkType = typeSelect?.value || 'water';
if (!amount || amount <= 0) {
alert('请输入正确的饮水量');
return;
}
const now = new Date();
const record = {
id: now.getTime(),
date: now.toISOString(),
amount,
type: drinkType,
};
await db.addDrinkRecord(record);
amountInput!.value = '250';
syncQuickRecordToNative(record);
});
}
bindQuickDrinkForm 函数负责给表单绑定提交事件,并在用户点击“立即记录”时完成数据收集与本地写入。首先通过 getElementById 获取表单和各个字段控件,在监听函数中调用 preventDefault 阻止浏览器默认的表单提交行为。随后读取饮水量与类型,并通过简单的合法性校验(例如 amount > 0)保证数据合理。如果校验失败,则通过 alert 向用户提示错误。
在构造 record 对象时,使用当前时间戳 now.getTime() 作为简单的主键,同时保存 ISO 格式日期、饮水量和类型。调用 db.addDrinkRecord 将记录写入 IndexedDB,这里假定数据库模块已经封装好了插入逻辑。写入完成后,将输入框重置为默认值 250,同时调用 syncQuickRecordToNative,把新记录同步给原生层,为后续的通知或动画提供依据。
document.addEventListener('DOMContentLoaded', () => {
bindQuickDrinkForm();
});
为了确保表单元素已经出现在 DOM 中,这段代码在 DOMContentLoaded 事件触发后执行 bindQuickDrinkForm。和 deviceready 不同,DOMContentLoaded 只关心文档结构是否已经解析完成,适合用于绑定纯 Web 事件处理逻辑。通过把绑定代码与业务逻辑分离,可以保证当页面结构发生变化时,只要保持相同的 id,就不需要大幅修改事件绑定部分。
四、通过 Cordova 将单条记录同步到原生
function syncQuickRecordToNative(record) {
if (!window.cordova) {
console.warn('[QuickDrink] cordova not ready, skip native sync');
return;
}
cordova.exec(
() => {
console.info('[QuickDrink] sync record success');
},
(err) => {
console.error('[QuickDrink] sync record failed', err);
},
'WaterTrackerQuickDrink',
'addRecord',
[record]
);
}
syncQuickRecordToNative 负责将刚刚保存到 IndexedDB 的饮水记录通过 Cordova 桥接发送到 ArkTS 插件中。这里同样首先检查 window.cordova 是否存在,如果 Cordova 尚未注入,就打印警告并终止流程,避免在不合适的时机调用导致异常。cordova.exec 的参数分别是成功回调、失败回调、插件名称、动作名称以及参数数组。本例中插件名设为 WaterTrackerQuickDrink,动作名为 addRecord,以便在 ArkTS 侧实现对应的方法。成功和失败回调中统一打印日志,为后续问题排查提供基础信息。
通过这种模式,Web 层在完成本地持久化之后,再把同一份记录推送给原生层,保证两端数据的一致性。原生层可以决定是否将这条记录用于刷新桌面小组件、展示动画或触发本地通知,而不需要 Web 层知道具体实现细节,从而实现职责分离。
五、OpenHarmony ArkTS 插件处理单条记录
// entry/src/main/ets/plugins/WaterTrackerQuickDrinkPlugin.ets
import common from '@ohos.app.ability.common';
export default class WaterTrackerQuickDrinkPlugin {
context: common.UIAbilityContext;
constructor(ctx: common.UIAbilityContext) {
this.context = ctx;
}
addRecord(args: Array<Object>, callbackId: number) {
const record = args[0] as {
id: number;
date: string;
amount: number;
type: string;
};
console.info(`[QuickDrinkPlugin] Receive record amount=${record.amount}, type=${record.type}`);
// 这里可以进一步把数据存入本地首选项或触发通知
}
}
这段 ArkTS 代码定义了一个接收单条饮水记录的插件 WaterTrackerQuickDrinkPlugin。在构造函数中,同样保存 UIAbilityContext 供后续使用;addRecord 方法按照 Cordova 的约定签名接收参数数组与回调编号,并从 args[0] 中解析出记录对象。这里给出了 id、date、amount 和 type 的字段定义,方便在 TypeScript 级别获得更好的类型检查。当前示例中仅通过 console.info 打印了一条日志,说明已经成功收到了来自 Web 的饮水记录。在实际项目中,可以在这个方法中进一步:
- 写入 ArkTS 侧自己的轻量数据存储;
- 触发一个本地通知,提醒用户喝水进度;
- 更新与喝水相关的桌面小组件或卡片。
把这些逻辑放在原生层可以减少 Web 层对系统能力的直接依赖,也能更充分地利用 HarmonyOS 提供的本地能力和资源管理机制。
六、ArkUI 页面中响应最新饮水记录
// entry/src/main/ets/pages/LatestDrinkPage.ets
@Component
struct LatestDrinkCard {
@State amount: number = 0;
@State drinkType: string = '';
build() {
Column() {
Text('最近一次喝水')
.fontSize(18)
.margin({ bottom: 8 });
Text(`饮水量:${this.amount} ml`)
.fontSize(16);
Text(`类型:${this.drinkType}`)
.fontSize(16)
.margin({ top: 4 });
}
.padding(16)
}
}
LatestDrinkCard 是一个简单的 ArkUI 组件,用于展示“最近一次喝水”的信息。组件内部使用两个 @State 状态变量 amount 和 drinkType 保存最新的饮水量与类型。build 方法中,使用 Column 进行纵向布局,依次渲染标题、饮水量和饮水类型三行文本。通过字符串模板将状态变量插入到 Text 中,当状态改变时界面会自动刷新。这里没有直接展示状态更新逻辑,而是假设插件在接收到新记录后,会通过某种共享状态机制或消息分发系统把最新数据推送给该组件,对应的状态更新代码可以在后续扩展中补充。
这样的 ArkUI 组件可以嵌入到主页面或单独页面中,使原生界面能够实时反映用户最近一次的喝水情况。与 Web 层的表单配合,就构成了从输入到展示的完整闭环。
七、小结
本篇文章介绍了“快速记录喝水”功能在 Cordova 与 OpenHarmony 混合应用中的典型实现流程。首先在 Web 侧通过一个简洁的表单收集饮水量和类型等数据,并将其写入 IndexedDB;随后使用 bindQuickDrinkForm 绑定表单提交事件,在校验通过后构造记录对象并调用 db.addDrinkRecord 进行持久化。紧接着,通过 syncQuickRecordToNative 使用 cordova.exec 将这条记录发送给 ArkTS 插件。
在鸿蒙侧,我们定义了 WaterTrackerQuickDrinkPlugin 插件,用来解析来自 Web 的记录对象,未来可以将其与本地通知、桌面卡片或者其他系统能力对接;同时给出了一个简单的 ArkUI 组件 LatestDrinkCard 作为展示层示例。通过一段代码一段说明的方式,可以清晰地看到每一步的职责:Web 负责表单交互和本地数据库,原生层负责系统能力与更丰富的 UI 表达。后续你可以基于同样的结构扩展更多字段(例如水温、容器类型等),或者为不同类型饮品设计不同的原生展示风格,从而在保持开发效率的同时,打造出更贴合用户习惯的喝水记录体验。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐


所有评论(0)