| @@ -5,31 +5,40 @@ | |||||
| gap: 20px 40px; | gap: 20px 40px; | ||||
| align-items: flex-start; | align-items: flex-start; | ||||
| width: 80%; | width: 80%; | ||||
| } | |||||
| &__item { | |||||
| display: flex; | |||||
| align-items: flex-start; | |||||
| width: calc(50% - 20px); | |||||
| font-size: 16px; | |||||
| line-height: 1.6; | |||||
| .kf-basic-info-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; | |||||
| &__label { | |||||
| position: relative; | |||||
| color: @text-color-secondary; | |||||
| text-align: justify; | |||||
| text-align-last: justify; | |||||
| &::after { | |||||
| position: absolute; | |||||
| content: ':'; | |||||
| } | } | ||||
| } | |||||
| &__value { | |||||
| display: flex; | |||||
| flex: 1; | |||||
| color: @text-color; | |||||
| word-break: break-all; | |||||
| &__value { | |||||
| flex: 1; | |||||
| margin-left: 16px; | |||||
| white-space: pre-line; | |||||
| word-break: break-all; | |||||
| } | |||||
| &::before { | |||||
| margin-right: 16px; | |||||
| content: ':'; | |||||
| } | |||||
| } | |||||
| &__text { | |||||
| color: @text-color; | |||||
| } | |||||
| &__link:hover { | |||||
| text-decoration: underline @underline-color; | |||||
| text-underline-offset: 3px; | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,27 +1,73 @@ | |||||
| import { isEmptyString } from '@/utils'; | |||||
| import { Link } from '@umijs/max'; | |||||
| import classNames from 'classnames'; | |||||
| import './index.less'; | import './index.less'; | ||||
| export type BasicInfoData = { | export type BasicInfoData = { | ||||
| label: string; | label: string; | ||||
| value?: any; | value?: any; | ||||
| format?: (_value: any) => string; | |||||
| link?: string; | |||||
| externalLink?: string; | |||||
| format?: (_value?: any) => string | undefined; | |||||
| }; | }; | ||||
| type BasicInfoProps = { | type BasicInfoProps = { | ||||
| datas: BasicInfoData[]; | datas: BasicInfoData[]; | ||||
| className?: string; | |||||
| style?: React.CSSProperties; | |||||
| labelWidth?: number; | |||||
| }; | }; | ||||
| function BasicInfo({ datas }: BasicInfoProps) { | |||||
| function BasicInfo({ datas, className, style, labelWidth = 100 }: BasicInfoProps) { | |||||
| return ( | return ( | ||||
| <div className="kf-basic-info"> | |||||
| <div className={classNames('kf-basic-info', className)} style={style}> | |||||
| {datas.map((item) => ( | {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> | |||||
| <BasicInfoItem key={item.label} data={item} labelWidth={labelWidth} /> | |||||
| ))} | ))} | ||||
| </div> | </div> | ||||
| ); | ); | ||||
| } | } | ||||
| type BasicInfoItemProps = { | |||||
| data: BasicInfoData; | |||||
| labelWidth?: number; | |||||
| }; | |||||
| function BasicInfoItem({ data, labelWidth = 100 }: BasicInfoItemProps) { | |||||
| const { label, value, externalLink, link, format } = data; | |||||
| const showValue = format ? format(value) : value; | |||||
| let valueComponent = undefined; | |||||
| if (externalLink && showValue) { | |||||
| valueComponent = ( | |||||
| <a | |||||
| className="kf-basic-info-item__value kf-basic-info-item__link" | |||||
| href={externalLink} | |||||
| target="_blank" | |||||
| rel="noopener noreferrer" | |||||
| > | |||||
| {showValue} | |||||
| </a> | |||||
| ); | |||||
| } else if (link && showValue) { | |||||
| valueComponent = ( | |||||
| <Link to={link} className="kf-basic-info-item__value kf-basic-info-item__link"> | |||||
| {showValue} | |||||
| </Link> | |||||
| ); | |||||
| } else { | |||||
| valueComponent = ( | |||||
| <div className="kf-basic-info-item__value kf-basic-info-item__text"> | |||||
| {isEmptyString(showValue) ? '--' : showValue} | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| return ( | |||||
| <div className="kf-basic-info-item" key={label}> | |||||
| <div className="kf-basic-info-item__label" style={{ width: labelWidth }}> | |||||
| {label} | |||||
| </div> | |||||
| {valueComponent} | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| export default BasicInfo; | export default BasicInfo; | ||||
| @@ -7,7 +7,6 @@ | |||||
| &__image { | &__image { | ||||
| width: 475px; | width: 475px; | ||||
| height: 292px; | |||||
| } | } | ||||
| &__title { | &__title { | ||||
| @@ -15,7 +14,6 @@ | |||||
| color: @text-color; | color: @text-color; | ||||
| font-weight: 500; | font-weight: 500; | ||||
| font-size: 30px; | font-size: 30px; | ||||
| letter-spacing: 5px; | |||||
| text-align: center; | text-align: center; | ||||
| } | } | ||||
| @@ -32,6 +30,7 @@ | |||||
| align-items: center; | align-items: center; | ||||
| justify-content: center; | justify-content: center; | ||||
| margin-top: 20px; | margin-top: 20px; | ||||
| margin-bottom: 30px; | |||||
| &__back-btn { | &__back-btn { | ||||
| height: 32px; | height: 32px; | ||||
| @@ -16,8 +16,8 @@ type EmptyProps = { | |||||
| content?: string; | content?: string; | ||||
| hasFooter?: boolean; | hasFooter?: boolean; | ||||
| footer?: () => React.ReactNode; | footer?: () => React.ReactNode; | ||||
| backTitle?: string; | |||||
| onBack?: () => void; | |||||
| buttonTitle?: string; | |||||
| onRefresh?: () => void; | |||||
| }; | }; | ||||
| function getEmptyImage(type: EmptyType) { | function getEmptyImage(type: EmptyType) { | ||||
| @@ -39,8 +39,8 @@ function KFEmpty({ | |||||
| content, | content, | ||||
| hasFooter = false, | hasFooter = false, | ||||
| footer, | footer, | ||||
| backTitle = '返回', | |||||
| onBack, | |||||
| buttonTitle = '刷新', | |||||
| onRefresh, | |||||
| }: EmptyProps) { | }: EmptyProps) { | ||||
| const image = getEmptyImage(type); | const image = getEmptyImage(type); | ||||
| @@ -54,8 +54,8 @@ function KFEmpty({ | |||||
| {footer ? ( | {footer ? ( | ||||
| footer() | footer() | ||||
| ) : ( | ) : ( | ||||
| <Button className="kf-empty__footer__back-btn" type="primary" onClick={onBack}> | |||||
| {backTitle} | |||||
| <Button className="kf-empty__footer__back-btn" type="primary" onClick={onRefresh}> | |||||
| {buttonTitle} | |||||
| </Button> | </Button> | ||||
| )} | )} | ||||
| </div> | </div> | ||||
| @@ -14,7 +14,7 @@ const filterResourceStandard: SelectProps<string, ComputingResource>['filterOpti | |||||
| }; | }; | ||||
| // id 从 number 转换为 string | // id 从 number 转换为 string | ||||
| const convertId = (item: any) => ({ ...item, id: String(item.id) }); | |||||
| const convertId = (item: any) => ({ ...item, id: `${item.id}-${item.identifier}` }); | |||||
| export type SelectPropsConfig = { | export type SelectPropsConfig = { | ||||
| getOptions: () => Promise<any>; // 获取下拉数据 | getOptions: () => Promise<any>; // 获取下拉数据 | ||||
| @@ -11,8 +11,8 @@ const NoFoundPage = () => { | |||||
| title="404" | title="404" | ||||
| content={'很抱歉,您访问的页面地址有误,\n或者该页面不存在。'} | content={'很抱歉,您访问的页面地址有误,\n或者该页面不存在。'} | ||||
| hasFooter={true} | hasFooter={true} | ||||
| backTitle="返回首页" | |||||
| onBack={() => navigate('/')} | |||||
| buttonTitle="返回首页" | |||||
| onRefresh={() => navigate('/')} | |||||
| ></KFEmpty> | ></KFEmpty> | ||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -171,6 +171,9 @@ function CodeConfigList() { | |||||
| className={styles['code-config-list__empty']} | className={styles['code-config-list__empty']} | ||||
| type={EmptyType.NoData} | type={EmptyType.NoData} | ||||
| title="暂无数据" | title="暂无数据" | ||||
| content={'很抱歉,没有搜索到您想要的内容\n建议刷新试试'} | |||||
| hasFooter={true} | |||||
| onRefresh={getDataList} | |||||
| /> | /> | ||||
| )} | )} | ||||
| </div> | </div> | ||||
| @@ -1,7 +1,8 @@ | |||||
| import { getAccessToken } from '@/access'; | import { getAccessToken } from '@/access'; | ||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import KFModal from '@/components/KFModal'; | import KFModal from '@/components/KFModal'; | ||||
| import { addDateset } from '@/services/dataset/index.js'; | |||||
| import { CategoryData, ResourceType, resourceConfig } from '@/pages/Dataset/config'; | |||||
| import { addDataset } from '@/services/dataset/index.js'; | |||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { getFileListFromEvent, validateUploadFiles } from '@/utils/ui'; | import { getFileListFromEvent, validateUploadFiles } from '@/utils/ui'; | ||||
| import { | import { | ||||
| @@ -18,7 +19,6 @@ import { | |||||
| } from 'antd'; | } from 'antd'; | ||||
| import { omit } from 'lodash'; | import { omit } from 'lodash'; | ||||
| import { useState } from 'react'; | import { useState } from 'react'; | ||||
| import { CategoryData } from '../../config'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| interface AddDatasetModalProps extends Omit<ModalProps, 'onOk'> { | interface AddDatasetModalProps extends Omit<ModalProps, 'onOk'> { | ||||
| @@ -37,7 +37,7 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr | |||||
| // 上传组件参数 | // 上传组件参数 | ||||
| const uploadProps: UploadProps = { | const uploadProps: UploadProps = { | ||||
| action: '/api/mmp/newdataset/upload', | |||||
| action: resourceConfig[ResourceType.Dataset].uploadAction, | |||||
| headers: { | headers: { | ||||
| Authorization: getAccessToken() || '', | Authorization: getAccessToken() || '', | ||||
| }, | }, | ||||
| @@ -54,7 +54,7 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr | |||||
| // 上传请求 | // 上传请求 | ||||
| const createDataset = async (params: any) => { | const createDataset = async (params: any) => { | ||||
| const [res] = await to(addDateset(params)); | |||||
| const [res] = await to(addDataset(params)); | |||||
| if (res) { | if (res) { | ||||
| message.success('创建成功'); | message.success('创建成功'); | ||||
| onOk?.(); | onOk?.(); | ||||
| @@ -120,6 +120,14 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr | |||||
| required: true, | required: true, | ||||
| message: '请输入数据集版本', | message: '请输入数据集版本', | ||||
| }, | }, | ||||
| { | |||||
| validator: (_rule, value) => { | |||||
| if (value === 'master') { | |||||
| return Promise.reject(`版本不能为 master`); | |||||
| } | |||||
| return Promise.resolve(); | |||||
| }, | |||||
| }, | |||||
| ]} | ]} | ||||
| > | > | ||||
| <Input placeholder="请输入数据集版本" showCount allowClear maxLength={64} /> | <Input placeholder="请输入数据集版本" showCount allowClear maxLength={64} /> | ||||
| @@ -1,7 +1,7 @@ | |||||
| import { getAccessToken } from '@/access'; | import { getAccessToken } from '@/access'; | ||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import KFModal from '@/components/KFModal'; | import KFModal from '@/components/KFModal'; | ||||
| import { CategoryData } from '@/pages/Dataset/config'; | |||||
| import { CategoryData, ResourceType, resourceConfig } from '@/pages/Dataset/config'; | |||||
| import { addModel } from '@/services/dataset/index.js'; | import { addModel } from '@/services/dataset/index.js'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { getFileListFromEvent, validateUploadFiles } from '@/utils/ui'; | import { getFileListFromEvent, validateUploadFiles } from '@/utils/ui'; | ||||
| @@ -9,6 +9,7 @@ import { | |||||
| Button, | Button, | ||||
| Form, | Form, | ||||
| Input, | Input, | ||||
| Radio, | |||||
| Select, | Select, | ||||
| Upload, | Upload, | ||||
| UploadFile, | UploadFile, | ||||
| @@ -31,7 +32,7 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps) | |||||
| // 上传组件参数 | // 上传组件参数 | ||||
| const uploadProps: UploadProps = { | const uploadProps: UploadProps = { | ||||
| action: '/api/mmp/models/upload', | |||||
| action: resourceConfig[ResourceType.Model].uploadAction, | |||||
| headers: { | headers: { | ||||
| Authorization: getAccessToken() || '', | Authorization: getAccessToken() || '', | ||||
| }, | }, | ||||
| @@ -53,7 +54,7 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps) | |||||
| if (validateUploadFiles(fileList)) { | if (validateUploadFiles(fileList)) { | ||||
| const params = { | const params = { | ||||
| ...omit(formData, ['fileList']), | ...omit(formData, ['fileList']), | ||||
| models_version_vos: fileList.map((item) => { | |||||
| model_version_vos: fileList.map((item) => { | |||||
| const data = item.response?.data?.[0] ?? {}; | const data = item.response?.data?.[0] ?? {}; | ||||
| return { | return { | ||||
| file_name: data.fileName, | file_name: data.fileName, | ||||
| @@ -77,7 +78,13 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps) | |||||
| form: 'form', | form: 'form', | ||||
| }} | }} | ||||
| > | > | ||||
| <Form name="form" layout="vertical" onFinish={onFinish} autoComplete="off"> | |||||
| <Form | |||||
| name="form" | |||||
| layout="vertical" | |||||
| onFinish={onFinish} | |||||
| autoComplete="off" | |||||
| initialValues={{ is_public: false }} | |||||
| > | |||||
| <Form.Item | <Form.Item | ||||
| label="模型名称" | label="模型名称" | ||||
| name="name" | name="name" | ||||
| @@ -88,9 +95,8 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps) | |||||
| }, | }, | ||||
| ]} | ]} | ||||
| > | > | ||||
| <Input placeholder="请输入模型名称" showCount allowClear maxLength={64} /> | |||||
| <Input placeholder="请输入模型名称" showCount allowClear maxLength={50} /> | |||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item | <Form.Item | ||||
| label="模型版本" | label="模型版本" | ||||
| name="version" | name="version" | ||||
| @@ -99,40 +105,24 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps) | |||||
| required: true, | required: true, | ||||
| message: '请输入模型版本', | message: '请输入模型版本', | ||||
| }, | }, | ||||
| ]} | |||||
| > | |||||
| <Input placeholder="请输入模型版本" allowClear maxLength={64} /> | |||||
| </Form.Item> | |||||
| <Form.Item | |||||
| label="模型简介" | |||||
| name="description" | |||||
| rules={[ | |||||
| { | { | ||||
| required: true, | |||||
| message: '请输入模型简介', | |||||
| validator: (_rule, value) => { | |||||
| if (value === 'master') { | |||||
| return Promise.reject(`版本不能为 master`); | |||||
| } | |||||
| return Promise.resolve(); | |||||
| }, | |||||
| }, | }, | ||||
| ]} | ]} | ||||
| > | > | ||||
| <Input.TextArea | |||||
| placeholder="请输入模型简介" | |||||
| showCount | |||||
| maxLength={256} | |||||
| autoSize={{ minRows: 2, maxRows: 6 }} | |||||
| allowClear | |||||
| /> | |||||
| <Input placeholder="请输入模型版本" showCount allowClear maxLength={64} /> | |||||
| </Form.Item> | </Form.Item> | ||||
| {/* <Form.Item label="可见范围" name="available_range"> | |||||
| <Radio.Group> | |||||
| <Radio value="0">仅自己可见</Radio> | |||||
| <Radio value="1">工作空间可见</Radio> | |||||
| </Radio.Group> | |||||
| </Form.Item> */} | |||||
| <Form.Item label="模型框架" name="model_type"> | <Form.Item label="模型框架" name="model_type"> | ||||
| <Select | <Select | ||||
| allowClear | allowClear | ||||
| placeholder="请选择模型类型" | placeholder="请选择模型类型" | ||||
| options={typeList} | options={typeList} | ||||
| fieldNames={{ label: 'name', value: 'id' }} | |||||
| fieldNames={{ label: 'name', value: 'name' }} | |||||
| optionFilterProp="name" | optionFilterProp="name" | ||||
| showSearch | showSearch | ||||
| /> | /> | ||||
| @@ -142,11 +132,39 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps) | |||||
| allowClear | allowClear | ||||
| placeholder="请选择模型标签" | placeholder="请选择模型标签" | ||||
| options={tagList} | options={tagList} | ||||
| fieldNames={{ label: 'name', value: 'id' }} | |||||
| fieldNames={{ label: 'name', value: 'name' }} | |||||
| optionFilterProp="name" | optionFilterProp="name" | ||||
| showSearch | showSearch | ||||
| /> | /> | ||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item | |||||
| label="模型简介" | |||||
| name="description" | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: '请输入模型简介', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Input.TextArea | |||||
| placeholder="请输入模型简介" | |||||
| maxLength={200} | |||||
| autoSize={{ minRows: 2, maxRows: 6 }} | |||||
| showCount | |||||
| allowClear | |||||
| /> | |||||
| </Form.Item> | |||||
| <Form.Item | |||||
| label="可见性" | |||||
| name="is_public" | |||||
| rules={[{ required: true, message: '请选择可见性' }]} | |||||
| > | |||||
| <Radio.Group> | |||||
| <Radio value={false}>私有</Radio> | |||||
| <Radio value={true}>公开</Radio> | |||||
| </Radio.Group> | |||||
| </Form.Item> | |||||
| <Form.Item | <Form.Item | ||||
| label="模型文件" | label="模型文件" | ||||
| name="fileList" | name="fileList" | ||||
| @@ -60,7 +60,7 @@ function AddVersionModal({ | |||||
| const onFinish = (formData: any) => { | const onFinish = (formData: any) => { | ||||
| const fileList: UploadFile[] = formData['fileList'] ?? []; | const fileList: UploadFile[] = formData['fileList'] ?? []; | ||||
| if (validateUploadFiles(fileList)) { | if (validateUploadFiles(fileList)) { | ||||
| const dataset_version_vos = fileList.map((item) => { | |||||
| const version_vos = fileList.map((item) => { | |||||
| const data = item.response?.data?.[0] ?? {}; | const data = item.response?.data?.[0] ?? {}; | ||||
| return { | return { | ||||
| file_name: data.fileName, | file_name: data.fileName, | ||||
| @@ -71,7 +71,7 @@ function AddVersionModal({ | |||||
| const params = { | const params = { | ||||
| id: resourceId, | id: resourceId, | ||||
| identifier, | identifier, | ||||
| dataset_version_vos, | |||||
| [config.filePropKey]: version_vos, | |||||
| ...omit(formData, 'fileList'), | ...omit(formData, 'fileList'), | ||||
| }; | }; | ||||
| createDatasetVersion(params); | createDatasetVersion(params); | ||||
| @@ -123,7 +123,7 @@ function AddVersionModal({ | |||||
| { | { | ||||
| validator: (_rule, value) => { | validator: (_rule, value) => { | ||||
| if (value === 'master') { | if (value === 'master') { | ||||
| return Promise.reject(`版本号不能为 master`); | |||||
| return Promise.reject(`版本不能为 master`); | |||||
| } | } | ||||
| return Promise.resolve(); | return Promise.resolve(); | ||||
| }, | }, | ||||
| @@ -127,14 +127,12 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||||
| const deleteVersion = async () => { | const deleteVersion = async () => { | ||||
| const request = config.deleteVersion; | const request = config.deleteVersion; | ||||
| const params = { | const params = { | ||||
| identifier: info.identifier, | |||||
| owner: info.owner, | |||||
| ...pick(info, ['id', 'owner', 'identifier', 'relative_paths']), | |||||
| version, | version, | ||||
| }; | }; | ||||
| const [res] = await to(request(params)); | const [res] = await to(request(params)); | ||||
| if (res) { | if (res) { | ||||
| message.success('删除成功'); | message.success('删除成功'); | ||||
| setVersion(undefined); | |||||
| getVersionList(pick(info, ['owner', 'identifier'])); | getVersionList(pick(info, ['owner', 'identifier'])); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -175,8 +173,8 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||||
| children: ( | children: ( | ||||
| <ModelEvolution | <ModelEvolution | ||||
| resourceId={resourceId} | resourceId={resourceId} | ||||
| versionList={versionList} | |||||
| version={version} | version={version} | ||||
| identifier={info.identifier} | |||||
| isActive={activeTab === ResourceInfoTabKeys.Evolution} | isActive={activeTab === ResourceInfoTabKeys.Evolution} | ||||
| onVersionChange={handleVersionChange} | onVersionChange={handleVersionChange} | ||||
| ></ModelEvolution> | ></ModelEvolution> | ||||
| @@ -184,22 +182,22 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||||
| }); | }); | ||||
| } | } | ||||
| const infoTypePropertyName = config.infoTypePropertyName as keyof ResourceData; | |||||
| const infoTagPropertyName = config.infoTagPropertyName as keyof ResourceData; | |||||
| const typePropertyName = config.typeParamKey as keyof ResourceData; | |||||
| const tagPropertyName = config.tagParamKey as keyof ResourceData; | |||||
| return ( | return ( | ||||
| <div className={styles['resource-info']}> | <div className={styles['resource-info']}> | ||||
| <div className={styles['resource-info__top']}> | <div className={styles['resource-info__top']}> | ||||
| <Flex align="center" gap={10} style={{ marginBottom: '20px' }}> | <Flex align="center" gap={10} style={{ marginBottom: '20px' }}> | ||||
| <div className={styles['resource-info__top__name']}>{info.name}</div> | <div className={styles['resource-info__top__name']}>{info.name}</div> | ||||
| {info[infoTypePropertyName] && ( | |||||
| {info[typePropertyName] && ( | |||||
| <div className={styles['resource-info__top__tag']}> | <div className={styles['resource-info__top__tag']}> | ||||
| {(info[infoTypePropertyName] as string) || '--'} | |||||
| {(info[typePropertyName] as string) || '--'} | |||||
| </div> | </div> | ||||
| )} | )} | ||||
| {info[infoTagPropertyName] && ( | |||||
| {info[tagPropertyName] && ( | |||||
| <div className={styles['resource-info__top__tag']}> | <div className={styles['resource-info__top__tag']}> | ||||
| {(info[infoTagPropertyName] as string) || '--'} | |||||
| {(info[tagPropertyName] as string) || '--'} | |||||
| </div> | </div> | ||||
| )} | )} | ||||
| </Flex> | </Flex> | ||||
| @@ -8,8 +8,24 @@ type ResourceIntroProps = { | |||||
| info: ResourceData; | info: ResourceData; | ||||
| }; | }; | ||||
| function ResourceIntro({ info }: ResourceIntroProps) { | |||||
| const basicDatas: BasicInfoData[] = [ | |||||
| const formatArray = (arr?: string[]) => { | |||||
| if (!arr || arr.length === 0) { | |||||
| return '--'; | |||||
| } | |||||
| return arr.join('\n'); | |||||
| }; | |||||
| const formatMap = (map?: Record<string, string>) => { | |||||
| if (!map || Object.keys(map).length === 0) { | |||||
| return '--'; | |||||
| } | |||||
| return Object.entries(map) | |||||
| .map(([key, value]) => `${key} = ${value}`) | |||||
| .join('\n'); | |||||
| }; | |||||
| function ResourceIntro({ resourceType, info }: ResourceIntroProps) { | |||||
| const datasetDatas: BasicInfoData[] = [ | |||||
| { | { | ||||
| label: '数据集名称', | label: '数据集名称', | ||||
| value: info.name, | value: info.name, | ||||
| @@ -52,6 +68,80 @@ function ResourceIntro({ info }: ResourceIntroProps) { | |||||
| }, | }, | ||||
| ]; | ]; | ||||
| const modelDatas: BasicInfoData[] = [ | |||||
| { | |||||
| label: '模型名称', | |||||
| value: info.name, | |||||
| }, | |||||
| { | |||||
| label: '版本', | |||||
| value: info.version, | |||||
| }, | |||||
| { | |||||
| label: '创建人', | |||||
| value: info.create_by, | |||||
| }, | |||||
| { | |||||
| label: '更新时间', | |||||
| value: info.update_time, | |||||
| }, | |||||
| { | |||||
| label: '训练镜像', | |||||
| value: info.image, | |||||
| }, | |||||
| { | |||||
| label: '训练代码', | |||||
| value: info.code, | |||||
| }, | |||||
| { | |||||
| label: '训练数据集', | |||||
| value: info.train_datasets, | |||||
| format: formatArray, | |||||
| }, | |||||
| { | |||||
| label: '测试数据集', | |||||
| value: info.test_datasets, | |||||
| format: formatArray, | |||||
| }, | |||||
| { | |||||
| label: '参数', | |||||
| value: info.params, | |||||
| format: formatMap, | |||||
| }, | |||||
| { | |||||
| label: '指标', | |||||
| value: info.metrics, | |||||
| format: formatMap, | |||||
| }, | |||||
| { | |||||
| label: '训练任务', | |||||
| value: info.train_task, | |||||
| }, | |||||
| { | |||||
| label: '模型来源', | |||||
| value: info.model_source, | |||||
| }, | |||||
| { | |||||
| label: '模型框架', | |||||
| value: info.model_type, | |||||
| }, | |||||
| { | |||||
| label: '模型能力', | |||||
| value: info.model_tag, | |||||
| }, | |||||
| { | |||||
| label: '模型描述', | |||||
| value: info.description, | |||||
| }, | |||||
| { | |||||
| label: '版本描述', | |||||
| value: info.version_desc, | |||||
| }, | |||||
| ]; | |||||
| const basicDatas: BasicInfoData[] = | |||||
| resourceType === ResourceType.Dataset ? datasetDatas : modelDatas; | |||||
| return ( | return ( | ||||
| <div className={styles['resource-intro']}> | <div className={styles['resource-intro']}> | ||||
| <SubAreaTitle | <SubAreaTitle | ||||
| @@ -60,7 +150,7 @@ function ResourceIntro({ info }: ResourceIntroProps) { | |||||
| style={{ marginBottom: '26px' }} | style={{ marginBottom: '26px' }} | ||||
| ></SubAreaTitle> | ></SubAreaTitle> | ||||
| <div className={styles['resource-intro__basic']}> | <div className={styles['resource-intro__basic']}> | ||||
| <BasicInfo datas={basicDatas}></BasicInfo> | |||||
| <BasicInfo datas={basicDatas} labelWidth={86}></BasicInfo> | |||||
| </div> | </div> | ||||
| <SubAreaTitle | <SubAreaTitle | ||||
| title="实例用法" | title="实例用法" | ||||
| @@ -40,7 +40,7 @@ function ResourceItem({ item, isPublic, onClick, onRemove }: ResourceItemProps) | |||||
| <Flex justify="space-between"> | <Flex justify="space-between"> | ||||
| <div className={styles['resource-item__time']}> | <div className={styles['resource-item__time']}> | ||||
| <img style={{ width: '17px', marginRight: '6px' }} src={creatByImg} alt="" /> | <img style={{ width: '17px', marginRight: '6px' }} src={creatByImg} alt="" /> | ||||
| <span>{item.create_by}</span> | |||||
| <span>{item.create_by ?? ''}</span> | |||||
| </div> | </div> | ||||
| <div className={styles['resource-item__time']}> | <div className={styles['resource-item__time']}> | ||||
| <img style={{ width: '12px', marginRight: '5px' }} src={clock} alt="" /> | <img style={{ width: '12px', marginRight: '5px' }} src={clock} alt="" /> | ||||
| @@ -87,15 +87,11 @@ function ResourceList( | |||||
| const params: Record<string, any> = { | const params: Record<string, any> = { | ||||
| page: pagination.current! - 1, | page: pagination.current! - 1, | ||||
| size: pagination.pageSize, | size: pagination.pageSize, | ||||
| is_public: isPublic, | |||||
| [config.typeParamKey]: dataType, | [config.typeParamKey]: dataType, | ||||
| [config.tagParamKey]: dataTag, | [config.tagParamKey]: dataTag, | ||||
| name: searchText !== '' ? searchText : undefined, | name: searchText !== '' ? searchText : undefined, | ||||
| }; | }; | ||||
| if (resourceType === ResourceType.Dataset) { | |||||
| params['is_public'] = isPublic; | |||||
| } else { | |||||
| params['available_range'] = isPublic ? 1 : 0; | |||||
| } | |||||
| const request = config.getList; | const request = config.getList; | ||||
| const [res] = await to(request(params)); | const [res] = await to(request(params)); | ||||
| if (res && res.data && res.data.content) { | if (res && res.data && res.data.content) { | ||||
| @@ -108,7 +104,7 @@ function ResourceList( | |||||
| }; | }; | ||||
| // 删除请求 | // 删除请求 | ||||
| const deleteRecord = async (params: { owner: string; identifier: string }) => { | |||||
| const deleteRecord = async (params: { owner: string; identifier: string; repo_id?: number }) => { | |||||
| const request = config.deleteRecord; | const request = config.deleteRecord; | ||||
| const [res] = await to(request(params)); | const [res] = await to(request(params)); | ||||
| if (res) { | if (res) { | ||||
| @@ -127,7 +123,7 @@ function ResourceList( | |||||
| modalConfirm({ | modalConfirm({ | ||||
| title: config.deleteModalTitle, | title: config.deleteModalTitle, | ||||
| onOk: () => { | onOk: () => { | ||||
| deleteRecord(pick(record, ['owner', 'identifier'])); | |||||
| deleteRecord(pick(record, ['owner', 'identifier', 'id'])); | |||||
| }, | }, | ||||
| }); | }); | ||||
| }; | }; | ||||
| @@ -223,6 +219,9 @@ function ResourceList( | |||||
| className={styles['resource-list__empty']} | className={styles['resource-list__empty']} | ||||
| type={EmptyType.NoData} | type={EmptyType.NoData} | ||||
| title="暂无数据" | title="暂无数据" | ||||
| content={'很抱歉,没有搜索到您想要的内容\n建议刷新试试'} | |||||
| hasFooter={true} | |||||
| onRefresh={getDataList} | |||||
| /> | /> | ||||
| )} | )} | ||||
| </div> | </div> | ||||
| @@ -17,7 +17,8 @@ type ResourceVersionProps = { | |||||
| }; | }; | ||||
| function ResourceVersion({ resourceType, info }: ResourceVersionProps) { | function ResourceVersion({ resourceType, info }: ResourceVersionProps) { | ||||
| const config = resourceConfig[resourceType]; | const config = resourceConfig[resourceType]; | ||||
| const fileList = info.dataset_version_vos ?? []; | |||||
| const filePropKey = config.filePropKey as keyof ResourceData; | |||||
| const fileList = (info[filePropKey] ?? []) as ResourceFileData[]; | |||||
| fileList.forEach((item) => (item.update_time = info.update_time)); | fileList.forEach((item) => (item.update_time = info.update_time)); | ||||
| // 全部导出 | // 全部导出 | ||||
| @@ -97,7 +98,7 @@ function ResourceVersion({ resourceType, info }: ResourceVersionProps) { | |||||
| </Button> | </Button> | ||||
| </Flex> | </Flex> | ||||
| </Flex> | </Flex> | ||||
| <Table columns={columns} dataSource={fileList} pagination={false} rowKey="id" /> | |||||
| <Table columns={columns} dataSource={fileList} pagination={false} rowKey="url" /> | |||||
| </div> | </div> | ||||
| ); | ); | ||||
| } | } | ||||
| @@ -2,7 +2,7 @@ import KFIcon from '@/components/KFIcon'; | |||||
| import { CommonTabKeys } from '@/enums'; | import { CommonTabKeys } from '@/enums'; | ||||
| import { | import { | ||||
| addDatasetVersion, | addDatasetVersion, | ||||
| addModelsVersionDetail, | |||||
| addModelVersion, | |||||
| deleteDataset, | deleteDataset, | ||||
| deleteDatasetVersion, | deleteDatasetVersion, | ||||
| deleteModel, | deleteModel, | ||||
| @@ -10,9 +10,9 @@ import { | |||||
| getDatasetInfo, | getDatasetInfo, | ||||
| getDatasetList, | getDatasetList, | ||||
| getDatasetVersionList, | getDatasetVersionList, | ||||
| getModelById, | |||||
| getModelInfo, | |||||
| getModelList, | getModelList, | ||||
| getModelVersionsById, | |||||
| getModelVersionList, | |||||
| } from '@/services/dataset/index.js'; | } from '@/services/dataset/index.js'; | ||||
| import type { TabsProps } from 'antd'; | import type { TabsProps } from 'antd'; | ||||
| @@ -31,7 +31,7 @@ type ResourceTypeInfo = { | |||||
| name: string; // 名称 | name: string; // 名称 | ||||
| typeParamKey: string; // 类型参数名称,获取资源列表接口使用 | typeParamKey: string; // 类型参数名称,获取资源列表接口使用 | ||||
| tagParamKey: string; // 标签参数名称,获取资源列表接口使用 | tagParamKey: string; // 标签参数名称,获取资源列表接口使用 | ||||
| fileReqParamKey: 'models_id' | 'dataset_id'; // 文件请求参数名称,获取文件列表接口使用 | |||||
| filePropKey: string; | |||||
| tabItems: TabsProps['items']; // tab 列表 | tabItems: TabsProps['items']; // tab 列表 | ||||
| typeTitle: string; // 类型标题 | typeTitle: string; // 类型标题 | ||||
| tagTitle: string; // 标签标题 | tagTitle: string; // 标签标题 | ||||
| @@ -40,13 +40,10 @@ type ResourceTypeInfo = { | |||||
| prefix: string; // 图片资源、详情 url 的前缀 | prefix: string; // 图片资源、详情 url 的前缀 | ||||
| deleteModalTitle: string; // 删除弹框的title | deleteModalTitle: string; // 删除弹框的title | ||||
| addBtnTitle: string; // 新增按钮的title | addBtnTitle: string; // 新增按钮的title | ||||
| idParamKey: 'models_id' | 'dataset_id'; // 新建版本、删除版本接口,版本 id 的参数名称 | |||||
| uploadAction: string; // 上传接口 url | uploadAction: string; // 上传接口 url | ||||
| uploadAccept?: string; // 上传文件类型 | uploadAccept?: string; // 上传文件类型 | ||||
| downloadAllAction: string; // 批量下载接口 url | downloadAllAction: string; // 批量下载接口 url | ||||
| downloadSingleAction: string; // 单个下载接口 url | downloadSingleAction: string; // 单个下载接口 url | ||||
| infoTypePropertyName: string; // 详情数据中,类型属性名称 | |||||
| infoTagPropertyName: string; // 详情数据中,标签属性名称 | |||||
| }; | }; | ||||
| export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = { | export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = { | ||||
| @@ -60,7 +57,7 @@ export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = { | |||||
| name: '数据集', | name: '数据集', | ||||
| typeParamKey: 'data_type', | typeParamKey: 'data_type', | ||||
| tagParamKey: 'data_tag', | tagParamKey: 'data_tag', | ||||
| fileReqParamKey: 'dataset_id', | |||||
| filePropKey: 'dataset_version_vos', | |||||
| tabItems: [ | tabItems: [ | ||||
| { | { | ||||
| key: CommonTabKeys.Public, | key: CommonTabKeys.Public, | ||||
| @@ -80,25 +77,22 @@ export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = { | |||||
| prefix: 'dataset', | prefix: 'dataset', | ||||
| deleteModalTitle: '确定删除该条数据集实例吗?', | deleteModalTitle: '确定删除该条数据集实例吗?', | ||||
| addBtnTitle: '新建数据集', | addBtnTitle: '新建数据集', | ||||
| idParamKey: 'dataset_id', | |||||
| uploadAction: '/api/mmp/newdataset/upload', | uploadAction: '/api/mmp/newdataset/upload', | ||||
| uploadAccept: '.zip,.tgz', | uploadAccept: '.zip,.tgz', | ||||
| downloadAllAction: '/api/mmp/newdataset/downloadAllFiles', | downloadAllAction: '/api/mmp/newdataset/downloadAllFiles', | ||||
| downloadSingleAction: '/api/mmp/newdataset/downloadSinggerFile', | downloadSingleAction: '/api/mmp/newdataset/downloadSinggerFile', | ||||
| infoTypePropertyName: 'data_type', | |||||
| infoTagPropertyName: 'data_tag', | |||||
| }, | }, | ||||
| [ResourceType.Model]: { | [ResourceType.Model]: { | ||||
| getList: getModelList, | getList: getModelList, | ||||
| getVersions: getModelVersionsById, | |||||
| getVersions: getModelVersionList, | |||||
| deleteRecord: deleteModel, | deleteRecord: deleteModel, | ||||
| addVersion: addModelsVersionDetail, | |||||
| addVersion: addModelVersion, | |||||
| deleteVersion: deleteModelVersion, | deleteVersion: deleteModelVersion, | ||||
| getInfo: getModelById, | |||||
| getInfo: getModelInfo, | |||||
| name: '模型', | name: '模型', | ||||
| typeParamKey: 'model_type', | typeParamKey: 'model_type', | ||||
| tagParamKey: 'model_tag', | tagParamKey: 'model_tag', | ||||
| fileReqParamKey: 'models_id', | |||||
| filePropKey: 'model_version_vos', | |||||
| tabItems: [ | tabItems: [ | ||||
| { | { | ||||
| key: CommonTabKeys.Public, | key: CommonTabKeys.Public, | ||||
| @@ -118,13 +112,10 @@ export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = { | |||||
| prefix: 'model', | prefix: 'model', | ||||
| deleteModalTitle: '确定删除该条模型实例吗?', | deleteModalTitle: '确定删除该条模型实例吗?', | ||||
| addBtnTitle: '新建模型', | addBtnTitle: '新建模型', | ||||
| idParamKey: 'models_id', | |||||
| uploadAction: '/api/mmp/models/upload', | |||||
| uploadAction: '/api/mmp/newmodel/upload', | |||||
| uploadAccept: undefined, | uploadAccept: undefined, | ||||
| downloadAllAction: '/api/mmp/models/downloadAllFiles', | |||||
| downloadSingleAction: '/api/mmp/models/download_model', | |||||
| infoTypePropertyName: 'model_type_name', | |||||
| infoTagPropertyName: 'model_tag_name', | |||||
| downloadAllAction: '/api/mmp/newmodel/downloadAllFiles', | |||||
| downloadSingleAction: '/api/mmp/newmodel/downloadSingleFile', | |||||
| }, | }, | ||||
| }; | }; | ||||
| @@ -136,27 +127,39 @@ export type CategoryData = { | |||||
| path: string; | path: string; | ||||
| }; | }; | ||||
| // 资源数据 | |||||
| // 数据集、模型列表数据 | |||||
| export type ResourceData = { | export type ResourceData = { | ||||
| id: number; | id: number; | ||||
| name: string; | name: string; | ||||
| identifier: string; | identifier: string; | ||||
| description: string; | |||||
| create_by: string; | |||||
| owner: string; | owner: string; | ||||
| update_time: string; | |||||
| time_ago: string; | |||||
| version: string; | |||||
| is_public: boolean; | is_public: boolean; | ||||
| model_type_name?: string; | |||||
| model_tag_name?: string; | |||||
| data_type?: string; | |||||
| data_tag?: string; | |||||
| version?: string; | |||||
| description?: string; | |||||
| create_by?: string; | |||||
| update_time?: string; | |||||
| time_ago?: string; | |||||
| version_desc?: string; | version_desc?: string; | ||||
| processing_code?: string; | |||||
| dataset_source?: string; | |||||
| usage?: string; | usage?: string; | ||||
| relative_paths?: string; | |||||
| // 数据集 | |||||
| data_type?: string; // 数据集分类 | |||||
| data_tag?: string; // 研究方向 | |||||
| processing_code?: string; // 处理代码 | |||||
| dataset_source?: string; // 数据来源 | |||||
| dataset_version_vos: ResourceFileData[]; | dataset_version_vos: ResourceFileData[]; | ||||
| // 模型 | |||||
| model_type?: string; // 模型框架 | |||||
| model_tag?: string; // 模型能力 | |||||
| image?: string; // 训练镜像 | |||||
| code?: string; // 训练镜像 | |||||
| train_datasets?: string[]; // 训练数据集 | |||||
| test_datasets?: string[]; // 测试数据集 | |||||
| params?: Record<string, string>; // 参数 | |||||
| metrics?: Record<string, string>; // 指标 | |||||
| train_task?: string; // 训练任务 | |||||
| model_source?: string; // 模型来源 | |||||
| model_version_vos: ResourceFileData[]; | |||||
| }; | }; | ||||
| // 版本数据 | // 版本数据 | ||||
| @@ -2,10 +2,10 @@ import editExperimentIcon from '@/assets/img/edit-experiment.png'; | |||||
| import KFModal from '@/components/KFModal'; | import KFModal from '@/components/KFModal'; | ||||
| import { type ResourceData } from '@/pages/Dataset/config'; | import { type ResourceData } from '@/pages/Dataset/config'; | ||||
| import { | import { | ||||
| addModelsVersionDetail, | |||||
| addModelVersion, | |||||
| exportModelReq, | exportModelReq, | ||||
| getModelList, | getModelList, | ||||
| getModelVersionsById, | |||||
| getModelVersionList, | |||||
| } from '@/services/dataset'; | } from '@/services/dataset'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { InfoCircleOutlined } from '@ant-design/icons'; | import { InfoCircleOutlined } from '@ant-design/icons'; | ||||
| @@ -85,7 +85,7 @@ function ExportModelModal({ path, onOk, ...rest }: ExportModelModalProps) { | |||||
| // 获取模型版本列表 | // 获取模型版本列表 | ||||
| const getModelVersions = async (id: number) => { | const getModelVersions = async (id: number) => { | ||||
| const [res] = await to(getModelVersionsById(id)); | |||||
| const [res] = await to(getModelVersionList(id)); | |||||
| if (res && res.data) { | if (res && res.data) { | ||||
| setVersions(res.data); | setVersions(res.data); | ||||
| } | } | ||||
| @@ -118,7 +118,7 @@ function ExportModelModal({ path, onOk, ...rest }: ExportModelModalProps) { | |||||
| // 创建模型版本 | // 创建模型版本 | ||||
| const createModelVersion = async (params: CreateModelVersionParams[]) => { | const createModelVersion = async (params: CreateModelVersionParams[]) => { | ||||
| const [res] = await to(addModelsVersionDetail(params)); | |||||
| const [res] = await to(addModelVersion(params)); | |||||
| if (res) { | if (res) { | ||||
| onOk(); | onOk(); | ||||
| } | } | ||||
| @@ -5,13 +5,11 @@ | |||||
| */ | */ | ||||
| import { useEffectWhen } from '@/hooks'; | import { useEffectWhen } from '@/hooks'; | ||||
| import { ResourceVersionData } from '@/pages/Dataset/config'; | |||||
| import { getModelAtlasReq } from '@/services/dataset/index.js'; | import { getModelAtlasReq } from '@/services/dataset/index.js'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import G6, { G6GraphEvent, Graph, INode } from '@antv/g6'; | import G6, { G6GraphEvent, Graph, INode } from '@antv/g6'; | ||||
| // @ts-ignore | |||||
| import { Flex, Select } from 'antd'; | |||||
| import { Flex } from 'antd'; | |||||
| import { useEffect, useRef, useState } from 'react'; | import { useEffect, useRef, useState } from 'react'; | ||||
| import GraphLegend from '../GraphLegend'; | import GraphLegend from '../GraphLegend'; | ||||
| import NodeTooltips from '../NodeTooltips'; | import NodeTooltips from '../NodeTooltips'; | ||||
| @@ -29,7 +27,7 @@ import { | |||||
| type modeModelEvolutionProps = { | type modeModelEvolutionProps = { | ||||
| resourceId: number; | resourceId: number; | ||||
| versionList: ResourceVersionData[]; | |||||
| identifier: string; | |||||
| version?: string; | version?: string; | ||||
| isActive: boolean; | isActive: boolean; | ||||
| onVersionChange: (version: string) => void; | onVersionChange: (version: string) => void; | ||||
| @@ -38,7 +36,7 @@ type modeModelEvolutionProps = { | |||||
| let graph: Graph; | let graph: Graph; | ||||
| function ModelEvolution({ | function ModelEvolution({ | ||||
| resourceId, | resourceId, | ||||
| versionList, | |||||
| identifier, | |||||
| version, | version, | ||||
| isActive, | isActive, | ||||
| onVersionChange, | onVersionChange, | ||||
| @@ -217,7 +215,8 @@ function ModelEvolution({ | |||||
| // 获取模型依赖 | // 获取模型依赖 | ||||
| const getModelAtlas = async () => { | const getModelAtlas = async () => { | ||||
| const params = { | const params = { | ||||
| current_model_id: resourceId, | |||||
| id: resourceId, | |||||
| identifier, | |||||
| version, | version, | ||||
| }; | }; | ||||
| const [res] = await to(getModelAtlasReq(params)); | const [res] = await to(getModelAtlasReq(params)); | ||||
| @@ -250,15 +249,6 @@ function ModelEvolution({ | |||||
| return ( | return ( | ||||
| <div className={styles['model-evolution']}> | <div className={styles['model-evolution']}> | ||||
| <Flex align="center" className={styles['model-evolution__top']}> | <Flex align="center" className={styles['model-evolution__top']}> | ||||
| <span style={{ marginRight: '10px' }}>版本号:</span> | |||||
| <Select | |||||
| placeholder="请选择版本号" | |||||
| style={{ width: '160px', marginRight: '20px' }} | |||||
| value={version} | |||||
| allowClear | |||||
| onChange={onVersionChange} | |||||
| options={versionList} | |||||
| /> | |||||
| <GraphLegend style={{ marginRight: 0, marginLeft: 'auto' }}></GraphLegend> | <GraphLegend style={{ marginRight: 0, marginLeft: 'auto' }}></GraphLegend> | ||||
| </Flex> | </Flex> | ||||
| <div className={styles['model-evolution__graph']} id="canvas" ref={graphRef}></div> | <div className={styles['model-evolution__graph']} id="canvas" ref={graphRef}></div> | ||||
| @@ -1,8 +1,8 @@ | |||||
| import ResourceIntro from '@/pages/Dataset/components/ResourceIntro'; | |||||
| import ResourceInfo from '@/pages/Dataset/components/ResourceInfo'; | |||||
| import { ResourceType } from '@/pages/Dataset/config'; | import { ResourceType } from '@/pages/Dataset/config'; | ||||
| function ModelIntro() { | |||||
| return <ResourceIntro resourceType={ResourceType.Model} />; | |||||
| function ModelInfo() { | |||||
| return <ResourceInfo resourceType={ResourceType.Model} />; | |||||
| } | } | ||||
| export default ModelIntro; | |||||
| export default ModelInfo; | |||||
| @@ -9,9 +9,9 @@ import { | |||||
| getDatasetInfo, | getDatasetInfo, | ||||
| getDatasetList, | getDatasetList, | ||||
| getDatasetVersionList, | getDatasetVersionList, | ||||
| getModelInfo, | |||||
| getModelList, | getModelList, | ||||
| getModelVersionIdList, | |||||
| getModelVersionsById, | |||||
| getModelVersionList, | |||||
| } from '@/services/dataset/index.js'; | } from '@/services/dataset/index.js'; | ||||
| import { getMirrorListReq, getMirrorVersionListReq } from '@/services/mirror'; | import { getMirrorListReq, getMirrorVersionListReq } from '@/services/mirror'; | ||||
| import type { TabsProps, TreeDataNode } from 'antd'; | import type { TabsProps, TreeDataNode } from 'antd'; | ||||
| @@ -121,7 +121,7 @@ const convertMirrorVersionToTreeData = ( | |||||
| // export const selectorTypeConfig: Record<ResourceSelectorType, SelectorTypeInfo> = { | // export const selectorTypeConfig: Record<ResourceSelectorType, SelectorTypeInfo> = { | ||||
| // [ResourceSelectorType.Model]: { | // [ResourceSelectorType.Model]: { | ||||
| // getList: getModelList, | // getList: getModelList, | ||||
| // getVersions: getModelVersionsById, | |||||
| // getVersions: getModelVersionList, | |||||
| // getFiles: getModelVersionIdList, | // getFiles: getModelVersionIdList, | ||||
| // name: '模型', | // name: '模型', | ||||
| @@ -226,7 +226,6 @@ export class DatasetSelector implements SelectorTypeInfo { | |||||
| } | } | ||||
| } | } | ||||
| async getVersions(parentKey: string, parentNode: ResourceData) { | async getVersions(parentKey: string, parentNode: ResourceData) { | ||||
| // const obj = parseDatasetVersionId(id); | |||||
| const res = await getDatasetVersionList(pick(parentNode, ['owner', 'identifier'])); | const res = await getDatasetVersionList(pick(parentNode, ['owner', 'identifier'])); | ||||
| if (res && res.data) { | if (res && res.data) { | ||||
| const list = res.data; | const list = res.data; | ||||
| @@ -237,7 +236,6 @@ export class DatasetSelector implements SelectorTypeInfo { | |||||
| } | } | ||||
| async getFiles(_parentKey: string, parentNode: ResourceData & ResourceVersionData) { | async getFiles(_parentKey: string, parentNode: ResourceData & ResourceVersionData) { | ||||
| //const obj = parseDatasetVersionId(parentKey); | |||||
| const params = pick(parentNode, ['owner', 'identifier', 'id', 'name', 'version']); | const params = pick(parentNode, ['owner', 'identifier', 'id', 'name', 'version']); | ||||
| const res = await getDatasetInfo(params); | const res = await getDatasetInfo(params); | ||||
| if (res && res.data) { | if (res && res.data) { | ||||
| @@ -275,32 +273,31 @@ export class ModelSelector implements SelectorTypeInfo { | |||||
| const list = res.data.content || []; | const list = res.data.content || []; | ||||
| return convertDatasetToTreeData(list); | return convertDatasetToTreeData(list); | ||||
| } else { | } else { | ||||
| return Promise.reject('获取数据集列表失败'); | |||||
| return Promise.reject('获取模型列表失败'); | |||||
| } | } | ||||
| } | } | ||||
| async getVersions(key: string, parentNode: ResourceData) { | async getVersions(key: string, parentNode: ResourceData) { | ||||
| //const obj = parseDatasetVersionId(id); | |||||
| const res = await getModelVersionIdList(pick(parentNode, ['owner', 'identifier'])); | |||||
| const res = await getModelVersionList(pick(parentNode, ['owner', 'identifier'])); | |||||
| if (res && res.data) { | if (res && res.data) { | ||||
| const list = res.data.content || []; | |||||
| const list = res.data; | |||||
| return convertDatasetVersionToTreeData(key, parentNode, list); | return convertDatasetVersionToTreeData(key, parentNode, list); | ||||
| } else { | } else { | ||||
| return Promise.reject('获取数据集版本列表失败'); | |||||
| return Promise.reject('获取模型版本列表失败'); | |||||
| } | } | ||||
| } | } | ||||
| async getFiles(_parentKey: string, parentNode: ResourceData & ResourceVersionData) { | async getFiles(_parentKey: string, parentNode: ResourceData & ResourceVersionData) { | ||||
| // const obj = parseDatasetVersionId(id); | |||||
| const params = pick(parentNode, ['owner', 'identifier', 'id', 'name', 'version']); | const params = pick(parentNode, ['owner', 'identifier', 'id', 'name', 'version']); | ||||
| const res = await getModelVersionsById(params); | |||||
| const res = await getModelInfo(params); | |||||
| if (res && res.data) { | if (res && res.data) { | ||||
| const list = res.data.dataset_version_vos || []; | |||||
| const path = res.data.relative_paths || ''; | |||||
| const list = res.data.model_version_vos || []; | |||||
| return { | return { | ||||
| path: res.data.path || '', | |||||
| path, | |||||
| content: list, | content: list, | ||||
| }; | }; | ||||
| } else { | } else { | ||||
| return Promise.reject('获取数据集文件列表失败'); | |||||
| return Promise.reject('获取模型文件列表失败'); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -348,13 +345,13 @@ export class MirrorSelector implements SelectorTypeInfo { | |||||
| } | } | ||||
| } | } | ||||
| async getFiles(parentKey: string, parentNode: MirrorVersionData) { | |||||
| async getFiles(_parentKey: string, parentNode: MirrorVersionData) { | |||||
| const { url } = parentNode; | const { url } = parentNode; | ||||
| return { | return { | ||||
| path: url, | path: url, | ||||
| content: [ | content: [ | ||||
| { | { | ||||
| id: parentKey, | |||||
| url: url, | |||||
| file_name: `${url}`, | file_name: `${url}`, | ||||
| }, | }, | ||||
| ], | ], | ||||
| @@ -34,6 +34,11 @@ | |||||
| border-bottom: 1px solid @border-color-secondary; | border-bottom: 1px solid @border-color-secondary; | ||||
| border-radius: 0; | border-radius: 0; | ||||
| } | } | ||||
| &__tree-title { | |||||
| display: inline-block; | |||||
| .singleLine(); | |||||
| } | |||||
| } | } | ||||
| &__right { | &__right { | ||||
| @@ -67,8 +72,3 @@ | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| .kf-tree-title { | |||||
| display: inline-block; | |||||
| .singleLine(); | |||||
| } | |||||
| @@ -6,6 +6,7 @@ | |||||
| import KFModal from '@/components/KFModal'; | import KFModal from '@/components/KFModal'; | ||||
| import { CommonTabKeys } from '@/enums'; | import { CommonTabKeys } from '@/enums'; | ||||
| import { ResourceFileData } from '@/pages/Dataset/config'; | |||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { Icon } from '@umijs/max'; | import { Icon } from '@umijs/max'; | ||||
| import type { GetRef, ModalProps, TreeDataNode, TreeProps } from 'antd'; | import type { GetRef, ModalProps, TreeDataNode, TreeProps } from 'antd'; | ||||
| @@ -24,11 +25,6 @@ export type ResourceSelectorResponse = { | |||||
| activeTab: CommonTabKeys; // 是我的还是公开的 | activeTab: CommonTabKeys; // 是我的还是公开的 | ||||
| }; | }; | ||||
| type ResourceFile = { | |||||
| id: number; // 文件 id | |||||
| file_name: string; // 文件 name | |||||
| }; | |||||
| export interface ResourceSelectorModalProps extends Omit<ModalProps, 'onOk'> { | export interface ResourceSelectorModalProps extends Omit<ModalProps, 'onOk'> { | ||||
| type: ResourceSelectorType; // 数据集\模型\镜像 | type: ResourceSelectorType; // 数据集\模型\镜像 | ||||
| defaultExpandedKeys?: React.Key[]; | defaultExpandedKeys?: React.Key[]; | ||||
| @@ -76,7 +72,7 @@ function ResourceSelectorModal({ | |||||
| const [checkedKeys, setCheckedKeys] = useState<React.Key[]>([]); | const [checkedKeys, setCheckedKeys] = useState<React.Key[]>([]); | ||||
| const [loadedKeys, setLoadedKeys] = useState<React.Key[]>([]); | const [loadedKeys, setLoadedKeys] = useState<React.Key[]>([]); | ||||
| const [originTreeData, setOriginTreeData] = useState<TreeDataNode[]>([]); | const [originTreeData, setOriginTreeData] = useState<TreeDataNode[]>([]); | ||||
| const [files, setFiles] = useState<ResourceFile[]>([]); | |||||
| const [files, setFiles] = useState<ResourceFileData[]>([]); | |||||
| const [versionPath, setVersionPath] = useState(''); | const [versionPath, setVersionPath] = useState(''); | ||||
| const [searchText, setSearchText] = useState(''); | const [searchText, setSearchText] = useState(''); | ||||
| const [firstLoadList, setFirstLoadList] = useState(false); | const [firstLoadList, setFirstLoadList] = useState(false); | ||||
| @@ -175,6 +171,7 @@ function ResourceSelectorModal({ | |||||
| const lastNode = checkedNodes[checkedNodes.length - 1]; | const lastNode = checkedNodes[checkedNodes.length - 1]; | ||||
| getFiles(last, lastNode); | getFiles(last, lastNode); | ||||
| } else { | } else { | ||||
| setVersionPath(''); | |||||
| setFiles([]); | setFiles([]); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -278,26 +275,24 @@ function ResourceSelectorModal({ | |||||
| loadedKeys={loadedKeys} | loadedKeys={loadedKeys} | ||||
| expandedKeys={expandedKeys} | expandedKeys={expandedKeys} | ||||
| onExpand={onExpand} | onExpand={onExpand} | ||||
| checkable | |||||
| titleRender={(nodeData) => { | titleRender={(nodeData) => { | ||||
| console.log(nodeData); | |||||
| return ( | return ( | ||||
| <span | <span | ||||
| className={styles['kf-tree-title']} | |||||
| className={styles['model-selector__left__tree-title']} | |||||
| style={{ width: nodeData.isLeaf ? '370px' : '420px' }} | style={{ width: nodeData.isLeaf ? '370px' : '420px' }} | ||||
| > | > | ||||
| {nodeData.title as string} | {nodeData.title as string} | ||||
| </span> | </span> | ||||
| ); | ); | ||||
| }} | }} | ||||
| checkable | |||||
| /> | /> | ||||
| </div> | </div> | ||||
| <div className={styles['model-selector__right']}> | <div className={styles['model-selector__right']}> | ||||
| <div className={styles['model-selector__right__title']}>{fileTitle}</div> | <div className={styles['model-selector__right__title']}>{fileTitle}</div> | ||||
| <div className={styles['model-selector__right__files']}> | <div className={styles['model-selector__right__files']}> | ||||
| {files.map((v) => ( | {files.map((v) => ( | ||||
| <div key={v.id} className={styles['model-selector__right__files__file']}> | |||||
| <div key={v.url} className={styles['model-selector__right__files__file']}> | |||||
| {v.file_name} | {v.file_name} | ||||
| </div> | </div> | ||||
| ))} | ))} | ||||
| @@ -11,8 +11,8 @@ const MissingPage = () => { | |||||
| title="敬请期待~" | title="敬请期待~" | ||||
| content={'很抱歉,您访问的正在开发中,\n请耐心等待。'} | content={'很抱歉,您访问的正在开发中,\n请耐心等待。'} | ||||
| hasFooter={true} | hasFooter={true} | ||||
| backTitle="返回首页" | |||||
| onBack={() => navigate('/')} | |||||
| buttonTitle="返回首页" | |||||
| onRefresh={() => navigate('/')} | |||||
| ></KFEmpty> | ></KFEmpty> | ||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -26,6 +26,7 @@ const popupError = (error: string, skipErrorHandler: boolean | undefined = false | |||||
| * @doc https://umijs.org/docs/max/request#配置 | * @doc https://umijs.org/docs/max/request#配置 | ||||
| */ | */ | ||||
| export const requestConfig: RequestConfig = { | export const requestConfig: RequestConfig = { | ||||
| timeout: 120 * 1000, | |||||
| requestInterceptors: [ | requestInterceptors: [ | ||||
| (url: string, options: AxiosRequestConfig) => { | (url: string, options: AxiosRequestConfig) => { | ||||
| const headers = options.headers ?? {}; | const headers = options.headers ?? {}; | ||||
| @@ -27,12 +27,9 @@ export function getDatasetInfo(params) { | |||||
| } | } | ||||
| // 新增数据集 | // 新增数据集 | ||||
| export function addDateset(data) { | |||||
| export function addDataset(data) { | |||||
| return request(`/api/mmp/newdataset/addDatasetAndVersion`, { | return request(`/api/mmp/newdataset/addDatasetAndVersion`, { | ||||
| method: 'POST', | method: 'POST', | ||||
| headers: { | |||||
| 'Content-Type': 'application/json;charset=UTF-8', | |||||
| }, | |||||
| data, | data, | ||||
| }); | }); | ||||
| } | } | ||||
| @@ -54,21 +51,10 @@ export function getDatasetVersionList(params) { | |||||
| }); | }); | ||||
| } | } | ||||
| // 查询数据集版本文件列表 | |||||
| // export function getDatasetVersionFiles(params) { | |||||
| // return request(`/api/mmp/datasetVersion/versions`, { | |||||
| // method: 'GET', | |||||
| // params, | |||||
| // }); | |||||
| // } | |||||
| // 新增数据集版本 | // 新增数据集版本 | ||||
| export function addDatasetVersion(data) { | export function addDatasetVersion(data) { | ||||
| return request(`/api/mmp/newdataset/addVersion`, { | return request(`/api/mmp/newdataset/addVersion`, { | ||||
| method: 'POST', | method: 'POST', | ||||
| headers: { | |||||
| 'Content-Type': 'application/json;charset=UTF-8', | |||||
| }, | |||||
| data, | data, | ||||
| }); | }); | ||||
| } | } | ||||
| @@ -101,7 +87,7 @@ export function deleteDatasetVersion(params) { | |||||
| // 分页查询模型列表 | // 分页查询模型列表 | ||||
| export function getModelList(params) { | export function getModelList(params) { | ||||
| return request(`/api/mmp/models`, { | |||||
| return request(`/api/mmp/newmodel/queryModels`, { | |||||
| method: 'GET', | method: 'GET', | ||||
| params, | params, | ||||
| }); | }); | ||||
| @@ -109,68 +95,59 @@ export function getModelList(params) { | |||||
| // 新增模型 | // 新增模型 | ||||
| export function addModel(data) { | export function addModel(data) { | ||||
| return request(`/api/mmp/models/addModelAndVersion`, { | |||||
| return request(`/api/mmp/newmodel/addModel`, { | |||||
| method: 'POST', | method: 'POST', | ||||
| headers: { | |||||
| 'Content-Type': 'application/json;charset=UTF-8', | |||||
| }, | |||||
| data, | data, | ||||
| }); | }); | ||||
| } | } | ||||
| // 查询模型简介 | |||||
| export function getModelById(id) { | |||||
| return request(`/api/mmp/models/${id}`, { | |||||
| method: 'GET', | |||||
| // 删除模型 | |||||
| export function deleteModel(params) { | |||||
| return request(`/api/mmp/newmodel/delete`, { | |||||
| method: 'DELETE', | |||||
| params, | |||||
| }); | }); | ||||
| } | } | ||||
| // 查询模型版本列表 | |||||
| export function getModelVersionsById(id) { | |||||
| return request(`/api/mmp/models/versions/${id}`, { | |||||
| // 查询模型详情 | |||||
| export function getModelInfo(params) { | |||||
| return request(`/api/mmp/newmodel/getModelDetail`, { | |||||
| method: 'GET', | method: 'GET', | ||||
| params, | |||||
| }); | }); | ||||
| } | } | ||||
| // 根据版本查询文件列表 | |||||
| export function getModelVersionIdList(params) { | |||||
| return request(`/api/mmp/modelsVersion/versions`, { | |||||
| // 查询模型版本列表 | |||||
| export function getModelVersionList(params) { | |||||
| return request(`/api/mmp/newmodel/getVersionList`, { | |||||
| method: 'GET', | method: 'GET', | ||||
| params, | params, | ||||
| }); | }); | ||||
| } | } | ||||
| // 新增模型版本 | // 新增模型版本 | ||||
| export function addModelsVersionDetail(data) { | |||||
| return request(`/api/mmp/modelsVersion/addModelVersions`, { | |||||
| export function addModelVersion(data) { | |||||
| return request(`/api/mmp/newmodel/addVersion`, { | |||||
| method: 'POST', | method: 'POST', | ||||
| headers: { | |||||
| 'Content-Type': 'application/json;charset=UTF-8', | |||||
| }, | |||||
| data, | data, | ||||
| }); | }); | ||||
| } | } | ||||
| // 删除模型 | |||||
| export function deleteModel(id) { | |||||
| return request(`/api/mmp/models/${id}`, { | |||||
| method: 'DELETE', | |||||
| }); | |||||
| } | |||||
| // 删除模型版本 | // 删除模型版本 | ||||
| export function deleteModelVersion(params) { | export function deleteModelVersion(params) { | ||||
| return request(`/api/mmp/modelsVersion/deleteVersion`, { | |||||
| return request(`/api/mmp/newmodel/deleteVersion`, { | |||||
| method: 'DELETE', | method: 'DELETE', | ||||
| params, | params, | ||||
| }); | }); | ||||
| } | } | ||||
| // 获取模型依赖 | // 获取模型依赖 | ||||
| export function getModelAtlasReq(data) { | |||||
| return request(`/api/mmp/modelDependency/queryModelAtlas`, { | |||||
| method: 'POST', | |||||
| data | |||||
| export function getModelAtlasReq(params) { | |||||
| return request(`/api/mmp/newmodel/getModelDependencyTree`, { | |||||
| method: 'GET', | |||||
| params | |||||
| }); | }); | ||||
| } | } | ||||
| @@ -190,3 +190,13 @@ export const fittingString = (str: string, maxWidth: number, fontSize: number) = | |||||
| }); | }); | ||||
| return res; | return res; | ||||
| }; | }; | ||||
| /** | |||||
| * Checks if a given string is empty, undefined, or null. | |||||
| * | |||||
| * @param {any} str - the string to be checked | |||||
| * @return {boolean} true if the string is empty, undefined, or null, false otherwise | |||||
| */ | |||||
| export const isEmptyString = (str: any): boolean => { | |||||
| return str === '' || str === undefined || str === null; | |||||
| }; | |||||