高德地图2.0 绘制、编辑多边形覆盖物(电子围栏)
1. 安装
npm i @amap/amap-jsapi-loader --save
移步:官方文档
2. map组件封装
<script lang="ts" setup>
import AMapLoader from '@amap/amap-jsapi-loader'
import { onMounted, ref } from 'vue'
import { propTypes } from '@/utils/propTypes'defineOptions({ name: 'Map' })const props = defineProps({bindId: propTypes.string.def('mapContainer'),modelValue: propTypes.array.def([]),title: propTypes.string.def(''),width: propTypes.string.def('100%'),height: propTypes.string.def('600px'),polygonPaths: propTypes.array.def([]), // 回显多边形路径 得是<nubmer[]>类型,不然无法编辑districtCode: propTypes.string.def('140101') // 行政区划代码
})watch(() => props.districtCode,(newVal, oldVal) => {if (newVal !== oldVal) {drawBounds()}}
)window._AMapSecurityConfig = {securityJsCode: '4e6ca573a89ac3176f29813d3fcc895e'
}
const mouseTool = ref()
const mapRef = ref(null)
const overlays = ref<object[]>([])
const polyEditor = ref<any>()
const district = ref<any>()
const AMapObj = ref<any>()// 新建
const createPolygon = () => {polyEditor.value.close()polyEditor.value.setTarget()polyEditor.value.open()
}const emits = defineEmits(['update:modelValue'])
//获取
const getPolygon = () => {let overlays = mapRef.value?.getAllOverlays('polygon')let polygonPaths = overlays.map((overlay: any) => overlay.getPath())polygonPaths.shift() // 去掉第一个多边形console.log('🚀 ~ getPolygon ~ polygonPaths:', polygonPaths)const paths: object[] = []polygonPaths.forEach((item: any) => {const pathItem = item.map((cItem: any) => {const lnglat = `${cItem.lng},${cItem.lat}`return lnglat})paths.push(pathItem)})emits('update:modelValue', paths)
}defineExpose({ getPolygon })//清除绘制的多边形
const clearPolygon = () => {try {closeEditor()mapRef.value?.clearMap()drawBounds()} catch (e) {console.log(e)}
}// 开启编辑
const openEditor = () => {if (polyEditor.value) {polyEditor.value.open()}
}// 关闭编辑
const closeEditor = () => {if (polyEditor.value) {polyEditor.value.close()}
}//加载行政区划插件
const drawBounds = () => {if (!district.value) {//实例化DistrictSearchvar opts = {subdistrict: 0, //获取边界不需要返回下级行政区extensions: 'all', //返回行政区边界坐标组等具体信息level: 'district' //查询行政级别为 市}district.value = new AMap.DistrictSearch(opts)}//行政区查询district.value.setLevel('district')var keyword = props.districtCodeif (keyword === '') {console.warn('行政区划不能为空')return}let polygon = nulldistrict.value.search(keyword, function (status, result) {if (polygon) {mapRef.value?.remove(polygon) //清除上次结果polygon = null}if (!result || !result.districtList || !result.districtList[0]) {console.warn('请正确填写名称或更新其他名称')return}var bounds = result.districtList[0].boundariesif (bounds) {//生成行政区划polygonfor (var i = 0; i < bounds.length; i += 1) {//构造MultiPolygon的pathbounds[i] = [bounds[i]]}polygon = new AMap.Polygon({path: bounds,strokeColor: '#F56C6C',strokeWeight: 4,fillOpacity: 0.1,fillColor: '#F56C6C',strokeStyle: 'dashed',strokeDasharray: [12, 3],zIndex: 0})mapRef.value?.add(polygon)mapRef.value?.setFitView(polygon) //视口自适应}})
}// 编辑事件处理
const editPolygon = () => {polyEditor.value.on('add', function (data) {var polygon = data.targetpolyEditor.value.addAdsorbPolygons(polygon)polygon.on('dblclick', (e) => {polyEditor.value.setTarget(polygon)polyEditor.value.open()})})// polyEditor.value.on('adjust', function (data) {// console.log('🚀 ~ polyEditor-adjust', data)// })// polyEditor.value.on('addnode', function (data) {// console.log('🚀 ~ polyEditor-addnode', data)// })// polyEditor.value.on('end', function (data) {// console.log('🚀 ~ polyEditor-end', data)// })
}// 回显多边形
const showPolygon = (polygonArr: any) => {// const polygonArr = [[116.368904, 39.913423], [116.382122, 39.901176],[116.387271, 39.912501],[116.398258, 39.9046]]const polygon = new AMap.Polygon({path: polygonArr,strokeColor: '#1791fc',strokeOpacity: 0.9,strokeWeight: 4,fillColor: '#1791fc',fillOpacity: 0.3,strokeStyle: 'solid', //solid, 线样式还支持 'dashed',strokeDasharray: [12, 4], // strokeStyle是dashed时有效zIndex: 16})// polygon.on('click', (e) => {// console.log('🚀 ~ polygon.on ~ e:', e)// })mapRef.value?.add(polygon)polyEditor.value.addAdsorbPolygons([polygon])polygon.on('dblclick', () => {polyEditor.value.setTarget(polygon)polyEditor.value.open()})mapRef.value?.setFitView(polygon) //视口自适应
}// defineExpose({ showPolygon })// 加载高德地图
const loader = () => {AMapLoader.load({key: '947ec8e0b6869f9ef9fc6badda641a06',version: '2.0', // 使用合适的版本plugins: ['AMap.Scale', 'AMap.MouseTool', 'AMap.PolygonEditor', 'AMap.DistrictSearch']}).then((AMap) => {AMapObj.value = AMap// 初始化地图mapRef.value = new AMap.Map(props.bindId, {zoom: 15,center: [116.397428, 39.90923] // 设置地图中心点})// 加载行政区划插件drawBounds()// 编辑事件处理polyEditor.value = new AMap.PolygonEditor(mapRef.value)editPolygon()// 回显保存的多边形props.polygonPaths.forEach((item: any) => {showPolygon(item)})mapRef.value?.on('click', (e) => {console.log('🚀 ~ mapRef.value.on ~ e:', e)})}).catch((error) => {console.error('加载高德地图失败', error)})
}onMounted(() => {loader()
})
</script><template><div class="mb-14px"><el-button @click="createPolygon" type="primary">新建</el-button><!-- <el-button @click="openEditor" type="primary">编辑</el-button> --><el-button @click="closeEditor" type="primary">关闭编辑</el-button><el-button @click="clearPolygon" type="danger" plain>清空</el-button><!-- <el-button @click="getPolygon" type="primary">获取</el-button> --></div><div :id="props.bindId" :style="{ width: width, height: height }"></div>
</template>
3. 使用组件
<template><el-drawer v-model="drawer2" :direction="direction" size="75%" :show-close="false"><template #header><div><divclass="w-full flex items-center justify-between border-b border-b-[var(--el-border-color)] border-b-solid pb14px"><div class="font-size-4">服务区域</div><el-button type="primary" @click="close" size="small" circle><Icon icon="ep:close-bold" :size="16" @click="cancelClick" /></el-button></div></div></template><template #default><div class="flex items-center justify-center pb30px font-size-15px color-#666">起始地: {{ curRow.startDistrictName }}<el-icon class="mx-20px"><Switch /></el-icon>目的地: {{ curRow.endDistrictName }}</div><div class="flex justify-center"><div class="mx-10px w-50%"><div><MapbindId="startMap"v-model:modelValue="startPolygonPaths"v-if="drawer2"ref="StartMapRef":polygonPaths="startDetailPaths":districtCode="curRow.startDistrictCode"/></div></div><div class="mx-10px w-50%"><div><MapbindId="endMap"v-model:modelValue="endPolygonPaths"v-if="drawer2"ref="EndMapRef":polygonPaths="endDetailPaths":districtCode="curRow.endDistrictCode"/></div></div></div></template><template #footer><div style="flex: auto"><el-button @click="cancelClick">取消</el-button><el-button type="primary" @click="confirmClick">确认</el-button></div></template></el-drawer>
</template><script lang="ts" setup>
import { Switch } from '@element-plus/icons-vue'
import { ref, nextTick } from 'vue'
import { Map } from '@/components/Map/index'
import { getLinesServiceAreaList, saveLinesServiceArea } from '@/api/routeManage/routeList/index.ts'
const drawer2 = ref(false)
const direction = ref('rtl')
const curRow = ref<object>({})
const dcistrictCode = ref('')
const startPolygonPaths = ref([])
const endPolygonPaths = ref([])
const startDetailPaths = ref([])
const endDetailPaths = ref([])
const message = useMessage()// 打开推窗
const open = async (row) => {curRow.value = rowawait getDetail()drawer2.value = true
}
defineExpose({ open })const StartMapRef = ref(null)
const EndMapRef = ref(null)
const getDetail = async () => {const params = {...}try {const data = await getLinesServiceAreaList(params)...startDetailPaths.value =[[112.557711,37.731122],[112.625256,37.734871],[112.558896,37.680496],[112.558896,37.680496]]endDetailPaths.value = [[112.676067,36.378644],[112.79624,36.385946],[112.732752,36.29097],[112.732752,36.29097]]} finally {}
}// 获取起点数据
const getStartData = () => {const linesServiceAreaList = startPolygonPaths.value.map((item) => {if (item) {return { areaType: 1, pointList: item }}})return linesServiceAreaList || []
}// 获取终点数据
const getEndData = () => {const linesServiceAreaList = endPolygonPaths.value.map((item) => {if (item) {return { areaType: 1, pointList: item }}})return linesServiceAreaList || []
}const confirmClick = async () => {await StartMapRef.value?.getPolygon()await EndMapRef.value?.getPolygon()const params = {areaList: [...getStartData(), ...getEndData()]}try {await saveLinesServiceArea(params)message.success('保存成功')} finally {drawer2.value = false}
}function cancelClick() {drawer2.value = false
}
</script>