import React, { useCallback, useMemo, useState } from 'react'; import { Box, HStack, useTheme } from '@chakra-ui/react'; import { ResponsiveContainer, AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, type TooltipProps } from 'recharts'; import { type NameType, type ValueType } from 'recharts/types/component/DefaultTooltipContent'; import { formatNumber } from '@fastgpt/global/common/math/tools'; import FillRowTabs from '../Tabs/FillRowTabs'; import { useTranslation } from 'next-i18next'; import { cloneDeep } from 'lodash'; type AreaConfig = { dataKey: string; name: string; color: string; gradient?: boolean; }; type TooltipItem = { label: string; dataKey: string; color: string; formatter?: (value: number) => string; customValue?: (data: Record) => number; }; type AreaChartComponentProps = { data: Record[]; title: string; HeaderLeftChildren?: React.ReactNode; lines: AreaConfig[]; tooltipItems?: TooltipItem[]; defaultDisplayMode?: 'incremental' | 'cumulative'; enableIncremental?: boolean; enableCumulative?: boolean; enableTooltip?: boolean; startDateValue?: number; }; const CustomTooltip = ({ active, payload, tooltipItems }: TooltipProps & { tooltipItems?: TooltipItem[] }) => { const data = payload?.[0]?.payload; if (!active || !data || !tooltipItems) { return null; } return ( {data.xLabel || data.x} {tooltipItems.map((item, index) => { const value = item.customValue ? item.customValue(data) : data[item.dataKey]; const displayValue = item.formatter ? item.formatter(value) : formatNumber(value); return ( {item.label} {displayValue.toLocaleString()} ); })} ); }; const AreaChartComponent = ({ data, title, HeaderLeftChildren, lines, tooltipItems, defaultDisplayMode = 'incremental', enableIncremental = true, enableCumulative = true, startDateValue = 0 }: AreaChartComponentProps) => { const theme = useTheme(); const { t } = useTranslation(); const [displayMode, setDisplayMode] = useState<'incremental' | 'cumulative'>(defaultDisplayMode); // Tab list constant const tabList = useMemo( () => [ ...(enableIncremental ? [{ label: t('common:chart_mode_incremental'), value: 'incremental' as const }] : []), ...(enableCumulative ? [{ label: t('common:chart_mode_cumulative'), value: 'cumulative' as const }] : []) ], [enableCumulative, enableIncremental, t] ); // Y-axis number formatter function const formatYAxisNumber = useCallback((value: number): string => { if (value >= 1000000) { return value / 1000000 + 'M'; } else if (value >= 1000) { return value / 1000 + 'K'; } return value.toString(); }, []); // Process data based on display mode const processedData = useMemo(() => { if (displayMode === 'incremental') { return data; } // Cumulative mode: accumulate values for each line's dataKey const cloneData = cloneDeep(data); const dataKeys = lines.map((item) => item.dataKey); return cloneData.map((item, index) => { if (index === 0) { item[dataKeys[0]] = startDateValue + item[dataKeys[0]]; return item; } dataKeys.forEach((key) => { if (typeof item[key] === 'number') { item[key] += cloneData[index - 1][key]; } }); return item; }); }, [displayMode, data, lines, startDateValue]); // Generate gradient definitions const gradientDefs = useMemo( () => ( {lines.map((line) => ( ))} ), [lines] ); return ( <> {title} {HeaderLeftChildren} {tabList.length > 1 && ( list={tabList} py={0.5} px={2} value={displayMode} onChange={setDisplayMode} /> )} {gradientDefs} {tooltipItems && } />} {lines.map((line, index) => ( ))} ); }; export default AreaChartComponent;