在这里插入图片描述

📌 模块概述

回收站功能为用户提供了一个安全的删除机制。当用户删除笔记时,笔记不会立即从数据库中删除,而是被移到回收站。用户可以在回收站中查看已删除的笔记,并选择恢复或永久删除。这个功能防止了用户误删笔记导致数据丢失的情况。

回收站页面展示了所有已删除的笔记。用户可以在这个页面上恢复笔记、永久删除笔记或清空整个回收站。为了防止回收站占用过多空间,我们可以设置一个自动清空策略,比如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

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐