基于HarmonyOS的情侣美食管理应用开发实战(十二)- 双向订单记录查询
·
基于HarmonyOS的情侣美食管理应用开发实战(十二)- 双向订单记录查询
📖 系列文章目录
- (一)项目设计与角色管理
- (二)菜谱管理与订单系统
- (三)美食相册与设置功能
- (四)数据库优化与问题解决
- (五)数据持久化问题修复
- (六)图片显示问题修复
- (七)菜谱详情页面开发
- (八)收藏功能实现
- (九)食客菜谱浏览功能
- (十)点餐清单与提交订单
- (十一)主厨订单处理功能
- (十二)双向订单记录查询(本文)
一、功能概述
1.1 需求分析
在情侣美食管理应用中,食客和主厨都需要查看订单记录。本章节将实现双向订单记录查询功能,包括:
- 食客订单列表:食客查看自己的历史订单
- 订单详情查看:查看订单的完整信息
- 时间线展示:展示订单的处理时间线
- 取消订单功能:食客可取消未处理的订单
1.2 功能特点
✅ 双向查询:食客和主厨都能查看订单
✅ 详情完整:展示订单和菜谱的完整信息
✅ 时间线清晰:直观展示订单处理流程
✅ 操作权限明确:根据角色和状态显示操作
二、食客订单列表
2.1 页面状态管理
@Component
struct DinerHomePage {
@State orders: Order[] = [] // 订单列表
@State myOrderCount: number = 0 // 订单数量
async initData() {
// 加载订单列表
await this.loadOrders()
// 加载订单数量
await this.loadMyOrderCount()
}
async loadOrders() {
try {
this.orders = await RdbUtil.queryAllOrders()
LogUtil.info('DinerHomePage', `加载订单列表成功,共 ${this.orders.length} 条`)
} catch (err) {
LogUtil.error('DinerHomePage', `加载订单列表失败: ${err}`)
}
}
}
2.2 订单列表UI
**
**
@Builder
MyOrdersTab() {
Column() {
// 标题栏
Row() {
Text('我的订单')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor(CommonStyle.TEXT_COLOR_PRIMARY)
Blank()
Text(`共 ${this.myOrderCount} 个`)
.fontSize(12)
.fontColor(CommonStyle.TEXT_COLOR_SECONDARY)
}
.width('100%')
.padding(16)
// 空状态提示
if (this.myOrderCount === 0) {
Column() {
Text('🕐')
.fontSize(80)
.fontColor(CommonStyle.TEXT_COLOR_HINT)
Text('暂无订单')
.fontSize(14)
.fontColor(CommonStyle.TEXT_COLOR_HINT)
.margin({ top: 16 })
Text('浏览菜谱并发起点餐')
.fontSize(12)
.fontColor(CommonStyle.TEXT_COLOR_HINT)
.margin({ top: 4 })
}
.width('100%')
.layoutWeight(1)
.justifyContent(FlexAlign.Center)
} else {
// 订单列表
List({ space: 12 }) {
ForEach(this.orders, (order: Order) => {
ListItem() {
this.OrderCard(order)
}
}, (order: Order) => order.id.toString())
}
.width('100%')
.layoutWeight(1)
.padding({ left: 16, right: 16 })
}
}
.width('100%')
.height('100%')
.backgroundColor(CommonStyle.BG_COLOR)
}
2.3 订单卡片设计
**
**
@Builder
OrderCard(order: Order) {
Column() {
// 订单头部
Row() {
Text(order.recipeName)
.fontSize(16)
.fontColor(CommonStyle.TEXT_COLOR_PRIMARY)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Text(this.getOrderStatusText(order.status))
.fontSize(12)
.fontColor(CommonStyle.TEXT_COLOR_WHITE)
.backgroundColor(this.getOrderStatusColor(order.status))
.borderRadius(CommonStyle.BORDER_RADIUS_ROUND)
.padding({ left: 12, right: 12, top: 4, bottom: 4 })
}
.width('100%')
// 订单信息
Row() {
if (order.recipeImage) {
Image(order.recipeImage)
.width(60)
.height(60)
.borderRadius(CommonStyle.BORDER_RADIUS_MD)
.objectFit(ImageFit.Cover)
} else {
Column() {
Text('🍳')
.fontSize(30)
}
.width(60)
.height(60)
.borderRadius(CommonStyle.BORDER_RADIUS_MD)
.backgroundColor(CommonStyle.BG_COLOR_GRAY)
.justifyContent(FlexAlign.Center)
}
Column() {
Text(`下单时间: ${this.formatTime(order.orderTime)}`)
.fontSize(12)
.fontColor(CommonStyle.TEXT_COLOR_SECONDARY)
if (order.remark) {
Text(`备注: ${order.remark}`)
.fontSize(12)
.fontColor(CommonStyle.TEXT_COLOR_SECONDARY)
.margin({ top: 4 })
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
.padding({ left: 12 })
}
.width('100%')
.margin({ top: 12 })
}
.width('100%')
.padding(16)
.backgroundColor(CommonStyle.BG_COLOR_WHITE)
.borderRadius(CommonStyle.BORDER_RADIUS_MD)
.onClick(() => {
router.pushUrl({
url: 'pages/OrderDetailPage',
params: { orderId: order.id }
})
})
}
三、订单详情页面
3.1 页面结构设计
**
**
┌─────────────────────────────┐
│ 导航栏(返回 + 标题) │
├─────────────────────────────┤
│ 订单状态卡片 │
│ - 菜名、状态标签 │
│ - 菜谱图片 │
├─────────────────────────────┤
│ 订单信息 │
│ - 下单时间 │
│ - 接单时间 │
│ - 完成时间 │
│ - 口味备注 │
├─────────────────────────────┤
│ 所需食材 │
│ - 食材列表 │
├─────────────────────────────┤
│ 制作步骤 │
│ - 步骤列表 │
├─────────────────────────────┤
│ 操作按钮(可选) │
└─────────────────────────────┘
3.2 页面状态管理
@Entry
@Component
struct OrderDetailPage {
@State order: Order | null = null
@State recipe: Recipe | null = null
@State currentRole: string = ''
private orderId: number = 0
async initData() {
// 获取当前角色
await RolePermissionUtil.init(this.context!)
this.currentRole = RolePermissionUtil.getCurrentRole()
// 加载订单信息
await this.loadOrder()
// 加载菜谱信息
if (this.order) {
await this.loadRecipe()
}
}
async loadOrder() {
this.order = await RdbUtil.queryOrderById(this.orderId)
}
async loadRecipe() {
if (this.order) {
this.recipe = await RdbUtil.queryRecipeById(this.order.recipeId)
}
}
}
3.3 时间线展示
@Builder
OrderTimeline() {
Column() {
// 下单时间
Row() {
Text('下单时间:')
.fontSize(14)
.fontColor(CommonStyle.TEXT_COLOR_SECONDARY)
Text(this.formatTime(this.order!.orderTime))
.fontSize(14)
.fontColor(CommonStyle.TEXT_COLOR_PRIMARY)
}
.width('100%')
// 接单时间
if (this.order!.acceptTime > 0) {
Row() {
Text('接单时间:')
.fontSize(14)
.fontColor(CommonStyle.TEXT_COLOR_SECONDARY)
Text(this.formatTime(this.order!.acceptTime))
.fontSize(14)
.fontColor(CommonStyle.TEXT_COLOR_PRIMARY)
}
.width('100%')
.margin({ top: 8 })
}
// 完成时间
if (this.order!.completeTime > 0) {
Row() {
Text('完成时间:')
.fontSize(14)
.fontColor(CommonStyle.TEXT_COLOR_SECONDARY)
Text(this.formatTime(this.order!.completeTime))
.fontSize(14)
.fontColor(CommonStyle.TEXT_COLOR_PRIMARY)
}
.width('100%')
.margin({ top: 8 })
}
}
.width('100%')
.alignItems(HorizontalAlign.Start)
}
四、取消订单功能
4.1 操作按钮显示规则
@Builder
OrderActions() {
// 食客可取消未处理的订单
if (this.currentRole === CommonConstant.ROLE_DINER &&
this.order!.status === CommonConstant.ORDER_STATUS_PENDING) {
Column() {
Button('取消订单')
.fontSize(16)
.fontColor(CommonStyle.TEXT_COLOR_WHITE)
.backgroundColor('#FF3B30')
.borderRadius(CommonStyle.BORDER_RADIUS_MD)
.width('100%')
.height(50)
.onClick(() => {
this.cancelOrder()
})
}
.width('100%')
.padding(16)
.margin({ top: 12, bottom: 20 })
}
}
4.2 取消订单逻辑
async cancelOrder() {
if (!this.order) {
return
}
try {
this.order.status = CommonConstant.ORDER_STATUS_CANCELLED
const success = await RdbUtil.updateOrder(this.order)
if (success) {
promptAction.showToast({
message: '订单已取消',
duration: 2000
})
router.back()
}
} catch (err) {
LogUtil.error('OrderDetailPage', `取消订单失败: ${err}`)
promptAction.showToast({
message: '操作失败',
duration: 2000
})
}
}
五、数据库查询方法
5.1 根据ID查询订单
static async queryOrderById(id: number): Promise<Order | null> {
try {
const predicates = new relationalStore.RdbPredicates('order')
predicates.equalTo('id', id)
const resultSet = await this.rdbStore.query(predicates)
if (resultSet.goToFirstRow()) {
const order = Order.fromJson({
id: resultSet.getLong(resultSet.getColumnIndex('id')),
recipe_id: resultSet.getLong(resultSet.getColumnIndex('recipe_id')),
recipe_name: resultSet.getString(resultSet.getColumnIndex('recipe_name')),
recipe_image: resultSet.getString(resultSet.getColumnIndex('recipe_image')),
status: resultSet.getLong(resultSet.getColumnIndex('status')),
remark: resultSet.getString(resultSet.getColumnIndex('remark')),
order_time: resultSet.getLong(resultSet.getColumnIndex('order_time')),
accept_time: resultSet.getLong(resultSet.getColumnIndex('accept_time')),
complete_time: resultSet.getLong(resultSet.getColumnIndex('complete_time')),
create_time: resultSet.getLong(resultSet.getColumnIndex('create_time')),
update_time: resultSet.getLong(resultSet.getColumnIndex('update_time'))
})
resultSet.close()
return order
}
resultSet.close()
return null
} catch (err) {
LogUtil.error('RdbUtil', `查询订单失败: ${err}`)
return null
}
}
5.2 根据状态查询订单
static async queryOrdersByStatus(status: number): Promise<Order[]> {
try {
const predicates = new relationalStore.RdbPredicates('order')
predicates.equalTo('status', status)
predicates.orderByDesc('order_time')
const resultSet = await this.rdbStore.query(predicates)
const orders: Order[] = []
while (resultSet.goToNextRow()) {
const order = Order.fromJson({...})
orders.push(order)
}
resultSet.close()
return orders
} catch (err) {
LogUtil.error('RdbUtil', `查询订单失败: ${err}`)
return []
}
}
六、技术要点总结
6.1 角色权限控制
// 根据角色显示不同的操作
if (this.currentRole === CommonConstant.ROLE_DINER) {
// 食客:可取消未处理的订单
} else if (this.currentRole === CommonConstant.ROLE_CHEF) {
// 主厨:可接受、制作、完成订单
}
6.2 时间格式化
formatTime(timestamp: number): string {
if (!timestamp) {
return ''
}
const date = new Date(timestamp)
const year = date.getFullYear()
const month = (date.getMonth() + 1).toString().padStart(2, '0')
const day = date.getDate().toString().padStart(2, '0')
const hour = date.getHours().toString().padStart(2, '0')
const minute = date.getMinutes().toString().padStart(2, '0')
return `${year}-${month}-${day} ${hour}:${minute}`
}
6.3 数据关联查询
// 先查询订单
this.order = await RdbUtil.queryOrderById(this.orderId)
// 再根据订单中的recipeId查询菜谱
if (this.order) {
this.recipe = await RdbUtil.queryRecipeById(this.order.recipeId)
}
七、总结
通过本篇文章,我们完成了双向订单记录查询功能的开发:
✅ 食客订单列表:展示历史订单,支持点击查看详情
✅ 订单详情完整:展示订单信息、菜谱信息、时间线
✅ 取消订单功能:食客可取消未处理的订单
✅ 角色权限明确:根据角色显示不同的操作
至此,情侣美食管理应用的核心功能已全部实现完成!
八、系列文章总结
通过十二篇文章的讲解,我们完整实现了情侣美食管理应用的所有核心功能:
8.1 功能模块
| 模块 | 功能 | 文章 |
|---|---|---|
| 角色管理 | 角色选择、权限控制 | (一) |
| 菜谱管理 | 菜谱CRUD、食材步骤 | (二) |
| 美食相册 | 图片上传、相册展示 | (三) |
| 数据库 | RDB操作、数据持久化 | (四)(五) |
| 图片显示 | 图片加载、缓存优化 | (六) |
| 菜谱详情 | 详情展示、收藏功能 | (七)(八) |
| 食客浏览 | 搜索筛选、随机点菜 | (九) |
| 点餐下单 | 备注输入、订单提交 | (十) |
| 主厨处理 | 订单管理、状态流转 | (十一) |
| 订单查询 | 双向查询、详情展示 | (十二) |
8.2 技术栈
- 开发语言:ArkTS
- UI框架:ArkUI
- 数据库:关系型数据库(RDB)
- 状态管理:@State、@Prop、@Link
- 路由导航:router模块
- 图片处理:Image组件、picker模块
8.3 项目亮点
✅ 角色分离:主厨和食客功能清晰分离
✅ 数据完整:菜谱、订单、相册数据完整管理
✅ 交互友好:卡片式布局,操作便捷
✅ 状态清晰:订单状态流转明确
✅ 权限控制:根据角色显示不同功能
更多推荐


所有评论(0)