cityfenbu.vue
<template> | |
<div > | |
<el-card class="seriesmap-box-card"> | |
<div slot="header" class="clearfix"> | |
<span>城市分布图 (点击可下钻到县)</span> | |
</div> | |
<div> | |
<div class="series-map" :style="{height:height,width:width}" ref="seriesMap"></div> | |
</div> | |
</el-card> | |
</div> | |
</template> | |
<script> | |
import resize from './resize.js'; | |
import echarts from 'echarts'; | |
import { getGeoJson } from './getGeoJson.js'; | |
import { getMapChartData } from './getMapChartData.js'; | |
export default { | |
name: 'cityfenbu', | |
mixins: [resize], | |
props: { | |
width: { | |
type: String, | |
default: '100%' | |
}, | |
height: { | |
type: String, | |
default: '608px' | |
} | |
}, | |
data() { | |
return { | |
geoJson: {}, | |
parentInfo: [ | |
{ | |
cityName: '全国', | |
code: 100000 | |
} | |
] | |
}; | |
}, | |
mounted() { | |
this.$nextTick(() => { | |
this.getMapJson(); | |
}); | |
}, | |
methods: { | |
//获取geoJson 地图 已封装好直接传citycode就行 | |
getMapJson() { | |
getGeoJson(this.parentInfo[this.parentInfo.length - 1].code).then(data => { | |
this.geoJson = data; | |
this.getMapData(); | |
}); | |
}, | |
//获取地图数据 模拟数据 数据格式:[{name:'武汉市',value:96},{name:'长沙市',value:75}] | |
// 必须要写成这种,而且name名字要和地图的geoJson名字一样,不然渲染不出来 | |
getMapData() { | |
getMapChartData(this.parentInfo[this.parentInfo.length - 1].code).then(res => { | |
const data = res.data; | |
this.initEchart(data); | |
}); | |
}, | |
initEchart(data) { | |
this.myChart = echarts.init(this.$refs.seriesMap); | |
//设置为 china 则显示南海诸岛 ,不需要可以去掉 | |
echarts.registerMap(this.parentInfo.length === 1 ? 'china' : 'map', this.geoJson); //注册 | |
const mapData = data.sort((a, b) => { | |
return b.value - a.value; | |
}); | |
let max = mapData[0].value; | |
let min = mapData[mapData.length - 1].value; | |
if (mapData.length === 1) { | |
min = 0; | |
} | |
this.myChart.setOption( | |
{ | |
tooltip: {}, | |
visualMap: { | |
min: min, | |
max: max, | |
left: '3%', | |
bottom: '1%', | |
calculable: true, | |
inRange: { | |
color: ['#24CFF4', '#2E98CA', '#1E62AC'] | |
}, | |
textStyle: { | |
color: '#24CFF4' | |
} | |
}, | |
series: [ | |
{ | |
name: '地图', | |
type: 'map', | |
map: this.parentInfo.length === 1 ? 'china' : 'map', | |
roam: true, //是否可缩放 | |
zoom: 1.22, //缩放比例 | |
// left: '', | |
// top: '15%', //可移动地图的位置 | |
data: mapData, | |
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 | |
} | |
} | |
} | |
] | |
}, | |
true | |
); | |
this.myChart.getZr().off('click'); | |
this.myChart.getZr().on('click', params => { | |
if (params.target) { | |
// 点的是地图 | |
const index = params.target.dataIndex; | |
if (index && data[index]) { | |
//如果当前是最后一级了,就直接return | |
if (this.parentInfo[this.parentInfo.length - 1].code == data[index].adcode) { | |
return; | |
} | |
//根据这个level判断是否下钻到县 | |
// if (data[index].level == 'city') { | |
// return | |
// } | |
this.parentInfo.push({ | |
cityName: data[index].name, | |
code: data[index].adcode | |
}); | |
this.getMapJson(); | |
this.$notify.info({ | |
title: '温馨提示', | |
message: '点击地图空白处即可返回上一级' | |
}); | |
} | |
} else { | |
//点的空白部分 | |
if (this.parentInfo.length === 1) { | |
return; | |
} | |
this.parentInfo.pop(); | |
this.getMapJson(); | |
} | |
}); | |
} | |
} | |
}; | |
</script> | |
<style lang="scss" scoped> | |
.seriesmap-box-card { | |
color:rgb(191, 203, 217); | |
background:#2d3a4b; | |
width: 100%; | |
height: 100%; | |
font-size: 14px; | |
.clearfix:before, | |
.clearfix:after { | |
display: table; | |
content: ""; | |
} | |
.clearfix:after { | |
clear: both | |
} | |
.series-map { | |
cursor:move; | |
} | |
} | |
</style> |
resize.js
import { debounce } from "./debounce.js"; | |
export default { | |
data() { | |
return { | |
myChart: null, | |
resizeHandler: null | |
}; | |
}, | |
computed: {}, | |
mounted() { | |
this.resizeHandler = debounce(() => { | |
if (this.myChart) { | |
this.myChart.resize(); | |
} | |
}, 100); | |
this.initResizeEvent(); | |
}, | |
methods: { | |
//监听resize | |
initResizeEvent() { | |
window.addEventListener("resize", this.resizeHandler); | |
}, | |
//移除resize | |
destroyResizeEvent() { | |
window.removeEventListener("resize", this.resizeHandler); | |
} | |
}, | |
beforeDestroy() { | |
this.destroyResizeEvent(); | |
if (!this.myChart) { | |
return; | |
} | |
this.myChart.dispose(); | |
this.myChart.off("click"); | |
this.myChart = null; | |
}, | |
activated() { | |
this.initResizeEvent(); | |
if (this.myChart) { | |
this.myChart.resize(); | |
} | |
}, | |
deactivated() { | |
this.destroyResizeEvent(); | |
}, | |
watch: {} | |
}; |
getGeoJson.js
/** | |
* 获取geoJson数据 通过高德获取 递归获取区县geoJson | |
* @param {string} adcode 行政区code | |
* @param {string} childAdcode 区县级行政区code | |
* @return {Array} | |
*/ | |
import remoteLoad from "./remoteLoad.js"; | |
const {AMapCDN, AMapUiCDN} = require("./cdn.js"); | |
export function getGeoJson(adcode, childAdcode = "") { | |
return new Promise((resolve, reject) => { | |
if (window.AMap && window.AMapUI) { | |
insideFun(adcode, childAdcode); | |
} else { | |
remoteLoad(AMapCDN).then(() => { | |
if (window.AMap) { | |
remoteLoad(AMapUiCDN).then(() => { | |
if (window.AMapUI) { | |
insideFun(adcode, childAdcode); | |
} else { | |
console.error("AMapUI获取失败"); | |
} | |
}); | |
} else { | |
console.error("AMap获取失败"); | |
} | |
}); | |
} | |
function insideFun(adcode, childAdcode) { | |
// eslint-disable-next-line | |
AMapUI.loadUI(["geo/DistrictExplorer"], DistrictExplorer => { | |
var districtExplorer = new DistrictExplorer(); | |
districtExplorer.loadAreaNode(adcode, function(error, areaNode) { | |
if (error) { | |
console.error(error); | |
reject(error); | |
return; | |
} | |
let Json = areaNode.getSubFeatures(); | |
if (Json.length === 0) { | |
let parent = areaNode._data.geoData.parent.properties.acroutes; | |
insideFun(parent[parent.length - 1], adcode); | |
return; | |
} | |
if (childAdcode) { | |
Json = Json.filter(item => { | |
return item.properties.adcode == childAdcode; | |
}); | |
} | |
let mapJson = { | |
features: Json | |
}; | |
resolve(mapJson); | |
}); | |
}); | |
} | |
}); | |
} |
getMapChartData.js
import { getGeoJson } from "./getGeoJson.js"; | |
/** 地图数据 | |
* @param {string} adcode 城市code | |
* @returns {Array} | |
*/ | |
export function getMapChartData(adcode) { | |
return new Promise((resolve, reject) => { | |
getGeoJson(adcode) | |
.then(res => { | |
const data = res.features; | |
const mapData = data.map(item => { | |
return { | |
name: item.properties.name, | |
value: parseFloat((Math.random() * 3000).toFixed(2)), | |
adcode: item.properties.adcode, | |
level: item.properties.level | |
}; | |
}); | |
resolve({ | |
code: 200, | |
data: mapData | |
}); | |
}) | |
.catch(error => { | |
reject(error); | |
}); | |
}); | |
} | |
/** 地图数据 散点 | |
* @param {string} adcode 城市code | |
* @returns {Array} | |
*/ | |
export function getPointChartData(adcode) { | |
return new Promise((resolve, reject) => { | |
getGeoJson(adcode) | |
.then(res => { | |
const data = res.features; | |
const mapData = data.map(item => { | |
return { | |
name: item.properties.name, | |
value: [ | |
item.properties.center[0], | |
item.properties.center[1], | |
parseFloat((Math.random(0.1, 1) * 1000).toFixed(2)) | |
], | |
adcode: item.properties.adcode, | |
level: item.properties.level | |
}; | |
}); | |
resolve({ | |
code: 200, | |
data: mapData | |
}); | |
}) | |
.catch(error => { | |
reject(error); | |
}); | |
}); | |
} | |
/** 地图数据 热力图 | |
* @param {string} adcode 城市code | |
* @returns {Array} | |
*/ | |
export function getHotMapChartData(adcode) { | |
const data = [ | |
{ | |
name: "地点1", | |
value: [114.412021, 30.481201, 1000] | |
}, | |
{ | |
name: "地点2", | |
value: [114.411266, 30.480921, 1000] | |
}, | |
{ | |
name: "地点3", | |
value: [114.411985, 30.481387, 1000] | |
}, | |
{ | |
name: "地点4", | |
value: [114.411159, 30.481917, 1000] | |
}, | |
{ | |
name: "地点5", | |
value: [114.412488, 30.481917, 1000] | |
}, | |
{ | |
name: "地点6", | |
value: [114.413638, 30.482726, 1000] | |
}, | |
{ | |
name: "地点7", | |
value: [114.412344, 30.48341, 1000] | |
}, | |
{ | |
name: "地点8", | |
value: [114.413494, 30.483939, parseInt(Math.random(0.6, 1) * 1000)] | |
}, | |
{ | |
name: "地点9", | |
value: [114.411877, 30.484469, parseInt(Math.random(0.6, 1) * 1000)] | |
}, | |
{ | |
name: "地点10", | |
value: [114.412308, 30.484531, parseInt(Math.random(0.6, 1) * 1000)] | |
}, | |
{ | |
name: "地点11", | |
value: [114.407853, 30.4845, parseInt(Math.random(0.6, 1) * 1000)] | |
}, | |
{ | |
name: "地点12", | |
value: [114.407242, 30.48285, parseInt(Math.random(0.1, 0.5) * 1000)] | |
}, | |
{ | |
name: "地点13", | |
value: [114.412021, 30.481201, parseInt(Math.random(0.1, 0.5) * 1000)] | |
}, | |
{ | |
name: "地点14", | |
value: [114.412021, 30.481201, parseInt(Math.random(0.1, 0.5) * 1000)] | |
}, | |
{ | |
name: "地点15", | |
value: [114.412021, 30.481201, parseInt(Math.random(0.1, 0.5) * 1000)] | |
}, | |
{ | |
name: "地点16", | |
value: [114.412021, 30.481201, parseInt(Math.random(0.1, 0.5) * 1000)] | |
}, | |
{ | |
name: "地点17", | |
value: [114.412021, 30.481201, parseInt(Math.random(0.1, 0.5) * 1000)] | |
}, | |
{ | |
name: "地点18", | |
value: [114.412021, 30.481201, parseInt(Math.random(0.1, 0.5) * 1000)] | |
}, | |
{ | |
name: "地点19", | |
value: [114.412021, 30.481201, parseInt(Math.random(0.1, 0.5) * 1000)] | |
}, | |
{ | |
name: "地点20", | |
value: [114.447306, 30.560407, parseInt(Math.random(0.1, 0.9) * 1000)] | |
}, | |
{ | |
name: "地点21", | |
value: [114.296104, 30.600017, parseInt(Math.random(0.1, 0.9) * 1000)] | |
}, | |
{ | |
name: "地点22", | |
value: [114.29402, 30.597406, parseInt(Math.random(0.1, 0.9) * 1000)] | |
}, | |
{ | |
name: "地点23", | |
value: [114.300487, 30.595106, parseInt(Math.random(0.1, 0.9) * 1000)] | |
}, | |
{ | |
name: "地点24", | |
value: [114.295026, 30.592805, parseInt(Math.random(0.1, 0.9) * 1000)] | |
}, | |
{ | |
name: "地点25", | |
value: [114.291648, 30.597282, 1000] | |
}, | |
{ | |
name: "地点26", | |
value: [114.287408, 30.599147, 1000] | |
}, | |
{ | |
name: "地点27", | |
value: [114.282378, 30.598649, 1000] | |
}, | |
{ | |
name: "地点28", | |
value: [114.286689, 30.600514, 1000] | |
} | |
]; | |
return new Promise((resolve, reject) => { | |
resolve({ | |
code: 200, | |
data: data | |
}); | |
}); | |
} |
remoteLoad.js
const remoteLoad = url => { | |
return new Promise((resolve, reject) => { | |
const existingScript = document.getElementById(url); | |
//如果script不存在 | |
if (!existingScript) { | |
const script = document.createElement("script"); | |
script.id = url; | |
script.src = url; | |
script.async = true; | |
document.body.appendChild(script); | |
script.onload = function() { | |
setTimeout(() => { | |
this.onerror = this.onload = null; | |
resolve(); | |
}, 500); | |
}; | |
script.onerror = function() { | |
this.onerror = this.onload = null; | |
reject("加载失败" + url); | |
}; | |
} else { | |
setTimeout(() => { | |
resolve(); | |
}, 500); | |
} | |
}); | |
}; | |
export default remoteLoad; |
cdn.js
const AMapCDN = | |
"https://webapi.amap.com/maps?v=1.3&key=73cddabc2173e0166a622f4483d3592a&plugin=AMap.DistrictSearch"; | |
const AMapUiCDN = "https://webapi.amap.com/ui/1.0/main.js"; | |
const VueCDN = "https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"; | |
const AxiosCDN = "https://cdn.bootcdn.net/ajax/libs/axios/0.21.0/axios.min.js"; | |
const VueRouterCDN = | |
"https://cdn.bootcdn.net/ajax/libs/vue-router/3.2.0/vue-router.min.js"; | |
const VuexCDN = "https://cdn.bootcdn.net/ajax/libs/vuex/3.5.1/vuex.min.js"; | |
const TinymceCDN = | |
"https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.3/tinymce.min.js"; | |
const html2canvasCDN = | |
"https://cdn.jsdelivr.net/npm/html2canvas@1.0.0-rc.7/dist/html2canvas.min.js"; | |
module.exports = { | |
AMapCDN, | |
AMapUiCDN, | |
VueCDN, | |
AxiosCDN, | |
VueRouterCDN, | |
VuexCDN, | |
TinymceCDN, | |
html2canvasCDN | |
}; |