WholeProcessPlatform/frontend/src/modules/shengtaidabiaoTwoModJJ/index.vue

676 lines
17 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>
<SidePanelItem title="生态流量达标情况" :click-action="{ show: true }">
<!-- 使用插槽自定义点击提示的内容 -->
<!-- 1时间2026-05-11 2026-06-11
-->
<template #click-action-content>
<div v-if="titleData.cnt" style="max-width: 300px; line-height: 1.6">
<p>
1、时间{{ getdate().startDate1 }} 至 {{ getdate().endDate1 }}
</p>
<p>
2、范围接入过生态流量数据的电站
<!-- <span
style="color: #5989ad; cursor: pointer"
@click.stop="handleStationClick()"
>
({{ titleData.cnt }}) </span
> -->
</p>
<p>3、达标判定按环评批复或报告中对生态流量限值有明确要求的评判。</p>
</div>
</template>
<div class="body_topOne">
<a-radio-group v-model:value="mode" @change="getEcharts()">
<a-radio-button value="top">按基地</a-radio-button>
<a-radio-button value="left">按调节性能</a-radio-button>
</a-radio-group>
</div>
<a-spin :spinning="spinning">
<div ref="chartRef" class="chart-container"></div>
</a-spin>
</SidePanelItem>
</div>
<!-- 环保自动监测工作开展情况弹框 -->
<a-modal
v-model:open="huanbaoModalVisible"
:title="'环保自动监测工作开展情况'"
width="1536px"
:footer="null"
>
<ModalYkzhbzdjcgz v-if="huanbaoModalVisible" :titleData="titleData" />
</a-modal>
<!-- 生态流量达标率详情弹框 -->
<a-modal
v-model:open="detailModalVisible"
:title="detailModalTitle"
width="1536px"
:footer="null"
>
<STLLXFFS
v-if="detailModalVisible"
:type="mode"
:basicId="detailData[0]?.baseId"
:qecPerformance="detailData[0].qecPerformance"
:date-arr="dateArr"
/>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, onMounted, watch } from 'vue';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import * as echarts from 'echarts';
import { eqqecRateCount, evnmAutoMonitorGetKendoListCust } from '@/api/stll';
import ModalYkzhbzdjcgz from '../shengtaidabiaoMod/TwoLayer/ModalYkzhbzdjcgz.vue';
import STLLXFFS from './TwoLayer/STLLXFFS.vue'
import { useMapViewStore } from '@/modules/map/stores/map-view.store';
// 定义组件名(便于调试和递归)
defineOptions({
name: 'shengtaidabiaoMod'
});
const mode = ref('top');
const spinning = ref(false);
const chartRef = ref<HTMLElement | null>(null);
let chartInstance: echarts.ECharts | null = null;
const mapViewStore = useMapViewStore();
// 动态数据存储
let dateArr = ref<any[]>([]);
const chartData = ref<{
categories: string[];
currentData: number[];
lastYearData: number[];
baseIds: (string | number)[];
qecPerformances: (string | number)[];
}>({
categories: [],
currentData: [],
lastYearData: [],
baseIds: [],
qecPerformances: []
});
const huanbaoModalVisible = ref(false);
// 处理电站数量点击事件
const handleStationClick = () => {
huanbaoModalVisible.value = true;
};
// 加载数据
const loadData = async () => {
spinning.value = true;
try {
// 先获取数据
await getEcharts();
// 确保 DOM 已渲染后初始化图表
setTimeout(() => {
initChart();
spinning.value = false;
}, 50);
} catch (error) {
console.error('数据加载失败:', error);
spinning.value = false;
}
};
// 初始化图表
const initChart = () => {
if (!chartRef.value) {
console.error('图表容器未渲染');
return;
}
// 检查容器尺寸
const containerHeight = chartRef.value.offsetHeight;
if (!containerHeight || containerHeight === 0) {
console.warn('容器高度为 0延迟重试');
setTimeout(() => initChart(), 50);
return;
}
// 如果实例存在,先销毁
if (chartInstance) {
chartInstance.dispose();
}
chartInstance = echarts.init(chartRef.value);
// 使用动态数据
const data = chartData.value;
const isHorizontal = mode.value === 'top';
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: function (params: any) {
let result = params[0].name + '<br/>';
params.forEach((param: any) => {
const percentage = param.value.toFixed(2) + '%';
result +=
param.marker +
param.seriesName +
'&nbsp;&nbsp;<b>' +
percentage +
'</b><br/>';
});
return result;
}
},
legend: {
data: ['当前', '去年同期']
},
grid: { left: 10, right: 10, bottom: 20, top: 40, containLabel: true },
dataZoom: [
{
type: 'inside',
// 横向柱形图(mode='top'): X轴是数值轴需要缩放
// 竖向柱状图(mode='left'): X轴是分类轴不需要缩放Y轴是数值轴也不需要内部缩放
[isHorizontal ? 'xAxisIndex' : 'xAxisIndex']: 0,
filterMode: 'empty',
zoomOnMouseWheel: true,
moveOnMouseMove: false,
moveOnMouseWheel: true,
start: 0,
end: 100,
minValueSpan: 0,
maxValueSpan: isHorizontal ? 20 : 5
}
],
xAxis: isHorizontal
? {
type: 'value',
min: 80,
max: 100,
splitLine: {
show: true,
lineStyle: {
color: '#E8E8E8',
type: 'solid'
}
},
axisLabel: {
color: '#666',
formatter: '{value}'
}
}
: {
type: 'category',
data: data.categories,
axisLabel: {
color: '#666',
fontSize: 12,
interval: 0,
rotate: 0,
margin: 10
},
axisLine: {
show: true,
lineStyle: {
color: '#666'
}
},
axisTick: {
show: true,
lineStyle: {
color: '#666'
}
},
splitLine: {
show: false
}
},
yAxis: isHorizontal
? {
type: 'category',
data: data.categories,
inverse: true,
axisLabel: {
color: '#666',
fontSize: 12,
interval: 0,
rotate: 45,
margin: 10
},
axisLine: {
show: true,
lineStyle: {
color: '#666'
}
},
axisTick: {
show: true,
lineStyle: {
color: '#666'
}
},
splitLine: {
show: false
}
}
: {
type: 'value',
min: 0,
max: 100,
splitLine: {
show: true,
lineStyle: {
color: '#E8E8E8',
type: 'solid'
}
},
axisLabel: {
color: '#666',
formatter: '{value}'
}
},
series: [
{
name: '当前',
type: 'bar',
data: data.currentData,
itemStyle: {
color: '#5470C6'
},
barWidth: isHorizontal ? 8 : 8
// barGap: '30%'
},
{
name: '去年同期',
type: 'bar',
data: data.lastYearData,
itemStyle: {
color: '#91CC75'
},
barWidth: isHorizontal ? 8 : 8
// barGap: '30%'
}
]
};
chartInstance.setOption(option);
// 添加点击事件
chartInstance.off('click');
chartInstance.on('click', (params: any) => {
handleChartClick(params);
});
// 强制重绘,确保尺寸正确
setTimeout(() => {
chartInstance?.resize();
}, 0);
};
// 处理图表点击事件
const handleChartClick = (params: any) => {
const categoryName = params.name;
const currentIndex = params.dataIndex;
//
// 设置弹框标题
detailModalTitle.value =
mode.value === 'top'
? `生态流量达标率(按基地统计) `
: `生态流量达标率(按调节性能统计)`;
// 获取当前点击项的详细数据
const baseId = mode.value === 'top' ? chartData.value.baseIds[currentIndex] : undefined;
const qecPerformance = mode.value === 'left' ? chartData.value.qecPerformances[currentIndex] : undefined;
//
const currentItem = {
baseId: baseId,
baseName: mode.value === 'top' ? categoryName :'',
qecPerformance: qecPerformance,
qecPerformanceName: mode.value === 'left' ? categoryName : '',
qecRate: chartData.value.currentData[currentIndex],
beforeQecRate: chartData.value.lastYearData[currentIndex]
};
detailData.value = [currentItem];
detailModalVisible.value = true;
};
const getdate = (offsetYears = 0) => {
const formatDate = (date: Date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
let startDate: string;
let endDate: string;
let startDate1: string;
let endDate1: string;
// 如果 dateArr.value 有数据,使用数组中的日期
if (dateArr.value && dateArr.value.length === 2) {
const start = new Date(dateArr.value[0]);
const end = new Date(dateArr.value[1]);
// 如果 offsetYears 不为 0表示获取去年同期的时间
if (offsetYears !== 0) {
start.setFullYear(start.getFullYear() + offsetYears);
end.setFullYear(end.getFullYear() + offsetYears);
}
startDate = `${formatDate(start)} 00:00:00`;
endDate = `${formatDate(end)} 23:59:59`;
// 提取日期部分(去除时间)
startDate1 = formatDate(start);
endDate1 = formatDate(end);
// debugger
} else {
// 否则使用默认的"一个月前到现在"
const now = new Date();
const oneMonthAgo = new Date(now);
oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
// 如果 offsetYears 不为 0表示获取去年同期的时间
if (offsetYears !== 0) {
now.setFullYear(now.getFullYear() + offsetYears);
oneMonthAgo.setFullYear(oneMonthAgo.getFullYear() + offsetYears);
}
startDate = `${formatDate(oneMonthAgo)} 00:00:00`;
endDate = `${formatDate(now)} 23:59:59`;
startDate1 = formatDate(oneMonthAgo);
endDate1 = formatDate(now);
}
return { startDate, endDate, startDate1, endDate1 };
};
const getEcharts = async () => {
// 获取当前时间范围
const currentDateRange = getdate(0);
// 获取去年同期时间范围(往前推一年)
const lastYearDateRange = getdate(-1);
// 构建基础 filter不包含时间
const baseFilter = {
logic: 'and',
filters: [
mode.value == 'top'
? {
field: 'dtinEnv',
operator: 'in',
dataType: 'string',
value: [1, 2]
}
: {
field: 'type',
operator: 'eq',
dataType: 'string',
value: 'DAY'
}
]
};
// 构建当前时间范围的 params
const currentParams = {
filter: {
...baseFilter,
filters: [
...baseFilter.filters,
{
field: 'tm',
operator: 'gte',
dataType: 'date',
value: currentDateRange.startDate
},
{
field: 'tm',
operator: 'lte',
dataType: 'date',
value: currentDateRange.endDate
}
]
},
group:
mode.value == 'top'
? [
{ dir: 'asc', field: 'basestepsort' },
{ dir: 'asc', field: 'baseId' },
{ dir: 'asc', field: 'baseName' }
]
: [{ dir: 'asc', field: 'qecPerformance' }]
};
// 构建去年同期时间范围的 params
const lastYearParams = {
filter: {
...baseFilter,
filters: [
...baseFilter.filters,
{
field: 'tm',
operator: 'gte',
dataType: 'date',
value: lastYearDateRange.startDate
},
{
field: 'tm',
operator: 'lte',
dataType: 'date',
value: lastYearDateRange.endDate
}
]
},
group: currentParams.group
};
try {
// 并行请求两个 API
const [currentRes, lastYearRes]: [any, any] = await Promise.all([
eqqecRateCount(currentParams),
eqqecRateCount(lastYearParams)
]);
// 处理当前数据
const currentApiData = (currentRes && currentRes.data && currentRes.data.data) ? currentRes.data.data : [];
// 处理去年同期数据
const lastYearApiData = (lastYearRes && lastYearRes.data && lastYearRes.data) ? lastYearRes.data.data : [];
// 根据 mode 提取不同的数据
if (mode.value === 'top') {
// 按基地模式:去重处理(避免 baseId 重复)
const uniqueMap = new Map();
currentApiData.forEach((item: any) => {
if (!uniqueMap.has(item.baseId)) {
uniqueMap.set(item.baseId, item);
}
});
const uniqueData = Array.from(uniqueMap.values());
// 建立去年同期的数据映射(按 baseId
const lastYearMap = new Map();
lastYearApiData.forEach((item: any) => {
if (!lastYearMap.has(item.baseId)) {
lastYearMap.set(item.baseId, item.qecRate);
}
});
chartData.value = {
categories: uniqueData.map((item: any) => item.baseName || ''),
currentData: uniqueData.map((item: any) =>
item.qecRate != null ? Number(item.qecRate) : 0
),
lastYearData: uniqueData.map((item: any) => {
const lastYearRate = lastYearMap.get(item.baseId);
return lastYearRate != null ? Number(lastYearRate) : 0;
}),
baseIds: uniqueData.map((item: any) => item.baseId),
qecPerformances: []
};
} else {
// 按调节性能模式
// 建立去年同期的数据映射(按 qecPerformance
const lastYearMap = new Map();
lastYearApiData.forEach((item: any) => {
if (!lastYearMap.has(item.qecPerformance)) {
lastYearMap.set(item.qecPerformance, item.qecRate);
}
});
chartData.value = {
categories: currentApiData.map((item: any) => item.qecPerformanceName || ''),
currentData: currentApiData.map((item: any) =>
item.qecRate != null ? Number(item.qecRate) : 0
),
lastYearData: currentApiData.map((item: any) => {
const lastYearRate = lastYearMap.get(item.qecPerformance);
return lastYearRate != null ? Number(lastYearRate) : 0;
}),
baseIds: [],
qecPerformances: currentApiData.map((item: any) => item.qecPerformance || '')
};
}
} catch (error) {
console.error('API 调用失败:', error);
// 发生错误时使用空数据
chartData.value = {
categories: [],
currentData: [],
lastYearData: [],
baseIds: [],
qecPerformances: []
};
}
};
//统计电站范围:接入过生态流量数据的电站
const titleData: any = ref({ cnt: 0 });
const getcont = async () => {
let params = {
filter: {
logic: 'and',
filters: [
{
field: 'showIds',
operator: 'in',
value: ['7']
}
]
}
};
let res = await evnmAutoMonitorGetKendoListCust(params);
// [
// {
// "cnt": 452,
// "orderInx": 7,
// "orderInxName": "生态流量",
// "sttpFullPath": "ENG",
// "sttpCode": "ENG",
// "sttpName": "生态流量"
// }
// ]
let list = res?.data?.data[0];
if (list) {
titleData.value = list;
}
//
};
// 页面加载时执行的逻辑
onMounted(() => {
loadData();
getcont();
// 监听窗口大小变化
window.addEventListener('resize', () => {
chartInstance?.resize();
});
});
watch(
() => mapViewStore.searchTimeRange,
(newVal, oldVal) => {
console.log('时间范围变化:', oldVal, '->', newVal);
dateArr.value = newVal
// debugger
loadData();
// 处理你的逻辑
},
{ deep: true }
);
// 详情弹框相关
const detailModalVisible = ref(false);
const detailModalTitle = ref('');
const detailData = ref<any[]>([]);
const detailColumns = ref([
{
title: mode.value === 'top' ? '基地名称' : '调节性能',
dataIndex: mode.value === 'top' ? 'baseName' : 'qecPerformanceName',
key: mode.value === 'top' ? 'baseName' : 'qecPerformanceName',
width: 150,
fixed: 'left'
},
{
title: '当前达标率',
dataIndex: 'qecRate',
key: 'qecRate',
width: 120,
align: 'right'
},
{
title: '去年同期达标率',
dataIndex: 'beforeQecRate',
key: 'beforeQecRate',
width: 150,
align: 'right'
}
]);
// 监听 mode 变化
watch(mode, () => {
loadData();
});
</script>
<style lang="scss" scoped>
.body_topOne {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
.title_text {
font-size: 16px;
}
}
.ant-radio-group {
.ant-radio-button-wrapper-checked {
border: 1px solid #2f6b98 !important;
background-color: #2f6b98 !important;
color: #fff !important;
}
.ant-radio-button-wrapper {
border: 2px solid #2f6b98 !important;
}
.ant-radio-button-wrapper-checked :before {
background-color: #2f6b98 !important;
}
}
.chart-container {
width: 100%;
height: 449px;
// padding: 10px;
}
.detail-content {
padding: 16px 0;
}
</style>