Explorar el Código

空间分析、实时标绘

Hwf hace 8 meses
padre
commit
94fd75c18e

+ 0 - 2
src/components/Map/YztMap/index.vue

@@ -11,8 +11,6 @@ import { PointType } from '@/api/globalMap/type';
 
 interface Props {
   activeMap: string;
-  drawing: boolean;
-  mouseToolState: MouseTool;
   pointType: PointType[];
 }
 const containerScale = inject('containerScale');

+ 16 - 29
src/components/Map/index.vue

@@ -38,8 +38,6 @@ import { pointDetailTemplate } from '@/views/globalMap/data/mapData';
 
 interface Props {
   activeMap: string;
-  drawing: boolean;
-  mouseToolState: MouseTool;
   pointType: PointType[];
 }
 
@@ -66,17 +64,7 @@ const mapState = reactive({
 let AMap, map, scale;
 
 // 鼠标绘制工具
-const { getMouseTool, initMouseTool, drawGraphics, closeDraw, handleUndo } = useDrawTool({
-  color: props.mouseToolState.color,
-  lineWidth: props.mouseToolState.lineWidth,
-  drawType: props.mouseToolState.drawType,
-  graphicsType: props.mouseToolState.graphicsType,
-  // 绘制完成事件
-  onDrawCompleted: (data, overlaysData, obj) => {
-    closeDraw();
-    emits('onDrawCompleted', data, overlaysData, obj);
-  }
-});
+const drawTool = useDrawTool();
 // 初始化地图
 const { getAMap, getMap, switchMap, addMarker, addSearchMarker, clearMarker, getMarkers, getScale, showInfo } = useAMap({
   key: '30d3d8448efd68cb0b284549fd41adcf', // 申请好的Web端开发者Key,首次调用 load 时必填
@@ -99,7 +87,7 @@ const { getAMap, getMap, switchMap, addMarker, addSearchMarker, clearMarker, get
       map.removeLayer();
     }
     map.on('zoomchange', zoomChangeHandler);
-    initMouseTool({ container: 'aMap', map, AMap });
+    drawTool.initMouseTool({ container: 'aMap', map, AMap });
     handleResize();
   },
   onMarkerClick: (data) => {
@@ -170,20 +158,19 @@ watch(
     switchMap(props.activeMap);
   }
 );
-let lnglat;
-watch(
-  () => props.mouseToolState,
-  () => {
-    if (props.drawing) {
-      drawGraphics(props.mouseToolState);
-    } else {
-      closeDraw();
-    }
-  },
-  {
-    deep: true
-  }
-);
+// watch(
+//   () => props.mouseToolState,
+//   () => {
+//     if (props.drawing) {
+//       drawGraphics(props.mouseToolState);
+//     } else {
+//       closeDraw();
+//     }
+//   },
+//   {
+//     deep: true
+//   }
+// );
 // 缩放级别变化
 const zoomChangeHandler = () => {
   mapState.zoom = map.getZoom();
@@ -231,7 +218,7 @@ const setCenter = (item) => {
   map.setCenter([item.longitude, item.latitude]);
 };
 
-defineExpose({ addMarker, addSearchMarker, setCenter, getMarkers, clearMarker, handleUndo, getMap, getMouseTool });
+defineExpose({ addMarker, addSearchMarker, setCenter, getMarkers, clearMarker, getMap, drawTool });
 const handleResize = () => {
   const containerWidth = containerRef.value.clientWidth * containerScale().scaleX;
   const containerHeight = containerRef.value.clientHeight * containerScale().scaleY;

+ 150 - 146
src/hooks/AMap/useDrawTool.ts

@@ -10,7 +10,7 @@ interface DrawToolOptions {
   graphicsType: string;
   onDrawCompleted?: Function;
 }
-export function useDrawTool(options: DrawToolOptions) {
+export function useDrawTool() {
   let drawOptions = {
     strokeColor: '#f80102',
     strokeOpacity: 1,
@@ -22,7 +22,6 @@ export function useDrawTool(options: DrawToolOptions) {
   const { currentState, commit, undo, history, future } = useHistory();
   const overlays = [];
   const overlaysData = [];
-  let graphicsType = options.graphicsType;
   let mouseTool, contextMenu, container, map, AMap, rightClickObj;
   const initMouseTool = (options2) => {
     container = document.getElementById('aMap');
@@ -32,59 +31,15 @@ export function useDrawTool(options: DrawToolOptions) {
     mouseTool = new AMap.MouseTool(map);
     //创建右键菜单
     contextMenu = new AMap.ContextMenu();
-    // 右键删除按钮
-    contextMenu.addItem(
-      '删除',
-      function () {
-        deleteGraphics();
-      },
-      0
-    );
-  };
-  // 初始化绘制完成回调
-  const initDrawMethod = (drawOptions) => {
-    mouseTool.on('draw', function (event) {
-      const obj = event.obj;
-      const id = nanoid();
-      obj._opts.extData = {
-        id: id
-      };
-      const data: any = deepClone(drawOptions);
-      data.id = id;
-      if (drawOptions.type == 'marker') {
-        data.path = obj.getPosition();
-      } else {
-        const path = obj.getPath();
-        // 将AMap.LngLat对象数组转换为经纬度数组
-        const pathArr = path.map((lngLat) => {
-          // 返回经度和纬度的数组
-          return [lngLat.lng, lngLat.lat];
-        });
-        pathArr.push(pathArr[0]);
-        data.path = pathArr;
-      }
-      overlays.push(obj);
-      overlaysData.push(data);
-      commit(deepClone(overlaysData));
-      debugger
-      if (typeof options.onDrawCompleted === 'function') {
-        options.onDrawCompleted(data, overlaysData, obj, event);
-      }
-      // 右击菜单
-      obj.on('rightclick', handleRightClick);
-    });
-  };
-  // 图形右击事件
-  const handleRightClick = (event) => {
-    rightClickObj = event.target;
-    contextMenu.open(map, event.lnglat);
   };
+
   // 删除图形
   const deleteGraphics = () => {
     const id = rightClickObj.getExtData()?.id;
     if (id) {
       for (let i = 0; i < overlays.length; i++) {
-        if (overlays[i].getExtData()?.id === id) {
+        const overlay = Array.isArray(overlays[i]) ? overlays[i][0] : overlays[i];
+        if (overlay?.getExtData().id === id) {
           removeOverlayByIndex(i);
           commit(deepClone(overlaysData));
           rightClickObj = null;
@@ -119,7 +74,7 @@ export function useDrawTool(options: DrawToolOptions) {
         anchor: 'bottom-center',
         title: newOptions.title,
         icon: newOptions.icon,
-        size: new AMap.Size(newOptions.size[0], newOptions.size[1])
+        size: [newOptions.size[0], newOptions.size[1]]
       };
     } else if (newOptions.graphicsType === 'text') {
       drawOptions = {
@@ -147,24 +102,44 @@ export function useDrawTool(options: DrawToolOptions) {
       mouseTool.polyline(drawOptions);
     } else if (newOptions.graphicsType === 'text') {
       // 绘制文字
-      addText(drawOptions);
+      return addText(drawOptions);
     } else if (newOptions.graphicsType === 'marker') {
       // 绘制图标
       mouseTool.marker(drawOptions);
     } else if (newOptions.graphicsType === 'measureArea') {
       // 测量面积
-      mouseTool.measureArea(drawOptions);
+      mouseTool.polygon(drawOptions);
     }
-    if (newOptions.graphicsType !== 'text') {
-      // 绘制完成事件
-      initDrawMethod(drawOptions);
+    return drawOptions;
+  };
+  // 空间分析绘制图形
+  const drawGraphics2 = (newOptions: MouseTool) => {
+    drawOptions = {
+      type: newOptions.graphicsType,
+      strokeColor: newOptions.color,
+      strokeOpacity: 1,
+      strokeWeight: '1',
+      fillColor: newOptions.color,
+      fillOpacity: newOptions.drawType === '1' ? 0 : 0.5,
+      strokeStyle: 'solid'
+    };
+    closeDraw();
+    if (newOptions.graphicsType === 'circle') {
+      // 绘制圆形
+      mouseTool.circle(drawOptions);
+    } else if (newOptions.graphicsType === 'rectangle') {
+      // 绘制矩形
+      mouseTool.rectangle(drawOptions);
+    } else if (newOptions.graphicsType === 'polygon') {
+      // 绘制多边形
+      mouseTool.polygon(drawOptions);
     }
   };
-  const addText = (drawOptions) => {
+  const addText = (options) => {
     // 文本覆盖物的样式
     const textStyle = {
-      fontSize: drawOptions.fontSize,
-      color: drawOptions.fontColor,
+      fontSize: options.fontSize,
+      color: options.fontColor,
       borderColor: 'transparent',
       backgroundColor: 'transparent',
       borderWidth: 0,
@@ -173,11 +148,11 @@ export function useDrawTool(options: DrawToolOptions) {
 
     // 创建文本覆盖物
     const text = new AMap.Text({
-      text: drawOptions.text, // 文本内容,可以根据需要自定义
-      position: drawOptions.lnglat, // 文本位置(经纬度)
+      text: options.text, // 文本内容,可以根据需要自定义
+      position: options.lnglat, // 文本位置(经纬度)
       style: textStyle, // 文本样式
       zIndex: 100, // 文本层级
-      draggable: true // 是否可拖动(可选)
+      draggable: false // 是否可拖动(可选)
     });
     // 将文本覆盖物添加到地图
     map.add(text);
@@ -185,58 +160,50 @@ export function useDrawTool(options: DrawToolOptions) {
     text._opts.extData = {
       id: id
     };
-    const data: any = deepClone(drawOptions);
+    const data: any = deepClone(options);
     data.id = id;
-    overlays.push(text);
-    overlaysData.push(data);
-    commit(deepClone(overlaysData));
-    console.log(overlaysData);
-    if (typeof options.onDrawCompleted === 'function') {
-      options.onDrawCompleted(data, overlaysData, text);
-    }
-    // 右击菜单
-    text.on('rightclick', handleRightClick);
+    return { text, data };
   };
   let polyline = null;
   let startPoint = null;
   let isDrawing = false;
   let lastPoint = null;
   // 绘制任意线
-  const drawAnyLine = (drawOptions) => {
-    container.addEventListener('mousedown', drawAnyLineMouseDown);
-
-    container.addEventListener('mousemove', (e) => {
-      e.preventDefault();
-      if (!isDrawing) return;
-
-      const mapPoint = this.map.getContainer().getBoundingClientRect();
-      const lngLat = this.map.getLngLatFromContainerPixel([e.clientX - mapPoint.left, e.clientY - mapPoint.top]);
-
-      // 更新线条(这里需要手动管理polyline的坐标)
-      if (polyline) {
-        polyline.setPath(polyline.getPath().concat([lngLat]));
-      } else {
-        // 首次绘制时创建polyline
-        polyline = new AMap.Polyline({
-          map: this.map,
-          path: [startPoint, lngLat],
-          strokeColor: drawOptions.strokeColor,
-          strokeWeight: drawOptions.strokeWeight,
-          strokeOpacity: drawOptions.strokeOpacity,
-          strokeStyle: 'solid',
-          lineJoin: 'round',
-          lineCap: 'round',
-          zIndex: 50
-        });
-      }
-      lastPoint = lngLat;
-      console.log(lngLat);
-    });
-
-    container.addEventListener('mouseup', (e) => {
-      isDrawing = false;
-      map.setDraggable(true);
-    });
+  const drawAnyLine = (options) => {
+    // container.addEventListener('mousedown', drawAnyLineMouseDown);
+    //
+    // container.addEventListener('mousemove', (e) => {
+    //   e.preventDefault();
+    //   if (!isDrawing) return;
+    //
+    //   const mapPoint = this.map.getContainer().getBoundingClientRect();
+    //   const lngLat = this.map.getLngLatFromContainerPixel([e.clientX - mapPoint.left, e.clientY - mapPoint.top]);
+    //
+    //   // 更新线条(这里需要手动管理polyline的坐标)
+    //   if (polyline) {
+    //     polyline.setPath(polyline.getPath().concat([lngLat]));
+    //   } else {
+    //     // 首次绘制时创建polyline
+    //     polyline = new AMap.Polyline({
+    //       map: this.map,
+    //       path: [startPoint, lngLat],
+    //       strokeColor: options.strokeColor,
+    //       strokeWeight: options.strokeWeight,
+    //       strokeOpacity: options.strokeOpacity,
+    //       strokeStyle: 'solid',
+    //       lineJoin: 'round',
+    //       lineCap: 'round',
+    //       zIndex: 50
+    //     });
+    //   }
+    //   lastPoint = lngLat;
+    //   console.log(lngLat);
+    // });
+    //
+    // container.addEventListener('mouseup', (e) => {
+    //   isDrawing = false;
+    //   map.setDraggable(true);
+    // });
   };
   // 任意线 鼠标按下事件
   const drawAnyLineMouseDown = (e) => {
@@ -262,19 +229,22 @@ export function useDrawTool(options: DrawToolOptions) {
   const createGraphics = (data: any) => {
     if (data.type === 'circle') {
       // 绘制圆形
-      createCircle(data);
-    } else if (data.type === 'rectangle') {
-      // 绘制矩形
-      createRectangle(data);
-    } else if (data.type === 'polygon') {
-      // 绘制多边形
-      createPolygon(data);
+      return createCircle(data);
+    } else if (['polygon', 'rectangle'].includes(data.type)) {
+      // 绘制矩形、多边形
+      return createPolygon(data);
     } else if (data.type === 'marker') {
       // 绘制图标
-      createMarker(data);
+      return createMarker(data);
     } else if (data.type === 'measureArea') {
-      // 绘制图标
-      createMeasureArea(data);
+      // 绘制面积
+      return createMeasureArea(data);
+    } else if (data.type === 'text') {
+      // 文字
+      return addText(data);
+    } else if (data.type === 'straightLine') {
+      // 直线
+      return createStraightLine(data);
     }
   };
   // 创建圆形
@@ -282,15 +252,15 @@ export function useDrawTool(options: DrawToolOptions) {
     const circle = new AMap.Circle({
       center: options.center,
       radius: options.radius, //半径
-      strokeColor: options.color,
-      strokeOpacity: 1,
-      strokeWeight: options.lineWidth,
-      fillColor: options.color,
-      fillOpacity: options.opacity,
+      strokeColor: options.strokeColor,
+      strokeOpacity: options.strokeOpacity,
+      strokeWeight: options.strokeWeight,
+      fillColor: options.fillColor,
+      fillOpacity: options.fillOpacity,
       strokeStyle: 'solid'
     });
-    overlays.push(circle);
     map.add(circle);
+    return circle;
   };
   // 创建矩形
   const createRectangle = (options: any) => {
@@ -299,17 +269,17 @@ export function useDrawTool(options: DrawToolOptions) {
     const bounds = new AMap.Bounds(southWest, northEast);
     const rectangle = new AMap.Rectangle({
       bounds: bounds,
-      strokeColor: options.color,
-      strokeOpacity: 1,
-      strokeWeight: options.lineWidth,
-      fillColor: options.color,
-      fillOpacity: options.opacity,
+      strokeColor: options.strokeColor,
+      strokeOpacity: options.strokeOpacity,
+      strokeWeight: options.strokeWeight,
+      fillColor: options.fillColor,
+      fillOpacity: options.fillOpacity,
       strokeStyle: 'solid'
     });
     overlays.push(rectangle);
     map.add(rectangle);
   };
-  // 创建
+  // 创建多边
   const createPolygon = (options: any) => {
     // 将数组转换为AMap.LngLat对象的数组
     const path = options.path.map((coord) => {
@@ -317,23 +287,39 @@ export function useDrawTool(options: DrawToolOptions) {
     });
     const polygon = new AMap.Polygon({
       path: path,
-      strokeColor: options.color,
-      strokeOpacity: 1,
-      strokeWeight: options.lineWidth,
-      fillColor: options.color,
-      fillOpacity: options.opacity,
+      strokeColor: options.strokeColor,
+      strokeOpacity: options.strokeOpacity,
+      strokeWeight: options.strokeWeight,
+      fillColor: options.fillColor,
+      fillOpacity: options.fillOpacity,
       strokeStyle: 'solid'
     });
-    overlays.push(polygon);
     map.add(polygon);
+    return polygon;
+  };
+  // 创建直线
+  const createStraightLine = (options: any) => {
+    // 将数组转换为AMap.LngLat对象的数组
+    const path = options.path.map((coord) => {
+      return new AMap.LngLat(coord[0], coord[1]);
+    });
+    const polyline = new AMap.Polyline({
+      path: path,
+      strokeColor: options.strokeColor,
+      strokeOpacity: options.strokeOpacity,
+      strokeWeight: options.strokeWeight,
+      strokeStyle: 'solid'
+    });
+    overlays.push(polyline);
+    map.add(polyline);
   };
   // 绘制图标
   const createMarker = (options: any) => {
     const marker = new AMap.Marker({
-      position: new AMap.LngLat(116.38, 39.92),
+      position: options.path,
       // 将一张图片的地址设置为 icon
       icon: options.icon,
-      size: new AMap.Size(options.size[0], options.size[1]),
+      size: [options.size[0], options.size[1]],
       anchor: 'bottom-center'
     });
     map.add(marker);
@@ -346,11 +332,11 @@ export function useDrawTool(options: DrawToolOptions) {
     });
     const polygon = new AMap.Polygon({
       path: path,
-      strokeColor: options.color,
+      strokeColor: options.strokeColor,
       strokeOpacity: 1,
-      strokeWeight: options.lineWidth,
-      fillColor: options.color,
-      fillOpacity: options.opacity,
+      strokeWeight: options.strokeWeight,
+      fillColor: options.fillColor,
+      fillOpacity: options.fillOpacity,
       strokeStyle: 'solid'
     });
     // 计算区域面积
@@ -360,6 +346,7 @@ export function useDrawTool(options: DrawToolOptions) {
       text: '区域面积' + area + '平方米',
       offset: new AMap.Pixel(-20, -20)
     });
+    overlays.push(polygon, text);
     map.add(polygon);
     map.add(text);
   };
@@ -397,20 +384,37 @@ export function useDrawTool(options: DrawToolOptions) {
   };
   // 根据索引移除覆盖物
   const removeOverlayByIndex = (index: number) => {
-    // 移除地图上覆盖物
-    map.remove(overlays[index]);
+    if (Array.isArray(overlays[index])) {
+      overlays[index].forEach((overlay) => {
+        // 移除地图上覆盖物
+        map.remove(overlay);
+      });
+    } else {
+      // 移除地图上覆盖物
+      map.remove(overlays[index]);
+    }
     overlays.splice(index, 1);
     overlaysData.splice(index, 1);
   };
-  // // 图层分析显示信息
-  // const addText = (text) => {
-  //
-  // }
+  const getContextMenu = () => {
+    return contextMenu;
+  };
+  const getAMap = () => {
+    return AMap;
+  };
+  const getMap = () => {
+    return map;
+  };
   return {
     overlays,
     getMouseTool,
+    getAMap,
+    getMap,
     initMouseTool,
+    getContextMenu,
+    createGraphics,
     drawGraphics,
+    drawGraphics2,
     closeDraw,
     createCircle,
     removeOverlayByIndex,

+ 1 - 2
src/types/components.d.ts

@@ -48,8 +48,6 @@ declare module 'vue' {
     ElRow: typeof import('element-plus/es')['ElRow']
     ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
     ElSelect: typeof import('element-plus/es')['ElSelect']
-    ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
-    ElSkeletonItem: typeof import('element-plus/es')['ElSkeletonItem']
     ElSlider: typeof import('element-plus/es')['ElSlider']
     ElStep: typeof import('element-plus/es')['ElStep']
     ElSteps: typeof import('element-plus/es')['ElSteps']
@@ -101,6 +99,7 @@ declare module 'vue' {
     UserSelect: typeof import('./../components/UserSelect/index.vue')['default']
     VideoContainer: typeof import('./../components/HKVideo/video-container.vue')['default']
     YMap: typeof import('./../components/Map/YMap.vue')['default']
+    YMapold: typeof import('./../components/Map/YMapold.vue')['default']
     YztMap: typeof import('./../components/Map/YztMap/index.vue')['default']
   }
   export interface ComponentCustomProperties {

+ 0 - 206
src/views/globalMap/DrawTools.vue

@@ -1,206 +0,0 @@
-<template>
-  <div class="draw-tools-container">
-    <div class="draw-item" @mouseover="showColorSelect = true" @mouseleave="showColorSelect = false">
-      <div class="item-label">画笔选择</div>
-      <i class="color-box" :style="{ backgroundColor: colorList[drawerColorIndex]?.value }" />
-      <!--弹窗选择器-->
-      <div ref="selectBoxRef" v-show="showColorSelect" class="select-box" style="top: -149px; padding-bottom: 20px">
-        <div class="box-content">
-          <div
-            v-for="(item, index) in colorList"
-            :key="index"
-            :class="drawType === item.value ? 'select-item active' : 'select-item'"
-            @click="selectColor(index)"
-          >
-            <i class="color-box" :style="{ backgroundColor: item.value }" />
-            <span>{{ item.label }}</span>
-          </div>
-        </div>
-      </div>
-    </div>
-    <div class="draw-item" @mouseover="showDrawTypeSelect = true" @mouseleave="showDrawTypeSelect = false">
-      <div class="item-label">框面类型</div>
-      <!--弹窗选择器-->
-      <div ref="selectBoxRef2" v-show="showDrawTypeSelect" class="select-box" style="top: -82px; padding-bottom: 20px">
-        <div class="box-content">
-          <div
-            v-for="(item, index) in drawTypeList"
-            :key="index"
-            :class="drawType === item.value ? 'select-item active' : 'select-item'"
-            @click="selectDrawType(index)"
-          >
-            <span>{{ item.label }}</span>
-          </div>
-        </div>
-      </div>
-    </div>
-    <div class="draw-item" @mouseover="showGraphicsTypeSelect = true" @mouseleave="showGraphicsTypeSelect = false">
-      <div class="item-label">图像类型</div>
-      <!--弹窗选择器-151-->
-      <div ref="selectBoxRef2" v-show="showGraphicsTypeSelect" class="select-box" style="top: -115.5px; padding-bottom: 20px">
-        <div class="box-content">
-          <div
-            v-for="(item, index) in graphicsTypeList"
-            :key="index"
-            :class="graphicsType === item.value ? 'select-item active' : 'select-item'"
-            @click="selectGraphicsType(index)"
-          >
-            <span>{{ item.label }}</span>
-          </div>
-        </div>
-      </div>
-    </div>
-    <div class="draw-item" @click="openDraw">{{ drawing ? '关闭绘制' : '开启绘制' }}</div>
-    <div class="draw-item" @click="undo">撤销</div>
-  </div>
-</template>
-
-<script lang="ts" setup name="DrawTools">
-interface ListItem {
-  label: string;
-  value: string;
-}
-interface Props {
-  drawing: boolean;
-  color: string;
-  drawType: string;
-  graphicsType: string;
-}
-const props = withDefaults(defineProps<Props>(), {});
-const emits = defineEmits(['update:drawType', 'update:color', 'update:graphicsType', 'update:drawing', 'undo']);
-const selectBoxRef = ref(null);
-const selectBoxRef2 = ref(null);
-const selectBoxRef3 = ref(null);
-
-// 显示画笔选择弹窗
-let showColorSelect = ref(false);
-// 画笔颜色索引
-let drawerColorIndex = ref(0);
-// 画笔颜色列表
-const colorList: ListItem[] = reactive([
-  { label: '红色', value: '#f80102' },
-  { label: '橙色', value: '#f4511e' },
-  { label: '蓝色', value: '#0197f8' },
-  { label: '绿色', value: '#42b884' }
-]);
-// 选中颜色
-const selectColor = (index) => {
-  drawerColorIndex.value = index;
-  emits('update:color', colorList[index].value);
-  showColorSelect.value = false;
-};
-// 点击颜色选择之外
-onClickOutside(selectBoxRef, () => {
-  if (!showColorSelect.value) return;
-  showColorSelect.value = false;
-});
-
-// 绘制类型列表
-const drawTypeList: ListItem[] = reactive([
-  { label: '线框', value: '1' },
-  { label: '面框', value: '2' }
-]);
-// 显示隐藏绘制类型弹窗
-let showDrawTypeSelect = ref(false);
-
-// 选择
-const selectDrawType = (index) => {
-  emits('update:drawType', drawTypeList[index].value);
-  showDrawTypeSelect.value = false;
-};
-// 点击绘制类型选择之外
-onClickOutside(selectBoxRef2, () => {
-  if (!showDrawTypeSelect.value) return;
-  showDrawTypeSelect.value = false;
-});
-
-// 绘制图形列表
-const graphicsTypeList: ListItem[] = reactive([
-  { label: '圆形', value: 'circle' },
-  { label: '矩形', value: 'rectangle' },
-  { label: '多边形', value: 'polygon' },
-  // { label: '索套', value: 'freePolygon' }
-]);
-// 显隐图像类型弹窗
-let showGraphicsTypeSelect = ref(false);
-
-// 选择
-const selectGraphicsType = (index) => {
-  emits('update:graphicsType', graphicsTypeList[index].value);
-  showGraphicsTypeSelect.value = false;
-};
-// 点击绘制类型选择之外
-onClickOutside(selectBoxRef3, () => {
-  if (!showGraphicsTypeSelect.value) return;
-  showGraphicsTypeSelect.value = false;
-});
-
-// 开启结束绘制
-const openDraw = () => {
-  emits('update:drawing', !props.drawing);
-};
-
-// 撤销绘制
-const undo = () => {
-  emits('undo', !props.drawing);
-};
-</script>
-
-<style lang="scss" scoped>
-.draw-tools-container {
-  background-color: #304468;
-  border-radius: 5px;
-  display: flex;
-  color: #fff;
-  font-size: 18px;
-  padding: 10px 0;
-  .draw-item {
-    display: flex;
-    align-items: center;
-    position: relative;
-    padding: 0 10px;
-    cursor: pointer;
-    border-left: 1px solid #3a74be;
-    &:first-child {
-      border-left: none;
-    }
-    .item-label {
-      margin-right: 10px;
-    }
-    .select-box {
-      position: absolute;
-      left: 0;
-      z-index: 7;
-      width: 100%;
-      .box-content {
-        background-color: #102341;
-      }
-      .select-item {
-        display: flex;
-        align-items: center;
-        cursor: pointer;
-        padding: 5px 10px;
-        &:hover {
-          background-color: rgba(22, 73, 142, 0.5);
-        }
-        &:first-child {
-          margin-left: 0;
-        }
-      }
-      .color-box {
-        margin-right: 5px;
-      }
-    }
-  }
-  .color-box {
-    display: block;
-    width: 20px;
-    height: 20px;
-    cursor: pointer;
-    border-radius: 3px;
-  }
-}
-.active {
-  background-color: rgba(22, 73, 142, 0.5);
-}
-</style>

+ 405 - 0
src/views/globalMap/RightMenu/DrawTools.vue

@@ -0,0 +1,405 @@
+<template>
+  <div class="draw-tools-container">
+    <div class="draw-item" @mouseover="showColorSelect = true" @mouseleave="showColorSelect = false">
+      <div class="item-label">画笔选择</div>
+      <i class="color-box" :style="{ backgroundColor: colorList[drawerColorIndex]?.value }" />
+      <!--弹窗选择器-->
+      <div v-show="showColorSelect" ref="selectBoxRef" class="select-box" style="top: -149px; padding-bottom: 20px">
+        <div class="box-content">
+          <div
+            v-for="(item, index) in colorList"
+            :key="index"
+            :class="mouseToolState.drawType === item.value ? 'select-item active' : 'select-item'"
+            @click="selectColor(index)"
+          >
+            <i class="color-box" :style="{ backgroundColor: item.value }" />
+            <span>{{ item.label }}</span>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="draw-item" @mouseover="showDrawTypeSelect = true" @mouseleave="showDrawTypeSelect = false">
+      <div class="item-label">框面类型</div>
+      <!--弹窗选择器-->
+      <div v-show="showDrawTypeSelect" ref="selectBoxRef2" class="select-box" style="top: -82px; padding-bottom: 20px">
+        <div class="box-content">
+          <div
+            v-for="(item, index) in drawTypeList"
+            :key="index"
+            :class="mouseToolState.drawType === item.value ? 'select-item active' : 'select-item'"
+            @click="selectDrawType(index)"
+          >
+            <span>{{ item.label }}</span>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="draw-item" @mouseover="showGraphicsTypeSelect = true" @mouseleave="showGraphicsTypeSelect = false">
+      <div class="item-label">图像类型</div>
+      <!--弹窗选择器-151-->
+      <div v-show="showGraphicsTypeSelect" ref="selectBoxRef2" class="select-box" style="top: -115.5px; padding-bottom: 20px">
+        <div class="box-content">
+          <div
+            v-for="(item, index) in graphicsTypeList"
+            :key="index"
+            :class="mouseToolState.graphicsType === item.value ? 'select-item active' : 'select-item'"
+            @click="selectGraphicsType(index)"
+          >
+            <span>{{ item.label }}</span>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="draw-item" @click="openDraw">{{ mouseToolState.drawing ? '关闭绘制' : '开启绘制' }}</div>
+    <div class="draw-item" @click="handleUndo">撤销</div>
+  </div>
+</template>
+
+<script lang="ts" setup name="DrawTools">
+import { useHistory } from '@/hooks/useHistory';
+import { nanoid } from 'nanoid';
+import { deepClone } from '@/utils';
+
+interface ListItem {
+  label: string;
+  value: string;
+}
+const getDrawTool = inject('getDrawTool');
+const getMap = inject('getMap');
+const emits = defineEmits(['handleAnalysisData']);
+const { currentState, commit, undo, history, future } = useHistory();
+const mouseToolState = reactive({
+  drawing: false,
+  color: '#f80102',
+  // 1线框 2区域
+  drawType: '1',
+  // 图形形状  circle圆形 rectangle矩形 polygon多边形
+  graphicsType: 'circle'
+});
+watch(
+  mouseToolState,
+  () => {
+    const drawTool = getDrawTool();
+    if (mouseToolState.drawing) {
+      drawOptions.color = mouseToolState.color;
+      drawOptions.drawType = mouseToolState.drawType;
+      drawOptions.graphicsType = mouseToolState.graphicsType;
+      drawTool.drawGraphics2(drawOptions);
+      drawTool.getMouseTool().on('draw', onDraw);
+    } else {
+      mouseToolState.drawing = false;
+      drawTool.closeDraw();
+    }
+  },
+  {
+    deep: true
+  }
+);
+const drawOptions = reactive({
+  color: '#f80102',
+  // 1线框 2区域
+  drawType: '1',
+  // 图形形状  circle圆形 rectangle矩形 polygon多边形
+  graphicsType: 'circle'
+});
+const selectBoxRef = ref(null);
+const selectBoxRef2 = ref(null);
+const selectBoxRef3 = ref(null);
+
+// 显示画笔选择弹窗
+let showColorSelect = ref(false);
+// 画笔颜色索引
+let drawerColorIndex = ref(0);
+// 画笔颜色列表
+const colorList: ListItem[] = reactive([
+  { label: '红色', value: '#f80102' },
+  { label: '橙色', value: '#f4511e' },
+  { label: '蓝色', value: '#0197f8' },
+  { label: '绿色', value: '#42b884' }
+]);
+// 选中颜色
+const selectColor = (index) => {
+  drawerColorIndex.value = index;
+  mouseToolState.color = colorList[index].value;
+  showColorSelect.value = false;
+};
+const overlays = [];
+const overlaysData = [];
+// 点击颜色选择之外
+onClickOutside(selectBoxRef, () => {
+  if (!showColorSelect.value) return;
+  showColorSelect.value = false;
+});
+
+// 绘制类型列表
+const drawTypeList: ListItem[] = reactive([
+  { label: '线框', value: '1' },
+  { label: '面框', value: '2' }
+]);
+// 显示隐藏绘制类型弹窗
+let showDrawTypeSelect = ref(false);
+
+// 选择
+const selectDrawType = (index) => {
+  mouseToolState.drawType = drawTypeList[index].value;
+  showDrawTypeSelect.value = false;
+};
+// 点击绘制类型选择之外
+onClickOutside(selectBoxRef2, () => {
+  if (!showDrawTypeSelect.value) return;
+  showDrawTypeSelect.value = false;
+});
+
+// 绘制图形列表
+const graphicsTypeList: ListItem[] = reactive([
+  { label: '圆形', value: 'circle' },
+  { label: '矩形', value: 'rectangle' },
+  { label: '多边形', value: 'polygon' }
+  // { label: '索套', value: 'freePolygon' }
+]);
+// 显隐图像类型弹窗
+let showGraphicsTypeSelect = ref(false);
+
+// 选择
+const selectGraphicsType = (index) => {
+  mouseToolState.graphicsType = graphicsTypeList[index].value;
+  showGraphicsTypeSelect.value = false;
+};
+// 点击绘制类型选择之外
+onClickOutside(selectBoxRef3, () => {
+  if (!showGraphicsTypeSelect.value) return;
+  showGraphicsTypeSelect.value = false;
+});
+
+// 开启结束绘制
+const openDraw = () => {
+  mouseToolState.drawing = !mouseToolState.drawing;
+};
+const onDraw = (event) => {
+  const obj = event.obj;
+  const id = nanoid();
+  obj._opts.extData = {
+    id: id
+  };
+  const data: any = {
+    id: id,
+    type: drawOptions.graphicsType,
+    color: obj._opts.strokeColor,
+    drawType: obj._opts.fillOpacity === 0 ? '1' : '2'
+  };
+  if (data.type === 'circle') {
+    data.center = [obj.getCenter().lng, obj.getCenter().lat];
+    data.radius = obj.getRadius();
+  }
+  const path = obj.getPath();
+  // 将AMap.LngLat对象数组转换为经纬度数组
+  const pathArr = path.map((lngLat) => {
+    // 返回经度和纬度的数组
+    return [lngLat.lng, lngLat.lat];
+  });
+  data.path = pathArr;
+  overlays.push(obj);
+  overlaysData.push(data);
+  commit(deepClone(overlaysData));
+  console.log(data);
+  // 右击进入编辑
+  obj.on('rightclick', handleRightClick);
+  if (overlaysData.length === 1) {
+    analysisSpatial(data);
+  }
+  // 点击空间分析
+  obj.on('click', function () {
+    // 没在编辑时
+    if (!mouseToolState.drawing) {
+      analysisSpatial(data);
+    }
+  });
+};
+let rightClickObj;
+// 图形右击事件
+let initContextMenu = false;
+const handleRightClick = (event) => {
+  rightClickObj = event.target;
+  const contextMenu = getDrawTool().getContextMenu();
+  if (!initContextMenu) {
+    // 右键删除按钮
+    contextMenu.addItem(
+      '删除',
+      function () {
+        contextMenu.close();
+        deleteGraphics();
+      },
+      0
+    );
+    initContextMenu = true;
+  }
+  contextMenu.open(getMap(), event.lnglat);
+};
+// 删除图形
+const deleteGraphics = () => {
+  const id = rightClickObj.getExtData()?.id;
+  if (id) {
+    for (let i = 0; i < overlays.length; i++) {
+      const overlay = Array.isArray(overlays[i]) ? overlays[i][0] : overlays[i];
+      if (overlay?.getExtData().id === id) {
+        removeOverlayByIndex(i);
+        commit(deepClone(overlaysData));
+        rightClickObj = null;
+      }
+    }
+  }
+};
+// 撤销绘制
+const handleUndo = () => {
+  mouseToolState.drawing = false;
+  const previous = history.value[history.value.length - 2];
+  if (history.value.length > 1) {
+    if (currentState.value.length > previous.length) {
+      // 撤销新增
+      removeOverlayByIndex(currentState.value.length - 1);
+    } else {
+      let restoreData;
+      for (let i = 0; i < previous.length; i++) {
+        let index = 0;
+        for (let k = 0; k < currentState.value.length; k++) {
+          if (previous[i].id !== currentState.value[k].id) {
+            index++;
+          } else {
+            break;
+          }
+        }
+        if (index === previous.length - 1) {
+          restoreData = previous[i];
+          break;
+        }
+      }
+      if (restoreData) {
+        const newData = {
+          type: restoreData.type,
+          path: restoreData.path,
+          strokeColor: restoreData.color,
+          strokeOpacity: 1,
+          strokeWeight: '1',
+          fillColor: restoreData.color,
+          fillOpacity: restoreData.drawType === '1' ? 0 : 0.5
+        };
+        if (restoreData.type === 'circlr') {
+          newData.center = restoreData.center;
+          newData.radius = restoreData.radius;
+        }
+        const obj = getDrawTool().createGraphics(newData);
+        overlays.push(obj);
+      }
+    }
+    undo();
+
+    console.log(history.value, future.value, currentState.value);
+  }
+};
+// 根据索引移除覆盖物
+const removeOverlayByIndex = (index: number) => {
+  const map = getMap();
+  if (Array.isArray(overlays[index])) {
+    overlays[index].forEach((overlay) => {
+      // 移除地图上覆盖物
+      map.remove(overlay);
+    });
+  } else {
+    // 移除地图上覆盖物
+    map.remove(overlays[index]);
+  }
+  overlays.splice(index, 1);
+  overlaysData.splice(index, 1);
+};
+let selectedScope = reactive({});
+// 空间分析数据
+const analysisSpatial = (data) => {
+  // 已选中的范围
+  if (selectedScope[data.id]) {
+    delete selectedScope[data.id];
+    if (JSON.stringify(selectedScope) === '{}') {
+      // rightMenuRef.value.clickContractMenu();
+      return;
+    }
+  } else {
+    selectedScope[data.id] = data;
+  }
+  let location = [];
+  for (let key in selectedScope) {
+    let itemLocation = [];
+    if (selectedScope[key].path && selectedScope[key].path.length > 1) {
+      selectedScope[key].path.forEach((item) => {
+        itemLocation.push({
+          x: item[0],
+          y: item[1]
+        });
+      });
+      itemLocation.push(itemLocation[0]);
+      location.push(itemLocation);
+    }
+  }
+  emits('handleAnalysisData', location);
+};
+</script>
+
+<style lang="scss" scoped>
+.draw-tools-container {
+  position: fixed;
+  top: 2200px;
+  left: 102px;
+  background-color: #304468;
+  border-radius: 5px;
+  display: flex;
+  color: #fff;
+  font-size: 18px;
+  padding: 10px 0;
+  .draw-item {
+    display: flex;
+    align-items: center;
+    position: relative;
+    padding: 0 10px;
+    cursor: pointer;
+    border-left: 1px solid #3a74be;
+    &:first-child {
+      border-left: none;
+    }
+    .item-label {
+      margin-right: 10px;
+    }
+    .select-box {
+      position: absolute;
+      left: 0;
+      z-index: 7;
+      width: 100%;
+      .box-content {
+        background-color: #102341;
+      }
+      .select-item {
+        display: flex;
+        align-items: center;
+        cursor: pointer;
+        padding: 5px 10px;
+        &:hover {
+          background-color: rgba(22, 73, 142, 0.5);
+        }
+        &:first-child {
+          margin-left: 0;
+        }
+      }
+      .color-box {
+        margin-right: 5px;
+      }
+    }
+  }
+  .color-box {
+    display: block;
+    width: 20px;
+    height: 20px;
+    cursor: pointer;
+    border-radius: 3px;
+  }
+}
+.active {
+  background-color: rgba(22, 73, 142, 0.5);
+}
+</style>

+ 211 - 44
src/views/globalMap/RightMenu/OnlinePlotting.vue

@@ -8,7 +8,7 @@
   <el-color-picker v-model="mouseToolState.color" popper-class="custom-color-picker" show-alpha size="large" />
   <el-button size="large" @click="changeDrawing">{{ drawing ? '关闭' : '开启' }}</el-button>
   <div class="flex">
-    <div class="draw-item" @click="undo">撤销</div>
+    <div class="draw-item" @click="handleUndo">撤销</div>
   </div>
   <div class="tabs1">
     <div v-for="(item, index) in menu" :key="index" :class="menuActive1 === index ? 'tab tab_active' : 'tab'" @click="clickTab(index)">
@@ -60,18 +60,19 @@
 </template>
 
 <script lang="ts" setup>
-interface Props {
-  drawing: boolean;
-  mouseToolState: MouseTool;
-}
+import { nanoid } from 'nanoid';
+import { deepClone } from '@/utils';
+import { useHistory } from '@/hooks/useHistory';
 
-const props = withDefaults(defineProps<Props>(), {});
-const emits = defineEmits(['updateDrawing', 'updateMouseToolState']);
+const getDrawTool = inject('getDrawTool');
 const getMap = inject('getMap');
-const getMouseTool = inject('getMouseTool');
-const updateMouseToolState = inject('updateMouseToolState');
-const undo = inject('undo');
-
+const { currentState, commit, undo, history, future } = useHistory();
+let drawing = ref(false);
+const mouseToolState = ref<MouseTool>({
+  color: 'rgba(248, 1, 2, 1)',
+  lineWidth: '1',
+  graphicsType: ''
+});
 const menuActive1 = ref<string | number>(0);
 const menuActive2 = ref<string | number>(0);
 const menuActive3 = ref<string | number>('');
@@ -153,10 +154,12 @@ const textEditState = reactive({
   text: '',
   lnglat: []
 });
+const overlays = [];
+const overlaysData = [];
 watch(
-  () => props.drawing,
+  () => drawing,
   () => {
-    if (!props.drawing) {
+    if (!drawing.value) {
       menuActive3.value = '';
     }
   }
@@ -190,81 +193,245 @@ const clickTab = (value: number) => {
 const clickTab2 = (value: number) => {
   menuActive2.value = value;
 };
+let drawOptions = {};
 // 点击三级菜单
 const clickTab3 = (item, index) => {
   const type = item.value;
-  let flag: boolean;
-  if (props.mouseToolState.graphicsType !== type || (props.mouseToolState.graphicsType === 'marker' && props.mouseToolState.title !== item.name)) {
-    flag = true;
+  if (mouseToolState.value.graphicsType !== type || (mouseToolState.value.graphicsType === 'marker' && mouseToolState.value.title !== item.name)) {
     if (type === 'text') {
+      mouseToolState.value.graphicsType = type;
       handleTextEdit();
     } else if (type === 'marker') {
       const data = {
         graphicsType: type,
-        lineWidth: props.mouseToolState.lineWidth,
-        color: props.mouseToolState.color,
+        lineWidth: mouseToolState.value.lineWidth,
+        color: mouseToolState.value.color,
         icon: item.icon,
         size: item.size,
         title: item.name
       };
-      emits('updateDrawing', true);
-      updateMouseToolState(data);
+      drawing.value = true;
+      mouseToolState.value = data;
     } else {
       textEditState.showTextEdit = false;
-      emits('updateDrawing', true);
-      props.mouseToolState.graphicsType = type;
+      drawing.value = true;
+      mouseToolState.value.graphicsType = type;
     }
+    const drawTool = getDrawTool();
+    const newOptions = drawTool.drawGraphics(mouseToolState.value);
+    if (newOptions.graphicsType !== 'text') {
+      // 绘制完成事件
+      initDrawMethod(newOptions);
+    }
+    menuActive3.value = index;
   } else {
-    flag = false;
     textEditState.showTextEdit = false;
-    emits('updateDrawing', false);
-    props.mouseToolState.graphicsType = '';
-  }
-  if (flag) {
-    menuActive3.value = index;
+    drawing.value = false;
+    mouseToolState.value.graphicsType = '';
+    menuActive3.value = '';
   }
 };
 const handleTextEdit = () => {
-  const map = getMap();
-  const mouseTool = getMouseTool();
-  mouseTool.close();
+  const drawTool = getDrawTool();
+  const map = drawTool.getMap();
+  drawTool.closeDraw();
   // 监听地图点击事件
   map.on('click', handleClickMap);
 };
 const handleClickMap = (e) => {
   // 获取点击位置的经纬度
-  textEditState.lnglat = e.lnglat;
+  textEditState.lnglat = [e.lnglat.lng, e.lnglat.lat];
   textEditState.showTextEdit = true;
 };
 const addText = () => {
   const data = {
     graphicsType: 'text',
-    lineWidth: props.mouseToolState.lineWidth,
-    color: props.mouseToolState.color,
+    lineWidth: mouseToolState.value.lineWidth,
+    color: mouseToolState.value.color,
     text: textEditState.text,
     fontColor: textEditState.fontColor,
     fontSize: textEditState.fontSize,
     lnglat: textEditState.lnglat
   };
   cancelEdit();
-  emits('updateDrawing', true);
-  updateMouseToolState(data);
+  drawing.value = true;
+  mouseToolState.value = data;
+  // 绘制完成事件
+  const drawTool = getDrawTool();
+  const res = drawTool.drawGraphics(mouseToolState.value);
+  overlays.push(res.text);
+  overlaysData.push(res.data);
+  commit(deepClone(overlaysData));
+  res.text.on('rightclick', handleRightClick);
+  const map = drawTool.getMap();
+  // 监听地图点击事件
+  map.off('click', handleClickMap);
+  close();
 };
 const cancelEdit = () => {
   textEditState.showTextEdit = false;
   textEditState.text = '';
 };
-watch(
-  () => props.mouseToolState.graphicsType,
-  (value) => {
-    if (value) {
+// watch(
+//   () => mouseToolState.value.graphicsType,
+//   (value) => {
+//     if (value) {
+//       const map = getMap();
+//       map.off('click', handleClickMap);
+//     }
+//   }
+// );
+const changeDrawing = () => {
+  drawing.value = !drawing.value;
+};
+const close = () => {
+  const drawTool = getDrawTool();
+  drawTool.closeDraw();
+  drawing.value = false;
+  menuActive3.value = '';
+  mouseToolState.value.graphicsType = '';
+};
+// 初始化绘制完成回调
+const initDrawMethod = (options) => {
+  const drawTool = getDrawTool();
+  const mouseTool = drawTool.getMouseTool();
+  const onDraw = (event) => {
+    mouseTool.off('draw', onDraw);
+    close();
+    const obj = event.obj;
+    const id = nanoid();
+    obj._opts.extData = {
+      id: id
+    };
+    const data: any = deepClone(options);
+    data.id = id;
+    if (options.type == 'marker') {
+      const position = obj.getPosition();
+      data.path = [position.lng, position.lat];
+    } else {
+      const path = obj.getPath();
+      // 将AMap.LngLat对象数组转换为经纬度数组
+      const pathArr = path.map((lngLat) => {
+        // 返回经度和纬度的数组
+        return [lngLat.lng, lngLat.lat];
+      });
+      if (options.type !== 'straightLine') {
+        pathArr.push(pathArr[0]);
+      }
+      data.path = pathArr;
+      if (options.type == 'circle') {
+        data.center = [obj.getCenter().lng, obj.getCenter().lat];
+        data.radius = obj.getRadius();
+      }
+    }
+    if (options.type == 'measureArea') {
+      const AMap = getDrawTool().getAMap();
       const map = getMap();
-      map.off('click', handleClickMap);
+      // 计算区域面积
+      const area = Math.round(AMap.GeometryUtil.ringArea(data.path));
+      const text = new AMap.Text({
+        position: data.path[data.path.length - 1],
+        text: '区域面积' + area + '平方米',
+        offset: new AMap.Pixel(-20, -20)
+      });
+      data.area = area;
+      map.add(text);
+      overlays.push([obj, text]);
+      overlaysData.push(data);
+      commit(deepClone(overlaysData));
+    } else {
+      overlays.push(obj);
+      overlaysData.push(data);
+      commit(deepClone(overlaysData));
+      console.log(overlaysData);
     }
+    // 右击进入编辑
+    obj.on('rightclick', handleRightClick);
+  };
+  mouseTool.on('draw', onDraw);
+};
+// 图形右击事件
+let rightClickObj;
+let initContextMenu = false;
+const handleRightClick = (event) => {
+  rightClickObj = event.target;
+  const contextMenu = getDrawTool().getContextMenu();
+  if (!initContextMenu) {
+    // 右键删除按钮
+    contextMenu.addItem(
+      '删除',
+      function () {
+        contextMenu.close();
+        deleteGraphics();
+      },
+      0
+    );
+    initContextMenu = true;
   }
-);
-const changeDrawing = () => {
-  emits('updateDrawing', !props.drawing);
+  contextMenu.open(getMap(), event.lnglat);
+};
+// 删除图形
+const deleteGraphics = () => {
+  const id = rightClickObj.getExtData()?.id;
+  if (id) {
+    for (let i = 0; i < overlays.length; i++) {
+      const overlay = Array.isArray(overlays[i]) ? overlays[i][0] : overlays[i];
+      if (overlay?.getExtData().id === id) {
+        removeOverlayByIndex(i);
+        commit(deepClone(overlaysData));
+        rightClickObj = null;
+      }
+    }
+  }
+};
+// 撤销绘制
+const handleUndo = () => {
+  // drawing.value = false;
+  const previous = history.value[history.value.length - 2];
+  if (history.value.length > 1) {
+    if (currentState.value.length > previous.length) {
+      // 撤销新增
+      removeOverlayByIndex(currentState.value.length - 1);
+    } else {
+      let restoreData;
+      for (let i = 0; i < previous.length; i++) {
+        let index = 0;
+        for (let k = 0; k < currentState.value.length; k++) {
+          if (previous[i].id !== currentState.value[k].id) {
+            index++;
+          } else {
+            break;
+          }
+        }
+        if (index === previous.length - 1) {
+          restoreData = previous[i];
+          break;
+        }
+      }
+      if (restoreData) {
+        const obj = getDrawTool().createGraphics(restoreData);
+        overlays.push(obj);
+      }
+    }
+    undo();
+
+    console.log(history.value, future.value, currentState.value);
+  }
+};
+// 根据索引移除覆盖物
+const removeOverlayByIndex = (index: number) => {
+  const map = getMap();
+  if (Array.isArray(overlays[index])) {
+    overlays[index].forEach((overlay) => {
+      // 移除地图上覆盖物
+      map.remove(overlay);
+    });
+  } else {
+    // 移除地图上覆盖物
+    map.remove(overlays[index]);
+  }
+  overlays.splice(index, 1);
+  overlaysData.splice(index, 1);
 };
 </script>
 

+ 13 - 7
src/views/globalMap/RightMenu/SpatialAnalysis.vue

@@ -28,17 +28,16 @@
       <div v-for="(item, index) in filteredList" :key="index" class="item2">{{ item.name + '(' + item.list.length + ')' }}</div>
     </div>
   </div>
+  <DrawTools @handleAnalysisData="handleAnalysisData" />
 </template>
 
 <script lang="ts" setup name="AnalyzeDataDialog">
+import DrawTools from './DrawTools.vue';
 import { validateNum } from '@/utils/ruoyi';
 import { getSpatialAnalysis } from '@/api/globalMap';
 import { deepClone } from '@/utils';
 
-interface Props {
-  location: {};
-}
-const props = withDefaults(defineProps<Props>(), {});
+const emits = defineEmits(['handleMenu']);
 const keyword = ref('');
 let analysisData = ref({
   townCount: '',
@@ -75,10 +74,11 @@ const filteredSubItems = (parentItem) => {
     return subItem.name.toLowerCase().includes(keyword.value.toLowerCase());
   });
 };
+const location = ref([]);
 watch(
-  () => props.location,
+  () => location,
   () => {
-    getSpatialAnalysis(props.location).then((res) => {
+    getSpatialAnalysis(location.value).then((res) => {
       if (res.data && res.data.list) {
         const list = [];
         res.data.list.forEach((item) => {
@@ -92,9 +92,15 @@ watch(
     });
   },
   {
-    immediate: true
+    immediate: true,
+    deep: true
   }
 );
+
+const handleAnalysisData = (data) => {
+  location.value = data;
+  emits('handleMenu', '空间分析');
+};
 </script>
 
 <style lang="scss" scoped>

+ 3 - 12
src/views/globalMap/RightMenu/index.vue

@@ -23,7 +23,7 @@
         <!--图层分析-->
         <LayerAnalysis v-if="menuState.showMenu && menuState.menuData[menuState.activeIndex]?.name === '图层分析'" :pointType="pointType" />
         <!--空间分析-->
-        <SpatialAnalysis v-if="menuState.showMenu && menuState.menuData[menuState.activeIndex]?.name === '空间分析'" :location="location" />
+        <SpatialAnalysis v-if="menuState.showMenu && menuState.menuData[menuState.activeIndex]?.name === '空间分析'" @handleMenu="handleMenu" />
         <!--江湖河库-->
         <Reservoir v-if="menuState.showMenu && menuState.menuData[menuState.activeIndex]?.name === '江湖河库'" />
         <!--路网视频-->
@@ -40,7 +40,6 @@
           :mouseToolState="mouseToolState"
           :drawing="drawing"
           @updateDrawing="updateDrawing"
-
         />
       </div>
     </div>
@@ -58,9 +57,7 @@ import OnlinePlotting from '@/views/globalMap/RightMenu/OnlinePlotting.vue';
 import { PointType } from '@/api/globalMap/type';
 
 interface Props {
-  drawing: boolean;
-  pointType: PointType;
-  mouseToolState: MouseTool;
+  pointType: PointType[];
 }
 
 withDefaults(defineProps<Props>(), {});
@@ -79,7 +76,6 @@ const activeName = computed(() => {
   }
   return name;
 });
-const location = ref([]);
 
 // 点击收缩展开
 const clickContractMenu = () => {
@@ -110,11 +106,6 @@ const handleMenu = (name) => {
   }
 };
 
-const setAnalysisData = (data) => {
-  location.value = data;
-  handleMenu('空间分析');
-};
-
 // 新增菜单 type 1 新增 2 删除
 const updateMenu = (type, menu) => {
   if (type === '1') {
@@ -133,7 +124,7 @@ const updateDrawing = (value: boolean) => {
   emits('update:drawing', value);
 };
 
-defineExpose({ handleMenu, clickContractMenu, setAnalysisData, updateMenu });
+defineExpose({ handleMenu, clickContractMenu, updateMenu });
 </script>
 
 <style lang="scss" scoped>

+ 7 - 98
src/views/globalMap/index.vue

@@ -2,34 +2,12 @@
   <div id="globalMap">
     <div :class="isComponent ? 'global-map' : 'global-map bg'">
       <MapLogical v-if="activeMap === 'logical'" :map-data="mapData" />
-      <YztMap
-        v-else-if="['satellite2', 'satellite3'].includes(activeMap)"
-        ref="map2Ref"
-        v-model:drawing="drawing"
-        :mouseToolState="mouseToolState"
-        :active-map="activeMap"
-        :pointType="pointType"
-        @onDrawCompleted="onDrawCompleted"
-      />
-      <Map
-        v-else
-        ref="mapRef"
-        v-model:drawing="drawing"
-        :mouseToolState="mouseToolState"
-        :active-map="activeMap"
-        :pointType="pointType"
-        @onDrawCompleted="onDrawCompleted"
-      />
+      <YztMap v-else-if="['satellite2', 'satellite3'].includes(activeMap)" ref="map2Ref" :active-map="activeMap" :pointType="pointType" />
+      <Map v-else ref="mapRef" :active-map="activeMap" :pointType="pointType" />
       <!--左侧菜单-->
       <LeftMenu style="position: absolute; top: 20px; left: 20px" @click-menu="clickMenu" @select-search-marker="selectSearchMarker" />
       <!--右侧菜单-->
-      <RightMenu
-        ref="rightMenuRef"
-        :analysis-data="selectedScope"
-        v-model:drawing="drawing"
-        :mouseToolState="mouseToolState"
-        :pointType="pointType"
-      />
+      <RightMenu ref="rightMenuRef" :pointType="pointType" />
       <!--更换地图类型-->
       <SwitchMapTool :active-map="activeMap" class="tool-box" @switch-map="switchMap" />
       <!--时间轴-->
@@ -154,68 +132,6 @@ const selectSearchMarker = (item) => {
   dom.addSearchMarker(item2);
 };
 
-// 实时标绘
-let drawing = ref(false);
-const mouseToolState = ref<MouseTool>({
-  color: 'rgba(248, 1, 2, 1)',
-  lineWidth: '1',
-  graphicsType: ''
-});
-const updateMouseToolState = (data: MouseTool) => {
-  mouseToolState.value = data;
-};
-// 点击撤销
-const undo = () => {
-  const dom = activeMap.value === 'satellite2' ? map2Ref.value : mapRef.value;
-  dom.handleUndo();
-};
-let selectedScope = reactive({});
-// 绘制结束事件
-const onDrawCompleted = (data, overlaysData, obj) => {
-  drawing.value = false;
-  if (overlaysData.length === 1) {
-    analysisSpatial(data);
-  }
-  // 点击空间分析
-  obj.on('click', function () {
-    // 没在编辑时
-    if (!drawing.value) {
-      analysisSpatial(data);
-    }
-  });
-};
-// 空间分析数据
-const analysisSpatial = (data) => {
-  // 已选中的范围
-  if (selectedScope[data.id]) {
-    delete selectedScope[data.id];
-    if (JSON.stringify(selectedScope) === '{}') {
-      // rightMenuRef.value.clickContractMenu();
-      return;
-    }
-  }
-  selectedScope[data.id] = data;
-  let location = [];
-  for (let key in selectedScope) {
-    let itemLocation = [];
-    if (selectedScope[key].path && selectedScope[key].path.length > 1) {
-      selectedScope[key].path.forEach((item) => {
-        itemLocation.push({
-          x: item[0],
-          y: item[1]
-        });
-      });
-      location.push(itemLocation);
-    }
-  }
-  rightMenuRef.value.setAnalysisData(location);
-};
-// 取消显示
-// const unSelectGraphics = (data) => {
-//   delete selectedScope[data.id];
-//   rightMenuRef.value.clickContractMenu();
-// };
-
 // 获取地图元素操作
 const getMap = () => {
   if (['satellite2', 'satellite3'].includes(activeMap.value)) {
@@ -225,18 +141,16 @@ const getMap = () => {
   }
   return {};
 };
-const getMouseTool = () => {
+const getDrawTool = () => {
   if (['satellite2', 'satellite3'].includes(activeMap.value)) {
-    return map2Ref.value.getMouseTool();
+    return map2Ref.value.drawTool;
   } else if (['vectorgraph', 'satellite'].includes(activeMap.value)) {
-    return mapRef.value.getMouseTool();
+    return mapRef.value.drawTool;
   }
   return {};
 };
 provide('getMap', getMap);
-provide('getMouseTool', getMouseTool);
-provide('updateMouseToolState', updateMouseToolState);
-provide('undo', undo);
+provide('getDrawTool', getDrawTool);
 </script>
 
 <style lang="scss" scoped>
@@ -261,11 +175,6 @@ provide('undo', undo);
     color: #fff;
   }
 }
-.absoluteTool {
-  position: absolute;
-  bottom: 100px;
-  left: 20px;
-}
 .box {
   position: absolute;
   top: 20px;