• vue+echarts可视化大屏,全国地图下钻,页面自适应


    之前写过一篇关于数据大屏及地图下钻的文章 https://www.cnblogs.com/weijiutao/p/13977011.html ,但是存在诸多问题,如地图边界线及行政区划老旧,无法自适应问题等,正好抽时间又整理了一下修改的思路.

    之前的文章已经获取了一套新的全国地图的行政区划及边界线,接下来就可以根据这套区划来进行地图的编写了.先来看一下最后的呈现效果.

    代码目录如下

    地图采用了最新的行政区划及边界进行加载,具体获取方式在另一篇文章 https://www.cnblogs.com/weijiutao/p/15989290.html

    地图边界下目录

     这次代码与上一个版本的最大区别在于china.vue文件,如下

    复制代码
      1 <template>
      2   <div id="map-container">
      3     <el-button type="text" size="large" class="back" @click="back" v-if="deepTree.length > 1">返回</el-button>
      4     <div class="echarts">
      5       <div id="map"></div>
      6     </div>
      7   </div>
      8 </template>
      9 
     10 <script>
     11 
     12 import {getChinaJson, getProvinceJson, getCityJson, getDistrictJson} from "@/api/map";
     13 import {mapOption} from '@/config/mapOption'
     14 import resize from '@/utils/resize'
     15 
     16 
     17 export default {
     18   mixins: [resize],
     19   name: "china",
     20   components: {},
     21   props: {
     22     areaCode: {
     23       type: String,
     24       default: '000000000000'
     25     },
     26     areaLevel: {
     27       type: [String, Number],
     28       default: 0
     29     },
     30     areaName: {
     31       type: String,
     32       default: 'china'
     33     },
     34     // 当前地图上的地区名字
     35     mapNameList: {
     36       type: Array,
     37       default() {
     38         return []
     39       }
     40     },
     41     // 当前地图上的地区Code
     42     mapCodeList: {
     43       type: Array,
     44       default() {
     45         return []
     46       }
     47     },
     48     // 地区统计数据
     49     areaStatistic: {
     50       type: Array,
     51       default() {
     52         return []
     53       }
     54     }
     55   },
     56   data() {
     57     return {
     58       chart: null, // 实例化echarts
     59       mapDataList: [], // 当前地图上的地区
     60       option: {...mapOption.basicOption}, // map的相关配置
     61       deepTree: [],// 点击地图时push,点返回时pop
     62       areaStatisticMapValue: {}, // 地图数据value, 只是amounts
     63       areaStatisticMapData: {}, // 地图数据data,包含所有数据
     64       areaLevelMap: {
     65         'country': 0,
     66         'china': 0,
     67         'province': 1,
     68         'city': 2,
     69         'district': 3,
     70       },
     71       tooltipAutoplay: null, // 提示框自动播放
     72       tooltipAutoplayIndex: 0, // 提示框自动播放index
     73     }
     74   },
     75   beforeDestroy() {
     76     if (!this.chart) {
     77       return
     78     }
     79     this.chart.dispose()
     80     this.chart = null
     81   },
     82   mounted() {
     83     this.$nextTick(() => {
     84       this.initEcharts();
     85       this.chart.on('click', this.echartsMapClick);
     86       this.chart.on('mouseover', this.echartsMapMouseover);
     87       this.chart.on('mouseout', this.echartsMapMouseout);
     88     });
     89   },
     90   watch: {
     91     areaStatistic: {
     92       handler(val) {
     93         var objValue = {}, objData = {}
     94         for (var i = 0; i < val.length; i++) {
     95           objValue[val[i]['areaCode'].substr(0, 6)] = val[i].amounts * 1
     96           objData[val[i]['areaCode'].substr(0, 6)] = val[i]
     97         }
     98         this.areaStatisticMapValue = objValue
     99         this.areaStatisticMapData = objData
    100         this.initEcharts()
    101       },
    102       deep: true,
    103     }
    104   },
    105   methods: {
    106     // 初次加载绘制地图
    107     initEcharts() {
    108       //地图容器
    109       // this.$echarts.dispose(document.getElementById('map'))
    110       this.chart = this.$echarts.init(document.getElementById('map'));
    111       if (this.areaLevel === 0) {
    112         this.requestGetChinaJson();
    113       } else if (this.areaLevel === 1) {
    114         this.requestGetProvinceJSON({name: this.areaName, level: 'province', adcode: this.areaCode.substr(0, 6)})
    115       } else if (this.areaLevel === 2) {
    116         this.requestGetCityJSON({name: this.areaName, level: 'city', adcode: this.areaCode.substr(0, 6)})
    117       } else if (this.areaLevel === 3) {
    118         this.requestGetDistrictJSON({name: this.areaName, level: 'district', adcode: this.areaCode.substr(0, 6)})
    119       } else {
    120         return false
    121       }
    122     },
    123     // 地图点击
    124     echartsMapClick(params) {
    125       this.$emit('update:areaCode', params.data.adcode + '000000')
    126       this.$emit('update:areaName', params.data.name)
    127       this.$emit('update:areaLevel', this.areaLevelMap[params.data.level])
    128       if (params.data.level === 'province') {
    129         this.requestGetProvinceJSON(params.data);
    130       } else if (params.data.level === 'city') {
    131         this.requestGetCityJSON(params.data)
    132       } else if (params.data.level === 'district' && this.mapDataList.length > 1) {
    133         this.requestGetDistrictJSON(params.data)
    134       } else {
    135         return false
    136       }
    137     },
    138     //绘制全国地图areaStatistic
    139     requestGetChinaJson() {
    140       getChinaJson().then(res => {
    141         // console.log('china--->', res)
    142         this.$emit('update:areaLevel', 0)
    143         this.setJsonData(res)
    144       });
    145     },
    146     // 加载省级地图
    147     requestGetProvinceJSON(params) {
    148       getProvinceJson(params.adcode).then(res => {
    149         // console.log('province--->', res)
    150         this.$emit('update:areaLevel', 1)
    151         this.setJsonData(res, params)
    152       });
    153     },
    154     // 加载市级地图
    155     requestGetCityJSON(params) {
    156       getCityJson(params.adcode).then(res => {
    157         // console.log('city--->', res)
    158         this.$emit('update:areaLevel', 2)
    159         this.setJsonData(res, params)
    160       })
    161     },
    162     // 加载县级地图
    163     requestGetDistrictJSON(params) {
    164       getDistrictJson(params.adcode).then(res => {
    165         // console.log('district--->', res)
    166         this.$emit('update:areaLevel', 3)
    167         this.setJsonData(res, params)
    168       })
    169     },
    170     // 设置数据
    171     setJsonData(res, params) {
    172       var mapDataList = [];
    173       var mapNameList = [];
    174       var mapCodeList = [];
    175       for (var i = 0; i < res.features.length; i++) {
    176         var obj = {
    177           ...res.features[i].properties,
    178           value: this._mathRandom1000(),
    179           valueData: this._mathRandom1000(),
    180         };
    181         mapDataList.unshift(obj)
    182         mapNameList.unshift(res.features[i].properties.name)
    183         mapCodeList.unshift(res.features[i].properties.adcode + '000000')
    184       }
    185       this.mapDataList = mapDataList;
    186       this.$emit('update:mapNameList', mapNameList)
    187       this.$emit('update:mapCodeList', mapCodeList)
    188       this.setMapData(res, params)
    189     },
    190     // 设置地图信息
    191     setMapData(res, params) {
    192       if (this.areaName === 'china') {
    193         this.deepTree.push({
    194           mapDataList: this.mapDataList,
    195           params: {name: 'china', level: 'country', adcode: '100000'}
    196         });
    197         //注册地图
    198         this.$echarts.registerMap('china', res);
    199         //绘制地图
    200         this.renderMap('china', this.mapDataList);
    201       } else {
    202         this.deepTree.push({mapDataList: this.mapDataList, params: params});
    203         this.$echarts.registerMap(params.name, res);
    204         this.renderMap(params.name, this.mapDataList);
    205       }
    206     },
    207     // 渲染地图
    208     renderMap(map, data) {
    209       var mapDataList = data.map(item => {
    210         return {
    211           name: item.name,
    212           value: item.value
    213         }
    214       })
    215       mapDataList = mapDataList.sort(function (a, b) {
    216         return b.value - a.value
    217       });
    218       var pointData = []
    219       for (var i = 0; i < data.length; i++) {
    220         if (data[i].value != 0) {
    221           pointData.push({
    222             ...data[i],
    223             value: [data[i].center[0], data[i].center[1], data[i].value],
    224           })
    225         }
    226       }
    227       // 设置左下角数量范围值
    228       this.option.visualMap.min = mapDataList.length > 1 ? mapDataList[mapDataList.length - 2].value : 0
    229       this.option.visualMap.max = mapDataList.length > 0 ? mapDataList[0].value : 0
    230       // 设置左上角当前位置
    231       this.option.title[0].text = map === 'china' ? '全国' : map
    232       this.option.geo = {
    233         show: false,
    234         map: map,
    235         zoom: 1.2, //当前视角的缩放比例
    236         roam: true, //是否开启平游或缩放
    237         center: undefined,
    238       }
    239       this.option.series = [
    240         {
    241           name: map,
    242           mapType: map,
    243           zoom: 1, //当前视角的缩放比例
    244           roam: false, //是否开启平游或缩放
    245           center: undefined,
    246           scaleLimit: { //滚轮缩放的极限控制
    247             min: .5,
    248             max: 10
    249           },
    250           ...mapOption.seriesOption,
    251           data: data
    252         },
    253         {
    254           name: '散点',//series名称
    255           type: 'effectScatter',//散点类型
    256           coordinateSystem: 'geo',// series坐标系类型
    257           rippleEffect: {
    258             brushType: 'fill'
    259           },
    260           normal: {
    261             show: true,
    262             // 提示内容
    263             formatter: params => {
    264               return params.name;
    265             },
    266             position: 'top', // 提示方向
    267             color: '#fff'
    268           },
    269           emphasis: {
    270             show: true //
    271           },
    272           itemStyle: {
    273             normal: {
    274               color: '#F4E925',
    275               shadowBlur: 10,
    276               shadowColor: '#000'
    277             }
    278           },
    279           // symbol:'pin', // 散点样式'pin'(标注)、'arrow'(箭头)
    280           data: pointData,
    281           symbolSize: function (val) {
    282             // return val[2] / 100;
    283             if (val[2] === mapDataList[0].value) {
    284               return 10
    285             }
    286             return 6
    287           },
    288           showEffectOn: 'render', //加载完毕显示特效
    289         },
    290       ]
    291       //渲染地图
    292       this.chart.setOption(this.option, true)
    293       this.setTooltipAutoplay()
    294     },
    295     // 地图鼠标移入事件
    296     echartsMapMouseover() {
    297       clearInterval(this.tooltipAutoplay)
    298     },
    299     // 地图鼠标移出事件
    300     echartsMapMouseout() {
    301       this.setTooltipAutoplay()
    302     },
    303     // 动态显示tooltip
    304     setTooltipAutoplay() {
    305       clearInterval(this.tooltipAutoplay)
    306       // var index = 0; //播放所在下标
    307       // if(this.chart.dispatchAction) {
    308       this.tooltipAutoplay = setInterval(() => {
    309         this.chart.dispatchAction({
    310           type: 'showTip',
    311           seriesIndex: 0,
    312           dataIndex: this.tooltipAutoplayIndex
    313         })
    314         this.tooltipAutoplayIndex++
    315         if (this.tooltipAutoplayIndex >= this.mapDataList.length) {
    316           this.tooltipAutoplayIndex = 0;
    317           this.setTooltipAutoplay()
    318         }
    319       }, 6666)
    320       // }
    321     },
    322     // 返回
    323     back() {
    324       if (this.deepTree.length > 1) {
    325         this.deepTree.pop();
    326         this.mapDataList = this.deepTree[this.deepTree.length - 1].mapDataList;
    327         var areaName = this.deepTree[this.deepTree.length - 1].params.name;
    328         var areaCode = this.deepTree[this.deepTree.length - 1].params.adcode;
    329         var areaLevel = this.deepTree[this.deepTree.length - 1].params.level;
    330         var mapNameList = this.mapDataList.map(item => {
    331           return item.name
    332         })
    333         var mapCodeList = this.mapDataList.map(item => {
    334           return item.adcode + '000000'
    335         })
    336         this.$emit('update:areaCode', (areaCode === '100000' ? '000000' : areaCode) + '000000')
    337         this.$emit('update:areaName', areaName)
    338         this.$emit('update:areaLevel', this.areaLevelMap[areaLevel])
    339         this.$emit('update:mapNameList', mapNameList)
    340         this.$emit('update:mapCodeList', mapCodeList)
    341         this.renderMap(areaName, this.mapDataList);
    342       }
    343     }
    344   }
    345 }
    346 
    347 </script>
    348 
    349 <style lang="scss" scoped>
    350 #map-container {
    351   height: 66.6%;
    352   position: relative;
    353 
    354   .echarts {
    355     height: 100%;
    356 
    357     #map {
    358       width: 100%;
    359       height: 100%;
    360     }
    361   }
    362 
    363   .back {
    364     position: absolute;
    365     top: 55px;
    366     left: 5px;
    367     z-index: 9;
    368     //color: #24CFF4;
    369     font-weight: bolder;
    370   }
    371 }
    372 
    373 </style>
    复制代码

    在上一套代码中,地图的边界上没有adcode(行政区划编码),这样就会导致在选取地区的时候只能根据汉字来进行匹配,导致不必要的错误,而最新抓去的行政区划里新增了adcode(行政区划)字段,这样就能根据该地区的行政区划来精准匹配.

       

    同时在上一个版本代码里,也对直辖市和特别行政区做了特殊处理,因为他们没有三级县级地图,而这次版本由于引入adcode,可以直接匹配到指定行政区划中,减少和很多不必要的判断操作,如下图

    做地图下钻本人也看过很多网上所说的,但是说的都不是很清楚,也没有专门对其进行代码的整理,这套代码是本人结合自身情况编写的,很多地方可能不是你想要的,需要对其进行取舍.

    做地图其实最重要的就是地图边界线,自从echarts不再更新维护地图之后,对于初识echarts地图的人来说不太好下手,希望本文可以帮助到你.

    如果有需要大家可以去以下地址下载源码学习。

    gitee源码地址:https://gitee.com/vijtor/vue-map-echarts

     

  • 相关阅读:
    C/C++实现:使用单向循环链表实现:编写相关函数来完成两个超长正整数的加法 某知名公司笔试题
    单元测试实战(二)Service 的测试
    CentOS更新镜像源
    SpringBoot SpringBoot 运维实用篇 2 配置高级 2.3 配置文件4级分类
    解决Word 表格不跨页的问题、方框带勾和叉的问题
    信息学奥赛研究1:竞赛时间表、学习规划
    IEEE投稿模板下载
    从业8年测试老鸟总结:软件测试员最核心的竞争力究竟是什么?
    基于SqlSugar的开发框架循序渐进介绍(12)-- 拆分页面模块内容为组件,实现分而治之的处理
    记一次查找投稿期刊的过程
  • 原文地址:https://www.cnblogs.com/weijiutao/p/15990813.html