项目演示

在这里插入图片描述

目录

  1. 项目概述与开发背景
  2. HarmonyOS API 24 技术特性深度剖析
  3. ArkTS 语言核心概念与实践
  4. 项目结构与初始化流程
  5. UI 设计与布局架构详解
  6. 状态管理与响应式机制原理
  7. 收支记录解析引擎设计与实现
  8. 数据结构设计与演进
  9. 记账优化提醒系统实现
  10. 收支分析算法实现
  11. JSON 输出规范与序列化机制
  12. 构建问题与踩坑记录
  13. 测试用例与效果展示
  14. 扩展优化方向

1. 项目概述与开发背景

1.1 记账应用的市场需求

随着数字化生活的普及,个人财务管理成为越来越多人关注的话题。传统的记账方式存在以下痛点:

  1. 记录繁琐:手动输入每笔收支需要打开专门的应用,操作步骤多,用户体验不佳。
  2. 格式不统一:不同用户有不同的记录习惯,有的喜欢分类记录,有的喜欢自由输入,难以统一管理。
  3. 分析滞后:缺乏实时的收支分析和消费提醒功能,用户无法及时了解自己的财务状况。
  4. 跨平台限制:部分记账应用无法在 HarmonyOS 设备上完美运行,用户被迫使用多个应用。

简易记账助手正是为了解决这些问题而设计的轻量级应用。它采用自然语言输入方式,用户只需输入类似"早餐15元,午餐30元,工资5000元"的文本,系统即可自动解析并生成结构化的收支记录。这种输入方式更加直观、高效,符合用户的日常使用习惯。

1.2 应用核心功能

本应用实现了以下核心功能:

  1. 自然语言输入:支持用户以自然语言方式输入收支记录,无需学习复杂的操作流程。
  2. 智能解析:自动识别收支项目、金额和类型,支持多种输入格式。
  3. 记账提醒:根据消费情况生成个性化的记账优化建议,帮助用户更好地管理财务。
  4. 收支分析:自动计算总收入、总支出、结余及支出占比,提供直观的财务分析报告。
  5. JSON 输出:生成标准的 JSON 格式输出,便于数据交换和存储。

1.3 技术选型考量

在技术选型方面,我们选择了 HarmonyOS ArkTS 作为开发语言,主要基于以下考量:

  1. 声明式 UI:ArkTS 的声明式语法使 UI 开发更加直观高效,开发者可以用更少的代码实现复杂的界面。
  2. 响应式状态管理:@State 装饰器提供了简洁的状态管理方案,数据变化自动触发 UI 更新。
  3. API 24 兼容性:支持最新的 HarmonyOS 特性,同时保持良好的向后兼容性。
  4. 跨设备适配:一套代码可运行在手机、平板、智慧屏等多种设备上,降低开发成本。

2. HarmonyOS API 24 技术特性深度剖析

2.1 API 24 概述

HarmonyOS API 24 是华为鸿蒙操作系统的重要版本,引入了许多新特性和改进。对于应用开发者而言,API 24 提供了更强大的开发能力和更好的用户体验。

API 24 主要包含以下几个方面的更新:

  1. 声明式 UI 框架增强:进一步完善了声明式 UI 语法,提供了更多的组件和布局选项。
  2. 性能优化:优化了应用启动速度和运行性能,提升了用户体验。
  3. 新能力开放:开放了更多系统能力,如分布式数据管理、跨设备协同等。
  4. 开发工具改进:改进了 DevEco Studio 开发工具,提供了更好的调试和测试支持。

2.2 声明式 UI 框架

API 24 进一步完善了声明式 UI 框架,开发者可以使用简洁的语法描述 UI 结构和行为。

声明式 UI 的核心思想是:UI 是数据的函数,当数据变化时,UI 自动更新。这种方式相比命令式 UI 开发有以下优势:

  1. 代码可读性更高:UI 结构一目了然,开发者可以快速理解界面布局。
  2. 状态管理更简单:数据变化自动触发 UI 更新,无需手动操作 DOM。
  3. 开发效率更高:减少了大量的样板代码,开发者可以专注于业务逻辑。

在 ArkTS 中,声明式 UI 的基本结构如下:

@Entry
@Component
struct Index {
  @State message: string = 'Hello World'
  
  build() {
    Column() {
      Text(this.message)
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
    }
  }
}

上述代码定义了一个简单的组件,包含一个文本显示"Hello World"。当 message 变量的值发生变化时,文本会自动更新。

2.3 组件化开发

API 24 强调组件化开发理念,每个组件都是独立的、可复用的模块。

组件化开发的优势:

  1. 代码复用:组件可以在多个页面中复用,减少重复代码。
  2. 职责分离:每个组件只负责自己的功能,代码更加清晰。
  3. 易于维护:组件独立开发和测试,便于维护和更新。

在 ArkTS 中,组件的定义使用以下装饰器:

  • @Entry:标识应用的入口组件,一个应用只能有一个入口组件。
  • @Component:标识自定义组件,可以被其他组件引用。
  • build():组件的构建方法,返回组件的 UI 结构。

2.4 响应式状态管理

API 24 的响应式状态管理是其核心特性之一。通过 @State 装饰器,开发者可以轻松实现数据驱动的 UI 更新。

响应式状态管理的工作原理:

  1. 状态定义:使用 @State 装饰器定义状态变量。
  2. UI 绑定:在 UI 组件中绑定状态变量。
  3. 自动更新:当状态变量的值发生变化时,所有绑定该变量的 UI 组件自动重新渲染。

这种机制大大简化了状态管理的复杂度,开发者无需手动管理 UI 更新。


3. ArkTS 语言核心概念与实践

3.1 ArkTS 简介

ArkTS 是 HarmonyOS 应用开发的主力语言,它在 TypeScript 的基础上扩展了声明式 UI 语法和状态管理能力。

ArkTS 的主要特点:

  1. 强类型系统:继承了 TypeScript 的强类型系统,提供了更好的代码提示和错误检查。
  2. 声明式 UI:扩展了 TypeScript,提供了声明式 UI 语法,使 UI 开发更加直观。
  3. 状态管理:提供了多种状态管理装饰器,如 @State、@Prop、@Link 等。
  4. 与 TypeScript 兼容:可以直接使用 TypeScript 的语法和库。

3.2 类型系统

ArkTS 继承了 TypeScript 的强类型系统,接口定义清晰地描述了数据结构,提高了代码的可维护性和可读性。

在本应用中,我们定义了以下接口:

interface RecordItem {
  description: string
  amount: number
  type: string
}

interface InferItem {
  item: string
  amount: number
  type: string
}

这些接口定义了收支记录的数据结构,确保了类型安全。

3.3 类与对象

ArkTS 支持面向对象编程,类的定义和使用与 TypeScript 类似。

在本应用中,OutputData 类用于封装最终的输出数据:

class OutputData {
  question: string = ''
  hintList: string[] = []
  inferList: InferItem[] = []
  analyse: string = ''
  successText: string = ''
}

使用类的方式创建对象可以确保数据结构的一致性,避免了对象字面量可能带来的类型推断问题。

3.4 装饰器语法

装饰器是 ArkTS 的重要特性,用于扩展类、方法和属性的行为。

在本应用中,我们使用了以下装饰器:

  • @Entry:标记应用的入口组件。
  • @Component:标记自定义组件。
  • @State:标记响应式状态变量。

这些装饰器简化了应用的开发流程,使代码更加简洁。


4. 项目结构与初始化流程

4.1 项目目录结构

一个典型的 HarmonyOS ArkTS 项目结构如下:

MyApplication11/
├── entry/
│   ├── src/
│   │   └── main/
│   │       ├── ets/
│   │       │   ├── pages/
│   │       │   │   └── Index.ets
│   │       │   ├── AppScope/
│   │       │   │   └── app.ets
│   │       │   └── entryability/
│   │       │       └── EntryAbility.ets
│   │       └── resources/
│   │           ├── base/
│   │           │   ├── element/
│   │           │   ├── media/
│   │           │   └── profile/
│   │           └── rawfile/
│   └── hvigorfile.ts
├── ohosTest/
├── build-profile.json5
├── hvigorfile.ts
└── package.json

各目录和文件的作用:

  1. entry/:应用的主模块,包含应用的代码和资源。
  2. src/main/ets/pages/:页面组件目录,存放应用的各个页面。
  3. src/main/ets/AppScope/:应用全局配置目录。
  4. src/main/ets/entryability/:应用入口能力目录。
  5. src/main/resources/:应用资源目录,包含图片、字符串、样式等资源。
  6. build-profile.json5:项目构建配置文件。
  7. hvigorfile.ts:构建脚本配置文件。

在本项目中,所有的业务逻辑都集中在 Index.ets 文件中,这符合用户"页面都要在 index.ets 内完成"的要求。

4.2 初始化步骤

创建一个新的 HarmonyOS ArkTS 项目需要以下步骤:

  1. 打开 DevEco Studio:启动华为官方开发工具 DevEco Studio。
  2. 选择"Create Project":在欢迎界面选择"Create Project"创建新项目。
  3. 选择模板:选择"Empty Ability"模板,这是一个空的应用模板。
  4. 配置项目:输入项目名称、包名、保存路径等信息。
  5. 选择 API 版本:选择 API 24 作为目标 API 版本。
  6. 完成创建:点击"Finish"按钮完成项目创建。

项目创建完成后,DevEco Studio 会自动生成基本的项目结构和代码。

4.3 配置文件说明

项目的配置文件包括:

  1. build-profile.json5:项目构建配置文件,定义了构建相关的配置,如编译选项、签名配置等。
  2. hvigorfile.ts:构建脚本配置文件,用于自定义构建流程。
  3. module.json5:模块配置文件,定义了应用的基本信息,如应用名称、图标、权限等。

这些配置文件是应用运行的基础,开发者需要根据实际需求进行配置。


5. UI 设计与布局架构详解

5.1 整体布局结构

本应用采用了简洁的垂直布局结构,主要分为以下几个区域:

  1. 标题区域:显示应用名称"简易记账助手",使用大字体和加粗样式突出显示。
  2. 输入区域:TextArea 组件,用于输入收支记录,支持多行输入。
  3. 错误提示区域:显示输入校验错误信息,使用红色字体突出显示。
  4. 按钮区域:包含"开始记账"和"重置"两个按钮,水平排列。
  5. 结果展示区域:显示 JSON 格式的输出结果,使用 Scroll 组件包裹,支持滚动查看。

5.2 Column 组件的使用

Column 组件是垂直布局的核心,用于将子组件按垂直方向排列。

在本应用中,Column 组件的使用示例:

build() {
  Column() {
    Text('简易记账助手')
      .fontSize(24)
      .fontWeight(FontWeight.Bold)
      .margin({ top: 20, bottom: 10 })
      .width('100%')
      .textAlign(TextAlign.Center)
    
    TextArea({ placeholder: '请输入收支记录,如:早餐15元,午餐30元,工资5000元' })
      .width('90%')
      .height(100)
      .borderRadius(10)
      .borderWidth(2)
      .borderColor('#E0E0E0')
      .padding(10)
      .fontSize(16)
      .onChange((value: string) => {
        this.inputText = value
        this.errorMsg = ''
      })
    
    // ... 其他组件
  }
  .width('100%')
  .height('100%')
  .padding(10)
  .justifyContent(FlexAlign.Start)
}

Column 组件的关键属性:

  • width/height:设置组件的宽度和高度,可以使用百分比或具体数值。
  • padding:设置内边距,控制子组件与组件边缘的距离。
  • justifyContent:设置子组件的垂直对齐方式,如 Start、Center、End 等。
  • margin:设置外边距,控制组件与其他组件的距离。

5.3 TextArea 组件详解

TextArea 是多行文本输入组件,适合用户输入较长的收支记录。

TextArea 的关键属性:

  1. placeholder:占位提示文本,引导用户输入格式。
  2. onChange:输入内容变化时的回调函数,实时更新状态变量。
  3. borderRadius:圆角边框,提升视觉效果。
  4. borderWidth/borderColor:边框宽度和颜色。
  5. padding:内边距,控制文本与边框的距离。
  6. fontSize:字体大小。

在本应用中,TextArea 的使用示例:

TextArea({ placeholder: '请输入收支记录,如:早餐15元,午餐30元,工资5000元' })
  .width('90%')
  .height(100)
  .borderRadius(10)
  .borderWidth(2)
  .borderColor('#E0E0E0')
  .padding(10)
  .fontSize(16)
  .onChange((value: string) => {
    this.inputText = value
    this.errorMsg = ''
  })

当用户输入内容时,onChange 回调函数会被触发,更新 inputText 状态变量,并清除之前的错误提示。

5.4 Row 组件的使用

Row 组件用于水平布局,将子组件按水平方向排列。

在本应用中,Row 组件用于排列"开始记账"和"重置"两个按钮:

Row() {
  Button('开始记账')
    .width('45%')
    .height(48)
    .margin({ top: 15, right: 10 })
    .backgroundColor('#007DFF')
    .fontColor('#FFFFFF')
    .fontSize(18)
    .fontWeight(FontWeight.Bold)
    .borderRadius(24)
    .onClick(() => {
      this.processAccount()
    })
  
  Button('重置')
    .width('45%')
    .height(48)
    .margin({ top: 15 })
    .backgroundColor('#F0F0F0')
    .fontColor('#333333')
    .fontSize(18)
    .fontWeight(FontWeight.Bold)
    .borderRadius(24)
    .onClick(() => {
      this.reset()
    })
}
.width('90%')

Row 组件的关键属性与 Column 类似,但方向不同。两个按钮各占 45% 的宽度,中间留有 10% 的间距,实现了均匀分布的效果。

5.5 Scroll 组件的使用

Scroll 组件用于包裹内容区域,当内容超出组件高度时可以滚动查看。

在本应用中,Scroll 组件用于包裹结果展示区域:

if (this.showResult) {
  Scroll() {
    Column() {
      Text(this.outputJson)
        .fontSize(12)
        .margin({ top: 20, left: 10, right: 10 })
        .width('100%')
        .fontColor('#333333')
        .backgroundColor('#F8F8F8')
        .padding(10)
        .borderRadius(8)
    }
    .width('100%')
    .padding(10)
  }
  .width('90%')
  .height(600)
  .borderRadius(10)
  .borderWidth(1)
  .borderColor('#E0E0E0')
  .margin({ top: 20 })
}

条件渲染 if (this.showResult) 确保只有在用户点击"开始记账"后才显示结果区域。Scroll 组件的高度设置为 600,当 JSON 内容超过这个高度时,用户可以通过滚动查看完整内容。


6. 状态管理与响应式机制原理

6.1 状态变量定义

本应用定义了以下状态变量:

@State inputText: string = ''      // 用户输入的收支记录文本
@State records: RecordItem[] = []  // 解析后的收支记录列表
@State outputJson: string = ''     // 最终生成的 JSON 字符串
@State showResult: boolean = false // 是否显示结果区域
@State errorMsg: string = ''       // 错误提示信息

这些状态变量都使用 @State 装饰器标记,当它们的值发生变化时,UI 会自动更新。

6.2 状态变化触发机制

ArkTS 的响应式机制基于依赖追踪,当状态变量被修改时,所有依赖该变量的 UI 组件都会重新渲染。

状态变化的触发流程:

  1. 状态修改:开发者修改状态变量的值。
  2. 依赖检测:框架检测哪些 UI 组件依赖了该状态变量。
  3. UI 更新:重新渲染依赖该状态变量的 UI 组件。

showResult 为例:

// 修改状态变量
this.showResult = true

// UI 自动更新,显示结果区域
if (this.showResult) {
  Scroll() {
    // ... 结果展示内容
  }
}

showResult 的值从 false 变为 true 时,条件渲染的内容会自动显示。

6.3 重置功能实现

重置功能需要清空所有状态变量,恢复应用的初始状态:

reset() {
  this.inputText = ''
  this.records = []
  this.outputJson = ''
  this.showResult = false
  this.errorMsg = ''
}

调用 reset() 方法后,所有状态变量恢复初始值,UI 会自动重新渲染到初始状态。


7. 收支记录解析引擎设计与实现

7.1 解析流程概述

收支记录解析是本应用的核心功能之一,其流程如下:

  1. 输入校验:检查用户输入是否为空。
  2. 文本分割:按逗号分割输入文本。
  3. 正则匹配:对每个片段进行正则匹配。
  4. 类型推断:根据关键词推断收支类型。
  5. 去重处理:避免重复记录。

7.2 输入校验

输入校验是必要的第一步,确保后续处理有有效的数据:

processAccount() {
  this.errorMsg = ''
  
  if (!this.inputText.trim()) {
    this.errorMsg = '请输入收支记录'
    return
  }
  
  // ... 后续解析逻辑
}

如果用户输入为空或只包含空白字符,显示错误提示"请输入收支记录",并终止后续处理。

7.3 文本分割策略

使用正则表达式 /[,,]/ 同时匹配英文逗号和中文逗号,将输入文本分割成多个片段:

const text = this.inputText
const segments = text.split(/[,,]/)

这种策略允许用户使用两种逗号作为分隔符,提高了输入的灵活性。

7.4 正则匹配引擎

正则匹配是解析的核心,本应用使用的正则表达式:

const pattern = /([一-龥a-zA-Z]+)\s*(\d+(?:\.\d+)?)\s*(元|块|钱)|(\d+(?:\.\d+)?)\s*(元|块|钱)\s*([一-龥a-zA-Z]+)/

这个正则表达式包含两个分支:

分支一([一-龥a-zA-Z]+)\s*(\d+(?:\.\d+)?)\s*(元|块|钱)

  • ([一-龥a-zA-Z]+):匹配一个或多个中文或英文字符,作为项目名称。
  • \s*:匹配零个或多个空白字符。
  • (\d+(?:\.\d+)?):匹配整数或小数金额。
  • (元|块|钱):匹配货币单位。

分支二(\d+(?:\.\d+)?)\s*(元|块|钱)\s*([一-龥a-zA-Z]+)

  • (\d+(?:\.\d+)?):匹配整数或小数金额。
  • \s*:匹配零个或多个空白字符。
  • (元|块|钱):匹配货币单位。
  • ([一-龥a-zA-Z]+):匹配一个或多个中文或英文字符,作为项目名称。

这种设计支持两种输入格式:“项目名称+金额+单位"和"金额+单位+项目名称”。

7.5 匹配结果处理

正则匹配返回一个数组,需要根据匹配的分支提取相应的捕获组:

for (const segment of segments) {
  const trimmed = segment.trim()
  if (!trimmed) continue
  
  const match = trimmed.match(pattern)
  
  if (match) {
    let description: string, amount: string
    
    if (match[4] && match[6]) {
      // 分支二匹配成功
      description = match[6]
      amount = match[4]
    } else {
      // 分支一匹配成功
      description = match[1]
      amount = match[2]
    }
    
    if (!description || !amount) continue
    
    // ... 类型推断和去重
  }
}

正则匹配的结果数组中,索引 1-3 对应分支一的捕获组,索引 4-6 对应分支二的捕获组。通过检查索引 4 和 6 是否存在,可以判断哪个分支匹配成功。

7.6 收支类型推断

通过关键词匹配来判断收支类型:

const incomeKeywords = ['工资', '收入', '奖金', '红包', '提成', '报酬', '退款']
const type = incomeKeywords.some(keyword => description.includes(keyword)) ? '收入' : '支出'

如果项目名称包含收入相关关键词(如工资、收入、奖金等),则标记为"收入",否则标记为"支出"。

7.7 去重策略

在添加新记录之前,检查是否已经存在相同描述的记录,避免重复添加:

const exists = this.records.some(r => r.description === description)
if (!exists) {
  this.records.push({
    description: description,
    amount: parseFloat(amount),
    type: type
  })
}

这种去重策略基于项目描述,确保相同的项目不会被重复记录。


8. 数据结构设计与演进

8.1 RecordItem 接口

RecordItem 用于存储解析后的原始收支记录:

interface RecordItem {
  description: string
  amount: number
  type: string
}

字段说明:

  • description:项目描述,如"早餐"、"工资"等。
  • amount:金额,数值类型,方便后续计算。
  • type:类型,“收入"或"支出”。

8.2 InferItem 接口

InferItem 用于输出格式化后的收支明细:

interface InferItem {
  item: string
  amount: number
  type: string
}

字段说明:

  • item:项目名称,对应 RecordItem 的 description。
  • amount:金额,数值类型。
  • type:类型,“收入"或"支出”。

8.3 OutputData 类

OutputData 类封装了最终的输出数据:

class OutputData {
  question: string = ''
  hintList: string[] = []
  inferList: InferItem[] = []
  analyse: string = ''
  successText: string = ''
}

字段说明:

  • question:用户原始输入的收支记录文本。
  • hintList:记账优化提醒列表,包含多条提醒信息。
  • inferList:整理好的收支明细,包含每条记录的项目名称、金额和类型。
  • analyse:收支分析报告,包含总收入、总支出、结余等信息。
  • successText:完成提示语,告知用户账目已整理完毕。

8.4 数据流转过程

数据在应用中的流转过程:

  1. 用户输入:用户在 TextArea 中输入收支记录文本。
  2. 状态更新inputText 状态变量更新为用户输入的文本。
  3. 解析处理:点击"开始记账"按钮后,调用 processAccount() 方法进行解析。
  4. 记录存储:解析后的记录存储在 records 状态变量中。
  5. 业务处理:调用 getHintList()getInferList()getAnalyse() 方法生成相关数据。
  6. 输出组装:调用 generateOutput() 方法组装输出数据。
  7. JSON 序列化:使用 JSON.stringify() 将输出数据序列化为 JSON 字符串。
  8. UI 展示outputJson 状态变量更新,UI 自动显示 JSON 输出。

9. 记账优化提醒系统实现

9.1 提醒规则设计

记账优化提醒系统根据以下规则生成提醒:

  1. 大额支出提醒:单笔支出超过 100 元,提醒用户确认是否必要。
  2. 餐饮消费提醒:餐饮类支出合计超过 50 元,提醒用户关注饮食消费。
  3. 记录数量提醒:记录数量超过 5 条,建议用户分类整理。

9.2 大额支出提醒

筛选出支出类型中金额超过 100 元的项目,并生成提醒信息:

const spendingRecords = this.records.filter(r => r.type === '支出')
const expensiveItems = spendingRecords.filter(r => r.amount > 100)
if (expensiveItems.length > 0) {
  hints.push(`大额支出提醒:${expensiveItems.map(i => i.description).join('、')},建议确认是否必要`)
}

9.3 餐饮消费提醒

通过关键词匹配识别餐饮类支出,计算合计金额并判断是否超过阈值:

const foodItems = spendingRecords.filter(r => 
  r.description.includes('餐') || 
  r.description.includes('饭') || 
  r.description.includes('食') || 
  r.description.includes('早') || 
  r.description.includes('午') || 
  r.description.includes('晚')
)
if (foodItems.length > 0) {
  const totalFood = foodItems.reduce((sum, r) => sum + r.amount, 0)
  if (totalFood > 50) {
    hints.push(`餐饮支出合计${totalFood.toFixed(2)}元,建议关注饮食消费`)
  }
}

9.4 记录数量提醒

当记录数量超过 5 条时,建议用户进行分类整理:

if (this.records.length > 5) {
  hints.push('记录较多,建议分类整理以便更好管理')
}

9.5 无提醒情况处理

如果没有触发任何提醒规则,返回默认提示:

return hints.length > 0 ? hints : ['暂无特别提醒']

10. 收支分析算法实现

10.1 收入计算

使用 filter 方法筛选出所有收入记录,然后使用 reduce 方法求和:

const income = this.records.filter(r => r.type === '收入').reduce((sum, r) => sum + r.amount, 0)

10.2 支出计算

同样的方式计算总支出:

const expense = this.records.filter(r => r.type === '支出').reduce((sum, r) => sum + r.amount, 0)

10.3 结余计算

结余 = 总收入 - 总支出:

const balance = income - expense

10.4 支出占比计算

支出占比 = (总支出 / 总收入) × 100%:

if (income > 0 && expense > 0) {
  const ratio = (expense / income * 100).toFixed(1)
  analyse += `支出占收入比例:${ratio}%\n`
}

需要注意避免除以零的情况,因此添加了 income > 0 的条件判断。

10.5 消费建议生成

根据结余情况生成不同的建议:

if (balance < 0) {
  analyse += '注意:今日支出大于收入,建议控制消费'
} else if (balance > 0) {
  analyse += '恭喜:今日有结余,可以考虑储蓄'
}

11. JSON 输出规范与序列化机制

11.1 输出格式要求

根据用户需求,输出的 JSON 必须包含五个字段:

{
  "question": "用户输入的收支记录",
  "hintList": ["提醒1", "提醒2"],
  "inferList": [
    {"item": "项目名称", "amount": 15, "type": "支出"},
    {"item": "工资", "amount": 5000, "type": "收入"}
  ],
  "analyse": "今日收支分析报告",
  "successText": "今日账目整理完毕,记得及时查看收支情况哦!"
}

11.2 序列化实现

使用 JSON.stringify 方法将对象序列化为 JSON 字符串:

generateOutput() {
  const output: OutputData = new OutputData()
  output.question = this.inputText
  output.hintList = this.getHintList()
  output.inferList = this.getInferList()
  output.analyse = this.getAnalyse()
  output.successText = '今日账目整理完毕,记得及时查看收支情况哦!'
  this.outputJson = JSON.stringify(output, null, 2)
}

JSON.stringify 的参数说明:

  • 第一个参数:要序列化的对象。
  • 第二个参数:自定义序列化函数,null 表示使用默认序列化。
  • 第三个参数:缩进空格数,2 表示缩进两个空格,使 JSON 更易读。

11.3 纯 JSON 约束

用户强调"只输出标准 JSON 格式,禁止多余文字",因此在 UI 设计中只展示 JSON 输出,不添加额外的格式化文本。


12. 构建问题与踩坑记录

在开发过程中,我们遇到了三处构建错误,以下是详细的问题分析和解决方案。

12.1 textAlign 方法报错

问题描述:在 Text 组件上调用 textAlign(TextAlign.Left) 时构建失败。

原因分析:在 HarmonyOS API 24 中,Text 组件的 textAlign 方法可能存在兼容性问题,或者该方法的参数类型与预期不符。

解决方案:移除 textAlign(TextAlign.Left) 调用。Text 组件默认就是左对齐的,移除后不影响显示效果。

// 修改前
Text(this.outputJson)
  .textAlign(TextAlign.Left)

// 修改后
Text(this.outputJson)

12.2 箭头函数返回对象报错

问题描述:使用箭头函数直接返回对象字面量时报错:

return this.records.map(r => ({
  item: r.description,
  amount: r.amount,
  type: r.type
}))

原因分析:在 ArkTS 中,箭头函数的语法可能与标准 TypeScript 存在差异,特别是在返回对象字面量时。

解决方案:改为使用 for 循环构建数组:

getInferList(): InferItem[] {
  const result: InferItem[] = []
  for (const r of this.records) {
    result.push({
      item: r.description,
      amount: r.amount,
      type: r.type
    })
  }
  return result
}

12.3 对象字面量定义报错

问题描述:直接使用对象字面量定义输出数据时报错:

const output = {
  question: this.inputText,
  hintList: this.getHintList(),
  inferList: this.getInferList(),
  analyse: this.getAnalyse(),
  successText: '今日账目整理完毕,记得及时查看收支情况哦!'
}

原因分析:ArkTS 对对象字面量的类型推断可能存在限制,或者与 JSON 序列化存在兼容性问题。

解决方案:定义 OutputData 类,使用类实例化的方式创建对象:

class OutputData {
  question: string = ''
  hintList: string[] = []
  inferList: InferItem[] = []
  analyse: string = ''
  successText: string = ''
}

generateOutput() {
  const output: OutputData = new OutputData()
  output.question = this.inputText
  output.hintList = this.getHintList()
  output.inferList = this.getInferList()
  output.analyse = this.getAnalyse()
  output.successText = '今日账目整理完毕,记得及时查看收支情况哦!'
  this.outputJson = JSON.stringify(output, null, 2)
}

13. 测试用例与效果展示

13.1 测试用例设计

设计以下测试用例验证应用功能:

测试用例 1:基本功能测试

  • 输入:早餐15元,午餐30元,工资5000元
  • 预期输出:解析出三条记录,正确分类收入和支出

测试用例 2:大额支出提醒

  • 输入:购物500元,午餐20元
  • 预期输出:触发大额支出提醒

测试用例 3:餐饮消费提醒

  • 输入:早餐20元,午餐35元,晚餐40元
  • 预期输出:触发餐饮消费提醒

测试用例 4:记录数量提醒

  • 输入:早餐15元,午餐30元,晚餐25元,交通10元,购物50元,娱乐30元
  • 预期输出:触发记录数量提醒

测试用例 5:空输入校验

  • 输入:``(空字符串)
  • 预期输出:显示错误提示"请输入收支记录"

测试用例 6:不同输入格式

  • 输入:15元早餐,午餐30元,5000元工资
  • 预期输出:正确解析不同格式的输入

13.2 效果展示

以下是应用运行效果的 JSON 输出示例:

示例 1:基本输入

输入:

早餐15元,午餐30元,工资5000元

输出:

{
  "question": "早餐15元,午餐30元,工资5000元",
  "hintList": [
    "餐饮支出合计45.00元,建议关注饮食消费"
  ],
  "inferList": [
    {
      "item": "早餐",
      "amount": 15,
      "type": "支出"
    },
    {
      "item": "午餐",
      "amount": 30,
      "type": "支出"
    },
    {
      "item": "工资",
      "amount": 5000,
      "type": "收入"
    }
  ],
  "analyse": "今日总收入:5000.00元\n今日总支出:45.00元\n今日结余:4955.00元\n支出占收入比例:0.9%\n恭喜:今日有结余,可以考虑储蓄",
  "successText": "今日账目整理完毕,记得及时查看收支情况哦!"
}

示例 2:大额支出

输入:

购物500元,午餐20元

输出:

{
  "question": "购物500元,午餐20元",
  "hintList": [
    "大额支出提醒:购物,建议确认是否必要",
    "餐饮支出合计20.00元,建议关注饮食消费"
  ],
  "inferList": [
    {
      "item": "购物",
      "amount": 500,
      "type": "支出"
    },
    {
      "item": "午餐",
      "amount": 20,
      "type": "支出"
    }
  ],
  "analyse": "今日总收入:0.00元\n今日总支出:520.00元\n今日结余:-520.00元\n注意:今日支出大于收入,建议控制消费",
  "successText": "今日账目整理完毕,记得及时查看收支情况哦!"
}

14. 扩展优化方向

14.1 功能扩展

  1. 历史记录管理:添加历史记录存储和查询功能,用户可以查看以往的收支记录。
  2. 数据持久化:使用 Preferences 或数据库存储收支记录,确保数据不会丢失。
  3. 图表展示:添加收支统计图表,直观展示消费趋势和分类占比。
  4. 预算管理:支持设置月度预算,超支时提醒用户。
  5. 分类管理:增加支出分类(餐饮、交通、购物等),方便用户统计不同类别的消费。
  6. 多账户支持:支持管理多个账户的收支,如现金、银行卡、支付宝等。

14.2 技术优化

  1. 正则表达式优化:支持更多输入格式和货币单位,提高解析准确率。
  2. 性能优化:优化大数据量下的解析和渲染性能,提升应用响应速度。
  3. 错误处理:增加更完善的错误处理和用户提示,提高应用的健壮性。
  4. 国际化:支持多语言界面,满足不同地区用户的需求。
  5. 主题切换:支持深色/浅色主题,提供更好的视觉体验。

14.3 用户体验提升

  1. 输入建议:根据历史输入提供智能输入建议,减少用户输入量。
  2. 快捷操作:添加常用收支项目的快捷按钮,一键录入。
  3. 语音输入:支持语音识别输入收支记录,更加便捷。
  4. 手势操作:支持滑动删除等手势操作,提升交互体验。

结语

简易记账助手应用展示了 HarmonyOS ArkTS 开发的核心技术和最佳实践。通过声明式 UI、响应式状态管理和正则表达式解析,我们实现了一个功能完整、交互友好的轻量级记账应用。

在开发过程中,我们遇到了一些 ArkTS 语法兼容性问题,但通过合理的解决方案成功克服了这些挑战。这些经验对于后续的 HarmonyOS 应用开发具有重要的参考价值。

未来,我们可以在此基础上进一步扩展功能,提升用户体验,将简易记账助手打造成一个更加完善的个人财务管理工具。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐