鸿蒙应用开发之购物车的数据操作

基于上期购物车基本UI渲染后的数据操作


前言

本期实现对购物车的商品的数量加减,更新购物车,删除商品至购物车以及父子组件的数据双向同步。(删除购物车貌似接口有问题,正常调用,返回success和200,刷新购物车之后并没有实现删除)。

 需要用到黑马智慧商城接口。地址:wiki - 智慧商城-实战项目 (apifox.com)


一、商品数量更新

通过点击商品列表中的加或减按钮,实现对商品数量的加减操作。

上期讲过,使用@ObjectLink修饰的变量,他的值更改,能被检测到,并实时刷新UI。

那么我们只需要通过父组件传的值,对数量的加减操作就可以自动实现价格的变化了。

注意:如果商品的数量小于1或者大于库存量的话,则不能继续的加减。

代码:

//减
        Text('-')
            .width('15%').border({width:1,color:Color.Orange}).textAlign(TextAlign.Center)
            .fontSize(30).fontColor(Color.Black).fontWeight(800)
            .onClick(()=>{
              if(this.arr.number-1 < 1){
                promptAction.showToast({message:'商品数量不能小于1哦~'})
              }else {
                this.arr.number--
              }
            })
//加
        Text('+')
            .width('15%').border({width:1,color:Color.Orange}).textAlign(TextAlign.Center)
            .fontSize(30).fontColor(Color.Black).fontWeight(800)
            .onClick(()=>{
              if(this.arr.number+1 > this.arr.stock){
                promptAction.showToast({message:'购买的商品数量大于库存了哦~'})
              }else {
                this.arr.number++
                this.updateCartData(this.arr.id,this.arr.number)
              }
            })

二、商品购物车更新

当我们对商品的数量更新之后,应该要同步发送数据给后台,否则下次刷新还是以前的数据。

在子组件cartItem中,arr数组包含了商品的id, 数量等。所以直接发送请求。

代码:

//更新购物车数量
 async updateCartData(gid,gnum){
    const data = await axios.post('https://smart-shop.itheima.net/index.php?s=/api/cart/update',{
      goodsId:gid,
      goodsNum:gnum,
      goodsSkuId:"0"//默认0
    },{
      headers:{
        ['Access-Token']:this.token,
        platform:'H5'
      }})
    promptAction.showToast({message:data.data.message})
  }

注意,子组件这里的token是根据父组件传递过来的。


三 、刷新购物车界面

当我们实现了第二步的操作之后,只是给后台发送了数据,但是我们UI并没有变化。

所以需要对UI进行刷新,我是通过将渲染UI的原数组清空,然后再重新赋值。

那么在子组件点击加减按钮时,需要对父组件的数组清空,并且重新赋值给父组件的数组,这里就需要进行父子双向数据同步。

需要在子组件用到@Link修饰器,那么父组件传值则需要使用$符传值。

详情请见官网@Link装饰器:父子双向同步 (openharmony.cn)

父组件向子组件传递数组代码:

//给使用@Link修饰器的变量传值时,不需要用this,而是$。
//list:UI渲染数组,index:循环中的位置索引,token:Token值。
cartItem({list:$cartDataarr:item,index:index,token:this.token})

子组件代码:

 //商品数据
  @Link list:cartInfo[]

 那么我们在第二步之后,将数组清空,重新获取购物车数据即可。

代码:

 //更新购物车数量
 async updateCartData(gid,gnum){
    const data = await axios.post('https://smart-shop.itheima.net/index.php?s=/api/cart/update',{
      goodsId:gid,
      goodsNum:gnum,
      goodsSkuId:"0"//默认0
    },{
      headers:{
        ['Access-Token']:this.token,
        platform:'H5'
      }})
    promptAction.showToast({message:data.data.message})
    //将数组清空
   this.list=[]
    //重新赋值
   this.getCartData()
  }


//获取购物车数据
  async getCartData(){
    const data = await axios.get('https://smart-shop.itheima.net/index.php?s=/api/cart/list',{
      headers:{
        ['Access-Token']:this.token,
        platform:'H5'
      }})
    const len = data.data.data.list.length
    const con = data.data.data.list
    for (let i = 0; i < len; i++) {
      var item = new cartInfo(true,con[i].goods_id,con[i].goods.goods_images[0].preview_url,con[i].goods.goods_name,con[i].goods.goods_price_min,con[i].goods_num,con[i].goods.stock_total)
      this.list.push(item)
    }
  }

四 、删除购物车

这里删除购物车有两种方式:

1.左滑单个商品,显示删除按钮,可以进行删除。

2.点击菜单编辑按钮,出现单选框,可以进行删除。

1.左滑删除

利用ListItem 自带的属性,左滑展示删除按钮。点击按钮即可实现删除商品。

删除商品之后也要更新购物车,即第三步方法。

注意:需要传递商品id。

给后台发请求代码:

//删除购物车
  async deleteCart(id){
    const data = await axios.post('https://smart-shop.itheima.net/index.php?s=/api/cart/clear',{
      cartIds:[id]
    },{
      headers:{
      ['Access-Token']:this.token,
      platform:'H5'
    }})
    promptAction.showToast({message:data.data.message})
  }

点击事件进行了场景优化:作为平台方,当然不希望用户从购物车删除商品。

所以增加一个确认取消的提示框。

点击删除按钮的代码:

//err:点击非提示框区域时的事件,res.index:两个按钮的索引,0:取消,1:确认
    Button('删除')
        .width('20%').type(ButtonType.Circle).backgroundColor(Color.Red)
        .onClick(()=>{
          promptAction.showDialog({message:'确定要删除该商品吗',
            buttons:[{text:'取消' ,color: '#000000'},{text:'确认' ,color: '#000000'}]},(err,res)=>{
            if(err){
              promptAction.showToast({message:'用户取消操作'})
            }else{
              if(res.index == 0){
                return
              }else{
                this.deleteCart(id)
                this.cartData = []
                this.getCartData()
              }
            }
          })
        })

 2.编辑删除

当我们点击编辑按钮时,在每个商品列表的前面展示一个单选框,用户点击选择,就可以删除指定商品。我们可以用一个状态变量来是否展示编辑时的按钮。

  //是否删除状态
  @State isDelete:boolean=true

当点击编辑按钮的时候,其本身内容也需要变化。

//编辑按钮组件
    Text(this.isDelete ? '编辑' : '返回')
          .fontSize(25).fontWeight(800)
          .onClick(()=>{
            this.isDelete = !this.isDelete
          })

 同时底部结算栏需要展示按钮(删除)。

if(this.isDelete){
        //底部
        Row(){
          Text('合计:').fontSize(20).margin({left:10})
          Text(this.cartData.reduce((acc,obj)=>acc+obj.number*obj.price,0).toString())
            .width('40%').fontSize(20).fontColor(Color.Red)
          Button('结算')
            .width('35%').backgroundColor(Color.Red).margin({left:10})
            .onClick(()=>{
            })
        }.width('100%').padding('2%').layoutWeight(1)
      }else {
        //底部
        Row(){
          Button('删除商品')
            .width('98%').backgroundColor(Color.Red).margin({left:10})
            .onClick(()=>{
            })
        }.width('100%').padding('2%').layoutWeight(1).justifyContent(FlexAlign.Center)
      }

子组件cartItem也需要根据状态是否展示单选框。

//正常情况,不显示单选框,默认一个空白占位
      if(this.show){
        Text()
          .width('5%')
      }else {
        //删除情况:则展示单选框
        Radio({ value: 'Radio1', group: 'radioGroup' })
          .width('5%')
          .checked(this.arr.flag)
          .onChange((value: boolean) => {
              this.deleteIndex = this.index
          })
      }

同时还需要获取用户点击的是哪个单选框,则需要获取索引位置。

//子组件:
  @Link deleteIndex:number//要删除额索引
//父组件:
  @State Index:number = 0//删除的索引
//父组件传值:
 cartItem({show:$isDelete,list:$cartData,deleteIndex:$Index,arr:item,index:index,token:this.token})

当用户点击了其中一个单选框的时候,父组件获得了其索引。

那么用户再点击删除按钮的时候,就可以进行对商品删除,删除完之后,需要复原状态。

代码:

     Button('删除商品')
            .width('98%').backgroundColor(Color.Red).margin({left:10})
            .onClick(()=>{ 
              //传递商品数组索引中的id值
              this.deleteCart(this.cartData[this.Index].id)
              this.cartData = []
              this.getCartData()
              //复原状态
              this.isDelete = !this.isDelete
            })

总结

完整代码如下:大家可以自行优化。

父组件(cart):

import axios from '@ohos/axios'
import preferences from '@ohos.data.preferences'
import { topContent } from '../components/topContent'
import common from '@ohos.app.ability.common'
import { cartInfo } from '../class/cartInfo'
import promptAction from '@ohos.promptAction'
import { cartItem } from '../components/cartItem'
@Entry
@Component
struct Cart{

  //上下文
  context:common.UIAbilityContext
  //token
  @State token:string = ''
  //获取商品购物车信息
  @State cartData:cartInfo[]=[]
  //是否删除状态
  @State isDelete:boolean=true
  //删除的索引
  @State Index:number = 0

  async aboutToAppear(){
    const a = await preferences.getPreferences(this.context,"tokensss")
    const b = await a.get("tokensss","")
    this.token = b.toString()//将首选项中的Token值赋给变量
    this.getCartData()
  }

  //获取购物车列表数据
  async getCartData(){
    const data = await axios.get('https://smart-shop.itheima.net/index.php?s=/api/cart/list',{
      //头部参数
      headers:{
        ['Access-Token']:this.token,
        platform:'H5'
      }})
    //定义数据的长度
    const len = data.data.data.list.length
    //简化
    const con = data.data.data.list
    //循环并赋值,将数据存储到数组中,cartInfo为一个类。
    for (let i = 0; i < len; i++) {
      var item = new cartInfo(false,con[i].goods_id,con[i].goods.goods_images[0].preview_url,con[i].goods.goods_name,con[i].goods.goods_price_min,con[i].goods_num,con[i].goods.stock_total)
      this.cartData.push(item)
    }
  }

  //删除购物车
  async deleteCart(id){
    const data = await axios.post('https://smart-shop.itheima.net/index.php?s=/api/cart/clear',{
      cartIds:[id]
    },{
      headers:{
      ['Access-Token']:this.token,
      platform:'H5'
    }})
    promptAction.showToast({message:data.data.message})
  }

  build(){
    Column(){
      //头部
      Row(){
        topContent({title:'购物车'})
      }.width('100%').padding('2%').layoutWeight(1)
      //菜单
      Row(){
        Text(){
          Span('共')
          Span(this.cartData.length.toString()).fontSize(25).fontColor(Color.Red)
          Span('种商品')
        }.fontSize(25)
        Text(this.isDelete ? '编辑' : '返回')
          .fontSize(25).fontWeight(800)
          .onClick(()=>{
            this.isDelete = !this.isDelete
          })
      }.width('100%').padding('2%').justifyContent(FlexAlign.SpaceBetween).layoutWeight(1)
      //商品列表
        List({space:5}){
          ForEach(this.cartData,(item:cartInfo,index:number)=>{
            ListItem(){
              //封装的商品子组件
              cartItem({show:$isDelete,list:$cartData,deleteIndex:$Index,arr:item,index:index,token:this.token})
            }.swipeAction({end:this.deleteCartShop(item.id)})
          })
        }.width('100%').layoutWeight(9).padding('2%')

      if(this.isDelete){
        //底部
        Row(){
          Text('合计:').fontSize(20).margin({left:10})
          Text(this.cartData.reduce((acc,obj)=>acc+obj.number*obj.price,0).toString())
            .width('40%').fontSize(20).fontColor(Color.Red)
          Button('结算')
            .width('35%').backgroundColor(Color.Red).margin({left:10})
            .onClick(()=>{
            })
        }.width('100%').padding('2%').layoutWeight(1)
      }else {
        //底部
        Row(){
          Button('删除商品')
            .width('98%').backgroundColor(Color.Red).margin({left:10})
            .onClick(()=>{
              this.deleteCart(this.cartData[this.Index].id)
              this.cartData = []
              this.getCartData()
              this.isDelete = !this.isDelete
            })
        }.width('100%').padding('2%').layoutWeight(1).justifyContent(FlexAlign.Center)
      }

    }
    .width('100%')
    .height('100%')
  }


  @Builder deleteCartShop(id:string){
    Row(){
      Button('删除')
        .width('20%').type(ButtonType.Circle).backgroundColor(Color.Red)
        .onClick(()=>{
          promptAction.showDialog({message:'确定要删除该商品吗',
            buttons:[{text:'取消' ,color: '#000000'},{text:'确认' ,color: '#000000'}]},(err,res)=>{
            if(err){
              promptAction.showToast({message:'用户取消操作'})
            }else{
              if(res.index == 0){
                return
              }else{
                this.deleteCart(id)
                this.cartData = []
                this.getCartData()
              }
            }
          })
        })
    }
  }

}

子组件(cartItem):

import promptAction from '@ohos.promptAction'
import axios from '@ohos/axios'
import { cartInfo } from '../class/cartInfo'
@Component
export struct cartItem{

  //是否展示
  @Link show:boolean
  //商品数据
  @Link list:cartInfo[]
  //要删除额索引
  @Link deleteIndex:number
  //foreach渲染过来的数据
  @ObjectLink arr:cartInfo
  //在list中的索引
  index:number = 0
  //token值
  token:string=''

//更新购物车数量
 async updateCartData(gid,gnum){
    const data = await axios.post('https://smart-shop.itheima.net/index.php?s=/api/cart/update',{
      goodsId:gid,
      goodsNum:gnum,
      goodsSkuId:"0"//默认0
    },{
      headers:{
        ['Access-Token']:this.token,
        platform:'H5'
      }})
    promptAction.showToast({message:data.data.message})
   this.list=[]
   this.getCartData()
  }

  //获取购物车数据
  async getCartData(){
    const data = await axios.get('https://smart-shop.itheima.net/index.php?s=/api/cart/list',{
      headers:{
        ['Access-Token']:this.token,
        platform:'H5'
      }})
    const len = data.data.data.list.length
    const con = data.data.data.list
    for (let i = 0; i < len; i++) {
      var item = new cartInfo(true,con[i].goods_id,con[i].goods.goods_images[0].preview_url,con[i].goods.goods_name,con[i].goods.goods_price_min,con[i].goods_num,con[i].goods.stock_total)
      this.list.push(item)
    }
  }

  build(){

    Row({space:5}){
      //正常情况,不显示单选框,默认一个空白占位
      if(this.show){
        Text()
          .width('5%')
      }else {
        //删除情况:则展示单选框
        Radio({ value: 'Radio1', group: 'radioGroup' })
          .width('5%')
          .checked(this.arr.flag)
          .onChange((value: boolean) => {
              this.deleteIndex = this.index
          })
      }
      //商品图片
      Image(this.arr.url)
        .width('30%').objectFit(ImageFit.Fill)
      Column({space:10}){
        //商品名称
        Text(this.arr.name).fontSize(20).fontWeight(700)
          .width('85%').maxLines(3).textOverflow({overflow:TextOverflow.Ellipsis})

        Row({space:10}){
          //商品的总价
          Text((this.arr.price*this.arr.number).toString())
            .width('30%')
            .fontSize(22).fontColor(Color.Red)
          //占位符
          Blank()
          Text('-')
            .width('15%').border({width:1,color:Color.Orange}).textAlign(TextAlign.Center)
            .fontSize(30).fontColor(Color.Black).fontWeight(800)
            .onClick(()=>{
              if(this.arr.number-1 < 1){
                promptAction.showToast({message:'商品数量不能小于1哦~'})
              }else {
                this.arr.number--
                this.updateCartData(this.arr.id,this.arr.number)
              }
            })
          Text(this.arr.number.toString())
            .fontSize(25).fontWeight(FontWeight.Bold)
          Text('+')
            .width('15%').border({width:1,color:Color.Orange}).textAlign(TextAlign.Center)
            .fontSize(30).fontColor(Color.Black).fontWeight(800)
            .onClick(()=>{
              if(this.arr.number+1 > this.arr.stock){
                promptAction.showToast({message:'购买的商品数量大于库存了哦~'})
              }else {
                this.arr.number++
                this.updateCartData(this.arr.id,this.arr.number)
              }
            })
        }
      }.width('65%').height('100%').alignItems(HorizontalAlign.Start).justifyContent(FlexAlign.SpaceEvenly)
    }.width('100%').height(180).border({width:2,color:"#ff480047"}).borderRadius(20)
  }

}
  • 23
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值