需要之前已经登录账户并成功添加商品至购物车。
没有的可以去黑马官方那登录并添加商品至购物车。
前言
简化了黑马全选与单选商品的逻辑(其实就是我不会-_-)
需要用到黑马智慧商城接口。地址:wiki - 智慧商城-实战项目 (apifox.com)
一、分析布局
如图:总体布局为纵向布局。
1.顶部
之前我们封装过一个组件,上期有讲,现在我们复用一次就好了。
代码:
//头部
Row(){
topContent({title:'购物车'})
}.width('100%').padding('2%').layoutWeight(1)//设置row在整个column中的高度比
2.菜单
在一个row中左右对齐(需要设置row的宽度)。
代码:
//菜单
Row(){
Text(){
Span('共')
Span(this.cartData.length.toString()).fontSize(25).fontColor(Color.Red)
Span('种商品')
}.fontSize(25)
Text(this.isDelete ? '编辑' : '返回')
.fontSize(25).fontWeight(800)
.onClick(()=>{
})
}.width('100%').padding('2%').justifyContent(FlexAlign.SpaceBetween).layoutWeight(1)
3.商品列表
将商品列表封装成一个子组件(必须是一个组件),通过foreach循环渲染。
注意:商品列表可能有许多个,会超出屏幕的高度,所以我们使用list组件。给list组件设定一个高度,那样就可以实现滑动并不会超出屏幕范围啦。
如图2效果,向左滑动即可展示删除按钮。
代码:
//商品列表
List({space:5}){
ForEach(this.cartData,(item:cartInfo,index:number)=>{
ListItem(){
//封装的商品子组件
cartItem()
}.swipeAction({end:this.deleteCartShop(item.id)})//向左滑动展示的内容
})
}.width('100%').layoutWeight(9).padding('2%')
cartItem组件代码:
//子组件cartItem的UI代码
Row({space:5}){
//正常情况,不显示单选框,默认一个空白占位
if(this.show){
Text()
.width('5%')
}else {
//删除情况:则展示单选框
Radio({ value: 'Radio1', group: 'radioGroup' })
.width('5%')
.checked(this.arr.flag)
.onChange((value: boolean) => {
})
}
//商品图片
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(()=>{
})
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(()=>{
})
}
}.width('65%').height('100%').alignItems(HorizontalAlign.Start).justifyContent(FlexAlign.SpaceEvenly)
}.width('100%').height(180).border({width:2,color:"#ff480047"}).borderRadius(20)
}
4.底部
底部内容固定在屏幕的底下,展示用户所有的商品总价(省去了全选框)和购买按钮。
另外,当我们点击菜单栏中的编辑时,底部展示的是删除商品的按钮,如上图3。
代码:
//根据状态判断展示哪个内容
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)
}
二、获取数据
1.获取首选项的Token
上期我们详讲了,如何获取Token并存储,这里不展示了。
代码:
const a = await preferences.getPreferences(this.context,"tokensss")
const b = await a.get("tokensss","")
this.token = b.toString()//将首选项中的Token值赋给变量
2.获取购物车数据
该接口为get请求,需要的参数为Token。
代码:
//获取购物车列表数据
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)
}
}
3.自定义一个类
注意:这里的this.cartData是一个自定义的数组,因为涉及到数组里面包函对象的格式,当我们更改数组中某个对象的属性时,是框架检测不到的,即不能同步刷新UI界面,所以我们必须要用@Observed和@ObjectLink来实现对数据的更改时能同步刷新UI。
关于@Observed和@ObjectLink的知识,详情请参考官网:
@Observed装饰器和 @ObjectLink装饰器:嵌套类对象属性变化 (openharmony.cn)
cartInfo类的代码:
@Observed//监视:使这个类中的数据变化可以被检测到从而刷新UI
export class cartInfo{
flag:boolean//是否选中
id:string//商品id
url:string//图片链接
name:string//商品名字
price:number//商品价格
number:number//商品数量
stock:number//商品库存
constructor(flag:boolean,id:string,url:string,name:string,price:number,number:number,stock:number) {
this.flag = flag
this.id = id
this.url = url
this.name = name
this.price = price
this.number = number
this.stock = stock
}
}
4.渲染界面
通过获取到的数据,使用foreach渲染,并传递给子组件:
ForEach(this.cartData,(item:cartInfo,index:number)=>{
ListItem(){
//封装的商品子组件
cartItem({arr:item})
}.swipeAction({end:this.deleteCartShop(item.id)})
})
子组件定义变量来接收:
//foreach渲染过来的数据
@ObjectLink arr:cartInfo
5.展示总价
展示总价:循环数组中的每一项商品的数量乘价格即可。
代码:
Text(this.cartData.reduce((acc,obj)=>acc+obj.number*obj.price,0).toString())
总结
本期内容主要是基本UI渲染,以及商品数据的渲染。
下一期我们学习对数据的操作:加减商品数量,更新购物车,删除购物车,父子数据双向同步。