HarmonyOS5 购物商城app(二):购物车与支付(附代码)

2025-06-28 20:20:07
109次阅读
0个评论

screenshots.gif

鸿蒙电商应用购物车模块开发指南

一、核心数据模型设计

1. 商品数据模型

class ShopClass {
  img: string;       // 商品图片路径
  name: string;      // 商品名称
  attributes: string;// 商品属性
  price: number;     // 商品单价
  id: number;        // 商品唯一ID
  quantity: number;  // 购买数量

  constructor(img: string, name: string, attributes: string, 
              price: number, id: number, quantity: number) {
    // 初始化逻辑
  }
}

2. 支付数据接口

interface pays {
  pay: number;              // 支付总金额
  shopDel: Array<ShopClass> // 待支付商品列表
}

二、购物车主页面架构

状态管理

@Entry
@Component
struct Index {
  @Provide('shopArray') ShopArray: ShopClass[] = [ // 商品数据源
    new ShopClass('app.media.shop_01', '华为家居', '蓝色 尺寸L', 3600, 1, 1),
    // 其他商品...
  ];
  
  @State ShopDel: ShopClass[] = []; // 已选商品
  @State priceSum: number = 0;      // 总金额
  @State flag: boolean = false;     // 全选状态
  
  @Provide('mainStarIndex') mainStarIndex: NavPathStack = new NavPathStack();
}

页面布局结构

build() {
  Navigation(this.mainStarIndex) {
    Column() {
      // 1. 标题栏
      this.buildTitleBar()
      
      // 2. 商品列表
      this.buildProductList()
      
      // 3. 结算栏
      this.buildCheckoutBar()
    }
  }
}

三、关键功能实现

1. 商品列表实现

@Builder
buildProductList() {
  Scroll() {
    List() {
      ForEach(this.ShopArray, (item: ShopClass) => {
        ListItem() {
          ShopItem({
            item: item,
            ShopDel: $ShopDel,
            priceSum: $priceSum,
            ShopArray: $ShopArray
          })
        }
      }
    }
  }
}

2. 商品项组件

@Reusable
@Component
struct ShopItem {
  @Prop item: ShopClass
  @Link ShopDel: ShopClass[]
  @Link priceSum: number
  @Link ShopArray: ShopClass[]
  
  build() {
    Row() {
      // 1. 选择复选框
      Checkbox().onChange(() => this.toggleSelect())
      
      // 2. 商品图片
      Image($r(this.item.img)).width(80)
      
      // 3. 商品信息
      Column() {
        Text(this.item.name)
        Text(this.item.attributes)
        Text(`¥${this.item.price}`)
      }
      
      // 4. 数量控制器
      QuantityController({
        quantity: $item.quantity,
        onChange: (val) => this.updateQuantity(val)
      })
    }
  }
  
  // 选择状态切换
  private toggleSelect() {
    // 实现选择逻辑
  }
  
  // 更新数量
  private updateQuantity(val: number) {
    // 实现数量更新逻辑
  }
}

3. 结算功能实现

@Builder
buildCheckoutBar() {
  Row() {
    // 全选复选框
    Checkbox()
      .onChange((val) => this.toggleAll(val))
    
    Text('全选')
    
    // 金额汇总
    Text(`合计:¥${this.priceSum}`)
    
    // 结算按钮
    Button('结算')
      .onClick(() => this.checkout())
  }
}

// 全选/取消全选
private toggleAll(checked: boolean) {
  this.ShopDel = checked ? [...this.ShopArray] : [];
  this.updateTotalPrice();
}

// 结算跳转
private checkout() {
  const paymentData: pays = {
    pay: this.priceSum,
    shopDel: this.ShopDel
  };
  this.mainStarIndex.pushPathByName('pay', paymentData);
}

四、支付页面实现

页面结构

@Component
struct PayPage {
  @Consume('mainStarIndex') mainStarIndex: NavPathStack;
  @Consume('shopArray') shopArray: ShopClass[];
  @Prop pay: pays;
  
  build() {
    NavDestination() {
      Column() {
        // 1. 金额展示
        this.buildAmountDisplay()
        
        // 2. 支付方式选择
        this.buildPaymentMethods()
        
        // 3. 支付按钮
        this.buildPayButton()
      }
    }
  }
}

支付功能实现

// 支付确认
private confirmPayment() {
  AlertDialog.show({
    message: '确认支付?',
    buttons: [
      {
        value: '确定',
        action: () => this.processPayment()
      },
      { value: '取消' }
    ]
  });
}

// 处理支付
private processPayment() {
  // 1. 更新商品列表
  this.shopArray = this.shopArray.filter(
    item => !this.pay.shopDel.some(p => p.id === item.id)
  
  // 2. 返回购物车
  this.mainStarIndex.pop();
  
  // 3. 显示支付结果
  prompt.showToast({ message: '支付成功!' });
}

五、关键交互逻辑

1. 金额计算

private updateTotalPrice() {
  this.priceSum = this.ShopDel.reduce(
    (sum, item) => sum + item.price * item.quantity, 0);
}

2. 选择状态同步

// 当商品项选择状态变化时
private syncCheckAllStatus() {
  this.flag = this.ShopDel.length === this.ShopArray.length;
}

六、UI优化技巧

1. 商品项样式优化

.width('100%')
.height(80)
.backgroundColor(Color.White)
.borderRadius(8)
.shadow({ color: '#1de9b6', radius: 4 })

2. 交互动效

// 按钮点击效果
.hoverEffect(HoverEffect.Scale)

// 图片加载过渡
.transition({ type: TransitionType.All, opacity: 0.8 })

七、完整代码结构

点击查看完整实现
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: () => {
            // 取消支付操作
          }
        }
      ]
    });
  }
}

八、设计要点总结

  1. 分层架构:清晰的数据层、UI层、业务逻辑层分离
  2. 状态管理:合理使用@Provide/@Consume和@State管理应用状态
  3. 组件化:通过@Builder和@Reusable构建可复用UI组件
  4. 交互优化:完善的用户操作反馈机制
  5. 导航流畅:NavPathStack实现页面无缝跳转
收藏00

登录 后评论。没有帐号? 注册 一个。