HarmonyOS NEXT 企业级记账APP:封装金额输入组件 MoneyInput
封装金额输入组件 MoneyInput本文是《HarmonyOS NEXT 企业级开发实战30篇打造智能记账APP》系列的第09篇对应 Git Tagv0.0.9。承接第 08 篇的占位 MoneyInput本篇完善金额输入组件支持实时格式化、千分位、小数点限制、最大金额校验、清除按钮。前言金额输入是记账应用最容易踩坑的组件。浮点精度问题、千分位显示、小数位限制、最大金额校验、清除按钮、输入光标跳位——任何一个处理不当都会让用户体验崩塌。本章封装一个企业级 MoneyInput所有问题一次解决。本文将带你完善 MoneyInput 支持 ¥ 符号 大字号金额实时格式化限制 2 位小数、千分位、最大金额添加清除按钮金额非空时显示通过Link双向绑定 ViewModel处理光标跳位与键盘类型企业级核心原则金额输入必须精度可控、格式实时、反馈清晰。参考 ArkUI TextInput 了解官方约定。一、MoneyInput 接口规划1.1 完整接口Prop/Event类型说明moneystring金额字符串双向LinktypeBillType收入/支出决定颜色maxAmountnumber最大金额分默认 99999999placeholderstring占位文字默认 “0.00”onInputbuilder输入回调1.2 校验规则校项规则处理数字限制仅数字 小数点非法字符直接过滤小数位≤ 2 位多余小数位禁止输入最大金额≤ maxAmount超出 Toast 提示并截断前导零禁止 “00” 开头自动去除空态显示 placeholder灰色 “0.00”二、MoneyInput 完整实现2.1 组件源码// components/form/MoneyInput.ets import { BillType } from ../../constants/BillType; import { AppColors } from ../../theme/Colors; import { AppFontSize, AppFontWeight } from ../../theme/Typography; import { AppSpace } from ../../theme/Spacing; import { MoneyUtil } from ../../utils/MoneyUtil; import { ToastUtil } from ../../utils/ToastUtil; Component export struct MoneyInput { Link money: string; Prop type: BillType BillType.EXPENSE; Prop maxAmount: number 99999999; // 默认最大 99万9千9百9十9元9角9分 Prop placeholder: string 0.00; BuilderParam onInput: (value: string) void; private getSemanticColor(): string { return this.type BillType.INCOME ? AppColors.Income : AppColors.Expense; } build() { Row() { Text(¥) .fontSize(36) .fontWeight(AppFontWeight.Bold) .fontColor(this.getSemanticColor()) Stack({ alignContent: Alignment.Center }) { TextInput({ text: this.money, placeholder: this.placeholder, controller: this.inputController }) .layoutWeight(1) .fontSize(36) .fontWeight(AppFontWeight.Bold) .fontColor(this.getSemanticColor()) .type(InputType.Number) .placeholderColor(AppColors.SecondaryText) .placeholderFont({ size: 36, weight: FontWeight.Bold }) .backgroundColor(Color.Transparent) .borderWidth(0) .onChange((value: string) { this.handleInput(value); }) // 清除按钮金额非空时显示 if (this.money.length 0) { Image($r(app.media.icon_clear)) .width(20) .height(20) .fillColor(AppColors.SecondaryText) .position({ x: 100%, y: 50% }) .markAnchor({ x: 100%, y: 50% }) .offset({ x: -8, y: 0 }) .onClick(() { this.handleClear(); }) } } .layoutWeight(1) } .width(100%) .padding({ top: AppSpace.LG, bottom: AppSpace.LG }) .alignItems(HorizontalAlign.Center) } private inputController: TextInputController new TextInputController(); /** 输入处理校验 格式化 回调 */ private handleInput(raw: string): void { const filtered this.filterInvalidChars(raw); const limited this.limitDecimalPlaces(filtered); const trimmed this.trimLeadingZeros(limited); const checked this.checkMaxAmount(trimmed); if (checked ! this.money) { this.money checked; this.onInput(checked); // 光标定位到末尾 this.inputController.caretPosition(checked.length); } } /** 过滤非法字符 */ private filterInvalidChars(s: string): string { return s.replace(/[^0-9.]/g, ); } /** 限制小数位 ≤ 2 */ private limitDecimalPlaces(s: string): string { const parts s.split(.); if (parts.length 1) return parts[0]; const intPart parts[0]; const decPart parts[1].substring(0, 2); return ${intPart}.${decPart}; } /** 去除前导零 */ private trimLeadingZeros(s: string): string { if (s 0 || s 0. || s 0.0 || s 0.00) return s; return s.replace(/^0([1-9])/, $1); } /** 校验最大金额 */ private checkMaxAmount(s: string): string { if (s.length 0) return s; const cents MoneyUtil.toCents(parseFloat(s) || 0); if (cents this.maxAmount) { ToastUtil.show(金额超出最大限制); return MoneyUtil.format(this.maxAmount); } return s; } /** 清除 */ private handleClear(): void { this.money ; this.onInput(); this.inputController.caretPosition(0); } }2.2 关键实现解析Link双向绑定父 ViewModel 改 money 同步刷新子组件改 money 父组件同步TextInputController通过caretPosition控制光标位置避免格式化后跳到开头Stack叠放清除按钮用positionmarkAnchoroffset精确定位到输入框右侧onChange链路raw → 过滤 → 限小数位 → 去前导零 → 校验最大值 → 同步 ViewModelInputType.Number弹起数字键盘仅含 0-9 与小数点三、MoneyUtil 工具类升级3.1 完善后源码// utils/MoneyUtil.ets升级版 export class MoneyUtil { /** 分 → 元字符串保留 2 位小数 */ static format(cents: number): string { return (cents / 100).toFixed(2); } /** 元 → 分四舍五入 */ static toCents(yuan: number): number { return Math.round(yuan * 100); } /** 千分位格式化1234.56 → 1,234.56 */ static formatWithComma(cents: number): string { const yuan this.format(cents); const parts yuan.split(.); parts[0] parts[0].replace(/\B(?(\d{3})(?!\d))/g, ,); return parts.join(.); } /** 解析字符串为分容错 */ static parseToCents(s: string): number { const yuan parseFloat(s.replace(/,/g, )) || 0; return this.toCents(yuan); } /** 中文金额1234.56 → 壹仟贰佰叁拾肆元伍角陆分 */ static toChinese(cents: number): string { const yuan cents / 100; // 简化版实际需完整中文转换逻辑 return ¥${yuan.toFixed(2)}; } }3.2 命名规范方法输入输出用途format分元字符串UI 展示toCents元分持久化formatWithComma分千分位元字符串统计报表parseToCents元字符串分解析输入toChinese分中文金额大额展示四、AddBillViewModel 集成升级4.1 改为 Link 双向绑定// viewmodel/AddBillViewModel.ets关键改动 // 旧money: string // 新通过 Link 直接绑定到 MoneyInput // pages/AddBillView.ets State viewModel: AddBillViewModel new AddBillViewModel(); MoneyInput({ money: this.viewModel.money, // 必须 LinkArkUI V1 用法 type: this.viewModel.currentType, maxAmount: 10000000, // 最大 10万元 onInput: (value: string) { this.viewModel.money value; } })4.2 金额校验升级// viewmodel/AddBillViewModel.ets canSave(): boolean { if (this.money.length 0) return false; const cents MoneyUtil.parseToCents(this.money); if (cents 0) return false; if (cents 10000000) return false; // 超出 10万元限制 if (!this.selectedCategory) return false; return true; } async save(): Promiseboolean { if (!this.canSave()) { if (this.money.length 0) { ToastUtil.show(请输入金额); } else if (!this.selectedCategory) { ToastUtil.show(请选择分类); } else { ToastUtil.show(金额无效); } return false; } const cents MoneyUtil.parseToCents(this.money); // ... 持久化同前 }五、最佳实践5.1 光标跳位问题// 错误onChange 后光标自动跳到开头 .onChange((value: string) { this.money this.format(value); // 格式化后光标乱了 }) // 正解用 TextInputController.caretPosition 主动定位 private inputController: TextInputController new TextInputController(); .onChange((value: string) { this.money this.format(value); this.inputController.caretPosition(this.money.length); // 光标末尾 })5.2 浮点精度问题// ❌ 错误直接用浮点存储金额 money: number 0; // 0.1 0.2 0.30000000000000004精度丢失 // ✅ 正解金额统一以分整数存储 money: number 0; // 单位分 // UI 展示时调用 MoneyUtil.format(300) 3.005.3 输入限制实战输入过滤后限小位去前导最终abc12312312312312312.34512.34512.3412.3412.3400.5000.5000.500.500.5099999999999999999999999999999999校验最大金额六、运行验证6.1 编译检查hvigorw assembleHap--modemodule-pproductdefault6.2 功能验证金额输入仅接受数字与小数点小数点后最多 2 位多余自动截断前导零自动去除00.5 → 0.5超出最大金额 Toast 提示并截断非空时显示清除按钮点击清空收入态金额绿色支出态红色截图清单① 默认空态 placeholder ② 输入数字实时格式 ③ 收入/支出色切换 ④ 清除按钮。七、常见问题7.1 千分位输入冲突// 如果在输入时显示千分位用户继续输入会错位 // 解决输入态显示纯数字失焦后再格式化为千分位 .onBlur(() { this.displayMoney MoneyUtil.formatWithCommas(cents); })HarmonyLedger 暂不启用输入时千分位避免冲突。统计报表展示时才用千分位。7.2 InputType.Number 不含小数点部分 HarmonyOS 版本数字键盘不含小数点需改用InputType.Normal并手动过滤。7.3 清除按钮遮挡输入// 用 Stack position 定位避免遮挡 Stack() { TextInput() // 占满 Image().position({ x: 100% }).markAnchor({ x: 100% }).offset({ x: -8 }) }八、Git 提交8.1 Commit Messagegitadd.gitcommit-mfeat(component): 完善金额输入组件 MoneyInput - 支持 ¥ 符号 大字号金额展示 - 实时格式化过滤非法字符、限制 2 位小数、去前导零 - 添加清除按钮非空时显示 - 用 TextInputController 处理光标跳位 - 校验最大金额并 Toast 提示 - 升级 MoneyUtil 新增千分位、解析、中文金额8.2 CHANGELOG## [v0.0.9] - 2026-07-27 ### Added - components/form/MoneyInput.ets完善版金额输入 - utils/MoneyUtil.ets新增 formatWithComma/parseToCents/toChinese ### Changed - viewmodel/AddBillViewModel.ets用 parseToCents 校验金额总结本文完整介绍了MoneyInput 组件的封装涵盖实时格式化、光标控制、清除按钮、最大金额校验、MoneyUtil 升级。通过本篇你可以用 TextInputController 解决光标跳位用正则过滤非法字符与限制小数位用 Stack position 精确定位辅助按钮理解金额存储用整数分避免精度问题通过 Link 双向同步 ViewModel下一篇预告《分类选择器开发》 将完善 CategorySelector 组件支持网格布局、图标 名称、选中态高亮、新增分类入口。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力相关资源本篇源码GitHub Tag v0.0.9ArkUI TextInputtextinputTextInputControllertextinput-controllerInputType 枚举inputtypeJavaScript 数值精度floating-point-guide鸿蒙金额处理实践money-handlingArkUI Stack 布局stack

相关新闻

有刷与无刷电机核心原理、驱动电路及FOC控制全解析

有刷与无刷电机核心原理、驱动电路及FOC控制全解析

1. 项目概述:从“刷”字说起,两种电机的核心分野聊到电机,尤其是直流电机,绕不开“有刷”和“无刷”这两个词。这听起来像是电机内部有没有一把小刷子,实际上,这个“刷”字,确实形象地指向了两种…

2026/7/30 2:46:43 阅读更多 →
Kimi K3开放权重了:2.8万亿参数意味着什么?普通电脑能跑吗?小白一文看懂

Kimi K3开放权重了:2.8万亿参数意味着什么?普通电脑能跑吗?小白一文看懂

Kimi K3开放权重了:2.8万亿参数意味着什么?普通电脑能跑吗?小白一文看懂 最近,Kimi-K3 在大模型和开源社区里迅速走红。 有人说它有 2.8万亿参数,有人说它一次能处理 100万 token,还有人看到 GitHub 和 H…

2026/7/30 2:45:43 阅读更多 →
GESP2026年3月认证C++七级( 第一部分选择题(1-7))精讲

GESP2026年3月认证C++七级( 第一部分选择题(1-7))精讲

第一题 递推式 T(n)2T(n-1)1第一步:不要急着套公式很多同学一看到递推式,就想到 Master 定理。但是 Master 定理只适用于T(n)aT(n/b)f(n)例如T(n)2T(n/2)n而本题是T(n)2T(n-1)1这里不是n 变成 n/2而是n 变成 n-1所以 Master 定理不能使用!第二…

2026/7/30 2:45:43 阅读更多 →

最新新闻

CAN FD与经典CAN 2.0帧结构深度对比与工程实践指南

CAN FD与经典CAN 2.0帧结构深度对比与工程实践指南

1. 项目概述:为什么我们需要对比CAN FD与经典CAN 2.0?如果你在汽车电子、工业控制或者机器人领域工作,那么“CAN总线”这个词对你来说就像空气一样熟悉。但最近几年,一个升级版的“CAN FD”开始越来越多地出现在项目需求、芯片选型…

2026/7/30 2:54:46 阅读更多 →
终极指南:VSCode Mermaid Preview高效图表可视化解决方案

终极指南:VSCode Mermaid Preview高效图表可视化解决方案

终极指南:VSCode Mermaid Preview高效图表可视化解决方案 【免费下载链接】vscode-mermaid-preview Previews Mermaid diagrams 项目地址: https://gitcode.com/gh_mirrors/vs/vscode-mermaid-preview 在当今技术文档编写和系统设计过程中,图表可…

2026/7/30 2:54:45 阅读更多 →
Palworld存档转换工具终极指南:轻松备份、修复和管理你的游戏进度

Palworld存档转换工具终极指南:轻松备份、修复和管理你的游戏进度

Palworld存档转换工具终极指南:轻松备份、修复和管理你的游戏进度 【免费下载链接】palworld-save-tools Tools for converting Palworld .sav files to JSON and back 项目地址: https://gitcode.com/gh_mirrors/pa/palworld-save-tools Palworld存档转换工…

2026/7/30 2:54:45 阅读更多 →
League Akari终极指南:英雄联盟智能助手全功能解析

League Akari终极指南:英雄联盟智能助手全功能解析

League Akari终极指南:英雄联盟智能助手全功能解析 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power 🚀. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit League Akari是一款基于官方LCU…

2026/7/30 2:54:45 阅读更多 →
Waves Ultimate 17一键安装完整版安装教程Waves 17最新版VR/R2R下载专用混音插件Win/Mac系统Waves 17/16/15/14视频安装教程一键安装

Waves Ultimate 17一键安装完整版安装教程Waves 17最新版VR/R2R下载专用混音插件Win/Mac系统Waves 17/16/15/14视频安装教程一键安装

Win/Mac Waves 17 / Waves16 最新中文完整版​ Waves 17 下载链接:Win系统 https://www.dygdu.com/16997.htmlMac 系统 https://www.dygdu.com/17000.html“Waves 17”通常指的是Waves Audio(全球知名音频插件开发商)推出的第16代音频插件套装…

2026/7/30 2:54:45 阅读更多 →
Android Camera2 API数码变焦实现:从SCALER_CROP_REGION到平滑缩放

Android Camera2 API数码变焦实现:从SCALER_CROP_REGION到平滑缩放

1. 从Camera1到Camera2:为什么数码变焦的实现方式变了?如果你是从Camera1 API时代过来的Android开发者,第一次接触Camera2 API时,可能会觉得有点懵。在Camera1里,控制相机预览缩放,我们通常直接操作Camera.…

2026/7/30 2:53:45 阅读更多 →

日新闻

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南 【免费下载链接】DriverStoreExplorer Driver Store Explorer 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer 您是否曾因Windows系统盘空间不足而烦恼?是否遇到过设…

2026/7/30 0:00:13 阅读更多 →
如何3步掌握Video Download Helper:网页视频下载的完整实战指南

如何3步掌握Video Download Helper:网页视频下载的完整实战指南

如何3步掌握Video Download Helper:网页视频下载的完整实战指南 【免费下载链接】VideoDownloadHelper Chrome Extension to Help Download Video for Some Video Sites. 项目地址: https://gitcode.com/gh_mirrors/vi/VideoDownloadHelper 你是否曾经在浏览…

2026/7/30 0:00:13 阅读更多 →
“双减”后首个AI备课压力测试报告:覆盖32所中小学的176节AI辅助课,暴露4大隐性增负节点

“双减”后首个AI备课压力测试报告:覆盖32所中小学的176节AI辅助课,暴露4大隐性增负节点

更多请点击: https://intelliparadigm.com 第一章:AI 教师备课辅助 AI 教师备课辅助系统正逐步成为教育数字化转型的核心支撑工具,它并非替代教师,而是通过语义理解、知识图谱与多模态生成能力,将教师从重复性劳动中解…

2026/7/30 0:00:13 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/29 22:18:20 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/29 14:34:28 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/29 15:00:03 阅读更多 →

月新闻