|
@@ -0,0 +1,297 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="monitor-cameras-container">
|
|
|
|
|
+ <div class="main-camera">
|
|
|
|
|
+ <LiveVideo
|
|
|
|
|
+ v-if="curPlayingCamera && getCameraUrl(curPlayingCamera)"
|
|
|
|
|
+ :url="getCameraUrl(curPlayingCamera)"
|
|
|
|
|
+ :poster="getCameraImg(curPlayingCamera)"
|
|
|
|
|
+ :id="`monitor-livevideo`"
|
|
|
|
|
+ class="main-video"
|
|
|
|
|
+ />
|
|
|
|
|
+ <img
|
|
|
|
|
+ v-if="curPlayingCamera && !getCameraUrl(curPlayingCamera)"
|
|
|
|
|
+ src="@/assets/images/disaster-overview/full-screen.png"
|
|
|
|
|
+ alt=""
|
|
|
|
|
+ class="full-screen"
|
|
|
|
|
+ @click="isFullScreen ? exitFullscreen() : fullScreen(`monitor-livevideo`, 'overview-monitor')"
|
|
|
|
|
+ />
|
|
|
|
|
+ <div class="no-main-camera" v-if="!curPlayingCamera || !getCameraUrl(curPlayingCamera)">
|
|
|
|
|
+ <img class="cameraEmptyImg" src="@/assets/icons/nine-square-grid/cameraEmpty.png" />
|
|
|
|
|
+ <span>暂无监控相机画面</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="all-cameras">
|
|
|
|
|
+ <div class="camera-edit">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <img src="@/assets/images/disaster-overview/camera.png" alt="" />
|
|
|
|
|
+ <span class="title">监控相机</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="add-area" @click="updateMonitorVisible = true">
|
|
|
|
|
+ <span class="add-icon">+</span>
|
|
|
|
|
+ <span>编辑监控相机</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="camera-list">
|
|
|
|
|
+ <div v-if="selectedCamerasOfCommandCenter.length === 0" class="no-camera-list">
|
|
|
|
|
+ <img class="empty-img" src="@/assets/images/empty.png" alt="" />
|
|
|
|
|
+ <span>暂无监控相机</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-for="(item, index) in selectedCamerasOfCommandCenter"
|
|
|
|
|
+ :key="index"
|
|
|
|
|
+ class="monitor-item"
|
|
|
|
|
+ :class="{ 'cur-playing': curPlayingCamera?.id === item.id }"
|
|
|
|
|
+ @click="curPlayingCamera = item"
|
|
|
|
|
+ >
|
|
|
|
|
+ <img
|
|
|
|
|
+ v-if="!getCameraUrl(item)"
|
|
|
|
|
+ src="@/assets/images/disaster-overview/no-camera-url.png"
|
|
|
|
|
+ alt=""
|
|
|
|
|
+ class="monitor-no-url"
|
|
|
|
|
+ />
|
|
|
|
|
+ <LiveVideo
|
|
|
|
|
+ :url="getCameraUrl(item)"
|
|
|
|
|
+ :poster="getCameraImg(item)"
|
|
|
|
|
+ :id="`monitor-livevideo-${index}`"
|
|
|
|
|
+ class="live-video"
|
|
|
|
|
+ />
|
|
|
|
|
+ <div>{{ item.name }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <UpdateMonitorArea v-if="updateMonitorVisible" @confirm="handleConfirmAddMonitor" @close="handleCloseAddMonitor" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script setup lang="ts">
|
|
|
|
|
+ import { onMounted, onUnmounted, ref, watch } from 'vue';
|
|
|
|
|
+ import { useRoute } from 'vue-router';
|
|
|
|
|
+ import { storeToRefs } from 'pinia';
|
|
|
|
|
+ import screenfull from 'screenfull';
|
|
|
|
|
+ import urlJoin from 'url-join';
|
|
|
|
|
+ import { ElMessage } from 'element-plus';
|
|
|
|
|
+ import LiveVideo from '@/components/live/LiveVideo.vue';
|
|
|
|
|
+ import UpdateMonitorArea from '@/components/monitor-camera-edit/UpdateMonitorArea.vue';
|
|
|
|
|
+ import { useMonitorCameraEdit } from '@/store/modules/useMonitorCameraEdit';
|
|
|
|
|
+ import { userSplitScreenFullScreen } from '@/store/modules/userSplitScreenFullScreen';
|
|
|
|
|
+ import { CameraInfo, CameraInTree, updateCameraToOverviewGroup } from '@/api/disaster-overview';
|
|
|
|
|
+
|
|
|
|
|
+ const monitorCameraEdit = useMonitorCameraEdit();
|
|
|
|
|
+ const { idOfEmergencyEvent, idOfCommandCenter, selectedCamerasOfCommandCenter } = storeToRefs(monitorCameraEdit);
|
|
|
|
|
+ const { getSelectedCameraIdsOfCommandCenter } = monitorCameraEdit;
|
|
|
|
|
+
|
|
|
|
|
+ const { isFullScreen, curFullScreenType } = storeToRefs(userSplitScreenFullScreen());
|
|
|
|
|
+ const { fullScreen, exitFullscreen } = userSplitScreenFullScreen();
|
|
|
|
|
+
|
|
|
|
|
+ const route = useRoute();
|
|
|
|
|
+ const emergencyEventId = Number(route.params.id);
|
|
|
|
|
+
|
|
|
|
|
+ const curPlayingCamera = ref<CameraInfo>();
|
|
|
|
|
+
|
|
|
|
|
+ const updateMonitorVisible = ref(false);
|
|
|
|
|
+
|
|
|
|
|
+ const isHttps = () => {
|
|
|
|
|
+ return window.location.protocol.startsWith('https');
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const getCameraUrl = (val: CameraInfo | undefined) => {
|
|
|
|
|
+ if (val?.pushStreamDTO && val?.pushStreamDTO.videoUrls) {
|
|
|
|
|
+ const videoUrl = val?.pushStreamDTO.videoUrls.pushstreamIp;
|
|
|
|
|
+ const protocol = isHttps() ? 'wss' : 'ws';
|
|
|
|
|
+ // 如果是绝对地址
|
|
|
|
|
+ if (videoUrl.startsWith('http')) {
|
|
|
|
|
+ // 如果是https的话,websocket要用wss
|
|
|
|
|
+ return videoUrl.replace('http', protocol);
|
|
|
|
|
+ }
|
|
|
|
|
+ const u = urlJoin(
|
|
|
|
|
+ `${protocol}://`,
|
|
|
|
|
+ window.location.host,
|
|
|
|
|
+ window.location.pathname === '/' ? '' : window.location.pathname,
|
|
|
|
|
+ videoUrl,
|
|
|
|
|
+ );
|
|
|
|
|
+ return u;
|
|
|
|
|
+ }
|
|
|
|
|
+ return '';
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const getCameraImg = (val: CameraInfo | undefined) => {
|
|
|
|
|
+ if (val?.pushStreamDTO && val?.pushStreamDTO.imageUrl) {
|
|
|
|
|
+ return val?.pushStreamDTO.imageUrl;
|
|
|
|
|
+ }
|
|
|
|
|
+ return '';
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const handleConfirmAddMonitor = (data: CameraInTree[]) => {
|
|
|
|
|
+ updateMonitorVisible.value = false;
|
|
|
|
|
+ const cameraAddIds: number[] = data.map((item) => item.id);
|
|
|
|
|
+ updateCameraToOverviewGroup({ groupId: idOfCommandCenter.value, cameraIdList: cameraAddIds }).then(() => {
|
|
|
|
|
+ getSelectedCameraIdsOfCommandCenter();
|
|
|
|
|
+ ElMessage({
|
|
|
|
|
+ message: '监测区域编辑成功',
|
|
|
|
|
+ type: 'success',
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const handleCloseAddMonitor = () => {
|
|
|
|
|
+ updateMonitorVisible.value = false;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ watch(
|
|
|
|
|
+ () => emergencyEventId,
|
|
|
|
|
+ (newVal) => {
|
|
|
|
|
+ idOfEmergencyEvent.value = newVal;
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ watch(
|
|
|
|
|
+ () => selectedCamerasOfCommandCenter.value,
|
|
|
|
|
+ (newVal) => {
|
|
|
|
|
+ curPlayingCamera.value = newVal[0];
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ onMounted(() => {
|
|
|
|
|
+ idOfEmergencyEvent.value = emergencyEventId;
|
|
|
|
|
+ getSelectedCameraIdsOfCommandCenter();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ window.onresize = () => {
|
|
|
|
|
+ if (!screenfull.isFullscreen) {
|
|
|
|
|
+ isFullScreen.value = false; //判断退出全屏,进行赋值
|
|
|
|
|
+ curFullScreenType.value = 'single';
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ onUnmounted(() => {
|
|
|
|
|
+ window.onresize = null;
|
|
|
|
|
+ });
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped lang="scss">
|
|
|
|
|
+ .monitor-cameras-container {
|
|
|
|
|
+ padding: 10px;
|
|
|
|
|
+
|
|
|
|
|
+ .main-camera {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: calc(100% - 220px);
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+
|
|
|
|
|
+ .main-video {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .full-screen {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ bottom: 0px;
|
|
|
|
|
+ right: 0px;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ padding: 12px;
|
|
|
|
|
+ background: rgba(0, 0, 0, 0.6);
|
|
|
|
|
+ border-radius: 8px 0px 0px 8px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .no-main-camera {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ gap: 20px;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .all-cameras {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 200px;
|
|
|
|
|
+ margin-top: 20px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .camera-edit {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+
|
|
|
|
|
+ .title {
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ color: #000000;
|
|
|
|
|
+ margin-left: 5px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .add-area {
|
|
|
|
|
+ padding: 0 16px;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ color: #333333;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+
|
|
|
|
|
+ .add-icon {
|
|
|
|
|
+ width: 24px;
|
|
|
|
|
+ height: 24px;
|
|
|
|
|
+ border: 1px dashed #1777ff;
|
|
|
|
|
+ color: #1777ff;
|
|
|
|
|
+ font-size: 20px;
|
|
|
|
|
+ line-height: 24px;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ margin-right: 12px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .add-area:hover {
|
|
|
|
|
+ color: #1777ff;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .camera-list {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: calc(100% - 40px);
|
|
|
|
|
+ margin-top: 10px;
|
|
|
|
|
+ overflow: auto;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
|
+ gap: 20px;
|
|
|
|
|
+
|
|
|
|
|
+ .monitor-item {
|
|
|
|
|
+ width: 235px;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .cur-playing .live-video {
|
|
|
|
|
+ border: 2px solid #1777ff;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .monitor-no-url {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: calc(100% - 26px);
|
|
|
|
|
+ object-fit: contain;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .live-video {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: calc(100% - 26px);
|
|
|
|
|
+ border: 1px solid #ddd;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .no-camera-list {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+
|
|
|
|
|
+ .empty-img {
|
|
|
|
|
+ height: 80%;
|
|
|
|
|
+ object-fit: contain;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+</style>
|