diff --git a/web/src/components/table/users/UsersColumnDefs.js b/web/src/components/table/users/UsersColumnDefs.js
index d668760b..774554cb 100644
--- a/web/src/components/table/users/UsersColumnDefs.js
+++ b/web/src/components/table/users/UsersColumnDefs.js
@@ -20,31 +20,14 @@ For commercial licensing, please contact support@quantumnous.com
import React from 'react';
import {
Button,
- Dropdown,
Space,
Tag,
Tooltip,
- Typography
+ Progress,
+ Switch,
} from '@douyinfe/semi-ui';
-import {
- User,
- Shield,
- Crown,
- HelpCircle,
- CheckCircle,
- XCircle,
- Minus,
- Coins,
- Activity,
- Users,
- DollarSign,
- UserPlus,
-} from 'lucide-react';
-import { IconMore } from '@douyinfe/semi-icons';
import { renderGroup, renderNumber, renderQuota } from '../../../helpers';
-const { Text } = Typography;
-
/**
* Render user role
*/
@@ -52,53 +35,31 @@ const renderRole = (role, t) => {
switch (role) {
case 1:
return (
- }>
+
{t('普通用户')}
);
case 10:
return (
- }>
+
{t('管理员')}
);
case 100:
return (
- }>
+
{t('超级管理员')}
);
default:
return (
- }>
+
{t('未知身份')}
);
}
};
-/**
- * Render user status
- */
-const renderStatus = (status, t) => {
- switch (status) {
- case 1:
- return }>{t('已激活')};
- case 2:
- return (
- }>
- {t('已封禁')}
-
- );
- default:
- return (
- }>
- {t('未知状态')}
-
- );
- }
-};
-
/**
* Render username with remark
*/
@@ -127,22 +88,91 @@ const renderUsername = (text, record) => {
/**
* Render user statistics
*/
-const renderStatistics = (text, record, t) => {
- return (
-
-
- }>
- {t('剩余')}: {renderQuota(record.quota)}
-
- }>
- {t('已用')}: {renderQuota(record.used_quota)}
-
- }>
- {t('调用')}: {renderNumber(record.request_count)}
-
-
+const renderStatistics = (text, record, showEnableDisableModal, t) => {
+ const enabled = record.status === 1;
+ const isDeleted = record.DeletedAt !== null;
+
+ // Determine tag text & color like original status column
+ let tagColor = 'grey';
+ let tagText = t('未知状态');
+ if (isDeleted) {
+ tagColor = 'red';
+ tagText = t('已注销');
+ } else if (record.status === 1) {
+ tagColor = 'green';
+ tagText = t('已激活');
+ } else if (record.status === 2) {
+ tagColor = 'red';
+ tagText = t('已封禁');
+ }
+
+ const handleToggle = (checked) => {
+ if (checked) {
+ showEnableDisableModal(record, 'enable');
+ } else {
+ showEnableDisableModal(record, 'disable');
+ }
+ };
+
+ const used = parseInt(record.used_quota) || 0;
+ const remain = parseInt(record.quota) || 0;
+ const total = used + remain;
+ const percent = total > 0 ? (remain / total) * 100 : 0;
+
+ const getProgressColor = (pct) => {
+ if (pct === 100) return 'var(--semi-color-success)';
+ if (pct <= 10) return 'var(--semi-color-danger)';
+ if (pct <= 30) return 'var(--semi-color-warning)';
+ return undefined;
+ };
+
+ const quotaSuffix = (
+
+
{`${renderQuota(remain)} / ${renderQuota(total)}`}
+
);
+
+ const content = (
+
+ }
+ suffixIcon={quotaSuffix}
+ >
+ {tagText}
+
+ );
+
+ const tooltipContent = (
+
+
{t('已用额度')}: {renderQuota(used)}
+
{t('剩余额度')}: {renderQuota(remain)} ({percent.toFixed(0)}%)
+
{t('总额度')}: {renderQuota(total)}
+
{t('调用次数')}: {renderNumber(record.request_count)}
+
+ );
+
+ return (
+
+ {content}
+
+ );
};
/**
@@ -152,31 +182,20 @@ const renderInviteInfo = (text, record, t) => {
return (
- }>
+
{t('邀请')}: {renderNumber(record.aff_count)}
- }>
+
{t('收益')}: {renderQuota(record.aff_history_quota)}
- }>
- {record.inviter_id === 0 ? t('无邀请人') : `邀请人: ${record.inviter_id}`}
+
+ {record.inviter_id === 0 ? t('无邀请人') : `${t('邀请人')}: ${record.inviter_id}`}
);
};
-/**
- * Render overall status including deleted status
- */
-const renderOverallStatus = (status, record, t) => {
- if (record.DeletedAt !== null) {
- return
}>{t('已注销')};
- } else {
- return renderStatus(status, t);
- }
-};
-
/**
* Render operations column
*/
@@ -185,7 +204,6 @@ const renderOperations = (text, record, {
setShowEditUser,
showPromoteModal,
showDemoteModal,
- showEnableDisableModal,
showDeleteModal,
t
}) => {
@@ -193,46 +211,6 @@ const renderOperations = (text, record, {
return <>>;
}
- // Create more operations dropdown menu items
- const moreMenuItems = [
- {
- node: 'item',
- name: t('提升'),
- type: 'warning',
- onClick: () => showPromoteModal(record),
- },
- {
- node: 'item',
- name: t('降级'),
- type: 'secondary',
- onClick: () => showDemoteModal(record),
- },
- {
- node: 'item',
- name: t('注销'),
- type: 'danger',
- onClick: () => showDeleteModal(record),
- }
- ];
-
- // Add enable/disable button dynamically
- if (record.status === 1) {
- moreMenuItems.splice(-1, 0, {
- node: 'item',
- name: t('禁用'),
- type: 'warning',
- onClick: () => showEnableDisableModal(record, 'disable'),
- });
- } else {
- moreMenuItems.splice(-1, 0, {
- node: 'item',
- name: t('启用'),
- type: 'secondary',
- onClick: () => showEnableDisableModal(record, 'enable'),
- disabled: record.status === 3,
- });
- }
-
return (
- showPromoteModal(record)}
>
- }
- />
-
+ {t('提升')}
+
+
+
);
};
@@ -289,16 +277,6 @@ export const getUsersColumns = ({
return
{renderGroup(text)}
;
},
},
- {
- title: t('统计信息'),
- dataIndex: 'info',
- render: (text, record, index) => renderStatistics(text, record, t),
- },
- {
- title: t('邀请信息'),
- dataIndex: 'invite',
- render: (text, record, index) => renderInviteInfo(text, record, t),
- },
{
title: t('角色'),
dataIndex: 'role',
@@ -308,13 +286,19 @@ export const getUsersColumns = ({
},
{
title: t('状态'),
- dataIndex: 'status',
- render: (text, record, index) => renderOverallStatus(text, record, t),
+ dataIndex: 'info',
+ render: (text, record, index) => renderStatistics(text, record, showEnableDisableModal, t),
+ },
+ {
+ title: t('邀请信息'),
+ dataIndex: 'invite',
+ render: (text, record, index) => renderInviteInfo(text, record, t),
},
{
title: '',
dataIndex: 'operate',
fixed: 'right',
+ width: 200,
render: (text, record, index) => renderOperations(text, record, {
setEditingUser,
setShowEditUser,
diff --git a/web/src/hooks/users/useUsersData.js b/web/src/hooks/users/useUsersData.js
index 56211057..828c1118 100644
--- a/web/src/hooks/users/useUsersData.js
+++ b/web/src/hooks/users/useUsersData.js
@@ -121,30 +121,36 @@ export const useUsersData = () => {
// Manage user operations (promote, demote, enable, disable, delete)
const manageUser = async (userId, action, record) => {
+ // Trigger loading state to force table re-render
+ setLoading(true);
+
const res = await API.post('/api/user/manage', {
id: userId,
action,
});
+
const { success, message } = res.data;
if (success) {
showSuccess('操作成功完成!');
- let user = res.data.data;
- let newUsers = [...users];
- if (action === 'delete') {
- // Mark as deleted
- const index = newUsers.findIndex(u => u.id === userId);
- if (index > -1) {
- newUsers[index].DeletedAt = new Date();
+ const user = res.data.data;
+
+ // Create a new array and new object to ensure React detects changes
+ const newUsers = users.map((u) => {
+ if (u.id === userId) {
+ if (action === 'delete') {
+ return { ...u, DeletedAt: new Date() };
+ }
+ return { ...u, status: user.status, role: user.role };
}
- } else {
- // Update status and role
- record.status = user.status;
- record.role = user.role;
- }
+ return u;
+ });
+
setUsers(newUsers);
} else {
showError(message);
}
+
+ setLoading(false);
};
// Handle page change
diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json
index 23d1a5e8..92ad7bd7 100644
--- a/web/src/i18n/locales/en.json
+++ b/web/src/i18n/locales/en.json
@@ -390,7 +390,6 @@
"已封禁": "Banned",
"搜索用户的 ID,用户名,显示名称,以及邮箱地址 ...": "Search user ID, username, display name, and email address...",
"用户名": "Username",
- "统计信息": "Statistics",
"用户角色": "User Role",
"未绑定邮箱地址": "Email not bound",
"请求次数": "Number of Requests",
@@ -1483,6 +1482,7 @@
"剩余": "Remaining",
"已用": "Used",
"调用": "Calls",
+ "调用次数": "Call Count",
"邀请": "Invitations",
"收益": "Earnings",
"无邀请人": "No Inviter",