vue使用高德地图


摘要:

需求是填写数据表单时会去选择省市区+具体地址+经纬度,数据的填写与数据的回显!


vue3:

bash 复制代码
安装amap/amap-jsapi-loader
npm install amap/amap-jsapi-loader
bash 复制代码
<ChooseMapPoiDialog ref="chooseMapPoiRef" @confirm="selectPoi" />
bash 复制代码
// 选择坐标
const chooseMapPoiRef = ref()
const openChooseMap = async () => {
  chooseMapPoiRef.value.open({
    title: '选择坐标',
    areaId: formData.value.areaId,
    address: formData.value.detailAddress,
    latitude: formData.value.splat,
    longitude: formData.value.splon
  })
}

const selectPoi = async (addInfo) => {
  if (addInfo) {
    formData.value.areaId = addInfo.areaId
    formData.value.splon = addInfo.lng
    formData.value.splat = addInfo.lat
    formData.value.detailAddress = addInfo.address
  }
}

ChooseMapPoiDialog.vue:

bash 复制代码
<template>
  <Dialog v-model="dialogVisible" title="选择坐标" width="65%" :close-on-click-modal="false">
    <div class="dialogModel">
      <div class="flex" style="margin-top: -10px;margin-bottom: 10px;">
        <el-input v-model="mapAddress" placeholder="请输入内容" class="!w-240px !mr-10px" />
        <el-button type="primary" @click="searchInputKeyword">
          <Icon class="mr-5px" icon="ep:search" />查询
        </el-button>
      </div>
      <div class="flex !mb-10px">
        <div class="!w-240px">
          <div class="searchResModel">
            <div class="searchResItem" @click="mapValueChangeNew(item.id)" v-for="(item, index) in poiList" :key="index">
              <p class="poiName">{{ item.name }}</p>
              <p class="poiAddress">{{ item.address }}</p>
            </div>
          </div>
        </div>
        <div style="flex:1;padding-left: 10px;box-sizing:border-box; width: calc(100% - 250px);">
          <div class="mapModel">
            <div id="container" class="map" style="height:500px"></div>
          </div>
        </div>
      </div>
    </div>
    <!-- 底部对话框操作按钮 -->
    <template #footer>
      <el-button type="primary" @click="onSelectAddress">确 定</el-button>
      <el-button @click="dialogVisible = false">取 消</el-button>
    </template>
  </Dialog>
</template>
<script lang="ts" setup>
import AMapLoader from "@amap/amap-jsapi-loader";

defineOptions({ name: 'ChooseMapPoiDialog' })

const message = useMessage() // 消息弹窗
const loading = ref(false)
const title = ref()
const showsearchResult = ref(false)
const thisPosition = ref({
  areaId: "",
  address: "",
  lat: "",
  lng: ""
})
const poiList = ref<[]>([])
const mapAddress = ref()
const visible = ref()
const latitude = ref()
const longitude = ref()
const mapValue = ref()
const autoCompleteComponent = ref()

const map = ref()
const address = ref()
const placeSearchComponent = ref()
const geocoder = ref()

/** 打开弹窗 */
const dialogVisible = ref(false)
const open = (data: any) => {
  dialogVisible.value = true
  window._AMapSecurityConfig = {
    securityJsCode: 'keykeykeykeykeykeykey',
  }

  // const { title, address, name, latitude, longitude } = data
  // if (title && title.trim().length) title.value = title
  if (data.address && data.address.trim().length) {
    //searchKeyWord(address);
    mapAddress.value = data.address
    thisPosition.value = {
      areaId: data.areaId,
      address: data.address,
      lng: data.longitude,
      lat: data.latitude
    }
  }
  if (data.latitude && data.longitude) {
    latitude.value = data.latitude
    longitude.value = data.longitude
  }
  visible.value = true;
  nextTick(() => initMap())

  // activeAppLink.value.path = link

}
defineExpose({ open })


const close = async () => {
  visible.value = false;
}
// 初始化搜索
const mapSearchInit = async () => {
  let autoOptions = {
    input: "tipInput",
  }
  let autoCompleteComponent = new AMap.Autocomplete(autoOptions);
  autoCompleteComponent.value = autoCompleteComponent;
  // 注册placeSearch组件
  placeSearchComponent.value = new AMap.PlaceSearch();
  if (mapAddress.value) {
    searchKeyWord(mapAddress.value)
  }
}

const searchInputKeyword = async () => {
  searchKeyWord(mapAddress.value);
}

// 根据输入内容查询
const searchKeyWord = async (query) => {
  placeSearchComponent.value.search(query, function (status, result) {
    if (status === 'complete' && result.info === "OK") {
      showsearchResult.value = true
      poiList.value = result.poiList.pois;
    } else {
      showsearchResult.value = false
      poiList.value = []
      message.error("没有查到结果")
    }
  })

}

const mapValueChange = async () => {
  let items = poiList.value.find((item) => {
    return item["id"] == mapValue.value
  })
  if (items) { markerResult(items) }
}

const mapValueChangeNew = async (id) => {
  let items = poiList.value.find((item) => {
    return item["id"] == id
  });
  if (items)
    markerResult(items)
}

//选择搜索的内容
const markerResult = async (data) => {
  console.log("data", data)
  showsearchResult.value = false;
  address.value = data.name;
  var marker = new AMap.Marker({
    position: [Number(data.location.lng), Number(data.location.lat)],
  })
  map.value.clearMap() // 清除所有覆盖物(点标志)
  console.log("marker", marker)
  map.value.add(marker) // 添加点标志
  showInfoWindow(marker);
  setTimeout(() => {
    map.value.setCenter(data.location);
    map.value.setZoom(18);
  }, 50)
  let lnglat = [data.location.lng, data.location.lat]
  geocoder.value.getAddress(lnglat, function (status, result) {
    if (status === 'complete' && result.regeocode) {
      address.value = result.regeocode.formattedAddress;

      // adcode.value = result.regeocode.addressComponent.adcode; // 区域ID

      showInfoWindow(marker); //自定义信息窗体
      thisPosition.value = {
        areaId: parseInt(result.regeocode.addressComponent.adcode),
        address: data.address + data.name,
        lng: data.location.lng,
        lat: data.location.lat
      };
      //that.$emit("select", that.thisPosition) //返回给父组件
    } else {
      message.error('根据经纬度查询地址失败')
    }
  })
}

const initMap = () => {
  AMapLoader.load({
    // key: "keykeykeykeykeykeykey", // 申请好的Web端开发者Key,首次调用 load 时必填
    key: "keykeykeykeykeykeykey",
    version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
    plugins: [
      'AMap.ToolBar',
      'AMap.Scale',
      'AMap.Geolocation',
      'AMap.PlaceSearch',
      'AMap.AutoComplete',
      'AMap.Geocoder',
      'AMap.CitySearch',
      'AMap.DistrictSearch'
    ],
    resizeEnable: true,
  }).then((AMap) => {
    const initParams = {
      viewMode: "3D", //是否为3D地图模式
      zoom: 18, //初始化地图级别
    };
    if (longitude.value && latitude.value) {
      initParams.center = [longitude.value, latitude.value]
    }
    console.log("initParams", initParams)
    map.value = new AMap.Map("container", initParams);
    geocoder.value = new AMap.Geocoder()
    map.value.addControl(new AMap.Scale()) // 在图面添加比例尺控件,展示地图在当前层级和纬度下的比例尺
    map.value.addControl(new AMap.ToolBar()) //在图面添加鹰眼控件,在地图右下角显示地图的缩略图

    handleClick(AMap) //地图选点
    mapSearchInit();
    if (longitude.value && latitude.value) {
      let marker = new AMap.Marker({
        position: new AMap.LngLat(longitude.value, latitude.value)
      })
      map.value.clearMap() // 清除所有覆盖物(点标志)
      map.value.add(marker) // 添加点标志
    }

  }).catch(e => {
    console.log(e);
  })
}

//点击地图获取地理位置
const handleClick = async () => {
  map.value.on('click', (e) => {
    console.log("e", e)

    let lng = e.lnglat.lng
    let lat = e.lnglat.lat
    let marker = new AMap.Marker({
      position: new AMap.LngLat(lng, lat)
    })
    map.value.clearMap() // 清除所有覆盖物(点标志)
    map.value.add(marker) // 添加点标志
    let lnglat = [lng, lat]
    geocoder.value.getAddress(lnglat, function (status, result) {
      if (status === 'complete' && result.regeocode) {
        address.value = result.regeocode.formattedAddress;

        // adcode.value = result.regeocode.addressComponent.adcode; // 区域ID

        showInfoWindow(marker); //自定义信息窗体
        thisPosition.value = {
          areaId: parseInt(result.regeocode.addressComponent.adcode),
          address: address.value,
          lng: lng,
          lat: lat
        };
        //that.$emit("select", that.thisPosition) //返回给父组件
      } else {
        message.error('根据经纬度查询地址失败')
      }
    })

    // 获取点击位置的区域ID
    // const districtSearch = new AMap.DistrictSearch({
    //   level: "district",
    // });

    // districtSearch.search("中国", (status, result) => {
    //   if (status === "complete") {
    //     for (let i = 0; i < result.districtList[0].districtList.length; i++) {
    //       const polygon = new AMap.Polygon({
    //         path: result.districtList[0].districtList[i].boundaries,
    //         strokeWeight: 2,
    //         strokeColor: "#ff33ff",
    //         fillColor: "#f5deb3",
    //         fillOpacity: 0.35,
    //       });

    //       polygon.setMap(map);

    //       // if (polygon.contains(e.lnglat)) {
    //         console.log("点击的区域ID:", result.districtList[0].districtList[i].adcode);
    //       //   break;
    //       // }
    //     }
    //   } else {
    //     console.error("获取区域信息失败");
    //   }
    // });
  })
}
//新增标记
const showLocation = async (data) => {
  let marker = new AMap.Marker({
    position: new AMap.LngLat(data[0], data[1]) //参数为经纬度
  })
  map.value.clearMap() // 清除所有覆盖物(点标志)
  map.value.add(marker) // 添加点标志
  showInfoWindow(marker); //自定义信息窗体
}

//自定义信息窗体
const showInfoWindow = async (marker) => {
  let infoWindow = new AMap.InfoWindow({
    isCustom: true, //是否自定义信息窗体
    content: `<div style="background-color: white;padding: 0 5px; border-radius: 5px;border: 1px solid #cccccc;"> 地址:${address.value}</div>`,
    closeWhenClickMap: true,
    zIndex: 999,
    offset: new AMap.Pixel(16, -35)
  });
  infoWindow.open(map.value, marker.getPosition())
}

const emit = defineEmits<{
  (e: 'confirm')
}>()
const onSelectAddress = () => {
  if (!thisPosition.value.address) {
    message.error('先选择经纬度')
    return;
  }

  emit('confirm', thisPosition.value)
  dialogVisible.value = false
}


/** 初始化 **/
onMounted(() => {

})
</script>

<style lang="scss" scoped>
.searchResModel {
  height: 500px;
  overflow-y: scroll;
}

.searchResItem {
  padding: 10px 0;
  border-bottom: 1px solid #ccc;
  cursor: pointer;
}

.searchResItem .poiName {
  font-size: 14px;
  font-weight: bolder;
  padding: 0;
  margin: 0;
  width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  word-break: break-all;
}

.searchResItem .poiAddress {
  font-size: 12px;
  color: #8c8c8c;
  padding: 0;
  margin-top: 4px;
  margin-bottom: 0;
  width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  word-break: break-all;
}

.mapModel {
  height: 500px;
}

.map_search_result {
  position: absolute;
  top: 80px;
  left: 40px;
  background-color: rgba(255, 255, 255, .9);
  z-index: 1000;
}

.map_search_result ul li {}

.input-card {
  bottom: 40px;
}

.input-item {
  height: auto;
}

.map {
  height: 100%;
}
</style>

vue2:

bash 复制代码
<template>
<el-dialog v-model="visible" align-center :append-to-body="false" :modal-append-to-body="false" draggable :title="title" width="1200px" @close="close">
    <div class="dialogModel">
        <div class="flex-x-left" style="margin-top: -10px;margin-bottom: 10px;">
            <div style="width: 240px;">
                <el-input v-model="mapAddress" placeholder="请输入内容"></el-input>
            </div>
            <div style="flex:1;margin-left: 10px;">
                <com-button icon="Search" type="primary"  @click="searchInputKeyword">查询</com-button>
            </div>
        </div>
        <div class="flex-x-left" style="margin-bottom: 10px;">
            <div style="width: 240px;">
                <div class="searchResModel">
                    <div class="searchResItem" @click="mapValueChangeNew(item.id)" v-for="(item, index) in poiList">
                        <p class="poiName">{{item.name}}</p>
                        <p class="poiAddress">{{item.address}}</p>
                    </div>
                </div>
            </div>
            <div style="flex:1;padding-left: 10px;box-sizing:border-box; width: calc(100% - 250px);">
                <div class="mapModel">
                    <div id="container" class="map" style="height:500px"></div>
                </div>
            </div>
        </div>
    </div>

    <template #footer>
        <el-button type="default" @click="close">关闭</el-button>
        <el-button type="primary" @click="onSelectAddress">确定</el-button>
    </template>
</el-dialog>
</template>

<script>
import AMapLoader from "@amap/amap-jsapi-loader";
window._AMapSecurityConfig = {
  securityJsCode: 'keykeykeykeykeykeykeykeykey',
}
export default {
    name: "ChooseMapPoi",
    data: function () {
        return {
            loading: false,
            showsearchResult: false,
            thisPosition: {
                address: "",
                lat: "",
                lng: ""
            },
            poiList: [],
            mapAddress: "",
            mapValue: "",
            visible: false,
            title: '选择坐标',
            latitude: 0,
            longitude: 0
        };
    },
    computed: {
        saveText: function () {
            return this.saveLoading ? "保存中..." : "保存";
        },
    },
    methods: {
        // options: {address}
        show(options) {
            const {
                title,
                address,
                name,
                latitude,
                longitude
            } = options;

            if (title && title.trim().length) this.title = title;
            if (address && address.trim().length) {
                //this.searchKeyWord(address);
                this.mapAddress = address;
                this.thisPosition = {
                    address: address,
                    lng: longitude,
                    lat: latitude
                };
            }
            if(latitude && longitude){
                this.latitude = latitude;
                this.longitude = longitude;
            }
            this.visible = true;
            this.$nextTick(() => {
                this.initMap();
            })

        },
        close() {
            this.visible = false;
        },

        /** 初始化搜索 */
        mapSearchInit() {
            let autoOptions = {
                input: "tipInput",
            }
            let autoCompleteComponent = new AMap.Autocomplete(autoOptions);
            this.autoCompleteComponent = autoCompleteComponent;
            // 注册placeSearch组件
            this.placeSearchComponent = new AMap.PlaceSearch();
            // console.log('this.mapAddress2', this.mapAddress);
            if (this.mapAddress != '') {
                this.searchKeyWord(this.mapAddress);
            }
        },
        searchInputKeyword() {
            this.searchKeyWord(this.mapAddress);
        },
        //根据输入内容查询
        searchKeyWord(query) {
            let that = this
            that.placeSearchComponent.search(query, function (status, result) {
                // console.log('result:',result);
                if (status === 'complete' && result.info === "OK") {
                    that.showsearchResult = true
                    that.poiList = result.poiList.pois;
                } else {
                    that.showsearchResult = false
                    that.poiList = []
                    that.$message({
                        message: "没有查到结果",
                        type: "warning",
                    });
                }
            })
        },
        mapValueChange() {
            let that = this;
            let items = this.poiList.find((item) => {
                return item["id"] == that.mapValue
            });
            if (items)
                this.markerResult(items);
        },
        mapValueChangeNew(id) {
            let that = this;
            let items = this.poiList.find((item) => {
                return item["id"] == id
            });
            if (items)
                this.markerResult(items);
        },
        //选择搜索的内容
        markerResult(data) {
            this.showsearchResult = false;
            this.address = data.name;
            var marker = new AMap.Marker({
                position: [Number(data.location.lng), Number(data.location.lat)],
            });
            this.map.clearMap() // 清除所有覆盖物(点标志)
            this.map.add(marker) // 添加点标志
            this.showInfoWindow(marker);
            setTimeout(() => {
                this.map.setCenter(data.location);
                this.map.setZoom(18);
            }, 50)
            this.thisPosition = {
                address: data.address + data.name,
                lng: data.location.lng,
                lat: data.location.lat
            };
        },
        onSelectAddress() {
            if (!this.thisPosition.address) {
                this.$message.error('先选择经纬度');
                return;
            }
            this.$emit("selectPoi",this.thisPosition)
            this.close();
        },
        initMap() {
            AMapLoader.load({
                // key: "keykeykeykeykeykeykey", // 申请好的Web端开发者Key,首次调用 load 时必填
                key: "keykeykeykeykeykeykey",
                version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
                plugins: [
                    'AMap.ToolBar',
                    'AMap.Scale',
                    'AMap.Geolocation',
                    'AMap.PlaceSearch',
                    'AMap.AutoComplete',
                    'AMap.Geocoder',
                    'AMap.CitySearch'
                ],
                resizeEnable: true,
            }).then((AMap) => {
                const that = this;
                const initParams = {
                    viewMode: "3D", //是否为3D地图模式
                    zoom: 18, //初始化地图级别
                };
                if(this.longitude && this.latitude){
                    initParams.center = [this.longitude,this.latitude]
                }
                that.map = new AMap.Map("container", initParams);
                that.geocoder = new AMap.Geocoder()
                that.map.addControl(new AMap.Scale()) // 在图面添加比例尺控件,展示地图在当前层级和纬度下的比例尺
                that.map.addControl(new AMap.ToolBar()) //在图面添加鹰眼控件,在地图右下角显示地图的缩略图
                that.handleClick(AMap) //地图选点
                that.mapSearchInit();
                if(this.longitude && this.latitude){
                    let marker = new AMap.Marker({
                        position: new AMap.LngLat(this.longitude, this.latitude)
                    })
                    this.map.clearMap() // 清除所有覆盖物(点标志)
                    this.map.add(marker) // 添加点标志
                }

            }).catch(e => {
                console.log(e);
            })
        },
        //点击地图获取地理位置
        handleClick() {
            this.map.on('click', (e) => {
                let lng = e.lnglat.lng
                let lat = e.lnglat.lat
                let marker = new AMap.Marker({
                    position: new AMap.LngLat(lng, lat)
                })
                this.map.clearMap() // 清除所有覆盖物(点标志)
                this.map.add(marker) // 添加点标志
                let lnglat = [lng, lat]
                let that = this
                that.geocoder.getAddress(lnglat, function (status, result) {
                    if (status === 'complete' && result.regeocode) {
                        that.address = result.regeocode.formattedAddress;
                        that.showInfoWindow(marker); //自定义信息窗体
                        that.thisPosition = {
                            address: that.address,
                            lng: lng,
                            lat: lat
                        };
                        //that.$emit("select", that.thisPosition) //返回给父组件
                    } else {
                        that.$message.error('根据经纬度查询地址失败')
                    }
                })
            })
        },
        //新增标记
        showLocation(data) {
            let marker = new AMap.Marker({
                position: new AMap.LngLat(data[0], data[1]) //参数为经纬度
            })
            this.map.clearMap() // 清除所有覆盖物(点标志)
            this.map.add(marker) // 添加点标志
            this.showInfoWindow(marker); //自定义信息窗体
        },
        //自定义信息窗体
        showInfoWindow(marker) {
            let infoWindow = new AMap.InfoWindow({
                isCustom: true, //是否自定义信息窗体
                content: `<div style="background-color: white;padding: 0 5px; border-radius: 5px;border: 1px solid #cccccc;"> 地址:${this.address}</div>`,
                closeWhenClickMap: true,
                zIndex: 999,
                offset: new AMap.Pixel(16, -35)
            });
            infoWindow.open(this.map, marker.getPosition());
        },

    },

    // mounted(){
    //     const recaptchaScript = document.createElement("script");
    //     recaptchaScript.setAttribute(
    //     "src",
    //     "https://webapi.amap.com/loader.js"
    //     );
    //     document.head.appendChild(recaptchaScript);
    // }
};
</script>

<style lang="scss">


.searchResModel {
    height: 500px;
    overflow-y: scroll;
}

.searchResItem {
    padding: 10px 0;
    border-bottom: 1px solid #ccc;
    cursor: pointer;
}

.searchResItem .poiName {
    font-size: 14px;
    font-weight: bolder;
    padding: 0;
    margin: 0;
    width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 1;
    -webkit-box-orient: vertical;
    word-break: break-all;
}

.searchResItem .poiAddress {
    font-size: 12px;
    color: #8c8c8c;
    padding: 0;
    margin-top: 4px;
    margin-bottom: 0;
    width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    word-break: break-all;
}

.mapModel {
    height: 500px;
}

.map_search_result {
    position: absolute;
    top: 80px;
    left: 40px;
    background-color: rgba(255, 255, 255, .9);
    z-index: 1000;
}

.map_search_result ul li {}

.input-card {
    bottom: 40px;
}

.input-item {
    height: auto;
}

.map {
    height: 100%;
}
</style>



相关推荐
一撮不知名的呆毛23 分钟前
Ajax局部刷新,异步请求
前端·javascript·ajax
好奇的菜鸟1 小时前
Vue.js 中 v-bind 和 v-model 的用法与异同
前端·javascript·vue.js
selfsuer2 小时前
Element-plus 【el-input输入框】和【el-select下拉选择框】样式修改
前端·javascript·vue.js
龙雨LongYu124 小时前
vue3+ts 我写了一个跟swagger.yml生成请求和响应实体(接口)
前端·vue.js·typescript
_志哥_5 小时前
web开发环境下启动HTTPS服务访问
前端·javascript·https
爱健身的小刘同学5 小时前
安装 electron 依赖报错
前端·javascript·electron
耶啵奶膘5 小时前
uniapp+vue2+uview2.0导航栏组件二次封装
前端·javascript·uni-app
雨中奔跑的小孩5 小时前
electron打包部署vue项目
javascript·vue.js·electron
khatung6 小时前
React——useReducer
前端·javascript·vscode·react.js·前端框架·1024程序员节
光影少年7 小时前
react和vue图片懒加载及实现原理
前端·vue.js·react.js