简易记账助手:基于 HarmonyOS API 24 的声明式应用开发全解析
项目演示

目录
- 项目概述与开发背景
- HarmonyOS API 24 技术特性深度剖析
- ArkTS 语言核心概念与实践
- 项目结构与初始化流程
- UI 设计与布局架构详解
- 状态管理与响应式机制原理
- 收支记录解析引擎设计与实现
- 数据结构设计与演进
- 记账优化提醒系统实现
- 收支分析算法实现
- JSON 输出规范与序列化机制
- 构建问题与踩坑记录
- 测试用例与效果展示
- 扩展优化方向
1. 项目概述与开发背景
1.1 记账应用的市场需求
随着数字化生活的普及,个人财务管理成为越来越多人关注的话题。传统的记账方式存在以下痛点:
- 记录繁琐:手动输入每笔收支需要打开专门的应用,操作步骤多,用户体验不佳。
- 格式不统一:不同用户有不同的记录习惯,有的喜欢分类记录,有的喜欢自由输入,难以统一管理。
- 分析滞后:缺乏实时的收支分析和消费提醒功能,用户无法及时了解自己的财务状况。
- 跨平台限制:部分记账应用无法在 HarmonyOS 设备上完美运行,用户被迫使用多个应用。
简易记账助手正是为了解决这些问题而设计的轻量级应用。它采用自然语言输入方式,用户只需输入类似"早餐15元,午餐30元,工资5000元"的文本,系统即可自动解析并生成结构化的收支记录。这种输入方式更加直观、高效,符合用户的日常使用习惯。
1.2 应用核心功能
本应用实现了以下核心功能:
- 自然语言输入:支持用户以自然语言方式输入收支记录,无需学习复杂的操作流程。
- 智能解析:自动识别收支项目、金额和类型,支持多种输入格式。
- 记账提醒:根据消费情况生成个性化的记账优化建议,帮助用户更好地管理财务。
- 收支分析:自动计算总收入、总支出、结余及支出占比,提供直观的财务分析报告。
- JSON 输出:生成标准的 JSON 格式输出,便于数据交换和存储。
1.3 技术选型考量
在技术选型方面,我们选择了 HarmonyOS ArkTS 作为开发语言,主要基于以下考量:
- 声明式 UI:ArkTS 的声明式语法使 UI 开发更加直观高效,开发者可以用更少的代码实现复杂的界面。
- 响应式状态管理:@State 装饰器提供了简洁的状态管理方案,数据变化自动触发 UI 更新。
- API 24 兼容性:支持最新的 HarmonyOS 特性,同时保持良好的向后兼容性。
- 跨设备适配:一套代码可运行在手机、平板、智慧屏等多种设备上,降低开发成本。
2. HarmonyOS API 24 技术特性深度剖析
2.1 API 24 概述
HarmonyOS API 24 是华为鸿蒙操作系统的重要版本,引入了许多新特性和改进。对于应用开发者而言,API 24 提供了更强大的开发能力和更好的用户体验。
API 24 主要包含以下几个方面的更新:
- 声明式 UI 框架增强:进一步完善了声明式 UI 语法,提供了更多的组件和布局选项。
- 性能优化:优化了应用启动速度和运行性能,提升了用户体验。
- 新能力开放:开放了更多系统能力,如分布式数据管理、跨设备协同等。
- 开发工具改进:改进了 DevEco Studio 开发工具,提供了更好的调试和测试支持。
2.2 声明式 UI 框架
API 24 进一步完善了声明式 UI 框架,开发者可以使用简洁的语法描述 UI 结构和行为。
声明式 UI 的核心思想是:UI 是数据的函数,当数据变化时,UI 自动更新。这种方式相比命令式 UI 开发有以下优势:
- 代码可读性更高:UI 结构一目了然,开发者可以快速理解界面布局。
- 状态管理更简单:数据变化自动触发 UI 更新,无需手动操作 DOM。
- 开发效率更高:减少了大量的样板代码,开发者可以专注于业务逻辑。
在 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 强调组件化开发理念,每个组件都是独立的、可复用的模块。
组件化开发的优势:
- 代码复用:组件可以在多个页面中复用,减少重复代码。
- 职责分离:每个组件只负责自己的功能,代码更加清晰。
- 易于维护:组件独立开发和测试,便于维护和更新。
在 ArkTS 中,组件的定义使用以下装饰器:
- @Entry:标识应用的入口组件,一个应用只能有一个入口组件。
- @Component:标识自定义组件,可以被其他组件引用。
- build():组件的构建方法,返回组件的 UI 结构。
2.4 响应式状态管理
API 24 的响应式状态管理是其核心特性之一。通过 @State 装饰器,开发者可以轻松实现数据驱动的 UI 更新。
响应式状态管理的工作原理:
- 状态定义:使用
@State装饰器定义状态变量。 - UI 绑定:在 UI 组件中绑定状态变量。
- 自动更新:当状态变量的值发生变化时,所有绑定该变量的 UI 组件自动重新渲染。
这种机制大大简化了状态管理的复杂度,开发者无需手动管理 UI 更新。
3. ArkTS 语言核心概念与实践
3.1 ArkTS 简介
ArkTS 是 HarmonyOS 应用开发的主力语言,它在 TypeScript 的基础上扩展了声明式 UI 语法和状态管理能力。
ArkTS 的主要特点:
- 强类型系统:继承了 TypeScript 的强类型系统,提供了更好的代码提示和错误检查。
- 声明式 UI:扩展了 TypeScript,提供了声明式 UI 语法,使 UI 开发更加直观。
- 状态管理:提供了多种状态管理装饰器,如 @State、@Prop、@Link 等。
- 与 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
各目录和文件的作用:
- entry/:应用的主模块,包含应用的代码和资源。
- src/main/ets/pages/:页面组件目录,存放应用的各个页面。
- src/main/ets/AppScope/:应用全局配置目录。
- src/main/ets/entryability/:应用入口能力目录。
- src/main/resources/:应用资源目录,包含图片、字符串、样式等资源。
- build-profile.json5:项目构建配置文件。
- hvigorfile.ts:构建脚本配置文件。
在本项目中,所有的业务逻辑都集中在 Index.ets 文件中,这符合用户"页面都要在 index.ets 内完成"的要求。
4.2 初始化步骤
创建一个新的 HarmonyOS ArkTS 项目需要以下步骤:
- 打开 DevEco Studio:启动华为官方开发工具 DevEco Studio。
- 选择"Create Project":在欢迎界面选择"Create Project"创建新项目。
- 选择模板:选择"Empty Ability"模板,这是一个空的应用模板。
- 配置项目:输入项目名称、包名、保存路径等信息。
- 选择 API 版本:选择 API 24 作为目标 API 版本。
- 完成创建:点击"Finish"按钮完成项目创建。
项目创建完成后,DevEco Studio 会自动生成基本的项目结构和代码。
4.3 配置文件说明
项目的配置文件包括:
- build-profile.json5:项目构建配置文件,定义了构建相关的配置,如编译选项、签名配置等。
- hvigorfile.ts:构建脚本配置文件,用于自定义构建流程。
- module.json5:模块配置文件,定义了应用的基本信息,如应用名称、图标、权限等。
这些配置文件是应用运行的基础,开发者需要根据实际需求进行配置。
5. UI 设计与布局架构详解
5.1 整体布局结构
本应用采用了简洁的垂直布局结构,主要分为以下几个区域:
- 标题区域:显示应用名称"简易记账助手",使用大字体和加粗样式突出显示。
- 输入区域:TextArea 组件,用于输入收支记录,支持多行输入。
- 错误提示区域:显示输入校验错误信息,使用红色字体突出显示。
- 按钮区域:包含"开始记账"和"重置"两个按钮,水平排列。
- 结果展示区域:显示 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 的关键属性:
- placeholder:占位提示文本,引导用户输入格式。
- onChange:输入内容变化时的回调函数,实时更新状态变量。
- borderRadius:圆角边框,提升视觉效果。
- borderWidth/borderColor:边框宽度和颜色。
- padding:内边距,控制文本与边框的距离。
- 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 组件都会重新渲染。
状态变化的触发流程:
- 状态修改:开发者修改状态变量的值。
- 依赖检测:框架检测哪些 UI 组件依赖了该状态变量。
- 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 解析流程概述
收支记录解析是本应用的核心功能之一,其流程如下:
- 输入校验:检查用户输入是否为空。
- 文本分割:按逗号分割输入文本。
- 正则匹配:对每个片段进行正则匹配。
- 类型推断:根据关键词推断收支类型。
- 去重处理:避免重复记录。
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 数据流转过程
数据在应用中的流转过程:
- 用户输入:用户在 TextArea 中输入收支记录文本。
- 状态更新:
inputText状态变量更新为用户输入的文本。 - 解析处理:点击"开始记账"按钮后,调用
processAccount()方法进行解析。 - 记录存储:解析后的记录存储在
records状态变量中。 - 业务处理:调用
getHintList()、getInferList()、getAnalyse()方法生成相关数据。 - 输出组装:调用
generateOutput()方法组装输出数据。 - JSON 序列化:使用
JSON.stringify()将输出数据序列化为 JSON 字符串。 - UI 展示:
outputJson状态变量更新,UI 自动显示 JSON 输出。
9. 记账优化提醒系统实现
9.1 提醒规则设计
记账优化提醒系统根据以下规则生成提醒:
- 大额支出提醒:单笔支出超过 100 元,提醒用户确认是否必要。
- 餐饮消费提醒:餐饮类支出合计超过 50 元,提醒用户关注饮食消费。
- 记录数量提醒:记录数量超过 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 功能扩展
- 历史记录管理:添加历史记录存储和查询功能,用户可以查看以往的收支记录。
- 数据持久化:使用 Preferences 或数据库存储收支记录,确保数据不会丢失。
- 图表展示:添加收支统计图表,直观展示消费趋势和分类占比。
- 预算管理:支持设置月度预算,超支时提醒用户。
- 分类管理:增加支出分类(餐饮、交通、购物等),方便用户统计不同类别的消费。
- 多账户支持:支持管理多个账户的收支,如现金、银行卡、支付宝等。
14.2 技术优化
- 正则表达式优化:支持更多输入格式和货币单位,提高解析准确率。
- 性能优化:优化大数据量下的解析和渲染性能,提升应用响应速度。
- 错误处理:增加更完善的错误处理和用户提示,提高应用的健壮性。
- 国际化:支持多语言界面,满足不同地区用户的需求。
- 主题切换:支持深色/浅色主题,提供更好的视觉体验。
14.3 用户体验提升
- 输入建议:根据历史输入提供智能输入建议,减少用户输入量。
- 快捷操作:添加常用收支项目的快捷按钮,一键录入。
- 语音输入:支持语音识别输入收支记录,更加便捷。
- 手势操作:支持滑动删除等手势操作,提升交互体验。
结语
简易记账助手应用展示了 HarmonyOS ArkTS 开发的核心技术和最佳实践。通过声明式 UI、响应式状态管理和正则表达式解析,我们实现了一个功能完整、交互友好的轻量级记账应用。
在开发过程中,我们遇到了一些 ArkTS 语法兼容性问题,但通过合理的解决方案成功克服了这些挑战。这些经验对于后续的 HarmonyOS 应用开发具有重要的参考价值。
未来,我们可以在此基础上进一步扩展功能,提升用户体验,将简易记账助手打造成一个更加完善的个人财务管理工具。
更多推荐



所有评论(0)