HarmonyOS ArkTS 实战:实现一个校园食堂点餐与取餐应用
HarmonyOS ArkTS 实战:实现一个校园食堂点餐与取餐应用
项目效果
本文使用 HarmonyOS 和 ArkTS 实现一个校园食堂点餐与取餐应用。
应用可以浏览食堂菜品,在线点餐下单,查看取餐号和排队进度,管理购物车,并提供食堂筛选、订单状态查看和消费统计等功能。
项目使用 DevEco Studio 开发,适配 API 23 及以上版本。
运行效果

功能介绍
本项目实现了以下功能:
- 浏览各食堂菜品
- 查看菜品价格和图片描述
- 选择菜品加入购物车
- 调整购买数量
- 选择食堂和取餐窗口
- 提交订单
- 查看取餐号
- 查看排队进度
- 确认取餐完成
- 按订单状态筛选
- 统计今日订单、待取餐、已完成数量
- 统计消费金额
- 取消未支付订单
定义数据结构
首先定义菜品和订单的数据结构:
interface FoodItem {
id: number;
name: string;
price: number;
canteen: string;
window: string;
description: string;
category: string;
}
interface OrderItem {
foodId: number;
foodName: string;
quantity: number;
price: number;
}
interface FoodOrder {
id: number;
orderNumber: string;
canteen: string;
window: string;
items: OrderItem[];
totalPrice: number;
status: string;
pickupNumber: number;
currentNumber: number;
orderTime: string;
remark: string;
}
字段说明如下:
FoodItem:菜品信息id:菜品编号name:菜品名称price:价格canteen:所属食堂window:窗口description:菜品描述category:分类
FoodOrder:订单信息id:订单编号orderNumber:订单号canteen:食堂window:取餐窗口items:订单菜品列表totalPrice:总价status:订单状态(待支付/制作中/待取餐/已完成/已取消)pickupNumber:取餐号currentNumber:当前叫号orderTime:下单时间remark:备注
初始化页面状态
使用 @State 保存购物车、食堂选择、筛选条件和订单数据:
@State private selectedCanteen: string = '一食堂';
@State private selectedCategory: string = '全部';
@State private filterStatus: string = '全部';
@State private remarkText: string = '';
@State private nextOrderId: number = 5;
@State private nextPickupNumber: number = 1024;
@State private cart: Map<number, number> = new Map();
准备一些初始菜品数据:
@State private foods: FoodItem[] = [
{
id: 1,
name: '黄焖鸡米饭',
price: 15,
canteen: '一食堂',
window: '三楼3号',
description: '经典黄焖鸡,配米饭,可加辣',
category: '主食'
},
{
id: 2,
name: '番茄鸡蛋面',
price: 10,
canteen: '一食堂',
window: '二楼1号',
description: '手工面,番茄鸡蛋汤底',
category: '面食'
},
{
id: 3,
name: '麻辣香锅',
price: 18,
canteen: '二食堂',
window: '一楼5号',
description: '自选菜品,麻辣鲜香',
category: '主食'
},
{
id: 4,
name: '珍珠奶茶',
price: 8,
canteen: '二食堂',
window: '水吧',
description: '冰/热可选,三分糖正常冰',
category: '饮品'
},
{
id: 5,
name: '烤肉饭',
price: 14,
canteen: '三食堂',
window: '二楼2号',
description: '土耳其烤肉,配蔬菜沙拉',
category: '主食'
},
{
id: 6,
name: '小笼包',
price: 8,
canteen: '一食堂',
window: '一楼早餐',
description: '一笼8个,鲜肉馅',
category: '早餐'
}
];
准备一些初始订单数据:
@State private orders: FoodOrder[] = [
{
id: 1,
orderNumber: '20260717001',
canteen: '一食堂',
window: '三楼3号',
items: [{ foodId: 1, foodName: '黄焖鸡米饭', quantity: 1, price: 15 }],
totalPrice: 15,
status: '待取餐',
pickupNumber: 1021,
currentNumber: 1018,
orderTime: '2026-07-17 12:05',
remark: '微辣'
},
{
id: 2,
orderNumber: '20260717002',
canteen: '二食堂',
window: '水吧',
items: [{ foodId: 4, foodName: '珍珠奶茶', quantity: 2, price: 8 }],
totalPrice: 16,
status: '制作中',
pickupNumber: 1022,
currentNumber: 1019,
orderTime: '2026-07-17 12:10',
remark: '少冰'
},
{
id: 3,
orderNumber: '20260716003',
canteen: '三食堂',
window: '二楼2号',
items: [{ foodId: 5, foodName: '烤肉饭', quantity: 1, price: 14 }],
totalPrice: 14,
status: '已完成',
pickupNumber: 998,
currentNumber: 1023,
orderTime: '2026-07-16 18:30',
remark: ''
}
];
购物车操作
用户可以将菜品加入购物车,调整数量:
private addToCart(foodId: number): void {
const current = this.cart.get(foodId) || 0;
this.cart.set(foodId, current + 1);
this.cart = new Map(this.cart);
}
private decreaseFromCart(foodId: number): void {
const current = this.cart.get(foodId) || 0;
if (current > 1) {
this.cart.set(foodId, current - 1);
} else {
this.cart.delete(foodId);
}
this.cart = new Map(this.cart);
}
private getCartQuantity(foodId: number): number {
return this.cart.get(foodId) || 0;
}
private getCartTotal(): number {
let total = 0;
this.cart.forEach((quantity: number, foodId: number) => {
const food = this.foods.find((f: FoodItem) => f.id === foodId);
if (food) {
total += food.price * quantity;
}
});
return total;
}
private getCartCount(): number {
let count = 0;
this.cart.forEach((quantity: number) => {
count += quantity;
});
return count;
}
支持增加、减少菜品数量,自动计算总价和总数量。
提交订单
购物车确认后可以提交订单:
private submitOrder(): void {
if (this.cart.size === 0) {
return;
}
const orderItems: OrderItem[] = [];
let canteen = '';
let window = '';
this.cart.forEach((quantity: number, foodId: number) => {
const food = this.foods.find((f: FoodItem) => f.id === foodId);
if (food) {
orderItems.push({
foodId: food.id,
foodName: food.name,
quantity,
price: food.price
});
canteen = food.canteen;
window = food.window;
}
});
const now = new Date();
const timeStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
const orderNum = `20260717${String(this.nextOrderId).padStart(3, '0')}`;
const order: FoodOrder = {
id: this.nextOrderId,
orderNumber: orderNum,
canteen,
window,
items: orderItems,
totalPrice: this.getCartTotal(),
status: '制作中',
pickupNumber: this.nextPickupNumber,
currentNumber: this.nextPickupNumber - 3,
orderTime: timeStr,
remark: this.remarkText.trim()
};
this.orders = [order, ...this.orders];
this.nextOrderId += 1;
this.nextPickupNumber += 1;
this.cart = new Map();
this.remarkText = '';
}
新订单默认处于"制作中"状态,自动生成取餐号。
订单状态流转
食堂订单按照以下流程处理:
制作中 -> 待取餐 -> 已完成
|
-> 已取消
使用以下方法更新订单状态:
private setReadyForPickup(id: number): void {
this.orders = this.orders.map((order: FoodOrder) => {
if (order.id === id && order.status === '制作中') {
return {
id: order.id,
orderNumber: order.orderNumber,
canteen: order.canteen,
window: order.window,
items: order.items,
totalPrice: order.totalPrice,
status: '待取餐',
pickupNumber: order.pickupNumber,
currentNumber: order.pickupNumber,
orderTime: order.orderTime,
remark: order.remark
};
}
return order;
});
}
private completeOrder(id: number): void {
this.orders = this.orders.map((order: FoodOrder) => {
if (order.id === id && order.status === '待取餐') {
return {
id: order.id,
orderNumber: order.orderNumber,
canteen: order.canteen,
window: order.window,
items: order.items,
totalPrice: order.totalPrice,
status: '已完成',
pickupNumber: order.pickupNumber,
currentNumber: order.currentNumber,
orderTime: order.orderTime,
remark: order.remark
};
}
return order;
});
}
制作完成后变为待取餐,用户取餐后变为已完成。
模拟叫号进度
可以模拟排队叫号进度:
private updateCallingNumber(): void {
this.orders = this.orders.map((order: FoodOrder) => {
if (order.status === '制作中' && order.currentNumber < order.pickupNumber) {
return {
id: order.id,
orderNumber: order.orderNumber,
canteen: order.canteen,
window: order.window,
items: order.items,
totalPrice: order.totalPrice,
status: order.status,
pickupNumber: order.pickupNumber,
currentNumber: order.currentNumber + 1,
orderTime: order.orderTime,
remark: order.remark
};
}
return order;
});
}
每次调用当前叫号加1,模拟排队进度。
筛选菜品和订单
页面支持按食堂和分类筛选菜品,按状态筛选订单:
private getFilteredFoods(): FoodItem[] {
return this.foods.filter((food: FoodItem) => {
const canteenMatch = food.canteen === this.selectedCanteen;
const categoryMatch = this.selectedCategory === '全部' || food.category === this.selectedCategory;
return canteenMatch && categoryMatch;
});
}
private getFilteredOrders(): FoodOrder[] {
if (this.filterStatus === '全部') {
return this.orders;
}
return this.orders.filter(
(order: FoodOrder) => order.status === this.filterStatus
);
}
食堂选择按钮封装:
@Builder
CanteenButton(name: string) {
Button(name)
.height(36)
.padding({ left: 16, right: 16 })
.fontSize(14)
.fontColor(this.selectedCanteen === name ? Color.White : '#344054')
.backgroundColor(
this.selectedCanteen === name ? '#DC2626' : '#FEE2E2'
)
.borderRadius(18)
.onClick(() => {
this.selectedCanteen = name;
});
}
统计订单和消费
统计各类订单数量和总消费:
private getOrderCount(status: string): number {
if (status === '全部') {
return this.orders.length;
}
return this.orders.filter((o: FoodOrder) => o.status === status).length;
}
private getTotalSpent(): number {
return this.orders
.filter((o: FoodOrder) => o.status === '已完成')
.reduce((sum: number, o: FoodOrder) => sum + o.totalPrice, 0);
}
顶部统计区域展示今日订单、待取餐、已完成和总消费:
this.StatCard(
'今日订单',
`${this.getOrderCount('全部')}`,
'#DC2626',
'#FEF2F2'
);
this.StatCard(
'待取餐',
`${this.getOrderCount('待取餐')}`,
'#D97706',
'#FFF7E8'
);
this.StatCard(
'已完成',
`${this.getOrderCount('已完成')}`,
'#059669',
'#ECFDF5'
);
this.StatCard(
'总消费',
`¥${this.getTotalSpent()}`,
'#7C3AED',
'#F5F3FF'
);
设置状态颜色
不同订单状态使用不同颜色:
private getStatusColor(status: string): ResourceColor {
if (status === '制作中') {
return '#2563EB';
}
if (status === '待取餐') {
return '#D97706';
}
if (status === '已完成') {
return '#059669';
}
return '#6B7280';
}
private getStatusBgColor(status: string): ResourceColor {
if (status === '制作中') {
return '#DBEAFE';
}
if (status === '待取餐') {
return '#FEF3C7';
}
if (status === '已完成') {
return '#DCFCE7';
}
return '#F3F4F6';
}
颜色含义如下:
- 蓝色:制作中
- 橙色:待取餐
- 绿色:已完成
- 灰色:已取消
- 红色:页面主题色
使用网格展示菜品
使用 Grid 展示菜品列表:
Grid() {
ForEach(
this.getFilteredFoods(),
(food: FoodItem) => {
GridItem() {
this.FoodCard(food)
}
},
(food: FoodItem) => food.id.toString()
);
}
.columnsTemplate('1fr 1fr')
.rowsGap(12)
.columnsGap(12)
.width('100%')
菜品使用两列网格布局,每个菜品卡片包含名称、价格、描述和加入购物车按钮。
页面设计说明
应用使用红色作为主题色,符合餐饮类应用的视觉习惯。
页面主要分为以下区域:
- 顶部标题和食堂选择
- 订单数据统计区域
- 菜品分类和菜品网格
- 购物车悬浮栏
- 订单列表
页面采用浅灰色背景和白色卡片。菜品价格使用红色字体突出显示,订单状态使用对应颜色标签,取餐号和排队进度使用大号字体展示,便于快速查看。
SDK 配置
本项目使用 HarmonyOS API 24,满足 API 23 及以上要求:
{
"name": "default",
"compatibleSdkVersion": "6.1.1(24)",
"runtimeOS": "HarmonyOS",
"targetSdkVersion": "6.1.1(24)",
"compileSdkVersion": "6.1.1(24)"
}
entry 模块中的运行系统也要保持一致:
{
"apiType": "stageMode",
"targets": [
{
"name": "default",
"runtimeOS": "HarmonyOS"
}
]
}
运行项目
使用 DevEco Studio 打开项目,然后找到:
entry/src/main/ets/pages/Index.ets
等待项目同步完成,点击右侧的 Preview 按钮即可查看应用效果。
项目总结
本文使用 HarmonyOS 和 ArkTS 实现了一个校园食堂点餐与取餐应用。
项目实现了菜品浏览、购物车管理、数量调整、在线下单、取餐号生成、排队进度、订单状态流转、消费统计等功能。
通过这个项目可以掌握:
- ArkTS 接口定义
@State和 Map 状态管理Grid网格布局展示菜品List和ForEach列表渲染map和filter数组操作- 购物车逻辑实现
- 排队叫号模拟
- 自定义
@Builder组件 - HarmonyOS 餐饮类应用布局
后续还可以加入菜品图片、在线支付、评价系统、营养成分显示、预约点餐、拼单功能和食堂人流实时显示等功能。
更多推荐



所有评论(0)