文章目录
一、购物车逻辑
1.1 购物车及加减菜
在utils
目录下新建CartStore.ets
文件,如下:
ts
import { FoodItem } from '../models'
// 本地持久化购物车数据
PersistentStorage.persistProp<FoodItem[]>('cartStore', [])
export class CartStore {
static getCarts() {
return AppStorage.get<FoodItem[]>('cartStore') || [] as FoodItem[]
}
/**
* 加菜or减菜
* @param foodItem
* @param type
*/
static addOrCutFood(foodItem: FoodItem, type: 'add' | 'cut') {
const cartList = CartStore.getCarts()
const item = cartList.find((item) => item.id === foodItem.id)
// 加菜
if (type === 'add') {
if (item) {
item.count++
} else {
foodItem.count = 1
cartList.unshift(foodItem)
}
} else { // 减菜
if (item && item.count > 0) {
item.count--
if (item.count === 0) {
const index = cartList.findIndex((item) => item.id === foodItem.id)
cartList.splice(index, 1)
}
}
}
AppStorage.set<FoodItem[]>('cartStore', [...cartList])
}
}
1.2 菜品的加减---方案一
实现如下效果,当选择数量大于0时展示-及数量
:
改造MTAddCutView
,如下:
ts
import { FoodItem } from '../models'
import { CartStore } from '../utils/CartStore'
@Preview
@Component
export struct MTAddCutView {
// 当前菜品
@Require @Prop foodItem: FoodItem = new FoodItem()
// 购物车数据
@Consume cartList: FoodItem[]
// 当前选择数量
getCount() {
return this.cartList.find(obj => obj.id === this.foodItem.id)?.count || 0
}
build() {
Row({ space: 8 }) {
Row() {
Image($r('app.media.ic_screenshot_line'))
.width(10)
.aspectRatio(1)
}
.width(16)
.height(16)
.justifyContent(FlexAlign.Center)
.backgroundColor(Color.White)
.borderRadius(4)
.border({
color: $r('app.color.main_color'),
width: 0.5
})
// 如果为0,则取消展示
.visibility(this.getCount() > 0 ? Visibility.Visible : Visibility.Hidden)
// 减少菜品
.onClick(() => {
CartStore.addOrCutFood(this.foodItem, 'cut')
})
Text(this.getCount().toString())
.fontSize(14)
.visibility(this.getCount() > 0 ? Visibility.Visible : Visibility.Hidden)
Row() {
Image($r('app.media.ic_public_add_filled'))
.width(10)
.aspectRatio(1)
}
.width(16)
.height(16)
.justifyContent(FlexAlign.Center)
.borderRadius(4)
.backgroundColor($r('app.color.main_color'))
// 添加菜品
.onClick(() => {
CartStore.addOrCutFood(this.foodItem, 'add')
})
}
}
}
在主页面MeiTuanPage.ets
中,通过Watch
和StorageProp
实现数据动态展示:
ts
// 方案一:使用StorageProp和Watch实现
@StorageProp('cartStore') @Watch('onCartChange') cartData: FoodItem[] = []
// 购物车数据变化发生回调
onCartChange() {
this.cartList = CartStore.getCarts()
}
1.3 菜品的加减---方案二
使用事件总线实现事件的发布和订阅。
在CartStore.ets
中增加事件发布:
ts
...
AppStorage.set<FoodItem[]>('cartStore', [...cartList])
// 方案二:使用事件总线
getContext().eventHub.emit('changeCart')
...
在MeiTuanPage.ets
中注册订阅:
ts
aboutToAppear(): void {
this.categoryList = mockCategory
this.cartList = CartStore.getCarts()
// 方案二:使用事件总线
getContext().eventHub.on('changeCart', () => {
this.cartList = CartStore.getCarts()
})
}
1.4 购物车View完善
购物车展示真实数据及加减菜品:
MTCartView
ts
import { FoodItem } from '../models'
import { MTCartItemView } from './MTCartItemView'
@Preview
@Component
export struct MTCartView {
@Consume cartList: FoodItem[]
build() {
Column() {
Column() {
// 头部
Row() {
Text('购物车')
.fontSize(14)
Text('清空购物车')
.fontColor($r('app.color.search_font_color'))
.fontSize(12)
}
.width('100%')
.height(48)
.justifyContent(FlexAlign.SpaceBetween)
.padding({ left: 15, right: 15 })
// 购物车列表
List() {
ForEach(this.cartList, (item: FoodItem) => {
ListItem() {
MTCartItemView({ foodItem: item })
}
})
}
.divider({ strokeWidth: 1, color: '#e5e5e5', startMargin: 20, endMargin: 20 })
}
.backgroundColor(Color.White)
.padding({
bottom: 88
})
.borderRadius({
topLeft: 12,
topRight: 12
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.End)
.backgroundColor('rgba(0,0,0,0.5)')
}
}
MTCartItemView
ts
import { FoodItem } from '../models'
import { MTAddCutView } from './MTAddCutView'
@Preview
@Component
export struct MTCartItemView {
foodItem: FoodItem = new FoodItem()
build() {
Row({ space: 6 }) {
Image('https://bkimg.cdn.bcebos.com/pic/4d086e061d950a7bc94a331704d162d9f3d3c9e2')
.width(42)
.aspectRatio(1)
.borderRadius(5)
Column({ space: 3 }) {
Text(this.foodItem.name)
Row() {
Text() {
Span('¥')
.fontSize(10)
Span(this.foodItem.price.toString())
.fontColor($r('app.color.main_color'))
.fontSize(14)
.fontWeight(600)
}
MTAddCutView({ foodItem: this.foodItem })
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
}
.height(60)
.alignItems(VerticalAlign.Top)
.width('100%')
.padding({ top: 12, left: 15, right: 15, bottom: 12 })
}
}
1.5 清空购物车
CartStore.ets
中增加清空方法:
ts
static clearCart() {
AppStorage.set('cartStore', [])
getContext().eventHub.emit('changeCart')
}
购物车View中增加点击事件:
ts
...
Text('清空购物车')
.fontColor($r('app.color.search_font_color'))
.fontSize(12)
.onClick(() => {
CartStore.clearCart()
})
...
1.5 购物车数量和价格
修改MTBottomView
,计算购物车数量和价格:
ts
import { FoodItem } from '../models'
@Component
export struct MTBottomView {
@Consume
showCart: boolean
@Consume cartList: FoodItem[]
// 获取总数量
getTotalCount() {
return this.cartList.reduce((pre: number, item: FoodItem) => {
return pre + item.count
}, 0)
}
// 获取总价格
getTotalPrice() {
return this.cartList.reduce((pre: number, item: FoodItem) => {
return pre + item.count * item.price
}, 0)
}
build() {
Row() {
// 小哥+角标
Badge({ value: this.getTotalCount().toString(), style: { badgeSize: 18 }, position: BadgePosition.Right }) {
Image($r('app.media.ic_public_cart'))
.height(69)
.width(47)
.position({
y: -20
})
}
.margin({ left: 28, right: 12 })
.onClick(() => {
this.showCart = !this.showCart
})
// 金额+描述
Column() {
Text() {
Span('¥')
.fontColor(Color.White)
.fontSize(12)
Span(this.getTotalPrice().toString())
.fontColor(Color.White)
.fontSize(25)
}
Text('预估另需配送费¥5')
.fontColor($r('app.color.search_font_color'))
.fontSize(12)
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
// 去结算
Text('去结算')
.width(80)
.height(50)
.fontSize(16)
.backgroundColor($r('app.color.main_color'))
.textAlign(TextAlign.Center)
.borderRadius({
topRight: 25,
bottomRight: 25
})
}
.height(50)
.width('88%')
.margin({ bottom: 20 })
.backgroundColor(Color.Black)
.borderRadius(26)
}
}
二、小结
- cartStore应用
- 加减菜逻辑
- 购物车逻辑
- 事件总线