|
|
@@ -19,12 +19,15 @@ import {
|
|
|
NEmpty,
|
|
|
NFlex,
|
|
|
} from "naive-ui";
|
|
|
-import { uuid } from "@/utils";
|
|
|
+import { useFlvPlayer } from "omnimatrix-video-player";
|
|
|
+import { uuid, $mitt } from "@/utils";
|
|
|
+import { useOutsideSystemStore } from "@/stores/modules/system";
|
|
|
|
|
|
interface IscreenInfo {
|
|
|
id: string;
|
|
|
loading: boolean;
|
|
|
error: boolean;
|
|
|
+ isRlt: boolean;
|
|
|
playerObj: null | any;
|
|
|
}
|
|
|
|
|
|
@@ -34,62 +37,198 @@ interface IscreenModeOptions {
|
|
|
screenInfo: IscreenInfo[];
|
|
|
}
|
|
|
|
|
|
-const { API_DEVICE_GET } = useRequest();
|
|
|
+const { API_DEVICE_GET, API_VIEW_BC_LIST_GET } = useRequest();
|
|
|
+const useSystem = useOutsideSystemStore();
|
|
|
|
|
|
const screenMode = ref(1); // 默认为单屏模式
|
|
|
const screenModeOptions: IscreenModeOptions[] = [
|
|
|
{
|
|
|
label: "单画面",
|
|
|
key: 1,
|
|
|
- screenInfo: [{ id: uuid(), loading: true, error: false, playerObj: null }],
|
|
|
+ screenInfo: [
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
+ ],
|
|
|
},
|
|
|
{
|
|
|
label: "4分屏",
|
|
|
key: 4,
|
|
|
screenInfo: [
|
|
|
- { id: uuid(), loading: true, error: false, playerObj: null },
|
|
|
- { id: uuid(), loading: true, error: false, playerObj: null },
|
|
|
- { id: uuid(), loading: true, error: false, playerObj: null },
|
|
|
- { id: uuid(), loading: true, error: false, playerObj: null },
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
],
|
|
|
},
|
|
|
{
|
|
|
label: "6分屏",
|
|
|
key: 6,
|
|
|
screenInfo: [
|
|
|
- { id: uuid(), loading: true, error: false, playerObj: null },
|
|
|
- { id: uuid(), loading: true, error: false, playerObj: null },
|
|
|
- { id: uuid(), loading: true, error: false, playerObj: null },
|
|
|
- { id: uuid(), loading: true, error: false, playerObj: null },
|
|
|
- { id: uuid(), loading: true, error: false, playerObj: null },
|
|
|
- { id: uuid(), loading: true, error: false, playerObj: null },
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
],
|
|
|
},
|
|
|
{
|
|
|
label: "9分屏",
|
|
|
key: 9,
|
|
|
screenInfo: [
|
|
|
- { id: uuid(), loading: true, error: false, playerObj: null },
|
|
|
- { id: uuid(), loading: true, error: false, playerObj: null },
|
|
|
- { id: uuid(), loading: true, error: false, playerObj: null },
|
|
|
- { id: uuid(), loading: true, error: false, playerObj: null },
|
|
|
- { id: uuid(), loading: true, error: false, playerObj: null },
|
|
|
- { id: uuid(), loading: true, error: false, playerObj: null },
|
|
|
- { id: uuid(), loading: true, error: false, playerObj: null },
|
|
|
- { id: uuid(), loading: true, error: false, playerObj: null },
|
|
|
- { id: uuid(), loading: true, error: false, playerObj: null },
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: uuid(),
|
|
|
+ loading: false,
|
|
|
+ error: true,
|
|
|
+ playerObj: null,
|
|
|
+ isRlt: false,
|
|
|
+ },
|
|
|
],
|
|
|
},
|
|
|
];
|
|
|
const screenInfoActive = ref(screenModeOptions[0].screenInfo[0].id);
|
|
|
-const screenInfo = ref<IscreenInfo[]>(screenModeOptions[0].screenInfo);
|
|
|
+const screenInfo = shallowRef<IscreenInfo[]>(screenModeOptions[0].screenInfo);
|
|
|
|
|
|
+const handleBtns = [
|
|
|
+ {
|
|
|
+ text: "关闭单个",
|
|
|
+ handle: () => destroyAllVideo(screenInfoActive.value),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ text: "关闭全部",
|
|
|
+ handle: () => destroyAllVideo(),
|
|
|
+ },
|
|
|
+];
|
|
|
const screenModeChange = (v) => {
|
|
|
- screenMode.value = v;
|
|
|
- screenInfo.value = screenModeOptions.find(
|
|
|
- (item) => item.key === v,
|
|
|
- ).screenInfo;
|
|
|
- screenInfoActive.value = screenInfo.value[0].id;
|
|
|
+ destroyAllVideo();
|
|
|
+ nextTick(() => {
|
|
|
+ screenMode.value = v;
|
|
|
+ screenInfo.value = screenModeOptions.find(
|
|
|
+ (item) => item.key === v,
|
|
|
+ ).screenInfo;
|
|
|
+ screenInfoActive.value = screenInfo.value[0].id;
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
// 设备列表数据
|
|
|
@@ -113,17 +252,107 @@ const alertInfo = [
|
|
|
const currentActive = ref("");
|
|
|
|
|
|
API_DEVICE_GET().then((res) => {
|
|
|
- console.log(res);
|
|
|
deviceList.value = (res as any[]).map((item) => ({
|
|
|
name: item.Alias,
|
|
|
key: item.Id,
|
|
|
}));
|
|
|
});
|
|
|
|
|
|
+provide("deviceId", currentActive);
|
|
|
const deviceAceiveClick = (item) => {
|
|
|
- console.log(item);
|
|
|
currentActive.value = item.key;
|
|
|
+ nextTick(() => {
|
|
|
+ playVideo(item.key);
|
|
|
+ ballCamera(item.key);
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+const videoList = computed(() => useSystem.videoList);
|
|
|
+const playVideo = (deviceId: string) => {
|
|
|
+ const item = screenInfo.value.find(
|
|
|
+ (item) => item.id === screenInfoActive.value,
|
|
|
+ );
|
|
|
+ if (item?.playerObj) {
|
|
|
+ item.playerObj.destroyed();
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ item.loading = true;
|
|
|
+ item.error = false;
|
|
|
+ const url = videoList.value?.pano_view
|
|
|
+ .replace("{4}", item.id)
|
|
|
+ .replace("{5}", deviceId);
|
|
|
+ item.playerObj = useFlvPlayer(url, ".pub-video" + item.id, () => {
|
|
|
+ item.isRlt = true;
|
|
|
+ item.loading = false;
|
|
|
+ // 强制响应式
|
|
|
+ triggerRef(screenInfo);
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ console.error(error);
|
|
|
+ error = true;
|
|
|
+ item.isRlt = false;
|
|
|
+ item.loading = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+let ballPlayerObj = null;
|
|
|
+const ballLoading = ref(false);
|
|
|
+const ballError = ref(true);
|
|
|
+const ballCamera = async (deviceId: string) => {
|
|
|
+ ballPlayerObj?.destroyed();
|
|
|
+ ballLoading.value = true;
|
|
|
+ const res = await API_VIEW_BC_LIST_GET({ DeviceId: deviceId });
|
|
|
+ const url = videoList.value?.ball_camera
|
|
|
+ .replace("{4}", uuid())
|
|
|
+ .replace("{12}", Object.keys(res)[0]);
|
|
|
+ try {
|
|
|
+ ballPlayerObj = useFlvPlayer(url, ".ball-video", () => {
|
|
|
+ ballLoading.value = false;
|
|
|
+ ballError.value = false;
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ console.error(error);
|
|
|
+ ballLoading.value = false;
|
|
|
+ ballError.value = true;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 重置并更新
|
|
|
+const destroyAllVideo = (current?: string) => {
|
|
|
+ if (current) {
|
|
|
+ screenInfo.value.forEach((item) => {
|
|
|
+ if (item.id === current) {
|
|
|
+ if (!item.playerObj) return;
|
|
|
+ item.isRlt = false;
|
|
|
+ item.playerObj.destroyed();
|
|
|
+ item.loading = false;
|
|
|
+ item.error = true;
|
|
|
+ item.id = uuid();
|
|
|
+ item.playerObj = null;
|
|
|
+ screenInfoActive.value = item.id;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ ballPlayerObj?.destroyed();
|
|
|
+ ballLoading.value = false;
|
|
|
+ ballError.value = true;
|
|
|
+ ballPlayerObj = null;
|
|
|
+ screenInfo.value.forEach((item) => {
|
|
|
+ if (!item.playerObj) return;
|
|
|
+ item.isRlt = false;
|
|
|
+ item.playerObj?.destroyed();
|
|
|
+ item.loading = false;
|
|
|
+ item.error = true;
|
|
|
+ item.id = uuid();
|
|
|
+ item.playerObj = null;
|
|
|
+ });
|
|
|
+ screenInfoActive.value = screenInfo.value[0].id;
|
|
|
+ currentActive.value = "";
|
|
|
+ }
|
|
|
+ triggerRef(screenInfo);
|
|
|
};
|
|
|
+
|
|
|
+onMounted(() => {});
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
@@ -148,7 +377,7 @@ const deviceAceiveClick = (item) => {
|
|
|
</n-flex>
|
|
|
</n-card>
|
|
|
<div class="p-4 flex-1 flex flex-col gap-4">
|
|
|
- <div class="flex justify-end">
|
|
|
+ <div class="flex justify-between">
|
|
|
<n-tabs
|
|
|
class="w-30%"
|
|
|
type="segment"
|
|
|
@@ -164,12 +393,42 @@ const deviceAceiveClick = (item) => {
|
|
|
>
|
|
|
</n-tab-pane>
|
|
|
</n-tabs>
|
|
|
+ <div class="flex gap-2">
|
|
|
+ <!-- <n-button
|
|
|
+ class="ml-2"
|
|
|
+ @click="screenModeChange('full')"
|
|
|
+ >
|
|
|
+ 全屏
|
|
|
+ </n-button> -->
|
|
|
+ <n-button
|
|
|
+ v-for="(btn, i) in handleBtns"
|
|
|
+ :key="i"
|
|
|
+ @click="btn.handle"
|
|
|
+ >{{ btn.text }}</n-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 视频监控画面显示区域 -->
|
|
|
- <div class="h-full flex flex-col gap-4">
|
|
|
+ <div class="flex-1 min-h-0 flex flex-col gap-4">
|
|
|
+ <div
|
|
|
+ v-if="screenMode === 1"
|
|
|
+ :class="[
|
|
|
+ 'w-full h-full bg-[#000] relative rounded cursor-pointer overflow-hidden',
|
|
|
+ 'outline-2 outline-offset-2 outline-solid',
|
|
|
+ ]"
|
|
|
+ >
|
|
|
+ <pub-video-new
|
|
|
+ :loading="screenInfo[0]?.loading"
|
|
|
+ :error="screenInfo[0]?.error"
|
|
|
+ :is-rlt="screenInfo[0]?.isRlt"
|
|
|
+ class="w-full h-full"
|
|
|
+ :newClass="['object-fill', 'pub-video' + screenInfo[0]?.id]"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
<n-grid
|
|
|
- class="flex-1"
|
|
|
+ v-else
|
|
|
+ :class="['flex-1']"
|
|
|
x-gap="4"
|
|
|
y-gap="4"
|
|
|
:cols="
|
|
|
@@ -183,6 +442,7 @@ const deviceAceiveClick = (item) => {
|
|
|
"
|
|
|
>
|
|
|
<n-grid-item
|
|
|
+ class="max-h-full"
|
|
|
v-for="(_, i) in screenMode === 1
|
|
|
? 1
|
|
|
: screenMode === 4
|
|
|
@@ -190,7 +450,7 @@ const deviceAceiveClick = (item) => {
|
|
|
: screenMode === 6
|
|
|
? 6
|
|
|
: 9"
|
|
|
- :key="i"
|
|
|
+ :key="screenInfo[i]?.id"
|
|
|
>
|
|
|
<div
|
|
|
:class="[
|
|
|
@@ -202,22 +462,24 @@ const deviceAceiveClick = (item) => {
|
|
|
@click="screenInfoActive = screenInfo[i].id"
|
|
|
>
|
|
|
<pub-video-new
|
|
|
+ :loading="screenInfo[i].loading"
|
|
|
+ :error="screenInfo[i].error"
|
|
|
+ :is-rlt="screenInfo[i].isRlt"
|
|
|
+ :newClass="['object-fill', 'pub-video' + screenInfo[i].id]"
|
|
|
class="w-full h-full"
|
|
|
- newClass="pub-video object-fill"
|
|
|
/>
|
|
|
</div>
|
|
|
</n-grid-item>
|
|
|
</n-grid>
|
|
|
<div class="flex gap-4 h-40%" v-if="screenMode == 1">
|
|
|
- <div class="aspect-video bg-[#000] relative">
|
|
|
- <n-empty
|
|
|
- class="absolute top-1/2 left-1/2 -translate-1/2"
|
|
|
- description="无信号"
|
|
|
- >
|
|
|
- <template #icon>
|
|
|
- <div class="size-20 i-heroicons-signal-slash-16-solid"></div>
|
|
|
- </template>
|
|
|
- </n-empty>
|
|
|
+ <div class="aspect-video bg-[#000] relative rounded">
|
|
|
+ <pub-video-new
|
|
|
+ :loading="ballLoading"
|
|
|
+ :error="ballError"
|
|
|
+ :is-rlt="false"
|
|
|
+ class="w-full h-full"
|
|
|
+ newClass="ball-video object-fill"
|
|
|
+ />
|
|
|
</div>
|
|
|
<div class="flex-1">
|
|
|
<n-card
|