mirror of
https://github.com/didi/KnowStreaming.git
synced 2026-01-04 11:52:07 +08:00
V2.4.1 FE
This commit is contained in:
@@ -100,7 +100,7 @@ export class ClusterConsumer extends SearchAndFilterContainer {
|
||||
<div className="k-row">
|
||||
<ul className="k-tab">
|
||||
<li>{this.props.tab}</li>
|
||||
{this.renderSearch()}
|
||||
{this.renderSearch('', '请输入消费组名称')}
|
||||
</ul>
|
||||
<Table
|
||||
columns={this.columns}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { SearchAndFilterContainer } from 'container/search-filter';
|
||||
import { Table } from 'component/antd';
|
||||
import { Table, Button, Popconfirm, Modal, Transfer, notification } from 'component/antd';
|
||||
// import { Transfer } from 'antd'
|
||||
import { observer } from 'mobx-react';
|
||||
import { pagination } from 'constants/table';
|
||||
import Url from 'lib/url-parser';
|
||||
@@ -16,8 +17,12 @@ import { timeFormat } from 'constants/strategy';
|
||||
export class ClusterController extends SearchAndFilterContainer {
|
||||
public clusterId: number;
|
||||
|
||||
public state = {
|
||||
public state: any = {
|
||||
searchKey: '',
|
||||
searchCandidateKey: '',
|
||||
isCandidateModel: false,
|
||||
mockData: [],
|
||||
targetKeys: [],
|
||||
};
|
||||
|
||||
constructor(props: any) {
|
||||
@@ -37,6 +42,94 @@ export class ClusterController extends SearchAndFilterContainer {
|
||||
return data;
|
||||
}
|
||||
|
||||
public getCandidateData<T extends IController>(origin: T[]) {
|
||||
let data: T[] = origin;
|
||||
let { searchCandidateKey } = this.state;
|
||||
searchCandidateKey = (searchCandidateKey + '').trim().toLowerCase();
|
||||
|
||||
data = searchCandidateKey ? origin.filter((item: IController) =>
|
||||
(item.host !== undefined && item.host !== null) && item.host.toLowerCase().includes(searchCandidateKey as string),
|
||||
) : origin;
|
||||
return data;
|
||||
}
|
||||
|
||||
// 候选controller
|
||||
public renderCandidateController() {
|
||||
const columns = [
|
||||
{
|
||||
title: 'BrokerId',
|
||||
dataIndex: 'brokerId',
|
||||
key: 'brokerId',
|
||||
width: '20%',
|
||||
sorter: (a: IController, b: IController) => b.brokerId - a.brokerId,
|
||||
render: (r: string, t: IController) => {
|
||||
return (
|
||||
<a href={`${this.urlPrefix}/admin/broker-detail?clusterId=${this.clusterId}&brokerId=${t.brokerId}`}>{r}
|
||||
</a>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'BrokerHost',
|
||||
key: 'host',
|
||||
dataIndex: 'host',
|
||||
width: '20%',
|
||||
// render: (r: string, t: IController) => {
|
||||
// return (
|
||||
// <a href={`${this.urlPrefix}/admin/broker-detail?clusterId=${this.clusterId}&brokerId=${t.brokerId}`}>{r}
|
||||
// </a>
|
||||
// );
|
||||
// },
|
||||
},
|
||||
{
|
||||
title: 'Broker状态',
|
||||
key: 'status',
|
||||
dataIndex: 'status',
|
||||
width: '20%',
|
||||
render: (r: number, t: IController) => {
|
||||
return (
|
||||
<span>{r === 1 ? '不在线' : '在线'}</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'startTime',
|
||||
key: 'startTime',
|
||||
width: '25%',
|
||||
sorter: (a: IController, b: IController) => b.timestamp - a.timestamp,
|
||||
render: (t: number) => moment(t).format(timeFormat),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
key: 'operation',
|
||||
width: '15%',
|
||||
render: (r: string, t: IController) => {
|
||||
return (
|
||||
<Popconfirm
|
||||
title="确定删除?"
|
||||
onConfirm={() => this.deleteCandidateCancel(t)}
|
||||
cancelText="取消"
|
||||
okText="确认"
|
||||
>
|
||||
<a>删除</a>
|
||||
</Popconfirm>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={this.getCandidateData(admin.controllerCandidate)}
|
||||
pagination={pagination}
|
||||
rowKey="key"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
public renderController() {
|
||||
|
||||
const columns = [
|
||||
@@ -58,12 +151,6 @@ export class ClusterController extends SearchAndFilterContainer {
|
||||
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: '变更时间',
|
||||
@@ -87,16 +174,104 @@ export class ClusterController extends SearchAndFilterContainer {
|
||||
|
||||
public componentDidMount() {
|
||||
admin.getControllerHistory(this.clusterId);
|
||||
admin.getCandidateController(this.clusterId);
|
||||
admin.getBrokersMetadata(this.clusterId);
|
||||
}
|
||||
|
||||
public addController = () => {
|
||||
this.setState({ isCandidateModel: true, targetKeys: [] })
|
||||
}
|
||||
|
||||
public addCandidateChange = (targetKeys: any) => {
|
||||
this.setState({ targetKeys })
|
||||
}
|
||||
|
||||
|
||||
|
||||
public handleCandidateCancel = () => {
|
||||
this.setState({ isCandidateModel: false });
|
||||
}
|
||||
|
||||
public handleCandidateOk = () => {
|
||||
let brokerIdList = this.state.targetKeys.map((item: any) => {
|
||||
return admin.brokersMetadata[item].brokerId
|
||||
})
|
||||
admin.addCandidateController(this.clusterId, brokerIdList).then(data => {
|
||||
notification.success({ message: '新增成功' });
|
||||
admin.getCandidateController(this.clusterId);
|
||||
}).catch(err => {
|
||||
notification.error({ message: '新增失败' });
|
||||
})
|
||||
this.setState({ isCandidateModel: false, targetKeys: [] });
|
||||
}
|
||||
|
||||
public deleteCandidateCancel = (target: any) => {
|
||||
admin.deleteCandidateCancel(this.clusterId, [target.brokerId]).then(() => {
|
||||
notification.success({ message: '删除成功' });
|
||||
});
|
||||
this.setState({ isCandidateModel: false });
|
||||
}
|
||||
|
||||
public renderAddCandidateController() {
|
||||
let filterControllerCandidate = admin.brokersMetadata.filter((item: any) => {
|
||||
return !admin.filtercontrollerCandidate.includes(item.brokerId)
|
||||
})
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="新增候选Controller"
|
||||
visible={this.state.isCandidateModel}
|
||||
// okText="确认"
|
||||
// cancelText="取消"
|
||||
maskClosable={false}
|
||||
// onOk={() => this.handleCandidateOk()}
|
||||
onCancel={() => this.handleCandidateCancel()}
|
||||
footer={<>
|
||||
<Button style={{ width: '60px' }} onClick={() => this.handleCandidateCancel()}>取消</Button>
|
||||
<Button disabled={this.state.targetKeys.length > 0 ? false : true} style={{ width: '60px' }} type="primary" onClick={() => this.handleCandidateOk()}>确定</Button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Transfer
|
||||
dataSource={filterControllerCandidate}
|
||||
targetKeys={this.state.targetKeys}
|
||||
render={item => item.host}
|
||||
onChange={(targetKeys) => this.addCandidateChange(targetKeys)}
|
||||
titles={['未选', '已选']}
|
||||
locale={{
|
||||
itemUnit: '项',
|
||||
itemsUnit: '项',
|
||||
}}
|
||||
listStyle={{
|
||||
width: "45%",
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<div className="k-row">
|
||||
<ul className="k-tab">
|
||||
<li>
|
||||
<span>候选Controller</span>
|
||||
<span style={{ display: 'inline-block', color: "#a7a8a9", fontSize: '12px', marginLeft: '15px' }}>Controller将会优先从以下Broker中选举</span>
|
||||
</li>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<div style={{ marginRight: '15px' }}>
|
||||
<Button onClick={() => this.addController()} type='primary'>新增候选Controller</Button>
|
||||
</div>
|
||||
{this.renderSearch('', '请查找Host', 'searchCandidateKey')}
|
||||
</div>
|
||||
</ul>
|
||||
{this.renderCandidateController()}
|
||||
<ul className="k-tab" style={{ marginTop: '10px' }}>
|
||||
<li>{this.props.tab}</li>
|
||||
{this.renderSearch('', '请输入Host')}
|
||||
</ul>
|
||||
{this.renderController()}
|
||||
{this.renderAddCandidateController()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ export class ClusterTopic extends SearchAndFilterContainer {
|
||||
key: 'appName',
|
||||
// width: '10%',
|
||||
render: (val: string, record: IClusterTopics) => (
|
||||
<Tooltip placement="bottomLeft" title={record.appId} >
|
||||
<Tooltip placement="bottomLeft" title={val} >
|
||||
{val}
|
||||
</Tooltip>
|
||||
),
|
||||
|
||||
@@ -314,8 +314,7 @@ export class ExclusiveCluster extends SearchAndFilterContainer {
|
||||
>
|
||||
<div className="region-prompt">
|
||||
<span>
|
||||
由于该Region已被逻辑集群【 {this.state.logicalClusterName} 】使用
|
||||
请先解除Region与逻辑集群的关系
|
||||
该Region已被逻辑集群【 {this.state.logicalClusterName} 】使用,请先解除Region与逻辑集群的关系
|
||||
</span>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
.traffic-table {
|
||||
margin: 10px 0;
|
||||
min-height: 450px;
|
||||
min-height: 330px;
|
||||
.traffic-header {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
@@ -94,4 +94,10 @@
|
||||
.region-prompt{
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.asd{
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -32,9 +32,9 @@ export class ClusterDetail extends React.Component {
|
||||
}
|
||||
|
||||
public render() {
|
||||
let content = {} as IMetaData;
|
||||
content = admin.basicInfo ? admin.basicInfo : content;
|
||||
return (
|
||||
let content = {} as IMetaData;
|
||||
content = admin.basicInfo ? admin.basicInfo : content;
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
className="detail topic-detail-header"
|
||||
@@ -46,7 +46,7 @@ export class ClusterDetail extends React.Component {
|
||||
<ClusterOverview basicInfo={content} />
|
||||
</TabPane>
|
||||
<TabPane tab="Topic信息" key="2">
|
||||
<ClusterTopic tab={'Topic信息'}/>
|
||||
<ClusterTopic tab={'Topic信息'} />
|
||||
</TabPane>
|
||||
<TabPane tab="Broker信息" key="3">
|
||||
<ClusterBroker tab={'Broker信息'} basicInfo={content} />
|
||||
@@ -60,11 +60,11 @@ export class ClusterDetail extends React.Component {
|
||||
<TabPane tab="逻辑集群信息" key="6">
|
||||
<LogicalCluster tab={'逻辑集群信息'} basicInfo={content} />
|
||||
</TabPane>
|
||||
<TabPane tab="Controller变更历史" key="7">
|
||||
<TabPane tab="Controller信息" key="7">
|
||||
<ClusterController tab={'Controller变更历史'} />
|
||||
</TabPane>
|
||||
<TabPane tab="限流信息" key="8">
|
||||
<CurrentLimiting tab={'限流信息'}/>
|
||||
<TabPane tab="限流信息" key="8">
|
||||
<CurrentLimiting tab={'限流信息'} />
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { wrapper } from 'store';
|
||||
import { observer } from 'mobx-react';
|
||||
import { IXFormWrapper, IMetaData, IRegister } from 'types/base-type';
|
||||
import { admin } from 'store/admin';
|
||||
import { users } from 'store/users';
|
||||
import { registerCluster, createCluster, pauseMonitoring } from 'lib/api';
|
||||
import { SearchAndFilterContainer } from 'container/search-filter';
|
||||
import { cluster } from 'store/cluster';
|
||||
@@ -12,6 +13,7 @@ import { urlPrefix } from 'constants/left-menu';
|
||||
import { indexUrl } from 'constants/strategy'
|
||||
import { region } from 'store';
|
||||
import './index.less';
|
||||
import Monacoeditor from 'component/editor/monacoEditor';
|
||||
import { getAdminClusterColumns } from '../config';
|
||||
|
||||
const { confirm } = Modal;
|
||||
@@ -77,34 +79,34 @@ export class ClusterList extends SearchAndFilterContainer {
|
||||
disabled: item ? true : false,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'idc',
|
||||
label: '数据中心',
|
||||
defaultValue: region.regionName,
|
||||
rules: [{ required: true, message: '请输入数据中心' }],
|
||||
attrs: {
|
||||
placeholder: '请输入数据中心',
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'mode',
|
||||
label: '集群类型',
|
||||
type: 'select',
|
||||
options: cluster.clusterModes.map(ele => {
|
||||
return {
|
||||
label: ele.message,
|
||||
value: ele.code,
|
||||
};
|
||||
}),
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请选择集群类型',
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请选择集群类型',
|
||||
},
|
||||
},
|
||||
// {
|
||||
// key: 'idc',
|
||||
// label: '数据中心',
|
||||
// defaultValue: region.regionName,
|
||||
// rules: [{ required: true, message: '请输入数据中心' }],
|
||||
// attrs: {
|
||||
// placeholder: '请输入数据中心',
|
||||
// disabled: true,
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// key: 'mode',
|
||||
// label: '集群类型',
|
||||
// type: 'select',
|
||||
// options: cluster.clusterModes.map(ele => {
|
||||
// return {
|
||||
// label: ele.message,
|
||||
// value: ele.code,
|
||||
// };
|
||||
// }),
|
||||
// rules: [{
|
||||
// required: true,
|
||||
// message: '请选择集群类型',
|
||||
// }],
|
||||
// attrs: {
|
||||
// placeholder: '请选择集群类型',
|
||||
// },
|
||||
// },
|
||||
{
|
||||
key: 'kafkaVersion',
|
||||
label: 'kafka版本',
|
||||
@@ -132,6 +134,25 @@ export class ClusterList extends SearchAndFilterContainer {
|
||||
"security.protocol": "SASL_PLAINTEXT",
|
||||
"sasl.mechanism": "PLAIN",
|
||||
"sasl.jaas.config": "org.apache.kafka.common.security.plain.PlainLoginModule required username=\\"xxxxxx\\" password=\\"xxxxxx\\";"
|
||||
}`,
|
||||
rows: 8,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'jmxProperties',
|
||||
label: 'JMX认证',
|
||||
type: 'text_area',
|
||||
rules: [{
|
||||
required: false,
|
||||
message: '请输入JMX认证',
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: `请输入JMX认证,例如:
|
||||
{
|
||||
"maxConn": 10, #KM对单台Broker的最大jmx连接数
|
||||
"username": "xxxxx", #用户名
|
||||
"password": "xxxxx", #密码
|
||||
"openSSL": true, #开启SSL,true表示开启SSL,false表示关闭
|
||||
}`,
|
||||
rows: 8,
|
||||
},
|
||||
@@ -256,32 +277,41 @@ export class ClusterList extends SearchAndFilterContainer {
|
||||
|
||||
public getColumns = () => {
|
||||
const cols = getAdminClusterColumns();
|
||||
const role = users.currentUser.role;
|
||||
const col = {
|
||||
title: '操作',
|
||||
render: (value: string, item: IMetaData) => (
|
||||
<>
|
||||
<a
|
||||
onClick={this.createOrRegisterCluster.bind(this, item)}
|
||||
className="action-button"
|
||||
>编辑
|
||||
</a>
|
||||
<Popconfirm
|
||||
title={`确定${item.status === 1 ? '暂停' : '开始'}${item.clusterName}监控?`}
|
||||
onConfirm={() => this.pauseMonitor(item)}
|
||||
cancelText="取消"
|
||||
okText="确认"
|
||||
>
|
||||
<Tooltip title="暂停监控将无法正常监控指标信息,建议开启监控">
|
||||
{
|
||||
role && role === 2 ? <>
|
||||
<a
|
||||
onClick={this.createOrRegisterCluster.bind(this, item)}
|
||||
className="action-button"
|
||||
>
|
||||
{item.status === 1 ? '暂停监控' : '开始监控'}
|
||||
>编辑
|
||||
</a>
|
||||
</Tooltip>
|
||||
</Popconfirm>
|
||||
<a onClick={this.showMonitor.bind(this, item)}>
|
||||
删除
|
||||
</a>
|
||||
<Popconfirm
|
||||
title={`确定${item.status === 1 ? '暂停' : '开始'}${item.clusterName}监控?`}
|
||||
onConfirm={() => this.pauseMonitor(item)}
|
||||
cancelText="取消"
|
||||
okText="确认"
|
||||
>
|
||||
<Tooltip placement="left" title="暂停监控将无法正常监控指标信息,建议开启监控">
|
||||
<a
|
||||
className="action-button"
|
||||
>
|
||||
{item.status === 1 ? '暂停监控' : '开始监控'}
|
||||
</a>
|
||||
</Tooltip>
|
||||
</Popconfirm>
|
||||
<a onClick={this.showMonitor.bind(this, item)}>
|
||||
删除
|
||||
</a>
|
||||
</> : <Tooltip placement="left" title="该功能只对运维人员开放">
|
||||
<a style={{ color: '#a0a0a0' }} className="action-button">编辑</a>
|
||||
<a className="action-button" style={{ color: '#a0a0a0' }}>{item.status === 1 ? '暂停监控' : '开始监控'}</a>
|
||||
<a style={{ color: '#a0a0a0' }}>删除</a>
|
||||
</Tooltip>
|
||||
}
|
||||
</>
|
||||
),
|
||||
};
|
||||
@@ -290,6 +320,7 @@ export class ClusterList extends SearchAndFilterContainer {
|
||||
}
|
||||
|
||||
public renderClusterList() {
|
||||
const role = users.currentUser.role;
|
||||
return (
|
||||
<>
|
||||
<div className="container">
|
||||
@@ -298,7 +329,14 @@ export class ClusterList extends SearchAndFilterContainer {
|
||||
{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>
|
||||
{
|
||||
role && role === 2 ?
|
||||
<Button type="primary" onClick={this.createOrRegisterCluster.bind(this, null)}>接入集群</Button>
|
||||
:
|
||||
<Tooltip placement="left" title="该功能只对运维人员开放" trigger='hover'>
|
||||
<Button disabled type="primary">接入集群</Button>
|
||||
</Tooltip>
|
||||
}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import { IUser, IUploadFile, IConfigure, IMetaData, IBrokersPartitions } from 'types/base-type';
|
||||
import { IUser, IUploadFile, IConfigure, IConfigGateway, IMetaData } from 'types/base-type';
|
||||
import { users } from 'store/users';
|
||||
import { version } from 'store/version';
|
||||
import { showApplyModal, showModifyModal, showConfigureModal } from 'container/modal/admin';
|
||||
import { showApplyModal, showApplyModalModifyPassword, showModifyModal, showConfigureModal, showConfigGatewayModal } from 'container/modal/admin';
|
||||
import { Popconfirm, Tooltip } from 'component/antd';
|
||||
import { admin } from 'store/admin';
|
||||
import { cellStyle } from 'constants/table';
|
||||
@@ -27,6 +27,7 @@ export const getUserColumns = () => {
|
||||
return (
|
||||
<span className="table-operation">
|
||||
<a onClick={() => showApplyModal(record)}>编辑</a>
|
||||
<a onClick={() => showApplyModalModifyPassword(record)}>修改密码</a>
|
||||
<Popconfirm
|
||||
title="确定删除?"
|
||||
onConfirm={() => users.deleteUser(record.username)}
|
||||
@@ -184,6 +185,87 @@ export const getConfigureColumns = () => {
|
||||
return columns;
|
||||
};
|
||||
|
||||
// 网关配置
|
||||
export const getConfigColumns = () => {
|
||||
const columns = [
|
||||
{
|
||||
title: '配置类型',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
width: '25%',
|
||||
ellipsis: true,
|
||||
sorter: (a: IConfigGateway, b: IConfigGateway) => a.type.charCodeAt(0) - b.type.charCodeAt(0),
|
||||
},
|
||||
{
|
||||
title: '配置键',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: '15%',
|
||||
ellipsis: true,
|
||||
sorter: (a: IConfigGateway, b: IConfigGateway) => a.name.charCodeAt(0) - b.name.charCodeAt(0),
|
||||
},
|
||||
{
|
||||
title: '配置值',
|
||||
dataIndex: 'value',
|
||||
key: 'value',
|
||||
width: '20%',
|
||||
ellipsis: true,
|
||||
sorter: (a: IConfigGateway, b: IConfigGateway) => a.value.charCodeAt(0) - b.value.charCodeAt(0),
|
||||
render: (t: string) => {
|
||||
return t.substr(0, 1) === '{' && t.substr(0, -1) === '}' ? JSON.stringify(JSON.parse(t), null, 4) : t;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '修改时间',
|
||||
dataIndex: 'modifyTime',
|
||||
key: 'modifyTime',
|
||||
width: '15%',
|
||||
sorter: (a: IConfigGateway, b: IConfigGateway) => b.modifyTime - a.modifyTime,
|
||||
render: (t: number) => moment(t).format(timeFormat),
|
||||
},
|
||||
{
|
||||
title: '版本号',
|
||||
dataIndex: 'version',
|
||||
key: 'version',
|
||||
width: '10%',
|
||||
ellipsis: true,
|
||||
sorter: (a: IConfigGateway, b: IConfigGateway) => b.version.charCodeAt(0) - a.version.charCodeAt(0),
|
||||
},
|
||||
{
|
||||
title: '描述信息',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
width: '20%',
|
||||
ellipsis: true,
|
||||
onCell: () => ({
|
||||
style: {
|
||||
maxWidth: 180,
|
||||
...cellStyle,
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: '10%',
|
||||
render: (text: string, record: IConfigGateway) => {
|
||||
return (
|
||||
<span className="table-operation">
|
||||
<a onClick={() => showConfigGatewayModal(record)}>编辑</a>
|
||||
<Popconfirm
|
||||
title="确定删除?"
|
||||
onConfirm={() => admin.deleteConfigGateway({ id: record.id })}
|
||||
cancelText="取消"
|
||||
okText="确认"
|
||||
>
|
||||
<a>删除</a>
|
||||
</Popconfirm>
|
||||
</span>);
|
||||
},
|
||||
},
|
||||
];
|
||||
return columns;
|
||||
};
|
||||
|
||||
const renderClusterHref = (value: number | string, item: IMetaData, key: number) => {
|
||||
return ( // 0 暂停监控--不可点击 1 监控中---可正常点击
|
||||
<>
|
||||
|
||||
@@ -3,11 +3,11 @@ import { SearchAndFilterContainer } from 'container/search-filter';
|
||||
import { Table, Button, Spin } from 'component/antd';
|
||||
import { admin } from 'store/admin';
|
||||
import { observer } from 'mobx-react';
|
||||
import { IConfigure } from 'types/base-type';
|
||||
import { IConfigure, IConfigGateway } from 'types/base-type';
|
||||
import { users } from 'store/users';
|
||||
import { pagination } from 'constants/table';
|
||||
import { getConfigureColumns } from './config';
|
||||
import { showConfigureModal } from 'container/modal/admin';
|
||||
import { getConfigureColumns, getConfigColumns } from './config';
|
||||
import { showConfigureModal, showConfigGatewayModal } from 'container/modal/admin';
|
||||
|
||||
@observer
|
||||
export class ConfigureManagement extends SearchAndFilterContainer {
|
||||
@@ -17,7 +17,12 @@ export class ConfigureManagement extends SearchAndFilterContainer {
|
||||
};
|
||||
|
||||
public componentDidMount() {
|
||||
admin.getConfigure();
|
||||
if (this.props.isShow) {
|
||||
admin.getGatewayList();
|
||||
admin.getGatewayType();
|
||||
} else {
|
||||
admin.getConfigure();
|
||||
}
|
||||
}
|
||||
|
||||
public getData<T extends IConfigure>(origin: T[]) {
|
||||
@@ -34,15 +39,34 @@ export class ConfigureManagement extends SearchAndFilterContainer {
|
||||
return data;
|
||||
}
|
||||
|
||||
public getGatewayData<T extends IConfigGateway>(origin: T[]) {
|
||||
let data: T[] = origin;
|
||||
let { searchKey } = this.state;
|
||||
searchKey = (searchKey + '').trim().toLowerCase();
|
||||
|
||||
data = searchKey ? origin.filter((item: IConfigGateway) =>
|
||||
((item.name !== undefined && item.name !== null) && item.name.toLowerCase().includes(searchKey as string))
|
||||
|| ((item.value !== undefined && item.value !== null) && item.value.toLowerCase().includes(searchKey as string))
|
||||
|| ((item.description !== undefined && item.description !== null) &&
|
||||
item.description.toLowerCase().includes(searchKey as string)),
|
||||
) : origin;
|
||||
return data;
|
||||
}
|
||||
|
||||
public renderTable() {
|
||||
return (
|
||||
<Spin spinning={users.loading}>
|
||||
<Table
|
||||
{this.props.isShow ? <Table
|
||||
rowKey="key"
|
||||
columns={getConfigColumns()}
|
||||
dataSource={this.getGatewayData(admin.configGatewayList)}
|
||||
pagination={pagination}
|
||||
/> : <Table
|
||||
rowKey="key"
|
||||
columns={getConfigureColumns()}
|
||||
dataSource={this.getData(admin.configureList)}
|
||||
pagination={pagination}
|
||||
/>
|
||||
/>}
|
||||
</Spin>
|
||||
|
||||
);
|
||||
@@ -53,7 +77,7 @@ export class ConfigureManagement extends SearchAndFilterContainer {
|
||||
<ul>
|
||||
{this.renderSearch('', '请输入配置键、值或描述')}
|
||||
<li className="right-btn-1">
|
||||
<Button type="primary" onClick={() => showConfigureModal()}>增加配置</Button>
|
||||
<Button type="primary" onClick={() => this.props.isShow ? showConfigGatewayModal() : showConfigureModal()}>增加配置</Button>
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
|
||||
@@ -6,6 +6,7 @@ import { curveKeys, CURVE_KEY_MAP, PERIOD_RADIO_MAP, PERIOD_RADIO } from './conf
|
||||
import moment = require('moment');
|
||||
import { observer } from 'mobx-react';
|
||||
import { timeStampStr } from 'constants/strategy';
|
||||
import { adminMonitor } from 'store/admin-monitor';
|
||||
|
||||
@observer
|
||||
export class DataCurveFilter extends React.Component {
|
||||
@@ -21,6 +22,7 @@ export class DataCurveFilter extends React.Component {
|
||||
}
|
||||
|
||||
public refreshAll = () => {
|
||||
adminMonitor.setRequestId(null);
|
||||
Object.keys(curveKeys).forEach((c: curveKeys) => {
|
||||
const { typeInfo, curveInfo: option } = CURVE_KEY_MAP.get(c);
|
||||
const { parser } = typeInfo;
|
||||
@@ -32,7 +34,7 @@ export class DataCurveFilter extends React.Component {
|
||||
return (
|
||||
<>
|
||||
<Radio.Group onChange={this.radioChange} defaultValue={curveInfo.periodKey}>
|
||||
{PERIOD_RADIO.map(p => <Radio.Button key={p.key} value={p.key}>{p.label}</Radio.Button>)}
|
||||
{PERIOD_RADIO.map(p => <Radio.Button key={p.key} value={p.key}>{p.label}</Radio.Button>)}
|
||||
</Radio.Group>
|
||||
<DatePicker.RangePicker
|
||||
format={timeStampStr}
|
||||
|
||||
@@ -11,3 +11,5 @@ export * from './operation-management/migration-detail';
|
||||
export * from './configure-management';
|
||||
export * from './individual-bill';
|
||||
export * from './bill-detail';
|
||||
export * from './operation-record';
|
||||
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
import * as React from 'react';
|
||||
import { cellStyle } from 'constants/table';
|
||||
|
||||
import { Tooltip } from 'antd';
|
||||
import { admin } from 'store/admin';
|
||||
import moment = require('moment');
|
||||
|
||||
const moduleList = [
|
||||
{ moduleId: 0, moduleName: 'Topic' },
|
||||
{ moduleId: 1, moduleName: '应用' },
|
||||
{ moduleId: 2, moduleName: '配额' },
|
||||
{ moduleId: 3, moduleName: '权限' },
|
||||
{ moduleId: 4, moduleName: '集群' },
|
||||
{ moduleId: 5, moduleName: '分区' },
|
||||
{ moduleId: 6, moduleName: 'Gateway配置' },
|
||||
]
|
||||
|
||||
export const operateList = {
|
||||
0: '新增',
|
||||
1: '删除',
|
||||
2: '修改'
|
||||
}
|
||||
|
||||
|
||||
// [
|
||||
// { operate: '新增', operateId: 0 },
|
||||
// { operate: '删除', operateId: 1 },
|
||||
// { operate: '修改', operateId: 2 },
|
||||
// ]
|
||||
|
||||
export const getJarFuncForm: any = (props: any) => {
|
||||
const formMap = [
|
||||
{
|
||||
key: 'moduleId',
|
||||
label: '模块',
|
||||
type: 'select',
|
||||
attrs: {
|
||||
style: {
|
||||
width: '130px'
|
||||
},
|
||||
placeholder: '请选择模块',
|
||||
},
|
||||
options: moduleList.map(item => {
|
||||
return {
|
||||
label: item.moduleName,
|
||||
value: item.moduleId
|
||||
}
|
||||
}),
|
||||
formAttrs: {
|
||||
initialvalue: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'operator',
|
||||
label: '操作人',
|
||||
type: 'input',
|
||||
attrs: {
|
||||
style: {
|
||||
width: '170px'
|
||||
},
|
||||
placeholder: '请输入操作人'
|
||||
},
|
||||
getvaluefromevent: (event: any) => {
|
||||
return event.target.value.replace(/\s+/g, '')
|
||||
},
|
||||
},
|
||||
// {
|
||||
// key: 'resource',
|
||||
// label: '资源名称',
|
||||
// type: 'input',
|
||||
// attrs: {
|
||||
// style: {
|
||||
// width: '170px'
|
||||
// },
|
||||
// placeholder: '请输入资源名称'
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// key: 'content',
|
||||
// label: '操作内容',
|
||||
// type: 'input',
|
||||
// attrs: {
|
||||
// style: {
|
||||
// width: '170px'
|
||||
// },
|
||||
// placeholder: '请输入操作内容'
|
||||
// },
|
||||
// },
|
||||
]
|
||||
return formMap;
|
||||
}
|
||||
export const getOperateColumns = () => {
|
||||
|
||||
const columns: any = [
|
||||
{
|
||||
title: '模块',
|
||||
dataIndex: 'module',
|
||||
key: 'module',
|
||||
align: 'center',
|
||||
width: '12%'
|
||||
},
|
||||
{
|
||||
title: '资源名称',
|
||||
dataIndex: 'resource',
|
||||
key: 'resource',
|
||||
align: 'center',
|
||||
width: '12%'
|
||||
},
|
||||
{
|
||||
title: '操作内容',
|
||||
dataIndex: 'content',
|
||||
key: 'content',
|
||||
align: 'center',
|
||||
width: '25%',
|
||||
onCell: () => ({
|
||||
style: {
|
||||
maxWidth: 350,
|
||||
...cellStyle,
|
||||
},
|
||||
}),
|
||||
render: (text: string, record: any) => {
|
||||
return (
|
||||
<Tooltip placement="topLeft" title={text} >{text}</Tooltip>);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作人',
|
||||
dataIndex: 'operator',
|
||||
align: 'center',
|
||||
width: '12%'
|
||||
},
|
||||
];
|
||||
return columns
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import * as React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { SearchAndFilterContainer } from 'container/search-filter';
|
||||
import { IXFormWrapper, IMetaData, IRegister } from 'types/base-type';
|
||||
import { admin } from 'store/admin';
|
||||
import { customPagination, cellStyle } from 'constants/table';
|
||||
import { Table, Tooltip } from 'component/antd';
|
||||
import { timeFormat } from 'constants/strategy';
|
||||
import { SearchFormComponent } from '../searchForm';
|
||||
import { getJarFuncForm, operateList, getOperateColumns } from './config'
|
||||
import moment = require('moment');
|
||||
import { tableFilter } from 'lib/utils';
|
||||
|
||||
@observer
|
||||
export class OperationRecord extends SearchAndFilterContainer {
|
||||
public state: any = {
|
||||
searchKey: '',
|
||||
filteredInfo: null,
|
||||
sortedInfo: null,
|
||||
};
|
||||
|
||||
public getData<T extends IMetaData>(origin: T[]) {
|
||||
let data: T[] = origin;
|
||||
let { searchKey } = this.state;
|
||||
searchKey = (searchKey + '').trim().toLowerCase();
|
||||
|
||||
data = searchKey ? origin.filter((item: IMetaData) =>
|
||||
(item.clusterName !== undefined && item.clusterName !== null) && item.clusterName.toLowerCase().includes(searchKey as string),
|
||||
) : origin;
|
||||
return data;
|
||||
};
|
||||
|
||||
public searchForm = (params: any) => {
|
||||
// this.props.setFuncSubValue(params)
|
||||
// getSystemFuncList(params).then(res => {
|
||||
// this.props.setSysFuncList(res.data)
|
||||
// this.props.setPagination(res.pagination)
|
||||
// })
|
||||
const { operator, moduleId } = params || {}
|
||||
operator ? admin.getOperationRecordData(params) : admin.getOperationRecordData({ moduleId })
|
||||
// getJarList(params).then(res => {
|
||||
// this.props.setJarList(res.data)
|
||||
// this.props.setPagination(res.pagination)
|
||||
// })
|
||||
}
|
||||
|
||||
public clearAll = () => {
|
||||
this.setState({
|
||||
filteredInfo: null,
|
||||
sortedInfo: null,
|
||||
});
|
||||
};
|
||||
|
||||
public setHandleChange = (pagination: any, filters: any, sorter: any) => {
|
||||
this.setState({
|
||||
filteredInfo: filters,
|
||||
sortedInfo: sorter,
|
||||
});
|
||||
}
|
||||
|
||||
public renderOperationRecordList() {
|
||||
let { sortedInfo, filteredInfo } = this.state;
|
||||
sortedInfo = sortedInfo || {};
|
||||
filteredInfo = filteredInfo || {};
|
||||
const operatingTime = Object.assign({
|
||||
title: '操作时间',
|
||||
dataIndex: 'modifyTime',
|
||||
key: 'modifyTime',
|
||||
align: 'center',
|
||||
sorter: (a: any, b: any) => a.modifyTime - b.modifyTime,
|
||||
render: (t: number) => moment(t).format(timeFormat),
|
||||
width: '15%',
|
||||
sortOrder: sortedInfo.columnKey === 'modifyTime' && sortedInfo.order,
|
||||
});
|
||||
|
||||
const operatingPractice = Object.assign({
|
||||
title: '行为',
|
||||
dataIndex: 'operate',
|
||||
key: 'operate',
|
||||
align: 'center',
|
||||
width: '12%',
|
||||
filters: tableFilter<any>(this.getData(admin.oRList), 'operateId', operateList),
|
||||
// filteredValue: filteredInfo.operate || null,
|
||||
onFilter: (value: any, record: any) => {
|
||||
return record.operateId === value
|
||||
}
|
||||
}, this.renderColumnsFilter('modifyTime'))
|
||||
|
||||
const columns = getOperateColumns()
|
||||
columns.splice(0, 0, operatingTime);
|
||||
columns.splice(3, 0, operatingPractice);
|
||||
return (
|
||||
<>
|
||||
<div className="container">
|
||||
<div className="table-operation-panel">
|
||||
<SearchFormComponent
|
||||
formMap={getJarFuncForm()}
|
||||
onSubmit={(params: any) => this.searchForm(params)}
|
||||
clearAll={() => this.clearAll()}
|
||||
isReset={true}
|
||||
/>
|
||||
</div>
|
||||
<div className="table-wrapper">
|
||||
<Table
|
||||
rowKey="key"
|
||||
loading={admin.loading}
|
||||
dataSource={this.getData(admin.oRList)}
|
||||
columns={columns}
|
||||
pagination={customPagination}
|
||||
bordered
|
||||
onChange={this.setHandleChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
admin.getOperationRecordData({ moduleId: 0 });
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>
|
||||
{
|
||||
this.renderOperationRecordList()
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@@ -13,17 +13,20 @@ export class PlatformManagement extends React.Component {
|
||||
public render() {
|
||||
return (
|
||||
<>
|
||||
<Tabs activeKey={location.hash.substr(1) || '1'} type="card" onChange={handleTabKey}>
|
||||
<TabPane tab="应用管理" key="1">
|
||||
<AdminAppList />
|
||||
</TabPane>
|
||||
<TabPane tab="用户管理" key="2">
|
||||
<UserManagement />
|
||||
</TabPane>
|
||||
<TabPane tab="配置管理" key="3">
|
||||
<ConfigureManagement />
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
<Tabs activeKey={location.hash.substr(1) || '1'} type="card" onChange={handleTabKey}>
|
||||
<TabPane tab="应用管理" key="1">
|
||||
<AdminAppList />
|
||||
</TabPane>
|
||||
<TabPane tab="用户管理" key="2">
|
||||
<UserManagement />
|
||||
</TabPane>
|
||||
<TabPane tab="平台配置" key="3">
|
||||
<ConfigureManagement isShow={false} />
|
||||
</TabPane>
|
||||
<TabPane tab="网关配置" key="4">
|
||||
<ConfigureManagement isShow={true} />
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
120
kafka-manager-console/src/container/admin/searchForm.tsx
Normal file
120
kafka-manager-console/src/container/admin/searchForm.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
import * as React from 'react';
|
||||
import { Select, Input, InputNumber, Form, Switch, Checkbox, DatePicker, Radio, Upload, Button, Icon, Tooltip } from 'component/antd';
|
||||
// import './index.less';
|
||||
const Search = Input.Search;
|
||||
export interface IFormItem {
|
||||
key: string;
|
||||
label: string;
|
||||
type: string;
|
||||
value?: string;
|
||||
// 内部组件属性注入
|
||||
attrs?: any;
|
||||
// form属性注入
|
||||
formAttrs?: any;
|
||||
defaultValue?: string | number | any[];
|
||||
rules?: any[];
|
||||
invisible?: boolean;
|
||||
getvaluefromevent: Function;
|
||||
}
|
||||
|
||||
interface SerachFormProps {
|
||||
formMap: IFormItem[];
|
||||
// formData: any;
|
||||
form: any;
|
||||
onSubmit: Function;
|
||||
isReset?: boolean;
|
||||
clearAll: Function;
|
||||
layout?: 'inline' | 'horizontal' | 'vertical';
|
||||
}
|
||||
|
||||
export interface IFormSelect extends IFormItem {
|
||||
options: Array<{ key?: string | number, value: string | number, label: string }>;
|
||||
}
|
||||
|
||||
class SearchForm extends React.Component<SerachFormProps>{
|
||||
public onSubmit = () => {
|
||||
// this.props.onSubmit()
|
||||
//
|
||||
}
|
||||
|
||||
public renderFormItem(item: IFormItem) {
|
||||
switch (item.type) {
|
||||
default:
|
||||
case 'input':
|
||||
return <Input key={item.key} {...item.attrs} />;
|
||||
case 'select':
|
||||
return (
|
||||
<Select
|
||||
// size="small"
|
||||
key={item.key}
|
||||
{...item.attrs}
|
||||
invisibleValue={item.formAttrs.invisibleValue}
|
||||
>
|
||||
{(item as IFormSelect).options && (item as IFormSelect).options.map((v, index) => (
|
||||
|
||||
<Select.Option
|
||||
key={v.value || v.key || index}
|
||||
value={v.value}
|
||||
>
|
||||
{v.label}
|
||||
{/* <Tooltip placement='left' title={v.value}>
|
||||
{v.label}
|
||||
</Tooltip> */}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public theQueryClick = (value: any) => {
|
||||
this.props.onSubmit(value)
|
||||
this.props.clearAll()
|
||||
// this.props.form.resetFields()
|
||||
}
|
||||
public resetClick = () => {
|
||||
this.props.form.resetFields()
|
||||
this.props.clearAll()
|
||||
this.theQueryClick(this.props.form.getFieldsValue())
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { form, formMap, isReset } = this.props;
|
||||
const { getFieldDecorator, getFieldsValue } = form;
|
||||
return (
|
||||
<Form layout='inline' onSubmit={this.onSubmit}>
|
||||
{
|
||||
formMap.map(formItem => {
|
||||
// const { initialValue, valuePropName } = this.handleFormItem(formItem, formData);
|
||||
// const getFieldValue = {
|
||||
// initialValue,
|
||||
// rules: formItem.rules || [{ required: false, message: '' }],
|
||||
// valuePropName,
|
||||
// };
|
||||
return (
|
||||
<Form.Item
|
||||
key={formItem.key}
|
||||
label={formItem.label}
|
||||
{...formItem.formAttrs}
|
||||
>
|
||||
{getFieldDecorator(formItem.key, {
|
||||
initialValue: formItem.formAttrs?.initialvalue,
|
||||
getValueFromEvent: formItem?.getvaluefromevent,
|
||||
})(
|
||||
this.renderFormItem(formItem),
|
||||
)}
|
||||
</Form.Item>
|
||||
);
|
||||
})
|
||||
}
|
||||
<Form.Item>
|
||||
{
|
||||
isReset && <Button style={{ width: '80px', marginRight: '20px' }} type="primary" onClick={() => this.resetClick()}>重置</Button>
|
||||
}
|
||||
<Button style={{ width: '80px' }} type="primary" onClick={() => this.theQueryClick(getFieldsValue())}>查询</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
}
|
||||
export const SearchFormComponent = Form.create<SerachFormProps>({ name: 'search-form' })(SearchForm);
|
||||
@@ -29,7 +29,7 @@ export class UserManagement extends SearchAndFilterContainer {
|
||||
searchKey = (searchKey + '').trim().toLowerCase();
|
||||
|
||||
data = searchKey ? origin.filter((item: IUser) =>
|
||||
(item.username !== undefined && item.username !== null) && item.username.toLowerCase().includes(searchKey as string)) : origin ;
|
||||
(item.username !== undefined && item.username !== null) && item.username.toLowerCase().includes(searchKey as string)) : origin;
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user