🎨 chore(web): apply ESLint and Prettier auto-fixes (baseline)

- Ran: bun run eslint:fix && bun run lint:fix
- Inserted AGPL license header via eslint-plugin-header
- Enforced no-multiple-empty-lines and other lint rules
- Formatted code using Prettier v3 (@so1ve/prettier-config)
- No functional changes; formatting-only baseline across JS/JSX files
This commit is contained in:
t0ng7u
2025-08-30 21:15:10 +08:00
parent 41cf516ec5
commit 0d57b1acd4
274 changed files with 11025 additions and 7659 deletions

View File

@@ -62,35 +62,35 @@ const TokensActions = ({
return (
<>
<div className="flex flex-wrap gap-2 w-full md:w-auto order-2 md:order-1">
<div className='flex flex-wrap gap-2 w-full md:w-auto order-2 md:order-1'>
<Button
type="primary"
className="flex-1 md:flex-initial"
type='primary'
className='flex-1 md:flex-initial'
onClick={() => {
setEditingToken({
id: undefined,
});
setShowEdit(true);
}}
size="small"
size='small'
>
{t('添加令牌')}
</Button>
<Button
type='tertiary'
className="flex-1 md:flex-initial"
className='flex-1 md:flex-initial'
onClick={handleCopySelectedTokens}
size="small"
size='small'
>
{t('复制所选令牌')}
</Button>
<Button
type='danger'
className="w-full md:w-auto"
className='w-full md:w-auto'
onClick={handleDeleteSelectedTokens}
size="small"
size='small'
>
{t('删除所选令牌')}
</Button>
@@ -115,4 +115,4 @@ const TokensActions = ({
);
};
export default TokensActions;
export default TokensActions;

View File

@@ -31,14 +31,14 @@ import {
Popover,
Typography,
Input,
Modal
Modal,
} from '@douyinfe/semi-ui';
import {
timestamp2string,
renderGroup,
renderQuota,
getModelCategories,
showError
showError,
} from '../../../helpers';
import {
IconTreeTriangleDown,
@@ -92,10 +92,15 @@ const renderGroupColumn = (text, t) => {
if (text === 'auto') {
return (
<Tooltip
content={t('当前分组为 auto会自动选择最优分组当一个组不可用时自动降级到下一个组熔断机制')}
content={t(
'当前分组为 auto会自动选择最优分组当一个组不可用时自动降级到下一个组熔断机制',
)}
position='top'
>
<Tag color='white' shape='circle'> {t('智能熔断')} </Tag>
<Tag color='white' shape='circle'>
{' '}
{t('智能熔断')}{' '}
</Tag>
</Tooltip>
);
}
@@ -105,7 +110,8 @@ const renderGroupColumn = (text, t) => {
// 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 maskedKey =
'sk-' + record.key.slice(0, 4) + '**********' + record.key.slice(-4);
const revealed = !!showKeys[record.id];
return (
@@ -124,7 +130,7 @@ const renderTokenKey = (text, record, showKeys, setShowKeys, copyText) => {
aria-label='toggle token visibility'
onClick={(e) => {
e.stopPropagation();
setShowKeys(prev => ({ ...prev, [record.id]: !revealed }));
setShowKeys((prev) => ({ ...prev, [record.id]: !revealed }));
}}
/>
<Button
@@ -156,14 +162,25 @@ const renderModelLimits = (text, record, t) => {
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 }));
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'>
<Tooltip
key={key}
content={vendorModels.join(', ')}
position='top'
showArrow
>
<Avatar
size='extra-extra-small'
alt={category.label}
color='transparent'
>
{category.icon}
</Avatar>
</Tooltip>
</Tooltip>,
);
vendorModels.forEach((m) => matchedModels.add(m));
}
@@ -172,19 +189,20 @@ const renderModelLimits = (text, record, t) => {
const unmatchedModels = models.filter((m) => !matchedModels.has(m));
if (unmatchedModels.length > 0) {
vendorAvatars.push(
<Tooltip key='unknown' content={unmatchedModels.join(', ')} position='top' showArrow>
<Tooltip
key='unknown'
content={unmatchedModels.join(', ')}
position='top'
showArrow
>
<Avatar size='extra-extra-small' alt='unknown'>
{t('其他')}
</Avatar>
</Tooltip>
</Tooltip>,
);
}
return (
<AvatarGroup size='extra-extra-small'>
{vendorAvatars}
</AvatarGroup>
);
return <AvatarGroup size='extra-extra-small'>{vendorAvatars}</AvatarGroup>;
} else {
return (
<Tag color='white' shape='circle'>
@@ -226,10 +244,8 @@ const renderAllowIps = (text, t) => {
position='top'
showArrow
>
<Tag shape='circle'>
{'+' + extraCount}
</Tag>
</Tooltip>
<Tag shape='circle'>{'+' + extraCount}</Tag>
</Tooltip>,
);
}
@@ -291,7 +307,16 @@ const renderQuotaUsage = (text, record, t) => {
};
// Render operations column
const renderOperations = (text, record, onOpenLink, setEditingToken, setShowEdit, manageToken, refresh, t) => {
const renderOperations = (
text,
record,
onOpenLink,
setEditingToken,
setShowEdit,
manageToken,
refresh,
t,
) => {
let chatsArray = [];
try {
const raw = localStorage.getItem('chats');
@@ -317,11 +342,11 @@ const renderOperations = (text, record, onOpenLink, setEditingToken, setShowEdit
return (
<Space wrap>
<SplitButtonGroup
className="overflow-hidden"
className='overflow-hidden'
aria-label={t('项目操作按钮组')}
>
<Button
size="small"
size='small'
type='tertiary'
onClick={() => {
if (chatsArray.length === 0) {
@@ -334,15 +359,11 @@ const renderOperations = (text, record, onOpenLink, setEditingToken, setShowEdit
>
{t('聊天')}
</Button>
<Dropdown
trigger='click'
position='bottomRight'
menu={chatsArray}
>
<Dropdown trigger='click' position='bottomRight' menu={chatsArray}>
<Button
type='tertiary'
icon={<IconTreeTriangleDown />}
size="small"
size='small'
></Button>
</Dropdown>
</SplitButtonGroup>
@@ -350,7 +371,7 @@ const renderOperations = (text, record, onOpenLink, setEditingToken, setShowEdit
{record.status === 1 ? (
<Button
type='danger'
size="small"
size='small'
onClick={async () => {
await manageToken(record.id, 'disable', record);
await refresh();
@@ -360,7 +381,7 @@ const renderOperations = (text, record, onOpenLink, setEditingToken, setShowEdit
</Button>
) : (
<Button
size="small"
size='small'
onClick={async () => {
await manageToken(record.id, 'enable', record);
await refresh();
@@ -372,7 +393,7 @@ const renderOperations = (text, record, onOpenLink, setEditingToken, setShowEdit
<Button
type='tertiary'
size="small"
size='small'
onClick={() => {
setEditingToken(record);
setShowEdit(true);
@@ -383,7 +404,7 @@ const renderOperations = (text, record, onOpenLink, setEditingToken, setShowEdit
<Button
type='danger'
size="small"
size='small'
onClick={() => {
Modal.confirm({
title: t('确定是否要删除此令牌?'),
@@ -439,7 +460,8 @@ export const getTokensColumns = ({
{
title: t('密钥'),
key: 'token_key',
render: (text, record) => renderTokenKey(text, record, showKeys, setShowKeys, copyText),
render: (text, record) =>
renderTokenKey(text, record, showKeys, setShowKeys, copyText),
},
{
title: t('可用模型'),
@@ -473,16 +495,17 @@ export const getTokensColumns = ({
title: '',
dataIndex: 'operate',
fixed: 'right',
render: (text, record, index) => renderOperations(
text,
record,
onOpenLink,
setEditingToken,
setShowEdit,
manageToken,
refresh,
t
),
render: (text, record, index) =>
renderOperations(
text,
record,
onOpenLink,
setEditingToken,
setShowEdit,
manageToken,
refresh,
t,
),
},
];
};
};

View File

@@ -26,9 +26,9 @@ const { Text } = Typography;
const TokensDescription = ({ compactMode, setCompactMode, t }) => {
return (
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-2 w-full">
<div className="flex items-center text-blue-500">
<Key size={16} className="mr-2" />
<div className='flex flex-col md:flex-row justify-between items-start md:items-center gap-2 w-full'>
<div className='flex items-center text-blue-500'>
<Key size={16} className='mr-2' />
<Text>{t('令牌管理')}</Text>
</div>
@@ -41,4 +41,4 @@ const TokensDescription = ({ compactMode, setCompactMode, t }) => {
);
};
export default TokensDescription;
export default TokensDescription;

View File

@@ -49,42 +49,42 @@ const TokensFilters = ({
}}
onSubmit={searchTokens}
allowEmpty={true}
autoComplete="off"
layout="horizontal"
trigger="change"
autoComplete='off'
layout='horizontal'
trigger='change'
stopValidateWithError={false}
className="w-full md:w-auto order-1 md:order-2"
className='w-full md:w-auto order-1 md:order-2'
>
<div className="flex flex-col md:flex-row items-center gap-2 w-full md:w-auto">
<div className="relative w-full md:w-56">
<div className='flex flex-col md:flex-row items-center gap-2 w-full md:w-auto'>
<div className='relative w-full md:w-56'>
<Form.Input
field="searchKeyword"
field='searchKeyword'
prefix={<IconSearch />}
placeholder={t('搜索关键字')}
showClear
pure
size="small"
size='small'
/>
</div>
<div className="relative w-full md:w-56">
<div className='relative w-full md:w-56'>
<Form.Input
field="searchToken"
field='searchToken'
prefix={<IconSearch />}
placeholder={t('密钥')}
showClear
pure
size="small"
size='small'
/>
</div>
<div className="flex gap-2 w-full md:w-auto">
<div className='flex gap-2 w-full md:w-auto'>
<Button
type="tertiary"
htmlType="submit"
type='tertiary'
htmlType='submit'
loading={loading || searching}
className="flex-1 md:flex-initial md:w-auto"
size="small"
className='flex-1 md:flex-initial md:w-auto'
size='small'
>
{t('查询')}
</Button>
@@ -92,8 +92,8 @@ const TokensFilters = ({
<Button
type='tertiary'
onClick={handleReset}
className="flex-1 md:flex-initial md:w-auto"
size="small"
className='flex-1 md:flex-initial md:w-auto'
size='small'
>
{t('重置')}
</Button>
@@ -103,4 +103,4 @@ const TokensFilters = ({
);
};
export default TokensFilters;
export default TokensFilters;

View File

@@ -76,13 +76,15 @@ const TokensTable = (tokensData) => {
// Handle compact mode by removing fixed positioning
const tableColumns = useMemo(() => {
return compactMode ? columns.map(col => {
if (col.dataIndex === 'operate') {
const { fixed, ...rest } = col;
return rest;
}
return col;
}) : columns;
return compactMode
? columns.map((col) => {
if (col.dataIndex === 'operate') {
const { fixed, ...rest } = col;
return rest;
}
return col;
})
: columns;
}, [compactMode, columns]);
return (
@@ -106,15 +108,17 @@ const TokensTable = (tokensData) => {
empty={
<Empty
image={<IllustrationNoResult style={{ width: 150, height: 150 }} />}
darkModeImage={<IllustrationNoResultDark style={{ width: 150, height: 150 }} />}
darkModeImage={
<IllustrationNoResultDark style={{ width: 150, height: 150 }} />
}
description={t('搜索无结果')}
style={{ padding: 30 }}
/>
}
className="rounded-xl overflow-hidden"
size="middle"
className='rounded-xl overflow-hidden'
size='middle'
/>
);
};
export default TokensTable;
export default TokensTable;

View File

@@ -18,8 +18,20 @@ For commercial licensing, please contact support@quantumnous.com
*/
import React, { useEffect, useRef, useState } from 'react';
import { Notification, Button, Space, Toast, Typography, Select } from '@douyinfe/semi-ui';
import { API, showError, getModelCategories, selectFilter } from '../../../helpers';
import {
Notification,
Button,
Space,
Toast,
Typography,
Select,
} from '@douyinfe/semi-ui';
import {
API,
showError,
getModelCategories,
selectFilter,
} from '../../../helpers';
import CardPro from '../../common/ui/CardPro';
import TokensTable from './TokensTable';
import TokensActions from './TokensActions';
@@ -33,9 +45,17 @@ import { createCardProPagination } from '../../../helpers/utils';
function TokensPage() {
// Define the function first, then pass it into the hook to avoid TDZ errors
const openFluentNotificationRef = useRef(null);
const tokensData = useTokensData((key) => openFluentNotificationRef.current?.(key));
const tokensData = useTokensData((key) =>
openFluentNotificationRef.current?.(key),
);
const isMobile = useIsMobile();
const latestRef = useRef({ tokens: [], selectedKeys: [], t: (k) => k, selectedModel: '', prefillKey: '' });
const latestRef = useRef({
tokens: [],
selectedKeys: [],
t: (k) => k,
selectedModel: '',
prefillKey: '',
});
const [modelOptions, setModelOptions] = useState([]);
const [selectedModel, setSelectedModel] = useState('');
const [fluentNoticeOpen, setFluentNoticeOpen] = useState(false);
@@ -50,7 +70,13 @@ function TokensPage() {
selectedModel,
prefillKey,
};
}, [tokensData.tokens, tokensData.selectedKeys, tokensData.t, selectedModel, prefillKey]);
}, [
tokensData.tokens,
tokensData.selectedKeys,
tokensData.t,
selectedModel,
prefillKey,
]);
const loadModels = async () => {
try {
@@ -68,7 +94,7 @@ function TokensPage() {
}
return {
label: (
<span className="flex items-center gap-1">
<span className='flex items-center gap-1'>
{icon}
{model}
</span>
@@ -90,7 +116,7 @@ function TokensPage() {
const SUPPRESS_KEY = 'fluent_notify_suppressed';
if (modelOptions.length === 0) {
// fire-and-forget; a later effect will refresh the notice content
loadModels()
loadModels();
}
if (!key && localStorage.getItem(SUPPRESS_KEY) === '1') return;
const container = document.getElementById('fluent-new-api-container');
@@ -123,19 +149,29 @@ function TokensPage() {
/>
</div>
<Space>
<Button theme="solid" type="primary" onClick={handlePrefillToFluent}>
<Button
theme='solid'
type='primary'
onClick={handlePrefillToFluent}
>
{t('一键填充到 FluentRead')}
</Button>
{!key && (
<Button type="warning" onClick={() => {
localStorage.setItem(SUPPRESS_KEY, '1');
Notification.close('fluent-detected');
Toast.info(t('已关闭后续提醒'));
}}>
<Button
type='warning'
onClick={() => {
localStorage.setItem(SUPPRESS_KEY, '1');
Notification.close('fluent-detected');
Toast.info(t('已关闭后续提醒'));
}}
>
{t('不再提醒')}
</Button>
)}
<Button type="tertiary" onClick={() => Notification.close('fluent-detected')}>
<Button
type='tertiary'
onClick={() => Notification.close('fluent-detected')}
>
{t('关闭')}
</Button>
</Space>
@@ -149,7 +185,13 @@ function TokensPage() {
// Prefill to Fluent handler
const handlePrefillToFluent = () => {
const { tokens, selectedKeys, t, selectedModel: chosenModel, prefillKey: overrideKey } = latestRef.current;
const {
tokens,
selectedKeys,
t,
selectedModel: chosenModel,
prefillKey: overrideKey,
} = latestRef.current;
const container = document.getElementById('fluent-new-api-container');
if (!container) {
Toast.error(t('未检测到 Fluent 容器'));
@@ -167,7 +209,7 @@ function TokensPage() {
try {
status = JSON.parse(status);
serverAddress = status.server_address || '';
} catch (_) { }
} catch (_) {}
}
if (!serverAddress) serverAddress = window.location.origin;
@@ -175,9 +217,12 @@ function TokensPage() {
if (overrideKey) {
apiKeyToUse = 'sk-' + overrideKey;
} else {
const token = (selectedKeys && selectedKeys.length === 1)
? selectedKeys[0]
: (tokens && tokens.length > 0 ? tokens[0] : null);
const token =
selectedKeys && selectedKeys.length === 1
? selectedKeys[0]
: tokens && tokens.length > 0
? tokens[0]
: null;
if (!token) {
Toast.warning(t('没有可用令牌用于填充'));
return;
@@ -192,7 +237,9 @@ function TokensPage() {
model: chosenModel,
};
container.dispatchEvent(new CustomEvent('fluent:prefill', { detail: payload }));
container.dispatchEvent(
new CustomEvent('fluent:prefill', { detail: payload }),
);
Toast.success(t('已发送到 Fluent'));
Notification.close('fluent-detected');
};
@@ -230,13 +277,18 @@ function TokensPage() {
const existing = document.querySelector(selector);
if (existing) {
console.log('Fluent container detected (initial):', existing);
window.dispatchEvent(new CustomEvent('fluent-container:appeared', { detail: existing }));
window.dispatchEvent(
new CustomEvent('fluent-container:appeared', { detail: existing }),
);
}
const isOrContainsTarget = (node) => {
if (!(node && node.nodeType === 1)) return false;
if (node.id === 'fluent-new-api-container') return true;
return typeof node.querySelector === 'function' && !!node.querySelector(selector);
return (
typeof node.querySelector === 'function' &&
!!node.querySelector(selector)
);
};
const observer = new MutationObserver((mutations) => {
@@ -247,7 +299,9 @@ function TokensPage() {
const el = document.querySelector(selector);
if (el) {
console.log('Fluent container appeared:', el);
window.dispatchEvent(new CustomEvent('fluent-container:appeared', { detail: el }));
window.dispatchEvent(
new CustomEvent('fluent-container:appeared', { detail: el }),
);
}
break;
}
@@ -310,7 +364,7 @@ function TokensPage() {
/>
<CardPro
type="type1"
type='type1'
descriptionArea={
<TokensDescription
compactMode={compactMode}
@@ -319,7 +373,7 @@ function TokensPage() {
/>
}
actionsArea={
<div className="flex flex-col md:flex-row justify-between items-center gap-2 w-full">
<div className='flex flex-col md:flex-row justify-between items-center gap-2 w-full'>
<TokensActions
selectedKeys={selectedKeys}
setEditingToken={setEditingToken}
@@ -330,7 +384,7 @@ function TokensPage() {
t={t}
/>
<div className="w-full md:w-full lg:w-auto order-1 md:order-2">
<div className='w-full md:w-full lg:w-auto order-1 md:order-2'>
<TokensFilters
formInitValues={formInitValues}
setFormApi={setFormApi}
@@ -359,4 +413,4 @@ function TokensPage() {
);
}
export default TokensPage;
export default TokensPage;

View File

@@ -49,17 +49,10 @@ const CopyTokensModal = ({ visible, onCancel, selectedKeys, copyText, t }) => {
onCancel={onCancel}
footer={
<Space>
<Button
type='tertiary'
onClick={handleCopyWithName}
>
<Button type='tertiary' onClick={handleCopyWithName}>
{t('名称+密钥')}
</Button>
<Button
onClick={handleCopyKeyOnly}
>
{t('仅密钥')}
</Button>
<Button onClick={handleCopyKeyOnly}>{t('仅密钥')}</Button>
</Space>
}
>
@@ -68,4 +61,4 @@ const CopyTokensModal = ({ visible, onCancel, selectedKeys, copyText, t }) => {
);
};
export default CopyTokensModal;
export default CopyTokensModal;

View File

@@ -20,20 +20,28 @@ For commercial licensing, please contact support@quantumnous.com
import React from 'react';
import { Modal } from '@douyinfe/semi-ui';
const DeleteTokensModal = ({ visible, onCancel, onConfirm, selectedKeys, t }) => {
const DeleteTokensModal = ({
visible,
onCancel,
onConfirm,
selectedKeys,
t,
}) => {
return (
<Modal
title={t('批量删除令牌')}
visible={visible}
onCancel={onCancel}
onOk={onConfirm}
type="warning"
type='warning'
>
<div>
{t('确定要删除所选的 {{count}} 个令牌吗?', { count: selectedKeys.length })}
{t('确定要删除所选的 {{count}} 个令牌吗?', {
count: selectedKeys.length,
})}
</div>
</Modal>
);
};
export default DeleteTokensModal;
export default DeleteTokensModal;

View File

@@ -111,7 +111,7 @@ const EditTokenModal = (props) => {
}
return {
label: (
<span className="flex items-center gap-1">
<span className='flex items-center gap-1'>
{icon}
{model}
</span>
@@ -239,7 +239,8 @@ const EditTokenModal = (props) => {
let successCount = 0;
for (let i = 0; i < count; i++) {
let { tokenCount: _tc, ...localInputs } = values;
const baseName = values.name.trim() === '' ? 'default' : values.name.trim();
const baseName =
values.name.trim() === '' ? 'default' : values.name.trim();
if (i !== 0 || values.name.trim() === '') {
localInputs.name = `${baseName}-${generateRandomSuffix()}`;
} else {
@@ -343,7 +344,9 @@ const EditTokenModal = (props) => {
</Avatar>
<div>
<Text className='text-lg font-medium'>{t('基本信息')}</Text>
<div className='text-xs text-gray-600'>{t('设置令牌的基本信息')}</div>
<div className='text-xs text-gray-600'>
{t('设置令牌的基本信息')}
</div>
</div>
</div>
<Row gutter={12}>
@@ -387,13 +390,16 @@ const EditTokenModal = (props) => {
{
validator: (rule, value) => {
// 允许 -1 表示永不过期,也允许空值在必填校验时被拦截
if (value === -1 || !value) return Promise.resolve();
if (value === -1 || !value)
return Promise.resolve();
const time = Date.parse(value);
if (isNaN(time)) {
return Promise.reject(t('过期时间格式错误!'));
}
if (time <= Date.now()) {
return Promise.reject(t('过期时间不能早于当前时间!'));
return Promise.reject(
t('过期时间不能早于当前时间!'),
);
}
return Promise.resolve();
},
@@ -444,7 +450,9 @@ const EditTokenModal = (props) => {
label={t('新建数量')}
min={1}
extraText={t('批量创建时会在名称后自动添加随机后缀')}
rules={[{ required: true, message: t('请输入新建数量') }]}
rules={[
{ required: true, message: t('请输入新建数量') },
]}
style={{ width: '100%' }}
/>
</Col>
@@ -460,7 +468,9 @@ const EditTokenModal = (props) => {
</Avatar>
<div>
<Text className='text-lg font-medium'>{t('额度设置')}</Text>
<div className='text-xs text-gray-600'>{t('设置令牌可用额度和数量')}</div>
<div className='text-xs text-gray-600'>
{t('设置令牌可用额度和数量')}
</div>
</div>
</div>
<Row gutter={12}>
@@ -472,7 +482,11 @@ const EditTokenModal = (props) => {
type='number'
disabled={values.unlimited_quota}
extraText={renderQuotaWithPrompt(values.remain_quota)}
rules={values.unlimited_quota ? [] : [{ required: true, message: t('请输入额度') }]}
rules={
values.unlimited_quota
? []
: [{ required: true, message: t('请输入额度') }]
}
data={[
{ value: 500000, label: '1$' },
{ value: 5000000, label: '10$' },
@@ -488,7 +502,9 @@ const EditTokenModal = (props) => {
field='unlimited_quota'
label={t('无限额度')}
size='large'
extraText={t('令牌的额度仅用于限制令牌本身的最大额度使用量,实际的使用受到账户的剩余额度限制')}
extraText={t(
'令牌的额度仅用于限制令牌本身的最大额度使用量,实际的使用受到账户的剩余额度限制',
)}
/>
</Col>
</Row>
@@ -497,12 +513,18 @@ const EditTokenModal = (props) => {
{/* 访问限制 */}
<Card className='!rounded-2xl shadow-sm border-0'>
<div className='flex items-center mb-2'>
<Avatar size='small' color='purple' className='mr-2 shadow-md'>
<Avatar
size='small'
color='purple'
className='mr-2 shadow-md'
>
<IconLink size={16} />
</Avatar>
<div>
<Text className='text-lg font-medium'>{t('访问限制')}</Text>
<div className='text-xs text-gray-600'>{t('设置令牌的访问限制')}</div>
<div className='text-xs text-gray-600'>
{t('设置令牌的访问限制')}
</div>
</div>
</div>
<Row gutter={12}>
@@ -510,7 +532,9 @@ const EditTokenModal = (props) => {
<Form.Select
field='model_limits'
label={t('模型限制列表')}
placeholder={t('请选择该令牌支持的模型,留空支持所有模型')}
placeholder={t(
'请选择该令牌支持的模型,留空支持所有模型',
)}
multiple
optionList={models}
extraText={t('非必要,不建议启用模型限制')}
@@ -543,4 +567,4 @@ const EditTokenModal = (props) => {
);
};
export default EditTokenModal;
export default EditTokenModal;