Skip to content

openlayer库完整代码

OpenlayerBaseMap

OpenlayerBaseMap/menuUtils.js

js
/**
 * 维护右键菜单栏
 */

import { ElMessage } from 'element-plus'
import * as mapUtils from "./mapUtils.js";
import mittBus from "@/utils/mittBus"; // mitt
import { copyTextToClipboard } from "@/utils/index.js";

// 菜单项
const menuMethodBtn = {
    // 公共选项,常驻菜单
    commonMenu: {
        // commonMenuMethod0: '地图功能测试',  // 公共选项0
        // commonMenuMethod01: '添加自定义标注点',  // 公共选项01
        // commonMenuMethod02: '添加自定义闪烁点',  // 公共选项02
        commonMenuMethod1: '拷贝当前经纬度',  // 公共选项1
        // commonMenuMethod2: '置顶要素',  // 公共选项2
        commonMenuMethod3: '切换天地图token',  // 公共选项3
    },
    // 公共动态选项,每个页面有需要才显示
    commonDynamicMenu: {
        commonDynamicMenuMethod1: '清除所有要素',  // 公共选项3
        commonDynamicMenuMethod2: '显示当前要素信息',  // 公共动态选项1
        commonDynamicMenuMethod3: '仅取消绘制状态',  // 公共动态选项3
        commonDynamicMenuMethod4: '展示分析结果',  // 公共动态选项4
        commonDynamicMenuMethod5: '删除所有绘制内容',  // 公共动态选项5
    },
    // 子页动态选项, 单页面显示或单页面有需要显示
    singleMenu: {
        singleMenuMethod1: '刷新地图',  // 子页动态选项1
        singleMenuMethod2: '清除POI点',  // 子页动态选项2
        // singleMenuMethod2: '展示分析结果'  // 子页动态选项2
    },
}

if (import.meta.env.VITE_APP_ENV === 'development') {
    menuMethodBtn.commonMenu = {
        ...menuMethodBtn.commonMenu,
        commonMenuMethod0: '地图功能测试',  // 公共选项0
        commonMenuMethod01: '添加自定义标注点',  // 公共选项01
        commonMenuMethod02: '添加自定义闪烁点',  // 公共选项02
    }
}

// 主菜单项
const { commonMenu } = menuMethodBtn  // 公共选项,常驻菜单
let commonMenuMethodsArr = []  // 常驻菜单数组
// 将对象中的值传入数组
for (let i in commonMenu) {
    commonMenuMethodsArr.push(commonMenu[i])
}

export default {
    // 菜单项
    menuMethodBtn,

    // 常驻菜单数组
    commonMenuMethodsArr,

    // 设置鼠标右键菜单栏方法
    setMenuMethods(olMap, option, feature, pixelPoint, proxy) {
        // console.log(olMap, option, feature, pixelPoint, proxy)

        // 经纬度
        let currentLonlat = mapUtils.transformToLonlat(pixelPoint)

        /**
         * 菜单栏
         * commonMenu  // 公共选项,常驻菜单
         * commonDynamicMenu  // 公共动态选项,每个页面有需要才显示
         * singleMenu  // 子页动态选项, 单页面显示或单页面有需要显示
         */
        const { commonMenu, commonDynamicMenu, singleMenu } = this.menuMethodBtn;

        switch (option) {
            /**
             * ===========================
             *      公共选项,常驻菜单
             * ===========================
             */
            // 地图功能测试
            case commonMenu.commonMenuMethod0:
                // console.log("test", commonMenu.commonMenuMethod0);

                mapUtils.olMapTestCommon(olMap, feature, pixelPoint);
                break;
            // 添加标注点
            case commonMenu.commonMenuMethod01:
                // console.log("test", commonMenu.commonMenuMethod1);

                mapUtils.addMyPointByMenu(olMap, pixelPoint);
                break;
            // 清空自定义标注点
            case '清空自定义标注点':
                // console.log("test", '清空自定义标注点');

                // 根据条件移除要素
                mapUtils.removeByCondition(olMap, currentFeature => {
                    return currentFeature.get('tempType') === 'myPointByMenu'
                })
                break;
            // 添加闪烁点
            case commonMenu.commonMenuMethod02:
                // console.log("test", commonMenu.commonMenuMethod2);

                mapUtils.addFlickerPoint(olMap, pixelPoint);
                break;
            // 清空自定义闪烁点
            case '清空自定义闪烁点':
                // console.log("test", '清空自定义闪烁点');

                let flickerPointDom = document.querySelectorAll('.flicker_point')

                flickerPointDom.forEach(item => {
                    item.classList.remove('flicker_point')
                })

                break;
            // 拷贝当前经纬度
            case commonMenu.commonMenuMethod1:
                // console.log("拷贝当前经纬度");
                copyTextToClipboard(`[${currentLonlat}]`, () => {
                    ElMessage.success(`[${currentLonlat}] 拷贝成功`);
                });
                break;
            // 置顶图层
            case commonMenu.commonMenuMethod2:
                // console.log("置顶图层");
                mapUtils.featureToMaxTop(olMap, feature);
                break;
            // 切换天地图token
            case commonMenu.commonMenuMethod3:
                // console.log("切换天地图token");

                mittBus.emit("showSetTokenDialog");
                break;
            /**
             * =======================================
             *      公共动态选项,每个页面有需要才显示
             * =======================================
             */
            // 清除所有要素
            case commonDynamicMenu.commonDynamicMenuMethod1:
                // console.log("清除所有要素");

                mapUtils.removeAllLayer(olMap);
                break;
            // 显示当前要素信息
            case commonDynamicMenu.commonDynamicMenuMethod2:
                // console.log("显示当前要素信息");
                mittBus.emit("singleFeaturesClick", { feature, pixelPoint });
                break;
            // 仅取消绘制状态
            case commonDynamicMenu.commonDynamicMenuMethod3:
                // console.log("仅取消绘制状态");

                mapUtils.cancelDrawInteraction(olMap)
                break;
            // 展示分析结果
            case commonDynamicMenu.commonDynamicMenuMethod4:
                let currentData = feature.get('pointData')
                // console.log("展示分析结果", feature.get('pointData'));
                mittBus.emit("analysisPointData", currentData);
                break;
            // 删除所有绘制内容
            case commonDynamicMenu.commonDynamicMenuMethod5:
                console.log("删除所有绘制内容");

                // 根据条件移除要素
                mapUtils.removeByCondition(olMap, currentFeature => {
                    return currentFeature.get('drawType')
                })

                // let currentData = feature.get('pointData')

                // mittBus.emit("analysisPointData", currentData);
                break;
            /**
             * =============================================
             *      子页动态选项, 单页面显示或单页面有需要显示
             * =============================================
             */
            // 刷新地图
            case singleMenu.singleMenuMethod1:
                // console.log("刷新地图");
                mittBus.emit("reflashMap");
                break;
            // 清除POI点
            case singleMenu.singleMenuMethod2:
                // console.log("清除POI点");
                mapUtils.removeLayerByBusinessType(olMap, "POIMarker"); // 根据类型移除图层
                break;
        }
    },

    // 根据具体页面配置菜单栏
    setMentBtnExtendByPage(currentPageType) {
        const { singleMenu } = this.menuMethodBtn

        switch (currentPageType) {
            case 'gis':
                mapUtils.menuAddSingleMethod(singleMenu.singleMenuMethod1)
                mapUtils.menuAddSingleMethod(singleMenu.singleMenuMethod2)
                break;
        }
    }
}
/**
 * 维护右键菜单栏
 */

import { ElMessage } from 'element-plus'
import * as mapUtils from "./mapUtils.js";
import mittBus from "@/utils/mittBus"; // mitt
import { copyTextToClipboard } from "@/utils/index.js";

// 菜单项
const menuMethodBtn = {
    // 公共选项,常驻菜单
    commonMenu: {
        // commonMenuMethod0: '地图功能测试',  // 公共选项0
        // commonMenuMethod01: '添加自定义标注点',  // 公共选项01
        // commonMenuMethod02: '添加自定义闪烁点',  // 公共选项02
        commonMenuMethod1: '拷贝当前经纬度',  // 公共选项1
        // commonMenuMethod2: '置顶要素',  // 公共选项2
        commonMenuMethod3: '切换天地图token',  // 公共选项3
    },
    // 公共动态选项,每个页面有需要才显示
    commonDynamicMenu: {
        commonDynamicMenuMethod1: '清除所有要素',  // 公共选项3
        commonDynamicMenuMethod2: '显示当前要素信息',  // 公共动态选项1
        commonDynamicMenuMethod3: '仅取消绘制状态',  // 公共动态选项3
        commonDynamicMenuMethod4: '展示分析结果',  // 公共动态选项4
        commonDynamicMenuMethod5: '删除所有绘制内容',  // 公共动态选项5
    },
    // 子页动态选项, 单页面显示或单页面有需要显示
    singleMenu: {
        singleMenuMethod1: '刷新地图',  // 子页动态选项1
        singleMenuMethod2: '清除POI点',  // 子页动态选项2
        // singleMenuMethod2: '展示分析结果'  // 子页动态选项2
    },
}

if (import.meta.env.VITE_APP_ENV === 'development') {
    menuMethodBtn.commonMenu = {
        ...menuMethodBtn.commonMenu,
        commonMenuMethod0: '地图功能测试',  // 公共选项0
        commonMenuMethod01: '添加自定义标注点',  // 公共选项01
        commonMenuMethod02: '添加自定义闪烁点',  // 公共选项02
    }
}

// 主菜单项
const { commonMenu } = menuMethodBtn  // 公共选项,常驻菜单
let commonMenuMethodsArr = []  // 常驻菜单数组
// 将对象中的值传入数组
for (let i in commonMenu) {
    commonMenuMethodsArr.push(commonMenu[i])
}

export default {
    // 菜单项
    menuMethodBtn,

    // 常驻菜单数组
    commonMenuMethodsArr,

    // 设置鼠标右键菜单栏方法
    setMenuMethods(olMap, option, feature, pixelPoint, proxy) {
        // console.log(olMap, option, feature, pixelPoint, proxy)

        // 经纬度
        let currentLonlat = mapUtils.transformToLonlat(pixelPoint)

        /**
         * 菜单栏
         * commonMenu  // 公共选项,常驻菜单
         * commonDynamicMenu  // 公共动态选项,每个页面有需要才显示
         * singleMenu  // 子页动态选项, 单页面显示或单页面有需要显示
         */
        const { commonMenu, commonDynamicMenu, singleMenu } = this.menuMethodBtn;

        switch (option) {
            /**
             * ===========================
             *      公共选项,常驻菜单
             * ===========================
             */
            // 地图功能测试
            case commonMenu.commonMenuMethod0:
                // console.log("test", commonMenu.commonMenuMethod0);

                mapUtils.olMapTestCommon(olMap, feature, pixelPoint);
                break;
            // 添加标注点
            case commonMenu.commonMenuMethod01:
                // console.log("test", commonMenu.commonMenuMethod1);

                mapUtils.addMyPointByMenu(olMap, pixelPoint);
                break;
            // 清空自定义标注点
            case '清空自定义标注点':
                // console.log("test", '清空自定义标注点');

                // 根据条件移除要素
                mapUtils.removeByCondition(olMap, currentFeature => {
                    return currentFeature.get('tempType') === 'myPointByMenu'
                })
                break;
            // 添加闪烁点
            case commonMenu.commonMenuMethod02:
                // console.log("test", commonMenu.commonMenuMethod2);

                mapUtils.addFlickerPoint(olMap, pixelPoint);
                break;
            // 清空自定义闪烁点
            case '清空自定义闪烁点':
                // console.log("test", '清空自定义闪烁点');

                let flickerPointDom = document.querySelectorAll('.flicker_point')

                flickerPointDom.forEach(item => {
                    item.classList.remove('flicker_point')
                })

                break;
            // 拷贝当前经纬度
            case commonMenu.commonMenuMethod1:
                // console.log("拷贝当前经纬度");
                copyTextToClipboard(`[${currentLonlat}]`, () => {
                    ElMessage.success(`[${currentLonlat}] 拷贝成功`);
                });
                break;
            // 置顶图层
            case commonMenu.commonMenuMethod2:
                // console.log("置顶图层");
                mapUtils.featureToMaxTop(olMap, feature);
                break;
            // 切换天地图token
            case commonMenu.commonMenuMethod3:
                // console.log("切换天地图token");

                mittBus.emit("showSetTokenDialog");
                break;
            /**
             * =======================================
             *      公共动态选项,每个页面有需要才显示
             * =======================================
             */
            // 清除所有要素
            case commonDynamicMenu.commonDynamicMenuMethod1:
                // console.log("清除所有要素");

                mapUtils.removeAllLayer(olMap);
                break;
            // 显示当前要素信息
            case commonDynamicMenu.commonDynamicMenuMethod2:
                // console.log("显示当前要素信息");
                mittBus.emit("singleFeaturesClick", { feature, pixelPoint });
                break;
            // 仅取消绘制状态
            case commonDynamicMenu.commonDynamicMenuMethod3:
                // console.log("仅取消绘制状态");

                mapUtils.cancelDrawInteraction(olMap)
                break;
            // 展示分析结果
            case commonDynamicMenu.commonDynamicMenuMethod4:
                let currentData = feature.get('pointData')
                // console.log("展示分析结果", feature.get('pointData'));
                mittBus.emit("analysisPointData", currentData);
                break;
            // 删除所有绘制内容
            case commonDynamicMenu.commonDynamicMenuMethod5:
                console.log("删除所有绘制内容");

                // 根据条件移除要素
                mapUtils.removeByCondition(olMap, currentFeature => {
                    return currentFeature.get('drawType')
                })

                // let currentData = feature.get('pointData')

                // mittBus.emit("analysisPointData", currentData);
                break;
            /**
             * =============================================
             *      子页动态选项, 单页面显示或单页面有需要显示
             * =============================================
             */
            // 刷新地图
            case singleMenu.singleMenuMethod1:
                // console.log("刷新地图");
                mittBus.emit("reflashMap");
                break;
            // 清除POI点
            case singleMenu.singleMenuMethod2:
                // console.log("清除POI点");
                mapUtils.removeLayerByBusinessType(olMap, "POIMarker"); // 根据类型移除图层
                break;
        }
    },

    // 根据具体页面配置菜单栏
    setMentBtnExtendByPage(currentPageType) {
        const { singleMenu } = this.menuMethodBtn

        switch (currentPageType) {
            case 'gis':
                mapUtils.menuAddSingleMethod(singleMenu.singleMenuMethod1)
                mapUtils.menuAddSingleMethod(singleMenu.singleMenuMethod2)
                break;
        }
    }
}

OpenlayerBaseMap/mapUtils.js

js
// map core
import 'ol/ol.css'
import { Map, View } from 'ol';
import WebGLTile from 'ol/layer/WebGLTile'; // 瓦片
// map 加载底图相关
import { /* OSM, */ XYZ, Vector as VectorSource, Cluster } from 'ol/source';
// map 坐标相关
import { fromLonLat, transform, toLonLat } from 'ol/proj';
import { getTopLeft, getBottomRight, getCenter } from 'ol/extent';
import { toStringHDMS } from 'ol/coordinate';
// map 控件相关
import { FullScreen, Zoom, ZoomSlider, ScaleLine } from "ol/control";
// map 图层相关
import Feature from 'ol/Feature';
import { Point, Circle, Polygon, LineString } from "ol/geom";
import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer';  // VectorLayer表示珊格图层
import LinearRing from 'ol/geom/LinearRing';
import Overlay from 'ol/Overlay';  // 气泡
import { getLength } from 'ol/sphere';
// map 样式
import { Circle as CircleStyle, Fill, Stroke, Style, Text, Icon } from 'ol/style';
// kml
import { KML, GeoJSON } from 'ol/format';
// 选择多边形
import { Draw, defaults/* , Modify, Snap */ } from 'ol/interaction';
// render
import { getVectorContext } from "ol/render";
// 菜单栏
import menuUtils from './menuUtils.js'
// store
import { gisDataStore } from '@/store/modules/gis.js'

/******************************
 * 变量(非地图)
 * ****************************
 */
let count = 0  // 地图点击打点变量

/******************************
 * 地图变量(工具)
 * ****************************
 */
// 普通搜索
// https://api.tianditu.gov.cn/v2/search?postStr={%22queryType%22:13,%22start%22:0,%22count%22:5,%22specify%22:%22156110000%22,%22dataTypes%22:%22%E6%B3%95%E9%99%A2,%E5%85%AC%E5%9B%AD%22}&type=query&tk=02dd5ea16a6b869b3b37e12f269b1463
// 周边搜索
// https://api.tianditu.gov.cn/v2/search?postStr={%22keyWord%22:%22%E5%85%AC%E5%9B%AD%22,%22level%22:12,%22queryRadius%22:5000,%22pointLonlat%22:%22121.6262019920349,29.879795341283085%22,%22queryType%22:3,%22start%22:0,%22count%22:10}&type=query&tk=02dd5ea16a6b869b3b37e12f269b1463

let tdtTk = import.meta.env.VITE_APP_MapToken  // 全局配置 - 天地图密钥
const gisStoreData = gisDataStore()

// 设置底图url
const setLayerUrl = (url, hasToken = true) => {
  if (gisStoreData.mapToken !== '') {
    tdtTk = gisStoreData.mapToken
  }
  if (hasToken) {
    return url + tdtTk
  }
  return url
}

// 创建底图基础配置
const createBaseLayerConfig = (url, layerSourceConfig = {}, layerConfig = {}) => {
  return new WebGLTile({
    source: new XYZ({
      url,
      ...layerSourceConfig
    }),
    type: 'baseLayer',
    url,
    ...layerConfig
  })
}

// 配置地图底图
const setBaseMapLayer = (url) => {
  return createBaseLayerConfig(url, {
    wrapX: false,
    crossOrigin: "anonymous",
  }, { layerType: 'baseMapLayer' })
}

// 配置地图注记
const setBaseMapTxt = (url) => {
  return createBaseLayerConfig(url, {}, { layerType: 'baseMapTxt' })
}

/**
 * 天地图底图配置
    天地图图层类型
    vec: 矢量底图
    cva: 矢量注记图层
    eva: 矢量注记图层-英文注记
    img: 影像底图
    cia: 影像注记图层
    eia: 影像注记图层 -英文
    ter: 地形底图
    cta: 地形注记图层
 */
const baseLayerUrlConfig = {
  // 天地图底图
  getBaseMapLayer(item) {
    switch (item) {
      case 't3imgPrivatization':
        return setLayerUrl("/wtms/googlemaps/satellite/{z}/{y}/{x}.png", false)
      case 't0vec':
        return setLayerUrl("http://t0.tianditu.com/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=")  // 街道底图
      case 't3img':
        return setLayerUrl("http://t3.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=")  // 卫星(影像)底图
      case 't4ter':
        return setLayerUrl("http://t4.tianditu.com/DataServer?T=ter_w&x={x}&y={y}&l={z}&tk=")  // 地形底图
      case 't07vec':
        return setLayerUrl("http://t{0-7}.tianditu.com/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=")  // 街道底图2
      case 't07img':
        return setLayerUrl("http://t{0-7}.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=")  // 卫星底图2
    }
  },
  getBaseMapTxt(item) {
    switch (item) {
      case 'empty':
        return setLayerUrl('', false)  // 空注记
      case 't0cva': // 街道图注记
        return setLayerUrl("http://t0.tianditu.com/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=")  // 街道图注记
      case 't4cva': // 地形图注记
        return setLayerUrl("http://t4.tianditu.com/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=")  // 地形图注记
      case 't07cia': // 卫星图注记
        return setLayerUrl("http://t{0-7}.tianditu.com/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=")  // 卫星图注记
      case 't07cva': // 卫星图注记
        return setLayerUrl("http://t{0-7}.tianditu.com/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=")  // 卫星图注记
    }
  }
}

export const minRenderZoom = 15
// 地图初始化配置
const mapInitConfig = {
  // ol地图底图 - 默认街道底图
  layers: [
    // 天地图底图
    setBaseMapLayer(baseLayerUrlConfig.getBaseMapLayer('t3imgPrivatization')),  // 私有化底图
    // 天地图注记
    setBaseMapTxt(baseLayerUrlConfig.getBaseMapTxt('empty')),
  ],
  // ol地图基本配置 - View默认使用EPSG3857坐标系
  view: new View({
    center: fromLonLat([121.63, 29.88]),
    zoom: 16,
    maxZoom: 17,
    minZoom: 13,
    constrainResolution: true,  // 设置缩放级别为整数 
    smoothResolutionConstraint: false,  // 关闭无级缩放地图
  }),
}

/******************************
 * 地图核心辅助方法
 * ****************************
 */
// 公共动态选项判断
const setCommonMenuMethod = (condition, commonDynamicMenuMethod) => {
  if (condition) {
    if (condition instanceof Array) {
      if (condition.length !== 0) {
        // console.log(condition.get('tempType'))

        menuAddSingleMethod(commonDynamicMenuMethod)
      }

      if (condition.length === 0) {
        menuUtils.commonMenuMethodsArr = menuUtils.commonMenuMethodsArr.filter(item => item !== commonDynamicMenuMethod)
      }
    } else {
      if (condition instanceof Feature) {
        if (!condition.get('tempType')) {
          menuAddSingleMethod(commonDynamicMenuMethod)
        }
      } else {
        menuAddSingleMethod(commonDynamicMenuMethod)

        if (condition.length === 0) {
          menuUtils.commonMenuMethodsArr = menuUtils.commonMenuMethodsArr.filter(item => item !== commonDynamicMenuMethod)
        }
      }
    }
  }
  if (!condition && menuUtils.commonMenuMethodsArr.includes(commonDynamicMenuMethod)) {
    menuUtils.commonMenuMethodsArr = menuUtils.commonMenuMethodsArr.filter(item => item !== commonDynamicMenuMethod)
  }
}

/**
 * 标注点样式
 */
const pointCircleStyle = new Style({
  image: new CircleStyle({
    radius: 5,
    fill: new Fill({
      color: '#f49d41'
    }),
    stroke: new Stroke({
      color: '#836365',
      width: 1
    })
  }),
})
const pointIconleStyle = (olMap, src) => {
  return new Style({
    image: new Icon({
      src,
      // image: new CircleStyle({
      anchor: [.8, 80],//图标的锚点,经纬度点所对应的图标的位置,默认是[0.5, 0.5],即为标注图标的中心点位置
      anchorOrigin: 'top-right',//锚点的偏移位置,默认是top-left,
      anchorXUnits: 'fraction',//锚点X的单位,默认为百分比,也可以使用px
      anchorYUnits: 'pixels',//锚点Y的单位,默认为百分比,也可以使用px
      offsetOrigin: 'top-left',//原点偏移bottom-left, bottom-right, top-left, top-right,默认 top-left
      size: [100, 100],
      offset: [3, -32],
      //图标缩放比例
      // scale: 0.5,//可以设置该比例实现,图标跟随地图层级缩放
      scale: olMap.getView().getZoom() / 30,
      //透明度
      opacity: 0.75,//如果想隐藏某个图标,可以单独设置该值,透明度为0时,即可隐藏,此为隐藏元素的方法之一。
    }),
  })
}

/**
 * 绘制扇形核心方法
 * APIMethod:OpenLayers绘制扇形的接口扩展
 * @param origin 圆心
 * @param radius 半径
 * @param sides 边数
 * @param r 弧度
 * @param angel 旋转角度(扇形右边半径与x正向轴的角度)
 * @returns {OpenLayers.Geometry.Polygon}
 */
const createRegularPolygonCurve = (origin, radius, sides, r, angel) => {
  let rotation = 360 - r;
  let angle = Math.PI * ((1 / sides) - (1 / 2));
  if (rotation) {
    angle += (rotation / 180) * Math.PI;
  }
  let rotatedAngle, x, y;
  let points = [];
  for (let i = 0; i < sides; ++i) {
    let an = i * ((360 - rotation) / 360);
    rotatedAngle = angle + (an * 2 * Math.PI / sides);
    x = origin[0] + (radius * Math.cos(rotatedAngle));
    y = origin[1] + (radius * Math.sin(rotatedAngle));
    points.push([x, y]);
  }
  if (rotation != 0) {
    points.push(origin);
  }
  let ring = new LinearRing(points);
  ring.rotate(angel / 57.3, origin);
  let list = ring.getCoordinates()

  return new Polygon([list]);
}

// 移除地图Overlay元素
const removeAllOverlay = () => {
  let flickerPointDom = document.querySelectorAll('.flicker_point')

  flickerPointDom.forEach(item => {
    item.classList.remove('flicker_point')
  })
}

/******************************
 * 地图核心方法供出
 * ****************************
 */
export const destroyMap = (olMap) => {
  if (olMap) {
    // 销毁所有图层
    olMap.getLayers().forEach(function (layer) {
      layer.setMap(null);
    });

    // 销毁视图
    olMap.setView(null);

    // 销毁地图实例
    olMap.setTarget(null);
    olMap = null;
  }
}

// 初始化地图
export const initOlMap = (target) => {
  return new Map({
    target,
    layers: mapInitConfig.layers,
    view: mapInitConfig.view,
    interactions: defaults({ mouseWheelZoom: true })  // 禁止缩放
  });
}

// 切换地图底图
export const switchBaseLayer = (olMap, type) => {
  let txtType = ''

  switch (type) {
    // 私有化底图
    case 't3imgPrivatization':
      txtType = 'empty'  // 卫星图注记
      break
    // 街道底图
    case 't0vec':
      txtType = 't0cva'  // 街道图注记
      break
    // 卫星(影像)底图
    case 't3img':
      txtType = 't07cia'  // 卫星图注记
      break
    // 地形底图
    case 't4ter':
      txtType = 't4cva'  // 地形图注记`
      break
  }

  // 天地图底图
  let newBaseMapLayer = setBaseMapLayer(baseLayerUrlConfig.getBaseMapLayer(type))
  // 天地图注记
  let newBaseMapTxt = setBaseMapTxt(baseLayerUrlConfig.getBaseMapTxt(txtType))

  console.log('当前天地图token:', tdtTk)
  console.log('当前天地图底图地址:', newBaseMapLayer.values_.url)
  console.log('当前天地图注记地址:', newBaseMapTxt.values_.url)

  // 获取当前地图中的所有图层
  const mapLayers = olMap.getLayers();

  olMap.getAllLayers().forEach((item, index) => {
    if (item.get('type') === 'baseLayer') {
      switch (item.get('layerType')) {
        case 'baseMapLayer':
          olMap.removeLayer(item)
          mapLayers.insertAt(index, newBaseMapLayer)
          break
        case 'baseMapTxt':
          if (newBaseMapTxt !== '') {
            olMap.removeLayer(item)
            mapLayers.insertAt(index, newBaseMapTxt)
          }

          if (newBaseMapTxt === '') {
            let newBaseMapTxtTmp = setBaseMapTxt('')
            olMap.removeLayer(item)
            mapLayers.insertAt(index, newBaseMapTxtTmp)
          }

          break
      }
    }
  })
}

// 获取可视区域的左上角和右下角坐标
export const getCurrentViewPosition = (olMap) => {
  const extent = olMap.getView().calculateExtent(olMap.getSize());

  // 获取投影坐标系
  const topLeftCoord = getTopLeft(extent);  // 左上角
  const bottomRightCoord = getBottomRight(extent);  // 右下角
  // console.log(topLeftCoord, bottomRightCoord)

  // 将投影坐标转换为地理坐标
  const topLeft = transform(topLeftCoord, 'EPSG:3857', 'EPSG:4326');
  const bottomRight = transform(bottomRightCoord, 'EPSG:3857', 'EPSG:4326');
  // console.log(topLeft, bottomRight)

  return {
    topLeft,
    bottomRight,
  }
}

// 初始化所有控件
export const resetControls = (olMap) => {
  olMap.getControls().clear()

  // 重新添加控件(如果需要)
  // olMap.addControl(new FullScreen());  // 全屏
  olMap.addControl(new Zoom());  // 缩放
  olMap.addControl(new ZoomSlider());  // 缩放
  olMap.addControl(new ScaleLine());  // 比例尺


  // olMap.addControl(new OverviewMap());  // 鹰眼
}

// 判断menu是否存在,不存在新增
export const menuAddSingleMethod = (singleMethod) => {
  if (!menuUtils.commonMenuMethodsArr.includes(singleMethod)) {
    menuUtils.commonMenuMethodsArr.push(singleMethod)
  }
}

// 重置右键属性菜单
export const resetContextMenu = (next) => {
  let mapWrap = document.querySelector('.ol_map_wrap')
  let menu = document.querySelector('.menu_wrap')

  // 判断是否存在menu
  if (menu) {
    mapWrap.removeChild(menu);
  }

  if (next) {
    next(mapWrap)
  }
}

// 设置鼠标右键属性
export const setContextmenu = (olMap, next, setMenuConfig) => {
  const { commonDynamicMenu, singleMenu } = menuUtils.menuMethodBtn  // 公共动态选项,每个页面有需要才显示

  // 添加右键菜单监听
  olMap.getViewport().addEventListener('contextmenu', e => {
    // console.log(transformToLonlat(olMap.getEventCoordinate(e)))  // 经纬度

    e.preventDefault(); // 阻止默认的右键菜单弹出

    /**
     * 子页动态选项
     */
    // 根据具体页面配置菜单栏
    if (setMenuConfig) {
      setMenuConfig(currentPageType => {
        // console.log('当前页面', currentPageType)

        // 根据具体页面配置菜单栏 - 子菜单在某些情况可能需要隐藏
        menuUtils.setMentBtnExtendByPage(currentPageType)
      })
    }

    // 屏幕坐标
    const pixelPoint = olMap.getEventCoordinate(e)
    const pixel = olMap.getPixelFromCoordinate(pixelPoint)
    const feature = olMap.forEachFeatureAtPixel(pixel, feature => {
      return feature
    })


    /**
     * 获取所有layer并做判断
     */
    let featureOnPage = []  // 页面所有的feature
    let myPointByMenuFeature = []  // 所有自定义定位点
    let POIPointByMenuFeature = []  // 所有POI定位点
    let drawTypeByMenuFeature = []  // 所有绘制内容
    getAllLayer(olMap, layerItem => {
      let currentFeature = layerItem.getSource().getFeatures()[0]

      if (!currentFeature) {
        return
      }

      featureOnPage.push(currentFeature)

      if (currentFeature.get('tempType') === 'myPointByMenu') {
        myPointByMenuFeature.push(currentFeature)
      }
      if (currentFeature.get('businessType') === 'POIMarker') {
        POIPointByMenuFeature.push(currentFeature)
      }
      if (currentFeature.get('drawType')) {
        drawTypeByMenuFeature.push(currentFeature)
      }
    })

    /**
     * 公共动态选项
     */
    // 判断是否显示清除所有要素
    setCommonMenuMethod(featureOnPage, commonDynamicMenu.commonDynamicMenuMethod1)

    // 判断是否显示当前要素
    setCommonMenuMethod(feature, commonDynamicMenu.commonDynamicMenuMethod2)

    // 判断是否需要显示停止绘制
    // 获取绘制的图形
    const drawInteraction = getDrawInteraction(olMap)
    setCommonMenuMethod(drawInteraction, commonDynamicMenu.commonDynamicMenuMethod3)

    // 自定义定位点
    setCommonMenuMethod(myPointByMenuFeature, '清空自定义标注点')

    // 自定义闪烁点
    let flickerPointDom = document.querySelectorAll('.flicker_point')
    setCommonMenuMethod(flickerPointDom, '清空自定义闪烁点')

    // 展示分析结果
    if (feature) {
      if (feature.get('pointData')?.isNeedAnalysis) {
        setCommonMenuMethod(feature, commonDynamicMenu.commonDynamicMenuMethod4)
      }
    }

    // 删除所有绘制内容  
    setCommonMenuMethod(drawTypeByMenuFeature, commonDynamicMenu.commonDynamicMenuMethod5)

    /**
     * 子页动态选项
     */
    // 清除POI点
    setCommonMenuMethod(POIPointByMenuFeature, singleMenu.singleMenuMethod2)


    // 重置右键属性菜单
    resetContextMenu(mapWrap => {
      let menu = null

      menu = document.createElement('div');
      menu.setAttribute("class", "menu_wrap");

      // 自定义菜单项
      let tempStr = ''
      menuUtils.commonMenuMethodsArr.forEach(item => {
        tempStr += `<li>${item}</li>`
      })

      menu.innerHTML = `
      <ul>
        ${tempStr}
      </ul>
    `;

      // 添加到页面上
      menu.style.position = 'fixed';
      menu.style.left = `${e.clientX}px`;
      menu.style.top = `${e.clientY}px`;
      mapWrap.appendChild(menu);

      // 监听菜单项的点击事件(可选)
      menu.addEventListener('click', (evt) => {
        const option = evt.target.textContent;
        next({ option, feature, pixelPoint })

        // 清理菜单
        mapWrap.removeChild(menu);
      });
    })
  });
}

// 获取当前绘制状态
export const getDrawInteraction = (olMap) => {
  // 获取绘制的图形
  return olMap.getInteractions().getArray().find(
    (interaction) => interaction instanceof Draw
  );
}

// 取消绘制(点线面)
export const cancelDrawInteraction = (olMap) => {
  // console.log('取消绘制(点线面)', olMap)

  // 获取绘制的图形
  const drawInteraction = getDrawInteraction(olMap)

  olMap.removeInteraction(drawInteraction); // 从地图中移除交互
}

// 投影坐标转换
export const transformToLonlat = (coordinate) => {
  return transform(coordinate, "EPSG:3857", "EPSG:4326")
}

// 屏幕坐标转换
export const transformToPixelPoint = (lon, lat) => {
  return fromLonLat([lon, lat])
}

// 获取经纬度
export const getHdms = (pixelPoint) => {
  return toStringHDMS(toLonLat(pixelPoint)); // 转换为经纬度显示
}

// 获取多边形中心点
export const getPolygonCenter = (feature) => {
  const geometry = feature.getGeometry();
  if (geometry.getType() === "Polygon") {
    const polygon = geometry.clone();
    polygon.transform("EPSG:3857", "EPSG:4326"); // 如果多边形在Web墨卡托坐标系中,需要转换到WGS84
    const extent = polygon.getExtent();
    const center = getCenter(extent);
    return center;
  }
  return null;
}

// 设置标注点
export const addPoint = (olMap, pointBusinessData, src = '/', pointConfig = {}, zIndex = 1) => {
  // 创建点的数据源
  const vectorSource = new VectorSource({
    features: [],
  });

  // 创建点图层
  const vectorLayer = new VectorLayer({
    source: vectorSource,
    zIndex,
    style: src === '/' ? pointCircleStyle : pointIconleStyle(olMap, src)
  });

  olMap.addLayer(vectorLayer);

  const featureConfig = (point, pointData) => {
    return {
      geometry: point,
      type: 'Marker',
      pointData,
      ...pointConfig,
    }
  }

  if (pointBusinessData instanceof Array) {
    pointBusinessData.forEach((item) => {
      const point = new Point(fromLonLat(item.lonlat));
      const feature = new Feature({
        ...featureConfig(point, item.pointData)
      });
      vectorSource.addFeature(feature);
    });
  } else {
    const point = new Point(fromLonLat(pointBusinessData.lonlat));
    const feature = new Feature({
      ...featureConfig(point, pointBusinessData.pointData)
    });
    vectorSource.addFeature(feature);
  }
}

// 点聚合
export const setCluster = (olMap, clusterBussinessData, src) => {
  // removeAllDefaultLayer(olMap); // 移除所有默认图层

  let source = new VectorSource();

  if (clusterBussinessData instanceof Array) {
    clusterBussinessData.forEach(item => {
      let coordinates = fromLonLat([item.longitude, item.latitude]);
      let feature = new Feature({
        geometry: new Point(coordinates),
        type: 'Marker',
        pointData: item
      });
      source.addFeature(feature);
    })
  } else {
    let coordinates = fromLonLat([clusterBussinessData.longitude, clusterBussinessData.latitude]);
    let feature = new Feature({
      geometry: new Point(coordinates),
      type: 'Marker',
      pointData: clusterBussinessData
    });
    source.addFeature(feature);
  }

  // 聚合
  let cluster = new Cluster({
    source: source,
    distance: 50
  })

  // 创建图层
  let clusterLayer = new VectorLayer({
    type: 'clusterLayer',
    source: cluster,
    style: function (feature) {
      feature.set('type', 'Cluster')
      let size = feature.get('features').length;

      // console.log(feature.get('features'))

      if (size === 1) {
        return src === '/' ? pointCircleStyle : pointIconleStyle(olMap, src)
      }
      else {
        return new Style({
          image: new CircleStyle({
            radius: 30,
            stroke: new Stroke({
              color: 'white'
            }),
            fill: new Fill({
              color: 'blue'
            })
          }),
          text: new Text({
            text: size.toString(),
            fill: new Fill({
              color: 'white'
            })
          })
        })
      }
    }
  });

  olMap.addLayer(clusterLayer);
}

// 添加线
export const addLine = (olMap, position, lineConfig = {}, style) => {
  // 创建线要素并添加到地图上
  const lineFeature = new Feature({
    // geometry: new LineString([[13538079.386677982, 3488521.2319548605], [13540229.178098504, 3488093.6623278903]]),
    geometry: new LineString(position),
    type: 'Line',
    ...lineConfig
  });

  if (!style) {
    // style = setFeaturesStyle('rgba(255, 255, 255, 0.2)', 'rgba(0, 0, 0, 0.5)', false, 3)
    /* style = new Style({
      fill: new Fill({
        color: 'rgba(255, 255, 255, 0.2)'
      }),
      stroke: new Stroke({
        color: 'rgba(0, 0, 0, 0.5)',
        width: 3
      }),
    }) */
  }

  lineFeature.setStyle(style);
  // lineFeature.setStyle(styleFunction);

  const vectorSource = new VectorSource({
    features: [lineFeature],
  });

  const vectorLayer = new VectorLayer({
    source: vectorSource,
    zIndex: 9
  });

  olMap.addLayer(vectorLayer);
}

// 根据feature获取layer
export const getLayerByFeature = (map = {}, feature = {}) => {
  let layers = map.getLayers().getArray();
  for (let i in layers) {
    let source = layers[i].getSource();
    if (source instanceof VectorSource) {
      let features = source.getFeatures();
      if (features.length > 0) {
        for (let j in features) {
          if (features[j] === feature) {
            return layers[i];
          }
        }
      }
    }
  }
  return {};
}

// 设置Features样式
export const setFeaturesStyle = (fillColor, strokeColor, isImg = false, width = 2, radius = 5) => {
  return new Style(!isImg ? {
    fill: new Fill({
      color: fillColor
    }),
    stroke: new Stroke({
      color: strokeColor,
      width
    }),
  } : {
    image: new CircleStyle({
      radius,
      fill: new Fill({
        color: fillColor,
      }),
      stroke: new Stroke({
        color: strokeColor,
        width,
      }),
    }),
  })
}

// 绘制区域样式
const setDrawFeaturesStyle = () => {
  return setFeaturesStyle("rgba(32, 157, 230, 0.2)", "rgb(64 158 255)")
}

// 给某个feature置顶
export const featureToMaxTop = (olMap, feature) => {
  // console.log('给某个feature置顶', olMap, feature)

  let currentStyle = null

  switch (feature.get('type')) {
    case 'Marker':
      currentStyle = setFeaturesStyle('#409eff', '#f00', true, 2, 10)
      break
    case 'Curve':
      currentStyle = setFeaturesStyle('#409eff', '#f00', false, 5)
      break
  }

  feature.setStyle(currentStyle)

  feature.set('temp', true)

  // 顶层图层
  let topLayer = new VectorLayer({
    source: new VectorSource(),
    // style: [selectPointStyle],
    zIndex: 999 // zIndex全地图最大
  });

  /* getAllLayer(olMap, layerItem => {
    let currentFeature = layerItem.getSource().getFeatures()[0]

    if (currentFeature.get('temp')) {
      olMap.removeLayer(layerItem)
    }
  }) */
  // 根据条件移除要素
  removeByCondition(olMap, currentFeature => {
    return currentFeature.get('temp')
  })

  topLayer.setOpacity(1)
  topLayer.getSource().addFeature(feature);

  olMap.addLayer(topLayer)
}

// 创建文字图层
export const addTextPoint = (olMap, text, position, textPointConfig = {}, isRemove) => {
  // console.log('创建文字图层', olMap, gridData)

  // 创建文本样式
  const textStyle = new Style({
    text: new Text({
      text,
      fill: new Fill({ color: '#333333' }),
      // stroke: new Stroke({ color: '#fff', width: 2 }),
      font: '18px pingfang',
      textAlign: 'center', // 文本对齐
      textBaseline: 'bottom', // 文本基线
      padding: [5, 10, 5, 10], // 文本周围的填充
      offsetX: -20,
      offsetY: -13,
      overflow: true, // 允许文本溢出
      rotateWithView: false, // 不随地图旋转
      rotation: 0, // 文本旋转角度
      // scale: olMap.getView().getZoom() / 30,
    }),
  });

  if (isRemove) {
    // 根据条件移除要素
    removeByCondition(olMap, currentFeature => {
      return isRemove(currentFeature)
    })
  }

  // 创建文本特征
  const feature = new Feature({
    geometry: new Point(fromLonLat(position)),
    type: 'textPoint',
    ...textPointConfig
  });

  // 设置特征的样式
  feature.setStyle(textStyle);

  // 创建文本图层
  const vectorLayer = new VectorLayer({
    source: new VectorSource({
      features: [feature],
    }),
    zIndex: 9
  });

  // 将文本图层添加到地图
  olMap.addLayer(vectorLayer);
}

/**
 * 气泡窗通用方法
 * @param {*} olMap 地图对象
 * @param {*} pixelPoint 屏幕坐标
 * @param {*} popupData 气泡窗数据
 * @param {*} next 事件方法
 * @param {*} overlayConfig 气泡窗配置 
 */
export const popupCommonConfig = (olMap, pixelPoint, popupInner, next, overlayConfig = null,) => {
  let container = document.getElementById('popup');
  // console.log('container', container)
  let closer = document.getElementById('popup-closer');
  // let content = document.getElementById('popup-content');
  container.style.display = 'block'

  /* let overlayContainer = document.querySelector('.ol-overlay-container')
  if (overlayContainer) {
    overlayContainer.remove()
  } */

  let overlay = new Overlay({
    element: container, //绑定 Overlay 对象和 DOM 对象的
    ...overlayConfig,
    zIndex: 9999,
  });
  olMap.addOverlay(overlay);

  closer.onclick = () => {
    overlay.setPosition(undefined);
    closer.blur();
    overlay = null;
    return false;
  };

  // console.log(popupInner)
  // content.innerHTML = popupData;  // 使用jsx,不直接进行inner
  overlay.setPosition(pixelPoint); //把 overlay 显示到指定的 x,y坐标

  // 使用addEventListener会无限叠加事件,且不好使用removeEventListener移除(匿名函数)
  overlay.getElement().onclick = e => {
    next(e)
  }
}

/**
 * 添加扇形
 * 绘制扇形核心方法
 * APIMethod:OpenLayers绘制扇形的接口扩展
 * @param origin 圆心
 * @param radius 半径
 * @param sides 边数
 * @param r 弧度
 * @param angel 旋转角度(扇形右边半径与x正向轴的角度)
 * @returns {OpenLayers.Geometry.Polygon}
 */
// 根据频段展示不同颜色 有边缘  
export const addCurve = (olMap, curveDataList, isResetStyle) => {
  let featureList = []  // 扇区feature列表

  // 根据业务数据修改feature数据
  curveDataList.forEach(item => {
    // 频率
    // console.log(item.curveData.workFrequency)

    // 扇区样式
    let curveStyle = setFeaturesStyle(
      "rgba(32, 222, 230, 0.4)",
      'rgba(255, 205, 67, 0.3)'
    )
    // 扇区半径
    let curveRadius = 100

    if (isResetStyle) {
      let { myCurveStyle, myCurveRadius } = isResetStyle(item)

      curveStyle = myCurveStyle
      curveRadius = myCurveRadius
    }

    let origi_point = fromLonLat(item.lonlat);  // 绘制扇形的顶点
    // let circle = createRegularPolygonCurve(origi_point, 150, 100, 45, 90) // 调用绘制扇形的方法得到扇形
    let circle = createRegularPolygonCurve(origi_point, curveRadius, 100, 45, item.curveData.antDirectionAngle) // 调用绘制扇形的方法得到扇形
    let feature = new Feature(circle);  // 把扇形加入 feature

    feature.setStyle(curveStyle)
    feature.set('type', 'Curve')  // 这是给这个扇形添加额外的参数 , 如果是设置id 用 setId方法
    // 这是给这个扇形添加额外的参数,这里的id和 setId的id没关系
    feature.set('curveData', item.curveData)
    featureList.push(feature)
  })

  let vectorSource = new VectorSource();  // 创建一个数据源
  vectorSource.addFeatures(featureList);   // 把两个扇形加进数据源
  let vectorLayer = new VectorLayer({     // 创建一个图层,把数据源加进图层
    source: vectorSource,
    zIndex: 1
  });
  olMap.addLayer(vectorLayer);   // 把图层加进地图
}

/**
 * 设置气泡窗
 * @param {*} olMap 地图对象
 * @param {*} pixelPoint 屏幕坐标
 * @param {*} popupData 气泡窗数据
 * @param {*} next 事件处理方法
 */
export const setPopup = (olMap, pixelPoint, popupInner, next) => {
  // const pixelPoint = e.coordinate  // 屏幕坐标
  popupCommonConfig(olMap, pixelPoint, popupInner, next, {
    autoPan: true, // 定义弹出窗口在边缘点击时候可能不完整 设置自动平移效果
    /* autoPanAnimation: {
        duration: 250, // 自动平移效果的动画时间 9毫秒)
    }, */
  })
}

// 自动弹出气泡窗
export const setAutoPopup = (pixelPoint, itemData, next) => {
  // 点击尺 (这里是尺(米),并不是经纬度);
  let hdms = getHdms(pixelPoint); // 转换为经纬度显示
  // let hdms = toStringHDMS(toLonLat(pixelPoint)); // 转换为经纬度显示

  const popupObj = {
    name: '通用信息展示弹窗',
    hdms,
    coordinate: [itemData.longitude, itemData.latitude],
    popupData: itemData  // 气泡窗业务数据
  }

  next(popupObj)
}

// 获取所有图层
export const getAllLayer = (olMap, next) => {
  // 获取当前地图上的所有图层
  let layers = olMap.getLayers().getArray();

  // 获取所有图层(从地图中移除所有图层)
  for (let i = layers.length - 1; i >= 0; --i) {
    if (layers[i] instanceof VectorLayer) {
      next(layers[i])
    };
  }
}

// 移除所有图层
export const removeAllLayer = (olMap) => {
  removeAllOverlay()  // 移除地图Overlay元素

  cancelDrawInteraction(olMap)  // 取消绘制(点线面)

  getAllLayer(olMap, layerItem => {
    olMap.removeLayer(layerItem)
  })
}

// 根据条件获取FeatureList
export const getFeaturesByCondition = (olMap, condition) => {
  let featureList = []
  getAllLayer(olMap, layerItem => {
    let currentFeature = layerItem.getSource().getFeatures()[0]

    if (condition(currentFeature)) {
      featureList.push(currentFeature)
    }
  })

  return featureList
}

// 根据条件移除要素
export const removeByCondition = (olMap, condition) => {
  getAllLayer(olMap, layerItem => {
    let currentFeature = layerItem.getSource().getFeatures()[0]

    if (condition(currentFeature)) {
      olMap.removeLayer(layerItem)
    }
  })
}

// 根据类型移除图层
export const removeLayerByType = (olMap, type) => {
  removeByCondition(olMap, currentFeature => {
    if (currentFeature) {
      return currentFeature.get('type') === type
    }
  })
}

// 根据业务类型移除图层
export const removeLayerByBusinessType = (olMap, type) => {
  removeByCondition(olMap, currentFeature => {
    if (currentFeature) {
      return currentFeature.get('businessType') === type
    }
  })
}

// 移除所有默认图层
export const removeAllDefaultLayer = (olMap) => {
  removeByCondition(olMap, currentFeature => {
    if (currentFeature) {
      return (currentFeature.get('type') === 'Marker' || currentFeature.get('type') === 'Curve' || currentFeature.get('type') === 'Cluster') && (!currentFeature.get('bussinessType'))
    }
  })
}

// 刷新地图需要移除的元素
export const removeByReflashMap = (olMap) => {
  // 根据条件移除要素
  removeByCondition(olMap, currentFeature => {
    if (currentFeature) {
      return currentFeature.get('tempType')
    }

  })

  removeAllOverlay()  // 移除地图Overlay元素
}

// 飞到指定坐标
export const flyToCoordinate = (olMap, lonlat) => {
  olMap.getView().animate({
    center: fromLonLat(lonlat),
    duration: 800, // 飞行时间,单位毫秒
  });
}

// 获取所有feature
export const getAllFeature = (olMap, next) => {
  olMap.getLayers().forEach(item => {
    let source = null;
    if (item) {
      source = item.getSource();
    }
    if (source instanceof VectorSource) {
      source.forEachFeature(feature => {
        // console.log(feature.get('type'))

        next(feature)
      });
    }
  });
}

// 加载kml
export const loadKML = (olMap, text) => {
  // console.log('加载kml', olMap, text)

  const format = new KML({
    extractStyles: false //至关重要
  });
  const features = format.readFeatures(text);
  features.forEach(item => {
    // 从EPSG:4326转换到EPSG:3857
    item.getGeometry().transform('EPSG:4326', 'EPSG:3857')
  })
  const vectorSource = new VectorSource({
    features: features,
  });
  olMap.getView().fit(vectorSource.getExtent());
  olMap.getLayers().push(
    new VectorLayer({
      source: vectorSource,
      style: setFeaturesStyle('blue', 'red'),
      zIndex: 100
    })
  );
}

// 创建多边形(选区)
export const drawPolygon = (olMap) => {
  // console.log('创建多边形(选区)', olMap)

  olMap.addInteraction(new Draw({
    source: new VectorSource(),
    type: 'Polygon',
  }));

  // 获取绘制完成的多边形
  olMap.getInteractions().getArray().forEach(interaction => {
    if (interaction instanceof Draw) {
      interaction.on('drawend', (event) => {
        const { feature } = event
        const geometry = feature.getGeometry();
        const coords = geometry.getCoordinates()[0]
        const lonlat = geometry.transform('EPSG:3857', 'EPSG:4326').getCoordinates()[0]
        // console.log(lonlat, coords)
        if (geometry instanceof Polygon) {
          // console.log("所选点位坐标", geometry.getCoordinates());

          createPolygon(olMap, { coords, lonlat }, { drawType: 'polygon' })

          olMap.removeInteraction(interaction); // 从地图中移除交互
        }
      });
    }
  });
}

/**
 * 绘制多边形
 * 
 * @param {*} olMap 
 * @param {*} coords 多边形的坐标数组
 */
export const createPolygon = (olMap, { coords, lonlat, polygonData }, polygonConfig = {}, next, polygonStyle = setDrawFeaturesStyle()) => {
  // console.log(olMap, coords, lonlat)

  // 创建多边形
  let polygon = new Feature({
    geometry: new Polygon([coords]),
    type: 'Polygon',
    ...polygonConfig,
    polygonData: {
      coords,
      lonlat,
      ...polygonData
    }
  });

  // 设置多边形样式
  polygon.setStyle(polygonStyle)

  // 创建矢量图层并添加多边形
  let vectorLayer = new VectorLayer({
    source: new VectorSource({
      features: [polygon]
    })
  });

  olMap.addLayer(vectorLayer);

  if (next) {
    next(polygon)
  }
}

// 取消绘制多边形(取消选区)
export const cancelPolygon = (olMap) => {
  console.log('取消绘制多边形(取消选区)', olMap)

  // cancelDrawInteraction(olMap)
}

// 创建圆形(选区)
export const drawCircle = (olMap) => {
  // console.log('创建圆形(选区)', olMap)

  olMap.addInteraction(new Draw({
    source: new VectorSource(),
    type: 'Circle',
  }));

  // 获取绘制完成的多边形
  olMap.getInteractions().getArray().forEach(interaction => {
    if (interaction instanceof Draw) {
      interaction.on('drawend', (event) => {
        const { feature } = event
        const geometry = feature.getGeometry();
        const lonlat = transform(geometry.getCenter(), 'EPSG:3857', 'EPSG:4326')
        // console.log(geometry, geometry.getRadius(), lonlat)

        let circleItem = {
          lonlat,
          radius: geometry.getRadius()
        }
        addCircle(olMap, circleItem, { drawType: 'Circle' })

        olMap.removeInteraction(interaction); // 从地图中移除交互
      });
    }
  });
}

// 绘制圆
export const addCircle = (olMap, circleItem, circleConfig = {}, isFlicker, isHide = false) => {
  if (!circleItem.radius) {
    circleItem.radius = 550
  }

  let features = []

  let feature = new Feature({
    type: "Circle",
    ...circleConfig,
    circleData: circleItem,
    // 圆心 - 半径
    geometry: new Circle(fromLonLat(circleItem.lonlat), circleItem.radius),
  })

  feature.setStyle(!isHide ? setDrawFeaturesStyle() : new Style({
    fill: new Fill({
      color: 'rgba(255, 0, 0, 0)'
    }),
    stroke: new Stroke({
      color: '#f00',
      color: 'rgba(255, 0, 0, 0)'
    }),
  }))

  features.push(feature)
  let source = new VectorSource()
  source.addFeatures(features)
  let layer = new VectorLayer({
    // opacity: 0.2,
    zIndex: 100,
  })
  layer.setSource(source)
  olMap.addLayer(layer)

  // 需要闪烁时调用
  if (isFlicker) {
    let radius = 0
    layer.on('postrender', evt => {
      if (radius >= 20) radius = 0;
      var opacity = (20 - radius) * (1 / 20); // 不透明度
      var pointStyle = new Style({
        radius: radius,
        stroke: new Stroke({
          color: "rgba(255,60,5" + opacity + ")",
          width: 18 - radius / 1.5, // 设置宽度
        }),
      });
      // 获取矢量要素上下文
      let vectorContext = getVectorContext(evt);
      vectorContext.setStyle(pointStyle);
      vectorContext.drawGeometry(feature.getGeometry());
      radius = radius + 0.2; //调整闪烁速度
      //请求地图渲染(在下一个动画帧处)
      olMap.render();
    })
  }
}

// 闪烁点图层函数
const flashPoints = (map, layer, record) => {
  if (JSON.stringify(record) === '{}') return;
  //清除上一次的闪烁图层
  if (layer.current) {
    map.removeLayer(layer.current);
  }
  //wgs84togcj02是自己封装的转换经纬度的函数,不需要的话可忽略
  // const lnglat = wgs84togcj02(record?.longitude, record?.latitude);
  //下面这段代码是先画一个普通的点位图层
  const features = [
    new Feature({
      geometry: new Point(fromLonLat(record)),
    }),
  ];
  layer.current = new VectorLayer({
    source: new VectorSource({ features }),
  });
  layer.current.setStyle(
    new Style({
      image: new CircleStyle({
        radius: 100,
        snapToPixel: false,
        stroke: new Stroke({
          color: 'red',
          size: 10,
        }),
      }),
    })
  );
  //这里是闪烁动画实现的关键
  var radius = 550;
  map.on('postcompose', function () {
    //0.6是每次扩大的幅度
    radius = radius + 0.6;
    //30是最大半径
    radius = radius % 30;
    layer.current.setStyle(
      new Style({
        image: new CircleStyle({
          radius,
          snapToPixel: false,
          stroke: new Stroke({
            color: 'red',
            width: 3,
            size: 10,
          }),
        }),
      })
    );
  });
  map.addLayer(layer.current);
};

// 绘制圆2
export const addCircle2 = (olMap, circleItem, circleConfig = {}, isFlicker = true) => {
  console.log('circleItem', circleItem.lonlat)

  let vectorLayer = new VectorLayer({
    opacity: 0.8
  });

  flashPoints(olMap, vectorLayer, circleItem.lonlat)

}

// 测距
export const testDistance = (olMap, next) => {
  // console.log("测距", olMap)

  let measure = new Draw({
    source: new VectorSource(),
    type: 'LineString',
    /* style: new Style({
      fill: new Fill({ color: 'rgba(255, 255, 255, 0.2)' }),
      stroke: new Stroke({ color: 'rgba(0, 0, 0, 0.5)', width: 2 })
    }) */
  })
  olMap.addInteraction(measure);

  measure.on('drawend', (event) => {
    const { feature } = event
    const line = feature.getGeometry();

    let length = getLength(line);
    length = length.toFixed(2)

    const coords = line.getCoordinates()
    const lonlat = line.transform('EPSG:3857', 'EPSG:4326').getCoordinates()
    // console.log('Line length: ' + length + ' meters');

    // 如果需要外部提供数据
    if (next) {
      next(length)
    }

    // 创建线要素并添加到地图上
    addLine(olMap, coords, { drawType: 'TestDistance' })

    // 创建文本要素以显示距离
    addTextPoint(olMap, length + '米', lonlat[0], { drawType: 'TestDistance' })

    // olMap.removeInteraction(measure); // 从地图中移除交互
  });
}

// 取消测距
export const cancelTestDistance = (olMap) => {
  // console.log('取消测距', olMap)

  // 根据条件移除要素
  removeByCondition(olMap, currentFeature => {
    currentFeature.get('drawType') === 'TestDistance'
  })
}

// 添加带箭头的线
export const addArrowLine = (olMap, position, src = '../src/components/OpenlayerBaseMap/icon/arrow.svg', businessType = 'arrowLine') => {
  console.log('添加带箭头的线', olMap)

  function stylefunction(feature) {
    const geometry = feature.getGeometry()
    // 轨迹地理长度
    const totalLength = geometry.getLength()
    // 像素间隔步长
    let step = 50
    // 将像素步长转实际地理距离步长
    let StepLength = step * olMap.getView().getResolution()
    // 箭头总数
    let arrowNum = Math.floor(totalLength / StepLength)

    const styles = [
      // linestring
      new Style({
        stroke: new Stroke({
          // color: 'rgb(164 164 162 / 88%)',
          color: '#42b983',
          width: 5,
        }),
      }),
    ];

    const rotations = [];
    const distances = [0];
    geometry.forEachSegment(function (start, end) {
      let dx = end[0] - start[0];
      let dy = end[1] - start[1];
      let rotation = Math.atan2(dy, dx);
      distances.unshift(Math.sqrt(dx ** 2 + dy ** 2) + distances[0]);
      rotations.push(rotation);
    });

    for (let i = 1; i < arrowNum; i++) {
      let arrow_coor = geometry.getCoordinateAt(i * 1.0 / arrowNum)
      const d = i * StepLength;
      const grid = distances.findIndex((x) => x <= d);
      styles.push(
        new Style({
          geometry: new Point(arrow_coor),
          image: new Icon({
            src,
            opacity: 1,
            anchor: [0.5, 0.5],
            rotateWithView: true,
            rotation: - rotations[distances.length - grid - 1],
            scale: 0.8
          })
        })
      )
    }

    return styles
  }

  addLine(olMap, position, { businessType }, stylefunction)
}

// 右键添加标注点
export const addMyPointByMenu = (olMap, pixelPoint) => {
  const features = [];

  const iconFeature = new Feature({
    geometry: new Point(pixelPoint),
    name: count++,
    location: pixelPoint,
    tempType: 'myPointByMenu'
  });
  const style = new Style({
    image: new CircleStyle({
      radius: 15,
      fill: new Fill({
        color: '#409eff'
      }),
      stroke: new Stroke({
        color: '#4440ff',
        width: 1
      })
    })
  });
  iconFeature.setStyle(style);
  features.push(iconFeature);
  const vectorSource = new VectorSource({
    features
  });
  const vectorLayer = new VectorLayer({
    source: vectorSource,
    opacity: 0.8
  });
  olMap.addLayer(vectorLayer);
}

// 添加闪烁点
export const addFlickerPoint = (olMap, pixelPoint, className = '', next) => {
  // console.log('添加闪烁点', olMap, pixelPoint)

  let point_div = document.createElement('div');
  point_div.className = `flicker_point ${className}`;
  let point_overlay = new Overlay({
    element: point_div,
    position: pixelPoint,
    // positioning: 'center-center'
    zIndex: 0
  });
  olMap.addOverlay(point_overlay);

  if (next) {
    point_div.addEventListener('click', () => {
      next()
    })
  }
}

/******************************
 * 测试 
 * ****************************
 */
// 用于作单元测试
export const olMapTestCommon = (olMap, feature, pixelPoint) => {
  console.log('地图功能测试', olMap, feature, pixelPoint)
  console.log('经纬度', transformToLonlat(pixelPoint))
}

// 打点测试
export const setPointTest = (olMap) => {
  // fromLonLat([121.63, 29.88])

  const features = [];
  // console.log(e.coordinate); // 获取坐标

  const iconFeature = new Feature({
    geometry: new Point(fromLonLat([121.63, 29.88])),
    // name: count++,
    location: fromLonLat([121.63, 29.88])
  });
  const style = new Style({
    image: new CircleStyle({
      radius: 10,
      fill: new Fill({
        color: '#f49d41'
      }),
      stroke: new Stroke({
        color: '#836365',
        width: 1
      })
    })
  });
  iconFeature.setStyle(style);
  features.push(iconFeature);
  const vectorSource = new VectorSource({
    features
  });
  const vectorLayer = new VectorLayer({
    source: vectorSource,
    opacity: 0.8
  });
  olMap.addLayer(vectorLayer);
}

// 移除标注测试
export const removePointTest = (olMap) => {
  const layers = olMap.getLayers();
  layers.forEach(item => {
    if (item instanceof VectorLayer) olMap.removeLayer(item);
  });
}

// 点击打点测试
export const clickSetPointTest = (olMap, e) => {
  const features = [];
  // console.log(e.coordinate); // 获取坐标

  const iconFeature = new Feature({
    geometry: new Point(e.coordinate),
    name: count++,
    location: e.coordinate
  });
  const style = new Style({
    image: new CircleStyle({
      radius: 10,
      fill: new Fill({
        color: '#f49d41'
      }),
      stroke: new Stroke({
        color: '#836365',
        width: 1
      })
    })
  });
  iconFeature.setStyle(style);
  features.push(iconFeature);
  const vectorSource = new VectorSource({
    features
  });
  const vectorLayer = new VectorLayer({
    source: vectorSource,
    opacity: 0.8
  });
  olMap.addLayer(vectorLayer);
}

// 绘制扇形测试
export const addCurveTest = (olMap) => {
  let origi_point = fromLonLat([121.63, 29.88]);  // 绘制扇形的顶点
  let circle = createRegularPolygonCurve(origi_point, 500, 100, 30, 90) // 调用绘制扇形的方法得到扇形
  let feature = new Feature(circle);  // 把扇形加入 feature
  feature.setStyle(  // 设置一下这个扇形的样式
    new Style({
      fill: new Fill({
        color: 'rgba(32, 157, 230, 0.3)'
      }),
      stroke: new Stroke({
        color: 'rgba(255, 205, 67, 0.3)',
        width: 2
      }),
    })
  )
  feature.set('type', 'Curve')  // 这是给这个扇形添加额外的参数 , 如果是设置id 用 setId方法
  feature.set('curve', {   // 这是给这个扇形添加额外的参数,这里的id和 setId的id没关系
    id: 1,
    title: '测试001',
    msg: '测试001-1',
    msg2: '测试001-2',
  })

  // 创建第二个扇形,和第一个一样
  let circle1 = createRegularPolygonCurve(origi_point, 500, 100, 30, 45)
  let feature1 = new Feature(circle1);
  feature1.setStyle(
    new Style({
      fill: new Fill({
        color: 'rgba(32, 157, 230, 0.3)'
      }),
      stroke: new Stroke({
        color: 'rgba(255, 205, 67, 0.3)',
        width: 2
      }),
    })
  )
  feature1.set('type', 'Curve')
  feature1.set('curve', {
    id: 2,
    title: '超级无敌炫酷爆龙战神',
    msg: '超级无敌炫酷爆龙战神 描述001',
    msg2: '超级无敌炫酷爆龙战神 描述002',
  })

  let vectorSource = new VectorSource();  // 创建一个数据源
  vectorSource.addFeatures([feature, feature1]);   // 把两个扇形加进数据源
  let vectorLayer = new VectorLayer({     // 创建一个图层,把数据源加进图层
    source: vectorSource
  });
  olMap.addLayer(vectorLayer);   // 把图层加进地图
}

// 绘制圆测试
export const addCircleTest = (olMap, circleList) => {
  let features = []

  circleList.forEach((item, index) => {
    let feature = new Feature({
      type: "Circle",
      title: item.name,
      geometry: new Circle(fromLonLat(item.site), 200),
    })
    feature.setStyle(
      new Style({
        fill: new Fill({
          color: 'rgba(32, 157, 230, 1)'
        }),
      })
    )
    features.push(feature)
  })
  let source = new VectorSource()
  source.addFeatures(features)
  let layer = new VectorLayer({
    opacity: 0.2
  })
  layer.setSource(source)
  olMap.addLayer(layer)
}

// 获取所有要素测试
export const getAllFeatureTest = (olMap) => {
  let layers = olMap.getLayers().getArray();
  layers.forEach(item => {
    if (item instanceof VectorLayer) {
      let currentFeature = item.getSource().getFeatures()
      console.log(currentFeature[0].get('type'))
    }
  })
}
// map core
import 'ol/ol.css'
import { Map, View } from 'ol';
import WebGLTile from 'ol/layer/WebGLTile'; // 瓦片
// map 加载底图相关
import { /* OSM, */ XYZ, Vector as VectorSource, Cluster } from 'ol/source';
// map 坐标相关
import { fromLonLat, transform, toLonLat } from 'ol/proj';
import { getTopLeft, getBottomRight, getCenter } from 'ol/extent';
import { toStringHDMS } from 'ol/coordinate';
// map 控件相关
import { FullScreen, Zoom, ZoomSlider, ScaleLine } from "ol/control";
// map 图层相关
import Feature from 'ol/Feature';
import { Point, Circle, Polygon, LineString } from "ol/geom";
import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer';  // VectorLayer表示珊格图层
import LinearRing from 'ol/geom/LinearRing';
import Overlay from 'ol/Overlay';  // 气泡
import { getLength } from 'ol/sphere';
// map 样式
import { Circle as CircleStyle, Fill, Stroke, Style, Text, Icon } from 'ol/style';
// kml
import { KML, GeoJSON } from 'ol/format';
// 选择多边形
import { Draw, defaults/* , Modify, Snap */ } from 'ol/interaction';
// render
import { getVectorContext } from "ol/render";
// 菜单栏
import menuUtils from './menuUtils.js'
// store
import { gisDataStore } from '@/store/modules/gis.js'

/******************************
 * 变量(非地图)
 * ****************************
 */
let count = 0  // 地图点击打点变量

/******************************
 * 地图变量(工具)
 * ****************************
 */
// 普通搜索
// https://api.tianditu.gov.cn/v2/search?postStr={%22queryType%22:13,%22start%22:0,%22count%22:5,%22specify%22:%22156110000%22,%22dataTypes%22:%22%E6%B3%95%E9%99%A2,%E5%85%AC%E5%9B%AD%22}&type=query&tk=02dd5ea16a6b869b3b37e12f269b1463
// 周边搜索
// https://api.tianditu.gov.cn/v2/search?postStr={%22keyWord%22:%22%E5%85%AC%E5%9B%AD%22,%22level%22:12,%22queryRadius%22:5000,%22pointLonlat%22:%22121.6262019920349,29.879795341283085%22,%22queryType%22:3,%22start%22:0,%22count%22:10}&type=query&tk=02dd5ea16a6b869b3b37e12f269b1463

let tdtTk = import.meta.env.VITE_APP_MapToken  // 全局配置 - 天地图密钥
const gisStoreData = gisDataStore()

// 设置底图url
const setLayerUrl = (url, hasToken = true) => {
  if (gisStoreData.mapToken !== '') {
    tdtTk = gisStoreData.mapToken
  }
  if (hasToken) {
    return url + tdtTk
  }
  return url
}

// 创建底图基础配置
const createBaseLayerConfig = (url, layerSourceConfig = {}, layerConfig = {}) => {
  return new WebGLTile({
    source: new XYZ({
      url,
      ...layerSourceConfig
    }),
    type: 'baseLayer',
    url,
    ...layerConfig
  })
}

// 配置地图底图
const setBaseMapLayer = (url) => {
  return createBaseLayerConfig(url, {
    wrapX: false,
    crossOrigin: "anonymous",
  }, { layerType: 'baseMapLayer' })
}

// 配置地图注记
const setBaseMapTxt = (url) => {
  return createBaseLayerConfig(url, {}, { layerType: 'baseMapTxt' })
}

/**
 * 天地图底图配置
    天地图图层类型
    vec: 矢量底图
    cva: 矢量注记图层
    eva: 矢量注记图层-英文注记
    img: 影像底图
    cia: 影像注记图层
    eia: 影像注记图层 -英文
    ter: 地形底图
    cta: 地形注记图层
 */
const baseLayerUrlConfig = {
  // 天地图底图
  getBaseMapLayer(item) {
    switch (item) {
      case 't3imgPrivatization':
        return setLayerUrl("/wtms/googlemaps/satellite/{z}/{y}/{x}.png", false)
      case 't0vec':
        return setLayerUrl("http://t0.tianditu.com/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=")  // 街道底图
      case 't3img':
        return setLayerUrl("http://t3.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=")  // 卫星(影像)底图
      case 't4ter':
        return setLayerUrl("http://t4.tianditu.com/DataServer?T=ter_w&x={x}&y={y}&l={z}&tk=")  // 地形底图
      case 't07vec':
        return setLayerUrl("http://t{0-7}.tianditu.com/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=")  // 街道底图2
      case 't07img':
        return setLayerUrl("http://t{0-7}.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=")  // 卫星底图2
    }
  },
  getBaseMapTxt(item) {
    switch (item) {
      case 'empty':
        return setLayerUrl('', false)  // 空注记
      case 't0cva': // 街道图注记
        return setLayerUrl("http://t0.tianditu.com/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=")  // 街道图注记
      case 't4cva': // 地形图注记
        return setLayerUrl("http://t4.tianditu.com/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=")  // 地形图注记
      case 't07cia': // 卫星图注记
        return setLayerUrl("http://t{0-7}.tianditu.com/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=")  // 卫星图注记
      case 't07cva': // 卫星图注记
        return setLayerUrl("http://t{0-7}.tianditu.com/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=")  // 卫星图注记
    }
  }
}

export const minRenderZoom = 15
// 地图初始化配置
const mapInitConfig = {
  // ol地图底图 - 默认街道底图
  layers: [
    // 天地图底图
    setBaseMapLayer(baseLayerUrlConfig.getBaseMapLayer('t3imgPrivatization')),  // 私有化底图
    // 天地图注记
    setBaseMapTxt(baseLayerUrlConfig.getBaseMapTxt('empty')),
  ],
  // ol地图基本配置 - View默认使用EPSG3857坐标系
  view: new View({
    center: fromLonLat([121.63, 29.88]),
    zoom: 16,
    maxZoom: 17,
    minZoom: 13,
    constrainResolution: true,  // 设置缩放级别为整数 
    smoothResolutionConstraint: false,  // 关闭无级缩放地图
  }),
}

/******************************
 * 地图核心辅助方法
 * ****************************
 */
// 公共动态选项判断
const setCommonMenuMethod = (condition, commonDynamicMenuMethod) => {
  if (condition) {
    if (condition instanceof Array) {
      if (condition.length !== 0) {
        // console.log(condition.get('tempType'))

        menuAddSingleMethod(commonDynamicMenuMethod)
      }

      if (condition.length === 0) {
        menuUtils.commonMenuMethodsArr = menuUtils.commonMenuMethodsArr.filter(item => item !== commonDynamicMenuMethod)
      }
    } else {
      if (condition instanceof Feature) {
        if (!condition.get('tempType')) {
          menuAddSingleMethod(commonDynamicMenuMethod)
        }
      } else {
        menuAddSingleMethod(commonDynamicMenuMethod)

        if (condition.length === 0) {
          menuUtils.commonMenuMethodsArr = menuUtils.commonMenuMethodsArr.filter(item => item !== commonDynamicMenuMethod)
        }
      }
    }
  }
  if (!condition && menuUtils.commonMenuMethodsArr.includes(commonDynamicMenuMethod)) {
    menuUtils.commonMenuMethodsArr = menuUtils.commonMenuMethodsArr.filter(item => item !== commonDynamicMenuMethod)
  }
}

/**
 * 标注点样式
 */
const pointCircleStyle = new Style({
  image: new CircleStyle({
    radius: 5,
    fill: new Fill({
      color: '#f49d41'
    }),
    stroke: new Stroke({
      color: '#836365',
      width: 1
    })
  }),
})
const pointIconleStyle = (olMap, src) => {
  return new Style({
    image: new Icon({
      src,
      // image: new CircleStyle({
      anchor: [.8, 80],//图标的锚点,经纬度点所对应的图标的位置,默认是[0.5, 0.5],即为标注图标的中心点位置
      anchorOrigin: 'top-right',//锚点的偏移位置,默认是top-left,
      anchorXUnits: 'fraction',//锚点X的单位,默认为百分比,也可以使用px
      anchorYUnits: 'pixels',//锚点Y的单位,默认为百分比,也可以使用px
      offsetOrigin: 'top-left',//原点偏移bottom-left, bottom-right, top-left, top-right,默认 top-left
      size: [100, 100],
      offset: [3, -32],
      //图标缩放比例
      // scale: 0.5,//可以设置该比例实现,图标跟随地图层级缩放
      scale: olMap.getView().getZoom() / 30,
      //透明度
      opacity: 0.75,//如果想隐藏某个图标,可以单独设置该值,透明度为0时,即可隐藏,此为隐藏元素的方法之一。
    }),
  })
}

/**
 * 绘制扇形核心方法
 * APIMethod:OpenLayers绘制扇形的接口扩展
 * @param origin 圆心
 * @param radius 半径
 * @param sides 边数
 * @param r 弧度
 * @param angel 旋转角度(扇形右边半径与x正向轴的角度)
 * @returns {OpenLayers.Geometry.Polygon}
 */
const createRegularPolygonCurve = (origin, radius, sides, r, angel) => {
  let rotation = 360 - r;
  let angle = Math.PI * ((1 / sides) - (1 / 2));
  if (rotation) {
    angle += (rotation / 180) * Math.PI;
  }
  let rotatedAngle, x, y;
  let points = [];
  for (let i = 0; i < sides; ++i) {
    let an = i * ((360 - rotation) / 360);
    rotatedAngle = angle + (an * 2 * Math.PI / sides);
    x = origin[0] + (radius * Math.cos(rotatedAngle));
    y = origin[1] + (radius * Math.sin(rotatedAngle));
    points.push([x, y]);
  }
  if (rotation != 0) {
    points.push(origin);
  }
  let ring = new LinearRing(points);
  ring.rotate(angel / 57.3, origin);
  let list = ring.getCoordinates()

  return new Polygon([list]);
}

// 移除地图Overlay元素
const removeAllOverlay = () => {
  let flickerPointDom = document.querySelectorAll('.flicker_point')

  flickerPointDom.forEach(item => {
    item.classList.remove('flicker_point')
  })
}

/******************************
 * 地图核心方法供出
 * ****************************
 */
export const destroyMap = (olMap) => {
  if (olMap) {
    // 销毁所有图层
    olMap.getLayers().forEach(function (layer) {
      layer.setMap(null);
    });

    // 销毁视图
    olMap.setView(null);

    // 销毁地图实例
    olMap.setTarget(null);
    olMap = null;
  }
}

// 初始化地图
export const initOlMap = (target) => {
  return new Map({
    target,
    layers: mapInitConfig.layers,
    view: mapInitConfig.view,
    interactions: defaults({ mouseWheelZoom: true })  // 禁止缩放
  });
}

// 切换地图底图
export const switchBaseLayer = (olMap, type) => {
  let txtType = ''

  switch (type) {
    // 私有化底图
    case 't3imgPrivatization':
      txtType = 'empty'  // 卫星图注记
      break
    // 街道底图
    case 't0vec':
      txtType = 't0cva'  // 街道图注记
      break
    // 卫星(影像)底图
    case 't3img':
      txtType = 't07cia'  // 卫星图注记
      break
    // 地形底图
    case 't4ter':
      txtType = 't4cva'  // 地形图注记`
      break
  }

  // 天地图底图
  let newBaseMapLayer = setBaseMapLayer(baseLayerUrlConfig.getBaseMapLayer(type))
  // 天地图注记
  let newBaseMapTxt = setBaseMapTxt(baseLayerUrlConfig.getBaseMapTxt(txtType))

  console.log('当前天地图token:', tdtTk)
  console.log('当前天地图底图地址:', newBaseMapLayer.values_.url)
  console.log('当前天地图注记地址:', newBaseMapTxt.values_.url)

  // 获取当前地图中的所有图层
  const mapLayers = olMap.getLayers();

  olMap.getAllLayers().forEach((item, index) => {
    if (item.get('type') === 'baseLayer') {
      switch (item.get('layerType')) {
        case 'baseMapLayer':
          olMap.removeLayer(item)
          mapLayers.insertAt(index, newBaseMapLayer)
          break
        case 'baseMapTxt':
          if (newBaseMapTxt !== '') {
            olMap.removeLayer(item)
            mapLayers.insertAt(index, newBaseMapTxt)
          }

          if (newBaseMapTxt === '') {
            let newBaseMapTxtTmp = setBaseMapTxt('')
            olMap.removeLayer(item)
            mapLayers.insertAt(index, newBaseMapTxtTmp)
          }

          break
      }
    }
  })
}

// 获取可视区域的左上角和右下角坐标
export const getCurrentViewPosition = (olMap) => {
  const extent = olMap.getView().calculateExtent(olMap.getSize());

  // 获取投影坐标系
  const topLeftCoord = getTopLeft(extent);  // 左上角
  const bottomRightCoord = getBottomRight(extent);  // 右下角
  // console.log(topLeftCoord, bottomRightCoord)

  // 将投影坐标转换为地理坐标
  const topLeft = transform(topLeftCoord, 'EPSG:3857', 'EPSG:4326');
  const bottomRight = transform(bottomRightCoord, 'EPSG:3857', 'EPSG:4326');
  // console.log(topLeft, bottomRight)

  return {
    topLeft,
    bottomRight,
  }
}

// 初始化所有控件
export const resetControls = (olMap) => {
  olMap.getControls().clear()

  // 重新添加控件(如果需要)
  // olMap.addControl(new FullScreen());  // 全屏
  olMap.addControl(new Zoom());  // 缩放
  olMap.addControl(new ZoomSlider());  // 缩放
  olMap.addControl(new ScaleLine());  // 比例尺


  // olMap.addControl(new OverviewMap());  // 鹰眼
}

// 判断menu是否存在,不存在新增
export const menuAddSingleMethod = (singleMethod) => {
  if (!menuUtils.commonMenuMethodsArr.includes(singleMethod)) {
    menuUtils.commonMenuMethodsArr.push(singleMethod)
  }
}

// 重置右键属性菜单
export const resetContextMenu = (next) => {
  let mapWrap = document.querySelector('.ol_map_wrap')
  let menu = document.querySelector('.menu_wrap')

  // 判断是否存在menu
  if (menu) {
    mapWrap.removeChild(menu);
  }

  if (next) {
    next(mapWrap)
  }
}

// 设置鼠标右键属性
export const setContextmenu = (olMap, next, setMenuConfig) => {
  const { commonDynamicMenu, singleMenu } = menuUtils.menuMethodBtn  // 公共动态选项,每个页面有需要才显示

  // 添加右键菜单监听
  olMap.getViewport().addEventListener('contextmenu', e => {
    // console.log(transformToLonlat(olMap.getEventCoordinate(e)))  // 经纬度

    e.preventDefault(); // 阻止默认的右键菜单弹出

    /**
     * 子页动态选项
     */
    // 根据具体页面配置菜单栏
    if (setMenuConfig) {
      setMenuConfig(currentPageType => {
        // console.log('当前页面', currentPageType)

        // 根据具体页面配置菜单栏 - 子菜单在某些情况可能需要隐藏
        menuUtils.setMentBtnExtendByPage(currentPageType)
      })
    }

    // 屏幕坐标
    const pixelPoint = olMap.getEventCoordinate(e)
    const pixel = olMap.getPixelFromCoordinate(pixelPoint)
    const feature = olMap.forEachFeatureAtPixel(pixel, feature => {
      return feature
    })


    /**
     * 获取所有layer并做判断
     */
    let featureOnPage = []  // 页面所有的feature
    let myPointByMenuFeature = []  // 所有自定义定位点
    let POIPointByMenuFeature = []  // 所有POI定位点
    let drawTypeByMenuFeature = []  // 所有绘制内容
    getAllLayer(olMap, layerItem => {
      let currentFeature = layerItem.getSource().getFeatures()[0]

      if (!currentFeature) {
        return
      }

      featureOnPage.push(currentFeature)

      if (currentFeature.get('tempType') === 'myPointByMenu') {
        myPointByMenuFeature.push(currentFeature)
      }
      if (currentFeature.get('businessType') === 'POIMarker') {
        POIPointByMenuFeature.push(currentFeature)
      }
      if (currentFeature.get('drawType')) {
        drawTypeByMenuFeature.push(currentFeature)
      }
    })

    /**
     * 公共动态选项
     */
    // 判断是否显示清除所有要素
    setCommonMenuMethod(featureOnPage, commonDynamicMenu.commonDynamicMenuMethod1)

    // 判断是否显示当前要素
    setCommonMenuMethod(feature, commonDynamicMenu.commonDynamicMenuMethod2)

    // 判断是否需要显示停止绘制
    // 获取绘制的图形
    const drawInteraction = getDrawInteraction(olMap)
    setCommonMenuMethod(drawInteraction, commonDynamicMenu.commonDynamicMenuMethod3)

    // 自定义定位点
    setCommonMenuMethod(myPointByMenuFeature, '清空自定义标注点')

    // 自定义闪烁点
    let flickerPointDom = document.querySelectorAll('.flicker_point')
    setCommonMenuMethod(flickerPointDom, '清空自定义闪烁点')

    // 展示分析结果
    if (feature) {
      if (feature.get('pointData')?.isNeedAnalysis) {
        setCommonMenuMethod(feature, commonDynamicMenu.commonDynamicMenuMethod4)
      }
    }

    // 删除所有绘制内容  
    setCommonMenuMethod(drawTypeByMenuFeature, commonDynamicMenu.commonDynamicMenuMethod5)

    /**
     * 子页动态选项
     */
    // 清除POI点
    setCommonMenuMethod(POIPointByMenuFeature, singleMenu.singleMenuMethod2)


    // 重置右键属性菜单
    resetContextMenu(mapWrap => {
      let menu = null

      menu = document.createElement('div');
      menu.setAttribute("class", "menu_wrap");

      // 自定义菜单项
      let tempStr = ''
      menuUtils.commonMenuMethodsArr.forEach(item => {
        tempStr += `<li>${item}</li>`
      })

      menu.innerHTML = `
      <ul>
        ${tempStr}
      </ul>
    `;

      // 添加到页面上
      menu.style.position = 'fixed';
      menu.style.left = `${e.clientX}px`;
      menu.style.top = `${e.clientY}px`;
      mapWrap.appendChild(menu);

      // 监听菜单项的点击事件(可选)
      menu.addEventListener('click', (evt) => {
        const option = evt.target.textContent;
        next({ option, feature, pixelPoint })

        // 清理菜单
        mapWrap.removeChild(menu);
      });
    })
  });
}

// 获取当前绘制状态
export const getDrawInteraction = (olMap) => {
  // 获取绘制的图形
  return olMap.getInteractions().getArray().find(
    (interaction) => interaction instanceof Draw
  );
}

// 取消绘制(点线面)
export const cancelDrawInteraction = (olMap) => {
  // console.log('取消绘制(点线面)', olMap)

  // 获取绘制的图形
  const drawInteraction = getDrawInteraction(olMap)

  olMap.removeInteraction(drawInteraction); // 从地图中移除交互
}

// 投影坐标转换
export const transformToLonlat = (coordinate) => {
  return transform(coordinate, "EPSG:3857", "EPSG:4326")
}

// 屏幕坐标转换
export const transformToPixelPoint = (lon, lat) => {
  return fromLonLat([lon, lat])
}

// 获取经纬度
export const getHdms = (pixelPoint) => {
  return toStringHDMS(toLonLat(pixelPoint)); // 转换为经纬度显示
}

// 获取多边形中心点
export const getPolygonCenter = (feature) => {
  const geometry = feature.getGeometry();
  if (geometry.getType() === "Polygon") {
    const polygon = geometry.clone();
    polygon.transform("EPSG:3857", "EPSG:4326"); // 如果多边形在Web墨卡托坐标系中,需要转换到WGS84
    const extent = polygon.getExtent();
    const center = getCenter(extent);
    return center;
  }
  return null;
}

// 设置标注点
export const addPoint = (olMap, pointBusinessData, src = '/', pointConfig = {}, zIndex = 1) => {
  // 创建点的数据源
  const vectorSource = new VectorSource({
    features: [],
  });

  // 创建点图层
  const vectorLayer = new VectorLayer({
    source: vectorSource,
    zIndex,
    style: src === '/' ? pointCircleStyle : pointIconleStyle(olMap, src)
  });

  olMap.addLayer(vectorLayer);

  const featureConfig = (point, pointData) => {
    return {
      geometry: point,
      type: 'Marker',
      pointData,
      ...pointConfig,
    }
  }

  if (pointBusinessData instanceof Array) {
    pointBusinessData.forEach((item) => {
      const point = new Point(fromLonLat(item.lonlat));
      const feature = new Feature({
        ...featureConfig(point, item.pointData)
      });
      vectorSource.addFeature(feature);
    });
  } else {
    const point = new Point(fromLonLat(pointBusinessData.lonlat));
    const feature = new Feature({
      ...featureConfig(point, pointBusinessData.pointData)
    });
    vectorSource.addFeature(feature);
  }
}

// 点聚合
export const setCluster = (olMap, clusterBussinessData, src) => {
  // removeAllDefaultLayer(olMap); // 移除所有默认图层

  let source = new VectorSource();

  if (clusterBussinessData instanceof Array) {
    clusterBussinessData.forEach(item => {
      let coordinates = fromLonLat([item.longitude, item.latitude]);
      let feature = new Feature({
        geometry: new Point(coordinates),
        type: 'Marker',
        pointData: item
      });
      source.addFeature(feature);
    })
  } else {
    let coordinates = fromLonLat([clusterBussinessData.longitude, clusterBussinessData.latitude]);
    let feature = new Feature({
      geometry: new Point(coordinates),
      type: 'Marker',
      pointData: clusterBussinessData
    });
    source.addFeature(feature);
  }

  // 聚合
  let cluster = new Cluster({
    source: source,
    distance: 50
  })

  // 创建图层
  let clusterLayer = new VectorLayer({
    type: 'clusterLayer',
    source: cluster,
    style: function (feature) {
      feature.set('type', 'Cluster')
      let size = feature.get('features').length;

      // console.log(feature.get('features'))

      if (size === 1) {
        return src === '/' ? pointCircleStyle : pointIconleStyle(olMap, src)
      }
      else {
        return new Style({
          image: new CircleStyle({
            radius: 30,
            stroke: new Stroke({
              color: 'white'
            }),
            fill: new Fill({
              color: 'blue'
            })
          }),
          text: new Text({
            text: size.toString(),
            fill: new Fill({
              color: 'white'
            })
          })
        })
      }
    }
  });

  olMap.addLayer(clusterLayer);
}

// 添加线
export const addLine = (olMap, position, lineConfig = {}, style) => {
  // 创建线要素并添加到地图上
  const lineFeature = new Feature({
    // geometry: new LineString([[13538079.386677982, 3488521.2319548605], [13540229.178098504, 3488093.6623278903]]),
    geometry: new LineString(position),
    type: 'Line',
    ...lineConfig
  });

  if (!style) {
    // style = setFeaturesStyle('rgba(255, 255, 255, 0.2)', 'rgba(0, 0, 0, 0.5)', false, 3)
    /* style = new Style({
      fill: new Fill({
        color: 'rgba(255, 255, 255, 0.2)'
      }),
      stroke: new Stroke({
        color: 'rgba(0, 0, 0, 0.5)',
        width: 3
      }),
    }) */
  }

  lineFeature.setStyle(style);
  // lineFeature.setStyle(styleFunction);

  const vectorSource = new VectorSource({
    features: [lineFeature],
  });

  const vectorLayer = new VectorLayer({
    source: vectorSource,
    zIndex: 9
  });

  olMap.addLayer(vectorLayer);
}

// 根据feature获取layer
export const getLayerByFeature = (map = {}, feature = {}) => {
  let layers = map.getLayers().getArray();
  for (let i in layers) {
    let source = layers[i].getSource();
    if (source instanceof VectorSource) {
      let features = source.getFeatures();
      if (features.length > 0) {
        for (let j in features) {
          if (features[j] === feature) {
            return layers[i];
          }
        }
      }
    }
  }
  return {};
}

// 设置Features样式
export const setFeaturesStyle = (fillColor, strokeColor, isImg = false, width = 2, radius = 5) => {
  return new Style(!isImg ? {
    fill: new Fill({
      color: fillColor
    }),
    stroke: new Stroke({
      color: strokeColor,
      width
    }),
  } : {
    image: new CircleStyle({
      radius,
      fill: new Fill({
        color: fillColor,
      }),
      stroke: new Stroke({
        color: strokeColor,
        width,
      }),
    }),
  })
}

// 绘制区域样式
const setDrawFeaturesStyle = () => {
  return setFeaturesStyle("rgba(32, 157, 230, 0.2)", "rgb(64 158 255)")
}

// 给某个feature置顶
export const featureToMaxTop = (olMap, feature) => {
  // console.log('给某个feature置顶', olMap, feature)

  let currentStyle = null

  switch (feature.get('type')) {
    case 'Marker':
      currentStyle = setFeaturesStyle('#409eff', '#f00', true, 2, 10)
      break
    case 'Curve':
      currentStyle = setFeaturesStyle('#409eff', '#f00', false, 5)
      break
  }

  feature.setStyle(currentStyle)

  feature.set('temp', true)

  // 顶层图层
  let topLayer = new VectorLayer({
    source: new VectorSource(),
    // style: [selectPointStyle],
    zIndex: 999 // zIndex全地图最大
  });

  /* getAllLayer(olMap, layerItem => {
    let currentFeature = layerItem.getSource().getFeatures()[0]

    if (currentFeature.get('temp')) {
      olMap.removeLayer(layerItem)
    }
  }) */
  // 根据条件移除要素
  removeByCondition(olMap, currentFeature => {
    return currentFeature.get('temp')
  })

  topLayer.setOpacity(1)
  topLayer.getSource().addFeature(feature);

  olMap.addLayer(topLayer)
}

// 创建文字图层
export const addTextPoint = (olMap, text, position, textPointConfig = {}, isRemove) => {
  // console.log('创建文字图层', olMap, gridData)

  // 创建文本样式
  const textStyle = new Style({
    text: new Text({
      text,
      fill: new Fill({ color: '#333333' }),
      // stroke: new Stroke({ color: '#fff', width: 2 }),
      font: '18px pingfang',
      textAlign: 'center', // 文本对齐
      textBaseline: 'bottom', // 文本基线
      padding: [5, 10, 5, 10], // 文本周围的填充
      offsetX: -20,
      offsetY: -13,
      overflow: true, // 允许文本溢出
      rotateWithView: false, // 不随地图旋转
      rotation: 0, // 文本旋转角度
      // scale: olMap.getView().getZoom() / 30,
    }),
  });

  if (isRemove) {
    // 根据条件移除要素
    removeByCondition(olMap, currentFeature => {
      return isRemove(currentFeature)
    })
  }

  // 创建文本特征
  const feature = new Feature({
    geometry: new Point(fromLonLat(position)),
    type: 'textPoint',
    ...textPointConfig
  });

  // 设置特征的样式
  feature.setStyle(textStyle);

  // 创建文本图层
  const vectorLayer = new VectorLayer({
    source: new VectorSource({
      features: [feature],
    }),
    zIndex: 9
  });

  // 将文本图层添加到地图
  olMap.addLayer(vectorLayer);
}

/**
 * 气泡窗通用方法
 * @param {*} olMap 地图对象
 * @param {*} pixelPoint 屏幕坐标
 * @param {*} popupData 气泡窗数据
 * @param {*} next 事件方法
 * @param {*} overlayConfig 气泡窗配置 
 */
export const popupCommonConfig = (olMap, pixelPoint, popupInner, next, overlayConfig = null,) => {
  let container = document.getElementById('popup');
  // console.log('container', container)
  let closer = document.getElementById('popup-closer');
  // let content = document.getElementById('popup-content');
  container.style.display = 'block'

  /* let overlayContainer = document.querySelector('.ol-overlay-container')
  if (overlayContainer) {
    overlayContainer.remove()
  } */

  let overlay = new Overlay({
    element: container, //绑定 Overlay 对象和 DOM 对象的
    ...overlayConfig,
    zIndex: 9999,
  });
  olMap.addOverlay(overlay);

  closer.onclick = () => {
    overlay.setPosition(undefined);
    closer.blur();
    overlay = null;
    return false;
  };

  // console.log(popupInner)
  // content.innerHTML = popupData;  // 使用jsx,不直接进行inner
  overlay.setPosition(pixelPoint); //把 overlay 显示到指定的 x,y坐标

  // 使用addEventListener会无限叠加事件,且不好使用removeEventListener移除(匿名函数)
  overlay.getElement().onclick = e => {
    next(e)
  }
}

/**
 * 添加扇形
 * 绘制扇形核心方法
 * APIMethod:OpenLayers绘制扇形的接口扩展
 * @param origin 圆心
 * @param radius 半径
 * @param sides 边数
 * @param r 弧度
 * @param angel 旋转角度(扇形右边半径与x正向轴的角度)
 * @returns {OpenLayers.Geometry.Polygon}
 */
// 根据频段展示不同颜色 有边缘  
export const addCurve = (olMap, curveDataList, isResetStyle) => {
  let featureList = []  // 扇区feature列表

  // 根据业务数据修改feature数据
  curveDataList.forEach(item => {
    // 频率
    // console.log(item.curveData.workFrequency)

    // 扇区样式
    let curveStyle = setFeaturesStyle(
      "rgba(32, 222, 230, 0.4)",
      'rgba(255, 205, 67, 0.3)'
    )
    // 扇区半径
    let curveRadius = 100

    if (isResetStyle) {
      let { myCurveStyle, myCurveRadius } = isResetStyle(item)

      curveStyle = myCurveStyle
      curveRadius = myCurveRadius
    }

    let origi_point = fromLonLat(item.lonlat);  // 绘制扇形的顶点
    // let circle = createRegularPolygonCurve(origi_point, 150, 100, 45, 90) // 调用绘制扇形的方法得到扇形
    let circle = createRegularPolygonCurve(origi_point, curveRadius, 100, 45, item.curveData.antDirectionAngle) // 调用绘制扇形的方法得到扇形
    let feature = new Feature(circle);  // 把扇形加入 feature

    feature.setStyle(curveStyle)
    feature.set('type', 'Curve')  // 这是给这个扇形添加额外的参数 , 如果是设置id 用 setId方法
    // 这是给这个扇形添加额外的参数,这里的id和 setId的id没关系
    feature.set('curveData', item.curveData)
    featureList.push(feature)
  })

  let vectorSource = new VectorSource();  // 创建一个数据源
  vectorSource.addFeatures(featureList);   // 把两个扇形加进数据源
  let vectorLayer = new VectorLayer({     // 创建一个图层,把数据源加进图层
    source: vectorSource,
    zIndex: 1
  });
  olMap.addLayer(vectorLayer);   // 把图层加进地图
}

/**
 * 设置气泡窗
 * @param {*} olMap 地图对象
 * @param {*} pixelPoint 屏幕坐标
 * @param {*} popupData 气泡窗数据
 * @param {*} next 事件处理方法
 */
export const setPopup = (olMap, pixelPoint, popupInner, next) => {
  // const pixelPoint = e.coordinate  // 屏幕坐标
  popupCommonConfig(olMap, pixelPoint, popupInner, next, {
    autoPan: true, // 定义弹出窗口在边缘点击时候可能不完整 设置自动平移效果
    /* autoPanAnimation: {
        duration: 250, // 自动平移效果的动画时间 9毫秒)
    }, */
  })
}

// 自动弹出气泡窗
export const setAutoPopup = (pixelPoint, itemData, next) => {
  // 点击尺 (这里是尺(米),并不是经纬度);
  let hdms = getHdms(pixelPoint); // 转换为经纬度显示
  // let hdms = toStringHDMS(toLonLat(pixelPoint)); // 转换为经纬度显示

  const popupObj = {
    name: '通用信息展示弹窗',
    hdms,
    coordinate: [itemData.longitude, itemData.latitude],
    popupData: itemData  // 气泡窗业务数据
  }

  next(popupObj)
}

// 获取所有图层
export const getAllLayer = (olMap, next) => {
  // 获取当前地图上的所有图层
  let layers = olMap.getLayers().getArray();

  // 获取所有图层(从地图中移除所有图层)
  for (let i = layers.length - 1; i >= 0; --i) {
    if (layers[i] instanceof VectorLayer) {
      next(layers[i])
    };
  }
}

// 移除所有图层
export const removeAllLayer = (olMap) => {
  removeAllOverlay()  // 移除地图Overlay元素

  cancelDrawInteraction(olMap)  // 取消绘制(点线面)

  getAllLayer(olMap, layerItem => {
    olMap.removeLayer(layerItem)
  })
}

// 根据条件获取FeatureList
export const getFeaturesByCondition = (olMap, condition) => {
  let featureList = []
  getAllLayer(olMap, layerItem => {
    let currentFeature = layerItem.getSource().getFeatures()[0]

    if (condition(currentFeature)) {
      featureList.push(currentFeature)
    }
  })

  return featureList
}

// 根据条件移除要素
export const removeByCondition = (olMap, condition) => {
  getAllLayer(olMap, layerItem => {
    let currentFeature = layerItem.getSource().getFeatures()[0]

    if (condition(currentFeature)) {
      olMap.removeLayer(layerItem)
    }
  })
}

// 根据类型移除图层
export const removeLayerByType = (olMap, type) => {
  removeByCondition(olMap, currentFeature => {
    if (currentFeature) {
      return currentFeature.get('type') === type
    }
  })
}

// 根据业务类型移除图层
export const removeLayerByBusinessType = (olMap, type) => {
  removeByCondition(olMap, currentFeature => {
    if (currentFeature) {
      return currentFeature.get('businessType') === type
    }
  })
}

// 移除所有默认图层
export const removeAllDefaultLayer = (olMap) => {
  removeByCondition(olMap, currentFeature => {
    if (currentFeature) {
      return (currentFeature.get('type') === 'Marker' || currentFeature.get('type') === 'Curve' || currentFeature.get('type') === 'Cluster') && (!currentFeature.get('bussinessType'))
    }
  })
}

// 刷新地图需要移除的元素
export const removeByReflashMap = (olMap) => {
  // 根据条件移除要素
  removeByCondition(olMap, currentFeature => {
    if (currentFeature) {
      return currentFeature.get('tempType')
    }

  })

  removeAllOverlay()  // 移除地图Overlay元素
}

// 飞到指定坐标
export const flyToCoordinate = (olMap, lonlat) => {
  olMap.getView().animate({
    center: fromLonLat(lonlat),
    duration: 800, // 飞行时间,单位毫秒
  });
}

// 获取所有feature
export const getAllFeature = (olMap, next) => {
  olMap.getLayers().forEach(item => {
    let source = null;
    if (item) {
      source = item.getSource();
    }
    if (source instanceof VectorSource) {
      source.forEachFeature(feature => {
        // console.log(feature.get('type'))

        next(feature)
      });
    }
  });
}

// 加载kml
export const loadKML = (olMap, text) => {
  // console.log('加载kml', olMap, text)

  const format = new KML({
    extractStyles: false //至关重要
  });
  const features = format.readFeatures(text);
  features.forEach(item => {
    // 从EPSG:4326转换到EPSG:3857
    item.getGeometry().transform('EPSG:4326', 'EPSG:3857')
  })
  const vectorSource = new VectorSource({
    features: features,
  });
  olMap.getView().fit(vectorSource.getExtent());
  olMap.getLayers().push(
    new VectorLayer({
      source: vectorSource,
      style: setFeaturesStyle('blue', 'red'),
      zIndex: 100
    })
  );
}

// 创建多边形(选区)
export const drawPolygon = (olMap) => {
  // console.log('创建多边形(选区)', olMap)

  olMap.addInteraction(new Draw({
    source: new VectorSource(),
    type: 'Polygon',
  }));

  // 获取绘制完成的多边形
  olMap.getInteractions().getArray().forEach(interaction => {
    if (interaction instanceof Draw) {
      interaction.on('drawend', (event) => {
        const { feature } = event
        const geometry = feature.getGeometry();
        const coords = geometry.getCoordinates()[0]
        const lonlat = geometry.transform('EPSG:3857', 'EPSG:4326').getCoordinates()[0]
        // console.log(lonlat, coords)
        if (geometry instanceof Polygon) {
          // console.log("所选点位坐标", geometry.getCoordinates());

          createPolygon(olMap, { coords, lonlat }, { drawType: 'polygon' })

          olMap.removeInteraction(interaction); // 从地图中移除交互
        }
      });
    }
  });
}

/**
 * 绘制多边形
 * 
 * @param {*} olMap 
 * @param {*} coords 多边形的坐标数组
 */
export const createPolygon = (olMap, { coords, lonlat, polygonData }, polygonConfig = {}, next, polygonStyle = setDrawFeaturesStyle()) => {
  // console.log(olMap, coords, lonlat)

  // 创建多边形
  let polygon = new Feature({
    geometry: new Polygon([coords]),
    type: 'Polygon',
    ...polygonConfig,
    polygonData: {
      coords,
      lonlat,
      ...polygonData
    }
  });

  // 设置多边形样式
  polygon.setStyle(polygonStyle)

  // 创建矢量图层并添加多边形
  let vectorLayer = new VectorLayer({
    source: new VectorSource({
      features: [polygon]
    })
  });

  olMap.addLayer(vectorLayer);

  if (next) {
    next(polygon)
  }
}

// 取消绘制多边形(取消选区)
export const cancelPolygon = (olMap) => {
  console.log('取消绘制多边形(取消选区)', olMap)

  // cancelDrawInteraction(olMap)
}

// 创建圆形(选区)
export const drawCircle = (olMap) => {
  // console.log('创建圆形(选区)', olMap)

  olMap.addInteraction(new Draw({
    source: new VectorSource(),
    type: 'Circle',
  }));

  // 获取绘制完成的多边形
  olMap.getInteractions().getArray().forEach(interaction => {
    if (interaction instanceof Draw) {
      interaction.on('drawend', (event) => {
        const { feature } = event
        const geometry = feature.getGeometry();
        const lonlat = transform(geometry.getCenter(), 'EPSG:3857', 'EPSG:4326')
        // console.log(geometry, geometry.getRadius(), lonlat)

        let circleItem = {
          lonlat,
          radius: geometry.getRadius()
        }
        addCircle(olMap, circleItem, { drawType: 'Circle' })

        olMap.removeInteraction(interaction); // 从地图中移除交互
      });
    }
  });
}

// 绘制圆
export const addCircle = (olMap, circleItem, circleConfig = {}, isFlicker, isHide = false) => {
  if (!circleItem.radius) {
    circleItem.radius = 550
  }

  let features = []

  let feature = new Feature({
    type: "Circle",
    ...circleConfig,
    circleData: circleItem,
    // 圆心 - 半径
    geometry: new Circle(fromLonLat(circleItem.lonlat), circleItem.radius),
  })

  feature.setStyle(!isHide ? setDrawFeaturesStyle() : new Style({
    fill: new Fill({
      color: 'rgba(255, 0, 0, 0)'
    }),
    stroke: new Stroke({
      color: '#f00',
      color: 'rgba(255, 0, 0, 0)'
    }),
  }))

  features.push(feature)
  let source = new VectorSource()
  source.addFeatures(features)
  let layer = new VectorLayer({
    // opacity: 0.2,
    zIndex: 100,
  })
  layer.setSource(source)
  olMap.addLayer(layer)

  // 需要闪烁时调用
  if (isFlicker) {
    let radius = 0
    layer.on('postrender', evt => {
      if (radius >= 20) radius = 0;
      var opacity = (20 - radius) * (1 / 20); // 不透明度
      var pointStyle = new Style({
        radius: radius,
        stroke: new Stroke({
          color: "rgba(255,60,5" + opacity + ")",
          width: 18 - radius / 1.5, // 设置宽度
        }),
      });
      // 获取矢量要素上下文
      let vectorContext = getVectorContext(evt);
      vectorContext.setStyle(pointStyle);
      vectorContext.drawGeometry(feature.getGeometry());
      radius = radius + 0.2; //调整闪烁速度
      //请求地图渲染(在下一个动画帧处)
      olMap.render();
    })
  }
}

// 闪烁点图层函数
const flashPoints = (map, layer, record) => {
  if (JSON.stringify(record) === '{}') return;
  //清除上一次的闪烁图层
  if (layer.current) {
    map.removeLayer(layer.current);
  }
  //wgs84togcj02是自己封装的转换经纬度的函数,不需要的话可忽略
  // const lnglat = wgs84togcj02(record?.longitude, record?.latitude);
  //下面这段代码是先画一个普通的点位图层
  const features = [
    new Feature({
      geometry: new Point(fromLonLat(record)),
    }),
  ];
  layer.current = new VectorLayer({
    source: new VectorSource({ features }),
  });
  layer.current.setStyle(
    new Style({
      image: new CircleStyle({
        radius: 100,
        snapToPixel: false,
        stroke: new Stroke({
          color: 'red',
          size: 10,
        }),
      }),
    })
  );
  //这里是闪烁动画实现的关键
  var radius = 550;
  map.on('postcompose', function () {
    //0.6是每次扩大的幅度
    radius = radius + 0.6;
    //30是最大半径
    radius = radius % 30;
    layer.current.setStyle(
      new Style({
        image: new CircleStyle({
          radius,
          snapToPixel: false,
          stroke: new Stroke({
            color: 'red',
            width: 3,
            size: 10,
          }),
        }),
      })
    );
  });
  map.addLayer(layer.current);
};

// 绘制圆2
export const addCircle2 = (olMap, circleItem, circleConfig = {}, isFlicker = true) => {
  console.log('circleItem', circleItem.lonlat)

  let vectorLayer = new VectorLayer({
    opacity: 0.8
  });

  flashPoints(olMap, vectorLayer, circleItem.lonlat)

}

// 测距
export const testDistance = (olMap, next) => {
  // console.log("测距", olMap)

  let measure = new Draw({
    source: new VectorSource(),
    type: 'LineString',
    /* style: new Style({
      fill: new Fill({ color: 'rgba(255, 255, 255, 0.2)' }),
      stroke: new Stroke({ color: 'rgba(0, 0, 0, 0.5)', width: 2 })
    }) */
  })
  olMap.addInteraction(measure);

  measure.on('drawend', (event) => {
    const { feature } = event
    const line = feature.getGeometry();

    let length = getLength(line);
    length = length.toFixed(2)

    const coords = line.getCoordinates()
    const lonlat = line.transform('EPSG:3857', 'EPSG:4326').getCoordinates()
    // console.log('Line length: ' + length + ' meters');

    // 如果需要外部提供数据
    if (next) {
      next(length)
    }

    // 创建线要素并添加到地图上
    addLine(olMap, coords, { drawType: 'TestDistance' })

    // 创建文本要素以显示距离
    addTextPoint(olMap, length + '米', lonlat[0], { drawType: 'TestDistance' })

    // olMap.removeInteraction(measure); // 从地图中移除交互
  });
}

// 取消测距
export const cancelTestDistance = (olMap) => {
  // console.log('取消测距', olMap)

  // 根据条件移除要素
  removeByCondition(olMap, currentFeature => {
    currentFeature.get('drawType') === 'TestDistance'
  })
}

// 添加带箭头的线
export const addArrowLine = (olMap, position, src = '../src/components/OpenlayerBaseMap/icon/arrow.svg', businessType = 'arrowLine') => {
  console.log('添加带箭头的线', olMap)

  function stylefunction(feature) {
    const geometry = feature.getGeometry()
    // 轨迹地理长度
    const totalLength = geometry.getLength()
    // 像素间隔步长
    let step = 50
    // 将像素步长转实际地理距离步长
    let StepLength = step * olMap.getView().getResolution()
    // 箭头总数
    let arrowNum = Math.floor(totalLength / StepLength)

    const styles = [
      // linestring
      new Style({
        stroke: new Stroke({
          // color: 'rgb(164 164 162 / 88%)',
          color: '#42b983',
          width: 5,
        }),
      }),
    ];

    const rotations = [];
    const distances = [0];
    geometry.forEachSegment(function (start, end) {
      let dx = end[0] - start[0];
      let dy = end[1] - start[1];
      let rotation = Math.atan2(dy, dx);
      distances.unshift(Math.sqrt(dx ** 2 + dy ** 2) + distances[0]);
      rotations.push(rotation);
    });

    for (let i = 1; i < arrowNum; i++) {
      let arrow_coor = geometry.getCoordinateAt(i * 1.0 / arrowNum)
      const d = i * StepLength;
      const grid = distances.findIndex((x) => x <= d);
      styles.push(
        new Style({
          geometry: new Point(arrow_coor),
          image: new Icon({
            src,
            opacity: 1,
            anchor: [0.5, 0.5],
            rotateWithView: true,
            rotation: - rotations[distances.length - grid - 1],
            scale: 0.8
          })
        })
      )
    }

    return styles
  }

  addLine(olMap, position, { businessType }, stylefunction)
}

// 右键添加标注点
export const addMyPointByMenu = (olMap, pixelPoint) => {
  const features = [];

  const iconFeature = new Feature({
    geometry: new Point(pixelPoint),
    name: count++,
    location: pixelPoint,
    tempType: 'myPointByMenu'
  });
  const style = new Style({
    image: new CircleStyle({
      radius: 15,
      fill: new Fill({
        color: '#409eff'
      }),
      stroke: new Stroke({
        color: '#4440ff',
        width: 1
      })
    })
  });
  iconFeature.setStyle(style);
  features.push(iconFeature);
  const vectorSource = new VectorSource({
    features
  });
  const vectorLayer = new VectorLayer({
    source: vectorSource,
    opacity: 0.8
  });
  olMap.addLayer(vectorLayer);
}

// 添加闪烁点
export const addFlickerPoint = (olMap, pixelPoint, className = '', next) => {
  // console.log('添加闪烁点', olMap, pixelPoint)

  let point_div = document.createElement('div');
  point_div.className = `flicker_point ${className}`;
  let point_overlay = new Overlay({
    element: point_div,
    position: pixelPoint,
    // positioning: 'center-center'
    zIndex: 0
  });
  olMap.addOverlay(point_overlay);

  if (next) {
    point_div.addEventListener('click', () => {
      next()
    })
  }
}

/******************************
 * 测试 
 * ****************************
 */
// 用于作单元测试
export const olMapTestCommon = (olMap, feature, pixelPoint) => {
  console.log('地图功能测试', olMap, feature, pixelPoint)
  console.log('经纬度', transformToLonlat(pixelPoint))
}

// 打点测试
export const setPointTest = (olMap) => {
  // fromLonLat([121.63, 29.88])

  const features = [];
  // console.log(e.coordinate); // 获取坐标

  const iconFeature = new Feature({
    geometry: new Point(fromLonLat([121.63, 29.88])),
    // name: count++,
    location: fromLonLat([121.63, 29.88])
  });
  const style = new Style({
    image: new CircleStyle({
      radius: 10,
      fill: new Fill({
        color: '#f49d41'
      }),
      stroke: new Stroke({
        color: '#836365',
        width: 1
      })
    })
  });
  iconFeature.setStyle(style);
  features.push(iconFeature);
  const vectorSource = new VectorSource({
    features
  });
  const vectorLayer = new VectorLayer({
    source: vectorSource,
    opacity: 0.8
  });
  olMap.addLayer(vectorLayer);
}

// 移除标注测试
export const removePointTest = (olMap) => {
  const layers = olMap.getLayers();
  layers.forEach(item => {
    if (item instanceof VectorLayer) olMap.removeLayer(item);
  });
}

// 点击打点测试
export const clickSetPointTest = (olMap, e) => {
  const features = [];
  // console.log(e.coordinate); // 获取坐标

  const iconFeature = new Feature({
    geometry: new Point(e.coordinate),
    name: count++,
    location: e.coordinate
  });
  const style = new Style({
    image: new CircleStyle({
      radius: 10,
      fill: new Fill({
        color: '#f49d41'
      }),
      stroke: new Stroke({
        color: '#836365',
        width: 1
      })
    })
  });
  iconFeature.setStyle(style);
  features.push(iconFeature);
  const vectorSource = new VectorSource({
    features
  });
  const vectorLayer = new VectorLayer({
    source: vectorSource,
    opacity: 0.8
  });
  olMap.addLayer(vectorLayer);
}

// 绘制扇形测试
export const addCurveTest = (olMap) => {
  let origi_point = fromLonLat([121.63, 29.88]);  // 绘制扇形的顶点
  let circle = createRegularPolygonCurve(origi_point, 500, 100, 30, 90) // 调用绘制扇形的方法得到扇形
  let feature = new Feature(circle);  // 把扇形加入 feature
  feature.setStyle(  // 设置一下这个扇形的样式
    new Style({
      fill: new Fill({
        color: 'rgba(32, 157, 230, 0.3)'
      }),
      stroke: new Stroke({
        color: 'rgba(255, 205, 67, 0.3)',
        width: 2
      }),
    })
  )
  feature.set('type', 'Curve')  // 这是给这个扇形添加额外的参数 , 如果是设置id 用 setId方法
  feature.set('curve', {   // 这是给这个扇形添加额外的参数,这里的id和 setId的id没关系
    id: 1,
    title: '测试001',
    msg: '测试001-1',
    msg2: '测试001-2',
  })

  // 创建第二个扇形,和第一个一样
  let circle1 = createRegularPolygonCurve(origi_point, 500, 100, 30, 45)
  let feature1 = new Feature(circle1);
  feature1.setStyle(
    new Style({
      fill: new Fill({
        color: 'rgba(32, 157, 230, 0.3)'
      }),
      stroke: new Stroke({
        color: 'rgba(255, 205, 67, 0.3)',
        width: 2
      }),
    })
  )
  feature1.set('type', 'Curve')
  feature1.set('curve', {
    id: 2,
    title: '超级无敌炫酷爆龙战神',
    msg: '超级无敌炫酷爆龙战神 描述001',
    msg2: '超级无敌炫酷爆龙战神 描述002',
  })

  let vectorSource = new VectorSource();  // 创建一个数据源
  vectorSource.addFeatures([feature, feature1]);   // 把两个扇形加进数据源
  let vectorLayer = new VectorLayer({     // 创建一个图层,把数据源加进图层
    source: vectorSource
  });
  olMap.addLayer(vectorLayer);   // 把图层加进地图
}

// 绘制圆测试
export const addCircleTest = (olMap, circleList) => {
  let features = []

  circleList.forEach((item, index) => {
    let feature = new Feature({
      type: "Circle",
      title: item.name,
      geometry: new Circle(fromLonLat(item.site), 200),
    })
    feature.setStyle(
      new Style({
        fill: new Fill({
          color: 'rgba(32, 157, 230, 1)'
        }),
      })
    )
    features.push(feature)
  })
  let source = new VectorSource()
  source.addFeatures(features)
  let layer = new VectorLayer({
    opacity: 0.2
  })
  layer.setSource(source)
  olMap.addLayer(layer)
}

// 获取所有要素测试
export const getAllFeatureTest = (olMap) => {
  let layers = olMap.getLayers().getArray();
  layers.forEach(item => {
    if (item instanceof VectorLayer) {
      let currentFeature = item.getSource().getFeatures()
      console.log(currentFeature[0].get('type'))
    }
  })
}

OpenlayerBaseMap/index.vue

vue
<template>
  <section class="ol_map_wrap">
    <!-- 插槽 -->
    <slot></slot>

    <!-- 地图 -->
    <section id="olMap" class="ol_map"></section>

    <!-- 图例 -->
    <lend ref="refLend" :currentPageType="currentPageType" :isShowLend="isShowLend" />

    <!-- 概览信息显示隐藏 -->
    <div class="overview_info_toggle" :title="`点击${!toggleFlag ? '显示' : '隐藏'}`" @click="toggleOverviewInfo"
      v-if="isControlOverviewInfo"></div>

    <!-- 切换底图控件 -->
    <switch-base-layer @switchBaseLayerType="switchBaseLayerType" />

    <!--  气泡窗 -->
    <popup-common ref="refPopupCommon" :currentPageType="currentPageType" />

    <!-- 底部信息 -->
    <div class="copyright_info" v-show="true">
      <p>Copyright © CMDI.vip All Rights Reserved.</p>
      <ul>
        <li>
          <dl>
            <dt>经纬度:</dt>
            <dd>{{ currentLonlat }}</dd>
          </dl>
        </li>
        <li>
          <dl>
            <dt>当前层级:</dt>
            <dd>{{ currentZoomLevel }}</dd>
          </dl>
        </li>
      </ul>
    </div>

    <!-- 切换天地图token 弹窗 -->
    <set-token-dialog ref="refSetTokenDialog" />
  </section>
</template>

<script setup name="gis">
// vue - core
import { ref, onMounted, defineEmits, nextTick } from "vue";
// map - core
import * as mapUtils from "./mapUtils.js";
import menuUtils from './menuUtils.js'
import { boundingExtent } from 'ol/extent'
// 组件
import PopupCommon from "./components/popup/PopupCommon.vue"; // 气泡窗
import Lend from "./components/Lend.vue"; // 图例
import SwitchBaseLayer from "./components/SwitchBaseLayer.vue"; // 切换底图控件
import SetTokenDialog from "./components/SetTokenDialog.vue"; // 切换天地图token
// 工具
import { objIsEmpty } from "@/utils/index.js";
// 组件传参
import mittBus from "@/utils/mittBus"; // mitt
// 其他工具
import * as popupInner from "./components/popup/popupInner.js";
/* import { gisDataStore } from '@/store/modules/gis.js'  // store

const gisStoreData = gisDataStore() */

// 自定义事件
const emit = defineEmits([
  // 全局
  "getOlMapInstance",
  "getCurrentViewData",
  "reflashMap",
  'toggleOverviewInfo',
  // gis
  // ......
  // 投诉
  "getAreaData",
  "setCircleDialogData",
]);

const props = defineProps({
  // 当前页面类型
  currentPageType: {
    type: String,
    default: "",
  },
  // 是否需要自动加载数据 - false地图移动不加载,点击刷新加载
  isAutoRenderData: {
    type: Boolean,
    default: false,
  },
  // 是否显示图例
  isShowLend: {
    type: Boolean,
    default: true,
  },
  // 自组件是否需要控制概览信息显示隐藏
  isControlOverviewInfo: {
    type: Boolean,
    default: false,
  }
});

const { proxy } = getCurrentInstance();

let myOlMap = null;

const refPopupCommon = ref(null);
const refLend = ref(null);
const refSetTokenDialog = ref(null);

let toggleFlag = ref(true);  // 概览信息默认显示

const currentZoomLevel = ref(0);
const currentLonlat = ref("");

const isRemoveMap = ref(true); // 判断渲染地图时是否移除当前地图要素

let currentSingleObjData = ref({}); // 当前检索到的数据

const mapCommonData = {
  minRenderZoom: mapUtils.minRenderZoom, // 最小渲染层级
};

// 概览信息显示隐藏
const toggleOverviewInfo = () => {
  toggleFlag.value = !toggleFlag.value;

  emit('toggleOverviewInfo', toggleFlag.value)
}

// 地图加载完初始化做的一些操作
const mapInit = (olMap) => {
  // console.log('地图加载完初始化做的一些操作', olMap)
  myOlMap = olMap; // 赋值全局变量,为后续业务操作做准备

  // 地图加载完初始化做的一些操作[业务相关]
  emit("getOlMapInstance", olMap, mapCommonData); // 向外供出olMap实例,以及一些公共数据

  mapUtils.resetControls(olMap); // 初始化所有控件

  // 设置鼠标右键属性
  mapUtils.setContextmenu(
    olMap,
    (option) => {
      // console.log(option, mapUtils.menuMethods);
      setMenuMethods(option);
    },
    (next) => {
      // 根据具体页面配置菜单栏
      next(props.currentPageType);
    }
  );
};

// 设置鼠标右键菜单栏方法
const setMenuMethods = ({ option, feature, pixelPoint }) => {
  // console.log("设置鼠标右键菜单栏方法", option, feature, pixelPoint);

  // 点击地图隐藏气泡窗
  refPopupCommon.value.hidePopup();

  menuUtils.setMenuMethods(myOlMap, option, feature, pixelPoint, proxy)
};

// 地图加载完初始化后获取地图的一些信息
const getMapInitInfo = (olMap) => {
  // console.log("地图加载完初始化后获取地图的一些信息", olMap)

  // 获取可视区域数据 - 如果不需要自动加载
  if (!props.isAutoRenderData) {
    // console.log('刷新加载地图', props.isAutoRenderData)
    emit("getCurrentViewData", olMap); // 地图加载时会自动触发一次
  }

  // 圆数据
  emit("getAreaData", olMap);
};

// 设置地图
const setOlmap = (olMap) => {
  mapEvent(olMap); // 地图事件
};

// 切换底图
const switchBaseLayerType = (val) => {
  // console.log('切换底图', val)

  mapUtils.switchBaseLayer(myOlMap, val)
}

/**
 * 业务方法
 */
// 根据数据渲染Feature
const renderFeatureByData = (olMap, dataList, renderFeature, isLoop = true) => {
  // console.log("根据数据渲染Feature", dataList);

  refPopupCommon.value.hidePopup();

  if (isRemoveMap.value) {
    // removeAllLayer(myOlMap); // 移除所有默认图层
    removeAllDefaultLayer(myOlMap); // 移除所有默认图层
  }
  // }

  setTimeout(() => {
    if (isLoop) {
      // console.log(dataList)
      dataList.forEach((item) => {
        renderFeature(olMap, item);
      });
    } else {
      renderFeature(olMap, dataList);
    }
  }, 500);
};

// 通过气泡数据设置地图
const setMapByAutoPopupData = async (olMap, itemData, fixData, renderFeature) => {
  // console.log('通过气泡数据设置地图', olMap, itemData)

  const { arrData } = itemData

  if (fixData) {
    fixData(itemData);
  }

  // 选择数据隐藏气泡
  refPopupCommon.value.hidePopup();

  olMap.getView().setZoom(18);

  // 调用函数飞到指定的坐标
  flyToCoordinate(myOlMap, [itemData.longitude, itemData.latitude]);
  // mapUtils.flyToCoordinate(myOlMap, [itemData.longitude, itemData.latitude]);

  // 只显示一个Feature暂时使用此方案
  mapUtils.removeAllLayer(myOlMap); // 移除所有图层

  if (renderFeature) {
    renderFeature(olMap, [itemData]);
  }

  // 检索到当前要素
  setTimeout(() => {
    // 获取所有图层
    mapUtils.getAllFeature(olMap, (featureItem) => {
      let featureData = {};

      switch (featureItem.get("type")) {
        case "Marker":
          featureData = featureItem.get("pointData");
          break;
        case "Curve":
          featureData = featureItem.get("curveData");
          break;
      }

      if (featureData.cgi === itemData.cgi) {
        currentSingleObjData.value = { featureData, featureItem };
      }
    });
  }, 1000);

  // 将经纬度转换为屏幕坐标
  const pixelPoint = transformToPixelPoint(
    itemData.longitude,
    itemData.latitude
  );

  // 展示气泡窗
  mapUtils.setAutoPopup(pixelPoint, itemData, (popupObj) => {
    if (arrData) {
      popupObj.popupData = arrData
      popupObj.noFeature = true
    } else {
      let popupBack = document.querySelector(`#popupBack`)
      if (popupBack) {
        // 判断是否有返回图标,有则删除
        popupBack.remove();
      }
    }
    mapUtils.popupCommonConfig(
      olMap,
      pixelPoint,
      arrData ? popupInner.featuresPopupInner(popupObj) : popupInner.commonPopupInner(popupObj),
      (e) => {
        const { target } = e; // 事件对象

        // 点击气泡窗获取更多
        if (target.getAttribute("data-function") === "getMore") {
          // console.log("点击气泡fsdfdsfvfdaaa窗获取更多", popupObj.popupData);
          /* if (JSON.stringify(gisStoreData.currentPopupData) !== '{}') {
            itemData.currentPopupData = gisStoreData.currentPopupData
          } else {
            itemData.currentPopupData = popupObj.popupData
          } */
          mittBus.emit("popupDataGetMore", {
            currentPopupObj: itemData,
            callback: (popupData) => {

              mittBus.emit("showPopupDialog", popupData)
            },
          });
        }

        // 点击唯一标识显示具体气泡信息
        if (target.getAttribute("data-function") === "getSingleByFeatures") {
          mittBus.emit("getSingleByFeatures", { unique: target.getAttribute("data-unique").trim(), itemData });
        }

        // 返回
        // 点击popupDom返回
        if (target.getAttribute("data-function") === "popupBack") {
          mittBus.emit("popupBack");
        }
      }
    );
  });
};

/**
 * 接收其他组件派发的方法
 */
// 根据不同token初始化地图
mittBus.on("initOlMapByToken", () => {
  resetOlMap()  // 初始化地图
});
/**
 * 刷新地图
 * 各个组件如果需要刷新地图通过派发组件,最终到这个文件里面去做最终地图的刷新
 * resetFlag true 不带任何条件查询,查全部
 */
mittBus.on("reflashMap", (resetFlag) => {
  // console.log("刷新地图", resetFlag);

  // 判断是否需要带条件
  if (
    Boolean(resetFlag) &&
    myOlMap.getView().getZoom() > mapUtils.minRenderZoom
  ) {
    // 如果需要带条件,需要出发点全局派发事件去修改按钮dom
    mittBus.emit("resetBtn");
  }

  mapUtils.removeByReflashMap(myOlMap)  // 刷新地图需要移除的元素

  // 最终的刷新需要自组件派发事件到父组件完成派发
  emit("reflashMap", resetFlag, () => {
    nextTick(() => {
      setTimeout(() => {
        // 刷新完成之后,需要对检索到的数据去做定位
        if (!objIsEmpty(currentSingleObjData.value)) {
          // console.log("当前有检索条件", currentSingleObjData.value);

          let { featureData, featureItem } = currentSingleObjData.value;
          // console.log(featureItem);

          // 置顶图层
          mapUtils.featureToMaxTop(myOlMap, featureItem);
        }
      }, 1000);
    });
  });
});

// 删除所有绘制内容并取消状态 - 取消绘制(点线面)
mittBus.on("cancelDrawInteraction", () => {
  // console.log('drawPolygon')

  // 根据条件移除要素
  mapUtils.removeByCondition(myOlMap, currentFeature => {
    return currentFeature.get('drawType')
  })

  mapUtils.cancelDrawInteraction(myOlMap); // 绘制多边形
});

// 绘制多边形
mittBus.on("drawPolygon", () => {
  // console.log('drawPolygon')

  mapUtils.drawPolygon(myOlMap); // 绘制多边形
});

// 取消绘制多边形
mittBus.on("cancelPolygon", () => {
  // console.log('取消绘制多边形')

  mapUtils.cancelPolygon(myOlMap); // 绘制多边形
});

// 绘制圆形
mittBus.on("drawCircle", () => {
  // console.log('绘制圆形')

  mapUtils.drawCircle(myOlMap); // 绘制圆形
});

// 取消绘制圆形
mittBus.on("cancelCircle", () => {
  // console.log('取消绘制圆形')
});

// 测距
mittBus.on("testDistance", (next) => {
  // console.log("测距");

  // 测距
  mapUtils.testDistance(myOlMap, next);
});

// 取消测距
mittBus.on("cancelTestDistance", () => {
  // console.log("取消测距");

  mapUtils.cancelTestDistance(myOlMap); // 取消测距
});

/**
 * menu方法接收
 */
// 显示当前要素信息
mittBus.on("singleFeaturesClick", ({ feature, pixelPoint }) => {
  // console.log("显示当前要素信息", feature, pixelPoint);

  if (props.currentPageType === "complain") {
    emit("setCircleDialogData", feature);

    return;
  }
  // 点击单个feature - map - click事件
  singleFeaturesClick(myOlMap, feature, pixelPoint);
});
// 切换天地图token
mittBus.on("showSetTokenDialog", () => {
  refSetTokenDialog.value?.show();
})

/**
 * 地图工具方法
 */
// 地图事件
const mapEvent = (olMap) => {
  // 监听鼠标移动事件
  olMap.on("pointermove", (e) => {
    // 鼠标移动到feature区域时变为手形
    let pixel = olMap.getEventPixel(e.originalEvent);
    let hit = olMap.hasFeatureAtPixel(pixel);
    olMap.getTargetElement().style.cursor = hit ? "pointer" : "";

    currentLonlat.value = mapUtils.transformToLonlat(e.coordinate);
  });

  // 监听鼠标单击事件
  olMap.on("singleclick", (e) => {
    // console.log("点击地图", mapUtils.transformToLonlat(e.coordinate));

    // 判断当前是否为绘制状态
    if (mapUtils.getDrawInteraction(olMap)) {
      return
    }

    // 点击地图隐藏气泡窗
    refPopupCommon.value.hidePopup();

    // 点击地图隐藏右键菜单
    mapUtils.resetContextMenu();

    // const pixelPoint = e.coordinate; // 屏幕坐标
    let pixel = olMap.getEventPixel(e.originalEvent);
    let featureList = olMap.getFeaturesAtPixel(pixel); // 点击时获取所有features
    // console.log('wwww', featureList)

    const currentShapeFeature = (shape) => {
      return featureList.filter((item) => item.get("type") === shape);
    };

    const judgeShape = (shape) => {
      return JSON.stringify(currentShapeFeature(shape)) !== "[]";
    };

    // 在选区中点击时触发
    const innerClick = () => {
      // 图形中的要素
      let pointFeatureList = (currentShapeFeature("Marker")[0] || currentShapeFeature("Curve")[0])

      // 如果只有2个要素
      if (featureList.length === 2) {
        // 是标注
        if (pointFeatureList && pointFeatureList.get("type")) {
          singleFeaturesClick(myOlMap, pointFeatureList, e.coordinate);
        }
      } else {
        refPopupCommon.value.setFeaturesPopup(
          olMap,
          e.coordinate,
          featureList
        );
      }
    }

    // 如果feature数组存在(不为空)
    if (featureList) {
      // console.log("featureList", featureList);

      // 点击单个feature
      if (featureList.length === 1) {
        // console.log("无重叠,单个feature", featureList);

        // refPopupCommon.value.showPopup(); // 需要气泡时弹出

        // 不显示气泡窗的要素禁止弹窗
        if (currentShapeFeature("Circle").length !== 0) {
          refPopupCommon.value.hidePopup();
        }

        // 点击单个feature - map - click事件
        singleFeaturesClick(olMap, featureList[0], e.coordinate);
      }

      // 多个feature
      if (featureList.length > 1) {
        // console.log("有重叠,多个feature", featureList);

        // console.log(currentShapeFeature("Circle"))

        // 如果存在聚合
        if (judgeShape("Cluster")) {
          // 点击地图隐藏气泡窗
          refPopupCommon.value.hidePopup();
          // console.log(featureList)
          singleFeaturesClick(olMap, featureList[0], e.coordinate);
        }

        // 点击时需要显示标注的选区
        if (judgeShape("Circle") || judgeShape("Polygon")) {
          innerClick()
        }

        // 如果重叠区存在圆形
        if (judgeShape("Circle")) {
          // console.log('点击圆区域内要素', featureList)

          /* if (pointFeatureList.get("bussinessType") === "analysisMarker") {
            return
          } */
        } else if (judgeShape("Polygon")) {
          // console.log('点击多边形区域内要素', featureList)
          /* let featureItem = featureList.filter(
            (item) => item.get("type") === "Polygon"
          );
          setPolygonDialogData(featureItem[0]); */
        } else {
          // 点击扇形弹出气泡
          if (
            featureList[0].get("type") === "Curve" ||
            featureList[0].get("type") === "Marker"
          ) {
            // refPopupCommon.value.showPopup(); // 需要气泡时弹出
            refPopupCommon.value.setFeaturesPopup(
              olMap,
              e.coordinate,
              featureList
            );
          }
        }
      }
    }
  });

  // 监听鼠标拖动地图事件
  olMap.on("moveend", (e) => {
    // console.log('拖拽移动触发事件', e)

    let view = myOlMap.getView();
    let zoom = view.getZoom();
    currentZoomLevel.value = zoom.toFixed(2);
    // console.log("当前缩放级别为:", zoom, mapUtils.minRenderZoom);

    // 获取当前中心点坐标
    let center = view.getCenter();
    mittBus.emit("getCurrentCenter", mapUtils.transformToLonlat(center));

    mittBus.emit("getCurrentZoom", {
      zoom,
      minRenderZoom: mapUtils.minRenderZoom,
    });

    if (props.isAutoRenderData) {
      // console.log('移动加载地图', props.isAutoRenderData)
      mapUtils.removeAllDefaultLayer(olMap)  // 移除默认要素
      emit("getCurrentViewData", olMap); // 刷新地图
    }
  });
};

// 点击单个feature - map - click事件
const singleFeaturesClick = (olMap, featureItem, pixelPoint) => {
  // console.log("无重叠,单个feature", featureItem, featureItem.get("type"));

  if (featureItem && featureItem.get('drawType') && mapUtils.getDrawInteraction(olMap)) {
    return
  }

  let popupData = null;

  // 点击聚合
  if (featureItem && featureItem.get("type") === "Cluster") {
    refPopupCommon.value.hidePopup();

    let clusterFeatureList = featureItem.get('features')

    if (clusterFeatureList.length === 1) {
      popupData = clusterFeatureList[0].get("pointData");
      refPopupCommon.value.setCommonPopup(olMap, pixelPoint, popupData, clusterFeatureList[0]);

      return
    }

    const extent = boundingExtent(
      clusterFeatureList.map((r) => r.getGeometry().getCoordinates())
    );
    console.log(extent)
    olMap
      .getView()
      .fit(extent, { duration: 1000, padding: [50, 50, 50, 50] });

    /* console.log(mapUtils.getLayerByFeature(olMap, featureItem))

    let currentLayer = mapUtils.getLayerByFeature(olMap, featureItem)

    currentLayer.getFeatures(pixelPoint).then(clickedFeatures => {
      console.log(clickedFeatures)
    }) */


    /* olMap.getAllLayers().forEach((item, index) => {
      if (item.get('type') === 'clusterLayer') {
        console.log(item)
      }
    }) */
  }

  // 点击点标注
  if (featureItem && featureItem.get("type") === "Marker") {
    // console.log('Marker点标注', featureItem);

    popupData = featureItem.get("pointData");
    // console.log('获取点标注数据', popupData)

    refPopupCommon.value.setCommonPopup(olMap, pixelPoint, popupData, featureItem);
  }

  // 点击扇形区域
  if (featureItem && featureItem.get("type") === "Curve") {
    // console.log('点击扇形区域', featureItem);

    popupData = featureItem.get("curveData");
    // console.log('获取扇形区数据', popupData)

    refPopupCommon.value.setCommonPopup(olMap, pixelPoint, popupData);
  }

  // 点击圆形区域
  if (featureItem && featureItem.get("type") === "Circle") {
    // console.log("点击圆形区域", featureItem);

    setCircleDialogData(featureItem);
  }

  // 点击多边形
  if (featureItem && featureItem.get("type") === "Polygon") {
    // console.log("点击多边形", featureItem.get("polygonData"));

    const polygonData = featureItem.get("polygonData");

    // 网格
    if (featureItem.get('businessType') === "grid") {
      // console.log("点击网格数据", polygonData);
      // return;
    }

    // 弹出多边形数据
    setPolygonDialogData(featureItem);
  }
};

// 点击圆形区域获取数据
const setCircleDialogData = (feature) => {
  let circleData = feature.get('circleData')
  // console.log(circleData)

  mittBus.emit("setCircleDialogData", circleData);
};

// 弹出多边形数据
const setPolygonDialogData = (featureItem) => {
  const { coords, lonlat, name } = featureItem.get("polygonData");

  mittBus.emit("setPolygonDialogData", { lonlat, name });
};

/**
 * 地图方法供出
 */

// 获取可视区域坐标
const getCurrentViewPosition = (olMap) => {
  return mapUtils.getCurrentViewPosition(olMap);
};

// 屏幕坐标转换
const transformToPixelPoint = (lon, lat) => {
  return mapUtils.transformToPixelPoint(lon, lat)
}

// 添加圆
const addCircle = (olMap, item, circleConfig = {}, isFlicker, isHide) => {
  mapUtils.addCircle(olMap, item, circleConfig, isFlicker, isHide);
};

// 添加圆
const addCircle2 = (olMap, item, circleConfig = {}) => {
  mapUtils.addCircle2(olMap, item, circleConfig);
};

// 添加闪烁点
const addFlickerPoint = (olMap, pixelPoint, className, next) => {
  mapUtils.addFlickerPoint(olMap, pixelPoint, className, next);
}

// 加载kml
const loadKML = (olMap, text) => {
  mapUtils.loadKML(olMap, text);
};

// 移除所有图层
const removeAllLayer = (olMap) => {
  // console.log(olMap, type);

  mapUtils.removeAllLayer(olMap);
};

// 根据类型移除图层
const removeLayerByType = (olMap, type) => {
  // console.log(olMap, type);

  mapUtils.removeLayerByType(olMap, type); // 移除所有图层
};
// 根据业务类型移除图层
const removeLayerByBusinessType = (olMap, type) => {
  // console.log(olMap, type);

  mapUtils.removeLayerByBusinessType(olMap, type); // 移除所有图层
};

// 批量添加点
const addPoint = (olMap, pointList, src, pointConfig = {}, zIndex) => {
  mapUtils.addPoint(olMap, pointList, src, pointConfig, zIndex);
};

// 点聚合
const setCluster = (olMap, dataList, src) => {
  mapUtils.setCluster(olMap, dataList, src);
};

// 移除所有默认图层
const removeAllDefaultLayer = (olMap) => {
  mapUtils.removeAllDefaultLayer(olMap);
}

// 设置 Features 样式
const setFeaturesStyle = (fillColor, strokeColor) => {
  return mapUtils.setFeaturesStyle(fillColor, strokeColor);
};

// 批量添加扇形
const addCurve = (olMap, pointList, isResetStyle) => {
  if (isResetStyle) {
    mapUtils.addCurve(olMap, pointList, (item) => {
      return isResetStyle(item);
    });
  } else {
    mapUtils.addCurve(olMap, pointList);
  }
};

const flyToCoordinate = (OlMap, lonlat) => {
  mapUtils.flyToCoordinate(OlMap, lonlat);
};

// 创建多边形
const createPolygon = (olMap, { coords, lonlat, polygonData }, polygonConfig, next, style) => {
  mapUtils.createPolygon(olMap, { coords, lonlat, polygonData }, polygonConfig, next, style);
};

// 创建文字图层
const addTextPoint = (olMap, text, position, businessType, textOverlayConfig = {}, isRemove) => {
  mapUtils.addTextPoint(olMap, text, position, { businessType, ...textOverlayConfig }, isRemove);
};

// 获取多边形中心点
const getPolygonCenter = (feature) => {
  return mapUtils.getPolygonCenter(feature);
};

// 添加带箭头的线
const addArrowLine = (olMap, position, src, businessType) => {
  mapUtils.addArrowLine(olMap, position, src, businessType)
}

// 初始化地图
const resetOlMap = () => {
  // console.log("地图初始化");
  /* if (myOlMap) {
    mapUtils.destroyMap(myOlMap)
  } */
  destroyMap(myOlMap)

  const olMap = mapUtils.initOlMap("olMap"); // 初始化地图

  mapInit(olMap); // 地图加载完初始化做的一些操作
  getMapInitInfo(olMap); // 地图加载完初始化后获取地图的一些信息
  setOlmap(olMap); // 设置地图

  // console.log("地图加载完成");
}

// 销毁地图
const destroyMap = (olMap) => {
  if (olMap) {
    mapUtils.destroyMap(olMap)
  }
}

/**
 * vue生命周期函数
 * 挂载后触发
 */
onMounted(() => {
  resetOlMap()  // 初始化地图

  switchBaseLayerType('t3imgPrivatization')  // 页面初始化时加载默认底图(有时候加载不出来)
});

/**
 * 暴露方法
 */
defineExpose({
  renderFeatureByData, // 根据数据渲染Feature
  setMapByAutoPopupData, // 通过气泡数据设置地图

  // 地图方法供出
  destroyMap, // 销毁地图
  getCurrentViewPosition, // 获取可视区域坐标
  transformToPixelPoint,  // 屏幕坐标转换
  addCircle, // 添加圆
  addCircle2, // 添加圆
  addFlickerPoint, // 添加闪烁点
  loadKML, // 加载kml
  removeAllLayer, // 移除所有图层
  removeLayerByType, // 根据类型移除图层
  removeLayerByBusinessType, // 根据业务类型移除图层
  addPoint, // 批量添加点
  setCluster,  // 点聚合
  removeAllDefaultLayer,  // 移除所有默认图层
  addCurve, // 批量添加扇形
  createPolygon, // 创建多边形
  flyToCoordinate, // 飞到指定的坐标
  setFeaturesStyle, // 设置 Features 样式
  addTextPoint, // 创建文字图层
  getPolygonCenter, // 获取多边形中心点
  addArrowLine,  // 添加带箭头的线
});
</script>

<style lang="scss">
$zoomMargin: 10em;

.ol_map_wrap {
  position: absolute;
  width: 100%;
  height: 100%;
  min-width: 1250px;

  &.nav_ol_map {
    .ol_map {

      // 控件相关
      .ol-overlaycontainer-stopevent {
        .ol-scale-line {
          bottom: 76px;
        }
      }
    }
  }

  .ol_map {
    width: 100%;
    height: 100%;
    // overflow: hidden;

    .ol-overlaycontainer-stopevent {
      .ol-overlay-container {
        &:first-child {
          z-index: 1;
        }

        &:not(:first-child) {
          z-index: -999;
          pointer-events: none !important;
        }
      }
    }

    .flicker_point {
      height: 50px;
      width: 50px;
      border-radius: 50%;
      background: rgba(255, 0, 0, 0.9);
      transform: scale(0);
      animation: flickerAnimation 3s;
      animation-iteration-count: infinite;
      cursor: pointer;

      &.complain_flicker_point {
        height: 75px;
        width: 75px;
      }
    }

    @keyframes flickerAnimation {
      to {
        transform: scale(2);
        background: rgba(255, 0, 0, 0.1);
      }
    }

    .ol-viewport {
      overflow: initial !important;
    }

    // 控件相关
    .ol-overlaycontainer-stopevent {
      // 全屏
      /* .ol-full-screen {
        position: absolute;
        top: -34px;
        right: 108px;
      } */

      // 缩放
      .ol-zoom,
      .ol-zoomslider {
        left: initial;
        right: 0.5em;
      }

      .ol-zoom {
        top: calc(0.5em + $zoomMargin);
      }

      .ol-zoomslider {
        top: calc(4.5em + $zoomMargin);
      }

      // 比例尺
      .ol-scale-line {
        position: absolute;
        // bottom: 76px;
        bottom: 38px;
        background: initial;
      }

      /* // 鹰眼
      .ol-overviewmap {
        position: absolute;
        left: 0;
        bottom: 20px;

        .ol-overviewmap-map {
          width: 100px;
          height: 100px;
        }
      } */
    }
  }
}

.menu_wrap {
  padding: 3px;
  max-height: 157px;
  background: rgba(255, 255, 255, 0.8);
  border: solid 1px #777777;
  border-radius: 5px;
  overflow: auto;

  ul {
    padding-bottom: 3px;

    li {
      padding: 5px 10px;
      font-size: 12px;
      color: #000;
      border-radius: 3px 3px 0 0;
      cursor: pointer;
      transition: 0.3s linear;

      &:hover {
        color: #715f5f;
        background: rgba(128, 128, 128, 0.25);
        transition: 0.3s linear;
      }

      &:not(:last-child) {
        margin-bottom: 3px;
        border-bottom: solid 1px #b3b3b3;
      }
    }
  }
}

.overview_info_toggle {
  position: absolute;
  right: 9px;
  top: 45px;
  width: 25px;
  height: 25px;
  z-index: 1;
  background: url("./icon/expand.svg") center center no-repeat;
  background-size: 100% 100%;
  cursor: pointer;
}

.copyright_info {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  min-width: 800px;
  min-height: 20px;
  display: flex;
  justify-content: space-between;
  padding: 0 25px 0 12px;
  z-index: 1;
  background: #8a8888c4;

  p,
  dt,
  dd {
    height: 25px;
    line-height: 28px;
    font-size: 12px;
  }

  ul {
    li {
      display: inline-block;

      &:first-child {
        dd {
          min-width: 350px;
        }
      }

      dl {

        dt,
        dd {
          display: inline-block;
        }
      }
    }
  }
}
</style>
<template>
  <section class="ol_map_wrap">
    <!-- 插槽 -->
    <slot></slot>

    <!-- 地图 -->
    <section id="olMap" class="ol_map"></section>

    <!-- 图例 -->
    <lend ref="refLend" :currentPageType="currentPageType" :isShowLend="isShowLend" />

    <!-- 概览信息显示隐藏 -->
    <div class="overview_info_toggle" :title="`点击${!toggleFlag ? '显示' : '隐藏'}`" @click="toggleOverviewInfo"
      v-if="isControlOverviewInfo"></div>

    <!-- 切换底图控件 -->
    <switch-base-layer @switchBaseLayerType="switchBaseLayerType" />

    <!--  气泡窗 -->
    <popup-common ref="refPopupCommon" :currentPageType="currentPageType" />

    <!-- 底部信息 -->
    <div class="copyright_info" v-show="true">
      <p>Copyright © CMDI.vip All Rights Reserved.</p>
      <ul>
        <li>
          <dl>
            <dt>经纬度:</dt>
            <dd>{{ currentLonlat }}</dd>
          </dl>
        </li>
        <li>
          <dl>
            <dt>当前层级:</dt>
            <dd>{{ currentZoomLevel }}</dd>
          </dl>
        </li>
      </ul>
    </div>

    <!-- 切换天地图token 弹窗 -->
    <set-token-dialog ref="refSetTokenDialog" />
  </section>
</template>

<script setup name="gis">
// vue - core
import { ref, onMounted, defineEmits, nextTick } from "vue";
// map - core
import * as mapUtils from "./mapUtils.js";
import menuUtils from './menuUtils.js'
import { boundingExtent } from 'ol/extent'
// 组件
import PopupCommon from "./components/popup/PopupCommon.vue"; // 气泡窗
import Lend from "./components/Lend.vue"; // 图例
import SwitchBaseLayer from "./components/SwitchBaseLayer.vue"; // 切换底图控件
import SetTokenDialog from "./components/SetTokenDialog.vue"; // 切换天地图token
// 工具
import { objIsEmpty } from "@/utils/index.js";
// 组件传参
import mittBus from "@/utils/mittBus"; // mitt
// 其他工具
import * as popupInner from "./components/popup/popupInner.js";
/* import { gisDataStore } from '@/store/modules/gis.js'  // store

const gisStoreData = gisDataStore() */

// 自定义事件
const emit = defineEmits([
  // 全局
  "getOlMapInstance",
  "getCurrentViewData",
  "reflashMap",
  'toggleOverviewInfo',
  // gis
  // ......
  // 投诉
  "getAreaData",
  "setCircleDialogData",
]);

const props = defineProps({
  // 当前页面类型
  currentPageType: {
    type: String,
    default: "",
  },
  // 是否需要自动加载数据 - false地图移动不加载,点击刷新加载
  isAutoRenderData: {
    type: Boolean,
    default: false,
  },
  // 是否显示图例
  isShowLend: {
    type: Boolean,
    default: true,
  },
  // 自组件是否需要控制概览信息显示隐藏
  isControlOverviewInfo: {
    type: Boolean,
    default: false,
  }
});

const { proxy } = getCurrentInstance();

let myOlMap = null;

const refPopupCommon = ref(null);
const refLend = ref(null);
const refSetTokenDialog = ref(null);

let toggleFlag = ref(true);  // 概览信息默认显示

const currentZoomLevel = ref(0);
const currentLonlat = ref("");

const isRemoveMap = ref(true); // 判断渲染地图时是否移除当前地图要素

let currentSingleObjData = ref({}); // 当前检索到的数据

const mapCommonData = {
  minRenderZoom: mapUtils.minRenderZoom, // 最小渲染层级
};

// 概览信息显示隐藏
const toggleOverviewInfo = () => {
  toggleFlag.value = !toggleFlag.value;

  emit('toggleOverviewInfo', toggleFlag.value)
}

// 地图加载完初始化做的一些操作
const mapInit = (olMap) => {
  // console.log('地图加载完初始化做的一些操作', olMap)
  myOlMap = olMap; // 赋值全局变量,为后续业务操作做准备

  // 地图加载完初始化做的一些操作[业务相关]
  emit("getOlMapInstance", olMap, mapCommonData); // 向外供出olMap实例,以及一些公共数据

  mapUtils.resetControls(olMap); // 初始化所有控件

  // 设置鼠标右键属性
  mapUtils.setContextmenu(
    olMap,
    (option) => {
      // console.log(option, mapUtils.menuMethods);
      setMenuMethods(option);
    },
    (next) => {
      // 根据具体页面配置菜单栏
      next(props.currentPageType);
    }
  );
};

// 设置鼠标右键菜单栏方法
const setMenuMethods = ({ option, feature, pixelPoint }) => {
  // console.log("设置鼠标右键菜单栏方法", option, feature, pixelPoint);

  // 点击地图隐藏气泡窗
  refPopupCommon.value.hidePopup();

  menuUtils.setMenuMethods(myOlMap, option, feature, pixelPoint, proxy)
};

// 地图加载完初始化后获取地图的一些信息
const getMapInitInfo = (olMap) => {
  // console.log("地图加载完初始化后获取地图的一些信息", olMap)

  // 获取可视区域数据 - 如果不需要自动加载
  if (!props.isAutoRenderData) {
    // console.log('刷新加载地图', props.isAutoRenderData)
    emit("getCurrentViewData", olMap); // 地图加载时会自动触发一次
  }

  // 圆数据
  emit("getAreaData", olMap);
};

// 设置地图
const setOlmap = (olMap) => {
  mapEvent(olMap); // 地图事件
};

// 切换底图
const switchBaseLayerType = (val) => {
  // console.log('切换底图', val)

  mapUtils.switchBaseLayer(myOlMap, val)
}

/**
 * 业务方法
 */
// 根据数据渲染Feature
const renderFeatureByData = (olMap, dataList, renderFeature, isLoop = true) => {
  // console.log("根据数据渲染Feature", dataList);

  refPopupCommon.value.hidePopup();

  if (isRemoveMap.value) {
    // removeAllLayer(myOlMap); // 移除所有默认图层
    removeAllDefaultLayer(myOlMap); // 移除所有默认图层
  }
  // }

  setTimeout(() => {
    if (isLoop) {
      // console.log(dataList)
      dataList.forEach((item) => {
        renderFeature(olMap, item);
      });
    } else {
      renderFeature(olMap, dataList);
    }
  }, 500);
};

// 通过气泡数据设置地图
const setMapByAutoPopupData = async (olMap, itemData, fixData, renderFeature) => {
  // console.log('通过气泡数据设置地图', olMap, itemData)

  const { arrData } = itemData

  if (fixData) {
    fixData(itemData);
  }

  // 选择数据隐藏气泡
  refPopupCommon.value.hidePopup();

  olMap.getView().setZoom(18);

  // 调用函数飞到指定的坐标
  flyToCoordinate(myOlMap, [itemData.longitude, itemData.latitude]);
  // mapUtils.flyToCoordinate(myOlMap, [itemData.longitude, itemData.latitude]);

  // 只显示一个Feature暂时使用此方案
  mapUtils.removeAllLayer(myOlMap); // 移除所有图层

  if (renderFeature) {
    renderFeature(olMap, [itemData]);
  }

  // 检索到当前要素
  setTimeout(() => {
    // 获取所有图层
    mapUtils.getAllFeature(olMap, (featureItem) => {
      let featureData = {};

      switch (featureItem.get("type")) {
        case "Marker":
          featureData = featureItem.get("pointData");
          break;
        case "Curve":
          featureData = featureItem.get("curveData");
          break;
      }

      if (featureData.cgi === itemData.cgi) {
        currentSingleObjData.value = { featureData, featureItem };
      }
    });
  }, 1000);

  // 将经纬度转换为屏幕坐标
  const pixelPoint = transformToPixelPoint(
    itemData.longitude,
    itemData.latitude
  );

  // 展示气泡窗
  mapUtils.setAutoPopup(pixelPoint, itemData, (popupObj) => {
    if (arrData) {
      popupObj.popupData = arrData
      popupObj.noFeature = true
    } else {
      let popupBack = document.querySelector(`#popupBack`)
      if (popupBack) {
        // 判断是否有返回图标,有则删除
        popupBack.remove();
      }
    }
    mapUtils.popupCommonConfig(
      olMap,
      pixelPoint,
      arrData ? popupInner.featuresPopupInner(popupObj) : popupInner.commonPopupInner(popupObj),
      (e) => {
        const { target } = e; // 事件对象

        // 点击气泡窗获取更多
        if (target.getAttribute("data-function") === "getMore") {
          // console.log("点击气泡fsdfdsfvfdaaa窗获取更多", popupObj.popupData);
          /* if (JSON.stringify(gisStoreData.currentPopupData) !== '{}') {
            itemData.currentPopupData = gisStoreData.currentPopupData
          } else {
            itemData.currentPopupData = popupObj.popupData
          } */
          mittBus.emit("popupDataGetMore", {
            currentPopupObj: itemData,
            callback: (popupData) => {

              mittBus.emit("showPopupDialog", popupData)
            },
          });
        }

        // 点击唯一标识显示具体气泡信息
        if (target.getAttribute("data-function") === "getSingleByFeatures") {
          mittBus.emit("getSingleByFeatures", { unique: target.getAttribute("data-unique").trim(), itemData });
        }

        // 返回
        // 点击popupDom返回
        if (target.getAttribute("data-function") === "popupBack") {
          mittBus.emit("popupBack");
        }
      }
    );
  });
};

/**
 * 接收其他组件派发的方法
 */
// 根据不同token初始化地图
mittBus.on("initOlMapByToken", () => {
  resetOlMap()  // 初始化地图
});
/**
 * 刷新地图
 * 各个组件如果需要刷新地图通过派发组件,最终到这个文件里面去做最终地图的刷新
 * resetFlag true 不带任何条件查询,查全部
 */
mittBus.on("reflashMap", (resetFlag) => {
  // console.log("刷新地图", resetFlag);

  // 判断是否需要带条件
  if (
    Boolean(resetFlag) &&
    myOlMap.getView().getZoom() > mapUtils.minRenderZoom
  ) {
    // 如果需要带条件,需要出发点全局派发事件去修改按钮dom
    mittBus.emit("resetBtn");
  }

  mapUtils.removeByReflashMap(myOlMap)  // 刷新地图需要移除的元素

  // 最终的刷新需要自组件派发事件到父组件完成派发
  emit("reflashMap", resetFlag, () => {
    nextTick(() => {
      setTimeout(() => {
        // 刷新完成之后,需要对检索到的数据去做定位
        if (!objIsEmpty(currentSingleObjData.value)) {
          // console.log("当前有检索条件", currentSingleObjData.value);

          let { featureData, featureItem } = currentSingleObjData.value;
          // console.log(featureItem);

          // 置顶图层
          mapUtils.featureToMaxTop(myOlMap, featureItem);
        }
      }, 1000);
    });
  });
});

// 删除所有绘制内容并取消状态 - 取消绘制(点线面)
mittBus.on("cancelDrawInteraction", () => {
  // console.log('drawPolygon')

  // 根据条件移除要素
  mapUtils.removeByCondition(myOlMap, currentFeature => {
    return currentFeature.get('drawType')
  })

  mapUtils.cancelDrawInteraction(myOlMap); // 绘制多边形
});

// 绘制多边形
mittBus.on("drawPolygon", () => {
  // console.log('drawPolygon')

  mapUtils.drawPolygon(myOlMap); // 绘制多边形
});

// 取消绘制多边形
mittBus.on("cancelPolygon", () => {
  // console.log('取消绘制多边形')

  mapUtils.cancelPolygon(myOlMap); // 绘制多边形
});

// 绘制圆形
mittBus.on("drawCircle", () => {
  // console.log('绘制圆形')

  mapUtils.drawCircle(myOlMap); // 绘制圆形
});

// 取消绘制圆形
mittBus.on("cancelCircle", () => {
  // console.log('取消绘制圆形')
});

// 测距
mittBus.on("testDistance", (next) => {
  // console.log("测距");

  // 测距
  mapUtils.testDistance(myOlMap, next);
});

// 取消测距
mittBus.on("cancelTestDistance", () => {
  // console.log("取消测距");

  mapUtils.cancelTestDistance(myOlMap); // 取消测距
});

/**
 * menu方法接收
 */
// 显示当前要素信息
mittBus.on("singleFeaturesClick", ({ feature, pixelPoint }) => {
  // console.log("显示当前要素信息", feature, pixelPoint);

  if (props.currentPageType === "complain") {
    emit("setCircleDialogData", feature);

    return;
  }
  // 点击单个feature - map - click事件
  singleFeaturesClick(myOlMap, feature, pixelPoint);
});
// 切换天地图token
mittBus.on("showSetTokenDialog", () => {
  refSetTokenDialog.value?.show();
})

/**
 * 地图工具方法
 */
// 地图事件
const mapEvent = (olMap) => {
  // 监听鼠标移动事件
  olMap.on("pointermove", (e) => {
    // 鼠标移动到feature区域时变为手形
    let pixel = olMap.getEventPixel(e.originalEvent);
    let hit = olMap.hasFeatureAtPixel(pixel);
    olMap.getTargetElement().style.cursor = hit ? "pointer" : "";

    currentLonlat.value = mapUtils.transformToLonlat(e.coordinate);
  });

  // 监听鼠标单击事件
  olMap.on("singleclick", (e) => {
    // console.log("点击地图", mapUtils.transformToLonlat(e.coordinate));

    // 判断当前是否为绘制状态
    if (mapUtils.getDrawInteraction(olMap)) {
      return
    }

    // 点击地图隐藏气泡窗
    refPopupCommon.value.hidePopup();

    // 点击地图隐藏右键菜单
    mapUtils.resetContextMenu();

    // const pixelPoint = e.coordinate; // 屏幕坐标
    let pixel = olMap.getEventPixel(e.originalEvent);
    let featureList = olMap.getFeaturesAtPixel(pixel); // 点击时获取所有features
    // console.log('wwww', featureList)

    const currentShapeFeature = (shape) => {
      return featureList.filter((item) => item.get("type") === shape);
    };

    const judgeShape = (shape) => {
      return JSON.stringify(currentShapeFeature(shape)) !== "[]";
    };

    // 在选区中点击时触发
    const innerClick = () => {
      // 图形中的要素
      let pointFeatureList = (currentShapeFeature("Marker")[0] || currentShapeFeature("Curve")[0])

      // 如果只有2个要素
      if (featureList.length === 2) {
        // 是标注
        if (pointFeatureList && pointFeatureList.get("type")) {
          singleFeaturesClick(myOlMap, pointFeatureList, e.coordinate);
        }
      } else {
        refPopupCommon.value.setFeaturesPopup(
          olMap,
          e.coordinate,
          featureList
        );
      }
    }

    // 如果feature数组存在(不为空)
    if (featureList) {
      // console.log("featureList", featureList);

      // 点击单个feature
      if (featureList.length === 1) {
        // console.log("无重叠,单个feature", featureList);

        // refPopupCommon.value.showPopup(); // 需要气泡时弹出

        // 不显示气泡窗的要素禁止弹窗
        if (currentShapeFeature("Circle").length !== 0) {
          refPopupCommon.value.hidePopup();
        }

        // 点击单个feature - map - click事件
        singleFeaturesClick(olMap, featureList[0], e.coordinate);
      }

      // 多个feature
      if (featureList.length > 1) {
        // console.log("有重叠,多个feature", featureList);

        // console.log(currentShapeFeature("Circle"))

        // 如果存在聚合
        if (judgeShape("Cluster")) {
          // 点击地图隐藏气泡窗
          refPopupCommon.value.hidePopup();
          // console.log(featureList)
          singleFeaturesClick(olMap, featureList[0], e.coordinate);
        }

        // 点击时需要显示标注的选区
        if (judgeShape("Circle") || judgeShape("Polygon")) {
          innerClick()
        }

        // 如果重叠区存在圆形
        if (judgeShape("Circle")) {
          // console.log('点击圆区域内要素', featureList)

          /* if (pointFeatureList.get("bussinessType") === "analysisMarker") {
            return
          } */
        } else if (judgeShape("Polygon")) {
          // console.log('点击多边形区域内要素', featureList)
          /* let featureItem = featureList.filter(
            (item) => item.get("type") === "Polygon"
          );
          setPolygonDialogData(featureItem[0]); */
        } else {
          // 点击扇形弹出气泡
          if (
            featureList[0].get("type") === "Curve" ||
            featureList[0].get("type") === "Marker"
          ) {
            // refPopupCommon.value.showPopup(); // 需要气泡时弹出
            refPopupCommon.value.setFeaturesPopup(
              olMap,
              e.coordinate,
              featureList
            );
          }
        }
      }
    }
  });

  // 监听鼠标拖动地图事件
  olMap.on("moveend", (e) => {
    // console.log('拖拽移动触发事件', e)

    let view = myOlMap.getView();
    let zoom = view.getZoom();
    currentZoomLevel.value = zoom.toFixed(2);
    // console.log("当前缩放级别为:", zoom, mapUtils.minRenderZoom);

    // 获取当前中心点坐标
    let center = view.getCenter();
    mittBus.emit("getCurrentCenter", mapUtils.transformToLonlat(center));

    mittBus.emit("getCurrentZoom", {
      zoom,
      minRenderZoom: mapUtils.minRenderZoom,
    });

    if (props.isAutoRenderData) {
      // console.log('移动加载地图', props.isAutoRenderData)
      mapUtils.removeAllDefaultLayer(olMap)  // 移除默认要素
      emit("getCurrentViewData", olMap); // 刷新地图
    }
  });
};

// 点击单个feature - map - click事件
const singleFeaturesClick = (olMap, featureItem, pixelPoint) => {
  // console.log("无重叠,单个feature", featureItem, featureItem.get("type"));

  if (featureItem && featureItem.get('drawType') && mapUtils.getDrawInteraction(olMap)) {
    return
  }

  let popupData = null;

  // 点击聚合
  if (featureItem && featureItem.get("type") === "Cluster") {
    refPopupCommon.value.hidePopup();

    let clusterFeatureList = featureItem.get('features')

    if (clusterFeatureList.length === 1) {
      popupData = clusterFeatureList[0].get("pointData");
      refPopupCommon.value.setCommonPopup(olMap, pixelPoint, popupData, clusterFeatureList[0]);

      return
    }

    const extent = boundingExtent(
      clusterFeatureList.map((r) => r.getGeometry().getCoordinates())
    );
    console.log(extent)
    olMap
      .getView()
      .fit(extent, { duration: 1000, padding: [50, 50, 50, 50] });

    /* console.log(mapUtils.getLayerByFeature(olMap, featureItem))

    let currentLayer = mapUtils.getLayerByFeature(olMap, featureItem)

    currentLayer.getFeatures(pixelPoint).then(clickedFeatures => {
      console.log(clickedFeatures)
    }) */


    /* olMap.getAllLayers().forEach((item, index) => {
      if (item.get('type') === 'clusterLayer') {
        console.log(item)
      }
    }) */
  }

  // 点击点标注
  if (featureItem && featureItem.get("type") === "Marker") {
    // console.log('Marker点标注', featureItem);

    popupData = featureItem.get("pointData");
    // console.log('获取点标注数据', popupData)

    refPopupCommon.value.setCommonPopup(olMap, pixelPoint, popupData, featureItem);
  }

  // 点击扇形区域
  if (featureItem && featureItem.get("type") === "Curve") {
    // console.log('点击扇形区域', featureItem);

    popupData = featureItem.get("curveData");
    // console.log('获取扇形区数据', popupData)

    refPopupCommon.value.setCommonPopup(olMap, pixelPoint, popupData);
  }

  // 点击圆形区域
  if (featureItem && featureItem.get("type") === "Circle") {
    // console.log("点击圆形区域", featureItem);

    setCircleDialogData(featureItem);
  }

  // 点击多边形
  if (featureItem && featureItem.get("type") === "Polygon") {
    // console.log("点击多边形", featureItem.get("polygonData"));

    const polygonData = featureItem.get("polygonData");

    // 网格
    if (featureItem.get('businessType') === "grid") {
      // console.log("点击网格数据", polygonData);
      // return;
    }

    // 弹出多边形数据
    setPolygonDialogData(featureItem);
  }
};

// 点击圆形区域获取数据
const setCircleDialogData = (feature) => {
  let circleData = feature.get('circleData')
  // console.log(circleData)

  mittBus.emit("setCircleDialogData", circleData);
};

// 弹出多边形数据
const setPolygonDialogData = (featureItem) => {
  const { coords, lonlat, name } = featureItem.get("polygonData");

  mittBus.emit("setPolygonDialogData", { lonlat, name });
};

/**
 * 地图方法供出
 */

// 获取可视区域坐标
const getCurrentViewPosition = (olMap) => {
  return mapUtils.getCurrentViewPosition(olMap);
};

// 屏幕坐标转换
const transformToPixelPoint = (lon, lat) => {
  return mapUtils.transformToPixelPoint(lon, lat)
}

// 添加圆
const addCircle = (olMap, item, circleConfig = {}, isFlicker, isHide) => {
  mapUtils.addCircle(olMap, item, circleConfig, isFlicker, isHide);
};

// 添加圆
const addCircle2 = (olMap, item, circleConfig = {}) => {
  mapUtils.addCircle2(olMap, item, circleConfig);
};

// 添加闪烁点
const addFlickerPoint = (olMap, pixelPoint, className, next) => {
  mapUtils.addFlickerPoint(olMap, pixelPoint, className, next);
}

// 加载kml
const loadKML = (olMap, text) => {
  mapUtils.loadKML(olMap, text);
};

// 移除所有图层
const removeAllLayer = (olMap) => {
  // console.log(olMap, type);

  mapUtils.removeAllLayer(olMap);
};

// 根据类型移除图层
const removeLayerByType = (olMap, type) => {
  // console.log(olMap, type);

  mapUtils.removeLayerByType(olMap, type); // 移除所有图层
};
// 根据业务类型移除图层
const removeLayerByBusinessType = (olMap, type) => {
  // console.log(olMap, type);

  mapUtils.removeLayerByBusinessType(olMap, type); // 移除所有图层
};

// 批量添加点
const addPoint = (olMap, pointList, src, pointConfig = {}, zIndex) => {
  mapUtils.addPoint(olMap, pointList, src, pointConfig, zIndex);
};

// 点聚合
const setCluster = (olMap, dataList, src) => {
  mapUtils.setCluster(olMap, dataList, src);
};

// 移除所有默认图层
const removeAllDefaultLayer = (olMap) => {
  mapUtils.removeAllDefaultLayer(olMap);
}

// 设置 Features 样式
const setFeaturesStyle = (fillColor, strokeColor) => {
  return mapUtils.setFeaturesStyle(fillColor, strokeColor);
};

// 批量添加扇形
const addCurve = (olMap, pointList, isResetStyle) => {
  if (isResetStyle) {
    mapUtils.addCurve(olMap, pointList, (item) => {
      return isResetStyle(item);
    });
  } else {
    mapUtils.addCurve(olMap, pointList);
  }
};

const flyToCoordinate = (OlMap, lonlat) => {
  mapUtils.flyToCoordinate(OlMap, lonlat);
};

// 创建多边形
const createPolygon = (olMap, { coords, lonlat, polygonData }, polygonConfig, next, style) => {
  mapUtils.createPolygon(olMap, { coords, lonlat, polygonData }, polygonConfig, next, style);
};

// 创建文字图层
const addTextPoint = (olMap, text, position, businessType, textOverlayConfig = {}, isRemove) => {
  mapUtils.addTextPoint(olMap, text, position, { businessType, ...textOverlayConfig }, isRemove);
};

// 获取多边形中心点
const getPolygonCenter = (feature) => {
  return mapUtils.getPolygonCenter(feature);
};

// 添加带箭头的线
const addArrowLine = (olMap, position, src, businessType) => {
  mapUtils.addArrowLine(olMap, position, src, businessType)
}

// 初始化地图
const resetOlMap = () => {
  // console.log("地图初始化");
  /* if (myOlMap) {
    mapUtils.destroyMap(myOlMap)
  } */
  destroyMap(myOlMap)

  const olMap = mapUtils.initOlMap("olMap"); // 初始化地图

  mapInit(olMap); // 地图加载完初始化做的一些操作
  getMapInitInfo(olMap); // 地图加载完初始化后获取地图的一些信息
  setOlmap(olMap); // 设置地图

  // console.log("地图加载完成");
}

// 销毁地图
const destroyMap = (olMap) => {
  if (olMap) {
    mapUtils.destroyMap(olMap)
  }
}

/**
 * vue生命周期函数
 * 挂载后触发
 */
onMounted(() => {
  resetOlMap()  // 初始化地图

  switchBaseLayerType('t3imgPrivatization')  // 页面初始化时加载默认底图(有时候加载不出来)
});

/**
 * 暴露方法
 */
defineExpose({
  renderFeatureByData, // 根据数据渲染Feature
  setMapByAutoPopupData, // 通过气泡数据设置地图

  // 地图方法供出
  destroyMap, // 销毁地图
  getCurrentViewPosition, // 获取可视区域坐标
  transformToPixelPoint,  // 屏幕坐标转换
  addCircle, // 添加圆
  addCircle2, // 添加圆
  addFlickerPoint, // 添加闪烁点
  loadKML, // 加载kml
  removeAllLayer, // 移除所有图层
  removeLayerByType, // 根据类型移除图层
  removeLayerByBusinessType, // 根据业务类型移除图层
  addPoint, // 批量添加点
  setCluster,  // 点聚合
  removeAllDefaultLayer,  // 移除所有默认图层
  addCurve, // 批量添加扇形
  createPolygon, // 创建多边形
  flyToCoordinate, // 飞到指定的坐标
  setFeaturesStyle, // 设置 Features 样式
  addTextPoint, // 创建文字图层
  getPolygonCenter, // 获取多边形中心点
  addArrowLine,  // 添加带箭头的线
});
</script>

<style lang="scss">
$zoomMargin: 10em;

.ol_map_wrap {
  position: absolute;
  width: 100%;
  height: 100%;
  min-width: 1250px;

  &.nav_ol_map {
    .ol_map {

      // 控件相关
      .ol-overlaycontainer-stopevent {
        .ol-scale-line {
          bottom: 76px;
        }
      }
    }
  }

  .ol_map {
    width: 100%;
    height: 100%;
    // overflow: hidden;

    .ol-overlaycontainer-stopevent {
      .ol-overlay-container {
        &:first-child {
          z-index: 1;
        }

        &:not(:first-child) {
          z-index: -999;
          pointer-events: none !important;
        }
      }
    }

    .flicker_point {
      height: 50px;
      width: 50px;
      border-radius: 50%;
      background: rgba(255, 0, 0, 0.9);
      transform: scale(0);
      animation: flickerAnimation 3s;
      animation-iteration-count: infinite;
      cursor: pointer;

      &.complain_flicker_point {
        height: 75px;
        width: 75px;
      }
    }

    @keyframes flickerAnimation {
      to {
        transform: scale(2);
        background: rgba(255, 0, 0, 0.1);
      }
    }

    .ol-viewport {
      overflow: initial !important;
    }

    // 控件相关
    .ol-overlaycontainer-stopevent {
      // 全屏
      /* .ol-full-screen {
        position: absolute;
        top: -34px;
        right: 108px;
      } */

      // 缩放
      .ol-zoom,
      .ol-zoomslider {
        left: initial;
        right: 0.5em;
      }

      .ol-zoom {
        top: calc(0.5em + $zoomMargin);
      }

      .ol-zoomslider {
        top: calc(4.5em + $zoomMargin);
      }

      // 比例尺
      .ol-scale-line {
        position: absolute;
        // bottom: 76px;
        bottom: 38px;
        background: initial;
      }

      /* // 鹰眼
      .ol-overviewmap {
        position: absolute;
        left: 0;
        bottom: 20px;

        .ol-overviewmap-map {
          width: 100px;
          height: 100px;
        }
      } */
    }
  }
}

.menu_wrap {
  padding: 3px;
  max-height: 157px;
  background: rgba(255, 255, 255, 0.8);
  border: solid 1px #777777;
  border-radius: 5px;
  overflow: auto;

  ul {
    padding-bottom: 3px;

    li {
      padding: 5px 10px;
      font-size: 12px;
      color: #000;
      border-radius: 3px 3px 0 0;
      cursor: pointer;
      transition: 0.3s linear;

      &:hover {
        color: #715f5f;
        background: rgba(128, 128, 128, 0.25);
        transition: 0.3s linear;
      }

      &:not(:last-child) {
        margin-bottom: 3px;
        border-bottom: solid 1px #b3b3b3;
      }
    }
  }
}

.overview_info_toggle {
  position: absolute;
  right: 9px;
  top: 45px;
  width: 25px;
  height: 25px;
  z-index: 1;
  background: url("./icon/expand.svg") center center no-repeat;
  background-size: 100% 100%;
  cursor: pointer;
}

.copyright_info {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  min-width: 800px;
  min-height: 20px;
  display: flex;
  justify-content: space-between;
  padding: 0 25px 0 12px;
  z-index: 1;
  background: #8a8888c4;

  p,
  dt,
  dd {
    height: 25px;
    line-height: 28px;
    font-size: 12px;
  }

  ul {
    li {
      display: inline-block;

      &:first-child {
        dd {
          min-width: 350px;
        }
      }

      dl {

        dt,
        dd {
          display: inline-block;
        }
      }
    }
  }
}
</style>

svg

  • back.svg
txt
<svg t="1713927038909" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1547" width="200" height="200"><path d="M482.7 249.9V106.1c0-37.4-45.3-56.2-71.7-29.7L140.3 347c-16.4 16.4-16.4 43 0 59.4L410.9 677c26.5 26.5 71.7 7.7 71.7-29.7v-155c96.1-0.3 271.5-10.7 271.5 227.7 0 118.1-92.8 216.8-216 239.6 198.1-24.4 326-236 326-361.9 0.1-292.6-309.4-346.3-381.4-347.8z" fill="#494949" p-id="1548"></path></svg>
<svg t="1713927038909" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1547" width="200" height="200"><path d="M482.7 249.9V106.1c0-37.4-45.3-56.2-71.7-29.7L140.3 347c-16.4 16.4-16.4 43 0 59.4L410.9 677c26.5 26.5 71.7 7.7 71.7-29.7v-155c96.1-0.3 271.5-10.7 271.5 227.7 0 118.1-92.8 216.8-216 239.6 198.1-24.4 326-236 326-361.9 0.1-292.6-309.4-346.3-381.4-347.8z" fill="#494949" p-id="1548"></path></svg>

OpenlayerBaseMap/components/PopupCommon.vue

vue
<template>
  <div id="popup" class="ol-popup popup_toggle" v-show="isShowPopup">
    <div class="popup_wrap">
      <a href="#" id="popup-closer" class="ol-popup-closer"></a>
      <div id="popup-content" class="popup-content"></div>
    </div>
  </div>

  <!-- 气泡详情弹窗 -->
  <!-- gis页面 -->
  <gis-popup-detail-dialog ref="refGisPopupDetailDialog" />
  <!-- 投诉页面 -->
  <complain-popup-detail-dialog ref="refComplainPopupDetailDialog" />
</template>

<script setup lang="jsx">
// vue core
import { nextTick, ref, h, render } from "vue";
// map data
import * as popupInner from "./popupInner";
// import { gisDataStore } from '@/store/modules/gis.js'  // store
// map core
import * as mapUtils from "../../mapUtils.js";
// 组件传参
import mittBus from "@/utils/mittBus"; // mitt
// 组件
import GisPopupDetailDialog from "@/views/gis/components/popup/GisPopupDetailDialog.vue";
import ComplainPopupDetailDialog from "@/views/complain/components/popup/ComplainPopupDetailDialog.vue";

// 如果popup不设置overflow的话,会在左下角显示,这里在一开始进行隐藏
let isShowPopup = ref(false);
let currentPopupObj = {};

let refGisPopupDetailDialog = ref(null);
let refComplainPopupDetailDialog = ref(null);

// const { setCurrentPopupData } = gisDataStore()

const props = defineProps({
  currentPageType: {
    type: String,
    default: "",
  },
});

/**
 * 接收其他组件派发的方法
 */
// 显示气泡弹出窗
mittBus.on("showPopupDialog", (popupData) => {
  // console.log(popupData, props.currentPageType);
  showPopupDialog(popupData, props.currentPageType); // 显示气泡弹出窗
});
// 点击唯一标识显示具体气泡信息
mittBus.on("getSingleByFeatures", ({ unique, itemData }) => {
  // console.log('点击唯一标识显示具体气泡信息', unique, itemData);

  let uniqueArr = unique.split(':')

  const { arrData } = itemData
  currentPopupObj.arrData = arrData

  arrData.forEach(item => {
    if (item[uniqueArr[0]] === uniqueArr[1]) {
      currentPopupObj.currentPopupData = item

      popupInner.commonPopupInner(currentPopupObj)
      setPopupBackDom(currentPopupObj)
    }
  })
});
// 点击popupDom返回
mittBus.on("popupBack", () => {
  // popupObj.popupData = arrData
  currentPopupObj.popupData = currentPopupObj.arrData
  currentPopupObj.noFeature = true
  popupInner.featuresPopupInner(currentPopupObj)
  // popupBack();
});

// 设置返回气泡窗返回dom
const setPopupBackDom = (inner) => {
  let content = document.getElementById("popup-content");
  popupInner.commonPopupInner(inner)  // jsx不需要赋值

  const backDom = document.createElement("b");
  backDom.setAttribute("data-function", "popupBack");
  backDom.setAttribute("id", "popupBack");
  backDom.setAttribute("title", "返回");
  backDom.innerHTML = "";
  content.appendChild(backDom);
}

// 显示气泡弹出窗
const showPopupDialog = (popupData, currentPageType) => {

  // 弹出相对应气泡窗
  switch (currentPageType) {
    case "gis":
      nextTick(() => {
        refGisPopupDetailDialog.value.show(popupData);
      });
      break;
    case "complain":
      nextTick(() => {
        refComplainPopupDetailDialog.value.show(popupData);
      });
      break;
  }
};

const showPopup = () => {
  isShowPopup.value = true;
};

const hidePopup = () => {
  isShowPopup.value = false;
};

// 通用信息展示弹窗
const setCommonPopup = (olMap, pixelPoint, popupData, featureItem) => {
  // console.log("点击标注弹出气泡", olMap, popupData, featureItem);

  // 判断是否有返回图标,有则删除 - 不为业务气泡窗不删除
  let popupBackDom = document.querySelector(`#popupBack`)
  if (popupBackDom) {
    if (featureItem && !featureItem.get('businessType')) {
      popupBackDom.remove();
    }
  }

  // 经纬度
  let coordinate = mapUtils.transformToLonlat(pixelPoint);
  // 点击尺 (这里是尺(米),并不是经纬度);
  let hdms = mapUtils.getHdms(pixelPoint); // 转换为经纬度显示

  const popupObj = {
    name: "通用信息展示弹窗",
    hdms,
    coordinate,
    popupData, // 气泡窗业务数据
  };
  currentPopupObj = popupObj;

  // 修改通用展示窗数据 - 子组件使用
  mittBus.emit("fixCommonPopupData", popupObj);

  mapUtils.setPopup(
    olMap,
    pixelPoint,
    popupInner.commonPopupInner(popupObj, featureItem),
    (event) => {
      popupClickEvent(event);
    }
  );
};

// 点击多个feature弹出气泡
const setFeaturesPopup = (olMap, pixelPoint, popupData) => {
  // console.log("点击多个feature弹出气泡", olMap);
  // console.log(popupData)

  // 经纬度
  let coordinate = mapUtils.transformToLonlat(pixelPoint);
  // 点击尺 (这里是尺(米),并不是经纬度);
  let hdms = mapUtils.getHdms(pixelPoint); // 转换为经纬度显示

  let popupTypeArr = [];
  popupData.forEach((item) => {
    switch (item.get("type")) {
      case "Marker":
        popupTypeArr.push("标注点");
        break;
      case "Curve":
        popupTypeArr.push("扇区");
        break;
    }
  });

  const popupObj = {
    name: popupTypeArr.length + "个重叠要素",
    type: Array.from(new Set(popupTypeArr)).join(","), // 去重转字符串以逗号隔开
    hdms, // 经纬度
    coordinate, // 坐标
    popupData, // 窗口业务数据 - 这里指所有的feature
  };
  //   console.log(hdms);
  currentPopupObj = popupObj;

  mapUtils.setPopup(
    olMap,
    // e.coordinate,
    pixelPoint,
    popupInner.featuresPopupInner(popupObj, (currentDataList) => {
      // console.log("输出业务数据", currentDataList);

      currentPopupObj.currentDataList = currentDataList;
    }),
    (event) => {
      popupClickEvent(event);
    }
  );
};

// 气泡弹出窗点击事件
const popupClickEvent = async (e) => {
  // console.log("气泡弹出窗点击事件", e)

  const { target } = e; // 事件对象
  // console.log("点击气泡窗返回气泡窗中的dom对象", target);

  // 当前dom绑定的函数
  let dataFunction = target.getAttribute("data-function");

  // 点击气泡窗获取更多
  if (dataFunction === "getMore") {
    // console.log("点击气泡窗获取更多");
    getMore();
  }

  // 点击唯一标识显示具体气泡信息
  if (dataFunction === "getSingleByFeatures") {
    // console.log(target.getAttribute("data-unique"));

    getSingleByFeatures(target.getAttribute("data-unique"));
  }

  // 点击popupDom返回
  if (dataFunction === "popupBack") {
    popupBack();
  }
};

// 点击唯一标识显示具体气泡信息
const getSingleByFeatures = (unique) => {
  // console.log("点击唯一标识显示具体气泡信息", unique);

  if (!unique) {
    return
  }

  // console.log(currentPopupObj)
  let uniqueArr = unique.split(':')

  const getObjData = (item, type) => {
    if (item.get('noPopup')) {
      return
    }

    if (item.get(type)[uniqueArr[0]] === uniqueArr[1]) {
      currentPopupObj.currentPopupData = item.get(type)
      return item
    }
  }

  currentPopupObj.currentPopupFeature = currentPopupObj.popupData.filter(item => {
    switch (item.get('type')) {
      case 'Marker':
        return getObjData(item, 'pointData')
      case 'Curve':
        return getObjData(item, 'curveData')
    }
  })
  // console.log(currentPopupObj.currentPopupFeature, currentPopupObj.currentPopupData)

  setPopupBackDom(currentPopupObj, currentPopupObj.currentPopupFeature[0])
};

// 点击popupDom返回
const popupBack = () => {
  if (currentPopupObj.popupData instanceof Array) {
    popupInner.featuresPopupInner(currentPopupObj)
  }
};

// 点击气泡窗获取更多
const getMore = () => {
  // console.log("点击气泡窗获取更多"); 
  // 气泡窗点击更多 - 子组件使用
  // console.log('气泡窗点击更多 - 子组件使用', currentPopupObj)

  // mittBus.emit("popupDataGetMoreTest", 'fgdsfgsdfg');
  mittBus.emit("popupDataGetMore", {
    currentPopupObj,
    callback: (popupData) => {
      // console.log("fwdsdfsw", popupData);

      // 获取完数据后进行弹窗
      showPopupDialog(popupData, props.currentPageType); // 显示气泡弹出窗
    },
  });
};

/**
 * 暴露方法 - 供父组件执行
 */
defineExpose({
  showPopup,
  hidePopup,
  setCommonPopup,
  setFeaturesPopup,
});
</script>

<style lang="scss" scoped>
// $popupBg: rgba(111, 168, 247, 0.8);
$popupBg: rgba(255, 255, 255, 0.8);

.ol-popup {
  position: absolute;
  left: -50px;
  bottom: 12px;
  padding: 15px;
  padding-right: 6px;
  background: $popupBg;

  .popup_wrap {
    margin-top: 10px;
    min-height: 20px;
    overflow: auto;
  }

  &::after,
  &::before {
    top: 100%;
    border: solid transparent;
    content: " ";
    height: 0;
    width: 0;
    position: absolute;
    pointer-events: none;
  }

  .ol-popup:after {
    border-top-color: $popupBg;
    border-width: 10px;
    left: 48px;
    margin-left: -10px;
  }

  &::before {
    border-top-color: $popupBg;
    border-width: 11px;
    left: 48px;
    margin-left: -11px;
  }

  .ol-popup-closer {
    position: absolute;
    top: 3px;
    right: 6px;
    width: 10px;
    height: 10px;
    cursor: pointer;

    &::after {
      content: "✖";
    }
  }

  /**
     * 此处开始气泡窗内容样式
     */
  :deep .popup-content {
    margin-right: 8px;
    margin-top: 5px;

    p,
    span,
    h3,
    li,
    dt,
    dd {
      font-size: 12px;
      color: #514b4b;
    }

    // 重叠feature气泡窗样式
    ul.features_top {
      li {
        margin-bottom: 5px;
        // color: #494949;
        color: #222222;
      }
    }

    .feature_item {
      margin-bottom: 10px;
      padding: 6px;
      transition: 0.2s linear;
      border-bottom: dashed 1px #707070;
      border-radius: 3px 3px 0 0;
      cursor: pointer;

      &:hover {
        background: rgb(193 231 254 / 80%);
        transition: 0.3s linear;
      }

      span {
        display: block;
        margin-bottom: 3px;
      }

      dl {
        margin-bottom: 2px;

        dt,
        dd {
          display: inline-block;
        }
      }
    }

    .common_simple_popup_item,
    .common_popup_item {
      &+b {
        position: absolute;
        top: 4px;
        left: 8px;
        width: 17px;
        height: 20px;
        background: url("../../icon/back.svg") center center no-repeat;
        background-size: 100% 100%;
        cursor: pointer;
      }
    }

    .common_simple_popup_item {
      min-width: 88px;
      max-width: 300px;
    }

    .common_features_popup_item {
      position: relative;
      min-width: 400px;
      max-height: 260px;
    }

    // 通用气泡窗样式
    .common_popup_item {
      position: relative;

      min-width: 400px;
      max-height: 260px;

      .el-descriptions {
        .el-descriptions__body {
          .el-descriptions__table {
            tr {
              td {
                padding: 0 7px;
              }

              td:first-child {
                width: 80px !important;

              }
            }
          }
        }
      }

      // background: #f00;
      h3 {
        font-size: 15px;
        margin-bottom: 10px;
        color: #494949;
      }

      ul {
        li {
          margin-bottom: 6px;

          dl {

            dt,
            dd {
              display: inline-block;
            }

            dt {
              font-weight: bold;
              margin-right: 5px;
              font-size: 13px;
            }
          }
        }
      }

      span.get_more {
        position: absolute;
        // right: -5px;
        left: 50%;
        transform: translateX(-50%);
        bottom: -33px;
        padding: 6px 10px;
        background: #70b5fa;
        color: #fff;
        border-radius: 6px;
        transition: 0.2s linear;
        cursor: pointer;

        &:hover {
          background: #2c94fe;
          transition: 0.3s linear;
        }
      }
    }
  }
}
</style>
<template>
  <div id="popup" class="ol-popup popup_toggle" v-show="isShowPopup">
    <div class="popup_wrap">
      <a href="#" id="popup-closer" class="ol-popup-closer"></a>
      <div id="popup-content" class="popup-content"></div>
    </div>
  </div>

  <!-- 气泡详情弹窗 -->
  <!-- gis页面 -->
  <gis-popup-detail-dialog ref="refGisPopupDetailDialog" />
  <!-- 投诉页面 -->
  <complain-popup-detail-dialog ref="refComplainPopupDetailDialog" />
</template>

<script setup lang="jsx">
// vue core
import { nextTick, ref, h, render } from "vue";
// map data
import * as popupInner from "./popupInner";
// import { gisDataStore } from '@/store/modules/gis.js'  // store
// map core
import * as mapUtils from "../../mapUtils.js";
// 组件传参
import mittBus from "@/utils/mittBus"; // mitt
// 组件
import GisPopupDetailDialog from "@/views/gis/components/popup/GisPopupDetailDialog.vue";
import ComplainPopupDetailDialog from "@/views/complain/components/popup/ComplainPopupDetailDialog.vue";

// 如果popup不设置overflow的话,会在左下角显示,这里在一开始进行隐藏
let isShowPopup = ref(false);
let currentPopupObj = {};

let refGisPopupDetailDialog = ref(null);
let refComplainPopupDetailDialog = ref(null);

// const { setCurrentPopupData } = gisDataStore()

const props = defineProps({
  currentPageType: {
    type: String,
    default: "",
  },
});

/**
 * 接收其他组件派发的方法
 */
// 显示气泡弹出窗
mittBus.on("showPopupDialog", (popupData) => {
  // console.log(popupData, props.currentPageType);
  showPopupDialog(popupData, props.currentPageType); // 显示气泡弹出窗
});
// 点击唯一标识显示具体气泡信息
mittBus.on("getSingleByFeatures", ({ unique, itemData }) => {
  // console.log('点击唯一标识显示具体气泡信息', unique, itemData);

  let uniqueArr = unique.split(':')

  const { arrData } = itemData
  currentPopupObj.arrData = arrData

  arrData.forEach(item => {
    if (item[uniqueArr[0]] === uniqueArr[1]) {
      currentPopupObj.currentPopupData = item

      popupInner.commonPopupInner(currentPopupObj)
      setPopupBackDom(currentPopupObj)
    }
  })
});
// 点击popupDom返回
mittBus.on("popupBack", () => {
  // popupObj.popupData = arrData
  currentPopupObj.popupData = currentPopupObj.arrData
  currentPopupObj.noFeature = true
  popupInner.featuresPopupInner(currentPopupObj)
  // popupBack();
});

// 设置返回气泡窗返回dom
const setPopupBackDom = (inner) => {
  let content = document.getElementById("popup-content");
  popupInner.commonPopupInner(inner)  // jsx不需要赋值

  const backDom = document.createElement("b");
  backDom.setAttribute("data-function", "popupBack");
  backDom.setAttribute("id", "popupBack");
  backDom.setAttribute("title", "返回");
  backDom.innerHTML = "";
  content.appendChild(backDom);
}

// 显示气泡弹出窗
const showPopupDialog = (popupData, currentPageType) => {

  // 弹出相对应气泡窗
  switch (currentPageType) {
    case "gis":
      nextTick(() => {
        refGisPopupDetailDialog.value.show(popupData);
      });
      break;
    case "complain":
      nextTick(() => {
        refComplainPopupDetailDialog.value.show(popupData);
      });
      break;
  }
};

const showPopup = () => {
  isShowPopup.value = true;
};

const hidePopup = () => {
  isShowPopup.value = false;
};

// 通用信息展示弹窗
const setCommonPopup = (olMap, pixelPoint, popupData, featureItem) => {
  // console.log("点击标注弹出气泡", olMap, popupData, featureItem);

  // 判断是否有返回图标,有则删除 - 不为业务气泡窗不删除
  let popupBackDom = document.querySelector(`#popupBack`)
  if (popupBackDom) {
    if (featureItem && !featureItem.get('businessType')) {
      popupBackDom.remove();
    }
  }

  // 经纬度
  let coordinate = mapUtils.transformToLonlat(pixelPoint);
  // 点击尺 (这里是尺(米),并不是经纬度);
  let hdms = mapUtils.getHdms(pixelPoint); // 转换为经纬度显示

  const popupObj = {
    name: "通用信息展示弹窗",
    hdms,
    coordinate,
    popupData, // 气泡窗业务数据
  };
  currentPopupObj = popupObj;

  // 修改通用展示窗数据 - 子组件使用
  mittBus.emit("fixCommonPopupData", popupObj);

  mapUtils.setPopup(
    olMap,
    pixelPoint,
    popupInner.commonPopupInner(popupObj, featureItem),
    (event) => {
      popupClickEvent(event);
    }
  );
};

// 点击多个feature弹出气泡
const setFeaturesPopup = (olMap, pixelPoint, popupData) => {
  // console.log("点击多个feature弹出气泡", olMap);
  // console.log(popupData)

  // 经纬度
  let coordinate = mapUtils.transformToLonlat(pixelPoint);
  // 点击尺 (这里是尺(米),并不是经纬度);
  let hdms = mapUtils.getHdms(pixelPoint); // 转换为经纬度显示

  let popupTypeArr = [];
  popupData.forEach((item) => {
    switch (item.get("type")) {
      case "Marker":
        popupTypeArr.push("标注点");
        break;
      case "Curve":
        popupTypeArr.push("扇区");
        break;
    }
  });

  const popupObj = {
    name: popupTypeArr.length + "个重叠要素",
    type: Array.from(new Set(popupTypeArr)).join(","), // 去重转字符串以逗号隔开
    hdms, // 经纬度
    coordinate, // 坐标
    popupData, // 窗口业务数据 - 这里指所有的feature
  };
  //   console.log(hdms);
  currentPopupObj = popupObj;

  mapUtils.setPopup(
    olMap,
    // e.coordinate,
    pixelPoint,
    popupInner.featuresPopupInner(popupObj, (currentDataList) => {
      // console.log("输出业务数据", currentDataList);

      currentPopupObj.currentDataList = currentDataList;
    }),
    (event) => {
      popupClickEvent(event);
    }
  );
};

// 气泡弹出窗点击事件
const popupClickEvent = async (e) => {
  // console.log("气泡弹出窗点击事件", e)

  const { target } = e; // 事件对象
  // console.log("点击气泡窗返回气泡窗中的dom对象", target);

  // 当前dom绑定的函数
  let dataFunction = target.getAttribute("data-function");

  // 点击气泡窗获取更多
  if (dataFunction === "getMore") {
    // console.log("点击气泡窗获取更多");
    getMore();
  }

  // 点击唯一标识显示具体气泡信息
  if (dataFunction === "getSingleByFeatures") {
    // console.log(target.getAttribute("data-unique"));

    getSingleByFeatures(target.getAttribute("data-unique"));
  }

  // 点击popupDom返回
  if (dataFunction === "popupBack") {
    popupBack();
  }
};

// 点击唯一标识显示具体气泡信息
const getSingleByFeatures = (unique) => {
  // console.log("点击唯一标识显示具体气泡信息", unique);

  if (!unique) {
    return
  }

  // console.log(currentPopupObj)
  let uniqueArr = unique.split(':')

  const getObjData = (item, type) => {
    if (item.get('noPopup')) {
      return
    }

    if (item.get(type)[uniqueArr[0]] === uniqueArr[1]) {
      currentPopupObj.currentPopupData = item.get(type)
      return item
    }
  }

  currentPopupObj.currentPopupFeature = currentPopupObj.popupData.filter(item => {
    switch (item.get('type')) {
      case 'Marker':
        return getObjData(item, 'pointData')
      case 'Curve':
        return getObjData(item, 'curveData')
    }
  })
  // console.log(currentPopupObj.currentPopupFeature, currentPopupObj.currentPopupData)

  setPopupBackDom(currentPopupObj, currentPopupObj.currentPopupFeature[0])
};

// 点击popupDom返回
const popupBack = () => {
  if (currentPopupObj.popupData instanceof Array) {
    popupInner.featuresPopupInner(currentPopupObj)
  }
};

// 点击气泡窗获取更多
const getMore = () => {
  // console.log("点击气泡窗获取更多"); 
  // 气泡窗点击更多 - 子组件使用
  // console.log('气泡窗点击更多 - 子组件使用', currentPopupObj)

  // mittBus.emit("popupDataGetMoreTest", 'fgdsfgsdfg');
  mittBus.emit("popupDataGetMore", {
    currentPopupObj,
    callback: (popupData) => {
      // console.log("fwdsdfsw", popupData);

      // 获取完数据后进行弹窗
      showPopupDialog(popupData, props.currentPageType); // 显示气泡弹出窗
    },
  });
};

/**
 * 暴露方法 - 供父组件执行
 */
defineExpose({
  showPopup,
  hidePopup,
  setCommonPopup,
  setFeaturesPopup,
});
</script>

<style lang="scss" scoped>
// $popupBg: rgba(111, 168, 247, 0.8);
$popupBg: rgba(255, 255, 255, 0.8);

.ol-popup {
  position: absolute;
  left: -50px;
  bottom: 12px;
  padding: 15px;
  padding-right: 6px;
  background: $popupBg;

  .popup_wrap {
    margin-top: 10px;
    min-height: 20px;
    overflow: auto;
  }

  &::after,
  &::before {
    top: 100%;
    border: solid transparent;
    content: " ";
    height: 0;
    width: 0;
    position: absolute;
    pointer-events: none;
  }

  .ol-popup:after {
    border-top-color: $popupBg;
    border-width: 10px;
    left: 48px;
    margin-left: -10px;
  }

  &::before {
    border-top-color: $popupBg;
    border-width: 11px;
    left: 48px;
    margin-left: -11px;
  }

  .ol-popup-closer {
    position: absolute;
    top: 3px;
    right: 6px;
    width: 10px;
    height: 10px;
    cursor: pointer;

    &::after {
      content: "✖";
    }
  }

  /**
     * 此处开始气泡窗内容样式
     */
  :deep .popup-content {
    margin-right: 8px;
    margin-top: 5px;

    p,
    span,
    h3,
    li,
    dt,
    dd {
      font-size: 12px;
      color: #514b4b;
    }

    // 重叠feature气泡窗样式
    ul.features_top {
      li {
        margin-bottom: 5px;
        // color: #494949;
        color: #222222;
      }
    }

    .feature_item {
      margin-bottom: 10px;
      padding: 6px;
      transition: 0.2s linear;
      border-bottom: dashed 1px #707070;
      border-radius: 3px 3px 0 0;
      cursor: pointer;

      &:hover {
        background: rgb(193 231 254 / 80%);
        transition: 0.3s linear;
      }

      span {
        display: block;
        margin-bottom: 3px;
      }

      dl {
        margin-bottom: 2px;

        dt,
        dd {
          display: inline-block;
        }
      }
    }

    .common_simple_popup_item,
    .common_popup_item {
      &+b {
        position: absolute;
        top: 4px;
        left: 8px;
        width: 17px;
        height: 20px;
        background: url("../../icon/back.svg") center center no-repeat;
        background-size: 100% 100%;
        cursor: pointer;
      }
    }

    .common_simple_popup_item {
      min-width: 88px;
      max-width: 300px;
    }

    .common_features_popup_item {
      position: relative;
      min-width: 400px;
      max-height: 260px;
    }

    // 通用气泡窗样式
    .common_popup_item {
      position: relative;

      min-width: 400px;
      max-height: 260px;

      .el-descriptions {
        .el-descriptions__body {
          .el-descriptions__table {
            tr {
              td {
                padding: 0 7px;
              }

              td:first-child {
                width: 80px !important;

              }
            }
          }
        }
      }

      // background: #f00;
      h3 {
        font-size: 15px;
        margin-bottom: 10px;
        color: #494949;
      }

      ul {
        li {
          margin-bottom: 6px;

          dl {

            dt,
            dd {
              display: inline-block;
            }

            dt {
              font-weight: bold;
              margin-right: 5px;
              font-size: 13px;
            }
          }
        }
      }

      span.get_more {
        position: absolute;
        // right: -5px;
        left: 50%;
        transform: translateX(-50%);
        bottom: -33px;
        padding: 6px 10px;
        background: #70b5fa;
        color: #fff;
        border-radius: 6px;
        transition: 0.2s linear;
        cursor: pointer;

        &:hover {
          background: #2c94fe;
          transition: 0.3s linear;
        }
      }
    }
  }
}
</style>

OpenlayerBaseMap/components/popupInner.js

js
/**
 * 气泡窗管理
 * @param {*} popupObj 
 * @returns 
 */

// 工具
import mittBus from "@/utils/mittBus"; // mitt - 组件传参工具
import { render } from 'vue';  // jsx渲染到dom需要使用vue自带的render函数

const innerDom = () => {
    return document.querySelector('#popup-content')
}

// 通用气泡窗
export const commonPopupInner = (popupObj, featureItem) => {
    mittBus.emit('setCommonPopupInner', {
        popupObj, featureItem, callback: (popupInnerDom) => {
            // 使用jsx,不需要return,直接将jsx结构render到dom即可
            render(popupInnerDom, innerDom())
        }
    })
}

// 多个feature气泡窗
export const featuresPopupInner = (popupObj, next) => {
    // console.log('多个feature气泡窗', popupObj)

    let featureDataList = []
    // 设置重叠要素气泡窗html
    mittBus.emit('setFeaturesPopupInner', {
        popupObj, callback: (popupInnerDom) => {
            render(popupInnerDom, innerDom())
        }, next: currentDataList => {
            featureDataList = currentDataList
        }
    })

    if (next) {
        next(featureDataList)
    }
}
/**
 * 气泡窗管理
 * @param {*} popupObj 
 * @returns 
 */

// 工具
import mittBus from "@/utils/mittBus"; // mitt - 组件传参工具
import { render } from 'vue';  // jsx渲染到dom需要使用vue自带的render函数

const innerDom = () => {
    return document.querySelector('#popup-content')
}

// 通用气泡窗
export const commonPopupInner = (popupObj, featureItem) => {
    mittBus.emit('setCommonPopupInner', {
        popupObj, featureItem, callback: (popupInnerDom) => {
            // 使用jsx,不需要return,直接将jsx结构render到dom即可
            render(popupInnerDom, innerDom())
        }
    })
}

// 多个feature气泡窗
export const featuresPopupInner = (popupObj, next) => {
    // console.log('多个feature气泡窗', popupObj)

    let featureDataList = []
    // 设置重叠要素气泡窗html
    mittBus.emit('setFeaturesPopupInner', {
        popupObj, callback: (popupInnerDom) => {
            render(popupInnerDom, innerDom())
        }, next: currentDataList => {
            featureDataList = currentDataList
        }
    })

    if (next) {
        next(featureDataList)
    }
}

OpenlayerBaseMap/components/DetailDialog.vue

vue
<template>
  <el-dialog width v-model="showDialog" :close-on-click-modal="false" :modal-append-to-body="false"
    :close-on-press-escape="false">
    <template #header>
      <slot name="DialogTitle"></slot>
    </template>
    <div class="container">
      <slot name="DialogContainer"></slot>
    </div>
    <template #footer>
      <span class="dialog-footer">
        <slot name="extendBtn"></slot>
        <el-button @click="showDialog = false">关闭</el-button>
      </span>
    </template>
  </el-dialog>
</template>

<script setup>
// core
import { ref } from "vue";

const props = defineProps({
  width: {
    type: String,
    default: "80%",
  },
});

const showDialog = ref(false);

/**
 * 父组件调弹框显示方法
 */
const show = () => {
  showDialog.value = true;
};

defineExpose({ show });
</script>
<template>
  <el-dialog width v-model="showDialog" :close-on-click-modal="false" :modal-append-to-body="false"
    :close-on-press-escape="false">
    <template #header>
      <slot name="DialogTitle"></slot>
    </template>
    <div class="container">
      <slot name="DialogContainer"></slot>
    </div>
    <template #footer>
      <span class="dialog-footer">
        <slot name="extendBtn"></slot>
        <el-button @click="showDialog = false">关闭</el-button>
      </span>
    </template>
  </el-dialog>
</template>

<script setup>
// core
import { ref } from "vue";

const props = defineProps({
  width: {
    type: String,
    default: "80%",
  },
});

const showDialog = ref(false);

/**
 * 父组件调弹框显示方法
 */
const show = () => {
  showDialog.value = true;
};

defineExpose({ show });
</script>

使用地图组件

index.vue

vue
<template>
  <openlayer-base-map class="xxxxxxxx_map" ref="refOpenlayerBaseMap" :currentPageType="'xxxxxxxx'" :isShowLend="false"
    @getOlMapInstance="getOlMapInstance" @getCurrentViewData="getCurrentViewData" :isAutoRenderData="true"
    :isControlOverviewInfo="true" @getCircleData="getXxxxxxxxData" @reflashMap="reflashMap"
    @toggleOverviewInfo="toggleOverviewInfo">

    <el-button type="primary" @click="reflashMap" class="reflash_map">刷新地图</el-button>

    <!-- 右侧信息 - 投诉信息 -->
    <xxxxxxxx-common v-show="isShowXxxxxxxxCommon" />

    <!-- 自定义圆形数据 -->
    <circle-info-dialog ref="refCircleInfoDialog" />
  </openlayer-base-map>
</template>

<script setup>
// vue - core
import { ref, onUnmounted } from "vue";
// 组件
import OpenlayerBaseMap from "@/components/OpenlayerBaseMap/index.vue";
import CircleInfoDialog from "./components/CircleInfoDialog.vue"; // 点击圆信息
import XxxxxxxxCommon from "./components/XxxxxxxxCommon.vue"; // 投诉信息
import { getPopupInnerDom, getPopupAnalysisDom, getFeaturesPopupInnerDom } from "./components/popup/xxxxxxxxPopup.jsx"; // 气泡窗dom
// api相关 - 工具
import { apiCommon, getNow, getFormulateDate, moreArraysIsEmpty } from "@/utils/index.js";
import * as xxxxxxxxApi from "@/api/xxxxxxxx/xxxxxxxx";
// 工具
import mittBus from "@/utils/mittBus"; // mitt - 组件传参工具
// svg
import xxxxxxxxMarker from "./icon/Xxxxxxxx_marker.svg";
import arrow from "./icon/arrow.svg";
import alarmPoint from "./icon/analysisPoint/alarmPoint.svg";
import interferencePoint from "./icon/analysisPoint/interferencePoint.svg";
import loadPoint from "./icon/analysisPoint/loadPoint.svg";
import performancePoint from "./icon/analysisPoint/performancePoint.svg";

const refOpenlayerBaseMap = ref(null); // 地图核心元素

const { proxy } = getCurrentInstance();

const refCircleDetailDialog = ref(null);
const refCircleInfoDialog = ref(null);

let isShowXxxxxxxxCommon = ref(true)

let myOlMap = null;
let myMapCommonData = null;

let xxxxxxxxDataList = null

/**
 * 接收其他组件派发的方法
 */
/**
 * 气泡窗
 */
// 设置通用气泡窗html
mittBus.on("setCommonPopupInner", ({ popupObj, featureItem, callback }) => {
  // console.log("设置通用气泡窗html", popupObj, featureItem, featureItem.get('bussinessType'), featureItem.get('bussinessType'), 'analysisMarker');

  if (featureItem && featureItem.get('bussinessType') === 'analysisMarker') {
    callback(getPopupAnalysisDom(popupObj));
  } else {
    callback(getPopupInnerDom(popupObj));
  }

});
// 设置重叠要素气泡窗html
mittBus.on('setFeaturesPopupInner', ({ popupObj, callback, next }) => {
  // console.log(popupObj, callback)

  callback(getFeaturesPopupInnerDom(popupObj, currentDataList => {
    // console.log(currentDataList)

    currentDataList = currentDataList.filter(item => item)
    // console.log(currentDataList)

    next(currentDataList)
  }));
})
//  气泡窗点击更多 - 子组件使用
mittBus.on("popupDataGetMore", async ({ currentPopupObj, callback }) => {
  // console.log("气泡窗点击更多", currentPopupObj);

  // 或者走接口,根据xxx获取详情
  const currentPopupAsyncObj = await apiCommon(XxxxxxxxApi.queryXxxxxxxxtInfo, {
    XxxxxxxxtId: currentPopupObj.popupData.XxxxxxxxtId,
  });
  currentPopupObj = currentPopupAsyncObj.data;

  // 获取完数据后进行弹窗
  callback(currentPopupObj);
});
//  数据分析
mittBus.on("analysisPointData", async (currentData) => {
  // console.log("数据分析", currentData.longitude, currentData.latitude);

  let startPixelPoint = refOpenlayerBaseMap.value.transformToPixelPoint(currentData.longitude, currentData.latitude)
  // console.log(currentPixelPoint)

  /* 分析状态[空 - 未匹配到分析结果.完成 - loadResult]
  未匹配到小区

  展示分析结果 */
  let analysisPointRes = await apiCommon(XxxxxxxxApi.queryXxxxxxxxtAnalysisResult, currentData.csId);
  // let analysisPointRes = await apiCommon(XxxxxxxxApi.queryXxxxxxxxtAnalysisResult, '20240603103828X13147');
  analysisPointRes = analysisPointRes.data;

  if (analysisPointRes) {
    const { alarmResult, interferenceResult, loadResult, performanceResult } = analysisPointRes
    const tempArr = [alarmResult, interferenceResult, loadResult, performanceResult]

    if (moreArraysIsEmpty(tempArr)) {
      proxy.$modal.msgWarning(`未匹配到小区`);
      return
    }

    refOpenlayerBaseMap.value.removeLayerByBusinessType(myOlMap, "analysisMarker"); // 根据类型移除图层 - 分析标注
    refOpenlayerBaseMap.value.removeLayerByBusinessType(myOlMap, "XxxxxxxxArrowLine"); // 根据类型移除图层 - 分析箭头
    refOpenlayerBaseMap.value.removeLayerByBusinessType(myOlMap, "XxxxxxxxTextPoint"); // 根据类型移除图层 - 分析文本

    // console.log(alarmResult, interferenceResult, loadResult, performanceResult)
    setXxxxxxxxAnalysisData(alarmResult, startPixelPoint, '故障分析', alarmPoint)
    setXxxxxxxxAnalysisData(interferenceResult, startPixelPoint, '高干扰分析', interferencePoint)
    setXxxxxxxxAnalysisData(loadResult, startPixelPoint, '性能分析', loadPoint)
    setXxxxxxxxAnalysisData(performanceResult, startPixelPoint, '高负荷分析', performancePoint)
  }
});
// 设置圆形数据
mittBus.on("setCircleDialogData", (circleData) => {
  refCircleInfoDialog.value.show(circleData);
});

/**
 * 工具方法
 */
// 分析数据批量处理要素
const setXxxxxxxxAnalysisData = (resDataList, startPixelPoint, typeTxt, pointIcon) => {
  if (resDataList.length !== 0) {
    resDataList.forEach(item => {

      item.analysisType = typeTxt

      let analysisPointItem = {
        lonlat: [item.longitude, item.latitude],
        pointData: item,
      }

      let endPixelPoint = refOpenlayerBaseMap.value.transformToPixelPoint(item.longitude, item.latitude)

      let position = [startPixelPoint, endPixelPoint]
      refOpenlayerBaseMap.value.addArrowLine(myOlMap, position, arrow, 'XxxxxxxxArrowLine')

      // 创建文本要素以显示距离
      refOpenlayerBaseMap.value.addTextPoint(myOlMap, typeTxt, [item.longitude, item.latitude], 'XxxxxxxxTextPoint')

      refOpenlayerBaseMap.value.addPoint(
        myOlMap,
        analysisPointItem,
        pointIcon,
        { bussinessType: 'analysisMarker', 'noPopup': true }, 999
      );
    })
  }
}

// 显示隐藏概览
const toggleOverviewInfo = (val) => {
  // console.log('显示隐藏概览', val)

  isShowXxxxxxxxCommon.value = val
}
// 刷新地图
const reflashMap = () => {
  refOpenlayerBaseMap.value.removeAllLayer(myOlMap)

  getXxxxxxxxData(myOlMap)
  getCurrentViewData(myOlMap)
}
// 获取可视区域坐标参数
const getCurrentPositionParams = (olMap) => {
  let viewPosition = refOpenlayerBaseMap.value.getCurrentViewPosition(olMap);
  // console.log("获取可视区域的左上角和右下角坐标", viewPosition)

  return {
    minLatitude: viewPosition.bottomRight[1],
    maxLatitude: viewPosition.topLeft[1],
    minLongitude: viewPosition.topLeft[0],
    maxLongitude: viewPosition.bottomRight[0],
  };
};

/**
 * 业务方法
 */
// 获取地图实例 - 地图加载完初始化做的一些操作[业务相关]
const getOlMapInstance = (olMap, mapCommonData) => {
  // console.log("获取地图实例 - 地图加载完初始化做的一些操作[业务相关]", olMap);

  myOlMap = olMap; // 赋值全局变量,为后续业务操作做准备

  myMapCommonData = mapCommonData;
};
// 获取可视区域数据 (主入口)
const getCurrentViewData = async (olMap) => {
  let params = {
    ...getCurrentPositionParams(olMap),
    // dateString: '2024-05-23'
    dateString: getNow()
  };

  xxxxxxxxDataList = await apiCommon(XxxxxxxxApi.queryXxxxxxxxtList, params);
  xxxxxxxxDataList = xxxxxxxxDataList.data;
  // console.log(xxxxxxxxDataList);

  renderFeatureByData(olMap, xxxxxxxxDataList);
};

// 根据数据渲染Feature
const renderFeatureByData = (olMap, dataList) => {
  refOpenlayerBaseMap.value.renderFeatureByData(
    olMap,
    dataList,
    (olMap, item) => {
      // console.log(olMap, item);

      item.dispatchTime = getFormulateDate(item.dispatchTime)
      item.isNeedAnalysis = true  // 判断是否需要进行数据分析

      // 批量添加点
      refOpenlayerBaseMap.value.addPoint(
        olMap,
        {
          lonlat: [item.longitude, item.latitude],
          pointData: item,
        },
        XxxxxxxxMarker
      );
    }
  );
};

// 设置圆数据(投诉)
const getXxxxxxxxData = async (olMap) => {
  // console.log(olMap);
  let params = {
    // date: '2024-05-23'
    date: getNow()
  }

  let circleList = await apiCommon(XxxxxxxxApi.queryXxxxxxxxtCluster, params);
  circleList = circleList.data
  // console.log(circleList)

  refOpenlayerBaseMap.value.renderFeatureByData(
    olMap,
    circleList,
    (olMap, item) => {

      item.lonlat = [item.longitude, item.latitude]

      // 添加圆
      refOpenlayerBaseMap.value.addCircle(olMap, item, { bussinessType: 'Xxxxxxxx' }, true);
    }
  );
};

/**
 * 生命周期
 */
onUnmounted(() => {
})
</script>

<style lang="scss" scoped>
.reflash_map {
  position: absolute;
  right: 10px;
  top: 10px;
  z-index: 1;
}

:deep .ol-popup {
  // background: #f00;
}

:deep.xxxxxxxx_map {

  // 控件相关
  .ol-overlaycontainer-stopevent {
    .ol-scale-line {
      bottom: 38px;
    }
  }
}
</style>
<template>
  <openlayer-base-map class="xxxxxxxx_map" ref="refOpenlayerBaseMap" :currentPageType="'xxxxxxxx'" :isShowLend="false"
    @getOlMapInstance="getOlMapInstance" @getCurrentViewData="getCurrentViewData" :isAutoRenderData="true"
    :isControlOverviewInfo="true" @getCircleData="getXxxxxxxxData" @reflashMap="reflashMap"
    @toggleOverviewInfo="toggleOverviewInfo">

    <el-button type="primary" @click="reflashMap" class="reflash_map">刷新地图</el-button>

    <!-- 右侧信息 - 投诉信息 -->
    <xxxxxxxx-common v-show="isShowXxxxxxxxCommon" />

    <!-- 自定义圆形数据 -->
    <circle-info-dialog ref="refCircleInfoDialog" />
  </openlayer-base-map>
</template>

<script setup>
// vue - core
import { ref, onUnmounted } from "vue";
// 组件
import OpenlayerBaseMap from "@/components/OpenlayerBaseMap/index.vue";
import CircleInfoDialog from "./components/CircleInfoDialog.vue"; // 点击圆信息
import XxxxxxxxCommon from "./components/XxxxxxxxCommon.vue"; // 投诉信息
import { getPopupInnerDom, getPopupAnalysisDom, getFeaturesPopupInnerDom } from "./components/popup/xxxxxxxxPopup.jsx"; // 气泡窗dom
// api相关 - 工具
import { apiCommon, getNow, getFormulateDate, moreArraysIsEmpty } from "@/utils/index.js";
import * as xxxxxxxxApi from "@/api/xxxxxxxx/xxxxxxxx";
// 工具
import mittBus from "@/utils/mittBus"; // mitt - 组件传参工具
// svg
import xxxxxxxxMarker from "./icon/Xxxxxxxx_marker.svg";
import arrow from "./icon/arrow.svg";
import alarmPoint from "./icon/analysisPoint/alarmPoint.svg";
import interferencePoint from "./icon/analysisPoint/interferencePoint.svg";
import loadPoint from "./icon/analysisPoint/loadPoint.svg";
import performancePoint from "./icon/analysisPoint/performancePoint.svg";

const refOpenlayerBaseMap = ref(null); // 地图核心元素

const { proxy } = getCurrentInstance();

const refCircleDetailDialog = ref(null);
const refCircleInfoDialog = ref(null);

let isShowXxxxxxxxCommon = ref(true)

let myOlMap = null;
let myMapCommonData = null;

let xxxxxxxxDataList = null

/**
 * 接收其他组件派发的方法
 */
/**
 * 气泡窗
 */
// 设置通用气泡窗html
mittBus.on("setCommonPopupInner", ({ popupObj, featureItem, callback }) => {
  // console.log("设置通用气泡窗html", popupObj, featureItem, featureItem.get('bussinessType'), featureItem.get('bussinessType'), 'analysisMarker');

  if (featureItem && featureItem.get('bussinessType') === 'analysisMarker') {
    callback(getPopupAnalysisDom(popupObj));
  } else {
    callback(getPopupInnerDom(popupObj));
  }

});
// 设置重叠要素气泡窗html
mittBus.on('setFeaturesPopupInner', ({ popupObj, callback, next }) => {
  // console.log(popupObj, callback)

  callback(getFeaturesPopupInnerDom(popupObj, currentDataList => {
    // console.log(currentDataList)

    currentDataList = currentDataList.filter(item => item)
    // console.log(currentDataList)

    next(currentDataList)
  }));
})
//  气泡窗点击更多 - 子组件使用
mittBus.on("popupDataGetMore", async ({ currentPopupObj, callback }) => {
  // console.log("气泡窗点击更多", currentPopupObj);

  // 或者走接口,根据xxx获取详情
  const currentPopupAsyncObj = await apiCommon(XxxxxxxxApi.queryXxxxxxxxtInfo, {
    XxxxxxxxtId: currentPopupObj.popupData.XxxxxxxxtId,
  });
  currentPopupObj = currentPopupAsyncObj.data;

  // 获取完数据后进行弹窗
  callback(currentPopupObj);
});
//  数据分析
mittBus.on("analysisPointData", async (currentData) => {
  // console.log("数据分析", currentData.longitude, currentData.latitude);

  let startPixelPoint = refOpenlayerBaseMap.value.transformToPixelPoint(currentData.longitude, currentData.latitude)
  // console.log(currentPixelPoint)

  /* 分析状态[空 - 未匹配到分析结果.完成 - loadResult]
  未匹配到小区

  展示分析结果 */
  let analysisPointRes = await apiCommon(XxxxxxxxApi.queryXxxxxxxxtAnalysisResult, currentData.csId);
  // let analysisPointRes = await apiCommon(XxxxxxxxApi.queryXxxxxxxxtAnalysisResult, '20240603103828X13147');
  analysisPointRes = analysisPointRes.data;

  if (analysisPointRes) {
    const { alarmResult, interferenceResult, loadResult, performanceResult } = analysisPointRes
    const tempArr = [alarmResult, interferenceResult, loadResult, performanceResult]

    if (moreArraysIsEmpty(tempArr)) {
      proxy.$modal.msgWarning(`未匹配到小区`);
      return
    }

    refOpenlayerBaseMap.value.removeLayerByBusinessType(myOlMap, "analysisMarker"); // 根据类型移除图层 - 分析标注
    refOpenlayerBaseMap.value.removeLayerByBusinessType(myOlMap, "XxxxxxxxArrowLine"); // 根据类型移除图层 - 分析箭头
    refOpenlayerBaseMap.value.removeLayerByBusinessType(myOlMap, "XxxxxxxxTextPoint"); // 根据类型移除图层 - 分析文本

    // console.log(alarmResult, interferenceResult, loadResult, performanceResult)
    setXxxxxxxxAnalysisData(alarmResult, startPixelPoint, '故障分析', alarmPoint)
    setXxxxxxxxAnalysisData(interferenceResult, startPixelPoint, '高干扰分析', interferencePoint)
    setXxxxxxxxAnalysisData(loadResult, startPixelPoint, '性能分析', loadPoint)
    setXxxxxxxxAnalysisData(performanceResult, startPixelPoint, '高负荷分析', performancePoint)
  }
});
// 设置圆形数据
mittBus.on("setCircleDialogData", (circleData) => {
  refCircleInfoDialog.value.show(circleData);
});

/**
 * 工具方法
 */
// 分析数据批量处理要素
const setXxxxxxxxAnalysisData = (resDataList, startPixelPoint, typeTxt, pointIcon) => {
  if (resDataList.length !== 0) {
    resDataList.forEach(item => {

      item.analysisType = typeTxt

      let analysisPointItem = {
        lonlat: [item.longitude, item.latitude],
        pointData: item,
      }

      let endPixelPoint = refOpenlayerBaseMap.value.transformToPixelPoint(item.longitude, item.latitude)

      let position = [startPixelPoint, endPixelPoint]
      refOpenlayerBaseMap.value.addArrowLine(myOlMap, position, arrow, 'XxxxxxxxArrowLine')

      // 创建文本要素以显示距离
      refOpenlayerBaseMap.value.addTextPoint(myOlMap, typeTxt, [item.longitude, item.latitude], 'XxxxxxxxTextPoint')

      refOpenlayerBaseMap.value.addPoint(
        myOlMap,
        analysisPointItem,
        pointIcon,
        { bussinessType: 'analysisMarker', 'noPopup': true }, 999
      );
    })
  }
}

// 显示隐藏概览
const toggleOverviewInfo = (val) => {
  // console.log('显示隐藏概览', val)

  isShowXxxxxxxxCommon.value = val
}
// 刷新地图
const reflashMap = () => {
  refOpenlayerBaseMap.value.removeAllLayer(myOlMap)

  getXxxxxxxxData(myOlMap)
  getCurrentViewData(myOlMap)
}
// 获取可视区域坐标参数
const getCurrentPositionParams = (olMap) => {
  let viewPosition = refOpenlayerBaseMap.value.getCurrentViewPosition(olMap);
  // console.log("获取可视区域的左上角和右下角坐标", viewPosition)

  return {
    minLatitude: viewPosition.bottomRight[1],
    maxLatitude: viewPosition.topLeft[1],
    minLongitude: viewPosition.topLeft[0],
    maxLongitude: viewPosition.bottomRight[0],
  };
};

/**
 * 业务方法
 */
// 获取地图实例 - 地图加载完初始化做的一些操作[业务相关]
const getOlMapInstance = (olMap, mapCommonData) => {
  // console.log("获取地图实例 - 地图加载完初始化做的一些操作[业务相关]", olMap);

  myOlMap = olMap; // 赋值全局变量,为后续业务操作做准备

  myMapCommonData = mapCommonData;
};
// 获取可视区域数据 (主入口)
const getCurrentViewData = async (olMap) => {
  let params = {
    ...getCurrentPositionParams(olMap),
    // dateString: '2024-05-23'
    dateString: getNow()
  };

  xxxxxxxxDataList = await apiCommon(XxxxxxxxApi.queryXxxxxxxxtList, params);
  xxxxxxxxDataList = xxxxxxxxDataList.data;
  // console.log(xxxxxxxxDataList);

  renderFeatureByData(olMap, xxxxxxxxDataList);
};

// 根据数据渲染Feature
const renderFeatureByData = (olMap, dataList) => {
  refOpenlayerBaseMap.value.renderFeatureByData(
    olMap,
    dataList,
    (olMap, item) => {
      // console.log(olMap, item);

      item.dispatchTime = getFormulateDate(item.dispatchTime)
      item.isNeedAnalysis = true  // 判断是否需要进行数据分析

      // 批量添加点
      refOpenlayerBaseMap.value.addPoint(
        olMap,
        {
          lonlat: [item.longitude, item.latitude],
          pointData: item,
        },
        XxxxxxxxMarker
      );
    }
  );
};

// 设置圆数据(投诉)
const getXxxxxxxxData = async (olMap) => {
  // console.log(olMap);
  let params = {
    // date: '2024-05-23'
    date: getNow()
  }

  let circleList = await apiCommon(XxxxxxxxApi.queryXxxxxxxxtCluster, params);
  circleList = circleList.data
  // console.log(circleList)

  refOpenlayerBaseMap.value.renderFeatureByData(
    olMap,
    circleList,
    (olMap, item) => {

      item.lonlat = [item.longitude, item.latitude]

      // 添加圆
      refOpenlayerBaseMap.value.addCircle(olMap, item, { bussinessType: 'Xxxxxxxx' }, true);
    }
  );
};

/**
 * 生命周期
 */
onUnmounted(() => {
})
</script>

<style lang="scss" scoped>
.reflash_map {
  position: absolute;
  right: 10px;
  top: 10px;
  z-index: 1;
}

:deep .ol-popup {
  // background: #f00;
}

:deep.xxxxxxxx_map {

  // 控件相关
  .ol-overlaycontainer-stopevent {
    .ol-scale-line {
      bottom: 38px;
    }
  }
}
</style>
jsx
export const getPopupInnerDom = (popupObj) => {
    return (
        <div class="common_popup_item">
            <h3>xxxx: {popupData.id}</h3>
            <ul>
                <li>
                    <dl>
                        <dt>xxxx:</dt>
                        <dd>{popupData.xxxx}</dd>
                    </dl>
                </li>
            </ul>

            <span class='get_more' data-function="getMore">查看更多</span>
        </div>
    )
}

export const getPopupAnalysisDom = (popupObj) => {
    return (<div class='common_simple_popup_item'>{popupData.analysisType}</div>)
}

export const getFeaturesPopupInnerDom = (popupObj, next) => {
    // console.log(popupObj)

    let currentDataList = []; // 数据集合

    const featuresPopupStr = popupObj.popupData.map(item => {
        let currentData = item.get('pointData')

        currentDataList.push(currentData)

        if (!currentData) {
            return
        }

        if (!currentData.analysisType) {
            return (
                <div class="feature_item" data-function='getSingleByFeatures' data-unique={`xxx:${currentData.xxx}`}>
                    <span data-function='getSingleByFeatures' data-unique={`xxx:${currentData.xxx}`}>ID: {currentData.id}</span>
                    <dl data-function='getSingleByFeatures' data-unique={`xxx:${currentData.xxx}`}>
                        <dt>xxx:</dt>
                        <dd>{currentData.xxx}</dd>
                    </dl>
                    <dl data-function='getSingleByFeatures' data-unique={`xxx:${currentData.xxx}`}>
                        <dt>xxxx:</dt>
                        <dd>{currentData.category}</dd>
                    </dl>
                </div>
            )
        } else {
            return (
                <div class="feature_item">
                    <span>xxx: {currentData.xxx}</span>
                    <span>xxxx: {currentData.networkType}</span>
                    <dl>
                        <dt>xxxx:</dt>
                        <dd>{currentData.analysisType}</dd>
                    </dl>
                </div>
            )
        }
    })

    next(currentDataList)  // 输出业务数据

    return (
        <>
            <div class="common_features_popup_item">
                {
                    <ul class="features_top">
                        <li>{popupObj.name}</li>
                        <li>类型: {popupObj.type}</li>
                    </ul>
                }
                {featuresPopupStr}
            </div>
        </>
    );
}
export const getPopupInnerDom = (popupObj) => {
    return (
        <div class="common_popup_item">
            <h3>xxxx: {popupData.id}</h3>
            <ul>
                <li>
                    <dl>
                        <dt>xxxx:</dt>
                        <dd>{popupData.xxxx}</dd>
                    </dl>
                </li>
            </ul>

            <span class='get_more' data-function="getMore">查看更多</span>
        </div>
    )
}

export const getPopupAnalysisDom = (popupObj) => {
    return (<div class='common_simple_popup_item'>{popupData.analysisType}</div>)
}

export const getFeaturesPopupInnerDom = (popupObj, next) => {
    // console.log(popupObj)

    let currentDataList = []; // 数据集合

    const featuresPopupStr = popupObj.popupData.map(item => {
        let currentData = item.get('pointData')

        currentDataList.push(currentData)

        if (!currentData) {
            return
        }

        if (!currentData.analysisType) {
            return (
                <div class="feature_item" data-function='getSingleByFeatures' data-unique={`xxx:${currentData.xxx}`}>
                    <span data-function='getSingleByFeatures' data-unique={`xxx:${currentData.xxx}`}>ID: {currentData.id}</span>
                    <dl data-function='getSingleByFeatures' data-unique={`xxx:${currentData.xxx}`}>
                        <dt>xxx:</dt>
                        <dd>{currentData.xxx}</dd>
                    </dl>
                    <dl data-function='getSingleByFeatures' data-unique={`xxx:${currentData.xxx}`}>
                        <dt>xxxx:</dt>
                        <dd>{currentData.category}</dd>
                    </dl>
                </div>
            )
        } else {
            return (
                <div class="feature_item">
                    <span>xxx: {currentData.xxx}</span>
                    <span>xxxx: {currentData.networkType}</span>
                    <dl>
                        <dt>xxxx:</dt>
                        <dd>{currentData.analysisType}</dd>
                    </dl>
                </div>
            )
        }
    })

    next(currentDataList)  // 输出业务数据

    return (
        <>
            <div class="common_features_popup_item">
                {
                    <ul class="features_top">
                        <li>{popupObj.name}</li>
                        <li>类型: {popupObj.type}</li>
                    </ul>
                }
                {featuresPopupStr}
            </div>
        </>
    );
}

dialog.vue

vue
<template>
    <detail-dialog ref="refDetailDialog">
        <!-- 标题 -->
        <template #DialogTitle>
            <span>xxxx</span>
        </template>

        <!-- 内容 -->
        <template #DialogContainer>
            xxxx内容
        </template>
    </detail-dialog>
</template>

<script setup>
import { ref } from 'vue'
import DetailDialog from "@/components/OpenlayerBaseMap/components/DetailDialog.vue";

const refDetailDialog = ref(null);
const currentData = ref({});

/**
 * 业务
 */
const show = (val) => {
    refDetailDialog.value.show();
    currentData.value = val; // 基本信息数据
};

defineExpose({
    show,
});

</script>

<style lang="scss" scoped></style>
<template>
    <detail-dialog ref="refDetailDialog">
        <!-- 标题 -->
        <template #DialogTitle>
            <span>xxxx</span>
        </template>

        <!-- 内容 -->
        <template #DialogContainer>
            xxxx内容
        </template>
    </detail-dialog>
</template>

<script setup>
import { ref } from 'vue'
import DetailDialog from "@/components/OpenlayerBaseMap/components/DetailDialog.vue";

const refDetailDialog = ref(null);
const currentData = ref({});

/**
 * 业务
 */
const show = (val) => {
    refDetailDialog.value.show();
    currentData.value = val; // 基本信息数据
};

defineExpose({
    show,
});

</script>

<style lang="scss" scoped></style>