HarmonyOS NEXT星河版之美团外卖点餐功能实战(下)

一、购物车逻辑

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中,通过WatchStorageProp实现数据动态展示:

// 方案一:使用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应用
  • 加减菜逻辑
  • 购物车逻辑
  • 事件总线
  • 14
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值