From f1506ed5da433aa83ea9af7a5c749b6641fd2d23 Mon Sep 17 00:00:00 2001 From: t0ng7u Date: Thu, 17 Jul 2025 22:07:06 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=B8=20feat(model-pricing):=20add=20?= =?UTF-8?q?=E2=80=9Cshow=20with=20recharge=20price=E2=80=9D=20toggle=20and?= =?UTF-8?q?=20USD/CNY=20selector?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Introduced `showWithRecharge` switch in the actions bar to display model prices based on recharge cost. * Added a `Select` dropdown (USD / CNY) that appears only when the recharge-price mode is enabled. * Implemented `displayPrice()` helper to: * Convert USD prices to recharge prices using `status.price` and `status.usd_exchange_rate`. * Format output according to the selected currency. * Updated price rendering for both quota types to use the new helper and respect K/M unit conversion. * Removed the old currency switch from the header, retaining only the K/M unit toggle. * Extended `SearchAndActions` memo dependencies; imported `Select` from Semi UI. * Minor refactors and comment clean-up. No breaking changes. --- web/src/components/table/ModelPricing.js | 86 ++++++++++++++++-------- web/src/i18n/locales/en.json | 3 +- 2 files changed, 59 insertions(+), 30 deletions(-) diff --git a/web/src/components/table/ModelPricing.js b/web/src/components/table/ModelPricing.js index 6e7dab87..e3f68a76 100644 --- a/web/src/components/table/ModelPricing.js +++ b/web/src/components/table/ModelPricing.js @@ -17,7 +17,8 @@ import { Tabs, TabPane, Empty, - Switch + Switch, + Select } from '@douyinfe/semi-ui'; import { IllustrationNoResult, @@ -47,11 +48,12 @@ const ModelPricing = () => { const [pageSize, setPageSize] = useState(10); const [currency, setCurrency] = useState('USD'); + const [showWithRecharge, setShowWithRecharge] = useState(false); const [tokenUnit, setTokenUnit] = useState('M'); const [statusState] = useContext(StatusContext); - const priceRate = useMemo(() => { - return statusState?.status?.price || 1; - }, [statusState]); + // 充值汇率(price)与美元兑人民币汇率(usd_exchange_rate) + const priceRate = useMemo(() => statusState?.status?.price ?? 1, [statusState]); + const usdExchangeRate = useMemo(() => statusState?.status?.usd_exchange_rate ?? priceRate, [statusState, priceRate]); const rowSelection = useMemo( () => ({ @@ -134,6 +136,18 @@ const ModelPricing = () => { ); } + const displayPrice = (usdPrice) => { + let priceInUSD = usdPrice; + if (showWithRecharge) { + priceInUSD = usdPrice * priceRate / usdExchangeRate; + } + + if (currency === 'CNY') { + return `¥${(priceInUSD * usdExchangeRate).toFixed(3)}`; + } + return `$${priceInUSD.toFixed(3)}`; + }; + const columns = [ { title: t('可用性'), @@ -257,13 +271,6 @@ const ModelPricing = () => { title: (
{t('模型价格')} - {/* 货币切换 */} - setCurrency(checked ? 'RMB' : 'USD')} - checkedText="¥" - uncheckedText="$" - /> {/* 计费单位切换 */} { render: (text, record, index) => { let content = text; if (record.quota_type === 0) { - let inputRatioPrice = record.model_ratio * 2 * groupRatio[selectedGroup]; - let completionRatioPrice = + let inputRatioPriceUSD = record.model_ratio * 2 * groupRatio[selectedGroup]; + let completionRatioPriceUSD = record.model_ratio * record.completion_ratio * 2 * groupRatio[selectedGroup]; - if (currency === 'RMB') { - inputRatioPrice = inputRatioPrice * priceRate; - completionRatioPrice = completionRatioPrice * priceRate; - } - const unitDivisor = tokenUnit === 'K' ? 1000 : 1; const unitLabel = tokenUnit === 'K' ? 'K' : 'M'; - inputRatioPrice = inputRatioPrice / unitDivisor; - completionRatioPrice = completionRatioPrice / unitDivisor; + + let displayInput = displayPrice(inputRatioPriceUSD); + let displayCompletion = displayPrice(completionRatioPriceUSD); + + const divisor = unitDivisor; + const numInput = parseFloat(displayInput.replace(/[^0-9.]/g, '')) / divisor; + const numCompletion = parseFloat(displayCompletion.replace(/[^0-9.]/g, '')) / divisor; + + displayInput = `${currency === 'CNY' ? '¥' : '$'}${numInput.toFixed(3)}`; + displayCompletion = `${currency === 'CNY' ? '¥' : '$'}${numCompletion.toFixed(3)}`; content = (
- {t('提示')} {currency === 'USD' ? '$' : '¥'}{inputRatioPrice.toFixed(3)} / 1{unitLabel} tokens + {t('提示')} {displayInput} / 1{unitLabel} tokens
- {t('补全')} {currency === 'USD' ? '$' : '¥'}{completionRatioPrice.toFixed(3)} / 1{unitLabel} tokens + {t('补全')} {displayCompletion} / 1{unitLabel} tokens
); } else { - let price = parseFloat(text) * groupRatio[selectedGroup]; - - if (currency === 'RMB') { - price = price * priceRate; - } + let priceUSD = parseFloat(text) * groupRatio[selectedGroup]; + let displayVal = displayPrice(priceUSD); content = (
- {t('模型价格')}:{currency === 'USD' ? '$' : '¥'}{price.toFixed(3)} + {t('模型价格')}:{displayVal}
); } @@ -482,9 +489,30 @@ const ModelPricing = () => { > {t('复制选中模型')} + + {/* 充值价格显示开关 */} + + {t('以充值价格显示')} + + {showWithRecharge && ( + + )} +
- ), [selectedRowKeys, t]); + ), [selectedRowKeys, t, showWithRecharge, currency]); const ModelTable = useMemo(() => ( diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index 7babcad6..9e62d25e 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1778,5 +1778,6 @@ "切换为单密钥模式": "Switch to single key mode", "将仅保留第一个密钥文件,其余文件将被移除,是否继续?": "Only the first key file will be retained, and the remaining files will be removed. Continue?", "自定义模型名称": "Custom model name", - "启用全部密钥": "Enable all keys" + "启用全部密钥": "Enable all keys", + "以充值价格显示": "Show with recharge price" } \ No newline at end of file