mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-11-10 18:43:41 +08:00
feat: i18n for token related pages
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
Button,
|
||||
Dropdown,
|
||||
@@ -21,64 +22,63 @@ import {
|
||||
import { ITEMS_PER_PAGE } from '../constants';
|
||||
import { renderQuota } from '../helpers/render';
|
||||
|
||||
const COPY_OPTIONS = [
|
||||
{ key: 'next', text: 'ChatGPT Next Web', value: 'next' },
|
||||
{ key: 'ama', text: 'BotGem', value: 'ama' },
|
||||
{ key: 'opencat', text: 'OpenCat', value: 'opencat' },
|
||||
{ key: 'lobechat', text: 'LobeChat', value: 'lobechat' },
|
||||
];
|
||||
|
||||
const OPEN_LINK_OPTIONS = [
|
||||
{ key: 'next', text: 'ChatGPT Next Web', value: 'next' },
|
||||
{ key: 'ama', text: 'BotGem', value: 'ama' },
|
||||
{ key: 'opencat', text: 'OpenCat', value: 'opencat' },
|
||||
{ key: 'lobechat', text: 'LobeChat', value: 'lobechat' },
|
||||
];
|
||||
|
||||
function renderTimestamp(timestamp) {
|
||||
return <>{timestamp2string(timestamp)}</>;
|
||||
}
|
||||
|
||||
function renderStatus(status) {
|
||||
function renderStatus(status, t) {
|
||||
switch (status) {
|
||||
case 1:
|
||||
return (
|
||||
<Label basic color='green'>
|
||||
已启用
|
||||
{t('token.table.status_enabled')}
|
||||
</Label>
|
||||
);
|
||||
case 2:
|
||||
return (
|
||||
<Label basic color='red'>
|
||||
{' '}
|
||||
已禁用{' '}
|
||||
{t('token.table.status_disabled')}
|
||||
</Label>
|
||||
);
|
||||
case 3:
|
||||
return (
|
||||
<Label basic color='yellow'>
|
||||
{' '}
|
||||
已过期{' '}
|
||||
{t('token.table.status_expired')}
|
||||
</Label>
|
||||
);
|
||||
case 4:
|
||||
return (
|
||||
<Label basic color='grey'>
|
||||
{' '}
|
||||
已耗尽{' '}
|
||||
{t('token.table.status_depleted')}
|
||||
</Label>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<Label basic color='black'>
|
||||
{' '}
|
||||
未知状态{' '}
|
||||
{t('token.table.status_unknown')}
|
||||
</Label>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const TokensTable = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const COPY_OPTIONS = [
|
||||
{ key: 'raw', text: t('token.copy_options.raw'), value: '' },
|
||||
{ key: 'next', text: t('token.copy_options.next'), value: 'next' },
|
||||
{ key: 'ama', text: t('token.copy_options.ama'), value: 'ama' },
|
||||
{ key: 'opencat', text: t('token.copy_options.opencat'), value: 'opencat' },
|
||||
{ key: 'lobe', text: t('token.copy_options.lobe'), value: 'lobechat' }
|
||||
];
|
||||
|
||||
const OPEN_LINK_OPTIONS = [
|
||||
{ key: 'next', text: t('token.copy_options.next'), value: 'next' },
|
||||
{ key: 'ama', text: t('token.copy_options.ama'), value: 'ama' },
|
||||
{ key: 'opencat', text: t('token.copy_options.opencat'), value: 'opencat' },
|
||||
{ key: 'lobe', text: t('token.copy_options.lobe'), value: 'lobechat' }
|
||||
];
|
||||
|
||||
const [tokens, setTokens] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [activePage, setActivePage] = useState(1);
|
||||
@@ -135,8 +135,7 @@ const TokensTable = () => {
|
||||
let nextUrl;
|
||||
|
||||
if (nextLink) {
|
||||
nextUrl =
|
||||
nextLink + `/#/?settings={"key":"sk-${key}","url":"${serverAddress}"}`;
|
||||
nextUrl = nextLink + `/#/?settings={"key":"sk-${key}","url":"${serverAddress}"}`;
|
||||
} else {
|
||||
nextUrl = `https://app.nextchat.dev/#/?settings={"key":"sk-${key}","url":"${serverAddress}"}`;
|
||||
}
|
||||
@@ -153,17 +152,15 @@ const TokensTable = () => {
|
||||
url = nextUrl;
|
||||
break;
|
||||
case 'lobechat':
|
||||
url =
|
||||
nextLink +
|
||||
`/?settings={"keyVaults":{"openai":{"apiKey":"sk-${key}","baseURL":"${serverAddress}/v1"}}}`;
|
||||
url = nextLink + `/?settings={"keyVaults":{"openai":{"apiKey":"sk-${key}","baseURL":"${serverAddress}/v1"}}}`;
|
||||
break;
|
||||
default:
|
||||
url = `sk-${key}`;
|
||||
}
|
||||
if (await copy(url)) {
|
||||
showSuccess('已复制到剪贴板!');
|
||||
showSuccess(t('token.messages.copy_success'));
|
||||
} else {
|
||||
showWarning('无法复制到剪贴板,请手动复制,已将令牌填入搜索框。');
|
||||
showWarning(t('token.messages.copy_failed'));
|
||||
setSearchKeyword(url);
|
||||
}
|
||||
};
|
||||
@@ -237,7 +234,7 @@ const TokensTable = () => {
|
||||
}
|
||||
const { success, message } = res.data;
|
||||
if (success) {
|
||||
showSuccess('操作成功完成!');
|
||||
showSuccess(t('token.messages.operation_success'));
|
||||
let token = res.data.data;
|
||||
let newTokens = [...tokens];
|
||||
let realIdx = (activePage - 1) * ITEMS_PER_PAGE + idx;
|
||||
@@ -308,7 +305,7 @@ const TokensTable = () => {
|
||||
icon='search'
|
||||
fluid
|
||||
iconPosition='left'
|
||||
placeholder='搜索令牌的名称 ...'
|
||||
placeholder={t('token.search')}
|
||||
value={searchKeyword}
|
||||
loading={searching}
|
||||
onChange={handleKeywordChange}
|
||||
@@ -324,7 +321,7 @@ const TokensTable = () => {
|
||||
sortToken('name');
|
||||
}}
|
||||
>
|
||||
名称
|
||||
{t('token.table.name')}
|
||||
</Table.HeaderCell>
|
||||
<Table.HeaderCell
|
||||
style={{ cursor: 'pointer' }}
|
||||
@@ -332,7 +329,7 @@ const TokensTable = () => {
|
||||
sortToken('status');
|
||||
}}
|
||||
>
|
||||
状态
|
||||
{t('token.table.status')}
|
||||
</Table.HeaderCell>
|
||||
<Table.HeaderCell
|
||||
style={{ cursor: 'pointer' }}
|
||||
@@ -340,7 +337,7 @@ const TokensTable = () => {
|
||||
sortToken('used_quota');
|
||||
}}
|
||||
>
|
||||
已用额度
|
||||
{t('token.table.used_quota')}
|
||||
</Table.HeaderCell>
|
||||
<Table.HeaderCell
|
||||
style={{ cursor: 'pointer' }}
|
||||
@@ -348,7 +345,7 @@ const TokensTable = () => {
|
||||
sortToken('remain_quota');
|
||||
}}
|
||||
>
|
||||
剩余额度
|
||||
{t('token.table.remain_quota')}
|
||||
</Table.HeaderCell>
|
||||
<Table.HeaderCell
|
||||
style={{ cursor: 'pointer' }}
|
||||
@@ -356,7 +353,7 @@ const TokensTable = () => {
|
||||
sortToken('created_time');
|
||||
}}
|
||||
>
|
||||
创建时间
|
||||
{t('token.table.created_time')}
|
||||
</Table.HeaderCell>
|
||||
<Table.HeaderCell
|
||||
style={{ cursor: 'pointer' }}
|
||||
@@ -364,9 +361,9 @@ const TokensTable = () => {
|
||||
sortToken('expired_time');
|
||||
}}
|
||||
>
|
||||
过期时间
|
||||
{t('token.table.expired_time')}
|
||||
</Table.HeaderCell>
|
||||
<Table.HeaderCell>操作</Table.HeaderCell>
|
||||
<Table.HeaderCell>{t('token.table.actions')}</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
|
||||
@@ -378,20 +375,37 @@ const TokensTable = () => {
|
||||
)
|
||||
.map((token, idx) => {
|
||||
if (token.deleted) return <></>;
|
||||
|
||||
const copyOptionsWithHandlers = COPY_OPTIONS.map(option => ({
|
||||
...option,
|
||||
onClick: async () => {
|
||||
await onCopy(option.value, token.key);
|
||||
}
|
||||
}));
|
||||
|
||||
const openLinkOptionsWithHandlers = OPEN_LINK_OPTIONS.map(option => ({
|
||||
...option,
|
||||
onClick: async () => {
|
||||
await onOpenLink(option.value, token.key);
|
||||
}
|
||||
}));
|
||||
|
||||
return (
|
||||
<Table.Row key={token.id}>
|
||||
<Table.Cell>{token.name ? token.name : '无'}</Table.Cell>
|
||||
<Table.Cell>{renderStatus(token.status)}</Table.Cell>
|
||||
<Table.Cell>{renderQuota(token.used_quota)}</Table.Cell>
|
||||
<Table.Cell>
|
||||
{token.name ? token.name : t('token.table.no_name')}
|
||||
</Table.Cell>
|
||||
<Table.Cell>{renderStatus(token.status, t)}</Table.Cell>
|
||||
<Table.Cell>{renderQuota(token.used_quota, t)}</Table.Cell>
|
||||
<Table.Cell>
|
||||
{token.unlimited_quota
|
||||
? '无限制'
|
||||
: renderQuota(token.remain_quota, 2)}
|
||||
? t('token.table.unlimited')
|
||||
: renderQuota(token.remain_quota, t, 2)}
|
||||
</Table.Cell>
|
||||
<Table.Cell>{renderTimestamp(token.created_time)}</Table.Cell>
|
||||
<Table.Cell>
|
||||
{token.expired_time === -1
|
||||
? '永不过期'
|
||||
? t('token.table.never_expire')
|
||||
: renderTimestamp(token.expired_time)}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
@@ -400,21 +414,14 @@ const TokensTable = () => {
|
||||
<Button
|
||||
size={'small'}
|
||||
positive
|
||||
onClick={async () => {
|
||||
await onCopy('', token.key);
|
||||
}}
|
||||
onClick={async () => await onCopy('', token.key)}
|
||||
>
|
||||
复制
|
||||
{t('token.buttons.copy')}
|
||||
</Button>
|
||||
<Dropdown
|
||||
className='button icon'
|
||||
floating
|
||||
options={COPY_OPTIONS.map((option) => ({
|
||||
...option,
|
||||
onClick: async () => {
|
||||
await onCopy(option.value, token.key);
|
||||
},
|
||||
}))}
|
||||
options={copyOptionsWithHandlers}
|
||||
trigger={<></>}
|
||||
/>
|
||||
</Button.Group>{' '}
|
||||
@@ -422,28 +429,21 @@ const TokensTable = () => {
|
||||
<Button
|
||||
size={'small'}
|
||||
positive
|
||||
onClick={() => {
|
||||
onOpenLink('', token.key);
|
||||
}}
|
||||
onClick={() => onOpenLink('', token.key)}
|
||||
>
|
||||
聊天
|
||||
{t('token.buttons.chat')}
|
||||
</Button>
|
||||
<Dropdown
|
||||
className='button icon'
|
||||
floating
|
||||
options={OPEN_LINK_OPTIONS.map((option) => ({
|
||||
...option,
|
||||
onClick: async () => {
|
||||
await onOpenLink(option.value, token.key);
|
||||
},
|
||||
}))}
|
||||
options={openLinkOptionsWithHandlers}
|
||||
trigger={<></>}
|
||||
/>
|
||||
</Button.Group>{' '}
|
||||
<Popup
|
||||
trigger={
|
||||
<Button size='small' negative>
|
||||
删除
|
||||
{t('token.buttons.delete')}
|
||||
</Button>
|
||||
}
|
||||
on='click'
|
||||
@@ -456,7 +456,7 @@ const TokensTable = () => {
|
||||
manageToken(token.id, 'delete', idx);
|
||||
}}
|
||||
>
|
||||
删除令牌 {token.name}
|
||||
{t('token.buttons.confirm_delete')} {token.name}
|
||||
</Button>
|
||||
</Popup>
|
||||
<Button
|
||||
@@ -469,14 +469,12 @@ const TokensTable = () => {
|
||||
);
|
||||
}}
|
||||
>
|
||||
{token.status === 1 ? '禁用' : '启用'}
|
||||
{token.status === 1
|
||||
? t('token.buttons.disable')
|
||||
: t('token.buttons.enable')}
|
||||
</Button>
|
||||
<Button
|
||||
size={'small'}
|
||||
as={Link}
|
||||
to={'/token/edit/' + token.id}
|
||||
>
|
||||
编辑
|
||||
<Button size={'small'} as={Link} to={'/token/edit/' + token.id}>
|
||||
{t('token.buttons.edit')}
|
||||
</Button>
|
||||
</div>
|
||||
</Table.Cell>
|
||||
@@ -489,24 +487,24 @@ const TokensTable = () => {
|
||||
<Table.Row>
|
||||
<Table.HeaderCell colSpan='7'>
|
||||
<Button size='small' as={Link} to='/token/add' loading={loading}>
|
||||
添加新的令牌
|
||||
{t('token.buttons.add')}
|
||||
</Button>
|
||||
<Button size='small' onClick={refresh} loading={loading}>
|
||||
刷新
|
||||
{t('token.buttons.refresh')}
|
||||
</Button>
|
||||
<Dropdown
|
||||
placeholder='排序方式'
|
||||
placeholder={t('token.sort.placeholder')}
|
||||
selection
|
||||
options={[
|
||||
{ key: '', text: '默认排序', value: '' },
|
||||
{ key: '', text: t('token.sort.default'), value: '' },
|
||||
{
|
||||
key: 'remain_quota',
|
||||
text: '按剩余额度排序',
|
||||
text: t('token.sort.by_remain'),
|
||||
value: 'remain_quota',
|
||||
},
|
||||
{
|
||||
key: 'used_quota',
|
||||
text: '按已用额度排序',
|
||||
text: t('token.sort.by_used'),
|
||||
value: 'used_quota',
|
||||
},
|
||||
]}
|
||||
|
||||
Reference in New Issue
Block a user