kafka-manager 2.0

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

View File

@@ -0,0 +1,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);
};

View File

@@ -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);

View File

@@ -0,0 +1,5 @@
export * from './user';
export * from './version';
export * from './cluster';
export * from './task';
export * from './migration';

View File

@@ -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);

View 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);
};

View 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: '操作成功' });
});
};

View 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);
};

View 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);
};