This commit is contained in:
zengqiao
2020-12-19 00:40:52 +08:00
parent 49280a8617
commit 447a575f4f
83 changed files with 1578 additions and 559 deletions

View File

@@ -169,6 +169,8 @@ export class ClusterBroker extends SearchAndFilterContainer {
title="确定删除?"
onConfirm={() => this.deteleTopic(record)}
disabled={record.status === 0}
cancelText="取消"
okText="确认"
>
<a style={record.status === 0 ? { cursor: 'not-allowed', color: '#999' } : {}}>

View File

@@ -72,7 +72,7 @@ export class ClusterConsumer extends SearchAndFilterContainer {
data = searchKey ? origin.filter((item: IOffset) =>
(item.consumerGroup !== undefined && item.consumerGroup !== null) && item.consumerGroup.toLowerCase().includes(searchKey as string)
|| (item.location !== undefined && item.location !== null) && item.location.toLowerCase().includes(searchKey as string),
) : origin ;
) : origin;
return data;
}
@@ -82,7 +82,7 @@ export class ClusterConsumer extends SearchAndFilterContainer {
public render() {
let details: any[];
details = this.consumerDetails ? this.consumerDetails.map((ele, index) => {
details = this.consumerDetails ? this.consumerDetails.map((ele, index) => {
return {
key: index,
topicName: ele,
@@ -90,41 +90,43 @@ export class ClusterConsumer extends SearchAndFilterContainer {
}) : [];
const consumptionColumns = [{
title: 'Topic名称',
title: '消费的Topic列表',
dataIndex: 'topicName',
key: 'topicName',
}];
return (
<>
<div className="k-row">
<ul className="k-tab">
<li>{this.props.tab}</li>
{this.renderSearch()}
</ul>
<Table
<div className="k-row">
<ul className="k-tab">
<li>{this.props.tab}</li>
{this.renderSearch()}
</ul>
<Table
columns={this.columns}
dataSource={this.getData(admin.consumerData)}
pagination={pagination}
rowKey="key"
/>
</div>
<Modal
title="消费的Topic"
visible={this.state.detailsVisible}
onOk={() => this.handleDetailsOk()}
onCancel={() => this.handleDetailsCancel()}
maskClosable={false}
footer={null}
>
<Table
columns={consumptionColumns}
dataSource={details}
pagination={pagination}
rowKey="key"
scroll={{ y: 260 }}
/>
</Modal>
/>
</div>
<Modal
title="消费的Topic"
visible={this.state.detailsVisible}
onOk={() => this.handleDetailsOk()}
onCancel={() => this.handleDetailsCancel()}
maskClosable={false}
footer={null}
// centered={true}
>
<Table
columns={consumptionColumns}
dataSource={details}
// 运维管控-消费组列表-详情
pagination={details.length < 10 ? false : pagination}
rowKey="key"
scroll={{ y: 260 }}
/>
</Modal>
</>
);
}

View File

@@ -33,7 +33,7 @@ export class ClusterController extends SearchAndFilterContainer {
data = searchKey ? origin.filter((item: IController) =>
(item.host !== undefined && item.host !== null) && item.host.toLowerCase().includes(searchKey as string),
) : origin ;
) : origin;
return data;
}
@@ -46,12 +46,6 @@ export class ClusterController extends SearchAndFilterContainer {
key: 'brokerId',
width: '30%',
sorter: (a: IController, b: IController) => b.brokerId - a.brokerId,
},
{
title: 'BrokerHost',
key: 'host',
dataIndex: 'host',
width: '30%',
render: (r: string, t: IController) => {
return (
<a href={`${this.urlPrefix}/admin/broker-detail?clusterId=${this.clusterId}&brokerId=${t.brokerId}`}>{r}
@@ -59,6 +53,18 @@ export class ClusterController extends SearchAndFilterContainer {
);
},
},
{
title: 'BrokerHost',
key: 'host',
dataIndex: 'host',
width: '30%',
// render: (r: string, t: IController) => {
// return (
// <a href={`${this.urlPrefix}/admin/broker-detail?clusterId=${this.clusterId}&brokerId=${t.brokerId}`}>{r}
// </a>
// );
// },
},
{
title: '变更时间',
dataIndex: 'timestamp',

View File

@@ -32,10 +32,12 @@ export class ClusterOverview extends React.Component<IOverview> {
const clusterContent = [{
value: content.clusterName,
label: '集群名称',
}, {
value: clusterTypeMap[content.mode],
label: '集群类型',
}, {
},
// {
// value: clusterTypeMap[content.mode],
// label: '集群类型',
// },
{
value: gmtCreate,
label: '接入时间',
}];

View File

@@ -2,6 +2,7 @@ import * as React from 'react';
import Url from 'lib/url-parser';
import { region } from 'store';
import { admin } from 'store/admin';
import { topic } from 'store/topic';
import { Table, notification, Tooltip, Popconfirm } from 'antd';
import { pagination, cellStyle } from 'constants/table';
import { observer } from 'mobx-react';
@@ -15,6 +16,8 @@ import './index.less';
import moment = require('moment');
import { ExpandPartitionFormWrapper } from 'container/modal/admin/expand-partition';
import { ConfirmDetailTopicFormWrapper } from 'container/modal/admin/confirm-detail-topic';
import { showEditClusterTopic } from 'container/modal/admin';
import { timeFormat } from 'constants/strategy';
@@ -26,6 +29,7 @@ export class ClusterTopic extends SearchAndFilterContainer {
public state = {
searchKey: '',
expandVisible: false,
detailTopicVisible: false,
};
constructor(props: any) {
@@ -44,10 +48,43 @@ export class ClusterTopic extends SearchAndFilterContainer {
this.setState({ expandVisible: val });
}
// 运维管控集群列表Topic列表修改删除业务逻辑-确认删除topic
public handleConfirmVisible(val: boolean) {
this.setState({ detailTopicVisible: val });
}
public expandPartition(item: IClusterTopics) {
// getTopicBasicInfo
admin.getTopicsBasicInfo(item.clusterId, item.topicName).then(data => {
console.log(admin.topicsBasic);
console.log(admin.basicInfo);
this.clusterTopicsFrom = item;
this.setState({
expandVisible: true,
});
});
// if (item.logicalClusterId) {
// topic.getTopicBasicInfo(item.logicalClusterId, item.topicName).then(data => {
// item.regionNameList = topic.baseInfo.regionNameList;
// this.clusterTopicsFrom = item;
// this.setState({
// expandVisible: true,
// });
// });
// } else {
// this.clusterTopicsFrom = item;
// this.setState({
// expandVisible: true,
// });
// }
}
// 运维管控集群列表Topic列表修改删除业务逻辑-确认删除topic
public confirmDetailTopic(item: IClusterTopics) {
this.clusterTopicsFrom = item;
// console.log(this.clusterTopicsFrom);
this.setState({
expandVisible: true,
detailTopicVisible: true,
});
}
@@ -71,7 +108,7 @@ export class ClusterTopic extends SearchAndFilterContainer {
data = searchKey ? origin.filter((item: IClusterTopics) =>
(item.appName !== undefined && item.appName !== null) && item.appName.toLowerCase().includes(searchKey as string)
|| (item.topicName !== undefined && item.topicName !== null) && item.topicName.toLowerCase().includes(searchKey as string),
) : origin ;
) : origin;
return data;
}
@@ -85,7 +122,7 @@ export class ClusterTopic extends SearchAndFilterContainer {
title: 'Topic名称',
dataIndex: 'topicName',
key: 'topicName',
width: '15%',
width: '120px',
sorter: (a: IClusterTopics, b: IClusterTopics) => a.topicName.charCodeAt(0) - b.topicName.charCodeAt(0),
render: (text: string, record: IClusterTopics) => {
return (
@@ -99,11 +136,18 @@ export class ClusterTopic extends SearchAndFilterContainer {
</Tooltip>);
},
},
{
title: '分区数',
dataIndex: 'partitionNum',
key: 'partitionNum',
width: '90px',
sorter: (a: IClusterTopics, b: IClusterTopics) => b.partitionNum - a.partitionNum,
},
{
title: 'QPS',
dataIndex: 'produceRequest',
key: 'produceRequest',
width: '10%',
// width: '10%',
sorter: (a: IClusterTopics, b: IClusterTopics) => b.produceRequest - a.produceRequest,
render: (t: number) => t === null ? '' : t.toFixed(2),
},
@@ -111,15 +155,23 @@ export class ClusterTopic extends SearchAndFilterContainer {
title: 'Bytes In(KB/s)',
dataIndex: 'byteIn',
key: 'byteIn',
width: '15%',
// width: '15%',
sorter: (a: IClusterTopics, b: IClusterTopics) => b.byteIn - a.byteIn,
render: (t: number) => t === null ? '' : (t / 1024).toFixed(2),
},
{
title: 'Bytes Out(KB/s)',
dataIndex: 'byteOut',
key: 'byteOut',
// width: '15%',
sorter: (a: IClusterTopics, b: IClusterTopics) => b.byteOut - a.byteOut,
render: (t: number) => t && t === null ? '' : (t / 1024).toFixed(2),
},
{
title: '所属应用',
dataIndex: 'appName',
key: 'appName',
width: '10%',
// width: '10%',
render: (val: string, record: IClusterTopics) => (
<Tooltip placement="bottomLeft" title={record.appId} >
{val}
@@ -130,22 +182,23 @@ export class ClusterTopic extends SearchAndFilterContainer {
title: '保存时间(h)',
dataIndex: 'retentionTime',
key: 'retentionTime',
width: '10%',
// width: '10%',
sorter: (a: IClusterTopics, b: IClusterTopics) => b.retentionTime - a.retentionTime,
render: (time: any) => transMSecondToHour(time),
render: (time: any) => transMSecondToHour(time),
},
{
title: '更新时间',
dataIndex: 'updateTime',
key: 'updateTime',
sorter: (a: IClusterTopics, b: IClusterTopics) => b.updateTime - a.updateTime,
render: (t: number) => moment(t).format(timeFormat),
width: '10%',
// width: '10%',
},
{
title: 'Topic说明',
dataIndex: 'description',
key: 'description',
width: '15%',
// width: '15%',
onCell: () => ({
style: {
maxWidth: 180,
@@ -155,14 +208,19 @@ export class ClusterTopic extends SearchAndFilterContainer {
},
{
title: '操作',
width: '30%',
width: '120px',
render: (value: string, item: IClusterTopics) => (
<>
<a onClick={() => this.getBaseInfo(item)} className="action-button"></a>
<a onClick={() => this.expandPartition(item)} className="action-button"></a>
{/* <a onClick={() => this.expandPartition(item)} className="action-button">删除</a> */}
<Popconfirm
title="确定删除?"
onConfirm={() => this.deleteTopic(item)}
// 运维管控集群列表Topic列表修改删除业务逻辑
onConfirm={() => this.confirmDetailTopic(item)}
// onConfirm={() => this.deleteTopic(item)}
cancelText="取消"
okText="确认"
>
<a></a>
</Popconfirm>
@@ -190,6 +248,24 @@ export class ClusterTopic extends SearchAndFilterContainer {
/>
</div>
{this.renderExpandModal()}
{this.renderConfirmDetailModal()}
</>
);
}
// 运维管控集群列表Topic列表修改删除业务逻辑-确认删除topic
public renderConfirmDetailModal() {
let formData = {} as IClusterTopics;
formData = this.clusterTopicsFrom ? this.clusterTopicsFrom : formData;
// console.log(formData);
return (
<>
{this.state.detailTopicVisible && <ConfirmDetailTopicFormWrapper
deleteTopic={(val: IClusterTopics) => this.deleteTopic(val)}
handleVisible={(val: boolean) => this.handleConfirmVisible(val)}
visible={this.state.detailTopicVisible}
formData={formData}
clusterId={this.clusterId}
/>}
</>
);
}

View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import { Table, notification, Tooltip, Popconfirm } from 'component/antd';
import { Table, notification, Tooltip, Popconfirm, Modal, Button } from 'component/antd';
import { observer } from 'mobx-react';
import { SearchAndFilterContainer } from 'container/search-filter';
import { pagination, cellStyle } from 'constants/table';
@@ -21,6 +21,8 @@ export class ExclusiveCluster extends SearchAndFilterContainer {
public state = {
searchKey: '',
filterStatus: false,
deteleRegion: false,
logicalClusterName: '',
};
private xFormModal: IXFormWrapper;
@@ -78,7 +80,7 @@ export class ExclusiveCluster extends SearchAndFilterContainer {
}),
render: (value: number[]) => {
const num = value ? value.join(',') : '';
return(
return (
<Tooltip placement="bottomLeft" title={num}>
{num}
</Tooltip>);
@@ -143,6 +145,8 @@ export class ExclusiveCluster extends SearchAndFilterContainer {
<Popconfirm
title="确定删除?"
onConfirm={() => this.handleDeleteRegion(record)}
cancelText="取消"
okText="确认"
>
<a></a>
</Popconfirm>
@@ -154,10 +158,31 @@ export class ExclusiveCluster extends SearchAndFilterContainer {
}
public handleDeleteRegion = (record: IBrokersRegions) => {
deleteRegions(record.id).then(() => {
notification.success({ message: '删除成功' });
admin.getBrokersRegions(this.clusterId);
});
const filterRegion = admin.logicalClusters.filter(item => item.regionIdList.includes(record.id));
if (!filterRegion) {
return;
}
if (filterRegion && filterRegion.length < 1) {
deleteRegions(record.id).then(() => {
notification.success({ message: '删除成功' });
admin.getBrokersRegions(this.clusterId);
});
return;
}
this.setState({ deteleRegion: true, logicalClusterName: filterRegion[0].logicalClusterName });
// deleteRegions(record.id).then(() => {
// notification.success({ message: '删除成功' });
// admin.getBrokersRegions(this.clusterId);
// });
}
public handleExpandOk = () => {
this.setState({ deteleRegion: false });
}
public handleExpandCancel = () => {
this.setState({ deteleCluster: false });
}
public addOrModifyRegion(record?: IBrokersRegions) {
@@ -185,9 +210,9 @@ export class ExclusiveCluster extends SearchAndFilterContainer {
key: 'brokerIdList',
label: 'Broker列表',
defaultValue: record ? record.brokerIdList.join(',') : [],
rules: [{ required: true, message: '请输入BrokerIdList' }],
rules: [{ required: true, message: '请输入BrokerID,多个BrokerID用半角逗号分隔' }],
attrs: {
placeholder: '请输入BrokerIdList',
placeholder: '请输入BrokerID,多个BrokerID用半角逗号分隔',
},
},
{
@@ -248,6 +273,7 @@ export class ExclusiveCluster extends SearchAndFilterContainer {
public componentDidMount() {
admin.getBrokersRegions(this.clusterId);
admin.getLogicalClusters(this.clusterId);
admin.getBrokersMetadata(this.clusterId);
}
@@ -255,10 +281,10 @@ export class ExclusiveCluster extends SearchAndFilterContainer {
let data: T[] = origin;
let { searchKey } = this.state;
searchKey = (searchKey + '').trim().toLowerCase();
data = searchKey ? origin.filter((item: IBrokersRegions) =>
(item.name !== undefined && item.name !== null) && item.name.toLowerCase().includes(searchKey as string),
) : origin ;
(item.name !== undefined && item.name !== null) && item.name.toLowerCase().includes(searchKey as string)
|| item.brokerIdList && item.brokerIdList.map(item => "" + item).join(',').includes(searchKey as string),
) : origin;
return data;
}
@@ -272,6 +298,30 @@ export class ExclusiveCluster extends SearchAndFilterContainer {
/>
);
}
// -删除RegionModal
public renderDeleteRegionModal() {
return (
<Modal
title="提示"
visible={this.state.deteleRegion}
// okText="确定"
// cancelText="取消"
maskClosable={false}
// onCancel={() => this.handleExpandCancel()}
closable={false}
// onOk={() => this.handleExpandOk()}
footer={<Button style={{ width: '80px' }} type="primary" onClick={() => this.handleExpandOk()}></Button>}
// onCancel={() => this.handleExpandCancel()}
>
<div className="region-prompt">
<span>
Region已被逻辑集群 {this.state.logicalClusterName} 使
Region与逻辑集群的关系
</span>
</div>
</Modal>
);
}
public render() {
return (
@@ -282,10 +332,10 @@ export class ExclusiveCluster extends SearchAndFilterContainer {
<i className="k-icon-xinjian didi-theme" />
<span>Region</span>
</li>
{this.renderSearch('', '请输入Region名称')}
{this.renderSearch('', '请输入Region名称broker ID')}
</ul>
{this.renderRegion()}
</div>
{this.renderRegion()}
</div >
);
}
}

View File

@@ -81,4 +81,17 @@
justify-content: space-between;
margin-bottom: 10px;
}
}
.cluster-prompt{
font-weight: bold;
margin-bottom: 10px;
}
.cluster-explain{
color: #838383;
// transform: scale(0.95,0.95);
// text-align: center;
}
.region-prompt{
font-weight: bold;
text-align: center;
}

View File

@@ -1,6 +1,7 @@
import * as React from 'react';
import { Table, notification, Popconfirm } from 'component/antd';
import { Modal } from 'antd';
import { observer } from 'mobx-react';
import { SearchAndFilterContainer } from 'container/search-filter';
import { pagination } from 'constants/table';
@@ -21,6 +22,8 @@ export class LogicalCluster extends SearchAndFilterContainer {
public state = {
searchKey: '',
filterStatus: false,
deteleCluster: false,
logicalClusterId: -1,
};
constructor(props: any) {
@@ -37,9 +40,16 @@ export class LogicalCluster extends SearchAndFilterContainer {
key: 'logicalClusterId',
},
{
title: '逻辑集群名称',
title: '逻辑集群中文名称',
dataIndex: 'logicalClusterName',
key: 'logicalClusterName',
width: '150px'
},
{
title: '逻辑集群英文名称',
dataIndex: 'logicalClusterName',
key: 'logicalClusterName1',
width: '150px'
},
{
title: '应用ID',
@@ -52,7 +62,7 @@ export class LogicalCluster extends SearchAndFilterContainer {
key: 'regionIdList',
render: (value: number[]) => {
const num = value ? `[${value.join(',')}]` : '';
return(
return (
<span>{num}</span>
);
},
@@ -63,12 +73,12 @@ export class LogicalCluster extends SearchAndFilterContainer {
key: 'mode',
render: (value: number) => {
let val = '';
cluster.clusterModes.forEach((ele: any) => {
cluster.clusterModes && cluster.clusterModes.forEach((ele: any) => {
if (value === ele.code) {
val = ele.message;
}
});
return(<span>{val}</span>);
return (<span>{val}</span>);
},
},
{
@@ -84,16 +94,20 @@ export class LogicalCluster extends SearchAndFilterContainer {
},
{
title: '操作',
width: '120px',
render: (text: string, record: ILogicalCluster) => {
return (
<span className="table-operation">
<a onClick={() => this.editRegion(record)}></a>
<Popconfirm
<a onClick={() => this.handleDeleteRegion(record)}></a>
{/* <Popconfirm
title="确定删除?"
cancelText="取消"
okText="确认"
onConfirm={() => this.handleDeleteRegion(record)}
>
<a>删除</a>
</Popconfirm>
</Popconfirm> */}
</span>
);
},
@@ -102,9 +116,24 @@ export class LogicalCluster extends SearchAndFilterContainer {
}
public handleDeleteRegion = (record: ILogicalCluster) => {
admin.deteleLogicalClusters(this.clusterId, record.logicalClusterId).then(() => {
this.setState({ deteleCluster: true, logicalClusterId: record.logicalClusterId });
// admin.deteleLogicalClusters(this.clusterId, record.logicalClusterId).then(() => {
// notification.success({ message: '删除成功' });
// });
}
// -删除逻辑集群
public handleExpandOk = () => {
const { logicalClusterId } = this.state;
admin.deteleLogicalClusters(this.clusterId, logicalClusterId).then(() => {
notification.success({ message: '删除成功' });
});
this.setState({ deteleCluster: false });
}
public handleExpandCancel = () => {
this.setState({ deteleCluster: false });
}
public async editRegion(record: ILogicalCluster) {
@@ -131,12 +160,11 @@ export class LogicalCluster extends SearchAndFilterContainer {
let data: T[] = origin;
let { searchKey } = this.state;
searchKey = (searchKey + '').trim().toLowerCase();
data = searchKey ? origin.filter((item: ILogicalCluster) =>
(item.logicalClusterName !== undefined && item.logicalClusterName !== null)
&& item.logicalClusterName.toLowerCase().includes(searchKey as string)
&& item.logicalClusterName.toLowerCase().includes(searchKey as string)
|| (item.appId !== undefined && item.appId !== null) && item.appId.toLowerCase().includes(searchKey as string),
) : origin ;
) : origin;
return data;
}
@@ -151,11 +179,37 @@ export class LogicalCluster extends SearchAndFilterContainer {
);
}
// -删除逻辑集群
public renderDeleteCluster() {
return (
<Modal
title="提示"
visible={this.state.deteleCluster}
okText="确认删除"
cancelText="取消"
maskClosable={false}
onOk={() => this.handleExpandOk()}
onCancel={() => this.handleExpandCancel()}
>
<div className="cluster-prompt">
<span>
TopicTopic管理页查看到该Topic
</span>
</div>
<div className="cluster-explain">
<span>
topic
</span>
</div>
</Modal>
);
}
public render() {
return (
<div className="k-row">
<ul className="k-tab">
<li>{this.props.tab}</li>
<li>{this.props.tab}</li>
<li className="k-add" onClick={() => this.addOrEditLogicalCluster()}>
<i className="k-icon-xinjian didi-theme" />
<span></span>
@@ -163,6 +217,7 @@ export class LogicalCluster extends SearchAndFilterContainer {
{this.renderSearch('', '请输入逻辑集群名称或AppId')}
</ul>
{this.renderLogicalCluster()}
{this.renderDeleteCluster()}
</div>
);
}

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import { Modal, Table, Button, notification, message, Tooltip, Icon, Popconfirm } from 'component/antd';
import { Modal, Table, Button, notification, message, Tooltip, Icon, Popconfirm, Alert } from 'component/antd';
import { wrapper } from 'store';
import { observer } from 'mobx-react';
import { IXFormWrapper, IMetaData, IRegister } from 'types/base-type';
@@ -9,6 +9,7 @@ import { SearchAndFilterContainer } from 'container/search-filter';
import { cluster } from 'store/cluster';
import { customPagination } from 'constants/table';
import { urlPrefix } from 'constants/left-menu';
import { indexUrl } from 'constants/strategy'
import { region } from 'store';
import './index.less';
import { getAdminClusterColumns } from '../config';
@@ -126,8 +127,13 @@ export class ClusterList extends SearchAndFilterContainer {
message: '请输入安全协议',
}],
attrs: {
placeholder: '请输入安全协议',
rows: 6,
placeholder: `请输入安全协议,例如:
{
"security.protocol": "SASL_PLAINTEXT",
"sasl.mechanism": "PLAIN",
"sasl.jaas.config": "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"xxxxxx\" password=\"xxxxxx\";"
}`,
rows: 8,
},
},
],
@@ -172,7 +178,7 @@ export class ClusterList extends SearchAndFilterContainer {
<span className="offline_span">
&nbsp;
<a>
<Tooltip placement="right" title={'当前集群存在逻辑集群,无法申请下线'} >
<Tooltip placement="right" title={'当前集群存在逻辑集群,无法删除'} >
<Icon type="question-circle" />
</Tooltip>
</a>
@@ -185,7 +191,7 @@ export class ClusterList extends SearchAndFilterContainer {
cancelText: '取消',
onOk() {
if (data.length) {
return message.warning('存在逻辑集群,无法申请下线');
return message.warning('存在逻辑集群,无法删除');
}
admin.deleteCluster(record.clusterId).then(data => {
notification.success({ message: '删除成功' });
@@ -204,7 +210,7 @@ export class ClusterList extends SearchAndFilterContainer {
};
const monitorColumns = [
{
title: '集群名称',
title: '逻辑集群列表',
dataIndex: 'name',
key: 'name',
onCell: () => ({
@@ -231,6 +237,7 @@ export class ClusterList extends SearchAndFilterContainer {
pagination={false}
bordered={true}
/>
<Alert message="若当前集群存在逻辑集群,则无法删除" type="error" showIcon={true} />
</div>
</>
);
@@ -243,7 +250,7 @@ export class ClusterList extends SearchAndFilterContainer {
data = searchKey ? origin.filter((item: IMetaData) =>
(item.clusterName !== undefined && item.clusterName !== null) && item.clusterName.toLowerCase().includes(searchKey as string),
) : origin ;
) : origin;
return data;
}
@@ -261,6 +268,8 @@ export class ClusterList extends SearchAndFilterContainer {
<Popconfirm
title={`确定${item.status === 1 ? '暂停' : '开始'}${item.clusterName}监控?`}
onConfirm={() => this.pauseMonitor(item)}
cancelText="取消"
okText="确认"
>
<a
className="action-button"
@@ -286,6 +295,7 @@ export class ClusterList extends SearchAndFilterContainer {
<ul>
{this.renderSearch('', '请输入集群名称')}
<li className="right-btn-1">
<a style={{ display: 'inline-block', marginRight: '20px' }} href={indexUrl.cagUrl} target="_blank"></a>
<Button type="primary" onClick={this.createOrRegisterCluster.bind(this, null)}></Button>
</li>
</ul>

View File

@@ -30,6 +30,8 @@ export const getUserColumns = () => {
<Popconfirm
title="确定删除?"
onConfirm={() => users.deleteUser(record.username)}
cancelText="取消"
okText="确认"
>
<a></a>
</Popconfirm>
@@ -54,7 +56,7 @@ export const getVersionColumns = () => {
render: (text: string, record: IUploadFile) => {
return (
<Tooltip placement="topLeft" title={text} >
<a href={`${urlPrefix}/info?fileId=${record.id}`} target="_blank">{text}</a>
<a href={`${window.origin}/api/v1/rd/kafka-files/${record.id}/config-files?dataCenter=cn`} target="_blank">{text}</a>
</Tooltip>);
},
}, {
@@ -70,7 +72,7 @@ export const getVersionColumns = () => {
render: (text: string) => {
return (
<Tooltip placement="bottomLeft" title={text} >
{text.substring(0, 8)}
{text.substring(0, 8)}
</Tooltip>);
},
}, {
@@ -95,26 +97,28 @@ export const getVersionColumns = () => {
render: (text: string) => {
return (
<Tooltip placement="topLeft" title={text} >
{text}
{text}
</Tooltip>);
},
}, {
title: '操作',
dataIndex: 'operation',
key: 'operation',
render: (text: string, record: IUploadFile) => {
return (
<span className="table-operation">
<a onClick={() => showModifyModal(record)}></a>
<Popconfirm
title="确定删除?"
onConfirm={() => version.deleteFile(record.id)}
>
<a></a>
</Popconfirm>
</span>);
},
dataIndex: 'operation',
key: 'operation',
render: (text: string, record: IUploadFile) => {
return (
<span className="table-operation">
<a onClick={() => showModifyModal(record)}></a>
<Popconfirm
title="确定删除?"
onConfirm={() => version.deleteFile(record.id)}
cancelText="取消"
okText="确认"
>
<a></a>
</Popconfirm>
</span>);
},
},
];
return columns;
};
@@ -168,6 +172,8 @@ export const getConfigureColumns = () => {
<Popconfirm
title="确定删除?"
onConfirm={() => admin.deleteConfigure(record.configKey)}
cancelText="取消"
okText="确认"
>
<a></a>
</Popconfirm>
@@ -178,11 +184,11 @@ export const getConfigureColumns = () => {
return columns;
};
const renderClusterHref = (value: number | string, item: IMetaData, key: number) => {
const renderClusterHref = (value: number | string, item: IMetaData, key: number) => {
return ( // 0 暂停监控--不可点击 1 监控中---可正常点击
<>
{item.status === 1 ? <a href={`${urlPrefix}/admin/cluster-detail?clusterId=${item.clusterId}#${key}`}>{value}</a>
: <a style={{ cursor: 'not-allowed', color: '#999' }}>{value}</a>}
{item.status === 1 ? <a href={`${urlPrefix}/admin/cluster-detail?clusterId=${item.clusterId}#${key}`}>{value}</a>
: <a style={{ cursor: 'not-allowed', color: '#999' }}>{value}</a>}
</>
);
};
@@ -190,13 +196,13 @@ const renderClusterHref = (value: number | string, item: IMetaData, key: number
export const getAdminClusterColumns = () => {
return [
{
title: '集群ID',
title: '物理集群ID',
dataIndex: 'clusterId',
key: 'clusterId',
sorter: (a: IMetaData, b: IMetaData) => b.clusterId - a.clusterId,
},
{
title: '集群名称',
title: '物理集群名称',
dataIndex: 'clusterName',
key: 'clusterName',
sorter: (a: IMetaData, b: IMetaData) => a.clusterName.charCodeAt(0) - b.clusterName.charCodeAt(0),

View File

@@ -26,11 +26,11 @@ export class ConfigureManagement extends SearchAndFilterContainer {
searchKey = (searchKey + '').trim().toLowerCase();
data = searchKey ? origin.filter((item: IConfigure) =>
((item.configKey !== undefined && item.configKey !== null) && item.configKey.toLowerCase().includes(searchKey as string))
|| ((item.configValue !== undefined && item.configValue !== null) && item.configValue.toLowerCase().includes(searchKey as string))
|| ((item.configDescription !== undefined && item.configDescription !== null) &&
((item.configKey !== undefined && item.configKey !== null) && item.configKey.toLowerCase().includes(searchKey as string))
|| ((item.configValue !== undefined && item.configValue !== null) && item.configValue.toLowerCase().includes(searchKey as string))
|| ((item.configDescription !== undefined && item.configDescription !== null) &&
item.configDescription.toLowerCase().includes(searchKey as string)),
) : origin ;
) : origin;
return data;
}

View File

@@ -55,7 +55,9 @@ export class EassentialInfo extends React.Component<IEassProps> {
<Descriptions.Item key={item.label || index} label={item.label}>{item.value}</Descriptions.Item>
))}
<Descriptions.Item key="server" label="server配置名">
<a href={`${urlPrefix}/info?fileId=${tasks.serverPropertiesFileId || ''}`} target="_blank">{tasks.serverPropertiesName}</a>
{/* /api/v1/rd/kafka-files/66/config-files?dataCenter=cn */}
<a href={`${window.origin}/api/v1/rd/kafka-files/${tasks.serverPropertiesFileId}/config-files?dataCenter=cn`} target="_blank">{tasks.serverPropertiesName}</a>
</Descriptions.Item>
<Descriptions.Item key="server" label="server配置 MD5">{tasks.serverPropertiesMd5}</Descriptions.Item>
</Descriptions>

View File

@@ -82,6 +82,8 @@ export class OperationDetail extends React.Component {
<Popconfirm
title={`确定${showContinue ? '开始' : '暂停'}`}
onConfirm={() => this.bindClick()}
cancelText="取消"
okText="确认"
>
<a>{showContinue ? '开始' : '暂停'}</a>
</Popconfirm>
@@ -94,6 +96,8 @@ export class OperationDetail extends React.Component {
<Popconfirm
title={`确定回滚?`}
onConfirm={() => this.callBackOrCancel('rollback')}
cancelText="取消"
okText="确认"
>
<a></a>
</Popconfirm>
@@ -106,6 +110,8 @@ export class OperationDetail extends React.Component {
<Popconfirm
title={`确定回滚?`}
onConfirm={() => this.callBackOrCancel('cancel')}
cancelText="取消"
okText="确认"
>
<a></a>
</Popconfirm>

View File

@@ -87,6 +87,8 @@ export class TaskStatusDetails extends SearchAndFilterContainer {
<Popconfirm
title={`确定忽略?`}
onConfirm={() => this.bindClick(record, 'ignore')}
cancelText="取消"
okText="确认"
>
<a></a>
</Popconfirm>
@@ -130,7 +132,7 @@ export class TaskStatusDetails extends SearchAndFilterContainer {
return (
<>
<div className="config-info">
{admin.clusterTaskLog}
{admin.clusterTaskLog ? admin.clusterTaskLog : '暂无数据'}
</div>
</>
);

View File

@@ -78,10 +78,10 @@ export class ClusterTask extends SearchAndFilterContainer {
cluster,
{
title: '创建时间',
dataIndex: 'gmtCreate',
key: 'gmtCreate',
dataIndex: 'createTime',
key: 'createTime',
width: '15%',
sorter: (a: ITaskManage, b: ITaskManage) => b.gmtCreate - a.gmtCreate,
sorter: (a: ITaskManage, b: ITaskManage) => b.createTime - a.createTime,
render: (t: number) => moment(t).format(timeFormat),
},
{

View File

@@ -41,6 +41,8 @@ export const migrationTaskColumns = (migrationUrl: string) => {
<Popconfirm
title="确定开始?"
onConfirm={() => startMigrationTask(item, 'start')}
cancelText="取消"
okText="确认"
>
<a style={{ marginRight: 16 }}></a>
</Popconfirm>}
@@ -49,6 +51,8 @@ export const migrationTaskColumns = (migrationUrl: string) => {
{item.status === 0 &&
<Popconfirm
title="确定取消?"
cancelText="取消"
okText="确认"
onConfirm={() => cancelMigrationTask(item, 'cancel')}
><a></a>
</Popconfirm>}

View File

@@ -35,7 +35,7 @@ export class MigrationDetail extends SearchAndFilterContainer {
const detail = expert.tasksDetail;
const gmtCreate = moment(detail.gmtCreate).format(timeFormat);
const startTime = moment(detail.beginTime).format(timeFormat);
const endTime = moment(detail.endTime).format(timeFormat);
const endTime = detail.endTime == null ? '任务运行中' : moment(detail.endTime).format(timeFormat);
const options = [{
value: detail.taskName,
label: '任务名称',

View File

@@ -20,7 +20,7 @@ export class VersionManagement extends SearchAndFilterContainer {
public async componentDidMount() {
if (!version.fileTypeList.length) {
await version.getFileTypeList();
await version.getFileTypeList();
}
if (!version.fileList.length) {
@@ -58,7 +58,7 @@ export class VersionManagement extends SearchAndFilterContainer {
if (searchKey) {
data = origin.filter((item: IUploadFile) => item.id + '' === searchKey
|| ((item.fileName !== undefined && item.fileName !== null) && item.fileName.toLowerCase().includes(searchKey as string)));
|| ((item.fileName !== undefined && item.fileName !== null) && item.fileName.toLowerCase().includes(searchKey as string)));
}
return data;
}

View File

@@ -76,6 +76,8 @@ export const getAlarmColumns = (urlPrefix: string) => {
<Popconfirm
title="确定删除?"
onConfirm={() => deteleMonitor(item)}
cancelText="取消"
okText="确认"
>
<a></a>
</Popconfirm>
@@ -193,21 +195,21 @@ export const xActionFormMap = [{
key: 'acceptGroup',
label: '报警接收组',
type: 'custom',
customFormItem: <AlarmSelect isDisabled={isDetailPage}/>,
customFormItem: <AlarmSelect isDisabled={isDetailPage} />,
rules: [{ required: true, message: '请输入报警接收组' }],
},
{
key: 'callback',
label: '回调地址',
rules: [{ required: false, message: '请输入回调地址' }],
attrs: {disabled: isDetailPage},
attrs: { disabled: isDetailPage },
}] as unknown as IFormSelect[]; // as IFormItem[];
export const xTypeFormMap = [{
key: 'alarmName',
label: '告警规则',
label: '告警规则名称',
rules: [{ required: true, message: '请输入告警规则' }],
attrs: {placeholder: '请输入', disabled: isDetailPage},
attrs: { placeholder: '请输入告警规则名称', disabled: isDetailPage },
}, {
key: 'app',
label: '所属应用',
@@ -217,8 +219,8 @@ export const xTypeFormMap = [{
optionFilterProp: 'children',
showSearch: true,
filterOption: (input: any, option: any) => {
if ( typeof option.props.children === 'object' ) {
const { props } = option.props.children as any;
if (typeof option.props.children === 'object') {
const { props } = option.props.children as any;
return (props.children + '').toLowerCase().indexOf(input.toLowerCase()) >= 0;
}
return (option.props.children + '').toLowerCase().indexOf(input.toLowerCase()) >= 0;

View File

@@ -260,7 +260,7 @@ export class DynamicSetFilter extends React.Component<IDynamicProps> {
rules: [{ required: showMore, message: '请选择消费组' }],
attrs: {
placeholder: '请选择消费组',
className: 'middle-size',
className: 'large-size',
disabled: this.isDetailPage,
onChange: (e: string) => this.handleSelectChange(e, 'consumerGroup'),
},

View File

@@ -45,10 +45,13 @@
}
&.type-form {
padding-top: 10px;
padding-top: 10px;
.ant-form{
min-width: 755px;
}
.ant-form-item {
width: 30%
width: 30%;
min-width: 360px;
}
.ant-form-item-label {
padding-left: 10px;
@@ -161,7 +164,7 @@
}
.dynamic-set {
padding: 15px 10px;
padding: 15px 0;
ul{
li{
@@ -176,7 +179,7 @@
.ant-form-item {
display: inline-block;
margin: 0px 5px 10px 5px;
margin: 0px 5px 10px 0;
.ant-select {
width: 150px;

View File

@@ -163,6 +163,8 @@ export class ShieldHistory extends React.Component {
<Popconfirm
title="确定删除?"
onConfirm={() => this.deleteSilences(record)}
cancelText="取消"
okText="确认"
>
<a></a>
</Popconfirm>

View File

@@ -7,7 +7,7 @@ const Option = Select.Option;
interface IStaffSelectProps {
selectData?: any[];
onChange?: (result: string []) => any;
onChange?: (result: string[]) => any;
value?: string[];
}
@@ -21,7 +21,7 @@ export class AppSelect extends React.Component<IStaffSelectProps> {
<Select
placeholder="请选择"
value={value || []}
onChange={(e: string []) => this.handleChange(e)}
onChange={(e: string[]) => this.handleChange(e)}
{...searchProps}
>
{selectData.map((d: any) =>
@@ -29,16 +29,18 @@ export class AppSelect extends React.Component<IStaffSelectProps> {
{d.name.length > 25 ? <Tooltip placement="bottomLeft" title={d.name}>{d.name}</Tooltip> : d.name}
</Option>)}
</Select>
{
selectData.length ? null : <i>
{/* {
selectData.length ? null : */}
<i>
<a href={`${urlPrefix}/topic/app-list?${query}`}></a>
</i>}
</i>
{/* } */}
</>
);
}
public handleChange(params: string []) {
public handleChange(params: string[]) {
const { onChange } = this.props;
// tslint:disable-next-line:no-unused-expression
onChange && onChange(params);

View File

@@ -61,6 +61,7 @@ export class AppDetail extends SearchAndFilterContainer {
title: '申请时间',
dataIndex: 'gmtCreate',
key: 'gmtCreate',
sorter: (a: any, b: any) => a.gmtCreate - b.gmtCreate,
render: (t: number) => moment(t).format(timeFormat),
},
statusColumn,
@@ -101,11 +102,11 @@ export class AppDetail extends SearchAndFilterContainer {
value: baseInfo.principals,
}];
const infoCopy: ILabelValue[] = [{
label: 'AppID',
value: baseInfo.appId,
}, {
label: '密钥',
value: baseInfo.password,
label: 'AppID',
value: baseInfo.appId,
}, {
label: '密钥',
value: baseInfo.password,
}];
return (
<PageHeader
@@ -118,7 +119,7 @@ export class AppDetail extends SearchAndFilterContainer {
<Descriptions.Item key={key} label={item.label}>
<Tooltip placement="bottomLeft" title={item.value}>
<span className="overview-bootstrap">
<i className="overview-boot">{item.value}</i>
<i className="overview-boot">{item.value}</i>
</span>
</Tooltip>
</Descriptions.Item>
@@ -136,14 +137,14 @@ export class AppDetail extends SearchAndFilterContainer {
))}
</Descriptions>
<Descriptions size="small" column={1}>
<Descriptions.Item label="应用描述">
<Tooltip placement="bottomLeft" title={baseInfo.description}>
<span className="overview-bootstrap" style={{width: '600px'}}>
<i className="overview-boot"> {baseInfo.description} </i>
</span>
</Tooltip>
</Descriptions.Item>
</Descriptions>
<Descriptions.Item label="应用描述">
<Tooltip placement="bottomLeft" title={baseInfo.description}>
<span className="overview-bootstrap" style={{ width: '600px' }}>
<i className="overview-boot"> {baseInfo.description} </i>
</span>
</Tooltip>
</Descriptions.Item>
</Descriptions>
</PageHeader>
);
}
@@ -156,7 +157,7 @@ export class AppDetail extends SearchAndFilterContainer {
data = searchKey ? origin.filter((item: ITopic) =>
(item.topicName !== undefined && item.topicName !== null) && item.topicName.toLowerCase().includes(searchKey as string)
|| (item.clusterName !== undefined && item.clusterName !== null) && item.clusterName.toLowerCase().includes(searchKey as string),
) : origin ;
) : origin;
return data;
}
@@ -183,22 +184,22 @@ export class AppDetail extends SearchAndFilterContainer {
const { currentTab } = app;
return (
<>
<div className="app-container">
<div className="base-info">
{this.renderBaseInfo(app.baseInfo)}
<div className="app-container">
<div className="base-info">
{this.renderBaseInfo(app.baseInfo)}
</div>
<div className="k-row">
<Tabs defaultActiveKey="1" type="card" onChange={(e) => this.onChangeTab(e)}>
<TabPane tab="创建的Topic" key="1" />
<TabPane tab="有权限Topic" key="2" />
</Tabs>
<ul className="k-tab">
<li>{currentTab === '1' ? '创建的Topic' : '有权限Topic'}</li>
{this.renderSearch('', '请输入Topic名称/集群名称')}
</ul>
{this.renderTable()}
</div>
</div>
<div className="k-row">
<Tabs defaultActiveKey="1" type="card" onChange={(e) => this.onChangeTab(e)}>
<TabPane tab="创建的Topic" key="1" />
<TabPane tab="有权限Topic" key="2" />
</Tabs>
<ul className="k-tab">
<li>{currentTab === '1' ? '创建的Topic' : '有权限Topic'}</li>
{this.renderSearch('', '请输入Topic名称/集群名称')}
</ul>
{this.renderTable()}
</div>
</div>
</>
);
}

View File

@@ -31,8 +31,13 @@ export class ClusterOverview extends React.Component<IOverview> {
const content = this.props.basicInfo as IBasicInfo;
const clusterContent = [{
value: content.clusterName,
label: '集群名称',
}, {
label: '集群中文名称',
},
{
value: content.clusterName,
label: '集群英文名称',
},
{
value: clusterTypeMap[content.mode],
label: '集群类型',
}, {
@@ -55,23 +60,23 @@ export class ClusterOverview extends React.Component<IOverview> {
<PageHeader className="detail" title="">
<Descriptions size="small" column={3}>
{clusterContent.map((item: ILabelValue, index: number) => (
<Descriptions.Item key={index} label={item.label} >
{item.value}
</Descriptions.Item>
<Descriptions.Item key={index} label={item.label} >
{item.value}
</Descriptions.Item>
))}
{clusterInfo.map((item: ILabelValue, index: number) => (
<Descriptions.Item key={index} label={item.label}>
<Tooltip placement="bottomLeft" title={item.value}>
<span className="overview-bootstrap">
<Icon
onClick={() => copyString(item.value)}
type="copy"
className="didi-theme overview-theme"
/>
<i className="overview-boot">{item.value}</i>
</span>
</Tooltip>
</Descriptions.Item>
<Descriptions.Item key={index} label={item.label}>
<Tooltip placement="bottomLeft" title={item.value}>
<span className="overview-bootstrap">
<Icon
onClick={() => copyString(item.value)}
type="copy"
className="didi-theme overview-theme"
/>
<i className="overview-boot">{item.value}</i>
</span>
</Tooltip>
</Descriptions.Item>
))}
</Descriptions>
</PageHeader>

View File

@@ -101,6 +101,7 @@ export class ClusterTopic extends SearchAndFilterContainer {
dataIndex: 'updateTime',
key: 'updateTime',
width: '20%',
sorter: (a: IClusterTopics, b: IClusterTopics) => b.updateTime - a.updateTime,
render: (t: number) => moment(t).format(timeFormat),
},
{

View File

@@ -13,10 +13,35 @@ const { confirm } = Modal;
export const getClusterColumns = (urlPrefix: string) => {
return [
{
title: '集群名称',
title: '逻辑集群ID',
dataIndex: 'clusterId',
key: 'clusterId',
width: '9%',
sorter: (a: IClusterData, b: IClusterData) => b.clusterId - a.clusterId,
},
{
title: '逻辑集群中文名称',
dataIndex: 'clusterName',
key: 'clusterName',
width: '15%',
width: '13%',
onCell: () => ({
style: {
maxWidth: 120,
...cellStyle,
},
}),
sorter: (a: IClusterData, b: IClusterData) => a.clusterName.charCodeAt(0) - b.clusterName.charCodeAt(0),
render: (text: string, record: IClusterData) => (
<Tooltip placement="bottomLeft" title={text} >
<a href={`${urlPrefix}/cluster/cluster-detail?clusterId=${record.clusterId}`}> {text} </a>
</Tooltip>
),
},
{
title: '逻辑集群英文名称',
dataIndex: 'clusterName',
key: 'clusterName',
width: '13%',
onCell: () => ({
style: {
maxWidth: 120,
@@ -34,21 +59,21 @@ export const getClusterColumns = (urlPrefix: string) => {
title: 'Topic数量',
dataIndex: 'topicNum',
key: 'topicNum',
width: '10%',
width: '9%',
sorter: (a: IClusterData, b: IClusterData) => b.topicNum - a.topicNum,
},
{
title: '集群类型',
dataIndex: 'mode',
key: 'mode',
width: '10%',
width: '9%',
render: (text: number) => (clusterTypeMap[text] || ''),
},
{
title: '集群版本',
dataIndex: 'clusterVersion',
key: 'clusterVersion',
width: '25%',
width: '9%',
onCell: () => ({
style: {
maxWidth: 200,
@@ -60,14 +85,14 @@ export const getClusterColumns = (urlPrefix: string) => {
title: '接入时间',
dataIndex: 'gmtCreate',
key: 'gmtCreate',
width: '15%',
width: '13%',
sorter: (a: IClusterData, b: IClusterData) => b.gmtCreate - a.gmtCreate,
render: (t: number) => moment(t).format(timeFormat),
}, {
title: '修改时间',
dataIndex: 'gmtModify',
key: 'gmtModify',
width: '15%',
width: '13%',
sorter: (a: IClusterData, b: IClusterData) => b.gmtModify - a.gmtModify,
render: (t: number) => moment(t).format(timeFormat),
},
@@ -75,7 +100,7 @@ export const getClusterColumns = (urlPrefix: string) => {
title: '操作',
dataIndex: 'action',
key: 'action',
width: '10%',
width: '20%',
render: (val: string, record: IClusterData) => (
<>
{
@@ -90,6 +115,6 @@ export const getClusterColumns = (urlPrefix: string) => {
];
};
export const showConfirm = (record: IClusterData) => {
export const showConfirm = (record: IClusterData) => {
modal.showOfflineClusterModal(record.clusterId);
};

View File

@@ -113,8 +113,10 @@
text-align: center;
height: 32px;
line-height: 32px;
padding: 0px 20px;
a {
display: inline-block;
padding: 0px 20px;
}
&:hover {
background: rgba(236, 111, 38, 0.1);
}
@@ -159,4 +161,17 @@
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
// .kafka-header-menu{
// .qqqq{
// position: relative;
// .popover {
// position: absolute;
// display: block;
// left: 0 !important;
// // left: 62% !important;
// // top: 68px !important;
// }
// }
// }

View File

@@ -2,7 +2,7 @@ import * as React from 'react';
import './index.less';
import { userLogOut } from 'lib/api';
import { notification, Dropdown, Icon, Tooltip } from 'component/antd';
import { notification, Dropdown, Icon, Tooltip, Popover } from 'component/antd';
import { urlPrefix } from 'constants/left-menu';
import { region, IRegionIdcs } from 'store/region';
import logoUrl from '../../assets/image/kafka-logo.png';
@@ -59,7 +59,11 @@ export const Header = observer((props: IHeader) => {
notification.success({ message: '退出成功' });
});
};
const content = (
<div style={{ height: '250px', padding: '5px' }} className="kafka-avatar-img">
<img style={{ width: '190px', height: '246px' }} src={weChat} alt="" />
</div>
);
const helpCenter = (
<ul className="kafka-header-menu">
<li>
@@ -70,25 +74,33 @@ export const Header = observer((props: IHeader) => {
</a></li>
<li>
<a
href="https://github.com/didi/kafka-manager"
href="https://github.com/didi/kafka-manager/blob/master/docs/user_guide/user_guide_cn.md"
target="_blank"
>使
</a></li>
<li>
<a
href="https://github.com/didi/kafka-manager/blob/master/docs/install_guide/install_guide_cn.md"
target="_blank"
>QuickStart
</a></li>
<li>
<a
href=""
href="https://github.com/didi/kafka-manager/blob/master/docs/user_guide/faq.md"
target="_blank"
>
</a></li>
<li>
<a
// tslint:disable-next-line:max-line-length
href="https://github.com/didi/kafka-manager"
target="_blank"
>
</a></li>
<li style={{ height: '80px', padding: '5px' }} className="kafka-avatar-img">
<img style={{ width: '70px', height: '70px' }} src={weChat} alt="" />
<Popover placement="left" content={content} trigger="hover">
<a
// tslint:disable-next-line:max-line-length
// href="https://github.com/didi/kafka-manager"
href="javascript:void(0)"
onClick={() => { return false }}
// target="_blank"
>
</a>
</Popover>
</li>
</ul>
);
@@ -111,7 +123,7 @@ export const Header = observer((props: IHeader) => {
const handleJumpLocation = (): string => {
const isDetailPage = window.location.pathname.includes('detail');
const pathNames = window.location.pathname.split('/');
const loc = window.location.pathname.includes('error') ? `${urlPrefix}/topic` : window.location.pathname ;
const loc = window.location.pathname.includes('error') ? `${urlPrefix}/topic` : window.location.pathname;
return isDetailPage ? pathNames.splice(0, 3).join('/') : loc;
};

View File

@@ -29,6 +29,7 @@ export const showEditClusterTopic = (item: IClusterTopics) => {
}],
attrs: {
placeholder: '请输入应用ID',
disabled: true,
},
},
{
@@ -57,7 +58,7 @@ export const showEditClusterTopic = (item: IClusterTopics) => {
key: 'properties',
label: 'Topic属性列表',
type: 'text_area',
rules: [{ required: false}],
rules: [{ required: false }],
attrs: {
placeholder: '请输入Topic属性列表',
},
@@ -78,7 +79,7 @@ export const showEditClusterTopic = (item: IClusterTopics) => {
clusterName: item.clusterName,
appId: item.appId,
topicName: item.topicName,
retentionTime: transMSecondToHour(item.retentionTime),
retentionTime: transMSecondToHour(item.retentionTime),
properties: JSON.stringify(item.properties, null, 4),
description: item.description,
},
@@ -97,18 +98,13 @@ export const showEditClusterTopic = (item: IClusterTopics) => {
};
export const showLogicalClusterOpModal = (clusterId: number, record?: ILogicalCluster) => {
let clusterModes = [] as IConfigInfo[];
clusterModes = cluster.clusterModes ? cluster.clusterModes : clusterModes;
const xFormModal = {
formMap: [
{
key: 'logicalClusterName',
label: '逻辑集群名称',
rules: [{ required: true, message: '请输入逻辑集群名称' }],
attrs: {
disabled: record ? true : false,
},
},
let isShow = false;
if (record && record.mode != 0) {
isShow = true;
}
const updateFormModal = (isShow: boolean) => {
const formMap = wrapper.xFormWrapper.formMap;
isShow ? formMap.splice(2, 0,
{
key: 'appId',
label: '所属应用',
@@ -123,6 +119,41 @@ export const showLogicalClusterOpModal = (clusterId: number, record?: ILogicalCl
attrs: {
placeholder: '请选择所属应用',
},
}) : formMap.splice(2, 1);
const formData = wrapper.xFormWrapper.formData;
wrapper.ref && wrapper.ref.updateFormMap$(formMap, formData || {});
};
let clusterModes = [] as IConfigInfo[];
clusterModes = cluster.clusterModes ? cluster.clusterModes : clusterModes;
let xFormModal = {
formMap: [
{
key: 'logicalClusterName',
label: '逻辑集群中文名称',
// defaultValue:'',
rules: [{
required: true,
message: '请输入逻辑集群中文名称,支持中文、字母、数字、下划线(_)和短划线(-)组成长度在3-128字符之间', // 不能以下划线_和短划线(-)开头和结尾
pattern: /^[a-zA-Z0-9_\-\u4e00-\u9fa5]{3,128}$/g, //(?!(_|\-))(?!.*?(_|\-)$)
}],
attrs: {
// disabled: record ? true : false,
placeholder:'请输入逻辑集群中文名称'
},
},
{
key: 'logicalClusterName1',
label: '逻辑集群英文名称',
// defaultValue:'',
rules: [{
required: true,
message: '请输入逻辑集群英文名称,支持字母、数字、下划线(_)和短划线(-)组成长度在3-128字符之间', //不能以下划线_和短划线(-)开头和结尾
pattern:/^[a-zA-Z0-9_\-]{3,128}$/g, //(?!(_|\-))(?!.*?(_|\-)$)
}],
attrs: {
disabled: record ? true : false,
placeholder:'请输入逻辑集群英文名称,创建后无法修改'
},
},
{
key: 'mode',
@@ -136,8 +167,32 @@ export const showLogicalClusterOpModal = (clusterId: number, record?: ILogicalCl
};
}),
attrs: {
onChange(item: any) {
if (isShow && item == 0) {
updateFormModal(false);
isShow = false;
} else if (!isShow && (item == 1 || item == 2)) {
updateFormModal(true);
isShow = true;
}
},
},
},
// {
// key: 'appId',
// label: '所属应用',
// rules: [{ required: true , message: '请选择所属应用' }],
// type: 'select',
// options: app.adminAppData.map(item => {
// return {
// label: item.name,
// value: item.appId,
// };
// }),
// attrs: {
// placeholder: '请选择所属应用',
// },
// },
{
key: 'regionIdList',
label: 'RegionIdList',
@@ -149,10 +204,10 @@ export const showLogicalClusterOpModal = (clusterId: number, record?: ILogicalCl
value: item.id,
};
}),
rules: [{ required: true, message: '请选择BrokerIdList' }],
rules: [{ required: true, message: '请选择RegionIdList' }],
attrs: {
mode: 'multiple',
placeholder: '请选择BrokerIdList',
placeholder: '请选择RegionIdList',
},
},
{
@@ -169,7 +224,7 @@ export const showLogicalClusterOpModal = (clusterId: number, record?: ILogicalCl
],
formData: record,
visible: true,
title: '新增逻辑集群',
title: record ? '编辑逻辑集群' : '新增逻辑集群',
onSubmit: (value: INewLogical) => {
const params = {
appId: value.appId,
@@ -178,6 +233,7 @@ export const showLogicalClusterOpModal = (clusterId: number, record?: ILogicalCl
id: record ? record.logicalClusterId : '',
mode: value.mode,
name: value.logicalClusterName,
englishName:value.logicalClusterEName, // 存储逻辑集群英文名称
regionIdList: value.regionIdList,
} as INewLogical;
if (record) {
@@ -190,11 +246,12 @@ export const showLogicalClusterOpModal = (clusterId: number, record?: ILogicalCl
});
},
};
wrapper.open(xFormModal);
};
export const showClusterRegionOpModal = (clusterId: number, content: IMetaData, record?: IBrokersRegions) => {
const xFormModal = {
const xFormModal = {
formMap: [
{
key: 'name',
@@ -216,9 +273,9 @@ export const showClusterRegionOpModal = (clusterId: number, content: IMetaData,
key: 'brokerIdList',
label: 'Broker列表',
defaultValue: record ? record.brokerIdList.join(',') : [] as any,
rules: [{ required: true, message: '请输入BrokerIdList' }],
rules: [{ required: true, message: '请输入BrokerID,多个BrokerID用半角逗号分隔' }],
attrs: {
placeholder: '请输入BrokerIdList',
placeholder: '请输入BrokerID,多个BrokerID用半角逗号分隔',
},
},
{
@@ -274,5 +331,5 @@ export const showClusterRegionOpModal = (clusterId: number, content: IMetaData,
});
},
};
wrapper.open(xFormModal);
wrapper.open(xFormModal);
};

View File

@@ -0,0 +1,173 @@
import * as React from 'react';
import { admin } from 'store/admin';
import { notification, Modal, Form, Input, Switch, Select, Tooltip, Radio } from 'antd';
import { IBrokersMetadata, IBrokersRegions, IExpand } from 'types/base-type';
import { searchProps } from 'constants/table';
import { expandPartition } from 'lib/api';
const layout = {
labelCol: { span: 6 },
wrapperCol: { span: 15 },
};
interface IXFormProps {
form: any;
formData?: any;
visible?: boolean;
handleVisible?: any;
clusterId?: number;
deleteTopic?: any;
}
class CustomForm extends React.Component<IXFormProps> {
// public state = {
// checked: false,
// };
// public onSwitchChange(checked: boolean) {
// this.setState({ checked });
// this.props.form.validateFields((err: any, values: any) => {
// checked ? values.brokerIdList = [] : values.regionId = '';
// });
// }
public handleExpandOk() {
this.props.form.validateFields((err: any, values: any) => {
const deleteData = this.props.formData;
if (!err) {
// console.log('values', values);
if (values.topicName !== this.props.formData.topicName) {
notification.error({ message: 'topic名称不正确请重新输入' });
} else {
this.props.handleVisible(false);
// 调用删除接口
this.props.deleteTopic(deleteData);
}
}
// if (!err) {
// this.props.handleVisible(false);
// const params = {
// topicName: values.topicName,
// clusterId: this.props.clusterId,
// partitionNum: values.partitionNum,
// } as IExpand;
// if (values.brokerIdList) {
// params.brokerIdList = values.brokerIdList;
// } else {
// params.regionId = values.regionId;
// }
// const valueParams = [] as IExpand[];
// valueParams.push(params);
// expandPartition(valueParams).then(data => {
// notification.success({ message: '扩分成功' });
// this.props.form.resetFields();
// admin.getClusterTopics(this.props.clusterId);
// });
});
}
public handleExpandCancel() {
this.props.handleVisible(false);
this.props.form.resetFields();
}
public componentDidMount() {
admin.getBrokersMetadata(this.props.clusterId);
admin.getBrokersRegions(this.props.clusterId);
}
public render() {
// console.log('props', this.props);
const { formData = {} as any, visible } = this.props;
const { getFieldDecorator } = this.props.form;
let metadata = [] as IBrokersMetadata[];
metadata = admin.brokersMetadata ? admin.brokersMetadata : metadata;
let regions = [] as IBrokersRegions[];
regions = admin.brokersRegions ? admin.brokersRegions : regions;
return (
<Modal
title="提示"
visible={visible}
onOk={() => this.handleExpandOk()}
onCancel={() => this.handleExpandCancel()}
maskClosable={false}
okText="确认"
cancelText="取消"
>
<Form {...layout} name="basic" onSubmit={() => ({})} >
{/* <Form.Item label="Topic名称" >
{getFieldDecorator('topicName', {
initialValue: formData.topicName,
rules: [{ required: true, message: '请输入Topic名称' }],
})(<Input disabled={true} placeholder="请输入Topic名称" />)}
</Form.Item> */}
{/* 运维管控-topic信息-扩分区操作 */}
{/* <Form.Item label="所属region" >
{getFieldDecorator('topicName', {
initialValue: formData.topicName,
rules: [{ required: true, message: '请输入所属region' }],
})(<Input disabled={true} placeholder="请输入所属region" />)}
</Form.Item> */}
{/* 运维管控-topic信息-扩分区操作 */}
<div style={{ textAlign: 'center', padding: '10px 0', fontSize: '13px', fontWeight: 'bold', color: 'red' }}>
<span>Topic是高危操作,Topic名称进行确认</span>
</div>
<Form.Item label="Topic名称" >
{getFieldDecorator('topicName', {
rules: [{
required: true,
message: 'topic名称错误',
}],
})(<Input />)}
</Form.Item>
{/* <Form.Item label="brokerIdList" style={{ display: this.state.checked ? 'none' : '' }}>
{getFieldDecorator('brokerIdList', {
initialValue: formData.brokerIdList,
rules: [{ required: !this.state.checked, message: '请输入brokerIdList' }],
})(
<Select
mode="multiple"
{...searchProps}
>
{metadata.map((v, index) => (
<Select.Option
key={v.brokerId || v.key || index}
value={v.brokerId}
>
{v.host.length > 16 ?
<Tooltip placement="bottomLeft" title={v.host}> {v.host} </Tooltip>
: v.host}
</Select.Option>
))}
</Select>,
)}
</Form.Item>*/}
{/* <Form.Item label="regionId" style={{ display: this.state.checked ? '' : 'none' }} >
{getFieldDecorator('regionId', {
initialValue: formData.regionId,
rules: [{ required: this.state.checked, message: '请选择regionId' }],
})(
<Select {...searchProps}>
{regions.map((v, index) => (
<Select.Option
key={v.id || v.key || index}
value={v.id}
>
{v.name.length > 16 ?
<Tooltip placement="bottomLeft" title={v.name}> {v.name} </Tooltip>
: v.name}
</Select.Option>
))}
</Select>,
)}
</Form.Item> */}
</Form>
</Modal>
);
}
}
export const ConfirmDetailTopicFormWrapper = Form.create<IXFormProps>()(CustomForm);

View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import { admin } from 'store/admin';
import { notification, Modal, Form, Input, Switch, Select, Tooltip } from 'antd';
import { notification, Modal, Form, Input, Switch, Select, Tooltip, Radio } from 'antd';
import { IBrokersMetadata, IBrokersRegions, IExpand } from 'types/base-type';
import { searchProps } from 'constants/table';
import { expandPartition } from 'lib/api';
@@ -89,14 +89,31 @@ class CustomForm extends React.Component<IXFormProps> {
rules: [{ required: true, message: '请输入Topic名称' }],
})(<Input disabled={true} placeholder="请输入Topic名称" />)}
</Form.Item>
{/* 运维管控-topic信息-扩分区操作 */}
<Form.Item label="所属region" >
{getFieldDecorator('regionNameList', {
initialValue: admin.topicsBasic ? admin.topicsBasic.regionNameList : '',
rules: [{ required: true, message: '请输入所属region' }],
})(<Input disabled={true} />)}
</Form.Item>
{/* 运维管控-topic信息-扩分区操作 */}
<Form.Item label="分区数" >
{getFieldDecorator('partitionNum', {
rules: [{ required: true,
message: '请输入分区数' }],
rules: [{
required: true,
message: '请输入分区数',
}],
})(<Input placeholder="请输入分区数" />)}
</Form.Item>
<Form.Item label={this.state.checked ? 'Region类型' : 'Borker类型'} >
<Switch onChange={(checked) => this.onSwitchChange(checked)} />
<Form.Item label="类型">
{/* <Form.Item label={this.state.checked ? 'Region类型' : 'Borker类型'} > */}
{/* <Switch onChange={(checked) => this.onSwitchChange(checked)} /> */}
<Radio.Group value={this.state.checked ? 'region' : 'broker'} onChange={(e) => { this.onSwitchChange(e.target.value === 'region' ? true : false); }}>
<Radio.Button value="region">Region类型</Radio.Button>
<Radio.Button value="broker">Borker类型</Radio.Button>
</Radio.Group>
</Form.Item>
<Form.Item label="brokerIdList" style={{ display: this.state.checked ? 'none' : '' }}>
{getFieldDecorator('brokerIdList', {
@@ -107,14 +124,14 @@ class CustomForm extends React.Component<IXFormProps> {
mode="multiple"
{...searchProps}
>
{ metadata.map((v, index) => (
{metadata.map((v, index) => (
<Select.Option
key={v.brokerId || v.key || index}
value={v.brokerId}
>
{v.host.length > 16 ?
<Tooltip placement="bottomLeft" title={v.host}> {v.host} </Tooltip>
: v.host}
{v.host.length > 16 ?
<Tooltip placement="bottomLeft" title={v.host}> {v.host} </Tooltip>
: v.host}
</Select.Option>
))}
</Select>,
@@ -127,7 +144,7 @@ class CustomForm extends React.Component<IXFormProps> {
rules: [{ required: this.state.checked, message: '请选择regionId' }],
})(
<Select {...searchProps}>
{ regions.map((v, index) => (
{regions.map((v, index) => (
<Select.Option
key={v.id || v.key || index}
value={v.id}

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import { Table, notification, Button, Modal, Input, Form, Select, message, Tooltip } from 'component/antd';
import { Table, notification, Button, Modal, Input, Form, Select, message, Tooltip, Icon, Spin } from 'component/antd';
import { IBrokersMetadata, IRebalance } from 'types/base-type';
import { admin } from 'store/admin';
import { implementRegions, rebalanceStatus } from 'lib/api';
@@ -27,6 +27,7 @@ class LeaderRebalanceModal extends React.Component<IXFormProps> {
public state = {
imVisible: false,
status: '',
isExecutionBtn: false
};
public handleRebalanceCancel() {
@@ -48,6 +49,7 @@ class LeaderRebalanceModal extends React.Component<IXFormProps> {
public handleSubmit = (e: any) => {
e.preventDefault();
this.props.form.validateFields((err: any, values: any) => {
values.brokerId && this.setState({ isExecutionBtn: true })
if (!err) {
let params = {} as IRebalance;
params = {
@@ -58,12 +60,19 @@ class LeaderRebalanceModal extends React.Component<IXFormProps> {
topicName: '',
};
implementRegions(params).then(data => {
message.success('获取成功');
// message.success('获取成功');
this.getStatus();
this.setState({
imVisible: true,
isExecutionBtn: false
});
});
}).catch((err) => {
message.error('获取失败')
this.setState({
imVisible: true,
isExecutionBtn: false
});
})
}
});
}
@@ -77,7 +86,7 @@ class LeaderRebalanceModal extends React.Component<IXFormProps> {
public getStatus() {
rebalanceStatus(this.props.clusterId).then((data: any) => {
message.success('状态更新成功');
// message.success('状态更新成功');
if (data.code === 30) { // code -1 未知 101 成功 30 运行中
setTimeout(this.iTimer, 0);
} else {
@@ -91,6 +100,8 @@ class LeaderRebalanceModal extends React.Component<IXFormProps> {
public componentWillUnmount() {
clearInterval(this.timer);
}
// 执行加载图标
public antIcon = <Icon type="loading" style={{ fontSize: 12, color: '#cccccc' }} spin />
public render() {
const { visible } = this.props;
@@ -140,21 +151,21 @@ class LeaderRebalanceModal extends React.Component<IXFormProps> {
{getFieldDecorator('brokerId', {
rules: [{ required: true, message: '请输入Broker' }],
})(
<Select
onChange={(value: number) => this.onMetaChange(value)}
{...searchProps}
>
{this.metadata.map((v, index) => (
<Select.Option
key={v.brokerId || v.key || index}
value={v.brokerId}
>
{v.host.length > 16 ?
<Tooltip placement="bottomLeft" title={v.host}> {v.host} </Tooltip>
: v.host}
</Select.Option>
))}
</Select>)}
<Select
onChange={(value: number) => this.onMetaChange(value)}
{...searchProps}
>
{this.metadata.map((v, index) => (
<Select.Option
key={v.brokerId || v.key || index}
value={v.brokerId}
>
{v.host.length > 16 ?
<Tooltip placement="bottomLeft" title={v.host}> {v.host} </Tooltip>
: v.host}
</Select.Option>
))}
</Select>)}
</Form.Item>
<Form.Item label="" >
{getFieldDecorator('submit')(
@@ -162,8 +173,9 @@ class LeaderRebalanceModal extends React.Component<IXFormProps> {
htmlType="submit"
type="primary"
className="implement-button"
disabled={this.state.isExecutionBtn}
>
{this.state.isExecutionBtn ? (<span><Spin indicator={this.antIcon} size="small" /></span>) : '执行'}
</Button>,
)}
</Form.Item>

View File

@@ -19,42 +19,42 @@ export const startMigrationTask = (item: IReassignTasks, action: string) => {
};
export const modifyMigrationTask = (item: IReassignTasks, action: string) => {
const status: number = item.status;
const xFormModal = {
formMap: [
{
key: 'beginTime',
label: '计划开始时间',
type: 'date_picker',
rules: [{
required: status === 0,
message: '请输入计划开始时间',
}],
attrs: {
placeholder: '请输入计划开始时间',
format: timeFormat,
showTime: true,
disabled: status !== 0,
},
const status: number = item.status;
const xFormModal = {
formMap: [
{
key: 'beginTime',
label: '计划开始时间',
type: 'date_picker',
rules: [{
required: status === 0,
message: '请输入计划开始时间',
}],
attrs: {
placeholder: '请输入计划开始时间',
format: timeFormat,
showTime: true,
disabled: status !== 0,
},
],
formData: {
beginTime: moment(item.beginTime),
},
visible: true,
title: '操作迁移任务',
onSubmit: (value: IExecute) => {
const params = {
action,
beginTime: +moment(value.beginTime).format('x'),
taskId: item.taskId,
} as IExecute;
expert.getExecuteTask(params).then(data => {
notification.success({ message: '操作成功' });
});
},
};
wrapper.open(xFormModal);
],
formData: {
beginTime: moment(item.beginTime),
},
visible: true,
title: '操作迁移任务',
onSubmit: (value: IExecute) => {
const params = {
action,
beginTime: +moment(value.beginTime).format('x'),
taskId: item.taskId,
} as IExecute;
expert.getExecuteTask(params).then(data => {
notification.success({ message: '操作成功' });
});
},
};
wrapper.open(xFormModal);
};
export const modifyTransferTask = (item: IReassign, action: string, taskId: number) => {
@@ -287,6 +287,7 @@ export const addMigrationTask = () => {
formData: {},
visible: true,
title: '新建集群任务',
isWaitting: true,
onSubmit: (value: INewBulidEnums) => {
value.kafkaPackageName = value.kafkafileNameMd5.split(',')[0];
value.kafkaPackageMd5 = value.kafkafileNameMd5.split(',')[1];
@@ -294,10 +295,21 @@ export const addMigrationTask = () => {
value.serverPropertiesMd5 = value.serverfileNameMd5.split(',')[1];
delete value.kafkafileNameMd5;
delete value.serverfileNameMd5;
admin.addMigrationTask(value).then(data => {
return admin.addMigrationTask(value).then(data => {
notification.success({ message: '新建集群任务成功' });
});
},
onSubmitFaild: (err: any, ref: any, formData: any, formMap: any) => {
if (err.message === '主机列表错误,请检查主机列表') {
const hostList = ref.getFieldValue('hostList');
ref.setFields({
hostList: {
value: hostList,
errors: [new Error('主机列表错误,请检查主机列表')],
}
})
}
}
};
wrapper.open(xFormModal);
};

View File

@@ -12,12 +12,12 @@ const updateFormModal = (topicName?: string) => {
const formMap = wrapper.xFormWrapper.formMap;
const formData = wrapper.xFormWrapper.formData;
if (topicName) {
formMap[5].options = expert.partitionIdMap[topicName]; // 3
formMap[2].options = expert.partitionIdMap[topicName]; // 3
formData.originalRetentionTime = transMSecondToHour(admin.topicsBasic.retentionTime);
} else {
formMap[1].options = expert.taskTopicMetadata;
formMap[3].options = admin.brokersMetadata; // 2
formMap[4].options = admin.brokersRegions;
formMap[4].options = admin.brokersMetadata; // 2
formMap[5].options = admin.brokersRegions;
}
// tslint:disable-next-line:no-unused-expression
wrapper.ref && wrapper.ref.updateFormMap$(formMap, wrapper.xFormWrapper.formData, !!topicName, ['partitionIdList']);
@@ -25,11 +25,11 @@ const updateFormModal = (topicName?: string) => {
const updateInputModal = (status?: string) => {
const formMap = wrapper.xFormWrapper.formMap;
formMap[3].invisible = status === 'region';
formMap[4].invisible = status !== 'region';
formMap[4].invisible = status === 'region';
formMap[5].invisible = status !== 'region';
formMap[3].rules = [{required: status !== 'region'}];
formMap[4].rules = [{required: status === 'region'}];
formMap[4].rules = [{required: status !== 'region'}];
formMap[5].rules = [{required: status === 'region'}];
// tslint:disable-next-line:no-unused-expression
wrapper.ref && wrapper.ref.updateFormMap$(formMap, wrapper.xFormWrapper.formData);
};
@@ -81,6 +81,19 @@ export const createMigrationTasks = () => {
},
},
},
{
key: 'partitionIdList',
label: '分区ID',
type: 'select',
defaultValue: [] as any,
rules: [{
required: false,
}],
attrs: {
mode: 'tags',
placeholder: '请选择PartitionIdList',
},
},
{
key: 'species',
label: '类型',
@@ -110,10 +123,10 @@ export const createMigrationTasks = () => {
defaultValue: [] as any,
invisible: false,
options: admin.brokersMetadata,
rules: [{ required: true, message: '请选择Broker' }],
rules: [{ required: true, message: '请选择目标Broker,Broker数量需大于等于副本数量' }],
attrs: {
mode: 'multiple',
placeholder: '请选择Broker',
placeholder: '请选择目标Broker,Broker数量需大于等于副本数量',
},
},
{
@@ -123,24 +136,12 @@ export const createMigrationTasks = () => {
defaultValue: [] as any,
invisible: true,
options: admin.brokersRegions,
rules: [{ required: false, message: '请选择Region' }],
rules: [{ required: false, message: '请选择目标Region' }],
attrs: {
placeholder: '请选择Region',
},
},
{
key: 'partitionIdList',
label: '分区ID',
type: 'select',
defaultValue: [] as any,
rules: [{
required: false,
}],
attrs: {
mode: 'tags',
placeholder: '请选择PartitionIdList',
placeholder: '请选择目标Region',
},
},
{
key: 'beginTime',
label: '计划开始时间',

View File

@@ -12,6 +12,9 @@ export const showApplyModal = (record?: IUser) => {
key: 'username',
label: '用户名',
rules: [{ required: true, message: '请输入用户名' }],
attrs: {
disabled: record ? true : false
}
}, {
key: 'role',
label: '角色',

View File

@@ -1,10 +1,15 @@
import * as React from 'react';
import { notification } from 'component/antd';
import { IUploadFile, IConfigure } from 'types/base-type';
import { version } from 'store/version';
import { admin } from 'store/admin';
import { wrapper } from 'store';
import { computeChecksumMd5 } from 'lib/utils';
import format2json from 'format-to-json';
interface ISearchAndFilterState {
[filter: string]: boolean | string | number | any[];
}
const handleSelectChange = (e: number) => {
version.setAcceptFileType(e);
updateFormModal(e);
@@ -150,7 +155,11 @@ export const showModifyModal = (record: IUploadFile) => {
wrapper.open(xFormModal);
};
export const showConfigureModal = (record?: IConfigure) => {
export const showConfigureModal = async (record?: IConfigure) => {
if (record) {
const result:any = await format2json(record.configValue);
record.configValue = result.result;
}
const xFormModal = {
formMap: [
{
@@ -163,8 +172,11 @@ export const showConfigureModal = (record?: IConfigure) => {
}, {
key: 'configValue',
label: '配置值',
type: 'text_area',
rules: [{ required: true, message: '请输入配置值' }],
type: 'monaco_editor',
rules: [{
required: true,
message: '请输入配置值',
}],
}, {
key: 'configDescription',
label: '备注',

View File

@@ -28,8 +28,8 @@ export const showEditModal = (record?: IAppItem, from?: string, isDisabled?: boo
defaultValue: record && record.name || '',
rules: [{
required: isDisabled ? false : true,
message: '请输入不得超过64字符',
pattern: /^.{1,64}$/,
message: '应用名称只支持中文、字母、数字、下划线、短划线长度限制在3-64字符',
pattern: /[\u4e00-\u9fa5_a-zA-Z0-9_-]{3,64}/,
}],
attrs: { disabled: isDisabled },
}, {
@@ -45,11 +45,11 @@ export const showEditModal = (record?: IAppItem, from?: string, isDisabled?: boo
attrs: {
disabled: true,
suffix: (
<Icon
onClick={() => copyString(record.password)}
type="copy"
className="icon-color"
/>),
<Icon
onClick={() => copyString(record.password)}
type="copy"
className="icon-color"
/>),
},
}, {
key: 'idc',
@@ -64,7 +64,7 @@ export const showEditModal = (record?: IAppItem, from?: string, isDisabled?: boo
key: 'principalList',
label: '负责人',
type: 'custom',
customFormItem: <StaffSelect isDisabled={isDisabled}/>,
customFormItem: <StaffSelect isDisabled={isDisabled} />,
rules: [{
required: isDisabled ? false : true,
message: '请选择负责人(至少两人)',
@@ -74,18 +74,20 @@ export const showEditModal = (record?: IAppItem, from?: string, isDisabled?: boo
}
return true;
},
}],
}],
}, {
key: 'description',
label: '应用描述',
type: 'text_area',
rules: [{ required: isDisabled ? false : true, message: '请输入描述'}],
rules: [{ required: isDisabled ? false : true, message: '请输入描述' }],
attrs: { disabled: isDisabled },
},
],
formData: record,
visible: true,
title: `${isDisabled ? '详情' : record ? '编辑' : '应用申请'}`,
title: isDisabled ? '详情' : record ? '编辑' : <div><span></span><a className='applicationDocument' href="###" target='_blank'></a></div>,
// customRenderElement: isDisabled ? '' : record ? '' : <span className="tips">集群资源充足时预计1分钟自动审批通过</span>,
isWaitting: true,
onSubmit: (value: IAppItem) => {
if (isDisabled) {
return;
@@ -98,7 +100,18 @@ export const showEditModal = (record?: IAppItem, from?: string, isDisabled?: boo
}
});
},
};
onSubmitFaild: (err: any, ref: any, formData: any, formMap: any) => {
if (err.message == '资源已经存在') {
const topic = ref.getFieldValue('name');
ref.setFields({
name: {
value: topic,
errors: [new Error('该应用名称已存在')],
}
})
}
}
};
wrapper.open(xFormModal);
};
@@ -116,7 +129,7 @@ const operateApp = (isEdit: boolean, value: IAppItem, record?: IAppItem, from?:
params.extensions = JSON.stringify({ principals, idc: value.idc, name: value.name });
let modifyParams = {};
if (isEdit) {
modifyParams = {
modifyParams = {
appId: record.appId,
description: value.description,
name: value.name,

View File

@@ -116,11 +116,11 @@ export class CancelTopicPermission extends React.Component {
type: 'check_box',
defaultValue: accessStatus,
options: [{
label: '消费权限',
label: '取消消费权限',
value: '1',
disabled: send,
}, {
label: '发送权限',
label: '取消发送权限',
value: '2',
disabled: consume,
}],

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import { Table, Modal, Tooltip, Icon, message, notification } from 'component/antd';
import { Table, Modal, Tooltip, Icon, message, notification, Alert } from 'component/antd';
import { getApplyOnlineColumns } from 'container/topic/config';
import { observer } from 'mobx-react';
import { modal } from 'store/modal';
@@ -59,7 +59,7 @@ export class ConnectTopicList extends React.Component {
maskClosable={false}
onCancel={this.handleCancel}
onOk={this.handleSubmit}
okText="下线"
okText="确认"
cancelText="取消"
okButtonProps={{ disabled: topic.connectLoading || !!topic.connectionInfo.length }}
width={700}
@@ -74,6 +74,7 @@ export class ConnectTopicList extends React.Component {
pagination={false}
bordered={true}
/>
<Alert message="如若有连接信息,则表示资源正处于使用中,禁止下线操作。如需下线烦请关闭连接信息中的Kafka发送/消费客户端后再进行下线。" type="error" showIcon />
</Modal>
</>
);

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import { Table, Modal, Tooltip, Icon, message, notification } from 'component/antd';
import { Table, Modal, Tooltip, Icon, message, notification, Alert } from 'component/antd';
import { app } from 'store/app';
import { getApplyOnlineColumns } from 'container/topic/config';
import { observer } from 'mobx-react';
@@ -55,7 +55,7 @@ export class ConnectAppList extends React.Component {
maskClosable={false}
onCancel={this.handleCancel}
onOk={this.handleSubmit}
okText="下线"
okText="确认"
cancelText="取消"
okButtonProps={{ disabled: app.connectLoading || !!app.appsConnections.length }}
width={700}
@@ -70,6 +70,7 @@ export class ConnectAppList extends React.Component {
pagination={false}
bordered={true}
/>
<Alert message="如若有连接信息,则表示资源正处于使用中,禁止下线操作。如需下线烦请关闭连接信息中的Kafka发送/消费客户端后再进行下线。" type="error" showIcon />
</Modal>
</>
);

View File

@@ -74,7 +74,7 @@ export class OfflineClusterModal extends React.Component {
maskClosable={false}
onCancel={this.handleCancel}
onOk={this.handleSubmit}
okText="下线"
okText="确认"
cancelText="取消"
okButtonProps={{ disabled: cluster.filterLoading || !!cluster.clusterMetaTopics.length }}
width={700}

View File

@@ -57,25 +57,26 @@ export const showApprovalModal = (info: IOrderInfo, status: number, from?: strin
}],
}, {
key: 'retentionTime',
label: '保存时间',
defaultValue: '48',
type: 'select',
options: [{
label: '12小时',
value: '12',
}, {
label: '24小时',
value: '24',
}, {
label: '48小时',
value: '48',
}, {
label: '72小时',
value: '72',
}],
label: '保存时间/小时)',
defaultValue: '12',
type: 'input_number',
// options: [{
// label: '12小时',
// value: '12',
// }, {
// label: '24小时',
// value: '24',
// }, {
// label: '48小时',
// value: '48',
// }, {
// label: '72小时',
// value: '72',
// }],
rules: [{
required: true,
message: '请选择',
message: '请输入大于12小于999的整数',
pattern: /^([1-9]{1}[0-9]{2})$|^([2-9]{1}[0-9]{1})$|^(1[2-9]{1})$/,
}],
}, {
key: 'species',
@@ -242,11 +243,15 @@ export const showApprovalModal = (info: IOrderInfo, status: number, from?: strin
if ((type === 2 || type === 12) && status === 1) { // 通过配额 12分区
xFormWrapper.formMap.splice(1, 0, ...quotaFormMap);
}
wrapper.open(xFormWrapper);
};
export const renderOrderOpModal = (selectedRowKeys: IBaseOrder[], status: number) => {
export const renderOrderOpModal = (selectedRowKeys: IBaseOrder[], status: number, rowsCallBack?: any) => {
if (modal.actionAfterClose === 'close') {
// tslint:disable-next-line: no-unused-expression
rowsCallBack && rowsCallBack.onChange([], []);
order.setSelectedRows([]);
}
const orderIdList = selectedRowKeys.map((ele: IBaseOrder) => {
return ele.id;
});
@@ -283,6 +288,7 @@ export const renderOrderOpModal = (selectedRowKeys: IBaseOrder[], status: number
order.batchApprovalOrders(params).then(data => {
modal.setAction('close');
modal.showOrderOpResult();
order.setSelectedRows();
});
},
};
@@ -293,6 +299,7 @@ export const RenderOrderOpResult = () => {
const handleOk = () => {
order.getApplyOrderList(0);
order.getApprovalList(0);
order.setSelectedRows();
modal.close();
};
@@ -309,7 +316,7 @@ export const RenderOrderOpResult = () => {
render: (t: number) => {
return (
<span className={t === 0 ? 'success' : 'fail'}>
{t === 0 ? '成功' : '失败'}
{t === 0 ? '成功' : '失败'}
</span>
);
},
@@ -327,7 +334,7 @@ export const RenderOrderOpResult = () => {
render: (text: string) => {
return (
<Tooltip placement="bottomLeft" title={text} >
{text}
{text}
</Tooltip>);
},
},

View File

@@ -59,7 +59,7 @@ export class RenderOrderOpResult extends React.Component {
maskClosable={false}
onCancel={this.handleCancel}
onOk={this.handleSubmit}
okText="下线"
okText="确认"
cancelText="取消"
okButtonProps={{ disabled: topic.connectLoading || !!topic.connectionInfo.length }}
width={700}

View File

@@ -22,7 +22,7 @@ export const applyTopic = () => {
formMap: [
{
key: 'clusterId',
label: '所属集群:',
label: '所属逻辑集群:',
type: 'select',
options: cluster.clusterData,
rules: [{ required: true, message: '请选择' }],
@@ -36,8 +36,8 @@ export const applyTopic = () => {
addonBefore: region.currentRegion === 'us' || region.currentRegion === 'ru' ? `${region.currentRegion}01_` : '',
},
rules: [
{ required: true },
{
required: true,
pattern: /^[-\w]{3,128}$/,
message: '只能包含字母、数字、下划线_和短划线(-),长度限制在3-128字符之间',
},
@@ -90,6 +90,8 @@ export const applyTopic = () => {
visible: true,
title: '申请Topic',
okText: '确认',
// customRenderElement: <span className="tips">集群资源充足时预计1分钟自动审批通过</span>,
isWaitting: true,
onSubmit: (value: any) => {
value.topicName = region.currentRegion === 'us' || region.currentRegion === 'ru' ?
`${region.currentRegion}01_` + value.topicName : value.topicName;
@@ -102,11 +104,21 @@ export const applyTopic = () => {
description: value.description,
extensions: JSON.stringify(params),
};
topic.applyTopic(quotaParams).then(data => {
notification.success({ message: '申请Topic成功' });
return topic.applyTopic(quotaParams).then(data => {
window.location.href = `${urlPrefix}/user/order-detail/?orderId=${data.id}&region=${region.currentRegion}`;
});
})
},
onSubmitFaild: (err: any, ref: any, formData: any, formMap: any) => {
if (err.message === 'topic already existed') {
const topic = ref.getFieldValue('topicName');
ref.setFields({
topicName: {
value: topic,
errors: [new Error('该topic名称已存在')],
}
})
}
}
};
wrapper.open(xFormModal);
};
@@ -170,7 +182,7 @@ export const showApplyQuatoModal = (item: ITopic | IAppsIdInfo, record: IQuotaQu
formMap: [
{
key: 'clusterName',
label: '集群名称',
label: '逻辑集群名称',
rules: [{ required: true, message: '' }],
attrs: { disabled: true },
invisible: !item.hasOwnProperty('clusterName'),
@@ -187,7 +199,7 @@ export const showApplyQuatoModal = (item: ITopic | IAppsIdInfo, record: IQuotaQu
attrs: { disabled: true },
}, {
key: 'produceQuota',
label: '发送数据速率',
label: '申请发送数据速率',
attrs: {
disabled: isProduce,
placeholder: '请输入',
@@ -199,7 +211,7 @@ export const showApplyQuatoModal = (item: ITopic | IAppsIdInfo, record: IQuotaQu
}],
}, {
key: 'consumeQuota',
label: '消费数据速率',
label: '申请消费数据速率',
attrs: {
disabled: isConsume,
placeholder: '请输入',
@@ -282,10 +294,10 @@ export const showTopicApplyQuatoModal = (item: ITopic) => {
formMap: [
{
key: 'clusterName',
label: '集群名称',
label: '逻辑集群名称',
rules: [{ required: true, message: '' }],
attrs: { disabled: true },
invisible: !item.hasOwnProperty('clusterName'),
// invisible: !item.hasOwnProperty('clusterName'),
}, {
key: 'topicName',
label: 'Topic名称',
@@ -318,7 +330,7 @@ export const showTopicApplyQuatoModal = (item: ITopic) => {
},
}, { // 0 无权限 1可读 2可写 3 可读写 4可读写可管理
key: 'produceQuota',
label: '发送数据速率',
label: '申请发送数据速率',
attrs: {
disabled: false,
placeholder: '请输入',
@@ -330,7 +342,7 @@ export const showTopicApplyQuatoModal = (item: ITopic) => {
}],
}, {
key: 'consumeQuota',
label: '消费数据速率',
label: '申请消费数据速率',
attrs: {
disabled: false,
placeholder: '请输入',
@@ -704,14 +716,14 @@ export const applyExpandModal = (item: ITopic) => {
rules: [{ required: true }],
}, {
key: 'needIncrPartitionNum',
label: '分区',
label: '申请增加分区数量',
type: 'input_number',
rules: [{
required: true,
message: '请输入0-100正整数',
pattern: /^((?!0)\d{1,2}|100)$/,
message: '请输入0-1000正整数',
pattern: /^((?!0)\d{1,3}|1000)$/,
}],
attrs: { placeholder: '0-100正整数' },
attrs: { placeholder: '0-1000正整数' },
renderExtraElement: () => <div className="form-tip mr--10">3MB/s一个</div>,
}, {
key: 'description',

View File

@@ -55,7 +55,7 @@ export class NetWorkFlow extends React.Component<any> {
<div className="chart-box-0">
<div className="chart-title">
<span className="action-button"></span>
<a href={indexUrl} target="_blank"></a>
<a href={indexUrl.indexUrl} target="_blank"></a>
</div>
<Divider />
<ChartWithDatePicker
@@ -70,18 +70,18 @@ export class NetWorkFlow extends React.Component<any> {
export const renderTrafficTable = (updateRealStatus: any, Element: React.ComponentClass) => {
return (
<div className="traffic-table">
<div className="traffic-header">
<span>
<span className="action-button"></span>
<a href={indexUrl} target="_blank"></a>
</span>
<span className="k-abs" onClick={updateRealStatus}>
<i className="k-icon-shuaxin didi-theme mr-5" />
<a></a>
</span>
</div>
<Element />
<div className="traffic-table">
<div className="traffic-header">
<span>
<span className="action-button"></span>
<a href={indexUrl.indexUrl} target="_blank"></a>
</span>
<span className="k-abs" onClick={updateRealStatus}>
<i className="k-icon-shuaxin didi-theme mr-5" />
<a></a>
</span>
</div>
<Element />
</div>
);
};

View File

@@ -18,7 +18,7 @@ interface IFilterParams {
}
interface ISearchAndFilterState {
[filter: string]: boolean | string | number;
[filter: string]: boolean | string | number | any[];
}
export class SearchAndFilterContainer extends React.Component<any, ISearchAndFilterState> {
@@ -88,14 +88,14 @@ export class SearchAndFilterContainer extends React.Component<any, ISearchAndFil
{...searchProps}
>
{cluster.selectAllData.map((d, index) =>
<Select.Option
value={d.value}
key={index}
>
{d.label.length > 16 ?
<Tooltip placement="bottomLeft" title={d.label}>{d.label}</Tooltip>
: d.label}
</Select.Option>)}
<Select.Option
value={d.value}
key={index}
>
{d.label.length > 16 ?
<Tooltip placement="bottomLeft" title={d.label}>{d.label}</Tooltip>
: d.label}
</Select.Option>)}
</Select>
</li>
);
@@ -113,14 +113,14 @@ export class SearchAndFilterContainer extends React.Component<any, ISearchAndFil
{...searchProps}
>
{expert.metaData.map((d, index) =>
<Select.Option
value={d.clusterId}
key={index}
>
{d.clusterName.length > 16 ?
<Tooltip placement="bottomLeft" title={d.clusterName}>{d.clusterName}</Tooltip>
: d.clusterName}
</Select.Option>)}
<Select.Option
value={d.clusterId}
key={index}
>
{d.clusterName.length > 16 ?
<Tooltip placement="bottomLeft" title={d.clusterName}>{d.clusterName}</Tooltip>
: d.clusterName}
</Select.Option>)}
</Select>
</li>
);

View File

@@ -28,7 +28,7 @@ export const renderMyTopicOperation = (record: ITopic) => {
item.clickFunc ? <a type="javascript;" key={index} onClick={() => item.clickFunc(record)}>{item.label}</a> :
<span key={index} className="mr-10">{item.label}</span>
))}
{getTopicBtn(record).length > 2 && <MoreBtns btns={leftBtns} data={record}/>}
{getTopicBtn(record).length > 2 && <MoreBtns btns={leftBtns} data={record} />}
</span>
</>);
};
@@ -72,7 +72,7 @@ const getTopicBtn = (record: ITopic) => {
export const renderAllTopicOperation = (item: ITopic) => {
return (
<>
{item.needAuth && <a className="mr-10" onClick={() => showAllPermissionModal(item)}></a>}
{item.needAuth && <a className="mr-10" onClick={() => showAllPermissionModal(item)}></a>}
</>
);
};
@@ -85,6 +85,7 @@ export const applyQuotaQuery = (item: ITopic) => {
};
export const applyTopicQuotaQuery = async (item: ITopic) => {
console.log(item)
await app.getTopicAppQuota(item.clusterId, item.topicName);
await showTopicApplyQuatoModal(item);
};
@@ -141,10 +142,8 @@ export const getAllTopicColumns = (urlPrefix: string) => {
<Tooltip placement="bottomLeft" title={record.topicName} >
<a
// tslint:disable-next-line:max-line-length
href={`${urlPrefix}/topic/topic-detail?clusterId=${record.clusterId}&topic=${record.topicName}&region=${region.currentRegion}`}
>
{text}
</a>
href={`${urlPrefix}/topic/topic-detail?clusterId=${record.clusterId}&topic=${record.topicName}&region=${region.currentRegion}&needAuth=${record.needAuth}&clusterName=${record.clusterName}`}
>{text}</a>
</Tooltip>);
},
}, {
@@ -276,7 +275,7 @@ export const getMyTopicColumns = (urlPrefix: string) => {
render: (text: string, record: ITopic) => (
<>
<Tooltip placement="bottomLeft" title={record.appId} >
{text}
{text}
</Tooltip>
</>
),

View File

@@ -1,6 +1,7 @@
import { Input } from 'component/antd';
import { region } from 'store';
import * as React from 'react';
import { indexUrl } from 'constants/strategy';
interface IPeakFlowProps {
value?: any;
@@ -23,7 +24,7 @@ export class PeakFlowInput extends React.Component<IPeakFlowProps> {
{region.currentRegion === 'cn' ? value * 40 : value * 45}/
<a
// tslint:disable-next-line:max-line-length
href="https://github.com/didi/kafka-manager"
href={indexUrl.indexUrl}
target="_blank"
>kafka计价方式
</a>

View File

@@ -15,7 +15,7 @@ import 'styles/table-filter.less';
@observer
export class AllTopic extends SearchAndFilterContainer {
public state = {
searchKey: '',
searchKey: ''
};
public componentDidMount() {

View File

@@ -3,13 +3,16 @@ import './index.less';
import Url from 'lib/url-parser';
import { observer } from 'mobx-react';
import { topic, IAppsIdInfo } from 'store/topic';
import { ITopic } from 'types/base-type';
import { Table, Tooltip } from 'component/antd';
import { SearchAndFilterContainer } from 'container/search-filter';
import { IQuotaQuery } from 'types/base-type';
import { showApplyQuatoModal } from 'container/modal';
import { pagination, cellStyle } from 'constants/table';
import { transBToMB } from 'lib/utils';
import { topicStatusMap } from 'constants/status-map';
import { tableFilter } from 'lib/utils';
import { users } from 'store/users';
@observer
export class AppIdInformation extends SearchAndFilterContainer {
public clusterId: number;
@@ -26,7 +29,20 @@ export class AppIdInformation extends SearchAndFilterContainer {
this.topicName = url.search.topic;
}
public renderColumns = () => {
public renderColumns = (data: any) => {
const statusColumn = Object.assign({
title: '权限',
dataIndex: 'access',
key: 'access',
filters: tableFilter<ITopic>(data, 'access', topicStatusMap),
onFilter: (text: number, record: ITopic) => record.access === text,
render: (val: number) => (
<div className={val === 0 ? '' : 'success'}>
{topicStatusMap[val] || ''}
</div>
),
}, this.renderColumnsFilter('filterStatus')) as any;
return [{
title: '应用Id',
key: 'appId',
@@ -53,7 +69,9 @@ export class AppIdInformation extends SearchAndFilterContainer {
{text}
</Tooltip>);
},
}, {
},
statusColumn,
{
title: '生产配额(MB/s)',
key: 'produceQuota',
dataIndex: 'produceQuota',
@@ -77,8 +95,13 @@ export class AppIdInformation extends SearchAndFilterContainer {
title: '操作',
key: 'action',
dataIndex: 'action',
render: (val: string, item: IAppsIdInfo) =>
<a onClick={() => this.applyQuotaQuery(item)}></a>,
render: (val: string, item: IAppsIdInfo) => {
const role = users.currentUser.role;
const showEditBtn = (role == 1 || role == 2) || (item && item.appPrincipals.includes(users.currentUser.username));
return (
showEditBtn ? <a onClick={() => this.applyQuotaQuery(item)}></a> : '--'
)
}
}];
}
@@ -116,7 +139,7 @@ export class AppIdInformation extends SearchAndFilterContainer {
<div style={searchKey ? { minHeight: 700 } : null}>
<Table
loading={topic.loading}
columns={this.renderColumns()}
columns={this.renderColumns(this.getData(topic.appsIdInfo))}
table-Layout="fixed"
dataSource={this.getData(topic.appsIdInfo)}
rowKey="key"

View File

@@ -3,7 +3,7 @@ import './index.less';
import Url from 'lib/url-parser';
import { observer } from 'mobx-react';
import { topic, IRealConsumeDetail, ITopicBaseInfo, IRealTimeTraffic } from 'store/topic';
import { Table, Tooltip, Icon, PageHeader, Descriptions, Spin } from 'component/antd';
import { Table, Tooltip, Icon, PageHeader, Descriptions, Spin, Switch } from 'component/antd';
import { ILabelValue } from 'types/base-type';
import { copyString, transMSecondToHour } from 'lib/utils';
import moment from 'moment';
@@ -19,12 +19,14 @@ interface IInfoProps {
export class BaseInformation extends React.Component<IInfoProps> {
public clusterId: number;
public topicName: string;
public percentile: string;
constructor(props: any) {
super(props);
const url = Url();
this.clusterId = Number(url.search.clusterId);
this.topicName = url.search.topic;
this.percentile = "75thPercentile";
}
public updateRealStatus = () => {
@@ -32,7 +34,11 @@ export class BaseInformation extends React.Component<IInfoProps> {
}
public updateConsumeStatus = () => {
topic.getRealConsume(this.clusterId, this.topicName);
topic.getRealConsume(this.clusterId, this.topicName, this.percentile);
}
public onSwitch = (val: boolean) => {
this.percentile = val ? '99thPercentile' : "75thPercentile"
topic.getRealConsume(this.clusterId, this.topicName, this.percentile);
}
public fillBaseInfo() {
@@ -63,8 +69,11 @@ export class BaseInformation extends React.Component<IInfoProps> {
label: '压缩格式',
value: baseInfo.topicCodeC,
}, {
label: '集群ID',
label: '所属物理集群ID',
value: baseInfo.clusterId,
}, {
label: '所属region',
value: baseInfo.regionNameList && baseInfo.regionNameList.join(','),
}];
const infoHide: ILabelValue[] = [{
label: 'Bootstrap Severs',
@@ -195,7 +204,11 @@ export class BaseInformation extends React.Component<IInfoProps> {
<div className="traffic-header">
<span>
<span className="action-button"></span>
<a href={indexUrl} target="_blank"></a>
<a href={indexUrl.indexUrl} target="_blank"></a>
</span>
<span className="switch">
<span>7599</span>
<Switch checkedChildren="开启" unCheckedChildren="关闭" onChange={this.onSwitch} />
</span>
<span className="k-abs" onClick={this.updateConsumeStatus}>
<i className="k-icon-shuaxin didi-theme mr-5" />
@@ -217,7 +230,7 @@ export class BaseInformation extends React.Component<IInfoProps> {
public componentDidMount() {
topic.getRealTimeTraffic(this.clusterId, this.topicName);
topic.getRealConsume(this.clusterId, this.topicName);
topic.getRealConsume(this.clusterId, this.topicName, this.percentile);
}
public render() {

View File

@@ -29,7 +29,7 @@ export class NetWorkFlow extends React.Component<any> {
}
public handleAppChange = (value: string) => {
const {clusterId, topicName} = this.props;
const { clusterId, topicName } = this.props;
topic.appId = value;
this.setState({ loading: true });
topic.getMetriceInfo(clusterId, topicName).then(data => {
@@ -42,8 +42,8 @@ export class NetWorkFlow extends React.Component<any> {
const isTrue = this.props.selectArr === selectOptionMap;
return (
<>
<li>
<span className="label"></span>
<li>
<span className="label"></span>
<Select
defaultValue={this.props.type}
style={{ width: 230 }}
@@ -53,29 +53,29 @@ export class NetWorkFlow extends React.Component<any> {
{this.props.selectArr.map((item: ILabelValue) => (
<Select.Option key={item.value} value={item.value}>
{item.label.length > 16 ?
<Tooltip placement="bottomLeft" title={item.label}>{item.label}</Tooltip>
: item.label}
<Tooltip placement="bottomLeft" title={item.label}>{item.label}</Tooltip>
: item.label}
</Select.Option>
))}
</Select>
</li>
<li className={!isTrue ? 'is-show' : ''}>
<span className="label"></span>
<Select
placeholder="请选择应用"
style={{ width: 180 }}
onChange={this.handleAppChange}
{...searchProps}
>
{topic.appInfo.map((item: IAppsIdInfo) => (
<Select.Option key={item.appId} value={item.appId}>
{item.appName.length > 16 ?
<Tooltip placement="bottomLeft" title={item.appName}> {item.appName} </Tooltip>
: item.appName}
</Select.Option>
))}
</Select>
</li>
</li>
<li className={!isTrue ? 'is-show' : ''}>
<span className="label"></span>
<Select
placeholder="请选择应用"
style={{ width: 180 }}
onChange={this.handleAppChange}
{...searchProps}
>
{topic.appInfo.map((item: IAppsIdInfo) => (
<Select.Option key={item.appId} value={item.appId}>
{item.appName && item.appName.length > 16 ?
<Tooltip placement="bottomLeft" title={item.appName}> {item.appName} </Tooltip>
: item.appName || ''}
</Select.Option>
))}
</Select>
</li>
</>
);
}

View File

@@ -92,8 +92,8 @@ export class ConnectInformation extends SearchAndFilterContainer {
}
public componentDidMount() {
const appId = this.props.baseInfo.appId;
topic.getConnectionInfo(this.clusterId, this.topicName, appId);
// const appId = this.props.baseInfo.appId;
topic.getConnectionInfo(this.clusterId, this.topicName, '');
}
public render() {

View File

@@ -215,3 +215,13 @@
.is-show {
display: none;
}
.switch {
position: absolute;
right: 70px;
span {
line-height: 20px !important;
font-size: 12px !important;
margin-right: 10px;
}
}

View File

@@ -11,6 +11,7 @@ import { PartitionInformation } from './partition-information';
import { BrokersInformation } from './brokers-information';
import { AppIdInformation } from './appid-information';
import { BillInformation } from './bill-information';
import { showAllPermissionModal } from 'container/modal';
import { IXFormWrapper, ITopic } from 'types/base-type';
import { getTopicCompile, getTopicSampling } from 'lib/api';
import { copyString } from 'lib/utils';
@@ -22,17 +23,22 @@ import { users } from 'store/users';
import { urlPrefix } from 'constants/left-menu';
import { handlePageBack } from 'lib/utils';
import Url from 'lib/url-parser';
import router from 'routers/router';
const { TabPane } = Tabs;
import { app } from 'store/app';
interface IInfoData {
value: string;
history: any;
}
@observer
export class TopicDetail extends React.Component {
export class TopicDetail extends React.Component<any> {
public clusterId: number;
public topicName: string;
public isPhysicalTrue: string;
public needAuth: string;
public clusterName: string;
public state = {
drawerVisible: false,
@@ -47,6 +53,8 @@ export class TopicDetail extends React.Component {
super(props);
const url = Url();
this.clusterId = Number(url.search.clusterId);
this.needAuth = url.search.needAuth;
this.clusterName = url.search.clusterName;
this.topicName = url.search.topic;
const isPhysical = Url().search.hasOwnProperty('isPhysicalClusterId');
this.isPhysicalTrue = isPhysical ? '&isPhysicalClusterId=true' : '';
@@ -110,11 +118,11 @@ export class TopicDetail extends React.Component {
const formMap = [
{
key: 'maxMsgNum',
label: '最大采样数据条数',
label: '最大采样条数',
type: 'input_number',
rules: [{
required: true,
message: '请输入最大采样数据条数',
message: '请输入最大采样条数',
}],
attrs: {
max: 100,
@@ -122,32 +130,32 @@ export class TopicDetail extends React.Component {
},
{
key: 'timeout',
label: '最大采样时间',
label: '采样超时时间(ms)',
type: 'input_number',
rules: [{
required: true,
message: '请输入最大采样时间',
message: '请输入采样超时时间(ms)',
}],
attrs: {
max: 300000,
max: 500000,
},
},
{
key: 'partitionId',
label: '分区号',
label: '采样分区号',
type: 'input_number',
rules: [{
required: false,
message: '请输入分区号',
message: '请输入采样分区号',
}],
},
{
key: 'offset',
label: '偏移量',
label: '采样offset位置',
type: 'input_number',
rules: [{
required: false,
message: '请输入偏移量',
message: '请输入采样offset位置',
}],
},
{
@@ -167,13 +175,13 @@ export class TopicDetail extends React.Component {
message: '请选择是否截断',
}],
},
] as IFormItem [];
] as IFormItem[];
const formData = {
maxMsgNum: 1,
timeout: 3000,
timeout: 5000,
};
const { infoVisible } = this.state;
return(
return (
<>
<Drawer
title="Topic 采样"
@@ -198,7 +206,7 @@ export class TopicDetail extends React.Component {
public getAllValue = () => {
const { infoTopicList } = this.state;
const text = infoTopicList.map(ele => ele.value );
const text = infoTopicList.map(ele => ele.value);
return text.join('\n\n');
}
@@ -274,11 +282,11 @@ export class TopicDetail extends React.Component {
let idx = str.indexOf(findStr);
let count = 1;
while (idx >= 0 && count < num) {
idx = str.indexOf(findStr, idx + 1);
count++;
idx = str.indexOf(findStr, idx + 1);
count++;
}
if (idx < 0) {
return '';
return '';
}
return str.substring(0, idx);
}
@@ -312,11 +320,13 @@ export class TopicDetail extends React.Component {
public render() {
const role = users.currentUser.role;
const baseInfo = topic.baseInfo as ITopicBaseInfo;
const showEditBtn = topic.topicBusiness && topic.topicBusiness.principals.includes(users.currentUser.username);
const showEditBtn = (role == 1 || role == 2) || (topic.topicBusiness && topic.topicBusiness.principals.includes(users.currentUser.username));
const topicRecord = {
clusterId: this.clusterId,
topicName: this.topicName,
clusterName: this.clusterName
} as ITopic;
app.getAppList();
return (
<>
@@ -329,10 +339,12 @@ export class TopicDetail extends React.Component {
title={this.topicName || ''}
extra={
<>
<Button key="1" type="primary" onClick={() => applyTopicQuotaQuery(topicRecord)} ></Button>
<Button key="2" type="primary" onClick={() => applyExpandModal(topicRecord)} ></Button>
<Button key="3" type="primary" onClick={this.showDrawer.bind(this)} ></Button>
{showEditBtn && <Button key="4" onClick={() => this.compileDetails()} type="primary"></Button>}
{this.needAuth == "true" && <Button key="0" type="primary" onClick={() => showAllPermissionModal(topicRecord)} ></Button>}
<Button key="1" type="primary" onClick={() => applyTopicQuotaQuery(topicRecord)} ></Button>
<Button key="2" type="primary" onClick={() => applyExpandModal(topicRecord)} ></Button>
<Button key="3" type="primary" onClick={() => this.props.history.push(`/alarm/add`)} ></Button>
<Button key="4" type="primary" onClick={this.showDrawer.bind(this)} ></Button>
{showEditBtn && <Button key="5" onClick={() => this.compileDetails()} type="primary"></Button>}
</>
}
/>
@@ -359,9 +371,9 @@ export class TopicDetail extends React.Component {
</TabPane>
{
role === 0 ? null :
<TabPane tab="Broker信息" key="6">
<BrokersInformation />
</TabPane>
<TabPane tab="Broker信息" key="6">
<BrokersInformation />
</TabPane>
}
<TabPane tab="应用信息" key="7">
<AppIdInformation />

View File

@@ -45,7 +45,7 @@ export class StatusChart extends React.Component {
<div className="chart-box-0">
<div className="chart-title">
<span className="action-button"></span>
<a href={indexUrl} target="_blank"></a>
<a href={indexUrl.indexUrl} target="_blank"></a>
</div>
<Divider className="chart-divider" />
<NetWorkFlow
@@ -61,7 +61,7 @@ export class StatusChart extends React.Component {
<div className="chart-box-0">
<div className="chart-title">
<span className="action-button"></span>
<a href={indexUrl} target="_blank"></a>
<a href={indexUrl.indexUrl} target="_blank"></a>
</div>
<Divider className="chart-divider" />
<NetWorkFlow

View File

@@ -2,12 +2,13 @@ import { observer } from 'mobx-react';
import { order } from 'store/order';
import { OrderList } from './order-list';
import * as React from 'react';
import { Table, Spin, Alert } from 'component/antd';
import { Table, Spin, Alert, Button } from 'component/antd';
import { modal } from 'store/modal';
import { pagination } from 'constants/table';
import { renderOrderOpModal } from 'container/modal/order';
import ReactDOM from 'react-dom';
import { IBaseOrder } from 'types/base-type';
// import { } from 'antd/es/radio';
@observer
export class MyApproval extends OrderList {
@@ -15,29 +16,36 @@ export class MyApproval extends OrderList {
public static defaultProps = {
type: 'approval',
};
public unpendinngRef: HTMLDivElement = null;
public onSelectChange = {
onChange: (selectedRowKeys: string[], selectedRows: []) => {
const num = selectedRows.length;
ReactDOM.render(
selectedRows.length ? (
<>
<Alert
type="warning"
message={`已选择 ${num}`}
showIcon={true}
closable={false}
/>
<span className="k-coll-btn" >
<a className="btn-right" onClick={renderOrderOpModal.bind(this, selectedRows, 1)}></a>
<a onClick={renderOrderOpModal.bind(this, selectedRows, 2)}></a>
</span>
</>) : null,
this.unpendinngRef,
);
order.setSelectedRows(selectedRows);
// console.log(selectedRows);
// ReactDOM.render(
// selectedRows.length ? (
// <>
// <Alert
// type="warning"
// message={`已选择 ${num} 项 `}
// showIcon={true}
// closable={false}
// />
// <span className="k-coll-btn" >
// <a className="btn-right" onClick={renderOrderOpModal.bind(this, selectedRows, 1)}>通过</a>
// <a onClick={renderOrderOpModal.bind(this, selectedRows, 2)}>驳回</a>
// </span>
// </>
// ) : null,
// this.unpendinngRef,
// );
},
// getCheckboxProps: (record: any) => {
// return {
// disabled: record.type === 0 || record.type === 12 || record.type === 1,
// name: record.name,
// };
// },
};
constructor(defaultProps: any) {
@@ -50,13 +58,33 @@ export class MyApproval extends OrderList {
}
public renderTableList(data: IBaseOrder[]) {
const { currentTab } = this.state;
if (modal.actionAfterClose === 'close') {
this.onSelectChange.onChange([], []);
}
const { currentTab, selectedRows } = this.state;
// if (modal.actionAfterClose === 'close') {
// this.onSelectChange.onChange([], []);
// }
return (
<Spin spinning={order.loading}>
<div className="k-collect" ref={(id) => this.unpendinngRef = id} />
<div style={{ marginBottom: '0' }} className="k-collect" ref={(id) => this.unpendinngRef = id} />
{/* 我的审批 业务逻辑修改 问题state无法定义 */}
<div style={{ paddingBottom: '10px' }}>
<Button
style={{ margin: '0 5px' }}
disabled={order.selectedRows.filter(item => item.type === 0 || item.type === 12).length > 0 || order.selectedRows.length < 1 ? true : false}
onClick={() => renderOrderOpModal(order.selectedRows, 1, this.onSelectChange)}
>
</Button>
<Button
disabled={order.selectedRows.length > 0 ? false : true}
onClick={() => renderOrderOpModal(order.selectedRows, 2, this.onSelectChange)}
>
</Button>
<span style={{ color: '#a1a0a0', fontSize: '10px', display: 'inline-block', margin: '0 10px' }}>
Topic申请
</span>
</div>
{/* 我的审批 业务逻辑修改 */}
<Table
rowKey="id"
columns={this.getColumns(data)}
@@ -74,7 +102,7 @@ export class MyApproval extends OrderList {
public render() {
return (
return (
<div className="container">
{this.renderTab()}
<div className="operation-panel">

View File

@@ -21,21 +21,20 @@ interface IProps {
@observer
export class OrderList extends SearchAndFilterContainer {
public orderType: string;
public state = {
public state: { [propname: string]: any } = {
searchKey: '',
currentTab: '0',
filterTypeVisible: false,
filterStatusVisible: false,
selectedRows: [],
};
public currentItem = {} as IBaseOrder;
constructor(props: IProps) {
super(props);
this.orderType = props.type;
// this.state.b;
}
public cancelOrder(record: IBaseOrder) {
@@ -73,9 +72,9 @@ export class OrderList extends SearchAndFilterContainer {
onFilter: (value: string, record: IBaseOrder) => record.status === +value,
render: (t: number) => (
<>
<span className={t === 1 ? 'success' : (t === 2 ? 'fail' : '')}>
{orderStatusMap[t] || ''}
</span>
<span className={t === 1 ? 'success' : (t === 2 ? 'fail' : '')}>
{orderStatusMap[t] || ''}
</span>
</>
),
}, this.renderColumnsFilter('filterStatusVisible'));