|
@@ -26,12 +26,14 @@ import { browser } from '@/helpers/utils'
|
|
|
import { Vue3Lottie } from 'vue3-lottie'
|
|
|
import playLoadData from './datas/data.json'
|
|
|
import { usePageVisibility } from '@vant/use'
|
|
|
+import { useInterval } from '@vueuse/core'
|
|
|
import PlayRecordTime from './playRecordTime'
|
|
|
import { handleCheckVip } from '../hook/useFee'
|
|
|
import OGuide from '@/components/o-guide'
|
|
|
import Tool, { ToolItem, ToolType } from './component/tool'
|
|
|
import Pen from './component/tools/pen'
|
|
|
import VideoItem from './component/video-item'
|
|
|
+import deepClone from '@/helpers/deep-clone'
|
|
|
|
|
|
export default defineComponent({
|
|
|
name: 'CoursewarePlay',
|
|
@@ -180,6 +182,8 @@ export default defineComponent({
|
|
|
|
|
|
list.push({
|
|
|
...material,
|
|
|
+ moreTime: [],
|
|
|
+ videoTime: 0, // 视频时长
|
|
|
iframeRef: null,
|
|
|
videoEle: null,
|
|
|
tabName: name,
|
|
@@ -232,6 +236,7 @@ export default defineComponent({
|
|
|
popupData.itemActive = item.id
|
|
|
popupData.itemName = item.name
|
|
|
nextTick(() => {
|
|
|
+ console.log(list, 'list')
|
|
|
data.itemList = list
|
|
|
checkedAnimation(popupData.activeIndex)
|
|
|
postMessage({
|
|
@@ -315,10 +320,28 @@ export default defineComponent({
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 获取学生观看数据
|
|
|
+ const getLookVideoData = async () => {
|
|
|
+ try {
|
|
|
+ const { data } = await request.get(
|
|
|
+ state.platformApi + `/studentCoursewareMaterialRelation/findByDetailId/${route.query.id}`,
|
|
|
+ {
|
|
|
+ hideLoading: true
|
|
|
+ }
|
|
|
+ )
|
|
|
+ console.log(data)
|
|
|
+ } catch {
|
|
|
+ //
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
onMounted(async () => {
|
|
|
+ if (state.platformType === 'STUDENT') {
|
|
|
+ await getLookVideoData()
|
|
|
+ }
|
|
|
await getDetail()
|
|
|
const hasFree = String(data.detail?.accessScope) === '0'
|
|
|
- if (!hasFree){
|
|
|
+ if (!hasFree) {
|
|
|
const hasVip = handleCheckVip()
|
|
|
if (!hasVip) {
|
|
|
nextTick(() => {
|
|
@@ -623,6 +646,132 @@ export default defineComponent({
|
|
|
return {}
|
|
|
})
|
|
|
let closeModelTimer: any = null
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 统计视频播放时间段
|
|
|
+ */
|
|
|
+ // 播放视频总时长
|
|
|
+ const videoIntervalRef = useInterval(1000, { controls: true })
|
|
|
+ videoIntervalRef.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)
|
|
|
+ // console.log(res, 'formatEffectiveTime')
|
|
|
+
|
|
|
+ return res
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 获取数据有效期
|
|
|
+ * @param intervals [[], []]
|
|
|
+ * @returns 0s
|
|
|
+ */
|
|
|
+ const formatTimer = (intervals: any[]) => {
|
|
|
+ const afterIntervals = formatEffectiveTime(intervals)
|
|
|
+ let time = 0
|
|
|
+ afterIntervals.forEach((t: any) => {
|
|
|
+ time += t[1] - t[0]
|
|
|
+ })
|
|
|
+ return time
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存零时时间
|
|
|
+ // const moreTime: any = ref([]) // 多个观看时间段 已经放到列表里面了
|
|
|
+ let tempTime: any = [] // 临时存储时间
|
|
|
+ const currentTimer = useInterval(1000, { controls: true })
|
|
|
+ // 监听播放状态,
|
|
|
+ watch(
|
|
|
+ () => videoIntervalRef.isActive.value,
|
|
|
+ (newVal: boolean) => {
|
|
|
+ initVideoCount(newVal)
|
|
|
+ }
|
|
|
+ )
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化视频时长
|
|
|
+ * @param newVal 播放状态
|
|
|
+ * @param repeat 是否为定时发送的
|
|
|
+ */
|
|
|
+ const initVideoCount = (newVal: any, repeat = false) => {
|
|
|
+ // console.log('watch', forms.player.currentTime)
|
|
|
+ const activeVideoRef = data.videoItemRef?.getPlyrRef()
|
|
|
+ const initTime = deepClone(tempTime)
|
|
|
+ if (repeat) {
|
|
|
+ if (tempTime.length > 0) {
|
|
|
+ // console.log('join video', tempTime, 'initTime', initTime)
|
|
|
+ tempTime[1] = Math.floor(activeVideoRef.currentTime)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (newVal) {
|
|
|
+ tempTime[0] = Math.floor(activeVideoRef.currentTime)
|
|
|
+ } else {
|
|
|
+ tempTime[1] = Math.floor(activeVideoRef.currentTime)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // console.log(newVal, repeat, tempTime, tempTime.length, 'videoIntervalRef.isActive.value in')
|
|
|
+ // console.log(activeVideoRef.speed, 'speed')
|
|
|
+
|
|
|
+ if (tempTime.length >= 2) {
|
|
|
+ // console.log(tempTime, 'tempTime', moreTime.value)
|
|
|
+ // 处理在短时间内的时间差 【视屏拖动,点击可能会导致时间差太大】
|
|
|
+ const diffTime = tempTime[1] - tempTime[0] - currentTimer.counter.value > 2
|
|
|
+ // 结束时间,如果 大于开始时间则清除
|
|
|
+ if (tempTime[1] >= tempTime[0] && !diffTime) {
|
|
|
+ data.itemList[popupData.activeIndex].moreTime.push(tempTime)
|
|
|
+ // moreTime.value.push(tempTime)
|
|
|
+ }
|
|
|
+ if (repeat) {
|
|
|
+ tempTime = deepClone(initTime)
|
|
|
+ } else {
|
|
|
+ tempTime = []
|
|
|
+ currentTimer.counter.value = 0
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 更新时间
|
|
|
+ const updateStat = async (pageBrowseTime = 10) => {
|
|
|
+ try {
|
|
|
+ // const videoBrowseData = moreTime.value.length > 0 ? formatEffectiveTime(moreTime.value) : []
|
|
|
+ // console.log(moreTime.value, videoBrowseData, 'video')
|
|
|
+ // const time = videoBrowseData.length > 0 ? formatTimer(videoBrowseData) : 0
|
|
|
+ // const videoCountTime = videoIntervalRef?.counter.value
|
|
|
+ // 判断如何视屏播放时间大于视屏播放有效时间则说明数据有问题,进行重置数据
|
|
|
+ // const rate = Math.floor((time / Math.floor(forms.pointVideoTime)) * 100)
|
|
|
+ // console.log('videoIntervalRef?.counter.value', videoIntervalRef?.counter.value)
|
|
|
+ // await request.post('/api-student/studentCoursewareMaterialRelation/save', {
|
|
|
+ // data: {
|
|
|
+ // lessonCoursewareDetailId: route.query.id,
|
|
|
+ // pageBrowseTime, // 固定10秒
|
|
|
+ // videoBrowseData: JSON.stringify(videoBrowseData), // 视屏播放数据
|
|
|
+ // videoBrowseDataTime: time || 0, // 有效的视频观看时长
|
|
|
+ // // videoBrowseTime: videoIntervalRef?.counter.value, // 视频观看时长
|
|
|
+ // // videoBrowsePoint: Math.floor(forms.player.currentTime || 0) // 视频最后观看点 - 向下取整
|
|
|
+ // }
|
|
|
+ // })
|
|
|
+ } catch {
|
|
|
+ //
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /** 统计视频播放时间段 */
|
|
|
return () => (
|
|
|
<div id="playContent" class={styles.playContent}>
|
|
|
<div
|
|
@@ -663,10 +812,15 @@ export default defineComponent({
|
|
|
onClose={setModelOpen}
|
|
|
onPlay={() => {
|
|
|
data.videoState = 'play'
|
|
|
+
|
|
|
+ // 设置视频时长
|
|
|
+ const videoTime = data.videoItemRef.getPlyrRef().duration || 0
|
|
|
+ data.itemList[popupData.activeIndex].videoTime = Math.floor(videoTime)
|
|
|
}}
|
|
|
onPause={() => {
|
|
|
clearTimeout(activeData.timer)
|
|
|
activeData.model = true
|
|
|
+ videoIntervalRef.pause()
|
|
|
}}
|
|
|
onEnded={() => {
|
|
|
const _index = popupData.activeIndex + 1
|
|
@@ -674,6 +828,25 @@ export default defineComponent({
|
|
|
handleSwipeChange(_index)
|
|
|
}
|
|
|
}}
|
|
|
+ onSeeked={() => {
|
|
|
+ videoIntervalRef.isActive.value && videoIntervalRef.pause()
|
|
|
+ }}
|
|
|
+ onSeeking={() => {
|
|
|
+ videoIntervalRef.isActive.value && videoIntervalRef.pause()
|
|
|
+ }}
|
|
|
+ onWaiting={() => {
|
|
|
+ videoIntervalRef.isActive.value && videoIntervalRef.pause()
|
|
|
+ }}
|
|
|
+ onTimeupdate={() => {
|
|
|
+ const activeVideoRef = data.videoItemRef?.getPlyrRef()
|
|
|
+ if (
|
|
|
+ !videoIntervalRef.isActive.value &&
|
|
|
+ activeVideoRef?.currentTime > 0 &&
|
|
|
+ activeVideoRef?.playing
|
|
|
+ ) {
|
|
|
+ videoIntervalRef.resume()
|
|
|
+ }
|
|
|
+ }}
|
|
|
/>
|
|
|
</div>
|
|
|
{data.itemList.map((m: any, mIndex: number) => {
|