
我们在开发中 总会有客户需求需要用户定位门店定位商家定位等这些是常见的一些需求所以自己写了一个组件记录一下。前期准备工作 首先需要安装高德插件 amap/amap-jsapi-loadernpm install amap/amap-jsapi-loader pnpm install amap/amap-jsapi-loader yarn add amap/amap-jsapi-loader // 三种方式都可以还需要到获取自己的高德应用的web 端的 密钥和key布局结构template el-dialog title选择位置 v-modeldialogVisible width70% closehandleClose :close-on-click-modalfalse destroy-on-close div classmap-container div idcontainer classamap-container/div div classsearch-box el-input v-modelsearchKeyword placeholder请输入关键字搜索全国地址例如上海市南京路、北京朝阳区 clearable keyup.enterhandleSearch classsearch-input template #append el-button clickhandleSearch :iconSearch搜索/el-button /template /el-input /div div classlocation-info v-ifselectedAddress div classinfo-title 当前位置信息/div div classinfo-grid div classinfo-item span classlabel详细地址/span span classvalue{{ selectedAddress.formattedAddress || -- }}/span /div div classinfo-item span classlabel省/span span classvalue{{ selectedAddress.province || -- }}/span /div div classinfo-item span classlabel市/span span classvalue{{ selectedAddress.city || -- }}/span /div div classinfo-item span classlabel区/县/span span classvalue{{ selectedAddress.district || -- }}/span /div div classinfo-item span classlabel街道/乡镇/span span classvalue{{ selectedAddress.township || -- }}/span /div div classinfo-item span classlabel道路/span span classvalue{{ selectedAddress.street || -- }}/span /div div classinfo-item span classlabel建筑/span span classvalue{{ selectedAddress.building || -- }}/span /div div classinfo-item span classlabel经纬度/span span classvalue{{ selectedAddress.lng?.toFixed(6) }}, {{ selectedAddress.lat?.toFixed(6) }}/span /div /div /div /div template #footer span classdialog-footer el-button clickhandleClose取消/el-button el-button typeprimary clickhandleConfirm :disabled!selectedAddress确认选择/el-button /span /template /el-dialog /templatejs结构script setup import { ref, watch, nextTick, onUnmounted } from vue; import AMapLoader from amap/amap-jsapi-loader; import { ElMessage } from element-plus; import { Search } from element-plus/icons-vue; const props defineProps({ modelValue: { type: Boolean, default: false } }); const emit defineEmits([update:modelValue, confirm]); const dialogVisible ref(props.modelValue); const searchKeyword ref(); const selectedAddress ref(null); let map null; let geocoder null; let marker null; let infoWindow null; let isMapReady false; // 监听 modelValue 变化 watch(() props.modelValue, (val) { dialogVisible.value val if (val) { nextTick(() { initMap() }) } }); // 监听 dialogVisible 变化 watch(dialogVisible, (val) { emit(update:modelValue, val); }); // 解析高德地址组件提取省市区详细地址 const parseAddressComponent (addressComponent) { return { province: addressComponent.province, city: addressComponent.city, district: addressComponent.district, township: addressComponent.township || , street: addressComponent.street || , streetNumber: addressComponent.streetNumber || , building: addressComponent.building || , citycode: addressComponent.citycode, adcode: addressComponent.adcode }; }; // 获取完整的地址信息通过经纬度 const getAddressByLngLat (lnglat) { return new Promise((resolve, reject) { if (!geocoder) { reject(new Error(地理编码器未初始化)); return } geocoder.getAddress(lnglat, (status, result) { if (status complete result.regeocode) { const regeocode result.regeocode; const addressComponent regeocode.addressComponent; const addressInfo { formattedAddress: regeocode.formattedAddress, ...parseAddressComponent(addressComponent), lat: lnglat.lat, lng: lnglat.lng, location: lnglat, adcode: addressComponent.adcode, citycode: addressComponent.citycode }; // 添加道路信息 if (regeocode.roads regeocode.roads.length 0) { addressInfo.road regeocode.roads[0].name; } // 添加POI信息 if (regeocode.pois regeocode.pois.length 0) { addressInfo.poi regeocode.pois[0].name; } resolve(addressInfo); } else { reject(new Error(获取地址信息失败)); } }); }); }; // 通过POI信息获取详细地址用于搜索 const getAddressByPoi (poi) { return new Promise((resolve, reject) { if (!geocoder) { reject(new Error(地理编码器未初始化)); return; } const lnglat new AMap.LngLat(poi.location.lng, poi.location.lat); geocoder.getAddress(lnglat, (status, result) { if (status complete result.regeocode) { const regeocode result.regeocode; const addressComponent regeocode.addressComponent; const addressInfo { formattedAddress: poi.address || regeocode.formattedAddress, name: poi.name, province: addressComponent.province, city: addressComponent.city, district: addressComponent.district, township: addressComponent.township || , street: addressComponent.street || , building: poi.building || addressComponent.building || , lat: poi.location.lat, lng: poi.location.lng, location: lnglat, adcode: addressComponent.adcode, citycode: addressComponent.citycode, pcode: addressComponent.pcode }; resolve(addressInfo); } else { // 如果逆地理编码失败至少返回POI的基本信息 resolve({ formattedAddress: poi.address || poi.name, name: poi.name, province: poi.pname || , city: poi.cityname || , district: poi.adname || , lat: poi.location.lat, lng: poi.location.lng, location: lnglat }); } }); }); }; // 搜索位置支持全国- 修复中心点问题 const handleSearch async () { if (!searchKeyword.value.trim()) { ElMessage.warning(请输入搜索关键字); return; } if (!map || !isMapReady) { ElMessage.warning(地图未初始化完成请稍后重试); return; } try { const AMap await AMapLoader.load({ key: 454c8e2f4a5acbed2215f8d85fba8188, version: 2.0 }); // 使用地点搜索服务进行全国搜索 const placeSearch new AMap.PlaceSearch({ city: 全国, citylimit: false, pageSize: 1, pageIndex: 1 }); ElMessage.info(正在搜索...); placeSearch.search(searchKeyword.value, async (status, result) { if (status complete result.poiList result.poiList.pois.length 0) { const poi result.poiList.pois[0]; const location new AMap.LngLat(poi.location.lng, poi.location.lat); console.log(搜索结果位置, location); // 关键修复确保地图中心点正确移动到搜索结果 // 方法1使用 setCenter 和 setZoom map.setCenter(location, true); // 第二个参数表示是否使用动画 map.setZoom(15); // 方法2同时使用 panTo 确保平滑移动可选 // map.panTo(location) // 强制刷新地图视图 setTimeout(() { map.setCenter(location); }, 100); // 获取详细地址信息 try { const addressInfo await getAddressByPoi(poi); selectedAddress.value addressInfo; addMarker(location, addressInfo); ElMessage.success(找到${poi.name}); } catch (error) { console.error(获取地址详情失败, error); // 即使获取详情失败也显示基本信息 selectedAddress.value { formattedAddress: poi.address || poi.name, name: poi.name, province: poi.pname || , city: poi.cityname || , district: poi.adname || , lat: poi.location.lat, lng: poi.location.lng, location: location }; addMarker(location, poi.name); ElMessage.warning(获取详细地址信息失败已使用基本信息); } } else { ElMessage.warning(未找到${searchKeyword.value}相关的位置请换个关键词试试); } }); } catch (error) { console.error(搜索失败, error); ElMessage.error(搜索失败请稍后重试); } }; // 添加地图标记 const addMarker (position, addressInfo) { if (marker) { map.remove(marker); } const AMap window.AMap; if (!AMap) return; const title addressInfo.name || addressInfo.formattedAddress || 选中位置; marker new AMap.Marker({ position: position, title: title, draggable: true, cursor: move, raiseOnDrag: true, bubble: true, offset: new AMap.Pixel(-13, -30) }); marker.on(dragend, async (e) { const lnglat e.target.getPosition(); try { const addressInfo await getAddressByLngLat(lnglat); selectedAddress.value addressInfo; updateMarkerContent(addressInfo); ElMessage.success(已更新位置信息); } catch (error) { console.error(拖拽获取位置失败, error); ElMessage.error(获取位置信息失败); } }); marker.on(click, (e) { const content div stylepadding: 12px; max-width: 300px; div stylefont-weight: bold; margin-bottom: 8px; color: #333;${title}/div div stylefont-size: 12px; color: #666; line-height: 1.5;${addressInfo.formattedAddress || }/div /div infoWindow.setContent(content); infoWindow.open(map, position); }); map.add(marker); // 打开信息窗口 const content div stylepadding: 12px; max-width: 300px; div stylefont-weight: bold; margin-bottom: 8px; color: #333;${title}/div div stylefont-size: 12px; color: #666; line-height: 1.5;${addressInfo.formattedAddress || }/div /div infoWindow.setContent(content); infoWindow.open(map, position); }; // 更新标记内容 const updateMarkerContent (addressInfo) { if (marker) { const title addressInfo.name || addressInfo.formattedAddress || 选中位置; marker.setTitle(title); } }; // 点击地图选点 const onMapClick async (e) { const lnglat e.lnglat; console.log(点击地图位置, lnglat); try { const addressInfo await getAddressByLngLat(lnglat); selectedAddress.value addressInfo; addMarker(lnglat, addressInfo); ElMessage.success(已获取位置信息); } catch (error) { console.error(获取地址失败, error); ElMessage.error(获取位置信息失败); } }; // 初始化地图 const initMap async () { if (map) { map.destroy(); map null; isMapReady false; } const container document.getElementById(container); if (!container) { console.error(地图容器不存在); return } try { window._AMapSecurityConfig { securityJsCode: //自己高德的密钥 } const AMap await AMapLoader.load({ key: , // 密钥对应的key version: 2.0, plugins: [AMap.Scale, AMap.ToolBar, AMap.Geocoder, AMap.PlaceSearch] }) map new AMap.Map(container, { zoom: 12, center: [116.397428, 39.90923], viewMode: 2D, resizeEnable: true }); // 添加工具条 map.addControl(new AMap.ToolBar({ position: RB // 定位到右上角 })); map.addControl(new AMap.Scale({ position: LB // 定位到左下角 })); // 初始化地理编码器 geocoder new AMap.Geocoder({ city: 全国, radius: 1000 }); // 初始化信息窗口 infoWindow new AMap.InfoWindow({ offset: new AMap.Pixel(0, -30), autoMove: true }); // 添加点击地图事件 map.on(click, onMapClick); // 地图加载完成标志 map.on(complete, () { isMapReady true; console.log(地图加载完成); }); console.log(地图初始化成功); } catch (error) { console.error(地图加载失败, error); ElMessage.error(地图加载失败请检查网络或配置); } }; // 确认选择 const handleConfirm () { if (selectedAddress.value) { emit(confirm, selectedAddress.value); handleClose(); } else { ElMessage.warning(请先选择一个位置); } }; // 关闭弹窗 const handleClose () { dialogVisible.value false; selectedAddress.value null; searchKeyword.value ; isMapReady false; if (marker) { map?.remove(marker); marker null; } }; // 组件卸载时销毁地图 onUnmounted(() { if (map) { map.destroy(); map null; } });css 结构style scoped .map-container { position: relative; width: 100%; height: 600px; } .amap-container { width: 100%; height: 100%; border-radius: 8px; overflow: hidden; } .search-box { position: absolute; top: 20px; left: 50%; transform: translateX(-50%); z-index: 100; width: 80%; max-width: 500px; min-width: 300px; } .search-input { width: 100%; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.15); } .location-info { position: absolute; bottom: 20px; left: 20px; right: 20px; background: rgba(255, 255, 255, 0.95); border-radius: 12px; padding: 16px 20px; box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.12); backdrop-filter: blur(8px); z-index: 100; border-left: 4px solid #409eff; max-height: 280px; overflow-y: auto; } .info-title { font-size: 14px; font-weight: 600; color: #303133; margin-bottom: 12px; padding-bottom: 8px; border-bottom: 1px solid #e4e7ed; } .info-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px 16px; } .info-item { font-size: 13px; line-height: 1.6; word-break: break-all; } .label { color: #909399; margin-right: 6px; } .value { color: #303133; font-weight: 500; } .dialog-footer { display: flex; justify-content: flex-end; gap: 12px; } /* 滚动条样式 */ .location-info::-webkit-scrollbar { width: 6px; } .location-info::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 3px; } .location-info::-webkit-scrollbar-thumb { background: #c1c1c1; border-radius: 3px; } .location-info::-webkit-scrollbar-thumb:hover { background: #a8a8a8; } /* 响应式调整 */ media (max-width: 768px) { .map-container { height: 500px; } .search-box { width: 90%; } .info-grid { grid-template-columns: 1fr; gap: 8px; } .location-info { padding: 12px 16px; } .info-item { font-size: 12px; } } /style在页面使用// 引入自己封装的文件 map import Map from /components/map.vue; Map v-modelmodelValue :lnginitialLng :latinitialLat confirmhandleLocationSelect/Map // 初始化经纬度 const initialLng ref(); const initialLat ref(); // 方法 const handleLocationSelect (location) { consloe.log(location) //返回信息 根据自己需求去获取赋值 };