507 lines
11 KiB
Markdown
507 lines
11 KiB
Markdown
# 前端代码实现 - 我的课程与学习记录关联
|
|
|
|
## 📱 1. API接口定义
|
|
|
|
在 `peidu/uniapp/src/api/parentAcademy.js` 中添加:
|
|
|
|
```javascript
|
|
// 课程学习相关接口
|
|
export default {
|
|
// ... 现有接口 ...
|
|
|
|
// 开始学习课程
|
|
startLearning(data) {
|
|
return request({
|
|
url: '/api/parent-academy/start-learning',
|
|
method: 'POST',
|
|
data
|
|
})
|
|
},
|
|
|
|
// 更新学习进度
|
|
updateProgress(data) {
|
|
return request({
|
|
url: '/api/parent-academy/update-progress',
|
|
method: 'POST',
|
|
data
|
|
})
|
|
},
|
|
|
|
// 完成课程学习
|
|
completeLearning(data) {
|
|
return request({
|
|
url: '/api/parent-academy/complete-learning',
|
|
method: 'POST',
|
|
data
|
|
})
|
|
},
|
|
|
|
// 获取学习历史
|
|
getLearningHistory(courseId) {
|
|
return request({
|
|
url: '/api/parent-academy/learning-history',
|
|
method: 'GET',
|
|
params: { courseId }
|
|
})
|
|
},
|
|
|
|
// 提交课程评价
|
|
submitReview(data) {
|
|
return request({
|
|
url: '/api/parent-academy/submit-review',
|
|
method: 'POST',
|
|
data
|
|
})
|
|
}
|
|
}
|
|
```
|
|
|
|
## 📄 2. 改造 my-courses.vue 页面
|
|
|
|
### 2.1 增强课程卡片显示
|
|
|
|
在课程卡片中添加学习进度条:
|
|
|
|
```vue
|
|
<template>
|
|
<view class="course-card" @tap="viewCourse(course)">
|
|
<!-- 现有内容 -->
|
|
|
|
<!-- 新增: 学习进度条 -->
|
|
<view class="progress-section" v-if="course.learningRecord">
|
|
<view class="progress-bar">
|
|
<view
|
|
class="progress-fill"
|
|
:style="{ width: course.learningRecord.learningProgress + '%' }"
|
|
></view>
|
|
</view>
|
|
<view class="progress-info">
|
|
<text class="progress-text">
|
|
学习进度: {{ course.learningRecord.learningProgress }}%
|
|
</text>
|
|
<text class="points-text" v-if="course.learningRecord.pointsAwarded > 0">
|
|
已获得 {{ course.learningRecord.pointsAwarded }} 积分
|
|
</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 新增: 完成标记 -->
|
|
<view class="completed-badge" v-if="course.learningRecord && course.learningRecord.isCompleted">
|
|
<text class="badge-icon">✓</text>
|
|
<text class="badge-text">已完成</text>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
methods: {
|
|
async loadMyCourses() {
|
|
// ... 现有代码 ...
|
|
|
|
// 转换数据格式时添加学习记录
|
|
this.courseList = list.map(item => {
|
|
const purchase = item.purchase || item
|
|
const course = item.course || item
|
|
const learningRecord = item.learningRecord || null
|
|
|
|
return {
|
|
// ... 现有字段 ...
|
|
learningRecord: learningRecord ? {
|
|
id: learningRecord.id,
|
|
learningProgress: learningRecord.learningProgress || 0,
|
|
lastPosition: learningRecord.lastPosition || 0,
|
|
isCompleted: learningRecord.isCompleted || 0,
|
|
pointsAwarded: learningRecord.pointsAwarded || 0,
|
|
notes: learningRecord.notes || ''
|
|
} : null
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* 学习进度条样式 */
|
|
.progress-section {
|
|
margin-top: 20rpx;
|
|
padding-top: 20rpx;
|
|
border-top: 1rpx solid #f0f0f0;
|
|
}
|
|
|
|
.progress-bar {
|
|
width: 100%;
|
|
height: 8rpx;
|
|
background: #f0f0f0;
|
|
border-radius: 4rpx;
|
|
overflow: hidden;
|
|
margin-bottom: 15rpx;
|
|
}
|
|
|
|
.progress-fill {
|
|
height: 100%;
|
|
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
|
|
border-radius: 4rpx;
|
|
transition: width 0.3s;
|
|
}
|
|
|
|
.progress-info {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.progress-text {
|
|
font-size: 24rpx;
|
|
color: #666666;
|
|
}
|
|
|
|
.points-text {
|
|
font-size: 24rpx;
|
|
color: #ff6b6b;
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* 完成标记样式 */
|
|
.completed-badge {
|
|
position: absolute;
|
|
top: 20rpx;
|
|
right: 20rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8rpx;
|
|
padding: 8rpx 16rpx;
|
|
background: linear-gradient(135deg, #66bb6a 0%, #43a047 100%);
|
|
border-radius: 20rpx;
|
|
box-shadow: 0 2rpx 8rpx rgba(67, 160, 71, 0.3);
|
|
}
|
|
|
|
.badge-icon {
|
|
font-size: 24rpx;
|
|
color: #ffffff;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.badge-text {
|
|
font-size: 22rpx;
|
|
color: #ffffff;
|
|
font-weight: 500;
|
|
}
|
|
</style>
|
|
```
|
|
|
|
## 📺 3. 创建课程学习页面
|
|
|
|
创建 `peidu/uniapp/src/user-package/pages/course/player.vue`:
|
|
|
|
```vue
|
|
<template>
|
|
<view class="course-player-page">
|
|
<!-- 视频播放器 -->
|
|
<video
|
|
v-if="course.videoUrl"
|
|
:src="course.videoUrl"
|
|
:initial-time="lastPosition"
|
|
class="video-player"
|
|
controls
|
|
@timeupdate="onTimeUpdate"
|
|
@ended="onVideoEnded"
|
|
></video>
|
|
|
|
<!-- 课程信息 -->
|
|
<view class="course-info">
|
|
<text class="course-title">{{ course.title }}</text>
|
|
<view class="course-meta">
|
|
<text class="meta-item">时长: {{ course.duration }}分钟</text>
|
|
<text class="meta-item">进度: {{ learningProgress }}%</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 学习笔记 -->
|
|
<view class="notes-section">
|
|
<text class="section-title">学习笔记</text>
|
|
<textarea
|
|
v-model="notes"
|
|
class="notes-input"
|
|
placeholder="记录你的学习心得..."
|
|
maxlength="500"
|
|
></textarea>
|
|
<text class="notes-count">{{ notes.length }}/500</text>
|
|
</view>
|
|
|
|
<!-- 完成按钮 -->
|
|
<view class="action-section">
|
|
<button
|
|
class="btn-complete"
|
|
:disabled="learningProgress < 90"
|
|
@tap="completeLearning"
|
|
>
|
|
{{ learningProgress >= 90 ? '完成学习' : '请观看至少90%的内容' }}
|
|
</button>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import api from '@/api/index.js'
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
courseId: null,
|
|
purchaseId: null,
|
|
learningRecordId: null,
|
|
course: {},
|
|
lastPosition: 0,
|
|
learningProgress: 0,
|
|
notes: '',
|
|
currentPosition: 0,
|
|
duration: 0,
|
|
updateTimer: null
|
|
}
|
|
},
|
|
|
|
onLoad(options) {
|
|
this.courseId = options.id
|
|
this.purchaseId = options.purchaseId
|
|
this.loadCourseData()
|
|
},
|
|
|
|
onUnload() {
|
|
// 页面卸载时保存进度
|
|
this.saveProgress()
|
|
if (this.updateTimer) {
|
|
clearInterval(this.updateTimer)
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
async loadCourseData() {
|
|
try {
|
|
// 获取课程信息
|
|
const courseData = await api.parentAcademyApi.getCourseDetail(this.courseId)
|
|
this.course = courseData
|
|
|
|
// 开始学习,获取学习记录
|
|
const learningData = await api.parentAcademyApi.startLearning({
|
|
courseId: this.courseId,
|
|
purchaseId: this.purchaseId
|
|
})
|
|
|
|
this.learningRecordId = learningData.learningRecordId
|
|
this.lastPosition = learningData.lastPosition || 0
|
|
this.learningProgress = learningData.learningProgress || 0
|
|
|
|
// 启动定时保存进度(每30秒)
|
|
this.updateTimer = setInterval(() => {
|
|
this.saveProgress()
|
|
}, 30000)
|
|
|
|
} catch (error) {
|
|
console.error('加载课程数据失败:', error)
|
|
uni.showToast({
|
|
title: '加载失败',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
},
|
|
|
|
onTimeUpdate(e) {
|
|
this.currentPosition = Math.floor(e.detail.currentTime)
|
|
this.duration = Math.floor(e.detail.duration)
|
|
|
|
// 计算进度
|
|
if (this.duration > 0) {
|
|
this.learningProgress = Math.floor((this.currentPosition / this.duration) * 100)
|
|
}
|
|
},
|
|
|
|
async saveProgress() {
|
|
if (!this.learningRecordId || this.currentPosition === 0) {
|
|
return
|
|
}
|
|
|
|
try {
|
|
await api.parentAcademyApi.updateProgress({
|
|
learningRecordId: this.learningRecordId,
|
|
position: this.currentPosition,
|
|
duration: this.duration
|
|
})
|
|
|
|
console.log('学习进度已保存:', this.currentPosition)
|
|
} catch (error) {
|
|
console.error('保存进度失败:', error)
|
|
}
|
|
},
|
|
|
|
onVideoEnded() {
|
|
// 视频播放完成
|
|
this.learningProgress = 100
|
|
this.saveProgress()
|
|
},
|
|
|
|
async completeLearning() {
|
|
if (this.learningProgress < 90) {
|
|
uni.showToast({
|
|
title: '请观看至少90%的内容',
|
|
icon: 'none'
|
|
})
|
|
return
|
|
}
|
|
|
|
try {
|
|
uni.showLoading({ title: '提交中...' })
|
|
|
|
const result = await api.parentAcademyApi.completeLearning({
|
|
learningRecordId: this.learningRecordId,
|
|
notes: this.notes
|
|
})
|
|
|
|
uni.hideLoading()
|
|
|
|
// 显示积分奖励
|
|
if (result.pointsAwarded > 0) {
|
|
uni.showModal({
|
|
title: '恭喜完成学习!',
|
|
content: `获得 ${result.pointsAwarded} 积分奖励`,
|
|
showCancel: false,
|
|
success: () => {
|
|
// 返回我的课程页面
|
|
uni.navigateBack()
|
|
}
|
|
})
|
|
} else {
|
|
uni.showToast({
|
|
title: '学习完成',
|
|
icon: 'success'
|
|
})
|
|
setTimeout(() => {
|
|
uni.navigateBack()
|
|
}, 1500)
|
|
}
|
|
|
|
} catch (error) {
|
|
uni.hideLoading()
|
|
console.error('完成学习失败:', error)
|
|
uni.showToast({
|
|
title: '提交失败',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.course-player-page {
|
|
min-height: 100vh;
|
|
background: #f5f7fa;
|
|
}
|
|
|
|
.video-player {
|
|
width: 100%;
|
|
height: 420rpx;
|
|
}
|
|
|
|
.course-info {
|
|
background: #ffffff;
|
|
padding: 30rpx;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.course-title {
|
|
display: block;
|
|
font-size: 32rpx;
|
|
font-weight: 500;
|
|
color: #333333;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.course-meta {
|
|
display: flex;
|
|
gap: 30rpx;
|
|
}
|
|
|
|
.meta-item {
|
|
font-size: 26rpx;
|
|
color: #666666;
|
|
}
|
|
|
|
.notes-section {
|
|
background: #ffffff;
|
|
padding: 30rpx;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.section-title {
|
|
display: block;
|
|
font-size: 28rpx;
|
|
font-weight: 500;
|
|
color: #333333;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.notes-input {
|
|
width: 100%;
|
|
min-height: 200rpx;
|
|
padding: 20rpx;
|
|
background: #f5f7fa;
|
|
border-radius: 8rpx;
|
|
font-size: 26rpx;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.notes-count {
|
|
display: block;
|
|
text-align: right;
|
|
font-size: 24rpx;
|
|
color: #999999;
|
|
margin-top: 10rpx;
|
|
}
|
|
|
|
.action-section {
|
|
padding: 30rpx;
|
|
}
|
|
|
|
.btn-complete {
|
|
width: 100%;
|
|
height: 88rpx;
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
color: #ffffff;
|
|
font-size: 30rpx;
|
|
border-radius: 44rpx;
|
|
border: none;
|
|
}
|
|
|
|
.btn-complete[disabled] {
|
|
background: #cccccc;
|
|
color: #999999;
|
|
}
|
|
</style>
|
|
```
|
|
|
|
## 🔄 4. 修改课程跳转逻辑
|
|
|
|
在 `my-courses.vue` 中修改 `viewCourse` 方法:
|
|
|
|
```javascript
|
|
viewCourse(course) {
|
|
console.log('查看课程:', course)
|
|
|
|
// 跳转到课程学习页面
|
|
uni.navigateTo({
|
|
url: `/user-package/pages/course/player?id=${course.id}&purchaseId=${course.purchaseId}`
|
|
})
|
|
}
|
|
```
|
|
|
|
## ✅ 完成后的功能
|
|
|
|
1. **断点续播** - 自动从上次学习位置继续
|
|
2. **进度保存** - 每30秒自动保存学习进度
|
|
3. **学习笔记** - 支持记录学习心得
|
|
4. **积分奖励** - 完成学习后自动发放积分
|
|
5. **进度可视化** - 实时显示学习进度百分比
|