Prechádzať zdrojové kódy

粤政图 绘制功能

Hwf 3 mesiacov pred
rodič
commit
641cba6aef

+ 1 - 0
package.json

@@ -33,6 +33,7 @@
     "element-plus": "^2.8.5",
     "element-resize-detector": "^1.2.4",
     "file-saver": "^2.0.5",
+    "gcoord": "^1.0.7",
     "jsencrypt": "^3.3.2",
     "nanoid": "^5.0.7",
     "normalize.css": "^8.0.1",

+ 9 - 0
pnpm-lock.yaml

@@ -53,6 +53,9 @@ importers:
       file-saver:
         specifier: ^2.0.5
         version: 2.0.5
+      gcoord:
+        specifier: ^1.0.7
+        version: 1.0.7
       jsencrypt:
         specifier: ^3.3.2
         version: 3.3.2
@@ -3188,6 +3191,10 @@ packages:
   functions-have-names@1.2.3:
     resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
 
+  gcoord@1.0.7:
+    resolution: {integrity: sha512-UCN2iSm69jBOYz2ma2eg5I5imp65Cj70rcTTfMNSNMvZpR1U6oGjmVh080aCvC/6lN1ClkuOoBeaLuebw9AZJg==}
+    engines: {node: '>=16.11.0'}
+
   gensync@1.0.0-beta.2:
     resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
     engines: {node: '>=6.9.0'}
@@ -9449,6 +9456,8 @@ snapshots:
 
   functions-have-names@1.2.3: {}
 
+  gcoord@1.0.7: {}
+
   gensync@1.0.0-beta.2: {}
 
   geojson-equality-ts@1.0.2:

BIN
src/assets/images/dotIcon/common.png


BIN
src/assets/images/dotIcon/common_hover.png


BIN
src/assets/images/dotIcon/mark.png


BIN
src/assets/images/map/mark.png


+ 109 - 190
src/components/Map/YztMap/index.vue

@@ -1,131 +1,96 @@
 <template>
   <div ref="containerRef" class="map-container">
-    <div
-        ref="mapRef"
-        id="YztMap"
-        class="map-container"
-        :style="{
-        width: width,
-        height: height
-      }"
-    />
+    <div ref="mapRef" id="YztMap" class="map-container2" />
   </div>
 </template>
 
 <script setup lang="ts">
+import 'ol/ol.css';
 import mmJson from '@/assets/json/mm2.json';
 import { olMap } from '@/utils/olMap/olMap';
-import { PointType } from '@/api/globalMap/type';
-import { ScaleLine } from 'ol/control';
-import { getPointInfoList } from '@/api/globalMap';
+import {getPointInfoList} from '@/api/globalMap';
 import { getDictLabel } from '@/utils/dict';
 import { methodList, titleList } from '../data';
-import { pointDetailTemplate } from '../mapData';
+import { pointDetailTemplate } from '@/components/Map/mapData';
+import useMapStore from '@/store/modules/map';
 
-interface Props {
-  activeMap: string;
-  pointType: PointType[];
-}
-const containerScale = inject('containerScale');
-const props = withDefaults(defineProps<Props>(), {});
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { point_type } = toRefs<any>(proxy?.useDict('point_type'));
-const emits = defineEmits(['update:drawing', 'selectGraphics', 'handleShowWarehouse', 'handleShowVideo', 'handleShowPeople']);
+const mapStore = useMapStore();
+const emits = defineEmits(['handleShowWarehouse', 'handleShowVideo', 'handleShowPeople', 'closeDetailDialog', 'resize']);
 
 const mapRef = ref(null);
-const mapState = reactive({
-  center: [110.925175, 21.678955],
-  zoom: 7.9,
-  minZoom: 6,
-  maxZoom: 16,
-  isThreeDimensional: false,
-  // 是否显示比例尺
-  showScale: true
-});
 const containerRef = ref();
-const width = ref('100%');
-const height = ref('100%');
 let map, mapUtils;
 
-// 监听是否开启绘制
-watch(
-    () => props.drawing,
-    (value) => {
-      if (value) {
-        mapUtils.drawGraphics(props.graphicsType);
-      } else {
-        mapUtils.closeDraw();
-      }
-    }
-);
 const mapList = reactive({
   satellite2: [
-    { layer: 'map', code: 'YZT1712111943104', zIndex: '-99', visible: true },
-    { layer: 'annotation', code: 'YZT1695608158269', zIndex: '-98', visible: true }
+    { layer: 'map', layerType: 'WMTS', code: 'YZT1708679726700', zIndex: '-100', visible: true },
+    { layer: 'map', layerType: 'WFS', code: 'YZT1712111943104', zIndex: '-99', visible: true },
+    { layer: 'annotation', layerType: 'WMTS', code: 'YZT1695608158269', zIndex: '-98', visible: true }
   ],
   satellite3: [
-    { layer: 'map', code: 'YZT1708679726700', zIndex: '-99', visible: true },
-    { layer: 'annotation', code: 'YZT1695608158269', zIndex: '-98', visible: true }
+    { layer: 'map', layerType: 'WMTS', code: 'YZT1708679726700', zIndex: '-99', visible: true },
+    { layer: 'annotation', layerType: 'WMTS', code: 'YZT1695608158269', zIndex: '-98', visible: true }
   ],
   imageMap: [
-    { layer: 'map', code: 'YZT1640925052482', zIndex: '-99' },
-    { layer: 'annotation', code: 'YZT1695608158269', zIndex: '-98', visible: true }
+    { layer: 'map', layerType: 'WMTS', code: 'YZT1640925052482', zIndex: '-99' },
+    { layer: 'annotation', layerType: 'WMTS', code: 'YZT1695608158269', zIndex: '-98', visible: true }
   ],
   imageMap2: [
-    { layer: 'map', code: 'YZT1640925052482', zIndex: '-99', visible: true },
-    { layer: 'annotation', code: 'YZT1695608158269', zIndex: '-98', visible: true }
+    { layer: 'map', layerType: 'JSON', code: 'gd', zIndex: '-99', visible: true },
+    { layer: 'annotation', layerType: 'WMTS', code: 'YZT1695608158269', zIndex: '-98', visible: true }
   ]
 });
 // 监听地图类型变化
 watch(
-    () => props.activeMap,
+    () => mapStore.activeMap,
     () => {
       if (!mapUtils) return;
-      mapUtils.replaceLayers(mapList[props.activeMap]);
+      mapUtils.replaceLayers(mapList[mapStore.activeMap]);
+    }
+);
+watch(
+    () => mapStore.showMask,
+    () => {
+      if (mapStore.showMask) {
+        mapUtils.createVecByJson2(mmJson, { strokeWeight: 2 });
+      } else {
+        mapUtils.removeMask3(true);
+      }
     }
 );
-let scale;
 const init = () => {
+  mapStore.setMapLoaded(false);
   mapUtils = new olMap({
     dom: mapRef.value,
-    id: mapList[props.activeMap],
-    center: mapState.center,
-    zoom: mapState.zoom,
-    minZoom: mapState.minZoom,
-    maxZoom: mapState.maxZoom,
+    id: mapList[mapStore.activeMap],
+    center: mapStore.mapState.center,
+    zoom: mapStore.mapState.zoom,
+    minZoom: mapStore.mapState.minZoom,
+    maxZoom: mapStore.mapState.maxZoom,
+    showScale: true,
     drawTool: {
-      use: true,
-      color: props.color,
-      drawType: props.drawType,
-      graphicsType: props.graphicsType,
-      // 绘制完成事件
-      onDrawCompleted: (data, overlaysData, obj) => {
-        emits('selectGraphics', data, overlaysData.length.toString());
-        // 点击空间分析
-        obj.on('click', function () {
-          // 没在编辑时
-          if (!props.drawing) {
-            emits('selectGraphics', data);
-          }
-        });
-      }
+      use: true
     },
     // 加载完成事件
     onLoadCompleted: (YztMap) => {
       map = YztMap;
-      // initMouseTool(mapUtils);
-      scale = new ScaleLine();
-      map.addControl(scale);
+      mapStore.setMapLoaded(true);
       map.getView().on('change:resolution', () => {
-        mapState.zoom = map.getView().getZoom().toFixed(2);
+        mapStore.setZoom(map.getView().getZoom().toFixed(2));
       });
+      if (mapStore.showMask) {
+        mapUtils.createVecByJson2(mmJson, { strokeWeight: 2 });
+      }
+      // let drawTool = new DrawTool({ mapUtils, map });
       handleResize();
     },
     onMarkerClick: (data) => {
       // 多点位
       if (data.type === '1') {
         let path = [];
-        props.pointType.forEach((item) => {
+        mapStore.pointType.forEach((item) => {
           path.push(item.component);
         });
         getPointInfoList({
@@ -182,42 +147,40 @@ const handlePointDetails = (data) => {
   method(data.id).then((res) => {
     if (!!pointDetailTemplate[data.dataType]) {
       let div = document.createElement('div');
-      // div.style.cssText = 'transform: scale(' + containerScale().scaleX + ');';
       div.className = 'point-info';
       let titleDom = document.createElement('div');
       titleDom.className = 'title-box';
       titleDom.innerHTML = '<div class="gradient-text">' + title + '</div></div>';
       div.appendChild(titleDom);
-      if (data.dataType === 2) {
-        let btnBox = document.createElement('div');
-        let btn = document.createElement('div');
-        btnBox.className = 'flex';
-        btn.className = 'btn';
-        btn.innerHTML = '<div class="video-icon"></div><div>物资详情</div>';
-        btn.onclick = () => {
-          emits('handleShowWarehouse', data);
-        };
-        btnBox.appendChild(btn);
-        div.appendChild(btnBox);
-      } else if (data.dataType === 4) {
-        let btnBox = document.createElement('div');
-        let btn = document.createElement('div');
-        btnBox.className = 'flex';
-        btn.className = 'btn';
-        btn.innerHTML = '<div class="video-icon"></div><div>附近视频</div>';
-        btn.onclick = () => {
-          emits('handleShowVideo', data);
+      const objs = {
+        '2': {
+          title: '物资详情',
+          method: 'handleShowWarehouse'
+        },
+        '41': {
+          title: '人员列表',
+          method: 'handleShowPeople'
+        },
+        '43': {
+          title: '历史轨迹',
+          method: 'handleShowTrack'
+        }
+      };
+      let obj = objs[data.dataType];
+      if ([4, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58].includes(data.dataType)) {
+        obj = {
+          title: '附近视频',
+          method: 'handleShowVideo'
         };
-        btnBox.appendChild(btn);
-        div.appendChild(btnBox);
-      } else if (data.dataType === 41) {
+      }
+      if (!!obj) {
         let btnBox = document.createElement('div');
         let btn = document.createElement('div');
         btnBox.className = 'flex';
         btn.className = 'btn';
-        btn.innerHTML = '<div class="video-icon"></div><div>人员列表</div>';
+        btn.innerHTML = '<div class="video-icon"></div>' + obj.title + '<div></div>';
         btn.onclick = () => {
-          emits('handleShowPeople', data);
+          emits(obj.method, data);
         };
         btnBox.appendChild(btn);
         div.appendChild(btnBox);
@@ -259,9 +222,14 @@ const handlePointDetails = (data) => {
       div.appendChild(table);
       let closeBtn = document.createElement('div');
       closeBtn.className = 'close';
-      closeBtn.onclick = () => mapUtils.hideInfo(true);
+      closeBtn.onclick = () => {
+        mapUtils.hideInfo(true);
+        emits('closeDetailDialog', data);
+      };
       div.appendChild(closeBtn);
-      mapUtils.showInfo(div, [data.longitude, data.latitude], true);
+      mapUtils.showInfo(div, [data.longitude, data.latitude], -data.scale * data.size[1], true);
+    } else if (data.dataType === 'video') {
+      emits('handleShowVideoDetail', res.data, true);
     }
   });
 };
@@ -269,13 +237,14 @@ const filterTd = (obj, dataType) => {
   let data = [];
   let tempData = {};
   let i = 0;
-  for (let key in obj) {
-    let keyLabel = pointDetailTemplate[dataType][key];
+  const pointDetailTemplateObj = pointDetailTemplate[dataType];
+  Object.keys(pointDetailTemplateObj).forEach((key) => {
+    let keyLabel = pointDetailTemplateObj[key];
     if (!!keyLabel) {
       if (i === 2) {
         i = 0;
       }
-      const value = !!obj[key] ? obj[key] : '';
+      const value = !!obj && !!obj[key] ? obj[key] : '';
       if (value && value.length > 8) {
         if (i === 0) {
           data.push({ type: 'longText', data: [{ label: keyLabel, value: value }] });
@@ -297,7 +266,7 @@ const filterTd = (obj, dataType) => {
         }
       }
     }
-  }
+  });
   if (!!tempData && JSON.stringify(tempData) !== '{}') {
     data.push(tempData);
   }
@@ -312,22 +281,30 @@ const addMarker = (points) => {
 const clearMarker = () => {
   mapUtils.clearMarker();
 };
+
+let inverseScale = ref({
+  inverseScaleX: 1,
+  inverseScaleY: 1
+});
 const handleResize = () => {
-  const containerWidth = containerRef.value.clientWidth * containerScale().scaleX;
-  const containerHeight = containerRef.value.clientHeight * containerScale().scaleY;
-  width.value = containerWidth + 'px';
-  height.value = containerHeight + 'px';
+  if (!containerRef.value) return;
   map.updateSize();
+  nextTick(() => {
+    emits('resize');
+  });
 };
 const getMap = () => {
   return map;
 };
-const getScale = () => {
-  return scale;
-};
 const getMapUtils = () => {
   return mapUtils;
 };
+const getDrawTool = () => {
+  return mapUtils.getDrawTool();
+};
+const addSearchMarker = (item) => {
+  return mapUtils.addSearchMarker(item);
+};
 // 加载事件
 onMounted(() => {
   init();
@@ -337,89 +314,31 @@ onMounted(() => {
 onUnmounted(() => {
   window.removeEventListener('resize', handleResize);
 });
-provide('getMapUtils', getMapUtils);
-provide('getMap', getMap);
-provide('getScale', getScale);
-defineExpose({ getMapUtils, addMarker, clearMarker });
+defineExpose({ getMap, getMapUtils, addSearchMarker, getDrawTool, addMarker, clearMarker });
 </script>
 
 <style scoped>
 @import '../map.scss';
+.map-container2 {
+  width: 100%;
+  height: 100%;
+  position: relative;
+  overflow: hidden;
+}
 .map-container {
   width: 100%;
   height: 100%;
   position: relative;
-  .zoom-text {
-    position: absolute;
-    bottom: 8px;
-    right: 75px;
-    font-size: 14px;
+  :deep(.ol-scale-line) {
+    left: unset;
+    bottom: 16px;
+    right: 48px;
+    background-color: transparent;
   }
-  .right-tool {
-    position: absolute;
-    bottom: 50px;
-    right: 5px;
-  }
-  .model-btn {
-    background-color: #022577;
-    font-size: 14px;
-    color: #889ee9;
-    cursor: pointer;
-    padding: 3px 3px;
-    border-radius: 5px;
-  }
-  :deep(.amap-scalecontrol) {
-    left: unset !important;
-    background-color: unset !important;
-    right: 74px;
-  }
-  :deep(.amap-scale-text) {
-    width: 230px !important;
-    text-align: left !important;
-    font-size: 14px;
-    padding-left: 20px;
-  }
-  :deep(.amap-scale-middle) {
-    width: 228px !important;
-  }
-  :deep(.amap-scale-edgeright) {
-    left: 228px !important;
-  }
-  :deep(.amap-logo),
-  :deep(.amap-copyright) {
-    display: none !important;
-  }
-  /* 自定义测距工具样式 */
-  :deep(.amap-ranger) {
-    background-color: #ffffff;
-    border: 1px solid #888888;
-    border-radius: 5px;
-    padding: 5px;
-  }
-
-  :deep(.amap-ranger .amap-toolbar) {
-    color: #333;
-    font-size: 12px;
-    padding: 5px;
-  }
-
-  :deep(.amap-ranger .amap-toolbar button) {
-    background-color: #022577;
-    color: #fff;
-    border: none;
-    border-radius: 4px;
-    padding: 5px;
-    margin-right: 5px;
-    cursor: pointer;
-  }
-
-  :deep(.amap-ranger .amap-toolbar button:hover) {
-    background-color: #1a3c75;
-  }
-
-  :deep(.amap-ranger .amap-toolbar .amap-toolbar-text) {
-    color: #444;
-    font-size: 14px;
+  :deep(.ol-scale-line-inner) {
+    color: #ffffff;
+    border: 1px solid #fff;
+    border-top: none;
   }
 }
 </style>

+ 24 - 68
src/components/Map/index.vue

@@ -11,54 +11,29 @@ import {getDictLabel} from '@/utils/dict';
 import {pointDetailTemplate} from './mapData';
 import {getPointInfoList} from "@/api/globalMap";
 import {methodList, titleList} from './data';
+import mmJson from '@/assets/json/mm2.json';
+import useMapStore from '@/store/modules/map';
 
-interface EventDetails {
-  'event_id': String;
-  'event_title': String;
-  'event_status': String;
-  'keep_time': String;
-  'event_type': String;
-  'event_level': String;
-  'address': String;
-  'event_time': String;
-  'event_source': String;
-  'latitude': String;
-  'longitude': String
-}
-
-interface Props {
-  activeMap: string;
-  pointType: [];
-  eventDetails?: EventDetails;
-  zoom?: number
-}
-
-const props = withDefaults(defineProps<Props>(), {});
 const {proxy} = getCurrentInstance() as ComponentInternalInstance;
 const {point_type} = toRefs<any>(proxy?.useDict('point_type'));
 
-const emits = defineEmits(['onLoadCompleted', 'selectGraphics', 'unSelectGraphics', 'showTextEditBox', 'onDrawCompleted', 'handleShowVideo', 'handleShowWarehouse']);
+const emits = defineEmits(['selectGraphics', 'unSelectGraphics', 'showTextEditBox', 'onDrawCompleted', 'handleShowVideo', 'handleShowWarehouse']);
+
+const mapStore = useMapStore();
 
-const mapState = reactive({
-  center: [110.925175, 21.678955],
-  zoom: 7.9,
-  minZoom: 6,
-  maxZoom: 20,
-  isThreeDimensional: false,
-  // 是否显示比例尺
-  showScale: true
-});
 let AMap, map, scale;
 // 鼠标绘制工具
 const drawTool = useDrawTool();
+mapStore.setMapLoaded(false);
 // 初始化地图
 const mapUtils = useAMap({
   key: '9c5041381e5e824f9ee324d8f7a40150', // 申请好的Web端开发者Key,首次调用 load 时必填
   securityJsCode: '4868bc1b8fac7d9e54e7279ed556879a', // 安全密钥
   version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
-  pitch: mapState.isThreeDimensional ? 45 : 0,
-  zoom: !!props.zoom ? props.zoom : mapState.zoom,
-  center: !!props.eventDetails && props.eventDetails.longitude && props.eventDetails.latitude ? [props.eventDetails.longitude, props.eventDetails.latitude] : [mapState.center[0], mapState.center[1]],
+  pitch: mapStore.mapState.isThreeDimensional ? 45 : 0,
+  zoom: mapStore.mapState.zoom,
+  maxZoom: mapStore.mapState.maxZoom,
+  center: mapStore.mapState.center,
   dragEnable: true,
   scrollWheel: true,
   enableMouseTool: true,
@@ -66,21 +41,21 @@ const mapUtils = useAMap({
   onLoadCompleted: () => {
     AMap = getAMap();
     map = getMap();
+    mapStore.setMapLoaded(true);
     scale = getScale();
-    if (!['logical', 'vectorgraph'].includes(props.activeMap)) {
-      switchMap(props.activeMap);
-    } else {
-      map.removeLayer();
+    switchMap(mapStore.activeMap);
+    // 添加遮罩
+    if (mapStore.showMask) {
+      creatMask2(mmJson, { strokeWeight: 2 });
     }
-    drawTool.initMouseTool({container: 'aMap', map, AMap});
+    drawTool.initMouseTool({ map, AMap });
     handleResize();
-    emits('onLoadCompleted')
   },
   onMarkerClick: (data) => {
     // 多点位
     if (data.type === '1') {
       let path = [];
-      props.pointType.forEach((item) => {
+      mapStore.pointType.forEach((item) => {
         path.push(item.component);
       });
       getPointInfoList({
@@ -121,7 +96,7 @@ const mapUtils = useAMap({
         closeBtn.className = 'close';
         closeBtn.onclick = hideInfo;
         content.appendChild(closeBtn);
-        showInfo(content, [data.longitude, data.latitude], true);
+        showInfo(content, [data.longitude, data.latitude], -data.size[1], true);
       });
     } else {
       handlePointDetails(data);
@@ -129,19 +104,15 @@ const mapUtils = useAMap({
   }
 });
 const {
-  getAMap,
   getMap,
+  getAMap,
   switchMap,
   addMarker,
-  addSearchMarker,
   clearMarker,
-  getMarkers,
   getScale,
   showInfo,
   hideInfo,
-  handleHover,
-  creatMask,
-  trackPlayback
+  creatMask2
 } = {...mapUtils};
 const handlePointDetails = (data) => {
   const method = methodList[data.dataType];
@@ -284,36 +255,21 @@ const filterTd = (obj, dataType) => {
 };
 // 监听地图类型变化
 watch(
-    () => props.activeMap,
+    () => mapStore.activeMap,
     () => {
-      switchMap(props.activeMap);
+      switchMap(mapStore.activeMap);
     }
 );
 
-watch(() => props.eventDetails, () => {
-  if (!!map) {
-    clearMarker('point');
-    map.setCenter(props.eventDetails.longitude && props.eventDetails.latitude ? [props.eventDetails.longitude, props.eventDetails.latitude] : [mapState.center[0], mapState.center[1]]);
-  }
-}, {
-  deep: true
-})
 const getMapUtils = () => {
   return mapUtils;
 };
-const getDrawTool = () => {
-  return mapUtils.getDrawTool();
-};
 defineExpose({
-  getDrawTool,
+  getMap,
   getMapUtils,
   addMarker,
-  addSearchMarker,
-  getMarkers,
   clearMarker,
-  getMap,
-  drawTool,
-  handleHover
+  drawTool
 });
 const handleResize = () => {
   map.resize();

+ 352 - 16
src/components/Map/mapData.ts

@@ -1,5 +1,5 @@
-const getImageUrl = (name) => {
-  return new URL(`../../../assets/images/dotIcon/${name}`, import.meta.url).href;
+export const getImageUrl = (name) => {
+  return new URL(`../../assets/images/dotIcon/${name}`, import.meta.url).href;
 };
 export const iconList = {
   '1': {
@@ -22,11 +22,6 @@ export const iconList = {
     imageHover: getImageUrl('4_easy_flood_point_hover.png'),
     size: [60, 60]
   },
-  'common': {
-    image: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png',
-    imageHover: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png',
-    size: [19, 31]
-  },
   '5': {
     image: getImageUrl('5_school.png'),
     imageHover: getImageUrl('5_school_hover.png'),
@@ -153,8 +148,8 @@ export const iconList = {
     size: [60, 60]
   },
   '30': {
-    image: getImageUrl('29_emergencyofficer.png'),
-    imageHover: getImageUrl('29_emergencyofficer_hover.png'),
+    image: getImageUrl('31_lakes_video.png'),
+    imageHover: getImageUrl('31_lakes_video_hover.png'),
     size: [40, 40]
   },
   '31': {
@@ -201,6 +196,106 @@ export const iconList = {
     image: getImageUrl('41_rescue_team.png'),
     imageHover: getImageUrl('41_rescue_team_hover.png'),
     size: [55, 60]
+  },
+  '42': {
+    image: getImageUrl('42_transportation_video.png'),
+    imageHover: getImageUrl('42_transportation_video_hover.png'),
+    size: [40, 40]
+  },
+  '43': {
+    image: getImageUrl('43_vehicle.png'),
+    imageHover: getImageUrl('43_vehicle_hover.png'),
+    size: [40, 44]
+  },
+  '44': {
+    image: getImageUrl('44_child_welfare_institution.png'),
+    imageHover: getImageUrl('44_child_welfare_institution_hover.png'),
+    size: [40, 44]
+  },
+  '45': {
+    image: getImageUrl('45_elderly_care_institution.png'),
+    imageHover: getImageUrl('45_elderly_care_institution_hover.png'),
+    size: [40, 44]
+  },
+  '46': {
+    image: getImageUrl('46_large_medium_reservoir.png'),
+    imageHover: getImageUrl('46_large_medium_reservoir_hover.png'),
+    size: [40, 44]
+  },
+  '47': {
+    image: getImageUrl('47_samll1_reservoir.png'),
+    imageHover: getImageUrl('47_samll1_reservoir_hover.png'),
+    size: [40, 44]
+  },
+  '48': {
+    image: getImageUrl('48_samll2_reservoir.png'),
+    imageHover: getImageUrl('48_samll2_reservoir_hover.png'),
+    size: [40, 44]
+  },
+  '49': {
+    image: getImageUrl('49_roof_pond.png'),
+    imageHover: getImageUrl('49_roof_pond_hover.png'),
+    size: [40, 44]
+  },
+  '50': {
+    image: getImageUrl('50_gaoba_mountain_pond.png'),
+    imageHover: getImageUrl('50_gaoba_mountain_pond_hover.png'),
+    size: [40, 44]
+  },
+  '51': {
+    image: getImageUrl('51_reservoir_downgraded_mountain_pond.png'),
+    imageHover: getImageUrl('51_reservoir_downgraded_mountain_pond_hover.png'),
+    size: [40, 44]
+  },
+  '52': {
+    image: getImageUrl('52_river_embankment.png'),
+    imageHover: getImageUrl('52_river_embankment_hover.png'),
+    size: [40, 44]
+  },
+  '53': {
+    image: getImageUrl('53_sea_embankment.png'),
+    imageHover: getImageUrl('53_sea_embankment_hover.png'),
+    size: [40, 44]
+  },
+  '54': {
+    image: getImageUrl('54_large_water_gate.png'),
+    imageHover: getImageUrl('54_large_water_gate_hover.png'),
+    size: [40, 44]
+  },
+  '55': {
+    image: getImageUrl('55_medium_water_gate.png'),
+    imageHover: getImageUrl('55_medium_water_gate_hover.png'),
+    size: [40, 44]
+  },
+  '56': {
+    image: getImageUrl('56_power_station.png'),
+    imageHover: getImageUrl('56_power_station_hover.png'),
+    size: [40, 44]
+  },
+  '57': {
+    image: getImageUrl('57_key_village.png'),
+    imageHover: getImageUrl('57_key_village_hover.png'),
+    size: [40, 44]
+  },
+  '58': {
+    image: getImageUrl('58_disaster_prone_village.png'),
+    imageHover: getImageUrl('58_disaster_prone_village_hover.png'),
+    size: [40, 44]
+  },
+  'video': {
+    image: getImageUrl('31_lakes_video.png'),
+    imageHover: getImageUrl('31_lakes_video_hover.png'),
+    size: [40, 40]
+  },
+  'common': {
+    image: getImageUrl('common.png'),
+    imageHover: getImageUrl('common_hover.png'),
+    size: [40, 44]
+  },
+  'common2': {
+    image: getImageUrl('common2.png'),
+    imageHover: getImageUrl('common2.png'),
+    size: [19, 31]
   }
 };
 
@@ -614,6 +709,14 @@ export const pointDetailTemplate = {
     mine_contact_person: '矿山联系人',
     mine_contact_phone: '矿山联系电话'
   },
+  '20': {
+    unit_name: '单位名称',
+    type: '单位类型',
+    area: '所属区县',
+    location: '详细地址',
+    longitude: '经度',
+    latitude: '纬度'
+  },
   '21': {
     prjcode: '项目编号',
     prjname: '项目名称',
@@ -855,14 +958,247 @@ export const pointDetailTemplate = {
     longitude: '经度',
     latitude: '纬度'
   },
-  'forestFireWarn': {
-    name: '地址',
-    time1: '热点上报时间',
-    data2: '反馈类型',
-    time2: '反馈时间',
-    data1: '亮温强度',
+  '42': {
+    name: '名称',
+    area: '所属区县',
+    video_type_label: '类型',
+    status: '状态',
+    longitude: '经度',
+    latitude: '纬度'
+  },
+  '43': {
+    vehicle_no: '车牌号',
+    vehicle_type: '车辆类型',
+    vehicle_color: '车牌类型',
+    vin: '车架号',
+    chelodmass: '核定吨位',
+    bnscope: '经营范围'
+  },
+  '44': {
+    jigoumincheng: '机构名称',
+    jigoudizhi: '机构地址',
+    jigouxingzhi: '机构性质',
+    jigouleixing: '机构类型',
+    area: '所属区域',
+    jingdu: '经度',
+    weidu: '纬度',
+    jigoujianjie: '机构简介'
+  },
+  '45': {
+    jigoumincheng: '机构名称',
+    dizhi: '地址',
+    // dianweixinxi: '详细地址',
+    chenliriqi: '成立日期',
+    jiegoupingji: '机构评级',
+    chuangweishu: '床位数',
+    hulichuangweishu: '护理床位数',
+    huliyuanshu: '护理员数',
+    congyerenyuanshu: '从业人员数',
+    nianmolaorenshu: '年末老年人数',
+    quantuochuangweishu: '全托床位数',
+    rituochuangweishu: '日托床位数',
+    ruzhulv: '入住率',
+    shifoubeian: '是否备案',
+    jingdu: '经度',
+    weidu: '纬度',
+    jianjie: '简介'
+  },
+  '46': {
+    reservoir_name: '水库名称',
+    county: '县(市区)',
+    town: '所在镇',
+    total_capacity: '总库容(亿m3)',
+    crest_length: '坝顶长度(m)',
+    max_dam_height: '最大坝高(m)',
+    verification_flood_level: '校核洪水位(m)',
+    normal_storage_level: '正常蓄水位(m)',
+    historical_highest_level: '历史最高水位(m)',
+    pre_flood_limit_level: '前汛期限值水位(m)',
+    mid_flood_limit_level: '中汛期限值水位(m)',
+    post_flood_limit_level: '后汛期限值水位(m)',
+    spillway_type: '溢洪道型式',
+    spillway_number: '溢洪道孔口数量(个)',
+    spillway_crest_elevation: '溢洪道堰顶高程(m)',
+    spillway_max_discharge: '溢洪道最大下泄流量(m3/s)',
+    irrigation_area: '灌溉面积(万亩)',
+    protected_cultivated_land: '捍卫耕地(万亩)',
+    protected_downstream: '捍卫下游(区县、镇)',
+    protected_population: '捍卫人口(万人)',
+    longitude: '经度',
+    latitude: '纬度',
+    remark: '备注'
+  },
+  '47': {
+    reservoir_name: '水库名称',
+    county: '县(市区)',
+    town: '所在镇',
+    total_capacity: '总库容(亿m3)',
+    crest_length: '坝顶长度(m)',
+    max_dam_height: '最大坝高(m)',
+    verification_flood_level: '校核洪水位(m)',
+    normal_storage_level: '正常蓄水位(m)',
+    historical_highest_level: '历史最高水位(m)',
+    pre_flood_limit_level: '前汛期限值水位(m)',
+    mid_flood_limit_level: '中汛期限值水位(m)',
+    post_flood_limit_level: '后汛期限值水位(m)',
+    spillway_type: '溢洪道型式',
+    spillway_number: '溢洪道孔口数量(个)',
+    spillway_crest_elevation: '溢洪道堰顶高程(m)',
+    spillway_max_discharge: '溢洪道最大下泄流量(m3/s)',
+    irrigation_area: '灌溉面积(万亩)',
+    protected_cultivated_land: '捍卫耕地(万亩)',
+    protected_downstream: '捍卫下游(区县、镇)',
+    protected_population: '捍卫人口(万人)',
     longitude: '经度',
     latitude: '纬度',
-    address: '详细地址'
+    remark: '备注'
+  },
+  '48': {
+    reservoir_name: '水库名称',
+    county: '县(市区)',
+    town: '所在镇',
+    total_capacity: '总库容(亿m3)',
+    crest_length: '坝顶长度(m)',
+    max_dam_height: '最大坝高(m)',
+    verification_flood_level: '校核洪水位(m)',
+    normal_storage_level: '正常蓄水位(m)',
+    historical_highest_level: '历史最高水位(m)',
+    pre_flood_limit_level: '前汛期限值水位(m)',
+    mid_flood_limit_level: '中汛期限值水位(m)',
+    post_flood_limit_level: '后汛期限值水位(m)',
+    spillway_type: '溢洪道型式',
+    spillway_number: '溢洪道孔口数量(个)',
+    spillway_crest_elevation: '溢洪道堰顶高程(m)',
+    spillway_max_discharge: '溢洪道最大下泄流量(m3/s)',
+    irrigation_area: '灌溉面积(万亩)',
+    protected_cultivated_land: '捍卫耕地(万亩)',
+    protected_downstream: '捍卫下游(区县、镇)',
+    protected_population: '捍卫人口(万人)',
+    longitude: '经度',
+    latitude: '纬度',
+    remark: '备注'
+  },
+  '49': {
+    pond_name: '山塘名称',
+    location: '所在地',
+    pond_location: '山塘位置',
+    management_unit: '管理单位',
+    longitude: '经度',
+    latitude: '纬度'
+  },
+  '50': {
+    pond_name: '山塘名称',
+    location: '所在地',
+    pond_location: '山塘位置',
+    management_unit: '管理单位',
+    longitude: '经度',
+    latitude: '纬度'
+  },
+  '51': {
+    pond_name: '山塘名称',
+    location: '所在地',
+    pond_location: '山塘位置',
+    management_unit: '管理单位',
+    longitude: '经度',
+    latitude: '纬度'
+  },
+  '52': {
+    start_end_location: '起止地点',
+    representative_location: '代表地点',
+    dike_type: '堤别',
+    river_system: '河系',
+    county: '县(市、区)',
+    dike_length: '堤长(km)',
+    crest_elevation: '堤顶高程(米)',
+    historical_highest_level: '历史最高水位(米)',
+    warning_level: '警戒水位(米)',
+    defense_standard: '防御标准',
+    design_flood_level: '设计洪水位(米)',
+    protected_cultivated_land: '捍卫耕地(万亩)',
+    protected_population: '捍卫人口(万人)',
+    existing_sluice_number: '现有水闸座数(座)',
+    existing_sluice_total_width: '现有水闸总净宽(米)',
+    longitude: '经度',
+    latitude: '纬度'
+  },
+  '53': {
+    start_end_location: '起止地点',
+    representative_location: '代表地点',
+    dike_type: '堤别',
+    county: '县(市、区)',
+    dike_length: '堤长(km)',
+    standard_dike_length: '达标堤长(km)',
+    surveyed_highest_tide_level: '调查最高潮位(米)',
+    warning_tide_level: '警戒潮位(米)',
+    defense_standard: '防御标准',
+    design_tide_level: '设计潮位(米)',
+    protected_cultivated_land: '卫耕地(万亩)',
+    protected_population: '捍卫人口(万人)',
+    existing_sluice_number: '现有水闸座数(座)',
+    existing_sluice_total_width: '现有水闸总净宽(米)',
+    longitude: '经度',
+    latitude: '纬度'
+  },
+  '54': {
+    sluice_name: '水闸名称',
+    location: '所在地',
+    design_discharge: '设计过闸流量(立方米/秒)',
+    verification_discharge: '校核过闸流量(立方米/秒)',
+    structure_form: '结构形式',
+    gate_number: '闸孔孔数(孔)',
+    gate_height: '闸孔高(米)',
+    gate_width: '闸孔宽(米)',
+    gate_total_net_width: '闸孔总净宽(米)',
+    crest_elevation: '堰顶高程(米)',
+    main_purpose: '主要用途',
+    beneficiary_area: '受益面积(万亩)',
+    longitude: '经度',
+    latitude: '纬度'
+  },
+  '55': {
+    sluice_name: '水闸名称',
+    location: '所在地',
+    design_discharge: '设计过闸流量(立方米/秒)',
+    verification_discharge: '校核过闸流量(立方米/秒)',
+    structure_form: '结构形式',
+    gate_number: '闸孔孔数(孔)',
+    gate_height: '闸孔高(米)',
+    gate_width: '闸孔宽(米)',
+    gate_total_net_width: '闸孔总净宽(米)',
+    crest_elevation: '堰顶高程(米)',
+    main_purpose: '主要用途',
+    beneficiary_area: '受益面积(万亩)',
+    longitude: '经度',
+    latitude: '纬度'
+  },
+  '56': {
+    station_name: '水电站名称',
+    annual_report_code: '电站年报代码',
+    county: '县(市、区)',
+    installed_capacity: '装机容量(KW)',
+    reservoir_capacity: '水库库容(万立方米)',
+    dam_height: '坝高(米)',
+    is_completed: '是否完成竣工验收',
+    is_earth_dam: '挡水建筑物是否为土坝',
+    safety_regulation_body: '安全监管责任主体',
+    safety_production_body: '安全生产责任主体',
+    longitude: '经度',
+    latitude: '纬度'
+  },
+  '57': {
+    village_name: '村名',
+    county: '县(市、区)',
+    town: '所属镇',
+    longitude: '经度',
+    latitude: '纬度'
+  },
+  '58': {
+    village: '村名',
+    city: '市',
+    county: '县(市、区)',
+    town: '镇(街道)',
+    disaster_type: '易发灾害',
+    longitude: '经度',
+    latitude: '纬度'
   }
 };

+ 97 - 99
src/components/Tabbar/index.vue

@@ -32,101 +32,6 @@ import { getUnreadMsgCount } from "@/api/InformationReception/InformationRecepti
 
 const active = ref(0);
 let tabBarData = ref([]);
-watch(
-  () => useUserStore().roles,
-  () => {
-    const roles = useUserStore().roles;
-    if (roles.includes("leader")) {
-      tabBarData.value = [
-        {
-          icon: "index",
-          iconActive: "indexActive",
-          title: "首页",
-          num: '0',
-          to: {
-            name: "LeaderIndex"
-          }
-        },
-        {
-          icon: "command",
-          iconActive: "commandActive",
-          title: "移动指挥",
-          num: '0',
-          to: {
-            name: "MobileControl"
-          }
-        },
-        {
-          icon: "threepro",
-          iconActive: "threeproActive",
-          title: "三防责任人",
-          num: '0',
-          to: {
-            name: "ThreePreventionResponsiblePerson"
-          }
-        },
-        // {
-        //   icon: "contact",
-        //   iconActive: "contactActive",
-        //   title: "通讯录",
-        //   to: {
-        //     name: "AddressBook"
-        //   }
-        // },
-        // {
-        //   icon: "mine",
-        //   iconActive: "mineActive",
-        //   title: "我的",
-        //   to: {
-        //     name: "My"
-        //   }
-        // }
-      ];
-    } else if (roles.includes("worker")) {
-      tabBarData.value = [
-        {
-          icon: "index",
-          iconActive: "indexActive",
-          title: "首页",
-          num: '0',
-          to: {
-            name: "WorkerIndex"
-          }
-        },
-        {
-          icon: "command",
-          iconActive: "commandActive",
-          num: '0',
-          title: "事件管理",
-          to: {
-            name: "workerEvent"
-          }
-        },
-        {
-          icon: "contact",
-          iconActive: "contactActive",
-          num: '0',
-          title: "值班管理",
-          to: {
-            name: "Duty"
-          }
-        },
-        // {
-        //   icon: "mine",
-        //   iconActive: "mineActive",
-        //   title: "我的",
-        //   to: {
-        //     name: "My"
-        //   }
-        // }
-      ];
-    }
-  },
-  {
-    immediate: true,
-    deep: true
-  }
-);
 
 const getImageUrl = name => {
   return new URL(`../../assets/images/tabBar/${name}.png`, import.meta.url)
@@ -162,11 +67,104 @@ const fetchData = async () => {
   })
 }
 
+watch(
+    () => useUserStore().roles,
+    () => {
+      const roles = useUserStore().roles;
+      if (roles.includes("leader")) {
+        tabBarData.value = [
+          {
+            icon: "index",
+            iconActive: "indexActive",
+            title: "首页",
+            num: '0',
+            to: {
+              name: "LeaderIndex"
+            }
+          },
+          {
+            icon: "command",
+            iconActive: "commandActive",
+            title: "移动指挥",
+            num: '0',
+            to: {
+              name: "MobileControl"
+            }
+          },
+          {
+            icon: "threepro",
+            iconActive: "threeproActive",
+            title: "三防责任人",
+            num: '0',
+            to: {
+              name: "ThreePreventionResponsiblePerson"
+            }
+          },
+          // {
+          //   icon: "contact",
+          //   iconActive: "contactActive",
+          //   title: "通讯录",
+          //   to: {
+          //     name: "AddressBook"
+          //   }
+          // },
+          // {
+          //   icon: "mine",
+          //   iconActive: "mineActive",
+          //   title: "我的",
+          //   to: {
+          //     name: "My"
+          //   }
+          // }
+        ];
+      } else if (roles.includes("worker")) {
+        tabBarData.value = [
+          {
+            icon: "index",
+            iconActive: "indexActive",
+            title: "首页",
+            num: '0',
+            to: {
+              name: "WorkerIndex"
+            }
+          },
+          {
+            icon: "command",
+            iconActive: "commandActive",
+            num: '0',
+            title: "事件管理",
+            to: {
+              name: "workerEvent"
+            }
+          },
+          {
+            icon: "contact",
+            iconActive: "contactActive",
+            num: '0',
+            title: "值班管理",
+            to: {
+              name: "Duty"
+            }
+          },
+          // {
+          //   icon: "mine",
+          //   iconActive: "mineActive",
+          //   title: "我的",
+          //   to: {
+          //     name: "My"
+          //   }
+          // }
+        ];
+        fetchData();
+      }
+    },
+    {
+      immediate: true,
+      deep: true
+    }
+);
+
 onMounted(() => {
-  // 统计数
-  nextTick(()=>{
-    fetchData();
-  });
   startFetchingData();
 });
 onUnmounted(() => {

+ 67 - 0
src/store/modules/map.ts

@@ -0,0 +1,67 @@
+import { PointType } from '@/api/globalMap/type';
+
+export const useMapStore = defineStore('map', () => {
+  const mapState = reactive({
+    center: [110.925175, 21.978955],
+    zoom: 8.5,
+    minZoom: 6,
+    maxZoom: 16,
+    isThreeDimensional: false,
+    // 是否显示比例尺
+    showScale: true
+  });
+  // 地图加载是否完成
+  const mapLoaded = ref(false);
+  // 当前地图类型
+  const activeMap = ref<string>('');
+  // 是否显示边界遮罩
+  const showMask = ref<boolean>(true);
+  // 高德地图类型
+  const AMapType: string[] = ['vectorgraph', 'satellite'];
+  // 粤政图地图类型
+  const YMapType: string[] = ['satellite2', 'satellite3', 'imageMap', 'imageMap2'];
+  // 是否是高德地图
+  const isAMap = computed(() => AMapType.includes(activeMap.value));
+  // 选中的菜单
+  const pointType = ref<PointType[]>([]);
+  // 设置地图加载完成状态
+  const setMapLoaded = (loaded: boolean) => {
+    mapLoaded.value = loaded;
+  };
+  // 设置zoom
+  const setZoom = (zoom: number) => {
+    mapState.zoom = zoom;
+  };
+  // 设置地图类
+  const setActiveMap = (newMapKey: string) => {
+    activeMap.value = newMapKey;
+  };
+  // 修改显示遮罩边界
+  const setShowMask = (show: boolean) => {
+    showMask.value = show;
+  };
+  // 跳转界面时 初始化所有数据
+  const initData = () => {
+    mapState.center = [110.925175, 21.978955];
+    activeMap.value = 'satellite2';
+    showMask.value = true;
+    pointType.value = [];
+  };
+  return {
+    mapState,
+    mapLoaded,
+    activeMap,
+    isAMap,
+    showMask,
+    AMapType,
+    YMapType,
+    pointType,
+    setMapLoaded,
+    setZoom,
+    setActiveMap,
+    setShowMask,
+    initData
+  };
+});
+
+export default useMapStore;

+ 45 - 20
src/utils/olMap/olMap.ts

@@ -188,7 +188,7 @@ export class olMap {
                   })
                 })
             );
-            options.onMarkerClick(extData);
+            options.onMarkerClick(extData, true);
           }
         } else if (extData.type === '3') {
           // 聚合要素
@@ -270,9 +270,7 @@ export class olMap {
         const xmlDoc = parser.parseFromString(res.data, 'text/xml');
         const featureType = xmlDoc.getElementsByTagName('FeatureType')[0];
         const layerParam = {
-          layerName: featureType.getElementsByTagName('Title')[0].textContent,
-          typeName: featureType.getElementsByTagName('Name')[0].textContent,
-          srsName: featureType.getElementsByTagName('SRS')[0].textContent
+          typeName: featureType.getElementsByTagName('Name')[0].textContent
         };
         this.createWfsLayer(options, layerParam);
       }
@@ -308,7 +306,6 @@ export class olMap {
         // 添加自定义 Headers
         const headers = globalHeaders();
         xhr.setRequestHeader('Authorization', headers.Authorization);
-        xhr.setRequestHeader('clientid', headers.clientid);
 
         xhr.responseType = 'arraybuffer'; // 确保支持图片二进制数据
         xhr.onload = function () {
@@ -340,22 +337,45 @@ export class olMap {
   createWfsLayer(options, layerParam) {
     const source = new VectorSource({
       format: new GeoJSON(),
-      url: `${commonUrl}${options.code}?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&typeName=流域:GEO_BAS_POLYGON&outputFormat=application/json& srsName=EPSG:4490`
+      loader: function (extent, resolution, projection) {
+        // 构建带认证头的请求参数
+        const url =
+            `${commonUrl}${options.code}?` +
+            `REQUEST=GetFeature` +
+            `&typeName=${encodeURIComponent(layerParam.typeName)}` +
+            `&outputFormat=application/json`;
+        const headers = globalHeaders();
+        fetch(url, {
+          headers: {
+            'Authorization': headers.Authorization
+          }
+        })
+            .then((response) => {
+              if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
+              return response.json();
+            })
+            .then((json) => {
+              const features = new GeoJSON().readFeatures(json, {
+                dataProjection: layerParam.srsName,
+                featureProjection: projection
+              });
+              this.addFeatures(features);
+            })
+            .catch(error => console.error('WFS加载失败:', error));
+      }
     });
 
     const vectorLayer = new VectorLayer({
       source: source,
       style: new Style({
-        fill: new Fill({
-          color: 'rgba(255, 255, 255, 0.6)'
-        }),
-        stroke: new Stroke({
-          color: '#319FD3',
-          width: 1
-        })
-      })
+        // 必须设置样式才能显示
+        fill: new Fill({ color: 'rgba(0,0,0, 0)' }),
+        stroke: new Stroke({ color: 'rgba(0,0,0, 1)', width: 1 })
+      }),
+      zIndex: options.zIndex ? options.zIndex : -99
     });
-
+    vectorLayer.set('layerName', options.layer);
+    vectorLayer.set('id', options.code);
     this.map.addLayer(vectorLayer);
   }
   // 加载json图层
@@ -376,6 +396,8 @@ export class olMap {
         }),
         zIndex: layer.zIndex ? layer.zIndex : -99
       });
+      jsonLayer.set('layerName', layer.layer);
+      jsonLayer.set('id', layer.code);
       this.map.addLayer(jsonLayer);
       resolve({});
     });
@@ -601,6 +623,7 @@ export class olMap {
       }
     };
     const handleTouchMove = (e) => {
+      e.preventDefault(); // 阻止默认滚动
       if (!this.drawing) return;
       this.path.push(e.coordinate);
       // 移除旧的线段
@@ -650,17 +673,19 @@ export class olMap {
     const layers = this.map.getLayers();
     const layerArray = layers.getArray().slice();
     layerArray.forEach((layer) => {
-      // 标注现在都是用同一个暂不移除'annotation'
-      if (!!layer && ['map'].includes(layer.get('layerName'))) {
+      if (!!layer && ['map', 'annotation'].includes(layer.get('layerName'))) {
         layer.getSource().clear();
         layer.dispose();
         this.map.removeLayer(layer);
       }
     });
-
     if (Array.isArray(newLayers)) {
       for (const layer of newLayers) {
-        await this.formatXml(layer);
+        if (layer.layerType === 'JSON') {
+          await this.createJsonLayer(layer);
+        } else {
+          await this.formatXml(layer);
+        }
       }
     } else if (newLayers.id === 'tianditu') {
       await this.formatXml2();
@@ -757,7 +782,7 @@ export class olMap {
     }
     this.addMarker2({ 'search': item });
     this.clickMarker = item;
-    this.options.onMarkerClick(item);
+    this.options.onMarkerClick(item, true);
   }
   addMarker(points) {
     this.clearMarker();

+ 0 - 1
src/views/mobileControl/EventBox.vue

@@ -38,7 +38,6 @@ import EventInfo from "./EventInfo.vue";
 import { ref } from "vue";
 import TaskCommand from "./TaskCommand.vue";
 import Briefing from "./Briefing.vue";
-import PositionSelect from "@/views/mobileControl/PositionSelect.vue";
 const emits = defineEmits(["confirm"]);
 const getDetail = () => {
   emits("confirm");

+ 17 - 9
src/views/mobileControl/LayerBox.vue

@@ -11,8 +11,8 @@
     <div class="popup-content">
       <div class="layer-box">
         <div v-for="(item, index) in layers" :key="index" class="layer-item" @click="selectItem(item.key)">
-          <div :class="activeMap === item.key ? 'item-bg bg-active ' + item.key : 'item-bg ' + item.key" />
-          <div :class="activeMap === item.key ? 'item-text text-active' : 'item-text'">{{ item.name }}</div>
+          <div :class="mapStore.activeMap === item.key ? 'item-bg bg-active ' + item.key : 'item-bg ' + item.key" />
+          <div :class="mapStore.activeMap === item.key ? 'item-text text-active' : 'item-text'">{{ item.name }}</div>
         </div>
       </div>
     </div>
@@ -20,13 +20,13 @@
 </template>
 
 <script lang="ts" setup>
-import {computed, reactive} from "vue";
+import useMapStore from '@/store/modules/map';
 
 const props = defineProps({
   modelValue: Boolean,
-  activeMap: String
 });
-const emits = defineEmits(['update:modelValue', 'update:activeMap']);
+const emits = defineEmits(['update:modelValue']);
+const mapStore = useMapStore();
 let show = computed({
   get() {
     return props.modelValue;
@@ -36,16 +36,20 @@ let show = computed({
   }
 });
 let layers = reactive([
-  { name: '卫星地图', key: 'satellite' },
-  { name: '矢量地图', key: 'vectorgraph' }
+  { name: '卫星图', key: 'satellite' },
+  { name: '业务图', key: 'vectorgraph' },
+  { name: '地形图', key: 'satellite3' },
+  { name: '流域图', key: 'satellite2' },
+  { name: '影像图', key: 'imageMap' },
+  { name: '简版图', key: 'imageMap2' }
 ])
 const handleClose = () => {
   emits('update:modelValue', false);
 };
 const selectItem = (key) => {
-  if (props.activeMap === key) return;
+  if (mapStore.activeMap === key) return;
+  mapStore.setActiveMap(key);
   emits('update:modelValue', false);
-  emits('update:activeMap', key);
 };
 </script>
 
@@ -128,6 +132,10 @@ const selectItem = (key) => {
       background: url('@/assets/images/map/satellite2.png') no-repeat;
       background-size: 100% 100%;
     }
+    .imageMap2 {
+      background: url('@/assets/images/map/satellite2.png') no-repeat;
+      background-size: 100% 100%;
+    }
     .satellite2 {
       background: url('@/assets/images/map/satellite2.png') no-repeat;
       background-size: 100% 100%;

+ 13 - 8
src/views/mobileControl/MaterialManage.vue

@@ -70,6 +70,7 @@ import {computed, inject, onMounted, reactive, ref} from "vue";
 import searchImg from "@/assets/images/search.png";
 import closeImg from "@/assets/images/close.png";
 import {getRescueMateriaWarehouseList, getRescueUnitsList} from "@/api/globalMap";
+import {getImageUrl} from "@/components/Map/mapData";
 
 const emits = defineEmits(['update:modelValue']);
 const props = defineProps({
@@ -171,13 +172,17 @@ const getList2 = () => {
 const handleClickItem = (item) => {
   item.checked = !item.checked;
   let points = [];
+  const dotIcon1 = getImageUrl('common.png');
+  const dotIcon2 = getImageUrl('common_hover.png');
+  const dotIcon3 = getImageUrl('28_emergencytransportresources.png');
+  const dotIcon4 = getImageUrl('28_emergencytransportresources_hover.png');
   dataList1.value.forEach((item2) => {
     if (!!item2.checked) {
       points.push({
-        icon: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png',
-        image: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png',
-        imageHover: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png',
-        size: [19, 31],
+        icon: dotIcon1,
+        image: dotIcon1,
+        imageHover: dotIcon2,
+        size: [40, 44],
         lnglat: [item2.longitude, item2.latitude],
         longitude: item2.longitude,
         latitude: item2.latitude
@@ -187,10 +192,10 @@ const handleClickItem = (item) => {
   dataList2.value.forEach((item2) => {
     if (!!item2.checked) {
       points.push({
-        icon: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png',
-        image: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png',
-        imageHover: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png',
-        size: [19, 31],
+        icon: dotIcon3,
+        image: dotIcon3,
+        imageHover: dotIcon4,
+        size: [60, 60],
         lnglat: [item2.longitude, item2.latitude],
         longitude: item2.longitude,
         latitude: item2.latitude

+ 198 - 90
src/views/mobileControl/OnlinePlotting/index.vue

@@ -45,6 +45,9 @@ import {deepClone} from "@/utils";
 import {useHistory} from "@/hooks/useHistory";
 import { ElColorPicker } from 'element-plus';
 import TextEdit from "./TextEdit.vue";
+import useMapStore from "@/store/modules/map";
+import Style from "ol/style/Style";
+import Icon from "ol/style/Icon";
 
 const emits = defineEmits(['update:modelValue', 'update:onlinePlottingId', 'handleSendData']);
 const getMapUtils = inject('getMapUtils');
@@ -55,6 +58,7 @@ const props = defineProps({
   modelValue: Boolean,
   onlinePlottingId: String
 });
+const mapStore = useMapStore();
 let drawing = ref(false);
 const mouseToolState = ref({
   color: 'rgba(248, 1, 2, 1)',
@@ -248,7 +252,6 @@ const menu = ref([
   }
 ]);
 
-
 // 点击三级菜单
 const clickTab = (item, index) => {
   const type = item.value;
@@ -295,7 +298,11 @@ const clickTab = (item, index) => {
 };
 const handleClickMap = (e) => {
   // 获取点击位置的经纬度
-  lnglat.value = [e.lnglat.lng, e.lnglat.lat];
+  if (mapStore.isAMap) {
+    lnglat.value = [e.lnglat.lng, e.lnglat.lat];
+  } else {
+    lnglat.value = e.coordinate;
+  }
   showTextEdit.value = true;
 };
 const handleTextEdit = () => {
@@ -329,15 +336,23 @@ const addText = (textEditState) => {
   res.text.on('rightclick', handleRightClick);
   const map = drawTool.getMap();
   // 监听地图点击事件
-  map.off('click', handleClickMap);
+  if (mapStore.isAMap) {
+    map.off('click', handleClickMap);
+  } else {
+    map.un('click', handleClickMap);
+  }
   close();
   sendWebSocket(data);
 };
 watch(() => props.onlinePlottingId, () => {
   if (props.onlinePlottingId !== null) {
     const map = getMap();
-    if (!!map) {
+    if (!map) return;
+    // 监听地图点击事件
+    if (mapStore.isAMap) {
       map.off('click', handleClickMap);
+    } else {
+      map.un('click', handleClickMap);
     }
     close();
   }
@@ -353,80 +368,161 @@ const close = () => {
 const initDrawMethod = (options) => {
   const drawTool = getDrawTool();
   const mouseTool = drawTool.getMouseTool();
-
-  const onDraw = (event) => {
-    mouseTool.off('draw', onDraw);
-    map.setStatus({
-      showIndoorMap: true,
-      dragEnable: true,
-      keyboardEnable: true,
-      doubleClickZoom: true,
-      zoomEnable: true,
-      rotateEnable: true
-    });
-    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.lnglat = [position.lng, position.lat];
-      data.latitude = position.lat;
-      data.longitude = position.lng;
-      data.icon = data.iconName;
-      data.image = data.iconName;
-      data.imageHover = data.iconName;
-      data.visible = true;
-      obj.setLabel({
-        content: '<div>' + data.title + '</div>',
-        direction: 'top'
-      });
-    } else {
-      const path = obj.getPath();
-      // 将AMap.LngLat对象数组转换为经纬度数组
-      const pathArr = path.map((lngLat) => {
-        // 返回经度和纬度的数组
-        return [lngLat.lng, lngLat.lat];
+  let onDraw;
+  if (mapStore.isAMap) {
+    onDraw = (event) => {
+      const map = getMap();
+      mouseTool.off('draw', onDraw);
+      map.setStatus({
+        showIndoorMap: true,
+        dragEnable: true,
+        keyboardEnable: true,
+        doubleClickZoom: true,
+        zoomEnable: true,
+        rotateEnable: true
       });
-      if (options.type !== 'straightLine') {
-        pathArr.push(pathArr[0]);
+      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.lnglat = [position.lng, position.lat];
+        data.latitude = position.lat;
+        data.longitude = position.lng;
+        data.icon = data.iconName;
+        data.image = data.iconName;
+        data.imageHover = data.iconName;
+        data.visible = true;
+        obj.setLabel({
+          content: '<div>' + data.title + '</div>',
+          direction: 'top'
+        });
+      } 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() / 100000;
+        }
       }
-      data.path = pathArr;
-      if (options.type == 'circle') {
-        data.center = [obj.getCenter().lng, obj.getCenter().lat];
-        data.radius = obj.getRadius() / 100000;
+      if (options.type == 'measureArea') {
+        const AMap = getDrawTool().getAMap();
+        const map = getMap();
+        // 计算区域面积
+        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);
+      sendWebSocket(data)
+    };
+    mouseTool.on('draw', onDraw);
+  } else {
+    onDraw = (event) => {
+      mouseTool.un('draw', onDraw);
+      close();
+      const feature = event.feature;
+      const geometry = feature.getGeometry();
+      const id = nanoid();
+      feature.set('id', id);
+      const data: any = deepClone(options);
+      data.id = id;
+      if (options.type == 'marker') {
+        const img = new Image();
+        img.onload = () => {
+          // 图片加载完成后,可以访问其 width 和 height 属性
+          const width = img.width;
+          const height = img.height;
+          const style = new Style({
+            image: new Icon({
+              src: options.icon,
+              anchor: [0.5, 0.5], // 图标锚点,通常是图标的中心点
+              anchorXUnits: 'fraction',
+              anchorYUnits: 'fraction',
+              size: [width, height],
+              scale: !!options.size[0] ? options.size[0] / width : 1
+            })
+            // text: new Text({
+            //   text: data.title, // 使用属性中的值作为文本
+            //   font: '14px sans-serif',
+            //   fill: new Fill({
+            //     color: '#ffff'
+            //   })
+            // })
+          });
+          feature.setStyle(style);
+        };
+        const position = geometry.getCoordinates();
+        data.lnglat = position;
+        data.longitude = position[0];
+        data.latitude = position[1];
+        data.icon = data.iconName;
+        data.image = data.iconName;
+        data.imageHover = data.iconName;
+        data.visible = true;
+        img.src = options.icon; // 设置图片的 URL,触发加载
+      } else {
+        if (options.type == 'circle') {
+          data.center = geometry.getCenter();
+          data.radius = geometry.getRadius();
+        } else {
+          const coordinates = geometry.getCoordinates();
+          if (options.type !== 'straightLine') {
+            data.path = coordinates[0];
+          } else {
+            data.path = coordinates;
+          }
+        }
+      }
+      if (options.type == 'measureArea') {
+        // 计算区域面积
+        const area = turf.area(turf.polygon([data.path]));
+        data.area = area;
+        overlays.push(feature);
+        overlaysData.push(data);
+        commit(deepClone(overlaysData));
+      } else {
+        overlays.push(feature);
+        overlaysData.push(data);
+        commit(deepClone(overlaysData));
+        console.log(overlaysData);
+      }
+      // 右击进入编辑
+      feature.on('rightclick', handleRightClick);
+      // 发送
+      sendWebSocket(data);
+    };
+    if (!!mouseTool) {
+      mouseTool.on('drawend', onDraw);
     }
-    if (options.type == 'measureArea') {
-      const AMap = getDrawTool().getAMap();
-      const map = getMap();
-      // 计算区域面积
-      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);
-    sendWebSocket(data)
-  };
-  mouseTool.on('draw', onDraw);
+  }
 };
 const handleEndDraw = (options, obj) => {
   const drawTool = getDrawTool();
@@ -435,23 +531,35 @@ const handleEndDraw = (options, obj) => {
   mouseToolState.value.graphicsType = '';
   drawTool.setDrawEndMethod();
   const id = nanoid();
-  obj._opts.extData = {
-    id: id
-  };
-  const data: any = deepClone(options);
-  data.id = id;
-  const path = obj.getPath();
-  // 将AMap.LngLat对象数组转换为经纬度数组
-  const pathArr = path.map((lngLat) => {
-    // 返回经度和纬度的数组
-    return [lngLat.lng, lngLat.lat];
-  });
-  pathArr.push(pathArr[0]);
-  data.path = pathArr;
-  if (data.type === 'circle') {
-    data.center = [obj.getCenter().lng, obj.getCenter().lat];
-    data.radius = obj.getRadius() / 100000;
+  let data;
+  if (mapStore.isAMap) {
+    obj._opts.extData = {
+      id: id
+    };
+    data = deepClone(options);
+    data.id = id;
+    const path = obj.getPath();
+    // 将AMap.LngLat对象数组转换为经纬度数组
+    const pathArr = path.map((lngLat) => {
+      // 返回经度和纬度的数组
+      return [lngLat.lng, lngLat.lat];
+    });
+    pathArr.push(pathArr[0]);
+    data.path = pathArr;
+    if (data.type === 'circle') {
+      data.center = [obj.getCenter().lng, obj.getCenter().lat];
+      data.radius = obj.getRadius() / 100000;
+    }
+  } else {
+    obj.set('id', id);
+    data = deepClone(options);
+    data.id = id;
+    if (data.type === 'anyLine') {
+      const path = obj.getGeometry().getCoordinates();
+      data.path = path;
+    }
   }
+
   overlays.push(obj);
   overlaysData.push(data);
   commit(deepClone(overlaysData));

+ 46 - 34
src/views/mobileControl/index.vue

@@ -1,19 +1,8 @@
 <template>
   <div class="container">
     <div class="top-content">
-      <!--    <YztMap v-if="['satellite2', 'satellite3'].includes(activeMap)" ref="map2Ref" :active-map="activeMap" :point-type="pointType" />-->
-      <Map
-        v-if="!loading"
-        ref="mapRef"
-        :active-map="activeMap"
-        :point-type="pointType"
-        :event-details="eventDetails"
-        :class="
-          showMenu && !!eventId && !temp && !fullscreen
-            ? 'containerHeight1'
-            : 'containerHeight2'
-        "
-      />
+      <Map v-if="mapStore.isAMap" ref="mapRef" :class="showMenu && !!eventId && !temp && !fullscreen? 'containerHeight1': 'containerHeight2'" />
+      <YztMap v-else-if="!!mapStore.activeMap" ref="map2Ref"  :class="showMenu && !!eventId && !temp && !fullscreen? 'containerHeight1': 'containerHeight2'" />
       <div v-show="!fullscreen" class="top-left-panel">
         <div class="select" @click="showSwitch = true">
           {{ eventDetails.event_title }}
@@ -130,7 +119,7 @@
       />
     </van-popup>
     <!--图层切换-->
-    <LayerBox v-model="showLayer" v-model:active-map="activeMap" />
+    <LayerBox v-model="showLayer" />
     <!--协同标绘-->
     <van-popup v-model:show="showOnlinePlotting" round position="bottom">
       <van-picker
@@ -152,12 +141,12 @@
 </template>
 
 <script lang="ts" setup name="mobileControl">
-import { nextTick, onMounted, provide, reactive, ref } from "vue";
 import { useRoute, useRouter } from "vue-router";
+import YztMap from '@/components/Map/YztMap/index.vue';
 import EventBox from "./EventBox.vue";
 import SearchBtn from "./SearchBtn.vue";
 import { deepClone } from "@/utils";
-import { iconList } from "@/components/Map/mapData";
+import { iconList, getImageUrl } from "@/components/Map/mapData";
 import PositionSelect from "./PositionSelect.vue";
 import { editEvent, getListActive, launchPlan } from "@/api/duty/eventing";
 import { showFailToast, showSuccessToast } from "vant";
@@ -167,10 +156,16 @@ import OnlinePlotting from "./OnlinePlotting/index.vue";
 import MaterialManage from "@/views/mobileControl/MaterialManage.vue";
 import { createWebSocket } from "@/utils/websocket";
 import { getPatternList2 } from "@/api/globalMap/onlinePlotting";
-import { closeCollaboration } from "@/api/onlineRollCall";
+import useMapStore from '@/store/modules/map';
+import gcoord from 'gcoord';
 
 const router = useRouter();
 const route = useRoute();
+const mapStore = useMapStore();
+// 地图
+let map: any = {};
+// 地图类
+let mapUtils: any = {};
 let loading = ref(true);
 let eventId = ref("");
 let selectEventId = ref([]);
@@ -339,6 +334,7 @@ const onConfirm = data => {
       };
     });
   };
+  showOnlinePlotting2.value = true;
   showOnlinePlotting.value = false;
   // showOnlinePlotting2.value = true;
 };
@@ -365,6 +361,7 @@ const handleShowMaterialManage = () => {
   materialManageHeight.value = 300;
 };
 onMounted(() => {
+  mapStore.initData();
   eventId.value = route.query.event_id as string;
   selectEventId.value = [route.query.event_id as string];
   getDetail();
@@ -388,31 +385,46 @@ const getDetail = () => {
 };
 // 获取地图元素操作
 const getMap = () => {
-  if (["imageMap", "satellite2", "satellite3"].includes(activeMap.value)) {
-    return map2Ref.value.getMap();
-  } else if (["vectorgraph", "satellite"].includes(activeMap.value)) {
-    return mapRef.value.getMap();
-  }
-  return {};
+  const domRef = mapStore.isAMap ? mapRef.value : map2Ref.value;
+  return !!domRef ? domRef.getMap() : {};
 };
 const getDrawTool = () => {
-  if (["imageMap", "satellite2", "satellite3"].includes(activeMap.value)) {
-    return map2Ref.value.drawTool;
-  } else if (["vectorgraph", "satellite"].includes(activeMap.value)) {
-    return mapRef.value.drawTool;
-  }
-  return {};
+  return mapStore.isAMap ? mapRef.value.drawTool : mapUtils;
 };
 const getMapUtils = () => {
-  if (["imageMap", "satellite2", "satellite3"].includes(activeMap.value)) {
-  } else if (["vectorgraph", "satellite"].includes(activeMap.value)) {
-    return mapRef.value.getMapUtils();
-  }
-  return {};
+  const domRef = mapStore.isAMap ? mapRef.value : map2Ref.value;
+  return !!domRef ? domRef.getMapUtils() : {};
 };
 const handleSendData = data => {
   webSock.send(data);
 };
+watch(
+    () => mapStore.mapLoaded,
+    (loaded) => {
+      if (loaded) {
+        map = getMap();
+        mapUtils = getMapUtils();
+        if (eventId.value) {
+          getEventDetail({ event_id: eventId.value }).then((res) => {
+            const lnglat = gcoord.transform([res.data.longitude, res.data.latitude], gcoord.GCJ02, gcoord.WGS84);
+            const newMap = mapStore.isAMap ? map : map.getView();
+            newMap.setCenter(lnglat);
+            mapUtils.setAddress({
+              longitude: lnglat[0],
+              latitude: lnglat[1],
+              image: getImageUrl('address.png'),
+              imageHover: getImageUrl('address_hover.png'),
+              size: [45, 48],
+              name: '灾害地点'
+            });
+          });
+        }
+      }
+    },
+    {
+      immediate: true
+    }
+);
 provide("getMapUtils", getMapUtils);
 provide("getMap", getMap);
 provide("getDrawTool", getDrawTool);