Просмотр исходного кода

💱 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.
t0ng7u 7 месяцев назад
Родитель
Сommit
afa9c650fe
1 измененных файлов с 48 добавлено и 11 удалено
  1. 48 11
      web/src/components/table/ModelPricing.js

+ 48 - 11
web/src/components/table/ModelPricing.js

@@ -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 =
-            record.model_ratio * 2 * groupRatio[selectedGroup];
+          let inputRatioPrice = record.model_ratio * 2 * groupRatio[selectedGroup];
           let completionRatioPrice =
           let completionRatioPrice =
-            record.model_ratio *
-            record.completion_ratio *
-            2 *
-            groupRatio[selectedGroup];
+            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;
           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>
           );
           );
         }
         }