封装了一个简单易懂的地图选址组件

796 阅读5分钟

封装了一个简单易懂的地图选址组件

高德地图 element-ui vue2

前言

​ 在做一些后台管理系统的时候,总会涉及到一些选择点位传递坐标之类的情况,如果每次都是重新配置高德地图的话,会显得非常麻烦,所以这次封装了一下地图选址组件,便于在后续开发的情况下直接使用,无需做重复配置。

功能

​ 先简单阐述一下这个组件的功能:

  1. 出现弹窗然后加载地图,如果频繁关闭弹窗不会重复加载,只有在组件销毁之后再打开的话,才会重新加载。
  2. 可以实现输入框输入地址进行搜索,点击结果后在地图上进行撒点出现。
  3. 点击地图可以获取所点击的位置信息,呈现在输入框中。
  4. 在初始化地图的时候可以传入城市adcode或者经纬度坐标,使地图定位到当前传入的地点。
1. 地图加载和使用初始化参数

​ 第一次使用高德地图的可以先看一下官方文档提供的加载方式,这里我使用的是旧版NPM加载方式

​ 我选择的打开地图方式不是传统的通过布尔值来控制,而是通过调用子组件的初始化函数来实现的。

​ 顺便说明一下初始化时传入的adcode或者坐标的使用。

    // 打开组件
    init(options) {
      // 重置结果参数
      this.reset();
      // 打开对话框
      this.open = true;
      // 初始化地图
      // 无需频繁初始化地图
      if (!(AMap && map)) {
        AMapLoader.load({
          "key": this.amapKey, // 申请好的Web端开发者Key,首次调用 load 时必填
          "version": "2.0",    // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
          "plugins": ['AMap.AutoComplete', 'AMap.DistrictSearch', 'AMap.Geocoder'],  // 需要使用的的插件列表,如比例尺'AMap.Scale'等
          "AMapUI": {             // 是否加载 AMapUI,缺省不加载
            "version": '1.1',   // AMapUI 缺省 1.1
            "plugins": []        // 需要加载的 AMapUI ui插件
          },
          "Loca": {               // 是否加载 Loca, 缺省不加载
            "version": '2.0'  // Loca 版本,缺省 1.3.2
          }
        }).then(AMap => {
          this.$nextTick(() => {
            this.creatAmap(AMap, options);
          })
        }).catch(e => {
          console.log(e);
        })
      } else {  // 如果已经创建了地图,则移动视角
        // 移动视角
        if (options.cityId) {
          // 如果设置了cityId就移动至cityId
          this.getLocationByCityId(options.cityId).then((res) => {
            map.setZoomAndCenter(options.zoom, res);
          });
        } else if (options.center) {
          // 如果设置了经纬度就移动至经纬度地址
          map.setZoomAndCenter(options.zoom, options.center);
        }
      }
    },
    // 加载地图
    async creatAmap(aMap, options) {
      AMap = aMap;
      let pOptions = options || {};
      // 初始化参数
      let creatOptions = {
        resizeEnable: true, //窗口大小调整
        center: this.defaultCenter,
        zoom: 15,
        ...pOptions
      };
      try {
        // 如果设置了cityId,将center改为cityId解析后的点位
        if (pOptions.cityId) {
          // console.log(pOptions.cityId);
          creatOptions.center = await this.getLocationByCityId(pOptions.cityId)
        }
      } catch (error) {
        console.log('初始化地图出错:', error);
      } finally {
        map = new AMap.Map(this.$refs.container, creatOptions);
        this.mapAddClick();
      }
    },
    // 根据cityId搜索坐标
    getLocationByCityId(cityId) {
      if (AMap && cityId) {
        return new Promise((resolve, reject) => {
          // 基础配置
          const districtSearchOptions = {
            showbiz: false,
            subdistrict: 0
          }
          const amapDistrictSearch = new AMap.DistrictSearch(districtSearchOptions);
          amapDistrictSearch.search(cityId, (status, result) => {
            if (status === 'complete' && result.info === 'OK') {
              // 取数组第一个
              if (result.districtList[0]) {
                const resCenter = result.districtList[0].center;
                resolve([resCenter.lng, resCenter.lat])
              }
            } else {
              reject(status)
            }
            // console.log('s:', status);
            // console.log('r:', result);
          })
        })
      } else {
        return Promise.reject('no AMap or no Id')
      }
    }
  1. 加载时调用子组件init函数,此时传入一个option对象,这个对象的参数就是高德地图初始化的参数,与之不同的是加入了cityId这个字段,如果有这个字段那么默认的center或者传入的center将会==被改写==,请注意。
  2. 与高德有关的对象例如AMap和map都没有在vue的data中定义,因为这些数据不需要vue的响应式来控制。
  3. 这里只对地图初始化的时候进行了简单配置,看个人需求如果需要修改可以直接修改源码配置。
2. 地址搜索

​ 之前使用的时候是直接使用高德提供的搜索结果样式,但是因为层级问题总是容易被遮挡而且样式也与普通的后台管理项目不搭配,所以改为了使用element uiautocomplete组件,比较契合当前的需求。

<template>
  <el-dialog
    title="选择位置"
    :visible.sync="open"
    :close-on-click-modal="false"
    width="700px"
    append-to-body
    @close="cancel"
  >
    <!-- 输入框 -->
    <el-autocomplete
      class="mb10"
      style="width: 100%"
      v-model="address"
      value-key="name"
      popper-class="my-autocomplete"
      placeholder="请输入内容"
      :fetch-suggestions="querySearchAsync"
      clearable
      @select="handleSelect"
    >
      <i class="el-icon-search el-input__icon" slot="suffix"></i>
      <template slot-scope="{ item }">
        <div class="name">{{ item.name }}</div>
        <span class="addr">{{ item.district }}</span>
      </template>
    </el-autocomplete>
	<!-- 地图容器 -->
    <div ref="container" class="container"></div>
    <div class="footer" slot="footer">
      <el-button type="primary" @click="submitForm">确 定</el-button>
    </div>
  </el-dialog>
</template>

<script>
import AMapLoader from "@amap/amap-jsapi-loader";

// 有关高德的对象定义在这里
let AMap = null,
  map = null,
  marker = null;

export default {
  name: "BaseAmapSelect",
  props: {
    amapKey: {
      type: String,
      default: "你的key",
    },
    amapCode: {
      type: String,
      default: "你的code",
    },
  },
  data() {
    return {
      open: false,
      latitude: null,
      longitude: null,
      address: null,
      // 可以在这里更改默认位置,在不传center或者不传cityId使用这个位置
      defaultCenter: [116.41, 39.9],
    };
  },
  mounted() {
    // 加载key
    if (!window._AMapSecurityConfig) {
      window._AMapSecurityConfig = {
        securityJsCode: this.amapCode,
      };
    }
  },
  destroyed() {
    // 销毁地图
    console.log("销毁");
    map && (map = undefined);
    AMap && (AMap = undefined);
  },
  methods: {
    // 搜索输入
    querySearchAsync(queryString, cb) {
      // 插件配置
      const autoOptions = {};
      let aMapAutoComplete = null;
      if (AMap && queryString) {
        aMapAutoComplete = new AMap.AutoComplete(autoOptions);
        // 搜索
        aMapAutoComplete.search(queryString, (status, result) => {
          if (status === "complete" && result.info === "OK") {
            cb(result.tips);
          }
          // console.log('s:', status);
          // console.log('r:', result);
        });
      }
    },
    // 选择搜索项
    handleSelect(item) {
      // console.log(item);
      if (map) {
        map.setCenter([item.location.lng, item.location.lat]);
        if (marker) {
          map.remove(marker);
        }
        // 添加标记点
        marker = new AMap.Marker({
          position: [item.location.lng, item.location.lat],
          map: map,
        });
        // 给表单数据
        this.address = item.district + item.address;
        this.latitude = item.location.lat;
        this.longitude = item.location.lng;
      }
    },
    // 关闭弹窗时
    cancel() {
      // 清空marker点标记
      if (!!(map && marker)) {
        map.remove(marker);
      }
    },
  },
};
</script>

<style lang='scss' scoped>
.container {
  width: 100%;
  height: 600px;
}

.footer {
  padding: 0 20px 10px;
}

// 自定义样式
.my-autocomplete {
  li {
    line-height: normal;
    padding: 7px;

    .name {
      text-overflow: ellipsis;
      overflow: hidden;
    }

    .addr {
      font-size: 12px;
      color: #b4b4b4;
    }

    .highlighted .addr {
      color: #ddd;
    }
  }
}
</style>
  1. 说明==el-autocomplete==组件的fetch-suggestions功能,只有这个不太好理解,这里给出官方文档指路,首先这个接受一个函数,这个函数有两个参数,第一个参数就是搜索值很好理解,第二个官网说接受一个回调函数,这个回调函数就是用来呈现搜索值的,这个回调函数只接收一个数组形式的参数,数组的每一项是对象形式,如果你没有配置value-key或者没有使用插槽的话,他会默认呈现对象的value属性。
  2. select事件绑定的函数参数为对应项的数据,拿到这个数据后先执行移动地图中心点并进行撒点的操作,然后再将对应的结果赋值给最后需要的经纬度坐标和地址,==我这里的地址是拼接的省市区名和地址名,使用的话看自己的需求进行更改==。
3. 点击地图

​ 这个实现起来及比较简单了

​ 给地图添加一个点击事件,点击之后获取结果进行解析

    // 给地图添加点击事件
    mapAddClick() {
      if (AMap && map) {
        let geoCoder = new AMap.Geocoder();
        map.on("click", (e) => {
          if (marker) {
            map.remove(marker);
          }
          // 添加标记点
          marker = new AMap.Marker({
            position: e.lnglat,
            map: map,
          });

          geoCoder.getAddress(e.lnglat, (status, res) => {
            if (status === "complete" && res.info === "OK") {
              this.address = res.regeocode.formattedAddress;
              this.latitude = e.lnglat.lat;
              this.longitude = e.lnglat.lng;
            } else {
              this.$message({
                message: "获取位置信息失败!",
                type: "warning",
              });
            }
          });
        });
      }
    },
总结

​ 为什么这个组件不使用像是提供props来进行参数配置,是因为最主要想让大家明白之后再改装成自己的代码,这样使用的时候就不需要配置多余的东西,可以直接简单的使用并得到效果。

<template>
  <div class="amap-select">
    <el-button type="primary" @click="openSelectAMap">打开选址</el-button>
    <!-- 组件 -->
    <AMapSelect ref="aMapSelect" @get-address="getAddress" />
  </div>
</template>

<script>
import AMapSelect from "@/components/BaseAmapSelect"

export default {
  name: "aMapSelect",
  components: {
    AMapSelect
  },
  methods: {
    openSelectAMap() {
      this.$refs.aMapSelect.init();
    },
    getAddress(data) {
      console.log(data);
    }
  }
};
</script>
说明文档及源码

说明文档

Attributes
参数说明类型可选值默认值
amapKey高德地图keyString--
amapCode高德地图codeString--
Vue Data
参数说明类型默认值
defaultCenter默认初始位置,如果初始化option中不传入center或者cityId的话,第一次地图打开位置默认为这个Array[116.41, 39.9]
Methods
方法名说明参数
init打开弹窗并初始化地图,参数说明见下方(option: object)
Init Option
参数说明
center打开地图时的中心点位,格式见高德地图Map对象文档
cityId城市adcode码,如果传入这个参数则会在解析后替换掉center的值,==请注意==
initPoint生成回显点位,格式参照高德地图Marker position属性,一般为同center的坐标数组。
initAddress生成回显地址
其他参数同高德地图Map对象参数
Events
事件名说明回调参数
get-address点击确定后触发,本次选址的结果,回调参数有三个字段,分别为latitude(经纬度),longitude(经纬度),address(地址)(data: object)

源码地址:

gitee源码示例

相关链接:

JSAPI 的加载-入门-教程-地图 JS API v2.0|高德地图API (amap.com)

参考手册-地图 JS API v2.0|高德地图API (amap.com)

组件 | Element

效果示例

示例图片.png

2023/05/18更新
  • 二次打开时可以设置zoom进行缩放更改。
            // 移动视角
        if (options.cityId) {
          // 如果设置了cityId就移动至cityId
          this.getLocationByCityId(options.cityId).then((res) => {
            map.setZoomAndCenter(options.zoom, res);
          });
        } else if (options.center) {
          // 如果设置了经纬度就移动至经纬度地址
          map.setZoomAndCenter(options.zoom, options.center);
        }
    
2023/06/08更新
  • 新增数据回显操作,在初始化init函数中加了两个参数。