feat: 完成数据集的修改
This commit is contained in:
parent
877816cb14
commit
da3f801fd0
Binary file not shown.
After Width: | Height: | Size: 392 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 459 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,35 @@
|
|||
.kf-basic-info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 40px;
|
||||
align-items: flex-start;
|
||||
width: 80%;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
width: calc(50% - 20px);
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
|
||||
&__label {
|
||||
width: 100px;
|
||||
color: @text-color-secondary;
|
||||
text-align: justify;
|
||||
text-align-last: justify;
|
||||
}
|
||||
|
||||
&__value {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
color: @text-color;
|
||||
word-break: break-all;
|
||||
|
||||
&::before {
|
||||
margin-right: 16px;
|
||||
content: ':';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import './index.less';
|
||||
export type BasicInfoData = {
|
||||
label: string;
|
||||
value?: any;
|
||||
format?: (_value: any) => string;
|
||||
};
|
||||
|
||||
type BasicInfoProps = {
|
||||
datas: BasicInfoData[];
|
||||
};
|
||||
|
||||
function BasicInfo({ datas }: BasicInfoProps) {
|
||||
return (
|
||||
<div className="kf-basic-info">
|
||||
{datas.map((item) => (
|
||||
<div className="kf-basic-info__item" key={item.label}>
|
||||
<div className="kf-basic-info__item__label">{item.label}</div>
|
||||
<div className="kf-basic-info__item__value">
|
||||
{item.format ? item.format(item.value) ?? '--' : item.value ?? '--'}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default BasicInfo;
|
|
@ -0,0 +1,40 @@
|
|||
.kf-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
|
||||
&__image {
|
||||
width: 475px;
|
||||
height: 292px;
|
||||
}
|
||||
|
||||
&__title {
|
||||
margin-top: 15px;
|
||||
color: @text-color;
|
||||
font-weight: 500;
|
||||
font-size: 30px;
|
||||
letter-spacing: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__content {
|
||||
margin-top: 15px;
|
||||
color: @text-color-secondary;
|
||||
font-size: 15px;
|
||||
white-space: pre-line;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
|
||||
&__back-btn {
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
import { Button } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import './index.less';
|
||||
|
||||
export enum EmptyType {
|
||||
NoData = 'NoData',
|
||||
NotFound = 'NotFound',
|
||||
Developing = 'Developing',
|
||||
}
|
||||
|
||||
type EmptyProps = {
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
type: EmptyType;
|
||||
title?: string;
|
||||
content?: string;
|
||||
hasFooter?: boolean;
|
||||
footer?: () => React.ReactNode;
|
||||
backTitle?: string;
|
||||
onBack?: () => void;
|
||||
};
|
||||
|
||||
function getEmptyImage(type: EmptyType) {
|
||||
switch (type) {
|
||||
case EmptyType.NoData:
|
||||
return require('@/assets/img/no-data.png');
|
||||
case EmptyType.NotFound:
|
||||
return require('@/assets/img/404.png');
|
||||
case EmptyType.Developing:
|
||||
return require('@/assets/img/missing-back.png');
|
||||
}
|
||||
}
|
||||
|
||||
function KFEmpty({
|
||||
className,
|
||||
style,
|
||||
type,
|
||||
title,
|
||||
content,
|
||||
hasFooter = false,
|
||||
footer,
|
||||
backTitle = '返回',
|
||||
onBack,
|
||||
}: EmptyProps) {
|
||||
const image = getEmptyImage(type);
|
||||
|
||||
return (
|
||||
<div className={classNames('kf-empty', className)} style={style}>
|
||||
<img className="kf-empty__image" src={image} />
|
||||
<div className="kf-empty__title">{title}</div>
|
||||
<div className="kf-empty__content">{content}</div>
|
||||
{hasFooter && (
|
||||
<div className="kf-empty__footer">
|
||||
{footer ? (
|
||||
footer()
|
||||
) : (
|
||||
<Button className="kf-empty__footer__back-btn" type="primary" onClick={onBack}>
|
||||
{backTitle}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default KFEmpty;
|
|
@ -4,7 +4,7 @@
|
|||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
z-index: 1001;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
|
|
@ -11,6 +11,7 @@ import ParameterInput, { type ParameterInputProps } from '../ParameterInput';
|
|||
import './index.less';
|
||||
|
||||
export { requiredValidator, type ParameterInputObject } from '../ParameterInput';
|
||||
export { ResourceSelectorType, selectorTypeConfig, type ResourceSelectorResponse };
|
||||
|
||||
type ResourceSelectProps = {
|
||||
type: ResourceSelectorType;
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -116,6 +116,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.ant-input.ant-input-disabled {
|
||||
height: 46px;
|
||||
}
|
||||
|
||||
// 选择框高度为46px
|
||||
.ant-select-single {
|
||||
height: 46px;
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
import { history } from '@umijs/max';
|
||||
import { Button, Result } from 'antd';
|
||||
import React from 'react';
|
||||
import KFEmpty, { EmptyType } from '@/components/KFEmpty';
|
||||
import { useNavigate } from '@umijs/max';
|
||||
|
||||
const NoFoundPage: React.FC = () => (
|
||||
<Result
|
||||
status="404"
|
||||
title="404"
|
||||
subTitle="Sorry, the page you visited does not exist."
|
||||
extra={
|
||||
<Button type="primary" onClick={() => history.push('/')}>
|
||||
Back Home
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
);
|
||||
const NoFoundPage = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<KFEmpty
|
||||
style={{ height: '100vh' }}
|
||||
type={EmptyType.NotFound}
|
||||
title="404"
|
||||
content={'很抱歉,您访问的页面地址有误,\n或者该页面不存在。'}
|
||||
hasFooter={true}
|
||||
backTitle="返回首页"
|
||||
onBack={() => navigate('/')}
|
||||
></KFEmpty>
|
||||
);
|
||||
};
|
||||
|
||||
export default NoFoundPage;
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import KFEmpty, { EmptyType } from '@/components/KFEmpty';
|
||||
import KFIcon from '@/components/KFIcon';
|
||||
import { deleteCodeConfigReq, getCodeConfigListReq } from '@/services/codeConfig';
|
||||
import { openAntdModal } from '@/utils/modal';
|
||||
import { to } from '@/utils/promise';
|
||||
import { modalConfirm } from '@/utils/ui';
|
||||
import { App, Button, Empty, Input, Pagination, PaginationProps } from 'antd';
|
||||
import { App, Button, Input, Pagination, PaginationProps } from 'antd';
|
||||
import { useEffect, useState } from 'react';
|
||||
import AddCodeConfigModal, { OperationType } from '../components/AddCodeConfigModal';
|
||||
import CodeConfigItem from '../components/CodeConfigItem';
|
||||
|
@ -31,7 +32,7 @@ export type ResourceListRef = {
|
|||
};
|
||||
|
||||
function CodeConfigList() {
|
||||
const [dataList, setDataList] = useState<CodeConfigData[]>([]);
|
||||
const [dataList, setDataList] = useState<CodeConfigData[] | undefined>(undefined);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [pagination, setPagination] = useState<PaginationProps>({
|
||||
current: 1,
|
||||
|
@ -56,6 +57,9 @@ function CodeConfigList() {
|
|||
if (res && res.data && res.data.content) {
|
||||
setDataList(res.data.content);
|
||||
setTotal(res.data.totalElements);
|
||||
} else {
|
||||
setDataList([]);
|
||||
setTotal(0);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -117,7 +121,7 @@ function CodeConfigList() {
|
|||
return (
|
||||
<div className={styles['code-config-list']}>
|
||||
<div className={styles['code-config-list__header']}>
|
||||
<span>数据总数:{total}个</span>
|
||||
<span>数据总数:{total} 个</span>
|
||||
<div>
|
||||
<Input.Search
|
||||
placeholder="按代码仓库名称筛选"
|
||||
|
@ -139,10 +143,10 @@ function CodeConfigList() {
|
|||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{dataList?.length !== 0 ? (
|
||||
{dataList && dataList.length !== 0 && (
|
||||
<>
|
||||
<div className={styles['code-config-list__content']}>
|
||||
{dataList?.map((item) => (
|
||||
{dataList.map((item) => (
|
||||
<CodeConfigItem
|
||||
item={item}
|
||||
key={item.id}
|
||||
|
@ -161,10 +165,13 @@ function CodeConfigList() {
|
|||
{...pagination}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div className={styles['code-config-list__empty']}>
|
||||
<Empty></Empty>
|
||||
</div>
|
||||
)}
|
||||
{dataList && dataList.length === 0 && (
|
||||
<KFEmpty
|
||||
className={styles['code-config-list__empty']}
|
||||
type={EmptyType.NoData}
|
||||
title="暂无数据"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
.upload-tip {
|
||||
margin-top: 5px;
|
||||
color: @error-color;
|
||||
color: @text-color-secondary;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.upload-button {
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { getAccessToken } from '@/access';
|
||||
import { DictValueEnumObj } from '@/components/DictTag';
|
||||
import KFIcon from '@/components/KFIcon';
|
||||
import KFModal from '@/components/KFModal';
|
||||
import { addDatesetAndVesion } from '@/services/dataset/index.js';
|
||||
import { getDictSelectOption } from '@/services/system/dict';
|
||||
import { addDateset } from '@/services/dataset/index.js';
|
||||
import { to } from '@/utils/promise';
|
||||
import { getFileListFromEvent, validateUploadFiles } from '@/utils/ui';
|
||||
import {
|
||||
|
@ -19,7 +17,7 @@ import {
|
|||
type UploadProps,
|
||||
} from 'antd';
|
||||
import { omit } from 'lodash';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { CategoryData } from '../../config';
|
||||
import styles from './index.less';
|
||||
|
||||
|
@ -31,15 +29,15 @@ interface AddDatasetModalProps extends Omit<ModalProps, 'onOk'> {
|
|||
|
||||
function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalProps) {
|
||||
const [uuid] = useState(Date.now());
|
||||
const [clusterOptions, setClusterOptions] = useState<DictValueEnumObj[]>([]);
|
||||
// const [clusterOptions, setClusterOptions] = useState<DictValueEnumObj[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
getClusterOptions();
|
||||
}, []);
|
||||
// useEffect(() => {
|
||||
// getClusterOptions();
|
||||
// }, []);
|
||||
|
||||
// 上传组件参数
|
||||
const uploadProps: UploadProps = {
|
||||
action: '/api/mmp/dataset/upload',
|
||||
action: '/api/mmp/newdataset/upload',
|
||||
headers: {
|
||||
Authorization: getAccessToken() || '',
|
||||
},
|
||||
|
@ -47,16 +45,16 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
|
|||
};
|
||||
|
||||
// 获取集群版本数据
|
||||
const getClusterOptions = async () => {
|
||||
const [res] = await to(getDictSelectOption('available_cluster'));
|
||||
if (res) {
|
||||
setClusterOptions(res);
|
||||
}
|
||||
};
|
||||
// const getClusterOptions = async () => {
|
||||
// const [res] = await to(getDictSelectOption('available_cluster'));
|
||||
// if (res) {
|
||||
// setClusterOptions(res);
|
||||
// }
|
||||
// };
|
||||
|
||||
// 上传请求
|
||||
const createDataset = async (params: any) => {
|
||||
const [res] = await to(addDatesetAndVesion(params));
|
||||
const [res] = await to(addDateset(params));
|
||||
if (res) {
|
||||
message.success('创建成功');
|
||||
onOk?.();
|
||||
|
@ -94,7 +92,13 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
|
|||
}}
|
||||
destroyOnClose
|
||||
>
|
||||
<Form name="form" layout="vertical" onFinish={onFinish} autoComplete="off">
|
||||
<Form
|
||||
name="form"
|
||||
layout="vertical"
|
||||
onFinish={onFinish}
|
||||
initialValues={{ is_public: false }}
|
||||
autoComplete="off"
|
||||
>
|
||||
<Form.Item
|
||||
label="数据集名称"
|
||||
name="name"
|
||||
|
@ -106,7 +110,7 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
|
|||
},
|
||||
]}
|
||||
>
|
||||
<Input placeholder="请输入数据名称" showCount allowClear maxLength={64} />
|
||||
<Input placeholder="请输入数据名称" showCount allowClear maxLength={50} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="数据集版本"
|
||||
|
@ -125,7 +129,7 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
|
|||
allowClear
|
||||
placeholder="请选择数据集分类"
|
||||
options={typeList}
|
||||
fieldNames={{ label: 'name', value: 'id' }}
|
||||
fieldNames={{ label: 'name', value: 'name' }}
|
||||
optionFilterProp="name"
|
||||
showSearch
|
||||
/>
|
||||
|
@ -135,14 +139,14 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
|
|||
allowClear
|
||||
placeholder="请选择研究方向/应用领域"
|
||||
options={tagList}
|
||||
fieldNames={{ label: 'name', value: 'id' }}
|
||||
fieldNames={{ label: 'name', value: 'name' }}
|
||||
optionFilterProp="name"
|
||||
showSearch
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="集群版本" name="available_cluster">
|
||||
{/* <Form.Item label="集群版本" name="available_cluster">
|
||||
<Select allowClear placeholder="请选择集群版本" options={clusterOptions} />
|
||||
</Form.Item>
|
||||
</Form.Item> */}
|
||||
<Form.Item
|
||||
label="数据集简介"
|
||||
name="description"
|
||||
|
@ -156,15 +160,19 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
|
|||
<Input.TextArea
|
||||
placeholder="请输入数据集简介"
|
||||
showCount
|
||||
maxLength={256}
|
||||
maxLength={200}
|
||||
autoSize={{ minRows: 2, maxRows: 6 }}
|
||||
allowClear
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="选择流水线" name="range">
|
||||
<Form.Item
|
||||
label="可见性"
|
||||
name="is_public"
|
||||
rules={[{ required: true, message: '请选择可见性' }]}
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value="0">仅自己可见</Radio>
|
||||
<Radio value="1">工作空间可见</Radio>
|
||||
<Radio value={false}>私有</Radio>
|
||||
<Radio value={true}>公开</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
|
@ -187,7 +195,7 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
|
|||
>
|
||||
上传文件
|
||||
</Button>
|
||||
<div className={styles['upload-tip']}>只允许上传.zip,.tgz格式文件</div>
|
||||
<div className={styles['upload-tip']}>只允许上传 .zip 和 .tgz 格式文件</div>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
|
|
@ -21,14 +21,16 @@ import styles from '../AddDatasetModal/index.less';
|
|||
interface AddVersionModalProps extends Omit<ModalProps, 'onOk'> {
|
||||
resourceType: ResourceType;
|
||||
resourceId: number;
|
||||
initialName: string;
|
||||
identifier: string;
|
||||
resoureName: string;
|
||||
onOk: () => void;
|
||||
}
|
||||
|
||||
function AddVersionModal({
|
||||
resourceType,
|
||||
resourceId,
|
||||
initialName,
|
||||
resoureName,
|
||||
identifier,
|
||||
onOk,
|
||||
...rest
|
||||
}: AddVersionModalProps) {
|
||||
|
@ -58,17 +60,20 @@ function AddVersionModal({
|
|||
const onFinish = (formData: any) => {
|
||||
const fileList: UploadFile[] = formData['fileList'] ?? [];
|
||||
if (validateUploadFiles(fileList)) {
|
||||
const otherParams = omit(formData, ['fileList']);
|
||||
const params = fileList.map((item) => {
|
||||
const dataset_version_vos = fileList.map((item) => {
|
||||
const data = item.response?.data?.[0] ?? {};
|
||||
return {
|
||||
...otherParams,
|
||||
[config.idParamKey]: resourceId,
|
||||
file_name: data.fileName,
|
||||
file_size: data.fileSize,
|
||||
url: data.url,
|
||||
};
|
||||
});
|
||||
const params = {
|
||||
id: resourceId,
|
||||
identifier,
|
||||
dataset_version_vos,
|
||||
...omit(formData, 'fileList'),
|
||||
};
|
||||
createDatasetVersion(params);
|
||||
}
|
||||
};
|
||||
|
@ -90,7 +95,7 @@ function AddVersionModal({
|
|||
name="form"
|
||||
layout="vertical"
|
||||
initialValues={{
|
||||
name: initialName,
|
||||
name: resoureName,
|
||||
}}
|
||||
onFinish={onFinish}
|
||||
autoComplete="off"
|
||||
|
@ -115,13 +120,21 @@ function AddVersionModal({
|
|||
required: true,
|
||||
message: `请输入${name}版本`,
|
||||
},
|
||||
{
|
||||
validator: (_rule, value) => {
|
||||
if (value === 'master') {
|
||||
return Promise.reject(`版本号不能为 master`);
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input placeholder={`请输入${name}版本`} maxLength={64} showCount allowClear />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="版本描述"
|
||||
name="description"
|
||||
name="version_desc"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
|
@ -158,7 +171,7 @@ function AddVersionModal({
|
|||
上传文件
|
||||
</Button>
|
||||
{resourceType === ResourceType.Dataset && (
|
||||
<div className={styles['upload-tip']}>只允许上传.zip格式文件</div>
|
||||
<div className={styles['upload-tip']}>只允许上传 .zip 格式文件</div>
|
||||
)}
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
.resource-info {
|
||||
height: 100%;
|
||||
|
||||
&__top {
|
||||
width: 100%;
|
||||
height: 125px;
|
||||
margin-bottom: 10px;
|
||||
padding: 20px 30px;
|
||||
background-image: url(@/assets/img/dataset-intro-top.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: top center;
|
||||
background-size: 100% 100%;
|
||||
|
||||
&__name {
|
||||
margin-right: 10px;
|
||||
color: @text-color;
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
&__tag {
|
||||
padding: 4px 10px;
|
||||
color: @primary-color;
|
||||
font-size: 14px;
|
||||
background: .addAlpha(@primary-color, 0.1) [];
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
:global {
|
||||
.ant-btn-dangerous {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__bottom {
|
||||
height: calc(100% - 135px);
|
||||
padding: 8px 30px 20px;
|
||||
background: #ffffff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 2px 12px rgba(180, 182, 191, 0.09);
|
||||
|
||||
:global {
|
||||
.ant-tabs {
|
||||
height: 100%;
|
||||
.ant-tabs-content-holder {
|
||||
height: 100%;
|
||||
.ant-tabs-content {
|
||||
height: 100%;
|
||||
.ant-tabs-tabpane {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* @Author: 赵伟
|
||||
* @Date: 2024-09-06 09:23:15
|
||||
* @Description: 数据集、模型详情
|
||||
*/
|
||||
|
||||
import KFIcon from '@/components/KFIcon';
|
||||
import {
|
||||
ResourceData,
|
||||
ResourceType,
|
||||
ResourceVersionData,
|
||||
resourceConfig,
|
||||
} from '@/pages/Dataset/config';
|
||||
import ModelEvolution from '@/pages/Model/components/ModelEvolution';
|
||||
import { openAntdModal } from '@/utils/modal';
|
||||
import { to } from '@/utils/promise';
|
||||
import { getSessionStorageItem, resourceItemKey } from '@/utils/sessionStorage';
|
||||
import { modalConfirm } from '@/utils/ui';
|
||||
import { useParams, useSearchParams } from '@umijs/max';
|
||||
import { App, Button, Flex, Select, Tabs } from 'antd';
|
||||
import { pick } from 'lodash';
|
||||
import { useEffect, useState } from 'react';
|
||||
import AddVersionModal from '../AddVersionModal';
|
||||
import ResourceIntro from '../ResourceIntro';
|
||||
import ResourceVersion from '../ResourceVersion';
|
||||
import styles from './index.less';
|
||||
|
||||
// 这里值小写是因为值会写在 url 中
|
||||
export enum ResourceInfoTabKeys {
|
||||
Introduction = 'introduction', // 简介
|
||||
Version = 'version', // 版本
|
||||
Evolution = 'evolution', // 演化
|
||||
}
|
||||
|
||||
type ResourceInfoProps = {
|
||||
resourceType: ResourceType;
|
||||
};
|
||||
|
||||
const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
|
||||
const [info, setInfo] = useState<ResourceData>({} as ResourceData);
|
||||
const locationParams = useParams();
|
||||
const [searchParams] = useSearchParams();
|
||||
// 模型演化传入的 tab
|
||||
const defaultTab = searchParams.get('tab') || ResourceInfoTabKeys.Introduction;
|
||||
// 模型演化传入的版本
|
||||
let versionParam = searchParams.get('version');
|
||||
const [versionList, setVersionList] = useState<ResourceVersionData[]>([]);
|
||||
const [version, setVersion] = useState<string | undefined>(undefined);
|
||||
const [activeTab, setActiveTab] = useState<string>(defaultTab);
|
||||
const resourceId = Number(locationParams.id);
|
||||
const config = resourceConfig[resourceType];
|
||||
const typeName = config.name; // 数据集/模型
|
||||
const { message } = App.useApp();
|
||||
|
||||
useEffect(() => {
|
||||
const info = getSessionStorageItem(resourceItemKey, true);
|
||||
if (info) {
|
||||
setInfo(info);
|
||||
getVersionList(pick(info, ['owner', 'identifier']));
|
||||
}
|
||||
}, [resourceId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (version) {
|
||||
getResourceDetail({
|
||||
...pick(info, ['owner', 'name', 'id', 'identifier']),
|
||||
version,
|
||||
});
|
||||
}
|
||||
}, [version]);
|
||||
|
||||
// 获取详情
|
||||
const getResourceDetail = async (params: {
|
||||
owner: string;
|
||||
name: string;
|
||||
id: number;
|
||||
identifier: string;
|
||||
version?: string;
|
||||
}) => {
|
||||
const request = config.getInfo;
|
||||
const [res] = await to(request(params));
|
||||
if (res) {
|
||||
setInfo(res.data);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取版本列表
|
||||
const getVersionList = async (params: { owner: string; identifier: string }) => {
|
||||
const request = config.getVersions;
|
||||
const [res] = await to(request(params));
|
||||
if (res && res.data && res.data.length > 0) {
|
||||
setVersionList(res.data);
|
||||
if (
|
||||
versionParam &&
|
||||
res.data.find((item: ResourceVersionData) => item.name === versionParam)
|
||||
) {
|
||||
setVersion(versionParam);
|
||||
versionParam = null;
|
||||
} else {
|
||||
setVersion(res.data[0].name);
|
||||
}
|
||||
} else {
|
||||
setVersion(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
// 新建版本
|
||||
const showModal = () => {
|
||||
const { close } = openAntdModal(AddVersionModal, {
|
||||
resourceType: resourceType,
|
||||
resourceId: resourceId,
|
||||
resoureName: info.name,
|
||||
identifier: info.identifier,
|
||||
onOk: () => {
|
||||
getVersionList(pick(info, ['owner', 'identifier']));
|
||||
close();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 版本变化
|
||||
const handleVersionChange = (value: string) => {
|
||||
setVersion(value);
|
||||
};
|
||||
|
||||
// 删除版本
|
||||
const deleteVersion = async () => {
|
||||
const request = config.deleteVersion;
|
||||
const params = {
|
||||
identifier: info.identifier,
|
||||
owner: info.owner,
|
||||
version,
|
||||
};
|
||||
const [res] = await to(request(params));
|
||||
if (res) {
|
||||
message.success('删除成功');
|
||||
setVersion(undefined);
|
||||
getVersionList(pick(info, ['owner', 'identifier']));
|
||||
}
|
||||
};
|
||||
|
||||
// 处理删除
|
||||
const hanldeDelete = () => {
|
||||
modalConfirm({
|
||||
title: '删除后,该版本将不可恢复',
|
||||
content: '是否确认删除?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
deleteVersion();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const items = [
|
||||
{
|
||||
key: ResourceInfoTabKeys.Introduction,
|
||||
label: `${typeName}简介`,
|
||||
icon: <KFIcon type="icon-moxingjianjie" />,
|
||||
children: <ResourceIntro resourceType={resourceType} info={info}></ResourceIntro>,
|
||||
},
|
||||
{
|
||||
key: ResourceInfoTabKeys.Version,
|
||||
label: `${typeName}文件`,
|
||||
icon: <KFIcon type="icon-moxingwenjian" />,
|
||||
children: <ResourceVersion resourceType={resourceType} info={info}></ResourceVersion>,
|
||||
},
|
||||
];
|
||||
|
||||
if (resourceType === ResourceType.Model) {
|
||||
items.push({
|
||||
key: ResourceInfoTabKeys.Evolution,
|
||||
label: `模型演化`,
|
||||
icon: <KFIcon type="icon-moxingyanhua1" />,
|
||||
children: (
|
||||
<ModelEvolution
|
||||
resourceId={resourceId}
|
||||
versionList={versionList}
|
||||
version={version}
|
||||
isActive={activeTab === ResourceInfoTabKeys.Evolution}
|
||||
onVersionChange={handleVersionChange}
|
||||
></ModelEvolution>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
const infoTypePropertyName = config.infoTypePropertyName as keyof ResourceData;
|
||||
const infoTagPropertyName = config.infoTagPropertyName as keyof ResourceData;
|
||||
|
||||
return (
|
||||
<div className={styles['resource-info']}>
|
||||
<div className={styles['resource-info__top']}>
|
||||
<Flex align="center" gap={10} style={{ marginBottom: '20px' }}>
|
||||
<div className={styles['resource-info__top__name']}>{info.name}</div>
|
||||
{info[infoTypePropertyName] && (
|
||||
<div className={styles['resource-info__top__tag']}>
|
||||
{(info[infoTypePropertyName] as string) || '--'}
|
||||
</div>
|
||||
)}
|
||||
{info[infoTagPropertyName] && (
|
||||
<div className={styles['resource-info__top__tag']}>
|
||||
{(info[infoTagPropertyName] as string) || '--'}
|
||||
</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: 'auto', marginRight: 0 }}
|
||||
onClick={hanldeDelete}
|
||||
icon={<KFIcon type="icon-shanchu" />}
|
||||
disabled={!version}
|
||||
danger
|
||||
>
|
||||
删除版本
|
||||
</Button>
|
||||
</Flex>
|
||||
</div>
|
||||
<div className={styles['resource-info__bottom']}>
|
||||
<Tabs activeKey={activeTab} items={items} onChange={(key) => setActiveTab(key)}></Tabs>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default ResourceInfo;
|
|
@ -1,65 +1,10 @@
|
|||
.resource-intro {
|
||||
height: 100%;
|
||||
|
||||
&__top {
|
||||
width: 100%;
|
||||
margin-top: 24px;
|
||||
&__basic {
|
||||
width: 100%;
|
||||
height: 110px;
|
||||
margin-bottom: 10px;
|
||||
padding: 20px 30px 0;
|
||||
background-image: url(@/assets/img/dataset-intro-top.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: top center;
|
||||
background-size: 100% 100%;
|
||||
|
||||
&__name {
|
||||
margin-bottom: 12px;
|
||||
color: @text-color;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
&__tag {
|
||||
margin-right: 10px;
|
||||
padding: 4px 10px;
|
||||
color: @primary-color;
|
||||
font-size: 14px;
|
||||
background: rgba(22, 100, 255, 0.1);
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&__bottom {
|
||||
height: calc(100% - 120px);
|
||||
padding: 8px 30px 20px;
|
||||
background: #ffffff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 2px 12px rgba(180, 182, 191, 0.09);
|
||||
|
||||
:global {
|
||||
.ant-tabs {
|
||||
height: 100%;
|
||||
.ant-tabs-content-holder {
|
||||
height: 100%;
|
||||
.ant-tabs-content {
|
||||
height: 100%;
|
||||
.ant-tabs-tabpane {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__title {
|
||||
margin: 30px 0 10px;
|
||||
color: @text-color;
|
||||
font-weight: 500;
|
||||
font-size: @font-size;
|
||||
}
|
||||
|
||||
&__intro {
|
||||
color: @text-color-secondary;
|
||||
font-size: 14px;
|
||||
&__usage {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,156 +1,78 @@
|
|||
import KFIcon from '@/components/KFIcon';
|
||||
import ModelEvolution from '@/pages/Model/components/ModelEvolution';
|
||||
import { to } from '@/utils/promise';
|
||||
import { useParams, useSearchParams } from '@umijs/max';
|
||||
import { Flex, Tabs } from 'antd';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ResourceData, ResourceType, resourceConfig } from '../../config';
|
||||
import ResourceVersion from '../ResourceVersion';
|
||||
import BasicInfo, { BasicInfoData } from '@/components/BasicInfo';
|
||||
import SubAreaTitle from '@/components/SubAreaTitle';
|
||||
import { ResourceData, ResourceType } from '@/pages/Dataset/config';
|
||||
import styles from './index.less';
|
||||
|
||||
// 这里值小写是因为值会写在 url 中
|
||||
export enum ResourceInfoTabKeys {
|
||||
Introduction = 'introduction', // 简介
|
||||
Version = 'version', // 版本
|
||||
Evolution = 'evolution', // 演化
|
||||
}
|
||||
|
||||
type ResourceIntroProps = {
|
||||
resourceType: ResourceType;
|
||||
info: ResourceData;
|
||||
};
|
||||
|
||||
const ResourceIntro = ({ resourceType }: ResourceIntroProps) => {
|
||||
const [info, setInfo] = useState<ResourceData>({} as ResourceData);
|
||||
const locationParams = useParams();
|
||||
const [searchParams] = useSearchParams();
|
||||
const defaultTab = searchParams.get('tab') || ResourceInfoTabKeys.Introduction;
|
||||
let versionParam = searchParams.get('version');
|
||||
const [versionList, setVersionList] = useState([]);
|
||||
const [version, setVersion] = useState<string | undefined>(undefined);
|
||||
const [activeTab, setActiveTab] = useState<string>(defaultTab);
|
||||
const resourceId = Number(locationParams.id);
|
||||
const config = resourceConfig[resourceType];
|
||||
const typeName = config.name; // 数据集/模型
|
||||
|
||||
useEffect(() => {
|
||||
getModelByDetail();
|
||||
getVersionList();
|
||||
}, [resourceId]);
|
||||
|
||||
// 获取详情
|
||||
const getModelByDetail = async () => {
|
||||
const request = config.getInfo;
|
||||
const [res] = await to(request(resourceId));
|
||||
if (res) {
|
||||
setInfo(res.data);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取版本列表
|
||||
const getVersionList = async () => {
|
||||
const request = config.getVersions;
|
||||
const [res] = await to(request(resourceId));
|
||||
if (res && res.data && res.data.length > 0) {
|
||||
setVersionList(
|
||||
res.data.map((item: string) => {
|
||||
return {
|
||||
label: item,
|
||||
value: item,
|
||||
};
|
||||
}),
|
||||
);
|
||||
if (versionParam && res.data.includes(versionParam)) {
|
||||
setVersion(versionParam);
|
||||
versionParam = null;
|
||||
} else {
|
||||
setVersion(res.data[0]);
|
||||
}
|
||||
} else {
|
||||
setVersion(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
// 版本变化
|
||||
const handleVersionChange = (value: string) => {
|
||||
setVersion(value);
|
||||
};
|
||||
|
||||
const items = [
|
||||
function ResourceIntro({ info }: ResourceIntroProps) {
|
||||
const basicDatas: BasicInfoData[] = [
|
||||
{
|
||||
key: ResourceInfoTabKeys.Introduction,
|
||||
label: `${typeName}简介`,
|
||||
icon: <KFIcon type="icon-moxingjianjie" />,
|
||||
children: (
|
||||
<>
|
||||
<div className={styles['resource-intro__title']}>简介</div>
|
||||
<div className={styles['resource-intro__intro']}>{info.description}</div>
|
||||
</>
|
||||
),
|
||||
label: '数据集名称',
|
||||
value: info.name,
|
||||
},
|
||||
{
|
||||
key: ResourceInfoTabKeys.Version,
|
||||
label: `${typeName}文件/版本`,
|
||||
icon: <KFIcon type="icon-moxingwenjian" />,
|
||||
children: (
|
||||
<ResourceVersion
|
||||
resourceType={resourceType}
|
||||
resourceId={resourceId}
|
||||
resourceName={info.name}
|
||||
isPublic={info.available_range === 1}
|
||||
versionList={versionList}
|
||||
version={version}
|
||||
isActive={activeTab === ResourceInfoTabKeys.Version}
|
||||
getVersionList={getVersionList}
|
||||
onVersionChange={handleVersionChange}
|
||||
></ResourceVersion>
|
||||
),
|
||||
label: '版本',
|
||||
value: info.version,
|
||||
},
|
||||
{
|
||||
label: '创建人',
|
||||
value: info.create_by,
|
||||
},
|
||||
{
|
||||
label: '更新时间',
|
||||
value: info.update_time,
|
||||
},
|
||||
{
|
||||
label: '数据来源',
|
||||
value: info.dataset_source,
|
||||
},
|
||||
{
|
||||
label: '处理代码',
|
||||
value: info.processing_code,
|
||||
},
|
||||
{
|
||||
label: '数据集分类',
|
||||
value: info.data_type,
|
||||
},
|
||||
{
|
||||
label: '研究方向',
|
||||
value: info.data_tag,
|
||||
},
|
||||
{
|
||||
label: '数据集描述',
|
||||
value: info.description,
|
||||
},
|
||||
{
|
||||
label: '版本描述',
|
||||
value: info.version_desc,
|
||||
},
|
||||
];
|
||||
|
||||
if (resourceType === ResourceType.Model) {
|
||||
items.push({
|
||||
key: ResourceInfoTabKeys.Evolution,
|
||||
label: `模型演化`,
|
||||
icon: <KFIcon type="icon-moxingyanhua1" />,
|
||||
children: (
|
||||
<ModelEvolution
|
||||
resourceId={resourceId}
|
||||
versionList={versionList}
|
||||
version={version}
|
||||
isActive={activeTab === ResourceInfoTabKeys.Evolution}
|
||||
onVersionChange={handleVersionChange}
|
||||
></ModelEvolution>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
const infoTypePropertyName = config.infoTypePropertyName as keyof ResourceData;
|
||||
const infoTagPropertyName = config.infoTagPropertyName as keyof ResourceData;
|
||||
|
||||
return (
|
||||
<div className={styles['resource-intro']}>
|
||||
<div className={styles['resource-intro__top']}>
|
||||
<div className={styles['resource-intro__top__name']}>{info.name}</div>
|
||||
<Flex align="center">
|
||||
<div className={styles['resource-intro__top__tag']}>
|
||||
{typeName} id:{info.id}
|
||||
</div>
|
||||
{info[infoTypePropertyName] && (
|
||||
<div className={styles['resource-intro__top__tag']}>
|
||||
{info[infoTypePropertyName] || '--'}
|
||||
</div>
|
||||
)}
|
||||
{info[infoTagPropertyName] && (
|
||||
<div className={styles['resource-intro__top__tag']}>
|
||||
{info[infoTagPropertyName] || '--'}
|
||||
</div>
|
||||
)}
|
||||
</Flex>
|
||||
</div>
|
||||
<div className={styles['resource-intro__bottom']}>
|
||||
<Tabs activeKey={activeTab} items={items} onChange={(key) => setActiveTab(key)}></Tabs>
|
||||
<SubAreaTitle
|
||||
title="基本信息"
|
||||
image={require('@/assets/img/mirror-basic.png')}
|
||||
style={{ marginBottom: '26px' }}
|
||||
></SubAreaTitle>
|
||||
<div className={styles['resource-intro__basic']}>
|
||||
<BasicInfo datas={basicDatas}></BasicInfo>
|
||||
</div>
|
||||
<SubAreaTitle
|
||||
title="实例用法"
|
||||
image={require('@/assets/img/usage-icon.png')}
|
||||
style={{ margin: '40px 0 24px' }}
|
||||
></SubAreaTitle>
|
||||
<div
|
||||
className={styles['resource-intro__usage']}
|
||||
dangerouslySetInnerHTML={{ __html: info.usage ?? '暂无实例用法' }}
|
||||
></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default ResourceIntro;
|
||||
|
|
|
@ -44,7 +44,10 @@ function ResourceItem({ item, isPublic, onClick, onRemove }: ResourceItemProps)
|
|||
</div>
|
||||
<div className={styles['resource-item__time']}>
|
||||
<img style={{ width: '12px', marginRight: '5px' }} src={clock} alt="" />
|
||||
<span>最近更新: {formatDate(item.update_time, 'YYYY-MM-DD')}</span>
|
||||
<span>
|
||||
{'最近更新: '}
|
||||
{item.update_time ? formatDate(item.update_time, 'YYYY-MM-DD') : item.time_ago ?? ''}
|
||||
</span>
|
||||
</div>
|
||||
</Flex>
|
||||
</div>
|
||||
|
|
|
@ -36,4 +36,8 @@
|
|||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
&__empty {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import KFEmpty, { EmptyType } from '@/components/KFEmpty';
|
||||
import KFIcon from '@/components/KFIcon';
|
||||
import { CommonTabKeys } from '@/enums';
|
||||
import AddModelModal from '@/pages/Dataset/components/AddModelModal';
|
||||
import { openAntdModal } from '@/utils/modal';
|
||||
import { to } from '@/utils/promise';
|
||||
import { resourceItemKey, setSessionStorageItem } from '@/utils/sessionStorage';
|
||||
import { modalConfirm } from '@/utils/ui';
|
||||
import { useNavigate } from '@umijs/max';
|
||||
import { App, Button, Input, Pagination, PaginationProps } from 'antd';
|
||||
import { pick } from 'lodash';
|
||||
import { Ref, forwardRef, useEffect, useImperativeHandle, useState } from 'react';
|
||||
import { CategoryData, ResourceData, ResourceType, resourceConfig } from '../../config';
|
||||
import AddDatasetModal from '../AddDatasetModal';
|
||||
|
@ -43,7 +46,7 @@ function ResourceList(
|
|||
ref: Ref<ResourceListRef>,
|
||||
) {
|
||||
const navigate = useNavigate();
|
||||
const [dataList, setDataList] = useState<ResourceData[]>([]);
|
||||
const [dataList, setDataList] = useState<ResourceData[] | undefined>(undefined);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [pagination, setPagination] = useState<PaginationProps>(
|
||||
initialPagination ?? {
|
||||
|
@ -71,7 +74,8 @@ function ResourceList(
|
|||
});
|
||||
setSearchText('');
|
||||
setInputText('');
|
||||
setDataList([]);
|
||||
setDataList(undefined);
|
||||
setTotal(0);
|
||||
},
|
||||
};
|
||||
},
|
||||
|
@ -80,26 +84,33 @@ function ResourceList(
|
|||
|
||||
// 获取数据请求
|
||||
const getDataList = async () => {
|
||||
const params = {
|
||||
const params: Record<string, any> = {
|
||||
page: pagination.current! - 1,
|
||||
size: pagination.pageSize,
|
||||
[config.typeParamKey]: dataType,
|
||||
[config.tagParamKey]: dataTag,
|
||||
available_range: isPublic ? 1 : 0,
|
||||
name: searchText !== '' ? searchText : undefined,
|
||||
};
|
||||
if (resourceType === ResourceType.Dataset) {
|
||||
params['is_public'] = isPublic;
|
||||
} else {
|
||||
params['available_range'] = isPublic ? 1 : 0;
|
||||
}
|
||||
const request = config.getList;
|
||||
const [res] = await to(request(params));
|
||||
if (res && res.data && res.data.content) {
|
||||
setDataList(res.data.content);
|
||||
setTotal(res.data.totalElements);
|
||||
} else {
|
||||
setDataList([]);
|
||||
setTotal(0);
|
||||
}
|
||||
};
|
||||
|
||||
// 删除请求
|
||||
const deleteRecord = async (id: number) => {
|
||||
const deleteRecord = async (params: { owner: string; identifier: string }) => {
|
||||
const request = config.deleteRecord;
|
||||
const [res] = await to(request(id));
|
||||
const [res] = await to(request(params));
|
||||
if (res) {
|
||||
getDataList();
|
||||
message.success('删除成功');
|
||||
|
@ -116,7 +127,7 @@ function ResourceList(
|
|||
modalConfirm({
|
||||
title: config.deleteModalTitle,
|
||||
onOk: () => {
|
||||
deleteRecord(record.id);
|
||||
deleteRecord(pick(record, ['owner', 'identifier']));
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@ -131,6 +142,7 @@ function ResourceList(
|
|||
activeTag: dataTag,
|
||||
});
|
||||
const prefix = config.prefix;
|
||||
setSessionStorageItem(resourceItemKey, record, true);
|
||||
navigate(`/dataset/${prefix}/info/${record.id}`);
|
||||
};
|
||||
|
||||
|
@ -158,7 +170,7 @@ function ResourceList(
|
|||
return (
|
||||
<div className={styles['resource-list']}>
|
||||
<div className={styles['resource-list__header']}>
|
||||
<span>数据总数:{total}个</span>
|
||||
<span>数据总数:{total} 个</span>
|
||||
<div>
|
||||
<Input.Search
|
||||
placeholder={`按${config.name}名称筛选`}
|
||||
|
@ -182,26 +194,37 @@ function ResourceList(
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles['resource-list__content']}>
|
||||
{dataList?.map((item) => (
|
||||
<ResourceItem
|
||||
item={item}
|
||||
key={item.id}
|
||||
isPublic={isPublic}
|
||||
onRemove={handleRemove}
|
||||
onClick={handleClick}
|
||||
></ResourceItem>
|
||||
))}
|
||||
</div>
|
||||
<Pagination
|
||||
total={total}
|
||||
showSizeChanger
|
||||
defaultPageSize={20}
|
||||
pageSizeOptions={[20, 40, 60, 80, 100]}
|
||||
showQuickJumper
|
||||
onChange={handlePageChange}
|
||||
{...pagination}
|
||||
/>
|
||||
{dataList && dataList.length > 0 && (
|
||||
<>
|
||||
<div className={styles['resource-list__content']}>
|
||||
{dataList?.map((item) => (
|
||||
<ResourceItem
|
||||
item={item}
|
||||
key={item.id}
|
||||
isPublic={isPublic}
|
||||
onRemove={handleRemove}
|
||||
onClick={handleClick}
|
||||
></ResourceItem>
|
||||
))}
|
||||
</div>
|
||||
<Pagination
|
||||
total={total}
|
||||
showSizeChanger
|
||||
defaultPageSize={20}
|
||||
pageSizeOptions={[20, 40, 60, 80, 100]}
|
||||
showQuickJumper
|
||||
onChange={handlePageChange}
|
||||
{...pagination}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{dataList && dataList.length === 0 && (
|
||||
<KFEmpty
|
||||
className={styles['resource-list__empty']}
|
||||
type={EmptyType.NoData}
|
||||
title="暂无数据"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,125 +1,35 @@
|
|||
import CommonTableCell from '@/components/CommonTableCell';
|
||||
import DateTableCell from '@/components/DateTableCell';
|
||||
import KFIcon from '@/components/KFIcon';
|
||||
import { useEffectWhen } from '@/hooks';
|
||||
import AddVersionModal from '@/pages/Dataset/components/AddVersionModal';
|
||||
import {
|
||||
ResourceData,
|
||||
ResourceFileData,
|
||||
ResourceType,
|
||||
ResourceVersionData,
|
||||
resourceConfig,
|
||||
} from '@/pages/Dataset/config';
|
||||
import { downLoadZip } from '@/utils/downloadfile';
|
||||
import { openAntdModal } from '@/utils/modal';
|
||||
import { to } from '@/utils/promise';
|
||||
import { modalConfirm } from '@/utils/ui';
|
||||
import { App, Button, Flex, Select, Table } from 'antd';
|
||||
import { useState } from 'react';
|
||||
import { Button, Flex, Table } from 'antd';
|
||||
import styles from './index.less';
|
||||
|
||||
type ResourceVersionProps = {
|
||||
resourceType: ResourceType;
|
||||
resourceId: number;
|
||||
resourceName: string;
|
||||
isPublic: boolean;
|
||||
versionList: ResourceVersionData[];
|
||||
version?: string;
|
||||
isActive: boolean;
|
||||
getVersionList: () => void;
|
||||
onVersionChange: (version: string) => void;
|
||||
info: ResourceData;
|
||||
};
|
||||
function ResourceVersion({
|
||||
resourceType,
|
||||
resourceId,
|
||||
resourceName,
|
||||
isPublic,
|
||||
versionList,
|
||||
version,
|
||||
isActive,
|
||||
getVersionList,
|
||||
onVersionChange,
|
||||
}: ResourceVersionProps) {
|
||||
const [fileList, setFileList] = useState<ResourceFileData[]>([]);
|
||||
const { message } = App.useApp();
|
||||
function ResourceVersion({ resourceType, info }: ResourceVersionProps) {
|
||||
const config = resourceConfig[resourceType];
|
||||
|
||||
// 获取版本文件列表
|
||||
useEffectWhen(
|
||||
() => {
|
||||
if (version) {
|
||||
getFileList(version);
|
||||
} else {
|
||||
setFileList([]);
|
||||
}
|
||||
},
|
||||
[resourceId, version],
|
||||
isActive,
|
||||
);
|
||||
|
||||
// 获取版本下的文件列表
|
||||
const getFileList = async (version: string) => {
|
||||
const params = {
|
||||
version,
|
||||
[config.fileReqParamKey]: resourceId,
|
||||
};
|
||||
const request = config.getFiles;
|
||||
const [res] = await to(request(params));
|
||||
if (res) {
|
||||
setFileList(res?.data?.content ?? []);
|
||||
}
|
||||
};
|
||||
|
||||
// 删除版本
|
||||
const deleteVersion = async () => {
|
||||
const request = config.deleteVersion;
|
||||
const params = {
|
||||
[config.idParamKey]: resourceId,
|
||||
version,
|
||||
};
|
||||
const [res] = await to(request(params));
|
||||
if (res) {
|
||||
getVersionList();
|
||||
message.success('删除成功');
|
||||
}
|
||||
};
|
||||
|
||||
// 新建版本
|
||||
const showModal = () => {
|
||||
const { close } = openAntdModal(AddVersionModal, {
|
||||
resourceType: resourceType,
|
||||
resourceId: resourceId,
|
||||
initialName: resourceName,
|
||||
onOk: () => {
|
||||
getVersionList();
|
||||
close();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 处理删除
|
||||
const hanldeDelete = () => {
|
||||
modalConfirm({
|
||||
title: '删除后,该版本将不可恢复',
|
||||
content: '是否确认删除?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
|
||||
onOk: () => {
|
||||
deleteVersion();
|
||||
},
|
||||
});
|
||||
};
|
||||
const fileList = info.dataset_version_vos ?? [];
|
||||
fileList.forEach((item) => (item.update_time = info.update_time));
|
||||
|
||||
// 全部导出
|
||||
const handleExport = async () => {
|
||||
const url = config.downloadAllAction;
|
||||
downLoadZip(url, { models_id: resourceId, version });
|
||||
downLoadZip(url, { name: info.name, id: info.id, version: info.version });
|
||||
};
|
||||
|
||||
// 单个导出
|
||||
const downloadAlone = (record: ResourceFileData) => {
|
||||
const downloadAlone = async (record: ResourceFileData) => {
|
||||
const url = config.downloadSingleAction;
|
||||
downLoadZip(`${url}/${record.id}`);
|
||||
downLoadZip(url, { url: record.url });
|
||||
};
|
||||
|
||||
const columns = [
|
||||
|
@ -142,12 +52,6 @@ function ResourceVersion({
|
|||
</a>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '版本号',
|
||||
dataIndex: 'version',
|
||||
key: 'version',
|
||||
render: CommonTableCell(),
|
||||
},
|
||||
{
|
||||
title: '文件大小',
|
||||
dataIndex: 'file_size',
|
||||
|
@ -163,7 +67,7 @@ function ResourceVersion({
|
|||
{
|
||||
title: '操作',
|
||||
dataIndex: 'option',
|
||||
width: '100px',
|
||||
width: 160,
|
||||
key: 'option',
|
||||
render: (_: any, record: ResourceFileData) => [
|
||||
<Button
|
||||
|
@ -183,32 +87,9 @@ function ResourceVersion({
|
|||
<div className={styles['resource-version']}>
|
||||
<Flex justify="space-between" align="center" style={{ margin: '30px 0' }}>
|
||||
<Flex align="center">
|
||||
<span style={{ marginRight: '10px' }}>版本号:</span>
|
||||
<Select
|
||||
placeholder="请选择版本号"
|
||||
style={{ width: '160px', marginRight: '20px' }}
|
||||
value={version}
|
||||
onChange={onVersionChange}
|
||||
options={versionList}
|
||||
/>
|
||||
<Button type="default" onClick={showModal} icon={<KFIcon type="icon-xinjian2" />}>
|
||||
创建新版本
|
||||
</Button>
|
||||
</Flex>
|
||||
<Flex align="center">
|
||||
{!isPublic && (
|
||||
<Button
|
||||
type="default"
|
||||
style={{ marginRight: '20px' }}
|
||||
onClick={hanldeDelete}
|
||||
icon={<KFIcon type="icon-shanchu" />}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type="default"
|
||||
disabled={!version}
|
||||
disabled={fileList.length === 0}
|
||||
onClick={handleExport}
|
||||
icon={<KFIcon type="icon-xiazai" />}
|
||||
>
|
||||
|
@ -216,11 +97,6 @@ function ResourceVersion({
|
|||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<div style={{ marginBottom: '30px', fontSize: '15px' }}>
|
||||
{fileList.length > 0 && fileList[0].description
|
||||
? '版本描述:' + fileList[0].description
|
||||
: null}
|
||||
</div>
|
||||
<Table columns={columns} dataSource={fileList} pagination={false} rowKey="id" />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
import KFIcon from '@/components/KFIcon';
|
||||
import { CommonTabKeys } from '@/enums';
|
||||
import {
|
||||
addDatasetVersionDetail,
|
||||
addDatasetVersion,
|
||||
addModelsVersionDetail,
|
||||
deleteDataset,
|
||||
deleteDatasetVersion,
|
||||
deleteModel,
|
||||
deleteModelVersion,
|
||||
getDatasetById,
|
||||
getDatasetInfo,
|
||||
getDatasetList,
|
||||
getDatasetVersionIdList,
|
||||
getDatasetVersionsById,
|
||||
getDatasetVersionList,
|
||||
getModelById,
|
||||
getModelList,
|
||||
getModelVersionIdList,
|
||||
getModelVersionsById,
|
||||
} from '@/services/dataset/index.js';
|
||||
import type { TabsProps } from 'antd';
|
||||
|
@ -26,7 +24,6 @@ export enum ResourceType {
|
|||
type ResourceTypeInfo = {
|
||||
getList: (params: any) => Promise<any>; // 获取资源列表
|
||||
getVersions: (params: any) => Promise<any>; // 获取版本列表
|
||||
getFiles: (params: any) => Promise<any>; // 获取版本下的文件列表
|
||||
deleteRecord: (params: any) => Promise<any>; // 删除
|
||||
addVersion: (params: any) => Promise<any>; // 新增版本
|
||||
deleteVersion: (params: any) => Promise<any>; // 删除版本
|
||||
|
@ -55,12 +52,11 @@ type ResourceTypeInfo = {
|
|||
export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = {
|
||||
[ResourceType.Dataset]: {
|
||||
getList: getDatasetList,
|
||||
getVersions: getDatasetVersionsById,
|
||||
getFiles: getDatasetVersionIdList,
|
||||
getVersions: getDatasetVersionList,
|
||||
deleteRecord: deleteDataset,
|
||||
addVersion: addDatasetVersionDetail,
|
||||
addVersion: addDatasetVersion,
|
||||
deleteVersion: deleteDatasetVersion,
|
||||
getInfo: getDatasetById,
|
||||
getInfo: getDatasetInfo,
|
||||
name: '数据集',
|
||||
typeParamKey: 'data_type',
|
||||
tagParamKey: 'data_tag',
|
||||
|
@ -85,17 +81,16 @@ export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = {
|
|||
deleteModalTitle: '确定删除该条数据集实例吗?',
|
||||
addBtnTitle: '新建数据集',
|
||||
idParamKey: 'dataset_id',
|
||||
uploadAction: '/api/mmp/dataset/upload',
|
||||
uploadAction: '/api/mmp/newdataset/upload',
|
||||
uploadAccept: '.zip,.tgz',
|
||||
downloadAllAction: '/api/mmp/dataset/downloadAllFilesl',
|
||||
downloadSingleAction: '/api/mmp/dataset/download',
|
||||
infoTypePropertyName: 'dataset_type_name',
|
||||
infoTagPropertyName: 'dataset_tag_name',
|
||||
downloadAllAction: '/api/mmp/newdataset/downloadAllFiles',
|
||||
downloadSingleAction: '/api/mmp/newdataset/downloadSinggerFile',
|
||||
infoTypePropertyName: 'data_type',
|
||||
infoTagPropertyName: 'data_tag',
|
||||
},
|
||||
[ResourceType.Model]: {
|
||||
getList: getModelList,
|
||||
getVersions: getModelVersionsById,
|
||||
getFiles: getModelVersionIdList,
|
||||
deleteRecord: deleteModel,
|
||||
addVersion: addModelsVersionDetail,
|
||||
deleteVersion: deleteModelVersion,
|
||||
|
@ -145,32 +140,37 @@ export type CategoryData = {
|
|||
export type ResourceData = {
|
||||
id: number;
|
||||
name: string;
|
||||
identifier: string;
|
||||
description: string;
|
||||
create_by: string;
|
||||
owner: string;
|
||||
update_time: string;
|
||||
available_range: number;
|
||||
time_ago: string;
|
||||
is_public: boolean;
|
||||
model_type_name?: string;
|
||||
model_tag_name?: string;
|
||||
dataset_type_name?: string;
|
||||
dataset_tag_name?: string;
|
||||
data_type?: string;
|
||||
data_tag?: string;
|
||||
version?: string;
|
||||
version_desc?: string;
|
||||
processing_code?: string;
|
||||
dataset_source?: string;
|
||||
usage?: string;
|
||||
dataset_version_vos: ResourceFileData[];
|
||||
};
|
||||
|
||||
// 版本数据
|
||||
export type ResourceVersionData = {
|
||||
label: string;
|
||||
value: string;
|
||||
name: string;
|
||||
http_url: string;
|
||||
tar_url: string;
|
||||
zip_url: string;
|
||||
};
|
||||
|
||||
// 版本文件数据
|
||||
export type ResourceFileData = {
|
||||
id: number;
|
||||
file_name: string;
|
||||
file_size: string;
|
||||
description: string;
|
||||
create_by: string;
|
||||
create_time: string;
|
||||
update_by: string;
|
||||
update_time: string;
|
||||
url: string;
|
||||
version: string;
|
||||
update_time?: string;
|
||||
};
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import ResourceIntro from '@/pages/Dataset/components/ResourceIntro';
|
||||
import ResourceInfo from '@/pages/Dataset/components/ResourceInfo';
|
||||
import { ResourceType } from '@/pages/Dataset/config';
|
||||
|
||||
function DatasetIntro() {
|
||||
return <ResourceIntro resourceType={ResourceType.Dataset} />;
|
||||
function DatasetInfo() {
|
||||
return <ResourceInfo resourceType={ResourceType.Dataset} />;
|
||||
}
|
||||
|
||||
export default DatasetIntro;
|
||||
export default DatasetInfo;
|
||||
|
|
|
@ -8,11 +8,11 @@ import KFRadio, { type KFRadioItem } from '@/components/KFRadio';
|
|||
import PageTitle from '@/components/PageTitle';
|
||||
import ResourceSelect, {
|
||||
requiredValidator,
|
||||
ResourceSelectorType,
|
||||
type ParameterInputObject,
|
||||
} from '@/components/ResourceSelect';
|
||||
import SubAreaTitle from '@/components/SubAreaTitle';
|
||||
import { useComputingResource } from '@/hooks/resource';
|
||||
import { ResourceSelectorType } from '@/pages/Pipeline/components/ResourceSelectorModal';
|
||||
import { createEditorReq } from '@/services/developmentEnvironment';
|
||||
import { to } from '@/utils/promise';
|
||||
import { useNavigate } from '@umijs/max';
|
||||
|
@ -90,7 +90,6 @@ function EditorCreate() {
|
|||
<Form
|
||||
name="editor-create"
|
||||
labelCol={{ flex: '100px' }}
|
||||
wrapperCol={{ flex: 1 }}
|
||||
labelAlign="left"
|
||||
form={form}
|
||||
initialValues={{ computing_resource: ComputingResourceType.GPU }}
|
||||
|
|
|
@ -8,6 +8,7 @@ import DateTableCell from '@/components/DateTableCell';
|
|||
import KFIcon from '@/components/KFIcon';
|
||||
import PageTitle from '@/components/PageTitle';
|
||||
import SubAreaTitle from '@/components/SubAreaTitle';
|
||||
import { MirrorVersionStatus } from '@/enums';
|
||||
import { useDomSize } from '@/hooks';
|
||||
import { useCacheState } from '@/hooks/pageCacheState';
|
||||
import {
|
||||
|
@ -36,7 +37,7 @@ import { useEffect, useMemo, useState } from 'react';
|
|||
import MirrorStatusCell from '../components/MirrorStatusCell';
|
||||
import styles from './index.less';
|
||||
|
||||
type MirrorInfoData = {
|
||||
export type MirrorInfoData = {
|
||||
name?: string;
|
||||
description?: string;
|
||||
version_count?: string;
|
||||
|
@ -44,13 +45,14 @@ type MirrorInfoData = {
|
|||
image_type?: number;
|
||||
};
|
||||
|
||||
type MirrorVersionData = {
|
||||
export type MirrorVersionData = {
|
||||
id: number;
|
||||
version: string;
|
||||
url: string;
|
||||
status: string;
|
||||
status: MirrorVersionStatus;
|
||||
file_size: string;
|
||||
create_time: string;
|
||||
tag_name: string;
|
||||
};
|
||||
|
||||
function MirrorInfo() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ResourceInfoTabKeys } from '@/pages/Dataset/components/ResourceIntro';
|
||||
import { ResourceInfoTabKeys } from '@/pages/Dataset/components/ResourceInfo';
|
||||
import { formatDate } from '@/utils/date';
|
||||
import { useNavigate } from '@umijs/max';
|
||||
import { ModelDepsData, NodeType, ProjectDependency, TrainDataset } from '../ModelEvolution/utils';
|
||||
|
|
|
@ -7,12 +7,12 @@ import KFIcon from '@/components/KFIcon';
|
|||
import PageTitle from '@/components/PageTitle';
|
||||
import ResourceSelect, {
|
||||
requiredValidator,
|
||||
ResourceSelectorType,
|
||||
type ParameterInputObject,
|
||||
} from '@/components/ResourceSelect';
|
||||
import SubAreaTitle from '@/components/SubAreaTitle';
|
||||
import { CommonTabKeys } from '@/enums';
|
||||
import { useComputingResource } from '@/hooks/resource';
|
||||
import { ResourceSelectorType } from '@/pages/Pipeline/components/ResourceSelectorModal';
|
||||
import {
|
||||
createModelDeploymentReq,
|
||||
restartModelDeploymentReq,
|
||||
|
|
|
@ -67,7 +67,7 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) {
|
|||
<KFModal
|
||||
{...rest}
|
||||
title="选择代码配置"
|
||||
image={require('@/assets/img/edit-experiment.png')}
|
||||
image={require('@/assets/img/modal-code-config.png')}
|
||||
width={920}
|
||||
footer={null}
|
||||
destroyOnClose
|
||||
|
|
|
@ -245,6 +245,10 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete
|
|||
// 获取选择数据集、模型后面按钮 icon
|
||||
const getSelectBtnIcon = (item: { item_type: string }) => {
|
||||
const type = item.item_type;
|
||||
if (type === 'code') {
|
||||
return <KFIcon type="icon-xuanzedaimapeizhi" />;
|
||||
}
|
||||
|
||||
let selectorType: ResourceSelectorType;
|
||||
if (type === 'dataset') {
|
||||
selectorType = ResourceSelectorType.Dataset;
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
import datasetImg from '@/assets/img/modal-select-dataset.png';
|
||||
import mirrorImg from '@/assets/img/modal-select-mirror.png';
|
||||
import modelImg from '@/assets/img/modal-select-model.png';
|
||||
import { CommonTabKeys, MirrorVersionStatus } from '@/enums';
|
||||
import { AvailableRange, CommonTabKeys } from '@/enums';
|
||||
import { ResourceData, ResourceVersionData } from '@/pages/Dataset/config';
|
||||
import { MirrorVersionData } from '@/pages/Mirror/Info';
|
||||
import { MirrorData } from '@/pages/Mirror/List';
|
||||
import {
|
||||
getDatasetInfo,
|
||||
getDatasetList,
|
||||
getDatasetVersionIdList,
|
||||
getDatasetVersionsById,
|
||||
getDatasetVersionList,
|
||||
getModelList,
|
||||
getModelVersionIdList,
|
||||
getModelVersionsById,
|
||||
} from '@/services/dataset/index.js';
|
||||
import { getMirrorListReq, getMirrorVersionListReq } from '@/services/mirror';
|
||||
import type { TabsProps } from 'antd';
|
||||
import type { TabsProps, TreeDataNode } from 'antd';
|
||||
import { pick } from 'lodash';
|
||||
|
||||
export enum ResourceSelectorType {
|
||||
Model = 'Model', // 模型
|
||||
|
@ -19,111 +23,347 @@ export enum ResourceSelectorType {
|
|||
Mirror = 'Mirror', //镜像
|
||||
}
|
||||
|
||||
export type MirrorVersion = {
|
||||
id: number; // 镜像版本 id
|
||||
status: MirrorVersionStatus; // 镜像版本状态
|
||||
tag_name: string; // 镜像版本 name
|
||||
url: string; // 镜像版本路径
|
||||
// 数据集、模型列表转为树形结构
|
||||
const convertDatasetToTreeData = (list: ResourceData[]): TreeDataNode[] => {
|
||||
return list.map((v) => ({
|
||||
...v,
|
||||
key: `${v.id}`,
|
||||
title: v.name,
|
||||
isLeaf: false,
|
||||
checkable: false,
|
||||
}));
|
||||
};
|
||||
|
||||
export type SelectorTypeInfo = {
|
||||
getList: (params: any) => Promise<any>; // 获取资源列表
|
||||
getVersions: (params: any) => Promise<any>; // 获取资源版本列表
|
||||
getFiles: (params: any) => Promise<any>; // 获取资源文件列表
|
||||
handleVersionResponse: (res: any) => any[]; // 处理版本列表接口数据
|
||||
modalIcon: string; // modal icon
|
||||
buttonIcon: string; // button icon
|
||||
name: string; // 名称
|
||||
litReqParamKey: 'available_range' | 'image_type'; // 表示是公开还是私有的参数名称,获取资源列表接口使用
|
||||
fileReqParamKey: 'models_id' | 'dataset_id'; // 文件请求参数名称,获取文件列表接口使用
|
||||
tabItems: TabsProps['items']; // tab 列表
|
||||
buttontTitle: string; // 按钮 title
|
||||
// 镜像列表转为树形结构
|
||||
const convertMirrorToTreeData = (list: MirrorData[]): TreeDataNode[] => {
|
||||
return list.map((v) => ({
|
||||
key: `${v.id}`,
|
||||
title: v.name,
|
||||
isLeaf: false,
|
||||
checkable: false,
|
||||
}));
|
||||
};
|
||||
|
||||
// 获取镜像文件列表,为了兼容数据集和模型
|
||||
const getMirrorFilesReq = ({ id, version }: { id: number; version: string }): Promise<any> => {
|
||||
const index = version.indexOf('-');
|
||||
const url = version.slice(index + 1);
|
||||
return Promise.resolve({
|
||||
data: {
|
||||
// 数据集版本列表转为树形结构
|
||||
const convertDatasetVersionToTreeData = (
|
||||
parentId: string,
|
||||
info: ResourceData,
|
||||
list: ResourceVersionData[],
|
||||
): TreeDataNode[] => {
|
||||
return list.map((item: ResourceVersionData) => ({
|
||||
...pick(info, ['id', 'name', 'owner', 'identifier']),
|
||||
version: item.name,
|
||||
title: item.name,
|
||||
key: `${parentId}-${item.name}`,
|
||||
isLeaf: true,
|
||||
checkable: true,
|
||||
}));
|
||||
};
|
||||
|
||||
// 镜像版本列表转为树形结构
|
||||
const convertMirrorVersionToTreeData = (
|
||||
parentId: string,
|
||||
list: MirrorVersionData[],
|
||||
): TreeDataNode[] => {
|
||||
return list.map((item: MirrorVersionData) => ({
|
||||
url: item.url,
|
||||
title: item.tag_name,
|
||||
key: `${parentId}-${item.id}`,
|
||||
isLeaf: true,
|
||||
checkable: true,
|
||||
}));
|
||||
};
|
||||
|
||||
// 从树形数据节点 id 中获取数据集版本列表的参数
|
||||
// const parseDatasetVersionId = (id: string) => {
|
||||
// const list = id.split('-');
|
||||
// return {
|
||||
// id: Number(list[0]),
|
||||
// name: list[1],
|
||||
// owner: list[2],
|
||||
// identifier: list[3],
|
||||
// version: list[4],
|
||||
// };
|
||||
// };
|
||||
|
||||
// 从树形数据节点 id 中获取数据集版本列表的参数
|
||||
// const parseMirrorVersionId = (id: string) => {
|
||||
// const list = id.split('-');
|
||||
// return {
|
||||
// parentId: Number(list[0]),
|
||||
// id: list[1],
|
||||
// url: list[2],
|
||||
// };
|
||||
// };
|
||||
|
||||
// export type MirrorVersion = {
|
||||
// id: number; // 镜像版本 id
|
||||
// status: MirrorVersionStatus; // 镜像版本状态
|
||||
// tag_name: string; // 镜像版本 name
|
||||
// url: string; // 镜像版本路径
|
||||
// };
|
||||
|
||||
// export type SelectorTypeInfo = {
|
||||
// getList: (params: any) => Promise<any>; // 获取资源列表
|
||||
// getVersions: (params: any) => Promise<any>; // 获取资源版本列表
|
||||
// getFiles: (params: any) => Promise<any>; // 获取资源文件列表
|
||||
// handleVersionResponse: (res: any) => any[]; // 处理版本列表接口数据
|
||||
// dataToTreeData: (data: any) => TreeDataNode[]; // 数据转树形结构
|
||||
// parseTreeNodeId: (id: string) => any; // 获取版本列表请求参数
|
||||
// modalIcon: string; // modal icon
|
||||
// buttonIcon: string; // button icon
|
||||
// name: string; // 名称
|
||||
// litReqParamKey: 'available_range' | 'image_type'; // 表示是公开还是私有的参数名称,获取资源列表接口使用
|
||||
// tabItems: TabsProps['items']; // tab 列表
|
||||
// buttontTitle: string; // 按钮 title
|
||||
// };
|
||||
|
||||
// export const selectorTypeConfig: Record<ResourceSelectorType, SelectorTypeInfo> = {
|
||||
// [ResourceSelectorType.Model]: {
|
||||
// getList: getModelList,
|
||||
// getVersions: getModelVersionsById,
|
||||
// getFiles: getModelVersionIdList,
|
||||
|
||||
// name: '模型',
|
||||
// modalIcon: modelImg,
|
||||
// buttonIcon: 'icon-xuanzemoxing',
|
||||
// litReqParamKey: 'available_range',
|
||||
// tabItems: [
|
||||
// {
|
||||
// key: CommonTabKeys.Private,
|
||||
// label: '我的模型',
|
||||
// },
|
||||
// {
|
||||
// key: CommonTabKeys.Public,
|
||||
// label: '公开模型',
|
||||
// },
|
||||
// ],
|
||||
// buttontTitle: '选择模型',
|
||||
// },
|
||||
// [ResourceSelectorType.Dataset]: {
|
||||
// getList: getDatasetList,
|
||||
// getVersions: getDatasetVersionList,
|
||||
// getFiles: getDatasetInfo,
|
||||
|
||||
// name: '数据集',
|
||||
// modalIcon: datasetImg,
|
||||
// buttonIcon: 'icon-xuanzeshujuji',
|
||||
// litReqParamKey: 'available_range',
|
||||
// tabItems: [
|
||||
// {
|
||||
// key: CommonTabKeys.Private,
|
||||
// label: '我的数据集',
|
||||
// },
|
||||
// {
|
||||
// key: CommonTabKeys.Public,
|
||||
// label: '公开数据集',
|
||||
// },
|
||||
// ],
|
||||
// buttontTitle: '选择数据集',
|
||||
// },
|
||||
// [ResourceSelectorType.Mirror]: {
|
||||
// getList: getMirrorListReq,
|
||||
// getVersions: (id: number) => getMirrorVersionListReq({ image_id: id, page: 0, size: 200 }),
|
||||
// getFiles: getMirrorFilesReq,
|
||||
// handleVersionResponse: (res) =>
|
||||
// res.data?.content?.filter(
|
||||
// (v: MirrorVersionData) => v.status === MirrorVersionStatus.Available,
|
||||
// ) || [],
|
||||
// dataToTreeData: convertMirrorToTreeData,
|
||||
// parseTreeNodeId: (id: string) => id,
|
||||
// name: '镜像',
|
||||
// modalIcon: mirrorImg,
|
||||
// buttonIcon: 'icon-xuanzejingxiang',
|
||||
// litReqParamKey: 'image_type',
|
||||
// tabItems: [
|
||||
// {
|
||||
// key: CommonTabKeys.Private,
|
||||
// label: '我的镜像',
|
||||
// },
|
||||
// {
|
||||
// key: CommonTabKeys.Public,
|
||||
// label: '公开镜像',
|
||||
// },
|
||||
// ],
|
||||
// buttontTitle: '选择镜像',
|
||||
// },
|
||||
// };
|
||||
|
||||
interface SelectorTypeInfo {
|
||||
getList: (isPublic: boolean) => Promise<any>; // 获取资源列表
|
||||
getVersions: (parentKey: string, parentNode: any) => Promise<any>; // 获取资源版本列表
|
||||
getFiles: (parentKey: string, parentNode: any) => Promise<any>; // 获取资源文件列表
|
||||
readonly modalIcon: string; // modal icon
|
||||
readonly buttonIcon: string; // button icon
|
||||
readonly name: string; // 名称
|
||||
readonly tabItems: TabsProps['items']; // tab 列表
|
||||
readonly buttontTitle: string; // 按钮 title
|
||||
}
|
||||
|
||||
export class DatasetSelector implements SelectorTypeInfo {
|
||||
readonly name = '数据集';
|
||||
readonly modalIcon = datasetImg;
|
||||
readonly buttonIcon = 'icon-xuanzeshujuji';
|
||||
readonly tabItems = [
|
||||
{
|
||||
key: CommonTabKeys.Private,
|
||||
label: '我的数据集',
|
||||
},
|
||||
{
|
||||
key: CommonTabKeys.Public,
|
||||
label: '公开数据集',
|
||||
},
|
||||
];
|
||||
readonly buttontTitle = '选择数据集';
|
||||
|
||||
async getList(isPublic: boolean) {
|
||||
const res = await getDatasetList({ is_public: isPublic, page: 0, size: 2000 });
|
||||
if (res && res.data) {
|
||||
const list = res.data.content || [];
|
||||
return convertDatasetToTreeData(list);
|
||||
} else {
|
||||
return Promise.reject('获取数据集列表失败');
|
||||
}
|
||||
}
|
||||
async getVersions(parentKey: string, parentNode: ResourceData) {
|
||||
// const obj = parseDatasetVersionId(id);
|
||||
const res = await getDatasetVersionList(pick(parentNode, ['owner', 'identifier']));
|
||||
if (res && res.data) {
|
||||
const list = res.data;
|
||||
return convertDatasetVersionToTreeData(parentKey, parentNode, list);
|
||||
} else {
|
||||
return Promise.reject('获取数据集版本列表失败');
|
||||
}
|
||||
}
|
||||
|
||||
async getFiles(_parentKey: string, parentNode: ResourceData & ResourceVersionData) {
|
||||
//const obj = parseDatasetVersionId(parentKey);
|
||||
const params = pick(parentNode, ['owner', 'identifier', 'id', 'name', 'version']);
|
||||
const res = await getDatasetInfo(params);
|
||||
if (res && res.data) {
|
||||
const path = res.data.relative_paths || '';
|
||||
const list = res.data.dataset_version_vos || [];
|
||||
return {
|
||||
path,
|
||||
content: list,
|
||||
};
|
||||
} else {
|
||||
return Promise.reject('获取数据集文件列表失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ModelSelector implements SelectorTypeInfo {
|
||||
readonly name = '模型';
|
||||
readonly modalIcon = modelImg;
|
||||
readonly buttonIcon = 'icon-xuanzemoxing';
|
||||
readonly tabItems = [
|
||||
{
|
||||
key: CommonTabKeys.Private,
|
||||
label: '我的模型',
|
||||
},
|
||||
{
|
||||
key: CommonTabKeys.Public,
|
||||
label: '公开模型',
|
||||
},
|
||||
];
|
||||
readonly buttontTitle = '选择模型';
|
||||
|
||||
async getList(isPublic: boolean) {
|
||||
const res = await getModelList({ is_public: isPublic, page: 0, size: 2000 });
|
||||
if (res && res.data) {
|
||||
const list = res.data.content || [];
|
||||
return convertDatasetToTreeData(list);
|
||||
} else {
|
||||
return Promise.reject('获取数据集列表失败');
|
||||
}
|
||||
}
|
||||
async getVersions(key: string, parentNode: ResourceData) {
|
||||
//const obj = parseDatasetVersionId(id);
|
||||
const res = await getModelVersionIdList(pick(parentNode, ['owner', 'identifier']));
|
||||
if (res && res.data) {
|
||||
const list = res.data.content || [];
|
||||
return convertDatasetVersionToTreeData(key, parentNode, list);
|
||||
} else {
|
||||
return Promise.reject('获取数据集版本列表失败');
|
||||
}
|
||||
}
|
||||
|
||||
async getFiles(_parentKey: string, parentNode: ResourceData & ResourceVersionData) {
|
||||
// const obj = parseDatasetVersionId(id);
|
||||
const params = pick(parentNode, ['owner', 'identifier', 'id', 'name', 'version']);
|
||||
const res = await getModelVersionsById(params);
|
||||
if (res && res.data) {
|
||||
const list = res.data.dataset_version_vos || [];
|
||||
return {
|
||||
path: res.data.path || '',
|
||||
content: list,
|
||||
};
|
||||
} else {
|
||||
return Promise.reject('获取数据集文件列表失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class MirrorSelector implements SelectorTypeInfo {
|
||||
readonly name = '镜像';
|
||||
readonly modalIcon = mirrorImg;
|
||||
readonly buttonIcon = 'icon-xuanzejingxiang';
|
||||
readonly tabItems = [
|
||||
{
|
||||
key: CommonTabKeys.Private,
|
||||
label: '我的镜像',
|
||||
},
|
||||
{
|
||||
key: CommonTabKeys.Public,
|
||||
label: '公开镜像',
|
||||
},
|
||||
];
|
||||
readonly buttontTitle = '选择镜像';
|
||||
|
||||
async getList(isPublic: boolean) {
|
||||
const res = await getMirrorListReq({
|
||||
image_type: isPublic ? AvailableRange.Public : AvailableRange.Private,
|
||||
page: 0,
|
||||
size: 2000,
|
||||
});
|
||||
if (res && res.data) {
|
||||
const list = res.data.content || [];
|
||||
return convertMirrorToTreeData(list);
|
||||
} else {
|
||||
return Promise.reject('获取镜像列表失败');
|
||||
}
|
||||
}
|
||||
async getVersions(parentKey: string) {
|
||||
const res = await getMirrorVersionListReq({
|
||||
image_id: parentKey,
|
||||
page: 0,
|
||||
size: 2000,
|
||||
});
|
||||
if (res && res.data) {
|
||||
const list = res.data.content || [];
|
||||
return convertMirrorVersionToTreeData(parentKey, list);
|
||||
} else {
|
||||
return Promise.reject('获取镜像版本列表失败');
|
||||
}
|
||||
}
|
||||
|
||||
async getFiles(parentKey: string, parentNode: MirrorVersionData) {
|
||||
const { url } = parentNode;
|
||||
return {
|
||||
path: url,
|
||||
content: [
|
||||
{
|
||||
id: `${id}-${version}`,
|
||||
id: parentKey,
|
||||
file_name: `${url}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const selectorTypeConfig: Record<ResourceSelectorType, SelectorTypeInfo> = {
|
||||
[ResourceSelectorType.Model]: {
|
||||
getList: getModelList,
|
||||
getVersions: getModelVersionsById,
|
||||
getFiles: getModelVersionIdList,
|
||||
handleVersionResponse: (res) => res.data || [],
|
||||
name: '模型',
|
||||
modalIcon: modelImg,
|
||||
buttonIcon: 'icon-xuanzemoxing',
|
||||
litReqParamKey: 'available_range',
|
||||
fileReqParamKey: 'models_id',
|
||||
tabItems: [
|
||||
{
|
||||
key: CommonTabKeys.Private,
|
||||
label: '我的模型',
|
||||
},
|
||||
{
|
||||
key: CommonTabKeys.Public,
|
||||
label: '公开模型',
|
||||
},
|
||||
],
|
||||
buttontTitle: '选择模型',
|
||||
},
|
||||
[ResourceSelectorType.Dataset]: {
|
||||
getList: getDatasetList,
|
||||
getVersions: getDatasetVersionsById,
|
||||
getFiles: getDatasetVersionIdList,
|
||||
handleVersionResponse: (res) => res.data || [],
|
||||
name: '数据集',
|
||||
modalIcon: datasetImg,
|
||||
buttonIcon: 'icon-xuanzeshujuji',
|
||||
litReqParamKey: 'available_range',
|
||||
fileReqParamKey: 'dataset_id',
|
||||
tabItems: [
|
||||
{
|
||||
key: CommonTabKeys.Private,
|
||||
label: '我的数据集',
|
||||
},
|
||||
{
|
||||
key: CommonTabKeys.Public,
|
||||
label: '公开数据集',
|
||||
},
|
||||
],
|
||||
buttontTitle: '选择数据集',
|
||||
},
|
||||
[ResourceSelectorType.Mirror]: {
|
||||
getList: getMirrorListReq,
|
||||
getVersions: (id: number) => getMirrorVersionListReq({ image_id: id, page: 0, size: 200 }),
|
||||
getFiles: getMirrorFilesReq,
|
||||
handleVersionResponse: (res) =>
|
||||
res.data?.content?.filter((v: MirrorVersion) => v.status === MirrorVersionStatus.Available) ||
|
||||
[],
|
||||
name: '镜像',
|
||||
modalIcon: mirrorImg,
|
||||
buttonIcon: 'icon-xuanzejingxiang',
|
||||
litReqParamKey: 'image_type',
|
||||
fileReqParamKey: 'dataset_id',
|
||||
tabItems: [
|
||||
{
|
||||
key: CommonTabKeys.Private,
|
||||
label: '我的镜像',
|
||||
},
|
||||
{
|
||||
key: CommonTabKeys.Public,
|
||||
label: '公开镜像',
|
||||
},
|
||||
],
|
||||
buttontTitle: '选择镜像',
|
||||
},
|
||||
[ResourceSelectorType.Model]: new ModelSelector(),
|
||||
[ResourceSelectorType.Dataset]: new DatasetSelector(),
|
||||
[ResourceSelectorType.Mirror]: new MirrorSelector(),
|
||||
};
|
||||
|
|
|
@ -67,3 +67,8 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.kf-tree-title {
|
||||
display: inline-block;
|
||||
.singleLine();
|
||||
}
|
||||
|
|
|
@ -11,24 +11,19 @@ import { Icon } from '@umijs/max';
|
|||
import type { GetRef, ModalProps, TreeDataNode, TreeProps } from 'antd';
|
||||
import { Input, Tabs, Tree } from 'antd';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { MirrorVersion, ResourceSelectorType, selectorTypeConfig } from './config';
|
||||
import { ResourceSelectorType, selectorTypeConfig } from './config';
|
||||
import styles from './index.less';
|
||||
export { ResourceSelectorType, selectorTypeConfig };
|
||||
|
||||
// 选择数据集\模型\镜像的返回类型
|
||||
export type ResourceSelectorResponse = {
|
||||
id: number; // 数据集\模型\镜像 id
|
||||
id: string; // 数据集\模型\镜像 id
|
||||
name: string; // 数据集\模型\镜像 name
|
||||
version: string; // 数据集\模型\镜像版本
|
||||
path: string; // 数据集\模型\镜像版本路径
|
||||
activeTab: CommonTabKeys; // 是我的还是公开的
|
||||
};
|
||||
|
||||
type ResourceGroup = {
|
||||
id: number; // 数据集\模型\镜像 id
|
||||
name: string; // 数据集\模型\镜像 name
|
||||
};
|
||||
|
||||
type ResourceFile = {
|
||||
id: number; // 文件 id
|
||||
file_name: string; // 文件 name
|
||||
|
@ -44,39 +39,8 @@ export interface ResourceSelectorModalProps extends Omit<ModalProps, 'onOk'> {
|
|||
|
||||
type TreeRef = GetRef<typeof Tree<TreeDataNode>>;
|
||||
|
||||
// list 数据转成 treeData
|
||||
const convertToTreeData = (list: ResourceGroup[]): TreeDataNode[] => {
|
||||
return list.map((v) => ({
|
||||
title: v.name,
|
||||
key: v.id,
|
||||
isLeaf: false,
|
||||
checkable: false,
|
||||
}));
|
||||
};
|
||||
|
||||
// 版本数据转成 treeData
|
||||
const convertVersionToTreeData = (parentId: number) => {
|
||||
return (item: string | MirrorVersion): TreeDataNode => {
|
||||
if (typeof item === 'string') {
|
||||
return {
|
||||
title: item,
|
||||
key: `${parentId}-${item}`,
|
||||
isLeaf: true,
|
||||
checkable: true,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
title: item.tag_name,
|
||||
key: `${parentId}-${item.id}-${item.url}`,
|
||||
isLeaf: true,
|
||||
checkable: true,
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// 更新树形结构的 children
|
||||
const updateChildren = (parentId: number, children: TreeDataNode[]) => {
|
||||
const updateChildren = (parentId: string, children: TreeDataNode[]) => {
|
||||
return (node: TreeDataNode) => {
|
||||
if (node.key === parentId) {
|
||||
return {
|
||||
|
@ -91,7 +55,7 @@ const updateChildren = (parentId: number, children: TreeDataNode[]) => {
|
|||
// 得到数据集\模型\镜像 id 和下属版本号
|
||||
const getIdAndVersion = (versionKey: string) => {
|
||||
const index = versionKey.indexOf('-');
|
||||
const id = Number(versionKey.slice(0, index));
|
||||
const id = versionKey.slice(0, index);
|
||||
const version = versionKey.slice(index + 1);
|
||||
return {
|
||||
id,
|
||||
|
@ -115,8 +79,8 @@ function ResourceSelectorModal({
|
|||
const [files, setFiles] = useState<ResourceFile[]>([]);
|
||||
const [versionPath, setVersionPath] = useState('');
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const [fisrtLoadList, setFisrtLoadList] = useState(false);
|
||||
const [fisrtLoadVersions, setFisrtLoadVersions] = useState(false);
|
||||
const [firstLoadList, setFirstLoadList] = useState(false);
|
||||
const [firstLoadVersions, setFirstLoadVersions] = useState(false);
|
||||
const treeRef = useRef<TreeRef>(null);
|
||||
const config = selectorTypeConfig[type];
|
||||
|
||||
|
@ -140,18 +104,10 @@ function ResourceSelectorModal({
|
|||
|
||||
// 获取数据集\模型\镜像列表
|
||||
const getTreeData = async () => {
|
||||
const available_range = activeTab === CommonTabKeys.Private ? 0 : 1;
|
||||
const params = {
|
||||
page: 0,
|
||||
size: 1000,
|
||||
[config.litReqParamKey]: available_range,
|
||||
};
|
||||
const getListReq = config.getList;
|
||||
const [res] = await to(getListReq(params));
|
||||
const isPublic = activeTab === CommonTabKeys.Private ? false : true;
|
||||
const [res] = await to(config.getList(isPublic));
|
||||
if (res) {
|
||||
const list = res.data?.content || [];
|
||||
const treeData = convertToTreeData(list);
|
||||
setOriginTreeData(treeData);
|
||||
setOriginTreeData(res);
|
||||
|
||||
// 恢复上一次的 Expand 操作
|
||||
restoreLastExpand();
|
||||
|
@ -161,21 +117,22 @@ function ResourceSelectorModal({
|
|||
};
|
||||
|
||||
// 获取数据集\模型\镜像版本列表
|
||||
const getVersions = async (parentId: number) => {
|
||||
const getVersionsReq = config.getVersions;
|
||||
const [res, error] = await to(getVersionsReq(parentId));
|
||||
const getVersions = async (parentId: string, parentNode: any) => {
|
||||
const [res, error] = await to(config.getVersions(parentId, parentNode));
|
||||
if (res) {
|
||||
const list = config.handleVersionResponse(res);
|
||||
const children = list.map(convertVersionToTreeData(parentId));
|
||||
// 更新 treeData children
|
||||
setOriginTreeData((prev) => prev.map(updateChildren(parentId, children)));
|
||||
setOriginTreeData((prev) => prev.map(updateChildren(parentId, res)));
|
||||
|
||||
// 缓存 loadedKeys
|
||||
const index = loadedKeys.find((v) => v === parentId);
|
||||
if (!index) {
|
||||
setLoadedKeys((prev) => prev.concat(parentId));
|
||||
}
|
||||
|
||||
// 恢复上一次的 Check 操作
|
||||
restoreLastCheck(parentId);
|
||||
setTimeout(() => {
|
||||
restoreLastCheck(parentId, res);
|
||||
}, 300);
|
||||
} else {
|
||||
setExpandedKeys([]);
|
||||
return Promise.reject(error);
|
||||
|
@ -183,14 +140,11 @@ function ResourceSelectorModal({
|
|||
};
|
||||
|
||||
// 获取版本下的文件
|
||||
const getFiles = async (id: number, version: string) => {
|
||||
const getFilesReq = config.getFiles;
|
||||
const paramsKey = config.fileReqParamKey;
|
||||
const params = { version: version, [paramsKey]: id };
|
||||
const [res] = await to(getFilesReq(params));
|
||||
const getFiles = async (parentId: string, parentNode: any) => {
|
||||
const [res] = await to(config.getFiles(parentId, parentNode));
|
||||
if (res) {
|
||||
setVersionPath(res.data?.path || '');
|
||||
setFiles(res.data?.content || []);
|
||||
setVersionPath(res.path);
|
||||
setFiles(res.content);
|
||||
} else {
|
||||
setVersionPath('');
|
||||
setFiles([]);
|
||||
|
@ -198,11 +152,11 @@ function ResourceSelectorModal({
|
|||
};
|
||||
|
||||
// 动态加载 tree children
|
||||
const onLoadData = ({ key, children }: TreeDataNode) => {
|
||||
const onLoadData = ({ key, children, ...rest }: TreeDataNode) => {
|
||||
if (children) {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return getVersions(key as number);
|
||||
return getVersions(key as string, rest);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -213,13 +167,13 @@ function ResourceSelectorModal({
|
|||
};
|
||||
|
||||
// 选中
|
||||
const onCheck: TreeProps['onCheck'] = (checkedKeysValue) => {
|
||||
const onCheck: TreeProps['onCheck'] = (checkedKeysValue, { checkedNodes }) => {
|
||||
const lastKeys = (checkedKeysValue as React.Key[]).slice(-1);
|
||||
setCheckedKeys(lastKeys);
|
||||
if (lastKeys.length) {
|
||||
if (lastKeys.length && checkedNodes.length) {
|
||||
const last = lastKeys[0] as string;
|
||||
const { id, version } = getIdAndVersion(last);
|
||||
getFiles(id, version);
|
||||
const lastNode = checkedNodes[checkedNodes.length - 1];
|
||||
getFiles(last, lastNode);
|
||||
} else {
|
||||
setFiles([]);
|
||||
}
|
||||
|
@ -229,10 +183,10 @@ function ResourceSelectorModal({
|
|||
// 判断是否有 defaultExpandedKeys,如果有,设置 expandedKeys
|
||||
// fisrtLoadList 标志位
|
||||
const restoreLastExpand = () => {
|
||||
if (!fisrtLoadList && defaultExpandedKeys.length > 0) {
|
||||
if (!firstLoadList && defaultExpandedKeys.length > 0) {
|
||||
setTimeout(() => {
|
||||
setExpandedKeys(defaultExpandedKeys);
|
||||
setFisrtLoadList(true);
|
||||
setFirstLoadList(true);
|
||||
setTimeout(() => {
|
||||
treeRef.current?.scrollTo({ key: defaultExpandedKeys[0], align: 'bottom' });
|
||||
}, 100);
|
||||
|
@ -243,16 +197,17 @@ function ResourceSelectorModal({
|
|||
// 恢复上一次的 Check 操作
|
||||
// 判断是否有 defaultCheckedKeys,如果有,设置 checkedKeys,并且调用获取文件列表接口
|
||||
// fisrtLoadVersions 标志位
|
||||
const restoreLastCheck = (parentId: number) => {
|
||||
if (!fisrtLoadVersions && defaultCheckedKeys.length > 0) {
|
||||
const restoreLastCheck = (parentId: string, versions: TreeDataNode[]) => {
|
||||
if (!firstLoadVersions && defaultCheckedKeys.length > 0) {
|
||||
const last = defaultCheckedKeys[0] as string;
|
||||
const { id, version } = getIdAndVersion(last);
|
||||
const { id } = getIdAndVersion(last);
|
||||
// 判断正在打开的 id 和 defaultCheckedKeys 的 id 是否一致
|
||||
if (id === parentId) {
|
||||
setTimeout(() => {
|
||||
setCheckedKeys(defaultCheckedKeys);
|
||||
getFiles(id, version);
|
||||
setFisrtLoadVersions(true);
|
||||
const parentNode = versions.find((v) => v.key === last);
|
||||
getFiles(last, parentNode);
|
||||
setFirstLoadVersions(true);
|
||||
setTimeout(() => {
|
||||
treeRef?.current?.scrollTo({
|
||||
key: defaultCheckedKeys[0],
|
||||
|
@ -269,7 +224,7 @@ function ResourceSelectorModal({
|
|||
if (checkedKeys.length > 0) {
|
||||
const last = checkedKeys[0] as string;
|
||||
const { id, version } = getIdAndVersion(last);
|
||||
const name = (treeData.find((v) => Number(v.key) === id)?.title ?? '') as string;
|
||||
const name = (treeData.find((v) => v.key === id)?.title ?? '') as string;
|
||||
const res = {
|
||||
id,
|
||||
name,
|
||||
|
@ -323,6 +278,18 @@ function ResourceSelectorModal({
|
|||
loadedKeys={loadedKeys}
|
||||
expandedKeys={expandedKeys}
|
||||
onExpand={onExpand}
|
||||
titleRender={(nodeData) => {
|
||||
console.log(nodeData);
|
||||
|
||||
return (
|
||||
<span
|
||||
className={styles['kf-tree-title']}
|
||||
style={{ width: nodeData.isLeaf ? '370px' : '420px' }}
|
||||
>
|
||||
{nodeData.title as string}
|
||||
</span>
|
||||
);
|
||||
}}
|
||||
checkable
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -62,6 +62,8 @@ const UserForm: React.FC<UserFormProps> = (props) => {
|
|||
loginIp: props.values.loginIp,
|
||||
loginDate: props.values.loginDate,
|
||||
remark: props.values.remark,
|
||||
gitLinkUsername: props.values.gitLinkUsername,
|
||||
gitLinkPassword: props.values.gitLinkPassword,
|
||||
});
|
||||
}, [form, props]);
|
||||
|
||||
|
@ -275,6 +277,28 @@ const UserForm: React.FC<UserFormProps> = (props) => {
|
|||
colProps={{ md: 12, xl: 12 }}
|
||||
rules={[{ required: true, message: '请选择角色!' }]}
|
||||
/>
|
||||
<ProFormText
|
||||
name="gitLinkUsername"
|
||||
label="Git 用户名"
|
||||
placeholder="请输入 Git 用户名"
|
||||
colProps={{ xs: 24, md: 12, xl: 12 }}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入 Git 用户名!',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormText.Password
|
||||
name="gitLinkPassword"
|
||||
label="Git 密码"
|
||||
placeholder="请输入 Git 密码"
|
||||
colProps={{ xs: 24, md: 12, xl: 12 }}
|
||||
fieldProps={{
|
||||
autoComplete: 'new-password',
|
||||
}}
|
||||
rules={props.values.userId ? [] : [{ required: true, message: '请输入 Git 密码!' }]}
|
||||
/>
|
||||
<ProFormTextArea
|
||||
name="remark"
|
||||
label={intl.formatMessage({
|
||||
|
|
|
@ -1,10 +1,20 @@
|
|||
import missingPage from '@/assets/img/missing-back.png';
|
||||
import KFEmpty, { EmptyType } from '@/components/KFEmpty';
|
||||
import { useNavigate } from '@umijs/max';
|
||||
|
||||
const MissingPage = () => (
|
||||
<div style={{ width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
||||
<img src={missingPage} style={{ width: '575px', margin: '278px 0 44px 0' }} alt="" />
|
||||
<span style={{ color: '#575757', fontSize: '16px' }}>页面开发中,敬请期待......</span>
|
||||
</div>
|
||||
);
|
||||
const MissingPage = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<KFEmpty
|
||||
style={{ height: '100%' }}
|
||||
type={EmptyType.Developing}
|
||||
title="敬请期待~"
|
||||
content={'很抱歉,您访问的正在开发中,\n请耐心等待。'}
|
||||
hasFooter={true}
|
||||
backTitle="返回首页"
|
||||
onBack={() => navigate('/')}
|
||||
></KFEmpty>
|
||||
);
|
||||
};
|
||||
|
||||
export default MissingPage;
|
||||
|
|
|
@ -7,6 +7,7 @@ import type { AxiosRequestConfig, AxiosResponse, RequestConfig, RequestOptions }
|
|||
import { message } from 'antd';
|
||||
import { clearSessionToken, getAccessToken } from './access';
|
||||
import { setRemoteMenu } from './services/session';
|
||||
import Loading from './utils/loading';
|
||||
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.
|
||||
|
@ -36,12 +37,14 @@ export const requestConfig: RequestConfig = {
|
|||
headers['Authorization'] = `Bearer ${accessToken}`;
|
||||
}
|
||||
}
|
||||
Loading.show();
|
||||
return { url, options };
|
||||
},
|
||||
],
|
||||
responseInterceptors: [
|
||||
[
|
||||
(response: AxiosResponse) => {
|
||||
Loading.hide();
|
||||
const { status, data, config } = response || {};
|
||||
const skipErrorHandler = (config as RequestOptions)?.skipErrorHandler;
|
||||
if (status >= 200 && status < 300) {
|
||||
|
@ -63,6 +66,7 @@ export const requestConfig: RequestConfig = {
|
|||
}
|
||||
},
|
||||
(error: Error) => {
|
||||
Loading.hide();
|
||||
popupError(error.message ?? '请求失败');
|
||||
return Promise.reject(error);
|
||||
},
|
||||
|
|
|
@ -1,21 +1,34 @@
|
|||
import { request } from '@umijs/max';
|
||||
// 分页查询数据集
|
||||
|
||||
// 查询数据集、模型分类
|
||||
export function getAssetIcon(params) {
|
||||
return request(`/api/mmp/assetIcon`, {
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// ----------------------------数据集---------------------------------
|
||||
|
||||
// 分页查询数据集列表
|
||||
export function getDatasetList(params) {
|
||||
return request(`/api/mmp/dataset`, {
|
||||
return request(`/api/mmp/newdataset/queryDatasets`, {
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
// 分页查询模型
|
||||
export function getModelList(params) {
|
||||
return request(`/api/mmp/models`, {
|
||||
|
||||
// 查询数据集详情
|
||||
export function getDatasetInfo(params) {
|
||||
return request(`/api/mmp/newdataset/getDatasetDetail`, {
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 新增数据集
|
||||
export function addDatesetAndVesion(data) {
|
||||
return request(`/api/mmp/dataset/addDatasetAndVersion`, {
|
||||
export function addDateset(data) {
|
||||
return request(`/api/mmp/newdataset/addDatasetAndVersion`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=UTF-8',
|
||||
|
@ -23,6 +36,77 @@ export function addDatesetAndVesion(data) {
|
|||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 删除数据集
|
||||
export function deleteDataset(params) {
|
||||
return request(`/api/mmp/newdataset/deleteDataset`, {
|
||||
method: 'DELETE',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 查询数据集版本列表
|
||||
export function getDatasetVersionList(params) {
|
||||
return request(`/api/mmp/newdataset/getVersionList`, {
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 查询数据集版本文件列表
|
||||
// export function getDatasetVersionFiles(params) {
|
||||
// return request(`/api/mmp/datasetVersion/versions`, {
|
||||
// method: 'GET',
|
||||
// params,
|
||||
// });
|
||||
// }
|
||||
|
||||
// 新增数据集版本
|
||||
export function addDatasetVersion(data) {
|
||||
return request(`/api/mmp/newdataset/addVersion`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=UTF-8',
|
||||
},
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 下载数据集所有文件
|
||||
export function downloadAllFiles(params) {
|
||||
return request(`/api/mmp/newdataset/downloadAllFiles`, {
|
||||
method: 'GET',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
// 下载数据集单个文件
|
||||
export function downloadSingleFile(params) {
|
||||
return request(`/api/mmp/newdataset/downloadSinggerFile`, {
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 删除数据集版本
|
||||
export function deleteDatasetVersion(params) {
|
||||
return request(`/api/mmp/newdataset/deleteDatasetVersion`, {
|
||||
method: 'DELETE',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// ----------------------------模型---------------------------------
|
||||
|
||||
// 分页查询模型列表
|
||||
export function getModelList(params) {
|
||||
return request(`/api/mmp/models`, {
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 新增模型
|
||||
export function addModel(data) {
|
||||
return request(`/api/mmp/models/addModelAndVersion`, {
|
||||
|
@ -33,75 +117,29 @@ export function addModel(data) {
|
|||
data,
|
||||
});
|
||||
}
|
||||
// 查询数据集简介
|
||||
export function getDatasetById(id) {
|
||||
return request(`/api/mmp/dataset/${id}`, {
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
// 查询左侧列表
|
||||
export function getAssetIcon(params) {
|
||||
return request(`/api/mmp/assetIcon`, {
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 查询模型简介
|
||||
export function getModelById(id) {
|
||||
return request(`/api/mmp/models/${id}`, {
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
// 查询数据版本集
|
||||
export function getDatasetVersionsById(id) {
|
||||
return request(`/api/mmp/dataset/versions/${id}`, {
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
// 查询模型版本集
|
||||
|
||||
// 查询模型版本列表
|
||||
export function getModelVersionsById(id) {
|
||||
return request(`/api/mmp/models/versions/${id}`, {
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
// 分页查询数据集
|
||||
export function getDatasetVersionIdList(params) {
|
||||
return request(`/api/mmp/datasetVersion/versions`, {
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
// 根据版本查询模型
|
||||
|
||||
// 根据版本查询文件列表
|
||||
export function getModelVersionIdList(params) {
|
||||
return request(`/api/mmp/modelsVersion/versions`, {
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
// 删除数据集
|
||||
export function deleteDatasetVersion(params) {
|
||||
return request(`/api/mmp/datasetVersion/deleteVersion`, {
|
||||
method: 'DELETE',
|
||||
params,
|
||||
});
|
||||
}
|
||||
// 删除模型
|
||||
export function deleteModelVersion(params) {
|
||||
return request(`/api/mmp/modelsVersion/deleteVersion`, {
|
||||
method: 'DELETE',
|
||||
params,
|
||||
});
|
||||
}
|
||||
// 新增数据集版本
|
||||
export function addDatasetVersionDetail(data) {
|
||||
return request(`/api/mmp/datasetVersion/addDatasetVersions`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=UTF-8',
|
||||
},
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 新增模型版本
|
||||
export function addModelsVersionDetail(data) {
|
||||
return request(`/api/mmp/modelsVersion/addModelVersions`, {
|
||||
|
@ -112,24 +150,22 @@ export function addModelsVersionDetail(data) {
|
|||
data,
|
||||
});
|
||||
}
|
||||
// 下载数据集
|
||||
export function exportDataset(id) {
|
||||
return request(`/api/mmp/dataset/download/${id}`, {
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
// 删除模型集
|
||||
|
||||
// 删除模型
|
||||
export function deleteModel(id) {
|
||||
return request(`/api/mmp/models/${id}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
// 删除数据集
|
||||
export function deleteDataset(id) {
|
||||
return request(`/api/mmp/dataset/${id}`, {
|
||||
|
||||
// 删除模型版本
|
||||
export function deleteModelVersion(params) {
|
||||
return request(`/api/mmp/modelsVersion/deleteVersion`, {
|
||||
method: 'DELETE',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 获取模型依赖
|
||||
export function getModelAtlasReq(data) {
|
||||
return request(`/api/mmp/modelDependency/queryModelAtlas`, {
|
||||
|
|
|
@ -19,6 +19,8 @@ declare namespace API.System {
|
|||
updateBy: string;
|
||||
updateTime: Date;
|
||||
remark: string;
|
||||
gitLinkUsername?: string;
|
||||
gitLinkPassword?: string;
|
||||
}
|
||||
|
||||
export interface UserListParams {
|
||||
|
|
|
@ -11,29 +11,37 @@ import zhCN from 'antd/locale/zh_CN';
|
|||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
export class Loading {
|
||||
static total = 0;
|
||||
static total: number = 0;
|
||||
static isShowing: boolean = false;
|
||||
static startTime: Date = new Date();
|
||||
static removeTimeout: ReturnType<typeof setTimeout> | undefined;
|
||||
static show(props?: SpinProps) {
|
||||
Loading.total += 1;
|
||||
if (Loading.total > 1) {
|
||||
this.total += 1;
|
||||
if (this.total > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 是否有延时未关闭的 loading
|
||||
if (this.isShowing) {
|
||||
this.clearRemoveTimeout();
|
||||
return;
|
||||
}
|
||||
const container = document.createElement('div');
|
||||
container.id = 'loading';
|
||||
const rootContainer = document.getElementsByTagName('main')[0];
|
||||
const rootContainer = document.body; //document.getElementsByTagName('main')[0];
|
||||
rootContainer?.appendChild(container);
|
||||
const root = createRoot(container);
|
||||
const global = globalConfig();
|
||||
let timeoutId: ReturnType<typeof setTimeout>;
|
||||
let renderTimeoutId: ReturnType<typeof setTimeout>;
|
||||
|
||||
function render(spinProps: SpinProps) {
|
||||
clearTimeout(timeoutId);
|
||||
const render = (spinProps: SpinProps) => {
|
||||
clearTimeout(renderTimeoutId);
|
||||
|
||||
timeoutId = setTimeout(() => {
|
||||
renderTimeoutId = setTimeout(() => {
|
||||
const rootPrefixCls = global.getPrefixCls();
|
||||
const iconPrefixCls = global.getIconPrefixCls();
|
||||
const theme = global.getTheme();
|
||||
const dom = <KFSpin {...spinProps} />;
|
||||
|
||||
root.render(
|
||||
<ConfigProvider
|
||||
prefixCls={rootPrefixCls}
|
||||
|
@ -45,21 +53,41 @@ export class Loading {
|
|||
</ConfigProvider>,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render({ size: 'large', ...props, spinning: true });
|
||||
this.startTime = new Date();
|
||||
this.isShowing = true;
|
||||
}
|
||||
|
||||
static clearRemoveTimeout() {
|
||||
if (this.removeTimeout) {
|
||||
clearTimeout(this.removeTimeout);
|
||||
this.removeTimeout = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
static removeLoading() {
|
||||
this.clearRemoveTimeout();
|
||||
const rootContainer = document.body; //document.getElementsByTagName('main')[0];
|
||||
const container = document.getElementById('loading');
|
||||
if (container) {
|
||||
rootContainer?.removeChild(container);
|
||||
}
|
||||
this.isShowing = false;
|
||||
}
|
||||
|
||||
static hide(force: boolean = false) {
|
||||
Loading.total -= 1;
|
||||
if (Loading.total <= 0 || force) {
|
||||
Loading.total = 0;
|
||||
const rootContainer = document.getElementsByTagName('main')[0];
|
||||
const container = document.getElementById('loading');
|
||||
if (container) {
|
||||
rootContainer?.removeChild(container);
|
||||
}
|
||||
this.total -= 1;
|
||||
if (this.total > 0 && !force) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.total = 0;
|
||||
const duration = new Date().getTime() - this.startTime.getTime();
|
||||
this.removeTimeout = setTimeout(() => {
|
||||
this.removeLoading();
|
||||
}, Math.max(300 - duration, 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,20 @@ export const mirrorNameKey = 'mirror-name';
|
|||
export const modelDeploymentInfoKey = 'model-deployment-info';
|
||||
// 编辑器 url
|
||||
export const editorUrlKey = 'editor-url';
|
||||
// 数据集、模型资源
|
||||
export const resourceItemKey = 'resource-item';
|
||||
|
||||
export const getSessionStorageItem = (key: string, isObject: boolean = false) => {
|
||||
/**
|
||||
* Retrieves an item from session storage by key.
|
||||
*
|
||||
* If `isObject` is true, the function attempts to parse the stored value as JSON.
|
||||
* If parsing fails, the function returns undefined.
|
||||
*
|
||||
* @param {string} key - The key of the item to retrieve
|
||||
* @param {boolean} [isObject=false] - Whether to parse the stored value as JSON
|
||||
* @return {any} The retrieved item, or undefined if not found or parsing fails
|
||||
*/
|
||||
export const getSessionStorageItem = (key: string, isObject: boolean = false): any => {
|
||||
const jsonStr = sessionStorage.getItem(key);
|
||||
if (!isObject) {
|
||||
return jsonStr;
|
||||
|
@ -20,18 +32,40 @@ export const getSessionStorageItem = (key: string, isObject: boolean = false) =>
|
|||
return undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets an item in session storage by key.
|
||||
*
|
||||
* If `isObject` is true, the function stringifies the state as JSON before storing.
|
||||
*
|
||||
* @param {string} key - The key of the item to set
|
||||
* @param {any} [state] - The value of the item to set
|
||||
* @param {boolean} [isObject=false] - Whether to stringify the state as JSON
|
||||
*/
|
||||
export const setSessionStorageItem = (key: string, state?: any, isObject: boolean = false) => {
|
||||
if (state) {
|
||||
sessionStorage.setItem(key, isObject ? JSON.stringify(state) : state);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes an item from session storage by key.
|
||||
*
|
||||
* @param {string} key - The key of the item to remove
|
||||
*/
|
||||
export const removeSessionStorageItem = (key: string) => {
|
||||
sessionStorage.removeItem(key);
|
||||
};
|
||||
|
||||
// 获取之后就删除,多用于上一个页面传递数据到下一个页面
|
||||
export const getSessionItemThenRemove = (key: string, isObject: boolean = false) => {
|
||||
/**
|
||||
* Retrieves an item from session storage by key and then removes it.
|
||||
*
|
||||
* This function is useful for passing data from one page to another.
|
||||
*
|
||||
* @param {string} key - The key of the item to retrieve
|
||||
* @param {boolean} [isObject=false] - Whether to parse the stored value as JSON
|
||||
* @return {any} The retrieved item, or undefined if not found or parsing fails
|
||||
*/
|
||||
export const getSessionItemThenRemove = (key: string, isObject: boolean = false): any => {
|
||||
const res = getSessionStorageItem(key, isObject);
|
||||
sessionStorage.removeItem(key);
|
||||
return res;
|
||||
|
|
Loading…
Reference in New Issue