前端调整

This commit is contained in:
zengqiao
2022-08-25 20:32:49 +08:00
parent 5262ae8907
commit 18832dc448
31 changed files with 369 additions and 405 deletions

View File

@@ -29,5 +29,6 @@ module.exports = {
'@typescript-eslint/no-var-requires': 0,
'prettier/prettier': 2, // 这项配置 对于不符合prettier规范的写法eslint会提示报错
'no-console': 1,
'react/display-name': 0,
},
};

View File

@@ -1,19 +1,43 @@
## 安装项目依赖
* 安装lerna
- 安装 lerna
```
npm i -g lerna
npm install -g lerna
```
- 安装项目依赖
```
npm run i
```
## 启动项目
```
npm run start
```
### 环境信息
http://localhost:port
## 构建项目
```
npm run build
```
## 目录结构
- packages
- layout-clusters-fe: 基座应用 & 多集群管理
- config-manager-fe: 子应用 - 系统管理
- tool: 启动 & 打包脚本
- ...
## 常见问题
Q: 执行 `npm run start` 时看不到应用构建和热加载过程?
A: 需要到具体的应用中执行 `npm run start`,例如 `cd packages/layout-clusters-fe` 后,执行 `npm run start`

View File

@@ -1,11 +1,40 @@
# `logi-fe`
## 使用说明
> TODO: description
### 依赖安装:
## Usage
```
npm install
```
### 启动
* npm i @didi/d1-cli -g
* d1 start
### 启动
### 常见问题
```
npm run start
```
### 构建:
```
npm run build
```
构建后的代码默认会存放到项目根路径下 `km-rest/src/main/resources/templates/config` 文件夹里
## 目录结构
- config: 开发 & 构建配置
- theme.js: antd 主题配置
- d1-webpack.base.js: webpack 基础配置
- src源代码所在目录
- @types: TypeScript 全局类型声明
- api: 请求定义
- assets全局资源 img、css
- components公共组件
- constants: 全局配置、通用方法
- locales: 国际化语言
- pages: 路由匹配的页面组件
- app.tsx: 菜单、路由配置组件
- index.html: 单页
- index.tsx: 入口文件
- tsconfig.json: TypeScript 配置
- webpack.config.js: webpack 配置入口

View File

@@ -1,37 +0,0 @@
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const StatsPlugin = require('stats-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const HappyPack = require('happypack');
const os = require('os');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
var cwd = process.cwd();
const path = require('path');
const isProd = process.env.NODE_ENV === 'production';
const babelOptions = {
cacheDirectory: true,
babelrc: false,
presets: [require.resolve('@babel/preset-env'), require.resolve('@babel/preset-typescript'), require.resolve('@babel/preset-react')],
plugins: [
new HtmlWebpackPlugin({
meta: {
manifest: 'manifest.json',
},
template: './src/index.html',
inject: 'body',
}),
],
output: {
library: pkgJson.ident,
libraryTarget: 'amd',
},
entry: {
[pkgJson.ident]: ['./src/index.tsx'],
},
};

View File

@@ -1,7 +0,0 @@
export const defaultPagination = {
current: 1,
pageSize: 10,
position: 'bottomRight',
showSizeChanger: true,
pageSizeOptions: ['10', '20', '50', '100'],
};

View File

@@ -376,7 +376,8 @@ export default (props: { curTabKey: string }): JSX.Element => {
const [pagination, setPagination] = useState<any>(defaultPagination);
const [loading, setLoading] = useState<boolean>(true);
const [deleteBtnLoading, setDeleteBtnLoading] = useState<number>(-1);
const [form] = Form.useForm();
const [searchKeywords, setSearchKeywords] = useState('');
const [searchKeywordsInput, setSearchKeywordsInput] = useState('');
const detailRef = useRef(null);
const assignRolesRef = useRef(null);
@@ -472,11 +473,10 @@ export default (props: { curTabKey: string }): JSX.Element => {
];
const getRoleList = (query = {}) => {
const formData = form.getFieldsValue();
const data = {
page: pagination.current,
size: pagination.pageSize,
...formData,
roleName: searchKeywords,
...query,
};
@@ -582,19 +582,33 @@ export default (props: { curTabKey: string }): JSX.Element => {
}
}, [curTabKey]);
useEffect(() => {
(searchKeywords || searchKeywords === '') && getRoleList({ pageNo: 1 });
}, [searchKeywords]);
return (
<>
<div className="operate-bar">
<Form form={form} layout="inline" onFinish={() => getRoleList({ page: 1 })}>
<Form.Item name="roleName">
<Input placeholder="请输入角色名称" />
</Form.Item>
<Form.Item>
<Button type="primary" ghost htmlType="submit">
</Button>
</Form.Item>
</Form>
<div className="operate-bar-right">
<Input
className="search-input"
suffix={
<IconFont
type="icon-fangdajing"
onClick={(_) => {
setSearchKeywords(searchKeywordsInput);
}}
style={{ fontSize: '16px' }}
/>
}
placeholder="请输入角色名称"
value={searchKeywordsInput}
onPressEnter={(_) => {
setSearchKeywords(searchKeywordsInput);
}}
onChange={(e) => {
setSearchKeywordsInput(e.target.value);
}}
/>
{global.hasPermission && global.hasPermission(ConfigPermissionMap.ROLE_ADD) ? (
<Button
type="primary"

View File

@@ -44,3 +44,13 @@
.role-tab-assign-user .desc-row {
margin-bottom: 24px;
}
.operate-bar-right {
display: flex;
justify-content: right;
margin-bottom: 12px;
.search-input {
width: 248px;
margin-right: 8px;
}
}

View File

@@ -1,32 +1,45 @@
## Usage
## 使用说明
### 依赖安装:
```
npm install
```
### 启动:
* 招行环境执行 npm start
* 内部环境执行 npm run start:inner
```
npm run start
```
### 构建:
* 招行环境执行 npm build
* 内部环境执行 npm run build:inner
构建后的代码默认会存放到 `../pub` 文件夹里
```
npm run build
```
### 部署
* 内部环境代码提交主干后会自动触发打包部署至http://10.190.14.125:8016
构建后的代码默认会存放到项目根路径下 `km-rest/src/main/resources/templates/layout` 文件夹里
## 目录结构
- config: 开发 & 构建配置
- theme.jsantd 主题配置
- webpack.dev.config.jswebpack 开发环境补充配置,覆盖默认配置
- webpack.build.config.jswebpack 构建补充配置,覆盖默认配置
- webpackConfigResolveAlias.js 文件路径别名配置
- theme.js: antd 主题配置
- registerApps.js: SPA 注册
- systemsConfig.js: 子应用配置
- d1-webpack.base.js: webpack 基础配置
- CoverHtmlWebpackPlugin.js: 输出 html 内容
- CountComponentWebpackPlugin.js: 计算 knowdesign 组件引用次数
- webpackConfigResolveAlias.js: 文件路径别名配置
- src源代码所在目录
- @types: TypeScript 全局类型声明
- api: 请求定义
- assets全局资源 img、css
- common: 全局配置、通用方法
- components公共组件
- pages路由匹配的页面组件
- app.jsx 菜单、路由配置组件
- index.html单页
- index.jsx入口文
- fetk.config.js 开发工具配置页面
- constants: 全局配置、通用方法
- locales: 国际化语言
- pages: 路由匹配的页面组件
- app.tsx: 菜单、路由配置组
- index.html: 单页
- index.tsx: 入口文件
- tsconfig.json: TypeScript 配置
- webpack.config.js: webpack 配置入口

View File

@@ -1,35 +0,0 @@
function getApi(path: string) {
const prefix = '/api/uic';
return `${prefix}${path}`;
}
function getOrderApi(path: string) {
const prefix = '/api/ticket';
return `${prefix}${path}`;
}
const api = {
login: getApi('/auth/login'),
logout: getApi('/auth/logout'),
selftProfile: getApi('/self/profile'),
selftPassword: getApi('/self/password'),
selftToken: getApi('/self/token'),
user: getApi('/user'),
tenant: getApi('/tenant'),
team: getApi('/team'),
configs: getApi('/configs'),
role: getApi('/role'),
ops: getApi('/ops'),
log: getApi('/log'),
homeStatistics: getApi('/home/statistics'),
project: getApi('/project'),
projects: getApi('/projects'),
queues: getOrderApi('/queues'),
tickets: getOrderApi('/tickets'),
template: getOrderApi('/templates'),
upload: getOrderApi('/file/upload'),
task: '/api/job-ce/task',
};
export default api;

View File

@@ -1,4 +0,0 @@
export const appname = 'ecmc';
export const prefixCls = appname;
export const loginPath = `/login`;
export const defaultPageSizeOptions = ['10', '30', '50', '100', '300', '500', '1000'];

View File

@@ -3,8 +3,9 @@ import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import CardBar from '@src/components/CardBar';
import { healthDataProps } from '.';
import { Tag, Utils } from 'knowdesign';
import { Tag, Tooltip, Utils } from 'knowdesign';
import api from '@src/api';
import { QuestionCircleOutlined } from '@ant-design/icons';
export default () => {
const routeParams = useParams<{
@@ -85,7 +86,16 @@ export default () => {
},
},
{
title: 'Similar Config',
title() {
return (
<div>
<span style={{ display: 'inline-block', marginRight: '8px' }}>Similar Config</span>
<Tooltip overlayClassName="rebalance-tooltip" title="所有broker配置是否一致">
<QuestionCircleOutlined />
</Tooltip>
</div>
);
},
value: () => {
return (
<>

View File

@@ -24,7 +24,7 @@ import './index.less';
import Api, { MetricType } from '@src/api/index';
import moment from 'moment';
import PreviewTaskPlan from './PreviewTaskPlan';
import { timeFormat } from '@src/lib/utils';
import { timeFormater } from '@src/constants/common';
import type { RangePickerProps } from 'knowdesign/es/basic/date-picker';
const { TextArea } = Input;
@@ -135,7 +135,7 @@ export default (props: DefaultConfig) => {
title: '数据保存时间',
dataIndex: 'retentionMs',
render: (v: any) => {
return timeFormat(v);
return timeFormater(v);
},
},
{
@@ -154,7 +154,6 @@ export default (props: DefaultConfig) => {
setMoveDataTimeRanges(moveDataTimeRangesCopy);
}}
formatter={(value) => (value ? `${value} h` : '')}
// @ts-ignore
parser={(value) => value.replace('h', '')}
></InputNumber>
);

View File

@@ -1,3 +1,7 @@
interface IMap {
[key: string]: string;
}
export const defaultPagination = {
current: 1,
pageSize: 10,
@@ -122,10 +126,37 @@ export const hashDataParse = (hash: string) => {
return newHashData;
};
const BUSINESS_VERSION = process.env.BUSINESS_VERSION;
export const urlParser = () => {
const Url = {
hash: {} as IMap,
search: {} as IMap,
} as {
hash: IMap;
search: IMap;
[key: string]: IMap;
};
window.location.hash
.slice(1)
.split('&')
.forEach((str) => {
const kv = str.split('=');
Url.hash[kv[0]] = kv[1];
});
window.location.search
.slice(1)
.split('&')
.forEach((str) => {
const kv = str.split('=');
Url.search[kv[0]] = kv[1];
});
return Url;
};
export const getLicenseInfo = (cbk: (msg: string) => void) => {
if (BUSINESS_VERSION) {
if (process.env.BUSINESS_VERSION) {
const info = (window as any).code;
if (!info) {
setTimeout(() => getLicenseInfo(cbk), 1000);
@@ -137,3 +168,96 @@ export const getLicenseInfo = (cbk: (msg: string) => void) => {
}
}
};
export const getRandomStr = (length?: number) => {
const NUM_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const LOW_LETTERS_LIST = [
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
'x',
'y',
'z',
];
const CAP_LETTERS_LIST = LOW_LETTERS_LIST.map((v) => v.toUpperCase());
const SPECIAL_LIST = [
'!',
'"',
'#',
'$',
'%',
'&',
"'",
'(',
')',
'*',
'+',
'-',
'.',
'/',
':',
';',
'<',
'=',
'>',
'?',
'@',
'[',
'\\',
']',
'^',
'_',
'`',
'{',
'|',
'}',
'~',
];
const ALL_LIST = [...NUM_list, ...LOW_LETTERS_LIST, ...CAP_LETTERS_LIST];
const randomNum = (Math.random() * 128) | 0;
const randomKeys = new Array(length ?? randomNum).fill('');
for (let i = 0; i < randomKeys.length; i++) {
// ALL_LIST 随机字符
const index = (Math.random() * ALL_LIST.length) | 0;
randomKeys[i] = ALL_LIST[index - 1];
}
return randomKeys.join('');
};
export const timeFormater = function formatDuring(mss: number) {
const days = Math.floor(mss / (1000 * 60 * 60 * 24));
const hours = Math.floor((mss % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((mss % (1000 * 60 * 60)) / (1000 * 60));
const seconds = (mss % (1000 * 60)) / 1000;
const parts = [
{ v: days, unit: '天' },
{ v: hours, unit: '小时' },
{ v: minutes, unit: '分钟' },
{ v: seconds, unit: '秒' },
];
return parts
.filter((o) => o.v > 0)
.map((o: any) => `${o.v}${o.unit}`)
.join();
};

View File

@@ -1,84 +0,0 @@
import React from 'react';
// TODO: 菜单配置接口有点乱,看是否可以归类下,以及是否可以去掉一些非必要的属性
export interface MenuConfItem {
key?: string;
name: string | React.ReactNode;
path: string;
icon?: string;
children?: MenuConfItem[];
visible?: boolean;
rootVisible?: boolean;
to?: string;
divider?: boolean;
target?: string;
getQuery?: (query: any) => any;
}
export interface TreeNode {
id: number;
pid: number;
name: string;
path: string;
type: number;
leaf: number;
children?: TreeNode[];
icon_color?: string;
icon_char?: string;
cate?: string;
note?: string;
selectable?: boolean;
}
export interface ResponseDat {
list: any[];
total: number;
}
export interface Response {
err: string;
dat: any | ResponseDat;
}
export interface UserProfile {
id: number;
username: string;
dispname: string;
email: string;
phone: string;
im: string;
isroot: boolean;
}
export interface Tenant {
id: number;
ident: string;
name: string;
note: string;
}
export interface Team {
id: number;
ident: string;
name: string;
note: string;
mgmt: number;
}
export interface Role {
id: number;
name: string;
note: string;
cate: 'global' | 'local';
operations: string[];
}
export interface Order {
id: number;
title: string;
levels: number;
cc: string;
content: string;
scheduleStartTime: string;
status: string;
creator: string;
}

View File

@@ -1,32 +0,0 @@
interface IMap {
[key: string]: string;
}
export default () => {
const Url = {
hash: {} as IMap,
search: {} as IMap,
} as {
hash: IMap;
search: IMap;
[key: string]: IMap;
};
window.location.hash
.slice(1)
.split('&')
.forEach((str) => {
const kv = str.split('=');
Url.hash[kv[0]] = kv[1];
});
window.location.search
.slice(1)
.split('&')
.forEach((str) => {
const kv = str.split('=');
Url.search[kv[0]] = kv[1];
});
return Url;
};

View File

@@ -1,88 +0,0 @@
export const getRandomStr = (length?: number) => {
const NUM_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const LOW_LETTERS_LIST = [
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
'x',
'y',
'z',
];
const CAP_LETTERS_LIST = LOW_LETTERS_LIST.map((v) => v.toUpperCase());
const SPECIAL_LIST = [
'!',
'"',
'#',
'$',
'%',
'&',
"'",
'(',
')',
'*',
'+',
'-',
'.',
'/',
':',
';',
'<',
'=',
'>',
'?',
'@',
'[',
'\\',
']',
'^',
'_',
'`',
'{',
'|',
'}',
'~',
];
const ALL_LIST = [...NUM_list, ...LOW_LETTERS_LIST, ...CAP_LETTERS_LIST];
const randomNum = (Math.random() * 128) | 0;
const randomKeys = new Array(length ?? randomNum).fill('');
for (let i = 0; i < randomKeys.length; i++) {
// ALL_LIST 随机字符
const index = (Math.random() * ALL_LIST.length) | 0;
randomKeys[i] = ALL_LIST[index - 1];
}
return randomKeys.join('');
};
export const timeFormat = function formatDuring(mss: number) {
var days = Math.floor(mss / (1000 * 60 * 60 * 24));
var hours = Math.floor((mss % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
var minutes = Math.floor((mss % (1000 * 60 * 60)) / (1000 * 60));
var seconds = (mss % (1000 * 60)) / 1000;
var parts = [
{ v: days, unit: "天" },
{ v: hours, unit: "小时" },
{ v: minutes, unit: "分钟" },
{ v: seconds, unit: "秒" },
]
return parts.filter(o => o.v > 0).map((o: any) => `${o.v}${o.unit}`).join();
}

View File

@@ -74,23 +74,23 @@ const BrokerDetail = (props: any) => {
useEffect(() => {
global?.clusterInfo?.id && hashDataParse(urlLocation.hash).brokerId
? Utils.request(Api.getBrokerMetadata(hashDataParse(urlLocation.hash).brokerId, global?.clusterInfo?.id), {
init: {
errorNoTips: true,
},
})
.then((brokerData: any) => {
if (brokerData?.exist && brokerData?.alive && hashDataParse(urlLocation.hash).host === brokerData.host) {
setHashData(brokerData);
setVisible(true);
} else {
init: {
errorNoTips: true,
},
})
.then((brokerData: any) => {
if (brokerData?.exist && brokerData?.alive) {
setHashData(brokerData);
setVisible(true);
} else {
history.replace(urlLocation.pathname);
setVisible(false);
}
})
.catch((err) => {
history.replace(urlLocation.pathname);
setVisible(false);
}
})
.catch((err) => {
history.replace(urlLocation.pathname);
setVisible(false);
})
})
: setVisible(false);
}, [hashDataParse(urlLocation.hash).brokerId, global?.clusterInfo, urlLocation]);

View File

@@ -114,7 +114,7 @@ export default (props: any) => {
key: 'memberId',
width: 200,
render: (v: string) => {
return <ContentWithCopy content={v} />;
return v ? <ContentWithCopy content={v} /> : '-';
},
},
{

View File

@@ -60,7 +60,10 @@ const AutoPage = (props: any) => {
setConsumersListLoading(true);
Utils.post(Api.getOperatingStateList(clusterPhyId), params).then((data: any) => {
setConsumersListLoading(false);
setConsumerGroupList(data?.bizData || []);
const newData = data?.bizData.map((item: any, key: number) => {
return { ...item, unique: key * pageIndex * pageSize + item?.groupName };
});
setConsumerGroupList(newData || []);
setPageIndex(data.pagination.pageNo);
setPageTotal(data.pagination.total);
setPageSize(data.pagination.pageSize);
@@ -230,7 +233,7 @@ const AutoPage = (props: any) => {
tableProps={{
loading: consumersListLoading,
showHeader: false,
rowKey: 'groupName',
rowKey: 'unique',
columns: columns(),
dataSource: consumerGroupList,
paginationProps:
@@ -250,7 +253,6 @@ const AutoPage = (props: any) => {
// },
onChange: onTableChange,
scroll: { y: 'calc(100vh - 400px)' },
// className: `frameless-table ${scene !== 'topicDetail' && 'clustom-table-content'}`, // 纯无边框表格类名
},
}}
/>

View File

@@ -1,9 +1,9 @@
import React, { useState, useEffect } from 'react';
import { Alert, Badge, Descriptions, Dropdown, ProTable, DTable, Table, Utils, Spin, Tag } from 'knowdesign';
import { Badge, Descriptions, ProTable, Utils, Spin, Tag } from 'knowdesign';
import { useParams } from 'react-router-dom';
import Api from '@src/api';
import { getNodeTrafficColumns } from './config';
import { getSizeAndUnit } from '@src/constants/common';
import TagsWithHide from '@src/components/TagsWithHide';
interface PropsType {
jobId?: any;
balanceData?: any;
@@ -54,7 +54,6 @@ const RebalancePlan = (props: PropsType) => {
// });
// setData(mockData);
setData(res || []);
setLoading(false);
})
@@ -221,9 +220,10 @@ const RebalancePlan = (props: PropsType) => {
<Spin spinning={loading}>
<Descriptions
style={{ fontSize: '13px' }}
column={2}
column={3}
labelStyle={{
display: 'flex',
textAlign: 'right',
justifyContent: 'end',
color: '#74788D',
fontSize: '13px',
@@ -231,11 +231,12 @@ const RebalancePlan = (props: PropsType) => {
contentStyle={{ fontSize: '13px' }}
>
<Descriptions.Item label="任务类型">{typeObj[data?.type] || '-'}</Descriptions.Item>
<Descriptions.Item label="总迁移大小">{Utils.getSizeAndUnit(data?.moveSize, 'B').valueWithUnit}</Descriptions.Item>
<Descriptions.Item label="Topic黑名单">
{data?.blackTopics && data?.blackTopics?.length > 0 ? data?.blackTopics.join('、') : '-'}
<Descriptions.Item labelStyle={{ width: '100px' }} label="总迁移大小">
{Utils.getSizeAndUnit(data?.moveSize, 'B').valueWithUnit}
</Descriptions.Item>
<Descriptions.Item labelStyle={{ width: '100px' }} label="迁移副本数">
{data?.replicas || '-'}
</Descriptions.Item>
<Descriptions.Item label="迁移副本数">{data?.replicas || '-'}</Descriptions.Item>
<Descriptions.Item label="均衡阈值">
{data?.clusterBalanceIntervalList
? data?.clusterBalanceIntervalList?.map((item: any) => {
@@ -247,6 +248,13 @@ const RebalancePlan = (props: PropsType) => {
})
: '-'}
</Descriptions.Item>
<Descriptions.Item labelStyle={{ width: '100px' }} label="Topic黑名单">
{data?.blackTopics && data?.blackTopics?.length > 0 ? (
<TagsWithHide placement="bottomLeft" list={data?.blackTopics} expandTagContent={(num: any) => `共有${num}`} />
) : (
'-'
)}
</Descriptions.Item>
</Descriptions>
</Spin>
<h3 style={{ fontSize: '16px' }}></h3>
@@ -271,7 +279,7 @@ const RebalancePlan = (props: PropsType) => {
</div>
{data?.reassignmentJson && (
<>
<h3 style={{ fontSize: '16px', marginTop: '22px' }}></h3>
<h3 style={{ fontSize: '16px' }}></h3>
<div>
<a href={`data:,${data?.reassignmentJson}`} rel="noopener" download="reassignment.json">
Reassignment json file

View File

@@ -1,18 +1,5 @@
import React, { useState, useEffect, useRef } from 'react';
import {
Utils,
Drawer,
Button,
Form,
Space,
Divider,
AppContainer,
Input,
Transfer,
message,
IconFont,
InputNumber,
} from 'knowdesign';
import { Utils, Drawer, Button, Form, Space, Divider, AppContainer, Input, Transfer, message, IconFont, InputNumber } from 'knowdesign';
import { CloseOutlined } from '@ant-design/icons';
import api from '../../api';
import './style/BalanceDrawer.less';
@@ -38,7 +25,7 @@ const ClusterNorms: React.FC<PropsType> = ({ onClose, visible, genData }) => {
// 周期均衡
form.validateFields().then((values) => {
const params = values?.brokers?.map((item: any) => {
const brokerId = nodeData?.filter((key) => key.brokerId === item && item)[0]?.brokerId;
const brokerId = nodeData?.filter((key) => key.brokerId === item)[0]?.brokerId;
const newValue = brokerId && { brokerId, cpu: values?.cpu, disk: values?.disk, flow: values?.flow };
return {
clusterId: global?.clusterInfo?.id + '',

View File

@@ -4,7 +4,7 @@ import { Button, Divider, Drawer, Form, Input, InputNumber, message, Radio, Sele
import * as React from 'react';
import { useIntl } from 'react-intl';
import api from '../../api';
import { regClusterName, regUsername } from '../../common/reg';
import { regClusterName, regUsername } from '../../constants/reg';
import { bootstrapServersErrCodes, jmxErrCodes, zkErrCodes } from './config';
import CodeMirrorFormItem from '@src/components/CodeMirrorFormItem';

View File

@@ -10,8 +10,12 @@
border-top-right-radius: 12px;
.operate-bar {
display: flex;
justify-content: space-between;
justify-content: right;
margin-bottom: 12px;
.search-input {
width: 248px;
margin-right: 8px;
}
}
}
}

View File

@@ -21,7 +21,7 @@ import { CloseOutlined, EyeInvisibleOutlined, EyeOutlined, LoadingOutlined } fro
import './index.less';
import api from '@src/api';
import { useParams } from 'react-router-dom';
import { regKafkaPassword } from '@src/common/reg';
import { regKafkaPassword } from '@src/constants/reg';
export const randomString = (len = 32, chars = 'abcdefghijklmnopqrstuvwxyz1234567890'): string => {
const maxPos = chars.length;
@@ -283,18 +283,17 @@ const SecurityUsers = (): JSX.Element => {
const [loading, setLoading] = useState<boolean>(true);
const [data, setData] = useState<UsersProps[]>([]);
const [pagination, setPagination] = useState<any>(defaultPagination);
const [form] = Form.useForm();
const [searchKeywords, setSearchKeywords] = useState('');
const [searchKeywordsInput, setSearchKeywordsInput] = useState('');
const editDrawerRef = useRef(null);
const getKafkaUserList = (query = {}) => {
const formData = form.getFieldsValue();
const queryParams = {
pageNo: pagination.current,
pageSize: pagination.pageSize,
...formData,
searchKeywords,
...query,
};
setLoading(true);
Utils.request(api.getKafkaUsers(clusterId), {
method: 'POST',
@@ -413,6 +412,10 @@ const SecurityUsers = (): JSX.Element => {
getKafkaUserList();
}, []);
useEffect(() => {
(searchKeywords || searchKeywords === '') && getKafkaUserList({ pageNo: 1 });
}, [searchKeywords]);
return (
<div className="security-users-page">
<DKSBreadcrumb
@@ -424,16 +427,26 @@ const SecurityUsers = (): JSX.Element => {
/>
<div className="security-users-page-list">
<div className="operate-bar">
<Form form={form} layout="inline" onFinish={() => getKafkaUserList({ pageNo: 1 })}>
<Form.Item name="searchKeywords">
<Input placeholder="请输入 Kafka User" />
</Form.Item>
<Form.Item>
<Button type="primary" ghost htmlType="submit">
</Button>
</Form.Item>
</Form>
<Input
className="search-input"
suffix={
<IconFont
type="icon-fangdajing"
onClick={(_) => {
setSearchKeywords(searchKeywordsInput);
}}
style={{ fontSize: '16px' }}
/>
}
placeholder="请输入 Kafka User"
value={searchKeywordsInput}
onPressEnter={(_) => {
setSearchKeywords(searchKeywordsInput);
}}
onChange={(e) => {
setSearchKeywordsInput(e.target.value);
}}
/>
<Button
type="primary"
// icon={<PlusOutlined />}

View File

@@ -107,6 +107,7 @@ const LeftSider = () => {
strokeWidth={4}
strokeColor={getHealthProcessColor(clusterMetrics?.HealthScore, clusterMetrics?.Alive)}
percent={clusterMetrics?.HealthScore ?? '-'}
className={+clusterMetrics.Alive <= 0 ? 'red-circle' : +clusterMetrics?.HealthScore < 90 ? 'blue-circle' : 'green-circle'}
format={() => (
<div className={`healthy-percent ${getHealthClassName(clusterMetrics?.HealthScore, clusterMetrics?.Alive)}`}>
{getHealthText(clusterMetrics?.HealthScore, clusterMetrics?.Alive)}

View File

@@ -156,7 +156,20 @@
.state-card {
display: flex;
width: 100%;
.green-circle {
.dcloud-progress-inner {
background: #f5fdfc;
overflow: hidden;
border-radius: 50%;
}
}
.red-circle {
.dcloud-progress-inner {
background: #fffafa;
overflow: hidden;
border-radius: 50%;
}
}
.healthy-percent {
font-family: DIDIFD-Regular;
font-size: 38px;

View File

@@ -219,7 +219,7 @@ const ConsumeClientTest = () => {
];
const _params = {
clusterId,
maxDurationUnitMs: 8000, // 前端超时时间为10s这里接口超时设置8s
maxDurationUnitMs: 5000, // 前端超时时间为10s这里接口超时设置8s
clientProperties: {},
maxRecords: 10,
topicName: values.topic,
@@ -239,9 +239,9 @@ const ConsumeClientTest = () => {
? offsetLists
: undefined
: lastPartitionList.current?.map((item) => ({
offset: item.consumedOffset,
partitionId: item.partitionId,
})),
offset: item.consumedOffset,
partitionId: item.partitionId,
})),
timestampUnitMs:
values.start[1] === 'a specific date'
? startFromMap[values.start[1]].getDate(values.startDate)

View File

@@ -3,8 +3,7 @@ import { IconFont, Switch, Tooltip } from 'knowdesign';
import { FormItemType, IFormItem } from 'knowdesign/lib/extend/x-form';
import moment from 'moment';
import React from 'react';
import { timeFormat } from '../../constants/common';
import { getRandomStr } from '../../lib/utils';
import { timeFormat, getRandomStr } from '@src/constants/common';
import { ControlStatusMap } from '../CommonRoute';
export const filterList = [

View File

@@ -8,7 +8,7 @@ const { request } = Utils;
const defaultParams: any = {
truncate: true,
maxRecords: 100,
pullTimeoutUnitMs: 8000,
pullTimeoutUnitMs: 5000,
// filterPartitionId: 1,
};
const defaultpaPagination = {