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

在这里插入图片描述

📌 概述

标签管理功能允许用户为旅行添加自定义标签,用于分类和组织旅行记录。标签是一种灵活的分类方式,一个旅行可以有多个标签,一个标签也可以对应多个旅行。标签管理提供了强大的组织和搜索能力。在 Cordova 与 OpenHarmony 的混合开发框架中,标签管理需要实现标签的创建、编辑、删除和关联操作。

🔗 完整流程

第一步:标签数据库设计与关联关系

标签管理需要在数据库中创建标签表和旅行-标签关联表。标签表存储标签的基本信息如名称、颜色、描述等。关联表存储旅行和标签的多对多关系。

标签表的设计需要考虑标签的唯一性,避免创建重复的标签。关联表需要建立索引,提高查询效率。

第二步:标签列表展示与操作

标签管理页面需要展示所有标签的列表。用户可以查看每个标签关联的旅行数量,也可以进行编辑、删除等操作。标签可以按名称、使用次数等进行排序。

标签管理还需要实现标签的批量操作,如批量删除、批量编辑等。

第三步:原生层标签缓存与性能优化

OpenHarmony 原生层可以实现标签的内存缓存,避免频繁的数据库查询。原生层还可以实现标签的快速搜索和匹配,提高用户体验。

🔧 Web 代码实现

标签管理页面 HTML 结构

<div id="tags-page" class="page">
    <div class="page-header">
        <h1>标签管理</h1>
        <button class="btn btn-primary" onclick="openTagModal()">
            ➕ 新建标签
        </button>
    </div>
    
    <div class="tags-container">
        <div class="tags-grid" id="tagsGrid">
            <!-- 标签列表动态加载 -->
        </div>
    </div>
</div>

HTML 结构使用网格布局展示标签,每个标签显示为一个卡片。

加载标签列表函数

async function loadTags() {
    try {
        // 从数据库查询所有标签
        const tags = await db.getAllTags();
        
        // 获取每个标签的关联旅行数
        for (let tag of tags) {
            const trips = await db.getTripsByTag(tag.id);
            tag.tripCount = trips.length;
        }
        
        // 按使用次数排序
        tags.sort((a, b) => b.tripCount - a.tripCount);
        
        // 渲染标签列表
        renderTagsList(tags);
    } catch (error) {
        console.error('Error loading tags:', error);
        showToast('加载标签失败');
    }
}

这个函数从数据库查询所有标签,计算每个标签的关联旅行数,然后按使用次数排序。loadTags 函数是标签管理的核心加载函数。函数首先从数据库获取所有标签记录。然后对每个标签,通过 getTripsByTag 方法查询关联的旅行数量,这样可以显示每个标签被使用的频率。接着按照使用次数进行降序排序,使得最常用的标签显示在最前面,方便用户快速找到常用的标签。最后调用 renderTagsList 函数将排序后的标签列表渲染到页面上。这种设计帮助用户了解标签的使用情况,并优化标签的组织方式。

标签列表渲染函数

function renderTagsList(tags) {
    const container = document.getElementById('tagsGrid');
    container.innerHTML = '';
    
    tags.forEach(tag => {
        const tagElement = document.createElement('div');
        tagElement.className = 'tag-card';
        tagElement.id = `tag-${tag.id}`;
        tagElement.style.borderColor = tag.color || '#409EFF';
        tagElement.innerHTML = `
            <div class="tag-header">
                <h3>${tag.name}</h3>
                <span class="tag-count">${tag.tripCount || 0}</span>
            </div>
            <div class="tag-body">
                <p class="tag-description">${tag.description || '暂无描述'}</p>
            </div>
            <div class="tag-footer">
                <button class="btn-small" onclick="editTag(${tag.id})">
                    编辑
                </button>
                <button class="btn-small btn-danger" onclick="deleteTag(${tag.id})">
                    删除
                </button>
            </div>
        `;
        container.appendChild(tagElement);
    });
}

标签列表渲染函数为每个标签创建一个卡片元素,显示标签的名称、描述和关联旅行数。renderTagsList 函数使用网格布局展示标签,每个标签显示为一个卡片。函数首先清空容器,然后遍历标签数组,为每个标签创建一个卡片元素。每个卡片包含标签的名称、关联旅行数、描述信息,以及编辑和删除按钮。通过设置 borderColor 样式,可以为每个标签显示不同的颜色,提高视觉效果。卡片的设计简洁清晰,用户可以一目了然地看到标签的信息。这种网格布局方式充分利用了屏幕空间,提供了良好的用户体验。

保存标签函数

async function saveTag(tagData) {
    try {
        // 验证数据
        if (!tagData.name || tagData.name.trim() === '') {
            showToast('标签名称不能为空');
            return;
        }
        
        // 检查标签是否已存在
        const existingTags = await db.getAllTags();
        const isDuplicate = existingTags.some(t => 
            t.name === tagData.name && t.id !== tagData.id
        );
        
        if (isDuplicate) {
            showToast('该标签已存在');
            return;
        }
        
        // 创建标签对象
        const tag = {
            id: tagData.id || Date.now(),
            name: tagData.name,
            description: tagData.description,
            color: tagData.color || '#409EFF',
            createdAt: tagData.createdAt || new Date().toISOString(),
            updatedAt: new Date().toISOString()
        };
        
        // 保存到数据库
        if (tagData.id) {
            await db.updateTag(tag);
            showToast('标签已更新');
        } else {
            await db.addTag(tag);
            showToast('标签已创建');
        }
        
        // 关闭模态框
        closeModal();
        
        // 重新加载列表
        loadTags();
        
        // 通知原生层
        if (window.cordova) {
            cordova.exec(
                (result) => console.log('Tag saved:', result),
                (error) => console.error('Save error:', error),
                'TagPlugin',
                'onTagSaved',
                [{ tagId: tag.id, timestamp: Date.now() }]
            );
        }
    } catch (error) {
        console.error('Error saving tag:', error);
        showToast('保存失败,请重试');
    }
}

保存标签函数验证标签名称的唯一性,然后保存到数据库。saveTag 函数是标签管理的核心保存函数,负责处理标签的创建和更新。函数首先进行数据验证,确保标签名称不为空。然后检查标签名称是否已存在,防止创建重复的标签。这个检查很重要,因为标签的唯一性对于数据的组织至关重要。检查时需要排除当前编辑的标签(通过比较 ID),以支持编辑操作。然后构建一个完整的标签对象,包含名称、描述、颜色等信息。函数通过检查 tagData.id 来判断是新建还是编辑操作。保存完成后,函数关闭模态框、重新加载列表,并通过 Cordova 插件通知原生层进行缓存更新。这种设计确保了标签数据的完整性和一致性。

编辑标签函数

async function editTag(tagId) {
    try {
        // 获取标签数据
        const tag = await db.getTag(tagId);
        
        if (tag) {
            // 打开编辑模态框
            openTagModal(tag);
        }
    } catch (error) {
        console.error('Error loading tag:', error);
        showToast('加载标签失败');
    }
}

编辑函数获取标签数据,然后打开编辑模态框。

删除标签函数

async function deleteTag(tagId) {
    if (!confirm('确定要删除这个标签吗?')) {
        return;
    }
    
    try {
        // 获取关联的旅行
        const trips = await db.getTripsByTag(tagId);
        
        // 从所有旅行中移除该标签
        for (let trip of trips) {
            trip.tags = trip.tags.filter(t => t !== tagId);
            await db.updateTrip(trip);
        }
        
        // 删除标签
        await db.deleteTag(tagId);
        
        showToast('标签已删除');
        
        // 从列表移除
        const element = document.getElementById(`tag-${tagId}`);
        if (element) {
            element.remove();
        }
        
        // 通知原生层
        if (window.cordova) {
            cordova.exec(
                (result) => console.log('Tag deleted:', result),
                (error) => console.error('Delete error:', error),
                'TagPlugin',
                'onTagDeleted',
                [{ tagId: tagId, timestamp: Date.now() }]
            );
        }
    } catch (error) {
        console.error('Error deleting tag:', error);
        showToast('删除失败,请重试');
    }
}

删除标签函数首先从所有关联的旅行中移除该标签,然后删除标签本身。deleteTag 函数处理标签的删除操作,这是一个相对复杂的操作,因为需要处理标签与旅行的关联关系。函数首先显示确认对话框,防止用户误删。然后查询所有关联该标签的旅行,并从每个旅行的标签列表中移除该标签。这个步骤很重要,确保了数据的一致性,避免了孤立的标签引用。接着删除标签本身。最后从页面上移除对应的 DOM 元素,并通知原生层进行相应的处理。这种设计确保了删除操作的完整性和数据的一致性。

为旅行添加标签函数

async function addTagToTrip(tripId, tagId) {
    try {
        // 获取旅行数据
        const trip = await db.getTrip(tripId);
        
        if (trip) {
            // 初始化标签数组
            if (!trip.tags) {
                trip.tags = [];
            }
            
            // 检查标签是否已添加
            if (!trip.tags.includes(tagId)) {
                trip.tags.push(tagId);
                
                // 保存到数据库
                await db.updateTrip(trip);
                
                showToast('标签已添加');
            }
        }
    } catch (error) {
        console.error('Error adding tag to trip:', error);
        showToast('添加标签失败');
    }
}

为旅行添加标签函数将标签 ID 添加到旅行的标签数组中。addTagToTrip 函数处理为旅行添加标签的操作。函数首先获取旅行数据,然后检查旅行是否有标签数组,如果没有则初始化一个空数组。接着检查标签是否已经添加到旅行中,防止添加重复的标签。如果标签未添加,则将标签 ID 添加到标签数组中,并保存到数据库。这个函数提供了一个简单的接口,用于在旅行编辑页面中快速添加标签。通过这个函数,用户可以灵活地为旅行分配标签,实现更好的组织和分类。

🔌 OpenHarmony 原生代码实现

标签缓存插件

// TagPlugin.ets
import { BusinessError } from '@ohos.base';

export class TagPlugin {
    private tagCache: Map<number, any> = new Map();
    private cacheExpireTime: number = 10 * 60 * 1000; // 10分钟过期
    
    // 处理标签保存事件
    onTagSaved(args: any, callback: Function): void {
        try {
            const tagId = args[0].tagId;
            const timestamp = args[0].timestamp;
            
            // 清除缓存
            this.tagCache.clear();
            
            console.log(`[Tag] Saved: ${tagId} at ${timestamp}`);
            
            callback({ success: true, message: '标签已保存' });
        } catch (error) {
            callback({ success: false, error: error.message });
        }
    }
    
    // 处理标签删除事件
    onTagDeleted(args: any, callback: Function): void {
        try {
            const tagId = args[0].tagId;
            
            // 清除缓存
            this.tagCache.delete(tagId);
            
            console.log(`[Tag] Deleted: ${tagId}`);
            
            callback({ success: true, message: '标签已删除' });
        } catch (error) {
            callback({ success: false, error: error.message });
        }
    }
    
    // 获取标签缓存
    getTagCache(args: any, callback: Function): void {
        try {
            const tagId = args[0].tagId;
            const cacheEntry = this.tagCache.get(tagId);
            
            if (cacheEntry && Date.now() - cacheEntry.timestamp < this.cacheExpireTime) {
                callback({ success: true, data: cacheEntry.data });
            } else {
                callback({ success: false, data: null });
            }
        } catch (error) {
            callback({ success: false, error: error.message });
        }
    }
}

标签缓存插件在原生层维护标签的内存缓存,提高查询效率。TagPlugin 是 OpenHarmony 原生层的标签管理插件,负责处理与标签相关的原生操作。插件维护了一个标签缓存 Map,用于存储最近访问的标签信息。缓存设置了 10 分钟的过期时间,确保缓存数据的新鲜度。onTagSaved 方法处理标签保存事件,当标签被保存时,清除整个缓存以确保数据一致性。onTagDeleted 方法处理标签删除事件,删除缓存中的相应标签数据。getTagCache 方法提供了获取缓存数据的接口,检查缓存是否存在且未过期。通过这个插件,Web 层可以充分利用原生层的缓存机制,提高标签查询的性能。

📝 总结

标签管理功能展示了如何在 Cordova 与 OpenHarmony 框架中实现一个灵活的标签系统。Web 层负责 UI 渲染和标签操作,原生层负责缓存管理。通过标签管理,用户可以灵活地组织和分类旅行记录,提升了应用的组织能力。

Logo

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

更多推荐