Compare commits

..

4 Commits

Author SHA1 Message Date
zengqiao
2fc283990a bump version to 2.1.0 2020-12-19 01:53:46 +08:00
zengqiao
55786cb7f7 修改node版本要求 2020-12-19 00:45:58 +08:00
zengqiao
447a575f4f v2.1 fe 2020-12-19 00:40:52 +08:00
zengqiao
49280a8617 v2.1版本更新 2020-12-19 00:27:16 +08:00
171 changed files with 2710 additions and 737 deletions

View File

@@ -3,8 +3,8 @@ workspace=$(cd $(dirname $0) && pwd -P)
cd $workspace
## constant
app_name=kafka-manager
output_dir=output
km_version=2.1.0
app_name=kafka-manager-$km_version
gitversion=.gitversion
control=./control.sh
@@ -38,17 +38,17 @@ function build() {
function make_output() {
# 新建output目录
rm -rf $output_dir &>/dev/null
mkdir -p $output_dir &>/dev/null
rm -rf $app_name &>/dev/null
mkdir -p $app_name &>/dev/null
# 填充output目录, output内的内容 即为 线上部署内容
(
# cp -rf $control $output_dir && # 拷贝 control.sh 脚本 至output目录
cp -rf $create_mysql_table $output_dir && # 拷贝 sql 初始化脚本 至output目录
cp -rf $app_config_file $output_dir && # 拷贝 application.yml 至output目录
cp -rf $create_mysql_table $app_name && # 拷贝 sql 初始化脚本 至output目录
cp -rf $app_config_file $app_name && # 拷贝 application.yml 至output目录
# 拷贝程序包到output路径
cp kafka-manager-web/target/${app_name}-*-SNAPSHOT.jar ${output_dir}/${app_name}.jar
cp kafka-manager-web/target/kafka-manager-web-$km_version-SNAPSHOT.jar ${app_name}/${app_name}-SNAPSHOT.jar
echo -e "make output ok."
) || { echo -e "make output error"; exit 2; } # 填充output目录失败后, 退出码为 非0
}
@@ -56,7 +56,7 @@ function make_output() {
function make_package() {
# 压缩output目录
(
tar cvzf ${app_name}.tar.gz ${output_dir}
tar cvzf ${app_name}.tar.gz ${app_name}
echo -e "make package ok."
) || { echo -e "make package error"; exit 2; } # 压缩output目录失败后, 退出码为 非0
}

View File

@@ -13,7 +13,7 @@
## 环境依赖
- `Maven 3.5+`(后端打包依赖)
- `node 10+`(前端打包依赖)
- `node v12+`(前端打包依赖)
- `Java 8+`(运行环境需要)
- `MySQL 5.7`(数据存储)
@@ -49,7 +49,7 @@ mvn install
cp kafka-manager-web/src/main/resources/application.yml kafka-manager-web/target/
cd kafka-manager-web/target/
nohup java -jar kafka-manager-web-2.0.0-SNAPSHOT.jar --spring.config.location=./application.yml > /dev/null 2>&1 &
nohup java -jar kafka-manager-web-2.1.0-SNAPSHOT.jar --spring.config.location=./application.yml > /dev/null 2>&1 &
```
## 使用

View File

@@ -5,13 +5,13 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.xiaojukeji.kafka</groupId>
<artifactId>kafka-manager-common</artifactId>
<version>2.0.0-SNAPSHOT</version>
<version>2.1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<artifactId>kafka-manager</artifactId>
<groupId>com.xiaojukeji.kafka</groupId>
<version>2.0.0-SNAPSHOT</version>
<version>2.1.0-SNAPSHOT</version>
</parent>
<properties>

View File

@@ -46,4 +46,15 @@ public class TopicCreationConstant {
public static final String TOPIC_NAME_PREFIX_RU = "ru01_";
public static final Integer TOPIC_NAME_MAX_LENGTH = 255;
/**
* 单次自动化审批, 默认允许的通过单子
*/
public static final Integer DEFAULT_MAX_PASSED_ORDER_NUM_PER_TASK = 1;
/**
* 单次自动化审批, 最多允许的通过单子
*/
public static final Integer MAX_PASSED_ORDER_NUM_PER_TASK = 200;
}

View File

@@ -86,6 +86,8 @@ public enum ResultStatus {
APP_ID_OR_PASSWORD_ILLEGAL(1000, "app or password illegal"),
SYSTEM_CODE_ILLEGAL(1000, "system code illegal"),
CLUSTER_TASK_HOST_LIST_ILLEGAL(1000, "主机列表错误,请检查主机列表"),

View File

@@ -1,5 +1,6 @@
package com.xiaojukeji.kafka.manager.common.entity.ao;
import java.util.List;
import java.util.Properties;
/**
@@ -23,6 +24,8 @@ public class RdTopicBasic {
private String description;
private List<String> regionNameList;
public Long getClusterId() {
return clusterId;
}
@@ -87,6 +90,14 @@ public class RdTopicBasic {
this.description = description;
}
public List<String> getRegionNameList() {
return regionNameList;
}
public void setRegionNameList(List<String> regionNameList) {
this.regionNameList = regionNameList;
}
@Override
public String toString() {
return "RdTopicBasic{" +
@@ -98,6 +109,7 @@ public class RdTopicBasic {
", appName='" + appName + '\'' +
", properties=" + properties +
", description='" + description + '\'' +
", regionNameList='" + regionNameList + '\'' +
'}';
}
}

View File

@@ -1,5 +1,8 @@
package com.xiaojukeji.kafka.manager.common.entity.ao.config;
import com.xiaojukeji.kafka.manager.common.constant.TopicCreationConstant;
import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils;
import java.util.List;
/**
@@ -7,8 +10,27 @@ import java.util.List;
* @date 20/7/24
*/
public class CreateTopicConfig {
/**
* 单次自动化审批, 允许的通过单子
*/
private Integer maxPassedOrderNumPerTask;
private List<CreateTopicElemConfig> configList;
public Integer getMaxPassedOrderNumPerTask() {
if (ValidateUtils.isNull(maxPassedOrderNumPerTask)) {
return TopicCreationConstant.DEFAULT_MAX_PASSED_ORDER_NUM_PER_TASK;
}
if (maxPassedOrderNumPerTask > TopicCreationConstant.MAX_PASSED_ORDER_NUM_PER_TASK) {
return TopicCreationConstant.MAX_PASSED_ORDER_NUM_PER_TASK;
}
return maxPassedOrderNumPerTask;
}
public void setMaxPassedOrderNumPerTask(Integer maxPassedOrderNumPerTask) {
this.maxPassedOrderNumPerTask = maxPassedOrderNumPerTask;
}
public List<CreateTopicElemConfig> getConfigList() {
return configList;
}
@@ -20,7 +42,8 @@ public class CreateTopicConfig {
@Override
public String toString() {
return "CreateTopicConfig{" +
"configList=" + configList +
"maxPassedOrderNumPerTask=" + maxPassedOrderNumPerTask +
", configList=" + configList +
'}';
}
}

View File

@@ -1,5 +1,7 @@
package com.xiaojukeji.kafka.manager.common.entity.ao.topic;
import java.util.List;
/**
* @author arthur
* @date 2018/09/03
@@ -17,7 +19,7 @@ public class TopicBasicDTO {
private String description;
private String region;
private List<String> regionNameList;
private Integer score;
@@ -83,12 +85,12 @@ public class TopicBasicDTO {
this.description = description;
}
public String getRegion() {
return region;
public List<String> getRegionNameList() {
return regionNameList;
}
public void setRegion(String region) {
this.region = region;
public void setRegionNameList(List<String> regionNameList) {
this.regionNameList = regionNameList;
}
public Integer getScore() {
@@ -164,7 +166,7 @@ public class TopicBasicDTO {
", principals='" + principals + '\'' +
", topicName='" + topicName + '\'' +
", description='" + description + '\'' +
", region='" + region + '\'' +
", regionNameList='" + regionNameList + '\'' +
", score=" + score +
", topicCodeC='" + topicCodeC + '\'' +
", partitionNum=" + partitionNum +

View File

@@ -18,6 +18,8 @@ public class TopicOverview {
private Object byteIn;
private Object byteOut;
private Object produceRequest;
private String appName;
@@ -78,6 +80,14 @@ public class TopicOverview {
this.byteIn = byteIn;
}
public Object getByteOut() {
return byteOut;
}
public void setByteOut(Object byteOut) {
this.byteOut = byteOut;
}
public Object getProduceRequest() {
return produceRequest;
}
@@ -135,6 +145,7 @@ public class TopicOverview {
", partitionNum=" + partitionNum +
", retentionTime=" + retentionTime +
", byteIn=" + byteIn +
", byteOut=" + byteOut +
", produceRequest=" + produceRequest +
", appName='" + appName + '\'' +
", appId='" + appId + '\'' +

View File

@@ -1,6 +1,7 @@
package com.xiaojukeji.kafka.manager.common.entity.dto.rd;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.xiaojukeji.kafka.manager.common.bizenum.ClusterModeEnum;
import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@@ -108,10 +109,13 @@ public class LogicalClusterDTO {
if (ValidateUtils.isNull(clusterId)
|| ValidateUtils.isNull(clusterId)
|| ValidateUtils.isEmptyList(regionIdList)
|| ValidateUtils.isNull(appId)
|| ValidateUtils.isNull(mode)) {
return false;
}
if (!ClusterModeEnum.SHARED_MODE.getCode().equals(mode) && ValidateUtils.isNull(appId)) {
return false;
}
appId = ValidateUtils.isNull(appId)? "": appId;
description = ValidateUtils.isNull(description)? "": description;
return true;
}

View File

@@ -1,5 +1,7 @@
package com.xiaojukeji.kafka.manager.common.entity.metrics;
import java.util.List;
/**
* @author zengqiao
* @date 20/6/17
@@ -11,6 +13,8 @@ public class TopicMetrics extends BaseMetrics {
private String topicName;
private List<BrokerMetrics> brokerMetricsList;
public TopicMetrics(Long clusterId, String topicName) {
super();
this.clusterId = clusterId;
@@ -24,6 +28,14 @@ public class TopicMetrics extends BaseMetrics {
this.topicName = topicName;
}
public TopicMetrics(String appId, Long clusterId, String topicName, List<BrokerMetrics> brokerMetricsList) {
super();
this.appId = appId;
this.clusterId = clusterId;
this.topicName = topicName;
this.brokerMetricsList = brokerMetricsList;
}
public String getAppId() {
return appId;
}
@@ -36,6 +48,14 @@ public class TopicMetrics extends BaseMetrics {
return topicName;
}
public void setBrokerMetricsList(List<BrokerMetrics> brokerMetricsList) {
this.brokerMetricsList = brokerMetricsList;
}
public List<BrokerMetrics> getBrokerMetricsList() {
return brokerMetricsList;
}
@Override
public String toString() {
return "TopicMetrics{" +

View File

@@ -28,6 +28,9 @@ public class TopicOverviewVO {
@ApiModelProperty(value = "每秒流入流量(B)")
private Object byteIn;
@ApiModelProperty(value = "每秒流出流量(B)")
private Object byteOut;
@ApiModelProperty(value = "发送请求数(个/秒)")
private Object produceRequest;
@@ -94,6 +97,14 @@ public class TopicOverviewVO {
this.byteIn = byteIn;
}
public Object getByteOut() {
return byteOut;
}
public void setByteOut(Object byteOut) {
this.byteOut = byteOut;
}
public Object getProduceRequest() {
return produceRequest;
}
@@ -151,6 +162,7 @@ public class TopicOverviewVO {
", partitionNum=" + partitionNum +
", retentionTime=" + retentionTime +
", byteIn=" + byteIn +
", byteOut=" + byteOut +
", produceRequest=" + produceRequest +
", appName='" + appName + '\'' +
", appId='" + appId + '\'' +

View File

@@ -3,6 +3,8 @@ package com.xiaojukeji.kafka.manager.common.entity.vo.normal.topic;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.List;
/**
* Topic的基本信息
* @author zengqiao
@@ -49,6 +51,9 @@ public class TopicBasicVO {
@ApiModelProperty(value = "集群地址")
private String bootstrapServers;
@ApiModelProperty(value = "所属region")
private List<String> regionNameList;
public Long getClusterId() {
return clusterId;
}
@@ -153,6 +158,14 @@ public class TopicBasicVO {
this.score = score;
}
public List<String> getRegionNameList() {
return regionNameList;
}
public void setRegionNameList(List<String> regionNameList) {
this.regionNameList = regionNameList;
}
@Override
public String toString() {
return "TopicBasicVO{" +
@@ -169,6 +182,7 @@ public class TopicBasicVO {
", topicCodeC='" + topicCodeC + '\'' +
", description='" + description + '\'' +
", bootstrapServers='" + bootstrapServers + '\'' +
", regionNameList=" + regionNameList +
'}';
}
}

View File

@@ -0,0 +1,39 @@
package com.xiaojukeji.kafka.manager.common.entity.vo.normal.topic;
/**
* author: mrazkonglingxu
* Date: 2020/12/7
* Time: 7:40 下午
*/
public class TopicBrokerRequestTimeVO {
private Long clusterId;
private Integer brokerId;
private TopicRequestTimeDetailVO brokerRequestTime;
public Long getClusterId() {
return clusterId;
}
public void setClusterId(Long clusterId) {
this.clusterId = clusterId;
}
public Integer getBrokerId() {
return brokerId;
}
public void setBrokerId(Integer brokerId) {
this.brokerId = brokerId;
}
public TopicRequestTimeDetailVO getBrokerRequestTime() {
return brokerRequestTime;
}
public void setBrokerRequestTime(TopicRequestTimeDetailVO brokerRequestTime) {
this.brokerRequestTime = brokerRequestTime;
}
}

View File

@@ -3,6 +3,8 @@ package com.xiaojukeji.kafka.manager.common.entity.vo.normal.topic;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.List;
/**
* @author zengqiao
* @date 20/4/8
@@ -33,6 +35,8 @@ public class TopicRequestTimeDetailVO {
@ApiModelProperty(value = "totalTimeMs")
private Object totalTimeMs;
private List<TopicBrokerRequestTimeVO> brokerRequestTimeList;
public String getRequestTimeType() {
return requestTimeType;
}
@@ -97,6 +101,14 @@ public class TopicRequestTimeDetailVO {
this.totalTimeMs = totalTimeMs;
}
public List<TopicBrokerRequestTimeVO> getBrokerRequestTimeList() {
return brokerRequestTimeList;
}
public void setBrokerRequestTimeList(List<TopicBrokerRequestTimeVO> brokerRequestTimeList) {
this.brokerRequestTimeList = brokerRequestTimeList;
}
@Override
public String toString() {
return "TopicRequestTimeDetailVO{" +

View File

@@ -3,6 +3,7 @@ package com.xiaojukeji.kafka.manager.common.entity.vo.rd;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.List;
import java.util.Properties;
/**
@@ -35,6 +36,9 @@ public class RdTopicBasicVO {
@ApiModelProperty(value = "备注")
private String description;
@ApiModelProperty(value = "所属region")
private List<String> regionNameList;
public Long getClusterId() {
return clusterId;
}
@@ -99,6 +103,14 @@ public class RdTopicBasicVO {
this.description = description;
}
public List<String> getRegionNameList() {
return regionNameList;
}
public void setRegionNameList(List<String> regionNameList) {
this.regionNameList = regionNameList;
}
@Override
public String toString() {
return "RdTopicBasicVO{" +
@@ -110,6 +122,7 @@ public class RdTopicBasicVO {
", appName='" + appName + '\'' +
", properties=" + properties +
", description='" + description + '\'' +
", regionNameList='" + regionNameList + '\'' +
'}';
}
}

View File

@@ -64,7 +64,7 @@ public class JsonUtils {
TopicConnectionDO connectionDO = new TopicConnectionDO();
String[] appIdDetailArray = appIdDetail.toString().split("#");
if (appIdDetailArray.length == 3) {
if (appIdDetailArray.length >= 3) {
connectionDO.setAppId(appIdDetailArray[0]);
connectionDO.setIp(appIdDetailArray[1]);
connectionDO.setClientVersion(appIdDetailArray[2]);

View File

@@ -170,7 +170,10 @@ public class MbeanNameUtilV2 {
new MbeanV2(
"TopicCodeC",
JmxAttributeEnum.VALUE_ATTRIBUTE,
"kafka.server:type=ReplicaManager,name=TopicCodeC"
Arrays.asList(
new AbstractMap.SimpleEntry<>(KafkaVersion.VERSION_0_10_3, "kafka.server:type=ReplicaManager,name=TopicCodeC"),
new AbstractMap.SimpleEntry<>(KafkaVersion.VERSION_MAX, "kafka.server:type=AppIdTopicMetrics,name=RecordCompression,appId=")
)
),
Arrays.asList(
KafkaMetricsCollections.TOPIC_BASIC_PAGE_METRICS

View File

@@ -8,7 +8,7 @@ package com.xiaojukeji.kafka.manager.common.zookeeper;
public class ZkPathUtil {
private static final String ZOOKEEPER_SEPARATOR = "/";
private static final String BROKER_ROOT_NODE = ZOOKEEPER_SEPARATOR + "brokers";
public static final String BROKER_ROOT_NODE = ZOOKEEPER_SEPARATOR + "brokers";
public static final String CONTROLLER_ROOT_NODE = ZOOKEEPER_SEPARATOR + "controller";

View File

@@ -33,6 +33,8 @@
"mobx": "^5.9.4",
"mobx-react": "^5.4.3",
"moment": "^2.24.0",
"monaco-editor": "^0.20.0",
"monaco-editor-webpack-plugin": "^1.9.0",
"optimize-css-assets-webpack-plugin": "^5.0.1",
"react": "^16.8.4",
"react-hot-loader": "^4.8.4",
@@ -45,9 +47,13 @@
"tslint": "^5.13.1",
"tslint-react": "^3.6.0",
"typescript": "^3.3.3333",
"url-loader": "^4.1.1",
"webpack": "^4.29.6",
"webpack-cli": "^3.2.3",
"webpack-dev-server": "^3.2.1",
"xlsx": "^0.16.1"
},
"dependencies": {
"format-to-json": "^1.0.4"
}
}

View File

@@ -8,7 +8,7 @@
<parent>
<artifactId>kafka-manager</artifactId>
<groupId>com.xiaojukeji.kafka</groupId>
<version>2.0.0-SNAPSHOT</version>
<version>2.1.0-SNAPSHOT</version>
</parent>
<build>
@@ -28,9 +28,10 @@
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<nodeVersion>v8.12.0</nodeVersion>
<npmVersion>6.4.1</npmVersion>
<nodeVersion>v12.20.0</nodeVersion>
<npmVersion>6.14.8</npmVersion>
<nodeDownloadRoot>http://npm.taobao.org/mirrors/node/</nodeDownloadRoot>
<npmDownloadRoot>https://registry.npm.taobao.org/npm/-/</npmDownloadRoot>
</configuration>
</execution>
<execution>

View File

@@ -48,6 +48,9 @@ import 'antd/es/notification/style';
import Tooltip from 'antd/es/tooltip';
import 'antd/es/tooltip/style';
import Popover from 'antd/es/popover';
import 'antd/es/popover/style';
import Radio from 'antd/es/radio';
import 'antd/es/radio';
import { RadioChangeEvent } from 'antd/es/radio';
@@ -97,6 +100,9 @@ import 'antd/es/time-picker/style';
import Badge from 'antd/es/badge';
import 'antd/es/badge/style';
import Progress from 'antd/es/progress';
import 'antd/es/progress/style';
import { RangePickerValue } from 'antd/es/date-picker/interface';
export {
@@ -136,4 +142,5 @@ export {
TimePicker,
RangePickerValue,
Badge,
Popover
};

View File

@@ -0,0 +1,68 @@
// import * as React from 'react';
// import CodeMirror from 'codemirror/lib/codemirror';
// import 'codemirror/lib/codemirror.css';
// import 'codemirror/mode/sql/sql';
// import 'codemirror/mode/javascript/javascript';
// import 'codemirror/addon/hint/show-hint.js';
// import 'codemirror/addon/hint/sql-hint.js';
// import 'codemirror/addon/hint/show-hint.css';
// import './index.less';
// import { indexStore } from 'store/my-index';
// interface IProps {
// value?: string;
// placeholder?: string;
// readOnly?: boolean;
// }
// export class CodeMirrorEditor extends React.Component<IProps> {
// public editor = null as any;
// public handleCodeFocus = () => {
// // tslint:disable-next-line:no-unused-expression
// this.editor && this.editor.focus();
// }
// public componentDidMount() {
// const { value, placeholder, readOnly } = this.props;
// const code = document.querySelector('.codemirror');
// code.innerHTML = '';
// const editor = CodeMirror(document.querySelector('.codemirror'), {
// mode: 'application/json',
// indentWithTabs: true,
// smartIndent: true,
// lineNumbers: true,
// matchBrackets: true,
// autoCloseBrackets: true,
// styleSelectedText: true,
// foldGutter: true,
// readOnly,
// extraKeys: readOnly ? {} : {
// 'Ctrl-Enter': 'autocomplete',
// 'Tab': (cm) => {
// const spaces = Array(cm.getOption('indentUnit') + 1).join(' ');
// cm.replaceSelection(spaces);
// },
// },
// placeholder,
// });
// editor.setValue(value || '');
// indexStore.setCodeEditorValue(value || '');
// editor.on('changes', (a: any) => {
// const data = a.getValue();
// indexStore.setCodeEditorValue(data);
// });
// this.editor = editor;
// }
// public render() {
// return (
// <div
// className="editor-wrap"
// onClick={this.handleCodeFocus}
// >
// <div className="codemirror" />
// </div >
// );
// }
// }

View File

@@ -0,0 +1,31 @@
.editor {
height: 100%;
}
.CodeMirror-placeholder {
color:#999;
font-size: 12px;
line-height: 14px;
font-family: -apple-system,BlinkMacSystemFont,Neue Haas Grotesk Text Pro,Arial Nova,Segoe UI,Helvetica Neue,\.PingFang SC,PingFang SC,Microsoft YaHei,Microsoft JhengHei,Source Han Sans SC,Noto Sans CJK SC,Source Han Sans CN,Noto Sans SC,Source Han Sans TC,Noto Sans CJK TC,Hiragino Sans GB,sans-serif;
}
.editor-wrap {
max-height: 100%;
}
.CodeMirror {
height: 100vh;
}
.monacoEditor{
height: 150px;
position: relative;
overflow: hidden;
border: 1px solid #cccccc;
border-radius: 4px;
.editor{
height: 100%;
position: absolute;
left: -14%;
width: 120%;
}
}

View File

@@ -0,0 +1,50 @@
import * as React from 'react';
import * as monaco from 'monaco-editor';
import './index.less';
export interface IEditorProps {
style?: React.CSSProperties;
options: monaco.editor.IStandaloneEditorConstructionOptions;
uri?: monaco.Uri;
autoUnmount?: boolean;
customMount?: (editor: monaco.editor.IStandaloneCodeEditor, monaco: any) => any;
placeholder?: string;
}
export class EditorCom extends React.Component<IEditorProps> {
public ref: HTMLElement = null;
public editor: monaco.editor.IStandaloneCodeEditor;
public state = {
placeholder: this.props.placeholder ?? '',
};
public componentWillUnmount() {
if (this.props.autoUnmount === false) return;
const model = this.editor.getModel();
model.dispose();
this.editor.dispose();
}
public componentDidMount() {
const { customMount, options, uri } = this.props;
const { value, language } = options;
if (uri) {
options.model = monaco.editor.createModel(value, language, uri);
}
this.editor = monaco.editor.create(this.ref,
options,
);
if (customMount) customMount(this.editor, monaco);
}
public render() {
const { style } = this.props;
return (
<>
<div style={style} className="editor" ref={(id) => { this.ref = id; }} />
</>
);
}
}

View File

@@ -0,0 +1,77 @@
import * as React from 'react';
import * as monaco from 'monaco-editor';
import format2json from 'format-to-json';
import { Input } from 'component/antd';
import './index.less';
export interface IEditorProps {
style?: React.CSSProperties;
options: monaco.editor.IStandaloneEditorConstructionOptions;
uri?: monaco.Uri;
autoUnmount?: boolean;
customMount?: (editor: monaco.editor.IStandaloneCodeEditor, monaco: any) => any;
placeholder?: string;
value: '';
onChange?: any;
}
class Monacoeditor extends React.Component<IEditorProps> {
public ref: HTMLElement = null;
public editor: monaco.editor.IStandaloneCodeEditor;
public state = {
placeholder: '',
};
// public arr = '{"clusterId":95,"startId":37397856,"step":100,"topicName":"kmo_topic_metrics_tempory_zq"}';
// public Ars(a: string) {
// const obj = JSON.parse(a);
// const newobj: any = {};
// for (const item in obj) {
// if (typeof obj[item] === 'object') {
// this.Ars(obj[item]);
// } else {
// newobj[item] = obj[item];
// }
// }
// return JSON.stringify(newobj);
// }
public async componentDidMount() {
const { value, onChange } = this.props;
const format: any = await format2json(value);
this.editor = monaco.editor.create(this.ref, {
value: format.result,
language: 'json',
lineNumbers: 'off',
scrollBeyondLastLine: false,
// selectOnLineNumbers: true,
// roundedSelection: false,
// readOnly: true,
minimap: {
enabled: false,
},
// automaticLayout: true, // 自动布局
glyphMargin: true, // 字形边缘 {},[]
// useTabStops: false,
// formatOnPaste: true,
// mode: 'application/json',
// indentWithTabs: true,
// smartIndent: true,
// matchBrackets: 'always',
// autoCloseBrackets: true,
// styleSelectedText: true,
// foldGutter: true,
});
this.editor.onDidChangeModelContent((e) => {
const newValue = this.editor.getValue();
onChange(newValue);
});
}
public render() {
return (
<div className="monacoEditor ant-input" >
<Input style={{ display: 'none' }} {...this.props} />
<div className="editor" {...this.props} ref={(id) => { this.ref = id; }} />
</div>
);
}
}
export default Monacoeditor;

View File

@@ -2,13 +2,13 @@ import * as React from 'react';
import { Drawer, Modal, Button, message } from 'component/antd';
import { XFormComponent } from 'component/x-form';
import { IXFormWrapper } from 'types/base-type';
import { wrapper } from 'store';
export class XFormWrapper extends React.Component<IXFormWrapper> {
public state = {
confirmLoading: false,
formMap: this.props.formMap || [] as any,
formData: this.props.formData || {},
formData: this.props.formData || {}
};
private $formRef: any;
@@ -108,7 +108,7 @@ export class XFormWrapper extends React.Component<IXFormWrapper> {
if (error) {
return;
}
const { onSubmit, isWaitting } = this.props;
const { onSubmit, isWaitting, onSubmitFaild } = this.props;
if (typeof onSubmit === 'function') {
if (isWaitting) {
@@ -116,12 +116,16 @@ export class XFormWrapper extends React.Component<IXFormWrapper> {
confirmLoading: true,
});
onSubmit(result).then(() => {
this.setState({
confirmLoading: false,
});
message.success('操作成功');
this.resetForm();
this.closeModalWrapper();
}).catch((err: any) => {
const { formMap, formData } = wrapper.xFormWrapper;
onSubmitFaild(err, this.$formRef, formData, formMap);
}).finally(() => {
this.setState({
confirmLoading: false,
});
});
return;
}

View File

@@ -1,5 +1,6 @@
import * as React from 'react';
import { Select, Input, InputNumber, Form, Switch, Checkbox, DatePicker, Radio, Upload, Button, Icon, Tooltip } from 'component/antd';
import Monacoeditor from 'component/editor/monacoEditor';
import { searchProps } from 'constants/table';
import './index.less';
@@ -19,6 +20,7 @@ export enum FormItemType {
rangePicker = 'range_picker',
radioGroup = 'radio_group',
upload = 'upload',
monacoEditor = 'monaco_editor',
}
export interface IFormItem {
@@ -105,13 +107,11 @@ class XForm extends React.Component<IXFormProps> {
<Form layout={layout || 'horizontal'} onSubmit={() => ({})}>
{formMap.map(formItem => {
const { initialValue, valuePropName } = this.handleFormItem(formItem, formData);
const getFieldValue = {
initialValue,
rules: formItem.rules || [{ required: false, message: '' }],
valuePropName,
};
if (formItem.type === FormItemType.upload) {
Object.assign(getFieldValue, {
getValueFromEvent: this.onUploadFileChange,
@@ -137,7 +137,6 @@ class XForm extends React.Component<IXFormProps> {
}
public renderFormItem(item: IFormItem) {
switch (item.type) {
default:
case FormItemType.input:
@@ -148,6 +147,9 @@ class XForm extends React.Component<IXFormProps> {
return <InputNumber {...item.attrs} />;
case FormItemType.textArea:
return <TextArea rows={5} {...item.attrs} />;
case FormItemType.monacoEditor:
// tslint:disable-next-line: jsx-wrap-multiline
return <Monacoeditor {...item.attrs} />;
case FormItemType.select:
return (
<Select

View File

@@ -66,7 +66,10 @@ export const timeMonthStr = 'YYYY/MM';
// tslint:disable-next-line:max-line-length
export const indexUrl = 'https://github.com/didi/kafka-manager';
export const indexUrl ={
indexUrl:'https://github.com/didi/kafka-manager',
cagUrl:'https://github.com/didi/Logi-KafkaManager/blob/master/docs/user_guide/add_cluster/add_cluster.md', // 集群接入指南 Cluster access Guide
}
export const expandRemarks = `请填写不少于5字的申请原因以便工作人员判断审核`;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
import fetch , { formFetch } from './fetch';
import fetch, { formFetch } from './fetch';
import { IUploadFile, IUser, IQuotaModelItem, ILimitsItem, ITopic, IOrderParams, ISample, IMigration, IExecute, IEepand, IUtils, ITopicMetriceParams, IRegister, IEditTopic, IExpand, IDeleteTopic, INewRegions, INewLogical, IRebalance, INewBulidEnums, ITrigger, IApprovalOrder, IMonitorSilences, IConfigure, IBatchApproval } from 'types/base-type';
import { IRequestParams } from 'types/alarm';
import { apiCache } from 'lib/api-cache';
@@ -94,8 +94,8 @@ export const getRealTimeTraffic = (clusterId: number, topicName: string) => {
return fetch(`/normal/${clusterId}/topics/${topicName}/metrics`);
};
export const getRealConsume = (clusterId: number, topicName: string) => {
return fetch(`/normal/${clusterId}/topics/${topicName}/request-time`);
export const getRealConsume = (clusterId: number, topicName: string, percentile: string) => {
return fetch(`/normal/${clusterId}/topics/${topicName}/request-time?percentile=${percentile}`);
};
export const getConnectionInfo = (clusterId: number, topicName: string, appId?: string) => {
@@ -354,12 +354,12 @@ export const approvalOrder = (params: IApprovalOrder) => {
* @see http://127.0.0.1:8080/swagger-ui.html#!/OP4524037213333045620851255092147540REST41/getOrderAppDetailUsingGET
*/
export const getOrderTypeList = () => {
return fetch('/normal/orders/type-enums') ;
return fetch('/normal/orders/type-enums');
};
// 我的申请
export const getApplyOrderList = (status: number) => {
return fetch(`/normal/orders?status=${status === 2 ? '' : status}`) ;
return fetch(`/normal/orders?status=${status === 2 ? '' : status}`);
};
export const getOrderDetail = (orderId: number) => {
@@ -436,7 +436,7 @@ export const editConfigure = (params: IConfigure) => {
});
};
export const deleteConfigure = (configKey: string) => {
export const deleteConfigure = (configKey: string) => {
return fetch(`/rd/configs?config-key=${configKey}`, {
method: 'DELETE',
});
@@ -800,21 +800,21 @@ export const getTicketBycode = (code: string) => {
method: 'POST',
body: JSON.stringify({ code }),
});
};
};
export const userLogOut = () => {
return fetch('/sso/logout', {
method: 'DELETE',
});
};
};
/**
* 监控报警 alarm
*/
/**
* 监控报警 alarm
*/
export const getMonitorStrategies = () => {
return fetch('/normal/monitor-strategies');
};
};
export const deteleMonitorStrategies = (monitorId: number) => {
return fetch(`/normal/monitor-strategies?monitorId=${monitorId}`, {
@@ -873,7 +873,7 @@ export const getMonitorDetail = (monitorId: number) => {
};
export const modifyMonitorStrategy = (params: IRequestParams) => {
return fetch('/normal/monitor-strategies', {
return fetch('/normal/monitor-strategies', {
method: 'PUT',
body: JSON.stringify(params),
});

View File

@@ -76,7 +76,8 @@ export const getPieChartOption = (data: ILabelValue[], legend: string[]) => {
},
emphasis: {
label: {
show: true,
// show: true,
show: false,
fontSize: '16',
fontWeight: 'bold',
},

View File

@@ -2,6 +2,7 @@ import * as ReactDOM from 'react-dom';
import { Provider } from 'mobx-react';
import * as React from 'react';
import Router from './router';
import 'styles/style.less';
const renderApp = () => {
ReactDOM.render(

View File

@@ -107,3 +107,10 @@ html, body, .router-nav {
padding: 10px;
}
.applicationDocument {
display: inline-block;
margin-left: 10px;
cursor: pointer;
font-size: 12px;
}

View File

@@ -597,7 +597,9 @@ class Admin {
}
public getTopicsBasicInfo(clusterId: number, topicName: string) {
return getTopicsBasicInfo(clusterId, topicName).then(this.setTopicsBasicInfo);
return getTopicsBasicInfo(clusterId, topicName).then(data => {
return this.setTopicsBasicInfo(data);
});
}
public getTasksKafkaFiles(clusterId?: any) {

View File

@@ -19,6 +19,9 @@ class Order {
@observable
public loading: boolean = false;
@observable
public selectedRows: any[] = [];
@observable
public orderList: IBaseOrder[] = [];
@@ -55,6 +58,15 @@ class Order {
this.loading = value;
}
@action.bound
public setSelectedRows(rows?: any[]) {
if (rows) {
this.selectedRows = rows;
} else {
this.selectedRows = [];
}
}
@action.bound
public setOrderList(data: IBaseOrder[]) {
this.orderList = data;

View File

@@ -22,8 +22,8 @@ class RegionCenter {
@observable
public regionIdcList: IRegionIdcs[] = [
{ name: '国内', idc: 'cn' },
{ name: '美东', idc: 'us' },
{ name: '俄罗斯', idc: 'ru' },
// { name: '美东', idc: 'us' },
// { name: '俄罗斯', idc: 'ru' },
];
@action.bound

View File

@@ -35,6 +35,8 @@ export interface ITopicBaseInfo {
score: number;
topicCodeC: string;
physicalClusterId: number;
percentile: string;
regionNameList: any;
}
export interface IRealTimeTraffic {
@@ -488,9 +490,9 @@ class Topic {
return getRealTimeTraffic(clusterId, topicName).then(this.setRealTimeTraffic);
}
public getRealConsume(clusterId: number, topicName: string) {
public getRealConsume(clusterId: number, topicName: string, percentile: string) {
this.setConsumeLoading(true);
return getRealConsume(clusterId, topicName).then(this.setRealConsume);
return getRealConsume(clusterId, topicName, percentile).then(this.setRealConsume);
}
public getConnectionInfo(clusterId: number, topicName: string, appId?: string) {

View File

@@ -8,7 +8,7 @@ interface IEnum {
suffix?: string;
}
interface IUploadFileType {
fileEnum: IEnum[] ;
fileEnum: IEnum[];
storageEnum: IEnum[];
}
@@ -44,11 +44,10 @@ export class Version {
@action.bound
public setFileList(data: IUploadFile[]) {
this.fileList = (data || []).map((item, index) => {
return {
...item,
configType: this.acceptFileMap[item.fileType],
configType: this.acceptFileMap[item.fileType] || '',
key: index,
};
});

View File

@@ -24,6 +24,7 @@ class Wrapper {
});
}
@action.bound
public setXFormWrapper(xFormWrapper: IXFormWrapper) {
this.xFormWrapper = xFormWrapper;

View File

@@ -0,0 +1,15 @@
.ant-spin-blur .ant-table-content {
opacity: 0;
}
.ant-alert-error {
margin-top: 10px;
}
.isSHowApp {
display: block;
}
.isHideApp {
display: none;
}
.tips {
margin-left: 48px;
}

View File

@@ -1,6 +1,6 @@
.content-container {
position: relative;
min-width: 760px;
.table-operation {
a {
color: @primary-color;

Some files were not shown because too many files have changed in this diff Show More