订单模块全量代码与效果:ArkTS 多表查询在 HarmonyOS 落地
实例:订单与明细(Order)|收官文章
一、文件清单
| 文件 | 职责 | 行数 |
|---|---|---|
database/OrderDao.ets | 数据层:双表建表、事务下单、JOIN 查询、状态流转、种子 | 约 370 行 |
pages/samples/OrderPage.ets | UI 层:主从嵌套列表 + 状态筛选 + 流转按钮 | 约 190 行 |
resources/base/profile/main_pages.json | 路由注册:pages/samples/OrderPage | 追加一行 |
pages/Index.ets | 首页入口按钮 | 追加一个按钮 |
本篇文章完整展示可编译运行的 OrderPage 代码,最后描述运行效果。
二、OrderPage 完整代码
import{common}from'@kit.AbilityKit';import{promptAction}from'@kit.ArkUI';import{OrderDao,OrderWithItems,OrderItem}from'../../database/OrderDao';@Entry@Componentstruct OrderPage{@Stateorders:OrderWithItems[]=[];@StatestatusFilter:number=-1;// -1 全部@Statestats:Record<string,number>={};privatecontext:common.UIAbilityContext=getContext(this)ascommon.UIAbilityContext;privatereadonlystatusNames:string[]=['待支付','已支付','已发货','已完成','已取消'];privatereadonlystatusColors:string[]=['#F59E0B','#3B82F6','#8B5CF6','#059669','#9CA3AF'];aboutToAppear():void{this.refresh();}asyncrefresh():Promise<void>{try{awaitOrderDao.initSeedData(this.context);this.stats=awaitOrderDao.statusStats(this.context);this.orders=awaitOrderDao.queryWithItems(this.context,this.statusFilter===-1?undefined:this.statusFilter);}catch(e){promptAction.showToast({message:`加载失败:${e}`});}}asyncswitchFilter(f:number):Promise<void>{this.statusFilter=f;awaitthis.refresh();}/** 状态流转:待支付 → 已支付 → 已发货 → 已完成 */nextStatus(orderId:number,status:number):void{if(status>=3){return;}constnext=status+1;OrderDao.updateStatus(this.context,orderId,next).then(async()=>{awaitthis.refresh();promptAction.showToast({message:`状态已更新为「${this.statusNames[next]}」`});});}cancelOrder(orderId:number):void{promptAction.showDialog({title:'取消订单',message:'确定取消该订单吗?',buttons:[{text:'取消',color:'#808080'},{text:'确认取消',color:'#EF4444'},],}).then((res:promptAction.ShowDialogSuccessResponse)=>{if(res.index===1){OrderDao.updateStatus(this.context,orderId,4).then(async()=>{awaitthis.refresh();promptAction.showToast({message:'❌ 订单已取消'});});}});}deleteOrder(id:number):void{promptAction.showDialog({title:'删除订单',message:'删除订单及全部明细,确定吗?',buttons:[{text:'取消',color:'#808080'},{text:'删除',color:'#EF4444'},],}).then((res:promptAction.ShowDialogSuccessResponse)=>{if(res.index===1){OrderDao.deleteOrder(this.context,id).then(async()=>{awaitthis.refresh();promptAction.showToast({message:'🗑 已删除'});});}});}privatefmtTime(ts:number):string{constd=newDate(ts);return`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;}build(){Column(){// ===== 标题栏 =====Row(){Column(){Text('🧾 订单与明细').fontSize(22).fontWeight(FontWeight.Bold)Text('外键一对多 · JOIN 联表查询').fontSize(11).fontColor('#999999').margin({top:2})}.alignItems(HorizontalAlign.Start).layoutWeight(1)Text('↻').fontSize(22).onClick(()=>this.refresh())}.width('100%').padding({left:16,right:16,top:12})// ===== 状态统计条 =====Scroll(){Row({space:8}){Text(`全部${this.totalOrders()}`).fontSize(13).padding({left:12,right:12,top:6,bottom:6}).borderRadius(16).backgroundColor(this.statusFilter===-1?'#111827':'#FFFFFF').fontColor(this.statusFilter===-1?Color.White:'#4B5563').onClick(()=>this.switchFilter(-1))ForEach(this.statusNames,(name:string,idx:number)=>{Text(`${name}${this.stats[String(idx)]??0}`).fontSize(13).padding({left:12,right:12,top:6,bottom:6}).borderRadius(16).backgroundColor(this.statusFilter===idx?this.statusColors[idx]:'#FFFFFF').fontColor(this.statusFilter===idx?Color.White:'#4B5563').onClick(()=>this.switchFilter(idx))},(name:string,idx:number)=>`${idx}-${name}`)}.padding({left:16,right:16,top:10})}.scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off).width('100%')// ===== 订单列表(主从嵌套) =====List({space:10}){ForEach(this.orders,(o:OrderWithItems)=>{ListItem(){Column(){// 订单头Row(){Column({space:2}){Text(`订单号${o.order.orderNo}`).fontSize(14).fontWeight(FontWeight.Bold)Text(`${o.order.customer}·${this.fmtTime(o.order.createdTime)}`).fontSize(11).fontColor('#999999')}.alignItems(HorizontalAlign.Start).layoutWeight(1)Text(this.statusNames[o.order.status]).fontSize(11).padding({left:8,right:8,top:3,bottom:3}).borderRadius(10).backgroundColor(this.statusColors[o.order.status]).fontColor(Color.White)}.width('100%')// 明细列表Column(){ForEach(o.items,(it:OrderItem)=>{Row({space:8}){Text(it.productName).fontSize(13).layoutWeight(1)Text(`¥${it.price.toFixed(2)}×${it.quantity}`).fontSize(12).fontColor('#6B7280')Text(`¥${it.subtotal.toFixed(2)}`).fontSize(13).fontWeight(FontWeight.Medium)}.width('100%').padding({top:5,bottom:5})},(it:OrderItem)=>`${it.id}-${it.productName}`)}.width('100%').padding(10).backgroundColor('#F8FAFC').borderRadius(8).margin({top:8})// 订单尾Row(){Text(`共${o.items.length}件商品`).fontSize(12).fontColor('#6B7280')Blank()Text(`合计 ¥${o.order.totalAmount.toFixed(2)}`).fontSize(16).fontWeight(FontWeight.Bold).fontColor('#DC2626')}.width('100%').margin({top:8})// 操作按钮Row({space:8}){if(o.order.status===0){Button('取消订单').layoutWeight(1).height(32).backgroundColor('#EEF2F7').fontColor('#EF4444').fontSize(13).onClick(()=>this.cancelOrder(o.order.id))}if(o.order.status<3){Button(`→${this.statusNames[o.order.status+1]}`).layoutWeight(1).height(32).backgroundColor('#3B82F6').fontSize(13).onClick(()=>this.nextStatus(o.order.id,o.order.status))}Button('删除').height(32).backgroundColor('#FEE2E2').fontColor('#EF4444').fontSize(13).onClick(()=>this.deleteOrder(o.order.id))}.width('100%').margin({top:10})}.padding(14).backgroundColor(Color.White).borderRadius(12)}},(o:OrderWithItems)=>`${o.order.id}-${o.order.orderNo}`)}.width('94%').layoutWeight(1).margin({top:10}).scrollBar(BarState.Off)}.width('100%').height('100%').backgroundColor('#F1F4F8')}privatetotalOrders():number{letsum=0;Object.keys(this.stats).forEach((k:string)=>{sum+=this.stats[k];});returnsum;}}三、注册与运行
main_pages.json追加"pages/samples/OrderPage";Index.ets追加:Button('🧾 9 订单与明细').fontSize(15).width('70%').onClick(()=>this.getUIContext().getRouter().pushUrl({url:'pages/samples/OrderPage'}))- 构建验证 →
BUILD SUCCESSFUL。
四、运行效果描述
进入「🧾 9 订单与明细」:
第一屏:标题栏「🧾 订单与明细 · 外键一对多 · JOIN 联表查询」;状态统计条「全部 6 · 待支付 1 · 已支付 1 · 已发货 1 · 已完成 2 · 已取消 1」;下方 6 张订单卡——每张依次是订单头(单号 + 收货人 + 彩色状态标签)→ 浅灰明细区(1~3 行商品:名称 + 单价×数量 + 小计)→ 订单尾(共 N 件 + 红色合计)→ 操作按钮行。
交互一(状态流转):点订单 1(待支付)的「→ 已支付」→ Toast「状态已更新为「已支付」」→ 标签变蓝、统计条刷新(待支付 0 / 已支付 2)→ 再点「→ 已发货」「→ 已完成」推进到底。
交互二(取消订单):对任意待支付订单点「取消订单」→ 确认框 → 状态变灰「已取消」,统计条同步。
交互三(状态筛选):点「已完成 2」→ 列表只剩 2 张绿色标签的订单 → 点「全部」恢复 6 张。
交互四(删除):点「删除」→ 确认框(提示删除明细)→ 订单与明细一起消失。
五、代码质量要点回顾
| 关注点 | 本实例做法 |
|---|---|
| 一对多 | orders + order_item + 聚合体组装 |
| 事务下单 | 先插主表 + 循环插从表 + commit |
| JOIN 查询 | LEFT JOIN + map 归组 |
| 状态机 | 页面守卫 status < 3,动态按钮 |
| 状态统计 | GROUP BY status |
| 破坏确认 | showDialog + index === 1 |
六、文章小结
实例 9「订单与明细」收官。五篇文章覆盖:一对多建模(9-1)→ 主从嵌套 UI(9-2)→ 事务下单与 JOIN(9-3)→ 双表种子(9-4)→ 全量代码(9-5)。核心技术是「先主后从」的事务写入、LEFT JOIN 展平 + 内存归组的联表查询、状态机驱动的动态按钮——多表建模与联表查询的能力至此系统建立。
