♻️ 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:
@@ -84,7 +84,7 @@ const PricingSidebar = ({
|
||||
|
||||
<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} />
|
||||
</div>
|
||||
|
||||
@@ -19,8 +19,7 @@ For commercial licensing, please contact support@quantumnous.com
|
||||
|
||||
import React from 'react';
|
||||
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';
|
||||
|
||||
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) {
|
||||
if (!endpoints || endpoints.length === 0) {
|
||||
return null;
|
||||
@@ -91,22 +63,20 @@ function renderSupportedEndpoints(endpoints) {
|
||||
export const getPricingTableColumns = ({
|
||||
t,
|
||||
selectedGroup,
|
||||
usableGroup,
|
||||
groupRatio,
|
||||
copyText,
|
||||
setModalImageUrl,
|
||||
setIsModalOpenurl,
|
||||
currency,
|
||||
showWithRecharge,
|
||||
tokenUnit,
|
||||
setTokenUnit,
|
||||
displayPrice,
|
||||
handleGroupClick,
|
||||
showRatio,
|
||||
}) => {
|
||||
const endpointColumn = {
|
||||
title: t('可用端点类型'),
|
||||
dataIndex: 'supported_endpoint_types',
|
||||
fixed: 'right',
|
||||
render: (text, record, index) => {
|
||||
return renderSupportedEndpoints(text);
|
||||
},
|
||||
@@ -135,56 +105,7 @@ export const getPricingTableColumns = ({
|
||||
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 = {
|
||||
title: () => (
|
||||
@@ -203,9 +124,8 @@ export const getPricingTableColumns = ({
|
||||
),
|
||||
dataIndex: 'model_ratio',
|
||||
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="text-gray-700">
|
||||
{t('模型倍率')}:{record.quota_type === 0 ? text : t('无')}
|
||||
@@ -238,25 +158,23 @@ export const getPricingTableColumns = ({
|
||||
),
|
||||
dataIndex: 'model_price',
|
||||
render: (text, record, index) => {
|
||||
let content = text;
|
||||
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];
|
||||
|
||||
const unitDivisor = tokenUnit === 'K' ? 1000 : 1;
|
||||
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="text-gray-700">
|
||||
{t('提示')} {displayInput} / 1{unitLabel} tokens
|
||||
@@ -267,15 +185,14 @@ export const getPricingTableColumns = ({
|
||||
</div>
|
||||
);
|
||||
} 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">
|
||||
{t('模型价格')}:{displayVal}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return content;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -284,6 +201,6 @@ export const getPricingTableColumns = ({
|
||||
columns.push(ratioColumn);
|
||||
}
|
||||
columns.push(priceColumn);
|
||||
columns.push(availabilityColumn);
|
||||
columns.push(endpointColumn);
|
||||
return columns;
|
||||
};
|
||||
@@ -25,24 +25,30 @@ import SelectableButtonGroup from '../../../common/ui/SelectableButtonGroup';
|
||||
* @param {string} filterGroup 当前选中的分组,'all' 表示不过滤
|
||||
* @param {Function} setFilterGroup 设置选中分组
|
||||
* @param {Record<string, any>} usableGroup 后端返回的可用分组对象
|
||||
* @param {Record<string, number>} groupRatio 分组倍率对象
|
||||
* @param {Array} models 模型列表
|
||||
* @param {boolean} loading 是否加载中
|
||||
* @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 items = groups.map((g) => {
|
||||
let count = 0;
|
||||
let ratioDisplay = '';
|
||||
if (g === 'all') {
|
||||
count = models.length;
|
||||
ratioDisplay = t('全部');
|
||||
} 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 {
|
||||
value: g,
|
||||
label: g === 'all' ? t('全部分组') : g,
|
||||
tagCount: count,
|
||||
tagCount: ratioDisplay,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -116,6 +116,7 @@ const PricingFilterModal = ({
|
||||
filterGroup={filterGroup}
|
||||
setFilterGroup={setFilterGroup}
|
||||
usableGroup={categoryProps.usableGroup}
|
||||
groupRatio={categoryProps.groupRatio}
|
||||
models={categoryProps.models}
|
||||
loading={loading}
|
||||
t={t}
|
||||
|
||||
Reference in New Issue
Block a user