基于HarmonyOS的情侣美食管理应用开发实战(十)- 点餐清单与提交订单
📖 系列文章目录
- (一)项目设计与角色管理
- (二)菜谱管理与订单系统
- (三)美食相册与设置功能
- (四)数据库优化与问题解决
- (五)数据持久化问题修复
- (六)图片显示问题修复
- (七)菜谱详情页面开发
- (八)收藏功能实现
- (九)食客菜谱浏览功能
- (十)点餐清单与提交订单(本文)
- (十一)主厨订单处理功能
- (十二)双向订单记录查询
一、功能概述
1.1 需求分析
食客在浏览菜谱后,需要能够发起正式的点餐请求。本章节将实现完整的点餐流程,包括:
- 菜谱详情展示:展示菜谱的完整信息(食材、步骤)
- 备注功能:食客可以添加口味偏好和特殊要求
- 快捷备注:提供常用备注选项,一键添加
- 订单提交:将点餐信息保存到数据库
1.2 功能特点
✅ 信息完整:展示菜谱的所有相关信息
✅ 备注灵活:支持自定义备注和快捷选择
✅ 交互友好:卡片式布局,操作便捷
✅ 数据准确:订单信息完整保存到数据库
二、订单数据模型
2.1 Order模型定义
export class Order {
id: number = 0
recipeId: number = 0 // 菜谱ID
recipeName: string = '' // 菜谱名称
recipeImage: string = '' // 菜谱图片
status: number = 0 // 订单状态
remark: string = '' // 口味备注
orderTime: number = 0 // 下单时间
acceptTime: number = 0 // 接单时间
completeTime: number = 0 // 完成时间
createTime: number = 0 // 创建时间
updateTime: number = 0 // 更新时间
}
2.2 订单状态定义
在CommonConstant中定义订单状态常量:
/**
* 订单状态
*/
export const ORDER_STATUS_PENDING = 0 // 待处理
export const ORDER_STATUS_ACCEPTED = 1 // 已接受
export const ORDER_STATUS_COOKING = 2 // 制作中
export const ORDER_STATUS_COMPLETED = 3 // 已完成
export const ORDER_STATUS_CANCELLED = 4 // 已取消
三、页面结构设计
3.1 页面布局
┌─────────────────────────────┐
│ 导航栏(返回 + 菜名) │
├─────────────────────────────┤
│ 菜谱图片 │
├─────────────────────────────┤
│ 基本信息 │
│ - 菜名、类型、烹饪时长 │
│ - 描述 │
├─────────────────────────────┤
│ 食材列表 │
│ - 食材名称 + 用量 │
├─────────────────────────────┤
│ 制作步骤 │
│ - 步骤编号 + 描述 │
├─────────────────────────────┤
│ 口味备注 │
│ - 备注输入框 │
│ - 快捷备注按钮 │
├─────────────────────────────┤
│ 提交订单按钮 │
└─────────────────────────────┘
3.2 状态变量定义
@Entry
@Component
struct DinerOrderPage {
@State recipe: Recipe | null = null
@State remark: string = '' // 备注内容
@State quickRemarks: string[] = [ // 快捷备注选项
'少辣', '不辣', '多放葱', '不要香菜', '少盐'
]
@State selectedQuickRemarks: string[] = [] // 已选快捷备注
private recipeId: number = 0
private context: common.UIAbilityContext | null = null
}
四、菜谱信息加载
4.1 数据库查询方法
在RdbUtil中添加根据ID查询菜谱的方法:
static async queryRecipeById(id: number): Promise<Recipe | null> {
try {
const predicates = new relationalStore.RdbPredicates('recipe')
predicates.equalTo('id', id)
const resultSet = await this.rdbStore.query(predicates)
if (resultSet.goToFirstRow()) {
const recipe = Recipe.fromJson({...})
resultSet.close()
return recipe
}
resultSet.close()
return null
} catch (err) {
LogUtil.error('RdbUtil', `查询菜谱失败: ${err}`)
return null
}
}
4.2 页面初始化
aboutToAppear() {
try {
this.context = getContext(this) as common.UIAbilityContext
// 获取路由参数
const params = router.getParams() as Record<string, Object>
if (params && params['recipeId']) {
this.recipeId = params['recipeId'] as number
this.loadRecipe()
}
} catch (err) {
LogUtil.error('DinerOrderPage', `初始化失败: ${err}`)
}
}
五、备注功能实现
5.1 备注输入框
**
**
@Builder
RemarkSection() {
Column() {
Text('口味备注')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(CommonStyle.TEXT_COLOR_PRIMARY)
.width('100%')
// 备注输入框
TextArea({ placeholder: '请输入口味偏好或特殊要求...', text: this.remark })
.width('100%')
.height(80)
.backgroundColor(CommonStyle.BG_COLOR_GRAY)
.borderRadius(CommonStyle.BORDER_RADIUS_MD)
.onChange((value: string) => {
this.remark = value
})
.margin({ top: 12 })
// 快捷备注
Row() {
ForEach(this.quickRemarks, (item: string) => {
Text(item)
.fontSize(14)
.fontColor(this.selectedQuickRemarks.includes(item) ?
CommonStyle.TEXT_COLOR_WHITE : CommonStyle.TEXT_COLOR_PRIMARY)
.backgroundColor(this.selectedQuickRemarks.includes(item) ?
RoleStyle.DINER_COLOR : CommonStyle.BG_COLOR_GRAY)
.borderRadius(CommonStyle.BORDER_RADIUS_ROUND)
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.onClick(() => {
this.toggleQuickRemark(item)
})
})
}
.width('100%')
.justifyContent(FlexAlign.Start)
.margin({ top: 8 })
}
.width('100%')
.padding(16)
.backgroundColor(CommonStyle.BG_COLOR_WHITE)
.borderRadius(CommonStyle.BORDER_RADIUS_MD)
.margin({ top: 12 })
}
5.2 快捷备注逻辑
toggleQuickRemark(remark: string) {
const index = this.selectedQuickRemarks.indexOf(remark)
if (index > -1) {
// 已选中,取消选择
this.selectedQuickRemarks.splice(index, 1)
} else {
// 未选中,添加选择
this.selectedQuickRemarks.push(remark)
}
// 更新备注内容
this.updateRemarkContent()
}
六、订单提交功能
6.1 订单插入方法
在RdbUtil中添加插入订单的方法:
static async insertOrder(order: Order): Promise<boolean> {
try {
const valueBucket: relationalStore.ValuesBucket = {
'recipe_id': order.recipeId,
'recipe_name': order.recipeName,
'recipe_image': order.recipeImage,
'status': order.status,
'remark': order.remark,
'order_time': order.orderTime,
'accept_time': order.acceptTime,
'complete_time': order.completeTime,
'create_time': Date.now(),
'update_time': Date.now()
}
const rowId = await this.rdbStore.insert('order', valueBucket)
return rowId > 0
} catch (err) {
LogUtil.error('RdbUtil', `插入订单失败: ${err}`)
return false
}
}
6.2 提交订单逻辑
**
**
async submitOrder() {
if (!this.recipe) {
promptAction.showToast({
message: '菜谱信息加载失败',
duration: 2000
})
return
}
// 创建订单对象
const order = new Order()
order.recipeId = this.recipe.id
order.recipeName = this.recipe.name
order.recipeImage = this.recipe.image || ''
order.status = CommonConstant.ORDER_STATUS_PENDING // 待处理
order.remark = this.remark
order.orderTime = Date.now()
order.acceptTime = 0
order.completeTime = 0
try {
const success = await RdbUtil.insertOrder(order)
if (success) {
promptAction.showToast({
message: '下单成功!',
duration: 2000
})
// 返回上一页
router.back()
}
} catch (err) {
LogUtil.error('DinerOrderPage', `提交订单失败: ${err}`)
}
}
七、技术要点总结
7.1 路由参数传递
// 发送方
router.pushUrl({
url: 'pages/DinerOrderPage',
params: { recipeId: recipe.id }
})
// 接收方
const params = router.getParams() as Record<string, Object>
if (params && params['recipeId']) {
this.recipeId = params['recipeId'] as number
}
7.2 快捷备注交互
设计思路:
- 提供常用备注选项,减少输入
- 支持多选,自动拼接
- 点击切换选中状态
- 实时更新备注内容
7.3 订单状态初始化
order.status = CommonConstant.ORDER_STATUS_PENDING // 待处理
order.orderTime = Date.now() // 当前时间戳
order.acceptTime = 0 // 未接单
order.completeTime = 0 // 未完成
八、总结
通过本篇文章,我们完成了点餐清单与提交订单功能的开发:
✅ 菜谱详情完整展示:图片、信息、食材、步骤
✅ 备注功能灵活:支持自定义和快捷选择
✅ 订单提交可靠:数据完整保存到数据库
✅ 交互体验友好:卡片式布局,操作便捷
下一篇文章将介绍主厨订单处理功能的实现,敬请期待!