第25章:地图下钻交互(点击省份,切换到对应省份城市地图)

第25章:地图下钻交互(点击省份,切换到对应省份城市地图)

业务场景:全国地图点击省份,自动加载该省的市级 GeoJSON 地图,实现地图下钻 ;支持返回全国地图按钮,复用我们封装好的useEcharts

前置准备:

  1. 已经有 src/assets/map/china.json
  2. 各个省份的 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>

核心知识点

  1. echarts.registerMap:可以多次注册不同地图,名称不能重复
  2. 下钻逻辑 :全国地图点击省份,拿到adcode编码,远程拉取该省 geojson,注册后渲染省份地图
  3. 增加返回按钮,切换回全国地图
  4. 注意:fetch加载远程 geojson,离线 / 内网环境会失效,内网项目需要提前下载全部省份 json 放到本地 assets 目录
  5. 复用我们封装好的useEcharts,resize、实例销毁逻辑自动生效

🧪 测试清单

  1. 保存页面,刷新浏览器
  2. 默认加载全国地图,hover、缩放拖拽正常
  3. 点击省份(如广东),页面自动切换为广东省市级地图,城市随机生成数据
  4. 【返回全国地图】按钮出现,点击回到全国地图
  5. 窗口缩放,地图自适应
  6. 切换菜单离开页面,图表实例正常销毁

⚠️ 注意:如果项目是内网部署,不能访问外网,需要把省份 geojson 下载到本地 assets,改成 import 引入。

问题定位

请求 URL 变成了 undefined_full.json,说明 params.adcode 是 undefined

原因:我们传给地图的 mapData 里只有 namevalue,没有 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>

✅ 测试步骤

  1. 替换页面代码,刷新
  2. 全国地图正常渲染
  3. 点击任意省份(比如广东),控制台打印出省份编码,正常加载该省城市地图
  4. 点击【返回全国地图】回到全国
  5. 切换路由,实例销毁无内存泄漏
相关推荐
daols881 小时前
vue 甘特图组件 vxe-gantt 自动计算工期显示关键路径
vue.js·vxe-gantt
—Qeyser2 小时前
Element Plus 安装与配置指南 (Vue 3 + Vite + TS)
vue.js
雪芽蓝域zzs2 小时前
第19章:ECharts 组合图(折线 + 柱状混合图,双 Y 轴)
vue.js·echars
雪芽蓝域zzs2 小时前
第17章:ECharts 饼图自定义样式(扇区颜色、轮廓、标签配置 + 空状态兼容)
vue.js·echars
雪芽蓝域zzs2 小时前
第15章:ECharts 图表事件交互(若依 Vue3)
vue.js·echars
honkun63 小时前
vue甘特图组件 vxe-gantt 实现拖拽任务自动更新紧前紧后依赖线关联任务的日期
vue.js·vxe-gantt
雪芽蓝域zzs3 小时前
第20章:ECharts 图表主题切换 + 自定义颜色渐变
vue.js·echars
mldong4 小时前
给流程设计器加国际化,我没引 vue-i18n
前端·javascript·vue.js
边境悍匪4 小时前
蜗牛学苑 Java 智能体学习 Day46|贯穿项目 2 思维导图复盘
java·开发语言·vue.js·学习·spring