♻️ 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
This commit is contained in:
t0ng7u
2025-07-23 11:20:55 +08:00
parent 8a54512037
commit a99dbc78c9
4 changed files with 31 additions and 107 deletions

View File

@@ -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>

View File

@@ -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 { IconHelpCircle } from '@douyinfe/semi-icons';
import { Popover } from '@douyinfe/semi-ui';
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 = { const baseColumns = [modelNameColumn, quotaColumn];
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 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; const completionRatio = parseFloat(record.completion_ratio.toFixed(3));
let completionRatio = parseFloat(record.completion_ratio.toFixed(3)); const content = (
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]; const inputRatioPriceUSD = record.model_ratio * 2 * groupRatio[selectedGroup];
let completionRatioPriceUSD = 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); const rawDisplayInput = displayPrice(inputRatioPriceUSD);
let displayCompletion = displayPrice(completionRatioPriceUSD); const rawDisplayCompletion = displayPrice(completionRatioPriceUSD);
const divisor = unitDivisor; const numInput = parseFloat(rawDisplayInput.replace(/[^0-9.]/g, '')) / unitDivisor;
const numInput = parseFloat(displayInput.replace(/[^0-9.]/g, '')) / divisor; const numCompletion = parseFloat(rawDisplayCompletion.replace(/[^0-9.]/g, '')) / unitDivisor;
const numCompletion = parseFloat(displayCompletion.replace(/[^0-9.]/g, '')) / divisor;
displayInput = `${currency === 'CNY' ? '¥' : '$'}${numInput.toFixed(3)}`; const displayInput = `${currency === 'CNY' ? '¥' : '$'}${numInput.toFixed(3)}`;
displayCompletion = `${currency === 'CNY' ? '¥' : '$'}${numCompletion.toFixed(3)}`; const displayCompletion = `${currency === 'CNY' ? '¥' : '$'}${numCompletion.toFixed(3)}`;
content = ( 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]; const priceUSD = parseFloat(text) * groupRatio[selectedGroup];
let displayVal = displayPrice(priceUSD); const displayVal = displayPrice(priceUSD);
content = ( 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;
}; };

View File

@@ -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,
}; };
}); });

View File

@@ -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}