欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

本文对应模块:pages.js 中“导入导出与设置”相关的 JS 逻辑与页面结构,以及 db.js + ArkTS FileManagerPlugin.ets 中的数据备份/恢复实现;重点说明如何在设置页面中完成数据导出、导入和基础设置(货币单位等),并保证 Web 与鸿蒙 ArkTS 之间的数据契约一致。

请添加图片描述

1. 模块定位:给应用一个“安全出口”和“控制面板”

前面的模块都在围绕核心业务:记账、账户、预算、报表、趋势和分类统计。本模块更多关心:

如何让用户在需要时把整套账本数据安全导出、在新设备恢复,以及调整一些全局设置(如货币符号等)。

从用户视角看,这通常集中在“设置”或“更多”页面中,包括:

  • 导出数据到本地文件;
  • 从备份文件导入数据;
  • 基本应用设置(比如货币单位、语言、主题等占位)。

本模块会从 Web 侧的设置页面 UI 和 JS 逻辑讲起,再结合鸿蒙 ArkTS 侧 FileManagerPlugin 的实现,串起完整的数据导入导出链路。


2. 设置页面 UI 结构:导出、导入和基础设置

pages.js 中,可以为“设置”页面设计如下模板(示意):

// ==================== 设置页面 ====================
'settings': () => `
  <div class="pc-page-container">
    <div class="pc-page-header">
      <h2>⚙️ 设置与数据管理</h2>
      <p>导出/导入数据以及调整应用基础设置</p>
    </div>

    <div class="pc-card">
      <div class="pc-card-header"><h3>数据导出</h3></div>
      <div class="pc-card-body">
        <p>将当前所有账本数据导出为 JSON 文件,方便备份或迁移到其他设备。</p>
        <button id="btn-export" class="pc-button pc-button-primary">导出数据</button>
      </div>
    </div>

    <div class="pc-card">
      <div class="pc-card-header"><h3>数据导入</h3></div>
      <div class="pc-card-body">
        <p>从之前导出的 JSON 备份文件中恢复数据,请谨慎操作。</p>
        <button id="btn-import" class="pc-button pc-button-danger">导入数据</button>
      </div>
    </div>

    <div class="pc-card">
      <div class="pc-card-header"><h3>基础设置</h3></div>
      <div class="pc-card-body">
        <div class="pc-form-group">
          <label class="pc-label">货币符号</label>
          <input type="text" id="setting-currency" class="pc-input" value="¥" maxlength="3">
        </div>
        <!-- 未来可以扩展语言、主题等更多设置项 -->
        <button id="btn-save-settings" class="pc-button">保存设置</button>
      </div>
    </div>
  </div>
`,

这里重点是两个按钮:btn-exportbtn-import,以及一个基础设置表单。


3. Web 侧导出逻辑:从 IndexedDB 到 JSON 字符串

pages.js 中,导出按钮通常会绑定一个类似 exportData 的方法:

async exportData() {
  try {
    // 1. 从 IndexedDB 把所有表导出为一个对象
    const data = await window.financeDB.exportData();

    // 2. 序列化为 JSON 字符串
    const json = JSON.stringify(data, null, 2);

    // 3. 通过 cordova.exec 调用鸿蒙 ArkTS 侧 FileManager 插件
    if (window.cordova && window.cordova.exec) {
      window.cordova.exec(
        (filePath) => {
          Toast.success(`数据已导出到: ${filePath}`);
        },
        (err) => {
          Toast.error('导出失败: ' + err);
        },
        'FileManager',
        'exportData',
        [json]
      );
    } else {
      Toast.error('当前环境不支持原生导出,请检查 Cordova/ArkTS 集成');
    }
  } catch (error) {
    console.error(error);
    Toast.error('导出失败: ' + (error && error.message ? error.message : String(error)));
  }
}

其中 financeDB.exportData() 负责把所有业务表打包成一个结构类似于:

{
  accounts: [...],
  transactions: [...],
  categories: [...],
  budgets: [...],
  goals: [...],
  tags: [...],
}

的对象,这一点在前面多个模块中都已经使用过。


4. Web 侧导入逻辑:从文件到 IndexedDB

导入逻辑则是反向:从用户选择的文件中读取 JSON,再调用 financeDB.importData(data) 写回 IndexedDB。

async importData() {
  try {
    Toast.info('请选择要导入的 JSON 备份文件...');

    const fileInput = document.createElement('input');
    fileInput.type = 'file';
    fileInput.accept = '.json';
    fileInput.style.display = 'none';

    fileInput.onchange = async (event) => {
      const file = event.target.files?.[0];
      if (!file) return;

      const reader = new FileReader();
      reader.onload = async (e) => {
        try {
          const jsonData = e.target?.result;
          if (!jsonData || typeof jsonData !== 'string') {
            Toast.error('读取文件失败');
            return;
          }

          const data = JSON.parse(jsonData);
          await window.financeDB.importData(data);
          Toast.success('数据导入成功,请刷新相关页面查看最新数据');
        } catch (err) {
          console.error(err);
          Toast.error('导入失败: ' + (err && err.message ? err.message : String(err)));
        }
      };

      reader.readAsText(file);
    };

    document.body.appendChild(fileInput);
    fileInput.click();
    document.body.removeChild(fileInput);
  } catch (error) {
    console.error(error);
    Toast.error('导入流程启动失败: ' + (error && error.message ? error.message : String(error)));
  }
}

这一逻辑在 Web 端就可以跑通,不依赖 ArkTS。但在鸿蒙环境下,我们希望导出文件能落到由 ArkTS 管理的安全路径上,因此导出部分改为通过 cordova.exec 调用原生插件,而导入部分可以选择:

  • 继续走浏览器文件选择(更简单,已如上所示);
  • 或者扩展 ArkTS 插件,让用户在系统文件选择器中选择备份(更原生,但实现更复杂)。

5. 鸿蒙 ArkTS 侧:FileManagerPlugin.ets 导出实现

ArkTS 侧的 FileManager 插件负责接收 Web 传来的 JSON 字符串,并把它写入应用可访问的文件路径。

import { CordovaPlugin, CallbackContext } from '@magongshou/harmony-cordova/Index';
import { PluginResult, MessageStatus } from '@magongshou/harmony-cordova/Index';
import { common } from '@kit.AbilityKit';
import { fileIo } from '@kit.CoreFileKit';

export class FileManagerPlugin extends CordovaPlugin {
  async exportData(callbackContext: CallbackContext, args: string[]): Promise<void> {
    try {
      const json: string = args[0] || '';
      if (!json) {
        const result = PluginResult.createByString(MessageStatus.ERROR, '导出数据为空');
        callbackContext.sendPluginResult(result);
        return;
      }

      const context = getContext() as common.UIAbilityContext;
      const cacheDir: string = context.cacheDir;
      const fileName: string = `finance-backup-${new Date().toISOString().slice(0, 10)}.json`;
      const filePath: string = `${cacheDir}/${fileName}`;

      const file = await fileIo.open(filePath, fileIo.OpenMode.WRITE_ONLY | fileIo.OpenMode.CREATE);
      await fileIo.write(file.fd, json);
      await fileIo.close(file.fd);

      const okResult = PluginResult.createByString(MessageStatus.OK, filePath);
      callbackContext.sendPluginResult(okResult);
    } catch (error) {
      const errMsg = (error as Error).message;
      const errResult = PluginResult.createByString(MessageStatus.ERROR, errMsg);
      callbackContext.sendPluginResult(errResult);
    }
  }
}

这段 ArkTS 代码与前文多个模块引用的实现风格保持一致:

  • 通过 args[0] 接收 Web 传来的 JSON 字符串;
  • 使用 context.cacheDir 作为基础路径写入备份文件;
  • 通过 PluginResult 将结果(成功时为文件路径)回传给 JS 层,便于在 Toast 中提示用户。

6. Web 与 ArkTS 的数据契约:exportData / importData

要让导入导出在 Web 与 ArkTS 间协同,关键是约定好 exportData / importData 调用的数据结构:

  • Web 侧 financeDB.exportData()

    • 返回一个包含多张表的对象:{ accounts, transactions, categories, budgets, goals, tags }
    • JS 使用 JSON.stringify 序列化后交给 ArkTS;
  • Web 侧 financeDB.importData(data)

    • 接收从 JSON 解析回来的对象;
    • 按表名分别写回 IndexedDB:this.importAccounts(data.accounts)this.importTransactions(data.transactions) 等;
  • ArkTS 侧 FileManager 只把这个 JSON 字符串当作“黑盒”:

    • 不解析业务含义,只负责写入或读取文件;
    • 保证导出的内容在导入时原样返回给 Web。

这种分工使得:

  • 业务演进(新增表、修改字段)主要集中在 Web 层的 exportData / importData 实现中;
  • ArkTS 插件只关心“字节流”的存储和返回,极大降低耦合度。

7. 基础设置示例:货币符号的存取

虽然设置页面可以承载很多选项,这里以货币符号为例,展示一个简单的存取逻辑。可以在 Web 层利用 localStorage 或 IndexedDB 的一个设置表:

async saveSettings() {
  const currencyInput = document.getElementById('setting-currency');
  const currency = currencyInput?.value.trim() || '¥';

  // 简单示例:直接存到 localStorage
  window.localStorage.setItem('finance.currency', currency);
  Toast.success('设置已保存');
}

function loadSettings() {
  const currencyInput = document.getElementById('setting-currency');
  if (!currencyInput) return;

  const saved = window.localStorage.getItem('finance.currency');
  if (saved) {
    currencyInput.value = saved;
  }
}

在页面渲染完成后调用 loadSettings(),即可把之前保存的货币符号填回输入框。记账页面在展示金额时,也可以根据这个设置动态切换前缀符号(例如 ¥$ 等)。

更进一步,也可以把这些设置放到 settings 表中,通过 financeDB 统一管理,但核心思路是一致的:

  • 设置页面负责让用户修改某些全局参数;
  • 其他页面在渲染时读取这些参数并调整展示。

8. 鸿蒙 ArkTS 侧承载设置页面

设置页面本身与其他 Web 页面一样,托管在 ArkTS 的 Index.ets 中:

import {
  MainPage,
  PluginEntry,
} from '@magongshou/harmony-cordova/Index';
import { FileManagerPlugin } from '../plugins/FileManagerPlugin';

@Entry
@Component
struct Index {
  cordovaPlugs: Array<PluginEntry> = [
    { pluginName: 'FileManager', pluginObject: new FileManagerPlugin() }
  ];

  build() {
    // MainPage 内部加载 rawfile/www/index.html
    // 设置页面只是 pages.js 中的一个路由页面
    MainPage({
      cordovaPlugs: this.cordovaPlugs
    });
  }
}

从 ArkTS 的角度看:

  • 它并不需要理解“设置页面”展示了什么;
  • 只需要:
    • 提供 Web 容器承载所有前端逻辑;
    • 通过 cordovaPlugs 暴露 FileManager 等原生能力给 Web;
    • 在用户点击“导出数据”时,配合完成文件写入和路径返回。

9. 小结:数据导入导出与设置页面的关键点

  1. 设置页面作为“系统控制中心”

    • 集中放置数据导出/导入按钮和基础设置入口;
  2. Web 侧导出/导入逻辑清晰

    • financeDB.exportData() 打包所有表 → JSON.stringify → 交给 ArkTS 写文件;
    • 导入时通过文件选择读取 JSON → financeDB.importData() 写回 IndexedDB;
  3. ArkTS FileManager 插件只负责“搬运字节”

    • 不解析业务,只负责把 JSON 写入/读出文件;
    • 通过 PluginResult 把状态和文件路径回传给 Web;
  4. Web 与 ArkTS 通过 JSON 契约解耦

    • Web 决定 JSON 的结构和表字段;
    • ArkTS 完全不用跟进业务字段演变,长期稳定;
  5. 设置页面便于未来扩展

    • 除货币符号外,还可以逐步加入语言、主题、默认首页等设置;
    • 所有这些设置都可以通过类似的“页面 UI + 数据存储 + 读取使用”模式实现。

至此,从模块 01 到模块 30,我们已经完整走完了这套鸿蒙 + Cordova 记账应用的核心模块:从页面容器、数据库,到记账、账户、预算、目标、报表、分类和趋势,再到本模块的导入导出与设置。整个系统的技术路径也在每篇文章中被拆解得足够细致,方便你后续查阅和继续演进。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐