Bläddra i källkod

实时标绘 绘制文字、图标

Hwf 8 månader sedan
förälder
incheckning
c48495e936

+ 1 - 1
.env.development

@@ -5,7 +5,7 @@ VITE_APP_TITLE = 应急指挥一张图
 VITE_APP_ENV = 'development'
 
 # 开发环境
-VITE_APP_BASE_API = 'http://19.155.220.206'
+VITE_APP_BASE_API = 'http://10.181.7.235'
 
 # 应用访问路径 例如使用前缀 /admin/
 VITE_APP_CONTEXT_PATH = '/'

+ 0 - 20
LICENSE

@@ -1,20 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2019 RuoYi-Vue-Plus
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 2 - 2
package-lock.json

@@ -1,11 +1,11 @@
 {
-  "name": "ruoyi-vue-plus",
+  "name": "yjdp-web",
   "version": "5.2.1",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
-      "name": "ruoyi-vue-plus",
+      "name": "yjdp-web",
       "version": "5.2.1",
       "license": "MIT",
       "dependencies": {

+ 1 - 2
package.json

@@ -1,8 +1,7 @@
 {
-  "name": "ruoyi-vue-plus",
+  "name": "yjdp-web",
   "version": "5.2.1",
   "description": "应急指挥一张图",
-  "author": "LionLi",
   "license": "MIT",
   "type": "module",
   "scripts": {

+ 0 - 11
src/components/Map/TextEdit/index.vue

@@ -1,11 +0,0 @@
-<template>
-
-</template>
-
-<script lang="ts" setup>
-
-</script>
-
-<style lang="scss" scoped>
-
-</style>

+ 4 - 13
src/components/Map/index.vue

@@ -66,7 +66,7 @@ const mapState = reactive({
 let AMap, map, scale;
 
 // 鼠标绘制工具
-const { initMouseTool, drawGraphics, closeDraw, handleUndo } = useDrawTool({
+const { getMouseTool, initMouseTool, drawGraphics, closeDraw, handleUndo } = useDrawTool({
   color: props.mouseToolState.color,
   lineWidth: props.mouseToolState.lineWidth,
   drawType: props.mouseToolState.drawType,
@@ -187,18 +187,9 @@ let lnglat;
 watch(
   () => props.mouseToolState,
   () => {
+    debugger
     if (props.drawing) {
-      if (props.mouseToolState.graphicsType === 'text') {
-        // 监听地图点击事件
-        map.on('click', function (e) {
-          // 获取点击位置的经纬度
-          lnglat = e.lnglat;
-          emits('showTextEditBox', true);
-        });
-        // 绘制文字点击地图触发
-      } else {
-        drawGraphics(props.mouseToolState);
-      }
+      drawGraphics(props.mouseToolState);
     } else {
       closeDraw();
     }
@@ -254,7 +245,7 @@ const setCenter = (item) => {
   map.setCenter([item.longitude, item.latitude]);
 };
 
-defineExpose({ addMarker, addSearchMarker, setCenter, getMarkers, clearMarker, handleUndo });
+defineExpose({ addMarker, addSearchMarker, setCenter, getMarkers, clearMarker, handleUndo, getMap, getMouseTool });
 const handleResize = () => {
   const containerWidth = containerRef.value.clientWidth * containerScale().scaleX;
   const containerHeight = containerRef.value.clientHeight * containerScale().scaleY;

+ 50 - 64
src/hooks/AMap/useDrawTool.ts

@@ -30,8 +30,6 @@ export function useDrawTool(options: DrawToolOptions) {
     AMap = options2.AMap;
     // 初始化绘制工具
     mouseTool = new AMap.MouseTool(map);
-    // 绘制完成事件
-    initDrawMethod();
     //创建右键菜单
     contextMenu = new AMap.ContextMenu();
     // 右键删除按钮
@@ -44,27 +42,27 @@ export function useDrawTool(options: DrawToolOptions) {
     );
   };
   // 初始化绘制完成回调
-  const initDrawMethod = () => {
+  const initDrawMethod = (options) => {
     mouseTool.on('draw', function (event) {
       const obj = event.obj;
       const id = nanoid();
       obj._opts.extData = {
         id: id
       };
-      const data: any = {
-        id: id,
-        type: getType(obj.className),
-        color: obj._opts.strokeColor,
-        drawType: obj._opts.fillOpacity === 0 ? '1' : '2'
-      };
-      const path = obj.getPath();
-      // 将AMap.LngLat对象数组转换为经纬度数组
-      const pathArr = path.map((lngLat) => {
-        // 返回经度和纬度的数组
-        return [lngLat.lng, lngLat.lat];
-      });
-      pathArr.push(pathArr[0]);
-      data.path = pathArr;
+      const data: any = deepClone(options);
+      data.id = id;
+      if (options.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));
@@ -93,24 +91,12 @@ export function useDrawTool(options: DrawToolOptions) {
       }
     }
   };
-  // 判断图形类型
-  const getType = (type) => {
-    let res = '';
-    if (type === 'Overlay.Circle') {
-      res = 'circle';
-    } else if (type === 'Overlay.Rectangle') {
-      res = 'rectangle';
-    } else if (type === 'Overlay.Polygon') {
-      res = 'polygon';
-    }
-    return res;
-  };
-  let lnglat = [];
   // 绘制图形
   const drawGraphics = (newOptions: MouseTool) => {
     const data = getRgba(newOptions.color);
     if (['circle', 'rectangle', 'polygon'].includes(newOptions.graphicsType)) {
       drawOptions = {
+        type: newOptions.graphicsType,
         strokeColor: !!data.color ? data.color : newOptions.color,
         strokeOpacity: 1,
         strokeWeight: newOptions.lineWidth,
@@ -120,6 +106,7 @@ export function useDrawTool(options: DrawToolOptions) {
       };
     } else if (['anyLine', 'straightLine'].includes(newOptions.graphicsType)) {
       drawOptions = {
+        type: newOptions.graphicsType,
         strokeColor: !!data.color ? data.color : newOptions.color,
         strokeOpacity: data.opacity,
         strokeWeight: newOptions.lineWidth,
@@ -127,17 +114,19 @@ export function useDrawTool(options: DrawToolOptions) {
       };
     } else if (newOptions.graphicsType === 'marker') {
       drawOptions = {
-        strokeColor: !!data.color ? data.color : newOptions.color,
-        strokeOpacity: 1,
-        strokeWeight: newOptions.lineWidth,
-        fillColor: data.color,
-        fillOpacity: data.opacity,
-        strokeStyle: 'solid'
+        type: newOptions.graphicsType,
+        anchor: 'bottom-center',
+        markerName: newOptions.markerName,
+        icon: newOptions.icon,
+        size: new AMap.Size(newOptions.size[0], newOptions.size[1])
       };
     } else if (newOptions.graphicsType === 'text') {
       drawOptions = {
+        type: newOptions.graphicsType,
+        text: newOptions.text,
         fontSize: newOptions.fontSize,
-        color: newOptions.color
+        fontColor: newOptions.fontColor,
+        lnglat: newOptions.lnglat
       };
     }
     closeDraw();
@@ -155,10 +144,17 @@ export function useDrawTool(options: DrawToolOptions) {
     } else if (newOptions.graphicsType === 'straightLine') {
       // 绘制直线
       mouseTool.polyline(drawOptions);
+    } else if (newOptions.graphicsType === 'text') {
+      // 绘制文字
+      addText(drawOptions);
     } else if (newOptions.graphicsType === 'marker') {
       // 绘制图标
       mouseTool.marker(drawOptions);
     }
+    if (newOptions.graphicsType !== 'text') {
+      // 绘制完成事件
+      initDrawMethod(drawOptions);
+    }
   };
   const addText = (drawOptions) => {
     // 文本覆盖物的样式
@@ -173,21 +169,29 @@ export function useDrawTool(options: DrawToolOptions) {
 
     // 创建文本覆盖物
     const text = new AMap.Text({
-      text: drawOptions.fontText, // 文本内容,可以根据需要自定义
-      position: lnglat, // 文本位置(经纬度)
+      text: drawOptions.text, // 文本内容,可以根据需要自定义
+      position: drawOptions.lnglat, // 文本位置(经纬度)
       style: textStyle, // 文本样式
       zIndex: 100, // 文本层级
       draggable: true // 是否可拖动(可选)
-      // 如果需要,可以在这里添加点击文本的回调事件
-      // events: {
-      //     click: function() {
-      //         // 处理文本点击事件
-      //         alert('您点击了文本!');
-      //     }
-      // }
     });
     // 将文本覆盖物添加到地图
     map.add(text);
+    const id = nanoid();
+    text._opts.extData = {
+      id: id
+    };
+    const data: any = deepClone(drawOptions);
+    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);
   };
   let polyline = null;
   let startPoint = null;
@@ -242,21 +246,6 @@ export function useDrawTool(options: DrawToolOptions) {
     isDrawing = true;
     lastPoint = startPoint;
   };
-  // 设置颜色
-  const setColor = (color: string) => {
-    drawOptions.strokeColor = color;
-    drawOptions.fillColor = color;
-  };
-  // 设置线框面框
-  const setDrawType = (drawType: string) => {
-    drawOptions.fillOpacity = drawType === '1' ? 0 : 0.5;
-  };
-  // 设置图形类型
-  const setGraphicsType = (type: string) => {
-    closeDraw();
-    graphicsType = type;
-    drawGraphics(type);
-  };
   // 关闭绘制
   const closeDraw = () => {
     mouseTool.close();
@@ -377,9 +366,6 @@ export function useDrawTool(options: DrawToolOptions) {
     initMouseTool,
     drawGraphics,
     closeDraw,
-    setColor,
-    setDrawType,
-    setGraphicsType,
     createCircle,
     removeOverlayByIndex,
     handleUndo,

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

@@ -93,13 +93,14 @@ declare module 'vue' {
     SizeSelect: typeof import('./../components/SizeSelect/index.vue')['default']
     SubmitVerify: typeof import('./../components/Process/submitVerify.vue')['default']
     SvgIcon: typeof import('./../components/SvgIcon/index.vue')['default']
-    TextEdit: typeof import('./../components/Map/TextEdit/index.vue')['default']
+    TextEdit: typeof import('@/views/globalMap/RightMenu/TextEdit/index.vue')['default']
     TimeAxis: typeof import('./../components/TimeAxis/index.vue')['default']
     TopNav: typeof import('./../components/TopNav/index.vue')['default']
     TreeSelect: typeof import('./../components/TreeSelect/index.vue')['default']
     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 {

+ 9 - 5
src/types/map.d.ts

@@ -1,9 +1,13 @@
 interface MouseTool {
-  color: string;
-  lineWidth: string;
-  drawType: string;
+  color?: string;
+  lineWidth?: string;
+  drawType?: string;
   graphicsType: string;
   text?: string;
-  fontSize?: number;
-  fontColor?: number;
+  fontSize?: string;
+  fontColor?: string;
+  lnglat?: string[];
+  markerName?: string;
+  icon?: string;
+  size?: number[];
 }

+ 80 - 20
src/views/globalMap/RightMenu/OnlinePlotting.vue

@@ -7,6 +7,9 @@
   </el-select>
   <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>
   <div class="tabs1">
     <div v-for="(item, index) in menu" :key="index" :class="menuActive1 === index ? 'tab tab_active' : 'tab'" @click="clickTab(index)">
       {{ item.name }}
@@ -27,7 +30,7 @@
           v-for="(item, index) in menu[menuActive1].children[menuActive2].children"
           :key="index"
           :class="menuActive2 === index ? 'tab tab_active' : 'tab'"
-          @click="clickTab3(item.value)"
+          @click="clickTab3(item)"
         >
           {{ item.name }}
         </div>
@@ -40,7 +43,7 @@
     <div class="edit-box">
       <div class="flex">
         <div class="text">字号</div>
-        <el-input v-model="textEditState.fonSize" />
+        <el-input v-model="textEditState.fontSize" />
       </div>
       <div class="flex">
         <div class="text">颜色</div>
@@ -48,7 +51,7 @@
       </div>
     </div>
     <div class="edit-btn-box">
-      <el-button size="large" @click="textEditState.show = false">取消</el-button>
+      <el-button size="large" @click="cancelEdit">取消</el-button>
       <el-button type="primary" size="large" @click="addText">确定</el-button>
     </div>
   </div>
@@ -62,6 +65,10 @@ interface Props {
 
 const props = withDefaults(defineProps<Props>(), {});
 const emits = defineEmits(['updateDrawing', 'updateMouseToolState']);
+const getMap = inject('getMap');
+const getMouseTool = inject('getMouseTool');
+const updateMouseToolState = inject('updateMouseToolState');
+const undo = inject('undo');
 
 const menuActive1 = ref(0);
 const menuActive2 = ref(0);
@@ -107,7 +114,15 @@ const menu = ref([
           }
         ]
       },
-      { name: '火点', value: 'firePoint', children: [] },
+      {
+        name: '火点',
+        value: 'firePoint',
+        children: [
+          { name: '起火点', value: 'marker', icon: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png', size: [19, 31] },
+          { name: '烟点', value: 'marker', icon: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png', size: [19, 31] },
+          { name: '已灭火点', value: 'marker', icon: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png', size: [19, 31] }
+        ]
+      },
       { name: '火线', value: 'firewire', children: [] },
       { name: '火场', value: 'fireGround', children: [] },
       { name: '箭头', value: 'arrow', children: [] },
@@ -119,7 +134,7 @@ const menu = ref([
     ]
   },
   {
-    name: '历史记录',
+    name: '历史预案',
     children: []
   }
 ]);
@@ -132,7 +147,8 @@ const textEditState = reactive({
   showTextEdit: false,
   fontColor: '#f80102',
   fontSize: '36',
-  text: ''
+  text: '',
+  lnglat: []
 });
 
 // 点击一级菜单
@@ -144,31 +160,75 @@ const clickTab2 = (value: number) => {
   menuActive2.value = value;
 };
 // 点击三级菜单
-const clickTab3 = (type) => {
-  if (props.mouseToolState.graphicsType === type) {
+const clickTab3 = (item) => {
+  const type = item.value;
+  if (
+    props.mouseToolState.graphicsType !== type ||
+    (props.mouseToolState.graphicsType === 'marker' && props.mouseToolState.markerName !== item.name)
+  ) {
+    if (type === 'text') {
+      handleTextEdit();
+    } else if (type === 'marker') {
+      const data = {
+        graphicsType: type,
+        lineWidth: props.mouseToolState.lineWidth,
+        color: props.mouseToolState.color,
+        icon: item.icon,
+        size: item.size,
+        markerName: item.name
+      };
+      emits('updateDrawing', true);
+      updateMouseToolState(data);
+    } else {
+      textEditState.showTextEdit = false;
+      emits('updateDrawing', true);
+      props.mouseToolState.graphicsType = type;
+    }
+  } else {
+    textEditState.showTextEdit = false;
     emits('updateDrawing', false);
     props.mouseToolState.graphicsType = '';
-  } else {
-    emits('updateDrawing', true);
-    props.mouseToolState.graphicsType = type;
   }
 };
-const handleClick = () => {
-  textEditState.fontColor = props.mouseToolState.color;
-  textEditState.fontSize = props.mouseToolState.fontSize;
+const handleTextEdit = () => {
+  const map = getMap();
+  const mouseTool = getMouseTool();
+  mouseTool.close();
+  // 监听地图点击事件
+  map.on('click', handleClickMap);
+};
+const handleClickMap = (e) => {
+  // 获取点击位置的经纬度
+  textEditState.lnglat = e.lnglat;
   textEditState.showTextEdit = true;
 };
 const addText = () => {
-  emits('updateMouseToolState', {
-    color: props.mouseToolState.color,
-    lineWidth: props.mouseToolState.lineWidth,
-    drawType: props.mouseToolState.drawType,
+  const data = {
     graphicsType: 'text',
+    lineWidth: props.mouseToolState.lineWidth,
+    color: props.mouseToolState.lineWidth,
+    text: textEditState.text,
     fontColor: textEditState.fontColor,
-    fontSize: textEditState.fontSize
-  });
+    fontSize: textEditState.fontSize,
+    lnglat: textEditState.lnglat
+  };
+  cancelEdit();
+  emits('updateDrawing', true);
+  updateMouseToolState(data);
+};
+const cancelEdit = () => {
   textEditState.showTextEdit = false;
+  textEditState.text = '';
 };
+watch(
+  () => props.mouseToolState.graphicsType,
+  (value) => {
+    if (value) {
+      const map = getMap();
+      map.off('click', handleClickMap);
+    }
+  }
+);
 const changeDrawing = () => {
   emits('updateDrawing', !props.drawing);
 };

+ 8 - 2
src/views/globalMap/RightMenu/index.vue

@@ -33,7 +33,12 @@
         <!--河道监测-->
         <RiverMonitor v-if="menuState.showMenu && menuState.menuData[menuState.activeIndex]?.name === '河道监测'" />
         <!--实时标绘-->
-        <OnlinePlotting v-if="menuState.showMenu && menuState.menuData[menuState.activeIndex]?.name === '实时标绘'" :mouseToolState="mouseToolState" :drawing="drawing" @updateDrawing="updateDrawing" />
+        <OnlinePlotting
+          v-if="menuState.showMenu && menuState.menuData[menuState.activeIndex]?.name === '实时标绘'"
+          :mouseToolState="mouseToolState"
+          :drawing="drawing"
+          @updateDrawing="updateDrawing"
+        />
       </div>
     </div>
   </div>
@@ -122,7 +127,8 @@ const updateMenu = (type, menu) => {
 
 const updateDrawing = (value: boolean) => {
   emits('update:drawing', value);
-}
+};
+
 defineExpose({ handleMenu, clickContractMenu, setAnalysisData, updateMenu });
 </script>
 

+ 24 - 22
src/views/globalMap/index.vue

@@ -20,7 +20,6 @@
         :pointType="pointType"
         @select-graphics="analysisSpatial"
         @un-select-graphics="unSelectGraphics"
-        @showTextEditBox="showTextEditBox"
       />
       <!--左侧菜单-->
       <LeftMenu style="position: absolute; top: 20px; left: 20px" @click-menu="clickMenu" @select-search-marker="selectSearchMarker" />
@@ -31,20 +30,11 @@
         v-model:drawing="drawing"
         :mouseToolState="mouseToolState"
         :pointType="pointType"
-        @updateMouseToolState="updateMouseToolState"
       />
       <!--更换地图类型-->
       <SwitchMapTool :active-map="activeMap" class="tool-box" @switch-map="switchMap" />
       <!--时间轴-->
       <TimeAxis />
-<!--      <DrawTools-->
-<!--        v-if="showDrawTools"-->
-<!--        v-model:drawing="mouseToolState.drawing"-->
-<!--        v-model:color="mouseToolState.color"-->
-<!--        v-model:graphicsType="mouseToolState.graphicsType"-->
-<!--        class="absoluteTool"-->
-<!--        @undo="undo"-->
-<!--      />-->
     </div>
   </div>
 </template>
@@ -57,7 +47,6 @@ import { iconList, logicalData } from './data/mapData';
 import SwitchMapTool from '@/views/globalMap/SwitchMapTool.vue';
 import LeftMenu from './LeftMenu.vue';
 import TimeAxis from '@/components/TimeAxis/index.vue';
-import DrawTools from '@/views/globalMap/DrawTools.vue';
 import { deepClone } from '@/utils';
 import { getPointInfo } from '@/api/globalMap';
 import RightMenu from './RightMenu/index.vue';
@@ -171,20 +160,11 @@ let drawing = ref(false);
 const mouseToolState = ref<MouseTool>({
   color: 'rgba(248, 1, 2, 1)',
   lineWidth: '1',
-  graphicsType: '',
-  text: '',
-  fontColor: '#f80102',
-  fontSize: '36'
+  graphicsType: ''
 });
-
-const showTextEditBox = () => {
-
-};
-
-const updateMouseToolState = (data) => {
+const updateMouseToolState = (data: MouseTool) => {
   mouseToolState.value = data;
 };
-
 // 点击撤销
 const undo = () => {
   const dom = activeMap.value === 'satellite2' ? map2Ref.value : mapRef.value;
@@ -222,6 +202,28 @@ const unSelectGraphics = (data) => {
   delete selectedScope[data.id];
   rightMenuRef.value.clickContractMenu();
 };
+
+// 获取地图元素操作
+const getMap = () => {
+  if (['satellite2', 'satellite3'].includes(activeMap.value)) {
+    return map2Ref.value.getMap();
+  } else if (['vectorgraph', 'satellite'].includes(activeMap.value)) {
+    return mapRef.value.getMap();
+  }
+  return {};
+};
+const getMouseTool = () => {
+  if (['satellite2', 'satellite3'].includes(activeMap.value)) {
+    return map2Ref.value.getMouseTool();
+  } else if (['vectorgraph', 'satellite'].includes(activeMap.value)) {
+    return mapRef.value.getMouseTool();
+  }
+  return {};
+};
+provide('getMap', getMap);
+provide('getMouseTool', getMouseTool);
+provide('updateMouseToolState', updateMouseToolState);
+provide('undo', undo);
 </script>
 
 <style lang="scss" scoped>