feat(ui): redesign model square pricing page

This commit is contained in:
CaIon
2026-04-30 16:29:32 +08:00
parent 5f86839c7e
commit d2b30dfc95
34 changed files with 2468 additions and 767 deletions
@@ -12,6 +12,10 @@ import { GroupBadge } from '@/components/group-badge'
import { DEFAULT_TOKEN_UNIT, QUOTA_TYPE_VALUES } from '../constants'
import { parseTags } from '../lib/filters'
import { isTokenBasedModel } from '../lib/model-helpers'
import {
getDynamicDisplayGroupRatio,
getDynamicPricingSummary,
} from '../lib/dynamic-price'
import {
formatPrice,
formatRequestPrice,
@@ -137,6 +141,63 @@ export function usePricingColumns(
),
cell: ({ row }) => {
const model = row.original
const dynamicSummary = getDynamicPricingSummary(model, {
tokenUnit,
showRechargePrice,
priceRate,
usdExchangeRate,
groupRatioMultiplier: getDynamicDisplayGroupRatio(model),
})
if (dynamicSummary) {
if (dynamicSummary.isSpecialExpression) {
return (
<div className='min-w-[200px] max-w-[320px]'>
<div className='text-amber-700 text-xs font-medium dark:text-amber-300'>
{t('Special billing expression')}
</div>
<div className='text-muted-foreground text-[11px]'>
{t('Unable to parse structured pricing')}
</div>
<code className='text-muted-foreground/70 mt-1 line-clamp-2 block break-all font-mono text-[10px] leading-relaxed'>
{dynamicSummary.rawExpression}
</code>
</div>
)
}
const primaryEntries = dynamicSummary.primaryEntries.slice(0, 2)
if (primaryEntries.length === 0) {
return (
<span className='text-muted-foreground text-xs'>
{t('Dynamic Pricing')}
</span>
)
}
return (
<div className='min-w-[180px]'>
<span className='font-mono text-sm tabular-nums'>
{primaryEntries.map((entry, index) => (
<span key={entry.key}>
{index > 0 && (
<span className='text-muted-foreground/40 mx-1'>/</span>
)}
{stripTrailingZeros(entry.formatted)}
</span>
))}
</span>
<div className='text-muted-foreground/50 text-[10px]'>
/ {tokenUnitLabel} tokens
{dynamicSummary.tierCount > 1 &&
` · ${t('{{count}} tiers', {
count: dynamicSummary.tierCount,
})}`}
</div>
</div>
)
}
const isTokenBased = isTokenBasedModel(model)
if (isTokenBased) {
@@ -204,6 +265,42 @@ export function usePricingColumns(
header: t('Cached'),
cell: ({ row }) => {
const model = row.original
const dynamicSummary = getDynamicPricingSummary(model, {
tokenUnit,
showRechargePrice,
priceRate,
usdExchangeRate,
groupRatioMultiplier: getDynamicDisplayGroupRatio(model),
})
if (dynamicSummary) {
if (dynamicSummary.isSpecialExpression) {
return (
<span className='text-muted-foreground/50 text-xs'>
{t('Special billing expression')}
</span>
)
}
const cacheEntry = dynamicSummary.entries.find(
(entry) => entry.field === 'cacheReadPrice'
)
if (!cacheEntry) {
return <span className='text-muted-foreground/30 text-xs'></span>
}
return (
<div className='min-w-[80px]'>
<span className='font-mono text-sm tabular-nums'>
{stripTrailingZeros(cacheEntry.formatted)}
</span>
<div className='text-muted-foreground/50 text-[10px]'>
/ {tokenUnitLabel}
</div>
</div>
)
}
const isTokenBased = isTokenBasedModel(model)
if (!isTokenBased || model.cache_ratio == null) {