From 5d43558c2cd035dd659dd1e5a922bc38bc55158f Mon Sep 17 00:00:00 2001 From: cp3hnu Date: Fri, 23 Aug 2024 14:49:27 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=E7=99=BB=E5=BD=95=E5=9B=A0?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=E9=94=99=E8=AF=AF=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E9=AA=8C=E8=AF=81=E7=A0=81=E8=BE=93=E5=85=A5?= =?UTF-8?q?=E6=A1=86=E8=87=AA=E5=8A=A8=E8=81=9A=E7=84=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- react-ui/src/pages/Experiment/index.jsx | 8 ++++---- react-ui/src/pages/User/Login/index.tsx | 18 ++++++++++++------ react-ui/src/requestConfig.ts | 2 +- react-ui/src/utils/promise.ts | 2 +- 4 files changed, 18 insertions(+), 12 deletions(-) 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/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/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]; From a7b5ecd3c26c319ec0eb993a81f7ce6fb449833c Mon Sep 17 00:00:00 2001 From: cp3hnu Date: Wed, 28 Aug 2024 14:57:33 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=E4=BF=AE=E6=94=B9=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E5=9B=BE=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/MenuIconSelector/index.tsx | 2 +- react-ui/src/iconfont/iconfont-menu.js | 2 +- .../{iconfont.json => iconfont-menu.json} | 84 +++++++++---------- react-ui/src/iconfont/iconfont.js | 2 +- 4 files changed, 45 insertions(+), 45 deletions(-) rename react-ui/src/iconfont/{iconfont.json => iconfont-menu.json} (86%) 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 From ed3a9304bf8059dc6a86d1b17ce0f2938498ebc0 Mon Sep 17 00:00:00 2001 From: cp3hnu Date: Wed, 28 Aug 2024 14:58:13 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E5=88=B6?= =?UTF-8?q?=E4=BD=9C=E9=95=9C=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DevelopmentEnvironment/List/index.tsx | 24 +++++ .../components/CreateMirrorModal/index.tsx | 100 ++++++++++++++++++ .../components/AddExperimentModal/index.tsx | 2 +- react-ui/src/pages/Mirror/Create/index.tsx | 8 ++ .../services/developmentEnvironment/index.ts | 9 ++ 5 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 react-ui/src/pages/DevelopmentEnvironment/components/CreateMirrorModal/index.tsx 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 ? ( + + ) : 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/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/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, + }); +}