Browse Source

feat:将认证后的重定向地址更新为上位机,优化标靶名称处理,并为实时图表实现交互式标靶显示/隐藏功能

master
qinjian 4 days ago
parent
commit
29ecefe176
  1. 2
      client/src/layout/container/index.jsx
  2. 3
      client/src/sections/wuyuanbiaoba/components/CameraView.jsx
  3. 137
      client/src/sections/wuyuanbiaoba/components/RealtimeCharts.jsx
  4. 10
      server/tcpProxy/index.js

2
client/src/layout/container/index.jsx

@ -11,7 +11,7 @@ export default (props) => {
useEffect(() => { useEffect(() => {
if (!sessionStorage.getItem("user")) { if (!sessionStorage.getItem("user")) {
window.location.replace("/signin"); window.location.replace("/wuyuanbiaoba");
} }
}, []); }, []);

3
client/src/sections/wuyuanbiaoba/components/CameraView.jsx

@ -286,8 +286,7 @@ const CameraView = ({
const templateParams = selectedTemplate const templateParams = selectedTemplate
? { ? {
// //
name: name: `target${rectangles.length + 1}` || selectedTemplate.name,
selectedTemplate.name || `target${rectangles.length + 1}`,
radius: selectedTemplate.physicalRadius || 40.0, radius: selectedTemplate.physicalRadius || 40.0,
isReferencePoint: selectedTemplate.isBaseline || false, isReferencePoint: selectedTemplate.isBaseline || false,
gradientThreshold: gradientThreshold:

137
client/src/sections/wuyuanbiaoba/components/RealtimeCharts.jsx

@ -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,

10
server/tcpProxy/index.js

@ -63,7 +63,7 @@ function setupTcpProxy(conf) {
// console.log('消息内容:', completeMessage); // console.log('消息内容:', completeMessage);
ws.send(completeMessage, (err) => { ws.send(completeMessage, (err) => {
if (err) { if (err) {
console.error('WebSocket发送数据错误:', err); console.error(`[${new Date().toLocaleString()}] WebSocket发送数据错误:`, err);
} else { } else {
// console.log('完整消息已转发到WebSocket客户端'); // console.log('完整消息已转发到WebSocket客户端');
} }
@ -101,7 +101,7 @@ function setupTcpProxy(conf) {
// 直接发送完整数据 // 直接发送完整数据
tcpClient.write(messageStr + '\n\n', (err) => { tcpClient.write(messageStr + '\n\n', (err) => {
if (err) { if (err) {
console.error('TCP发送数据错误:', err); console.error(`[${new Date().toLocaleString()}] TCP发送数据错误:`, err);
} else { } else {
// console.log('数据已发送到TCP服务器'); // console.log('数据已发送到TCP服务器');
} }
@ -113,7 +113,7 @@ function setupTcpProxy(conf) {
// TCP连接错误处理 // TCP连接错误处理
tcpClient.on('error', (err) => { tcpClient.on('error', (err) => {
console.error('TCP连接错误:', err); console.error(`[${new Date().toLocaleString()}] TCP连接错误:`, err);
tcpDataBuffer = ''; // 清理缓冲区 tcpDataBuffer = ''; // 清理缓冲区
if (ws.readyState === WebSocket.OPEN) { if (ws.readyState === WebSocket.OPEN) {
ws.close(1011, 'TCP连接错误'); ws.close(1011, 'TCP连接错误');
@ -140,7 +140,7 @@ function setupTcpProxy(conf) {
// WebSocket错误处理 // WebSocket错误处理
ws.on('error', (err) => { ws.on('error', (err) => {
console.error('WebSocket错误:', err); console.error(`[${new Date().toLocaleString()}] WebSocket错误:`, err);
if (tcpClient.writable) { if (tcpClient.writable) {
tcpClient.destroy(); tcpClient.destroy();
} }
@ -155,7 +155,7 @@ function setupTcpProxy(conf) {
}); });
wss.on('error', (err) => { wss.on('error', (err) => {
console.error('WebSocket服务器错误:', err); console.error(`[${new Date().toLocaleString()}] WebSocket服务器错误:`, err);
}); });
return wss; return wss;

Loading…
Cancel
Save