mirror of
https://github.com/didi/KnowStreaming.git
synced 2026-01-12 19:12:48 +08:00
feat: 新增Topic 复制功能
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { AppContainer, ProTable, Utils, Tag, Modal, Tooltip } from 'knowdesign';
|
||||
import Api from '@src/api';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { getDataUnit } from '@src/constants/chartConfig';
|
||||
import message from '@src/components/Message';
|
||||
import { ClustersPermissionMap } from '../CommonConfig';
|
||||
import { ControlStatusMap } from '../CommonRoute';
|
||||
const { request } = Utils;
|
||||
|
||||
const getColmns = (arg: any) => {
|
||||
const formattedBytes = (v: number) => {
|
||||
const [unit, size] = getDataUnit['Memory'](v);
|
||||
return `${(v / size).toFixed(2)}${unit}/s`;
|
||||
};
|
||||
const tagEle = (
|
||||
<Tag
|
||||
style={{
|
||||
color: '#5664FF',
|
||||
padding: '2px 5px',
|
||||
background: '#eff1fd',
|
||||
marginLeft: '-4px',
|
||||
transform: 'scale(0.83,0.83)',
|
||||
}}
|
||||
>
|
||||
当前集群
|
||||
</Tag>
|
||||
);
|
||||
const baseColumns: any = [
|
||||
{
|
||||
title: '源集群',
|
||||
dataIndex: 'sourceClusterName',
|
||||
key: 'sourceClusterName',
|
||||
render: (t: string, record: any) => (
|
||||
<>
|
||||
<span>{t || '-'}</span>
|
||||
{record.sourceClusterId == arg.clusterId && tagEle}
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '目标集群',
|
||||
dataIndex: 'destClusterName',
|
||||
key: 'destClusterName',
|
||||
render: (t: string, record: any) => (
|
||||
<>
|
||||
<span>{t || '-'}</span>
|
||||
{record.destClusterId == arg.clusterId && tagEle}
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '消息写入速率',
|
||||
dataIndex: 'bytesIn',
|
||||
key: 'bytesIn',
|
||||
width: 150,
|
||||
render: (t: number) => (t !== null && t !== undefined ? formattedBytes(t) : '-'),
|
||||
},
|
||||
{
|
||||
title: '消息复制速率',
|
||||
dataIndex: 'replicationBytesIn',
|
||||
key: 'replicationBytesIn',
|
||||
width: 150,
|
||||
render: (t: number) => (t !== null && t !== undefined ? formattedBytes(t) : '-'),
|
||||
},
|
||||
{
|
||||
title: '延迟(个消息)',
|
||||
dataIndex: 'lag',
|
||||
key: 'lag',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'option',
|
||||
key: 'option',
|
||||
width: 100,
|
||||
render: (_t: any, r: any) => {
|
||||
return arg.global.hasPermission(ClustersPermissionMap.TOPIC_CANCEL_REPLICATOR) ? (
|
||||
<a onClick={() => arg.cancelSync(r)}>取消同步</a>
|
||||
) : (
|
||||
'-'
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return baseColumns;
|
||||
};
|
||||
|
||||
const Replicator = (props: any) => {
|
||||
const { hashData } = props;
|
||||
const urlParams = useParams<any>(); // 获取地址栏参数
|
||||
const [global] = AppContainer.useGlobalValue();
|
||||
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 genData = () => {
|
||||
if (urlParams?.clusterId === undefined || hashData?.topicName === undefined) return;
|
||||
setLoading(true);
|
||||
request(Api.getTopicMirrorList(urlParams?.clusterId, hashData?.topicName))
|
||||
.then((res: any = []) => {
|
||||
setData(res);
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
};
|
||||
|
||||
const cancelSync = (item: any) => {
|
||||
Modal.confirm({
|
||||
title: `确认取消此Topic同步吗?`,
|
||||
okType: 'primary',
|
||||
centered: true,
|
||||
okButtonProps: {
|
||||
size: 'small',
|
||||
danger: true,
|
||||
},
|
||||
cancelButtonProps: {
|
||||
size: 'small',
|
||||
},
|
||||
maskClosable: false,
|
||||
onOk(close) {
|
||||
close();
|
||||
const data = [
|
||||
{
|
||||
destClusterPhyId: item.destClusterId,
|
||||
sourceClusterPhyId: item.sourceClusterId,
|
||||
topicName: item.topicName,
|
||||
},
|
||||
];
|
||||
Utils.delete(Api.handleTopicMirror(), { data }).then(() => {
|
||||
message.success('成功取消Topic同步');
|
||||
genData();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onTableChange = (pagination: any, filters: any, sorter: any, extra: any) => {
|
||||
setPagination(pagination);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
props.positionType === 'Replicator' && genData();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="pro-table-wrap">
|
||||
<ProTable
|
||||
showQueryForm={false}
|
||||
tableProps={{
|
||||
loading,
|
||||
showHeader: false,
|
||||
rowKey: 'unique',
|
||||
columns: getColmns({ global, cancelSync, clusterId: urlParams?.clusterId }),
|
||||
dataSource: data,
|
||||
paginationProps: pagination,
|
||||
attrs: {
|
||||
onChange: onTableChange,
|
||||
scroll: { y: 'calc(100vh - 400px)' },
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Replicator;
|
||||
@@ -10,6 +10,7 @@ import ConsumerGroups from './ConsumerGroups';
|
||||
import ACLs from './ACLs';
|
||||
import Configuration from './Configuration';
|
||||
import Consumers from './ConsumerGroups';
|
||||
import Replicator from './Replicator';
|
||||
// import Consumers from '@src/pages/Consumers';
|
||||
import './index.less';
|
||||
import TopicDetailHealthCheck from '@src/components/CardBar/TopicDetailHealthCheck';
|
||||
@@ -206,6 +207,9 @@ const TopicDetail = (props: any) => {
|
||||
<Configuration searchKeywords={searchKeywords} positionType={positionType} hashData={hashData} />
|
||||
)}
|
||||
</TabPane>
|
||||
<TabPane tab="Replicator" key="Replicator">
|
||||
{positionType === 'Replicator' && <Replicator searchKeywords={searchKeywords} positionType={positionType} hashData={hashData} />}
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</Drawer>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable react/display-name */
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { AppContainer, Input, ProTable, Select, Switch, Tooltip, Utils, Dropdown, Menu, Button, Divider } from 'knowdesign';
|
||||
import { AppContainer, Input, ProTable, Select, Switch, Tooltip, Utils, Dropdown, Menu, Button, Divider, Tag } from 'knowdesign';
|
||||
import { IconFont } from '@knowdesign/icons';
|
||||
import Create from './Create';
|
||||
import './index.less';
|
||||
@@ -15,10 +15,12 @@ import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb';
|
||||
import ReplicaChange from '@src/components/TopicJob/ReplicaChange';
|
||||
import SmallChart from '@src/components/SmallChart';
|
||||
import ReplicaMove from '@src/components/TopicJob/ReplicaMove';
|
||||
import TopicMirror from '@src/components/TopicJob/TopicMirror';
|
||||
import { formatAssignSize } from '../Jobs/config';
|
||||
import { DownOutlined } from '@ant-design/icons';
|
||||
import { tableHeaderPrefix } from '@src/constants/common';
|
||||
import { HealthStateMap } from './config';
|
||||
import { ControlStatusMap } from '../CommonRoute';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
@@ -39,6 +41,7 @@ const AutoPage = (props: any) => {
|
||||
const [type, setType] = useState<string>('');
|
||||
const [changeVisible, setChangeVisible] = useState(false);
|
||||
const [moveVisible, setMoveVisible] = useState(false);
|
||||
const [mirrorVisible, setMirrorVisible] = useState(false);
|
||||
const [selectValue, setSelectValue] = useState('批量操作');
|
||||
|
||||
const [sortObj, setSortObj] = useState<{
|
||||
@@ -131,17 +134,34 @@ const AutoPage = (props: any) => {
|
||||
className: 'clean-padding-left',
|
||||
lineClampOne: true,
|
||||
// eslint-disable-next-line react/display-name
|
||||
render: (t: string, r: any) => {
|
||||
render: (t: string, record: any) => {
|
||||
return (
|
||||
<Tooltip title={t}>
|
||||
<a
|
||||
onClick={() => {
|
||||
window.location.hash = `topicName=${t}`;
|
||||
}}
|
||||
>
|
||||
{t}
|
||||
</a>
|
||||
</Tooltip>
|
||||
<>
|
||||
<Tooltip title={t}>
|
||||
<a
|
||||
onClick={() => {
|
||||
window.location.hash = `topicName=${t}`;
|
||||
}}
|
||||
>
|
||||
{t}
|
||||
</a>
|
||||
</Tooltip>
|
||||
{record.inMirror && (
|
||||
<div>
|
||||
<Tag
|
||||
style={{
|
||||
color: '#5664FF',
|
||||
padding: '2px 5px',
|
||||
background: '#eff1fd',
|
||||
marginLeft: '-4px',
|
||||
transform: 'scale(0.83,0.83)',
|
||||
}}
|
||||
>
|
||||
复制中...
|
||||
</Tag>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -256,6 +276,7 @@ const AutoPage = (props: any) => {
|
||||
const onclose = () => {
|
||||
setChangeVisible(false);
|
||||
setMoveVisible(false);
|
||||
setMirrorVisible(false);
|
||||
setSelectValue('批量操作');
|
||||
};
|
||||
|
||||
@@ -271,6 +292,11 @@ const AutoPage = (props: any) => {
|
||||
<a onClick={() => setMoveVisible(true)}>迁移副本</a>
|
||||
</Menu.Item>
|
||||
)}
|
||||
{global.hasPermission(ClustersPermissionMap.TOPIC_REPLICATOR) && (
|
||||
<Menu.Item>
|
||||
<a onClick={() => setMirrorVisible(true)}>Topic复制</a>
|
||||
</Menu.Item>
|
||||
)}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
@@ -296,6 +322,8 @@ const AutoPage = (props: any) => {
|
||||
<ReplicaChange drawerVisible={changeVisible} jobId={''} topics={selectedRowKeys} onClose={onclose}></ReplicaChange>
|
||||
{/* 批量迁移 */}
|
||||
<ReplicaMove drawerVisible={moveVisible} jobId={''} topics={selectedRowKeys} onClose={onclose}></ReplicaMove>
|
||||
{/* Topic复制 */}
|
||||
<TopicMirror drawerVisible={mirrorVisible} genData={getTopicsList} onClose={onclose}></TopicMirror>
|
||||
|
||||
<div className={`${tableHeaderPrefix}-left-refresh`} onClick={() => getTopicsList()}>
|
||||
<IconFont className={`${tableHeaderPrefix}-left-refresh-icon`} type="icon-shuaxin1" />
|
||||
@@ -334,7 +362,8 @@ const AutoPage = (props: any) => {
|
||||
}}
|
||||
/>
|
||||
{(global.hasPermission(ClustersPermissionMap.TOPIC_CHANGE_REPLICA) ||
|
||||
global.hasPermission(ClustersPermissionMap.TOPIC_MOVE_REPLICA)) && (
|
||||
global.hasPermission(ClustersPermissionMap.TOPIC_MOVE_REPLICA) ||
|
||||
global.hasPermission(ClustersPermissionMap.TOPIC_REPLICATOR)) && (
|
||||
<Dropdown overlay={menu} trigger={['click']}>
|
||||
<Button className="batch-btn" icon={<DownOutlined />} type="primary" ghost>
|
||||
批量变更
|
||||
|
||||
Reference in New Issue
Block a user