| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- /*
- Copyright (C) 2025 QuantumNous
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
- For commercial licensing, please contact [email protected]
- */
- import React from 'react';
- import {
- Button,
- Space,
- Tag,
- Tooltip,
- Progress,
- Popover,
- Typography,
- } from '@douyinfe/semi-ui';
- import { renderGroup, renderNumber, renderQuota } from '../../../helpers';
- /**
- * Render user role
- */
- const renderRole = (role, t) => {
- switch (role) {
- case 1:
- return (
- <Tag color='blue' shape='circle'>
- {t('普通用户')}
- </Tag>
- );
- case 10:
- return (
- <Tag color='yellow' shape='circle'>
- {t('管理员')}
- </Tag>
- );
- case 100:
- return (
- <Tag color='orange' shape='circle'>
- {t('超级管理员')}
- </Tag>
- );
- default:
- return (
- <Tag color='red' shape='circle'>
- {t('未知身份')}
- </Tag>
- );
- }
- };
- /**
- * Render username with remark
- */
- const renderUsername = (text, record) => {
- const remark = record.remark;
- if (!remark) {
- return <span>{text}</span>;
- }
- const maxLen = 10;
- const displayRemark =
- remark.length > maxLen ? remark.slice(0, maxLen) + '…' : remark;
- return (
- <Space spacing={2}>
- <span>{text}</span>
- <Tooltip content={remark} position='top' showArrow>
- <Tag color='white' shape='circle' className='!text-xs'>
- <div className='flex items-center gap-1'>
- <div
- className='w-2 h-2 flex-shrink-0 rounded-full'
- style={{ backgroundColor: '#10b981' }}
- />
- {displayRemark}
- </div>
- </Tag>
- </Tooltip>
- </Space>
- );
- };
- /**
- * Render user statistics
- */
- const renderStatistics = (text, record, showEnableDisableModal, t) => {
- 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 content = (
- <Tag color={tagColor} shape='circle' size='small'>
- {tagText}
- </Tag>
- );
- const tooltipContent = (
- <div className='text-xs'>
- <div>
- {t('调用次数')}: {renderNumber(record.request_count)}
- </div>
- </div>
- );
- return (
- <Tooltip content={tooltipContent} position='top'>
- {content}
- </Tooltip>
- );
- };
- // Render separate quota usage column
- const renderQuotaUsage = (text, record, t) => {
- const { Paragraph } = Typography;
- 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 popoverContent = (
- <div className='text-xs p-2'>
- <Paragraph copyable={{ content: renderQuota(used) }}>
- {t('已用额度')}: {renderQuota(used)}
- </Paragraph>
- <Paragraph copyable={{ content: renderQuota(remain) }}>
- {t('剩余额度')}: {renderQuota(remain)} ({percent.toFixed(0)}%)
- </Paragraph>
- <Paragraph copyable={{ content: renderQuota(total) }}>
- {t('总额度')}: {renderQuota(total)}
- </Paragraph>
- </div>
- );
- return (
- <Popover content={popoverContent} position='top'>
- <Tag color='white' shape='circle'>
- <div className='flex flex-col items-end'>
- <span className='text-xs leading-none'>{`${renderQuota(remain)} / ${renderQuota(total)}`}</span>
- <Progress
- percent={percent}
- aria-label='quota usage'
- format={() => `${percent.toFixed(0)}%`}
- style={{ width: '100%', marginTop: '1px', marginBottom: 0 }}
- />
- </div>
- </Tag>
- </Popover>
- );
- };
- /**
- * Render invite information
- */
- const renderInviteInfo = (text, record, t) => {
- return (
- <div>
- <Space spacing={1}>
- <Tag color='white' shape='circle' className='!text-xs'>
- {t('邀请')}: {renderNumber(record.aff_count)}
- </Tag>
- <Tag color='white' shape='circle' className='!text-xs'>
- {t('收益')}: {renderQuota(record.aff_history_quota)}
- </Tag>
- <Tag color='white' shape='circle' className='!text-xs'>
- {record.inviter_id === 0
- ? t('无邀请人')
- : `${t('邀请人')}: ${record.inviter_id}`}
- </Tag>
- </Space>
- </div>
- );
- };
- /**
- * Render operations column
- */
- const renderOperations = (
- text,
- record,
- {
- setEditingUser,
- setShowEditUser,
- showPromoteModal,
- showDemoteModal,
- showEnableDisableModal,
- showDeleteModal,
- showResetPasskeyModal,
- showResetTwoFAModal,
- t,
- },
- ) => {
- if (record.DeletedAt !== null) {
- return <></>;
- }
- return (
- <Space>
- {record.status === 1 ? (
- <Button
- type='danger'
- size='small'
- onClick={() => showEnableDisableModal(record, 'disable')}
- >
- {t('禁用')}
- </Button>
- ) : (
- <Button
- size='small'
- onClick={() => showEnableDisableModal(record, 'enable')}
- >
- {t('启用')}
- </Button>
- )}
- <Button
- type='tertiary'
- size='small'
- onClick={() => {
- setEditingUser(record);
- setShowEditUser(true);
- }}
- >
- {t('编辑')}
- </Button>
- <Button
- type='warning'
- size='small'
- onClick={() => showPromoteModal(record)}
- >
- {t('提升')}
- </Button>
- <Button
- type='secondary'
- size='small'
- onClick={() => showDemoteModal(record)}
- >
- {t('降级')}
- </Button>
- <Button
- type='warning'
- size='small'
- onClick={() => showResetPasskeyModal(record)}
- >
- {t('重置 Passkey')}
- </Button>
- <Button
- type='warning'
- size='small'
- onClick={() => showResetTwoFAModal(record)}
- >
- {t('重置 2FA')}
- </Button>
- <Button
- type='danger'
- size='small'
- onClick={() => showDeleteModal(record)}
- >
- {t('注销')}
- </Button>
- </Space>
- );
- };
- /**
- * Get users table column definitions
- */
- export const getUsersColumns = ({
- t,
- setEditingUser,
- setShowEditUser,
- showPromoteModal,
- showDemoteModal,
- showEnableDisableModal,
- showDeleteModal,
- showResetPasskeyModal,
- showResetTwoFAModal,
- }) => {
- return [
- {
- title: 'ID',
- dataIndex: 'id',
- },
- {
- title: t('用户名'),
- dataIndex: 'username',
- render: (text, record) => renderUsername(text, record),
- },
- {
- title: t('状态'),
- dataIndex: 'info',
- render: (text, record, index) =>
- renderStatistics(text, record, showEnableDisableModal, t),
- },
- {
- title: t('剩余额度/总额度'),
- key: 'quota_usage',
- render: (text, record) => renderQuotaUsage(text, record, t),
- },
- {
- title: t('分组'),
- dataIndex: 'group',
- render: (text, record, index) => {
- return <div>{renderGroup(text)}</div>;
- },
- },
- {
- title: t('角色'),
- dataIndex: 'role',
- render: (text, record, index) => {
- return <div>{renderRole(text, t)}</div>;
- },
- },
- {
- 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,
- showPromoteModal,
- showDemoteModal,
- showEnableDisableModal,
- showDeleteModal,
- showResetPasskeyModal,
- showResetTwoFAModal,
- t,
- }),
- },
- ];
- };
|