鸿蒙应用购物车模块开发实践:从代码解析到功能实现
一、引言
在鸿蒙(HarmonyOS)应用生态中,购物车功能是电商类应用的核心组成部分,它承担着整合用户选购商品、计算总价、支撑支付流程等关键职责。本文将深入剖析一段鸿蒙应用购物车功能的实现代码,详细解读从商品数据管理、界面交互到支付流程衔接的完整逻辑,为鸿蒙应用开发者提供实践参考。
二、数据模型搭建:构建购物车基石
(一)商品类 ShopClass 定义
class ShopClass {
img: string = ''
name: string = ''
attributes: string = ''
price: number = 0
id: number = 0
quantity: number = 0
constructor(img: string, name: string, attributes: string, price: number, id: number, quantity: number) {
this.img = img
this.name = name
this.attributes = attributes
this.price = price
this.id = id
this.quantity = quantity
}
}
ShopClass 作为购物车商品的数据载体,清晰定义了商品图片路径、名称、属性描述、价格、唯一标识及数量等核心属性 。通过构造函数,能便捷地初始化商品信息,为后续购物车功能实现提供基础数据模型,让每个商品在购物车流程中都有明确的“身份”与“特征”。
(二)支付信息接口 pays 设计
interface pays{
pay: number
shopDel: Array<ShopClass>
}
pays 接口用于规范购物车结算时传递的支付相关数据结构,其中 pay 存储结算总金额,shopDel 存放已选中待支付的商品集合,确保购物车页面与支付页面间数据传递的准确性与规范性,是衔接购物车结算和支付流程的关键数据桥梁。
三、主页面 Index:购物车核心交互载体
(一)状态与数据管理
@Entry
@Component
struct Index {
@Provide ('shopArray') ShopArray: Array<ShopClass> = [
new ShopClass('app.media.shop_01', '华为家居小户科技布', '蓝色 尺寸L', 3600, 1, 1),
new ShopClass('app.media.shop_02', '简约现代餐桌椅组合', '白色 一套', 8999, 2, 1),
new ShopClass('app.media.shop_03', '智能无线吸尘器', '星空灰', 1499, 3, 1)
]
@State ShopDel: Array<ShopClass> = []
@State flag: boolean = false
@State priceSum: number = 0
@Provide('mainStarIndex') mainStarIndex: NavPathStack = new NavPathStack();
//...
}
@Provide与@State装饰器:@Provide('shopArray')共享商品列表数据,方便子组件访问;@State分别管理已选中商品集合ShopDel、全选状态flag以及总价priceSum,实现组件内部状态的响应式更新。NavPathStack:借助其实现页面导航功能,搭建购物车主页与支付页的跳转逻辑,让用户在购物流程中能顺畅切换页面。
(二)界面构建与交互逻辑
1. 标题栏设计
Row() {
Row({ space: 10 }) {
Image($r('app.media.shopcar'))
.width(50)
.height(50)
Text('购物车')
.fontSize(24)
.fontWeight(FontWeight.Bold)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
.padding({ top: 10, bottom: 15 })
}
通过 Row 组件搭建购物车标题栏,整合购物车图标与标题文本。设置布局属性,让标题栏在界面上美观且布局合理,为用户提供清晰的功能入口标识。
2. 商品列表展示
Scroll() {
Column() {
List() {
ForEach(this.ShopArray, (item: ShopClass, index) => {
ListItem() {
shops({
item: item,
ShopDel: this.ShopDel,
priceSum: this.priceSum,
ShopArray: this.ShopArray
})
}
.margin({ top: 10 })
})
}
.lanes(1, 10)
}
.height('100%')
.justifyContent(FlexAlign.Start)
.backgroundColor('#fff5f5f5')
}
.layoutWeight(1)
.width('100%')
.padding(10)
利用 Scroll、List、ForEach 等组件构建商品滚动列表。shops 子组件负责渲染单个商品条目,实现商品展示、选中状态切换及数量调整等交互功能,让用户能直观浏览和操作购物车中的商品。
3. 结算栏交互
Row({ space: 8 }) {
Checkbox()
.onChange((val) => {
this.flag = val
if (val) {
this.ShopArray.forEach((item) => {
if (!this.ShopDel.some(delItem => delItem.id === item.id)) {
this.ShopDel.push(item);
}
})
} else {
this.ShopDel = []
}
this.updateTotalPrice()
})
Text('全选')
.layoutWeight(1)
Text('合计:')
Text(`¥${this.priceSum}`)
Button('结算')
.backgroundColor(Color.Orange)
.onClick(() => {
const pay:pays = { pay: this.priceSum, shopDel: this.ShopDel }
this.mainStarIndex.pushPathByName('pay', pay)
})
}
.height(60)
.width('100%')
.padding(16)
- 全选功能:复选框
Checkbox结合onChange事件,实现全选或取消全选逻辑。全选时遍历商品列表,将未选中商品加入ShopDel;取消全选则清空ShopDel,并调用updateTotalPrice更新总价。 - 结算流程:结算按钮点击时,构建支付数据
pays,通过NavPathStack的pushPathByName方法跳转至支付页面,开启支付流程。
四、商品条目 shops 组件:精细化商品交互
@Reusable
@Component
struct shops {
@Prop item: ShopClass
@Link ShopDel: Array<ShopClass>
@Link priceSum: number
@Link ShopArray: Array<ShopClass>
@State checkboxif: boolean = false
build() {
Row({ space: 10 }) {
Checkbox()
.onChange((value) => {
if (value) {
if (!this.ShopDel.some(delItem => delItem.id === this.item.id)) {
this.checkboxif = true
this.ShopDel.push(this.item);
}
} else {
this.ShopDel = this.ShopDel.filter((delItem) => delItem.id !== this.item.id);
this.checkboxif = false
}
this.updateTotalPrice()
this.checkboxif = this.ShopDel.length === this.ShopArray.length
})
.width(20)
// 商品图片、信息列、数量控制区域等构建逻辑
}
.width('100%')
.height(80)
.backgroundColor(Color.White)
.borderRadius(8)
.shadow({ color: '#1de9b6', radius: 4 })
}
updateTotalPrice(): void {
let total = 0
this.ShopDel.forEach((shopItem) => {
total += shopItem.price * shopItem.quantity
})
this.priceSum = total
}
}
- 交互逻辑:作为商品列表子组件,通过
@Prop、@Link接收数据并实现双向绑定。复选框实现商品选中/取消选中功能,同步更新ShopDel、总价及全选状态;数量控制区域的 “-”“+” 按钮,调整商品数量并实时更新总价,让用户能精细化操作单个商品。 - 界面美化:设置组件背景色、圆角及阴影效果,提升商品条目的视觉呈现,优化用户体验。
五、支付页面 pay 组件:完成购物闭环
@Component
struct pay {
@Consume('mainStarIndex') mainStarIndex: NavPathStack;
@Consume ('shopArray') shopArray: Array<ShopClass>
@Prop pay: pays
build() {
NavDestination(){
Column() {
// 金额展示、支付方式选择、按钮等构建逻辑
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Start)
.alignItems(HorizontalAlign.Center)
}
}
onClickPay() {
AlertDialog.show({
message: '是否支付!',
buttons: [
{
value: '确定',
action: () => {
this.shopArray = this.shopArray.filter((item) => !this.pay.shopDel.some(delItem => delItem.id === item.id))
this.mainStarIndex.pop();
prompt.showToast({ message: '支付成功!' });
}
},
{
value: '取消',
action: () => {
}
}
]
});
}
}
- 支付流程:接收支付数据与商品列表,展示支付金额及支付宝、微信等支付方式选项。确认支付按钮点击后,弹出对话框询问用户,确认则从商品列表移除已结算商品,通过
mainStarIndex.pop()返回购物车主页并提示支付成功;取消则终止支付流程,完成购物闭环的最后一环。 - 用户反馈:利用
prompt.showToast提供支付结果反馈,增强用户对操作的感知。
六、附:代码
import { prompt } from '@kit.ArkUI'
// 商品类定义
class ShopClass {
img: string = ''
name: string = ''
attributes: string = ''
price: number = 0
id: number = 0
quantity: number = 0
constructor(img: string, name: string, attributes: string, price: number, id: number, quantity: number) {
this.img = img
this.name = name
this.attributes = attributes
this.price = price
this.id = id
this.quantity = quantity
}
}
interface pays{
pay: number
shopDel: Array<ShopClass>
}
@Entry
@Component
struct Index {
// 购物车商品列表数据源
@Provide ('shopArray') ShopArray: Array<ShopClass> = [
new ShopClass('app.media.shop_01', '华为家居小户科技布', '蓝色 尺寸L', 3600, 1, 1),
new ShopClass('app.media.shop_02', '简约现代餐桌椅组合', '白色 一套', 8999, 2, 1),
new ShopClass('app.media.shop_03', '智能无线吸尘器', '星空灰', 1499, 3, 1)
]
// 已选中商品集合
@State ShopDel: Array<ShopClass> = []
// 全选状态标志
@State flag: boolean = false
// 总价
@State priceSum: number = 0
@Provide('mainStarIndex') mainStarIndex: NavPathStack = new NavPathStack();
@Builder
shopPage(name: string, params: pays) {
if (name === 'pay') {
pay({
pay: params,
})
}
}
build() {
Navigation(this.mainStarIndex){
Column() {
// 购物车标题栏
Row() {
Row({ space: 10 }) {
Image($r('app.media.shopcar')) // 使用合适的购物车图标资源
.width(50)
.height(50)
Text('购物车')
.fontSize(24)
.fontWeight(FontWeight.Bold)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
.padding({ top: 10, bottom: 15 })
}
Divider()
// 商品滚动列表
Scroll() {
Column() {
// 商品列表容器
List() {
ForEach(this.ShopArray, (item: ShopClass, index) => {
ListItem() {
shops({
item: item,
ShopDel: this.ShopDel,
priceSum: this.priceSum,
ShopArray: this.ShopArray
})
}
.margin({ top: 10 })
})
}
.lanes(1, 10)
}
.height('100%')
.justifyContent(FlexAlign.Start)
.backgroundColor('#fff5f5f5')
}
.layoutWeight(1)
.width('100%')
.padding(10)
Divider()
// 结算栏
Row({ space: 8 }) {
Checkbox()
.onChange((val) => {
// 全选/取消全选逻辑
this.flag = val
if (val) {
this.ShopArray.forEach((item) => {
if (!this.ShopDel.some(delItem => delItem.id === item.id)) {
this.ShopDel.push(item);
}
})
} else {
// 取消全选时清空ShopDel
this.ShopDel = []
}
// 更新总价
this.updateTotalPrice()
})
Text('全选')
.layoutWeight(1)
// 计算总价
Text('合计:')
Text(`¥${this.priceSum}`)
Button('结算')
.backgroundColor(Color.Orange)
.onClick(() => {
// 模拟结算操作
const pay:pays = { pay: this.priceSum, shopDel: this.ShopDel }
this.mainStarIndex.pushPathByName('pay', pay)
})
}
.height(60)
.width('100%')
.padding(16)
}
.width('100%')
.height('100%')
}
.hideTitleBar(true)
.mode(NavigationMode.Stack)
.navDestination(this.shopPage)
}
// 单独提取更新总价的函数,便于维护和调用
updateTotalPrice(): void {
this.priceSum = this.ShopDel.reduce((sum, item) => sum + item.price * item.quantity, 0)
}
}
// 商品项构建器
@Reusable
@Component
struct shops {
@Prop item: ShopClass
@Link ShopDel: Array<ShopClass>
@Link priceSum: number
@Link ShopArray: Array<ShopClass>
@State checkboxif: boolean = false // shop状态
build() {
Row({ space: 10 }) {
// 复选框 - 用于选择商品
Checkbox()
.onChange((value) => {
if (value) {
// 如果 value 为 true,将 item 添加到 this.ShopDel 中
if (!this.ShopDel.some(delItem => delItem.id === this.item.id)) {
this.checkboxif = true
this.ShopDel.push(this.item);
}
} else {
// 如果 value 为 false,从 this.ShopDel 中移除当前的 item
this.ShopDel = this.ShopDel.filter((delItem) => delItem.id !== this.item.id);
this.checkboxif = false
}
// 更新总价
this.updateTotalPrice()
// 更新全选状态
this.checkboxif = this.ShopDel.length === this.ShopArray.length
})
.width(20)
// 商品图片
Image($r(this.item.img))
.width(80)
// 商品信息列
Column() {
// 商品名称
Text(this.item.name)
.width('100%')
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
// 商品属性
Text(this.item.attributes)
.width('100%')
// 商品价格
Text(`¥${this.item.price}`)
.width('100%')
.fontColor('#ff527fb') // 修正颜色值格式
}
.width(100)
// 数量控制区域
Column({ space: 5 }) {
Text('数量:')
.fontSize(12)
.textAlign(TextAlign.Center)
Row({ space: 5 }) {
Text('-')
.width(25)
.height(25)
.onClick(() => {
if (this.item.quantity > 1) {
this.item.quantity--
this.updateTotalPrice()
}
})
Text(`${this.item.quantity}`)
.width(25)
.textAlign(TextAlign.Center)
Text('+')
.width(25)
.height(25)
.onClick(() => {
this.item.quantity++
this.updateTotalPrice()
})
}
.justifyContent(FlexAlign.SpaceAround)
}
.width(80)
// 详情箭头图标
Image($r('app.media.black'))
.width(10)
.onClick(() => {
// TODO 模拟跳转详情页
})
}
.width('100%')
.height(80)
.backgroundColor(Color.White)
.borderRadius(8) // 添加圆角效果
.shadow({ color: '#1de9b6', radius: 4 }) // 添加阴影效果
}
// 提取公共方法来更新总价
updateTotalPrice(): void {
let total = 0
this.ShopDel.forEach((shopItem) => {
total += shopItem.price * shopItem.quantity
})
this.priceSum = total
}
}
@Component
struct pay {
@Consume('mainStarIndex') mainStarIndex: NavPathStack;
@Consume ('shopArray') shopArray: Array<ShopClass>
@Prop pay: pays
build() {
NavDestination(){
Column() {
Row(){
Text('合计:')
.fontSize(30)
.fontWeight(FontWeight.Bold)
Text(`¥${this.pay.pay}`)
.fontSize(30)
.fontWeight(FontWeight.Bold)
}
// 支付方式标题
Text('选择支付方式')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.padding({ top: 20, bottom: 10 })
Divider()
// 支付宝支付选项
Row({ space: 15 }) {
Image($r('app.media.alipay')) // 假设已有支付宝图标资源
.width(30)
.height(30)
Text('支付宝')
.fontSize(16)
Checkbox()
.onChange((value) => {
if (value) {
// 可以添加相关逻辑,如记录用户选择了支付宝支付
console.log("用户选择了支付宝支付")
}
})
}
.width('90%')
.padding({ left: 20, right: 20 })
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
.margin({ top: 10 })
Divider()
// 微信支付选项
Row({ space: 15 }) {
Image($r('app.media.wechatpay')) // 假设已有微信支付图标资源
.width(30)
.height(30)
Text('微信')
.fontSize(16)
Checkbox()
.onChange((value) => {
if (value) {
// 可以添加相关逻辑,如记录用户选择了微信支付
console.log("用户选择了微信支付")
}
})
}
.width('90%')
.padding({ left: 20, right: 20 })
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
.margin({ top: 10 })
Divider()
// 提交支付按钮
Button('确认支付')
.onClick(() => {
// 跳转·到支付页面
this.onClickPay()
})
.backgroundColor(Color.Orange)
.width('80%')
.margin({ top: 30 })
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Start)
.alignItems(HorizontalAlign.Center)
}
}
// 支付点击事件处理函数
onClickPay() {
// 模拟支付成功提示
AlertDialog.show({
message: '是否支付!',
buttons: [
{
value: '确定',
action: () => {
// 移除已选商品并返回首页
// this.pay.ShopDel = this.ShopDel.filter((delItem) => delItem.id !== item.id);
this.shopArray = this.shopArray.filter((item) => !this.pay.shopDel.some(delItem => delItem.id === item.id));
this.mainStarIndex.pop(); // 返回首页
// 提示支付成功
prompt.showToast({ message: '支付成功!' });
}
},
{
value: '取消',
action: () => {
// 取消支付操作
}
}
]
});
}
}