目录
- 前情回顾与本节目标
- 第一步:数据模型准备
-
- [1.1 预约课程表(MBA_BookingCourses)](#1.1 预约课程表(MBA_BookingCourses))
- [1.2 预约记录表(MBA_Bookings)](#1.2 预约记录表(MBA_Bookings))
- 第二步:创建页面
- 第三步:页面布局
- 第四步:预约规则说明
-
- [4.1 规则展开区域](#4.1 规则展开区域)
- [4.2 规则展开收起](#4.2 规则展开收起)
- 第五步:课程列表区域
- 第六步:预约功能实现
- 最终效果
- 总结
前情回顾与本节目标
在上一节中,我们完成了教务端的预约管理功能。本节我们将实现小程序端的课程预约功能,学员可以查看可预约的课程列表,进行预约和取消预约操作。
本节核心目标:
- 课程列表展示:显示可预约的课程列表,包含课程信息和名额状态
- 预约功能:学员可以预约课程,扣减剩余名额
- 取消预约:学员可以取消已预约的课程
- 黑名单校验:黑名单学员无法预约
第一步:数据模型准备
1.1 预约课程表(MBA_BookingCourses)
| 字段名称 | 字段标识 | 字段类型 | 说明 |
|---|---|---|---|
| 课程ID | _id | 文本 | 主键,系统自动生成 |
| 关联课程 | rel_schedule_id | 多对一 | 关联 Schedules 表 |
| 课程名称 | course_name | 文本 | 课程名称 |
| 授课教师 | teacher_name | 文本 | 教师姓名 |
| 上课日期 | course_date | 日期 | 上课日期 |
| 开始时间 | start_time | 时间 | 开始时间 |
| 结束时间 | end_time | 时间 | 结束时间 |
| 总名额 | total_spots | 数字 | 总名额 |
| 剩余名额 | remaining_spots | 数字 | 剩余名额 |
| 预约状态 | status | 枚举 | 1-可预约、2-已约满、3-已结束 |
1.2 预约记录表(MBA_Bookings)
| 字段名称 | 字段标识 | 字段类型 | 说明 |
|---|---|---|---|
| 预约ID | _id | 文本 | 主键,系统自动生成 |
| 关联课程 | rel_course_id | 多对一 | 关联 BookingCourses 表 |
| 关联学员 | rel_student_id | 多对一 | 关联 StudentProfiles 表 |
| 预约状态 | status | 枚举 | 1-已预约、2-已签到、3-缺勤、4-已取消 |
| 预约时间 | book_time | 日期时间 | 预约时间 |
第二步:创建页面
点击创建页面 图标,输入"课程预约"

切换到布局设计,选择tab栏导航布局,加入菜单

第三步:页面布局
切回到页面设计,添加容器组件,设置宽、高和背景色:
css
:root {
width: 100%;
min-height: 100vh;
background-color: #F6F7F9;
}

继续添加普通容器,设置背景色、内边距和圆角:
css
:root {
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
padding: 48px 20px 24px 20px;
border-radius: 0 0 24px 24px;
}

里边添加文本组件,设置文本颜色、字号和加粗的效果:

第四步:预约规则说明
4.1 规则展开区域
添加普通容器,里边添加两个普通容器,设置外层容器的布局为横向排列,两端对齐

内层的第一个普通容器里添加图标和文本组件,设置横向排列

内层的第二个普通容器里添加两个按钮组件

然后配置背景色、圆角、内外边距

4.2 规则展开收起
创建一个自定义变量用来控制按钮的显示

给按钮配置条件展示

bash
$w.page.dataset.state.showRules
另外一个按钮进行取反

bash
!$w.page.dataset.state.showRules
给按钮配置点击事件,对变量进行赋值

添加普通容器,里边添加文本组件,用来显示规则说明

绑定条件展示

bash
!$w.page.dataset.state.showRules
第五步:课程列表区域
添加数据列表组件 ,数据模型选择预约课程表

设置数据列表的第一个普通容器的背景色、外阴影和圆角

清掉默认的布局组件,里边添加三个普通容器

第一个普通容器里添加两个文本组件,设置横向排列,两端对齐

分别绑定课程名称、预约状态字段

bash
$w.item_listView1.course_name
bash
$w.app.utils.formatEnum($w.item_listView1.status, 'yuyuezhuangtai', $w.app)
第二个普通容器添加三个普通容器,里边添加图标和文本组件,文本组件绑定上课日期时间

bash
$w.DateText($w.item_listView1.created, 'MM月DD日')+$w.DateText($w.item_listView1.start_time, 'HH:mm')+"-"+$w.DateText($w.item_listView1.end_time, 'HH:mm')
按照同样的方法配置其余字段
bash
$w.item_listView1.teacher_name
bash
"剩余"+($w.item_listView1.remaining_spots||0)+"/"+$w.item_listView1.total_spots+"名额"
在第三个容器里添加按钮组件,修改文本为立即预约

第六步:预约功能实现
创建自定义方法 bookCourse:
javascript
export default async function bookCourse({ event, data }) {
try {
const course = data.target;
const userInfo = $w.app.dataset.state.currentUser;
// 校验是否登录
if (!userInfo) {
return $w.utils.showToast({ title: '请先登录', icon: 'error' });
}
// 校验黑名单
if (userInfo.is_blacklisted) {
return $w.utils.showModal({
title: '提示',
content: '您已被加入黑名单,无法预约课程,请联系老师解除',
showCancel: false
});
}
// 校验名额
if (course.remaining_spots <= 0) {
return $w.utils.showToast({ title: '名额已满', icon: 'error' });
}
// 校验是否已经预约(查询预约记录表)
const existingBookingRes = await $w.cloud.callDataSource({
dataSourceName: 'MBA_Bookings',
methodName: 'wedaGetRecordsV2',
params: {
filter: {
where: {
$and: [
{ rel_course_id: { $eq: course._id } },
{ rel_student_id: { $eq: userInfo._id } },
{ status: { $eq: '1' } } // 已预约状态
]
}
},
select: { $master: true }
}
});
if (existingBookingRes.records && existingBookingRes.records.length > 0) {
return $w.utils.showToast({ title: '已预约该课程', icon: 'none' });
}
$w.utils.showLoading({ title: '预约中...' });
// 创建预约记录
await $w.cloud.callDataSource({
dataSourceName: 'MBA_Bookings',
methodName: 'wedaCreateV2',
params: {
data: {
rel_course_id: { _id: course._id },
rel_student_id: { _id: userInfo._id },
status: '1',
book_time: $w.Now()
}
}
});
// 更新课程剩余名额
await $w.cloud.callDataSource({
dataSourceName: 'MBA_BookingCourses',
methodName: 'wedaUpdateV2',
params: {
data: {
remaining_spots: course.remaining_spots - 1
},
filter: {
where: {
_id: { $eq: course._id }
}
}
}
});
$w.utils.hideLoading();
$w.utils.showToast({ title: '预约成功', icon: 'success' });
// 刷新列表
$w.listView1.refresh({})
} catch (e) {
console.error('预约失败', e);
$w.utils.hideLoading();
$w.utils.showToast({ title: '预约失败', icon: 'error' });
}
}
给按钮设置点击事件,调用方法,传入所在行数据

最终效果
进入首页,点击课程预约,可以看到可供预约的课程


总结
本节完成了小程序端课程预约功能的实现:
- 课程列表展示:显示可预约课程,包含课程信息、名额状态
- 预约功能:学员预约课程,扣减剩余名额
- 取消预约:学员取消已预约课程,恢复名额
- 黑名单校验:黑名单学员无法预约