|
|
@@ -0,0 +1,263 @@
|
|
|
+<template>
|
|
|
+ <div class="video">
|
|
|
+ <div class="video-left">
|
|
|
+ <div class="video-left-top">
|
|
|
+ <n-input
|
|
|
+ placeholder="搜索"
|
|
|
+ clearable
|
|
|
+ style=" width: 150px;height:28px;"
|
|
|
+ size="tiny"
|
|
|
+ />
|
|
|
+ <n-select
|
|
|
+ v-model:value="selectRow"
|
|
|
+ :options="option"
|
|
|
+ size="small"
|
|
|
+ @update:value="onChange"
|
|
|
+ />
|
|
|
+ <n-button
|
|
|
+ secondary
|
|
|
+ size="small"
|
|
|
+ style="margin-left: 10px;"
|
|
|
+ @click="onChange(selectRow)"
|
|
|
+ >
|
|
|
+ 清空
|
|
|
+ </n-button>
|
|
|
+ </div>
|
|
|
+ <div class="video-left-tree">
|
|
|
+ <n-scrollbar>
|
|
|
+ <n-tree
|
|
|
+ :data="treeData"
|
|
|
+ block-line
|
|
|
+ expand-on-click
|
|
|
+ draggable
|
|
|
+ cascade
|
|
|
+ accordion
|
|
|
+ @dragend="onDrop"
|
|
|
+ />
|
|
|
+ </n-scrollbar>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="video-right">
|
|
|
+ <template
|
|
|
+ v-for="(item, index) in videoItem"
|
|
|
+ :key="index"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ id="videoBoxs"
|
|
|
+ class="video-right-item"
|
|
|
+ :style="`width:${(100 / selectRow) - 0.05}%;height:${(100 / selectRow) - 0.15}%;`"
|
|
|
+ >
|
|
|
+ <div :id="item.id" />
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang='ts'>
|
|
|
+import {
|
|
|
+ onMounted, onUnmounted, ref, watch
|
|
|
+} from 'vue'
|
|
|
+import EzVideoService from './ezVideo.service'
|
|
|
+import { useNotification } from 'naive-ui'
|
|
|
+
|
|
|
+interface VideoItem {
|
|
|
+ id: string,
|
|
|
+ liveUrl: string,
|
|
|
+ accessToken: string,
|
|
|
+ video: undefined | any,
|
|
|
+ play: (a: number, b: number) => void,
|
|
|
+ destory: () => void,
|
|
|
+ resize: (a: number, b: number) => void
|
|
|
+}
|
|
|
+
|
|
|
+const notification = useNotification()
|
|
|
+const option = [
|
|
|
+ { label: '1分屏', value: 1 },
|
|
|
+ { label: '4分屏', value: 2 },
|
|
|
+ { label: '9分屏', value: 3 }
|
|
|
+]
|
|
|
+const selectRow = ref(2)
|
|
|
+const videoItem = ref([] as VideoItem[])
|
|
|
+const treeData = ref([
|
|
|
+ {
|
|
|
+ key: '1',
|
|
|
+ label: '1',
|
|
|
+ children: [
|
|
|
+ {
|
|
|
+ key: '1-2',
|
|
|
+ label: '2',
|
|
|
+ children: [
|
|
|
+ {
|
|
|
+ accessToken: 'at.a82kqljj96ja8ony6e57nrqi4aigp963-7sn8ji0ji3-16njxp9-rmioevhxz',
|
|
|
+ label: 'test',
|
|
|
+ key: '1-2-1',
|
|
|
+ liveUrl: 'ezopen://aczn1688@open.ys7.com/FH2861570/2.live'
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+])
|
|
|
+
|
|
|
+function onChange(v: number) {
|
|
|
+ // 清空一次
|
|
|
+ if (videoItem.value.length) {
|
|
|
+ for (let k = 0; k < videoItem.value.length; k++) {
|
|
|
+ const el = videoItem.value[k]
|
|
|
+ el.destory()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 重新组装分屏数据
|
|
|
+ const arr = [] as VideoItem[]
|
|
|
+ const n = v * v
|
|
|
+ for (let k = 0; k < n; k++) {
|
|
|
+ const ezVideoService = new EzVideoService()
|
|
|
+ arr.push({
|
|
|
+ id: `video${k}`,
|
|
|
+ liveUrl: '',
|
|
|
+ accessToken: '',
|
|
|
+ video: undefined as any,
|
|
|
+ play(width: number, height: number) {
|
|
|
+ if (this.video) this.destory()
|
|
|
+ if (!width || !height) return
|
|
|
+ this.video = ezVideoService.init({
|
|
|
+ id: this.id, url: this.liveUrl, accessToken: this.accessToken, width, height
|
|
|
+ })
|
|
|
+ },
|
|
|
+ destory() {
|
|
|
+ if (this.video) {
|
|
|
+ this.video.stop()
|
|
|
+ this.video = undefined
|
|
|
+ document.getElementById(this.id)?.remove()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ resize(width: number, height: number) {
|
|
|
+ if (!width || !height) return
|
|
|
+ if (this.video) this.video.reSize(width, height)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ videoItem.value = arr
|
|
|
+}
|
|
|
+
|
|
|
+function onDrop(data: any) {
|
|
|
+ // 查找节点下的摄像头
|
|
|
+ const recursion = (list: any, arr = [] as VideoItem[]) => {
|
|
|
+ for (let k = 0; k < list.length; k++) {
|
|
|
+ const el = list[k]
|
|
|
+ if (el.accessToken) arr.push(el)
|
|
|
+ if (el.children) recursion(el.children, arr)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const arr: VideoItem[] = []
|
|
|
+ recursion([ data.node ], arr)
|
|
|
+ if (!arr.length) {
|
|
|
+ notification.warning({ title: `当前${data.node.label}无视频设备`, duration: 2000 })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 查找没有播放的分屏
|
|
|
+ const index = videoItem.value.findIndex((el) => !el.accessToken && !el.liveUrl)
|
|
|
+ console.log(7777, arr, videoItem.value, index)
|
|
|
+ if (index < 0) return
|
|
|
+ for (let k = 0; k < arr.length; k++) {
|
|
|
+ const el = arr[k]
|
|
|
+ const num = index + k
|
|
|
+ if (num > (videoItem.value.length - 1)) continue
|
|
|
+ videoItem.value[num].accessToken = el.accessToken
|
|
|
+ videoItem.value[num].liveUrl = el.liveUrl
|
|
|
+ setTimeout(() => {
|
|
|
+ const dom = document.getElementById('videoBoxs')
|
|
|
+ videoItem.value[num].play(dom?.clientWidth || 0, dom?.clientHeight || 0)
|
|
|
+ }, 200)
|
|
|
+ }
|
|
|
+ console.log(888, videoItem.value)
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(() => onChange(selectRow.value))
|
|
|
+
|
|
|
+onUnmounted(() => onChange(selectRow.value))
|
|
|
+
|
|
|
+</script>
|
|
|
+<style lang="scss" scoped>
|
|
|
+.video {
|
|
|
+ display: flex;
|
|
|
+ width: 100vw;
|
|
|
+ height: 100vh;
|
|
|
+ box-sizing: border-box;
|
|
|
+ padding: 5px;
|
|
|
+
|
|
|
+ &-left {
|
|
|
+ width: 320px;
|
|
|
+ background: #0c1f3ad2;
|
|
|
+ margin-right: 10px;
|
|
|
+ padding: 10px;
|
|
|
+ box-sizing: border-box;
|
|
|
+
|
|
|
+ &-top {
|
|
|
+ display: flex;
|
|
|
+ margin-bottom: 10px;
|
|
|
+
|
|
|
+ :deep(.n-select) {
|
|
|
+ width: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.n-base-selection) {
|
|
|
+ --n-color-active: rgba(21, 50, 90, 0.6);
|
|
|
+ --n-color-active-error: rgba(21, 50, 90, 0.6);
|
|
|
+ --n-text-color: white;
|
|
|
+ width: 90px;
|
|
|
+ margin-left: 10px;
|
|
|
+
|
|
|
+ .n-base-selection-label {
|
|
|
+ background: rgba(21, 50, 90, 0.6);
|
|
|
+ }
|
|
|
+
|
|
|
+ .n-base-selection__border {
|
|
|
+ border: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .n-base-selection__state-border {
|
|
|
+ border-radius: 2px;
|
|
|
+ border: 1px solid rgba(27, 175, 255, 0.50);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &-tree {
|
|
|
+ height: calc(100% - 38px);
|
|
|
+
|
|
|
+ :deep(.n-tree) {
|
|
|
+ --n-drop-mark-color: rgba(0, 0, 0, 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &-right {
|
|
|
+ width: calc(100% - 330px);
|
|
|
+ height: 100%;
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ background: #646464;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ box-sizing: border-box;
|
|
|
+ padding: 0 1px;
|
|
|
+
|
|
|
+ &-item {
|
|
|
+ background: black;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ &>div {
|
|
|
+ overflow: hidden;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .active {
|
|
|
+ border: solid 1px #FCFF00;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|