diff --git a/react-ui/src/components/MenuIconSelector/index.tsx b/react-ui/src/components/MenuIconSelector/index.tsx
index 2709c22d..2204c348 100644
--- a/react-ui/src/components/MenuIconSelector/index.tsx
+++ b/react-ui/src/components/MenuIconSelector/index.tsx
@@ -1,6 +1,6 @@
import KFIcon from '@/components/KFIcon';
import KFModal from '@/components/KFModal';
-import iconData from '@/iconfont/iconfont.json';
+import iconData from '@/iconfont/iconfont-menu.json';
import { type ModalProps } from 'antd';
import { useEffect, useState } from 'react';
import styles from './index.less';
diff --git a/react-ui/src/iconfont/iconfont-menu.js b/react-ui/src/iconfont/iconfont-menu.js
index 211a58a7..2b0bcaa9 100644
--- a/react-ui/src/iconfont/iconfont-menu.js
+++ b/react-ui/src/iconfont/iconfont-menu.js
@@ -1 +1 @@
-window._iconfont_svg_string_4511326='',function(t){var a=(a=document.getElementsByTagName("script"))[a.length-1],l=a.getAttribute("data-injectcss"),a=a.getAttribute("data-disable-injectsvg");if(!a){var h,i,o,c,e,m=function(a,l){l.parentNode.insertBefore(a,l)};if(l&&!t.__iconfont__svg__cssinject__){t.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}h=function(){var a,l=document.createElement("div");l.innerHTML=t._iconfont_svg_string_4511326,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(a=document.body).firstChild?m(l,a.firstChild):a.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(i=function(){document.removeEventListener("DOMContentLoaded",i,!1),h()},document.addEventListener("DOMContentLoaded",i,!1)):document.attachEvent&&(o=h,c=t.document,e=!1,n(),c.onreadystatechange=function(){"complete"==c.readyState&&(c.onreadystatechange=null,p())})}function p(){e||(e=!0,o())}function n(){try{c.documentElement.doScroll("left")}catch(a){return void setTimeout(n,50)}p()}}(window);
\ No newline at end of file
+window._iconfont_svg_string_4511326='',(t=>{var a=(l=(l=document.getElementsByTagName("script"))[l.length-1]).getAttribute("data-injectcss"),l=l.getAttribute("data-disable-injectsvg");if(!l){var i,h,o,c,e,m=function(a,l){l.parentNode.insertBefore(a,l)};if(a&&!t.__iconfont__svg__cssinject__){t.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}i=function(){var a,l=document.createElement("div");l.innerHTML=t._iconfont_svg_string_4511326,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(a=document.body).firstChild?m(l,a.firstChild):a.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(i,0):(h=function(){document.removeEventListener("DOMContentLoaded",h,!1),i()},document.addEventListener("DOMContentLoaded",h,!1)):document.attachEvent&&(o=i,c=t.document,e=!1,n(),c.onreadystatechange=function(){"complete"==c.readyState&&(c.onreadystatechange=null,p())})}function p(){e||(e=!0,o())}function n(){try{c.documentElement.doScroll("left")}catch(a){return void setTimeout(n,50)}p()}})(window);
\ No newline at end of file
diff --git a/react-ui/src/iconfont/iconfont.json b/react-ui/src/iconfont/iconfont-menu.json
similarity index 86%
rename from react-ui/src/iconfont/iconfont.json
rename to react-ui/src/iconfont/iconfont-menu.json
index db913c64..297c7837 100644
--- a/react-ui/src/iconfont/iconfont.json
+++ b/react-ui/src/iconfont/iconfont-menu.json
@@ -5,6 +5,48 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
+ {
+ "icon_id": "41643218",
+ "name": "模型开发",
+ "font_class": "model-icon",
+ "unicode": "e624",
+ "unicode_decimal": 58916
+ },
+ {
+ "icon_id": "41643132",
+ "name": "工作空间",
+ "font_class": "workspace-icon",
+ "unicode": "e628",
+ "unicode_decimal": 58920
+ },
+ {
+ "icon_id": "41643135",
+ "name": "系统管理",
+ "font_class": "system-icon",
+ "unicode": "e622",
+ "unicode_decimal": 58914
+ },
+ {
+ "icon_id": "41643131",
+ "name": "数据准备",
+ "font_class": "datasetPreparation-icon",
+ "unicode": "e625",
+ "unicode_decimal": 58917
+ },
+ {
+ "icon_id": "41643133",
+ "name": "开发环境",
+ "font_class": "developmentEnvironment-icon",
+ "unicode": "e626",
+ "unicode_decimal": 58918
+ },
+ {
+ "icon_id": "41642989",
+ "name": "使用手册",
+ "font_class": "manual-icon",
+ "unicode": "e623",
+ "unicode_decimal": 58915
+ },
{
"icon_id": "40233218",
"name": "操作手册-active",
@@ -12,13 +54,6 @@
"unicode": "e62c",
"unicode_decimal": 58924
},
- {
- "icon_id": "40233217",
- "name": "操作手册",
- "font_class": "manual-icon",
- "unicode": "e62d",
- "unicode_decimal": 58925
- },
{
"icon_id": "40171713",
"name": "监控运维-active",
@@ -40,20 +75,6 @@
"unicode": "e62a",
"unicode_decimal": 58922
},
- {
- "icon_id": "40171699",
- "name": "开发环境",
- "font_class": "developmentEnvironment-icon",
- "unicode": "e62b",
- "unicode_decimal": 58923
- },
- {
- "icon_id": "39969575",
- "name": "系统管理",
- "font_class": "system-icon",
- "unicode": "e618",
- "unicode_decimal": 58904
- },
{
"icon_id": "39969573",
"name": "流水线-active",
@@ -68,13 +89,6 @@
"unicode": "e61c",
"unicode_decimal": 58908
},
- {
- "icon_id": "39969568",
- "name": "数据准备",
- "font_class": "datasetPreparation-icon",
- "unicode": "e61d",
- "unicode_decimal": 58909
- },
{
"icon_id": "39969570",
"name": "模型在线部署",
@@ -103,13 +117,6 @@
"unicode": "e621",
"unicode_decimal": 58913
},
- {
- "icon_id": "39969580",
- "name": "工作空间",
- "font_class": "workspace-icon",
- "unicode": "e611",
- "unicode_decimal": 58897
- },
{
"icon_id": "39969572",
"name": "模型开发-active",
@@ -131,13 +138,6 @@
"unicode": "e613",
"unicode_decimal": 58899
},
- {
- "icon_id": "39969565",
- "name": "模型开发",
- "font_class": "model-icon",
- "unicode": "e614",
- "unicode_decimal": 58900
- },
{
"icon_id": "39969577",
"name": "应用开发",
diff --git a/react-ui/src/iconfont/iconfont.js b/react-ui/src/iconfont/iconfont.js
index 13c3ff7c..5326fd3a 100644
--- a/react-ui/src/iconfont/iconfont.js
+++ b/react-ui/src/iconfont/iconfont.js
@@ -1 +1 @@
-window._iconfont_svg_string_4511447='',function(t){var a=(a=document.getElementsByTagName("script"))[a.length-1],h=a.getAttribute("data-injectcss"),a=a.getAttribute("data-disable-injectsvg");if(!a){var l,v,z,i,o,m=function(a,h){h.parentNode.insertBefore(a,h)};if(h&&!t.__iconfont__svg__cssinject__){t.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}l=function(){var a,h=document.createElement("div");h.innerHTML=t._iconfont_svg_string_4511447,(h=h.getElementsByTagName("svg")[0])&&(h.setAttribute("aria-hidden","true"),h.style.position="absolute",h.style.width=0,h.style.height=0,h.style.overflow="hidden",h=h,(a=document.body).firstChild?m(h,a.firstChild):a.appendChild(h))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(l,0):(v=function(){document.removeEventListener("DOMContentLoaded",v,!1),l()},document.addEventListener("DOMContentLoaded",v,!1)):document.attachEvent&&(z=l,i=t.document,o=!1,d(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,p())})}function p(){o||(o=!0,z())}function d(){try{i.documentElement.doScroll("left")}catch(a){return void setTimeout(d,50)}p()}}(window);
\ No newline at end of file
+window._iconfont_svg_string_4511447='',(t=>{var a=(h=(h=document.getElementsByTagName("script"))[h.length-1]).getAttribute("data-injectcss"),h=h.getAttribute("data-disable-injectsvg");if(!h){var l,v,z,i,o,m=function(a,h){h.parentNode.insertBefore(a,h)};if(a&&!t.__iconfont__svg__cssinject__){t.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}l=function(){var a,h=document.createElement("div");h.innerHTML=t._iconfont_svg_string_4511447,(h=h.getElementsByTagName("svg")[0])&&(h.setAttribute("aria-hidden","true"),h.style.position="absolute",h.style.width=0,h.style.height=0,h.style.overflow="hidden",h=h,(a=document.body).firstChild?m(h,a.firstChild):a.appendChild(h))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(l,0):(v=function(){document.removeEventListener("DOMContentLoaded",v,!1),l()},document.addEventListener("DOMContentLoaded",v,!1)):document.attachEvent&&(z=l,i=t.document,o=!1,d(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,p())})}function p(){o||(o=!0,z())}function d(){try{i.documentElement.doScroll("left")}catch(a){return void setTimeout(d,50)}p()}})(window);
\ No newline at end of file
diff --git a/react-ui/src/pages/DevelopmentEnvironment/List/index.tsx b/react-ui/src/pages/DevelopmentEnvironment/List/index.tsx
index 504a4d9d..2b8fdf4e 100644
--- a/react-ui/src/pages/DevelopmentEnvironment/List/index.tsx
+++ b/react-ui/src/pages/DevelopmentEnvironment/List/index.tsx
@@ -3,6 +3,7 @@
* @Date: 2024-04-16 13:58:08
* @Description: 开发环境列表
*/
+
import CommonTableCell from '@/components/CommonTableCell';
import DateTableCell from '@/components/DateTableCell';
import KFIcon from '@/components/KFIcon';
@@ -15,6 +16,7 @@ import {
stopEditorReq,
} from '@/services/developmentEnvironment';
import themes from '@/styles/theme.less';
+import { openAntdModal } from '@/utils/modal';
import { to } from '@/utils/promise';
import { editorUrlKey, setSessionStorageItem } from '@/utils/sessionStorage';
import { modalConfirm } from '@/utils/ui';
@@ -29,6 +31,7 @@ import {
} from 'antd';
import classNames from 'classnames';
import { useEffect, useState } from 'react';
+import CreateMirrorModal from '../components/CreateMirrorModal';
import EditorStatusCell from '../components/EditorStatusCell';
import styles from './index.less';
@@ -110,6 +113,16 @@ function EditorList() {
}
};
+ // 制作镜像
+ const createMirror = (id: number) => {
+ const { close } = openAntdModal(CreateMirrorModal, {
+ envId: id,
+ onOk: () => {
+ close();
+ },
+ });
+ };
+
// 处理删除
const handleEditorDelete = (record: EditorData) => {
modalConfirm({
@@ -218,6 +231,17 @@ function EditorList() {
启动
)}
+ {record.status === DevEditorStatus.Running ? (
+ }
+ onClick={() => createMirror(record.id)}
+ >
+ 制作镜像
+
+ ) : null}
{
+ envId: number; // 开发环境id
+ onOk: () => void;
+}
+
+function CreateMirrorModal({ envId, onOk, ...rest }: CreateMirrorModalProps) {
+ // 上传请求
+ const createDatasetVersion = async (params: any) => {
+ const [res] = await to(
+ createEditorMirrorReq({
+ ...params,
+ dev_environment_id: envId,
+ upload_type: 1,
+ version: params['tagName'],
+ }),
+ );
+ if (res) {
+ message.success('创建成功,请到 “AI资产” - “个人镜像” 中查看');
+ onOk?.();
+ }
+ };
+
+ // 提交
+ const onFinish = (formData: any) => {
+ createDatasetVersion(formData);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default CreateMirrorModal;
diff --git a/react-ui/src/pages/Experiment/components/AddExperimentModal/index.tsx b/react-ui/src/pages/Experiment/components/AddExperimentModal/index.tsx
index becfc0a7..126e0557 100644
--- a/react-ui/src/pages/Experiment/components/AddExperimentModal/index.tsx
+++ b/react-ui/src/pages/Experiment/components/AddExperimentModal/index.tsx
@@ -51,7 +51,7 @@ export const getParamRules = (paramType: number, required: boolean = false): For
// 防止后台返回不是 number 类型
if (Number(paramType) === 2) {
rules.push({
- pattern: /^-?\d+(\.\d+)?$/,
+ pattern: /^-?((0(\.0*[1-9]\d*)?)|([1-9]\d*(\.\d+)?))$/,
message: '整型必须是数字',
});
}
diff --git a/react-ui/src/pages/Experiment/index.jsx b/react-ui/src/pages/Experiment/index.jsx
index 9f3d55c7..9faf4eee 100644
--- a/react-ui/src/pages/Experiment/index.jsx
+++ b/react-ui/src/pages/Experiment/index.jsx
@@ -74,7 +74,7 @@ function Experiment() {
page: pageOption.current.page - 1,
size: pageOption.current.size,
};
- const [res, _] = await to(getExperiment(params));
+ const [res] = await to(getExperiment(params));
if (res && res.data && Array.isArray(res.data.content)) {
setExperimentList(
res.data.content.map((item) => {
@@ -88,7 +88,7 @@ function Experiment() {
// 获取流水线列表
const getWorkflowList = async () => {
- const [res, _] = await to(getWorkflow(queryFlow));
+ const [res] = await to(getWorkflow(queryFlow));
if (res && res.data && res.data.content) {
setWorkflowList(res.data.content);
}
@@ -236,7 +236,7 @@ function Experiment() {
...values,
global_param,
};
- const [res, _] = await to(postExperiment(params));
+ const [res] = await to(postExperiment(params));
if (res) {
message.success('新建实验成功');
setIsModalOpen(false);
@@ -244,7 +244,7 @@ function Experiment() {
}
} else {
const params = { ...values, global_param, id: experimentId };
- const [res, _] = await to(putExperiment(params));
+ const [res] = await to(putExperiment(params));
if (res) {
message.success('编辑实验成功');
setIsModalOpen(false);
diff --git a/react-ui/src/pages/Mirror/Create/index.tsx b/react-ui/src/pages/Mirror/Create/index.tsx
index 2f03a901..4c60cc77 100644
--- a/react-ui/src/pages/Mirror/Create/index.tsx
+++ b/react-ui/src/pages/Mirror/Create/index.tsx
@@ -153,6 +153,10 @@ function MirrorCreate() {
required: true,
message: '请输入镜像名称',
},
+ {
+ pattern: /^[a-zA-Z0-9_-]*$/,
+ message: '只支持字母、数字、下划线、中横线',
+ },
]}
>
diff --git a/react-ui/src/pages/User/Login/index.tsx b/react-ui/src/pages/User/Login/index.tsx
index c4450e3f..81397fcd 100644
--- a/react-ui/src/pages/User/Login/index.tsx
+++ b/react-ui/src/pages/User/Login/index.tsx
@@ -3,8 +3,8 @@ import { getCaptchaImg, login } from '@/services/system/auth';
import { loginPasswordKey, loginUserKey, rememberPasswordKey } from '@/utils/localStorage';
import { to } from '@/utils/promise';
import { history, useModel } from '@umijs/max';
-import { Button, Checkbox, Flex, Form, Image, Input, message } from 'antd';
-import React, { useEffect, useState } from 'react';
+import { Button, Checkbox, Flex, Form, Image, Input, message, type InputRef } from 'antd';
+import { useEffect, useRef, useState } from 'react';
import { flushSync } from 'react-dom';
import styles from './login.less';
@@ -17,12 +17,13 @@ const LoginInputPrefix = ({ icon }: { icon: string }) => {
);
};
-const Login: React.FC = () => {
+const Login = () => {
const { initialState, setInitialState } = useModel('@@initialState');
const [captchaCode, setCaptchaCode] = useState('');
const [uuid, setUuid] = useState('');
const [form] = Form.useForm();
const [usernameReadOnly, setUsernameReadOnly] = useState(true);
+ const captchaInputRef = useRef(null);
useEffect(() => {
getCaptchaCode();
@@ -59,11 +60,11 @@ const Login: React.FC = () => {
// 登录
const handleSubmit = async (values: API.LoginParams) => {
- const [response] = await to(login({ ...values, uuid }));
- if (response && response.data) {
+ const [res, error] = await to(login({ ...values, uuid }));
+ if (res && res.data) {
const current = new Date();
const expireTime = current.setTime(current.getTime() + 1000 * 12 * 60 * 60);
- const { access_token } = response.data;
+ const { access_token } = res.data;
setSessionToken(access_token, access_token, expireTime);
message.success('登录成功!');
@@ -80,6 +81,10 @@ const Login: React.FC = () => {
const urlParams = new URL(window.location.href).searchParams;
history.push(urlParams.get('redirect') || '/');
} else {
+ if (error?.data?.code === 500 && error?.data?.msg === '验证码错误') {
+ captchaInputRef.current?.focus();
+ }
+
clearSessionToken();
getCaptchaCode();
}
@@ -156,6 +161,7 @@ const Login: React.FC = () => {
prefix={
}
+ ref={captchaInputRef}
allowClear
/>
diff --git a/react-ui/src/requestConfig.ts b/react-ui/src/requestConfig.ts
index 7ea1bf26..01911926 100644
--- a/react-ui/src/requestConfig.ts
+++ b/react-ui/src/requestConfig.ts
@@ -10,7 +10,7 @@ import { setRemoteMenu } from './services/session';
import { gotoLoginPage } from './utils/ui';
// [antd: Notification] You are calling notice in render which will break in React 18 concurrent mode. Please trigger in effect instead.
-const popupError = (error: string, skipErrorHandler?: boolean = false) => {
+const popupError = (error: string, skipErrorHandler: boolean | undefined = false) => {
if (skipErrorHandler) {
return;
}
diff --git a/react-ui/src/services/developmentEnvironment/index.ts b/react-ui/src/services/developmentEnvironment/index.ts
index 1d7b6f4d..31f81736 100644
--- a/react-ui/src/services/developmentEnvironment/index.ts
+++ b/react-ui/src/services/developmentEnvironment/index.ts
@@ -49,9 +49,18 @@ export function startEditorReq(id: number) {
method: 'POST',
});
}
+
// 停止编辑器
export function stopEditorReq(id: number) {
return request(`/api/mmp/jupyter/stop/${id}`, {
method: 'DELETE',
});
}
+
+// 制作镜像
+export function createEditorMirrorReq(data: any) {
+ return request(`/api/mmp/image/saveImage`, {
+ method: 'POST',
+ data,
+ });
+}
diff --git a/react-ui/src/utils/promise.ts b/react-ui/src/utils/promise.ts
index 661275ff..1919ecd1 100644
--- a/react-ui/src/utils/promise.ts
+++ b/react-ui/src/utils/promise.ts
@@ -2,7 +2,7 @@
* @param { Promise } promise
* @return { Promise }
*/
-export async function to(promise: Promise): Promise<[T, null] | [null, U]> {
+export async function to(promise: Promise): Promise<[T, null] | [null, U]> {
try {
const data = await promise;
return [data, null];