This commit is contained in:
2026-08-06 14:18:45 +08:00
parent dc3c4f7e4d
commit 395d979208
3 changed files with 34 additions and 9 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -28,7 +28,7 @@
} }
})(); })();
</script> </script>
<script type="module" crossorigin src="/assets/index-nOnojq6k.js"></script> <script type="module" crossorigin src="/assets/index-7k1wmlIX.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-D3fwIbOp.css"> <link rel="stylesheet" crossorigin href="/assets/index-D3fwIbOp.css">
</head> </head>
<body> <body>
+32 -7
View File
@@ -231,18 +231,39 @@ const AdminApiKeys: React.FC = () => {
const handleCopyKey = async (key: ApiKey) => { const handleCopyKey = async (key: ApiKey) => {
try { try {
const result = await revealApiKey(key.id); const result = await revealApiKey(key.id);
if (result?.apiKey) { const plainKey: string | undefined = result?.apiKey || result?.data?.apiKey;
await navigator.clipboard.writeText(result.apiKey); if (!plainKey) {
message.success('API Key 已复制到剪贴板');
} else {
message.error('获取 API Key 失败'); message.error('获取 API Key 失败');
return;
} }
// 优先用 Clipboard API,不支持时回退到 execCommand
if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
try {
await navigator.clipboard.writeText(plainKey);
} catch {
fallbackCopy(plainKey);
}
} else {
fallbackCopy(plainKey);
}
message.success('API Key 已复制到剪贴板');
} catch (e: any) { } catch (e: any) {
const msg = e?.response?.data?.detail || '复制失败'; const msg = e?.response?.data?.detail || '复制失败';
message.error(msg); message.error(msg);
} }
}; };
const fallbackCopy = (text: string) => {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
};
const viewUsage = async (key: ApiKey) => { const viewUsage = async (key: ApiKey) => {
try { try {
const usage = await getApiKeyUsage(key.id, 30); const usage = await getApiKeyUsage(key.id, 30);
@@ -305,8 +326,13 @@ const AdminApiKeys: React.FC = () => {
{ {
title: 'Key 前缀', title: 'Key 前缀',
dataIndex: 'apiKeyPrefix', dataIndex: 'apiKeyPrefix',
width: 110, width: 180,
render: (v: string) => <code style={{ background: '#f5f5f5', padding: '2px 6px', borderRadius: 4 }}>{v}****</code>, render: (v: string, r: ApiKey) => (
<Space size={4}>
<code style={{ background: '#f5f5f5', padding: '2px 6px', borderRadius: 4 }}>{v}****</code>
<Button type="link" size="small" icon={<CopyOutlined />} onClick={() => handleCopyKey(r)}>api-key</Button>
</Space>
),
}, },
{ {
title: '配额(元)', title: '配额(元)',
@@ -355,7 +381,6 @@ const AdminApiKeys: React.FC = () => {
width: 260, width: 260,
render: (_: any, r: ApiKey) => ( render: (_: any, r: ApiKey) => (
<Space size={0}> <Space size={0}>
<Button type="link" size="small" icon={<CopyOutlined />} onClick={() => handleCopyKey(r)}></Button>
<Button type="link" size="small" icon={<EyeOutlined />} onClick={() => viewUsage(r)}></Button> <Button type="link" size="small" icon={<EyeOutlined />} onClick={() => viewUsage(r)}></Button>
<Button type="link" size="small" icon={<EditOutlined />} onClick={() => openEdit(r)}></Button> <Button type="link" size="small" icon={<EditOutlined />} onClick={() => openEdit(r)}></Button>
<Popconfirm title="确定删除?" onConfirm={() => handleDelete(r.id)}> <Popconfirm title="确定删除?" onConfirm={() => handleDelete(r.id)}>