鸿蒙应用开发之购物车的数据操作
鸿蒙应用之仿黑马智慧商城购物车数据操作超详细教程
基于上期购物车基本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)
}
}
更多推荐
所有评论(0)