| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | import React from 'react'; |
| | import { |
| | Button, |
| | Space, |
| | Tag, |
| | Tooltip, |
| | Progress, |
| | Popover, |
| | Typography, |
| | Dropdown, |
| | } from '@douyinfe/semi-ui'; |
| | import { IconMore } from '@douyinfe/semi-icons'; |
| | import { renderGroup, renderNumber, renderQuota } from '../../../helpers'; |
| |
|
| | |
| | |
| | |
| | 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> |
| | ); |
| | } |
| | }; |
| |
|
| | |
| | |
| | |
| | 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> |
| | ); |
| | }; |
| |
|
| | |
| | |
| | |
| | const renderStatistics = (text, record, showEnableDisableModal, t) => { |
| | const isDeleted = record.DeletedAt !== null; |
| |
|
| | |
| | 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> |
| | ); |
| | }; |
| |
|
| | |
| | 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> |
| | ); |
| | }; |
| |
|
| | |
| | |
| | |
| | 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> |
| | ); |
| | }; |
| |
|
| | |
| | |
| | |
| | const renderOperations = ( |
| | text, |
| | record, |
| | { |
| | setEditingUser, |
| | setShowEditUser, |
| | showPromoteModal, |
| | showDemoteModal, |
| | showEnableDisableModal, |
| | showDeleteModal, |
| | showResetPasskeyModal, |
| | showResetTwoFAModal, |
| | t, |
| | }, |
| | ) => { |
| | if (record.DeletedAt !== null) { |
| | return <></>; |
| | } |
| |
|
| | const moreMenu = [ |
| | { |
| | node: 'item', |
| | name: t('重置 Passkey'), |
| | onClick: () => showResetPasskeyModal(record), |
| | }, |
| | { |
| | node: 'item', |
| | name: t('重置 2FA'), |
| | onClick: () => showResetTwoFAModal(record), |
| | }, |
| | { |
| | node: 'divider', |
| | }, |
| | { |
| | node: 'item', |
| | name: t('注销'), |
| | type: 'danger', |
| | onClick: () => showDeleteModal(record), |
| | }, |
| | ]; |
| |
|
| | 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> |
| | <Dropdown menu={moreMenu} trigger='click' position='bottomRight'> |
| | <Button type='tertiary' size='small' icon={<IconMore />} /> |
| | </Dropdown> |
| | </Space> |
| | ); |
| | }; |
| |
|
| | |
| | |
| | |
| | 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, |
| | }), |
| | }, |
| | ]; |
| | }; |
| |
|