高德地图经纬度显示点位及输入位置选择经纬度

194 阅读3分钟

根据经纬度展示地图,点击选择经纬度按钮打开弹窗(选择地点组件更新,可搜索或自己点击)

image.png

image.png 父组件

<template>
  <div>
    <el-row :gutter="24">
      <el-col :span="6">
        <el-form-item label="经纬度" prop="latLng">
          <el-input v-model="latLng" placeholder="请选择经纬度" @clear="clearLatLng"  clearable />
        </el-form-item>
      </el-col>

      <el-col :span="6">
        <el-button type="primary" @click="openChoose">选择经纬度</el-button>
      </el-col>
    </el-row>

    <lat-lng-area ref="latLngArea"></lat-lng-area>

    <lat-lng-choose ref="latLngChoose" @chooseSuccess="chooseSuccess"></lat-lng-choose>
  </div>

</template>

<script setup>

import LatLngChoose from "../../latLngChoose/latLngChoose.vue";
import LatLngArea from "../../latLngChoose/latLngArea.vue";
import {useRoute} from "vue-router";

const route = useRoute()
const emit = defineEmits(['success'])

const latLngChoose = ref(null)
const latLngArea = ref(null)

const props = defineProps({
  latLng: {
    type: String,
    default: ''
  },
})

const latLng = ref('')

//经纬度
function clearLatLng(){
  emit('success', latLng.value)
}

const chooseSuccess = (getLatLng) =>{
  latLng.value = getLatLng
  emit('success', latLng.value)
}
//经纬度弹窗
const openChoose = ()=>{
  nextTick(()=>{
    if(latLngChoose.value){
      latLngChoose.value.show(latLng.value)
    }
  })
}

function changeLatLng(value){
  console.log(value)
  if(value){
    latLng.value = value
    nextTick(()=>{
      if(latLngArea.value){
        latLngArea.value.show(latLng.value)
      }
    })
  }else{
    latLng.value = value
    if(latLngArea.value){
      latLngArea.value.resetMapState()
    }
  }

}

watch(() => props.latLng, value => changeLatLng(value))
</script>

<style scoped>

</style>

展示经纬度位置

<template>
  <div>
    <div id="map_container" ref="map" style="height: 500px"></div>
  </div>
</template>

<script setup>
import { ref, nextTick } from 'vue'

const map = ref(null)
//地图实例
const mapInstance = ref(null)

const poiPickerInstance = ref(null)
const markerInstance = ref(null)
const infoWindowInstance = ref(null)


let initialLatLng = ''

const show = (value)=>{
  initialLatLng  = value
  resetMapState()

  makeLat()
}

// 统一清理函数
const resetMapState = () => {
  if (poiPickerInstance.value) {
    poiPickerInstance.value.destroy()
    poiPickerInstance.value = null
  }
  clearMapOverlays()
}

// 清理函数
const clearMapOverlays = () => {
  // 移除标记
  if (markerInstance.value) {
    markerInstance.value.setMap(null) // 关键:从地图移除
    markerInstance.value = null
  }

  // 关闭信息窗口
  if (infoWindowInstance.value) {
    infoWindowInstance.value.close() // 关键:关闭窗口
    infoWindowInstance.value = null
  }
}

const makeLat = () => {
  nextTick(() => {

    AMapUI.loadUI(['misc/PoiPicker'], function(PoiPicker) {
      poiPickerInstance.value = new PoiPicker({

        placeSearchOptions: {
          map: mapInstance.value,
          pageSize: 10
        },
      });
      poiPickerReady(poiPickerInstance.value);
    })

    function poiPickerReady(poiPicker) {

      window.poiPicker=null
      window.poiPicker = poiPicker;
      markerInstance.value =null
          markerInstance.value = new AMap.Marker();
      infoWindowInstance.value = null
          infoWindowInstance.value = new AMap.InfoWindow({
        offset: new AMap.Pixel(0, -20),
        isCustom: true,  // 使用自定义窗体
        autoMove: true,  // 是否自动调整窗体到视野内
      });

      if (initialLatLng) {
        const position = new AMap.LngLat(...initialLatLng.split(',').map(Number));
        mapInstance.value.setCenter(position);
        markerInstance.value.setMap(mapInstance.value);
        markerInstance.value.setPosition(position);
        // 使用地理编码服务获取地址信息
        const geocoder = new AMap.Geocoder();
        geocoder.getAddress(position, (status, result) => {
          if (status === 'complete' && result.info === 'OK') {
            const address = result.regeocode;
            infoWindowInstance.value.setContent(`
              <div style="
                padding: 12px 16px;
                width: 280px;
                background: white;
                border-radius: 8px;
                box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
                transition: all 0.3s ease;
              ">
                <h4 style="
                  margin: 0 0 6px 0;
                  padding-bottom: 4px;
                  border-bottom: 1px solid #eee;
                  color: #333;
                  font-size: 15px;
                ">📌 位置信息</h4>
                <div style="
                  font-size: 13px;
                  line-height: 2;
                  color: #666;
                ">
                  <div><b>经纬度:</b>${initialLatLng}</div>
                  <div><b>详细地址:</b>${address.formattedAddress}</div>
                </div>
              </div>
            `);
            infoWindowInstance.value.setMap(mapInstance.value);
            infoWindowInstance.value.setPosition(position);
            infoWindowInstance.value.open(mapInstance.value, position);
          }
        });

      }
    }
  });
}

onMounted(()=>{
  // 创建地图实例
  mapInstance.value= new AMap.Map(map.value, {
    zoom: 11,
    center: [120.30548699999986, 31.86],
  });
})

defineExpose({
  show,resetMapState
})
</script>





<style>
/* 移除高德地图相关元素 */
.dg, .main, .a{
  display: none;
}
.amap-logo, .amap-copyright {
  display: none !important;
}
</style>
<style scoped>
html,
body,
#container {
  width: 100%;
  height: 100%;
  margin: 0px;
  font-size: 13px;
}
.pickerBox {
  position: absolute;
  z-index: 9999;
  top: 30px; /* 调整位置避免重叠 */
  right: 20px;
  width: 280px;
}

/* 添加深度选择器处理第三方样式 */
:deep(.amap-layers) {
  z-index: 1;
}
:deep(.amap-markers) {
  z-index: 999;
}

#pickerInput {
  width: 200px;
  padding: 5px 5px;
}

.poiInfo {
  background: #fff;
}

.amap_lib_placeSearch .poibox.highlight {
  background-color: #CAE1FF;
}

.amap_lib_placeSearch .poi-more {
  display: none!important;
}
</style>

选择地点

<template>
<el-dialog
    title="选择经纬度"
    v-model="visible"
    width="900px"
    append-to-body
    destroy-on-close
    @close="cancel"
>
  <div id="pickerBox">
    <input id="pickerInput" placeholder="输入关键字选取地点" />
  </div>
  <div id="container" style="height: 400px"></div>
  <div class="input-card">
    <label style="color: grey">
      可点击地图或搜索后选择具体位置以获取准确经纬度
    </label>
  </div>
  <template #footer>
    <div class="dialog-footer">
      <el-button @click="cancel">取 消</el-button>
      <el-button type="primary" @click="submitForm">确 定</el-button>
    </div>
  </template>
</el-dialog>
</template>

<script setup>
import { ref, nextTick } from 'vue'

const visible   = ref(false)
const getLatLng = ref('')          // 最终经纬度字符串
let initialLatLng = ''             // 父组件传入的初始值
let mapObj   = null
let currentMarker = null
let infoWindow    = null

/* 用于回显/保存的完整信息 */
let currentName    = ''   // 地点名称
let currentAddress = ''   // 详细地址

const emits = defineEmits(['chooseSuccess'])

const show = (latLng) => {
initialLatLng = latLng || ''
visible.value = true
nextTick(() => makeLat())
}
const cancel = () => { visible.value = false }

const submitForm = () => {
if (getLatLng.value) {
  /* 把完整信息一起回传,父组件可按需取用 */
  emits('chooseSuccess', getLatLng.value)
}
cancel()
}

/* 统一清除旧标记 & 窗体 */
const clearOverlay = () => {
if (currentMarker) { currentMarker.setMap(null); currentMarker = null }
if (infoWindow)    { infoWindow.close(); infoWindow = null }
}

/* 创建单个标记 + 信息窗体(点击地图 & 搜索 POI 共用) */
const drawPoint = (lng, lat, name = '', address = '') => {
clearOverlay()

const position = new AMap.LngLat(lng, lat)

currentMarker = new AMap.Marker({ position, map: mapObj })

const titleStr = name || '地图选点'
const addrStr  = address || `${lng},${lat}`

infoWindow = new AMap.InfoWindow({
  offset: new AMap.Pixel(0, -20),
  isCustom: true,
  autoMove: true,
  content: `
    <div style="padding:12px 16px;width:280px;background:#fff;border-radius:8px;box-shadow:0 2px 12px rgba(0,0,0,.1)">
      <h4 style="margin:0 0 6px;padding-bottom:4px;border-bottom:1px solid #eee;color:#333;font-size:15px">📌 ${titleStr}</h4>
      <div style="font-size:13px;line-height:2;color:#666">
        <div><b>经纬度:</b>${lng},${lat}</div>
        <div><b>详细地址:</b>${addrStr}</div>
      </div>
    </div>`
})
infoWindow.open(mapObj, position)

/* 保存到变量,供确定/回显使用 */
getLatLng.value  = `${lng},${lat}`
currentName      = name
currentAddress   = address
}

/* 初始化地图 & 事件 */
const makeLat = () => {
nextTick(() => {
  mapObj = new AMap.Map('container', {
    zoom: 11,
    center: initialLatLng ? initialLatLng.split(',') : [120.30548699999986, 31.86]
  })

  /* 点击地图选点 */
  mapObj.on('click', (e) => {
    const lng = e.lnglat.getLng()
    const lat = e.lnglat.getLat()
    /* 逆地理编码 -> 获取地址描述 */
    const geocoder = new AMap.Geocoder({ radius: 1000, extensions: 'base' })
    geocoder.getAddress([lng, lat], (status, result) => {
      let addr = ''
      if (status === 'complete' && result.info === 'OK') {
        addr = result.regeocode.formattedAddress
      }
      drawPoint(lng, lat, '', addr)
    })
  })

  /* 搜索面板 */
  AMapUI.loadUI(['misc/PoiPicker'], (PoiPicker) => {
    const poiPicker = new PoiPicker({
      input: 'pickerInput',
      placeSearchOptions: { map: mapObj, pageSize: 10 }
    })

    poiPicker.on('poiPicked', (poiResult) => {
      const { name, location, address } = poiResult.item
      drawPoint(location.lng, location.lat, name, address)
      mapObj.setCenter(location)
    })
  })

  /* 初始值回显 */
  if (initialLatLng) {
    const [lng, lat] = initialLatLng.split(',').map(Number)
    /* 同样逆地理编码,拿到地址,保证回显时三项完整 */
    const geocoder = new AMap.Geocoder({ radius: 1000, extensions: 'base' })
    geocoder.getAddress([lng, lat], (status, result) => {
      let addr = ''
      if (status === 'complete' && result.info === 'OK') addr = result.regeocode.formattedAddress
      drawPoint(lng, lat, '', addr)
    })
  }
})
}

defineExpose({ show })
</script>

<style>
.amap-logo,
.amap-copyright {
display: none !important;
}
</style>