<!-- 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>