Commit 4ec71ac5 by zhanghaozhe

限时免费

parent 65adf51c
import React, { Component } from 'react'
import HeaderBar from '@/common/HeaderBar'
import './video.scss'
import { NavLink, Route, Redirect, Switch, Link } from 'react-router-dom'
import { NavLink, Route, Redirect, Switch} from 'react-router-dom'
import { http, getParam, browser } from '@/utils'
import Recommendation from './recommendation'
import VideoCatalog from './video-catalog'
......@@ -9,8 +9,8 @@ import DatumCatalog from './datum-catalog'
import { Toast } from 'antd-mobile'
import videojs from 'video.js'
import 'video.js/dist/video-js.min.css'
import { Modal } from "antd-mobile"
import { Loading, Popup } from '@/common'
import { Modal} from "antd-mobile"
import { Loading} from '@/common'
import { connect } from "react-redux"
import jsCookie from 'js-cookie'
import Single from "@/components/detail/single";
......@@ -21,873 +21,896 @@ import './CustomPlayButton'
let alert = Modal.alert
function ProgressShareModal(props) {
return (
props.isShow &&
<div className='progress-share-modal-wrapper'>
<div className="progress-share-modal">
<div className="title">每日打卡</div>
<ul className="progress-container">
<li>
<div className="title">累计学习</div>
<div className="number"><span className='num'>{props.data.learn_day_count}</span>天</div>
</li>
<li>
<div className="title">行动力超过</div>
<div className="number"><span className='num'>{parseFloat(props.data.action_power)}</span>%
</div>
</li>
</ul>
<div className="share-container">
<div className="title">分享到</div>
<ul>
<li className='share-icon'>
<a style={{display: 'block'}} href={props.data.url}>
<div className="icon"><i className='iconfont iconweixinzhifu'/></div>
<div className='text'>微信好友</div>
</a>
</li>
<li className='share-icon'>
<a style={{display: 'block'}} href={props.data.url}>
<div className="icon"><i className='iconfont iconpengyouquaniconx'/></div>
<div className='text'>朋友圈</div>
</a>
</li>
</ul>
</div>
<i className="iconfont iconiconfront-2 close" onClick={props.closeShareModal}/>
return (
props.isShow &&
<div className='progress-share-modal-wrapper'>
<div className="progress-share-modal">
<div className="title">每日打卡</div>
<ul className="progress-container">
<li>
<div className="title">累计学习</div>
<div className="number"><span className='num'>{props.data.learn_day_count}</span>天</div>
</li>
<li>
<div className="title">行动力超过</div>
<div className="number"><span className='num'>{parseFloat(props.data.action_power)}</span>%
</div>
</li>
</ul>
<div className="share-container">
<div className="title">分享到</div>
<ul>
<li className='share-icon'>
<a style={{display: 'block'}} href={props.data.url}>
<div className="icon"><i className='iconfont iconweixinzhifu'/></div>
<div className='text'>微信好友</div>
</a>
</li>
<li className='share-icon'>
<a style={{display: 'block'}} href={props.data.url}>
<div className="icon"><i className='iconfont iconpengyouquaniconx'/></div>
<div className='text'>朋友圈</div>
</a>
</li>
</ul>
</div>
)
<i className="iconfont iconiconfront-2 close" onClick={props.closeShareModal}/>
</div>
</div>
)
}
class Video extends Component {
video //video element
player //video player instance
courseID
ws //websocket instance
timer
token
count
watchSec
previousPlaybackRate = 1
currentPlaybackRate = 1
reconnect = true
// timeEnough = false
recordSocket
recordTimer
isCurrentVideoFirstPlay = true
RECENTLEARN = "recent_learn"
state = {
title: '',
courseId: null,
videoList: [],
datum: [],
currentVideoSrc: '',
activeIndex: 0,
isAuth: true,
course: {}, // course.course_id 为 0 或 '' 时 为免费课程
salePrice: null,
vCourseId: null,
isLoading: true,
isShowShareModal: false,
shareData: {},
singleBox: false,
singMess: '',
singleType: 1,// 单集购买需要
nowPrice: 0,// 单集购买需要
laterPrice: 0,// 单集购买需要
video //video element
player //video player instance
courseID
ws //websocket instance
timer
token
count
watchSec
previousPlaybackRate = 1
currentPlaybackRate = 1
reconnect = true
// timeEnough = false
recordSocket
recordTimer
isCurrentVideoFirstPlay = true
RECENTLEARN = "recent_learn"
state = {
title: '',
courseId: null,
videoList: [],
datum: [],
currentVideoSrc: '',
activeIndex: 0,
isAuth: true,
course: {}, // course.course_id 为 0 或 '' 时 为免费课程
salePrice: null,
vCourseId: null,
isLoading: true,
isShowShareModal: false,
shareData: {},
singleBox: false,
singMess: '',
singleType: 1,// 单集购买需要
nowPrice: 0,// 单集购买需要
laterPrice: 0,// 单集购买需要
limitFreeNoPromptChecked: false,
showLimitFreePopup: true //todo 联调
}
componentDidMount() {
if (window.location.protocol === 'https:') {
window.location.replace('http' + window.location.href.slice(5))
return
}
componentDidMount() {
if (window.location.protocol === 'https:') {
window.location.replace('http' + window.location.href.slice(5))
return
}
this.courseID = getParam('id')
if (!this.courseID) {
this.props.history.replace('/')
return
}
this.setState({
courseId: this.courseID
})
const {location, location: {state = {}}} = this.props;
if (state.oid) {
this.check(state.oid);
}
if (getParam('is_class') === 1 || getParam('weixinpay')) {
this.payCallback()
}
if (browser.isWeixin) {
this.isweixinPay()
}
this.token = jsCookie.get('token')
this.getVideoList()
this.getDatumCatalog()
this.showLimitFreePopup({
title: '想领取【AI工程师必备干货礼包】? 想深入了解进阶课程? 职业前景不明朗? 资深规划师免费为你服务!',
})
this.courseID = getParam('id')
if (!this.courseID) {
this.props.history.replace('/')
return
}
// 直接购买
tobuy = () => {
// 详情页单集购买到该页面,url中的id不是课程id
const {course = {}} = this.state;
http.get(`${API['base-api']}/m/cart/addtopreorder/[${course.course_id}]`).then((res) => {
if (res.data.errno === 0) {
this.props.history.push(`/order?id=${course.course_id}`, {simple: 1})
} else {
Toast.info(res.data.msg, 2);
}
})
this.setState({
courseId: this.courseID
})
const {location, location: {state = {}}} = this.props;
if (state.oid) {
this.check(state.oid);
}
// 购买单集
toSingleset = (item) => {
// console.log(item);
this.setState({
singleBox: true,
singleType: 1,
singMess: item
})
window.localStorage.setItem('singMess', JSON.stringify(item))
if (getParam('is_class') === 1 || getParam('weixinpay')) {
this.payCallback()
}
// 自组件传给父组件的boxHide
boxHide = (val) => {
this.setState({singleBox: val, singleType: 1})
if (browser.isWeixin) {
this.isweixinPay()
}
// 单集购买 H5支付成功后回调
payCallback = () => {
const _this = this;
if (!getParam('oid')) {
return;
} else {
this.setState({
singMess: JSON.parse(window.localStorage.getItem('singMess'))
})
_this.intervalPayStatus = setInterval(function () {
http.get(`${API['base-api']}/m/orderState/oid/${getParam('oid')}`).then(res => {
if (res.data.errno === 401) {
clearInterval(_this.intervalPayStatus);
_this.intervalPayStatus = null;
// 获取课程类型
http.get(`${API['base-api']}/class_order_status/${getParam('oid')}`).then((res) => {
if (Number(res.data.data.errno) === 200) {
// 正常购买单集成功
_this.setState({
singleType: 6,
})
} else if (Number(res.data.data.errno) === 201) {
// 0元参团
_this.setState({
singleType: 4,
})
} else if (Number(res.data.data.errno) === 202) {
// 0元购
_this.setState({
singleType: 3,
})
} else if (Number(res.data.data.errno) === 203) {
// 三天内特价
_this.setState({
nowPrice: res.data.data.data.now_price,
laterPrice: res.data.data.data.three_day_later_price,
singleType: 2,
})
} else {
Toast.info(res.data.data.msg, 2)
}
})
}
})
}, 1000)
}
};
// 单集购买 微信内支付成功后回调
isweixinPay = () => {
let _this = this;
let weixin_code = getParam('code');
if (weixin_code) {
if (!getParam('oid')) {
return
} else {
this.setState({
singMess: JSON.parse(window.localStorage.getItem('singMess'))
})
// this.props.weixinPay(weixin_code)
http.get(`${API['base-api']}/pay/wxpay/pub_charge/oid/${getParam('oid')}/code/${weixin_code}`).then((res) => {
if (res.data.errno === 0) {
const data = res.data.data;
function onBridgeReady() {
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId": data.appId, //公众号名称,由商户传入
"timeStamp": data.timeStamp, //时间戳,自1970年以来的秒数
"nonceStr": data.nonceStr, //随机串
"package": data.package,
"signType": data.signType, //微信签名方式:
"paySign": data.paySign //微信签名
},
function (res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
Toast.info('支付成功', 2);
_this.intervalPayStatus = setInterval(function () {
http.get(`${API['base-api']}/m/orderState/oid/${getParam('oid')}`).then(res => {
if (res.data.errno === 401) {
clearInterval(_this.intervalPayStatus);
_this.intervalPayStatus = null;
// 获取课程类型
http.get(`${API['base-api']}/class_order_status/${getParam('oid')}`).then((res) => {
if (Number(res.data.data.errno) === 200) {
// 正常购买单集成功
_this.setState({
singleType: 6,
})
} else if (Number(res.data.data.errno) === 201) {
// 0元参团
_this.setState({
singleType: 4,
})
} else if (Number(res.data.data.errno) === 202) {
// 0元购
_this.setState({
singleType: 3,
})
} else if (Number(res.data.data.errno) === 203) {
// 三天内特价
_this.setState({
nowPrice: res.data.data.data.now_price,
laterPrice: res.data.data.data.three_day_later_price,
singleType: 2,
})
} else {
Toast.info(res.data.data.msg, 2)
}
})
}
})
}, 1000)
} else {
alert('支付失败')
}
}
)
}
if (typeof WeixinJSBridge == "undefined") {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false)
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady)
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady)
}
} else {
onBridgeReady()
}
} else {
Toast.info(res.data.msg, 2)
}
})
}
}
};
// 判断支付是否成功
check = (oid) => {
this.setState({
singMess: JSON.parse(window.localStorage.getItem('singMess'))
})
http.get(`${API['base-api']}/class_order_status/${oid}`).then((res) => {
if (Number(res.data.data.errno) === 200) {
this.token = jsCookie.get('token')
this.getVideoList()
this.getDatumCatalog()
}
// 直接购买
tobuy = () => {
// 详情页单集购买到该页面,url中的id不是课程id
const {course = {}} = this.state;
http.get(`${API['base-api']}/m/cart/addtopreorder/[${course.course_id}]`).then((res) => {
if (res.data.errno === 0) {
this.props.history.push(`/order?id=${course.course_id}`, {simple: 1})
} else {
Toast.info(res.data.msg, 2);
}
})
}
// 购买单集
toSingleset = (item) => {
// console.log(item);
this.setState({
singleBox: true,
singleType: 1,
singMess: item
})
window.localStorage.setItem('singMess', JSON.stringify(item))
}
// 自组件传给父组件的boxHide
boxHide = (val) => {
this.setState({singleBox: val, singleType: 1})
}
// 单集购买 H5支付成功后回调
payCallback = () => {
const _this = this;
if (!getParam('oid')) {
return;
} else {
this.setState({
singMess: JSON.parse(window.localStorage.getItem('singMess'))
})
_this.intervalPayStatus = setInterval(function () {
http.get(`${API['base-api']}/m/orderState/oid/${getParam('oid')}`).then(res => {
if (res.data.errno === 401) {
clearInterval(_this.intervalPayStatus);
_this.intervalPayStatus = null;
// 获取课程类型
http.get(`${API['base-api']}/class_order_status/${getParam('oid')}`).then((res) => {
if (Number(res.data.data.errno) === 200) {
// 正常购买单集成功
this.setState({
singleType: 6,
_this.setState({
singleType: 6,
})
} else if (Number(res.data.data.errno) === 201) {
} else if (Number(res.data.data.errno) === 201) {
// 0元参团
this.setState({
singleType: 4,
_this.setState({
singleType: 4,
})
} else if (Number(res.data.data.errno) === 202) {
} else if (Number(res.data.data.errno) === 202) {
// 0元购
this.setState({
singleType: 3,
_this.setState({
singleType: 3,
})
} else if (Number(res.data.data.errno) === 203) {
} else if (Number(res.data.data.errno) === 203) {
// 三天内特价
this.setState({
nowPrice: res.data.data.data.now_price,
laterPrice: res.data.data.data.three_day_later_price,
singleType: 2,
_this.setState({
nowPrice: res.data.data.data.now_price,
laterPrice: res.data.data.data.three_day_later_price,
singleType: 2,
})
} else {
} else {
Toast.info(res.data.data.msg, 2)
}
}
})
}
})
}, 1000)
}
// 9502 初始化 监听事件
setupWS = () => {
this.ws = new WebSocket(API["process-api"]);
this.ws.addEventListener('error', () => {
this.ws = null
})
this.ws.addEventListener('close', () => {
if (this.reconnect) {
this.ws = null
setTimeout(() => {
this.setupWS();
}, 1000)
}
clearInterval(this.timer)
this.timer = null;
};
// 单集购买 微信内支付成功后回调
isweixinPay = () => {
let _this = this;
let weixin_code = getParam('code');
if (weixin_code) {
if (!getParam('oid')) {
return
} else {
this.setState({
singMess: JSON.parse(window.localStorage.getItem('singMess'))
})
this.ws.addEventListener('message', e => {
const data = JSON.parse(e.data);
data.code == 4040 && (this.reconnect = false);
if(data.code === 0) {
console.log("上次的学习记录" + JSON.stringify(data));
if(data.data && data.data.position) {
this.player.currentTime(data.data.position);
// this.props.weixinPay(weixin_code)
http.get(`${API['base-api']}/pay/wxpay/pub_charge/oid/${getParam('oid')}/code/${weixin_code}`).then((res) => {
if (res.data.errno === 0) {
const data = res.data.data;
function onBridgeReady() {
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId": data.appId, //公众号名称,由商户传入
"timeStamp": data.timeStamp, //时间戳,自1970年以来的秒数
"nonceStr": data.nonceStr, //随机串
"package": data.package,
"signType": data.signType, //微信签名方式:
"paySign": data.paySign //微信签名
},
function (res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
Toast.info('支付成功', 2);
_this.intervalPayStatus = setInterval(function () {
http.get(`${API['base-api']}/m/orderState/oid/${getParam('oid')}`).then(res => {
if (res.data.errno === 401) {
clearInterval(_this.intervalPayStatus);
_this.intervalPayStatus = null;
// 获取课程类型
http.get(`${API['base-api']}/class_order_status/${getParam('oid')}`).then((res) => {
if (Number(res.data.data.errno) === 200) {
// 正常购买单集成功
_this.setState({
singleType: 6,
})
} else if (Number(res.data.data.errno) === 201) {
// 0元参团
_this.setState({
singleType: 4,
})
} else if (Number(res.data.data.errno) === 202) {
// 0元购
_this.setState({
singleType: 3,
})
} else if (Number(res.data.data.errno) === 203) {
// 三天内特价
_this.setState({
nowPrice: res.data.data.data.now_price,
laterPrice: res.data.data.data.three_day_later_price,
singleType: 2,
})
} else {
Toast.info(res.data.data.msg, 2)
}
})
}
})
}, 1000)
} else {
alert('支付失败')
}
}
)
}
})
}
sendMessage = message => {
let readyState = this.ws.readyState, _this = this;
if(readyState === 1) {
this.ws && this.ws.send(JSON.stringify(message))
}else if(readyState === 3) {
this.ws.close();
this.ws = null;
let reconnect = setTimeout(function() {
clearTimeout(reconnect);
reconnect = null;
_this.ws = new WebSocket(PROCESS_URL);
}, 500);
}
if (typeof WeixinJSBridge == "undefined") {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false)
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady)
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady)
}
} else {
onBridgeReady()
}
} else {
Toast.info(res.data.msg, 2)
}
})
}
}
};
// 判断支付是否成功
check = (oid) => {
this.setState({
singMess: JSON.parse(window.localStorage.getItem('singMess'))
})
http.get(`${API['base-api']}/class_order_status/${oid}`).then((res) => {
if (Number(res.data.data.errno) === 200) {
// 正常购买单集成功
this.setState({
singleType: 6,
//视频结束请求接口
getShareProgressInfo = () => {
http.get(`${API['base-api']}/m/aist/share_data/${this.courseID}/${this.state.videoList[this.state.activeIndex]['id']}`)
.then(res => {
const {data} = res
if (data.errno == 200) {
this.setState({shareData: data.data, isShowShareModal: true})
}
})
}
})
} else if (Number(res.data.data.errno) === 201) {
// 0元参团
this.setState({
singleType: 4,
//告诉服务端计算进度
countSchedule = () => {
const {videoList, activeIndex, vCourseId, course = {}} = this.state
if (Number(course.course_id) === 0 || course.course_id === '') {
console.log('免费课程 拦截');
return;
}
let ctype = 0;
if (course.is_aist) {
ctype = 2;
}
// 计算进度 根据ctype判断 课程类型 0-视频 1-直播 2-AI特训营
this.sendMessage({
mtype: 'count_schedule',
uid: this.props.user.data.uid,
token: this.token,
platform: 5,
video_id: videoList[activeIndex]['id'],
course_id: this.state.courseId,
v_course_id: vCourseId,
ctype: ctype,
})
} else if (Number(res.data.data.errno) === 202) {
// 0元购
this.setState({
singleType: 3,
}
// 发送时间消息
sendWatchTime = (sec, rate) => {
const {videoList, activeIndex, vCourseId, course = {}} = this.state
// 免费课程不发送
// if (Number(course.course_id) === 0 || course.course_id === '') {
// console.log('免费课程 拦截');
// return;
// }
// 时间为0 不发送消息
if (Number(sec) === 0) {
return;
}
let ctype = 0;
if (course.is_aist) {
ctype = 2;
}
// 时间足够不发送
// if(this.timeEnough) {
// console.log('5001 时间足够');
// return;
// }
this.sendMessage({
mtype: 'watch_time',
rate,
time: sec,
video_id: videoList[activeIndex]['id'],
course_id: this.state.courseId,
v_course_id: vCourseId,
uid: this.props.user.data.uid,
token: this.token,
platform: 5,
position: parseInt(this.player.currentTime()),
ctype: ctype,
})
}
} else if (Number(res.data.data.errno) === 203) {
// 三天内特价
this.setState({
nowPrice: res.data.data.data.now_price,
laterPrice: res.data.data.data.three_day_later_price,
singleType: 2,
setupTimer = () => {
this.count = 0
this.watchSec = 0
clearInterval(this.timer)
this.timer = null;
this.timer = setInterval(() => {
if (this.player && this.player.player()) {
if (this.count === 5) {
this.sendWatchTime(this.watchSec, this.currentPlaybackRate)
this.count = this.watchSec = 0
} else {
!this.player.paused() && this.watchSec++
!this.player.paused() && this.count++
}
}
})
} else {
Toast.info(res.data.data.msg, 2)
}
})
}
// 9502 初始化 监听事件
setupWS = () => {
this.ws = new WebSocket(API["process-api"]);
this.ws.addEventListener('error', () => {
this.ws = null
})
this.ws.addEventListener('close', () => {
if (this.reconnect) {
this.ws = null
setTimeout(() => {
this.setupWS();
}, 1000)
}
clearInterval(this.timer)
this.timer = null;
})
this.ws.addEventListener('message', e => {
const data = JSON.parse(e.data);
data.code == 4040 && (this.reconnect = false);
if (data.code === 0) {
console.log("上次的学习记录" + JSON.stringify(data));
if (data.data && data.data.position) {
this.player.currentTime(data.data.position);
}
}
})
}
sendMessage = message => {
let readyState = this.ws.readyState, _this = this;
if (readyState === 1) {
this.ws && this.ws.send(JSON.stringify(message))
} else if (readyState === 3) {
this.ws.close();
this.ws = null;
let reconnect = setTimeout(function () {
clearTimeout(reconnect);
reconnect = null;
_this.ws = new WebSocket(PROCESS_URL);
}, 500);
}
// 初始化视频播放器
initializePlayer = () => {
window.HELP_IMPROVE_VIDEOJS = false;
this.player = videojs(this.video, {
controls: true,
preload: 'auto',
bigPlayButton: false,
textTrackDisplay: false,
posterImage: false,
errorDisplay: false,
playbackRates: ['0.75', '1', '1.5', '2'],
controlBar: {
pictureInPictureToggle: false
}
})
this.player.addChild('CustomPlayButtonCover')
this.player.on('play', () => {
const {videoList, activeIndex, vCourseId, course = {}} = this.state
// 当视频播放时 看是否是第一次播放(初次进入页面 刷新页面 切换视频 都是第一次播放 需要获取上次的播放时间)
if(this.isCurrentVideoFirstPlay) {
// 当某些原因导致视频暂停时(用户暂停 网络不好等) 再播放时不需要发送
this.isCurrentVideoFirstPlay = false;
// 发送消息 recent_learn
this.ws.send(JSON.stringify({
mtype: this.RECENTLEARN,
uid: this.props.user.data.uid,
token: this.token,
platform: 5,
video_id: videoList[activeIndex]['id'],
course_id: this.state.courseId,
v_course_id: vCourseId,
is_live: 0,
}))
}
if(!this.timer) {
this.setupTimer();
}
})
this.player.on('ratechange', () => {
this.currentPlaybackRate = this.player.playbackRate()
this.sendWatchTime(this.watchSec, this.previousPlaybackRate)
this.count = this.watchSec = 0
this.previousPlaybackRate = this.currentPlaybackRate
})
this.player.on('ended', () => {
this.sendWatchTime(this.watchSec, this.currentPlaybackRate);
this.count = this.watchSec = 0;
this.countSchedule(); // 计算进度 -- 播放完毕
// 返现课程才出现打卡记录
if(this.state.course.is_aist) {
this.getShareProgressInfo()
}
clearInterval(this.timer);
this.timer = null;
})
}
//视频结束请求接口
getShareProgressInfo = () => {
http.get(`${API['base-api']}/m/aist/share_data/${this.courseID}/${this.state.videoList[this.state.activeIndex]['id']}`)
.then(res => {
const {data} = res
if (data.errno == 200) {
this.setState({shareData: data.data, isShowShareModal: true})
}
})
}
//告诉服务端计算进度
countSchedule = () => {
const {videoList, activeIndex, vCourseId, course = {}} = this.state
if (Number(course.course_id) === 0 || course.course_id === '') {
console.log('免费课程 拦截');
return;
}
sendLastRecord = () => {
http.post(`${API.home}/m/course/record_last_video`, {
v_course_id: this.state.course['v_course_id'],
video_id: this.state.videoList[this.state.activeIndex].id
})
let ctype = 0;
if (course.is_aist) {
ctype = 2;
}
componentWillUnmount() {
this.player && this.player.dispose()
clearInterval(this.timer)
this.timer = null;
this.ws && this.ws.close()
this.ws = null
clearInterval(this.recordTimer)
this.recordSocket && this.recordSocket.close()
this.recordSocket = null
// 计算进度 根据ctype判断 课程类型 0-视频 1-直播 2-AI特训营
this.sendMessage({
mtype: 'count_schedule',
uid: this.props.user.data.uid,
token: this.token,
platform: 5,
video_id: videoList[activeIndex]['id'],
course_id: this.state.courseId,
v_course_id: vCourseId,
ctype: ctype,
})
}
// 发送时间消息
sendWatchTime = (sec, rate) => {
const {videoList, activeIndex, vCourseId, course = {}} = this.state
// 免费课程不发送
// if (Number(course.course_id) === 0 || course.course_id === '') {
// console.log('免费课程 拦截');
// return;
// }
// 时间为0 不发送消息
if (Number(sec) === 0) {
return;
}
// 选择新的视频
selectVideo = index => {
if (index === this.state.activeIndex) {
return
let ctype = 0;
if (course.is_aist) {
ctype = 2;
}
// 时间足够不发送
// if(this.timeEnough) {
// console.log('5001 时间足够');
// return;
// }
this.sendMessage({
mtype: 'watch_time',
rate,
time: sec,
video_id: videoList[activeIndex]['id'],
course_id: this.state.courseId,
v_course_id: vCourseId,
uid: this.props.user.data.uid,
token: this.token,
platform: 5,
position: parseInt(this.player.currentTime()),
ctype: ctype,
})
}
setupTimer = () => {
this.count = 0
this.watchSec = 0
clearInterval(this.timer)
this.timer = null;
this.timer = setInterval(() => {
if (this.player && this.player.player()) {
if (this.count === 5) {
this.sendWatchTime(this.watchSec, this.currentPlaybackRate)
this.count = this.watchSec = 0
} else {
!this.player.paused() && this.watchSec++
!this.player.paused() && this.count++
}
console.log('selectVideo 先发送时间 再发送进度 在重置定时器');
this.isCurrentVideoFirstPlay = true; // 切换视频则重置这个变量 因为新视频肯定是首次播放
this.sendWatchTime(this.watchSec, this.currentPlaybackRate)
this.countSchedule(); // 计算进度 -- 选择新视频(可能是M端特有的)
}
}, 1000)
}
// 初始化视频播放器
initializePlayer = () => {
window.HELP_IMPROVE_VIDEOJS = false;
this.player = videojs(this.video, {
controls: true,
preload: 'auto',
bigPlayButton: false,
textTrackDisplay: false,
posterImage: false,
errorDisplay: false,
playbackRates: ['0.75', '1', '1.5', '2'],
controlBar: {
pictureInPictureToggle: false
}
})
this.player.addChild('CustomPlayButtonCover')
this.player.on('play', () => {
const {videoList, activeIndex, vCourseId, course = {}} = this.state
// 当视频播放时 看是否是第一次播放(初次进入页面 刷新页面 切换视频 都是第一次播放 需要获取上次的播放时间)
if (this.isCurrentVideoFirstPlay) {
// 当某些原因导致视频暂停时(用户暂停 网络不好等) 再播放时不需要发送
this.isCurrentVideoFirstPlay = false;
// 发送消息 recent_learn
this.ws.send(JSON.stringify({
mtype: this.RECENTLEARN,
uid: this.props.user.data.uid,
token: this.token,
platform: 5,
video_id: videoList[activeIndex]['id'],
course_id: this.state.courseId,
v_course_id: vCourseId,
is_live: 0,
}))
}
if (!this.timer) {
this.setupTimer();
this.setState(
{
activeIndex: index
},
() => {
if (this.hasAuth(this.state.activeIndex)) {
this.setPlayerSrc(this.state.videoList[index]['play_url'])
this.sendLastRecord()
this.playVideo()
} else {
this.getCoursePrice();
}
}
);
}
})
this.player.on('ratechange', () => {
this.currentPlaybackRate = this.player.playbackRate()
this.sendWatchTime(this.watchSec, this.previousPlaybackRate)
this.count = this.watchSec = 0
this.previousPlaybackRate = this.currentPlaybackRate
})
this.player.on('ended', () => {
this.sendWatchTime(this.watchSec, this.currentPlaybackRate);
this.count = this.watchSec = 0;
this.countSchedule(); // 计算进度 -- 播放完毕
// 返现课程才出现打卡记录
if (this.state.course.is_aist) {
this.getShareProgressInfo()
}
clearInterval(this.timer);
this.timer = null;
})
}
sendLastRecord = () => {
http.post(`${API.home}/m/course/record_last_video`, {
v_course_id: this.state.course['v_course_id'],
video_id: this.state.videoList[this.state.activeIndex].id
})
}
componentWillUnmount() {
this.player && this.player.dispose()
clearInterval(this.timer)
this.timer = null;
this.ws && this.ws.close()
this.ws = null
clearInterval(this.recordTimer)
this.recordSocket && this.recordSocket.close()
this.recordSocket = null
}
// 选择新的视频
selectVideo = index => {
if (index === this.state.activeIndex) {
return
}
getLastVideoIndex = lastIndex => {
return this.state.videoList.findIndex(item => item.id == lastIndex)
}
getVideoList = () => {
let url = '';
if (getParam('video_id')) {
url = `${API.home}/m/course/play/${this.courseID + '?video_id=' + getParam('video_id')}`
http.post(`${API['base-api']}/sys/get_class_audition`, {
video_id: getParam('video_id')
})
console.log('selectVideo 先发送时间 再发送进度 在重置定时器');
this.isCurrentVideoFirstPlay = true; // 切换视频则重置这个变量 因为新视频肯定是首次播放
this.sendWatchTime(this.watchSec, this.currentPlaybackRate)
this.countSchedule(); // 计算进度 -- 选择新视频(可能是M端特有的)
this.setupTimer();
this.setState(
{
activeIndex: index
},
() => {
if (this.hasAuth(this.state.activeIndex)) {
this.setPlayerSrc(this.state.videoList[index]['play_url'])
this.sendLastRecord()
this.playVideo()
} else {
url = `${API.home}/m/course/play/${this.courseID}`
this.getCoursePrice();
}
http.get(url).then(res => {
const {data = {}, code} = res.data;
if (code === 200) {
this.setState(
state => ({
videoList: data['lessons'],
currentVideoSrc: data['lessons'][state.activeIndex]['play_url'],
course: data.course,
courseId: data.course['course_id'],
vCourseId: data.course['v_course_id'],
title: data.course['course_title'],
isLoading: false
}),
this.playSetup
)
} else {
Toast.info(data.msg)
}
}
)
}
);
}
getLastVideoIndex = lastIndex => {
return this.state.videoList.findIndex(item => item.id == lastIndex)
}
getVideoList = () => {
let url = '';
if (getParam('video_id')) {
url = `${API.home}/m/course/play/${this.courseID + '?video_id=' + getParam('video_id')}`
http.post(`${API['base-api']}/sys/get_class_audition`, {
video_id: getParam('video_id')
})
} else {
url = `${API.home}/m/course/play/${this.courseID}`
}
playSetup = () => {
// is_aist,是否AI特训营
const {course = {}} = this.state;
// if (Number(course.course_id) === 0 || course.course_id === '') {
// console.log('免费课程 拦截');
// }else{
let _this = this;
this.setupWS();
this.setupTimer();
let scheduleTime = setTimeout(function () {
clearTimeout(scheduleTime);
scheduleTime = null;
_this.countSchedule(); // 刚进入页面的时候 就计算进度 先获取视频列表getVideoList 获取列表后 播放选择的视频 然后计算进度
}, 1000);
// }
let index = this.getLastVideoIndex(course.last_video_id);
index = index >= 0 ? index : 0;
this.setState(
{
activeIndex: index
},
() => {
if (this.lessonAvailable(index)) {
if (this.hasAuth(index)) {
Promise.resolve().then(() => {
this.initializePlayer()
this.playWithAuth()
})
} else {
this.getCoursePrice();
}
} else {
alert('暂无视频', '', [{
text: 'OK',
onPress: () => {
this.props.history.push('/')
}
}])
}
http.get(url).then(res => {
const {data = {}, code} = res.data;
if (code === 200) {
this.setState(
state => ({
videoList: data['lessons'],
currentVideoSrc: data['lessons'][state.activeIndex]['play_url'],
course: data.course,
courseId: data.course['course_id'],
vCourseId: data.course['v_course_id'],
title: data.course['course_title'],
isLoading: false
}),
this.playSetup
)
} else {
Toast.info(data.msg)
}
}
)
}
playSetup = () => {
// is_aist,是否AI特训营
const {course = {}} = this.state;
// if (Number(course.course_id) === 0 || course.course_id === '') {
// console.log('免费课程 拦截');
// }else{
let _this = this;
this.setupWS();
this.setupTimer();
let scheduleTime = setTimeout(function () {
clearTimeout(scheduleTime);
scheduleTime = null;
_this.countSchedule(); // 刚进入页面的时候 就计算进度 先获取视频列表getVideoList 获取列表后 播放选择的视频 然后计算进度
}, 1000);
// }
let index = this.getLastVideoIndex(course.last_video_id);
index = index >= 0 ? index : 0;
this.setState(
{
activeIndex: index
},
() => {
if (this.lessonAvailable(index)) {
if (this.hasAuth(index)) {
Promise.resolve().then(() => {
this.initializePlayer()
this.playWithAuth()
})
} else {
this.getCoursePrice();
}
} else {
alert('暂无视频', '', [{
text: 'OK',
onPress: () => {
this.props.history.push('/')
}
);
}
setPlayerSrc = src => {
if (!this.player) {
this.initializePlayer()
}])
}
this.player.src({
src,
type: 'application/x-mpegURL'
})
}
}
);
playVideo = () => {
this.player.ready(() => {
this.player.play()
})
}
setPlayerSrc = src => {
if (!this.player) {
this.initializePlayer()
}
this.player.src({
src,
type: 'application/x-mpegURL'
})
}
playVideo = () => {
this.player.ready(() => {
this.player.play()
})
getDatumCatalog() {
http.get(`${API.home}/m/course/data/${this.courseID}`)
.then(res => {
const data = res.data
if (data.code === 200) {
}
this.setState({
datum: data.data
})
} else {
Toast.info(data.msg)
}
})
}
getDatumCatalog() {
http.get(`${API.home}/m/course/data/${this.courseID}`)
.then(res => {
const data = res.data
if (data.code === 200) {
lessonAvailable = index => {
return this.state.videoList[index]['video_size'] !== 0
}
this.setState({
datum: data.data
})
getCoursePrice = () => {
const {course = {}} = this.state;
http.get(`${API.home}/sys/course/price/${course.course_id}`)
.then(res => {
const {data} = res
if (data.code === 200) {
this.setState({
salePrice: data.data['sale_price']
})
}
})
}
} else {
Toast.info(data.msg)
}
})
}
lessonAvailable = index => {
return this.state.videoList[index]['video_size'] !== 0
}
getCoursePrice = () => {
const {course = {}} = this.state;
http.get(`${API.home}/sys/course/price/${course.course_id}`)
.then(res => {
const {data} = res
if (data.code === 200) {
this.setState({
salePrice: data.data['sale_price']
})
}
})
}
playWithAuth = () => {
const {videoList, activeIndex} = this.state
playWithAuth = () => {
const {videoList, activeIndex} = this.state
if (this.hasAuth(activeIndex)) {
this.setPlayerSrc(videoList[activeIndex]['play_url'])
}
if (this.hasAuth(activeIndex)) {
this.setPlayerSrc(videoList[activeIndex]['play_url'])
}
}
hasAuth = index => {
const {videoList} = this.state
hasAuth = index => {
const {videoList} = this.state
let lesson = videoList[index]
let lesson = videoList[index]
if (lesson['video_auth']) {
this.setState({
isAuth: true
})
return true
} else {
this.setState({
isAuth: false
})
return false
if (lesson['video_auth']) {
this.setState({
isAuth: true
})
return true
} else {
this.setState({
isAuth: false
})
return false
}
}
showLimitFreePopup = (title, id) => {
Popup({
title: <span>想领取【AI工程师必备干货礼包】? 想深入了解进阶课程? 职业前景不明朗? 资深规划师免费为你服务!</span>,
className: 'free-popup',
content: <div className={'des'}>
<img className="qrcode" src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/tinypng-common/right_weixin.png"/>
</div>
})
}
render() {
let {match, location, history} = this.props
const {videoList, activeIndex, isAuth, salePrice, course, singleBox, singleType, showLimitFreePopup} = this.state;
let toHref = '';
if (location.state && location.state.to && location.state.to === 'detail') {
toHref = `/detail?id=${course.course_id}`
}
render() {
let {match, location, history} = this.props
const {videoList, activeIndex, isAuth, salePrice, course, singleBox, singleType} = this.state;
let toHref = '';
if (location.state && location.state.to && location.state.to === 'detail') {
toHref = `/detail?id=${course.course_id}`
}
return (
<div className='play'>
<HeaderBar title={this.state.title} arrow={true} toHref={() => {
toHref ? history.push(
toHref,
{
to: 'classify'
}
) : history.go(-1)
}}/>
<Loading isLoading={this.state.isLoading}>
<div className="video">
<video className={'video-js'} ref={el => this.video = el}
webkit-playsinline="true"
playsInline={true}
x-webkit-airplay="allow"
x5-video-player-type="h5">
<source src={'/'} type='application/x-mpegURL'/>
</video>
{
!isAuth && !!videoList[activeIndex]['is_class'] && (
<div className="purchase-box">
<div className='hint'>您尚未购买该课时,请购买后学习。</div>
<div className='btns'>
<button
type='button'
onClick={this.tobuy}
className='purchase-class'
>
¥{salePrice} 购买课程
</button>
<button
type='button'
onClick={this.toSingleset.bind(this, videoList[activeIndex])}
className='purchase-episode'
>
¥{videoList.length && videoList[activeIndex]['class_price']} 购买单集
</button>
</div>
</div>
)
}
{
!isAuth && !!course.is_aist && (
<div className="is-aist-box">
<i className={'iconfont iconiconfront-21'}></i>
<p className={'time'}>{videoList[activeIndex]['aist_start_time']}</p>
<p className={'time'}>请耐心等待...</p>
</div>
)
}
</div>
<div className='tab'>
<div>
<NavLink to={{pathname: `${match.url}/video`, search: `?id=${this.courseID}`}}
replace
activeClassName='active'
>视频</NavLink>
</div>
<div>
<NavLink to={{pathname: `${match.url}/datum`, search: `?id=${this.courseID}`}}
replace
activeClassName='active'
>资料</NavLink>
</div>
</div>
{/*单集购买*/}
{
singleBox &&
<Single
courseId={course.course_id}
singleBox={this.state.singleBox}
boxHide={this.boxHide}
data={this.state.singMess}
singleType={this.state.singleType}
vcourseId={course.v_course_id}
videoId={this.state.singMess.video_id}
check={this.check}
title={this.state.singMess.course_tile}/>
}
{/* 单集购买成功 */}
{
singleType !== 1 &&
<SingleSuccess
courseId={course.course_id}
boxHide={this.boxHide}
data={this.state.singMess}
singleType={singleType}
vcourseId={course.v_course_id}
videoId={this.state.singMess.video_id}
nowPrice={this.state.nowPrice}
laterPrice={this.state.laterPrice}
/>
}
</Loading>
<Switch>
<Redirect exact from={'/play'} to={{
pathname: '/play/video',
search: location.search
}}/>
<Route
path={`${match.path}/video`}
render={props => {
return (
<VideoCatalog
activeIndex={this.state.activeIndex}
selectVideo={this.selectVideo}
videoCatalog={videoList}
{...props}
/>
);
}}
/>
<Route path={`${match.path}/datum`} render={props => {
return <DatumCatalog {...props} datum={this.state.datum}/>
}}/>
</Switch>
<Route render={props => {
return this.state.vCourseId ? <Recommendation {...props} vCourseId={this.state.vCourseId}/>
: null
}}/>
<ProgressShareModal isShow={this.state.isShowShareModal}
closeShareModal={() => this.setState({isShowShareModal: false})}
data={this.state.shareData}
return (
<div className='play'>
<HeaderBar title={this.state.title} arrow={true} toHref={() => {
toHref ? history.push(
toHref,
{
to: 'classify'
}
) : history.go(-1)
}}/>
<Loading isLoading={this.state.isLoading}>
<div className="video">
<video className={'video-js'} ref={el => this.video = el}
webkit-playsinline="true"
playsInline={true}
x-webkit-airplay="allow"
x5-video-player-type="h5">
<source src={'/'} type='application/x-mpegURL'/>
</video>
{
!isAuth && !!videoList[activeIndex]['is_class'] && (
<div className="purchase-box">
<div className='hint'>您尚未购买该课时,请购买后学习。</div>
<div className='btns'>
<button
type='button'
onClick={this.tobuy}
className='purchase-class'
>
¥{salePrice} 购买课程
</button>
<button
type='button'
onClick={this.toSingleset.bind(this, videoList[activeIndex])}
className='purchase-episode'
>
¥{videoList.length && videoList[activeIndex]['class_price']} 购买单集
</button>
</div>
</div>
)
}
{
!isAuth && !!course.is_aist && (
<div className="is-aist-box">
<i className={'iconfont iconiconfront-21'}></i>
<p className={'time'}>{videoList[activeIndex]['aist_start_time']}</p>
<p className={'time'}>请耐心等待...</p>
</div>
)
}
</div>
<div className='tab'>
<div>
<NavLink to={{pathname: `${match.url}/video`, search: `?id=${this.courseID}`}}
replace
activeClassName='active'
>视频</NavLink>
</div>
<div>
<NavLink to={{pathname: `${match.url}/datum`, search: `?id=${this.courseID}`}}
replace
activeClassName='active'
>资料</NavLink>
</div>
</div>
{/*单集购买*/}
{
singleBox &&
<Single
courseId={course.course_id}
singleBox={this.state.singleBox}
boxHide={this.boxHide}
data={this.state.singMess}
singleType={this.state.singleType}
vcourseId={course.v_course_id}
videoId={this.state.singMess.video_id}
check={this.check}
title={this.state.singMess.course_tile}/>
}
{/* 单集购买成功 */}
{
singleType !== 1 &&
<SingleSuccess
courseId={course.course_id}
boxHide={this.boxHide}
data={this.state.singMess}
singleType={singleType}
vcourseId={course.v_course_id}
videoId={this.state.singMess.video_id}
nowPrice={this.state.nowPrice}
laterPrice={this.state.laterPrice}
/>
}
</Loading>
<Switch>
<Redirect exact from={'/play'} to={{
pathname: '/play/video',
search: location.search
}}/>
<Route
path={`${match.path}/video`}
render={props => {
return (
<VideoCatalog
activeIndex={this.state.activeIndex}
selectVideo={this.selectVideo}
videoCatalog={videoList}
{...props}
/>
);
}}
/>
<Route path={`${match.path}/datum`} render={props => {
return <DatumCatalog {...props} datum={this.state.datum}/>
}}/>
</Switch>
<Route render={props => {
return this.state.vCourseId ? <Recommendation {...props} vCourseId={this.state.vCourseId}/>
: null
}}/>
<ProgressShareModal isShow={this.state.isShowShareModal}
closeShareModal={() => this.setState({isShowShareModal: false})}
data={this.state.shareData}
/>
{
showLimitFreePopup &&
<div className={'limit-free-cover'}>
<div className="free-popup">
<div className="title">
<span>想领取【AI工程师必备干货礼包】? 想深入了解进阶课程? 职业前景不明朗? 资深规划师免费为你服务!</span>
</div>
<div className={'des'}>
<img className="qrcode"
src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/tinypng-common/right_weixin.png" alt=''/>
<span>长按/扫码识别</span>
<span>添加时请备注<span>142</span>哦</span>
<div className="no-prompt">
<label htmlFor="no-prompt">
<span className={`checkbox-label ${this.state.limitFreeNoPromptChecked ? 'checked' : 'unchecked'}`}>
<i className={'iconfont iconiconfront-73'}/>
</span>
<input type="checkbox" id={'no-prompt'} onChange={(e) => {
this.setState({
limitFreeNoPromptChecked: e.target.checked
})
}}/>
<span>本课程不再提示</span>
</label>
</div>
</div>
<i className={'close-btn iconfont iconiconfront-2'} onClick={() => {
this.setState({
showLimitFreePopup: false
})
localStorage.setItem('neverShowLimitFreePopup', '1')
}}/>
</div>
);
}
</div>
}
</div>
);
}
}
export default connect(
state => ({user: state.user}),
null
state => ({user: state.user}),
null
)(Video);
......@@ -281,12 +281,26 @@ $tabHeight: 44px;
}
}
.limit-free-cover {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, .8);
z-index: 999;
}
.free-popup {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 290px;
height: 366px;
border-radius: 5px !important;
padding: 0 !important;
overflow: hidden;
background: url("https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/time_limited_free/M/popup-bg.png") !important;
background-size: cover !important;
......@@ -299,5 +313,90 @@ $tabHeight: 44px;
font-size: 15px;
}
.des {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding-top: 20px;
.qrcode {
margin-bottom: 10px;
width: 118px;
height: 118px;
}
& > span:nth-of-type(1) {
color: #666;
font-size: 13px;
margin-bottom: 10px;
}
& > span:nth-of-type(2) {
color: #333;
font-size: 15px;
margin-bottom: 14px;
}
span span {
color: #FF2121;
}
.no-prompt {
label {
position: relative;
padding-left: 21px;
height: 14px;
}
input, .checkbox-label {
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
width: 13px;
height: 13px;
-webkit-appearance: none;
outline: 0;
background: #fff;
}
input {
opacity: 0;
}
.checkbox-label {
border: 1px solid rgba(84, 92, 100, .6);
border-radius: 1px;
left: -1px;
box-sizing: border-box;
.iconfont {
color: #fff;
font-size: 12px;
}
}
span {
color: #545C64;
font-size: 13px;
margin-bottom: 0;
line-height: 14px;
}
.checked {
background: #09f;
}
}
}
.close-btn {
position: absolute;
bottom: -44px;
left: 50%;
transform: translateX(-50%);
font-size: 26px;
color: #fff;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment