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