feat: 更新视频播放器组件,添加章节视频功能并优化返回逻辑

This commit is contained in:
2026-04-09 16:48:57 +08:00
parent 943cc862fc
commit 17022d498a
19 changed files with 1192 additions and 1599 deletions

View File

@@ -0,0 +1,235 @@
<template>
<view class="course-video">
<view v-if="videoList.length > 0" class="video-player" :class="{'full-screen': isFullScreen}">
<VideoPlayer
ref="commonVideo"
:videoTitle="videoTitle"
:currentVideoIndex="currentVideoIndex"
:currentVideoList="videoList"
:http="http"
:poster="cover"
@unlockChangeVideo="unlockChangeVideo"
@changeVideo="changeVideo"
@fullScreenChange="changeScreen"
/>
</view>
<view v-if="videoList.length > 0" style="height: 400rpx;"></view>
<view class="course-info">
<view class="course-flag">视频教学</view>
<view class="course-title">课程{{ course.courseTitle || '' }}</view>
<view class="chapter-title">章节{{ course.chapterTitle || '' }}</view>
<view v-if="videoList.length > 0" scroll-x="true" class="video-list">
<view v-for="(v, i) in videoList" :class="`video_item ${currentVideo.id == v.id ? 'active' : ''}`"
@click="handleClick(v,i)">
{{ v.type == "2" ? "音频" : "视频" }}{{ getNumber(i + 1) }}
</view>
</view>
</view>
</view>
</template>
<script>
import VideoPlayer from '@/components/video-player/index.vue'
import { BEFORE_NAVIGATE_BACK_EVENT } from '@/components/nav-bar/back-events'
export default {
components: {
VideoPlayer,
},
props: {
course: {
type: Object,
default: () => ({
courseTitle: '',
chapterTitle: '',
}),
},
cover: {
type: String,
default: '',
},
videoList: {
type: Array,
default: () => [],
},
currentIndex: {
type: Number,
default: 0,
},
http: {
type: Object,
default: () => {},
},
},
computed: {
videoTitle() {
return (this.course.chapterTitle || '') + ' ' + (this.getNumber(this.currentVideoIndex + 1))
},
},
watch: {
videoList: {
handler(newVal) {
this.currentVideoIndex = this.currentIndex;
if (newVal.length > 0) {
this.currentVideo = newVal[this.currentVideoIndex];
}
},
immediate: true,
},
},
data() {
return {
currentVideoIndex: this.currentIndex,
currentVideo: this.videoList[this.currentVideoIndex] || {},
screenLoading: false,
isFullScreen: false,
changeVideoLock: false,
_backHandler: null,
}
},
created() {
this._bindBackEvent()
},
beforeUnmount() {
this._unbindBackEvent()
this.unload()
},
beforeDestroy() {
this._unbindBackEvent()
this.unload()
},
methods: {
_bindBackEvent() {
if (this._backHandler) return
this._backHandler = () => { this.unload() }
uni.$on(BEFORE_NAVIGATE_BACK_EVENT, this._backHandler)
},
_unbindBackEvent() {
if (!this._backHandler) return
uni.$off(BEFORE_NAVIGATE_BACK_EVENT, this._backHandler)
this._backHandler = null
},
unload() {
this.$refs.commonVideo && this.$refs.commonVideo.unload && this.$refs.commonVideo.unload()
},
handleClick(v, i) {
if (this.changeVideoLock) {
uni.showToast({
title: '操作太频繁了',
icon: 'none',
})
return
}
this.currentVideoIndex = i;
this.currentVideo = v;
this.changeVideo(this.currentVideoIndex);
},
changeVideo(currentVideoIndex, type) {
switch (type) {
case 'next':
this.currentVideoIndex = currentVideoIndex + 1;
break;
case 'prev':
this.currentVideoIndex = currentVideoIndex - 1;
break;
default:
this.currentVideoIndex = currentVideoIndex;
break;
}
this.currentVideo = this.videoList[this.currentVideoIndex];
},
getNumber(num) {
return num >= 10 ? num : `0${num}`;
},
changeScreenLoading(status) {
this.screenLoading = status;
},
changeScreen(status) {
this.isFullScreen = status;
},
unlockChangeVideo() {
this.changeVideoLock = false
},
initVideo() {
this.changeVideoLock = true
this.screenLoading = false;
this.$nextTick(() => {
if (this.currentVideo.type != 2 && this.$refs.commonVideo && this.$refs.commonVideo.init) {
this.$refs.commonVideo.init({
currentVideo: this.currentVideo,
currentVideoList: this.videoList,
});
}
this.changeVideoLock = false
});
},
},
}
</script>
<style lang="scss" scoped>
.video-player {
position: fixed;
left: 0;
width: 100%;
height: 400rpx;
z-index: 2;
background-color: #000;
&.full-screen {
z-index: 9999999;
}
}
.course-info {
padding: 20rpx;
}
.course-flag {
font-size: 40rpx; color: #3983FF;
font-family: PangMenZhengDaoBiaoTiTiMianFeiBan;
padding: 20rpx 0;
}
.course-title {
font-size: 32rpx;
line-height: 1.2;
padding: 20rpx 0;
}
.chapter-title {
font-size: 28rpx;
line-height: 1.2;
padding: 20rpx 0;
}
.video-list {
width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
align-items: center;
gap: 20rpx;
padding: 20rpx 0;
margin-top: 10rpx;
.video_item {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
background: #fff;
color: #5188e5;
border-radius: 10rpx;
line-height: 1;
border: 1px solid #5188e5;
box-shadow: 0px 0px 6rpx 0px rgba(255, 255, 255, 1);
padding: 18rpx 20rpx;
font-weight: bold;
font-size: 28rpx;
&:nth-child(4n) {
margin-right: 0;
}
&.active {
background-color: #5188e5 !important;
color: #fff !important;
}
}
}
</style>