腾讯位置服务点标记
效果:
1、子组件
<template>
<div class="tengxunMapMarker" :style="{ height: `${height}px` }">
<div :id="`container-${mid}`" class="xn-wh"></div>
</div>
</template>
<script setup name="tengxunMapMarker">
import { onMounted, onUnmounted, ref, shallowRef } from 'vue'
const props = defineProps({
mid: {
type: Number,
default: new Date().getTime()
},
height: {
type: Number,
default: 800
},
apiKey: {
type: String,
required: true
},
center: {
type: Array
},
zoom: {
type: Number,
default: 12
},
mapStyle: {
type: String,
default: '1', // 默认普通样式
validator(value) {
return ['1', '2', '3', '4'].includes(value) // 1:普通 2:夜景 3:卫星 4:混合
}
},
markerCluster: {
type: Boolean,
default: true
}
})
const emits = defineEmits(['complete', 'markerClick'])
const map = shallowRef(null)
const markers = ref([])
const infoWindows = ref({})
// 初始化地图
const initMap = () => {
// 确保腾讯地图JS API已加载
if (!window.TMap) {
const script = document.createElement('script')
script.type = 'text/javascript'
script.src = `https://map.qq.com/api/gljs?v=1.exp&key=${props.apiKey}`
script.onload = () => {
createMap()
}
document.head.appendChild(script)
} else {
createMap()
}
}
// 创建地图实例
const createMap = () => {
// console.log('center....', props.center)
map.value = new TMap.Map(document.getElementById(`container-${props.mid}`), {
zoom: props.zoom,
mapStyleId: props.mapStyle
})
if (props.center) {
map.value.setCenter(props.center)
}
// 添加地图点击事件监听
map.value.on('click', (evt) => {
emits('markerClick', {
lnglat: {
lng: evt.latLng.lng,
lat: evt.latLng.lat
}
})
})
map.value.on('tilesloaded', () => {
emits('complete')
})
}
// 添加标记点
const renderMarker = (dataArr) => {
clearOverlay()
const markerLayer = new TMap.MultiMarker({
map: map.value,
styles: {
default: new TMap.MarkerStyle({
width: 25,
height: 35,
anchor: {x: 12.5, y: 35},
})
},
geometries: dataArr.map((item) => {
return {
id: item.id || String(Math.random()),
position: new TMap.LatLng(item.position[1], item.position[0]), // 纬度在前,经度在后
properties: {
title: item.title || ''
}
}
})
})
// console.log('Marker layer created:', markerLayer)
markers.value.push(markerLayer)
}
// 添加信息窗体
const renderInfoWindow = (dataArr) => {
dataArr.forEach((item) => {
const position = new TMap.LatLng(item.position[0], item.position[1])
const info = new TMap.InfoWindow({
map: map.value,
position,
content: item.content.join('<br>'),
offset: {x: 0, y: -32}
})
infoWindows.value[item.position.join(',')] = info
info.close() // 默认关闭
})
}
// 打开信息窗体
const openInfoWindow = (position) => {
const key = position.join(',')
if (infoWindows.value[key]) {
infoWindows.value[key].open()
}
}
// 清除覆盖物
const clearOverlay = () => {
markers.value.forEach((marker) => {
marker.setMap(null)
})
markers.value = []
Object.values(infoWindows.value).forEach((info) => {
info.setMap(null)
})
infoWindows.value = {}
}
// 绘制线段
const renderPolyline = (dataArr, option = {}) => {
const polylineLayer = new TMap.MultiPolyline({
map: map.value,
styles: {
style_blue: new TMap.PolylineStyle({
color: option.strokeColor || '#3777FF',
width: option.strokeWeight || 2,
borderWidth: option.borderWeight || 1,
opacity: option.strokeOpacity || 0.5
})
},
geometries: [
{
styleId: 'style_blue',
paths: dataArr.map((item) => new TMap.LatLng(item.position[0], item.position[1]))
}
]
})
markers.value.push(polylineLayer)
}
// 绘制圆形
const renderCircle = (position, radius, option = {}) => {
const circleLayer = new TMap.MultiCircle({
map: map.value,
styles: {
circle: new TMap.CircleStyle({
color: option.fillColor || '#3777FF',
fillColor: option.fillColor || '#3777FF',
fillOpacity: option.fillOpacity || 0.5,
strokeColor: option.strokeColor || '#3777FF',
strokeWidth: option.strokeWeight || 2,
strokeOpacity: option.strokeOpacity || 0.5
})
},
geometries: [
{
center: new TMap.LatLng(position[0], position[1]),
radius: radius
}
]
})
markers.value.push(circleLayer)
}
// 暴露获取地址的方法
const getAddress = async (lnglat) => {
return new Promise((resolve) => {
geocoder.getAddress(lnglat, (status, result) => {
if (status === 'complete' && result.info === 'OK') {
resolve(result.regeocode.formattedAddress)
} else {
resolve(null)
}
})
})
}
onMounted(() => {
setTimeout(() => {
initMap()
}, 100)
})
onUnmounted(() => {
if (map.value) {
map.value.destroy()
}
})
defineExpose({
renderMarker,
renderInfoWindow,
openInfoWindow,
clearOverlay,
renderPolyline,
renderCircle,
getAddress
})
</script>
<style lang="less">
.tengxunMapMarker {
position: relative;
overflow: hidden;
.xn-wh {
width: 100%;
height: 100%;
min-height: 300px;
}
}
</style>
2、父组件
<a-form-item>
<a-alert message="点击地图,快速选择地点" type="warning" closable/>
<TengxunMapMarker
:zoom="17"
:height="300"
:apiKey="props.apiKey"
@markerClick="onMapClick"
@complete="onMapComplete"
:center="mapCenter"
:pitch="30"
ref="tengxunMapRef"
/>
</a-form-item>
const mapCenter = computed(() => ({
lat: Number(latitude.value),
lng: Number(longitude.value)
}))
// 处理第一个地图的点击事件(选择中心点)
const onMapClick = (e) => {
// 更新表单数据
lng = e.lnglat.lng.toFixed(6)
lat = e.lnglat.lat.toFixed(6)
// 更新地图中心点
latitude.value = Number(e.lnglat.lat)
longitude.value = Number(e.lnglat.lng)
tengxunMapRef.value.renderMarker([
{
id: 'current',
position: [longitude.value, latitude.value], // 经度在前,纬度在后
title: '当前位置'
}
])
}
const onMapComplete = () => {
if (formData.value.id) {
latitude.value = Number(formData.value.lat)
longitude.value = Number(formData.value.lng)
tengxunMapRef.value.renderMarker([
{
id: formData.value.id,
position: [formData.value.lng, formData.value.lat], // 经度在前,纬度在后
title: formData.value.name || '标注点'
}
])
}
}