在许多业务场景中,我们需要展示与地理位置相关的数据。本文将详细介绍如何在 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 应用中实现一个交互式的中国地图,支持省份点击和返回功能。