【Vue】高德地图API 的组件封装

【Vue】高德地图API 的组件封装

在无人船巡航管理后台项目中,有多处需要高德地图进行定位与轨迹显示,为此对高德地图API组件进行封装。

一、首先确定template

HTML结构并不复杂,仅仅是需要一个容器MapContainer
对于本项目而言,地图最经常使用的功能是清除标记和视角重置的控件,这里定义一个插槽提供内部的方法。

<template>
    <div class="map-view">
        
        <slot name="header">slot>
        <div id="MapContainer">div>
        
        <slot name="footer" :clear="clear" :fit="fitView">slot>
    div>
template>

绑定的:clear:fit属性,提供插槽作用域让外部使用,以下是使用ZMap(组件名)的示例。

<ZMap ref="zmap" :initConfig="initConfig">
	<template #footer="{ clear, fit }">
	  <div class="text-center m-t-10">
	    <el-button @click="clear('note')" size="mini" type="primary">清空标记el-button>
	    <el-button @click="fit" size="mini" type="info">视角恢复el-button>
	  div>
	template>
ZMap>

二、确定生成地图的参数

高德地图生成所必须的两个key定义在组件内即可,不需要在外部配置定义。

window._AMapSecurityConfig = {
    securityJsCode: "****************************", //开启地图安全密钥
};
var webKey = '****************************';    // 申请好的Web端开发者Key

项目经常需要多个标记,为此需要引入不同的icon:

data() {
		var mUrl = 'assets/mapEle/';// 标记点url路径
        return {
            map: null,
            contextMenu: null,
            infoWindow: null,
            markerType: {
                pink: require(`@/${mUrl}marker-pink.svg`),
                red: require(`@/${mUrl}marker-red.svg`),
                green: require(`@/${mUrl}marker-green.svg`),
                blue: require(`@/${mUrl}marker-blue.svg`),
                I1: require(`@/${mUrl}indexMarker1.png`),
                I2: require(`@/${mUrl}indexMarker2.png`),
                I3: require(`@/${mUrl}indexMarker3.png`),
                I4: require(`@/${mUrl}indexMarker4.png`),
            }
        }
    },

使用props传入必传的外部配置参数initConfig

    props: {
        initConfig: {
            type: Object,
            required: true
        }
    }

initConfig配置与初始化

export const initConfig = {
    isAsync: true,//允许可异步加载
    initMap: {
        zoom: 18, //初始化地图级别
        center: ["110.92330940012255", "21.67770383372478"],
        showLabel: false, //不显示地图文字标记
        viewMode: '2D',
        pitch: 50,
    },
    plugins: ["AMap.Geocoder", "AMap.HeatMap", "AMap.ControlBar"],//所要用到的插件
    defaultStyle: {//所要用到的样式
        polylineStyle: {
			//直线样式...
        },
        followLineStyle: {
			//跟随曲线样式...
        },
        circleStyle: {
            //圆标记样式...
        }
    }
}

在ZMap的created钩子中注入data:

    created() {
        // 把配置的样式注入
        let { initConfig: { defaultStyle } } = this;
        for (let k in defaultStyle) {
            this[k] = defaultStyle[k];
        }
    },

在ZMap组件内定义initMap()初始化函数:

initMap() {
    let { initMap, plugins = [] } = this.initConfig;
    return new Promise((resolve, reject) => {
        AMapLoader.load({
            key: webKey, // 申请好的Web端开发者Key,首次调用 load 时必填
            version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
            plugins
        }).then((AMap) => {
            let map = new AMap.Map("MapContainer", initMap);
            // 设置地图鼠标样式
            map.setDefaultCursor("pointer");
            this.map = map;
            // 插件初始化:
            // 1.3D地图方向调节
            if (plugins?.includes('AMap.Geocoder')) {
                // 定义逆地址编码
                this.geocoder = new AMap.Geocoder({
                    // city 指定进行编码查询的城市,支持传入城市名、adcode 和 citycode
                    city: "全国",
                });
            }
            if (plugins?.includes("AMap.ControlBar")) {
                map.addControl(new AMap.ControlBar({}));
            }
            // 定义菜单实例
            this.contextMenu = new AMap.ContextMenu();
            // 定义信息窗体
            this.infoWindow = new AMap.InfoWindow({
                offset: new AMap.Pixel(0, -30),
            });
            resolve(true);
        }).catch((e) => {
            // this.$Notify('地图加载失败!', e);
            resolve(false);
        });
    })
},

如果传入了相应的插件就需要进行插件的初始化,并且用可选链判断plugins?.includes

可选择的异步加载

值得注意的是,高德地图的地图加载和请求会带有缓存,初始加载和后续加载的时间不同,所以地图的加载时间难以确定。
如果随这个ZMap组件渲染的时候同步加载地图,一旦当前业务需要先请求一些标记数据,就很容易造成,数据回来了但地图还未加载,或者一些其他错位问题。
为了更方便地使用组件,这里选择自设一个异步加载配置:isAsync: true,//手动异步加载
让外部使用组件的时候可以控制组件是同步加载,还是异步加载,如果是异步加载,可以自己在外部控制地图的加载时机,为的就是把地图加载完毕的时间点牢牢掌握在自己手上。

//ZMap组件内:
mounted() {
    let { isAsync = false } = this.initConfig;
    if (isAsync) return;//如果isAsync:true,停止自动加载。
    this.initMap();//默认自动加载
},
//使用ZMap的组件:
mounted(){
   this.opm('initMap').then(res => {
      if (!res) return
      // 加载完地图之后需要获取无线传感网络的位置
      this.toGetSensor();
      // 加载水质区域状态热力图
      this.initHeatData();
    })
}
methods:{
	//这个函数用于更便捷地操作组件内的方法
	//传入方法名和参数,就可以调用组件内部的方法
    opm(fn, ...args) {
      return this.$refs.zmap[fn](...args)
    },
}

三、定义常用的方法

以下是项目常用的方法

  • 获取所有标记点:用于过滤出想获得的标记
  • 增加折线标记
  • 增加圆形标记
  • 批量增加Marker锚点
  • 逆地理编码
  • 清空所有标记
  • 调整视角
  • 信息窗口
// 获取指定的标记
getA_Type(type) {
    return this.map.getAllOverlays(type);
},
// 增加折线
addPolyline(config, styleName) {
    const polyline = new AMap.Polyline(this[styleName]); 
    polyline.setOptions({
        ...config
    });
    this.map.add(polyline); //添加入地图
    return polyline
},
// 增加圆形
addCircle(config, styleName) {
	//......
    return circle;
},
// 往地图上批量添加点标记
addMarker(markers = []) {
    if (markers.length === 0) {
        console.warn('未传入标记变量!')
        return false;
    };
    let { markerType } = this;
    markers.forEach(item => {
        let { showInfo = false, content, icon = 'blue', options } = item;
        let marker = new AMap.Marker({
            anchor: "bottom-center",
            zIndex: 10,
            ...options,
            icon: markerType[icon]
        });
        if (showInfo) {
            //设定信息窗口
            marker.content = content || '空';
            //添加点击出现信息窗口事件
            marker.on("click", this.markerClick);
        }
        this.map.add(marker);
    })
},
// 获取信息窗体
getInfoWindow(options) {
    return new AMap.InfoWindow(options);
},
// 逆地理编码地址[longitude, latitude]
useGeocoder(lnglat) {
    if (this.geocoder) {
        return new Promise((res, rej) => {
            this.geocoder.getAddress(lnglat, function (status, result) {
                if (status === "complete" && result.regeocode) {
                    res(result.regeocode.formattedAddress)
                } else rej(false)
            });
        })
    } else {
        return false;
    }
},
// 点击锚点标记出现信息窗口
markerClick(e) {
    this.infoWindow.setContent(e.target.content);
    this.infoWindow.open(this.map, e.target.getPosition());
},
// 清空地图上所有标记
clear(able) {
    this.map.clearMap();
    if (able === 'note') {
        this.$Notify('已清空所有标记!');
    }
},
// 调整视角
fitView() {
    this.map.setFitView();
},
// 地图销毁
destroy() {
    let { map } = this;
    map && map.destroy();
}

四、使用例

<template>
  <el-card class="box-card card-header m-t-20">
    <div slot="header" class="clearfix f-medium">
      <i class="fa fa-map m-r-10 text-c-blue" aria-hidden="true">i>
      <span>电子地图span>
    div>
    <div class="map-view">
      <ZMap ref="zmap" :initConfig="initConfig">
        <template #footer="{ clear, fit }">
          <div class="text-center m-t-10">
            <el-button @click="clear('note')" size="mini" type="primary">清空标记el-button>
            <el-button @click="fit" size="mini" type="info">视角恢复el-button>
          div>
        template>
      ZMap>
    div>
  el-card>
template>

<script>
import { initConfig } from '../config/map-config'
export default {
  name: "EMap",
  data() {
    return {
      initConfig,
    };
  },
  mounted() {
    this.$bus.$on("toAddMarker", this.addMarker);
  },
  beforeDestroy() {
    this.$bus.$off("toAddMarker");
  },
  methods: {
    // 对map组件的操作:传入要调用的函数名+参数
    op_map(fn, ...query) {
      return this.$refs.zmap[fn](...query)
    },
    // 添加标记点
    addMarker(data) {
      let { op_map } = this;
      let MarkerIndex = data.index + 1;
      let lnglat = [data.pos[0], data.pos[1]];
      // 如果已经标记,则取消上一个标记
      const allMarkers = op_map('getA_Type', "marker");
      let indexItem = allMarkers.filter(
        (item) => item.getExtData().id == MarkerIndex
      );
      if (indexItem.length) indexItem[0].setMap(null);
      op_map('addMarker', [{
        options: {
          position: lnglat,
          extData: {
            id: MarkerIndex,
          },
        },
        showInfo: true,
        icon: 'blue',
        content: `${MarkerIndex}张图拍摄坐标为${lnglat}`
      }])
      op_map('fitView');
    },
  },
};
script>

你可能感兴趣的:(vue.js,前端,javascript)