📌 概述

编辑记录模块允许用户修改已保存的喝茶记录信息。该模块集成了 Cordova 框架与 OpenHarmony 原生能力,提供了完整的数据加载、表单验证和更新功能。用户可以修改茶叶类型、品尝日期、消费金额、品质评分等信息。模块支持修改历史记录,用户可以查看每次修改的时间和修改内容。编辑操作会自动同步到原生数据库,确保数据的一致性。

🔗 完整流程

第一步:记录数据加载

当用户点击编辑按钮时,应用会根据记录 ID 从数据库中加载该记录的详细信息。应用会显示加载动画直到数据加载完成。同时,应用会加载所有可用的茶叶分类和产地信息,用于填充下拉选择框。为了提高用户体验,应用会记录原始数据,以便用户可以取消修改或查看修改前的数据。

第二步:表单填充与交互

数据加载完成后,应用会将记录信息填充到编辑表单中。用户可以修改任何字段的信息。应用会实时验证用户输入的数据,确保数据的有效性。当用户修改数据时,应用会标记该字段为已修改,以便后续的更新操作。应用支持撤销修改,用户可以点击"重置"按钮恢复原始数据。

第三步:数据保存与同步

当用户点击"保存"按钮时,应用会进行最终的数据验证。如果所有字段都通过验证,应用会将修改后的数据保存到 IndexedDB 数据库中。同时,应用会通过 Cordova 调用原生插件,将数据同步到应用的关系型数据库中,并记录修改历史。保存完成后,应用会显示成功提示并返回到记录列表页面。

🔧 Web 代码实现

HTML 编辑表单

<div id="edit-record-page" class="page">
    <div class="page-header">
        <h1>编辑记录</h1>
        <button class="btn-icon" onclick="viewHistory()">📜 历史</button>
    </div>
    
    <form id="edit-record-form" class="form">
        <div class="form-group">
            <label for="edit-tea-type">茶叶类型 *</label>
            <select id="edit-tea-type" name="teaType" required>
                <option value="">请选择茶叶类型</option>
            </select>
            <span class="error-message" id="edit-tea-type-error"></span>
        </div>
        
        <div class="form-group">
            <label for="edit-tea-origin">产地 *</label>
            <select id="edit-tea-origin" name="origin" required>
                <option value="">请选择产地</option>
            </select>
            <span class="error-message" id="edit-origin-error"></span>
        </div>
        
        <div class="form-group">
            <label for="edit-record-date">品尝日期 *</label>
            <input type="date" id="edit-record-date" name="date" required>
            <span class="error-message" id="edit-date-error"></span>
        </div>
        
        <div class="form-group">
            <label for="edit-price">消费金额 (元)</label>
            <input type="number" id="edit-price" name="price" min="0" step="0.01">
            <span class="error-message" id="edit-price-error"></span>
        </div>
        
        <div class="form-group">
            <label for="edit-rating">品质评分 (1-5)</label>
            <div class="rating-input" id="edit-rating-input">
                <span class="star" data-value="1"></span>
                <span class="star" data-value="2"></span>
                <span class="star" data-value="3"></span>
                <span class="star" data-value="4"></span>
                <span class="star" data-value="5"></span>
            </div>
            <input type="hidden" id="edit-rating" name="rating" value="0">
        </div>
        
        <div class="form-group">
            <label for="edit-notes">备注</label>
            <textarea id="edit-notes" name="notes" rows="4"></textarea>
        </div>
        
        <div class="form-actions">
            <button type="submit" class="btn btn-primary">保存修改</button>
            <button type="button" class="btn btn-secondary" onclick="resetForm()">重置</button>
            <button type="button" class="btn btn-danger" onclick="deleteRecord()">删除记录</button>
        </div>
    </form>
</div>

编辑表单与添加记录表单类似,但包含了额外的历史记录按钮和删除按钮。表单中的所有字段都会被填充为现有的记录数据。

编辑逻辑实现

let currentRecordId = null;
let originalRecord = null;

async function initEditRecord(recordId) {
    try {
        currentRecordId = recordId;
        
        // 加载记录数据
        const record = await db.getRecord(recordId);
        if (!record) {
            showToast('记录不存在', 'error');
            navigateTo('record-list');
            return;
        }
        
        originalRecord = JSON.parse(JSON.stringify(record));
        
        // 加载分类和产地
        const categories = await db.getTeaCategories();
        const origins = await db.getOrigins();
        
        // 填充下拉选择框
        populateSelectOptions('edit-tea-type', categories, record.teaType);
        populateSelectOptions('edit-tea-origin', origins, record.origin);
        
        // 填充表单数据
        document.getElementById('edit-tea-type').value = record.teaType;
        document.getElementById('edit-tea-origin').value = record.origin;
        document.getElementById('edit-record-date').value = record.date;
        document.getElementById('edit-price').value = record.price || 0;
        document.getElementById('edit-notes').value = record.notes || '';
        
        // 设置评分
        setRating('edit-rating-input', 'edit-rating', record.rating);
        
        // 绑定事件
        document.getElementById('edit-record-form').addEventListener('submit', handleEditRecord);
        bindEditRatingStars();
        
    } catch (error) {
        console.error('Failed to init edit form:', error);
        showToast('加载记录失败', 'error');
    }
}

function populateSelectOptions(selectId, options, selectedValue) {
    const select = document.getElementById(selectId);
    options.forEach(opt => {
        const option = document.createElement('option');
        option.value = opt.id;
        option.textContent = opt.name;
        if (opt.id === selectedValue) {
            option.selected = true;
        }
        select.appendChild(option);
    });
}

function setRating(containerId, inputId, rating) {
    const container = document.getElementById(containerId);
    const input = document.getElementById(inputId);
    const stars = container.querySelectorAll('.star');
    
    input.value = rating;
    stars.forEach(star => {
        if (star.dataset.value <= rating) {
            star.classList.add('active');
        }
    });
}

function bindEditRatingStars() {
    const stars = document.querySelectorAll('#edit-rating-input .star');
    const ratingInput = document.getElementById('edit-rating');
    
    stars.forEach(star => {
        star.addEventListener('click', function() {
            const value = this.dataset.value;
            ratingInput.value = value;
            
            stars.forEach(s => {
                if (s.dataset.value <= value) {
                    s.classList.add('active');
                } else {
                    s.classList.remove('active');
                }
            });
        });
    });
}

async function handleEditRecord(event) {
    event.preventDefault();
    
    const formData = new FormData(document.getElementById('edit-record-form'));
    const updatedRecord = {
        id: currentRecordId,
        teaType: formData.get('teaType'),
        origin: formData.get('origin'),
        date: formData.get('date'),
        price: parseFloat(formData.get('price')) || 0,
        rating: parseInt(formData.get('rating')) || 0,
        notes: formData.get('notes'),
        updatedAt: new Date().toISOString()
    };
    
    // 验证数据
    const errors = validateRecord(updatedRecord);
    if (Object.keys(errors).length > 0) {
        displayErrors(errors);
        return;
    }
    
    try {
        // 保存修改
        await db.updateRecord(currentRecordId, updatedRecord);
        
        // 记录修改历史
        const changes = getChanges(originalRecord, updatedRecord);
        await db.addModificationHistory(currentRecordId, changes);
        
        // 调用原生插件
        if (window.cordova) {
            cordova.exec(
                function() { console.log('Record updated'); },
                function(err) { console.error('Error:', err); },
                'TeaLogger',
                'logEvent',
                ['record_updated', { recordId: currentRecordId }]
            );
            
            cordova.exec(
                null, null,
                'HapticFeedback',
                'vibrate',
                [{ type: 'success' }]
            );
        }
        
        showToast('记录已更新', 'success');
        setTimeout(() => navigateTo('record-list'), 1000);
    } catch (error) {
        console.error('Failed to update record:', error);
        showToast('更新失败,请重试', 'error');
    }
}

function getChanges(original, updated) {
    const changes = [];
    
    Object.keys(updated).forEach(key => {
        if (original[key] !== updated[key]) {
            changes.push({
                field: key,
                oldValue: original[key],
                newValue: updated[key],
                changedAt: new Date().toISOString()
            });
        }
    });
    
    return changes;
}

function resetForm() {
    if (originalRecord) {
        document.getElementById('edit-tea-type').value = originalRecord.teaType;
        document.getElementById('edit-tea-origin').value = originalRecord.origin;
        document.getElementById('edit-record-date').value = originalRecord.date;
        document.getElementById('edit-price').value = originalRecord.price || 0;
        document.getElementById('edit-notes').value = originalRecord.notes || '';
        setRating('edit-rating-input', 'edit-rating', originalRecord.rating);
        showToast('已重置为原始数据', 'info');
    }
}

async function viewHistory() {
    const history = await db.getModificationHistory(currentRecordId);
    // 显示修改历史的模态框
    showHistoryModal(history);
}

这段代码实现了完整的编辑流程。initEditRecord() 加载记录数据并填充表单。handleEditRecord() 处理表单提交,验证数据并保存修改。getChanges() 比较原始数据和修改后的数据,生成修改记录。resetForm() 允许用户撤销修改。viewHistory() 显示修改历史。

🔌 OpenHarmony 原生代码

修改历史记录

// entry/src/main/ets/plugins/ModificationHistory.ets
import { relationalStore } from '@kit.ArkData';

export class ModificationHistory {
    private store: relationalStore.RdbStore;
    
    async createHistoryTable(): Promise<void> {
        const createTableSql = `
            CREATE TABLE IF NOT EXISTS modification_history (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                record_id INTEGER NOT NULL,
                field TEXT NOT NULL,
                old_value TEXT,
                new_value TEXT,
                changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY(record_id) REFERENCES tea_records(id)
            )
        `;
        
        await this.store.executeSql(createTableSql);
    }
    
    async addHistory(recordId: number, changes: ModificationRecord[]): Promise<void> {
        for (const change of changes) {
            const values: relationalStore.ValuesBucket = {
                record_id: recordId,
                field: change.field,
                old_value: change.oldValue?.toString(),
                new_value: change.newValue?.toString(),
                changed_at: change.changedAt
            };
            
            await this.store.insert('modification_history', values);
        }
        
        hilog.info(0xFF00, 'ModificationHistory', `Added ${changes.length} history records`);
    }
    
    async getHistory(recordId: number): Promise<ModificationRecord[]> {
        const predicates = new relationalStore.RdbPredicates('modification_history');
        predicates.equalTo('record_id', recordId).orderByDesc('changed_at');
        
        const resultSet = await this.store.query(predicates);
        const history: ModificationRecord[] = [];
        
        while (resultSet.goToNextRow()) {
            history.push({
                field: resultSet.getColumnValue(resultSet.getColumnIndex('field')) as string,
                oldValue: resultSet.getColumnValue(resultSet.getColumnIndex('old_value')),
                newValue: resultSet.getColumnValue(resultSet.getColumnIndex('new_value')),
                changedAt: resultSet.getColumnValue(resultSet.getColumnIndex('changed_at')) as string
            });
        }
        
        resultSet.close();
        return history;
    }
}

interface ModificationRecord {
    field: string;
    oldValue: any;
    newValue: any;
    changedAt: string;
}

这个类管理修改历史记录。createHistoryTable() 创建修改历史表。addHistory() 添加修改记录。getHistory() 查询特定记录的修改历史。

数据更新操作

// entry/src/main/ets/plugins/DatabaseHelper.ets (扩展)
async updateTeaRecord(recordId: number, updates: Partial<TeaRecord>): Promise<void> {
    const predicates = new relationalStore.RdbPredicates('tea_records');
    predicates.equalTo('id', recordId);
    
    const values: relationalStore.ValuesBucket = {
        ...updates,
        updated_at: new Date().toISOString()
    };
    
    try {
        await this.store.update(values, predicates);
        hilog.info(0xFF00, 'DatabaseHelper', `Record ${recordId} updated successfully`);
    } catch (error) {
        hilog.error(0xFF00, 'DatabaseHelper', `Failed to update record: ${error}`);
        throw error;
    }
}

async deleteTeaRecord(recordId: number): Promise<void> {
    const predicates = new relationalStore.RdbPredicates('tea_records');
    predicates.equalTo('id', recordId);
    
    try {
        await this.store.delete(predicates);
        hilog.info(0xFF00, 'DatabaseHelper', `Record ${recordId} deleted successfully`);
    } catch (error) {
        hilog.error(0xFF00, 'DatabaseHelper', `Failed to delete record: ${error}`);
        throw error;
    }
}

这段代码提供了更新和删除记录的原生操作。updateTeaRecord() 更新指定的记录。deleteTeaRecord() 删除指定的记录。

📝 总结

编辑记录模块展示了如何在 Cordova 框架中实现完整的数据修改功能。通过 Web 层的表单处理和用户交互,结合原生层的数据库操作和修改历史记录,为用户提供了安全可靠的数据编辑体验。该模块的设计模式可以应用到其他需要数据修改的功能中。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

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

更多推荐