Sfoglia il codice sorgente

✨ 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.
Apple\Apple 9 mesi fa
parent
commit
8184357b49

+ 33 - 13
web/src/components/table/ChannelsTable.js

@@ -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>

+ 31 - 16
web/src/components/table/LogsTable.js

@@ -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);

+ 60 - 33
web/src/components/table/MjLogsTable.js

@@ -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);
               }}
               }}

+ 21 - 7
web/src/components/table/RedemptionsTable.js

@@ -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>
+        );
       },
       },
     },
     },
     {
     {

+ 32 - 18
web/src/components/table/TaskLogsTable.js

@@ -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);
               }}
               }}

+ 48 - 12
web/src/components/table/TokensTable.js

@@ -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>
             )}
             )}

+ 31 - 16
web/src/components/table/UsersTable.js

@@ -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)
             )}
             )}

+ 157 - 66
web/src/helpers/render.js

@@ -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}}',
-                      {
-                        count: webSearchCallCount,
-                        price: webSearchPrice,
-                        ratio: groupRatio,
-                      },
-                    )
+                    ' + Web搜索 {{count}}次 / 1K 次 * ${{price}} * 分组倍率 {{ratio}}',
+                    {
+                      count: webSearchCallCount,
+                      price: webSearchPrice,
+                      ratio: groupRatio,
+                    },
+                  )
                   : '',
                   : '',
                 fileSearch && fileSearchCallCount > 0
                 fileSearch && fileSearchCallCount > 0
                   ? i18next.t(
                   ? i18next.t(
-                      ' + 文件搜索 {{count}}次 / 1K 次 * ${{price}} * 分组倍率 {{ratio}}',
-                      {
-                        count: fileSearchCallCount,
-                        price: fileSearchPrice,
-                        ratio: groupRatio,
-                      },
-                    )
+                    ' + 文件搜索 {{count}}次 / 1K 次 * ${{price}} * 分组倍率 {{ratio}}',
+                    {
+                      count: fileSearchCallCount,
+                      price: fileSearchPrice,
+                      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 *
-        audioRatio *
-        audioCompletionRatio *
-        groupRatio;
+      inputRatioPrice *
+      audioRatio *
+      audioCompletionRatio *
+      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: inputTokens - cacheTokens,
-                    cacheInput: cacheTokens,
-                    cachePrice: inputRatioPrice * cacheRatio,
-                    price: inputRatioPrice,
-                    completion: completionTokens,
-                    compPrice: completionRatioPrice,
-                    total: textPrice.toFixed(6),
-                  },
-                )
+                '文字提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
+                {
+                  nonCacheInput: inputTokens - cacheTokens,
+                  cacheInput: cacheTokens,
+                  cachePrice: inputRatioPrice * cacheRatio,
+                  price: inputRatioPrice,
+                  completion: completionTokens,
+                  compPrice: completionRatioPrice,
+                  total: textPrice.toFixed(6),
+                },
+              )
               : i18next.t(
               : i18next.t(
-                  '文字提示 {{input}} tokens / 1M tokens * ${{price}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
-                  {
-                    input: inputTokens,
-                    price: inputRatioPrice,
-                    completion: completionTokens,
-                    compPrice: completionRatioPrice,
-                    total: textPrice.toFixed(6),
-                  },
-                )}
+                '文字提示 {{input}} tokens / 1M tokens * ${{price}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
+                {
+                  input: inputTokens,
+                  price: inputRatioPrice,
+                  completion: completionTokens,
+                  compPrice: completionRatioPrice,
+                  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: nonCachedTokens,
-                    cacheInput: cacheTokens,
-                    cacheRatio: cacheRatio,
-                    cacheCreationInput: cacheCreationTokens,
-                    cacheCreationRatio: cacheCreationRatio,
-                    cachePrice: cacheRatioPrice,
-                    cacheCreationPrice: cacheCreationRatioPrice,
-                    price: inputRatioPrice,
-                    completion: completionTokens,
-                    compPrice: completionRatioPrice,
-                    ratio: groupRatio,
-                    total: price.toFixed(6),
-                  },
-                )
+                '提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * ${{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * 分组 {{ratio}} = ${{total}}',
+                {
+                  nonCacheInput: nonCachedTokens,
+                  cacheInput: cacheTokens,
+                  cacheRatio: cacheRatio,
+                  cacheCreationInput: cacheCreationTokens,
+                  cacheCreationRatio: cacheCreationRatio,
+                  cachePrice: cacheRatioPrice,
+                  cacheCreationPrice: cacheCreationRatioPrice,
+                  price: inputRatioPrice,
+                  completion: completionTokens,
+                  compPrice: completionRatioPrice,
+                  ratio: groupRatio,
+                  total: price.toFixed(6),
+                },
+              )
               : i18next.t(
               : i18next.t(
-                  '提示 {{input}} tokens / 1M tokens * ${{price}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * 分组 {{ratio}} = ${{total}}',
-                  {
-                    input: inputTokens,
-                    price: inputRatioPrice,
-                    completion: completionTokens,
-                    compPrice: completionRatioPrice,
-                    ratio: groupRatio,
-                    total: price.toFixed(6),
-                  },
-                )}
+                '提示 {{input}} tokens / 1M tokens * ${{price}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * 分组 {{ratio}} = ${{total}}',
+                {
+                  input: inputTokens,
+                  price: inputRatioPrice,
+                  completion: completionTokens,
+                  compPrice: completionRatioPrice,
+                  ratio: groupRatio,
+                  total: price.toFixed(6),
+                },
+              )}
           </p>
           </p>
           <p>{i18next.t('仅供参考,以实际扣费为准')}</p>
           <p>{i18next.t('仅供参考,以实际扣费为准')}</p>
         </article>
         </article>

+ 1 - 0
web/src/index.css

@@ -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;
 }
 }