|
@@ -0,0 +1,316 @@
|
|
|
+<template>
|
|
|
+ <div class="weixin-body">
|
|
|
+
|
|
|
+ <div class="weixin-content-box">
|
|
|
+ <div class="weixin-title">{{media.name|| tpl.name}}</div>
|
|
|
+ <div class="weixin-content">
|
|
|
+ <video-player
|
|
|
+ id="myVideo1"
|
|
|
+ ref="videoPlayer"
|
|
|
+ :playsinline="true"
|
|
|
+ @pause="onPlayerPause($event)"
|
|
|
+ @timeupdate="onPlayerTimeupdate($event)"
|
|
|
+ @play="onPlayerStart($event)"
|
|
|
+ @ready="playerReadied"
|
|
|
+ @canplay="canpay($event)"
|
|
|
+ @ended="onPlayerEnded($event)"
|
|
|
+ :globalOptions="{controls:true}"
|
|
|
+ :options="options">
|
|
|
+ </video-player>
|
|
|
+ <div style="font-size: 16px;; margin: 10px auto;text-align: center;">
|
|
|
+ <span>{{curTimes|useTime}}</span>
|
|
|
+ <strong>/</strong>
|
|
|
+ <span>{{media.duration|useTime}}</span>
|
|
|
+ <span v-if="media.isFinish==1" style="color: green;">已完成</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- <div style="width: 100%; margin: 0px auto;"> -->
|
|
|
+ <div class="weixin-content-box" style="padding: 4px;">
|
|
|
+ <div class="weixin-content" v-for="item in list" v-if="item.isFinish==0">
|
|
|
+ <div class="weixin-course-name">
|
|
|
+ <el-button @click="loadMedia(item)" type="text">{{item.name}} </el-button>
|
|
|
+ </div>
|
|
|
+ <ul class="weixin-ul-info clear">
|
|
|
+ <li class="weixin-info">
|
|
|
+ <span>学时:</span>
|
|
|
+ <span>{{item.xs/10}}</span>
|
|
|
+ </li>
|
|
|
+ <li class="weixin-info">
|
|
|
+ <span>学习进度:</span>
|
|
|
+ <span>{{(item.position*100/item.duration).toFixed(2)}}%</span>
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+<script>
|
|
|
+ import { mapGetters,mapActions } from "vuex";
|
|
|
+ import {getPercent} from '@/utils/index.js'
|
|
|
+ import {
|
|
|
+ videoPlayer
|
|
|
+ } from 'vue-video-player';
|
|
|
+ // import 'video.js/dist/video-js.css'
|
|
|
+ import {
|
|
|
+ httpServer
|
|
|
+ } from "@/components/httpServer/httpServer.js";
|
|
|
+ export default {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ id: '',
|
|
|
+ media:{},
|
|
|
+ options:{
|
|
|
+ controls:true,
|
|
|
+ autoplay: true, // 如果true,浏览器准备好时开始回放。
|
|
|
+ muted: true, // 默认情况下将会消除任何音频。
|
|
|
+ loop: false, // 导致视频一结束就重新开始。
|
|
|
+ preload: "auto", // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
|
|
|
+ language: 'zh-CN',
|
|
|
+ aspectRatio: '4:3', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
|
|
|
+ fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
|
|
|
+ sources: [],
|
|
|
+ poster: '', // 你的封面地址
|
|
|
+ notSupportedMessage: '无法播放媒体源', // 允许覆盖Video.js无法播放媒体源时显示的默认信息。
|
|
|
+ playtimes: '',
|
|
|
+ controlBar: {
|
|
|
+ timeDivider: true,
|
|
|
+ durationDisplay: true,
|
|
|
+ remainingTimeDisplay: false,
|
|
|
+ fullscreenToggle: true //全屏按钮
|
|
|
+ }
|
|
|
+ },
|
|
|
+ curTimes:0,
|
|
|
+ tpl:{},
|
|
|
+ info: {},
|
|
|
+ list: [],
|
|
|
+ timer: false,
|
|
|
+ tickNum: 0,
|
|
|
+ prevTime: 0,
|
|
|
+ isReady: false,
|
|
|
+ onPlay: false
|
|
|
+ };
|
|
|
+ },
|
|
|
+ components: {
|
|
|
+ videoPlayer
|
|
|
+ },
|
|
|
+ filters:{
|
|
|
+ useTime(val) {
|
|
|
+ val = parseInt(val)||0
|
|
|
+ let timestr = ""
|
|
|
+ let hour = parseInt(val / 3600);
|
|
|
+ let min = parseInt(val / 60 % 60);
|
|
|
+ let sec = parseInt(val % 60);
|
|
|
+ if (hour < 10) hour = "0" + hour;
|
|
|
+ if (min < 10) min = "0" + min;
|
|
|
+ if (sec < 10) sec = "0" + sec;
|
|
|
+ return hour + ":" + min + ":" + sec
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ ...mapActions("user", ["loadBaseInfo"]),
|
|
|
+ getCourse() {
|
|
|
+ let param = { courseId: this.courseId }
|
|
|
+ httpServer("course.getCourse", param).then(res => {
|
|
|
+ if (res.code == 200) {
|
|
|
+ let {info, extra, list, tpl, extraXs, examList} = res.data;
|
|
|
+ this.info = Object.assign( info, extra||{});
|
|
|
+ this.tpl = tpl||{};
|
|
|
+ this.list = list.map( (item)=>{
|
|
|
+ item.percent = getPercent(item)||0;
|
|
|
+ return item;
|
|
|
+ })
|
|
|
+ for( let i in this,list){
|
|
|
+ if( this.list[i].mediaId == this.userInfo.mediaId){
|
|
|
+ this.loadMedia( this.list[i])
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ startTick() {
|
|
|
+ let tick = this.tryTick;
|
|
|
+ if (this.timer) clearInterval(this.timer);
|
|
|
+ this.timer = setInterval(tick, 5 * 1000);
|
|
|
+ },
|
|
|
+ stopTick() {
|
|
|
+ if (this.timer) clearInterval(this.timer);
|
|
|
+ },
|
|
|
+ tryTick() {
|
|
|
+ let that = this;
|
|
|
+ try {
|
|
|
+ that.tick()
|
|
|
+ } catch (err) {
|
|
|
+ that.reportErr("play", '' + err.message)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ playerReadied(audio) {
|
|
|
+ this.isReady = true
|
|
|
+ let myPlayer = this.$refs.videoPlayer.player;
|
|
|
+ myPlayer && myPlayer.play()
|
|
|
+ },
|
|
|
+ onPlayerTimeupdate(player) {
|
|
|
+ let myPlayer = this.$refs.videoPlayer.player;
|
|
|
+ let curTimes = player.cache_.currentTime;
|
|
|
+ this.curTimes = curTimes || 0
|
|
|
+ if (this.media.isFinish) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ canpay(player){
|
|
|
+ console.log("player", this.media.position )
|
|
|
+ if( this.media.position > this.curTimes){
|
|
|
+ player.currentTime(this.media.position+3);
|
|
|
+ setTimeout( ()=>{player.play()}, 100)
|
|
|
+
|
|
|
+ }
|
|
|
+ },
|
|
|
+ setposition(position) {
|
|
|
+ if (position > this.media.duration) position = this.media.duration;
|
|
|
+ let player = this.$refs.videoPlayer.player;
|
|
|
+ player.currentTime(position);
|
|
|
+ player.play()
|
|
|
+ if (this.media.isFinish) return;
|
|
|
+ if (this.media.position >= this.media.duration - 10 && !this.media.isFinish) {
|
|
|
+ this.tick(true)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onPlayerPause(event) {
|
|
|
+ this.reportErr("play", 'pause');
|
|
|
+ this.stopTick()
|
|
|
+ this.onPlay = false
|
|
|
+ },
|
|
|
+ onPlayerEnded(event) {
|
|
|
+ this.reportErr("play", 'end');
|
|
|
+ this.tick(true)
|
|
|
+ },
|
|
|
+ onClose() {
|
|
|
+ this.reportErr("play", 'close')
|
|
|
+ this.doPause()
|
|
|
+ this.$emit("close")
|
|
|
+ },
|
|
|
+ doPause() {
|
|
|
+ // console.log("doPause")
|
|
|
+ this.stopTick()
|
|
|
+ this.onPlay = false
|
|
|
+ let myPlayer = this.$refs.videoPlayer.player;
|
|
|
+ myPlayer && myPlayer.pause()
|
|
|
+ },
|
|
|
+ doPlay() {
|
|
|
+ console.log("doPlay")
|
|
|
+ this.onPlay = true
|
|
|
+ this.startTick();
|
|
|
+ if (!this.$refs.videoPlayer || !this.$refs.videoPlayer.player) return;
|
|
|
+ if (!this.dialog) return this.doPause();
|
|
|
+ let myPlayer = this.$refs.videoPlayer.player;
|
|
|
+ myPlayer && myPlayer.play()
|
|
|
+ this.tickNum = 0
|
|
|
+ },
|
|
|
+ onPlayerStart() {
|
|
|
+ console.log("onPlayerStart")
|
|
|
+ this.reportErr("play", 'start');
|
|
|
+ this.startTick();
|
|
|
+ this.onPlay = true
|
|
|
+ },
|
|
|
+ reportErr(action, msg) {
|
|
|
+ httpServer("course.report", {
|
|
|
+ action,
|
|
|
+ msg
|
|
|
+ })
|
|
|
+ },
|
|
|
+ tick(force = false) {
|
|
|
+ let media = this.media;
|
|
|
+ this.tickNum++
|
|
|
+ if (this.media.isFinish) {
|
|
|
+ console.log("finish")
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 主动暂停
|
|
|
+ let myPlayer = this.$refs.videoPlayer.player;
|
|
|
+ let curTimes = parseInt(myPlayer.currentTime());
|
|
|
+ // 后退无心跳
|
|
|
+ if (this.media.position > curTimes && !force) return;
|
|
|
+ if (curTimes < 4) {
|
|
|
+ console.log("curTimes")
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ let isFinish = force ? 1 : 0
|
|
|
+ if (curTimes >= media.duration) isFinish = 1;
|
|
|
+ // 拉到后面
|
|
|
+ if (!isFinish) {
|
|
|
+ if (!this.onPlay) return;
|
|
|
+ }
|
|
|
+ if(window.navigator.webdriver){
|
|
|
+ this.reportErr("play", 'webdriver');
|
|
|
+ this.doPause()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 强制完成
|
|
|
+ let param = {
|
|
|
+ id: media.id,
|
|
|
+ position: curTimes,
|
|
|
+ isFinish
|
|
|
+ };
|
|
|
+ httpServer("course.tick", param, true).then(res => {
|
|
|
+ if (res.code == 200) {
|
|
|
+ let {
|
|
|
+ skip,
|
|
|
+ position,
|
|
|
+ pause,
|
|
|
+ closed
|
|
|
+ } = res.data
|
|
|
+ if (pause || closed) {
|
|
|
+ this.doPause();
|
|
|
+ this.$emit("close")
|
|
|
+ if (closed) {
|
|
|
+ this.$message.errorMsg("课程关闭学习", 5);
|
|
|
+ } else if (pause) {
|
|
|
+ this.$message.errorMsg("多处同时播放视频", 5);
|
|
|
+ }
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (!skip) this.setposition(position);
|
|
|
+ Object.assign(this.media, res.data)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ loadMedia( item ) {
|
|
|
+ this.media = item;
|
|
|
+ httpServer('course.GetMedia', {id:item.id}).then( res => {
|
|
|
+ if( res.code != 200) return;
|
|
|
+ let {mediaUrl, id, position, marks,duration} = res.data||{};
|
|
|
+ this.mediaUrl = res.data.mediaUrl;
|
|
|
+ this.media.position = position;
|
|
|
+ this.media.duration = duration;
|
|
|
+ this.media.id = id;
|
|
|
+ this.options.sources = [{src:this.mediaUrl,type: "application/x-mpegURL"}];
|
|
|
+ this.options.playtimes = position||1;
|
|
|
+ this.options.autoplay = position>0;
|
|
|
+ this.curTimes = 0
|
|
|
+ this.$refs.player && this.$refs.player.preload()
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ let {uid,token,courseId, mediaId} = this.$route.query;
|
|
|
+ this.userInfo = {uid, token, courseId, mediaId};
|
|
|
+ this.courseId = +courseId
|
|
|
+ if(uid) localStorage.uid = uid
|
|
|
+ if(token) localStorage.token = token
|
|
|
+ if (uid && token && courseId ) {
|
|
|
+ this.getCourse();
|
|
|
+ }
|
|
|
+ document.addEventListener('touchmove', function (event) {
|
|
|
+ console.log('touchmove')
|
|
|
+ event.preventDefault();
|
|
|
+ }, { passive: false });
|
|
|
+ }
|
|
|
+ }
|
|
|
+</script>
|
|
|
+<style>
|
|
|
+ @import url("./index.css");
|
|
|
+</style>
|