🎨 refactor(TokensTable): refactor TokensTable UI & UX for clearer data and inline actions
This commit overhauls the `TokensTable` component to deliver a cleaner, more intuitive experience.
Key changes
1. Quota
• Merged “Used” & “Remaining” into a single “Quota” column.
• Uses a circular `Progress` with %-label; full details shown on tooltip.
2. Status
• Tag now embeds a small `Switch` (prefixIcon) to enable/disable a token in-place.
• Removed enable/disable actions from the old dropdown.
3. Columns & layout
• Added dedicated “Group” column (moved from Status).
• Added “Key” column:
– Read-only `Input` styled like Home page base-URL field.
– Masked value (`sk-abc********xyz`) by default.
– Eye button toggles reveal/hide; Copy button copies full key (without modal).
• Dropped “More” menu; Delete is now a direct button in the action area.
4. Model limits
• Shows vendor icons inside an `AvatarGroup`; tooltip lists the exact models.
5. IP restriction
• Displays first IP, extra count as “+N” Tag with tooltip.
• Unlimited shows white Tag.
6. Cleanup / misc.
• Removed unused helpers (`getQuotaPerUnit`), icons (`IconMore`, eye/copy duplicates, etc.).
• Replaced legacy modal view of key with inline input behaviour.
• Tweaked paddings, themes, sizes to align with design system.
BREAKING CHANGE: Table column order & names have changed; update any tests or docs referencing the old structure.
This commit is contained in:
@@ -34,7 +34,7 @@ const ParameterControl = ({
|
|||||||
<Typography.Text strong className="text-sm">
|
<Typography.Text strong className="text-sm">
|
||||||
Temperature
|
Temperature
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
<Tag size="small" className="!rounded-full">
|
<Tag size="small" shape='circle'>
|
||||||
{inputs.temperature}
|
{inputs.temperature}
|
||||||
</Tag>
|
</Tag>
|
||||||
</div>
|
</div>
|
||||||
@@ -70,7 +70,7 @@ const ParameterControl = ({
|
|||||||
<Typography.Text strong className="text-sm">
|
<Typography.Text strong className="text-sm">
|
||||||
Top P
|
Top P
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
<Tag size="small" className="!rounded-full">
|
<Tag size="small" shape='circle'>
|
||||||
{inputs.top_p}
|
{inputs.top_p}
|
||||||
</Tag>
|
</Tag>
|
||||||
</div>
|
</div>
|
||||||
@@ -106,7 +106,7 @@ const ParameterControl = ({
|
|||||||
<Typography.Text strong className="text-sm">
|
<Typography.Text strong className="text-sm">
|
||||||
Frequency Penalty
|
Frequency Penalty
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
<Tag size="small" className="!rounded-full">
|
<Tag size="small" shape='circle'>
|
||||||
{inputs.frequency_penalty}
|
{inputs.frequency_penalty}
|
||||||
</Tag>
|
</Tag>
|
||||||
</div>
|
</div>
|
||||||
@@ -142,7 +142,7 @@ const ParameterControl = ({
|
|||||||
<Typography.Text strong className="text-sm">
|
<Typography.Text strong className="text-sm">
|
||||||
Presence Penalty
|
Presence Penalty
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
<Tag size="small" className="!rounded-full">
|
<Tag size="small" shape='circle'>
|
||||||
{inputs.presence_penalty}
|
{inputs.presence_penalty}
|
||||||
</Tag>
|
</Tag>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -118,25 +118,25 @@ const ChannelSelectorModal = forwardRef(({
|
|||||||
switch (status) {
|
switch (status) {
|
||||||
case 1:
|
case 1:
|
||||||
return (
|
return (
|
||||||
<Tag size='large' color='green' shape='circle' prefixIcon={<CheckCircle size={14} />}>
|
<Tag color='green' shape='circle' prefixIcon={<CheckCircle size={14} />}>
|
||||||
{t('已启用')}
|
{t('已启用')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 2:
|
case 2:
|
||||||
return (
|
return (
|
||||||
<Tag size='large' color='red' shape='circle' prefixIcon={<XCircle size={14} />}>
|
<Tag color='red' shape='circle' prefixIcon={<XCircle size={14} />}>
|
||||||
{t('已禁用')}
|
{t('已禁用')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 3:
|
case 3:
|
||||||
return (
|
return (
|
||||||
<Tag size='large' color='yellow' shape='circle' prefixIcon={<AlertCircle size={14} />}>
|
<Tag color='yellow' shape='circle' prefixIcon={<AlertCircle size={14} />}>
|
||||||
{t('自动禁用')}
|
{t('自动禁用')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<Tag size='large' color='grey' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
<Tag color='grey' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
||||||
{t('未知状态')}
|
{t('未知状态')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ const ChannelsTable = () => {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Tag
|
<Tag
|
||||||
size='large'
|
|
||||||
color={type2label[type]?.color}
|
color={type2label[type]?.color}
|
||||||
shape='circle'
|
shape='circle'
|
||||||
prefixIcon={getChannelIcon(type)}
|
prefixIcon={getChannelIcon(type)}
|
||||||
@@ -77,7 +76,6 @@ const ChannelsTable = () => {
|
|||||||
return (
|
return (
|
||||||
<Tag
|
<Tag
|
||||||
color='light-blue'
|
color='light-blue'
|
||||||
size='large'
|
|
||||||
shape='circle'
|
shape='circle'
|
||||||
type='light'
|
type='light'
|
||||||
>
|
>
|
||||||
@@ -90,25 +88,25 @@ const ChannelsTable = () => {
|
|||||||
switch (status) {
|
switch (status) {
|
||||||
case 1:
|
case 1:
|
||||||
return (
|
return (
|
||||||
<Tag size='large' color='green' shape='circle'>
|
<Tag color='green' shape='circle'>
|
||||||
{t('已启用')}
|
{t('已启用')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 2:
|
case 2:
|
||||||
return (
|
return (
|
||||||
<Tag size='large' color='red' shape='circle'>
|
<Tag color='red' shape='circle'>
|
||||||
{t('已禁用')}
|
{t('已禁用')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 3:
|
case 3:
|
||||||
return (
|
return (
|
||||||
<Tag size='large' color='yellow' shape='circle'>
|
<Tag color='yellow' shape='circle'>
|
||||||
{t('自动禁用')}
|
{t('自动禁用')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<Tag size='large' color='grey' shape='circle'>
|
<Tag color='grey' shape='circle'>
|
||||||
{t('未知状态')}
|
{t('未知状态')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
@@ -120,31 +118,31 @@ const ChannelsTable = () => {
|
|||||||
time = time.toFixed(2) + t(' 秒');
|
time = time.toFixed(2) + t(' 秒');
|
||||||
if (responseTime === 0) {
|
if (responseTime === 0) {
|
||||||
return (
|
return (
|
||||||
<Tag size='large' color='grey' shape='circle'>
|
<Tag color='grey' shape='circle'>
|
||||||
{t('未测试')}
|
{t('未测试')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
} else if (responseTime <= 1000) {
|
} else if (responseTime <= 1000) {
|
||||||
return (
|
return (
|
||||||
<Tag size='large' color='green' shape='circle'>
|
<Tag color='green' shape='circle'>
|
||||||
{time}
|
{time}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
} else if (responseTime <= 3000) {
|
} else if (responseTime <= 3000) {
|
||||||
return (
|
return (
|
||||||
<Tag size='large' color='lime' shape='circle'>
|
<Tag color='lime' shape='circle'>
|
||||||
{time}
|
{time}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
} else if (responseTime <= 5000) {
|
} else if (responseTime <= 5000) {
|
||||||
return (
|
return (
|
||||||
<Tag size='large' color='yellow' shape='circle'>
|
<Tag color='yellow' shape='circle'>
|
||||||
{time}
|
{time}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Tag size='large' color='red' shape='circle'>
|
<Tag color='red' shape='circle'>
|
||||||
{time}
|
{time}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
@@ -331,7 +329,7 @@ const ChannelsTable = () => {
|
|||||||
<div>
|
<div>
|
||||||
<Space spacing={1}>
|
<Space spacing={1}>
|
||||||
<Tooltip content={t('已用额度')}>
|
<Tooltip content={t('已用额度')}>
|
||||||
<Tag color='white' type='ghost' size='large' shape='circle'>
|
<Tag color='white' type='ghost' shape='circle'>
|
||||||
{renderQuota(record.used_quota)}
|
{renderQuota(record.used_quota)}
|
||||||
</Tag>
|
</Tag>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -339,7 +337,6 @@ const ChannelsTable = () => {
|
|||||||
<Tag
|
<Tag
|
||||||
color='white'
|
color='white'
|
||||||
type='ghost'
|
type='ghost'
|
||||||
size='large'
|
|
||||||
shape='circle'
|
shape='circle'
|
||||||
onClick={() => updateChannelBalance(record)}
|
onClick={() => updateChannelBalance(record)}
|
||||||
>
|
>
|
||||||
@@ -352,7 +349,7 @@ const ChannelsTable = () => {
|
|||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Tooltip content={t('已用额度')}>
|
<Tooltip content={t('已用额度')}>
|
||||||
<Tag color='white' type='ghost' size='large' shape='circle'>
|
<Tag color='white' type='ghost' shape='circle'>
|
||||||
{renderQuota(record.used_quota)}
|
{renderQuota(record.used_quota)}
|
||||||
</Tag>
|
</Tag>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -1240,7 +1237,7 @@ const ChannelsTable = () => {
|
|||||||
tab={
|
tab={
|
||||||
<span className="flex items-center gap-2">
|
<span className="flex items-center gap-2">
|
||||||
{t('全部')}
|
{t('全部')}
|
||||||
<Tag color={activeTypeKey === 'all' ? 'red' : 'grey'} size='small' shape='circle'>
|
<Tag color={activeTypeKey === 'all' ? 'red' : 'grey'} shape='circle'>
|
||||||
{channelTypeCounts['all'] || 0}
|
{channelTypeCounts['all'] || 0}
|
||||||
</Tag>
|
</Tag>
|
||||||
</span>
|
</span>
|
||||||
@@ -1258,7 +1255,7 @@ const ChannelsTable = () => {
|
|||||||
<span className="flex items-center gap-2">
|
<span className="flex items-center gap-2">
|
||||||
{getChannelIcon(option.value)}
|
{getChannelIcon(option.value)}
|
||||||
{option.label}
|
{option.label}
|
||||||
<Tag color={activeTypeKey === key ? 'red' : 'grey'} size='small' shape='circle'>
|
<Tag color={activeTypeKey === key ? 'red' : 'grey'} shape='circle'>
|
||||||
{count}
|
{count}
|
||||||
</Tag>
|
</Tag>
|
||||||
</span>
|
</span>
|
||||||
@@ -1461,7 +1458,7 @@ const ChannelsTable = () => {
|
|||||||
|
|
||||||
const fixChannelsAbilities = async () => {
|
const fixChannelsAbilities = async () => {
|
||||||
const res = await API.post(`/api/channel/fix`);
|
const res = await API.post(`/api/channel/fix`);
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
showSuccess(t('已修复 ${success} 个通道,失败 ${fails} 个通道。').replace('${success}', data.success).replace('${fails}', data.fails));
|
showSuccess(t('已修复 ${success} 个通道,失败 ${fails} 个通道。').replace('${success}', data.success).replace('${fails}', data.fails));
|
||||||
await refresh();
|
await refresh();
|
||||||
@@ -2033,7 +2030,7 @@ const ChannelsTable = () => {
|
|||||||
|
|
||||||
if (isTesting) {
|
if (isTesting) {
|
||||||
return (
|
return (
|
||||||
<Tag size='large' color='blue' shape='circle'>
|
<Tag color='blue' shape='circle'>
|
||||||
{t('测试中')}
|
{t('测试中')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
@@ -2041,7 +2038,7 @@ const ChannelsTable = () => {
|
|||||||
|
|
||||||
if (!testResult) {
|
if (!testResult) {
|
||||||
return (
|
return (
|
||||||
<Tag size='large' color='grey' shape='circle'>
|
<Tag color='grey' shape='circle'>
|
||||||
{t('未开始')}
|
{t('未开始')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
@@ -2050,7 +2047,6 @@ const ChannelsTable = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Tag
|
<Tag
|
||||||
size='large'
|
|
||||||
color={testResult.success ? 'green' : 'red'}
|
color={testResult.success ? 'green' : 'red'}
|
||||||
shape='circle'
|
shape='circle'
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -78,37 +78,37 @@ const LogsTable = () => {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case 1:
|
case 1:
|
||||||
return (
|
return (
|
||||||
<Tag color='cyan' size='large' shape='circle'>
|
<Tag color='cyan' shape='circle'>
|
||||||
{t('充值')}
|
{t('充值')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 2:
|
case 2:
|
||||||
return (
|
return (
|
||||||
<Tag color='lime' size='large' shape='circle'>
|
<Tag color='lime' shape='circle'>
|
||||||
{t('消费')}
|
{t('消费')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 3:
|
case 3:
|
||||||
return (
|
return (
|
||||||
<Tag color='orange' size='large' shape='circle'>
|
<Tag color='orange' shape='circle'>
|
||||||
{t('管理')}
|
{t('管理')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 4:
|
case 4:
|
||||||
return (
|
return (
|
||||||
<Tag color='purple' size='large' shape='circle'>
|
<Tag color='purple' shape='circle'>
|
||||||
{t('系统')}
|
{t('系统')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 5:
|
case 5:
|
||||||
return (
|
return (
|
||||||
<Tag color='red' size='large' shape='circle'>
|
<Tag color='red' shape='circle'>
|
||||||
{t('错误')}
|
{t('错误')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<Tag color='grey' size='large' shape='circle'>
|
<Tag color='grey' shape='circle'>
|
||||||
{t('未知')}
|
{t('未知')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
@@ -118,13 +118,13 @@ const LogsTable = () => {
|
|||||||
function renderIsStream(bool) {
|
function renderIsStream(bool) {
|
||||||
if (bool) {
|
if (bool) {
|
||||||
return (
|
return (
|
||||||
<Tag color='blue' size='large' shape='circle'>
|
<Tag color='blue' shape='circle'>
|
||||||
{t('流')}
|
{t('流')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Tag color='purple' size='large' shape='circle'>
|
<Tag color='purple' shape='circle'>
|
||||||
{t('非流')}
|
{t('非流')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
@@ -135,21 +135,21 @@ const LogsTable = () => {
|
|||||||
const time = parseInt(type);
|
const time = parseInt(type);
|
||||||
if (time < 101) {
|
if (time < 101) {
|
||||||
return (
|
return (
|
||||||
<Tag color='green' size='large' shape='circle'>
|
<Tag color='green' shape='circle'>
|
||||||
{' '}
|
{' '}
|
||||||
{time} s{' '}
|
{time} s{' '}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
} else if (time < 300) {
|
} else if (time < 300) {
|
||||||
return (
|
return (
|
||||||
<Tag color='orange' size='large' shape='circle'>
|
<Tag color='orange' shape='circle'>
|
||||||
{' '}
|
{' '}
|
||||||
{time} s{' '}
|
{time} s{' '}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Tag color='red' size='large' shape='circle'>
|
<Tag color='red' shape='circle'>
|
||||||
{' '}
|
{' '}
|
||||||
{time} s{' '}
|
{time} s{' '}
|
||||||
</Tag>
|
</Tag>
|
||||||
@@ -162,21 +162,21 @@ const LogsTable = () => {
|
|||||||
time = time.toFixed(1);
|
time = time.toFixed(1);
|
||||||
if (time < 3) {
|
if (time < 3) {
|
||||||
return (
|
return (
|
||||||
<Tag color='green' size='large' shape='circle'>
|
<Tag color='green' shape='circle'>
|
||||||
{' '}
|
{' '}
|
||||||
{time} s{' '}
|
{time} s{' '}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
} else if (time < 10) {
|
} else if (time < 10) {
|
||||||
return (
|
return (
|
||||||
<Tag color='orange' size='large' shape='circle'>
|
<Tag color='orange' shape='circle'>
|
||||||
{' '}
|
{' '}
|
||||||
{time} s{' '}
|
{time} s{' '}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Tag color='red' size='large' shape='circle'>
|
<Tag color='red' shape='circle'>
|
||||||
{' '}
|
{' '}
|
||||||
{time} s{' '}
|
{time} s{' '}
|
||||||
</Tag>
|
</Tag>
|
||||||
@@ -363,7 +363,6 @@ const LogsTable = () => {
|
|||||||
<Tooltip content={record.channel_name || '[未知]'}>
|
<Tooltip content={record.channel_name || '[未知]'}>
|
||||||
<Tag
|
<Tag
|
||||||
color={colors[parseInt(text) % colors.length]}
|
color={colors[parseInt(text) % colors.length]}
|
||||||
size='large'
|
|
||||||
shape='circle'
|
shape='circle'
|
||||||
>
|
>
|
||||||
{' '}
|
{' '}
|
||||||
@@ -415,7 +414,6 @@ const LogsTable = () => {
|
|||||||
<div>
|
<div>
|
||||||
<Tag
|
<Tag
|
||||||
color='grey'
|
color='grey'
|
||||||
size='large'
|
|
||||||
shape='circle'
|
shape='circle'
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
//cancel the row click event
|
//cancel the row click event
|
||||||
@@ -567,7 +565,6 @@ const LogsTable = () => {
|
|||||||
<Tooltip content={text}>
|
<Tooltip content={text}>
|
||||||
<Tag
|
<Tag
|
||||||
color='orange'
|
color='orange'
|
||||||
size='large'
|
|
||||||
shape='circle'
|
shape='circle'
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
copyText(event, text);
|
copyText(event, text);
|
||||||
|
|||||||
@@ -185,115 +185,115 @@ const LogsTable = () => {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case 'IMAGINE':
|
case 'IMAGINE':
|
||||||
return (
|
return (
|
||||||
<Tag color='blue' size='large' shape='circle' prefixIcon={<Palette size={14} />}>
|
<Tag color='blue' shape='circle' prefixIcon={<Palette size={14} />}>
|
||||||
{t('绘图')}
|
{t('绘图')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'UPSCALE':
|
case 'UPSCALE':
|
||||||
return (
|
return (
|
||||||
<Tag color='orange' size='large' shape='circle' prefixIcon={<ZoomIn size={14} />}>
|
<Tag color='orange' shape='circle' prefixIcon={<ZoomIn size={14} />}>
|
||||||
{t('放大')}
|
{t('放大')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'VIDEO':
|
case 'VIDEO':
|
||||||
return (
|
return (
|
||||||
<Tag color='orange' size='large' shape='circle' prefixIcon={<Video size={14} />}>
|
<Tag color='orange' shape='circle' prefixIcon={<Video size={14} />}>
|
||||||
{t('视频')}
|
{t('视频')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'EDITS':
|
case 'EDITS':
|
||||||
return (
|
return (
|
||||||
<Tag color='orange' size='large' shape='circle' prefixIcon={<Video size={14} />}>
|
<Tag color='orange' shape='circle' prefixIcon={<Video size={14} />}>
|
||||||
{t('编辑')}
|
{t('编辑')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'VARIATION':
|
case 'VARIATION':
|
||||||
return (
|
return (
|
||||||
<Tag color='purple' size='large' shape='circle' prefixIcon={<Shuffle size={14} />}>
|
<Tag color='purple' shape='circle' prefixIcon={<Shuffle size={14} />}>
|
||||||
{t('变换')}
|
{t('变换')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'HIGH_VARIATION':
|
case 'HIGH_VARIATION':
|
||||||
return (
|
return (
|
||||||
<Tag color='purple' size='large' shape='circle' prefixIcon={<Shuffle size={14} />}>
|
<Tag color='purple' shape='circle' prefixIcon={<Shuffle size={14} />}>
|
||||||
{t('强变换')}
|
{t('强变换')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'LOW_VARIATION':
|
case 'LOW_VARIATION':
|
||||||
return (
|
return (
|
||||||
<Tag color='purple' size='large' shape='circle' prefixIcon={<Shuffle size={14} />}>
|
<Tag color='purple' shape='circle' prefixIcon={<Shuffle size={14} />}>
|
||||||
{t('弱变换')}
|
{t('弱变换')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'PAN':
|
case 'PAN':
|
||||||
return (
|
return (
|
||||||
<Tag color='cyan' size='large' shape='circle' prefixIcon={<Move size={14} />}>
|
<Tag color='cyan' shape='circle' prefixIcon={<Move size={14} />}>
|
||||||
{t('平移')}
|
{t('平移')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'DESCRIBE':
|
case 'DESCRIBE':
|
||||||
return (
|
return (
|
||||||
<Tag color='yellow' size='large' shape='circle' prefixIcon={<FileText size={14} />}>
|
<Tag color='yellow' shape='circle' prefixIcon={<FileText size={14} />}>
|
||||||
{t('图生文')}
|
{t('图生文')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'BLEND':
|
case 'BLEND':
|
||||||
return (
|
return (
|
||||||
<Tag color='lime' size='large' shape='circle' prefixIcon={<Blend size={14} />}>
|
<Tag color='lime' shape='circle' prefixIcon={<Blend size={14} />}>
|
||||||
{t('图混合')}
|
{t('图混合')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'UPLOAD':
|
case 'UPLOAD':
|
||||||
return (
|
return (
|
||||||
<Tag color='blue' size='large' shape='circle' prefixIcon={<Upload size={14} />}>
|
<Tag color='blue' shape='circle' prefixIcon={<Upload size={14} />}>
|
||||||
上传文件
|
上传文件
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'SHORTEN':
|
case 'SHORTEN':
|
||||||
return (
|
return (
|
||||||
<Tag color='pink' size='large' shape='circle' prefixIcon={<Minimize2 size={14} />}>
|
<Tag color='pink' shape='circle' prefixIcon={<Minimize2 size={14} />}>
|
||||||
{t('缩词')}
|
{t('缩词')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'REROLL':
|
case 'REROLL':
|
||||||
return (
|
return (
|
||||||
<Tag color='indigo' size='large' shape='circle' prefixIcon={<RotateCcw size={14} />}>
|
<Tag color='indigo' shape='circle' prefixIcon={<RotateCcw size={14} />}>
|
||||||
{t('重绘')}
|
{t('重绘')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'INPAINT':
|
case 'INPAINT':
|
||||||
return (
|
return (
|
||||||
<Tag color='violet' size='large' shape='circle' prefixIcon={<PaintBucket size={14} />}>
|
<Tag color='violet' shape='circle' prefixIcon={<PaintBucket size={14} />}>
|
||||||
{t('局部重绘-提交')}
|
{t('局部重绘-提交')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'ZOOM':
|
case 'ZOOM':
|
||||||
return (
|
return (
|
||||||
<Tag color='teal' size='large' shape='circle' prefixIcon={<Focus size={14} />}>
|
<Tag color='teal' shape='circle' prefixIcon={<Focus size={14} />}>
|
||||||
{t('变焦')}
|
{t('变焦')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'CUSTOM_ZOOM':
|
case 'CUSTOM_ZOOM':
|
||||||
return (
|
return (
|
||||||
<Tag color='teal' size='large' shape='circle' prefixIcon={<Move3D size={14} />}>
|
<Tag color='teal' shape='circle' prefixIcon={<Move3D size={14} />}>
|
||||||
{t('自定义变焦-提交')}
|
{t('自定义变焦-提交')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'MODAL':
|
case 'MODAL':
|
||||||
return (
|
return (
|
||||||
<Tag color='green' size='large' shape='circle' prefixIcon={<Monitor size={14} />}>
|
<Tag color='green' shape='circle' prefixIcon={<Monitor size={14} />}>
|
||||||
{t('窗口处理')}
|
{t('窗口处理')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'SWAP_FACE':
|
case 'SWAP_FACE':
|
||||||
return (
|
return (
|
||||||
<Tag color='light-green' size='large' shape='circle' prefixIcon={<UserCheck size={14} />}>
|
<Tag color='light-green' shape='circle' prefixIcon={<UserCheck size={14} />}>
|
||||||
{t('换脸')}
|
{t('换脸')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<Tag color='white' size='large' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
<Tag color='white' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
||||||
{t('未知')}
|
{t('未知')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
@@ -304,31 +304,31 @@ const LogsTable = () => {
|
|||||||
switch (code) {
|
switch (code) {
|
||||||
case 1:
|
case 1:
|
||||||
return (
|
return (
|
||||||
<Tag color='green' size='large' shape='circle' prefixIcon={<CheckCircle size={14} />}>
|
<Tag color='green' shape='circle' prefixIcon={<CheckCircle size={14} />}>
|
||||||
{t('已提交')}
|
{t('已提交')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 21:
|
case 21:
|
||||||
return (
|
return (
|
||||||
<Tag color='lime' size='large' shape='circle' prefixIcon={<Clock size={14} />}>
|
<Tag color='lime' shape='circle' prefixIcon={<Clock size={14} />}>
|
||||||
{t('等待中')}
|
{t('等待中')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 22:
|
case 22:
|
||||||
return (
|
return (
|
||||||
<Tag color='orange' size='large' shape='circle' prefixIcon={<Copy size={14} />}>
|
<Tag color='orange' shape='circle' prefixIcon={<Copy size={14} />}>
|
||||||
{t('重复提交')}
|
{t('重复提交')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 0:
|
case 0:
|
||||||
return (
|
return (
|
||||||
<Tag color='yellow' size='large' shape='circle' prefixIcon={<FileX size={14} />}>
|
<Tag color='yellow' shape='circle' prefixIcon={<FileX size={14} />}>
|
||||||
{t('未提交')}
|
{t('未提交')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<Tag color='white' size='large' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
<Tag color='white' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
||||||
{t('未知')}
|
{t('未知')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
@@ -339,43 +339,43 @@ const LogsTable = () => {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case 'SUCCESS':
|
case 'SUCCESS':
|
||||||
return (
|
return (
|
||||||
<Tag color='green' size='large' shape='circle' prefixIcon={<CheckCircle size={14} />}>
|
<Tag color='green' shape='circle' prefixIcon={<CheckCircle size={14} />}>
|
||||||
{t('成功')}
|
{t('成功')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'NOT_START':
|
case 'NOT_START':
|
||||||
return (
|
return (
|
||||||
<Tag color='grey' size='large' shape='circle' prefixIcon={<Pause size={14} />}>
|
<Tag color='grey' shape='circle' prefixIcon={<Pause size={14} />}>
|
||||||
{t('未启动')}
|
{t('未启动')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'SUBMITTED':
|
case 'SUBMITTED':
|
||||||
return (
|
return (
|
||||||
<Tag color='yellow' size='large' shape='circle' prefixIcon={<Clock size={14} />}>
|
<Tag color='yellow' shape='circle' prefixIcon={<Clock size={14} />}>
|
||||||
{t('队列中')}
|
{t('队列中')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'IN_PROGRESS':
|
case 'IN_PROGRESS':
|
||||||
return (
|
return (
|
||||||
<Tag color='blue' size='large' shape='circle' prefixIcon={<Loader size={14} />}>
|
<Tag color='blue' shape='circle' prefixIcon={<Loader size={14} />}>
|
||||||
{t('执行中')}
|
{t('执行中')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'FAILURE':
|
case 'FAILURE':
|
||||||
return (
|
return (
|
||||||
<Tag color='red' size='large' shape='circle' prefixIcon={<XCircle size={14} />}>
|
<Tag color='red' shape='circle' prefixIcon={<XCircle size={14} />}>
|
||||||
{t('失败')}
|
{t('失败')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'MODAL':
|
case 'MODAL':
|
||||||
return (
|
return (
|
||||||
<Tag color='yellow' size='large' shape='circle' prefixIcon={<AlertCircle size={14} />}>
|
<Tag color='yellow' shape='circle' prefixIcon={<AlertCircle size={14} />}>
|
||||||
{t('窗口等待')}
|
{t('窗口等待')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<Tag color='white' size='large' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
<Tag color='white' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
||||||
{t('未知')}
|
{t('未知')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
@@ -405,7 +405,7 @@ const LogsTable = () => {
|
|||||||
const color = durationSec > 60 ? 'red' : 'green';
|
const color = durationSec > 60 ? 'red' : 'green';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tag color={color} size='large' shape='circle' prefixIcon={<Clock size={14} />}>
|
<Tag color={color} shape='circle' prefixIcon={<Clock size={14} />}>
|
||||||
{durationSec} {t('秒')}
|
{durationSec} {t('秒')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
@@ -439,7 +439,6 @@ const LogsTable = () => {
|
|||||||
<div>
|
<div>
|
||||||
<Tag
|
<Tag
|
||||||
color={colors[parseInt(text) % colors.length]}
|
color={colors[parseInt(text) % colors.length]}
|
||||||
size='large'
|
|
||||||
shape='circle'
|
shape='circle'
|
||||||
prefixIcon={<Hash size={14} />}
|
prefixIcon={<Hash size={14} />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@@ -76,13 +76,13 @@ const ModelPricing = () => {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case 1:
|
case 1:
|
||||||
return (
|
return (
|
||||||
<Tag color='teal' size='large' shape='circle'>
|
<Tag color='teal' shape='circle'>
|
||||||
{t('按次计费')}
|
{t('按次计费')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 0:
|
case 0:
|
||||||
return (
|
return (
|
||||||
<Tag color='violet' size='large' shape='circle'>
|
<Tag color='violet' shape='circle'>
|
||||||
{t('按量计费')}
|
{t('按量计费')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
@@ -116,7 +116,6 @@ const ModelPricing = () => {
|
|||||||
<Tag
|
<Tag
|
||||||
key={endpoint}
|
key={endpoint}
|
||||||
color={stringToColor(endpoint)}
|
color={stringToColor(endpoint)}
|
||||||
size='large'
|
|
||||||
shape='circle'
|
shape='circle'
|
||||||
>
|
>
|
||||||
{endpoint}
|
{endpoint}
|
||||||
@@ -179,7 +178,7 @@ const ModelPricing = () => {
|
|||||||
if (usableGroup[group]) {
|
if (usableGroup[group]) {
|
||||||
if (group === selectedGroup) {
|
if (group === selectedGroup) {
|
||||||
return (
|
return (
|
||||||
<Tag color='blue' size='large' shape='circle' prefixIcon={<IconVerify />}>
|
<Tag color='blue' shape='circle' prefixIcon={<IconVerify />}>
|
||||||
{group}
|
{group}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
@@ -187,7 +186,6 @@ const ModelPricing = () => {
|
|||||||
return (
|
return (
|
||||||
<Tag
|
<Tag
|
||||||
color='blue'
|
color='blue'
|
||||||
size='large'
|
|
||||||
shape='circle'
|
shape='circle'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedGroup(group);
|
setSelectedGroup(group);
|
||||||
@@ -392,7 +390,6 @@ const ModelPricing = () => {
|
|||||||
{category.label}
|
{category.label}
|
||||||
<Tag
|
<Tag
|
||||||
color={activeKey === key ? 'red' : 'grey'}
|
color={activeKey === key ? 'red' : 'grey'}
|
||||||
size='small'
|
|
||||||
shape='circle'
|
shape='circle'
|
||||||
>
|
>
|
||||||
{modelCount}
|
{modelCount}
|
||||||
@@ -436,7 +433,6 @@ const ModelPricing = () => {
|
|||||||
onCompositionEnd={handleCompositionEnd}
|
onCompositionEnd={handleCompositionEnd}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
showClear
|
showClear
|
||||||
size="large"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
@@ -446,7 +442,6 @@ const ModelPricing = () => {
|
|||||||
onClick={() => copyText(selectedRowKeys)}
|
onClick={() => copyText(selectedRowKeys)}
|
||||||
disabled={selectedRowKeys.length === 0}
|
disabled={selectedRowKeys.length === 0}
|
||||||
className="!bg-blue-500 hover:!bg-blue-600 text-white"
|
className="!bg-blue-500 hover:!bg-blue-600 text-white"
|
||||||
size="large"
|
|
||||||
>
|
>
|
||||||
{t('复制选中模型')}
|
{t('复制选中模型')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -53,31 +53,31 @@ const RedemptionsTable = () => {
|
|||||||
const renderStatus = (status, record) => {
|
const renderStatus = (status, record) => {
|
||||||
if (isExpired(record)) {
|
if (isExpired(record)) {
|
||||||
return (
|
return (
|
||||||
<Tag color='orange' size='large' shape='circle'>{t('已过期')}</Tag>
|
<Tag color='orange' shape='circle'>{t('已过期')}</Tag>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 1:
|
case 1:
|
||||||
return (
|
return (
|
||||||
<Tag color='green' size='large' shape='circle'>
|
<Tag color='green' shape='circle'>
|
||||||
{t('未使用')}
|
{t('未使用')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 2:
|
case 2:
|
||||||
return (
|
return (
|
||||||
<Tag color='red' size='large' shape='circle'>
|
<Tag color='red' shape='circle'>
|
||||||
{t('已禁用')}
|
{t('已禁用')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 3:
|
case 3:
|
||||||
return (
|
return (
|
||||||
<Tag color='grey' size='large' shape='circle'>
|
<Tag color='grey' shape='circle'>
|
||||||
{t('已使用')}
|
{t('已使用')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<Tag color='black' size='large' shape='circle'>
|
<Tag color='black' shape='circle'>
|
||||||
{t('未知状态')}
|
{t('未知状态')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
@@ -107,7 +107,7 @@ const RedemptionsTable = () => {
|
|||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Tag size={'large'} color={'grey'} shape='circle'>
|
<Tag color='grey' shape='circle'>
|
||||||
{renderQuota(parseInt(text))}
|
{renderQuota(parseInt(text))}
|
||||||
</Tag>
|
</Tag>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ function renderDuration(submit_time, finishTime) {
|
|||||||
|
|
||||||
// 返回带有样式的颜色标签
|
// 返回带有样式的颜色标签
|
||||||
return (
|
return (
|
||||||
<Tag color={color} size='large' prefixIcon={<Clock size={14} />}>
|
<Tag color={color} prefixIcon={<Clock size={14} />}>
|
||||||
{durationSec} 秒
|
{durationSec} 秒
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
@@ -198,31 +198,31 @@ const LogsTable = () => {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case 'MUSIC':
|
case 'MUSIC':
|
||||||
return (
|
return (
|
||||||
<Tag color='grey' size='large' shape='circle' prefixIcon={<Music size={14} />}>
|
<Tag color='grey' shape='circle' prefixIcon={<Music size={14} />}>
|
||||||
{t('生成音乐')}
|
{t('生成音乐')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'LYRICS':
|
case 'LYRICS':
|
||||||
return (
|
return (
|
||||||
<Tag color='pink' size='large' shape='circle' prefixIcon={<FileText size={14} />}>
|
<Tag color='pink' shape='circle' prefixIcon={<FileText size={14} />}>
|
||||||
{t('生成歌词')}
|
{t('生成歌词')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case TASK_ACTION_GENERATE:
|
case TASK_ACTION_GENERATE:
|
||||||
return (
|
return (
|
||||||
<Tag color='blue' size='large' shape='circle' prefixIcon={<Sparkles size={14} />}>
|
<Tag color='blue' shape='circle' prefixIcon={<Sparkles size={14} />}>
|
||||||
{t('图生视频')}
|
{t('图生视频')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case TASK_ACTION_TEXT_GENERATE:
|
case TASK_ACTION_TEXT_GENERATE:
|
||||||
return (
|
return (
|
||||||
<Tag color='blue' size='large' shape='circle' prefixIcon={<Sparkles size={14} />}>
|
<Tag color='blue' shape='circle' prefixIcon={<Sparkles size={14} />}>
|
||||||
{t('文生视频')}
|
{t('文生视频')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<Tag color='white' size='large' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
<Tag color='white' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
||||||
{t('未知')}
|
{t('未知')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
@@ -233,25 +233,25 @@ const LogsTable = () => {
|
|||||||
switch (platform) {
|
switch (platform) {
|
||||||
case 'suno':
|
case 'suno':
|
||||||
return (
|
return (
|
||||||
<Tag color='green' size='large' shape='circle' prefixIcon={<Music size={14} />}>
|
<Tag color='green' shape='circle' prefixIcon={<Music size={14} />}>
|
||||||
Suno
|
Suno
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'kling':
|
case 'kling':
|
||||||
return (
|
return (
|
||||||
<Tag color='orange' size='large' shape='circle' prefixIcon={<Video size={14} />}>
|
<Tag color='orange' shape='circle' prefixIcon={<Video size={14} />}>
|
||||||
Kling
|
Kling
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'jimeng':
|
case 'jimeng':
|
||||||
return (
|
return (
|
||||||
<Tag color='purple' size='large' shape='circle' prefixIcon={<Video size={14} />}>
|
<Tag color='purple' shape='circle' prefixIcon={<Video size={14} />}>
|
||||||
Jimeng
|
Jimeng
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<Tag color='white' size='large' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
<Tag color='white' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
||||||
{t('未知')}
|
{t('未知')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
@@ -262,55 +262,55 @@ const LogsTable = () => {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case 'SUCCESS':
|
case 'SUCCESS':
|
||||||
return (
|
return (
|
||||||
<Tag color='green' size='large' shape='circle' prefixIcon={<CheckCircle size={14} />}>
|
<Tag color='green' shape='circle' prefixIcon={<CheckCircle size={14} />}>
|
||||||
{t('成功')}
|
{t('成功')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'NOT_START':
|
case 'NOT_START':
|
||||||
return (
|
return (
|
||||||
<Tag color='grey' size='large' shape='circle' prefixIcon={<Pause size={14} />}>
|
<Tag color='grey' shape='circle' prefixIcon={<Pause size={14} />}>
|
||||||
{t('未启动')}
|
{t('未启动')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'SUBMITTED':
|
case 'SUBMITTED':
|
||||||
return (
|
return (
|
||||||
<Tag color='yellow' size='large' shape='circle' prefixIcon={<Clock size={14} />}>
|
<Tag color='yellow' shape='circle' prefixIcon={<Clock size={14} />}>
|
||||||
{t('队列中')}
|
{t('队列中')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'IN_PROGRESS':
|
case 'IN_PROGRESS':
|
||||||
return (
|
return (
|
||||||
<Tag color='blue' size='large' shape='circle' prefixIcon={<Play size={14} />}>
|
<Tag color='blue' shape='circle' prefixIcon={<Play size={14} />}>
|
||||||
{t('执行中')}
|
{t('执行中')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'FAILURE':
|
case 'FAILURE':
|
||||||
return (
|
return (
|
||||||
<Tag color='red' size='large' shape='circle' prefixIcon={<XCircle size={14} />}>
|
<Tag color='red' shape='circle' prefixIcon={<XCircle size={14} />}>
|
||||||
{t('失败')}
|
{t('失败')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'QUEUED':
|
case 'QUEUED':
|
||||||
return (
|
return (
|
||||||
<Tag color='orange' size='large' shape='circle' prefixIcon={<List size={14} />}>
|
<Tag color='orange' shape='circle' prefixIcon={<List size={14} />}>
|
||||||
{t('排队中')}
|
{t('排队中')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 'UNKNOWN':
|
case 'UNKNOWN':
|
||||||
return (
|
return (
|
||||||
<Tag color='white' size='large' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
<Tag color='white' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
||||||
{t('未知')}
|
{t('未知')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case '':
|
case '':
|
||||||
return (
|
return (
|
||||||
<Tag color='grey' size='large' shape='circle' prefixIcon={<Loader size={14} />}>
|
<Tag color='grey' shape='circle' prefixIcon={<Loader size={14} />}>
|
||||||
{t('正在提交')}
|
{t('正在提交')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<Tag color='white' size='large' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
<Tag color='white' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
||||||
{t('未知')}
|
{t('未知')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
timestamp2string,
|
timestamp2string,
|
||||||
renderGroup,
|
renderGroup,
|
||||||
renderQuota,
|
renderQuota,
|
||||||
getQuotaPerUnit
|
getModelCategories
|
||||||
} from '../../helpers';
|
} from '../../helpers';
|
||||||
import { ITEMS_PER_PAGE } from '../../constants';
|
import { ITEMS_PER_PAGE } from '../../constants';
|
||||||
import {
|
import {
|
||||||
@@ -22,6 +22,12 @@ import {
|
|||||||
SplitButtonGroup,
|
SplitButtonGroup,
|
||||||
Table,
|
Table,
|
||||||
Tag,
|
Tag,
|
||||||
|
AvatarGroup,
|
||||||
|
Avatar,
|
||||||
|
Tooltip,
|
||||||
|
Progress,
|
||||||
|
Switch,
|
||||||
|
Input,
|
||||||
Typography
|
Typography
|
||||||
} from '@douyinfe/semi-ui';
|
} from '@douyinfe/semi-ui';
|
||||||
import {
|
import {
|
||||||
@@ -31,7 +37,10 @@ import {
|
|||||||
import {
|
import {
|
||||||
IconSearch,
|
IconSearch,
|
||||||
IconTreeTriangleDown,
|
IconTreeTriangleDown,
|
||||||
IconMore,
|
IconCopy,
|
||||||
|
IconEyeOpened,
|
||||||
|
IconEyeClosed,
|
||||||
|
IconBolt,
|
||||||
} from '@douyinfe/semi-icons';
|
} from '@douyinfe/semi-icons';
|
||||||
import { Key } from 'lucide-react';
|
import { Key } from 'lucide-react';
|
||||||
import EditToken from '../../pages/Token/EditToken';
|
import EditToken from '../../pages/Token/EditToken';
|
||||||
@@ -47,49 +56,6 @@ function renderTimestamp(timestamp) {
|
|||||||
const TokensTable = () => {
|
const TokensTable = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const renderStatus = (status, model_limits_enabled = false) => {
|
|
||||||
switch (status) {
|
|
||||||
case 1:
|
|
||||||
if (model_limits_enabled) {
|
|
||||||
return (
|
|
||||||
<Tag color='green' size='large' shape='circle' >
|
|
||||||
{t('已启用:限制模型')}
|
|
||||||
</Tag>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<Tag color='green' size='large' shape='circle' >
|
|
||||||
{t('已启用')}
|
|
||||||
</Tag>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
return (
|
|
||||||
<Tag color='red' size='large' shape='circle' >
|
|
||||||
{t('已禁用')}
|
|
||||||
</Tag>
|
|
||||||
);
|
|
||||||
case 3:
|
|
||||||
return (
|
|
||||||
<Tag color='yellow' size='large' shape='circle' >
|
|
||||||
{t('已过期')}
|
|
||||||
</Tag>
|
|
||||||
);
|
|
||||||
case 4:
|
|
||||||
return (
|
|
||||||
<Tag color='grey' size='large' shape='circle' >
|
|
||||||
{t('已耗尽')}
|
|
||||||
</Tag>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<Tag color='black' size='large' shape='circle' >
|
|
||||||
{t('未知状态')}
|
|
||||||
</Tag>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t('名称'),
|
title: t('名称'),
|
||||||
@@ -99,66 +65,249 @@ const TokensTable = () => {
|
|||||||
title: t('状态'),
|
title: t('状态'),
|
||||||
dataIndex: 'status',
|
dataIndex: 'status',
|
||||||
key: 'status',
|
key: 'status',
|
||||||
render: (text, record, index) => {
|
render: (text, record) => {
|
||||||
return (
|
const enabled = text === 1;
|
||||||
<div>
|
const handleToggle = (checked) => {
|
||||||
<Space>
|
if (checked) {
|
||||||
{renderStatus(text, record.model_limits_enabled)}
|
manageToken(record.id, 'enable', record);
|
||||||
{renderGroup(record.group)}
|
|
||||||
</Space>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('已用额度'),
|
|
||||||
dataIndex: 'used_quota',
|
|
||||||
render: (text, record, index) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Tag size={'large'} color={'grey'} shape='circle' >
|
|
||||||
{renderQuota(parseInt(text))}
|
|
||||||
</Tag>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('剩余额度'),
|
|
||||||
dataIndex: 'remain_quota',
|
|
||||||
render: (text, record, index) => {
|
|
||||||
const getQuotaColor = (quotaValue) => {
|
|
||||||
const quotaPerUnit = getQuotaPerUnit();
|
|
||||||
const dollarAmount = quotaValue / quotaPerUnit;
|
|
||||||
|
|
||||||
if (dollarAmount <= 0) {
|
|
||||||
return 'red';
|
|
||||||
} else if (dollarAmount <= 100) {
|
|
||||||
return 'yellow';
|
|
||||||
} else {
|
} else {
|
||||||
return 'green';
|
manageToken(record.id, 'disable', record);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let tagColor = 'black';
|
||||||
|
let tagText = t('未知状态');
|
||||||
|
if (enabled) {
|
||||||
|
tagColor = 'green';
|
||||||
|
tagText = t('已启用');
|
||||||
|
} else if (text === 2) {
|
||||||
|
tagColor = 'red';
|
||||||
|
tagText = t('已禁用');
|
||||||
|
} else if (text === 3) {
|
||||||
|
tagColor = 'yellow';
|
||||||
|
tagText = t('已过期');
|
||||||
|
} else if (text === 4) {
|
||||||
|
tagColor = 'grey';
|
||||||
|
tagText = t('已耗尽');
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Tag
|
||||||
{record.unlimited_quota ? (
|
color={tagColor}
|
||||||
<Tag size={'large'} color={'white'} shape='circle' >
|
shape='circle'
|
||||||
{t('无限制')}
|
prefixIcon={
|
||||||
</Tag>
|
<Switch
|
||||||
) : (
|
size='small'
|
||||||
<Tag
|
checked={enabled}
|
||||||
size={'large'}
|
onChange={handleToggle}
|
||||||
color={getQuotaColor(parseInt(text))}
|
aria-label='token status switch'
|
||||||
shape='circle'
|
/>
|
||||||
>
|
}
|
||||||
{renderQuota(parseInt(text))}
|
>
|
||||||
</Tag>
|
{tagText}
|
||||||
)}
|
</Tag>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('分组'),
|
||||||
|
dataIndex: 'group',
|
||||||
|
key: 'group',
|
||||||
|
render: (text) => {
|
||||||
|
if (text === 'auto') {
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
content={t('当前分组为 auto,会自动选择最优分组,当一个组不可用时自动降级到下一个组(熔断机制)')}
|
||||||
|
position='top'
|
||||||
|
>
|
||||||
|
<Tag color='blue' shape='circle' prefixIcon={<IconBolt />}> {t('智能熔断')} </Tag>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return renderGroup(text);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('密钥'),
|
||||||
|
key: 'token_key',
|
||||||
|
render: (text, record) => {
|
||||||
|
const fullKey = 'sk-' + record.key;
|
||||||
|
const maskedKey = 'sk-' + record.key.slice(0, 3) + '********' + record.key.slice(-3);
|
||||||
|
const revealed = !!showKeys[record.id];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='w-[200px]'>
|
||||||
|
<Input
|
||||||
|
readOnly
|
||||||
|
value={revealed ? fullKey : maskedKey}
|
||||||
|
size='small'
|
||||||
|
suffix={
|
||||||
|
<div className='flex items-center'>
|
||||||
|
<Button
|
||||||
|
theme='borderless'
|
||||||
|
size='small'
|
||||||
|
type='tertiary'
|
||||||
|
icon={revealed ? <IconEyeClosed /> : <IconEyeOpened />}
|
||||||
|
aria-label='toggle token visibility'
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setShowKeys(prev => ({ ...prev, [record.id]: !revealed }));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
theme='borderless'
|
||||||
|
size='small'
|
||||||
|
type='tertiary'
|
||||||
|
icon={<IconCopy />}
|
||||||
|
aria-label='copy token key'
|
||||||
|
onClick={async (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
await copyText(fullKey);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t('额度'),
|
||||||
|
key: 'quota',
|
||||||
|
render: (text, record) => {
|
||||||
|
if (record.unlimited_quota) {
|
||||||
|
return <Tag color='white' shape='circle'>{t('无限制')}</Tag>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const used = parseInt(record.used_quota) || 0;
|
||||||
|
const remain = parseInt(record.remain_quota) || 0;
|
||||||
|
const total = used + remain;
|
||||||
|
const percent = total > 0 ? (used / total) * 100 : 0;
|
||||||
|
|
||||||
|
const getProgressColor = (pct) => {
|
||||||
|
if (pct >= 90) return 'var(--semi-color-danger)';
|
||||||
|
if (pct >= 70) return 'var(--semi-color-warning)';
|
||||||
|
return undefined; // default color
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
content={
|
||||||
|
<div className='text-xs'>
|
||||||
|
<div>{t('已用额度')}: {renderQuota(used)}</div>
|
||||||
|
<div>{t('剩余额度')}: {renderQuota(remain)}</div>
|
||||||
|
<div>{t('总额度')}: {renderQuota(total)}</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className='w-[30px]'>
|
||||||
|
<Progress
|
||||||
|
percent={percent}
|
||||||
|
stroke={getProgressColor(percent)}
|
||||||
|
showInfo
|
||||||
|
aria-label='quota usage'
|
||||||
|
format={percent => <span className="text-xs">{percent.toFixed(0)}%</span>}
|
||||||
|
type="circle"
|
||||||
|
width={30}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('可用模型'),
|
||||||
|
dataIndex: 'model_limits',
|
||||||
|
render: (text, record) => {
|
||||||
|
if (record.model_limits_enabled && text) {
|
||||||
|
const models = text.split(',').filter(Boolean);
|
||||||
|
const categories = getModelCategories(t);
|
||||||
|
|
||||||
|
const vendorAvatars = [];
|
||||||
|
Object.entries(categories).forEach(([key, category]) => {
|
||||||
|
if (key === 'all') return;
|
||||||
|
if (!category.icon || !category.filter) return;
|
||||||
|
const vendorModels = models.filter((m) => category.filter({ model_name: m }));
|
||||||
|
if (vendorModels.length > 0) {
|
||||||
|
vendorAvatars.push(
|
||||||
|
<Tooltip key={key} content={vendorModels.join(', ')} position='top' showArrow>
|
||||||
|
<Avatar size='extra-extra-small' alt={category.label} color='transparent'>
|
||||||
|
{category.icon}
|
||||||
|
</Avatar>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (vendorAvatars.length === 0) {
|
||||||
|
vendorAvatars.push(
|
||||||
|
<Tooltip key='default' content={models.join(', ')} position='top' showArrow>
|
||||||
|
<Avatar size='extra-extra-small' alt='models'>
|
||||||
|
{models[0].slice(0, 2).toUpperCase()}
|
||||||
|
</Avatar>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AvatarGroup size='extra-extra-small'>
|
||||||
|
{vendorAvatars}
|
||||||
|
</AvatarGroup>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Tag color='white' shape='circle'>
|
||||||
|
{t('无限制')}
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('IP限制'),
|
||||||
|
dataIndex: 'allow_ips',
|
||||||
|
render: (text) => {
|
||||||
|
if (!text || text.trim() === '') {
|
||||||
|
return (
|
||||||
|
<Tag color='white' shape='circle'>
|
||||||
|
{t('无限制')}
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ips = text
|
||||||
|
.split('\n')
|
||||||
|
.map((ip) => ip.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
const displayIps = ips.slice(0, 1);
|
||||||
|
const extraCount = ips.length - displayIps.length;
|
||||||
|
|
||||||
|
const ipTags = displayIps.map((ip, idx) => (
|
||||||
|
<Tag key={idx} shape='circle'>
|
||||||
|
{ip}
|
||||||
|
</Tag>
|
||||||
|
));
|
||||||
|
|
||||||
|
if (extraCount > 0) {
|
||||||
|
ipTags.push(
|
||||||
|
<Tooltip
|
||||||
|
key='extra'
|
||||||
|
content={ips.slice(2).join(', ')}
|
||||||
|
position='top'
|
||||||
|
showArrow
|
||||||
|
>
|
||||||
|
<Tag shape='circle'>
|
||||||
|
{'+' + extraCount}
|
||||||
|
</Tag>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Space wrap>{ipTags}</Space>;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t('创建时间'),
|
title: t('创建时间'),
|
||||||
dataIndex: 'created_time',
|
dataIndex: 'created_time',
|
||||||
@@ -211,58 +360,6 @@ const TokensTable = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建更多操作的下拉菜单项
|
|
||||||
const moreMenuItems = [
|
|
||||||
{
|
|
||||||
node: 'item',
|
|
||||||
name: t('查看'),
|
|
||||||
onClick: () => {
|
|
||||||
Modal.info({
|
|
||||||
title: t('令牌详情'),
|
|
||||||
content: 'sk-' + record.key,
|
|
||||||
size: 'large',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
node: 'item',
|
|
||||||
name: t('删除'),
|
|
||||||
type: 'danger',
|
|
||||||
onClick: () => {
|
|
||||||
Modal.confirm({
|
|
||||||
title: t('确定是否要删除此令牌?'),
|
|
||||||
content: t('此修改将不可逆'),
|
|
||||||
onOk: () => {
|
|
||||||
manageToken(record.id, 'delete', record).then(() => {
|
|
||||||
removeRecord(record.key);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// 动态添加启用/禁用按钮
|
|
||||||
if (record.status === 1) {
|
|
||||||
moreMenuItems.push({
|
|
||||||
node: 'item',
|
|
||||||
name: t('禁用'),
|
|
||||||
type: 'warning',
|
|
||||||
onClick: () => {
|
|
||||||
manageToken(record.id, 'disable', record);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
moreMenuItems.push({
|
|
||||||
node: 'item',
|
|
||||||
name: t('启用'),
|
|
||||||
type: 'secondary',
|
|
||||||
onClick: () => {
|
|
||||||
manageToken(record.id, 'enable', record);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
<SplitButtonGroup
|
<SplitButtonGroup
|
||||||
@@ -304,17 +401,6 @@ const TokensTable = () => {
|
|||||||
</Dropdown>
|
</Dropdown>
|
||||||
</SplitButtonGroup>
|
</SplitButtonGroup>
|
||||||
|
|
||||||
<Button
|
|
||||||
theme='light'
|
|
||||||
type='secondary'
|
|
||||||
size="small"
|
|
||||||
onClick={async (text) => {
|
|
||||||
await copyText('sk-' + record.key);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('复制')}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
theme='light'
|
theme='light'
|
||||||
type='tertiary'
|
type='tertiary'
|
||||||
@@ -327,18 +413,24 @@ const TokensTable = () => {
|
|||||||
{t('编辑')}
|
{t('编辑')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Dropdown
|
<Button
|
||||||
trigger='click'
|
theme='light'
|
||||||
position='bottomRight'
|
type='danger'
|
||||||
menu={moreMenuItems}
|
size="small"
|
||||||
|
onClick={() => {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('确定是否要删除此令牌?'),
|
||||||
|
content: t('此修改将不可逆'),
|
||||||
|
onOk: () => {
|
||||||
|
manageToken(record.id, 'delete', record).then(() => {
|
||||||
|
removeRecord(record.key);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
{t('删除')}
|
||||||
icon={<IconMore />}
|
</Button>
|
||||||
theme='light'
|
|
||||||
type='tertiary'
|
|
||||||
size="small"
|
|
||||||
/>
|
|
||||||
</Dropdown>
|
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -357,6 +449,7 @@ const TokensTable = () => {
|
|||||||
id: undefined,
|
id: undefined,
|
||||||
});
|
});
|
||||||
const [compactMode, setCompactMode] = useTableCompactMode('tokens');
|
const [compactMode, setCompactMode] = useTableCompactMode('tokens');
|
||||||
|
const [showKeys, setShowKeys] = useState({});
|
||||||
|
|
||||||
// Form 初始值
|
// Form 初始值
|
||||||
const formInitValues = {
|
const formInitValues = {
|
||||||
|
|||||||
@@ -54,25 +54,25 @@ const UsersTable = () => {
|
|||||||
switch (role) {
|
switch (role) {
|
||||||
case 1:
|
case 1:
|
||||||
return (
|
return (
|
||||||
<Tag size='large' color='blue' shape='circle' prefixIcon={<User size={14} />}>
|
<Tag color='blue' shape='circle' prefixIcon={<User size={14} />}>
|
||||||
{t('普通用户')}
|
{t('普通用户')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 10:
|
case 10:
|
||||||
return (
|
return (
|
||||||
<Tag color='yellow' size='large' shape='circle' prefixIcon={<Shield size={14} />}>
|
<Tag color='yellow' shape='circle' prefixIcon={<Shield size={14} />}>
|
||||||
{t('管理员')}
|
{t('管理员')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
case 100:
|
case 100:
|
||||||
return (
|
return (
|
||||||
<Tag color='orange' size='large' shape='circle' prefixIcon={<Crown size={14} />}>
|
<Tag color='orange' shape='circle' prefixIcon={<Crown size={14} />}>
|
||||||
{t('超级管理员')}
|
{t('超级管理员')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<Tag color='red' size='large' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
<Tag color='red' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
||||||
{t('未知身份')}
|
{t('未知身份')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
@@ -82,16 +82,16 @@ const UsersTable = () => {
|
|||||||
const renderStatus = (status) => {
|
const renderStatus = (status) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 1:
|
case 1:
|
||||||
return <Tag size='large' color='green' shape='circle' prefixIcon={<CheckCircle size={14} />}>{t('已激活')}</Tag>;
|
return <Tag color='green' shape='circle' prefixIcon={<CheckCircle size={14} />}>{t('已激活')}</Tag>;
|
||||||
case 2:
|
case 2:
|
||||||
return (
|
return (
|
||||||
<Tag size='large' color='red' shape='circle' prefixIcon={<XCircle size={14} />}>
|
<Tag color='red' shape='circle' prefixIcon={<XCircle size={14} />}>
|
||||||
{t('已封禁')}
|
{t('已封禁')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<Tag size='large' color='grey' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
<Tag color='grey' shape='circle' prefixIcon={<HelpCircle size={14} />}>
|
||||||
{t('未知状态')}
|
{t('未知状态')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
@@ -117,7 +117,7 @@ const UsersTable = () => {
|
|||||||
<Space spacing={2}>
|
<Space spacing={2}>
|
||||||
<span>{text}</span>
|
<span>{text}</span>
|
||||||
<Tooltip content={remark} position="top" showArrow>
|
<Tooltip content={remark} position="top" showArrow>
|
||||||
<Tag color='white' size='large' shape='circle' className="!text-xs">
|
<Tag color='white' shape='circle' className="!text-xs">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<div className="w-2 h-2 flex-shrink-0 rounded-full" style={{ backgroundColor: '#10b981' }} />
|
<div className="w-2 h-2 flex-shrink-0 rounded-full" style={{ backgroundColor: '#10b981' }} />
|
||||||
{displayRemark}
|
{displayRemark}
|
||||||
@@ -142,13 +142,13 @@ const UsersTable = () => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Space spacing={1}>
|
<Space spacing={1}>
|
||||||
<Tag color='white' size='large' shape='circle' className="!text-xs" prefixIcon={<Coins size={14} />}>
|
<Tag color='white' shape='circle' className="!text-xs" prefixIcon={<Coins size={14} />}>
|
||||||
{t('剩余')}: {renderQuota(record.quota)}
|
{t('剩余')}: {renderQuota(record.quota)}
|
||||||
</Tag>
|
</Tag>
|
||||||
<Tag color='white' size='large' shape='circle' className="!text-xs" prefixIcon={<Coins size={14} />}>
|
<Tag color='white' shape='circle' className="!text-xs" prefixIcon={<Coins size={14} />}>
|
||||||
{t('已用')}: {renderQuota(record.used_quota)}
|
{t('已用')}: {renderQuota(record.used_quota)}
|
||||||
</Tag>
|
</Tag>
|
||||||
<Tag color='white' size='large' shape='circle' className="!text-xs" prefixIcon={<Activity size={14} />}>
|
<Tag color='white' shape='circle' className="!text-xs" prefixIcon={<Activity size={14} />}>
|
||||||
{t('调用')}: {renderNumber(record.request_count)}
|
{t('调用')}: {renderNumber(record.request_count)}
|
||||||
</Tag>
|
</Tag>
|
||||||
</Space>
|
</Space>
|
||||||
@@ -163,13 +163,13 @@ const UsersTable = () => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Space spacing={1}>
|
<Space spacing={1}>
|
||||||
<Tag color='white' size='large' shape='circle' className="!text-xs" prefixIcon={<Users size={14} />}>
|
<Tag color='white' shape='circle' className="!text-xs" prefixIcon={<Users size={14} />}>
|
||||||
{t('邀请')}: {renderNumber(record.aff_count)}
|
{t('邀请')}: {renderNumber(record.aff_count)}
|
||||||
</Tag>
|
</Tag>
|
||||||
<Tag color='white' size='large' shape='circle' className="!text-xs" prefixIcon={<DollarSign size={14} />}>
|
<Tag color='white' shape='circle' className="!text-xs" prefixIcon={<DollarSign size={14} />}>
|
||||||
{t('收益')}: {renderQuota(record.aff_history_quota)}
|
{t('收益')}: {renderQuota(record.aff_history_quota)}
|
||||||
</Tag>
|
</Tag>
|
||||||
<Tag color='white' size='large' shape='circle' className="!text-xs" prefixIcon={<UserPlus size={14} />}>
|
<Tag color='white' shape='circle' className="!text-xs" prefixIcon={<UserPlus size={14} />}>
|
||||||
{record.inviter_id === 0 ? t('无邀请人') : `邀请人: ${record.inviter_id}`}
|
{record.inviter_id === 0 ? t('无邀请人') : `邀请人: ${record.inviter_id}`}
|
||||||
</Tag>
|
</Tag>
|
||||||
</Space>
|
</Space>
|
||||||
|
|||||||
@@ -539,7 +539,7 @@ export function stringToColor(str) {
|
|||||||
export function renderModelTag(modelName, options = {}) {
|
export function renderModelTag(modelName, options = {}) {
|
||||||
const {
|
const {
|
||||||
color,
|
color,
|
||||||
size = 'large',
|
size = 'default',
|
||||||
shape = 'circle',
|
shape = 'circle',
|
||||||
onClick,
|
onClick,
|
||||||
suffixIcon,
|
suffixIcon,
|
||||||
@@ -584,7 +584,7 @@ export function renderText(text, limit) {
|
|||||||
export function renderGroup(group) {
|
export function renderGroup(group) {
|
||||||
if (group === '') {
|
if (group === '') {
|
||||||
return (
|
return (
|
||||||
<Tag size='large' key='default' color='orange' shape='circle'>
|
<Tag key='default' color='orange' shape='circle'>
|
||||||
{i18next.t('用户分组')}
|
{i18next.t('用户分组')}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
@@ -603,7 +603,6 @@ export function renderGroup(group) {
|
|||||||
<span key={group}>
|
<span key={group}>
|
||||||
{groups.map((group) => (
|
{groups.map((group) => (
|
||||||
<Tag
|
<Tag
|
||||||
size='large'
|
|
||||||
color={tagColors[group] || stringToColor(group)}
|
color={tagColors[group] || stringToColor(group)}
|
||||||
key={group}
|
key={group}
|
||||||
shape='circle'
|
shape='circle'
|
||||||
|
|||||||
@@ -373,6 +373,9 @@
|
|||||||
"搜索令牌的名称 ...": "Search for the name of the token...",
|
"搜索令牌的名称 ...": "Search for the name of the token...",
|
||||||
"已用额度": "Quota used",
|
"已用额度": "Quota used",
|
||||||
"剩余额度": "Remaining quota",
|
"剩余额度": "Remaining quota",
|
||||||
|
"总额度": "Total quota",
|
||||||
|
"智能熔断": "Smart fallback",
|
||||||
|
"当前分组为 auto,会自动选择最优分组,当一个组不可用时自动降级到下一个组(熔断机制)": "The current group is auto, it will automatically select the optimal group, and automatically downgrade to the next group when a group is unavailable (breakage mechanism)",
|
||||||
"过期时间": "Expiration time",
|
"过期时间": "Expiration time",
|
||||||
"无": "None",
|
"无": "None",
|
||||||
"无限制": "Unlimited",
|
"无限制": "Unlimited",
|
||||||
@@ -962,6 +965,7 @@
|
|||||||
"启用突发备用号池(建议勾选,极大降低故障率)": "Enable burst backup number pool (it is recommended to check this box to greatly reduce the failure rate)",
|
"启用突发备用号池(建议勾选,极大降低故障率)": "Enable burst backup number pool (it is recommended to check this box to greatly reduce the failure rate)",
|
||||||
"查看说明": "View instructions",
|
"查看说明": "View instructions",
|
||||||
"添加令牌": "Create token",
|
"添加令牌": "Create token",
|
||||||
|
"IP限制": "IP restrictions",
|
||||||
"令牌纬度控制 Midjouney 配置,设置优先级:令牌 > 路径参数 > 系统默认": "Token latitude controls Midjouney configuration, setting priority: token > path parameter > system default",
|
"令牌纬度控制 Midjouney 配置,设置优先级:令牌 > 路径参数 > 系统默认": "Token latitude controls Midjouney configuration, setting priority: token > path parameter > system default",
|
||||||
"启用速率限制": "Enable rate limiting",
|
"启用速率限制": "Enable rate limiting",
|
||||||
"复制BaseURL": "Copy BaseURL",
|
"复制BaseURL": "Copy BaseURL",
|
||||||
|
|||||||
@@ -1381,7 +1381,7 @@ const Detail = (props) => {
|
|||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Bell size={16} />
|
<Bell size={16} />
|
||||||
{t('系统公告')}
|
{t('系统公告')}
|
||||||
<Tag size="small" color="grey" shape="circle">
|
<Tag color="grey" shape="circle">
|
||||||
{t('显示最新20条')}
|
{t('显示最新20条')}
|
||||||
</Tag>
|
</Tag>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ const Setup = () => {
|
|||||||
title={
|
title={
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<span className="font-medium">{t('数据库警告')}</span>
|
<span className="font-medium">{t('数据库警告')}</span>
|
||||||
<Tag color='orange' size='small' className="ml-2 !rounded-full">
|
<Tag color='orange' shape='circle' className="ml-2">
|
||||||
SQLite
|
SQLite
|
||||||
</Tag>
|
</Tag>
|
||||||
</div>
|
</div>
|
||||||
@@ -222,7 +222,7 @@ const Setup = () => {
|
|||||||
title={
|
title={
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<span className="font-medium">{t('数据库信息')}</span>
|
<span className="font-medium">{t('数据库信息')}</span>
|
||||||
<Tag color='blue' size='small' className="ml-2 !rounded-full">
|
<Tag color='blue' shape='circle' className="ml-2">
|
||||||
MySQL
|
MySQL
|
||||||
</Tag>
|
</Tag>
|
||||||
</div>
|
</div>
|
||||||
@@ -256,7 +256,7 @@ const Setup = () => {
|
|||||||
title={
|
title={
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<span className="font-medium">{t('数据库信息')}</span>
|
<span className="font-medium">{t('数据库信息')}</span>
|
||||||
<Tag color='green' size='small' className="ml-2 !rounded-full">
|
<Tag color='green' shape='circle' className="ml-2">
|
||||||
PostgreSQL
|
PostgreSQL
|
||||||
</Tag>
|
</Tag>
|
||||||
</div>
|
</div>
|
||||||
@@ -425,7 +425,7 @@ const Setup = () => {
|
|||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="font-medium text-gray-900 mb-1">{t('对外运营模式')}</div>
|
<div className="font-medium text-gray-900 mb-1">{t('对外运营模式')}</div>
|
||||||
<div className="text-sm text-gray-500">{t('适用于为多个用户提供服务的场景')}</div>
|
<div className="text-sm text-gray-500">{t('适用于为多个用户提供服务的场景')}</div>
|
||||||
<Tag color='blue' size='small' className="!rounded-full mt-2">
|
<Tag color='blue' shape='circle' className="mt-2">
|
||||||
{t('默认模式')}
|
{t('默认模式')}
|
||||||
</Tag>
|
</Tag>
|
||||||
</div>
|
</div>
|
||||||
@@ -443,7 +443,7 @@ const Setup = () => {
|
|||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="font-medium text-gray-900 mb-1">{t('自用模式')}</div>
|
<div className="font-medium text-gray-900 mb-1">{t('自用模式')}</div>
|
||||||
<div className="text-sm text-gray-500">{t('适用于个人使用的场景,不需要设置模型价格')}</div>
|
<div className="text-sm text-gray-500">{t('适用于个人使用的场景,不需要设置模型价格')}</div>
|
||||||
<Tag color='green' size='small' className="!rounded-full mt-2">
|
<Tag color='green' shape='circle' className="mt-2">
|
||||||
{t('无需计费')}
|
{t('无需计费')}
|
||||||
</Tag>
|
</Tag>
|
||||||
</div>
|
</div>
|
||||||
@@ -461,7 +461,7 @@ const Setup = () => {
|
|||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="font-medium text-gray-900 mb-1">{t('演示站点模式')}</div>
|
<div className="font-medium text-gray-900 mb-1">{t('演示站点模式')}</div>
|
||||||
<div className="text-sm text-gray-500">{t('适用于展示系统功能的场景,提供基础功能演示')}</div>
|
<div className="text-sm text-gray-500">{t('适用于展示系统功能的场景,提供基础功能演示')}</div>
|
||||||
<Tag color='purple' size='small' className="!rounded-full mt-2">
|
<Tag color='purple' shape='circle' className="mt-2">
|
||||||
{t('演示体验')}
|
{t('演示体验')}
|
||||||
</Tag>
|
</Tag>
|
||||||
</div>
|
</div>
|
||||||
@@ -522,8 +522,8 @@ const Setup = () => {
|
|||||||
<p>{t('默认模式,适用于为多个用户提供服务的场景。')}</p>
|
<p>{t('默认模式,适用于为多个用户提供服务的场景。')}</p>
|
||||||
<p>{t('此模式下,系统将计算每次调用的用量,您需要对每个模型都设置价格,如果没有设置价格,用户将无法使用该模型。')}</p>
|
<p>{t('此模式下,系统将计算每次调用的用量,您需要对每个模型都设置价格,如果没有设置价格,用户将无法使用该模型。')}</p>
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<Tag color='blue' className="!rounded-full mr-2">{t('计费模式')}</Tag>
|
<Tag color='blue' shape='circle' className="mr-2">{t('计费模式')}</Tag>
|
||||||
<Tag color='blue' className="!rounded-full">{t('多用户支持')}</Tag>
|
<Tag color='blue' shape='circle'>{t('多用户支持')}</Tag>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -542,8 +542,8 @@ const Setup = () => {
|
|||||||
<p>{t('适用于个人使用的场景。')}</p>
|
<p>{t('适用于个人使用的场景。')}</p>
|
||||||
<p>{t('不需要设置模型价格,系统将弱化用量计算,您可专注于使用模型。')}</p>
|
<p>{t('不需要设置模型价格,系统将弱化用量计算,您可专注于使用模型。')}</p>
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<Tag color='green' className="!rounded-full mr-2">{t('无需计费')}</Tag>
|
<Tag color='green' shape='circle' className="mr-2">{t('无需计费')}</Tag>
|
||||||
<Tag color='green' className="!rounded-full">{t('个人使用')}</Tag>
|
<Tag color='green' shape='circle'>{t('个人使用')}</Tag>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -562,8 +562,8 @@ const Setup = () => {
|
|||||||
<p>{t('适用于展示系统功能的场景。')}</p>
|
<p>{t('适用于展示系统功能的场景。')}</p>
|
||||||
<p>{t('提供基础功能演示,方便用户了解系统特性。')}</p>
|
<p>{t('提供基础功能演示,方便用户了解系统特性。')}</p>
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<Tag color='purple' className="!rounded-full mr-2">{t('功能演示')}</Tag>
|
<Tag color='purple' shape='circle' className="mr-2">{t('功能演示')}</Tag>
|
||||||
<Tag color='purple' className="!rounded-full">{t('体验试用')}</Tag>
|
<Tag color='purple' shape='circle'>{t('体验试用')}</Tag>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user