Vue使用高德地图-官方API

970 阅读1分钟

前言✨

技术栈:

写法:vue2 + @vue/composition-api ≈ vue3

ui库:element 2.14

最终完成结果:

1.弹窗内嵌入高德地图,支持点击获取省市县名称、code、经纬度、具体地址,并插入标记

2.支持搜索并支持输入提示,点击直接跳转定位到选中地址,并获取以上信息。

开发前准备:

1.首先,注册开发者账号,成为高德开放平台开发者

2.登陆之后,在进入应用管理页面,创建新应用,为应用添加 Key和安全密钥lbs.amap.com/api/javascr…

高德地图开发者平台地址:

lbs.amap.com/api/jsapi-v…

安装依赖

1.安装高德地图loader

$ npm i @amap/amap-jsapi-loader

2.安装Vue3的shallowRef方法

使用 shallowRef 进行非深度监听,因为在 Vue3 所使用的 Proxy 拦截操作会改变 JSAPI 原生对象,所以此处需要区别 Vue2 使用方式对地图对象行非深度监听,否则会出现问题,建议 JS API 相关对象采用非响应式的普通对象来存储。

& npm i @vue/reactivity

vue3中在setup函数中声明map对象

setup(){
  const map = shallowRef(null);
    return{
       map,
    }
},

如果是vue2就不用安装了直接声明即可,如下

data(){
      return{
        map:null,
     } 
 },

弹窗组件代码

CommonDialog组件是基于el-dialog封装的,直接改成el-dailog即可

// MapContainer.vue
<template>
  <CommonDialog
    :visible.sync="visible"
    @confirm="confirm"
    width="900px"
    @handleClose="handleClose"
  >
    <div v-loading="loading" class="map-dialog-warp">
      <div id="container"></div>
      <div class="info-card">
        <div class="tip">左击获取位置信息:</div>
        <div v-if="lng">{{ lng }}, {{ lat }}</div>
        <div>{{ addressLabel }}</div>
      </div>
      <div class="info-card search-card">
        <div class="tip">地址搜索:</div>
        <el-select
          v-model="keyword"
          filterable
          clearable
          placeholder="请输入地址"
          size="small"
          :filter-method="searchInput"
          @clear="clearSelect"
        >
          <el-option
            v-for="(item, index) in searchList"
            :key="index"
            :label="item.name"
            :value="item.id"
            @click.native="selectClick(item)"
          >
          </el-option>
        </el-select>
      </div>
    </div>
  </CommonDialog>
</template>

<script>
import {
  defineComponent,
  onMounted,
  ref,
  reactive,
  toRefs,
} from "@vue/composition-api";
import AMapLoader from "@amap/amap-jsapi-loader";
import { shallowRef } from "@vue/reactivity";
import { Message } from "element-ui";
export default defineComponent({
  name: "MapContainer",
  props: {
    visible: {
      type: Boolean,
      default: false,
    },
  },
  setup(props, context) {
    onMounted(() => {
      initMap();
    });
    const keyword = ref(""); // 搜索关键词
    const searchList = ref([]);// 搜索提示下拉列表
    const loading = ref(false);
    const map = shallowRef(null);// map示例
    const marker = ref(null);// 标注
    // 经纬度
    const lnglat = reactive({
      lng: "",
      lat: "",
    });
    // 地址信息 省、市、区、详情
    const addressInfo = reactive({
      addressLabel: "",
      provinceCode: "",
      cityCode: "",
      districtCode: "",
    });
    // 初始化地图
    const initMap = () => {
      loading.value = true;
      AMapLoader.load({
        key: "xxxxxxx", // 开发者Key
        version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
        plugin: [
          "AMap.Scale", // 地图比例尺插件
          "AMap.AutoComplete", // 输入提示插件
          "AMap.Geolocation", // 定位插件,整合了浏览器定位、精确IP定位、sdk辅助定位多种手段
          "AMap.Geocoder", // 地址编码服务(地理编码和逆地理编码)
          "AMap.Marker", // 点标记插件
          "AMap.DistrictSearch", // 地区查询插件
        ],
      })
        .then((AMap) => {
          map.value = new AMap.Map("container", {
            //设置地图容器id
            viewMode: "3D", //是否为3D地图模式
            zoom: 10, //初始化地图级别
            // resizeEnable: true,// 默认定位当前城市
          });
          marker.value = new AMap.Marker({
            map: map.value,
            cursor: "move",
            raiseOnDrag: true,
            title: "中心",
          });
          autocomplete(AMap);
          getGeolocation();
          mapClick();
          loading.value = false;
        })
        .catch((e) => {
          loading.value = false;
        });
    };
    // 获取当前定位和地址信息(pc端会优先获取ip定位所以不准确)
    const getGeolocation = () => {
      let geolocation;
      map.value.plugin(["AMap.Geolocation"], function () {
        geolocation = new AMap.Geolocation({
          enableHighAccuracy: true, //  是否使用高精度定位,默认:true
          timeout: 10000, //  超过10秒后停止定位,默认:无穷大
          maximumAge: 0, // 定位结果缓存0毫秒,默认:0
          convert: true, // 自动偏移坐标,偏移后的坐标为高德坐标,默认:true
          showButton: true, //  显示定位按钮,默认:true
          buttonPosition: "LB", // 定位按钮停靠位置,默认:'LB',左下角
          buttonOffset: new AMap.Pixel(10, 20), //  定位按钮与设置的停靠位置的偏移量,默认:Pixel(10, 20)
          showMarker: true, //  定位成功后在定位到的位置显示点标记,默认:true
          showCircle: true, //  定位成功后用圆圈表示定位精度范围,默认:true
          panToLocation: true, //  定位成功后将定位到的位置作为地图中心点,默认:true
          zoomToAccuracy: true, //  定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false
        });
        map.value.addControl(geolocation);
        // 初始化当前信息
        geolocation.getCurrentPosition(function (status, result) {
          if (status == "complete") {
            lnglat.lng = result.position.lng;
            lnglat.lat = result.position.lat;
            getGeocoder();
          }
        });
      });
    };
    // 地图点击获取信息
    const mapClick = () => {
      map.value.on("click", function (e) {
        lnglat.lng = e.lnglat.getLng();
        lnglat.lat = e.lnglat.getLat();
        getGeocoder();
        // 添加标记
        marker.value.setPosition([lnglat.lng, lnglat.lat]);
      });
    };
    // 获取地址信息
    const getGeocoder = () => {
      map.value.plugin(["AMap.Geocoder"], function () {
        let geocoder = new AMap.Geocoder({
          city: "全国", //默认:全国
          radius: 500, //范围,默认:500
          extensions: "all",
        });
        geocoder.getAddress(lnglat, function (status, result) {
          if (status === "complete" && result.regeocode) {
            addressInfo.addressLabel = result.regeocode.formattedAddress;
            getCityCode("province", result.regeocode.addressComponent.province); // 省级

            getCityCode("district", result.regeocode.addressComponent.district); // 区级、县级
            // 判断是否是直辖市
            if (result.regeocode.addressComponent.city) {
              getCityCode("city", result.regeocode.addressComponent.city); // 市级
            } else {
              // 特殊处理直辖市
              getCityCode("only", result.regeocode.addressComponent.province); // 市级
            }
          } else {
            Message.error("地址超出范围");
          }
        });
      });
    };

    // 获取省市县编码
    const getCityCode = (level, name) => {
      map.value.plugin("AMap.DistrictSearch", function () {
        let districtSearch = new AMap.DistrictSearch({
          level: level, // 关键字对应的行政区级别
          subdistrict: 1, //  显示下级行政区级数,1表示返回下一级行政区
        });
        // 搜索所有省/直辖市信息
        districtSearch.search(name, function (status, result) {
          try {
            if (status == "complete") {
              if (level === "province") {
                addressInfo.provinceCode = result.districtList[0].adcode;
              } else if (level === "city") {
                addressInfo.cityCode = result.districtList[0].adcode;
              } else if (level === "district") {
                addressInfo.districtCode = result.districtList[0].adcode;
              } else if (level === "only") {
                addressInfo.cityCode =
                  result.districtList[0].districtList[0].adcode;
              }
            }
          } catch (error) {
            console.error(error);
          }
        });
      });
    };
    // 搜索提示
    const autoComplete = ref(null);
    const autocomplete = (AMap) => {
      map.value.plugin("AMap.AutoComplete", function () {
        const autoOptions = {
          city: "全国",
          input: "tipinput",
        };
        autoComplete.value = new AMap.AutoComplete(autoOptions);
      });
    };
    // 地址搜索提示
    const searchInput = (val) => {
      if (val) {
        keyword.value = val;
        autoComplete.value.search(keyword.value, function (status, result) {
          if (status === "complete") {
            searchList.value = result.tips;
          }
        });
      } else {
        searchList.value = [];
      }
    };
    // 选中下拉框
    const selectClick = (item) => {
      try {
        if (item.location) {
          lnglat.lng = item.location.lng;
          lnglat.lat = item.location.lat;
          getGeocoder();
          marker.value.setPosition([item.location.lng, item.location.lat]);
          map.value.setFitView(marker.value);
        } else {
          Message.error("请输入准确地址");
        }
      } catch (error) {}
    };
    // 清除输入框
    const clearSelect = () => {
      keyword.value = "";
      searchList.value = [];
    };
    // 确定,组件返回数据
    const confirm = () => {
      if (!addressInfo.addressLabel) {
        Message.error("请选择地址");
        return;
      }
      const info = {
        areaArr: [
          addressInfo.provinceCode,
          addressInfo.cityCode,
          addressInfo.districtCode,
        ],
        addressLabel: addressInfo.addressLabel, // 详细地址
        lng: lnglat.lng, // 经度
        lat: lnglat.lat, // 维度
      };
      context.emit("returnAddress", info);
      handleClose();
    };
    // 取消
    const handleClose = () => {
      context.emit("update:visible", false);
    };

    return {
      map,
      confirm,
      handleClose,
      ...toRefs(lnglat),
      loading,
      ...toRefs(addressInfo),
      keyword,
      searchList,
      searchInput,
      clearSelect,
      selectClick,
    };
  },
});
</script>

<style lang="less" scoped>
.map-dialog-warp {
  position: relative;
  #container {
    padding: 0px;
    width: 100%;
    height: 500px;
  }
  .info-card {
    position: absolute;
    top: 10px;
    left: 10px;
    background-color: #fff;
    background-clip: border-box;
    border-radius: 4px;
    width: 200px;
    border-width: 0;
    box-shadow: 0 2px 6px 0 rgb(114 124 245 / 50%);
    padding: 10px;
    opacity: 0.9;
    color: #333;
    .tip {
      font-weight: bold;
    }
    div {
      margin-bottom: 5px;
      &:last-child {
        margin-bottom: 0;
      }
    }
  }
  .search-card {
    right: 10px !important;
    left: auto !important;
    opacity: 1;
  }
}
</style>

组件使用

returnAddress是组件返回的信息

 <MapContainer
    v-if="visible"
    :visible.sync="visible"
    @returnAddress="returnAddress"
 ></MapContainer>

踩坑

1.AMap.Geolocation定位失败

由于Chrome、IOS10等已不再支持非安全域的浏览器定位请求,为保证定位成功率和精度,请尽快升级您的站点到HTTPS。

官方回答:lbs.amap.com/faq/js-api/…