一,安装
pnpm install @amap/amap-jsapi-loader
二,使用手册
1,vue3 引用,初始化
<template>
<div id="mechanism-map"></div>
</template>
<script setup lang="ts">
import AMapLoader from '@amap/amap-jsapi-loader'
let map = null
let AMapObj = null
// 地图初始化
const initMap = async() => {
try {
AMapObj = await AMapLoader.load({
key: import.meta.env.VITE_APP_MAP_KEY,
version: '2.0',
plugins: [] // ['AMap.Geocoder', 'AMap.Geolocation'] 定位插件
})
} catch (e) {
console.error('地图初始化失败:', e)
}
}
onMounted(async () => {
initMap()
})
onUnmounted(() => {
map?.destroy()
})
</script>
2,地图显示
2.1 地图加载、点击事件,中心点显示
// 地图显示,并标记中心点
const getMarker = (lng, lat) => {
const lnglat = new AMapObj.LngLat(lng, lat)
// 地图
map = new AMapObj.Map('mechanism-map', {
viewMode: '2D',
zoom: 11,
center: lnglat,
mapStyle: 'amap://styles/macaron', // 主题
showLabel: true,
showIndoorMap: true
})
map.setStatus({
dragEnable: true,
zoomEnable: false
})
// 添加回到已知位置的按钮
setReloadLocal(longitude, latitude)
// 添加本地位置标记
localMarker(lnglat)
// 地图点击事件
map.on('click', (e) => {
proxy.$router.push('/mechanism-map')
})
// 地图加载完事件
map.on('complete', onLabelMarker)
}
const initMap = async () => {
try {
AMapObj = await AMapLoader.load({
key: import.meta.env.VITE_APP_MAP_KEY,
version: '2.0',
plugins: ['AMap.Geocoder', 'AMap.Geolocation', 'AMap.CitySearch']
})
const { longitude, latitude } = proxy.locationInfo
getMarker(longitude, latitude)
} catch (e) {
console.error('地图初始化失败:', e)
}
}
2.2 添加本地位置标记
const localMarker = (lnglat) => {
const icon = new AMapObj.Icon({
size: new AMapObj.Size(72, 72),
image: new URL('../../assets/point.png', import.meta.url).href,
imageSize: new _AMap.Size(26, 26)
})
new _AMap.Marker({
map: map,
icon, // 扩展需要的图标,没有则显示默认的图标
position: lnglat
})
map.add(marker)
}
2.3 添加回到已知位置的按钮
const setReloadLocal = (longitude: number, latitude: number) => {
const imgDiv = document.createElement('div')
imgDiv.classList.add('amap-geolocation-reload')
imgDiv.style.position = 'absolute'
imgDiv.style.bottom = '100px'
imgDiv.style.right = '15px'
imgDiv.style.padding = '4px'
imgDiv.style.borderRadius = '50px'
imgDiv.style.background = '#fff'
imgDiv.style.fontSize = '0'
imgDiv.style.boxShadow = '0 0 5px silver'
const imgElement = document.createElement('img')
imgElement.src = new URL('../../assets/map_position.png', import.meta.url).href
imgElement.width = 24
imgElement.height = 24
imgDiv.appendChild(imgElement)
document.body.appendChild(imgDiv)
// 点击按钮时回到已知位置
imgElement.addEventListener('click', () => {
const { longitude, latitude } = proxy.locationInfo
// 设置地图中心点
map?.setCenter([lng || Number(longitude), lat || Number(latitude)])
// 可选:设置缩放级别
map?.setZoom(11)
})
}

image.png
2.4 标记避让
标注是否避让,true:避让,不会把所有位置相近的点都显示出来;false:不避让,把所有点都显示出来。
const onLabelMarker = () => {
labelsLayerIcon = new _AMap.LabelsLayer({
zooms: [2, 20],
zIndex: 100,
collision: true // 标注是否避让
})
map.add(labelsLayerIcon)
setLabelMarker()
// 地图点击事件
map.on('click', markerUnBind)
// 地图加载完成事件
map.on('complete', () => {
if (result.value.length > 0) {
console.log('bbbdddd')
}
})
}
// 点击其他区域隐藏所有标记点的文本和样式
const markerUnBind = () => {
markers.forEach((marker) => {
marker.setIcon({
size: [24, 24]
})
})
normalMarker && map?.remove(normalMarker)
}
3,AMap.Geolocation 精度定位插件
const getGeolocation = () => {
try {
// 初始化定位插件
map.plugin('AMap.Geolocation', () => {
const geolocation = new AMapObj.Geolocation({
enableHighAccuracy: true, //是否使用高精度定位,默认:true
timeout: 10000, //超过10秒后停止定位,默认:无穷大
maximumAge: 0, //定位结果缓存0毫秒,默认:0
convert: true, //自动偏移坐标,偏移后的坐标为高德坐标,默认:true
showButton: true, //显示定位按钮,默认:true
buttonPosition: 'RB', //定位按钮停靠位置,默认:'LB',左下角
buttonOffset: new AMap.Pixel(10, 20), //定位按钮与设置的停靠位置的偏移量,默认:Pixel(10, 20)
showMarker: true, //定位成功后在定位到的位置显示点标记,默认:true
showCircle: true, //定位成功后用圆圈表示定位精度范围,默认:true
panToLocation: true, //定位成功后将定位到的位置作为地图中心点,默认:true
zoomToAccuracy: false //定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false
})
geolocation.getCurrentPosition((status, result) => {
if (status === 'complete') {
console.log('定位成功', result)
} else {
console.error('定位失败', result)
}
})
// 监听定位成功事件
geolocation.on('complete', (result) => {
console.log('定位成功', result)
const { position } = result // 获取定位结果
const lng = position.lng // 经度
const lat = position.lat // 纬度
console.log(`当前位置:经度 ${lng},纬度 ${lat}`)
})
// 监听定位失败事件
geolocation.on('error', (error) => {
console.error('定位失败', error)
})
}
} catch(e) {
console.log('定位失败', e)
}
}

image.png

image.png
4,逆地理编码
// 通过经纬度获取地址信息
const getAddress = (lng, lat) => {
try {
map.plugin('AMap.Geolocation', () => {
const geocoder = new AMapObj.Geocoder({
radius: 1000,
extensions: 'all'
})
geocoder.getAddress([lng, lat], (status, result) => {
console.log('逆地理编码结果', status, result)
if (status === 'complete' && result.info === 'OK') {
if (result.regeocode) {
// 地址信息
console.log('address', result.regeocode.formattedAddress)
}
}
})
}
} catch (e) {
console.log('')
}
}
5,AMap.ToolBar 放大缩小地图插件
工具条只显示放大缩小按钮
AMapLoader.load({
key: import.meta.env.VITE_APP_MAP_KEY,
version: '2.0',
plugins: ['AMap.ToolBar']
})
.then(async (AMap) => {
_AMap = AMap
const { longitude, latitude } = proxy.locationInfo
const lnglat = new AMap.LngLat(longitude, latitude)
map = new AMap.Map('mechanism-map', {
viewMode: '2D',
zoom: 11,
center: lnglat,
showLabel: true,
showIndoorMap: true
})
// 添加工具条控件
map.addControl(new AMap.ToolBar({
position: 'RB', // 将工具条显示在右下角,LT:左上角;LB:左下角;RT:右上角
}))
map.on('complete', onLabelMarker)
})
.catch((e) => {
console.error(e)
})

image.png
6,AMap.LabelMarker 带标签的标记点
6.1 直接显示标点文本
const result = ref([])
result.value = [{
"type": "1",
"agmx0100": "1904421574694694913",
"agmx0101": "大连中山桂林养护院",
"agmx0135": "121.66",
"agmx0136": "38.91"
},
{
"type": "2",
"agmx0100": "1904769910488461314",
"agmx0101": "大连庄河市颐养服务中心",
"agmx0135": "122.984648",
"agmx0136": "39.691699"
},
{
"type": "3",
"agmx0100": "1904693894285914113",
"agmx0101": "大连市椒金山养老服务中心",
"agmx0135": "121.608556",
"agmx0136": "38.978"
},
{
"type": "4",
"agmx0100": "1904693894285914113",
"agmx0101": "大连市椒金山养老服务中心",
"agmx0135": "121.608556",
"agmx0136": "38.978"
}]
let map = null
let _AMap = null
let markers = null
let labelsLayerIcon = null
const onLabelMarker = () => {
labelsLayerIcon = new _AMap.LabelsLayer({
zooms: [2, 20],
zIndex: 100,
collision: true // 标注是否避让,true:避让,不会把所有位置相近的点都显示出来;false:不避让,把所有点都显示出来。
})
// 将标记点添加到地图上。
map.add(labelsLayerIcon)
const textColor = ['#32a5f1', '#25c8b1', '#4922e5', '#ef1530']
const icons = [
new URL('./assets/map_mechanism.png', import.meta.url).href,
new URL('./assets/map_community.png', import.meta.url).href,
new URL('./assets/map_canteen.png', import.meta.url).href,
new URL('./assets/map_branches.png', import.meta.url).href
]
result.value.forEach((item) => {
if (item.agmx0135 && item.agmx0136) {
let labelMarker = new _AMap.LabelMarker({
name: '标注',
// 确保经纬度是有效的数字
position: checkIfRepeatLayer(Number(item.agmx0135), Number(item.agmx0136)).position,
icon: {
type: 'image',
image: icons[Number(item.type) - 1] || icons[0],
size: [24, 24],
anchor: 'bottom-center'
},
text: {
content: item.agmx0101,
direction: 'bottom', // 文字显示位置,left, right, top, bottom
style: {
fold: true, // 支持回行,一行最多6个字
fontSize: 12,
fillColor: textColor[Number(item.type) - 1] || textColor[0]
}
},
// 其他属性,通过 .getExtData() 获取
extData: { ...item, Number(item.agmx0136)).repeat },
rank: Number(item.type), // 避让显示的层级,最大显示最上面
zIndex: Number(item.type)
})
markers.push(labelMarker)
标记点击事件
labelMarker.on('click', labelMarkerClick)
}
})
// labelMarker 统一添加到地图
labelsLayerIcon.add(markers)
}
// 点击标记点时的事件
const labelMarkerClick = (event) => {
const targetMarker = event.target // 获取当前点击的标记点
const position = event.data.data && event.data.data.position
const targetData = targetMarker.getExtData() // 获取标记点的数据
// 点击一次地图层级放大一级
if (map.getZoom() < 20) {
map.zoomIn()
}
// 将点击的图标设置为中心点
map.setCenter(position)
}

image.png
6.2 点击标点显示文本
// 普通提示点
let normalMarker = null
const onLabelMarker = () => {
labelsLayerIcon = new _AMap.LabelsLayer({
zooms: [2, 20],
zIndex: 100,
collision: true // 标注是否避让
})
map.add(labelsLayerIcon)
const textColor = ['#32a5f1', '#25c8b1', '#4922e5', '#ef1530']
const icons = [
new URL('./assets/map_mechanism.png', import.meta.url).href,
new URL('./assets/map_community.png', import.meta.url).href,
new URL('./assets/map_canteen.png', import.meta.url).href,
new URL('./assets/map_branches.png', import.meta.url).href
]
result.value.forEach((item) => {
if (item.agmx0135 && item.agmx0136) {
let labelMarker = new _AMap.LabelMarker({
name: '标注',
position: checkIfRepeatLayer(Number(item.agmx0135), Number(item.agmx0136)).position,
icon: {
type: 'image',
image: icons[Number(item.type) - 1] || icons[0],
size: [24, 24],
anchor: 'bottom-center'
}
extData: { ...item, Number(item.agmx0136)).repeat },
rank: Number(item.type),
zIndex: Number(item.type)
})
markers.push(labelMarker)
labelMarker.on('click', labelMarkerClick)
}
})
labelsLayerIcon.add(markers)
// 标题提示点
normalMarker = new _AMap.Marker({
anchor: 'bottom-center',
offset: [0, -36]
})
map.on('click', markerUnBind)
}
// 点击标记点时的事件
const labelMarkerClick = (event) => {
const targetMarker = event.target // 获取当前点击的标记点
const position = event.data.data && event.data.data.position
const targetData = targetMarker.getExtData() // 获取标记点的数据
// 点击一次地图层级放大一级
if (map.getZoom() < 20) {
map.zoomIn()
}
map.setCenter(position)
markerUnBind()
// 当前点击图标放大
targetMarker.setIcon({
size: [28, 28]
})
// 点击弹出文本区域标点
if (position) {
normalMarker.setContent('<div class="amap-info-window">' + targetData.agmx0101 + '</div>')
normalMarker.setPosition(position)
map.add(normalMarker)
}
}
// 点击其他区域隐藏所有标记点的文本和样式
const markerUnBind = () => {
markers.forEach((marker) => {
// 恢复原点大小
marker.setIcon({
size: [24, 24]
})
})
// 移除普通文本显示标记
normalMarker && map?.remove(normalMarker)
}
<style lang="scss" scoped>
:deep(.amap-info-window) {
flex: none;
font-size: 12px;
background: #8f8f8f;
color: #fff;
border-radius: 6px;
padding: 5px 12px;
box-shadow: 0 2px 6px 0 rgba(133, 133, 133, 0.5);
position: relative;
&::after {
content: '';
display: block;
position: absolute;
top: 25px;
bottom: 0;
left: 50%;
margin-left: -8px;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 8px solid #8f8f8f;
}
}
}
</style>

image.png

image.png
7,替换地图主题
// 主题
mapStyle: 'amap://styles/macaron', // 马卡龙
mapStyle: 'amap://styles/whitesmoke' // 远山黛
mapStyle: 'amap://styles/normal' // 标准
const initMap = () => {
AMapLoader.load({
key: import.meta.env.VITE_APP_MAP_KEY,
version: '2.0',
plugins: ['AMap.LabelsLayer']
})
.then(async (AMap) => {
_AMap = AMap
const { longitude, latitude } = proxy.locationInfo
const lnglat = new AMap.LngLat(longitude, latitude)
map = new AMap.Map('mechanism-map', {
viewMode: '2D',
zoom: 11,
center: lnglat,
showLabel: true,
mapStyle: 'amap://styles/macaron', // 主题
labelRejectMask: true,
showIndoorMap: true
})
})
.catch((e) => {
console.error(e)
})
}

image.png

image.png

image.png
8,导航功能
<template>
<div>
<div class="intro-content_service">
<p class="flex-between-center" @click="onMap()">导航至当前位置</p>
</div>
<van-action-sheet
v-model:show="mapShow"
:actions="actions"
cancel-text="取消"
close-on-click-action
@select="openAmapNavigation"
/>
</div>
</template>
<script setup lang="ts">
const mapShow = ref(false)
const actions = [{ name: '高德地图' }]
const onMap = () => {
mapShow.value = true
}
const startLocal = ref(null)
const endLocal = ref(null)
const locationInfo = {
longitude: 'xxx.xx',
latitude: 'xxxx.xx',
address: '当前位置地址'
}
const webGaoDeMap = () => {
try {
// 浏览器端跳转高德
const [startLng, startLat, startName] = startLocal.value
const [endLng, endLat, endName] = endLocal.value
let path = `?from=${startLng},${startLat},${encodeURIComponent(
startName
)}&to=${endLng},${endLat},${encodeURIComponent(endName)}&mode=car`
// 桌面端会打开网页版
const webUrl = `https://uri.amap.com/navigation/${path}`
window.open(webUrl, '_blank')
} catch (err) {
console.error('高德地图导航异常:', err)
}
}
// 打开高德地图导航
const openAmapNavigation = () => {
startLocal.value = [Number(locationInfo.longitude), Number(locationInfo.latitude), locationInfo.address]
if (!endLocal.value || endLocal.value.length == 0) {
console.error('没有终点地址')
return
}
// 浏览器端跳转高德
webGaoDeMap()
}
</script>
<style lang="scss" scoped></style>
完整代码
<template>
<div id="mechanism-map"></div>
</template>
<script setup lang="ts">
import AMapLoader from '@amap/amap-jsapi-loader'
import { fetchMapMechanismAll } from '@/api/business'
interface Props {
type?: string
}
const props = withDefaults(defineProps<Props>(), {
type: ''
})
const { proxy } = getCurrentInstance()
let map = null
let _AMap = null
let labelsLayerIcon = null
let markers = []
const result = ref([])
// 普通提示点
let normalMarker = null
watch(
() => props.type,
async () => {
labelsLayerIcon?.remove(markers)
markerUnBind()
returnToOrigin()
markers = []
await getMapMechanism()
onLabelMarker()
}
)
const returnToOrigin = (lng?: number, lat?: number) => {
const { longitude, latitude } = proxy.locationInfo
map?.setCenter([lng || Number(longitude), lat || Number(latitude)])
map?.setZoom(11)
}
const getMapMechanism = async () => {
try {
result.value = [
{
type: '1',
agmx0100: '1904421574694694913',
agmx0101: '大连中山桂林养护院',
agmx0135: '121.66',
agmx0136: '38.91'
},
{
type: '2',
agmx0100: '1904769910488461314',
agmx0101: '大连庄河市颐养服务中心',
agmx0135: '122.984648',
agmx0136: '39.691699'
},
{
type: '3',
agmx0100: '1904693894285914113',
agmx0101: '大连市椒金山养老服务中心',
agmx0135: '121.608556',
agmx0136: '38.978'
},
{
type: '4',
agmx0100: '1904693894285914113',
agmx0101: '大连市椒金山养老服务中心',
agmx0135: '121.608556',
agmx0136: '38.978'
}
]
} catch (error) {
console.error(error)
}
}
const onLabelMarker = () => {
labelsLayerIcon = new _AMap.LabelsLayer({
zooms: [2, 20],
zIndex: 100,
collision: true // 标注是否避让
})
map.add(labelsLayerIcon)
const textColor = ['#32a5f1', '#25c8b1', '#4922e5', '#ef1530']
const icons = [
new URL('./assets/map_mechanism.png', import.meta.url).href,
new URL('./assets/map_community.png', import.meta.url).href,
new URL('./assets/map_canteen.png', import.meta.url).href,
new URL('./assets/map_branches.png', import.meta.url).href
]
result.value.forEach((item) => {
if (item.agmx0135 && item.agmx0136) {
let labelMarker = new _AMap.LabelMarker({
name: '标注',
position: checkIfRepeatLayer(Number(item.agmx0135), Number(item.agmx0136)).position,
icon: {
type: 'image',
image: icons[Number(item.type) - 1] || icons[0],
size: [24, 24],
anchor: 'bottom-center'
},
text: {
content: item.agmx0101,
direction: 'bottom',
style: {
fontSize: 12,
fillColor: textColor[Number(item.type) - 1] || textColor[0]
}
},
extData: { ...item, repeat: checkIfRepeatLayer(Number(item.agmx0135), Number(item.agmx0136)).repeat },
rank: Number(item.type),
zIndex: Number(item.type)
})
markers.push(labelMarker)
labelMarker.on('click', labelMarkerClick)
}
})
labelsLayerIcon.add(markers)
// // 标题提示点
// normalMarker = new _AMap.Marker({
// anchor: 'top-center',
// offset: [0, -36]
// })
map.on('click', markerUnBind)
}
// 判断标记点是否经纬度一样
const checkIfRepeatLayer = (lng: number, lat: number) => {
let newLng = lng
let newLat = lat
// 筛选出位于目标位置的所有标记点
const overlappedMarkers = markers.filter((marker) => {
const position = marker.getPosition()
return position.lng === lng && position.lat === lat
})
if (overlappedMarkers.length > 0) {
newLng = Number(overlappedMarkers[overlappedMarkers.length - 1].getPosition().lng) + 0.00003
}
return {
position: [newLng, newLat],
repeat: overlappedMarkers.length > 0 ? true : false
}
}
// 点击标记点时的事件
const labelMarkerClick = (event) => {
const targetMarker = event.target // 获取当前点击的标记点
const position = event.data.data && event.data.data.position
const targetData = targetMarker.getExtData() // 获取标记点的数据
// 点击一次地图层级放大一级
if (map.getZoom() < 20) {
map.zoomIn()
}
// targetData.repeat && map.setZoom(20) // 如果有重复的标记点,放大地图
map.setCenter(position)
markerUnBind()
targetMarker.setIcon({
size: [28, 28]
})
// 点击弹出文本区域标点
// if (position) {
// normalMarker.setContent('<div class="amap-info-window">' + targetData.agmx0101 + '</div>')
// normalMarker.setPosition(position)
// map.add(normalMarker)
// }
}
// 点击其他区域隐藏所有标记点的文本和样式
const markerUnBind = () => {
markers.forEach((marker) => {
marker.setIcon({
size: [22, 22]
})
})
normalMarker && map?.remove(normalMarker)
emitter.emit('onVisibleState', false)
}
// 添加本地位置标记
const localMarker = (longitude: number, latitude: number) => {
const icon = new _AMap.Icon({
size: new _AMap.Size(72, 72),
image: new URL('../../assets/point.png', import.meta.url).href,
imageSize: new _AMap.Size(26, 26)
})
new _AMap.Marker({
map: map,
icon,
position: [longitude, latitude]
})
}
// 添加回到已知位置的按钮
const setReloadLocal = (longitude: number, latitude: number) => {
const imgDiv = document.createElement('div')
imgDiv.classList.add('amap-geolocation-reload')
imgDiv.style.position = 'absolute'
imgDiv.style.bottom = '100px'
imgDiv.style.right = '15px'
imgDiv.style.padding = '4px'
imgDiv.style.borderRadius = '50px'
imgDiv.style.background = '#fff'
imgDiv.style.fontSize = '0'
imgDiv.style.boxShadow = '0 0 5px silver'
const imgElement = document.createElement('img')
imgElement.src = new URL('../../assets/map_position.png', import.meta.url).href
imgElement.width = 24
imgElement.height = 24
imgDiv.appendChild(imgElement)
document.body.appendChild(imgDiv)
// 点击按钮时回到已知位置
imgElement.addEventListener('click', () => {
returnToOrigin(longitude, latitude)
})
}
const initMap = () => {
AMapLoader.load({
key: import.meta.env.VITE_APP_MAP_KEY,
version: '2.0',
plugins: ['AMap.Geolocation']
})
.then(async (AMap) => {
_AMap = AMap
const { longitude, latitude } = proxy.locationInfo
const lnglat = new AMap.LngLat(longitude, latitude)
map = new AMap.Map('mechanism-map', {
viewMode: '2D',
zoom: 11,
center: lnglat,
showLabel: true,
showIndoorMap: true
})
setReloadLocal(longitude, latitude)
localMarker(Number(longitude), Number(latitude))
map.on('complete', onLabelMarker)
})
.catch((e) => {
console.error(e)
})
}
onMounted(async () => {
await getMapMechanism()
initMap()
})
onUnmounted(() => {
map?.destroy()
const elements = document.querySelectorAll('.amap-geolocation-reload')
elements.forEach((element) => {
element?.remove() // 移除每个匹配的元素
})
})
</script>
<style lang="scss" scoped>
#mechanism-map {
:deep(.amap-toolbar) {
bottom: 110px !important;
}
:deep(.amap-geolocation) {
bottom: 100px !important;
}
:deep(.amap-marker) {
display: flex !important;
justify-content: center;
align-items: center;
}
:deep(.amap-info-window) {
flex: none;
font-size: 12px;
background: #8f8f8f;
color: #fff;
border-radius: 6px;
padding: 5px 12px;
box-shadow: 0 2px 6px 0 rgba(133, 133, 133, 0.5);
position: relative;
&::after {
content: '';
display: block;
position: absolute;
top: 25px;
bottom: 0;
left: 50%;
margin-left: -8px;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 8px solid #8f8f8f;
}
}
}
</style>
<style scoped>
#mechanism-map {
width: 100%;
height: 100vh;
}
</style>