提交 3ab45d93 编写于 作者: guofeng.chen's avatar guofeng.chen

create new page

上级 b9a49ed7
<template> <template>
<div> <div class="router-container">
<transition name="router-fade" mode="out-in"> <transition name="router-fade" mode="out-in">
<keep-alive> <keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view> <router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive> </keep-alive>
</transition> </transition>
<transition name="router-fade" mode="out-in"> <transition name="router-fade" mode="out-in">
<router-view v-if="!$route.meta.keepAlive"></router-view> <router-view v-if="!$route.meta.keepAlive"></router-view>
</transition> </transition>
<!-- <svg-icon></svg-icon> --> <!-- <svg-icon></svg-icon> -->
...@@ -32,4 +32,7 @@ ...@@ -32,4 +32,7 @@
.router-fade-enter, .router-fade-leave-active { .router-fade-enter, .router-fade-leave-active {
opacity: 0; opacity: 0;
} }
.router-container{
height: 100%;
}
</style> </style>
<template>
<div
class="pica-video"
:style="{position: isFullScreen ? 'fixed' : ''}">
<video
:id="vid"
:src="url"
:poster="poster"
preload
webkit-playsinline
playsinline
x5-video-player-type="h5"
mtt-playsinline="true"
@loadedmetadata="loadedMetaData"
@timeupdate="onTimeUpdate"
@ended="onEnded"
@error="onError"
>
</video>
<div class="video-cover"></div>
<!-- 控制栏 -->
<div class="control-box">
<div class="control-bar">
<div class="btn-play" :class="{'btn-pause': isPaused}" @click="togglePlay"></div>
<div class="progress-box" @click="setProgress">
<div class="progress-inner">
<div class="progress-bar" :style="progressBar"></div>
</div>
</div>
<div class="time-box">{{ playTime }}/{{ totalTime }}</div>
<div class="bar-empty"></div>
<div class="btn-screen" :class="{'btn-screen-mini': isFullScreen}" @click="onFullscreen"></div>
</div>
</div>
<!-- 播放遮罩 -->
<transition name="fade">
<div v-show="isPaused" class="cover cover-play" @click="togglePlay"></div>
</transition>
<!-- 播放错误 -->
<transition name="fade">
<div v-show="showError" class="cover cover-error">
<p>播放失败,请确认网络正常或重新进入页面</p>
</div>
</transition>
<!-- 接续播放 -->
<transition name="fade">
<div v-show="showResume" class="cover cover-resume">
<p>上次观看至{{ history > 59 ? `${parseInt(history / 60)}分钟` : `${history}秒` }},正在续播</p>
</div>
</transition>
<!-- 弹窗放在外部处理 -->
<slot></slot>
<!-- 试看提示 -->
<div class="proved-box" v-show="showProved">
可试看{{ proved > 59 ? `${parseInt(proved / 60)}分钟` : `${proved}秒` }},激活/购买后可看完整课程
<div class="proved-close" @click="showProved=false"></div>
</div>
</div>
</template>
<script>
/**
* 不使用参数,调用两个方法;
* switchUrl(opt)和switchUrlAndPlay(opt)
* opt: {
* url, // 视频链接
* poster, // 封面
* proved, // 试看时长,单位秒
* history, // 上次观看时长,单位秒
* enable, // 是否可看
* }
* 暴露一个回调onVideoEnd,参数:
* type => 1 播放结束,2 试看结束
*/
import { formatLeftTimeObj } from '@/utils';
export default {
name: 'pica-video',
data() {
return {
url: '', // 视频链接
poster: '', // 封面
proved: 0, // 试看时长,单位秒
history: 0, // 上次观看时长,单位秒
enable: true, // 是否可看
vid: '', // 唯一id
playTime: '00:00', // 播放时长
totalTime: '00:00', // 总时长
isPaused: false, // 是否暂停
isFullScreen: false, // 是否全屏
progressBar: {}, // 进度条
showProved: false, // 试看提示文字
showError: false, // 播放错误
showResume: false, // 继续播放
}
},
computed: {
logged() {
return this.$store.getters.logged;
},
},
created() {
this.vid = `video_${this._uid}`;
},
mounted() {
const player = document.getElementById(this.vid);
player.addEventListener("timeupdate", this.onTimeUpdate, false);
},
beforeDestroy() {
this.timer && clearTimeout(this.timer);
if (this.currentTime) {
this.reportLeave();
}
if (!this.isPaused && !this.finish) {
this.reportOnOff(2);
}
},
methods: {
togglePlay() {
if (!this.logged) {
this.$store.dispatch('goLogin');
return;
}
if (!this.url || !this.loaded || !this.enable) {
return;
}
let isPaused = this.isPaused;
const player = document.getElementById(this.vid);
// 试看,且超过时间
if (this.proved && player.currentTime >= this.proved) {
return;
}
if (isPaused) {
this.commonBuriedData(`984#984001`);
player.play();
} else {
player.pause();
}
this.reportOnOff(isPaused ? 1 : 2);
this.isPaused = !isPaused;
},
// 切换视频
switchUrl(opts = {}) {
const { url = '', poster = '', proved = 0, history = 0, enable = true } = opts;
const player = document.getElementById(this.vid);
this.loaded = false;
player.src = url;
this.url = url;
this.poster = poster;
this.proved = proved;
this.history = history;
this.enable = enable;
this.showProved = proved > 0;
this.provedOver = false;
this.finish = false;
this.opts = opts;
this.currentTime = null;
this.duration = null;
if (this.showError) this.showError = false;
return player;
},
// 切换并播放
switchUrlAndPlay(opts = {}) {
if (!this.isPaused && !this.finish) {
this.reportOnOff(2);
}
this.reportLeave();
const player = this.switchUrl(opts);
if (!this.enable) {
return;
}
const loop = () => {
if (this.loaded) {
player.play();
this.isPaused = false;
this.reportOnOff(1);
return null;
} else {
return setTimeout(() => {
loop()
}, 100);
}
}
this.timer = loop();
},
// 加载完毕,获取总时长和音量
loadedMetaData(e) {
let { duration } = e.target;
this.totalTime = this.formatTime(duration);
this.loaded = true;
if (this.history) {
e.target.currentTime = this.history;
this.showResume = true;
setTimeout(() => {
this.showResume = false;
}, 2000);
}
},
// 播放中
onTimeUpdate(e) {
if (this.provedOver) {
return
}
const { currentTime, duration } = e.target;
if (currentTime) {
this.playTime = this.formatTime(currentTime);
}
if (currentTime && duration) {
this.setBarPosition(currentTime * 100 / duration, 'progressBar');
this.currentTime = currentTime;
this.duration = duration;
}
// 试看,且超过时间
if (this.proved && currentTime >= this.proved) {
this.provedEnd();
}
},
// 设置进度条位置
setBarPosition(percent, target) {
this[target] = {
transform: `translateX(${percent}%)`,
'-webkit-transform': `-webkit-translateX(${percent}%)`,
'-moz-transform': `-moz-translateX(${percent}%)`,
'-ms-transform': `-ms-translateX(${percent}%)`,
}
},
// 设置进度
setProgress(e) {
if (!this.url || !this.loaded) {
return;
}
const { offsetX } = e;
const { width } = e.target.getBoundingClientRect();
const player = document.getElementById(this.vid);
const duration = player.duration;
// 试看,且超过时间
if (this.proved && offsetX / width >= this.proved / duration) {
return;
}
this.setBarPosition(offsetX * 100 / width, 'progressBar');
player.currentTime = offsetX * duration / width;
if (this.isPaused) {
player.play();
this.isPaused = false;
this.reportOnOff(1);
}
},
formatTime(t) {
const time = formatLeftTimeObj(t);
const h = time.h === '00' ? '' : `${time.h}:`;
return `${h}${time.f}:${time.s}`;
},
// 试看结束
provedEnd() {
this.provedOver = true;
this.showProved = false;
const player = document.getElementById(this.vid);
player.pause();
this.$emit('onVideoEnd', { type: 2 });
this.finish = true;
this.reportOnOff(2);
},
// 播放结束
onEnded() {
this.$emit('onVideoEnd', { type: 1 });
this.finish = true;
this.reportOnOff(2);
},
onError() {
if (this.url) {
this.showError = true
}
},
// 全屏
onFullscreen() {
this.isFullScreen = !this.isFullScreen;
},
// 上报播放、暂停,status:1开始,2暂停
reportOnOff(status = 1) {
const { chapterId, courseId, lectureId } = this.opts;
this.$api.pauseCourse({
fileType: 1,
resourceInfo1: courseId,
resourceInfo2: chapterId,
resourceInfo3: lectureId,
resourceType: 1,
status,
systemType: 2,
timestamp: Date.now(),
showError: false,
})
},
// 上报离开
reportLeave() {
if (!this.currentTime || !this.duration) {
return;
}
const { chapterId, courseId, lectureId } = this.opts;
this.$api.joinCourse({
requestList: [{
chapterId,
courseId,
lectureId,
nowTime: this.currentTime,
time: this.duration,
timeStamp: Date.now(),
}],
showError: false,
})
},
},
}
</script>
<style lang="scss" scoped>
@import "../../style/mixin";
.pica-video{
position: relative;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: #333;
overflow: hidden;
z-index: 999;
video{
position: absolute;
left: 50%;
top: 50%;
width: 100%;
max-width: 100%;
max-height: 100%;
transform: translate3d(-50%, -50%, 0);
}
.video-cover{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.control-box{
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: px2rem(44px);
}
.control-bar{
position: absolute;
display: flex;
align-items: center;
left: 0;
top: 0;
width: 100%;
height: px2rem(44px);
// transform: translate3d(0, 60px, 0);
// transition: transform 0.2s;
background: linear-gradient(180deg,rgba(0,0,0,0) 0%,rgba(0,0,0,1) 100%);
}
.btn-play{
width: px2rem(44px);
height: px2rem(44px);
background-image: url('~@/images/video/play.png');
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: px2rem(26px) auto;
}
.btn-pause{
background-image: url('~@/images/video/pause.png');
background-position: 50% 50%;
background-size: px2rem(24px) auto;
}
.time-box{
color: #fff;
font-size: 14px;
line-height: 14px;
letter-spacing: 1px;
margin-left: 12px;
}
.bar-empty{
flex: 1;
width: 1px;
}
.btn-screen{
width: px2rem(44px);
height: px2rem(44px);
background-image: url('~@/images/video/screen_full.png');
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: px2rem(20px) auto;
}
.btn-screen-mini{
background-image: url('~@/images/video/screen_mini.png');
}
.progress-box{
position: absolute;
left: 0;
top: 0;
width: 100%;
padding: 3px 0;
}
.progress-inner{
position: relative;
height: 4px;
background: rgba(255, 255, 255, 0.4);
}
.progress-bar{
position: absolute;
left: -100%;
top: 0;
width: 100%;
height: 100%;
background: #449284;
transform: translateX(0);
pointer-events: none;
}
.proved-box{
position: absolute;
color: #fff;
font-size: 14px;
left: 20px;
bottom: 70px;
height: 30px;
line-height: 30px;
padding: 0 27px 0 31px;
border-radius: 15px;
background: url('~@/images/video/tip.png') no-repeat 12px center;
background-size: 14px auto;
background-color: #373839;
}
.proved-close{
position: absolute;
right: 0;
top: 0;
width: 30px;
height: 30px;
background: url('~@/images/video/close.png') no-repeat 10px center;
background-size: 10px auto;
}
.cover-play{
background: url('~@/images/video/cover_play.png') no-repeat center center;
background-size: px2rem(50px) auto;
}
.cover-error, .cover-resume{
p{
position: absolute;
left: 0;
top: 50%;
width: 100%;
color: #fff;
font-size: 18px;
text-align: center;
transform: translate3d(0, -50%, 0);
}
}
.cover{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
}
}
</style>
...@@ -7,8 +7,7 @@ const mixins = require('@/utils/mixins'); ...@@ -7,8 +7,7 @@ const mixins = require('@/utils/mixins');
import '@/utils/rem' // 引入自适应 import '@/utils/rem' // 引入自适应
import '@/utils/yqy-bridge' // 移动端服务 import '@/utils/yqy-bridge' // 移动端服务
// import FastClick from 'fastclick' // import FastClick from 'fastclick'
import vueFilters from '@/utils/filter' import vueFilters from '@/utils/filter';
import VConsole from 'vconsole/dist/vconsole.min.js'
import BuriedPoint, { sendBuriedData } from 'web-buried-point'; import BuriedPoint, { sendBuriedData } from 'web-buried-point';
import Vant from 'vant'; import Vant from 'vant';
import 'vant/lib/index.css'; import 'vant/lib/index.css';
...@@ -17,7 +16,7 @@ import clipboard from 'clipboard'; ...@@ -17,7 +16,7 @@ import clipboard from 'clipboard';
Vue.prototype.clipboard = clipboard; Vue.prototype.clipboard = clipboard;
if (!(process.env.BUILD_ENV === 'uat' || process.env.BUILD_ENV === 'pro')) { if (!(process.env.BUILD_ENV === 'uat' || process.env.BUILD_ENV === 'pro')) {
// if(!(process.env.BUILD_ENV === 'pro')) { const VConsole = require('vconsole');
let vConsole = new VConsole() // 初始化 let vConsole = new VConsole() // 初始化
} }
......
...@@ -3,30 +3,35 @@ import App from '../App' ...@@ -3,30 +3,35 @@ import App from '../App'
const index = r => require.ensure([], () => r(require('../views/index')), 'index') const index = r => require.ensure([], () => r(require('../views/index')), 'index')
const merge = r => require.ensure([], () => r(require('../views/merge-detail')), 'merge') const merge = r => require.ensure([], () => r(require('../views/merge-detail')), 'merge')
const test = r => require.ensure([], () => r(require('../views/test-components')), 'test-components') const test = r => require.ensure([], () => r(require('../views/test-components')), 'test-components')
const courseDetail = r => require.ensure([], () => r(require('../views/course-detail')), 'course-detail')
export default [{ export default [{
path: '/', path: '/',
component: App, component: App,
children: [ children: [
{ {
path: '', path: '',
redirect: '/index' redirect: '/index'
}, },
{ {
path: '/index', path: '/index',
component: index component: index
}, },
{ {
path: '/home', path: '/home',
component: index component: index
}, },
{ {
path: '/coop', path: '/coop',
component: merge component: merge
}, },
{ {
path: '/test', path: '/test',
component: test component: test
}, },
] {
path: '/course-detail',
component: courseDetail
},
]
}] }]
...@@ -143,3 +143,6 @@ html,body{ ...@@ -143,3 +143,6 @@ html,body{
// color: #fff !important; // color: #fff !important;
// } // }
#app{
height: 100%;
}
...@@ -73,7 +73,7 @@ export function setEventByModuleCode(itemData){ ...@@ -73,7 +73,7 @@ export function setEventByModuleCode(itemData){
if( modeCode === 'M001' || modeCode === 'M002' || modeCode === 'M003') { if( modeCode === 'M001' || modeCode === 'M002' || modeCode === 'M003') {
paramList = '' paramList = ''
}else if( modeCode === 'M100' || modeCode === 'M300' ) { }else if( modeCode === 'M100' || modeCode === 'M300' ) {
let urlPara = getUrlParmByCode(paramList); let urlPara = getUrlParmByCode(paramList);
paramList[0] && (paramList[0].value += urlPara); paramList[0] && (paramList[0].value += urlPara);
...@@ -121,7 +121,7 @@ export function deepCopy(obj) { ...@@ -121,7 +121,7 @@ export function deepCopy(obj) {
} }
export function mergeObjs(...ojbs){ export function mergeObjs(...ojbs){
} }
// 跳转:在App中使用原生跳转,在浏览器中使用本地路由跳转 // 跳转:在App中使用原生跳转,在浏览器中使用本地路由跳转
...@@ -143,4 +143,28 @@ export function gotoPage(context, pageUrl) { ...@@ -143,4 +143,28 @@ export function gotoPage(context, pageUrl) {
jsonString: paramList jsonString: paramList
}); });
} }
} }
\ No newline at end of file
function formatNum(n) {
return n * 1 < 10 ? `0${n}` : n;
}
// 传入秒,计算出剩余时间的时分秒
export function formatLeftTimeObj(time, hasZero = true) {
if (typeof time !== 'number') {
time = parseInt(time)
}
// time = parseInt(time / 1000)
// const d = parseInt(time / 86400)
// time = time - d * 86400
const h = parseInt(time / 3600)
time = time - h * 3600
const f = parseInt(time / 60)
const s = parseInt(time - f * 60)
return {
// d: hasZero ? formatNum(d) : d,
h: hasZero ? formatNum(h) : h,
f: hasZero ? formatNum(f) : f,
s: hasZero ? formatNum(s) : s,
}
}
<template>
<div class="course-detail">
<!-- 视频 -->
<div class="video-box">
<pica-video ref="picaVideo" @onVideoEnd="onVideoEnd" />
</div>
<!-- 内容滚动 -->
<div class="scroll-box">
<div class="scroll-content">
</div>
</div>
<!-- 打开、下载App -->
<div class="download-box">
<img src="~@/images/icon-pica.png" alt="logo" />
<div class="content">
<p class="title">云鹊医</p>
<p class="sub-title">高效学习 轻松学医</p>
</div>
<div class="btn-download">下载</div>
<div class="btn-open">打开</div>
</div>
</div>
</template>
<script>
import PicaVideo from '@/components/course/pica-video';
export default {
components: {
PicaVideo
},
data() {
return {
isShowDialog: true,
};
},
mounted() {
this.$refs.picaVideo.switchUrl({
url: 'https://video.yunqueyi.com/TNB/002/01_01_APP_SD.mp4',
});
},
methods: {
// 视频播放结束
onVideoEnd() {
}
}
};
</script>
<style lang="scss" scoped>
@import "../style/mixin";
.course-detail{
position: relative;
height: 100%;
padding-top: px2rem(210px);
box-sizing: border-box;
.video-box{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: px2rem(210px);
}
.scroll-box{
height: 100%;
overflow-x: hidden;
overflow-y: scroll;
}
.scroll-content{
padding-bottom: px2rem(60px);
}
.download-box{
display: flex;
align-items: center;
position: fixed;
left: 0;
bottom: 0;
width: 100%;
height: px2rem(60px);
background-color: #fff;
img{
display: block;
width: px2rem(40px);
height: px2rem(40px);
margin: px2rem(10px) px2rem(10px) 0 px2rem(15px);
}
.content{
flex: 1;
width: 50px;
padding-top: px2rem(10px);
}
.title{
font-size: px2rem(16px);
line-height: px2rem(22px);
}
.sub-title{
color: #717171;
font-size: px2rem(12px);
line-height: px2rem(18px);
}
.btn-download{
color: #449284;
display: inline-block;
height: px2rem(30px);
line-height: px2rem(30px);
font-size: px2rem(14px);
padding: 0 px2rem(15px);
margin-right: px2rem(15px);
border: 1px solid #449284;
border-radius: px2rem(15px);
}
.btn-open{
color: #fff;
display: inline-block;
height: px2rem(30px);
line-height: px2rem(30px);
font-size: px2rem(14px);
padding: 0 px2rem(15px);
margin-right: px2rem(15px);
background-color: #449284;
border-radius: px2rem(15px);
}
}
}
</style>
...@@ -248,7 +248,8 @@ export default { ...@@ -248,7 +248,8 @@ export default {
this.showLoading = false; this.showLoading = false;
if (res.code == "000000") { if (res.code == "000000") {
this.existBind = res.data.existBind || 0; this.existBind = res.data.existBind || 0;
this.titleTestModelList = res.data.titleTestModelList; this.titleTestModelList = res.data.titleTestModelList || [];
let len = this.titleTestModelList.length > 0 ? (this.titleTestModelList.length - 1) : 0;
// 如果有绑定的卡,并且之前没有做定位,则进行定位 // 如果有绑定的卡,并且之前没有做定位,则进行定位
if (this.existBind && !this.hasResetPosition) { if (this.existBind && !this.hasResetPosition) {
this.hasResetPosition = true; this.hasResetPosition = true;
...@@ -257,8 +258,9 @@ export default { ...@@ -257,8 +258,9 @@ export default {
} else { } else {
this.listData = this.listData =
res.data.titleTestModelList[ res.data.titleTestModelList[
this.currentModelIndex len
].firstSubjectModelList; ].firstSubjectModelList;
this.currentModelIndex = len;
} }
} else { } else {
this.message.error(res.message); this.message.error(res.message);
......
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册