旅行记录应用回收站管理 - Cordova & OpenHarmony 混合开发实战
本文介绍了开源鸿蒙跨平台开发中的回收站功能实现方案。该功能采用软删除机制,通过标记isDeleted状态而非物理删除数据,支持用户恢复误删记录。技术实现包含三个核心部分:1)软删除机制与标记管理,2)回收站列表展示与管理界面,3)原生层历史记录与自动清理功能。文章详细展示了Web端的HTML页面结构、JavaScript软删除函数、回收站列表加载与渲染等关键代码实现,为开发者提供了完整的跨平台回收
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
📌 概述
回收站功能允许用户安全地删除旅行记录。被删除的旅行不会立即从数据库中移除,而是被标记为已删除,存放在回收站中。用户可以在回收站中查看已删除的旅行,并选择恢复或永久删除。这个功能提供了数据安全保护,防止用户误删重要数据。在 Cordova 与 OpenHarmony 的混合开发框架中,回收站功能需要实现软删除机制、恢复功能和永久删除功能。
🔗 完整流程
第一步:软删除机制与标记管理
回收站功能基于软删除机制。当用户删除旅行时,不是直接从数据库中删除记录,而是将旅行的 isDeleted 标志设置为 true,并记录删除时间。这样可以保留用户的删除历史,并允许用户恢复已删除的数据。
软删除机制需要在所有查询操作中考虑 isDeleted 标志,确保已删除的旅行不会显示在正常列表中。
第二步:回收站列表展示与管理
回收站页面需要展示所有已删除的旅行。用户可以在回收站中查看旅行的详情,也可以选择恢复或永久删除。回收站列表通常按删除时间排序,最近删除的旅行显示在最前面。
回收站还可以实现自动清空功能,删除超过一定时间(如 30 天)的旅行。
第三步:原生层删除历史记录与清理机制
OpenHarmony 原生层可以实现删除历史的记录和管理。原生层可以维护一个删除历史日志,记录所有删除操作的时间和用户信息。原生层还可以实现自动清理机制,定期清理过期的已删除旅行。
🔧 Web 代码实现
回收站页面 HTML 结构
<div id="trash-page" class="page">
<div class="page-header">
<h1>回收站</h1>
<button class="btn-icon" onclick="emptyTrash()">🗑️</button>
</div>
<div class="trash-container">
<div class="trash-list" id="trashList">
<!-- 已删除旅行列表动态加载 -->
</div>
<div class="empty-state" id="emptyState" style="display: none;">
<div class="empty-icon">🗑️</div>
<p>回收站为空</p>
</div>
</div>
</div>
HTML 结构包含已删除旅行列表和清空回收站按钮。用户可以一键清空所有已删除的旅行。
软删除函数
async function deleteTrip(tripId) {
if (!confirm('确定要删除这个旅行吗?')) {
return;
}
try {
// 获取旅行数据
const trip = await db.getTrip(tripId);
if (trip) {
// 标记为已删除
trip.isDeleted = true;
trip.deletedAt = new Date().toISOString();
// 保存到数据库
await db.updateTrip(trip);
showToast('旅行已删除,可在回收站中恢复');
// 刷新当前页面
if (window.currentPage === 'all-trips') {
loadTrips(1);
}
// 通知原生层
if (window.cordova) {
cordova.exec(
(result) => console.log('Trip deleted:', result),
(error) => console.error('Delete error:', error),
'TrashPlugin',
'onTripDeleted',
[{ tripId: tripId, timestamp: Date.now() }]
);
}
}
} catch (error) {
console.error('Error deleting trip:', error);
showToast('删除失败,请重试');
}
}
软删除函数将旅行标记为已删除,并记录删除时间。函数不会从数据库中移除旅行,而是保留数据以便恢复。
加载回收站列表函数
async function loadTrash() {
try {
// 从数据库查询所有旅行
const allTrips = await db.getAllTrips();
// 筛选出已删除的旅行
const trashedTrips = allTrips
.filter(trip => trip.isDeleted === true)
.sort((a, b) => {
const timeA = new Date(a.deletedAt).getTime();
const timeB = new Date(b.deletedAt).getTime();
return timeB - timeA;
});
// 处理空状态
if (trashedTrips.length === 0) {
document.getElementById('emptyState').style.display = 'block';
document.getElementById('trashList').innerHTML = '';
return;
}
document.getElementById('emptyState').style.display = 'none';
// 渲染回收站列表
renderTrashList(trashedTrips);
} catch (error) {
console.error('Error loading trash:', error);
showToast('加载回收站失败');
}
}
这个函数从数据库查询所有已删除的旅行,按删除时间降序排序。函数处理空状态,当回收站为空时显示提示信息。
回收站列表渲染函数
function renderTrashList(trips) {
const container = document.getElementById('trashList');
container.innerHTML = '';
trips.forEach(trip => {
const trashElement = document.createElement('div');
trashElement.className = 'trash-item';
trashElement.id = `trash-${trip.id}`;
const deletedTime = new Date(trip.deletedAt);
const timeAgo = getTimeAgoString(deletedTime);
trashElement.innerHTML = `
<div class="trash-header">
<h3>${trip.destination}</h3>
<span class="deleted-time">${timeAgo}删除</span>
</div>
<div class="trash-body">
<p class="trip-description">${trip.description || '暂无描述'}</p>
<div class="trip-meta">
<span>📅 ${formatDate(trip.startDate)}</span>
<span>💰 ¥${trip.expense}</span>
</div>
</div>
<div class="trash-footer">
<button class="btn-small" onclick="restoreTrip(${trip.id})">
恢复
</button>
<button class="btn-small btn-danger" onclick="permanentlyDeleteTrip(${trip.id})">
永久删除
</button>
</div>
`;
container.appendChild(trashElement);
});
}
回收站列表渲染函数为每个已删除的旅行创建一个 DOM 元素。每个元素包含恢复和永久删除两个操作按钮。
恢复旅行函数
async function restoreTrip(tripId) {
try {
// 获取旅行数据
const trip = await db.getTrip(tripId);
if (trip) {
// 取消删除标记
trip.isDeleted = false;
trip.deletedAt = null;
// 保存到数据库
await db.updateTrip(trip);
showToast('旅行已恢复');
// 从回收站移除该项
const element = document.getElementById(`trash-${tripId}`);
if (element) {
element.remove();
}
// 重新加载回收站列表
loadTrash();
// 通知原生层
if (window.cordova) {
cordova.exec(
(result) => console.log('Trip restored:', result),
(error) => console.error('Restore error:', error),
'TrashPlugin',
'onTripRestored',
[{ tripId: tripId, timestamp: Date.now() }]
);
}
}
} catch (error) {
console.error('Error restoring trip:', error);
showToast('恢复失败,请重试');
}
}
恢复函数将旅行的 isDeleted 标志设置为 false,使旅行重新出现在正常列表中。函数还通知原生层恢复操作。
永久删除函数
async function permanentlyDeleteTrip(tripId) {
if (!confirm('确定要永久删除这个旅行吗?此操作无法撤销。')) {
return;
}
try {
// 从数据库永久删除
await db.deleteTrip(tripId);
showToast('旅行已永久删除');
// 从回收站移除该项
const element = document.getElementById(`trash-${tripId}`);
if (element) {
element.remove();
}
// 重新加载回收站列表
loadTrash();
// 通知原生层
if (window.cordova) {
cordova.exec(
(result) => console.log('Trip permanently deleted:', result),
(error) => console.error('Delete error:', error),
'TrashPlugin',
'onTripPermanentlyDeleted',
[{ tripId: tripId, timestamp: Date.now() }]
);
}
} catch (error) {
console.error('Error permanently deleting trip:', error);
showToast('永久删除失败,请重试');
}
}
永久删除函数直接从数据库中删除旅行记录。这个操作是不可逆的,需要用户确认。
清空回收站函数
async function emptyTrash() {
if (!confirm('确定要清空回收站吗?此操作无法撤销。')) {
return;
}
try {
// 获取所有已删除的旅行
const allTrips = await db.getAllTrips();
const trashedTrips = allTrips.filter(trip => trip.isDeleted === true);
// 永久删除所有已删除的旅行
for (let trip of trashedTrips) {
await db.deleteTrip(trip.id);
}
showToast('回收站已清空');
// 重新加载回收站列表
loadTrash();
// 通知原生层
if (window.cordova) {
cordova.exec(
(result) => console.log('Trash emptied:', result),
(error) => console.error('Empty error:', error),
'TrashPlugin',
'onTrashEmptied',
[{ timestamp: Date.now() }]
);
}
} catch (error) {
console.error('Error emptying trash:', error);
showToast('清空回收站失败');
}
}
清空回收站函数永久删除所有已删除的旅行。这个操作需要用户确认,确保用户不会误操作。
🔌 OpenHarmony 原生代码实现
回收站管理插件
// TrashPlugin.ets
import { BusinessError } from '@ohos.base';
export class TrashPlugin {
private deleteHistory: Map<number, number> = new Map(); // tripId -> deleteTime
private autoCleanupInterval: number = 30 * 24 * 60 * 60 * 1000; // 30天
// 处理旅行删除事件
onTripDeleted(args: any, callback: Function): void {
try {
const tripId = args[0].tripId;
const timestamp = args[0].timestamp;
// 记录删除时间
this.deleteHistory.set(tripId, timestamp);
console.log(`[Trash] Trip ${tripId} deleted at ${timestamp}`);
callback({ success: true, message: '删除记录已保存' });
} catch (error) {
callback({ success: false, error: error.message });
}
}
// 处理旅行恢复事件
onTripRestored(args: any, callback: Function): void {
try {
const tripId = args[0].tripId;
// 删除删除记录
this.deleteHistory.delete(tripId);
console.log(`[Trash] Trip ${tripId} restored`);
callback({ success: true, message: '恢复记录已更新' });
} catch (error) {
callback({ success: false, error: error.message });
}
}
// 处理永久删除事件
onTripPermanentlyDeleted(args: any, callback: Function): void {
try {
const tripId = args[0].tripId;
// 删除删除记录
this.deleteHistory.delete(tripId);
console.log(`[Trash] Trip ${tripId} permanently deleted`);
callback({ success: true, message: '永久删除记录已更新' });
} catch (error) {
callback({ success: false, error: error.message });
}
}
// 处理回收站清空事件
onTrashEmptied(args: any, callback: Function): void {
try {
this.deleteHistory.clear();
console.log('[Trash] Trash emptied');
callback({ success: true, message: '回收站已清空' });
} catch (error) {
callback({ success: false, error: error.message });
}
}
// 自动清理过期的已删除旅行
performAutoCleanup(callback: Function): void {
try {
const now = Date.now();
const toDelete: number[] = [];
this.deleteHistory.forEach((deleteTime, tripId) => {
if (now - deleteTime > this.autoCleanupInterval) {
toDelete.push(tripId);
}
});
toDelete.forEach(tripId => {
this.deleteHistory.delete(tripId);
});
console.log(`[Trash] Auto cleanup: ${toDelete.length} trips deleted`);
callback({ success: true, deletedCount: toDelete.length });
} catch (error) {
callback({ success: false, error: error.message });
}
}
}
回收站管理插件在原生层维护删除历史记录。插件实现了自动清理机制,定期清理超过 30 天的已删除旅行。
🛡️ 软删除 vs 硬删除
软删除和硬删除是两种不同的删除策略。硬删除直接从数据库中删除数据,无法恢复。软删除只是标记数据为已删除,数据仍然保存在数据库中。
软删除的优点是可以恢复数据,提供了数据安全保护。缺点是会占用更多的存储空间,并且需要在所有查询中考虑 isDeleted 标志。硬删除的优点是节省存储空间,缺点是无法恢复数据。
在实际应用中,通常使用软删除来保护用户数据。用户可以在回收站中恢复误删的数据。经过一段时间后(如 30 天),再进行硬删除,彻底清理数据库。
🔄 删除流程的设计
删除流程应该分为两个阶段:软删除和硬删除。在软删除阶段,用户可以随时恢复数据。在硬删除阶段,数据被彻底删除,无法恢复。
在软删除和硬删除之间应该有一个缓冲期,给用户足够的时间来决定是否恢复数据。这个缓冲期通常是 30 天。
📋 回收站的管理
回收站需要展示所有已删除的数据。用户可以在回收站中查看删除的数据,也可以选择恢复或永久删除。回收站应该按删除时间排序,最近删除的数据显示在最前面。
回收站还应该显示每个数据的删除时间和剩余的恢复时间。例如,如果一个旅行在 25 天前被删除,还有 5 天就会被永久删除,应该显示"5 天后永久删除"。这样可以提醒用户及时恢复重要数据。
🔐 删除权限与审计
在多用户应用中,删除操作需要权限控制。只有数据的所有者或管理员才能删除数据。删除操作应该被记录在审计日志中,用于追踪谁删除了什么数据。
对于重要的数据,可以实现二次确认机制。用户需要输入密码或进行其他验证才能删除数据。这样可以防止误删。
💾 删除历史的记录
删除操作应该被记录在删除历史中。删除历史包括删除的数据、删除时间、删除者等信息。这些信息可以用于审计和恢复。
删除历史应该被保存在一个单独的表中,与正常的数据分离。这样可以避免删除历史被意外删除。
🧹 自动清理机制
回收站应该实现自动清理机制。超过一定时间(如 30 天)的已删除数据应该被自动永久删除。这样可以避免回收站无限增长。
自动清理应该在后台进行,不影响用户的正常使用。可以在应用启动时或定期执行清理任务。
📊 删除统计与分析
删除数据可以提供一些有趣的统计信息。例如,可以统计每天有多少数据被删除,哪些数据最容易被删除等。这些统计信息可以帮助改进应用的设计。
如果发现某个功能的数据经常被删除,可能说明这个功能有问题,需要改进。
🔔 删除通知
当用户删除数据时,应该显示一个通知,告诉用户数据已被删除,可以在回收站中恢复。这样可以防止用户误删数据后不知道如何恢复。
对于重要的删除操作,可以发送一个更显眼的通知,甚至可以提供一个快速恢复按钮。
📝 总结
回收站管理功能展示了如何在 Cordova 与 OpenHarmony 框架中实现一个安全的删除机制。通过软删除机制,用户可以安全地删除数据,并在需要时恢复。原生层的自动清理机制确保了数据库的整洁。这个功能提供了数据安全保护,提升了用户体验。在实现过程中,需要重点关注删除流程设计、权限控制、自动清理等方面,确保回收站功能能够提供最佳的用户体验。
更多推荐




所有评论(0)