vue+echarts視覺化大屏,全國地圖下鑽,頁面自適應

豐寸發表於2022-03-16

之前寫過一篇關於資料大屏及地圖下鑽的文章 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地圖的人來說不太好下手,希望本文可以幫助到你.

如果有需要大家可以去以下地址下載原始碼學習,也歡迎star。

gitee原始碼地址:https://gitee.com/vijtor/vue-map-echarts

 

相關文章