diff --git a/kafka-manager-console/src/component/antd/index.tsx b/kafka-manager-console/src/component/antd/index.tsx index 2d771efe..d0958daf 100644 --- a/kafka-manager-console/src/component/antd/index.tsx +++ b/kafka-manager-console/src/component/antd/index.tsx @@ -94,6 +94,9 @@ import 'antd/es/divider/style'; import Upload from 'antd/es/upload'; import 'antd/es/upload/style'; +import Transfer from 'antd/es/transfer'; +import 'antd/es/transfer/style'; + import TimePicker from 'antd/es/time-picker'; import 'antd/es/time-picker/style'; @@ -142,5 +145,6 @@ export { TimePicker, RangePickerValue, Badge, - Popover + Popover, + Transfer }; diff --git a/kafka-manager-console/src/component/editor/index.less b/kafka-manager-console/src/component/editor/index.less index 4ff05854..36c52cde 100644 --- a/kafka-manager-console/src/component/editor/index.less +++ b/kafka-manager-console/src/component/editor/index.less @@ -25,7 +25,7 @@ .editor{ height: 100%; position: absolute; - left: -14%; + left: -12%; width: 120%; } } diff --git a/kafka-manager-console/src/component/editor/monacoEditor.tsx b/kafka-manager-console/src/component/editor/monacoEditor.tsx index 7a0dd44c..ac0a297a 100644 --- a/kafka-manager-console/src/component/editor/monacoEditor.tsx +++ b/kafka-manager-console/src/component/editor/monacoEditor.tsx @@ -21,24 +21,12 @@ class Monacoeditor extends React.Component { 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, + value: format.result || value, language: 'json', lineNumbers: 'off', scrollBeyondLastLine: false, @@ -48,7 +36,7 @@ class Monacoeditor extends React.Component { minimap: { enabled: false, }, - // automaticLayout: true, // 自动布局 + automaticLayout: true, // 自动布局 glyphMargin: true, // 字形边缘 {},[] // useTabStops: false, // formatOnPaste: true, diff --git a/kafka-manager-console/src/component/flow-table/index.tsx b/kafka-manager-console/src/component/flow-table/index.tsx index 1f7d9be6..c62f7089 100644 --- a/kafka-manager-console/src/component/flow-table/index.tsx +++ b/kafka-manager-console/src/component/flow-table/index.tsx @@ -68,8 +68,8 @@ export class StatusGraghCom extends React.Component { public render() { const statusData = this.getData(); const loading = this.getLoading(); - if (!statusData) return null; const data: any[] = []; + if (!statusData) return ; Object.keys(statusData).map((key) => { if (statusData[key]) { const v = key === 'byteIn' || key === 'byteOut' ? statusData[key].map(i => i && (i / 1024).toFixed(2)) : @@ -85,7 +85,7 @@ export class StatusGraghCom extends React.Component { } }); return ( -
+
); } } diff --git a/kafka-manager-console/src/component/x-form/index.less b/kafka-manager-console/src/component/x-form/index.less index 95152e60..a08230a6 100644 --- a/kafka-manager-console/src/component/x-form/index.less +++ b/kafka-manager-console/src/component/x-form/index.less @@ -1,4 +1,4 @@ -.ant-input-number { +.ant-input-number, .ant-form-item-children .ant-select { width: 314px } diff --git a/kafka-manager-console/src/component/x-form/index.tsx b/kafka-manager-console/src/component/x-form/index.tsx index 9530f2bb..dc435d0f 100755 --- a/kafka-manager-console/src/component/x-form/index.tsx +++ b/kafka-manager-console/src/component/x-form/index.tsx @@ -130,6 +130,8 @@ class XForm extends React.Component { this.renderFormItem(formItem), )} {formItem.renderExtraElement ? formItem.renderExtraElement() : null} + {/* 添加保存时间提示文案 */} + {formItem.attrs?.prompttype ? {formItem.attrs.prompttype} : null} ); })} diff --git a/kafka-manager-console/src/constants/left-menu.ts b/kafka-manager-console/src/constants/left-menu.ts index 27fcfe0b..e7452667 100644 --- a/kafka-manager-console/src/constants/left-menu.ts +++ b/kafka-manager-console/src/constants/left-menu.ts @@ -59,6 +59,10 @@ export const adminMenu = [{ href: `/admin/bill`, i: 'k-icon-renwuliebiao', title: '用户账单', +},{ + href: `/admin/operation-record`, + i: 'k-icon-operationrecord', + title: '操作记录', }] as ILeftMenu[]; export const expertMenu = [{ diff --git a/kafka-manager-console/src/constants/strategy.ts b/kafka-manager-console/src/constants/strategy.ts index c0d19001..e92563e6 100644 --- a/kafka-manager-console/src/constants/strategy.ts +++ b/kafka-manager-console/src/constants/strategy.ts @@ -67,7 +67,7 @@ export const timeMonthStr = 'YYYY/MM'; // tslint:disable-next-line:max-line-length export const indexUrl ={ - indexUrl:'https://github.com/didi/kafka-manager', + indexUrl:'https://github.com/didi/Logi-KafkaManager/blob/master/docs/user_guide/kafka_metrics_desc.md', // 指标说明 cagUrl:'https://github.com/didi/Logi-KafkaManager/blob/master/docs/user_guide/add_cluster/add_cluster.md', // 集群接入指南 Cluster access Guide } diff --git a/kafka-manager-console/src/container/admin/cluster-detail/cluster-consumer.tsx b/kafka-manager-console/src/container/admin/cluster-detail/cluster-consumer.tsx index 5605a372..911f44d2 100644 --- a/kafka-manager-console/src/container/admin/cluster-detail/cluster-consumer.tsx +++ b/kafka-manager-console/src/container/admin/cluster-detail/cluster-consumer.tsx @@ -100,7 +100,7 @@ export class ClusterConsumer extends SearchAndFilterContainer {
  • {this.props.tab}
  • - {this.renderSearch()} + {this.renderSearch('', '请输入消费组名称')}
(origin: T[]) { + let data: T[] = origin; + let { searchCandidateKey } = this.state; + searchCandidateKey = (searchCandidateKey + '').trim().toLowerCase(); + + data = searchCandidateKey ? origin.filter((item: IController) => + (item.host !== undefined && item.host !== null) && item.host.toLowerCase().includes(searchCandidateKey as string), + ) : origin; + return data; + } + + // 候选controller + public renderCandidateController() { + const columns = [ + { + title: 'BrokerId', + dataIndex: 'brokerId', + key: 'brokerId', + width: '20%', + sorter: (a: IController, b: IController) => b.brokerId - a.brokerId, + render: (r: string, t: IController) => { + return ( + {r} + + ); + }, + }, + { + title: 'BrokerHost', + key: 'host', + dataIndex: 'host', + width: '20%', + // render: (r: string, t: IController) => { + // return ( + // {r} + // + // ); + // }, + }, + { + title: 'Broker状态', + key: 'status', + dataIndex: 'status', + width: '20%', + render: (r: number, t: IController) => { + return ( + {r === 1 ? '不在线' : '在线'} + ); + }, + }, + { + title: '创建时间', + dataIndex: 'startTime', + key: 'startTime', + width: '25%', + sorter: (a: IController, b: IController) => b.timestamp - a.timestamp, + render: (t: number) => moment(t).format(timeFormat), + }, + { + title: '操作', + dataIndex: 'operation', + key: 'operation', + width: '15%', + render: (r: string, t: IController) => { + return ( + this.deleteCandidateCancel(t)} + cancelText="取消" + okText="确认" + > + 删除 + + ); + }, + }, + ]; + + return ( +
+ ); + } + public renderController() { const columns = [ @@ -58,12 +151,6 @@ export class ClusterController extends SearchAndFilterContainer { key: 'host', dataIndex: 'host', width: '30%', - // render: (r: string, t: IController) => { - // return ( - // {r} - // - // ); - // }, }, { title: '变更时间', @@ -87,16 +174,104 @@ export class ClusterController extends SearchAndFilterContainer { public componentDidMount() { admin.getControllerHistory(this.clusterId); + admin.getCandidateController(this.clusterId); + admin.getBrokersMetadata(this.clusterId); + } + + public addController = () => { + this.setState({ isCandidateModel: true, targetKeys: [] }) + } + + public addCandidateChange = (targetKeys: any) => { + this.setState({ targetKeys }) + } + + + + public handleCandidateCancel = () => { + this.setState({ isCandidateModel: false }); + } + + public handleCandidateOk = () => { + let brokerIdList = this.state.targetKeys.map((item: any) => { + return admin.brokersMetadata[item].brokerId + }) + admin.addCandidateController(this.clusterId, brokerIdList).then(data => { + notification.success({ message: '新增成功' }); + admin.getCandidateController(this.clusterId); + }).catch(err => { + notification.error({ message: '新增失败' }); + }) + this.setState({ isCandidateModel: false, targetKeys: [] }); + } + + public deleteCandidateCancel = (target: any) => { + admin.deleteCandidateCancel(this.clusterId, [target.brokerId]).then(() => { + notification.success({ message: '删除成功' }); + }); + this.setState({ isCandidateModel: false }); + } + + public renderAddCandidateController() { + let filterControllerCandidate = admin.brokersMetadata.filter((item: any) => { + return !admin.filtercontrollerCandidate.includes(item.brokerId) + }) + + return ( + this.handleCandidateOk()} + onCancel={() => this.handleCandidateCancel()} + footer={<> + + + + } + > + item.host} + onChange={(targetKeys) => this.addCandidateChange(targetKeys)} + titles={['未选', '已选']} + locale={{ + itemUnit: '项', + itemsUnit: '项', + }} + listStyle={{ + width: "45%", + }} + /> + + ); } public render() { return (
    +
  • + 候选Controller + Controller将会优先从以下Broker中选举 +
  • +
    +
    + +
    + {this.renderSearch('', '请查找Host', 'searchCandidateKey')} +
    +
+ {this.renderCandidateController()} +
  • {this.props.tab}
  • {this.renderSearch('', '请输入Host')}
{this.renderController()} + {this.renderAddCandidateController()}
); } diff --git a/kafka-manager-console/src/container/admin/cluster-detail/cluster-topic.tsx b/kafka-manager-console/src/container/admin/cluster-detail/cluster-topic.tsx index 7b7aaae7..c2d3aa54 100644 --- a/kafka-manager-console/src/container/admin/cluster-detail/cluster-topic.tsx +++ b/kafka-manager-console/src/container/admin/cluster-detail/cluster-topic.tsx @@ -172,7 +172,7 @@ export class ClusterTopic extends SearchAndFilterContainer { key: 'appName', // width: '10%', render: (val: string, record: IClusterTopics) => ( - + {val} ), diff --git a/kafka-manager-console/src/container/admin/cluster-detail/exclusive-cluster.tsx b/kafka-manager-console/src/container/admin/cluster-detail/exclusive-cluster.tsx index 6aaa2e78..efc28e68 100644 --- a/kafka-manager-console/src/container/admin/cluster-detail/exclusive-cluster.tsx +++ b/kafka-manager-console/src/container/admin/cluster-detail/exclusive-cluster.tsx @@ -314,8 +314,7 @@ export class ExclusiveCluster extends SearchAndFilterContainer { >
- 由于该Region已被逻辑集群【 {this.state.logicalClusterName} 】使用 - 请先解除Region与逻辑集群的关系 + 该Region已被逻辑集群【 {this.state.logicalClusterName} 】使用,请先解除Region与逻辑集群的关系
diff --git a/kafka-manager-console/src/container/admin/cluster-detail/index.less b/kafka-manager-console/src/container/admin/cluster-detail/index.less index 65c45b9c..9b5d5103 100644 --- a/kafka-manager-console/src/container/admin/cluster-detail/index.less +++ b/kafka-manager-console/src/container/admin/cluster-detail/index.less @@ -16,7 +16,7 @@ .traffic-table { margin: 10px 0; - min-height: 450px; + min-height: 330px; .traffic-header { width: 100%; height: 44px; @@ -94,4 +94,10 @@ .region-prompt{ font-weight: bold; text-align: center; +} + +.asd{ + display: flex; + justify-content: space-around; + align-items: center; } \ No newline at end of file diff --git a/kafka-manager-console/src/container/admin/cluster-detail/index.tsx b/kafka-manager-console/src/container/admin/cluster-detail/index.tsx index 5882dd57..027dde27 100644 --- a/kafka-manager-console/src/container/admin/cluster-detail/index.tsx +++ b/kafka-manager-console/src/container/admin/cluster-detail/index.tsx @@ -32,9 +32,9 @@ export class ClusterDetail extends React.Component { } public render() { - let content = {} as IMetaData; - content = admin.basicInfo ? admin.basicInfo : content; - return ( + let content = {} as IMetaData; + content = admin.basicInfo ? admin.basicInfo : content; + return ( <> - + @@ -60,11 +60,11 @@ export class ClusterDetail extends React.Component { - + - - + + diff --git a/kafka-manager-console/src/container/admin/cluster-list/index.tsx b/kafka-manager-console/src/container/admin/cluster-list/index.tsx index 63ccd93e..be6956d6 100644 --- a/kafka-manager-console/src/container/admin/cluster-list/index.tsx +++ b/kafka-manager-console/src/container/admin/cluster-list/index.tsx @@ -4,6 +4,7 @@ import { wrapper } from 'store'; import { observer } from 'mobx-react'; import { IXFormWrapper, IMetaData, IRegister } from 'types/base-type'; import { admin } from 'store/admin'; +import { users } from 'store/users'; import { registerCluster, createCluster, pauseMonitoring } from 'lib/api'; import { SearchAndFilterContainer } from 'container/search-filter'; import { cluster } from 'store/cluster'; @@ -12,6 +13,7 @@ import { urlPrefix } from 'constants/left-menu'; import { indexUrl } from 'constants/strategy' import { region } from 'store'; import './index.less'; +import Monacoeditor from 'component/editor/monacoEditor'; import { getAdminClusterColumns } from '../config'; const { confirm } = Modal; @@ -77,34 +79,34 @@ export class ClusterList extends SearchAndFilterContainer { disabled: item ? true : false, }, }, - { - key: 'idc', - label: '数据中心', - defaultValue: region.regionName, - rules: [{ required: true, message: '请输入数据中心' }], - attrs: { - placeholder: '请输入数据中心', - disabled: true, - }, - }, - { - key: 'mode', - label: '集群类型', - type: 'select', - options: cluster.clusterModes.map(ele => { - return { - label: ele.message, - value: ele.code, - }; - }), - rules: [{ - required: true, - message: '请选择集群类型', - }], - attrs: { - placeholder: '请选择集群类型', - }, - }, + // { + // key: 'idc', + // label: '数据中心', + // defaultValue: region.regionName, + // rules: [{ required: true, message: '请输入数据中心' }], + // attrs: { + // placeholder: '请输入数据中心', + // disabled: true, + // }, + // }, + // { + // key: 'mode', + // label: '集群类型', + // type: 'select', + // options: cluster.clusterModes.map(ele => { + // return { + // label: ele.message, + // value: ele.code, + // }; + // }), + // rules: [{ + // required: true, + // message: '请选择集群类型', + // }], + // attrs: { + // placeholder: '请选择集群类型', + // }, + // }, { key: 'kafkaVersion', label: 'kafka版本', @@ -132,6 +134,25 @@ export class ClusterList extends SearchAndFilterContainer { "security.protocol": "SASL_PLAINTEXT", "sasl.mechanism": "PLAIN", "sasl.jaas.config": "org.apache.kafka.common.security.plain.PlainLoginModule required username=\\"xxxxxx\\" password=\\"xxxxxx\\";" +}`, + rows: 8, + }, + }, + { + key: 'jmxProperties', + label: 'JMX认证', + type: 'text_area', + rules: [{ + required: false, + message: '请输入JMX认证', + }], + attrs: { + placeholder: `请输入JMX认证,例如: +{ +"maxConn": 10, #KM对单台Broker的最大jmx连接数 +"username": "xxxxx", #用户名 +"password": "xxxxx", #密码 +"openSSL": true, #开启SSL,true表示开启SSL,false表示关闭 }`, rows: 8, }, @@ -256,32 +277,41 @@ export class ClusterList extends SearchAndFilterContainer { public getColumns = () => { const cols = getAdminClusterColumns(); + const role = users.currentUser.role; const col = { title: '操作', render: (value: string, item: IMetaData) => ( <> - 编辑 - - this.pauseMonitor(item)} - cancelText="取消" - okText="确认" - > - + { + role && role === 2 ? <> - {item.status === 1 ? '暂停监控' : '开始监控'} + >编辑 - - - - 删除 - + this.pauseMonitor(item)} + cancelText="取消" + okText="确认" + > + + + {item.status === 1 ? '暂停监控' : '开始监控'} + + + + + 删除 + + : + 编辑 + {item.status === 1 ? '暂停监控' : '开始监控'} + 删除 + + } ), }; @@ -290,6 +320,7 @@ export class ClusterList extends SearchAndFilterContainer { } public renderClusterList() { + const role = users.currentUser.role; return ( <>
@@ -298,7 +329,14 @@ export class ClusterList extends SearchAndFilterContainer { {this.renderSearch('', '请输入集群名称')}
  • 集群接入指南 - + { + role && role === 2 ? + + : + + + + }
  • diff --git a/kafka-manager-console/src/container/admin/config.tsx b/kafka-manager-console/src/container/admin/config.tsx index bce02cce..09a70f83 100644 --- a/kafka-manager-console/src/container/admin/config.tsx +++ b/kafka-manager-console/src/container/admin/config.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; -import { IUser, IUploadFile, IConfigure, IMetaData, IBrokersPartitions } from 'types/base-type'; +import { IUser, IUploadFile, IConfigure, IConfigGateway, IMetaData } from 'types/base-type'; import { users } from 'store/users'; import { version } from 'store/version'; -import { showApplyModal, showModifyModal, showConfigureModal } from 'container/modal/admin'; +import { showApplyModal, showApplyModalModifyPassword, showModifyModal, showConfigureModal, showConfigGatewayModal } from 'container/modal/admin'; import { Popconfirm, Tooltip } from 'component/antd'; import { admin } from 'store/admin'; import { cellStyle } from 'constants/table'; @@ -27,6 +27,7 @@ export const getUserColumns = () => { return ( showApplyModal(record)}>编辑 + showApplyModalModifyPassword(record)}>修改密码 users.deleteUser(record.username)} @@ -184,6 +185,87 @@ export const getConfigureColumns = () => { return columns; }; +// 网关配置 +export const getConfigColumns = () => { + const columns = [ + { + title: '配置类型', + dataIndex: 'type', + key: 'type', + width: '25%', + ellipsis: true, + sorter: (a: IConfigGateway, b: IConfigGateway) => a.type.charCodeAt(0) - b.type.charCodeAt(0), + }, + { + title: '配置键', + dataIndex: 'name', + key: 'name', + width: '15%', + ellipsis: true, + sorter: (a: IConfigGateway, b: IConfigGateway) => a.name.charCodeAt(0) - b.name.charCodeAt(0), + }, + { + title: '配置值', + dataIndex: 'value', + key: 'value', + width: '20%', + ellipsis: true, + sorter: (a: IConfigGateway, b: IConfigGateway) => a.value.charCodeAt(0) - b.value.charCodeAt(0), + render: (t: string) => { + return t.substr(0, 1) === '{' && t.substr(0, -1) === '}' ? JSON.stringify(JSON.parse(t), null, 4) : t; + }, + }, + { + title: '修改时间', + dataIndex: 'modifyTime', + key: 'modifyTime', + width: '15%', + sorter: (a: IConfigGateway, b: IConfigGateway) => b.modifyTime - a.modifyTime, + render: (t: number) => moment(t).format(timeFormat), + }, + { + title: '版本号', + dataIndex: 'version', + key: 'version', + width: '10%', + ellipsis: true, + sorter: (a: IConfigGateway, b: IConfigGateway) => b.version.charCodeAt(0) - a.version.charCodeAt(0), + }, + { + title: '描述信息', + dataIndex: 'description', + key: 'description', + width: '20%', + ellipsis: true, + onCell: () => ({ + style: { + maxWidth: 180, + ...cellStyle, + }, + }), + }, + { + title: '操作', + width: '10%', + render: (text: string, record: IConfigGateway) => { + return ( + + showConfigGatewayModal(record)}>编辑 + admin.deleteConfigGateway({ id: record.id })} + cancelText="取消" + okText="确认" + > + 删除 + + ); + }, + }, + ]; + return columns; +}; + const renderClusterHref = (value: number | string, item: IMetaData, key: number) => { return ( // 0 暂停监控--不可点击 1 监控中---可正常点击 <> diff --git a/kafka-manager-console/src/container/admin/configure-management.tsx b/kafka-manager-console/src/container/admin/configure-management.tsx index 680d1da7..5c3494b9 100644 --- a/kafka-manager-console/src/container/admin/configure-management.tsx +++ b/kafka-manager-console/src/container/admin/configure-management.tsx @@ -3,11 +3,11 @@ import { SearchAndFilterContainer } from 'container/search-filter'; import { Table, Button, Spin } from 'component/antd'; import { admin } from 'store/admin'; import { observer } from 'mobx-react'; -import { IConfigure } from 'types/base-type'; +import { IConfigure, IConfigGateway } from 'types/base-type'; import { users } from 'store/users'; import { pagination } from 'constants/table'; -import { getConfigureColumns } from './config'; -import { showConfigureModal } from 'container/modal/admin'; +import { getConfigureColumns, getConfigColumns } from './config'; +import { showConfigureModal, showConfigGatewayModal } from 'container/modal/admin'; @observer export class ConfigureManagement extends SearchAndFilterContainer { @@ -17,7 +17,12 @@ export class ConfigureManagement extends SearchAndFilterContainer { }; public componentDidMount() { - admin.getConfigure(); + if (this.props.isShow) { + admin.getGatewayList(); + admin.getGatewayType(); + } else { + admin.getConfigure(); + } } public getData(origin: T[]) { @@ -34,15 +39,34 @@ export class ConfigureManagement extends SearchAndFilterContainer { return data; } + public getGatewayData(origin: T[]) { + let data: T[] = origin; + let { searchKey } = this.state; + searchKey = (searchKey + '').trim().toLowerCase(); + + data = searchKey ? origin.filter((item: IConfigGateway) => + ((item.name !== undefined && item.name !== null) && item.name.toLowerCase().includes(searchKey as string)) + || ((item.value !== undefined && item.value !== null) && item.value.toLowerCase().includes(searchKey as string)) + || ((item.description !== undefined && item.description !== null) && + item.description.toLowerCase().includes(searchKey as string)), + ) : origin; + return data; + } + public renderTable() { return ( -
    :
    + />} ); @@ -53,7 +77,7 @@ export class ConfigureManagement extends SearchAndFilterContainer {
      {this.renderSearch('', '请输入配置键、值或描述')}
    • - +
    ); diff --git a/kafka-manager-console/src/container/admin/data-curve/index.tsx b/kafka-manager-console/src/container/admin/data-curve/index.tsx index bd113aeb..b822957c 100644 --- a/kafka-manager-console/src/container/admin/data-curve/index.tsx +++ b/kafka-manager-console/src/container/admin/data-curve/index.tsx @@ -6,6 +6,7 @@ import { curveKeys, CURVE_KEY_MAP, PERIOD_RADIO_MAP, PERIOD_RADIO } from './conf import moment = require('moment'); import { observer } from 'mobx-react'; import { timeStampStr } from 'constants/strategy'; +import { adminMonitor } from 'store/admin-monitor'; @observer export class DataCurveFilter extends React.Component { @@ -21,6 +22,7 @@ export class DataCurveFilter extends React.Component { } public refreshAll = () => { + adminMonitor.setRequestId(null); Object.keys(curveKeys).forEach((c: curveKeys) => { const { typeInfo, curveInfo: option } = CURVE_KEY_MAP.get(c); const { parser } = typeInfo; @@ -32,7 +34,7 @@ export class DataCurveFilter extends React.Component { return ( <> - {PERIOD_RADIO.map(p => {p.label})} + {PERIOD_RADIO.map(p => {p.label})} { + const formMap = [ + { + key: 'moduleId', + label: '模块', + type: 'select', + attrs: { + style: { + width: '130px' + }, + placeholder: '请选择模块', + }, + options: moduleList.map(item => { + return { + label: item.moduleName, + value: item.moduleId + } + }), + formAttrs: { + initialvalue: 0, + }, + }, + { + key: 'operator', + label: '操作人', + type: 'input', + attrs: { + style: { + width: '170px' + }, + placeholder: '请输入操作人' + }, + getvaluefromevent: (event: any) => { + return event.target.value.replace(/\s+/g, '') + }, + }, + // { + // key: 'resource', + // label: '资源名称', + // type: 'input', + // attrs: { + // style: { + // width: '170px' + // }, + // placeholder: '请输入资源名称' + // }, + // }, + // { + // key: 'content', + // label: '操作内容', + // type: 'input', + // attrs: { + // style: { + // width: '170px' + // }, + // placeholder: '请输入操作内容' + // }, + // }, + ] + return formMap; +} +export const getOperateColumns = () => { + + const columns: any = [ + { + title: '模块', + dataIndex: 'module', + key: 'module', + align: 'center', + width: '12%' + }, + { + title: '资源名称', + dataIndex: 'resource', + key: 'resource', + align: 'center', + width: '12%' + }, + { + title: '操作内容', + dataIndex: 'content', + key: 'content', + align: 'center', + width: '25%', + onCell: () => ({ + style: { + maxWidth: 350, + ...cellStyle, + }, + }), + render: (text: string, record: any) => { + return ( + {text}); + }, + }, + { + title: '操作人', + dataIndex: 'operator', + align: 'center', + width: '12%' + }, + ]; + return columns +} \ No newline at end of file diff --git a/kafka-manager-console/src/container/admin/operation-record/index.tsx b/kafka-manager-console/src/container/admin/operation-record/index.tsx new file mode 100644 index 00000000..2b18458c --- /dev/null +++ b/kafka-manager-console/src/container/admin/operation-record/index.tsx @@ -0,0 +1,130 @@ +import * as React from 'react'; +import { observer } from 'mobx-react'; +import { SearchAndFilterContainer } from 'container/search-filter'; +import { IXFormWrapper, IMetaData, IRegister } from 'types/base-type'; +import { admin } from 'store/admin'; +import { customPagination, cellStyle } from 'constants/table'; +import { Table, Tooltip } from 'component/antd'; +import { timeFormat } from 'constants/strategy'; +import { SearchFormComponent } from '../searchForm'; +import { getJarFuncForm, operateList, getOperateColumns } from './config' +import moment = require('moment'); +import { tableFilter } from 'lib/utils'; + +@observer +export class OperationRecord extends SearchAndFilterContainer { + public state: any = { + searchKey: '', + filteredInfo: null, + sortedInfo: null, + }; + + public getData(origin: T[]) { + let data: T[] = origin; + let { searchKey } = this.state; + searchKey = (searchKey + '').trim().toLowerCase(); + + data = searchKey ? origin.filter((item: IMetaData) => + (item.clusterName !== undefined && item.clusterName !== null) && item.clusterName.toLowerCase().includes(searchKey as string), + ) : origin; + return data; + }; + + public searchForm = (params: any) => { + // this.props.setFuncSubValue(params) + // getSystemFuncList(params).then(res => { + // this.props.setSysFuncList(res.data) + // this.props.setPagination(res.pagination) + // }) + const { operator, moduleId } = params || {} + operator ? admin.getOperationRecordData(params) : admin.getOperationRecordData({ moduleId }) + // getJarList(params).then(res => { + // this.props.setJarList(res.data) + // this.props.setPagination(res.pagination) + // }) + } + + public clearAll = () => { + this.setState({ + filteredInfo: null, + sortedInfo: null, + }); + }; + + public setHandleChange = (pagination: any, filters: any, sorter: any) => { + this.setState({ + filteredInfo: filters, + sortedInfo: sorter, + }); + } + + public renderOperationRecordList() { + let { sortedInfo, filteredInfo } = this.state; + sortedInfo = sortedInfo || {}; + filteredInfo = filteredInfo || {}; + const operatingTime = Object.assign({ + title: '操作时间', + dataIndex: 'modifyTime', + key: 'modifyTime', + align: 'center', + sorter: (a: any, b: any) => a.modifyTime - b.modifyTime, + render: (t: number) => moment(t).format(timeFormat), + width: '15%', + sortOrder: sortedInfo.columnKey === 'modifyTime' && sortedInfo.order, + }); + + const operatingPractice = Object.assign({ + title: '行为', + dataIndex: 'operate', + key: 'operate', + align: 'center', + width: '12%', + filters: tableFilter(this.getData(admin.oRList), 'operateId', operateList), + // filteredValue: filteredInfo.operate || null, + onFilter: (value: any, record: any) => { + return record.operateId === value + } + }, this.renderColumnsFilter('modifyTime')) + + const columns = getOperateColumns() + columns.splice(0, 0, operatingTime); + columns.splice(3, 0, operatingPractice); + return ( + <> +
    +
    + this.searchForm(params)} + clearAll={() => this.clearAll()} + isReset={true} + /> +
    +
    +
    + + + + ) + }; + + componentDidMount() { + admin.getOperationRecordData({ moduleId: 0 }); + } + + render() { + return
    + { + this.renderOperationRecordList() + } +
    + } +} \ No newline at end of file diff --git a/kafka-manager-console/src/container/admin/platform-management.tsx b/kafka-manager-console/src/container/admin/platform-management.tsx index d48823a4..25f7f0bd 100644 --- a/kafka-manager-console/src/container/admin/platform-management.tsx +++ b/kafka-manager-console/src/container/admin/platform-management.tsx @@ -13,17 +13,20 @@ export class PlatformManagement extends React.Component { public render() { return ( <> - - - - - - - - - - - + + + + + + + + + + + + + + ); } diff --git a/kafka-manager-console/src/container/admin/searchForm.tsx b/kafka-manager-console/src/container/admin/searchForm.tsx new file mode 100644 index 00000000..afbff03c --- /dev/null +++ b/kafka-manager-console/src/container/admin/searchForm.tsx @@ -0,0 +1,120 @@ +import * as React from 'react'; +import { Select, Input, InputNumber, Form, Switch, Checkbox, DatePicker, Radio, Upload, Button, Icon, Tooltip } from 'component/antd'; +// import './index.less'; +const Search = Input.Search; +export interface IFormItem { + key: string; + label: string; + type: string; + value?: string; + // 内部组件属性注入 + attrs?: any; + // form属性注入 + formAttrs?: any; + defaultValue?: string | number | any[]; + rules?: any[]; + invisible?: boolean; + getvaluefromevent: Function; +} + +interface SerachFormProps { + formMap: IFormItem[]; + // formData: any; + form: any; + onSubmit: Function; + isReset?: boolean; + clearAll: Function; + layout?: 'inline' | 'horizontal' | 'vertical'; +} + +export interface IFormSelect extends IFormItem { + options: Array<{ key?: string | number, value: string | number, label: string }>; +} + +class SearchForm extends React.Component{ + public onSubmit = () => { + // this.props.onSubmit() + // + } + + public renderFormItem(item: IFormItem) { + switch (item.type) { + default: + case 'input': + return ; + case 'select': + return ( + + ); + } + } + + public theQueryClick = (value: any) => { + this.props.onSubmit(value) + this.props.clearAll() + // this.props.form.resetFields() + } + public resetClick = () => { + this.props.form.resetFields() + this.props.clearAll() + this.theQueryClick(this.props.form.getFieldsValue()) + } + + public render() { + const { form, formMap, isReset } = this.props; + const { getFieldDecorator, getFieldsValue } = form; + return ( +
    + { + formMap.map(formItem => { + // const { initialValue, valuePropName } = this.handleFormItem(formItem, formData); + // const getFieldValue = { + // initialValue, + // rules: formItem.rules || [{ required: false, message: '' }], + // valuePropName, + // }; + return ( + + {getFieldDecorator(formItem.key, { + initialValue: formItem.formAttrs?.initialvalue, + getValueFromEvent: formItem?.getvaluefromevent, + })( + this.renderFormItem(formItem), + )} + + ); + }) + } + + { + isReset && + } + + + + ); + } +} +export const SearchFormComponent = Form.create({ name: 'search-form' })(SearchForm); \ No newline at end of file diff --git a/kafka-manager-console/src/container/admin/user-management.tsx b/kafka-manager-console/src/container/admin/user-management.tsx index 757ceabb..1dc38e06 100644 --- a/kafka-manager-console/src/container/admin/user-management.tsx +++ b/kafka-manager-console/src/container/admin/user-management.tsx @@ -29,7 +29,7 @@ export class UserManagement extends SearchAndFilterContainer { searchKey = (searchKey + '').trim().toLowerCase(); data = searchKey ? origin.filter((item: IUser) => - (item.username !== undefined && item.username !== null) && item.username.toLowerCase().includes(searchKey as string)) : origin ; + (item.username !== undefined && item.username !== null) && item.username.toLowerCase().includes(searchKey as string)) : origin; return data; } diff --git a/kafka-manager-console/src/container/alarm/add-alarm/alarm-select.tsx b/kafka-manager-console/src/container/alarm/add-alarm/alarm-select.tsx index 6d19ec26..5cd1f4f0 100644 --- a/kafka-manager-console/src/container/alarm/add-alarm/alarm-select.tsx +++ b/kafka-manager-console/src/container/alarm/add-alarm/alarm-select.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { alarm } from 'store/alarm'; import { IMonitorGroups } from 'types/base-type'; -import { getValueFromLocalStorage, setValueToLocalStorage } from 'lib/local-storage'; +import { getValueFromLocalStorage, setValueToLocalStorage, deleteValueFromLocalStorage } from 'lib/local-storage'; import { VirtualScrollSelect } from '../../../component/virtual-scroll-select'; interface IAlarmSelectProps { @@ -36,6 +36,10 @@ export class AlarmSelect extends React.Component { onChange && onChange(params); } + public componentWillUnmount() { + deleteValueFromLocalStorage('monitorGroups'); + } + public render() { const { value, isDisabled } = this.props; return ( diff --git a/kafka-manager-console/src/container/alarm/add-alarm/filter-form.tsx b/kafka-manager-console/src/container/alarm/add-alarm/filter-form.tsx index 3e5dd0b7..fe74e66f 100644 --- a/kafka-manager-console/src/container/alarm/add-alarm/filter-form.tsx +++ b/kafka-manager-console/src/container/alarm/add-alarm/filter-form.tsx @@ -149,9 +149,9 @@ export class DynamicSetFilter extends React.Component { public handleSelectChange = (e: string, type: 'topic' | 'consumerGroup' | 'location') => { switch (type) { case 'topic': - if (!this.clusterId) { - return message.info('请选择集群'); - } + // if (!this.clusterId) { + // return message.info('请选择集群'); + // } this.topicName = e; const type = this.dealMonitorType(); if (['kafka-consumer-maxLag', 'kafka-consumer-maxDelayTime', 'kafka-consumer-lag'].indexOf(type) > -1) { diff --git a/kafka-manager-console/src/container/alarm/alarm-list.tsx b/kafka-manager-console/src/container/alarm/alarm-list.tsx index 6dd6680b..54d266f1 100644 --- a/kafka-manager-console/src/container/alarm/alarm-list.tsx +++ b/kafka-manager-console/src/container/alarm/alarm-list.tsx @@ -9,6 +9,7 @@ import { pagination } from 'constants/table'; import { urlPrefix } from 'constants/left-menu'; import { alarm } from 'store/alarm'; import 'styles/table-filter.less'; +import { Link } from 'react-router-dom'; @observer export class AlarmList extends SearchAndFilterContainer { @@ -24,7 +25,7 @@ export class AlarmList extends SearchAndFilterContainer { if (app.active !== '-1' || searchKey !== '') { data = origin.filter(d => ((d.name !== undefined && d.name !== null) && d.name.toLowerCase().includes(searchKey as string) - || ((d.operator !== undefined && d.operator !== null) && d.operator.toLowerCase().includes(searchKey as string))) + || ((d.operator !== undefined && d.operator !== null) && d.operator.toLowerCase().includes(searchKey as string))) && (app.active === '-1' || d.appId === (app.active + '')), ); } else { @@ -55,9 +56,7 @@ export class AlarmList extends SearchAndFilterContainer { {this.renderSearch('名称:', '请输入告警规则或者操作人')}
  • @@ -68,6 +67,9 @@ export class AlarmList extends SearchAndFilterContainer { if (!alarm.monitorStrategies.length) { alarm.getMonitorStrategies(); } + if (!app.data.length) { + app.getAppList(); + } } public render() { diff --git a/kafka-manager-console/src/container/app/app-list.tsx b/kafka-manager-console/src/container/app/app-list.tsx index ef897246..d49b1048 100644 --- a/kafka-manager-console/src/container/app/app-list.tsx +++ b/kafka-manager-console/src/container/app/app-list.tsx @@ -52,8 +52,7 @@ export class CommonAppList extends SearchAndFilterContainer { }, }), render: (text: string, record: IAppItem) => { - return ( - {text}); + return ({text}); }, }, { @@ -103,7 +102,7 @@ export class CommonAppList extends SearchAndFilterContainer { } public getOnlineConnect(record: IAppItem) { - modal.showOfflineAppModal(record.appId); + modal.showOfflineAppNewModal(record.appId); } public getData(origin: T[]) { @@ -114,7 +113,7 @@ export class CommonAppList extends SearchAndFilterContainer { data = searchKey ? origin.filter((item: IAppItem) => ((item.name !== undefined && item.name !== null) && item.name.toLowerCase().includes(searchKey as string)) || ((item.principals !== undefined && item.principals !== null) && item.principals.toLowerCase().includes(searchKey as string)) || - ((item.appId !== undefined && item.appId !== null) && item.appId.toLowerCase().includes(searchKey as string)) ) : origin; + ((item.appId !== undefined && item.appId !== null) && item.appId.toLowerCase().includes(searchKey as string))) : origin; return data; } diff --git a/kafka-manager-console/src/container/cluster/my-cluster.tsx b/kafka-manager-console/src/container/cluster/my-cluster.tsx index 3c54763a..3cb6115f 100644 --- a/kafka-manager-console/src/container/cluster/my-cluster.tsx +++ b/kafka-manager-console/src/container/cluster/my-cluster.tsx @@ -29,16 +29,16 @@ export class MyCluster extends SearchAndFilterContainer { public applyCluster() { const xFormModal = { formMap: [ - { - key: 'idc', - label: '数据中心', - defaultValue: region.regionName, - rules: [{ required: true, message: '请输入数据中心' }], - attrs: { - placeholder: '请输入数据中心', - disabled: true, - }, - }, + // { + // key: 'idc', + // label: '数据中心', + // defaultValue: region.regionName, + // rules: [{ required: true, message: '请输入数据中心' }], + // attrs: { + // placeholder: '请输入数据中心', + // disabled: true, + // }, + // }, { key: 'appId', label: '所属应用', @@ -91,7 +91,7 @@ export class MyCluster extends SearchAndFilterContainer { ], formData: {}, visible: true, - title: '申请集群', + title:
    申请集群资源申请文档
    , okText: '确认', onSubmit: (value: any) => { value.idc = region.currentRegion; diff --git a/kafka-manager-console/src/container/drawer/data-migration.tsx b/kafka-manager-console/src/container/drawer/data-migration.tsx index 28353004..4da64f5c 100644 --- a/kafka-manager-console/src/container/drawer/data-migration.tsx +++ b/kafka-manager-console/src/container/drawer/data-migration.tsx @@ -117,12 +117,12 @@ class DataMigrationFormTable extends React.Component { key: 'maxThrottle', editable: true, }, { - title: '迁移保存时间(h)', + title: '迁移后Topic保存时间(h)', dataIndex: 'reassignRetentionTime', key: 'reassignRetentionTime', editable: true, }, { - title: '原本保存时间(h)', + title: '原Topic保存时间(h)', dataIndex: 'retentionTime', key: 'retentionTime', // originalRetentionTime width: '132px', diff --git a/kafka-manager-console/src/container/expert/topic-governance/index.tsx b/kafka-manager-console/src/container/expert/topic-governance/index.tsx index 21bd9b10..b495e2f8 100644 --- a/kafka-manager-console/src/container/expert/topic-governance/index.tsx +++ b/kafka-manager-console/src/container/expert/topic-governance/index.tsx @@ -133,15 +133,15 @@ export class GovernanceTopic extends SearchAndFilterContainer { width: '30%', sorter: (a: IResource, b: IResource) => a.topicName.charCodeAt(0) - b.topicName.charCodeAt(0), render: (text: string, item: IResource) => - ( - - - {text} - - ), + ( + + + {text} + + ), }, { title: '所在集群', @@ -215,7 +215,7 @@ export class GovernanceTopic extends SearchAndFilterContainer { return ( <> - {this.pendingTopic(this.getData(expert.resourceData))} + {this.pendingTopic(this.getData(expert.resourceData))} ); } diff --git a/kafka-manager-console/src/container/header/index.less b/kafka-manager-console/src/container/header/index.less index 98aefd99..53301a92 100644 --- a/kafka-manager-console/src/container/header/index.less +++ b/kafka-manager-console/src/container/header/index.less @@ -16,6 +16,14 @@ line-height: 64px; vertical-align: middle; } + .kafka-header-version{ + display: inline-block; + vertical-align: middle; + padding-top:5px; + font-size: 12px; + margin-left:10px; + color:#a0a0a0; + } } .mid-content { diff --git a/kafka-manager-console/src/container/header/index.tsx b/kafka-manager-console/src/container/header/index.tsx index e86bcf11..ebda9760 100644 --- a/kafka-manager-console/src/container/header/index.tsx +++ b/kafka-manager-console/src/container/header/index.tsx @@ -145,6 +145,8 @@ export const Header = observer((props: IHeader) => {
    Kafka Manager + v2.4.0 + {/* 添加版本超链接 */}
    {headerMenu.map((item: IMenuItem, index: number) => diff --git a/kafka-manager-console/src/container/modal/admin/cluster.ts b/kafka-manager-console/src/container/modal/admin/cluster.ts index cea987f6..ac4af3a5 100644 --- a/kafka-manager-console/src/container/modal/admin/cluster.ts +++ b/kafka-manager-console/src/container/modal/admin/cluster.ts @@ -22,11 +22,11 @@ export const showEditClusterTopic = (item: IClusterTopics) => { }, { key: 'appId', - label: '应用ID', + label: '应用名称', type: 'select', options: app.adminAppData.map(item => { return { - label: item.appId, + label: item.name, value: item.appId, }; }), @@ -61,6 +61,7 @@ export const showEditClusterTopic = (item: IClusterTopics) => { attrs: { placeholder: '请输入保存时间', suffix: '小时', + prompttype: '修改保存时间,预计一分钟左右生效!' }, }, { diff --git a/kafka-manager-console/src/container/modal/admin/confirm-detail-topic.tsx b/kafka-manager-console/src/container/modal/admin/confirm-detail-topic.tsx index 41f6ed61..83d1b02d 100644 --- a/kafka-manager-console/src/container/modal/admin/confirm-detail-topic.tsx +++ b/kafka-manager-console/src/container/modal/admin/confirm-detail-topic.tsx @@ -35,7 +35,6 @@ class CustomForm extends React.Component { 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 { @@ -77,7 +76,6 @@ class CustomForm extends React.Component { } public render() { - // console.log('props', this.props); const { formData = {} as any, visible } = this.props; const { getFieldDecorator } = this.props.form; let metadata = [] as IBrokersMetadata[]; diff --git a/kafka-manager-console/src/container/modal/admin/expand-partition.tsx b/kafka-manager-console/src/container/modal/admin/expand-partition.tsx index dfb51ba9..44001081 100644 --- a/kafka-manager-console/src/container/modal/admin/expand-partition.tsx +++ b/kafka-manager-console/src/container/modal/admin/expand-partition.tsx @@ -111,11 +111,11 @@ class CustomForm extends React.Component { })()} - {/* */} + {/* */} {/* this.onSwitchChange(checked)} /> */} { this.onSwitchChange(e.target.value === 'region' ? true : false); }}> Region类型 - Borker类型 + Broker类型 diff --git a/kafka-manager-console/src/container/modal/admin/task.ts b/kafka-manager-console/src/container/modal/admin/task.ts index be240f7a..12a46d53 100644 --- a/kafka-manager-console/src/container/modal/admin/task.ts +++ b/kafka-manager-console/src/container/modal/admin/task.ts @@ -28,8 +28,8 @@ const updateInputModal = (status?: string) => { formMap[4].invisible = status === 'region'; formMap[5].invisible = status !== 'region'; - formMap[4].rules = [{required: status !== 'region'}]; - formMap[5].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); }; @@ -103,7 +103,7 @@ export const createMigrationTasks = () => { label: 'Region', value: 'region', }, { - label: 'Borker', + label: 'Broker', value: 'broker', }], rules: [{ @@ -141,7 +141,7 @@ export const createMigrationTasks = () => { placeholder: '请选择目标Region', }, }, - + { key: 'beginTime', label: '计划开始时间', @@ -158,26 +158,26 @@ export const createMigrationTasks = () => { }, { key: 'originalRetentionTime', - label: '原本保存时间', + label: '原Topic保存时间', rules: [{ required: true, - message: '请输入原本保存时间', + message: '请输入原Topic保存时间', }], attrs: { disabled: true, - placeholder: '请输入原本保存时间', + placeholder: '请输入原Topic保存时间', suffix: '小时', }, }, { key: 'reassignRetentionTime', - label: '迁移保存时间', + label: '迁移后Topic保存时间', rules: [{ required: true, - message: '请输入迁移保存时间', + message: '请输入迁移后Topic保存时间', }], attrs: { - placeholder: '请输入迁移保存时间', + placeholder: '请输入迁移后Topic保存时间', suffix: '小时', }, }, diff --git a/kafka-manager-console/src/container/modal/admin/user.ts b/kafka-manager-console/src/container/modal/admin/user.ts index 9f35e4cf..51ca360d 100644 --- a/kafka-manager-console/src/container/modal/admin/user.ts +++ b/kafka-manager-console/src/container/modal/admin/user.ts @@ -24,26 +24,111 @@ export const showApplyModal = (record?: IUser) => { value: +item, })), rules: [{ required: true, message: '请选择角色' }], - }, { - key: 'password', - label: '密码', - type: FormItemType.inputPassword, - rules: [{ required: !record, message: '请输入密码' }], - }, + }, + // { + // key: 'password', + // label: '密码', + // type: FormItemType.inputPassword, + // rules: [{ required: !record, message: '请输入密码' }], + // }, ], formData: record || {}, visible: true, title: record ? '修改用户' : '新增用户', onSubmit: (value: IUser) => { if (record) { - return users.modfiyUser(value).then(() => { - message.success('操作成功'); - }); + return users.modfiyUser(value) } return users.addUser(value).then(() => { message.success('操作成功'); }); }, }; + if(!record){ + let formMap: any = xFormModal.formMap + formMap.splice(2, 0,{ + key: 'password', + label: '密码', + type: FormItemType.inputPassword, + rules: [{ required: !record, message: '请输入密码' }], + },) + } wrapper.open(xFormModal); }; + +// const handleCfPassword = (rule:any, value:any, callback:any)=>{ +// if() +// } +export const showApplyModalModifyPassword = (record: IUser) => { + const xFormModal:any = { + formMap: [ + // { + // key: 'oldPassword', + // label: '旧密码', + // type: FormItemType.inputPassword, + // rules: [{ + // required: true, + // message: '请输入旧密码', + // }] + // }, + { + key: 'newPassword', + label: '新密码', + type: FormItemType.inputPassword, + rules: [ + { + required: true, + message: '请输入新密码', + } + ], + attrs:{ + onChange:(e:any)=>{ + users.setNewPassWord(e.target.value) + } + } + }, + { + key: 'confirmPassword', + label: '确认密码', + type: FormItemType.inputPassword, + rules: [ + { + required: true, + message: '请确认密码', + validator:(rule:any, value:any, callback:any) => { + // 验证新密码的一致性 + if(users.newPassWord){ + if(value!==users.newPassWord){ + rule.message = "两次密码输入不一致"; + callback('两次密码输入不一致') + }else{ + callback() + } + }else if(!value){ + rule.message = "请确认密码"; + callback('请确认密码'); + }else{ + callback() + } + }, + } + ], + }, + ], + formData: record || {}, + visible: true, + title: '修改密码', + onSubmit: (value: IUser) => { + let params:any = { + username:record?.username, + password:value.confirmPassword, + role:record?.role, + } + return users.modfiyUser(params).then(() => { + message.success('操作成功'); + }); + }, + } + wrapper.open(xFormModal); +}; + diff --git a/kafka-manager-console/src/container/modal/admin/version.ts b/kafka-manager-console/src/container/modal/admin/version.ts index ea642a8f..c863eba1 100644 --- a/kafka-manager-console/src/container/modal/admin/version.ts +++ b/kafka-manager-console/src/container/modal/admin/version.ts @@ -1,6 +1,6 @@ import * as React from 'react'; -import { notification } from 'component/antd'; -import { IUploadFile, IConfigure } from 'types/base-type'; +import { notification, Select } from 'component/antd'; +import { IUploadFile, IConfigure, IConfigGateway } from 'types/base-type'; import { version } from 'store/version'; import { admin } from 'store/admin'; import { wrapper } from 'store'; @@ -97,8 +97,8 @@ const updateFormModal = (type: number) => { formMap[2].attrs = { accept: version.fileSuffix, }, - // tslint:disable-next-line:no-unused-expression - wrapper.ref && wrapper.ref.updateFormMap$(formMap, wrapper.xFormWrapper.formData, true); + // tslint:disable-next-line:no-unused-expression + wrapper.ref && wrapper.ref.updateFormMap$(formMap, wrapper.xFormWrapper.formData, true); } }; @@ -157,8 +157,8 @@ export const showModifyModal = (record: IUploadFile) => { export const showConfigureModal = async (record?: IConfigure) => { if (record) { - const result:any = await format2json(record.configValue); - record.configValue = result.result; + const result: any = await format2json(record.configValue); + record.configValue = result.result || record.configValue; } const xFormModal = { formMap: [ @@ -193,10 +193,69 @@ export const showConfigureModal = async (record?: IConfigure) => { return admin.editConfigure(value).then(data => { notification.success({ message: '编辑配置成功' }); }); + } else { + return admin.addNewConfigure(value).then(data => { + notification.success({ message: '新建配置成功' }); + }); + } + }, + }; + wrapper.open(xFormModal); +}; + +export const showConfigGatewayModal = async (record?: IConfigGateway) => { + const xFormModal = { + formMap: [ + { + key: 'type', + label: '配置类型', + rules: [{ required: true, message: '请选择配置类型' }], + type: "select", + options: admin.gatewayType.map((item: any, index: number) => ({ + key: index, + label: item.configName, + value: item.configType, + })), + attrs: { + disabled: record ? true : false, + } + }, { + key: 'name', + label: '配置键', + rules: [{ required: true, message: '请输入配置键' }], + attrs: { + disabled: record ? true : false, + }, + }, { + key: 'value', + label: '配置值', + type: 'text_area', + rules: [{ + required: true, + message: '请输入配置值', + }], + }, { + key: 'description', + label: '描述', + type: 'text_area', + rules: [{ required: true, message: '请输入备注' }], + }, + ], + formData: record || {}, + visible: true, + isWaitting: true, + title: `${record ? '编辑配置' : '新建配置'}`, + onSubmit: async (parmas: IConfigGateway) => { + if (record) { + parmas.id = record.id; + return admin.editConfigGateway(parmas).then(data => { + notification.success({ message: '编辑配置成功' }); + }); + } else { + return admin.addNewConfigGateway(parmas).then(data => { + notification.success({ message: '新建配置成功' }); + }); } - return admin.addNewConfigure(value).then(data => { - notification.success({ message: '新建配置成功' }); - }); }, }; wrapper.open(xFormModal); diff --git a/kafka-manager-console/src/container/modal/app.tsx b/kafka-manager-console/src/container/modal/app.tsx index bda37418..bb0320ec 100644 --- a/kafka-manager-console/src/container/modal/app.tsx +++ b/kafka-manager-console/src/container/modal/app.tsx @@ -85,7 +85,7 @@ export const showEditModal = (record?: IAppItem, from?: string, isDisabled?: boo ], formData: record, visible: true, - title: isDisabled ? '详情' : record ? '编辑' :
    应用申请应用申请文档
    , + title: isDisabled ? '详情' : record ? '编辑' :
    应用申请资源申请文档
    , // customRenderElement: isDisabled ? '' : record ? '' : 集群资源充足时,预计1分钟自动审批通过, isWaitting: true, onSubmit: (value: IAppItem) => { diff --git a/kafka-manager-console/src/container/modal/expert.tsx b/kafka-manager-console/src/container/modal/expert.tsx index 96a1f312..2ff5e5f2 100644 --- a/kafka-manager-console/src/container/modal/expert.tsx +++ b/kafka-manager-console/src/container/modal/expert.tsx @@ -20,14 +20,14 @@ export interface IRenderData { } export const migrationModal = (renderData: IRenderData[]) => { - const xFormWrapper = { + const xFormWrapper = { type: 'drawer', visible: true, width: 1000, title: '新建迁移任务', - customRenderElement: , + customRenderElement: , nofooter: true, noform: true, }; - wrapper.open(xFormWrapper as IXFormWrapper); + wrapper.open(xFormWrapper as IXFormWrapper); }; diff --git a/kafka-manager-console/src/container/modal/offline-app-modal-new.tsx b/kafka-manager-console/src/container/modal/offline-app-modal-new.tsx new file mode 100644 index 00000000..2b582f77 --- /dev/null +++ b/kafka-manager-console/src/container/modal/offline-app-modal-new.tsx @@ -0,0 +1,78 @@ +import * as React from 'react'; +import { Table, Modal, Tooltip, Icon, message, notification, Alert, Button } from 'component/antd'; +import { app } from 'store/app'; +import { getApplyOnlineColumns } from 'container/topic/config'; +import { observer } from 'mobx-react'; +import { modal } from 'store/modal'; +import { users } from 'store/users'; +import { urlPrefix } from 'constants/left-menu'; +import { region } from 'store'; + +@observer +export class ConnectAppNewList extends React.Component { + + public componentDidMount() { + app.getAppsConnections(modal.params); + } + + public handleCancel = () => { + app.setAppsConnections([]); + modal.close(); + } + + public handleSubmit = () => { + const connectionList = app.appsConnections; + if (connectionList && connectionList.length) { + return message.warning('存在连接信息,无法申请下线!'); + } + const offlineParams = { + type: 11, + applicant: users.currentUser.username, + description: '', + extensions: JSON.stringify({ appId: modal.params }), + }; + app.applyAppOffline(offlineParams).then((data: any) => { + notification.success({ message: '申请下线成功' }); + window.location.href = `${urlPrefix}/user/order-detail/?orderId=${data.id}®ion=${region.currentRegion}`; + }); + modal.close(); + } + + public render() { + const connectionList = app.appsConnections; + return ( + <> + 确定 + : + <> + + + + } + width={500} + > +
    + { + connectionList && connectionList.length + ? + 该应用已与Topic关联,请先解除应用与Topic之间的关系。点击查看 + : + 应用下线后,AppID、密钥将会失效,请谨慎操作! + } +
    +
    + + ); + } +} diff --git a/kafka-manager-console/src/container/modal/order.tsx b/kafka-manager-console/src/container/modal/order.tsx index 2930e3df..ac2a82b7 100644 --- a/kafka-manager-console/src/container/modal/order.tsx +++ b/kafka-manager-console/src/container/modal/order.tsx @@ -4,6 +4,7 @@ import { message, Icon, notification, Modal, Table, Tooltip } from 'component/an import { IApprovalOrder, IBaseOrder, IOrderInfo } from 'types/base-type'; import { admin } from 'store/admin'; import { modal } from 'store/modal'; +import { cluster } from 'store/cluster'; import { cellStyle } from 'constants/table'; import * as React from 'react'; @@ -37,6 +38,12 @@ const renderModalTilte = (type: number, status: number) => { export const showApprovalModal = (info: IOrderInfo, status: number, from?: string) => { const { id, type } = info; const formMap = [{ + key: 'clusterId', + label: '所属集群', + type: 'input_number', + defaultValue: info.detail.logicalClusterName, + attrs: { disabled: true }, + }, { key: 'partitionNum', label: '分区数', type: 'input_number', @@ -75,8 +82,8 @@ export const showApprovalModal = (info: IOrderInfo, status: number, from?: strin // }], rules: [{ required: true, - message: '请输入大于12小于999的整数', - pattern: /^([1-9]{1}[0-9]{2})$|^([2-9]{1}[0-9]{1})$|^(1[2-9]{1})$/, + message: '请输入大于0小于10000的整数', + pattern: /^\+?[1-9]\d{0,3}(\.\d*)?$/, }], }, { key: 'species', @@ -87,7 +94,7 @@ export const showApprovalModal = (info: IOrderInfo, status: number, from?: strin label: 'Region', value: 'region', }, { - label: 'Borker', + label: 'Broker', value: 'broker', }], rules: [{ required: false, message: '请选择类型' }], diff --git a/kafka-manager-console/src/container/modal/topic.tsx b/kafka-manager-console/src/container/modal/topic.tsx index 7053a497..4e026641 100644 --- a/kafka-manager-console/src/container/modal/topic.tsx +++ b/kafka-manager-console/src/container/modal/topic.tsx @@ -88,7 +88,7 @@ export const applyTopic = () => { ], formData: {}, visible: true, - title: '申请Topic', + title: , okText: '确认', // customRenderElement: 集群资源充足时,预计1分钟自动审批通过, isWaitting: true, @@ -399,8 +399,8 @@ export const updateAllTopicFormModal = () => { const formMap = wrapper.xFormWrapper.formMap; if (topic.authorities) { const { consume, send, checkStatus } = judgeAccessStatus(topic.authorities.access); - formMap[3].defaultValue = checkStatus; - formMap[3].options = [{ + formMap[2].defaultValue = checkStatus; + formMap[2].options = [{ label: `消费权限${consume ? '(已拥有)' : ''}`, value: '1', disabled: consume, @@ -409,7 +409,7 @@ export const updateAllTopicFormModal = () => { value: '2', disabled: send, }]; - formMap[3].rules = [{ + formMap[2].rules = [{ required: true, validator: (rule: any, value: any, callback: any) => getPowerValidator(rule, value, callback, checkStatus, 'allTopic'), }]; @@ -476,7 +476,6 @@ export const showAllPermissionModal = (item: ITopic) => { const showAllPermission = (appId: string, item: ITopic, access: number) => { const { consume, send, checkStatus } = judgeAccessStatus(access); - const xFormModal = { formMap: [ { @@ -489,16 +488,6 @@ const showAllPermission = (appId: string, item: ITopic, access: number) => { disabled: true, }, }, - { - key: 'clusterName', - label: '集群名称', - defaultValue: item.clusterName, - rules: [{ required: true, message: '请输入集群名称' }], - attrs: { - placeholder: '请输入集群名称', - disabled: true, - }, - }, { key: 'appId', label: '绑定应用', @@ -526,6 +515,26 @@ const showAllPermission = (appId: string, item: ITopic, access: number) => { validator: (rule: any, value: any, callback: any) => getPowerValidator(rule, value, callback, checkStatus, 'allTopic'), }], }, + // { + // key: 'clusterName', + // label: '集群名称', + // defaultValue: item.clusterName, + // rules: [{ required: true, message: '请输入集群名称' }], + // attrs: { + // placeholder: '请输入集群名称', + // disabled: true, + // }, + // }, + // { + // key: 'clusterName', + // label: '集群名称', + // defaultValue: item.clusterName, + // rules: [{ required: true, message: '请输入集群名称' }], + // attrs: { + // placeholder: '请输入集群名称', + // disabled: true, + // }, + // }, { key: 'description', label: '申请原因', @@ -587,16 +596,16 @@ export const showPermissionModal = (item: ITopic) => { disabled: true, }, }, - { - key: 'clusterName', - label: '集群名称', - defaultValue: item.clusterName, - rules: [{ required: true, message: '请输入集群名称' }], - attrs: { - placeholder: '请输入集群名称', - disabled: true, - }, - }, + // { + // key: 'clusterName', + // label: '集群名称', + // defaultValue: item.clusterName, + // rules: [{ required: true, message: '请输入集群名称' }], + // attrs: { + // placeholder: '请输入集群名称', + // disabled: true, + // }, + // }, { key: 'appName', label: '绑定应用', diff --git a/kafka-manager-console/src/container/search-filter.tsx b/kafka-manager-console/src/container/search-filter.tsx index 12603d40..f6ed09fa 100644 --- a/kafka-manager-console/src/container/search-filter.tsx +++ b/kafka-manager-console/src/container/search-filter.tsx @@ -194,7 +194,7 @@ export class SearchAndFilterContainer extends React.Component { + public renderColumnsFilter = (type: string, params?: any) => { return { filterIcon: this.renderFilterIcon.bind(null, type), filterDropdownVisible: this.state[type] as boolean, diff --git a/kafka-manager-console/src/container/staff-select.tsx b/kafka-manager-console/src/container/staff-select.tsx index e9072569..f01637e9 100644 --- a/kafka-manager-console/src/container/staff-select.tsx +++ b/kafka-manager-console/src/container/staff-select.tsx @@ -28,7 +28,8 @@ export class StaffSelect extends React.Component { public getStaffList = () => { const { value } = this.props; const current = users.currentUser.username || getCookie('username'); - const principals = value || (current ? [current] : []); + const principals = ['']; + // const principals = value || (current ? [current] : []); const promises: any[] = []; for (const item of principals) { @@ -64,7 +65,6 @@ export class StaffSelect extends React.Component { const { value, isDisabled } = this.props; const current = users.currentUser.username || getCookie('username'); const principals = value || (current ? [current] : []); - return (
    @@ -214,7 +215,12 @@ export class GroupID extends SearchAndFilterContainer { dataIndex: 'location', key: 'location', width: '34%', - }, + }, { + title: '状态', + dataIndex: 'state', + key: 'state', + width: '34%', + } ]; return ( <> @@ -236,7 +242,17 @@ export class GroupID extends SearchAndFilterContainer { data = searchKey ? origin.filter((item: IConsumerGroups) => (item.consumerGroup !== undefined && item.consumerGroup !== null) && item.consumerGroup.toLowerCase().includes(searchKey as string), - ) : origin ; + ) : origin; + return data; + } + + public getDetailData(origin: T[]) { + let data: T[] = origin; + let { searchKey } = this.state; + searchKey = (searchKey + '').trim().toLowerCase(); + data = searchKey ? origin.filter((item: IConsumeDetails) => + (item.clientId !== undefined && item.clientId !== null) && item.clientId.toLowerCase().includes(searchKey as string), + ) : origin; return data; } diff --git a/kafka-manager-console/src/container/topic/topic-detail/reset-offset.tsx b/kafka-manager-console/src/container/topic/topic-detail/reset-offset.tsx index be0767e8..531f69c6 100644 --- a/kafka-manager-console/src/container/topic/topic-detail/reset-offset.tsx +++ b/kafka-manager-console/src/container/topic/topic-detail/reset-offset.tsx @@ -71,32 +71,32 @@ class ResetOffset extends React.Component { const { getFieldDecorator } = this.props.form; const { typeValue, offsetValue } = this.state; return ( - <> - - - + <> + + + {/* */}
    - + 重置到指定时间
    - - 最新offset - 自定义 - + + 最新offset + 自定义 + {typeValue === 'time' && offsetValue === 'custom' && getFieldDecorator('timestamp', { rules: [{ required: false, message: '' }], initialValue: moment(), - })( + })( { 重置指定分区及偏移 - + diff --git a/kafka-manager-console/src/container/user-center/config.tsx b/kafka-manager-console/src/container/user-center/config.tsx index 94de7fb7..43c9c85d 100644 --- a/kafka-manager-console/src/container/user-center/config.tsx +++ b/kafka-manager-console/src/container/user-center/config.tsx @@ -30,7 +30,7 @@ export const getInfoRenderItem = (orderInfo: IOrderInfo, result: boolean) => { value: orderInfo.detail.principals, }]; - const clusterTypelist: ILabelValue[] = [ { + const clusterTypelist: ILabelValue[] = [{ label: '物理集群名称', value: orderInfo.detail.physicalClusterName, }, { @@ -80,23 +80,25 @@ export const getInfoRenderItem = (orderInfo: IOrderInfo, result: boolean) => { phyAuthOfflineList.splice(3, 0, ...clusterTypelist); const clusterInfoList: ILabelValue[] = [{ - label: '流入流量', - value: `${transBToMB(orderInfo.detail.bytesIn)} MB/s`, - }, { - label: '数据中心', - value: orderInfo.detail.idc, - }, { - label: '集群类型', - value: clusterTypeMap[orderInfo.detail.mode], - }, { - label: '应用ID', - value: orderInfo.detail.appId, - }, + label: '流入流量', + value: `${transBToMB(orderInfo.detail.bytesIn)} MB/s`, + }, + // { + // label: '数据中心', + // value: orderInfo.detail.idc, + // }, + { + label: '集群类型', + value: clusterTypeMap[orderInfo.detail.mode], + }, { + label: '应用ID', + value: orderInfo.detail.appId, + }, ]; const clusterOfflineList: ILabelValue[] = expansionList; const phyClusterOfflineList: ILabelValue[] = clusterTypelist; - const maxAvgBytesIn = orderInfo.detail.maxAvgBytesInList && orderInfo.detail.maxAvgBytesInList.map(item => { + const maxAvgBytesIn = orderInfo.detail.maxAvgBytesInList && orderInfo.detail.maxAvgBytesInList.map(item => { const val = `${transBToMB(item)} MB/s`; return val; }); @@ -125,18 +127,18 @@ export const getInfoRenderItem = (orderInfo: IOrderInfo, result: boolean) => { phyQuotaInfoList.splice(3, 0, ...clusterTypelist); const partitionList: ILabelValue[] = expansionList.concat([{ - label: 'Topic名称', - value: orderInfo.detail.topicName, - }, { - label: '申请分区数', - value: orderInfo.detail.needIncrPartitionNum, - }, { - label: '当前流入流量', - value: `${transBToMB(orderInfo.detail.bytesIn)} MB/s`, - }, { - label: '近三天峰值流入流量', - value: maxAvgBytesIn && maxAvgBytesIn.join('、'), - }, + label: 'Topic名称', + value: orderInfo.detail.topicName, + }, { + label: '申请分区数', + value: orderInfo.detail.needIncrPartitionNum, + }, { + label: '当前流入流量', + value: `${transBToMB(orderInfo.detail.bytesIn)} MB/s`, + }, { + label: '近三天峰值流入流量', + value: maxAvgBytesIn && maxAvgBytesIn.join('、'), + }, ]); const phyPartitionList: ILabelValue[] = partitionList.filter(i => !cluster.includes(i.label)); diff --git a/kafka-manager-console/src/container/user-center/order-detail.tsx b/kafka-manager-console/src/container/user-center/order-detail.tsx index 58abd8d0..30a3afd7 100644 --- a/kafka-manager-console/src/container/user-center/order-detail.tsx +++ b/kafka-manager-console/src/container/user-center/order-detail.tsx @@ -133,6 +133,12 @@ export class OrderDetail extends React.Component { width: '20%', render: (t: string) => {t === 'consumer' ? '消费' : '生产'}, }, + // { + // title: '客户端语言', + // dataIndex: 'language', + // key: 'language', + // width: '20%', + // }, ]; return ( <> diff --git a/kafka-manager-console/src/container/wrapper/custom-modal.tsx b/kafka-manager-console/src/container/wrapper/custom-modal.tsx index f6745789..93cf0148 100644 --- a/kafka-manager-console/src/container/wrapper/custom-modal.tsx +++ b/kafka-manager-console/src/container/wrapper/custom-modal.tsx @@ -3,6 +3,7 @@ import { observer } from 'mobx-react'; import { modal } from 'store/modal'; import { ConnectTopicList } from '../modal/connect-topic-list'; import { ConnectAppList } from '../modal/offline-app-modal'; +import { ConnectAppNewList } from '../modal/offline-app-modal-new'; import { CancelTopicPermission } from 'container/modal/cancel-topic-permission'; import { OfflineClusterModal } from 'container/modal/offline-cluster-modal'; import { RenderOrderOpResult } from 'container/modal/order'; @@ -22,6 +23,7 @@ export default class AllCustomModalInOne extends React.Component { const modalMap = { offlineTopicModal: , + offlineAppNewModal: , offlineAppModal: , cancelTopicPermission: , offlineClusterModal: , diff --git a/kafka-manager-console/src/lib/api.ts b/kafka-manager-console/src/lib/api.ts index f53f6852..39bb63ff 100644 --- a/kafka-manager-console/src/lib/api.ts +++ b/kafka-manager-console/src/lib/api.ts @@ -1,5 +1,5 @@ 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 { IUploadFile, IUser, IQuotaModelItem, ILimitsItem, ITopic, IOrderParams, ISample, IMigration, IExecute, IEepand, IUtils, ITopicMetriceParams, IRegister, IEditTopic, IExpand, IDeleteTopic, INewRegions, INewLogical, IRebalance, INewBulidEnums, ITrigger, IApprovalOrder, IMonitorSilences, IConfigure, IConfigGateway, IBatchApproval } from 'types/base-type'; import { IRequestParams } from 'types/alarm'; import { apiCache } from 'lib/api-cache'; @@ -418,6 +418,13 @@ export const getMetaData = (needDetail: boolean = true) => { return fetch(`/rd/clusters/basic-info?need-detail=${needDetail}`); }; +export const getOperationRecordData = (params: any) => { + return fetch(`/rd/operate-record`,{ + method: 'POST', + body: JSON.stringify(params), + }); +}; + export const getConfigure = () => { return fetch(`/rd/configs`); }; @@ -442,6 +449,34 @@ export const deleteConfigure = (configKey: string) => { }); }; +export const getGatewayList = () => { + return fetch(`/rd/gateway-configs`); +}; + +export const getGatewayType = () => { + return fetch(`/op/gateway-configs/type-enums`); +}; + +export const addNewConfigGateway = (params: IConfigGateway) => { + return fetch(`/op/gateway-configs`, { + method: 'POST', + body: JSON.stringify(params), + }); +}; + +export const editConfigGateway = (params: IConfigGateway) => { + return fetch(`/op/gateway-configs`, { + method: 'PUT', + body: JSON.stringify(params), + }); +}; +export const deleteConfigGateway = (params: IConfigure) => { + return fetch(`/op/gateway-configs`, { + method: 'DELETE', + body: JSON.stringify(params), + }); +}; + export const getDataCenter = () => { return fetch(`/normal/configs/idc`); }; @@ -530,6 +565,23 @@ export const getControllerHistory = (clusterId: number) => { return fetch(`/rd/clusters/${clusterId}/controller-history`); }; +export const getCandidateController = (clusterId: number) => { + return fetch(`/rd/clusters/${clusterId}/controller-preferred-candidates`); +}; + +export const addCandidateController = (params:any) => { + return fetch(`/op/cluster-controller/preferred-candidates`, { + method: 'POST', + body: JSON.stringify(params), + }); +}; + +export const deleteCandidateCancel = (params:any)=>{ + return fetch(`/op/cluster-controller/preferred-candidates`, { + method: 'DELETE', + body: JSON.stringify(params), + }); +} /** * 运维管控 broker */ diff --git a/kafka-manager-console/src/lib/line-charts-config.ts b/kafka-manager-console/src/lib/line-charts-config.ts index fe9880a6..4a667c0c 100644 --- a/kafka-manager-console/src/lib/line-charts-config.ts +++ b/kafka-manager-console/src/lib/line-charts-config.ts @@ -77,7 +77,7 @@ export const getControlMetricOption = (type: IOptionType, data: IClusterMetrics[ name = '条'; data.map(item => { item.messagesInPerSec = item.messagesInPerSec !== null ? Number(item.messagesInPerSec.toFixed(2)) : null; - }); + }); break; case 'brokerNum': case 'topicNum': @@ -224,7 +224,7 @@ export const getClusterMetricOption = (type: IOptionType, record: IClusterMetric name = '条'; data.map(item => { item.messagesInPerSec = item.messagesInPerSec !== null ? Number(item.messagesInPerSec.toFixed(2)) : null; - }); + }); break; default: const { name: unitName, data: xData } = dealFlowData(metricTypeMap[type], data); @@ -248,8 +248,8 @@ export const getClusterMetricOption = (type: IOptionType, record: IClusterMetric const unitSeries = item.data[item.seriesName] !== null ? Number(item.data[item.seriesName]) : null; // tslint:disable-next-line:max-line-length result += ''; - if ( (item.data.produceThrottled && item.seriesName === 'appIdBytesInPerSec') - || (item.data.consumeThrottled && item.seriesName === 'appIdBytesOutPerSec') ) { + if ((item.data.produceThrottled && item.seriesName === 'appIdBytesInPerSec') + || (item.data.consumeThrottled && item.seriesName === 'appIdBytesOutPerSec')) { return result += item.seriesName + ': ' + unitSeries + '(被限流)' + '
    '; } return result += item.seriesName + ': ' + unitSeries + '
    '; @@ -317,7 +317,7 @@ export const getMonitorMetricOption = (seriesName: string, data: IMetricPoint[]) if (ele.name === item.seriesName) { // tslint:disable-next-line:max-line-length result += ''; - return result += item.seriesName + ': ' + (item.data.value === null ? '' : item.data.value.toFixed(2)) + '
    '; + return result += item.seriesName + ': ' + (item.data.value === null ? '' : item.data.value.toFixed(2)) + '
    '; } }); }); diff --git a/kafka-manager-console/src/routers/page/admin.tsx b/kafka-manager-console/src/routers/page/admin.tsx index 95159027..1a6f19ff 100644 --- a/kafka-manager-console/src/routers/page/admin.tsx +++ b/kafka-manager-console/src/routers/page/admin.tsx @@ -3,7 +3,7 @@ import CommonRoutePage from './common'; import urlParser from 'lib/url-parser'; import urlQuery from 'store/url-query'; import { AppDetail } from 'container/app'; -import { AdminAppList, ClusterList, ClusterDetail, BrokerDetail, UserManagement, VersionManagement, OperationManagement, OperationDetail, BillManagement, ConfigureManagement, IndividualBill, MigrationDetail, BillDetail } from 'container/admin'; +import { AdminAppList, ClusterList, ClusterDetail, BrokerDetail, UserManagement, VersionManagement, OperationManagement, OperationDetail, BillManagement, ConfigureManagement, IndividualBill, MigrationDetail, BillDetail, OperationRecord } from 'container/admin'; import { PlatformManagement } from 'container/admin/platform-management'; export default class Home extends React.Component { @@ -52,7 +52,11 @@ export default class Home extends React.Component { path: '/admin/migration-detail', exact: true, component: MigrationDetail, - }]; + }, { + path: '/admin/operation-record', + exact: true, + component: OperationRecord, + },]; constructor(props: any) { super(props); @@ -66,7 +70,7 @@ export default class Home extends React.Component { public render() { return ( - + ); } } diff --git a/kafka-manager-console/src/store/admin-monitor.ts b/kafka-manager-console/src/store/admin-monitor.ts index 4071e1c5..7e257637 100644 --- a/kafka-manager-console/src/store/admin-monitor.ts +++ b/kafka-manager-console/src/store/admin-monitor.ts @@ -3,6 +3,11 @@ import { observable, action } from 'mobx'; import { getBrokersMetricsHistory } from 'lib/api'; import { IClusterMetrics } from 'types/base-type'; +const STATUS = { + PENDING: 'pending', + REJECT: 'reject', + FULLFILLED: 'fullfilled' +} class AdminMonitor { @observable public currentClusterId = null as number; @@ -33,33 +38,42 @@ class AdminMonitor { @action.bound public setBrokersChartsData(data: IClusterMetrics[]) { this.brokersMetricsHistory = data; - this.setRequestId(null); + this.setRequestId(STATUS.FULLFILLED); + Promise.all(this.taskQueue).then(() => { + this.setRequestId(null); + this.taskQueue = []; + }) return data; } + public taskQueue = [] as any[]; public getBrokersMetricsList = async (startTime: string, endTime: string) => { - if (this.requestId && this.requestId !== 'error') { - return new Promise((res, rej) => { - window.setTimeout(() => { - if (this.requestId === 'error') { - rej(); - } else { + if (this.requestId) { + //逐条定时查询任务状态 + const p = new Promise((res, rej) => { + const timer = window.setInterval(() => { + if (this.requestId === STATUS.REJECT) { + rej(this.brokersMetricsHistory); + window.clearInterval(timer); + } else if (this.requestId === STATUS.FULLFILLED) { res(this.brokersMetricsHistory); + window.clearInterval(timer); } - }, 800); // TODO: 该实现方式待优化 + }, (this.taskQueue.length + 1) * 100); }); + this.taskQueue.push(p); + return p; } - this.setRequestId('requesting'); + this.setRequestId(STATUS.PENDING); return getBrokersMetricsHistory(this.currentClusterId, this.currentBrokerId, startTime, endTime) - .then(this.setBrokersChartsData).catch(() => this.setRequestId('error')); + .then(this.setBrokersChartsData).catch(() => this.setRequestId(STATUS.REJECT)); } public getBrokersChartsData = async (startTime: string, endTime: string, reload?: boolean) => { if (this.brokersMetricsHistory && !reload) { return new Promise(res => res(this.brokersMetricsHistory)); } - return this.getBrokersMetricsList(startTime, endTime); } } diff --git a/kafka-manager-console/src/store/admin.ts b/kafka-manager-console/src/store/admin.ts index f3d08264..582950a3 100644 --- a/kafka-manager-console/src/store/admin.ts +++ b/kafka-manager-console/src/store/admin.ts @@ -1,5 +1,5 @@ import { observable, action } from 'mobx'; -import { INewBulidEnums, ILabelValue, IClusterReal, IOptionType, IClusterMetrics, IClusterTopics, IKafkaFiles, IMetaData, IConfigure, IBrokerData, IOffset, IController, IBrokersBasicInfo, IBrokersStatus, IBrokersTopics, IBrokersPartitions, IBrokersAnalysis, IAnalysisTopicVO, IBrokersMetadata, IBrokersRegions, IThrottles, ILogicalCluster, INewRegions, INewLogical, ITaskManage, IPartitionsLocation, ITaskType, ITasksEnums, ITasksMetaData, ITaskStatusDetails, IKafkaRoles, IEnumsMap, IStaffSummary, IBill, IBillDetail } from 'types/base-type'; +import { INewBulidEnums, ILabelValue, IClusterReal, IOptionType, IClusterMetrics, IClusterTopics, IKafkaFiles, IMetaData, IConfigure, IConfigGateway, IBrokerData, IOffset, IController, IBrokersBasicInfo, IBrokersStatus, IBrokersTopics, IBrokersPartitions, IBrokersAnalysis, IAnalysisTopicVO, IBrokersMetadata, IBrokersRegions, IThrottles, ILogicalCluster, INewRegions, INewLogical, ITaskManage, IPartitionsLocation, ITaskType, ITasksEnums, ITasksMetaData, ITaskStatusDetails, IKafkaRoles, IEnumsMap, IStaffSummary, IBill, IBillDetail } from 'types/base-type'; import { deleteCluster, getBasicInfo, @@ -9,10 +9,16 @@ import { getTopicsBasicInfo, getTasksKafkaFiles, getMetaData, + getOperationRecordData, getConfigure, addNewConfigure, editConfigure, + addNewConfigGateway, deleteConfigure, + getGatewayList, + getGatewayType, + editConfigGateway, + deleteConfigGateway, getDataCenter, getClusterBroker, getClusterConsumer, @@ -49,6 +55,9 @@ import { getStaffSummary, getBillStaffSummary, getBillStaffDetail, + getCandidateController, + addCandidateController, + deleteCandidateCancel } from 'lib/api'; import { getControlMetricOption, getClusterMetricOption } from 'lib/line-charts-config'; @@ -59,6 +68,7 @@ import { transBToMB } from 'lib/utils'; import moment from 'moment'; import { timestore } from './time'; +import { message } from 'component/antd'; class Admin { @observable @@ -94,9 +104,23 @@ class Admin { @observable public metaList: IMetaData[] = []; + @observable + public oRList: any[] = []; + + @observable + public oRparams:any={ + moduleId:0 + }; + @observable public configureList: IConfigure[] = []; + @observable + public configGatewayList: IConfigGateway[] = []; + + @observable + public gatewayType: []; + @observable public dataCenterList: string[] = []; @@ -142,6 +166,12 @@ class Admin { @observable public controllerHistory: IController[] = []; + @observable + public controllerCandidate: IController[] = []; + + @observable + public filtercontrollerCandidate: string = ''; + @observable public brokersPartitions: IBrokersPartitions[] = []; @@ -152,7 +182,7 @@ class Admin { public brokersAnalysisTopic: IAnalysisTopicVO[] = []; @observable - public brokersMetadata: IBrokersMetadata[] = []; + public brokersMetadata: IBrokersMetadata[] | any = []; @observable public brokersRegions: IBrokersRegions[] = []; @@ -206,10 +236,10 @@ class Admin { public kafkaRoles: IKafkaRoles[]; @observable - public controlType: IOptionType = 'byteIn/byteOut' ; + public controlType: IOptionType = 'byteIn/byteOut'; @observable - public type: IOptionType = 'byteIn/byteOut' ; + public type: IOptionType = 'byteIn/byteOut'; @observable public currentClusterId = null as number; @@ -241,7 +271,7 @@ class Admin { @action.bound public setClusterRealTime(data: IClusterReal) { - this.clusterRealData = data; + this.clusterRealData = data; this.getRealClusterLoading(false); } @@ -284,7 +314,7 @@ class Admin { return { ...item, label: item.fileName, - value: item.fileName + ',' + item.fileMd5, + value: item.fileName + ',' + item.fileMd5, }; })); } @@ -298,6 +328,15 @@ class Admin { }) : []; } + @action.bound + public setOperationRecordList(data:any){ + this.setLoading(false); + this.oRList = data ? data.map((item:any, index: any) => { + item.key = index; + return item; + }) : []; + } + @action.bound public setConfigure(data: IConfigure[]) { this.configureList = data ? data.map((item, index) => { @@ -306,6 +345,20 @@ class Admin { }) : []; } + @action.bound + public setConfigGatewayList(data: IConfigGateway[]) { + this.configGatewayList = data ? data.map((item, index) => { + item.key = index; + return item; + }) : []; + } + + @action.bound + public setConfigGatewayType(data: any) { + this.setLoading(false); + this.gatewayType = data || []; + } + @action.bound public setDataCenter(data: string[]) { this.dataCenterList = data || []; @@ -335,6 +388,17 @@ class Admin { }) : []; } + @action.bound + public setCandidateController(data: IController[]) { + this.controllerCandidate = data ? data.map((item, index) => { + item.key = index; + return item; + }) : []; + this.filtercontrollerCandidate = data?data.map((item,index)=>{ + return item.brokerId + }).join(','):'' + } + @action.bound public setBrokersBasicInfo(data: IBrokersBasicInfo) { this.brokersBasicInfo = data; @@ -356,10 +420,10 @@ class Admin { this.replicaStatus = data.brokerReplicaStatusList.slice(1); this.bytesInStatus.forEach((item, index) => { - this.peakValueList.push({ name: peakValueMap[index], value: item}); + this.peakValueList.push({ name: peakValueMap[index], value: item }); }); this.replicaStatus.forEach((item, index) => { - this.copyValueList.push({name: copyValueMap[index], value: item}); + this.copyValueList.push({ name: copyValueMap[index], value: item }); }); } @@ -415,16 +479,16 @@ class Admin { } @action.bound - public setBrokersMetadata(data: IBrokersMetadata[]) { - this.brokersMetadata = data ? data.map((item, index) => { - item.key = index; - return { - ...item, - text: `${item.host} (BrokerID:${item.brokerId})`, - label: item.host, - value: item.brokerId, - }; - }) : []; + public setBrokersMetadata(data: IBrokersMetadata[]|any) { + this.brokersMetadata = data ? data.map((item:any, index:any) => { + item.key = index; + return { + ...item, + text: `${item.host} (BrokerID:${item.brokerId})`, + label: item.host, + value: item.brokerId, + }; + }) : []; } @action.bound @@ -461,9 +525,9 @@ class Admin { @action.bound public setLogicalClusters(data: ILogicalCluster[]) { this.logicalClusters = data ? data.map((item, index) => { - item.key = index; - return item; - }) : []; + item.key = index; + return item; + }) : []; } @action.bound @@ -474,25 +538,25 @@ class Admin { @action.bound public setClustersThrottles(data: IThrottles[]) { this.clustersThrottles = data ? data.map((item, index) => { - item.key = index; - return item; - }) : []; + item.key = index; + return item; + }) : []; } @action.bound public setPartitionsLocation(data: IPartitionsLocation[]) { this.partitionsLocation = data ? data.map((item, index) => { - item.key = index; - return item; - }) : []; + item.key = index; + return item; + }) : []; } @action.bound public setTaskManagement(data: ITaskManage[]) { this.taskManagement = data ? data.map((item, index) => { - item.key = index; - return item; - }) : []; + item.key = index; + return item; + }) : []; } @action.bound @@ -568,7 +632,7 @@ class Admin { return deleteCluster(clusterId).then(() => this.getMetaData(true)); } - public getPeakFlowChartData(value: ILabelValue[], map: string []) { + public getPeakFlowChartData(value: ILabelValue[], map: string[]) { return getPieChartOption(value, map); } @@ -611,6 +675,12 @@ class Admin { getMetaData(needDetail).then(this.setMetaList); } + public getOperationRecordData(params: any) { + this.setLoading(true); + this.oRparams = params + getOperationRecordData(params).then(this.setOperationRecordList); + } + public getConfigure() { getConfigure().then(this.setConfigure); } @@ -627,6 +697,30 @@ class Admin { deleteConfigure(configKey).then(() => this.getConfigure()); } + public getGatewayList() { + getGatewayList().then(this.setConfigGatewayList); + } + + public getGatewayType() { + this.setLoading(true); + getGatewayType().then(this.setConfigGatewayType); + } + + public addNewConfigGateway(params: IConfigGateway) { + return addNewConfigGateway(params).then(() => this.getGatewayList()); + } + + public editConfigGateway(params: IConfigGateway) { + return editConfigGateway(params).then(() => this.getGatewayList()); + } + + public deleteConfigGateway(params: any) { + deleteConfigGateway(params).then(() => { + // message.success('删除成功') + this.getGatewayList() + }); + } + public getDataCenter() { getDataCenter().then(this.setDataCenter); } @@ -643,6 +737,20 @@ class Admin { return getControllerHistory(clusterId).then(this.setControllerHistory); } + public getCandidateController(clusterId: number) { + return getCandidateController(clusterId).then(data=>{ + return this.setCandidateController(data) + }); + } + + public addCandidateController(clusterId: number, brokerIdList: any) { + return addCandidateController({clusterId, brokerIdList}).then(()=>this.getCandidateController(clusterId)); + } + + public deleteCandidateCancel(clusterId: number, brokerIdList: any){ + return deleteCandidateCancel({clusterId, brokerIdList}).then(()=>this.getCandidateController(clusterId)); + } + public getBrokersBasicInfo(clusterId: number, brokerId: number) { return getBrokersBasicInfo(clusterId, brokerId).then(this.setBrokersBasicInfo); } diff --git a/kafka-manager-console/src/store/alarm.ts b/kafka-manager-console/src/store/alarm.ts index b3e004df..e57631f0 100644 --- a/kafka-manager-console/src/store/alarm.ts +++ b/kafka-manager-console/src/store/alarm.ts @@ -181,6 +181,7 @@ class Alarm { public modifyMonitorStrategy(params: IRequestParams) { return modifyMonitorStrategy(params).then(() => { message.success('操作成功'); + window.location.href = `${urlPrefix}/alarm`; }).finally(() => this.setLoading(false)); } diff --git a/kafka-manager-console/src/store/modal.ts b/kafka-manager-console/src/store/modal.ts index 5cb6b37f..cc9bcf39 100644 --- a/kafka-manager-console/src/store/modal.ts +++ b/kafka-manager-console/src/store/modal.ts @@ -31,6 +31,12 @@ class CustomModal { this.params = value; } + @action.bound + public showOfflineAppNewModal(value: any) { + this.modalId = 'offlineAppNewModal'; + this.params = value; + } + @action.bound public showOrderOpResult() { this.modalId = 'orderOpResult'; diff --git a/kafka-manager-console/src/store/users.ts b/kafka-manager-console/src/store/users.ts index 8d53114e..249a0187 100644 --- a/kafka-manager-console/src/store/users.ts +++ b/kafka-manager-console/src/store/users.ts @@ -19,6 +19,9 @@ export class Users { @observable public staff: IStaff[] = []; + @observable + public newPassWord: any = null; + @action.bound public setAccount(data: IUser) { setCookie([{ key: 'role', value: `${data.role}`, time: 1 }]); @@ -42,6 +45,11 @@ export class Users { this.loading = value; } + @action.bound + public setNewPassWord(value: boolean) { + this.newPassWord = value; + } + public getAccount() { getAccount().then(this.setAccount); } diff --git a/kafka-manager-console/src/styles/font.css b/kafka-manager-console/src/styles/font.css index a4b2b2f3..57f6707e 100644 --- a/kafka-manager-console/src/styles/font.css +++ b/kafka-manager-console/src/styles/font.css @@ -6,6 +6,16 @@ url('//at.alicdn.com/t/font_1251424_q66z80q0hio.ttf?t=1577526422376') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ url('//at.alicdn.com/t/font_1251424_q66z80q0hio.svg?t=1577526422376#kafka-manager') format('svg'); /* iOS 4.1- */ } +@font-face { + font-family: 'kafka-manager'; + /* project id 2406313 */ + src: url('//at.alicdn.com/t/font_2406313_rbsze6uqtta.eot'); + src: url('//at.alicdn.com/t/font_2406313_rbsze6uqtta.eot?#iefix') format('embedded-opentype'), + url('//at.alicdn.com/t/font_2406313_rbsze6uqtta.woff2') format('woff2'), + url('//at.alicdn.com/t/font_2406313_rbsze6uqtta.woff') format('woff'), + url('//at.alicdn.com/t/font_2406313_rbsze6uqtta.ttf') format('truetype'), + url('//at.alicdn.com/t/font_2406313_rbsze6uqtta.svg#iconfont') format('svg'); +} .kafka-manager { font-family: "kafka-manager" !important; @@ -15,6 +25,15 @@ -moz-osx-font-smoothing: grayscale; } +/* .kafka-manager-record { + font-family: "kafka-manager" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} */ + + .k-icon-fuwurenwuguanli:before { content: "\e660"; } @@ -47,6 +66,10 @@ content: "\e634"; } +.k-icon-operationrecord:before { + content: "\e772"; +} + .k-icon-menu2:before { content: "\e609"; } diff --git a/kafka-manager-console/src/types/base-type.ts b/kafka-manager-console/src/types/base-type.ts index 1170a696..f0858c3a 100644 --- a/kafka-manager-console/src/types/base-type.ts +++ b/kafka-manager-console/src/types/base-type.ts @@ -190,6 +190,7 @@ export interface IUser { chineseName?: string; department?: string; key?: number; + confirmPassword?: string } export interface IOffset { @@ -486,6 +487,17 @@ export interface IConfigure { key?: number; } +export interface IConfigGateway { + id: number; + key?: number; + modifyTime: number; + name: string; + value: string; + version: string; + type: string; + description: string; +} + export interface IEepand { brokerIdList: number[]; clusterId: number; @@ -650,8 +662,10 @@ export interface IBrokerData { export interface IController { brokerId: number; host: string; - timestamp: number; - version: number; + timestamp?: number; + version?: number; + startTime?: number; + status?: number; key?: number; } @@ -925,7 +939,7 @@ export interface INewLogical { logicalClusterName?: string; logicalClusterNameCn?: string; regionIdList: number[]; - logicalClusterIdentification?:string + logicalClusterIdentification?: string } export interface IPartitionsLocation {