2024-03-15 16:05:33 +08:00
|
|
|
|
import React, { useEffect, useState } from 'react';
|
2024-03-23 21:24:39 +08:00
|
|
|
|
import {
|
|
|
|
|
|
API,
|
|
|
|
|
|
isMobile,
|
|
|
|
|
|
shouldShowPrompt,
|
|
|
|
|
|
showError,
|
|
|
|
|
|
showInfo,
|
|
|
|
|
|
showSuccess,
|
2024-05-16 18:31:03 +08:00
|
|
|
|
showWarning,
|
2024-11-19 01:13:18 +08:00
|
|
|
|
timestamp2string
|
2024-03-23 21:24:39 +08:00
|
|
|
|
} from '../helpers';
|
2024-03-15 16:05:33 +08:00
|
|
|
|
|
|
|
|
|
|
import { CHANNEL_OPTIONS, ITEMS_PER_PAGE } from '../constants';
|
2023-12-05 18:15:40 +08:00
|
|
|
|
import {
|
2024-03-23 21:24:39 +08:00
|
|
|
|
renderGroup,
|
|
|
|
|
|
renderNumberWithPoint,
|
2024-11-19 01:13:18 +08:00
|
|
|
|
renderQuota
|
2024-03-23 21:24:39 +08:00
|
|
|
|
} from '../helpers/render';
|
|
|
|
|
|
import {
|
2024-11-12 17:00:06 +08:00
|
|
|
|
Button, Divider,
|
2024-03-23 21:24:39 +08:00
|
|
|
|
Dropdown,
|
|
|
|
|
|
Form,
|
|
|
|
|
|
InputNumber,
|
|
|
|
|
|
Popconfirm,
|
|
|
|
|
|
Space,
|
|
|
|
|
|
SplitButtonGroup,
|
|
|
|
|
|
Switch,
|
|
|
|
|
|
Table,
|
|
|
|
|
|
Tag,
|
|
|
|
|
|
Tooltip,
|
2024-11-19 01:13:18 +08:00
|
|
|
|
Typography
|
2024-03-15 16:05:33 +08:00
|
|
|
|
} from '@douyinfe/semi-ui';
|
|
|
|
|
|
import EditChannel from '../pages/Channel/EditChannel';
|
|
|
|
|
|
import { IconTreeTriangleDown } from '@douyinfe/semi-icons';
|
2024-05-12 19:07:33 +08:00
|
|
|
|
import { loadChannelModels } from './utils.js';
|
2024-11-19 01:13:18 +08:00
|
|
|
|
import EditTagModal from '../pages/Channel/EditTagModal.js';
|
2023-04-22 21:41:16 +08:00
|
|
|
|
|
2023-04-23 15:42:23 +08:00
|
|
|
|
function renderTimestamp(timestamp) {
|
2024-03-23 21:24:39 +08:00
|
|
|
|
return <>{timestamp2string(timestamp)}</>;
|
2023-04-23 15:42:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let type2label = undefined;
|
|
|
|
|
|
|
|
|
|
|
|
function renderType(type) {
|
2024-03-15 16:05:33 +08:00
|
|
|
|
if (!type2label) {
|
|
|
|
|
|
type2label = new Map();
|
|
|
|
|
|
for (let i = 0; i < CHANNEL_OPTIONS.length; i++) {
|
|
|
|
|
|
type2label[CHANNEL_OPTIONS[i].value] = CHANNEL_OPTIONS[i];
|
2023-04-23 15:42:23 +08:00
|
|
|
|
}
|
2024-03-15 16:05:33 +08:00
|
|
|
|
type2label[0] = { value: 0, text: '未知类型', color: 'grey' };
|
|
|
|
|
|
}
|
2024-03-23 21:24:39 +08:00
|
|
|
|
return (
|
2024-11-19 01:13:18 +08:00
|
|
|
|
<Tag size="large" color={type2label[type]?.color}>
|
2024-08-06 15:40:44 +08:00
|
|
|
|
{type2label[type]?.text}
|
|
|
|
|
|
</Tag>
|
2024-03-23 21:24:39 +08:00
|
|
|
|
);
|
2023-04-22 21:41:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const ChannelsTable = () => {
|
2024-03-15 16:05:33 +08:00
|
|
|
|
const columns = [
|
|
|
|
|
|
// {
|
|
|
|
|
|
// title: '',
|
|
|
|
|
|
// dataIndex: 'checkbox',
|
|
|
|
|
|
// className: 'checkbox',
|
|
|
|
|
|
// },
|
|
|
|
|
|
{
|
|
|
|
|
|
title: 'ID',
|
2024-11-19 01:13:18 +08:00
|
|
|
|
dataIndex: 'id'
|
2024-03-15 16:05:33 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '名称',
|
2024-11-19 01:13:18 +08:00
|
|
|
|
dataIndex: 'name'
|
2024-03-15 16:05:33 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '分组',
|
|
|
|
|
|
dataIndex: 'group',
|
|
|
|
|
|
render: (text, record, index) => {
|
|
|
|
|
|
return (
|
2024-08-06 15:40:44 +08:00
|
|
|
|
<div>
|
|
|
|
|
|
<Space spacing={2}>
|
2024-11-19 01:13:18 +08:00
|
|
|
|
{text?.split(',').map((item, index) => {
|
2024-08-06 15:40:44 +08:00
|
|
|
|
return renderGroup(item);
|
|
|
|
|
|
})}
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
</div>
|
2024-03-15 16:05:33 +08:00
|
|
|
|
);
|
2024-11-19 01:13:18 +08:00
|
|
|
|
}
|
2024-03-15 16:05:33 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '类型',
|
|
|
|
|
|
dataIndex: 'type',
|
|
|
|
|
|
render: (text, record, index) => {
|
2024-03-23 21:24:39 +08:00
|
|
|
|
return <div>{renderType(text)}</div>;
|
2024-11-19 01:13:18 +08:00
|
|
|
|
}
|
2024-03-15 16:05:33 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '状态',
|
|
|
|
|
|
dataIndex: 'status',
|
|
|
|
|
|
render: (text, record, index) => {
|
2024-06-16 14:53:39 +08:00
|
|
|
|
if (text === 3) {
|
|
|
|
|
|
if (record.other_info === '') {
|
2024-11-19 01:13:18 +08:00
|
|
|
|
record.other_info = '{}';
|
2024-06-16 14:53:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
let otherInfo = JSON.parse(record.other_info);
|
|
|
|
|
|
let reason = otherInfo['status_reason'];
|
|
|
|
|
|
let time = otherInfo['status_time'];
|
|
|
|
|
|
return (
|
2024-08-06 15:40:44 +08:00
|
|
|
|
<div>
|
|
|
|
|
|
<Tooltip content={'原因:' + reason + ',时间:' + timestamp2string(time)}>
|
|
|
|
|
|
{renderStatus(text)}
|
|
|
|
|
|
</Tooltip>
|
|
|
|
|
|
</div>
|
2024-06-16 14:53:39 +08:00
|
|
|
|
);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return renderStatus(text);
|
|
|
|
|
|
}
|
2024-11-19 01:13:18 +08:00
|
|
|
|
}
|
2024-03-15 16:05:33 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '响应时间',
|
|
|
|
|
|
dataIndex: 'response_time',
|
|
|
|
|
|
render: (text, record, index) => {
|
2024-03-23 21:24:39 +08:00
|
|
|
|
return <div>{renderResponseTime(text)}</div>;
|
2024-11-19 01:13:18 +08:00
|
|
|
|
}
|
2024-03-15 16:05:33 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '已用/剩余',
|
|
|
|
|
|
dataIndex: 'expired_time',
|
|
|
|
|
|
render: (text, record, index) => {
|
2024-11-19 01:13:18 +08:00
|
|
|
|
if (record.children === undefined) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<Space spacing={1}>
|
|
|
|
|
|
<Tooltip content={'已用额度'}>
|
|
|
|
|
|
<Tag color="white" type="ghost" size="large">
|
|
|
|
|
|
{renderQuota(record.used_quota)}
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
</Tooltip>
|
|
|
|
|
|
<Tooltip content={'剩余额度' + record.balance + ',点击更新'}>
|
|
|
|
|
|
<Tag
|
|
|
|
|
|
color="white"
|
|
|
|
|
|
type="ghost"
|
|
|
|
|
|
size="large"
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
updateChannelBalance(record);
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
${renderNumberWithPoint(record.balance)}
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
</Tooltip>
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return <Tooltip content={'已用额度'}>
|
|
|
|
|
|
<Tag color="white" type="ghost" size="large">
|
|
|
|
|
|
{renderQuota(record.used_quota)}
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
</Tooltip>;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-03-15 16:05:33 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '优先级',
|
|
|
|
|
|
dataIndex: 'priority',
|
|
|
|
|
|
render: (text, record, index) => {
|
2024-11-19 01:13:18 +08:00
|
|
|
|
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}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return <>
|
|
|
|
|
|
<Button theme="outline" type="primary">修改</Button>
|
|
|
|
|
|
</>;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-03-15 16:05:33 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '权重',
|
|
|
|
|
|
dataIndex: 'weight',
|
|
|
|
|
|
render: (text, record, index) => {
|
2024-11-19 01:13:18 +08:00
|
|
|
|
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}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Button
|
|
|
|
|
|
theme="outline"
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
>
|
|
|
|
|
|
修改
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-03-15 16:05:33 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '',
|
|
|
|
|
|
dataIndex: 'operate',
|
2024-11-19 01:13:18 +08:00
|
|
|
|
render: (text, record, index) => {
|
|
|
|
|
|
if (record.children === undefined) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<SplitButtonGroup
|
|
|
|
|
|
style={{ marginRight: 1 }}
|
|
|
|
|
|
aria-label="测试单个渠道操作项目组"
|
|
|
|
|
|
>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
theme="light"
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
testChannel(record, '');
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
测试
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Dropdown
|
|
|
|
|
|
trigger="click"
|
|
|
|
|
|
position="bottomRight"
|
|
|
|
|
|
menu={record.test_models}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
style={{ padding: '8px 4px' }}
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
icon={<IconTreeTriangleDown />}
|
|
|
|
|
|
></Button>
|
|
|
|
|
|
</Dropdown>
|
|
|
|
|
|
</SplitButtonGroup>
|
|
|
|
|
|
{/*<Button theme='light' type='primary' style={{marginRight: 1}} onClick={()=>testChannel(record)}>测试</Button>*/}
|
|
|
|
|
|
<Popconfirm
|
|
|
|
|
|
title="确定是否要删除此渠道?"
|
|
|
|
|
|
content="此修改将不可逆"
|
|
|
|
|
|
okType={'danger'}
|
|
|
|
|
|
position={'left'}
|
|
|
|
|
|
onConfirm={() => {
|
|
|
|
|
|
manageChannel(record.id, 'delete', record).then(() => {
|
|
|
|
|
|
removeRecord(record.id);
|
|
|
|
|
|
});
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Button theme="light" type="danger" style={{ marginRight: 1 }}>
|
|
|
|
|
|
删除
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</Popconfirm>
|
|
|
|
|
|
{record.status === 1 ? (
|
|
|
|
|
|
<Button
|
|
|
|
|
|
theme="light"
|
|
|
|
|
|
type="warning"
|
|
|
|
|
|
style={{ marginRight: 1 }}
|
|
|
|
|
|
onClick={async () => {
|
|
|
|
|
|
manageChannel(record.id, 'disable', record);
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
禁用
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<Button
|
|
|
|
|
|
theme="light"
|
|
|
|
|
|
type="secondary"
|
|
|
|
|
|
style={{ marginRight: 1 }}
|
|
|
|
|
|
onClick={async () => {
|
|
|
|
|
|
manageChannel(record.id, 'enable', record);
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
启用
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
)}
|
2024-08-06 15:40:44 +08:00
|
|
|
|
<Button
|
2024-11-19 01:13:18 +08:00
|
|
|
|
theme="light"
|
|
|
|
|
|
type="tertiary"
|
|
|
|
|
|
style={{ marginRight: 1 }}
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setEditingChannel(record);
|
|
|
|
|
|
setShowEdit(true);
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
编辑
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Popconfirm
|
|
|
|
|
|
title="确定是否要复制此渠道?"
|
|
|
|
|
|
content="复制渠道的所有信息"
|
|
|
|
|
|
okType={'danger'}
|
|
|
|
|
|
position={'left'}
|
|
|
|
|
|
onConfirm={async () => {
|
|
|
|
|
|
copySelectedChannel(record.id);
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Button theme="light" type="primary" style={{ marginRight: 1 }}>
|
|
|
|
|
|
复制
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</Popconfirm>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
theme="light"
|
|
|
|
|
|
type="secondary"
|
|
|
|
|
|
style={{ marginRight: 1 }}
|
|
|
|
|
|
onClick={async () => {
|
|
|
|
|
|
manageTag(record.key, 'enable');
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
启用全部
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
theme="light"
|
|
|
|
|
|
type="warning"
|
|
|
|
|
|
style={{ marginRight: 1 }}
|
|
|
|
|
|
onClick={async () => {
|
|
|
|
|
|
manageTag(record.key, 'disable');
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
禁用全部
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
theme="light"
|
|
|
|
|
|
type="tertiary"
|
|
|
|
|
|
style={{ marginRight: 1 }}
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setShowEditTag(true);
|
|
|
|
|
|
setEditingTag(record.key);
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
编辑
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-03-15 16:05:33 +08:00
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
const [channels, setChannels] = useState([]);
|
|
|
|
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
|
|
const [activePage, setActivePage] = useState(1);
|
|
|
|
|
|
const [idSort, setIdSort] = useState(false);
|
|
|
|
|
|
const [searchKeyword, setSearchKeyword] = useState('');
|
|
|
|
|
|
const [searchGroup, setSearchGroup] = useState('');
|
|
|
|
|
|
const [searchModel, setSearchModel] = useState('');
|
|
|
|
|
|
const [searching, setSearching] = useState(false);
|
|
|
|
|
|
const [updatingBalance, setUpdatingBalance] = useState(false);
|
|
|
|
|
|
const [pageSize, setPageSize] = useState(ITEMS_PER_PAGE);
|
2024-03-23 21:24:39 +08:00
|
|
|
|
const [showPrompt, setShowPrompt] = useState(
|
2024-11-19 01:13:18 +08:00
|
|
|
|
shouldShowPrompt('channel-test')
|
2024-03-23 21:24:39 +08:00
|
|
|
|
);
|
2024-03-15 16:05:33 +08:00
|
|
|
|
const [channelCount, setChannelCount] = useState(pageSize);
|
|
|
|
|
|
const [groupOptions, setGroupOptions] = useState([]);
|
|
|
|
|
|
const [showEdit, setShowEdit] = useState(false);
|
|
|
|
|
|
const [enableBatchDelete, setEnableBatchDelete] = useState(false);
|
|
|
|
|
|
const [editingChannel, setEditingChannel] = useState({
|
2024-11-19 01:13:18 +08:00
|
|
|
|
id: undefined
|
2024-03-15 16:05:33 +08:00
|
|
|
|
});
|
2024-11-19 01:13:18 +08:00
|
|
|
|
const [showEditTag, setShowEditTag] = useState(false);
|
|
|
|
|
|
const [editingTag, setEditingTag] = useState('');
|
2024-03-15 16:05:33 +08:00
|
|
|
|
const [selectedChannels, setSelectedChannels] = useState([]);
|
|
|
|
|
|
|
2024-03-23 21:24:39 +08:00
|
|
|
|
const removeRecord = (id) => {
|
2024-03-15 16:05:33 +08:00
|
|
|
|
let newDataSource = [...channels];
|
|
|
|
|
|
if (id != null) {
|
2024-03-23 21:24:39 +08:00
|
|
|
|
let idx = newDataSource.findIndex((data) => data.id === id);
|
2024-03-15 16:05:33 +08:00
|
|
|
|
|
|
|
|
|
|
if (idx > -1) {
|
|
|
|
|
|
newDataSource.splice(idx, 1);
|
|
|
|
|
|
setChannels(newDataSource);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const setChannelFormat = (channels) => {
|
2024-11-19 01:13:18 +08:00
|
|
|
|
let channelDates = [];
|
|
|
|
|
|
let channelTags = {};
|
2024-03-15 16:05:33 +08:00
|
|
|
|
for (let i = 0; i < channels.length; i++) {
|
|
|
|
|
|
channels[i].key = '' + channels[i].id;
|
2024-11-19 01:13:18 +08:00
|
|
|
|
|
|
|
|
|
|
if (channels[i].tag === '' || channels[i].tag === null) {
|
|
|
|
|
|
let test_models = [];
|
|
|
|
|
|
channels[i].models.split(',').forEach((item, index) => {
|
|
|
|
|
|
test_models.push({
|
|
|
|
|
|
node: 'item',
|
|
|
|
|
|
name: item,
|
|
|
|
|
|
onClick: () => {
|
|
|
|
|
|
testChannel(channels[i], item);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2024-03-15 16:05:33 +08:00
|
|
|
|
});
|
2024-11-19 01:13:18 +08:00
|
|
|
|
channels[i].test_models = test_models;
|
|
|
|
|
|
channelDates.push(channels[i]);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
let tag = channels[i].tag;
|
|
|
|
|
|
// find from channelTags
|
|
|
|
|
|
let tagIndex = channelTags[tag];
|
|
|
|
|
|
let tagChannelDates = undefined;
|
|
|
|
|
|
if (tagIndex === undefined) {
|
|
|
|
|
|
// not found, create a new tag
|
|
|
|
|
|
channelTags[tag] = 1;
|
|
|
|
|
|
tagChannelDates = {
|
|
|
|
|
|
key: tag,
|
|
|
|
|
|
id: tag,
|
|
|
|
|
|
tag: tag,
|
|
|
|
|
|
name: '标签:' + tag,
|
|
|
|
|
|
group: '',
|
|
|
|
|
|
used_quota: 0,
|
|
|
|
|
|
response_time: 0
|
|
|
|
|
|
};
|
|
|
|
|
|
tagChannelDates.children = [];
|
|
|
|
|
|
channelDates.push(tagChannelDates);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// found, add to the tag
|
|
|
|
|
|
tagChannelDates = channelDates.find((item) => item.key === tag);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (tagChannelDates.group === '') {
|
|
|
|
|
|
tagChannelDates.group = channels[i].group;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
let channelGroupsStr = channels[i].group;
|
|
|
|
|
|
channelGroupsStr.split(',').forEach((item, index) => {
|
|
|
|
|
|
if (tagChannelDates.group.indexOf(item) === -1) {
|
|
|
|
|
|
tagChannelDates.group += item + ',';
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tagChannelDates.children.push(channels[i]);
|
|
|
|
|
|
if (channels[i].status === 1) {
|
|
|
|
|
|
tagChannelDates.status = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
tagChannelDates.used_quota += channels[i].used_quota;
|
|
|
|
|
|
tagChannelDates.response_time += channels[i].response_time;
|
|
|
|
|
|
tagChannelDates.response_time = tagChannelDates.response_time / 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-03-15 16:05:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
// data.key = '' + data.id
|
2024-11-19 01:13:18 +08:00
|
|
|
|
setChannels(channelDates);
|
|
|
|
|
|
if (channelDates.length >= pageSize) {
|
|
|
|
|
|
setChannelCount(channelDates.length + pageSize);
|
2024-03-15 16:05:33 +08:00
|
|
|
|
} else {
|
2024-11-19 01:13:18 +08:00
|
|
|
|
setChannelCount(channelDates.length);
|
2024-03-15 16:05:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const loadChannels = async (startIdx, pageSize, idSort) => {
|
|
|
|
|
|
setLoading(true);
|
2024-03-23 21:24:39 +08:00
|
|
|
|
const res = await API.get(
|
2024-11-19 01:13:18 +08:00
|
|
|
|
`/api/channel/?p=${startIdx}&page_size=${pageSize}&id_sort=${idSort}`
|
2024-03-23 21:24:39 +08:00
|
|
|
|
);
|
2024-03-25 14:10:57 +08:00
|
|
|
|
if (res === undefined) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2024-03-15 16:05:33 +08:00
|
|
|
|
const { success, message, data } = res.data;
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
if (startIdx === 0) {
|
|
|
|
|
|
setChannelFormat(data);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
let newChannels = [...channels];
|
|
|
|
|
|
newChannels.splice(startIdx * pageSize, data.length, ...data);
|
|
|
|
|
|
setChannelFormat(newChannels);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showError(message);
|
|
|
|
|
|
}
|
|
|
|
|
|
setLoading(false);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2024-04-10 02:51:21 +08:00
|
|
|
|
const copySelectedChannel = async (id) => {
|
2024-05-12 19:07:33 +08:00
|
|
|
|
const channelToCopy = channels.find(
|
2024-11-19 01:13:18 +08:00
|
|
|
|
(channel) => String(channel.id) === String(id)
|
2024-05-12 19:07:33 +08:00
|
|
|
|
);
|
|
|
|
|
|
console.log(channelToCopy);
|
2024-04-10 02:51:21 +08:00
|
|
|
|
channelToCopy.name += '_复制';
|
|
|
|
|
|
channelToCopy.created_time = null;
|
|
|
|
|
|
channelToCopy.balance = 0;
|
|
|
|
|
|
channelToCopy.used_quota = 0;
|
|
|
|
|
|
if (!channelToCopy) {
|
2024-05-12 19:07:33 +08:00
|
|
|
|
showError('渠道未找到,请刷新页面后重试。');
|
|
|
|
|
|
return;
|
2024-04-10 02:51:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
try {
|
2024-05-12 19:07:33 +08:00
|
|
|
|
const newChannel = { ...channelToCopy, id: undefined };
|
|
|
|
|
|
const response = await API.post('/api/channel/', newChannel);
|
|
|
|
|
|
if (response.data.success) {
|
|
|
|
|
|
showSuccess('渠道复制成功');
|
|
|
|
|
|
await refresh();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showError(response.data.message);
|
|
|
|
|
|
}
|
2024-04-10 02:51:21 +08:00
|
|
|
|
} catch (error) {
|
2024-05-12 19:07:33 +08:00
|
|
|
|
showError('渠道复制失败: ' + error.message);
|
2024-04-10 02:51:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2024-03-15 16:05:33 +08:00
|
|
|
|
const refresh = async () => {
|
|
|
|
|
|
await loadChannels(activePage - 1, pageSize, idSort);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
// console.log('default effect')
|
|
|
|
|
|
const localIdSort = localStorage.getItem('id-sort') === 'true';
|
2024-03-23 21:24:39 +08:00
|
|
|
|
const localPageSize =
|
2024-08-06 15:40:44 +08:00
|
|
|
|
parseInt(localStorage.getItem('page-size')) || ITEMS_PER_PAGE;
|
2024-03-15 16:05:33 +08:00
|
|
|
|
setIdSort(localIdSort);
|
|
|
|
|
|
setPageSize(localPageSize);
|
|
|
|
|
|
loadChannels(0, localPageSize, localIdSort)
|
2024-08-06 15:40:44 +08:00
|
|
|
|
.then()
|
|
|
|
|
|
.catch((reason) => {
|
|
|
|
|
|
showError(reason);
|
|
|
|
|
|
});
|
2024-03-15 16:05:33 +08:00
|
|
|
|
fetchGroups().then();
|
2024-05-12 19:07:33 +08:00
|
|
|
|
loadChannelModels().then();
|
2024-03-15 16:05:33 +08:00
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
const manageChannel = async (id, action, record, value) => {
|
|
|
|
|
|
let data = { id };
|
|
|
|
|
|
let res;
|
|
|
|
|
|
switch (action) {
|
|
|
|
|
|
case 'delete':
|
|
|
|
|
|
res = await API.delete(`/api/channel/${id}/`);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'enable':
|
|
|
|
|
|
data.status = 1;
|
|
|
|
|
|
res = await API.put('/api/channel/', data);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'disable':
|
|
|
|
|
|
data.status = 2;
|
|
|
|
|
|
res = await API.put('/api/channel/', data);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'priority':
|
|
|
|
|
|
if (value === '') {
|
|
|
|
|
|
return;
|
2023-12-05 18:15:40 +08:00
|
|
|
|
}
|
2024-03-15 16:05:33 +08:00
|
|
|
|
data.priority = parseInt(value);
|
|
|
|
|
|
res = await API.put('/api/channel/', data);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'weight':
|
|
|
|
|
|
if (value === '') {
|
|
|
|
|
|
return;
|
2023-12-14 16:35:03 +08:00
|
|
|
|
}
|
2024-03-15 16:05:33 +08:00
|
|
|
|
data.weight = parseInt(value);
|
|
|
|
|
|
if (data.weight < 0) {
|
|
|
|
|
|
data.weight = 0;
|
2023-12-14 16:35:03 +08:00
|
|
|
|
}
|
2024-03-15 16:05:33 +08:00
|
|
|
|
res = await API.put('/api/channel/', data);
|
|
|
|
|
|
break;
|
2023-12-14 16:35:03 +08:00
|
|
|
|
}
|
2024-03-15 16:05:33 +08:00
|
|
|
|
const { success, message } = res.data;
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
showSuccess('操作成功完成!');
|
|
|
|
|
|
let channel = res.data.data;
|
|
|
|
|
|
let newChannels = [...channels];
|
|
|
|
|
|
if (action === 'delete') {
|
|
|
|
|
|
} else {
|
|
|
|
|
|
record.status = channel.status;
|
|
|
|
|
|
}
|
|
|
|
|
|
setChannels(newChannels);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showError(message);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2024-11-19 01:13:18 +08:00
|
|
|
|
const manageTag = async (tag, action) => {
|
|
|
|
|
|
console.log(tag, action);
|
|
|
|
|
|
let res;
|
|
|
|
|
|
switch (action) {
|
|
|
|
|
|
case 'enable':
|
|
|
|
|
|
res = await API.post('/api/channel/tag/enabled', {
|
|
|
|
|
|
tag: tag
|
|
|
|
|
|
});
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'disable':
|
|
|
|
|
|
res = await API.post('/api/channel/tag/disabled', {
|
|
|
|
|
|
tag: tag
|
|
|
|
|
|
});
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
const { success, message } = res.data;
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
showSuccess('操作成功完成!');
|
|
|
|
|
|
let newChannels = [...channels];
|
|
|
|
|
|
for (let i = 0; i < newChannels.length; i++) {
|
|
|
|
|
|
if (newChannels[i].tag === tag) {
|
|
|
|
|
|
let status = action === 'enable' ? 1 : 2;
|
|
|
|
|
|
newChannels[i]?.children?.forEach((channel) => {
|
|
|
|
|
|
channel.status = status;
|
|
|
|
|
|
});
|
|
|
|
|
|
newChannels[i].status = status;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
setChannels(newChannels);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showError(message);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2024-03-15 16:05:33 +08:00
|
|
|
|
const renderStatus = (status) => {
|
|
|
|
|
|
switch (status) {
|
|
|
|
|
|
case 1:
|
2024-03-23 21:24:39 +08:00
|
|
|
|
return (
|
2024-11-19 01:13:18 +08:00
|
|
|
|
<Tag size="large" color="green">
|
2024-08-06 15:40:44 +08:00
|
|
|
|
已启用
|
|
|
|
|
|
</Tag>
|
2024-03-23 21:24:39 +08:00
|
|
|
|
);
|
2024-03-15 16:05:33 +08:00
|
|
|
|
case 2:
|
|
|
|
|
|
return (
|
2024-11-19 01:13:18 +08:00
|
|
|
|
<Tag size="large" color="yellow">
|
2024-08-06 15:40:44 +08:00
|
|
|
|
已禁用
|
|
|
|
|
|
</Tag>
|
2024-03-15 16:05:33 +08:00
|
|
|
|
);
|
|
|
|
|
|
case 3:
|
|
|
|
|
|
return (
|
2024-11-19 01:13:18 +08:00
|
|
|
|
<Tag size="large" color="yellow">
|
2024-08-06 15:40:44 +08:00
|
|
|
|
自动禁用
|
|
|
|
|
|
</Tag>
|
2024-03-15 16:05:33 +08:00
|
|
|
|
);
|
|
|
|
|
|
default:
|
|
|
|
|
|
return (
|
2024-11-19 01:13:18 +08:00
|
|
|
|
<Tag size="large" color="grey">
|
2024-08-06 15:40:44 +08:00
|
|
|
|
未知状态
|
|
|
|
|
|
</Tag>
|
2024-03-15 16:05:33 +08:00
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const renderResponseTime = (responseTime) => {
|
|
|
|
|
|
let time = responseTime / 1000;
|
|
|
|
|
|
time = time.toFixed(2) + ' 秒';
|
|
|
|
|
|
if (responseTime === 0) {
|
2024-03-23 21:24:39 +08:00
|
|
|
|
return (
|
2024-11-19 01:13:18 +08:00
|
|
|
|
<Tag size="large" color="grey">
|
2024-08-06 15:40:44 +08:00
|
|
|
|
未测试
|
|
|
|
|
|
</Tag>
|
2024-03-23 21:24:39 +08:00
|
|
|
|
);
|
2024-03-15 16:05:33 +08:00
|
|
|
|
} else if (responseTime <= 1000) {
|
2024-03-23 21:24:39 +08:00
|
|
|
|
return (
|
2024-11-19 01:13:18 +08:00
|
|
|
|
<Tag size="large" color="green">
|
2024-08-06 15:40:44 +08:00
|
|
|
|
{time}
|
|
|
|
|
|
</Tag>
|
2024-03-23 21:24:39 +08:00
|
|
|
|
);
|
2024-03-15 16:05:33 +08:00
|
|
|
|
} else if (responseTime <= 3000) {
|
2024-03-23 21:24:39 +08:00
|
|
|
|
return (
|
2024-11-19 01:13:18 +08:00
|
|
|
|
<Tag size="large" color="lime">
|
2024-08-06 15:40:44 +08:00
|
|
|
|
{time}
|
|
|
|
|
|
</Tag>
|
2024-03-23 21:24:39 +08:00
|
|
|
|
);
|
2024-03-15 16:05:33 +08:00
|
|
|
|
} else if (responseTime <= 5000) {
|
2024-03-23 21:24:39 +08:00
|
|
|
|
return (
|
2024-11-19 01:13:18 +08:00
|
|
|
|
<Tag size="large" color="yellow">
|
2024-08-06 15:40:44 +08:00
|
|
|
|
{time}
|
|
|
|
|
|
</Tag>
|
2024-03-23 21:24:39 +08:00
|
|
|
|
);
|
2024-03-15 16:05:33 +08:00
|
|
|
|
} else {
|
2024-03-23 21:24:39 +08:00
|
|
|
|
return (
|
2024-11-19 01:13:18 +08:00
|
|
|
|
<Tag size="large" color="red">
|
2024-08-06 15:40:44 +08:00
|
|
|
|
{time}
|
|
|
|
|
|
</Tag>
|
2024-03-23 21:24:39 +08:00
|
|
|
|
);
|
2024-03-15 16:05:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const searchChannels = async (searchKeyword, searchGroup, searchModel) => {
|
|
|
|
|
|
if (searchKeyword === '' && searchGroup === '' && searchModel === '') {
|
|
|
|
|
|
// if keyword is blank, load files instead.
|
|
|
|
|
|
await loadChannels(0, pageSize, idSort);
|
|
|
|
|
|
setActivePage(1);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
setSearching(true);
|
2024-03-23 21:24:39 +08:00
|
|
|
|
const res = await API.get(
|
2024-11-19 01:13:18 +08:00
|
|
|
|
`/api/channel/search?keyword=${searchKeyword}&group=${searchGroup}&model=${searchModel}`
|
2024-03-23 21:24:39 +08:00
|
|
|
|
);
|
2024-03-15 16:05:33 +08:00
|
|
|
|
const { success, message, data } = res.data;
|
|
|
|
|
|
if (success) {
|
2024-07-08 17:00:10 +08:00
|
|
|
|
setChannelFormat(data);
|
2024-03-15 16:05:33 +08:00
|
|
|
|
setActivePage(1);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showError(message);
|
2024-01-10 13:23:43 +08:00
|
|
|
|
}
|
2024-03-15 16:05:33 +08:00
|
|
|
|
setSearching(false);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const testChannel = async (record, model) => {
|
|
|
|
|
|
const res = await API.get(`/api/channel/test/${record.id}?model=${model}`);
|
|
|
|
|
|
const { success, message, time } = res.data;
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
record.response_time = time * 1000;
|
|
|
|
|
|
record.test_time = Date.now() / 1000;
|
|
|
|
|
|
showInfo(`通道 ${record.name} 测试成功,耗时 ${time.toFixed(2)} 秒。`);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showError(message);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const testAllChannels = async () => {
|
|
|
|
|
|
const res = await API.get(`/api/channel/test`);
|
|
|
|
|
|
const { success, message } = res.data;
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
showInfo('已成功开始测试所有通道,请刷新页面查看结果。');
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showError(message);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const deleteAllDisabledChannels = async () => {
|
|
|
|
|
|
const res = await API.delete(`/api/channel/disabled`);
|
|
|
|
|
|
const { success, message, data } = res.data;
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
showSuccess(`已删除所有禁用渠道,共计 ${data} 个`);
|
|
|
|
|
|
await refresh();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showError(message);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const updateChannelBalance = async (record) => {
|
|
|
|
|
|
const res = await API.get(`/api/channel/update_balance/${record.id}/`);
|
|
|
|
|
|
const { success, message, balance } = res.data;
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
record.balance = balance;
|
|
|
|
|
|
record.balance_updated_time = Date.now() / 1000;
|
|
|
|
|
|
showInfo(`通道 ${record.name} 余额更新成功!`);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showError(message);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const updateAllChannelsBalance = async () => {
|
|
|
|
|
|
setUpdatingBalance(true);
|
|
|
|
|
|
const res = await API.get(`/api/channel/update_balance`);
|
|
|
|
|
|
const { success, message } = res.data;
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
showInfo('已更新完毕所有已启用通道余额!');
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showError(message);
|
|
|
|
|
|
}
|
|
|
|
|
|
setUpdatingBalance(false);
|
|
|
|
|
|
};
|
2024-01-10 13:23:43 +08:00
|
|
|
|
|
2024-03-15 16:05:33 +08:00
|
|
|
|
const batchDeleteChannels = async () => {
|
|
|
|
|
|
if (selectedChannels.length === 0) {
|
|
|
|
|
|
showError('请先选择要删除的通道!');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
setLoading(true);
|
|
|
|
|
|
let ids = [];
|
|
|
|
|
|
selectedChannels.forEach((channel) => {
|
|
|
|
|
|
ids.push(channel.id);
|
|
|
|
|
|
});
|
|
|
|
|
|
const res = await API.post(`/api/channel/batch`, { ids: ids });
|
|
|
|
|
|
const { success, message, data } = res.data;
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
showSuccess(`已删除 ${data} 个通道!`);
|
|
|
|
|
|
await refresh();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showError(message);
|
|
|
|
|
|
}
|
|
|
|
|
|
setLoading(false);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const fixChannelsAbilities = async () => {
|
|
|
|
|
|
const res = await API.post(`/api/channel/fix`);
|
|
|
|
|
|
const { success, message, data } = res.data;
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
showSuccess(`已修复 ${data} 个通道!`);
|
|
|
|
|
|
await refresh();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showError(message);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
2023-12-05 18:15:40 +08:00
|
|
|
|
|
2024-03-23 21:24:39 +08:00
|
|
|
|
let pageData = channels.slice(
|
2024-08-06 15:40:44 +08:00
|
|
|
|
(activePage - 1) * pageSize,
|
2024-11-19 01:13:18 +08:00
|
|
|
|
activePage * pageSize
|
2024-03-23 21:24:39 +08:00
|
|
|
|
);
|
2023-12-05 18:15:40 +08:00
|
|
|
|
|
2024-03-23 21:24:39 +08:00
|
|
|
|
const handlePageChange = (page) => {
|
2024-03-15 16:05:33 +08:00
|
|
|
|
setActivePage(page);
|
|
|
|
|
|
if (page === Math.ceil(channels.length / pageSize) + 1) {
|
|
|
|
|
|
// In this case we have to load more data and then append them.
|
2024-11-19 01:13:18 +08:00
|
|
|
|
loadChannels(page - 1, pageSize, idSort).then((r) => {
|
|
|
|
|
|
});
|
2023-12-05 18:15:40 +08:00
|
|
|
|
}
|
2024-03-15 16:05:33 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handlePageSizeChange = async (size) => {
|
|
|
|
|
|
localStorage.setItem('page-size', size + '');
|
|
|
|
|
|
setPageSize(size);
|
|
|
|
|
|
setActivePage(1);
|
|
|
|
|
|
loadChannels(0, size, idSort)
|
2024-08-06 15:40:44 +08:00
|
|
|
|
.then()
|
|
|
|
|
|
.catch((reason) => {
|
|
|
|
|
|
showError(reason);
|
|
|
|
|
|
});
|
2024-03-15 16:05:33 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const fetchGroups = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
let res = await API.get(`/api/group/`);
|
|
|
|
|
|
// add 'all' option
|
|
|
|
|
|
// res.data.data.unshift('all');
|
2024-03-25 14:10:57 +08:00
|
|
|
|
if (res === undefined) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2024-03-23 21:24:39 +08:00
|
|
|
|
setGroupOptions(
|
2024-08-06 15:40:44 +08:00
|
|
|
|
res.data.data.map((group) => ({
|
|
|
|
|
|
label: group,
|
2024-11-19 01:13:18 +08:00
|
|
|
|
value: group
|
|
|
|
|
|
}))
|
2024-03-23 21:24:39 +08:00
|
|
|
|
);
|
2024-03-15 16:05:33 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
showError(error.message);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const closeEdit = () => {
|
|
|
|
|
|
setShowEdit(false);
|
|
|
|
|
|
};
|
2023-12-05 18:15:40 +08:00
|
|
|
|
|
2024-03-15 16:05:33 +08:00
|
|
|
|
const handleRow = (record, index) => {
|
|
|
|
|
|
if (record.status !== 1) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
style: {
|
2024-11-19 01:13:18 +08:00
|
|
|
|
background: 'var(--semi-color-disabled-border)'
|
|
|
|
|
|
}
|
2024-03-15 16:05:33 +08:00
|
|
|
|
};
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return {};
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
2024-11-19 01:13:18 +08:00
|
|
|
|
<>
|
|
|
|
|
|
<EditTagModal
|
|
|
|
|
|
visible={showEditTag}
|
|
|
|
|
|
tag={editingTag}
|
|
|
|
|
|
handleClose={() => setShowEditTag(false)}
|
|
|
|
|
|
refresh={refresh}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<EditChannel
|
|
|
|
|
|
refresh={refresh}
|
|
|
|
|
|
visible={showEdit}
|
|
|
|
|
|
handleClose={closeEdit}
|
|
|
|
|
|
editingChannel={editingChannel}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Form
|
|
|
|
|
|
onSubmit={() => {
|
|
|
|
|
|
searchChannels(searchKeyword, searchGroup, searchModel);
|
|
|
|
|
|
}}
|
|
|
|
|
|
labelPosition="left"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div style={{ display: 'flex' }}>
|
|
|
|
|
|
<Space>
|
|
|
|
|
|
<Form.Input
|
|
|
|
|
|
field="search_keyword"
|
|
|
|
|
|
label="搜索渠道关键词"
|
|
|
|
|
|
placeholder="ID,名称和密钥 ..."
|
|
|
|
|
|
value={searchKeyword}
|
|
|
|
|
|
loading={searching}
|
|
|
|
|
|
onChange={(v) => {
|
|
|
|
|
|
setSearchKeyword(v.trim());
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Form.Input
|
|
|
|
|
|
field="search_model"
|
|
|
|
|
|
label="模型"
|
|
|
|
|
|
placeholder="模型关键字"
|
|
|
|
|
|
value={searchModel}
|
|
|
|
|
|
loading={searching}
|
|
|
|
|
|
onChange={(v) => {
|
|
|
|
|
|
setSearchModel(v.trim());
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Form.Select
|
|
|
|
|
|
field="group"
|
|
|
|
|
|
label="分组"
|
|
|
|
|
|
optionList={[{ label: '选择分组', value: null }, ...groupOptions]}
|
|
|
|
|
|
initValue={null}
|
|
|
|
|
|
onChange={(v) => {
|
|
|
|
|
|
setSearchGroup(v);
|
|
|
|
|
|
searchChannels(searchKeyword, v, searchModel);
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
label="查询"
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
htmlType="submit"
|
|
|
|
|
|
className="btn-margin-right"
|
|
|
|
|
|
style={{ marginRight: 8 }}
|
|
|
|
|
|
>
|
|
|
|
|
|
查询
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Form>
|
|
|
|
|
|
<Divider style={{ marginBottom: 15 }} />
|
|
|
|
|
|
<div
|
|
|
|
|
|
style={{
|
|
|
|
|
|
display: isMobile() ? '' : 'flex',
|
|
|
|
|
|
marginTop: isMobile() ? 0 : -45,
|
|
|
|
|
|
zIndex: 999,
|
|
|
|
|
|
pointerEvents: 'none'
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Space
|
|
|
|
|
|
style={{ pointerEvents: 'auto', marginTop: isMobile() ? 0 : 45 }}
|
2024-11-12 17:00:06 +08:00
|
|
|
|
>
|
2024-11-19 01:13:18 +08:00
|
|
|
|
<Typography.Text strong>使用ID排序</Typography.Text>
|
|
|
|
|
|
<Switch
|
|
|
|
|
|
checked={idSort}
|
|
|
|
|
|
label="使用ID排序"
|
|
|
|
|
|
uncheckedText="关"
|
|
|
|
|
|
aria-label="是否用ID排序"
|
|
|
|
|
|
onChange={(v) => {
|
|
|
|
|
|
localStorage.setItem('id-sort', v + '');
|
|
|
|
|
|
setIdSort(v);
|
|
|
|
|
|
loadChannels(0, pageSize, v)
|
|
|
|
|
|
.then()
|
|
|
|
|
|
.catch((reason) => {
|
|
|
|
|
|
showError(reason);
|
|
|
|
|
|
});
|
|
|
|
|
|
}}
|
|
|
|
|
|
></Switch>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
theme="light"
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
style={{ marginRight: 8 }}
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setEditingChannel({
|
|
|
|
|
|
id: undefined
|
|
|
|
|
|
});
|
|
|
|
|
|
setShowEdit(true);
|
2024-11-12 17:00:06 +08:00
|
|
|
|
}}
|
|
|
|
|
|
>
|
2024-11-19 01:13:18 +08:00
|
|
|
|
添加渠道
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Popconfirm
|
|
|
|
|
|
title="确定?"
|
|
|
|
|
|
okType={'warning'}
|
|
|
|
|
|
onConfirm={testAllChannels}
|
|
|
|
|
|
position={isMobile() ? 'top' : 'top'}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Button theme="light" type="warning" style={{ marginRight: 8 }}>
|
|
|
|
|
|
测试所有通道
|
2024-11-12 17:00:06 +08:00
|
|
|
|
</Button>
|
2024-11-19 01:13:18 +08:00
|
|
|
|
</Popconfirm>
|
|
|
|
|
|
<Popconfirm
|
|
|
|
|
|
title="确定?"
|
|
|
|
|
|
okType={'secondary'}
|
|
|
|
|
|
onConfirm={updateAllChannelsBalance}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Button theme="light" type="secondary" style={{ marginRight: 8 }}>
|
|
|
|
|
|
更新所有已启用通道余额
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</Popconfirm>
|
|
|
|
|
|
<Popconfirm
|
|
|
|
|
|
title="确定是否要删除禁用通道?"
|
|
|
|
|
|
content="此修改将不可逆"
|
|
|
|
|
|
okType={'danger'}
|
|
|
|
|
|
onConfirm={deleteAllDisabledChannels}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Button theme="light" type="danger" style={{ marginRight: 8 }}>
|
|
|
|
|
|
删除禁用通道
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</Popconfirm>
|
2024-11-12 17:00:06 +08:00
|
|
|
|
|
2024-11-19 01:13:18 +08:00
|
|
|
|
<Button
|
|
|
|
|
|
theme="light"
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
style={{ marginRight: 8 }}
|
|
|
|
|
|
onClick={refresh}
|
|
|
|
|
|
>
|
|
|
|
|
|
刷新
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div style={{ marginTop: 20 }}>
|
|
|
|
|
|
<Space>
|
|
|
|
|
|
<Typography.Text strong>开启批量删除</Typography.Text>
|
|
|
|
|
|
<Switch
|
|
|
|
|
|
label="开启批量删除"
|
|
|
|
|
|
uncheckedText="关"
|
|
|
|
|
|
aria-label="是否开启批量删除"
|
|
|
|
|
|
onChange={(v) => {
|
|
|
|
|
|
setEnableBatchDelete(v);
|
|
|
|
|
|
}}
|
|
|
|
|
|
></Switch>
|
|
|
|
|
|
<Popconfirm
|
|
|
|
|
|
title="确定是否要删除所选通道?"
|
|
|
|
|
|
content="此修改将不可逆"
|
|
|
|
|
|
okType={'danger'}
|
|
|
|
|
|
onConfirm={batchDeleteChannels}
|
|
|
|
|
|
disabled={!enableBatchDelete}
|
|
|
|
|
|
position={'top'}
|
|
|
|
|
|
>
|
2024-03-23 21:24:39 +08:00
|
|
|
|
<Button
|
2024-11-19 01:13:18 +08:00
|
|
|
|
disabled={!enableBatchDelete}
|
|
|
|
|
|
theme="light"
|
|
|
|
|
|
type="danger"
|
|
|
|
|
|
style={{ marginRight: 8 }}
|
2024-03-23 21:24:39 +08:00
|
|
|
|
>
|
2024-11-19 01:13:18 +08:00
|
|
|
|
删除所选通道
|
2024-03-23 21:24:39 +08:00
|
|
|
|
</Button>
|
2024-11-19 01:13:18 +08:00
|
|
|
|
</Popconfirm>
|
|
|
|
|
|
<Popconfirm
|
|
|
|
|
|
title="确定是否要修复数据库一致性?"
|
|
|
|
|
|
content="进行该操作时,可能导致渠道访问错误,请仅在数据库出现问题时使用"
|
|
|
|
|
|
okType={'warning'}
|
|
|
|
|
|
onConfirm={fixChannelsAbilities}
|
|
|
|
|
|
position={'top'}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Button theme="light" type="secondary" style={{ marginRight: 8 }}>
|
|
|
|
|
|
修复数据库一致性
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</Popconfirm>
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
</div>
|
2024-08-06 15:40:44 +08:00
|
|
|
|
|
2024-11-19 01:13:18 +08:00
|
|
|
|
|
|
|
|
|
|
<Table
|
|
|
|
|
|
className={'channel-table'}
|
|
|
|
|
|
style={{ marginTop: 15 }}
|
|
|
|
|
|
columns={columns}
|
|
|
|
|
|
dataSource={pageData}
|
|
|
|
|
|
pagination={{
|
|
|
|
|
|
currentPage: activePage,
|
|
|
|
|
|
pageSize: pageSize,
|
|
|
|
|
|
total: channelCount,
|
|
|
|
|
|
pageSizeOpts: [10, 20, 50, 100],
|
|
|
|
|
|
showSizeChanger: true,
|
|
|
|
|
|
formatPageText: (page) => '',
|
|
|
|
|
|
onPageSizeChange: (size) => {
|
|
|
|
|
|
handlePageSizeChange(size).then();
|
|
|
|
|
|
},
|
|
|
|
|
|
onPageChange: handlePageChange
|
|
|
|
|
|
}}
|
|
|
|
|
|
loading={loading}
|
|
|
|
|
|
onRow={handleRow}
|
|
|
|
|
|
rowSelection={
|
|
|
|
|
|
enableBatchDelete
|
|
|
|
|
|
? {
|
|
|
|
|
|
onChange: (selectedRowKeys, selectedRows) => {
|
|
|
|
|
|
// console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
|
|
|
|
|
|
setSelectedChannels(selectedRows);
|
|
|
|
|
|
}
|
2024-11-12 17:00:06 +08:00
|
|
|
|
}
|
2024-11-19 01:13:18 +08:00
|
|
|
|
: null
|
|
|
|
|
|
}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</>
|
2024-03-15 16:05:33 +08:00
|
|
|
|
);
|
2023-04-22 21:41:16 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export default ChannelsTable;
|