mirror of
https://github.com/didi/KnowStreaming.git
synced 2026-01-06 13:51:08 +08:00
初始化3.0.0版本
This commit is contained in:
@@ -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: '需迁移MessageSize(MB)',
|
||||
// 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: 'BytesIn(MB/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>
|
||||
);
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
669
km-console/packages/layout-clusters-fe/src/pages/Jobs/config.tsx
Normal file
669
km-console/packages/layout-clusters-fe/src/pages/Jobs/config.tsx
Normal 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: '需迁移MessageSize(MB)',
|
||||
// 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: '需迁移MessageSize(MB)',
|
||||
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 In(MB/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 Out(MB/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 BytesIn(MB/s)',
|
||||
dataIndex: 'byteInTotal',
|
||||
key: 'byteInTotal',
|
||||
render: (t: any, r: any) => {
|
||||
return t || t === 0 ? Utils.transBToMB(t) : '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Total Bytes Out(MB/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`;
|
||||
};
|
||||
156
km-console/packages/layout-clusters-fe/src/pages/Jobs/index.less
Normal file
156
km-console/packages/layout-clusters-fe/src/pages/Jobs/index.less
Normal 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;
|
||||
}
|
||||
}
|
||||
285
km-console/packages/layout-clusters-fe/src/pages/Jobs/index.tsx
Normal file
285
km-console/packages/layout-clusters-fe/src/pages/Jobs/index.tsx
Normal 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;
|
||||
Reference in New Issue
Block a user