Skip to content

高德地图可视化

amap-jsapi-loader

基本使用

ts
npm install @amap/amap-jsapi-loader --save

准备容器

ts
准备一个容器 必须要有id
<div id="map"></div>

引入高德地图

ts
import AMapLoader from "@amap/amap-jsapi-loader";

加载地图

ts
const initMap = () => {
  AMapLoader.load({
    key: "申请到的key", // 申请好的Web端开发者Key,首次调用 load 时必填
    version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
    // 需要使用的的插件列表
    plugins: [
      "AMap.Geocoder", // 逆向地理解码插件
      "AMap.Marker", // 点标记插件
      "AMap.MapType" // 地图类型
    ],
    AMapUI: {
      version: "1.1",
      plugins: ["overlay/SimpleMarker"]
    }
  }).then(AMap => {
    const map = new AMap.Map("map", {
      resizeEnable: true,
      zoom: 10
    });
	
	// 地图类型
    map.addControl(
     new AMap.MapType({
       defaultType: 0 //0代表默认,1代表卫星
     })
    );
    // 地图样式
    map.setMapStyle("amap://styles/darkblue");
    
    // 绘制标记点点
    var markerPoint = new AMap.Marker({
      position: [] // 经纬度 自行添加
    });
	
	// 点位标注
    markerPoint.setLabel({
      direction: "bottom",
      offset: new AMap.Pixel(0, 5), //设置文本标注偏移量
      content: `<div class='info'>${position.name}</div>` //设置文本标注内容
    });
    
    map.add(markerPoint);
    // 缩放地图到合适的视野级别
    map.setFitView();
  });
};

初始化

ts
onMounted(async () => {
  // DOM初始化完成进行地图初始化
  initMap()
});

地图容器样式(增加定位 设置z-index 将地图放在最底部)

ts
#map {
  height: 100vh;
  width: 100%;
  z-index: -99;
  position: fixed;
  bottom: 0;
}

vue-amap插件

image-20241213162100005

高德官网

ts
https://lbs.amap.com/api/javascript-api/summary/

vue-amap

ts
https://elemefe.github.io/vue-amap/#/zh-cn/introduction/install

基本使用

安装

ts
npm install vue-amap --save

项目导入

ts
需要提前申请高德key,步骤附在最后了

// 高德地图配置
import VueAMap from 'vue-amap' // 引入插件
Vue.use(VueAMap) // 使用插件
VueAMap.initAMapApiLoader({ // 初始化插件
  key: '高德申请的key', // 高德key
  plugin: ['AMap.Geocoder', 'AMap.PlaceSearch', 'AMap.Geolocation', 'AMap.CitySearch'], // 插件集合,根据自己的需求添加
  uiVersion: '1.0.11', // 不加会报错,加上吧
  v: '1.4.15' // 默认高德 sdk 版本为 1.4.4
})
// 申请的Web端(JS API)的需要写上下面这段话
window._AMapSecurityConfig = {
  securityJsCode: '高德申请的kedy对应的安全密钥' // 高德Web端安全密钥
}

新建一个Map.vue文件

vue
<template>
  <div class="Map">
    <!-- 地图组件 -->
    <el-amap
      vid="amapContainer"
      class="amapClass"
      :zoom="zoom"
      :plugin="plugin"
      :center="center"
      :events="events"
    >
      <!-- 这里用于点击时的标点 -->
      <el-amap-marker
        v-for="(marker, index) in markers"
        :key="index + '1'"
        :position="marker"
      />
      <!-- 这里用于特殊位置的标点 -->
      <el-amap-marker
        v-for="(marker, index) in markerList"
        :key="index + '2'"
        :position="marker.lnglats"
        :icon="getIcon(marker)"
        :events="marker.events"
        :offset="[-24, -24]"
      />
      <!-- 信息窗体 -->
      <el-amap-info-window
        v-if="currentWindow.visible"
        :position="currentWindow.position"
        :visible.sync="currentWindow.visible"
        :content="currentWindow.content"
        :close-when-click-map="false"
        :events="currentWindow.events"
        :offset="[0, -24]"
      />
    </el-amap>
  </div>
</template>
 
<script>
 
import LED0 from '@/assets/imgs/LED-0.png'
import LED1 from '@/assets/imgs/LED-1.png'
import lamppost0 from '@/assets/imgs/lamppost-0.png'
import lamppost1 from '@/assets/imgs/lamppost-1.png'
import tourGuide0 from '@/assets/imgs/tourGuide-0.png'
import tourGuide1 from '@/assets/imgs/tourGuide-1.png'
export default {
  name: 'Map',
  props: {
    zoom: { // 地图缩放比例。zoom值越小,越宏观、越大,越微观。
      type: Number,
      default: 14
    },
    center: {
      type: Array,
      default: () => [120.147076, 30.245426] // 西湖风景区的经纬度
    },
    markers: {
      type: Array,
      default: () => []
    },
    markerList: {
      type: Array,
      default: () => []
    },
    currentWindow: {
      type: Object,
      default: () => ({
        position: [],
        visible: false,
        content: '测试'
      })
    }
  },
  data() {
    const self = this
    return {
      LED0,
      LED1,
      lamppost0,
      lamppost1,
      tourGuide0,
      tourGuide1,
      address: null,
      loaded: false,
      events: {
        init(o) {
          console.log('地图初始化', o)
          // ... 初始地图的一些操作
        },
        complete(){
          // ... 地图渲染完成后的一些操作
        }
        // 点击地图
        click: self.click
      },
      // 一些工具插件
      plugin: [
        {
          pName: 'Geocoder',
          events: {
            init(o) {
              console.log('Geocoder', o.getAddress())
            }
          }
        },
        {
          // 定位
          pName: 'Geolocation',
          events: {
            init(o) {
              // o是高德地图定位插件实例
              o.getCurrentPosition((status, result) => {
                // 会存在失败的时候,官网回答:https://lbs.amap.com/faq/js-api/map-js-api/position-related/43361/
                console.log('查询成功定位信息', status, result)
                if (result && result.position) {
                  // 根据经纬度设置坐标 --不想替换掉默认的center的可以先注释
                  self.$emit('update:center', [result.position.lng, result.position.lat])
                  // self.$emit('update:markers', [[result.position.lng, result.position.lat]])
                  // load
                  self.loaded = true
                  // 页面渲染好后
                  self.$nextTick()
                }
              })
            }
          }
        },
        {
          // 搜索 --我这里没用到
          pName: 'PlaceSearch',
          events: {
            init(instance) {
              console.log('搜索', instance)
            }
          }
        },
        {
          // 定位城市
          pName: 'CitySearch',
          events: {
            init(o) {
              // o是高德地图定位插件实例
              o.getLocalCity(function(status, result) {
                if (status === 'complete' && result.info === 'OK') {
                  // 查询成功,result即为当前所在城市信息
                  console.log('查询成功,当前所在城市信息', result)
                }
              })
            }
          }
        }
      ]
    }
  },
  methods: {
    // 点击地图获取经纬度和具体位置
    click(e) {
      const { lng, lat } = e.lnglat
      this.$emit('update:center', [lng, lat])
      this.$emit('update:markers', [[lng, lat]])
      console.log('[lng, lat]', [lng, lat])
 
      // 这里通过高德 SDK 完成。获取具体地址的
      const geocoder = new AMap.Geocoder({
        radius: 1000,
        extensions: 'all'
      })
      const that = this
      geocoder.getAddress([lng, lat], function(status, result) {
        if (status === 'complete' && result.info === 'OK') {
          if (result && result.regeocode) {
            that.address = result.regeocode.formattedAddress
            console.log('具体位置:', result.regeocode.formattedAddress)
            that.$nextTick()
          }
        }
        that.$emit('getLocation', { lng, lat, address: that.address })
      })
    },
    getIcon(item) {
      if (item.mediaTypeName === '灯杆屏') {
        return item.status ? this.lamppost0 : this.lamppost1
      } else if (item.mediaTypeName === '导览屏') {
        return item.status ? this.tourGuide0 : this.tourGuide1
      } else return item.status ? this.LED0 : this.LED1
    }
  }
}
</script>
<style lang="scss" scoped>
/* 指定地图的宽高 */
.amapClass, .Map {
  width: 100%;
  height: 100%;
}
</style>

关于el-amap-marker的属性

ts
1、position:为marker点的坐标(经纬度),接收数组

2、icon:自定义图标地址

3、events:事件集合对象,click、dblclick、rightclick、mouseover、mouseout等

4、offset:偏移量

其他我没用到的还有:

5、clickable:true允许用户可点击marker点(默认也是true)

6、animation:marker点的动画效果

      marker点弹跳效果:设置值为 AMAP_ANIMATION_BOUNCE

7、label:点显示的简略信息

8、content可以是文字字符串也可以是html
ts
events: {
    click: (e) => {
        // ...点击操作
    },
    dblclick: (e) => {
        // ...双击操作
    },
    ...
}

关于el-amap-info-window信息窗体的属性

静态属性

image-20241212163318741

动态属性

image-20241212163336740

ref 可用方法

ts
提供无副作用的同步帮助方法

函数					返回					说明
$$getInstance()		AMap.InfoWindow		获取infoWindow实例

详细见 高德API https://lbs.amap.com/api/javascript-api/reference/infowindow

事件(也是events里的)

image-20241212163606342

使用地图组件

我把使用的页面也全贴上了,但其实search-box里面的东西大家可以忽略,只是针对markerList的查询过滤

vue
<template>
  <div class="box">
    <div class="search-box">
      <el-form ref="queryForm" label-width="auto" class="searchForm" :model="searchForm" style="padding: 16px 0 0;">
        <el-form-item label="类型" prop="mediaType">
          <el-select v-model="searchForm.mediaType">
            <el-option v-for="item in digitalTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
          </el-select>
        </el-form-item>
        <el-form-item label="区域" prop="region">
          <el-select v-model="searchForm.region" clearable>
            <el-option v-for="item in regionOptions" :key="item.value" :label="item.label" :value="item.value" />
          </el-select>
        </el-form-item>
        <el-form-item label-width="20px">
          <el-button v-permission="['ResourceDistribution:query']" class="search-btn" icon="el-icon-search" type="primary" @click="getDetail">
            查询
          </el-button>
          <el-button class="reset-btn" icon="el-icon-refresh-left" @click="resetForm('queryForm')">
            重置
          </el-button>
        </el-form-item>
        <el-form-item label="设备名称" prop="">
          <el-select v-model="searchKey" filterable clearable placeholder="请输入关键字" @change="quickOrientation">
            <el-option v-for="item in markerList" :key="item.mediaCode" :label="item.mediaName" :value="item.mediaCode" />
          </el-select>
          <!-- <el-input id="search" v-model="searchKey" placeholder="请输入设备名称" /> -->
        </el-form-item>
        <el-form-item label-width="20px" style="width: 100px;">
          <el-button v-permission="['ResourceDistribution:query']" class="search-btn" type="primary" @click="quickOrientation">
            快速定位
          </el-button>
        </el-form-item>
      </el-form>
      <div class="rightType">
        <div v-for="item in digitalTypeOptions" :key="item.value" class="iconItem">
          <svg-icon v-if="item.label === '灯杆屏'" icon-class="lamppost" />
          <svg-icon v-else-if="item.label === '导览屏'" icon-class="tourGuide" />
          <svg-icon v-else icon-class="LED" />
          <span>{{ item.label }}</span>
        </div>
      </div>
    </div>
    <div class="rightBottom">
      <span class="successStatus">已启用</span>
      <span class="errorStatus">已禁用</span>
    </div>
    <!-- 地图组件 -->
    <Map
      :center.sync="center"
      :markers.sync="markers"
      :zoom.sync="zoom"
      :marker-list="markerList"
      :current-window="currentWindow"
      @getLocation="getLocation"
    />
  </div>
</template>
 
<script>
import Map from '@/components/Map'
import { dictList, deviceFilter } from '@/api/resource'
export default {
  name: 'GaodeDituAmap',
  components: { Map },
  data() {
    return {
      searchForm: {
        mediaType: '',
        region: ''
      },
      searchKey: '',
      digitalTypeOptions: [],
      regionOptions: [],
      currentWindow: {
        position: [],
        visible: false,
        content: '',
        events: {}
      },
      markerList: [],
      markers: [],
      center: [121.133858, 30.606042],
      zoom: 14 // 地图缩放比例。zoom值越小,越宏观、越大,越微观。
    }
  },
  mounted() {
    this.getDict()
    this.getDetail()
  },
  methods: {
    // 获取标点信息
    async getDetail() {
      this.currentWindow.visible = false
      const that = this
      const { data } = await deviceFilter(this.searchForm)
      data.forEach(item => {
        item.lnglats = [item.longitude, item.latitude]
        item.events = {
          click(e) {
            console.log('点击标点', e)
            that.center = item.lnglats
            that.currentWindow.content = that.createWinContent(item)
            that.currentWindow.position = item.lnglats
            that.currentWindow.events = {
              close(e) {
                console.log('窗体关闭', e)
                that.currentWindow.visible = false
              }
            }
            that.currentWindow.visible = true
          }
        }
      })
      this.markerList = data
    },
    /**
     * 基础配置信息
     */
    async getDict() {
      const { data: { area, digitalType }} = await dictList('dictNames=area&dictNames=digitalType')
      this.regionOptions = area
      this.digitalTypeOptions = digitalType
    },
    // 重置
    resetForm(formName) {
      this.$refs[formName].resetFields()
      this.zoom = this.$options.data.call(this).zoom
      this.getDetail()
    },
    // 快速定位
    quickOrientation() {
      if (!this.searchKey) {
        this.markers = []
        this.currentWindow.visible = false
        return
      }
      const cur = this.markerList.find(f => f.mediaCode === this.searchKey)
      if (cur) {
        this.center = [cur.longitude, cur.latitude]
        // this.markers = [this.center]
        this.currentWindow.content = this.createWinContent(cur)
        this.currentWindow.position = [cur.longitude, cur.latitude]
        this.currentWindow.visible = true
        this.zoom = 15
      }
    },
 
    // 获取经纬度及地理位置
    getLocation({ lng, lat, address }) {
      console.log('获取到经纬度和地址啦:', lng, lat, address)
    },
    // 画信息窗
    createWinContent(item) {
      return `
        <div>
          <div style="display: flex;margin-bottom: 12px">
            ${item.src ? `<img src="${item.src}" style="width: 120px;margin-right: 16px;">` : ''}
            <div style="display: flex;flex-direction: column;justify-content: space-around;">
              <strong style="font-size: 18px">${item.mediaName}</strong>
              <div style="margin-top: 6px">
                <span style="font-size: 14px;color: #0082FF;padding: 0px 6px;background: rgba(0,130,255,0.15);border-radius: 4px;">${item.mediaTypeName}</span>
                <span style="font-size: 14px;color: ${!item.status ? '#46A77B' : '#E30000'};padding: 0px 6px;background: ${!item.status ? '#46A77B26' : '#E3000026'};border-radius: 4px;">${!item.status ? '已启用' : '已禁用'}</span>
              </div>
              <div style="margin-top: 8px">
                <i class="el-icon-location" style="color: #0082FF"></i>
                <span style="font-size: 14px;">${item.regionName}</span>
              </div>
            </div>
          </div>
          <div style="font-size: 14px;color: #666666;line-height: 20px;">
            <span>${item.specification},</span>
            <span>${item.supplier},</span>
            <span>${item.supplierContact},</span>
            <span>${item.supplierPhone}</span>
          </div>
        </div>`
    }
  }
}
</script>
<style lang="scss" scoped>
.box {
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  padding: 0px;
  position: relative;
 
  .search-box {
    position: absolute;
    background-color: #FFFFFF;
    width: calc(100% - 32px);
    top: 16px;
    left: 16px;
    z-index: 999;
    border-radius: 4px;
 
    .searchForm .el-form-item {
      margin: 0 0px 16px;
    }
 
    .tip-box {
      width: 100%;
      max-height: 260px;
      position: absolute;
      top: 30px;
      overflow-y: auto;
      background-color: #fff;
    }
    .rightType{
      position: absolute;
      right: 0px;
      bottom: -24px;
      transform: translateY(100%);
      z-index: 999;
      background: #FFFFFF;
      border-radius: 4px;
      padding: 16px;
      display: flex;
      align-items: center;
      flex-direction: column;
      .iconItem{
        font-size: 14px;
        color: #4A4A4A;
        display: flex;
        align-items: center;
        flex-direction: column;
        .svg-icon{
          width: 24px;
          height: 24px;
        }
      }
      .iconItem+.iconItem{
        margin-top: 24px;
      }
    }
  }
  .rightBottom{
    position: absolute;
    right: 16px;
    bottom: 16px;
    z-index: 999;
    padding: 16px;
    font-size: 14px;
    color: #4A4A4A;
    background-color: #FFFFFF;
    border-radius: 4px;
    .successStatus{
      margin-right: 24px;
    }
    .successStatus::before{
      content: '';
      display: inline-block;
      height: 8px;
      width: 8px;
      background-color: #46A77B;
      border-radius: 50%;
      margin-right: 5px;
    }
    .errorStatus::before{
      content: '';
      display: inline-block;
      height: 8px;
      width: 8px;
      background-color: #FF4747;
      border-radius: 50%;
      margin-right: 5px;
    }
  }
}
 
/* 指定地图的宽高 */
.amapClass {
  width: 100%;
  height: 100%;
}
</style>

vuemap/vue-amap插件

https://www.npmjs.com/package/@vuemap/vue-amap

ts
// 安装核心库
npm i -S @vuemap/vue-amap
// 安装loca相关库
npm i -S @vuemap/vue-amap-loca
// 安装其他扩展库,主要为threejs相关
npm i -S @vuemap/vue-amap-extra

基本使用

引入@vuemap/vue-amap

ts
// 引入vue-amap
import VueAMap, {initAMapApiLoader} from '@vuemap/vue-amap';
import '@vuemap/vue-amap/dist/style.css';

// 初始化vue-amap
initAMapApiLoader({
  // 高德的key
  key: 'YOUR_KEY',
  securityJsCode: 'securityJsCode', // 新版key需要配合安全密钥使用
  //Loca:{
  //  version: '2.0.0'
  //} // 如果需要使用loca组件库,需要加载Loca
});
createApp(App).use(VueAMap)

引入类型 忽略

ts
shims-vue-amap.d.ts

declare module 'vue-amap' {
    import { App } from 'vue';
  
    const VueAMap: {
      install: (app: App) => void;
      initAMapApiLoader: (config: {
        key: string;
        plugin?: string[];
        v?: string;
        uiVersion?: string;
      }) => void;
    };
  
    export default VueAMap;
  }