💄 style: Use segmented renderer for billing types in Models table; keep Pricing view unchanged

Frontend
- Models table (model management):
  - Render billing types with the same segmented list component (renderLimitedItems) used by tags and endpoints
  - Display quota_types as an array with capped items (maxDisplay: 3) and graceful fallback for unknown types
- Pricing view (unchanged by request):
  - Revert to single-value quota_type rendering and sorter
  - Keep ratio display logic based on quota_type only

Files
- web/src/components/table/models/ModelsColumnDefs.js
- web/src/components/table/model-pricing/view/table/PricingTableColumns.js

Notes
- This commit only adjusts the model management UI rendering; pricing views remain as-is
This commit is contained in:
t0ng7u
2025-08-11 15:53:55 +08:00
parent 4ad8eefaec
commit da17bdb688
3 changed files with 48 additions and 55 deletions

View File

@@ -41,7 +41,7 @@ const CardTable = ({
}) => { }) => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const { t } = useTranslation(); const { t } = useTranslation();
const showSkeleton = useMinimumLoadingTime(loading); const showSkeleton = useMinimumLoadingTime(loading);
const getRowKey = (record, index) => { const getRowKey = (record, index) => {

View File

@@ -23,31 +23,23 @@ import { IconHelpCircle } from '@douyinfe/semi-icons';
import { renderModelTag, stringToColor, calculateModelPrice, getLobeHubIcon } from '../../../../../helpers'; import { renderModelTag, stringToColor, calculateModelPrice, getLobeHubIcon } from '../../../../../helpers';
import { renderLimitedItems, renderDescription } from '../../../../common/ui/RenderUtils'; import { renderLimitedItems, renderDescription } from '../../../../common/ui/RenderUtils';
function renderQuotaTypes(types, t) { function renderQuotaType(type, t) {
if (!Array.isArray(types) || types.length === 0) return '-'; switch (type) {
const renderOne = (type, idx) => { case 1:
switch (type) { return (
case 1: <Tag color='teal' shape='circle'>
return ( {t('按次计费')}
<Tag key={`qt-${type}-${idx}`} color='teal' shape='circle'> </Tag>
{t('按次计费')} );
</Tag> case 0:
); return (
case 0: <Tag color='violet' shape='circle'>
return ( {t('按量计费')}
<Tag key={`qt-${type}-${idx}`} color='violet' shape='circle'> </Tag>
{t('按量计费')} );
</Tag> default:
); return t('未知');
default: }
return (
<Tag key={`qt-${type}-${idx}`} color='white' shape='circle'>
{type}
</Tag>
);
}
};
return <Space wrap>{types.map((t0, idx) => renderOne(t0, idx))}</Space>;
} }
// Render vendor name // Render vendor name
@@ -130,8 +122,11 @@ export const getPricingTableColumns = ({
const quotaColumn = { const quotaColumn = {
title: t('计费类型'), title: t('计费类型'),
dataIndex: 'quota_types', dataIndex: 'quota_type',
render: (text, record, index) => renderQuotaTypes(text, t), render: (text, record, index) => {
return renderQuotaType(parseInt(text), t);
},
sorter: (a, b) => a.quota_type - b.quota_type,
}; };
const descriptionColumn = { const descriptionColumn = {
@@ -175,11 +170,11 @@ export const getPricingTableColumns = ({
const content = ( const content = (
<div className="space-y-1"> <div className="space-y-1">
<div className="text-gray-700"> <div className="text-gray-700">
{t('模型倍率')}{Array.isArray(record.quota_types) && record.quota_types.includes(0) ? text : t('无')} {t('模型倍率')}{record.quota_type === 0 ? text : t('无')}
</div> </div>
<div className="text-gray-700"> <div className="text-gray-700">
{t('补全倍率')} {t('补全倍率')}
{Array.isArray(record.quota_types) && record.quota_types.includes(0) ? completionRatio : t('无')} {record.quota_type === 0 ? completionRatio : t('无')}
</div> </div>
<div className="text-gray-700"> <div className="text-gray-700">
{t('分组倍率')}{groupRatio[selectedGroup]} {t('分组倍率')}{groupRatio[selectedGroup]}

View File

@@ -121,36 +121,34 @@ const renderEndpoints = (value) => {
} }
}; };
// Render quota types (array) // Render quota types (array) using common limited items renderer
const renderQuotaTypes = (arr, t) => { const renderQuotaTypes = (arr, t) => {
if (!Array.isArray(arr) || arr.length === 0) return '-'; if (!Array.isArray(arr) || arr.length === 0) return '-';
const renderOne = (qt, idx) => { return renderLimitedItems({
if (qt === 1) { items: arr,
renderItem: (qt, idx) => {
if (qt === 1) {
return (
<Tag key={`${qt}-${idx}`} color='teal' size='small' shape='circle'>
{t('按次计费')}
</Tag>
);
}
if (qt === 0) {
return (
<Tag key={`${qt}-${idx}`} color='violet' size='small' shape='circle'>
{t('按量计费')}
</Tag>
);
}
return ( return (
<Tag key={`${qt}-${idx}`} color='teal' size='small' shape='circle'> <Tag key={`${qt}-${idx}`} color='white' size='small' shape='circle'>
{t('按次计费')} {qt}
</Tag> </Tag>
); );
} },
if (qt === 0) { maxDisplay: 3,
return ( });
<Tag key={`${qt}-${idx}`} color='violet' size='small' shape='circle'>
{t('按量计费')}
</Tag>
);
}
// 未来新增模式的兜底展示
return (
<Tag key={`${qt}-${idx}`} color='white' size='small' shape='circle'>
{qt}
</Tag>
);
};
return (
<Space wrap>
{arr.map((qt, idx) => renderOne(qt, idx))}
</Space>
);
}; };
// Render bound channels // Render bound channels