第25章:地图下钻交互(点击省份,切换到对应省份城市地图)
业务场景:全国地图点击省份,自动加载该省的市级 GeoJSON 地图,实现地图下钻 ;支持返回全国地图按钮,复用我们封装好的
useEcharts。前置准备:
- 已经有
src/assets/map/china.json- 各个省份的 geojson,我们这里用动态远程加载方案,不用本地一次性下载全部省份 json,减少项目体积。
完整页面代码
<template>
<div class="chart-container">
<el-alert title="地图下钻:全国→省份城市地图,点击返回全国" type="info" style="margin-bottom:10px"/>
<el-button v-if="currentMapName !== 'china'" type="primary" @click="backToChina">返回全国地图</el-button>
<div
ref="chartRef"
style="width:100%;height:480px;border:1px solid #eee;margin-top:10px"
></div>
</div>
</template>
<script setup>
import * as echarts from 'echarts'
import { ref, onMounted } from 'vue'
import { useEcharts } from '@/composables/echartHelper'
import chinaJson from '@/assets/map/china.json'
const { chartRef, setChartOption, onChartEvent } = useEcharts()
// 当前加载的地图名称
const currentMapName = ref('china')
// 模拟各省销售数据
const mapData = [
{ name: '北京', value: 132 },
{ name: '上海', value: 215 },
{ name: '广东', value: 280 },
{ name: '山东', value: 178 },
{ name: '江苏', value: 196 },
{ name: '浙江', value: 162 },
{ name: '四川', value: 95 },
{ name: '河南', value: 88 }
]
// 加载地图并渲染
async function renderMap(mapName, geoJson, dataList) {
// 注册地图
echarts.registerMap(mapName, geoJson)
const option = {
title: {
text: mapName === 'china' ? '全国区域销售额分布' : `${mapName} 城市销售额`,
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{b}<br/>销售额:{c}万'
},
visualMap: {
min: 0,
max: 300,
left: 'left',
bottom: '5%',
text: ['高','低'],
calculable: true,
inRange: {
color: ['#e0f7fa','#b2ebf2','#80deea','#4dd0e1','#26c6da','#00bcd4','#00acc1']
}
},
series: [
{
name: '销售额',
type: 'map',
map: mapName,
roam: true,
zoom: 1.2,
label: {
show: true,
fontSize: 10
},
data: dataList
}
]
}
await setChartOption(option)
}
// 返回全国地图
async function backToChina() {
currentMapName.value = 'china'
renderMap('china', chinaJson, mapData)
}
// 点击地图区域,下钻
async function mapClickHandle(params) {
// 只有全国地图才允许下钻
if(currentMapName.value !== 'china') return
const provinceName = params.name
if(!provinceName) return
// 阿里云datav公开geojson接口,获取省份边界数据
// 注意:这个是公开静态资源,内网环境会无法访问
const res = await fetch(`https://geo.datav.aliyun.com/areas_v3/bound/${params.adcode}_full.json`)
const provinceGeo = await res.json()
// 模拟省份下的城市数据
const cityData = provinceGeo.features.map(item=>{
return {
name: item.properties.name,
value: Math.floor(Math.random()*200)
}
})
currentMapName.value = provinceName
renderMap(provinceName, provinceGeo, cityData)
}
onMounted(async () => {
// 初始化全国地图
renderMap('china', chinaJson, mapData)
// 绑定点击事件
onChartEvent('click', mapClickHandle)
})
</script>
核心知识点
- echarts.registerMap:可以多次注册不同地图,名称不能重复
- 下钻逻辑 :全国地图点击省份,拿到
adcode编码,远程拉取该省 geojson,注册后渲染省份地图 - 增加返回按钮,切换回全国地图
- 注意:
fetch加载远程 geojson,离线 / 内网环境会失效,内网项目需要提前下载全部省份 json 放到本地 assets 目录 - 复用我们封装好的
useEcharts,resize、实例销毁逻辑自动生效
🧪 测试清单
- 保存页面,刷新浏览器
- 默认加载全国地图,hover、缩放拖拽正常
- 点击省份(如广东),页面自动切换为广东省市级地图,城市随机生成数据
- 【返回全国地图】按钮出现,点击回到全国地图
- 窗口缩放,地图自适应
- 切换菜单离开页面,图表实例正常销毁
⚠️ 注意:如果项目是内网部署,不能访问外网,需要把省份 geojson 下载到本地 assets,改成 import 引入。

问题定位
请求 URL 变成了 undefined_full.json,说明 params.adcode 是 undefined 。
原因:我们传给地图的 mapData 里只有 name 和 value,没有 adcode 字段,所以点击时拿不到省份编码。
修复方案:从 chinaJson 里根据省份名称查找 adcode
修改 mapClickHandle 函数:
// 点击地图区域,下钻
async function mapClickHandle(params) {
// 只有全国地图才允许下钻
if(currentMapName.value !== 'china') return
const provinceName = params.name
if(!provinceName) return
// 从chinaJson里根据省份名称,找到对应的adcode
const provinceInfo = chinaJson.features.find(item => {
// 匹配名称(去掉"市"、"省"后缀,避免匹配不上)
return item.properties.name === provinceName ||
item.properties.name.replace(/省|市|自治区|壮族|回族|维吾尔/g, '') === provinceName.replace(/省|市|自治区|壮族|回族|维吾尔/g, '')
})
if(!provinceInfo) {
alert('该省份暂不支持下钻')
return
}
const adcode = provinceInfo.properties.adcode
console.log('省份编码:', adcode)
// 拉取该省份的geojson
const res = await fetch(`https://geo.datav.aliyun.com/areas_v3/bound/${adcode}_full.json`)
const provinceGeo = await res.json()
// 模拟省份下的城市数据
const cityData = provinceGeo.features.map(item=>{
return {
name: item.properties.name,
value: Math.floor(Math.random()*200)
}
})
currentMapName.value = provinceName
renderMap(provinceName, provinceGeo, cityData)
}
完整修正后的页面 script 部分
<script setup>
import * as echarts from 'echarts'
import { ref, onMounted } from 'vue'
import { useEcharts } from '@/composables/echartHelper'
import chinaJson from '@/assets/map/china.json'
const { chartRef, setChartOption, onChartEvent } = useEcharts()
const currentMapName = ref('china')
// 模拟各省销售数据
const mapData = [
{ name: '北京', value: 132 },
{ name: '上海', value: 215 },
{ name: '广东', value: 280 },
{ name: '山东', value: 178 },
{ name: '江苏', value: 196 },
{ name: '浙江', value: 162 },
{ name: '四川', value: 95 },
{ name: '河南', value: 88 }
]
// 加载地图并渲染
async function renderMap(mapName, geoJson, dataList) {
echarts.registerMap(mapName, geoJson)
const option = {
title: {
text: mapName === 'china' ? '全国区域销售额分布' : `${mapName} 城市销售额`,
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{b}<br/>销售额:{c}万'
},
visualMap: {
min: 0,
max: 300,
left: 'left',
bottom: '5%',
text: ['高','低'],
calculable: true,
inRange: {
color: ['#e0f7fa','#b2ebf2','#80deea','#4dd0e1','#26c6da','#00bcd4','#00acc1']
}
},
series: [
{
name: '销售额',
type: 'map',
map: mapName,
roam: true,
zoom: 1.2,
label: {
show: true,
fontSize: 10
},
data: dataList
}
]
}
await setChartOption(option)
}
// 返回全国地图
async function backToChina() {
currentMapName.value = 'china'
renderMap('china', chinaJson, mapData)
}
// 点击地图区域,下钻
async function mapClickHandle(params) {
if(currentMapName.value !== 'china') return
const provinceName = params.name
if(!provinceName) return
// 从chinaJson里根据省份名称,找到对应的adcode
const provinceInfo = chinaJson.features.find(item => {
return item.properties.name === provinceName
})
if(!provinceInfo) {
alert('该省份暂不支持下钻')
return
}
const adcode = provinceInfo.properties.adcode
console.log('省份编码:', adcode)
const res = await fetch(`https://geo.datav.aliyun.com/areas_v3/bound/${adcode}_full.json`)
const provinceGeo = await res.json()
const cityData = provinceGeo.features.map(item=>{
return {
name: item.properties.name,
value: Math.floor(Math.random()*200)
}
})
currentMapName.value = provinceName
renderMap(provinceName, provinceGeo, cityData)
}
onMounted(async () => {
renderMap('china', chinaJson, mapData)
onChartEvent('click', mapClickHandle)
})
</script>
✅ 测试步骤
- 替换页面代码,刷新
- 全国地图正常渲染
- 点击任意省份(比如广东),控制台打印出省份编码,正常加载该省城市地图
- 点击【返回全国地图】回到全国
- 切换路由,实例销毁无内存泄漏

