vue在PC端引用百度地图实现自定义省、市、区、项目、设备海量点,根据地图的zoomLevel设置成4-6级是省级别、7-9级是市级别、10-12级是区级别、13和14级是项目级别、15级-15级以上是设备级别
1.登录百度地图开发平台创建key 应用类型选择"浏览器端"
链接: 登录百度账号
2.可自行设置个性化样式模板-下图使用的是"眼眸"
链接: 登录百度账号
效果图
一.定义utils工具
src/utils/arrayOperation.ts
// JS去除JSON字符串各种空格、换行符
export function disposeJsonStr(str: string) {
return str.replace(/\r\n/g, "").replace(/\n/g, "").replace(/\s+/g, "");
}
src/utils/baidu-map.ts
declare const window: any;
// GL版本
export function baiduMapGL() {
return new Promise(function (resolve, reject) {
if (typeof window.initBMapGL !== "undefined") {
resolve(window.initBMapGL);
return;
}
window.initBMapGL= function () {
resolve(window.initBMapGL);
// getPosition()
};
const script = document.createElement("script");
script.type = "text/javascript";
script.src = `https://api.map.baidu.com/api?v=1.0&type=webgl&ak=${你的ak}&callback=initBMapGL`;
script.onerror = reject;
document.head.appendChild(script);
});
}
二.定义图标
放在src/assets目录下
m1.png
m2.png
m3.png
markerIcon.png
三.定义组件
在与vue页面文件同级目录定义组件 bmap.vue
<template>
<!-- 地图 -->
<div id="container" ref="container" style="z-index:1" v-loading="loading" element-loading-text="拼命加载中"
element-loading-background="rgba(0, 0, 0, 0)"></div>
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, getCurrentInstance, defineComponent, nextTick, watch, onBeforeUnmount } from 'vue';
import { disposeJsonStr } from '/@/utils/arrayOperation';
import mapDevIcon from '/@/assets/markerIcon.png';
import m1Png from '/@/assets/m1.png';
import m2Png from '/@/assets/m2.png';
import m3Png from '/@/assets/m3.png';
import { baiduMapGL } from "/@/utils/baidu-map";
let map: any = null;
const _window = window as any;
export default defineComponent({
name: 'Bmap',
setup(props: any, { emit }: any) {
onBeforeUnmount(() => {
map && map.destroy();
map = null
})
// 页面加载时
onMounted(async () => {
baiduMapGL().then(() => {
initMap(); // 创建地图
})
});
const { proxy }: any = getCurrentInstance();
const state = reactive({
countryZoom: 3, // 小于等于当前 属于国范围级别
min_provinceZoom: 4,// 省范围级别
middle_provinceZoom: 5,// 省范围级别
max_provinceZoom: 6,// 省范围级别
min_cityZoom: 7,// 市范围级别
middle_cityZoom: 8,// 市范围级别
max_cityZoom: 9,// 市范围级别
min_areaZoom: 10,// 区范围级别
middle_areaZoom: 11,// 区范围级别
max_areaZoom: 12,// 区范围级别
min_projectZoom: 13,// 项目范围级别
max_projectZoom: 14,// 项目范围级别
deviceZoom: 15,// 大于等于当前 属于设备范围级别
latitude: 22.55329,
longitude: 113.88308,
zoom: 5,
provinceList: [], //省数据
cityList: [], //市数据
areaList: [], //区数据
projectList: [], //项目列表
deviceList: [], //设备列表
isInitMapDataList: false,
noTriggerMapTimer: null as any,
newZoom: 0,
loading: false,
});
const initMap = async () => {
map = new BMapGL.Map('container');
// 给百度地图设置个性化地图样式
map.setMapStyleV2({
styleId: "", // 你的styleId
});
map.enableScrollWheelZoom(true);
var cityCtrl = new BMapGL.LocationControl(); // 添加城市列表控件
map.addControl(cityCtrl);
map.centerAndZoom(new BMapGL.Point(state.longitude, state.latitude), state.zoom); // 深圳的经纬度 113.88308, 22.55329
setTimeout(() => {
initMapDataList();
}, 2000);
map.addEventListener('zoomend', (e: any) => {
state.newZoom = parseInt(e.target.zoomLevel);
if (state.isInitMapDataList) return
state.zoom = parseInt(e.target.zoomLevel)
initMapDataList(undefined, 'zoomend');
});
map.addEventListener('dragend', (e: any) => {
if (state.isInitMapDataList) return
state.zoom = parseInt(map.getZoom());
initMapDataList()
});
};
watch(() => state.newZoom, (newZoom: any) => {
clearTimeout(state.noTriggerMapTimer);
state.noTriggerMapTimer = null;
state.noTriggerMapTimer = setTimeout(() => {
if (state.isInitMapDataList) return
if (newZoom !== state.zoom) {
state.zoom = newZoom;
initMapDataList();
}
}, 1000);
});
const initMapDataList = async (id?: number, sign?: string,) => {
if (state.zoom <= state.countryZoom) {
state.cityList = [];
state.provinceList = [];
state.areaList = [];
state.projectList = [];
state.deviceList = [];
return false
} else if (state.zoom <= state.max_provinceZoom) {
state.cityList = [];
state.areaList = [];
state.projectList = [];
state.deviceList = [];
} else if (state.zoom <= state.max_cityZoom) {
state.provinceList = [];
state.areaList = [];
state.projectList = [];
state.deviceList = [];
} else if (state.zoom <= state.max_areaZoom) {
state.provinceList = [];
state.cityList = [];
state.projectList = [];
state.deviceList = [];
} else if (state.zoom <= state.max_projectZoom) {
state.provinceList = [];
state.cityList = [];
state.areaList = [];
state.deviceList = [];
} else {
state.provinceList = [];
state.cityList = [];
state.areaList = [];
state.projectList = [];
}
if (state.provinceList.length > 0 && state.zoom <= state.max_provinceZoom) return;
if (sign === 'zoomend') {
if (state.cityList.length > 0 && state.zoom <= state.max_cityZoom) return;
if (state.areaList.length > 0 && state.zoom <= state.max_areaZoom) return;
if (state.projectList.length > 0 && state.zoom <= state.max_projectZoom) return;
if (state.deviceList.length > 0 && state.zoom >= state.deviceZoom) return;
}
state.isInitMapDataList = true // 开始加载地图数据
let res: any;
if (id) {
res = await proxy.apis.ajaxmap({ areaid: id, grade: state.zoom });
// 点击项目或者设备级别有数据就跳到有数据的位置
if (state.zoom >= state.min_projectZoom) {
if (res.code === 200) {
let filterArr = res.data.filter((item: any) => item.longitude && item.latitude && Number(item.longitude) > Number(item.latitude))
if (filterArr.length > 0) {
state.loading = true
const element = filterArr[0];
let point = new BMapGL.Point(Number(element.longitude), Number(element.latitude))
setTimeout(() => {
state.loading = false
map.centerAndZoom(point, state.zoom);
}, 500);
} else {
return proxy.$message.warning("当前项目位置有误")
}
}
}
} else {
res = await proxy.apis.ajaxmap({
lng: map.getCenter().lng.toFixed(6),
lag: map.getCenter().lat.toFixed(6),
grade: state.zoom,
});
}
if (res.code === 200) {
map.clearOverlays();
setTimeout(() => {
state.isInitMapDataList = false
}, 1000);
let list = [] as any[];
if (state.zoom <= state.countryZoom) {
} else if (state.zoom <= state.max_provinceZoom) {
state.provinceList = res.data;
list = state.provinceList;
} else if (state.zoom <= state.max_cityZoom) {
state.cityList = res.data;
list = state.cityList;
} else if (state.zoom <= state.max_areaZoom) {
state.areaList = res.data;
list = state.areaList;
} else if (state.zoom <= state.max_projectZoom) {
state.projectList = res.data;
list = state.projectList;
} else {
state.deviceList = res.data;
list = state.deviceList;
}
for (let i = 0; i < list.length; i++) {
const item = list[i];
var points = new BMapGL.Point(item.longitude ? Number(item.longitude) : 0, item.latitude ? Number(item.latitude) : 0); //创建坐标点
let valueBgc = state.zoom <= state.max_provinceZoom ? '#3a7e7e' : state.zoom <= state.max_cityZoom ? '#afc018' : '#8e4191';
let bgImg = item.value >= 100 ? m3Png : item.value >= 10 ? m2Png : m1Png;
let placeName_dom =
`<div class='regionLabel'><div class='name'>${item.name}</div><div class="value" style="background:${valueBgc};border: 1px solid ${valueBgc};">${item.value}</div> <div class="triangle"></div></div>`;
let projectText =
`<div class='projectText' data-projectItem=${disposeJsonStr(JSON.stringify(item))} onclick="$handleProjectText(event)" style="z-index:999999;">${item.name}<div class="triangle"></div></div>`
let project_dom =
`<div class='projectBgImgLabel' style="background-image: url(${bgImg});background-size:contain;">${item.value} ${projectText}</div>`;
let label_dom = state.zoom <= state.max_areaZoom ? placeName_dom : state.zoom <= state.max_projectZoom ? project_dom : '';
let markersLabel = new BMapGL.Label(label_dom, {
offset: new BMapGL.Size(-22, -45),
direction: 'top', //设置文本标注方位
});
markersLabel.setStyle({
border: '0',
backgroundColor: 'none',
});
markersLabel.addEventListener('click', async function (event: any) {
if (state.isInitMapDataList) return
if (state.zoom >= state.min_areaZoom && Number(item.value) === 0) return proxy.$message.warning(`${item.name}没有项目`)
let lnglatInfo = new BMapGL.Point(Number(item.longitude), Number(item.latitude))
if (state.zoom >= state.max_areaZoom && state.zoom <= state.max_projectZoom) {
state.zoom += 2
} else {
state.zoom += 3
}
if (state.zoom <= state.max_areaZoom) {
map.centerAndZoom(lnglatInfo, state.zoom);
}
initMapDataList(item.id,);
});
// 渲染Marker 省市区项目级只显示label
initMarker(points, markersLabel, item,);
}
} else {
proxy.$message.error(res.message)
}
};
function initMarker(points: any, markersLabel: any, row: any,) {
var myIcon = new BMapGL.Icon(mapDevIcon, new BMapGL.Size(state.zoom >= state.deviceZoom ? 50 : 0, state.zoom >= state.deviceZoom ? 50 : 0));
let markers = new BMapGL.Marker(points, {
icon: myIcon,
enableDragging: true, // 可移动
});
nextTick(() => {
map.addOverlay(markers);
markers.setLabel(state.zoom >= state.deviceZoom ? '' : markersLabel);
})
if (state.zoom >= state.deviceZoom) {
markers.addEventListener('click', function (event: any) {
infoWindow(points, row);
});
let route_lng = Number(proxy.$route.query.longitude);
let route_lat = Number(proxy.$route.query.latitude);
if (route_lng === points.lng && route_lat === points.lat) {
state.loading = true;
setTimeout(() => {
let havePoints = new BMapGL.Point(Number(route_lng), Number(route_lat)); //创建坐标点
infoWindow(havePoints, row);
}, 500);
}
}
}
_window.$handleProjectText = function handleProjectText(e: any,) {
e.stopPropagation();
const item = JSON.parse(e.target.dataset.projectitem)
openProjectInfoWindow(item);
}
const openProjectInfoWindow = function (item: any) {
let points = new BMapGL.Point(item.longitude, item.latitude); //创建坐标点
let geoc = new BMapGL.Geocoder();
geoc.getLocation(points, (rs: any) => {
// 地址名称
let currentAddress = rs.address;
let dom = `
<div>${item.id ? "设备数量" : "分路数量"}: ${item.value}</div>
<div>详细地址: ${currentAddress}</div>
<div class="btnBox"><div class="actionBtn" data-item=${disposeJsonStr(JSON.stringify(item))} onclick="$detailsAction()">项目详情</div></div>`;
// 创建文本标注对象
var labelopts = {
position: points, // 指定文本标注所在的地理位置
offset: new BMapGL.Size(10, -75), // 设置文本偏移量
title: item.name,
// width: 50,
// height: 100,
};
let infoWindows = new BMapGL.InfoWindow(dom, labelopts);
map.openInfoWindow(infoWindows, points); // 开启信息窗口 鼠标移出
});
};
_window.$detailsAction = function detailsAction() {
const dom = document.querySelector('.actionBtn') as HTMLElement;
let item: any = dom.getAttribute('data-item');
item = JSON.parse(item);
if (item.id) {
proxy.$router.push(`/deviceManage/projectDevice?projectid=${item.id}`);
}
if (item.deviceid) {
proxy.$router.push(`/deviceManage/sonDevice?deviceid=${item.deviceid}`);
}
};
const infoWindow = function (points: any, row: any) {
var opts = {
// width: 50,
// height: 100,
title: row.name,
};
var geoc = new BMapGL.Geocoder();
geoc.getLocation(points, (rs: any) => {
// 地址名称
let currentAddress = rs.address;
let dom = `
<div>${row.id ? "设备数量" : "分路数量"}: ${row.value}</div>
<div>详细地址: ${currentAddress}</div>
<div class="btnBox"><div class="actionBtn" data-item=${disposeJsonStr(JSON.stringify(row))} onclick="$detailsAction()">设备详情</div></div>`;
var infoWindows = new BMapGL.InfoWindow(dom, opts);
map.openInfoWindow(infoWindows, points); // 开启信息窗口 鼠标移出
state.loading = false;
});
};
return {
...toRefs(state),
};
},
});
</script>
<style scoped lang="scss">
#container {
height: 100%;
width: 100%;
}
:deep(.BMapLabel) {
.regionLabel {
color: #fff;
height: 28px;
line-height: 28px;
position: relative;
display: flex;
border: 1px solid #30f0f2;
border-radius: 5px 0 5px 0;
background: rgba(57, 91, 102, 0.6);
.name {
padding: 0 6px;
}
.value {
font-size: 16px;
border-radius: 0 0 5px 0;
padding: 0 6px;
}
.triangle {
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid #30f0f2;
width: 0;
height: 0;
position: absolute;
left: 15px;
bottom: -6.5px;
}
}
.projectBgImgLabel {
width: 56px;
height: 56px;
text-align: center;
line-height: 56px;
background-position: center;
color: #fff;
font-size: 12px;
.projectText {
position: absolute;
top: -30px;
left: 50%;
transform: translateX(-50%);
color: #fff;
height: 30px;
line-height: 30px;
display: flex;
border: 1px solid #30f0f2;
border-radius: 5px;
background: rgba(57, 91, 102, 1);
padding: 0 6px;
cursor: pointer;
.triangle {
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid #30f0f2;
width: 0;
height: 0;
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: -6.5px;
}
}
}
}
:deep(.BMap_bubble_pop) {
background: #0f2858 !important;
.BMap_bubble_title {
font-size: 16px;
color: #fff !important;
}
.BMap_bubble_center {
.BMap_bubble_content {
color: #fff !important;
.btnBox {
display: flex;
justify-content: flex-end;
margin: 5px;
height: 26px;
line-height: 26px;
.actionBtn {
border: 1px solid #ccc;
font-size: 12px;
background: transparent;
color: #fff;
padding: 0 5px;
border-radius: 3px;
cursor: pointer;
&:hover {
color: #6dcff6;
border: 1px solid #6dcff6;
}
}
}
.noneBtnBox {
display: none;
}
}
}
}
</style>
四.页面文件引用
1.在script脚本引入、注册组件
import Bmap from "./bmap.vue"
components: { Bmap },
2.在template模板使用组件
<Bmap></Bmap>
五.前后端交互的数据
1.拖拽和缩放地图的请求载荷
传给后端的字段是根据地图的缩放等级(grade)和地图的中心经度(lng)、地图的中心纬度(lat)
2.点击海量点的请求载荷
传给后端的字段是地图的被点击海量点的id(areaid)和地图的缩放等级(grade)
3.后端返回给前端的数据格式,都是数组嵌套对象
省、市、区、项目的数据,对象的字段是: name、value、latitude、longitude、id
设备的数据,对象的字段是: name、value、latitude、longitude、deviceid
省数据
市数据
区数据
项目数据
设备数据