回收站 Cordova 与 OpenHarmony 混合开发实战
文章摘要: 回收站功能为用户提供安全的删除机制,通过将删除的笔记标记为isDeleted状态而非直接删除,支持30天内恢复或永久删除。功能实现包括:1)删除笔记时记录时间并支持30秒内撤销;2)回收站页面展示已删除笔记,按时间排序并显示剩余保留天数;3)提供恢复、永久删除和清空回收站操作。代码示例展示了Web端的页面渲染、笔记状态修改及数据库操作逻辑,同时提及OpenHarmony原生实现框架。系
📌 模块概述
回收站功能为用户提供了一个安全的删除机制。当用户删除笔记时,笔记不会立即从数据库中删除,而是被移到回收站。用户可以在回收站中查看已删除的笔记,并选择恢复或永久删除。这个功能防止了用户误删笔记导致数据丢失的情况。
回收站页面展示了所有已删除的笔记。用户可以在这个页面上恢复笔记、永久删除笔记或清空整个回收站。为了防止回收站占用过多空间,我们可以设置一个自动清空策略,比如30天后自动删除回收站中的笔记。
🔗 完整流程
第一步:删除笔记到回收站
当用户删除笔记时,我们不是直接从数据库中删除笔记,而是将笔记的isDeleted属性设置为true,并记录删除时间。这样笔记仍然存储在数据库中,但不会在正常的笔记列表中显示。
删除操作应该是可撤销的,用户可以在一定时间内(比如30秒内)撤销删除操作。这需要我们实现一个撤销栈,记录最近的操作。
第二步:加载和显示回收站
当用户进入回收站页面时,需要从数据库中加载所有isDeleted为true的笔记。这些笔记应该按照删除时间排序,最近删除的笔记排在前面。
为了提高用户体验,我们可以显示笔记的删除时间和剩余保留时间。如果笔记即将被自动删除,我们可以显示一个警告信息。
第三步:恢复和永久删除
用户可以选择恢复笔记或永久删除笔记。恢复操作将isDeleted属性设置为false,笔记就会重新出现在正常的笔记列表中。永久删除操作会从数据库中彻底删除笔记,这个操作是不可撤销的。
🔧 Web代码实现
// 回收站页面渲染函数
async renderTrash() {
// 从数据库获取所有笔记
const allNotes = await noteDB.getAllNotes();
// 过滤出已删除的笔记
const trashedNotes = allNotes.filter(note => note.isDeleted === true);
// 按删除时间排序
const sortedTrash = trashedNotes.sort((a, b) =>
new Date(b.deletedAt) - new Date(a.deletedAt)
);
return `
<div class="page active">
<div class="page-header">
<h1 class="page-title">🗑️ 回收站</h1>
<p class="page-subtitle">共 ${trashedNotes.length} 个已删除项目</p>
<div class="page-actions">
<button class="btn btn-danger" onclick="app.emptyTrash()">清空回收站</button>
</div>
</div>
`;
}
这段代码展示了回收站页面的初始化。首先获取所有笔记,然后过滤出isDeleted为true的笔记。最后按照删除时间排序。
// 生成回收站列表HTML
const trashListHTML = sortedTrash.map(note => {
const deletedTime = Utils.formatDate(note.deletedAt);
const daysLeft = Math.ceil((30 - (Date.now() - new Date(note.deletedAt).getTime()) / (1000 * 60 * 60 * 24)));
return `
<div class="table-row trash-item" data-note-id="${note.id}">
<div class="table-cell">
<strong>${Utils.escapeHtml(note.title)}</strong>
</div>
<div class="table-cell">
删除于: ${deletedTime}
</div>
<div class="table-cell">
${daysLeft > 0 ? `将在 ${daysLeft} 天后永久删除` : '即将永久删除'}
</div>
<div class="table-cell">
<button class="btn btn-sm btn-success" onclick="app.restoreNote(${note.id})">恢复</button>
<button class="btn btn-sm btn-danger" onclick="app.permanentlyDeleteNote(${note.id})">永久删除</button>
</div>
</div>
`;
}).join('');
这段代码生成了回收站列表的HTML。每个已删除的笔记都显示其删除时间和剩余保留时间。用户可以选择恢复或永久删除笔记。
// 删除笔记到回收站
async deleteNote(noteId) {
try {
const note = await noteDB.getNote(noteId);
if (!note) return;
// 设置删除标记和删除时间
note.isDeleted = true;
note.deletedAt = new Date().toISOString();
// 保存到数据库
await noteDB.updateNote(note);
// 显示撤销提示
Utils.showToast('笔记已删除,30秒内可撤销', 'info', {
action: '撤销',
callback: () => this.undoDelete(noteId)
});
// 重新渲染当前页面
await this.renderCurrentPage();
} catch (error) {
console.error('删除笔记失败:', error);
Utils.showToast('操作失败,请重试', 'error');
}
}
// 恢复笔记
async restoreNote(noteId) {
try {
const note = await noteDB.getNote(noteId);
if (!note) return;
// 清除删除标记
note.isDeleted = false;
note.deletedAt = null;
// 保存到数据库
await noteDB.updateNote(note);
// 显示成功提示
Utils.showToast('笔记已恢复', 'success');
// 重新渲染页面
await this.renderTrash();
} catch (error) {
console.error('恢复笔记失败:', error);
Utils.showToast('操作失败,请重试', 'error');
}
}
// 永久删除笔记
async permanentlyDeleteNote(noteId) {
// 显示确认对话框
if (!confirm('确定要永久删除这个笔记吗?此操作不可撤销。')) {
return;
}
try {
// 从数据库中删除笔记
await noteDB.deleteNote(noteId);
// 显示成功提示
Utils.showToast('笔记已永久删除', 'success');
// 重新渲染页面
await this.renderTrash();
} catch (error) {
console.error('永久删除笔记失败:', error);
Utils.showToast('操作失败,请重试', 'error');
}
}
// 清空回收站
async emptyTrash() {
// 显示确认对话框
if (!confirm('确定要清空回收站吗?此操作不可撤销。')) {
return;
}
try {
// 获取所有已删除的笔记
const allNotes = await noteDB.getAllNotes();
const trashedNotes = allNotes.filter(note => note.isDeleted === true);
// 永久删除所有已删除的笔记
for (const note of trashedNotes) {
await noteDB.deleteNote(note.id);
}
// 显示成功提示
Utils.showToast(`已清空 ${trashedNotes.length} 个项目`, 'success');
// 重新渲染页面
await this.renderTrash();
} catch (error) {
console.error('清空回收站失败:', error);
Utils.showToast('操作失败,请重试', 'error');
}
}
这段代码实现了回收站的核心功能。deleteNote()方法将笔记标记为已删除,并显示一个撤销提示。restoreNote()方法恢复已删除的笔记。permanentlyDeleteNote()方法从数据库中彻底删除笔记。emptyTrash()方法清空整个回收站。
🔌 OpenHarmony 原生代码
// TrashPlugin.ets - 回收站管理插件
import { webview } from '@kit.ArkWeb';
import { common } from '@kit.AbilityKit';
import { fileIo } from '@kit.CoreFileKit';
@NativeComponent
export class TrashPlugin {
private context: common.UIAbilityContext;
constructor(context: common.UIAbilityContext) {
this.context = context;
}
// 初始化插件
public init(webviewController: webview.WebviewController): void {
webviewController.registerJavaScriptProxy(
new TrashJSProxy(this),
'trashPlugin',
['getTrash', 'restoreNote', 'permanentlyDelete', 'emptyTrash', 'autoCleanup']
);
}
// 获取回收站中的笔记
public getTrash(): Promise<Array<any>> {
return new Promise((resolve) => {
try {
const notesPath = this.context.cacheDir + '/notes.json';
const content = fileIo.readTextSync(notesPath);
const allNotes = JSON.parse(content);
// 过滤出已删除的笔记
const trashedNotes = allNotes.filter((note: any) => note.isDeleted === true);
// 按删除时间排序
trashedNotes.sort((a: any, b: any) =>
new Date(b.deletedAt).getTime() - new Date(a.deletedAt).getTime()
);
resolve(trashedNotes);
} catch (error) {
console.error('Failed to get trash:', error);
resolve([]);
}
});
}
// 恢复笔记
public restoreNote(noteId: number): Promise<boolean> {
return new Promise((resolve) => {
try {
const notesPath = this.context.cacheDir + '/notes.json';
const content = fileIo.readTextSync(notesPath);
const allNotes = JSON.parse(content);
// 查找并恢复笔记
const note = allNotes.find((n: any) => n.id === noteId);
if (note) {
note.isDeleted = false;
note.deletedAt = null;
fileIo.writeTextSync(notesPath, JSON.stringify(allNotes, null, 2));
resolve(true);
} else {
resolve(false);
}
} catch (error) {
console.error('Failed to restore note:', error);
resolve(false);
}
});
}
// 永久删除笔记
public permanentlyDelete(noteId: number): Promise<boolean> {
return new Promise((resolve) => {
try {
const notesPath = this.context.cacheDir + '/notes.json';
const content = fileIo.readTextSync(notesPath);
let allNotes = JSON.parse(content);
// 删除笔记
allNotes = allNotes.filter((n: any) => n.id !== noteId);
fileIo.writeTextSync(notesPath, JSON.stringify(allNotes, null, 2));
resolve(true);
} catch (error) {
console.error('Failed to permanently delete:', error);
resolve(false);
}
});
}
// 清空回收站
public emptyTrash(): Promise<number> {
return new Promise((resolve) => {
try {
const notesPath = this.context.cacheDir + '/notes.json';
const content = fileIo.readTextSync(notesPath);
let allNotes = JSON.parse(content);
// 计算要删除的笔记数
const trashedCount = allNotes.filter((n: any) => n.isDeleted === true).length;
// 删除所有已删除的笔记
allNotes = allNotes.filter((n: any) => n.isDeleted !== true);
fileIo.writeTextSync(notesPath, JSON.stringify(allNotes, null, 2));
resolve(trashedCount);
} catch (error) {
console.error('Failed to empty trash:', error);
resolve(0);
}
});
}
// 自动清理过期的笔记
public autoCleanup(): Promise<number> {
return new Promise((resolve) => {
try {
const notesPath = this.context.cacheDir + '/notes.json';
const content = fileIo.readTextSync(notesPath);
let allNotes = JSON.parse(content);
const now = Date.now();
const thirtyDaysMs = 30 * 24 * 60 * 60 * 1000;
// 删除超过30天的已删除笔记
const beforeCount = allNotes.length;
allNotes = allNotes.filter((note: any) => {
if (note.isDeleted && note.deletedAt) {
const deletedTime = new Date(note.deletedAt).getTime();
return now - deletedTime < thirtyDaysMs;
}
return true;
});
const deletedCount = beforeCount - allNotes.length;
fileIo.writeTextSync(notesPath, JSON.stringify(allNotes, null, 2));
resolve(deletedCount);
} catch (error) {
console.error('Failed to auto cleanup:', error);
resolve(0);
}
});
}
}
// TrashJSProxy.ets - JavaScript代理类
class TrashJSProxy {
private plugin: TrashPlugin;
constructor(plugin: TrashPlugin) {
this.plugin = plugin;
}
getTrash(): void {
this.plugin.getTrash().then(notes => {
console.log('Trash loaded:', notes.length);
});
}
restoreNote(noteId: number): void {
this.plugin.restoreNote(noteId).then(success => {
console.log('Note restored:', success);
});
}
permanentlyDelete(noteId: number): void {
this.plugin.permanentlyDelete(noteId).then(success => {
console.log('Note permanently deleted:', success);
});
}
emptyTrash(): void {
this.plugin.emptyTrash().then(count => {
console.log('Trash emptied, deleted:', count);
});
}
autoCleanup(): void {
this.plugin.autoCleanup().then(count => {
console.log('Auto cleanup completed, deleted:', count);
});
}
}
这段OpenHarmony原生代码展示了如何在原生层实现回收站的管理。TrashPlugin类提供了获取回收站、恢复笔记、永久删除和自动清理的功能。
autoCleanup()方法实现了自动清理功能,删除超过30天的已删除笔记。这个方法可以定期调用,防止回收站占用过多空间。
Web-Native 通信
// 在Web端调用原生方法获取回收站
async function getTrashFromNative() {
return new Promise((resolve) => {
cordova.exec(
function(notes) {
console.log('Trash from native:', notes);
resolve(notes);
},
function(error) {
console.error('Failed to get trash:', error);
resolve([]);
},
'TrashPlugin',
'getTrash',
[]
);
});
}
// 在Web端调用原生方法清空回收站
async function emptyTrashNative() {
return new Promise((resolve) => {
cordova.exec(
function(count) {
console.log('Trash emptied, deleted:', count);
resolve(count);
},
function(error) {
console.error('Failed to empty trash:', error);
resolve(0);
},
'TrashPlugin',
'emptyTrash',
[]
);
});
}
📝 总结
回收站功能展示了如何在Cordova与OpenHarmony混合开发中实现一个安全的删除机制。通过将删除操作分为两步(软删除和硬删除),我们为用户提供了一个安全的数据恢复途径。
自动清理功能的实现展示了如何在原生层处理定时任务,防止数据库无限增长。这种设计既保护了用户的数据安全,又确保了应用的性能。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐


所有评论(0)