使用arco组件库,data.v在线地图数据,代码主要用来操作地图上的所示点。借鉴了他人代码和gpt,未简洁代码
<template>
<div class="container">
<a-card class="general-card" :bordered="false">
<a-breadcrumb :routes="mapList">
<template #item-render="{route, paths}">
<a-link @click="renderMapEcharts(route.mapName)">
{{ route.name }}
</a-link>
</template>
</a-breadcrumb>
<div style="height: 800px;">
<Chart id="mycharts" :option="mapOption" :autoresize="true" @click="handleClick"
@contextmenu="showContextMenu" />
<div
v-if="contextMenu.visible"
class="context-menu"
:style="{ left: `${contextMenu.position.x}px`, top: `${contextMenu.position.y}px`}"
>
<a-space>
<a-button type="outline" size="small" @click="editDistrict(id)" :disabled="existData">编辑</a-button>
<a-popconfirm content="确定删除该区域吗?" position="top" @ok="delDistrict(id)">
<a-button type="outline" size="small" :disabled="existData">删除</a-button>
</a-popconfirm>
<a-button type="text" shape="circle" status="danger" @click="hideContextMenu">
<icon-close />
</a-button>
</a-space>
</div>
</div>
</a-card>
<save-district ref="saveDistrict" />
</div>
</template>
<script setup lang="ts">
import { registerMap, use } from "echarts/core";
import Chart from "vue-echarts";
import { CanvasRenderer } from "echarts/renderers";
import { EffectScatterChart, MapChart, ScatterChart } from "echarts/charts";
import {
GridComponent,
LegendComponent,
TitleComponent,
TooltipComponent,
VisualMapComponent
} from "echarts/components";
import { inject, provide, ref } from "vue";
import { Notification } from "@arco-design/web-vue";
import SaveDistrict from "@/views/program/apply/district/component/saveDistrict.vue";
import { deleteStation, getMapData } from "@/api/program/district";
use([
CanvasRenderer,
TitleComponent,
TooltipComponent,
LegendComponent,
GridComponent,
VisualMapComponent,
MapChart,
ScatterChart,
EffectScatterChart
]);
const reload: any = inject("reload");
const mapOption = ref();
const mapList = ref<any[]>([{
mapName: "150000_full",
name: "内蒙古自治区"
}]); // 记录地图
const getMapJson = async (mapName: string) => {
const url = `https://geo.datav.aliyun.com/areas_v3/bound/${mapName}.json`;
return await fetch(url).then(res => res.json());
};
const setOptions = (mapName: string, mapData: any) => {
return {
tooltip: {
trigger: "item", // 触发类型为基于图形元素的触发
formatter: function(params: any) {
if (params.seriesType === "effectScatter") { // 确保处理的是scatter系列的数据
const { name, value } = params.data; // 这里的value是站点的经纬度
return `名称: ${name}<br>经纬度: ${value}`;
}
}
},
geo: {
map: mapName,
roam: true,
select: false,
zoom: 1.2,
// layoutCenter: ["45%", "70%"],//页面位置和占比
layoutSize: "100%",
// 图形上的文本标签,可用于说明图形的一些数据信息,比如值,名称等。
selectedMode: "single",
label: {
show: true,
backgroundColor: "transparent",
position: "top",
formatter: "{a}",
color: "#7c9dd9",
fontWeight: "bold"
},
itemStyle: {
areaColor: "#323c48", // 默认区块颜色
borderColor: "#9ea5af",
borderWidth: 0.5,
shadowBlur: 5, // 阴影模糊程度
shadowColor: "rgba(0, 0, 0, 0.3)", // 阴影颜色,使用半透明黑色
shadowOffsetX: 0, // 阴影在X轴方向上的偏移距离
shadowOffsetY: 0 // 阴影在Y轴方向上的偏移距离
},
emphasis: {
label: {
fontSize: 14
},
itemStyle: {
areaColor: "#596f9a", // 鼠标悬浮时的区块颜色
borderColor: "#add1dc",
borderWidth: 0.5,
shadowBlur: 10, // 增加阴影模糊程度
shadowColor: "rgba(0, 0, 0, 0.6)", // 使用更深一点的半透明黑色
shadowOffsetX: 0,
shadowOffsetY: 0
}
}
},
series: [
// 数据
{
type: "map",
map: mapName,
roam: false,
geoIndex: 0,
select: false,
data: mapData
},
{
name: "位置",
type: "effectScatter",
coordinateSystem: "geo",
data: mapData.flatMap(item => item.data),
symbolSize: 3,
label: {
formatter: " {b}",
position: "right",
show: true,
color: "yellow"
},
itemStyle: {
color: "yellow",
shadowBlur: 10,
shadowColor: "beige"
},
showEffectOn: "render",
rippleEffect: {
brushType: "stroke",
scale: 8, // 增大波纹扩散比例
period: 8, // 增加波纹动画周期,使其变化更慢,更容易看到
color: "rgba(247,255,0,0.34)"// 调整波纹颜色,增加透明度,保证与站点颜色的对比度
},
zlevel: 1
}
]
};
};
const contextMenu = ref({
visible: false,
position: { x: 0, y: 0 }
});
const id = ref();
const existData = ref(false);
const operateType = ref();
const operateId = ref();
// 方法:显示上下文菜单
const showContextMenu = (event: MouseEvent) => {
let parent = document.getElementById("mycharts");
parent.oncontextmenu = () => false; // 阻止默认的右键菜单显示
if (event.data.id != null) {
contextMenu.value.position = { x: event.event.event.pageX - 55, y: event.event.event.pageY - 45 };
contextMenu.value.visible = true; //弹框启用
id.value = event.data.id;
existData.value = false;
} else {
contextMenu.value.visible = false;
existData.value = true;
}
};
// 方法:隐藏上下文菜单
const hideContextMenu = () => {
contextMenu.value.visible = false;
};
const renderMapEcharts = async (mapName: string) => {
const mapJson = await getMapJson(mapName);
registerMap(mapName, mapJson);
// 去除集合在这之后的数据
mapList.value.map((item, index) => {
if (item.mapName === mapName) {
mapList.value = mapList.value.splice(0, index + 1);
}
});
const map = await getMapData();
const mapdata = mapJson.features.map((item: { properties: any }) => {
const tempValue = item.properties.center ? [...item.properties.center] : item.properties.center;
// 调用接口 赋值data(没有按照父级关系绑定,有的点会在当前地图外展示出来
const data = map.data.map(item => {
return {
id: item.id,
name: item.name,
value: item.value
};
});
return {
id: null,
name: item.properties.name,
value: tempValue, // 中心点经纬度
adcode: item.properties.adcode, // 区域编码
level: item.properties.level, // 层级
data: data
};
});
// 设置options并渲染
mapOption.value = setOptions(mapName, mapdata);
};
renderMapEcharts("150000_full"); // 初始化绘制中国地图
// 点击下钻
const handleClick = (param: any) => {
contextMenu.value.visible = false;
// 只有点击地图才触发
if (param.seriesType !== "map") return;
if (param.data) {
const { adcode, level, name } = param.data;
const mapName = level === "district" ? adcode : adcode + "_full";
// 防止最后一个层级被重复点击,返回上一级出错
if (mapList.value[mapList.value.length - 1].mapName === mapName) {
return Notification.warning("已下钻到底!");
}
mapList.value.push({
mapName: mapName,
name: name
});
renderMapEcharts(mapName);
}
};
const saveDistrict = ref();
provide("operateInfo", {
id: operateId,
type: operateType
});
const showModal = (type: string, id: string) => {
operateType.value = type;
operateId.value = id;
saveDistrict.value.handleClick();
};
const editDistrict = (id) => {
showModal("edit", id);
contextMenu.value.visible = false;
};
const delDistrict = async (id) => {
await deleteStation(id);
Notification.success("删除成功");
contextMenu.value.visible = false;
reload();
};
</script>
<style lang="less">
.container {
padding: 0 20px 20px 20px;
}
.context-menu {
background-color: transparent;
border: none;
position: fixed;
z-index: 9999;
}
</style>