在许多业务场景中,我们需要展示与地理位置相关的数据。本文将详细介绍如何在 Vue3 中结合 ECharts 库来实现一个可交互的中国地图,并支持用户通过点击地图上的省份来查看更详细的地区数据。
实现效果
最终的效果是:用户可以点击中国地图上的任意省份,然后页面会展示该省份的详细信息;同时提供一个“返回”按钮,用户可以点击它回到初始的中国地图视图。
技术栈
Vue.js 3:用于构建前端应用程序。ECharts:用于数据可视化。Fetch:用于请求本地JSON文件。步骤详解
1. 模板定义
在 Vue3 组件的模板中定义一个用于显示地图的容器和一个返回按钮。
<template>  <div style="position: relative; width: 100%; height: 100%">    <div id="map" ref="mapRef" style="width: 100%; height: 400px" />    <span v-if="isBack" class="go-back" @click="gotoParent">返回</span>  </div></template> 2. 请求省份 JSON 数据
使用Fetch加载中国省份的地图数据(GeoJSON 格式)。
async function fetchMapJson(adcode) {  // 使用 fetch 加载 JSON 文件  const response = await fetch("/json/map/" + adcode + ".json");  mapJsonData.value = await response.json();} 3. 注册地图数据
使用 ECharts 的 registerMap 方法来注册地图数据。
// 注册地图数据function handleRegisterMap(regionName) {  mapChart.value = echarts.init(mapRef.value); // 初始化地图  echarts.registerMap(regionName, mapJsonData.value);  const option = {    geo: {      map: regionName,      roam: true,      label: {        normal: { show: false },        emphasis: { show: true },      },      itemStyle: {        normal: {          areaColor: "#2B91B7",          borderColor: "#111",        },        emphasis: {          areaColor: "#0489d6",        },      },    },    series: [      {        type: "scatter",        coordinateSystem: "geo",        map: regionName,        data: [], // 这里可以填充省份数据        label: {          normal: {            show: false,          },          emphasis: {            show: true,            textStyle: {              color: "#fff",            },          },        },        itemStyle: {          normal: {            areaColor: "#2B91B7",            borderColor: "#aaa",          },          emphasis: {            areaColor: "#0489d6",          },        },      },    ],  };  mapChart.value.setOption(option);} 4. 添加省份点击事件
// 添加省份点击事件let returnArray = ref([]);const handleClickMap = async (params) => {  if (params.componentType === "geo" && params.name !== "") {    // 查找地理编码    let adcode = null;    const features = mapJsonData.value.features;    const filterArray = features.filter((item) => {      return item.properties.name === params.name;    });    if (filterArray && filterArray.length > 0) {      adcode = filterArray[0].id;    }    const regionName = params.name;    if (adcode) {      isBack.value = true;      await fetchMapJson(adcode);      returnArray.value.push({        adcode: adcode,        regionName: regionName,        mapJsonData: JSON.stringify(mapJsonData.value),      });      // 清除当前地图选项      mapChart.value.clear();      // 刷新地图实例      mapChart.value.resize();      handleRegisterMap(regionName);    }  } else if (params.componentType === "series") {    // 点击的数据    const data = params.data;    console.log("data", data);  }}; 5. 初始化中国地图
设置 ECharts 实例,并配置地图的基本选项。
let chinaJsonData = ref({}); // 中国地图数据onMounted(async () => {  // 使用 fetch 加载 JSON 文件  await fetchMapJson(100000);  chinaJsonData.value = mapJsonData.value; // 先保留中国地图json  handleRegisterMap("china");  isBack.value = false;  returnArray.value.push({    adcode: 100000,    regionName: "china",    mapJsonData: JSON.stringify(chinaJsonData.value),  });  mapChart.value.on("click", handleClickMap);  window.addEventListener("resize", () => {    mapChart.value.resize();  });}); 6. 返回上级
实现返回按钮的功能,当点击返回时可以返回它的上一级,直至中国地图。
// 返回上级async function gotoParent() {  if (isBack.value) {    if (returnArray.value.length > 0) {      returnArray.value.pop();      let backMap = returnArray.value.slice(-1);      if (backMap && backMap.length > 0) {        mapJsonData.value = JSON.parse(backMap[0].mapJsonData);        // 清除当前地图选项        mapChart.value.clear();        // 刷新地图实例        mapChart.value.resize();        handleRegisterMap(backMap[0].regionName);        if (backMap[0].adcode == 100000) {          isBack.value = false;          returnArray.value = [            {              adcode: 100000,              regionName: "china",              mapJsonData: JSON.stringify(chinaJsonData.value),            },          ];        }      }    } else {      isBack.value = false;    }  }} 完整代码
为了便于理解,以下是完整的组件代码示例:
<template>  <div style="position: relative; width: 100%; height: 100%">    <div id="map" ref="mapRef" style="width: 100%; height: 400px" />    <span v-if="isBack" class="go-back" @click="gotoParent">返回</span>  </div></template><script setup>import * as echarts from "echarts";import { ref, onMounted, onBeforeUnmount } from "vue";const mapRef = ref(null);let mapJsonData = ref({});let mapChart = ref(null);let isBack = ref(false); // 加标记,是否返回上一级// 根据地理编号请求json数据async function fetchMapJson(adcode) {  // 使用 fetch 加载 JSON 文件  const response = await fetch("/json/map/" + adcode + ".json");  mapJsonData.value = await response.json();}// 注册地图数据function handleRegisterMap(regionName) {  mapChart.value = echarts.init(mapRef.value); // 初始化地图  echarts.registerMap(regionName, mapJsonData.value);  const option = {    geo: {      map: regionName,      roam: true,      label: {        normal: { show: false },        emphasis: { show: true },      },      itemStyle: {        normal: {          areaColor: "#2B91B7",          borderColor: "#111",        },        emphasis: {          areaColor: "#0489d6",        },      },    },    series: [      {        type: "scatter",        coordinateSystem: "geo",        map: regionName,        data: [], // 这里可以填充省份数据        label: {          normal: {            show: false,          },          emphasis: {            show: true,            textStyle: {              color: "#fff",            },          },        },        itemStyle: {          normal: {            areaColor: "#2B91B7",            borderColor: "#aaa",          },          emphasis: {            areaColor: "#0489d6",          },        },      },    ],  };  mapChart.value.setOption(option);}// 添加省份点击事件let returnArray = ref([]);const handleClickMap = async (params) => {  if (params.componentType === "geo" && params.name !== "") {    // 查找地理编码    let adcode = null;    const features = mapJsonData.value.features;    const filterArray = features.filter((item) => {      return item.properties.name === params.name;    });    if (filterArray && filterArray.length > 0) {      adcode = filterArray[0].id;    }    const regionName = params.name;    if (adcode) {      isBack.value = true;      await fetchMapJson(adcode);      returnArray.value.push({        adcode: adcode,        regionName: regionName,        mapJsonData: JSON.stringify(mapJsonData.value),      });      // 清除当前地图选项      mapChart.value.clear();      // 刷新地图实例      mapChart.value.resize();      handleRegisterMap(regionName);    }  } else if (params.componentType === "series") {    // 点击的数据    const data = params.data;    console.log("data", data);  }};// 返回上级async function gotoParent() {  if (isBack.value) {    if (returnArray.value.length > 0) {      returnArray.value.pop();      let backMap = returnArray.value.slice(-1);      if (backMap && backMap.length > 0) {        mapJsonData.value = JSON.parse(backMap[0].mapJsonData);        // 清除当前地图选项        mapChart.value.clear();        // 刷新地图实例        mapChart.value.resize();        handleRegisterMap(backMap[0].regionName);        if (backMap[0].adcode == 100000) {          isBack.value = false;          returnArray.value = [            {              adcode: 100000,              regionName: "china",              mapJsonData: JSON.stringify(chinaJsonData.value),            },          ];        }      }    } else {      isBack.value = false;    }  }}let chinaJsonData = ref({}); // 中国地图数据onMounted(async () => {  // 使用 fetch 加载 JSON 文件  await fetchMapJson(100000);  chinaJsonData.value = mapJsonData.value; // 先保留中国地图json  handleRegisterMap("china");  isBack.value = false;  returnArray.value.push({    adcode: 100000,    regionName: "china",    mapJsonData: JSON.stringify(chinaJsonData.value),  });  mapChart.value.on("click", handleClickMap);  window.addEventListener("resize", () => {    mapChart.value.resize();  });});onBeforeUnmount(() => {  // 移除地图点击事件监听器  mapChart.value.off("click", handleClickMap);});</script><style scoped>#map {  width: 100%;  height: 400px;}.go-back {  display: inline-block;  position: absolute;  right: 20px;  top: 20px;  z-index: 9999;  cursor: pointer;  color: #36cfff;}</style> 总结
通过上述步骤,您可以在 Vue3 应用中实现一个交互式的中国地图,支持省份点击和返回功能。

