feat: nodes list
This commit is contained in:
+3
-1
@@ -13,10 +13,12 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^5.3.7",
|
"@ant-design/icons": "^5.3.7",
|
||||||
"@ant-design/pro-components": "^2.7.1",
|
"@ant-design/pro-components": "^2.7.1",
|
||||||
|
"@types/lodash": "^4.17.4",
|
||||||
"@umijs/max": "^4.2.1",
|
"@umijs/max": "^4.2.1",
|
||||||
"antd": "^5.17.0",
|
"antd": "^5.17.0",
|
||||||
"antd-style": "^3.6.2",
|
"antd-style": "^3.6.2",
|
||||||
"classnames": "^2.5.1"
|
"classnames": "^2.5.1",
|
||||||
|
"lodash": "^4.17.21"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^18.3.1",
|
"@types/react": "^18.3.1",
|
||||||
|
|||||||
Generated
+10
@@ -11,6 +11,9 @@ dependencies:
|
|||||||
'@ant-design/pro-components':
|
'@ant-design/pro-components':
|
||||||
specifier: ^2.7.1
|
specifier: ^2.7.1
|
||||||
version: 2.7.1(antd@5.17.0)(rc-field-form@1.44.0)(react-dom@18.3.1)(react@18.3.1)
|
version: 2.7.1(antd@5.17.0)(rc-field-form@1.44.0)(react-dom@18.3.1)(react@18.3.1)
|
||||||
|
'@types/lodash':
|
||||||
|
specifier: ^4.17.4
|
||||||
|
version: 4.17.4
|
||||||
'@umijs/max':
|
'@umijs/max':
|
||||||
specifier: ^4.2.1
|
specifier: ^4.2.1
|
||||||
version: 4.2.1(@babel/core@7.24.5)(@types/react-dom@18.3.0)(@types/react@18.3.1)(dva@2.5.0-beta.2)(prettier@3.2.5)(rc-field-form@1.44.0)(react-dom@18.3.1)(react@18.3.1)(typescript@5.4.5)(webpack@5.91.0)
|
version: 4.2.1(@babel/core@7.24.5)(@types/react-dom@18.3.0)(@types/react@18.3.1)(dva@2.5.0-beta.2)(prettier@3.2.5)(rc-field-form@1.44.0)(react-dom@18.3.1)(react@18.3.1)(typescript@5.4.5)(webpack@5.91.0)
|
||||||
@@ -23,6 +26,9 @@ dependencies:
|
|||||||
classnames:
|
classnames:
|
||||||
specifier: ^2.5.1
|
specifier: ^2.5.1
|
||||||
version: 2.5.1
|
version: 2.5.1
|
||||||
|
lodash:
|
||||||
|
specifier: ^4.17.21
|
||||||
|
version: 4.17.21
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@types/react':
|
'@types/react':
|
||||||
@@ -2611,6 +2617,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@types/lodash@4.17.4:
|
||||||
|
resolution: {integrity: sha512-wYCP26ZLxaT3R39kiN2+HcJ4kTd3U1waI/cY7ivWYqFP6pW3ZNpvi6Wd6PHZx7T/t8z0vlkXMg3QYLa7DZ/IJQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@types/minimist@1.2.5:
|
/@types/minimist@1.2.5:
|
||||||
resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==}
|
resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
// 全局初始化数据配置,用于 Layout 用户信息和权限初始化
|
// 全局初始化数据配置,用于 Layout 用户信息和权限初始化
|
||||||
// 更多信息见文档:https://umijs.org/docs/api/runtime-config#getinitialstate
|
// 更多信息见文档:https://umijs.org/docs/api/runtime-config#getinitialstate
|
||||||
export async function getInitialState(): Promise<{ name: string }> {
|
export async function getInitialState(): Promise<{ name: string }> {
|
||||||
return { name: 'admin' };
|
return { name: 'admin', logout: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
// export const layout: RunTimeLayoutConfig = () => {
|
// export const layout: RunTimeLayoutConfig = () => {
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
.status-tag {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2px 10px;
|
||||||
|
border-radius: 20px;
|
||||||
|
width: fit-content;
|
||||||
|
font-size: var(--font-size-base);
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { StatusColorMap } from '@/config';
|
||||||
|
import { StatusType } from '@/config/types';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import styles from './index.less';
|
||||||
|
|
||||||
|
export const StatusMaps = {
|
||||||
|
running: 'blue',
|
||||||
|
error: 'red',
|
||||||
|
warning: 'orange',
|
||||||
|
success: 'success'
|
||||||
|
};
|
||||||
|
|
||||||
|
type StatusTagProps = {
|
||||||
|
statusValue: {
|
||||||
|
status: StatusType;
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const StatusTag: React.FC<StatusTagProps> = ({ statusValue }) => {
|
||||||
|
const { text, status } = statusValue;
|
||||||
|
const [statusColor, setStatusColor] = useState<{ text: string; bg: string }>({
|
||||||
|
text: '',
|
||||||
|
bg: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setStatusColor(StatusColorMap[status]);
|
||||||
|
}, [status]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={styles['status-tag']}
|
||||||
|
style={{
|
||||||
|
color: statusColor.text,
|
||||||
|
border: `1px solid ${statusColor.text}`
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StatusTag;
|
||||||
+25
-1
@@ -1,7 +1,31 @@
|
|||||||
import { PageActionType } from './types';
|
import { PageActionType, StatusType } from './types';
|
||||||
|
|
||||||
export const PageAction: Record<string, PageActionType> = {
|
export const PageAction: Record<string, PageActionType> = {
|
||||||
CREATE: 'create',
|
CREATE: 'create',
|
||||||
UPDATE: 'update',
|
UPDATE: 'update',
|
||||||
VIEW: 'view'
|
VIEW: 'view'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const StatusColorMap: Record<StatusType, { text: string; bg: string }> =
|
||||||
|
{
|
||||||
|
error: {
|
||||||
|
text: `var(--ant-red-6)`,
|
||||||
|
bg: `var(--ant-red-1)`
|
||||||
|
},
|
||||||
|
warning: {
|
||||||
|
text: `var(--ant-orange-6)`,
|
||||||
|
bg: `var(--ant-orange-1)`
|
||||||
|
},
|
||||||
|
transitioning: {
|
||||||
|
text: `var(--ant-blue-6)`,
|
||||||
|
bg: `var(--ant-blue-1)`
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
text: `var(--ant-color-primary)`,
|
||||||
|
bg: `var(--ant-green-1)`
|
||||||
|
},
|
||||||
|
inactive: {
|
||||||
|
text: `var(--ant-color-border)`,
|
||||||
|
bg: `var(--ant-color-fill)`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -7,3 +7,10 @@ export interface DropDownItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type PageActionType = 'create' | 'update' | 'view';
|
export type PageActionType = 'create' | 'update' | 'view';
|
||||||
|
|
||||||
|
export type StatusType =
|
||||||
|
| 'error'
|
||||||
|
| 'warning'
|
||||||
|
| 'transitioning'
|
||||||
|
| 'success'
|
||||||
|
| 'inactive';
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ html {
|
|||||||
--color-white-1: #fff;
|
--color-white-1: #fff;
|
||||||
--font-weight-normal: 500;
|
--font-weight-normal: 500;
|
||||||
--font-weight-bold: 700;
|
--font-weight-bold: 700;
|
||||||
--color-text-1: #1d2129;
|
--color-text-1: var(--ant-color-text);
|
||||||
--color-bg-light-1: #f0fff6;
|
--color-bg-light-1: #f0fff6;
|
||||||
--font-size-base: 12px;
|
--font-size-base: 12px;
|
||||||
--ant-color-fill-secondary: rgba(0, 0, 0, 0.06);
|
--ant-color-fill-secondary: rgba(0, 0, 0, 0.06);
|
||||||
|
|||||||
+17
-23
@@ -12,19 +12,19 @@ import {
|
|||||||
useAppData,
|
useAppData,
|
||||||
useLocation,
|
useLocation,
|
||||||
useNavigate,
|
useNavigate,
|
||||||
type IRoute,
|
type IRoute
|
||||||
} from '@umijs/max';
|
} from '@umijs/max';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import Exception from './Exception';
|
import Exception from './Exception';
|
||||||
import './Layout.css';
|
import './Layout.css';
|
||||||
import Logo from './Logo';
|
import Logo from './Logo';
|
||||||
import { getRightRenderContent } from './rightRender';
|
import { getRightRenderContent } from './rightRender';
|
||||||
import { renderMenuIcon,patchRoutes } from './runtime';
|
import { patchRoutes } from './runtime';
|
||||||
|
|
||||||
// 过滤出需要显示的路由, 这里的filterFn 指 不希望显示的层级
|
// 过滤出需要显示的路由, 这里的filterFn 指 不希望显示的层级
|
||||||
const filterRoutes = (
|
const filterRoutes = (
|
||||||
routes: IRoute[],
|
routes: IRoute[],
|
||||||
filterFn: (route: IRoute) => boolean,
|
filterFn: (route: IRoute) => boolean
|
||||||
) => {
|
) => {
|
||||||
if (routes.length === 0) {
|
if (routes.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
@@ -78,34 +78,33 @@ export default (props: any) => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { clientRoutes, pluginManager } = useAppData();
|
const { clientRoutes, pluginManager } = useAppData();
|
||||||
|
|
||||||
console.log('pluginManager===========', pluginManager);
|
|
||||||
|
|
||||||
const initialInfo = (useModel && useModel('@@initialState')) || {
|
const initialInfo = (useModel && useModel('@@initialState')) || {
|
||||||
initialState: undefined,
|
initialState: undefined,
|
||||||
loading: false,
|
loading: false,
|
||||||
setInitialState: null,
|
setInitialState: null
|
||||||
};
|
};
|
||||||
const { initialState, loading, setInitialState } = initialInfo;
|
const { initialState, loading, setInitialState } = initialInfo;
|
||||||
|
|
||||||
const userConfig = {
|
const userConfig = {
|
||||||
title: '',
|
title: '',
|
||||||
layout: 'mix',
|
layout: 'mix'
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatMessage = undefined;
|
const formatMessage = undefined;
|
||||||
const runtimeConfig = pluginManager.applyPlugins({
|
const runtimeConfig = pluginManager.applyPlugins({
|
||||||
key: 'layout',
|
key: 'layout',
|
||||||
type: 'modify',
|
type: 'modify',
|
||||||
|
logout: true,
|
||||||
initialValue: {
|
initialValue: {
|
||||||
...initialInfo,
|
...initialInfo,
|
||||||
notFound: <div>not found</div>
|
notFound: <div>not found</div>
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
console.log(
|
console.log(
|
||||||
'clientRoute===========',
|
'clientRoute===========',
|
||||||
clientRoutes,
|
clientRoutes,
|
||||||
runtimeConfig,
|
runtimeConfig,
|
||||||
initialInfo,
|
initialInfo
|
||||||
);
|
);
|
||||||
|
|
||||||
// 现在的 layout 及 wrapper 实现是通过父路由的形式实现的, 会导致路由数据多了冗余层级, proLayout 消费时, 无法正确展示菜单, 这里对冗余数据进行过滤操作
|
// 现在的 layout 及 wrapper 实现是通过父路由的形式实现的, 会导致路由数据多了冗余层级, proLayout 消费时, 无法正确展示菜单, 这里对冗余数据进行过滤操作
|
||||||
@@ -116,18 +115,17 @@ export default (props: any) => {
|
|||||||
(!!route.isLayout && route.id !== '@@/global-layout') ||
|
(!!route.isLayout && route.id !== '@@/global-layout') ||
|
||||||
!!route.isWrapper
|
!!route.isWrapper
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
console.log('clientRoutes===========', clientRoutes, newRoutes)
|
console.log('clientRoutes===========', clientRoutes, newRoutes);
|
||||||
const [route] = useAccessMarkedRoutes(mapRoutes(newRoutes));
|
const [route] = useAccessMarkedRoutes(mapRoutes(newRoutes));
|
||||||
patchRoutes({ routes: route?.children || [] });
|
patchRoutes({ routes: route?.children || [] });
|
||||||
|
|
||||||
|
|
||||||
const matchedRoute = useMemo(
|
const matchedRoute = useMemo(
|
||||||
() => matchRoutes(route?.children || [], location.pathname)?.pop?.()?.route,
|
() => matchRoutes(route?.children || [], location.pathname)?.pop?.()?.route,
|
||||||
[location.pathname],
|
[location.pathname]
|
||||||
);
|
);
|
||||||
console.log('route===========', route)
|
console.log('route===========', route);
|
||||||
return (
|
return (
|
||||||
<ProLayout
|
<ProLayout
|
||||||
route={route}
|
route={route}
|
||||||
@@ -147,7 +145,7 @@ console.log('route===========', route)
|
|||||||
menu={{ locale: userConfig.locale }}
|
menu={{ locale: userConfig.locale }}
|
||||||
logo={Logo}
|
logo={Logo}
|
||||||
menuItemRender={(menuItemProps, defaultDom) => {
|
menuItemRender={(menuItemProps, defaultDom) => {
|
||||||
console.log('meurender=========',{ defaultDom})
|
console.log('meurender=========', { defaultDom });
|
||||||
if (menuItemProps.isUrl || menuItemProps.children) {
|
if (menuItemProps.isUrl || menuItemProps.children) {
|
||||||
return defaultDom;
|
return defaultDom;
|
||||||
}
|
}
|
||||||
@@ -158,15 +156,11 @@ console.log('route===========', route)
|
|||||||
to={menuItemProps.path.replace('/*', '')}
|
to={menuItemProps.path.replace('/*', '')}
|
||||||
target={menuItemProps.target}
|
target={menuItemProps.target}
|
||||||
>
|
>
|
||||||
{defaultDom}
|
{defaultDom}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return <>{defaultDom}</>;
|
||||||
<>
|
|
||||||
{defaultDom}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
itemRender={(route, _, routes) => {
|
itemRender={(route, _, routes) => {
|
||||||
const { breadcrumbName, title, path } = route;
|
const { breadcrumbName, title, path } = route;
|
||||||
@@ -190,7 +184,7 @@ console.log('route===========', route)
|
|||||||
runtimeConfig,
|
runtimeConfig,
|
||||||
loading,
|
loading,
|
||||||
initialState,
|
initialState,
|
||||||
setInitialState,
|
setInitialState
|
||||||
});
|
});
|
||||||
if (runtimeConfig.rightContentRender) {
|
if (runtimeConfig.rightContentRender) {
|
||||||
return runtimeConfig.rightContentRender(layoutProps, dom, {
|
return runtimeConfig.rightContentRender(layoutProps, dom, {
|
||||||
@@ -199,7 +193,7 @@ console.log('route===========', route)
|
|||||||
runtimeConfig,
|
runtimeConfig,
|
||||||
loading,
|
loading,
|
||||||
initialState,
|
initialState,
|
||||||
setInitialState,
|
setInitialState
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return dom;
|
return dom;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
// This file is generated by Umi automatically
|
// This file is generated by Umi automatically
|
||||||
// DO NOT CHANGE IT MANUALLY!
|
// DO NOT CHANGE IT MANUALLY!
|
||||||
|
import avatarImg from '@/assets/images/avatar.png';
|
||||||
import { LogoutOutlined } from '@ant-design/icons';
|
import { LogoutOutlined } from '@ant-design/icons';
|
||||||
import { Avatar, Dropdown, Menu, Spin, version } from 'antd';
|
import { Avatar, Dropdown, Menu, Spin, version } from 'antd';
|
||||||
|
|
||||||
@@ -14,7 +15,7 @@ export function getRightRenderContent(opts: {
|
|||||||
return opts.runtimeConfig.rightRender(
|
return opts.runtimeConfig.rightRender(
|
||||||
opts.initialState,
|
opts.initialState,
|
||||||
opts.setInitialState,
|
opts.setInitialState,
|
||||||
opts.runtimeConfig,
|
opts.runtimeConfig
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,10 +33,7 @@ export function getRightRenderContent(opts: {
|
|||||||
<Avatar
|
<Avatar
|
||||||
size="small"
|
size="small"
|
||||||
className="umi-plugin-layout-avatar"
|
className="umi-plugin-layout-avatar"
|
||||||
src={
|
src={opts.initialState?.avatar || avatarImg}
|
||||||
opts.initialState?.avatar ||
|
|
||||||
'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png'
|
|
||||||
}
|
|
||||||
alt="avatar"
|
alt="avatar"
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
@@ -68,9 +66,9 @@ export function getRightRenderContent(opts: {
|
|||||||
),
|
),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
opts?.runtimeConfig?.logout?.(opts.initialState);
|
opts?.runtimeConfig?.logout?.(opts.initialState);
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
};
|
};
|
||||||
// antd@5 和 4.24 之后推荐使用 menu,性能更好
|
// antd@5 和 4.24 之后推荐使用 menu,性能更好
|
||||||
let dropdownProps;
|
let dropdownProps;
|
||||||
@@ -86,7 +84,7 @@ export function getRightRenderContent(opts: {
|
|||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
))}
|
))}
|
||||||
</Menu>
|
</Menu>
|
||||||
),
|
)
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// 需要 antd 4.20.0 以上版本
|
// 需要 antd 4.20.0 以上版本
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
export interface Capacity {
|
||||||
|
cpu: number;
|
||||||
|
gpu: number;
|
||||||
|
memory: string;
|
||||||
|
gram: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeItem {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
address: string;
|
||||||
|
hostname: string;
|
||||||
|
labels: object;
|
||||||
|
resources: {
|
||||||
|
capacity: Capacity;
|
||||||
|
allocable: Capacity;
|
||||||
|
};
|
||||||
|
state: string;
|
||||||
|
}
|
||||||
+271
-10
@@ -1,16 +1,277 @@
|
|||||||
|
import PageTools from '@/components/page-tools';
|
||||||
|
import StatusTag from '@/components/status-tag';
|
||||||
|
import useTableRowSelection from '@/hooks/use-table-row-selection';
|
||||||
|
import useTableSort from '@/hooks/use-table-sort';
|
||||||
|
import { SyncOutlined } from '@ant-design/icons';
|
||||||
import { PageContainer } from '@ant-design/pro-components';
|
import { PageContainer } from '@ant-design/pro-components';
|
||||||
|
import { Button, Input, Progress, Space, Table } from 'antd';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import { memo, useMemo, useState } from 'react';
|
||||||
|
import { NodeItem } from './config/types';
|
||||||
|
const { Column } = Table;
|
||||||
|
|
||||||
|
const dataSource: NodeItem[] = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: '192.168.1.2',
|
||||||
|
address: '192.168.1.2',
|
||||||
|
hostname: 'node-1',
|
||||||
|
labels: {},
|
||||||
|
resources: {
|
||||||
|
capacity: {
|
||||||
|
cpu: 4,
|
||||||
|
gpu: 2,
|
||||||
|
memory: '64 GiB',
|
||||||
|
gram: '24 Gib'
|
||||||
|
},
|
||||||
|
allocable: {
|
||||||
|
cpu: 2.5,
|
||||||
|
gpu: 1.6,
|
||||||
|
memory: '64',
|
||||||
|
gram: '24 Gib'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state: 'ALIVE'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: '192.168.1.2',
|
||||||
|
address: '192.168.1.5',
|
||||||
|
hostname: 'node-2',
|
||||||
|
labels: {},
|
||||||
|
resources: {
|
||||||
|
capacity: {
|
||||||
|
cpu: 4,
|
||||||
|
gpu: 2,
|
||||||
|
memory: '64 GiB',
|
||||||
|
gram: '24 Gib'
|
||||||
|
},
|
||||||
|
allocable: {
|
||||||
|
cpu: 2,
|
||||||
|
gpu: 1.5,
|
||||||
|
memory: '32 GiB',
|
||||||
|
gram: '12 Gib'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state: 'ALIVE'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const Models: React.FC = () => {
|
||||||
|
const rowSelection = useTableRowSelection();
|
||||||
|
const { sortOrder, setSortOrder } = useTableSort({
|
||||||
|
defaultSortOrder: 'descend'
|
||||||
|
});
|
||||||
|
const [total, setTotal] = useState(100);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [queryParams, setQueryParams] = useState({
|
||||||
|
current: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
name: ''
|
||||||
|
});
|
||||||
|
const handleShowSizeChange = (current: number, size: number) => {
|
||||||
|
console.log(current, size);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePageChange = (page: number, pageSize: number | undefined) => {
|
||||||
|
console.log(page, pageSize);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTableChange = (pagination: any, filters: any, sorter: any) => {
|
||||||
|
setSortOrder(sorter.order);
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
console.log('fetchData');
|
||||||
|
};
|
||||||
|
const handleSearch = (e: any) => {
|
||||||
|
fetchData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNameChange = (e: any) => {
|
||||||
|
setQueryParams({
|
||||||
|
...queryParams,
|
||||||
|
name: e.target.value
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClickMenu = (e: any) => {
|
||||||
|
console.log('click', e);
|
||||||
|
};
|
||||||
|
|
||||||
|
const RenderProgress = memo(
|
||||||
|
(props: { record: NodeItem; dataIndex: string }) => {
|
||||||
|
const { record, dataIndex } = props;
|
||||||
|
const value1 = useMemo(() => {
|
||||||
|
let value = _.get(record, ['resources', 'allocable', dataIndex]);
|
||||||
|
if (['gram', 'memory'].includes(dataIndex)) {
|
||||||
|
value = _.toNumber(value.replace(/GiB|Gib/, ''));
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}, [record, dataIndex]);
|
||||||
|
|
||||||
|
const value2 = useMemo(() => {
|
||||||
|
let value = _.get(record, ['resources', 'capacity', dataIndex]);
|
||||||
|
if (['gram', 'memory'].includes(dataIndex)) {
|
||||||
|
value = _.toNumber(value.replace(/GiB|Gib/, ''));
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}, [record, dataIndex]);
|
||||||
|
|
||||||
|
if (!value1 || !value2) {
|
||||||
|
return <Progress percent={0} strokeColor="var(--ant-color-primary)" />;
|
||||||
|
}
|
||||||
|
const percent = _.round(value1 / value2, 2) * 100;
|
||||||
|
const strokeColor = useMemo(() => {
|
||||||
|
if (percent <= 50) {
|
||||||
|
return 'var(--ant-color-primary)';
|
||||||
|
}
|
||||||
|
if (percent <= 80) {
|
||||||
|
return 'var(--ant-color-warning)';
|
||||||
|
}
|
||||||
|
return 'var(--ant-color-error)';
|
||||||
|
}, [percent]);
|
||||||
|
return (
|
||||||
|
<Progress
|
||||||
|
steps={5}
|
||||||
|
format={() => {
|
||||||
|
return (
|
||||||
|
<span style={{ color: 'var(--ant-color-text)' }}>{percent}%</span>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
percent={percent}
|
||||||
|
strokeColor={strokeColor}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const Dashboard: React.FC = () => {
|
|
||||||
return (
|
return (
|
||||||
<PageContainer
|
<>
|
||||||
ghost
|
<PageContainer
|
||||||
header={{
|
ghost
|
||||||
title: 'Dashboard',
|
header={{
|
||||||
}}
|
title: 'Nodes'
|
||||||
>
|
}}
|
||||||
<div>Nodes</div>
|
extra={[]}
|
||||||
</PageContainer>
|
>
|
||||||
|
<PageTools
|
||||||
|
marginBottom={22}
|
||||||
|
left={
|
||||||
|
<Space>
|
||||||
|
<Input
|
||||||
|
placeholder="按名称查询"
|
||||||
|
style={{ width: 300 }}
|
||||||
|
onChange={handleNameChange}
|
||||||
|
></Input>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
style={{ color: 'var(--ant-color-primary)' }}
|
||||||
|
onClick={handleSearch}
|
||||||
|
icon={<SyncOutlined></SyncOutlined>}
|
||||||
|
></Button>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
></PageTools>
|
||||||
|
<Table
|
||||||
|
dataSource={dataSource}
|
||||||
|
rowSelection={rowSelection}
|
||||||
|
loading={loading}
|
||||||
|
onChange={handleTableChange}
|
||||||
|
pagination={{
|
||||||
|
showSizeChanger: true,
|
||||||
|
pageSize: 10,
|
||||||
|
current: 2,
|
||||||
|
total: total,
|
||||||
|
hideOnSinglePage: true,
|
||||||
|
onShowSizeChange: handleShowSizeChange,
|
||||||
|
onChange: handlePageChange
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Column title="Host Name" dataIndex="hostname" key="hostname" />
|
||||||
|
<Column
|
||||||
|
title="State"
|
||||||
|
dataIndex="state"
|
||||||
|
key="state"
|
||||||
|
render={(text, record) => {
|
||||||
|
return (
|
||||||
|
<StatusTag
|
||||||
|
statusValue={{
|
||||||
|
status: 'success',
|
||||||
|
text: 'ALIVE'
|
||||||
|
}}
|
||||||
|
></StatusTag>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Column title="IP / PID" dataIndex="address" key="address" />
|
||||||
|
<Column
|
||||||
|
title="CPU"
|
||||||
|
dataIndex="CPU"
|
||||||
|
key="CPU"
|
||||||
|
render={(text, record: NodeItem) => {
|
||||||
|
return (
|
||||||
|
<RenderProgress
|
||||||
|
record={record}
|
||||||
|
dataIndex="cpu"
|
||||||
|
></RenderProgress>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Column
|
||||||
|
title="Memory"
|
||||||
|
dataIndex="memory"
|
||||||
|
key="Memory"
|
||||||
|
render={(text, record: NodeItem) => {
|
||||||
|
return (
|
||||||
|
<RenderProgress
|
||||||
|
record={record}
|
||||||
|
dataIndex="memory"
|
||||||
|
></RenderProgress>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Column
|
||||||
|
title="GPU"
|
||||||
|
dataIndex="GPU"
|
||||||
|
key="GPU"
|
||||||
|
render={(text, record: NodeItem) => {
|
||||||
|
return (
|
||||||
|
<RenderProgress
|
||||||
|
record={record}
|
||||||
|
dataIndex="gpu"
|
||||||
|
></RenderProgress>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Column
|
||||||
|
title="GRAM"
|
||||||
|
dataIndex="GRAM"
|
||||||
|
key="GRAM"
|
||||||
|
render={(text, record: NodeItem) => {
|
||||||
|
return (
|
||||||
|
<RenderProgress
|
||||||
|
record={record}
|
||||||
|
dataIndex="gram"
|
||||||
|
></RenderProgress>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Column
|
||||||
|
title="Operation"
|
||||||
|
key="operation"
|
||||||
|
render={(text, record) => {
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
<Button size="middle">Logs</Button>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Table>
|
||||||
|
</PageContainer>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Dashboard;
|
export default Models;
|
||||||
|
|||||||
Vendored
+1
@@ -14,5 +14,6 @@ declare module 'omit.js';
|
|||||||
declare module 'numeral';
|
declare module 'numeral';
|
||||||
declare module 'mockjs';
|
declare module 'mockjs';
|
||||||
declare module 'react-fittext';
|
declare module 'react-fittext';
|
||||||
|
declare module 'lodash';
|
||||||
|
|
||||||
declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false;
|
declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false;
|
||||||
|
|||||||
Reference in New Issue
Block a user