2026-08-11 16:24:41 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<SidePanelItem title="水温监测数据" :shrink="false">
|
|
|
|
|
|
<template #title-right-content>
|
|
|
|
|
|
<a-range-picker
|
|
|
|
|
|
class="w-[220px]"
|
|
|
|
|
|
v-model:value="dateRange"
|
|
|
|
|
|
format="YYYY-MM-DD"
|
|
|
|
|
|
:show-time="showTimeConfig"
|
|
|
|
|
|
:allowClear="false"
|
|
|
|
|
|
:presets="DateSetting.RangeButton.month1"
|
|
|
|
|
|
:disabled-date="disabledDate"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
@change="handleSearch"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<a-spin :spinning="isLoading" tip="加载中...">
|
|
|
|
|
|
<div class="monitor-chart">
|
|
|
|
|
|
<div ref="chartRef" class="chart-container"></div>
|
|
|
|
|
|
<a-empty
|
|
|
|
|
|
v-if="!chartData || chartData.length === 0"
|
|
|
|
|
|
description="暂无数据"
|
|
|
|
|
|
class="chart-empty"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="monitor-table">
|
2026-09-03 16:47:28 +08:00
|
|
|
|
<a-spin :spinning="tableLoading" tip="加载中...">
|
|
|
|
|
|
<BasicTable
|
|
|
|
|
|
ref="tableRef"
|
|
|
|
|
|
:scrollY="240"
|
|
|
|
|
|
:scrollX="tableScrollX"
|
|
|
|
|
|
:columns="currentColumns"
|
|
|
|
|
|
:data="tableData"
|
|
|
|
|
|
:paginationConfig="{
|
|
|
|
|
|
showSizeChanger: false,
|
|
|
|
|
|
showQuickJumper: false
|
|
|
|
|
|
}"
|
|
|
|
|
|
@sort-change="handleSortChange"
|
|
|
|
|
|
>
|
|
|
|
|
|
<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"
|
2026-08-11 16:24:41 +08:00
|
|
|
|
>
|
2026-09-03 16:47:28 +08:00
|
|
|
|
{{ 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>
|
|
|
|
|
|
</a-spin>
|
2026-08-11 16:24:41 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</a-spin>
|
|
|
|
|
|
</SidePanelItem>
|
|
|
|
|
|
</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 { useUiStore } from '@/store/modules/ui';
|
|
|
|
|
|
import { getMonitorDataWaterTemp } from '@/api/mapModal';
|
|
|
|
|
|
import { useModelStore } from '@/store/modules/model';
|
|
|
|
|
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
|
|
|
|
|
import BasicTable from '@/components/BasicTable/index.vue';
|
2026-09-03 16:47:28 +08:00
|
|
|
|
import { useServerSortTableData } from '@/hooks/useServerSortTableData';
|
2026-08-11 16:24:41 +08:00
|
|
|
|
import { DateSetting } from '@/utils/enumeration';
|
|
|
|
|
|
import { InfoCircleOutlined } from '@ant-design/icons-vue';
|
|
|
|
|
|
|
|
|
|
|
|
const uiStore = useUiStore();
|
|
|
|
|
|
const modelStore = useModelStore();
|
|
|
|
|
|
|
|
|
|
|
|
const isLoading = ref(false);
|
|
|
|
|
|
const chartData = ref<any[]>([]);
|
|
|
|
|
|
const tableRef = ref<any>();
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 图表 ====================
|
|
|
|
|
|
const chartRef = ref<HTMLElement>();
|
|
|
|
|
|
let chartInstance: echarts.ECharts | null = null;
|
|
|
|
|
|
let resizeObserver: ResizeObserver | null = null;
|
|
|
|
|
|
|
|
|
|
|
|
const initChart = () => {
|
|
|
|
|
|
if (!chartRef.value) return;
|
|
|
|
|
|
if (chartInstance) chartInstance.dispose();
|
|
|
|
|
|
chartInstance = echarts.init(chartRef.value);
|
|
|
|
|
|
updateChart(chartData.value);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
|
|
const 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: 40, right: 20, top: 44, bottom: 50 },
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
chartInstance.setOption(option, true);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 数据变化时动态刷新图表
|
|
|
|
|
|
watch(chartData, newData => {
|
|
|
|
|
|
updateChart(newData);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const handleResize = () => {
|
|
|
|
|
|
if (chartInstance) chartInstance.resize();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
initChart();
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
if (chartInstance) chartInstance.resize();
|
|
|
|
|
|
}, 200);
|
|
|
|
|
|
if (!resizeObserver && chartRef.value) {
|
|
|
|
|
|
resizeObserver = new ResizeObserver(() => {
|
|
|
|
|
|
if (chartInstance) chartInstance.resize();
|
|
|
|
|
|
});
|
|
|
|
|
|
resizeObserver.observe(chartRef.value);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
window.addEventListener('resize', handleResize);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
onBeforeUnmount(() => {
|
|
|
|
|
|
if (chartInstance) {
|
|
|
|
|
|
chartInstance.dispose();
|
|
|
|
|
|
chartInstance = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
window.removeEventListener('resize', handleResize);
|
|
|
|
|
|
if (resizeObserver) {
|
|
|
|
|
|
resizeObserver.disconnect();
|
|
|
|
|
|
resizeObserver = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 时间选择器配置
|
|
|
|
|
|
const showTimeConfig = {
|
|
|
|
|
|
format: 'HH:mm',
|
|
|
|
|
|
hourStep: 1,
|
|
|
|
|
|
minuteStep: 5,
|
|
|
|
|
|
secondStep: 60
|
|
|
|
|
|
};
|
|
|
|
|
|
const disabledDate = (current: Dayjs) =>
|
|
|
|
|
|
current && current.isAfter(dayjs(), 'day');
|
|
|
|
|
|
|
|
|
|
|
|
const initDateRange = (): [Dayjs, Dayjs] => {
|
|
|
|
|
|
return [dayjs().subtract(1, 'month'), dayjs()];
|
|
|
|
|
|
};
|
|
|
|
|
|
const dateRange = ref<[Dayjs, Dayjs] | undefined>(initDateRange());
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 表格列配置 ====================
|
|
|
|
|
|
const baseWaterTempColumns = [
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '时间',
|
|
|
|
|
|
dataIndex: 'tm',
|
|
|
|
|
|
fixed: 'left',
|
2026-09-03 16:47:28 +08:00
|
|
|
|
sort: true,
|
2026-08-11 16:24:41 +08:00
|
|
|
|
customRender: ({ text }: any) =>
|
|
|
|
|
|
text ? dayjs(text).format('YYYY-MM-DD HH:mm') : '-'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '水温(°C)',
|
|
|
|
|
|
dataIndex: 'wt',
|
|
|
|
|
|
summary: true,
|
2026-09-03 16:47:28 +08:00
|
|
|
|
sort: true,
|
2026-08-11 16:24:41 +08:00
|
|
|
|
customRender: ({ text }: any) =>
|
|
|
|
|
|
text !== undefined && text !== null ? Number(text).toFixed(1) : '-'
|
|
|
|
|
|
}
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
const currentColumns = baseWaterTempColumns;
|
|
|
|
|
|
|
|
|
|
|
|
const tableScrollX = computed(() => {
|
|
|
|
|
|
const columns = currentColumns;
|
|
|
|
|
|
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'
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const summaryColumns = computed(() => {
|
|
|
|
|
|
return currentColumns
|
|
|
|
|
|
.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;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 数据请求 ====================
|
2026-09-03 16:47:28 +08:00
|
|
|
|
// 构造表格/图表请求参数(stcd + 时间范围),排序传入时叠加 sort
|
|
|
|
|
|
const buildSearchParams = (sort?: { field: string; dir: 'asc' | 'desc' }) => {
|
2026-08-11 16:24:41 +08:00
|
|
|
|
const stcd = modelStore.selectedAnchorPoint?.stcd;
|
2026-09-03 16:47:28 +08:00
|
|
|
|
if (!dateRange.value || !stcd) return null;
|
|
|
|
|
|
return {
|
|
|
|
|
|
filter: {
|
|
|
|
|
|
logic: 'and',
|
|
|
|
|
|
filters: [
|
|
|
|
|
|
{
|
|
|
|
|
|
field: 'stcd',
|
|
|
|
|
|
operator: 'eq',
|
|
|
|
|
|
dataType: 'string',
|
|
|
|
|
|
value: stcd
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
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: sort ? [sort] : [{ field: 'tm', dir: 'asc' }]
|
|
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// data 模式表格:点击排序时重新请求接口(服务端排序)
|
|
|
|
|
|
const {
|
|
|
|
|
|
tableData,
|
|
|
|
|
|
isLoading: tableLoading,
|
|
|
|
|
|
handleSortChange
|
|
|
|
|
|
} = useServerSortTableData<any>({
|
|
|
|
|
|
request: getMonitorDataWaterTemp,
|
|
|
|
|
|
buildSearchParams
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const fetchData = async () => {
|
|
|
|
|
|
const params = buildSearchParams();
|
|
|
|
|
|
if (!params) return;
|
2026-08-11 16:24:41 +08:00
|
|
|
|
isLoading.value = true;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
2026-09-03 16:47:28 +08:00
|
|
|
|
const res = await getMonitorDataWaterTemp(params);
|
2026-08-11 16:24:41 +08:00
|
|
|
|
const rawData = res?.data?.data || res?.data?.records || [];
|
|
|
|
|
|
|
|
|
|
|
|
chartData.value = rawData;
|
|
|
|
|
|
tableData.value = [...rawData].reverse();
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取数据失败:', error);
|
|
|
|
|
|
chartData.value = [];
|
|
|
|
|
|
tableData.value = [];
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
isLoading.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 时间选择器 change 触发查询
|
|
|
|
|
|
const handleSearch = () => fetchData();
|
|
|
|
|
|
watch(
|
|
|
|
|
|
() => uiStore.searchDrawerOpen,
|
|
|
|
|
|
newVal => {
|
|
|
|
|
|
if (!newVal) {
|
|
|
|
|
|
dateRange.value = initDateRange();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
|
|
|
|
|
// 选中点位变化时刷新;初始已有点位时立即加载
|
|
|
|
|
|
watch(
|
|
|
|
|
|
() => modelStore.selectedAnchorPoint?.stcd,
|
|
|
|
|
|
(newStcd, oldStcd) => {
|
|
|
|
|
|
if (!newStcd) return;
|
|
|
|
|
|
if (newStcd !== oldStcd) fetchData();
|
|
|
|
|
|
},
|
|
|
|
|
|
{ immediate: true }
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// 全局时间范围变化时同步本地日期选择器(默认参数联动)
|
|
|
|
|
|
watch(
|
|
|
|
|
|
() => modelStore.filter.rangeTm,
|
|
|
|
|
|
newRange => {
|
|
|
|
|
|
if (Array.isArray(newRange) && newRange.length > 0) {
|
|
|
|
|
|
dateRange.value = [dayjs(newRange[0]), dayjs(newRange[1])];
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
{ deep: true }
|
|
|
|
|
|
);
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
.monitor-chart {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 300px;
|
|
|
|
|
|
|
|
|
|
|
|
.chart-container {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.chart-empty {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 50%;
|
|
|
|
|
|
left: 50%;
|
|
|
|
|
|
transform: translate(-50%, -50%);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.monitor-table {
|
|
|
|
|
|
margin-top: 12px;
|
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
.table-container {
|
|
|
|
|
|
border: 1px solid #dcdfe6;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.summary-tip-icon) {
|
|
|
|
|
|
color: rgb(53, 169, 255);
|
|
|
|
|
|
margin-left: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.summary-row {
|
|
|
|
|
|
background-color: #fafafa;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|