202 lines
4.7 KiB
Vue
202 lines
4.7 KiB
Vue
<template>
|
|
<div class="examine-all">
|
|
<el-dialog
|
|
:title="title"
|
|
align="center"
|
|
:visible.sync="dialogVisible"
|
|
width="90%"
|
|
:close-on-click-modal="false"
|
|
:before-close="handleClose"
|
|
@opened="onOpened"
|
|
>
|
|
<div class="media-box">
|
|
<div
|
|
class="video-box"
|
|
v-for="(item, i) in list"
|
|
:key="item.url + '-' + i"
|
|
>
|
|
<div class="title" :title="item.title || '未知'">
|
|
{{ item.title || "未知" }}
|
|
</div>
|
|
<video
|
|
ref="videoEls"
|
|
class="preview-video"
|
|
controls
|
|
preload="auto"
|
|
playsinline
|
|
></video>
|
|
</div>
|
|
<div v-if="!list.length" class="empty-tip">当前列表没有可预览的视频</div>
|
|
</div>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import Hls from "hls.js";
|
|
import baseConfig from "@/utils/serverUrlConfig";
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
title: "查看当前列表所有视频",
|
|
dialogVisible: false,
|
|
list: [],
|
|
hlsList: []
|
|
};
|
|
},
|
|
computed: {
|
|
token() {
|
|
return this.$store.state.token;
|
|
}
|
|
},
|
|
beforeDestroy() {
|
|
this.destroyPlayers();
|
|
},
|
|
methods: {
|
|
getVideoEl(i) {
|
|
const refs = this.$refs.videoEls;
|
|
if (!refs) return null;
|
|
return Array.isArray(refs) ? refs[i] : i === 0 ? refs : null;
|
|
},
|
|
destroyPlayers() {
|
|
this.hlsList.forEach(hls => {
|
|
if (hls) hls.destroy();
|
|
});
|
|
this.hlsList = [];
|
|
|
|
const refs = this.$refs.videoEls;
|
|
const videos = !refs ? [] : Array.isArray(refs) ? refs : [refs];
|
|
videos.forEach(video => {
|
|
if (!video) return;
|
|
video.pause();
|
|
video.removeAttribute("src");
|
|
video.load();
|
|
});
|
|
},
|
|
handleClose(done) {
|
|
this.destroyPlayers();
|
|
this.list = [];
|
|
this.dialogVisible = false;
|
|
if (typeof done === "function") {
|
|
done();
|
|
}
|
|
},
|
|
buildMediaUrl(url) {
|
|
if (!url) return "";
|
|
let path = String(url);
|
|
if (path.slice(0, 1) === "/") {
|
|
path = path.slice(1);
|
|
}
|
|
return (
|
|
baseConfig.baseServerUrl +
|
|
baseConfig.playVideoUrl +
|
|
path +
|
|
"?token=" +
|
|
this.token
|
|
);
|
|
},
|
|
initHlsPlayer(i, src) {
|
|
const video = this.getVideoEl(i);
|
|
if (!video || !src) return;
|
|
|
|
if (Hls.isSupported()) {
|
|
const hls = new Hls({
|
|
enableWorker: true,
|
|
maxBufferLength: 15,
|
|
maxMaxBufferLength: 180
|
|
});
|
|
this.hlsList[i] = hls;
|
|
hls.loadSource(src);
|
|
hls.attachMedia(video);
|
|
hls.on(Hls.Events.ERROR, (event, data) => {
|
|
if (!data.fatal || !this.hlsList[i]) return;
|
|
if (data.type === Hls.ErrorTypes.NETWORK_ERROR) {
|
|
this.hlsList[i].startLoad();
|
|
} else if (data.type === Hls.ErrorTypes.MEDIA_ERROR) {
|
|
this.hlsList[i].recoverMediaError();
|
|
} else {
|
|
this.hlsList[i].destroy();
|
|
this.hlsList[i] = null;
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Safari 等原生 HLS
|
|
if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
video.src = src;
|
|
}
|
|
},
|
|
initAllPlayers() {
|
|
if (!this.dialogVisible || !this.list.length) return;
|
|
// 先清旧实例,避免 opened / nextTick 重复初始化
|
|
this.destroyPlayers();
|
|
this.list.forEach((item, i) => {
|
|
this.initHlsPlayer(i, this.buildMediaUrl(item.url));
|
|
});
|
|
},
|
|
onOpened() {
|
|
this.$nextTick(() => {
|
|
this.initAllPlayers();
|
|
});
|
|
},
|
|
open(list) {
|
|
this.destroyPlayers();
|
|
this.list = (list || []).filter(item => item && item.url);
|
|
// 已打开时不会再触发 opened,需要手动初始化
|
|
if (this.dialogVisible) {
|
|
this.$nextTick(() => {
|
|
this.initAllPlayers();
|
|
});
|
|
return;
|
|
}
|
|
this.dialogVisible = true;
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.examine-all {
|
|
.media-box {
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow: auto;
|
|
}
|
|
.el-dialog {
|
|
margin-top: 70px !important;
|
|
}
|
|
.el-dialog__body {
|
|
overflow: auto;
|
|
max-height: calc(100vh - 200px);
|
|
}
|
|
.empty-tip {
|
|
text-align: center;
|
|
color: #999;
|
|
padding: 40px 0;
|
|
}
|
|
.video-box {
|
|
width: 19%;
|
|
float: left;
|
|
height: 330px;
|
|
margin-left: 1%;
|
|
margin-bottom: 30px;
|
|
.preview-video {
|
|
width: 100%;
|
|
height: calc(100% - 30px);
|
|
background: #000;
|
|
object-fit: contain;
|
|
}
|
|
.title {
|
|
width: 100%;
|
|
height: 30px;
|
|
line-height: 30px;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
}
|
|
}
|
|
</style>
|