最近记录模块 Cordova 与 OpenHarmony 混合开发实战
最近记录模块为用户提供了快速访问最新喝茶记录的入口。该功能通过卡片式设计展示最近修改的10条记录,包含茶叶类型、品尝日期、消费金额等关键信息,并支持快速操作。实现上采用IndexedDB数据库加载并按修改时间排序,使用响应式卡片渲染,确保跨设备兼容性。模块还具备实时更新机制,当用户在其他页面修改记录时会自动刷新显示。代码层面通过Cordova框架与OpenHarmony原生能力集成,提供高效的数据

📌 概述
最近记录模块展示了用户最近添加或修改的喝茶记录,为用户提供了快速访问最新数据的入口。该模块集成了 Cordova 框架与 OpenHarmony 原生能力,实现了高效的时间序列数据展示和实时更新。用户可以快速查看最近的喝茶体验、修改或删除记录。模块采用了卡片式设计,每个记录卡片包含关键信息和快速操作按钮,提高了用户的操作效率。
🔗 完整流程
第一步:数据加载与排序
当用户进入最近记录页面时,应用会从 IndexedDB 数据库中加载最近修改的 10 条记录。应用会按修改时间从新到旧排序,确保最新的记录显示在最前面。为了提高性能,应用会使用数据库的排序功能而不是在 JavaScript 中排序。应用会显示加载动画直到数据加载完成。
第二步:卡片渲染与展示
数据加载完成后,应用会将记录渲染为卡片形式。每个卡片显示茶叶类型、品尝日期、消费金额、品质评分和修改时间等信息。卡片采用了响应式设计,在不同屏幕尺寸上都能正常显示。用户可以点击卡片查看详细信息或进行快速操作。
第三步:实时更新与交互
当用户在其他页面添加或修改记录时,最近记录页面会自动更新,显示最新的记录。应用使用了事件系统实现这种实时更新。用户可以在卡片上进行快速操作,如编辑、删除或分享记录,无需跳转到其他页面。
🔧 Web 代码实现
HTML 卡片结构
<div id="recent-records-page" class="page">
<div class="page-header">
<h1>最近记录</h1>
<span class="subtitle">最近修改的记录</span>
</div>
<div id="recent-records-container" class="cards-container">
<!-- 卡片动态生成 -->
</div>
<div id="no-recent-message" class="no-data" style="display: none;">
<p>暂无最近记录</p>
<button class="btn btn-primary" onclick="navigateTo('add-record')">添加第一条记录</button>
</div>
</div>
最近记录页面使用卡片容器来展示记录。当没有记录时,显示友好的提示信息和快速操作按钮。
卡片数据加载与渲染
async function renderRecentRecords() {
try {
// 加载最近的 10 条记录
const recentRecords = await db.getRecentRecords(10);
if (recentRecords.length === 0) {
document.getElementById('no-recent-message').style.display = 'block';
document.getElementById('recent-records-container').innerHTML = '';
return;
}
document.getElementById('no-recent-message').style.display = 'none';
const container = document.getElementById('recent-records-container');
container.innerHTML = '';
recentRecords.forEach(record => {
const cardEl = createRecordCard(record);
container.appendChild(cardEl);
});
// 触发原生事件记录
if (window.cordova) {
cordova.exec(
null, null,
'TeaLogger',
'logEvent',
['recent_records_loaded', { count: recentRecords.length }]
);
}
} catch (error) {
console.error('Failed to render recent records:', error);
showToast('加载最近记录失败', 'error');
}
}
function createRecordCard(record) {
const card = document.createElement('div');
card.className = 'record-card';
card.dataset.recordId = record.id;
const date = new Date(record.createdAt).toLocaleDateString('zh-CN');
const modifiedDate = new Date(record.updatedAt || record.createdAt);
const timeAgo = getTimeAgoString(modifiedDate);
const stars = '★'.repeat(record.rating) + '☆'.repeat(5 - record.rating);
card.innerHTML = `
<div class="card-header">
<div class="card-title">${record.teaType}</div>
<div class="card-time">${timeAgo}</div>
</div>
<div class="card-content">
<div class="card-info-row">
<span class="label">产地:</span>
<span class="value">${record.origin}</span>
</div>
<div class="card-info-row">
<span class="label">日期:</span>
<span class="value">${date}</span>
</div>
<div class="card-info-row">
<span class="label">金额:</span>
<span class="value">¥${record.price.toFixed(2)}</span>
</div>
<div class="card-rating">
<span class="label">评分:</span>
<span class="stars">${stars}</span>
</div>
${record.notes ? `<div class="card-notes">${record.notes}</div>` : ''}
</div>
<div class="card-actions">
<button class="btn-icon" onclick="viewRecord(${record.id})" title="查看">👁️</button>
<button class="btn-icon" onclick="editRecord(${record.id})" title="编辑">✏️</button>
<button class="btn-icon" onclick="shareRecord(${record.id})" title="分享">📤</button>
<button class="btn-icon" onclick="deleteRecord(${record.id})" title="删除">🗑️</button>
</div>
`;
return card;
}
function getTimeAgoString(date) {
const now = new Date();
const diffMs = now - date;
const diffMins = Math.floor(diffMs / 60000);
const diffHours = Math.floor(diffMs / 3600000);
const diffDays = Math.floor(diffMs / 86400000);
if (diffMins < 1) return '刚刚';
if (diffMins < 60) return `${diffMins} 分钟前`;
if (diffHours < 24) return `${diffHours} 小时前`;
if (diffDays < 7) return `${diffDays} 天前`;
return date.toLocaleDateString('zh-CN');
}
这段代码实现了最近记录的加载和卡片渲染。renderRecentRecords() 从数据库加载最近的记录。createRecordCard() 为每条记录创建卡片 DOM 元素。getTimeAgoString() 计算相对时间字符串,如"5 分钟前"。
实时更新机制
// 监听数据库变化事件
document.addEventListener('recordAdded', function(event) {
renderRecentRecords();
});
document.addEventListener('recordUpdated', function(event) {
renderRecentRecords();
});
document.addEventListener('recordDeleted', function(event) {
renderRecentRecords();
});
// 定时刷新(可选)
setInterval(() => {
if (isCurrentPage('recent-records')) {
renderRecentRecords();
}
}, 30000); // 每 30 秒刷新一次
这段代码实现了实时更新机制。当数据库中的数据发生变化时,会自动触发最近记录的重新加载。同时,应用还支持定时刷新,确保数据的最新性。
🔌 OpenHarmony 原生代码
时间序列数据查询
// entry/src/main/ets/plugins/TimeSeriesQuery.ets
import { relationalStore } from '@kit.ArkData';
export class TimeSeriesQuery {
private store: relationalStore.RdbStore;
async getRecentRecords(limit: number = 10): Promise<TeaRecord[]> {
const predicates = new relationalStore.RdbPredicates('tea_records');
predicates.orderByDesc('updated_at').limit(limit);
const resultSet = await this.store.query(predicates);
const records: TeaRecord[] = [];
while (resultSet.goToNextRow()) {
records.push(this.parseRecord(resultSet));
}
resultSet.close();
return records;
}
async getRecordsByTimeRange(startTime: Date, endTime: Date): Promise<TeaRecord[]> {
const predicates = new relationalStore.RdbPredicates('tea_records');
predicates.between('created_at', startTime.toISOString(), endTime.toISOString());
predicates.orderByDesc('created_at');
const resultSet = await this.store.query(predicates);
const records: TeaRecord[] = [];
while (resultSet.goToNextRow()) {
records.push(this.parseRecord(resultSet));
}
resultSet.close();
return records;
}
private parseRecord(resultSet: relationalStore.ResultSet): TeaRecord {
return {
id: resultSet.getColumnValue(resultSet.getColumnIndex('id')) as number,
teaType: resultSet.getColumnValue(resultSet.getColumnIndex('tea_type')) as string,
origin: resultSet.getColumnValue(resultSet.getColumnIndex('origin')) as string,
date: resultSet.getColumnValue(resultSet.getColumnIndex('date')) as string,
price: resultSet.getColumnValue(resultSet.getColumnIndex('price')) as number,
rating: resultSet.getColumnValue(resultSet.getColumnIndex('rating')) as number,
notes: resultSet.getColumnValue(resultSet.getColumnIndex('notes')) as string,
createdAt: resultSet.getColumnValue(resultSet.getColumnIndex('created_at')) as string,
updatedAt: resultSet.getColumnValue(resultSet.getColumnIndex('updated_at')) as string
};
}
}
interface TeaRecord {
id?: number;
teaType: string;
origin: string;
date: string;
price: number;
rating: number;
notes?: string;
createdAt: string;
updatedAt?: string;
}
这个类提供了时间序列数据查询功能。getRecentRecords() 查询最近修改的记录。getRecordsByTimeRange() 查询指定时间范围内的记录。parseRecord() 将数据库结果转换为 TypeScript 对象。
事件通知机制
// entry/src/main/ets/plugins/EventNotifier.ets
export class EventNotifier {
private static listeners: Map<string, Function[]> = new Map();
static on(eventName: string, callback: Function): void {
if (!this.listeners.has(eventName)) {
this.listeners.set(eventName, []);
}
this.listeners.get(eventName)?.push(callback);
}
static emit(eventName: string, data?: any): void {
const callbacks = this.listeners.get(eventName);
if (callbacks) {
callbacks.forEach(callback => {
try {
callback(data);
} catch (error) {
hilog.error(0xFF00, 'EventNotifier', `Error in callback: ${error}`);
}
});
}
}
static off(eventName: string, callback: Function): void {
const callbacks = this.listeners.get(eventName);
if (callbacks) {
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
}
}
}
}
这个事件通知类实现了观察者模式,允许不同的模块之间进行事件通信。当数据发生变化时,可以通过 emit() 方法触发事件,所有监听该事件的模块都会收到通知。
📝 总结
最近记录模块展示了如何在 Cordova 框架中实现实时数据展示功能。通过 Web 层的卡片渲染和事件监听,结合原生层的高效数据查询和事件通知,为用户提供了快速访问最新数据的入口。该模块的设计模式可以应用到其他需要实时更新的功能中。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐
所有评论(0)