在这里插入图片描述

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

本文对应模块:pages.js 中“财务目标”页面的 HTML 模板与 UI 结构,以及与 db.js 中 goals 表的配合;同时会补充一段鸿蒙 ArkTS 代码,说明目标数据如何通过 FileManager 插件参与导出与导入。


1. 模块定位:从“记账”到“有目标地记账”

前面的模块更多关注日常收支、账户和预算,本模块要解决的是另一个问题:

不只是记录历史,而是给未来定一个可以量化的目标,并在页面中清楚地看到当前达成进度。

典型的财务目标示例:

  • 存 5 万元作为应急金;
  • 在一年内攒够首付;
  • 在半年内还清某笔贷款。

在 UI 层面,我们需要一个页面:

  • 列出所有目标;
  • 展示当前进度(进度条或百分比);
  • 提供新增/编辑目标的入口。

2. 页面整体结构:目标列表 + 进度概览

pages.js 中,“财务目标”页面的结构可以设计为:

// ==================== 财务目标页面 ====================
'goals': () => `
  <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>
        <button id="goal-add-btn" class="pc-button pc-button-primary">新增目标</button>
      </div>
      <div class="pc-card-body">
        <div id="goals-list" class="pc-goals-list">
          <!-- JS 动态渲染每个目标及其进度条 -->
        </div>
      </div>
    </div>
  </div>
`,

这里沿用了熟悉的 PC 布局骨架:

  • pc-page-container + pc-page-header 作为页面的统一头部;
  • 用一张 pc-card 承载“目标列表”;
  • goal-add-btn 是新增目标的主入口;
  • goals-list 容器承载每一个目标项的 UI 结构。

3. 单个目标项的 UI 结构

一个目标项至少需要展示:

  • 目标名称;
  • 目标金额;
  • 当前已完成金额;
  • 进度条和百分比;
  • 操作按钮(编辑 / 删除)。

示例结构:

function renderGoalItem(goal) {
  const ratio = goal.targetAmount > 0
    ? Math.min(goal.currentAmount / goal.targetAmount, 1)
    : 0;

  const percent = Math.round(ratio * 100);

  return `
    <div class="pc-goal-item" data-id="${goal.id}">
      <div class="pc-goal-main">
        <div class="pc-goal-title">${goal.name}</div>
        <div class="pc-goal-amounts">
          <span>目标:¥${goal.targetAmount.toFixed(2)}</span>
          <span>当前:¥${goal.currentAmount.toFixed(2)}</span>
        </div>
        <div class="pc-goal-progress">
          <div class="pc-goal-progress-bar">
            <div
              class="pc-goal-progress-inner"
              style="width: ${percent}%;"
            ></div>
          </div>
          <span class="pc-goal-progress-text">${percent}%</span>
        </div>
      </div>
      <div class="pc-goal-actions">
        <button class="pc-button pc-button-sm" data-action="edit">编辑</button>
        <button class="pc-button pc-button-sm pc-button-danger" data-action="delete">删除</button>
      </div>
    </div>
  `;
}

通过 pc-goal-progress-inner 的宽度控制进度条长度,配合百分比文本,使用户一眼就能看到目标完成度。


4. 从数据库加载目标并渲染

db.js 中,我们可以有一个 goals 表,结构类似:

// 目标表
if (!db.objectStoreNames.contains('goals')) {
  const goalStore = db.createObjectStore('goals', { keyPath: 'id' });
  goalStore.createIndex('createdAt', 'createdAt', { unique: false });
}

async getGoals() {
  return this.getAll('goals');
}

pages.js 中加载并渲染:

async loadGoalsPage() {
  const container = document.getElementById('goals-list');
  if (!container) return;

  const goals = await window.financeDB.getGoals();
  if (!goals || goals.length === 0) {
    container.innerHTML = '<p class="pc-text-muted">暂时还没有任何财务目标,可以点击右上角“新增目标”创建一个。</p>';
    return;
  }

  const html = goals.map(g => renderGoalItem(g)).join('');
  container.innerHTML = html;
}

这段逻辑与前面交易列表、账户列表的加载方式是同一种思路:

  • 从 IndexedDB 取出所有目标;
  • 映射成 HTML 片段;
  • 写入页面容器。

5. 新增和编辑目标:表单与保存逻辑

新增目标可以通过一个模态框或右侧表单来完成,这里展示一个简化的保存逻辑:

async saveGoal() {
  const nameInput = document.getElementById('goal-name');
  const targetInput = document.getElementById('goal-target');
  const currentInput = document.getElementById('goal-current');

  const name = nameInput?.value.trim();
  const targetAmount = parseFloat(targetInput?.value || '0');
  const currentAmount = parseFloat(currentInput?.value || '0');

  if (!name) {
    Toast.error('请输入目标名称');
    return;
  }
  if (!targetAmount || targetAmount <= 0) {
    Toast.error('请输入合理的目标金额');
    return;
  }
  if (currentAmount < 0) {
    Toast.error('当前金额不能为负数');
    return;
  }

  const goal = {
    name,
    targetAmount,
    currentAmount,
  };

  await window.financeDB.addGoal(goal);
  Toast.success('目标已保存');
  this.loadGoalsPage();
}

对应的数据库操作(示例):

async addGoal(goal) {
  goal.id = this.generateId();
  goal.createdAt = new Date().toISOString();
  return this.add('goals', goal);
}

编辑目标则是在 UI 中找出对应 data-id,从 goals 表读取后填充表单,再通过 updateGoal 保存修改即可。


6. ArkTS 侧:财务目标数据的导出与导入

财务目标作为 goals 表的一部分,同样需要在导出/导入时被保留下来。导出调用链和前面预算模块类似:

  1. Web 层调用 financeDB.exportData(),返回包含 goals 在内的完整数据对象;
  2. JS 使用 JSON.stringify 序列化;
  3. 通过 cordova.exec('FileManager', 'exportData', [json]) 把字符串交给 ArkTS 插件;
  4. ArkTS 插件使用 fileIo 将其写入备份文件。

下面是 FileManagerPlugin.ets 中的鸿蒙 ArkTS 片段(与前文保持一致风格):

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 = args[0]; // 其中包含 goals 表以及其他所有表的数据
      const context = getContext() as common.UIAbilityContext;
      const cacheDir: string = context.cacheDir;
      const filePath: string = `${cacheDir}/finance-backup.json`;

      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 result = PluginResult.createByString(MessageStatus.OK, filePath);
      callbackContext.sendPluginResult(result);
    } catch (error) {
      const result = PluginResult.createByString(MessageStatus.ERROR, (error as Error).message);
      callbackContext.sendPluginResult(result);
    }
  }
}

从目标模块视角看:

  • Web 层专注于目标的创建、进度计算和展示;
  • ArkTS 插件专注于“把包含目标在内的全部数据安全写入文件”;
  • 当在新设备上导入备份后,只要 goals 表被正确恢复,目标页面 UI 就能重新渲染出所有目标及其进度。

7. 小结:财务目标页面 UI 的关键设计点

  1. 统一 PC 布局与组件风格

    • 继续复用 pc-page-containerpc-card 等组件,让目标页面在整体视觉上与其他模块保持一致;
  2. 进度条直观表达完成度

    • 通过 pc-goal-progress-inner 的宽度和百分比文本,让用户一眼看懂“目标完成了多少”;
  3. 与 goals 表结构紧密结合

    • 目标名称、目标金额、当前金额在 UI 与数据库之间一一映射,便于后续做统计或报表;
  4. 新增/编辑操作与列表展示解耦

    • 使用独立的表单负责新增/编辑,列表只负责展示和触发操作事件,代码结构更清晰;
  5. 与 ArkTS FileManager 插件协同

    • 目标数据作为整体数据库快照的一部分,由 ArkTS 插件负责导出/导入,保证在多设备之间迁移时不会丢失;

通过本模块,你的应用从“记录历史 + 管理当前”进一步扩展到了“规划未来”:用户不仅能看到资产现状,还能在目标页面上持续跟踪自己距离目标的差距和进展,这也是一个财务管理工具真正开始“有温度”的地方。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐