在这里插入图片描述

📌 概述

最近记录模块展示了用户最近添加或修改的喝茶记录,为用户提供了快速访问最新数据的入口。该模块集成了 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

Logo

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

更多推荐