15 KiB
15 KiB
学习记录功能完善 - 实施步骤
🎯 目标
将"学习记录"功能统一到"成长记录",消除功能重复,明确数据来源
📋 实施步骤
步骤1:修改前端入口(5分钟)
文件:peidu/uniapp/src/pages/user/index.vue
找到学习记录入口,修改跳转路径:
<!-- 修改前 -->
<view class="menu-item" @click="goPage('/user-package/pages/user/learning-record')">
<text class="menu-icon">📚</text>
<text>学习记录</text>
<text class="arrow">></text>
</view>
<!-- 修改后 -->
<view class="menu-item" @click="goPage('/user-package/pages/growth/list')">
<text class="menu-icon">📚</text>
<text>成长记录</text>
<text class="arrow">></text>
</view>
步骤2:完善成长记录列表页(15分钟)
文件:peidu/uniapp/src/user-package/pages/growth/list.vue
2.1 添加统计数据
在 <template> 的 page-header 后面添加:
<!-- 统计卡片 -->
<view class="stats-card">
<view class="stat-item">
<text class="stat-value">{{ stats.totalHours }}</text>
<text class="stat-label">累计学习(小时)</text>
</view>
<view class="stat-divider"></view>
<view class="stat-item">
<text class="stat-value">{{ stats.totalSessions }}</text>
<text class="stat-label">服务次数</text>
</view>
<view class="stat-divider"></view>
<view class="stat-item">
<text class="stat-value">{{ stats.avgScore }}</text>
<text class="stat-label">平均评分</text>
</view>
</view>
2.2 添加data属性
data() {
return {
// 新增统计数据
stats: {
totalHours: '0',
totalSessions: 0,
avgScore: '0'
},
// ... 保持原有数据
}
}
2.3 添加loadStats方法
methods: {
// 新增:加载统计数据
async loadStats() {
try {
const res = await request.get('/api/growth-record/parent/stats', {
studentId: this.studentId
})
if (res.code === 200 && res.data) {
this.stats = {
totalHours: res.data.totalHours || '0',
totalSessions: res.data.totalSessions || 0,
avgScore: res.data.avgScore || '0'
}
}
} catch (e) {
console.error('加载统计数据失败:', e)
}
},
// ... 保持原有方法
}
2.4 在onLoad中调用
onLoad(options) {
console.log('=== 成长记录页面加载 ===')
this.studentId = options.studentId || this.getDefaultStudentId()
console.log('studentId:', this.studentId)
this.loadStats() // 新增这一行
this.loadRecordList()
}
2.5 添加样式
在 <style> 中添加:
.stats-card {
background: linear-gradient(135deg, #2d9687 0%, #3da896 100%);
padding: 40rpx 30rpx;
display: flex;
justify-content: space-around;
align-items: center;
margin-bottom: 20rpx;
.stat-item {
flex: 1;
text-align: center;
.stat-value {
display: block;
font-size: 48rpx;
font-weight: bold;
color: #fff;
margin-bottom: 12rpx;
}
.stat-label {
display: block;
font-size: 24rpx;
color: rgba(255, 255, 255, 0.9);
}
}
.stat-divider {
width: 2rpx;
height: 60rpx;
background: rgba(255, 255, 255, 0.3);
}
}
步骤3:添加后端统计接口(20分钟)
文件:peidu/backend/src/main/java/com/peidu/controller/GrowthRecordController.java
在类的开头添加必要的依赖注入:
@Autowired
private com.peidu.mapper.StudentMapper studentMapper;
@Autowired
private com.peidu.mapper.TeacherMapper teacherMapper;
@Autowired
private com.peidu.mapper.ReviewMapper reviewMapper;
然后添加两个新接口(在文件末尾添加):
/**
* 获取家长端统计数据
*/
@ApiOperation("获取家长端统计数据")
@GetMapping("/parent/stats")
public Result<Map<String, Object>> getParentStats(
@RequestParam Long studentId) {
try {
log.info("获取家长端统计数据: studentId={}", studentId);
// 查询该学生的所有成长记录
com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<com.peidu.entity.GrowthRecord> wrapper =
new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<>();
wrapper.eq("student_id", studentId)
.eq("record_type", "daily")
.orderByDesc("record_date");
java.util.List<com.peidu.entity.GrowthRecord> records = growthRecordService.list(wrapper);
// 统计数据
int totalMinutes = 0;
int totalSessions = records.size();
double totalRating = 0;
int ratingCount = 0;
for (com.peidu.entity.GrowthRecord record : records) {
// 计算服务时长
if (record.getOrderId() != null) {
com.peidu.entity.Order order = orderService.getById(record.getOrderId());
if (order != null) {
// 从签到记录获取实际服务时长
com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<com.peidu.entity.CheckInRecord> checkInWrapper =
new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<>();
checkInWrapper.eq("order_id", record.getOrderId())
.orderByAsc("check_time");
java.util.List<com.peidu.entity.CheckInRecord> checkInRecords = checkInRecordMapper.selectList(checkInWrapper);
java.time.LocalDateTime checkInTime = null;
java.time.LocalDateTime checkOutTime = null;
for (com.peidu.entity.CheckInRecord checkRecord : checkInRecords) {
if ("checkin".equals(checkRecord.getCheckType()) && checkInTime == null) {
checkInTime = checkRecord.getCheckTime();
} else if ("checkout".equals(checkRecord.getCheckType())) {
checkOutTime = checkRecord.getCheckTime();
}
}
if (checkInTime != null && checkOutTime != null) {
java.time.Duration duration = java.time.Duration.between(checkInTime, checkOutTime);
totalMinutes += (int) duration.toMinutes();
}
// 获取评分
com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<com.peidu.entity.Review> reviewWrapper =
new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<>();
reviewWrapper.eq("order_id", record.getOrderId());
com.peidu.entity.Review review = reviewMapper.selectOne(reviewWrapper);
if (review != null && review.getRating() != null) {
totalRating += review.getRating();
ratingCount++;
}
}
}
}
// 构建返回数据
java.util.Map<String, Object> stats = new java.util.HashMap<>();
stats.put("totalHours", String.format("%.1f", totalMinutes / 60.0));
stats.put("totalSessions", totalSessions);
stats.put("avgScore", ratingCount > 0 ?
String.format("%.1f", totalRating / ratingCount) : "0");
log.info("统计结果: {}", stats);
return Result.success(stats);
} catch (Exception e) {
log.error("获取统计数据失败", e);
return Result.error(e.getMessage());
}
}
/**
* 获取家长端成长记录列表
*/
@ApiOperation("获取家长端成长记录列表")
@GetMapping("/parent/list")
public Result<com.baomidou.mybatisplus.extension.plugins.pagination.Page<GrowthRecordVO>> getParentList(
@RequestParam Long studentId,
@RequestParam(required = false) String recordType,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size) {
try {
log.info("获取家长端成长记录列表: studentId={}, recordType={}, page={}, size={}",
studentId, recordType, page, size);
com.baomidou.mybatisplus.extension.plugins.pagination.Page<com.peidu.entity.GrowthRecord> pageParam =
new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(page, size);
com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<com.peidu.entity.GrowthRecord> wrapper =
new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<>();
wrapper.eq("student_id", studentId);
if (recordType != null && !"all".equals(recordType)) {
wrapper.eq("record_type", recordType);
}
wrapper.orderByDesc("record_date");
com.baomidou.mybatisplus.extension.plugins.pagination.Page<com.peidu.entity.GrowthRecord> recordPage =
growthRecordService.page(pageParam, wrapper);
// 转换为VO
com.baomidou.mybatisplus.extension.plugins.pagination.Page<GrowthRecordVO> voPage =
new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>();
voPage.setCurrent(recordPage.getCurrent());
voPage.setSize(recordPage.getSize());
voPage.setTotal(recordPage.getTotal());
java.util.List<GrowthRecordVO> voList = new java.util.ArrayList<>();
for (com.peidu.entity.GrowthRecord record : recordPage.getRecords()) {
GrowthRecordVO vo = convertToVO(record);
voList.add(vo);
}
voPage.setRecords(voList);
log.info("查询成功,共{}条记录", voPage.getTotal());
return Result.success(voPage);
} catch (Exception e) {
log.error("获取家长端成长记录列表失败", e);
return Result.error(e.getMessage());
}
}
/**
* 转换为VO对象
*/
private GrowthRecordVO convertToVO(com.peidu.entity.GrowthRecord record) {
GrowthRecordVO vo = new GrowthRecordVO();
vo.setId(record.getId());
vo.setRecordDate(record.getRecordDate());
vo.setRecordType(record.getRecordType());
vo.setContent(record.getContent());
vo.setImageList(record.getImageList());
vo.setVideoList(record.getVideoList());
// 设置类型名称
String typeName = "每日反馈";
if ("weekly".equals(record.getRecordType())) {
typeName = "周反馈";
} else if ("monthly".equals(record.getRecordType())) {
typeName = "月反馈";
}
vo.setRecordTypeName(typeName);
// 获取学生信息
if (record.getStudentId() != null) {
com.peidu.entity.Student student = studentMapper.selectById(record.getStudentId());
if (student != null) {
vo.setStudentName(student.getName());
}
}
// 获取教师信息
if (record.getTeacherId() != null) {
com.peidu.entity.Teacher teacher = teacherMapper.selectById(record.getTeacherId());
if (teacher != null) {
vo.setTeacherName(teacher.getName());
}
}
// 获取服务时长
if (record.getOrderId() != null) {
com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<com.peidu.entity.CheckInRecord> checkInWrapper =
new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<>();
checkInWrapper.eq("order_id", record.getOrderId())
.orderByAsc("check_time");
java.util.List<com.peidu.entity.CheckInRecord> checkInRecords = checkInRecordMapper.selectList(checkInWrapper);
java.time.LocalDateTime checkInTime = null;
java.time.LocalDateTime checkOutTime = null;
for (com.peidu.entity.CheckInRecord checkRecord : checkInRecords) {
if ("checkin".equals(checkRecord.getCheckType()) && checkInTime == null) {
checkInTime = checkRecord.getCheckTime();
} else if ("checkout".equals(checkRecord.getCheckType())) {
checkOutTime = checkRecord.getCheckTime();
}
}
if (checkInTime != null && checkOutTime != null) {
java.time.Duration duration = java.time.Duration.between(checkInTime, checkOutTime);
int minutes = (int) duration.toMinutes();
int hours = minutes / 60;
int mins = minutes % 60;
vo.setDurationText(hours > 0 ?
String.format("%d小时%d分钟", hours, mins) :
String.format("%d分钟", mins));
}
}
return vo;
}
步骤4:更新VO类(5分钟)
文件:peidu/backend/src/main/java/com/peidu/vo/GrowthRecordVO.java
添加新字段:
/**
* 学生姓名
*/
private String studentName;
/**
* 教师姓名
*/
private String teacherName;
/**
* 记录类型名称
*/
private String recordTypeName;
/**
* 服务时长文本
*/
private String durationText;
步骤5:清理冗余代码(5分钟)
5.1 删除学习记录页面文件
# 在项目根目录执行
del peidu\uniapp\src\user-package\pages\user\learning-record.vue
del peidu\uniapp\src\user-package\pages\user\learning-record-detail.vue
5.2 清理API定义
文件:peidu/uniapp/src/api/index.js
找到并删除 recordApi 的定义:
// 删除这部分代码(大约在436行)
export const recordApi = {
// 获取学习记录列表
getRecordList(params) {
return request.get('/api/record/list', { params })
},
// 获取学习记录详情
getRecordDetail(id) {
return request.get(`/api/record/detail/${id}`)
},
// 获取学习统计
getStats() {
return request.get('/api/record/stats')
}
}
同时删除导出列表中的 recordApi:
// 找到export default,删除recordApi
export default {
// ... 其他API
// recordApi, // 删除这一行
// ... 其他API
}
步骤6:编译和测试(10分钟)
6.1 编译后端
cd peidu/backend
mvn clean compile
6.2 重启后端服务
# 停止现有服务
# 启动新服务
6.3 编译前端
在HBuilderX中:
- 右键项目
- 选择"运行" → "运行到小程序模拟器"
- 等待编译完成
6.4 测试功能
-
测试入口
- 打开家长端
- 进入"我的"页面
- 点击"成长记录"
- ✅ 应该跳转到成长记录列表页
-
测试统计数据
- 查看顶部统计卡片
- ✅ 应该显示:累计学习时长、服务次数、平均评分
-
测试列表数据
- 查看记录列表
- ✅ 应该显示所有成长记录
- ✅ 每条记录应包含:日期、类型、学生、陪伴员、服务时长、内容预览
-
测试详情页
- 点击任意记录
- ✅ 应该显示完整的成长记录详情
✅ 验证清单
- 用户中心"成长记录"入口正常
- 统计数据显示正确
- 列表数据加载正常
- 详情页信息完整
- 服务时长计算准确
- 评分显示正确(如果有评价)
- 照片/视频正常显示
- 学习记录页面已删除
- recordApi已清理
- 后端编译无错误
- 前端编译无错误
🎉 完成标志
当所有验证项都通过后,学习记录功能完善工作即完成!
家长端现在拥有统一的"成长记录"功能,数据来源清晰,信息完整。