| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import React from 'react'; |
| import { |
| Button, |
| Dropdown, |
| InputNumber, |
| Modal, |
| Space, |
| SplitButtonGroup, |
| Tag, |
| Tooltip, |
| Typography, |
| } from '@douyinfe/semi-ui'; |
| import { |
| timestamp2string, |
| renderGroup, |
| renderQuota, |
| getChannelIcon, |
| renderQuotaWithAmount, |
| showSuccess, |
| showError, |
| } from '../../../helpers'; |
| import { CHANNEL_OPTIONS } from '../../../constants'; |
| import { IconTreeTriangleDown, IconMore } from '@douyinfe/semi-icons'; |
| import { FaRandom } from 'react-icons/fa'; |
|
|
| |
| const renderType = (type, channelInfo = undefined, t) => { |
| let type2label = new Map(); |
| for (let i = 0; i < CHANNEL_OPTIONS.length; i++) { |
| type2label[CHANNEL_OPTIONS[i].value] = CHANNEL_OPTIONS[i]; |
| } |
| type2label[0] = { value: 0, label: t('未知类型'), color: 'grey' }; |
|
|
| let icon = getChannelIcon(type); |
|
|
| if (channelInfo?.is_multi_key) { |
| icon = |
| channelInfo?.multi_key_mode === 'random' ? ( |
| <div className='flex items-center gap-1'> |
| <FaRandom className='text-blue-500' /> |
| {icon} |
| </div> |
| ) : ( |
| <div className='flex items-center gap-1'> |
| <IconTreeTriangleDown className='text-blue-500' /> |
| {icon} |
| </div> |
| ); |
| } |
|
|
| return ( |
| <Tag color={type2label[type]?.color} shape='circle' prefixIcon={icon}> |
| {type2label[type]?.label} |
| </Tag> |
| ); |
| }; |
|
|
| const renderTagType = (t) => { |
| return ( |
| <Tag color='light-blue' shape='circle' type='light'> |
| {t('标签聚合')} |
| </Tag> |
| ); |
| }; |
|
|
| const renderStatus = (status, channelInfo = undefined, t) => { |
| if (channelInfo) { |
| if (channelInfo.is_multi_key) { |
| let keySize = channelInfo.multi_key_size; |
| let enabledKeySize = keySize; |
| if (channelInfo.multi_key_status_list) { |
| enabledKeySize = |
| keySize - Object.keys(channelInfo.multi_key_status_list).length; |
| } |
| return renderMultiKeyStatus(status, keySize, enabledKeySize, t); |
| } |
| } |
| switch (status) { |
| case 1: |
| return ( |
| <Tag color='green' shape='circle'> |
| {t('已启用')} |
| </Tag> |
| ); |
| case 2: |
| return ( |
| <Tag color='red' shape='circle'> |
| {t('已禁用')} |
| </Tag> |
| ); |
| case 3: |
| return ( |
| <Tag color='yellow' shape='circle'> |
| {t('自动禁用')} |
| </Tag> |
| ); |
| default: |
| return ( |
| <Tag color='grey' shape='circle'> |
| {t('未知状态')} |
| </Tag> |
| ); |
| } |
| }; |
|
|
| const renderMultiKeyStatus = (status, keySize, enabledKeySize, t) => { |
| switch (status) { |
| case 1: |
| return ( |
| <Tag color='green' shape='circle'> |
| {t('已启用')} {enabledKeySize}/{keySize} |
| </Tag> |
| ); |
| case 2: |
| return ( |
| <Tag color='red' shape='circle'> |
| {t('已禁用')} {enabledKeySize}/{keySize} |
| </Tag> |
| ); |
| case 3: |
| return ( |
| <Tag color='yellow' shape='circle'> |
| {t('自动禁用')} {enabledKeySize}/{keySize} |
| </Tag> |
| ); |
| default: |
| return ( |
| <Tag color='grey' shape='circle'> |
| {t('未知状态')} {enabledKeySize}/{keySize} |
| </Tag> |
| ); |
| } |
| }; |
|
|
| const renderResponseTime = (responseTime, t) => { |
| let time = responseTime / 1000; |
| time = time.toFixed(2) + t(' 秒'); |
| if (responseTime === 0) { |
| return ( |
| <Tag color='grey' shape='circle'> |
| {t('未测试')} |
| </Tag> |
| ); |
| } else if (responseTime <= 1000) { |
| return ( |
| <Tag color='green' shape='circle'> |
| {time} |
| </Tag> |
| ); |
| } else if (responseTime <= 3000) { |
| return ( |
| <Tag color='lime' shape='circle'> |
| {time} |
| </Tag> |
| ); |
| } else if (responseTime <= 5000) { |
| return ( |
| <Tag color='yellow' shape='circle'> |
| {time} |
| </Tag> |
| ); |
| } else { |
| return ( |
| <Tag color='red' shape='circle'> |
| {time} |
| </Tag> |
| ); |
| } |
| }; |
|
|
| export const getChannelsColumns = ({ |
| t, |
| COLUMN_KEYS, |
| updateChannelBalance, |
| manageChannel, |
| manageTag, |
| submitTagEdit, |
| testChannel, |
| setCurrentTestChannel, |
| setShowModelTestModal, |
| setEditingChannel, |
| setShowEdit, |
| setShowEditTag, |
| setEditingTag, |
| copySelectedChannel, |
| refresh, |
| activePage, |
| channels, |
| setShowMultiKeyManageModal, |
| setCurrentMultiKeyChannel, |
| }) => { |
| return [ |
| { |
| key: COLUMN_KEYS.ID, |
| title: t('ID'), |
| dataIndex: 'id', |
| }, |
| { |
| key: COLUMN_KEYS.NAME, |
| title: t('名称'), |
| dataIndex: 'name', |
| render: (text, record, index) => { |
| if (record.remark && record.remark.trim() !== '') { |
| return ( |
| <Tooltip |
| content={ |
| <div className='flex flex-col gap-2 max-w-xs'> |
| <div className='text-sm'>{record.remark}</div> |
| <Button |
| size='small' |
| type='primary' |
| theme='outline' |
| onClick={(e) => { |
| e.stopPropagation(); |
| navigator.clipboard |
| .writeText(record.remark) |
| .then(() => { |
| showSuccess(t('复制成功')); |
| }) |
| .catch(() => { |
| showError(t('复制失败')); |
| }); |
| }} |
| > |
| {t('复制')} |
| </Button> |
| </div> |
| } |
| trigger='hover' |
| position='topLeft' |
| > |
| <span>{text}</span> |
| </Tooltip> |
| ); |
| } |
| return text; |
| }, |
| }, |
| { |
| key: COLUMN_KEYS.GROUP, |
| title: t('分组'), |
| dataIndex: 'group', |
| render: (text, record, index) => ( |
| <div> |
| <Space spacing={2}> |
| {text |
| ?.split(',') |
| .sort((a, b) => { |
| if (a === 'default') return -1; |
| if (b === 'default') return 1; |
| return a.localeCompare(b); |
| }) |
| .map((item, index) => renderGroup(item))} |
| </Space> |
| </div> |
| ), |
| }, |
| { |
| key: COLUMN_KEYS.TYPE, |
| title: t('类型'), |
| dataIndex: 'type', |
| render: (text, record, index) => { |
| if (record.children === undefined) { |
| if (record.channel_info) { |
| if (record.channel_info.is_multi_key) { |
| return <>{renderType(text, record.channel_info, t)}</>; |
| } |
| } |
| return <>{renderType(text, undefined, t)}</>; |
| } else { |
| return <>{renderTagType(t)}</>; |
| } |
| }, |
| }, |
| { |
| key: COLUMN_KEYS.STATUS, |
| title: t('状态'), |
| dataIndex: 'status', |
| render: (text, record, index) => { |
| if (text === 3) { |
| if (record.other_info === '') { |
| record.other_info = '{}'; |
| } |
| let otherInfo = JSON.parse(record.other_info); |
| let reason = otherInfo['status_reason']; |
| let time = otherInfo['status_time']; |
| return ( |
| <div> |
| <Tooltip |
| content={ |
| t('原因:') + reason + t(',时间:') + timestamp2string(time) |
| } |
| > |
| {renderStatus(text, record.channel_info, t)} |
| </Tooltip> |
| </div> |
| ); |
| } else { |
| return renderStatus(text, record.channel_info, t); |
| } |
| }, |
| }, |
| { |
| key: COLUMN_KEYS.RESPONSE_TIME, |
| title: t('响应时间'), |
| dataIndex: 'response_time', |
| render: (text, record, index) => <div>{renderResponseTime(text, t)}</div>, |
| }, |
| { |
| key: COLUMN_KEYS.BALANCE, |
| title: t('已用/剩余'), |
| dataIndex: 'expired_time', |
| render: (text, record, index) => { |
| if (record.children === undefined) { |
| return ( |
| <div> |
| <Space spacing={1}> |
| <Tooltip content={t('已用额度')}> |
| <Tag color='white' type='ghost' shape='circle'> |
| {renderQuota(record.used_quota)} |
| </Tag> |
| </Tooltip> |
| <Tooltip |
| content={t('剩余额度$') + record.balance + t(',点击更新')} |
| > |
| <Tag |
| color='white' |
| type='ghost' |
| shape='circle' |
| onClick={() => updateChannelBalance(record)} |
| > |
| {renderQuotaWithAmount(record.balance)} |
| </Tag> |
| </Tooltip> |
| </Space> |
| </div> |
| ); |
| } else { |
| return ( |
| <Tooltip content={t('已用额度')}> |
| <Tag color='white' type='ghost' shape='circle'> |
| {renderQuota(record.used_quota)} |
| </Tag> |
| </Tooltip> |
| ); |
| } |
| }, |
| }, |
| { |
| key: COLUMN_KEYS.PRIORITY, |
| title: t('优先级'), |
| dataIndex: 'priority', |
| render: (text, record, index) => { |
| if (record.children === undefined) { |
| return ( |
| <div> |
| <InputNumber |
| style={{ width: 70 }} |
| name='priority' |
| onBlur={(e) => { |
| manageChannel(record.id, 'priority', record, e.target.value); |
| }} |
| keepFocus={true} |
| innerButtons |
| defaultValue={record.priority} |
| min={-999} |
| size='small' |
| /> |
| </div> |
| ); |
| } else { |
| return ( |
| <InputNumber |
| style={{ width: 70 }} |
| name='priority' |
| keepFocus={true} |
| onBlur={(e) => { |
| Modal.warning({ |
| title: t('修改子渠道优先级'), |
| content: |
| t('确定要修改所有子渠道优先级为 ') + |
| e.target.value + |
| t(' 吗?'), |
| onOk: () => { |
| if (e.target.value === '') { |
| return; |
| } |
| submitTagEdit('priority', { |
| tag: record.key, |
| priority: e.target.value, |
| }); |
| }, |
| }); |
| }} |
| innerButtons |
| defaultValue={record.priority} |
| min={-999} |
| size='small' |
| /> |
| ); |
| } |
| }, |
| }, |
| { |
| key: COLUMN_KEYS.WEIGHT, |
| title: t('权重'), |
| dataIndex: 'weight', |
| render: (text, record, index) => { |
| if (record.children === undefined) { |
| return ( |
| <div> |
| <InputNumber |
| style={{ width: 70 }} |
| name='weight' |
| onBlur={(e) => { |
| manageChannel(record.id, 'weight', record, e.target.value); |
| }} |
| keepFocus={true} |
| innerButtons |
| defaultValue={record.weight} |
| min={0} |
| size='small' |
| /> |
| </div> |
| ); |
| } else { |
| return ( |
| <InputNumber |
| style={{ width: 70 }} |
| name='weight' |
| keepFocus={true} |
| onBlur={(e) => { |
| Modal.warning({ |
| title: t('修改子渠道权重'), |
| content: |
| t('确定要修改所有子渠道权重为 ') + |
| e.target.value + |
| t(' 吗?'), |
| onOk: () => { |
| if (e.target.value === '') { |
| return; |
| } |
| submitTagEdit('weight', { |
| tag: record.key, |
| weight: e.target.value, |
| }); |
| }, |
| }); |
| }} |
| innerButtons |
| defaultValue={record.weight} |
| min={-999} |
| size='small' |
| /> |
| ); |
| } |
| }, |
| }, |
| { |
| key: COLUMN_KEYS.OPERATE, |
| title: '', |
| dataIndex: 'operate', |
| fixed: 'right', |
| render: (text, record, index) => { |
| if (record.children === undefined) { |
| const moreMenuItems = [ |
| { |
| node: 'item', |
| name: t('删除'), |
| type: 'danger', |
| onClick: () => { |
| Modal.confirm({ |
| title: t('确定是否要删除此渠道?'), |
| content: t('此修改将不可逆'), |
| onOk: () => { |
| (async () => { |
| await manageChannel(record.id, 'delete', record); |
| await refresh(); |
| setTimeout(() => { |
| if (channels.length === 0 && activePage > 1) { |
| refresh(activePage - 1); |
| } |
| }, 100); |
| })(); |
| }, |
| }); |
| }, |
| }, |
| { |
| node: 'item', |
| name: t('复制'), |
| type: 'tertiary', |
| onClick: () => { |
| Modal.confirm({ |
| title: t('确定是否要复制此渠道?'), |
| content: t('复制渠道的所有信息'), |
| onOk: () => copySelectedChannel(record), |
| }); |
| }, |
| }, |
| ]; |
|
|
| return ( |
| <Space wrap> |
| <SplitButtonGroup |
| className='overflow-hidden' |
| aria-label={t('测试单个渠道操作项目组')} |
| > |
| <Button |
| size='small' |
| type='tertiary' |
| onClick={() => testChannel(record, '')} |
| > |
| {t('测试')} |
| </Button> |
| <Button |
| size='small' |
| type='tertiary' |
| icon={<IconTreeTriangleDown />} |
| onClick={() => { |
| setCurrentTestChannel(record); |
| setShowModelTestModal(true); |
| }} |
| /> |
| </SplitButtonGroup> |
| |
| {record.status === 1 ? ( |
| <Button |
| type='danger' |
| size='small' |
| onClick={() => manageChannel(record.id, 'disable', record)} |
| > |
| {t('禁用')} |
| </Button> |
| ) : ( |
| <Button |
| size='small' |
| onClick={() => manageChannel(record.id, 'enable', record)} |
| > |
| {t('启用')} |
| </Button> |
| )} |
| |
| {record.channel_info?.is_multi_key ? ( |
| <SplitButtonGroup aria-label={t('多密钥渠道操作项目组')}> |
| <Button |
| type='tertiary' |
| size='small' |
| onClick={() => { |
| setEditingChannel(record); |
| setShowEdit(true); |
| }} |
| > |
| {t('编辑')} |
| </Button> |
| <Dropdown |
| trigger='click' |
| position='bottomRight' |
| menu={[ |
| { |
| node: 'item', |
| name: t('多密钥管理'), |
| onClick: () => { |
| setCurrentMultiKeyChannel(record); |
| setShowMultiKeyManageModal(true); |
| }, |
| }, |
| ]} |
| > |
| <Button |
| type='tertiary' |
| size='small' |
| icon={<IconTreeTriangleDown />} |
| /> |
| </Dropdown> |
| </SplitButtonGroup> |
| ) : ( |
| <Button |
| type='tertiary' |
| size='small' |
| onClick={() => { |
| setEditingChannel(record); |
| setShowEdit(true); |
| }} |
| > |
| {t('编辑')} |
| </Button> |
| )} |
| |
| <Dropdown |
| trigger='click' |
| position='bottomRight' |
| menu={moreMenuItems} |
| > |
| <Button icon={<IconMore />} type='tertiary' size='small' /> |
| </Dropdown> |
| </Space> |
| ); |
| } else { |
| |
| return ( |
| <Space wrap> |
| <Button |
| type='tertiary' |
| size='small' |
| onClick={() => manageTag(record.key, 'enable')} |
| > |
| {t('启用全部')} |
| </Button> |
| <Button |
| type='tertiary' |
| size='small' |
| onClick={() => manageTag(record.key, 'disable')} |
| > |
| {t('禁用全部')} |
| </Button> |
| <Button |
| type='tertiary' |
| size='small' |
| onClick={() => { |
| setShowEditTag(true); |
| setEditingTag(record.key); |
| }} |
| > |
| {t('编辑')} |
| </Button> |
| </Space> |
| ); |
| } |
| }, |
| }, |
| ]; |
| }; |
|
|