WholeProcessPlatform/frontend/src/components/MapModal/components/WaterTemperature.vue

1063 lines
30 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="water-temperature">
<a-tabs v-model:activeKey="activeTabKey">
<a-tab-pane v-for="tab in tabsList" :key="tab.key" :tab="tab.name" />
<template #rightExtra>
<div class="search-bar">
<a-range-picker
v-model:value="dateRange"
format="YYYY-MM-DD HH:mm"
:show-time="showTimeConfig"
:allowClear="false"
:presets="DateSetting.RangeButton.month1"
:disabled-date="disabledDate"
/>
<a-button type="primary" class="search-btn" @click="handleSearch">
查询
</a-button>
<a-button
v-if="isSummaryMode"
class="search-btn"
@click="handleExport"
>
导出
</a-button>
</div>
</template>
</a-tabs>
<div class="tab-checkbox">
<a-checkbox-group
v-model:value="selectedColumns"
name="checkboxgroup"
:options="filteredCheckBoxOptions"
class="checkbox-group"
@change="handleCheckboxChange"
/>
</div>
<a-spin :spinning="isLoading" tip="加载中...">
<div class="tab-content">
<div class="content-body">
<div class="chart-wrapper">
<div ref="chartRef" class="chart-container"></div>
<a-empty
v-if="!chartData || chartData.length === 0"
description="暂无数据"
class="chart-empty"
/>
</div>
<div class="table-wrapper">
<BasicTable
:scrollY="360"
:scrollX="tableScrollX"
:columns="currentColumns"
:data="tableData"
>
<template #summary>
<a-table-summary fixed>
<template
v-for="summaryRow in summaryRows"
:key="summaryRow.label"
>
<a-table-summary-row class="summary-row">
<a-table-summary-cell :index="0">
{{ summaryRow.label }}
</a-table-summary-cell>
<a-table-summary-cell
v-for="col in summaryColumns"
:key="col.dataIndex"
:index="col.summaryIndex"
>
{{ summaryRow.cells[col.dataIndex]?.value ?? '-' }}
<Tooltip
v-if="summaryRow.cells[col.dataIndex]?.time"
:title="`时间:${
summaryRow.cells[col.dataIndex].time
}`"
placement="top"
>
<InfoCircleOutlined class="summary-tip-icon" />
</Tooltip>
</a-table-summary-cell>
</a-table-summary-row>
</template>
</a-table-summary>
</template>
</BasicTable>
</div>
</div>
</div>
</a-spin>
</div>
</template>
<script lang="ts" setup>
import {
ref,
watch,
computed,
onMounted,
onBeforeUnmount,
nextTick
} from 'vue';
import dayjs, { Dayjs } from 'dayjs';
import * as echarts from 'echarts';
import { Tooltip } from 'ant-design-vue';
import {
// getMonitorDataWaterTempList,
getMonitorDataWaterTemp,
getMonitorDataWaterTempPowerStation,
getMonitorDataWaterTempPowerStation2
} from '@/api/mapModal';
import { useModelStore } from '@/store/modules/model';
import BasicTable from '@/components/BasicTable/index.vue';
import { DateSetting } from '@/utils/enumeration';
import { InfoCircleOutlined } from '@ant-design/icons-vue';
const modelStore = useModelStore();
const props = defineProps({
code: { type: String, default: '' },
isActive: { type: Boolean, default: false },
stcd: { type: String, default: '' }
});
const hasLoaded = ref(false);
const isLoading = ref(false);
const tableData = ref<any[]>([]);
const chartData = ref<any[]>([]);
const chartRef = ref<HTMLElement>();
let chartInstance: echarts.ECharts | null = null;
const tabsList = [{ name: '水温', key: 'swjc.tabs.jcsj' }];
const activeTabKey = ref('swjc.tabs.jcsj');
// 时间选择器配置
const showTimeConfig = {
format: 'HH:mm',
hourStep: 1,
minuteStep: 5,
secondStep: 60
};
const disabledDate = (current: Dayjs) =>
current && current.isAfter(dayjs(), 'day');
const initDateRange = (): [Dayjs, Dayjs] => {
const startDate = dayjs().subtract(7, 'day').startOf('day');
return [startDate, dayjs()];
};
const dateRange = ref<[Dayjs, Dayjs] | undefined>(initDateRange());
// ==================== Checkbox 配置 ====================
const selectedColumns = ref<any[]>([]);
const checkBoxOptions = [
{ label: '综合分析', value: 'summary' },
{ label: '等深水温', value: 'deep' }
];
const showSummaryCheckbox = ref(false);
const showDeepCheckbox = ref(false);
const isDataEmpty = computed(() => {
const data = Array.isArray(chartData.value) ? chartData.value : [];
return data.length === 0;
});
const filteredCheckBoxOptions = computed(() => {
if (isDataEmpty.value) return [];
const available = checkBoxOptions.filter(opt => {
if (opt.value === 'summary' && !showSummaryCheckbox.value) return false;
if (opt.value === 'deep' && !showDeepCheckbox.value) return false;
return true;
});
// 互斥逻辑:已选中等温时只显示等温,已选中综合时只显示综合
const selected = selectedColumns.value;
if (selected.length === 1) {
const val = selected[0];
return available.filter(opt => opt.value === val);
}
return available;
});
const isDeepMode = computed(() => selectedColumns.value.includes('deep'));
const isSummaryMode = computed(() => selectedColumns.value.includes('summary'));
const currentChartMode = computed(() => {
if (isSummaryMode.value) return 'summary';
if (isDeepMode.value) return 'deep';
return 'base';
});
const handleCheckboxChange = (values: any[]) => {
const hasSummary = values.includes('summary');
const hasDeep = values.includes('deep');
if (hasSummary && hasDeep) {
// 互斥:同时勾选时保留最后点击的那个
selectedColumns.value = [values[values.length - 1]];
} else if (!hasSummary && !hasDeep) {
// 全部取消,清空选择
selectedColumns.value = [];
} else {
selectedColumns.value = values;
}
fetchData();
};
// ==================== 表格列配置 ====================
const baseWaterTempColumns = [
{
title: '时间',
dataIndex: 'tm',
fixed: 'left',
customRender: ({ text }: any) =>
text ? dayjs(text).format('YYYY-MM-DD HH:mm') : '-'
},
{
title: '水温(°C)',
dataIndex: 'wt',
summary: true,
customRender: ({ text }: any) =>
text !== undefined && text !== null ? Number(text).toFixed(1) : '-'
}
];
const deepModeExtraColumns = [
{
title: '等温水深(m)',
dataIndex: 'deep_wt',
summary: true,
customRender: ({ record }: any) => {
const val = record?.wtvtDataVo?.wthg;
return val !== undefined && val !== null ? Number(val).toFixed(2) : '-';
}
},
{
title: '垂向水温(℃)',
dataIndex: 'vert_wt',
summary: true,
customRender: ({ record }: any) => {
const val = record?.wtvtDataVo?.vwt;
return val !== undefined && val !== null ? Number(val).toFixed(1) : '-';
}
}
];
const summaryModeColumns = [
{
title: '时间',
dataIndex: 'tm',
width: 130,
fixed: 'left',
customRender: ({ text }: any) =>
text ? dayjs(text).format('YYYY-MM-DD HH:mm') : '-'
},
{
title: '出库水温(℃)',
dataIndex: 'wt',
summary: true,
customRender: ({ text }: any) =>
text !== undefined && text !== null ? Number(text).toFixed(1) : '-'
},
{
title: '入库水温(℃)',
dataIndex: 'iwt_wt',
summary: true,
customRender: ({ record }: any) =>
record?.iwtDataVo?.wt !== undefined && record?.iwtDataVo?.wt !== null
? Number(record.iwtDataVo.wt).toFixed(1)
: '-'
},
{
title: '天然水温(°C)',
dataIndex: 'natureTmp',
summary: true,
customRender: ({ text }: any) =>
text !== undefined && text !== null ? Number(text).toFixed(1) : '-'
},
{
title: '气温(℃)',
dataIndex: 'tmp_at',
summary: true,
width: 80,
customRender: ({ record }: any) =>
record?.tmpDataVo?.at !== undefined && record?.tmpDataVo?.at !== null
? Number(record.tmpDataVo.at).toFixed(1)
: '-'
},
{
title: '出库流量(m³/s)',
dataIndex: 'hydropw_qo',
summary: true,
width: 120,
customRender: ({ record }: any) =>
record?.hydropwDataVo?.qo !== undefined &&
record?.hydropwDataVo?.qo !== null
? Number(record.hydropwDataVo.qo).toFixed(3)
: '-'
},
{
title: '入库流量(m³/s)',
dataIndex: 'hydropw_qi',
summary: true,
width: 120,
customRender: ({ record }: any) =>
record?.hydropwDataVo?.qi !== undefined &&
record?.hydropwDataVo?.qi !== null
? Number(record.hydropwDataVo.qi).toFixed(3)
: '-'
}
];
const swjcColumns = computed(() => {
if (isSummaryMode.value) return summaryModeColumns;
if (isDeepMode.value)
return [...baseWaterTempColumns, ...deepModeExtraColumns];
return baseWaterTempColumns;
});
const currentColumns = swjcColumns;
const tableScrollX = computed(() => {
const columns = currentColumns.value;
const totalWidth = columns.reduce(
(sum: number, col: any) => sum + (col.width || 100),
0
);
return totalWidth > 600 ? totalWidth : undefined;
});
// ==================== 合计行 ====================
const dataPathMap: Record<string, string> = {
wt: 'wt',
iwt_wt: 'iwtDataVo.wt',
natureTmp: 'natureTmp',
tmp_at: 'tmpDataVo.at',
hydropw_qo: 'hydropwDataVo.qo',
hydropw_qi: 'hydropwDataVo.qi',
deep_wt: 'wtvtDataVo.wthg',
vert_wt: 'wtvtDataVo.vwt'
};
const summaryColumns = computed(() => {
const modeCols = swjcColumns.value;
return modeCols
.filter((col: any) => col.summary === true)
.map((col: any, idx: number) => ({ ...col, summaryIndex: idx + 1 }));
});
const getNestedValue = (obj: any, path: string) => {
if (!path || !obj) return undefined;
return path.split('.').reduce((acc, key) => acc?.[key], obj);
};
const calcSummary = (dataIndex: string, type: 'max' | 'min' | 'avg') => {
const data = Array.isArray(chartData.value) ? chartData.value : [];
if (!data || data.length === 0) return null;
const dataPath = dataPathMap[dataIndex] || dataIndex;
const values = data
.map((r: any) => {
const val = getNestedValue(r, dataPath);
return val !== null && val !== undefined && val !== '-'
? Number(val)
: NaN;
})
.filter((v: number) => !isNaN(v));
if (values.length === 0) return null;
if (type === 'max') return Math.max(...values);
if (type === 'min') return Math.min(...values);
return values.reduce((s, v) => s + v, 0) / values.length;
};
const getSummaryTime = (dataIndex: string, type: 'max' | 'min') => {
const data = Array.isArray(chartData.value) ? chartData.value : [];
if (!data || data.length === 0) return '';
const dataPath = dataPathMap[dataIndex] || dataIndex;
const records = data.filter((r: any) => {
const val = getNestedValue(r, dataPath);
return val !== null && val !== undefined && val !== '-';
});
if (records.length === 0) return '';
const target = Math[type](
...records.map((r: any) => Number(getNestedValue(r, dataPath)))
);
const record = records.find(
(r: any) => Number(getNestedValue(r, dataPath)) === target
);
return record?.tm ? dayjs(record.tm).format('YYYY-MM-DD HH:mm:ss') : '';
};
const formatSummaryValue = (dataIndex: string, value: number | null) => {
if (value === null) return '-';
if (dataIndex.includes('qo') || dataIndex.includes('qi'))
return Number(value).toFixed(3);
if (dataIndex === 'deep_wt') return Number(value).toFixed(2);
return Number(value).toFixed(1);
};
const summaryRows = computed(() => {
// Force dependency tracking
void chartData.value;
void summaryColumns.value;
if (!Array.isArray(chartData.value) || chartData.value.length === 0)
return [];
const cols = summaryColumns.value;
if (!cols || cols.length === 0) return [];
const rows = [
{
label: '最大值',
showTime: true,
cells: {} as Record<string, { value: string; time: string }>
},
{
label: '最小值',
showTime: true,
cells: {} as Record<string, { value: string; time: string }>
},
{
label: '平均值',
showTime: false,
cells: {} as Record<string, { value: string; time: string }>
}
];
cols.forEach((col: any) => {
const dataIndex = col.dataIndex;
const maxVal = calcSummary(dataIndex, 'max');
const minVal = calcSummary(dataIndex, 'min');
const avgVal = calcSummary(dataIndex, 'avg');
rows[0].cells[dataIndex] = {
value: formatSummaryValue(dataIndex, maxVal),
time: maxVal !== null ? getSummaryTime(dataIndex, 'max') : ''
};
rows[1].cells[dataIndex] = {
value: formatSummaryValue(dataIndex, minVal),
time: minVal !== null ? getSummaryTime(dataIndex, 'min') : ''
};
rows[2].cells[dataIndex] = {
value: formatSummaryValue(dataIndex, avgVal),
time: ''
};
});
return rows;
});
// ==================== 图表 ====================
function shouldStagger(width: number, dataLen: number) {
return dataLen > Math.floor(width / 100);
}
function getAxisLabelConfig(chartWidth: number, xAxisData: string[]) {
const needStagger = shouldStagger(chartWidth, xAxisData.length);
return {
fontSize: 12,
interval: 'auto',
formatter: (value: string, index: number) => {
const [date, time] = value.split(' ');
if (needStagger) {
return index % 2 === 0 ? `${date}\n${time}\n ` : ` \n${date}\n${time}`;
}
return `${date}\n${time}`;
}
};
}
const updateChart = (data: any[]) => {
if (!chartInstance) return;
if (!data || data.length === 0) {
chartInstance.clear();
return;
}
const sorted = [...data].sort(
(a, b) => new Date(a.tm).getTime() - new Date(b.tm).getTime()
);
const xAxisData = sorted.map(item =>
dayjs(item.tm).format('YYYY-MM-DD HH:mm')
);
const width = chartRef.value?.clientWidth || 0;
const axisLabelConfig = getAxisLabelConfig(width, xAxisData);
let option: any;
if (currentChartMode.value === 'base') {
option = {
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(50,50,50,0.9)',
textStyle: { color: '#fff', fontSize: 12 },
axisPointer: { type: 'cross' },
formatter: (params: any) => {
if (!params?.length) return '';
const idx = params[0].dataIndex;
const fullTime = sorted[idx]?.tm
? dayjs(sorted[idx].tm).format('YYYY-MM-DD HH:mm')
: '';
let html = `<div style="font-size:16px;margin-bottom:8px;">${fullTime}</div>`;
params.forEach((p: any) => {
const v = p.value != null ? Number(p.value).toFixed(1) : '-';
html += `<div style="display:flex;align-items:center;justify-content:space-between;margin:4px 0;"><span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:${p.color};margin-right:8px;"></span><span style="flex:1;font-size:14px;">水温: </span><span style="font-size:14px;min-width:60px;text-align:right;"><strong>${v}</strong> °C</span></div>`;
});
return html;
}
},
legend: {
width: '80%',
type: 'scroll',
top: 10,
data: ['水温'],
textStyle: { fontSize: 12 }
},
grid: { left: 60, right: 40, top: 80, bottom: 80 },
xAxis: {
type: 'category',
data: xAxisData,
axisLine: { lineStyle: { color: '#000000' } },
axisTick: { show: false },
axisLabel: axisLabelConfig,
splitLine: {
show: true,
lineStyle: { color: '#bfbfbf', type: 'solid' }
}
},
yAxis: {
name: '水温(°C)',
type: 'value',
axisLine: { lineStyle: { color: '#000000' } },
scale: true,
splitLine: {
show: true,
lineStyle: { color: '#bfbfbf', type: 'solid' }
}
},
series: [
{
name: '水温',
type: 'line',
data: sorted.map(item => item.wt),
smooth: true,
symbol: 'none',
lineStyle: { color: '#4B79AB', width: 2 },
itemStyle: { color: '#4B79AB' },
areaStyle: { color: '#85A9D0' }
}
],
dataZoom: [
{ type: 'inside', xAxisIndex: [0], throttle: 50, start: 0, end: 100 }
],
toolbox: {
show: true,
feature: {
saveAsImage: { title: '保存为图片', type: 'png', pixelRatio: 2 }
},
right: 20,
top: 10
}
};
} else if (currentChartMode.value === 'deep') {
option = {
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(50,50,50,0.9)',
textStyle: { color: '#fff', fontSize: 12 },
axisPointer: { type: 'cross' },
formatter: (params: any) => {
if (!params?.length) return '';
const idx = params[0].dataIndex;
const fullTime = sorted[idx]?.tm
? dayjs(sorted[idx].tm).format('YYYY-MM-DD HH:mm')
: '';
let html = `<div style="font-size:16px;margin-bottom:8px;">${fullTime}</div>`;
params.forEach((p: any) => {
const isDepth = p.seriesName.includes('水深');
const v =
p.value != null
? isDepth
? Number(p.value).toFixed(2)
: Number(p.value).toFixed(1)
: '-';
const unit = isDepth ? 'm' : '°C';
html += `<div style="display:flex;align-items:center;justify-content:space-between;margin:4px 0;"><span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:${p.color};margin-right:8px;"></span><span style="flex:1;font-size:14px;">${p.seriesName}: </span><span style="font-size:14px;min-width:60px;text-align:right;"><strong>${v}</strong> ${unit}</span></div>`;
});
return html;
}
},
legend: {
type: 'scroll',
top: 10,
data: ['水温', '等温水深'],
textStyle: { fontSize: 12 }
},
grid: { left: 60, right: 60, top: 80, bottom: 80 },
xAxis: {
type: 'category',
data: xAxisData,
axisLine: { lineStyle: { color: '#000000' } },
axisTick: { show: false },
axisLabel: axisLabelConfig,
splitLine: {
show: true,
lineStyle: { color: '#bfbfbf', type: 'solid' }
}
},
yAxis: [
{
name: '水温(°C)',
type: 'value',
position: 'left',
axisLine: { lineStyle: { color: '#000000' } },
scale: true,
splitLine: {
show: true,
lineStyle: { color: '#bfbfbf', type: 'solid' }
}
},
{
name: '水深(m)',
type: 'value',
position: 'right',
axisLine: { lineStyle: { color: '#000000' } },
scale: true,
splitLine: { show: false }
}
],
series: [
{
name: '水温',
type: 'line',
yAxisIndex: 0,
data: sorted.map(item => item.wt),
smooth: true,
showSymbol: false,
lineStyle: { color: '#4B79AB', width: 2 },
itemStyle: { color: '#4B79AB' }
},
{
name: '等温水深',
type: 'line',
yAxisIndex: 1,
data: sorted.map(item => item?.wtvtDataVo?.wthg),
smooth: true,
showSymbol: false,
lineStyle: { color: '#AB7AE5', width: 2 },
itemStyle: { color: '#AB7AE5' }
}
],
dataZoom: [
{ type: 'inside', xAxisIndex: [0], throttle: 50, start: 0, end: 100 }
],
toolbox: {
show: true,
feature: {
saveAsImage: { title: '保存为图片', type: 'png', pixelRatio: 2 }
},
right: 20,
top: 10
}
};
} else {
option = {
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(50,50,50,0.9)',
textStyle: { color: '#fff', fontSize: 12 },
axisPointer: { type: 'cross' },
formatter: (params: any) => {
if (!params?.length) return '';
const idx = params[0].dataIndex;
const fullTime = sorted[idx]?.tm
? dayjs(sorted[idx].tm).format('YYYY-MM-DD HH:mm')
: '';
let html = `<div style="font-size:16px;margin-bottom:8px;">${fullTime}</div>`;
params.forEach((p: any) => {
const isFlow = p.seriesName.includes('流量');
const v =
p.value != null
? isFlow
? Number(p.value).toFixed(3)
: Number(p.value).toFixed(1)
: '-';
const unit = isFlow ? 'm³/s' : '°C';
html += `<div style="display:flex;align-items:center;justify-content:space-between;margin:4px 0;"><span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:${p.color};margin-right:8px;"></span><span style="flex:1;font-size:14px;">${p.seriesName}: </span><span style="font-size:14px;min-width:60px;text-align:right;"><strong>${v}</strong> ${unit}</span></div>`;
});
return html;
}
},
legend: {
type: 'scroll',
width: '80%',
right: 60,
top: 10,
data: [
'出库水温',
'入库水温',
'天然水温',
'气温',
'出库流量',
'入库流量'
],
textStyle: { fontSize: 12 }
},
grid: { left: 60, right: 60, top: 80, bottom: 80 },
xAxis: {
type: 'category',
data: xAxisData,
axisLine: { lineStyle: { color: '#000000' } },
axisTick: { show: false },
axisLabel: axisLabelConfig,
splitLine: {
show: true,
lineStyle: { color: '#bfbfbf', type: 'solid' }
}
},
yAxis: [
{
name: '水温(°C)',
type: 'value',
position: 'left',
axisLine: { lineStyle: { color: '#000000' } },
scale: true,
splitLine: {
show: true,
lineStyle: { color: '#bfbfbf', type: 'solid' }
}
},
{
name: '流量(m³/s)',
type: 'value',
position: 'right',
axisLine: { lineStyle: { color: '#000000' } },
scale: true,
splitLine: { show: false }
}
],
series: [
{
name: '出库水温',
type: 'line',
yAxisIndex: 0,
data: sorted.map(item => item.wt),
smooth: true,
showSymbol: false,
lineStyle: { color: '#78C300', width: 2 },
itemStyle: { color: '#78C300' }
},
{
name: '入库水温',
type: 'line',
yAxisIndex: 0,
data: sorted.map(item => item?.iwtDataVo?.wt),
smooth: true,
showSymbol: false,
lineStyle: { color: '#9557A4', width: 2 },
itemStyle: { color: '#9557A4' }
},
{
name: '天然水温',
type: 'line',
yAxisIndex: 0,
data: sorted.map(item => item.natureTmp),
smooth: true,
showSymbol: false,
lineStyle: { color: '#DF91AB', width: 2 },
itemStyle: { color: '#DF91AB' }
},
{
name: '气温',
type: 'line',
yAxisIndex: 0,
data: sorted.map(item => item?.tmpDataVo?.at),
smooth: true,
showSymbol: false,
lineStyle: { color: '#7399C6', width: 2 },
itemStyle: { color: '#7399C6' }
},
{
name: '出库流量',
type: 'line',
yAxisIndex: 1,
data: sorted.map(item => item?.hydropwDataVo?.qo),
smooth: true,
showSymbol: false,
lineStyle: { color: '#56C2E3', width: 2 },
itemStyle: { color: '#56C2E3' }
},
{
name: '入库流量',
type: 'line',
yAxisIndex: 1,
data: sorted.map(item => item?.hydropwDataVo?.qi),
smooth: true,
showSymbol: false,
lineStyle: { color: '#DBB629', width: 2 },
itemStyle: { color: '#DBB629' }
}
],
dataZoom: [
{ type: 'inside', xAxisIndex: [0], throttle: 50, start: 0, end: 100 }
],
toolbox: {
show: true,
feature: {
saveAsImage: { title: '保存为图片', type: 'png', pixelRatio: 2 }
},
right: 20,
top: 10
}
};
}
chartInstance.setOption(option, true);
};
// ==================== 数据请求 ====================
const isQxdMode = computed(() => props.code === 'qxd.tabs.jcsj');
const fetchData = async () => {
if (!dateRange.value) return;
// qxd模式必须有stcd才能请求
if (isQxdMode.value && !props.stcd) {
return;
}
isLoading.value = true;
try {
// 确定使用的stcdqxd模式使用props.stcd否则使用modelStore.params.stcd
const currentStcd = isQxdMode.value ? props.stcd : modelStore.params.stcd;
const filterParams = {
filter: {
logic: 'and',
filters: [
{
field: 'stcd',
operator: 'eq',
dataType: 'string',
value: currentStcd
},
{
field: 'tm',
operator: 'gte',
dataType: 'date',
value: dateRange.value[0].format('YYYY-MM-DD HH:mm:ss')
},
{
field: 'tm',
operator: 'lte',
dataType: 'date',
value: dateRange.value[1].format('YYYY-MM-DD HH:mm:ss')
}
]
},
sort: [{ field: 'tm', dir: 'asc' }]
};
// qxd模式只调用getMonitorDataWaterTemp
if (isQxdMode.value) {
const mainRes = await getMonitorDataWaterTemp(filterParams);
const mainData = mainRes?.data?.data || mainRes?.data?.records || [];
showSummaryCheckbox.value = false;
showDeepCheckbox.value = false;
selectedColumns.value = [];
tableData.value = [...mainData].reverse();
chartData.value = mainData;
nextTick(() => updateChart(mainData));
} else {
// 原有模式
const [mainRes, powerStationRes, powerStationRes2] = await Promise.all([
getMonitorDataWaterTemp(filterParams),
getMonitorDataWaterTempPowerStation({ stcd: currentStcd }),
getMonitorDataWaterTempPowerStation2({ stcd: currentStcd })
]);
const mainData = mainRes?.data?.data || mainRes?.data?.records || [];
showSummaryCheckbox.value = !!powerStationRes?.data?.ioWtrv;
const powerStation2Data =
powerStationRes2?.data?.data || powerStationRes2?.data || null;
showDeepCheckbox.value =
!!powerStationRes?.data?.hasRstcdWtvt && powerStation2Data !== null;
tableData.value = [...mainData].reverse();
chartData.value = mainData;
nextTick(() => updateChart(mainData));
}
} catch (error) {
console.error('获取数据失败:', error);
tableData.value = [];
chartData.value = [];
if (chartInstance) chartInstance.clear();
} finally {
isLoading.value = false;
}
};
const handleSearch = () => fetchData();
const handleExport = () => {
/* 导出逻辑 */
};
const initData = () => {
if (hasLoaded.value) return;
nextTick(() => {
if (chartRef.value && !chartInstance)
chartInstance = echarts.init(chartRef.value);
});
fetchData();
hasLoaded.value = true;
};
watch(
() => props.isActive,
active => {
if (active && !hasLoaded.value) initData();
},
{ immediate: true }
);
watch(
() => modelStore.params.stcd,
(newStcd, oldStcd) => {
// 非qxd模式才监听store的stcd变化
if (!isQxdMode.value && newStcd && newStcd !== oldStcd) fetchData();
}
);
// qxd模式下监听props.stcd变化
watch(
() => props.stcd,
(newStcd, oldStcd) => {
if (isQxdMode.value && newStcd && newStcd !== oldStcd) {
hasLoaded.value = false;
initData();
}
}
);
// ResizeObserver
let resizeObserver: ResizeObserver | null = null;
watch(
() => props.isActive,
active => {
if (active && chartRef.value) {
setTimeout(() => {
if (chartInstance) chartInstance.resize();
}, 200);
if (!resizeObserver) {
resizeObserver = new ResizeObserver(() => {
if (chartInstance) chartInstance.resize();
});
resizeObserver.observe(chartRef.value);
}
}
}
);
const handleResize = () => {
if (chartInstance) chartInstance.resize();
};
onMounted(() => {
window.addEventListener('resize', handleResize);
});
onBeforeUnmount(() => {
if (chartInstance) {
chartInstance.dispose();
chartInstance = null;
}
window.removeEventListener('resize', handleResize);
if (resizeObserver) {
resizeObserver.disconnect();
resizeObserver = null;
}
});
</script>
<style lang="scss" scoped>
.water-temperature {
width: 100%;
min-height: 600px;
position: relative;
:deep(.ant-tabs-nav) {
margin-bottom: 4px;
}
.search-bar {
width: 100%;
margin-bottom: 12px;
display: flex;
align-items: center;
justify-content: flex-end;
.search-btn {
margin-left: 8px;
}
}
.tab-checkbox {
position: absolute;
top: 12px;
left: 30%;
z-index: 100;
}
.summary-tip-icon {
color: rgb(53, 169, 255);
margin-left: 4px;
}
.tab-content {
width: 100%;
.content-body {
width: 100%;
display: flex;
height: 600px;
position: relative;
.chart-wrapper {
flex: 1;
height: 100%;
position: relative;
box-sizing: border-box;
padding-top: 10px;
.chart-container {
width: 100%;
height: 100%;
}
}
.chart-empty {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.table-wrapper {
flex: 1;
min-width: 500px;
height: 100%;
box-sizing: border-box;
padding: 10px;
overflow: hidden;
overflow-x: auto;
border-left: 1px solid #dcdfe6;
margin-left: 10px;
}
}
}
}
.summary-row {
background-color: #fafafa;
}
</style>