kafka-manager 2.0

This commit is contained in:
zengqiao
2020-09-28 15:46:34 +08:00
parent 28d985aaf1
commit c6e4b60424
1253 changed files with 82183 additions and 37179 deletions

View File

@@ -0,0 +1,121 @@
import * as React from 'react';
import { ILabelValue, IBrokersBasicInfo, IOptionType, IClusterReal } from 'types/base-type';
import { observer } from 'mobx-react';
import moment from 'moment';
import Url from 'lib/url-parser';
import { admin } from 'store/admin';
import { PageHeader, Descriptions, Spin } from 'component/antd';
import { selectBrokerMap } from 'constants/status-map';
import { StatusGraghCom } from 'component/flow-table';
import { NetWorkFlow, renderTrafficTable } from 'container/network-flow';
import { timeFormat } from 'constants/strategy';
@observer
export class BaseInfo extends React.Component {
public clusterId: number;
public brokerId: number;
constructor(props: any) {
super(props);
const url = Url();
this.clusterId = Number(url.search.clusterId);
this.brokerId = Number(url.search.brokerId);
}
public updateRealStatus = () => {
admin.getBrokersMetrics(this.clusterId, this.brokerId);
}
public onSelectChange(e: IOptionType) {
return admin.changeBrokerType(e);
}
public getOptionApi = () => {
return admin.getBrokersMetricsHistory(this.clusterId, this.brokerId);
}
public componentDidMount() {
admin.getBrokersBasicInfo(this.clusterId, this.brokerId);
admin.getBrokersMetrics(this.clusterId, this.brokerId);
}
public renderBrokerContent() {
let content = {} as IBrokersBasicInfo;
content = admin.brokersBasicInfo ? admin.brokersBasicInfo : content;
const brokerContent = [{
value: content.host,
label: '主机名',
}, {
value: content.port,
label: '服务端口',
}, {
value: content.jmxPort,
label: 'JMX端口',
}, {
value: content.topicNum,
label: 'Topic数',
}, {
value: content.leaderCount,
label: 'Leader分区数',
}, {
value: content.partitionCount,
label: '分区数',
}, {
value: moment(content.startTime).format(timeFormat),
label: '启动时间',
}];
return (
<>
<div className="chart-title"></div>
<PageHeader className="detail" title="">
<Descriptions size="small" column={3}>
{brokerContent.map((item: ILabelValue, index: number) => (
<Descriptions.Item key={index} label={item.label}>{item.value}</Descriptions.Item>
))}
</Descriptions>
</PageHeader>
</>
);
}
public renderHistoryTraffic() {
return (
<NetWorkFlow
key="1"
selectArr={selectBrokerMap}
type={admin.type}
selectChange={(value: IOptionType) => this.onSelectChange(value)}
getApi={() => this.getOptionApi()}
/>
);
}
public renderTrafficInfo = () => {
return (
<Spin spinning={admin.realBrokerLoading}>
{renderTrafficTable(this.updateRealStatus, StatusGragh)}
</Spin>
);
}
public render() {
return (
<>
{this.renderBrokerContent()}
{this.renderTrafficInfo()}
{this.renderHistoryTraffic()}
</>
);
}
}
@observer
class StatusGragh extends StatusGraghCom<IClusterReal> {
public getData = () => {
return admin.brokersMetrics;
}
public getLoading = () => {
return admin.realBrokerLoading;
}
}

View File

@@ -0,0 +1,172 @@
import * as React from 'react';
import { observer } from 'mobx-react';
import { Table, Tooltip } from 'component/antd';
import { diskDefault } from 'constants/status-map';
import Url from 'lib/url-parser';
import { pagination } from 'constants/table';
import { admin } from 'store/admin';
import { IPartitionsLocation } from 'types/base-type';
import { SearchAndFilterContainer } from 'container/search-filter';
import './index.less';
@observer
export class DiskInfo extends SearchAndFilterContainer {
public clusterId: number;
public brokerId: number;
public state = {
searchKey: '',
filterStatusVisible: false,
};
constructor(props: any) {
super(props);
const url = Url();
this.clusterId = Number(url.search.clusterId);
this.brokerId = Number(url.search.brokerId);
}
public getDescription = (value: any, record: any) => {
return Object.keys(value).map((key: keyof any, index: any) => {
return (
<>
<p key={index}>
<span>{value[key]}</span>
{(record[key] as []).join(',')}{(record[key] as []).length})
</p>
</>
);
});
}
public getMoreDetail = (record: IPartitionsLocation) => {
return (
<div className="p-description" key={record.key}>
<p><span>diskName: </span>{record.diskName}</p>
<p><span>brokerId: </span>{record.brokerId}</p>
<p><span>isUnderReplicated:</span>{record.underReplicated + ''}</p>
<p><span>topic: </span>{record.topicName}</p>
{this.getDescription(diskDefault, record)}
<p><span>clusterId: </span>{record.clusterId}</p>
<p><span>underReplicatedPartitions: </span></p>
</div>
);
}
public componentDidMount() {
admin.getPartitionsLocation(this.clusterId, this.brokerId);
}
public getData<T extends IPartitionsLocation>(origin: T[]) {
let data: T[] = origin;
let { searchKey } = this.state;
searchKey = (searchKey + '').trim().toLowerCase();
data = searchKey ? origin.filter((item: IPartitionsLocation) =>
(item.diskName !== undefined && item.diskName !== null) && item.diskName.toLowerCase().includes(searchKey as string)
|| (item.topicName !== undefined && item.topicName !== null) && item.topicName.toLowerCase().includes(searchKey as string),
) : origin ;
return data;
}
public renderDiskInfo() {
const underReplicated = Object.assign({
title: '状态',
dataIndex: 'underReplicated',
key: 'underReplicated',
filters: [{ text: '已同步', value: 'false' }, { text: '未同步', value: 'true' }],
onFilter: (value: string, record: IPartitionsLocation) => record.underReplicated + '' === value,
render: (t: boolean) => <span>{t ? '未同步' : '已同步'}</span>,
}, this.renderColumnsFilter('filterStatusVisible'));
const columns = [{
title: '磁盘名称',
dataIndex: 'diskName',
key: 'diskName',
sorter: (a: IPartitionsLocation, b: IPartitionsLocation) => a.diskName.charCodeAt(0) - b.diskName.charCodeAt(0),
render: (val: string) => <Tooltip placement="bottomLeft" title={val}> {val} </Tooltip>,
}, {
title: 'Topic名称',
dataIndex: 'topicName',
key: 'topicName',
sorter: (a: IPartitionsLocation, b: IPartitionsLocation) => a.topicName.charCodeAt(0) - b.topicName.charCodeAt(0),
render: (val: string) => <Tooltip placement="bottomLeft" title={val}> {val} </Tooltip>,
}, {
title: 'Leader分区',
dataIndex: 'leaderPartitions',
key: 'leaderPartitions',
onCell: () => ({
style: {
maxWidth: 250,
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
cursor: 'pointer',
},
}),
render: (value: number[]) => {
return (
<Tooltip placement="bottomLeft" title={value.join('、')}>
{value.map(i => <span key={i} className="p-params">{i}</span>)}
</Tooltip>
);
},
}, {
title: 'Follow分区',
dataIndex: 'followerPartitions',
key: 'followerPartitions',
onCell: () => ({
style: {
maxWidth: 250,
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
cursor: 'pointer',
},
}),
render: (value: number[]) => {
return (
<Tooltip placement="bottomLeft" title={value.join('、')}>
{value.map(i => <span key={i} className="p-params">{i}</span>)}
</Tooltip>
);
},
}, {
title: '未同步副本',
dataIndex: 'notUnderReplicatedPartitions',
render: (value: number[]) => {
return (
<Tooltip placement="bottomLeft" title={value.join('、')}>
{value.map(i => <span key={i} className="p-params p-params-unFinished">{i}</span>)}
</Tooltip>
);
},
},
underReplicated,
];
return (
<>
<div className="k-row">
<ul className="k-tab">
<li>{this.props.tab}</li>
{this.renderSearch('', '请输入磁盘名或者Topic名称')}
</ul>
<Table
columns={columns}
expandIconColumnIndex={-1}
expandedRowRender={this.getMoreDetail}
dataSource={this.getData(admin.partitionsLocation)}
rowKey="key"
pagination={pagination}
/>
</div>
</>
);
}
public render() {
return (
admin.partitionsLocation ? <> {this.renderDiskInfo()} </> : null
);
}
}

View File

@@ -0,0 +1,139 @@
.p-params {
display: inline-block;
padding: 0px 10px;
border-radius: 4px;
border: 1px solid rgba(217, 217, 217, 1);
margin: 0px 8px 8px 0px;
&-unFinished {
background: rgba(245, 34, 45, 0.2);
}
}
.p-description {
margin-left: 20px;
span {
display: inline-block;
width: 180px;
text-align: right;
margin-right: 10px;
}
}
p.k-title {
width: 100%;
font-size: 14px;
font-family: PingFangSC-Medium;
font-weight: 500;
color: rgba(0, 0, 0, 0.85);
height: 48px;
line-height: 48px;
background: rgba(0, 0, 0, 0.02);
padding-left: 24px;
margin: 0;
}
.right-flow {
.k-abs {
right: 24px;
cursor: pointer;
& > i {
margin-right: 5px;
}
}
}
.title-flow{
width: 100%;
display: flex;
justify-content: space-around;
.k-text{
width: 420px;
line-height: 50px;
background: #00000005;
}
}
.mb-24 {
margin-bottom: 24px;
}
.k-summary {
width: 100%;
font-family: PingFangSC-Regular;
background: #fff;
.k-row-1 {
width: 100%;
height: 48px;
display: flex;
border-bottom: solid 1px #e8e8e8;
div {
flex: 1;
line-height: 48px;
padding-left: 32px;
font-size: 15px;
color: rgba(0, 0, 0, 0.85);
}
div + div {
border-left: solid 1px #e8e8e8;
}
}
.k-row-2 {
width: 100%;
padding: 24px 0;
border-top: solid 1px #e8e8e8;
border-bottom: solid 1px #e8e8e8;
display: flex;
text-align: center;
div {
height: 58px;
flex: 1;
span {
display: block;
line-height: 22px;
color: rgba(0, 0, 0, 0.45);
font-size: 14px;
}
p {
line-height: 32px;
margin-top: 4px;
font-size: 24px;
font-weight: 400;
color: rgba(0, 0, 0, 0.85);
}
}
div + div {
border-left: solid 2px #e8e8e8;
}
}
.k-row-3 {
width: 100%;
text-align: center;
display: flex;
div {
padding: 9px 0;
flex: 1;
span {
display: block;
line-height: 22px;
color: rgba(0, 0, 0, 0.45);
font-size: 14px;
}
p {
line-height: 22px;
margin-top: 1px;
font-size: 18px;
font-weight: 400;
color: rgba(0, 0, 0, 0.85);
}
}
.long-text {
font-size: 12px;
}
div + div {
border-left: solid 2px #e8e8e8;
}
}
}
.option-map {
min-width: 1200px;
}

View File

@@ -0,0 +1,67 @@
import * as React from 'react';
import { observer } from 'mobx-react';
import { Tabs, PageHeader } from 'antd';
import { handleTabKey } from 'lib/utils';
import { IMetaData } from 'types/base-type';
import Url from 'lib/url-parser';
import { BaseInfo } from './base-info';
import { MonitorInfo } from './monitor-info';
import { TopicInfo } from './topic-info';
import { DiskInfo } from './disk-info';
import { PartitionInfo } from './partition-info';
import { TopicAnalysis } from './topic-analysis';
import { handlePageBack } from 'lib/utils';
import { admin } from 'store/admin';
const { TabPane } = Tabs;
@observer
export class BrokerDetail extends React.Component {
public clusterId: number;
public brokerId: number;
constructor(props: any) {
super(props);
const url = Url();
this.clusterId = Number(url.search.clusterId);
this.brokerId = Number(url.search.brokerId);
}
public componentDidMount() {
admin.getBasicInfo(this.clusterId);
}
public render() {
let content = {} as IMetaData;
content = admin.basicInfo ? admin.basicInfo : content;
return (
<>
<PageHeader
className="detail topic-detail-header"
onBack={() => handlePageBack(`/admin/cluster-detail?clusterId=${this.clusterId}#3`)}
title={`集群列表/${content.clusterName || ''}/${this.brokerId || ''}`}
/>
<Tabs activeKey={location.hash.substr(1) || '1'} type="card" onChange={handleTabKey}>
<TabPane tab="基本信息" key="1">
<BaseInfo/>
</TabPane>
<TabPane tab="监控信息" key="2">
<MonitorInfo />
</TabPane>
<TabPane tab="Topic信息" key="3">
<TopicInfo tab={'Topic信息'} />
</TabPane>
<TabPane tab="磁盘信息" key="4">
<DiskInfo tab={'磁盘信息'} />
</TabPane>
<TabPane tab="partition信息" key="5">
<PartitionInfo tab={'partition信息'} />
</TabPane>
<TabPane tab="Topic分析" key="6">
<TopicAnalysis />
</TabPane>
</Tabs>
</>
);
}
}

View File

@@ -0,0 +1,65 @@
import * as React from 'react';
import { observer } from 'mobx-react';
import Url from 'lib/url-parser';
import { adminMonitor } from 'store/admin-monitor';
import moment from 'moment';
import './index.less';
import { ExpandCard } from 'component/expand-card';
import { DataCurveFilter } from '../data-curve';
import { allCurves, ICurveType } from '../data-curve/config';
import { CommonCurve } from 'container/common-curve';
@observer
export class MonitorInfo extends React.Component {
public clusterId: number;
public brokerId: number;
public $chart: any;
public chart11: any;
public state = {
startTime: moment().subtract(1, 'hour'),
endTime: moment(),
};
constructor(props: any) {
super(props);
const url = Url();
this.clusterId = Number(url.search.clusterId);
this.brokerId = Number(url.search.brokerId);
adminMonitor.setCurrentBrokerId(this.brokerId);
adminMonitor.setCurrentClusterId(this.clusterId);
}
public handleRef(chart: any, index: number) {
this.$chart[index] = chart;
}
public getCurves = (curveType: ICurveType) => {
return curveType.curves.map(o => {
return <CommonCurve key={o.path} options={o} parser={curveType.parser} />;
});
}
public renderChart() {
return (
<div className="curve-wrapper">
<DataCurveFilter />
{allCurves.map(c => {
return <ExpandCard key={c.type} title={c.title} charts={this.getCurves(c)} />;
})}
</div>
);
}
public componentDidMount() {
adminMonitor.getBrokersMetricsList(moment().subtract(1, 'hour').format('x'), moment().format('x'));
}
public render() {
return (
<>
{adminMonitor.brokersMetricsHistory ? this.renderChart() : null}
</>
);
}
}

View File

@@ -0,0 +1,104 @@
import * as React from 'react';
import { observer } from 'mobx-react';
import { Table, Tooltip } from 'component/antd';
import { columsDefault } from 'constants/status-map';
import Url from 'lib/url-parser';
import { pagination } from 'constants/table';
import { admin } from 'store/admin';
import { IBrokersPartitions } from 'types/base-type';
import './index.less';
import { SearchAndFilterContainer } from 'container/search-filter';
import { getPartitionInfoColumns } from '../config';
@observer
export class PartitionInfo extends SearchAndFilterContainer {
public clusterId: number;
public brokerId: number;
public state = {
searchKey: '',
filterStatusVisible: false,
};
constructor(props: any) {
super(props);
const url = Url();
this.clusterId = Number(url.search.clusterId);
this.brokerId = Number(url.search.brokerId);
}
public getColumns = () => {
const columns = getPartitionInfoColumns();
const status = Object.assign({
title: '状态',
dataIndex: 'underReplicated',
key: 'underReplicated',
onCell: null,
width: '7%',
filters: [{ text: '已同步', value: true }, { text: '未同步', value: false }],
onFilter: (value: string, record: IBrokersPartitions) => record.underReplicated === Boolean(value),
render: (value: string) => <span>{Boolean(value) ? '已同步' : '未同步'}</span>,
}, this.renderColumnsFilter('filterStatusVisible'));
const col = columns.splice(4, 0, status);
return columns;
}
public getDescription = (value: any, record: any) => {
return Object.keys(value).map((key: keyof any, index: number) => {
return (
<>
<p key={index}>
<span>{value[key]}</span>
{(record[key] as []).join(',')}{(record[key] as []).length})
</p>
</>
);
});
}
public getMoreDetail = (record: IBrokersPartitions) => {
return (
<div className="p-description">
<p><span>Topic: </span>{record.topicName}</p>
<p><span>isUnderReplicated:</span>{record.underReplicated ? '已同步' : '未同步'}</p>
{this.getDescription(columsDefault, record)}
</div>
);
}
public getData<T extends IBrokersPartitions>(origin: T[]) {
let data: T[] = origin;
let { searchKey } = this.state;
searchKey = (searchKey + '').trim().toLowerCase();
data = searchKey ? origin.filter((item: IBrokersPartitions) =>
(item.topicName !== undefined && item.topicName !== null) && item.topicName.toLowerCase().includes(searchKey as string),
) : origin ;
return data;
}
public componentDidMount() {
admin.getBrokersPartitions(this.clusterId, this.brokerId);
}
public render() {
return (
<div className="k-row">
<ul className="k-tab">
<li>{this.props.tab}</li>
{this.renderSearch('', '请输入Topic')}
</ul>
<Table
loading={admin.realBrokerLoading}
columns={this.getColumns()}
expandIconAsCell={true}
expandIconColumnIndex={-1}
expandedRowRender={this.getMoreDetail}
dataSource={this.getData(admin.brokersPartitions)}
rowKey="key"
pagination={pagination}
/>
</div>
);
}
}

View File

@@ -0,0 +1,117 @@
import * as React from 'react';
import { Table, Tooltip } from 'component/antd';
import { observer } from 'mobx-react';
import { admin } from 'store/admin';
import { brokerMetrics } from 'constants/status-map';
import { IBrokerHistory, IAnalysisTopicVO } from 'types/base-type';
import Url from 'lib/url-parser';
import './index.less';
const columns = [{
title: 'Topic名称',
dataIndex: 'topicName',
key: 'topicName',
sorter: (a: IAnalysisTopicVO, b: IAnalysisTopicVO) => a.topicName.charCodeAt(0) - b.topicName.charCodeAt(0),
render: (val: string) => <Tooltip placement="bottomLeft" title={val}> {val} </Tooltip>,
},
{
title: 'Bytes In(KB/s)',
dataIndex: 'bytesInRate',
key: 'bytesInRate',
sorter: (a: IAnalysisTopicVO, b: IAnalysisTopicVO) => Number(b.bytesIn) - Number(a.bytesIn),
render: (t: number, record: any) => `${record && (record.bytesIn / 1024).toFixed(2)} (${+Math.ceil((t * 100))}%)`,
},
{
title: 'Bytes Out(KB/s)',
dataIndex: 'bytesOutRate',
key: 'bytesOutRate',
sorter: (a: IAnalysisTopicVO, b: IAnalysisTopicVO) => Number(b.bytesOut) - Number(a.bytesOut),
render: (t: number, record: any) => `${record && (record.bytesOut / 1024).toFixed(2)} (${+Math.ceil((t * 100))}%)`,
},
{
title: 'Message In(秒)',
dataIndex: 'messagesInRate',
key: 'messagesInRate',
sorter: (a: IAnalysisTopicVO, b: IAnalysisTopicVO) => Number(b.messagesIn) - Number(a.messagesIn),
render: (t: number, record: any) => `${record && record.messagesIn} (${+Math.ceil((t * 100))}%)`,
},
{
title: 'Total Fetch Requests(秒)',
dataIndex: 'totalFetchRequestsRate',
key: 'totalFetchRequestsRate',
sorter: (a: IAnalysisTopicVO, b: IAnalysisTopicVO) => Number(b.totalFetchRequests) - Number(a.totalFetchRequests),
render: (t: number, record: any) => `${record && record.totalFetchRequests} (${+Math.ceil((t * 100))}%)`,
},
{
title: 'Total Produce Requests(秒)',
dataIndex: 'totalProduceRequestsRate',
key: 'totalProduceRequestsRate',
sorter: (a: IAnalysisTopicVO, b: IAnalysisTopicVO) => Number(b.totalProduceRequests) - Number(a.totalProduceRequests),
render: (t: number, record: any) => `${record && record.totalProduceRequests} (${+Math.ceil((t * 100))}%)`,
}];
@observer
export class TopicAnalysis extends React.Component {
public clusterId: number;
public brokerId: number;
constructor(props: any) {
super(props);
const url = Url();
this.clusterId = Number(url.search.clusterId);
this.brokerId = Number(url.search.brokerId);
}
public brokerStatus() {
return (
<div className="k-summary">
<div className="k-row-3">
<div>
<span>Broker ID</span>
<p>{this.brokerId}</p>
</div>
{admin.brokersAnalysis ?
Object.keys(brokerMetrics).map((i: keyof IBrokerHistory) => {
return (
<div key={i}>
<span className={brokerMetrics[i].length > 25 ? 'long-text' : ''}>
{brokerMetrics[i]}</span>
<p>{(admin.brokersAnalysis[i] === null || admin.brokersAnalysis[i] === undefined) ?
'' : admin.brokersAnalysis[i].toFixed(2)}</p>
</div>
);
}) : ''}
</div>
</div>
);
}
public componentDidMount() {
admin.getBrokersAnalysis(this.clusterId, this.brokerId);
}
public render() {
let analysisTopic = [] as IAnalysisTopicVO[];
analysisTopic = admin.brokersAnalysisTopic ? admin.brokersAnalysisTopic : analysisTopic;
return(
<>
<div className="k-row right-flow mb-24">
<p className="k-title">Broker </p>
{this.brokerStatus()}
</div>
<div className="k-row right-flow">
<div className="title-flow">
<p className="k-title">Topic </p>
<span className="didi-theme k-text">Broker总量的百分比</span>
</div>
<Table
rowKey="key"
columns={columns}
dataSource={analysisTopic}
pagination={false}
/>
</div>
</>
);
}
}

View File

@@ -0,0 +1,127 @@
import * as React from 'react';
import moment from 'moment';
import { observer } from 'mobx-react';
import { pagination, cellStyle } from 'constants/table';
import { SearchAndFilterContainer } from 'container/search-filter';
import { IBrokersTopics } from 'types/base-type';
import Url from 'lib/url-parser';
import { Table, Tooltip } from 'component/antd';
import { admin } from 'store/admin';
import { region } from 'store/region';
import { timeFormat } from 'constants/strategy';
@observer
export class TopicInfo extends SearchAndFilterContainer {
public state = {
searchKey: '',
};
public clusterId: number;
public brokerId: number;
constructor(props: any) {
super(props);
const url = Url();
this.clusterId = Number(url.search.clusterId);
this.brokerId = Number(url.search.brokerId);
}
public getData<T extends IBrokersTopics>(origin: T[]) {
let data: T[] = origin;
let { searchKey } = this.state;
searchKey = (searchKey + '').trim().toLowerCase();
data = searchKey ? origin.filter((item: IBrokersTopics) =>
(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 ;
return data;
}
public renderTopicInfo() {
const cloumns = [{
title: 'Topic名称',
key: 'topicName',
onCell: () => ({
style: {
maxWidth: 250,
...cellStyle,
},
}),
sorter: (a: IBrokersTopics, b: IBrokersTopics) => a.topicName.charCodeAt(0) - b.topicName.charCodeAt(0),
render: (t: string, r: IBrokersTopics) => {
return (
<Tooltip placement="bottomLeft" title={r.topicName} >
<a
// tslint:disable-next-line:max-line-length
href={`${this.urlPrefix}/topic/topic-detail?clusterId=${this.clusterId}&topic=${r.topicName || ''}&isPhysicalClusterId=true&region=${region.currentRegion}`}
>
{r.topicName}
</a>
</Tooltip>
);
},
}, {
title: '分区数',
dataIndex: 'partitionNum',
key: 'partitionNum',
sorter: (a: IBrokersTopics, b: IBrokersTopics) => b.partitionNum - a.partitionNum,
}, {
title: '副本数',
dataIndex: 'replicaNum',
key: 'replicaNum',
sorter: (a: IBrokersTopics, b: IBrokersTopics) => b.replicaNum - a.replicaNum,
}, {
title: 'Bytes In(KB/s)',
dataIndex: 'byteIn',
key: 'byteIn',
sorter: (a: IBrokersTopics, b: IBrokersTopics) => b.byteIn - a.byteIn,
render: (t: number) => t === null ? '' : (t / 1024).toFixed(2),
}, {
title: 'QPS',
dataIndex: 'produceRequest',
key: 'produceRequest',
width: '10%',
sorter: (a: IBrokersTopics, b: IBrokersTopics) => b.produceRequest - a.produceRequest,
render: (t: number) => t === null ? '' : t.toFixed(2),
}, {
title: '所属应用',
dataIndex: 'appName',
key: 'appName',
width: '10%',
render: (val: string, record: IBrokersTopics) => (
<Tooltip placement="bottomLeft" title={record.appId} >
{val}
</Tooltip>
),
}, {
title: '修改时间',
dataIndex: 'updateTime',
key: 'updateTime',
sorter: (a: IBrokersTopics, b: IBrokersTopics) => b.updateTime - a.updateTime,
render: (t: number) => moment(t).format(timeFormat),
}];
return (
<>
<div className="k-row">
<ul className="k-tab">
<li>{this.props.tab}</li>
{this.renderSearch('', '请输入Topic名称或者负责人')}
</ul>
<Table columns={cloumns} dataSource={this.getData(admin.brokersTopics)} rowKey="key" pagination={pagination} />
</div>
</>
);
}
public componentDidMount() {
admin.getBrokersTopics(this.clusterId, this.brokerId);
}
public render() {
return (
admin.brokersTopics ? <> {this.renderTopicInfo()} </> : null
);
}
}