文章目录
- 一、购物车逻辑
 - 1.1 购物车及加减菜
 - 1.2 菜品的加减---方案一
 - 1.3 菜品的加减---方案二
 - 1.4 购物车View完善
 - 1.5 清空购物车
 - 1.5 购物车数量和价格
 
- 二、小结
 
一、购物车逻辑
1.1 购物车及加减菜
在utils目录下新建CartStore.ets文件,如下:
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,如下:
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实现数据动态展示:
// 方案一:使用StorageProp和Watch实现
@StorageProp('cartStore') @Watch('onCartChange') cartData: FoodItem[] = []
// 购物车数据变化发生回调
onCartChange() {
  this.cartList = CartStore.getCarts()
}
 
1.3 菜品的加减—方案二
使用事件总线实现事件的发布和订阅。
 在CartStore.ets中增加事件发布:
...
 AppStorage.set<FoodItem[]>('cartStore', [...cartList])
// 方案二:使用事件总线
getContext().eventHub.emit('changeCart')
...
 
在MeiTuanPage.ets中注册订阅:
aboutToAppear(): void {
  this.categoryList = mockCategory
  this.cartList = CartStore.getCarts()
  // 方案二:使用事件总线
  getContext().eventHub.on('changeCart', () => {
    this.cartList = CartStore.getCarts()
  })
}
 
1.4 购物车View完善
购物车展示真实数据及加减菜品:
 MTCartView
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
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中增加清空方法:
static clearCart() {
  AppStorage.set('cartStore', [])
  getContext().eventHub.emit('changeCart')
}
 
购物车View中增加点击事件:
...
Text('清空购物车')
  .fontColor($r('app.color.search_font_color'))
  .fontSize(12)
  .onClick(() => {
    CartStore.clearCart()
  })
...
 
1.5 购物车数量和价格

修改MTBottomView,计算购物车数量和价格:
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应用
 - 加减菜逻辑
 - 购物车逻辑
 - 事件总线
 


















