243 lines
4.5 KiB
Vue
243 lines
4.5 KiB
Vue
<template>
|
|
<view class="video-player">
|
|
<!-- 视频播放器 -->
|
|
<video
|
|
v-if="currentVideo"
|
|
:id="videoId"
|
|
:src="currentVideo.url"
|
|
:title="currentVideo.title"
|
|
:controls="true"
|
|
:show-fullscreen-btn="true"
|
|
:show-play-btn="true"
|
|
:enable-progress-gesture="true"
|
|
:object-fit="objectFit"
|
|
class="video-element"
|
|
@fullscreenchange="handleFullscreenChange"
|
|
@ended="handleVideoEnd"
|
|
@error="handleVideoError"
|
|
/>
|
|
|
|
<!-- 自动播放下一个提示 -->
|
|
<view v-if="showCountDown && hasNext" class="countdown-overlay">
|
|
<view class="countdown-content">
|
|
<text class="countdown-text">{{ countDownSeconds }}秒后自动播放下一个</text>
|
|
<wd-button size="small" @click="cancelAutoPlay">
|
|
取消自动播放
|
|
</wd-button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
|
|
import type { IVideo } from '@/types/course'
|
|
|
|
interface Props {
|
|
videoList: IVideo[]
|
|
currentIndex: number
|
|
noRecored?: boolean // 是否为试听(不记录进度)
|
|
}
|
|
|
|
const props = defineProps<Props>()
|
|
|
|
const emit = defineEmits<{
|
|
end: []
|
|
fullscreen: [isFullScreen: boolean]
|
|
change: [index: number]
|
|
}>()
|
|
|
|
const videoId = 'course-video-player'
|
|
const videoContext = ref<any>(null)
|
|
const objectFit = ref<'contain' | 'fill' | 'cover'>('contain')
|
|
const showCountDown = ref(false)
|
|
const countDownSeconds = ref(10)
|
|
const countDownTimer = ref<any>(null)
|
|
|
|
/**
|
|
* 当前视频
|
|
*/
|
|
const currentVideo = computed(() => {
|
|
if (props.videoList.length === 0) return null
|
|
return props.videoList[props.currentIndex] || null
|
|
})
|
|
|
|
/**
|
|
* 是否有下一个视频
|
|
*/
|
|
const hasNext = computed(() => {
|
|
return props.currentIndex < props.videoList.length - 1
|
|
})
|
|
|
|
/**
|
|
* 初始化视频上下文
|
|
*/
|
|
const initVideoContext = () => {
|
|
videoContext.value = uni.createVideoContext(videoId)
|
|
}
|
|
|
|
/**
|
|
* 视频播放结束
|
|
*/
|
|
const handleVideoEnd = () => {
|
|
emit('end')
|
|
|
|
// 如果有下一个视频,开始倒计时
|
|
if (hasNext.value) {
|
|
startCountDown()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 开始倒计时
|
|
*/
|
|
const startCountDown = () => {
|
|
showCountDown.value = true
|
|
countDownSeconds.value = 10
|
|
|
|
countDownTimer.value = setInterval(() => {
|
|
countDownSeconds.value--
|
|
|
|
if (countDownSeconds.value <= 0) {
|
|
stopCountDown()
|
|
playNext()
|
|
}
|
|
}, 1000)
|
|
}
|
|
|
|
/**
|
|
* 停止倒计时
|
|
*/
|
|
const stopCountDown = () => {
|
|
if (countDownTimer.value) {
|
|
clearInterval(countDownTimer.value)
|
|
countDownTimer.value = null
|
|
}
|
|
showCountDown.value = false
|
|
}
|
|
|
|
/**
|
|
* 取消自动播放
|
|
*/
|
|
const cancelAutoPlay = () => {
|
|
stopCountDown()
|
|
}
|
|
|
|
/**
|
|
* 播放下一个视频
|
|
*/
|
|
const playNext = () => {
|
|
if (hasNext.value) {
|
|
emit('change', props.currentIndex + 1)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 全屏变化
|
|
*/
|
|
const handleFullscreenChange = (e: any) => {
|
|
const isFullScreen = e.detail.fullScreen
|
|
emit('fullscreen', isFullScreen)
|
|
|
|
// 全屏时使用 cover 模式
|
|
objectFit.value = isFullScreen ? 'cover' : 'contain'
|
|
}
|
|
|
|
/**
|
|
* 视频错误
|
|
*/
|
|
const handleVideoError = (e: any) => {
|
|
console.error('视频播放错误:', e)
|
|
uni.showToast({
|
|
title: '课程视频播放组件正在开发中,现在还不能播放视频',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 播放视频
|
|
*/
|
|
const play = () => {
|
|
if (videoContext.value) {
|
|
videoContext.value.play()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 暂停视频
|
|
*/
|
|
const pause = () => {
|
|
if (videoContext.value) {
|
|
videoContext.value.pause()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 停止视频
|
|
*/
|
|
const stop = () => {
|
|
if (videoContext.value) {
|
|
videoContext.value.stop()
|
|
}
|
|
}
|
|
|
|
// 监听视频变化,重新播放
|
|
watch(() => props.currentIndex, () => {
|
|
stopCountDown()
|
|
// 延迟播放,确保视频元素已更新
|
|
setTimeout(() => {
|
|
play()
|
|
}, 300)
|
|
})
|
|
|
|
onMounted(() => {
|
|
initVideoContext()
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
stopCountDown()
|
|
stop()
|
|
})
|
|
|
|
// 暴露方法给父组件
|
|
defineExpose({
|
|
play,
|
|
pause,
|
|
stop,
|
|
cancelAutoPlay: stopCountDown
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.video-player {
|
|
position: relative;
|
|
width: 100%;
|
|
background-color: #000;
|
|
|
|
.video-element {
|
|
width: 100%;
|
|
height: 400rpx;
|
|
}
|
|
|
|
.countdown-overlay {
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
background: rgba(0, 0, 0, 0.7);
|
|
padding: 20rpx;
|
|
|
|
.countdown-content {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
|
|
.countdown-text {
|
|
color: #fff;
|
|
font-size: 28rpx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|