vue+echarts+datav大屏数据展示及实现中国地图省市县下钻

随着前端技术的飞速发展,大数据时代的来临,我们在开发项目时越来越多的客户会要求我们做一个数据展示的大屏,可以直观的展示用户想要的数据,同时炫酷的界面也会深受客户的喜欢。

大屏展示其实就是一堆的图表能够让人一目了然地看到该系统下的一些基本数据信息的汇总,也会有一些实时数据刷新,信息预警之类的。笔者在之前也做过一些大屏类的数据展示,但是由于都是一些图表类的,觉得没什么可说的,加之数据也都牵扯到公司,所以没有沉淀下来什么。

最近有朋友要做一个大屏,问了自己一个问题,自己也刚好做了一个简单的大屏数据展示,趁此机会做一个小总结。

先看一下效果:

 

 由于数据牵扯到公司内部信息,所以将一些复杂的切换逻辑都去掉类,但保留了一些数据间但相互联动。

项目采用的是Vue+Echanrts+datav写的,结构目录如下:

 

 

 由于只是一个单一页面,数据处理也不是复杂,没有涉及到router和vuex,从结构目录上看就是一个很典型的vue-cli项目,在之前我也讲过关于vue-cli项目的一些操作和目录结构解释,这里就不做多做说明了,在文章最后会提供该项目的源码地址库。

大屏主要的炫酷效果本人引用的是datav组件,地址:http://datav.jiaminghi.com/,这简直就是数据可视化的一款神器,神奇之处我就不多说了,大家可以自己去它的网站上自行体会。它也提供了如何在vue 中使用该组件。

datav可以全局注入,也可以按需注入,本人省事就直接在main.js中进行了全局注入。

 

 所有的页面代码都放在了views文件目录下:

 

  其中index.vue文件为主文件入口,其他都是其子组件,组件名称以方位的形式命名,如centerForm.vue就是中间的表单控件。

本项目引入了中国地图并实现省市县下钻,最初采用的是阿里旗下的高德地图,后来因为种种原因改为了百度提供的Echarts来实现,但两种使用方法都保留了下来,大家可以根据自己的需求进行选择。

其中Echarts中国地图的代码如下:

  1 <template>
  2     <div id="china_map_box">
  3         <el-button type="primary" size="mini" 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} from "../api/get-json";
 13     import {cityProvincesMap} from '../config/cityProvincesMap'
 14     import {mapOption} from '../config/mapOption'
 15 
 16 
 17     export default {
 18         name: "china",
 19         components: {},
 20         data() {
 21             return {
 22                 chart: null, // 实例化echarts
 23                 provincesMap: cityProvincesMap.provincesMap, // 省拼音,用于查找对应json
 24                 provincesCode: cityProvincesMap.provincesCode, // 市行政区划,用于查找对应json
 25                 areaMap: cityProvincesMap.areaMap, // 省行政区划,用于数据的查找,按行政区划查数据
 26                 special: ["北京市", "天津市", "上海市", "重庆市", "香港", "澳门"],//直辖市和特别行政区-只有二级地图,没有三级地图
 27                 mapData: [], // 当前地图上的地区
 28                 option: {...mapOption.basicOption}, // map的相关配置
 29                 deepTree: [],// 点击地图时push,点返回时pop
 30                 areaName: '中国', // 当前地名
 31                 areaCode: '000000', // 当前行政区划
 32                 areaLevel: 'country', // 当前级别
 33             }
 34         },
 35         mounted() {
 36             this.$nextTick(() => {
 37                 this.initEcharts();
 38                 this.chart.on('click', this.echartsMapClick);
 39             });
 40         },
 41         methods: {
 42             // 初次加载绘制地图
 43             initEcharts() {
 44                 //地图容器
 45                 this.chart = this.$echarts.init(document.getElementById('map'));
 46                 if (this.areaCode === '000000') {
 47                     this.requestGetChinaJson();
 48                 } else {
 49                     this.requestGetProvinceJSON({areaName: this.areaName, areaCode: this.areaCode})
 50                 }
 51             },
 52             // 地图点击
 53             echartsMapClick(params) {
 54                 // console.log(params);
 55                 this.areaName = params.areaName;
 56                 if (params.name in this.provincesMap) {
 57                     this.areaCode = params.data.areaCode;
 58                     this.areaLevel = params.data.areaLevel;
 59                     //如果点击的是34个省、市、自治区,绘制选中地区的二级地图
 60                     this.requestGetProvinceJSON(params.data);
 61                 } else if (params.seriesName in this.provincesMap) {
 62                     //如果是【直辖市/特别行政区】只有二级下钻
 63                     if (this.special.indexOf(params.seriesName) >= 0) {
 64                         return;
 65                     } else {
 66                         this.areaCode = this.areaMap[params.name];
 67                         this.areaLevel = params.data.areaLevel;
 68                         //显示县级地图
 69                         this.requestGetCityJSON(params.data)
 70                     }
 71                 } else {
 72                     return;
 73                 }
 74                 this.$emit('map-change', params.data);
 75             },
 76             //绘制全国地图
 77             requestGetChinaJson() {
 78                 getChinaJson().then(res => {
 79                     let arr = [];
 80                     for (let i = 0; i < res.features.length; i++) {
 81                         let obj = {
 82                             name: res.features[i].properties.name,
 83                             areaName: res.features[i].properties.name,
 84                             areaCode: res.features[i].id,
 85                             areaLevel: 'province',
 86                             value: Math.round(Math.random()),
 87                         };
 88                         arr.push(obj)
 89                     }
 90                     this.mapData = arr;
 91                     this.deepTree.push({
 92                         mapData: arr,
 93                         params: {name: 'china', areaName: 'china', areaLevel: 'country', areaCode: '000000'}
 94                     });
 95                     //注册地图
 96                     this.$echarts.registerMap('china', res);
 97                     //绘制地图
 98                     this.renderMap('china', arr);
 99                 });
100             },
101             // 加载省级地图
102             requestGetProvinceJSON(params) {
103                 getProvinceJSON(params.areaCode).then(res => {
104                     this.$echarts.registerMap(params.areaName, res);
105                     let arr = [];
106                     for (let i = 0; i < res.features.length; i++) {
107                         let obj = {
108                             name: res.features[i].properties.name,
109                             areaName: res.features[i].properties.name,
110                             areaCode: res.features[i].id,
111                             areaLevel: 'city',
112                             value: Math.round(Math.random()),
113                         };
114                         arr.push(obj)
115                     }
116                     this.mapData = arr;
117                     this.deepTree.push({
118                         mapData: arr,
119                         params: params,
120                     });
121                     this.renderMap(params.areaName, arr);
122                 });
123             },
124             // 加载市级地图
125             requestGetCityJSON(params) {
126                 this.areaLevel = params.areaLevel;
127                 getCityJSON(params.areaCode).then(res => {
128                     this.$echarts.registerMap(params.areaName, res);
129                     let arr = [];
130                     for (let i = 0; i < res.features.length; i++) {
131                         let obj = {
132                             name: res.features[i].properties.name,
133                             areaName: res.features[i].properties.areaName,
134                             areaCode: res.features[i].id,
135                             areaLevel: 'districts',
136                             value: Math.round(Math.random()),
137                         };
138                         arr.push(obj)
139                     }
140                     this.mapData = arr;
141                     this.deepTree.push({mapData: arr, params: params});
142                     this.renderMap(params.areaName, arr);
143                 })
144             },
145             renderMap(map, data) {
146                 this.option.series = [
147                     {
148                         name: map,
149                         mapType: map,
150                         ...mapOption.seriesOption,
151                         data: data
152                     }
153                 ];
154                 //渲染地图
155                 this.chart.setOption(this.option);
156             },
157             // 返回
158             back() {
159                 // console.log(this.deepTree);
160                 if (this.deepTree.length > 1) {
161                     this.deepTree.pop();
162                     let areaName = this.deepTree[this.deepTree.length - 1].params.areaName;
163                     let mapData = this.deepTree[this.deepTree.length - 1].mapData;
164                     this.$emit('back-change', this.deepTree[this.deepTree.length - 1].params);
165                     this.renderMap(areaName, mapData);
166                 }
167             }
168         }
169     }
170 
171 </script>
172 
173 <style lang="scss" scoped>
174     #china_map_box {
175         display: flex;
176         width: 100%;
177         height: 100%;
178         position: relative;
179         .echarts {
180             width: 0;
181             flex: 1;
182             background-size: 100% 100%;
183             #map {
184                 height: 100%;
185             }
186         }
187         .back {
188             position: absolute;
189             top: .8rem;
190             right: .5rem;
191             z-index: 999;
192             padding-left: .12rem;
193             padding-right: .12rem;
194 
195         }
196     }
197 
198 </style>

在调用省市地图时本人采用的是将地图信息的json存放在了本地,这是由于本人的项目中很多地市的行政区划很多需要变动,这也是放弃高德地图的原因之一。json文件放在了public文件目录下,如下图:

 

 里面有一些自己没用到的json数据本人进行了删除,关于中国详细的json数据大家可以去https://datav.aliyun.com/tools/atlas/#&lat=30.332329214580188&lng=106.72278672066881&zoom=3.5下载,内容由高德开放平台提供。

全国行政区划编码地址 http://www.mca.gov.cn/article/sj/xzqh/2020/,内容由国家统计局提供

高德地图chinaGaode.vue代码如下:

  1 <template>
  2     <div id="china_map_box">
  3         <el-button type="primary" size="mini" class="back" @click="back">返回</el-button>
  4         <div class="map" >
  5             <map-range @change="search"></map-range>
  6         </div>
  7         <div class="echarts">
  8             <div id="map"></div>
  9         </div>
 10     </div>
 11 </template>
 12 
 13 <script>
 14     import mapRange from "./mapRange";
 15 
 16     export default {
 17         name: "chinaGaode",
 18         components: {
 19             mapRange
 20         },
 21         data() {
 22             return {
 23                 provinceSelect: null,
 24                 citySelect: null,
 25                 districtSelect: null,
 26                 areaName: '中国',
 27                 geoJsonData: '',
 28                 echartsMap: null,
 29                 map: null,
 30                 district: null,
 31                 polygons: [],
 32                 areaCode: 100000,
 33                 opts: {},
 34                 areaData: {},
 35                 mapData: [],
 36                 deepTree:[],
 37             }
 38         },
 39         mounted() {
 40             this.provinceSelect = document.getElementById('province');
 41             this.citySelect = document.getElementById('city');
 42             this.districtSelect = document.getElementById('district');
 43             this.deepTree = [{mapData: this.mapData,code: 100000}];
 44             this.echartsMap = this.$echarts.init(document.getElementById('map'));
 45             this.echartsMap.on('click', this.echartsMapClick);
 46             this.map = new AMap.Map('container', {
 47                 resizeEnable: true,
 48                 center: [116.30946, 39.937629],
 49                 zoom: 3
 50             });
 51             this.opts = {
 52                 subdistrict: 1,   //返回下一级行政区
 53                 showbiz: false  //最后一级返回街道信息
 54             };
 55             this.district = new AMap.DistrictSearch(this.opts);//注意:需要使用插件同步下发功能才能这样直接使用
 56             this.district.search('中国', (status, result) => {
 57                 if (status == 'complete') {
 58                     this.getData(result.districtList[0], '', 100000);
 59                 }
 60             });
 61         },
 62         methods: {
 63             //地图点击事件
 64             echartsMapClick(params) {
 65                 if (params.data.level == 'street') return;
 66                 //清除地图上所有覆盖物
 67                 for (var i = 0, l = this.polygons.length; i < l; i++) {
 68                     this.polygons[i].setMap(null);
 69                 }
 70                 this.areaName = params.data.name;
 71                 this.areaCode = params.data.areaCode;
 72                 this.district.setLevel(params.data.level); //行政区级别
 73                 this.district.setExtensions('all');
 74                 //行政区查询
 75                 //按照adcode进行查询可以保证数据返回的唯一性
 76                 this.district.search(this.areaCode, (status, result) => {
 77                     if (status === 'complete') {
 78                         this.deepTree.push({mapData: this.mapData,code: params.data.areaCode});
 79                         this.getData(result.districtList[0], params.data.level, this.areaCode);
 80                     }
 81                 });
 82                 this.$emit('map-change', params.data);
 83             },
 84             loadMapData(areaCode) {
 85                 AMapUI.loadUI(['geo/DistrictExplorer'], DistrictExplorer => {
 86                     //创建一个实例
 87                     var districtExplorer = window.districtExplorer = new DistrictExplorer({
 88                         eventSupport: true, //打开事件支持
 89                         map: this.map
 90                     });
 91                     districtExplorer.loadAreaNode(areaCode, (error, areaNode) => {
 92                         if (error) {
 93                             console.error(error);
 94                             return;
 95                         }
 96                         let mapJson = {};
 97                         mapJson.type = "FeatureCollection";
 98                         mapJson.features = areaNode.getSubFeatures();
 99                         this.loadMap(this.areaName, mapJson);
100                         this.geoJsonData = mapJson;
101                     });
102                 });
103             },
104             loadMap(mapName, data) {
105                 if (data) {
106                     this.$echarts.registerMap(mapName, data);
107                     var option = {
108 
109                         visualMap: {
110                             type: 'piecewise',
111                             pieces: [
112                                 {max: 1, label: '审核完成', color: '#2c9a42'},
113                                 {min: -1, max: 1, label: '未完成', color: '#d08a00'},
114                                 // {min: 60, label: '危险', color: '#c23c33'},
115                             ],
116                             color: '#fff',
117                             textStyle: {
118                                 color: '#fff',
119                             },
120                             visibility: 'off',
121                             top:50,
122                             left:30,
123                         },
124                         series: [{
125                             name: '数据名称',
126                             type: 'map',
127                             roam: false,
128                             mapType: mapName,
129                             selectedMode: 'single',
130                             showLegendSymbol: false,
131                             visibility: 'off',
132                             itemStyle: {
133                                 normal: {
134                                     color: '#ccc',
135                                     areaColor: '#fff',
136                                     borderColor: '#fff',
137                                     borderWidth: 0.5,
138                                     label: {
139                                         show: true,
140                                         textStyle: {
141                                             color: "rgb(249, 249, 249)",
142                                             fontSize: '1rem'
143                                         }
144                                     }
145                                 },
146                                 emphasis: {
147                                     areaColor: false,
148                                     borderColor: '#fff',
149                                     areaStyle: {
150                                         color: '#fff'
151                                     },
152                                     label: {
153                                         show: true,
154                                         textStyle: {
155                                             color: "rgb(249, 249, 249)"
156                                         }
157                                     }
158                                 }
159                             },
160                             data: this.mapData,
161                         }]
162                     };
163                     this.echartsMap.setOption(option);
164                 }
165             },
166             getData(data, level, adcode) {
167                 var bounds = data.boundaries;
168                 if (bounds) {
169                     for (var i = 0, l = bounds.length; i < l; i++) {
170                         var polygon = new AMap.Polygon({
171                             map: this.map,
172                             strokeWeight: 1,
173                             strokeColor: '#0091ea',
174                             fillColor: '#80d8ff',
175                             fillOpacity: 0.2,
176                             path: bounds[i]
177                         });
178                         this.polygons.push(polygon);
179                     }
180                     this.map.setFitView();//地图自适应
181                 }
182 
183                 //清空下一级别的下拉列表
184                 if (level === 'province') {
185                     this.citySelect.innerHTML = '';
186                     this.districtSelect.innerHTML = '';
187                 } else if (level === 'city') {
188                     this.districtSelect.innerHTML = '';
189                 }
190                 var subList = data.districtList;
191                 if (subList) {
192                     let optionName = '--请选择--';
193                     var contentSub = new Option(optionName);
194                     var curlevel = subList[0].level;
195                     if (curlevel === 'street') {
196                         let mapJsonList = this.geoJsonData.features;
197                         let mapJson = {};
198                         for (let i in mapJsonList) {
199                             if (mapJsonList[i].properties.name == this.areaName) {
200                                 mapJson.type = "FeatureCollection";
201                                 mapJson.features = [].concat(mapJsonList[i]);
202                             }
203                         }
204                         this.mapData = [];
205                         this.mapData.push({name: this.areaName, value: 0, level: curlevel});
206                         this.loadMap(this.areaName, mapJson);
207                         return;
208                     }
209 
210                     var curList = document.querySelector('#' + curlevel);
211                     curList.add(contentSub);
212                     this.mapData = [];
213                     for (var i = 0, l = subList.length; i < l; i++) {
214                         var name = subList[i].name;
215                         var areaCode = subList[i].adcode;
216                         this.mapData.push({
217                             name: name,
218                             value: Math.round(Math.random()),
219                             areaCode: areaCode,
220                             level: curlevel
221                         });
222                         var levelSub = subList[i].level;
223                         contentSub = new Option(name);
224                         contentSub.setAttribute("value", levelSub);
225                         contentSub.center = subList[i].center;
226                         contentSub.adcode = subList[i].adcode;
227                         curList.add(contentSub);
228                     }
229                     this.loadMapData(adcode);
230                     this.areaData[curlevel] = curList;
231                 }
232 
233             },
234             search(area) {
235                 let obj = this.areaData[area];
236                 //清除地图上所有覆盖物
237                 for (var i = 0, l = this.polygons.length; i < l; i++) {
238                     this.polygons[i].setMap(null);
239                 }
240                 var option = obj[obj.options.selectedIndex];
241 
242                 var keyword = option.text; //关键字
243                 var adcode = option.adcode;
244                 this.areaName = keyword;
245                 this.areaCode = adcode;
246                 this.district.setLevel(option.value); //行政区级别
247                 this.district.setExtensions('all');
248                 //行政区查询
249                 //按照adcode进行查询可以保证数据返回的唯一性
250                 this.district.search(adcode, (status, result) => {
251                     if (status === 'complete') {
252                         this.deepTree.push({mapData: this.mapData,code:adcode});
253                         this.getData(result.districtList[0], obj.id, adcode);
254                     }
255                 });
256                 var params = {
257                     areaCode: adcode,
258                     level: area,
259                     name: keyword,
260                     value: '',
261                 };
262                 this.$emit('map-change', params);
263             },
264             back() {
265                 // console.log(this.deepTree)
266                 if (this.deepTree.length > 1) {
267                     this.mapData = this.deepTree[this.deepTree.length - 1].mapData;
268                     this.deepTree.pop();
269                     // console.log(this.deepTree[this.deepTree.length - 1], 'back');
270                     this.loadMapData(this.deepTree[this.deepTree.length - 1].code)
271                 }
272             }
273         }
274     }
275 </script>
276 
277 <style lang="scss" scoped>
278     #china_map_box {
279         display: flex;
280         width: 100%;
281         height: 100%;
282         position: relative;
283         .echarts {
284             width: 0;
285             flex: 1;
286             background-size: 100% 100%;
287             #map {
288                 height: 100%;
289             }
290         }
291         .back {
292             position: absolute;
293             top: .8rem;
294             right: .5rem;
295             z-index: 999;
296         }
297 
298     }
299 
300 </style>

 

在网上有很多下伙伴都在查找如何使用中国地图并实现下钻,在实际使用地图时其实并不难,以上是本人提供的一些解决方案和代码提供。

由于代码是从本人的一个项目中剥离而来,代码的质量可能欠佳,有些逻辑处理和傅子组件间的数据联动也都有所减少,但并不影响该项目demo的使用,如果有需要大家可以去以下地址下载源码学习。

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

另一优化版本地址: https://www.cnblogs.com/weijiutao/p/15990813.html

 

posted @ 2020-11-16 10:24  丰寸  阅读(11930)  评论(9编辑  收藏  举报