ToB企服应用市场:ToB评测及商务社交产业平台

标题: 基于鸿蒙HarmonyOS_NEXT的美团点餐案例 [打印本页]

作者: 用户国营    时间: 2024-12-25 03:07
标题: 基于鸿蒙HarmonyOS_NEXT的美团点餐案例
工程目录:
-pages
        -MeiTuan
                -api 请求
                -components 组件
                -models 类型
                -utils 工具类
                -MTIndex.ets(Page) 主页面


MTIndex
  1. import MTBottom from './components/MTBottom'
  2. import MTCart from './components/MTCart'
  3. import MTMain from './components/MTMain'
  4. import MTTop from './components/MTTop'
  5. import { FoodItem } from './models'
  6. import { CartStore } from './utils'
  7. import { promptAction } from '@kit.ArkUI'
  8. //主页面:
  9. @Entry
  10.   @Component
  11.   struct MTIndex {
  12.     @Provide//判断购物车是否出来的一个变量
  13.     showCart:boolean=false
  14.     @Provide//接收CartStore.getCarts()传过来的购物车数据,然后传给MTAddCut,MTCart,MTBottom的consume
  15.     userCart: FoodItem[] = []
  16.     // @StorageProp("user_cart")//创建一个data来拿到utils的数据,watch监听utils的数据变化
  17.     // @Watch("updateCart")
  18.     // data:FoodItem[]=[]
  19.     aboutToAppear(): void {//一打开页面就接收购物车,只执行一次
  20.       this.userCart = CartStore.getCarts()
  21.       getContext().eventHub.on("change_cart",()=>{  //监听线程间通信的更新
  22.         this.userCart = CartStore.getCarts()//重新赋值给userCart
  23.       })
  24.     }
  25.     // updateCart(){//监听到data变化,进行持久化数据的更新
  26.     //   this.userCart = CartStore.getCarts()
  27.     // }
  28.     build() {
  29.       Column() {
  30.         Stack({ alignContent: Alignment.Bottom }) {
  31.           Column() {
  32.             MTTop()
  33.             MTMain()
  34.           }
  35.           .height("100%")
  36.           if (this.showCart){  //如果showCart=1,打开购物车MTCart。
  37.             MTCart()
  38.           }
  39.           MTBottom()//底层组件
  40.         }.layoutWeight(1)
  41.       }
  42.       .width('100%')
  43.         .height("100%")
  44.         .backgroundColor($r("app.color.white"))
  45.     }
  46.   }
复制代码
API/index
  1. import { http } from '@kit.NetworkKit'
  2. import { promptAction } from '@kit.ArkUI'
  3. import { Category } from '../models'
  4. //发情网络请求方法 api/index.ets 使用 http 发送请求,获取数据
  5. export const getFoodData = async ()=>{
  6.   const res = http.createHttp()
  7.   try {
  8.     const result = await res.request('https://zhousg.atomgit.net/harmonyos-next/takeaway.json')
  9.     return  JSON.parse(result.result as string)as  Category[]
  10.   }
  11.   catch (error){//出现问题执行:
  12.     promptAction.showToast({message:error.message})
  13.     return Promise.reject(error)//逻辑请求已经出错,不能再继续了
  14.   }
  15.   finally {
  16.     res.destroy()//销毁这个请求
  17.   }
  18. }
复制代码
MTAddCut
  1. //夹菜和减菜的组件:
  2. import { FoodItem } from '../models'
  3. import { AddCutEnum, CartStore } from '../utils'
  4. @Component
  5.   struct MTAddCut {
  6.     @Consume
  7.     userCart:FoodItem[]//接收MTIndex的provide传过来的购物车
  8.     fid:number=0//接收MTFoodItem传过来的id来跟购物车判断:在不在购物车里
  9.     item:FoodItem = new FoodItem()//接收MTFoodItem传过来的item
  10.     getCount(){//判断当前的菜品和购物车的关系,只要当前菜品在购物车里,就把<数量>显示
  11.       return this.userCart.find(item => item.id === this.fid)?.count || 0  //去购物车里找有没有和这个id一样的菜品,如果有说明这个菜品在购物车里
  12.     }
  13.     build() {
  14.       Row({ space: 8 }) {
  15.         Row() {
  16.           Image($r('app.media.ic_screenshot_line'))
  17.             .width(10)
  18.             .aspectRatio(1)
  19.         }
  20.         .width(16)
  21.           .aspectRatio(1)
  22.           .justifyContent(FlexAlign.Center)
  23.           .backgroundColor($r("app.color.white"))
  24.           .borderRadius(4)
  25.           .border({ width: 0.5 , color: $r("app.color.main_color")})
  26.           .visibility(this.getCount() ? Visibility.Visible : Visibility.Hidden)//没有就消失,但是会占位
  27.           .onClick(()=>{//减菜
  28.             CartStore.addCutCart(AddCutEnum.CUT,this.item)//把item传给-菜方法
  29.           })
  30.         Text(this.getCount().toString())
  31.           .visibility(this.getCount() ? Visibility.Visible : Visibility.Hidden)//没有就消失,但是会占位
  32.         Row() {
  33.           Image($r('app.media.ic_public_add_filled'))
  34.             .width(10)
  35.             .aspectRatio(1)
  36.         }
  37.         .width(16)
  38.           .aspectRatio(1)
  39.           .justifyContent(FlexAlign.Center)
  40.           .backgroundColor($r("app.color.main_color"))
  41.           .borderRadius(4)
  42.           .onClick(()=>{//+菜
  43.             CartStore.addCutCart(AddCutEnum.ADD,this.item)//把item传给加菜方法
  44.           })
  45.       }
  46.     }
  47.   }
  48. export default MTAddCut
复制代码
MTBottom
  1. import { FoodItem } from '../models'
  2. @Component
  3.   struct MTBottom {
  4.     @Consume//用来接收MTIndex传过来的showCart
  5.     showCart:boolean
  6.     @Consume//接MTIndex传下来的购物车userCart
  7.     userCart:FoodItem[]
  8.     //总件数方法:
  9.     getTotalCount(){
  10.       return this.userCart.reduce((preValue:number,item:FoodItem)=>{
  11.         return preValue + item.count //0+总数
  12.       },0)
  13.     }
  14.     //算总价格:
  15.     getTotalPrice(){
  16.       return this.userCart.reduce((preValue:number,item:FoodItem)=>{
  17.         return preValue + item.count * item.price //0+总数
  18.       },0)
  19.     }
  20.     build() {
  21.       Row(){
  22.         Row(){
  23.           //小哥图像显示:
  24.           Badge({ value: this.getTotalCount().toString(), position: BadgePosition.Right, style:{badgeSize:18}}){
  25.             Image($r("app.media.ic_public_cart"))
  26.               .width(47)
  27.               .height(69)
  28.               .position({y:-20})
  29.           }
  30.           .margin({left:25,right:10})
  31.             .onClick(()=>{//点击小哥,就把showCart取反,来关闭和打开购物车
  32.               this.showCart=!this.showCart
  33.             })
  34.           //显示总费用:
  35.           Column(){
  36.             Text(){
  37.               //span imagespan
  38.               Span("¥")
  39.                 .fontSize(12)
  40.               Span(this.getTotalPrice().toString())
  41.                 .fontSize(24)
  42.             }
  43.             .fontColor($r("app.color.white"))
  44.             Text("预估另需配送费¥5元")
  45.               .fontColor($r("app.color.search_font_color"))
  46.               .fontSize(14)
  47.           }
  48.           .alignItems(HorizontalAlign.Start)//column的水平轴对齐方式
  49.             .layoutWeight(1)
  50.           //去结算按钮:
  51.           Text("去结算")
  52.             .height(50)
  53.             .textAlign(TextAlign.Center)//字要居中
  54.             .width(80)
  55.             .backgroundColor($r("app.color.main_color"))
  56.             .borderRadius({topRight:25,bottomRight:20})
  57.         }
  58.         .height(50)
  59.           .backgroundColor($r("app.color.bottom_back"))
  60.           .width('100%')
  61.           .borderRadius(25)
  62.       }
  63.       .padding({
  64.         left:20,
  65.         right:20,
  66.         bottom:20,
  67.       })
  68.         .width('100%')
  69.     }
  70.   }
  71. export default MTBottom
复制代码
MTCart
  1. import { FoodItem } from '../models'
  2. import { CartStore } from '../utils'
  3. import MTCartItem from './MTCartItem'
  4. //购物车组件:
  5. @Component
  6.   struct MTCart {
  7.     @Consume
  8.     userCart:FoodItem[]//接收MTIndex的provide传过来的购物车
  9.     build() {
  10.       Column(){
  11.         Column(){
  12.           Row(){
  13.             Text("购物车")
  14.               .fontSize(12)
  15.             Text("清空购物车")
  16.               .fontSize(12)
  17.               .fontColor($r("app.color.search_font_color"))
  18.               .onClick(()=>{
  19.                 CartStore.clearCart()
  20.               })
  21.           }
  22.           .height(50)
  23.             .width('100%')
  24.             .justifyContent(FlexAlign.SpaceBetween)//两头对其
  25.             .padding({left:15,right:15})
  26.           //生成n个的菜单项:
  27.           List({space:20}){
  28.             ForEach(this.userCart,(item:FoodItem)=>{
  29.               ListItem(){
  30.                 MTCartItem({item:item})
  31.               }}, (item:FoodItem) => item.id.toString() )//第三个参数 防止闪烁
  32.           }.padding({left:15,right:15})
  33.         }
  34.         .borderRadius({ topLeft:16,topRight:16})
  35.           .padding({ bottom:100 })
  36.           .width('100%')
  37.           .backgroundColor($r('app.color.white'))
  38.       }
  39.       .width('100%')
  40.         .height('100%')
  41.         .justifyContent(FlexAlign.End)
  42.         .backgroundColor("rgba(0,0,0,0.5)")//透明的背景色 0.5透明度
  43.     }
  44.   }
  45. export default MTCart
复制代码
MTCartItem
  1. import { FoodItem } from '../models'
  2. import MTAddCut from './MTAddCut'
  3. @Component
  4.   struct MTCartItem {//购物车里商品抽提成的组件   单个
  5.     item:FoodItem= new FoodItem()//接收MTCart的foreach传过来的item进行单个渲染
  6.     build() {
  7.       Row({space:10}){
  8.         Image(this.item.picture)
  9.           .width(60)
  10.           .aspectRatio(1)
  11.           .borderRadius(8)
  12.         Column(){
  13.           Text(this.item.name)
  14.             .fontSize(14)
  15.             .textOverflow({
  16.               overflow:TextOverflow.Ellipsis
  17.             })
  18.           Row(){
  19.             Text(){
  20.               Span("¥")
  21.                 .fontSize(10)
  22.               Span(this.item.price.toString())//价格
  23.                 .fontSize(20)
  24.                 .fontWeight(600)
  25.             }
  26.             .fontColor($r('app.color.font_main_color'))
  27.             MTAddCut({fid:this.item.id,item:this.item})
  28.           }
  29.           .width('100%')
  30.             .justifyContent(FlexAlign.SpaceBetween)
  31.         }
  32.         .layoutWeight(1)
  33.           .alignItems(HorizontalAlign.Start)//column的副轴对齐方式
  34.       }
  35.       .width('100%')
  36.     }
  37.   }
  38. export default MTCartItem
复制代码
MTFoodItem
  1. import { FoodItem } from '../models'
  2. import MTAddCut from './MTAddCut'
  3. @Component
  4.   struct MTFoodItem {       //菜单栏的商品抽提成的组件  单个
  5.     item :FoodItem = new FoodItem()//接收main的 ForEach传过来的数据并渲染
  6.     build() {
  7.       Row() {
  8.         Image(this.item.picture)
  9.           .width(90)
  10.           .aspectRatio(1)
  11.         Column({ space: 5 }) {
  12.           Text(this.item.name)
  13.             .textOverflow({
  14.               overflow: TextOverflow.Ellipsis,
  15.             })
  16.             .maxLines(2)
  17.             .fontWeight(600)
  18.           Text(this.item.description)
  19.             .textOverflow({
  20.               overflow: TextOverflow.Ellipsis,
  21.             })
  22.             .maxLines(1)
  23.             .fontSize(12)
  24.             .fontColor($r("app.color.food_item_second_color"))
  25.           ForEach(this.item.food_tag_list,(str:string)=>{
  26.             Text(str)
  27.               .fontSize(10)
  28.               .backgroundColor($r("app.color.food_item_label_color"))
  29.               .fontColor($r("app.color.font_main_color"))
  30.               .padding({ top: 2, bottom: 2, right: 5, left: 5 })
  31.               .borderRadius(2)
  32.           })
  33.           Text() {
  34.             Span('月销售'+this.item.month_saled)
  35.             Span(' ')
  36.             Span(this.item.like_ratio_desc)
  37.           }
  38.           .fontSize(12)
  39.             .fontColor($r("app.color.black"))
  40.           Row() {
  41.             Text() {
  42.               Span('¥ ')
  43.                 .fontColor($r("app.color.font_main_color"))
  44.                 .fontSize(10)
  45.               Span(this.item.price.toString())
  46.                 .fontColor($r("app.color.font_main_color"))
  47.                 .fontWeight(FontWeight.Bold)
  48.             }
  49.             MTAddCut({ fid:this.item.id ,item:this.item})//把id传给MTAddCut
  50.           }
  51.           .justifyContent(FlexAlign.SpaceBetween)
  52.             .width('100%')
  53.         }
  54.         .layoutWeight(1)
  55.           .alignItems(HorizontalAlign.Start)
  56.           .padding({ left: 10, right: 10 })
  57.       }
  58.       .padding(10)
  59.         .alignItems(VerticalAlign.Top)
  60.     }
  61.   }
  62. export default MTFoodItem
复制代码
MTMain
  1. import { getFoodData } from '../API'
  2. import { Category, FoodItem } from '../models'
  3. import MTFoodItem from './MTFoodItem'
  4. @Component
  5.   struct MTMain {
  6.     @State
  7.     list:Category[]=[]
  8.     // @State
  9.     // list:string[]=["一人套餐","特色烧烤","杂粮主食"]
  10.     @State
  11.     activeIndex:number = 0//激活项的点菜类型索引
  12.     aboutToAppear(): void {
  13.       this.getData()
  14.     }
  15.     //获取数据的方法:
  16.     async getData(){
  17.       this.list = await getFoodData()//调用网络请求方法,把数据传给list
  18.     }
  19.     build() {
  20.       Row(){
  21.         //左侧分类:
  22.         Column(){
  23.           ForEach(this.list,(item:Category,index:number)=>{
  24.             Text(item.name)
  25.               .height(50)
  26.               .width('100%')
  27.               .fontSize(14)
  28.               .backgroundColor(this.activeIndex === index?$r("app.color.white"):$r("app.color.left_back_color"))//选中的话显示白色,不选中默认色
  29.               .textAlign(TextAlign.Center)
  30.               .onClick(()=>{
  31.                 this.activeIndex=index//点到谁就把它的索引给激活项索引
  32.               })
  33.           })
  34.         }
  35.         .height('100%')
  36.           .backgroundColor($r("app.color.left_back_color"))
  37.           .width(90)
  38.         //右侧食物列表:
  39.         List(){
  40.           ForEach( this.list[this.activeIndex]?.foods || [] ,(item:FoodItem)=>{
  41.             ListItem(){
  42.               MTFoodItem({item:item})//把数据传给MTFoodItem去渲染一个一个的菜品
  43.             }
  44.           })
  45.         }
  46.         .height('100%')
  47.           .layoutWeight(1)
  48.           .padding({bottom:120})
  49.       }
  50.       .width('100%')
  51.     }
  52.   }
  53. export default MTMain
复制代码
MTTop
  1. @Component
  2.   struct MTTop {
  3.     @Builder
  4.     NavItem(active: boolean, title: string, subTitle?: string) {
  5.       Column() {
  6.         Text() {
  7.           Span(title)
  8.           if (subTitle) {
  9.             Span(' ' + subTitle)
  10.               .fontSize(10)
  11.               .fontColor(active ? $r("app.color.black") : $r("app.color.un_select_color"))
  12.           }
  13.         }.layoutWeight(1)
  14.           .fontColor(active ? $r("app.color.black") : $r("app.color.un_select_color"))
  15.           .fontWeight(active ? FontWeight.Bold : FontWeight.Normal)
  16.         Text()
  17.           .height(1)
  18.           .width(20)
  19.           .margin({ left: 6 })
  20.           .backgroundColor(active ? $r("app.color.select_border_color") : 'transparent')
  21.       }
  22.       .width(73)
  23.         .alignItems(HorizontalAlign.Start)
  24.         .padding({ top: 3 })
  25.     }
  26.     build() {
  27.       Row() {
  28.         this.NavItem(true, '点菜')
  29.         this.NavItem(false, '评价', '1796')
  30.         this.NavItem(false, '商家')
  31.         Row() {
  32.           Image($r('app.media.ic_public_search'))
  33.             .width(14)
  34.             .aspectRatio(1)
  35.             .fillColor($r("app.color.search_font_color"))
  36.           Text('请输入菜品名称')
  37.             .fontSize(12)
  38.             .fontColor($r("app.color.search_back_color"))
  39.         }
  40.         .backgroundColor($r("app.color.search_back_color"))
  41.           .height(25)
  42.           .borderRadius(13)
  43.           .padding({ left: 5, right: 5 })
  44.           .layoutWeight(1)
  45.       }
  46.       .padding({ left: 15, right: 15 })
  47.         .height(40)
  48.         .border({ width: { bottom: 0.5 }, color: $r("app.color.top_border_color") })
  49.     }
  50.   }
  51. export default MTTop
复制代码
model/index
  1. //数据类型:
  2. //1.菜品类型:
  3. export class FoodItem {
  4.   id: number = 0
  5.   name: string = ""
  6.   like_ratio_desc: string = ""//好评率
  7.   food_tag_list: string[] = []//点评网友推荐
  8.   price: number = 0//价格
  9.   picture: string = ""
  10.   description: string = ""//描述
  11.   tag: string = ""
  12.   month_saled: number = 0//月销量
  13.   count: number = 0
  14. }
  15. //2.分类类型:
  16. export class Category {
  17.   tag: string = ""
  18.   name: string =""
  19.   foods: FoodItem[] = []
  20. }
复制代码
utils/index
  1. import { it } from '@ohos/hypium'
  2. import { FoodItem } from '../models'
  3. //控制购物车的读取
  4. //单例 只有一个实例
  5. PersistentStorage.persistProp("user_cart",[]) //声明了一个持久化的数据叫user_cart,初始值为空数组
  6. export class CartStore {
  7.   //1.获取购物车方法:
  8.   static getCarts (): FoodItem[] {
  9.     return  AppStorage.get("user_cart") || [] as FoodItem[] //如果拿不到,给上一个空数组
  10.   }
  11.   //2.加菜和减菜的方法:
  12.   static addCutCart(type:AddCutEnum ,item:FoodItem){
  13.     const list = CartStore.getCarts()//拿到持久化购物车给list
  14.     const findFood = list.find(obj => obj.id === item.id)
  15.     if (type===AddCutEnum.ADD) {
  16.       //加菜:这个菜在购物车存在与否?
  17.       if (findFood) {//意味着你点击的菜购物车里已经有了
  18.         findFood.count++
  19.       }
  20.       else {//你的购物车里没有这个菜:给购物车加入这个菜,数量为1:
  21.         list.unshift(item)//把这个菜加到头部
  22.         item.count=1
  23.       }
  24.     }
  25.     else if (type===AddCutEnum.CUT){
  26.       //减菜:
  27.       if (findFood && findFood.count>0 ) {//意味着你点击的菜 购物车里已经有了
  28.         findFood.count--
  29.         if (findFood.count === 0) {//减完了,得把这个菜删除购物车
  30.           let index = list.findIndex(obj=>obj.id === findFood.id)
  31.           list.splice(index,1)
  32.         }
  33.       }
  34.     }
  35.     //最后要把这个list数据更新到持久化
  36.     AppStorage.set("user_cart",[...list])//写入持久化,把list进行了一次拷贝
  37.     //写入的时候,如果发现对象的地址一致,就不写入
  38.     //第二种方法:事件总线
  39.     //线程内事件总线,线程间事件总线,进程间事件总线
  40.     getContext().eventHub.emit("change_cart")//触发更新购物车事件
  41.   }
  42.   //3.清空购物车方法:
  43.   static clearCart(){
  44.     AppStorage.set("user_cart",[])//把"user_cart"设成空数组
  45.     getContext().eventHub.emit("change_cart")//触发更新购物车事件
  46.   }
  47. }
  48. //写成枚举给addCutCart用
  49. export enum AddCutEnum{
  50.   ADD,
  51.   CUT
  52. }
复制代码
效果:



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4