mirror of
https://github.com/didi/KnowStreaming.git
synced 2026-01-14 20:18:31 +08:00
初始化3.0.0版本
This commit is contained in:
@@ -0,0 +1,706 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import {
|
||||
Utils,
|
||||
Drawer,
|
||||
Button,
|
||||
Form,
|
||||
Space,
|
||||
Divider,
|
||||
AppContainer,
|
||||
Radio,
|
||||
InputNumber,
|
||||
Transfer,
|
||||
Select,
|
||||
message,
|
||||
IconFont,
|
||||
Tooltip,
|
||||
} from 'knowdesign';
|
||||
|
||||
import CronInput from './CronInput';
|
||||
import BalanceEditTable from './BalanceEditTable';
|
||||
import PlanDrawer from './PlanDrawer';
|
||||
import api from '../../api';
|
||||
import './style/BalanceDrawer.less';
|
||||
interface PropsType {
|
||||
onClose: Function;
|
||||
visible: boolean;
|
||||
isCycle?: boolean;
|
||||
formData?: any;
|
||||
genData?: any;
|
||||
}
|
||||
|
||||
const IndexCalculations = [
|
||||
{
|
||||
label: '近5mins',
|
||||
value: 5 * 60,
|
||||
},
|
||||
{
|
||||
label: '近10mins',
|
||||
value: 10 * 60,
|
||||
},
|
||||
{
|
||||
label: '近30mins',
|
||||
value: 30 * 60,
|
||||
},
|
||||
{
|
||||
label: '近1h',
|
||||
value: 60 * 60,
|
||||
},
|
||||
{
|
||||
label: '近6h',
|
||||
value: 6 * 60 * 60,
|
||||
},
|
||||
{
|
||||
label: '近12h',
|
||||
value: 12 * 60 * 60,
|
||||
},
|
||||
{
|
||||
label: '近24h',
|
||||
value: 24 * 60 * 60,
|
||||
},
|
||||
];
|
||||
|
||||
const BalancedDimensions = [
|
||||
// 本期没有CPU
|
||||
// {
|
||||
// label: 'CPU',
|
||||
// value: 'cpu',
|
||||
// },
|
||||
{
|
||||
label: 'Disk',
|
||||
value: 'disk',
|
||||
},
|
||||
{
|
||||
label: 'BytesIn',
|
||||
value: 'bytesIn',
|
||||
},
|
||||
{
|
||||
label: 'BytesOut',
|
||||
value: 'bytesOut',
|
||||
},
|
||||
];
|
||||
|
||||
const Schedules1 = [
|
||||
{
|
||||
label: '每天',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '每周',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
label: '每月',
|
||||
value: 3,
|
||||
},
|
||||
];
|
||||
|
||||
const SchedulesWeeks = [
|
||||
{
|
||||
label: '每周一',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '每周二',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
label: '每周三',
|
||||
value: 3,
|
||||
},
|
||||
{
|
||||
label: '每周四',
|
||||
value: 4,
|
||||
},
|
||||
{
|
||||
label: '每周五',
|
||||
value: 5,
|
||||
},
|
||||
{
|
||||
label: '每周六',
|
||||
value: 6,
|
||||
},
|
||||
{
|
||||
label: '每周日',
|
||||
value: 7,
|
||||
},
|
||||
];
|
||||
|
||||
const SchedulesMouths: any[] = [];
|
||||
for (let i = 1; i <= 28; i++) {
|
||||
SchedulesMouths.push({
|
||||
label: `每月${i}日`,
|
||||
value: i,
|
||||
});
|
||||
}
|
||||
|
||||
const BalanceDrawer: React.FC<PropsType> = ({ onClose, visible, isCycle = false, formData, genData }) => {
|
||||
const [global] = AppContainer.useGlobalValue();
|
||||
const [form] = Form.useForm();
|
||||
const customFormRef = useRef<any>();
|
||||
const [nodeData, setNodeData] = useState([]);
|
||||
const [nodeTargetKeys, setNodeTargetKeys] = useState([]);
|
||||
const [topicData, setTopicData] = useState([]);
|
||||
const [topicTargetKeys, setTopicTargetKeys] = useState([]);
|
||||
const [tableData, setTableData] = useState<any[]>([]);
|
||||
const [dimension, setDimension] = useState<string[]>(['disk', 'bytesIn', 'bytesOut']);
|
||||
|
||||
const [planVisible, setPlanVisible] = useState<boolean>(false);
|
||||
const [planDetailData, setPlanDetailData] = useState({});
|
||||
const [parallelNum, setParallelNum] = useState(0);
|
||||
const [executionStrategy, setExecutionStrategy] = useState(1);
|
||||
useEffect(() => {
|
||||
getNodeList();
|
||||
getTopicList();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
init();
|
||||
}, [visible, formData]);
|
||||
|
||||
useEffect(() => {
|
||||
setNodeTargetKeys(formData?.brokers || []);
|
||||
}, [nodeData]);
|
||||
|
||||
useEffect(() => {
|
||||
setTopicTargetKeys(formData?.topicBlackList || []);
|
||||
}, [topicData]);
|
||||
|
||||
const init = () => {
|
||||
if (formData && Object.keys(formData).length > 0) {
|
||||
console.log(formData, '有FormData');
|
||||
const tableData = formData?.clusterBalanceIntervalList?.map((item: any) => {
|
||||
const finfIndex = BalancedDimensions.findIndex((item1) => item1?.value === item?.type);
|
||||
return {
|
||||
...item,
|
||||
name: BalancedDimensions[finfIndex]?.label,
|
||||
};
|
||||
});
|
||||
setTableData(tableData || []);
|
||||
const dimension = formData?.clusterBalanceIntervalList?.map((item: any) => {
|
||||
return item?.type;
|
||||
});
|
||||
|
||||
form.setFieldsValue({
|
||||
...formData,
|
||||
brokers: formData?.brokers || [],
|
||||
topicBlackList: formData?.topicBlackList || [],
|
||||
dimension,
|
||||
throttleUnitM: formData?.throttleUnitB / 1024 / 1024,
|
||||
});
|
||||
} else {
|
||||
const defaultDimension = ['disk', 'bytesIn', 'bytesOut'];
|
||||
form.resetFields();
|
||||
form.setFieldsValue({ dimension: defaultDimension, metricCalculationPeriod: 600 });
|
||||
const res = defaultDimension?.map((item, index) => {
|
||||
const finfIndex = BalancedDimensions.findIndex((item1) => item1.value === item);
|
||||
return {
|
||||
type: item,
|
||||
name: BalancedDimensions[finfIndex]?.label,
|
||||
intervalPercent: 10,
|
||||
priority: index + 1,
|
||||
};
|
||||
});
|
||||
console.log(res, '表单回显立即均衡');
|
||||
setTableData(res);
|
||||
setDimension(['disk', 'bytesIn', 'bytesOut']);
|
||||
setNodeTargetKeys([]);
|
||||
setTopicTargetKeys([]);
|
||||
}
|
||||
};
|
||||
const submit = () => {
|
||||
// 周期均衡 / 立即均衡
|
||||
customFormRef.current.editTableValidate().then((res: any) => {
|
||||
form.validateFields().then((values) => {
|
||||
const params = {
|
||||
...values,
|
||||
clusterId: global?.clusterInfo?.id,
|
||||
clusterBalanceIntervalList: tableData,
|
||||
scheduleJob: isCycle || false,
|
||||
throttleUnitB: values?.throttleUnitM * 1024 * 1024,
|
||||
};
|
||||
|
||||
if (!isCycle) {
|
||||
if (values?.priority === 'throughput') {
|
||||
params.parallelNum = 0;
|
||||
params.executionStrategy = 1;
|
||||
} else if (values?.priority === 'stability') {
|
||||
params.parallelNum = 1;
|
||||
params.executionStrategy = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (formData?.jobId) {
|
||||
const handledData = {
|
||||
creator: JSON.parse(global?.userInfo)?.userName,
|
||||
jobType: 2, // type 0 topic迁移 1 扩缩容 2集群均衡
|
||||
planTime: formData?.record?.planTime,
|
||||
jobStatus: formData?.record?.jobStatus || 2, //status 2 创建
|
||||
target: formData?.record?.target,
|
||||
id: formData?.record?.id,
|
||||
jobDesc: formData?.record?.description || '',
|
||||
jobData: JSON.stringify({ ...formData?.jobData, ...params }),
|
||||
};
|
||||
Utils.put(api.putJobsTaskData(global?.clusterInfo?.id), handledData)
|
||||
.then(() => {
|
||||
message.success('集群均衡任务编辑成功');
|
||||
drawerClose(true);
|
||||
setPlanVisible(false);
|
||||
genData();
|
||||
})
|
||||
.catch((err: any) => {
|
||||
console.log(err, 'err');
|
||||
});
|
||||
} else {
|
||||
Utils.request(api.balanceStrategy(global?.clusterInfo?.id), {
|
||||
method: 'POST',
|
||||
data: params,
|
||||
}).then((res: any) => {
|
||||
const dataDe = res || [];
|
||||
message.success(isCycle ? '成功创建周期均衡策略' : '成功创建立即均衡策略');
|
||||
drawerClose(true);
|
||||
setPlanVisible(false);
|
||||
isCycle && genData();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const preview = () => {
|
||||
// 立即均衡
|
||||
customFormRef.current.editTableValidate().then((res: any) => {
|
||||
form.validateFields().then((values) => {
|
||||
const params = {
|
||||
...values,
|
||||
clusterId: global?.clusterInfo?.id,
|
||||
clusterBalanceIntervalList: tableData,
|
||||
scheduleJob: isCycle || false,
|
||||
throttleUnitB: values?.throttleUnitM * 1024 * 1024,
|
||||
// parallelNum: !isCycle ? values?.priority === 'throughput'? 0 : : null
|
||||
};
|
||||
|
||||
if (!isCycle) {
|
||||
if (values?.priority === 'throughput') {
|
||||
params.parallelNum = 0;
|
||||
params.executionStrategy = 1;
|
||||
} else if (values?.priority === 'stability') {
|
||||
params.parallelNum = 1;
|
||||
params.executionStrategy = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// 预览计划
|
||||
if (formData?.jobId) {
|
||||
Utils.request(api.getBalancePlan(global?.clusterInfo?.id, formData?.jobId), {
|
||||
method: 'GET',
|
||||
}).then((res: any) => {
|
||||
const dataDe = res || {};
|
||||
setPlanDetailData(dataDe);
|
||||
setPlanVisible(true);
|
||||
});
|
||||
} else {
|
||||
Utils.request(api.balancePreview(global?.clusterInfo?.id), {
|
||||
method: 'POST',
|
||||
data: params,
|
||||
}).then((res: any) => {
|
||||
const dataDe = res || {};
|
||||
setPlanDetailData(dataDe);
|
||||
setPlanVisible(true);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const nodeChange = (val: any) => {
|
||||
setNodeTargetKeys(val);
|
||||
};
|
||||
|
||||
const topicChange = (val: any) => {
|
||||
setTopicTargetKeys(val);
|
||||
};
|
||||
|
||||
const getNodeList = () => {
|
||||
Utils.request(api.getBrokersMetaList(global?.clusterInfo?.id), {
|
||||
method: 'GET',
|
||||
}).then((res: any) => {
|
||||
const dataDe = res || [];
|
||||
const dataHandle = dataDe.map((item: any) => {
|
||||
return {
|
||||
...item,
|
||||
key: item.brokerId,
|
||||
title: `${item.brokerId} (${item.host})`,
|
||||
};
|
||||
});
|
||||
setNodeData(dataHandle);
|
||||
});
|
||||
};
|
||||
const getTopicList = () => {
|
||||
Utils.request(api.getTopicMetaList(global?.clusterInfo?.id), {
|
||||
method: 'GET',
|
||||
}).then((res: any) => {
|
||||
const dataDe = res || [];
|
||||
const dataHandle = dataDe.map((item: any) => {
|
||||
return {
|
||||
...item,
|
||||
key: item.topicName,
|
||||
title: item.topicName,
|
||||
};
|
||||
});
|
||||
setTopicData(dataHandle);
|
||||
});
|
||||
};
|
||||
|
||||
const dimensionChange = (val: string[]) => {
|
||||
const res = val?.map((item, index) => {
|
||||
const finfIndex = BalancedDimensions.findIndex((item1) => item1.value === item);
|
||||
const tableIndex = tableData?.findIndex((item2) => item2.type === item);
|
||||
return {
|
||||
type: item,
|
||||
name: BalancedDimensions[finfIndex]?.label,
|
||||
intervalPercent: tableIndex > -1 ? tableData[tableIndex].intervalPercent : 10,
|
||||
priority: index + 1,
|
||||
};
|
||||
});
|
||||
setTableData(res);
|
||||
setDimension(val);
|
||||
};
|
||||
|
||||
const tableDataChange = (data: any[]) => {
|
||||
setTableData(data);
|
||||
};
|
||||
|
||||
const planClose = () => {
|
||||
setPlanVisible(false);
|
||||
// onClose();
|
||||
};
|
||||
|
||||
const balanceImmediate = () => {
|
||||
submit();
|
||||
};
|
||||
|
||||
const drawerClose = (isArg?: boolean) => {
|
||||
isArg ? onClose(isArg) : onClose();
|
||||
form.resetFields();
|
||||
};
|
||||
|
||||
const priorityChange = (e: any) => {
|
||||
if (e.target.value === 'throughput') {
|
||||
setParallelNum(0);
|
||||
setExecutionStrategy(1);
|
||||
} else if (e.target.value === 'stability') {
|
||||
setParallelNum(1);
|
||||
setExecutionStrategy(2);
|
||||
} else {
|
||||
form.setFieldsValue({ parallelNum, executionStrategy });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<PlanDrawer
|
||||
visible={planVisible}
|
||||
onClose={planClose}
|
||||
balanceImmediate={balanceImmediate}
|
||||
detailData={planDetailData}
|
||||
isPrevew={true}
|
||||
isEdit={formData?.jobId ? true : false}
|
||||
/>
|
||||
<Drawer
|
||||
title={isCycle ? '周期均衡' : '立即均衡'}
|
||||
width="600px"
|
||||
destroyOnClose={true}
|
||||
className="balance-drawer"
|
||||
onClose={() => drawerClose()}
|
||||
visible={visible}
|
||||
maskClosable={false}
|
||||
extra={
|
||||
<Space>
|
||||
<Button size="small" onClick={() => drawerClose()}>
|
||||
取消
|
||||
</Button>
|
||||
{isCycle ? (
|
||||
<Button type="primary" size="small" disabled={false} onClick={submit}>
|
||||
确定
|
||||
</Button>
|
||||
) : (
|
||||
<Button className="btn-width84" type="primary" size="small" disabled={false} onClick={preview}>
|
||||
预览计划
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Divider type="vertical" />
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
preserve={false}
|
||||
initialValues={{
|
||||
status: 1,
|
||||
}}
|
||||
>
|
||||
{/* {!isCycle && (
|
||||
<>
|
||||
<h6 className="form-title">均衡节点范围</h6>
|
||||
<Form.Item
|
||||
name="brokers"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: `请选择!`,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Transfer
|
||||
dataSource={nodeData}
|
||||
titles={['待选节点', '已选节点']}
|
||||
customHeader
|
||||
showSelectedCount
|
||||
locale={{
|
||||
itemUnit: '',
|
||||
itemsUnit: '',
|
||||
}}
|
||||
showSearch
|
||||
filterOption={(inputValue, option) => option.host.indexOf(inputValue) > -1}
|
||||
targetKeys={nodeTargetKeys}
|
||||
onChange={nodeChange}
|
||||
render={(item) => item.title}
|
||||
suffix={<IconFont type="icon-fangdajing" />}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
)} */}
|
||||
|
||||
<h6 className="form-title">均衡策略</h6>
|
||||
<Form.Item
|
||||
name="metricCalculationPeriod"
|
||||
label="指标计算周期"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: `请选择!`,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select placeholder={`请选择指标计算周期`} options={IndexCalculations} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="均衡维度"
|
||||
name="dimension"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: `请选择!`,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
placeholder={`请选择均衡维度`}
|
||||
mode="multiple"
|
||||
value={dimension}
|
||||
options={BalancedDimensions}
|
||||
onChange={dimensionChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<BalanceEditTable ref={customFormRef} tableData={tableData} tableDataChange={tableDataChange} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="topicBlackList"
|
||||
label="Topic黑名单"
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
message: `请选择!`,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Transfer
|
||||
dataSource={topicData}
|
||||
titles={['待选黑名单', '已选黑名单']}
|
||||
customHeader
|
||||
showSelectedCount
|
||||
locale={{
|
||||
itemUnit: '',
|
||||
itemsUnit: '',
|
||||
}}
|
||||
showSearch
|
||||
filterOption={(inputValue, option) => option.topicName.indexOf(inputValue) > -1}
|
||||
targetKeys={topicTargetKeys}
|
||||
onChange={topicChange}
|
||||
render={(item) => item.title}
|
||||
suffix={<IconFont type="icon-fangdajing" />}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<h6 className="form-title">运行配置</h6>
|
||||
{!isCycle && (
|
||||
<Form.Item label="" name="priority" rules={[{ required: true, message: 'Principle 不能为空' }]} initialValue="throughput">
|
||||
<Radio.Group onChange={priorityChange}>
|
||||
<Radio value="throughput">吞吐量优先</Radio>
|
||||
<Radio value="stability">稳定性优先</Radio>
|
||||
<Radio value="custom">自定义</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{!isCycle && (
|
||||
<Form.Item dependencies={['priority']} style={{ marginBottom: 0 }}>
|
||||
{({ getFieldValue }) =>
|
||||
getFieldValue('priority') === 'custom' ? (
|
||||
<div className="form-item-group">
|
||||
<Form.Item
|
||||
name="parallelNum"
|
||||
label={
|
||||
<span>
|
||||
任务并行度
|
||||
<Tooltip title="每个节点同时迁移的副本数量">
|
||||
<IconFont style={{ fontSize: '14px', marginLeft: '5px' }} type="icon-zhushi" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: `请输入!`,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={999} placeholder="请输入任务并行度" style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="executionStrategy"
|
||||
label={
|
||||
<span>
|
||||
执行策略
|
||||
<Tooltip title="不同大小副本执行的顺序">
|
||||
<IconFont style={{ fontSize: '14px', marginLeft: '5px' }} type="icon-zhushi" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: `请选择!`,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value={1}>优先最大副本</Radio>
|
||||
<Radio value={2}>优先最小副本</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{isCycle && (
|
||||
<Form.Item
|
||||
name="parallelNum"
|
||||
label={
|
||||
<span>
|
||||
任务并行度
|
||||
<Tooltip title="每个节点同时迁移的副本数量">
|
||||
<IconFont style={{ fontSize: '14px', marginLeft: '5px' }} type="icon-zhushi" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: `请输入!`,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={999} placeholder="请输入任务并行度" style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{isCycle && (
|
||||
<Form.Item
|
||||
className="schedule-cron"
|
||||
name="scheduleCron"
|
||||
label="任务周期"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: `请输入!`,
|
||||
},
|
||||
{
|
||||
validator: (_, value) => {
|
||||
const valArr = value.split(' ');
|
||||
if (valArr[1] === '*' || valArr[2] === '*') {
|
||||
return Promise.reject(new Error('任务周期必须指定分钟、小时'));
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<CronInput />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{isCycle && (
|
||||
<Form.Item
|
||||
name="executionStrategy"
|
||||
label={
|
||||
<span>
|
||||
执行策略
|
||||
<Tooltip title="不同大小副本执行的顺序">
|
||||
<IconFont style={{ fontSize: '14px', marginLeft: '5px' }} type="icon-zhushi" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: `请选择!`,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value={1}>优先最大副本</Radio>
|
||||
<Radio value={2}>优先最小副本</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item
|
||||
name="throttleUnitM"
|
||||
label="限流"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: `请输入!`,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={1} max={99999} placeholder="请输入限流" addonAfter="MB/s" style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
|
||||
{isCycle && (
|
||||
<>
|
||||
<Form.Item name="status" label="是否启用">
|
||||
<Radio.Group>
|
||||
<Radio value={1}>启用</Radio>
|
||||
<Radio value={0}>禁用</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BalanceDrawer;
|
||||
@@ -0,0 +1,183 @@
|
||||
/* eslint-disable react/display-name */
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Table, Input, InputNumber, Form, Tooltip, Button, message, IconFont } from 'knowdesign';
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||
|
||||
const EditableCell = ({ dataIndex, editable, title, inputType, handleSave, placeholder, record, index, children, ...restProps }: any) => {
|
||||
const inputRef = useRef(null);
|
||||
|
||||
const save = async () => {
|
||||
try {
|
||||
handleSave({ ...record });
|
||||
} catch (errInfo) {
|
||||
console.log('Save failed:', errInfo);
|
||||
}
|
||||
};
|
||||
const inputNode =
|
||||
inputType === 'number' ? (
|
||||
<InputNumber
|
||||
style={{ width: '130px' }}
|
||||
min={1}
|
||||
max={100}
|
||||
autoComplete="off"
|
||||
placeholder={placeholder}
|
||||
ref={inputRef}
|
||||
formatter={(value: number | string) => `${value}%`}
|
||||
parser={(value: number | string) => value!.toString().replace('%', '')}
|
||||
onPressEnter={save}
|
||||
onBlur={save}
|
||||
prefix="avg+-"
|
||||
/>
|
||||
) : (
|
||||
<Input autoComplete="off" placeholder={placeholder} />
|
||||
);
|
||||
return (
|
||||
<td {...restProps}>
|
||||
{editable ? (
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<span style={{ marginRight: '5px' }}>avg ±</span>
|
||||
<Form.Item
|
||||
name={dataIndex}
|
||||
style={{
|
||||
margin: 0,
|
||||
}}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: `请输入!`,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{inputNode}
|
||||
</Form.Item>
|
||||
</div>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</td>
|
||||
);
|
||||
};
|
||||
|
||||
const BalanceEditTable = React.forwardRef((props: any, ref: any) => {
|
||||
const { colCustomConfigs, tableData, tableDataChange } = props;
|
||||
const [form] = Form.useForm();
|
||||
const [dataSource, setDataSource] = useState(tableData);
|
||||
|
||||
useEffect(() => {
|
||||
setDataSource(tableData);
|
||||
const formData: any = {};
|
||||
tableData?.forEach((item: any) => {
|
||||
formData[`${item.type}-intervalPercent`] = item.intervalPercent;
|
||||
});
|
||||
form.setFieldsValue({ ...formData });
|
||||
}, [tableData]);
|
||||
|
||||
const editTableValidate = async () => {
|
||||
const values = await form.validateFields();
|
||||
return values;
|
||||
};
|
||||
|
||||
const handleSave = async (row: any) => {
|
||||
let values: any = {};
|
||||
form
|
||||
.validateFields()
|
||||
.then((data) => {
|
||||
values = data;
|
||||
tableChange(values);
|
||||
})
|
||||
.catch((errs) => {
|
||||
values = errs?.values;
|
||||
tableChange(values);
|
||||
});
|
||||
};
|
||||
|
||||
const tableChange = (formData: any) => {
|
||||
const newData = [...dataSource];
|
||||
Object.keys(formData).forEach((key) => {
|
||||
const type = key.split('-')[0];
|
||||
const index = newData.findIndex((item) => type === item.type);
|
||||
const item = newData[index];
|
||||
newData.splice(index, 1, {
|
||||
...item,
|
||||
intervalPercent: formData[key],
|
||||
});
|
||||
});
|
||||
tableDataChange(newData);
|
||||
};
|
||||
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
editTableValidate,
|
||||
}));
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '均衡维度',
|
||||
dataIndex: 'name',
|
||||
width: '40%',
|
||||
},
|
||||
{
|
||||
title: () => (
|
||||
<span>
|
||||
均衡区间{' '}
|
||||
<Tooltip title="单位:%,大于0,小于100">
|
||||
{' '}
|
||||
<QuestionCircleOutlined />
|
||||
</Tooltip>
|
||||
</span>
|
||||
),
|
||||
dataIndex: 'intervalPercent',
|
||||
width: '40%',
|
||||
editable: true,
|
||||
},
|
||||
{
|
||||
title: '优先级',
|
||||
dataIndex: 'priority',
|
||||
width: '20%',
|
||||
},
|
||||
];
|
||||
|
||||
const mergedColumns = columns.map((col, index) => {
|
||||
if (!col.editable) {
|
||||
return col;
|
||||
}
|
||||
|
||||
return {
|
||||
...col,
|
||||
onCell: (record: any, index: number) =>
|
||||
Object.assign(
|
||||
{
|
||||
record,
|
||||
inputType: 'number',
|
||||
dataIndex: `${record.type}-${col.dataIndex}`,
|
||||
title: col.title,
|
||||
editable: col.editable,
|
||||
index: index,
|
||||
handleSave,
|
||||
form,
|
||||
},
|
||||
colCustomConfigs?.[index]
|
||||
),
|
||||
title: colCustomConfigs?.[index]?.title || col.title,
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="edit-table-form">
|
||||
<Form form={form} component={false}>
|
||||
<Table
|
||||
components={{
|
||||
body: {
|
||||
cell: EditableCell,
|
||||
},
|
||||
}}
|
||||
dataSource={dataSource}
|
||||
columns={mergedColumns}
|
||||
rowClassName={() => 'editable-row'}
|
||||
pagination={false}
|
||||
/>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default BalanceEditTable;
|
||||
@@ -0,0 +1,240 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Button, Popover, IconFont, Row, Col, Select } from 'knowdesign';
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
|
||||
const balancePrefix = 'custom-popover-balance';
|
||||
interface FilterListType {
|
||||
id: number;
|
||||
firstLevel: string;
|
||||
secondLevel: number;
|
||||
}
|
||||
|
||||
const filterNorm = [
|
||||
{
|
||||
label: 'Disk',
|
||||
value: 'disk',
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
label: 'Byte In',
|
||||
value: 'bytesIn',
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
label: 'Byte Out',
|
||||
value: 'bytesOut',
|
||||
disabled: false,
|
||||
},
|
||||
];
|
||||
|
||||
const isBalance = [
|
||||
{
|
||||
label: '已均衡',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: '未均衡',
|
||||
value: 2,
|
||||
},
|
||||
];
|
||||
|
||||
export const BalanceFilter = (props: any) => {
|
||||
const [visible, setVisible] = useState<boolean>(false);
|
||||
const [filterList, setFilterList] = useState<FilterListType[]>(null);
|
||||
const [filterNormList, setFilterNormList] = useState(filterNorm);
|
||||
|
||||
// 添加一个筛选条件
|
||||
const addClick = (key: number) => {
|
||||
if (filterList.length >= filterNorm.length) return;
|
||||
const newFilterList = JSON.parse(JSON.stringify(filterList));
|
||||
const getDate = new Date().getTime();
|
||||
newFilterList.push({ id: getDate, firstLevel: '', secondLevel: null });
|
||||
setFilterList(newFilterList);
|
||||
};
|
||||
|
||||
// 减少一个筛选条件
|
||||
const reduceClick = (key: number) => {
|
||||
const newFilterList = filterList.filter((item, index) => {
|
||||
return item.id !== key;
|
||||
});
|
||||
// 取消已清除的指标的禁用
|
||||
const filterNewFilterList = newFilterList.map((item: any) => item.firstLevel);
|
||||
const newfilterNormList = JSON.parse(JSON.stringify(filterNorm)).map((item: any) => {
|
||||
if (filterNewFilterList.includes(item.value)) {
|
||||
return { ...item, disabled: true };
|
||||
}
|
||||
return item;
|
||||
});
|
||||
newfilterNormList.sort((a: any, b: any) => a.disabled - b.disabled);
|
||||
|
||||
setFilterList(newFilterList);
|
||||
setFilterNormList(newfilterNormList);
|
||||
};
|
||||
|
||||
// 第一列下拉操作
|
||||
const firstLevelChange = (value: any, rowId: number) => {
|
||||
const newFilterList = JSON.parse(JSON.stringify(filterList));
|
||||
const newFilterListIndex = newFilterList.findIndex((item: any) => item.id === rowId);
|
||||
newFilterList[newFilterListIndex].firstLevel = value;
|
||||
|
||||
// 控制已选中的指标无法再次被选中
|
||||
const filterNewFilterList = newFilterList.map((item: any) => item.firstLevel);
|
||||
const newfilterNormList = JSON.parse(JSON.stringify(filterNorm)).map((item: any) => {
|
||||
if (filterNewFilterList.includes(item.value)) {
|
||||
return { ...item, disabled: true };
|
||||
}
|
||||
return item;
|
||||
});
|
||||
newfilterNormList.sort((a: any, b: any) => a.disabled - b.disabled);
|
||||
|
||||
setFilterList(newFilterList);
|
||||
setFilterNormList(newfilterNormList);
|
||||
};
|
||||
|
||||
// 第二列下拉操作
|
||||
const secondLevelChange = (value: any, rowId: number) => {
|
||||
const newFilterList = JSON.parse(JSON.stringify(filterList));
|
||||
const newFilterListIndex = newFilterList.findIndex((item: any) => item.id === rowId);
|
||||
newFilterList[newFilterListIndex].secondLevel = value;
|
||||
setFilterList(newFilterList);
|
||||
};
|
||||
|
||||
const submitClick = () => {
|
||||
const newFilterList = filterList.filter((item: any) => {
|
||||
return item.firstLevel && (item.secondLevel === 0 || item.secondLevel);
|
||||
});
|
||||
props?.getNorms(newFilterList);
|
||||
onClose();
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
// 控制已选中的指标无法再次被选中
|
||||
if (props?.filterList && props?.filterList.length > 0) {
|
||||
const filterNewFilterList = props?.filterList.map((item: any) => item.firstLevel);
|
||||
const newfilterNormList = JSON.parse(JSON.stringify(filterNorm)).map((item: any) => {
|
||||
if (filterNewFilterList.includes(item.value)) {
|
||||
return { ...item, disabled: true };
|
||||
}
|
||||
return item;
|
||||
});
|
||||
newfilterNormList.sort((a: any, b: any) => a.disabled - b.disabled);
|
||||
setFilterList(props?.filterList);
|
||||
setFilterNormList(newfilterNormList);
|
||||
} else {
|
||||
setFilterList([
|
||||
{
|
||||
id: 0,
|
||||
firstLevel: null,
|
||||
secondLevel: null,
|
||||
},
|
||||
]);
|
||||
setFilterNormList(filterNorm);
|
||||
}
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// 控制已选中的指标无法再次被选中
|
||||
if (props?.filterList && props?.filterList.length > 0) {
|
||||
const filterNewFilterList = props?.filterList.map((item: any) => item.firstLevel);
|
||||
const newfilterNormList = JSON.parse(JSON.stringify(filterNorm)).map((item: any) => {
|
||||
if (filterNewFilterList.includes(item.value)) {
|
||||
return { ...item, disabled: true };
|
||||
}
|
||||
return item;
|
||||
});
|
||||
newfilterNormList.sort((a: any, b: any) => a.disabled - b.disabled);
|
||||
setFilterList(props?.filterList);
|
||||
setFilterNormList(newfilterNormList);
|
||||
} else {
|
||||
setFilterList([
|
||||
{
|
||||
id: 0,
|
||||
firstLevel: null,
|
||||
secondLevel: null,
|
||||
},
|
||||
]);
|
||||
setFilterNormList(filterNorm);
|
||||
}
|
||||
}, [props?.filterList]);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
placement="bottomLeft"
|
||||
overlayClassName={balancePrefix}
|
||||
trigger={'click'}
|
||||
title={
|
||||
<div className={`${balancePrefix}-title`}>
|
||||
<div style={{ fontSize: '16px' }}>{props?.title}</div>
|
||||
<Button type="text" icon={<CloseOutlined className="close-icon" />} onClick={onClose} />
|
||||
</div>
|
||||
}
|
||||
visible={visible}
|
||||
onVisibleChange={(visible) => setVisible(visible)}
|
||||
destroyTooltipOnHide
|
||||
content={
|
||||
<div className={`${balancePrefix}-container`}>
|
||||
<div className={`${balancePrefix}-container-main`}>
|
||||
<div>
|
||||
{filterList &&
|
||||
filterList.length > 0 &&
|
||||
filterList.map((item, index) => {
|
||||
return (
|
||||
<Row key={item.id} style={{ padding: '9px 0' }}>
|
||||
{index !== 0 && (
|
||||
<Col span={2}>
|
||||
<span className={`${balancePrefix}-container-main-andlink`}>
|
||||
<span>and</span>
|
||||
</span>
|
||||
</Col>
|
||||
)}
|
||||
<Col span={index !== 0 ? 10 : 12}>
|
||||
<Select
|
||||
size="small"
|
||||
value={item.firstLevel || null}
|
||||
placeholder="请选择"
|
||||
style={{ width: index !== 0 ? 148 : 180 }}
|
||||
options={filterNormList}
|
||||
onChange={(e) => firstLevelChange(e, item.id)}
|
||||
></Select>
|
||||
</Col>
|
||||
<Col span={10}>
|
||||
<Select
|
||||
size="small"
|
||||
value={item.secondLevel}
|
||||
placeholder="请选择"
|
||||
options={isBalance}
|
||||
style={{ width: 150 }}
|
||||
onChange={(e) => secondLevelChange(e, item.id)}
|
||||
></Select>
|
||||
</Col>
|
||||
<Col className={`${balancePrefix}-container-main-option`} span={2}>
|
||||
<span onClick={() => addClick(item.id)}>
|
||||
<IconFont type="icon-jiahao" />
|
||||
</span>
|
||||
{index !== 0 && (
|
||||
<span onClick={() => reduceClick(item.id)}>
|
||||
<IconFont type="icon-jian" />
|
||||
</span>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className={`${balancePrefix}-container-main-footer`}>
|
||||
<Button onClick={onClose} size="small">
|
||||
取消
|
||||
</Button>
|
||||
<Button onClick={submitClick} style={{ marginLeft: '10px' }} size="small" type="primary">
|
||||
确定
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Button icon={<IconFont type="icon-shaixuan" />}>筛选列表</Button>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,194 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import {
|
||||
Utils,
|
||||
Drawer,
|
||||
Button,
|
||||
Form,
|
||||
Space,
|
||||
Divider,
|
||||
AppContainer,
|
||||
Input,
|
||||
Transfer,
|
||||
message,
|
||||
IconFont,
|
||||
InputNumber,
|
||||
} from 'knowdesign';
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import api from '../../api';
|
||||
import './style/BalanceDrawer.less';
|
||||
interface PropsType extends React.HTMLAttributes<HTMLDivElement> {
|
||||
onClose: () => void;
|
||||
visible: boolean;
|
||||
isCycle?: boolean;
|
||||
formData?: any;
|
||||
genData?: any;
|
||||
}
|
||||
const ClusterNorms: React.FC<PropsType> = ({ onClose, visible, genData }) => {
|
||||
const [global] = AppContainer.useGlobalValue();
|
||||
const [form] = Form.useForm();
|
||||
const [nodeData, setNodeData] = useState([]);
|
||||
const [nodeTargetKeys, setNodeTargetKeys] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
visible && getNodeList();
|
||||
visible && getTopicList();
|
||||
}, [visible]);
|
||||
|
||||
const submit = () => {
|
||||
// 周期均衡
|
||||
form.validateFields().then((values) => {
|
||||
const params = values?.brokers?.map((item: any) => {
|
||||
const brokerId = nodeData?.filter((key) => key.brokerId === item && item)[0]?.brokerId;
|
||||
const newValue = brokerId && { brokerId, cpu: values?.cpu, disk: values?.disk, flow: values?.flow };
|
||||
return {
|
||||
clusterId: global?.clusterInfo?.id + '',
|
||||
value: JSON.stringify(newValue),
|
||||
valueGroup: 'BROKER_SPEC',
|
||||
valueName: brokerId + '',
|
||||
description: '',
|
||||
};
|
||||
});
|
||||
|
||||
Utils.put(api.putPlatformConfig(), params).then((res: any) => {
|
||||
const dataDe = res || [];
|
||||
onClose();
|
||||
message.success('设置集群规格成功');
|
||||
genData();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const nodeChange = (val: any) => {
|
||||
setNodeTargetKeys(val);
|
||||
};
|
||||
|
||||
const getNodeList = () => {
|
||||
Utils.request(api.getBrokersMetaList(global?.clusterInfo?.id), {
|
||||
method: 'GET',
|
||||
}).then((res: any) => {
|
||||
const dataDe = res || [];
|
||||
const dataHandle = dataDe.map((item: any) => {
|
||||
return {
|
||||
...item,
|
||||
key: item.brokerId,
|
||||
title: `${item.brokerId} (${item.host})`,
|
||||
};
|
||||
});
|
||||
setNodeData(dataHandle);
|
||||
});
|
||||
};
|
||||
|
||||
const getTopicList = () => {
|
||||
Utils.request(api.getPlatformConfig(global?.clusterInfo?.id, 'BROKER_SPEC')).then((res: any) => {
|
||||
const targetKeys = res?.map((item: any) => {
|
||||
return JSON.parse(item.value).brokerId;
|
||||
});
|
||||
setNodeTargetKeys(targetKeys || []);
|
||||
const newValues = JSON.parse(res?.[0].value);
|
||||
const fieldValue = {
|
||||
cpu: newValues?.cpu,
|
||||
disk: newValues?.disk,
|
||||
flow: newValues?.flow,
|
||||
brokers: targetKeys || [],
|
||||
};
|
||||
form.setFieldsValue(fieldValue);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Drawer
|
||||
title={'设置集群规格'}
|
||||
width="600px"
|
||||
destroyOnClose={true}
|
||||
className="balance-drawer"
|
||||
onClose={onClose}
|
||||
visible={visible}
|
||||
maskClosable={false}
|
||||
extra={
|
||||
<Space>
|
||||
<Button size="small" onClick={onClose}>
|
||||
取消
|
||||
</Button>
|
||||
<Button type="primary" size="small" disabled={false} onClick={submit}>
|
||||
确定
|
||||
</Button>
|
||||
|
||||
<Divider type="vertical" />
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
preserve={false}
|
||||
initialValues={{
|
||||
status: 1,
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
name="brokers"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: `请选择!`,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Transfer
|
||||
dataSource={nodeData}
|
||||
showSearch
|
||||
filterOption={(inputValue, option) => option.host.indexOf(inputValue) > -1}
|
||||
targetKeys={nodeTargetKeys}
|
||||
onChange={nodeChange}
|
||||
render={(item) => item.title}
|
||||
titles={['待选节点', '已选节点']}
|
||||
customHeader
|
||||
showSelectedCount
|
||||
locale={{ itemUnit: '', itemsUnit: '' }}
|
||||
suffix={<IconFont type="icon-fangdajing" />}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="cpu"
|
||||
label="单机核数"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: `请输入!`,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber decimalSeparator={'0'} min={0} max={99999} placeholder="请输入单机核数" addonAfter="C" style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="disk"
|
||||
label="单机磁盘"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: `请输入!`,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={99999} placeholder="请输入磁盘大小" addonAfter="GB" style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="flow"
|
||||
label="单机网络"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: `请输入!`,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={99999} placeholder="请输入单机网络" addonAfter="MB/s" style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ClusterNorms;
|
||||
@@ -0,0 +1,27 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { Dropdown, Input } from 'knowdesign';
|
||||
import Cron from 'react-cron-antd';
|
||||
// import QnnReactCron, { CronProps, CronFns } from "qnn-react-cron";
|
||||
import './style/CronInput.less';
|
||||
interface PropsType extends React.HTMLAttributes<HTMLDivElement> {
|
||||
onChange?: Function
|
||||
value?: boolean;
|
||||
}
|
||||
|
||||
const CronInput: React.FC<PropsType> = (props) => {
|
||||
const { value, onChange } = props;
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
placement="bottomLeft"
|
||||
overlayClassName="cron-input-dropDown"
|
||||
overlay={<Cron className="cron-input" style={{ width: '553px' }} value={value} onOk={onChange} />}
|
||||
>
|
||||
<Input value={value} placeholder="请输入任务周期" />
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
export default CronInput;
|
||||
@@ -0,0 +1,170 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Utils, Drawer, Button, ProTable, Space, Divider, AppContainer } from 'knowdesign';
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import moment from 'moment';
|
||||
import api from '../../api';
|
||||
import { defaultPagination } from './index';
|
||||
import PlanDrawer from './PlanDrawer';
|
||||
import { timeFormat } from '../../constants/common';
|
||||
|
||||
interface PropsType extends React.HTMLAttributes<HTMLDivElement> {
|
||||
onClose: () => void;
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
const HistoryDrawer: React.FC<PropsType> = ({ onClose, visible }) => {
|
||||
const [global] = AppContainer.useGlobalValue();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [pagination, setPagination] = useState<any>(defaultPagination);
|
||||
const [data, setData] = useState([]);
|
||||
const [planDetailData, setPlanDetailData] = useState({});
|
||||
const [planVisible, setPlanVisible] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
getList();
|
||||
}, []);
|
||||
|
||||
const columns = () => [
|
||||
{
|
||||
title: '执行时间',
|
||||
dataIndex: 'begin',
|
||||
key: 'begin',
|
||||
render: (t: number) => (t ? moment(t).format(timeFormat) : '-'),
|
||||
},
|
||||
{
|
||||
title: '结束时间',
|
||||
dataIndex: 'end',
|
||||
key: 'end',
|
||||
render: (t: number) => (t ? moment(t).format(timeFormat) : '-'),
|
||||
},
|
||||
// {
|
||||
// title: 'CPU均衡率',
|
||||
// dataIndex: 'cpu',
|
||||
// render: (text: any, row: any) => {
|
||||
// return `${row?.sub?.cpu?.successNu} (已均衡) / ${row?.sub?.cpu?.failedNu} (未均衡)`
|
||||
// }
|
||||
// },
|
||||
{
|
||||
title: 'Disk均衡率',
|
||||
dataIndex: 'disk',
|
||||
render: (text: any, row: any) => {
|
||||
return `${row?.sub?.disk?.successNu} (已均衡) / ${row?.sub?.disk?.failedNu} (未均衡)`;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'BytesIn均衡率',
|
||||
dataIndex: 'bytesIn',
|
||||
render: (text: any, row: any) => {
|
||||
return `${row?.sub?.bytesIn?.successNu} (已均衡) / ${row?.sub?.bytesIn?.failedNu} (未均衡)`;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'BytesOut均衡率',
|
||||
dataIndex: 'bytesOut',
|
||||
render: (text: any, row: any) => {
|
||||
return `${row?.sub?.bytesOut?.successNu} (已均衡) / ${row?.sub?.bytesOut?.failedNu} (未均衡)`;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 150,
|
||||
fixed: 'right',
|
||||
render: (text: any, row: any) => {
|
||||
return (
|
||||
<Button type="link" size="small" onClick={() => CheckDetail(row.jobId)}>
|
||||
查看详情
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const getList = (query = {}) => {
|
||||
const queryParams = {
|
||||
pageNo: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
...query,
|
||||
};
|
||||
|
||||
setLoading(true);
|
||||
Utils.request(api.getBalanceHistory(global?.clusterInfo?.id), {
|
||||
method: 'POST',
|
||||
data: queryParams,
|
||||
}).then(
|
||||
(res: any) => {
|
||||
const { pageNo, pageSize, pages, total } = res.pagination;
|
||||
|
||||
setPagination({
|
||||
...pagination,
|
||||
current: pageNo,
|
||||
pageSize,
|
||||
total,
|
||||
});
|
||||
const dataDe = res?.bizData || [];
|
||||
const dataHandle = dataDe.map((item: any) => {
|
||||
return {
|
||||
...item,
|
||||
};
|
||||
});
|
||||
setData(dataHandle);
|
||||
setLoading(false);
|
||||
},
|
||||
() => setLoading(false)
|
||||
);
|
||||
};
|
||||
|
||||
const CheckDetail = (jobId: any) => {
|
||||
Utils.request(api.getBalancePlan(global?.clusterInfo?.id, jobId), {
|
||||
method: 'GET',
|
||||
}).then((res: any) => {
|
||||
const dataDe = res || {};
|
||||
setPlanDetailData(dataDe);
|
||||
setPlanVisible(true);
|
||||
});
|
||||
};
|
||||
|
||||
const onTableChange = (curPagination: any) => {
|
||||
getList({ page: curPagination.current, size: curPagination.pageSize });
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<PlanDrawer visible={planVisible} onClose={() => setPlanVisible(false)} detailData={planDetailData} isPrevew={false} />
|
||||
<Drawer
|
||||
title={'均衡历史'}
|
||||
width="1080px"
|
||||
destroyOnClose={true}
|
||||
className="plan-drawer"
|
||||
onClose={onClose}
|
||||
visible={visible}
|
||||
closable={false}
|
||||
maskClosable={false}
|
||||
extra={
|
||||
<Space>
|
||||
<Button type="text" size="small" icon={<CloseOutlined />} onClick={onClose} />
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<ProTable
|
||||
tableProps={{
|
||||
showHeader: false,
|
||||
loading,
|
||||
rowKey: 'jobId',
|
||||
dataSource: data,
|
||||
paginationProps: pagination,
|
||||
columns: columns() as any,
|
||||
lineFillColor: true,
|
||||
attrs: {
|
||||
onChange: onTableChange,
|
||||
scroll: {
|
||||
x: 'max-content',
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default HistoryDrawer;
|
||||
@@ -0,0 +1,64 @@
|
||||
import React, { useState } from 'react';
|
||||
import { message, Drawer, Button, Space, Divider, AppContainer, IconFont } from 'knowdesign';
|
||||
|
||||
import RebalancePlan from '../Jobs/RebalancePlan';
|
||||
interface PropsType extends React.HTMLAttributes<HTMLDivElement> {
|
||||
onClose: () => void;
|
||||
balanceImmediate?: Function;
|
||||
visible: boolean;
|
||||
isPrevew?: boolean;
|
||||
detailData?: any;
|
||||
isEdit?: boolean;
|
||||
}
|
||||
|
||||
const PlanDrawer: React.FC<PropsType> = ({ onClose, visible, detailData, isPrevew, balanceImmediate, isEdit }) => {
|
||||
const [global] = AppContainer.useGlobalValue();
|
||||
|
||||
const submit = () => {
|
||||
if (detailData?.replicas === 0) {
|
||||
message['warning']('replicas=0,该集群已达到均衡要求,不需要再执行均衡任务。');
|
||||
} else {
|
||||
balanceImmediate && balanceImmediate();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Drawer
|
||||
title={
|
||||
<Space size={0}>
|
||||
<Button type="text" className="drawer-title-left-button" icon={<IconFont type="icon-fanhui1" />} onClick={onClose} />
|
||||
<Divider type="vertical" />
|
||||
<span>执行计划</span>
|
||||
</Space>
|
||||
}
|
||||
width="1080px"
|
||||
destroyOnClose={true}
|
||||
className="plan-drawer"
|
||||
onClose={onClose}
|
||||
visible={visible}
|
||||
maskClosable={false}
|
||||
extra={
|
||||
<Space>
|
||||
{!!isPrevew && (
|
||||
<>
|
||||
<Button size="small" onClick={onClose}>
|
||||
取消
|
||||
</Button>
|
||||
<Button className={isEdit ? '' : 'btn-width84'} type="primary" size="small" disabled={false} onClick={submit}>
|
||||
{isEdit ? '确定' : '立即均衡'}
|
||||
</Button>
|
||||
|
||||
<Divider type="vertical" />
|
||||
</>
|
||||
)}
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<RebalancePlan balanceData={detailData} />
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlanDrawer;
|
||||
@@ -0,0 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import React from 'react';
|
||||
import { timeFormat, getSizeAndUnit } from '../../constants/common';
|
||||
import moment from 'moment';
|
||||
import { Tooltip } from 'knowdesign';
|
||||
@@ -0,0 +1,145 @@
|
||||
.load-rebalance-container {
|
||||
.balance-main {
|
||||
padding: 16px 24px;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0 2px 4px 0 rgba(0,0,0,0.01), 0 3px 6px 3px rgba(0,0,0,0.01), 0 2px 6px 0 rgba(0,0,0,0.03);
|
||||
// border-radius: 12px;
|
||||
|
||||
.dcloud-table-container {
|
||||
.dot {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
zoom: 0.5;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
vertical-align: middle;
|
||||
margin-top: -2px;
|
||||
}
|
||||
.isbalance {
|
||||
.dot {
|
||||
background-color: #00C0A2;
|
||||
|
||||
}
|
||||
}
|
||||
.noBalance {
|
||||
color: #FF7066;
|
||||
.dot {
|
||||
background-color: #FF7066;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-con {
|
||||
overflow: hidden;
|
||||
margin-bottom: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.dcloud-form {
|
||||
float: left;
|
||||
.dcloud-form-item {
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.float-r {
|
||||
font-family: @font-family-bold;
|
||||
float: right;
|
||||
.dcloud-btn {
|
||||
margin-left: 8px;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.balance-drawer{
|
||||
.form-item-group {
|
||||
padding: 16px 20px 1px 20px;
|
||||
margin-bottom: 16px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.dcloud-form-item-control-input {
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-popover-balance{
|
||||
width: 432px;
|
||||
padding-top: 0 !important;
|
||||
|
||||
&-title{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&-container{
|
||||
height: 100%;
|
||||
&-main{
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
&-andlink{
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
background-color: #556EE6;
|
||||
color: #ffffff;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
&>span{
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
transform: scale(0.83,0.83);
|
||||
}
|
||||
}
|
||||
&-option{
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&-footer{
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding: 23px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dcloud-popover-content{
|
||||
min-height: 268px;
|
||||
}
|
||||
.dcloud-popover-arrow{
|
||||
display: none;
|
||||
}
|
||||
.dcloud-popover-inner{
|
||||
height: 100%;
|
||||
border-radius: 12px !important;
|
||||
box-shadow: 0 0 8px 0 rgba(0,0,0,0.04), 0 6px 12px 12px rgba(0,0,0,0.04), 0 6px 10px 0 rgba(0,0,0,0.08);
|
||||
}
|
||||
.dcloud-popover-inner-content{
|
||||
padding: 0 24px 16px;
|
||||
height: 220px;
|
||||
}
|
||||
.dcloud-popover-title{
|
||||
padding: 12px 24px 0;
|
||||
font-family: @font-family-bold;
|
||||
font-size: 16px;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
// .dcloud-table{
|
||||
// margin: 0 !important;
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,433 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Select, Form, Utils, AppContainer, Input, Button, ProTable, Badge, Tag, SearchInput } from 'knowdesign';
|
||||
import BalanceDrawer from './BalanceDrawer';
|
||||
import HistoryDrawer from './HistoryDrawer';
|
||||
import DBreadcrumb from 'knowdesign/lib/extend/d-breadcrumb';
|
||||
import { getSizeAndUnit } from '../../constants/common';
|
||||
import api from '../../api';
|
||||
import './index.less';
|
||||
import LoadRebalanceCardBar from '@src/components/CardBar/LoadRebalanceCardBar';
|
||||
import { BalanceFilter } from './BalanceFilter';
|
||||
|
||||
const Balance_Status_OPTIONS = [
|
||||
{
|
||||
label: '全部',
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
label: '已均衡',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: '未均衡',
|
||||
value: 2,
|
||||
},
|
||||
];
|
||||
|
||||
const balanceStatus: any = {
|
||||
0: '已均衡',
|
||||
2: '未均衡',
|
||||
};
|
||||
|
||||
const filterNorms: any = {
|
||||
['disk']: 'Disk',
|
||||
['bytesIn']: 'Byte In',
|
||||
['bytesOut']: 'Byte Out',
|
||||
};
|
||||
|
||||
export const defaultPagination = {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
position: 'bottomRight',
|
||||
showSizeChanger: true,
|
||||
pageSizeOptions: ['10', '20', '50', '100'],
|
||||
};
|
||||
|
||||
const LoadBalance: React.FC = (props: any) => {
|
||||
const [global] = AppContainer.useGlobalValue();
|
||||
const [form] = Form.useForm();
|
||||
const [pagination, setPagination] = useState<any>(defaultPagination);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [data, setData] = useState([]);
|
||||
const [visible, setVisible] = useState<boolean>(false);
|
||||
const [isCycle, setIsCycle] = useState<boolean>(false);
|
||||
const [planVisible, setPlanVisible] = useState<boolean>(false);
|
||||
const [circleFormData, setCircleFormData] = useState(null);
|
||||
const [trigger, setTrigger] = useState(false);
|
||||
const [filterList, setFilterList] = useState<any>(null);
|
||||
const [balanceList, setBalanceList] = useState<string>(null);
|
||||
|
||||
const [searchKeywords, setSearchKeywords] = useState<string>('');
|
||||
const [searchValue, setSearchValue] = useState<string>('');
|
||||
|
||||
const columns = () => [
|
||||
{
|
||||
title: 'Broker ID',
|
||||
dataIndex: 'brokerId',
|
||||
key: 'brokerId',
|
||||
fixed: 'left',
|
||||
width: 140,
|
||||
},
|
||||
{
|
||||
title: 'Host',
|
||||
dataIndex: 'host',
|
||||
key: 'host',
|
||||
width: 140,
|
||||
},
|
||||
{
|
||||
title: 'Leader',
|
||||
dataIndex: 'leader',
|
||||
key: 'leader',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: 'Replicas',
|
||||
dataIndex: 'replicas',
|
||||
key: 'replicas',
|
||||
width: 100,
|
||||
},
|
||||
// {
|
||||
// title: 'CPU',
|
||||
// children: [
|
||||
// {
|
||||
// title: '规格',
|
||||
// dataIndex: 'cpu_spec',
|
||||
// key: 'cpu_spec',
|
||||
// render: (text: any, row: any) => {
|
||||
// return text !== null ? `${text}` : '-';
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// title: 'AVG',
|
||||
// dataIndex: 'cpu_avg',
|
||||
// key: 'cpu_avg',
|
||||
// render: (text: any, row: any) => {
|
||||
// return text !== null ? `${text} (${(row.cpu_avg * 100 / row.cpu_spec).toFixed(2)}%)` : '-';
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// title: '是否均衡',
|
||||
// dataIndex: 'cpu_status',
|
||||
// key: 'cpu_status',
|
||||
|
||||
// render: (text: any, row: any) => {
|
||||
// // 0:已均衡,非0:未均衡
|
||||
// return text !== null ? (text === 0 ? (
|
||||
// <span className="isbalance">
|
||||
// <span className="dot"></span>已均衡
|
||||
// </span>
|
||||
// ) : (
|
||||
// <span className="noBalance">
|
||||
// <span className="dot"></span>未均衡
|
||||
// </span>
|
||||
// )) : '-';
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
title: 'Disk规格',
|
||||
dataIndex: 'disk_spec',
|
||||
key: 'disk_spec',
|
||||
width: '150px',
|
||||
render: (text: any, row: any) => {
|
||||
return text !== null ? `${text}GB` : '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Disk AVG',
|
||||
dataIndex: 'disk_avg',
|
||||
key: 'disk_avg',
|
||||
width: '200px',
|
||||
render: (text: any, row: any) => {
|
||||
return text !== null ? (
|
||||
<span>
|
||||
<Badge status={row?.disk_status === 0 ? 'success' : 'error'} />
|
||||
{`${getSizeAndUnit(text, 'B').valueWithUnit} (${((row.disk_avg * 100) / Utils.transGBToB(row.disk_spec)).toFixed(2)}%)`}
|
||||
</span>
|
||||
) : (
|
||||
'-'
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'BytesIn规格',
|
||||
dataIndex: 'bytesIn_spec',
|
||||
key: 'bytesIn_spec',
|
||||
width: '150px',
|
||||
render: (text: any, row: any) => {
|
||||
return text !== null ? `${text}MB/s` : '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'BytesIn AVG',
|
||||
dataIndex: 'bytesIn_avg',
|
||||
key: 'bytesIn_avg',
|
||||
width: '200px',
|
||||
render: (text: any, row: any) => {
|
||||
return text !== null ? (
|
||||
<span>
|
||||
<Badge status={row?.bytesIn_status === 0 ? 'success' : 'error'} />
|
||||
{`${getSizeAndUnit(text, 'B/s').valueWithUnit} (${((row.bytesIn_avg * 100) / (row.bytesIn_spec * 1024 * 1024)).toFixed(2)}%)`}
|
||||
</span>
|
||||
) : (
|
||||
'-'
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'BytesOut规格',
|
||||
dataIndex: 'bytesOut_spec',
|
||||
key: 'bytesOut_spec',
|
||||
width: '150px',
|
||||
render: (text: any, row: any) => {
|
||||
return text !== null ? `${text}MB/s` : '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'BytesOut AVG',
|
||||
dataIndex: 'bytesOut_avg',
|
||||
key: 'bytesOut_avg',
|
||||
width: '200px',
|
||||
// eslint-disable-next-line react/display-name
|
||||
render: (text: any, row: any) => {
|
||||
return text !== null ? (
|
||||
<span>
|
||||
<Badge status={row?.bytesOut_status === 0 ? 'success' : 'error'} />
|
||||
{`${getSizeAndUnit(text, 'B/s').valueWithUnit} (${((row.bytesOut_avg * 100) / (row.bytesOut_spec * 1024 * 1024)).toFixed(2)}%)`}
|
||||
</span>
|
||||
) : (
|
||||
'-'
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
getList();
|
||||
}, []);
|
||||
|
||||
const onTableChange = (curPagination: any) => {
|
||||
setPagination({
|
||||
...curPagination,
|
||||
});
|
||||
getList({ pageNo: curPagination.current, pageSize: curPagination.pageSize });
|
||||
};
|
||||
|
||||
const resetList = () => {
|
||||
setPagination({
|
||||
...pagination,
|
||||
pageNo: 1,
|
||||
});
|
||||
getList();
|
||||
};
|
||||
|
||||
const hostSearch = (e: any) => {
|
||||
setFilterList([]);
|
||||
setPagination({
|
||||
...pagination,
|
||||
pageNo: 1,
|
||||
});
|
||||
setSearchKeywords(e);
|
||||
getList({ searchKeywords: e, stateParam: balanceList });
|
||||
};
|
||||
|
||||
const getList = (query = {}) => {
|
||||
const formData = form.getFieldsValue();
|
||||
const queryParams = {
|
||||
pageNo: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
...formData,
|
||||
...query,
|
||||
};
|
||||
|
||||
setLoading(true);
|
||||
Utils.request(api.getBalanceList(global?.clusterInfo?.id), {
|
||||
method: 'POST',
|
||||
data: queryParams,
|
||||
}).then(
|
||||
(res: any) => {
|
||||
const { pageNo, pageSize, pages, total } = res.pagination;
|
||||
|
||||
setPagination({
|
||||
...pagination,
|
||||
current: pageNo,
|
||||
pageSize,
|
||||
total,
|
||||
});
|
||||
const dataDe = res?.bizData || [];
|
||||
const dataHandle = dataDe.map((item: any) => {
|
||||
return {
|
||||
...item,
|
||||
cpu_spec: item?.sub?.cpu?.spec,
|
||||
cpu_avg: item?.sub?.cpu?.avg,
|
||||
cpu_status: item?.sub?.cpu?.status,
|
||||
disk_spec: item?.sub?.disk?.spec,
|
||||
disk_avg: item?.sub?.disk?.avg,
|
||||
disk_status: item?.sub?.disk?.status,
|
||||
bytesIn_spec: item?.sub?.bytesIn?.spec,
|
||||
bytesIn_avg: item?.sub?.bytesIn?.avg,
|
||||
bytesIn_status: item?.sub?.bytesIn?.status,
|
||||
bytesOut_spec: item?.sub?.bytesOut?.spec,
|
||||
bytesOut_avg: item?.sub?.bytesOut?.avg,
|
||||
bytesOut_status: item?.sub?.bytesOut?.status,
|
||||
};
|
||||
});
|
||||
setData(dataHandle);
|
||||
setLoading(false);
|
||||
},
|
||||
() => setLoading(false)
|
||||
);
|
||||
};
|
||||
|
||||
const drawerClose = (sure?: boolean) => {
|
||||
if (sure) {
|
||||
setTrigger(!trigger);
|
||||
}
|
||||
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
const balanceClick = (val: boolean = false) => {
|
||||
if (val) {
|
||||
Utils.request(api.getBalanceForm(global?.clusterInfo?.id), {
|
||||
method: 'GET',
|
||||
})
|
||||
.then((res: any) => {
|
||||
const dataDe = res || {};
|
||||
setCircleFormData(dataDe);
|
||||
})
|
||||
.catch(() => {
|
||||
setCircleFormData(null);
|
||||
});
|
||||
} else {
|
||||
setCircleFormData(null);
|
||||
}
|
||||
setIsCycle(val);
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
const getNorms = (stateParamArr: any) => {
|
||||
const stateParam: any = {};
|
||||
stateParamArr.forEach((item: any) => {
|
||||
stateParam[item.firstLevel] = item.secondLevel;
|
||||
});
|
||||
setFilterList(stateParamArr);
|
||||
setPagination({
|
||||
...pagination,
|
||||
pageNo: 1,
|
||||
});
|
||||
setBalanceList(stateParam);
|
||||
getList({ searchKeywords, stateParam });
|
||||
};
|
||||
|
||||
const filterNormsClose = (rowId: any) => {
|
||||
const newFilterList = filterList.filter((item: any) => item.id !== rowId);
|
||||
getNorms(newFilterList);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="breadcrumb" style={{ marginBottom: '10px' }}>
|
||||
<DBreadcrumb
|
||||
breadcrumbs={[
|
||||
{ label: '多集群管理', aHref: '/' },
|
||||
{ label: global?.clusterInfo?.name, aHref: `/cluster/${global?.clusterInfo?.id}` },
|
||||
{ label: 'Load Rebalance', aHref: `` },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ margin: '12px 0' }}>
|
||||
<LoadRebalanceCardBar trigger={trigger} genData={resetList} filterList={filterList} />
|
||||
</div>
|
||||
<div className="load-rebalance-container">
|
||||
<div className="balance-main clustom-table-content">
|
||||
<div className="header-con">
|
||||
{/* <Form form={form} layout="inline" onFinish={resetList}>
|
||||
<Form.Item name="status">
|
||||
<Select className="grid-select" placeholder="请选择状态" style={{ width: '180px' }} options={Balance_Status_OPTIONS} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="searchKeywords">
|
||||
<Input placeholder="请输入Host" style={{ width: '180px' }} />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button type="primary" ghost htmlType="submit">
|
||||
查询
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form> */}
|
||||
<BalanceFilter title="负载均衡列表筛选" data={[]} getNorms={getNorms} filterList={filterList} />
|
||||
<div className="float-r">
|
||||
<SearchInput
|
||||
onSearch={hostSearch}
|
||||
attrs={{
|
||||
value: searchValue,
|
||||
onChange: setSearchValue,
|
||||
placeholder: '请输入 Host',
|
||||
style: { width: '210px' },
|
||||
maxLength: 128,
|
||||
}}
|
||||
/>
|
||||
<Button type="primary" ghost onClick={() => setPlanVisible(true)}>
|
||||
均衡历史
|
||||
</Button>
|
||||
<Button type="primary" ghost onClick={() => balanceClick(true)}>
|
||||
周期均衡
|
||||
</Button>
|
||||
<Button type="primary" onClick={() => balanceClick(false)}>
|
||||
立即均衡
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{filterList && filterList.length > 0 && (
|
||||
<div style={{ marginBottom: '12px' }}>
|
||||
<span style={{ marginRight: '6px' }}>筛选结果:{pagination?.total || 0}条</span>
|
||||
{filterList.map((item: any) => {
|
||||
return (
|
||||
<Tag
|
||||
style={{ padding: '6px 10px', backgroundColor: 'rgba(33,37,41,0.08)', color: '#495057', borderRadius: '6px' }}
|
||||
key={item.id}
|
||||
closable
|
||||
onClose={() => filterNormsClose(item.id)}
|
||||
>
|
||||
<span>{filterNorms[item.firstLevel]}:</span>
|
||||
<span>{balanceStatus[item.secondLevel]}</span>
|
||||
</Tag>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<ProTable
|
||||
tableProps={{
|
||||
showHeader: false,
|
||||
loading,
|
||||
rowKey: 'brokerId',
|
||||
dataSource: data,
|
||||
paginationProps: pagination,
|
||||
columns: columns() as any,
|
||||
lineFillColor: true,
|
||||
attrs: {
|
||||
onChange: onTableChange,
|
||||
// bordered: false,
|
||||
// className: 'remove-last-border',
|
||||
scroll: { x: 'max-content', y: 'calc(100vh - 440px)' },
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<BalanceDrawer visible={visible} isCycle={isCycle} onClose={drawerClose} formData={circleFormData} genData={getList} />
|
||||
{planVisible && (
|
||||
<HistoryDrawer
|
||||
visible={planVisible}
|
||||
onClose={() => {
|
||||
setPlanVisible(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoadBalance;
|
||||
@@ -0,0 +1,19 @@
|
||||
.balance-drawer {
|
||||
.form-title {
|
||||
font-size: 16px;
|
||||
color: #212529;
|
||||
letter-spacing: 0;
|
||||
line-height: 25px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
.schedule-cron {
|
||||
.dcloud-select {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.dcloud-drawer-body{
|
||||
padding: 0 20px 14px !important;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
.cron-input-dropDown {
|
||||
.cron-input {
|
||||
width: 553px;
|
||||
}
|
||||
.ant-tabs-nav-list {
|
||||
// 秒
|
||||
// .ant-tabs-tab:nth-child(1) {
|
||||
// display: none;
|
||||
// }
|
||||
.ant-tabs-tab:nth-child(7) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.ant-tabs-content {
|
||||
// 秒
|
||||
.ant-tabs-tabpane:nth-child(1) {
|
||||
.ant-radio-group {
|
||||
.ant-radio-wrapper:nth-child(2) {
|
||||
display: none !important;
|
||||
}
|
||||
.ant-radio-wrapper:nth-child(3) {
|
||||
display: none !important;
|
||||
}
|
||||
.ant-radio-wrapper:nth-child(4) {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 分
|
||||
.ant-tabs-tabpane:nth-child(2) {
|
||||
.ant-radio-group {
|
||||
.ant-radio-wrapper:nth-child(1) {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 时
|
||||
.ant-tabs-tabpane:nth-child(3) {
|
||||
.ant-radio-group {
|
||||
.ant-radio-wrapper:nth-child(1) {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user