一、项目概述
本Demo是一个基于ArkTS语言的HarmonyOS ATM 应用简易实现,主要功能包括插卡进入系统、余额查询、存款与取款操作。该项目采用声明式UI设计和组件化开发思想,实现了基础的银行ATM交互逻辑。
二、技术架构
1. 项目结构
- Index.ets:页面入口文件,提供全局状态管理。
- /compents/index.ets:包含多个可复用的组件。
- /utils/index.ets:工具函数封装模块。
2. 核心技术栈
- ArkTS:基于TypeScript扩展的声明式编程语言。
- HarmonyOS SDK:用于调用系统级能力如弹窗提示等。
- 组件化开发模式:通过装饰器实现组件间通信。
三、核心功能实现
1. 状态管理
使用 @Provide
和 @Consume
装饰器进行跨层级组件状态共享:
// Index.ets - 提供全局状态
@Provide balanceMoney: number = 0
@Provide enterAmount: string = ''
@Provide isStart: boolean = false
// DisplayReveal & KeyBord - 消费全局状态
@Consume balanceMoney: number
@Consume enterAmount: string
@Consume isStart: boolean
作用机制:
@Provide
提供的状态可在其子组件树中被任意层级的 @Consume
消费。
状态更新时,所有消费该状态的组件会自动刷新 UI
2. 主要组件解析
a. TopTitle
顶部标题栏,展示静态内容“ATM”。
@Component
struct TopTitle {
build() {
Row(){
Text('ATM')
.fontSize(18)
.fontWeight(600)
.fontColor(Color.Blue)
}
.justifyContent(FlexAlign.Center)
.border({width:{bottom:1},color:Color.Blue})
.padding(5)
.width('100%')
}
}
b. DisplayReveal
显示账户余额及输入框,根据是否插卡状态切换显示内容。
@Component
struct DisplayReveal {
@Consume balanceMoney:number
@Consume enterAmount: string
@Consume isStart: boolean
build() {
Column(){
Row(){
Text() {
if (this.isStart) {
Span('余额: ¥')
Span(`${this.balanceMoney.toFixed(2)}`)
} else {
Span('请先插卡进入银行管理系统..')
}
}
.layoutWeight(1)
.fontColor('#405046')
}
.border({width:{bottom:1},style:BorderStyle.Dashed})
.padding({left: 16,right: 16,top: 10,bottom: 10})
.width('100%')
TextInput({placeholder:'请输入金额...',text: $$this.enterAmount})
.type(InputType.Number)
.borderRadius(0)
.backgroundColor(Color.Transparent)
}
.backgroundColor('#798d6b')
.borderRadius({topLeft: 10, topRight: 10})
}
}
c. KeyBord
虚拟键盘组件,处理插卡/拔卡、金额输入、存取款等业务逻辑。
@Component
struct KeyBord {
@Consume balanceMoney: number
@Consume enterAmount: string
@Consume isStart: boolean
build() {
Column(){
Grid(){
ForEach(Array(15).fill(''), (t: string, i: number) => {
GridItem(){
if (i < 2) {
ControlButton({text: `${i == 0 ? '插卡' : '拔卡'}`, fontColor: `${i ==0 ? '#768fff' : '#b168fc'}` })
} else if (i === 2){
ControlButton({icon: 'ic_del_arrow', imgFillColor: Color.Red})
} else if (i === 12) {
ControlButton({text: '取', bgcColor: Color.Red})
} else if (i === 14) {
ControlButton({text: '存', bgcColor: Color.Blue})
} else {
Text(`${(i - 3) === 10 ? 0 : ((i - 3) + 1)}`)
.width(60)
.height(60)
.borderRadius(50)
.fontSize(24)
.fontColor(Color.White)
.textAlign(TextAlign.Center)
.backgroundColor('#5e636c')
}
}
.onClick(() => {
if (!this.isStart && i !== 0) {
promptAction.showToast({message: '请先插卡!谢谢!', textColor: Color.Red})
return
}
if (i === 2) { // 删除按键
this.enterAmount = this.enterAmount.slice(0, -1)
return
}
if (i === 0) { //进入按键(插卡)
if (this.isStart) {
promptAction.showToast({message: '请勿重复插卡!谢谢!', textColor: Color.Red})
return;
}
this.isStart = true
this.balanceMoney = 100000
this.enterAmount = ''
promptAction.showToast({
message: '欢迎进入银行账户资金管理系统!',
textColor:Color.Pink,
duration: 3000})
return
}
if (i === 1){ // 退出按键(拔卡)
this.balanceMoney = 0
this.enterAmount = ''
promptAction.showToast({message: '谢谢,欢迎下次光临,已拔卡!',textColor:Color.Blue})
this.isStart = false
return
}
if (i === 12 || i === 14) {
if (!this.enterAmount.trim()) {
promptAction.showToast({message: '请先输入金额!',textColor:Color.Red})
return
}
if (i === 12) { // 取钱
if ( Number(this.enterAmount) > this.balanceMoney ) {
promptAction.showToast({message: '余额不足!',textColor:Color.Red})
return
}
this.balanceMoney -= Number(this.enterAmount)
promptAction.showToast({
message: `已取出人民币${Number(this.enterAmount).toFixed(2)}元,剩余人民币${this.balanceMoney.toFixed(2)}元,请继续操作!`,
textColor:Color.Green,
duration: 2000
})
} else { // 存钱
this.balanceMoney += Number(this.enterAmount)
promptAction.showToast({
message: `存入人民币${Number(this.enterAmount).toFixed(2)}元,余额${this.balanceMoney.toFixed(2)}元,10个小目标指日可待!`,
textColor:Color.Green,
duration: 2000
})
}
this.enterAmount = ''
return;
}
const entry = computedKeyBordNum(i - 3)
this.enterAmount += `${entry}`
})
})
}
.rowsGap(10)
.columnsTemplate('1fr 1fr 1fr')
.borderStyle(BorderStyle.Dotted)
}
.height(380)
.padding({top: 15})
.backgroundColor('#eaecf3')
}
}
- 按钮布局:使用
Grid
+ForEach
实现15个按键。 - 事件绑定:每个按键通过
onClick
处理不同行为。 - 逻辑控制:判断用户是否已插卡,验证金额合法性等。
d. ControlButton
通用按钮组件,支持文本或图标形式渲染。
@Component
struct ControlButton {
@Prop text: string = ''
@Prop bgcColor: string | Color = '#cfd2e0'
@Prop icon: string = ''
@Prop fontSize: number = 20
@Prop fontColor: string | Color = '#fff'
@Prop imgFillColor: string | Color
@Styles
controlStyles(){
.width(60)
.height(60)
.borderRadius(50)
.backgroundColor(this.bgcColor)
}
build() {
if (this.icon) {
Image($r(`app.media.${this.icon}`))
.controlStyles()
.padding(9)
.fillColor(this.imgFillColor)
} else {
Text(`${this.text}`)
.fontSize(this.fontSize)
.fontColor(this.fontColor)
.textAlign(TextAlign.Center)
.controlStyles()
}
}
}
四、关键业务逻辑
1. 插卡/拔卡流程
插卡 (i === 0
):初始化账户余额为100000元,设置 isStart = true
。
if (i === 0) {
if (this.isStart) {
promptAction.showToast({message: '请勿重复插卡!谢谢!'})
return;
}
this.isStart = true
this.balanceMoney = 100000
this.enterAmount = ''
promptAction.showToast({message: '欢迎进入银行账户资金管理系统!'})
}
拔卡 (i === 1
):重置所有状态,并提示用户已拔卡。
if (i === 1){
this.balanceMoney = 0
this.enterAmount = ''
promptAction.showToast({message: '谢谢,欢迎下次光临,已拔卡!'})
this.isStart = false
}
2. 金额输入
数字键点击后调用 computedKeyBordNum(i - 3)
映射对应数字
const computedKeyBordNum = (entryNum: number) => {
return entryNum == 10 ? '0' : `${entryNum + 1}`
}
此函数将索引值转换为对应的数字字符,例如:
entryNum = 0 → 1
entryNum = 10 → '0'
然后拼接到 enterAmount 字符串中,用于后续取款或存款判断。
3. 存取款操作
取款 (i === 12
):检查余额是否足够,不足则提示。
if ( Number(this.enterAmount) > this.balanceMoney ) {
promptAction.showToast({message: '余额不足!'})
return
}
this.balanceMoney -= Number(this.enterAmount)
promptAction.showToast({
message: `已取出人民币${Number(this.enterAmount).toFixed(2)}元...`
})
存款 (i === 14
):直接增加余额并提示成功信息。
this.balanceMoney += Number(this.enterAmount)
promptAction.showToast({
message: `存入人民币${Number(this.enterAmount).toFixed(2)}元...`
})
五、UI 设计亮点
- 响应式布局:使用
Flex
和Grid
实现自适应布局。 - 颜色主题统一:采用蓝色系作为主色调,提升用户体验。
- 交互反馈机制:利用
promptAction.showToast
提供及时操作反馈。
六、可拓展性建议
- 数据持久化:当前余额仅在内存中维护,后续可接入数据库或本地存储。
- 安全性增强:添加密码验证环节,防止非法访问。
- 多语言支持:适配国际化需求,根据不同地区显示相应语言提示。
- 动画效果优化:对按钮点击、金额变化等添加过渡动画,提升视觉体验。
七、总结
该ATM Demo 展示了如何利用ArkTS构建一个结构清晰、易于维护的小型金融类应用。通过合理划分组件职责并有效管理状态,可以快速搭建出具备基本交互能力的应用原型。未来可根据实际需求进一步完善安全机制与复杂业务流程。