diff --git a/.gitignore b/.gitignore
index 9be5c6a9..54c883c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -62,3 +62,6 @@ mvnw
*storybook.log
/react-ui/docs
+/react-ui/types/tsconfig.tsbuildinfo
+/react-ui/storybook-static
+/react-ui/.storybook/scripts
diff --git a/react-ui/.storybook/babel-plugin-auto-css-modules.js b/react-ui/.storybook/babel-plugin-auto-css-modules.js
index 9c7709ff..660744b4 100644
--- a/react-ui/.storybook/babel-plugin-auto-css-modules.js
+++ b/react-ui/.storybook/babel-plugin-auto-css-modules.js
@@ -4,7 +4,6 @@ export default function(babel) {
visitor: {
ImportDeclaration(path) {
const source = path.node.source.value;
- // console.log("zzzz", source);
if (source.endsWith('.less')) {
if (path.node.specifiers.length > 0) {
path.node.source.value += "?modules";
diff --git a/react-ui/.storybook/main.ts b/react-ui/.storybook/main.ts
index 1512aa48..c21ab45b 100644
--- a/react-ui/.storybook/main.ts
+++ b/react-ui/.storybook/main.ts
@@ -16,7 +16,11 @@ const config: StorybookConfig = {
name: '@storybook/react-webpack5',
options: {},
},
- staticDirs: ['../public', { from: '../docs', to: '/docs' }],
+ staticDirs: [
+ '../public',
+ { from: '../docs', to: '/docs' },
+ { from: '../docs/index.html', to: '/docs/index.html' },
+ ],
docs: {
defaultName: 'Documentation',
},
diff --git a/react-ui/Dockerfile b/react-ui/Dockerfile
new file mode 100644
index 00000000..077c862f
--- /dev/null
+++ b/react-ui/Dockerfile
@@ -0,0 +1,3 @@
+# Dockerfile
+FROM nginx:alpine
+COPY storybook-static/ /usr/share/nginx/html
\ No newline at end of file
diff --git a/react-ui/config/routes.ts b/react-ui/config/routes.ts
index ed228aa8..a1bb3d0c 100644
--- a/react-ui/config/routes.ts
+++ b/react-ui/config/routes.ts
@@ -143,6 +143,11 @@ export default [
path: 'compare',
component: './Experiment/Comparison/index',
},
+ {
+ name: '实验可视化对比',
+ path: 'compare-visual',
+ component: './Experiment/Aim/index',
+ },
],
},
{
@@ -307,7 +312,18 @@ export default [
{
name: '镜像详情',
path: 'info/:id',
- component: './Mirror/Info',
+ routes: [
+ {
+ name: '镜像详情',
+ path: '',
+ component: './Mirror/Info',
+ },
+ {
+ name: '新增镜像版本',
+ path: 'add-version',
+ component: './Mirror/Create',
+ },
+ ],
},
{
name: '创建镜像',
diff --git a/react-ui/package.json b/react-ui/package.json
index e4d95504..c614d920 100644
--- a/react-ui/package.json
+++ b/react-ui/package.json
@@ -16,7 +16,7 @@
"docker:dev": "docker-compose -f ./docker/docker-compose.dev.yml up",
"docker:push": "npm run docker-hub:build && npm run docker:tag && docker push antdesign/ant-design-pro",
"docker:tag": "docker tag ant-design-pro antdesign/ant-design-pro",
- "docs": "typedoc --entryPointStrategy expand --entryPoints 'src/utils' --skipErrorChecking --out docs",
+ "docs": "typedoc",
"gh-pages": "gh-pages -d dist",
"i18n-remove": "pro i18n-remove --locale=zh-CN --write",
"postinstall": "max setup",
@@ -40,6 +40,7 @@
"start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev max dev",
"storybook": "storybook dev -p 6006",
"storybook-build": "storybook build",
+ "storybook-deploy": "./.storybook/scripts/upload-deploy.sh",
"storybook-docs": "storybook dev --docs",
"storybook-docs-build": "storybook build --docs",
"test": "jest",
diff --git a/react-ui/public/fonts/TaoBaoMaiCaiTi-Regular.otf b/react-ui/public/fonts/TaoBaoMaiCaiTi-Regular.otf
deleted file mode 100644
index 21ba9622..00000000
Binary files a/react-ui/public/fonts/TaoBaoMaiCaiTi-Regular.otf and /dev/null differ
diff --git a/react-ui/public/fonts/TaoBaoMaiCaiTi-Regular.ttf b/react-ui/public/fonts/TaoBaoMaiCaiTi-Regular.ttf
deleted file mode 100644
index 31ab5e30..00000000
Binary files a/react-ui/public/fonts/TaoBaoMaiCaiTi-Regular.ttf and /dev/null differ
diff --git a/react-ui/public/fonts/TaoBaoMaiCaiTi-Regular.woff b/react-ui/public/fonts/TaoBaoMaiCaiTi-Regular.woff
deleted file mode 100644
index 0abc0f0e..00000000
Binary files a/react-ui/public/fonts/TaoBaoMaiCaiTi-Regular.woff and /dev/null differ
diff --git a/react-ui/public/fonts/TaoBaoMaiCaiTi-Regular.woff2 b/react-ui/public/fonts/TaoBaoMaiCaiTi-Regular.woff2
deleted file mode 100644
index f4433e2e..00000000
Binary files a/react-ui/public/fonts/TaoBaoMaiCaiTi-Regular.woff2 and /dev/null differ
diff --git a/react-ui/public/fonts/font.css b/react-ui/public/fonts/font.css
index 06af2fdb..012b9f59 100644
--- a/react-ui/public/fonts/font.css
+++ b/react-ui/public/fonts/font.css
@@ -4,16 +4,6 @@
font-display: swap;
}
-@font-face {
- font-family: 'TaoBaoMaiCaiTi';
- src: url('./TaoBaoMaiCaiTi-Regular.woff2') format('woff2'), /* 最优先使用 woff2 */
- url('./TaoBaoMaiCaiTi-Regular.woff') format('woff'), /* 兼容性较好的 woff */
- url('./TaoBaoMaiCaiTi-Regular.ttf') format('truetype'), /* ttf 作为备选 */
- url('./TaoBaoMaiCaiTi-Regular.otf') format('opentype'); /* otf 作为最后选项 */
- font-display: swap; /* 优化页面加载时的字体显示 */
-}
-
-
@font-face {
font-family: 'DingTalk-JinBuTi';
src: url('./DingTalk-JinBuTi.woff2') format('woff2'), /* 最优先使用 woff2 */
diff --git a/react-ui/src/app.tsx b/react-ui/src/app.tsx
index 7c026d3d..6ef5c5b8 100644
--- a/react-ui/src/app.tsx
+++ b/react-ui/src/app.tsx
@@ -1,13 +1,16 @@
import RightContent from '@/components/RightContent';
import themes from '@/styles/theme.less';
+import { type GlobalInitialState } from '@/types';
+import { menuItemRender } from '@/utils/menuRender';
import type { Settings as LayoutSettings } from '@ant-design/pro-components';
import { RuntimeConfig, history } from '@umijs/max';
import { RuntimeAntdConfig } from 'umi';
import defaultSettings from '../config/defaultSettings';
import '../public/fonts/font.css';
import { getAccessToken } from './access';
+import ErrorBoundary from './components/ErrorBoundary';
import './dayjsConfig';
-import { removeAllPageCacheState } from './hooks/pageCacheState';
+import { removeAllPageCacheState } from './hooks/useCacheState';
import {
getRemoteMenu,
getRoutersInfo,
@@ -16,14 +19,9 @@ import {
setRemoteMenu,
} from './services/session';
import './styles/menu.less';
-export { requestConfig as request } from './requestConfig';
-// const isDev = process.env.NODE_ENV === 'development';
-import { type GlobalInitialState } from '@/types';
-// import '@/utils/clipboard';
-import { menuItemRender } from '@/utils/menuRender';
-import ErrorBoundary from './components/ErrorBoundary';
import { needAuth } from './utils';
import { gotoLoginPage } from './utils/ui';
+export { requestConfig as request } from './requestConfig';
/**
* @see https://umijs.org/zh-CN/plugins/plugin-initial-state
@@ -139,7 +137,6 @@ export const layout: RuntimeConfig['layout'] = ({ initialState }) => {
onClick: () => {
// 点击菜单项,删除所有的页面 state 缓存
removeAllPageCacheState();
- // console.log('click menu');
},
},
...initialState?.settings,
diff --git a/react-ui/src/assets/img/confirm-icon.png b/react-ui/src/assets/img/confirm-icon.png
new file mode 100644
index 00000000..6865b719
Binary files /dev/null and b/react-ui/src/assets/img/confirm-icon.png differ
diff --git a/react-ui/src/assets/img/comfirm-icon.png b/react-ui/src/assets/img/copy-icon.png
similarity index 100%
rename from react-ui/src/assets/img/comfirm-icon.png
rename to react-ui/src/assets/img/copy-icon.png
diff --git a/react-ui/src/components/CodeConfigItem/index.tsx b/react-ui/src/components/CodeConfigItem/index.tsx
index 673bca40..ff48368c 100644
--- a/react-ui/src/components/CodeConfigItem/index.tsx
+++ b/react-ui/src/components/CodeConfigItem/index.tsx
@@ -2,6 +2,7 @@ import { AvailableRange } from '@/enums';
import { type CodeConfigData } from '@/pages/CodeConfig/List';
import { Flex, Typography } from 'antd';
import classNames from 'classnames';
+import { useState } from 'react';
import styles from './index.less';
type CodeConfigItemProps = {
@@ -10,6 +11,7 @@ type CodeConfigItemProps = {
};
function CodeConfigItem({ item, onClick }: CodeConfigItemProps) {
+ const [isEllipsis, setIsEllipsis] = useState(false);
return (
onClick?.(item)}>
@@ -32,11 +34,20 @@ function CodeConfigItem({ item, onClick }: CodeConfigItemProps) {
setIsEllipsis(ellipsis),
+ }}
>
{item.git_url}
-
{item.git_branch}
+
+ {item.git_branch}
+
);
}
diff --git a/react-ui/src/utils/clipboard.js b/react-ui/src/components/CopyingText/clipboard.js
similarity index 100%
rename from react-ui/src/utils/clipboard.js
rename to react-ui/src/components/CopyingText/clipboard.js
diff --git a/react-ui/src/pages/AutoML/components/CopyingText/index.less b/react-ui/src/components/CopyingText/index.less
similarity index 100%
rename from react-ui/src/pages/AutoML/components/CopyingText/index.less
rename to react-ui/src/components/CopyingText/index.less
diff --git a/react-ui/src/components/CopyingText/index.tsx b/react-ui/src/components/CopyingText/index.tsx
new file mode 100644
index 00000000..5a87ebe3
--- /dev/null
+++ b/react-ui/src/components/CopyingText/index.tsx
@@ -0,0 +1,26 @@
+import KFIcon from '@/components/KFIcon';
+import { Tooltip } from 'antd';
+import styles from './index.less';
+
+export type CopyingTextProps = {
+ text: string;
+};
+
+function CopyingText({ text }: CopyingTextProps) {
+ return (
+
+ {text}
+
+
+
+
+ );
+}
+
+export default CopyingText;
diff --git a/react-ui/src/components/IFramePage/index.tsx b/react-ui/src/components/IFramePage/index.tsx
index aa292d47..c89e39c2 100644
--- a/react-ui/src/components/IFramePage/index.tsx
+++ b/react-ui/src/components/IFramePage/index.tsx
@@ -3,6 +3,7 @@ import KFSpin from '@/components/KFSpin';
import { getLabelStudioUrl } from '@/services/developmentEnvironment';
import { to } from '@/utils/promise';
import SessionStorage from '@/utils/sessionStorage';
+import { FloatButton } from 'antd';
import classNames from 'classnames';
import { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
@@ -13,6 +14,7 @@ export enum IframePageType {
AppDevelopment = 'AppDevelopment', // 应用开发
DevEnv = 'DevEnv', // 开发环境
GitLink = 'GitLink', // git link
+ Aim = 'Aim', // 实验对比
}
const getRequestAPI = (type: IframePageType): (() => Promise) => {
@@ -29,12 +31,20 @@ const getRequestAPI = (type: IframePageType): (() => Promise) => {
});
case IframePageType.GitLink: // git link
return () => Promise.resolve({ code: 200, data: 'http://172.20.32.201:4000' });
+ case IframePageType.Aim: // Aim
+ return () =>
+ Promise.resolve({
+ code: 200,
+ data: SessionStorage.getItem(SessionStorage.aimUrlKey) || '',
+ });
}
};
type IframePageProps = {
/** 子系统 */
type: IframePageType;
+ /** 是否可以在页签上打开 */
+ openInTab?: boolean;
/** 自定义样式类名 */
className?: string;
/** 自定义样式 */
@@ -42,7 +52,7 @@ type IframePageProps = {
};
/** 系统内嵌 iframe,目前系统有数据标注、应用开发、开发环境、GitLink 四个子系统,使用时可以添加其他子系统 */
-function IframePage({ type, className, style }: IframePageProps) {
+function IframePage({ type, openInTab = false, className, style }: IframePageProps) {
const [iframeUrl, setIframeUrl] = useState('');
const [loading, setLoading] = useState(false);
@@ -68,6 +78,7 @@ function IframePage({ type, className, style }: IframePageProps) {
{loading && createPortal(, document.body)}
+ {openInTab && window.open(iframeUrl, '_blank')} />}
);
}
diff --git a/react-ui/src/components/KFSpin/index.less b/react-ui/src/components/KFSpin/index.less
index 753154d7..7d532d2d 100644
--- a/react-ui/src/components/KFSpin/index.less
+++ b/react-ui/src/components/KFSpin/index.less
@@ -4,7 +4,7 @@
right: 0;
bottom: 0;
left: 0;
- z-index: 1001;
+ z-index: 1001; // 设置大于 Modal 的 z-index
display: flex;
flex-direction: column;
align-items: center;
diff --git a/react-ui/src/components/ParameterInput/index.less b/react-ui/src/components/ParameterInput/index.less
index fff69eb1..ff0a21f7 100644
--- a/react-ui/src/components/ParameterInput/index.less
+++ b/react-ui/src/components/ParameterInput/index.less
@@ -22,7 +22,7 @@
border-radius: 4px;
&__value {
- .singleLine();
+ //.singleLine();
margin-right: 8px;
font-size: @font-size-input;
line-height: 1.5714285714285714;
diff --git a/react-ui/src/components/ParameterInput/index.tsx b/react-ui/src/components/ParameterInput/index.tsx
index 32672b98..08cf8649 100644
--- a/react-ui/src/components/ParameterInput/index.tsx
+++ b/react-ui/src/components/ParameterInput/index.tsx
@@ -6,7 +6,7 @@
import { CommonTabKeys } from '@/enums';
import { CloseOutlined } from '@ant-design/icons';
-import { ConfigProvider, Form, Input } from 'antd';
+import { ConfigProvider, Form, Input, Typography } from 'antd';
import { RuleObject } from 'antd/es/form';
import classNames from 'classnames';
import './index.less';
@@ -120,7 +120,12 @@ function ParameterInput({
>
{valueObj?.showValue ? (
-
{valueObj?.showValue}
+
+ {valueObj.showValue}
+
void;
}
@@ -32,6 +36,7 @@ function ParameterSelect({
dataType,
display = false,
value,
+ isPipeline = false,
onChange,
...rest
}: ParameterSelectProps) {
@@ -39,6 +44,12 @@ function ParameterSelect({
const propsConfig = paramSelectConfig[dataType];
const valueText = typeof value === 'object' && value !== null ? value.value : value;
const [resourceStandardList] = useComputingResource();
+ const computingResource = isPipeline
+ ? resourceStandardList.map((v) => ({
+ ...v,
+ id: String(v.id),
+ }))
+ : resourceStandardList;
useEffect(() => {
// 获取下拉数据
@@ -56,7 +67,7 @@ function ParameterSelect({
getSelectOptions();
}, [propsConfig]);
- const selectOptions = dataType === 'resource' ? resourceStandardList : options;
+ const selectOptions = dataType === 'resource' ? computingResource : options;
const handleChange = (text: string) => {
if (typeof value === 'object' && value !== null) {
diff --git a/react-ui/src/components/ResourceSelectorModal/config.tsx b/react-ui/src/components/ResourceSelectorModal/config.tsx
index 80e3ab3c..5dc7b961 100644
--- a/react-ui/src/components/ResourceSelectorModal/config.tsx
+++ b/react-ui/src/components/ResourceSelectorModal/config.tsx
@@ -224,6 +224,8 @@ export class MirrorSelector implements SelectorTypeInfo {
image_id: parentKey,
page: 0,
size: 2000,
+ status: 'available',
+ state: 1,
});
if (res && res.data) {
const list = res.data.content || [];
diff --git a/react-ui/src/components/RightContent/AvatarDropdown.tsx b/react-ui/src/components/RightContent/AvatarDropdown.tsx
index d93d4f74..ce90efb5 100644
--- a/react-ui/src/components/RightContent/AvatarDropdown.tsx
+++ b/react-ui/src/components/RightContent/AvatarDropdown.tsx
@@ -2,8 +2,9 @@ import { clearSessionToken } from '@/access';
import { setRemoteMenu } from '@/services/session';
import { logout } from '@/services/system/auth';
import { ClientInfo } from '@/types';
+import { sleep } from '@/utils/promise';
import SessionStorage from '@/utils/sessionStorage';
-import { gotoLoginPage } from '@/utils/ui';
+import { gotoLoginPage, oauthLogout } from '@/utils/ui';
import { LogoutOutlined, UserOutlined } from '@ant-design/icons';
import { setAlpha } from '@ant-design/pro-components';
import { useEmotionCss } from '@ant-design/use-emotion-css';
@@ -62,7 +63,9 @@ const AvatarDropdown: React.FC = ({ menu }) => {
* 退出登录,并且将当前的 url 保存
*/
const loginOut = async () => {
- await logout();
+ oauthLogout('http://172.20.32.197:31209/oauth/logout');
+ // 至少 1 秒后跳转,希望子系统能完成注销
+ await Promise.all([logout(), sleep(1000)]);
clearSessionToken();
setRemoteMenu(null);
gotoLoginPage();
diff --git a/react-ui/src/global.less b/react-ui/src/global.less
index 9944c70e..df79e9f7 100644
--- a/react-ui/src/global.less
+++ b/react-ui/src/global.less
@@ -160,3 +160,8 @@ ol {
input:-webkit-autofill {
transition: background-color 5000s ease-in-out 0s;
}
+
+.ant-typography {
+ color: inherit;
+ font-size: inherit;
+}
diff --git a/react-ui/src/hooks/index.ts b/react-ui/src/hooks/index.ts
deleted file mode 100644
index 59ba1d6e..00000000
--- a/react-ui/src/hooks/index.ts
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * @Author: 赵伟
- * @Date: 2024-04-15 10:01:29
- * @Description: 自定义 hooks
- */
-import { FormInstance } from 'antd';
-import { debounce } from 'lodash';
-import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
-/**
- * 生成具有初始值的状态引用
- *
- * @param initialValue - 状态的初始值
- * @return 包含状态值、状态设置函数和可变引用对象的数组
- */
-export function useStateRef(initialValue: T) {
- const [value, setValue] = useState(initialValue);
-
- const ref = useRef(value);
-
- useEffect(() => {
- ref.current = value;
- }, [value]);
-
- return [value, setValue, ref] as const;
-}
-
-/**
- * 生成一个自定义钩子,用于管理模态框的可见性状态。
- *
- * @param initialValue - 模态框的初始可见性状态。
- * @return 一个数组,包含可见性状态和打开和关闭模态框的函数。
- */
-export function useVisible(initialValue: boolean) {
- const [visible, setVisible] = useState(initialValue);
- const ref = useRef(initialValue);
-
- const open = useCallback(() => {
- setVisible(true);
- }, []);
-
- const close = useCallback(() => {
- setVisible(false);
- }, []);
-
- useEffect(() => {
- ref.current = visible;
- }, [visible]);
-
- return [visible, open, close, ref] as const;
-}
-
-type Callback = (state: T) => void;
-
-/**
- * 生成一个具有回调机制的可变状态值和更新它的函数。
- *
- * @param initialValue - 初始状态值。
- * @return 一个元组,包含当前状态值和用于更新状态的函数。
- */
-export function useCallbackState(initialValue: T) {
- const [state, _setState] = useState(initialValue);
- const callbackQueue = useRef[]>([]);
- useEffect(() => {
- callbackQueue.current.forEach((cb) => cb(state));
- callbackQueue.current = [];
- }, [state]);
- const setState = (newValue: T | ((prevState: T) => T), callback?: Callback) => {
- _setState(newValue);
- if (callback && typeof callback === 'function') {
- callbackQueue.current.push(callback);
- }
- };
- return [state, setState] as const;
-}
-
-/**
- * 用于追踪 DOM 元素尺寸的 hook。
- *
- * @param initialWidth - 初始宽度。
- * @param initialHeight - 初始高度。
- * @param deps - 依赖列表。
- * @return 一个元组,包含 DOM 元素的 ref、当前宽度和当前高度。
- */
-export function useDomSize(
- initialWidth: number,
- initialHeight: number,
- deps: React.DependencyList = [],
-) {
- const domRef = useRef(null);
- const [width, setWidth] = useState(initialWidth);
- const [height, setHeight] = useState(initialHeight);
-
- useEffect(() => {
- const setDomHeight = () => {
- if (domRef.current) {
- setHeight(domRef.current.offsetHeight);
- setWidth(domRef.current.offsetWidth);
- }
- };
- const debounceFunc = debounce(setDomHeight, 100);
-
- setDomHeight();
- window.addEventListener('resize', debounceFunc);
-
- return () => {
- window.removeEventListener('resize', debounceFunc);
- };
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [domRef, ...deps]);
-
- return [domRef, { width, height }] as const;
-}
-
-/**
- * 用于在 modal 关闭时重置 Form 表单的 hook。
- *
- * @param form - Ant Design Form 表单实例
- * @param open - modal 是否打开
- */
-export const useResetFormOnCloseModal = (form: FormInstance, open: boolean) => {
- const prevOpenRef = useRef();
-
- useEffect(() => {
- prevOpenRef.current = open;
- }, [open]);
-
- const prevOpen = prevOpenRef.current;
-
- useEffect(() => {
- if (!open && prevOpen) {
- form.resetFields();
- }
- }, [form, prevOpen, open]);
-};
-
-/**
- * Executes the effect function when the specified condition is true.
- *
- * @param effect - The effect function to execute.
- * @param when - The condition to trigger the effect.
- * @param deps - The dependencies for the effect.
- */
-export const useEffectWhen = (effect: () => void, when: boolean, deps: React.DependencyList) => {
- const requestFns = useRef<(() => void)[]>([]);
- useEffect(() => {
- if (when) {
- effect();
- } else {
- requestFns.current.splice(0, 1, effect);
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, deps);
-
- useEffect(() => {
- if (when) {
- const fn = requestFns.current.pop();
- fn?.();
- }
- }, [when]);
-};
-
-// 选择、全选操作
-export const useCheck = (list: T[]) => {
- const [selected, setSelected] = useState([]);
-
- const checked = useMemo(() => {
- return selected.length === list.length && selected.length > 0;
- }, [selected, list]);
-
- const indeterminate = useMemo(() => {
- return selected.length > 0 && selected.length < list.length;
- }, [selected, list]);
-
- const checkAll = useCallback(() => {
- setSelected(checked ? [] : list);
- }, [list, checked]);
-
- const isSingleChecked = useCallback((item: T) => selected.includes(item), [selected]);
-
- const checkSingle = useCallback(
- (item: T) => {
- setSelected((prev) => {
- if (isSingleChecked(item)) {
- return prev.filter((i) => i !== item);
- } else {
- return [...prev, item];
- }
- });
- },
- [isSingleChecked],
- );
-
- return [
- selected,
- setSelected,
- checked,
- indeterminate,
- checkAll,
- isSingleChecked,
- checkSingle,
- ] as const;
-};
diff --git a/react-ui/src/hooks/pageCacheState.ts b/react-ui/src/hooks/useCacheState.ts
similarity index 94%
rename from react-ui/src/hooks/pageCacheState.ts
rename to react-ui/src/hooks/useCacheState.ts
index e320b0a6..78b16fa7 100644
--- a/react-ui/src/hooks/pageCacheState.ts
+++ b/react-ui/src/hooks/useCacheState.ts
@@ -29,13 +29,18 @@ const removeCacheState = (key: string) => {
}
};
-// 移除所有页面 state 缓存
+/**
+ * 移除所有页面 state 缓存
+ */
export const removeAllPageCacheState = () => {
pageKeys.forEach((key) => {
sessionStorage.removeItem(key);
});
};
+/**
+ * 缓存页面数据
+ */
export const useCacheState = () => {
const { pathname } = window.location;
const key = 'pagecache:' + pathname;
diff --git a/react-ui/src/hooks/useCallbackState.ts b/react-ui/src/hooks/useCallbackState.ts
new file mode 100644
index 00000000..820e770b
--- /dev/null
+++ b/react-ui/src/hooks/useCallbackState.ts
@@ -0,0 +1,25 @@
+import { useEffect, useRef, useState } from 'react';
+
+type Callback = (state: T) => void;
+
+/**
+ * 生成一个具有回调机制的可变状态值和更新它的函数。谨慎使用
+ *
+ * @param initialValue - 初始状态值。
+ * @return 一个元组,包含当前状态值和用于更新状态的函数。
+ */
+export function useCallbackState(initialValue: T) {
+ const [state, _setState] = useState(initialValue);
+ const callbackQueue = useRef[]>([]);
+ useEffect(() => {
+ callbackQueue.current.forEach((cb) => cb(state));
+ callbackQueue.current = [];
+ }, [state]);
+ const setState = (newValue: T | ((prevState: T) => T), callback?: Callback) => {
+ _setState(newValue);
+ if (callback && typeof callback === 'function') {
+ callbackQueue.current.push(callback);
+ }
+ };
+ return [state, setState] as const;
+}
diff --git a/react-ui/src/hooks/useCheck.ts b/react-ui/src/hooks/useCheck.ts
new file mode 100644
index 00000000..3c9c985b
--- /dev/null
+++ b/react-ui/src/hooks/useCheck.ts
@@ -0,0 +1,47 @@
+import { useCallback, useMemo, useState } from 'react';
+
+/**
+ * 选择、全选操作
+ * @param list - 需要进行选择的列表
+ * @return [选中的项, 设置选中的方法, 是否全选, 是否部分选中, 全选方法,是否单个选中,选中单个方法]
+ */
+export const useCheck = (list: T[]) => {
+ const [selected, setSelected] = useState([]);
+
+ const checked = useMemo(() => {
+ return selected.length === list.length && selected.length > 0;
+ }, [selected, list]);
+
+ const indeterminate = useMemo(() => {
+ return selected.length > 0 && selected.length < list.length;
+ }, [selected, list]);
+
+ const checkAll = useCallback(() => {
+ setSelected(checked ? [] : list);
+ }, [list, checked]);
+
+ const isSingleChecked = useCallback((item: T) => selected.includes(item), [selected]);
+
+ const checkSingle = useCallback(
+ (item: T) => {
+ setSelected((prev) => {
+ if (isSingleChecked(item)) {
+ return prev.filter((i) => i !== item);
+ } else {
+ return [...prev, item];
+ }
+ });
+ },
+ [isSingleChecked],
+ );
+
+ return [
+ selected,
+ setSelected,
+ checked,
+ indeterminate,
+ checkAll,
+ isSingleChecked,
+ checkSingle,
+ ] as const;
+};
diff --git a/react-ui/src/hooks/resource.ts b/react-ui/src/hooks/useComputingResource.ts
similarity index 95%
rename from react-ui/src/hooks/resource.ts
rename to react-ui/src/hooks/useComputingResource.ts
index e1e27506..b2239247 100644
--- a/react-ui/src/hooks/resource.ts
+++ b/react-ui/src/hooks/useComputingResource.ts
@@ -12,7 +12,7 @@ import { useCallback, useEffect, useState } from 'react';
const computingResource: ComputingResource[] = [];
-// 过滤资源规格
+/** 过滤资源规格 */
export const filterResourceStandard: SelectProps['filterOption'] = (
input: string,
option?: ComputingResource,
@@ -22,13 +22,13 @@ export const filterResourceStandard: SelectProps['fil
);
};
-// 资源规格字段
+/** 资源规格字段 */
export const resourceFieldNames = {
label: 'description',
value: 'id',
};
-// 获取资源规格
+/** 获取资源规格 */
export function useComputingResource() {
const [resourceStandardList, setResourceStandardList] = useState([]);
diff --git a/react-ui/src/hooks/useDomSize.ts b/react-ui/src/hooks/useDomSize.ts
new file mode 100644
index 00000000..ba1992ea
--- /dev/null
+++ b/react-ui/src/hooks/useDomSize.ts
@@ -0,0 +1,40 @@
+import { debounce } from 'lodash';
+import { useEffect, useRef, useState } from 'react';
+
+/**
+ * 用于追踪 DOM 元素尺寸的 hook。
+ *
+ * @param initialWidth - 初始宽度。
+ * @param initialHeight - 初始高度。
+ * @param deps - 依赖列表。
+ * @return 一个元组,包含 DOM 元素的 ref、当前宽度和当前高度。
+ */
+export function useDomSize(
+ initialWidth: number,
+ initialHeight: number,
+ deps: React.DependencyList = [],
+) {
+ const domRef = useRef(null);
+ const [width, setWidth] = useState(initialWidth);
+ const [height, setHeight] = useState(initialHeight);
+
+ useEffect(() => {
+ const setDomHeight = () => {
+ if (domRef.current) {
+ setHeight(domRef.current.offsetHeight);
+ setWidth(domRef.current.offsetWidth);
+ }
+ };
+ const debounceFunc = debounce(setDomHeight, 100);
+
+ setDomHeight();
+ window.addEventListener('resize', debounceFunc);
+
+ return () => {
+ window.removeEventListener('resize', debounceFunc);
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, deps);
+
+ return [domRef, { width, height }] as const;
+}
diff --git a/react-ui/src/hooks/draggable.ts b/react-ui/src/hooks/useDraggable.ts
similarity index 91%
rename from react-ui/src/hooks/draggable.ts
rename to react-ui/src/hooks/useDraggable.ts
index b093ea6f..121aaafa 100644
--- a/react-ui/src/hooks/draggable.ts
+++ b/react-ui/src/hooks/useDraggable.ts
@@ -1,6 +1,8 @@
-// 处理 react-draggable 组件拖动结束时,响应了点击事件的
import { useState } from 'react';
+/**
+ * 处理 react-draggable 组件拖动结束时,响应了点击事件的
+ */
export const useDraggable = (onClick: () => void) => {
const [isDragging, setIsDragging] = useState(false);
diff --git a/react-ui/src/hooks/useEffectWhen.ts b/react-ui/src/hooks/useEffectWhen.ts
new file mode 100644
index 00000000..12f1aad0
--- /dev/null
+++ b/react-ui/src/hooks/useEffectWhen.ts
@@ -0,0 +1,24 @@
+import { useEffect, useRef } from 'react';
+
+/**
+ * 当指定的条件为真时执行 Effect 函数。
+ *
+ * @param effect - The effect function to execute.
+ * @param when - The condition to trigger the effect.
+ * @param deps - The dependencies for the effect.
+ */
+export const useEffectWhen = (effect: () => void, when: boolean, deps: React.DependencyList) => {
+ const requestFn = useRef<(() => void) | undefined>(effect);
+ useEffect(() => {
+ requestFn.current = effect;
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [...deps, effect]);
+
+ useEffect(() => {
+ if (when && requestFn.current) {
+ requestFn.current();
+ requestFn.current = undefined;
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [...deps, when]);
+};
diff --git a/react-ui/src/hooks/useResetForm.ts b/react-ui/src/hooks/useResetForm.ts
new file mode 100644
index 00000000..acefd84e
--- /dev/null
+++ b/react-ui/src/hooks/useResetForm.ts
@@ -0,0 +1,24 @@
+import { FormInstance } from 'antd';
+import { useEffect, useRef } from 'react';
+
+/**
+ * 用于在 modal 关闭时重置 Form 表单的 hook。
+ *
+ * @param form - Ant Design Form 表单实例
+ * @param open - modal 是否打开
+ */
+export const useResetForm = (form: FormInstance, open: boolean) => {
+ const prevOpenRef = useRef();
+
+ useEffect(() => {
+ prevOpenRef.current = open;
+ }, [open]);
+
+ const prevOpen = prevOpenRef.current;
+
+ useEffect(() => {
+ if (!open && prevOpen) {
+ form.resetFields();
+ }
+ }, [form, prevOpen, open]);
+};
diff --git a/react-ui/src/hooks/useSSE.ts b/react-ui/src/hooks/useSSE.ts
new file mode 100644
index 00000000..5e278675
--- /dev/null
+++ b/react-ui/src/hooks/useSSE.ts
@@ -0,0 +1,46 @@
+import { parseJsonText } from '@/utils';
+import { useCallback, useRef } from 'react';
+
+export const useSSE = (onMessage: (data: any) => void) => {
+ const evtSourceRef = useRef(null);
+
+ const setupSSE = useCallback(
+ (name: string, namespace: string) => {
+ const { origin } = location;
+ const params = encodeURIComponent(`metadata.namespace=${namespace},metadata.name=${name}`);
+ const evtSource = new EventSource(
+ `${origin}/api/v1/realtimeStatus?listOptions.fieldSelector=${params}`,
+ { withCredentials: false },
+ );
+ evtSource.onmessage = (event) => {
+ const data = event?.data;
+ if (!data) {
+ return;
+ }
+ const dataJson = parseJsonText(data);
+ if (dataJson) {
+ const nodes = dataJson?.result?.object?.status?.nodes;
+ if (nodes) {
+ onMessage(nodes);
+ }
+ }
+ };
+
+ evtSource.onerror = (error) => {
+ console.error('SSE error: ', error);
+ };
+
+ evtSourceRef.current = evtSource;
+ },
+ [onMessage],
+ );
+
+ const closeSSE = useCallback(() => {
+ if (evtSourceRef.current) {
+ evtSourceRef.current.close();
+ evtSourceRef.current = null;
+ }
+ }, []);
+
+ return [setupSSE, closeSSE];
+};
diff --git a/react-ui/src/hooks/useStateRef.ts b/react-ui/src/hooks/useStateRef.ts
new file mode 100644
index 00000000..5cc260b3
--- /dev/null
+++ b/react-ui/src/hooks/useStateRef.ts
@@ -0,0 +1,19 @@
+import { useEffect, useRef, useState } from 'react';
+
+/**
+ * 生成具有初始值的状态引用
+ *
+ * @param initialValue - 状态的初始值
+ * @return 包含状态值、状态设置函数和可变引用对象的数组
+ */
+export function useStateRef(initialValue: T) {
+ const [value, setValue] = useState(initialValue);
+
+ const ref = useRef(value);
+
+ useEffect(() => {
+ ref.current = value;
+ }, [value]);
+
+ return [value, setValue, ref] as const;
+}
diff --git a/react-ui/src/hooks/useVisible.ts b/react-ui/src/hooks/useVisible.ts
new file mode 100644
index 00000000..461fa3c8
--- /dev/null
+++ b/react-ui/src/hooks/useVisible.ts
@@ -0,0 +1,26 @@
+import { useCallback, useEffect, useRef, useState } from 'react';
+
+/**
+ * 生成一个自定义钩子,用于管理模态框的可见性状态。
+ *
+ * @param initialValue - 模态框的初始可见性状态。
+ * @return 一个数组,包含 visible、打开函数、关闭函数和 visible ref。
+ */
+export function useVisible(initialValue: boolean) {
+ const [visible, setVisible] = useState(initialValue);
+ const ref = useRef(initialValue);
+
+ const open = useCallback(() => {
+ setVisible(true);
+ }, []);
+
+ const close = useCallback(() => {
+ setVisible(false);
+ }, []);
+
+ useEffect(() => {
+ ref.current = visible;
+ }, [visible]);
+
+ return [visible, open, close, ref] as const;
+}
diff --git a/react-ui/src/overrides.less b/react-ui/src/overrides.less
index 4709a97c..e129b4a7 100644
--- a/react-ui/src/overrides.less
+++ b/react-ui/src/overrides.less
@@ -261,8 +261,3 @@
}
}
}
-
-.ant-typography {
- color: inherit;
- font-size: inherit;
-}
diff --git a/react-ui/src/pages/AutoML/Instance/index.tsx b/react-ui/src/pages/AutoML/Instance/index.tsx
index 5e409c75..933b496e 100644
--- a/react-ui/src/pages/AutoML/Instance/index.tsx
+++ b/react-ui/src/pages/AutoML/Instance/index.tsx
@@ -186,6 +186,7 @@ function AutoMLInstance() {
icon: ,
children: (
diff --git a/react-ui/src/pages/AutoML/components/CopyingText/index.tsx b/react-ui/src/pages/AutoML/components/CopyingText/index.tsx
deleted file mode 100644
index 586de40b..00000000
--- a/react-ui/src/pages/AutoML/components/CopyingText/index.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import KFIcon from '@/components/KFIcon';
-import { Typography } from 'antd';
-import styles from './index.less';
-
-export type CopyingTextProps = {
- text: string;
-};
-
-function CopyingText({ text }: CopyingTextProps) {
- return (
-
-
- {text}
-
-
-
- );
-}
-
-export default CopyingText;
diff --git a/react-ui/src/pages/AutoML/components/ExperimentHistory/index.tsx b/react-ui/src/pages/AutoML/components/ExperimentHistory/index.tsx
index 09d0cd6e..0441a623 100644
--- a/react-ui/src/pages/AutoML/components/ExperimentHistory/index.tsx
+++ b/react-ui/src/pages/AutoML/components/ExperimentHistory/index.tsx
@@ -8,8 +8,9 @@ import TrialStatusCell from '../TrialStatusCell';
import styles from './index.less';
type ExperimentHistoryProps = {
- fileUrl?: string;
- isClassification: boolean;
+ calcMetrics?: string; // 计算指标
+ fileUrl?: string; // 文件url
+ isClassification: boolean; // 是否是分类
};
type TableData = {
@@ -22,7 +23,7 @@ type TableData = {
althorithm?: string;
};
-function ExperimentHistory({ fileUrl, isClassification }: ExperimentHistoryProps) {
+function ExperimentHistory({ calcMetrics, fileUrl, isClassification }: ExperimentHistoryProps) {
const [tableData, setTableData] = useState([]);
useEffect(() => {
// 获取实验运行历史记录
@@ -33,7 +34,7 @@ function ExperimentHistory({ fileUrl, isClassification }: ExperimentHistoryProps
const list: TableData[] = data.map((item) => {
return {
id: item[0]?.[0],
- accuracy: item[1]?.[5]?.accuracy,
+ accuracy: calcMetrics ? item[1]?.[5]?.[calcMetrics] : undefined,
duration: item[1]?.[5]?.duration,
train_loss: item[1]?.[5]?.train_loss,
status: item[1]?.[2]?.['__enum__']?.split('.')?.[1],
@@ -64,12 +65,6 @@ function ExperimentHistory({ fileUrl, isClassification }: ExperimentHistoryProps
width: 80,
render: tableCellRender(false),
},
- {
- title: '准确率',
- dataIndex: 'accuracy',
- key: 'accuracy',
- render: tableCellRender(true),
- },
{
title: '耗时',
dataIndex: 'duration',
@@ -103,6 +98,15 @@ function ExperimentHistory({ fileUrl, isClassification }: ExperimentHistoryProps
},
];
+ if (calcMetrics) {
+ columns.splice(0, 0, {
+ title: `指标:${calcMetrics}`,
+ dataIndex: 'accuracy',
+ key: 'accuracy',
+ render: tableCellRender(true),
+ });
+ }
+
return (
diff --git a/react-ui/src/pages/AutoML/components/ExperimentInstance/index.tsx b/react-ui/src/pages/AutoML/components/ExperimentInstance/index.tsx
index e7837120..9b27862d 100644
--- a/react-ui/src/pages/AutoML/components/ExperimentInstance/index.tsx
+++ b/react-ui/src/pages/AutoML/components/ExperimentInstance/index.tsx
@@ -1,6 +1,6 @@
import KFIcon from '@/components/KFIcon';
import { ExperimentStatus } from '@/enums';
-import { useCheck } from '@/hooks';
+import { useCheck } from '@/hooks/useCheck';
import { experimentStatusInfo } from '@/pages/Experiment/status';
import themes from '@/styles/theme.less';
import { type ExperimentInstance } from '@/types';
@@ -58,7 +58,8 @@ function ExperimentInstanceComponent({
// 删除实验实例确认
const handleRemove = (instance: ExperimentInstance) => {
modalConfirm({
- title: '确定删除该条实例吗?',
+ title: '删除后,该实验实例将不可恢复',
+ content: '是否确认删除?',
onOk: () => {
deleteExperimentInstance(instance.id);
},
@@ -96,6 +97,18 @@ function ExperimentInstanceComponent({
}
};
+ // 终止实验实例
+ const handleTerminate = (instance: ExperimentInstance) => {
+ modalConfirm({
+ title: '终止后,该次实验运行将不可恢复',
+ content: '是否确认终止?',
+ isDelete: false,
+ onOk: () => {
+ terminateExperimentInstance(instance);
+ },
+ });
+ };
+
// 终止实验实例
const terminateExperimentInstance = async (instance: ExperimentInstance) => {
const request = config.stopInsReq;
@@ -188,7 +201,7 @@ function ExperimentInstanceComponent({
item.status === ExperimentStatus.Terminated
}
icon={
}
- onClick={() => terminateExperimentInstance(item)}
+ onClick={() => handleTerminate(item)}
>
终止
diff --git a/react-ui/src/pages/AutoML/components/ExperimentList/index.tsx b/react-ui/src/pages/AutoML/components/ExperimentList/index.tsx
index b4e7f24b..d4628c24 100644
--- a/react-ui/src/pages/AutoML/components/ExperimentList/index.tsx
+++ b/react-ui/src/pages/AutoML/components/ExperimentList/index.tsx
@@ -7,7 +7,7 @@
import KFIcon from '@/components/KFIcon';
import PageTitle from '@/components/PageTitle';
import { ExperimentStatus } from '@/enums';
-import { useCacheState } from '@/hooks/pageCacheState';
+import { useCacheState } from '@/hooks/useCacheState';
import { AutoMLData } from '@/pages/AutoML/types';
import { experimentStatusInfo } from '@/pages/Experiment/status';
import themes from '@/styles/theme.less';
@@ -93,17 +93,14 @@ function ExperimentList({ type }: ExperimentListProps) {
const [res] = await to(request(record.id));
if (res) {
message.success('删除成功');
- // 如果是一页的唯一数据,删除时,请求第一页的数据
+ // 如果是一页的唯一数据,删除后,请求第一页的数据
// 否则直接刷新这一页的数据
- // 避免回到第一页
- if (tableData.length > 1) {
- setPagination((prev) => ({
+ setPagination((prev) => {
+ return {
...prev,
- current: 1,
- }));
- } else {
- getAutoMLList();
- }
+ current: tableData.length === 1 ? Math.max(1, prev.current! - 1) : prev.current,
+ };
+ });
}
};
@@ -188,6 +185,7 @@ function ExperimentList({ type }: ExperimentListProps) {
if (expanded) {
setExpandedRowKeys([record.id]);
getExperimentInsList(record.id, 0);
+ refreshExperimentList();
} else {
setExpandedRowKeys([]);
}
diff --git a/react-ui/src/pages/CodeConfig/List/index.tsx b/react-ui/src/pages/CodeConfig/List/index.tsx
index 3f567465..a1ccd09c 100644
--- a/react-ui/src/pages/CodeConfig/List/index.tsx
+++ b/react-ui/src/pages/CodeConfig/List/index.tsx
@@ -75,8 +75,15 @@ function CodeConfigList() {
const deleteRecord = async (id: number) => {
const [res] = await to(deleteCodeConfigReq(id));
if (res) {
- getDataList();
message.success('删除成功');
+ // 如果是一页的唯一数据,删除后,请求第一页的数据
+ // 否则直接刷新这一页的数据
+ setPagination((prev) => {
+ return {
+ ...prev,
+ current: dataList!.length === 1 ? Math.max(1, prev.current! - 1) : prev.current,
+ };
+ });
}
};
diff --git a/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.tsx b/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.tsx
index de903f47..fd9c967d 100644
--- a/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.tsx
+++ b/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.tsx
@@ -70,7 +70,12 @@ function CodeConfigItem({ item, onClick, onEdit, onRemove }: CodeConfigItemProps
>
{item.git_url}
-
{item.git_branch}
+
+ {item.git_branch}
+
diff --git a/react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx b/react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx
index db05393e..624ef833 100644
--- a/react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx
+++ b/react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx
@@ -4,7 +4,12 @@ import KFModal from '@/components/KFModal';
import { CategoryData, DataSource, ResourceType, resourceConfig } from '@/pages/Dataset/config';
import { addDataset } from '@/services/dataset/index.js';
import { to } from '@/utils/promise';
-import { getFileListFromEvent, limitUploadFileType, validateUploadFiles } from '@/utils/ui';
+import {
+ getFileListFromEvent,
+ limitUploadFileType,
+ removeUploadedFile,
+ validateUploadFiles,
+} from '@/utils/ui';
import {
Button,
Form,
@@ -29,11 +34,6 @@ interface AddDatasetModalProps extends Omit {
function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalProps) {
const [uuid] = useState(Date.now());
- // const [clusterOptions, setClusterOptions] = useState([]);
-
- // useEffect(() => {
- // getClusterOptions();
- // }, []);
// 上传组件参数
const uploadProps: UploadProps = {
@@ -44,16 +44,9 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
defaultFileList: [],
accept: '.zip,.tgz',
beforeUpload: limitUploadFileType('zip,tgz'),
+ onRemove: removeUploadedFile,
};
- // 获取集群版本数据
- // const getClusterOptions = async () => {
- // const [res] = await to(getDictSelectOption('available_cluster'));
- // if (res) {
- // setClusterOptions(res);
- // }
- // };
-
// 上传请求
const createDataset = async (params: any) => {
const [res] = await to(addDataset(params));
@@ -113,7 +106,7 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
},
]}
>
-
+
- {/*
-
- */}
+
+
+
-
+
+
+
+
{
onOk: () => {
getVersionList(true);
close();
+ window.postMessage({ type: VersionChangedMessage });
},
});
};
@@ -170,6 +172,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
if (res) {
message.success('删除成功');
getVersionList(true);
+ window.postMessage({ type: VersionChangedMessage });
}
};
diff --git a/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx b/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx
index 10d7d9d2..947ef228 100644
--- a/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx
+++ b/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx
@@ -21,7 +21,7 @@ const getDatasetDatas = (data: DatasetData): BasicInfoData[] => [
value: data.name,
},
{
- label: '版本',
+ label: '数据集版本',
value: data.version,
},
{
@@ -64,7 +64,7 @@ const getModelDatas = (data: ModelData): BasicInfoData[] => [
ellipsis: true,
},
{
- label: '版本',
+ label: '模型版本',
value: data.version,
ellipsis: true,
},
diff --git a/react-ui/src/pages/Dataset/components/ResourceList/index.tsx b/react-ui/src/pages/Dataset/components/ResourceList/index.tsx
index 6549b9c1..2f1e8d4e 100644
--- a/react-ui/src/pages/Dataset/components/ResourceList/index.tsx
+++ b/react-ui/src/pages/Dataset/components/ResourceList/index.tsx
@@ -107,8 +107,15 @@ function ResourceList(
const request = config.deleteRecord;
const [res] = await to(request(params));
if (res) {
- getDataList();
message.success('删除成功');
+ // 如果是一页的唯一数据,删除后,请求第一页的数据
+ // 否则直接刷新这一页的数据
+ setPagination((prev) => {
+ return {
+ ...prev,
+ current: dataList!.length === 1 ? Math.max(1, prev.current! - 1) : prev.current,
+ };
+ });
}
};
diff --git a/react-ui/src/pages/Dataset/components/ResourcePage/index.tsx b/react-ui/src/pages/Dataset/components/ResourcePage/index.tsx
index f8a22729..fce95046 100644
--- a/react-ui/src/pages/Dataset/components/ResourcePage/index.tsx
+++ b/react-ui/src/pages/Dataset/components/ResourcePage/index.tsx
@@ -1,5 +1,5 @@
import { CommonTabKeys } from '@/enums';
-import { useCacheState } from '@/hooks/pageCacheState';
+import { useCacheState } from '@/hooks/useCacheState';
import { getAssetIcon } from '@/services/dataset/index.js';
import { to } from '@/utils/promise';
import { Flex, Tabs, type TabsProps } from 'antd';
diff --git a/react-ui/src/pages/Dataset/components/ResourceVersion/index.tsx b/react-ui/src/pages/Dataset/components/ResourceVersion/index.tsx
index 3dbb3c40..18fbc3ee 100644
--- a/react-ui/src/pages/Dataset/components/ResourceVersion/index.tsx
+++ b/react-ui/src/pages/Dataset/components/ResourceVersion/index.tsx
@@ -1,3 +1,9 @@
+/*
+ * @Author: 赵伟
+ * @Date: 2025-03-24 15:41:42
+ * @Description: 版本文件列表
+ */
+
import KFIcon from '@/components/KFIcon';
import {
ResourceData,
diff --git a/react-ui/src/pages/Dataset/components/VersionCompareModal/index.less b/react-ui/src/pages/Dataset/components/VersionCompareModal/index.less
index f1935eb2..4a1c0d94 100644
--- a/react-ui/src/pages/Dataset/components/VersionCompareModal/index.less
+++ b/react-ui/src/pages/Dataset/components/VersionCompareModal/index.less
@@ -11,7 +11,6 @@
text-align: center;
background: @background;
border-radius: 4px 4px 0 0;
- .singleLine();
}
.text() {
@@ -20,7 +19,6 @@
font-size: 13px;
line-height: 22px;
word-break: break-all;
- .singleLine();
}
.version-container(@background) {
diff --git a/react-ui/src/pages/Dataset/components/VersionCompareModal/index.tsx b/react-ui/src/pages/Dataset/components/VersionCompareModal/index.tsx
index 10660221..5e2292f8 100644
--- a/react-ui/src/pages/Dataset/components/VersionCompareModal/index.tsx
+++ b/react-ui/src/pages/Dataset/components/VersionCompareModal/index.tsx
@@ -88,7 +88,7 @@ function VersionCompareModal({
format: formatProject,
},
{
- key: 'description',
+ key: 'version_desc',
text: '版本描述',
},
]
@@ -123,7 +123,7 @@ function VersionCompareModal({
format: formatTrainTask,
},
{
- key: 'description',
+ key: 'version_desc',
text: '版本描述',
},
],
@@ -193,7 +193,14 @@ function VersionCompareModal({
))}
-
{v1.version}
+
+
+ {v1.version}
+
+
{fields.map(({ key, format }) => {
const text = getValue(v1, key as keyof typeof v1, format);
return (
@@ -203,7 +210,7 @@ function VersionCompareModal({
[styles['version-compare__left__text--different']]: isDifferent(key),
})}
>
-
+
{isEmpty(text) ? '--' : text}
@@ -211,7 +218,14 @@ function VersionCompareModal({
})}
-
{v2.version}
+
+
+ {v2.version}
+
+
{fields.map(({ key, format }) => {
const text = getValue(v2, key as keyof typeof v2, format);
return (
@@ -221,7 +235,7 @@ function VersionCompareModal({
[styles['version-compare__right__text--different']]: isDifferent(key),
})}
>
-
+
{isEmpty(text) ? '--' : text}
diff --git a/react-ui/src/pages/DevelopmentEnvironment/Create/index.tsx b/react-ui/src/pages/DevelopmentEnvironment/Create/index.tsx
index d7717f54..e713b295 100644
--- a/react-ui/src/pages/DevelopmentEnvironment/Create/index.tsx
+++ b/react-ui/src/pages/DevelopmentEnvironment/Create/index.tsx
@@ -104,16 +104,16 @@ function EditorCreate() {
-
+
diff --git a/react-ui/src/pages/DevelopmentEnvironment/List/index.tsx b/react-ui/src/pages/DevelopmentEnvironment/List/index.tsx
index 4af043ba..5e48e3b2 100644
--- a/react-ui/src/pages/DevelopmentEnvironment/List/index.tsx
+++ b/react-ui/src/pages/DevelopmentEnvironment/List/index.tsx
@@ -6,7 +6,9 @@
import KFIcon from '@/components/KFIcon';
import { DevEditorStatus } from '@/enums';
-import { useCacheState } from '@/hooks/pageCacheState';
+import { useCacheState } from '@/hooks/useCacheState';
+import { useComputingResource } from '@/hooks/useComputingResource';
+import { DatasetData, ModelData } from '@/pages/Dataset/config';
import {
deleteEditorReq,
getEditorListReq,
@@ -14,6 +16,7 @@ import {
stopEditorReq,
} from '@/services/developmentEnvironment';
import themes from '@/styles/theme.less';
+import { parseJsonText } from '@/utils';
import { openAntdModal } from '@/utils/modal';
import { to } from '@/utils/promise';
import SessionStorage from '@/utils/sessionStorage';
@@ -42,6 +45,10 @@ export type EditorData = {
update_by: string;
create_time: string;
url: string;
+ computing_resource_id: number;
+ dataset?: string | DatasetData;
+ model?: string | ModelData;
+ image?: string;
};
function EditorList() {
@@ -56,6 +63,7 @@ function EditorList() {
pageSize: 10,
},
);
+ const getResourceDescription = useComputingResource()[1];
// 获取编辑器列表
const getEditorList = useCallback(async () => {
@@ -66,6 +74,10 @@ function EditorList() {
const [res] = await to(getEditorListReq(params));
if (res && res.data) {
const { content = [], totalElements = 0 } = res.data;
+ content.forEach((item: EditorData) => {
+ item.dataset = typeof item.dataset === 'string' ? parseJsonText(item.dataset) : null;
+ item.model = typeof item.model === 'string' ? parseJsonText(item.model) : null;
+ });
setTableData(content);
setTotal(totalElements);
}
@@ -80,17 +92,14 @@ function EditorList() {
const [res] = await to(deleteEditorReq(id));
if (res) {
message.success('删除成功');
- // 如果是一页的唯一数据,删除时,请求第一页的数据
+ // 如果是一页的唯一数据,删除后,请求第一页的数据
// 否则直接刷新这一页的数据
- // 避免回到第一页
- if (tableData.length > 1) {
- setPagination((prev) => ({
+ setPagination((prev) => {
+ return {
...prev,
- current: 1,
- }));
- } else {
- getEditorList();
- }
+ current: tableData.length === 1 ? Math.max(1, prev.current! - 1) : prev.current,
+ };
+ });
}
};
@@ -105,11 +114,18 @@ function EditorList() {
// 停止编辑器
const stopEditor = async (id: number) => {
- const [res] = await to(stopEditorReq(id));
- if (res) {
- message.success('操作成功');
- getEditorList();
- }
+ modalConfirm({
+ title: '停止后,该编辑器将不可使用',
+ content: '是否确认停止?',
+ isDelete: false,
+ onOk: async () => {
+ const [res] = await to(stopEditorReq(id));
+ if (res) {
+ message.success('操作成功');
+ getEditorList();
+ }
+ },
+ });
};
// 制作镜像
@@ -168,44 +184,72 @@ function EditorList() {
title: '编辑器名称',
dataIndex: 'name',
key: 'name',
- width: '30%',
- render: (text, record) =>
- record.url && record.status === DevEditorStatus.Running ? (
- gotoEditorPage(e, record)}>
- {text}
-
- ) : (
- {text ?? '--'}
- ),
- },
- {
- title: '状态',
- dataIndex: 'status',
- key: 'status',
- width: '10%',
- render: EditorStatusCell,
+ width: '20%',
+ render: (text, record, index) =>
+ record.url && record.status === DevEditorStatus.Running
+ ? tableCellRender(true, TableCellValueType.Link, {
+ onClick: (record, e) => gotoEditorPage(e, record),
+ })(text, record, index)
+ : tableCellRender(true, TableCellValueType.Text)(text, record, index),
},
{
- title: '资源',
+ title: '计算资源',
dataIndex: 'computing_resource',
key: 'computing_resource',
- width: '20%',
+ width: 100,
render: tableCellRender(),
},
+ {
+ title: '资源规格',
+ dataIndex: 'computing_resource_id',
+ key: 'computing_resource_id',
+ width: '20%',
+ render: tableCellRender(true, TableCellValueType.Custom, {
+ format: getResourceDescription,
+ }),
+ },
+ {
+ title: '数据集',
+ dataIndex: ['dataset', 'showValue'],
+ key: 'dataset',
+ width: '15%',
+ render: tableCellRender(true),
+ },
+ {
+ title: '模型',
+ dataIndex: ['model', 'showValue'],
+ key: 'model',
+ width: '15%',
+ render: tableCellRender(true),
+ },
+ {
+ title: '镜像',
+ dataIndex: ['image'],
+ key: 'image',
+ width: '15%',
+ render: tableCellRender(true),
+ },
{
title: '创建者',
dataIndex: 'update_by',
key: 'update_by',
- width: '20%',
- render: tableCellRender(),
+ width: '15%',
+ render: tableCellRender(true),
},
{
title: '创建时间',
dataIndex: 'create_time',
key: 'create_time',
- width: '20%',
+ width: 180,
render: tableCellRender(false, TableCellValueType.Date),
},
+ {
+ title: '状态',
+ dataIndex: 'status',
+ key: 'status',
+ width: 80,
+ render: EditorStatusCell,
+ },
{
title: '操作',
dataIndex: 'operation',
diff --git a/react-ui/src/pages/DevelopmentEnvironment/components/CreateMirrorModal/index.tsx b/react-ui/src/pages/DevelopmentEnvironment/components/CreateMirrorModal/index.tsx
index 8d2b27fa..7155bc30 100644
--- a/react-ui/src/pages/DevelopmentEnvironment/components/CreateMirrorModal/index.tsx
+++ b/react-ui/src/pages/DevelopmentEnvironment/components/CreateMirrorModal/index.tsx
@@ -20,7 +20,7 @@ function CreateMirrorModal({ envId, onOk, ...rest }: CreateMirrorModalProps) {
}),
);
if (res) {
- message.success('创建成功,请到 “AI资产” - “个人镜像” 中查看');
+ message.success('创建成功,请到 “多形态资源库” - “个人镜像” 中查看');
onOk?.();
}
};
@@ -51,20 +51,20 @@ function CreateMirrorModal({ envId, onOk, ...rest }: CreateMirrorModalProps) {
message: '请输入镜像名称',
},
{
- pattern: /^[a-z0-9/_-]*$/,
- message: '只支持小写字母、数字、下划线(_)、中横线(-)、斜杠(/)',
+ pattern: /^[a-z0-9/._-]*$/,
+ message: '只支持小写字母、数字、点(.)、下划线(_)、中横线(-)、斜杠(/)',
},
]}
>
-
+
diff --git a/react-ui/src/pages/Experiment/Aim/index.tsx b/react-ui/src/pages/Experiment/Aim/index.tsx
new file mode 100644
index 00000000..3a8d1d0d
--- /dev/null
+++ b/react-ui/src/pages/Experiment/Aim/index.tsx
@@ -0,0 +1,12 @@
+/*
+ * @Author: 赵伟
+ * @Date: 2025-03-31 16:38:59
+ * @Description: 实验对比 Aim
+ */
+
+import IframePage, { IframePageType } from '@/components/IFramePage';
+
+function AimPage() {
+ return ;
+}
+export default AimPage;
diff --git a/react-ui/src/pages/Experiment/Comparison/index.tsx b/react-ui/src/pages/Experiment/Comparison/index.tsx
index 4b2d6d5b..200b4ba1 100644
--- a/react-ui/src/pages/Experiment/Comparison/index.tsx
+++ b/react-ui/src/pages/Experiment/Comparison/index.tsx
@@ -12,8 +12,9 @@ import {
} from '@/services/experiment';
import { tableSorter } from '@/utils';
import { to } from '@/utils/promise';
+import SessionStorage from '@/utils/sessionStorage';
import tableCellRender, { TableCellValueType } from '@/utils/table';
-import { useSearchParams } from '@umijs/max';
+import { useNavigate, useSearchParams } from '@umijs/max';
import { App, Button, Table, TablePaginationConfig, TableProps } from 'antd';
import classNames from 'classnames';
import { useEffect, useMemo, useState } from 'react';
@@ -46,6 +47,7 @@ function ExperimentComparison() {
});
const { message } = App.useApp();
+ const navigate = useNavigate();
const config = comparisonConfig[comparisonType];
useEffect(() => {
@@ -73,7 +75,9 @@ function ExperimentComparison() {
const [res] = await to(getExpMetricsReq(selectedRowKeys));
if (res && res.data) {
const url = res.data;
- window.open(url, '_blank');
+ // window.open(url, '_blank');
+ SessionStorage.setItem(SessionStorage.aimUrlKey, url);
+ navigate('../compare-visual');
}
};
diff --git a/react-ui/src/pages/Experiment/Info/index.jsx b/react-ui/src/pages/Experiment/Info/index.jsx
index f0cf9ae6..fd795873 100644
--- a/react-ui/src/pages/Experiment/Info/index.jsx
+++ b/react-ui/src/pages/Experiment/Info/index.jsx
@@ -1,5 +1,6 @@
import { ExperimentStatus } from '@/enums';
-import { useStateRef, useVisible } from '@/hooks';
+import { useStateRef } from '@/hooks/useStateRef';
+import { useVisible } from '@/hooks/useVisible';
import { getExperimentIns } from '@/services/experiment/index.js';
import { getWorkflowById } from '@/services/pipeline/index.js';
import themes from '@/styles/theme.less';
@@ -179,11 +180,12 @@ function ExperimentText() {
if (!statusNode) {
return;
}
- const { finishedAt, startedAt, phase, id } = statusNode;
+ const { finishedAt, startedAt, phase, id, message } = statusNode;
workflowNode.experimentStartTime = startedAt;
workflowNode.experimentEndTime = finishedAt;
workflowNode.experimentStatus = phase;
workflowNode.workflowId = id;
+ workflowNode.message = message;
workflowNode.img = phase
? `${workflowNode.imgName}-${phase}.png`
: `${workflowNode.imgName}.png`;
diff --git a/react-ui/src/pages/Experiment/components/AddExperimentModal/index.tsx b/react-ui/src/pages/Experiment/components/AddExperimentModal/index.tsx
index 38a88b3a..710a4df4 100644
--- a/react-ui/src/pages/Experiment/components/AddExperimentModal/index.tsx
+++ b/react-ui/src/pages/Experiment/components/AddExperimentModal/index.tsx
@@ -3,7 +3,7 @@ import editExperimentIcon from '@/assets/img/edit-experiment.png';
import KFModal from '@/components/KFModal';
import { type PipelineGlobalParam } from '@/types';
import { to } from '@/utils/promise';
-import { Button, Form, Input, Radio, Select, type FormRule } from 'antd';
+import { Button, Form, Input, Radio, Select, Typography, type FormRule } from 'antd';
import { useState } from 'react';
import styles from './index.less';
@@ -63,13 +63,14 @@ export const getParamRules = (paramType: number, required: boolean = false): For
};
// 根据参数设置 label
-export const getParamType = (param: PipelineGlobalParam): string => {
+export const getParamLabel = (param: PipelineGlobalParam): React.ReactNode => {
const paramTypes: Readonly> = {
1: '字符串',
2: '整型',
3: '布尔类型',
};
- return param.param_name + `(${paramTypes[param.param_type]})`;
+ const label = param.param_name + `(${paramTypes[param.param_type]})`;
+ return {label};
};
function AddExperimentModal({
@@ -99,8 +100,8 @@ function AddExperimentModal({
};
const paramLayout = {
- labelCol: { span: 8 },
- wrapperCol: { span: 16 },
+ labelCol: { span: 6 },
+ wrapperCol: { span: 18 },
};
// 除了流水线选择发生变化
@@ -157,7 +158,6 @@ function AddExperimentModal({
form={form}
{...layout}
labelAlign="left"
- labelWrap
>
{getParamComponent(
globalParam[name]['param_type'],
diff --git a/react-ui/src/pages/Experiment/components/ExperimentDrawer/index.less b/react-ui/src/pages/Experiment/components/ExperimentDrawer/index.less
index e524a987..41cb8a19 100644
--- a/react-ui/src/pages/Experiment/components/ExperimentDrawer/index.less
+++ b/react-ui/src/pages/Experiment/components/ExperimentDrawer/index.less
@@ -13,7 +13,6 @@
}
&__tabs {
- height: calc(100% - 169px);
:global {
.ant-tabs-nav {
padding-left: 24px;
@@ -35,7 +34,7 @@
display: flex;
align-items: center;
margin-bottom: 15px;
- padding-left: 24px;
+ padding: 0 24px;
color: @text-color;
font-size: 15px;
}
diff --git a/react-ui/src/pages/Experiment/components/ExperimentDrawer/index.tsx b/react-ui/src/pages/Experiment/components/ExperimentDrawer/index.tsx
index c1a70141..3ee726e5 100644
--- a/react-ui/src/pages/Experiment/components/ExperimentDrawer/index.tsx
+++ b/react-ui/src/pages/Experiment/components/ExperimentDrawer/index.tsx
@@ -3,7 +3,7 @@ import { experimentStatusInfo } from '@/pages/Experiment/status';
import { PipelineNodeModelSerialize } from '@/types';
import { elapsedTime, formatDate } from '@/utils/date';
import { CloseOutlined, DatabaseOutlined, ProfileOutlined } from '@ant-design/icons';
-import { Drawer, Tabs } from 'antd';
+import { Drawer, Tabs, Typography } from 'antd';
import { useMemo } from 'react';
import ExperimentParameter from '../ExperimentParameter';
import ExperimentResult from '../ExperimentResult';
@@ -129,6 +129,14 @@ const ExperimentDrawer = ({
'--'
)}
+ {instanceNodeData.message && (
+
+
消息:
+
+ {instanceNodeData.message ?? '--'}
+
+
+ )}
启动时间:{formatDate(instanceNodeStartTime)}
@@ -137,7 +145,14 @@ const ExperimentDrawer = ({
{elapsedTime(instanceNodeStartTime, instanceNodeEndTime)}
-
+
);
};
diff --git a/react-ui/src/pages/Experiment/components/ExperimentInstance/index.tsx b/react-ui/src/pages/Experiment/components/ExperimentInstance/index.tsx
index d184deee..362cf995 100644
--- a/react-ui/src/pages/Experiment/components/ExperimentInstance/index.tsx
+++ b/react-ui/src/pages/Experiment/components/ExperimentInstance/index.tsx
@@ -1,6 +1,6 @@
import KFIcon from '@/components/KFIcon';
import { ExperimentStatus } from '@/enums';
-import { useCheck } from '@/hooks';
+import { useCheck } from '@/hooks/useCheck';
import { experimentStatusInfo } from '@/pages/Experiment/status';
import {
deleteManyExperimentIns,
@@ -62,7 +62,8 @@ function ExperimentInstanceComponent({
// 删除实验实例确认
const handleRemove = (instance: ExperimentInstance) => {
modalConfirm({
- title: '确定删除该条实例吗?',
+ title: '删除后,该实验实例将不可恢复',
+ content: '是否确认删除?',
onOk: () => {
deleteExperimentInstance(instance.id);
},
@@ -101,7 +102,8 @@ function ExperimentInstanceComponent({
// 终止实验实例
const handleTerminate = (instance: ExperimentInstance) => {
modalConfirm({
- title: '确定要终止此次实验运行吗?',
+ title: '终止后,该次实验运行将不可恢复',
+ content: '是否确认终止?',
isDelete: false,
onOk: () => {
terminateExperimentInstance(instance);
diff --git a/react-ui/src/pages/Experiment/components/LogGroup/index.tsx b/react-ui/src/pages/Experiment/components/LogGroup/index.tsx
index 3bc1b566..f64f23c4 100644
--- a/react-ui/src/pages/Experiment/components/LogGroup/index.tsx
+++ b/react-ui/src/pages/Experiment/components/LogGroup/index.tsx
@@ -5,7 +5,7 @@
*/
import { ExperimentStatus } from '@/enums';
-import { useStateRef } from '@/hooks';
+import { useStateRef } from '@/hooks/useStateRef';
import { getExperimentPodsLog } from '@/services/experiment/index.js';
import { DoubleRightOutlined, DownOutlined, UpOutlined } from '@ant-design/icons';
import { Button } from 'antd';
diff --git a/react-ui/src/pages/Experiment/components/ViewParamsModal/index.less b/react-ui/src/pages/Experiment/components/ViewParamsModal/index.less
index a2b0ded5..06c5a4f0 100644
--- a/react-ui/src/pages/Experiment/components/ViewParamsModal/index.less
+++ b/react-ui/src/pages/Experiment/components/ViewParamsModal/index.less
@@ -1,31 +1,14 @@
-.params_container {
- max-height: 230px;
- padding: 15px 15px 0;
+.params-container {
+ max-height: calc(100vh - 300px);
+ padding: 24px 24px 0;
overflow-y: auto;
border: 1px solid #e6e6e6;
border-radius: 8px;
-
- &_line {
- display: flex;
- align-items: center;
- margin-bottom: 15px;
-
- &_label {
- width: 180px;
- color: @text-color;
- font-size: 15px;
- }
- &_value {
- flex: 1;
- width: 100px;
- margin-left: 15px;
- padding: 10px 20px;
- color: @text-color;
- font-size: @font-size;
- line-height: 20px;
- background: #f6f6f6;
- border: 1px solid #e0e0e1;
- border-radius: 4px;
+}
+.params-empty {
+ :global {
+ .kf-empty__image {
+ width: 300px;
}
}
}
diff --git a/react-ui/src/pages/Experiment/components/ViewParamsModal/index.tsx b/react-ui/src/pages/Experiment/components/ViewParamsModal/index.tsx
index f860135a..8bd49817 100644
--- a/react-ui/src/pages/Experiment/components/ViewParamsModal/index.tsx
+++ b/react-ui/src/pages/Experiment/components/ViewParamsModal/index.tsx
@@ -4,9 +4,11 @@
* @Description: 查看实验使用的参数
*/
import parameterImg from '@/assets/img/modal-parameter.png';
+import KFEmpty, { EmptyType } from '@/components/KFEmpty';
import KFModal from '@/components/KFModal';
import { type PipelineGlobalParam } from '@/types';
-import { getParamType } from '../AddExperimentModal';
+import { Form } from 'antd';
+import { getParamComponent, getParamLabel } from '../AddExperimentModal';
import styles from './index.less';
type ParamsModalProps = {
@@ -26,14 +28,44 @@ function ParamsModal({ open, onCancel, globalParam = [] }: ParamsModalProps) {
cancelButtonProps={{ style: { display: 'none' } }}
width={825}
>
-
- {globalParam?.map((item) => (
-
- {getParamType(item)}
- {item.param_value}
-
- ))}
-
+ {Array.isArray(globalParam) && globalParam.length > 0 ? (
+
+
+ {(fields) =>
+ fields.map(({ key, name, ...restField }) => (
+
+ {getParamComponent(
+ globalParam[name]['param_type'],
+ globalParam[name]['is_sensitive'],
+ )}
+
+ ))
+ }
+
+
+
+ ) : (
+
+ )}
);
}
diff --git a/react-ui/src/pages/Experiment/index.jsx b/react-ui/src/pages/Experiment/index.jsx
index 9b7cab84..b0006ce0 100644
--- a/react-ui/src/pages/Experiment/index.jsx
+++ b/react-ui/src/pages/Experiment/index.jsx
@@ -1,7 +1,7 @@
import KFIcon from '@/components/KFIcon';
import PageTitle from '@/components/PageTitle';
import { ExperimentStatus, TensorBoardStatus } from '@/enums';
-import { useCacheState } from '@/hooks/pageCacheState';
+import { useCacheState } from '@/hooks/useCacheState';
import {
deleteExperimentById,
getExperiment,
@@ -206,6 +206,7 @@ function Experiment() {
setExpandedRowKeys(null);
} else {
getQueryByExperiment(record.id, 0);
+ refreshExperimentList();
}
};
@@ -285,8 +286,6 @@ function Experiment() {
message.success('运行成功');
refreshExperimentList();
refreshExperimentIns(id);
- } else {
- message.error('运行失败');
}
};
@@ -377,6 +376,31 @@ function Experiment() {
getQueryByExperiment(expandedRowKeys, page);
};
+ // 处理删除
+ const handleExperimentDelete = (record) => {
+ modalConfirm({
+ title: '删除后,该实验将不可恢复',
+ content: '是否确认删除?',
+ onOk: () => {
+ deleteExperimentById(record.id).then((ret) => {
+ if (ret.code === 200) {
+ message.success('删除成功');
+ // 如果是一页的唯一数据,删除后,请求第一页的数据
+ // 否则直接刷新这一页的数据
+ setPagination((prev) => {
+ return {
+ ...prev,
+ current: experimentList.length === 1 ? Math.max(1, prev.current - 1) : prev.current,
+ };
+ });
+ } else {
+ message.error(ret.msg);
+ }
+ });
+ },
+ });
+ };
+
const columns = [
{
title: '实验名称',
@@ -475,22 +499,7 @@ function Experiment() {
size="small"
key="batchRemove"
icon={}
- onClick={() => {
- modalConfirm({
- title: '删除后,该实验将不可恢复',
- content: '是否确认删除?',
- onOk: () => {
- deleteExperimentById(record.id).then((ret) => {
- if (ret.code === 200) {
- message.success('删除成功');
- getExperimentList();
- } else {
- message.error(ret.msg);
- }
- });
- },
- });
- }}
+ onClick={() => handleExperimentDelete(record)}
>
删除
@@ -499,6 +508,7 @@ function Experiment() {
),
},
];
+
return (
diff --git a/react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.tsx b/react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.tsx
index feb72d48..77cbff36 100644
--- a/react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.tsx
+++ b/react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.tsx
@@ -1,6 +1,6 @@
import ConfigInfo, { type BasicInfoData } from '@/components/ConfigInfo';
import { hyperParameterOptimizedMode } from '@/enums';
-import { useComputingResource } from '@/hooks/resource';
+import { useComputingResource } from '@/hooks/useComputingResource';
import { experimentStatusInfo } from '@/pages/Experiment/status';
import {
schedulerAlgorithms,
diff --git a/react-ui/src/pages/Mirror/Create/index.tsx b/react-ui/src/pages/Mirror/Create/index.tsx
index 7db2f4d0..a32e99a1 100644
--- a/react-ui/src/pages/Mirror/Create/index.tsx
+++ b/react-ui/src/pages/Mirror/Create/index.tsx
@@ -44,7 +44,7 @@ const mirrorRadioItems: KFRadioItem[] = [
function MirrorCreate() {
const navigate = useNavigate();
const [form] = Form.useForm();
- const [nameDisabled, setNameDisabled] = useState(false);
+ const [isAddVersion, setIsAddVersion] = useState(false); // 是制作镜像还是新增镜像版本
const { message } = App.useApp();
const uploadProps: UploadProps = {
@@ -60,7 +60,7 @@ function MirrorCreate() {
const name = SessionStorage.getItem(SessionStorage.mirrorNameKey);
if (name) {
form.setFieldValue('name', name);
- setNameDisabled(true);
+ setIsAddVersion(true);
}
return () => {
SessionStorage.removeItem(SessionStorage.mirrorNameKey);
@@ -70,32 +70,37 @@ function MirrorCreate() {
// 创建公网、本地镜像
const createPublicMirror = async (formData: FormData) => {
const upload_type = formData['upload_type'];
- let params;
+
if (upload_type === CommonTabKeys.Public) {
- params = {
+ const params = {
...omit(formData, ['upload_type']),
upload_type: 0,
image_type: 0,
};
+ const [res] = await to(createMirrorReq(params));
+ if (res) {
+ message.success('创建成功');
+ navigate(-1);
+ }
} else {
const fileList = formData['fileList'] ?? [];
if (validateUploadFiles(fileList)) {
const file = fileList[0];
- params = {
+ const params = {
...omit(formData, ['fileList', 'upload_type']),
path: file.response.data.url,
file_size: file.response.data.fileSize,
+ file_name: file.response.data.fileName,
upload_type: 1,
image_type: 0,
};
+ const [res] = await to(createMirrorReq(params));
+ if (res) {
+ message.success('创建成功');
+ navigate(-1);
+ }
}
}
-
- const [res] = await to(createMirrorReq(params));
- if (res) {
- message.success('创建成功');
- navigate(-1);
- }
};
// 提交
@@ -118,14 +123,16 @@ function MirrorCreate() {
return true;
};
+ const descTitle = isAddVersion ? '版本描述' : '镜像描述';
+
return (
-
+
@@ -174,33 +181,33 @@ function MirrorCreate() {
rules={[
{
required: true,
- message: '请输入镜像Tag',
+ message: '请输入镜像版本',
},
{
pattern: /^[a-zA-Z0-9._-]+$/,
- message: '版本只支持字母、数字、点(.)、下划线(_)、中横线(-)',
+ message: '镜像版本只支持字母、数字、点(.)、下划线(_)、中横线(-)',
},
]}
>
-
+
@@ -303,7 +310,7 @@ function MirrorCreate() {
)}