|
@@ -20,8 +20,11 @@ import {
|
|
|
onMounted,
|
|
|
onUnmounted,
|
|
|
reactive,
|
|
|
- ref
|
|
|
+ ref,
|
|
|
+ watch
|
|
|
} from 'vue';
|
|
|
+import TCPlayer from 'tcplayer.js';
|
|
|
+import 'tcplayer.js/dist/tcplayer.css';
|
|
|
import qs from 'query-string';
|
|
|
import {
|
|
|
state as baseState,
|
|
@@ -53,7 +56,7 @@ import giftTip from './images/new/icon-4.png';
|
|
|
import iconGift from './images/new/icon-gift.png';
|
|
|
import dayjs from 'dayjs';
|
|
|
// import MMessageTip from '@/components/m-message-tip';
|
|
|
-import { CurrentTime, useCountDown } from '@vant/use';
|
|
|
+import { CurrentTime, useCountDown, usePageVisibility } from '@vant/use';
|
|
|
import Payment from '../adapay/payment';
|
|
|
import QrcodePayment from './qrcode-payment';
|
|
|
import MImgCode from '@/components/m-img-code';
|
|
@@ -113,6 +116,7 @@ export default defineComponent({
|
|
|
name: 'student-register',
|
|
|
setup() {
|
|
|
const route = useRoute();
|
|
|
+ const pageVisibility = usePageVisibility();
|
|
|
const studentRegisterStore = useStudentRegisterStore();
|
|
|
const router = useRouter();
|
|
|
// 初始化学校编号
|
|
@@ -212,17 +216,6 @@ export default defineComponent({
|
|
|
orderTimer: null as any
|
|
|
});
|
|
|
|
|
|
- /*
|
|
|
- 新用户:
|
|
|
- autoRegister: true
|
|
|
- loginType: 'SMS'
|
|
|
-
|
|
|
- 已存在用户:
|
|
|
- autoRegister: false
|
|
|
- loginType: 'TOKEN'
|
|
|
- password: xxx
|
|
|
- */
|
|
|
-
|
|
|
const studentInfo = reactive({
|
|
|
autoRegister: true,
|
|
|
multiUser: true, // 是否为多用户
|
|
@@ -241,10 +234,111 @@ export default defineComponent({
|
|
|
password: '',
|
|
|
username: ''
|
|
|
});
|
|
|
+
|
|
|
+ const videoForms = reactive({
|
|
|
+ introductionVideo: '',
|
|
|
+ introductionVideoTime: 0, // 视频总时长
|
|
|
+ videoBrowsePoint: 0, // 视频最后观看点
|
|
|
+ player: null as any,
|
|
|
+ playerSpeed: 1,
|
|
|
+ intervalFnRef: null as any,
|
|
|
+ videoDetails: [] as any, // 节点列表
|
|
|
+ pointVideo: {} as any, // 需要处理有效的时间段
|
|
|
+ pointVideoTime: 0 // 有效时长
|
|
|
+ });
|
|
|
+
|
|
|
+ // 播放视频总时长
|
|
|
+ const videoIntervalRef = useInterval(1000, { controls: true });
|
|
|
+ videoIntervalRef.pause();
|
|
|
+
|
|
|
// 页面定时
|
|
|
const pageTimer = useInterval(1000, { controls: true });
|
|
|
pageTimer.pause();
|
|
|
|
|
|
+ /**
|
|
|
+ * 格式化视屏播放有效时间 - 合并区间
|
|
|
+ * @param intervals [[], []]
|
|
|
+ * @example [[4, 8],[0, 4],[10, 30]]
|
|
|
+ * @returns [[0, 8], [10, 30]]
|
|
|
+ */
|
|
|
+ const formatEffectiveTime = (intervals: any[]) => {
|
|
|
+ const res: any = [];
|
|
|
+ intervals.sort((a, b) => a[0] - b[0]);
|
|
|
+ let prev = intervals[0];
|
|
|
+ for (let i = 1; i < intervals.length; i++) {
|
|
|
+ const cur = intervals[i];
|
|
|
+ if (prev[1] >= cur[0]) {
|
|
|
+ // 有重合
|
|
|
+ prev[1] = Math.max(cur[1], prev[1]);
|
|
|
+ } else {
|
|
|
+ // 不重合,prev推入res数组
|
|
|
+ res.push(prev);
|
|
|
+ prev = cur; // 更新 prev
|
|
|
+ }
|
|
|
+ }
|
|
|
+ res.push(prev);
|
|
|
+ return res;
|
|
|
+ };
|
|
|
+
|
|
|
+ const formatEffectiveTimeToAfter = (res: any[]) => {
|
|
|
+ // 格式化有效时间
|
|
|
+ const effective: any = [];
|
|
|
+ const startNode = videoForms.pointVideo.startNode;
|
|
|
+ const endNode = videoForms.pointVideo.endNode;
|
|
|
+ // console.log(startNode, endNode, 'startNode')
|
|
|
+ res.forEach((item: any) => {
|
|
|
+ // 开始时间大于 设置时间
|
|
|
+ if (item[1] >= item[0]) {
|
|
|
+ /**
|
|
|
+ * 1、开始时间
|
|
|
+ */
|
|
|
+ if (
|
|
|
+ item[0] >= startNode &&
|
|
|
+ item[0] <= endNode &&
|
|
|
+ item[1] <= endNode
|
|
|
+ ) {
|
|
|
+ // console.log(1)
|
|
|
+ effective.push(item);
|
|
|
+ }
|
|
|
+ if (item[0] >= startNode && item[0] <= endNode && item[1] > endNode) {
|
|
|
+ // console.log(3)
|
|
|
+ effective.push([item[0], endNode]);
|
|
|
+ }
|
|
|
+ if (
|
|
|
+ item[0] < startNode &&
|
|
|
+ item[1] > startNode &&
|
|
|
+ item[1] <= endNode
|
|
|
+ ) {
|
|
|
+ // console.log(4)
|
|
|
+ effective.push([startNode, item[1]]);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (item[0] < startNode && item[1] > startNode && item[1] > endNode) {
|
|
|
+ // console.log(4)
|
|
|
+ effective.push([startNode, endNode]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // console.log(effective, 'effective')
|
|
|
+ return effective;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取数据有效期
|
|
|
+ * @param intervals [[], []]
|
|
|
+ * @returns 0s
|
|
|
+ */
|
|
|
+ const formatTimer = (intervals: any[]) => {
|
|
|
+ const afterIntervals = formatEffectiveTime(intervals);
|
|
|
+ // console.log(afterIntervals, 'afterIntervals')
|
|
|
+ let time = 0;
|
|
|
+ afterIntervals.forEach((t: any) => {
|
|
|
+ time += t[1] - t[0];
|
|
|
+ });
|
|
|
+ return time;
|
|
|
+ };
|
|
|
+
|
|
|
const overCountDown = useCountDown({
|
|
|
time: forms.activeOverTime,
|
|
|
onFinish() {
|
|
@@ -1132,15 +1226,35 @@ export default defineComponent({
|
|
|
schoolId?: string
|
|
|
) => {
|
|
|
try {
|
|
|
+ const videoBrowseData =
|
|
|
+ moreTime.value.length > 0 ? formatEffectiveTime(moreTime.value) : [];
|
|
|
+ const time =
|
|
|
+ videoBrowseData.length > 0 ? formatTimer(videoBrowseData) : 0;
|
|
|
+ console.log(moreTime.value, videoBrowseData, 'video', time);
|
|
|
+ // const videoCountTime = videoIntervalRef?.counter.value
|
|
|
+ // 判断 视屏播放时间大于视屏播放有效时间则说明数据有问题,进行重置数据
|
|
|
+ const rate = Math.floor(
|
|
|
+ (time / Math.floor(videoForms.player.duration())) * 100
|
|
|
+ );
|
|
|
+ console.log(
|
|
|
+ videoIntervalRef?.counter.value,
|
|
|
+ 'videoIntervalRef?.counter.value'
|
|
|
+ );
|
|
|
const { data } = await requestStudent.post(
|
|
|
'/edu-app/open/studentRegisterPointRecord/update',
|
|
|
{
|
|
|
data: {
|
|
|
id: forms.saveId,
|
|
|
- useTime: pageBrowseTime, // 固定10秒
|
|
|
+ useTime: pageBrowseTime, // 固定5秒
|
|
|
joinType,
|
|
|
userId,
|
|
|
- schoolId
|
|
|
+ schoolId,
|
|
|
+ // pageBrowseTime, // 固定10秒
|
|
|
+ videoBrowseData: JSON.stringify(videoBrowseData), // 视屏播放数据
|
|
|
+ videoBrowseDataTime: time || 0, // 有效的视频观看时长
|
|
|
+ videoBrowsePercentage: rate || 0, // 有效的视频观看时长百分比
|
|
|
+ videoBrowseTime: videoIntervalRef?.counter.value, // 视频观看时长
|
|
|
+ videoBrowsePoint: Math.floor(videoForms.player.currentTime() || 0) // 视频最后观看点 - 向下取整
|
|
|
}
|
|
|
}
|
|
|
);
|
|
@@ -1196,6 +1310,200 @@ export default defineComponent({
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+ /**
|
|
|
+ * 视屏累计时长
|
|
|
+ * 1、视屏开始播放时-开始计时
|
|
|
+ * 2、视频暂停时暂停-停止计时
|
|
|
+ * 3、视频加载时-停止计时
|
|
|
+ * 4、视频倍数播放时,时间正常计时
|
|
|
+ * 5、点击视频进度或拖动进度时,时间暂停
|
|
|
+ */
|
|
|
+ const _init = () => {
|
|
|
+ const Button = TCPlayer.getComponent('Button');
|
|
|
+ const BigPlayButton = TCPlayer.getComponent('BigPlayButton');
|
|
|
+ BigPlayButton.prototype.createEl = function () {
|
|
|
+ const el = Button.prototype.createEl.call(this);
|
|
|
+ const _html =
|
|
|
+ '<button><svg width="41px"height="41px"viewBox="0 0 41 41"version="1.1"xmlns="http://www.w3.org/2000/svg"xmlns:xlink="http://www.w3.org/1999/xlink"><g stroke="none"stroke-width="1"fill="none"fill-rule="evenodd"><g transform="translate(-167.000000, -155.000000)"><g transform="translate(0.000000, 85.000000)"><g transform="translate(158.000000, 70.000000)"><g transform="translate(9.000000, 0.000000)"><circle id="椭圆形"stroke="#FFFFFF"fill-opacity="0.1"fill="#D8D8D8"cx="20.5"cy="20.5"r="20"></circle><path d="M14.5483871,27.6859997 L14.5483871,13.4342349 C14.5480523,12.8729571 14.8729597,12.356555 15.3949624,12.0887034 C15.9169651,11.8208518 16.5522696,11.8445472 17.0503046,12.1504437 L28.6530473,19.2778563 C29.1119763,19.5602271 29.3887725,20.0426422 29.3887725,20.5601173 C29.3887725,21.0775924 29.1119763,21.5600075 28.6530473,21.8423783 L17.0503046,28.9697909 C16.5522696,29.2756874 15.9169651,29.2993828 15.3949624,29.0315312 C14.8729597,28.7636796 14.5480523,28.2472775 14.5483871,27.6859997 Z"id="路径"fill="#FFFFFF"fill-rule="nonzero"></path></g></g></g></g></g></svg></button>';
|
|
|
+
|
|
|
+ el.appendChild(
|
|
|
+ TCPlayer.dom.createEl('div', {
|
|
|
+ className: 'vjs-button-icon',
|
|
|
+ innerHTML: _html
|
|
|
+ })
|
|
|
+ );
|
|
|
+ return el;
|
|
|
+ };
|
|
|
+ videoForms.player = TCPlayer('register-video', {
|
|
|
+ appID: '',
|
|
|
+ controls: true,
|
|
|
+ plugins: {}
|
|
|
+ }); // player-container-id 为播放器容器 ID,必须与 html 中一致
|
|
|
+ if (videoForms.player) {
|
|
|
+ videoForms.player.src(
|
|
|
+ videoForms.introductionVideo ||
|
|
|
+ 'https://oss.dayaedu.com/ktqy/1712800085691.mp4'
|
|
|
+ ); // url 播放地址
|
|
|
+ // videoForms.player.poster(videoForms.coverImg || '');
|
|
|
+
|
|
|
+ videoForms.player.on('ready', (item: any) => {
|
|
|
+ console.log('ready', item);
|
|
|
+
|
|
|
+ // videoForms.player.pause()
|
|
|
+ });
|
|
|
+ videoForms.player.on('loadedmetadata', () => {
|
|
|
+ console.log('loadedmetadata');
|
|
|
+ // videoForms.loading = false;
|
|
|
+ videoForms.player.currentTime(videoForms.videoBrowsePoint);
|
|
|
+ });
|
|
|
+
|
|
|
+ // 速度变化时
|
|
|
+ videoForms.player.on('ratechange', () => {
|
|
|
+ videoForms.playerSpeed =
|
|
|
+ videoForms.playerSpeed < videoForms.player.speed
|
|
|
+ ? videoForms.player.speed
|
|
|
+ : videoForms.playerSpeed;
|
|
|
+ });
|
|
|
+
|
|
|
+ videoForms.player.on('seeking', () => {
|
|
|
+ console.log('seeking');
|
|
|
+ videoIntervalRef.isActive.value && videoIntervalRef.pause();
|
|
|
+ });
|
|
|
+
|
|
|
+ // // 拖动结束时
|
|
|
+ videoForms.player.on('seeked', () => {
|
|
|
+ console.log('seeked');
|
|
|
+ videoIntervalRef.isActive.value && videoIntervalRef.pause();
|
|
|
+ });
|
|
|
+
|
|
|
+ // 正在搜索中
|
|
|
+ videoForms.player.on('waiting', () => {
|
|
|
+ // console.log('waiting pause')
|
|
|
+ videoIntervalRef.isActive.value && videoIntervalRef.pause();
|
|
|
+ });
|
|
|
+
|
|
|
+ // 如何视频在缓存不会触发
|
|
|
+ videoForms.player.on('timeupdate', () => {
|
|
|
+ console.log('timeupdate', videoForms.player.currentTime());
|
|
|
+ // 判断视频计时器是否暂停,如果暂停则恢复
|
|
|
+ // 添加 「videoForms.player.playing」 是由会跳转到上次播放时间,会触发些方法
|
|
|
+ if (
|
|
|
+ !videoIntervalRef.isActive.value &&
|
|
|
+ videoForms.player.currentTime() > 0 &&
|
|
|
+ videoForms.player.playing
|
|
|
+ ) {
|
|
|
+ // console.log('timeupdate play')
|
|
|
+ videoIntervalRef.resume();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 视屏播放时暂停
|
|
|
+ videoForms.player.on('ended', () => {
|
|
|
+ videoForms.player.pause();
|
|
|
+ });
|
|
|
+
|
|
|
+ // 开始播放
|
|
|
+ videoForms.player.on('play', () => {
|
|
|
+ console.log('play');
|
|
|
+ // 判断视频计时器是否暂停,如果暂停则恢复
|
|
|
+ videoIntervalRef.resume();
|
|
|
+ });
|
|
|
+
|
|
|
+ // 暂停播放
|
|
|
+ videoForms.player.on('pause', () => {
|
|
|
+ console.log('pause', videoIntervalRef.isActive.value);
|
|
|
+
|
|
|
+ videoIntervalRef.pause();
|
|
|
+ });
|
|
|
+
|
|
|
+ videoForms.player.on('fullscreenchange', () => {
|
|
|
+ if (videoForms.player.isFullscreen()) {
|
|
|
+ console.log('fullscreen');
|
|
|
+ const i = document.createElement('i');
|
|
|
+ i.id = 'fullscreen-back';
|
|
|
+ i.className = 'van-icon van-icon-arrow-left video-back';
|
|
|
+ i.addEventListener('click', () => {
|
|
|
+ videoForms.player.exitFullscreen();
|
|
|
+ });
|
|
|
+ document.getElementsByClassName('video-js')[0].appendChild(i);
|
|
|
+ } else {
|
|
|
+ console.log('exitfullscreen');
|
|
|
+ const i = document.getElementById('fullscreen-back');
|
|
|
+ i && i.remove();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 保存零时时间
|
|
|
+ const moreTime: any = ref([]); // 多个观看时间段
|
|
|
+ let tempTime: any = []; // 临时存储时间
|
|
|
+
|
|
|
+ const currentTimer = useInterval(1000, { controls: true });
|
|
|
+ // 监听播放状态,
|
|
|
+ watch(
|
|
|
+ () => videoIntervalRef.isActive.value,
|
|
|
+ (newVal: boolean) => {
|
|
|
+ console.log(videoIntervalRef.isActive.value, 'videoIntervalRef');
|
|
|
+ initVideoCount(newVal);
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化视频时长
|
|
|
+ * @param newVal 播放状态
|
|
|
+ * @param repeat 是否为定时发送的
|
|
|
+ */
|
|
|
+ const initVideoCount = (newVal: any, repeat = false) => {
|
|
|
+ // console.log('watch', videoForms.player.currentTime())
|
|
|
+ const initTime = deepClone(tempTime);
|
|
|
+ if (repeat) {
|
|
|
+ if (tempTime.length > 0) {
|
|
|
+ // console.log('join video', tempTime, 'initTime', initTime)
|
|
|
+ tempTime[1] = Math.floor(videoForms.player.currentTime());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (newVal) {
|
|
|
+ console.log(
|
|
|
+ videoForms.player.currentTime(),
|
|
|
+ 'videoForms.player.currentTime()'
|
|
|
+ );
|
|
|
+ tempTime[0] = Math.floor(videoForms.player.currentTime());
|
|
|
+ } else {
|
|
|
+ tempTime[1] = Math.floor(videoForms.player.currentTime());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // console.log(newVal, repeat, tempTime, tempTime.length, 'videoIntervalRef.isActive.value in')
|
|
|
+ // console.log(videoForms.player.speed, 'speed')
|
|
|
+
|
|
|
+ if (tempTime.length >= 2) {
|
|
|
+ // console.log(tempTime, 'tempTime', moreTime.value)
|
|
|
+ // 处理在短时间内的时间差 【视屏拖动,点击可能会导致时间差太大】
|
|
|
+ const diffTime =
|
|
|
+ tempTime[1] -
|
|
|
+ tempTime[0] -
|
|
|
+ currentTimer.counter.value * videoForms.playerSpeed >
|
|
|
+ 2;
|
|
|
+ // console.log(diffTime, 'diffTime', currentTimer.counter.value, videoForms.playerSpeed, 'value')
|
|
|
+ // 结束时间,如果 大于开始时间则清除
|
|
|
+ if (tempTime[1] >= tempTime[0] && !diffTime)
|
|
|
+ moreTime.value.push(tempTime);
|
|
|
+ if (repeat) {
|
|
|
+ tempTime = deepClone(initTime);
|
|
|
+ } else {
|
|
|
+ tempTime = [];
|
|
|
+ currentTimer.counter.value = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ watch(pageVisibility, (value: any) => {
|
|
|
+ if (value == 'hidden') {
|
|
|
+ videoForms.player.pause();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
const pagePointInit = async () => {
|
|
|
try {
|
|
|
// 判断是否获取微信code码
|
|
@@ -1212,6 +1520,15 @@ export default defineComponent({
|
|
|
);
|
|
|
forms.saveId = data.id;
|
|
|
forms.openId = data.openId;
|
|
|
+
|
|
|
+ moreTime.value = data.videoBrowseData
|
|
|
+ ? JSON.parse(data.videoBrowseData)
|
|
|
+ : [];
|
|
|
+ videoForms.videoBrowsePoint = data.videoBrowsePoint || 0;
|
|
|
+ if (videoForms.player) {
|
|
|
+ videoForms.player.currentTime(data.videoBrowsePoint || 0);
|
|
|
+ }
|
|
|
+
|
|
|
sessionStorage.setItem('active-open-id', data.openId);
|
|
|
|
|
|
// 间隔多少时间同步数据
|
|
@@ -1220,8 +1537,10 @@ export default defineComponent({
|
|
|
pageTimer.counter.value = 0;
|
|
|
pageTimer.resume();
|
|
|
// 同步数据时先进行有效时间进行保存
|
|
|
-
|
|
|
+ initVideoCount(false, true);
|
|
|
await updateStat();
|
|
|
+
|
|
|
+ videoIntervalRef.counter.value = 0;
|
|
|
}, 5000);
|
|
|
} catch {}
|
|
|
};
|
|
@@ -1283,6 +1602,9 @@ export default defineComponent({
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 初始化视频播放器
|
|
|
+ _init();
|
|
|
+
|
|
|
await getRegisterGoods();
|
|
|
} catch {}
|
|
|
});
|
|
@@ -1321,6 +1643,16 @@ export default defineComponent({
|
|
|
</div>
|
|
|
)}
|
|
|
|
|
|
+ <div class={styles['video-content']}>
|
|
|
+ <video
|
|
|
+ id="register-video"
|
|
|
+ class={styles['video']}
|
|
|
+ src={'https://oss.dayaedu.com/ktqy/1712800085691.mp4'}
|
|
|
+ playsinline={true}
|
|
|
+ // poster={forms.coverImg}
|
|
|
+ preload="auto"></video>
|
|
|
+ </div>
|
|
|
+
|
|
|
<div
|
|
|
class={[
|
|
|
styles.studentSection,
|