2026-04-02 09:26:02 +08:00
|
|
|
|
<!-- SidePanelItem.vue -->
|
|
|
|
|
|
<template>
|
2026-06-16 17:39:51 +08:00
|
|
|
|
<div class="xie-fang-fang-shi-container">
|
|
|
|
|
|
<SidePanelItem title="生态流量泄放方式">
|
|
|
|
|
|
<!-- Loading 状态 -->
|
2026-06-22 10:00:26 +08:00
|
|
|
|
<a-spin
|
|
|
|
|
|
v-if="loading"
|
|
|
|
|
|
:spinning="true"
|
|
|
|
|
|
style="
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 350px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
2026-06-16 17:39:51 +08:00
|
|
|
|
<!-- 空状态提示 -->
|
2026-06-22 10:00:26 +08:00
|
|
|
|
<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;
|
|
|
|
|
|
"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
2026-06-16 17:39:51 +08:00
|
|
|
|
<!-- 图表容器 -->
|
|
|
|
|
|
<div v-else ref="chartRef" class="pie-chart"></div>
|
|
|
|
|
|
</SidePanelItem>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 详情弹框 -->
|
|
|
|
|
|
<a-modal
|
|
|
|
|
|
v-model:open="modalVisible"
|
|
|
|
|
|
:title="'生态流量泄放方式'"
|
2026-07-07 13:50:21 +08:00
|
|
|
|
width="80%"
|
2026-06-16 17:39:51 +08:00
|
|
|
|
@cancel="handleModalCancel"
|
|
|
|
|
|
>
|
|
|
|
|
|
<STLLXFFS
|
|
|
|
|
|
v-if="modalVisible"
|
|
|
|
|
|
:basicId="wbsCode"
|
2026-06-22 10:00:26 +08:00
|
|
|
|
:sttpCodekey="selectedData.sttpCode"
|
2026-06-16 17:39:51 +08:00
|
|
|
|
/>
|
|
|
|
|
|
</a-modal>
|
|
|
|
|
|
</div>
|
2026-04-02 09:26:02 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
2026-06-16 17:39:51 +08:00
|
|
|
|
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
|
2026-04-02 09:26:02 +08:00
|
|
|
|
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';
|
2026-06-22 10:00:26 +08:00
|
|
|
|
import STLLXFFS from './TwoLayer/STLLXFFS.vue';
|
2026-07-07 10:33:29 +08:00
|
|
|
|
import { useDraggable } from '@/utils/drag';
|
|
|
|
|
|
|
2026-04-02 09:26:02 +08:00
|
|
|
|
// 定义组件名(便于调试和递归)
|
|
|
|
|
|
defineOptions({
|
2026-06-16 17:39:51 +08:00
|
|
|
|
name: 'xieFangFangShi'
|
2026-04-02 09:26:02 +08:00
|
|
|
|
});
|
2026-06-16 17:39:51 +08:00
|
|
|
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
2026-04-02 09:26:02 +08:00
|
|
|
|
const chartRef = ref<HTMLElement | null>(null);
|
|
|
|
|
|
let chartInstance: ECharts | null = null;
|
2026-06-16 17:39:51 +08:00
|
|
|
|
let wbsCode = ref('');
|
2026-04-02 09:26:02 +08:00
|
|
|
|
|
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);
|
2026-07-07 10:33:29 +08:00
|
|
|
|
useDraggable(modalVisible, { boundary: true, resetOnOpen: true });
|
2026-04-02 09:26:02 +08:00
|
|
|
|
// 颜色配置(根据图片配色)
|
|
|
|
|
|
const colors = [
|
2026-06-16 17:39:51 +08:00
|
|
|
|
'#9556a4', // 基荷发电 - 紫色
|
|
|
|
|
|
'#30b7b9', // 泄洪设施 - 青绿色
|
|
|
|
|
|
'#4b79ab', // 生态机组 - 蓝色
|
|
|
|
|
|
'#dbb629', // 生态放流管 - 黄色
|
|
|
|
|
|
'#df91ab', // 生态放流闸 - 粉色
|
|
|
|
|
|
'#78c300', // 生态放流孔 - 浅绿色
|
|
|
|
|
|
'#7399c6' // 生态放流洞 - 浅蓝色
|
2026-04-02 09:26:02 +08:00
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化图表
|
|
|
|
|
|
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-04-02 09:26:02 +08:00
|
|
|
|
},
|
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-04-02 09:26:02 +08:00
|
|
|
|
},
|
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-04-02 09:26:02 +08:00
|
|
|
|
}
|
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-04-02 09:26:02 +08:00
|
|
|
|
};
|
2026-06-16 17:39:51 +08:00
|
|
|
|
|
|
|
|
|
|
modalTitle.value = `${clickedData.name} - 详细信息`;
|
|
|
|
|
|
modalVisible.value = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 弹框取消事件
|
|
|
|
|
|
*/
|
|
|
|
|
|
const handleModalCancel = () => {
|
|
|
|
|
|
modalVisible.value = false;
|
|
|
|
|
|
selectedData.value = null;
|
2026-04-02 09:26:02 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 处理响应式
|
|
|
|
|
|
const handleResize = () => {
|
2026-06-16 17:39:51 +08:00
|
|
|
|
chartInstance?.resize();
|
2026-04-02 09:26:02 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 页面加载时执行的逻辑
|
|
|
|
|
|
onMounted(() => {
|
2026-06-16 17:39:51 +08:00
|
|
|
|
// 使用 nextTick 确保 DOM 已经完全渲染
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
initChart();
|
|
|
|
|
|
// 初始化后再 resize 一次,确保尺寸正确
|
2026-04-02 09:26:02 +08:00
|
|
|
|
setTimeout(() => {
|
2026-06-16 17:39:51 +08:00
|
|
|
|
chartInstance?.resize();
|
|
|
|
|
|
}, 200);
|
|
|
|
|
|
}, 50);
|
|
|
|
|
|
window.addEventListener('resize', handleResize);
|
|
|
|
|
|
|
|
|
|
|
|
// 组件挂载时获取数据
|
|
|
|
|
|
if (wbsCode.value) {
|
|
|
|
|
|
getecharts();
|
|
|
|
|
|
}
|
2026-04-02 09:26:02 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 组件卸载时清理
|
|
|
|
|
|
onBeforeUnmount(() => {
|
2026-06-16 17:39:51 +08:00
|
|
|
|
window.removeEventListener('resize', handleResize);
|
|
|
|
|
|
chartInstance?.dispose();
|
2026-04-02 09:26:02 +08:00
|
|
|
|
});
|
2026-06-16 17:39:51 +08:00
|
|
|
|
const getecharts = async () => {
|
|
|
|
|
|
loading.value = true;
|
2026-06-22 10:00:26 +08:00
|
|
|
|
|
2026-06-16 17:39:51 +08:00
|
|
|
|
let params = {
|
|
|
|
|
|
filter: {
|
|
|
|
|
|
logic: 'and',
|
|
|
|
|
|
filters: [
|
|
|
|
|
|
wbsCode.value != 'all'
|
|
|
|
|
|
? {
|
|
|
|
|
|
field: 'baseId',
|
|
|
|
|
|
operator: 'eq',
|
|
|
|
|
|
dataType: 'string',
|
|
|
|
|
|
value: wbsCode.value
|
|
|
|
|
|
}
|
|
|
|
|
|
: null,
|
|
|
|
|
|
{
|
2026-06-22 10:00:26 +08:00
|
|
|
|
field: 'sttp',
|
2026-06-16 17:39:51 +08:00
|
|
|
|
operator: 'contains',
|
|
|
|
|
|
dataType: 'string',
|
2026-06-22 10:00:26 +08:00
|
|
|
|
value: 'EQ_'
|
2026-06-16 17:39:51 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
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 }
|
|
|
|
|
|
);
|
2026-04-02 09:26:02 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
.xie-fang-fang-shi-container {
|
2026-06-16 17:39:51 +08:00
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
2026-04-02 09:26:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.pie-chart {
|
2026-06-16 17:39:51 +08:00
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 350px;
|
2026-04-02 09:26:02 +08:00
|
|
|
|
}
|
2026-06-16 17:39:51 +08:00
|
|
|
|
</style>
|