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

482 lines
11 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>
<SidePanelItem title="泄放方式分布情况">
<div ref="chartRef" class="bar-chart-container"></div>
<!-- 空状态提示 -->
<a-empty v-if="showEmpty" description="暂无数据" />
</SidePanelItem>
<!-- 详情弹框 -->
<a-modal
v-model:open="modalVisible"
title="泄放方式分布情况"
width="1536px"
:footer="null"
@cancel="handleModalCancel"
>
<FBQKTwolayer
v-if="modalVisible"
:selectData="selectData"
:jidiName="jidiName"
:jiDiList="JidiSelectEventStore.jidiData"
></FBQKTwolayer>
</a-modal>
</template>
<script lang="ts" setup>
import {
ref,
onMounted,
onBeforeUnmount,
watch,
computed,
nextTick
} from 'vue';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import * as echarts from 'echarts';
import type { ECharts } from 'echarts';
import { vmsstbprptGetKendoList } from '@/api/stll';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import FBQKTwolayer from './TwoLayer/FenBuQingKuangTwoLayer.vue';
import { useDraggable } from '@/utils/drag';
// 定义组件名
defineOptions({
name: 'XieFangFenBu'
});
const JidiSelectEventStore = useJidiSelectEventStore();
// DOM 引用
const chartRef = ref<HTMLElement | null>(null);
let chartInstance: ECharts | null = null;
// 响应式数据
const loading = ref(false);
const data = ref<any[]>([]);
const echartsXData = ref<string[]>([]);
const wbsCode = ref('');
// 弹框状态
const modalVisible = ref(false);
const selectData = ref(''); // 选中的设施类型名称
const jidiName = ref(''); // 选中的基地名称
useDraggable(modalVisible, { boundary: true, resetOnOpen: true });
// 设施类型配置
const typeName = [
{ label: '生态放流孔', eqtp: 'EQ_1' },
{ label: '生态放流闸', eqtp: 'EQ_2' },
{ label: '生态放流洞', eqtp: 'EQ_3' },
{ label: '生态放流管', eqtp: 'EQ_4' },
{ label: '基荷发电', eqtp: 'EQ_5' },
{ label: '生态机组', eqtp: 'EQ_6' },
{ label: '泄洪设施', eqtp: 'EQ_7' }
];
// 默认配色方案(当 getUnitConfigByCode 不可用时使用)
const defaultColors = [
'#9556a4', // EQ_1 - 紫色
'#30b7b9', // EQ_2 - 青绿色
'#4b79ab', // EQ_3 - 蓝色
'#dbb629', // EQ_4 - 黄色
'#df91ab', // EQ_5 - 粉色
'#78c300', // EQ_6 - 浅绿色
'#7399c6' // EQ_7 - 浅蓝色
];
// 计算是否显示空状态
const showEmpty = computed(() => {
return !loading.value && (!data.value || data.value.length === 0);
});
/**
* 手写 omit 函数(替代 lodash.omit
*/
const omit = (obj: any, keys: string[]) => {
const result = { ...obj };
keys.forEach(key => {
delete result[key];
});
return result;
};
/**
* 获取颜色配置(模拟 getUnitConfigByCode
* TODO: 如果项目中有等效实现,请替换此处逻辑
*/
const getColorByType = (type: string): string => {
// 这里可以替换为实际的 getUnitConfigByCode 调用
// const config = getUnitConfigByCode('Other', type);
// return config?.lineColor || config?.pieColor;
// 暂时使用默认配色
const index = typeName.findIndex(item => item.eqtp === type);
return defaultColors[index] || '#999999';
};
/**
* 初始化图表
*/
const initChart = () => {
if (!chartRef.value) return;
// 确保容器有正确的高度
if (chartRef.value.clientHeight === 0) {
setTimeout(() => {
initChart();
}, 100);
return;
}
if (!chartInstance) {
chartInstance = echarts.init(chartRef.value);
}
const option = buildChartOption();
chartInstance.setOption(option, true);
// 绑定点击事件
chartInstance.off('click');
chartInstance.on('click', handleChartClick);
};
/**
* 构建图表配置
*/
const buildChartOption = () => {
// 计算每个基地的总数
const countSums: Record<string, number> = {};
data.value.forEach(item => {
const baseid = item.baseid;
const count = Number(item.count) || 0;
if (countSums[baseid]) {
countSums[baseid] += count;
} else {
countSums[baseid] = count;
}
});
const values = Object.values(countSums);
const maxValue = values.length > 0 ? Math.max(...values) : 0;
// 计算总计数据
const totalData: number[] = [];
const uniqueBaseNames = [...new Set(echartsXData.value)];
uniqueBaseNames.forEach((basename, index) => {
let total = 0;
data.value.forEach(item => {
if (item.basename === basename) {
total += Number(item.count) || 0;
}
});
totalData[index] = total;
});
// 构建 series 数据
const seriesData = typeName.map(type => {
return uniqueBaseNames.map(basename => {
const found = data.value.find(
item => item.eqtp === type.eqtp && item.basename === basename
);
return found ? Number(found.count) || 0 : 0;
});
});
// 获取颜色数组
const colors = typeName.map(type => getColorByType(type.eqtp));
const option: any = {
tooltip: {
trigger: 'axis',
confine: true,
formatter: (params: any) => {
const content = params
.map((item: any) => {
let value = item.value || 0;
if (item.seriesName === '总计') {
value = totalData[item.dataIndex];
}
return `<div>${item.marker}${item.seriesName}: &nbsp;&nbsp;&nbsp;&nbsp;<span style="float:right">${value} </span></div>`;
})
.join('');
return `<h3>${params[0].name}</h3>${content}`;
},
axisPointer: {
type: 'line'
}
},
grid: {
left: '8%',
right: '4%',
bottom: '20%',
top: '20%'
},
legend: {
type: 'scroll',
icon: 'rect',
orient: 'horizontal',
itemWidth: 12,
itemHeight: 12,
textStyle: {
fontSize: 12,
color: '#6A93B9',
height: 8
},
data: typeName.map(item => item.label)
},
xAxis: {
show: true,
type: 'category',
axisLine: { show: true },
axisLabel: {
fontSize: 12,
color: '#686868',
rotate: 37
},
axisTick: {
alignWithLabel: true
},
data: uniqueBaseNames
},
yAxis: {
max: maxValue,
type: 'value',
barWidth: 10,
name: '数量(个)',
nameTextStyle: {
fontSize: 12,
color: '#848484',
align: 'center'
},
axisTick: { show: false },
axisLabel: {
fontSize: 12,
color: '#343434',
fontFamily: 'Bebas'
}
},
series: [
...typeName.map((type, index) => ({
name: type.label,
type: 'bar' as const,
stack: 'total',
barCategoryGap: '50%',
data: seriesData[index]
})),
{
name: '总计',
type: 'bar' as const,
stack: 'total',
label: {
show: true,
position: 'top',
formatter: (params: any) => {
return String(totalData[params.dataIndex]);
}
},
itemStyle: {
color: 'transparent'
},
data: Array(totalData.length).fill(0)
}
],
color: colors
};
return option;
};
/**
* 图表点击事件
*/
const handleChartClick = (params: any) => {
// 根据系列名称找到对应的 sttpCode (eqtp)
const typeConfig = typeName.find(item => item.label === params.seriesName);
selectData.value = typeConfig ? typeConfig.eqtp : '';
// 将基地名称转换为 wbsCode
const jidiInfo = JidiSelectEventStore.jidiData.find(
(item: any) => item.wbsName === params.name
);
jidiName.value = jidiInfo ? jidiInfo.wbsCode : params.name;
modalVisible.value = true;
};
/**
* 弹框取消事件
*/
const handleModalCancel = () => {
modalVisible.value = false;
selectData.value = '';
jidiName.value = '';
};
/**
* 获取数据
*/
const fetchData = async () => {
if (!wbsCode.value) return;
loading.value = true;
try {
// 构建请求参数
let params: any = {
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,'
}
].filter(Boolean)
},
group: [
{
dir: 'asc',
field: 'baseStepSort'
},
{
dir: 'asc',
field: 'baseId'
},
{
dir: 'desc',
field: 'sttpCode'
}
],
groupResultFlat: 'true'
};
// 如果是全部基地,移除 baseId 过滤条件
if (wbsCode.value === 'all') {
params = omit(params, ['filter']);
params.filter = {
logic: 'and',
filters: [
{
field: 'sttpFullPath',
operator: 'contains',
dataType: 'string',
value: 'ENV,ENVP,EQ,'
}
]
};
}
const res = await vmsstbprptGetKendoList(params);
if (res.data && res.data.data) {
const apiData = res.data.data;
// 处理数据
const list: any[] = [];
apiData.forEach((it: any) => {
list.push({
key: it.baseId,
count: it.count_sttpCode,
eqtp: it.sttpCode
});
});
// 映射基地名称:使用 JidiSelectEventStore.jidiData
const processedData = list.map((item: any) => {
// 从 jidiData 中查找匹配的基地名称
const jidiInfo = JidiSelectEventStore.jidiData.find(
(jidi: any) => jidi.wbsCode === item.key
);
return {
baseid: item.key,
basename: jidiInfo ? jidiInfo.wbsName : item.key, // 如果找到则使用 wbsName否则使用 baseId
count: item.count,
eqtp: item.eqtp
};
});
data.value = processedData;
//
// 提取唯一的基地名称作为 X 轴数据
echartsXData.value = [
...new Set(processedData.map(item => item.basename))
];
// 数据更新后重新渲染图表
await nextTick();
initChart();
} else {
data.value = [];
echartsXData.value = [];
}
} catch (error) {
console.error('获取泄放方式分布数据失败:', error);
data.value = [];
echartsXData.value = [];
} finally {
loading.value = false;
}
};
/**
* 处理窗口大小变化
*/
const handleResize = () => {
chartInstance?.resize();
};
// 生命周期钩子
onMounted(() => {
// 延迟初始化图表,确保 DOM 已渲染
setTimeout(() => {
initChart();
setTimeout(() => {
chartInstance?.resize();
}, 200);
}, 50);
window.addEventListener('resize', handleResize);
// 组件挂载时获取数据
if (wbsCode.value) {
fetchData();
}
});
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize);
chartInstance?.dispose();
chartInstance = null;
});
// 监听基地选择变化
watch(
() => JidiSelectEventStore.selectedItem,
newVal => {
console.log('流域切换:', newVal);
if (newVal?.wbsCode) {
wbsCode.value = newVal.wbsCode;
fetchData();
}
},
{ deep: true, immediate: true }
);
</script>
<style lang="scss" scoped>
.bar-chart-container {
width: 100%;
height: 350px;
}
</style>