mirror of
https://github.com/didi/KnowStreaming.git
synced 2025-12-24 11:52:08 +08:00
前端调整
This commit is contained in:
@@ -29,5 +29,6 @@ module.exports = {
|
||||
'@typescript-eslint/no-var-requires': 0,
|
||||
'prettier/prettier': 2, // 这项配置 对于不符合prettier规范的写法,eslint会提示报错
|
||||
'no-console': 1,
|
||||
'react/display-name': 0,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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`。
|
||||
|
||||
@@ -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 配置入口
|
||||
|
||||
@@ -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'],
|
||||
},
|
||||
};
|
||||
0
km-console/packages/config-manager-fe/src/@types/index.d.ts
vendored
Normal file
0
km-console/packages/config-manager-fe/src/@types/index.d.ts
vendored
Normal file
@@ -1,7 +0,0 @@
|
||||
export const defaultPagination = {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
position: 'bottomRight',
|
||||
showSizeChanger: true,
|
||||
pageSizeOptions: ['10', '20', '50', '100'],
|
||||
};
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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.js:antd 主题配置
|
||||
- webpack.dev.config.js:webpack 开发环境补充配置,覆盖默认配置
|
||||
- webpack.build.config.js:webpack 构建补充配置,覆盖默认配置
|
||||
- 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 配置入口
|
||||
|
||||
@@ -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;
|
||||
@@ -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'];
|
||||
@@ -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 (
|
||||
<>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ export default (props: any) => {
|
||||
key: 'memberId',
|
||||
width: 200,
|
||||
render: (v: string) => {
|
||||
return <ContentWithCopy content={v} />;
|
||||
return v ? <ContentWithCopy content={v} /> : '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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'}`, // 纯无边框表格类名
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -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(点击下载)
|
||||
|
||||
@@ -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 + '',
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 />}
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -8,7 +8,7 @@ const { request } = Utils;
|
||||
const defaultParams: any = {
|
||||
truncate: true,
|
||||
maxRecords: 100,
|
||||
pullTimeoutUnitMs: 8000,
|
||||
pullTimeoutUnitMs: 5000,
|
||||
// filterPartitionId: 1,
|
||||
};
|
||||
const defaultpaPagination = {
|
||||
|
||||
Reference in New Issue
Block a user