@@ -837,9 +837,14 @@ export default function DashboardPage() {
837837
838838 const hourlySeries = useMemo ( ( ) => {
839839 if ( ! overviewData ?. byHour ) return [ ] as UsageSeriesPoint [ ] ;
840- if ( hourRange === "all" ) return overviewData . byHour ;
841- const hours = hourRange === "24h" ? 24 : 72 ;
842- return buildHourlySeries ( overviewData . byHour , hours , bucketTimezone ) ;
840+ const raw = hourRange === "all"
841+ ? overviewData . byHour
842+ : buildHourlySeries ( overviewData . byHour , hourRange === "24h" ? 24 : 72 , bucketTimezone ) ;
843+ // 输入仅计未命中缓存部分,避免与缓存字段在堆叠图中重复计数
844+ return raw . map ( p => ( {
845+ ...p ,
846+ inputTokens : Math . max ( 0 , ( p . inputTokens ?? 0 ) - ( p . cachedTokens ?? 0 ) )
847+ } ) ) ;
843848 } , [ hourRange , overviewData ?. byHour , bucketTimezone ] ) ;
844849
845850 const hourlyLineStyle = useMemo (
@@ -1556,9 +1561,17 @@ export default function DashboardPage() {
15561561 </ div >
15571562 </ div >
15581563 < div className = "mt-4 grid grid-cols-2 gap-x-6 gap-y-2 text-sm" >
1559- < div className = "flex items-center justify-between" >
1560- < span className = { darkMode ? "text-slate-400" : "text-slate-500" } > 输入</ span >
1561- < span className = "font-medium" style = { { color : darkMode ? "#fb7185" : "#e11d48" } } > { formatNumberWithCommas ( overviewData . totalInputTokens ) } </ span >
1564+ < div className = "flex items-center justify-between group cursor-default" >
1565+ < span className = { `relative ${ darkMode ? "text-slate-400" : "text-slate-500" } ` } >
1566+ < span className = "transition-opacity duration-200 group-hover:opacity-0 select-none" > 输入</ span >
1567+ < span className = "absolute left-0 top-0 whitespace-nowrap opacity-0 transition-opacity duration-200 group-hover:opacity-100" > 未命中输入</ span >
1568+ </ span >
1569+ < span className = "relative font-medium" style = { { color : darkMode ? "#fb7185" : "#e11d48" } } >
1570+ < span className = "transition-opacity duration-200 group-hover:opacity-0 select-none" > { formatNumberWithCommas ( overviewData . totalInputTokens ) } </ span >
1571+ < span className = "absolute right-0 top-0 whitespace-nowrap opacity-0 transition-opacity duration-200 group-hover:opacity-100" >
1572+ { formatNumberWithCommas ( Math . max ( 0 , overviewData . totalInputTokens - overviewData . totalCachedTokens ) ) }
1573+ </ span >
1574+ </ span >
15621575 </ div >
15631576 < div className = "flex items-center justify-between" >
15641577 < span className = { darkMode ? "text-slate-400" : "text-slate-500" } > 输出</ span >
@@ -1568,9 +1581,21 @@ export default function DashboardPage() {
15681581 < span className = { darkMode ? "text-slate-400" : "text-slate-500" } > 思考</ span >
15691582 < span className = "font-medium" style = { { color : darkMode ? "#fbbf24" : "#d97706" } } > { formatNumberWithCommas ( overviewData . totalReasoningTokens ) } </ span >
15701583 </ div >
1571- < div className = "flex items-center justify-between" >
1572- < span className = { darkMode ? "text-slate-400" : "text-slate-500" } > 缓存</ span >
1573- < span className = "font-medium" style = { { color : darkMode ? "#c084fc" : "#9333ea" } } > { formatNumberWithCommas ( overviewData . totalCachedTokens ) } </ span >
1584+ < div className = "flex items-center justify-between group cursor-default" >
1585+ < span className = { `relative ${ darkMode ? "text-slate-400" : "text-slate-500" } ` } >
1586+ < span className = "transition-opacity duration-200 group-hover:opacity-0 select-none" > 缓存命中率</ span >
1587+ < span className = "absolute left-0 top-0 whitespace-nowrap opacity-0 transition-opacity duration-200 group-hover:opacity-100" > 缓存</ span >
1588+ </ span >
1589+ < span className = "relative font-medium" style = { { color : darkMode ? "#c084fc" : "#9333ea" } } >
1590+ < span className = "transition-opacity duration-200 group-hover:opacity-0 select-none" >
1591+ { overviewData . totalInputTokens > 0
1592+ ? `${ ( ( overviewData . totalCachedTokens / overviewData . totalInputTokens ) * 100 ) . toFixed ( 2 ) } %`
1593+ : "0.00%" }
1594+ </ span >
1595+ < span className = "absolute right-0 top-0 whitespace-nowrap opacity-0 transition-opacity duration-200 group-hover:opacity-100" >
1596+ { formatNumberWithCommas ( overviewData . totalCachedTokens ) }
1597+ </ span >
1598+ </ span >
15741599 </ div >
15751600 </ div >
15761601 </ div >
@@ -2065,9 +2090,9 @@ export default function DashboardPage() {
20652090 />
20662091 { /* 堆积柱状图 - 柔和配色,仅顶部圆角,增强动画 */ }
20672092 < Bar hide = { ! hourlyVisible . inputTokens } yAxisId = "right" dataKey = "inputTokens" name = "输入" stackId = "tokens" fill = "url(#gradInput)" fillOpacity = { 0.8 } animationDuration = { 600 } barSize = { 24 } />
2093+ < Bar hide = { ! hourlyVisible . cachedTokens } yAxisId = "right" dataKey = "cachedTokens" name = "缓存" stackId = "tokens" fill = "url(#gradCached)" fillOpacity = { 0.8 } animationDuration = { 600 } barSize = { 24 } />
20682094 < Bar hide = { ! hourlyVisible . outputTokens } yAxisId = "right" dataKey = "outputTokens" name = "输出" stackId = "tokens" fill = "url(#gradOutput)" fillOpacity = { 0.8 } animationDuration = { 600 } barSize = { 24 } />
2069- < Bar hide = { ! hourlyVisible . reasoningTokens } yAxisId = "right" dataKey = "reasoningTokens" name = "思考" stackId = "tokens" fill = "url(#gradReasoning)" fillOpacity = { 0.8 } animationDuration = { 600 } barSize = { 24 } />
2070- < Bar hide = { ! hourlyVisible . cachedTokens } yAxisId = "right" dataKey = "cachedTokens" name = "缓存" stackId = "tokens" fill = "url(#gradCached)" fillOpacity = { 0.8 } radius = { [ 4 , 4 , 0 , 0 ] } animationDuration = { 600 } barSize = { 24 } />
2095+ < Bar hide = { ! hourlyVisible . reasoningTokens } yAxisId = "right" dataKey = "reasoningTokens" name = "思考" stackId = "tokens" fill = "url(#gradReasoning)" fillOpacity = { 0.8 } radius = { [ 4 , 4 , 0 , 0 ] } animationDuration = { 600 } barSize = { 24 } />
20712096 { /* 曲线在最上层 - 带描边突出显示 */ }
20722097 < Line
20732098 hide = { ! hourlyVisible . requests }
@@ -2772,9 +2797,9 @@ export default function DashboardPage() {
27722797 const keyMap : Record < string , string > = {
27732798 "请求数" : "requests" ,
27742799 "输入" : "inputTokens" ,
2800+ "缓存" : "cachedTokens" ,
27752801 "输出" : "outputTokens" ,
2776- "思考" : "reasoningTokens" ,
2777- "缓存" : "cachedTokens"
2802+ "思考" : "reasoningTokens"
27782803 } ;
27792804 const key = keyMap [ value ] ;
27802805 const isVisible = hourlyVisible [ key ] ;
@@ -2786,9 +2811,9 @@ export default function DashboardPage() {
27862811 const colors : Record < string , string > = {
27872812 "请求数" : darkMode ? "#60a5fa" : "#3b82f6" ,
27882813 "输入" : darkMode ? "#fb7185" : "#e11d48" ,
2814+ "缓存" : darkMode ? "#c084fc" : "#9333ea" ,
27892815 "输出" : darkMode ? "#4ade80" : "#16a34a" ,
2790- "思考" : darkMode ? "#fbbf24" : "#d97706" ,
2791- "缓存" : darkMode ? "#c084fc" : "#9333ea"
2816+ "思考" : darkMode ? "#fbbf24" : "#d97706"
27922817 } ;
27932818 return < span style = { { color : colors [ value ] || "inherit" , fontWeight : 500 } } title = "按住 Ctrl 点击可只显示该项" > { value } </ span > ;
27942819 } }
@@ -2805,16 +2830,16 @@ export default function DashboardPage() {
28052830 { fullscreenHourlyMode === "area" ? (
28062831 < >
28072832 < Area hide = { ! hourlyVisible . inputTokens } yAxisId = "right" dataKey = "inputTokens" name = "输入" stackId = "tokens" type = "monotone" stroke = "#fca5a5" fill = "url(#gradInputFS)" fillOpacity = { 0.35 } animationDuration = { 600 } />
2833+ < Area hide = { ! hourlyVisible . cachedTokens } yAxisId = "right" dataKey = "cachedTokens" name = "缓存" stackId = "tokens" type = "monotone" stroke = "#c084fc" fill = "url(#gradCachedFS)" fillOpacity = { 0.35 } animationDuration = { 600 } />
28082834 < Area hide = { ! hourlyVisible . outputTokens } yAxisId = "right" dataKey = "outputTokens" name = "输出" stackId = "tokens" type = "monotone" stroke = "#4ade80" fill = "url(#gradOutputFS)" fillOpacity = { 0.35 } animationDuration = { 600 } />
28092835 < Area hide = { ! hourlyVisible . reasoningTokens } yAxisId = "right" dataKey = "reasoningTokens" name = "思考" stackId = "tokens" type = "monotone" stroke = "#fbbf24" fill = "url(#gradReasoningFS)" fillOpacity = { 0.35 } animationDuration = { 600 } />
2810- < Area hide = { ! hourlyVisible . cachedTokens } yAxisId = "right" dataKey = "cachedTokens" name = "缓存" stackId = "tokens" type = "monotone" stroke = "#c084fc" fill = "url(#gradCachedFS)" fillOpacity = { 0.35 } animationDuration = { 600 } />
28112836 </ >
28122837 ) : (
28132838 < >
28142839 < Bar hide = { ! hourlyVisible . inputTokens } yAxisId = "right" dataKey = "inputTokens" name = "输入" stackId = "tokens" fill = "url(#gradInputFS)" fillOpacity = { 0.8 } animationDuration = { 600 } barSize = { 32 } />
2815- < Bar hide = { ! hourlyVisible . outputTokens } yAxisId = "right" dataKey = "outputTokens" name = "输出" stackId = "tokens" fill = "url(#gradOutputFS)" fillOpacity = { 0.8 } animationDuration = { 600 } barSize = { 32 } />
2816- < Bar hide = { ! hourlyVisible . reasoningTokens } yAxisId = "right" dataKey = "reasoningTokens" name = "思考" stackId = "tokens" fill = "url(#gradReasoningFS)" fillOpacity = { 0.8 } animationDuration = { 600 } barSize = { 32 } />
28172840 < Bar hide = { ! hourlyVisible . cachedTokens } yAxisId = "right" dataKey = "cachedTokens" name = "缓存" stackId = "tokens" fill = "url(#gradCachedFS)" fillOpacity = { 0.8 } animationDuration = { 600 } barSize = { 32 } />
2841+ < Bar hide = { ! hourlyVisible . outputTokens } yAxisId = "right" dataKey = "outputTokens" name = "输出" stackId = "tokens" fill = "url(#gradOutputFS)" fillOpacity = { 0.8 } animationDuration = { 600 } barSize = { 32 } />
2842+ < Bar hide = { ! hourlyVisible . reasoningTokens } yAxisId = "right" dataKey = "reasoningTokens" name = "思考" stackId = "tokens" fill = "url(#gradReasoningFS)" fillOpacity = { 0.8 } radius = { [ 4 , 4 , 0 , 0 ] } animationDuration = { 600 } barSize = { 32 } />
28182843 </ >
28192844 ) }
28202845 { /* 曲线在最上层 - 带描边突出显示 */ }
0 commit comments