refactor: simplify graph code and adjust text margins

This commit is contained in:
wonfen 2025-03-27 13:11:39 +08:00
parent daad623855
commit f800e2e3b6
2 changed files with 52 additions and 186 deletions

View File

@ -17,6 +17,7 @@
#### 优化了:
- 重构了后端内核管理逻辑,更轻量化和有效的管理内核,提高了性能和稳定性
- 前端统一刷新应用数据,优化数据获取和刷新逻辑
- 优化首页流量图表代码,调整图表文字边距
## v2.2.2

View File

@ -39,69 +39,35 @@ export interface EnhancedTrafficGraphRef {
// 时间范围类型
type TimeRange = 1 | 5 | 10; // 分钟
// 创建一个明确的类型
// 数据点类型
type DataPoint = ITrafficItem & { name: string; timestamp: number };
// 控制帧率的工具函数
const FPS_LIMIT = 1; // 限制为1fps因为数据每秒才更新一次
const FRAME_MIN_TIME = 1000 / FPS_LIMIT; // 每帧最小时间间隔即1000ms
// 全局存储流量数据历史记录
declare global {
interface Window {
trafficHistoryData?: DataPoint[];
trafficHistoryStyle?: "line" | "area";
trafficHistoryTimeRange?: TimeRange;
}
}
// 初始化全局存储
if (typeof window !== "undefined" && !window.trafficHistoryData) {
window.trafficHistoryData = [];
window.trafficHistoryStyle = "area";
window.trafficHistoryTimeRange = 10;
}
/**
*
* Recharts 线
*/
export const EnhancedTrafficGraph = memo(forwardRef<EnhancedTrafficGraphRef>(
(props, ref) => {
const theme = useTheme();
const { t } = useTranslation();
// 从全局变量恢复状态
const [timeRange, setTimeRange] = useState<TimeRange>(
window.trafficHistoryTimeRange || 10
);
const [chartStyle, setChartStyle] = useState<"line" | "area">(
window.trafficHistoryStyle || "area"
);
// 使用useRef存储数据避免不必要的重渲染
const dataBufferRef = useRef<DataPoint[]>([]);
// 只为渲染目的的状态
// 基础状态
const [timeRange, setTimeRange] = useState<TimeRange>(10);
const [chartStyle, setChartStyle] = useState<"line" | "area">("area");
const [displayData, setDisplayData] = useState<DataPoint[]>([]);
// 帧率控制
const lastUpdateTimeRef = useRef<number>(0);
const pendingUpdateRef = useRef<boolean>(false);
const rafIdRef = useRef<number | null>(null);
// 数据缓冲区
const dataBufferRef = useRef<DataPoint[]>([]);
// 根据时间范围计算保留的数据点数量
const getMaxPointsByTimeRange = useCallback(
(minutes: TimeRange): number => {
// 使用更低的采样率来减少点的数量每2秒一个点而不是每秒一个点
return minutes * 30; // 每分钟30个点(每2秒1个点)
},
[],
(minutes: TimeRange): number => minutes * 30,
[]
);
// 最大数据点数量 - 基于选择的时间范围
// 最大数据点数量
const MAX_BUFFER_SIZE = useMemo(
() => getMaxPointsByTimeRange(10),
[getMaxPointsByTimeRange],
[getMaxPointsByTimeRange]
);
// 颜色配置
@ -113,150 +79,51 @@ export const EnhancedTrafficGraph = memo(forwardRef<EnhancedTrafficGraphRef>(
tooltip: theme.palette.background.paper,
text: theme.palette.text.primary,
}),
[theme],
[theme]
);
// 切换时间范围
const handleTimeRangeClick = useCallback(() => {
setTimeRange((prevRange) => {
// 在1、5、10分钟之间循环切换
const newRange = prevRange === 1 ? 5 : prevRange === 5 ? 10 : 1;
window.trafficHistoryTimeRange = newRange; // 保存到全局
return newRange;
return prevRange === 1 ? 5 : prevRange === 5 ? 10 : 1;
});
}, []);
// 初始化数据缓冲区
useEffect(() => {
let initialBuffer: DataPoint[] = [];
// 如果全局有保存的数据,优先使用
if (window.trafficHistoryData && window.trafficHistoryData.length > 0) {
initialBuffer = [...window.trafficHistoryData];
// 确保数据长度符合要求
if (initialBuffer.length > MAX_BUFFER_SIZE) {
initialBuffer = initialBuffer.slice(-MAX_BUFFER_SIZE);
} else if (initialBuffer.length < MAX_BUFFER_SIZE) {
// 如果历史数据不足,则在前面补充空数据
const now = Date.now();
const oldestTimestamp = initialBuffer.length > 0
? initialBuffer[0].timestamp
: now - 10 * 60 * 1000;
const additionalPoints = MAX_BUFFER_SIZE - initialBuffer.length;
const timeInterval = initialBuffer.length > 0
? (initialBuffer[0].timestamp - (now - 10 * 60 * 1000)) / additionalPoints
: (10 * 60 * 1000) / MAX_BUFFER_SIZE;
const emptyPrefix: DataPoint[] = Array.from(
{ length: additionalPoints },
(_, index) => {
const pointTime = oldestTimestamp - (additionalPoints - index) * timeInterval;
const date = new Date(pointTime);
return {
up: 0,
down: 0,
timestamp: pointTime,
name: date.toLocaleTimeString("en-US", {
hour12: false,
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}),
};
}
);
initialBuffer = [...emptyPrefix, ...initialBuffer];
// 创建初始空数据
const now = Date.now();
const tenMinutesAgo = now - 10 * 60 * 1000;
const initialBuffer = Array.from(
{ length: MAX_BUFFER_SIZE },
(_, index) => {
const pointTime =
tenMinutesAgo + index * ((10 * 60 * 1000) / MAX_BUFFER_SIZE);
const date = new Date(pointTime);
return {
up: 0,
down: 0,
timestamp: pointTime,
name: date.toLocaleTimeString("en-US", {
hour12: false,
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}),
};
}
} else {
// 没有历史数据时,创建空的初始缓冲区
const now = Date.now();
const tenMinutesAgo = now - 10 * 60 * 1000;
initialBuffer = Array.from(
{ length: MAX_BUFFER_SIZE },
(_, index) => {
const pointTime =
tenMinutesAgo + index * ((10 * 60 * 1000) / MAX_BUFFER_SIZE);
const date = new Date(pointTime);
return {
up: 0,
down: 0,
timestamp: pointTime,
name: date.toLocaleTimeString("en-US", {
hour12: false,
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}),
};
}
);
}
);
dataBufferRef.current = initialBuffer;
window.trafficHistoryData = initialBuffer; // 保存到全局
// 更新显示数据
const pointsToShow = getMaxPointsByTimeRange(timeRange);
setDisplayData(initialBuffer.slice(-pointsToShow));
// 清理函数,取消任何未完成的动画帧
return () => {
if (rafIdRef.current !== null) {
cancelAnimationFrame(rafIdRef.current);
rafIdRef.current = null;
}
};
}, [MAX_BUFFER_SIZE, getMaxPointsByTimeRange, timeRange]);
// 处理数据更新并控制帧率的函数
const updateDisplayData = useCallback(() => {
if (pendingUpdateRef.current) {
pendingUpdateRef.current = false;
// 根据当前时间范围计算需要显示的点数
const pointsToShow = getMaxPointsByTimeRange(timeRange);
// 从缓冲区中获取最新的数据点
const newDisplayData = dataBufferRef.current.slice(-pointsToShow);
setDisplayData(newDisplayData);
}
rafIdRef.current = null;
}, [timeRange, getMaxPointsByTimeRange]);
// 节流更新函数
const throttledUpdateData = useCallback(() => {
pendingUpdateRef.current = true;
const now = performance.now();
const timeSinceLastUpdate = now - lastUpdateTimeRef.current;
if (rafIdRef.current === null) {
if (timeSinceLastUpdate >= FRAME_MIN_TIME) {
// 如果距离上次更新已经超过最小帧时间,立即更新
lastUpdateTimeRef.current = now;
rafIdRef.current = requestAnimationFrame(updateDisplayData);
} else {
// 否则,在适当的时间进行更新
const timeToWait = FRAME_MIN_TIME - timeSinceLastUpdate;
setTimeout(() => {
lastUpdateTimeRef.current = performance.now();
rafIdRef.current = requestAnimationFrame(updateDisplayData);
}, timeToWait);
}
}
}, [updateDisplayData]);
// 监听时间范围变化,更新显示数据
useEffect(() => {
throttledUpdateData();
}, [timeRange, throttledUpdateData]);
// 添加数据点方法
const appendData = useCallback((data: ITrafficItem) => {
// 安全处理数据
@ -281,24 +148,24 @@ export const EnhancedTrafficGraph = memo(forwardRef<EnhancedTrafficGraphRef>(
timestamp: timestamp,
};
// 更新ref保持原数组大小
// 更新缓冲区,保持原数组大小
const newBuffer = [...dataBufferRef.current.slice(1), newPoint];
dataBufferRef.current = newBuffer;
// 保存到全局变量
window.trafficHistoryData = newBuffer;
// 使用节流更新显示数据
throttledUpdateData();
}, [throttledUpdateData]);
// 更新显示数据
const pointsToShow = getMaxPointsByTimeRange(timeRange);
setDisplayData(newBuffer.slice(-pointsToShow));
}, [timeRange, getMaxPointsByTimeRange]);
// 监听时间范围变化,更新显示数据
useEffect(() => {
const pointsToShow = getMaxPointsByTimeRange(timeRange);
setDisplayData(dataBufferRef.current.slice(-pointsToShow));
}, [timeRange, getMaxPointsByTimeRange]);
// 切换图表样式
const toggleStyle = useCallback(() => {
setChartStyle((prev) => {
const newStyle = prev === "line" ? "area" : "line";
window.trafficHistoryStyle = newStyle; // 保存到全局
return newStyle;
});
setChartStyle((prev) => prev === "line" ? "area" : "line");
}, []);
// 暴露方法给父组件
@ -308,13 +175,12 @@ export const EnhancedTrafficGraph = memo(forwardRef<EnhancedTrafficGraphRef>(
appendData,
toggleStyle,
}),
[appendData, toggleStyle],
[appendData, toggleStyle]
);
// 格式化工具提示内容
const formatTooltip = useCallback((value: number, name: string, props: any) => {
const [num, unit] = parseTraffic(value);
// 使用props.dataKey判断是上传还是下载
return [`${num} ${unit}/s`, props?.dataKey === "up" ? t("Upload") : t("Download")];
}, [t]);
@ -327,7 +193,6 @@ export const EnhancedTrafficGraph = memo(forwardRef<EnhancedTrafficGraphRef>(
// 格式化X轴标签
const formatXLabel = useCallback((value: string) => {
if (!value) return "";
// 只显示小时和分钟
const parts = value.split(":");
return `${parts[0]}:${parts[1]}`;
}, []);
@ -352,7 +217,7 @@ export const EnhancedTrafficGraph = memo(forwardRef<EnhancedTrafficGraphRef>(
isAnimationActive: false, // 禁用动画以减少CPU使用
}), []);
// 曲线类型 - 使用线性曲线避免错位
// 曲线类型
const curveType = "monotone";
return (
@ -386,7 +251,7 @@ export const EnhancedTrafficGraph = memo(forwardRef<EnhancedTrafficGraphRef>(
tick={{ fontSize: 10, fill: colors.text }}
tickLine={{ stroke: colors.grid }}
axisLine={{ stroke: colors.grid }}
width={40}
width={43}
domain={[0, "auto"]}
/>
<Tooltip
@ -470,7 +335,7 @@ export const EnhancedTrafficGraph = memo(forwardRef<EnhancedTrafficGraphRef>(
tick={{ fontSize: 10, fill: colors.text }}
tickLine={{ stroke: colors.grid }}
axisLine={{ stroke: colors.grid }}
width={40}
width={43}
domain={[0, "auto"]}
padding={{ top: 10, bottom: 0 }}
/>