编辑记录模块 Cordova 与 OpenHarmony 混合开发实战
摘要 编辑记录模块提供了修改喝茶记录的功能,支持数据加载、表单验证和更新操作。主要流程包括:1) 根据ID加载记录详情和可选分类;2) 填充表单并实时验证用户修改;3) 保存时同步更新至IndexedDB和原生数据库。模块采用HTML表单实现编辑界面,包含必填字段、星级评分和备注等元素,通过JavaScript处理数据加载、表单交互和数据库同步逻辑,确保数据一致性并保留修改历史记录。
📌 概述
编辑记录模块允许用户修改已保存的喝茶记录信息。该模块集成了 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
更多推荐


所有评论(0)