| @@ -0,0 +1,7 @@ | |||
| .kf-confirm-modal { | |||
| &__content { | |||
| width: 100%; | |||
| font-size: 18px; | |||
| text-align: center; | |||
| } | |||
| } | |||
| @@ -0,0 +1,37 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-10-10 10:54:25 | |||
| * @Description: 自定义 Confirm Modal | |||
| */ | |||
| import classNames from 'classnames'; | |||
| import KFModal, { KFModalProps } from '../KFModal'; | |||
| import './index.less'; | |||
| export interface KFConfirmModalProps extends KFModalProps { | |||
| content: string; | |||
| } | |||
| function KFConfirmModal({ | |||
| title, | |||
| image, | |||
| className = '', | |||
| centered, | |||
| maskClosable, | |||
| content, | |||
| ...rest | |||
| }: KFConfirmModalProps) { | |||
| return ( | |||
| <KFModal | |||
| className={classNames(['kf-confirm-modal', className])} | |||
| {...rest} | |||
| centered={centered ?? true} | |||
| maskClosable={maskClosable ?? false} | |||
| title={title} | |||
| image={image ?? require('@/assets/img/edit-experiment.png')} | |||
| > | |||
| <div className="kf-confirm-modal__content">{content}</div> | |||
| </KFModal> | |||
| ); | |||
| } | |||
| export default KFConfirmModal; | |||
| @@ -80,7 +80,7 @@ export enum ServiceType { | |||
| export const serviceTypeOptions = [ | |||
| { label: '视频', value: ServiceType.Video }, | |||
| { label: '图像', value: ServiceType.Image }, | |||
| { label: '图片', value: ServiceType.Image }, | |||
| { label: '音频', value: ServiceType.Audio }, | |||
| { label: '文本', value: ServiceType.Text }, | |||
| ]; | |||
| @@ -18,7 +18,7 @@ import { useNavigate } from '@umijs/max'; | |||
| import { App, Button, Col, Form, Input, Row, Select } from 'antd'; | |||
| import { pick } from 'lodash'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { ServiceData, ServiceOperationType } from '../types'; | |||
| import { ServiceData, ServiceOperationType, createServiceVersionMessage } from '../types'; | |||
| import styles from './index.less'; | |||
| // 表单数据 | |||
| @@ -59,9 +59,12 @@ function CreateService() { | |||
| ...formData, | |||
| }; | |||
| const [res] = await to(request(params)); | |||
| if (res) { | |||
| if (res && res.data) { | |||
| message.success('操作成功'); | |||
| navigate(-1); | |||
| setTimeout(() => { | |||
| window.postMessage({ type: createServiceVersionMessage, payload: res.data.id }); | |||
| }, 500); | |||
| } | |||
| }; | |||
| @@ -31,7 +31,12 @@ import { useNavigate, useParams } from '@umijs/max'; | |||
| import { App, Button, Col, Flex, Form, Input, Row, Select } from 'antd'; | |||
| import { omit, pick } from 'lodash'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { ServiceData, ServiceOperationType, ServiceVersionData } from '../types'; | |||
| import { | |||
| CreateServiceVersionFrom, | |||
| ServiceData, | |||
| ServiceOperationType, | |||
| ServiceVersionData, | |||
| } from '../types'; | |||
| import styles from './index.less'; | |||
| // 表单数据 | |||
| @@ -53,6 +58,7 @@ function CreateServiceVersion() { | |||
| const [form] = Form.useForm(); | |||
| const [resourceStandardList, filterResourceStandard] = useComputingResource(); | |||
| const [operationType, setOperationType] = useState(ServiceOperationType.Create); | |||
| const [lastPage, setLastPage] = useState(CreateServiceVersionFrom.ServiceInfo); | |||
| const { message } = App.useApp(); | |||
| const [serviceInfo, setServiceInfo] = useState<ServiceData | undefined>(undefined); | |||
| const [versionInfo, setVersionInfo] = useState<ServiceVersionData | undefined>(undefined); | |||
| @@ -60,10 +66,15 @@ function CreateServiceVersion() { | |||
| const id = params.id; | |||
| useEffect(() => { | |||
| const res: (ServiceVersionData & { operationType: ServiceOperationType }) | undefined = | |||
| getSessionStorageItem(serviceVersionInfoKey, true); | |||
| const res: | |||
| | (ServiceVersionData & { | |||
| operationType: ServiceOperationType; | |||
| lastPage: CreateServiceVersionFrom; | |||
| }) | |||
| | undefined = getSessionStorageItem(serviceVersionInfoKey, true); | |||
| if (res) { | |||
| setOperationType(res.operationType); | |||
| setLastPage(res.lastPage); | |||
| setVersionInfo(res); | |||
| let model, codeConfig, envVariables; | |||
| if (res.model && typeof res.model === 'object') { | |||
| @@ -156,7 +167,11 @@ function CreateServiceVersion() { | |||
| const [res] = await to(request(params)); | |||
| if (res) { | |||
| message.success('操作成功'); | |||
| navigate(-1); | |||
| if (lastPage === CreateServiceVersionFrom.ServiceInfo) { | |||
| navigate(-1); | |||
| } else { | |||
| navigate(`/modelDeployment/serviceInfo/${serviceInfo?.id}`, { replace: true }); | |||
| } | |||
| } | |||
| }; | |||
| @@ -401,7 +416,8 @@ function CreateServiceVersion() { | |||
| }, | |||
| { | |||
| pattern: /^\/[a-zA-Z0-9._/-]+$/, | |||
| message: '请输入正确的挂载绝对路径', | |||
| message: | |||
| '请输入正确的挂载路径,以 / 开头,只支持字母、数字、点、下划线、横杠、斜杠', | |||
| }, | |||
| ]} | |||
| > | |||
| @@ -12,7 +12,11 @@ import { useCacheState } from '@/hooks/pageCacheState'; | |||
| import { deleteServiceReq, getServiceListReq } from '@/services/modelDeployment'; | |||
| import themes from '@/styles/theme.less'; | |||
| import { to } from '@/utils/promise'; | |||
| import { serviceInfoKey, setSessionStorageItem } from '@/utils/sessionStorage'; | |||
| import { | |||
| serviceInfoKey, | |||
| serviceVersionInfoKey, | |||
| setSessionStorageItem, | |||
| } from '@/utils/sessionStorage'; | |||
| import { modalConfirm } from '@/utils/ui'; | |||
| import { useNavigate } from '@umijs/max'; | |||
| import { | |||
| @@ -28,7 +32,12 @@ import { | |||
| import { type SearchProps } from 'antd/es/input'; | |||
| import classNames from 'classnames'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { ServiceData, ServiceOperationType } from '../types'; | |||
| import { | |||
| CreateServiceVersionFrom, | |||
| ServiceData, | |||
| ServiceOperationType, | |||
| createServiceVersionMessage, | |||
| } from '../types'; | |||
| import styles from './index.less'; | |||
| const allServiceTypeOptions = [{ label: '全部', value: '' }, ...serviceTypeOptions]; | |||
| @@ -49,6 +58,13 @@ function ModelDeployment() { | |||
| }, | |||
| ); | |||
| useEffect(() => { | |||
| window.addEventListener('message', handleMessage); | |||
| return () => { | |||
| window.removeEventListener('message', handleMessage); | |||
| }; | |||
| }, []); | |||
| useEffect(() => { | |||
| getServiceList(); | |||
| }, [pagination, searchText, serviceType]); | |||
| @@ -137,6 +153,35 @@ function ModelDeployment() { | |||
| navigate(`/modelDeployment/serviceInfo/${record.id}`); | |||
| }; | |||
| const handleMessage = (e: MessageEvent) => { | |||
| const { type, payload } = e.data; | |||
| if (type === createServiceVersionMessage) { | |||
| modalConfirm({ | |||
| title: '创建服务成功', | |||
| content: '是否创建服务版本?', | |||
| isDelete: false, | |||
| cancelText: '稍后创建', | |||
| onOk: () => { | |||
| gotoCreateServiceVersion(payload); | |||
| }, | |||
| }); | |||
| } | |||
| }; | |||
| // 去创建服务版本 | |||
| const gotoCreateServiceVersion = (serviceId: number) => { | |||
| setSessionStorageItem( | |||
| serviceVersionInfoKey, | |||
| { | |||
| operationType: ServiceOperationType.Create, | |||
| lastPage: CreateServiceVersionFrom.CreateService, | |||
| }, | |||
| true, | |||
| ); | |||
| navigate(`/modelDeployment/addVersion/${serviceId}`); | |||
| }; | |||
| // 分页切换 | |||
| const handleTableChange: TableProps['onChange'] = (pagination, _filters, _sorter, { action }) => { | |||
| if (action === 'paginate') { | |||
| @@ -38,7 +38,12 @@ import { type SearchProps } from 'antd/es/input'; | |||
| import classNames from 'classnames'; | |||
| import { useEffect, useState } from 'react'; | |||
| import ServiceRunStatusCell from '../components/ModelDeployStatusCell'; | |||
| import { ServiceData, ServiceOperationType, ServiceVersionData } from '../types'; | |||
| import { | |||
| CreateServiceVersionFrom, | |||
| ServiceData, | |||
| ServiceOperationType, | |||
| ServiceVersionData, | |||
| } from '../types'; | |||
| import styles from './index.less'; | |||
| const allServiceStatusOptions = [{ label: '全部', value: '' }, ...serviceStatusOptions]; | |||
| @@ -171,13 +176,14 @@ function ServiceInfo() { | |||
| }); | |||
| }; | |||
| // 创建、更新、重启模型部署 | |||
| // 创建、更新、重启服务版本 | |||
| const createServiceVersion = (type: ServiceOperationType, record?: ServiceVersionData) => { | |||
| setSessionStorageItem( | |||
| serviceVersionInfoKey, | |||
| { | |||
| ...record, | |||
| operationType: type, | |||
| lastPage: CreateServiceVersionFrom.ServiceInfo, | |||
| }, | |||
| true, | |||
| ); | |||
| @@ -1,7 +1,7 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-04-18 18:35:41 | |||
| * @Description: 模型部署状态 | |||
| * @Description: 服务运行状态 | |||
| */ | |||
| import { ServiceRunStatus } from '@/enums'; | |||
| import styles from './index.less'; | |||
| @@ -57,7 +57,7 @@ function ServerLog({ info }: ServerLogProps) { | |||
| getModelDeploymentLog(); | |||
| }, [info, logTime]); | |||
| // 获取模型部署日志 | |||
| // 获取服务日志 | |||
| const getModelDeploymentLog = async () => { | |||
| if (info && logTime && logTime.length === 2) { | |||
| const params = { | |||
| @@ -14,7 +14,7 @@ function UserGuide({ info }: UserGuideProps) { | |||
| getModelDeploymentDocs(); | |||
| }, [info]); | |||
| // 获取模型部署文档 | |||
| // 获取服务文档 | |||
| const getModelDeploymentDocs = async () => { | |||
| if (info) { | |||
| const [res] = await to(getServiceVersionDocsReq(info.id)); | |||
| @@ -57,3 +57,12 @@ export enum ServiceOperationType { | |||
| Update = 'Update', // 更新 | |||
| Restart = 'Restart', // 重启 | |||
| } | |||
| // 操作类型 | |||
| export enum CreateServiceVersionFrom { | |||
| CreateService = 'CreateService', // 来自创建服务 | |||
| ServiceInfo = 'ServiceInfo', // 来自服务详情 | |||
| } | |||
| // 去创建服务版本消息 | |||
| export const createServiceVersionMessage = 'createServiceVersion'; | |||
| @@ -10,8 +10,20 @@ import { history } from '@umijs/max'; | |||
| import { Modal, message, type ModalFuncProps, type UploadFile } from 'antd'; | |||
| import { closeAllModals } from './modal'; | |||
| // 自定义 Confirm 弹框 | |||
| export function modalConfirm({ title, content, onOk, ...rest }: ModalFuncProps) { | |||
| type ModalConfirmProps = ModalFuncProps & { | |||
| isDelete?: boolean; | |||
| }; | |||
| // 自定义删除 Confirm 弹框 | |||
| export function modalConfirm({ | |||
| title, | |||
| content, | |||
| okText = '确认', | |||
| cancelText = '取消', | |||
| isDelete = true, | |||
| onOk, | |||
| ...rest | |||
| }: ModalConfirmProps) { | |||
| Modal.confirm({ | |||
| ...rest, | |||
| width: 600, | |||
| @@ -19,7 +31,11 @@ export function modalConfirm({ title, content, onOk, ...rest }: ModalFuncProps) | |||
| title: ( | |||
| <div> | |||
| <img | |||
| src={require('@/assets/img/delete-icon.png')} | |||
| src={ | |||
| isDelete | |||
| ? require('@/assets/img/delete-icon.png') | |||
| : require('@/assets/img/comfirm-icon.png') | |||
| } | |||
| style={{ width: '120px', marginBottom: '24px' }} | |||
| draggable={false} | |||
| alt="" | |||
| @@ -28,8 +44,8 @@ export function modalConfirm({ title, content, onOk, ...rest }: ModalFuncProps) | |||
| </div> | |||
| ), | |||
| content: content && <div style={{ color: themes.textColor, fontSize: '15px' }}>{content}</div>, | |||
| okText: '确认', | |||
| cancelText: '取消', | |||
| okText: okText, | |||
| cancelText: cancelText, | |||
| onOk: onOk, | |||
| }); | |||
| } | |||