You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

256 lines
6.0 KiB

<template>
<el-dialog
v-model="dialogVisible"
:title="title"
width="800px"
:close-on-click-modal="false"
@closed="handleClose"
>
<div class="map-search-box">
<el-input
v-model="searchKeyword"
placeholder="请输入地名搜索"
class="search-input"
@keyup.enter="handleSearch"
>
<template #append>
<el-button @click="handleSearch">搜索</el-button>
</template>
</el-input>
</div>
<div id="mapContainer" class="map-container"></div>
<template #footer>
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handleConfirm">确定</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, onMounted, watch, nextTick } from 'vue'
import AMapLoader from '@amap/amap-jsapi-loader'
// AMap
declare const AMap: any;
// Window
declare global {
interface Window {
_AMapSecurityConfig: {
securityJsCode: string;
};
}
}
const props = defineProps<{
modelValue: boolean
title?: string
lng?: string | number
lat?: string | number
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: boolean): void
(e: 'confirm', location: { lng: string, lat: string }): void
}>()
const dialogVisible = ref(false)
const searchKeyword = ref('')
const map = ref<any>(null)
const marker = ref<any>(null)
const currentLocation = ref<{ lng: string, lat: string }>({
lng: props.lng?.toString() || '',
lat: props.lat?.toString() || ''
})
// 监听弹窗显示状态
watch(() => props.modelValue, (val) => {
dialogVisible.value = val
if (val) {
nextTick(() => {
initMap()
})
}
})
// 监听经纬度变化
watch(() => [props.lng, props.lat], ([newLng, newLat]) => {
if (newLng && newLat) {
currentLocation.value = {
lng: newLng.toString(),
lat: newLat.toString()
}
// 如果地图已加载,则更新标记
if (map.value) {
updateMarker(newLng, newLat)
}
}
}, { immediate: true })
watch(dialogVisible, (val) => {
emit('update:modelValue', val)
})
// 初始化地图
const initMap = async () => {
try {
window._AMapSecurityConfig = {
securityJsCode: '33ae5910cb2d9ccabc9c1a2ed887181b'
}
const AMap = await AMapLoader.load({
key: '2467ab6c994eb7d244ed68a830d35cde',
version: '2.0',
plugins: ['AMap.PlaceSearch', 'AMap.Geocoder']
} as any)
// 确保在创建地图实例前清除旧的实例
if (map.value) {
map.value.destroy()
map.value = null
}
map.value = new AMap.Map('mapContainer', {
zoom: 15,
center: [currentLocation.value.lng || 121.4737, currentLocation.value.lat || 31.23037],
viewMode: '3D',
mapStyle: 'amap://styles/darkblue',
features: ['bg', 'building', 'point'],
skyColor: '#1E1E1E',
pitch: 40,
resizeEnable: true,
buildingAnimation: true, // 仅显示背景、建筑物和POI点
showBuildingBlock: true,
showIndoorMap: false,
showLabel: true, // 启用标签显示
showRoad: true, // 显示道路提供参考
showTraffic: false, // 不显示交通
zoomEnable: true, // 确保允许缩放
dragEnable: true, // 确保允许拖动
scrollWheel: true, // 启用鼠标滚轮缩放
keyboardEnable: true, // 启用键盘控制
doubleClickZoom: true, // 启用双击缩放
touchZoom: true, // 启用触摸缩放
jogEnable: true, // 地图
logoVisible: false, // 隐藏左下角高德地图标记
})
// 添加点击事件
map.value.on('click', (e: any) => {
const { lng, lat } = e.lnglat
updateMarker(lng, lat)
// 更新当前选中位置
currentLocation.value = {
lng: lng.toString(),
lat: lat.toString()
}
})
// 确保地图加载完成后再添加标记
map.value.on('complete', () => {
if (currentLocation.value.lng && currentLocation.value.lat) {
updateMarker(currentLocation.value.lng, currentLocation.value.lat)
}
})
} catch (error) {
console.error('地图初始化失败:', error)
}
}
// 更新标记位置
const updateMarker = (lng: number | string, lat: number | string) => {
if (!map.value) return
// 清除旧的标记
if (marker.value) {
marker.value.setMap(null)
marker.value = null
}
// 创建新标记
marker.value = new AMap.Marker({
position: [lng, lat],
map: map.value,
animation: 'AMAP_ANIMATION_DROP'
})
// 更新当前选中位置
currentLocation.value = {
lng: lng.toString(),
lat: lat.toString()
}
// 将地图中心移动到标记位置
map.value.setCenter([lng, lat])
}
// 搜索位置
const handleSearch = async () => {
if (!searchKeyword.value || !map.value) return
try {
const placeSearch = new AMap.PlaceSearch({
city: '全国'
})
placeSearch.search(searchKeyword.value, (status: string, result: any) => {
if (status === 'complete' && result.poiList?.pois?.length > 0) {
const poi = result.poiList.pois[0]
const { location } = poi
map.value.setCenter([location.lng, location.lat])
updateMarker(location.lng, location.lat)
}
})
} catch (error) {
console.error('搜索失败:', error)
}
}
// 确认选择
const handleConfirm = () => {
emit('confirm', currentLocation.value)
dialogVisible.value = false
}
// 取消选择
const handleCancel = () => {
dialogVisible.value = false
}
// 关闭弹窗时清理地图实例
const handleClose = () => {
if (map.value) {
map.value.destroy()
map.value = null
}
if (marker.value) {
marker.value = null
}
}
</script>
<style scoped>
.map-container {
width: 100%;
height: 400px;
margin: 16px 0;
}
.map-search-box {
margin-bottom: 16px;
}
.search-input {
width: 100%;
}
:deep(.el-input-group__append) {
padding: 0;
}
:deep(.el-input-group__append .el-button) {
margin: 0;
border: none;
height: 100%;
padding: 0 15px;
}
</style>