HarmonyOS ArkTS 实战:实现一个校园体育场馆预约应用

HarmonyOS ArkTS 实战:校园体育场馆预约完整实现

摘要

项目难度:⭐⭐⭐ 中级实战

适用人群:有 ArkTS 基础、希望学习预约类应用开发的 HarmonyOS 开发者

开发环境:DevEco Studio Next / API 24+

代码规模:单页面约 700 行,可直接运行

通过本项目,你将掌握:

  • 时间段选择与场地资源的冲突检测
  • 场馆预约的完整状态流转管理
  • 在线支付与退款的业务逻辑设计
  • 多类型场馆的分类展示与筛选
  • 运动数据统计与可视化展示

目录

  • 一、项目概述与功能架构

    • 1.1 应用场景与项目价值

    • 1.2 完整功能清单

    • 1.3 技术选型说明

  • 二、数据结构设计

    • 2.1 场馆模型 Venue

    • 2.2 预约记录模型 Booking

    • 2.3 设计思路说明

  • 三、核心功能实现详解

    • 3.1 场馆浏览与筛选

    • 3.2 时间段选择与预约

    • 3.3 取消预约与退款规则

    • 3.4 签到入场与使用记录

  • 四、UI 设计与交互体验

    • 4.1 整体布局结构

    • 4.2 主题色与视觉规范

    • 4.3 状态视觉区分表

  • 五、关键技术点解析

    • 5.1 时间段资源的冲突检测算法

    • 5.2 退款规则的阶梯式设计

  • 六、常见问题与优化建议

  • 七、项目总结与扩展方向

  • 附录:运行方式


一、项目概述与功能架构

1.1 应用场景与项目价值

运动是校园生活的重要组成部分,羽毛球、篮球、网球、游泳、乒乓球等体育运动深受学生喜爱。然而热门场馆常常"一场难求",线下预约不方便,时间冲突难协调,运动记录难追踪,一直是校园体育生活的痛点。

本项目基于 HarmonyOS ArkTS 实现了一个校园体育场馆预约应用,整合了场馆浏览、场地预约、时间选择、在线支付、预约记录、签到入场等功能,实现校园体育场馆的数字化预约管理。

项目核心价值:

  1. 便利价值:随时随地查看场馆空闲情况并预约,不用跑线下
  2. 公平价值:线上公开透明预约,避免人情场、内部场
  3. 数据价值:运动数据自动统计,帮助学生养成运动习惯

1.2 完整功能清单

功能模块 具体功能 实现难度⭐
场馆浏览 场馆列表、类型筛选、详情查看、开放时间 ⭐⭐
场地预约 日期选择、时段选择、场地分配、预约确认 ⭐⭐⭐
支付系统 余额支付、在线支付、订单详情、支付记录 ⭐⭐⭐
预约管理 我的预约、取消预约、退款申请、预约提醒 ⭐⭐⭐
签到入场 扫码签到、核销入场、使用记录、时长统计 ⭐⭐
数据统计 运动时长、运动次数、消费统计、运动排行 ⭐⭐

1.3 技术选型说明

技术维度 选型方案 选型理由
开发语言 ArkTS 强类型支持,适合时间计算等精确业务逻辑
状态管理 @State 装饰器 轻量响应式,满足预约状态和场馆数据更新
时间选择 自定义时段组件 灵活展示可用/已预约时间段
数据存储 内存状态 演示简化,生产环境需对接场馆管理后台
最低版本 API 24 兼容主流 HarmonyOS 设备

⚠️ 注意:本项目为前端演示版本,场地库存、支付等均为本地模拟。真实场馆预约系统需要对接学校体育部后台,确保数据准确和资金安全。


二、数据结构设计

2.1 场馆模型 Venue

场馆是预约系统的核心资源,包含基本信息和场地数量。

typescript 复制代码
/**
 * 体育场馆数据模型
 */
interface Venue {
  id: number;              // 场馆ID
  name: string;            // 场馆名称
  type: string;            // 运动类型:羽毛球/篮球/网球/游泳/乒乓球
  location: string;        // 场馆位置
  price: number;           // 单价(元/小时)
  openTime: string;        // 开放时间
  image: string;           // 场馆图片
  totalCourts: number;     // 总场地数
  availableCourts: number; // 当前可用场地数
}

2.2 预约记录模型 Booking

预约记录追踪用户的每一次场地预约,包含完整的时间和状态信息。

typescript 复制代码
/**
 * 场馆预约记录数据模型
 */
interface Booking {
  id: number;         // 预约ID
  venueId: number;    // 场馆ID
  venueName: string;  // 场馆名称
  courtNo: string;    // 场地号
  date: string;       // 预约日期
  startTime: string;  // 开始时间
  endTime: string;    // 结束时间
  price: number;      // 预约费用
  status: string;     // 预约状态:已预约/已取消/已使用/已过期
  bookTime: string;   // 下单时间
}

2.3 设计思路说明

为什么场馆和场地分开两个层级?

这是典型的一对多资源结构:

  1. 场馆是大类:如"羽毛球馆",包含多个场地
  2. 场地是具体资源:如"1号场""2号场",是实际预约的单位
  3. 展示层面:用户浏览时按场馆分类,不用关心具体几号场
  4. 预约层面:系统分配具体场地号,用户到场后按号使用

这种设计既简化了用户的选择,又保证了资源管理的精确性。

关于预约状态的四态设计

已预约 → 已使用 / 已取消 / 已过期,覆盖了预约的所有可能结果:

  • 已预约:下单成功,等待使用
  • 已使用:正常到场使用完毕
  • 已取消:用户主动取消预约
  • 已过期:过了时间没使用也没取消

四种状态覆盖了完整的预约生命周期。


三、核心功能实现详解

3.1 页面状态初始化

typescript 复制代码
/**
 * 页面状态变量定义
 */
@State private selectedDate: string = '2026-07-19';  // 选择的日期
@State private selectedTime: string = '14:00-15:00';  // 选择的时段
@State private nextBookId: number = 10;                // 下一个预约ID
@State private balance: number = 100;                  // 账户余额
@State private venues: Venue[] = [];                   // 场馆列表
@State private bookings: Booking[] = [];               // 预约记录

3.2 场馆浏览与筛选

typescript 复制代码
/**
 * 按运动类型筛选场馆
 * @param type 运动类型
 */
private filterVenuesByType(type: string): Venue[] {
  if (type === '全部') {
    return this.venues;
  }
  return this.venues.filter(v => v.type === type);
}

/**
 * 获取可用场馆数量
 */
private getAvailableVenuesCount(): number {
  return this.venues.filter(v => v.availableCourts > 0).length;
}

3.3 预约场地

选择日期和时段后提交预约,系统分配场地号并扣费。

typescript 复制代码
/**
 * 预约场地
 * @param venueId 场馆ID
 * @param venueName 场馆名称
 * @param price 单价
 */
private bookCourt(venueId: number, venueName: string, price: number): void {
  // 1. 余额校验
  if (this.balance < price) {
    // 生产环境提示余额不足
    return;
  }
  
  // 2. 场地可用性校验(简化处理)
  const venue = this.venues.find(v => v.id === venueId);
  if (!venue || venue.availableCourts <= 0) {
    return;
  }
  
  // 3. 随机分配场地号
  const courtNo = String(Math.floor(Math.random() * 8) + 1) + '号场';
  
  // 4. 解析时间段
  const [startTime, endTime] = this.selectedTime.split('-');
  
  // 5. 创建预约记录
  const booking: Booking = {
    id: this.nextBookId,
    venueId: venueId,
    venueName: venueName,
    courtNo: courtNo,
    date: this.selectedDate,
    startTime: startTime,
    endTime: endTime,
    price: price,
    status: '已预约',
    bookTime: new Date().toLocaleString()
  };
  
  // 6. 更新数据
  this.bookings = [booking, ...this.bookings];
  this.balance -= price;
  this.nextBookId++;
  
  // 7. 减少可用场地数
  this.venues = this.venues.map(v => 
    v.id === venueId ? { ...v, availableCourts: v.availableCourts - 1 } : v
  );
}

设计要点解析:

  • 前置校验:余额、场地可用性都要先校验再执行
  • 自动分配场地:用户不用选具体几号场,系统自动分配,简化操作
  • 实时扣减库存:预约成功后立即减少可用场地数,防止超卖
  • 时间解析:时间段字符串解析为开始和结束时间,便于后续计算

3.4 取消预约与退款

取消预约按规则退还部分费用。

typescript 复制代码
/**
 * 取消预约
 * 退款规则:提前24小时以上退80%,24小时内不退
 * @param bookId 预约ID
 * @param price 预约金额
 */
private cancelBooking(bookId: number, price: number): void {
  // 1. 查找预约记录
  const booking = this.bookings.find(b => b.id === bookId);
  if (!booking || booking.status !== '已预约') {
    return;
  }
  
  // 2. 更新预约状态
  this.bookings = this.bookings.map(b => 
    b.id === bookId ? { ...b, status: '已取消' } : b
  );
  
  // 3. 退款:简化处理,退80%
  const refund = Math.round(price * 0.8);
  this.balance += refund;
  
  // 4. 恢复场地可用性
  const venueId = booking.venueId;
  this.venues = this.venues.map(v => 
    v.id === venueId ? { ...v, availableCourts: v.availableCourts + 1 } : v
  );
}

3.5 签到入场

typescript 复制代码
/**
 * 签到入场
 * @param bookId 预约ID
 */
private checkIn(bookId: number): void {
  this.bookings = this.bookings.map(b => 
    b.id === bookId ? { ...b, status: '已使用' } : b
  );
}

/**
 * 获取今日预约数量
 */
private getTodayBookingsCount(): number {
  const today = new Date().toISOString().split('T')[0];
  return this.bookings.filter(b => b.date === today && b.status === '已预约').length;
}

四、UI 设计与交互体验

4.1 整体布局结构

复制代码
┌─────────────────────────┐
│     运动数据统计         │  ← 可用场馆/今日预约/本周时长/累计消费
├─────────────────────────┤
│     运动类型标签栏       │  ← 羽毛球/篮球/网球/游泳/乒乓球
├─────────────────────────┤
│     Tab 切换栏           │  ← 场馆预约 / 我的预约
├─────────────────────────┤
│                         │
│     内容展示区           │  ← 场馆卡片 / 预约记录
│                         │
└─────────────────────────┘

4.2 主题色与视觉规范

主色调:翡翠绿 #059669

选择翡翠绿作为主题色的设计考量:

  1. 运动与健康象征:绿色是运动、健康、活力的代表色,与体育运动的"健康活力"属性高度契合,传递积极向上的运动精神
  2. 清新自然感:翡翠绿的清新感令人联想到户外运动和大自然,营造轻松愉悦的运动氛围
  3. 状态色兼容性好:翡翠绿与红色(已满)、琥珀色(紧张)、灰色(不可用)等状态色都能形成清晰对比
  4. 视觉舒适度高:中等饱和度的绿色长时间观看不刺眼,适合频繁使用的工具类应用

辅助色规范:

  • 主色调:#059669(翡翠绿)
  • 可预约状态:#059669(绿色可选)
  • 已预约状态:#9CA3AF(灰色已占)
  • 名额紧张:#F59E0B(琥珀色警告)
  • 已取消状态:#9CA3AF(灰色)
  • 已使用状态:#6B7280(深灰)

4.3 状态视觉区分表

场馆状态 标识颜色 预约按钮 视觉提示
场地充足 绿色 绿色实心按钮 充足
名额紧张 琥珀色 琥珀色按钮 仅剩N场
已满员 灰色 灰色禁用按钮 已满
未开放 灰色蒙版 不可点击 未开放
预约状态 标签颜色 可执行操作 退款规则
已预约 绿色实心 取消预约 提前24h退80%
已使用 灰色实心 查看详情 不退款
已取消 灰色空心 重新预约 已退款80%
已过期 红色实心 不可操作 不退款

五、关键技术点解析

5.1 时间段资源的冲突检测算法

场馆预约的核心是时间段资源管理,必须确保同一时间段不被重复预约。

冲突检测原理:

两个时间段 s1, e1s2, e2 冲突的条件:

复制代码
s1 < e2 且 s2 < e1

也就是两个时间段有重叠就算冲突,包含以下几种情况:

  1. 完全重叠:A包含B或B包含A
  2. 部分重叠:A的结束在B中间,或B的结束在A中间
  3. 边界相邻:A结束等于B开始,不算冲突,可以无缝衔接

时间段粒度选择:

  • 30分钟:粒度细,灵活度高,但选择麻烦
  • 60分钟:最常用,整点开始,符合习惯
  • 90分钟:适合篮球等长时段运动
  • 120分钟:适合包场

校园场景一般以1小时为基本单位,支持连续多选。

5.2 退款规则的阶梯式设计

取消预约的退款规则是场馆运营的重要部分,既要保障用户权益,也要避免恶意占场。

常见的阶梯退款规则:

取消时间 退款比例 说明
提前48小时以上 100%全额退 充分提前,场地容易重新租出
提前24-48小时 80%退款 较提前,大部分可重新安排
提前12-24小时 50%退款 时间较近,重新租出有难度
提前12小时以内 不退费 临时取消,场地基本空耗

设计考量:

  1. 越晚取消退越少:鼓励用户尽早决定,给场馆留出重新出租的时间
  2. 设置违约记录:频繁临时取消的用户可以计入黑名单
  3. 特殊情况豁免:如恶劣天气、场馆故障等特殊情况全额退款

六、常见问题与优化建议

踩坑点 1:预约时间冲突

问题表现 :两人同时预约同一时段,都显示预约成功

解决方案:使用数据库乐观锁或分布式锁,确保同一时段不超卖

踩坑点 2:退款金额计算错误

问题表现 :边界时间判断不准确,退款比例算错

解决方案:使用时间戳精确计算间隔,明确边界条件归属,单元测试覆盖所有边界

踩坑点 3:开放时间设置混乱

问题表现 :节假日、寒暑假开放时间不同,用户白跑一趟

解决方案:支持特殊日期排班设置,节假日前公告通知,预约时展示当天开放时间

踩坑点 4:爽约用户无约束

问题表现 :部分用户预约了不来,浪费场地资源

解决方案:建立爽约记录机制,累计多次爽约限制预约权限,引导用户按时使用

优化方向

  1. 增加约球组队功能,发布找人信息一起运动
  2. 实现教练预约,一对一专业指导
  3. 接入赛事报名功能,组织校内联赛
  4. 增加运动数据同步,对接手环记录运动数据

七、项目总结与扩展方向

核心收获

通过完成体育场馆预约项目,你将掌握:

  • 时间段资源预约的冲突检测算法
  • 预约订单的完整状态流转管理
  • 退款规则的阶梯式业务设计
  • 运动健康类应用的视觉设计

后续扩展功能

  • 👥 约球组队:发布找人信息,匹配球友一起运动
  • 👨‍🏫 教练预约:专业教练一对一指导,提升技术水平
  • 🏆 赛事报名:校内联赛报名,组织竞技比赛
  • 运动数据:对接智能手环,同步运动数据
  • 📍 场馆导航:地图导航到场馆,查看场馆设施
  • 📊 运动排行:运动时长排行榜,激励坚持锻炼

运行效果

相关推荐
网络工程小王2 小时前
【HCIE-AI】11.模型 昇腾迁移适配-精度调试-性能调优
人工智能·学习·华为·迁移学习·昇腾
xianjixiance_2 小时前
HarmonyOS开发实战:小分享-ArkUI 基础组件详解——Text、Button、Image
华为·harmonyos
不如语冰2 小时前
AI大模型入门-Python进阶-上下文管理与with语句
开发语言·数据结构·数据库·人工智能·pytorch·redis·python
ov二号3 小时前
鸿蒙原生ArkTS布局方式之侧边栏SideBarContainer布局深度指南
华为·harmonyos
echohelloworld113 小时前
HarmonyOS开发实战:笔友-EmptyState 空状态组件的条件式占位设计
harmonyos·鸿蒙
qizayaoshuap3 小时前
# 倒计时器 — HarmonyOS TextInput与计时任务管理深入实践
pytorch·深度学习·华为·harmonyos
木木子224 小时前
# 猜数字游戏 — HarmonyOS交互逻辑与随机算法实现
算法·游戏·华为·交互·harmonyos
爱写代码的阿木4 小时前
基于鸿蒙OS开发附近社交游戏平台(十二)-狼人杀角色技能与投票系统
游戏·华为·harmonyos
程序员黑豆5 小时前
鸿蒙开发:RelativeContainer相对布局组件实战——实现5个文本组件对齐
前端·华为·harmonyos