初始化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,191 @@
import React, { useEffect, useState } from 'react';
import { Badge, ProTable, Tooltip, Utils, Progress } from 'knowdesign';
import { formatAssignSize, runningStatusEnum } from './config';
const columns: any = [
{
title: 'Partition',
dataIndex: 'partitionId',
key: 'partitionId',
render: (t: any, r: any) => {
// return runningStatusEnum[r?.status];
return (
<span>
{t}
<Badge
style={{ marginLeft: '6px' }}
status={r?.status === 1 ? 'warning' : r?.status === 4 ? 'error' : r?.status === 3 ? 'success' : 'warning'}
/>
</span>
);
},
},
{
title: '源BrokerID',
dataIndex: 'sourceBrokerIds',
key: 'sourceBrokerIds',
render: (t: any, r: any) => {
return t && t.length > 0 ? t.join('、') : '-';
},
},
{
title: '目标BrokerID',
dataIndex: 'desBrokerIds',
key: 'desBrokerIds',
render(t: any, r: any) {
return t && t.length > 0 ? t.join('、') : '-';
},
},
// {
// title: '需迁移MessageSizeMB',
// dataIndex: 'totalSize',
// key: 'totalSize',
// render(t: any, r: any) {
// return t || t === 0 ? formatAssignSize(t, 'MB') : '-';
// },
// },
// {
// title: '已完成MessageSize MB',
// dataIndex: 'movedSize',
// key: 'movedSize',
// render(t: any, r: any) {
// return t || t === 0 ? formatAssignSize(t, 'MB') : '-';
// },
// },
{
title: 'MessageSize迁移进度(MB)',
dataIndex: 'movedSize',
key: 'movedSize',
render(t: any, r: any) {
const movedSize = r.movedSize ? Number(Utils.formatAssignSize(t, 'MB')) : 0;
const totalSize = r.totalSize ? Number(Utils.formatAssignSize(t, 'MB')) : 0;
return (
<div className="message-size">
<Tooltip title={(movedSize > 0 && totalSize > 0 ? (movedSize / totalSize) * 100 : 0) + '%'}>
<Progress
percent={movedSize > 0 && totalSize > 0 ? (movedSize / totalSize) * 100 : 0}
strokeColor="#556EE6"
trailColor="#ECECF1"
showInfo={false}
/>
</Tooltip>
<span>{movedSize + '/' + totalSize}</span>
</div>
);
},
},
// {
// title: '状态',
// key: 'status',
// width: 100,
// render: (t: any, r: any) => {
// return (
// <span>
// <Badge status={r?.status === 1 ? 'warning' : r?.status === 4 ? 'error' : r?.status === 3 ? 'success' : 'warning'} />
// {runningStatusEnum[r?.status]}
// </span>
// );
// },
// },
// {
// title: 'BytesInMB/s',
// dataIndex: 'byteIn',
// key: 'byteIn',
// render(t: any, r: any) {
// return t ? t : '-';
// },
// },
// {
// title: '同步速率MB/s',
// dataIndex: 'byteMove',
// key: 'byteMove',
// render(t: any, r: any) {
// return t ? t : '-';
// },
// },
{
title: '预计剩余时长',
dataIndex: 'remainTime',
key: 'remainTime',
render(t: any, r: any) {
return t ? Utils.transUnitTime(t) : t === 0 ? t : '-';
},
},
];
export const ExpandedRow: any = ({ record, data, loading }: any) => {
const [pagination, setPagination] = useState<any>({
current: 1,
pageSize: 5,
// total: data[record.key]?.length,
simple: true,
hideOnSinglePage: false,
});
const [status, setStatus] = useState({
total: 0,
success: 0,
doing: 0,
fail: 0,
});
const onTableChange = (pagination: any, filters: any) => {
setPagination(pagination);
};
const calcStatus = () => {
const success = data[record.key]?.filter((item: any) => runningStatusEnum[item.status] === 'Success').length || 0;
const doing = data[record.key]?.filter((item: any) => runningStatusEnum[item.status] === 'Doing').length || 0;
const fail = data[record.key]?.filter((item: any) => runningStatusEnum[item.status] === 'Fail').length || 0;
const total = data[record.key]?.length || 0;
setStatus({ total, success, doing, fail });
};
useEffect(() => {
calcStatus();
}, [data[record.key]]);
return (
<div
key={record.key}
style={{ position: 'relative', padding: '12px 16px', border: '1px solid #EFF2F7', borderRadius: '8px', backgroundColor: '#ffffff' }}
>
<ProTable
// bordered
tableProps={{
showHeader: false,
loading: loading[record.key],
rowKey: 'key',
dataSource: data[record.key] || [],
columns,
lineFillColor: true,
// noPagination: true,
paginationProps: pagination,
attrs: {
className: 'expanded-table',
onChange: onTableChange,
scroll: { x: 'max-content' },
size: 'small',
bordered: false,
rowClassName: 'table-small-bgcolor',
},
}}
/>
<div style={{ position: 'absolute', bottom: '12px', left: '16px' }}>
<span></span>
<span>Partition总数 {(status?.success || 0) + (status?.doing || 0) + (status?.fail || 0)}</span>
<span style={{ marginLeft: '34px', display: 'inline-block' }}>
<Badge status="success" />
Success {status?.success || 0}
</span>
<span style={{ marginLeft: '34px', display: 'inline-block' }}>
<Badge status="error" />
Fail {status?.fail || 0}
</span>
<span style={{ marginLeft: '34px', display: 'inline-block' }}>
<Badge status="warning" />
Doing {status?.doing || 0}
</span>
{/* <Pagination size="small" onChange={onTableChange} simple total={data[record.key]?.length} /> */}
</div>
</div>
);
};

View File

@@ -0,0 +1,99 @@
import React, { useState, useEffect } from 'react';
import { Alert, Badge, Dropdown, ProTable, Space, Table, Utils } from 'knowdesign';
import { useParams } from 'react-router-dom';
import Api from '@src/api';
import { getNodeTrafficColumns } from './config';
const { request, post } = Utils;
const NodeTraffic = (props: any) => {
const { hashData } = props;
const urlParams = useParams<any>(); // 获取地址栏参数
const [loading, setLoading] = useState(false);
const [data, setData] = useState([]);
const [pagination, setPagination] = useState<any>({
current: 1,
pageSize: 10,
position: 'bottomRight',
showSizeChanger: true,
pageSizeOptions: ['10', '20', '50', '100', '200', '500'],
});
const [popoverVisible, setPopoverVisible] = useState(false);
// 获取
const genData = async ({ pageNo, pageSize }: any) => {
if (urlParams?.clusterId === undefined) return;
setData([]);
setLoading(true);
post(Api.getJobNodeTraffic(urlParams?.clusterId, props?.jobId))
.then((res: any) => {
// setPagination({
// current: res.pagination?.pageNo,
// pageSize: res.pagination?.pageSize,
// total: res.pagination?.total,
// });
const mockData = [];
for (let i = 0; i < 20; i++) {
mockData.push({
byteInJob: 0,
byteInTotal: 0,
byteOutJob: 0,
byteOutTotal: 0,
createTime: 1645608135717,
host: 'string',
id: 0,
updateTime: 1645608135717,
});
}
setData(res || mockData);
// setData(res?.bizData || []);
setLoading(false);
})
.catch((err) => {
setLoading(false);
});
};
const onTableChange = (pagination: any, filters: any, sorter: any) => {
// setPagination(pagination);
// const asc = sorter?.order && sorter?.order === 'ascend' ? true : false;
// const sortColumn = sorter.field && toLine(sorter.field);
genData({ pageNo: pagination.current, pageSize: pagination.pageSize });
};
useEffect(() => {
genData({
pageNo: 1,
pageSize: pagination.pageSize,
// sorter: defaultSorter
});
}, []);
return (
<>
<div style={{ maxWidth: '1032px' }}>
<ProTable
showQueryForm={false}
tableProps={{
showHeader: false,
rowKey: 'key',
loading: loading,
columns: getNodeTrafficColumns({ popoverVisible, setPopoverVisible }),
dataSource: data,
paginationProps: { ...pagination },
attrs: {
bordered: false,
// className: 'frameless-table', // 纯无边框表格类名
onChange: onTableChange,
size: 'middle',
},
}}
/>
</div>
</>
);
};
export default NodeTraffic;

View File

@@ -0,0 +1,75 @@
import React, { useState } from 'react';
import { Button, ProTable, Popover } from 'knowdesign';
import { CloseOutlined } from '@ant-design/icons';
export const PopoverBroker = (props: any) => {
const [visible, setVisible] = useState<boolean>(false);
const columns = [
{
title: 'BrokerID',
dataIndex: 'brokerId',
key: 'brokerHost',
},
{
title: 'Host',
dataIndex: 'brokerHost',
key: 'brokerHost',
},
];
const newPropsData = Object.keys(props.data).map((item) => {
return {
brokerId: item,
brokerHost: props.data[item],
};
});
return (
<Popover
// getPopupContainer={(triggerNode: any) => {
// return triggerNode;
// }}
placement="bottomLeft"
overlayClassName="custom-popover-borker"
trigger={'click'}
title={
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ fontSize: '16px' }}>{props?.title}</div>
<Button
type="text"
icon={<CloseOutlined className="close-icon" />}
onClick={() => {
setVisible(false);
}}
/>
</div>
}
visible={visible}
onVisibleChange={(visible) => setVisible(visible)}
content={
<div className="container">
<div className="main">
<ProTable
showQueryForm={false}
tableProps={{
noPagination: true,
showHeader: false,
rowKey: 'key',
// loading: loading,
columns,
dataSource: newPropsData,
// paginationProps: { ...pagination },
attrs: {
bordered: false,
// className: 'frameless-table', // 纯无边框表格类名
// onChange: onTableChange,
size: 'middle',
},
}}
/>
</div>
</div>
}
>
{newPropsData && newPropsData.length > 0 && <a style={{ display: 'inline-block', marginLeft: '8px' }}></a>}
</Popover>
);
};

View File

@@ -0,0 +1,294 @@
import React, { useState, useEffect } from 'react';
import { Alert, Badge, Descriptions, Dropdown, ProTable, DTable, Table, Utils, Spin, Tag } from 'knowdesign';
import { useParams } from 'react-router-dom';
import Api from '@src/api';
import { getNodeTrafficColumns } from './config';
import { getSizeAndUnit } from '@src/constants/common';
interface PropsType {
jobId?: any;
balanceData?: any;
status?: any;
[name: string]: any;
// [clasgfag: any]: any;
}
const typeObj: any = {
1: '周期均衡',
2: '立即均衡',
};
const { request, post } = Utils;
const RebalancePlan = (props: PropsType) => {
const { jobId, balanceData, status } = props;
const urlParams = useParams<any>(); // 获取地址栏参数
const [loading, setLoading] = useState(false);
const [data, setData] = useState<any>({});
const [pagination, setPagination] = useState<any>({
current: 1,
pageSize: 10,
position: 'bottomRight',
showSizeChanger: true,
pageSizeOptions: ['10', '20', '50', '100', '200', '500'],
// hideOnSinglePage: false,
// showTotal: (total: number) => `共 ${total} 条目`,
});
// 获取
const genData = async ({ pageNo, pageSize }: any) => {
if (urlParams?.clusterId === undefined) return;
setLoading(true);
if (balanceData) {
setData(balanceData);
setLoading(false);
return;
}
setData([]);
request(Api.getJobsPlanRebalance(urlParams?.clusterId, jobId))
.then((res: any) => {
// setPagination({
// current: res.pagination?.pageNo,
// pageSize: res.pagination?.pageSize,
// total: res.pagination?.total,
// });
// setData(mockData);
setData(res || []);
setLoading(false);
})
.catch((err) => {
setLoading(false);
});
};
const onTableChange = (pagination: any, filters: any, sorter: any) => {
setPagination(pagination);
// const asc = sorter?.order && sorter?.order === 'ascend' ? true : false;
// const sortColumn = sorter.field && toLine(sorter.field);
// genData({ pageNo: pagination.current, pageSize: pagination.pageSize });
};
useEffect(() => {
genData({
pageNo: 1,
pageSize: pagination.pageSize,
// sorter: defaultSorter
});
}, []);
const columns: any = [
{
title: '节点',
dataIndex: 'host',
key: 'host',
fixed: 'left',
render: (t: any, r: any) => {
return (
<span>
{t}
<Badge style={{ marginLeft: '4px' }} status={r?.status === 0 ? 'success' : 'error'} />
</span>
);
},
},
{
title: (
<div>
Disk使用率
<span className="balance-list-title">
{`(`}
<span className="titleImg"></span>
{`)`}
</span>
</div>
),
dataIndex: 'diskBefore',
key: 'diskBefore',
width: 140,
render: (t: any, r: any) => {
const befaore = r?.diskBefore || r?.diskBefore === 0 ? r?.diskBefore.toFixed(2) + '%' : '0%';
const after = r?.diskAfter || r?.diskAfter === 0 ? r?.diskAfter.toFixed(2) + '%' : '0%';
return (
<span className="balance-list-contert">
<span className="balance-list-contert-text">{befaore}</span>
<span className="balance-list-contert-img"></span>
<span className="balance-list-contert-text-right">{after}</span>
</span>
);
},
},
{
title: (
<div>
BytesIn使用率
<span className="balance-list-title">
{`(`}
<span className="titleImg"></span>
{`)`}
</span>
</div>
),
dataIndex: 'byteInBefore',
key: 'byteInBefore',
width: 140,
render: (t: any, r: any) => {
const befaore = r?.byteInBefore || r?.byteInBefore === 0 ? r?.byteInBefore.toFixed(2) + '%' : '0%';
const after = r?.byteInAfter || r?.byteInAfter === 0 ? r?.byteInAfter.toFixed(2) + '%' : '0%';
return (
<span className="balance-list-contert">
<span className="balance-list-contert-text">{befaore}</span>
<span className="balance-list-contert-img"></span>
<span className="balance-list-contert-text-right">{after}</span>
</span>
);
},
},
{
title: (
<div>
BytesOut使用率
<span className="balance-list-title">
{`(`}
<span className="titleImg"></span>
{`)`}
</span>
</div>
),
dataIndex: 'byteOutBefore',
key: 'byteOutBefore',
width: 140,
render: (t: any, r: any) => {
const befaore = r?.byteOutBefore || r?.byteOutBefore === 0 ? r?.byteOutBefore.toFixed(2) + '%' : '0%';
const after = r?.byteOutAfter || r?.byteOutAfter === 0 ? r?.byteOutAfter.toFixed(2) + '%' : '0%';
return (
<span className="balance-list-contert">
<span className="balance-list-contert-text">{befaore}</span>
<span className="balance-list-contert-img"></span>
<span className="balance-list-contert-text-right">{after}</span>
</span>
);
},
},
{
title: '移入情况',
dataIndex: 'inReplica',
key: 'inReplica',
render: (t: any, r: any) => {
return (t || t === 0 ? t : 0) + '/' + (r?.inSize || r?.inSize === 0 ? getSizeAndUnit(r?.inSize, 'B', 0).valueWithUnit : 0);
},
},
{
title: '移出情况',
dataIndex: 'outReplica',
key: 'outReplica',
render: (t: any, r: any) => {
return (t || t === 0 ? t : 0) + '/' + (r?.outSize || r?.outSize === 0 ? getSizeAndUnit(r?.outSize, 'B', 0).valueWithUnit : 0);
},
},
];
const mockdata: any = [];
for (let i = 0; i < 100; i++) {
mockdata.push({
key: i,
name: 'John Brown',
age: i + 1,
street: 'Lake Park',
building: 'C',
number: 2035,
companyAddress: 'Lake Street 42',
companyName: 'SoftLake Co',
gender: 'M',
});
}
// const download= (url:any, name:any)=>{
// const a:any = document.createElement(\'a\')
// a.download = name
// a.rel = \'noopener\'
// a.href = url
// // 触发模拟点击
// // a.dispatchEvent(new MouseEvent(\'click\'))
// // 或者模拟点击
// a.click()
// }
return (
<>
<h3 style={{ fontSize: '16px' }}></h3>
<Spin spinning={loading}>
<Descriptions
style={{ fontSize: '13px' }}
column={2}
labelStyle={{
width: '100px',
textAlign: 'right',
display: 'flex',
justifyContent: 'end',
color: '#74788D',
fontSize: '13px',
}}
contentStyle={{ fontSize: '13px' }}
>
<Descriptions.Item labelStyle={{ width: '79px' }} label="任务类型">
{typeObj[data?.type] || '-'}
</Descriptions.Item>
<Descriptions.Item labelStyle={{ width: '79px' }} label="总迁移大小">
{Utils.getSizeAndUnit(data?.moveSize, 'B').valueWithUnit}
</Descriptions.Item>
<Descriptions.Item label="Topic黑名单">
{data?.blackTopics && data?.blackTopics?.length > 0 ? data?.blackTopics.join('、') : '-'}
</Descriptions.Item>
<Descriptions.Item labelStyle={{ width: '79px' }} label="迁移副本数">
{data?.replicas || '-'}
</Descriptions.Item>
<Descriptions.Item labelStyle={{ width: '79px' }} label="均衡阈值">
{data?.clusterBalanceIntervalList
? data?.clusterBalanceIntervalList?.map((item: any) => {
return (
<Tag style={{ padding: '4px 8px', backgroundColor: 'rgba(33,37,41,0.08)', marginRight: '4px' }} key={item?.priority}>
{item.type + ':' + item.intervalPercent + '%'}
</Tag>
);
})
: '-'}
</Descriptions.Item>
</Descriptions>
</Spin>
<h3 style={{ fontSize: '16px' }}></h3>
<div style={{ maxWidth: '1032px' }}>
{/* <Table columns={columns} dataSource={mockdata} bordered scroll={{ x: 'max-content' }}></Table> */}
<ProTable
tableProps={{
showHeader: false,
loading,
rowKey: 'key',
dataSource: data?.detail,
paginationProps: pagination,
columns,
lineFillColor: true,
attrs: {
onChange: onTableChange,
scroll: { x: 'max-content' },
// className: 'remove-last-border',
},
}}
/>
</div>
{data?.reassignmentJson && (
<>
<h3 style={{ fontSize: '16px', marginTop: '22px' }}></h3>
<div>
<a href={`data:,${data?.reassignmentJson}`} rel="noopener" download="reassignment.json">
Reassignment json file
</a>
</div>
</>
)}
</>
);
};
export default RebalancePlan;

View File

@@ -0,0 +1,148 @@
/* eslint-disable react/display-name */
import React, { useState, useEffect } from 'react';
import { Alert, Badge, Dropdown, IconFont, ProTable, Space, Table, Utils } from 'knowdesign';
import { useParams } from 'react-router-dom';
import Api from '@src/api';
import { getTaskDetailsColumns, getMoveBalanceColumns } from './config';
import { ExpandedRow } from './ExpandedRow';
const { request, post } = Utils;
const TaskDetails = (props: any) => {
const { hashData } = props;
const urlParams = useParams<any>(); // 获取地址栏参数
const [loading, setLoading] = useState(false);
const [data, setData] = useState([]);
const [expandedData, setExpandedData] = useState([]);
const [loadingObj, setLoadingObj] = useState<any>({});
const [pagination, setPagination] = useState<any>({
current: 1,
pageSize: 10,
position: 'bottomRight',
showSizeChanger: true,
pageSizeOptions: ['10', '20', '50', '100'],
});
// TODO 获取行详情数据接口
const getRowDetailData = (topicName: string, key?: number) => {
return post(Api.getJobPartitionDetail(urlParams?.clusterId, props?.jobId, topicName));
};
// TODO 获取行详情数据接口
const queryExpandedData = async (record: any, key: any) => {
if (urlParams?.clusterId === undefined) return;
try {
const table = { ...expandedData };
const loading = { ...loadingObj };
getRowDetailData(record.topicName).then((res) => {
table[key] = res;
loading[key] = false;
setExpandedData(table);
setLoadingObj(loading);
});
} catch (error) {
console.log(error);
}
};
const onClickExpand = (expanded: any, record: any) => {
const key = record?.key;
// 之前展开过
if (expandedData[key]?.length) return;
// 第一次展开
const loading = { ...loadingObj };
loading[key] = true;
setLoadingObj(loading);
queryExpandedData(record, key);
};
const onTableChange = (pagination: any, filters: any, sorter: any) => {
setPagination(pagination);
// const asc = sorter?.order && sorter?.order === 'ascend' ? true : false;
// const sortColumn = sorter.field && toLine(sorter.field);
// genData({ pageNo: pagination.current, pageSize: pagination.pageSize });
};
const newData =
props?.detailData?.subJobs?.map((item: any, index: number) => {
return {
...item,
key: index,
};
}) || [];
return (
<>
<Alert
message={
<div style={{ color: '#592D00', fontSize: '14px' }}>
<span></span>
<span>Topic总数 {newData?.length}</span>
<span style={{ marginLeft: '34px', display: 'inline-block' }}>
<Badge status="success" />
Success {props?.detailData?.success}
</span>
<span style={{ marginLeft: '34px', display: 'inline-block' }}>
<Badge status="error" />
Fail {props?.detailData?.fail}
</span>
<span style={{ marginLeft: '34px', display: 'inline-block' }}>
<Badge status="warning" />
Doing {props?.detailData?.doing}
</span>
</div>
}
style={{ background: '#FFF9E6', padding: '6px 12px', border: 'none', marginBottom: '12px', borderRadius: '4px' }}
/>
<div className="job-detail">
<ProTable
showQueryForm={false}
tableProps={{
showHeader: false,
rowKey: 'key',
loading: loading,
columns: props?.detailData?.jobType === 1 ? getTaskDetailsColumns() : getMoveBalanceColumns(),
dataSource: newData,
paginationProps: { ...pagination },
// noPagination: true,
attrs: {
bordered: false,
onChange: onTableChange,
tableLayout: 'auto',
scroll: { x: 'max-content' },
expandable: {
expandedRowRender: (r: any) => <ExpandedRow record={r} data={expandedData} loading={loadingObj} />,
// expandedRowRender,
onExpand: onClickExpand,
columnWidth: '20px',
fixed: 'left',
expandIcon: ({ expanded, onExpand, record }: any) => {
return expanded ? (
<IconFont
style={{ fontSize: '16px' }}
type="icon-xia"
onClick={(e: any) => {
onExpand(record, e);
}}
/>
) : (
<IconFont
style={{ fontSize: '16px' }}
type="icon-jiantou_1"
onClick={(e: any) => {
onExpand(record, e);
}}
/>
);
},
},
style: {
width: '1032px',
},
},
}}
/>
</div>
</>
);
};
export default TaskDetails;

View File

@@ -0,0 +1,149 @@
import React, { useState, useEffect } from 'react';
import moment from 'moment';
import { useParams } from 'react-router-dom';
import { Button, Drawer, Utils, Descriptions, Tabs, Input, IconFont, message, Spin, InputNumber } from 'knowdesign';
import TaskDetails from './TeskDetails';
import NodeTraffic from './NodeTraffic';
import RebalancePlan from './RebalancePlan';
import { jobTypeEnum, runningStatusEnum } from './config';
import { timeFormat, getSizeAndUnit } from '../../constants/common';
import Api from '@src/api';
import { CheckOutlined, CloseOutlined } from '@ant-design/icons';
const { request, post } = Utils;
const { TabPane } = Tabs;
export const ViewJobsProgress = (props: any) => {
const urlParams = useParams<any>(); // 获取地址栏参数
const [detailData, setDetailData] = useState(null);
const [loading, setLoading] = useState(false);
const [positionType, setPositionType] = useState<string>('TeskDetails');
const [editFlowLimitStatus, setEditFlowLimitStatus] = useState<boolean>(false); // 修改限流状态
const [flowLimit, setFlowLimit] = useState<any>(); // 修改限流数值
const callback = (key: any) => {
// setSearchValue('');
// setSearchKeywords('');
setPositionType(key);
};
// * 修改限流事件
const editFlowLimit = () => {
if (!flowLimit) {
message.error('限流值不能为空');
return;
}
if (flowLimit < 0) {
message.error('限流值最小值为0');
return;
}
const newSize = Utils.transMBToB(flowLimit);
post(Api.getJobTraffic(urlParams.clusterId, props?.record?.id, newSize)).then((res) => {
message.success('修改限流成功');
getDetailData();
setEditFlowLimitStatus(false);
});
};
// * 获取详情和任务明细数据
const getDetailData = () => {
setLoading(true);
props?.record &&
request(Api.getJobDetail(urlParams.clusterId, props?.record?.id))
.then((res: any) => {
setDetailData(res || []);
const newFlowLimit = Utils.transBToMB(res?.flowLimit as number);
setFlowLimit(newFlowLimit);
// setDetailData(res || mockData);
setLoading(false);
})
.catch((err) => {
setLoading(false);
});
};
useEffect(() => {
getDetailData();
}, [props?.record]);
return (
<Drawer
width={1080}
title="查看进度"
visible={props.visible}
onClose={() => props.setVisble(false)}
destroyOnClose={true}
maskClosable={false}
>
<Spin spinning={loading}>
<Descriptions
style={{ fontSize: '13px' }}
column={3}
labelStyle={{
width: '92px',
textAlign: 'right',
display: 'flex',
justifyContent: 'end',
color: '#74788D',
fontSize: '13px',
}}
contentStyle={{ fontSize: '13px' }}
>
<Descriptions.Item label="任务类型">{jobTypeEnum[detailData?.jobType] || '-'}</Descriptions.Item>
<Descriptions.Item label="运行状态">{runningStatusEnum[detailData?.jobStatus] || '-'}</Descriptions.Item>
<Descriptions.Item label="限流">
{/* 修改限流 */}
<div>
{editFlowLimitStatus ? (
<div className="edit-flow-limit" style={{ display: 'flex', alignItems: 'center' }}>
<InputNumber
controls={false}
size="small"
defaultValue={Utils.transBToMB(detailData?.flowLimit)}
value={flowLimit}
onChange={(e: any) => setFlowLimit(e)}
min={0}
max={99999}
style={{ width: '150px' }}
/>
<CheckOutlined style={{ margin: '0 10px', color: 'green' }} onClick={editFlowLimit} />
<CloseOutlined style={{ color: 'red' }} onClick={() => setEditFlowLimitStatus(false)} />
</div>
) : (
<span>
{detailData?.flowLimit ? Utils.transBToMB(detailData?.flowLimit) + ' MB/s ' : '-'}
{runningStatusEnum[detailData?.jobStatus] && runningStatusEnum[detailData?.jobStatus] !== 'Success' ? (
<IconFont style={{ fontSize: '14px' }} type="icon-bianji1" onClick={() => setEditFlowLimitStatus(true)} />
) : null}
</span>
)}
</div>
</Descriptions.Item>
<Descriptions.Item label="计划执行时间">
{detailData?.planTime ? moment(detailData?.planTime).format(timeFormat) : '-'}
</Descriptions.Item>
<Descriptions.Item label="实际执行时间">
{detailData?.startTime ? moment(detailData?.startTime).format(timeFormat) : '-'}
</Descriptions.Item>
<Descriptions.Item label="任务完成时间">
{detailData?.endTime ? moment(detailData?.endTime).format(timeFormat) : '-'}
</Descriptions.Item>
</Descriptions>
</Spin>
<Tabs className={'custom_tabs_class'} defaultActiveKey="Configuration" onChange={callback} destroyInactiveTabPane>
{props?.record?.jobType === 2 && (
<TabPane tab="均衡计划" key="Rebalance">
<RebalancePlan status={props?.record?.jobStatus} jobId={props?.record?.id} />
</TabPane>
)}
{/* {
<TabPane tab="均衡计划" key="Rebalance">
<RebalancePlan jobId={props?.record?.id} />
</TabPane>
} */}
<TabPane tab="任务明细" key="TeskDetails">
{<TaskDetails detailData={detailData} jobId={props?.record?.id} />}
</TabPane>
<TabPane tab="节点流量情况" key="NodeTraffic">
{<NodeTraffic jobId={props?.record?.id} />}
</TabPane>
</Tabs>
</Drawer>
);
};

View File

@@ -0,0 +1,669 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import React from 'react';
import { timeFormat, getSizeAndUnit } from '../../constants/common';
import moment from 'moment';
import { Tooltip, Tag, Badge, Utils, Progress } from 'knowdesign';
import { renderTableOpts } from 'knowdesign/es/common-pages/render-table-opts';
import TagsWithHide from '@src/components/TagsWithHide';
import { PopoverBroker } from './PopoverBroker';
// 任务类型下拉
export const jobType = [
{
label: 'Topic迁移',
value: 0,
},
{
label: '扩缩副本',
value: 1,
},
process.env.BUSSINESS_VERSION
? {
label: '集群均衡',
value: 2,
}
: undefined,
].filter((t) => t);
//
export const jobTypeEnum: any = {
0: 'Topic迁移',
1: '扩缩副本',
2: '集群均衡',
};
// 运行状态下拉
export const runningStatus = [
{
label: 'Doing',
value: 1,
},
{
label: 'Prepare',
value: 2,
},
{
label: 'Success',
value: 3,
},
{
label: 'Failed',
value: 4,
},
{
label: 'Canceled',
value: 5,
},
];
export const runningStatusEnum: any = {
1: 'Doing',
2: 'Prepare',
3: 'Success',
4: 'Failed',
5: 'Canceled',
};
export const getJobsListColumns = (arg?: any) => {
const columns = [
// {
// title: '任务队列',
// dataIndex: 'taskQueue',
// key: 'taskQueue',
// },
{
title: '任务ID',
dataIndex: 'id',
key: 'id',
},
{
title: '任务类型',
dataIndex: 'jobType',
key: 'jobType',
render(t: any, r: any) {
return jobTypeEnum[t];
},
},
{
title: '任务执行对象',
dataIndex: 'target',
key: 'target',
render(t: any, r: any) {
return (
<div style={{ width: '232px' }}>
<TagsWithHide placement="bottom" list={t.split(',')} expandTagContent={(num: any) => `共有${num}`} />
</div>
);
},
},
{
title: '运行状态',
dataIndex: 'jobStatus',
key: 'jobStatus',
render(t: any, r: any) {
return (
<Tag
className="12312512512"
style={{
background: t === 1 ? 'rgba(85,110,230,0.10)' : t === 4 ? '#fff3e4' : t === 3 ? 'rgba(0,192,162,0.10)' : '#ebebf6',
color: t === 1 ? '#556EE6' : t === 4 ? '#F58342' : t === 3 ? '#00C0A2' : '#495057',
padding: '3px 6px',
}}
>
{runningStatusEnum[t]}
</Tag>
);
},
},
{
title: '运行进度',
dataIndex: 'progress',
key: 'progress',
width: 90,
render: (_t: any, r: any) => {
const { success, total } = r;
return (success || 0) + '/' + (total || 0);
},
},
{
title: '运行结果',
dataIndex: 'result',
key: 'result',
// eslint-disable-next-line react/display-name
render: (_t: any, r: any) => {
const { success, fail, waiting, doing } = r;
return (
<div className="run-result">
<span>
:
<span>
{success === 0 || success ? (
(success + '').length < 3 ? (
success
) : (
<Tooltip title={success}>{(success + '').slice(0, 2) + '...'}</Tooltip>
)
) : (
'-'
)}
</span>
</span>
<span>
:
{fail === 0 || fail ? (fail + '').length < 3 ? fail : <Tooltip title={fail}>{(fail + '').slice(0, 2) + '...'}</Tooltip> : '-'}
</span>
<span>
:
{doing === 0 || doing ? (
(doing + '').length < 3 ? (
doing
) : (
<Tooltip title={doing}>{(doing + '').slice(0, 2) + '...'}</Tooltip>
)
) : (
'-'
)}
</span>
<span>
:
{waiting === 0 || waiting ? (
(waiting + '').length < 3 ? (
waiting
) : (
<Tooltip title={waiting}>{(waiting + '').slice(0, 2) + '...'}</Tooltip>
)
) : (
'-'
)}
</span>
</div>
);
},
},
{
title: '描述',
dataIndex: 'jobDesc',
key: 'jobDesc',
width: 150,
needTooltip: true,
},
{
title: '提交人',
dataIndex: 'creator',
key: 'creator',
},
{
title: '计划执行时间',
dataIndex: 'planTime',
width: 160,
key: 'planTime',
render: (t: any, r: any) => {
return t ? moment(t).format(timeFormat) : '-';
},
},
{
title: '实际执行时间',
dataIndex: 'startTime',
key: 'startTime',
width: 160,
render: (t: any, r: any) => {
if (moment(t).format('x') < moment(r.planTime).format('x')) {
return '已逾期';
}
// 判断是否为 mysql 默认的1971-01-01 00:00:00
if (+moment(t).format('x') === 31507200000) {
return '-';
}
return t ? moment(t).format(timeFormat) : '-';
},
},
{
title: '操作',
dataIndex: 'options',
key: 'options',
width: 150,
filterTitle: true,
fixed: 'right',
// eslint-disable-next-line react/display-name
render: (_t: any, r: any) => {
return (
<div>
{r.jobStatus !== 2 && r.jobStatus !== 5 ? <a onClick={() => arg.setViewProgress(r)}></a> : null}
{/* 编辑任务 */}
{r.jobStatus === 2 ? (
<a style={{ marginRight: '16px' }} onClick={() => arg.setViewProgress(r, r.jobType)}>
</a>
) : null}
{r.jobStatus === 2 ? <a onClick={() => arg.onDelete(r)}></a> : null}
</div>
);
},
},
];
return columns;
};
// * 获取任务明细-扩缩副本列表配置
export const getTaskDetailsColumns = (arg?: any) => {
const columns = [
{
title: 'Topic',
dataIndex: 'topicName',
key: 'topicName',
fixed: 'left',
width: 102,
render: (t: any, r: any) => {
return (
<span>
{t}
<Badge
style={{ marginLeft: '6px' }}
status={r?.status === 1 ? 'warning' : r?.status === 4 ? 'error' : r?.status === 3 ? 'success' : 'warning'}
/>
</span>
);
},
},
{
title: '当前副本数',
dataIndex: 'oldReplicaNu',
key: 'oldReplicaNu',
},
{
title: '源BrokerID',
dataIndex: 'sourceBrokers',
key: 'sourceBrokers',
render: (t: any, r: any) => {
return t && t.length > 0 ? t.join('、') : '-';
},
// render(t: any, r: any) {
// return (
// <div style={{ width: '100px' }}>
// <TagsWithHide list={t} expandTagContent={(num: any) => `共有${num}个`} />
// </div>
// );
// },
},
{
title: '新副本数',
dataIndex: 'newReplicaNu',
key: 'newReplicaNu',
},
{
title: '目标BrokerID',
dataIndex: 'desBrokers',
key: 'desBrokers',
render: (t: any, r: any) => {
return t && t.length > 0 ? t.join('、') : '-';
},
// render(t: any, r: any) {
// return (
// <div style={{ width: '100px' }}>
// <TagsWithHide list={t} expandTagContent={(num: any) => `共有${num}个`} />
// </div>
// );
// },
},
{
title: 'MessageSize迁移进度(MB)',
dataIndex: 'movedSize',
key: 'movedSize',
render(t: any, r: any) {
const movedSize = r.movedSize ? Number(Utils.formatAssignSize(t, 'MB')) : 0;
const totalSize = r.totalSize ? Number(Utils.formatAssignSize(t, 'MB')) : 0;
return (
<div className="message-size">
<Tooltip title={(movedSize > 0 && totalSize > 0 ? (movedSize / totalSize) * 100 : 0) + '%'}>
<Progress
percent={movedSize > 0 && totalSize > 0 ? (movedSize / totalSize) * 100 : 0}
strokeColor="#556EE6"
showInfo={false}
/>
</Tooltip>
<span>{movedSize + '/' + totalSize}</span>
</div>
);
},
},
{
title: '分区进度',
dataIndex: 'partitionRate',
key: 'partitionRate',
render: (t: any, r: any) => {
return (r.success || r.success === 0 ? r.success : '-') + '/' + (r.total || r.total === 0 ? r.total : '-');
},
},
{
title: '预计剩余时长',
dataIndex: 'remainTime',
key: 'remainTime',
render(t: any, r: any) {
return t ? Utils.transUnitTime(t) : t === 0 ? t : '-';
},
},
// {
// title: '当前副本数',
// dataIndex: 'progress',
// key: 'progress',
// render: (_t: any, r: any) => {
// return r.success + '/' + (r.success + r.doing + r.fail);
// },
// },
];
return columns;
};
// * 获取任务明细-Topic迁移列表配置
export const getMoveBalanceColumns = (arg?: any) => {
const columns = [
{
title: 'Topic',
dataIndex: 'topicName',
key: 'topicName',
fixed: 'left',
width: 102,
render: (t: any, r: any) => {
return (
<span>
{t}
<Badge
style={{ marginLeft: '6px' }}
status={r?.status === 1 ? 'warning' : r?.status === 4 ? 'error' : r?.status === 3 ? 'success' : 'warning'}
/>
</span>
);
},
},
{
title: '分区',
dataIndex: 'partitions',
key: 'partitions',
render: (t: any, r: any) => {
return t && t.length > 0 ? t.join('、') : '-';
},
},
{
title: '源BrokerID',
dataIndex: 'sourceBrokers',
key: 'sourceBrokers',
render: (t: any, r: any) => {
return t && t.length > 0 ? t.join('、') : '-';
},
// render(t: any, r: any) {
// return (
// <div style={{ width: '100px' }}>
// <TagsWithHide list={t} expandTagContent={(num: any) => `共有${num}个`} />
// </div>
// );
// },
},
{
title: '目标BrokerID',
dataIndex: 'desBrokers',
key: 'desBrokers',
render: (t: any, r: any) => {
return t && t.length > 0 ? t.join('、') : '-';
},
// render(t: any, r: any) {
// return (
// <div style={{ width: '100px' }}>
// <TagsWithHide list={t} expandTagContent={(num: any) => `共有${num}个`} />
// </div>
// );
// },
},
{
title: '当前数据保存时间 h',
dataIndex: 'currentTimeSpent',
key: 'currentTimeSpent',
render: (t: any, r: any) => {
return t || t === 0 ? Utils.transMSecondToHour(+t) : '-';
},
},
{
title: '迁移数据时间范围 h',
dataIndex: 'moveTimeSpent',
key: 'moveTimeSpent',
render: (t: any, r: any) => {
return t || t === 0 ? Utils.transMSecondToHour(+t) : '-';
},
},
{
title: 'MessageSize迁移进度(MB)',
dataIndex: 'movedSize',
key: 'movedSize',
render(t: any, r: any) {
const movedSize = r.movedSize ? Number(Utils.formatAssignSize(t, 'MB')) : 0;
const totalSize = r.totalSize ? Number(Utils.formatAssignSize(t, 'MB')) : 0;
return (
<div className="message-size">
<Tooltip title={(movedSize > 0 && totalSize > 0 ? (movedSize / totalSize) * 100 : 0) + '%'}>
<Progress
percent={movedSize > 0 && totalSize > 0 ? (movedSize / totalSize) * 100 : 0}
strokeColor="#556EE6"
showInfo={false}
/>
</Tooltip>
<span>{movedSize + '/' + totalSize}</span>
</div>
);
},
},
// {
// title: '需迁移MessageSizeMB',
// dataIndex: 'totalSize',
// key: 'totalSize',
// width: 100,
// render: (t: any, r: any) => {
// return t || t === 0 ? formatAssignSize(t, 'MB') : '-';
// },
// },
{
title: '分区进度',
dataIndex: 'partitionProgress',
key: 'partitionProgress',
render: (t: any, r: any) => {
return (r.success || r.success === 0 ? r.success : '-') + '/' + (r.total || r.total === 0 ? r.total : '-');
},
},
// {
// title: '已完成MessageSize MB',
// dataIndex: 'movedSize',
// key: 'movedSize',
// width: 100,
// render: (t: any, r: any) => {
// return t || t === 0 ? formatAssignSize(t, 'MB') : '-';
// },
// },
// {
// title: '状态',
// dataIndex: 'status',
// key: 'status',
// width: 100,
// render: (t: any) => {
// return (
// <span>
// <Badge status={t === 1 ? 'warning' : t === 4 ? 'error' : t === 3 ? 'success' : 'warning'} />
// {runningStatusEnum[t]}
// </span>
// );
// },
// },
{
title: '预计剩余时长',
dataIndex: 'remainTime',
key: 'remainTime',
render(t: any, r: any) {
return t ? Utils.transUnitTime(t) : t === 0 ? t : '-';
},
},
// {
// title: '当前副本数',
// dataIndex: 'progress',
// key: 'progress',
// render: (_t: any, r: any) => {
// return r.success + '/' + (r.success + r.doing + r.fail);
// },
// },
];
return columns;
};
// * 获取任务明细-集群均衡列表配置
export const getClusterBalanceColumns = (arg?: any) => {
const columns = [
{
title: 'Topic',
dataIndex: 'topicName',
key: 'topicName',
width: 102,
needTooltip: true,
},
{
title: '分区',
dataIndex: 'partition',
key: 'partition',
},
{
title: '源BrokerID',
dataIndex: 'sourceBrokerIds',
key: 'sourceBrokerIds',
},
{
title: '目标BrokerID',
dataIndex: 'desBrokerIds',
key: 'desBrokerIds',
},
{
title: '当前数据保存时间 h',
dataIndex: 'currentTimeSpent',
key: 'currentTimeSpent',
},
{
title: '迁移数据时间范围 h',
dataIndex: 'currentTimeSpent',
key: 'currentTimeSpent',
},
{
title: '需迁移MessageSizeMB',
dataIndex: 'totalSize',
key: 'totalSize',
width: 100,
},
{
title: '分区进度',
dataIndex: 'partitionProgress',
key: 'partitionProgress',
},
{
title: '已完成MessageSize MB',
dataIndex: 'movedSize',
key: 'movedSize',
width: 100,
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render(t: any, r: any) {
return runningStatusEnum[t];
},
width: 100,
},
{
title: '预计剩余时长',
dataIndex: 'remainTime',
key: 'remainTime',
},
// {
// title: '当前副本数',
// dataIndex: 'progress',
// key: 'progress',
// render: (_t: any, r: any) => {
// return r.success + '/' + (r.success + r.doing + r.fail);
// },
// },
];
return columns;
};
export const getNodeTrafficColumns = (arg?: any) => {
const columns = [
{
title: 'Broker',
dataIndex: 'brokerId',
key: 'brokerId',
},
{
title: 'Host',
dataIndex: 'brokerHost',
key: 'brokerHost',
},
{
title: 'Job Bytes InMB/s',
dataIndex: 'byteInJob',
key: 'byteInJob',
render: (t: any, r: any) => {
return (
<div>
<span>{t || t === 0 ? Utils.transBToMB(t) : '-'}</span>
{<PopoverBroker title="Job Bytes In" data={r?.inBrokers} />}
</div>
);
},
},
{
title: 'Job Bytes OutMB/s',
dataIndex: 'byteOutJob',
key: 'byteOutJob',
render: (t: any, r: any) => {
return (
<div>
<span>{t || t === 0 ? Utils.transBToMB(t) : '-'}</span>
{r?.outBrokers && <PopoverBroker title="Job Bytes Out" data={r?.outBrokers} />}
</div>
);
},
},
{
title: 'Total BytesInMB/s',
dataIndex: 'byteInTotal',
key: 'byteInTotal',
render: (t: any, r: any) => {
return t || t === 0 ? Utils.transBToMB(t) : '-';
},
},
{
title: 'Total Bytes OutMB/s',
dataIndex: 'byteOutTotal',
key: 'byteOutTotal',
render: (t: any, r: any) => {
return t || t === 0 ? Utils.transBToMB(t) : '-';
},
},
];
return columns;
};
export const defaultPagination = {
current: 1,
pageSize: 10,
position: 'bottomRight',
showSizeChanger: true,
pageSizeOptions: ['10', '20', '50', '100', '200', '500'],
};
const KB = 1024;
const MB = KB * KB;
const GB = MB * KB;
const TB = GB * KB;
export const formatAssignSize = (size: number, convertTarget: string, fix = 2) => {
if (size === undefined || size === null) return '';
if (convertTarget === undefined || convertTarget === null) return size;
if (convertTarget === 'KB') return `${(size / KB).toFixed(fix)}`;
if (convertTarget === 'MB') return `${(size / MB).toFixed(fix)}`;
if (convertTarget === 'GB') return `${(size / GB).toFixed(fix)}`;
return `${(size / TB).toFixed(fix)}TB`;
};

View File

@@ -0,0 +1,156 @@
// .table {
// background: #F8F9FA;
// // margin-top: 12px;
// border-radius: 8px;
// .dcloud-table {
// height: 210px;
// overflow: auto;
// background-color: transparent;
// .dcloud-table-content .dcloud-table-cell {
// background-color: transparent;
// }
// }
// .dcloud-pagination {
// height: 32px;
// margin-bottom: 0;
// margin-top: 8px;
// }
// }
.edit-flow-limit {
display: flex;
align-items: center;
input {
height: 20px;
}
}
.custom-expanded-table {
border-radius: 20px;
.dcloud-table-container {
overflow: hidden;
border-radius: 4px;
}
.dcloud-table {
td,
th {
background-color: #f8f9fa !important;
}
}
}
.custom-popover-borker {
width: 281px;
padding-top: 0 !important;
.dcloud-popover-arrow {
display: none;
}
.dcloud-popover-inner {
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 12px;
}
.dcloud-popover-title {
padding: 12px 24px;
font-family: @font-family-bold;
font-size: 16px;
border-bottom: 0;
}
// .dcloud-table{
// margin: 0 !important;
// }
}
.job-detail {
.dcloud-table-cell {
padding: 7px 16px 8px 2px !important;
}
.dcloud-table-row-expand-icon-cell {
padding: 7px 7px 5px 24px !important;
}
.dcloud-table-expanded-row-fixed {
padding: 16px 20px !important;
.dcloud-table-cell {
font-size: 12px !important;
}
}
}
.run-result {
& > span {
display: inline-block;
}
& > span:not(:last-child) {
margin-right: 18px;
}
}
.expanded-table {
.dcloud-table-container::after {
box-shadow: none !important;
}
.dcloud-pagination{
margin-bottom: 0 !important;
}
}
.balance-list-title {
font-size: 12px;
color: #74788d;
display: inline-flex;
align-items: center;
.titleImg {
display: inline-block;
width: 16px;
height: 17px;
margin-left: 2px;
background-image: url('../../assets/arrowSmall.svg');
background-position: center, center;
background-repeat: no-repeat, repeat;
}
}
.balance-list-contert {
display: inline-flex;
align-items: center;
&-text,&-text-right{
display: inline-block;
width: 50px;
}
&-text-right{
text-align: right;
color: #556EE6;
}
&-img {
display: inline-block;
width: 26px;
height: 17px;
margin: 0 3px 0 5px;
background-image: url('../../assets/arrowLarge.svg');
background-position: center, center;
background-repeat: no-repeat, repeat;
}
}
.message-size {
display: flex;
align-items: center;
.dcloud-progress {
width: 140px;
&-inner {
background-color: #ececf1;
border-radius: 5px;
}
}
& > span {
margin-left: 4px;
color: #adb5bc;
transform: scale(0.83, 0.83);
text-align: left;
}
}

View File

@@ -0,0 +1,285 @@
import React, { useState, useEffect, memo } from 'react';
import { useParams, useHistory, useLocation } from 'react-router-dom';
import { ProTable, Drawer, Utils, AppContainer, Form, Select, Input, Button, message, Modal } from 'knowdesign';
import API from '../../api';
import { getJobsListColumns, defaultPagination, runningStatus, jobType } from './config';
import JobsCheck from '@src/components/CardBar/JobsCheck';
import DBreadcrumb from 'knowdesign/lib/extend/d-breadcrumb';
import { ViewJobsProgress } from './ViewJobsProgress';
import './index.less';
import ReplicaChange from '@src/components/TopicJob/ReplicaChange';
import ReplicaMove from '@src/components/TopicJob/ReplicaMove';
import BalanceDrawer from '../LoadRebalance/BalanceDrawer';
const { request } = Utils;
const JobsList: React.FC = (props: any) => {
const [global] = AppContainer.useGlobalValue();
const [form] = Form.useForm();
const urlParams = useParams<any>(); // 获取地址栏参数
const history = useHistory();
const [loading, setLoading] = useState(false);
const [data, setData] = useState([]);
const [searchWords, setSearchWords] = useState<{ type: any; jobTarget: any; status: any }>();
// const [filteredInfo, setFilteredInfo] = useState(null);
const [pagination, setPagination] = useState<any>(defaultPagination);
const [viewVisble, setViewVisble] = useState<any>(false);
const [record, setRecord] = useState(null); // 获取当前点击行的数据;
const [changeVisible, setChangeVisible] = useState(false);
const [moveVisible, setMoveVisible] = useState(false);
const [balanceVisible, setBalanceVisible] = useState(false);
const [balanceFromData, setBalanceFormData] = useState(false);
// 默认排序
const defaultSorter = {
sortField: 'brokerId',
sortType: 'asc',
};
// 请求接口获取数据
const genData = async ({ pageNo, pageSize }: any) => {
if (urlParams?.clusterId === undefined) return;
// filters = filters || filteredInfo;
setLoading(true);
const params = {
...searchWords,
pageNo,
pageSize,
type: searchWords?.type === 0 || searchWords?.type ? searchWords?.type : -1,
};
request(API.getJobsList(urlParams?.clusterId), { method: 'POST', data: params })
.then((res: any) => {
setPagination({
current: res.pagination?.pageNo,
pageSize: res.pagination?.pageSize,
total: res.pagination?.total,
});
setData(res?.bizData || []);
setLoading(false);
})
.catch((err) => {
setData([]);
setLoading(false);
});
};
// 获取立即均衡formData
const getBalanceFormData = (jobId: any, clusterId: any) => {
const params = {
clusterId,
jobId,
};
return request(API.getJobsTaskData(params.clusterId, params.jobId), params);
};
// 查询
const onFinish = (formData: any) => {
setSearchWords(formData);
};
const onTableChange = (pagination: any, filters: any, sorter: any) => {
// setFilteredInfo(filters);
genData({ pageNo: pagination.current, pageSize: pagination.pageSize, filters, sorter });
};
const getSearchKeywords = (value: string) => {
// setSearchKeywords(value);
};
// 删除modal
const onDelete = (record: any) => {
Modal.confirm({
title: `确认删除"任务【${record.id}】"吗?`,
okText: '删除',
okType: 'primary',
centered: true,
cancelText: '取消',
okButtonProps: {
// disabled: record.status === 1,
size: 'small',
danger: true,
},
cancelButtonProps: {
size: 'small',
},
maskClosable: false,
onOk(close) {
// 缺少判断当前任务是否是doing的状态
return Utils.delete(API.getJobsDelete(urlParams?.clusterId, record?.id)).then((_) => {
message.success('删除任务成功');
// getConfigList();
genData({
pageNo: 1,
pageSize: pagination.pageSize,
});
close();
});
},
});
};
const setViewProgress = (record: any, visibleType?: number) => {
setRecord(record);
if (visibleType === 0) {
setMoveVisible(true);
} else if (visibleType === 1) {
setChangeVisible(true);
} else if (visibleType === 2) {
getBalanceFormData(record.id, urlParams.clusterId).then((res: any) => {
const jobData = (res && JSON.parse(res?.jobData)) || {};
setBalanceFormData({
...jobData,
jobId: record.id,
record,
jobData,
});
setBalanceVisible(true);
});
} else {
setViewVisble(true);
}
};
const onclose = () => {
setChangeVisible(false);
setMoveVisible(false);
setBalanceVisible(false);
setRecord(null);
};
useEffect(() => {
genData({
pageNo: 1,
pageSize: pagination.pageSize,
// sorter: defaultSorter
});
}, [searchWords]);
return (
<div key="brokerList">
<div className="breadcrumb">
<DBreadcrumb
breadcrumbs={[
{ label: '多集群管理', aHref: '/' },
{ label: global?.clusterInfo?.name, aHref: `/cluster/${global?.clusterInfo?.id}` },
{ label: 'Job', aHref: `` },
]}
/>
</div>
<div style={{ margin: '12px 0' }}>
<JobsCheck />
</div>
{/* <Form form={form} layout="inline" onFinish={onFinish}> */}
<div className="clustom-table-content">
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '12px' }}>
<Form form={form} layout="inline" onFinish={onFinish}>
<Form.Item name="type">
<Select
allowClear
options={jobType}
style={{ width: '190px' }}
className={'detail-table-select'}
placeholder="选择任务类型"
/>
</Form.Item>
<Form.Item name="jobTarget">
<Input allowClear style={{ width: '190px' }} placeholder="请输入执行任务对象" />
</Form.Item>
<Form.Item name="status">
<Select
mode="multiple"
maxTagCount={1}
options={runningStatus}
style={{ width: '190px' }}
className={'detail-table-select'}
placeholder="选择运行状态"
showArrow
allowClear
/>
</Form.Item>
</Form>
<div>
<Form style={{ justifyContent: 'flex-end' }} form={form} layout="inline" onFinish={onFinish}>
<Form.Item style={{ marginRight: 0 }}>
<Button type="primary" ghost htmlType="submit">
</Button>
</Form.Item>
</Form>
</div>
</div>
{/* </Form> */}
<ProTable
key="brokerTable"
showQueryForm={false}
tableProps={{
tableId: 'jobs_list',
showHeader: false,
rowKey: 'jobs_list',
loading: loading,
columns: getJobsListColumns({ onDelete, setViewProgress }),
dataSource: data,
paginationProps: { ...pagination },
attrs: {
// className: 'frameless-table', // 纯无边框表格类名
onChange: onTableChange,
scroll: { x: 'max-content', y: 'calc(100vh - 400px)' }, // calc(100vh - 270px)
bordered: false,
},
}}
/>
</div>
{viewVisble && <ViewJobsProgress visible={viewVisble} setVisble={setViewVisble} record={record} />}
{/* 批量扩缩副本 */}
{changeVisible && (
<ReplicaChange
drawerVisible={changeVisible}
jobId={record?.id}
jobStatus={record?.jobStatus}
topics={record?.target?.split(',') || []}
onClose={onclose}
genData={() =>
genData({
pageNo: 1,
pageSize: pagination.pageSize,
// sorter: defaultSorter
})
}
></ReplicaChange>
)}
{/* 批量迁移 */}
{moveVisible && (
<ReplicaMove
drawerVisible={moveVisible}
jobId={record?.id}
jobStatus={record?.jobStatus}
topics={record?.target?.split(',') || []}
onClose={onclose}
genData={() =>
genData({
pageNo: 1,
pageSize: pagination.pageSize,
// sorter: defaultSorter
})
}
></ReplicaMove>
)}
{/* 立即均衡 */}
{balanceVisible && (
<BalanceDrawer
visible={balanceVisible}
formData={balanceFromData}
onClose={onclose}
genData={() =>
genData({
pageNo: 1,
pageSize: pagination.pageSize,
})
}
/>
)}
</div>
);
};
export default JobsList;