Browse Source

Merge pull request '合并dev-zw' (#128) from dev-zw-newdataset into dev

dev-DXTZYK
cp3hnu 1 year ago
parent
commit
03c6415376
14 changed files with 159 additions and 225 deletions
  1. +2
    -1
      react-ui/src/components/IFramePage/index.tsx
  2. +9
    -1
      react-ui/src/components/ParameterSelect/config.tsx
  3. +4
    -1
      react-ui/src/components/ResourceSelect/index.tsx
  4. +7
    -0
      react-ui/src/pages/Dataset/components/ResourceInfo/index.less
  5. +28
    -16
      react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx
  6. +3
    -3
      react-ui/src/pages/Dataset/components/ResourceList/index.tsx
  7. +2
    -7
      react-ui/src/pages/Model/components/ModelEvolution/index.less
  8. +5
    -8
      react-ui/src/pages/Model/components/ModelEvolution/index.tsx
  9. +71
    -53
      react-ui/src/pages/Model/components/ModelEvolution/utils.tsx
  10. +2
    -0
      react-ui/src/pages/Model/components/NodeTooltips/index.less
  11. +14
    -18
      react-ui/src/pages/Model/components/NodeTooltips/index.tsx
  12. +4
    -1
      react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx
  13. +0
    -115
      react-ui/src/pages/Pipeline/components/ResourceSelectorModal/config.tsx
  14. +8
    -1
      react-ui/src/pages/Pipeline/components/ResourceSelectorModal/index.tsx

+ 2
- 1
react-ui/src/components/IFramePage/index.tsx View File

@@ -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>
); );


+ 9
- 1
react-ui/src/components/ParameterSelect/config.tsx View File

@@ -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>; // 获取下拉数据


+ 4
- 1
react-ui/src/components/ResourceSelect/index.tsx View File

@@ -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}`;


+ 7
- 0
react-ui/src/pages/Dataset/components/ResourceInfo/index.less View File

@@ -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%;


+ 28
- 16
react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx View File

@@ -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>
); );


+ 3
- 3
react-ui/src/pages/Dataset/components/ResourceList/index.tsx View File

@@ -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}`,
);
}; };


// 分页切换 // 分页切换


+ 2
- 7
react-ui/src/pages/Model/components/ModelEvolution/index.less View File

@@ -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%;


+ 5
- 8
react-ui/src/pages/Model/components/ModelEvolution/index.tsx View File

@@ -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


+ 71
- 53
react-ui/src/pages/Model/components/ModelEvolution/utils.tsx View File

@@ -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
- 0
react-ui/src/pages/Model/components/NodeTooltips/index.less View File

@@ -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
- 18
react-ui/src/pages/Model/components/NodeTooltips/index.tsx View File

@@ -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>
</> </>


+ 4
- 1
react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx View File

@@ -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, {


+ 0
- 115
react-ui/src/pages/Pipeline/components/ResourceSelectorModal/config.tsx View File

@@ -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>; // 获取资源版本列表


+ 8
- 1
react-ui/src/pages/Pipeline/components/ResourceSelectorModal/index.tsx View File

@@ -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);


Loading…
Cancel
Save