diff --git a/client/src/sections/wuyuanbiaoba/components/RealtimeCharts.jsx b/client/src/sections/wuyuanbiaoba/components/RealtimeCharts.jsx
index 6cc3386..74f1e63 100644
--- a/client/src/sections/wuyuanbiaoba/components/RealtimeCharts.jsx
+++ b/client/src/sections/wuyuanbiaoba/components/RealtimeCharts.jsx
@@ -1,233 +1,344 @@
-import React from 'react';
-import { Typography, Badge } from 'antd';
-import ReactECharts from 'echarts-for-react';
+import React, { useMemo, useEffect, useRef } from "react";
+import { Typography, Badge } from "antd";
+import {
+ Chart as ChartJS,
+ CategoryScale,
+ LinearScale,
+ PointElement,
+ LineElement,
+ Title as ChartTitle,
+ Tooltip,
+ Legend,
+} from "chart.js";
+import { Line } from "react-chartjs-2";
+
+// 注册Chart.js组件
+ChartJS.register(
+ CategoryScale,
+ LinearScale,
+ PointElement,
+ LineElement,
+ ChartTitle,
+ Tooltip,
+ Legend
+);
const { Title } = Typography;
const RealtimeCharts = ({ tableData, lastUpdateTime }) => {
+ const xChartRef = useRef(null);
+ const yChartRef = useRef(null);
+
+ // 添加数据验证
+ if (!Array.isArray(tableData)) {
+ console.warn('tableData is not an array:', tableData);
+ return
数据格式错误
;
+ }
+
// 固定的设备颜色映射,确保颜色一致性
const getDeviceColor = (deviceId) => {
const colorMap = {
- 'DEV000': "#1890ff", // 蓝色
- 'DEV001': "#52c41a", // 绿色
- 'DEV002': "#faad14", // 橙色
- 'DEV003': "#f5222d", // 红色
- 'DEV004': "#722ed1", // 紫色
- 'DEV005': "#fa8c16", // 橙红色
- 'DEV006': "#13c2c2", // 青色
- 'DEV007': "#eb2f96", // 粉色
- 'DEV008': "#2f54eb", // 深蓝色
- 'DEV009': "#fa541c", // 火橙色
+ target1: "#52c41a", // 绿色
+ target2: "#faad14", // 橙色
+ target3: "#f5222d", // 红色
+ target4: "#722ed1", // 紫色
+ target5: "#fa8c16", // 橙红色
+ target6: "#13c2c2", // 青色
+ target7: "#eb2f96", // 粉色
+ target8: "#2f54eb", // 深蓝色
+ target9: "#fa541c", // 火橙色
+ target10: "#1890ff", // 蓝色
};
- // 如果设备ID在映射中,使用固定颜色
- if (colorMap[deviceId]) {
- return colorMap[deviceId];
- }
-
- // 对于未预定义的设备,基于设备编号生成颜色
- const deviceNumber = deviceId.replace(/\D/g, ''); // 提取数字
- const colors = [
- "#1890ff", "#52c41a", "#faad14", "#f5222d", "#722ed1",
- "#fa8c16", "#13c2c2", "#eb2f96", "#2f54eb", "#fa541c"
- ];
- return colors[parseInt(deviceNumber) % colors.length];
+ return colorMap[deviceId] || "#1890ff";
};
- // 准备图表数据
- const prepareChartData = () => {
- // 获取所有唯一的设备ID并排序,确保顺序一致
- const deviceIds = [...new Set(tableData.map((item) => item.deviceId))].sort();
+ // 数据采样函数 - 每秒采样一次,最多保留25个数据点
+ const sampleData = (data) => {
+ if (!data || data.length === 0) {
+ return {
+ labels: [],
+ deviceIds: [],
+ timeGroups: {},
+ sortedTimes: []
+ };
+ }
- // 获取所有时间点并排序
- const allTimes = [
- ...new Set(tableData.map((item) => item.updateTime)),
- ].sort((a, b) => new Date(a) - new Date(b));
- const timeLabels = allTimes.map((time) => {
- const date = new Date(time);
- return date.toLocaleTimeString("zh-CN", {
- hour: "2-digit",
- minute: "2-digit",
- second: "2-digit",
+ try {
+ // 按时间分组数据
+ const timeGroups = {};
+ data.forEach((item) => {
+ if (!item || !item.updateTime) return;
+
+ const timeKey = Math.floor(new Date(item.updateTime).getTime() / 1000); // 按秒分组
+ if (!timeGroups[timeKey]) {
+ timeGroups[timeKey] = [];
+ }
+ timeGroups[timeKey].push(item);
});
- });
- // 为每个设备准备数据
- const deviceData = deviceIds.map((deviceId) => {
- const deviceItems = tableData.filter(
- (item) => item.deviceId === deviceId
- );
+ // 取最新的25秒数据
+ const sortedTimes = Object.keys(timeGroups)
+ .sort((a, b) => Number(b) - Number(a))
+ .slice(0, 25)
+ .reverse();
- // 为每个时间点找到对应的X和Y值,如果没有则为null
- const xData = allTimes.map((time) => {
- const item = deviceItems.find((d) => d.updateTime === time);
- return item ? parseFloat(item.xValue) : null;
- });
+ // 获取所有设备ID
+ const deviceIds = [...new Set(data.map((item) => item?.deviceId).filter(Boolean))].sort();
- const yData = allTimes.map((time) => {
- const item = deviceItems.find((d) => d.updateTime === time);
- return item ? parseFloat(item.yValue) : null;
+ // 生成时间标签
+ const labels = sortedTimes.map((timeKey) => {
+ return new Date(Number(timeKey) * 1000).toLocaleTimeString("zh-CN", {
+ hour: "2-digit",
+ minute: "2-digit",
+ second: "2-digit",
+ });
});
- // 使用固定的颜色映射
- const color = getDeviceColor(deviceId);
-
+ return { labels, deviceIds, timeGroups, sortedTimes };
+ } catch (error) {
+ console.error('数据采样出错:', error);
return {
- deviceId,
- xData,
- yData,
- color,
+ labels: [],
+ deviceIds: [],
+ timeGroups: {},
+ sortedTimes: []
};
- });
-
- return { timeLabels, deviceData };
+ }
};
- const { timeLabels, deviceData } = prepareChartData();
+ // 准备X轴图表数据
+ const xChartData = useMemo(() => {
+ try {
+ const { labels, deviceIds, timeGroups, sortedTimes } = sampleData(tableData);
- // X值折线图配置
- const getXChartOption = () => ({
- title: {
- text: "X轴位移数据",
- left: "center",
- textStyle: {
- fontSize: 16,
- fontWeight: "normal",
- },
- },
- tooltip: {
- trigger: "axis",
- formatter: function (params) {
- let result = `时间: ${params[0].axisValue}
`;
- params.forEach((param) => {
- if (param.value !== null) {
- result += `${param.seriesName}: ${param.value} mm
`;
- }
+ if (!deviceIds || deviceIds.length === 0) {
+ return { labels: [], datasets: [] };
+ }
+
+ const datasets = deviceIds.map((deviceId) => {
+ const data = sortedTimes.map((timeKey) => {
+ const timeData = timeGroups[timeKey] || [];
+ const deviceData = timeData.find(
+ (item) => item.deviceId === deviceId
+ );
+ return deviceData && deviceData.xValue !== undefined ? parseFloat(deviceData.xValue) : null;
});
- return result;
- },
+
+ return {
+ label: deviceId,
+ data: data,
+ borderColor: getDeviceColor(deviceId),
+ backgroundColor: getDeviceColor(deviceId) + "20",
+ borderWidth: 2,
+ pointRadius: 3,
+ pointHoverRadius: 5,
+ tension: 0,
+ connectNulls: false,
+ };
+ });
+
+ return { labels, datasets };
+ } catch (error) {
+ console.error('准备X轴图表数据出错:', error);
+ return { labels: [], datasets: [] };
+ }
+ }, [tableData]);
+
+ // 准备Y轴图表数据
+ const yChartData = useMemo(() => {
+ try {
+ const { labels, deviceIds, timeGroups, sortedTimes } = sampleData(tableData);
+
+ if (!deviceIds || deviceIds.length === 0) {
+ return { labels: [], datasets: [] };
+ }
+
+ const datasets = deviceIds.map((deviceId) => {
+ const data = sortedTimes.map((timeKey) => {
+ const timeData = timeGroups[timeKey] || [];
+ const deviceData = timeData.find(
+ (item) => item.deviceId === deviceId
+ );
+ return deviceData && deviceData.yValue !== undefined ? parseFloat(deviceData.yValue) : null;
+ });
+
+ return {
+ label: deviceId,
+ data: data,
+ borderColor: getDeviceColor(deviceId),
+ backgroundColor: getDeviceColor(deviceId) + "20",
+ borderWidth: 2,
+ pointRadius: 3,
+ pointHoverRadius: 5,
+ tension: 0,
+ connectNulls: false,
+ };
+ });
+
+ return { labels, datasets };
+ } catch (error) {
+ console.error('准备Y轴图表数据出错:', error);
+ return { labels: [], datasets: [] };
+ }
+ }, [tableData]);
+
+ // Chart.js配置选项
+ const chartOptions = {
+ responsive: true,
+ maintainAspectRatio: false,
+ interaction: {
+ mode: "index",
+ intersect: false,
},
- legend: {
- orient: "horizontal",
- bottom: "5%",
- textStyle: {
- fontSize: 12,
+ plugins: {
+ legend: {
+ position: "bottom",
+ labels: {
+ usePointStyle: true,
+ padding: 20,
+ font: {
+ size: 12,
+ },
+ },
},
- },
- grid: {
- left: "3%",
- right: "4%",
- bottom: "15%",
- top: "15%",
- containLabel: true,
- },
- xAxis: {
- type: "category",
- data: timeLabels,
- axisLabel: {
- rotate: 45,
- fontSize: 11,
+ tooltip: {
+ filter: function (tooltipItem) {
+ return tooltipItem.parsed.y !== null;
+ },
},
},
- yAxis: {
- type: "value",
- name: "X值(mm)",
- nameTextStyle: {
- fontSize: 13,
+ scales: {
+ x: {
+ display: true,
+ title: {
+ display: true,
+ text: "时间",
+ },
+ ticks: {
+ maxRotation: 45,
+ minRotation: 0,
+ font: {
+ size: 10,
+ },
+ },
},
- axisLabel: {
- fontSize: 11,
+ y: {
+ display: true,
+ title: {
+ display: true,
+ font: {
+ size: 12,
+ },
+ },
+ ticks: {
+ font: {
+ size: 10,
+ },
+ },
},
},
- series: deviceData.map((device) => ({
- name: device.deviceId,
- type: "line",
- data: device.xData,
- smooth: false,
- connectNulls: false,
- lineStyle: {
- color: device.color,
- width: 2,
+ elements: {
+ point: {
+ radius: 3,
+ hoverRadius: 5,
},
- itemStyle: {
- color: device.color,
- },
- symbol: "circle",
- symbolSize: 4,
- })),
- });
-
- // Y值折线图配置
- const getYChartOption = () => ({
- title: {
- text: "Y轴位移数据",
- left: "center",
- textStyle: {
- fontSize: 16,
- fontWeight: "normal",
+ line: {
+ tension: 0,
},
},
- tooltip: {
- trigger: "axis",
- formatter: function (params) {
- let result = `时间: ${params[0].axisValue}
`;
- params.forEach((param) => {
- if (param.value !== null) {
- result += `${param.seriesName}: ${param.value} mm
`;
- }
- });
- return result;
+ };
+
+ // X轴图表配置
+ const xChartOptions = {
+ ...chartOptions,
+ plugins: {
+ ...chartOptions.plugins,
+ title: {
+ display: true,
+ text: "X轴位移数据",
+ font: {
+ size: 16,
+ },
},
},
- legend: {
- orient: "horizontal",
- bottom: "5%",
- textStyle: {
- fontSize: 12,
+ scales: {
+ ...chartOptions.scales,
+ y: {
+ ...chartOptions.scales.y,
+ title: {
+ ...chartOptions.scales.y.title,
+ text: "X值(mm)",
+ },
},
},
- grid: {
- left: "3%",
- right: "4%",
- bottom: "15%",
- top: "15%",
- containLabel: true,
- },
- xAxis: {
- type: "category",
- data: timeLabels,
- axisLabel: {
- rotate: 45,
- fontSize: 11,
+ };
+
+ // Y轴图表配置
+ const yChartOptions = {
+ ...chartOptions,
+ plugins: {
+ ...chartOptions.plugins,
+ title: {
+ display: true,
+ text: "Y轴位移数据",
+ font: {
+ size: 16,
+ },
},
},
- yAxis: {
- type: "value",
- name: "Y值(mm)",
- nameTextStyle: {
- fontSize: 13,
- },
- axisLabel: {
- fontSize: 11,
+ scales: {
+ ...chartOptions.scales,
+ y: {
+ ...chartOptions.scales.y,
+ title: {
+ ...chartOptions.scales.y.title,
+ text: "Y值(mm)",
+ },
},
},
- series: deviceData.map((device) => ({
- name: device.deviceId,
- type: "line",
- data: device.yData,
- smooth: false,
- connectNulls: false,
- lineStyle: {
- color: device.color,
- width: 2,
- },
- itemStyle: {
- color: device.color,
- },
- symbol: "circle",
- symbolSize: 4,
- })),
- });
+ };
+
+ // 添加调试信息
+ useEffect(() => {
+ console.log('RealtimeCharts - tableData:', tableData);
+ console.log('RealtimeCharts - xChartData:', xChartData);
+ console.log('RealtimeCharts - yChartData:', yChartData);
+ }, [tableData, xChartData, yChartData]);
+
+ // 如果没有数据,显示空状态
+ if (!tableData || tableData.length === 0) {
+ return (
+
+ );
+ }
return (
{
backgroundColor: "white",
borderRadius: "8px",
border: "1px solid #e8e8e8",
- minHeight: "250px",
+ minHeight: "280px",
+ padding: "16px",
}}
>
-
@@ -279,13 +391,14 @@ const RealtimeCharts = ({ tableData, lastUpdateTime }) => {
backgroundColor: "white",
borderRadius: "8px",
border: "1px solid #e8e8e8",
- minHeight: "250px",
+ minHeight: "280px",
+ padding: "16px",
}}
>
-
diff --git a/client/src/sections/wuyuanbiaoba/components/RealtimeDataTable.jsx b/client/src/sections/wuyuanbiaoba/components/RealtimeDataTable.jsx
index 020f740..2881194 100644
--- a/client/src/sections/wuyuanbiaoba/components/RealtimeDataTable.jsx
+++ b/client/src/sections/wuyuanbiaoba/components/RealtimeDataTable.jsx
@@ -10,15 +10,21 @@ const RealtimeDataTable = ({ realtimeData }) => {
title: "X值(mm)",
dataIndex: "xValue",
key: "xValue",
+
render: (text) => Number(text),
},
{
title: "Y值(mm)",
dataIndex: "yValue",
key: "yValue",
+
render: (text) => Number(text),
},
- { title: "更新时间", dataIndex: "updateTime", key: "updateTime", width: 180 },
+ {
+ title: "更新时间",
+ dataIndex: "updateTime",
+ key: "updateTime",
+ },
];
return (
@@ -48,10 +54,6 @@ const RealtimeDataTable = ({ realtimeData }) => {
columns={tableColumns}
pagination={false}
size="small"
- scroll={{
- y: 508,
- }}
- virtual
/>
diff --git a/client/src/sections/wuyuanbiaoba/container/index.jsx b/client/src/sections/wuyuanbiaoba/container/index.jsx
index f46cea9..b91b2fa 100644
--- a/client/src/sections/wuyuanbiaoba/container/index.jsx
+++ b/client/src/sections/wuyuanbiaoba/container/index.jsx
@@ -10,7 +10,11 @@ import {
TemplateModal,
TargetDetailModal,
} from "../components";
-import { WebSocketProvider, useWebSocket, useWebSocketSubscription } from "../actions/websocket.jsx";
+import {
+ WebSocketProvider,
+ useWebSocket,
+ useWebSocketSubscription,
+} from "../actions/websocket.jsx";
import { useTemplateStorage } from "../hooks/useTemplateStorage.js";
import { useTargetStorage } from "../hooks/useTargetStorage.js";
@@ -21,7 +25,7 @@ const WuyuanbiaobaContent = () => {
const { isConnected, sendMessage } = useWebSocket();
// 订阅实时数据
- const realtimeDataSubscription = useWebSocketSubscription('dev', 'data');
+ const realtimeDataSubscription = useWebSocketSubscription("dev", "data");
const {
templates: tempListData,
@@ -45,6 +49,9 @@ const WuyuanbiaobaContent = () => {
const [realtimeData, setRealtimeData] = useState([]);
const [lastUpdateTime, setLastUpdateTime] = useState(new Date());
+ // 数据采样相关状态
+ const [lastSampleTime, setLastSampleTime] = useState(0);
+
// 模板相关状态
const [templateModalVisible, setTemplateModalVisible] = useState(false);
const [templateModalMode, setTemplateModalMode] = useState("add"); // 'add' | 'edit'
@@ -65,9 +72,9 @@ const WuyuanbiaobaContent = () => {
return [];
}
- return data.data.map(item => ({
+ return data.data.map((item) => ({
key: item.pos,
- deviceId: `target${Number(item.pos)+1}`,
+ deviceId: `target${Number(item.pos) + 1}`,
xValue: item.x,
yValue: item.y,
updateTime: data.time || new Date().toLocaleString(),
@@ -79,22 +86,23 @@ const WuyuanbiaobaContent = () => {
// 初始化空的实时数据表格
setRealtimeData([]);
setTableData([]);
- console.log('数据已初始化,等待实时数据...',import.meta.env.MODE);
-
+ console.log("数据已初始化,等待实时数据...", import.meta.env.MODE);
}, []);
// 模板数据加载完成后,默认选中内置模板
useEffect(() => {
if (!templatesLoading && tempListData.length > 0 && !selectedTemplate) {
// 查找内置模板
- const builtinTemplate = tempListData.find(template => template.key === 'builtin_1');
+ const builtinTemplate = tempListData.find(
+ (template) => template.key === "builtin_1"
+ );
if (builtinTemplate) {
- setSelectedTemplate('builtin_1');
- console.log('默认选中内置模板:', builtinTemplate.name);
+ setSelectedTemplate("builtin_1");
+ console.log("默认选中内置模板:", builtinTemplate.name);
} else {
// 如果没有内置模板,选择第一个模板
setSelectedTemplate(tempListData[0].key);
- console.log('默认选中第一个模板:', tempListData[0].name);
+ console.log("默认选中第一个模板:", tempListData[0].name);
}
}
}, [templatesLoading, tempListData, selectedTemplate]);
@@ -110,59 +118,59 @@ const WuyuanbiaobaContent = () => {
// 调试实时数据订阅状态
useEffect(() => {
- console.log('实时数据订阅状态:', {
- hasData: !!realtimeDataSubscription.latest,
- dataCount: realtimeDataSubscription.data?.length || 0,
- latestTimestamp: realtimeDataSubscription.latest?.timestamp,
- });
+ // console.log('实时数据订阅状态:', {
+ // hasData: !!realtimeDataSubscription.latest,
+ // dataCount: realtimeDataSubscription.data?.length || 0,
+ // latestTimestamp: realtimeDataSubscription.latest?.timestamp,
+ // });
}, [realtimeDataSubscription]);
// 处理实时数据更新
useEffect(() => {
- if (realtimeDataSubscription.latest && realtimeDataSubscription.latest.values) {
- const newRealtimeData = processRealtimeData(realtimeDataSubscription.latest.values);
+ if (
+ realtimeDataSubscription.latest &&
+ realtimeDataSubscription.latest.values
+ ) {
+ const newRealtimeData = processRealtimeData(
+ realtimeDataSubscription.latest.values
+ );
if (newRealtimeData.length > 0) {
- console.log('收到实时数据:', newRealtimeData);
-
- // 更新最新数据表格 - 保留之前设备的数据,只更新有新数据的设备
- setRealtimeData(prevRealtimeData => {
- // 创建设备ID到数据的映射
- const deviceDataMap = new Map();
-
- // 先保留所有之前的设备数据
- prevRealtimeData.forEach(data => {
- deviceDataMap.set(data.deviceId, data);
- });
-
- // 用新数据更新或添加设备
- newRealtimeData.forEach(data => {
- deviceDataMap.set(data.deviceId, data);
+ const currentTime = Date.now();
+ const currentSecond = Math.floor(currentTime / 1000);
+
+ // 每秒采样一次数据用于图表和表格显示
+ if (currentSecond > lastSampleTime) {
+ setLastSampleTime(currentSecond);
+
+ // 更新采样后的历史数据(用于图表显示)
+ setTableData((prevData) => {
+ const updatedData = [
+ ...prevData,
+ ...newRealtimeData.map((point) => ({
+ ...point,
+ key: `${currentTime}_${point.key}`,
+ timestamp: currentTime,
+ updateTime: new Date(currentTime).toLocaleString(),
+ })),
+ ];
+
+ // 只保留最近25个数据点(25秒)
+ return updatedData.slice(-75); // 3个设备 * 25个时间点
});
- // 转换回数组格式,按设备ID排序保持一致性
- return Array.from(deviceDataMap.values()).sort((a, b) =>
- a.deviceId.localeCompare(b.deviceId)
- );
- });
-
- // 更新历史数据(用于图表显示)
- setTableData(prevData => {
- const updatedData = [
- ...prevData,
- ...newRealtimeData.map((point, index) => ({
- ...point,
- key: `${Date.now()}_${point.key}`, // 为历史数据生成唯一key
- })),
- ];
- // 保持最多100个数据点
- return updatedData.slice(-100);
- });
+ // 更新实时数据表格(使用最新的采样数据)
+ setRealtimeData(newRealtimeData.map((point) => ({
+ ...point,
+ key: `realtime_${point.key}`,
+ updateTime: new Date(currentTime).toLocaleString(),
+ })));
+ }
setLastUpdateTime(new Date());
}
}
- }, [realtimeDataSubscription.latest]);
+ }, [realtimeDataSubscription.latest, lastSampleTime]);
// 编辑标靶的处理函数
const handleEditTarget = (target) => {
@@ -436,7 +444,7 @@ const WuyuanbiaobaContent = () => {
lastUpdateTime={lastUpdateTime}
/>
- {/* Table 区域 */}
+ {/* Table 区域 - 使用采样数据显示 */}
diff --git a/package-lock.json b/package-lock.json
index aabb1f7..dc2e88f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,9 +17,9 @@
"devDependencies": {
"@ant-design/icons": "^5.4.0",
"antd": "^5.19.0",
- "echarts": "^5.6.0",
- "echarts-for-react": "^3.0.2",
+ "chart.js": "^4.5.0",
"react": "18.x",
+ "react-chartjs-2": "^5.3.0",
"react-dom": "18.x",
"react-redux": "^9.1.2",
"react-router-dom": "^6.26.0"
@@ -862,6 +862,13 @@
"node": ">= 18"
}
},
+ "node_modules/@kurkle/color": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmmirror.com/@kurkle/color/-/color-0.3.4.tgz",
+ "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@noble/hashes": {
"version": "1.8.0",
"resolved": "https://nexus.ngaiot.com/repository/fs-npm/@noble/hashes/-/hashes-1.8.0.tgz",
@@ -1771,6 +1778,19 @@
"tslib": "^2.0.3"
}
},
+ "node_modules/chart.js": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmmirror.com/chart.js/-/chart.js-4.5.0.tgz",
+ "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@kurkle/color": "^0.3.0"
+ },
+ "engines": {
+ "pnpm": ">=8"
+ }
+ },
"node_modules/classnames": {
"version": "2.5.1",
"resolved": "https://nexus.ngaiot.com/repository/fs-npm/classnames/-/classnames-2.5.1.tgz",
@@ -2112,39 +2132,6 @@
"node": ">= 0.4"
}
},
- "node_modules/echarts": {
- "version": "5.6.0",
- "resolved": "https://nexus.ngaiot.com/repository/fs-npm/echarts/-/echarts-5.6.0.tgz",
- "integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "2.3.0",
- "zrender": "5.6.1"
- }
- },
- "node_modules/echarts-for-react": {
- "version": "3.0.3",
- "resolved": "https://nexus.ngaiot.com/repository/fs-npm/echarts-for-react/-/echarts-for-react-3.0.3.tgz",
- "integrity": "sha512-KdvZGkCwmx5DTHl7vjo0CBodSaPY31hPWRC4NZ5B+utQfoW+M54OTBvkoCmktR0kJ+1Bj6rP7pIJJnxPdySyug==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "fast-deep-equal": "^3.1.3",
- "size-sensor": "^1.0.1"
- },
- "peerDependencies": {
- "echarts": "^3.0.0 || ^4.0.0 || ^5.0.0",
- "react": "^15.0.0 || >=16.0.0"
- }
- },
- "node_modules/echarts/node_modules/tslib": {
- "version": "2.3.0",
- "resolved": "https://nexus.ngaiot.com/repository/fs-npm/tslib/-/tslib-2.3.0.tgz",
- "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
- "dev": true,
- "license": "0BSD"
- },
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://nexus.ngaiot.com/repository/fs-npm/ee-first/-/ee-first-1.1.1.tgz",
@@ -2289,13 +2276,6 @@
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
"license": "MIT"
},
- "node_modules/fast-deep-equal": {
- "version": "3.1.3",
- "resolved": "https://nexus.ngaiot.com/repository/fs-npm/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=",
- "dev": true,
- "license": "MIT"
- },
"node_modules/fast-safe-stringify": {
"version": "2.1.1",
"resolved": "https://nexus.ngaiot.com/repository/fs-npm/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
@@ -4123,6 +4103,17 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-chartjs-2": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmmirror.com/react-chartjs-2/-/react-chartjs-2-5.3.0.tgz",
+ "integrity": "sha512-UfZZFnDsERI3c3CZGxzvNJd02SHjaSJ8kgW1djn65H1KK8rehwTjyrRKOG3VTMG8wtHZ5rgAO5oTHtHi9GCCmw==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "chart.js": "^4.1.1",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/react-dom": {
"version": "18.3.1",
"resolved": "https://nexus.ngaiot.com/repository/fs-npm/react-dom/-/react-dom-18.3.1.tgz",
@@ -4503,13 +4494,6 @@
"node": ">=18"
}
},
- "node_modules/size-sensor": {
- "version": "1.0.2",
- "resolved": "https://nexus.ngaiot.com/repository/fs-npm/size-sensor/-/size-sensor-1.0.2.tgz",
- "integrity": "sha512-2NCmWxY7A9pYKGXNBfteo4hy14gWu47rg5692peVMst6lQLPKrVjhY+UTEsPI5ceFRJSl3gVgMYaUi/hKuaiKw==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/snake-case": {
"version": "3.0.4",
"resolved": "https://nexus.ngaiot.com/repository/fs-npm/snake-case/-/snake-case-3.0.4.tgz",
@@ -5004,23 +4988,6 @@
"engines": {
"node": ">= 4.0.0"
}
- },
- "node_modules/zrender": {
- "version": "5.6.1",
- "resolved": "https://nexus.ngaiot.com/repository/fs-npm/zrender/-/zrender-5.6.1.tgz",
- "integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "tslib": "2.3.0"
- }
- },
- "node_modules/zrender/node_modules/tslib": {
- "version": "2.3.0",
- "resolved": "https://nexus.ngaiot.com/repository/fs-npm/tslib/-/tslib-2.3.0.tgz",
- "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
- "dev": true,
- "license": "0BSD"
}
}
}
diff --git a/package.json b/package.json
index 48391ac..b19adc5 100644
--- a/package.json
+++ b/package.json
@@ -16,11 +16,11 @@
"devDependencies": {
"@ant-design/icons": "^5.4.0",
"antd": "^5.19.0",
+ "chart.js": "^4.5.0",
"react": "18.x",
+ "react-chartjs-2": "^5.3.0",
"react-dom": "18.x",
"react-redux": "^9.1.2",
- "echarts": "^5.6.0",
- "echarts-for-react": "^3.0.2",
"react-router-dom": "^6.26.0"
},
"dependencies": {
diff --git a/server/tcpProxy/index.js b/server/tcpProxy/index.js
index afba718..cef885e 100644
--- a/server/tcpProxy/index.js
+++ b/server/tcpProxy/index.js
@@ -38,7 +38,7 @@ function setupTcpProxy(conf) {
// 尝试解析为文本
try {
textData = data.toString('utf8');
- console.log('收到TCP数据片段:', textData.length, '字节');
+ // console.log('收到TCP数据片段:', textData.length, '字节');
} catch (e) {
console.log('TCP数据无法解析为文本');
return;
@@ -59,8 +59,8 @@ function setupTcpProxy(conf) {
// 转发完整消息到WebSocket
if (ws.readyState === WebSocket.OPEN) {
- console.log('准备发送完整消息到WebSocket:', completeMessage.length, '字节');
- console.log('消息内容:', completeMessage);
+ // console.log('准备发送完整消息到WebSocket:', completeMessage.length, '字节');
+ // console.log('消息内容:', completeMessage);
ws.send(completeMessage, (err) => {
if (err) {
console.error('WebSocket发送数据错误:', err);