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