vue的h5页面中使用视频播放插件
h5项目中需要做视频课程播放,在网上搜了一下对应的插件,发觉xg-player西瓜播放器用起来不错.当然也踩了一些坑.
**西瓜播放器官方地址: **http://h5player.bytedance.com/
西瓜播放器githuab官网: https://github.com/bytedance/xgplayer-vue
初步使用
因为自己做的是个老项目,freemarker模板语法写的(总之是旧时代那种,你知道jsp就名字了),但是我想用自己的方式开发,所以在里面用了vue.
先写一个静态页面的demo,看下效果
功能:
- 进入页面根据id播放列表对应的视频
- 列表视频之间播放切换
- 点击正在播放的视频进行暂停
- 点击列表播放按钮暂停,视频暂停
- 点击正在播放的视频,课程列表播放暂停
视频播放逻辑
/**
* 视频播放逻辑
* 一、点击按钮
* 1. 视频正在播放
* 1.1 如果是当前列表项,暂停播放,视频暂停播放
* item.play_status = false
* player.pause()
* 2. 视频未播放
* 2.1 播放的列表暂停播放,点击的列表进行播放
* 2.2 切换播放视频进行播放
* 二、点击播放器按钮
* 1. 视频正在播放
* 1.1 暂停播放,获取暂停状态,列表暂停播放
* 2. 视频停止/暂停播放
* 2.1 播放视频,列表对应按钮播放
*/
静态demo代码
1. 引入reset.css
body, h1, h2, h3, h4, p, dl, dd, ul, ol, form, input, textarea, th, td, select {
margin: 0;
padding: 0;
font-family: '微软雅黑';
}
em {
font-style: normal;
}
li {
list-style: none;
}
a {
text-decoration: none;
}
img {
border: none;
vertical-align: middle;
}
table {
border-collapse: collapse;
}
input, textarea {
outline: none;
}
textarea {
resize: none;
overflow: auto;
}
html{
font-size: 100px !important;
}
@media only screen and (min-width: 320px) {
html {
font-size: 85.4px !important
}
}
@media only screen and (min-width: 360px) {
html {
font-size: 96px !important
}
}
@media only screen and (min-width: 375px) {
html {
font-size: 100px !important
}
}
@media only screen and (min-width: 410px) {
html {
font-size: 110.4px !important
}
}
@media only screen and (min-width: 500px) {
html {
font-size: 133.4px !important
}
}
@media only screen and (min-width: 750px) {
html {
font-size: 200px !important
}
}
.nav-menu-open:checked ~ .nav-menu-bg {
-webkit-transform: scale(42) !important;
transform: scale(42) !important;
}
2. 引入rem.js的CDN(https://www.bootcdn.cn/rem/)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>播放课程</title>
<link rel="stylesheet" href="../common/css/reset.css">
<link rel="stylesheet" href="./courseList.css">
<link rel="stylesheet" href="./index.css">
</head>
<style>
[v-cloak] {
display: none;
}
</style>
<body>
<div id="app" v-cloak>
<div class="course-deatil-warper">
<div v-if="watch_type==1" class="movie-warper" id="mse"></div>
<div v-if="watch_type==0" class="course-image-text">
<div class="image-warper">
<img src="../agent/phoenixAcademy/phoenixAcademy/b-progressbar2@2x.png" alt="">
</div>
<div class="text-warper">
全面深化改革,新时代中国的重要命题。党的十八届三中全会以来,以习近平同志为核心的党中央总揽全局、统筹谋划,注重解决体制性的深层次障碍,推出一系列重大体制改革,有效解决了一批结构性矛盾,很多领域实现了历史性变革、系统性重塑、整体性重构;注重克服机制性的梗阻问题,打通理顺了许多堵点难点,增强了全社会发展活力和创新活力。
</div>
</div>
<div class="watcher-footer">
<div class="footer-title">01初识生殖美学</div>
<div class="footer-content">
<div class="footer-teacher">主讲老师:李四</div>
<div class="footer-watcher">
<img src="../agent/phoenixAcademy/phoenixAcademy/openoneseyes.png" alt="">
12131
</div>
</div>
</div>
</div>
<div class="course-list-warper">
<div class="course-list-title">课程列表</div>
<div class="course-list-box" v-for="(course,index) in courseList" :key="index">
<course-list @onplay="onPlay" :course="course" :index="index"></course-list>
</div>
</div>
</div>
</body>
<script src="https://cdn.bootcss.com/rem/1.3.4/js/rem.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="//cdn.jsdelivr.net/npm/xgplayer/browser/index.js" charset="utf-8"></script>
<script src="./index.js"></script>
</html>
3. js代码
Vue.component('courseList', {
props: {
course: Object,
index: Number
},
data: function () {
return {
pauseSrc: '../agent/phoenixAcademy/phoenixAcademy/player@pause.png',
playSrc: '../agent/phoenixAcademy/phoenixAcademy/player@play.png',
playing: false,
}
},
template: `
<div class="course-list" >
<div class="list-left">
<div class="item-img">
<img :src="course.img" alt="">
</div>
<div class="item-content">
<div class="item-index">{{index+1>9?index+1:'0'+(index+1)}}</div>
<div class="item-title">{{course.title}}</div>
<div class="item-play-times">
点击次数:{{course.play_times}}
</div>
<div class="item-teacher">主讲老师:{{course.teacher}}</div>
</div>
</div>
<div class="list-right" @click="onHandle(index,course)">
<img :src="course.play_status?pauseSrc:playSrc" alt="">
<div v-if="course.play_status" class="playing-text">正在播放</div>
</div>
</div>
`,
methods: {
onHandle(course_index, course) {
this.$emit('onplay', course_index, course)
}
},
})
let player = null;
var app = new Vue({
el: '#app',
data: {
watch_type: '1',
courseList: [{
id: 1,
img: '../agent/phoenixAcademy/phoenixAcademy/f-photograph@2x.png',
title: '初识生殖美学',
play_times: '3000',
teacher: 'zhangsan',
play_status: false,
url: 'https://st.wssqxt.com/pcImg-20191125195819.mp4',
}, {
id: 2,
img: '../agent/phoenixAcademy/phoenixAcademy/f-photograph@2x.png',
title: '初识生殖美学',
play_times: '3000',
teacher: 'zhangsan',
play_status: false,
url: 'https://st.wssqxt.com/pcImg-20191125211214.mp4'
}, {
id: 3,
img: '../agent/phoenixAcademy/phoenixAcademy/f-photograph@2x.png',
title: '初识生殖美学',
play_times: '3000',
teacher: 'zhangsan',
play_status: false,
url: 'https://st.wssqxt.com/pcImg-20191125195819.mp4',
}],
currentId: null,
currentIndex: null
},
methods: {
onPlay(course_index, course) {
const courseList = this.courseList
console.log(course_index, course.play_status);
// 当前是否播放
if (course.play_status) {
console.log('当前正在播放');
// 如果正在播放
player.pause()
this.currentId = course.id
this.currentIndex = course_index
courseList[course_index].play_status = false;
} else {
//note: 1.如果未播放
//是否是当前项
let isCurrentItem = course.id == this.currentId
if (isCurrentItem) {
//note: 如果是当前暂停项要进行再次播放
player.play()
courseList[course_index].play_status = true;
} else {
//note: 如果是要播放新的视频
console.log('列表中的其他视频播放');
// console.log(this.currentIndex);
courseList.forEach((item, index) => {
if (item.play_status) {
// 如果有正在播放的项,暂停播放
item.play_status = !item.play_status
}
// 切换当前项进行播放
if (course_index === index) {
item.play_status = !item.play_status
this.currentId = item.id
this._switchVideo(course.url)
}
});
}
}
},
_switchVideo(url) {
if (player) {
player.destroy();
}
setTimeout(() => {
player = null;
player = new Player({
id: 'mse',
url: url,
fluid: true,
});
player.start(url)
player.play()
}, 0);
},
_playVideo(url) {
player = new Player({
id: 'mse',
url: url,
fluid: true,
poster: 'http://st.wssqxt.com/pcImg-20191011173312.png'
});
player.start(url)
player.play()
},
navTo(course_index) {
console.log(course_index);
}
},
created() {
},
mounted() {
let id = 1;
this.courseList.forEach(item => {
// console.log(item);
// 如果获取的id和列表id相同,列表中的按钮处于播放状态
if (item.id == id) {
item.play_status = true;
this._playVideo(item.url)
}
});
}
})
4. less样式表
body {
font-size: 0.12rem;
background: #f0f0f0;
}
.reward-warpper {
position: relative;
padding-top: 0.2rem;
padding-bottom: 0.9rem;
background: linear-gradient(to right, #08abb2, #04bdc8, #00d0de);
z-index: -1;
.togger-btn-box {
display: flex;
justify-content: center;
align-content: center;
color: #ccc;
font-size: 0.16rem;
.btn-item {
// width: 25%;
margin: 0 0.1rem;
padding-bottom: 0.05rem;
text-align: center;
}
.active {
color: #fff;
border-bottom: 1px solid #fff;
}
}
}
.reward-panel {
position: relative;
width: 90%;
margin: 0 auto;
margin-top: -0.7rem;
z-index: 1;
padding: 0.1rem 0.1rem 0.3rem 0.1rem;
background: #fff;
.panel-date {
font-size: 0.12rem;
}
.panel-bounty {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 0.1rem;
.title {
margin-bottom: 0.1rem;
}
.money {
font-size: 0.25rem;
font-weight: bold;
margin-bottom: 0.1rem;
}
.opposite-money-warper {
padding: 0.03rem 0.1rem;
border-radius: 20px;
background: #fafafa;
font-size: 0.12rem;
.opposite-money {
margin: 0 0.03rem;
font-size: 0.16rem;
font-weight: 500;
color: red;
}
}
}
.grid-warpper {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
transition: all 2s;
.warper-item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
// width: 30%;
width: calc((100% - 24px)/3);
padding: 0.1rem 0.07rem;
margin: 0.05rem;
// background: #ccc;
.item-title {
color: #afaaaa;
}
.item-number {
margin-top: 0.1rem;
font-weight: 600;
}
}
}
.btn-warper {
position: absolute;
bottom: -0.16rem;
left: 50%;
margin-left: -0.2rem;
.btn {
padding: 0.06rem 0.1rem;
border-radius: 16px;
background: #fafafa;
}
}
}
.award-user-warper {
margin-top: 0.2rem;
.award-user-title {
padding: 0 0.1rem;
font-size: 0.14rem;
}
}
.award-list-box {
.award-list {
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.1rem;
background: #fff;
// border-bottom: 1px solid #000;
&::after {
content: '';
position: absolute;
bottom: 0;
background: #aba3a3;
width: 100%;
height: 1px;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
}
&:last-child {
&::after {
height: 0;
}
}
.list-left {
width: 80%;
.left-item {
display: flex;
// justify-content: space-around;
align-items: center;
.item-ranking {
margin-right: 0.1rem;
.ranking-img {
width: 0.1rem;
}
}
.item-user-img {
margin-right: 0.1rem;
img {
width: 0.45rem;
}
}
.item-user-info {
.user-name {
width: 1.5rem;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.user-cont {
margin-top: 0.1rem;
img {
width: 0.12rem;
// height: 0.15rem;
}
}
}
}
}
.list-right {
flex: 1;
display: flex;
justify-content: space-between;
align-items: center;
.right-cont {
margin-right: 0.05rem;
font-size: 0.16rem;
font-weight: 500;
color: #000;
}
.right-img {
margin-bottom: 0.03rem;
img {
width: 0.05rem;
}
}
}
}
}
5. 遇到的坑
1. 西瓜播放器切换视频
官网写的老的切换方式:
(1)先销毁视频,
player.destroy(isDelDom)
(2)然后重新创建player实例
new Player({
id:'mse',
url:'/mp4/'
})
(3)启动播放器,播放视频
player.start(url)
player.play()
我写完了之后重新整理笔记,看到官网有更好的api切换视频,暂时还没有试过.
player.src = 'new url'
项目实战
项目中的课程播放要比这个demo复杂的多,涉及到视频播放,图文播放,视频图文的切换播放
看下项目中的效果
功能:
- 点击播放课程,进入播放页面立即播放课程
- 图文课程和视频课程的切换播放
- 视频课程切换到图文课程要暂停播放视频
- 图文课程一直处于播放状态,不可暂停
- 课程列表分页
- 记录播放次数
- 点击播放一次就算播放一次
- 记录培训进度
- 图文课程点击进入就算此课程已看完,进行培训记录
- 视频课程点击播放,播放结束表示课程已看完,进行培训记录
项目课程播放逻辑
开始想的是获取课程列表,然后前台筛选判断应该播放哪一项,但是考虑到加载分页时会影响当前播放的视频,所以分页列表和正在播放视频分成了两个接口,并且前台需要处理的只是逻辑判断,不需要遍历操作整个列表,简化了查询匹配课程播放的问题.
/**
* 初始化播放:
* 通过query获取id,请求要播放的视频
* 判断当前的播放类型
* 1. 如果历史播放和现在播放都是图文
* 如果点击项id和历史id相同,则不作任何事情
* 否则,请求后台,播放图文
* 2. 如果历史播放是图文,现在播放是视频
* 请求后台,播放视频
* 3. 如果历史播和现在播放都是视频
* 如果正在播放的视频url和点击项视频url相同
* 如果选择视频正在播放,则暂停播放
* 否则播放视频
* 否则请求后台播放新的视频
* 4. 如果历史播放是视频,现在播放是图文
* 销毁视频播放实例
* 请求后台播放图文
* 5. 如果加载分页,当前有正在播放的视频,则不请求播放接口
*/
在视频播放中需要监听视频的暂停,继续,结束状态.
<!DOCTYPE html>
<html>
<head lang="en">
<title id="title">播放课程</title>
<#include "../../../../common/header.ftl"/>
<link rel="stylesheet" href="/static/css/chaozhimei/agent/phoenixAcademy/courseList/courseList.css?${version}" />
<link rel="stylesheet" href="/static/css/chaozhimei/agent/phoenixAcademy/playCourse/playCourse.css?${version}" />
<script src="/static/js/vue/vue.min.js"></script>
</head>
<style>
[v-cloak] {
display: none;
}
</style>
<body>
<div id="app" v-cloak>
<div class="course-deatil-warper" v-show="watch_type==1">
<div class="movie-warper" id="mse">
</div>
<div class="watcher-footer">
<div class="footer-title">{{playingCourseData.sort>9?playingCourseData.sort:'0'+(playingCourseData.sort)}}
{{playingCourseData.title}}</div>
<div class="footer-content">
<div class="footer-teacher">{{playingCourseData.summary}}</div>
<div class="footer-watcher">
<img src="/static/img/chaozhimei/agent/phoenixAcademy/openoneseyes.png" alt="">
{{playingCourseData.is_read}}
</div>
</div>
</div>
</div>
<div class="course-graphic-warper" v-show="watch_type==0">
<div class="course-image-text" v-html="graphicCourseData.content"></div>
<div class="watcher-footer">
<div class="footer-title">
{{graphicCourseData.sort>9?graphicCourseData.sort:'0'+(graphicCourseData.sort)}}
{{graphicCourseData.title}}
</div>
<div class="footer-content">
<div class="footer-teacher">{{graphicCourseData.summary}}</div>
<div class="footer-watcher">
<img src="/static/img/chaozhimei/agent/phoenixAcademy/openoneseyes.png" alt="">
{{graphicCourseData.is_read}}
</div>
</div>
</div>
</div>
<#-- :style="watch_type==1?'margin-top: 2.8rem;' : 'margin-top: 0.1rem;'" -->
<div class="course-list-warper" >
<div class="course-list-title">课程列表</div>
<div class="course-list-box" v-for="(course,index) in courseList" :key="index">
<course-list @onplay="onPlay" :course="course" :index="index"></course-list>
</div>
</div>
</div>
<script type="text/javascript" src="/static/js/chaozhimei/common/utils.js"></script>
<script src="//cdn.jsdelivr.net/npm/xgplayer/browser/index.js" charset="utf-8"></script>
<script src="//cdn.jsdelivr.net/npm/xgplayer-mp4/browser/index.js" charset="utf-8"></script>
<script type="text/javascript" src="/static/js/chaozhimei/agent/phoenixAcademy/playCourse/playCourse.js?${version}"></script>
</body>
</html>
removejscssfile('/static/css/indexAll.css', 'css');
Vue.component('courseList', {
props: {
course: {
type: Array,
required: true
},
index: Number
},
data: function () {
return {
pauseSrc: '/static/img/chaozhimei/agent/phoenixAcademy/player@pause.png',
playSrc: '/static/img/chaozhimei/agent/phoenixAcademy/player@play.png',
playing: false,
}
},
template: `
<div class="course-list" >
<div class="list-left">
<div class="item-img">
<img :src="course.logo_url" alt="">
</div>
<div class="item-content">
<!-- <div class="item-index">{{index+1>9?index+1:'0'+(index+1)}}</div>-->
<div class="item-index">{{course.sort>9?course.sort:'0'+(course.sort)}}</div>
<div class="item-title">{{course.title}}</div>
<div class="item-play-times">
点击次数:{{course.is_read}}
</div>
<div class="item-teacher">{{course.summary}}</div>
</div>
</div>
<div class="list-right" @click="onHandle(index,course)">
<img :src="course.play_status?pauseSrc:playSrc" alt="">
<div v-if="course.play_status" class="playing-text">正在播放</div>
</div>
</div>
`,
methods: {
onHandle(course_index, course) {
this.$emit('onplay', course_index, course)
}
},
});
let player = null;
let initNum = 0;
let params = {
pageNo: 1,
pageSize: 2,
typeId: ''
};
let classId = null;
let playHistoryType = null;
let playHistoryId = null;
var app = new Vue({
el: '#app',
data: {
watch_type: 1, //0:图文课程 1:视频课程
courseList: [],
currentId: null, // 当前播放id
graphicCourseData: '', // 图文课程
playingCourseData: {}, // 播放的课程
},
methods: {
onPlay(course_index, course) {
utils.toTop();
// console.log(course.videourl);
if (course.videourl=='') {
this.watch_type = 0
} else {
this.watch_type = 1
}
// note:如果历史播放和现在播放都是图文
if (playHistoryType ==0 && this.watch_type == 0) {
console.log('都是图文');
if(classId==course.id) {
console.log('选择重复项了');
return
}
classId = course.id;
// getPlayingCourse();
collegeRead(course);
}
// TODO:如果历史播放是图文,现在播放是视频
if (playHistoryType == 0 && this.watch_type == 1) {
console.log('播放视频');
classId = course.id;
// getPlayingCourse();
collegeRead(course);
// this._switchVideo(course.videourl);
return
}
// TODO:如果历史播和现在播放都是视频
if (playHistoryType ==1 && this.watch_type == 1) {
console.log('都是视频播放');
// 如果当前视频src和点击src相同,( 继续播放
if (this.playingCourseData.videourl == course.videourl) {
console.log('选择的是当前视频');
// 如果现在正在播放,( 暂停播放
if (course.play_status) {
console.log('正在播放');
player.pause();
return;
}
// 如果现在没有播放, (继续播放
player.play();
return
}
classId = course.id;
// getPlayingCourse();
collegeRead(course);
// this._switchVideo(course.videourl)
}
// TODO:如果历史播放是视频,现在播放是图文,( 暂停播放
if (playHistoryType == 1 && this.watch_type == 0) {
// this.currentId = course.id;
console.log('播放图文');
// console.log(player);
if (player!=null&&player) {
player.destroy();
player = null
}
classId = course.id;
// getPlayingCourse();
collegeRead(course);
return;
}
},
_switchVideo(url) {
if (player) {
player.destroy();
}
setTimeout(() => {
player = null;
player = new Player({
id: 'mse',
url: url,
fluid: true,
});
//note: 播放器销毁并再次初始化后,需要先调用start方法
player.start(url);
player.play();
this._monitorPlaying();
this._monitorPause();
this._monitorEnded();
this._monitorDestroy();
}, 0);
},
_playVideo(url) {
// console.log(url)
if (player) {
player.destroy();
}
setTimeout(() => {
player = null;
player = new Player({
id: 'mse',
url: url,
fluid: true,
});
//note: 播放器销毁并再次初始化后,需要先调用start方法
player.start(url);
player.play();
player.once('ready', () => {
console.log('启动')
});
this._monitorPlaying();
this._monitorPause();
this._monitorEnded();
// this._monitorDestroy();
}, 0);
},
// 监听继续播放
_monitorPlaying() {
player.on('playing', () => {
this.courseList.forEach(item => {
if (item.id == classId) {
item.play_status = true;
}
});
})
},
// 监听暂停播放
_monitorPause() {
player.on('pause', () => {
console.log('暂停');
this.courseList.forEach(item => {
if (item.id == classId) {
item.play_status = false;
}
});
})
},
// 监听结束播放
_monitorEnded() {
player.on('ended', () => {
console.log('结束')
updateUserCourseSchedule()
});
},
// 监听销毁
_monitorDestroy() {
player.on('destroy', () => {
console.log('destroy')
this.courseList.forEach(item => {
if (item.id == classId) {
item.play_status = 0;
}
});
})
},
},
mounted() {
loadStart();
params.typeId = utils.getQueryVariable('typeId');
classId = utils.getQueryVariable('classId');
// this.currentId = classId;
initUserList();
}
});
// 获取正在播放课程
function initPlayingCourse() {
let isPlaying = app.courseList.some(item=>item.play_status == 1);
console.log(isPlaying);
if(isPlaying) return ;
getPlayingCourse();
}
function getPlayingCourse() {
$.ajax({
url: '/college/getCourse',
type: 'POST',
dataType: 'json',
data: {
id: classId
},
success: function (res) {
// console.log(res.data)
let courseData = res.data;
if (res.code == 0) {
// console.log(courseData.videourl)
if(!courseData.videourl) {
// TODO: 图文课程
// console.log('图文课程')
app.watch_type = 0;
playHistoryType = 0;
playHistoryId = classId;
app.graphicCourseData = courseData;
app.courseList.forEach(item => {
item.play_status = 0;
if (item.id == classId) {
item.play_status = 1;
// 图文教程默认请求到就是培训完成
updateUserCourseSchedule();
}
});
return
}
// TODO: 视频课程
app.watch_type = 1;
playHistoryType = 1;
playHistoryId = classId
// console.log('视频课程')
app._playVideo(courseData.videourl);
app.playingCourseData = courseData;
// 如果获取的id和列表id相同,列表中的按钮处于播放状态
app.courseList.forEach(item => {
item.play_status = 0;
if (item.id == classId) {
item.play_status = 1;
}
});
} else {
if (res.message) {
layer.msg(res.message);
}
}
},
error: function () {
layer.msg("网络繁忙");
},
complete: function () {
loadComplete();
}
})
}
// 初始化加载分页
function initUserList() {
initNum++;
var initIndex = initNum;
// console.log(initIndex)
$('.layui-flow-more').remove();
getPageDatas('#app', function (page, next) {
// console.log(page)
if (initIndex != initNum) {
return;
}
getClassList(page, function (pages) {
next(null, page < pages);
});
});
}
// 获取课程列表
function getClassList(page, callback) {
params.pageNo = page;
$.ajax({
url: '/college/selectCollegeDetails',
type: 'POST',
dataType: 'json',
data: params,
success: function (res) {
// console.log(res)
if (res.code == 0) {
callback(res.data.pages);
app.courseList = app.courseList.concat(res.data.list);
// console.log(JSON.stringify(app.courseList))
initPlayingCourse();
} else {
if (res.message) {
layer.msg(res.message);
}
callback(1);
}
},
error: function () {
layer.msg("网络繁忙");
},
complete: function () {
loadComplete();
}
})
}
// 记录播放次数
function collegeRead(course) {
$.ajax({
url: `/college/read/${course.id}`,
type: 'POST',
dataType: 'json',
data: {
id: course.id
},
success: function (res) {
// console.log(res)
if (res.code == 0) {
// window.location.href = `/view/user/play/course?classId=${course.id}&typeId=${course.train_typeid}`;
getPlayingCourse();
app.courseList.forEach(item=> {
if(item.id==course.id) {
item.is_read++
}
})
} else {
if (res.message) {
layer.msg(res.message);
}
}
},
error: function () {
layer.msg("网络繁忙");
},
complete: function () {
loadComplete();
}
})
}
// 记录培训进度
function updateUserCourseSchedule() {
$.ajax({
url: `/college/updateUserCourseSchedule`,
type: 'POST',
dataType: 'json',
data: {
id: classId
},
success: function (res) {
// console.log(res)
if (res.code == 0) {
// console.log('完成课程')
} else {
if (res.message) {
layer.msg(res.message);
}
}
},
error: function () {
layer.msg("网络繁忙");
},
complete: function () {
loadComplete();
}
})
}