peixue-dev/peidu/uniapp/training-package/pages/training/course-detail.vue

370 lines
8.5 KiB
Vue

<template>
<view class="course-detail">
<!-- 课程头部 -->
<view class="course-header">
<image :src="course.coverImage" class="cover-image" mode="aspectFill"></image>
<view class="course-info">
<view class="course-title">{{ course.courseName }}</view>
<view class="course-meta">
<text class="meta-item">{{ course.courseType === 'companion' ? '陪伴员' : course.courseType === 'manager' ? '管理师' : '分销员' }}</text>
<text class="meta-item">{{ course.level === 'basic' ? '基础' : course.level === 'advanced' ? '进阶' : '专家' }}</text>
<text class="meta-item">{{ course.duration }}分钟</text>
</view>
</view>
</view>
<!-- 学习进度 -->
<view class="progress-section" v-if="learningRecord">
<view class="progress-header">
<text class="progress-title">学习进度</text>
<text class="progress-text">{{ learningRecord.progress }}%</text>
</view>
<view class="progress-bar">
<view class="progress-fill" :style="{ width: learningRecord.progress + '%' }"></view>
</view>
</view>
<!-- 课程内容 -->
<view class="course-content">
<view class="section-title">课程介绍</view>
<view class="content-text">{{ course.description }}</view>
<view class="section-title">课程目标</view>
<view class="content-text">{{ course.objectives }}</view>
<view class="section-title">适用对象</view>
<view class="content-text">{{ course.targetAudience }}</view>
</view>
<!-- 视频播放器 -->
<view class="video-section" v-if="course.videoUrl">
<video
:src="course.videoUrl"
class="video-player"
controls
@timeupdate="onVideoTimeUpdate"
@ended="onVideoEnded"
></video>
</view>
<!-- 课程资料 -->
<view class="materials-section" v-if="course.materials">
<view class="section-title">课程资料</view>
<view class="material-item" v-for="(material, index) in materials" :key="index" @click="downloadMaterial(material)">
<text class="material-icon">📄</text>
<text class="material-name">{{ material.name }}</text>
<text class="material-size">{{ material.size }}</text>
</view>
</view>
<!-- 底部操作栏 -->
<view class="bottom-bar">
<button v-if="!learningRecord" class="btn-primary" @click="startCourse">开始学习</button>
<button v-else-if="learningRecord.progress < 100" class="btn-primary" @click="continueCourse">继续学习</button>
<button v-else class="btn-success" @click="goToExam">参加考试</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
courseId: null,
course: {},
learningRecord: null,
materials: [],
videoCurrentTime: 0
};
},
onLoad(options) {
this.courseId = options.id;
this.loadCourseDetail();
this.loadLearningRecord();
},
methods: {
// 加载课程详情
async loadCourseDetail() {
try {
const res = await this.$http.get(`/api/training/courses/${this.courseId}`);
this.course = res.data;
if (this.course.materials) {
this.materials = JSON.parse(this.course.materials);
}
} catch (error) {
uni.showToast({ title: '加载失败', icon: 'none' });
}
},
// 加载学习记录
async loadLearningRecord() {
try {
const res = await this.$http.get('/api/training/learning-records', {
params: { courseId: this.courseId }
});
this.learningRecord = res.data;
} catch (error) {
console.log('暂无学习记录');
}
},
// 开始学习
async startCourse() {
try {
await this.$http.post('/api/training/start-course', {
courseId: this.courseId
});
uni.showToast({ title: '开始学习', icon: 'success' });
this.loadLearningRecord();
} catch (error) {
uni.showToast({ title: '操作失败', icon: 'none' });
}
},
// 继续学习
continueCourse() {
uni.showToast({ title: '继续学习', icon: 'success' });
},
// 视频播放进度更新
async onVideoTimeUpdate(e) {
this.videoCurrentTime = e.detail.currentTime;
const duration = e.detail.duration;
const progress = Math.floor((this.videoCurrentTime / duration) * 100);
// 每10%更新一次进度
if (progress % 10 === 0 && progress !== this.learningRecord?.progress) {
await this.updateProgress(progress);
}
},
// 视频播放结束
async onVideoEnded() {
await this.updateProgress(100);
await this.completeCourse();
},
// 更新学习进度
async updateProgress(progress) {
try {
await this.$http.post('/api/training/update-progress', {
courseId: this.courseId,
progress: progress
});
this.learningRecord.progress = progress;
} catch (error) {
console.log('更新进度失败');
}
},
// 完成课程
async completeCourse() {
try {
await this.$http.post('/api/training/complete-course', {
courseId: this.courseId
});
uni.showToast({ title: '恭喜完成课程!', icon: 'success' });
this.loadLearningRecord();
} catch (error) {
console.log('完成课程失败');
}
},
// 下载资料
downloadMaterial(material) {
uni.downloadFile({
url: material.url,
success: (res) => {
uni.showToast({ title: '下载成功', icon: 'success' });
}
});
},
// 前往考试
goToExam() {
uni.navigateTo({
url: `/pages/training/exam?courseType=${this.course.courseType}&level=${this.course.level}`
});
}
}
};
</script>
<style lang="scss" scoped>
.course-detail {
min-height: 100vh;
background: #f5f5f5;
padding-bottom: 120rpx;
}
.course-header {
background: #fff;
margin-bottom: 20rpx;
.cover-image {
width: 100%;
height: 400rpx;
}
.course-info {
padding: 30rpx;
.course-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
}
.course-meta {
display: flex;
gap: 20rpx;
.meta-item {
font-size: 24rpx;
color: #999;
padding: 8rpx 16rpx;
background: #f5f5f5;
border-radius: 8rpx;
}
}
}
}
.progress-section {
background: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
.progress-header {
display: flex;
justify-content: space-between;
margin-bottom: 20rpx;
.progress-title {
font-size: 28rpx;
color: #333;
}
.progress-text {
font-size: 28rpx;
color: #7dd3c0;
font-weight: bold;
}
}
.progress-bar {
height: 12rpx;
background: #f0f0f0;
border-radius: 6rpx;
overflow: hidden;
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #7dd3c0, #5bc0ad);
transition: width 0.3s;
}
}
}
.course-content {
background: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin: 30rpx 0 20rpx;
&:first-child {
margin-top: 0;
}
}
.content-text {
font-size: 28rpx;
color: #666;
line-height: 1.8;
}
}
.video-section {
background: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
.video-player {
width: 100%;
height: 400rpx;
}
}
.materials-section {
background: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
}
.material-item {
display: flex;
align-items: center;
padding: 20rpx;
background: #f5f5f5;
border-radius: 12rpx;
margin-bottom: 20rpx;
.material-icon {
font-size: 40rpx;
margin-right: 20rpx;
}
.material-name {
flex: 1;
font-size: 28rpx;
color: #333;
}
.material-size {
font-size: 24rpx;
color: #999;
}
}
}
.bottom-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 20rpx 30rpx;
background: #fff;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
button {
width: 100%;
height: 88rpx;
border-radius: 44rpx;
font-size: 32rpx;
border: none;
}
.btn-primary {
background: linear-gradient(135deg, #7dd3c0, #5bc0ad);
color: #fff;
}
.btn-success {
background: linear-gradient(135deg, #52c41a, #389e0d);
color: #fff;
}
}
</style>