Browse Source

Merge remote-tracking branch 'origin/dev' into dev

pull/44/head
西大锐 1 year ago
parent
commit
eb6260cdc8
100 changed files with 1794 additions and 467 deletions
  1. +2
    -3
      react-ui/config/config.ts
  2. +47
    -27
      react-ui/config/routes.ts
  3. +2
    -0
      react-ui/package.json
  4. BIN
      react-ui/public/assets/材料科研软件平台使用文档.pdf
  5. +54
    -20
      react-ui/src/app.tsx
  6. BIN
      react-ui/src/assets/img/avatar-default.png
  7. BIN
      react-ui/src/assets/img/blue-triangle.png
  8. BIN
      react-ui/src/assets/img/functional-material.png
  9. BIN
      react-ui/src/assets/img/molecular-material.png
  10. BIN
      react-ui/src/assets/img/user-avatar/1.png
  11. BIN
      react-ui/src/assets/img/user-avatar/2.png
  12. BIN
      react-ui/src/assets/img/user-avatar/3.png
  13. BIN
      react-ui/src/assets/img/user-avatar/4.png
  14. BIN
      react-ui/src/assets/img/user-avatar/5.png
  15. BIN
      react-ui/src/assets/img/user-avatar/6.png
  16. BIN
      react-ui/src/assets/img/user-avatar/7.png
  17. BIN
      react-ui/src/assets/img/user-avatar/8.png
  18. BIN
      react-ui/src/assets/img/workspace-experiment.png
  19. BIN
      react-ui/src/assets/img/workspace-intro-icon.png
  20. BIN
      react-ui/src/assets/img/workspace-intro.png
  21. BIN
      react-ui/src/assets/img/workspace-pipeline.png
  22. BIN
      react-ui/src/assets/img/workspace-quick-start.png
  23. BIN
      react-ui/src/assets/img/workspace-user.png
  24. +6
    -0
      react-ui/src/components/CommonTableCell/index.tsx
  25. +7
    -1
      react-ui/src/components/DateTableCell/index.tsx
  26. +4
    -3
      react-ui/src/components/IconSelector/IconPicSearcher.tsx
  27. +3
    -2
      react-ui/src/components/KFIcon/index.tsx
  28. +9
    -16
      react-ui/src/components/KFModal/index.less
  29. +9
    -18
      react-ui/src/components/KFModal/index.tsx
  30. +5
    -3
      react-ui/src/components/KFRadio/index.tsx
  31. +0
    -2
      react-ui/src/components/KFTabs/index.less
  32. +0
    -16
      react-ui/src/components/KFTabs/index.tsx
  33. +2
    -2
      react-ui/src/components/ModalTitle/index.less
  34. +12
    -3
      react-ui/src/components/ModalTitle/index.tsx
  35. +6
    -6
      react-ui/src/components/RightContent/AvatarDropdown.tsx
  36. +1
    -1
      react-ui/src/components/RightContent/index.tsx
  37. +4
    -2
      react-ui/src/components/SubAreaTitle/index.tsx
  38. +15
    -132
      react-ui/src/global.less
  39. +2
    -2
      react-ui/src/hooks/index.ts
  40. +58
    -0
      react-ui/src/hooks/pageCacheState.ts
  41. +1
    -1
      react-ui/src/iconfont/iconfont.js
  42. +85
    -0
      react-ui/src/overrides.less
  43. +1
    -1
      react-ui/src/pages/Dataset/index.jsx
  44. +11
    -11
      react-ui/src/pages/Dataset/personalData.jsx
  45. +11
    -11
      react-ui/src/pages/Dataset/publicData.jsx
  46. +0
    -0
      react-ui/src/pages/Docs/index.less
  47. +9
    -0
      react-ui/src/pages/Docs/index.tsx
  48. +9
    -7
      react-ui/src/pages/Experiment/index.less
  49. +1
    -1
      react-ui/src/pages/Experiment/status.ts
  50. +2
    -5
      react-ui/src/pages/Mirror/create.tsx
  51. +27
    -12
      react-ui/src/pages/Mirror/info.tsx
  52. +27
    -24
      react-ui/src/pages/Mirror/list.tsx
  53. +1
    -1
      react-ui/src/pages/Model/index.jsx
  54. +11
    -11
      react-ui/src/pages/Model/personalData.jsx
  55. +11
    -11
      react-ui/src/pages/Model/publicData.jsx
  56. +4
    -4
      react-ui/src/pages/Monitor/Job/detail.tsx
  57. +3
    -3
      react-ui/src/pages/Monitor/Job/edit.tsx
  58. +4
    -10
      react-ui/src/pages/Monitor/JobLog/detail.tsx
  59. +4
    -4
      react-ui/src/pages/Pipeline/components/ResourceSelectorModal/index.less
  60. +4
    -4
      react-ui/src/pages/System/Config/edit.tsx
  61. +4
    -4
      react-ui/src/pages/System/Dept/edit.tsx
  62. +4
    -4
      react-ui/src/pages/System/Dict/edit.tsx
  63. +4
    -4
      react-ui/src/pages/System/DictData/edit.tsx
  64. +4
    -4
      react-ui/src/pages/System/Logininfor/edit.tsx
  65. +6
    -5
      react-ui/src/pages/System/Menu/edit.tsx
  66. +4
    -4
      react-ui/src/pages/System/Notice/edit.tsx
  67. +4
    -4
      react-ui/src/pages/System/Operlog/detail.tsx
  68. +4
    -4
      react-ui/src/pages/System/Post/edit.tsx
  69. +4
    -4
      react-ui/src/pages/System/Role/components/DataScope.tsx
  70. +3
    -4
      react-ui/src/pages/System/Role/components/UserSelectorModal.tsx
  71. +4
    -4
      react-ui/src/pages/System/Role/edit.tsx
  72. +4
    -11
      react-ui/src/pages/System/User/components/AuthRole.tsx
  73. +4
    -10
      react-ui/src/pages/System/User/components/ResetPwd.tsx
  74. +4
    -10
      react-ui/src/pages/System/User/edit.tsx
  75. +4
    -4
      react-ui/src/pages/Tool/Gen/components/PreviewCode.tsx
  76. +4
    -3
      react-ui/src/pages/User/Center/components/AvatarCropper/index.tsx
  77. +54
    -0
      react-ui/src/pages/Workspace/components/AssetsManagement/index.less
  78. +81
    -0
      react-ui/src/pages/Workspace/components/AssetsManagement/index.tsx
  79. +7
    -0
      react-ui/src/pages/Workspace/components/ExperimentChart/index.less
  80. +214
    -0
      react-ui/src/pages/Workspace/components/ExperimentChart/index.tsx
  81. +58
    -0
      react-ui/src/pages/Workspace/components/ExperimentTable/index.less
  82. +59
    -0
      react-ui/src/pages/Workspace/components/ExperimentTable/index.tsx
  83. +60
    -0
      react-ui/src/pages/Workspace/components/QuickStart/WorkArrow.tsx
  84. +31
    -0
      react-ui/src/pages/Workspace/components/QuickStart/WorkFlow.tsx
  85. +96
    -0
      react-ui/src/pages/Workspace/components/QuickStart/index.less
  86. +149
    -0
      react-ui/src/pages/Workspace/components/QuickStart/index.tsx
  87. +41
    -0
      react-ui/src/pages/Workspace/components/TotalStatistics/index.less
  88. +25
    -0
      react-ui/src/pages/Workspace/components/TotalStatistics/index.tsx
  89. +65
    -0
      react-ui/src/pages/Workspace/components/UserSpace/index.less
  90. +47
    -0
      react-ui/src/pages/Workspace/components/UserSpace/index.tsx
  91. +43
    -0
      react-ui/src/pages/Workspace/components/WorkspaceIntro/index.less
  92. +43
    -0
      react-ui/src/pages/Workspace/components/WorkspaceIntro/index.tsx
  93. +45
    -0
      react-ui/src/pages/Workspace/index.less
  94. +71
    -0
      react-ui/src/pages/Workspace/index.tsx
  95. +2
    -1
      react-ui/src/requestConfig.ts
  96. +3
    -0
      react-ui/src/services/ant-design-pro/typings.d.ts
  97. +1
    -1
      react-ui/src/services/mirror/index.ts
  98. +21
    -0
      react-ui/src/services/workspace/index.ts
  99. +23
    -2
      react-ui/src/styles/theme.less
  100. +16
    -0
      react-ui/src/types.ts

+ 2
- 3
react-ui/config/config.ts View File

@@ -78,7 +78,7 @@ export default defineConfig({
*/
title: '智能软件开发平台',
layout: {
locale: true,
locale: false,
...defaultSettings,
},
// keepalive: [/./],
@@ -97,10 +97,8 @@ export default defineConfig({
* @doc https://umijs.org/docs/max/i18n
*/
locale: {
// default zh-CN
default: 'zh-CN',
antd: true,
// default true, when it is true, will use `navigator.language` overwrite default
baseNavigator: true,
},
/**
@@ -110,6 +108,7 @@ export default defineConfig({
*/
antd: {
configProvider: {},
appConfig: {},
},
/**
* @name 网络请求配置


+ 47
- 27
react-ui/config/routes.ts View File

@@ -13,7 +13,7 @@
export default [
{
path: '/',
redirect: '/account/center',
redirect: '/workspace',
},
{
path: '*',
@@ -88,13 +88,6 @@ export default [
},
],
},
{
name: 'experiment',
path: '/experiment',
routes: [
],
},
{
name: 'developmentEnvironment',
path: '/developmentEnvironment',
@@ -127,14 +120,36 @@ export default [
path: '/dataset',
routes: [
{
name: '数据集管理',
path: '/dataset/datasetIndex',
component: './Dataset/index',
name: '数据集',
path: 'dataset',
routes: [
{
name: '数据集列表',
path: '',
component: './Dataset/index',
},
{
name: '数据集简介',
path: ':id',
component: './Dataset/datasetIntro',
},
],
},
{
name: '数据集简介',
path: '/dataset/datasetIntro/:id',
component: './Dataset/datasetIntro',
name: '模型',
path: 'model',
routes: [
{
name: '模型列表',
path: '',
component: './Model/index',
},
{
name: '模型简介',
path: ':id',
component: './Model/modelIntro',
},
],
},
{
name: '镜像',
@@ -157,19 +172,9 @@ export default [
},
],
},
{
name: '模型管理',
path: '/dataset/modelIndex',
component: './Model/index',
},
{
name: '模型简介',
path: '/dataset/modelIntro/:id',
component: './Model/modelIntro',
},
],
},

{
name: 'workspace',
path: '/workspace',
@@ -177,7 +182,8 @@ export default [
{
name: '工作空间',
path: '',
component: './missingPage.jsx',
key: 'workspace',
component: './Workspace/index',
},
],
},
@@ -188,11 +194,11 @@ export default [
{
name: '模型部署',
path: '',
key: 'modelDseployment',
component: './missingPage.jsx',
},
],
},
{
name: 'appsDeployment',
path: '/appsDeployment',
@@ -200,6 +206,7 @@ export default [
{
name: '应用开发',
path: '',
key: 'appsDeployment',
component: './missingPage.jsx',
},
],
@@ -211,6 +218,7 @@ export default [
{
name: '监控运维',
path: '',
key: 'see',
component: './missingPage.jsx',
},
],
@@ -242,4 +250,16 @@ export default [
},
],
},
{
name: 'docs',
path: '/docs',
routes: [
{
name: '使用指南',
path: '',
key: 'docs',
component: './Docs/index',
},
],
},
];

+ 2
- 0
react-ui/package.json View File

@@ -60,6 +60,7 @@
"@umijs/route-utils": "^4.0.1",
"antd": "^5.4.4",
"classnames": "^2.3.2",
"echarts": "^5.5.0",
"fabric": "^5.3.0",
"highlight.js": "^11.7.0",
"lodash": "^4.17.21",
@@ -70,6 +71,7 @@
"rc-menu": "^9.8.4",
"rc-util": "^5.30.0",
"react": "^18.2.0",
"react-activation": "^0.12.4",
"react-cropper": "^2.3.3",
"react-dev-inspector": "^1.8.1",
"react-dom": "^18.2.0",


BIN
react-ui/public/assets/材料科研软件平台使用文档.pdf View File


+ 54
- 20
react-ui/src/app.tsx View File

@@ -1,8 +1,7 @@
import RightContent from '@/components/RightContent';
import themes from '@/styles/theme.less';
import type { Settings as LayoutSettings } from '@ant-design/pro-components';
import type { RunTimeLayoutConfig } from '@umijs/max';
import { history } from '@umijs/max';
import { RuntimeConfig, history } from '@umijs/max';
import { RuntimeAntdConfig } from 'umi';
import defaultSettings from '../config/defaultSettings';
import '../public/fonts/font.css';
@@ -10,6 +9,7 @@ import { getAccessToken } from './access';
import './dayjsConfig';
import { PageEnum } from './enums/pagesEnums';
import './global.less';
import { removeAllPageCacheState } from './hooks/pageCacheState';
import {
getRemoteMenu,
getRoutersInfo,
@@ -29,20 +29,18 @@ export async function getInitialState(): Promise<{
loading?: boolean;
fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;
}> {
console.log('getInitialState');

// console.log('getInitialState');
const fetchUserInfo = async () => {
try {
const response = await getUserInfo({
skipErrorHandler: true,
});
response.user.avatar =
response.user.avatar ||
'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png';
return {
...response.user,
avatar: response.user.avatar || require('@/assets/img/avatar-default.png'),
permissions: response.permissions,
roles: response.roles,
roleNames: response.user.roles,
} as API.CurrentUser;
} catch (error) {
console.log(error);
@@ -67,7 +65,7 @@ export async function getInitialState(): Promise<{
}

// ProLayout 支持的api https://procomponents.ant.design/components/layout
export const layout: RunTimeLayoutConfig = ({ initialState }) => {
export const layout: RuntimeConfig['layout'] = ({ initialState }) => {
return {
rightContentRender: () => <RightContent />,
waterMarkProps: {
@@ -129,7 +127,7 @@ export const layout: RunTimeLayoutConfig = ({ initialState }) => {
// </Link>,
// ]
// : [],
menuHeaderRender: undefined,
menuHeaderRender: false,
// 自定义 403 页面
// unAccessible: <div>unAccessible</div>,
// 增加一个 loading 的状态
@@ -137,30 +135,66 @@ export const layout: RunTimeLayoutConfig = ({ initialState }) => {
// if (initialState?.loading) return <PageLoading />;
return <>{children}</>;
},
menuProps: {
onClick: () => {
// 点击菜单项,删除所有的页面 state 缓存
removeAllPageCacheState();
},
// onSelect: (e) => {
// console.log(e);
// },
},
...initialState?.settings,
token: {
sider: {
colorTextMenu: themes['textColor'],
colorTextMenuSelected: themes['primaryColor'],
colorTextMenuActive: themes['primaryColor'],
colorBgMenuItemSelected: 'rgba(197, 232, 255, 0.8)',
colorMenuBackground: themes['siderBGColor'],
},
},
// menuItemRender: (itemProps, defaultDom, props) => {
// console.log('menuItemProps', itemProps);
// console.log('defaultDom', defaultDom);
// console.log('props', props);

// const { pathname } = window.location;
// const isSelected = pathname === itemProps.path;

// // 根据菜单项的状态动态显示不同的 icon
// const icon = isSelected ? 'icon-developmentEnvironment-icon' : 'icon-kaifahuanjing';
// return (
// <Link to={itemProps.path || ''} target={itemProps.target}>
// <KFIcon type={icon} />
// {itemProps.name}
// </Link>
// );
// },
};
};

export async function onRouteChange({ clientRoutes, location }: any) {
export const onRouteChange: RuntimeConfig['onRouteChange'] = async (e) => {
const { location } = e;
const menus = getRemoteMenu();
// console.log('onRouteChange', clientRoutes, location, menus);
console.log('onRouteChange', e);
if (menus === null && location.pathname !== PageEnum.LOGIN) {
console.log('refresh');
history.go(0);
}
}
};

export function patchRoutes({ routes, routeComponents }: any) {
//console.log('patchRoutes', routes, routeComponents);
}
export const patchRoutes: RuntimeConfig['patchRoutes'] = (e) => {
console.log('patchRoutes', e);
};

export async function patchClientRoutes({ routes }: any) {
// console.log('patchClientRoutes', routes);
patchRouteWithRemoteMenus(routes);
}
export const patchClientRoutes: RuntimeConfig['patchClientRoutes'] = (e) => {
console.log('patchClientRoutes', e);
patchRouteWithRemoteMenus(e.routes);
};

export function render(oldRender: () => void) {
// console.log('render get routers', oldRender);
console.log('render');
const token = getAccessToken();
if (!token || token?.length === 0) {
oldRender();


BIN
react-ui/src/assets/img/avatar-default.png View File

Before After
Width: 109  |  Height: 109  |  Size: 9.7 kB

BIN
react-ui/src/assets/img/blue-triangle.png View File

Before After
Width: 17  |  Height: 20  |  Size: 376 B

BIN
react-ui/src/assets/img/functional-material.png View File

Before After
Width: 57  |  Height: 57  |  Size: 8.0 kB

BIN
react-ui/src/assets/img/molecular-material.png View File

Before After
Width: 57  |  Height: 57  |  Size: 8.1 kB

BIN
react-ui/src/assets/img/user-avatar/1.png View File

Before After
Width: 73  |  Height: 73  |  Size: 10 kB

BIN
react-ui/src/assets/img/user-avatar/2.png View File

Before After
Width: 73  |  Height: 73  |  Size: 11 kB

BIN
react-ui/src/assets/img/user-avatar/3.png View File

Before After
Width: 73  |  Height: 73  |  Size: 11 kB

BIN
react-ui/src/assets/img/user-avatar/4.png View File

Before After
Width: 73  |  Height: 73  |  Size: 11 kB

BIN
react-ui/src/assets/img/user-avatar/5.png View File

Before After
Width: 73  |  Height: 73  |  Size: 9.9 kB

BIN
react-ui/src/assets/img/user-avatar/6.png View File

Before After
Width: 73  |  Height: 73  |  Size: 12 kB

BIN
react-ui/src/assets/img/user-avatar/7.png View File

Before After
Width: 73  |  Height: 73  |  Size: 10 kB

BIN
react-ui/src/assets/img/user-avatar/8.png View File

Before After
Width: 73  |  Height: 73  |  Size: 10 kB

BIN
react-ui/src/assets/img/workspace-experiment.png View File

Before After
Width: 147  |  Height: 148  |  Size: 17 kB

BIN
react-ui/src/assets/img/workspace-intro-icon.png View File

Before After
Width: 363  |  Height: 216  |  Size: 71 kB

BIN
react-ui/src/assets/img/workspace-intro.png View File

Before After
Width: 1626  |  Height: 235  |  Size: 159 kB

BIN
react-ui/src/assets/img/workspace-pipeline.png View File

Before After
Width: 169  |  Height: 159  |  Size: 21 kB

BIN
react-ui/src/assets/img/workspace-quick-start.png View File

Before After
Width: 3669  |  Height: 1848  |  Size: 429 kB

BIN
react-ui/src/assets/img/workspace-user.png View File

Before After
Width: 654  |  Height: 214  |  Size: 99 kB

+ 6
- 0
react-ui/src/components/CommonTableCell/index.tsx View File

@@ -1,3 +1,9 @@
/*
* @Author: 赵伟
* @Date: 2024-04-28 14:18:11
* @Description: 自定义 Table 单元格,没有数据时展示 --
*/

function CommonTableCell(text?: string | null) {
return <span>{text ?? '--'}</span>;
}


+ 7
- 1
react-ui/src/components/DateTableCell/index.tsx View File

@@ -1,3 +1,9 @@
/*
* @Author: 赵伟
* @Date: 2024-04-28 14:18:11
* @Description: 自定义 Table 日期类单元格
*/

import dayjs from 'dayjs';

function DateTableCell(text?: string | null) {
@@ -5,7 +11,7 @@ function DateTableCell(text?: string | null) {
return <span>--</span>;
}
if (!dayjs(text).isValid()) {
return <span>日期无效</span>;
return <span>无效的日期</span>;
}
return <span>{dayjs(text).format('YYYY-MM-DD HH:mm:ss')}</span>;
}


+ 4
- 3
react-ui/src/components/IconSelector/IconPicSearcher.tsx View File

@@ -1,6 +1,7 @@
import KFModal from '@/components/KFModal';
import * as AntdIcons from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Modal, Popover, Progress, Result, Spin, Tooltip, Upload } from 'antd';
import { Popover, Progress, Result, Spin, Tooltip, Upload } from 'antd';
import React, { useCallback, useEffect, useState } from 'react';
import './style.less';

@@ -134,7 +135,7 @@ const PicSearcher: React.FC = () => {
>
<AntdIcons.CameraOutlined className="icon-pic-btn" onClick={toggleModal} />
</Popover>
<Modal
<KFModal
title={intl.formatMessage({
id: 'app.docs.components.icon.pic-searcher.title',
defaultMessage: '信息',
@@ -228,7 +229,7 @@ const PicSearcher: React.FC = () => {
)}
</div>
</Spin>
</Modal>
</KFModal>
</div>
);
};


+ 3
- 2
react-ui/src/components/KFIcon/index.tsx View File

@@ -17,15 +17,16 @@ interface KFIconProps extends IconFontProps {
font?: number;
color?: string;
style?: React.CSSProperties;
className?: string;
}

function KFIcon({ type, font = 15, color = '', style = {} }: KFIconProps) {
function KFIcon({ type, font = 15, color = '', style = {}, className }: KFIconProps) {
const iconStyle = {
...style,
fontSize: font,
color,
};
return <Icon type={type} style={iconStyle} />;
return <Icon type={type} className={className} style={iconStyle} />;
}

export default KFIcon;

+ 9
- 16
react-ui/src/components/KFModal/index.less View File

@@ -1,30 +1,29 @@
.kf-modal {
.ant-modal-content {
padding: 20px 67px;
padding: 40px 67px;
background-image: url(/assets/images/modal-back.png);
background-repeat:no-repeat;
background-size:100%;
background-position: top center;
// background: linear-gradient(180deg, #cfdfff 0%, #d4e2ff 9.77%, #ffffff 40%, #ffffff 100%);
border-radius: 21px;
background-repeat: no-repeat;
background-position: top center;
background-size: 100%;
border-radius: 20px;
}
.ant-modal-header {
margin: 20px 0 30px;
margin-bottom: 30px;
background-color: transparent;
}
.ant-modal-footer {
display: flex;
justify-content: center;
margin: 40px 0 20px 0;
margin-top: 40px;

.ant-btn {
height: 40px;
padding: 0 30px;
font-size: 16px;
font-size: @font-size-content;
border-radius: 10px;
}
.ant-btn-default {
color: #1d1d20;
color: @text-color;
background: rgba(22, 100, 255, 0.06);
border-color: transparent;
}
@@ -32,10 +31,4 @@ background-position: top center;
margin-left: 20px;
}
}
.ant-modal-close {
top: 27px;
right: 27px;
width: 26px;
height: 26px;
}
}

+ 9
- 18
react-ui/src/components/KFModal/index.tsx View File

@@ -5,31 +5,22 @@
*/

import ModalTitle from '@/components/ModalTitle';
import { ConfigProvider, Modal, theme, type ModalProps } from 'antd';
import { Modal, type ModalProps } from 'antd';
import classNames from 'classnames';
import { useAntdConfig } from 'umi';
import './index.less';

const { useToken } = theme;

export interface KFModalProps extends ModalProps {
image: string;
image?: string;
}
function KFModal({ title, image, children, className = '', ...rest }: KFModalProps) {
const { token } = useToken();
console.log('token', token);
const antdConfig = useAntdConfig();
console.log('antdConfig', antdConfig);
return (
<ConfigProvider {...antdConfig}>
<Modal
className={classNames(['kf-modal', className])}
{...rest}
title={<ModalTitle title={title} image={image}></ModalTitle>}
>
{children}
</Modal>
</ConfigProvider>
<Modal
className={classNames(['kf-modal', className])}
{...rest}
title={<ModalTitle title={title} image={image}></ModalTitle>}
>
{children}
</Modal>
);
}



+ 5
- 3
react-ui/src/components/KFRadio/index.tsx View File

@@ -1,7 +1,7 @@
/*
* @Author: 赵伟
* @Date: 2024-04-17 16:59:42
* @Description: 自定义Radio
* @Description: 自定义 Radio
*/

import classNames from 'classnames';
@@ -16,12 +16,14 @@ export type KFRadioItem = {
type KFRadioProps = {
items: KFRadioItem[];
value?: string;
style?: React.CSSProperties;
className?: string;
onChange?: (value: string) => void;
};

function KFRadio({ items, value, onChange }: KFRadioProps) {
function KFRadio({ items, value, style, className, onChange }: KFRadioProps) {
return (
<span className={'kf-radio'}>
<span className={classNames('kf-radio', className)} style={style}>
{items.map((item) => {
return (
<span


+ 0
- 2
react-ui/src/components/KFTabs/index.less View File

@@ -1,2 +0,0 @@
.tabs-container {
}

+ 0
- 16
react-ui/src/components/KFTabs/index.tsx View File

@@ -1,16 +0,0 @@
// import { Tabs } from 'antd';
// import { useEffect, useState } from 'react';
// import styles from './index.less';

// function KFTabs() {
// const [iframeUrl, setIframeUrl] = useState('');
// useEffect(() => {}, []);

// return (
// <div className={styles}>
// <Tabs></Tabs>
// </div>
// );
// }

// export default KFTabs;

+ 2
- 2
react-ui/src/components/ModalTitle/index.less View File

@@ -1,11 +1,11 @@
.modal_title {
.modal-title {
display: flex;
align-items: center;
color: @primary-color;
font-weight: 400;
font-size: 20px;

&_image {
&__image {
width: 22px;
margin-right: 10px;
}


+ 12
- 3
react-ui/src/components/ModalTitle/index.tsx View File

@@ -1,15 +1,24 @@
/*
* @Author: 赵伟
* @Date: 2024-04-28 14:18:11
* @Description: 自定义 Modal Title
*/

import classNames from 'classnames';
import React from 'react';
import styles from './index.less';

type ModalTitleProps = {
title: React.ReactNode;
image?: string;
style?: React.CSSProperties;
className?: string;
};

function ModalTitle({ title, image }: ModalTitleProps) {
function ModalTitle({ title, image, style, className }: ModalTitleProps) {
return (
<div className={styles.modal_title}>
<img className={styles.modal_title_image} src={image} alt="" />
<div className={classNames(styles['modal-title'], className)} style={style}>
{image && <img className={styles['modal-title__image']} src={image} alt="" />}
{title}
</div>
);


+ 6
- 6
react-ui/src/components/RightContent/AvatarDropdown.tsx View File

@@ -2,7 +2,7 @@ import { clearSessionToken } from '@/access';
import { setRemoteMenu } from '@/services/session';
import { logout } from '@/services/system/auth';
import { gotoLoginPage } from '@/utils/ui';
import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
import { LogoutOutlined, UserOutlined } from '@ant-design/icons';
import { setAlpha } from '@ant-design/pro-components';
import { useEmotionCss } from '@ant-design/use-emotion-css';
import { history, useModel } from '@umijs/max';
@@ -127,11 +127,11 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
icon: <UserOutlined />,
label: '个人中心',
},
{
key: 'settings',
icon: <SettingOutlined />,
label: '个人设置',
},
// {
// key: 'settings',
// icon: <SettingOutlined />,
// label: '个人设置',
// },
{
type: 'divider' as const,
},


+ 1
- 1
react-ui/src/components/RightContent/index.tsx View File

@@ -48,7 +48,7 @@ const GlobalHeaderRight: React.FC = () => {
>
<QuestionCircleOutlined />
</span> */}
<Avatar menu={false} />
<Avatar menu={true} />
{/* <SelectLang className={actionClassName} /> */}
</div>
);


+ 4
- 2
react-ui/src/components/SubAreaTitle/index.tsx View File

@@ -4,17 +4,19 @@
* @Description: 分区标题
*/

import classNames from 'classnames';
import './index.less';

type SubAreaTitleProps = {
title: string;
image: string;
style?: React.CSSProperties;
className?: string;
};

function SubAreaTitle({ title, image, style }: SubAreaTitleProps) {
function SubAreaTitle({ title, image, style, className }: SubAreaTitleProps) {
return (
<div className={'kf-subarea-title'} style={style}>
<div className={classNames('kf-subarea-title', className)} style={style}>
<img src={image} width={14} />
<span style={{ marginLeft: '8px' }}>{title}</span>
</div>


+ 15
- 132
react-ui/src/global.less View File

@@ -1,6 +1,7 @@
html,
body,
#root {
min-width: 1440px;
height: 100%;
margin: 0;
padding: 0;
@@ -20,9 +21,6 @@ body,
.ant-pro-sider.ant-layout-sider.ant-pro-sider-fixed {
left: unset;
}
.ant-layout-sider-children {
margin-top: 60px !important;
}
canvas {
display: block;
}
@@ -33,58 +31,29 @@ body {
-moz-osx-font-smoothing: grayscale;
}
.ant-pro-layout .ant-pro-layout-content {
padding: 10px;
padding: 0 10px 10px;
background-color: transparent;
}
.ant-pro-layout .ant-pro-layout-bg-list {
background: #f9fafb;
background: @background-color;
}

.ant-menu-light .ant-menu-item-selected {
background: rgba(197, 232, 255, 0.8) !important;
}
.ant-menu-light .ant-menu-item-selected .ant-pro-base-menu-inline-item-text {
// color: #1664ff;
}
.ant-pro-layout .ant-pro-sider .ant-layout-sider-children {
background: #f2f5f7;
}
.ant-pro-base-menu-inline-item-title .ant-pro-base-menu-inline-item-text {
// color: #1d1d20;
font-size: 16px;
}
// .ant-menu-light .ant-menu-item-selected{
// color:#1664ff;
// }

.ant-pro-layout .ant-pro-sider-menu {
padding-top: 40px;
}
.ant-table-wrapper .ant-table-container table > thead > tr:first-child > *:first-child,
.ant-table-wrapper .ant-table-tbody>tr>td:first-child {
padding: 0 30px;
}


.ant-pro-global-header-logo-mix {
width: 257px;
height: 75px;
margin-left: -16px;
padding-left: 28px;
background: #f2f5f7;
border-bottom: 1px solid rgba(233, 237, 240, 1);
border-top-right-radius: 20px;
padding-left: 12px;
}
.ant-pro-layout .ant-pro-sider .ant-layout-sider-children {
border-right: unset;
border-bottom-right-radius: 20px;
}
.ant-pro-base-menu-inline {
// height: 87vh;
background: #f2f5f7;
border-radius: 0px 20px 20px 0px;
}
.ant-pro-layout .ant-pro-layout-content {
background-color: transparent;
}
.ant-drawer .ant-drawer-body {
padding: 0;
}
@@ -102,99 +71,17 @@ body {
padding: 20px 16px;
background-color: #fff;
}
// .ant-table-wrapper .ant-table {
// height: 81vh;
// // overflow-y: auto;
// }
.ant-pro-global-header-logo img {
height: 21px;
}
.ant-pro-layout .ant-layout-sider.ant-pro-sider {
height: 94vh;
height: 100vh;
padding-top: 56px;
}
.ant-pro-layout .ant-pro-layout-container {
height: 100vh;
overflow-y: hidden;
}
.ant-modal-confirm .ant-modal-confirm-paragraph {
margin: 54px 0 auto;
text-align: center;
}
.ant-modal-confirm-confirm .ant-modal-confirm-body > .anticon {
display: none;
}
.ant-modal-confirm .ant-modal-confirm-btns {
margin-top: 30px;
text-align: center;
}
.ant-modal-confirm-btns .ant-btn-default {
width: 110px;
height: 40px;
margin-right: 10px;
// color: #1d1d20;
font-size: 18px;
background: rgba(22, 100, 255, 0.06);
border-color: transparent;
border-radius: 10px;
}
.ant-modal-confirm-btns .ant-btn-default:hover {
background: rgba(22, 100, 255, 0.06);
border-color: transparent;
}
.ant-modal-confirm-btns .ant-btn-primary {
width: 110px;
height: 40px;
font-size: 18px;
border-radius: 10px;
border-radius: 10px;
}
.ant-modal .ant-input-affix-wrapper {
height: 46px;
padding: 1px 11px;
}
.ant-input-textarea-affix-wrapper.ant-input-affix-wrapper{
padding: 0;
}
.ant-modal .ant-select-single {
height: 46px;
}
.ant-modal .ant-select-single .ant-select-selector .ant-select-selection-placeholder {
line-height: 46px;
}
.ant-menu-light.ant-menu-inline .ant-menu-item{
color:#575757;
}
.ant-modal .ant-modal-close-x {
width: 26px;
height: 26px;
color: #272536;
border: 2px solid #272536;
border-radius: 50%;
}
.ant-modal-content {
margin-top: 50px;
margin-left: -130px;
}
.ant-modal .ant-modal-content {
padding: 0;
}
.ant-modal-confirm-body-wrapper {
height: 303px;
background-image: url(/assets/images/modal-back.png);
background-repeat: no-repeat;
background-position: top center;
background-size: 100%;
border-radius: 0;
}
.ant-modal .ant-modal-content {
border-radius: 20px;
}
.ant-modal .ant-modal-close:hover {
background-color: transparent;
}
.ant-modal .ant-modal-footer > .ant-btn + .ant-btn {
margin-left: 20px;
}
.ant-pagination .ant-pagination-item.ant-pagination-item-active {
background: @primary-color;
border-width: 0;
@@ -212,22 +99,18 @@ body {
border: 1px solid #e6e6e6;
}

// ::-webkit-scrollbar-button {
// background: #97a1bd;
// }
::-webkit-scrollbar {
width: 9px;
border-radius: 2px;
width: 8px;
background: transparent;
}
::-webkit-scrollbar-thumb {
// background-color: #9aa3bc!important;
width: 7px;
background: rgba(77, 87, 123, 0.5) !important;
width: 8px;
background: rgba(0, 0, 0, 0.5);
border-radius: 99px;
}
::-webkit-scrollbar-track {
// background-color: #eaf1ff!important;
width: 9px;
background: rgba(22, 100, 255, 0.06) !important;
width: 8px;
background: transparent;
}
ul,
ol {


+ 2
- 2
react-ui/src/hooks/index.ts View File

@@ -41,7 +41,7 @@ export function useVisible(initialValue: boolean) {
setVisible(false);
}, []);

return [visible, open, close];
return [visible, open, close] as const;
}

type Callback<T> = (state: T) => void;
@@ -92,7 +92,7 @@ export function useDomSize<T extends HTMLElement>(
setWidth(domRef.current.offsetWidth);
}
};
const debounceFunc = debounce(setDomHeight, 200);
const debounceFunc = debounce(setDomHeight, 100);

setDomHeight();
window.addEventListener('resize', debounceFunc);


+ 58
- 0
react-ui/src/hooks/pageCacheState.ts View File

@@ -0,0 +1,58 @@
/*
* @Author: 赵伟
* @Date: 2024-04-28 11:49:48
* @Description: 页面状态缓存,pop 回到这个页面的时候,重新构建之前的状态
*/

import { useCallback, useState } from 'react';

const pageKeys: string[] = [];
// let stateCaches: Record<string, any> = {};

// 获取页面缓存
const getCacheState = (key: string) => {
const jsonStr = sessionStorage.getItem(key);
if (jsonStr) {
removeCacheState(key);
try {
return JSON.parse(jsonStr);
} catch (error) {
return undefined;
}
}
return undefined;
};

// 移除页面 state 缓存
const removeCacheState = (key: string) => {
sessionStorage.removeItem(key);
const index = pageKeys.indexOf(key);
if (index !== -1) {
pageKeys.splice(index, 1);
}
};

// 移除所有页面 state 缓存
export const removeAllPageCacheState = () => {
pageKeys.forEach((key) => {
sessionStorage.removeItem(key);
});
};

export const useCacheState = () => {
const { pathname } = window.location;
const key = 'pagecache:' + pathname;

const setCacheState = useCallback(
(state?: any) => {
if (state) {
pageKeys.push(key);
sessionStorage.setItem(key, JSON.stringify(state));
}
},
[key],
);

const [cacheState] = useState(() => getCacheState(key));
return [cacheState, setCacheState] as const;
};

+ 1
- 1
react-ui/src/iconfont/iconfont.js
File diff suppressed because it is too large
View File


+ 85
- 0
react-ui/src/overrides.less View File

@@ -52,3 +52,88 @@
.ant-table-wrapper .ant-table-thead > tr > td {
background-color: #fff;
}

.ant-pro-page-container {
overflow-y: auto;
}

// Input
.ant-input-textarea-affix-wrapper.ant-input-affix-wrapper {
padding: 0;
}

// Modal
.ant-modal {
.ant-modal-close {
top: 27px;
right: 27px;
width: 26px;
height: 26px;
color: @text-color-secondary;
border: 2px solid @text-color-secondary;
border-radius: 50%;

&:hover,
&:active {
color: #272536;
background-color: transparent;
border: 2px solid #272536;
}
}

.ant-input-affix-wrapper {
height: 46px;
padding: 1px 11px;
}

.ant-select-single {
height: 46px;
}

.ant-select-single .ant-select-selector .ant-select-selection-placeholder {
line-height: 46px;
}
}

// Confirm Modal
.ant-modal-confirm {
.ant-modal-content {
padding: 40px 67px;
background-image: url(/assets/images/modal-back.png);
background-repeat: no-repeat;
background-position: top center;
background-size: 100%;
border-radius: 20px;
}
.ant-modal-confirm-body {
.anticon {
display: none;
}
}

.ant-modal-confirm-paragraph {
max-width: 100%;
margin-top: 27px;
text-align: center;
}

.ant-modal-confirm-btns {
margin-top: 40px;
text-align: center;

.ant-btn {
height: 40px;
padding: 0 30px;
font-size: @font-size-content;
border-radius: 10px;
}
.ant-btn-default {
color: @text-color;
background: rgba(22, 100, 255, 0.06);
border-color: transparent;
}
.ant-btn + .ant-btn {
margin-left: 20px;
}
}
}

+ 1
- 1
react-ui/src/pages/Dataset/index.jsx View File

@@ -46,7 +46,7 @@ const Dataset = () => {
const onFinish = (values) => {};
const routeToIntro = (e, record) => {
e.stopPropagation();
navgite({ pathname: '/dataset/datasetIntro' });
navgite({ pathname: '/dataset/dataset' });
};
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo);


+ 11
- 11
react-ui/src/pages/Dataset/personalData.jsx View File

@@ -117,14 +117,14 @@ const PublicData = (React.FC = () => {
};
const chooseDatasetType = (val, item) => {
console.log(val, item);
if (item.path == queryFlow.data_type) {
if (item.id == queryFlow.data_type) {
setActiveType('');
setQueryFlow({ ...queryFlow, data_type: null });
getDatasetlist({ ...queryFlow, data_type: null });
} else {
setActiveType(item.path);
setQueryFlow({ ...queryFlow, data_type: item.path });
getDatasetlist({ ...queryFlow, data_type: item.path });
setActiveType(item.id);
setQueryFlow({ ...queryFlow, data_type: item.id });
getDatasetlist({ ...queryFlow, data_type: item.id });
}
// setQueryFlow({...queryFlow,data_type:item.path},()=>{
// getDatasetlist()
@@ -132,14 +132,14 @@ const PublicData = (React.FC = () => {
};
const chooseDatasetTag = (val, item) => {
console.log(val, item);
if (item.path == queryFlow.data_tag) {
if (item.id == queryFlow.data_tag) {
setActiveTag('');
setQueryFlow({ ...queryFlow, data_tag: null });
getDatasetlist({ ...queryFlow, data_tag: null });
} else {
setActiveTag(item.path);
setQueryFlow({ ...queryFlow, data_tag: item.path });
getDatasetlist({ ...queryFlow, data_tag: item.path });
setActiveTag(item.id);
setQueryFlow({ ...queryFlow, data_tag: item.id });
getDatasetlist({ ...queryFlow, data_tag: item.id });
}
// setQueryFlow({...queryFlow,data_type:item.path},()=>{
// getDatasetlist()
@@ -155,7 +155,7 @@ const PublicData = (React.FC = () => {
const routeToIntro = (e, record) => {
e.stopPropagation();
console.log(record);
navgite({ pathname: `/dataset/datasetIntro/${record.id}` });
navgite({ pathname: `/dataset/dataset/${record.id}` });
};
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo);
@@ -196,7 +196,7 @@ const PublicData = (React.FC = () => {
<div
className={[
Styles.messageBox,
item.path === activeType ? Styles.active : null,
item.id === activeType ? Styles.active : null,
].join(' ')}
onClick={(e) => {
chooseDatasetType(e, item);
@@ -230,7 +230,7 @@ const PublicData = (React.FC = () => {
<div
className={[
Styles.messageBox,
item.path === activeTag ? Styles.active : null,
item.id === activeTag ? Styles.active : null,
].join(' ')}
onClick={(e) => {
chooseDatasetTag(e, item);


+ 11
- 11
react-ui/src/pages/Dataset/publicData.jsx View File

@@ -76,14 +76,14 @@ const PublicData = () => {
};
const chooseDatasetType = (val, item) => {
console.log(val, item);
if (item.path == queryFlow.data_type) {
if (item.id == queryFlow.data_type) {
setActiveType('');
setQueryFlow({ ...queryFlow, data_type: null });
getDatasetlist({ ...queryFlow, data_type: null });
} else {
setActiveType(item.path);
setQueryFlow({ ...queryFlow, data_type: item.path });
getDatasetlist({ ...queryFlow, data_type: item.path });
setActiveType(item.id);
setQueryFlow({ ...queryFlow, data_type: item.id });
getDatasetlist({ ...queryFlow, data_type: item.id });
}
// setQueryFlow({...queryFlow,data_type:item.path},()=>{
// getDatasetlist()
@@ -91,14 +91,14 @@ const PublicData = () => {
};
const chooseDatasetTag = (val, item) => {
console.log(val, item);
if (item.path == queryFlow.data_tag) {
if (item.id == queryFlow.data_tag) {
setActiveTag('');
setQueryFlow({ ...queryFlow, data_tag: null });
getDatasetlist({ ...queryFlow, data_tag: null });
} else {
setActiveTag(item.path);
setQueryFlow({ ...queryFlow, data_tag: item.path });
getDatasetlist({ ...queryFlow, data_tag: item.path });
setActiveTag(item.id);
setQueryFlow({ ...queryFlow, data_tag: item.id });
getDatasetlist({ ...queryFlow, data_tag: item.id });
}
// setQueryFlow({...queryFlow,data_type:item.path},()=>{
// getDatasetlist()
@@ -108,7 +108,7 @@ const PublicData = () => {
const routeToIntro = (e, record) => {
e.stopPropagation();
console.log(record);
navgite({ pathname: `/dataset/datasetIntro/${record.id}` });
navgite({ pathname: `/dataset/dataset/${record.id}` });
};
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo);
@@ -146,7 +146,7 @@ const PublicData = () => {
<div
className={[
Styles.messageBox,
item.path === activeType ? Styles.active : null,
item.id === activeType ? Styles.active : null,
].join(' ')}
onClick={(e) => {
chooseDatasetType(e, item);
@@ -180,7 +180,7 @@ const PublicData = () => {
<div
className={[
Styles.messageBox,
item.path === activeTag ? Styles.active : null,
item.id === activeTag ? Styles.active : null,
].join(' ')}
onClick={(e) => {
chooseDatasetTag(e, item);


+ 0
- 0
react-ui/src/pages/Docs/index.less View File


+ 9
- 0
react-ui/src/pages/Docs/index.tsx View File

@@ -0,0 +1,9 @@
const Docs = () => {
return (
<iframe
style={{ width: '100%', height: '100%', border: 0 }}
src={'/assets/材料科研软件平台使用文档.pdf'}
></iframe>
);
};
export default Docs;

+ 9
- 7
react-ui/src/pages/Experiment/index.less View File

@@ -24,7 +24,7 @@
align-items: center;
width: 100%;
padding: 0 0 0 33px;
color: #1d1d20;
color: @text-color;
font-size: 15px;

& > div {
@@ -76,16 +76,18 @@
.statusBox:hover .statusIcon {
visibility: visible;
}
.experimentBox{
.experimentBox {
height: calc(100% - 20px);
.experimentTable{
.experimentTable {
height: calc(100% - 60px);
:global{
.ant-table-wrapper .ant-table{
:global {
.ant-table-wrapper .ant-table {
// overflow-y: auto;
height: calc(100% - 48px);
}
.ant-table-row-expand-icon-cell {
padding: 0 30px;
}
}
}

}
}

+ 1
- 1
react-ui/src/pages/Experiment/status.ts View File

@@ -16,7 +16,7 @@ export enum ExperimentStatus {
}

type ExperimentStatusKeys = keyof typeof ExperimentStatus;
type ExperimentStatusValues = (typeof ExperimentStatus)[ExperimentStatusKeys];
export type ExperimentStatusValues = (typeof ExperimentStatus)[ExperimentStatusKeys];

export const experimentStatusInfo: Record<ExperimentStatusValues, StatusInfo | undefined> = {
Running: {


+ 2
- 5
react-ui/src/pages/Mirror/create.tsx View File

@@ -11,7 +11,7 @@ import SubAreaTitle from '@/components/SubAreaTitle';
import { CommonTabKeys } from '@/enums';
import { createMirrorReq } from '@/services/mirror';
import { to } from '@/utils/promise';
import { mirrorNameKey } from '@/utils/sessionKeys';
import { getSessionItemThenRemove, mirrorNameKey } from '@/utils/sessionStorage';
import { getFileListFromEvent } from '@/utils/ui';
import { useNavigate } from '@umijs/max';
import { Button, Col, Form, Input, Row, Upload, UploadFile, message, type UploadProps } from 'antd';
@@ -56,14 +56,11 @@ function MirrorCreate() {
};

useEffect(() => {
const name = sessionStorage.getItem(mirrorNameKey);
const name = getSessionItemThenRemove(mirrorNameKey);
if (name) {
form.setFieldValue('name', name);
setNameDisabled(true);
}
return () => {
sessionStorage.removeItem(mirrorNameKey);
};
}, []);

// 创建公网、本地镜像


+ 27
- 12
react-ui/src/pages/Mirror/info.tsx View File

@@ -9,6 +9,7 @@ import KFIcon from '@/components/KFIcon';
import PageTitle from '@/components/PageTitle';
import SubAreaTitle from '@/components/SubAreaTitle';
import { useDomSize } from '@/hooks';
import { useCacheState } from '@/hooks/pageCacheState';
import {
deleteMirrorVersionReq,
getMirrorInfoReq,
@@ -16,17 +17,17 @@ import {
} from '@/services/mirror';
import themes from '@/styles/theme.less';
import { to } from '@/utils/promise';
import { mirrorNameKey } from '@/utils/sessionKeys';
import { mirrorNameKey, setSessionStorageItem } from '@/utils/sessionStorage';
import { modalConfirm } from '@/utils/ui';
import { useNavigate, useParams, useSearchParams } from '@umijs/max';
import {
App,
Button,
Col,
ConfigProvider,
Flex,
Row,
Table,
message,
type TablePaginationConfig,
type TableProps,
} from 'antd';
@@ -56,17 +57,19 @@ function MirrorInfo() {
const navigate = useNavigate();
const urlParams = useParams();
const [searchParams] = useSearchParams();
const [cacheState, setCacheState] = useCacheState();
const [mirrorInfo, setMirrorInfo] = useState<MirrorInfoData>({});
const [tableData, setTableData] = useState<MirrorVersionData[]>([]);
const [topRef, { height: topHeight }] = useDomSize<HTMLDivElement>(0, 0, [mirrorInfo]);
const [total, setTotal] = useState(0);
const [pagination, setPagination] = useState<TablePaginationConfig>({
showSizeChanger: true,
showQuickJumper: true,
current: 1,
pageSize: 10,
});
const [pagination, setPagination] = useState<TablePaginationConfig>(
cacheState?.pagination ?? {
current: 1,
pageSize: 10,
},
);
const isPublic = searchParams.get('isPublic') === 'true';
const { message } = App.useApp();
useEffect(() => {
getMirrorInfo();
}, []);
@@ -115,7 +118,7 @@ function MirrorInfo() {
// 如果是一页的唯一数据,删除时,请求第一页的数据
// 否则直接刷新这一页的数据
// 避免回到第一页
if (tableData.length > 1) {
if (tableData.length === 1) {
setPagination((prev) => ({
...prev,
current: 1,
@@ -146,7 +149,10 @@ function MirrorInfo() {

const createMirrorVersion = () => {
navigate(`/dataset/mirror/create`);
sessionStorage.setItem(mirrorNameKey, mirrorInfo.name || '');
setSessionStorageItem(mirrorNameKey, mirrorInfo.name || '');
setCacheState({
pagination,
});
};

const columns: TableProps<MirrorVersionData>['columns'] = [
@@ -256,13 +262,14 @@ function MirrorInfo() {
</Col>
</Row>
</div>
<Flex justify="space-between" align="center" style={{ marginTop: '40px' }}>
<Flex justify="flex-start" align="center" style={{ marginTop: '40px' }}>
<SubAreaTitle
title="镜像版本"
image={require('@/assets/img/mirror-version.png')}
></SubAreaTitle>
{!isPublic && (
<Button
style={{ marginRight: 0, marginLeft: 'auto' }}
type="default"
onClick={createMirrorVersion}
icon={<KFIcon type="icon-xinjian2" />}
@@ -270,6 +277,14 @@ function MirrorInfo() {
新增镜像版本
</Button>
)}
<Button
style={{ marginLeft: '20px' }}
type="default"
onClick={getMirrorVersionList}
icon={<KFIcon type="icon-shuaxin" />}
>
刷新
</Button>
</Flex>
</div>
<div
@@ -280,7 +295,7 @@ function MirrorInfo() {
dataSource={tableData}
columns={columns}
scroll={{ y: 'calc(100% - 55px)' }}
pagination={{ ...pagination, total }}
pagination={{ ...pagination, total, showSizeChanger: true, showQuickJumper: true }}
onChange={handleTableChange}
rowKey="id"
/>


+ 27
- 24
react-ui/src/pages/Mirror/list.tsx View File

@@ -7,11 +7,12 @@ import CommonTableCell from '@/components/CommonTableCell';
import DateTableCell from '@/components/DateTableCell';
import KFIcon from '@/components/KFIcon';
import { CommonTabKeys } from '@/enums';
import { useCacheState } from '@/hooks/pageCacheState';
import { deleteMirrorReq, getMirrorListReq } from '@/services/mirror';
import themes from '@/styles/theme.less';
import { to } from '@/utils/promise';
import { modalConfirm } from '@/utils/ui';
import { useNavigate, useSearchParams } from '@umijs/max';
import { useNavigate } from '@umijs/max';
import {
Button,
ConfigProvider,
@@ -50,20 +51,17 @@ export type MirrorData = {

function MirrorList() {
const navigate = useNavigate();
const [searchParams, setSearchParams] = useSearchParams();
const isPrivate = searchParams.get('isPublic') === 'false';
const [activeTab, setActiveTab] = useState<string>(
isPrivate ? CommonTabKeys.Private : CommonTabKeys.Public,
);
const [searchText, setSearchText] = useState('');
const [cacheState, setCacheState] = useCacheState();
const [activeTab, setActiveTab] = useState<string>(cacheState?.activeTab ?? CommonTabKeys.Public);
const [searchText, setSearchText] = useState(cacheState?.searchText);
const [tableData, setTableData] = useState<MirrorData[]>([]);
const [total, setTotal] = useState(0);
const [pagination, setPagination] = useState<TablePaginationConfig>({
showSizeChanger: true,
showQuickJumper: true,
current: 1,
pageSize: 10,
});
const [pagination, setPagination] = useState<TablePaginationConfig>(
cacheState?.pagination ?? {
current: 1,
pageSize: 10,
},
);

useEffect(() => {
getMirrorList();
@@ -73,17 +71,12 @@ function MirrorList() {
const hanleTabChange: TabsProps['onChange'] = (value) => {
setSearchText('');
setPagination({
showSizeChanger: true,
showQuickJumper: true,
current: 1,
pageSize: 10,
});
setTotal(0);
setTableData([]);
setActiveTab(value);
setSearchParams([['isPublic', value === CommonTabKeys.Public ? 'true' : 'false']], {
replace: true,
});
};
// 获取镜像列表
const getMirrorList = async (params?: Record<string, any>) => {
@@ -131,11 +124,11 @@ function MirrorList() {

// 查看详情
const toDetail = (record: MirrorData) => {
console.log('record', record);
navigate(`/dataset/mirror/${record.id}?isPublic=${activeTab === CommonTabKeys.Public}`, {
state: {
isPublic: activeTab === CommonTabKeys.Public,
},
navigate(`/dataset/mirror/${record.id}?isPublic=${activeTab === CommonTabKeys.Public}`);
setCacheState({
activeTab,
pagination,
searchText,
});
};

@@ -153,6 +146,11 @@ function MirrorList() {
// 创建镜像
const createMirror = () => {
navigate(`/dataset/mirror/create`);
setCacheState({
activeTab,
pagination,
searchText,
});
};

// 分页切换
@@ -276,7 +274,12 @@ function MirrorList() {
dataSource={tableData}
columns={columns}
scroll={{ y: 'calc(100% - 55px)' }}
pagination={{ ...pagination, total: total }}
pagination={{
...pagination,
total: total,
showSizeChanger: true,
showQuickJumper: true,
}}
onChange={handleTableChange}
rowKey="id"
/>


+ 1
- 1
react-ui/src/pages/Model/index.jsx View File

@@ -46,7 +46,7 @@ const Dataset = () => {
const onFinish = (values) => {};
const routeToIntro = (e, record) => {
e.stopPropagation();
navgite({ pathname: '/dataset/datasetIntro' });
navgite({ pathname: '/dataset/dataset' });
};
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo);


+ 11
- 11
react-ui/src/pages/Model/personalData.jsx View File

@@ -121,14 +121,14 @@ const PublicData = () => {

const chooseModelType = (val, item) => {
console.log(val, item);
if (item.path == queryFlow.model_type) {
if (item.id == queryFlow.model_type) {
setActiveType('');
setQueryFlow({ ...queryFlow, model_type: null });
getModelLists({ ...queryFlow, model_type: null });
} else {
setActiveType(item.path);
setQueryFlow({ ...queryFlow, model_type: item.path });
getModelLists({ ...queryFlow, model_type: item.path });
setActiveType(item.id);
setQueryFlow({ ...queryFlow, model_type: item.id });
getModelLists({ ...queryFlow, model_type: item.id });
}

// setQueryFlow({...queryFlow,data_type:item.path},()=>{
@@ -136,14 +136,14 @@ const PublicData = () => {
// })
};
const chooseModelTag = (val, item) => {
if (item.path == queryFlow.model_tag) {
if (item.id == queryFlow.model_tag) {
setActiveTag('');
setQueryFlow({ ...queryFlow, model_tag: null });
getModelLists({ ...queryFlow, model_tag: null });
} else {
setActiveTag(item.path);
setQueryFlow({ ...queryFlow, model_tag: item.path });
getModelLists({ ...queryFlow, model_tag: item.path });
setActiveTag(item.id);
setQueryFlow({ ...queryFlow, model_tag: item.id });
getModelLists({ ...queryFlow, model_tag: item.id });
}
// setQueryFlow({...queryFlow,data_type:item.path},()=>{
// getDatasetlist()
@@ -152,7 +152,7 @@ const PublicData = () => {
const routeToIntro = (e, record) => {
e.stopPropagation();
console.log(record);
navgite({ pathname: `/dataset/modelIntro/${record.id}` });
navgite({ pathname: `/dataset/model/${record.id}` });
};
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo);
@@ -190,7 +190,7 @@ const PublicData = () => {
<div
className={[
Styles.messageBox,
item.path === activeType ? Styles.active : null,
item.id === activeType ? Styles.active : null,
].join(' ')}
onClick={(e) => {
chooseModelType(e, item);
@@ -231,7 +231,7 @@ const PublicData = () => {
<div
className={[
Styles.messageBox,
item.path === activeTag ? Styles.active : null,
item.id === activeTag ? Styles.active : null,
].join(' ')}
onClick={(e) => {
chooseModelTag(e, item);


+ 11
- 11
react-ui/src/pages/Model/publicData.jsx View File

@@ -77,14 +77,14 @@ const PublicData = () => {
};
const chooseModelType = (val, item) => {
console.log(val, item);
if (item.path == queryFlow.model_type) {
if (item.id == queryFlow.model_type) {
setActiveType('');
setQueryFlow({ ...queryFlow, model_type: null });
getModelLists({ ...queryFlow, model_type: null });
} else {
setActiveType(item.path);
setQueryFlow({ ...queryFlow, model_type: item.path });
getModelLists({ ...queryFlow, model_type: item.path });
setActiveType(item.id);
setQueryFlow({ ...queryFlow, model_type: item.id });
getModelLists({ ...queryFlow, model_type: item.id });
}

// setQueryFlow({...queryFlow,data_type:item.path},()=>{
@@ -92,14 +92,14 @@ const PublicData = () => {
// })
};
const chooseModelTag = (val, item) => {
if (item.path == queryFlow.model_tag) {
if (item.id == queryFlow.model_tag) {
setActiveTag('');
setQueryFlow({ ...queryFlow, model_tag: null });
getModelLists({ ...queryFlow, model_tag: null });
} else {
setActiveTag(item.path);
setQueryFlow({ ...queryFlow, model_tag: item.path });
getModelLists({ ...queryFlow, model_tag: item.path });
setActiveTag(item.id);
setQueryFlow({ ...queryFlow, model_tag: item.id });
getModelLists({ ...queryFlow, model_tag: item.id });
}
// setQueryFlow({...queryFlow,data_type:item.path},()=>{
// getDatasetlist()
@@ -109,7 +109,7 @@ const PublicData = () => {
const routeToIntro = (e, record) => {
e.stopPropagation();
console.log(record);
navgite({ pathname: `/dataset/modelIntro/${record.id}` });
navgite({ pathname: `/dataset/model/${record.id}` });
};
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo);
@@ -147,7 +147,7 @@ const PublicData = () => {
<div
className={[
Styles.messageBox,
item.path === activeType ? Styles.active : null,
item.id === activeType ? Styles.active : null,
].join(' ')}
onClick={(e) => {
chooseModelType(e, item);
@@ -181,7 +181,7 @@ const PublicData = () => {
<div
className={[
Styles.messageBox,
item.path === activeTag ? Styles.active : null,
item.id === activeTag ? Styles.active : null,
].join(' ')}
onClick={(e) => {
chooseModelTag(e, item);


+ 4
- 4
react-ui/src/pages/Monitor/Job/detail.tsx View File

@@ -1,9 +1,9 @@
import { DictValueEnumObj } from '@/components/DictTag';
import KFModal from '@/components/KFModal';
import { getValueEnumLabel } from '@/utils/options';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Button, Descriptions, Modal } from 'antd';
import { Button, Descriptions } from 'antd';
import React, { useEffect } from 'react';

/* *
*
* @author whiteshader@163.com
@@ -39,7 +39,7 @@ const OperlogForm: React.FC<OperlogFormProps> = (props) => {
};

return (
<Modal
<KFModal
width={800}
title={intl.formatMessage({
id: 'monitor.job.detail',
@@ -124,7 +124,7 @@ const OperlogForm: React.FC<OperlogFormProps> = (props) => {
{values.invokeTarget}
</Descriptions.Item>
</Descriptions>
</Modal>
</KFModal>
);
};



+ 3
- 3
react-ui/src/pages/Monitor/Job/edit.tsx View File

@@ -11,7 +11,7 @@ import {
import { FormattedMessage, useIntl } from '@umijs/max';
import { Form, Modal } from 'antd';
import React, { useEffect } from 'react';
import KFModal from '@/components/KFModal';
/**
* 定时任务调度 Edit Form
*
@@ -66,7 +66,7 @@ const JobForm: React.FC<JobFormProps> = (props) => {
};

return (
<Modal
<KFModal
width={640}
title={intl.formatMessage({
id: 'monitor.job.title',
@@ -238,7 +238,7 @@ const JobForm: React.FC<JobFormProps> = (props) => {
]}
/>
</ProForm>
</Modal>
</KFModal>
);
};



+ 4
- 10
react-ui/src/pages/Monitor/JobLog/detail.tsx View File

@@ -1,16 +1,10 @@
import { DictValueEnumObj } from '@/components/DictTag';
import KFModal from '@/components/KFModal';
import { getValueEnumLabel } from '@/utils/options';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Descriptions, Modal } from 'antd';
import { Descriptions } from 'antd';
import React, { useEffect } from 'react';

/* *
*
* @author whiteshader@163.com
* @datetime 2021/09/16
*
* */

export type JobLogFormValueType = Record<string, unknown> & Partial<API.Monitor.JobLog>;

export type JobLogFormProps = {
@@ -33,7 +27,7 @@ const JobLogDetailForm: React.FC<JobLogFormProps> = (props) => {
};

return (
<Modal
<KFModal
width={640}
title={intl.formatMessage({
id: 'monitor.job.log.title',
@@ -95,7 +89,7 @@ const JobLogDetailForm: React.FC<JobLogFormProps> = (props) => {
{getValueEnumLabel(statusOptions, values.status, '未知')}
</Descriptions.Item>
</Descriptions>
</Modal>
</KFModal>
);
};



+ 4
- 4
react-ui/src/pages/Pipeline/components/ResourceSelectorModal/index.less View File

@@ -22,7 +22,7 @@
height: 398px;
margin-right: 15px;
padding: 15px;
background-color: @background-color-primay;
background-color: @background-color-primary;
border: 1px solid @border-color;
border-radius: 8px;

@@ -31,7 +31,7 @@
padding-left: 0;
background-color: transparent;
border-width: 0;
border-bottom: 1px solid @border-color-second;
border-bottom: 1px solid @border-color-secondary;
border-radius: 0;
}
}
@@ -40,7 +40,7 @@
width: calc(100% - 488px - 15px);
height: 398px;
padding: 15px;
background-color: @background-color-primay;
background-color: @background-color-primary;
border: 1px solid @border-color;
border-radius: 8px;

@@ -49,7 +49,7 @@
padding: 3px 0 6px;
color: @text-color;
font-size: @font-size;
border-bottom: 1px solid @border-color-second;
border-bottom: 1px solid @border-color-secondary;
}
&__files {
height: calc(100% - 75px);


+ 4
- 4
react-ui/src/pages/System/Config/edit.tsx View File

@@ -1,4 +1,5 @@
import { DictValueEnumObj } from '@/components/DictTag';
import KFModal from '@/components/KFModal';
import {
ProForm,
ProFormDigit,
@@ -7,9 +8,8 @@ import {
ProFormTextArea,
} from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Form, Modal } from 'antd';
import { Form } from 'antd';
import React, { useEffect } from 'react';

export type ConfigFormData = Record<string, unknown> & Partial<API.System.Config>;

export type ConfigFormProps = {
@@ -53,7 +53,7 @@ const ConfigForm: React.FC<ConfigFormProps> = (props) => {
};

return (
<Modal
<KFModal
width={640}
title={intl.formatMessage({
id: 'system.config.title',
@@ -166,7 +166,7 @@ const ConfigForm: React.FC<ConfigFormProps> = (props) => {
]}
/>
</ProForm>
</Modal>
</KFModal>
);
};



+ 4
- 4
react-ui/src/pages/System/Dept/edit.tsx View File

@@ -1,4 +1,5 @@
import { DictValueEnumObj } from '@/components/DictTag';
import KFModal from '@/components/KFModal';
import {
ProForm,
ProFormDigit,
@@ -7,10 +8,9 @@ import {
ProFormTreeSelect,
} from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Form, Modal } from 'antd';
import { Form } from 'antd';
import { DataNode } from 'antd/es/tree';
import React, { useEffect } from 'react';

export type DeptFormData = Record<string, unknown> & Partial<API.System.Dept>;

export type DeptFormProps = {
@@ -59,7 +59,7 @@ const DeptForm: React.FC<DeptFormProps> = (props) => {
};

return (
<Modal
<KFModal
width={640}
title={intl.formatMessage({
id: 'system.dept.title',
@@ -204,7 +204,7 @@ const DeptForm: React.FC<DeptFormProps> = (props) => {
]}
/>
</ProForm>
</Modal>
</KFModal>
);
};



+ 4
- 4
react-ui/src/pages/System/Dict/edit.tsx View File

@@ -1,4 +1,5 @@
import { DictValueEnumObj } from '@/components/DictTag';
import KFModal from '@/components/KFModal';
import {
ProForm,
ProFormDigit,
@@ -7,9 +8,8 @@ import {
ProFormTextArea,
} from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Form, Modal } from 'antd';
import { Form } from 'antd';
import React, { useEffect } from 'react';

export type DictTypeFormData = Record<string, unknown> & Partial<API.System.DictType>;

export type DictTypeFormProps = {
@@ -52,7 +52,7 @@ const DictTypeForm: React.FC<DictTypeFormProps> = (props) => {
};

return (
<Modal
<KFModal
width={640}
title={intl.formatMessage({
id: 'system.dict.title',
@@ -146,7 +146,7 @@ const DictTypeForm: React.FC<DictTypeFormProps> = (props) => {
]}
/>
</ProForm>
</Modal>
</KFModal>
);
};



+ 4
- 4
react-ui/src/pages/System/DictData/edit.tsx View File

@@ -1,4 +1,5 @@
import { DictValueEnumObj } from '@/components/DictTag';
import KFModal from '@/components/KFModal';
import {
ProForm,
ProFormDigit,
@@ -8,9 +9,8 @@ import {
ProFormTextArea,
} from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Form, Modal } from 'antd';
import { Form } from 'antd';
import React, { useEffect } from 'react';

export type DataFormData = Record<string, unknown> & Partial<API.System.DictData>;

export type DataFormProps = {
@@ -58,7 +58,7 @@ const DictDataForm: React.FC<DataFormProps> = (props) => {
};

return (
<Modal
<KFModal
width={640}
title={intl.formatMessage({
id: 'system.dict.data.title',
@@ -246,7 +246,7 @@ const DictDataForm: React.FC<DataFormProps> = (props) => {
]}
/>
</ProForm>
</Modal>
</KFModal>
);
};



+ 4
- 4
react-ui/src/pages/System/Logininfor/edit.tsx View File

@@ -1,4 +1,5 @@
import { DictValueEnumObj } from '@/components/DictTag';
import KFModal from '@/components/KFModal';
import {
ProForm,
ProFormDigit,
@@ -7,9 +8,8 @@ import {
ProFormTimePicker,
} from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Form, Modal } from 'antd';
import { Form } from 'antd';
import React, { useEffect } from 'react';

export type LogininforFormData = Record<string, unknown> & Partial<API.Monitor.Logininfor>;

export type LogininforFormProps = {
@@ -53,7 +53,7 @@ const LogininforForm: React.FC<LogininforFormProps> = (props) => {
};

return (
<Modal
<KFModal
width={640}
title={intl.formatMessage({
id: 'system.logininfor.title',
@@ -209,7 +209,7 @@ const LogininforForm: React.FC<LogininforFormProps> = (props) => {
]}
/>
</ProForm>
</Modal>
</KFModal>
);
};



+ 6
- 5
react-ui/src/pages/System/Menu/edit.tsx View File

@@ -1,5 +1,6 @@
import { DictValueEnumObj } from '@/components/DictTag';
import IconSelector from '@/components/IconSelector';
import KFModal from '@/components/KFModal';
import { createIcon } from '@/utils/IconUtil';
import {
ProForm,
@@ -10,7 +11,7 @@ import {
ProFormTreeSelect,
} from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Form, Modal } from 'antd';
import { Form } from 'antd';
import { DataNode } from 'antd/es/tree';
import React, { useEffect, useState } from 'react';

@@ -73,7 +74,7 @@ const MenuForm: React.FC<MenuFormProps> = (props) => {
};

return (
<Modal
<KFModal
width={640}
title={intl.formatMessage({
id: 'system.menu.title',
@@ -367,7 +368,7 @@ const MenuForm: React.FC<MenuFormProps> = (props) => {
}}
/>
</ProForm>
<Modal
<KFModal
width={800}
open={iconSelectorOpen}
onCancel={() => {
@@ -382,8 +383,8 @@ const MenuForm: React.FC<MenuFormProps> = (props) => {
setIconSelectorOpen(false);
}}
/>
</Modal>
</Modal>
</KFModal>
</KFModal>
);
};



+ 4
- 4
react-ui/src/pages/System/Notice/edit.tsx View File

@@ -1,4 +1,5 @@
import { DictValueEnumObj } from '@/components/DictTag';
import KFModal from '@/components/KFModal';
import {
ProForm,
ProFormDigit,
@@ -8,9 +9,8 @@ import {
ProFormTextArea,
} from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Form, Modal } from 'antd';
import { Form } from 'antd';
import React, { useEffect } from 'react';

export type NoticeFormData = Record<string, unknown> & Partial<API.System.Notice>;

export type NoticeFormProps = {
@@ -55,7 +55,7 @@ const NoticeForm: React.FC<NoticeFormProps> = (props) => {
};

return (
<Modal
<KFModal
width={640}
title={intl.formatMessage({
id: 'system.notice.title',
@@ -168,7 +168,7 @@ const NoticeForm: React.FC<NoticeFormProps> = (props) => {
]}
/>
</ProForm>
</Modal>
</KFModal>
);
};



+ 4
- 4
react-ui/src/pages/System/Operlog/detail.tsx View File

@@ -1,9 +1,9 @@
import { DictValueEnumObj } from '@/components/DictTag';
import KFModal from '@/components/KFModal';
import { getValueEnumLabel } from '@/utils/options';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Descriptions, Modal } from 'antd';
import { Descriptions } from 'antd';
import React from 'react';

export type OperlogFormData = Record<string, unknown> & Partial<API.Monitor.Operlog>;

export type OperlogFormProps = {
@@ -28,7 +28,7 @@ const OperlogDetailForm: React.FC<OperlogFormProps> = (props) => {
};

return (
<Modal
<KFModal
width={640}
title={intl.formatMessage({
id: 'monitor.operlog.title',
@@ -107,7 +107,7 @@ const OperlogDetailForm: React.FC<OperlogFormProps> = (props) => {
{values.operTime?.toString()}
</Descriptions.Item>
</Descriptions>
</Modal>
</KFModal>
);
};



+ 4
- 4
react-ui/src/pages/System/Post/edit.tsx View File

@@ -1,4 +1,5 @@
import { DictValueEnumObj } from '@/components/DictTag';
import KFModal from '@/components/KFModal';
import {
ProForm,
ProFormDigit,
@@ -7,9 +8,8 @@ import {
ProFormTextArea,
} from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Form, Modal } from 'antd';
import { Form } from 'antd';
import React, { useEffect } from 'react';

export type PostFormData = Record<string, unknown> & Partial<API.System.Post>;

export type PostFormProps = {
@@ -53,7 +53,7 @@ const PostForm: React.FC<PostFormProps> = (props) => {
};

return (
<Modal
<KFModal
width={640}
title={intl.formatMessage({
id: 'system.post.title',
@@ -160,7 +160,7 @@ const PostForm: React.FC<PostFormProps> = (props) => {
]}
/>
</ProForm>
</Modal>
</KFModal>
);
};



+ 4
- 4
react-ui/src/pages/System/Role/components/DataScope.tsx View File

@@ -1,10 +1,10 @@
import KFModal from '@/components/KFModal';
import { Key, ProForm, ProFormDigit, ProFormSelect, ProFormText } from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Checkbox, Col, Form, Modal, Row, Tree } from 'antd';
import { Checkbox, Col, Form, Row, Tree } from 'antd';
import { CheckboxValueType } from 'antd/es/checkbox/Group';
import { DataNode } from 'antd/es/tree';
import React, { useEffect, useState } from 'react';

/* *
*
* @author whiteshader@163.com
@@ -90,7 +90,7 @@ const DataScopeForm: React.FC<DataScopeFormProps> = (props) => {
};

return (
<Modal
<KFModal
width={640}
title={intl.formatMessage({
id: 'system.user.auth.role',
@@ -234,7 +234,7 @@ const DataScopeForm: React.FC<DataScopeFormProps> = (props) => {
</Row>
</ProForm.Item>
</ProForm>
</Modal>
</KFModal>
);
};



+ 3
- 4
react-ui/src/pages/System/Role/components/UserSelectorModal.tsx View File

@@ -1,4 +1,5 @@
import DictTag from '@/components/DictTag';
import KFModal from '@/components/KFModal';
import { getDictValueEnum } from '@/services/system/dict';
import {
ActionType,
@@ -8,9 +9,7 @@ import {
RequestData,
} from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Modal } from 'antd';
import React, { useEffect, useRef, useState } from 'react';

/* *
*
* @author whiteshader@163.com
@@ -90,7 +89,7 @@ const UserSelectorModal: React.FC<DataScopeFormProps> = (props) => {
];

return (
<Modal
<KFModal
width={800}
title={intl.formatMessage({
id: 'system.role.auth.user',
@@ -122,7 +121,7 @@ const UserSelectorModal: React.FC<DataScopeFormProps> = (props) => {
},
}}
/>
</Modal>
</KFModal>
);
};



+ 4
- 4
react-ui/src/pages/System/Role/edit.tsx View File

@@ -1,4 +1,5 @@
import { DictValueEnumObj } from '@/components/DictTag';
import KFModal from '@/components/KFModal';
import {
ProForm,
ProFormDigit,
@@ -7,10 +8,9 @@ import {
ProFormTextArea,
} from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Form, Modal } from 'antd';
import { Form } from 'antd';
import Tree, { DataNode } from 'antd/es/tree';
import React, { useEffect, useState } from 'react';

export type RoleFormData = Record<string, unknown> & Partial<API.System.Role>;

export type RoleFormProps = {
@@ -61,7 +61,7 @@ const RoleForm: React.FC<RoleFormProps> = (props) => {
};

return (
<Modal
<KFModal
width={640}
title={intl.formatMessage({
id: 'system.role.title',
@@ -198,7 +198,7 @@ const RoleForm: React.FC<RoleFormProps> = (props) => {
]}
/>
</ProForm>
</Modal>
</KFModal>
);
};



+ 4
- 11
react-ui/src/pages/System/User/components/AuthRole.tsx View File

@@ -1,15 +1,8 @@
import KFModal from '@/components/KFModal';
import { ProForm, ProFormSelect } from '@ant-design/pro-components';
import { useIntl } from '@umijs/max';
import { Form, Modal } from 'antd';
import { Form } from 'antd';
import React, { useEffect } from 'react';

/* *
*
* @author whiteshader@163.com
* @datetime 2023/02/06
*
* */

export type FormValueType = any & Partial<API.System.Dept>;

export type AuthRoleFormProps = {
@@ -40,7 +33,7 @@ const AuthRoleForm: React.FC<AuthRoleFormProps> = (props) => {
};

return (
<Modal
<KFModal
width={640}
title={intl.formatMessage({
id: 'system.user.auth.role',
@@ -74,7 +67,7 @@ const AuthRoleForm: React.FC<AuthRoleFormProps> = (props) => {
rules={[{ required: true, message: '请选择角色!' }]}
/>
</ProForm>
</Modal>
</KFModal>
);
};



+ 4
- 10
react-ui/src/pages/System/User/components/ResetPwd.tsx View File

@@ -1,15 +1,9 @@
import KFModal from '@/components/KFModal';
import { ProForm, ProFormText } from '@ant-design/pro-components';
import { useIntl } from '@umijs/max';
import { Form, Modal } from 'antd';
import { Form } from 'antd';
import React from 'react';

/* *
*
* @author whiteshader@163.com
* @datetime 2023/02/06
*
* */

export type FormValueType = any & Partial<API.System.User>;

export type UpdateFormProps = {
@@ -44,7 +38,7 @@ const UpdateForm: React.FC<UpdateFormProps> = (props) => {
};

return (
<Modal
<KFModal
width={640}
title={intl.formatMessage({
id: 'system.user.reset.password',
@@ -88,7 +82,7 @@ const UpdateForm: React.FC<UpdateFormProps> = (props) => {
]}
/>
</ProForm>
</Modal>
</KFModal>
);
};



+ 4
- 10
react-ui/src/pages/System/User/edit.tsx View File

@@ -1,4 +1,5 @@
import { DictValueEnumObj } from '@/components/DictTag';
import KFModal from '@/components/KFModal';
import {
ProForm,
ProFormRadio,
@@ -8,17 +9,10 @@ import {
ProFormTreeSelect,
} from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Form, Modal } from 'antd';
import { Form } from 'antd';
import { DataNode } from 'antd/es/tree';
import React, { useEffect } from 'react';

/* *
*
* @author whiteshader@163.com
* @datetime 2023/02/06
*
* */

export type UserFormData = Record<string, unknown> & Partial<API.System.User>;

export type UserFormProps = {
@@ -74,7 +68,7 @@ const UserForm: React.FC<UserFormProps> = (props) => {
};

return (
<Modal
<KFModal
width={640}
title={intl.formatMessage({
id: 'system.user.title',
@@ -260,7 +254,7 @@ const UserForm: React.FC<UserFormProps> = (props) => {
]}
/>
</ProForm>
</Modal>
</KFModal>
);
};



+ 4
- 4
react-ui/src/pages/Tool/Gen/components/PreviewCode.tsx View File

@@ -1,10 +1,10 @@
import KFModal from '@/components/KFModal';
import { useIntl } from '@umijs/max';
import type { TabsProps } from 'antd';
import { Modal, Tabs } from 'antd';
import { Tabs } from 'antd';
import 'highlight.js/styles/base16/material.css';
import React, { useEffect } from 'react';
import Highlight from 'react-highlight';

interface PreviewTableProps {
open: boolean;
data?: any;
@@ -26,7 +26,7 @@ const PreviewTableCode: React.FC<PreviewTableProps> = (props) => {
useEffect(() => {}, []);

return (
<Modal
<KFModal
width={900}
title={intl.formatMessage({
id: 'gen.preview',
@@ -43,7 +43,7 @@ const PreviewTableCode: React.FC<PreviewTableProps> = (props) => {
}}
>
<Tabs defaultActiveKey="1" items={panes}></Tabs>
</Modal>
</KFModal>
);
};



+ 4
- 3
react-ui/src/pages/User/Center/components/AvatarCropper/index.tsx View File

@@ -1,3 +1,4 @@
import KFModal from '@/components/KFModal';
import { uploadAvatar } from '@/services/system/user';
import {
MinusOutlined,
@@ -7,7 +8,7 @@ import {
UploadOutlined,
} from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Button, Col, Modal, Row, Space, Upload, message } from 'antd';
import { Button, Col, Row, Space, Upload, message } from 'antd';
import React, { useEffect, useRef, useState } from 'react';
import { Cropper } from 'react-cropper';
import './cropper.css';
@@ -88,7 +89,7 @@ const AvatarCropperForm: React.FC<AvatarCropperProps> = (props) => {
};
};
return (
<Modal
<KFModal
width={800}
title={intl.formatMessage({
id: 'system.user.modify_avatar',
@@ -137,7 +138,7 @@ const AvatarCropperForm: React.FC<AvatarCropperProps> = (props) => {
</Space>
</Col>
</Row>
</Modal>
</KFModal>
);
};



+ 54
- 0
react-ui/src/pages/Workspace/components/AssetsManagement/index.less View File

@@ -0,0 +1,54 @@
.assets-management {
flex: 1;
width: 100%;
padding: 20px 20px 0;
background-color: white;
border-radius: 4px;

:global {
.ant-select-filled {
background-color: rgba(138, 138, 138, 0.12);
border-radius: 2px;

.ant-select-selection-item {
color: @text-color-secondary !important;
font-size: 13px;
}
}
}

&__title {
color: @text-color;
font-weight: 500;
font-size: @font-size-title;
}

&__increase {
display: inline-block;
margin-top: 12px;
margin-bottom: 30px;
padding: 2px 7px;
color: @primary-color-secondary;
font-size: 13px;
background-color: rgba(187, 210, 255, 0.29);
border-radius: 2px;
}

&__summary {
display: flex;
flex-direction: column;
width: 33.33%;

&__title {
margin-bottom: 12px;
color: @text-color-secondary;
font-size: @font-size;
}

&__value {
color: @text-color;
font-weight: 500;
font-size: 22px;
}
}
}

+ 81
- 0
react-ui/src/pages/Workspace/components/AssetsManagement/index.tsx View File

@@ -0,0 +1,81 @@
import { CommonTabKeys } from '@/enums';
import { getWorkspaceAssetCountReq } from '@/services/workspace';
import { to } from '@/utils/promise';
import { Flex, Select } from 'antd';
import { useEffect, useState } from 'react';
import styles from './index.less';
function AssetsManagement() {
const [type, setType] = useState(CommonTabKeys.Public);
const [assetCounts, setAssetCounts] = useState<{ title: string; value: number }[]>([]);
useEffect(() => {
getWorkspacAssetCount();
}, [type]);
// 获取工作空间资产数量
const getWorkspacAssetCount = async () => {
const params = {
isPublic: type === CommonTabKeys.Public,
};
const [res] = await to(getWorkspaceAssetCountReq(params));
if (res && res.data) {
const { component, dataset, image, model, workflow } = res.data;
const items = [
{
title: '数据集',
value: dataset,
},
{
title: '模型',
value: model,
},
{
title: '镜像',
value: image,
},
{
title: '组件',
value: component,
},
{
title: '代码配置',
value: 0,
},
{
title: '流水线模版',
value: workflow,
},
];
setAssetCounts(items);
}
};

return (
<div className={styles['assets-management']}>
<Flex justify="space-between">
<div className={styles['assets-management__title']}>AI资产</div>
<Select
size="small"
value={type}
style={{ width: 70 }}
onChange={setType}
variant="filled"
options={[
{ value: CommonTabKeys.Public, label: '公开' },
{ value: CommonTabKeys.Private, label: '个人' },
]}
/>
</Flex>

<div className={styles['assets-management__increase']}>今日新增数量:5</div>
<Flex justify="space-between" gap="22px 0" wrap="wrap">
{assetCounts.map((item, index) => (
<div className={styles['assets-management__summary']} key={index}>
<div className={styles['assets-management__summary__title']}>{item.title}</div>
<div className={styles['assets-management__summary__value']}>{item.value}</div>
</div>
))}
</Flex>
</div>
);
}

export default AssetsManagement;

+ 7
- 0
react-ui/src/pages/Workspace/components/ExperimentChart/index.less View File

@@ -0,0 +1,7 @@
.experiment-chart {
width: 295px;
min-width: 295px;
height: 140px;
background-color: @workspace-background;
border-radius: 4px;
}

+ 214
- 0
react-ui/src/pages/Workspace/components/ExperimentChart/index.tsx View File

@@ -0,0 +1,214 @@
import themes from '@/styles/theme.less';
import * as echarts from 'echarts';
import React, { useEffect, useRef } from 'react';
import styles from './index.less';

const color1 = new echarts.graphic.LinearGradient(
0,
0,
0,
1,
[
{ offset: 0, color: '#c73131' },
{ offset: 1, color: '#ff7e96' },
],
false,
);

const color2 = new echarts.graphic.LinearGradient(
0,
0,
0,
1,
[
{ offset: 0, color: '#6ac21d' },
{ offset: 1, color: '#96e850' },
],
false,
);
const color3 = new echarts.graphic.LinearGradient(
0,
0,
0,
1,
[
{ offset: 0, color: '#8c8c8c' },
{ offset: 1, color: '#c8c6c6' },
],
false,
);
const color4 = new echarts.graphic.LinearGradient(
0,
0,
0,
1,
[
{ offset: 0, color: '#ecb934' },
{ offset: 1, color: '#f0864d' },
],
false,
);

const color5 = new echarts.graphic.LinearGradient(
0,
0,
0,
1,
[
{ offset: 0, color: '#7ea9fe' },
{ offset: 1, color: '#1664ff' },
],
false,
);

const color6 = new echarts.graphic.LinearGradient(
0,
0,
0,
1,
[
{ offset: 0, color: 'rgba(255, 255, 255, 0.62)' },
{ offset: 1, color: '#ebf2ff ' },
],
false,
);

export type ExperimentStatistics = {
Failed: number;
Pending: number;
Running: number;
Succeeded: number;
Terminated: number;
};

type ExperimentChartProps = {
style?: React.CSSProperties;
chartData: ExperimentStatistics;
};

function ExperimentChart({ chartData, style }: ExperimentChartProps) {
const chartRef = useRef<HTMLDivElement>(null);
const total =
chartData.Failed +
chartData.Pending +
chartData.Running +
chartData.Succeeded +
chartData.Terminated;
const options: echarts.EChartsOption = {
title: {
show: true,
left: '29%',
top: 'center',
textAlign: 'center',
text: [`{a|${total}}`, '{b|实验状态}'].join('\n'),
textStyle: {
rich: {
a: {
color: themes['textColor'],
fontSize: 20,
fontWeight: 700,
lineHeight: 28,
},
b: {
color: themes['textColorSecondary'],
fontSize: 10,
fontWeight: 'normal',
},
},
},
},
tooltip: {
trigger: 'item',
},
legend: {
top: 'center',
right: '5%',
orient: 'vertical',
icon: 'circle',
itemWidth: 6,
itemGap: 20,
height: 100,
},
color: [color1, color2, color3, color4, color5],
series: [
{
type: 'pie',
radius: ['70%', '80%'],
center: ['30%', '50%'],
avoidLabelOverlap: false,
padAngle: 3,
itemStyle: {
borderRadius: 3,
},
label: {
show: false,
},
emphasis: {
label: {
show: false,
},
},
labelLine: {
show: false,
},
data: [
{ value: chartData.Failed, name: '失败' },
{ value: chartData.Succeeded, name: '成功' },
{ value: chartData.Terminated, name: '中止' },
{ value: chartData.Pending, name: '等待' },
{ value: chartData.Running, name: '运行中' },
],
},
{
type: 'pie',
radius: '60%',
center: ['30%', '50%'],
avoidLabelOverlap: false,
label: {
show: false,
},
tooltip: {
show: false,
},
emphasis: {
label: {
show: false,
},
disabled: true,
},
animation: false,
labelLine: {
show: false,
},
data: [
{
value: 100,
itemStyle: { color: color6, borderColor: 'rgba(22, 100, 255, 0.08)', borderWidth: 1 },
},
],
},
],
};

useEffect(() => {
// 创建一个echarts实例,返回echarts实例
const chart = echarts.init(chartRef.current);

// 设置图表实例的配置项和数据
chart.setOption(options);

// 组件卸载
return () => {
// myChart.dispose() 销毁实例
chart.dispose();
};
}, []);

return (
<div className={styles['experiment-chart']} style={style}>
<div style={{ width: '100%', height: '100%' }} ref={chartRef}></div>
</div>
);
}

export default ExperimentChart;

+ 58
- 0
react-ui/src/pages/Workspace/components/ExperimentTable/index.less View File

@@ -0,0 +1,58 @@
.experiment-table {
flex: 1;
min-width: 500px;
height: 140px;
padding: 12px 24px;
background-color: @workspace-background;
border-radius: 4px;

&__header {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 4px;
color: @text-color;
font-size: @font-size;
line-height: 20px;
}

&__content {
display: flex;
align-items: center;
justify-content: center;
padding: 6px 0;
color: .addAlpha(@text-color, 0.75) [];
font-size: 14px;
line-height: 20px;
border-bottom: 1px solid rgba(234, 234, 234, 0.8);

&:last-child {
border-bottom: 0;
}
}

&__status {
width: 20%;
}

&__duration {
width: 25%;
}

&__date {
width: 35%;
}

&__operation {
width: 20%;

:global {
.ant-btn-link {
height: 20px;
padding: 0;
line-height: 20px;
border: 0;
}
}
}
}

+ 59
- 0
react-ui/src/pages/Workspace/components/ExperimentTable/index.tsx View File

@@ -0,0 +1,59 @@
import KFIcon from '@/components/KFIcon';
import { ExperimentStatusValues, experimentStatusInfo } from '@/pages/Experiment/status';
import { ExperimentInstance } from '@/types';
import { elapsedTime, formatDate } from '@/utils/date';
import { useNavigate } from '@umijs/max';
import { Button } from 'antd';
import styles from './index.less';
type ExperimentTableProps = {
tableData: ExperimentInstance[];
style?: React.CSSProperties;
};

function ExperimentTable({ tableData = [], style }: ExperimentTableProps) {
const navgite = useNavigate();
const gotoExperiment = (record: ExperimentInstance) => {
navgite(`/pipeline/experimentPytorchtext/${record.workflow_id}/${record.id}`);
};

return (
<div className={styles['experiment-table']} style={style}>
<div className={styles['experiment-table__header']}>
<div className={styles['experiment-table__status']}>状态</div>
<div className={styles['experiment-table__duration']}>运行时长</div>
<div className={styles['experiment-table__date']}>开始时间</div>
<div className={styles['experiment-table__operation']}>操作</div>
</div>
{tableData?.map((item) => (
<div className={styles['experiment-table__content']} key={item.id}>
<div className={styles['experiment-table__status']} style={{ paddingLeft: '6.5px' }}>
<img
src={experimentStatusInfo[item.status as ExperimentStatusValues]?.icon}
width={17}
height={17}
/>
</div>
<div className={styles['experiment-table__duration']}>
{elapsedTime(
new Date(item.create_time),
item.finish_time ? new Date(item.finish_time) : new Date(),
)}
</div>
<div className={styles['experiment-table__date']}>{formatDate(item.create_time)}</div>
<div className={styles['experiment-table__operation']}>
<Button
size="small"
type="link"
icon={<KFIcon type="icon-xiangqing2" font={14} />}
onClick={() => gotoExperiment(item)}
>
详情
</Button>
</div>
</div>
))}
</div>
);
}

export default ExperimentTable;

+ 60
- 0
react-ui/src/pages/Workspace/components/QuickStart/WorkArrow.tsx View File

@@ -0,0 +1,60 @@
import styles from './index.less';

type WorkArrowProps = {
width?: number;
height?: number;
x: number;
y: number;
arrowLeft: number;
arrorwTop: number;
borderLeft?: number;
borderTop?: number;
borderRight?: number;
borderBottom?: number;
arrrowAngle?: number;
};

function WorkArrow({
width = 1,
height = 1,
x,
y,
arrowLeft,
arrorwTop,
borderLeft = 0,
borderTop = 0,
borderRight = 0,
borderBottom = 0,
arrrowAngle = 0,
}: WorkArrowProps) {
return (
<div
className={styles['work-arrow']}
style={{
left: `${x}px`,
top: `${y}px`,
width: `${width}px`,
height: `${height}px`,
borderLeftWidth: `${borderLeft}px`,
borderTopWidth: `${borderTop}px`,
borderRightWidth: `${borderRight}px`,
borderBottomWidth: `${borderBottom}px`,
}}
>
<img
className={styles['work-arrow__img']}
src={require('@/assets/img/blue-triangle.png')}
alt=""
width={10}
height={9}
style={{
left: `${arrowLeft}px`,
top: `${arrorwTop}px`,
transform: `rotate(${arrrowAngle}deg)`,
}}
/>
</div>
);
}

export default WorkArrow;

+ 31
- 0
react-ui/src/pages/Workspace/components/QuickStart/WorkFlow.tsx View File

@@ -0,0 +1,31 @@
import { Button } from 'antd';
import styles from './index.less';

type WorkFlowProps = {
content: string;
buttonText: string;
tips?: string;
onClick?: () => void;
buttonTop?: number;
x: number;
y: number;
};

function WorkFlow({ content, buttonText, tips, buttonTop = 20, x, y, onClick }: WorkFlowProps) {
return (
<div className={styles['work-flow']} style={{ left: `${x}px`, top: `${y}px` }}>
{tips && <div className={styles['work-flow__tips']}>{tips}</div>}
<div className={styles['work-flow__content']}>{content}</div>
<Button
className={styles['work-flow__button']}
type="primary"
style={{ marginTop: `${buttonTop}px` }}
onClick={onClick}
>
{buttonText}
</Button>
</div>
);
}

export default WorkFlow;

+ 96
- 0
react-ui/src/pages/Workspace/components/QuickStart/index.less View File

@@ -0,0 +1,96 @@
.quick-start {
width: calc(100% - 326px);
padding: 20px 30px;
background-color: white;
border-radius: 4px;

&__title {
margin-bottom: 20px;
color: @text-color;
font-weight: 500;
font-size: @font-size-title;
}

&__content {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 610px;
background-image: url(@/assets/img/workspace-quick-start.png);
background-repeat: no-repeat;
background-position: top left;
background-size: 100% 100%;

&__canvas {
position: relative;
width: 1223px;
height: 610px;
transform-origin: center left;

&__model {
position: absolute;
top: 358px;
left: 920px;
display: flex;
flex-direction: column;
align-items: center;
color: @primary-color;
font-size: @font-size;
}

&__task {
position: absolute;
top: 110px;
left: 603px;
display: flex;
align-items: center;
justify-content: center;
width: 131px;
height: 41px;
color: @primary-color;
font-size: @font-size;
background-color: rgba(22, 100, 255, 0.05);
border: 1px dashed @primary-color;
border-radius: 4px;
box-shadow: 0px 0px 6px rgba(22, 100, 255, 0.07);
}
}
}
}

.work-flow {
position: absolute;
width: 192px;
padding: 15px;
background-color: white;
border: 1px solid;
border-color: rgba(22, 100, 255, 0.08);
border-radius: 0px 8px 0px 0px;
box-shadow: 0px 0px 10px rgba(22, 100, 255, 0.06);

&__content {
color: @text-color-secondary;
font-size: @font-size;
}

&__tips {
position: absolute;
top: -16px;
left: 0;
padding: 4px 10px;
color: white;
font-size: @font-size;
background-color: #333333;
}
}

.work-arrow {
position: absolute;
border: 0 dashed @primary-color;

&__img {
position: absolute;
}
}

+ 149
- 0
react-ui/src/pages/Workspace/components/QuickStart/index.tsx View File

@@ -0,0 +1,149 @@
import KFIcon from '@/components/KFIcon';
import { useNavigate } from '@umijs/max';
import { debounce } from 'lodash';
import { useEffect, useState } from 'react';
import WorkArrow from './WorkArrow';
import WorkFlow from './WorkFlow';
import styles from './index.less';

function QuickStart() {
const navgite = useNavigate();
const [scale, setScale] = useState(1);

useEffect(() => {
const changeScale = () => {
const width = document.body.offsetWidth - 256 - 80 - 60 - 326 - 15 - 8;
const ratio = width >= 1223 ? 1 : width / 1223;
setScale(ratio);
};

const debounceFunc = debounce(changeScale, 16);
window.addEventListener('resize', debounceFunc);

return () => {
window.removeEventListener('resize', debounceFunc);
};
}, []);

return (
<div className={styles['quick-start']}>
<div className={styles['quick-start__title']}>快速开始</div>
<div className={styles['quick-start__content']}>
<div
className={styles['quick-start__content__canvas']}
style={{ transform: `scale(${scale})` }}
>
<WorkFlow
content="为开发者提供数据智能标注与数据回流服务"
buttonText="数据准备"
buttonTop={40}
x={20}
y={309}
onClick={() => navgite('/datasetPreparation/datasetAnnotation')}
/>
<WorkFlow
content="为开发者提供定制化编辑器,开发者可根据自己需求选择配置,保存编译器中的调试环境为镜像供训练使用"
buttonText="开发环境"
buttonTop={20}
x={248}
y={301}
onClick={() => navgite('/developmentEnvironment')}
/>
<WorkFlow
content="为开发者提供定制化编辑器,开发者可根据自己需求选择配置,保存编译器中的调试环境为镜像供训练使用"
tips="可视化建模Designer"
buttonText="流水线"
buttonTop={20}
x={476}
y={276}
onClick={() => navgite('/pipeline/pipelineText')}
/>
<WorkFlow
content="开发者可以在这里运行流水线模板,产生实验实例,对比实验训练过程与产生的实验训练数据"
buttonText="实验"
buttonTop={40}
x={699}
y={295}
onClick={() => navgite('/pipeline/experimentText')}
/>
<WorkFlow
content="支持异构硬件(CPU/GPU)的模型加载,高吞吐,低延迟;支持大规模复杂模型的一键部署,实时弹性扩缩容;提供完整的运维监控体系。"
tips="模型在线服务"
buttonText="模型在线部署"
buttonTop={20}
x={1010}
y={263}
onClick={() => navgite('/modelDseployment')}
/>
<div className={styles['quick-start__content__canvas__model']}>
<KFIcon type="icon-moxingguanli" font={38} />
<span>模型管理</span>
</div>
<div className={styles['quick-start__content__canvas__task']}>
<KFIcon type="icon-tiaoduguanli" font={13} style={{ marginRight: '5px' }} />
<span>任务自动调度</span>
</div>
<WorkArrow
x={213}
y={378}
width={22}
height={1}
arrowLeft={22}
arrorwTop={-4}
borderBottom={1}
/>
<WorkArrow
x={441}
y={378}
width={22}
height={1}
arrowLeft={22}
arrorwTop={-4}
borderBottom={1}
/>
<WorkArrow
x={893}
y={378}
width={22}
height={1}
arrowLeft={22}
arrorwTop={-4}
borderBottom={1}
/>
<WorkArrow
x={974}
y={378}
width={22}
height={1}
arrowLeft={22}
arrorwTop={-4}
borderBottom={1}
/>
<WorkArrow
x={532}
y={139}
width={54}
height={125}
arrowLeft={54}
arrorwTop={-4}
borderLeft={1}
borderTop={1}
/>
<WorkArrow
x={740}
y={127}
width={49}
height={156}
arrowLeft={44}
arrorwTop={156}
arrrowAngle={90}
borderRight={1}
borderTop={1}
/>
</div>
</div>
</div>
);
}

export default QuickStart;

+ 41
- 0
react-ui/src/pages/Workspace/components/TotalStatistics/index.less View File

@@ -0,0 +1,41 @@
.total-statistics {
display: flex;
align-items: center;
justify-content: center;
width: 400px;
height: 140px;
background-color: @workspace-background;
border-radius: 4px;

&__icon {
width: 85px;
height: 80px;
margin-right: 40px;
}

&__title {
position: relative;
margin-bottom: 6px;
color: @text-color-secondary;
font-size: @font-size-content;
}

&__title-shadow {
position: absolute;
bottom: 6px;
left: 0;
width: 79px;
height: 6px;
background-color: linear-gradient(
87.07deg,
rgba(22, 100, 255, 0.6) 0%,
rgba(22, 100, 255, 0) 100%
);
}

&__count {
color: @text-color;
font-weight: 700;
font-size: 25px;
}
}

+ 25
- 0
react-ui/src/pages/Workspace/components/TotalStatistics/index.tsx View File

@@ -0,0 +1,25 @@
import styles from './index.less';

type TotalStatisticsProps = {
icon: string;
title: string;
count?: string | number;
style?: React.CSSProperties;
};

function TotalStatistics({ icon = '', title = '', count = 0, style }: TotalStatisticsProps) {
return (
<div className={styles['total-statistics']} style={style}>
<img className={styles['total-statistics__icon']} src={icon} />
<div>
<div className={styles['total-statistics__title']}>
<span>{title}</span>
<div className={styles['total-statistics__title-shadow']}></div>
</div>
<div className={styles['total-statistics__count']}>{count ?? '--'}</div>
</div>
</div>
);
}

export default TotalStatistics;

+ 65
- 0
react-ui/src/pages/Workspace/components/UserSpace/index.less View File

@@ -0,0 +1,65 @@
.user-space {
margin-bottom: 16px;
padding-bottom: 20px;
background-color: white;
border-radius: 4px;
&__title {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 107px;
color: @text-color;
font-size: @font-size-title;
background-image: url(@/assets/img/workspace-user.png);
background-repeat: no-repeat;
background-position: top left;
background-size: 100% 100%;
}

&__avatar {
position: relative;
top: -28px;
width: 56px;
height: 56px;
}

&__name {
margin-top: -20px;
margin-bottom: 8px;
color: @text-color;
font-size: @font-size-content;
}

&__role {
display: inline-block;
padding: 1px 7px;
color: @primary-color-secondary;
font-size: 13px;
background-color: rgba(187, 210, 255, 0.29);
border-radius: 2px;
}

&__participant {
&__title {
color: @text-color-secondary;
font-size: @font-size-content;
}

&__count {
width: 24px;
height: 24px;
color: #8a8a8a;
font-size: 12px;
line-height: 24px;
text-align: center;
background-color: rgba(153, 153, 153, 0.13);
border-radius: 50%;
}

&__user {
width: 36px;
height: 36px;
}
}
}

+ 47
- 0
react-ui/src/pages/Workspace/components/UserSpace/index.tsx View File

@@ -0,0 +1,47 @@
import { useModel } from '@umijs/max';
import { Divider, Flex, Space } from 'antd';
import styles from './index.less';

type UserSpaceProps = {
users: any[];
};

function UserSpace({ users = [] }: UserSpaceProps) {
const { initialState } = useModel('@@initialState');
const { currentUser } = initialState || {};

return (
<div className={styles['user-space']}>
<div className={styles['user-space__title']}>工作空间管理</div>
<div style={{ padding: '0 20px' }}>
<img className={styles['user-space__avatar']} src={currentUser?.avatar} alt="" />
<div className={styles['user-space__name']}>{currentUser?.nickName}</div>
<div className={styles['user-space__role']}>{currentUser?.roleNames?.[0]?.roleName}</div>
<Divider
dashed
style={{ borderColor: 'rgba(22, 100, 255, 0.19)', margin: '20px 0' }}
></Divider>
<div className={styles['user-space__participant']}>
<Space align="center" size={10} style={{ marginBottom: '20px' }}>
<div className={styles['user-space__participant__title']}>参与者</div>
<div className={styles['user-space__participant__count']}>8</div>
</Space>
<Flex align="center" gap={12} wrap="wrap">
{users?.map((item, index) => {
return (
<img
className={styles['user-space__participant__user']}
key={index}
src={require(`@/assets/img/user-avatar/${index + 1}.png`)}
alt=""
/>
);
})}
</Flex>
</div>
</div>
</div>
);
}

export default UserSpace;

+ 43
- 0
react-ui/src/pages/Workspace/components/WorkspaceIntro/index.less View File

@@ -0,0 +1,43 @@
.workspace-intro {
display: flex;
align-items: center;
margin-bottom: 16px;
padding: 0 30px;
background-image: url(@/assets/img/workspace-intro.png);
background-repeat: no-repeat;
background-position: top right;
background-size: 100% 100%;
border-radius: 4px;

&__left {
padding: 30px 0 34px;
}

&__right {
display: flex;
flex: 1;
align-items: center;
justify-content: center;
}

&__title {
margin-bottom: 20px;
color: @text-color;
font-weight: 500;
font-size: 20px;
}

&__content {
max-width: 980px;
margin-bottom: 20px;
color: @text-color-secondary;
font-size: @font-size-title;
line-height: 1.8;
letter-spacing: 1px;
}

&__icon {
width: 363px;
height: 216px;
}
}

+ 43
- 0
react-ui/src/pages/Workspace/components/WorkspaceIntro/index.tsx View File

@@ -0,0 +1,43 @@
import { Button } from 'antd';
import styles from './index.less';

function WorkspaceIntro() {
return (
<div className={styles['workspace-intro']}>
<div className={styles['workspace-intro__left']}>
<div className={styles['workspace-intro__title']}>自主实验平台</div>
<div className={styles['workspace-intro__content']}>
材料领域的自主实验系统是一种用于材料研究和开发的技术平台,它旨在提供实验数据收集、分析和可视化等功能,
以支持材料工程师、科学家和研究人员在材料设计、性能评估和工艺优化方面的工作
</div>
<div className={styles['workspace-intro__buttons']}>
<Button
type="primary"
style={{ marginRight: '20px' }}
icon={
<img src={require('@/assets/img/functional-material.png')} width={19} height={19} />
}
>
功能材料自主实验系统
</Button>
<Button
type="default"
icon={
<img src={require('@/assets/img/molecular-material.png')} width={19} height={19} />
}
>
分子材料自主实验系统
</Button>
</div>
</div>
<div className={styles['workspace-intro__right']}>
<img
className={styles['workspace-intro__icon']}
src={require('@/assets/img/workspace-intro-icon.png')}
/>
</div>
</div>
);
}

export default WorkspaceIntro;

+ 45
- 0
react-ui/src/pages/Workspace/index.less View File

@@ -0,0 +1,45 @@
.workspace {
height: 100%;
padding: 20px 30px 10px;
overflow-y: auto;
background-color: linear-gradient(#ecf2fe, #f9fafb);

&__overview {
margin-bottom: 16px;
padding: 20px 30px;
background-color: white;
border-radius: 4px;

&__title {
margin-bottom: 20px;
color: @text-color;
font-weight: 500;
font-size: @font-size-title;
}

&__content {
display: flex;
gap: 15px;
align-items: center;

@media screen and (max-width: 1800px) {
flex-wrap: wrap;
}
}
}

&__quick-start {
display: flex;
gap: 15px;
align-items: flex-start;
width: 100%;
}

&__user {
display: flex;
flex-direction: column;
width: 326px;
min-width: 326px;
height: 700px;
}
}

+ 71
- 0
react-ui/src/pages/Workspace/index.tsx View File

@@ -0,0 +1,71 @@
import { getWorkspaceOverviewReq } from '@/services/workspace';
import { ExperimentInstance } from '@/types';
import { to } from '@/utils/promise';
import { useEffect, useState } from 'react';
import AssetsManagement from './components/AssetsManagement';
import ExperimentChart, { type ExperimentStatistics } from './components/ExperimentChart';
import ExperitableTable from './components/ExperimentTable';
import QuickStart from './components/QuickStart';
import TotalStatistics from './components/TotalStatistics';
import UserSpace from './components/UserSpace';
import WorkspaceIntro from './components/WorkspaceIntro';
import styles from './index.less';

type OverviewData = {
workflowCount: number;
runningExperimentInsCount: number;
experimentInsStatus: ExperimentStatistics;
latestExperimentInsList: ExperimentInstance[];
};

function Workspace() {
const [overviewData, setOverviewData] = useState<OverviewData>();
const users: number[] = new Array(8).fill(0);
useEffect(() => {
getWorkspaceOverview();
}, []);

// 获取工作空间概况
const getWorkspaceOverview = async () => {
const [res] = await to(getWorkspaceOverviewReq());
if (res && res.data) {
setOverviewData(res.data);
}
};

return (
<div className={styles.workspace}>
<WorkspaceIntro></WorkspaceIntro>
<div className={styles['workspace__overview']}>
<div className={styles['workspace__overview__title']}>运行概览</div>
<div className={styles['workspace__overview__content']}>
<TotalStatistics
icon={require('@/assets/img/workspace-pipeline.png')}
title="流水线总数"
count={overviewData?.workflowCount}
/>
<TotalStatistics
icon={require('@/assets/img/workspace-experiment.png')}
title="正在运行实例总数"
count={overviewData?.runningExperimentInsCount}
/>
<ExperitableTable
tableData={overviewData?.latestExperimentInsList || []}
></ExperitableTable>
{overviewData?.experimentInsStatus && (
<ExperimentChart chartData={overviewData?.experimentInsStatus}></ExperimentChart>
)}
</div>
</div>
<div className={styles['workspace__quick-start']}>
<QuickStart></QuickStart>
<div className={styles['workspace__user']}>
<UserSpace users={users}></UserSpace>
<AssetsManagement></AssetsManagement>
</div>
</div>
</div>
);
}

export default Workspace;

+ 2
- 1
react-ui/src/requestConfig.ts View File

@@ -46,7 +46,8 @@ export const requestConfig: RequestConfig = {
],
responseInterceptors: [
(response: any) => {
const { status, data } = response;
const { status, data } = response || {};
console.log('response2', response);
if (status >= 200 && status < 300) {
if (data && (data instanceof Blob || data.code === 200)) {
return response;


+ 3
- 0
react-ui/src/services/ant-design-pro/typings.d.ts View File

@@ -14,6 +14,9 @@ declare namespace API {
};
address?: string;
phone?: string;
roleNames?: {
roleName?: string;
}[];
};

type ErrorResponse = {


+ 1
- 1
react-ui/src/services/mirror/index.ts View File

@@ -1,7 +1,7 @@
/*
* @Author: 赵伟
* @Date: 2024-04-16 14:29:44
* @Description:
* @Description: 镜像管理接口
*/
import { request } from '@umijs/max';



+ 21
- 0
react-ui/src/services/workspace/index.ts View File

@@ -0,0 +1,21 @@
/*
* @Author: 赵伟
* @Date: 2024-04-16 14:29:44
* @Description: 工作空间接口
*/
import { request } from '@umijs/max';

// 获取工作空间概况
export function getWorkspaceOverviewReq() {
return request(`/api/mmp/workspace/overview`, {
method: 'GET',
});
}

// 获取工作空间概况
export function getWorkspaceAssetCountReq(params: any) {
return request(`/api/mmp/workspace/assetCount`, {
method: 'GET',
params,
});
}

+ 23
- 2
react-ui/src/styles/theme.less View File

@@ -6,6 +6,7 @@

// 颜色
@primary-color: #1664ff; // 主色调
@primary-color-secondary: #4e89ff;
@primary-color-hover: #69b1ff;
@background-color: #f9fafb; // 页面背景颜色
@text-color: #1d1d20;
@@ -15,17 +16,35 @@
@warning-color: #f98e1b;

@border-color: rgba(22, 100, 255, 0.3);
@border-color-second: rgba(22, 100, 255, 0.1);
@background-color-primay: rgba(22, 100, 255, 0.03);
@border-color-secondary: rgba(22, 100, 255, 0.1);
@background-color-primary: rgba(22, 100, 255, 0.03);
@background-color-gray: rgba(4, 3, 3, 0.06);

@heading-color: rgba(0, 0, 0, 0.85);
@input-icon-hover-color: rgba(0, 0, 0, 0.85);
@border-color-base: #d9d9d9;
@link-hover-color: #69b1ff;
@sider-background-color: #f2f5f7;

@workspace-background: linear-gradient(
179.03deg,
rgba(138, 138, 138, 0.06) 0%,
rgba(22, 100, 255, 0.02) 100%
);

// 字体大小
@font-size: 15px;
@font-size-title: 18px;
@font-size-content: 16px;

// 函数
.addAlpha(@color, @alpha) {
@red: red(@color);
@green: green(@color);
@blue: blue(@color);

@result: rgba(@red, @green, @blue, @alpha);
}

// 导出变量
:export {
@@ -34,5 +53,7 @@
errorColor: @error-color;
warningColor: @warning-color;
textColor: @text-color;
textColorSecondary: @text-color-secondary;
fontSize: @font-size;
siderBGColor: @sider-background-color;
}

+ 16
- 0
react-ui/src/types.ts View File

@@ -12,3 +12,19 @@ export type PipelineGlobalParam = {
param_value: number | string | boolean;
is_sensitive: number;
};

// 实验实例
export type ExperimentInstance = {
id: number;
experiment_id: number;
workflow_id: number;
create_time: string;
finish_time: string;
update_time: string;
status: string;
argo_ins_name: string;
argo_ins_ns: string;
nodes_result: string;
nodes_status: string;
global_param: PipelineGlobalParam[];
};

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

Loading…
Cancel
Save