功能:地点搜索、点击地图拾取坐标和地址。
效果图:
npm下载高德插件:
npm i @amap/amap-jsapi-loader
全部代码:
<template>
<el-dialog v-model="dialogVisible" title="选择地点" width="800" :before-close="handleClose">
<div class="map-container">
<div id="container" class="map-content"></div>
<div class="search-box">
<el-input v-model="searchKeyword" placeholder="请输入地址" @keyup.enter="searchPlace">
<template #append>
<el-button @click="searchPlace">搜索</el-button>
</template>
</el-input>
<div v-if="searchResults.length > 0" class="search-results">
<div
v-for="(item, index) in searchResults" :key="index" class="result-item"
@click="selectSearchResult(item)">
{{ item.name }} - {{ item.address }}
</div>
</div>
</div>
<div class="location-info" v-if="selectedLocation">
<p>当前选择位置:{{ selectedLocation.address }}</p>
<p>经度:{{ selectedLocation.lng }} 纬度:{{ selectedLocation.lat }}</p>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="sure" :disabled="!selectedLocation">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, watch, nextTick, onUnmounted } from 'vue'
import AMapLoader from '@amap/amap-jsapi-loader'
// 分别定义 JS API key 和 Web 服务 key
const JSAPI_KEY = '' // JS API key
const WEBSERVICE_KEY = '' // Web 服务 key
const props = defineProps({
show: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['update:show', 'select-location','handleClose'])
const dialogVisible = ref(false)
const map = ref(null)
const marker = ref(null)
const searchKeyword = ref('')
const searchResults = ref([])
const selectedLocation = ref(null)
const placeSearch = ref(null)
watch(() => props.show, (newValue) => {
dialogVisible.value = newValue
if (newValue) {
// 延迟初始化地图,确保容器已经渲染
nextTick(() => {
initMap()
})
}
})
watch(() => dialogVisible.value, (val) => {
emit('update:show', val)
})
const initMap = async () => {
if (map.value) return
try {
const AMap = await AMapLoader.load({
key: JSAPI_KEY, // 使用 JS API key
version: '2.0'
})
map.value = new AMap.Map('container', {
zoom: 11,
center: [116.397428, 39.90923]
})
// 点击地图事件
map.value.on('click', async (e) => {
const lnglat = e.lnglat
let lnglatArr = [lnglat.lng, lnglat.lat]
updateMarker(lnglatArr)
await getAddress(lnglatArr)
})
} catch (e) {
console.error('地图加载失败:', e)
}
}
const updateMarker = (lnglat) => {
if (marker.value) {
marker.value.setPosition(new AMap.LngLat(lnglat[0], lnglat[1]))
} else {
marker.value = new AMap.Marker({
position: new AMap.LngLat(lnglat[0], lnglat[1]),
map: map.value
})
}
}
const getAddress = async (lnglat) => {
try {
const response = await fetch(
`https://restapi.amap.com/v3/geocode/regeo?key=${WEBSERVICE_KEY}&location=${lnglat[0]},${lnglat[1]}&extensions=base&batch=false&roadlevel=0`
)
const data = await response.json()
if (data.status === '1' && data.info === 'OK') {
selectedLocation.value = {
address: data.regeocode.formatted_address,
lng: lnglat[0],
lat: lnglat[1]
}
} else {
console.error('获取地址失败:', data)
throw new Error('获取地址失败')
}
} catch (error) {
console.error('获取地址失败:', error)
}
}
const searchPlace = async () => {
if (!searchKeyword.value) return
try {
const response = await fetch(
`https://restapi.amap.com/v3/place/text?key=${WEBSERVICE_KEY}&keywords=${encodeURIComponent(searchKeyword.value)}&city=全国&children=1&offset=20&page=1&extensions=base`
)
const data = await response.json()
if (data.status === '1' && data.info === 'OK') {
searchResults.value = data.pois.map(poi => ({
name: poi.name,
address: poi.address,
location: {
lng: poi.location.split(',')[0],
lat: poi.location.split(',')[1]
}
}))
}
} catch (error) {
console.error('搜索地点失败:', error)
}
}
const selectSearchResult = (item) => {
const lnglatArr = [item.location.lng, item.location.lat]
map.value.setCenter(new AMap.LngLat(lnglatArr[0], lnglatArr[1]))
updateMarker(lnglatArr)
selectedLocation.value = {
address: item.address,
lng: lnglatArr[0],
lat: lnglatArr[1]
}
searchResults.value = [] // 清空搜索结果
}
const handleClose = () => {
dialogVisible.value = false
selectedLocation.value = null
searchResults.value = []
searchKeyword.value = ''
emit('handleClose')
}
const sure = () => {
if (selectedLocation.value) {
emit('select-location', selectedLocation.value)
}
handleClose()
}
// 组件销毁时清理地图实例
onUnmounted(() => {
if (map.value) {
map.value.destroy()
map.value = null
}
})
</script>
<style lang="scss" scoped>
.map-container {
position: relative;
width: 100%;
height: 500px;
}
.map-content {
width: 100%;
height: 100%;
}
.search-box {
position: absolute;
top: 10px;
left: 10px;
width: 300px;
z-index: 1;
}
.search-results {
background: white;
border: 1px solid #dcdfe6;
border-radius: 4px;
margin-top: 4px;
max-height: 200px;
overflow-y: auto;
}
.result-item {
padding: 8px 12px;
cursor: pointer;
&:hover {
background-color: #f5f7fa;
}
}
.location-info {
position: absolute;
bottom: 10px;
left: 10px;
background: white;
padding: 10px;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
z-index: 1;
p {
margin: 5px 0;
}
}
</style>