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

♻️ refactor(model-pricing): improve table UI and optimize code structure (#1365)

- Replace model count with group ratio display (x2.2, x1) in group filter
- Remove redundant "Available Groups" column from pricing table
- Remove "Availability" column and related logic completely
- Move "Supported Endpoint Types" column to fixed right position
- Clean up unused parameters and variables in PricingTableColumns.js
- Optimize variable declarations (let → const) and simplify render logic
- Improve code readability and reduce memory allocations

This refactor enhances user experience by:
- Providing clearer group ratio information in filters
- Simplifying table layout while maintaining essential functionality
- Improving performance through better code organization

Breaking changes: None
t0ng7u 7 месяцев назад
Родитель
Сommit
a99dbc78c9

+ 1 - 1
web/src/components/table/model-pricing/PricingSidebar.jsx

@@ -84,7 +84,7 @@ const PricingSidebar = ({
 
 
       <PricingCategories {...categoryProps} setActiveKey={setActiveKey} loading={loading} t={t} />
       <PricingCategories {...categoryProps} setActiveKey={setActiveKey} loading={loading} t={t} />
 
 
-      <PricingGroups filterGroup={filterGroup} setFilterGroup={setFilterGroup} usableGroup={categoryProps.usableGroup} models={categoryProps.models} loading={loading} t={t} />
+      <PricingGroups filterGroup={filterGroup} setFilterGroup={setFilterGroup} usableGroup={categoryProps.usableGroup} groupRatio={categoryProps.groupRatio} models={categoryProps.models} loading={loading} t={t} />
 
 
       <PricingQuotaTypes filterQuotaType={filterQuotaType} setFilterQuotaType={setFilterQuotaType} models={categoryProps.models} loading={loading} t={t} />
       <PricingQuotaTypes filterQuotaType={filterQuotaType} setFilterQuotaType={setFilterQuotaType} models={categoryProps.models} loading={loading} t={t} />
     </div>
     </div>

+ 18 - 101
web/src/components/table/model-pricing/PricingTableColumns.js

@@ -19,8 +19,7 @@ For commercial licensing, please contact support@quantumnous.com
 
 
 import React from 'react';
 import React from 'react';
 import { Tag, Space, Tooltip, Switch } from '@douyinfe/semi-ui';
 import { Tag, Space, Tooltip, Switch } from '@douyinfe/semi-ui';
-import { IconHelpCircle, IconCheckCircleStroked, IconClose } from '@douyinfe/semi-icons';
-import { Popover } from '@douyinfe/semi-ui';
+import { IconHelpCircle } from '@douyinfe/semi-icons';
 import { renderModelTag, stringToColor } from '../../../helpers';
 import { renderModelTag, stringToColor } from '../../../helpers';
 
 
 function renderQuotaType(type, t) {
 function renderQuotaType(type, t) {
@@ -42,33 +41,6 @@ function renderQuotaType(type, t) {
   }
   }
 }
 }
 
 
-function renderAvailable(available, t) {
-  if (available) {
-    return (
-      <Popover
-        content={<div style={{ padding: 8 }}>{t('您的分组可以使用该模型')}</div>}
-        position='top'
-        key={String(available)}
-        className="bg-green-50"
-      >
-        <IconCheckCircleStroked style={{ color: 'rgb(22 163 74)' }} size='large' />
-      </Popover>
-    );
-  }
-
-  // 分组不可用时显示红色关闭图标
-  return (
-    <Popover
-      content={<div style={{ padding: 8 }}>{t('你的分组无权使用该模型')}</div>}
-      position='top'
-      key="not-available"
-      className="bg-red-50"
-    >
-      <IconClose style={{ color: 'rgb(239 68 68)' }} size='large' />
-    </Popover>
-  );
-}
-
 function renderSupportedEndpoints(endpoints) {
 function renderSupportedEndpoints(endpoints) {
   if (!endpoints || endpoints.length === 0) {
   if (!endpoints || endpoints.length === 0) {
     return null;
     return null;
@@ -91,22 +63,20 @@ function renderSupportedEndpoints(endpoints) {
 export const getPricingTableColumns = ({
 export const getPricingTableColumns = ({
   t,
   t,
   selectedGroup,
   selectedGroup,
-  usableGroup,
   groupRatio,
   groupRatio,
   copyText,
   copyText,
   setModalImageUrl,
   setModalImageUrl,
   setIsModalOpenurl,
   setIsModalOpenurl,
   currency,
   currency,
-  showWithRecharge,
   tokenUnit,
   tokenUnit,
   setTokenUnit,
   setTokenUnit,
   displayPrice,
   displayPrice,
-  handleGroupClick,
   showRatio,
   showRatio,
 }) => {
 }) => {
   const endpointColumn = {
   const endpointColumn = {
     title: t('可用端点类型'),
     title: t('可用端点类型'),
     dataIndex: 'supported_endpoint_types',
     dataIndex: 'supported_endpoint_types',
+    fixed: 'right',
     render: (text, record, index) => {
     render: (text, record, index) => {
       return renderSupportedEndpoints(text);
       return renderSupportedEndpoints(text);
     },
     },
@@ -135,56 +105,7 @@ export const getPricingTableColumns = ({
     sorter: (a, b) => a.quota_type - b.quota_type,
     sorter: (a, b) => a.quota_type - b.quota_type,
   };
   };
 
 
-  const enableGroupColumn = {
-    title: t('可用分组'),
-    dataIndex: 'enable_groups',
-    render: (text, record, index) => {
-      return (
-        <Space wrap>
-          {text.map((group) => {
-            if (usableGroup[group]) {
-              if (group === selectedGroup) {
-                return (
-                  <Tag key={group} color='blue' shape='circle' prefixIcon={<IconCheckCircleStroked />}>
-                    {group}
-                  </Tag>
-                );
-              } else {
-                return (
-                  <Tag
-                    key={group}
-                    color='blue'
-                    shape='circle'
-                    onClick={() => handleGroupClick(group)}
-                    className="cursor-pointer hover:opacity-80 transition-opacity"
-                  >
-                    {group}
-                  </Tag>
-                );
-              }
-            }
-          })}
-        </Space>
-      );
-    },
-  };
-
-  const baseColumns = [endpointColumn, modelNameColumn, quotaColumn, enableGroupColumn];
-
-  const availabilityColumn = {
-    title: t('可用性'),
-    dataIndex: 'available',
-    fixed: 'right',
-    render: (text, record, index) => {
-      return renderAvailable(record.enable_groups.includes(selectedGroup), t);
-    },
-    sorter: (a, b) => {
-      const aAvailable = a.enable_groups.includes(selectedGroup);
-      const bAvailable = b.enable_groups.includes(selectedGroup);
-      return Number(aAvailable) - Number(bAvailable);
-    },
-    defaultSortOrder: 'descend',
-  };
+  const baseColumns = [modelNameColumn, quotaColumn];
 
 
   const ratioColumn = {
   const ratioColumn = {
     title: () => (
     title: () => (
@@ -203,9 +124,8 @@ export const getPricingTableColumns = ({
     ),
     ),
     dataIndex: 'model_ratio',
     dataIndex: 'model_ratio',
     render: (text, record, index) => {
     render: (text, record, index) => {
-      let content = text;
-      let completionRatio = parseFloat(record.completion_ratio.toFixed(3));
-      content = (
+      const completionRatio = parseFloat(record.completion_ratio.toFixed(3));
+      const content = (
         <div className="space-y-1">
         <div className="space-y-1">
           <div className="text-gray-700">
           <div className="text-gray-700">
             {t('模型倍率')}:{record.quota_type === 0 ? text : t('无')}
             {t('模型倍率')}:{record.quota_type === 0 ? text : t('无')}
@@ -238,25 +158,23 @@ export const getPricingTableColumns = ({
     ),
     ),
     dataIndex: 'model_price',
     dataIndex: 'model_price',
     render: (text, record, index) => {
     render: (text, record, index) => {
-      let content = text;
       if (record.quota_type === 0) {
       if (record.quota_type === 0) {
-        let inputRatioPriceUSD = record.model_ratio * 2 * groupRatio[selectedGroup];
-        let completionRatioPriceUSD =
+        const inputRatioPriceUSD = record.model_ratio * 2 * groupRatio[selectedGroup];
+        const completionRatioPriceUSD =
           record.model_ratio * record.completion_ratio * 2 * groupRatio[selectedGroup];
           record.model_ratio * record.completion_ratio * 2 * groupRatio[selectedGroup];
 
 
         const unitDivisor = tokenUnit === 'K' ? 1000 : 1;
         const unitDivisor = tokenUnit === 'K' ? 1000 : 1;
         const unitLabel = tokenUnit === 'K' ? 'K' : 'M';
         const unitLabel = tokenUnit === 'K' ? 'K' : 'M';
 
 
-        let displayInput = displayPrice(inputRatioPriceUSD);
-        let displayCompletion = displayPrice(completionRatioPriceUSD);
+        const rawDisplayInput = displayPrice(inputRatioPriceUSD);
+        const rawDisplayCompletion = displayPrice(completionRatioPriceUSD);
 
 
-        const divisor = unitDivisor;
-        const numInput = parseFloat(displayInput.replace(/[^0-9.]/g, '')) / divisor;
-        const numCompletion = parseFloat(displayCompletion.replace(/[^0-9.]/g, '')) / divisor;
+        const numInput = parseFloat(rawDisplayInput.replace(/[^0-9.]/g, '')) / unitDivisor;
+        const numCompletion = parseFloat(rawDisplayCompletion.replace(/[^0-9.]/g, '')) / unitDivisor;
 
 
-        displayInput = `${currency === 'CNY' ? '¥' : '$'}${numInput.toFixed(3)}`;
-        displayCompletion = `${currency === 'CNY' ? '¥' : '$'}${numCompletion.toFixed(3)}`;
-        content = (
+        const displayInput = `${currency === 'CNY' ? '¥' : '$'}${numInput.toFixed(3)}`;
+        const displayCompletion = `${currency === 'CNY' ? '¥' : '$'}${numCompletion.toFixed(3)}`;
+        return (
           <div className="space-y-1">
           <div className="space-y-1">
             <div className="text-gray-700">
             <div className="text-gray-700">
               {t('提示')} {displayInput} / 1{unitLabel} tokens
               {t('提示')} {displayInput} / 1{unitLabel} tokens
@@ -267,15 +185,14 @@ export const getPricingTableColumns = ({
           </div>
           </div>
         );
         );
       } else {
       } else {
-        let priceUSD = parseFloat(text) * groupRatio[selectedGroup];
-        let displayVal = displayPrice(priceUSD);
-        content = (
+        const priceUSD = parseFloat(text) * groupRatio[selectedGroup];
+        const displayVal = displayPrice(priceUSD);
+        return (
           <div className="text-gray-700">
           <div className="text-gray-700">
             {t('模型价格')}:{displayVal}
             {t('模型价格')}:{displayVal}
           </div>
           </div>
         );
         );
       }
       }
-      return content;
     },
     },
   };
   };
 
 
@@ -284,6 +201,6 @@ export const getPricingTableColumns = ({
     columns.push(ratioColumn);
     columns.push(ratioColumn);
   }
   }
   columns.push(priceColumn);
   columns.push(priceColumn);
-  columns.push(availabilityColumn);
+  columns.push(endpointColumn);
   return columns;
   return columns;
 }; 
 }; 

+ 11 - 5
web/src/components/table/model-pricing/filter/PricingGroups.jsx

@@ -25,24 +25,30 @@ import SelectableButtonGroup from '../../../common/ui/SelectableButtonGroup';
  * @param {string} filterGroup 当前选中的分组,'all' 表示不过滤
  * @param {string} filterGroup 当前选中的分组,'all' 表示不过滤
  * @param {Function} setFilterGroup 设置选中分组
  * @param {Function} setFilterGroup 设置选中分组
  * @param {Record<string, any>} usableGroup 后端返回的可用分组对象
  * @param {Record<string, any>} usableGroup 后端返回的可用分组对象
+ * @param {Record<string, number>} groupRatio 分组倍率对象
  * @param {Array} models 模型列表
  * @param {Array} models 模型列表
  * @param {boolean} loading 是否加载中
  * @param {boolean} loading 是否加载中
  * @param {Function} t i18n
  * @param {Function} t i18n
  */
  */
-const PricingGroups = ({ filterGroup, setFilterGroup, usableGroup = {}, models = [], loading = false, t }) => {
+const PricingGroups = ({ filterGroup, setFilterGroup, usableGroup = {}, groupRatio = {}, models = [], loading = false, t }) => {
   const groups = ['all', ...Object.keys(usableGroup).filter(key => key !== '')];
   const groups = ['all', ...Object.keys(usableGroup).filter(key => key !== '')];
 
 
   const items = groups.map((g) => {
   const items = groups.map((g) => {
-    let count = 0;
+    let ratioDisplay = '';
     if (g === 'all') {
     if (g === 'all') {
-      count = models.length;
+      ratioDisplay = t('全部');
     } else {
     } else {
-      count = models.filter(m => m.enable_groups && m.enable_groups.includes(g)).length;
+      const ratio = groupRatio[g];
+      if (ratio !== undefined && ratio !== null) {
+        ratioDisplay = `x${ratio}`;
+      } else {
+        ratioDisplay = 'x1';
+      }
     }
     }
     return {
     return {
       value: g,
       value: g,
       label: g === 'all' ? t('全部分组') : g,
       label: g === 'all' ? t('全部分组') : g,
-      tagCount: count,
+      tagCount: ratioDisplay,
     };
     };
   });
   });
 
 

+ 1 - 0
web/src/components/table/model-pricing/modal/PricingFilterModal.jsx

@@ -116,6 +116,7 @@ const PricingFilterModal = ({
           filterGroup={filterGroup}
           filterGroup={filterGroup}
           setFilterGroup={setFilterGroup}
           setFilterGroup={setFilterGroup}
           usableGroup={categoryProps.usableGroup}
           usableGroup={categoryProps.usableGroup}
+          groupRatio={categoryProps.groupRatio}
           models={categoryProps.models}
           models={categoryProps.models}
           loading={loading}
           loading={loading}
           t={t}
           t={t}