2026-05-12 14:34:58 +08:00
|
|
|
|
<!-- SidePanelItem.vue -->
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
2026-06-01 08:37:38 +08:00
|
|
|
|
<SidePanelItem title="沿程水质变化" :iconmap="iconmap" :select="select" :datetimePicker="datetimePicker"
|
|
|
|
|
|
@update-values="handlePanelChange1">
|
|
|
|
|
|
<div class="chart-wrapper">
|
|
|
|
|
|
<a-spin :spinning="loading">
|
|
|
|
|
|
<!-- 始终渲染图表容器,确保有固定宽高 -->
|
|
|
|
|
|
<div ref="chartRef" class="chart-container"></div>
|
|
|
|
|
|
<!-- 无数据时显示Empty,但不影响容器尺寸 -->
|
|
|
|
|
|
<div v-if="!loading && !hasData" class="empty-overlay">
|
|
|
|
|
|
<a-empty description="暂无数据" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</a-spin>
|
|
|
|
|
|
</div>
|
2026-05-12 14:34:58 +08:00
|
|
|
|
</SidePanelItem>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
2026-06-01 08:37:38 +08:00
|
|
|
|
import { ref, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
|
2026-05-12 14:34:58 +08:00
|
|
|
|
import * as echarts from 'echarts';
|
|
|
|
|
|
import type { EChartsOption } from 'echarts';
|
|
|
|
|
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
2026-06-01 08:37:38 +08:00
|
|
|
|
import { qgcGetKendoListCust, wbsbGetKendoList } from '@/api/sz'
|
|
|
|
|
|
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
|
|
|
|
|
import { useModelStore } from "@/store/modules/model";
|
2026-05-12 14:34:58 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 定义组件名(便于调试和递归)
|
|
|
|
|
|
defineOptions({
|
|
|
|
|
|
name: 'waterQuality'
|
|
|
|
|
|
});
|
2026-06-01 08:37:38 +08:00
|
|
|
|
const modelStore = useModelStore();
|
|
|
|
|
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
2026-05-15 18:08:29 +08:00
|
|
|
|
const iconmap = ref({
|
|
|
|
|
|
show: true,
|
|
|
|
|
|
value: '注:最新数据时间为2026-05-14 23:00',
|
|
|
|
|
|
icon: 'iconfont icon-time',
|
|
|
|
|
|
});
|
2026-05-12 14:34:58 +08:00
|
|
|
|
// ==================== 响应式数据 ====================
|
|
|
|
|
|
const chartRef = ref<HTMLElement | null>(null);
|
|
|
|
|
|
let chartInstance: echarts.ECharts | null = null;
|
|
|
|
|
|
|
2026-06-01 08:37:38 +08:00
|
|
|
|
// Loading 状态和数据状态
|
|
|
|
|
|
const loading = ref(false);
|
|
|
|
|
|
const hasData = ref(true); // 初始化为 true,因为 initChart 会使用 mockData 渲染
|
|
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
// 获取当天早上8:00的时间
|
|
|
|
|
|
const now = new Date();
|
|
|
|
|
|
const todayAtEightAM = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 8, 0, 0);
|
|
|
|
|
|
const defaultValue = `${todayAtEightAM.getFullYear()}-${String(todayAtEightAM.getMonth() + 1).padStart(2, '0')}-${String(todayAtEightAM.getDate()).padStart(2, '0')} ${String(todayAtEightAM.getHours()).padStart(2, '0')}:${String(todayAtEightAM.getMinutes()).padStart(2, '0')}`;
|
|
|
|
|
|
|
|
|
|
|
|
const datetimePicker = ref({
|
|
|
|
|
|
show: true,
|
|
|
|
|
|
value: defaultValue,
|
|
|
|
|
|
format: 'YYYY-MM-DD hh:mm',
|
|
|
|
|
|
picker: 'date',
|
|
|
|
|
|
options: []
|
|
|
|
|
|
});
|
2026-06-01 08:37:38 +08:00
|
|
|
|
const select = ref({
|
|
|
|
|
|
show: true,
|
|
|
|
|
|
value: '',
|
|
|
|
|
|
options: [],
|
|
|
|
|
|
picker: undefined,
|
|
|
|
|
|
format: undefined,
|
|
|
|
|
|
});
|
2026-05-12 14:34:58 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 可见系列队列(最多2个)====================
|
|
|
|
|
|
const visibleSeriesQueue = ref<string[]>([]);
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 模拟数据 ====================
|
|
|
|
|
|
const generateMockData = () => {
|
|
|
|
|
|
const stations = [
|
|
|
|
|
|
{ stnm: '两河口', stcd: 'ST001', type: '0' },
|
|
|
|
|
|
{ stnm: '锦屏一级', stcd: 'ST002', type: '1' },
|
|
|
|
|
|
{ stnm: '桐子林', stcd: 'ST003', type: '0' },
|
|
|
|
|
|
{ stnm: '溪洛渡', stcd: 'ST004', type: '0' },
|
|
|
|
|
|
{ stnm: '向家坝', stcd: 'ST005', type: '1' },
|
|
|
|
|
|
{ stnm: '三峡', stcd: 'ST006', type: '0' },
|
|
|
|
|
|
{ stnm: '葛洲坝', stcd: 'ST007', type: '0' }
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
const tm = '2024-01-15 08:00:00';
|
|
|
|
|
|
|
|
|
|
|
|
return stations.map((station, index) => ({
|
|
|
|
|
|
...station,
|
|
|
|
|
|
tm,
|
|
|
|
|
|
SORT: index + 1,
|
|
|
|
|
|
ph: 7.2 + Math.random() * 0.8,
|
|
|
|
|
|
dox: 6.5 + Math.random() * 2.5,
|
|
|
|
|
|
codmn: 1.5 + Math.random() * 2.0,
|
|
|
|
|
|
nh3n: 0.15 + Math.random() * 0.35,
|
|
|
|
|
|
tp: 0.05 + Math.random() * 0.15,
|
|
|
|
|
|
tn: 0.8 + Math.random() * 1.2
|
|
|
|
|
|
}));
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const mockData = ref(generateMockData());
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 指标配置 ====================
|
|
|
|
|
|
const indicatorConfig = [
|
|
|
|
|
|
{ key: 'ph', name: 'pH', unit: '', color: '#5470C6' },
|
|
|
|
|
|
{ key: 'dox', name: '溶解氧', unit: 'mg/L', color: '#91CC75' },
|
|
|
|
|
|
{ key: 'codmn', name: '高锰酸盐指数', unit: 'mg/L', color: '#FAC858' },
|
|
|
|
|
|
{ key: 'nh3n', name: '氨氮', unit: 'mg/L', color: '#EE6666' },
|
|
|
|
|
|
{ key: 'tp', name: '总磷', unit: 'mg/L', color: '#73C0DE' },
|
|
|
|
|
|
{ key: 'tn', name: '总氮', unit: 'mg/L', color: '#3BA272' }
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 图表配置生成 ====================
|
|
|
|
|
|
const getChartOption = (): EChartsOption => {
|
|
|
|
|
|
const data = mockData.value;
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
if (!data || data.length === 0) {
|
|
|
|
|
|
return {};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// X轴数据(站点名称)
|
2026-06-01 08:37:38 +08:00
|
|
|
|
const xData = data.map((item, index) => {
|
|
|
|
|
|
// 去除站点名称中的"出库水质监测站"或"坝上水质监测站"后缀
|
|
|
|
|
|
let stnm = item.stnm || '-';
|
|
|
|
|
|
if (stnm.includes('出库水质监测站')) {
|
|
|
|
|
|
stnm = stnm.replace('出库水质监测站', '');
|
|
|
|
|
|
} else if (stnm.includes('坝上水质监测站')) {
|
|
|
|
|
|
stnm = stnm.replace('坝上水质监测站', '');
|
2026-05-12 14:34:58 +08:00
|
|
|
|
}
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
value: stnm,
|
|
|
|
|
|
textStyle: {
|
|
|
|
|
|
padding: index % 2 !== 0 ? [16, 0, 0, 0] : 0
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
2026-05-12 14:34:58 +08:00
|
|
|
|
|
|
|
|
|
|
// 电站标记线数据
|
|
|
|
|
|
const markLineData: any[] = [];
|
|
|
|
|
|
data.forEach((item, index) => {
|
|
|
|
|
|
if (item.type === '1') {
|
|
|
|
|
|
markLineData.push({ xAxis: index });
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 图例数据
|
|
|
|
|
|
const legendData = indicatorConfig.map(item => item.name);
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
// 根据可见队列构建选中状态
|
|
|
|
|
|
const selectedState: Record<string, boolean> = {};
|
|
|
|
|
|
indicatorConfig.forEach(config => {
|
|
|
|
|
|
selectedState[config.name] = visibleSeriesQueue.value.includes(config.name);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Series 数据
|
|
|
|
|
|
const seriesData = indicatorConfig.map((config, index) => {
|
|
|
|
|
|
const _key = config.key === 'dox' ? 'do' : config.key;
|
|
|
|
|
|
return {
|
|
|
|
|
|
name: config.name,
|
|
|
|
|
|
type: 'line' as const,
|
|
|
|
|
|
yAxisIndex: index,
|
|
|
|
|
|
smooth: true,
|
|
|
|
|
|
connectNulls: true,
|
|
|
|
|
|
symbol: 'circle' as const,
|
|
|
|
|
|
symbolSize: 6,
|
|
|
|
|
|
itemStyle: {
|
|
|
|
|
|
color: config.color
|
|
|
|
|
|
},
|
|
|
|
|
|
data: data.map(item => {
|
|
|
|
|
|
const value = item[config.key as keyof typeof item];
|
|
|
|
|
|
return typeof value === 'number' && !isNaN(value) ? value : null;
|
|
|
|
|
|
}),
|
|
|
|
|
|
markLine: index === 0 ? {
|
|
|
|
|
|
symbol: ['none', 'none'],
|
|
|
|
|
|
label: { show: false },
|
|
|
|
|
|
lineStyle: {
|
|
|
|
|
|
color: '#ccc',
|
|
|
|
|
|
type: 'dashed' as const
|
|
|
|
|
|
},
|
|
|
|
|
|
data: markLineData
|
|
|
|
|
|
} : undefined
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-06-01 08:37:38 +08:00
|
|
|
|
// 计算每个Y轴的数据范围(用于自适应,使用整数)
|
|
|
|
|
|
const calculateYAxisRange = (configIndex: number) => {
|
|
|
|
|
|
if (!selectedState[indicatorConfig[configIndex].name]) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const values = data
|
|
|
|
|
|
.map(item => item[indicatorConfig[configIndex].key as keyof typeof item])
|
|
|
|
|
|
.filter((val): val is number => typeof val === 'number' && !isNaN(val));
|
|
|
|
|
|
|
|
|
|
|
|
if (values.length === 0) {
|
|
|
|
|
|
return { min: 0, max: 10 };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const min = Math.min(...values);
|
|
|
|
|
|
const max = Math.max(...values);
|
|
|
|
|
|
|
|
|
|
|
|
// Y轴使用整数:向下取整-1,向上取整+1
|
|
|
|
|
|
return {
|
|
|
|
|
|
min: Math.floor(min) - 1,
|
|
|
|
|
|
max: Math.ceil(max) + 1
|
|
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
// Y轴配置(左1右N原则)
|
|
|
|
|
|
const yAxisData = indicatorConfig.map((config, index) => {
|
|
|
|
|
|
const isLeft = index === 0;
|
|
|
|
|
|
const offset = index < 2 ? 0 : (index - 1) * 60;
|
2026-06-01 08:37:38 +08:00
|
|
|
|
const range = calculateYAxisRange(index);
|
|
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
return {
|
|
|
|
|
|
type: 'value' as const,
|
|
|
|
|
|
name: config.name,
|
|
|
|
|
|
position: isLeft ? ('left' as const) : ('right' as const),
|
|
|
|
|
|
offset,
|
2026-06-01 08:37:38 +08:00
|
|
|
|
min: range ? range.min : undefined,
|
|
|
|
|
|
max: range ? range.max : undefined,
|
2026-05-12 14:34:58 +08:00
|
|
|
|
axisLine: {
|
|
|
|
|
|
show: true,
|
|
|
|
|
|
lineStyle: {
|
|
|
|
|
|
color: config.color
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
axisLabel: {
|
|
|
|
|
|
color: config.color,
|
|
|
|
|
|
formatter: (value: number) => {
|
2026-06-01 08:37:38 +08:00
|
|
|
|
// Y轴刻度使用整数显示
|
|
|
|
|
|
return Math.round(value).toString();
|
2026-05-12 14:34:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
splitLine: {
|
|
|
|
|
|
show: true,
|
|
|
|
|
|
lineStyle: {
|
|
|
|
|
|
color: '#e0e0e0',
|
|
|
|
|
|
type: 'solid' as const
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
show: selectedState[config.name]
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const option: EChartsOption = {
|
|
|
|
|
|
tooltip: {
|
|
|
|
|
|
trigger: 'axis',
|
|
|
|
|
|
confine: true,
|
|
|
|
|
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
|
|
|
|
borderColor: 'transparent',
|
|
|
|
|
|
textStyle: {
|
|
|
|
|
|
color: '#ffffff'
|
|
|
|
|
|
},
|
|
|
|
|
|
formatter: (params: any) => {
|
|
|
|
|
|
if (!params || params.length === 0) return '';
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
|
|
|
|
|
// 在最上面显示用户选择的时间
|
|
|
|
|
|
let result = `<div style="font-weight: bold; margin-bottom: 8px;">${datetimePicker.value.value}</div>`;
|
|
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
const tm = data.find(item => item.stnm === params[0].name)?.tm;
|
|
|
|
|
|
result += `${params[0].name}<br/>`;
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
params.forEach((param: any) => {
|
|
|
|
|
|
const config = indicatorConfig.find(c => c.name === param.seriesName);
|
|
|
|
|
|
const unit = config?.unit || '';
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
if (param.value !== null && param.value !== undefined) {
|
2026-06-01 08:37:38 +08:00
|
|
|
|
// 折线数据保留一位小数
|
2026-05-12 14:34:58 +08:00
|
|
|
|
const numValue = Number(param.value);
|
|
|
|
|
|
let displayValue = param.value;
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
if (!isNaN(numValue)) {
|
2026-06-01 08:37:38 +08:00
|
|
|
|
displayValue = numValue.toFixed(1);
|
2026-05-12 14:34:58 +08:00
|
|
|
|
}
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
result += `${param.marker}${param.seriesName}:${displayValue}${unit}<br/>`;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
legend: {
|
|
|
|
|
|
type: 'scroll',
|
|
|
|
|
|
top: 0,
|
|
|
|
|
|
data: legendData,
|
|
|
|
|
|
selected: selectedState,
|
|
|
|
|
|
inactiveColor: '#ccc',
|
|
|
|
|
|
itemWidth: 16,
|
|
|
|
|
|
itemHeight: 12,
|
|
|
|
|
|
itemGap: 8,
|
|
|
|
|
|
textStyle: {
|
|
|
|
|
|
fontSize: 14
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-06-01 08:37:38 +08:00
|
|
|
|
// 添加 dataZoom 组件实现X轴滚轮缩放(仅内置类型,无滑动条)
|
|
|
|
|
|
dataZoom: [
|
|
|
|
|
|
{
|
|
|
|
|
|
type: 'inside', // 内置型数据区域缩放组件(支持鼠标滚轮)
|
|
|
|
|
|
xAxisIndex: 0, // 控制第一个X轴
|
|
|
|
|
|
start: 0, // 默认显示全部数据
|
|
|
|
|
|
end: 100,
|
|
|
|
|
|
zoomOnMouseWheel: true, // 开启鼠标滚轮缩放
|
|
|
|
|
|
moveOnMouseMove: true, // 开启鼠标移动平移
|
|
|
|
|
|
filterMode: 'filter' // 过滤模式
|
|
|
|
|
|
}
|
|
|
|
|
|
],
|
2026-05-12 14:34:58 +08:00
|
|
|
|
grid: {
|
|
|
|
|
|
top: 60,
|
|
|
|
|
|
bottom: 50,
|
|
|
|
|
|
left: '20px',
|
|
|
|
|
|
right: '20px',
|
|
|
|
|
|
containLabel: true
|
|
|
|
|
|
},
|
|
|
|
|
|
xAxis: {
|
|
|
|
|
|
type: 'category',
|
|
|
|
|
|
data: xData,
|
|
|
|
|
|
axisLabel: {
|
|
|
|
|
|
interval: 0,
|
|
|
|
|
|
color: '#333',
|
|
|
|
|
|
fontSize: 12
|
|
|
|
|
|
},
|
|
|
|
|
|
axisLine: {
|
|
|
|
|
|
lineStyle: {
|
|
|
|
|
|
color: '#8f8f8f'
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
splitLine: {
|
|
|
|
|
|
show: true,
|
|
|
|
|
|
lineStyle: {
|
|
|
|
|
|
color: '#e0e0e0',
|
|
|
|
|
|
type: 'solid' as const
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
yAxis: yAxisData,
|
|
|
|
|
|
series: seriesData
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return option;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 图例选择事件处理(最多2个系列)====================
|
|
|
|
|
|
const handleLegendSelectChanged = (params: any) => {
|
|
|
|
|
|
if (!chartInstance) return;
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
const { selected, name } = params;
|
|
|
|
|
|
const clickedName = name;
|
|
|
|
|
|
const isNowSelected = selected[clickedName];
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
console.log(`图例点击: ${clickedName}, 当前状态: ${isNowSelected ? '显示' : '隐藏'}`);
|
|
|
|
|
|
console.log('当前可见队列:', visibleSeriesQueue.value);
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
if (isNowSelected) {
|
|
|
|
|
|
// 用户想要显示这个系列
|
|
|
|
|
|
if (visibleSeriesQueue.value.length >= 2) {
|
|
|
|
|
|
// 已满2个,移除最早的(队列头部)
|
|
|
|
|
|
const removed = visibleSeriesQueue.value.shift();
|
|
|
|
|
|
console.log(`已达上限,移除最早系列: ${removed}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
// 添加到队列尾部
|
|
|
|
|
|
if (!visibleSeriesQueue.value.includes(clickedName)) {
|
|
|
|
|
|
visibleSeriesQueue.value.push(clickedName);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 用户想要隐藏这个系列
|
|
|
|
|
|
const index = visibleSeriesQueue.value.indexOf(clickedName);
|
|
|
|
|
|
if (index > -1) {
|
|
|
|
|
|
visibleSeriesQueue.value.splice(index, 1);
|
|
|
|
|
|
console.log(`从队列中移除: ${clickedName}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
console.log('更新后可见队列:', visibleSeriesQueue.value);
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
// 根据队列构建最终的 selected 状态
|
|
|
|
|
|
const finalSelected: Record<string, boolean> = {};
|
|
|
|
|
|
indicatorConfig.forEach(config => {
|
|
|
|
|
|
finalSelected[config.name] = visibleSeriesQueue.value.includes(config.name);
|
|
|
|
|
|
});
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
// 深拷贝当前配置
|
|
|
|
|
|
const currentOption = JSON.parse(JSON.stringify(chartInstance.getOption()));
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
// 同步图例选中状态
|
|
|
|
|
|
currentOption.legend[0].selected = finalSelected;
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
// 根据图例状态更新 Y 轴显隐
|
|
|
|
|
|
const newYAxis = currentOption.yAxis.map((item: any) => {
|
|
|
|
|
|
let isShow = false;
|
|
|
|
|
|
for (const key in finalSelected) {
|
|
|
|
|
|
if (key === item.name) {
|
|
|
|
|
|
isShow = finalSelected[key];
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return { ...item, show: isShow };
|
|
|
|
|
|
});
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
currentOption.yAxis = newYAxis;
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
// 动态调整 Y 轴布局
|
|
|
|
|
|
yAxisShowDynamic(finalSelected, currentOption);
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
// 完全替换模式更新
|
|
|
|
|
|
try {
|
|
|
|
|
|
chartInstance.setOption(currentOption, true);
|
|
|
|
|
|
console.log('图表配置已更新');
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('图表更新失败:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-06-01 08:37:38 +08:00
|
|
|
|
// ==================== 数据点点击事件处理 ====================
|
|
|
|
|
|
const handleDataPointClick = (params: any) => {
|
|
|
|
|
|
if (!params || !params.dataIndex) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取点击的数据点对应的站点信息
|
|
|
|
|
|
const dataIndex = params.dataIndex;
|
|
|
|
|
|
const stationData = mockData.value[dataIndex];
|
|
|
|
|
|
|
|
|
|
|
|
if (stationData && stationData.stcd) {
|
|
|
|
|
|
modelStore.modalVisible = true;
|
|
|
|
|
|
modelStore.params.sttp = "fh_wq_point";
|
|
|
|
|
|
modelStore.title = stationData.stnm + "详情信息";
|
|
|
|
|
|
// modelStore.isBasicEdit = true;
|
|
|
|
|
|
modelStore.params.stcd = stationData.stcd;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
// ==================== Y轴动态布局调整 ====================
|
|
|
|
|
|
const yAxisShowDynamic = (selected: Record<string, boolean>, options: any) => {
|
|
|
|
|
|
const allShow = options.yAxis?.filter((item: any) => item.show);
|
|
|
|
|
|
const showCount = allShow?.length || 0;
|
|
|
|
|
|
|
|
|
|
|
|
// 没有显示的Y轴
|
|
|
|
|
|
if (showCount === 0) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 只有一个Y轴:统一置于左侧
|
|
|
|
|
|
if (showCount === 1) {
|
|
|
|
|
|
delete options.grid.right;
|
|
|
|
|
|
options.grid.left = '80px';
|
|
|
|
|
|
|
|
|
|
|
|
options.yAxis = options.yAxis.map((item: any) => {
|
|
|
|
|
|
if (item.show) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
...item,
|
|
|
|
|
|
position: 'left',
|
|
|
|
|
|
offset: 0
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
return item;
|
|
|
|
|
|
});
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 两个及以上Y轴:左侧1个,右侧其余
|
|
|
|
|
|
if (showCount >= 2) {
|
|
|
|
|
|
let leftIndex = 0;
|
|
|
|
|
|
let rightIndex = 0;
|
|
|
|
|
|
|
|
|
|
|
|
options.yAxis = options.yAxis.map((item: any) => {
|
|
|
|
|
|
if (!item.show) {
|
|
|
|
|
|
return item;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 第一个可见的Y轴放在左侧
|
|
|
|
|
|
if (leftIndex === 0) {
|
|
|
|
|
|
leftIndex++;
|
|
|
|
|
|
delete options.grid.right;
|
|
|
|
|
|
options.grid.left = '80px';
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
...item,
|
|
|
|
|
|
position: 'left',
|
|
|
|
|
|
offset: 0
|
|
|
|
|
|
};
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 其余Y轴放在右侧
|
|
|
|
|
|
rightIndex++;
|
|
|
|
|
|
|
|
|
|
|
|
if (rightIndex > 1) {
|
|
|
|
|
|
options.grid.right = '100px';
|
|
|
|
|
|
return {
|
|
|
|
|
|
...item,
|
|
|
|
|
|
position: 'right',
|
|
|
|
|
|
offset: 60
|
|
|
|
|
|
};
|
|
|
|
|
|
} else {
|
|
|
|
|
|
options.grid.right = '60px';
|
|
|
|
|
|
return {
|
|
|
|
|
|
...item,
|
|
|
|
|
|
position: 'right',
|
|
|
|
|
|
offset: 0
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 图表初始化 ====================
|
|
|
|
|
|
const initChart = async () => {
|
|
|
|
|
|
if (!chartRef.value) {
|
|
|
|
|
|
console.warn('图表容器未就绪');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查容器尺寸
|
|
|
|
|
|
const rect = chartRef.value.getBoundingClientRect();
|
|
|
|
|
|
if (rect.width === 0 || rect.height === 0) {
|
|
|
|
|
|
console.warn('图表容器尺寸为0,等待渲染...');
|
2026-06-01 08:37:38 +08:00
|
|
|
|
// 使用 requestAnimationFrame 确保在浏览器下一重绘帧再初始化
|
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
initChart().then(resolve);
|
|
|
|
|
|
}, 50);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果图表实例已存在,先销毁
|
|
|
|
|
|
if (chartInstance) {
|
|
|
|
|
|
chartInstance.dispose();
|
|
|
|
|
|
chartInstance = null;
|
2026-05-12 14:34:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化可见队列:默认只显示第一个系列
|
|
|
|
|
|
visibleSeriesQueue.value = [indicatorConfig[0].name];
|
|
|
|
|
|
console.log('初始化可见队列:', visibleSeriesQueue.value);
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化 ECharts 实例
|
|
|
|
|
|
chartInstance = echarts.init(chartRef.value);
|
|
|
|
|
|
|
|
|
|
|
|
// 设置初始配置
|
|
|
|
|
|
const option = getChartOption();
|
|
|
|
|
|
chartInstance.setOption(option);
|
|
|
|
|
|
|
|
|
|
|
|
// 监听图例选择变化事件
|
|
|
|
|
|
chartInstance.on('legendselectchanged', handleLegendSelectChanged);
|
|
|
|
|
|
|
2026-06-01 08:37:38 +08:00
|
|
|
|
// 监听数据点点击事件
|
|
|
|
|
|
chartInstance.on('click', handleDataPointClick);
|
|
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
// 强制 resize 确保正确渲染
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
chartInstance?.resize();
|
|
|
|
|
|
}, 50);
|
|
|
|
|
|
|
|
|
|
|
|
console.log('图表初始化成功');
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 窗口大小变化处理 ====================
|
|
|
|
|
|
const handleResize = () => {
|
|
|
|
|
|
chartInstance?.resize();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 生命周期钩子 ====================
|
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
|
await nextTick();
|
|
|
|
|
|
// 增加额外延迟确保容器完全渲染
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
initChart();
|
|
|
|
|
|
}, 50);
|
|
|
|
|
|
|
|
|
|
|
|
window.addEventListener('resize', handleResize);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
onBeforeUnmount(() => {
|
|
|
|
|
|
window.removeEventListener('resize', handleResize);
|
2026-06-01 08:37:38 +08:00
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
if (chartInstance) {
|
|
|
|
|
|
chartInstance.off('legendselectchanged', handleLegendSelectChanged);
|
2026-06-01 08:37:38 +08:00
|
|
|
|
chartInstance.off('click', handleDataPointClick);
|
2026-05-12 14:34:58 +08:00
|
|
|
|
chartInstance.dispose();
|
|
|
|
|
|
chartInstance = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-06-01 08:37:38 +08:00
|
|
|
|
//获取图参数
|
|
|
|
|
|
const getecharts = async () => {
|
|
|
|
|
|
if (!select.value.value) {
|
|
|
|
|
|
console.warn('选择器值为空,无法获取图表数据');
|
|
|
|
|
|
loading.value = false;
|
|
|
|
|
|
hasData.value = false;
|
|
|
|
|
|
chartInstance?.clear();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log('开始获取图表数据,select.value:', select.value.value);
|
|
|
|
|
|
|
|
|
|
|
|
// 设置 loading 状态
|
|
|
|
|
|
loading.value = true;
|
|
|
|
|
|
hasData.value = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 确保图表实例存在
|
|
|
|
|
|
if (!chartInstance) {
|
|
|
|
|
|
console.log('图表实例不存在,重新初始化...');
|
|
|
|
|
|
await nextTick();
|
|
|
|
|
|
await initChart();
|
|
|
|
|
|
// 等待图表初始化完成
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let params = {
|
|
|
|
|
|
"filter": {
|
|
|
|
|
|
"logic": "and",
|
|
|
|
|
|
"filters": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"field": "baseId",
|
|
|
|
|
|
"operator": "eq",
|
|
|
|
|
|
"dataType": "string",
|
|
|
|
|
|
"value": wbsCode.value
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"field": "rvcd",
|
|
|
|
|
|
"operator": "eq",
|
|
|
|
|
|
"dataType": "string",
|
|
|
|
|
|
"value": select.value.value
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"field": "tm",
|
|
|
|
|
|
"operator": "gte",
|
|
|
|
|
|
"dataType": "date",
|
|
|
|
|
|
"value": getStartTime()
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"field": "tm",
|
|
|
|
|
|
"operator": "lte",
|
|
|
|
|
|
"dataType": "date",
|
|
|
|
|
|
"value": getEndTime()
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
"sort": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"field": "sort",
|
|
|
|
|
|
"dir": "asc"
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
console.log('请求参数:', params);
|
|
|
|
|
|
let res = await qgcGetKendoListCust(params);
|
|
|
|
|
|
console.log('接口返回数据:', res);
|
|
|
|
|
|
|
|
|
|
|
|
// 处理返回的数据 - 兼容多种数据结构
|
|
|
|
|
|
let apiData: any[] = [];
|
|
|
|
|
|
|
|
|
|
|
|
if (res && res.data) {
|
|
|
|
|
|
// 情况1: res.data.data 是数组(当前实际情况)
|
|
|
|
|
|
if (Array.isArray(res.data.data)) {
|
|
|
|
|
|
apiData = res.data.data;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 情况2: res.data.data.data 是数组
|
|
|
|
|
|
else if (res.data.data && Array.isArray(res.data.data.data)) {
|
|
|
|
|
|
apiData = res.data.data.data;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 情况3: res.data 直接是数组
|
|
|
|
|
|
else if (Array.isArray(res.data)) {
|
|
|
|
|
|
apiData = res.data;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log('原始数据条数:', apiData.length);
|
|
|
|
|
|
|
|
|
|
|
|
// 过滤出有水质数据的站点(sttp === 'WQ')并按sort排序
|
|
|
|
|
|
const filteredData = apiData
|
|
|
|
|
|
.filter((item: any) => item.sttp === 'WQ')
|
|
|
|
|
|
.sort((a: any, b: any) => {
|
|
|
|
|
|
// 如果有sort字段则使用,否则按索引排序
|
|
|
|
|
|
return (a.sort || 0) - (b.sort || 0);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
console.log('过滤后水质数据条数:', filteredData.length);
|
|
|
|
|
|
|
|
|
|
|
|
// 转换为图表所需的数据格式
|
|
|
|
|
|
const chartData = filteredData.map((item: any, index: number) => {
|
|
|
|
|
|
// 去除站点名称中的"出库水质站"后缀
|
|
|
|
|
|
let stnm = item.stnm || '-';
|
|
|
|
|
|
if (stnm.includes('出库水质站')) {
|
|
|
|
|
|
stnm = stnm.replace('出库水质站', '');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
stnm: stnm,
|
|
|
|
|
|
stcd: item.stcd || '',
|
|
|
|
|
|
type: item.rstcd ? '1' : '0', // 有rstcd表示是电站出库站
|
|
|
|
|
|
tm: item.tm || '',
|
|
|
|
|
|
SORT: index + 1,
|
|
|
|
|
|
ph: item.ph != null ? Number(item.ph) : null,
|
|
|
|
|
|
dox: item.dox != null ? Number(item.dox) : null,
|
|
|
|
|
|
codmn: item.codmn != null ? Number(item.codmn) : null,
|
|
|
|
|
|
nh3n: item.nh3n != null ? Number(item.nh3n) : null,
|
|
|
|
|
|
tp: item.tp != null ? Number(item.tp) : null,
|
|
|
|
|
|
tn: item.tn != null ? Number(item.tn) : null
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 更新模拟数据
|
|
|
|
|
|
mockData.value = chartData;
|
|
|
|
|
|
|
|
|
|
|
|
// 判断是否有数据
|
|
|
|
|
|
hasData.value = chartData.length > 0;
|
|
|
|
|
|
console.log('hasData 设置为:', hasData.value);
|
|
|
|
|
|
|
|
|
|
|
|
// 重新渲染图表 - 使用 nextTick 确保 DOM 更新完成
|
|
|
|
|
|
await nextTick();
|
|
|
|
|
|
|
|
|
|
|
|
if (chartInstance && hasData.value) {
|
|
|
|
|
|
// 使用 requestAnimationFrame 确保容器尺寸正确
|
|
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
|
|
const option = getChartOption();
|
|
|
|
|
|
chartInstance?.setOption(option, true);
|
|
|
|
|
|
chartInstance?.resize();
|
|
|
|
|
|
console.log('图表数据已更新,共', chartData.length, '个站点');
|
|
|
|
|
|
});
|
|
|
|
|
|
} else if (!hasData.value) {
|
|
|
|
|
|
// 无数据时清空图表
|
|
|
|
|
|
console.log('无数据,清空图表');
|
|
|
|
|
|
chartInstance?.clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取图表数据失败:', error);
|
|
|
|
|
|
hasData.value = false;
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
// 关闭 loading 状态 - 使用 nextTick 确保在下一个 tick 关闭
|
|
|
|
|
|
await nextTick();
|
|
|
|
|
|
console.log('关闭 loading 状态');
|
|
|
|
|
|
loading.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取起始时间(当前选择日期前一个月)
|
|
|
|
|
|
const getStartTime = () => {
|
|
|
|
|
|
const currentDate = new Date(datetimePicker.value.value);
|
|
|
|
|
|
const oneMonthAgo = new Date(currentDate);
|
|
|
|
|
|
oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
|
|
|
|
|
|
|
|
|
|
|
|
const year = oneMonthAgo.getFullYear();
|
|
|
|
|
|
const month = String(oneMonthAgo.getMonth() + 1).padStart(2, '0');
|
|
|
|
|
|
const day = String(oneMonthAgo.getDate()).padStart(2, '0');
|
|
|
|
|
|
const hours = String(oneMonthAgo.getHours()).padStart(2, '0');
|
|
|
|
|
|
const minutes = String(oneMonthAgo.getMinutes()).padStart(2, '0');
|
|
|
|
|
|
const seconds = String(oneMonthAgo.getSeconds()).padStart(2, '0');
|
|
|
|
|
|
|
|
|
|
|
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 获取结束时间(当前选择日期的当天 23:59:59)
|
|
|
|
|
|
const getEndTime = () => {
|
|
|
|
|
|
const currentDate = new Date(datetimePicker.value.value);
|
|
|
|
|
|
|
|
|
|
|
|
const year = currentDate.getFullYear();
|
|
|
|
|
|
const month = String(currentDate.getMonth() + 1).padStart(2, '0');
|
|
|
|
|
|
const day = String(currentDate.getDate()).padStart(2, '0');
|
|
|
|
|
|
|
|
|
|
|
|
return `${year}-${month}-${day} 23:59:59`;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//根据流域获取河段
|
|
|
|
|
|
const getSelect = async () => {
|
|
|
|
|
|
if (!wbsCode.value) {
|
|
|
|
|
|
console.warn('wbsCode 为空,无法获取河段数据');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let params = {
|
|
|
|
|
|
"filter": {
|
|
|
|
|
|
"logic": "and",
|
|
|
|
|
|
"filters": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"field": "objId",
|
|
|
|
|
|
"operator": "eq",
|
|
|
|
|
|
"dataType": "string",
|
|
|
|
|
|
"value": wbsCode.value
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"field": "wbsType",
|
|
|
|
|
|
"operator": "eq",
|
|
|
|
|
|
"dataType": "string",
|
|
|
|
|
|
"value": "PSB_RVCD"
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
"group": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"dir": "asc",
|
|
|
|
|
|
"field": "orderIndex"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"dir": "des",
|
|
|
|
|
|
"field": "wbsCode"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"dir": "des",
|
|
|
|
|
|
"field": "wbsName"
|
|
|
|
|
|
}
|
|
|
|
|
|
],
|
|
|
|
|
|
"groupResultFlat": "true"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
let res = await wbsbGetKendoList(params);
|
|
|
|
|
|
|
|
|
|
|
|
// 处理返回的数据 - 注意数据结构可能是嵌套的
|
|
|
|
|
|
let data;
|
|
|
|
|
|
if (res && res.data && res.data.data) {
|
|
|
|
|
|
// 如果 res.data.data 是数组,直接使用
|
|
|
|
|
|
if (Array.isArray(res.data.data)) {
|
|
|
|
|
|
data = res.data.data;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 如果 res.data.data 是对象且有 data 属性,使用 res.data.data.data
|
|
|
|
|
|
else if (res.data.data.data && Array.isArray(res.data.data.data)) {
|
|
|
|
|
|
data = res.data.data.data;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.warn('无法解析河段数据:', res);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.warn('未获取到河段数据');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (data && data.length > 0) {
|
|
|
|
|
|
select.value.value = data[0].wbsCode;
|
|
|
|
|
|
select.value.options = data.map(item => {
|
|
|
|
|
|
return {
|
|
|
|
|
|
value: item.wbsCode,
|
|
|
|
|
|
label: item.wbsName
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 获取完选择器数据后,立即加载图表数据
|
|
|
|
|
|
await nextTick();
|
|
|
|
|
|
getecharts();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.warn('河段数据为空');
|
|
|
|
|
|
select.value.value = '';
|
|
|
|
|
|
select.value.options = [];
|
|
|
|
|
|
hasData.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取河段数据失败:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
//监听子组件的数据变化
|
|
|
|
|
|
const handlePanelChange1 = async (data) => {
|
|
|
|
|
|
console.log('当前所有控件状态:', data);
|
|
|
|
|
|
// 当选择器或日期变化时,重新加载图表数据
|
|
|
|
|
|
if (data.datetime || data.select) {
|
|
|
|
|
|
getecharts()
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
const wbsCode = ref('');
|
|
|
|
|
|
watch(
|
|
|
|
|
|
() => JidiSelectEventStore.selectedItem,
|
|
|
|
|
|
(newVal) => {
|
|
|
|
|
|
console.log(newVal);
|
|
|
|
|
|
wbsCode.value = newVal.wbsCode;
|
|
|
|
|
|
getSelect()
|
|
|
|
|
|
},
|
|
|
|
|
|
{ deep: true, immediate: true }
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-05-12 14:34:58 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
2026-06-01 08:37:38 +08:00
|
|
|
|
.chart-wrapper {
|
2026-05-12 14:34:58 +08:00
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 280px;
|
|
|
|
|
|
min-height: 231px;
|
2026-06-01 08:37:38 +08:00
|
|
|
|
position: relative;
|
|
|
|
|
|
/* 为empty-overlay提供定位上下文 */
|
|
|
|
|
|
|
|
|
|
|
|
.chart-container {
|
|
|
|
|
|
width: 100% !important;
|
|
|
|
|
|
height: 100% !important;
|
|
|
|
|
|
min-width: 100%;
|
|
|
|
|
|
min-height: 280px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.empty-overlay {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.9);
|
|
|
|
|
|
z-index: 10;
|
|
|
|
|
|
}
|
2026-05-12 14:34:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
</style>
|