([]);
const [timeRange, setTimeRange] = useState<[number, number] | null>(null);
+ const [brushIndices, setBrushIndices] = useState<{
+ startIndex?: number;
+ endIndex?: number;
+ }>({});
const [cutPeak, setCutPeak] = useState(false);
const [connectBreaks, setConnectBreaks] = useState(
useConfigItem("enableConnectBreaks")
);
+ const [isResetting, setIsResetting] = useState(false);
const maxPointsToRender = useConfigItem("pingChartMaxPoints") || 0; // 0表示不限制
const isMobile = useIsMobile();
@@ -55,25 +61,13 @@ const PingChart = memo(({ node, hours }: PingChartProps) => {
}
}, [pingHistory?.tasks]);
- const lableFormatter = useCallback(
- (value: any) => {
- const date = new Date(value);
- if (hours === 0) {
- return date.toLocaleTimeString([], {
- hour: "2-digit",
- minute: "2-digit",
- second: "2-digit",
- });
- }
- return date.toLocaleString([], {
- month: "2-digit",
- day: "2-digit",
- hour: "2-digit",
- minute: "2-digit",
- });
- },
- [hours]
- );
+ useEffect(() => {
+ if (isResetting) {
+ setTimeRange(null);
+ setBrushIndices({});
+ setIsResetting(false);
+ }
+ }, [isResetting]);
const chartMargin = { top: 8, right: 16, bottom: 8, left: 16 };
@@ -170,32 +164,6 @@ const PingChart = memo(({ node, hours }: PingChartProps) => {
return [...pingHistory.tasks].sort((a, b) => a.id - b.id);
}, [pingHistory?.tasks]);
- const generateColor = useCallback(
- (taskName: string, total: number) => {
- const index = sortedTasks.findIndex((t) => t.name === taskName);
- if (index === -1) return "#000000"; // Fallback color
-
- const hue = (index * (360 / total)) % 360;
-
- // 使用OKLCH色彩空间,优化折线图的颜色区分度
- // L=0.7 (较高亮度,便于在图表背景上清晰显示)
- // C=0.2 (较高饱和度,增强颜色区分度)
- const oklchColor = `oklch(0.6 0.2 ${hue} / .8)`;
-
- // 为不支持OKLCH的浏览器提供HSL备用色
- // 使用更高的饱和度和适中的亮度来匹配OKLCH的视觉效果
- const hslFallback = `hsl(${hue}, 50%, 60%)`;
-
- // 检查浏览器是否支持OKLCH
- if (CSS.supports("color", oklchColor)) {
- return oklchColor;
- } else {
- return hslFallback;
- }
- },
- [sortedTasks]
- );
-
const breakPoints = useMemo(() => {
if (!connectBreaks || !chartData || chartData.length < 2) {
return [];
@@ -219,13 +187,13 @@ const PingChart = memo(({ node, hours }: PingChartProps) => {
if (isBreak) {
points.push({
x: currentPoint.time,
- color: generateColor(task.name, sortedTasks.length),
+ color: generateColor(task.name, sortedTasks),
});
}
}
}
return points;
- }, [chartData, sortedTasks, visiblePingTasks, generateColor, connectBreaks]);
+ }, [chartData, sortedTasks, visiblePingTasks, connectBreaks]);
const taskStats = useMemo(() => {
if (!pingHistory?.records || !sortedTasks.length) return [];
@@ -241,10 +209,10 @@ const PingChart = memo(({ node, hours }: PingChartProps) => {
value: latestValue,
time: latestTime,
loss: loss,
- color: generateColor(task.name, sortedTasks.length),
+ color: generateColor(task.name, sortedTasks),
};
});
- }, [pingHistory?.records, sortedTasks, generateColor, timeRange]);
+ }, [pingHistory?.records, sortedTasks, timeRange]);
return (
@@ -342,7 +310,7 @@ const PingChart = memo(({ node, hours }: PingChartProps) => {
-
+
+
@@ -366,7 +366,7 @@ const PingChart = memo(({ node, hours }: PingChartProps) => {
{
});
}}
tick={{ fill: "var(--theme-text-muted-color)" }}
+ axisLine={{
+ stroke: "var(--theme-line-muted-color)",
+ }}
scale="time"
/>
}
+ content={
+ lableFormatter(value, hours)}
+ />
+ }
/>
{connectBreaks &&
breakPoints.map((point, index) => (
@@ -417,7 +427,7 @@ const PingChart = memo(({ node, hours }: PingChartProps) => {
type={"monotone"}
dataKey={String(task.id)}
name={task.name}
- stroke={generateColor(task.name, sortedTasks.length)}
+ stroke={generateColor(task.name, sortedTasks)}
strokeWidth={2}
hide={!visiblePingTasks.includes(task.id)}
dot={false}
@@ -425,6 +435,7 @@ const PingChart = memo(({ node, hours }: PingChartProps) => {
/>
))}
{
chartData[e.startIndex].time,
chartData[e.endIndex].time,
]);
+ setBrushIndices({
+ startIndex: e.startIndex,
+ endIndex: e.endIndex,
+ });
} else {
setTimeRange(null);
+ setBrushIndices({});
}
}}
/>
diff --git a/src/utils/chartHelper.ts b/src/utils/chartHelper.ts
new file mode 100644
index 0000000..9bedcd2
--- /dev/null
+++ b/src/utils/chartHelper.ts
@@ -0,0 +1,77 @@
+import type { PingTask } from "@/types/node";
+
+/**
+ * 根据任务名称和任务列表生成颜色
+ * @param taskName - 任务名称
+ * @param sortedTasks - 已排序的任务列表
+ * @returns CSS 颜色字符串
+ */
+export const generateColor = (taskName: string, sortedTasks: PingTask[]) => {
+ const index = sortedTasks.findIndex((t) => t.name === taskName);
+ if (index === -1) return "#000000"; // Fallback color
+
+ const total = sortedTasks.length;
+ const hue = (index * (360 / total)) % 360;
+
+ // 使用OKLCH色彩空间,优化折线图的颜色区分度
+ const oklchColor = `oklch(0.6 0.2 ${hue} / .8)`;
+
+ // 为不支持OKLCH的浏览器提供HSL备用色
+ const hslFallback = `hsl(${hue}, 50%, 60%)`;
+
+ // 检查浏览器是否支持OKLCH
+ if (
+ typeof window !== "undefined" &&
+ window.CSS &&
+ CSS.supports("color", oklchColor)
+ ) {
+ return oklchColor;
+ } else {
+ return hslFallback;
+ }
+};
+
+/**
+ * 格式化图表X轴的标签
+ * @param value - 时间戳
+ * @param hours - 当前选择的时间范围(小时)
+ * @returns 格式化后的时间字符串
+ */
+export const lableFormatter = (value: any, hours: number) => {
+ const date = new Date(value);
+ if (hours === 0) {
+ return date.toLocaleTimeString([], {
+ hour: "2-digit",
+ minute: "2-digit",
+ second: "2-digit",
+ });
+ }
+ return date.toLocaleString([], {
+ month: "2-digit",
+ day: "2-digit",
+ hour: "2-digit",
+ minute: "2-digit",
+ });
+};
+
+/**
+ * 格式化负载图表X轴的时间标签
+ * @param value - 时间戳
+ * @param index - 索引
+ * @param dataLength - 数据总长度
+ * @returns 格式化后的时间字符串 (只显示首尾)
+ */
+export const loadChartTimeFormatter = (
+ value: any,
+ index: number,
+ dataLength: number
+) => {
+ if (dataLength === 0) return "";
+ if (index === 0 || index === dataLength - 1) {
+ return new Date(value).toLocaleTimeString([], {
+ hour: "2-digit",
+ minute: "2-digit",
+ });
+ }
+ return "";
+};