Newer
Older
robot_dog_patrol_front / src / components / map / index.vue
<!--
  Description: 高德地图  -- 在线版
  Author: 李亚光
  Date: 2023-04-21
 -->
<script lang="ts" setup name="GuadMap">
import AMapLoader from '@amap/amap-jsapi-loader'
// import { styleType } from 'element-plus/es/components/table-v2/src/common'

const $props = defineProps({
  // 初始图层 normal satellite dark
  layer: {
    type: String,
    default: 'normal',
  },
  center: {
    type: Array,
    default: () => ([116.397428, 39.90923]),
  },
  zoom: {
    type: Number,
    default: 9,
  },
  showPiepleLayer: {
    type: Boolean,
    default: false,
  },
})
const $emits = defineEmits(['complete', 'marke', 'mapClick', 'markerClick', 'lineClick', 'polygonClick', 'massMarksClick'])
const loading = ref(true)
const publicPath = window.location.href.split('#')[0]
// 设置安全密钥
window._AMapSecurityConfig = {
  securityJsCode: localStorage.getItem('securityJsCode')!, // 后期需替换
}
// 地图实例
const map = shallowRef()
const AMap = ref()
// 使用高德地图API的逆地理编码服务
const getPosition = (location: string[], resultFun: Function) => {
  let position = ''
  AMap.value.plugin('AMap.Geocoder', () => {
    var geocoder = new AMap.value.Geocoder()
    var lnglat = location
    geocoder.getAddress(lnglat, (status: any, result: any) => {
      if (status === 'complete' && result.info === 'OK') {
        // result为对应的地理位置详细信息
        console.log(result.regeocode.formattedAddress)
        position = result.regeocode.formattedAddress
        resultFun(position)
      }
      else {
        position = ''
        resultFun(position)
      }
    })
  })
}
// 是否展示管线图层
const showPieple = ref(true)
// 地图缩放等级
const zoom = ref(0)
// 图层
const layer = ref()
// 图层标识
const layerFlag = ref('normal')
const xunteng = ref()
// const publicPath = import.meta.env.BASE_URL
// console.log(publicPath, 'publicPath')

// -------------------------------------初始化地图--------------------------------------------------
const wrapRef = ref()
const initMap = () => {
  AMapLoader.load({
    key: localStorage.getItem('JsKey')!, // 后期需替换
    version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
    plugins: ['AMap.Scale', 'AMap.MouseTool', 'AMap.Geocoder'], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
  })
    .then((AMap1: any) => {
      AMap.value = AMap1
      // 初始化地图
      map.value = new AMap1.Map('map', {
        viewMode: '3D', //  是否为3D地图模式
        zoom: $props.zoom, // 初始化地图级别
        resizeEnable: true,
        center: $props.center,
      })
      //     // 地图图块加载完成后触发
      map.value.on('complete', () => {
        //  修改loading状态
        console.log('地图加载完成')
        setTimeout(() => {
          $emits('complete', AMap.value)
          loading.value = false
        }, 1500)
        // AMap.value.reGeocode({
        //   location: $props.center,
        //   success: (res: any) => {
        //     // 获取到详细地址信息
        //     const formattedAddress = res.regeocode.formattedAddress
        //     console.log('地址:', formattedAddress)
        //   },
        //   error: (err: any) => {
        //     console.error('获取地址失败:', err)
        //   },
        // })
      })
      try {
        // 图层
        if ($props.layer === 'satellite') {
          // 卫星图层
          layer.value = new AMap.value.TileLayer.Satellite()
          map.value.addLayer(layer.value)
          map.value.setFeatures(['bg', 'building'])
          layerFlag.value = 'satellite'
        }
        else if ($props.layer === 'dark') {
          // 深色图层
          map.value.setMapStyle('amap://styles/darkblue')
        }
        else if ($props.layer === 'xunteng') {
          map.value.setZoom(12)
          xunteng.value = new AMap.value.TileLayer({
            visible: true,
            zIndex: 0,
            opacity: 1,
            zooms: [1, 19],
            dataZooms: [1, 19],
            getTileUrl: (a: any, b: any, c: any) => {
              // 经纬度转换成本地瓦片所在路径
              const flag = '00000000'
              const zz = c
              const z = `L${String(zz).length === 1 ? `0${zz}` : zz}`
              console.log(zz, 'zz')
              const xx = a.toString(16)
              const x = `C${flag.substring(0, 8 - xx.length)}${xx}`
              const yy = b.toString(16)
              const y = `R${flag.substring(0, 8 - yy.length)}${yy}`
              return `${window.localStorage.getItem('xuntengMap')}/${z}/${y}/${x}.png`
            },
          })
          map.value.addLayer(xunteng.value)
          map.value.setMapStyle('amap://styles/darkblue')
        }
        if ($props.showPiepleLayer) {
          addPiepleLayer()
        }
      }
      catch (error) {
        console.log(error, 'error')
      }
      // 地图绑定右击事件
      map.value.on('mousedown', clickHandler)
      // 地图绑定点击击事件
      map.value.on('click', (e: any) => {
        $emits('mapClick', e)
      })
      //     // 地图zoom
      map.value.on('zoomend', () => {
        zoom.value = map.value.getZoom() // 获取当前缩放级别
        console.log('当前缩放级别:', zoom.value)
      })
      // loading.value = false
    })
    .catch((e) => {
      console.log(e, '地图加载失败')
      loading.value = false
    })
}
// ------------------------------------------------------------------------------------------------
function clickHandler(e) {
  const location = [e.lnglat.getLng(), e.lnglat.getLat()]
  if (event.button === 2) {
    console.log('当前点击坐标为:   ', location)
  }
}
// -------------------------------------切换图层----------------------------------------------------
const changeLayerGrop = () => {
  if (layerFlag.value === 'normal') {
    layer.value = new AMap.value.TileLayer.Satellite()
    map.value.addLayer(layer.value)
    map.value.setFeatures(['bg', 'building'])
    layerFlag.value = 'satellite'
  }
  else {
    map.value.removeLayer(layer.value)
    layerFlag.value = 'normal'
  }
}
// 添加管线图层
function addPiepleLayer() {
  map.value.setZoom(12)
  if (xunteng.value) {
    xunteng.value.show()
    return
  }
  xunteng.value = new AMap.value.TileLayer({
    visible: true,
    zIndex: 0,
    opacity: 1,
    zooms: [1, 19],
    dataZooms: [1, 19],
    getTileUrl: (a: any, b: any, c: any) => {
      // 经纬度转换成本地瓦片所在路径
      const flag = '00000000'
      const zz = c
      const z = `L${String(zz).length === 1 ? `0${zz}` : zz}`
      console.log(zz, 'zz')
      const xx = a.toString(16)
      const x = `C${flag.substring(0, 8 - xx.length)}${xx}`
      const yy = b.toString(16)
      const y = `R${flag.substring(0, 8 - yy.length)}${yy}`
      return `${window.localStorage.getItem('xuntengMap')}/${z}/${y}/${x}.png`
    },
  })
  map.value.addLayer(xunteng.value)
}
// 移除管线图层
function removePiepleLayer() {
  xunteng.value.hide()
  // map.value.removeLayer(xunteng.value)
}
watch(() => showPieple.value, (newVal) => {
  if (!$props.showPiepleLayer) { return }
  if (String(newVal) === 'true') {
    const zoom = map.value.getZoom()
    addPiepleLayer()
    setTimeout(() => {
      map.value.setZoom(zoom)
    })
  }
  else if (String(newVal) === 'false') {
    console.log('移除管线图层')
    removePiepleLayer()
  }
}, {
  deep: true,
})
// ------------------------------------------------------------------------------------------------
// -------------------------------------多边形操作--------------------------------------------------
// 多边形数组
const polygonAllList = ref<any>([])
// 绘制多边形
function addPolygon(data: any[], style = {
  name: '',
  fillColor: '#ccebc5', // //多边形填充颜色
  strokeOpacity: 1, // 多边形填充透明度
  fillOpacity: 0.3, // 多边形填充透明度
  strokeColor: '#2b8cbe', // 线条颜色
  strokeWeight: 2, // 轮廓线宽度
  strokeStyle: 'dashed',
  strokeDasharray: [5, 5],
  activeFillColor: '#7bccc4', // 鼠标进入多边形的激活颜色
  dbclickSetCenter: false, // 双击多边形自动放大切切换中心试图
  dbclickCenter: [], // 中心坐标
  needHover: true,
}) {
  const polygon = new AMap.value.Polygon({
    path: data, // 边界数据
    fillColor: style.fillColor,
    strokeOpacity: style.strokeOpacity,
    fillOpacity: style.fillOpacity,
    strokeColor: style.strokeColor,
    strokeWeight: style.strokeWeight,
    strokeStyle: style.strokeStyle,
    strokeDasharray: style.strokeDasharray,
  })

  polygon.on('mouseover', () => {
    if (!style.needHover) {
      return
    }
    polygon.setOptions({
      fillOpacity: style.fillOpacity + 0.2,
      fillColor: style.activeFillColor,
    })
  })
  polygon.on('mouseout', () => {
    if (!style.needHover) {
      return
    }
    polygon.setOptions({
      fillOpacity: style.fillOpacity,
      fillColor: style.fillColor,
    })
  })
  polygon.on('mousedown', clickHandler)
  // if (style.dbclickSetCenter) {
  polygon.on('dblclick', (e) => {
    console.log(`当前点击的是${style.name}`)
    $emits('polygonClick', { event: e, map: map.value, style })
  })
  polygonAllList.value.push(polygon)
  // }
  map.value.add(polygon)
}
// 移除多边形
const removePolygon = () => {
  polygonAllList.value.forEach((item: any) => {
    item.setMap(null)
  })
  polygonAllList.value = []
  console.log('清除绘制的多边形')
}
// ------------------------------------------------------------------------------------------------

// -------------------------------------标记文本操作--------------------------------------------------
// 标记文本数组
const textAllList = ref<any>([])
// 添加标记文字
function addText(data: any, style = {
  /**
   * data 示例
   *  text: 文本内容
   *  position: 位置
   */
  // 'padding': '.75rem 1.25rem',
  // 'width': '15rem',
  // 'border-width': 0,
  // 'text-align': 'center',
  'font-size': '18px',
  'color': 'white',
  'background-color': 'transparent',
  'border-color': 'transparent',
}) {
  // 创建纯文本标记
  var Text = new AMap.value.Text({
    text: data.text, // 文本内容
    anchor: 'center', // 设置文本标记锚点
    draggable: false, // 是否可拖拽
    cursor: '', // 鼠标样式
    angle: 0, // 旋转角度
    style, // 文本样式
    position: data.position, // 点标记在地图上显示的位置
  })
  textAllList.value.push(Text)
  Text.setMap(map.value)
}
// 移除标记文字
const removeText = () => {
  textAllList.value.forEach((item: any) => {
    item.setMap(null)
  })
  textAllList.value = []
}
// ------------------------------------------------------------------------------------------------

// -------------------------------------点标记操作--------------------------------------------------
// 点标记(一般点标记)数组
const markerAllList = ref<any>([])
const markerHtml = `<img width="19px" height="32px" src="${publicPath}/image/mark_bs.png" />`
// 添加点标记 (一般点标记)
const addMarker = (data: any) => {
  /**
   * data 示例
   *  position: 位置
   *  content: 标记点内容 html
   *  label: 文本标记
   */
  var marker = new AMap.value.Marker({
    position: data.position,
    // 将 html 传给 content
    content: data.content || markerHtml,
    // 以 icon 的 [center bottom] 为原点
    offset: new AMap.value.Pixel(0, 0),
  })
  markerAllList.value.push(marker)
  // markers的信息窗体
  marker.on('click', (e: any) => {
    console.log('标记点')
    $emits('markerClick', { event: e, map: map.value, data, marker })
  })
  // 标记点文本标记
  marker.setLabel({
    offset: new AMap.value.Pixel(0, -5), // 设置文本标注偏移量
    content: `<div style="color:white;font-size:15px;">${data.label}</div>`, // 设置文本标注内容
    direction: 'top-center', // 设置文本标注方位
  })
  // 将 markers 添加到地图
  map.value.add(marker)
}
// 移除点标记 (一般点标记)
const removeMarker = () => {
  markerAllList.value.forEach((item: any) => {
    item.setMap(null)
  })
  markerAllList.value = []
}
// ------------------------------------------------------------------------------------------------

// -------------------------------------绘线操作--------------------------------------------------
// 折线数组
const lineAllList = ref<any[]>([])
// 添加折线
const addPolyline = (data: any) => {
  /**
   * data : {path: [],style: {}}
   * path: 折线点位数据  [[],[],[]]
   * style 折线样式
   *    style样式参考
   *      isOutline: true,
          outlineColor: '#ffeeff',
          borderWeight: 3,
          strokeColor: "#3366FF",
          strokeOpacity: 1,
          strokeWeight: 6,
          // 折线样式还支持 'dashed'
          strokeStyle: "dashed",
          // strokeStyle是dashed时有效
          strokeDasharray: [15, 5],
         lineJoin: 'round',
         lineCap: 'round',
         zIndex: 50,
   */
  var polyline = new AMap.value.Polyline({
    path: data.path.map((item: any) => (new AMap.value.LngLat(Number(item[0]), Number(item[1])))),
    ...data.style,
  })
  polyline.on('click', (e) => {
    console.log('点击了线')
    $emits('lineClick', { event: e, map: map.value, data, polyline })
  })
  console.log(polyline, 'polyline')
  lineAllList.value.push(polyline)
  map.value.add([polyline])
}
// 移除折线
const removePolyline = () => {
  lineAllList.value.forEach((item: any) => {
    item.setMap(null)
  })
  lineAllList.value = []
}
// ------------------------------------------------------------------------------------------------

// -----------------------------------海量点标记操作------------------------------------------------
// 点标记(海量点)数组
const massMarksAllList = ref<any>([])
// 添加点标记(海量点)
const addMassMarks = (data: any) => {
  var massMarks = new AMap.value.MassMarks(data.path, {
    zIndex: data.zIndex, // 海量点图层叠加的顺序
    zooms: data.zooms || [], // 在指定地图缩放级别范围内展示海量点图层
    style: data.style, // 设置样式对象
  })
  massMarksAllList.value.push(massMarks)
  massMarks.setMap(map.value)
  massMarks.on('click', (e) => {
    console.log('点击了海量点标记')
    $emits('massMarksClick', { event: e, map: map.value, data, massMarks })
  })
}
// 移除点标记(海量点)
const removeMassMarks = () => {
  massMarksAllList.value.forEach((item: any) => {
    item.setMap(null)
  })
  massMarksAllList.value = []
}
// ------------------------------------------------------------------------------------------------

// -------------------------------------绘线操作----------------------------------------------------
const massLineAllList = ref<any>([]) // 海量线数组
// 添加折线(海量)
const addMassLine = (data: any) => {
  /**
   * data
   * path: 折线点位数据  [[],[],[]]
   * style 折线样式
   *    style样式参考
          strokeColor: "#3366FF",
          strokeWeight: 6,
          // 折线样式还支持 'dashed'
          strokeStyle: "solid",
         zIndex: 50,
         zoomLevels: [1,18]
   */
  // const multiPolyline = new AMap.value.MultiPolyline({
  //   map: map.value,
  //   styles: {
  //     ...data.style,
  //   },
  // })
  // massLineAllList.value.push(multiPolyline)
  // multiPolyline.setPaths(data.path)
}
// 移除折线
const removeMassLine = () => {
  // massLineAllList.value.forEach((item: any) => {
  //   item.setMap(null)
  // })
  // massLineAllList.value = []
}
// ------------------------------------------------------------------------------------------------
onMounted(() => {
  loading.value = true
  initMap()
})
onUnmounted(() => {
  if (xunteng.value) {
    map.value.remove(xunteng.value)
    map.value.removeLayer(xunteng.value)
    xunteng.value = null
  }
  if (map.value) {
    console.log('销毁')
    map.value.destroy()
    map.value = null
    AMap.value = null
  }
})
onBeforeUnmount(() => {
  if (xunteng.value) {
    map.value.remove(xunteng.value)
    map.value.removeLayer(xunteng.value)
    xunteng.value = null
  }
  if (map.value) {
    console.log('销毁')
    map.value.destroy()
    map.value = null
    AMap.value = null
  }
})
defineExpose({
  map,
  AMap,
  zoom, // 当前缩放等级
  changeLayerGrop, // 切换图层
  layerFlag, // 当前图层标识
  polygonAllList, // 多边形区域数据
  addPolygon, // 绘制多边形区域
  removePolygon, // 移除多边形
  addText, // 添加标记文字
  removeText, // 移除标记文字
  addMarker, // 添加点标记 (一般点标记)
  removeMarker, // 移除点标记 (一般点标记)
  addPolyline, // 添加折线
  lineAllList, // 折线数组
  removePolyline, // 移除折线
  addMassMarks, // 添加点标记(海量点)
  massMarksAllList,
  removeMassMarks, // 移除点标记(海量点)
  addMassLine, // 添加折线(海量线)
  removeMassLine, // 移除折线(海量线)
  getPosition, // 根据坐标获取详细位置信息
})
</script>

<template>
  <div class="container">
    <!-- 地图 -->
    <div id="map" ref="wrapRef " v-loading="loading" />
    <!-- 管线图层 -->
    <el-checkbox v-if="showPiepleLayer" v-model="showPieple" class="checkbox-layer" label="管线图层" size="large" />
  </div>
</template>

<style lang="scss" scoped>
.container {
  height: 100%;
  width: 100%;
  position: relative;
}

::v-deep(.amap-marker-label) {
  background-color: transparent;
  border: none;
}

.checkbox-layer {
  position: absolute;
  z-index: 99;
  top: 10px;
  right: 10px;
}

#map {
  overflow: hidden;
  width: 100%;
  height: 100%;
  margin: 0;
  font-family: "微软雅黑";
}
</style>

<style>
/* 隐藏高德logo  */
.amap-logo {
  display: none !important;
}

/* 隐藏高德版权  */
.amap-copyright {
  display: none !important;
}

.img-map-marker {
  width: 20px;
  height: 20px;
}
</style>