You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

886 lines
20 KiB

<template>
<div v-wechat-title="'正在播放' + play_title"></div>
<appHeader />
<div v-if="!com_user_token">
<el-empty :image-size="200" description=" ">无权限查看,当前页面需要 <a :href="com_login_url" class="col008">登录</a></el-empty>
</div>
<div v-if="isAuth && com_user_token">
>
<div class="main">
<div class="videoBox flex-2">
<div class="viewtil" style="min-height:8%">
<el-page-header @back="goBack" title="返回班级" :content="video.title"> </el-page-header>
</div>
<div :class="{ affix: is_scroll }"></div>
<div id="VideoPlay" style=" width:100%; height: 92%; margin-top: auto;" :class="{ affixcss: is_scroll }"></div>
<div class="autotime" v-show="showAutoBox">
<div class="pssbox flex-5">
<div class="pos-box">
<div class="txt">本节视频已看完</div>
<div v-if="playThisId != playNextId && playNextId != 0">
<div class="txt_next">
下一节:<span class="fw600">{{ playNextTitle }}</span>
</div>
<div class="sel_btn_box flex-3">
<div class="btn_sty" @click="getvideo_click(playThisId, 1)">重播</div>
<div class="btn_sty" @click="getvideo_click(playNextId)">播放下一节</div>
</div>
</div>
<div v-else>
<div class="txt_next">已是最后一节</div>
<div class="sel_btn_box flex-3">
<div class="btn_sty" style="margin: 0 auto;" @click="getvideo_click(playThisId, 1)">重播</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="navbox" id="videoList">
<div class="classtil m-dn">
<router-link :to="'/class/' + classid">{{ video.className }}</router-link>
</div>
<div class="cate">
<i class="iconfont icon-mulu"></i>
<span>课程章节</span>
</div>
<el-scrollbar style="height: 86%; overflow: hidden;" ref="myscroll">
<div style="padding-bottom: 40px;">
<div class="looplist" v-for="(item, i) in lessonList" :key="i">
<div class="otile hide-txt">{{ item.title }}</div>
<div v-if="item.video.length === 0" class="novideo col617">等待老师发布...</div>
<ul class="vlist">
<li class="item" v-for="(item, i) in item.video" :key="i">
<!-- <el-tooltip :content="item.title" placement="left" effect="light"> -->
<a class="a-play flex-1" href="javascript:;" @click="getvideo_click(item.id)" :class="{ playcur: playcur == item.id }">
<img src="@/assets/img/playing.gif" width="18" v-if="playcur == item.id" />
<i class="iconfont icon-bofang2" v-else></i>
<div class="title hide-txt">{{ item.title }}</div>
<div v-if="isMobile" class="time">
<div v-if="item.play_progress" class="f12">
<div v-if="item.play_progress === 100" class="col52c">已看完</div>
<div v-else class="colffa">已看 {{ item.play_progress }}%</div>
</div>
<div v-else>{{ item.videoTime }}</div>
</div>
<div class="time" v-else>{{ item.videoTime }}</div>
</a>
<!-- </el-tooltip> -->
</li>
</ul>
</div>
</div>
</el-scrollbar>
</div>
</div>
</div>
<div v-else>
<el-empty :image-size="200" :description="msg" v-if="empty"></el-empty>
</div>
</template>
<script>
import appHeader from "@/components/Header";
import { useRoute, useRouter } from "vue-router";
import { getVideo, getPlayList, classlog } from "@/api/study";
import { toRefs, reactive, onMounted, ref, onUnmounted } from "vue";
export default {
name: "classPlay",
components: {
appHeader,
},
setup() {
const route = useRoute();
const router = useRouter();
const classid = route.params.classid;
const videoid = route.params.videoid;
const state = reactive({
isAuth: true,
msg: 0,
empty: false,
activeIndex: 1,
video: {},
arrVideo: [],
lessonList: [],
videoObject: {
container: "#VideoPlay",
variable: "player",
autoplay: false,
html5m3u8: true,
subtitle: false,
loop: false,
//loaded: 'loadHandler',//加载播放器后调用的函数
video: "",
seek: "0",
//debug:true,
},
playcur: 0,
logid: 0,
videoid: 0,
videopid: 0,
play_title: "",
ckPlayer: null,
playIndex: 0,
playNextId: 0,
playNextTitle: 0,
myautoTime: 5,
tmProgress: 0,
timerTxt: null,
timerPrss: null,
showAutoBox: false,
is_duration: true,
playThisId: route.params.videoid,
is_scroll: false,
isMobile: document.body.clientWidth < 1200,
});
window.addEventListener("resize", function() {
state.isMobile = document.body.clientWidth < 1200;
});
// 获取视频 待删 MARKET-4168
// const resVideoData = async () => {
// const res = await getVideo(classid, videoid);
// if (res.code === 200) {
// if (res.data.playtime > 0) {
// state.videoObject.seek = res.data.playtime;
// }
// state.msg = res.message;
// if (res.status === -1) {
// state.isAuth = false;
// state.empty = true;
// } else {
// state.isAuth = true;
// }
// state.video = res.data;
// state.play_title = res.data.title;
// state.videoObject.video = res.data.videourl;
// state.playcur = videoid;
// state.logid = res.data.logid;
// state.videoid = res.data.id;
// state.videopid = res.data.it618_pid;
// }
// };
// resVideoData();
const goBack = () => {
router.push("/class/" + classid);
};
// 埋点请求
const postlogData = async (logData) => {
const getLogdata = state.arrVideo.filter((t) => t.id == state.videoid);
await classlog(logData, getLogdata[0].logdata);
};
// 列表点击切换视频
const getvideo_click = async (id, replay = 0) => {
state.playThisId = id;
state.is_duration = false;
clearInterval(state.timerTxt);
clearInterval(state.timerPrss);
state.showAutoBox = false;
state.ckPlayer.videoClear();
state.ckPlayer = null;
document.getElementById("VideoPlay").innerHTML = "";
setTimeout(function() {
getvideoDom(id, replay);
getNextId(id);
state.videoObject.autoplay = true;
state.videoObject.seek = 0;
}, 500);
};
/**
* 封装视频渲染
* @replay 1=重播
* @resOne 1=第一次进入页面播放
**/
const getvideoDom = async (id, replay = 0, resOne = 0) => {
// 获取视频
const res = await getVideo(classid, id);
if (res.code === 200) {
state.playcur = id;
state.video = res.data;
state.play_title = res.data.title;
state.videoObject.video = res.data.videourl;
state.arrVideo.push(res.data);
if (res.data.playtime > 0) {
state.videoObject.seek = res.data.playtime;
}
if (replay == 1) {
state.videoObject.seek = 0;
}
state.msg = res.message;
if (res.status === -1) {
state.isAuth = false;
state.empty = true;
} else {
state.isAuth = true;
}
}
// 实例化播放器
state.ckPlayer = new ckplayer(state.videoObject);
// 开启duration计时器
state.is_duration = true;
// 监听播放时间
state.ckPlayer.addListener("time", timeHandler);
function timeHandler(t) {
let tm = parseInt(t);
localStorage.setItem(id + "_playTime", tm);
}
// 监听播放和暂停 实现计时功能
state.ckPlayer.addListener("play", playHandler);
function playHandler() {
myCounter.startCount();
}
// 监听空格
state.ckPlayer.addListener("pause", pauseHandler);
function pauseHandler() {
myCounter.stopCount();
}
// 监听播放结束
state.ckPlayer.addListener("ended", endedHandler);
function endedHandler() {
myCounter.stopCount();
// funAutoPlay();
state.showAutoBox = true;
}
//自定义计时器
const myCounter = (function() {
var step = 0;
var intervalId = null;
function count() {
localStorage.setItem(id + "_duration", parseInt(step));
step = step + 1;
// 通过状态改变来关闭计时器
if (state.is_duration === false) {
clearInterval(intervalId);
intervalId = null;
//console.log('count',state.is_duration);
}
}
function start() {
if (intervalId == null) {
count();
intervalId = setInterval(count, 1000);
}
}
function stop() {
clearInterval(intervalId);
intervalId = null;
}
return { startCount: start, stopCount: stop };
})();
// 如果没播放权限清除视频
if (state.isAuth === false) {
state.ckPlayer.videoClear();
state.ckPlayer = null;
document.getElementById("VideoPlay").innerHTML = "";
}
// 发送埋点 切换视频发送 第一次播放不发送
let get_duration = localStorage.getItem(state.videoid + "_duration");
if (resOne != 1 && get_duration) {
let logData = {
logid: state.logid,
pytime: localStorage.getItem(state.videoid + "_playTime"),
duration: localStorage.getItem(state.videoid + "_duration"),
videoid: state.videoid,
};
postlogData(logData);
}
// 重置新id
state.logid = res.data.logid;
state.videoid = res.data.id;
console.log("log_videoid", state.videoid);
// 修改路由
let winState = { url: id };
window.history.pushState(winState, "", id);
return state;
};
// 播放列表
const resLessonList = async () => {
const res = await getPlayList(classid);
if (res.code === 200) {
state.lessonList = res.data;
state.msg = res.message;
// 下一个视频id
getNextId(videoid);
if (res.data.videoClassName) {
state.video.videoClassName = res.data.videoClassName;
state.videopid = res.data.pid;
}
if (res.status === -1) {
state.isAuth = false;
state.empty = true;
} else {
state.isAuth = true;
}
}
};
resLessonList();
// 返回下标下输出一个id
const getNextId = async (id) => {
state.lessonList.forEach((item, parent) => {
state.lessonList[parent].video.forEach((item, child) => {
if (item.id == id) {
if (state.lessonList[parent].video.length == child + 1) {
getNextIdarr(parent + 1, 0);
} else {
getNextIdarr(parent, child + 1);
}
}
});
});
};
// 查找下一个视频
const getNextIdarr = async (parent, child) => {
//console.log(parent, child);
state.lessonList.forEach((item, index) => {
if (index == parent) {
state.lessonList[index].video.forEach((item, myindex) => {
if (myindex === child) {
state.playNextId = item.id;
state.playNextTitle = item.title;
//console.log(state.playNextId);
}
});
}
});
};
const myscroll = ref(null);
onMounted(() => {
var new_element = document.createElement("script");
new_element.setAttribute("type", "text/javascript");
new_element.setAttribute("src", "/js/ckplayerX2/ckplayer.min.js?v=2.1");
document.body.appendChild(new_element);
// 第一个视频的渲染 第一个视频不发送时长埋点
getvideoDom(videoid, 0, 1);
setTimeout(function() {
// 滚动条自动定位
let playcur = document.getElementsByClassName("playcur")[0] || 0;
let gettop = playcur.offsetTop;
myscroll.value.wrap.scrollTop = gettop - 200;
}, 1000);
window.onbeforeunload = function() {
// 定义对象
let logData = {
logid: state.video.logid,
pytime: localStorage.getItem(state.videoid + "_playTime"),
duration: localStorage.getItem(state.videoid + "_duration"),
videoid: state.videoid,
};
postlogData(logData);
};
// 监听a点击执行埋点
var tbody = document.getElementById("myheader");
tbody.onclick = function(e) {
myaddEvent(e);
};
var videolista = document.getElementById("videoList");
videolista.onclick = function(e) {
myaddEvent(e);
};
function myaddEvent(e) {
e = e || event;
var target = e.target || e.srcElement.href;
e.preventDefault();
if (target.tagName == "A") {
//console.log(target.href);
// 定义对象
let logData = {
logid: state.video.logid,
pytime: localStorage.getItem(state.videoid + "_playTime"),
duration: localStorage.getItem(state.videoid + "_duration"),
videoid: state.videoid,
};
// 发送埋单请求
postlogData(logData);
setTimeout("top.location.href = '" + target.href + "'", 100);
}
}
window.addEventListener("scroll", function() {
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
if (scrollTop > 50) {
state.is_scroll = true;
} else {
state.is_scroll = false;
}
});
});
onUnmounted(() => {
// 定义对象
let logData = {
logid: state.video.logid,
pytime: localStorage.getItem(state.videoid + "_playTime"),
duration: localStorage.getItem(state.videoid + "_duration"),
videoid: state.videoid,
};
postlogData(logData);
});
return {
...toRefs(state),
classid,
videoid,
getvideoDom,
getvideo_click,
myscroll,
goBack,
};
},
};
</script>
<style lang="scss" scoped>
html,
body {
overflow-y: hidden;
}
.autotime {
position: absolute;
z-index: 82;
width: 100%;
height: 100%;
text-align: center;
background-color: rgb(0, 0, 0, 0.7);
backdrop-filter: saturate(180%) blur(20px);
-webkit-backdrop-filter: saturate(180%) blur(20px);
z-index: 900;
}
.pssbox {
height: 100%;
.pos-box {
margin-top: 52px;
}
.txt {
color: #fff;
font-size: 24px;
padding-bottom: 24px;
}
.txt_next {
color: #fff;
font-size: 16px;
margin-bottom: 36px;
}
.sel_btn_box {
width: 310px;
margin: 0 auto;
.btn_sty {
width: 46%;
height: 40px;
line-height: 40px;
color: #fff;
text-align: center;
font-size: 16px;
border: 1px solid #fff;
border-radius: 6px;
cursor: pointer;
&:hover {
background-color: #0082fc;
border: 1px solid #0082fc;
}
}
}
.clbtn {
cursor: pointer;
color: #fff;
padding-top: 14px;
}
.clbtn:hover {
color: #0082fc;
}
}
.autotime-value {
width: 70px;
height: 70px;
border-radius: 100px;
margin-left: 5px;
font-size: 22px;
color: #fff;
}
.nullData {
position: absolute;
z-index: 82;
color: #b3c1d3;
font-size: 24px;
width: 100%;
text-align: center;
}
.status_1,
.status_2,
.status_3 {
padding: 0 10px 0 4px;
}
.status_1 {
color: #ff3c23;
}
.status_2 {
color: #67c23a;
}
.status_3 {
color: #0082fc;
}
.main {
position: absolute;
top: 96px;
left: 0;
bottom: 0;
right: 0;
background-color: #000;
}
.mbox {
position: relative;
width: 1200px;
height: 100%;
margin: 0 auto;
overflow: hidden;
}
.videoBox {
position: absolute;
left: 0;
width: 80%;
height: 100%;
}
.viewtil {
color: #fff;
font-size: 18px;
padding: 16px 20px;
}
.navbox {
position: absolute;
right: 0;
width: 20%;
height: 100%;
overflow: hidden;
background-color: #0f0f0f;
}
.classtil {
font-size: 22px;
color: #eee;
background-color: #000;
padding: 14px 20px 0 20px;
font-weight: 600;
a {
color: #eee;
}
a:hover {
color: #0082fc;
}
}
.cate {
color: #eee;
padding: 14px 20px;
border-bottom: 1px solid #333;
background-color: #000;
span {
padding-left: 6px;
}
}
.looplist {
padding: 0 20px;
.otile {
padding: 16px 0 4px;
font-size: 15px;
font-weight: 600;
color: #8496ab;
}
.item {
font-size: 14px;
color: #617288;
a {
padding: 10px 0;
width: 100%;
color: #617288;
.title {
padding-left: 6px;
width: 80%;
}
.time {
margin-left: auto;
}
}
a:hover {
color: #0082fc;
}
.playcur {
color: #0082fc;
}
.playing {
width: 16px;
height: 16px;
}
}
}
.goback {
display: inline-block;
font-size: 15px;
color: #b3c1d3;
padding: 4px 20px;
border-radius: 100px;
background-color: #465266;
color: #fff;
}
.goback:hover {
background-color: #617288;
}
</style>
<style>
.el-scrollbar__bar.is-vertical {
width: 8px;
}
.el-scrollbar__thumb {
background-color: #617288;
}
.el-page-header__content {
color: #fff;
}
.el-page-header__left:hover {
color: #0082fc;
}
</style>
<style lang="scss">
/*响应式-移动端*/
@import "~@/assets/css/m.css";
@media screen and (max-width: 1200px) {
.affix {
height: 35vh;
background-color: #fff;
}
.affixcss {
position: fixed !important;
top: 0 !important;
z-index: 8;
}
.main {
position: static !important;
.flex-2 {
display: block !important;
}
.autotime {
height: 35vh;
top: 50px;
}
#VideoPlay {
height: 35vh !important;
}
.videoBox {
position: static !important;
width: auto !important;
.viewtil {
position: absolute;
top: 0;
left: 0;
z-index: 99;
background-color: #fff;
color: #333;
height: 50px;
padding-top: 13px !important;
min-height: auto !important;
overflow: hidden;
.el-page-header__left {
min-width: 80px;
}
.el-page-header__content {
color: #333;
flex: 1;
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
.navbox {
position: static !important;
width: 100%;
background-color: #fff;
.cate {
background-color: #fff;
color: #333;
border-bottom: solid 1px #ceddf2;
}
}
.looplist {
.otile {
border-bottom: solid 1px #ceddf2;
padding-bottom: 12px;
color: #333;
}
.vlist {
.item {
margin-left: 20px;
border-bottom: solid 1px #ceddf2;
color: #333;
.icon-bofang2 {
color: #b3c1d3 !important;
}
}
}
}
}
.nullData {
position: absolute;
z-index: 999999 !important;
top: 20vh;
font-size: 16px !important;
.ff18 {
font-size: 14px !important;
}
.medium {
padding: 6px 12px !important;
}
}
.m-footr {
margin-top: 0;
}
}
</style>
<style lang="scss">
@media screen and (max-width: 700px) {
.main {
.videoBox {
.viewtil {
width: 100%;
}
}
.looplist {
.otile {
border-bottom: solid 1px #ceddf2;
padding-bottom: 12px;
color: #333;
}
.vlist {
.item {
margin-left: 10px !important;
border-bottom: solid 1px #ceddf2;
color: #333;
.icon-bofang2 {
color: #b3c1d3 !important;
}
a {
padding: 10px 0;
width: 100%;
color: #617288;
.title {
padding-left: 6px;
width: 77% !important;
}
.time {
margin-left: auto;
}
}
}
}
}
}
}
</style>