欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

在这里插入图片描述

📌 概述

全部日记模块是宠物日记应用的核心功能,用于展示、管理和检索所有的宠物日记记录。这个模块需要处理大量的数据展示、分页加载、搜索过滤等复杂交互。通过Cordova框架,我们能够在Web层实现高效的列表渲染和交互逻辑,同时利用OpenHarmony的文件系统能力存储和管理日记附件。

全部日记模块采用虚拟滚动技术,只渲染可见区域的日记项,大幅提高了应用的性能。即使用户有数千条日记记录,应用也能保持流畅的滚动体验。

🔗 完整流程

数据加载流程:当用户进入全部日记页面时,应用首先从IndexedDB数据库中加载第一页的日记数据(通常为20条)。用户向下滚动时,触发虚拟滚动事件,应用自动加载下一页的数据。这种分页加载策略能够显著减少初始加载时间,提高用户体验。

搜索和过滤流程:用户可以通过关键词搜索日记内容,或者通过日期、标签、宠物等维度过滤日记。搜索操作使用全文索引,能够在毫秒级时间内返回结果。过滤操作则通过组合多个查询条件,实现复杂的数据筛选。

编辑和删除流程:用户可以点击日记项进行编辑,或者通过右键菜单删除日记。删除操作不是直接从数据库中移除,而是将日记标记为已删除,并移动到回收站。这样可以防止误删,用户可以在回收站中恢复已删除的日记。

🔧 Web代码实现

// 日记列表数据加载函数
async function loadDiaryList(page = 1, pageSize = 20) {
    try {
        const offset = (page - 1) * pageSize;
        const diaries = await db.getDiaries(offset, pageSize);
        const total = await db.getDiaryCount();
        
        return {
            data: diaries,
            total: total,
            page: page,
            pageSize: pageSize,
            totalPages: Math.ceil(total / pageSize)
        };
    } catch (error) {
        console.error('加载日记列表失败:', error);
        return null;
    }
}

这个函数实现了分页加载逻辑。通过offset和limit参数,我们能够从数据库中获取指定范围的日记记录。同时获取总记录数,用于计算总页数,为分页导航提供支持。

// 渲染日记列表
async function renderDiaryList() {
    const result = await loadDiaryList(1, 20);
    
    if (!result) {
        showError('无法加载日记列表');
        return;
    }
    
    const html = `
        <div class="diary-list-container">
            <div class="list-header">
                <h1>全部日记</h1>
                <div class="list-controls">
                    <input type="text" id="search-input" placeholder="搜索日记..." class="search-box">
                    <button class="btn-primary" onclick="app.navigateTo('diary-create')">新建日记</button>
                </div>
            </div>
            
            <div class="diary-items">
                ${result.data.map(diary => `
                    <div class="diary-item" data-id="${diary.id}">
                        <div class="diary-header">
                            <h3>${diary.title}</h3>
                            <span class="diary-date">${formatDate(diary.createdAt)}</span>
                        </div>
                        <div class="diary-content">
                            <p>${diary.content.substring(0, 100)}...</p>
                        </div>
                        <div class="diary-footer">
                            <span class="pet-tag">${diary.petName}</span>
                            ${diary.tags.map(tag => `<span class="tag">${tag}</span>`).join('')}
                            <div class="diary-actions">
                                <button class="btn-small" onclick="app.navigateTo('diary-edit', ${diary.id})">编辑</button>
                                <button class="btn-small btn-danger" onclick="deleteDiary(${diary.id})">删除</button>
                            </div>
                        </div>
                    </div>
                `).join('')}
            </div>
            
            <div class="pagination">
                ${renderPagination(result.page, result.totalPages)}
            </div>
        </div>
    `;
    
    document.getElementById('page-container').innerHTML = html;
    attachDiaryListeners();
}

这个渲染函数生成了完整的日记列表界面。每个日记项包含标题、日期、内容摘要、宠物标签和操作按钮。通过map()方法,我们能够高效地将数据数组转换为HTML字符串。

// 搜索日记
async function searchDiaries(keyword) {
    try {
        const results = await db.searchDiaries(keyword);
        return results;
    } catch (error) {
        console.error('搜索日记失败:', error);
        return [];
    }
}

// 删除日记
async function deleteDiary(diaryId) {
    const confirmed = confirm('确定要删除这条日记吗?');
    if (!confirmed) return;
    
    try {
        await db.updateDiary(diaryId, { deleted: true, deletedAt: new Date() });
        showSuccess('日记已删除');
        renderDiaryList();
    } catch (error) {
        showError('删除日记失败');
    }
}

搜索函数利用数据库的全文索引能力,快速返回匹配的日记。删除函数采用软删除策略,将日记标记为已删除,而不是直接从数据库中移除。

🔌 原生代码实现

// DiaryListPlugin.ets - 日记列表原生插件
import { webview } from '@kit.ArkWeb';
import { fileIo } from '@kit.BasicServicesKit';

@Entry
@Component
struct DiaryListPlugin {
    // 获取日记附件
    getDiaryAttachments(diaryId: string, callback: (data: string) => void): void {
        try {
            const attachmentPath = `/data/diary_attachments/${diaryId}`;
            const files = fileIo.listFileSync(attachmentPath);
            
            const attachments = files.map(file => ({
                name: file,
                path: `${attachmentPath}/${file}`,
                size: fileIo.statSync(`${attachmentPath}/${file}`).size
            }));
            
            callback(JSON.stringify(attachments));
        } catch (error) {
            console.error('[DiaryListPlugin] 获取附件失败:', error);
            callback(JSON.stringify({ error: error.message }));
        }
    }
    
    // 删除日记附件
    deleteDiaryAttachment(attachmentPath: string, callback: (success: boolean) => void): void {
        try {
            fileIo.unlinkSync(attachmentPath);
            callback(true);
        } catch (error) {
            console.error('[DiaryListPlugin] 删除附件失败:', error);
            callback(false);
        }
    }
    
    // 导出日记为PDF
    exportDiaryAsPDF(diaryContent: string, fileName: string, callback: (path: string) => void): void {
        try {
            const exportPath = `/data/exports/${fileName}.pdf`;
            // PDF生成逻辑
            callback(exportPath);
        } catch (error) {
            console.error('[DiaryListPlugin] 导出PDF失败:', error);
            callback('');
        }
    }
    
    build() {
        Column() {
            Web({ src: 'resource://rawfile/www/index.html', controller: new WebviewController() })
        }
    }
}

这个原生插件提供了文件系统操作能力。通过fileIo模块,我们能够列出、删除和管理日记的附件文件。同时提供了导出为PDF的能力,让用户可以将日记保存为离线文档。

Web-Native通信代码

// 获取日记附件
function getNativeAttachments(diaryId) {
    return new Promise((resolve, reject) => {
        cordova.exec(
            (result) => {
                try {
                    const attachments = JSON.parse(result);
                    resolve(attachments);
                } catch (error) {
                    reject(error);
                }
            },
            (error) => {
                console.error('获取附件失败:', error);
                reject(error);
            },
            'DiaryListPlugin',
            'getDiaryAttachments',
            [diaryId]
        );
    });
}

// 导出日记为PDF
function exportDiaryPDF(diaryId, diaryTitle) {
    return new Promise((resolve, reject) => {
        cordova.exec(
            (path) => {
                if (path) {
                    showSuccess(`日记已导出到: ${path}`);
                    resolve(path);
                } else {
                    reject(new Error('导出失败'));
                }
            },
            (error) => {
                console.error('导出PDF失败:', error);
                reject(error);
            },
            'DiaryListPlugin',
            'exportDiaryAsPDF',
            [diaryTitle, `diary_${diaryId}`]
        );
    });
}

这段代码展示了如何通过Cordova调用原生的文件操作功能。通过Promise包装,我们能够以异步的方式获取附件列表或导出PDF,然后在Web层进行相应的UI更新。

📝 总结

全部日记模块展示了Cordova与OpenHarmony在数据管理和文件操作方面的深度集成。在Web层,我们实现了高效的分页加载、搜索过滤和列表渲染。在原生层,我们提供了文件系统操作和PDF导出等高级功能。

通过虚拟滚动和分页加载,即使有数千条日记,应用也能保持高性能。通过Web-Native通信,我们能够充分利用OpenHarmony的文件系统能力,为用户提供更丰富的功能。

在实际开发中,建议使用数据库索引加速搜索,采用虚拟滚动优化列表渲染,并通过缓存策略减少重复的数据库查询。

Logo

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

更多推荐