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

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

1、准备工作

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

2、项目安装依赖

复制代码
npm i @amap/amap-jsapi-loader --save

3、地图容器

html

html 复制代码
<template>
    <!-- 绘制地图区域组件(圆、矩形、多边形) -->
    <el-dialog
        v-model="state.showDialog"
        :title="props.title"
        @close="handleClose"
        destroy-on-close
        :close-on-click-modal="false"
        :close-on-press-escape="false"
        align-center
        draggable
        :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-button
                        type="primary"
                        @click="handleEditor(1)"
                        :disabled="state.drawType === 2 || state.drawType === 3 || props.isView">
                        {{ state.areaLayer && state.drawType === 1 ? '编辑圆' : '添加圆' }}
                    </el-button>
                    <el-button
                        type="primary"
                        @click="handleEditor(2)"
                        :disabled="state.drawType === 1 || state.drawType === 3 || props.isView">
                        {{ state.areaLayer && state.drawType === 2 ? '编辑矩形' : '添加矩形' }}
                    </el-button>
                    <el-button
                        type="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

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要恢复为新增状态,让按钮可点击。

ts 复制代码
<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、实现效果如图

相关推荐
持续前行15 小时前
vscode 中找settings.json 配置
前端·javascript·vue.js
JosieBook16 小时前
【Vue】11 Vue技术——Vue 中的事件处理详解
前端·javascript·vue.js
安逸点16 小时前
Vue项目中使用xlsx库解析Excel文件
vue.js
一只小阿乐16 小时前
vue 改变查询参数的值
前端·javascript·vue.js·路由·router·网文·未花中文网
小酒星小杜17 小时前
在AI时代下,技术人应该学会构建自己的反Demo地狱系统
前端·vue.js·ai编程
Code知行合壹17 小时前
Pinia入门
vue.js
今天也要晒太阳47317 小时前
element表单和vxe表单联动校验的实现
vue.js
依赖_赖18 小时前
前端实现token无感刷新
前端·javascript·vue.js
hhcccchh19 小时前
学习vue第十三天 Vue3组件深入指南:组件的艺术与科学
javascript·vue.js·学习
zhengxianyi51519 小时前
ruoyi-vue-pro本地环境搭建(超级详细,带异常处理)
前端·vue.js·前后端分离·ruoyi-vue-pro