feat: Add lucide-react icons to all table Tag components

- Add semantic icons to ChannelsTable.js for channel status, response time, and quota display
- Add status and quota icons to TokensTable.js for better visual distinction
- Add status and quota icons to RedemptionsTable.js for redemption code management
- Add role, status, and statistics icons to UsersTable.js for user management
- Import appropriate lucide-react icons for each table component
- Enhance UI consistency and user experience across all table interfaces

Icons added include:
- Status indicators: CheckCircle, XCircle, AlertCircle, HelpCircle
- Performance metrics: Zap, Timer, Clock, AlertTriangle, TestTube
- Financial data: Coins, DollarSign
- User roles: User, Shield, Crown
- Activity tracking: Activity, Users, UserPlus

This improves visual clarity and makes table data more intuitive to understand.
This commit is contained in:
Apple\Apple
2025-06-08 23:13:45 +08:00
parent 7a83060012
commit 8184357b49
9 changed files with 414 additions and 181 deletions

View File

@@ -7,9 +7,23 @@ import {
timestamp2string, timestamp2string,
renderGroup, renderGroup,
renderNumberWithPoint, renderNumberWithPoint,
renderQuota renderQuota,
getChannelIcon
} from '../../helpers/index.js'; } from '../../helpers/index.js';
import {
CheckCircle,
XCircle,
AlertCircle,
HelpCircle,
TestTube,
Zap,
Timer,
Clock,
AlertTriangle,
Coins
} from 'lucide-react';
import { CHANNEL_OPTIONS, ITEMS_PER_PAGE } from '../../constants/index.js'; import { CHANNEL_OPTIONS, ITEMS_PER_PAGE } from '../../constants/index.js';
import { import {
Button, Button,
@@ -63,7 +77,12 @@ const ChannelsTable = () => {
type2label[0] = { value: 0, label: t('未知类型'), color: 'grey' }; type2label[0] = { value: 0, label: t('未知类型'), color: 'grey' };
} }
return ( return (
<Tag size='large' color={type2label[type]?.color} shape='circle'> <Tag
size='large'
color={type2label[type]?.color}
shape='circle'
prefixIcon={getChannelIcon(type)}
>
{type2label[type]?.label} {type2label[type]?.label}
</Tag> </Tag>
); );
@@ -87,25 +106,25 @@ const ChannelsTable = () => {
switch (status) { switch (status) {
case 1: case 1:
return ( return (
<Tag size='large' color='green' shape='circle'> <Tag size='large' color='green' shape='circle' prefixIcon={<CheckCircle size={14} />}>
{t('已启用')} {t('已启用')}
</Tag> </Tag>
); );
case 2: case 2:
return ( return (
<Tag size='large' color='yellow' shape='circle'> <Tag size='large' color='yellow' shape='circle' prefixIcon={<XCircle size={14} />}>
{t('已禁用')} {t('已禁用')}
</Tag> </Tag>
); );
case 3: case 3:
return ( return (
<Tag size='large' color='yellow' shape='circle'> <Tag size='large' color='yellow' shape='circle' prefixIcon={<AlertCircle size={14} />}>
{t('自动禁用')} {t('自动禁用')}
</Tag> </Tag>
); );
default: default:
return ( return (
<Tag size='large' color='grey' shape='circle'> <Tag size='large' color='grey' shape='circle' prefixIcon={<HelpCircle size={14} />}>
{t('未知状态')} {t('未知状态')}
</Tag> </Tag>
); );
@@ -117,31 +136,31 @@ const ChannelsTable = () => {
time = time.toFixed(2) + t(' 秒'); time = time.toFixed(2) + t(' 秒');
if (responseTime === 0) { if (responseTime === 0) {
return ( return (
<Tag size='large' color='grey' shape='circle'> <Tag size='large' color='grey' shape='circle' prefixIcon={<TestTube size={14} />}>
{t('未测试')} {t('未测试')}
</Tag> </Tag>
); );
} else if (responseTime <= 1000) { } else if (responseTime <= 1000) {
return ( return (
<Tag size='large' color='green' shape='circle'> <Tag size='large' color='green' shape='circle' prefixIcon={<Zap size={14} />}>
{time} {time}
</Tag> </Tag>
); );
} else if (responseTime <= 3000) { } else if (responseTime <= 3000) {
return ( return (
<Tag size='large' color='lime' shape='circle'> <Tag size='large' color='lime' shape='circle' prefixIcon={<Timer size={14} />}>
{time} {time}
</Tag> </Tag>
); );
} else if (responseTime <= 5000) { } else if (responseTime <= 5000) {
return ( return (
<Tag size='large' color='yellow' shape='circle'> <Tag size='large' color='yellow' shape='circle' prefixIcon={<Clock size={14} />}>
{time} {time}
</Tag> </Tag>
); );
} else { } else {
return ( return (
<Tag size='large' color='red' shape='circle'> <Tag size='large' color='red' shape='circle' prefixIcon={<AlertTriangle size={14} />}>
{time} {time}
</Tag> </Tag>
); );
@@ -228,7 +247,7 @@ const ChannelsTable = () => {
<div> <div>
<Space spacing={1}> <Space spacing={1}>
<Tooltip content={t('已用额度')}> <Tooltip content={t('已用额度')}>
<Tag color='white' type='ghost' size='large' shape='circle'> <Tag color='white' type='ghost' size='large' shape='circle' prefixIcon={<Coins size={14} />}>
{renderQuota(record.used_quota)} {renderQuota(record.used_quota)}
</Tag> </Tag>
</Tooltip> </Tooltip>
@@ -238,6 +257,7 @@ const ChannelsTable = () => {
type='ghost' type='ghost'
size='large' size='large'
shape='circle' shape='circle'
prefixIcon={<Coins size={14} />}
onClick={() => updateChannelBalance(record)} onClick={() => updateChannelBalance(record)}
> >
${renderNumberWithPoint(record.balance)} ${renderNumberWithPoint(record.balance)}
@@ -249,7 +269,7 @@ const ChannelsTable = () => {
} else { } else {
return ( return (
<Tooltip content={t('已用额度')}> <Tooltip content={t('已用额度')}>
<Tag color='white' type='ghost' size='large' shape='circle'> <Tag color='white' type='ghost' size='large' shape='circle' prefixIcon={<Coins size={14} />}>
{renderQuota(record.used_quota)} {renderQuota(record.used_quota)}
</Tag> </Tag>
</Tooltip> </Tooltip>

View File

@@ -1,5 +1,18 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import {
CreditCard,
ShoppingCart,
Settings,
Server,
AlertTriangle,
HelpCircle,
Zap,
Play,
Clock,
Hash,
Key
} from 'lucide-react';
import { import {
API, API,
copy, copy,
@@ -20,7 +33,7 @@ import {
renderQuota, renderQuota,
stringToColor, stringToColor,
getLogOther, getLogOther,
renderModelTag, renderModelTag
} from '../../helpers'; } from '../../helpers';
import { import {
@@ -38,7 +51,7 @@ import {
Card, Card,
Typography, Typography,
Divider, Divider,
Form, Form
} from '@douyinfe/semi-ui'; } from '@douyinfe/semi-ui';
import { ITEMS_PER_PAGE } from '../../constants'; import { ITEMS_PER_PAGE } from '../../constants';
import Paragraph from '@douyinfe/semi-ui/lib/es/typography/paragraph'; import Paragraph from '@douyinfe/semi-ui/lib/es/typography/paragraph';
@@ -71,37 +84,37 @@ const LogsTable = () => {
switch (type) { switch (type) {
case 1: case 1:
return ( return (
<Tag color='cyan' size='large' shape='circle'> <Tag color='cyan' size='large' shape='circle' prefixIcon={<CreditCard size={14} />}>
{t('充值')} {t('充值')}
</Tag> </Tag>
); );
case 2: case 2:
return ( return (
<Tag color='lime' size='large' shape='circle'> <Tag color='lime' size='large' shape='circle' prefixIcon={<ShoppingCart size={14} />}>
{t('消费')} {t('消费')}
</Tag> </Tag>
); );
case 3: case 3:
return ( return (
<Tag color='orange' size='large' shape='circle'> <Tag color='orange' size='large' shape='circle' prefixIcon={<Settings size={14} />}>
{t('管理')} {t('管理')}
</Tag> </Tag>
); );
case 4: case 4:
return ( return (
<Tag color='purple' size='large' shape='circle'> <Tag color='purple' size='large' shape='circle' prefixIcon={<Server size={14} />}>
{t('系统')} {t('系统')}
</Tag> </Tag>
); );
case 5: case 5:
return ( return (
<Tag color='red' size='large' shape='circle'> <Tag color='red' size='large' shape='circle' prefixIcon={<AlertTriangle size={14} />}>
{t('错误')} {t('错误')}
</Tag> </Tag>
); );
default: default:
return ( return (
<Tag color='grey' size='large' shape='circle'> <Tag color='grey' size='large' shape='circle' prefixIcon={<HelpCircle size={14} />}>
{t('未知')} {t('未知')}
</Tag> </Tag>
); );
@@ -111,13 +124,13 @@ const LogsTable = () => {
function renderIsStream(bool) { function renderIsStream(bool) {
if (bool) { if (bool) {
return ( return (
<Tag color='blue' size='large' shape='circle'> <Tag color='blue' size='large' shape='circle' prefixIcon={<Zap size={14} />}>
{t('流')} {t('流')}
</Tag> </Tag>
); );
} else { } else {
return ( return (
<Tag color='purple' size='large' shape='circle'> <Tag color='purple' size='large' shape='circle' prefixIcon={<Play size={14} />}>
{t('非流')} {t('非流')}
</Tag> </Tag>
); );
@@ -128,21 +141,21 @@ const LogsTable = () => {
const time = parseInt(type); const time = parseInt(type);
if (time < 101) { if (time < 101) {
return ( return (
<Tag color='green' size='large' shape='circle'> <Tag color='green' size='large' shape='circle' prefixIcon={<Clock size={14} />}>
{' '} {' '}
{time} s{' '} {time} s{' '}
</Tag> </Tag>
); );
} else if (time < 300) { } else if (time < 300) {
return ( return (
<Tag color='orange' size='large' shape='circle'> <Tag color='orange' size='large' shape='circle' prefixIcon={<Clock size={14} />}>
{' '} {' '}
{time} s{' '} {time} s{' '}
</Tag> </Tag>
); );
} else { } else {
return ( return (
<Tag color='red' size='large' shape='circle'> <Tag color='red' size='large' shape='circle' prefixIcon={<Clock size={14} />}>
{' '} {' '}
{time} s{' '} {time} s{' '}
</Tag> </Tag>
@@ -155,21 +168,21 @@ const LogsTable = () => {
time = time.toFixed(1); time = time.toFixed(1);
if (time < 3) { if (time < 3) {
return ( return (
<Tag color='green' size='large' shape='circle'> <Tag color='green' size='large' shape='circle' prefixIcon={<Zap size={14} />}>
{' '} {' '}
{time} s{' '} {time} s{' '}
</Tag> </Tag>
); );
} else if (time < 10) { } else if (time < 10) {
return ( return (
<Tag color='orange' size='large' shape='circle'> <Tag color='orange' size='large' shape='circle' prefixIcon={<Zap size={14} />}>
{' '} {' '}
{time} s{' '} {time} s{' '}
</Tag> </Tag>
); );
} else { } else {
return ( return (
<Tag color='red' size='large' shape='circle'> <Tag color='red' size='large' shape='circle' prefixIcon={<Zap size={14} />}>
{' '} {' '}
{time} s{' '} {time} s{' '}
</Tag> </Tag>
@@ -356,6 +369,7 @@ const LogsTable = () => {
color={colors[parseInt(text) % colors.length]} color={colors[parseInt(text) % colors.length]}
size='large' size='large'
shape='circle' shape='circle'
prefixIcon={<Hash size={14} />}
> >
{' '} {' '}
{text}{' '} {text}{' '}
@@ -408,6 +422,7 @@ const LogsTable = () => {
color='grey' color='grey'
size='large' size='large'
shape='circle' shape='circle'
prefixIcon={<Key size={14} />}
onClick={(event) => { onClick={(event) => {
//cancel the row click event //cancel the row click event
copyText(event, text); copyText(event, text);

View File

@@ -1,12 +1,38 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import {
Palette,
ZoomIn,
Shuffle,
Move,
FileText,
Blend,
Upload,
Minimize2,
RotateCcw,
PaintBucket,
Focus,
Move3D,
Monitor,
UserCheck,
HelpCircle,
CheckCircle,
Clock,
Copy,
FileX,
Pause,
XCircle,
Loader,
AlertCircle,
Hash
} from 'lucide-react';
import { import {
API, API,
copy, copy,
isAdmin, isAdmin,
showError, showError,
showSuccess, showSuccess,
timestamp2string, timestamp2string
} from '../../helpers'; } from '../../helpers';
import { import {
@@ -22,13 +48,13 @@ import {
Skeleton, Skeleton,
Table, Table,
Tag, Tag,
Typography, Typography
} from '@douyinfe/semi-ui'; } from '@douyinfe/semi-ui';
import { ITEMS_PER_PAGE } from '../../constants'; import { ITEMS_PER_PAGE } from '../../constants';
import { import {
IconEyeOpened, IconEyeOpened,
IconSearch, IconSearch,
IconSetting, IconSetting
} from '@douyinfe/semi-icons'; } from '@douyinfe/semi-icons';
const { Text } = Typography; const { Text } = Typography;
@@ -153,103 +179,103 @@ const LogsTable = () => {
switch (type) { switch (type) {
case 'IMAGINE': case 'IMAGINE':
return ( return (
<Tag color='blue' size='large' shape='circle'> <Tag color='blue' size='large' shape='circle' prefixIcon={<Palette size={14} />}>
{t('绘图')} {t('绘图')}
</Tag> </Tag>
); );
case 'UPSCALE': case 'UPSCALE':
return ( return (
<Tag color='orange' size='large' shape='circle'> <Tag color='orange' size='large' shape='circle' prefixIcon={<ZoomIn size={14} />}>
{t('放大')} {t('放大')}
</Tag> </Tag>
); );
case 'VARIATION': case 'VARIATION':
return ( return (
<Tag color='purple' size='large' shape='circle'> <Tag color='purple' size='large' shape='circle' prefixIcon={<Shuffle size={14} />}>
{t('变换')} {t('变换')}
</Tag> </Tag>
); );
case 'HIGH_VARIATION': case 'HIGH_VARIATION':
return ( return (
<Tag color='purple' size='large' shape='circle'> <Tag color='purple' size='large' shape='circle' prefixIcon={<Shuffle size={14} />}>
{t('强变换')} {t('强变换')}
</Tag> </Tag>
); );
case 'LOW_VARIATION': case 'LOW_VARIATION':
return ( return (
<Tag color='purple' size='large' shape='circle'> <Tag color='purple' size='large' shape='circle' prefixIcon={<Shuffle size={14} />}>
{t('弱变换')} {t('弱变换')}
</Tag> </Tag>
); );
case 'PAN': case 'PAN':
return ( return (
<Tag color='cyan' size='large' shape='circle'> <Tag color='cyan' size='large' shape='circle' prefixIcon={<Move size={14} />}>
{t('平移')} {t('平移')}
</Tag> </Tag>
); );
case 'DESCRIBE': case 'DESCRIBE':
return ( return (
<Tag color='yellow' size='large' shape='circle'> <Tag color='yellow' size='large' shape='circle' prefixIcon={<FileText size={14} />}>
{t('图生文')} {t('图生文')}
</Tag> </Tag>
); );
case 'BLEND': case 'BLEND':
return ( return (
<Tag color='lime' size='large' shape='circle'> <Tag color='lime' size='large' shape='circle' prefixIcon={<Blend size={14} />}>
{t('图混合')} {t('图混合')}
</Tag> </Tag>
); );
case 'UPLOAD': case 'UPLOAD':
return ( return (
<Tag color='blue' size='large' shape='circle'> <Tag color='blue' size='large' shape='circle' prefixIcon={<Upload size={14} />}>
上传文件 上传文件
</Tag> </Tag>
); );
case 'SHORTEN': case 'SHORTEN':
return ( return (
<Tag color='pink' size='large' shape='circle'> <Tag color='pink' size='large' shape='circle' prefixIcon={<Minimize2 size={14} />}>
{t('缩词')} {t('缩词')}
</Tag> </Tag>
); );
case 'REROLL': case 'REROLL':
return ( return (
<Tag color='indigo' size='large' shape='circle'> <Tag color='indigo' size='large' shape='circle' prefixIcon={<RotateCcw size={14} />}>
{t('重绘')} {t('重绘')}
</Tag> </Tag>
); );
case 'INPAINT': case 'INPAINT':
return ( return (
<Tag color='violet' size='large' shape='circle'> <Tag color='violet' size='large' shape='circle' prefixIcon={<PaintBucket size={14} />}>
{t('局部重绘-提交')} {t('局部重绘-提交')}
</Tag> </Tag>
); );
case 'ZOOM': case 'ZOOM':
return ( return (
<Tag color='teal' size='large' shape='circle'> <Tag color='teal' size='large' shape='circle' prefixIcon={<Focus size={14} />}>
{t('变焦')} {t('变焦')}
</Tag> </Tag>
); );
case 'CUSTOM_ZOOM': case 'CUSTOM_ZOOM':
return ( return (
<Tag color='teal' size='large' shape='circle'> <Tag color='teal' size='large' shape='circle' prefixIcon={<Move3D size={14} />}>
{t('自定义变焦-提交')} {t('自定义变焦-提交')}
</Tag> </Tag>
); );
case 'MODAL': case 'MODAL':
return ( return (
<Tag color='green' size='large' shape='circle'> <Tag color='green' size='large' shape='circle' prefixIcon={<Monitor size={14} />}>
{t('窗口处理')} {t('窗口处理')}
</Tag> </Tag>
); );
case 'SWAP_FACE': case 'SWAP_FACE':
return ( return (
<Tag color='light-green' size='large' shape='circle'> <Tag color='light-green' size='large' shape='circle' prefixIcon={<UserCheck size={14} />}>
{t('换脸')} {t('换脸')}
</Tag> </Tag>
); );
default: default:
return ( return (
<Tag color='white' size='large' shape='circle'> <Tag color='white' size='large' shape='circle' prefixIcon={<HelpCircle size={14} />}>
{t('未知')} {t('未知')}
</Tag> </Tag>
); );
@@ -260,31 +286,31 @@ const LogsTable = () => {
switch (code) { switch (code) {
case 1: case 1:
return ( return (
<Tag color='green' size='large' shape='circle'> <Tag color='green' size='large' shape='circle' prefixIcon={<CheckCircle size={14} />}>
{t('已提交')} {t('已提交')}
</Tag> </Tag>
); );
case 21: case 21:
return ( return (
<Tag color='lime' size='large' shape='circle'> <Tag color='lime' size='large' shape='circle' prefixIcon={<Clock size={14} />}>
{t('等待中')} {t('等待中')}
</Tag> </Tag>
); );
case 22: case 22:
return ( return (
<Tag color='orange' size='large' shape='circle'> <Tag color='orange' size='large' shape='circle' prefixIcon={<Copy size={14} />}>
{t('重复提交')} {t('重复提交')}
</Tag> </Tag>
); );
case 0: case 0:
return ( return (
<Tag color='yellow' size='large' shape='circle'> <Tag color='yellow' size='large' shape='circle' prefixIcon={<FileX size={14} />}>
{t('未提交')} {t('未提交')}
</Tag> </Tag>
); );
default: default:
return ( return (
<Tag color='white' size='large' shape='circle'> <Tag color='white' size='large' shape='circle' prefixIcon={<HelpCircle size={14} />}>
{t('未知')} {t('未知')}
</Tag> </Tag>
); );
@@ -295,43 +321,43 @@ const LogsTable = () => {
switch (type) { switch (type) {
case 'SUCCESS': case 'SUCCESS':
return ( return (
<Tag color='green' size='large' shape='circle'> <Tag color='green' size='large' shape='circle' prefixIcon={<CheckCircle size={14} />}>
{t('成功')} {t('成功')}
</Tag> </Tag>
); );
case 'NOT_START': case 'NOT_START':
return ( return (
<Tag color='grey' size='large' shape='circle'> <Tag color='grey' size='large' shape='circle' prefixIcon={<Pause size={14} />}>
{t('未启动')} {t('未启动')}
</Tag> </Tag>
); );
case 'SUBMITTED': case 'SUBMITTED':
return ( return (
<Tag color='yellow' size='large' shape='circle'> <Tag color='yellow' size='large' shape='circle' prefixIcon={<Clock size={14} />}>
{t('队列中')} {t('队列中')}
</Tag> </Tag>
); );
case 'IN_PROGRESS': case 'IN_PROGRESS':
return ( return (
<Tag color='blue' size='large' shape='circle'> <Tag color='blue' size='large' shape='circle' prefixIcon={<Loader size={14} />}>
{t('执行中')} {t('执行中')}
</Tag> </Tag>
); );
case 'FAILURE': case 'FAILURE':
return ( return (
<Tag color='red' size='large' shape='circle'> <Tag color='red' size='large' shape='circle' prefixIcon={<XCircle size={14} />}>
{t('失败')} {t('失败')}
</Tag> </Tag>
); );
case 'MODAL': case 'MODAL':
return ( return (
<Tag color='yellow' size='large' shape='circle'> <Tag color='yellow' size='large' shape='circle' prefixIcon={<AlertCircle size={14} />}>
{t('窗口等待')} {t('窗口等待')}
</Tag> </Tag>
); );
default: default:
return ( return (
<Tag color='white' size='large' shape='circle'> <Tag color='white' size='large' shape='circle' prefixIcon={<HelpCircle size={14} />}>
{t('未知')} {t('未知')}
</Tag> </Tag>
); );
@@ -361,7 +387,7 @@ const LogsTable = () => {
const color = durationSec > 60 ? 'red' : 'green'; const color = durationSec > 60 ? 'red' : 'green';
return ( return (
<Tag color={color} size='large' shape='circle'> <Tag color={color} size='large' shape='circle' prefixIcon={<Clock size={14} />}>
{durationSec} {t('秒')} {durationSec} {t('秒')}
</Tag> </Tag>
); );
@@ -397,6 +423,7 @@ const LogsTable = () => {
color={colors[parseInt(text) % colors.length]} color={colors[parseInt(text) % colors.length]}
size='large' size='large'
shape='circle' shape='circle'
prefixIcon={<Hash size={14} />}
onClick={() => { onClick={() => {
copyText(text); copyText(text);
}} }}

View File

@@ -8,6 +8,14 @@ import {
renderQuota renderQuota
} from '../../helpers'; } from '../../helpers';
import {
CheckCircle,
XCircle,
Minus,
HelpCircle,
Coins
} from 'lucide-react';
import { ITEMS_PER_PAGE } from '../../constants'; import { ITEMS_PER_PAGE } from '../../constants';
import { import {
Button, Button,
@@ -20,7 +28,7 @@ import {
Space, Space,
Table, Table,
Tag, Tag,
Typography, Typography
} from '@douyinfe/semi-ui'; } from '@douyinfe/semi-ui';
import { import {
IconPlus, IconPlus,
@@ -31,7 +39,7 @@ import {
IconDelete, IconDelete,
IconStop, IconStop,
IconPlay, IconPlay,
IconMore, IconMore
} from '@douyinfe/semi-icons'; } from '@douyinfe/semi-icons';
import EditRedemption from '../../pages/Redemption/EditRedemption'; import EditRedemption from '../../pages/Redemption/EditRedemption';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@@ -49,25 +57,25 @@ const RedemptionsTable = () => {
switch (status) { switch (status) {
case 1: case 1:
return ( return (
<Tag color='green' size='large' shape='circle'> <Tag color='green' size='large' shape='circle' prefixIcon={<CheckCircle size={14} />}>
{t('未使用')} {t('未使用')}
</Tag> </Tag>
); );
case 2: case 2:
return ( return (
<Tag color='red' size='large' shape='circle'> <Tag color='red' size='large' shape='circle' prefixIcon={<XCircle size={14} />}>
{t('已禁用')} {t('已禁用')}
</Tag> </Tag>
); );
case 3: case 3:
return ( return (
<Tag color='grey' size='large' shape='circle'> <Tag color='grey' size='large' shape='circle' prefixIcon={<Minus size={14} />}>
{t('已使用')} {t('已使用')}
</Tag> </Tag>
); );
default: default:
return ( return (
<Tag color='black' size='large' shape='circle'> <Tag color='black' size='large' shape='circle' prefixIcon={<HelpCircle size={14} />}>
{t('未知状态')} {t('未知状态')}
</Tag> </Tag>
); );
@@ -95,7 +103,13 @@ const RedemptionsTable = () => {
title: t('额度'), title: t('额度'),
dataIndex: 'quota', dataIndex: 'quota',
render: (text, record, index) => { render: (text, record, index) => {
return <div>{renderQuota(parseInt(text))}</div>; return (
<div>
<Tag size={'large'} color={'grey'} shape='circle' prefixIcon={<Coins size={14} />}>
{renderQuota(parseInt(text))}
</Tag>
</div>
);
}, },
}, },
{ {

View File

@@ -1,12 +1,25 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import {
Music,
FileText,
HelpCircle,
CheckCircle,
Pause,
Clock,
Play,
XCircle,
Loader,
List,
Hash
} from 'lucide-react';
import { import {
API, API,
copy, copy,
isAdmin, isAdmin,
showError, showError,
showSuccess, showSuccess,
timestamp2string, timestamp2string
} from '../../helpers'; } from '../../helpers';
import { import {
@@ -21,13 +34,13 @@ import {
Skeleton, Skeleton,
Table, Table,
Tag, Tag,
Typography, Typography
} from '@douyinfe/semi-ui'; } from '@douyinfe/semi-ui';
import { ITEMS_PER_PAGE } from '../../constants'; import { ITEMS_PER_PAGE } from '../../constants';
import { import {
IconEyeOpened, IconEyeOpened,
IconSearch, IconSearch,
IconSetting, IconSetting
} from '@douyinfe/semi-icons'; } from '@douyinfe/semi-icons';
const { Text } = Typography; const { Text } = Typography;
@@ -96,7 +109,7 @@ function renderDuration(submit_time, finishTime) {
// 返回带有样式的颜色标签 // 返回带有样式的颜色标签
return ( return (
<Tag color={color} size='large'> <Tag color={color} size='large' prefixIcon={<Clock size={14} />}>
{durationSec} {durationSec}
</Tag> </Tag>
); );
@@ -187,19 +200,19 @@ const LogsTable = () => {
switch (type) { switch (type) {
case 'MUSIC': case 'MUSIC':
return ( return (
<Tag color='grey' size='large' shape='circle'> <Tag color='grey' size='large' shape='circle' prefixIcon={<Music size={14} />}>
{t('生成音乐')} {t('生成音乐')}
</Tag> </Tag>
); );
case 'LYRICS': case 'LYRICS':
return ( return (
<Tag color='pink' size='large' shape='circle'> <Tag color='pink' size='large' shape='circle' prefixIcon={<FileText size={14} />}>
{t('生成歌词')} {t('生成歌词')}
</Tag> </Tag>
); );
default: default:
return ( return (
<Tag color='white' size='large' shape='circle'> <Tag color='white' size='large' shape='circle' prefixIcon={<HelpCircle size={14} />}>
{t('未知')} {t('未知')}
</Tag> </Tag>
); );
@@ -210,13 +223,13 @@ const LogsTable = () => {
switch (type) { switch (type) {
case 'suno': case 'suno':
return ( return (
<Tag color='green' size='large' shape='circle'> <Tag color='green' size='large' shape='circle' prefixIcon={<Music size={14} />}>
Suno Suno
</Tag> </Tag>
); );
default: default:
return ( return (
<Tag color='white' size='large' shape='circle'> <Tag color='white' size='large' shape='circle' prefixIcon={<HelpCircle size={14} />}>
{t('未知')} {t('未知')}
</Tag> </Tag>
); );
@@ -227,55 +240,55 @@ const LogsTable = () => {
switch (type) { switch (type) {
case 'SUCCESS': case 'SUCCESS':
return ( return (
<Tag color='green' size='large' shape='circle'> <Tag color='green' size='large' shape='circle' prefixIcon={<CheckCircle size={14} />}>
{t('成功')} {t('成功')}
</Tag> </Tag>
); );
case 'NOT_START': case 'NOT_START':
return ( return (
<Tag color='grey' size='large' shape='circle'> <Tag color='grey' size='large' shape='circle' prefixIcon={<Pause size={14} />}>
{t('未启动')} {t('未启动')}
</Tag> </Tag>
); );
case 'SUBMITTED': case 'SUBMITTED':
return ( return (
<Tag color='yellow' size='large' shape='circle'> <Tag color='yellow' size='large' shape='circle' prefixIcon={<Clock size={14} />}>
{t('队列中')} {t('队列中')}
</Tag> </Tag>
); );
case 'IN_PROGRESS': case 'IN_PROGRESS':
return ( return (
<Tag color='blue' size='large' shape='circle'> <Tag color='blue' size='large' shape='circle' prefixIcon={<Play size={14} />}>
{t('执行中')} {t('执行中')}
</Tag> </Tag>
); );
case 'FAILURE': case 'FAILURE':
return ( return (
<Tag color='red' size='large' shape='circle'> <Tag color='red' size='large' shape='circle' prefixIcon={<XCircle size={14} />}>
{t('失败')} {t('失败')}
</Tag> </Tag>
); );
case 'QUEUED': case 'QUEUED':
return ( return (
<Tag color='orange' size='large' shape='circle'> <Tag color='orange' size='large' shape='circle' prefixIcon={<List size={14} />}>
{t('排队中')} {t('排队中')}
</Tag> </Tag>
); );
case 'UNKNOWN': case 'UNKNOWN':
return ( return (
<Tag color='white' size='large' shape='circle'> <Tag color='white' size='large' shape='circle' prefixIcon={<HelpCircle size={14} />}>
{t('未知')} {t('未知')}
</Tag> </Tag>
); );
case '': case '':
return ( return (
<Tag color='grey' size='large' shape='circle'> <Tag color='grey' size='large' shape='circle' prefixIcon={<Loader size={14} />}>
{t('正在提交')} {t('正在提交')}
</Tag> </Tag>
); );
default: default:
return ( return (
<Tag color='white' size='large' shape='circle'> <Tag color='white' size='large' shape='circle' prefixIcon={<HelpCircle size={14} />}>
{t('未知')} {t('未知')}
</Tag> </Tag>
); );
@@ -320,6 +333,7 @@ const LogsTable = () => {
color={colors[parseInt(text) % colors.length]} color={colors[parseInt(text) % colors.length]}
size='large' size='large'
shape='circle' shape='circle'
prefixIcon={<Hash size={14} />}
onClick={() => { onClick={() => {
copyText(text); copyText(text);
}} }}

View File

@@ -6,7 +6,8 @@ import {
showSuccess, showSuccess,
timestamp2string, timestamp2string,
renderGroup, renderGroup,
renderQuota renderQuota,
getQuotaPerUnit
} from '../../helpers'; } from '../../helpers';
import { ITEMS_PER_PAGE } from '../../constants'; import { ITEMS_PER_PAGE } from '../../constants';
@@ -19,9 +20,20 @@ import {
Space, Space,
SplitButtonGroup, SplitButtonGroup,
Table, Table,
Tag, Tag
} from '@douyinfe/semi-ui'; } from '@douyinfe/semi-ui';
import {
CheckCircle,
Shield,
XCircle,
Clock,
Gauge,
HelpCircle,
Infinity,
Coins
} from 'lucide-react';
import { import {
IconPlus, IconPlus,
IconCopy, IconCopy,
@@ -32,7 +44,7 @@ import {
IconDelete, IconDelete,
IconStop, IconStop,
IconPlay, IconPlay,
IconMore, IconMore
} from '@douyinfe/semi-icons'; } from '@douyinfe/semi-icons';
import EditToken from '../../pages/Token/EditToken'; import EditToken from '../../pages/Token/EditToken';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@@ -49,38 +61,38 @@ const TokensTable = () => {
case 1: case 1:
if (model_limits_enabled) { if (model_limits_enabled) {
return ( return (
<Tag color='green' size='large' shape='circle'> <Tag color='green' size='large' shape='circle' prefixIcon={<Shield size={14} />}>
{t('已启用:限制模型')} {t('已启用:限制模型')}
</Tag> </Tag>
); );
} else { } else {
return ( return (
<Tag color='green' size='large' shape='circle'> <Tag color='green' size='large' shape='circle' prefixIcon={<CheckCircle size={14} />}>
{t('已启用')} {t('已启用')}
</Tag> </Tag>
); );
} }
case 2: case 2:
return ( return (
<Tag color='red' size='large' shape='circle'> <Tag color='red' size='large' shape='circle' prefixIcon={<XCircle size={14} />}>
{t('已禁用')} {t('已禁用')}
</Tag> </Tag>
); );
case 3: case 3:
return ( return (
<Tag color='yellow' size='large' shape='circle'> <Tag color='yellow' size='large' shape='circle' prefixIcon={<Clock size={14} />}>
{t('已过期')} {t('已过期')}
</Tag> </Tag>
); );
case 4: case 4:
return ( return (
<Tag color='grey' size='large' shape='circle'> <Tag color='grey' size='large' shape='circle' prefixIcon={<Gauge size={14} />}>
{t('已耗尽')} {t('已耗尽')}
</Tag> </Tag>
); );
default: default:
return ( return (
<Tag color='black' size='large' shape='circle'> <Tag color='black' size='large' shape='circle' prefixIcon={<HelpCircle size={14} />}>
{t('未知状态')} {t('未知状态')}
</Tag> </Tag>
); );
@@ -111,21 +123,45 @@ const TokensTable = () => {
title: t('已用额度'), title: t('已用额度'),
dataIndex: 'used_quota', dataIndex: 'used_quota',
render: (text, record, index) => { render: (text, record, index) => {
return <div>{renderQuota(parseInt(text))}</div>; return (
<div>
<Tag size={'large'} color={'grey'} shape='circle' prefixIcon={<Coins size={14} />}>
{renderQuota(parseInt(text))}
</Tag>
</div>
);
}, },
}, },
{ {
title: t('剩余额度'), title: t('剩余额度'),
dataIndex: 'remain_quota', dataIndex: 'remain_quota',
render: (text, record, index) => { render: (text, record, index) => {
const getQuotaColor = (quotaValue) => {
const quotaPerUnit = getQuotaPerUnit();
const dollarAmount = quotaValue / quotaPerUnit;
if (dollarAmount <= 0) {
return 'red';
} else if (dollarAmount <= 100) {
return 'yellow';
} else {
return 'green';
}
};
return ( return (
<div> <div>
{record.unlimited_quota ? ( {record.unlimited_quota ? (
<Tag size={'large'} color={'white'} shape='circle'> <Tag size={'large'} color={'white'} shape='circle' prefixIcon={<Infinity size={14} />}>
{t('无限制')} {t('无限制')}
</Tag> </Tag>
) : ( ) : (
<Tag size={'large'} color={'light-blue'} shape='circle'> <Tag
size={'large'}
color={getQuotaColor(parseInt(text))}
shape='circle'
prefixIcon={<Coins size={14} />}
>
{renderQuota(parseInt(text))} {renderQuota(parseInt(text))}
</Tag> </Tag>
)} )}

View File

@@ -1,5 +1,20 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { API, showError, showSuccess, renderGroup, renderNumber, renderQuota } from '../../helpers'; import { API, showError, showSuccess, renderGroup, renderNumber, renderQuota } from '../../helpers';
import {
User,
Shield,
Crown,
HelpCircle,
CheckCircle,
XCircle,
Minus,
Coins,
Activity,
Users,
DollarSign,
UserPlus
} from 'lucide-react';
import { import {
Button, Button,
Card, Card,
@@ -10,7 +25,7 @@ import {
Space, Space,
Table, Table,
Tag, Tag,
Typography, Typography
} from '@douyinfe/semi-ui'; } from '@douyinfe/semi-ui';
import { import {
IconPlus, IconPlus,
@@ -22,7 +37,7 @@ import {
IconMore, IconMore,
IconUserAdd, IconUserAdd,
IconArrowUp, IconArrowUp,
IconArrowDown, IconArrowDown
} from '@douyinfe/semi-icons'; } from '@douyinfe/semi-icons';
import { ITEMS_PER_PAGE } from '../../constants'; import { ITEMS_PER_PAGE } from '../../constants';
import AddUser from '../../pages/User/AddUser'; import AddUser from '../../pages/User/AddUser';
@@ -38,25 +53,25 @@ const UsersTable = () => {
switch (role) { switch (role) {
case 1: case 1:
return ( return (
<Tag size='large' color='blue' shape='circle'> <Tag size='large' color='blue' shape='circle' prefixIcon={<User size={14} />}>
{t('普通用户')} {t('普通用户')}
</Tag> </Tag>
); );
case 10: case 10:
return ( return (
<Tag color='yellow' size='large' shape='circle'> <Tag color='yellow' size='large' shape='circle' prefixIcon={<Shield size={14} />}>
{t('管理员')} {t('管理员')}
</Tag> </Tag>
); );
case 100: case 100:
return ( return (
<Tag color='orange' size='large' shape='circle'> <Tag color='orange' size='large' shape='circle' prefixIcon={<Crown size={14} />}>
{t('超级管理员')} {t('超级管理员')}
</Tag> </Tag>
); );
default: default:
return ( return (
<Tag color='red' size='large' shape='circle'> <Tag color='red' size='large' shape='circle' prefixIcon={<HelpCircle size={14} />}>
{t('未知身份')} {t('未知身份')}
</Tag> </Tag>
); );
@@ -66,16 +81,16 @@ const UsersTable = () => {
const renderStatus = (status) => { const renderStatus = (status) => {
switch (status) { switch (status) {
case 1: case 1:
return <Tag size='large' color='green' shape='circle'>{t('已激活')}</Tag>; return <Tag size='large' color='green' shape='circle' prefixIcon={<CheckCircle size={14} />}>{t('已激活')}</Tag>;
case 2: case 2:
return ( return (
<Tag size='large' color='red' shape='circle'> <Tag size='large' color='red' shape='circle' prefixIcon={<XCircle size={14} />}>
{t('已封禁')} {t('已封禁')}
</Tag> </Tag>
); );
default: default:
return ( return (
<Tag size='large' color='grey' shape='circle'> <Tag size='large' color='grey' shape='circle' prefixIcon={<HelpCircle size={14} />}>
{t('未知状态')} {t('未知状态')}
</Tag> </Tag>
); );
@@ -105,13 +120,13 @@ const UsersTable = () => {
return ( return (
<div> <div>
<Space spacing={1}> <Space spacing={1}>
<Tag color='white' size='large' shape='circle' className="!text-xs"> <Tag color='white' size='large' shape='circle' className="!text-xs" prefixIcon={<Coins size={14} />}>
{t('剩余')}: {renderQuota(record.quota)} {t('剩余')}: {renderQuota(record.quota)}
</Tag> </Tag>
<Tag color='white' size='large' shape='circle' className="!text-xs"> <Tag color='white' size='large' shape='circle' className="!text-xs" prefixIcon={<Coins size={14} />}>
{t('已用')}: {renderQuota(record.used_quota)} {t('已用')}: {renderQuota(record.used_quota)}
</Tag> </Tag>
<Tag color='white' size='large' shape='circle' className="!text-xs"> <Tag color='white' size='large' shape='circle' className="!text-xs" prefixIcon={<Activity size={14} />}>
{t('调用')}: {renderNumber(record.request_count)} {t('调用')}: {renderNumber(record.request_count)}
</Tag> </Tag>
</Space> </Space>
@@ -126,13 +141,13 @@ const UsersTable = () => {
return ( return (
<div> <div>
<Space spacing={1}> <Space spacing={1}>
<Tag color='white' size='large' shape='circle' className="!text-xs"> <Tag color='white' size='large' shape='circle' className="!text-xs" prefixIcon={<Users size={14} />}>
{t('邀请')}: {renderNumber(record.aff_count)} {t('邀请')}: {renderNumber(record.aff_count)}
</Tag> </Tag>
<Tag color='white' size='large' shape='circle' className="!text-xs"> <Tag color='white' size='large' shape='circle' className="!text-xs" prefixIcon={<DollarSign size={14} />}>
{t('收益')}: {renderQuota(record.aff_history_quota)} {t('收益')}: {renderQuota(record.aff_history_quota)}
</Tag> </Tag>
<Tag color='white' size='large' shape='circle' className="!text-xs"> <Tag color='white' size='large' shape='circle' className="!text-xs" prefixIcon={<UserPlus size={14} />}>
{record.inviter_id === 0 ? t('无邀请人') : `邀请人: ${record.inviter_id}`} {record.inviter_id === 0 ? t('无邀请人') : `邀请人: ${record.inviter_id}`}
</Tag> </Tag>
</Space> </Space>
@@ -154,7 +169,7 @@ const UsersTable = () => {
return ( return (
<div> <div>
{record.DeletedAt !== null ? ( {record.DeletedAt !== null ? (
<Tag color='red' shape='circle'>{t('已注销')}</Tag> <Tag color='red' shape='circle' prefixIcon={<Minus size={14} />}>{t('已注销')}</Tag>
) : ( ) : (
renderStatus(text) renderStatus(text)
)} )}

View File

@@ -24,6 +24,13 @@ import {
XAI, XAI,
Ollama, Ollama,
Doubao, Doubao,
Suno,
Xinference,
OpenRouter,
Dify,
Coze,
SiliconCloud,
FastGPT
} from '@lobehub/icons'; } from '@lobehub/icons';
import { import {
@@ -40,6 +47,7 @@ import {
User, User,
Settings, Settings,
CircleUser, CircleUser,
Users
} from 'lucide-react'; } from 'lucide-react';
// 侧边栏图标颜色映射 // 侧边栏图标颜色映射
@@ -308,6 +316,88 @@ export const getModelCategories = (() => {
}; };
})(); })();
/**
* 根据渠道类型返回对应的厂商图标
* @param {number} channelType - 渠道类型值
* @returns {JSX.Element|null} - 对应的厂商图标组件
*/
export function getChannelIcon(channelType) {
const iconSize = 14;
switch (channelType) {
case 1: // OpenAI
case 3: // Azure OpenAI
return <OpenAI size={iconSize} />;
case 2: // Midjourney Proxy
case 5: // Midjourney Proxy Plus
return <Midjourney size={iconSize} />;
case 36: // Suno API
return <Suno size={iconSize} />;
case 4: // Ollama
return <Ollama size={iconSize} />;
case 14: // Anthropic Claude
case 33: // AWS Claude
return <Claude.Color size={iconSize} />;
case 41: // Vertex AI
return <Gemini.Color size={iconSize} />;
case 34: // Cohere
return <Cohere.Color size={iconSize} />;
case 39: // Cloudflare
return <Cloudflare.Color size={iconSize} />;
case 43: // DeepSeek
return <DeepSeek.Color size={iconSize} />;
case 15: // 百度文心千帆
case 46: // 百度文心千帆V2
return <Wenxin.Color size={iconSize} />;
case 17: // 阿里通义千问
return <Qwen.Color size={iconSize} />;
case 18: // 讯飞星火认知
return <Spark.Color size={iconSize} />;
case 16: // 智谱 ChatGLM
case 26: // 智谱 GLM-4V
return <Zhipu.Color size={iconSize} />;
case 24: // Google Gemini
case 11: // Google PaLM2
return <Gemini.Color size={iconSize} />;
case 47: // Xinference
return <Xinference.Color size={iconSize} />;
case 25: // Moonshot
return <Moonshot size={iconSize} />;
case 20: // OpenRouter
return <OpenRouter size={iconSize} />;
case 19: // 360 智脑
return <Ai360.Color size={iconSize} />;
case 23: // 腾讯混元
return <Hunyuan.Color size={iconSize} />;
case 31: // 零一万物
return <Yi.Color size={iconSize} />;
case 35: // MiniMax
return <Minimax.Color size={iconSize} />;
case 37: // Dify
return <Dify.Color size={iconSize} />;
case 38: // Jina
return <Jina size={iconSize} />;
case 40: // SiliconCloud
return <SiliconCloud.Color size={iconSize} />;
case 42: // Mistral AI
return <Mistral.Color size={iconSize} />;
case 45: // 字节火山方舟、豆包通用
return <Doubao.Color size={iconSize} />;
case 48: // xAI
return <XAI size={iconSize} />;
case 49: // Coze
return <Coze size={iconSize} />;
case 8: // 自定义渠道
case 22: // 知识库FastGPT
return <FastGPT.Color size={iconSize} />;
case 21: // 知识库AI Proxy
case 44: // 嵌入模型MokaAI M3E
default:
return null; // 未知类型或自定义渠道不显示图标
}
}
// 颜色列表 // 颜色列表
const colors = [ const colors = [
'amber', 'amber',
@@ -490,7 +580,7 @@ export function renderText(text, limit) {
export function renderGroup(group) { export function renderGroup(group) {
if (group === '') { if (group === '') {
return ( return (
<Tag size='large' key='default' color='orange' shape='circle'> <Tag size='large' key='default' color='orange' shape='circle' prefixIcon={<Users size={14} />}>
{i18next.t('用户分组')} {i18next.t('用户分组')}
</Tag> </Tag>
); );
@@ -513,13 +603,14 @@ export function renderGroup(group) {
color={tagColors[group] || stringToColor(group)} color={tagColors[group] || stringToColor(group)}
key={group} key={group}
shape='circle' shape='circle'
prefixIcon={<Users size={14} />}
onClick={async (event) => { onClick={async (event) => {
event.stopPropagation(); event.stopPropagation();
if (await copy(group)) { if (await copy(group)) {
showSuccess(i18next.t('已复制:') + group); showSuccess(i18next.t('已复制:') + group);
} else { } else {
Modal.error({ Modal.error({
title: t('无法复制到剪贴板,请手动复制'), title: i18next.t('无法复制到剪贴板,请手动复制'),
content: group, content: group,
}); });
} }
@@ -956,23 +1047,23 @@ export function renderModelPrice(
const extraServices = [ const extraServices = [
webSearch && webSearchCallCount > 0 webSearch && webSearchCallCount > 0
? i18next.t( ? i18next.t(
' + Web搜索 {{count}}次 / 1K 次 * ${{price}} * 分组倍率 {{ratio}}', ' + Web搜索 {{count}}次 / 1K 次 * ${{price}} * 分组倍率 {{ratio}}',
{ {
count: webSearchCallCount, count: webSearchCallCount,
price: webSearchPrice, price: webSearchPrice,
ratio: groupRatio, ratio: groupRatio,
}, },
) )
: '', : '',
fileSearch && fileSearchCallCount > 0 fileSearch && fileSearchCallCount > 0
? i18next.t( ? i18next.t(
' + 文件搜索 {{count}}次 / 1K 次 * ${{price}} * 分组倍率 {{ratio}}', ' + 文件搜索 {{count}}次 / 1K 次 * ${{price}} * 分组倍率 {{ratio}}',
{ {
count: fileSearchCallCount, count: fileSearchCallCount,
price: fileSearchPrice, price: fileSearchPrice,
ratio: groupRatio, ratio: groupRatio,
}, },
) )
: '', : '',
].join(''); ].join('');
@@ -1156,10 +1247,10 @@ export function renderAudioModelPrice(
let audioPrice = let audioPrice =
(audioInputTokens / 1000000) * inputRatioPrice * audioRatio * groupRatio + (audioInputTokens / 1000000) * inputRatioPrice * audioRatio * groupRatio +
(audioCompletionTokens / 1000000) * (audioCompletionTokens / 1000000) *
inputRatioPrice * inputRatioPrice *
audioRatio * audioRatio *
audioCompletionRatio * audioCompletionRatio *
groupRatio; groupRatio;
let price = textPrice + audioPrice; let price = textPrice + audioPrice;
return ( return (
<> <>
@@ -1215,27 +1306,27 @@ export function renderAudioModelPrice(
<p> <p>
{cacheTokens > 0 {cacheTokens > 0
? i18next.t( ? i18next.t(
'文字提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}', '文字提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
{ {
nonCacheInput: inputTokens - cacheTokens, nonCacheInput: inputTokens - cacheTokens,
cacheInput: cacheTokens, cacheInput: cacheTokens,
cachePrice: inputRatioPrice * cacheRatio, cachePrice: inputRatioPrice * cacheRatio,
price: inputRatioPrice, price: inputRatioPrice,
completion: completionTokens, completion: completionTokens,
compPrice: completionRatioPrice, compPrice: completionRatioPrice,
total: textPrice.toFixed(6), total: textPrice.toFixed(6),
}, },
) )
: i18next.t( : i18next.t(
'文字提示 {{input}} tokens / 1M tokens * ${{price}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}', '文字提示 {{input}} tokens / 1M tokens * ${{price}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
{ {
input: inputTokens, input: inputTokens,
price: inputRatioPrice, price: inputRatioPrice,
completion: completionTokens, completion: completionTokens,
compPrice: completionRatioPrice, compPrice: completionRatioPrice,
total: textPrice.toFixed(6), total: textPrice.toFixed(6),
}, },
)} )}
</p> </p>
<p> <p>
{i18next.t( {i18next.t(
@@ -1372,33 +1463,33 @@ export function renderClaudeModelPrice(
<p> <p>
{cacheTokens > 0 || cacheCreationTokens > 0 {cacheTokens > 0 || cacheCreationTokens > 0
? i18next.t( ? i18next.t(
'提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * ${{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * 分组 {{ratio}} = ${{total}}', '提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * ${{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * 分组 {{ratio}} = ${{total}}',
{ {
nonCacheInput: nonCachedTokens, nonCacheInput: nonCachedTokens,
cacheInput: cacheTokens, cacheInput: cacheTokens,
cacheRatio: cacheRatio, cacheRatio: cacheRatio,
cacheCreationInput: cacheCreationTokens, cacheCreationInput: cacheCreationTokens,
cacheCreationRatio: cacheCreationRatio, cacheCreationRatio: cacheCreationRatio,
cachePrice: cacheRatioPrice, cachePrice: cacheRatioPrice,
cacheCreationPrice: cacheCreationRatioPrice, cacheCreationPrice: cacheCreationRatioPrice,
price: inputRatioPrice, price: inputRatioPrice,
completion: completionTokens, completion: completionTokens,
compPrice: completionRatioPrice, compPrice: completionRatioPrice,
ratio: groupRatio, ratio: groupRatio,
total: price.toFixed(6), total: price.toFixed(6),
}, },
) )
: i18next.t( : i18next.t(
'提示 {{input}} tokens / 1M tokens * ${{price}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * 分组 {{ratio}} = ${{total}}', '提示 {{input}} tokens / 1M tokens * ${{price}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * 分组 {{ratio}} = ${{total}}',
{ {
input: inputTokens, input: inputTokens,
price: inputRatioPrice, price: inputRatioPrice,
completion: completionTokens, completion: completionTokens,
compPrice: completionRatioPrice, compPrice: completionRatioPrice,
ratio: groupRatio, ratio: groupRatio,
total: price.toFixed(6), total: price.toFixed(6),
}, },
)} )}
</p> </p>
<p>{i18next.t('仅供参考,以实际扣费为准')}</p> <p>{i18next.t('仅供参考,以实际扣费为准')}</p>
</article> </article>

View File

@@ -73,6 +73,7 @@ code {
.semi-page-item, .semi-page-item,
.semi-navigation-item, .semi-navigation-item,
.semi-tag-closable, .semi-tag-closable,
.semi-input-wrapper,
.semi-datepicker-range-input { .semi-datepicker-range-input {
border-radius: 9999px !important; border-radius: 9999px !important;
} }