|
@@ -1,6 +1,12 @@
|
|
|
<template>
|
|
<template>
|
|
|
<div style="height: 100%">
|
|
<div style="height: 100%">
|
|
|
- <el-card id="devicePosition" shadow="never" style="height: calc(100% - 40px);border: 0;" :body-style="{ padding: '0px' }" class="border-none">
|
|
|
|
|
|
|
+ <el-card
|
|
|
|
|
+ id="devicePosition"
|
|
|
|
|
+ shadow="never"
|
|
|
|
|
+ style="height: calc(100% - 40px); border: 0"
|
|
|
|
|
+ :body-style="{ padding: '0px' }"
|
|
|
|
|
+ class="border-none"
|
|
|
|
|
+ >
|
|
|
<el-container
|
|
<el-container
|
|
|
v-loading="loading"
|
|
v-loading="loading"
|
|
|
style="height: 100%"
|
|
style="height: 100%"
|
|
@@ -66,7 +72,8 @@
|
|
|
<player
|
|
<player
|
|
|
v-else
|
|
v-else
|
|
|
:ref="(el) => setPlayerRef(el, i - 1)"
|
|
:ref="(el) => setPlayerRef(el, i - 1)"
|
|
|
- :video-url="videoUrl[i - 1]"
|
|
|
|
|
|
|
+ :videourl="videoUrl[i - 1]"
|
|
|
|
|
+ :playerInfo="playInfoRes"
|
|
|
fluent
|
|
fluent
|
|
|
autoplay
|
|
autoplay
|
|
|
@screenshot="shot"
|
|
@screenshot="shot"
|
|
@@ -85,10 +92,11 @@
|
|
|
import { ref, reactive, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
|
|
import { ref, reactive, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
|
|
|
import { useRoute } from 'vue-router'
|
|
import { useRoute } from 'vue-router'
|
|
|
import { FullScreen, Menu, Grid } from '@element-plus/icons-vue'
|
|
import { FullScreen, Menu, Grid } from '@element-plus/icons-vue'
|
|
|
-import player from './components/player/jessibuca.vue'
|
|
|
|
|
|
|
+// import player from './components/player/jessibuca.vue'
|
|
|
|
|
+import player from './components/player/easy.vue'
|
|
|
import DeviceTree from './components/player/DeviceTree.vue'
|
|
import DeviceTree from './components/player/DeviceTree.vue'
|
|
|
import { startPlay } from '@/api/pms/video/channel'
|
|
import { startPlay } from '@/api/pms/video/channel'
|
|
|
-import { listSipDeviceChannel } from '@/api/pms/video/sipdevice';
|
|
|
|
|
|
|
+import { listSipDeviceChannel } from '@/api/pms/video/sipdevice'
|
|
|
|
|
|
|
|
const { t } = useI18n()
|
|
const { t } = useI18n()
|
|
|
|
|
|
|
@@ -107,8 +115,6 @@ const count = ref(15)
|
|
|
const total = ref(0)
|
|
const total = ref(0)
|
|
|
const loading = ref(false)
|
|
const loading = ref(false)
|
|
|
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
// 设置播放器引用
|
|
// 设置播放器引用
|
|
|
const setPlayerRef = (el, index) => {
|
|
const setPlayerRef = (el, index) => {
|
|
|
if (el) {
|
|
if (el) {
|
|
@@ -146,24 +152,25 @@ watch(
|
|
|
async (newSplit, oldSplit) => {
|
|
async (newSplit, oldSplit) => {
|
|
|
// 只有当分屏数增加(例如从1到4,或从4到9)且有当前选中设备时才执行
|
|
// 只有当分屏数增加(例如从1到4,或从4到9)且有当前选中设备时才执行
|
|
|
if (newSplit > oldSplit && currentDevice.value) {
|
|
if (newSplit > oldSplit && currentDevice.value) {
|
|
|
- console.log(`分屏从 ${oldSplit} 变为 ${newSplit},自动填充设备 ${currentDevice.value.name} 的通道`)
|
|
|
|
|
-
|
|
|
|
|
|
|
+ console.log(
|
|
|
|
|
+ `分屏从 ${oldSplit} 变为 ${newSplit},自动填充设备 ${currentDevice.value.name} 的通道`
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
// 延迟执行,确保DOM更新完成
|
|
// 延迟执行,确保DOM更新完成
|
|
|
await nextTick()
|
|
await nextTick()
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
loading.value = true
|
|
loading.value = true
|
|
|
try {
|
|
try {
|
|
|
// 1. 重新获取当前设备的通道
|
|
// 1. 重新获取当前设备的通道
|
|
|
const channels = await getDeviceChannels(currentDevice.value.id)
|
|
const channels = await getDeviceChannels(currentDevice.value.id)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (!channels || channels.length === 0) {
|
|
if (!channels || channels.length === 0) {
|
|
|
console.warn('该设备下没有可用通道')
|
|
console.warn('该设备下没有可用通道')
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 2. 重新分配通道到所有播放器
|
|
// 2. 重新分配通道到所有播放器
|
|
|
await assignChannelsToPlayers(channels)
|
|
await assignChannelsToPlayers(channels)
|
|
|
-
|
|
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
console.error('分屏变化时填充通道出错:', error)
|
|
console.error('分屏变化时填充通道出错:', error)
|
|
|
ElMessage.error('自动填充通道失败')
|
|
ElMessage.error('自动填充通道失败')
|
|
@@ -209,72 +216,81 @@ const destroy = (idx) => {
|
|
|
}
|
|
}
|
|
|
const currentDevice = ref(null) // 存储当前选中的设备信息
|
|
const currentDevice = ref(null) // 存储当前选中的设备信息
|
|
|
// 点击事件
|
|
// 点击事件
|
|
|
|
|
+let playerInfo = ref([])
|
|
|
const clickEvent = async (data) => {
|
|
const clickEvent = async (data) => {
|
|
|
// 情况1:点击的是设备节点 (type === 0)
|
|
// 情况1:点击的是设备节点 (type === 0)
|
|
|
if (data.type === 0) {
|
|
if (data.type === 0) {
|
|
|
- currentDevice.value = {
|
|
|
|
|
|
|
+ currentDevice.value = {
|
|
|
id: data.userData?.serialNumber,
|
|
id: data.userData?.serialNumber,
|
|
|
name: data.name,
|
|
name: data.name,
|
|
|
data: data
|
|
data: data
|
|
|
}
|
|
}
|
|
|
- const deviceId = data.userData?.serialNumber; // 从树节点数据中提取设备ID
|
|
|
|
|
- if (!deviceId) return;
|
|
|
|
|
|
|
+ const deviceId = data.userData?.serialNumber // 从树节点数据中提取设备ID
|
|
|
|
|
+ if (!deviceId) return
|
|
|
|
|
|
|
|
- loading.value = true;
|
|
|
|
|
|
|
+ loading.value = true
|
|
|
try {
|
|
try {
|
|
|
// 1. 获取该设备下的所有通道
|
|
// 1. 获取该设备下的所有通道
|
|
|
- const channels = await getDeviceChannels(deviceId);
|
|
|
|
|
|
|
+ const channels = await getDeviceChannels(deviceId)
|
|
|
|
|
+ channels.forEach((channel) => {
|
|
|
|
|
+ if (channel.basicData.model === 'Camera') {
|
|
|
|
|
+ playerInfo.value.push(channel)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ console.log('playerInfo>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>', playerInfo.value)
|
|
|
|
|
+ console.log('videourl**************************', videoUrl.value)
|
|
|
|
|
+
|
|
|
if (!channels || channels.length === 0) {
|
|
if (!channels || channels.length === 0) {
|
|
|
- console.warn('该设备下没有可用通道');
|
|
|
|
|
- return;
|
|
|
|
|
|
|
+ console.warn('该设备下没有可用通道')
|
|
|
|
|
+ return
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 2. 将通道智能分配给播放器
|
|
// 2. 将通道智能分配给播放器
|
|
|
- await assignChannelsToPlayers(channels);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ await assignChannelsToPlayers(channels)
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
- console.error('处理设备通道时出错:', error);
|
|
|
|
|
|
|
+ console.error('处理设备通道时出错:', error)
|
|
|
} finally {
|
|
} finally {
|
|
|
- loading.value = false;
|
|
|
|
|
|
|
+ loading.value = false
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
// 情况2:点击的是具体的通道节点,保持原有逻辑不变
|
|
// 情况2:点击的是具体的通道节点,保持原有逻辑不变
|
|
|
else if (data.userData?.channelSipId) {
|
|
else if (data.userData?.channelSipId) {
|
|
|
- sendDevicePush(data.userData);
|
|
|
|
|
|
|
+ sendDevicePush(data.userData)
|
|
|
}
|
|
}
|
|
|
-};
|
|
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
const getDeviceChannels = async (deviceId) => {
|
|
const getDeviceChannels = async (deviceId) => {
|
|
|
try {
|
|
try {
|
|
|
- const response = await listSipDeviceChannel(deviceId);
|
|
|
|
|
- console.log('获取设备通道列表成功:', response);
|
|
|
|
|
|
|
+ const response = await listSipDeviceChannel(deviceId)
|
|
|
|
|
+ console.log('获取设备通道列表成功:', response)
|
|
|
// 假设接口返回的是通道数组,且每个通道对象结构符合 { basicData: { channelSipId, ... }, ... }
|
|
// 假设接口返回的是通道数组,且每个通道对象结构符合 { basicData: { channelSipId, ... }, ... }
|
|
|
- return response || [];
|
|
|
|
|
|
|
+ return response || []
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
- console.error('获取设备通道列表失败:', error);
|
|
|
|
|
- return [];
|
|
|
|
|
|
|
+ console.error('获取设备通道列表失败:', error)
|
|
|
|
|
+ return []
|
|
|
}
|
|
}
|
|
|
-};
|
|
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
const assignChannelsToPlayers = async (channels) => {
|
|
const assignChannelsToPlayers = async (channels) => {
|
|
|
const currentSplit = spilt.value
|
|
const currentSplit = spilt.value
|
|
|
console.log(`分配 ${channels.length} 个通道到 ${currentSplit} 个播放窗口`)
|
|
console.log(`分配 ${channels.length} 个通道到 ${currentSplit} 个播放窗口`)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 决定从哪个播放器窗口开始分配:始终从第一个窗口(0)开始
|
|
// 决定从哪个播放器窗口开始分配:始终从第一个窗口(0)开始
|
|
|
// 这样保证每次分屏变化时都能从头开始重新分配
|
|
// 这样保证每次分屏变化时都能从头开始重新分配
|
|
|
const startPlayerIndex = 0
|
|
const startPlayerIndex = 0
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 决定从哪个通道开始取:总是从第一个通道开始
|
|
// 决定从哪个通道开始取:总是从第一个通道开始
|
|
|
const startChannelIndex = 0
|
|
const startChannelIndex = 0
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 循环,为当前分屏布局中的每个位置分配通道
|
|
// 循环,为当前分屏布局中的每个位置分配通道
|
|
|
for (let screenPosition = 0; screenPosition < currentSplit; screenPosition++) {
|
|
for (let screenPosition = 0; screenPosition < currentSplit; screenPosition++) {
|
|
|
const channelIndex = startChannelIndex + screenPosition
|
|
const channelIndex = startChannelIndex + screenPosition
|
|
|
const targetPlayerIndex = startPlayerIndex + screenPosition
|
|
const targetPlayerIndex = startPlayerIndex + screenPosition
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 延迟执行,避免同时发起大量请求
|
|
// 延迟执行,避免同时发起大量请求
|
|
|
- await new Promise(resolve => setTimeout(resolve, 50))
|
|
|
|
|
-
|
|
|
|
|
|
|
+ await new Promise((resolve) => setTimeout(resolve, 50))
|
|
|
|
|
+
|
|
|
if (channelIndex < channels.length) {
|
|
if (channelIndex < channels.length) {
|
|
|
// 有对应通道,开始播放
|
|
// 有对应通道,开始播放
|
|
|
const channelData = channels[channelIndex]
|
|
const channelData = channels[channelIndex]
|
|
@@ -301,39 +317,45 @@ const assignChannelsToPlayers = async (channels) => {
|
|
|
// 通知设备上传媒体流
|
|
// 通知设备上传媒体流
|
|
|
const sendDevicePush = (itemData, targetIndex = null) => {
|
|
const sendDevicePush = (itemData, targetIndex = null) => {
|
|
|
// 关键修改:允许从外部传入 targetIndex,默认使用当前激活的 playerIdx
|
|
// 关键修改:允许从外部传入 targetIndex,默认使用当前激活的 playerIdx
|
|
|
- const playIndex = targetIndex !== null ? targetIndex : playerIdx.value;
|
|
|
|
|
|
|
+ const playIndex = targetIndex !== null ? targetIndex : playerIdx.value
|
|
|
|
|
|
|
|
- save(itemData, playIndex); // 保存数据也要使用正确的索引
|
|
|
|
|
|
|
+ save(itemData, playIndex) // 保存数据也要使用正确的索引
|
|
|
|
|
|
|
|
- let deviceId = itemData.deviceSipId;
|
|
|
|
|
- let channelId = itemData.channelSipId;
|
|
|
|
|
- loading.value = true;
|
|
|
|
|
|
|
+ let deviceId = itemData.deviceSipId
|
|
|
|
|
+ let channelId = itemData.channelSipId
|
|
|
|
|
+ loading.value = true
|
|
|
|
|
|
|
|
return startPlay(deviceId, channelId)
|
|
return startPlay(deviceId, channelId)
|
|
|
.then((response) => {
|
|
.then((response) => {
|
|
|
- let res = response;
|
|
|
|
|
|
|
+ let res = response
|
|
|
if (window.location.protocol === 'http:') {
|
|
if (window.location.protocol === 'http:') {
|
|
|
- itemData.playUrl = res.ws_flv;
|
|
|
|
|
|
|
+ itemData.playUrl = res.ws_flv
|
|
|
} else {
|
|
} else {
|
|
|
- itemData.playUrl = res.wss_flv;
|
|
|
|
|
|
|
+ itemData.playUrl = res.wss_flv
|
|
|
}
|
|
}
|
|
|
- itemData.streamId = res.streamId;
|
|
|
|
|
- setPlayUrl(itemData.playUrl, playIndex); // 播放URL设置到指定位置
|
|
|
|
|
|
|
+ itemData.streamId = res.streamId
|
|
|
|
|
+ setPlayUrl(itemData.playUrl, playIndex) // 播放URL设置到指定位置
|
|
|
})
|
|
})
|
|
|
.finally(() => {
|
|
.finally(() => {
|
|
|
- loading.value = false;
|
|
|
|
|
- });
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
|
|
+ loading.value = false
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
|
|
+let playInfoRes = ref('')
|
|
|
// 设置播放URL
|
|
// 设置播放URL
|
|
|
const setPlayUrl = (url, idx) => {
|
|
const setPlayUrl = (url, idx) => {
|
|
|
const newVideoUrls = [...videoUrl.value]
|
|
const newVideoUrls = [...videoUrl.value]
|
|
|
newVideoUrls[idx] = url
|
|
newVideoUrls[idx] = url
|
|
|
videoUrl.value = newVideoUrls
|
|
videoUrl.value = newVideoUrls
|
|
|
|
|
|
|
|
|
|
+ playerInfo.value.forEach((item) => {
|
|
|
|
|
+ if (videoUrl.value[0].includes(item.id) && videoUrl.value[0].includes(item.deviceId)) {
|
|
|
|
|
+ playInfoRes.value = item
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ console.log('hhhhhhhhhhhhhhhhhhhhhhh', videoUrl.value)
|
|
|
|
|
+
|
|
|
setTimeout(() => {
|
|
setTimeout(() => {
|
|
|
window.localStorage.setItem('videoUrl', JSON.stringify(videoUrl.value))
|
|
window.localStorage.setItem('videoUrl', JSON.stringify(videoUrl.value))
|
|
|
}, 100)
|
|
}, 100)
|
|
@@ -367,18 +389,18 @@ const shot = (e) => {
|
|
|
let blob = base64ToBlob(e)
|
|
let blob = base64ToBlob(e)
|
|
|
let evt = document.createEvent('HTMLEvents')
|
|
let evt = document.createEvent('HTMLEvents')
|
|
|
evt.initEvent('click', true, true)
|
|
evt.initEvent('click', true, true)
|
|
|
- aLink.download = 't("sip.splitview998531-5")'
|
|
|
|
|
|
|
+ aLink.download = 't("sip.splitview998531-5")'
|
|
|
aLink.href = URL.createObjectURL(blob)
|
|
aLink.href = URL.createObjectURL(blob)
|
|
|
aLink.click()
|
|
aLink.click()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 保存数据
|
|
// 保存数据
|
|
|
const save = (item, index) => {
|
|
const save = (item, index) => {
|
|
|
- let dataStr = window.localStorage.getItem('playData') || '[]';
|
|
|
|
|
- let data = JSON.parse(dataStr);
|
|
|
|
|
- data[index] = item;
|
|
|
|
|
- window.localStorage.setItem('playData', JSON.stringify(data));
|
|
|
|
|
-};
|
|
|
|
|
|
|
+ let dataStr = window.localStorage.getItem('playData') || '[]'
|
|
|
|
|
+ let data = JSON.parse(dataStr)
|
|
|
|
|
+ data[index] = item
|
|
|
|
|
+ window.localStorage.setItem('playData', JSON.stringify(data))
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
// 清除数据
|
|
// 清除数据
|
|
|
const clear = (idx) => {
|
|
const clear = (idx) => {
|