2026-03-31 14:17:30 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="mapLegendView">
|
|
|
|
|
|
<div class="legendTitle">
|
|
|
|
|
|
图例
|
|
|
|
|
|
<span class="legendBtn" @click="isOpen = !isOpen">
|
2026-06-01 08:42:06 +08:00
|
|
|
|
<i
|
|
|
|
|
|
class="icon iconfont"
|
|
|
|
|
|
:class="isOpen ? 'icon-fold' : 'icon-unFold'"
|
|
|
|
|
|
></i>
|
2026-03-31 14:17:30 +08:00
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="legendContent" v-show="isOpen">
|
2026-06-01 08:42:06 +08:00
|
|
|
|
<a-spin :spinning="loadLegendData" tip="加载中...">
|
|
|
|
|
|
<div class="legendContainer">
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="legendGroup"
|
|
|
|
|
|
v-for="i in legendData"
|
|
|
|
|
|
:key="i.name"
|
|
|
|
|
|
:class="{ envFac: i.name == '环保设施' }"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="groupTitle"
|
|
|
|
|
|
v-if="i.name != '环保设施'"
|
|
|
|
|
|
:class="{ gray: isGroupAllUnchecked(i) }"
|
|
|
|
|
|
@click="toggleGroup(i)"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ i.name }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="groupContent"
|
|
|
|
|
|
:class="{ envFacBottom: hasEnvFac && i.name != '环保设施' }"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template v-for="j in i.childrenList" :key="j.name">
|
|
|
|
|
|
<LegendItem
|
|
|
|
|
|
v-if="j.layerCode"
|
|
|
|
|
|
:item="j"
|
|
|
|
|
|
@click="legendItemClick(j)"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<div
|
|
|
|
|
|
v-else-if="j.childrenList && j.childrenList.length > 0"
|
|
|
|
|
|
class="legendGroup"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="groupTitle"
|
|
|
|
|
|
:class="{ gray: isGroupAllUnchecked(j) }"
|
|
|
|
|
|
@click="toggleGroup(j)"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ j.name }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="groupContent">
|
|
|
|
|
|
<LegendItem
|
|
|
|
|
|
v-for="item in j.childrenList"
|
|
|
|
|
|
:key="item.layerCode"
|
|
|
|
|
|
:item="item"
|
|
|
|
|
|
@click="legendItemClick(item)"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
2026-04-03 16:04:16 +08:00
|
|
|
|
</div>
|
2026-06-01 08:42:06 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
</div>
|
2026-03-31 14:17:30 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</a-spin>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
2026-06-01 08:42:06 +08:00
|
|
|
|
import { onMounted, ref, watch, computed } from 'vue';
|
|
|
|
|
|
import { useMapStore } from '@/store/modules/map';
|
|
|
|
|
|
import { useRoute } from 'vue-router';
|
|
|
|
|
|
import LegendItem from '@/components/mapLegend/LegendItem.vue';
|
|
|
|
|
|
import { MapClass } from '@/components/gis/map.class';
|
|
|
|
|
|
|
2026-04-03 16:04:16 +08:00
|
|
|
|
const mapStore: any = useMapStore();
|
2026-06-01 08:42:06 +08:00
|
|
|
|
const route = useRoute();
|
|
|
|
|
|
const mapClass = MapClass.getInstance();
|
|
|
|
|
|
|
|
|
|
|
|
// 从路由获取 pageKey
|
|
|
|
|
|
const pageKey = computed(() => {
|
|
|
|
|
|
const path = route.path;
|
|
|
|
|
|
const parts = path.split('/');
|
|
|
|
|
|
if (parts.length >= 3) {
|
|
|
|
|
|
return parts[1] + '_' + parts[2];
|
|
|
|
|
|
}
|
|
|
|
|
|
return '';
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 图例数据:使用选中的图例(由图层勾选状态决定)
|
|
|
|
|
|
const legendData = ref<any[]>([]);
|
2026-04-03 17:04:34 +08:00
|
|
|
|
const loadLegendData = ref(false);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
|
|
|
|
|
// 判断图例数据中是否有"环保设施"
|
|
|
|
|
|
const hasEnvFac = computed(() => {
|
|
|
|
|
|
return legendData.value.some((item: any) => item.name === '环保设施');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-04-03 16:04:16 +08:00
|
|
|
|
const legendItemClick = (item: any) => {
|
2026-06-01 08:42:06 +08:00
|
|
|
|
if (item.parentName == '环保设施') return;
|
2026-04-03 16:04:16 +08:00
|
|
|
|
if (item.canBeChecked == 0) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-06-01 08:42:06 +08:00
|
|
|
|
const newChecked = item.checked == 0 ? 1 : 0;
|
|
|
|
|
|
item.checked = newChecked;
|
|
|
|
|
|
|
|
|
|
|
|
// 更新 store 中的图例状态
|
|
|
|
|
|
if (item.nameEn) {
|
|
|
|
|
|
mapStore.updateLegendChecked(item.nameEn, newChecked);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 通过 nameEn 和 anchoPointState 控制单个图例的锚点显示隐藏
|
|
|
|
|
|
if (item.nameEn && item.layerCode) {
|
|
|
|
|
|
const layerKey = item.layerCode;
|
|
|
|
|
|
// 查找图层对象,判断图层类型(图层可能有多层嵌套)
|
|
|
|
|
|
const layerItem = mapStore.findLayerByKey(mapStore.layerData, layerKey);
|
|
|
|
|
|
console.log(`图例 ${item.nameEn} 控制图层:`, layerItem);
|
|
|
|
|
|
|
|
|
|
|
|
// 处理 alarm_range_ 前缀转换:添加 large_eng_built_ 前缀
|
|
|
|
|
|
const nameEnToUse = item.nameEn.includes('alarm_range_')
|
|
|
|
|
|
? `large_eng_built_${item.nameEn}`
|
|
|
|
|
|
: item.nameEn;
|
|
|
|
|
|
|
|
|
|
|
|
if (layerItem) {
|
|
|
|
|
|
if (layerItem.type === 'pointMap') {
|
|
|
|
|
|
// 锚点图层:控制锚点显示隐藏
|
|
|
|
|
|
mapClass.setLegendPointVisible(layerKey, nameEnToUse, newChecked === 1);
|
|
|
|
|
|
} else if (layerItem.type === 'GISMap') {
|
|
|
|
|
|
// GIS图层:控制图层显示隐藏
|
|
|
|
|
|
mapClass.controlBaseLayerTreeShowAndHidden(
|
|
|
|
|
|
layerKey,
|
|
|
|
|
|
layerItem.config?.id,
|
|
|
|
|
|
newChecked === 1
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 默认处理为锚点图层
|
|
|
|
|
|
mapClass.setLegendPointVisible(layerKey, nameEnToUse, newChecked === 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-04-03 16:04:16 +08:00
|
|
|
|
};
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
|
|
|
|
|
// 监听选中图例数据的变化(图层勾选变化时更新)
|
2026-04-03 16:04:16 +08:00
|
|
|
|
watch(
|
|
|
|
|
|
() => mapStore.legendDataSelected,
|
2026-06-01 08:42:06 +08:00
|
|
|
|
newValue => {
|
|
|
|
|
|
console.log('图例数据更新:', newValue);
|
2026-04-03 16:04:16 +08:00
|
|
|
|
legendData.value = newValue;
|
|
|
|
|
|
},
|
|
|
|
|
|
{ immediate: true, deep: true }
|
|
|
|
|
|
);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
|
() => mapStore.loading,
|
|
|
|
|
|
newValue => {
|
|
|
|
|
|
console.log('图例加载状态:', newValue);
|
|
|
|
|
|
loadLegendData.value = newValue;
|
|
|
|
|
|
},
|
|
|
|
|
|
{ immediate: true }
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2026-03-31 14:17:30 +08:00
|
|
|
|
const isOpen = ref(true);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
|
|
|
|
|
const getAllLeafItems = (group: any): any[] => {
|
|
|
|
|
|
const items: any[] = [];
|
|
|
|
|
|
if (!group.childrenList) return items;
|
|
|
|
|
|
|
|
|
|
|
|
group.childrenList.forEach((child: any) => {
|
|
|
|
|
|
if (child.layerCode) {
|
|
|
|
|
|
items.push(child);
|
|
|
|
|
|
} else if (child.childrenList && child.childrenList.length > 0) {
|
|
|
|
|
|
items.push(...getAllLeafItems(child));
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
return items;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const isGroupAllUnchecked = (group: any): boolean => {
|
|
|
|
|
|
const leafItems = getAllLeafItems(group);
|
|
|
|
|
|
if (leafItems.length === 0) return false;
|
|
|
|
|
|
return leafItems.every((item: any) => item.checked === 0);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const toggleGroup = (group: any) => {
|
|
|
|
|
|
const leafItems = getAllLeafItems(group);
|
|
|
|
|
|
if (leafItems.length === 0) return;
|
|
|
|
|
|
|
|
|
|
|
|
const hasAnyChecked = leafItems.some((item: any) => item.checked === 1);
|
|
|
|
|
|
const targetChecked = hasAnyChecked ? 0 : 1;
|
|
|
|
|
|
|
|
|
|
|
|
leafItems.forEach((item: any) => {
|
|
|
|
|
|
if (item.parentName === '环保设施') return;
|
|
|
|
|
|
if (item.canBeChecked === 0) return;
|
|
|
|
|
|
if (item.checked !== targetChecked) {
|
|
|
|
|
|
item.checked = targetChecked;
|
|
|
|
|
|
|
|
|
|
|
|
if (item.nameEn) {
|
|
|
|
|
|
mapStore.updateLegendChecked(item.nameEn, targetChecked);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (item.nameEn && item.layerCode) {
|
|
|
|
|
|
const layerKey = item.layerCode;
|
|
|
|
|
|
const layerItem = mapStore.findLayerByKey(mapStore.layerData, layerKey);
|
|
|
|
|
|
|
|
|
|
|
|
const nameEnToUse = item.nameEn.includes('alarm_range_')
|
|
|
|
|
|
? `large_eng_built_${item.nameEn}`
|
|
|
|
|
|
: item.nameEn;
|
|
|
|
|
|
|
|
|
|
|
|
if (layerItem) {
|
|
|
|
|
|
if (layerItem.type === 'pointMap') {
|
|
|
|
|
|
mapClass.setLegendPointVisible(
|
|
|
|
|
|
layerKey,
|
|
|
|
|
|
nameEnToUse,
|
|
|
|
|
|
targetChecked === 1
|
|
|
|
|
|
);
|
|
|
|
|
|
} else if (layerItem.type === 'GISMap') {
|
|
|
|
|
|
mapClass.controlBaseLayerTreeShowAndHidden(
|
|
|
|
|
|
layerKey,
|
|
|
|
|
|
layerItem.config?.id,
|
|
|
|
|
|
targetChecked === 1
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
mapClass.setLegendPointVisible(
|
|
|
|
|
|
layerKey,
|
|
|
|
|
|
nameEnToUse,
|
|
|
|
|
|
targetChecked === 1
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
2026-03-31 14:17:30 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
.mapLegendView {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
min-width: 72px;
|
|
|
|
|
|
margin: 24px 0 16px 16px;
|
|
|
|
|
|
border: 1px solid #ccdae7;
|
|
|
|
|
|
padding: 6px;
|
|
|
|
|
|
z-index: 9;
|
|
|
|
|
|
background-color: #ffffff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.legendTitle {
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
.legendBtn {
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-01 08:42:06 +08:00
|
|
|
|
.envFac {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
background-color: #ffffff;
|
|
|
|
|
|
border-top: 1px solid #eeeeee;
|
|
|
|
|
|
.groupContent {
|
|
|
|
|
|
padding: 0 !important;
|
|
|
|
|
|
:deep(.legendItem) {
|
|
|
|
|
|
cursor: none;
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
.legendIconTitle {
|
|
|
|
|
|
cursor: auto;
|
|
|
|
|
|
color: rgba(0, 0, 0, 0.85);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.envFacBottom {
|
|
|
|
|
|
padding-bottom: 80px !important;
|
|
|
|
|
|
}
|
2026-03-31 14:17:30 +08:00
|
|
|
|
.legendContent {
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
max-width: 450px;
|
2026-06-01 08:42:06 +08:00
|
|
|
|
max-height: 450px;
|
2026-03-31 14:17:30 +08:00
|
|
|
|
overflow-x: scroll;
|
|
|
|
|
|
overflow-y: scroll;
|
|
|
|
|
|
border-top: 1px solid #eeeeee;
|
2026-06-01 08:42:06 +08:00
|
|
|
|
:deep(.ant-spin-nested-loading) {
|
|
|
|
|
|
min-height: 50px;
|
|
|
|
|
|
}
|
|
|
|
|
|
&::-webkit-scrollbar {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.legendContainer {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
min-height: 20px;
|
|
|
|
|
|
min-width: 30px;
|
|
|
|
|
|
overflow: auto;
|
|
|
|
|
|
padding-bottom: 8px;
|
|
|
|
|
|
}
|
2026-03-31 14:17:30 +08:00
|
|
|
|
.legendGroup {
|
|
|
|
|
|
display: inline-block;
|
|
|
|
|
|
vertical-align: top;
|
|
|
|
|
|
padding: 0 4px;
|
|
|
|
|
|
.groupTitle {
|
|
|
|
|
|
text-align: left;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
.groupContent {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
padding: 10px 0;
|
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
|
.gray {
|
|
|
|
|
|
filter: grayscale(100%);
|
|
|
|
|
|
color: #00000073;
|
|
|
|
|
|
}
|
2026-04-03 16:04:16 +08:00
|
|
|
|
.disabled {
|
|
|
|
|
|
cursor: not-allowed !important;
|
2026-03-31 14:17:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-01 08:42:06 +08:00
|
|
|
|
.legendItem {
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
margin-right: 10px;
|
|
|
|
|
|
padding: 4px 0;
|
|
|
|
|
|
min-height: 30px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
|
.legendIcon {
|
|
|
|
|
|
width: 25px !important;
|
|
|
|
|
|
height: 25px !important;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
img {
|
|
|
|
|
|
max-width: 80%;
|
|
|
|
|
|
max-height: 80%;
|
|
|
|
|
|
vertical-align: middle;
|
|
|
|
|
|
display: inline-block;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.smallIcon {
|
|
|
|
|
|
width: 22px !important;
|
|
|
|
|
|
height: 22px !important;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
.legendIconTitle {
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
flex: 1 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
color: #2f6b98;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-31 14:17:30 +08:00
|
|
|
|
</style>
|