| @@ -93,7 +93,7 @@ function AddCodeConfigModal({ opType, codeConfigData, onOk, ...rest }: AddCodeCo | |||
| return ( | |||
| <KFModal | |||
| {...rest} | |||
| title="新建代码配置" | |||
| title={opType === OperationType.Create ? '新建代码配置' : '修改代码配置'} | |||
| image={require('@/assets/img/create-experiment.png')} | |||
| width={825} | |||
| okButtonProps={{ | |||
| @@ -39,7 +39,6 @@ function CategoryItem({ resourceType, item, isSelected, onClick }: CategoryItemP | |||
| > | |||
| {item.name} | |||
| </Typography.Text> | |||
| {/* <span className={styles['category-item__name']}>{item.name}</span> */} | |||
| </div> | |||
| ); | |||
| } | |||
| @@ -22,6 +22,8 @@ import { useEffect, useState } from 'react'; | |||
| import AddVersionModal from '../AddVersionModal'; | |||
| import ResourceIntro from '../ResourceIntro'; | |||
| import ResourceVersion from '../ResourceVersion'; | |||
| import VersionCompareModal from '../VersionCompareModal'; | |||
| import VersionSelectorModal from '../VersionSelectorModal'; | |||
| import styles from './index.less'; | |||
| // 这里值小写是因为值会写在 url 中 | |||
| @@ -47,7 +49,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| const name = searchParams.get('name') || ''; | |||
| const owner = searchParams.get('owner') || ''; | |||
| 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 [version, setVersion] = useState<string | undefined>(undefined); | |||
| const [activeTab, setActiveTab] = useState<string>(defaultTab); | |||
| @@ -67,7 +69,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| name, | |||
| identifier, | |||
| version, | |||
| is_public: is_public === 'true', | |||
| is_public: is_public, | |||
| }); | |||
| } | |||
| }, [version]); | |||
| @@ -121,7 +123,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| resoureName: info.name, | |||
| owner: info.owner, | |||
| identifier: info.identifier, | |||
| is_public: info.is_public, | |||
| is_public: is_public, | |||
| onOk: () => { | |||
| getVersionList(); | |||
| 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) => { | |||
| setVersion(value); | |||
| @@ -237,6 +262,9 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| <Button type="default" onClick={showModal} icon={<KFIcon type="icon-xinjian2" />}> | |||
| 创建新版本 | |||
| </Button> | |||
| <Button type="default" style={{ marginLeft: '20px' }} onClick={showVersionSelector}> | |||
| 版本对比 | |||
| </Button> | |||
| <Button | |||
| type="default" | |||
| style={{ marginLeft: 'auto', marginRight: 0 }} | |||
| @@ -24,7 +24,7 @@ type ResourceIntroProps = { | |||
| version?: string; | |||
| }; | |||
| const formatDataset = (datasets?: DatasetData[]) => { | |||
| export const formatDataset = (datasets?: DatasetData[]) => { | |||
| if (!datasets || datasets.length === 0) { | |||
| 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) { | |||
| return undefined; | |||
| } | |||
| @@ -42,7 +42,7 @@ const getProjectUrl = (project?: ProjectDependency) => { | |||
| return getGitUrl(url, branch); | |||
| }; | |||
| const formatProject = (project?: ProjectDependency) => { | |||
| export const formatProject = (project?: ProjectDependency) => { | |||
| if (!project) { | |||
| return undefined; | |||
| } | |||
| @@ -52,7 +52,7 @@ const formatProject = (project?: ProjectDependency) => { | |||
| }; | |||
| }; | |||
| const formatTrainTask = (task?: TrainTask) => { | |||
| export const formatTrainTask = (task?: TrainTask) => { | |||
| if (!task) { | |||
| return undefined; | |||
| } | |||
| @@ -62,7 +62,7 @@ const formatTrainTask = (task?: TrainTask) => { | |||
| }; | |||
| }; | |||
| const formatSource = (source?: string) => { | |||
| export const formatSource = (source?: string) => { | |||
| if (source === DataSource.Create) { | |||
| return '用户上传'; | |||
| } 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 { | |||
| addDatasetVersion, | |||
| addModelVersion, | |||
| compareDatasetVersion, | |||
| compareModelVersion, | |||
| deleteDataset, | |||
| deleteDatasetVersion, | |||
| deleteModel, | |||
| @@ -35,6 +37,7 @@ type ResourceTypeInfo = { | |||
| addVersion: (params: any) => Promise<any>; // 新增版本 | |||
| deleteVersion: (params: any) => Promise<any>; // 删除版本 | |||
| getInfo: (params: any) => Promise<any>; // 获取详情 | |||
| compareVersion: (params: any) => Promise<any>; // 版本对比 | |||
| name: string; // 名称 | |||
| typeParamKey: 'data_type' | 'model_type'; // 类型参数名称,获取资源列表接口使用 | |||
| tagParamKey: 'data_tag' | 'model_tag'; // 标签参数名称,获取资源列表接口使用 | |||
| @@ -63,6 +66,7 @@ export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = { | |||
| addVersion: addDatasetVersion, | |||
| deleteVersion: deleteDatasetVersion, | |||
| getInfo: getDatasetInfo, | |||
| compareVersion: compareDatasetVersion, | |||
| name: '数据集', | |||
| typeParamKey: 'data_type', | |||
| tagParamKey: 'data_tag', | |||
| @@ -100,6 +104,7 @@ export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = { | |||
| addVersion: addModelVersion, | |||
| deleteVersion: deleteModelVersion, | |||
| getInfo: getModelInfo, | |||
| compareVersion: compareModelVersion, | |||
| name: '模型', | |||
| typeParamKey: 'model_type', | |||
| tagParamKey: 'model_tag', | |||
| @@ -58,18 +58,15 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr | |||
| checkSingleMetrics, | |||
| ] = useCheck(allMetricsNames); | |||
| useEffect(() => { | |||
| getModelPageVersions(); | |||
| }, []); | |||
| useEffect(() => { | |||
| if (selectedMetrics.length !== 0 && selectedRowKeys.length !== 0) { | |||
| getModelVersionsMetrics(); | |||
| } else { | |||
| setChartData(undefined); | |||
| } | |||
| }, [selectedMetrics, selectedRowKeys]); | |||
| }, [selectedMetrics, selectedRowKeys, identifier, resourceId]); | |||
| // 版本切换,自动勾选当前版本 | |||
| useEffect(() => { | |||
| const curRow = tableData.find((item) => item.name === version); | |||
| if ( | |||
| @@ -80,7 +77,11 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr | |||
| ) { | |||
| setSelectedRowKeys([version, ...selectedRowKeys]); | |||
| } | |||
| }, [version]); | |||
| }, [tableData, version]); | |||
| useEffect(() => { | |||
| getModelPageVersions(); | |||
| }, [pagination, identifier, owner]); | |||
| // 获取模型版本列表,带有参数和指标数据 | |||
| const getModelPageVersions = async () => { | |||
| @@ -3,6 +3,7 @@ import { ServiceRunStatus } from '@/enums'; | |||
| import { useComputingResource } from '@/hooks/resource'; | |||
| import { type ServiceVersionData } from '@/pages/ModelDeployment/types'; | |||
| import { getServiceVersionCompareReq } from '@/services/modelDeployment'; | |||
| import { isEmpty } from '@/utils'; | |||
| import { to } from '@/utils/promise'; | |||
| import { Typography, type ModalProps } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| @@ -24,7 +25,7 @@ type FiledType = { | |||
| format?: (data: any) => any; | |||
| }; | |||
| interface CreateMirrorModalProps extends Omit<ModalProps, 'onOk'> { | |||
| interface VersionCompareModalProps extends Omit<ModalProps, 'onOk'> { | |||
| version1: string; | |||
| version2: string; | |||
| } | |||
| @@ -39,7 +40,7 @@ const formatEnvText = (env: Record<string, string>) => { | |||
| .join(','); | |||
| }; | |||
| function VersionCompareModal({ version1, version2, ...rest }: CreateMirrorModalProps) { | |||
| function VersionCompareModal({ version1, version2, ...rest }: VersionCompareModalProps) { | |||
| const [compareData, setCompareData] = useState<CompareData | undefined>(undefined); | |||
| const getResourceDescription = useComputingResource()[2]; | |||
| @@ -165,7 +166,7 @@ function VersionCompareModal({ version1, version2, ...rest }: CreateMirrorModalP | |||
| })} | |||
| > | |||
| <Typography.Text ellipsis={{ tooltip: text }} style={{ color: 'inherit' }}> | |||
| {text} | |||
| {isEmpty(text) ? '--' : text} | |||
| </Typography.Text> | |||
| </div> | |||
| ); | |||
| @@ -183,7 +184,7 @@ function VersionCompareModal({ version1, version2, ...rest }: CreateMirrorModalP | |||
| })} | |||
| > | |||
| <Typography.Text ellipsis={{ tooltip: text }} style={{ color: 'inherit' }}> | |||
| {text} | |||
| {isEmpty(text) ? '--' : text} | |||
| </Typography.Text> | |||
| </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', | |||
| 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); | |||
| }); | |||
| }); | |||