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
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> |