From ed35e8c122598544b420235049e014b47fdd5774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=93=E9=BC=A0?= <71394853+hamster1963@users.noreply.github.com> Date: Fri, 25 Apr 2025 10:50:01 +0800 Subject: [PATCH] Feat: Multiple choice (#41) * feat: enable multi-selection for charts in NetworkChart component * feat: add clear selections button for active charts in NetworkChart component * chore: auto-fix linting and formatting issues --- src/components/NetworkChart.tsx | 205 ++++++++++++++++++-------------- 1 file changed, 113 insertions(+), 92 deletions(-) diff --git a/src/components/NetworkChart.tsx b/src/components/NetworkChart.tsx index 30b09ac..288e64b 100644 --- a/src/components/NetworkChart.tsx +++ b/src/components/NetworkChart.tsx @@ -90,21 +90,30 @@ export const NetworkChartClient = React.memo(function NetworkChart({ }) { const { t } = useTranslation() - const defaultChart = "All" - const customBackgroundImage = (window.CustomBackgroundImage as string) !== "" ? window.CustomBackgroundImage : undefined const forcePeakCutEnabled = (window.ForcePeakCutEnabled as boolean) ?? false - const [activeChart, setActiveChart] = React.useState(defaultChart) + // Change from string to string array for multi-selection + const [activeCharts, setActiveCharts] = React.useState([]) const [isPeakEnabled, setIsPeakEnabled] = React.useState(forcePeakCutEnabled) - const handleButtonClick = useCallback( - (chart: string) => { - setActiveChart((prev) => (prev === chart ? defaultChart : chart)) - }, - [defaultChart], - ) + // Function to clear all selected charts + const clearAllSelections = useCallback(() => { + setActiveCharts([]) + }, []) + + // Updated to handle multiple selections + const handleButtonClick = useCallback((chart: string) => { + setActiveCharts((prev) => { + // If chart is already selected, remove it + if (prev.includes(chart)) { + return prev.filter((c) => c !== chart) + } + // Otherwise, add it to selected charts + return [...prev, chart] + }) + }, []) const getColorByIndex = useCallback( (chart: string) => { @@ -119,7 +128,7 @@ export const NetworkChartClient = React.memo(function NetworkChart({ chartDataKey.map((key) => ( )), - [chartDataKey, activeChart, chartData, handleButtonClick], + [chartDataKey, activeCharts, chartData, handleButtonClick], ) const chartLines = useMemo(() => { - if (activeChart !== defaultChart) { - return + // If we have active charts selected, render only those + if (activeCharts.length > 0) { + return activeCharts.map((chart) => ( + + )) } + // Otherwise show all charts (default view) return chartDataKey.map((key) => ( )) - }, [activeChart, defaultChart, chartDataKey, getColorByIndex]) + }, [activeCharts, chartDataKey, getColorByIndex]) const processedData = useMemo(() => { if (!isPeakEnabled) { - return activeChart === defaultChart ? formattedData : chartData[activeChart] + // Always use formattedData when multiple charts are selected or none selected + return formattedData } - const data = (activeChart === defaultChart ? formattedData : chartData[activeChart]) as ResultItem[] + // For peak cutting, always use the formatted data which contains all series + const data = formattedData const windowSize = 11 // 增加窗口大小以获取更好的统计效果 const alpha = 0.3 // EWMA平滑因子 @@ -200,43 +225,29 @@ export const NetworkChartClient = React.memo(function NetworkChart({ const window = data.slice(index - windowSize + 1, index + 1) const smoothed = { ...point } as ResultItem - if (activeChart === defaultChart) { - chartDataKey.forEach((key) => { - const values = window.map((w) => w[key]).filter((v) => v !== undefined && v !== null) as number[] + // Process all chart keys or just the selected ones + const keysToProcess = activeCharts.length > 0 ? activeCharts : chartDataKey - if (values.length > 0) { - const processed = processValues(values) - if (processed !== null) { - // 应用EWMA平滑 - if (ewmaHistory[key] === undefined) { - ewmaHistory[key] = processed - } else { - ewmaHistory[key] = alpha * processed + (1 - alpha) * ewmaHistory[key] - } - smoothed[key] = ewmaHistory[key] - } - } - }) - } else { - const values = window.map((w) => w.avg_delay).filter((v) => v !== undefined && v !== null) as number[] + keysToProcess.forEach((key) => { + const values = window.map((w) => w[key]).filter((v) => v !== undefined && v !== null) as number[] if (values.length > 0) { const processed = processValues(values) if (processed !== null) { - // 应用EWMA平滑 - if (ewmaHistory["current"] === undefined) { - ewmaHistory["current"] = processed + // Apply EWMA smoothing + if (ewmaHistory[key] === undefined) { + ewmaHistory[key] = processed } else { - ewmaHistory["current"] = alpha * processed + (1 - alpha) * ewmaHistory["current"] + ewmaHistory[key] = alpha * processed + (1 - alpha) * ewmaHistory[key] } - smoothed.avg_delay = ewmaHistory["current"] + smoothed[key] = ewmaHistory[key] } } - } + }) return smoothed }) - }, [isPeakEnabled, activeChart, formattedData, chartData, chartDataKey, defaultChart]) + }, [isPeakEnabled, activeCharts, formattedData, chartDataKey]) return ( {chartButtons} - - - - { - if (array.length < 6) { - return index === 0 || index === array.length - 1 - } +
+ {activeCharts.length > 0 && ( + + )} + + + + { + if (array.length < 6) { + return index === 0 || index === array.length - 1 + } - // 计算数据的总时间跨度(毫秒) - const timeSpan = array[array.length - 1].created_at - array[0].created_at - const hours = timeSpan / (1000 * 60 * 60) + // 计算数据的总时间跨度(毫秒) + const timeSpan = array[array.length - 1].created_at - array[0].created_at + const hours = timeSpan / (1000 * 60 * 60) - // 根据时间跨度调整显示间隔 - if (hours <= 12) { - // 12小时内,每60分钟显示一个刻度 - return index === 0 || index === array.length - 1 || new Date(item.created_at).getMinutes() % 60 === 0 - } - // 超过12小时,每2小时显示一个刻度 - const date = new Date(item.created_at) - return date.getMinutes() === 0 && date.getHours() % 2 === 0 - }) - .map((item) => item.created_at)} - tickFormatter={(value) => { - const date = new Date(value) - const minutes = date.getMinutes() - return minutes === 0 ? `${date.getHours()}:00` : `${date.getHours()}:${minutes}` - }} - /> - `${value}ms`} /> - { - return formatTime(payload[0].payload.created_at) - }} - /> - } - /> - {activeChart === defaultChart && } />} - {chartLines} - - + // 根据时间跨度调整显示间隔 + if (hours <= 12) { + // 12小时内,每60分钟显示一个刻度 + return index === 0 || index === array.length - 1 || new Date(item.created_at).getMinutes() % 60 === 0 + } + // 超过12小时,每2小时显示一个刻度 + const date = new Date(item.created_at) + return date.getMinutes() === 0 && date.getHours() % 2 === 0 + }) + .map((item) => item.created_at)} + tickFormatter={(value) => { + const date = new Date(value) + const minutes = date.getMinutes() + return minutes === 0 ? `${date.getHours()}:00` : `${date.getHours()}:${minutes}` + }} + /> + `${value}ms`} /> + { + return formatTime(payload[0].payload.created_at) + }} + /> + } + /> + } /> + {chartLines} + + +
)