目录
- 前情回顾与本节目标
- 第一步:数据模型准备
-
- [1.1 学习卡表(MBA_LearningCards)](#1.1 学习卡表(MBA_LearningCards))
- [1.2 消费记录表(MBA_ConsumptionRecords)](#1.2 消费记录表(MBA_ConsumptionRecords))
- 第二步:搭建学习卡页面布局
-
- [2.1 创建页面](#2.1 创建页面)
- [2.2 页面头部](#2.2 页面头部)
- [2.3 消费记录区域](#2.3 消费记录区域)
- 第四步:签到扣课时功能
-
- [4.1 修改签到逻辑](#4.1 修改签到逻辑)
- 最终效果
- 总结
前情回顾与本节目标
在上一节中,我们完成了我的课表和请假功能。本节我们将实现学习卡功能,显示学员的课时余额、总课时、已消课,并在签到时自动扣减课时,记录消费明细。
本节核心目标:
- 学习卡页面布局:使用低代码组件搭建学习卡页面
- 数据加载:查询学习卡余额和消费记录
- 签到扣课时:签到成功时自动扣减1课时
- 消费记录展示:显示消课明细

第一步:数据模型准备
1.1 学习卡表(MBA_LearningCards)
用于记录学员的学习卡信息:
| 字段名称 | 字段标识 | 字段类型 | 说明 |
|---|---|---|---|
| 学习卡ID | _id | 文本 | 主键,系统自动生成 |
| 关联学员 | rel_student_id | 多对一 | 关联 StudentProfiles 表 |
| 总课时 | total_hours | 数字 | 总课时数 |
| 剩余课时 | remaining_hours | 数字 | 剩余课时数 |
| 已消课时 | used_hours | 数字 | 已消课时数 |
| 状态 | status | 枚举 | 1-正常、2-已停用 |
| 创建时间 | created | 日期时间 | 创建时间 |
1.2 消费记录表(MBA_ConsumptionRecords)
用于记录课时消费明细:
| 字段名称 | 字段标识 | 字段类型 | 说明 |
|---|---|---|---|
| 记录ID | _id | 文本 | 主键,系统自动生成 |
| 关联学员 | rel_student_id | 多对一 | 关联 StudentProfiles 表 |
| 关联课表 | rel_schedule_id | 多对一 | 关联 Schedules 表 |
| 关联课程 | rel_course_id | 多对一 | 关联 Product 表 |
| 关联考勤 | rel_attendance_id | 多对一 | 关联 Attendance 表 |
| 消费类型 | type | 枚举 | 1-消课、2-充值 |
| 消费数量 | amount | 数字 | 消费数量(正数充值,负数消课) |
| 变动后余额 | balance | 数字 | 变动后的余额 |
| 备注 | remark | 文本 | 备注信息 |
| 消费时间 | created | 日期时间 | 消费时间 |
第二步:搭建学习卡页面布局
2.1 创建页面
点击创建页面 图标

输入"学习卡"

复制页面的ID

回到首页,找到我们的快捷菜单跳转方法,修改页面ID

bash
export default function({event, data}) {
const actionType = data.target;
switch(actionType) {
case 'checkin':
$w.utils.navigateTo({ pageId: 'u_jin_ri_qie_dao' });
break;
case 'schedule':
$w.utils.navigateTo({ pageId: 'u_wo_de_ke_biao' });
break;
case 'card':
$w.utils.navigateTo({ pageId: 'u_xue_xi_ka' });
break;
case 'homework':
$w.utils.navigateTo({ pageId: 'homework' });
break;
case 'resources':
$w.utils.navigateTo({ pageId: 'resources' });
break;
case 'survey':
$w.utils.navigateTo({ pageId: 'survey' });
break;
}
}
2.2 页面头部
添加容器组件 ,设置宽、高和背景色

bash
:root {
width: 100%;
min-height: 100vh;
background-color: #F6F7F9;
}
继续添加普通容器,设置背景色、内边距和圆角

bash
:root {
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
padding: 48px 20px 24px 20px;
border-radius: 0 0 24px 24px;
}
里边添加文本组件,设置文本颜色、字号和加粗的效果

继续添加普通容器,设置浅色背景

bash
:root {
background-color: rgba(255, 255, 255, 0.1);
border-radius: 16px;
padding: 20px;
margin-top: 16px;
}
继续添加三个文本组件,用来显示课时

从当前登录用户绑定余额字段

bash
$w.app.dataset.state.currentUser.classInfo[0].rel_card_id.remain_hours+"课时"
绑定总课时

创建一个内置数据表查询,查询考勤记录表

绑定已消课课时

bash
"总课时"+$w.app.dataset.state.currentUser.classInfo[0].rel_card_id.total_hours+" 已消课"+$w.queryAttendance.data.records.reduce((sum, item) => {
// 只累加非null的有效数字
return sum + (item.hours_spent ?? 0);
}, 0)
2.3 消费记录区域
添加数据列表组件 ,数据模型选择考勤记录表

调整文本的绑定字段,绑定好对应字段

第四步:签到扣课时功能
4.1 修改签到逻辑
回到课程签到页面,修改 handleCheckin 方法,在签到成功后扣减课时:
javascript
export default async function handleCheckin({ event, data }) {
try {
const userInfo = $w.app.dataset.state.currentUser;
const currentAttendance = $w.queryAttendance.data;
console.log(currentAttendance)
if (!userInfo) {
return $w.utils.showToast({ title: '请先登录', icon: 'error' });
}
// 2. 检查是否有待签到记录
if (!currentAttendance) {
return $w.utils.showModal({
title: '提示',
content: '今日暂无课程安排或管理员未录入考勤',
showCancel: false
});
}
$w.utils.showLoading({ title: '签到中...' });
// 3. 判断签到状态(正常/迟到)
console.log("scheldDate",currentAttendance.rel_schedule_id?.course_date+currentAttendance.rel_schedule_id?.start_time)
console.log("now",$w.Now())
const diffMinutes = $w.MinuteDiff(currentAttendance.rel_schedule_id?.course_date+currentAttendance.rel_schedule_id?.start_time,$w.Now())
console.log("diffMinutes",diffMinutes)
let status = '2'; // 正常签到
if (diffMinutes > 15) {
status = '3'; // 迟到(超过15分钟)
}
// 4. 更新考勤记录状态(管理员事先录入,学员只需更新)
await $w.cloud.callDataSource({
dataSourceName: 'MBA_Attendance',
methodName: 'wedaUpdateV2',
params: {
data: {
status: status,
sign_in_time: $w.Now(),
},
filter: {
where: {
_id: { $eq: currentAttendance._id }
}
}
}
});
// 5. 查询学习卡,检查课时余额
const cardRes = await $w.cloud.callDataSource({
dataSourceName: 'MBA_LearningCards',
methodName: 'wedaGetRecordsV2',
params: {
filter: {
where: {
rel_student_id: { $eq: userInfo._id }
}
}
}
});
if (!cardRes.records || cardRes.records.length === 0) {
$w.utils.hideLoading();
return $w.utils.showModal({
title: '提示',
content: '您没有学习卡,请先联系管理员开通',
showCancel: false
});
}
const learningCard = cardRes.records[0];
if (learningCard.remaining_hours < 1) {
$w.utils.hideLoading();
return $w.utils.showModal({
title: '提示',
content: '课时不足,请先充值',
showCancel: false
});
}
// 6. 扣减学习卡课时
const newRemaining = learningCard.remaining_hours - 1;
const newUsed = learningCard.used_hours + 1;
await $w.cloud.callDataSource({
dataSourceName: 'MBA_LearningCards',
methodName: 'wedaUpdateV2',
params: {
data: {
remaining_hours: newRemaining,
used_hours: newUsed
},
filter: {
where: {
_id: { $eq: learningCard._id }
}
}
}
});
// 7. 创建消费记录
await $w.cloud.callDataSource({
dataSourceName: 'MBA_ConsumptionRecords',
methodName: 'wedaCreateV2',
params: {
data: {
rel_student_id: { _id: userInfo._id },
rel_schedule_id: { _id: currentAttendance.rel_schedule_id._id },
rel_attendance_id: { _id: currentAttendance._id },
rel_course_id:{_id:currentAttendance.rel_schedule_id.rel_course_id},
type: '1',
amount: -1,
balance: newRemaining,
remark: currentAttendance.rel_schedule_id?.course_name || '课程签到',
created: $w.Now()
}
}
});
$w.utils.hideLoading();
$w.utils.showToast({ title: '签到成功!', icon: 'success' });
$w.queryAttendance.trigger()
} catch (e) {
console.error('签到失败', e);
$w.utils.hideLoading();
$w.utils.showToast({ title: '签到失败,请重试', icon: 'error' });
}
}
最终效果
打开首页,点击学习卡

显示学习卡信息

总结
本节完成了学习卡功能的实现:
- 学习卡页面布局:头部、余额卡片、消费记录列表
- 数据加载:查询学习卡余额和消费记录
- 签到扣课时:签到成功时自动扣减1课时
- 消费记录展示:显示消课明细和充值记录