1. 安装
bash
npm i @amap/amap-jsapi-loader --save
移步:官方文档
2. map组件封装
javascript
<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) {
//实例化DistrictSearch
var opts = {
subdistrict: 0, //获取边界不需要返回下级行政区
extensions: 'all', //返回行政区边界坐标组等具体信息
level: 'district' //查询行政级别为 市
}
district.value = new AMap.DistrictSearch(opts)
}
//行政区查询
district.value.setLevel('district')
var keyword = props.districtCode
if (keyword === '') {
console.warn('行政区划不能为空')
return
}
let polygon = null
district.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].boundaries
if (bounds) {
//生成行政区划polygon
for (var i = 0; i < bounds.length; i += 1) {
//构造MultiPolygon的path
bounds[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.target
polyEditor.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. 使用组件
javascript
<template>
<el-drawer v-model="drawer2" :direction="direction" size="75%" :show-close="false">
<template #header>
<div>
<div
class="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>
<Map
bindId="startMap"
v-model:modelValue="startPolygonPaths"
v-if="drawer2"
ref="StartMapRef"
:polygonPaths="startDetailPaths"
:districtCode="curRow.startDistrictCode"
/>
</div>
</div>
<div class="mx-10px w-50%">
<div>
<Map
bindId="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 = row
await 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>