效果图:
Vue + Echarts + 高德地图 实现点击下探图表_前端

1. 高德地图 Key 申请

参考文章传送门

2. 引入使用AMapJS

AMapJS 是 AMap高德地图API加载器。
帮助开发者快速加载高德地图相关API,在模块化应用、异步编程中使用API更加灵活便捷。
安装:

npm i amap-js -S

完整引入:

import AMapJS from 'amap-js';

本文仅使用AMapUILoader

3. Vue 中实现效果图完整代码

<template>
  <div class="app-container">
    <div id="chinaMap"></div>
    <div class="mapChoose">
      <span v-for="(item,index) in parentInfo" :key="item.code">
        <span
          class="title"
          @click="chooseArea(item,index)"
        >{{item.cityName=='全国'?'中国':item.cityName}}</span>
        <span class="icon" v-show="index+1!=parentInfo.length">></span>
      </span>
    </div>
  </div>
</template>

<script>
import AMapJS from 'amap-js'
import resize from '@/mixins/resize'

export default {
  name: 'home',
  components: {},
  data() {
    return {
      // 创建AMapJSAPI Loader
      amapLoader: new AMapJS.AMapLoader({
        key: '申请的高德地图key',
        version: '1.4.15',
        plugins: [],
      }),
      // 创建AMapUI Loader
      amapuiLoader: new AMapJS.AMapUILoader({ version: '1.0' }),
      timeTitle: ['2016', '2017', '2018', '2019', '2020'],
      parentInfo: [
        {
          cityName: '全国',
          code: 100000,
        },
      ],
      geoJson: {
        features: [],
      },
    }
  },
  mixins: [resize],
  methods: {
    async getGeoJson(adcode) {
      await this.amapLoader.load()
      await this.amapuiLoader.load()

      let that = this
      AMapUI.loadUI(['geo/DistrictExplorer'], DistrictExplorer => {
        var districtExplorer = new DistrictExplorer()
        districtExplorer.loadAreaNode(adcode, function(error, areaNode) {
          if (error) {
            console.error(error)
            return
          }
          let Json = areaNode.getSubFeatures()
          if (Json.length > 0) {
            that.geoJson.features = Json
          } else if (Json.length === 0) {
            that.geoJson.features = that.geoJson.features.filter(
              item => item.properties.adcode == adcode
            )
            if (that.geoJson.features.length === 0) return
          }
          that.getMapData()
        })
      })
    },
    // 获取数据
    getMapData() {
      let mapData = {},
        pointData = {},
        sum = {}
      for (let i = 0; i < this.timeTitle.length; i++) {
        mapData[this.timeTitle[i]] = []
        pointData[this.timeTitle[i]] = []
        sum[this.timeTitle[i]] = 0
        for (let j = 0; j < this.geoJson.features.length; j++) {
          let value = Math.random() * 3000
          mapData[this.timeTitle[i]].push({
            name: this.geoJson.features[j].properties.name,
            value: value,
            level: this.geoJson.features[j].properties.level,
            cityCode: this.geoJson.features[j].properties.adcode,
          })
          pointData[this.timeTitle[i]].push({
            name: this.geoJson.features[j].properties.name,
            value: [
              this.geoJson.features[j].properties.center[0],
              this.geoJson.features[j].properties.center[1],
              value,
            ],
            cityCode: this.geoJson.features[j].properties.adcode,
          })
          sum[this.timeTitle[i]] += value
        }
        mapData[this.timeTitle[i]] = mapData[this.timeTitle[i]].sort(function(
          a,
          b
        ) {
          return b.value - a.value
        })
      }
      this.drawChinaMap(mapData, pointData, sum)
    },

    drawChinaMap(mapData, pointData, sum) {
      var echarts = require('echarts')
      var mapChart = echarts.init(document.getElementById('chinaMap'))
      //这里做个切换,全国的时候才显示南海诸岛  只有当注册的名字为china的时候才会显示南海诸岛
      if (this.parentInfo.length === 1) {
        echarts.registerMap('china', this.geoJson) //注册
      } else {
        echarts.registerMap('map', this.geoJson) //注册
      }

      var option = {
        timeline: {
          data: this.timeTitle,
          axisType: 'category',
          autoPlay: true,
          playInterval: 3000,
          left: '10%',
          right: '10%',
          bottom: '2%',
          width: '80%',
          label: {
            normal: {
              textStyle: {
                color: 'rgb(179, 239, 255)',
              },
            },
            emphasis: {
              textStyle: {
                color: '#fff',
              },
            },
          },
          symbolSize: 10,
          lineStyle: {
            color: '#8df4f4',
          },
          checkpointStyle: {
            borderColor: '#8df4f4',
            color: '#53D9FF',
            borderWidth: 2,
          },
          controlStyle: {
            showNextBtn: true,
            showPrevBtn: true,
            normal: {
              color: '#53D9FF',
              borderColor: '#53D9FF',
            },
            emphasis: {
              color: 'rgb(58,115,192)',
              borderColor: 'rgb(58,115,192)',
            },
          },
        },
        baseOption: {
          animation: true,
          animationDuration: 900,
          animationEasing: 'cubicInOut',
          animationDurationUpdate: 900,
          animationEasingUpdate: 'cubicInOut',
          tooltip: {
            trigger: 'axis',
            axisPointer: {
              type: 'shadow',
            },
          },
          grid: {
            right: '2%',
            top: '12%',
            bottom: '8%',
            width: '20%',
          },
          toolbox: {
            feature: {
              restore: {
                show: false,
              },
              dataView: {
                optionToContent: function(opt) {
                  let series = opt.series[0].data //折线图数据
                  //表头
                  let tdHeads =
                    '<th  style="padding: 0 20px">所在地区</th><th style="padding: 0 20px">销售额</th>' 
                  let tdBodys = '' //数据
                  let table = `<table border="1" style="margin-left:20px;border-collapse:collapse;
                  	font-size:14px;text-align:left;"><tbody><tr>${tdHeads} </tr>`
                  for (let i = 0; i < series.length; i++) {
                    table += `<tr>
                              <td style="padding: 0 50px">${series[i].name}</td>
                              <td style="padding: 0 50px">${series[
                                i
                              ].value.toFixed(2)}万</td>
                              </tr>`
                  }
                  table += '</tbody></table>'
                  return table
                },
              },
              saveAsImage: {
                name:
                  this.parentInfo[this.parentInfo.length - 1].cityName +
                  '销售额统计图',
              },
              dataZoom: {
                show: false,
              },
              magicType: {
                show: false,
              },
            },
            iconStyle: {
              normal: {
                borderColor: '#1990DA',
              },
            },
            top: 15,
            right: 35,
          },
          // 地理坐标系组件
          geo: {
            map: this.parentInfo.length === 1 ? 'china' : 'map',
            zoom: 1.1, // 当前视角的缩放比例 
            roam: true, // 鼠标缩放和平移
            center:
              this.parentInfo.length === 1
                ? ['118.83531246', '32.0267395887']
                : false,
            tooltip: {
              trigger: 'item',
              formatter: p => {
                let val = p.value[2]
                if (window.isNaN(val)) {
                  val = 0
                }
                let txtCon =
                  "<div style='text-align:left'>" +
                  p.name +
                  ':<br />销售额:' +
                  val.toFixed(2) +
                  '万</div>'
                return txtCon
              },
            },
            label: {
              normal: {
                show: true,
                color: 'rgb(249, 249, 249)', //省份标签字体颜色
                formatter: p => {
                  switch (p.name) {
                    case '内蒙古自治区':
                      p.name = '内蒙古'
                      break
                    case '西藏自治区':
                      p.name = '西藏'
                      break
                    case '新疆维吾尔自治区':
                      p.name = '新疆'
                      break
                    case '宁夏回族自治区':
                      p.name = '宁夏'
                      break
                    case '广西壮族自治区':
                      p.name = '广西'
                      break
                    case '香港特别行政区':
                      p.name = '香港'
                      break
                    case '澳门特别行政区':
                      p.name = '澳门'
                      break
                    default:
                      break
                  }
                  return p.name
                },
              },
              emphasis: {
                show: true,
                color: '#f75a00',
              },
            },
            itemStyle: {
              normal: {
                areaColor: '#24CFF4',
                borderColor: '#53D9FF',
                borderWidth: 1.3,
                shadowBlur: 15,
                shadowColor: 'rgb(58,115,192)',
                shadowOffsetX: 7,
                shadowOffsetY: 6,
              },
              emphasis: {
                areaColor: '#8dd7fc',
                borderWidth: 1.6,
                shadowBlur: 25,
              },
            },
          },
        },
        options: [],
      }

      this.timeTitle.forEach(item => {
        var xData = [],
          yData = []
        var min = mapData[item][mapData[item].length - 1].value
        var max = mapData[item][0].value
        if (mapData[item].length === 1) {
          min = 0
        }
        mapData[item].forEach(c => {
          xData.unshift(c.name)
          yData.unshift(c.value)
        })
        option.options.push({
          backgroundColor: '#012248',
          title: [
            {
              left: 'center',
              top: 10,
              text:
                item +
                this.parentInfo[this.parentInfo.length - 1].cityName +
                '销售额统计图(可点击下钻到县)',
              textStyle: {
                color: 'rgb(179, 239, 255)',
                fontSize: 16,
              },
            },
            {
              text: '销售总额:' + sum[item].toFixed(2) + '万',
              left: 'center',
              top: '6.5%',
              textStyle: {
                color: '#FFAC50',
                fontSize: 26,
              },
            },
          ],
          // 视觉映射组件
          visualMap: {
            min: min,
            max: max,
            left: '3%',
            bottom: '5%',
            calculable: true, // 显示拖拽手柄
            seriesIndex: [0],
            inRange: {
              color: ['#24CFF4', '#2E98CA', '#1E62AC'],
            },
            textStyle: {
              color: '#24CFF4',
            },
          },
          // 柱状图横轴
          xAxis: {
            type: 'value',
            scale: true,
            position: 'top',
            boundaryGap: false,
            splitLine: {
              show: false,
            },
            axisLine: {
              show: true,
              lineStyle: {
                color: '#455B77',
              },
            },
            axisTick: {
              show: false,
            },
            axisLabel: {
              margin: 2,
              textStyle: {
                color: '#c0e6f9',
              },
            },
          },
          // 柱状图纵轴
          yAxis: {
            type: 'category',
            nameGap: 16,
            axisLine: {
              show: true,
              lineStyle: {
                color: '#455B77',
              },
            },
            axisTick: {
              show: false,
            },
            axisLabel: {
              interval: 0,
              textStyle: {
                color: '#c0e6f9',
              },
            },
            data: xData,
          },
          series: [
            {
              name: item + '销售额度',
              type: 'map',
              geoIndex: 0,
              map: this.parentInfo.length === 1 ? 'china' : 'map',
              roam: true,
              zoom: 1.3,
              tooltip: {
                trigger: 'item',
                formatter: p => {
                  let val = p.value
                  if (p.name == '南海诸岛') return
                  if (window.isNaN(val)) {
                    val = 0
                  }
                  let txtCon =
                    "<div style='text-align:left'>" +
                    p.name +
                    ':<br />销售额:' +
                    val.toFixed(2) +
                    '万</div>'
                  return txtCon
                },
              },
              label: {
                normal: {
                  show: false,
                },
                emphasis: {
                  show: false,
                },
              },
              data: mapData[item],
            },
            {
              name: '散点',
              type: 'effectScatter',
              coordinateSystem: 'geo',
              rippleEffect: {
                brushType: 'fill',
              },
              label: {
                normal: {
                  formatter: p => {
                    return p.value[2].toFixed()
                  },
                  position: 'center', //地图上是否有文字
                  show: true,
                  textStyle: {
                    color: '#fff'
                  }
                },
                emphasis: {
                  show: false
                }
              },
              itemStyle: {
                normal: {
                  color: '#F4E925',
                  shadowBlur: 10,
                  shadowColor: '#333',
                },
              },
              data: pointData[item],
              symbolSize: function(val) {
                let value = val[2]
                if (value == max) {
                  return 27
                }
                return 10
              },
              showEffectOn: 'render', //加载完毕显示特效
            },
            {
              type: 'bar',
              barGap: '-100%',
              barCategoryGap: '60%',
              itemStyle: {
                normal: {
                  color: '#11AAFE',
                },
                emphasis: {
                  show: false,
                },
              },
              data: yData,
            },
          ],
        })
      })

      mapChart.setOption(option, true)

      //点击前解绑,防止点击事件触发多次
      mapChart.off('click')
      mapChart.on('click', this.echartsMapClick)
    },

    //echarts点击事件
    echartsMapClick(params) {
      if (!params.data) {
        return
      } else {
        //如果当前是最后一级,那就直接return
        if (
          this.parentInfo[this.parentInfo.length - 1].code ==
          params.data.cityCode
        ) {
          return
        }
        let data = params.data
        this.parentInfo.push({
          cityName: data.name,
          level: data.level,
          code: data.cityCode,
        })
        this.getGeoJson(data.cityCode)
      }
    },
    //选择切换市县
    chooseArea(val, index) {
      if (this.parentInfo.length === index + 1) {
        return
      }
      this.parentInfo.splice(index + 1)
      this.getGeoJson(this.parentInfo[this.parentInfo.length - 1].code)
    },
  },
  mounted() {
    this.getGeoJson(100000)
  },
}
</script>

<style lang="scss" scoped>
.app-container {
  #chinaMap {
    width: 1400px;
    height: 800px;
  }
  .mapChoose {
    position: absolute;
    left: 20px;
    top: 55px;
    color: #eee;

    .title {
      padding: 5px;
      border-top: 1px solid rgba(147, 235, 248, 0.8);
      border-bottom: 1px solid rgba(147, 235, 248, 0.8);
      cursor: pointer;
    }

    .icon {
      font-family: 'simsun';
      font-size: 25px;
      margin: 0 11px;
    }
  }
}
</style>

resize.js

import {
  debounce
} from "@/utils/index.js"

export default {
  data() {
    return {
      myChart: null,
      resizeHandler: null
    }
  },
  mounted() {
    this.resizeHandler = debounce(() => {
      if (this.myChart) {
        this.myChart.resize()
      }
    }, 200)
    this.initResizeEvent();
  },

  beforeDestroy() {
    this.destroyResizeEvent()
    if (!this.myChart) {
      return
    }
    this.myChart.dispose()
    this.myChart.off('click')
    this.myChart = null
  },

  activated() {
    this.initResizeEvent()
  },

  deactivated() {
    this.destroyResizeEvent()
  },

  methods: {
    //监听resize
    initResizeEvent() {
      window.addEventListener('resize', this.resizeHandler)
    },
    //移除resize
    destroyResizeEvent() {
      window.removeEventListener('resize', this.resizeHandler);
    }
  }
}

index.js

/**
 *  函数防抖
 *  @param {Function} func  包装的函数
 *  @param {num} delay      延迟时间
 *  @param {boolean} immediate 第一次滚动会执行两次  开始滚动和结束滚动的时候
 *  @return {*}      
 */

export function debounce(func, delay, immediate = false) {
  let timer, context = this
  return (...args) => {
    if (immediate) {
      func.apply(context, args)
      immediate = false
      return
    }
    clearTimeout(timer)
    timer = setTimeout(() => {
      func.apply(context, args)
    }, delay)
  }
}

参考echarts图表(侵删)