高德地图在vue3项目中使用:实现画矢量图、编辑矢量图

devtools/2024/12/22 18:47:53/

使用高德地图实现画多边形、矩形、圆,并进行编辑保存和回显。

1、准备工作

参考高德地图官网,进行项目key申请,链接: 准备

2、项目安装依赖

npm i @amap/amap-jsapi-loader --save

3、地图容器

html

<template><!-- 绘制地图区域组件(圆、矩形、多边形) --><el-dialogv-model="state.showDialog":title="props.title"@close="handleClose"destroy-on-close:close-on-click-modal="false":close-on-press-escape="false"align-centerdraggable:style="{ padding: 0, width: dialogWidth, height: dialogHeight }"><div class="position" v-if="state.showDialog"><div class="hover-txt" :style="{left: state.pointX+'px', top: state.pointY+'px'}">{{ hoverTxt }}</div><div ref="mapRef" id="container"></div><div class="search_box"><div class="change-btn"><el-button :type="state.stateLayer === 0 ? '' : 'primary'" @click="changeMap"> 卫星图 </el-button><el-button :type="state.stateLayer === 1 ? '' : 'primary'" @click="changeMap"> 标准地图 </el-button></div><div class="btn"><el-buttontype="primary"@click="handleEditor(1)":disabled="state.drawType === 2 || state.drawType === 3 || props.isView">{{ state.areaLayer && state.drawType === 1 ? '编辑圆' : '添加圆' }}</el-button><el-buttontype="primary"@click="handleEditor(2)":disabled="state.drawType === 1 || state.drawType === 3 || props.isView">{{ state.areaLayer && state.drawType === 2 ? '编辑矩形' : '添加矩形' }}</el-button><el-buttontype="primary"@click="handleEditor(3)":disabled="state.drawType === 1 || state.drawType === 2 || props.isView">{{ state.areaLayer && state.drawType === 3 ? '编辑多边形' : '添加多边形' }}</el-button><el-button type="danger" @click="handleClear" :disabled="props.isView">清除</el-button><el-button type="primary" @click="handleEmit" :disabled="props.isView">完成</el-button></div></div></div></el-dialog>
</template>

css

<style lang="scss" scoped>.position {box-sizing: border-box;padding: 0 20px;width: 100%;height: 100%;position: relative;#container {width: 100%;height: 100%;}.hover-txt {position: absolute;z-index: 50;border: 1px solid #303133;background: #fff;color: #303133;padding: 2px 4px;}.search_box {width: 100%;height: 50px;background-color: #fff;position: absolute;top: 0;left: 20px;.change-btn {position: absolute;right: 40px;}.btn {margin-right: 20px;display: flex;align-items: center;.search_input {width: 100px;margin-right: 10px;}}}}
</style>

4、业务逻辑

需求是只画多边形、矩形、圆三者中的一个,保存完之后再进行编辑。如果实现画多个图形,须把保存图层变量换成数组,每次编辑完之后,drawState要恢复为新增状态,让按钮可点击。

<script lang="ts" setup>import { onBeforeUnmount, onMounted, reactive, shallowRef, computed } from 'vue';import AMapLoader from '@amap/amap-jsapi-loader';import { getCompanyInfo } from '@/api/base/enterpriseInfo';import { ElMessage } from 'element-plus';const props = defineProps({// 区域范围areaRangeInfo: {type: String,default: '',},// 弹窗大小size: {type: Number,default: 2,},// 查看定位isView: {type: Boolean,default: false,},// 弹窗标题title: {type: String,default: '选取区域',},});//一定要加上密钥,否则服务类插件会失效,个人账户每天只有5000次提示机会(window as any)._AMapSecurityConfig = {key: 'xxxxxxxxxxxxxxxxxxxxxxxxx',securityJsCode: 'xxxxxxxxxx',};onBeforeUnmount(() => {// 移除监听map.off('click', (e: any) => {console.log('移除点击事件');});// 移除悬浮事件if(state.mousemoveEvent) {state.mousemoveEvent.removeEventListener('mousemove', (e:any) => {state.pointX = -500;state.pointY = -500;});}});onMounted(async () => {if(props.areaRangeInfo) {state.areaLayerInfo = JSON.parse(props.areaRangeInfo);}if (state.areaLayerInfo && state.areaLayerInfo.type && state.areaLayerInfo.type != 0) {state.drawType = Number(state.areaLayerInfo.type);state.drawState = 1;state.lng = state.areaLayerInfo.center[0];state.lat = state.areaLayerInfo.center[1];} else {state.drawState = -1;await getCompanyInfo('').then((res: any) => {if (res.code == 0) {if (!res.data.dzLng || !res.data.dzLat) {state.lng = 116.397935;state.lat = 39.900402;} else {state.lng = Number(res.data.dzLng);state.lat = Number(res.data.dzLat);}}});}await initMap();});let map = shallowRef(null as any);  // 高德地图实例const state = reactive({pointX: -500,pointY: -500,lng: 0 as number,               // 经度lat: 0 as number,               // 纬度AMap: null as any,              // 高德地图对象showDialog: true,               // 控制弹窗状态stateLayer: 1 as number,        // 0 街道 1 卫星drawType: 0 as number,          // 绘制图形类型 0 未绘制 1 圆 2 矩形 3 多边形drawState: -1 as number,        // -1 未绘制或已结束绘制 0 新增状态 1 编辑状态areaEditor: null as any,        // 画圆、矩形、多边形的map实例对象linePoints: [] as any,          // 点位坐标areaLayer: null as any,         // 画的图层实例mouseTool: null as any,         // 绘图工具mousemoveEvent: null as any,areaLayerInfo: null as any,     // 图层位置信息});const emits = defineEmits(['submit', 'handleClose']);const dialogWidth = computed(() => {if (props.size == 1) {return '86%';} else if (props.size == 2) {return '78%';} else {return '72%';}});const dialogHeight = computed(() => {if (props.size == 1) {return '90vh';} else if (props.size == 2) {return '82vh';} else {return '76vh';}});let satelliteLayer: any = null;let defaultStreet: any = null;// 初始化地图const initMap = () => {AMapLoader.load({key: '1e7b6041cfc63f32e8eafdf98005e45f',version: '2.0',plugins: ['AMap.ToolBar',         // 工具条,控制地图的缩放、平移'AMap.Scale',           // 比例尺'AMap.ControlBar',      // 组合了旋转、倾斜、复位在内的地图控件'AMap.Geolocation',     // 定位插件],}).then((AMap: any) => {state.AMap = AMap;map = new AMap.Map('container', {viewMode: '2D',zoom: 17,center: [state.lng, state.lat],});// 添加卫星图覆盖satelliteLayer = new AMap.TileLayer.Satellite();// 默认矢量街道图defaultStreet = new AMap.TileLayer();map.add([satelliteLayer]);// 加载工具类插件map.addControl(new AMap.ToolBar({position:{right: '40px', top: '160px'}}));map.addControl(new AMap.Scale());map.addControl(new AMap.ControlBar({position:{right: '10px', top: '60px'}}));map.addControl(new AMap.Geolocation());// 判断当前区域类型if(state.areaLayerInfo && state.areaLayerInfo.type) {// 绘制图形类型 0 未绘制 1 圆 2 矩形 3 多边形state.drawType = Number(state.areaLayerInfo.type);if(state.drawType == 1) {state.areaLayer = new AMap.Circle({center: state.areaLayerInfo.path,radius: state.areaLayerInfo.radius, //半径strokeColor: '#1791FC',strokeOpacity: 1,strokeWeight: 2,fillColor: '#1791FC',fillOpacity: 0.4,cursor:'pointer',strokeStyle: 'solid'});map.add(state.areaLayer);} else if(state.drawType == 2) {let southWest:any = new AMap.LngLat(state.areaLayerInfo.southWest[0], state.areaLayerInfo.southWest[1])let northEast:any = new AMap.LngLat(state.areaLayerInfo.northEast[0], state.areaLayerInfo.northEast[1])let bounds:any = new AMap.Bounds(southWest, northEast)state.areaLayer = new AMap.Rectangle({bounds: bounds,strokeColor: '#1791FC',strokeOpacity: 1,strokeWeight: 2,fillColor: '#1791FC',fillOpacity: 0.4,cursor:'pointer',strokeStyle: 'solid'});map.add(state.areaLayer);} else if(state.drawType == 3) {state.areaLayer = new AMap.Polygon({path: state.areaLayerInfo.path,strokeColor: '#1791FC',strokeOpacity: 1,strokeWeight: 2,fillColor: '#1791FC',fillOpacity: 0.4,cursor:'pointer',strokeStyle: 'solid'});map.add(state.areaLayer);}}// 地图点击事件map.on('click', (e: any) => {// console.log(e)});let domEvent:any = document.getElementById('container');state.mousemoveEvent = domEvent.addEventListener('mousemove', (e:any) => {if(state.drawType !== 0 && state.drawState !== -1) {state.pointX = e.offsetX + 30;state.pointY = e.offsetY + 15;}});})};// 获取图层位置信息const getAreaLayerInfo = () => {let centerInfo:any = map.getCenter();if(state.drawType === 1) {// 圆let centerPoint:any = state.areaLayer.getCenter();let radius:any = state.areaLayer.getRadius();state.areaLayerInfo = {type: '1',path: [centerPoint.lng, centerPoint.lat],radius: radius,center: [centerInfo.lng, centerInfo.lat]}} else if(state.drawType === 2) {let pathObj:any = state.areaLayer.getBounds();state.areaLayerInfo = {type: state.drawType,path: [[pathObj.southWest.lng,pathObj.southWest.lat],[pathObj.northEast.lng,pathObj.northEast.lat]],southWest: [pathObj.southWest.lng,pathObj.southWest.lat],northEast: [pathObj.northEast.lng,pathObj.northEast.lat],center: [centerInfo.lng, centerInfo.lat]}} else if(state.drawType === 3) {// 多边形let pathArr:Array<any> = state.areaLayer.getPath();let pointArr:Array<any> = [];if(pathArr.length > 0) {pathArr.map((point:any) => {pointArr.push([point.lng, point.lat])});state.areaLayerInfo = {type: state.drawType,path: pointArr,center: [centerInfo.lng, centerInfo.lat]}} else {state.areaLayerInfo = {type: '0',path: [],center: []}}} else {state.areaLayerInfo = {type: '0',path: []}}emits('submit', state.areaLayerInfo);state.showDialog = false;};// 开始画区域const handleEditor = (type: number) => {if(state.areaLayer) {state.drawState = 1;} else {state.drawState = 0;}state.AMap.plugin('AMap.MouseTool', () => {// 异步加载插件state.mouseTool = new state.AMap.MouseTool(map);state.drawType = type;switch (state.drawType) {case 1:if(state.drawState === 0) {// 新增圆handleCircleDraw();} else {// 编辑圆handleCircleEditor();}break;case 2:if(state.drawState === 0) {// 新增矩形handleRectangleDraw();} else {// 编辑矩形handleRectangleEditor();}break;case 3:if(state.drawState === 0) {// 新增多边形handlePolygonDraw();} else {// 编辑多边形handlePolygonEditor();}break;}state.mouseTool.on('draw',(e:any) => {// 绘制结束state.mouseTool.close(false); // 关闭state.drawState = -1;state.pointX = -500;state.pointY = -500;map.setDefaultCursor('pointer');if(state.drawType === 1) {state.areaLayer = map.getAllOverlays('circle')[0];} else if(state.drawType === 2) {state.areaLayer = map.getAllOverlays('rectangle')[0];} else if(state.drawType === 3) {state.areaLayer = map.getAllOverlays('polygon')[0];}})});};// 开始画圆const handleCircleDraw = () => {map.setDefaultCursor('crosshair');state.mouseTool.circle({strokeColor: '#1791FC',strokeOpacity: 1,strokeWeight: 2,fillColor: '#1791FC',fillOpacity: 0.4,cursor:'pointer',strokeStyle: 'solid'});};// 开始编辑圆const handleCircleEditor = () => {state.AMap.plugin('AMap.CircleEditor', () => {// 异步加载插件if (state.areaLayer) {state.areaEditor = new state.AMap.CircleEditor(map, state.areaLayer);state.areaEditor.open();}});};// 开始画矩形const handleRectangleDraw = () => {map.setDefaultCursor('crosshair');state.mouseTool.rectangle({strokeColor: '#1791FC',strokeOpacity: 1,strokeWeight: 2,fillColor: '#1791FC',fillOpacity: 0.4,cursor:'pointer',strokeStyle: 'solid'});};// 开始编辑矩形const handleRectangleEditor = () => {state.AMap.plugin('AMap.RectangleEditor', () => {// 异步加载插件if (state.areaLayer) {state.areaEditor = new state.AMap.RectangleEditor(map, state.areaLayer);state.areaEditor.open();}});};// 开始画多边形const handlePolygonDraw = () => {map.setDefaultCursor('pointer');state.mouseTool.polygon({strokeColor: '#1791FC',strokeOpacity: 1,strokeWeight: 2,fillColor: '#1791FC',fillOpacity: 0.4,cursor:'pointer',strokeStyle: 'solid'});};// 开始编辑多边形const handlePolygonEditor = () => {state.AMap.plugin('AMap.PolyEditor', () => {// 异步加载插件if (state.areaLayer) {state.areaEditor = new state.AMap.PolyEditor(map, state.areaLayer);state.areaEditor.open();}});};// 清除矢量图形const handleClear = () => {// 清除画区域实例对象if(state.areaEditor) {state.areaEditor.close();}// 清除图层if(state.areaLayer) {map.clearMap();}state.areaEditor = null;state.linePoints = [];state.areaLayer = null;state.drawState = 0;state.drawType = 0;};// 完成const handleEmit = () => {// 退出编辑状态if(state.areaEditor) {state.areaEditor.close();state.areaEditor = null;}state.pointX = -500;state.pointY = -500;state.drawState = -1;getAreaLayerInfo();};// 关闭弹窗const handleClose = () => {emits('handleClose');};// 切换地图const changeMap = () => {if (state.stateLayer == 0) {map.remove([defaultStreet]);map.add([satelliteLayer]);state.stateLayer = 1;} else {map.remove([satelliteLayer]);map.add([defaultStreet]);state.stateLayer = 0;}};// 悬浮提示const hoverTxt = computed(() => {if (state.drawType === 1) {return state.areaLayer ? '点击完成结束编辑圆' : '按住左键并拖拽绘制圆';} else if (state.drawType === 2) {return state.areaLayer ? '点击完成结束编辑矩形' : '按住左键并拖拽绘制矩形';} else if (state.drawType === 3) {return state.areaLayer ? '点击完成结束编辑多边形' : '点击地图选择拐点,右键结束绘制多边形';} else {return ''}});
</script>

5、实现效果如图

在这里插入图片描述


http://www.ppmy.cn/devtools/30804.html

相关文章

webpack 常用插件

clean-webpack-plugin 这个插件的主要作用是清除构建目录中的旧文件&#xff0c;以确保每次构建时都能得到一个干净的环境。 var { CleanWebpackPlugin } require("clean-webpack-plugin") const path require("path");module.exports {mode: "de…

基于深度学习检测恶意流量识别框架(80+特征/99%识别率)

基于深度学习检测恶意流量识别框架 目录 基于深度学习检测恶意流量识别框架简要示例a.检测攻击类别b.模型训练结果输出参数c.前端检测页面d.前端训练界面e.前端审计界面&#xff08;后续更新了&#xff09;f.前端自学习界面&#xff08;自学习模式转换&#xff09;f1.自学习模式…

【Vulhub靶场】Nginx 漏洞复现

Nginx 漏洞复现 一、Nginx 文件名逻辑漏洞&#xff08;CVE-2013-4547&#xff09;1、影响版本2、漏洞原理3、漏洞复现 二、Nginx 解析漏洞1、版本信息&#xff1a;2、漏洞详情3、漏洞复现 一、Nginx 文件名逻辑漏洞&#xff08;CVE-2013-4547&#xff09; 1、影响版本 Nginx …

呆马科技——智慧应急执法监管平台

在当今社会&#xff0c;安全生产的重要性日益凸显。对于各级政府和企事业单位&#xff0c;当务之急是如何高效地对突发事件进行执法管理。平台应运而生&#xff0c;旨在通过信息化、智能化技术&#xff0c;提升安全管理的效率与准确性。 一、平台特点 整合各类平台的信息资源&…

什么是网络安全等级保护测评(等保测评)?

什么是网络安全等级保护测评&#xff08;等保测评&#xff09;呢&#xff1f;今天永恒无限就为大家介绍下网络安全等级保护测评&#xff08;等保测评&#xff09; 网络安全等级保护测评&#xff08;等保测评&#xff09;是指对信息和信息系统按照重要性等级进行的保护测评。它…

Android4.4真机移植过程笔记(三)

如果文章字体看得不是很清楚&#xff0c;大家可以下载pdf文档查看&#xff0c;文档已上传&#xff5e;oo&#xff5e; 7、安装加密APK 需要修改文件如下&#xff1a; 相对Android4.2改动还是蛮大的&#xff0c;有些文件连路径都变了: //Android4.2 1、frameworks/native/libs…

《深入理解mybatis原理》 MyBatis的架构设计以及实例分析

《深入理解mybatis原理》 MyBatis的架构设计以及实例分析 MyBatis是目前非常流行的ORM框架&#xff0c;它的功能很强大&#xff0c;然而其实现却比较简单、优雅。本文主要讲述MyBatis的架构设计思路&#xff0c;并且讨论MyBatis的几个核心部件&#xff0c;然后结合一个select查…

笔记-mathtype公式在PDF或打印出来显示不全

原文中的公式&#xff1a; 纸质版打印出来的公式有缺失 问题描述&#xff1a;mathtype公式编辑器所编辑的公式转成PDF或者打印出来有缺失 以下是解决方法的具体描述。 目录 一、准备工作二、操作步骤 一、准备工作 1、工具&#xff1a;mathtype、微软word 二、操作步骤 …