HarmonyOS ArkTS 实战:实现一个校园打印复印预约应用
项目效果
本文使用 HarmonyOS 和 ArkTS 实现一个校园打印复印预约应用。
应用可以上传文件,选择打印参数,选择打印店,预约取件时间,在线支付,并提供打印状态跟踪、取件码和消费统计等功能。
项目使用 DevEco Studio 开发,适配 API 23 及以上版本。
运行效果

功能介绍
本项目实现了以下功能:
- 上传打印文件
- 选择打印类型(黑白/彩色)
- 选择单双面
- 设置打印份数
- 选择打印店
- 预约取件时间
- 费用计算
- 在线支付
- 打印状态跟踪
- 取件码生成
- 按状态筛选订单
- 消费统计
定义数据结构
首先定义打印订单的数据结构:
typescript
interface PrintOrder {
id: number;
fileName: string;
fileType: string;
printType: string;
duplex: string;
copies: number;
pages: number;
shopName: string;
pickupTime: string;
price: number;
status: string;
pickupCode: string;
remark: string;
submitTime: string;
payTime: string;
completeTime: string;
}
字段说明如下:
PrintOrder:打印订单id:订单编号fileName:文件名fileType:文件类型(文档/图片/PPT/PDF)printType:打印类型(黑白/彩色)duplex:单双面(单面/双面)copies:份数pages:页数shopName:打印店pickupTime:取件时间price:价格status:状态(待支付/打印中/待取件/已完成/已取消)pickupCode:取件码remark:备注submitTime:提交时间payTime:支付时间completeTime:完成时间
初始化页面状态
使用 @State 保存打印参数、选择项:
typescript
@State private fileNameText: string = '';
@State private copiesText: string = '1';
@State private pagesText: string = '';
@State private pickupTimeText: string = '';
@State private remarkText: string = '';
@State private selectedFileType: string = 'PDF';
@State private selectedPrintType: string = '黑白';
@State private selectedDuplex: string = '单面';
@State private selectedShop: string = '图书馆打印店';
@State private filterStatus: string = '全部';
@State private nextOrderId: number = 5;
准备一些初始订单数据:
typescript
@State private orders: PrintOrder[] = [
{
id: 1,
fileName: '毕业论文初稿.pdf',
fileType: 'PDF',
printType: '黑白',
duplex: '双面',
copies: 2,
pages: 30,
shopName: '图书馆打印店',
pickupTime: '2026-07-16 15:00',
price: 6,
status: '待取件',
pickupCode: 'A302',
remark: '装订成册',
submitTime: '2026-07-15 10:30',
payTime: '2026-07-15 10:32',
completeTime: ''
},
{
id: 2,
fileName: '竞赛海报.png',
fileType: '图片',
printType: '彩色',
duplex: '单面',
copies: 5,
pages: 1,
shopName: '东门图文店',
pickupTime: '2026-07-15 14:00',
price: 25,
status: '已完成',
pickupCode: 'B105',
remark: '铜版纸打印',
submitTime: '2026-07-14 16:20',
payTime: '2026-07-14 16:22',
completeTime: '2026-07-15 13:45'
},
{
id: 3,
fileName: '复习资料.docx',
fileType: '文档',
printType: '黑白',
duplex: '双面',
copies: 1,
pages: 20,
shopName: '宿舍楼下打印店',
pickupTime: '2026-07-18 10:00',
price: 2,
status: '待支付',
pickupCode: '',
remark: '',
submitTime: '2026-07-17 20:15',
payTime: '',
completeTime: ''
}
];
计算价格
根据打印参数自动计算价格:
typescript
private calculatePrice(): number {
const pages = parseInt(this.pagesText) || 0;
const copies = parseInt(this.copiesText) || 1;
let pricePerPage = 0.1; // 黑白单面
if (this.selectedPrintType === '彩色' && this.selectedDuplex === '单面') {
pricePerPage = 1;
} else if (this.selectedPrintType === '彩色' && this.selectedDuplex === '双面') {
pricePerPage = 0.8;
} else if (this.selectedPrintType === '黑白' && this.selectedDuplex === '双面') {
pricePerPage = 0.08;
}
const totalPages = pages * copies;
let total = totalPages * pricePerPage;
if (this.selectedShop === '东门图文店') {
total += 1; // 服务费
}
return Math.round(total * 10) / 10;
}
价格规则:
- 黑白单面:0.1元/页
- 黑白双面:0.08元/页
- 彩色单面:1元/页
- 彩色双面:0.8元/页
- 东门图文店加收1元服务费
提交订单
用户提交打印订单:
typescript
private submitOrder(): void {
const fileName = this.fileNameText.trim();
const pages = parseInt(this.pagesText);
const copies = parseInt(this.copiesText);
const pickupTime = this.pickupTimeText.trim();
if (fileName.length === 0 || isNaN(pages) || isNaN(copies) || pickupTime.length === 0) {
return;
}
const now = new Date();
const submitTime = `${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 code = String.fromCharCode(65 + Math.floor(Math.random() * 3)) +
String(Math.floor(Math.random() * 900) + 100);
const order: PrintOrder = {
id: this.nextOrderId,
fileName,
fileType: this.selectedFileType,
printType: this.selectedPrintType,
duplex: this.selectedDuplex,
copies,
pages,
shopName: this.selectedShop,
pickupTime,
price: this.calculatePrice(),
status: '待支付',
pickupCode: '',
remark: this.remarkText.trim(),
submitTime,
payTime: '',
completeTime: ''
};
this.orders = [order, ...this.orders];
this.nextOrderId += 1;
this.fileNameText = '';
this.pagesText = '';
this.copiesText = '1';
this.pickupTimeText = '';
this.remarkText = '';
}
支付和状态更新
typescript
private payOrder(orderId: number): void {
const now = new Date();
const payTime = `${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 code = String.fromCharCode(65 + Math.floor(Math.random() * 3)) +
String(Math.floor(Math.random() * 900) + 100);
this.orders = this.orders.map((order: PrintOrder) => {
if (order.id === orderId && order.status === '待支付') {
return {
id: order.id,
fileName: order.fileName,
fileType: order.fileType,
printType: order.printType,
duplex: order.duplex,
copies: order.copies,
pages: order.pages,
shopName: order.shopName,
pickupTime: order.pickupTime,
price: order.price,
status: '打印中',
pickupCode: code,
remark: order.remark,
submitTime: order.submitTime,
payTime,
completeTime: ''
};
}
return order;
});
}
private completeOrder(orderId: number): void {
const now = new Date();
const completeTime = `${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')}`;
this.orders = this.orders.map((order: PrintOrder) => {
if (order.id === orderId && order.status === '待取件') {
return {
id: order.id,
fileName: order.fileName,
fileType: order.fileType,
printType: order.printType,
duplex: order.duplex,
copies: order.copies,
pages: order.pages,
shopName: order.shopName,
pickupTime: order.pickupTime,
price: order.price,
status: '已完成',
pickupCode: order.pickupCode,
remark: order.remark,
submitTime: order.submitTime,
payTime: order.payTime,
completeTime
};
}
return order;
});
}
打印店自动更新状态:待支付 -> 打印中 -> 待取件 -> 已完成
取消订单
typescript
private cancelOrder(orderId: number): void {
this.orders = this.orders.map((order: PrintOrder) => {
if (order.id === orderId && order.status === '待支付') {
return {
id: order.id,
fileName: order.fileName,
fileType: order.fileType,
printType: order.printType,
duplex: order.duplex,
copies: order.copies,
pages: order.pages,
shopName: order.shopName,
pickupTime: order.pickupTime,
price: order.price,
status: '已取消',
pickupCode: '',
remark: order.remark,
submitTime: order.submitTime,
payTime: '',
completeTime: ''
};
}
return order;
});
}
筛选订单
typescript
private getFilteredOrders(): PrintOrder[] {
if (this.filterStatus === '全部') {
return this.orders;
}
return this.orders.filter(o => o.status === this.filterStatus);
}
打印类型按钮封装:
typescript
@Builder
PrintTypeButton(text: string) {
Button(text)
.height(32)
.padding({ left: 14, right: 14 })
.fontSize(12)
.fontColor(this.selectedPrintType === text ? Color.White : '#344054')
.backgroundColor(
this.selectedPrintType === text ? '#06B6D4' : '#CFFAFE'
)
.borderRadius(16)
.onClick(() => {
this.selectedPrintType = text;
});
}
统计数据
typescript
private getPrintingCount(): number {
return this.orders.filter(o => o.status === '打印中' || o.status === '待取件').length;
}
private getCompletedCount(): number {
return this.orders.filter(o => o.status === '已完成').length;
}
private getTotalSpent(): number {
return this.orders
.filter(o => o.status !== '待支付' && o.status !== '已取消')
.reduce((sum, o) => sum + o.price, 0);
}
private getTotalPages(): number {
return this.orders
.filter(o => o.status === '已完成')
.reduce((sum, o) => sum + o.pages * o.copies, 0);
}
顶部统计区域展示打印中、已完成、总消费、总页数:
typescript
this.StatCard(
'打印中',
`${this.getPrintingCount()}`,
'#06B6D4',
'#ECFEFF'
);
this.StatCard(
'已完成',
`${this.getCompletedCount()}`,
'#059669',
'#ECFDF5'
);
this.StatCard(
'总消费',
`¥${this.getTotalSpent().toFixed(1)}`,
'#D97706',
'#FFF7E8'
);
this.StatCard(
'总页数',
`${this.getTotalPages()}页`,
'#2563EB',
'#EFF6FF'
);
设置状态颜色
typescript
private getStatusColor(status: string): ResourceColor {
if (status === '待支付') return '#DC2626';
if (status === '打印中') return '#D97706';
if (status === '待取件') return '#06B6D4';
if (status === '已完成') return '#059669';
return '#6B7280';
}
private getStatusBgColor(status: string): ResourceColor {
if (status === '待支付') return '#FEE2E2';
if (status === '打印中') return '#FEF3C7';
if (status === '待取件') return '#CFFAFE';
if (status === '已完成') return '#DCFCE7';
return '#F3F4F6';
}
- 红色:待支付
- 橙色:打印中
- 青色:待取件
- 绿色:已完成
- 灰色:已取消
- 青色:页面主题色
页面设计说明
应用使用青色作为主题色,体现高效、便捷、科技的感觉。
页面主要分为以下区域:
- 顶部标题
- 数据统计区域
- 提交打印表单
- 打印参数选择
- 价格实时计算
- 状态筛选
- 订单列表
- 取件码弹窗
页面采用浅灰色背景和白色卡片。待取件订单突出显示取件码(大号字体),打印参数使用按钮组选择,价格使用大号绿色字体实时显示,文件类型使用图标区分。
SDK 配置
本项目使用 HarmonyOS API 24,满足 API 23 及以上要求:
json5
{
"name": "default",
"compatibleSdkVersion": "6.1.1(24)",
"runtimeOS": "HarmonyOS",
"targetSdkVersion": "6.1.1(24)",
"compileSdkVersion": "6.1.1(24)"
}
运行项目
使用 DevEco Studio 打开项目,然后找到:
entry/src/main/ets/pages/Index.ets
等待项目同步完成,点击右侧的 Preview 按钮即可查看应用效果。
项目总结
本文使用 HarmonyOS 和 ArkTS 实现了一个校园打印复印预约应用。
项目实现了文件上传、打印参数选择、价格计算、在线支付、状态跟踪、取件码生成、订单筛选、消费统计等功能。
通过这个项目可以掌握:
- ArkTS 接口定义
@State状态管理- 价格实时计算
- 订单状态流转
List和ForEach列表渲染- 取件码随机生成
- 自定义
@Builder组件 - HarmonyOS 生活服务类应用布局
后续还可以加入文件在线预览、打印进度实时推送、配送上门、发票申请、会员折扣、打印模板、多文件合并打印和打印店评价等功能。