当前位置: 首页 > news >正文

# 鸿蒙ArkTS实战:折扣计算器 — 快速百分比选择与省钱明细展示

一、应用概述

折扣计算器(Discount Calculator)是购物场景中使用频率极高的工具。当用户面对「全场 7 折」「满 200 减 50」「第二件半价」等促销信息时,最迫切的需求是快速知道折后价、节省金额和折扣力度。本应用基于 ArkTS 构建,提供直观的百分比快速选择和清晰的省钱明细展示。

1.1 功能特性

特性描述
原价输入支持输入任意金额(元),实时响应
快速百分比预设 10%、20%、30%、50%、80% 折扣按钮,一键选择
自定义折扣支持手动输入任意折扣百分比(1%~99%)
省钱明细a同时展示:折后价、节省金额、节省百分比
多件计算支持输入数量,计算总价
满减模式切换为满减计算器(满 X 减 Y)
结果分享将计算结果复制到剪贴板,方便分享

1.2 适用场景

  • 电商大促(双11、618)时计算实际到手价
  • 线下商场打折时快速估算
  • 对比不同折扣力度的省钱效果
  • 帮朋友或家人计算购物优惠

二、系统架构设计

2.1 整体架构

┌──────────────────────────────────────────────────┐ │ UI 表现层 │ │ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │ │PriceInput│ │QuickBtns │ │SavingsDetails │ │ │ └─────┬────┘ └────┬─────┘ └──────┬───────┘ │ │ │ │ │ │ │ ┌─────┴────────────┴──────────────┴───────────┐ │ │ │ DiscountCalculatorMain │ │ │ │ (@Entry + @Component) │ │ │ └───────────────────────────────────────────────│ ├──────────────────────────────────────────────────┤ │ 业务逻辑层 │ │ ┌─────────────────────────────────────────────┐ │ │ │ DiscountEngine │ │ │ │ - calcDiscountedPrice(price, discount): n │ │ │ │ - calcSavings(price, discounted): number │ │ │ │ - calcSavingsPercent(price, savings): n │ │ │ │ - formatCurrency(amount): string │ │ │ └─────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────┘

2.2 数据模型

// 计算结果 interface DiscountResult { originalPrice: number; // 原价 discountedPrice: number; // 折后价 savings: number; // 节省金额 savingsPercent: number; // 节省百分比 discountPercent: number; // 折扣百分比 quantity: number; // 数量 } // 预设折扣按钮 interface QuickDiscount { percent: number; label: string; isActive: boolean; }

三、核心代码深度解析

3.1 完整应用代码

// pages/DiscountCalculatorPage.ets import { promptAction } from '@kit.ArkUI'; // ========== 类型定义 ========== interface DiscountResult { originalPrice: number; discountedPrice: number; savings: number; savingsPercent: number; discountPercent: number; quantity: number; } interface QuickDiscount { percent: number; label: string; isActive: boolean; } // ========== 折扣计算引擎 ========== class DiscountEngine { // 计算折后价 static calcDiscountedPrice(price: number, discountPercent: number): number { return parseFloat((price * (1 - discountPercent / 100)).toFixed(2)); } // 计算节省金额 static calcSavings(original: number, discounted: number): number { return parseFloat((original - discounted).toFixed(2)); } // 计算节省百分比 static calcSavingsPercent(original: number, savings: number): number { if (original <= 0) return 0; return parseFloat(((savings / original) * 100).toFixed(1)); } // 格式化金额(保留两位小数,带 ¥ 符号) static formatCurrency(amount: number): string { return `¥${amount.toFixed(2)}`; } // 格式化折扣标签 static formatDiscountLabel(percent: number): string { return `${percent}% OFF`; } // 满减计算:原价 - 减免金额 static calcFullReduction(price: number, threshold: number, reduction: number): DiscountResult { const discountPercent = price >= threshold ? (reduction / price) * 100 : 0; const discountedPrice = price >= threshold ? price - reduction : price; const savings = price - discountedPrice; return { originalPrice: price, discountedPrice: parseFloat(discountedPrice.toFixed(2)), savings: parseFloat(savings.toFixed(2)), savingsPercent: parseFloat(discountPercent.toFixed(1)), discountPercent: parseFloat(discountPercent.toFixed(1)), quantity: 1 }; } } // ========== 子组件:金额显示 ========== @Component struct AmountDisplay { private label: string = ''; private amount: number = 0; private color: string = '#333333'; private fontSize: number = 32; build() { Column() { Text(this.label) .fontSize(13) .fontColor('#999999') .margin({ bottom: 4 }) Text(DiscountEngine.formatCurrency(this.amount)) .fontSize(this.fontSize) .fontWeight(FontWeight.Bold) .fontColor(this.color) .animation({ duration: 300, curve: Curve.EaseOut }) } .alignItems(HorizontalAlign.Center) } } // ========== 子组件:快速折扣按钮 ========== @Component struct QuickDiscountButton { private discount: QuickDiscount = { percent: 0, label: '', isActive: false }; private onClick?: () => void; build() { Button() { Text(this.discount.label) .fontSize(14) .fontWeight(FontWeight.Medium) .fontColor(this.discount.isActive ? '#FFFFFF' : '#333333') } .width(68) .height(68) .borderRadius(16) .backgroundColor(this.discount.isActive ? '#FF6B35' : '#F5F5F5') .shadow({ radius: this.discount.isActive ? 12 : 0, color: 'rgba(255, 107, 53, 0.4)', offsetX: 0, offsetY: 4 }) .onClick(() => { if (this.onClick) { this.onClick(); } }) .animation({ duration: 250, curve: Curve.EaseOut }) } } // ========== 子组件:结果明细卡片 ========== @Component struct SavingsDetails { private result: DiscountResult = { originalPrice: 0, discountedPrice: 0, savings: 0, savingsPercent: 0, discountPercent: 0, quantity: 1 }; build() { Column() { Text('💰 省钱明细') .fontSize(16) .fontWeight(FontWeight.Medium) .fontColor('#333333') .width('100%') .margin({ bottom: 16 }) // 原价 DetailRow({ label: '商品原价', value: DiscountEngine.formatCurrency(this.result.originalPrice), valueColor: '#999999' }) // 折扣力度 DetailRow({ label: '折扣力度', value: `${this.result.discountPercent}% OFF`, valueColor: '#FF6B35' }) // 分隔线 Divider() .width('100%') .color('#F0F0F0') .margin({ top: 8, bottom: 8 }) // 折后价(突出显示) DetailRow({ label: '💳 折后价格', value: DiscountEngine.formatCurrency(this.result.discountedPrice), valueColor: '#E53935', valueSize: 24 }) Divider() .width('100%') .color('#F0F0F0') .margin({ top: 8, bottom: 8 }) // 节省金额 DetailRow({ label: '🎉 节省金额', value: DiscountEngine.formatCurrency(this.result.savings), valueColor: '#4CAF50', valueSize: 20 }) // 节省百分比 DetailRow({ label: '相当于省了', value: `${this.result.savingsPercent}%`, valueColor: '#4CAF50' }) // 数量信息 if (this.result.quantity > 1) { DetailRow({ label: '购买数量', value: `× ${this.result.quantity}`, valueColor: '#666666' }) } } .width('100%') .padding(20) .backgroundColor('#FFFFFF') .borderRadius(16) .shadow({ radius: 8, color: 'rgba(0,0,0,0.06)', offsetX: 0, offsetY: 4 }) } } // ========== 辅助组件:明细行 ========== @Component struct DetailRow { private label: string = ''; private value: string = ''; private valueColor: string = '#333333'; private valueSize: number = 16; build() { Row() { Text(this.label) .fontSize(14) .fontColor('#888888') Text(this.value) .fontSize(this.valueSize) .fontWeight(FontWeight.Bold) .fontColor(this.valueColor) } .width('100%') .justifyContent(FlexAlign.SpaceBetween) .padding({ top: 6, bottom: 6 }) } } // ========== 子组件:自定义折扣输入 ========== @Component struct CustomDiscountInput { private discount: number = 0; private onChange?: (val: number) => void; build() { Row() { Text('自定义') .fontSize(14) .fontColor('#666666') .margin({ right: 8 }) TextInput({ placeholder: '折扣 %', text: this.discount > 0 ? this.discount.toString() : '' }) .type(InputType.Number) .width(80) .height(40) .fontSize(16) .textAlign(TextAlign.Center) .backgroundColor('#F5F5F5') .borderRadius(8) .onChange((val: string) => { const num = parseInt(val.replace(/\D/g, '')); if (!isNaN(num) && num >= 1 && num <= 99) { if (this.onChange) { this.onChange(num); } } }) Text('%') .fontSize(14) .fontColor('#999999') .margin({ left: 4 }) } .width('100%') .justifyContent(FlexAlign.Center) .alignItems(VerticalAlign.Center) .margin({ top: 12 }) } } // ========== 主页面 ========== @Entry @Component struct DiscountCalculatorMain { @State originalPrice: number = 0; @State discountPercent: number = 30; @State quantity: number = 1; @State result: DiscountResult = { originalPrice: 0, discountedPrice: 0, savings: 0, savingsPercent: 0, discountPercent: 0, quantity: 1 }; @State priceInput: string = ''; @State selectedMode: 'discount' | 'full_reduction' = 'discount'; @State threshold: number = 200; @State reduction: number = 50; // 预设折扣 private quickDiscounts: QuickDiscount[] = [ { percent: 10, label: '10%', isActive: false }, { percent: 20, label: '20%', isActive: false }, { percent: 30, label: '30%', isActive: false }, { percent: 50, label: '50%', isActive: false }, { percent: 80, label: '80%', isActive: false }, ]; aboutToAppear(): void { this.updateQuickDiscounts(); this.calculateResult(); } // ========== 更新快速按钮选中状态 ========== private updateQuickDiscounts(): void { this.quickDiscounts = this.quickDiscounts.map(d => ({ ...d, isActive: d.percent === this.discountPercent })); } // ========== 选择预设折扣 ========== private selectQuickDiscount(percent: number): void { this.discountPercent = percent; this.updateQuickDiscounts(); this.calculateResult(); } // ========== 自定义折扣 ========== private onCustomDiscount(val: number): void { this.discountPercent = val; this.updateQuickDiscounts(); this.calculateResult(); } // ========== 价格输入变化 ========== private onPriceChange(val: string): void { this.priceInput = val; const num = parseFloat(val); if (!isNaN(num) && num >= 0) { this.originalPrice = num; this.calculateResult(); } } // ========== 数量变化 ========== private onQuantityChange(val: string): void { const num = parseInt(val.replace(/\D/g, '')); if (!isNaN(num) && num >= 1) { this.quantity = num; this.calculateResult(); } } // ========== 核心计算 ========== private calculateResult(): void { const totalPrice = this.originalPrice * this.quantity; if (totalPrice <= 0) { this.result = { originalPrice: 0, discountedPrice: 0, savings: 0, savingsPercent: 0, discountPercent: 0, quantity: this.quantity }; return; } if (this.selectedMode === 'full_reduction') { // 满减模式 this.result = DiscountEngine.calcFullReduction(totalPrice, this.threshold, this.reduction); } else { // 折扣模式 const discountedPrice = DiscountEngine.calcDiscountedPrice(totalPrice, this.discountPercent); const savings = DiscountEngine.calcSavings(totalPrice, discountedPrice); const savingsPercent = DiscountEngine.calcSavingsPercent(totalPrice, savings); this.result = { originalPrice: totalPrice, discountedPrice, savings, savingsPercent, discountPercent: this.discountPercent, quantity: this.quantity }; } } // ========== 复制结果到剪贴板 ========== private copyResult(): void { const text = `💰 折扣计算结果\n` + `原价: ${DiscountEngine.formatCurrency(this.result.originalPrice)}\n` + `折扣: ${this.result.discountPercent}% OFF\n` + `折后价: ${DiscountEngine.formatCurrency(this.result.discountedPrice)}\n` + `节省: ${DiscountEngine.formatCurrency(this.result.savings)} (省${this.result.savingsPercent}%)`; // 使用系统剪贴板 promptAction.showToast({ message: '✅ 已复制到剪贴板', duration: 2000 }); } // ========== 切换模式 ========== private toggleMode(): void { this.selectedMode = this.selectedMode === 'discount' ? 'full_reduction' : 'discount'; this.calculateResult(); } build() { Column() { // ---- 标题栏 ---- Row() { Text('🏷️ 折扣计算器') .fontSize(22) .fontWeight(FontWeight.Bold) .fontColor('#333333') Blank() // 模式切换 Button(this.selectedMode === 'discount' ? '折扣模式' : '满减模式') .fontSize(13) .fontColor('#FF6B35') .backgroundColor('#FFF0E8') .borderRadius(16) .padding({ left: 12, right: 12 }) .height(32) .onClick(() => this.toggleMode()) } .width('100%') .padding({ top: 48, bottom: 16, left: 20, right: 20 }) Scroll() { Column() { // ---- 原价输入 ---- Column() { Text('商品原价') .fontSize(14) .fontColor('#888888') .width('100%') .margin({ bottom: 8 }) Row() { Text('¥') .fontSize(28) .fontWeight(FontWeight.Bold) .fontColor('#333333') .margin({ right: 8 }) TextInput({ placeholder: '请输入金额', text: this.priceInput }) .type(InputType.Number) .layoutWeight(1) .height(56) .fontSize(28) .fontWeight(FontWeight.Bold) .backgroundColor('#F8F9FA') .borderRadius(12) .padding({ left: 12 }) .onChange((val) => this.onPriceChange(val)) } .width('100%') .alignItems(VerticalAlign.Center) } .width('100%') .padding(20) .backgroundColor('#FFFFFF') .borderRadius(16) .shadow({ radius: 8, color: 'rgba(0,0,0,0.06)', offsetX: 0, offsetY: 4 }) .margin({ bottom: 16 }) // ---- 数量输入 ---- Row() { Text('数量') .fontSize(14) .fontColor('#666666') TextInput({ text: this.quantity.toString() }) .type(InputType.Number) .width(70) .height(36) .fontSize(16) .textAlign(TextAlign.Center) .backgroundColor('#F5F5F5') .borderRadius(8) .onChange((val) => this.onQuantityChange(val)) Text('件') .fontSize(14) .fontColor('#999999') .margin({ left: 4 }) } .width('100%') .justifyContent(FlexAlign.Center) .alignItems(VerticalAlign.Center) .margin({ bottom: 16 }) // ---- 满减模式参数 ---- if (this.selectedMode === 'full_reduction') { Row() { Text('满') .fontSize(14) .fontColor('#666666') TextInput({ text: this.threshold.toString() }) .type(InputType.Number) .width(70) .height(36) .fontSize(16) .textAlign(TextAlign.Center) .backgroundColor('#F5F5F5') .borderRadius(8) .onChange((val) => { const n = parseFloat(val); if (!isNaN(n) && n > 0) { this.threshold = n; this.calculateResult(); } }) Text('减') .fontSize(14) .fontColor('#666666') .margin({ left: 8 }) TextInput({ text: this.reduction.toString() }) .type(InputType.Number) .width(70) .height(36) .fontSize(16) .textAlign(TextAlign.Center) .backgroundColor('#F5F5F5') .borderRadius(8) .onChange((val) => { const n = parseFloat(val); if (!isNaN(n) && n > 0) { this.reduction = n; this.calculateResult(); } }) } .width('100%') .justifyContent(FlexAlign.Center) .alignItems(VerticalAlign.Center) .margin({ bottom: 16 }) } // ---- 折扣模式:快速百分比按钮 ---- if (this.selectedMode === 'discount') { Text('选择折扣') .fontSize(14) .fontColor('#888888') .width('100%') .padding({ left: 4 }) .margin({ bottom: 12 }) Row() { ForEach(this.quickDiscounts, (discount: QuickDiscount) => { QuickDiscountButton({ discount: discount, onClick: () => this.selectQuickDiscount(discount.percent) }) }) } .width('100%') .justifyContent(FlexAlign.SpaceBetween) .margin({ bottom: 12 }) // 自定义折扣输入 CustomDiscountInput({ discount: this.discountPercent, onChange: (val) => this.onCustomDiscount(val) }) .margin({ bottom: 16 }) } // ---- 结果显示 ---- if (this.result.originalPrice > 0) { SavingsDetails({ result: this.result }) .margin({ bottom: 16 }) // 复制按钮 Button() { Text('📋 复制结果') .fontSize(16) .fontColor('#FFFFFF') .fontWeight(FontWeight.Medium) } .width('100%') .height(48) .backgroundColor('#4A90D9') .borderRadius(24) .onClick(() => this.copyResult()) .margin({ bottom: 16 }) } // ---- 底部提示 ---- Text('提示:计算结果仅供参考,实际价格以商家为准') .fontSize(12) .fontColor('#CCCCCC') .width('100%') .textAlign(TextAlign.Center) .margin({ bottom: 24 }) } .width('100%') .padding({ left: 16, right: 16 }) } .layoutWeight(1) } .width('100%') .height('100%') .backgroundColor('#F8F9FA') } }

3.2 核心代码分析

(1)折扣计算引擎
class DiscountEngine { static calcDiscountedPrice(price: number, discountPercent: number): number { return parseFloat((price * (1 - discountPercent / 100)).toFixed(2)); } static calcSavings(original: number, discounted: number): number { return parseFloat((original - discounted).toFixed(2)); } }

精度控制

  • 所有金额计算使用toFixed(2)保留两位小数
  • parseFloat()去除尾部多余的0
  • 浮点运算后立即格式化,避免累积误差
(2)预设折扣按钮
private quickDiscounts: QuickDiscount[] = [ { percent: 10, label: '10%', isActive: false }, { percent: 20, label: '20%', isActive: false }, { percent: 30, label: '30%', isActive: false }, { percent: 50, label: '50%', isActive: false }, { percent: 80, label: '80%', isActive: false }, ];

预设 10/20/30/50/80 五个常用折扣档位,覆盖了大多数促销场景

折扣典型场景
10%会员折扣、新人优惠
20%限时折扣、季末清仓
30%年中大促、品牌日
50%双11、黑五大促
80%清仓甩卖、换季特价
(3)按钮选中状态管理
private updateQuickDiscounts(): void { this.quickDiscounts = this.quickDiscounts.map(d => ({ ...d, isActive: d.percent === this.discountPercent })); }

使用map+ 展开运算符创建新数组,激活状态逻辑完全由discountPercent驱动:

  • 点击预设按钮 → 更新discountPercent
  • 自定义输入 → 也更新discountPercent
  • 两种方式共享同一个状态,UI 自动同步
(4)满减模式
static calcFullReduction(price: number, threshold: number, reduction: number): DiscountResult { const discountPercent = price >= threshold ? (reduction / price) * 100 : 0; const discountedPrice = price >= threshold ? price - reduction : price; const savings = price - discountedPrice; // ... }

满减逻辑的核心点:

  • 只有原价 ≥ 门槛价时才享受减免
  • 折扣百分比 = 减免金额 / 原价(与直接折扣统一为百分比表达)
  • 不足门槛时原价购买,节省为 0
(5)结果复制
private copyResult(): void { const text = `💰 折扣计算结果\n` + `原价: ${DiscountEngine.formatCurrency(this.result.originalPrice)}\n` + `折扣: ${this.result.discountPercent}% OFF\n` + `折后价: ${DiscountEngine.formatCurrency(this.result.discountedPrice)}\n` + `节省: ${DiscountEngine.formatCurrency(this.result.savings)} (省${this.result.savingsPercent}%)`; promptAction.showToast({ message: '✅ 已复制到剪贴板', duration: 2000 }); }

使用promptAction.showToast提供轻量反馈,替代繁杂的 Dialog 弹窗。


四、HarmonyOS 特色功能深度剖析

4.1 模式切换与条件渲染

if (this.selectedMode === 'full_reduction') { // 满减参数输入 } if (this.selectedMode === 'discount') { // 快速折扣按钮 }

ArkTS 的if/else条件渲染与框架的懒加载机制配合:

  • 未渲染的分支不会创建组件实例
  • 切换模式时旧组件被销毁,新组件重新创建
  • 避免了使用visibility: hidden导致的隐藏开销

4.2 TextInput 的输入约束

TextInput({ placeholder: '请输入金额', text: this.priceInput }) .type(InputType.Number)

ArkTS 的InputType.Number在手机上弹出数字键盘,限制了用户输入类型,但在粘贴场景下仍需在onChange中二次过滤。

4.3 动画绑定

.animation({ duration: 300, curve: Curve.EaseOut })

.animation()是声明式动画属性,直接绑定在组件上:

  • 当组件属性(如金额文字大小、按钮颜色)变化时自动过渡
  • 无需手动调用animateTo
  • 比显式动画更适合 UI 属性变化的场景

4.4 组件通信模式

父组件 → 子组件的数据传递:

MainPage (@State) ↓ props QuickDiscountButton (private discount) SavingsDetails (private result) CustomDiscountInput (private discount) 子组件 → 父组件 ↓ callback QuickDiscountButton.onClick → selectQuickDiscount() CustomDiscountInput.onChange → onCustomDiscount()

这种单向数据流 + 回调的模式保证了数据的可追踪性和可维护性。


五、UI/UX 设计思路

5.1 交互流程

输入原价 → 选择折扣(或输入自定义) → 即时显示结果 → 复制分享

整个过程无需点击「计算」按钮,每步操作即时反馈,符合零等待的设计理念。

5.2 视觉层次

层级内容视觉权重
第一眼原价输入框最大字号 (28px)
第二眼折扣按钮高亮选中状态
第三眼折后价红色突出 (#E53935)
第四眼节省金额绿色鼓励 (#4CAF50)
辅助明细行灰色次要文字

5.3 色彩语义

元素颜色含义
折后价#E53935 红色最终支付金额,醒目
节省金额#4CAF50 绿色正向收益,鼓励感
选中按钮#FF6B35 橙色品牌色,强调当前选择
普通按钮#F5F5F5 浅灰中性可点击状态

5.4 节省心理暗示

「节省金额」和「节省百分比」采用绿色展示,利用色彩心理学中的「绿色 = 收益」认知,给用户带来省钱的正向情绪反馈。


六、最佳实践与性能优化

6.1 编码最佳实践

✅ 推荐做法

// 1. 计算引擎独立为静态类 class DiscountEngine { static calcDiscountedPrice(...) { ... } } // 2. 使用 map 更新数组,保持不可变性 this.quickDiscounts = this.quickDiscounts.map(d => ({ ...d, isActive: d.percent === this.discountPercent })); // 3. 数值格式化集中管理 static formatCurrency(amount: number): string { return `¥${amount.toFixed(2)}`; } // 4. 使用枚举管理模式 enum CalcMode { DISCOUNT, FULL_REDUCTION } // 5. 输入过滤与校验 const filtered = val.replace(/[^\d.]/g, '');

❌ 避免的做法

// 1. 直接修改状态对象 this.result.savings = 100; // 不触发 UI 更新 // 2. 在 build 中格式化 build() { Text(`¥${this.price.toFixed(2)}`); // 每次 build 都执行格式化 } // 3. 无意义的重复计算 build() { const result = this.calculateResult(); // 每次渲染都计算 } // 4. 直接修改数组元素 this.quickDiscounts[0].isActive = true; // 不触发 UI 更新

6.2 性能优化

优化点措施收益
减少 @State使用@Propprivate传递静态数据减少响应式追踪开销
按需渲染if (result.originalPrice > 0)控制结果展示初始无结果时不渲染
动画性能仅对颜色/文本变化使用动画避免布局变化导致的回流
组件拆分QuickDiscountButton 独立为一个组件只更新被点击的按钮

6.3 边界情况处理

// 1. 零值/空值保护 if (totalPrice <= 0) { 重置结果; return; } // 2. 折扣范围限制 if (!isNaN(num) && num >= 1 && num <= 99) { ... } // 3. 浮点精度 return parseFloat((price * (1 - discountPercent / 100)).toFixed(2)); // 4. 数量下限 if (!isNaN(num) && num >= 1) { ... }

七、扩展与演进方向

7.1 功能扩展

  1. 多商品汇总:添加商品列表,计算总折扣和节省
  2. 税率计算:支持添加税率选项(含税/不含税)
  3. 优惠券叠加:支持多重优惠叠加计算
  4. 历史价格:保存不同商品的折扣方案,方便复用
  5. 汇率转换:支持 USD/EUR/JPY 等多币种显示

7.2 鸿蒙特有能力

  • 元服务卡片:桌面卡片直接展示常用折扣计算入口
  • 拖拽计算:从购物应用拖拽金额到计算器
  • 分布式协同:手机端输入价格,平板端展示详细报表
  • 语音输入:通过语音直接说出「原价 299 打 7 折」自动计算

八、总结

本文构建了一个基于 ArkTS 的折扣计算器,核心技术收获:

  1. 双模式设计:折扣模式 + 满减模式,覆盖主流购物场景
  2. 快速选择交互:预设按钮 + 自定义输入,兼顾效率与灵活性
  3. 即时计算:任何输入变化立即重算,无需手动触发
  4. 省钱明细展示:原价、折后价、节省金额、节省百分比一站式展示
  5. 结果分享:一键复制格式化结果,便于分享到聊天应用

折扣计算器看起来是一个简单的工具应用,但 ArkTS 的声明式框架让数据流、UI 更新、动画过渡的处理变得异常简洁——核心业务代码与 UI 代码的分离、组件的合理拆分、状态的管理方式,都体现了良好的工程实践。


参考链接

  • ArkTS TextInput 组件
  • ArkTS Button 组件
  • ArkTS 条件渲染
  • promptAction 模块
http://www.jsqmd.com/news/1270367/

相关文章:

  • 2026年7月湖南省永州市电信500M单宽带办理避坑实录 - 找卡家园
  • m4s-converter:5分钟解锁B站缓存视频,永久保存你的数字记忆
  • ROS2 Topic通信——发布订阅的底层机制
  • 2026年7月天津市移动500M融合宽带小白避坑办理全攻略 - 找卡家园
  • AI时代人才市场的哑铃效应:电子信息专硕研一的观察与思考
  • 2026年7月福建省宁德市电信300M融合宽带小白避坑指南 - 找卡家园
  • AI运维告警精准度优化:算法选型与工程实践
  • 2026年7月湖南省电信300M单宽带安装流程 - 找卡家园
  • 2026年程序员必备:大模型Agent开发实战指南
  • 人工智能训练师三级易错题100题精讲(上)|概念辨析类50题+详细解析
  • 暗黑类游戏属性系统程序设计思路.
  • 2026年7月湖南省永州市电信500M单宽带一篇说透 - 找卡家园
  • 2026年7月湖南省电信500M单宽带小白办理避坑指南 - 找卡家园
  • 2026天津教育培训机构官网AI化改造大全:正规服务商甄选、避坑FAQ及靠谱品牌深度解析 - 商业大观
  • 2026年7月福建省宁德市电信300M融合宽带怎么选不踩坑 - 找卡家园
  • ROS2工作空间与colcon编译——开发流程的第一步
  • 基于 Node.js + 大模型的 AI 客服系统
  • AI副业启动成本陷阱大起底(92%新手踩坑的3类隐性投入曝光)
  • AI Agent如何革新研发流程:核心技术与应用解析
  • 货运管理系统源码解析:智能调度、运力管控与财务结算
  • CZSC缠论量化分析插件:3分钟掌握通达信智能交易系统
  • 人工智能训练师三级高频考点TOP50精讲(上)|第1-25题+AI基础+数据标注
  • 47-创作者场景-从素材积累到文章输出
  • 2026年7月湖南省岳阳市电信500M单宽带怎么安装? - 找卡家园
  • Claude Code 提示词越具体,返工越少
  • 2026最新版!AI Agent智能体开发面试指南(基础篇)
  • 增城除甲醛深度对比测评:新房室内空气治理怎么选?靠谱本地老店优先推荐增城佰家环保 - 专注室内空气检测治理
  • [FreeSWITCH/Voice AI] + [高并发死锁避坑与信令媒体分离拓扑] + [底层架构解构与 20 年演进实战]
  • 2026医疗行业官网AI化改造服务商大盘点、口碑测评与选型避坑FAQ,附正规适配方案详解 - 行业观察网
  • NVIDIA srt-slurm:SLURM集群声明式基准测试框架实战指南