mirror of
https://github.com/didi/KnowStreaming.git
synced 2026-01-04 20:02:07 +08:00
kafka-manager 2.0
This commit is contained in:
277
kafka-manager-console/src/container/modal/admin/cluster.ts
Normal file
277
kafka-manager-console/src/container/modal/admin/cluster.ts
Normal file
@@ -0,0 +1,277 @@
|
||||
import { notification } from 'component/antd';
|
||||
import { wrapper } from 'store';
|
||||
import { IClusterTopics, IEditTopic, IConfigInfo, ILogicalCluster, INewLogical, IMetaData, IBrokersRegions, INewRegions } from 'types/base-type';
|
||||
import { editTopic } from 'lib/api';
|
||||
import { transMSecondToHour, transHourToMSecond } from 'lib/utils';
|
||||
import { cluster } from 'store/cluster';
|
||||
import { admin } from 'store/admin';
|
||||
import { app } from 'store/app';
|
||||
|
||||
export const showEditClusterTopic = (item: IClusterTopics) => {
|
||||
const xFormModal = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'clusterName',
|
||||
label: '集群名称',
|
||||
rules: [{
|
||||
required: true,
|
||||
}],
|
||||
attrs: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'appId',
|
||||
label: '应用ID',
|
||||
rules: [{
|
||||
required: true,
|
||||
}],
|
||||
attrs: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'topicName',
|
||||
label: 'Topic名称',
|
||||
rules: [{
|
||||
required: true,
|
||||
}],
|
||||
attrs: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'retentionTime',
|
||||
label: '保存时间',
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入保存时间',
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请输入保存时间',
|
||||
suffix: '小时',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'properties',
|
||||
label: 'Topic属性列表',
|
||||
type: 'text_area',
|
||||
rules: [{ required: false}],
|
||||
attrs: {
|
||||
placeholder: '请输入Topic属性列表',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
label: '备注',
|
||||
type: 'text_area',
|
||||
rules: [{
|
||||
required: false,
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请输入备注',
|
||||
},
|
||||
},
|
||||
],
|
||||
formData: {
|
||||
clusterName: item.clusterName,
|
||||
appId: item.appId,
|
||||
topicName: item.topicName,
|
||||
retentionTime: transMSecondToHour(item.retentionTime),
|
||||
properties: JSON.stringify(item.properties, null, 4),
|
||||
description: item.description,
|
||||
},
|
||||
visible: true,
|
||||
title: 'Topic编辑',
|
||||
onSubmit: (value: IEditTopic) => {
|
||||
value.clusterId = item.clusterId;
|
||||
value.properties = value.properties ? JSON.parse(value.properties) : {};
|
||||
value.retentionTime = transHourToMSecond(value.retentionTime);
|
||||
editTopic(value).then(data => {
|
||||
notification.success({ message: '编辑Topic成功' });
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
|
||||
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,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'appId',
|
||||
label: '所属应用',
|
||||
rules: [{ required: true, message: '请选择所属应用' }],
|
||||
type: 'select',
|
||||
options: app.adminAppData.map(item => {
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.appId,
|
||||
};
|
||||
}),
|
||||
attrs: {
|
||||
placeholder: '请选择所属应用',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'mode',
|
||||
label: '集群模式',
|
||||
type: 'select',
|
||||
rules: [{ required: true, message: '请选择集群模式' }],
|
||||
options: clusterModes.map(item => {
|
||||
return {
|
||||
label: item.message,
|
||||
value: item.code,
|
||||
};
|
||||
}),
|
||||
attrs: {
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'regionIdList',
|
||||
label: 'RegionIdList',
|
||||
type: 'select',
|
||||
defaultValue: [] as any,
|
||||
options: admin.brokersRegions.map(item => {
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
};
|
||||
}),
|
||||
rules: [{ required: true, message: '请选择BrokerIdList' }],
|
||||
attrs: {
|
||||
mode: 'multiple',
|
||||
placeholder: '请选择BrokerIdList',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
label: '备注',
|
||||
type: 'text_area',
|
||||
rules: [{
|
||||
required: false,
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请输入备注',
|
||||
},
|
||||
},
|
||||
],
|
||||
formData: record,
|
||||
visible: true,
|
||||
title: '新增逻辑集群',
|
||||
onSubmit: (value: INewLogical) => {
|
||||
const params = {
|
||||
appId: value.appId,
|
||||
clusterId,
|
||||
description: value.description,
|
||||
id: record ? record.logicalClusterId : '',
|
||||
mode: value.mode,
|
||||
name: value.logicalClusterName,
|
||||
regionIdList: value.regionIdList,
|
||||
} as INewLogical;
|
||||
if (record) {
|
||||
return admin.editLogicalClusters(clusterId, params).then(data => {
|
||||
notification.success({ message: '编辑逻辑集群成功' });
|
||||
});
|
||||
}
|
||||
return admin.createLogicalClusters(clusterId, params).then(data => {
|
||||
notification.success({ message: '新建逻辑集群成功' });
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
|
||||
export const showClusterRegionOpModal = (clusterId: number, content: IMetaData, record?: IBrokersRegions) => {
|
||||
const xFormModal = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'Region名称',
|
||||
rules: [{ required: true, message: '请输入Region名称' }],
|
||||
attrs: { placeholder: '请输入Region名称' },
|
||||
},
|
||||
{
|
||||
key: 'clusterName',
|
||||
label: '集群名称',
|
||||
rules: [{ required: true, message: '请输入集群名称' }],
|
||||
defaultValue: content.clusterName,
|
||||
attrs: {
|
||||
disabled: true,
|
||||
placeholder: '请输入集群名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'brokerIdList',
|
||||
label: 'Broker列表',
|
||||
defaultValue: record ? record.brokerIdList.join(',') : [] as any,
|
||||
rules: [{ required: true, message: '请输入BrokerIdList' }],
|
||||
attrs: {
|
||||
placeholder: '请输入BrokerIdList',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
label: '状态',
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: '正常',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: '容量已满',
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
defaultValue: 0,
|
||||
rules: [{ required: true, message: '请选择状态' }],
|
||||
attrs: {
|
||||
placeholder: '请选择状态',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
label: '备注',
|
||||
type: 'text_area',
|
||||
rules: [{
|
||||
required: false,
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请输入备注',
|
||||
},
|
||||
},
|
||||
],
|
||||
formData: record,
|
||||
visible: true,
|
||||
title: `${record ? '编辑' : '新增Region'}`,
|
||||
onSubmit: (value: INewRegions) => {
|
||||
value.clusterId = clusterId;
|
||||
value.brokerIdList = value.brokerIdList && Array.isArray(value.brokerIdList) ?
|
||||
value.brokerIdList : value.brokerIdList.split(',');
|
||||
if (record) {
|
||||
value.id = record.id;
|
||||
}
|
||||
delete value.clusterName;
|
||||
if (record) {
|
||||
return admin.editRegions(clusterId, value).then(data => {
|
||||
notification.success({ message: '编辑Region成功' });
|
||||
});
|
||||
}
|
||||
return admin.addNewRegions(clusterId, value).then(data => {
|
||||
notification.success({ message: '新建Region成功' });
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
@@ -0,0 +1,149 @@
|
||||
import * as React from 'react';
|
||||
import { admin } from 'store/admin';
|
||||
import { notification, Modal, Form, Input, Switch, Select, Tooltip } 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;
|
||||
}
|
||||
|
||||
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) => {
|
||||
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() {
|
||||
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="Topic扩分区"
|
||||
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>
|
||||
<Form.Item label="分区数" >
|
||||
{getFieldDecorator('partitionNum', {
|
||||
rules: [{ required: true,
|
||||
message: '请输入分区数' }],
|
||||
})(<Input placeholder="请输入分区数" />)}
|
||||
</Form.Item>
|
||||
<Form.Item label={this.state.checked ? 'Region类型' : 'Borker类型'} >
|
||||
<Switch onChange={(checked) => this.onSwitchChange(checked)} />
|
||||
</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 ExpandPartitionFormWrapper = Form.create<IXFormProps>()(CustomForm);
|
||||
5
kafka-manager-console/src/container/modal/admin/index.ts
Normal file
5
kafka-manager-console/src/container/modal/admin/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from './user';
|
||||
export * from './version';
|
||||
export * from './cluster';
|
||||
export * from './task';
|
||||
export * from './migration';
|
||||
@@ -0,0 +1,186 @@
|
||||
import * as React from 'react';
|
||||
import { Table, notification, Button, Modal, Input, Form, Select, message, Tooltip } from 'component/antd';
|
||||
import { IBrokersMetadata, IRebalance } from 'types/base-type';
|
||||
import { admin } from 'store/admin';
|
||||
import { implementRegions, rebalanceStatus } from 'lib/api';
|
||||
import { searchProps } from 'constants/table';
|
||||
|
||||
interface IXFormProps {
|
||||
form: any;
|
||||
changeVisible?: (visible: boolean) => any;
|
||||
visible?: boolean;
|
||||
clusterId?: number;
|
||||
clusterName?: string;
|
||||
}
|
||||
|
||||
const layout = {
|
||||
labelCol: { span: 6 },
|
||||
wrapperCol: { span: 15 },
|
||||
};
|
||||
|
||||
class LeaderRebalanceModal extends React.Component<IXFormProps> {
|
||||
public brokerId: number;
|
||||
public host: string;
|
||||
public metadata = [] as IBrokersMetadata[];
|
||||
public timer = null as any;
|
||||
|
||||
public state = {
|
||||
imVisible: false,
|
||||
status: '',
|
||||
};
|
||||
|
||||
public handleRebalanceCancel() {
|
||||
this.props.changeVisible(false);
|
||||
this.props.form.resetFields();
|
||||
this.setState({ imVisible: false });
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
|
||||
public onMetaChange(value: number) {
|
||||
this.brokerId = value;
|
||||
this.metadata.forEach((element: IBrokersMetadata) => {
|
||||
if (element.brokerId === value) {
|
||||
this.host = element.host;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public handleSubmit = (e: any) => {
|
||||
e.preventDefault();
|
||||
this.props.form.validateFields((err: any, values: any) => {
|
||||
if (!err) {
|
||||
let params = {} as IRebalance;
|
||||
params = {
|
||||
clusterId: this.props.clusterId,
|
||||
brokerId: values.brokerId,
|
||||
dimension: 2,
|
||||
regionId: 0,
|
||||
topicName: '',
|
||||
};
|
||||
implementRegions(params).then(data => {
|
||||
message.success('获取成功');
|
||||
this.getStatus();
|
||||
this.setState({
|
||||
imVisible: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 定时器
|
||||
public iTimer = () => {
|
||||
this.timer = setInterval(() => {
|
||||
this.getStatus();
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
public getStatus() {
|
||||
rebalanceStatus(this.props.clusterId).then((data: any) => {
|
||||
message.success('状态更新成功');
|
||||
if (data.code === 30) { // code -1 未知 101 成功 30 运行中
|
||||
setTimeout(this.iTimer, 0);
|
||||
} else {
|
||||
this.setState({ status: data.message });
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 组件清除时清除定时器
|
||||
public componentWillUnmount() {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { visible } = this.props;
|
||||
const reblanceData = [
|
||||
{
|
||||
clusterName: this.props.clusterName,
|
||||
host: this.host,
|
||||
status: this.state.status,
|
||||
},
|
||||
];
|
||||
const columns = [
|
||||
{
|
||||
title: '集群名称',
|
||||
dataIndex: 'clusterName',
|
||||
key: 'clusterName',
|
||||
},
|
||||
{
|
||||
title: 'BrokerHost',
|
||||
dataIndex: 'host',
|
||||
key: 'host',
|
||||
},
|
||||
{
|
||||
title: 'Status',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
},
|
||||
];
|
||||
this.metadata = admin.brokersMetadata ? admin.brokersMetadata : this.metadata;
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
visible={visible}
|
||||
title="Leader Rebalance"
|
||||
onCancel={() => this.handleRebalanceCancel()}
|
||||
maskClosable={false}
|
||||
footer={null}
|
||||
>
|
||||
<Form {...layout} name="basic" onSubmit={this.handleSubmit} >
|
||||
<Form.Item label="集群名称" >
|
||||
{getFieldDecorator('clusterName', {
|
||||
initialValue: this.props.clusterName,
|
||||
rules: [{ required: true, message: '请输入集群名称' }],
|
||||
})(<Input disabled={true} />)}
|
||||
</Form.Item>
|
||||
<Form.Item label="Broker" >
|
||||
{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>)}
|
||||
</Form.Item>
|
||||
<Form.Item label="" >
|
||||
{getFieldDecorator('submit')(
|
||||
<Button
|
||||
htmlType="submit"
|
||||
type="primary"
|
||||
className="implement-button"
|
||||
>
|
||||
执行
|
||||
</Button>,
|
||||
)}
|
||||
</Form.Item>
|
||||
{
|
||||
this.state.imVisible && <Table
|
||||
rowKey="clusterName"
|
||||
bordered={true}
|
||||
dataSource={reblanceData}
|
||||
columns={columns}
|
||||
pagination={false}
|
||||
/>
|
||||
}
|
||||
</Form>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const LeaderRebalanceWrapper = Form.create<IXFormProps>()(LeaderRebalanceModal);
|
||||
303
kafka-manager-console/src/container/modal/admin/migration.ts
Normal file
303
kafka-manager-console/src/container/modal/admin/migration.ts
Normal file
@@ -0,0 +1,303 @@
|
||||
import { wrapper } from 'store';
|
||||
import { IReassignTasks, IExecute, IReassign, INewBulidEnums, IEnumsMap } from 'types/base-type';
|
||||
import { notification } from 'component/antd';
|
||||
import { expert } from 'store/expert';
|
||||
import { transMBToB, transBToMB } from 'lib/utils';
|
||||
import moment = require('moment');
|
||||
import { admin } from 'store/admin';
|
||||
import { timeFormat } from 'constants/strategy';
|
||||
|
||||
export const startMigrationTask = (item: IReassignTasks, action: string) => {
|
||||
const params = {
|
||||
action,
|
||||
beginTime: +moment(item.beginTime).format('x'),
|
||||
taskId: item.taskId,
|
||||
} as IExecute;
|
||||
expert.getExecuteTask(params).then(data => {
|
||||
notification.success({ message: '操作成功' });
|
||||
});
|
||||
};
|
||||
|
||||
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,
|
||||
},
|
||||
},
|
||||
],
|
||||
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) => {
|
||||
const status: number = item.status;
|
||||
const xFormModal = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'throttle',
|
||||
label: '初始限流',
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入初始限流',
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请输入初始限流',
|
||||
suffix: 'MB/s',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'maxThrottle',
|
||||
label: '限流上限',
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入限流上限',
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请输入限流上限',
|
||||
suffix: 'MB/s',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'minThrottle',
|
||||
label: '限流下限',
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入限流下限',
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请输入限流下限',
|
||||
suffix: 'MB/s',
|
||||
},
|
||||
},
|
||||
],
|
||||
formData: {
|
||||
throttle: transBToMB(item.realThrottle),
|
||||
maxThrottle: transBToMB(item.maxThrottle),
|
||||
minThrottle: transBToMB(item.minThrottle),
|
||||
},
|
||||
visible: true,
|
||||
title: '修改',
|
||||
onSubmit: (value: IExecute) => {
|
||||
const params = {
|
||||
action,
|
||||
throttle: transMBToB(value.throttle),
|
||||
maxThrottle: transMBToB(value.maxThrottle),
|
||||
minThrottle: transMBToB(value.minThrottle),
|
||||
subTaskId: item.subTaskId,
|
||||
} as IExecute;
|
||||
expert.getExecuteSubTask(params, taskId).then(data => {
|
||||
notification.success({ message: '操作成功' });
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
|
||||
const updateFormModal = () => {
|
||||
const formMap = wrapper.xFormWrapper.formMap;
|
||||
formMap[2].options = admin.packageList;
|
||||
formMap[3].options = admin.serverPropertiesList;
|
||||
// tslint:disable-next-line:no-unused-expression
|
||||
wrapper.ref && wrapper.ref.updateFormMap$(formMap, wrapper.xFormWrapper.formData);
|
||||
};
|
||||
|
||||
const updateFormExclude = (value: boolean) => {
|
||||
const formMap = wrapper.xFormWrapper.formMap;
|
||||
if (value) {
|
||||
formMap[4].invisible = false;
|
||||
formMap[5].invisible = false;
|
||||
formMap[6].invisible = true;
|
||||
|
||||
formMap[4].rules = [{
|
||||
required: true,
|
||||
}];
|
||||
formMap[5].rules = [{
|
||||
required: false,
|
||||
}];
|
||||
formMap[6].rules = [{
|
||||
required: false,
|
||||
}];
|
||||
} else {
|
||||
formMap[4].invisible = true;
|
||||
formMap[5].invisible = true;
|
||||
formMap[6].invisible = false;
|
||||
|
||||
formMap[4].rules = [{
|
||||
required: false,
|
||||
}];
|
||||
formMap[5].rules = [{
|
||||
required: false,
|
||||
}];
|
||||
formMap[6].rules = [{
|
||||
required: true,
|
||||
}];
|
||||
}
|
||||
// tslint:disable-next-line:no-unused-expression
|
||||
wrapper.ref && wrapper.ref.updateFormMap$(formMap, wrapper.xFormWrapper.formData);
|
||||
};
|
||||
|
||||
export const addMigrationTask = () => {
|
||||
const taskStatus = admin.configsTaskStatus ? admin.configsTaskStatus : [] as IEnumsMap[];
|
||||
const xFormModal = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'clusterId',
|
||||
label: '集群',
|
||||
type: 'select',
|
||||
options: admin.metaList.map(item => {
|
||||
return {
|
||||
label: item.clusterName,
|
||||
value: item.clusterId,
|
||||
};
|
||||
}),
|
||||
rules: [{
|
||||
required: true,
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请选择集群',
|
||||
onChange: (value: number) => {
|
||||
admin.getTasksKafkaFiles(value).then(() => {
|
||||
updateFormModal();
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'taskType',
|
||||
label: '任务类型',
|
||||
type: 'select',
|
||||
options: admin.tasksEnums,
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请选择集群任务',
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请选择集群任务',
|
||||
onChange: (value: string) => {
|
||||
value === 'role_upgrade' ? updateFormExclude(true) : updateFormExclude(false);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'kafkafileNameMd5',
|
||||
label: '包版本',
|
||||
type: 'select',
|
||||
options: admin.packageList,
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请选择包版本',
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请选择包版本',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'serverfileNameMd5',
|
||||
label: 'server配置',
|
||||
type: 'select',
|
||||
options: admin.serverPropertiesList,
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请选择server配置',
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请选择server配置',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'upgradeSequenceList',
|
||||
label: '升级顺序',
|
||||
type: 'select',
|
||||
options: admin.kafkaRoles.map(item => {
|
||||
return {
|
||||
label: item.role,
|
||||
value: item.role,
|
||||
};
|
||||
}),
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入升级顺序',
|
||||
}],
|
||||
defaultValue: [] as any,
|
||||
attrs: {
|
||||
mode: 'multiple',
|
||||
placeholder: '请选择升级顺序',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'ignoreList',
|
||||
label: '排除主机列表',
|
||||
type: 'select',
|
||||
invisible: true,
|
||||
rules: [{
|
||||
required: false,
|
||||
message: '请输入排除主机列表',
|
||||
}],
|
||||
defaultValue: [] as any,
|
||||
attrs: {
|
||||
placeholder: '请输入排除主机列表',
|
||||
mode: 'tags',
|
||||
tokenSeparators: [','],
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'hostList',
|
||||
label: '主机列表',
|
||||
type: 'select',
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入主机列表',
|
||||
}],
|
||||
defaultValue: [] as any,
|
||||
attrs: {
|
||||
placeholder: '请输入主机列表',
|
||||
mode: 'tags',
|
||||
tokenSeparators: [' '],
|
||||
},
|
||||
},
|
||||
],
|
||||
formData: {},
|
||||
visible: true,
|
||||
title: '新建集群任务',
|
||||
onSubmit: (value: INewBulidEnums) => {
|
||||
value.kafkaPackageName = value.kafkafileNameMd5.split(',')[0];
|
||||
value.kafkaPackageMd5 = value.kafkafileNameMd5.split(',')[1];
|
||||
value.serverPropertiesName = value.serverfileNameMd5.split(',')[0];
|
||||
value.serverPropertiesMd5 = value.serverfileNameMd5.split(',')[1];
|
||||
delete value.kafkafileNameMd5;
|
||||
delete value.serverfileNameMd5;
|
||||
admin.addMigrationTask(value).then(data => {
|
||||
notification.success({ message: '新建集群任务成功' });
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
277
kafka-manager-console/src/container/modal/admin/task.ts
Normal file
277
kafka-manager-console/src/container/modal/admin/task.ts
Normal file
@@ -0,0 +1,277 @@
|
||||
import { wrapper } from 'store';
|
||||
import { notification } from 'component/antd';
|
||||
import { expert } from 'store/expert';
|
||||
import { admin } from 'store/admin';
|
||||
import { IMigration, IReassignTasks, IExecute } from 'types/base-type';
|
||||
import { createMigrationTask } from 'lib/api';
|
||||
import { transMBToB, transHourToMSecond, transMSecondToHour } from 'lib/utils';
|
||||
import moment = require('moment');
|
||||
import { timeFormat } from 'constants/strategy';
|
||||
|
||||
const updateFormModal = (topicName?: string) => {
|
||||
const formMap = wrapper.xFormWrapper.formMap;
|
||||
const formData = wrapper.xFormWrapper.formData;
|
||||
if (topicName) {
|
||||
formMap[5].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;
|
||||
}
|
||||
// tslint:disable-next-line:no-unused-expression
|
||||
wrapper.ref && wrapper.ref.updateFormMap$(formMap, wrapper.xFormWrapper.formData, !!topicName, ['partitionIdList']);
|
||||
};
|
||||
|
||||
const updateInputModal = (status?: string) => {
|
||||
const formMap = wrapper.xFormWrapper.formMap;
|
||||
formMap[3].invisible = status === 'region';
|
||||
formMap[4].invisible = status !== 'region';
|
||||
|
||||
formMap[3].rules = [{required: status !== 'region'}];
|
||||
formMap[4].rules = [{required: status === 'region'}];
|
||||
// tslint:disable-next-line:no-unused-expression
|
||||
wrapper.ref && wrapper.ref.updateFormMap$(formMap, wrapper.xFormWrapper.formData);
|
||||
};
|
||||
|
||||
let clusterId = 0 as number;
|
||||
|
||||
export const createMigrationTasks = () => {
|
||||
const xFormModal = {
|
||||
type: 'drawer',
|
||||
width: 700,
|
||||
formMap: [
|
||||
{
|
||||
key: 'clusterId',
|
||||
label: '集群名称',
|
||||
type: 'select',
|
||||
options: expert.metaData ? expert.metaData.slice(1).map(item => {
|
||||
return {
|
||||
label: item.clusterName,
|
||||
value: item.clusterId,
|
||||
};
|
||||
}) : [],
|
||||
rules: [{
|
||||
required: true,
|
||||
}],
|
||||
attrs: {
|
||||
async onChange(value: number) {
|
||||
clusterId = value;
|
||||
await admin.getBrokersMetadata(value);
|
||||
await admin.getBrokersRegions(value);
|
||||
await expert.getTaskTopicMetadata(value);
|
||||
updateFormModal();
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'topicName',
|
||||
label: 'Topic名称',
|
||||
type: 'select',
|
||||
options: (expert.taskTopicMetadata),
|
||||
rules: [{
|
||||
required: true,
|
||||
}],
|
||||
attrs: {
|
||||
showSearch: true,
|
||||
optionFilterProp: 'children',
|
||||
async onChange(value: string) {
|
||||
await admin.getTopicsBasicInfo(clusterId, value);
|
||||
updateFormModal(value);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'species',
|
||||
label: '类型',
|
||||
type: 'radio_group',
|
||||
defaultValue: 'broker',
|
||||
options: [{
|
||||
label: 'Region',
|
||||
value: 'region',
|
||||
}, {
|
||||
label: 'Borker',
|
||||
value: 'broker',
|
||||
}],
|
||||
rules: [{
|
||||
required: false,
|
||||
message: '请选择类型',
|
||||
}],
|
||||
attrs: {
|
||||
onChange(item: any) {
|
||||
updateInputModal(item.target.value);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'brokerIdList',
|
||||
label: 'Broker',
|
||||
type: 'select',
|
||||
defaultValue: [] as any,
|
||||
invisible: false,
|
||||
options: admin.brokersMetadata,
|
||||
rules: [{ required: true, message: '请选择Broker' }],
|
||||
attrs: {
|
||||
mode: 'multiple',
|
||||
placeholder: '请选择Broker',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'regionId',
|
||||
label: 'Region',
|
||||
type: 'select',
|
||||
defaultValue: [] as any,
|
||||
invisible: true,
|
||||
options: admin.brokersRegions,
|
||||
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',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'beginTime',
|
||||
label: '计划开始时间',
|
||||
type: 'date_picker',
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入计划开始时间',
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请输入计划开始时间',
|
||||
format: timeFormat,
|
||||
showTime: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'originalRetentionTime',
|
||||
label: '原本保存时间',
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入原本保存时间',
|
||||
}],
|
||||
attrs: {
|
||||
disabled: true,
|
||||
placeholder: '请输入原本保存时间',
|
||||
suffix: '小时',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'reassignRetentionTime',
|
||||
label: '迁移保存时间',
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入迁移保存时间',
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请输入迁移保存时间',
|
||||
suffix: '小时',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'throttle',
|
||||
label: '初始限流',
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入初始限流',
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请输入初始限流',
|
||||
suffix: 'MB/s',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'maxThrottle',
|
||||
label: '限流上限',
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入限流上限',
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请输入限流上限',
|
||||
suffix: 'MB/s',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'minThrottle',
|
||||
label: '限流下限',
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入限流下限',
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请输入限流下限',
|
||||
suffix: 'MB/s',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
label: '备注',
|
||||
type: 'text_area',
|
||||
rules: [{
|
||||
required: false,
|
||||
message: '请输入至少5个字符',
|
||||
pattern: /^.{5,}.$/,
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请输入备注',
|
||||
},
|
||||
},
|
||||
],
|
||||
formData: {},
|
||||
visible: true,
|
||||
title: '新建迁移任务',
|
||||
onSubmit: (value: any) => {
|
||||
const params = {
|
||||
clusterId: value.clusterId,
|
||||
beginTime: +moment(value.beginTime).format('x'),
|
||||
originalRetentionTime: transHourToMSecond(value.originalRetentionTime),
|
||||
reassignRetentionTime: transHourToMSecond(value.reassignRetentionTime),
|
||||
throttle: transMBToB(value.throttle),
|
||||
maxThrottle: transMBToB(value.maxThrottle),
|
||||
minThrottle: transMBToB(value.minThrottle),
|
||||
description: value.description,
|
||||
brokerIdList: value.brokerIdList,
|
||||
regionId: value.regionId,
|
||||
partitionIdList: value.partitionIdList,
|
||||
topicName: value.topicName,
|
||||
} as IMigration;
|
||||
if (value.regionId) {
|
||||
delete params.brokerIdList;
|
||||
} else {
|
||||
delete params.regionId;
|
||||
}
|
||||
createMigrationTask([params]).then(data => {
|
||||
notification.success({ message: '新建迁移任务成功' });
|
||||
expert.getReassignTasks();
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
|
||||
export const cancelMigrationTask = (item: IReassignTasks, action: string) => {
|
||||
const params = {
|
||||
action,
|
||||
taskId: item.taskId,
|
||||
beginTime: +moment(item.beginTime).format('x'),
|
||||
throttle: Number(item.throttle),
|
||||
maxThrottle: Number(item.maxThrottle),
|
||||
minThrottle: Number(item.minThrottle),
|
||||
} as IExecute;
|
||||
expert.getExecuteTask(params).then(data => {
|
||||
notification.success({ message: '操作成功' });
|
||||
});
|
||||
};
|
||||
46
kafka-manager-console/src/container/modal/admin/user.ts
Normal file
46
kafka-manager-console/src/container/modal/admin/user.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { IUser } from 'types/base-type';
|
||||
import { users } from 'store/users';
|
||||
import { wrapper } from 'store';
|
||||
import { roleMap } from 'constants/status-map';
|
||||
import { message } from 'component/antd';
|
||||
import { FormItemType } from 'component/x-form';
|
||||
|
||||
export const showApplyModal = (record?: IUser) => {
|
||||
const xFormModal = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'username',
|
||||
label: '用户名',
|
||||
rules: [{ required: true, message: '请输入用户名' }],
|
||||
}, {
|
||||
key: 'role',
|
||||
label: '角色',
|
||||
type: 'select',
|
||||
options: Object.keys(roleMap).map((item) => ({
|
||||
label: roleMap[+item],
|
||||
value: +item,
|
||||
})),
|
||||
rules: [{ required: true, message: '请选择角色' }],
|
||||
}, {
|
||||
key: 'password',
|
||||
label: '密码',
|
||||
type: FormItemType.inputPassword,
|
||||
rules: [{ required: !record, message: '请输入密码' }],
|
||||
},
|
||||
],
|
||||
formData: record || {},
|
||||
visible: true,
|
||||
title: record ? '修改用户信息' : '新增用户',
|
||||
onSubmit: (value: IUser) => {
|
||||
if (record) {
|
||||
return users.modfiyUser(value).then(() => {
|
||||
message.success('操作成功');
|
||||
});
|
||||
}
|
||||
return users.addUser(value).then(() => {
|
||||
message.success('操作成功');
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
191
kafka-manager-console/src/container/modal/admin/version.ts
Normal file
191
kafka-manager-console/src/container/modal/admin/version.ts
Normal file
@@ -0,0 +1,191 @@
|
||||
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';
|
||||
|
||||
const handleSelectChange = (e: number) => {
|
||||
version.setAcceptFileType(e);
|
||||
updateFormModal(e);
|
||||
};
|
||||
|
||||
export const showUploadModal = () => {
|
||||
const xFormModal = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'fileType',
|
||||
label: '文件类型',
|
||||
type: 'select',
|
||||
options: version.fileTypeList,
|
||||
attrs: {
|
||||
onChange: (e: number) => handleSelectChange(e),
|
||||
},
|
||||
rules: [{ required: true, message: '请选择文件类型' }],
|
||||
}, {
|
||||
key: 'clusterId',
|
||||
label: '集群',
|
||||
type: 'select',
|
||||
invisible: true,
|
||||
options: admin.metaList.map(item => ({
|
||||
...item,
|
||||
label: item.clusterName,
|
||||
value: item.clusterId,
|
||||
})),
|
||||
rules: [{ required: false, message: '请选择集群' }],
|
||||
}, {
|
||||
key: 'uploadFile',
|
||||
label: '上传文件',
|
||||
type: 'upload',
|
||||
attrs: {
|
||||
accept: version.fileSuffix,
|
||||
},
|
||||
rules: [{
|
||||
required: true,
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
if (value.length) {
|
||||
if (value.length > 1) {
|
||||
callback('一次仅支持上传一份文件!');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
callback(`请上传文件`);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
}],
|
||||
}, {
|
||||
key: 'description',
|
||||
label: '备注',
|
||||
type: 'text_area',
|
||||
rules: [{ required: false, message: '请输入备注' }],
|
||||
},
|
||||
],
|
||||
formData: {},
|
||||
visible: true,
|
||||
title: '上传',
|
||||
onText: '保存',
|
||||
isWaitting: true,
|
||||
onSubmit: (value: IUploadFile) => {
|
||||
value.file = value.uploadFile[0].originFileObj;
|
||||
return computeChecksumMd5(value.file).then(md5 => {
|
||||
const params = {
|
||||
fileName: value.file.name,
|
||||
fileMd5: md5,
|
||||
clusterId: value.clusterId || -1,
|
||||
...value,
|
||||
};
|
||||
return version.addFile(params);
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
|
||||
const updateFormModal = (type: number) => {
|
||||
const formMap = wrapper.xFormWrapper.formMap;
|
||||
|
||||
if (formMap && formMap.length > 2) {
|
||||
formMap[1].invisible = !version.currentFileType;
|
||||
formMap[1].rules = [{ required: version.currentFileType, message: '请上传文件' }];
|
||||
formMap[2].attrs = {
|
||||
accept: version.fileSuffix,
|
||||
},
|
||||
// tslint:disable-next-line:no-unused-expression
|
||||
wrapper.ref && wrapper.ref.updateFormMap$(formMap, wrapper.xFormWrapper.formData, true);
|
||||
}
|
||||
};
|
||||
|
||||
export const showModifyModal = (record: IUploadFile) => {
|
||||
version.setAcceptFileType(record.fileType);
|
||||
const xFormModal = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'uploadFile',
|
||||
label: '上传文件',
|
||||
type: 'upload',
|
||||
attrs: {
|
||||
accept: version.fileSuffix,
|
||||
},
|
||||
rules: [{
|
||||
required: true,
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
if (value.length) {
|
||||
if (value.length > 1) {
|
||||
callback('一次仅支持上传一份文件!');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
callback(`请上传文件`);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
}],
|
||||
}, {
|
||||
key: 'description',
|
||||
label: '备注',
|
||||
type: 'text_area',
|
||||
rules: [{ required: false, message: '请输入备注' }],
|
||||
},
|
||||
],
|
||||
formData: record || {},
|
||||
visible: true,
|
||||
isWaitting: true,
|
||||
title: '修改',
|
||||
onSubmit: async (value: IUploadFile) => {
|
||||
value.file = value.uploadFile[0].originFileObj;
|
||||
const md5 = await computeChecksumMd5(value.file);
|
||||
const params = {
|
||||
fileName: value.file.name,
|
||||
fileMd5: md5 as string,
|
||||
description: value.description,
|
||||
file: value.file,
|
||||
id: record.id,
|
||||
};
|
||||
return version.modfiyFile(params);
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
|
||||
export const showConfigureModal = (record?: IConfigure) => {
|
||||
const xFormModal = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'configKey',
|
||||
label: '配置键',
|
||||
rules: [{ required: true, message: '请输入配置键' }],
|
||||
attrs: {
|
||||
disabled: record ? true : false,
|
||||
},
|
||||
}, {
|
||||
key: 'configValue',
|
||||
label: '配置值',
|
||||
type: 'text_area',
|
||||
rules: [{ required: true, message: '请输入配置值' }],
|
||||
}, {
|
||||
key: 'configDescription',
|
||||
label: '备注',
|
||||
type: 'text_area',
|
||||
rules: [{ required: true, message: '请输入备注' }],
|
||||
},
|
||||
],
|
||||
formData: record || {},
|
||||
visible: true,
|
||||
isWaitting: true,
|
||||
title: `${record ? '修改配置' : '新建配置'}`,
|
||||
onSubmit: async (value: IConfigure) => {
|
||||
if (record) {
|
||||
return admin.editConfigure(value).then(data => {
|
||||
notification.success({ message: '修改配置成功' });
|
||||
});
|
||||
}
|
||||
return admin.addNewConfigure(value).then(data => {
|
||||
notification.success({ message: '新建配置成功' });
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
80
kafka-manager-console/src/container/modal/alarm.tsx
Normal file
80
kafka-manager-console/src/container/modal/alarm.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import { notification } from 'component/antd';
|
||||
import { wrapper, region } from 'store';
|
||||
import { IMonitorSilences } from 'types/base-type';
|
||||
import { alarm } from 'store/alarm';
|
||||
import { urlPrefix } from 'constants/left-menu';
|
||||
import moment from 'moment';
|
||||
import { timeFormat } from 'constants/strategy';
|
||||
|
||||
export const createMonitorSilences = (monitorId: number, monitorName: string) => {
|
||||
const xFormWrapper = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'monitorName',
|
||||
label: '告警名称',
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入告警名称',
|
||||
}],
|
||||
attrs: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'beginEndTime',
|
||||
label: '开始~结束时间',
|
||||
type: 'range_picker',
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入开始~结束时间',
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: ['开始时间', '结束时间'],
|
||||
format: timeFormat,
|
||||
showTime: true,
|
||||
ranges: {
|
||||
'1小时': [moment(), moment().add(1, 'hour')],
|
||||
'2小时': [moment(), moment().add(2, 'hour')],
|
||||
'6小时': [moment(), moment().add(6, 'hour')],
|
||||
'12小时': [moment(), moment().add(12, 'hour')],
|
||||
'1天': [moment(), moment().add(1, 'day')],
|
||||
'2天': [moment(), moment().add(7, 'day')],
|
||||
'7天': [moment(), moment().add(7, 'day')],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
label: '说明',
|
||||
type: 'text_area',
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入说明',
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请输入备注',
|
||||
},
|
||||
},
|
||||
],
|
||||
formData: {
|
||||
monitorName,
|
||||
},
|
||||
okText: '确认',
|
||||
visible: true,
|
||||
width: 600,
|
||||
title: '屏蔽',
|
||||
onSubmit: (value: any) => {
|
||||
const params = {
|
||||
description: value.description,
|
||||
startTime: +moment(value.beginEndTime[0]).format('x'),
|
||||
endTime: +moment(value.beginEndTime[1]).format('x'),
|
||||
monitorId,
|
||||
} as IMonitorSilences;
|
||||
alarm.createSilences(params, monitorId).then(data => {
|
||||
notification.success({ message: '屏蔽成功' });
|
||||
window.location.href = `${urlPrefix}/alarm/alarm-detail?id=${monitorId}®ion=${region.currentRegion}#3`;
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormWrapper);
|
||||
};
|
||||
127
kafka-manager-console/src/container/modal/app.tsx
Normal file
127
kafka-manager-console/src/container/modal/app.tsx
Normal file
@@ -0,0 +1,127 @@
|
||||
import { IAppItem, IOrderParams } from 'types/base-type';
|
||||
import * as React from 'react';
|
||||
import { wrapper, region } from 'store';
|
||||
import { users } from 'store/users';
|
||||
import { app } from 'store/app';
|
||||
import { StaffSelect } from 'container/staff-select';
|
||||
import { message, Icon } from 'component/antd';
|
||||
import { urlPrefix } from 'constants/left-menu';
|
||||
import { copyString } from 'lib/utils';
|
||||
|
||||
export const showEditModal = (record?: IAppItem, from?: string, isDisabled?: boolean) => {
|
||||
const xFormModal = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'appId',
|
||||
label: '应用ID',
|
||||
invisible: !record,
|
||||
defaultValue: record && record.appId || '',
|
||||
rules: [{
|
||||
required: isDisabled ? false : record && record.appId,
|
||||
message: '请输入不得超过64个字符',
|
||||
pattern: /^.{1,64}$/,
|
||||
}],
|
||||
attrs: { disabled: true },
|
||||
}, {
|
||||
key: 'name',
|
||||
label: '应用名称',
|
||||
defaultValue: record && record.name || '',
|
||||
rules: [{
|
||||
required: isDisabled ? false : true,
|
||||
message: '请输入不得超过64个字符',
|
||||
pattern: /^.{1,64}$/,
|
||||
}],
|
||||
attrs: { disabled: isDisabled },
|
||||
}, {
|
||||
key: 'password',
|
||||
label: '密码',
|
||||
invisible: !record,
|
||||
defaultValue: record && record.password || '',
|
||||
rules: [{
|
||||
required: isDisabled ? false : record && record.password,
|
||||
message: '请输入不得超过64个字符',
|
||||
pattern: /^.{1,64}$/,
|
||||
}],
|
||||
attrs: {
|
||||
disabled: true,
|
||||
suffix: (
|
||||
<Icon
|
||||
onClick={() => copyString(record.password)}
|
||||
type="copy"
|
||||
className="icon-color"
|
||||
/>),
|
||||
},
|
||||
}, {
|
||||
key: 'idc',
|
||||
label: '数据中心',
|
||||
defaultValue: region.regionName,
|
||||
rules: [{ required: isDisabled ? false : true, message: '请输入' }],
|
||||
attrs: {
|
||||
placeholder: '请输入',
|
||||
disabled: true,
|
||||
},
|
||||
}, {
|
||||
key: 'principalList',
|
||||
label: '负责人',
|
||||
type: 'custom',
|
||||
customFormItem: <StaffSelect isDisabled={isDisabled}/>,
|
||||
rules: [{
|
||||
required: isDisabled ? false : true,
|
||||
message: '请选择负责人(至少两人)',
|
||||
validator: (rule: any, value: []) => {
|
||||
if (value.length < 2) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}],
|
||||
}, {
|
||||
key: 'description',
|
||||
label: '应用描述',
|
||||
type: 'text_area',
|
||||
rules: [{ required: isDisabled ? false : true, message: '请输入描述'}],
|
||||
attrs: { disabled: isDisabled },
|
||||
},
|
||||
],
|
||||
formData: record,
|
||||
visible: true,
|
||||
title: `${isDisabled ? '详情' : record ? '修改' : '应用申请'}`,
|
||||
onSubmit: (value: IAppItem) => {
|
||||
if (isDisabled) {
|
||||
return;
|
||||
}
|
||||
value.idc = region.currentRegion;
|
||||
return operateApp(!!record, value, record, from).then((data: any) => {
|
||||
message.success('操作成功');
|
||||
if (!record) {
|
||||
window.location.href = `${urlPrefix}/user/order-detail/?orderId=${data.id}®ion=${region.currentRegion}`;
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
|
||||
const operateApp = (isEdit: boolean, value: IAppItem, record?: IAppItem, from?: string) => {
|
||||
const params: IOrderParams = {
|
||||
description: value.description,
|
||||
type: 1,
|
||||
applicant: users.currentUser.username,
|
||||
};
|
||||
let principals = '';
|
||||
|
||||
if (value.principalList && value.principalList.length) {
|
||||
principals = value.principalList.join(',');
|
||||
}
|
||||
params.extensions = JSON.stringify({ principals, idc: value.idc, name: value.name });
|
||||
let modifyParams = {};
|
||||
if (isEdit) {
|
||||
modifyParams = {
|
||||
appId: record.appId,
|
||||
description: value.description,
|
||||
name: value.name,
|
||||
principals,
|
||||
};
|
||||
}
|
||||
return isEdit ? app.modfiyApplication(modifyParams, from) : app.applyApplication(params);
|
||||
};
|
||||
@@ -0,0 +1,181 @@
|
||||
import * as React from 'react';
|
||||
import { Table, Modal, Tooltip, Icon, message, notification } from 'component/antd';
|
||||
import { app } from 'store/app';
|
||||
import { observer } from 'mobx-react';
|
||||
import { modal } from 'store/modal';
|
||||
import { users } from 'store/users';
|
||||
import { urlPrefix } from 'constants/left-menu';
|
||||
import { region } from 'store';
|
||||
import { topic, IConnectionInfo } from 'store/topic';
|
||||
import urlQuery from 'store/url-query';
|
||||
import { cellStyle } from 'constants/table';
|
||||
import { XFormComponent } from 'component/x-form';
|
||||
import { ILimitsItem } from 'types/base-type';
|
||||
|
||||
const formLayout = {
|
||||
labelCol: { span: 2 },
|
||||
wrapperCol: { span: 16 },
|
||||
};
|
||||
|
||||
@observer
|
||||
export class CancelTopicPermission extends React.Component {
|
||||
private $formRef: any;
|
||||
|
||||
public componentDidMount() {
|
||||
topic.getConnectionInfo(modal.params.clusterId, modal.params.topicName, urlQuery.appId);
|
||||
}
|
||||
|
||||
public handleCancel = () => {
|
||||
topic.setConnectionInfo([]);
|
||||
modal.close();
|
||||
}
|
||||
|
||||
public handleSubmit = () => {
|
||||
this.$formRef.validateFields((error: Error, value: ILimitsItem) => {
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
if (topic.connectionInfo && topic.connectionInfo.length) {
|
||||
return message.warning('存在连接信息,无法取消权限!');
|
||||
}
|
||||
const appId = urlQuery.appId;
|
||||
if (value.access.length) {
|
||||
value.access = value.access.length === 2 ? '3' : value.access[0];
|
||||
}
|
||||
const extensions = {
|
||||
appId,
|
||||
clusterId: modal.params.clusterId,
|
||||
topicName: modal.params.topicName,
|
||||
access: Number(value.access),
|
||||
};
|
||||
app.cancelProdPermission({
|
||||
type: 13,
|
||||
description: '',
|
||||
applicant: users.currentUser.username,
|
||||
extensions: JSON.stringify(extensions),
|
||||
}).then((data: any) => {
|
||||
notification.success({ message: '取消权限操作成功' });
|
||||
window.location.href = `${urlPrefix}/user/order-detail/?orderId=${data.id}®ion=${region.currentRegion}`;
|
||||
});
|
||||
modal.close();
|
||||
});
|
||||
}
|
||||
|
||||
public getColumns = () => {
|
||||
const onlineColumns = [
|
||||
{
|
||||
title: 'AppID',
|
||||
dataIndex: 'appId',
|
||||
key: 'appId',
|
||||
width: '20%',
|
||||
sorter: (a: IConnectionInfo, b: IConnectionInfo) => a.appId.charCodeAt(0) - b.appId.charCodeAt(0),
|
||||
},
|
||||
{
|
||||
title: '主机名',
|
||||
dataIndex: 'hostname',
|
||||
key: 'hostname',
|
||||
width: '40%',
|
||||
onCell: () => ({
|
||||
style: {
|
||||
maxWidth: 250,
|
||||
...cellStyle,
|
||||
},
|
||||
}),
|
||||
render: (t: string) => {
|
||||
return (
|
||||
<Tooltip placement="bottomLeft" title={t} >{t}</Tooltip>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '客户端版本',
|
||||
dataIndex: 'clientVersion',
|
||||
key: 'clientVersion',
|
||||
width: '20%',
|
||||
},
|
||||
{
|
||||
title: '客户端类型',
|
||||
dataIndex: 'clientType',
|
||||
key: 'clientType',
|
||||
width: '20%',
|
||||
render: (t: string) => <span>{t === 'consumer' ? '消费' : '生产'}</span>,
|
||||
},
|
||||
] as any;
|
||||
return onlineColumns;
|
||||
}
|
||||
|
||||
public render() {
|
||||
const access: number = modal.params.access;
|
||||
const consume = access === 0 || access === 1;
|
||||
const send = access === 0 || access === 2;
|
||||
const accessStatus = access === 0 ? [] : (access === 1 || access === 2) ? [access + ''] : ['1', '2'];
|
||||
const formMap = [
|
||||
{
|
||||
key: 'access',
|
||||
label: '权限',
|
||||
type: 'check_box',
|
||||
defaultValue: accessStatus,
|
||||
options: [{
|
||||
label: '消费权限',
|
||||
value: '1',
|
||||
disabled: send,
|
||||
}, {
|
||||
label: '发送权限',
|
||||
value: '2',
|
||||
disabled: consume,
|
||||
}],
|
||||
rules: [{ required: access !== 0, message: '请选择' }],
|
||||
},
|
||||
] as any;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
visible={true}
|
||||
className="stream-debug-modal"
|
||||
title={
|
||||
<span>
|
||||
取消权限申请
|
||||
{topic.connectionInfo.length ?
|
||||
<Tooltip placement="right" title={'如若有连接信息,则表示资源正处于使用中,禁止下线操作。如需下线,烦请关闭连接信息中的Kafka发送/消费客户端后再进行下线。'} >
|
||||
<Icon className="question-icon" type="question-circle" />
|
||||
</Tooltip> : null}
|
||||
</span>
|
||||
}
|
||||
maskClosable={false}
|
||||
onCancel={this.handleCancel}
|
||||
onOk={this.handleSubmit}
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
okButtonProps={{ disabled: topic.loading || !!topic.connectionInfo.length }}
|
||||
width={700}
|
||||
>
|
||||
<>
|
||||
<Table
|
||||
title={() => '连接信息'}
|
||||
loading={topic.loading}
|
||||
className="custom-content"
|
||||
rowKey="key"
|
||||
scroll={{ x: 450, y: 260 }}
|
||||
dataSource={topic.connectionInfo}
|
||||
columns={this.getColumns()}
|
||||
pagination={false}
|
||||
bordered={true}
|
||||
/>
|
||||
<div className="topic-x-form-box">
|
||||
<XFormComponent
|
||||
ref={form => this.$formRef = form}
|
||||
formData={{}}
|
||||
formMap={formMap}
|
||||
formLayout={formLayout}
|
||||
/>
|
||||
<div>
|
||||
注意: 如需取消权限,请把对应选项打勾!
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
58
kafka-manager-console/src/container/modal/cluster.tsx
Normal file
58
kafka-manager-console/src/container/modal/cluster.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import { IClusterData } from 'types/base-type';
|
||||
import { users } from 'store/users';
|
||||
import { wrapper } from 'store';
|
||||
import { cluster } from 'store/cluster';
|
||||
import { notification } from 'component/antd';
|
||||
import { urlPrefix } from 'constants/left-menu';
|
||||
import { region } from 'store';
|
||||
|
||||
export const showCpacityModal = (item: IClusterData) => {
|
||||
const xFormModal = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'type',
|
||||
label: '扩缩容:',
|
||||
type: 'select',
|
||||
options: [{
|
||||
label: '扩容',
|
||||
value: 5,
|
||||
}, {
|
||||
label: '缩容',
|
||||
value: 15,
|
||||
}],
|
||||
rules: [{ required: true, message: '请选择' }],
|
||||
attrs: {
|
||||
placeholder: '请选择',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
label: '申请原因',
|
||||
type: 'text_area',
|
||||
rules: [{ required: true, pattern: /^.{5,}.$/, message: '请输入至少5个字符' }],
|
||||
attrs: {
|
||||
placeholder: '请输入至少5个字符',
|
||||
},
|
||||
},
|
||||
],
|
||||
formData: {},
|
||||
visible: true,
|
||||
title: '申请扩缩容',
|
||||
okText: '确认',
|
||||
onSubmit: (value: any) => {
|
||||
const cpacityParams = {
|
||||
type: value.type,
|
||||
applicant: users.currentUser.username,
|
||||
description: value.description,
|
||||
extensions: JSON.stringify({clusterId: item.clusterId}),
|
||||
};
|
||||
cluster.applyCpacity(cpacityParams).then(data => {
|
||||
notification.success({
|
||||
message: `申请${value.type === 5 ? '扩容' : '缩容'}成功`,
|
||||
});
|
||||
window.location.href = `${urlPrefix}/user/order-detail/?orderId=${data.id}®ion=${region.currentRegion}`;
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
import * as React from 'react';
|
||||
import { Table, Modal, Tooltip, Icon, message, notification } from 'component/antd';
|
||||
import { getApplyOnlineColumns } from 'container/topic/config';
|
||||
import { observer } from 'mobx-react';
|
||||
import { modal } from 'store/modal';
|
||||
import { users } from 'store/users';
|
||||
import { urlPrefix } from 'constants/left-menu';
|
||||
import { region } from 'store';
|
||||
import { topic } from 'store/topic';
|
||||
|
||||
@observer
|
||||
export class ConnectTopicList extends React.Component {
|
||||
|
||||
public componentDidMount() {
|
||||
topic.getConnectionInfo(modal.params.clusterId, modal.params.topicName, modal.params.appId);
|
||||
}
|
||||
|
||||
public handleCancel = () => {
|
||||
topic.setConnectionInfo([]);
|
||||
modal.close();
|
||||
}
|
||||
|
||||
public handleSubmit = () => {
|
||||
const connectionList = topic.connectionInfo;
|
||||
if (connectionList && connectionList.length) {
|
||||
return message.warning('存在连接信息,无法申请下线!');
|
||||
}
|
||||
const online = {
|
||||
clusterId: modal.params.clusterId,
|
||||
topicName: modal.params.topicName,
|
||||
};
|
||||
const offlineParams = {
|
||||
type: 10,
|
||||
applicant: users.currentUser.username,
|
||||
description: '',
|
||||
extensions: JSON.stringify(online),
|
||||
};
|
||||
topic.applyTopicOnline(offlineParams).then((data: any) => {
|
||||
notification.success({ message: '申请下线成功' });
|
||||
window.location.href = `${urlPrefix}/user/order-detail/?orderId=${data.id}®ion=${region.currentRegion}`;
|
||||
});
|
||||
modal.close();
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
visible={true}
|
||||
className="stream-debug-modal"
|
||||
title={
|
||||
<span>
|
||||
申请下线
|
||||
<Tooltip placement="right" title={' 如若有连接信息,则表示资源正处于使用中,禁止下线操作。如需下线,烦请关闭连接信息中的Kafka发送/消费客户端后再进行下线。 '} >
|
||||
<Icon className="question-icon" type="question-circle" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
maskClosable={false}
|
||||
onCancel={this.handleCancel}
|
||||
onOk={this.handleSubmit}
|
||||
okText="下线"
|
||||
cancelText="取消"
|
||||
okButtonProps={{ disabled: topic.connectLoading || !!topic.connectionInfo.length }}
|
||||
width={700}
|
||||
>
|
||||
<Table
|
||||
rowKey="key"
|
||||
title={() => '连接信息'}
|
||||
loading={topic.connectLoading}
|
||||
scroll={{ x: 450, y: 260 }}
|
||||
dataSource={topic.connectionInfo}
|
||||
columns={getApplyOnlineColumns()}
|
||||
pagination={false}
|
||||
bordered={true}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
33
kafka-manager-console/src/container/modal/expert.tsx
Normal file
33
kafka-manager-console/src/container/modal/expert.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { wrapper } from 'store';
|
||||
import { IXFormWrapper } from 'types/base-type';
|
||||
|
||||
import * as React from 'react';
|
||||
import { WrappedDataMigrationFormTable } from 'container/drawer/data-migration';
|
||||
|
||||
export interface IRenderData {
|
||||
brokerIdList: number[];
|
||||
partitionIdList: number[];
|
||||
topicName: string;
|
||||
clusterId?: number;
|
||||
clusterName?: string;
|
||||
throttle: number;
|
||||
maxThrottle: number;
|
||||
minThrottle: number;
|
||||
originalRetentionTime: number;
|
||||
reassignRetentionTime: number;
|
||||
retentionTime: number;
|
||||
key?: string | number;
|
||||
}
|
||||
|
||||
export const migrationModal = (renderData: IRenderData[]) => {
|
||||
const xFormWrapper = {
|
||||
type: 'drawer',
|
||||
visible: true,
|
||||
width: 1000,
|
||||
title: '新建迁移任务',
|
||||
customRenderElement: <WrappedDataMigrationFormTable data={renderData}/>,
|
||||
nofooter: true,
|
||||
noform: true,
|
||||
};
|
||||
wrapper.open(xFormWrapper as IXFormWrapper);
|
||||
};
|
||||
5
kafka-manager-console/src/container/modal/index.tsx
Normal file
5
kafka-manager-console/src/container/modal/index.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from './admin';
|
||||
export * from './app';
|
||||
export * from './alarm';
|
||||
export * from './topic';
|
||||
export * from './cluster';
|
||||
@@ -0,0 +1,77 @@
|
||||
import * as React from 'react';
|
||||
import { Table, Modal, Tooltip, Icon, message, notification } from 'component/antd';
|
||||
import { app } from 'store/app';
|
||||
import { getApplyOnlineColumns } from 'container/topic/config';
|
||||
import { observer } from 'mobx-react';
|
||||
import { modal } from 'store/modal';
|
||||
import { users } from 'store/users';
|
||||
import { urlPrefix } from 'constants/left-menu';
|
||||
import { region } from 'store';
|
||||
|
||||
@observer
|
||||
export class ConnectAppList extends React.Component {
|
||||
|
||||
public componentDidMount() {
|
||||
app.getAppsConnections(modal.params);
|
||||
}
|
||||
|
||||
public handleCancel = () => {
|
||||
app.setAppsConnections([]);
|
||||
modal.close();
|
||||
}
|
||||
|
||||
public handleSubmit = () => {
|
||||
const connectionList = app.appsConnections;
|
||||
if (connectionList && connectionList.length) {
|
||||
return message.warning('存在连接信息,无法申请下线!');
|
||||
}
|
||||
const offlineParams = {
|
||||
type: 11,
|
||||
applicant: users.currentUser.username,
|
||||
description: '',
|
||||
extensions: JSON.stringify({ appId: modal.params }),
|
||||
};
|
||||
app.applyAppOffline(offlineParams).then((data: any) => {
|
||||
notification.success({ message: '申请下线成功' });
|
||||
window.location.href = `${urlPrefix}/user/order-detail/?orderId=${data.id}®ion=${region.currentRegion}`;
|
||||
});
|
||||
modal.close();
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
visible={true}
|
||||
className="stream-debug-modal"
|
||||
title={
|
||||
<span>
|
||||
申请下线
|
||||
<Tooltip placement="right" title={'如若有连接信息,则表示资源正处于使用中,禁止下线操作。如需下线,烦请关闭连接信息中的Kafka发送/消费客户端后再进行下线。'} >
|
||||
<Icon className="question-icon" type="question-circle" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
maskClosable={false}
|
||||
onCancel={this.handleCancel}
|
||||
onOk={this.handleSubmit}
|
||||
okText="下线"
|
||||
cancelText="取消"
|
||||
okButtonProps={{ disabled: app.connectLoading || !!app.appsConnections.length }}
|
||||
width={700}
|
||||
>
|
||||
<Table
|
||||
rowKey="key"
|
||||
title={() => '连接信息'}
|
||||
loading={app.connectLoading}
|
||||
scroll={{ x: 450, y: 260 }}
|
||||
dataSource={app.appsConnections}
|
||||
columns={getApplyOnlineColumns()}
|
||||
pagination={false}
|
||||
bordered={true}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import * as React from 'react';
|
||||
import { Table, Modal, Tooltip, Icon, message, notification } from 'component/antd';
|
||||
import { app } from 'store/app';
|
||||
import { observer } from 'mobx-react';
|
||||
import { modal } from 'store/modal';
|
||||
import { users } from 'store/users';
|
||||
import { cellStyle } from 'constants/table';
|
||||
import { cluster } from 'store/cluster';
|
||||
|
||||
@observer
|
||||
export class OfflineClusterModal extends React.Component {
|
||||
|
||||
public componentDidMount() {
|
||||
cluster.getClusterMetaTopics(modal.params);
|
||||
}
|
||||
|
||||
public handleCancel = () => {
|
||||
modal.close();
|
||||
cluster.setClusterTopicsMeta([]);
|
||||
}
|
||||
|
||||
public handleSubmit = () => {
|
||||
if (cluster.clusterMetaTopics.length) {
|
||||
return message.warning('存在Topic信息,无法申请下线!');
|
||||
}
|
||||
const offlineParams = {
|
||||
type: 14,
|
||||
applicant: users.currentUser.username,
|
||||
description: '',
|
||||
extensions: JSON.stringify({clusterId: modal.params}),
|
||||
};
|
||||
cluster.applyClusterOffline(offlineParams).then(data => {
|
||||
notification.success({ message: '申请下线成功' });
|
||||
});
|
||||
modal.close();
|
||||
}
|
||||
|
||||
public getColumns = () => {
|
||||
const offlineColumns = [
|
||||
{
|
||||
title: 'Topic列表',
|
||||
dataIndex: 'topicName',
|
||||
key: 'topicName',
|
||||
onCell: () => ({
|
||||
style: {
|
||||
maxWidth: 250,
|
||||
...cellStyle,
|
||||
},
|
||||
}),
|
||||
render: (t: string) => {
|
||||
return (
|
||||
<Tooltip placement="bottomLeft" title={t} >{t}</Tooltip>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
return offlineColumns;
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
visible={true}
|
||||
className="stream-debug-modal"
|
||||
title={
|
||||
<span>
|
||||
申请下线
|
||||
<Tooltip placement="right" title={'如若有topic列表,则表示资源正处于使用中,禁止下线操作。如需下线,烦请下线topic列表所有topic。'} >
|
||||
<Icon className="question-icon" type="question-circle" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
maskClosable={false}
|
||||
onCancel={this.handleCancel}
|
||||
onOk={this.handleSubmit}
|
||||
okText="下线"
|
||||
cancelText="取消"
|
||||
okButtonProps={{ disabled: cluster.filterLoading || !!cluster.clusterMetaTopics.length }}
|
||||
width={700}
|
||||
>
|
||||
<Table
|
||||
rowKey="key"
|
||||
loading={cluster.filterLoading}
|
||||
dataSource={cluster.clusterMetaTopics}
|
||||
columns={this.getColumns()}
|
||||
scroll={{ x: 300, y: 320 }}
|
||||
pagination={false}
|
||||
bordered={true}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
353
kafka-manager-console/src/container/modal/order.tsx
Normal file
353
kafka-manager-console/src/container/modal/order.tsx
Normal file
@@ -0,0 +1,353 @@
|
||||
import { wrapper } from 'store';
|
||||
import { order } from 'store/order';
|
||||
import { message, Icon, notification, Modal, Table, Tooltip } from 'component/antd';
|
||||
import { IApprovalOrder, IBaseOrder, IOrderInfo } from 'types/base-type';
|
||||
import { admin } from 'store/admin';
|
||||
import { modal } from 'store/modal';
|
||||
import { cellStyle } from 'constants/table';
|
||||
import * as React from 'react';
|
||||
|
||||
const updateInputModal = (status: string, type: number) => {
|
||||
const formMap = wrapper.xFormWrapper.formMap;
|
||||
const region = type === 0 ? 5 : 3;
|
||||
const broker = type === 0 ? 6 : 4;
|
||||
formMap[region].invisible = status === 'region';
|
||||
formMap[broker].invisible = status !== 'region';
|
||||
|
||||
formMap[region].rules = type === 0 ? [{ required: status !== 'region' }] : [{ required: false }];
|
||||
formMap[broker].rules = type === 0 ? [{ required: status === 'region' }] : [{ required: false }];
|
||||
// tslint:disable-next-line:no-unused-expression
|
||||
wrapper.ref && wrapper.ref.updateFormMap$(formMap, wrapper.xFormWrapper.formData);
|
||||
};
|
||||
|
||||
const renderModalTilte = (type: number, status: number) => {
|
||||
return (
|
||||
<>
|
||||
<span key={'title-1'}>审批</span>
|
||||
{
|
||||
type === 3 && status === 1 ? <span key={'subtitle-1'}>
|
||||
<a target="_blank" href="https://github.com/didi/kafka-manager">
|
||||
<span className="safe-tip" key={1}>敏感操作:请确认您当前操作是否包含敏感信息</span>
|
||||
<Icon key={2} type="question-circle" />
|
||||
</a>
|
||||
</span> : null}
|
||||
</>);
|
||||
};
|
||||
|
||||
export const showApprovalModal = (info: IOrderInfo, status: number, from?: string) => {
|
||||
const { id, type } = info;
|
||||
const formMap = [{
|
||||
key: 'partitionNum',
|
||||
label: '分区数',
|
||||
type: 'input_number',
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入分区数(正数)',
|
||||
pattern: /^[1-9]\d*$/,
|
||||
}],
|
||||
}, {
|
||||
key: 'replicaNum',
|
||||
label: '副本数',
|
||||
type: 'input_number',
|
||||
defaultValue: 3,
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入副本数(正数)',
|
||||
pattern: /^[1-9]\d*$/,
|
||||
}],
|
||||
}, {
|
||||
key: 'retentionTime',
|
||||
label: '保存时间',
|
||||
defaultValue: '48',
|
||||
type: 'select',
|
||||
options: [{
|
||||
label: '12小时',
|
||||
value: '12',
|
||||
}, {
|
||||
label: '24小时',
|
||||
value: '24',
|
||||
}, {
|
||||
label: '48小时',
|
||||
value: '48',
|
||||
}, {
|
||||
label: '72小时',
|
||||
value: '72',
|
||||
}],
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请选择',
|
||||
}],
|
||||
}, {
|
||||
key: 'species',
|
||||
label: '类型',
|
||||
type: 'radio_group',
|
||||
defaultValue: 'region',
|
||||
options: [{
|
||||
label: 'Region',
|
||||
value: 'region',
|
||||
}, {
|
||||
label: 'Borker',
|
||||
value: 'broker',
|
||||
}],
|
||||
rules: [{ required: false, message: '请选择类型' }],
|
||||
attrs: {
|
||||
onChange(item: any) {
|
||||
updateInputModal(item.target.value, type);
|
||||
},
|
||||
},
|
||||
}, {
|
||||
key: 'brokerIdList',
|
||||
label: 'Broker',
|
||||
invisible: true,
|
||||
rules: [{ required: false, message: '请输入Broker' }],
|
||||
attrs: {
|
||||
placeholder: '请输入Broker',
|
||||
},
|
||||
}, {
|
||||
key: 'regionId',
|
||||
label: 'Region',
|
||||
type: 'select',
|
||||
invisible: false,
|
||||
options: admin.brokersRegions,
|
||||
rules: [{ required: true, message: '请选择Region' }],
|
||||
attrs: {
|
||||
placeholder: '请选择Region',
|
||||
},
|
||||
}] as any;
|
||||
|
||||
const quotaFormMap = [{
|
||||
key: 'nowPartitionNum',
|
||||
label: '现有分区数',
|
||||
type: 'input_number',
|
||||
defaultValue: info.detail.partitionNum || info.detail.presentPartitionNum,
|
||||
rules: [{ required: false }],
|
||||
attrs: { disabled: true },
|
||||
}, {
|
||||
key: 'regionName',
|
||||
label: '所属Region',
|
||||
defaultValue: info.detail.regionNameList,
|
||||
rules: [{ required: false }],
|
||||
attrs: { disabled: true },
|
||||
}, {
|
||||
key: 'regionBrokerIdList',
|
||||
label: 'RegionBroker',
|
||||
defaultValue: info.detail.regionBrokerIdList,
|
||||
rules: [{ required: false }],
|
||||
attrs: { disabled: true },
|
||||
}, {
|
||||
key: 'topicBrokerIdList',
|
||||
label: 'TopicBroker',
|
||||
defaultValue: info.detail.topicBrokerIdList,
|
||||
rules: [{ required: false }],
|
||||
attrs: { disabled: true },
|
||||
}, {
|
||||
key: 'partitionNum',
|
||||
label: '新增分区数',
|
||||
type: 'input_number',
|
||||
rules: [{
|
||||
required: type !== 2,
|
||||
message: '请输入分区数(正数)',
|
||||
pattern: /^[1-9]\d*$/,
|
||||
}],
|
||||
}, {
|
||||
key: 'brokerIdList',
|
||||
label: '扩至Broker',
|
||||
defaultValue: info.detail.regionBrokerIdList,
|
||||
rules: [{ required: true }],
|
||||
}] as any;
|
||||
|
||||
const xFormWrapper = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'id',
|
||||
label: '工单ID',
|
||||
type: 'input_number',
|
||||
attrs: { disabled: true },
|
||||
},
|
||||
{
|
||||
key: 'opinion',
|
||||
label: '审批意见',
|
||||
type: 'text_area',
|
||||
rules: [{
|
||||
required: status === 2,
|
||||
validator: (rule: any, value: string, callback: any) => {
|
||||
if (status === 2) {
|
||||
value = value.trim();
|
||||
const regexp = /^[ ]+$/;
|
||||
if (value.length <= 0 || regexp.test(value)) {
|
||||
callback('审批意见不能为空');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}],
|
||||
}],
|
||||
formData: { id },
|
||||
okText: status === 1 ? '通过' : '驳回',
|
||||
visible: true,
|
||||
width: type === 0 ? 760 : 520,
|
||||
title: renderModalTilte(type, status) as any,
|
||||
onSubmit: (value: any) => {
|
||||
value.id = id;
|
||||
value.status = status;
|
||||
if (value.brokerIdList) {
|
||||
value.brokerIdList = value.brokerIdList && Array.isArray(value.brokerIdList) ?
|
||||
value.brokerIdList : value.brokerIdList.split(',');
|
||||
}
|
||||
let params = {} as any;
|
||||
if (type === 0) {
|
||||
params = {
|
||||
replicaNum: value.replicaNum ? Number(value.replicaNum) : '',
|
||||
partitionNum: value.partitionNum ? Number(value.partitionNum) : null,
|
||||
retentionTime: value.retentionTime,
|
||||
brokerIdList: value.brokerIdList,
|
||||
regionId: value.regionId,
|
||||
};
|
||||
params.regionId ? delete params.brokerIdList : delete params.regionId;
|
||||
} else if (type === 2 || type === 12) {
|
||||
params = {
|
||||
partitionNum: value.partitionNum ? Number(value.partitionNum) : null,
|
||||
brokerIdList: value.brokerIdList,
|
||||
};
|
||||
} else {
|
||||
params = {};
|
||||
}
|
||||
const orderParams = {
|
||||
id,
|
||||
opinion: value.opinion.trim(),
|
||||
status,
|
||||
detail: JSON.stringify(params),
|
||||
} as IApprovalOrder;
|
||||
order.approvalOrder(orderParams).then(() => {
|
||||
message.success('操作成功');
|
||||
if (from) {
|
||||
order.getOrderDetail(id);
|
||||
}
|
||||
order.getApplyOrderList(0);
|
||||
order.getApprovalList(0);
|
||||
}).catch(err => {
|
||||
if (!err || !err.code) {
|
||||
notification.error({
|
||||
message: '错误',
|
||||
description: '网络或服务器错误,请重试!',
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
if (type === 0 && status === 1) { // 通过 topic
|
||||
xFormWrapper.formMap.splice(1, 0, ...formMap);
|
||||
}
|
||||
if ((type === 2 || type === 12) && status === 1) { // 通过配额 12分区
|
||||
xFormWrapper.formMap.splice(1, 0, ...quotaFormMap);
|
||||
}
|
||||
|
||||
wrapper.open(xFormWrapper);
|
||||
};
|
||||
|
||||
export const renderOrderOpModal = (selectedRowKeys: IBaseOrder[], status: number) => {
|
||||
const orderIdList = selectedRowKeys.map((ele: IBaseOrder) => {
|
||||
return ele.id;
|
||||
});
|
||||
const xFormWrapper = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'opinion',
|
||||
label: '审批意见',
|
||||
type: 'text_area',
|
||||
rules: [{
|
||||
required: true,
|
||||
validator: (rule: any, value: string, callback: any) => {
|
||||
value = value.trim();
|
||||
const regexp = /^[ ]+$/;
|
||||
if (value.length <= 0 || regexp.test(value)) {
|
||||
callback('审批意见不能为空');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}],
|
||||
},
|
||||
],
|
||||
formData: {},
|
||||
visible: true,
|
||||
okText: status === 1 ? '通过' : '驳回',
|
||||
title: status === 1 ? '通过' : '驳回',
|
||||
onSubmit: async (value: any) => {
|
||||
const params = {
|
||||
opinion: value.opinion,
|
||||
orderIdList,
|
||||
status,
|
||||
};
|
||||
order.batchApprovalOrders(params).then(data => {
|
||||
modal.setAction('close');
|
||||
modal.showOrderOpResult();
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormWrapper);
|
||||
};
|
||||
|
||||
export const RenderOrderOpResult = () => {
|
||||
const handleOk = () => {
|
||||
order.getApplyOrderList(0);
|
||||
order.getApprovalList(0);
|
||||
modal.close();
|
||||
};
|
||||
|
||||
const flowColumns = [
|
||||
{
|
||||
title: '工单Id',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'code',
|
||||
key: 'code',
|
||||
render: (t: number) => {
|
||||
return (
|
||||
<span className={t === 0 ? 'success' : 'fail'}>
|
||||
{t === 0 ? '成功' : '失败'}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '原因',
|
||||
dataIndex: 'message',
|
||||
key: 'message',
|
||||
onCell: () => ({
|
||||
style: {
|
||||
maxWidth: 120,
|
||||
...cellStyle,
|
||||
},
|
||||
}),
|
||||
render: (text: string) => {
|
||||
return (
|
||||
<Tooltip placement="bottomLeft" title={text} >
|
||||
{text}
|
||||
</Tooltip>);
|
||||
},
|
||||
},
|
||||
];
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
title="审批结果"
|
||||
visible={true}
|
||||
onOk={handleOk}
|
||||
onCancel={handleOk}
|
||||
>
|
||||
<Table
|
||||
columns={flowColumns}
|
||||
rowKey="id"
|
||||
dataSource={order.batchApprovalList}
|
||||
scroll={{ x: 450, y: 260 }}
|
||||
pagination={false}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
import * as React from 'react';
|
||||
import { Table, Modal, Tooltip, Icon, message, notification } from 'component/antd';
|
||||
import { getApplyOnlineColumns } from 'container/topic/config';
|
||||
import { observer } from 'mobx-react';
|
||||
import { modal } from 'store/modal';
|
||||
import { users } from 'store/users';
|
||||
import { urlPrefix } from 'constants/left-menu';
|
||||
import { region } from 'store';
|
||||
import { topic } from 'store/topic';
|
||||
|
||||
@observer
|
||||
export class RenderOrderOpResult extends React.Component {
|
||||
|
||||
public componentDidMount() {
|
||||
topic.getConnectionInfo(modal.params.clusterId, modal.params.topicName, modal.params.appId);
|
||||
}
|
||||
|
||||
public handleCancel = () => {
|
||||
topic.setConnectionInfo([]);
|
||||
modal.close();
|
||||
}
|
||||
|
||||
public handleSubmit = () => {
|
||||
const connectionList = topic.connectionInfo;
|
||||
if (connectionList && connectionList.length) {
|
||||
return message.warning('存在连接信息,无法申请下线!');
|
||||
}
|
||||
const online = {
|
||||
clusterId: modal.params.clusterId,
|
||||
topicName: modal.params.topicName,
|
||||
};
|
||||
const offlineParams = {
|
||||
type: 10,
|
||||
applicant: users.currentUser.username,
|
||||
description: '',
|
||||
extensions: JSON.stringify(online),
|
||||
};
|
||||
topic.applyTopicOnline(offlineParams).then((data: any) => {
|
||||
notification.success({ message: '申请下线成功' });
|
||||
window.location.href = `${urlPrefix}/user/order-detail/?orderId=${data.id}®ion=${region.currentRegion}`;
|
||||
});
|
||||
modal.close();
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
visible={true}
|
||||
className="stream-debug-modal"
|
||||
title={
|
||||
<span>
|
||||
申请下线
|
||||
<Tooltip placement="right" title={'当前Topic存在消费的AppID,请联系相关AppID负责人取消权限才能继续下线'} >
|
||||
<Icon className="question-icon" type="question-circle" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
maskClosable={false}
|
||||
onCancel={this.handleCancel}
|
||||
onOk={this.handleSubmit}
|
||||
okText="下线"
|
||||
cancelText="取消"
|
||||
okButtonProps={{ disabled: topic.connectLoading || !!topic.connectionInfo.length }}
|
||||
width={700}
|
||||
>
|
||||
<Table
|
||||
rowKey="key"
|
||||
title={() => '连接信息'}
|
||||
loading={topic.connectLoading}
|
||||
scroll={{ x: 450, y: 260 }}
|
||||
dataSource={topic.connectionInfo}
|
||||
columns={getApplyOnlineColumns()}
|
||||
pagination={false}
|
||||
bordered={true}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
753
kafka-manager-console/src/container/modal/topic.tsx
Normal file
753
kafka-manager-console/src/container/modal/topic.tsx
Normal file
@@ -0,0 +1,753 @@
|
||||
import { wrapper } from 'store';
|
||||
import { notification } from 'component/antd';
|
||||
import { IQuotaModelItem, ITopic, ILimitsItem, IQuotaQuery } from 'types/base-type';
|
||||
import { topic, IAppsIdInfo } from 'store/topic';
|
||||
import { users } from 'store/users';
|
||||
import { app } from 'store/app';
|
||||
import { cluster } from 'store/cluster';
|
||||
import { AppSelect } from 'container/app-select';
|
||||
import { PeakFlowInput } from '../topic/peak-flow';
|
||||
import { urlPrefix } from 'constants/left-menu';
|
||||
import { transMBToB, transBToMB } from 'lib/utils';
|
||||
import { region } from 'store';
|
||||
import * as React from 'react';
|
||||
import '../app/index.less';
|
||||
import { modal } from 'store/modal';
|
||||
import { TopicAppSelect } from '../topic/topic-app-select';
|
||||
import Url from 'lib/url-parser';
|
||||
import { expandRemarks, quotaRemarks } from 'constants/strategy';
|
||||
|
||||
export const applyTopic = () => {
|
||||
const xFormModal = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'clusterId',
|
||||
label: '所属集群:',
|
||||
type: 'select',
|
||||
options: cluster.clusterData,
|
||||
rules: [{ required: true, message: '请选择' }],
|
||||
attrs: {
|
||||
placeholder: '请选择',
|
||||
},
|
||||
}, {
|
||||
key: 'topicName',
|
||||
label: 'Topic名称:',
|
||||
attrs: {
|
||||
addonBefore: region.currentRegion === 'us' || region.currentRegion === 'ru' ? `${region.currentRegion}01_` : '',
|
||||
},
|
||||
rules: [
|
||||
{ required: true },
|
||||
{
|
||||
pattern: /^[-\w]{3,128}$/,
|
||||
message: '只能包含字母、数字、下划线(_)和短划线(-),长度限制在3-128字符之间',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'appId',
|
||||
label: '所属应用:',
|
||||
type: 'custom',
|
||||
defaultValue: '',
|
||||
rules: [{ required: true, message: '请选择' }],
|
||||
customFormItem: <AppSelect selectData={app.data} />,
|
||||
}, {
|
||||
key: 'peakBytesIn',
|
||||
label: '峰值流量',
|
||||
type: 'custom',
|
||||
rules: [{
|
||||
required: true,
|
||||
pattern: /^[1-9]\d*$/,
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
const regexp = /^[1-9]\d*$/;
|
||||
if (value.length <= 0) {
|
||||
callback('流量上限不能为空');
|
||||
return false;
|
||||
} else if (!regexp.test(value)) {
|
||||
callback('流量上限只能填写大于0的正整数');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}],
|
||||
customFormItem: <PeakFlowInput />,
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
label: '申请原因',
|
||||
type: 'text_area',
|
||||
rules: [{ required: true, pattern: /^.{5,}.$/s, message: '请输入至少5个字符' }],
|
||||
attrs: {
|
||||
placeholder: `概要描述Topic的数据源, Topic数据的生产者/消费者, Topic的申请原因及备注信息等。(最多100个字)
|
||||
例如:
|
||||
数据源:xxx
|
||||
生产消费方:xxx
|
||||
申请原因及备注:xxx`,
|
||||
rows: 7,
|
||||
},
|
||||
},
|
||||
],
|
||||
formData: {},
|
||||
visible: true,
|
||||
title: '申请Topic',
|
||||
okText: '确认',
|
||||
onSubmit: (value: any) => {
|
||||
value.topicName = region.currentRegion === 'us' || region.currentRegion === 'ru' ?
|
||||
`${region.currentRegion}01_` + value.topicName : value.topicName;
|
||||
value.peakBytesIn = transMBToB(value.peakBytesIn);
|
||||
const params = JSON.parse(JSON.stringify(value));
|
||||
delete (params.description);
|
||||
const quotaParams = {
|
||||
type: 0,
|
||||
applicant: users.currentUser.username,
|
||||
description: value.description,
|
||||
extensions: JSON.stringify(params),
|
||||
};
|
||||
topic.applyTopic(quotaParams).then(data => {
|
||||
notification.success({ message: '申请Topic成功' });
|
||||
window.location.href = `${urlPrefix}/user/order-detail/?orderId=${data.id}®ion=${region.currentRegion}`;
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
|
||||
export const deferTopic = (item: ITopic) => {
|
||||
const xFormModal = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'topicName',
|
||||
label: 'Topic名称',
|
||||
value: '',
|
||||
rules: [{ required: false, disabled: true }],
|
||||
attrs: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'retainDays',
|
||||
label: '延期时间',
|
||||
type: 'select',
|
||||
value: '',
|
||||
options: [{
|
||||
label: '一周',
|
||||
value: '7',
|
||||
}, {
|
||||
label: '一月',
|
||||
value: '30',
|
||||
}, {
|
||||
label: '三月',
|
||||
value: '90',
|
||||
}],
|
||||
rules: [{ required: false }],
|
||||
attrs: { placeholder: '请选择延期时间' },
|
||||
},
|
||||
],
|
||||
formData: {
|
||||
topicName: item.topicName,
|
||||
retainDays: '',
|
||||
},
|
||||
visible: true,
|
||||
title: '申请延期',
|
||||
okText: '确认',
|
||||
onSubmit: (value: any) => {
|
||||
value.clusterId = item.clusterId;
|
||||
topic.deferTopic(value).then(data => {
|
||||
notification.success({ message: '申请延期成功' });
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
|
||||
export const applyOnlineModal = (item: ITopic) => {
|
||||
modal.showOfflineTopicModal(item);
|
||||
};
|
||||
|
||||
export const showApplyQuatoModal = (item: ITopic | IAppsIdInfo, record: IQuotaQuery) => {
|
||||
const isProduce = item.access === 0 || item.access === 1;
|
||||
const isConsume = item.access === 0 || item.access === 2;
|
||||
const xFormModal = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'clusterName',
|
||||
label: '集群名称',
|
||||
rules: [{ required: true, message: '' }],
|
||||
attrs: { disabled: true },
|
||||
invisible: !item.hasOwnProperty('clusterName'),
|
||||
}, {
|
||||
key: 'topicName',
|
||||
label: 'Topic名称',
|
||||
rules: [{ required: true, message: '' }],
|
||||
attrs: { disabled: true },
|
||||
}, {
|
||||
key: 'appId',
|
||||
label: '所属应用:',
|
||||
defaultValue: '',
|
||||
rules: [{ required: true, message: '请输入' }],
|
||||
attrs: { disabled: true },
|
||||
}, {
|
||||
key: 'produceQuota',
|
||||
label: '发送数据速率',
|
||||
attrs: {
|
||||
disabled: isProduce,
|
||||
placeholder: '请输入',
|
||||
suffix: 'MB/s',
|
||||
},
|
||||
rules: [{
|
||||
required: !isProduce,
|
||||
message: '请输入',
|
||||
}],
|
||||
}, {
|
||||
key: 'consumeQuota',
|
||||
label: '消费数据速率',
|
||||
attrs: {
|
||||
disabled: isConsume,
|
||||
placeholder: '请输入',
|
||||
suffix: 'MB/s',
|
||||
},
|
||||
rules: [{
|
||||
required: !isConsume,
|
||||
message: '请输入',
|
||||
}],
|
||||
}, {
|
||||
key: 'description',
|
||||
label: '申请原因',
|
||||
type: 'text_area',
|
||||
rules: [{ required: true, pattern: /^.{5,}.$/, message: quotaRemarks }],
|
||||
attrs: {
|
||||
placeholder: quotaRemarks,
|
||||
},
|
||||
}],
|
||||
formData: {
|
||||
clusterName: item.clusterName,
|
||||
topicName: record.topicName || item.topicName,
|
||||
appId: record.appId || item.appId,
|
||||
produceQuota: transBToMB(record.produceQuota),
|
||||
consumeQuota: transBToMB(record.consumeQuota),
|
||||
},
|
||||
okText: '确认',
|
||||
visible: true,
|
||||
title: '申请配额',
|
||||
onSubmit: (value: any) => {
|
||||
const quota = {} as IQuotaModelItem;
|
||||
Object.assign(quota, {
|
||||
clusterId: record.clusterId || item.clusterId,
|
||||
topicName: record.topicName || item.topicName,
|
||||
appId: record.appId || item.appId,
|
||||
consumeQuota: transMBToB(value.consumeQuota),
|
||||
produceQuota: transMBToB(value.produceQuota),
|
||||
});
|
||||
if (item.isPhysicalClusterId) {
|
||||
Object.assign(quota, {
|
||||
isPhysicalClusterId: true,
|
||||
});
|
||||
}
|
||||
const quotaParams = {
|
||||
type: 2,
|
||||
applicant: users.currentUser.username,
|
||||
description: value.description,
|
||||
extensions: JSON.stringify(quota),
|
||||
};
|
||||
topic.applyQuota(quotaParams).then((data) => {
|
||||
notification.success({ message: '申请配额成功' });
|
||||
window.location.href = `${urlPrefix}/user/order-detail/?orderId=${data.id}®ion=${region.currentRegion}`;
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
|
||||
let permission: number = null;
|
||||
|
||||
const updateFormModal = (appId: string) => {
|
||||
const formMap = wrapper.xFormWrapper.formMap;
|
||||
const formData = wrapper.xFormWrapper.formData;
|
||||
const quota = app.appQuota.filter(ele => ele.appId === appId);
|
||||
permission = quota[0].access;
|
||||
const isProduce = quota[0].access === 0 || quota[0].access === 1;
|
||||
const isConsume = quota[0].access === 0 || quota[0].access === 2;
|
||||
|
||||
formData.produceQuota = transBToMB(quota[0].produceQuota);
|
||||
formData.consumeQuota = transBToMB(quota[0].consumerQuota);
|
||||
formMap[3].attrs = { disabled: isProduce, suffix: 'MB/s' };
|
||||
formMap[3].rules = [{ required: !isProduce, message: '请输入' }];
|
||||
formMap[4].attrs = { disabled: isConsume, suffix: 'MB/s' };
|
||||
formMap[4].rules = [{ required: !isConsume, message: '请输入' }];
|
||||
// tslint:disable-next-line:no-unused-expression
|
||||
wrapper.ref && wrapper.ref.updateFormMap$(formMap, formData);
|
||||
};
|
||||
|
||||
export const showTopicApplyQuatoModal = (item: ITopic) => {
|
||||
const xFormModal = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'clusterName',
|
||||
label: '集群名称',
|
||||
rules: [{ required: true, message: '' }],
|
||||
attrs: { disabled: true },
|
||||
invisible: !item.hasOwnProperty('clusterName'),
|
||||
}, {
|
||||
key: 'topicName',
|
||||
label: 'Topic名称',
|
||||
rules: [{ required: true, message: '' }],
|
||||
attrs: { disabled: true },
|
||||
}, {
|
||||
key: 'appId',
|
||||
label: '所属应用:',
|
||||
defaultValue: '',
|
||||
rules: [{
|
||||
required: true,
|
||||
validator: (rule: any, value: string, callback: any) => {
|
||||
if (!value) {
|
||||
callback('请选择应用');
|
||||
return false;
|
||||
}
|
||||
if (permission === 0) {
|
||||
callback('该应用无当前topic权限!请选择其他应用,或申请权限。');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}],
|
||||
type: 'select',
|
||||
options: app.appQuota,
|
||||
attrs: {
|
||||
onChange(value: string) {
|
||||
updateFormModal(value);
|
||||
},
|
||||
},
|
||||
}, { // 0 无权限 1可读 2可写 3 可读写 4可读写可管理
|
||||
key: 'produceQuota',
|
||||
label: '发送数据速率',
|
||||
attrs: {
|
||||
disabled: false,
|
||||
placeholder: '请输入',
|
||||
suffix: 'MB/s',
|
||||
},
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入',
|
||||
}],
|
||||
}, {
|
||||
key: 'consumeQuota',
|
||||
label: '消费数据速率',
|
||||
attrs: {
|
||||
disabled: false,
|
||||
placeholder: '请输入',
|
||||
suffix: 'MB/s',
|
||||
},
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入',
|
||||
}],
|
||||
}, {
|
||||
key: 'description',
|
||||
label: '申请原因',
|
||||
type: 'text_area',
|
||||
rules: [{ required: true, pattern: /^.{5,}$/, message: quotaRemarks }],
|
||||
attrs: {
|
||||
placeholder: quotaRemarks,
|
||||
},
|
||||
}],
|
||||
formData: {
|
||||
clusterName: item.clusterName,
|
||||
topicName: item.topicName,
|
||||
},
|
||||
okText: '确认',
|
||||
visible: true,
|
||||
title: '申请配额',
|
||||
onSubmit: (value: any) => {
|
||||
const quota = {} as IQuotaModelItem;
|
||||
Object.assign(quota, {
|
||||
clusterId: item.clusterId,
|
||||
topicName: item.topicName,
|
||||
appId: value.appId,
|
||||
consumeQuota: transMBToB(value.consumeQuota),
|
||||
produceQuota: transMBToB(value.produceQuota),
|
||||
});
|
||||
const quotaParams = {
|
||||
type: 2,
|
||||
applicant: users.currentUser.username,
|
||||
description: value.description,
|
||||
extensions: JSON.stringify(quota),
|
||||
};
|
||||
topic.applyQuota(quotaParams).then((data) => {
|
||||
notification.success({ message: '申请配额成功' });
|
||||
window.location.href = `${urlPrefix}/user/order-detail/?orderId=${data.id}®ion=${region.currentRegion}`;
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
|
||||
export const updateAllTopicFormModal = () => {
|
||||
const formMap = wrapper.xFormWrapper.formMap;
|
||||
if (topic.authorities) {
|
||||
const { consume, send, checkStatus } = judgeAccessStatus(topic.authorities.access);
|
||||
formMap[3].defaultValue = checkStatus;
|
||||
formMap[3].options = [{
|
||||
label: `消费权限${consume ? '(已拥有)' : ''}`,
|
||||
value: '1',
|
||||
disabled: consume,
|
||||
}, {
|
||||
label: `发送权限${send ? '(已拥有)' : ''}`,
|
||||
value: '2',
|
||||
disabled: send,
|
||||
}];
|
||||
formMap[3].rules = [{
|
||||
required: true,
|
||||
validator: (rule: any, value: any, callback: any) => getPowerValidator(rule, value, callback, checkStatus, 'allTopic'),
|
||||
}];
|
||||
}
|
||||
// tslint:disable-next-line:no-unused-expression
|
||||
wrapper.ref && wrapper.ref.updateFormMap$(formMap, wrapper.xFormWrapper.formData, true, ['access', 'description']);
|
||||
};
|
||||
|
||||
const getPowerValidator = (rule: any, value: any, callback: any, checkStatus: any, isAll?: any) => {
|
||||
if (
|
||||
(!checkStatus.length && !value.length) ||
|
||||
(checkStatus.indexOf('1') !== -1 || checkStatus.indexOf('2') !== -1) && value.length === 1
|
||||
) {
|
||||
callback('请选择权限!');
|
||||
return false;
|
||||
}
|
||||
if (isAll && checkStatus.length === 2) {
|
||||
callback('您已拥有发送,消费权限!');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const getCheckStatus = (checkStatus: string[], accessValue: string) => {
|
||||
let access = null as string;
|
||||
if (!checkStatus.length) {
|
||||
access = accessValue.length === 2 ? '3' : accessValue[0];
|
||||
} else if (checkStatus.indexOf('1') !== -1 && checkStatus.length === 1) {
|
||||
access = '2';
|
||||
} else if (checkStatus.indexOf('2') !== -1 && checkStatus.length === 1) {
|
||||
access = '1';
|
||||
}
|
||||
return access;
|
||||
};
|
||||
|
||||
const judgeAccessStatus = (access: number) => {
|
||||
const consume = access === 1 || access === 3 || access === 4;
|
||||
const send = access === 2 || access === 3 || access === 4;
|
||||
const checkStatus = access === 0 ? [] : (access === 1 || access === 2) ? [access + ''] : ['1', '2'];
|
||||
return { consume, send, checkStatus };
|
||||
};
|
||||
|
||||
export const showAllPermissionModal = (item: ITopic) => {
|
||||
let appId: string = null;
|
||||
|
||||
if (!app.data || !app.data.length) {
|
||||
return notification.info({
|
||||
message: (
|
||||
<>
|
||||
<span>
|
||||
您的账号暂无可用应用,请先
|
||||
<a href={`${urlPrefix}/topic/app-list?application=1`}>申请应用</a>
|
||||
</span>
|
||||
</>),
|
||||
});
|
||||
}
|
||||
const index = app.data.findIndex(row => row.appId === item.appId);
|
||||
|
||||
appId = index > -1 ? item.appId : app.data[0].appId;
|
||||
topic.getAuthorities(appId, item.clusterId, item.topicName).then((data) => {
|
||||
showAllPermission(appId, item, data.access);
|
||||
});
|
||||
};
|
||||
|
||||
const showAllPermission = (appId: string, item: ITopic, access: number) => {
|
||||
const { consume, send, checkStatus } = judgeAccessStatus(access);
|
||||
|
||||
const xFormModal = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'topicName',
|
||||
label: 'Topic名称',
|
||||
defaultValue: item.topicName,
|
||||
rules: [{ required: true, message: '请输入Topic名称' }],
|
||||
attrs: {
|
||||
placeholder: '请输入Topic名称',
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'clusterName',
|
||||
label: '集群名称',
|
||||
defaultValue: item.clusterName,
|
||||
rules: [{ required: true, message: '请输入集群名称' }],
|
||||
attrs: {
|
||||
placeholder: '请输入集群名称',
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'appId',
|
||||
label: '绑定应用',
|
||||
defaultValue: appId,
|
||||
rules: [{ required: true, message: '请选择应用' }],
|
||||
type: 'custom',
|
||||
customFormItem: <TopicAppSelect selectData={app.data} parameter={item} />,
|
||||
},
|
||||
{
|
||||
key: 'access',
|
||||
label: '权限',
|
||||
type: 'check_box',
|
||||
defaultValue: checkStatus,
|
||||
options: [{
|
||||
label: `消费权限${consume ? '(已拥有)' : ''}`,
|
||||
value: '1',
|
||||
disabled: consume,
|
||||
}, {
|
||||
label: `发送权限${send ? '(已拥有)' : ''}`,
|
||||
value: '2',
|
||||
disabled: send,
|
||||
}],
|
||||
rules: [{
|
||||
required: true,
|
||||
validator: (rule: any, value: any, callback: any) => getPowerValidator(rule, value, callback, checkStatus, 'allTopic'),
|
||||
}],
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
label: '申请原因',
|
||||
type: 'text_area',
|
||||
rules: [{
|
||||
required: true,
|
||||
validator: (rule: any, value: string, callback: any) => {
|
||||
const regexp = /^.{5,}.$/;
|
||||
value = value.trim();
|
||||
if (!regexp.test(value)) {
|
||||
callback('请输入至少5个字符');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请输入至少5个字符',
|
||||
},
|
||||
},
|
||||
],
|
||||
formData: {},
|
||||
visible: true,
|
||||
title: '申请权限',
|
||||
okText: '确认',
|
||||
onSubmit: (value: ILimitsItem) => {
|
||||
const { checkStatus: originStatus } = judgeAccessStatus(topic.authorities.access);
|
||||
const access = getCheckStatus(originStatus, value.access);
|
||||
const params = {} as ILimitsItem;
|
||||
Object.assign(params, { clusterId: item.clusterId, topicName: item.topicName, appId: value.appId, access });
|
||||
const accessParams = {
|
||||
type: 3,
|
||||
applicant: users.currentUser.username,
|
||||
description: value.description.trim(),
|
||||
extensions: JSON.stringify(params),
|
||||
};
|
||||
topic.applyQuota(accessParams).then(data => {
|
||||
notification.success({ message: '申请权限成功' });
|
||||
window.location.href = `${urlPrefix}/user/order-detail/?orderId=${data.id}®ion=${region.currentRegion}`;
|
||||
}).catch((err) => {
|
||||
notification.error({ message: '申请权限失败' });
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
|
||||
export const showPermissionModal = (item: ITopic) => {
|
||||
const { consume, send, checkStatus } = judgeAccessStatus(item.access);
|
||||
const xFormModal = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'topicName',
|
||||
label: 'Topic名称',
|
||||
defaultValue: item.topicName,
|
||||
rules: [{ required: true, message: '请输入Topic名称' }],
|
||||
attrs: {
|
||||
placeholder: '请输入Topic名称',
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'clusterName',
|
||||
label: '集群名称',
|
||||
defaultValue: item.clusterName,
|
||||
rules: [{ required: true, message: '请输入集群名称' }],
|
||||
attrs: {
|
||||
placeholder: '请输入集群名称',
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'appName',
|
||||
label: '绑定应用',
|
||||
defaultValue: `${item.appName}(${item.appId})`,
|
||||
rules: [{ required: true, message: '请选择应用' }],
|
||||
attrs: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'access',
|
||||
label: '权限',
|
||||
type: 'check_box',
|
||||
defaultValue: checkStatus,
|
||||
options: [{
|
||||
label: `消费权限${consume ? '(已拥有)' : ''}`,
|
||||
value: '1',
|
||||
disabled: consume,
|
||||
}, {
|
||||
label: `发送权限${send ? '(已拥有)' : ''}`,
|
||||
value: '2',
|
||||
disabled: send,
|
||||
}],
|
||||
rules: [{
|
||||
required: true,
|
||||
validator: (rule: any, value: any, callback: any) => getPowerValidator(rule, value, callback, checkStatus),
|
||||
}],
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
label: '申请原因',
|
||||
type: 'text_area',
|
||||
rules: [{
|
||||
required: true,
|
||||
validator: (rule: any, value: string, callback: any) => {
|
||||
const regexp = /^.{5,}.$/;
|
||||
value = value.trim();
|
||||
if (!regexp.test(value)) {
|
||||
callback('请输入至少5个字符');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}],
|
||||
attrs: {
|
||||
placeholder: '请输入至少5个字符',
|
||||
},
|
||||
},
|
||||
],
|
||||
formData: {},
|
||||
visible: true,
|
||||
title: '申请权限',
|
||||
okText: '确认',
|
||||
onSubmit: (value: ILimitsItem) => {
|
||||
const access = getCheckStatus(checkStatus, value.access);
|
||||
const params = {} as ILimitsItem;
|
||||
Object.assign(params, { clusterId: item.clusterId, topicName: item.topicName, appId: item.appId, access });
|
||||
const accessParams = {
|
||||
type: 3,
|
||||
applicant: users.currentUser.username,
|
||||
description: value.description.trim(),
|
||||
extensions: JSON.stringify(params),
|
||||
};
|
||||
topic.applyQuota(accessParams).then(data => {
|
||||
notification.success({ message: '申请权限成功' });
|
||||
window.location.href = `${urlPrefix}/user/order-detail/?orderId=${data.id}®ion=${region.currentRegion}`;
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
|
||||
export const showTopicEditModal = (item: ITopic) => {
|
||||
const xFormModal = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'topicName',
|
||||
label: 'Topic名称',
|
||||
attrs: { disabled: true },
|
||||
rules: [{ required: false }],
|
||||
}, {
|
||||
key: 'description',
|
||||
label: '备注',
|
||||
type: 'text_area',
|
||||
rules: [{ required: false }, { pattern: /^.{5,}.$/, message: '请输入至少5个字符' }],
|
||||
},
|
||||
],
|
||||
formData: {
|
||||
topicName: item.topicName,
|
||||
description: item.description,
|
||||
},
|
||||
visible: true,
|
||||
title: '编辑',
|
||||
onSubmit: (value: any) => {
|
||||
const params = {} as IQuotaModelItem;
|
||||
Object.assign(params,
|
||||
{
|
||||
clusterId: item.clusterId,
|
||||
topicName: value.topicName,
|
||||
description: value.description,
|
||||
});
|
||||
|
||||
topic.updateTopic(params).then(() => {
|
||||
notification.success({ message: '编辑成功' });
|
||||
topic.getTopic();
|
||||
topic.getExpired();
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
|
||||
export const applyExpandModal = (item: ITopic) => {
|
||||
const xFormModal = {
|
||||
formMap: [
|
||||
{
|
||||
key: 'topicName',
|
||||
label: 'Topic名称',
|
||||
attrs: { disabled: true },
|
||||
rules: [{ required: true }],
|
||||
}, {
|
||||
key: 'needIncrPartitionNum',
|
||||
label: '分区',
|
||||
type: 'input_number',
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入0-100正整数',
|
||||
pattern: /^((?!0)\d{1,2}|100)$/,
|
||||
}],
|
||||
attrs: { placeholder: '0-100正整数' },
|
||||
renderExtraElement: () => <div className="form-tip mr--10">分区标准为3MB/s一个,请按需申请</div>,
|
||||
}, {
|
||||
key: 'description',
|
||||
label: '申请原因',
|
||||
type: 'text_area',
|
||||
rules: [{ required: true, pattern: /^.{5,}.$/, message: expandRemarks }],
|
||||
attrs: { placeholder: expandRemarks },
|
||||
},
|
||||
],
|
||||
formData: {
|
||||
topicName: item.topicName,
|
||||
description: item.description,
|
||||
},
|
||||
visible: true,
|
||||
title: '申请分区',
|
||||
customRenderElement: <div className="expand-text">若Topic已被限流,则申请分区无效,请直接“申请配额”!</div>,
|
||||
onSubmit: (value: any) => {
|
||||
const isPhysicalClusterId = Url().search.hasOwnProperty('isPhysicalClusterId') && Url().search.isPhysicalClusterId;
|
||||
const offlineParams = {
|
||||
type: 12,
|
||||
applicant: users.currentUser.username,
|
||||
description: value.description,
|
||||
extensions: JSON.stringify({
|
||||
clusterId: item.clusterId,
|
||||
topicName: item.topicName,
|
||||
isPhysicalClusterId,
|
||||
needIncrPartitionNum: value.needIncrPartitionNum,
|
||||
}),
|
||||
};
|
||||
app.applyExpand(offlineParams).then((data: any) => {
|
||||
notification.success({ message: '申请分区成功' });
|
||||
window.location.href = `${urlPrefix}/user/order-detail/?orderId=${data.id}®ion=${region.currentRegion}`;
|
||||
}).catch((err) => {
|
||||
notification.error({ message: '申请权限失败' });
|
||||
});
|
||||
},
|
||||
};
|
||||
wrapper.open(xFormModal);
|
||||
};
|
||||
Reference in New Issue
Block a user