初始化3.0.0版本

This commit is contained in:
zengqiao
2022-08-18 17:04:05 +08:00
parent 462303fca0
commit 51832385b1
2446 changed files with 93177 additions and 127211 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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>
);
};

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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';

View File

@@ -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;
// }
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}
}
}