WholeProcessPlatform/frontend/src/modules/shengTaiLiuLiangXieFangSheShiMod/xieFangFangShi/index.vue

366 lines
9.1 KiB
Vue
Raw Normal View History

<!-- SidePanelItem.vue -->
<template>
2026-06-16 17:39:51 +08:00
<div class="xie-fang-fang-shi-container">
<SidePanelItem title="生态流量泄放方式">
<!-- Loading 状态 -->
<a-spin v-if="loading" :spinning="true" style="width: 100%; height: 350px; display: flex; justify-content: center; align-items: center;" />
<!-- 空状态提示 -->
<a-empty v-else-if="!loading && pieData.length === 0" description="暂无数据" style="height: 350px; display: flex; justify-content: center; align-items: center;flex-direction: column;text-indent:0em" />
<!-- 图表容器 -->
<div v-else ref="chartRef" class="pie-chart"></div>
</SidePanelItem>
<!-- 详情弹框 -->
<a-modal
v-model:open="modalVisible"
:title="'生态流量泄放方式'"
width="1536px"
@cancel="handleModalCancel"
>
<STLLXFFS
v-if="modalVisible"
:basicId="wbsCode"
:sttpCodekey ="selectedData.sttpCode"
/>
</a-modal>
</div>
</template>
<script lang="ts" setup>
2026-06-16 17:39:51 +08:00
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import * as echarts from 'echarts';
import type { ECharts } from 'echarts';
2026-06-16 17:39:51 +08:00
import { msstbprptGetKendoList } from '@/api/stll';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import STLLXFFS from './TwoLayer/STLLXFFS.vue'
// 定义组件名(便于调试和递归)
defineOptions({
2026-06-16 17:39:51 +08:00
name: 'xieFangFangShi'
});
2026-06-16 17:39:51 +08:00
const JidiSelectEventStore = useJidiSelectEventStore();
const chartRef = ref<HTMLElement | null>(null);
let chartInstance: ECharts | null = null;
2026-06-16 17:39:51 +08:00
let wbsCode = ref('');
2026-06-16 17:39:51 +08:00
// Loading 状态
const loading = ref(false);
// 动态数据
const pieData = ref<any[]>([]);
const totalCount = ref(0);
const apiRawData = ref<any[]>([]); // 保存原始 API 数据
// 弹框状态
const modalVisible = ref(false);
const modalTitle = ref('');
const selectedData = ref<any>(null);
// 颜色配置(根据图片配色)
const colors = [
2026-06-16 17:39:51 +08:00
'#9556a4', // 基荷发电 - 紫色
'#30b7b9', // 泄洪设施 - 青绿色
'#4b79ab', // 生态机组 - 蓝色
'#dbb629', // 生态放流管 - 黄色
'#df91ab', // 生态放流闸 - 粉色
'#78c300', // 生态放流孔 - 浅绿色
'#7399c6' // 生态放流洞 - 浅蓝色
];
// 初始化图表
const initChart = () => {
2026-06-16 17:39:51 +08:00
if (!chartRef.value) return;
// 确保容器有正确的高度
if (chartRef.value.clientHeight === 0) {
// 如果容器高度为 0延迟初始化
setTimeout(() => {
initChart();
}, 100);
return;
}
chartInstance = echarts.init(chartRef.value);
const option = {
tooltip: {
trigger: 'item',
formatter: '{b}: {c}%'
},
series: [
{
name: '泄放设施',
type: 'pie',
radius: ['42%', '62%'], // 环形:内径 42%,外径 62%(进一步缩小)
center: ['50%', '50%'],
avoidLabelOverlap: true,
itemStyle: {
borderRadius: 0,
borderColor: '#fff',
borderWidth: 2
},
2026-06-16 17:39:51 +08:00
label: {
show: true,
position: 'outside',
alignTo: 'edge', // 标签对齐到边缘
margin: 8, // 标签与圆环的距离
formatter: (params: any) => {
return `{name|${params.name}}\n{value|${params.value}%}`;
},
rich: {
name: {
fontSize: 12,
color: '#666666', // 灰蓝色标签
lineHeight: 18,
fontFamily: 'Microsoft YaHei, sans-serif',
align: 'left' // 左对齐
},
2026-06-16 17:39:51 +08:00
value: {
fontSize: 12,
color: '#2f6b98', // 灰蓝色百分比
lineHeight: 18,
fontFamily: 'Microsoft YaHei, sans-serif',
align: 'center', // 左对齐
fontWeight: 'bold' // 百分比加粗
}
2026-06-16 17:39:51 +08:00
},
overflow: 'none', // 不省略,完整显示
padding: [0, 0]
},
labelLine: {
show: true,
length: 12,
length2: 12,
lineStyle: {
width: 1
}
},
emphasis: {
scale: false, // 禁用悬停放大
itemStyle: {
brightness: 1.2, // 鼠标悬停时颜色变亮 20%
shadowBlur: 0 // 禁用阴影
},
labelLine: {
show: true // 悬停时不显示引导线变化
}
},
data: pieData.value.map((item, index) => ({
...item,
itemStyle: { color: colors[index % colors.length] },
labelLine: {
lineStyle: {
color: colors[index % colors.length]
}
}
}))
}
],
// 中心文字
title: {
text: totalCount.value.toString(),
subtext: '泄放设施总数量\n(个)',
left: 'center',
top: 'center',
textStyle: {
color: '#2e86de',
fontSize: 30,
fontWeight: 'bold',
fontFamily: 'Arial'
},
subtextStyle: {
color: '#666',
fontSize: 12,
lineHeight: 16,
fontFamily: 'Microsoft YaHei, sans-serif'
}
}
};
chartInstance.setOption(option);
// 绑定点击事件
chartInstance.on('click', handleChartClick);
};
/**
* 图表点击事件
*/
const handleChartClick = (params: any) => {
// 获取点击的数据项
const clickedData = params.data;
if (clickedData) {
selectedData.value = {
name: clickedData.name,
value: clickedData.value,
sttpCode: clickedData.sttpCode,
count:
apiRawData.value.find(
(item: any) => item.sttpCode === clickedData.sttpCode
)?.count_sttpName || 0,
...apiRawData.value.find(
(item: any) => item.sttpCode === clickedData.sttpCode
) // 保留所有原始数据字段
};
2026-06-16 17:39:51 +08:00
modalTitle.value = `${clickedData.name} - 详细信息`;
modalVisible.value = true;
}
};
/**
* 弹框取消事件
*/
const handleModalCancel = () => {
modalVisible.value = false;
selectedData.value = null;
};
// 处理响应式
const handleResize = () => {
2026-06-16 17:39:51 +08:00
chartInstance?.resize();
};
// 页面加载时执行的逻辑
onMounted(() => {
2026-06-16 17:39:51 +08:00
// 使用 nextTick 确保 DOM 已经完全渲染
setTimeout(() => {
initChart();
// 初始化后再 resize 一次,确保尺寸正确
setTimeout(() => {
2026-06-16 17:39:51 +08:00
chartInstance?.resize();
}, 200);
}, 50);
window.addEventListener('resize', handleResize);
// 组件挂载时获取数据
if (wbsCode.value) {
getecharts();
}
});
// 组件卸载时清理
onBeforeUnmount(() => {
2026-06-16 17:39:51 +08:00
window.removeEventListener('resize', handleResize);
chartInstance?.dispose();
});
2026-06-16 17:39:51 +08:00
const getecharts = async () => {
loading.value = true;
let params = {
filter: {
logic: 'and',
filters: [
wbsCode.value != 'all'
? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: wbsCode.value
}
: null,
{
field: 'sttpFullPath',
operator: 'contains',
dataType: 'string',
value: 'ENV,ENVP,EQ,'
},
{
field: 'sttpFullPath',
operator: 'neq',
dataType: 'string',
value: 'ENV,ENVP,EQ,'
},
{
field: 'baseName',
operator: 'isnotnull',
dataType: 'string'
}
].filter(Boolean)
},
group: [
{
dir: 'desc',
field: 'sttpCode'
},
{
dir: 'desc',
field: 'sttpName'
}
],
groupResultFlat: 'true'
};
try {
let res = await msstbprptGetKendoList(params);
// 处理返回的数据结构
if (!res || !res.data || !res.data.data || !Array.isArray(res.data.data)) {
console.warn('未获取到有效的泄放设施数据:', res);
pieData.value = [];
totalCount.value = 0;
return;
}
const apiData = res.data.data;
// 保存原始数据
apiRawData.value = apiData;
// 计算总数
const total = apiData.reduce((sum: number, item: any) => {
return sum + (item.count_sttpName || 0);
}, 0);
totalCount.value = total;
// 映射数据结构,计算百分比(四舍五入保留两位小数)
pieData.value = apiData.map((item: any) => ({
name: item.sttpName,
value:
total > 0
? Number(((item.count_sttpName / total) * 100).toFixed(2))
: 0,
sttpCode: item.sttpCode // 添加 sttpCode 字段
}));
// 数据更新后重新渲染图表
setTimeout(() => {
initChart();
}, 100);
} catch (error) {
console.error('获取泄放设施数据失败:', error);
pieData.value = [];
totalCount.value = 0;
} finally {
loading.value = false;
}
};
watch(
() => JidiSelectEventStore.selectedItem,
newVal => {
console.log('流域切换:', newVal);
if (newVal?.wbsCode) {
wbsCode.value = newVal.wbsCode;
getecharts();
}
},
{ deep: true, immediate: true }
);
</script>
<style lang="scss" scoped>
.xie-fang-fang-shi-container {
2026-06-16 17:39:51 +08:00
width: 100%;
height: 100%;
}
.pie-chart {
2026-06-16 17:39:51 +08:00
width: 100%;
height: 350px;
}
2026-06-16 17:39:51 +08:00
</style>