Просмотр исходного кода

Merge branch 'AddMapCameraPreview' into 'master'

车间地图配置中相机预览功能

See merge request tian-group/skyeye-admin-fe!87
楼航飞 2 лет назад
Родитель
Сommit
9114572a15

+ 174 - 0
src/views/map-config/mini-map/MapBase/CameraPreview.vue

@@ -0,0 +1,174 @@
+<template>
+  <div
+    class="video-preview"
+    :style="{
+      position: 'fixed',
+      left: posX + 'px',
+      top: posY + 'px',
+      width: boxWidth + 'px',
+      height: boxHeight + 'px',
+      cursor: cursor,
+    }"
+    @mousedown="startDrag"
+    @mousemove="changeCursor"
+  >
+    <el-icon :size="20" class="preview-close" @click="closeVideo"><Close /></el-icon>
+    <LiveVideo :url="props.videoUrl" />
+  </div>
+</template>
+<script lang="ts" setup>
+  import { ref } from 'vue';
+  import LiveVideo from '@/components/LiveVideo/LiveVideo.vue';
+  import { Close } from '@element-plus/icons-vue';
+  import { onMounted } from 'vue';
+
+  const props = defineProps<{
+    lastPosX: number;
+    lastPosY: number;
+    videoUrl: string;
+  }>();
+  const emit = defineEmits(['close']);
+
+  onMounted(() => {
+    posX.value = props.lastPosX;
+    posY.value = props.lastPosY;
+  });
+
+  const posX = ref<number>();
+  const posY = ref<number>();
+
+  const scaleDistance = 10;
+  const boxWidth = ref<number>(400);
+  const boxHeight = ref<number>(300);
+
+  const isResizing = ref<boolean>(false);
+  const initialWidth = ref<number>(0);
+  const initialHeight = ref<number>(0);
+  const initialMouseX = ref<number>(0);
+  const initialMouseY = ref<number>(0);
+  const isTopLeft = ref<boolean>(false);
+  const isBottomLeft = ref<boolean>(false);
+  const isBottomRight = ref<boolean>(false);
+  const initialPosX = ref<number>(0);
+  const initialPosY = ref<number>(0);
+  const cursor = ref<string>('default');
+
+  const startDrag = (e) => {
+    const rect = e.target.getBoundingClientRect();
+    const offsetX = e.clientX - rect.left;
+    const offsetY = e.clientY - rect.top;
+    isTopLeft.value = Math.abs(offsetX) < scaleDistance && Math.abs(offsetY) < scaleDistance;
+    isBottomLeft.value =
+      Math.abs(offsetX) < scaleDistance && Math.abs(offsetY - rect.height) < scaleDistance;
+
+    isBottomRight.value =
+      Math.abs(offsetX - rect.width) < scaleDistance &&
+      Math.abs(offsetY - rect.height) < scaleDistance;
+
+    if (isTopLeft.value || isBottomLeft.value || isBottomRight.value) {
+      isResizing.value = true;
+      initialWidth.value = boxWidth.value;
+      initialHeight.value = boxHeight.value;
+      initialMouseX.value = e.clientX;
+      initialMouseY.value = e.clientY;
+      initialPosX.value = posX.value!;
+      initialPosY.value = posY.value!;
+      window.addEventListener('mousemove', handleResize);
+      window.addEventListener('mouseup', stopResize);
+    } else {
+      initialMouseX.value = e.clientX;
+      initialMouseY.value = e.clientY;
+      window.addEventListener('mousemove', handleMove);
+      window.addEventListener('mouseup', stopMove);
+    }
+  };
+
+  const changeCursor = (e) => {
+    const rect = e.target.getBoundingClientRect();
+    const offsetX = e.clientX - rect.left;
+    const offsetY = e.clientY - rect.top;
+    const isTopLeftMove = Math.abs(offsetX) < scaleDistance && Math.abs(offsetY) < scaleDistance;
+    const isBottomLeftMove =
+      Math.abs(offsetX) < scaleDistance && Math.abs(offsetY - rect.height) < scaleDistance;
+    const isBottomRightMove =
+      Math.abs(offsetX - rect.width) < scaleDistance &&
+      Math.abs(offsetY - rect.height) < scaleDistance;
+
+    if (isTopLeftMove || isBottomRightMove) {
+      cursor.value = 'nwse-resize';
+    } else if (isBottomLeftMove) {
+      cursor.value = 'nesw-resize';
+    } else {
+      cursor.value = 'default';
+    }
+  };
+
+  const handleResize = (e) => {
+    const deltaX = e.clientX - initialMouseX.value;
+    const deltaY = e.clientY - initialMouseY.value;
+    boxWidth.value = initialWidth.value + deltaX;
+    boxHeight.value = initialHeight.value + deltaY;
+    if (isTopLeft.value) {
+      posX.value = initialPosX.value + deltaX;
+      posY.value = initialPosY.value + deltaY;
+    } else if (isBottomLeft.value) {
+      posX.value = initialPosX.value + deltaX;
+    }
+
+    if (
+      (isTopLeft.value && deltaX < 0) ||
+      (isBottomLeft.value && deltaX < 0) ||
+      (isBottomRight.value && deltaX > 0)
+    ) {
+      boxWidth.value = initialWidth.value + Math.abs(deltaX);
+      boxHeight.value = initialHeight.value + Math.abs(deltaY);
+    } else {
+      boxWidth.value = initialWidth.value - Math.abs(deltaX);
+      boxHeight.value = initialHeight.value - Math.abs(deltaY);
+    }
+  };
+
+  const handleMove = (e) => {
+    const deltaX = e.clientX - initialMouseX.value;
+    const deltaY = e.clientY - initialMouseY.value;
+
+    posX.value = posX.value! + deltaX;
+    posY.value = posY.value! + deltaY!;
+
+    initialMouseX.value = e.clientX;
+    initialMouseY.value = e.clientY;
+  };
+
+  const stopResize = () => {
+    isResizing.value = false;
+    cursor.value = 'default';
+    window.removeEventListener('mousemove', handleResize);
+    window.removeEventListener('mouseup', stopResize);
+  };
+
+  const stopMove = () => {
+    window.removeEventListener('mousemove', handleMove);
+    window.removeEventListener('mouseup', stopMove);
+  };
+
+  const closeVideo = () => {
+    emit('close');
+  };
+</script>
+<style scoped>
+  .video-preview {
+    width: 400px;
+    height: 300px;
+    background-color: #a3a5a5;
+    position: relative;
+    z-index: 8;
+  }
+
+  .preview-close {
+    position: absolute;
+    top: 2px;
+    right: 2px;
+    z-index: 99;
+    cursor: pointer;
+  }
+</style>

+ 40 - 5
src/views/map-config/mini-map/MapBase/KonvaMap.vue

@@ -20,12 +20,24 @@
       </v-layer>
     </v-stage>
     <div
-      @click="setDefaultCamera"
       v-show="defaultShow"
       class="opt-container"
       :style="{ position: 'absolute', left: posX + 'px', top: posY + 'px' }"
-      ><div class="opt-item" :class="{ disabled: disabledSet }">设为默认相机</div></div
     >
+      <div class="opt-item" :class="{ disabled: disabledSet }" @click="setDefaultCamera"
+        >设为默认相机</div
+      >
+      <div class="opt-item" @click="previewCamera">预览相机</div>
+    </div>
+
+    <CameraPreview
+      v-if="isShow"
+      :last-pos-x="posX!"
+      :last-pos-y="posY!"
+      :video-url="videoUrl"
+      @close="closePreview"
+    />
+
     <DefaultTip
       v-show="tipShow"
       :position="pos"
@@ -47,7 +59,7 @@
   import useMiniMap from '../use-mini-map';
   import { storeToRefs } from 'pinia';
   import { updateMinMapViewLayoutApi } from '@/api/scene/scene';
-  import { emitKeypressEvents } from 'readline';
+  import CameraPreview from './CameraPreview.vue';
 
   const globSetting = useGlobSetting();
 
@@ -88,6 +100,7 @@
   const pos = ref(TipPositionEnum.TOP);
   //上一次点击的相机
   const lastClickedGroupId = ref<string | null>(null);
+  const lastClickedVideoUrl = ref<string | null>(null);
 
   const bgImgUrl = ref<string | null>('');
   const cameras = ref<camerasGroupType[]>([]);
@@ -95,6 +108,7 @@
   const defaultCameraId = ref('');
   const tipShow = ref(false);
   const disabledSet = ref(false);
+  const videoUrl = ref<string>('');
 
   const cameraIconSize = { width: 52, height: 52 };
 
@@ -209,6 +223,13 @@
       // 判断是否为右键点击
       if (e.evt.button === 2) {
         lastClickedGroupId.value = parent.id();
+        const clickedVideoUrl = props.filterData.find(
+          (item) => item.code === lastClickedGroupId.value,
+        );
+        if (clickedVideoUrl) {
+          lastClickedVideoUrl.value = clickedVideoUrl.pushstreamIp;
+        }
+        isShow.value = false;
         posX.value = e.evt.offsetX + 20;
         posY.value = e.evt.offsetY;
         disabledSet.value = defaultCameraId.value === parent.id();
@@ -286,6 +307,19 @@
     emit('change', true);
   };
 
+  const isShow = ref<boolean>(false);
+
+  const previewCamera = () => {
+    videoUrl.value = lastClickedVideoUrl.value!;
+    isShow.value = true;
+    defaultShow.value = false;
+  };
+
+  const closePreview = () => {
+    isShow.value = false;
+    defaultShow.value = false;
+  };
+
   watch(
     lastClickedGroupId,
     () => {
@@ -340,8 +374,6 @@
     const stage = transformer.value.getNode().getStage();
     const groups = stage.find('.group');
     const tempList = cloneDeep(cameras.value);
-    // console.log('cameras.value', cameras.value);
-
     const camerasLists = tempList.map((item, index) => {
       item.groupConfig.x = groups[index].attrs.x;
       item.groupConfig.y = groups[index].attrs.y;
@@ -404,6 +436,9 @@
     clearBg();
     cameras.value = [];
     lastClickedGroupId.value = null;
+    lastClickedGroupId.value = null;
+    videoUrl.value = '';
+    isShow.value = false;
     defaultCameraId.value = '';
   };