louhangfei преди 2 години
родител
ревизия
06803f9846

+ 1 - 0
src/views/map-config/mini-map/MapBase/types.ts

@@ -35,3 +35,4 @@ export const isCanvas = (canvas: Canvas | null | undefined): canvas is Canvas =>
 export type OnSelect = (image: CameraImage | null) => unknown;
 export type OnRightClick = (e: fabric.IEvent<MouseEvent>) => unknown;
 export type OnMoving = (e: fabric.IEvent<MouseEvent>) => unknown;
+export type OnRotating = (e: fabric.IEvent<MouseEvent>) => unknown;

+ 54 - 69
src/views/map-config/mini-map/MapBase/useCameraMap.ts

@@ -1,13 +1,18 @@
 import { fabric } from 'fabric';
 import { ref } from 'vue';
-import cameraActiveImg from '@/assets/camera/camera-active.png';
 import cameraImg from '@/assets/camera/camera.png';
-import favoritesImg from '@/assets/camera/favorites.png';
-import { CameraImage, MapData, OnMoving, OnRightClick, OnSelect, isCanvas } from './types';
+import {
+  CameraImage,
+  MapData,
+  OnMoving,
+  OnRightClick,
+  OnSelect,
+  isCanvas,
+  OnRotating,
+} from './types';
 import { fabricSetting } from './fabricSetting';
 import { getRandomPosition } from './utils';
-// import templateGroup from './CameraGroup';
-import { createGroup, toggleGroupSelected, toggleCameraDefault } from './CameraStarGroup';
+import { object } from 'vue-types';
 
 fabricSetting();
 
@@ -15,6 +20,7 @@ interface Props {
   onSelect: OnSelect;
   onRightClick: OnRightClick;
   onMoving: OnMoving;
+  onRotating: OnRotating;
 }
 
 function useCameraMap(props: Props) {
@@ -29,23 +35,6 @@ function useCameraMap(props: Props) {
     window.canvas = canvas.value;
   };
 
-  // constructor(param: {
-  //   canvasId: string;
-  //   onSelect: OnSelect;
-  //   onRightClick: OnRightClick;
-  //   onMoving: OnMoving;
-  // }) {
-  //   this.canvas.value = new fabric.Canvas(param.canvasId, {
-  //     fireRightClick: true, // 启用右键,button的数字为3
-  //     stopContextMenu: true, // 禁止默认右键菜单
-  //   });
-  //   this.addListener();
-  //   this.onSelect = param.onSelect;
-  //   this.onRightClick = param.onRightClick;
-  //   this.onMoving = param.onMoving;
-  //   window.canvas = this.canvas.value;
-  // }
-
   /** 监听点击事件 */
   const addListener = () => {
     if (!isCanvas(canvas.value)) return;
@@ -66,25 +55,16 @@ function useCameraMap(props: Props) {
         return;
       }
 
-      canvas.value?.forEachObject((object) => {
-        if (object === options.target) return;
-        (object as CameraImage).setSrc(cameraImg, () => {
-          canvas.value?.renderAll();
-        });
-      });
       if (!cameraId || !target) {
         props.onSelect(null);
         return;
       }
-      target.setSrc(cameraActiveImg, () => {
-        canvas.value.renderAll();
-      });
 
       props.onSelect(target);
     });
   };
 
-  /** 上传背景图 */
+  /**  上传背景图 */
   const uploadBg = (imgUrl: string) => {
     if (!isCanvas(canvas.value)) return;
     fabric.Image.fromURL(imgUrl, (img) => {
@@ -99,46 +79,44 @@ function useCameraMap(props: Props) {
     });
   };
 
-  /** 将所有的摄像头都设置为非激活状态 */
-  const setAllCameraUnActive = () => {
-    if (!isCanvas(canvas.value)) return;
-    canvas.value.forEachObject((object) => {
-      (object as fabric.Image).setSrc(cameraImg, () => {
-        canvas.value?.renderAll();
-      });
-    });
-  };
-
-  /** 增加一个摄像头 */
+  /**  增加一个摄像头 */
   const addCamera = (cameraId: string): Promise<CameraImage> => {
     if (!isCanvas(canvas.value)) return Promise.reject();
     // eslint-disable-next-line @typescript-eslint/no-this-alias
     return new Promise((resolve) => {
-      fabric.Image.fromURL(cameraActiveImg, (cImg) => {
-        const cameraImg = ref(cImg as unknown as CameraImage);
-        setAllCameraUnActive();
-        cameraImg.value.set({
+      fabric.Image.fromURL(cameraImg, (img) => {
+        const cImg = ref(img as unknown as CameraImage);
+        cImg.value.set({
           left: getRandomPosition(),
           top: getRandomPosition(),
           cameraId,
         });
-        cameraImg.value.lockScalingX = true;
-        cameraImg.value.lockScalingY = true;
-        canvas.value?.add(cameraImg.value);
-        cameraImg.value.on('moving', function (e) {
-          // console.log('move', e);
+        cImg.value.lockScalingX = true;
+        cImg.value.lockScalingY = true;
+        canvas.value?.add(cImg.value);
+        canvas.value?.setActiveObject(cImg.value);
+        cImg.value.on('moving', function (e) {
           props.onMoving(e);
         });
-        resolve(cameraImg.value);
+        cImg.value.on('rotating', function (e) {
+          props.onRotating(e);
+        });
+
+        resolve(cImg.value);
       });
     });
   };
 
-  /** 删除一个摄像头 */
-  const removeCamera = (cameraImage: CameraImage) => {
+  /**  删除一个摄像头 */
+  const removeActiveCamera = () => {
     if (!isCanvas(canvas.value)) return;
-    console.log('removeCamera', cameraImage);
-    canvas.value.remove(cameraImage);
+    const activeObject = canvas.value?.getActiveObject();
+    if (!activeObject) return;
+    canvas.value.remove(activeObject);
+    const objects = canvas.value.getObjects();
+    if (objects.length > 0) {
+      canvas.value.setActiveObject(objects[0]);
+    }
   };
 
   /** 导出JSON格式 */
@@ -168,7 +146,7 @@ function useCameraMap(props: Props) {
     return newJson;
   };
 
-  /** 从json中加载 */
+  /**  从json中加载 */
   const loadFromJSON = (json: MapData): Promise<void> => {
     if (!isCanvas(canvas.value)) return Promise.reject();
     return new Promise((resolve) => {
@@ -187,26 +165,32 @@ function useCameraMap(props: Props) {
     });
   };
 
-  /** 更新摄像头的渲染 */
+  /**  更新摄像头的渲染 */
   const renderCamera = () => {
-    canvas.value?.renderAll();
+    canvas.value?.renderAll.bind(canvas.value);
   };
-
+  /**  */
   const clear = () => {
     canvas.value?.clear();
   };
 
-  /** 是否已经存在这个cameraId */
+  /**  是否已经存在这个cameraId */
   const hasCamera = (cameraId: string) => {
-    const cameraIds = canvas.value
-      ?.toJSON(['cameraId'])
-      .objects.map((item) => (item as CameraImage).cameraId);
-    return cameraIds?.includes(cameraId);
+    const cameraIds =
+      canvas.value?.toJSON(['cameraId']).objects.map((item) => (item as CameraImage).cameraId) ||
+      [];
+    return cameraIds.includes(cameraId);
+  };
+
+  const getObjects = () => {
+    return canvas.value?.getObjects() as CameraImage[];
   };
 
-  /** 根据cameraId查找某个元素 */
+  /**  根据cameraId查找某个元素 */
   const getCameraById = (cameraId: string) => {
-    return canvas.value?.getObjects().find((x) => (x as CameraImage).cameraId === cameraId);
+    return canvas.value
+      ?.getObjects()
+      .find((x) => (x as CameraImage).cameraId === cameraId) as CameraImage;
   };
 
   return {
@@ -214,13 +198,14 @@ function useCameraMap(props: Props) {
     createMap,
     uploadBg,
     addCamera,
-    removeCamera,
+    removeActiveCamera,
     toJSON,
     loadFromJSON,
     renderCamera,
     clear,
     hasCamera,
     getCameraById,
+    getObjects,
   };
 }
 

+ 6 - 0
src/views/map-config/mini-map/MapBase/utils.ts

@@ -6,3 +6,9 @@ export function getRandomPosition() {
 export function getFavPositionByCamera(position: { left?: number; top?: number }) {
   return { left: position.left || 0, top: position.top || 0 };
 }
+
+export function createSelectedPositionHash(target) {
+  return (
+    String(target.oCoords.tr.x) + '_' + String(target.oCoords.tr.y) + '_' + String(target.angle)
+  );
+}

+ 43 - 69
src/views/map-config/mini-map/MiniMapConfig.vue

@@ -29,7 +29,10 @@
             v-for="item in filterShopCameraList"
             :key="item.code"
             class="camera-item flex justify-start"
-            :class="{ isAdded: isAddedToMap(item.code) }"
+            :class="{
+              isAdded: isAddedToMap(item.code),
+              isActive: item.code === selectedCamera?.cameraId,
+            }"
             @click="handleAddCamera(item.code)"
           >
             <span class="camera-id">{{ item.name }}</span>
@@ -39,18 +42,6 @@
       </div>
       <div class="draw-container">
         <div style="display: flex; margin-bottom: 20px">
-          <div>
-            默认摄像头:
-            <ElSelect v-model="defaultCameraId">
-              <ElOption
-                :key="item.value"
-                :label="item.value"
-                :value="item.value"
-                v-for="item in cameraOptions"
-              />
-            </ElSelect>
-          </div>
-
           <el-upload
             class="avatar-uploader flex justify-center items-center"
             action="/temp/api/layout/uploadPicture"
@@ -77,8 +68,7 @@
               <SelectedCameraToolbar
                 @render-map="renderMap"
                 :selected-camera="selectedCamera!"
-                :positionHash="positionHash"
-                :key="positionHash"
+                :key="selectedPositionHash"
               />
             </div>
           </div>
@@ -110,7 +100,6 @@
   import urlJoin from 'url-join';
   import { onMounted, ref, toRaw } from 'vue';
   import useCameraMap from './MapBase/useCameraMap';
-  import { ElSelect, ElOption } from 'element-plus';
   import { updateMinMapViewLayoutApi } from '@/api/scene/scene';
   import { onUnmounted, watchEffect } from 'vue';
   import { useGlobSetting } from '@/hooks/setting';
@@ -119,17 +108,16 @@
   import ContextMenu from './MapBase/ContextMenu.vue';
   import DefaultCameraIcon from './MapBase/DefaultCameraIcon.vue';
   import { CameraImage } from './MapBase/types';
-  import { getFavPositionByCamera } from './MapBase/utils';
+  import { createSelectedPositionHash, getFavPositionByCamera } from './MapBase/utils';
   import SelectedCameraToolbar from './components/SelectedCameraToolbar.vue';
 
   const miniMap = useMiniMap();
   const globSetting = useGlobSetting();
   const { scenesTree, shopCameraList, selectedShopCode, selectedShopDetail } = storeToRefs(miniMap);
   const { getScenesTree, getShowCameras, getMapLayout } = miniMap;
-  const positionHash = ref('');
+  const selectedPositionHash = ref('');
 
   const onSelectCamera = (cameraImg: CameraImage) => {
-    console.log('onSelectCamera', cameraImg);
     selectedCamera.value = cameraImg;
     menuVisible.value = false;
   };
@@ -142,45 +130,45 @@
 
   const handleMoving = (e) => {
     const target = e.transform.target;
-    selectedCamera.value = target;
-    // console.log('target', target);
-    if (target?.cameraId !== defaultCameraId.value) return;
+    selectedPositionHash.value = createSelectedPositionHash(target);
+    if (target?.cameraId === defaultCamera.value?.cameraId) {
+      favPosition.value = getFavPositionByCamera({
+        left: target.oCoords.tr.x,
+        top: target.oCoords.tr.y,
+      });
+    }
+  };
+
+  const handleRoating = (e) => {
+    const target = e.transform.target;
+    selectedPositionHash.value = createSelectedPositionHash(target);
     favPosition.value = getFavPositionByCamera({
       left: target.oCoords.tr.x,
       top: target.oCoords.tr.y,
     });
-    positionHash.value = String(target.oCoords.tr.x) + '_' + String(target.oCoords.tr.y);
   };
 
   const map = useCameraMap({
     onSelect: onSelectCamera,
     onRightClick: handleRightClick,
     onMoving: handleMoving,
+    onRotating: handleRoating,
   });
 
   const selectedCamera = ref<CameraImage | null>();
   /** 右键选中的摄像机 */
   const rightSelectedCamera = ref<CameraImage | null>(null);
-  const cameraOptions = ref<{ label: string; value: string }[]>([]);
-  const defaultCameraId = ref('');
-  const defaultCamera = ref<CameraImage | null>(null);
+  const defaultCamera = ref<CameraImage | null>();
   const searchKey = ref('');
 
   const mousePosition = ref<{ left: number; top: number }>({ left: 0, top: 0 });
   const menuVisible = ref(false);
   const favPosition = ref<{ left: number; top: number } | null>(null);
 
-  watch(
-    map.canvas,
-    (c) => {
-      console.log('map.canvas', c);
-      selectedCamera.value = c?.getActiveObject() as CameraImage;
-    },
-    { deep: true },
-  );
-
   watchEffect(() => {
-    console.log('selectedCamera.value', selectedCamera.value?.left);
+    /** 删除或者新增的时候会执行 */
+    console.log('getActiveObject');
+    selectedCamera.value = map.canvas.value?.getActiveObject() as CameraImage;
   });
 
   const handleAvatarSuccess = (e) => {
@@ -191,6 +179,10 @@
 
   const renderMap = () => {
     map.renderCamera();
+    favPosition.value = getFavPositionByCamera({
+      left: defaultCamera.value?.oCoords?.tr.x,
+      top: defaultCamera.value?.oCoords?.tr.y,
+    });
   };
 
   const changeShop = (code: string) => {
@@ -204,12 +196,11 @@
         map.clear();
         return;
       }
-      defaultCameraId.value = res.defaultCameraId;
       map.loadFromJSON(res).then(() => {
-        mapJSONToOptions();
-        if (!defaultCameraId.value) {
-          defaultCamera.value = cameraOptions.value[0];
-          defaultCameraId.value = cameraOptions.value[0]?.value;
+        if (res.defaultCameraId) {
+          defaultCamera.value = map.getCameraById(res.defaultCameraId);
+        } else {
+          defaultCamera.value = map.getObjects()?.[0] as CameraImage;
         }
       });
     });
@@ -226,9 +217,7 @@
   const keyupListener = (e) => {
     const keyCode = e.code;
     if (keyCode === 'Delete') {
-      if (selectedCamera.value) {
-        handleDeleteCamera();
-      }
+      handleDeleteCamera();
     }
   };
 
@@ -240,10 +229,6 @@
     document.removeEventListener('keyup', keyupListener);
   });
 
-  const selectedCameraDetail = computed(() => {
-    return shopCameraList.value.find((item) => item.code === selectedCamera.value?.cameraId);
-  });
-
   const filterShopCameraList = computed(() => {
     const k = searchKey.value.trim();
     if (!k) return shopCameraList.value;
@@ -252,7 +237,7 @@
 
   /** 摄像机是否已添加到底图 */
   const isAddedToMap = (cameraId: string): boolean => {
-    return cameraOptions.value.some((x) => x.value === cameraId);
+    return map.hasCamera(cameraId);
   };
 
   const handleAddCamera = (cameraId: string) => {
@@ -261,23 +246,12 @@
       return;
     }
     map.addCamera(cameraId).then((cameraImg) => {
-      onSelectCamera(cameraImg);
-      mapJSONToOptions();
-      if (!defaultCameraId.value) {
-        defaultCameraId.value = cameraId;
+      if (!defaultCamera.value?.cameraId) {
+        defaultCamera.value = cameraImg;
       }
     });
   };
 
-  const mapJSONToOptions = () => {
-    const objects = map.toJSON()?.objects || [];
-    console.log('objects', objects);
-    cameraOptions.value = objects?.map((x) => ({
-      label: x.cameraId,
-      value: x.cameraId,
-    }));
-  };
-
   const handleSave = () => {
     const json = map.toJSON();
     console.log('save json', json);
@@ -285,7 +259,7 @@
       ElMessage.error('背景图片未添加');
       return;
     }
-    const layout = JSON.stringify({ ...json, defaultCameraId: defaultCameraId.value });
+    const layout = JSON.stringify({ ...json, defaultCameraId: defaultCamera.value?.cameraId });
     updateMinMapViewLayoutApi({ layout, targetId: selectedShopCode.value + '' }).then((res) => {
       console.log('updateMinMapViewLayoutApi', res);
       ElMessage.success('保存成功');
@@ -294,20 +268,17 @@
 
   const handleDeleteCamera = () => {
     if (!selectedCamera.value) return;
-    map.removeCamera(toRaw(selectedCamera.value!));
+    map.removeActiveCamera();
     /** 如果删除的是默认选中的摄像头,那么先清空默认的摄像头再 */
-    mapJSONToOptions();
-    if (selectedCamera.value.cameraId === defaultCameraId.value) {
-      defaultCameraId.value = cameraOptions.value[0]?.value;
+    if (selectedCamera.value.cameraId === defaultCamera.value?.cameraId) {
+      defaultCamera.value = map.getObjects()?.[0];
     }
-    selectedCamera.value = null;
   };
 
   const handleSetDefault = () => {
     const cameraId = rightSelectedCamera.value?.cameraId;
     if (!cameraId) return;
     defaultCamera.value = rightSelectedCamera.value;
-    defaultCameraId.value = cameraId;
     /** 选择完成后隐藏 */
     menuVisible.value = false;
   };
@@ -377,5 +348,8 @@
   .isAdded {
     color: #409eff;
   }
+  .isActive {
+    background-color: #f00;
+  }
 </style>
 ./MapBase/useCameraMap ./MapBase/CameraMapBak

+ 19 - 6
src/views/map-config/mini-map/components/EditDimension.vue

@@ -1,15 +1,28 @@
 <template>
-  <span>
-    <span>{{ props.label }}</span>
-    <ElInput style="width: 50px" size="small" @input="handleChange" v-model="val" />
+  <span style="padding-left: 10px">
+    <span>{{ props.label }}: </span>
+    <ElInput
+      style="width: 50px"
+      size="small"
+      @input="handleChange"
+      v-model="val"
+      :disabled="props.disabled"
+    />
   </span>
 </template>
 <script lang="ts" setup>
-  import { ref } from 'vue';
+  import { ref, watch } from 'vue';
   import { ElInput } from 'element-plus';
-  const props = defineProps<{ label: string; modelValue: number }>();
+  const props = defineProps<{ label: string; modelValue: number; disabled?: boolean }>();
 
-  const val = ref(props.modelValue);
+  const val = ref();
+  watch(
+    () => props.modelValue,
+    () => {
+      val.value = Math.floor(props.modelValue);
+    },
+    { immediate: true },
+  );
 
   const emits = defineEmits<{ (e: 'update:modelValue', val: number): unknown }>();
 

+ 7 - 2
src/views/map-config/mini-map/components/SelectedCameraToolbar.vue

@@ -2,13 +2,18 @@
   <div>
     已选中相机<span>: {{ selectedCamera?.cameraId }}</span>
     <span
-      ><EditDimension v-model="selectedCamera.width" label="width" @update:model-value="renderMap"
+      ><EditDimension
+        v-model="selectedCamera.width"
+        label="width"
+        @update:model-value="renderMap"
+        disabled
     /></span>
     <span
       ><EditDimension
         v-model="selectedCamera.height"
         label="height"
         @update:model-value="renderMap"
+        disabled
     /></span>
     <span
       ><EditDimension v-model="selectedCamera.left" label="left" @update:model-value="renderMap"
@@ -27,7 +32,7 @@
 
   const props = defineProps<{
     selectedCamera: {
-      cameraId?: string;
+      cameraId: string;
       width: number;
       height: number;
       left: number;