louhangfei преди 2 години
родител
ревизия
526d9589d2

BIN
src/assets/camera/favorites - 副本.png


BIN
src/assets/camera/favorites.png


+ 51 - 41
src/views/map-config/mini-map/MapBase/CameraMap.ts

@@ -1,48 +1,49 @@
 import { fabric } from 'fabric';
+import { ref } from 'vue';
 import cameraActiveImg from '@/assets/camera/camera-active.png';
 import cameraImg from '@/assets/camera/camera.png';
-import { MapData } from './types';
+import favoritesImg from '@/assets/camera/favorites.png';
+import { CameraImage, MapData, OnMoving, OnRightClick, OnSelect, isCanvas } from './types';
 import { fabricSetting } from './fabricSetting';
+import { getRandomPosition } from './utils';
+// import templateGroup from './CameraGroup';
+import { createGroup, toggleGroupSelected, toggleCameraDefault } from './CameraStarGroup';
 
-export interface CameraImage extends fabric.Image {
-  cameraId: string;
-}
-
-type Canvas = fabric.Canvas;
-
-const isCanvas = (canvas: Canvas | null): canvas is Canvas => {
-  return Boolean(canvas);
-};
-
-type OnSelect = (image: CameraImage | null) => unknown;
-type OnRightClick = (e: fabric.IEvent<MouseEvent>) => unknown;
-fabricSetting(fabric);
+fabricSetting();
 
 class CameraMap {
-  public canvas: fabric.Canvas | null = null;
+  public canvas = ref<fabric.Canvas | null>();
   private onSelect: OnSelect;
   private onRightClick: OnRightClick;
-
-  constructor(param: { canvasId: string; onSelect: OnSelect; onRightClick: OnRightClick }) {
-    this.canvas = new fabric.Canvas(param.canvasId, {
+  private onMoving: OnMoving;
+
+  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;
-    // window.canvas = this.canvas;
+    this.onMoving = param.onMoving;
+    window.canvas = this.canvas.value;
   }
 
   /** 监听点击事件 */
   private addListener() {
-    const canvas = this.canvas;
+    const canvas = this.canvas.value;
     if (!isCanvas(canvas)) return;
 
     canvas.on('mouse:down', (options) => {
       const target = options.target as CameraImage;
       const cameraId = target?.cameraId;
       // console.log('当前选中的id是', cameraId);
+      console.log('mouse:down');
       console.log(options);
 
       // 判断:右键,且在元素上右键
@@ -73,22 +74,22 @@ class CameraMap {
 
   /** 上传背景图 */
   public uploadBg(imgUrl: string) {
-    const canvas = this.canvas;
+    const canvas = this.canvas.value;
     if (!isCanvas(canvas)) return;
     fabric.Image.fromURL(imgUrl, (img) => {
       console.log('image', img);
-      this.canvas!.setWidth(img.width!);
-      this.canvas!.setHeight(img.height!);
+      canvas!.setWidth(img.width!);
+      canvas!.setHeight(img.height!);
       img.lockScalingX = true;
       img.lockScalingY = true;
       // 设置背景图
-      canvas.setBackgroundImage(img, this.canvas!.renderAll.bind(this.canvas));
+      canvas.setBackgroundImage(img, this.canvas.value!.renderAll.bind(this.canvas));
     });
   }
 
   /** 将所有的摄像头都设置为非激活状态 */
   private setAllCameraUnActive() {
-    const canvas = this.canvas;
+    const canvas = this.canvas.value;
     if (!isCanvas(canvas)) return;
     canvas.forEachObject((object) => {
       (object as fabric.Image).setSrc(cameraImg, () => {
@@ -97,24 +98,28 @@ class CameraMap {
     });
   }
 
-  private getRandomPosition() {
-    return 100 + Math.floor(Math.random() * 30);
-  }
-
   /** 增加一个摄像头 */
   public addCamera(cameraId: string): Promise<CameraImage> {
-    const canvas = this.canvas;
+    const canvas = this.canvas.value;
     if (!isCanvas(canvas)) return Promise.reject();
+    // eslint-disable-next-line @typescript-eslint/no-this-alias
+    const that = this;
     return new Promise((resolve) => {
       fabric.Image.fromURL(cameraActiveImg, (cImg) => {
-        const cameraImg: CameraImage = cImg as unknown as CameraImage;
+        const cameraImg = ref(cImg as unknown as CameraImage);
         this.setAllCameraUnActive();
-        cameraImg.set({
-          left: this.getRandomPosition(),
-          top: this.getRandomPosition(),
+        cameraImg.value.set({
+          left: getRandomPosition(),
+          top: getRandomPosition(),
           cameraId,
         });
-        canvas.add(cameraImg);
+        cameraImg.value.lockScalingX = true;
+        cameraImg.value.lockScalingY = true;
+        canvas.add(cameraImg.value);
+        cameraImg.value.on('moving', function (e) {
+          console.log('move', e);
+          that.onMoving(e);
+        });
         resolve(cameraImg);
       });
     });
@@ -122,7 +127,7 @@ class CameraMap {
 
   /** 删除一个摄像头 */
   public removeCamera(cameraImage: CameraImage) {
-    const canvas = this.canvas;
+    const canvas = this.canvas.value;
     if (!isCanvas(canvas)) return;
     console.log('removeCamera', cameraImage);
     canvas.remove(cameraImage);
@@ -130,7 +135,7 @@ class CameraMap {
 
   /** 导出JSON格式 */
   public toJSON() {
-    const canvas = this.canvas;
+    const canvas = this.canvas.value;
     if (!isCanvas(canvas)) return;
     const initialJSON = canvas.toJSON(['cameraId']);
     /** toJSON返回值的类型它写错了,应该是有backgroundImage的 */
@@ -158,7 +163,7 @@ class CameraMap {
 
   /** 从json中加载 */
   public loadFromJSON(json: MapData): Promise<void> {
-    const canvas = this.canvas;
+    const canvas = this.canvas.value;
     if (!isCanvas(canvas)) return Promise.reject();
     return new Promise((resolve) => {
       const { width, height } = json.backgroundImage;
@@ -178,24 +183,29 @@ class CameraMap {
 
   /** 更新摄像头的渲染 */
   public renderCamera() {
-    const canvas = this.canvas;
+    const canvas = this.canvas.value;
     if (!isCanvas(canvas)) return;
     canvas.renderAll();
   }
 
   public clear() {
-    this.canvas?.clear();
+    this.canvas.value?.clear();
   }
 
   /** 是否已经存在这个cameraId */
   public hasCamera(cameraId: string) {
-    const canvas = this.canvas;
+    const canvas = this.canvas.value;
     if (!isCanvas(canvas)) return;
     const cameraIds = canvas
       .toJSON(['cameraId'])
       .objects.map((item) => (item as CameraImage).cameraId);
     return cameraIds.includes(cameraId);
   }
+
+  /** 根据cameraId查找某个元素 */
+  public getCameraById(cameraId: string) {
+    return this.canvas.value?.getObjects().find((x) => (x as CameraImage).cameraId === cameraId);
+  }
 }
 
 export default CameraMap;

+ 21 - 0
src/views/map-config/mini-map/MapBase/DefaultCameraIcon.vue

@@ -0,0 +1,21 @@
+<template>
+  <!-- 默认选中图片的icon -->
+  <img
+    v-if="props.position"
+    :src="favIcon"
+    class="defaultCameraImg"
+    :style="{ left: props.position?.left + 'px', top: props.position?.top + 'px' }"
+  />
+</template>
+<script lang="ts" setup>
+  import favIcon from '@/assets/camera/favorites.png';
+
+  const props = defineProps<{ position: { left: number; top: number } | null }>();
+</script>
+<style scoped>
+  .defaultCameraImg {
+    position: absolute;
+    width: 15px;
+    /* height: 50px; */
+  }
+</style>

+ 31 - 1
src/views/map-config/mini-map/MapBase/fabricSetting.ts

@@ -1,4 +1,7 @@
-export function fabricSetting(fabric) {
+import { fabric } from 'fabric';
+// import favoritesImg from '@/assets/camera/favorites.png';
+
+export function fabricSetting() {
   fabric.Object.prototype.padding = 10;
 
   // 修改控制点的形状,默认为`rect`矩形,可选的值还有`circle`圆形
@@ -26,3 +29,30 @@ export function fabricSetting(fabric) {
 
   window.fabric = fabric;
 }
+
+// // 渲染元素的icon按钮
+// function renderIcon(icon) {
+//   return function (ctx, left, top, styleOverride, fabricObject) {
+//     const size = this.cornerSize;
+//     ctx.save();
+//     ctx.translate(left, top);
+//     ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle));
+//     ctx.drawImage(icon, -size / 2, -size / 2, size, size);
+//     ctx.restore();
+//   };
+// }
+// function setControlIcon() {
+//   const favImg = document.createElement('img');
+//   favImg.src = favoritesImg;
+//   // 删除按钮控件
+//   fabric.Object.prototype.controls.favIcon = new fabric.Control({
+//     x: 0.5,
+//     y: -0.5,
+//     offsetY: -16,
+//     offsetX: 26,
+//     cursorStyle: 'pointer',
+//     // mouseUpHandler: deleteObject,
+//     render: renderIcon(favImg),
+//     cornerSize: 24,
+//   });
+// }

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

@@ -21,3 +21,17 @@ export interface CameraImgObject {
   angle: number;
   cameraId: string;
 }
+
+export interface CameraImage extends fabric.Image {
+  cameraId?: string;
+}
+
+export type Canvas = fabric.Canvas;
+
+export const isCanvas = (canvas: Canvas | null | undefined): canvas is Canvas => {
+  return Boolean(canvas);
+};
+
+export type OnSelect = (image: CameraImage | null) => unknown;
+export type OnRightClick = (e: fabric.IEvent<MouseEvent>) => unknown;
+export type OnMoving = (e: fabric.IEvent<MouseEvent>) => unknown;

+ 227 - 0
src/views/map-config/mini-map/MapBase/useCameraMap.ts

@@ -0,0 +1,227 @@
+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 { fabricSetting } from './fabricSetting';
+import { getRandomPosition } from './utils';
+// import templateGroup from './CameraGroup';
+import { createGroup, toggleGroupSelected, toggleCameraDefault } from './CameraStarGroup';
+
+fabricSetting();
+
+interface Props {
+  onSelect: OnSelect;
+  onRightClick: OnRightClick;
+  onMoving: OnMoving;
+}
+
+function useCameraMap(props: Props) {
+  const canvas = ref<fabric.Canvas | null>();
+
+  const createMap = (canvasId: string) => {
+    canvas.value = new fabric.Canvas(canvasId, {
+      fireRightClick: true, // 启用右键,button的数字为3
+      stopContextMenu: true, // 禁止默认右键菜单
+    });
+    addListener();
+    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;
+
+    canvas.value.on('mouse:down', (options) => {
+      if (!canvas.value) return;
+      const target = options.target as CameraImage;
+      const cameraId = target?.cameraId;
+      // console.log('当前选中的id是', cameraId);
+      console.log('mouse:down');
+      console.log(options);
+
+      // 判断:右键,且在元素上右键
+      // opt.button: 1-左键;2-中键;3-右键
+      // 在画布上点击:opt.target 为 null
+      if (options.button === 3 && options.target) {
+        props.onRightClick(options);
+        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) => {
+      const refImg = ref(img);
+      console.log('image', img);
+      canvas.value!.setWidth(img.width!);
+      canvas.value!.setHeight(img.height!);
+      refImg.value.lockScalingX = true;
+      refImg.value.lockScalingY = true;
+      // 设置背景图
+      canvas.value?.setBackgroundImage(refImg.value, canvas.value!.renderAll.bind(canvas.value));
+    });
+  };
+
+  /** 将所有的摄像头都设置为非激活状态 */
+  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({
+          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);
+          props.onMoving(e);
+        });
+        resolve(cameraImg.value);
+      });
+    });
+  };
+
+  /** 删除一个摄像头 */
+  const removeCamera = (cameraImage: CameraImage) => {
+    if (!isCanvas(canvas.value)) return;
+    console.log('removeCamera', cameraImage);
+    canvas.value.remove(cameraImage);
+  };
+
+  /** 导出JSON格式 */
+  const toJSON = () => {
+    if (!isCanvas(canvas.value)) return;
+    const initialJSON = canvas.value.toJSON(['cameraId']);
+    /** toJSON返回值的类型它写错了,应该是有backgroundImage的 */
+    const { src, type, version, width, height, left, top, angle } =
+      (initialJSON as any).backgroundImage || {};
+
+    const newObjects = initialJSON.objects.map((item) => {
+      return {
+        type: item.type,
+        width: item.width,
+        height: item.height,
+        left: item.left,
+        top: item.top,
+        angle: item.angle,
+        cameraId: (item as CameraImage).cameraId,
+      };
+    });
+    const newJson = {
+      version: initialJSON.version,
+      backgroundImage: { src, type, version, width, height, left, top, angle },
+      objects: newObjects,
+    };
+    return newJson;
+  };
+
+  /** 从json中加载 */
+  const loadFromJSON = (json: MapData): Promise<void> => {
+    if (!isCanvas(canvas.value)) return Promise.reject();
+    return new Promise((resolve) => {
+      const { width, height } = json.backgroundImage;
+      canvas.value?.setWidth(width);
+      canvas.value?.setHeight(height);
+      const objects = json.objects.map((item) => {
+        return {
+          ...item,
+          src: cameraImg,
+        };
+      });
+      canvas.value?.loadFromJSON({ ...json, objects }, () => {
+        resolve();
+      });
+    });
+  };
+
+  /** 更新摄像头的渲染 */
+  const renderCamera = () => {
+    canvas.value?.renderAll();
+  };
+
+  const clear = () => {
+    canvas.value?.clear();
+  };
+
+  /** 是否已经存在这个cameraId */
+  const hasCamera = (cameraId: string) => {
+    const cameraIds = canvas.value
+      ?.toJSON(['cameraId'])
+      .objects.map((item) => (item as CameraImage).cameraId);
+    return cameraIds?.includes(cameraId);
+  };
+
+  /** 根据cameraId查找某个元素 */
+  const getCameraById = (cameraId: string) => {
+    return canvas.value?.getObjects().find((x) => (x as CameraImage).cameraId === cameraId);
+  };
+
+  return {
+    canvas,
+    createMap,
+    uploadBg,
+    addCamera,
+    removeCamera,
+    toJSON,
+    loadFromJSON,
+    renderCamera,
+    clear,
+    hasCamera,
+    getCameraById,
+  };
+}
+
+export default useCameraMap;

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

@@ -0,0 +1,8 @@
+export function getRandomPosition() {
+  return 100 + Math.floor(Math.random() * 30);
+}
+
+/** 根据camera位置得到fav icon的位置 */
+export function getFavPositionByCamera(position: { left?: number; top?: number }) {
+  return { left: position.left || 0, top: position.top || 0 };
+}

+ 82 - 27
src/views/map-config/mini-map/MiniMapConfig.vue

@@ -74,13 +74,16 @@
         <div>
           <div style="height: 20px; margin-bottom: 10px">
             <div v-if="selectedCamera">
-              已选中相机<span>: {{ selectedCamera?.cameraId }}</span>
-              <span style="margin-left: 20px">
-                工位:{{ selectedCameraDetail?.workSpaceName }}
-              </span>
+              <SelectedCameraToolbar
+                @render-map="renderMap"
+                :selected-camera="selectedCamera!"
+                :positionHash="positionHash"
+                :key="positionHash"
+              />
             </div>
           </div>
         </div>
+        <!-- <div>activeObject: {{ selectedCamera?.left }}</div> -->
         <div style="overflow: auto; position: relative">
           <canvas
             width="400"
@@ -93,6 +96,7 @@
             :position="mousePosition"
             :set-default="handleSetDefault"
           />
+          <DefaultCameraIcon :position="favPosition" />
         </div>
       </div>
     </div>
@@ -105,30 +109,79 @@
   import { ElMessage, ElInput } from 'element-plus';
   import urlJoin from 'url-join';
   import { onMounted, ref, toRaw } from 'vue';
-  import CameraMap, { CameraImage } from './MapBase/CameraMap';
+  import useCameraMap from './MapBase/useCameraMap';
   import { ElSelect, ElOption } from 'element-plus';
   import { updateMinMapViewLayoutApi } from '@/api/scene/scene';
-  import { onUnmounted } from 'vue';
+  import { onUnmounted, watchEffect } from 'vue';
   import { useGlobSetting } from '@/hooks/setting';
-  import { computed } from 'vue';
+  import { computed, watch } from 'vue';
   import { Search } from '@element-plus/icons-vue';
   import ContextMenu from './MapBase/ContextMenu.vue';
+  import DefaultCameraIcon from './MapBase/DefaultCameraIcon.vue';
+  import { CameraImage } from './MapBase/types';
+  import { 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('');
 
-  let map: CameraMap;
-  const selectedCamera = ref<CameraImage | null>(null);
+  const onSelectCamera = (cameraImg: CameraImage) => {
+    console.log('onSelectCamera', cameraImg);
+    selectedCamera.value = cameraImg;
+    menuVisible.value = false;
+  };
+
+  const handleRightClick = (e) => {
+    mousePosition.value = { left: e.pointer.x, top: e.pointer.y };
+    menuVisible.value = true;
+    rightSelectedCamera.value = e.target;
+  };
+
+  const handleMoving = (e) => {
+    const target = e.transform.target;
+    selectedCamera.value = target;
+    // console.log('target', target);
+    if (target?.cameraId !== defaultCameraId.value) return;
+    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,
+  });
+
+  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 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);
+  });
 
   const handleAvatarSuccess = (e) => {
     const imgPath = e.data;
@@ -136,6 +189,10 @@
     map.uploadBg(imgUrl);
   };
 
+  const renderMap = () => {
+    map.renderCamera();
+  };
+
   const changeShop = (code: string) => {
     getShopContent(code);
   };
@@ -151,6 +208,7 @@
       map.loadFromJSON(res).then(() => {
         mapJSONToOptions();
         if (!defaultCameraId.value) {
+          defaultCamera.value = cameraOptions.value[0];
           defaultCameraId.value = cameraOptions.value[0]?.value;
         }
       });
@@ -162,11 +220,7 @@
     if (selectedShopCode.value) {
       getShopContent(selectedShopCode.value);
     }
-    map = new CameraMap({
-      canvasId: 'mapEditCanvas',
-      onSelect: onSelectCamera,
-      onRightClick: handleRightClick,
-    });
+    map.createMap('mapEditCanvas');
   });
 
   const keyupListener = (e) => {
@@ -210,23 +264,11 @@
       onSelectCamera(cameraImg);
       mapJSONToOptions();
       if (!defaultCameraId.value) {
-        defaultCameraId.value = cameraOptions.value[0]?.value;
+        defaultCameraId.value = cameraId;
       }
     });
   };
 
-  const onSelectCamera = (cameraImg: CameraImage) => {
-    console.log('onSelectCamera', cameraImg);
-    selectedCamera.value = cameraImg;
-    menuVisible.value = false;
-  };
-
-  const handleRightClick = (e) => {
-    mousePosition.value = { left: e.pointer.x, top: e.pointer.y };
-    menuVisible.value = true;
-    rightSelectedCamera.value = e.target;
-  };
-
   const mapJSONToOptions = () => {
     const objects = map.toJSON()?.objects || [];
     console.log('objects', objects);
@@ -264,10 +306,22 @@
   const handleSetDefault = () => {
     const cameraId = rightSelectedCamera.value?.cameraId;
     if (!cameraId) return;
+    defaultCamera.value = rightSelectedCamera.value;
     defaultCameraId.value = cameraId;
     /** 选择完成后隐藏 */
     menuVisible.value = false;
   };
+
+  watchEffect(() => {
+    if (!defaultCamera.value) {
+      favPosition.value = null;
+      return;
+    }
+    favPosition.value = getFavPositionByCamera({
+      left: defaultCamera.value?.oCoords?.tr.x,
+      top: defaultCamera.value?.oCoords?.tr.y,
+    });
+  });
 </script>
 
 <style scoped>
@@ -324,3 +378,4 @@
     color: #409eff;
   }
 </style>
+./MapBase/useCameraMap ./MapBase/CameraMapBak

+ 20 - 0
src/views/map-config/mini-map/components/EditDimension.vue

@@ -0,0 +1,20 @@
+<template>
+  <span>
+    <span>{{ props.label }}</span>
+    <ElInput style="width: 50px" size="small" @input="handleChange" v-model="val" />
+  </span>
+</template>
+<script lang="ts" setup>
+  import { ref } from 'vue';
+  import { ElInput } from 'element-plus';
+  const props = defineProps<{ label: string; modelValue: number }>();
+
+  const val = ref(props.modelValue);
+
+  const emits = defineEmits<{ (e: 'update:modelValue', val: number): unknown }>();
+
+  const handleChange = (e) => {
+    emits('update:modelValue', Number(e));
+  };
+</script>
+<style scoped></style>

+ 47 - 0
src/views/map-config/mini-map/components/SelectedCameraToolbar.vue

@@ -0,0 +1,47 @@
+<template>
+  <div>
+    已选中相机<span>: {{ selectedCamera?.cameraId }}</span>
+    <span
+      ><EditDimension v-model="selectedCamera.width" label="width" @update:model-value="renderMap"
+    /></span>
+    <span
+      ><EditDimension
+        v-model="selectedCamera.height"
+        label="height"
+        @update:model-value="renderMap"
+    /></span>
+    <span
+      ><EditDimension v-model="selectedCamera.left" label="left" @update:model-value="renderMap"
+    /></span>
+    <span
+      ><EditDimension v-model="selectedCamera.top" label="top" @update:model-value="renderMap"
+    /></span>
+    <span
+      ><EditDimension v-model="selectedCamera.angle" label="angle" @update:model-value="renderMap"
+    /></span>
+  </div>
+</template>
+<script lang="ts" setup>
+  import { ref } from 'vue';
+  import EditDimension from './EditDimension.vue';
+
+  const props = defineProps<{
+    selectedCamera: {
+      cameraId?: string;
+      width: number;
+      height: number;
+      left: number;
+      top: number;
+      angle: number;
+    };
+  }>();
+
+  const selectedCamera = ref(props.selectedCamera);
+
+  const emits = defineEmits<{ (e: 'renderMap'): unknown }>();
+
+  const renderMap = () => {
+    emits('renderMap');
+  };
+</script>
+<style scoped></style>