mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-10-15 07:20:26 +00:00
feat: i18n support
This commit is contained in:
@@ -240,5 +240,36 @@
|
|||||||
"display_short": "${{amount}}",
|
"display_short": "${{amount}}",
|
||||||
"unit": "$"
|
"unit": "$"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"redemption": {
|
||||||
|
"title": "Redemption Management",
|
||||||
|
"edit": {
|
||||||
|
"title_edit": "Update Redemption Code",
|
||||||
|
"title_create": "Create New Redemption Code",
|
||||||
|
"name": "Name",
|
||||||
|
"name_placeholder": "Please enter name",
|
||||||
|
"quota": "Quota",
|
||||||
|
"quota_placeholder": "Please enter quota per redemption code",
|
||||||
|
"count": "Generate Count",
|
||||||
|
"count_placeholder": "Please enter number of codes to generate",
|
||||||
|
"buttons": {
|
||||||
|
"submit": "Submit",
|
||||||
|
"cancel": "Cancel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"log": {
|
||||||
|
"title": "Operation Log",
|
||||||
|
"usage_details": "Usage Details",
|
||||||
|
"total_quota": "Total Quota Used",
|
||||||
|
"click_to_view": "Click to View",
|
||||||
|
"table": {
|
||||||
|
"id": "ID",
|
||||||
|
"username": "Username",
|
||||||
|
"type": "Type",
|
||||||
|
"content": "Content",
|
||||||
|
"amount": "Amount",
|
||||||
|
"time": "Time"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -240,5 +240,36 @@
|
|||||||
"display_short": "${{amount}}",
|
"display_short": "${{amount}}",
|
||||||
"unit": "$"
|
"unit": "$"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"redemption": {
|
||||||
|
"title": "兑换管理",
|
||||||
|
"edit": {
|
||||||
|
"title_edit": "更新兑换码信息",
|
||||||
|
"title_create": "创建新的兑换码",
|
||||||
|
"name": "名称",
|
||||||
|
"name_placeholder": "请输入名称",
|
||||||
|
"quota": "额度",
|
||||||
|
"quota_placeholder": "请输入单个兑换码中包含的额度",
|
||||||
|
"count": "生成数量",
|
||||||
|
"count_placeholder": "请输入生成数量",
|
||||||
|
"buttons": {
|
||||||
|
"submit": "提交",
|
||||||
|
"cancel": "取消"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"log": {
|
||||||
|
"title": "操作日志",
|
||||||
|
"usage_details": "使用明细",
|
||||||
|
"total_quota": "总消耗额度",
|
||||||
|
"click_to_view": "点击查看",
|
||||||
|
"table": {
|
||||||
|
"id": "ID",
|
||||||
|
"username": "用户名",
|
||||||
|
"type": "类型",
|
||||||
|
"content": "内容",
|
||||||
|
"amount": "数量",
|
||||||
|
"time": "时间"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -45,7 +45,8 @@ function App() {
|
|||||||
try {
|
try {
|
||||||
const res = await API.get('/api/status');
|
const res = await API.get('/api/status');
|
||||||
const { success, message, data } = res.data || {}; // Add default empty object
|
const { success, message, data } = res.data || {}; // Add default empty object
|
||||||
if (success && data) { // Check data exists
|
if (success && data) {
|
||||||
|
// Check data exists
|
||||||
localStorage.setItem('status', JSON.stringify(data));
|
localStorage.setItem('status', JSON.stringify(data));
|
||||||
statusDispatch({ type: 'set', payload: data });
|
statusDispatch({ type: 'set', payload: data });
|
||||||
localStorage.setItem('system_name', data.system_name);
|
localStorage.setItem('system_name', data.system_name);
|
||||||
|
@@ -18,6 +18,7 @@ import {
|
|||||||
showWarning,
|
showWarning,
|
||||||
timestamp2string,
|
timestamp2string,
|
||||||
} from '../helpers';
|
} from '../helpers';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { ITEMS_PER_PAGE } from '../constants';
|
import { ITEMS_PER_PAGE } from '../constants';
|
||||||
import { renderColorLabel, renderQuota } from '../helpers/render';
|
import { renderColorLabel, renderQuota } from '../helpers/render';
|
||||||
@@ -137,6 +138,7 @@ function renderDetail(log) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const LogsTable = () => {
|
const LogsTable = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const [logs, setLogs] = useState([]);
|
const [logs, setLogs] = useState([]);
|
||||||
const [showStat, setShowStat] = useState(false);
|
const [showStat, setShowStat] = useState(false);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
@@ -309,14 +311,14 @@ const LogsTable = () => {
|
|||||||
<>
|
<>
|
||||||
<>
|
<>
|
||||||
<Header as='h3'>
|
<Header as='h3'>
|
||||||
使用明细(总消耗额度:
|
{t('log.usage_details')}({t('log.total_quota')}:
|
||||||
{showStat && renderQuota(stat.quota)}
|
{showStat && renderQuota(stat.quota, t)}
|
||||||
{!showStat && (
|
{!showStat && (
|
||||||
<span
|
<span
|
||||||
onClick={handleEyeClick}
|
onClick={handleEyeClick}
|
||||||
style={{ cursor: 'pointer', color: 'gray' }}
|
style={{ cursor: 'pointer', color: 'gray' }}
|
||||||
>
|
>
|
||||||
点击查看
|
{t('log.click_to_view')}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
)
|
)
|
||||||
@@ -554,7 +556,7 @@ const LogsTable = () => {
|
|||||||
{log.completion_tokens ? log.completion_tokens : ''}
|
{log.completion_tokens ? log.completion_tokens : ''}
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
{log.quota ? renderQuota(log.quota, 6) : ''}
|
{log.quota ? renderQuota(log.quota, t, 6) : ''}
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Form,
|
Form,
|
||||||
@@ -25,39 +26,37 @@ function renderTimestamp(timestamp) {
|
|||||||
return <>{timestamp2string(timestamp)}</>;
|
return <>{timestamp2string(timestamp)}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderStatus(status) {
|
function renderStatus(status, t) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 1:
|
case 1:
|
||||||
return (
|
return (
|
||||||
<Label basic color='green'>
|
<Label basic color='green'>
|
||||||
未使用
|
{t('redemption.status.unused')}
|
||||||
</Label>
|
</Label>
|
||||||
);
|
);
|
||||||
case 2:
|
case 2:
|
||||||
return (
|
return (
|
||||||
<Label basic color='red'>
|
<Label basic color='red'>
|
||||||
{' '}
|
{t('redemption.status.disabled')}
|
||||||
已禁用{' '}
|
|
||||||
</Label>
|
</Label>
|
||||||
);
|
);
|
||||||
case 3:
|
case 3:
|
||||||
return (
|
return (
|
||||||
<Label basic color='grey'>
|
<Label basic color='grey'>
|
||||||
{' '}
|
{t('redemption.status.used')}
|
||||||
已使用{' '}
|
|
||||||
</Label>
|
</Label>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<Label basic color='black'>
|
<Label basic color='black'>
|
||||||
{' '}
|
{t('redemption.status.unknown')}
|
||||||
未知状态{' '}
|
|
||||||
</Label>
|
</Label>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const RedemptionsTable = () => {
|
const RedemptionsTable = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const [redemptions, setRedemptions] = useState([]);
|
const [redemptions, setRedemptions] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [activePage, setActivePage] = useState(1);
|
const [activePage, setActivePage] = useState(1);
|
||||||
@@ -260,8 +259,8 @@ const RedemptionsTable = () => {
|
|||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
{redemption.name ? redemption.name : '无'}
|
{redemption.name ? redemption.name : '无'}
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell>{renderStatus(redemption.status)}</Table.Cell>
|
<Table.Cell>{renderStatus(redemption.status, t)}</Table.Cell>
|
||||||
<Table.Cell>{renderQuota(redemption.quota)}</Table.Cell>
|
<Table.Cell>{renderQuota(redemption.quota, t)}</Table.Cell>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
{renderTimestamp(redemption.created_time)}
|
{renderTimestamp(redemption.created_time)}
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
|
@@ -69,14 +69,14 @@ const TokensTable = () => {
|
|||||||
{ key: 'next', text: t('token.copy_options.next'), value: 'next' },
|
{ key: 'next', text: t('token.copy_options.next'), value: 'next' },
|
||||||
{ key: 'ama', text: t('token.copy_options.ama'), value: 'ama' },
|
{ key: 'ama', text: t('token.copy_options.ama'), value: 'ama' },
|
||||||
{ key: 'opencat', text: t('token.copy_options.opencat'), value: 'opencat' },
|
{ key: 'opencat', text: t('token.copy_options.opencat'), value: 'opencat' },
|
||||||
{ key: 'lobe', text: t('token.copy_options.lobe'), value: 'lobechat' }
|
{ key: 'lobe', text: t('token.copy_options.lobe'), value: 'lobechat' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const OPEN_LINK_OPTIONS = [
|
const OPEN_LINK_OPTIONS = [
|
||||||
{ key: 'next', text: t('token.copy_options.next'), value: 'next' },
|
{ key: 'next', text: t('token.copy_options.next'), value: 'next' },
|
||||||
{ key: 'ama', text: t('token.copy_options.ama'), value: 'ama' },
|
{ key: 'ama', text: t('token.copy_options.ama'), value: 'ama' },
|
||||||
{ key: 'opencat', text: t('token.copy_options.opencat'), value: 'opencat' },
|
{ key: 'opencat', text: t('token.copy_options.opencat'), value: 'opencat' },
|
||||||
{ key: 'lobe', text: t('token.copy_options.lobe'), value: 'lobechat' }
|
{ key: 'lobe', text: t('token.copy_options.lobe'), value: 'lobechat' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const [tokens, setTokens] = useState([]);
|
const [tokens, setTokens] = useState([]);
|
||||||
@@ -135,7 +135,8 @@ const TokensTable = () => {
|
|||||||
let nextUrl;
|
let nextUrl;
|
||||||
|
|
||||||
if (nextLink) {
|
if (nextLink) {
|
||||||
nextUrl = nextLink + `/#/?settings={"key":"sk-${key}","url":"${serverAddress}"}`;
|
nextUrl =
|
||||||
|
nextLink + `/#/?settings={"key":"sk-${key}","url":"${serverAddress}"}`;
|
||||||
} else {
|
} else {
|
||||||
nextUrl = `https://app.nextchat.dev/#/?settings={"key":"sk-${key}","url":"${serverAddress}"}`;
|
nextUrl = `https://app.nextchat.dev/#/?settings={"key":"sk-${key}","url":"${serverAddress}"}`;
|
||||||
}
|
}
|
||||||
@@ -152,7 +153,9 @@ const TokensTable = () => {
|
|||||||
url = nextUrl;
|
url = nextUrl;
|
||||||
break;
|
break;
|
||||||
case 'lobechat':
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
url = `sk-${key}`;
|
url = `sk-${key}`;
|
||||||
@@ -376,19 +379,21 @@ const TokensTable = () => {
|
|||||||
.map((token, idx) => {
|
.map((token, idx) => {
|
||||||
if (token.deleted) return <></>;
|
if (token.deleted) return <></>;
|
||||||
|
|
||||||
const copyOptionsWithHandlers = COPY_OPTIONS.map(option => ({
|
const copyOptionsWithHandlers = COPY_OPTIONS.map((option) => ({
|
||||||
...option,
|
...option,
|
||||||
onClick: async () => {
|
onClick: async () => {
|
||||||
await onCopy(option.value, token.key);
|
await onCopy(option.value, token.key);
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const openLinkOptionsWithHandlers = OPEN_LINK_OPTIONS.map(option => ({
|
const openLinkOptionsWithHandlers = OPEN_LINK_OPTIONS.map(
|
||||||
|
(option) => ({
|
||||||
...option,
|
...option,
|
||||||
onClick: async () => {
|
onClick: async () => {
|
||||||
await onOpenLink(option.value, token.key);
|
await onOpenLink(option.value, token.key);
|
||||||
}
|
},
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table.Row key={token.id}>
|
<Table.Row key={token.id}>
|
||||||
@@ -473,7 +478,11 @@ const TokensTable = () => {
|
|||||||
? t('token.buttons.disable')
|
? t('token.buttons.disable')
|
||||||
: t('token.buttons.enable')}
|
: t('token.buttons.enable')}
|
||||||
</Button>
|
</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')}
|
{t('token.buttons.edit')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -10,6 +10,7 @@ import {
|
|||||||
} from 'semantic-ui-react';
|
} from 'semantic-ui-react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { API, showError, showSuccess } from '../helpers';
|
import { API, showError, showSuccess } from '../helpers';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { ITEMS_PER_PAGE } from '../constants';
|
import { ITEMS_PER_PAGE } from '../constants';
|
||||||
import {
|
import {
|
||||||
@@ -33,6 +34,7 @@ function renderRole(role) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const UsersTable = () => {
|
const UsersTable = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const [users, setUsers] = useState([]);
|
const [users, setUsers] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [activePage, setActivePage] = useState(1);
|
const [activePage, setActivePage] = useState(1);
|
||||||
@@ -266,12 +268,12 @@ const UsersTable = () => {
|
|||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<Popup
|
<Popup
|
||||||
content='剩余额度'
|
content='剩余额度'
|
||||||
trigger={<Label basic>{renderQuota(user.quota)}</Label>}
|
trigger={<Label basic>{renderQuota(user.quota, t)}</Label>}
|
||||||
/>
|
/>
|
||||||
<Popup
|
<Popup
|
||||||
content='已用额度'
|
content='已用额度'
|
||||||
trigger={
|
trigger={
|
||||||
<Label basic>{renderQuota(user.used_quota)}</Label>
|
<Label basic>{renderQuota(user.used_quota, t)}</Label>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Popup
|
<Popup
|
||||||
|
@@ -41,8 +41,11 @@ export function renderNumber(num) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function renderQuota(quota, t, precision = 2) {
|
export function renderQuota(quota, t, precision = 2) {
|
||||||
const displayInCurrency = localStorage.getItem('display_in_currency') === 'true';
|
const displayInCurrency =
|
||||||
const quotaPerUnit = parseFloat(localStorage.getItem('quota_per_unit') || '1');
|
localStorage.getItem('display_in_currency') === 'true';
|
||||||
|
const quotaPerUnit = parseFloat(
|
||||||
|
localStorage.getItem('quota_per_unit') || '1'
|
||||||
|
);
|
||||||
|
|
||||||
if (displayInCurrency) {
|
if (displayInCurrency) {
|
||||||
const amount = (quota / quotaPerUnit).toFixed(precision);
|
const amount = (quota / quotaPerUnit).toFixed(precision);
|
||||||
@@ -53,8 +56,11 @@ export function renderQuota(quota, t, precision = 2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function renderQuotaWithPrompt(quota, t) {
|
export function renderQuotaWithPrompt(quota, t) {
|
||||||
const displayInCurrency = localStorage.getItem('display_in_currency') === 'true';
|
const displayInCurrency =
|
||||||
const quotaPerUnit = parseFloat(localStorage.getItem('quota_per_unit') || '1');
|
localStorage.getItem('display_in_currency') === 'true';
|
||||||
|
const quotaPerUnit = parseFloat(
|
||||||
|
localStorage.getItem('quota_per_unit') || '1'
|
||||||
|
);
|
||||||
|
|
||||||
if (displayInCurrency) {
|
if (displayInCurrency) {
|
||||||
const amount = (quota / quotaPerUnit).toFixed(2);
|
const amount = (quota / quotaPerUnit).toFixed(2);
|
||||||
|
@@ -1,16 +1,21 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Card } from 'semantic-ui-react';
|
import { Card } from 'semantic-ui-react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import LogsTable from '../../components/LogsTable';
|
import LogsTable from '../../components/LogsTable';
|
||||||
|
|
||||||
const Log = () => (
|
const Log = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
<div className='dashboard-container'>
|
<div className='dashboard-container'>
|
||||||
<Card fluid className='chart-card'>
|
<Card fluid className='chart-card'>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
{/*<Card.Header className='header'>操作日志</Card.Header>*/}
|
<Card.Header className='header'>{t('log.title')}</Card.Header>
|
||||||
<LogsTable />
|
<LogsTable />
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default Log;
|
export default Log;
|
||||||
|
@@ -1,10 +1,12 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Button, Form, Card } from 'semantic-ui-react';
|
import { Button, Form, Card } from 'semantic-ui-react';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { API, downloadTextAsFile, showError, showSuccess } from '../../helpers';
|
import { API, downloadTextAsFile, showError, showSuccess } from '../../helpers';
|
||||||
import { renderQuota, renderQuotaWithPrompt } from '../../helpers/render';
|
import { renderQuota, renderQuotaWithPrompt } from '../../helpers/render';
|
||||||
|
|
||||||
const EditRedemption = () => {
|
const EditRedemption = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const redemptionId = params.id;
|
const redemptionId = params.id;
|
||||||
@@ -83,14 +85,14 @@ const EditRedemption = () => {
|
|||||||
<Card fluid className='chart-card'>
|
<Card fluid className='chart-card'>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<Card.Header className='header'>
|
<Card.Header className='header'>
|
||||||
{isEdit ? '更新兑换码信息' : '创建新的兑换码'}
|
{isEdit ? t('redemption.edit.title_edit') : t('redemption.edit.title_create')}
|
||||||
</Card.Header>
|
</Card.Header>
|
||||||
<Form loading={loading} autoComplete='new-password'>
|
<Form loading={loading} autoComplete='new-password'>
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='名称'
|
label={t('redemption.edit.name')}
|
||||||
name='name'
|
name='name'
|
||||||
placeholder={'请输入名称'}
|
placeholder={t('redemption.edit.name_placeholder')}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
value={name}
|
value={name}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
@@ -99,9 +101,9 @@ const EditRedemption = () => {
|
|||||||
</Form.Field>
|
</Form.Field>
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label={`额度${renderQuotaWithPrompt(quota)}`}
|
label={`${t('redemption.edit.quota')}${renderQuotaWithPrompt(quota, t)}`}
|
||||||
name='quota'
|
name='quota'
|
||||||
placeholder={'请输入单个兑换码中包含的额度'}
|
placeholder={t('redemption.edit.quota_placeholder')}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
value={quota}
|
value={quota}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
@@ -112,9 +114,9 @@ const EditRedemption = () => {
|
|||||||
<>
|
<>
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='生成数量'
|
label={t('redemption.edit.count')}
|
||||||
name='count'
|
name='count'
|
||||||
placeholder={'请输入生成数量'}
|
placeholder={t('redemption.edit.count_placeholder')}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
value={count}
|
value={count}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
@@ -124,9 +126,11 @@ const EditRedemption = () => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Button positive onClick={submit}>
|
<Button positive onClick={submit}>
|
||||||
提交
|
{t('redemption.edit.buttons.submit')}
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleCancel}>
|
||||||
|
{t('redemption.edit.buttons.cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleCancel}>取消</Button>
|
|
||||||
</Form>
|
</Form>
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card>
|
</Card>
|
||||||
|
@@ -107,12 +107,12 @@ const EditToken = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isEdit) {
|
if (isEdit) {
|
||||||
loadToken().catch(error => {
|
loadToken().catch((error) => {
|
||||||
showError(error.message || 'Failed to load token');
|
showError(error.message || 'Failed to load token');
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
loadAvailableModels().catch(error => {
|
loadAvailableModels().catch((error) => {
|
||||||
showError(error.message || 'Failed to load models');
|
showError(error.message || 'Failed to load models');
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
@@ -255,7 +255,10 @@ const EditToken = () => {
|
|||||||
<Message>{t('token.edit.quota_notice')}</Message>
|
<Message>{t('token.edit.quota_notice')}</Message>
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label={`${t('token.edit.quota')}${renderQuotaWithPrompt(remain_quota, t)}`}
|
label={`${t('token.edit.quota')}${renderQuotaWithPrompt(
|
||||||
|
remain_quota,
|
||||||
|
t
|
||||||
|
)}`}
|
||||||
name='remain_quota'
|
name='remain_quota'
|
||||||
placeholder={t('token.edit.quota_placeholder')}
|
placeholder={t('token.edit.quota_placeholder')}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
|
@@ -131,7 +131,7 @@ const TopUp = () => {
|
|||||||
<div style={{ textAlign: 'center', paddingTop: '1em' }}>
|
<div style={{ textAlign: 'center', paddingTop: '1em' }}>
|
||||||
<Statistic>
|
<Statistic>
|
||||||
<Statistic.Value style={{ color: '#2185d0' }}>
|
<Statistic.Value style={{ color: '#2185d0' }}>
|
||||||
{renderQuota(userQuota)}
|
{renderQuota(userQuota, t)}
|
||||||
</Statistic.Value>
|
</Statistic.Value>
|
||||||
<Statistic.Label>
|
<Statistic.Label>
|
||||||
{t('topup.get_code.current_quota')}
|
{t('topup.get_code.current_quota')}
|
||||||
|
Reference in New Issue
Block a user