| @@ -9,6 +9,7 @@ import { | |||
| } from '@/utils/sessionStorage'; | |||
| import classNames from 'classnames'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { createPortal } from 'react-dom'; | |||
| import './index.less'; | |||
| export enum IframePageType { | |||
| @@ -61,7 +62,7 @@ function IframePage({ type, className, style }: IframePageProps) { | |||
| return ( | |||
| <div className={classNames('kf-iframe-page', className)} style={style}> | |||
| {loading && <KFSpin size="large" />} | |||
| {loading && createPortal(<KFSpin size="large" />, document.body)} | |||
| <FullScreenFrame url={iframeUrl} onload={hideLoading} onerror={hideLoading} /> | |||
| </div> | |||
| ); | |||
| @@ -14,7 +14,15 @@ const filterResourceStandard: SelectProps<string, ComputingResource>['filterOpti | |||
| }; | |||
| // id 从 number 转换为 string | |||
| const convertId = (item: any) => ({ ...item, id: `${item.id}-${item.identifier}` }); | |||
| const convertId = (item: any) => ({ | |||
| ...item, | |||
| id: JSON.stringify({ | |||
| id: item.id, | |||
| name: item.name, | |||
| identifier: item.identifier, | |||
| owner: item.owner, | |||
| }), | |||
| }); | |||
| export type SelectPropsConfig = { | |||
| getOptions: () => Promise<any>; // 获取下拉数据 | |||
| @@ -37,7 +37,7 @@ function ResourceSelect({ type, value, onChange, ...rest }: ResourceSelectProps) | |||
| onOk: (res) => { | |||
| setSelectedResource(res); | |||
| if (res) { | |||
| const { activeTab, id, name, version, path } = res; | |||
| const { activeTab, id, name, version, path, identifier, owner } = res; | |||
| if (type === ResourceSelectorType.Mirror) { | |||
| onChange?.({ | |||
| value: path, | |||
| @@ -50,8 +50,11 @@ function ResourceSelect({ type, value, onChange, ...rest }: ResourceSelectProps) | |||
| } else { | |||
| const jsonObj = { | |||
| id, | |||
| name, | |||
| version, | |||
| path, | |||
| identifier, | |||
| owner, | |||
| }; | |||
| const jsonObjStr = JSON.stringify(jsonObj); | |||
| const showValue = `${name}:${version}`; | |||
| @@ -34,12 +34,19 @@ | |||
| } | |||
| &__bottom { | |||
| position: relative; | |||
| height: calc(100% - 135px); | |||
| padding: 8px 30px 20px; | |||
| background: #ffffff; | |||
| border-radius: 10px; | |||
| box-shadow: 0px 2px 12px rgba(180, 182, 191, 0.09); | |||
| &__legend { | |||
| position: absolute; | |||
| top: 20px; | |||
| right: 30px; | |||
| } | |||
| :global { | |||
| .ant-tabs { | |||
| height: 100%; | |||
| @@ -11,14 +11,13 @@ import { | |||
| ResourceVersionData, | |||
| resourceConfig, | |||
| } from '@/pages/Dataset/config'; | |||
| import GraphLegend from '@/pages/Model/components/GraphLegend'; | |||
| import ModelEvolution from '@/pages/Model/components/ModelEvolution'; | |||
| import { openAntdModal } from '@/utils/modal'; | |||
| import { to } from '@/utils/promise'; | |||
| import { getSessionStorageItem, resourceItemKey } from '@/utils/sessionStorage'; | |||
| import { modalConfirm } from '@/utils/ui'; | |||
| import { useParams, useSearchParams } from '@umijs/max'; | |||
| import { App, Button, Flex, Select, Tabs } from 'antd'; | |||
| import { pick } from 'lodash'; | |||
| import { useEffect, useState } from 'react'; | |||
| import AddVersionModal from '../AddVersionModal'; | |||
| import ResourceIntro from '../ResourceIntro'; | |||
| @@ -40,30 +39,32 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| const [info, setInfo] = useState<ResourceData>({} as ResourceData); | |||
| const locationParams = useParams(); | |||
| const [searchParams] = useSearchParams(); | |||
| const resourceId = Number(locationParams.id); | |||
| // 模型演化传入的 tab | |||
| const defaultTab = searchParams.get('tab') || ResourceInfoTabKeys.Introduction; | |||
| // 模型演化传入的版本 | |||
| let versionParam = searchParams.get('version'); | |||
| const name = searchParams.get('name') || ''; | |||
| const owner = searchParams.get('owner') || ''; | |||
| const identifier = searchParams.get('identifier') || ''; | |||
| const [versionList, setVersionList] = useState<ResourceVersionData[]>([]); | |||
| const [version, setVersion] = useState<string | undefined>(undefined); | |||
| const [activeTab, setActiveTab] = useState<string>(defaultTab); | |||
| const resourceId = Number(locationParams.id); | |||
| const config = resourceConfig[resourceType]; | |||
| const typeName = config.name; // 数据集/模型 | |||
| const { message } = App.useApp(); | |||
| useEffect(() => { | |||
| const info = getSessionStorageItem(resourceItemKey, true); | |||
| if (info) { | |||
| setInfo(info); | |||
| getVersionList(pick(info, ['owner', 'identifier'])); | |||
| } | |||
| }, [resourceId]); | |||
| getVersionList(); | |||
| }, [resourceId, owner, identifier]); | |||
| useEffect(() => { | |||
| if (version) { | |||
| getResourceDetail({ | |||
| ...pick(info, ['owner', 'name', 'id', 'identifier']), | |||
| id: resourceId, | |||
| owner, | |||
| name, | |||
| identifier, | |||
| version, | |||
| }); | |||
| } | |||
| @@ -85,9 +86,14 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| }; | |||
| // 获取版本列表 | |||
| const getVersionList = async (params: { owner: string; identifier: string }) => { | |||
| const getVersionList = async () => { | |||
| const request = config.getVersions; | |||
| const [res] = await to(request(params)); | |||
| const [res] = await to( | |||
| request({ | |||
| owner, | |||
| identifier, | |||
| }), | |||
| ); | |||
| if (res && res.data && res.data.length > 0) { | |||
| setVersionList(res.data); | |||
| if ( | |||
| @@ -112,7 +118,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| resoureName: info.name, | |||
| identifier: info.identifier, | |||
| onOk: () => { | |||
| getVersionList(pick(info, ['owner', 'identifier'])); | |||
| getVersionList(); | |||
| close(); | |||
| }, | |||
| }); | |||
| @@ -127,13 +133,16 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| const deleteVersion = async () => { | |||
| const request = config.deleteVersion; | |||
| const params = { | |||
| ...pick(info, ['id', 'owner', 'identifier', 'relative_paths']), | |||
| id: resourceId, | |||
| owner, | |||
| identifier, | |||
| relative_paths: info.relative_paths, | |||
| version, | |||
| }; | |||
| const [res] = await to(request(params)); | |||
| if (res) { | |||
| message.success('删除成功'); | |||
| getVersionList(pick(info, ['owner', 'identifier'])); | |||
| getVersionList(); | |||
| } | |||
| }; | |||
| @@ -174,7 +183,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| <ModelEvolution | |||
| resourceId={resourceId} | |||
| version={version} | |||
| identifier={info.identifier} | |||
| identifier={identifier} | |||
| isActive={activeTab === ResourceInfoTabKeys.Evolution} | |||
| onVersionChange={handleVersionChange} | |||
| ></ModelEvolution> | |||
| @@ -228,6 +237,9 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| </div> | |||
| <div className={styles['resource-info__bottom']}> | |||
| <Tabs activeKey={activeTab} items={items} onChange={(key) => setActiveTab(key)}></Tabs> | |||
| <div className={styles['resource-info__bottom__legend']}> | |||
| {activeTab === ResourceInfoTabKeys.Evolution && <GraphLegend />} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| ); | |||
| @@ -4,7 +4,6 @@ import { CommonTabKeys } from '@/enums'; | |||
| import AddModelModal from '@/pages/Dataset/components/AddModelModal'; | |||
| import { openAntdModal } from '@/utils/modal'; | |||
| import { to } from '@/utils/promise'; | |||
| import { resourceItemKey, setSessionStorageItem } from '@/utils/sessionStorage'; | |||
| import { modalConfirm } from '@/utils/ui'; | |||
| import { useNavigate } from '@umijs/max'; | |||
| import { App, Button, Input, Pagination, PaginationProps } from 'antd'; | |||
| @@ -138,8 +137,9 @@ function ResourceList( | |||
| activeTag: dataTag, | |||
| }); | |||
| const prefix = config.prefix; | |||
| setSessionStorageItem(resourceItemKey, record, true); | |||
| navigate(`/dataset/${prefix}/info/${record.id}`); | |||
| navigate( | |||
| `/dataset/${prefix}/info/${record.id}?name=${record.name}&owner=${record.owner}&identifier=${record.identifier}`, | |||
| ); | |||
| }; | |||
| // 分页切换 | |||
| @@ -1,16 +1,11 @@ | |||
| .model-evolution { | |||
| width: 100%; | |||
| height: 100%; | |||
| overflow-x: hidden; | |||
| background-color: white; | |||
| &__top { | |||
| padding: 30px 0; | |||
| color: @text-color; | |||
| font-size: @font-size-content; | |||
| } | |||
| &__graph { | |||
| height: calc(100% - 92px); | |||
| height: calc(100%); | |||
| background-color: @background-color; | |||
| background-image: url(@/assets/img/pipeline-canvas-bg.png); | |||
| background-size: 100% 100%; | |||
| @@ -9,9 +9,8 @@ import { getModelAtlasReq } from '@/services/dataset/index.js'; | |||
| import themes from '@/styles/theme.less'; | |||
| import { to } from '@/utils/promise'; | |||
| import G6, { G6GraphEvent, Graph, INode } from '@antv/g6'; | |||
| import { Flex } from 'antd'; | |||
| import { useEffect, useRef, useState } from 'react'; | |||
| import GraphLegend from '../GraphLegend'; | |||
| import NodeTooltips from '../NodeTooltips'; | |||
| import styles from './index.less'; | |||
| import type { ModelDepsData, ProjectDependency, TrainDataset } from './utils'; | |||
| @@ -145,14 +144,15 @@ function ModelEvolution({ | |||
| // 更加缩放,调整 tooltip 位置 | |||
| const offsetX = (nodeWidth * zoom) / 4; | |||
| const offsetY = (nodeHeight * zoom) / 4; | |||
| point.x += offsetX; | |||
| const canvasWidth = graphRef.current!.clientWidth; | |||
| if (point.x + 300 > canvasWidth) { | |||
| point.x = canvasWidth - 300; | |||
| if (point.x + 300 > canvasWidth + 30) { | |||
| point.x = canvasWidth + 30 - 300; | |||
| } | |||
| setHoverNodeData(model); | |||
| setNodeToolTipX(point.x + offsetX); | |||
| setNodeToolTipX(point.x); | |||
| setNodeToolTipY(graphRef.current!.clientHeight - point.y + offsetY); | |||
| setShowNodeTooltip(true); | |||
| }); | |||
| @@ -248,9 +248,6 @@ function ModelEvolution({ | |||
| return ( | |||
| <div className={styles['model-evolution']}> | |||
| <Flex align="center" className={styles['model-evolution__top']}> | |||
| <GraphLegend style={{ marginRight: 0, marginLeft: 'auto' }}></GraphLegend> | |||
| </Flex> | |||
| <div className={styles['model-evolution__graph']} id="canvas" ref={graphRef}></div> | |||
| {(showNodeTooltip || enterTooltip) && ( | |||
| <NodeTooltips | |||
| @@ -38,9 +38,11 @@ export type TrainTask = { | |||
| }; | |||
| export interface TrainDataset extends NodeConfig { | |||
| dataset_id: number; | |||
| dataset_name: string; | |||
| dataset_version: string; | |||
| repo_id: number; | |||
| name: string; | |||
| version: string; | |||
| identifier: string; | |||
| owner: string; | |||
| model_type: NodeType.TestDataset | NodeType.TrainDataset; | |||
| } | |||
| @@ -51,34 +53,33 @@ export interface ProjectDependency extends NodeConfig { | |||
| model_type: NodeType.Project; | |||
| } | |||
| export type ModalDetail = { | |||
| export type ModelMeta = { | |||
| train_datasets?: TrainDataset[]; | |||
| test_datasets?: TrainDataset[]; | |||
| project_depency?: ProjectDependency; | |||
| train_task?: TrainTask; | |||
| name: string; | |||
| available_range: number; | |||
| file_name: string; | |||
| file_size: string; | |||
| description: string; | |||
| model_type_name: string; | |||
| model_tag_name: string; | |||
| version: string; | |||
| model_source: string; | |||
| model_type: string; | |||
| create_time: string; | |||
| file_size: string; | |||
| is_public: boolean; | |||
| }; | |||
| export interface ModelDepsAPIData { | |||
| current_model_id: number; | |||
| repo_id: number; | |||
| model_name: string; | |||
| version: string; | |||
| workflow_id: number; | |||
| exp_ins_id: number; | |||
| model_type: NodeType.Children | NodeType.Current | NodeType.Parent; | |||
| current_model_name: string; | |||
| project_dependency?: ProjectDependency; | |||
| test_dataset: TrainDataset[]; | |||
| train_dataset: TrainDataset[]; | |||
| train_task: TrainTask; | |||
| model_version_dependcy_vo: ModalDetail; | |||
| children_models: ModelDepsAPIData[]; | |||
| parent_models: ModelDepsAPIData[]; | |||
| model_meta: ModelMeta; | |||
| child_model_list: ModelDepsAPIData[]; | |||
| parent_model_vo?: ModelDepsAPIData; | |||
| } | |||
| export interface ModelDepsData extends Omit<ModelDepsAPIData, 'children_models'>, TreeGraphData { | |||
| export interface ModelDepsData extends Omit<ModelDepsAPIData, 'child_model_list'>, TreeGraphData { | |||
| children: ModelDepsData[]; | |||
| expanded: boolean; // 是否展开 | |||
| level: number; // 层级,从 0 开始 | |||
| @@ -92,8 +93,11 @@ export function normalizeChildren(data: ModelDepsData[]) { | |||
| item.model_type = NodeType.Children; | |||
| item.expanded = false; | |||
| item.level = 0; | |||
| item.datasetLen = item.train_dataset.length + item.test_dataset.length; | |||
| item.id = `$M_${item.current_model_id}_${item.version}`; | |||
| item.datasetLen = getDatasetLen( | |||
| item.model_meta.train_datasets, | |||
| item.model_meta.test_datasets, | |||
| ); | |||
| item.id = `$M_${item.repo_id}_${item.version}`; | |||
| item.label = getLabel(item); | |||
| item.style = getStyle(NodeType.Children); | |||
| normalizeChildren(item.children); | |||
| @@ -104,16 +108,17 @@ export function normalizeChildren(data: ModelDepsData[]) { | |||
| // 获取 label | |||
| export function getLabel(node: ModelDepsData | ModelDepsAPIData) { | |||
| return ( | |||
| fittingString( | |||
| `${node.model_version_dependcy_vo.name ?? ''}`, | |||
| nodeWidth - labelPadding, | |||
| nodeFontSize, | |||
| ) + | |||
| fittingString(`${node.model_name ?? ''}`, nodeWidth - labelPadding, nodeFontSize) + | |||
| '\n' + | |||
| fittingString(`${node.version}`, nodeWidth - labelPadding, nodeFontSize) | |||
| ); | |||
| } | |||
| // 获取数据集数量 | |||
| export function getDatasetLen(train?: TrainDataset[], test?: TrainDataset[]) { | |||
| return (train?.length || 0) + (test?.length || 0); | |||
| } | |||
| // 获取 style | |||
| export function getStyle(model_type: NodeType) { | |||
| let fill = ''; | |||
| @@ -148,41 +153,43 @@ export function getStyle(model_type: NodeType) { | |||
| export function normalizeTreeData(apiData: ModelDepsAPIData): ModelDepsData { | |||
| // 将 children_models 转换成 children | |||
| let normalizedData = changePropertyName(apiData, { | |||
| children_models: 'children', | |||
| child_model_list: 'children', | |||
| }) as ModelDepsData; | |||
| // 设置当前模型的数据 | |||
| normalizedData.model_type = NodeType.Current; | |||
| normalizedData.id = `$M_${normalizedData.current_model_id}_${normalizedData.version}`; | |||
| normalizedData.id = `$M_${normalizedData.repo_id}_${normalizedData.version}`; | |||
| normalizedData.label = getLabel(normalizedData); | |||
| normalizedData.style = getStyle(NodeType.Current); | |||
| normalizedData.expanded = true; | |||
| normalizedData.datasetLen = | |||
| normalizedData.train_dataset.length + normalizedData.test_dataset.length; | |||
| normalizedData.datasetLen = getDatasetLen( | |||
| normalizedData.model_meta.train_datasets, | |||
| normalizedData.model_meta.test_datasets, | |||
| ); | |||
| normalizeChildren(normalizedData.children as ModelDepsData[]); | |||
| normalizedData.level = 0; | |||
| // 将 parent_models 转换成树形结构 | |||
| let parent_models = normalizedData.parent_models || []; | |||
| while (parent_models.length > 0) { | |||
| const parent = parent_models[0]; | |||
| let parent_model = normalizedData.parent_model_vo; | |||
| while (parent_model) { | |||
| const parent = parent_model; | |||
| normalizedData = { | |||
| ...parent, | |||
| expanded: false, | |||
| level: 0, | |||
| datasetLen: parent.train_dataset.length + parent.test_dataset.length, | |||
| datasetLen: getDatasetLen(parent.model_meta.train_datasets, parent.model_meta.test_datasets), | |||
| model_type: NodeType.Parent, | |||
| id: `$M_${parent.current_model_id}_${parent.version}`, | |||
| id: `$M_${parent.repo_id}_${parent.version}`, | |||
| label: getLabel(parent), | |||
| style: getStyle(NodeType.Parent), | |||
| children: [ | |||
| { | |||
| ...normalizedData, | |||
| parent_models: [], | |||
| parent_model: null, | |||
| }, | |||
| ], | |||
| }; | |||
| parent_models = normalizedData.parent_models || []; | |||
| parent_model = normalizedData.parent_model_vo; | |||
| } | |||
| return normalizedData; | |||
| } | |||
| @@ -195,11 +202,12 @@ export function getGraphData(data: ModelDepsData, hierarchyNodes: ModelDepsData[ | |||
| getWidth: () => nodeWidth, | |||
| getVGap: (node: NodeConfig) => { | |||
| const model = node as ModelDepsData; | |||
| const { model_type, expanded, project_dependency } = model; | |||
| const { model_type, expanded, model_meta } = model; | |||
| const { project_depency } = model_meta; | |||
| if (model_type === NodeType.Current || model_type === NodeType.Parent) { | |||
| return vGap / 2; | |||
| } | |||
| const selfGap = expanded && project_dependency?.url ? nodeHeight + vGap : 0; | |||
| const selfGap = expanded && project_depency?.url ? nodeHeight + vGap : 0; | |||
| const nextNode = getSameHierarchyNextNode(model, hierarchyNodes); | |||
| if (!nextNode) { | |||
| return vGap / 2; | |||
| @@ -254,28 +262,35 @@ const addDatasetDependency = ( | |||
| nodes: NodeConfig[], | |||
| edges: EdgeConfig[], | |||
| ) => { | |||
| const { train_dataset, test_dataset, id } = data; | |||
| train_dataset.forEach((item) => { | |||
| item.id = `$DTrain_${id}_${item.dataset_id}_${item.dataset_version}`; | |||
| const { repo_id, model_meta } = data; | |||
| const { train_datasets, test_datasets } = model_meta; | |||
| train_datasets?.forEach((item) => { | |||
| if (!item.repo_id) { | |||
| item.repo_id = item.id; | |||
| } | |||
| item.id = `$DTrain_${repo_id}_${item.repo_id}_${item.version}`; | |||
| item.model_type = NodeType.TrainDataset; | |||
| item.style = getStyle(NodeType.TrainDataset); | |||
| }); | |||
| test_dataset.forEach((item) => { | |||
| item.id = `$DTest_${id}_${item.dataset_id}_${item.dataset_version}`; | |||
| test_datasets?.forEach((item) => { | |||
| if (!item.repo_id) { | |||
| item.repo_id = item.id; | |||
| } | |||
| item.id = `$DTest_${repo_id}_${item.repo_id}_${item.version}`; | |||
| item.model_type = NodeType.TestDataset; | |||
| item.style = getStyle(NodeType.TestDataset); | |||
| }); | |||
| datasetNodes.length = 0; | |||
| const len = train_dataset.length + test_dataset.length; | |||
| [...train_dataset, ...test_dataset].forEach((item, index) => { | |||
| const len = getDatasetLen(train_datasets, test_datasets); | |||
| [...(train_datasets ?? []), ...(test_datasets ?? [])].forEach((item, index) => { | |||
| const node = { ...item }; | |||
| node.type = 'ellipse'; | |||
| node.size = [ellipseWidth, nodeHeight]; | |||
| node.label = | |||
| fittingString(node.dataset_name, ellipseWidth - labelPadding, nodeFontSize) + | |||
| fittingString(node.name, ellipseWidth - labelPadding, nodeFontSize) + | |||
| '\n' + | |||
| fittingString(node.dataset_version, ellipseWidth - labelPadding, nodeFontSize); | |||
| fittingString(node.version, ellipseWidth - labelPadding, nodeFontSize); | |||
| const half = len / 2 - 0.5; | |||
| node.x = currentNode.x! - (half - index) * (ellipseWidth + datasetHGap); | |||
| @@ -299,10 +314,11 @@ const addProjectDependency = ( | |||
| nodes: NodeConfig[], | |||
| edges: EdgeConfig[], | |||
| ) => { | |||
| const { project_dependency, id } = data; | |||
| if (project_dependency?.url) { | |||
| const node = { ...project_dependency }; | |||
| node.id = `$P_${id}_${node.url}_${node.branch}`; | |||
| const { repo_id, model_meta } = data; | |||
| const { project_depency } = model_meta; | |||
| if (project_depency?.url) { | |||
| const node = { ...project_depency }; | |||
| node.id = `$P_${repo_id}_${node.url}_${node.branch}`; | |||
| node.model_type = NodeType.Project; | |||
| node.type = 'rect'; | |||
| node.label = fittingString(node.name, nodeWidth - labelPadding, nodeFontSize); | |||
| @@ -322,6 +338,7 @@ const addProjectDependency = ( | |||
| } | |||
| }; | |||
| /* | |||
| // 判断两个矩形是否相交 | |||
| function isRectanglesOverlap(rect1: Rect, rect2: Rect) { | |||
| const a2x = rect1.x + rect1.width / 2; | |||
| @@ -366,6 +383,7 @@ function adjustDatasetPosition(node: NodeConfig) { | |||
| }); | |||
| } | |||
| } | |||
| */ | |||
| // 层级遍历树结构 | |||
| export function traverseHierarchically(data: ModelDepsData | undefined): ModelDepsData[] { | |||
| @@ -2,6 +2,7 @@ | |||
| position: absolute; | |||
| bottom: -100px; | |||
| left: -300px; | |||
| z-index: 10; | |||
| width: 300px; | |||
| padding: 10px; | |||
| background: white; | |||
| @@ -50,6 +51,7 @@ | |||
| flex: 1; | |||
| min-width: 0; | |||
| font-weight: 500; | |||
| word-break: break-all; | |||
| &:hover { | |||
| text-decoration: underline @underline-color; | |||
| @@ -14,9 +14,9 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) { | |||
| const navigate = useNavigate(); | |||
| const gotoExperimentPage = () => { | |||
| if (data.train_task?.ins_id) { | |||
| if (data.model_meta.train_task?.ins_id) { | |||
| const { origin } = location; | |||
| const url = `${origin}/pipeline/experiment/instance/${data.workflow_id}/${data.train_task.ins_id}`; | |||
| const url = `${origin}/pipeline/experiment/instance/${data.model_meta.train_task.task_id}/${data.model_meta.train_task.ins_id}`; | |||
| window.open(url, '_blank'); | |||
| } | |||
| }; | |||
| @@ -25,10 +25,10 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) { | |||
| if (data.model_type === NodeType.Current) { | |||
| return; | |||
| } | |||
| if (data.current_model_id === resourceId) { | |||
| if (data.repo_id === resourceId) { | |||
| onVersionChange?.(data.version); | |||
| } else { | |||
| const path = `/dataset/model/info/${data.current_model_id}?tab=${ResourceInfoTabKeys.Evolution}&version=${data.version}`; | |||
| const path = `/dataset/model/info/${data.repo_id}?tab=${ResourceInfoTabKeys.Evolution}&version=${data.version}&name=${data.model_name}&owner=${data.owner}&identifier=${data.identifier}`; | |||
| navigate(path); | |||
| } | |||
| }; | |||
| @@ -40,12 +40,10 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) { | |||
| <div className={styles['node-tooltips__row']}> | |||
| <span className={styles['node-tooltips__row__title']}>模型名称:</span> | |||
| {data.model_type === NodeType.Current ? ( | |||
| <span className={styles['node-tooltips__row__value']}> | |||
| {data.model_version_dependcy_vo?.name || '--'} | |||
| </span> | |||
| <span className={styles['node-tooltips__row__value']}>{data.model_name || '--'}</span> | |||
| ) : ( | |||
| <ValueLink | |||
| value={data.model_version_dependcy_vo?.name} | |||
| value={data.model_name} | |||
| className={styles['node-tooltips__row__link']} | |||
| nullClassName={styles['node-tooltips__row__value']} | |||
| onClick={gotoModelPage} | |||
| @@ -59,25 +57,25 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) { | |||
| <div className={styles['node-tooltips__row']}> | |||
| <span className={styles['node-tooltips__row__title']}>模型框架:</span> | |||
| <span className={styles['node-tooltips__row__value']}> | |||
| {data.model_version_dependcy_vo?.model_type_name || '--'} | |||
| {data.model_meta.model_type || '--'} | |||
| </span> | |||
| </div> | |||
| <div className={styles['node-tooltips__row']}> | |||
| <span className={styles['node-tooltips__row__title']}>模型大小:</span> | |||
| <span className={styles['node-tooltips__row__value']}> | |||
| {data.model_version_dependcy_vo?.file_size || '--'} | |||
| {data.model_meta.file_size || '--'} | |||
| </span> | |||
| </div> | |||
| <div className={styles['node-tooltips__row']}> | |||
| <span className={styles['node-tooltips__row__title']}>创建时间:</span> | |||
| <span className={styles['node-tooltips__row__value']}> | |||
| {formatDate(data.model_version_dependcy_vo?.create_time)} | |||
| {formatDate(data.model_meta.create_time || '--')} | |||
| </span> | |||
| </div> | |||
| <div className={styles['node-tooltips__row']}> | |||
| <span className={styles['node-tooltips__row__title']}>模型权限:</span> | |||
| <span className={styles['node-tooltips__row__value']}> | |||
| {data.model_version_dependcy_vo?.available_range === 1 ? '公开' : '私有'} | |||
| {data.model_meta.is_public ? '公开' : '私有'} | |||
| </span> | |||
| </div> | |||
| </div> | |||
| @@ -86,7 +84,7 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) { | |||
| <div className={styles['node-tooltips__row']}> | |||
| <span className={styles['node-tooltips__row__title']}>训练任务:</span> | |||
| <ValueLink | |||
| value={data.train_task?.name} | |||
| value={data.model_meta.train_task?.name} | |||
| className={styles['node-tooltips__row__link']} | |||
| nullClassName={styles['node-tooltips__row__value']} | |||
| onClick={gotoExperimentPage} | |||
| @@ -100,7 +98,7 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) { | |||
| function DatasetInfo({ data }: { data: TrainDataset }) { | |||
| const gotoDatasetPage = () => { | |||
| const { origin } = location; | |||
| const url = `${origin}/dataset/dataset/info/${data.dataset_id}?tab=${ResourceInfoTabKeys.Version}&version=${data.dataset_version}`; | |||
| const url = `${origin}/dataset/dataset/info/${data.repo_id}?tab=${ResourceInfoTabKeys.Version}&version=${data.version}&name=${data.name}&owner=${data.owner}&identifier=${data.identifier}`; | |||
| window.open(url, '_blank'); | |||
| }; | |||
| @@ -111,7 +109,7 @@ function DatasetInfo({ data }: { data: TrainDataset }) { | |||
| <div className={styles['node-tooltips__row']}> | |||
| <span className={styles['node-tooltips__row__title']}>数据集名称:</span> | |||
| <ValueLink | |||
| value={data.dataset_name} | |||
| value={data.name} | |||
| className={styles['node-tooltips__row__link']} | |||
| nullClassName={styles['node-tooltips__row__value']} | |||
| onClick={gotoDatasetPage} | |||
| @@ -119,9 +117,7 @@ function DatasetInfo({ data }: { data: TrainDataset }) { | |||
| </div> | |||
| <div className={styles['node-tooltips__row']}> | |||
| <span className={styles['node-tooltips__row__title']}>数据集版本:</span> | |||
| <span className={styles['node-tooltips__row__value']}> | |||
| {data.dataset_version || '--'} | |||
| </span> | |||
| <span className={styles['node-tooltips__row__value']}>{data.version || '--'}</span> | |||
| </div> | |||
| </div> | |||
| </> | |||
| @@ -204,11 +204,14 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||
| }); | |||
| } | |||
| } else { | |||
| const { activeTab, id, name, version, path } = res; | |||
| const { activeTab, id, name, version, path, identifier, owner } = res; | |||
| const value = JSON.stringify({ | |||
| id, | |||
| name, | |||
| version, | |||
| path, | |||
| identifier, | |||
| owner, | |||
| }); | |||
| const showValue = `${name}:${version}`; | |||
| form.setFieldValue(formItemName, { | |||
| @@ -74,121 +74,6 @@ const convertMirrorVersionToTreeData = ( | |||
| })); | |||
| }; | |||
| // 从树形数据节点 id 中获取数据集版本列表的参数 | |||
| // const parseDatasetVersionId = (id: string) => { | |||
| // const list = id.split('-'); | |||
| // return { | |||
| // id: Number(list[0]), | |||
| // name: list[1], | |||
| // owner: list[2], | |||
| // identifier: list[3], | |||
| // version: list[4], | |||
| // }; | |||
| // }; | |||
| // 从树形数据节点 id 中获取数据集版本列表的参数 | |||
| // const parseMirrorVersionId = (id: string) => { | |||
| // const list = id.split('-'); | |||
| // return { | |||
| // parentId: Number(list[0]), | |||
| // id: list[1], | |||
| // url: list[2], | |||
| // }; | |||
| // }; | |||
| // export type MirrorVersion = { | |||
| // id: number; // 镜像版本 id | |||
| // status: MirrorVersionStatus; // 镜像版本状态 | |||
| // tag_name: string; // 镜像版本 name | |||
| // url: string; // 镜像版本路径 | |||
| // }; | |||
| // export type SelectorTypeInfo = { | |||
| // getList: (params: any) => Promise<any>; // 获取资源列表 | |||
| // getVersions: (params: any) => Promise<any>; // 获取资源版本列表 | |||
| // getFiles: (params: any) => Promise<any>; // 获取资源文件列表 | |||
| // handleVersionResponse: (res: any) => any[]; // 处理版本列表接口数据 | |||
| // dataToTreeData: (data: any) => TreeDataNode[]; // 数据转树形结构 | |||
| // parseTreeNodeId: (id: string) => any; // 获取版本列表请求参数 | |||
| // modalIcon: string; // modal icon | |||
| // buttonIcon: string; // button icon | |||
| // name: string; // 名称 | |||
| // litReqParamKey: 'available_range' | 'image_type'; // 表示是公开还是私有的参数名称,获取资源列表接口使用 | |||
| // tabItems: TabsProps['items']; // tab 列表 | |||
| // buttontTitle: string; // 按钮 title | |||
| // }; | |||
| // export const selectorTypeConfig: Record<ResourceSelectorType, SelectorTypeInfo> = { | |||
| // [ResourceSelectorType.Model]: { | |||
| // getList: getModelList, | |||
| // getVersions: getModelVersionList, | |||
| // getFiles: getModelVersionIdList, | |||
| // name: '模型', | |||
| // modalIcon: modelImg, | |||
| // buttonIcon: 'icon-xuanzemoxing', | |||
| // litReqParamKey: 'available_range', | |||
| // tabItems: [ | |||
| // { | |||
| // key: CommonTabKeys.Private, | |||
| // label: '我的模型', | |||
| // }, | |||
| // { | |||
| // key: CommonTabKeys.Public, | |||
| // label: '公开模型', | |||
| // }, | |||
| // ], | |||
| // buttontTitle: '选择模型', | |||
| // }, | |||
| // [ResourceSelectorType.Dataset]: { | |||
| // getList: getDatasetList, | |||
| // getVersions: getDatasetVersionList, | |||
| // getFiles: getDatasetInfo, | |||
| // name: '数据集', | |||
| // modalIcon: datasetImg, | |||
| // buttonIcon: 'icon-xuanzeshujuji', | |||
| // litReqParamKey: 'available_range', | |||
| // tabItems: [ | |||
| // { | |||
| // key: CommonTabKeys.Private, | |||
| // label: '我的数据集', | |||
| // }, | |||
| // { | |||
| // key: CommonTabKeys.Public, | |||
| // label: '公开数据集', | |||
| // }, | |||
| // ], | |||
| // buttontTitle: '选择数据集', | |||
| // }, | |||
| // [ResourceSelectorType.Mirror]: { | |||
| // getList: getMirrorListReq, | |||
| // getVersions: (id: number) => getMirrorVersionListReq({ image_id: id, page: 0, size: 200 }), | |||
| // getFiles: getMirrorFilesReq, | |||
| // handleVersionResponse: (res) => | |||
| // res.data?.content?.filter( | |||
| // (v: MirrorVersionData) => v.status === MirrorVersionStatus.Available, | |||
| // ) || [], | |||
| // dataToTreeData: convertMirrorToTreeData, | |||
| // parseTreeNodeId: (id: string) => id, | |||
| // name: '镜像', | |||
| // modalIcon: mirrorImg, | |||
| // buttonIcon: 'icon-xuanzejingxiang', | |||
| // litReqParamKey: 'image_type', | |||
| // tabItems: [ | |||
| // { | |||
| // key: CommonTabKeys.Private, | |||
| // label: '我的镜像', | |||
| // }, | |||
| // { | |||
| // key: CommonTabKeys.Public, | |||
| // label: '公开镜像', | |||
| // }, | |||
| // ], | |||
| // buttontTitle: '选择镜像', | |||
| // }, | |||
| // }; | |||
| interface SelectorTypeInfo { | |||
| getList: (isPublic: boolean) => Promise<any>; // 获取资源列表 | |||
| getVersions: (parentKey: string, parentNode: any) => Promise<any>; // 获取资源版本列表 | |||
| @@ -22,6 +22,8 @@ export type ResourceSelectorResponse = { | |||
| name: string; // 数据集\模型\镜像 name | |||
| version: string; // 数据集\模型\镜像版本 | |||
| path: string; // 数据集\模型\镜像版本路径 | |||
| identifier: string; // 数据集\模型 identifier | |||
| owner: string; // 数据集\模型 owner | |||
| activeTab: CommonTabKeys; // 是我的还是公开的 | |||
| }; | |||
| @@ -221,12 +223,17 @@ function ResourceSelectorModal({ | |||
| if (checkedKeys.length > 0) { | |||
| const last = checkedKeys[0] as string; | |||
| const { id, version } = getIdAndVersion(last); | |||
| const name = (treeData.find((v) => v.key === id)?.title ?? '') as string; | |||
| const treeNode = treeData.find((v) => v.key === id) as any; | |||
| const name = (treeNode?.title ?? '') as string; | |||
| const identifier = (treeNode?.identifier ?? '') as string; | |||
| const owner = (treeNode?.owner ?? '') as string; | |||
| const res = { | |||
| id, | |||
| name, | |||
| path: versionPath, | |||
| version, | |||
| identifier, | |||
| owner, | |||
| activeTab: activeTab as CommonTabKeys, | |||
| }; | |||
| onOk?.(res); | |||