蛋糕美食元服务_订单实现指南

订单模块实现流程操作指南

订单页面

一、模块概述

订单模块是蛋糕美食元服务的核心业务闭环模块,包含**订单确认(PageOrderPreview)订单历史(OrderList)**两个子功能。该模块实现了从购物车到下单、从订单创建到历史查看的完整订单生命周期管理。

二、模块职责

子功能 职责
PageOrderPreview 订单确认页:展示订单明细、价格汇总、支付操作
OrderList 订单历史:展示所有历史订单,支持刷新
OrderViewModel 订单业务逻辑:创建订单、查询订单、取消订单
OrderModel 订单数据模型:订单ID、金额、状态、商品列表

三、数据模型

订单模型(OrderModel.ets)

typescript 复制代码
export class OrderDetail {
  orderId: string = ''        // 格式: CK + 时间戳
  price: number = 0           // 订单总价
  orderType: OrderType = 0    // 0=堂食, 1=外卖
  orderShop: string = ''      // 下单门店
  takeOutAddress: string = '' // 配送地址(外卖时)
  orderTime: string = ''      // 下单时间戳
  orderStatus: OrderStatus = 0 // 0=待支付,1=处理中,2=已取消
  orderItem: CakeDetail[] = [] // 订单商品列表
}

// 订单状态枚举
export enum OrderStatus {
  UN_PAY,     // 待支付
  COMMITED,   // 处理中
  CANCELED    // 已取消
}

// 时间格式化工具
export function getFormatTime(timestamp: string): string {
  // 返回 "2025-06-04 14:30:00" 格式
}

四、订单确认页(PageOrderPreview)实现

步骤1:页面数据初始化

从 LocalStorage 读取购物车数据,恢复 CartViewModel:

typescript 复制代码
@Component
export struct PageOrderPreview {
  @LocalStorageLink('buyFoods') buyFoodsJson: string = '[]'
  @LocalStorageLink('choosePrice') choosePrice: number = 0
  @LocalStorageLink('orderType') orderType: number = 0
  @LocalStorageLink('chooseShop') chooseShop: string = ''
  @StorageLink('selectedIndex') selectedIndex: number = 0
  @State cartVM: CartViewModel = new CartViewModel()
  @State enableLoading: boolean = false

  aboutToAppear(): void {
    if (this.buyFoodsJson.length > 2) {
      this.cartVM = CartViewModel.fromJSON(this.buyFoodsJson)
    }
  }
}

步骤2:门店信息区域

typescript 复制代码
Column() {
  Row() {
    Text('🏪').fontSize(24)
    Text(this.chooseShop).layoutWeight(1)
  }
  Row() {
    Text(this.orderType === OrderType.DINE_IN ? '堂食' : '外卖配送')
      .backgroundColor('#FFF0EB')
    Text(this.orderType === OrderType.DINE_IN
      ? '预计取餐时间:30分钟内'
      : '预计配送时间:45分钟内')
  }
}

步骤3:外卖地址显示(条件渲染)

typescript 复制代码
if (this.orderType === OrderType.TAKEOUT) {
  Column() {
    Row() { Text('📍 配送地址') }
    if (this.takeOutAddress.length > 0) {
      Text(this.takeOutAddress)
    } else {
      Text('请选择配送地址')
        .onClick(() => {
          RouterModule.push({
            stackName: NavStackMap.MAIN_STACK,
            url: NavRouterMap.PAGE_TAKE_OUT
          } as NavRouterInfo)
        })
    }
  }
}

步骤4:订单明细列表

遍历购物车中的商品,展示名称、描述、单价和数量:

typescript 复制代码
ForEach(this.cartVM.getCartItems(), (item: CakeDetail) => {
  Row() {
    Text(item.pic).width(40).height(40)
    Column() {
      Text(item.title)
      Text(item.desc)
    }.layoutWeight(1)
    Text(`¥${item.price.toFixed(1)}`)
    Text(`x${item.buyNum}`)
  }
})

步骤5:价格汇总

typescript 复制代码
Column() {
  Row() {
    Text('商品总价')
    Blank()
    Text(`¥${this.cartVM.getTotalPrice().toFixed(1)}`)
  }
  Row() {
    Text('配送费')
    Blank()
    Text(this.orderType === OrderType.DINE_IN ? '¥0.0' : '¥5.0')
  }
  Row() {
    Text('实付金额')
    Blank()
    Text(`¥${(this.cartVM.getTotalPrice() +
      (this.orderType === OrderType.DINE_IN ? 0 : 5)).toFixed(1)}`)
      .fontColor('#FF6B35')
  }
}

价格计算规则

  • 堂食:无配送费
  • 外卖:固定配送费 ¥5.0

步骤6:支付流程

使用 LoadingProgress 组件模拟支付过程:

typescript 复制代码
private async submitOrder(): Promise<void> {
  this.enableLoading = true
  try {
    const context = getContext(this) as Context
    const order = await OrderViewModel.createOrder(
      context as UIAbilityContext,
      this.cartVM,
      this.orderType === OrderType.DINE_IN ? OrderType.DINE_IN : OrderType.TAKEOUT,
      this.chooseShop,
      this.takeOutAddress
    )
    if (order) {
      this.buyFoodsJson = '[]'    // 清空购物车
      this.choosePrice = 0
      this.selectedIndex = 2       // 切换到订单Tab
      RouterModule.popToName(NavStackMap.MAIN_STACK, NavRouterMap.PAGE_MAIN)
    }
  } catch (e) {}
  this.enableLoading = false
}

五、订单业务逻辑(OrderViewModel)

创建订单

typescript 复制代码
static async createOrder(
  context: UIAbilityContext,
  cart: CartViewModel,
  orderType: OrderType,
  shopName: string,
  address: string
): Promise<OrderDetail | undefined> {
  const order = new OrderDetail(
    createOrderId(),           // CK + Date.now()
    cart.getTotalPrice(),
    orderType, shopName, address,
    Date.now().toString(),
    OrderStatus.COMMITED,      // 直接标记为已提交
    cart.getCartItems()
  )
  await PreferenceUtil.putPreference(
    context, 'cake.order', order.orderId, JSON.stringify(order)
  )
  cart.clearCart()  // 清空购物车
  return order
}

查询订单列表

typescript 复制代码
static async getOrderList(context: UIAbilityContext): Promise<OrderDetail[]> {
  const store = await PreferenceUtil.getPreference(context, 'cake.order')
  const entries = await store.getAll()
  const orderList: OrderDetail[] = []
  const keys: string[] = Object.keys(entries)
  for (const key of keys) {
    const value = entries[key]
    if (value && typeof value === 'string' && value.length > 0) {
      orderList.push(JSON.parse(value as string) as OrderDetail)
    }
  }
  // 按时间倒序排列
  orderList.sort((a, b) => Number(b.orderTime) - Number(a.orderTime))
  return orderList
}

六、订单历史页(OrderList)实现

步骤1:数据加载

typescript 复制代码
aboutToAppear(): void {
  this.loadOrders()
}

private async loadOrders(): Promise<void> {
  this.isLoading = true
  this.orderList = await OrderViewModel.getOrderList(context)
  this.isLoading = false
}

步骤2:空状态展示

typescript 复制代码
if (this.orderList.length === 0) {
  Column() {
    Text('📭').fontSize(60)
    Text('暂无订单记录')
    Text('去订购一个美味的蛋糕吧!')
  }
}

步骤3:订单卡片渲染

使用 OrderItemView 组件展示每个订单:

typescript 复制代码
ForEach(this.orderList, (order: OrderDetail) => {
  OrderItemView({ order: order })
})

七、订单数据流图

复制代码
美食页面(Food)                    订单确认页(PageOrderPreview)
  │ buyFoods (LocalStorage)  ─────→  aboutToAppear 恢复 CartVM
  │                                  │
  │                               用户点击"确认付款"
  │                                  │
  │                               OrderViewModel.createOrder()
  │                                  │
  │                               Preferences 持久化存储
  │                                  │
  │                               cartVM.clearCart()
  │                                  │
  │ buyFoods = '[]'  ←──────────── 清空购物车
  │ selectedIndex = 2 ←─────────── 切换到订单Tab
  │                                  │
  └────────────────────────────→ 订单历史页(OrderList)
                                   │
                                aboutToAppear 加载订单
                                   │
                                Preferences.getAll()

八、注意事项

  1. 订单ID格式为 CK + 时间戳,确保全局唯一
  2. popToName 用于返回到主页面,清除中间页面栈
  3. 使用 async/await 处理异步操作,避免阻塞UI
  4. enableLoading 状态控制支付按钮可用性,防止重复提交
  5. 订单时间存储为字符串类型的时间戳,展示时格式化为可读格式
相关推荐
Swift社区1 小时前
鸿蒙游戏如何实现多端一致性?
游戏·华为·harmonyos
木咺吟1 小时前
【鸿蒙原生应用开发实战】第一篇:项目初始化与架构设计——从零搭建“阅迹“阅读应用
华为·harmonyos
组合缺一1 小时前
SolonCode(编码智能体)支持鸿蒙 PC
java·华为·ai·ai编程·harmonyos·solon·soloncode
xym1 小时前
鸿蒙 Node-API 自用整理
harmonyos
yuegu7772 小时前
HarmonyOS应用<节气通>开发第24篇:响应式布局设计
深度学习·harmonyos
JohnnyDeng942 小时前
【鸿蒙】HarmonyOS 通知与后台任务:WorkScheduler 机制深度解析
harmonyos·arkts·鸿蒙·arkui·后台任务
祭曦念3 小时前
BLOG_番茄钟开发实战
华为·harmonyos
TrisighT3 小时前
Electron 本地图片在鸿蒙 PC 上白图,我注册了个自定义协议
electron·harmonyos
祭曦念3 小时前
鸿蒙原生 ArkTS 布局探索(一):FlexRowReverse 弹性布局深度解析
华为·harmonyos