💸 feat(model-pricing): add “show with recharge price” toggle and USD/CNY selector
* 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.
This commit is contained in:
@@ -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: (
|
||||
<div className="flex items-center space-x-2">
|
||||
<span>{t('模型价格')}</span>
|
||||
{/* 货币切换 */}
|
||||
<Switch
|
||||
checked={currency === 'RMB'}
|
||||
onChange={(checked) => setCurrency(checked ? 'RMB' : 'USD')}
|
||||
checkedText="¥"
|
||||
uncheckedText="$"
|
||||
/>
|
||||
{/* 计费单位切换 */}
|
||||
<Switch
|
||||
checked={tokenUnit === 'K'}
|
||||
@@ -277,38 +284,38 @@ const ModelPricing = () => {
|
||||
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 = (
|
||||
<div className="space-y-1">
|
||||
<div className="text-gray-700">
|
||||
{t('提示')} {currency === 'USD' ? '$' : '¥'}{inputRatioPrice.toFixed(3)} / 1{unitLabel} tokens
|
||||
{t('提示')} {displayInput} / 1{unitLabel} tokens
|
||||
</div>
|
||||
<div className="text-gray-700">
|
||||
{t('补全')} {currency === 'USD' ? '$' : '¥'}{completionRatioPrice.toFixed(3)} / 1{unitLabel} tokens
|
||||
{t('补全')} {displayCompletion} / 1{unitLabel} tokens
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
let price = parseFloat(text) * groupRatio[selectedGroup];
|
||||
|
||||
if (currency === 'RMB') {
|
||||
price = price * priceRate;
|
||||
}
|
||||
let priceUSD = parseFloat(text) * groupRatio[selectedGroup];
|
||||
let displayVal = displayPrice(priceUSD);
|
||||
content = (
|
||||
<div className="text-gray-700">
|
||||
{t('模型价格')}:{currency === 'USD' ? '$' : '¥'}{price.toFixed(3)}
|
||||
{t('模型价格')}:{displayVal}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -482,9 +489,30 @@ const ModelPricing = () => {
|
||||
>
|
||||
{t('复制选中模型')}
|
||||
</Button>
|
||||
|
||||
{/* 充值价格显示开关 */}
|
||||
<Space align="center">
|
||||
<span>{t('以充值价格显示')}</span>
|
||||
<Switch
|
||||
checked={showWithRecharge}
|
||||
onChange={setShowWithRecharge}
|
||||
size="small"
|
||||
/>
|
||||
{showWithRecharge && (
|
||||
<Select
|
||||
value={currency}
|
||||
onChange={setCurrency}
|
||||
size="small"
|
||||
style={{ width: 100 }}
|
||||
>
|
||||
<Select.Option value="USD">USD ($)</Select.Option>
|
||||
<Select.Option value="CNY">CNY (¥)</Select.Option>
|
||||
</Select>
|
||||
)}
|
||||
</Space>
|
||||
</div>
|
||||
</Card>
|
||||
), [selectedRowKeys, t]);
|
||||
), [selectedRowKeys, t, showWithRecharge, currency]);
|
||||
|
||||
const ModelTable = useMemo(() => (
|
||||
<Card className="!rounded-xl overflow-hidden" bordered={false}>
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
Reference in New Issue
Block a user