Newer
Older
carbon-metering-front / src / components / map / leaflet.vue
<!--
  Description: leaflet地图  --离线
  Author: 李亚光
  Date: 2023-04-21
 -->
<script lang="ts" setup name="leafletMap">
import L from 'leaflet'
import infoDetail from './infoWindow.vue'
import infoDetail2 from './infoWindow2.vue'
import infoDetailHot from './infoWindowHot.vue'
import { getDictByCode } from '@/api/system/dict'
import { electricityBranch, electricityNode, getAreaPosition, getDeviceListPage, getElectricityListAll, getMonitorStationList } from '@/api/api/index'
const $props = defineProps({
  isDashboard: {
    type: Boolean,
    default: true,
  },
})
const $emits = defineEmits(['complete'])
// import 'leaflet/dist/leaflet.css'
const leafletRef = ref()
const electricityList = ref()
const publicPath = window.location.href.split('#')[0]
// 电网节点数组
const markerArr = ref([
])
const markerArrInfo = ref()
const markerArrInfo1 = ref()
// 折现节点坐标
const lineArr = ref([])
// 光伏数组
const markerArr1 = ref([
])
// 用热坐标数组
const markerArrHot = ref()
const markerArrHotInfo = ref()
// 信息窗体
const infoWindow = ref()
const infoWindow2 = ref()
const infoWindowHot = ref()
const windowInfoRef = ref()
const windowInfoRef2 = ref()
const windowInfoHotRef = ref()
const map = ref()
// 图层实例
const layer = ref()
const satellite = ref()
const flag = ref(true)
const layerAllList = ref<any[]>([])
// 切换图层
const changeLayerGrop = () => {
  if (flag.value) {
    map.value?.addLayer(layer.value)
    map.value?.removeLayer(satellite.value)
  }
  else {
    map.value?.removeLayer(layer.value)
    map.value?.addLayer(satellite.value)
  }
  flag.value = !flag.value
}
// 地图点击事件
const clickHandler = function (e) {
  if (event.button === 2) {
    console.log('当前点击坐标为:   ', e.latlng)
  }
}
const init = async () => {
  var container = L.DomUtil.get(leafletRef.value)
  if (container != null) {
    container._leaflet_id = null
  }
  //
  layer.value = L.tileLayer('', {
    subdomains: ['1', '2', '3', '4'],
    minZoom: 8,
    maxZoom: 18,
  })
  layer.value.getTileUrl = (croods: any) => {
    const { x, y, z } = croods
    const flag = '00000000'
    const zz = z
    const z1 = `L${zz}`
    const xx = x.toString(16)
    const x1 = `C${flag.substring(0, 8 - xx.length)}${xx}`
    const yy = y.toString(16)
    const y1 = `R${flag.substring(0, 8 - yy.length)}${yy}`
    return `${window.localStorage.getItem('amapResource')}/ordinary/${z1}/${y1}/${x1}.png`
  }
  // 卫星图层
  satellite.value = L.tileLayer('', {
    subdomains: ['1', '2', '3', '4'],
    minZoom: 8,
    maxZoom: 18,
  })
  satellite.value.getTileUrl = (croods: any) => {
    const { x, y, z } = croods
    const flag = '00000000'
    const zz = z
    const z1 = `L${zz}`
    const xx = x.toString(16)
    const x1 = `C${flag.substring(0, 8 - xx.length)}${xx}`
    const yy = y.toString(16)
    const y1 = `R${flag.substring(0, 8 - yy.length)}${yy}`
    return `${window.localStorage.getItem('amapResource')}/satellite/${z1}/${y1}/${x1}.png`
  }
  map.value = L.map(leafletRef.value, {
    layers: [layer.value],
    /* center:[纬度,经度] */
    center: [40.830627, 111.73142],
    zoom: 18,
    doubleClickZoom: true, // 禁用双击放大
    attributionControl: true, // 移除右下角leaflet标识
    zoomControl: true, // 禁用 + - 按钮
  })
  $emits('complete', map.value)
  map.value.on('mousedown', clickHandler)
}
// onMounted(() => {
//   init()
// })
// 绘制用电监测相关
const useElectricity = () => {
  // 电网支路标记点绘制
  for (let i = 0; i < markerArr.value.length; i++) {
    // marker点标记
    const myIcon = L.icon({
      iconUrl: `${publicPath}img/icon1.png`,
      iconSize: [25, 25], // 图标的大小
      iconAnchor: [10, 10], // 图标的锚点,即图标的位置应该放置在地图上的位置
      popupAnchor: [0, 0], // 弹出框的锚点,即当你点击图标时,弹出框应该出现在哪个位置
    })
    const marker = L.marker([markerArr.value[i][1], markerArr.value[i][0]], { icon: myIcon, data: markerArrInfo.value[i] })
    layerAllList.value.push(marker)
    map.value.addLayer(marker)
    // 文本标记
    const myText = L.divIcon({
      html: `${markerArrInfo.value[i].nodeName}`,
      className: 'leaflet-my-div-text',
      iconSize: 24,
    })
    const text = L.marker([markerArr.value[i][1], markerArr.value[i][0]], { icon: myText })
    layerAllList.value.push(text)
    map.value.addLayer(text)
    // 添加信息窗体
    const element = document.getElementById('leaflet-windowInfoRef')
    marker.bindPopup(element)
    marker.on('click', (e) => {
      // 传递数据
      windowInfoRef.value.initialize({
        info: e.target.options.data,
        map: map.value,
        position: [markerArr.value[i][1], markerArr.value[i][0]],
      })
      // 打开窗体
      marker.openPopup()
    })
  }
  // 光伏标记点及光伏绘制
  for (let i = 0; i < markerArr1.value.length; i++) {
    // marker点标记
    const myIcon = L.icon({
      iconUrl: `${publicPath}img/icon2.png`,
      iconSize: [25, 25], // 图标的大小
      iconAnchor: [10, 10], // 图标的锚点,即图标的位置应该放置在地图上的位置
      popupAnchor: [0, 0], // 弹出框的锚点,即当你点击图标时,弹出框应该出现在哪个位置
    })
    const marker = L.marker([markerArr1.value[i][1], markerArr1.value[i][0]], { icon: myIcon, data: markerArrInfo1.value[i] })
    layerAllList.value.push(marker)
    map.value.addLayer(marker)
    // 文本标记
    const myText = L.divIcon({
      html: `${markerArrInfo1.value[i].stationName}`,
      className: 'leaflet-my-div-text',
      iconSize: 24,
    })
    const text = L.marker([markerArr1.value[i][1], markerArr1.value[i][0]], { icon: myText })
    layerAllList.value.push(text)
    map.value.addLayer(text)
    const element = document.getElementById('leaflet-windowInfoRef2')
    marker.bindPopup(element)
    marker.on('click', (e) => {
      // 传递数据
      windowInfoRef2.value.initialize({
        info: e.target.options.data,
        map: map.value,
        position: [markerArr1.value[i][1], markerArr1.value[i][0]],
      })
      // 打开窗体
      marker.openPopup()
    })

    // 绘制电网和节点连线
    const path = markerArrInfo1.value.map((item) => {
      return [[Number(item.lng), Number(item.lat)], [Number(item.nodeLng), Number(item.nodeLat)]]
    })
    for (let i = 0; i < path.length; i++) {
      const line = L.polyline(path[i].map((item: number[]) => [item[1], item[0]]), {
        color: '#03A382',
        weight: 2,
        smoothFactor: 5,
        lineCap: 'round',
        lineJoin: 'round',
        dashArray: [10, 6],
      })
      layerAllList.value.push(line)
      map.value.addLayer(line)
    }
  }

  // // 电网支路折线绘制
  for (let i = 0; i < lineArr.value.length; i++) {
    const path = lineArr.value.map(item => item.branchNodeInfos)
    const line = path.map(item => (
      item.map((child) => {
        return ([[child.flng, child.flat], [child.tlng, child.tlat], child.status2])
      })
    ))
    line.forEach((item) => {
      item.forEach((child) => {
        const path = [child[0].reverse(), child[1].reverse()]
        const line = L.polyline(path, {
          color: child[2] === 1 ? '#008d68' : '#000',
          weight: 2,
          smoothFactor: 5,
          lineCap: 'round',
          lineJoin: 'round',
          dashArray: [10, 8],
        })
        layerAllList.value.push(line)
        map.value.addLayer(line)
        // 离线支路连接添加开关图标
        if (child[2] === 0) {
          const data = [(Number(child[0][0]) + Number(child[1][0])) / 2, (Number(child[0][1]) + Number(child[1][1])) / 2]
          console.log(data, 'data')
          const myIcon = L.icon({
            iconUrl: `${publicPath}img/icon3.png`,
            iconSize: [25, 25], // 图标的大小
            iconAnchor: [10, 10], // 图标的锚点,即图标的位置应该放置在地图上的位置
            popupAnchor: [0, 0], // 弹出框的锚点,即当你点击图标时,弹出框应该出现在哪个位置
          })
          const marker = L.marker(data, { icon: myIcon })
          layerAllList.value.push(marker)
          map.value.addLayer(marker)
        }
      })
    })
  }
  if (!$props.isDashboard) {
    clearAllLayer()
  }
  // $emits('complete', map.value)
}
// 绘制用热监测相关
const useHot = () => {
  for (let i = 0; i < markerArrHot.value.length; i++) {
    // marker点标记
    const myIcon = L.icon({
      iconUrl: `${publicPath}img/icon4.png`,
      iconSize: [25, 25], // 图标的大小
      iconAnchor: [10, 10], // 图标的锚点,即图标的位置应该放置在地图上的位置
      popupAnchor: [0, 0], // 弹出框的锚点,即当你点击图标时,弹出框应该出现在哪个位置
    })
    const marker = L.marker([markerArrHot.value[i][1], markerArrHot.value[i][0]], { icon: myIcon, data: markerArrHotInfo.value[i] })
    layerAllList.value.push(marker)
    map.value.addLayer(marker)
    // 文本标记
    const myText = L.divIcon({
      html: `${markerArrHotInfo.value[i].deviceName}`,
      className: 'leaflet-my-div-text',
      iconSize: 24,
    })
    const text = L.marker([markerArrHot.value[i][1], markerArrHot.value[i][0]], { icon: myText })
    layerAllList.value.push(text)
    map.value.addLayer(text)
    // 添加信息窗体
    const element = document.getElementById('leaflet-windowInfoHotRef')
    marker.bindPopup(element)
    marker.on('click', (e) => {
      // 传递数据
      windowInfoHotRef.value.initialize({
        info: e.target.options.data,
        map: map.value,
        position: [markerArrHot.value[i][1], markerArrHot.value[i][0]],
      })
      // 打开窗体
      marker.openPopup()
    })
  }
}
onMounted(async () => {
  // if (!$props.isDashboard) {
  //   await init()
  //   return
  // }
  // 获取电网列表
  try {
    const list = await getElectricityListAll()
    // 电网列表
    electricityList.value = list.data
    // 查询节点列表(标记点)
    if (electricityList.value.length) {
      const res = await electricityNode(electricityList.value[0].id)
      markerArr.value = res.data.map(item => ([Number(item.lng), Number(item.lat)]))
      markerArrInfo.value = res.data
      // 获取支路信息(折线)
      const res1 = await electricityBranch(electricityList.value[0].id)
      lineArr.value = res1.data
    }
    // 获取光伏列表
    const list1 = await getMonitorStationList()
    markerArr1.value = list1.data.map(item => ([Number(item.lng), Number(item.lat)]))
    markerArrInfo1.value = list1.data
    // 初始化地图
    await init()
    useElectricity()
  }
  catch (error) {
    init()
  }
  // 获取用热数据
  getDeviceListPage({
    offset: 1,
    limit: 999,
    ptn: '',
    deviceType: '2',
  }).then((res) => {
    markerArrHotInfo.value = res.data.rows
    markerArrHot.value = res.data.rows.map(item => ([Number(item.lng), Number(item.lat)]))
  })
  // getElectricityListAll().then((res) => {
  //   // 电网列表
  //   electricityList.value = res.data
  //   // 默认选中第一个查询节点
  //   if (electricityList.value.length) {
  //     electricityNode(electricityList.value[0].id).then((res) => {
  //       markerArr.value = res.data.map(item => ([Number(item.lng), Number(item.lat)]))
  //       markerArrInfo.value = res.data
  //       // 获取支路
  //       electricityBranch(electricityList.value[0].id).then(res => {
  //         lineArr.value = res.data
  //         // console.log(res.data)
  //         initMap()
  //       })
  //     })
  //   }
  // })
})

// 分区
const area = ref('')
const areaList = ref([])
// 获取分区列表
const fetchAreaList = () => {
  getDictByCode('ptn_type').then((res) => {
    areaList.value = res.data
  })
}
fetchAreaList()
const areaMarkerList = ref()
const polygons = ref()
// 绘制区域
const drawArea = (markerList: any, color: string) => {
  const polygon = L.polygon(
    markerList.map((item: number[]) => item.reverse()),
    {
      color,
    })
  polygons.value = polygon
  map.value.addLayer(polygon)
}
const clearDraw = () => {
  if (polygons.value) {
    console.log('删除')
    polygons.value.remove()
    // map.value.removeLayer(polygons.value)
    area.value = ''
  }
}
// 监听分区变化
watch(() => area.value, (newVal) => {
  console.log(newVal, 'newVal')
  if (polygons.value) {
    console.log('删除')
    // map.value.removeLayer(polygons.value)
    polygons.value.remove()
  }
  if (!newVal) {
    return
  }
  getAreaPosition(newVal).then((res) => {
    const color = {
      1: '#2b8cbe',
      2: '#ccebc5',
      3: '#7bccc4',
    }
    areaMarkerList.value = res.data
    if (res.data.length) {
      areaMarkerList.value.push(res.data[0])
      const data = areaMarkerList.value.map(item => ([Number(item.lng), Number(item.lat)]))
      drawArea(data, color[newVal])
    }
  })
})
function clearAllLayer() {
  layerAllList.value.forEach((item: any) => {
    map.value.removeLayer(item)
  })
  layerAllList.value = []
  map.value.setView([40.830627, 111.73142], 18)
  // 区域框展示
  area.value = ''
  $emits('complete', map.value)
}
// 用电检测/用热监测 默认用电
const flagType = ref(true)
// 切换用电检测/用热监测
const changeFlagType = () => {
  layerAllList.value.forEach((item: any) => {
    map.value.removeLayer(item)
  })
  layerAllList.value = []
  map.value.setView([40.830627, 111.73142], 18)
  // 区域框展示
  area.value = ''
  if (flagType.value) {
    useHot()
  }
  else {
    useElectricity()
  }
  flagType.value = !flagType.value
}
defineExpose({ changeLayerGrop, changeFlagType, flag, map, layerAllList })
onUnmounted(() => {
  console.log('页面要销毁')
  map.value?.remove()
  document.getElementById('map-leaflet111')?.remove()
})
</script>

<template>
  <div id="map-leaflet111" ref="leafletRef" />
  <info-detail v-if="isDashboard" ref="windowInfoRef" />
  <info-detail2 v-if="isDashboard" ref="windowInfoRef2" />
  <info-detail-hot v-if="isDashboard" ref="windowInfoHotRef" />
  <div v-if="isDashboard" class="btns">
    <el-radio-group v-model="area">
      <el-radio-button v-for="item in areaList" :key="item.id" :label="item.value" @dblclick="clearDraw">
        {{ item.name }}
      </el-radio-button>
    </el-radio-group>
  </div>
</template>

<style>
.leaflet-my-div-text {
  white-space: nowrap !important;
  margin-top: 10px !important;
}

.leaflet-popup-content {
  width: 100% !important;
  padding-top: 8px;
}
</style>

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

.btns {
  position: absolute;
  left: 10px;
  bottom: 10px;
  z-index: 999;
}
</style>