WholeProcessPlatform/frontend/src/components/mapLegend/index.vue
2026-07-09 11:33:04 +08:00

263 lines
6.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="mapLegendView">
<div class="legendTitle">
图例
<span class="legendBtn" @click="isOpen = !isOpen">
<i
class="icon iconfont"
:class="isOpen ? 'icon-fold' : 'icon-unFold'"
></i>
</span>
</div>
<div class="legendContent" v-show="isOpen">
<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>
</div>
</template>
</div>
</div>
</div>
</a-spin>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { useMapOrchestrator } from '@/modules/map/application/map-orchestrator';
import { useMapConfigStore } from '@/modules/map/stores/map-config.store';
import { useMapStore } from '@/store/modules/map';
import LegendItem from '@/components/mapLegend/LegendItem.vue';
const mapStore = useMapStore();
const mapConfigStore = useMapConfigStore();
const mapOrchestrator = useMapOrchestrator();
const isOpen = ref(true);
// 备注图例组件直接渲染当前已派生好的图例运行态不再维护本地同步副本
const legendData = computed(() => mapStore.legendDataSelected || []);
// 备注:图例转圈只跟图例配置接口绑定,不再等待全部锚点接口加载完成。
const loadLegendData = computed(() => mapConfigStore.legendLoading);
// 备注:环保设施分组展示状态直接从当前图例数据派生,保持组件只负责渲染。
const hasEnvFac = computed(() =>
legendData.value.some((item: any) => item.name === '环保设施')
);
const legendItemClick = (item: any) => {
if (item.parentName == '环保设施') return;
if (item.canBeChecked == 0) {
return;
}
if (item.nameEn) {
mapOrchestrator.toggleLegend(item.nameEn, item.checked == 0 ? 1 : 0);
}
};
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;
const targetNameEns = leafItems
.filter(
(item: any) => item.parentName !== '环保设施' && item.canBeChecked !== 0
)
.filter((item: any) => item.checked !== targetChecked)
.map((item: any) => item.nameEn)
.filter(Boolean);
mapOrchestrator.toggleLegendBatch(targetNameEns, targetChecked);
};
</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;
}
}
.envFac {
position: absolute;
left: 0;
bottom: 0;
background-color: #ffffff;
border-top: 1px solid #eeeeee;
.groupContent {
padding: 0 !important;
:deep(.legendItem) {
cursor: none;
.legendIconTitle {
span {
white-space: nowrap !important;
}
}
&:hover {
.legendIconTitle {
cursor: auto;
color: rgba(0, 0, 0, 0.85);
}
}
}
}
}
.envFacBottom {
padding-bottom: 80px !important;
}
.legendContent {
white-space: nowrap;
max-width: 450px;
max-height: 450px;
overflow-x: scroll;
overflow-y: scroll;
border-top: 1px solid #eeeeee;
: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;
}
.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;
}
.disabled {
cursor: not-allowed !important;
}
}
}
}
.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;
}
}
</style>