feat(ui): enhance pricing table & filters with responsive button-group, fixed column, scroll tweaks (#1365)

• SelectableButtonGroup
  • Added optional collapsible support with gradient mask & toggle
  • Dynamic tagCount badge support for groups / quota types
  • Switched to responsive Row/Col (`xs 24`, `sm 24`, `lg 12`, `xl 8`) for fluid layout
  • Shows expand button only when item count exceeds visible rows

• Sidebar filters
  • PricingGroups & PricingQuotaTypes now pass tag counts to button-group
  • Counts derived from current models & quota_type

• PricingTableColumns
  • Moved “Availability” column to far right; fixed via `fixed: 'right'`
  • Re-ordered columns and preserved ratio / price logic

• PricingTable
  • Added `compactMode` prop; strips fixed columns and sets `scroll={compactMode ? undefined : { x: 'max-content' }}`
  • Processes columns to remove `fixed` in compact mode

• PricingPage & index.css
  • Added `.pricing-scroll-hide` utility to hide Y-axis scrollbar for `Sider` & `Content`

• Responsive / style refinements
  • Sidebar width adjusted to 460px
  • Scrollbars hidden uniformly across pricing modules

These changes complete the model-pricing UI refactor, ensuring clean scrolling, responsive filters, and fixed availability column for better usability.
This commit is contained in:
t0ng7u
2025-07-23 02:23:25 +08:00
parent a044070e1d
commit b964f755ec
6 changed files with 102 additions and 88 deletions

View File

@@ -92,84 +92,88 @@ export const getPricingTableColumns = ({
handleGroupClick,
showRatio,
}) => {
const baseColumns = [
{
title: t('可用性'),
dataIndex: 'available',
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 endpointColumn = {
title: t('可用端点类型'),
dataIndex: 'supported_endpoint_types',
render: (text, record, index) => {
return renderSupportedEndpoints(text);
},
{
title: t('可用端点类型'),
dataIndex: 'supported_endpoint_types',
render: (text, record, index) => {
return renderSupportedEndpoints(text);
},
},
{
title: t('模型名称'),
dataIndex: 'model_name',
render: (text, record, index) => {
return renderModelTag(text, {
onClick: () => {
copyText(text);
}
});
},
onFilter: (value, record) =>
record.model_name.toLowerCase().includes(value.toLowerCase()),
},
{
title: t('计费类型'),
dataIndex: 'quota_type',
render: (text, record, index) => {
return renderQuotaType(parseInt(text), t);
},
sorter: (a, b) => a.quota_type - b.quota_type,
},
{
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={<IconVerify />}>
{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 modelNameColumn = {
title: t('模型名称'),
dataIndex: 'model_name',
render: (text, record, index) => {
return renderModelTag(text, {
onClick: () => {
copyText(text);
}
});
},
onFilter: (value, record) =>
record.model_name.toLowerCase().includes(value.toLowerCase()),
};
const quotaColumn = {
title: t('计费类型'),
dataIndex: 'quota_type',
render: (text, record, index) => {
return renderQuotaType(parseInt(text), t);
},
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={<IconVerify />}>
{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',
};
// 倍率列 - 只有在showRatio为true时才包含
const ratioColumn = {
title: () => (
<div className="flex items-center space-x-1">
@@ -207,7 +211,6 @@ export const getPricingTableColumns = ({
},
};
// 价格列
const priceColumn = {
title: (
<div className="flex items-center space-x-2">
@@ -264,12 +267,11 @@ export const getPricingTableColumns = ({
},
};
// 根据showRatio决定是否包含倍率列
const columns = [...baseColumns];
if (showRatio) {
columns.push(ratioColumn);
}
columns.push(priceColumn);
columns.push(availabilityColumn);
return columns;
};