diff --git a/react-ui/src/components/ResourceSelect/index.less b/react-ui/src/components/ResourceSelect/index.less new file mode 100644 index 00000000..ede74eb0 --- /dev/null +++ b/react-ui/src/components/ResourceSelect/index.less @@ -0,0 +1,11 @@ +.resource-select { + position: relative; + display: flex; + align-items: center; + + &__button { + position: absolute; + top: 0; + left: calc(100% + 10px); + } +} diff --git a/react-ui/src/components/ResourceSelect/index.tsx b/react-ui/src/components/ResourceSelect/index.tsx new file mode 100644 index 00000000..95f5c27b --- /dev/null +++ b/react-ui/src/components/ResourceSelect/index.tsx @@ -0,0 +1,104 @@ +import KFIcon from '@/components/KFIcon'; +import ResourceSelectorModal, { + ResourceSelectorResponse, + ResourceSelectorType, + selectorTypeConfig, +} from '@/pages/Pipeline/components/ResourceSelectorModal'; +import { openAntdModal } from '@/utils/modal'; +import { Button } from 'antd'; +import { useState } from 'react'; +import ParameterInput, { type ParameterInputProps } from '../ParameterInput'; +import styles from './index.less'; + +export { requiredValidator, type ParameterInputObject } from '../ParameterInput'; + +type ResourceSelectProps = { + type: ResourceSelectorType; +} & ParameterInputProps; + +// 获取选择数据集、模型后面按钮 icon +const getSelectBtnIcon = (type: ResourceSelectorType) => { + return ; +}; + +function ResourceSelect({ type, value, onChange, ...rest }: ResourceSelectProps) { + const [selectedResource, setSelectedResource] = useState( + undefined, + ); + + const selectResource = () => { + const resource = selectedResource; + const { close } = openAntdModal(ResourceSelectorModal, { + type, + defaultExpandedKeys: resource ? [resource.id] : [], + defaultCheckedKeys: resource ? [`${resource.id}-${resource.version}`] : [], + defaultActiveTab: resource?.activeTab, + onOk: (res) => { + setSelectedResource(res); + if (res) { + const { activeTab, id, name, version, path } = res; + if (type === ResourceSelectorType.Mirror) { + onChange?.({ + value: path, + showValue: path, + fromSelect: true, + activeTab, + expandedKeys: [`${id}`], + checkedKeys: [`${id}-${version}`], + }); + } else { + const jsonObj = { + id, + version, + path, + }; + const jsonObjStr = JSON.stringify(jsonObj); + const showValue = `${name}:${version}`; + onChange?.({ + value: jsonObjStr, + showValue, + fromSelect: true, + activeTab, + expandedKeys: [`${id}`], + checkedKeys: [`${id}-${version}`], + ...jsonObj, + }); + } + } else { + onChange?.({ + value: undefined, + showValue: undefined, + fromSelect: false, + activeTab: undefined, + expandedKeys: [], + checkedKeys: [], + }); + } + close(); + }, + }); + }; + + return ( +
+ setSelectedResource(undefined)} + onClick={selectResource} + > + +
+ ); +} + +export default ResourceSelect; diff --git a/react-ui/src/pages/DevelopmentEnvironment/Create/index.tsx b/react-ui/src/pages/DevelopmentEnvironment/Create/index.tsx index 036fc12c..e59e9698 100644 --- a/react-ui/src/pages/DevelopmentEnvironment/Create/index.tsx +++ b/react-ui/src/pages/DevelopmentEnvironment/Create/index.tsx @@ -1,35 +1,32 @@ /* * @Author: 赵伟 * @Date: 2024-04-16 13:58:08 - * @Description: 创建镜像 + * @Description: 创建开发环境 */ import KFIcon from '@/components/KFIcon'; import KFRadio, { type KFRadioItem } from '@/components/KFRadio'; import PageTitle from '@/components/PageTitle'; -import ParameterInput from '@/components/ParameterInput'; +import ResourceSelect, { + requiredValidator, + type ParameterInputObject, +} from '@/components/ResourceSelect'; import SubAreaTitle from '@/components/SubAreaTitle'; import { useComputingResource } from '@/hooks/resource'; -import ResourceSelectorModal, { - ResourceSelectorResponse, - ResourceSelectorType, - selectorTypeConfig, -} from '@/pages/Pipeline/components/ResourceSelectorModal'; +import { ResourceSelectorType } from '@/pages/Pipeline/components/ResourceSelectorModal'; import { createEditorReq } from '@/services/developmentEnvironment'; -import { openAntdModal } from '@/utils/modal'; import { to } from '@/utils/promise'; import { useNavigate } from '@umijs/max'; import { App, Button, Col, Form, Input, Row, Select } from 'antd'; -import { pick } from 'lodash'; -import { useState } from 'react'; +import { omit, pick } from 'lodash'; import styles from './index.less'; type FormData = { name: string; computing_resource: string; standard: string; - image: string; - model: ResourceSelectorResponse; - dataset: ResourceSelectorResponse; + image: ParameterInputObject; + model: ParameterInputObject; + dataset: ParameterInputObject; }; enum ComputingResourceType { @@ -55,25 +52,20 @@ function EditorCreate() { const [form] = Form.useForm(); const { message } = App.useApp(); const [resourceStandardList, filterResourceStandard] = useComputingResource(); - const [selectedModel, setSelectedModel] = useState( - undefined, - ); // 选择的模型,为了再次打开时恢复原来的选择 - const [selectedDataset, setSelectedDataset] = useState( - undefined, - ); // 选择的数据集,为了再次打开时恢复原来的选择 - const [selectedMirror, setSelectedMirror] = useState( - undefined, - ); // 选择的镜像,为了再次打开时恢复原来的选择 // 创建编辑器 const createEditor = async (formData: FormData) => { - // const { model, dataset } = formData; - // const params = { - // ...formData, - // model: JSON.stringify(omit(model, ['showValue'])), - // dataset: JSON.stringify(dataset, ['showValue']), - // }; - const [res] = await to(createEditorReq(formData)); + // 根据后台要求,修改表单数据 + const image = formData['image']; + const model = formData['model']; + const dataset = formData['dataset']; + const params = { + ...omit(formData, ['image', 'model', 'dataset']), + image: image.value, + model: pick(model, ['id', 'version', 'path', 'showValue']), + dataset: pick(dataset, ['id', 'version', 'path', 'showValue']), + }; + const [res] = await to(createEditorReq(params)); if (res) { message.success('创建成功'); navgite(-1); @@ -89,61 +81,6 @@ function EditorCreate() { const cancel = () => { navgite(-1); }; - // 获取选择数据集、模型后面按钮 icon - const getSelectBtnIcon = (type: ResourceSelectorType) => { - return ; - }; - - // 选择模型、镜像、数据集 - const selectResource = (name: string, type: ResourceSelectorType) => { - let resource: ResourceSelectorResponse | undefined; - switch (type) { - case ResourceSelectorType.Model: - resource = selectedModel; - break; - case ResourceSelectorType.Dataset: - resource = selectedDataset; - break; - default: - resource = selectedMirror; - break; - } - const { close } = openAntdModal(ResourceSelectorModal, { - type, - defaultExpandedKeys: resource ? [resource.id] : [], - defaultCheckedKeys: resource ? [`${resource.id}-${resource.version}`] : [], - defaultActiveTab: resource?.activeTab, - onOk: (res) => { - if (res) { - if (type === ResourceSelectorType.Mirror) { - form.setFieldValue(name, res.path); - setSelectedMirror(res); - } else { - const showValue = `${res.name}:${res.version}`; - form.setFieldValue(name, { - ...pick(res, ['id', 'version', 'path']), - showValue, - }); - if (type === ResourceSelectorType.Model) { - setSelectedModel(res); - } else if (type === ResourceSelectorType.Dataset) { - setSelectedDataset(res); - } - } - } else { - if (type === ResourceSelectorType.Model) { - setSelectedModel(undefined); - } else if (type === ResourceSelectorType.Dataset) { - setSelectedDataset(undefined); - } else if (type === ResourceSelectorType.Mirror) { - setSelectedMirror(undefined); - } - form.setFieldValue(name, ''); - } - close(); - }, - }); - }; return (
@@ -230,64 +167,46 @@ function EditorCreate() { - selectResource('image', ResourceSelectorType.Mirror)} /> - - - - selectResource('model', ResourceSelectorType.Model)} /> - - - @@ -296,29 +215,20 @@ function EditorCreate() { name="dataset" rules={[ { - required: true, + validator: requiredValidator, message: '请选择数据集', }, ]} + required > - selectResource('dataset', ResourceSelectorType.Dataset)} /> - - - diff --git a/react-ui/src/pages/DevelopmentEnvironment/List/index.tsx b/react-ui/src/pages/DevelopmentEnvironment/List/index.tsx index c7b22c6a..38a98e6a 100644 --- a/react-ui/src/pages/DevelopmentEnvironment/List/index.tsx +++ b/react-ui/src/pages/DevelopmentEnvironment/List/index.tsx @@ -1,7 +1,7 @@ /* * @Author: 赵伟 * @Date: 2024-04-16 13:58:08 - * @Description: 开发环境 + * @Description: 开发环境列表 */ import CommonTableCell from '@/components/CommonTableCell'; import DateTableCell from '@/components/DateTableCell'; diff --git a/react-ui/src/pages/ModelDeployment/Create/index.tsx b/react-ui/src/pages/ModelDeployment/Create/index.tsx index d9836195..0fa85fcf 100644 --- a/react-ui/src/pages/ModelDeployment/Create/index.tsx +++ b/react-ui/src/pages/ModelDeployment/Create/index.tsx @@ -5,22 +5,20 @@ */ import KFIcon from '@/components/KFIcon'; import PageTitle from '@/components/PageTitle'; -import ParameterInput, { requiredValidator } from '@/components/ParameterInput'; +import ResourceSelect, { + requiredValidator, + type ParameterInputObject, +} from '@/components/ResourceSelect'; import SubAreaTitle from '@/components/SubAreaTitle'; import { CommonTabKeys } from '@/enums'; import { useComputingResource } from '@/hooks/resource'; -import ResourceSelectorModal, { - ResourceSelectorResponse, - ResourceSelectorType, - selectorTypeConfig, -} from '@/pages/Pipeline/components/ResourceSelectorModal'; +import { ResourceSelectorType } from '@/pages/Pipeline/components/ResourceSelectorModal'; import { createModelDeploymentReq, restartModelDeploymentReq, updateModelDeploymentReq, } from '@/services/modelDeployment'; import { camelCaseToUnderscore, underscoreToCamelCase } from '@/utils'; -import { openAntdModal } from '@/utils/modal'; import { to } from '@/utils/promise'; import { getSessionStorageItem, @@ -39,13 +37,8 @@ import styles from './index.less'; export type FormData = { serviceName: string; // 服务名称 description: string; // 描述 - model: { - id: number; - version: string; - value: string; - showValue: string; - }; // 模型 - image: string; // 镜像 + model: ParameterInputObject; // 模型 + image: ParameterInputObject; // 镜像 resource: string; // 资源规格 replicas: string; // 副本数量 modelPath: string; // 模型路径 @@ -56,16 +49,10 @@ function ModelDeploymentCreate() { const navgite = useNavigate(); const [form] = Form.useForm(); const [resourceStandardList, filterResourceStandard] = useComputingResource(); - const [selectedModel, setSelectedModel] = useState( - undefined, - ); // 选择的模型,为了再次打开时恢复原来的选择 const [operationType, setOperationType] = useState(ModelDeploymentOperationType.Create); const [modelDeploymentInfo, setModelDeploymentInfo] = useState( undefined, ); - const [selectedMirror, setSelectedMirror] = useState( - undefined, - ); // 选择的镜像,为了再次打开时恢复原来的选择 const { message } = App.useApp(); useEffect(() => { @@ -81,78 +68,23 @@ function ModelDeploymentCreate() { }; }, []); - // 获取选择数据集、模型后面按钮 icon - const getSelectBtnIcon = (type: ResourceSelectorType) => { - return ; - }; - - // 选择模型、镜像 - const selectResource = (formItemName: string, type: ResourceSelectorType) => { - let resource: ResourceSelectorResponse | undefined; - switch (type) { - case ResourceSelectorType.Model: - resource = selectedModel; - break; - default: - resource = selectedMirror; - break; - } - const { close } = openAntdModal(ResourceSelectorModal, { - type, - defaultExpandedKeys: resource ? [resource.id] : [], - defaultCheckedKeys: resource ? [`${resource.id}-${resource.version}`] : [], - defaultActiveTab: resource?.activeTab, - onOk: (res) => { - if (res) { - if (type === ResourceSelectorType.Mirror) { - form.setFieldValue(formItemName, res.path); - setSelectedMirror(res); - } else { - const { activeTab, id, name, version, path } = res; - const jsonObj = { - id, - version, - path, - }; - const value = JSON.stringify(jsonObj); - const showValue = `${name}:${version}`; - form.setFieldValue(formItemName, { - value, - showValue, - fromSelect: true, - activeTab, - expandedKeys: [id], - checkedKeys: [`${id}-${version}`], - ...jsonObj, - }); - setSelectedModel(res); - } - } else { - if (type === ResourceSelectorType.Model) { - setSelectedModel(undefined); - } else { - setSelectedMirror(undefined); - } - form.setFieldValue(formItemName, ''); - } - form.validateFields([formItemName]); - close(); - }, - }); - }; - // 创建 const createModelDeployment = async (formData: FormData) => { const envList = formData['env'] ?? []; + const image = formData['image']; + const model = formData['model']; const env = envList.reduce((acc, cur) => { acc[cur.key] = cur.value; return acc; }, {} as Record); + // 根据后台要求,修改表单数据 const object = camelCaseToUnderscore({ - ...omit(formData, ['replicas', 'env']), + ...omit(formData, ['replicas', 'env', 'image', 'model']), replicas: Number(formData.replicas), env, + image: image.value, + model: pick(model, ['id', 'version', 'path', 'showValue']), }); const params = @@ -277,27 +209,15 @@ function ModelDeploymentCreate() { ]} required > - selectResource('model', ResourceSelectorType.Model)} - onChange={() => setSelectedModel(undefined)} /> - - - @@ -312,25 +232,14 @@ function ModelDeploymentCreate() { ]} required > - selectResource('image', ResourceSelectorType.Mirror)} - onChange={() => setSelectedMirror(undefined)} /> - - - diff --git a/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/config.tsx b/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/config.tsx index 66562e13..01e30ab0 100644 --- a/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/config.tsx +++ b/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/config.tsx @@ -37,6 +37,7 @@ export type SelectorTypeInfo = { litReqParamKey: 'available_range' | 'image_type'; // 表示是公开还是私有的参数名称,获取资源列表接口使用 fileReqParamKey: 'models_id' | 'dataset_id'; // 文件请求参数名称,获取文件列表接口使用 tabItems: TabsProps['items']; // tab 列表 + buttontTitle: string; // 按钮 title }; // 获取镜像文件列表,为了兼容数据集和模型 @@ -77,6 +78,7 @@ export const selectorTypeConfig: Record label: '公开模型', }, ], + buttontTitle: '选择模型', }, [ResourceSelectorType.Dataset]: { getList: getDatasetList, @@ -98,6 +100,7 @@ export const selectorTypeConfig: Record label: '公开数据集', }, ], + buttontTitle: '选择数据集', }, [ResourceSelectorType.Mirror]: { getList: getMirrorListReq, @@ -121,5 +124,6 @@ export const selectorTypeConfig: Record label: '公开镜像', }, ], + buttontTitle: '选择镜像', }, }; diff --git a/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/index.tsx b/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/index.tsx index 92b9e0b8..f4caf03a 100644 --- a/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/index.tsx +++ b/react-ui/src/pages/Pipeline/components/ResourceSelectorModal/index.tsx @@ -39,7 +39,7 @@ export interface ResourceSelectorModalProps extends Omit { defaultExpandedKeys?: React.Key[]; defaultCheckedKeys?: React.Key[]; defaultActiveTab?: CommonTabKeys; - onOk?: (params: ResourceSelectorResponse | null) => void; + onOk?: (params: ResourceSelectorResponse | undefined) => void; } type TreeRef = GetRef>; @@ -279,7 +279,7 @@ function ResourceSelectorModal({ }; onOk?.(res); } else { - onOk?.(null); + onOk?.(undefined); } };