diff --git a/src/components/charts/bar-leaderboard-chart.tsx b/src/components/charts/bar-leaderboard-chart.tsx index 2f28c570..b666395a 100755 --- a/src/components/charts/bar-leaderboard-chart.tsx +++ b/src/components/charts/bar-leaderboard-chart.tsx @@ -189,14 +189,6 @@ export function BarLeaderboardChart({ el.scrollBy({ left: delta, behavior: 'smooth' }); }, []); - if (!data.length) { - return ( -
- 暂无数据 -
- ); - } - const axisKey = mode === 'provider' ? 'provider' : 'normalizedModelDisplay'; const metricAxis = metric === 'ttft' @@ -209,7 +201,8 @@ export function BarLeaderboardChart({ } : getMetricAxisProps('tps'); - const showLabelText = effectiveMode === 'scroll' || data.length <= (isMobile ? 6 : 12); + const showLabelText = data.length <= (isMobile ? 6 : 12); + const xAxisHeight = showLabelText ? (isMobile ? 40 : 52) : (isMobile ? 24 : 30); const hasOverflow = effectiveMode === 'scroll' && innerWidth > wrapperWidth; const showLeftFade = hasOverflow && scrollState.left > 4; const showRightFade = hasOverflow && scrollState.left < scrollState.max - 4; @@ -228,8 +221,8 @@ export function BarLeaderboardChart({ interval={effectiveMode === 'scroll' ? 0 : isMobile ? 'preserveStartEnd' : 0} tickLine={false} axisLine={false} - tick={} - height={isMobile ? 40 : 52} + tick={} + height={xAxisHeight} /> ({ [data, dataKey, axisKey, mode, metric, tooltipContent, isMobile, effectiveMode, innerWidth, effectiveHeight, showLabelText, metricAxis], ); + if (!data.length) { + return ( +
+ 暂无数据 +
+ ); + } + return (
({ ) : null} {effectiveMode === 'scroll' ? ( -
+
可横向拖动 / 滚轮 / 滑动浏览全部 {data.length} 项
) : null} @@ -349,9 +350,10 @@ interface BarXAxisTickProps { data: BarLeaderboardDatumBase[]; isMobile: boolean; showText: boolean; + axisHeight: number; } -function BarXAxisTick({ x = 0, y = 0, payload, mode, data, isMobile, showText }: BarXAxisTickProps) { +function BarXAxisTick({ x = 0, y = 0, payload, mode, data, isMobile, showText, axisHeight }: BarXAxisTickProps) { if (!payload) return null; const value = String(payload.value ?? ''); const record = data.find((d) => { @@ -363,13 +365,16 @@ function BarXAxisTick({ x = 0, y = 0, payload, mode, data, isMobile, showText }: const maxLen = isMobile ? 6 : 10; const label = value.length > maxLen ? `${value.slice(0, maxLen)}…` : value; const glyphSize = isMobile ? 14 : 18; + const glyphContainerHeight = glyphSize + 4; + const glyphY = showText ? 2 : Math.max(0, (axisHeight - glyphContainerHeight) / 2); + const textY = isMobile ? 26 : 34; return (
@@ -383,7 +388,7 @@ function BarXAxisTick({ x = 0, y = 0, payload, mode, data, isMobile, showText }: {showText ? ( (() => + typeof window === 'undefined' ? false : window.matchMedia('(min-width: 1280px)').matches, + ); + + useLayoutEffect(() => { + if (typeof window === 'undefined') return; + const mq = window.matchMedia('(min-width: 1280px)'); + const update = () => setIsDesktop(mq.matches); + update(); + mq.addEventListener('change', update); + return () => mq.removeEventListener('change', update); + }, []); + + return isDesktop; +} + +function useElementHeight() { + const ref = useRef(null); + const [height, setHeight] = useState(0); + + useLayoutEffect(() => { + const el = ref.current; + if (!el) return; + + const update = () => setHeight(el.getBoundingClientRect().height); + update(); + + const ro = new ResizeObserver(update); + ro.observe(el); + return () => ro.disconnect(); + }, []); + + return [ref, height] as const; +} + export function LeaderboardsRoute() { const { latest, loading } = useBenchmarkData(); const enriched = useEnrichedResults(latest); const providerAverages = useProviderAverages(enriched); + const isDesktopLeaderboardLayout = useIsDesktopLeaderboardLayout(); + const [compositeChartRef, compositeChartHeight] = useElementHeight(); + const [tpsChartRef, tpsChartHeight] = useElementHeight(); + const [ttftChartRef, ttftChartHeight] = useElementHeight(); const [compositeMode, setCompositeMode] = useState('model'); const [tpsMode, setTpsMode] = useState('model'); @@ -72,245 +117,263 @@ export function LeaderboardsRoute() { {/* === 综合 === */}
- - 综合排行榜 - - - } - subtitle="综合评分 = TPS 分 × 0.6 + TTFT 分 × 0.4,兼顾生成速度与首字响应。" - action={} - > -
- {loading && !compositeSorted.length ? ( -
正在加载…
- ) : compositeMode === 'provider' ? ( - compositeProviderSorted.map((item, index) => ( - + + 综合排行榜 + + + } + subtitle="综合评分 = TPS 分 × 0.6 + TTFT 分 × 0.4,兼顾生成速度与首字响应。" + action={} + className={leaderboardCardClassName} + contentClassName={leaderboardContentClassName} + > +
+ {loading && !compositeSorted.length ? ( +
正在加载…
+ ) : compositeMode === 'provider' ? ( + compositeProviderSorted.map((item, index) => ( + {formatNumber(item.compositeScore)} 分} + secondary={ + <> + TPS {formatNumber(item.averageMedianTps || item.averageTps)} · TTFT {formatNumber(item.averageTtft, 0)} ms + + } + /> + )) + ) : ( + compositeSorted.map((item, index) => ( + {formatNumber(item.compositeScore)} 分} + secondary={ + <> + TPS {formatNumber(item.medianTps || item.avgTps)} · TTFT {formatNumber(item.avgTtft, 0)} ms + + } + /> + )) + )} +
+
+
+ +
+ +
+
+
厂家平均
+ ({ ...item, label: item.provider }))} + dataKey="compositeScore" mode="provider" - provider={item.provider} - model={item.model} - modelCount={item.modelCount} - hasThinking={item.hasThinking} - hasAnomaly={item.hasAnomaly} - primary={<>{formatNumber(item.compositeScore)} 分} - secondary={ - <> - TPS {formatNumber(item.averageMedianTps || item.averageTps)} · TTFT {formatNumber(item.averageTtft, 0)} ms - - } + metric="composite" + tooltipContent={} /> - )) - ) : ( - compositeSorted.map((item, index) => ( - +
+
模型逐一
+ {formatNumber(item.compositeScore)} 分} - secondary={ - <> - TPS {formatNumber(item.medianTps || item.avgTps)} · TTFT {formatNumber(item.avgTtft, 0)} ms - - } + metric="composite" + tooltipContent={} /> - )) - )} -
- - - -
-
-
厂家平均
- ({ ...item, label: item.provider }))} - dataKey="compositeScore" - mode="provider" - metric="composite" - tooltipContent={} - /> +
-
-
模型逐一
- } - /> -
-
- + +
{/* === TPS === */}
- - TPS 排行榜 - - - } - subtitle={tpsMode === 'provider' ? '按厂商聚合,展示多个模型的平均 MedianTPS。' : '最近一次拨测全部模型的 MedianTPS。'} - action={} - > -
- {loading && !enriched.length ? ( -
正在加载…
- ) : tpsMode === 'provider' ? ( - providerAverages.map((item, index) => ( - + + TPS 排行榜 + + + } + subtitle={tpsMode === 'provider' ? '按厂商聚合,展示多个模型的平均 MedianTPS。' : '最近一次拨测全部模型的 MedianTPS。'} + action={} + className={leaderboardCardClassName} + contentClassName={leaderboardContentClassName} + > +
+ {loading && !enriched.length ? ( +
正在加载…
+ ) : tpsMode === 'provider' ? ( + providerAverages.map((item, index) => ( + {formatNumber(item.averageMedianTps || item.averageTps)} tok/s} + secondary={<>TTFT {formatNumber(item.averageTtft, 0)} ms} + /> + )) + ) : ( + tpsChartModels.map((item, index) => ( + {formatNumber(item.medianTps || item.avgTps)} tok/s} + secondary={<>TTFT {formatNumber(item.avgTtft, 0)} ms} + /> + )) + )} +
+
+
+ +
+ +
+
+
厂家平均 MedianTPS
+ ({ ...item, label: item.provider }))} + dataKey="averageMedianTps" mode="provider" - provider={item.provider} - model={item.model} - modelCount={item.modelCount} - hasThinking={item.hasThinking} - hasAnomaly={item.hasAnomaly} - primary={<>{formatNumber(item.averageMedianTps || item.averageTps)} tok/s} - secondary={<>TTFT {formatNumber(item.averageTtft, 0)} ms} + metric="tps" + tooltipContent={} /> - )) - ) : ( - tpsChartModels.map((item, index) => ( - +
+
模型平均 MedianTPS
+ {formatNumber(item.medianTps || item.avgTps)} tok/s} - secondary={<>TTFT {formatNumber(item.avgTtft, 0)} ms} + metric="tps" + tooltipContent={} /> - )) - )} -
- - - -
-
-
厂家平均 MedianTPS
- ({ ...item, label: item.provider }))} - dataKey="averageMedianTps" - mode="provider" - metric="tps" - tooltipContent={} - /> -
-
-
模型平均 MedianTPS
- } - /> +
-
- + +
{/* === TTFT === */}
- - TTFT 排行榜 - - - } - subtitle={ttftMode === 'provider' ? '按厂商聚合平均 TTFT,越低越靠前。' : '按模型排序平均 TTFT,越低越靠前。'} - action={} - > -
- {loading && !enriched.length ? ( -
正在加载…
- ) : ttftMode === 'provider' ? ( - ttftSortedProviders.map((item, index) => ( - + + TTFT 排行榜 + + + } + subtitle={ttftMode === 'provider' ? '按厂商聚合平均 TTFT,越低越靠前。' : '按模型排序平均 TTFT,越低越靠前。'} + action={} + className={leaderboardCardClassName} + contentClassName={leaderboardContentClassName} + > +
+ {loading && !enriched.length ? ( +
正在加载…
+ ) : ttftMode === 'provider' ? ( + ttftSortedProviders.map((item, index) => ( + {formatNumber(item.averageTtft, 0)} ms} + secondary={<>{formatNumber(item.averageMedianTps || item.averageTps)} tok/s} + /> + )) + ) : ( + ttftSortedModels.map((item, index) => ( + {formatNumber(item.avgTtft, 0)} ms} + secondary={<>{formatNumber(item.medianTps || item.avgTps)} tok/s} + /> + )) + )} +
+
+
+ +
+ +
+
+
厂家平均 TTFT
+ ({ ...item, label: item.provider }))} + dataKey="averageTtftChart" mode="provider" - provider={item.provider} - model={item.model} - modelCount={item.modelCount} - hasThinking={item.hasThinking} - hasAnomaly={item.hasAnomaly} - primary={<>{formatNumber(item.averageTtft, 0)} ms} - secondary={<>{formatNumber(item.averageMedianTps || item.averageTps)} tok/s} + metric="ttft" + tooltipContent={} /> - )) - ) : ( - ttftSortedModels.map((item, index) => ( - +
+
模型平均 TTFT
+ {formatNumber(item.avgTtft, 0)} ms} - secondary={<>{formatNumber(item.medianTps || item.avgTps)} tok/s} + metric="ttft" + tooltipContent={} /> - )) - )} -
- - - -
-
-
厂家平均 TTFT
- ({ ...item, label: item.provider }))} - dataKey="averageTtftChart" - mode="provider" - metric="ttft" - tooltipContent={} - /> -
-
-
模型平均 TTFT
- } - /> +
-
- + +