Browse Source

chore: merge dev-zw-components

pull/276/head
zhaowei 5 months ago
parent
commit
056a3d67e2
25 changed files with 692 additions and 448 deletions
  1. +1
    -0
      .gitignore
  2. +51
    -11
      react-ui/src/components/ParameterSelect/config.tsx
  3. +51
    -27
      react-ui/src/components/ParameterSelect/index.tsx
  4. +71
    -47
      react-ui/src/hooks/useComputingResource.ts
  5. +2
    -2
      react-ui/src/pages/ActiveLearn/components/ActiveLearnBasic/index.tsx
  6. +2
    -2
      react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx
  7. +7
    -121
      react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx
  8. +7
    -113
      react-ui/src/pages/Dataset/components/AddModelModal/index.tsx
  9. +20
    -2
      react-ui/src/pages/Dataset/components/AddVersionModal/index.tsx
  10. +7
    -0
      react-ui/src/pages/Dataset/components/ResourceInfo/index.less
  11. +85
    -62
      react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx
  12. +5
    -0
      react-ui/src/pages/Dataset/config.tsx
  13. +2
    -2
      react-ui/src/pages/DevelopmentEnvironment/List/index.tsx
  14. +13
    -32
      react-ui/src/pages/Experiment/components/ExportModelModal/index.tsx
  15. +2
    -2
      react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.tsx
  16. +2
    -2
      react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx
  17. +2
    -2
      react-ui/src/pages/ModelDeployment/components/VersionBasicInfo/index.tsx
  18. +2
    -2
      react-ui/src/pages/ModelDeployment/components/VersionCompareModal/index.tsx
  19. +4
    -10
      react-ui/src/pages/Pipeline/Info/utils.tsx
  20. +55
    -8
      react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx
  21. +19
    -1
      react-ui/src/services/dataset/index.js
  22. +87
    -0
      react-ui/src/services/external/index.ts
  23. +140
    -0
      react-ui/src/state/jcdResource.ts
  24. +53
    -0
      react-ui/src/state/systemResource.ts
  25. +2
    -0
      react-ui/src/utils/sessionStorage.ts

+ 1
- 0
.gitignore View File

@@ -65,3 +65,4 @@ mvnw
/react-ui/types/tsconfig.tsbuildinfo
/react-ui/storybook-static
/react-ui/.storybook/scripts
/react-ui/dist.zip

+ 51
- 11
react-ui/src/components/ParameterSelect/config.tsx View File

@@ -1,8 +1,9 @@
import { filterResourceStandard, resourceFieldNames } from '@/hooks/useComputingResource';
import { DatasetData, ModelData } from '@/pages/Dataset/config';
import { ServiceData } from '@/pages/ModelDeployment/types';
import { getDatasetList, getModelList } from '@/services/dataset/index.js';
import { getServiceListReq } from '@/services/modelDeployment';
import type { JCCResourceImage, JCCResourceStandard, JCCResourceType } from '@/state/jcdResource';
import { filterResourceStandard, resourceFieldNames } from '@/state/systemResource';
import { type SelectProps } from 'antd';

export type SelectPropsConfig = {
@@ -10,12 +11,21 @@ export type SelectPropsConfig = {
fieldNames?: SelectProps['fieldNames']; // 下拉数据字段
optionFilterProp?: SelectProps['optionFilterProp']; // 过滤字段名
filterOption?: SelectProps['filterOption']; // 过滤函数
getValue: (value: any) => string | number;
getLabel: (value: any) => string;
isObjectValue: boolean; // value 是对象
getValue?: (value: any) => string | number; // 对象类型时,获取其值
getLabel?: (value: any) => string; // 对象类型时,获取其 label
};

export const paramSelectConfig: Record<string, SelectPropsConfig> = {
export type ParameterSelectDataType =
| 'dataset'
| 'model'
| 'service'
| 'resource'
| 'remote-image'
| 'remote-resource-type'
| 'remote-resource';

export const paramSelectConfig: Record<ParameterSelectDataType, SelectPropsConfig> = {
dataset: {
getOptions: async () => {
const res = await getDatasetList({
@@ -72,14 +82,44 @@ export const paramSelectConfig: Record<string, SelectPropsConfig> = {
resource: {
fieldNames: resourceFieldNames,
filterOption: filterResourceStandard as SelectProps['filterOption'],
// 不会用到
getValue: () => {
return '';
isObjectValue: false,
},
'remote-resource-type': {
optionFilterProp: 'label',
isObjectValue: false,
getValue: (value: JCCResourceType) => {
return value.value;
},
// 不会用的
getLabel: () => {
return '';
getLabel: (value: JCCResourceType) => {
return value.label;
},
isObjectValue: false,
},
'remote-image': {
optionFilterProp: 'label',
getValue: (value: JCCResourceImage) => {
return value.imageID;
},
getLabel: (value: JCCResourceImage) => {
return value.name;
},
isObjectValue: true,
},
'remote-resource': {
optionFilterProp: 'label',
getValue: (value: JCCResourceStandard) => {
return value.id;
},
getLabel: (value: JCCResourceStandard) => {
const cpu = value.baseResourceSpecs.find((v) => v.type === 'CPU');
const ram = value.baseResourceSpecs.find((v) => v.type === 'MEMORY' && v.name === 'RAM');
const vram = value.baseResourceSpecs.find((v) => v.type === 'MEMORY' && v.name === 'VRAM');
const cpuText = cpu ? `CPU:${cpu.availableValue}, ` : '';
const ramText = ram ? `内存: ${ram.availableValue}${ram.availableUnit?.toUpperCase()}` : '';
const vramText = vram
? `(显存${vram.availableValue}${vram.availableUnit?.toUpperCase()})`
: '';
return `${value.type}: ${value.availableCount}*${value.name}${vramText}, ${cpuText}${ramText}`;
},
isObjectValue: true,
},
};

+ 51
- 27
react-ui/src/components/ParameterSelect/index.tsx View File

@@ -4,19 +4,25 @@
* @Description: 参数下拉选择组件,支持资源规格、数据集、模型、服务
*/

import { useComputingResource } from '@/hooks/useComputingResource';
import jccResourceState from '@/state/jcdResource';
import systemResourceState, { getSystemResources } from '@/state/systemResource';
import { to } from '@/utils/promise';
import { useSnapshot } from '@umijs/max';
import { Select, type SelectProps } from 'antd';
import { useEffect, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import FormInfo from '../FormInfo';
import { paramSelectConfig } from './config';
import { paramSelectConfig, type ParameterSelectDataType } from './config';

export { type ParameterSelectDataType };

export type ParameterSelectObject = {
value: any;
[key: string]: any;
};

export type ParameterSelectDataType = 'dataset' | 'model' | 'service' | 'resource';
type SelectOptions = SelectProps['options'];

const identityFunc = (value: any) => value;

export interface ParameterSelectProps extends SelectProps {
/** 类型 */
@@ -25,8 +31,6 @@ export interface ParameterSelectProps extends SelectProps {
display?: boolean;
/** 值,支持对象,对象必须包含 value */
value?: string | ParameterSelectObject;
/** 用于流水线, 流水线资源规格要求 id 为字符串 */
isPipeline?: boolean;
/** 修改后回调 */
onChange?: (value: string | ParameterSelectObject) => void;
}
@@ -36,15 +40,14 @@ function ParameterSelect({
dataType,
display = false,
value,
// isPipeline = false,
onChange,
...rest
}: ParameterSelectProps) {
const [options, setOptions] = useState<SelectProps['options']>([]);
const [options, setOptions] = useState<SelectOptions>([]);
const propsConfig = paramSelectConfig[dataType];
const {
getLabel,
getValue,
getLabel = identityFunc,
getValue = identityFunc,
getOptions,
filterOption,
fieldNames,
@@ -55,29 +58,45 @@ function ParameterSelect({
// 数据集、模型、服务,对象转换成 json 字符串
const valueText =
typeof selectValue === 'object' && selectValue !== null ? getValue(selectValue) : selectValue;
const [resourceStandardList] = useComputingResource();
// const computingResource = isPipeline
// ? resourceStandardList.map((v) => ({
// ...v,
// id: String(v.id),
// }))
// : resourceStandardList;
const jccResourceSnap = useSnapshot(jccResourceState);
const { getResourceTypes } = jccResourceSnap;
const systemResourceSnap = useSnapshot(systemResourceState);

const objectOptions = useMemo(() => {
return options?.map((v) => ({
label: getLabel(v),
value: getValue(v),
}));
}, [getLabel, getValue, options]);
return dataType === 'remote-resource-type'
? jccResourceSnap.types
: dataType === 'remote-image'
? jccResourceSnap.images
: dataType === 'remote-resource'
? jccResourceSnap.resources
: options;
}, [dataType, options, jccResourceSnap.types, jccResourceSnap.images, jccResourceSnap.resources]);

// 将对象类型转换为 Select Options
const converObjectToOptions = useCallback(
(v: any) => {
return {
label: getLabel(v),
value: getValue(v),
};
},
[getLabel, getValue],
);

// 数据集、模型、服务获取数据后,进行转换
const objectSelectOptions = useMemo(() => {
return objectOptions?.map(converObjectToOptions);
}, [converObjectToOptions, objectOptions]);

// 快速得到选中的对象
const valueMap = useMemo(() => {
const map = new Map<string | number, any>();
options?.forEach((v) => {
objectOptions?.forEach((v) => {
map.set(getValue(v), v);
});

return map;
}, [options, getValue]);
}, [objectOptions, getValue]);

useEffect(() => {
// 获取下拉数据
@@ -87,13 +106,18 @@ function ParameterSelect({
if (res) {
setOptions(res);
}
} else if (dataType === 'remote-resource-type') {
getResourceTypes();
} else if (dataType === 'resource') {
getSystemResources();
}
};

getSelectOptions();
}, [getOptions]);
}, [getOptions, dataType, getResourceTypes]);

const selectOptions = dataType === 'resource' ? resourceStandardList : objectOptions;
const selectOptions = (
dataType === 'resource' ? systemResourceSnap.resources : objectSelectOptions
) as SelectOptions;

const handleChange = (text: string) => {
// 数据集、模型、服务,转换成对象


+ 71
- 47
react-ui/src/hooks/useComputingResource.ts View File

@@ -4,66 +4,90 @@
* @Description: 资源规格 hook
*/

import { getComputingResourceReq } from '@/services/pipeline';
import { ComputingResource } from '@/types';
import { to } from '@/utils/promise';
import { type SelectProps } from 'antd';
import { useCallback, useEffect, useState } from 'react';
// import { getComputingResourceReq } from '@/services/pipeline';
// import { ComputingResource } from '@/types';
// import { to } from '@/utils/promise';
// import { type SelectProps } from 'antd';
// import { useCallback, useEffect, useState } from 'react';

const computingResource: ComputingResource[] = [];
// const computingResource: ComputingResource[] = [];

/** 过滤资源规格 */
export const filterResourceStandard: SelectProps<string, ComputingResource>['filterOption'] = (
input: string,
option?: ComputingResource,
) => {
return (
option?.computing_resource?.toLocaleLowerCase()?.includes(input.toLocaleLowerCase()) ?? false
);
};
// /** 过滤资源规格 */
// export const filterResourceStandard: SelectProps<string, ComputingResource>['filterOption'] = (
// input: string,
// option?: ComputingResource,
// ) => {
// return (
// option?.computing_resource?.toLocaleLowerCase()?.includes(input.toLocaleLowerCase()) ?? false
// );
// };

/** 资源规格字段 */
export const resourceFieldNames = {
label: 'description',
value: 'id',
};
// /** 资源规格字段 */
// export const resourceFieldNames = {
// label: 'description',
// value: 'id',
// };

/** 获取资源规格 */
export function useComputingResource() {
const [resourceStandardList, setResourceStandardList] = useState<ComputingResource[]>([]);
// /** 获取资源规格 */
// export function useComputingResource() {
// const [resourceStandardList, setResourceStandardList] = useState<ComputingResource[]>([]);

useEffect(() => {
// 获取资源规格列表数据
const getComputingResource = async () => {
const params = {
page: 0,
size: 1000,
resource_type: '',
};
const [res] = await to(getComputingResourceReq(params));
if (res && res.data && Array.isArray(res.data.content)) {
setResourceStandardList(res.data.content);
computingResource.splice(0, computingResource.length, ...res.data.content);
}
};
// useEffect(() => {
// // 获取资源规格列表数据
// const getComputingResource = async () => {
// const params = {
// page: 0,
// size: 1000,
// resource_type: '',
// };
// const [res] = await to(getComputingResourceReq(params));
// if (res && res.data && Array.isArray(res.data.content)) {
// setResourceStandardList(res.data.content);
// computingResource.splice(0, computingResource.length, ...res.data.content);
// }
// };

// if (computingResource.length > 0) {
// setResourceStandardList(computingResource);
// } else {
// getComputingResource();
// }
// }, []);

if (computingResource.length > 0) {
setResourceStandardList(computingResource);
} else {
getComputingResource();
}
// // 根据 standard 获取 description
// const getDescription = useCallback(
// (id?: string | number) => {
// if (!id) {
// return undefined;
// }
// return resourceStandardList.find((item) => Number(item.id) === Number(id))?.description;
// },
// [resourceStandardList],
// );

// return [resourceStandardList, getDescription] as const;
// }

import state, { getSystemResources } from '@/state/systemResource';
import { useSnapshot } from '@umijs/max';
import { useCallback, useEffect } from 'react';

export const useSystemResource = () => {
useEffect(() => {
getSystemResources();
}, []);

// 根据 standard 获取 description
const snap = useSnapshot(state);
/* 根据 standard 获取 description */
const getDescription = useCallback(
(id?: string | number) => {
if (!id) {
return undefined;
}
return resourceStandardList.find((item) => Number(item.id) === Number(id))?.description;
return snap.resources.find((item) => Number(item.id) === Number(id))?.description;
},
[resourceStandardList],
[snap.resources],
);

return [resourceStandardList, getDescription] as const;
}
return getDescription;
};

+ 2
- 2
react-ui/src/pages/ActiveLearn/components/ActiveLearnBasic/index.tsx View File

@@ -1,6 +1,6 @@
import ConfigInfo, { type BasicInfoData } from '@/components/ConfigInfo';
import { AutoMLTaskType, autoMLTaskTypeOptions, ExperimentStatus } from '@/enums';
import { useComputingResource } from '@/hooks/useComputingResource';
import { useSystemResource } from '@/hooks/useComputingResource';
import {
classifierAlgorithms,
FrameworkType,
@@ -39,7 +39,7 @@ function BasicInfo({
instanceStatus,
isInstance = false,
}: BasicInfoProps) {
const getResourceDescription = useComputingResource()[1];
const getResourceDescription = useSystemResource();
const basicDatas: BasicInfoData[] = useMemo(() => {
if (!info) {
return [];


+ 2
- 2
react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx View File

@@ -6,7 +6,7 @@ import {
autoMLEnsembleClassOptions,
autoMLTaskTypeOptions,
} from '@/enums';
import { useComputingResource } from '@/hooks/useComputingResource';
import { useSystemResource } from '@/hooks/useComputingResource';
import {
classificationAlgorithms,
featureAlgorithms,
@@ -76,7 +76,7 @@ function AutoMLBasic({
instanceStatus,
isInstance = false,
}: AutoMLBasicProps) {
const getResourceDescription = useComputingResource()[1];
const getResourceDescription = useSystemResource();
const basicDatas: BasicInfoData[] = useMemo(() => {
if (!info) {
return [];


+ 7
- 121
react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx View File

@@ -1,30 +1,8 @@
import { getAccessToken } from '@/access';
import KFIcon from '@/components/KFIcon';
import KFModal from '@/components/KFModal';
import { CategoryData, DataSource, ResourceType, resourceConfig } from '@/pages/Dataset/config';
import { CategoryData, DataSource } from '@/pages/Dataset/config';
import { addDataset } from '@/services/dataset/index.js';
import { to } from '@/utils/promise';
import {
getFileListFromEvent,
limitUploadFileType,
removeUploadedFile,
validateUploadFiles,
} from '@/utils/ui';
import {
Button,
Form,
Input,
Radio,
Select,
Upload,
UploadFile,
message,
type ModalProps,
type UploadProps,
} from 'antd';
import { omit } from 'lodash';
import { useState } from 'react';
import styles from './index.less';
import { Form, Input, Radio, Select, message, type ModalProps } from 'antd';

interface AddDatasetModalProps extends Omit<ModalProps, 'onOk'> {
typeList: CategoryData[];
@@ -33,20 +11,6 @@ interface AddDatasetModalProps extends Omit<ModalProps, 'onOk'> {
}

function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalProps) {
const [uuid] = useState(Date.now());

// 上传组件参数
const uploadProps: UploadProps = {
action: resourceConfig[ResourceType.Dataset].uploadAction,
headers: {
Authorization: getAccessToken() || '',
},
defaultFileList: [],
accept: '.zip,.tgz',
beforeUpload: limitUploadFileType('zip,tgz'),
onRemove: removeUploadedFile,
};

// 上传请求
const createDataset = async (params: any) => {
const [res] = await to(addDataset(params));
@@ -58,22 +22,11 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr

// 提交
const onFinish = (formData: any) => {
const fileList: UploadFile[] = formData['fileList'] ?? [];
if (validateUploadFiles(fileList)) {
const params = {
...omit(formData, ['fileList']),
dataset_source: DataSource.Create,
dataset_version_vos: fileList.map((item) => {
const data = item.response?.data?.[0] ?? {};
return {
file_name: data.fileName,
file_size: data.fileSize,
url: data.url,
};
}),
};
createDataset(params);
}
const params = {
...formData,
dataset_source: DataSource.Create,
};
createDataset(params);
};

return (
@@ -108,32 +61,6 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
>
<Input placeholder="请输入数据名称" showCount allowClear maxLength={40} />
</Form.Item>
<Form.Item
label="数据集版本"
name="version"
rules={[
{
required: true,
message: '请输入数据集版本',
},
{
pattern: /^[a-zA-Z0-9._-]+$/,
message: '数据集版本只支持字母、数字、点(.)、下划线(_)、中横线(-)',
},
{
validator: (_rule, value) => {
if (value === 'master') {
return Promise.reject(`数据集版本不能为 master`);
} else if (value === 'origin') {
return Promise.reject(`数据集版本不能为 origin`);
}
return Promise.resolve();
},
},
]}
>
<Input placeholder="请输入数据集版本" showCount allowClear maxLength={64} />
</Form.Item>
<Form.Item label="数据集分类" name="data_type">
<Select
allowClear
@@ -172,24 +99,6 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
allowClear
/>
</Form.Item>
<Form.Item
label="版本描述"
name="version_desc"
rules={[
{
required: true,
message: '请输入版本描述',
},
]}
>
<Input.TextArea
placeholder="请输入版本描述"
autoSize={{ minRows: 2, maxRows: 6 }}
maxLength={200}
showCount
allowClear
/>
</Form.Item>
<Form.Item
label="可见性"
name="is_public"
@@ -200,29 +109,6 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
<Radio value={true}>公开</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
label="数据集文件"
name="fileList"
valuePropName="fileList"
getValueFromEvent={getFileListFromEvent}
rules={[
{
required: true,
message: '请上传数据集文件',
},
]}
>
<Upload {...uploadProps} data={{ uuid: uuid }}>
<Button
className={styles['upload-button']}
type="default"
icon={<KFIcon type="icon-shangchuan" />}
>
上传文件
</Button>
<div className={styles['upload-tip']}>只允许上传 .zip 和 .tgz 格式文件</div>
</Upload>
</Form.Item>
</Form>
</KFModal>
);


+ 7
- 113
react-ui/src/pages/Dataset/components/AddModelModal/index.tsx View File

@@ -1,25 +1,8 @@
import { getAccessToken } from '@/access';
import KFIcon from '@/components/KFIcon';
import KFModal from '@/components/KFModal';
import { CategoryData, DataSource, ResourceType, resourceConfig } from '@/pages/Dataset/config';
import { CategoryData, DataSource } from '@/pages/Dataset/config';
import { addModel } from '@/services/dataset/index.js';
import { to } from '@/utils/promise';
import { getFileListFromEvent, removeUploadedFile, validateUploadFiles } from '@/utils/ui';
import {
Button,
Form,
Input,
Radio,
Select,
Upload,
UploadFile,
message,
type ModalProps,
type UploadProps,
} from 'antd';
import { omit } from 'lodash';
import { useState } from 'react';
import styles from '../AddDatasetModal/index.less';
import { Form, Input, Radio, Select, message, type ModalProps } from 'antd';

interface AddModelModalProps extends Omit<ModalProps, 'onOk'> {
typeList: CategoryData[];
@@ -28,18 +11,6 @@ interface AddModelModalProps extends Omit<ModalProps, 'onOk'> {
}

function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps) {
const [uuid] = useState(Date.now());

// 上传组件参数
const uploadProps: UploadProps = {
action: resourceConfig[ResourceType.Model].uploadAction,
headers: {
Authorization: getAccessToken() || '',
},
defaultFileList: [],
onRemove: removeUploadedFile,
};

// 上传请求
const createModel = async (params: any) => {
const [res] = await to(addModel(params));
@@ -51,22 +22,11 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps)

// 提交
const onFinish = (formData: any) => {
const fileList: UploadFile[] = formData['fileList'] ?? [];
if (validateUploadFiles(fileList)) {
const params = {
...omit(formData, ['fileList']),
model_source: DataSource.Create,
model_version_vos: fileList.map((item) => {
const data = item.response?.data?.[0] ?? {};
return {
file_name: data.fileName,
file_size: data.fileSize,
url: data.url,
};
}),
};
createModel(params);
}
const params = {
...formData,
model_source: DataSource.Create,
};
createModel(params);
};

return (
@@ -99,32 +59,6 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps)
>
<Input placeholder="请输入模型名称" showCount allowClear maxLength={40} />
</Form.Item>
<Form.Item
label="模型版本"
name="version"
rules={[
{
required: true,
message: '请输入模型版本',
},
{
pattern: /^[a-zA-Z0-9._-]+$/,
message: '模型版本只支持字母、数字、点(.)、下划线(_)、中横线(-)',
},
{
validator: (_rule, value) => {
if (value === 'master') {
return Promise.reject(`模型版本不能为 master`);
} else if (value === 'origin') {
return Promise.reject(`模型版本不能为 origin`);
}
return Promise.resolve();
},
},
]}
>
<Input placeholder="请输入模型版本" showCount allowClear maxLength={64} />
</Form.Item>
<Form.Item label="模型框架" name="model_type">
<Select
allowClear
@@ -163,24 +97,6 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps)
allowClear
/>
</Form.Item>
<Form.Item
label="版本描述"
name="version_desc"
rules={[
{
required: true,
message: '请输入版本描述',
},
]}
>
<Input.TextArea
placeholder="请输入版本描述"
autoSize={{ minRows: 2, maxRows: 6 }}
maxLength={200}
showCount
allowClear
/>
</Form.Item>
<Form.Item
label="可见性"
name="is_public"
@@ -191,28 +107,6 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps)
<Radio value={true}>公开</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
label="模型文件"
name="fileList"
valuePropName="fileList"
getValueFromEvent={getFileListFromEvent}
rules={[
{
required: true,
message: '请上传模型文件',
},
]}
>
<Upload {...uploadProps} data={{ uuid: uuid }}>
<Button
className={styles['upload-button']}
type="default"
icon={<KFIcon type="icon-shangchuan" />}
>
上传文件
</Button>
</Upload>
</Form.Item>
</Form>
</KFModal>
);


+ 20
- 2
react-ui/src/pages/Dataset/components/AddVersionModal/index.tsx View File

@@ -15,7 +15,7 @@ import {
type UploadProps,
} from 'antd';
import { omit } from 'lodash';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import styles from '../AddDatasetModal/index.less';

interface AddVersionModalProps extends Omit<ModalProps, 'onOk'> {
@@ -40,6 +40,23 @@ function AddVersionModal({
}: AddVersionModalProps) {
const [uuid] = useState(Date.now());
const config = resourceConfig[resourceType];
const [form] = Form.useForm();

useEffect(() => {
const getNextVersion = async () => {
const request = config.getNextVersion;
const params = {
identifier,
owner,
};
const [res] = await to(request(params));
if (res && res.data) {
const nextVersion = res.data;
form.setFieldValue('version', nextVersion);
}
};
getNextVersion();
}, [identifier, owner, config, form]);

// 上传组件参数
const uploadProps: UploadProps = {
@@ -109,6 +126,7 @@ function AddVersionModal({
}}
onFinish={onFinish}
autoComplete="off"
form={form}
>
<Form.Item
label={`${name}名称`}
@@ -146,7 +164,7 @@ function AddVersionModal({
},
]}
>
<Input placeholder={`请输入${name}版本`} maxLength={64} showCount allowClear />
<Input placeholder={`请输入${name}版本`} maxLength={64} showCount allowClear disabled />
</Form.Item>
<Form.Item
label="版本描述"


+ 7
- 0
react-ui/src/pages/Dataset/components/ResourceInfo/index.less View File

@@ -28,8 +28,15 @@
border-radius: 4px;
}

&__desc {
margin-bottom: 0 !important;
color: @text-color;
font-size: @font-size;
}

&__praise {
display: flex;
flex: none;
align-items: center;
justify-content: center;
width: 70px;


+ 85
- 62
react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx View File

@@ -4,6 +4,7 @@
* @Description: 数据集、模型详情
*/

import KFEmpty, { EmptyType } from '@/components/KFEmpty';
import KFIcon from '@/components/KFIcon';
import {
ResourceData,
@@ -19,7 +20,7 @@ import { openAntdModal } from '@/utils/modal';
import { to } from '@/utils/promise';
import { modalConfirm } from '@/utils/ui';
import { useParams, useSearchParams } from '@umijs/max';
import { App, Button, Flex, Select, Tabs } from 'antd';
import { App, Button, Flex, Select, Tabs, Typography } from 'antd';
import classNames from 'classnames';
import { useCallback, useEffect, useState } from 'react';
import AddVersionModal from '../AddVersionModal';
@@ -62,21 +63,24 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
const { message } = App.useApp();

// 获取详情
const getResourceDetail = useCallback(async () => {
const params = {
id: resourceId,
owner,
name,
identifier,
version,
is_public,
};
const request = config.getInfo;
const [res] = await to(request(params));
if (res && res.data) {
setInfo(res.data);
}
}, [config, resourceId, owner, name, identifier, version, is_public]);
const getResourceDetail = useCallback(
async (version: string | undefined) => {
const params = {
id: resourceId,
owner,
name,
identifier,
version,
is_public,
};
const request = config.getInfo;
const [res] = await to(request(params));
if (res && res.data) {
setInfo(res.data);
}
},
[config, resourceId, owner, name, identifier, is_public],
);

// 获取版本列表
const getVersionList = useCallback(
@@ -101,14 +105,15 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
}
} else {
setVersion(undefined);
getResourceDetail(undefined);
}
},
[config, owner, identifier, versionParam],
[config, owner, identifier, versionParam, getResourceDetail],
);

useEffect(() => {
if (version) {
getResourceDetail();
getResourceDetail(version);
}
}, [version, getResourceDetail]);

@@ -117,7 +122,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
}, [getVersionList]);

// 新建版本
const showModal = () => {
const showAddVersionModal = () => {
const { close } = openAntdModal(AddVersionModal, {
resourceType: resourceType,
resourceId: resourceId,
@@ -291,52 +296,70 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
<span>{info.praises_count}</span>
</div>
</Flex>
<Flex align="center">
<span style={{ marginRight: '10px' }}>版本号:</span>
<Select
placeholder="请选择版本号"
style={{ width: '160px', marginRight: '20px' }}
value={version}
onChange={handleVersionChange}
fieldNames={{ label: 'name', value: 'name' }}
options={versionList}
/>
<Button type="default" onClick={showModal} icon={<KFIcon type="icon-xinjian2" />}>
创建新版本
</Button>
<Button
type="default"
style={{ marginLeft: '20px' }}
icon={<KFIcon type="icon-bianji" />}
onClick={showEditVersionModal}
>
版本编辑
</Button>
<Button
type="default"
style={{ marginLeft: '20px' }}
icon={<KFIcon type="icon-banbenduibi" />}
onClick={showVersionSelector}
>
版本对比
</Button>
<Button
type="default"
style={{ marginLeft: '20px' }}
onClick={handleDelete}
icon={<KFIcon type="icon-shanchu" />}
disabled={!version}
danger
{version ? (
<Flex align="center">
<span style={{ marginRight: '10px' }}>版本号:</span>
<Select
placeholder="请选择版本号"
style={{ width: '160px', marginRight: '20px' }}
value={version}
onChange={handleVersionChange}
fieldNames={{ label: 'name', value: 'name' }}
options={versionList}
/>
<Button
type="default"
onClick={showAddVersionModal}
icon={<KFIcon type="icon-xinjian2" />}
>
创建新版本
</Button>
<Button
type="default"
style={{ marginLeft: '20px' }}
icon={<KFIcon type="icon-banbenduibi" />}
onClick={showVersionSelector}
>
版本对比
</Button>
<Button
type="default"
style={{ marginLeft: '20px' }}
onClick={handleDelete}
icon={<KFIcon type="icon-shanchu" />}
danger
>
删除版本
</Button>
</Flex>
) : (
<Typography.Paragraph
className={styles['resource-info__top__desc']}
ellipsis={{ tooltip: info.description }}
>
删除版本
</Button>
</Flex>
{info.description ?? '暂无描述'}
</Typography.Paragraph>
)}
</div>
<div className={styles['resource-info__bottom']}>
<Tabs activeKey={activeTab} items={items} onChange={(key) => setActiveTab(key)}></Tabs>
<div className={styles['resource-info__bottom__legend']}>
{activeTab === ResourceInfoTabKeys.Evolution && <GraphLegend />}
</div>
{version ? (
<>
<Tabs activeKey={activeTab} items={items} onChange={(key) => setActiveTab(key)}></Tabs>
<div className={styles['resource-info__bottom__legend']}>
{activeTab === ResourceInfoTabKeys.Evolution && <GraphLegend />}
</div>
</>
) : (
<KFEmpty
style={{ height: '100%' }}
type={EmptyType.NoData}
title="暂无版本"
content={`请创建${config.name}版本`}
hasFooter={true}
buttonTitle="创建版本"
onButtonClick={showAddVersionModal}
/>
)}
</div>
</div>
);


+ 5
- 0
react-ui/src/pages/Dataset/config.tsx View File

@@ -13,9 +13,11 @@ import {
editModelVersion,
getDatasetInfo,
getDatasetList,
getDatasetNextVersionReq,
getDatasetVersionList,
getModelInfo,
getModelList,
getModelNextVersionReq,
getModelVersionList,
} from '@/services/dataset/index.js';
import { limitUploadFileType } from '@/utils/ui';
@@ -42,6 +44,7 @@ type ResourceTypeInfo = {
deleteVersion: (params: any) => Promise<any>; // 删除版本
getInfo: (params: any) => Promise<any>; // 获取详情
compareVersion: (params: any) => Promise<any>; // 版本对比
getNextVersion: (params: any) => Promise<any>; // 获取下一个版本
name: string; // 名称
typeParamKey: 'data_type' | 'model_type'; // 类型参数名称,获取资源列表接口使用
tagParamKey: 'data_tag' | 'model_tag'; // 标签参数名称,获取资源列表接口使用
@@ -72,6 +75,7 @@ export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = {
deleteVersion: deleteDatasetVersion,
getInfo: getDatasetInfo,
compareVersion: compareDatasetVersion,
getNextVersion: getDatasetNextVersionReq,
name: '数据集',
typeParamKey: 'data_type',
tagParamKey: 'data_tag',
@@ -111,6 +115,7 @@ export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = {
deleteVersion: deleteModelVersion,
getInfo: getModelInfo,
compareVersion: compareModelVersion,
getNextVersion: getModelNextVersionReq,
name: '模型',
typeParamKey: 'model_type',
tagParamKey: 'model_tag',


+ 2
- 2
react-ui/src/pages/DevelopmentEnvironment/List/index.tsx View File

@@ -8,7 +8,7 @@ import { CodeConfigData } from '@/components/CodeSelectorModal';
import KFIcon from '@/components/KFIcon';
import { DevEditorStatus } from '@/enums';
import { useCacheState } from '@/hooks/useCacheState';
import { useComputingResource } from '@/hooks/useComputingResource';
import { useSystemResource } from '@/hooks/useComputingResource';
import { DatasetData, ModelData } from '@/pages/Dataset/config';
import {
deleteEditorReq,
@@ -66,7 +66,7 @@ function EditorList() {
pageSize: 10,
},
);
const getResourceDescription = useComputingResource()[1];
const getResourceDescription = useSystemResource();

// 获取编辑器列表
const getEditorList = useCallback(async () => {


+ 13
- 32
react-ui/src/pages/Experiment/components/ExportModelModal/index.tsx View File

@@ -3,12 +3,10 @@ import KFModal from '@/components/KFModal';
import {
DataSource,
ResourceType,
ResourceVersionData,
resourceConfig,
type ResourceData,
} from '@/pages/Dataset/config';
import { to } from '@/utils/promise';
import { InfoCircleOutlined } from '@ant-design/icons';
import { Form, Input, ModalProps, Select } from 'antd';
import { pick } from 'lodash';
import { useEffect, useState } from 'react';
@@ -44,7 +42,6 @@ function ExportModelModal({
}: ExportModelModalProps) {
const [form] = Form.useForm();
const [resources, setResources] = useState<ResourceData[]>([]);
const [versions, setVersions] = useState<ResourceVersionData[]>([]);
const config = resourceConfig[resourceType];

const layout = {
@@ -77,35 +74,24 @@ function ExportModelModal({
return undefined;
};

// 版本 tooltip
const getTooltip = () => {
const id = form.getFieldValue('id');
const resource = getSelectedResource(id);
const name = resource?.name ?? '';
const versionNames = versions.map((item: ResourceVersionData) => item.name).join('、');
const tooltip =
versions.length > 0 ? `${name}有以下版本:\n${versionNames}\n注意不能重复` : undefined;
return tooltip;
};

// 处理数据集、模型选择变化
const handleResourceChange = (id: number | undefined) => {
if (id) {
getRecourceVersions(id);
getRecourceNextVersion(id);
} else {
setVersions([]);
form.setFieldValue('version', '');
}
};

// 获取数据集、模型版本列表
const getRecourceVersions = async (id: number) => {
// 获取数据集、模型下一个版本
const getRecourceNextVersion = async (id: number) => {
const resource = getSelectedResource(id);
if (!resource) {
return;
}
const [res] = await to(config.getVersions(pick(resource, ['identifier', 'owner'])));
const [res] = await to(config.getNextVersion(pick(resource, ['identifier', 'owner'])));
if (res && res.data) {
setVersions(res.data);
form.setFieldValue('version', res.data);
}
};

@@ -184,15 +170,6 @@ function ExportModelModal({
<Form.Item
label={`${config.name}版本`}
name="version"
tooltip={
getTooltip()
? {
overlayClassName: styles['export-model-modal__tooltip'],
title: getTooltip(),
icon: <InfoCircleOutlined />,
}
: undefined
}
rules={[
{ required: true, message: `请输入${config.name}版本` },
{
@@ -205,8 +182,6 @@ function ExportModelModal({
return Promise.reject(`${config.name}版本不能为 master`);
} else if (value === 'origin') {
return Promise.reject(`${config.name}版本不能为 origin`);
} else if (value && versions.map((item) => item.name).includes(value)) {
return Promise.reject(`${config.name}版本已存在`);
} else {
return Promise.resolve();
}
@@ -214,7 +189,13 @@ function ExportModelModal({
},
]}
>
<Input placeholder={`请输入${config.name}版本`} maxLength={64} showCount allowClear />
<Input
placeholder={`请输入${config.name}版本`}
maxLength={64}
showCount
allowClear
disabled
/>
</Form.Item>
<Form.Item
label="版本描述"


+ 2
- 2
react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.tsx View File

@@ -1,6 +1,6 @@
import ConfigInfo, { type BasicInfoData } from '@/components/ConfigInfo';
import { ExperimentStatus, hyperParameterOptimizedMode } from '@/enums';
import { useComputingResource } from '@/hooks/useComputingResource';
import { useSystemResource } from '@/hooks/useComputingResource';
import ExperimentRunBasic from '@/pages/AutoML/components/ExperimentRunBasic';
import {
schedulerAlgorithms,
@@ -41,7 +41,7 @@ function HyperParameterBasic({
instanceStatus,
isInstance = false,
}: HyperParameterBasicProps) {
const getResourceDescription = useComputingResource()[1];
const getResourceDescription = useSystemResource();

const basicDatas: BasicInfoData[] = useMemo(() => {
if (!info) {


+ 2
- 2
react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx View File

@@ -9,7 +9,7 @@ import PageTitle from '@/components/PageTitle';
import SubAreaTitle from '@/components/SubAreaTitle';
import { ServiceRunStatus, serviceStatusOptions } from '@/enums';
import { useCacheState } from '@/hooks/useCacheState';
import { useComputingResource } from '@/hooks/useComputingResource';
import { useSystemResource } from '@/hooks/useComputingResource';
import { ModelData } from '@/pages/Dataset/config';
import {
deleteServiceVersionReq,
@@ -89,7 +89,7 @@ function ServiceInfo() {
format: formatDate,
},
];
const getResourceDescription = useComputingResource()[1];
const getResourceDescription = useSystemResource();

// 获取服务详情
const getServiceInfo = useCallback(async () => {


+ 2
- 2
react-ui/src/pages/ModelDeployment/components/VersionBasicInfo/index.tsx View File

@@ -1,6 +1,6 @@
import BasicInfo, { type BasicInfoData } from '@/components/BasicInfo';
import { ServiceRunStatus } from '@/enums';
import { useComputingResource } from '@/hooks/useComputingResource';
import { useSystemResource } from '@/hooks/useComputingResource';
import { ServiceVersionData } from '@/pages/ModelDeployment/types';
import { formatDate } from '@/utils/date';
import { formatMirror, formatModel } from '@/utils/format';
@@ -36,7 +36,7 @@ const formatEnvText = (env?: Record<string, string>) => {
};

function VersionBasicInfo({ info }: BasicInfoProps) {
const getResourceDescription = useComputingResource()[1];
const getResourceDescription = useSystemResource();

const datas: BasicInfoData[] = [
{


+ 2
- 2
react-ui/src/pages/ModelDeployment/components/VersionCompareModal/index.tsx View File

@@ -1,6 +1,6 @@
import KFModal from '@/components/KFModal';
import { ServiceRunStatus } from '@/enums';
import { useComputingResource } from '@/hooks/useComputingResource';
import { useSystemResource } from '@/hooks/useComputingResource';
import { type ServiceVersionData } from '@/pages/ModelDeployment/types';
import { getServiceVersionCompareReq } from '@/services/modelDeployment';
import { isEmpty } from '@/utils';
@@ -42,7 +42,7 @@ const formatEnvText = (env: Record<string, string>) => {

function VersionCompareModal({ version1, version2, ...rest }: VersionCompareModalProps) {
const [compareData, setCompareData] = useState<CompareData | undefined>(undefined);
const getResourceDescription = useComputingResource()[1];
const getResourceDescription = useSystemResource();

const fields: FiledType[] = useMemo(
() => [


+ 4
- 10
react-ui/src/pages/Pipeline/Info/utils.tsx View File

@@ -70,15 +70,6 @@ export function createMenuItems(
}
}

export function getInParameterComponent(
parameter: PipelineNodeModelParameter,
): React.ReactNode | null {
if (parameter.value) {
}

return null;
}

// 判断是否允许输入
export function canInput(parameter: PipelineNodeModelParameter) {
const { type, item_type } = parameter;
@@ -87,6 +78,9 @@ export function canInput(parameter: PipelineNodeModelParameter) {
(item_type === 'dataset' ||
item_type === 'model' ||
item_type === 'image' ||
item_type === 'code')
item_type === 'code' ||
item_type === 'remote-dataset' ||
item_type === 'remote-model' ||
item_type === 'remote-code')
);
}

+ 55
- 8
react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx View File

@@ -1,7 +1,10 @@
import CodeSelectorModal, { CodeConfigData } from '@/components/CodeSelectorModal';
import KFIcon from '@/components/KFIcon';
import ParameterInput, { requiredValidator } from '@/components/ParameterInput';
import ParameterSelect, { type ParameterSelectDataType } from '@/components/ParameterSelect';
import ParameterSelect, {
type ParameterSelectDataType,
type ParameterSelectObject,
} from '@/components/ParameterSelect';
import ResourceSelectorModal, {
ResourceSelectorType,
selectorTypeConfig,
@@ -9,6 +12,7 @@ import ResourceSelectorModal, {
import SubAreaTitle from '@/components/SubAreaTitle';
import { CommonTabKeys, ComponentType } from '@/enums';
import { canInput, createMenuItems } from '@/pages/Pipeline/Info/utils';
import state from '@/state/jcdResource';
import {
PipelineGlobalParam,
PipelineNodeModel,
@@ -20,6 +24,7 @@ import { to } from '@/utils/promise';
import { removeFormListItem } from '@/utils/ui';
import { MinusCircleOutlined, PlusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { INode } from '@antv/g6';
import { useSnapshot } from '@umijs/max';
import { Button, Drawer, Flex, Form, Input, MenuProps } from 'antd';
import { RuleObject } from 'antd/es/form';
import { NamePath } from 'antd/es/form/interface';
@@ -38,6 +43,17 @@ type PipelineNodeParameterProps = {
onFormChange: (data: PipelineNodeModelSerialize) => void;
};

// 自定义的下拉组件类型
const parameterSelectList = [
'dataset',
'model',
'service',
'resource',
'remote-resource-type',
'remote-image',
'remote-resource',
];

const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParameterProps, ref) => {
const [form] = Form.useForm();
const [stagingItem, setStagingItem] = useState<PipelineNodeModelSerialize>(
@@ -45,6 +61,8 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete
);
const [open, setOpen] = useState(false);
const [menuItems, setMenuItems] = useState<MenuProps['items']>([]);
const snap = useSnapshot(state);
const { setCurrentType } = snap;

const afterOpenChange = async () => {
if (!open) {
@@ -119,6 +137,12 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete

// 参数下拉菜单
setMenuItems(createMenuItems(params, parentNodes));

// 云际组件,设置 store 当前资源类型
if (model.id.startsWith('remote-task')) {
const resourceType = model.in_parameters['--resource_type'].value;
setCurrentType(resourceType);
}
},
close: () => {
onClose();
@@ -136,7 +160,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete
}
},
}),
[form, open],
[form, open, setCurrentType],
);

// ref 类型选择
@@ -144,7 +168,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete
formItemName: NamePath,
item: PipelineNodeModelParameter | Pick<PipelineNodeModelParameter, 'item_type'>,
) => {
if (item.item_type === 'code') {
if (item.item_type === 'code' || item.item_type === 'remote-code') {
selectCodeConfig(formItemName, item);
} else {
selectResource(formItemName, item);
@@ -183,9 +207,11 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete
let type: ResourceSelectorType;
switch (item.item_type) {
case 'dataset':
case 'remote-dataset':
type = ResourceSelectorType.Dataset;
break;
case 'model':
case 'remote-model':
type = ResourceSelectorType.Model;
break;
default:
@@ -249,14 +275,14 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete
// 获取选择数据集、模型后面按钮 icon
const getSelectBtnIcon = (item: { item_type: string }) => {
const type = item.item_type;
if (type === 'code') {
if (type === 'code' || type === 'remote-code') {
return <KFIcon type="icon-xuanzedaimapeizhi" />;
}

let selectorType: ResourceSelectorType;
if (type === 'dataset') {
if (type === 'dataset' || type === 'remote-dataset') {
selectorType = ResourceSelectorType.Dataset;
} else if (type === 'model') {
} else if (type === 'model' || type === 'remote-model') {
selectorType = ResourceSelectorType.Model;
} else {
selectorType = ResourceSelectorType.Mirror;
@@ -331,6 +357,21 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete
return rules;
};

// 云际组件,选择类型后,重置镜像和资源,获取镜像、资源列表
const handleParameterSelect = (
value: ParameterSelectObject,
itemType: string,
parentName: string,
) => {
if (itemType === 'remote-resource-type') {
snap.setCurrentType(value.value);
const remoteImage = form.getFieldValue([parentName, '--image']);
form.setFieldValue([parentName, '--image'], { ...remoteImage, value: undefined });
const remoteResource = form.getFieldValue([parentName, '--resource']);
form.setFieldValue([parentName, '--resource'], { ...remoteResource, value: undefined });
}
};

// 表单组件
const getFormComponent = (
item: { key: string; value: PipelineNodeModelParameter },
@@ -361,12 +402,18 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete
</Flex>
)}
{item.value.type === ComponentType.Select &&
(['dataset', 'model', 'service', 'resource'].includes(item.value.item_type) ? (
(parameterSelectList.includes(item.value.item_type) ? (
<Form.Item name={[parentName, item.key]} rules={getFormRules(item)} noStyle>
<ParameterSelect
isPipeline
dataType={item.value.item_type as ParameterSelectDataType}
placeholder={item.value.placeholder}
onChange={(value) =>
handleParameterSelect(
value as ParameterSelectObject,
item.value.item_type,
parentName,
)
}
/>
</Form.Item>
) : null)}


+ 19
- 1
react-ui/src/services/dataset/index.js View File

@@ -90,6 +90,15 @@ export function compareDatasetVersion(data) {
});
}

// 获取数据集下个版本号
export function getDatasetNextVersionReq(data) {
return request(`/api/mmp/newdataset/queryNextVersion`, {
method: 'POST',
data,
});
}


// ----------------------------模型---------------------------------

// 分页查询模型列表
@@ -157,6 +166,14 @@ export function deleteModelVersion(params) {
});
}

// 获取模型下个版本号
export function getModelNextVersionReq(data) {
return request(`/api/mmp/newmodel/queryNextVersion`, {
method: 'POST',
data,
});
}

// 获取模型依赖
export function getModelAtlasReq(params) {
return request(`/api/mmp/newmodel/getModelDependencyTree`, {
@@ -218,4 +235,5 @@ export function unpraiseResourceReq(id) {
return request(`/api/mmp/newmodel/unpraise/${id}`, {
method: 'DELETE',
});
}
}


+ 87
- 0
react-ui/src/services/external/index.ts View File

@@ -0,0 +1,87 @@
// 外部系统

import { request } from '@umijs/max';

// 云际系统登录
export function jccLoginReq() {
return request(`http://119.45.255.234:30180/jcc-admin/admin/login`, {
method: 'POST',
data: {
username: 'iflytek',
password: 'iflytek@123',
},
headers: {
isToken: false,
},
skipLoading: true,
skipValidating: true,
});
}

// 云际系统获取资源类型
export function jccGetResourceTypesReq(token: string, userId: number) {
return request(`http://119.45.255.234:30180/jsm/jobSet/resourceRange`, {
method: 'POST',
data: {
userID: userId,
},
headers: {
authorization: `${token}`,
isToken: false,
},
skipLoading: true,
skipValidating: true,
});
}

// 云际系统获取资源镜像
export function jccGetImagesReq(token: string, cardTypes: string[]) {
return request(`http://119.45.255.234:30180/jsm/jobSet/queryImages`, {
method: 'POST',
data: {
cardTypes: cardTypes,
},
headers: {
authorization: `${token}`,
isToken: false,
},
skipLoading: true,
skipValidating: true,
});
}

// 云际系统获取资源列表
export function jccGetResourcesReq(token: string, cardType: string) {
return request(`http://119.45.255.234:30180/jsm/jobSet/queryResource`, {
method: 'POST',
data: {
queryResource: {
cpu: {
min: 0,
max: 0,
},
memory: {
min: 0,
max: 0,
},
gpu: {
min: 0,
max: 0,
},
storage: {
min: 0,
max: 0,
},
type: cardType,
},
resourceType: 'Train',
clusterIDs: ['1865927992266461184', ''],
},
headers: {
authorization: `${token}`,
isToken: false,
},
skipLoading: true,
skipValidating: true,
});
}

+ 140
- 0
react-ui/src/state/jcdResource.ts View File

@@ -0,0 +1,140 @@
import {
jccGetImagesReq,
jccGetResourcesReq,
jccGetResourceTypesReq,
jccLoginReq,
} from '@/services/external';
import { to } from '@/utils/promise';
import { proxy } from '@umijs/max';

export type JCCResourceRange = {
type: string;
};

export type JCCResourceType = {
label: string;
value: string;
};

export interface JCCResourceImage {
imageID: number;
name: string;
createTime: Date;
clusterImages: JCCClusterImage[];
}

export interface JCCClusterImage {
imageID: number;
clusterID: string;
originImageType: string;
originImageID: string;
originImageName: string;
cards: JCCCard[];
}

export interface JCCCard {
originImageID: string;
card: string;
}

export interface JCCResourceStandard {
id: number;
sourceKey: string;
type: string;
name: string;
totalCount: number;
availableCount: number;
changeType: number;
status: number;
region: string;
clusterId: string;
costPerUnit: number;
costType: string;
tag: string;
userId: number;
createTime: Date;
updateTime: Date;
baseResourceSpecs: JCCBaseResourceSpec[];
}

export interface JCCBaseResourceSpec {
id: number;
resourceSpecId: number;
type: string;
name: string;
totalValue: number;
totalUnit: string;
availableValue: number;
availableUnit: string;
userId: number;
createTime: Date;
updateTime: Date;
}

type JCCResourceTypeStore = {
token: string;
types: JCCResourceType[];
images: JCCResourceImage[];
resources: JCCResourceStandard[];
currentType: string | undefined | null;
jccLogin: () => void;
getResourceTypes: () => void;
getImages: (cardTypes: string[]) => void;
getResources: (cardType: string) => void;
setCurrentType: (cardType: string) => void;
};

const state = proxy<JCCResourceTypeStore>({
token: '',
types: [],
images: [],
resources: [],
currentType: undefined,
jccLogin: async () => {
const [res] = await to(jccLoginReq());
if (res && res.code === 200 && res.data) {
const { tokenHead, token } = res.data;
state.token = tokenHead + token;
}
},
getResourceTypes: async () => {
const [loginRes] = await to(jccLoginReq());
if (loginRes && loginRes.code === 200 && loginRes.data) {
const { tokenHead, token, jsmUserInfo } = loginRes.data;
state.token = tokenHead + token;
const userID = jsmUserInfo?.data?.userID;
const [res] = await to(jccGetResourceTypesReq(tokenHead + token, userID));
if (res && res.code === 'OK' && res.data) {
state.types = res.data.resourceRanges?.map((v: JCCResourceRange) => ({
label: v.type,
value: v.type,
}));
}
}
},
getImages: async (cardTypes: string[]) => {
const [res] = await to(jccGetImagesReq(state.token, cardTypes));
if (res && res.code === 'OK' && res.data) {
state.images = res.data.images;
}
},
getResources: async (cardType: string) => {
const [res] = await to(jccGetResourcesReq(state.token, cardType));
if (res && res.code === 'OK' && res.data) {
state.resources = res.data.resource;
}
},
setCurrentType: (cardType: string | undefined | null) => {
if (state.currentType !== cardType) {
state.currentType = cardType;
state.images = [];
state.resources = [];
if (cardType) {
state.getImages([cardType]);
state.getResources(cardType);
}
}
},
});

export default state;

+ 53
- 0
react-ui/src/state/systemResource.ts View File

@@ -0,0 +1,53 @@
/*
* @Author: 赵伟
* @Date: 2024-10-10 08:51:41
* @Description: 资源规格
*/

import { getComputingResourceReq } from '@/services/pipeline';
import { ComputingResource } from '@/types';
import { to } from '@/utils/promise';
import { proxy } from '@umijs/max';
import { type SelectProps } from 'antd';

/** 过滤资源规格 */
export const filterResourceStandard: SelectProps<string, ComputingResource>['filterOption'] = (
input: string,
option?: ComputingResource,
) => {
return (
option?.computing_resource?.toLocaleLowerCase()?.includes(input.toLocaleLowerCase()) ?? false
);
};

/** 资源规格字段 */
export const resourceFieldNames = {
label: 'description',
value: 'id',
};

export interface SystemResourceStore {
resources: ComputingResource[];
}

const state = proxy<SystemResourceStore>({
resources: [],
});

/** 获取资源列表 */
export const getSystemResources = async () => {
if (state.resources.length > 0) {
return;
}
const params = {
page: 0,
size: 1000,
resource_type: '',
};
const [res] = await to(getComputingResourceReq(params));
if (res && res.data && Array.isArray(res.data.content)) {
state.resources = res.data.content;
}
};

export default state;

+ 2
- 0
react-ui/src/utils/sessionStorage.ts View File

@@ -13,6 +13,8 @@ export default class SessionStorage {
static readonly aimUrlKey = 'aim-url';
/** tensorBoard url */
static readonly tensorBoardUrlKey = 'tensor-board-url';
// /** 云际系统 Token */
// static readonly jccTokenKey = 'jcc-token';

/**
* 获取 SessionStorage 值


Loading…
Cancel
Save