| @@ -93,7 +93,7 @@ function AddCodeConfigModal({ opType, codeConfigData, onOk, ...rest }: AddCodeCo | |||||
| return ( | return ( | ||||
| <KFModal | <KFModal | ||||
| {...rest} | {...rest} | ||||
| title="新建代码配置" | |||||
| title={opType === OperationType.Create ? '新建代码配置' : '修改代码配置'} | |||||
| image={require('@/assets/img/create-experiment.png')} | image={require('@/assets/img/create-experiment.png')} | ||||
| width={825} | width={825} | ||||
| okButtonProps={{ | okButtonProps={{ | ||||
| @@ -39,7 +39,6 @@ function CategoryItem({ resourceType, item, isSelected, onClick }: CategoryItemP | |||||
| > | > | ||||
| {item.name} | {item.name} | ||||
| </Typography.Text> | </Typography.Text> | ||||
| {/* <span className={styles['category-item__name']}>{item.name}</span> */} | |||||
| </div> | </div> | ||||
| ); | ); | ||||
| } | } | ||||
| @@ -22,6 +22,8 @@ import { useEffect, useState } from 'react'; | |||||
| import AddVersionModal from '../AddVersionModal'; | import AddVersionModal from '../AddVersionModal'; | ||||
| import ResourceIntro from '../ResourceIntro'; | import ResourceIntro from '../ResourceIntro'; | ||||
| import ResourceVersion from '../ResourceVersion'; | import ResourceVersion from '../ResourceVersion'; | ||||
| import VersionCompareModal from '../VersionCompareModal'; | |||||
| import VersionSelectorModal from '../VersionSelectorModal'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| // 这里值小写是因为值会写在 url 中 | // 这里值小写是因为值会写在 url 中 | ||||
| @@ -47,7 +49,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||||
| const name = searchParams.get('name') || ''; | const name = searchParams.get('name') || ''; | ||||
| const owner = searchParams.get('owner') || ''; | const owner = searchParams.get('owner') || ''; | ||||
| const identifier = searchParams.get('identifier') || ''; | const identifier = searchParams.get('identifier') || ''; | ||||
| const is_public = searchParams.get('is_public') || ''; | |||||
| const is_public = (searchParams.get('is_public') || '') === 'true'; | |||||
| const [versionList, setVersionList] = useState<ResourceVersionData[]>([]); | const [versionList, setVersionList] = useState<ResourceVersionData[]>([]); | ||||
| const [version, setVersion] = useState<string | undefined>(undefined); | const [version, setVersion] = useState<string | undefined>(undefined); | ||||
| const [activeTab, setActiveTab] = useState<string>(defaultTab); | const [activeTab, setActiveTab] = useState<string>(defaultTab); | ||||
| @@ -67,7 +69,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||||
| name, | name, | ||||
| identifier, | identifier, | ||||
| version, | version, | ||||
| is_public: is_public === 'true', | |||||
| is_public: is_public, | |||||
| }); | }); | ||||
| } | } | ||||
| }, [version]); | }, [version]); | ||||
| @@ -121,7 +123,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||||
| resoureName: info.name, | resoureName: info.name, | ||||
| owner: info.owner, | owner: info.owner, | ||||
| identifier: info.identifier, | identifier: info.identifier, | ||||
| is_public: info.is_public, | |||||
| is_public: is_public, | |||||
| onOk: () => { | onOk: () => { | ||||
| getVersionList(); | getVersionList(); | ||||
| close(); | close(); | ||||
| @@ -129,6 +131,29 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||||
| }); | }); | ||||
| }; | }; | ||||
| // 选择版本 | |||||
| const showVersionSelector = () => { | |||||
| const { close } = openAntdModal(VersionSelectorModal, { | |||||
| versions: versionList, | |||||
| onOk: (version: string[]) => { | |||||
| showVersionComparison(version); | |||||
| close(); | |||||
| }, | |||||
| }); | |||||
| }; | |||||
| // 版本对比 | |||||
| const showVersionComparison = (versions: string[]) => { | |||||
| openAntdModal(VersionCompareModal, { | |||||
| versions: versions, | |||||
| resourceType: resourceType, | |||||
| repo_id: resourceId, | |||||
| owner: info.owner, | |||||
| identifier: info.identifier, | |||||
| is_public: is_public, | |||||
| }); | |||||
| }; | |||||
| // 版本变化 | // 版本变化 | ||||
| const handleVersionChange = (value: string) => { | const handleVersionChange = (value: string) => { | ||||
| setVersion(value); | setVersion(value); | ||||
| @@ -237,6 +262,9 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||||
| <Button type="default" onClick={showModal} icon={<KFIcon type="icon-xinjian2" />}> | <Button type="default" onClick={showModal} icon={<KFIcon type="icon-xinjian2" />}> | ||||
| 创建新版本 | 创建新版本 | ||||
| </Button> | </Button> | ||||
| <Button type="default" style={{ marginLeft: '20px' }} onClick={showVersionSelector}> | |||||
| 版本对比 | |||||
| </Button> | |||||
| <Button | <Button | ||||
| type="default" | type="default" | ||||
| style={{ marginLeft: 'auto', marginRight: 0 }} | style={{ marginLeft: 'auto', marginRight: 0 }} | ||||
| @@ -24,7 +24,7 @@ type ResourceIntroProps = { | |||||
| version?: string; | version?: string; | ||||
| }; | }; | ||||
| const formatDataset = (datasets?: DatasetData[]) => { | |||||
| export const formatDataset = (datasets?: DatasetData[]) => { | |||||
| if (!datasets || datasets.length === 0) { | if (!datasets || datasets.length === 0) { | ||||
| return undefined; | return undefined; | ||||
| } | } | ||||
| @@ -34,7 +34,7 @@ const formatDataset = (datasets?: DatasetData[]) => { | |||||
| })); | })); | ||||
| }; | }; | ||||
| const getProjectUrl = (project?: ProjectDependency) => { | |||||
| export const getProjectUrl = (project?: ProjectDependency) => { | |||||
| if (!project || !project.url || !project.branch) { | if (!project || !project.url || !project.branch) { | ||||
| return undefined; | return undefined; | ||||
| } | } | ||||
| @@ -42,7 +42,7 @@ const getProjectUrl = (project?: ProjectDependency) => { | |||||
| return getGitUrl(url, branch); | return getGitUrl(url, branch); | ||||
| }; | }; | ||||
| const formatProject = (project?: ProjectDependency) => { | |||||
| export const formatProject = (project?: ProjectDependency) => { | |||||
| if (!project) { | if (!project) { | ||||
| return undefined; | return undefined; | ||||
| } | } | ||||
| @@ -52,7 +52,7 @@ const formatProject = (project?: ProjectDependency) => { | |||||
| }; | }; | ||||
| }; | }; | ||||
| const formatTrainTask = (task?: TrainTask) => { | |||||
| export const formatTrainTask = (task?: TrainTask) => { | |||||
| if (!task) { | if (!task) { | ||||
| return undefined; | return undefined; | ||||
| } | } | ||||
| @@ -62,7 +62,7 @@ const formatTrainTask = (task?: TrainTask) => { | |||||
| }; | }; | ||||
| }; | }; | ||||
| const formatSource = (source?: string) => { | |||||
| export const formatSource = (source?: string) => { | |||||
| if (source === DataSource.Create) { | if (source === DataSource.Create) { | ||||
| return '用户上传'; | return '用户上传'; | ||||
| } else if (source === DataSource.HandExport) { | } else if (source === DataSource.HandExport) { | ||||
| @@ -0,0 +1,120 @@ | |||||
| @purple-color: #6516ff; | |||||
| .title(@color, @background) { | |||||
| width: 100%; | |||||
| margin-bottom: 20px; | |||||
| color: @color; | |||||
| font-weight: 500; | |||||
| font-size: @font-size; | |||||
| line-height: 42px; | |||||
| text-align: center; | |||||
| background: @background; | |||||
| border-radius: 4px 4px 0 0; | |||||
| .singleLine(); | |||||
| } | |||||
| .text() { | |||||
| margin-bottom: 20px !important; | |||||
| color: @text-color-secondary; | |||||
| font-size: 13px; | |||||
| line-height: 22px; | |||||
| word-break: break-all; | |||||
| .singleLine(); | |||||
| } | |||||
| .version-container(@background) { | |||||
| flex: 1; | |||||
| min-width: 0; | |||||
| background: @background; | |||||
| border-radius: 4px; | |||||
| } | |||||
| .version-compare { | |||||
| :global { | |||||
| .ant-modal-content { | |||||
| padding: 40px 40px 25px !important; | |||||
| } | |||||
| .ant-modal-header { | |||||
| margin-bottom: 20px !important; | |||||
| } | |||||
| .kf-modal-title { | |||||
| color: @text-color; | |||||
| font-weight: 500; | |||||
| font-size: 20px; | |||||
| } | |||||
| } | |||||
| &__container { | |||||
| display: flex; | |||||
| flex-wrap: nowrap; | |||||
| gap: 0 5px; | |||||
| align-items: stretch; | |||||
| height: 100%; | |||||
| } | |||||
| &__fields { | |||||
| flex: none; | |||||
| width: 117px; | |||||
| padding: 0 15px; | |||||
| background: rgba(255, 255, 255, 0.1); | |||||
| border: 1px solid .addAlpha(@primary-color, 0.2) []; | |||||
| border-radius: 4px; | |||||
| box-shadow: 0px 3px 6px .addAlpha(@primary-color, 0.1) [] inset; | |||||
| &__title { | |||||
| margin-bottom: 20px; | |||||
| color: @text-color; | |||||
| font-size: @font-size; | |||||
| line-height: 42px; | |||||
| } | |||||
| &__text { | |||||
| .text(); | |||||
| &--different { | |||||
| color: @error-color; | |||||
| } | |||||
| } | |||||
| } | |||||
| &__left { | |||||
| .version-container(.addAlpha(@primary-color, 0.04) []); | |||||
| &__title { | |||||
| .title(@primary-color, linear-gradient( | |||||
| 159.9deg,rgba(138, 177, 255, 0.5) 0%, | |||||
| rgba(22, 100, 255, 0.5) 100% | |||||
| )); | |||||
| } | |||||
| &__text { | |||||
| padding: 0 15px; | |||||
| text-align: center; | |||||
| .text(); | |||||
| &--different { | |||||
| color: @primary-color; | |||||
| } | |||||
| } | |||||
| } | |||||
| &__right { | |||||
| .version-container(rgba(100, 30, 237, 0.04)); | |||||
| &__title { | |||||
| .title(@purple-color, linear-gradient( | |||||
| 159.9deg, | |||||
| rgba(193, 138, 255, 0.5) 0%, | |||||
| rgba(146, 22, 255, 0.5) 100% | |||||
| )); | |||||
| } | |||||
| &__text { | |||||
| padding: 0 15px; | |||||
| text-align: center; | |||||
| .text(); | |||||
| &--different { | |||||
| color: @purple-color; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,236 @@ | |||||
| import KFModal from '@/components/KFModal'; | |||||
| import { | |||||
| DatasetData, | |||||
| ModelData, | |||||
| ProjectDependency, | |||||
| ResourceType, | |||||
| TrainTask, | |||||
| resourceConfig, | |||||
| } from '@/pages/Dataset/config'; | |||||
| import { isEmpty } from '@/utils'; | |||||
| import { to } from '@/utils/promise'; | |||||
| import { Typography, type ModalProps } from 'antd'; | |||||
| import classNames from 'classnames'; | |||||
| import { useEffect, useMemo, useState } from 'react'; | |||||
| import { formatSource } from '../ResourceIntro'; | |||||
| import styles from './index.less'; | |||||
| type CompareData = { | |||||
| differences: Record<string, any>; | |||||
| version1: DatasetData | ModelData; | |||||
| version2: DatasetData | ModelData; | |||||
| }; | |||||
| type FiledType<T extends DatasetData | ModelData> = { | |||||
| key: keyof T; | |||||
| text: string; | |||||
| format?: (data: any) => any; | |||||
| }; | |||||
| interface VersionCompareModalProps extends Omit<ModalProps, 'onOk'> { | |||||
| versions: string[]; | |||||
| resourceType: ResourceType; | |||||
| identifier: string; | |||||
| is_public: boolean; | |||||
| owner: string; | |||||
| repo_id: number; | |||||
| } | |||||
| const formatDataset = (datasets?: DatasetData[]) => { | |||||
| if (!datasets || datasets.length === 0) { | |||||
| return undefined; | |||||
| } | |||||
| return datasets.map((item) => `${item.name}:${item.version}`).join(','); | |||||
| }; | |||||
| const formatProject = (project?: ProjectDependency) => { | |||||
| if (!project) { | |||||
| return undefined; | |||||
| } | |||||
| return project.name; | |||||
| }; | |||||
| export const formatTrainTask = (task?: TrainTask) => { | |||||
| if (!task) { | |||||
| return undefined; | |||||
| } | |||||
| return `${task.name}:${task.ins_id}`; | |||||
| }; | |||||
| function VersionCompareModal({ | |||||
| versions, | |||||
| resourceType, | |||||
| identifier, | |||||
| is_public, | |||||
| owner, | |||||
| repo_id, | |||||
| ...rest | |||||
| }: VersionCompareModalProps) { | |||||
| const [compareData, setCompareData] = useState<CompareData | undefined>(undefined); | |||||
| const config = resourceConfig[resourceType]; | |||||
| const fields: FiledType<DatasetData>[] | FiledType<ModelData>[] = useMemo( | |||||
| () => | |||||
| resourceType === ResourceType.Dataset | |||||
| ? [ | |||||
| { | |||||
| key: 'dataset_source', | |||||
| text: '数据来源', | |||||
| format: formatSource, | |||||
| }, | |||||
| { | |||||
| key: 'train_task', | |||||
| text: '训练任务', | |||||
| format: formatTrainTask, | |||||
| }, | |||||
| { | |||||
| key: 'processing_code', | |||||
| text: '处理代码', | |||||
| format: formatProject, | |||||
| }, | |||||
| { | |||||
| key: 'description', | |||||
| text: '版本描述', | |||||
| }, | |||||
| ] | |||||
| : [ | |||||
| { | |||||
| key: 'image', | |||||
| text: '训练镜像', | |||||
| }, | |||||
| { | |||||
| key: 'project_depency', | |||||
| text: '训练代码', | |||||
| format: formatProject, | |||||
| }, | |||||
| { | |||||
| key: 'train_datasets', | |||||
| text: '训练数据集', | |||||
| format: formatDataset, | |||||
| }, | |||||
| { | |||||
| key: 'test_datasets', | |||||
| text: '测试数据集', | |||||
| format: formatDataset, | |||||
| }, | |||||
| { | |||||
| key: 'model_source', | |||||
| text: '模型来源', | |||||
| format: formatSource, | |||||
| }, | |||||
| { | |||||
| key: 'train_task', | |||||
| text: '训练任务', | |||||
| format: formatTrainTask, | |||||
| }, | |||||
| { | |||||
| key: 'description', | |||||
| text: '版本描述', | |||||
| }, | |||||
| ], | |||||
| [], | |||||
| ); | |||||
| useEffect(() => { | |||||
| getServiceVersionCompare(); | |||||
| }, []); | |||||
| // 获取对比数据 | |||||
| const getServiceVersionCompare = async () => { | |||||
| const params = { | |||||
| versions, | |||||
| identifier, | |||||
| is_public, | |||||
| owner, | |||||
| repo_id, | |||||
| }; | |||||
| const request = config.compareVersion; | |||||
| const [res] = await to(request(params)); | |||||
| if (res && res.data) { | |||||
| setCompareData(res.data); | |||||
| } | |||||
| }; | |||||
| // 获取值 | |||||
| function getValue<T extends DatasetData | ModelData>( | |||||
| data: T, | |||||
| key: keyof T, | |||||
| format?: (data: any) => any, | |||||
| ) { | |||||
| const value = data[key]; | |||||
| return format ? format(value) : value; | |||||
| } | |||||
| const { | |||||
| version1: v1 = {} as DatasetData | ModelData, | |||||
| version2: v2 = {} as DatasetData | ModelData, | |||||
| differences = {}, | |||||
| } = compareData || {}; | |||||
| const isDifferent = (key: string) => { | |||||
| return Object.keys(differences).includes(key); | |||||
| }; | |||||
| return ( | |||||
| <KFModal | |||||
| {...rest} | |||||
| title={`${config.name}版本对比`} | |||||
| width={825} | |||||
| footer={null} | |||||
| className={styles['version-compare']} | |||||
| > | |||||
| <div className={styles['version-compare__container']}> | |||||
| <div className={styles['version-compare__fields']}> | |||||
| <div className={styles['version-compare__fields__title']}>基础版本号</div> | |||||
| {fields.map(({ key, text }) => ( | |||||
| <div | |||||
| className={classNames(styles['version-compare__fields__text'], { | |||||
| [styles['version-compare__fields__text--different']]: isDifferent(key), | |||||
| })} | |||||
| key={key} | |||||
| > | |||||
| {text} | |||||
| </div> | |||||
| ))} | |||||
| </div> | |||||
| <div className={styles['version-compare__left']}> | |||||
| <div className={styles['version-compare__left__title']}>{v1.version}</div> | |||||
| {fields.map(({ key, format }) => { | |||||
| const text = getValue(v1, key as keyof typeof v1, format); | |||||
| return ( | |||||
| <div | |||||
| key={key} | |||||
| className={classNames(styles['version-compare__left__text'], { | |||||
| [styles['version-compare__left__text--different']]: isDifferent(key), | |||||
| })} | |||||
| > | |||||
| <Typography.Text ellipsis={{ tooltip: text }} style={{ color: 'inherit' }}> | |||||
| {isEmpty(text) ? '--' : text} | |||||
| </Typography.Text> | |||||
| </div> | |||||
| ); | |||||
| })} | |||||
| </div> | |||||
| <div className={styles['version-compare__right']}> | |||||
| <div className={styles['version-compare__right__title']}>{v2.version}</div> | |||||
| {fields.map(({ key, format }) => { | |||||
| const text = getValue(v2, key as keyof typeof v2, format); | |||||
| return ( | |||||
| <div | |||||
| key={key} | |||||
| className={classNames(styles['version-compare__right__text'], { | |||||
| [styles['version-compare__right__text--different']]: isDifferent(key), | |||||
| })} | |||||
| > | |||||
| <Typography.Text ellipsis={{ tooltip: text }} style={{ color: 'inherit' }}> | |||||
| {isEmpty(text) ? '--' : text} | |||||
| </Typography.Text> | |||||
| </div> | |||||
| ); | |||||
| })} | |||||
| </div> | |||||
| </div> | |||||
| </KFModal> | |||||
| ); | |||||
| } | |||||
| export default VersionCompareModal; | |||||
| @@ -0,0 +1,59 @@ | |||||
| .version-selector-modal { | |||||
| :global { | |||||
| .ant-modal-content { | |||||
| padding: 40px !important; | |||||
| } | |||||
| .kf-modal-title { | |||||
| color: @text-color; | |||||
| font-weight: 500; | |||||
| font-size: 20px; | |||||
| } | |||||
| } | |||||
| } | |||||
| .version-selector { | |||||
| display: flex; | |||||
| flex-direction: row; | |||||
| flex-wrap: wrap; | |||||
| gap: 20px; | |||||
| width: 100%; | |||||
| padding: 30px 13px 20px; | |||||
| :global { | |||||
| .ant-checkbox-group { | |||||
| display: flex; | |||||
| flex-direction: column; | |||||
| gap: 10px 0; | |||||
| } | |||||
| } | |||||
| &__item { | |||||
| display: flex; | |||||
| flex-direction: column; | |||||
| align-items: center; | |||||
| width: 103px; | |||||
| padding: 15px; | |||||
| color: @text-color-secondary; | |||||
| font-size: 15px; | |||||
| border: 1px solid rgba(136, 149, 168, 0.16); | |||||
| border-radius: 6px; | |||||
| img { | |||||
| width: 26px; | |||||
| height: 26px; | |||||
| margin-bottom: 4px; | |||||
| } | |||||
| &:hover { | |||||
| color: @text-color-secondary; | |||||
| background-color: .addAlpha(@primary-color, 0.08) []; | |||||
| border: 1px solid transparent; | |||||
| } | |||||
| &--active { | |||||
| color: @primary-color !important; | |||||
| background-color: .addAlpha(@primary-color, 0.08) [] !important; | |||||
| border: 1px solid @primary-color !important; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,63 @@ | |||||
| import KFModal from '@/components/KFModal'; | |||||
| import { ResourceVersionData } from '@/pages/Dataset/config'; | |||||
| import { Typography, message, type ModalProps } from 'antd'; | |||||
| import classNames from 'classnames'; | |||||
| import { useState } from 'react'; | |||||
| import styles from './index.less'; | |||||
| interface VersionSelectorModalProps extends Omit<ModalProps, 'onOk'> { | |||||
| versions: ResourceVersionData[]; | |||||
| onOk: (versions: string[]) => void; | |||||
| } | |||||
| function VersionSelectorModal({ versions, onOk, ...rest }: VersionSelectorModalProps) { | |||||
| const [selVersions, setSelVersions] = useState<string[]>([]); | |||||
| const handleOk = () => { | |||||
| if (selVersions.length !== 2) { | |||||
| message.error('请选择两个版本进行对比'); | |||||
| return; | |||||
| } | |||||
| onOk(selVersions); | |||||
| }; | |||||
| const handleClick = (version: string) => { | |||||
| setSelVersions((prev) => { | |||||
| if (prev.includes(version)) { | |||||
| return prev.filter((item) => item !== version); | |||||
| } | |||||
| return [...prev, version]; | |||||
| }); | |||||
| }; | |||||
| return ( | |||||
| <KFModal | |||||
| {...rest} | |||||
| title="请选择版本" | |||||
| width={825} | |||||
| onOk={handleOk} | |||||
| className={styles['version-selector-modal']} | |||||
| > | |||||
| <div className={styles['version-selector']}> | |||||
| {versions.map((item) => { | |||||
| return ( | |||||
| <div | |||||
| key={item.name} | |||||
| className={classNames(styles['version-selector__item'], { | |||||
| [styles['version-selector__item--active']]: selVersions.includes(item.name), | |||||
| })} | |||||
| onClick={() => handleClick(item.name)} | |||||
| > | |||||
| <img src={require('@/assets/img/dataset-version.png')} alt="" draggable={false} /> | |||||
| <Typography.Text ellipsis={{ tooltip: item.name }} style={{ color: 'inherit' }}> | |||||
| {item.name} | |||||
| </Typography.Text> | |||||
| </div> | |||||
| ); | |||||
| })} | |||||
| </div> | |||||
| </KFModal> | |||||
| ); | |||||
| } | |||||
| export default VersionSelectorModal; | |||||
| @@ -3,6 +3,8 @@ import { CommonTabKeys } from '@/enums'; | |||||
| import { | import { | ||||
| addDatasetVersion, | addDatasetVersion, | ||||
| addModelVersion, | addModelVersion, | ||||
| compareDatasetVersion, | |||||
| compareModelVersion, | |||||
| deleteDataset, | deleteDataset, | ||||
| deleteDatasetVersion, | deleteDatasetVersion, | ||||
| deleteModel, | deleteModel, | ||||
| @@ -35,6 +37,7 @@ type ResourceTypeInfo = { | |||||
| addVersion: (params: any) => Promise<any>; // 新增版本 | addVersion: (params: any) => Promise<any>; // 新增版本 | ||||
| deleteVersion: (params: any) => Promise<any>; // 删除版本 | deleteVersion: (params: any) => Promise<any>; // 删除版本 | ||||
| getInfo: (params: any) => Promise<any>; // 获取详情 | getInfo: (params: any) => Promise<any>; // 获取详情 | ||||
| compareVersion: (params: any) => Promise<any>; // 版本对比 | |||||
| name: string; // 名称 | name: string; // 名称 | ||||
| typeParamKey: 'data_type' | 'model_type'; // 类型参数名称,获取资源列表接口使用 | typeParamKey: 'data_type' | 'model_type'; // 类型参数名称,获取资源列表接口使用 | ||||
| tagParamKey: 'data_tag' | 'model_tag'; // 标签参数名称,获取资源列表接口使用 | tagParamKey: 'data_tag' | 'model_tag'; // 标签参数名称,获取资源列表接口使用 | ||||
| @@ -63,6 +66,7 @@ export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = { | |||||
| addVersion: addDatasetVersion, | addVersion: addDatasetVersion, | ||||
| deleteVersion: deleteDatasetVersion, | deleteVersion: deleteDatasetVersion, | ||||
| getInfo: getDatasetInfo, | getInfo: getDatasetInfo, | ||||
| compareVersion: compareDatasetVersion, | |||||
| name: '数据集', | name: '数据集', | ||||
| typeParamKey: 'data_type', | typeParamKey: 'data_type', | ||||
| tagParamKey: 'data_tag', | tagParamKey: 'data_tag', | ||||
| @@ -100,6 +104,7 @@ export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = { | |||||
| addVersion: addModelVersion, | addVersion: addModelVersion, | ||||
| deleteVersion: deleteModelVersion, | deleteVersion: deleteModelVersion, | ||||
| getInfo: getModelInfo, | getInfo: getModelInfo, | ||||
| compareVersion: compareModelVersion, | |||||
| name: '模型', | name: '模型', | ||||
| typeParamKey: 'model_type', | typeParamKey: 'model_type', | ||||
| tagParamKey: 'model_tag', | tagParamKey: 'model_tag', | ||||
| @@ -58,18 +58,15 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr | |||||
| checkSingleMetrics, | checkSingleMetrics, | ||||
| ] = useCheck(allMetricsNames); | ] = useCheck(allMetricsNames); | ||||
| useEffect(() => { | |||||
| getModelPageVersions(); | |||||
| }, []); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| if (selectedMetrics.length !== 0 && selectedRowKeys.length !== 0) { | if (selectedMetrics.length !== 0 && selectedRowKeys.length !== 0) { | ||||
| getModelVersionsMetrics(); | getModelVersionsMetrics(); | ||||
| } else { | } else { | ||||
| setChartData(undefined); | setChartData(undefined); | ||||
| } | } | ||||
| }, [selectedMetrics, selectedRowKeys]); | |||||
| }, [selectedMetrics, selectedRowKeys, identifier, resourceId]); | |||||
| // 版本切换,自动勾选当前版本 | |||||
| useEffect(() => { | useEffect(() => { | ||||
| const curRow = tableData.find((item) => item.name === version); | const curRow = tableData.find((item) => item.name === version); | ||||
| if ( | if ( | ||||
| @@ -80,7 +77,11 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr | |||||
| ) { | ) { | ||||
| setSelectedRowKeys([version, ...selectedRowKeys]); | setSelectedRowKeys([version, ...selectedRowKeys]); | ||||
| } | } | ||||
| }, [version]); | |||||
| }, [tableData, version]); | |||||
| useEffect(() => { | |||||
| getModelPageVersions(); | |||||
| }, [pagination, identifier, owner]); | |||||
| // 获取模型版本列表,带有参数和指标数据 | // 获取模型版本列表,带有参数和指标数据 | ||||
| const getModelPageVersions = async () => { | const getModelPageVersions = async () => { | ||||
| @@ -3,6 +3,7 @@ import { ServiceRunStatus } from '@/enums'; | |||||
| import { useComputingResource } from '@/hooks/resource'; | import { useComputingResource } from '@/hooks/resource'; | ||||
| import { type ServiceVersionData } from '@/pages/ModelDeployment/types'; | import { type ServiceVersionData } from '@/pages/ModelDeployment/types'; | ||||
| import { getServiceVersionCompareReq } from '@/services/modelDeployment'; | import { getServiceVersionCompareReq } from '@/services/modelDeployment'; | ||||
| import { isEmpty } from '@/utils'; | |||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { Typography, type ModalProps } from 'antd'; | import { Typography, type ModalProps } from 'antd'; | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| @@ -24,7 +25,7 @@ type FiledType = { | |||||
| format?: (data: any) => any; | format?: (data: any) => any; | ||||
| }; | }; | ||||
| interface CreateMirrorModalProps extends Omit<ModalProps, 'onOk'> { | |||||
| interface VersionCompareModalProps extends Omit<ModalProps, 'onOk'> { | |||||
| version1: string; | version1: string; | ||||
| version2: string; | version2: string; | ||||
| } | } | ||||
| @@ -39,7 +40,7 @@ const formatEnvText = (env: Record<string, string>) => { | |||||
| .join(','); | .join(','); | ||||
| }; | }; | ||||
| function VersionCompareModal({ version1, version2, ...rest }: CreateMirrorModalProps) { | |||||
| function VersionCompareModal({ version1, version2, ...rest }: VersionCompareModalProps) { | |||||
| const [compareData, setCompareData] = useState<CompareData | undefined>(undefined); | const [compareData, setCompareData] = useState<CompareData | undefined>(undefined); | ||||
| const getResourceDescription = useComputingResource()[2]; | const getResourceDescription = useComputingResource()[2]; | ||||
| @@ -165,7 +166,7 @@ function VersionCompareModal({ version1, version2, ...rest }: CreateMirrorModalP | |||||
| })} | })} | ||||
| > | > | ||||
| <Typography.Text ellipsis={{ tooltip: text }} style={{ color: 'inherit' }}> | <Typography.Text ellipsis={{ tooltip: text }} style={{ color: 'inherit' }}> | ||||
| {text} | |||||
| {isEmpty(text) ? '--' : text} | |||||
| </Typography.Text> | </Typography.Text> | ||||
| </div> | </div> | ||||
| ); | ); | ||||
| @@ -183,7 +184,7 @@ function VersionCompareModal({ version1, version2, ...rest }: CreateMirrorModalP | |||||
| })} | })} | ||||
| > | > | ||||
| <Typography.Text ellipsis={{ tooltip: text }} style={{ color: 'inherit' }}> | <Typography.Text ellipsis={{ tooltip: text }} style={{ color: 'inherit' }}> | ||||
| {text} | |||||
| {isEmpty(text) ? '--' : text} | |||||
| </Typography.Text> | </Typography.Text> | ||||
| </div> | </div> | ||||
| ); | ); | ||||
| @@ -75,6 +75,14 @@ export function deleteDatasetVersion(params) { | |||||
| }); | }); | ||||
| } | } | ||||
| // 数据集版本对比 | |||||
| export function compareDatasetVersion(data) { | |||||
| return request(`/api/mmp/newdataset/getVersionsCompare`, { | |||||
| method: 'POST', | |||||
| data, | |||||
| }); | |||||
| } | |||||
| // ----------------------------模型--------------------------------- | // ----------------------------模型--------------------------------- | ||||
| // 分页查询模型列表 | // 分页查询模型列表 | ||||
| @@ -165,4 +173,12 @@ export function getModelVersionsMetricsReq(data) { | |||||
| method: 'POST', | method: 'POST', | ||||
| data | data | ||||
| }); | }); | ||||
| } | |||||
| // 模型版本对比 | |||||
| export function compareModelVersion(data) { | |||||
| return request(`/api/mmp/newmodel/getVersionsCompare`, { | |||||
| method: 'POST', | |||||
| data, | |||||
| }); | |||||
| } | } | ||||
| @@ -0,0 +1,23 @@ | |||||
| import { canBeConvertToDate } from '../src/utils/date'; | |||||
| describe('canBeConvertToDate()', () => { | |||||
| test('null', () => { | |||||
| expect(canBeConvertToDate(null)).toBe(false); | |||||
| }); | |||||
| test('undefined', () => { | |||||
| expect(canBeConvertToDate(undefined)).toBe(false); | |||||
| }); | |||||
| test('empty string', () => { | |||||
| expect(canBeConvertToDate('')).toBe(false); | |||||
| }); | |||||
| test('valid string', () => { | |||||
| expect(canBeConvertToDate('2024-10-10T10:10:10+08:00')).toBe(true); | |||||
| }); | |||||
| test('numeric', () => { | |||||
| expect(canBeConvertToDate(0)).toBe(true); | |||||
| }); | |||||
| }); | |||||