这类需求我一年能接到十几回:后台管理系统里要画地块、画区域、做标注,画完还得能改、能存、能还原。标题里的“高德地图JS API地块多样式多图形绘制编辑导出导入删除”基本就是一套完整的地图绘制模块需求。我最初以为只是调几个API就行,后来发现高德开放平台的文档只提供了散装能力,把它们串成一套完整业务工具需要自己处理大量细节。这篇文章就把我实际落地的一套方案拆开讲,覆盖图形绘制、多样式管理、编辑交互、数据导入导出、批量删除和常见坑,适合做GIS项目、地块管理后台、园区招商系统的前端同学参考。
1. 整体设计思路:先把“地块编辑器”拆成四个能力
1.1 这类需求到底在解决什么问题
很多业务场景都需要在地图上画“地块”:园区管理系统里标注入驻企业占地,招商系统里划分可售楼栋和地块,农业平台圈定种植区域,甚至物业系统里画停车场和绿化边界。纯展示地图满足不了这类需求,用户要求的是能交互:新增一个地块、编辑边界、换个颜色代表不同用途、把数据保存下来下次打开还能看到。
“多样式”这个词很关键。地块不是画出来就完事,不同性质的地块得用不同颜色区分,否则一片全绿谁分得清哪块是住宅、哪块是商业?这里样式的背后其实是业务属性。“多图形”则是说,除了多边形地块,可能还要用到圆、矩形、线、点标记,比如圆形表示半径范围,矩形做快速圈选,折线画道路或轨迹,标记点做定位。“绘制编辑导出导入删除”连在一起,才是完整闭环。
1.2 技术选型:高德JS API加自研交互层
高德JS API本身提供覆盖物对象,包括AMap.Polygon、AMap.Circle、AMap.Rectangle、AMap.Polyline、AMap.Marker。绘制方面有AMap.MouseTool插件,这个插件封装了鼠标在地图上画图的能力。但这里有一个问题:MouseTool只管画,画完之后你拿到一个覆盖物对象,后面要编辑、要管理场景数据、要序列化保存,它一概不管。
我的做法是把高德覆盖物当成“渲染层”,业务上再包一层统一数据结构。每个图形在业务层是一个对象,包含图形类型、业务属性、样式配置、图形坐标数据,同时内部持有对应的高德覆盖物实例。地图上所有的删除、编辑、导入导出操作,都是先操作业务数据,再同步刷新覆盖物。这样代码结构清晰,也方便对接后端的保存接口。
1.3 数据模型和整体数据流
我定义的核心数据模型大致如下:
javascript复制// 每个地块或图形对应一个 item
{
id: 'plot_1697412345678',
type: 'polygon', // polygon | circle | rectangle | polyline | marker
name: 'A-01 号楼地块',
bizType: 'commercial', // 业务类型,决定默认样式
style: {
strokeColor: '#5470c6',
strokeWeight: 3,
fillColor: '#5470c6',
fillOpacity: 0.2,
lineDash: null,
// marker 样式
icon: 'https://...',
size: [26, 32]
},
// 坐标数据
path: [[lng, lat], [lng, lat]], // polygon / polyline / rectangle 用
center: [lng, lat], // circle、marker 用
radius: 500, // circle 用
extData: {}, // 自定义业务数据
overlay: null // 高德覆盖物实例,运行时不序列化
}
整体数据流是:用户操作 -> 更新这个item -> 调用渲染函数同步到地图覆盖物;后端保存时,把item数组里的overlay字段剥离,序列化成JSON传给接口;页面重新加载时,从接口拿JSON,先重建item,再逐个覆盖物渲染上地图。所有功能绕这个核心转,代码就不会散。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 绘制模块实现:多样式多图形的接入细节
2.1 接入AMap.MouseTool完成基础绘制
AMap.MouseTool是高德地图提供的鼠标绘图工具,支持画标记、折线、多边形、矩形、圆。用起来很简单,但有个容易踩的坑:插件加载方式是异步插件,需要在AMap.plugin回调里使用。我封装了一个绘制管理器,通过调用draw(type, styleOptions)来启动不同类型绘制:
javascript复制async function loadMouseTool() {
return new Promise((resolve) => {
AMap.plugin('AMap.MouseTool', () => {
resolve(true);
});
});
}
function startDraw(map, drawType, styleOptions) {
// 先关闭上一个绘制工具,避免叠加
if (window._mouseTool) {
window._mouseTool.close(true);
}
const mouseTool = new AMap.MouseTool(map);
window._mouseTool = mouseTool;
const drawMap = {
polygon: () => mouseTool.polygon(styleOptions),
rectangle: () => mouseTool.rectangle(styleOptions),
circle: () => mouseTool.circle(styleOptions),
polyline: () => mouseTool.polyline(styleOptions),
marker: () => mouseTool.marker(styleOptions)
};
drawMap[drawType]?.();
// 监听绘制完成事件,只挂一次
mouseTool.off('draw', onDrawDone);
mouseTool.on('draw', onDrawDone);
}
这里我特别解释下mouseTool.close(true)的作用。如果不主动关闭,上一个开启的绘制模式会一直生效,用户再次点击地图可能触发上一次的绘制回调,造成重复创建图形。true参数表示同时移除当前工具绘制过程中产生的辅助图形,比如画了一半的虚线段。不清理的话,用户取消绘制后地图上会残留一条虚线,非常恼人。
MouseTool的draw事件回调参数里,e.obj就是绘制完成的覆盖物实例,可以直接拿到坐标数据。对不同类型要分别处理坐标获取,这点细节较多,见下节。
javascript复制function onDrawDone(e) {
const overlay = e.obj;
const drawType = overlay.className; // 'AMap.Polygon' / ...
// 根据实际类型提取坐标,生成 item
const newItem = createItemFromOverlay(overlay, typeMapping[drawType]);
items.push(newItem);
bindOverlayEvents(newItem);
handleStyleChange(newItem, currentStylePreset);
}
overlay.className在高德覆盖物文档里不一定被强调,但实例上确实存在这个属性,可以用于判断当前图形类型。稳妥的做法是绘制前自己记住类型,或者用instanceof判断。我在项目里是绘制前传入
