import React from 'react';
import {
Button,
Dropdown,
Space,
SplitButtonGroup,
Tag,
AvatarGroup,
Avatar,
Tooltip,
Progress,
Switch,
Input,
Modal
} from '@douyinfe/semi-ui';
import {
timestamp2string,
renderGroup,
renderQuota,
getModelCategories,
showError
} from '../../../helpers';
import {
IconTreeTriangleDown,
IconCopy,
IconEyeOpened,
IconEyeClosed,
} from '@douyinfe/semi-icons';
// Render functions
function renderTimestamp(timestamp) {
return <>{timestamp2string(timestamp)}>;
}
// Render status column with switch and progress bar
const renderStatus = (text, record, manageToken, t) => {
const enabled = text === 1;
const handleToggle = (checked) => {
if (checked) {
manageToken(record.id, 'enable', record);
} else {
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('已耗尽');
}
const used = parseInt(record.used_quota) || 0;
const remain = parseInt(record.remain_quota) || 0;
const total = used + remain;
const percent = total > 0 ? (remain / total) * 100 : 0;
const getProgressColor = (pct) => {
if (pct === 100) return 'var(--semi-color-success)';
if (pct <= 10) return 'var(--semi-color-danger)';
if (pct <= 30) return 'var(--semi-color-warning)';
return undefined;
};
const quotaSuffix = record.unlimited_quota ? (
{t('无限额度')}
) : (
{`${renderQuota(remain)} / ${renderQuota(total)}`}
);
const content = (
}
suffixIcon={quotaSuffix}
>
{tagText}
);
if (record.unlimited_quota) {
return content;
}
return (
{t('已用额度')}: {renderQuota(used)}
{t('剩余额度')}: {renderQuota(remain)} ({percent.toFixed(0)}%)
{t('总额度')}: {renderQuota(total)}
}
>
{content}
);
};
// Render group column
const renderGroupColumn = (text, t) => {
if (text === 'auto') {
return (
{t('智能熔断')}
);
}
return renderGroup(text);
};
// Render token key column with show/hide and copy functionality
const renderTokenKey = (text, record, showKeys, setShowKeys, copyText) => {
const fullKey = 'sk-' + record.key;
const maskedKey = 'sk-' + record.key.slice(0, 4) + '**********' + record.key.slice(-4);
const revealed = !!showKeys[record.id];
return (
: }
aria-label='toggle token visibility'
onClick={(e) => {
e.stopPropagation();
setShowKeys(prev => ({ ...prev, [record.id]: !revealed }));
}}
/>
}
aria-label='copy token key'
onClick={async (e) => {
e.stopPropagation();
await copyText(fullKey);
}}
/>
}
/>
);
};
// Render model limits column
const renderModelLimits = (text, record, t) => {
if (record.model_limits_enabled && text) {
const models = text.split(',').filter(Boolean);
const categories = getModelCategories(t);
const vendorAvatars = [];
const matchedModels = new Set();
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(
{category.icon}
);
vendorModels.forEach((m) => matchedModels.add(m));
}
});
const unmatchedModels = models.filter((m) => !matchedModels.has(m));
if (unmatchedModels.length > 0) {
vendorAvatars.push(
{t('其他')}
);
}
return (
{vendorAvatars}
);
} else {
return (
{t('无限制')}
);
}
};
// Render IP restrictions column
const renderAllowIps = (text, t) => {
if (!text || text.trim() === '') {
return (
{t('无限制')}
);
}
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) => (
{ip}
));
if (extraCount > 0) {
ipTags.push(
{'+' + extraCount}
);
}
return {ipTags};
};
// Render operations column
const renderOperations = (text, record, onOpenLink, setEditingToken, setShowEdit, manageToken, refresh, t) => {
let chats = localStorage.getItem('chats');
let chatsArray = [];
let shouldUseCustom = true;
if (shouldUseCustom) {
try {
chats = JSON.parse(chats);
if (Array.isArray(chats)) {
for (let i = 0; i < chats.length; i++) {
let chat = {};
chat.node = 'item';
for (let key in chats[i]) {
if (chats[i].hasOwnProperty(key)) {
chat.key = i;
chat.name = key;
chat.onClick = () => {
onOpenLink(key, chats[i][key], record);
};
}
}
chatsArray.push(chat);
}
}
} catch (e) {
console.log(e);
showError(t('聊天链接配置错误,请联系管理员'));
}
}
return (
}
size="small"
>
);
};
export const getTokensColumns = ({
t,
showKeys,
setShowKeys,
copyText,
manageToken,
onOpenLink,
setEditingToken,
setShowEdit,
refresh,
}) => {
return [
{
title: t('名称'),
dataIndex: 'name',
},
{
title: t('状态'),
dataIndex: 'status',
key: 'status',
render: (text, record) => renderStatus(text, record, manageToken, t),
},
{
title: t('分组'),
dataIndex: 'group',
key: 'group',
render: (text) => renderGroupColumn(text, t),
},
{
title: t('密钥'),
key: 'token_key',
render: (text, record) => renderTokenKey(text, record, showKeys, setShowKeys, copyText),
},
{
title: t('可用模型'),
dataIndex: 'model_limits',
render: (text, record) => renderModelLimits(text, record, t),
},
{
title: t('IP限制'),
dataIndex: 'allow_ips',
render: (text) => renderAllowIps(text, t),
},
{
title: t('创建时间'),
dataIndex: 'created_time',
render: (text, record, index) => {
return {renderTimestamp(text)}
;
},
},
{
title: t('过期时间'),
dataIndex: 'expired_time',
render: (text, record, index) => {
return (
{record.expired_time === -1 ? t('永不过期') : renderTimestamp(text)}
);
},
},
{
title: '',
dataIndex: 'operate',
fixed: 'right',
render: (text, record, index) => renderOperations(
text,
record,
onOpenLink,
setEditingToken,
setShowEdit,
manageToken,
refresh,
t
),
},
];
};