|
@ -1,5 +1,5 @@ |
|
|
import React, { useMemo, useEffect, useRef } from "react"; |
|
|
import React, { useMemo, useEffect, useRef, useState, useCallback } from "react"; |
|
|
import { Typography, Badge } from "antd"; |
|
|
import { Typography, Badge, Button, Space } from "antd"; |
|
|
import { |
|
|
import { |
|
|
Chart as ChartJS, |
|
|
Chart as ChartJS, |
|
|
CategoryScale, |
|
|
CategoryScale, |
|
@ -29,9 +29,11 @@ const RealtimeCharts = ({ tableData, lastUpdateTime }) => { |
|
|
const xChartRef = useRef(null); |
|
|
const xChartRef = useRef(null); |
|
|
const yChartRef = useRef(null); |
|
|
const yChartRef = useRef(null); |
|
|
|
|
|
|
|
|
|
|
|
// 添加标靶显示状态控制 |
|
|
|
|
|
const [visibleTargets, setVisibleTargets] = useState({}); |
|
|
|
|
|
|
|
|
// 添加数据验证 |
|
|
// 添加数据验证 |
|
|
if (!Array.isArray(tableData)) { |
|
|
if (!Array.isArray(tableData)) { |
|
|
// console.warn('tableData is not an array:', tableData); |
|
|
|
|
|
return <div>数据格式错误</div>; |
|
|
return <div>数据格式错误</div>; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -104,7 +106,7 @@ const RealtimeCharts = ({ tableData, lastUpdateTime }) => { |
|
|
|
|
|
|
|
|
return { labels, deviceIds, timeGroups, sortedTimes }; |
|
|
return { labels, deviceIds, timeGroups, sortedTimes }; |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
console.error("数据采样出错:", error); |
|
|
console.error(`${new Date().toLocaleString()} - 数据采样出错:`, error); |
|
|
return { |
|
|
return { |
|
|
labels: [], |
|
|
labels: [], |
|
|
deviceIds: [], |
|
|
deviceIds: [], |
|
@ -114,6 +116,72 @@ const RealtimeCharts = ({ tableData, lastUpdateTime }) => { |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 初始化可见目标状态 |
|
|
|
|
|
const { deviceIds } = sampleData(tableData); |
|
|
|
|
|
useEffect(() => { |
|
|
|
|
|
if (deviceIds.length > 0) { |
|
|
|
|
|
const initialVisible = {}; |
|
|
|
|
|
deviceIds.forEach(deviceId => { |
|
|
|
|
|
initialVisible[deviceId] = true; // 默认所有标靶都显示 |
|
|
|
|
|
}); |
|
|
|
|
|
setVisibleTargets(prev => ({ ...initialVisible, ...prev })); |
|
|
|
|
|
} |
|
|
|
|
|
}, [deviceIds.join(',')]); |
|
|
|
|
|
|
|
|
|
|
|
// 切换标靶显示状态的函数 |
|
|
|
|
|
const toggleTargetVisibility = useCallback((deviceId) => { |
|
|
|
|
|
setVisibleTargets(prev => ({ |
|
|
|
|
|
...prev, |
|
|
|
|
|
[deviceId]: !prev[deviceId] |
|
|
|
|
|
})); |
|
|
|
|
|
|
|
|
|
|
|
// 同时控制两个图表的显示状态 |
|
|
|
|
|
const xChart = xChartRef.current; |
|
|
|
|
|
const yChart = yChartRef.current; |
|
|
|
|
|
|
|
|
|
|
|
if (xChart && yChart) { |
|
|
|
|
|
const xDatasetIndex = xChart.data.datasets.findIndex(dataset => dataset.label === deviceId); |
|
|
|
|
|
const yDatasetIndex = yChart.data.datasets.findIndex(dataset => dataset.label === deviceId); |
|
|
|
|
|
|
|
|
|
|
|
if (xDatasetIndex !== -1 && yDatasetIndex !== -1) { |
|
|
|
|
|
const isVisible = xChart.isDatasetVisible(xDatasetIndex); |
|
|
|
|
|
|
|
|
|
|
|
// 切换显示状态 |
|
|
|
|
|
xChart.setDatasetVisibility(xDatasetIndex, !isVisible); |
|
|
|
|
|
yChart.setDatasetVisibility(yDatasetIndex, !isVisible); |
|
|
|
|
|
|
|
|
|
|
|
// 更新图表 |
|
|
|
|
|
xChart.update(); |
|
|
|
|
|
yChart.update(); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
|
|
// 全部显示/隐藏的函数 |
|
|
|
|
|
const toggleAllTargets = useCallback((visible) => { |
|
|
|
|
|
const newVisibleTargets = {}; |
|
|
|
|
|
deviceIds.forEach(deviceId => { |
|
|
|
|
|
newVisibleTargets[deviceId] = visible; |
|
|
|
|
|
}); |
|
|
|
|
|
setVisibleTargets(newVisibleTargets); |
|
|
|
|
|
|
|
|
|
|
|
// 同时控制两个图表 |
|
|
|
|
|
const xChart = xChartRef.current; |
|
|
|
|
|
const yChart = yChartRef.current; |
|
|
|
|
|
|
|
|
|
|
|
if (xChart && yChart) { |
|
|
|
|
|
xChart.data.datasets.forEach((_, index) => { |
|
|
|
|
|
xChart.setDatasetVisibility(index, visible); |
|
|
|
|
|
}); |
|
|
|
|
|
yChart.data.datasets.forEach((_, index) => { |
|
|
|
|
|
yChart.setDatasetVisibility(index, visible); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
xChart.update(); |
|
|
|
|
|
yChart.update(); |
|
|
|
|
|
} |
|
|
|
|
|
}, [deviceIds]); |
|
|
|
|
|
|
|
|
// 准备X轴图表数据 |
|
|
// 准备X轴图表数据 |
|
|
const xChartData = useMemo(() => { |
|
|
const xChartData = useMemo(() => { |
|
|
try { |
|
|
try { |
|
@ -145,15 +213,16 @@ const RealtimeCharts = ({ tableData, lastUpdateTime }) => { |
|
|
pointHoverRadius: 5, |
|
|
pointHoverRadius: 5, |
|
|
tension: 0, |
|
|
tension: 0, |
|
|
connectNulls: false, |
|
|
connectNulls: false, |
|
|
|
|
|
hidden: !visibleTargets[deviceId], // 根据状态控制显示 |
|
|
}; |
|
|
}; |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
return { labels, datasets }; |
|
|
return { labels, datasets }; |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
console.error("准备X轴图表数据出错:", error); |
|
|
console.error(`${new Date().toLocaleString()} - 准备X轴图表数据出错:`, error); |
|
|
return { labels: [], datasets: [] }; |
|
|
return { labels: [], datasets: [] }; |
|
|
} |
|
|
} |
|
|
}, [tableData]); |
|
|
}, [tableData, visibleTargets]); |
|
|
|
|
|
|
|
|
// 准备Y轴图表数据 |
|
|
// 准备Y轴图表数据 |
|
|
const yChartData = useMemo(() => { |
|
|
const yChartData = useMemo(() => { |
|
@ -186,15 +255,16 @@ const RealtimeCharts = ({ tableData, lastUpdateTime }) => { |
|
|
pointHoverRadius: 5, |
|
|
pointHoverRadius: 5, |
|
|
tension: 0, |
|
|
tension: 0, |
|
|
connectNulls: false, |
|
|
connectNulls: false, |
|
|
|
|
|
hidden: !visibleTargets[deviceId], // 根据状态控制显示 |
|
|
}; |
|
|
}; |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
return { labels, datasets }; |
|
|
return { labels, datasets }; |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
console.error("准备Y轴图表数据出错:", error); |
|
|
console.error(`${new Date().toLocaleString()} - 准备Y轴图表数据出错:`, error); |
|
|
return { labels: [], datasets: [] }; |
|
|
return { labels: [], datasets: [] }; |
|
|
} |
|
|
} |
|
|
}, [tableData]); |
|
|
}, [tableData, visibleTargets]); |
|
|
|
|
|
|
|
|
// Chart.js配置选项 |
|
|
// Chart.js配置选项 |
|
|
const chartOptions = { |
|
|
const chartOptions = { |
|
@ -206,14 +276,7 @@ const RealtimeCharts = ({ tableData, lastUpdateTime }) => { |
|
|
}, |
|
|
}, |
|
|
plugins: { |
|
|
plugins: { |
|
|
legend: { |
|
|
legend: { |
|
|
position: "bottom", |
|
|
display: false, // 隐藏默认图例,使用自定义控制器 |
|
|
labels: { |
|
|
|
|
|
usePointStyle: true, |
|
|
|
|
|
padding: 20, |
|
|
|
|
|
font: { |
|
|
|
|
|
size: 12, |
|
|
|
|
|
}, |
|
|
|
|
|
}, |
|
|
|
|
|
}, |
|
|
}, |
|
|
tooltip: { |
|
|
tooltip: { |
|
|
filter: function (tooltipItem) { |
|
|
filter: function (tooltipItem) { |
|
@ -312,13 +375,6 @@ const RealtimeCharts = ({ tableData, lastUpdateTime }) => { |
|
|
}, |
|
|
}, |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// 添加调试信息 |
|
|
|
|
|
// useEffect(() => { |
|
|
|
|
|
// console.log('RealtimeCharts - tableData:', tableData); |
|
|
|
|
|
// console.log('RealtimeCharts - xChartData:', xChartData); |
|
|
|
|
|
// console.log('RealtimeCharts - yChartData:', yChartData); |
|
|
|
|
|
// }, [tableData, xChartData, yChartData]); |
|
|
|
|
|
|
|
|
|
|
|
// 如果没有数据,显示空状态 |
|
|
// 如果没有数据,显示空状态 |
|
|
if (!tableData || tableData.length === 0) { |
|
|
if (!tableData || tableData.length === 0) { |
|
|
return ( |
|
|
return ( |
|
@ -378,6 +434,41 @@ const RealtimeCharts = ({ tableData, lastUpdateTime }) => { |
|
|
/> |
|
|
/> |
|
|
</Title> |
|
|
</Title> |
|
|
|
|
|
|
|
|
|
|
|
{/* 统一的图例控制器 */} |
|
|
|
|
|
<div style={{ marginBottom: "16px", padding: "12px", backgroundColor: "#fafafa", borderRadius: "6px" }}> |
|
|
|
|
|
<div style={{ marginBottom: "8px", fontSize: "14px", fontWeight: "500" }}>显示控制</div> |
|
|
|
|
|
<Space wrap> |
|
|
|
|
|
<Button |
|
|
|
|
|
size="small" |
|
|
|
|
|
onClick={() => toggleAllTargets(true)} |
|
|
|
|
|
type="primary" |
|
|
|
|
|
> |
|
|
|
|
|
全部显示 |
|
|
|
|
|
</Button> |
|
|
|
|
|
<Button |
|
|
|
|
|
size="small" |
|
|
|
|
|
onClick={() => toggleAllTargets(false)} |
|
|
|
|
|
> |
|
|
|
|
|
全部隐藏 |
|
|
|
|
|
</Button> |
|
|
|
|
|
{deviceIds.map(deviceId => ( |
|
|
|
|
|
<Button |
|
|
|
|
|
key={deviceId} |
|
|
|
|
|
size="small" |
|
|
|
|
|
type={visibleTargets[deviceId] ? "primary" : "default"} |
|
|
|
|
|
style={{ |
|
|
|
|
|
borderColor: getDeviceColor(deviceId), |
|
|
|
|
|
color: visibleTargets[deviceId] ? "#fff" : getDeviceColor(deviceId), |
|
|
|
|
|
backgroundColor: visibleTargets[deviceId] ? getDeviceColor(deviceId) : "#fff" |
|
|
|
|
|
}} |
|
|
|
|
|
onClick={() => toggleTargetVisibility(deviceId)} |
|
|
|
|
|
> |
|
|
|
|
|
{deviceId} |
|
|
|
|
|
</Button> |
|
|
|
|
|
))} |
|
|
|
|
|
</Space> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
<div |
|
|
<div |
|
|
style={{ |
|
|
style={{ |
|
|
flex: 1, |
|
|
flex: 1, |
|
|