💱 feat(model-pricing): add currency (USD/CNY) & token unit (M/K) toggles to Model Pricing table
* Introduced a currency switch to toggle prices between USD and CNY. * CNY prices are calculated by multiplying USD prices with the site-wide `price` rate from `/api/status`. * Added a second switch to display prices per 1 M tokens or per 1 K tokens. * When “K” is selected, prices are divided by 1 000 and labels are updated accordingly. * Extended component state with `currency` and `tokenUnit` variables. * Integrated `StatusContext` to retrieve and memoize the current exchange rate. * Updated price rendering logic and labels to reflect selected currency and token unit. * Minor UI tweaks: kept Switch components compact and aligned with the table header. No breaking changes.
This commit is contained in:
@@ -16,7 +16,8 @@ import {
|
|||||||
Card,
|
Card,
|
||||||
Tabs,
|
Tabs,
|
||||||
TabPane,
|
TabPane,
|
||||||
Empty
|
Empty,
|
||||||
|
Switch
|
||||||
} from '@douyinfe/semi-ui';
|
} from '@douyinfe/semi-ui';
|
||||||
import {
|
import {
|
||||||
IllustrationNoResult,
|
IllustrationNoResult,
|
||||||
@@ -32,6 +33,7 @@ import {
|
|||||||
} from '@douyinfe/semi-icons';
|
} from '@douyinfe/semi-icons';
|
||||||
import { UserContext } from '../../context/User/index.js';
|
import { UserContext } from '../../context/User/index.js';
|
||||||
import { AlertCircle } from 'lucide-react';
|
import { AlertCircle } from 'lucide-react';
|
||||||
|
import { StatusContext } from '../../context/Status/index.js';
|
||||||
|
|
||||||
const ModelPricing = () => {
|
const ModelPricing = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -44,6 +46,13 @@ const ModelPricing = () => {
|
|||||||
const [activeKey, setActiveKey] = useState('all');
|
const [activeKey, setActiveKey] = useState('all');
|
||||||
const [pageSize, setPageSize] = useState(10);
|
const [pageSize, setPageSize] = useState(10);
|
||||||
|
|
||||||
|
const [currency, setCurrency] = useState('USD');
|
||||||
|
const [tokenUnit, setTokenUnit] = useState('M');
|
||||||
|
const [statusState] = useContext(StatusContext);
|
||||||
|
const priceRate = useMemo(() => {
|
||||||
|
return statusState?.status?.price || 1;
|
||||||
|
}, [statusState]);
|
||||||
|
|
||||||
const rowSelection = useMemo(
|
const rowSelection = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
onChange: (selectedRowKeys, selectedRows) => {
|
onChange: (selectedRowKeys, selectedRows) => {
|
||||||
@@ -245,33 +254,61 @@ const ModelPricing = () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('模型价格'),
|
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'}
|
||||||
|
onChange={(checked) => setTokenUnit(checked ? 'K' : 'M')}
|
||||||
|
checkedText="K"
|
||||||
|
uncheckedText="M"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
dataIndex: 'model_price',
|
dataIndex: 'model_price',
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
let content = text;
|
let content = text;
|
||||||
if (record.quota_type === 0) {
|
if (record.quota_type === 0) {
|
||||||
let inputRatioPrice =
|
let inputRatioPrice = record.model_ratio * 2 * groupRatio[selectedGroup];
|
||||||
record.model_ratio * 2 * groupRatio[selectedGroup];
|
|
||||||
let completionRatioPrice =
|
let completionRatioPrice =
|
||||||
record.model_ratio *
|
record.model_ratio * record.completion_ratio * 2 * groupRatio[selectedGroup];
|
||||||
record.completion_ratio *
|
|
||||||
2 *
|
if (currency === 'RMB') {
|
||||||
groupRatio[selectedGroup];
|
inputRatioPrice = inputRatioPrice * priceRate;
|
||||||
|
completionRatioPrice = completionRatioPrice * priceRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unitDivisor = tokenUnit === 'K' ? 1000 : 1;
|
||||||
|
const unitLabel = tokenUnit === 'K' ? 'K' : 'M';
|
||||||
|
inputRatioPrice = inputRatioPrice / unitDivisor;
|
||||||
|
completionRatioPrice = completionRatioPrice / unitDivisor;
|
||||||
content = (
|
content = (
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="text-gray-700">
|
<div className="text-gray-700">
|
||||||
{t('提示')} ${inputRatioPrice.toFixed(3)} / 1M tokens
|
{t('提示')} {currency === 'USD' ? '$' : '¥'}{inputRatioPrice.toFixed(3)} / 1{unitLabel} tokens
|
||||||
</div>
|
</div>
|
||||||
<div className="text-gray-700">
|
<div className="text-gray-700">
|
||||||
{t('补全')} ${completionRatioPrice.toFixed(3)} / 1M tokens
|
{t('补全')} {currency === 'USD' ? '$' : '¥'}{completionRatioPrice.toFixed(3)} / 1{unitLabel} tokens
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let price = parseFloat(text) * groupRatio[selectedGroup];
|
let price = parseFloat(text) * groupRatio[selectedGroup];
|
||||||
|
|
||||||
|
if (currency === 'RMB') {
|
||||||
|
price = price * priceRate;
|
||||||
|
}
|
||||||
content = (
|
content = (
|
||||||
<div className="text-gray-700">
|
<div className="text-gray-700">
|
||||||
{t('模型价格')}:${price.toFixed(3)}
|
{t('模型价格')}:{currency === 'USD' ? '$' : '¥'}{price.toFixed(3)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user