diff --git a/react-ui/config/routes.ts b/react-ui/config/routes.ts
index 200bbd20..1351cd2c 100644
--- a/react-ui/config/routes.ts
+++ b/react-ui/config/routes.ts
@@ -77,7 +77,7 @@ export default [
},
{
name: '流水线详情',
- path: ':id/:name',
+ path: ':id',
component: './Pipeline/editPipeline/index',
},
],
diff --git a/react-ui/src/locales/zh-CN/pages.ts b/react-ui/src/locales/zh-CN/pages.ts
index 5e4565d5..ed9afc5e 100644
--- a/react-ui/src/locales/zh-CN/pages.ts
+++ b/react-ui/src/locales/zh-CN/pages.ts
@@ -1,12 +1,12 @@
export default {
'pages.layouts.userLayout.title': 'Ant Design 是西湖区最具影响力的 Web 设计规范',
'pages.login.accountLogin.tab': '账户密码登录',
- 'pages.login.accountLogin.errorMessage': '错误的用户名和密码(admin/admin123)',
+ 'pages.login.accountLogin.errorMessage': '错误的用户名和密码',
'pages.login.failure': '登录失败,请重试!',
'pages.login.success': '登录成功!',
- 'pages.login.username.placeholder': '用户名: admin',
+ 'pages.login.username.placeholder': '用户名',
'pages.login.username.required': '用户名是必填项!',
- 'pages.login.password.placeholder': '密码: admin123',
+ 'pages.login.password.placeholder': '密码',
'pages.login.password.required': '密码是必填项!',
'pages.login.phoneLogin.tab': '手机号登录',
'pages.login.phoneLogin.errorMessage': '验证码错误',
diff --git a/react-ui/src/locales/zh-TW/pages.ts b/react-ui/src/locales/zh-TW/pages.ts
index f9e265b0..b3e37ef5 100644
--- a/react-ui/src/locales/zh-TW/pages.ts
+++ b/react-ui/src/locales/zh-TW/pages.ts
@@ -1,12 +1,12 @@
export default {
'pages.layouts.userLayout.title': 'Ant Design 是西湖區最具影響力的 Web 設計規範',
'pages.login.accountLogin.tab': '賬戶密碼登錄',
- 'pages.login.accountLogin.errorMessage': '錯誤的用戶名和密碼(admin/admin123)',
+ 'pages.login.accountLogin.errorMessage': '錯誤的用戶名和密碼',
'pages.login.failure': '登錄失敗,請重試!',
'pages.login.success': '登錄成功!',
- 'pages.login.username.placeholder': '用戶名: admin',
+ 'pages.login.username.placeholder': '用戶名',
'pages.login.username.required': '用戶名是必填項!',
- 'pages.login.password.placeholder': '密碼: admin123',
+ 'pages.login.password.placeholder': '密碼',
'pages.login.password.required': '密碼是必填項!',
'pages.login.phoneLogin.tab': '手機號登錄',
'pages.login.phoneLogin.errorMessage': '驗證碼錯誤',
diff --git a/react-ui/src/pages/Experiment/index.jsx b/react-ui/src/pages/Experiment/index.jsx
index aa6349d8..69648b49 100644
--- a/react-ui/src/pages/Experiment/index.jsx
+++ b/react-ui/src/pages/Experiment/index.jsx
@@ -198,7 +198,7 @@ function Experiment() {
};
const routeToEdit = (e, record) => {
e.stopPropagation();
- navgite({ pathname: `/pipeline/template/${record.workflow_id}/${record.workflow_name}` });
+ navgite({ pathname: `/pipeline/template/${record.workflow_id}` });
};
// 创建或者编辑实验接口请求
const handleAddExperiment = async (values) => {
diff --git a/react-ui/src/pages/Experiment/training/index.jsx b/react-ui/src/pages/Experiment/training/index.jsx
index 6cbabfe0..991e9855 100644
--- a/react-ui/src/pages/Experiment/training/index.jsx
+++ b/react-ui/src/pages/Experiment/training/index.jsx
@@ -23,24 +23,12 @@ function ExperimentText() {
const [paramsModalOpen, openParamsModal, closeParamsModal] = useVisible(false);
const graphRef = useRef();
- // const onDragEnd = (val) => {
- // console.log(val, 'eee');
- // const _x = val.x;
- // const _y = val.y;
- // const point = graph.getPointByClient(_x, _y);
- // let model = {};
- // // 元模型
- // model = {
- // ...val,
- // x: point.x,
- // y: point.y,
- // id: val.component_name + '-' + s8(),
- // isCluster: false,
- // };
- // graph.addItem('node', model, true);
- // };
const getGraphData = (data) => {
if (graph) {
+ // 修改历史数据有蓝色边框的问题
+ data.nodes.forEach((item) => {
+ item.style.stroke = '#fff';
+ });
graph.data(data);
graph.render();
} else {
@@ -92,9 +80,11 @@ function ExperimentText() {
getAnchorPoints(cfg) {
return (
cfg.anchorPoints || [
- // 上下各3,左右各1
+ // 四个,上下左右
[0.5, 0],
[0.5, 1],
+ [0, 0.5],
+ [1, 0.5],
]
);
},
@@ -120,6 +110,7 @@ function ExperimentText() {
textAlign: 'left',
textBaseline: 'middle',
fill: '#000',
+ cursor: 'pointer',
},
name: 'text-shape',
draggable: true,
@@ -166,8 +157,12 @@ function ExperimentText() {
width: graphRef.current.clientWidth || 500,
height: graphRef.current.clientHeight || 760,
animate: false,
- groupByTypes: false,
- enabledStack: true,
+ groupByTypes: true,
+ enabledStack: false,
+ fitView: true,
+ minZoom: 0.5,
+ maxZoom: 5,
+ fitViewPadding: 300,
modes: {
default: [
// config the shouldBegin for drag-node to avoid node moving while dragging on the anchor-point circles
@@ -181,15 +176,6 @@ function ExperimentText() {
// config the shouldBegin and shouldEnd to make sure the create-edge is began and ended at anchor-point circles
'drag-canvas',
'zoom-canvas',
- // 'brush-select',
- 'drag-combo',
- ],
- altSelect: [
- {
- type: 'brush-select',
- trigger: 'drag',
- },
- 'drag-node',
],
},
@@ -217,7 +203,7 @@ function ExperimentText() {
},
defaultEdge: {
// type: 'quadratic',
- type: 'cubic-vertical',
+ // type: 'cubic-vertical',
style: {
endArrow: {
@@ -232,15 +218,8 @@ function ExperimentText() {
stroke: '#a2a6b5',
radius: 1,
},
- nodeStateStyle: {
- hover: {
- opacity: 1,
- stroke: '#8fe8ff',
- },
- },
labelCfg: {
autoRotate: true,
- // refY: 10,
style: {
fontSize: 10,
fill: '#FFF',
@@ -257,11 +236,6 @@ function ExperimentText() {
cursor: 'pointer',
},
},
- // linkCenter: true,
- fitView: true,
- minZoom: 0.5,
- maxZoom: 5,
- fitViewPadding: 300,
});
graph.on('node:click', (e) => {
if (e.target.get('name') !== 'anchor-point' && e.item) {
diff --git a/react-ui/src/pages/Model/components/ModelEvolution/index.tsx b/react-ui/src/pages/Model/components/ModelEvolution/index.tsx
index bd8286cf..6a6e74e8 100644
--- a/react-ui/src/pages/Model/components/ModelEvolution/index.tsx
+++ b/react-ui/src/pages/Model/components/ModelEvolution/index.tsx
@@ -12,7 +12,14 @@ import GraphLegend from '../GraphLegend';
import NodeTooltips from '../NodeTooltips';
import styles from './index.less';
import type { ModelDepsData, ProjectDependency, TrainDataset } from './utils';
-import { NodeType, getGraphData, nodeHeight, nodeWidth, normalizeTreeData } from './utils';
+import {
+ NodeType,
+ getGraphData,
+ nodeFontSize,
+ nodeHeight,
+ nodeWidth,
+ normalizeTreeData,
+} from './utils';
type modeModelEvolutionProps = {
resourceId: number;
@@ -73,7 +80,7 @@ function ModelEvolution({
width: graphRef.current!.clientWidth,
height: graphRef.current!.clientHeight,
fitView: true,
- fitViewPadding: [50, 100, 50, 100],
+ fitViewPadding: [100, 100, 100, 100],
minZoom: 0.5,
maxZoom: 5,
defaultNode: {
@@ -95,7 +102,7 @@ function ModelEvolution({
position: 'center',
style: {
fill: '#ffffff',
- fontSize: 8,
+ fontSize: nodeFontSize,
textAlign: 'center',
cursor: 'pointer',
},
@@ -107,7 +114,7 @@ function ModelEvolution({
autoRotate: true,
},
style: {
- stroke: '#a2c1ff',
+ stroke: '#DEE0E5',
lineWidth: 1,
},
},
@@ -163,28 +170,32 @@ function ModelEvolution({
graph.on('node:click', (e: G6GraphEvent) => {
const nodeItem = e.item;
- const model = nodeItem.getModel();
+ const model = nodeItem.getModel() as ModelDepsData | ProjectDependency | TrainDataset;
const { model_type } = model;
const { origin } = location;
let url: string = '';
switch (model_type) {
case NodeType.children:
case NodeType.parent: {
- const { current_model_id, version } = model as ModelDepsData;
+ const { current_model_id, version } = model;
url = `${origin}/dataset/model/${current_model_id}?tab=${ResourceInfoTabKeys.Evolution}&version=${version}`;
break;
}
case NodeType.project: {
- const { url: projectUrl } = model as ProjectDependency;
+ const { url: projectUrl } = model;
url = projectUrl;
break;
}
case NodeType.trainDataset:
case NodeType.testDataset: {
- const { dataset_id, dataset_version } = model as TrainDataset;
+ const { dataset_id, dataset_version } = model;
url = `${origin}/dataset/dataset/${dataset_id}?tab=${ResourceInfoTabKeys.Version}&version=${dataset_version}`;
break;
}
+ case NodeType.current: {
+ // TODO: 隐藏数据集和项目
+ break;
+ }
default:
break;
}
diff --git a/react-ui/src/pages/Model/components/ModelEvolution/utils.tsx b/react-ui/src/pages/Model/components/ModelEvolution/utils.tsx
index 78fdb87a..3c3f4225 100644
--- a/react-ui/src/pages/Model/components/ModelEvolution/utils.tsx
+++ b/react-ui/src/pages/Model/components/ModelEvolution/utils.tsx
@@ -6,8 +6,10 @@ import Hierarchy from '@antv/hierarchy';
export const nodeWidth = 110;
export const nodeHeight = 50;
export const vGap = nodeHeight + 20;
-export const hGap = nodeHeight + 20;
+export const hGap = nodeWidth;
export const ellipseWidth = nodeWidth;
+export const labelPadding = 30;
+export const nodeFontSize = 8;
// 数据集节点
const datasetNodes: NodeConfig[] = [];
@@ -38,14 +40,14 @@ export interface TrainDataset extends NodeConfig {
dataset_id: number;
dataset_name: string;
dataset_version: string;
- model_type: NodeType;
+ model_type: NodeType.testDataset | NodeType.trainDataset;
}
export interface ProjectDependency extends NodeConfig {
url: string;
name: string;
branch: string;
- model_type: NodeType;
+ model_type: NodeType.project;
}
export type ModalDetail = {
@@ -63,7 +65,7 @@ export interface ModelDepsAPIData {
current_model_id: number;
version: string;
exp_ins_id: number;
- model_type: NodeType;
+ model_type: NodeType.children | NodeType.current | NodeType.parent;
current_model_name: string;
project_dependency: ProjectDependency;
test_dataset: TrainDataset[];
@@ -94,9 +96,13 @@ export function normalizeChildren(data: ModelDepsData[]) {
// 获取 label
export function getLabel(node: ModelDepsData | ModelDepsAPIData) {
return (
- fittingString(`${node.model_version_dependcy_vo.name ?? ''}`, nodeWidth - 12, 8) +
+ fittingString(
+ `${node.model_version_dependcy_vo.name ?? ''}`,
+ nodeWidth - labelPadding,
+ nodeFontSize,
+ ) +
'\n' +
- fittingString(`${node.version}`, nodeWidth - 12, 8)
+ fittingString(`${node.version}`, nodeWidth - labelPadding, nodeFontSize)
);
}
@@ -231,12 +237,12 @@ const addDatasetDependency = (
node.type = 'ellipse';
node.size = [ellipseWidth, nodeHeight];
node.label =
- fittingString(node.dataset_name, ellipseWidth - 12, 8) +
+ fittingString(node.dataset_name, ellipseWidth - labelPadding, nodeFontSize) +
'\n' +
- fittingString(node.dataset_version, ellipseWidth - 12, 8);
+ fittingString(node.dataset_version, ellipseWidth - labelPadding, nodeFontSize);
const half = len / 2 - 0.5;
- node.x = currentNode.x! - (half - index) * (ellipseWidth + hGap / 2);
+ node.x = currentNode.x! - (half - index) * (ellipseWidth + 20);
node.y = currentNode.y! - nodeHeight - vGap;
nodes.push(node);
datasetNodes.push(node);
@@ -263,7 +269,7 @@ const addProjectDependency = (
node.id = `$P_${node.url}_${node.branch}`;
node.model_type = NodeType.project;
node.type = 'rect';
- node.label = fittingString(node.name, nodeWidth - 12, 8);
+ node.label = fittingString(node.name, nodeWidth - labelPadding, nodeFontSize);
node.style = getStyle(NodeType.project);
node.style.radius = nodeHeight / 2;
node.x = currentNode.x;
@@ -311,15 +317,13 @@ function adjustDatasetPosition(node: NodeConfig) {
};
const overlapRect = isChildrenOverlapDataset(datasetNodes, nodeRect);
if (overlapRect) {
- console.log(node);
-
const adjustRect = {
x: overlapRect.x - nodeWidth - hGap / 2,
y: overlapRect.y,
width: overlapRect.width,
height: overlapRect.height,
};
- const lastNode = datasetNodes[datasetNodes.length - 1] as NodeConfig;
+ const lastNode = datasetNodes[datasetNodes.length - 1];
const distance = lastNode.x! - adjustRect.x;
datasetNodes.forEach((item) => {
item.x = item.x! - distance;
diff --git a/react-ui/src/pages/Model/components/NodeTooltips/index.tsx b/react-ui/src/pages/Model/components/NodeTooltips/index.tsx
index dc926114..a4b3f13b 100644
--- a/react-ui/src/pages/Model/components/NodeTooltips/index.tsx
+++ b/react-ui/src/pages/Model/components/NodeTooltips/index.tsx
@@ -118,14 +118,18 @@ function ProjectInfo({ data }: { data: ProjectDependency }) {
function NodeTooltips({ data, x, y, onMouseEnter, onMouseLeave }: NodeTooltipsProps) {
if (!data) return null;
- let Component;
+ let Component = null;
const { model_type } = data;
if (model_type === NodeType.testDataset || model_type === NodeType.trainDataset) {
- Component = ;
+ Component = ;
} else if (model_type === NodeType.project) {
- Component = ;
- } else {
- Component = ;
+ Component = ;
+ } else if (
+ model_type === NodeType.children ||
+ model_type === NodeType.parent ||
+ model_type === NodeType.current
+ ) {
+ Component = ;
}
return (
{
const [paramsDrawerOpen, openParamsDrawer, closeParamsDrawer] = useVisible(false);
const [globalParam, setGlobalParam, globalParamRef] = useStateRef([]);
const { message } = App.useApp();
- let sourceAnchorIdx, targetAnchorIdx;
+ let sourceAnchorIdx, targetAnchorIdx, dropAnchorIdx;
+ let dragSourceNode;
useEffect(() => {
initMenu();
@@ -35,9 +36,8 @@ const EditPipeline = () => {
}, []);
const onDragEnd = (val) => {
- const _x = val.x;
- const _y = val.y;
- const point = graph.getPointByClient(_x, _y);
+ const { x, y } = val;
+ const point = graph.getPointByClient(x, y);
// 元模型
const model = {
...val,
@@ -46,8 +46,8 @@ const EditPipeline = () => {
id: val.component_name + '-' + s8(),
isCluster: false,
};
- console.log('model', model);
- graph.addItem('node', model, true);
+ // console.log('model', model);
+ graph.addItem('node', model, false);
};
const formChange = (val) => {
if (graph) {
@@ -83,11 +83,11 @@ const EditPipeline = () => {
return;
}
- const [propsRes, propsError] = await to(propsRef.current.getFieldsValue());
- if (propsError) {
- message.error('基本信息必填项需配置');
- return;
- }
+ // const [propsRes, propsError] = await to(propsRef.current.getFieldsValue());
+ // if (propsError) {
+ // message.error('基本信息必填项需配置');
+ // return;
+ // }
propsRef.current.propClose();
setTimeout(() => {
const data = graph.save();
@@ -110,6 +110,10 @@ const EditPipeline = () => {
};
const getGraphData = (data) => {
if (graph) {
+ // 修改历史数据有蓝色边框的问题
+ data.nodes.forEach((item) => {
+ item.style.stroke = '#fff';
+ });
graph.data(data);
graph.render();
} else {
@@ -118,7 +122,6 @@ const EditPipeline = () => {
}, 500);
}
};
-
const processParallelEdgesOnAnchorPoint = (
edges,
offsetDiff = 15,
@@ -230,6 +233,15 @@ const EditPipeline = () => {
}
return edges;
};
+ // 判断两个节点之间是否有边
+ const hasEdge = (source, target) => {
+ const neighbors = source.getNeighbors();
+ for (const node of neighbors) {
+ // 新建边的时候,获取的 neighbors 的数据有问题,不全是 INode 类型,可能没有 getID 方法
+ if (node.getID?.() === target.getID?.()) return true;
+ }
+ return false;
+ };
const cloneElement = (item) => {
console.log(item);
let data = graph.save();
@@ -254,15 +266,13 @@ const EditPipeline = () => {
}
});
};
- const handlerContextMenu = (e) => {
- e.stopPropagation();
- // this.menuType = e.item._cfg.type;
- };
// 上下文菜单
const initMenu = () => {
// const selectedNodes = this.selectedNodes;
contextMenu = new G6.Menu({
getContent(evt) {
+ const type = evt.item.getType();
+ const cloneDisplay = type === 'node' ? 'block' : 'none';
return `
`;
},
@@ -314,11 +323,11 @@ const EditPipeline = () => {
getAnchorPoints(cfg) {
return (
cfg.anchorPoints || [
- // 四个
+ // 四个,上下左右
[0.5, 0],
[0.5, 1],
- // [0, 0.5],
- // [1, 0.5],
+ [0, 0.5],
+ [1, 0.5],
]
);
},
@@ -344,6 +353,7 @@ const EditPipeline = () => {
textAlign: 'left',
textBaseline: 'middle',
fill: '#000',
+ cursor: 'pointer',
},
name: 'text-shape',
draggable: true,
@@ -360,6 +370,7 @@ const EditPipeline = () => {
fill: '#fff',
stroke: '#a4a4a5',
cursor: 'crosshair',
+ lineWidth: 1,
},
name: `anchor-point`, // the name, for searching by group.find(ele => ele.get('name') === 'anchor-point')
anchorPointIdx: i, // flag the idx of the anchor-point circle
@@ -380,10 +391,9 @@ const EditPipeline = () => {
// if (value || point.get('links') > 0) point.show();
// else point.hide();
// });
-
const group = item.getContainer();
const shape = group.get('children')[0];
- const anchorPoints = group.findAll((ele) => ele.get('name') === 'anchor-point');
+ const anchorPoints = group.findAll((item) => item.get('name') === 'anchor-point');
if (name === 'hover') {
if (value) {
shape.attr('stroke', themes['primaryColor']);
@@ -396,6 +406,18 @@ const EditPipeline = () => {
point.hide();
});
}
+ } else if (name === 'drag') {
+ if (sourceAnchorIdx !== null && sourceAnchorIdx !== undefined) {
+ const anchorPoint = anchorPoints[sourceAnchorIdx];
+ anchorPoint.attr('stroke', value ? themes['primaryColor'] : '#a4a4a5');
+ anchorPoint.attr('lineWidth', value ? 2 : 1);
+ }
+ } else if (name === 'drop') {
+ if (dropAnchorIdx !== null && dropAnchorIdx !== undefined) {
+ const anchorPoint = anchorPoints[dropAnchorIdx];
+ anchorPoint.attr('stroke', value ? themes['primaryColor'] : '#a4a4a5');
+ anchorPoint.attr('lineWidth', value ? 2 : 1);
+ }
}
},
},
@@ -407,10 +429,14 @@ const EditPipeline = () => {
width: graphRef.current.clientWidth || 500,
height: graphRef.current.clientHeight || '100%',
animate: false,
- groupByTypes: false,
+ groupByTypes: true,
fitView: true,
plugins: [contextMenu],
- enabledStack: true,
+ enabledStack: false,
+ fitView: true,
+ minZoom: 0.5,
+ maxZoom: 5,
+ fitViewPadding: 300,
modes: {
default: [
// config the shouldBegin for drag-node to avoid node moving while dragging on the anchor-point circles
@@ -420,10 +446,6 @@ const EditPipeline = () => {
if (e.target.get('name') === 'anchor-point') return false;
return true;
},
- // shouldEnd: e => {
- // console.log(e);
- // return false;
- // },
},
// config the shouldBegin and shouldEnd to make sure the create-edge is began and ended at anchor-point circles
{
@@ -434,11 +456,17 @@ const EditPipeline = () => {
if (e.target && e.target.get('name') !== 'anchor-point') return false;
sourceAnchorIdx = e.target.get('anchorPointIdx');
e.target.set('links', e.target.get('links') + 1); // cache the number of edge connected to this anchor-point circle
+ dragSourceNode = e.item;
return true;
},
shouldEnd: (e) => {
// avoid ending at other shapes on the node
if (e.target && e.target.get('name') !== 'anchor-point') return false;
+ if (!dragSourceNode || !e.item) return false;
+ // 不允许连接自己
+ if (dragSourceNode.getID() === e.item.getID()) return false;
+ // 两个节点不允许多条边
+ if (hasEdge(dragSourceNode, e.item)) return false;
if (e.target) {
targetAnchorIdx = e.target.get('anchorPointIdx');
e.target.set('links', e.target.get('links') + 1); // cache the number of edge connected to this anchor-point circle
@@ -450,22 +478,11 @@ const EditPipeline = () => {
},
'drag-canvas',
'zoom-canvas',
- // 'brush-select',
- 'drag-combo',
- ],
- altSelect: [
- {
- type: 'brush-select',
- trigger: 'drag',
- },
- 'drag-node',
],
},
-
defaultNode: {
type: 'rect-node',
size: [110, 36],
-
labelCfg: {
style: {
fill: 'transparent',
@@ -488,8 +505,7 @@ const EditPipeline = () => {
},
},
defaultEdge: {
- //type: 'cubic-vertical',
-
+ // type: 'cubic-vertical',
style: {
endArrow: {
// 设置终点箭头
@@ -503,15 +519,8 @@ const EditPipeline = () => {
stroke: '#CDD0DC',
radius: 1,
},
- nodeStateStyle: {
- hover: {
- opacity: 1,
- stroke: '#8fe8ff',
- },
- },
labelCfg: {
autoRotate: true,
- // refY: 10,
style: {
fontSize: 10,
fill: '#FFF',
@@ -528,20 +537,16 @@ const EditPipeline = () => {
cursor: 'pointer',
},
},
- // linkCenter: true,
- fitView: true,
- minZoom: 0.5,
- maxZoom: 5,
- fitViewPadding: 300,
});
graph.on('node:click', (e) => {
e.stopPropagation();
if (e.target.get('name') !== 'anchor-point' && e.item) {
- // graph.setItemState(e.item, 'nodeClicked', true);
+ // 获取所有的上游节点
const parentNodes = findAllParentNodes(graph, e.item);
// 如果没有打开过全局参数抽屉,获取不到全局参数
const globalParams =
paramsDrawerRef.current.getFieldsValue().global_param || globalParamRef.current;
+ // 打开节点编辑抽屉
propsRef.current.showDrawer(e, globalParams, parentNodes);
}
});
@@ -550,23 +555,26 @@ const EditPipeline = () => {
graph.updateItem(e.edge, {
sourceAnchor: sourceAnchorIdx,
targetAnchor: targetAnchorIdx,
+ type:
+ targetAnchorIdx === 0 || targetAnchorIdx === 1 ? 'cubic-vertical' : 'cubic-horizontal',
});
// update the curveOffset for parallel edges
- const edges = graph.save().edges;
- processParallelEdgesOnAnchorPoint(edges);
- graph.getEdges().forEach((edge, i) => {
- graph.updateItem(edge, {
- curveOffset: edges[i].curveOffset,
- curvePosition: edges[i].curvePosition,
- });
- });
+ // const edges = graph.save().edges;
+ // processParallelEdgesOnAnchorPoint(edges);
+ // graph.getEdges().forEach((edge, i) => {
+ // graph.updateItem(edge, {
+ // curveOffset: edges[i].curveOffset,
+ // curvePosition: edges[i].curvePosition,
+ // });
+ // });
});
+ // 删除边时,修改 anchor-point 的 links 值
graph.on('afterremoveitem', (e) => {
if (e.item && e.item.source && e.item.target) {
- const sourceNode = graph.findById(e.item.source);
- const targetNode = graph.findById(e.item.target);
- const { sourceAnchor, targetAnchor } = e.item;
+ const { source, target, sourceAnchor, targetAnchor } = e.item;
+ const sourceNode = graph.findById(source);
+ const targetNode = graph.findById(target);
if (sourceNode && !isNaN(sourceAnchor)) {
const sourceAnchorShape = sourceNode
.getContainer()
@@ -587,9 +595,10 @@ const EditPipeline = () => {
}
}
});
- // after clicking on the first node, the edge is created, update the sourceAnchor
+ // after drag on the first node, the edge is created, update the sourceAnchor
graph.on('afteradditem', (e) => {
- if (e.item && e.item.getType() === 'edge') {
+ const sourceAnchor = e.item.getModel().sourceAnchor;
+ if (e.item && e.item.getType() === 'edge' && !sourceAnchor) {
graph.updateItem(e.item, {
sourceAnchor: sourceAnchorIdx,
});
@@ -601,17 +610,34 @@ const EditPipeline = () => {
graph.on('node:mouseleave', (e) => {
graph.setItemState(e.item, 'hover', false);
});
- graph.on('node:dragenter', (e) => {
+ graph.on('node:dragstart', (e) => {
graph.setItemState(e.item, 'hover', true);
+ graph.setItemState(e.item, 'drag', true);
});
- graph.on('node:dragleave', (e) => {
+ graph.on('node:dragend', (e) => {
graph.setItemState(e.item, 'hover', false);
+ graph.setItemState(e.item, 'drag', false);
});
- graph.on('node:dragstart', (e) => {
+ graph.on('node:dragenter', (e) => {
+ if (e.item?.getID() === dragSourceNode?.getID()) return;
graph.setItemState(e.item, 'hover', true);
+ if (e.target.get('name') === 'anchor-point') {
+ dropAnchorIdx = e.target.get('anchorPointIdx');
+ graph.setItemState(e.item, 'drop', true);
+ } else {
+ graph.setItemState(e.item, 'drop', false);
+ }
});
- graph.on('node:drag', (e) => {
- graph.setItemState(e.item, 'hover', true);
+ graph.on('node:dragleave', (e) => {
+ if (e.item?.getID() === dragSourceNode?.getID()) return;
+ graph.setItemState(e.item, 'hover', false);
+ graph.setItemState(e.item, 'drop', false);
+ dropAnchorIdx = undefined;
+ });
+ graph.on('node:drop', (e) => {
+ graph.setItemState(e.item, 'hover', false);
+ graph.setItemState(e.item, 'drop', false);
+ dropAnchorIdx = undefined;
});
window.onresize = () => {
if (!graph || graph.get('destroyed')) return;
diff --git a/react-ui/src/pages/Pipeline/index.jsx b/react-ui/src/pages/Pipeline/index.jsx
index cf5fb571..12dbaae8 100644
--- a/react-ui/src/pages/Pipeline/index.jsx
+++ b/react-ui/src/pages/Pipeline/index.jsx
@@ -43,32 +43,30 @@ const Pipeline = () => {
};
const routeToEdit = (e, record) => {
e.stopPropagation();
- navgite({ pathname: `/pipeline/template/${record.id}/${record.name}` });
+ navgite({ pathname: `/pipeline/template/${record.id}` });
};
const showModal = () => {
form.resetFields();
+ setFormId(null);
setDialogTitle('新建流水线');
setIsModalOpen(true);
};
- const handleOk = () => {
- console.log(1111);
- setIsModalOpen(false);
- };
const handleCancel = () => {
setIsModalOpen(false);
};
const onFinish = (values) => {
if (formId) {
editWorkflow({ ...values, id: formId }).then((ret) => {
+ setIsModalOpen(false);
message.success('编辑成功');
getList();
- setIsModalOpen(false);
});
} else {
addWorkflow(values).then((ret) => {
- console.log(ret);
+ setIsModalOpen(false);
+ message.success('新建成功');
if (ret.code === 200) {
- navgite({ pathname: `/pipeline/template/${ret.data.id}/${ret.data.name}` });
+ navgite({ pathname: `/pipeline/template/${ret.data.id}` });
}
});
}
diff --git a/react-ui/src/pages/User/Login/index.tsx b/react-ui/src/pages/User/Login/index.tsx
index f531cbad..220a2b17 100644
--- a/react-ui/src/pages/User/Login/index.tsx
+++ b/react-ui/src/pages/User/Login/index.tsx
@@ -344,7 +344,7 @@ const Login: React.FC = () => {
}}
placeholder={intl.formatMessage({
id: 'pages.login.password.placeholder',
- defaultMessage: '密码: admin123',
+ defaultMessage: '请输入密码',
})}
rules={[
{
@@ -374,7 +374,7 @@ const Login: React.FC = () => {
required: true,
message: (
),
diff --git a/react-ui/src/requestConfig.ts b/react-ui/src/requestConfig.ts
index 8a976abc..4934396c 100644
--- a/react-ui/src/requestConfig.ts
+++ b/react-ui/src/requestConfig.ts
@@ -9,12 +9,19 @@ import { clearSessionToken, getAccessToken } from './access';
import { setRemoteMenu } from './services/session';
import { gotoLoginPage } from './utils/ui';
+// [antd: Notification] You are calling notice in render which will break in React 18 concurrent mode. Please trigger in effect instead.
+const popupError = (error: string) => {
+ // 直接调用 message.error 有时候不弹出来
+ setTimeout(() => {
+ message.error(error);
+ }, 100);
+};
+
/**
* Umi Max 网络请求配置
* @doc https://umijs.org/docs/max/request#配置
*/
export const requestConfig: RequestConfig = {
- errorConfig: {},
requestInterceptors: [
(url: string, options: AxiosRequestConfig) => {
const headers = options.headers ?? {};
@@ -30,26 +37,32 @@ export const requestConfig: RequestConfig = {
},
],
responseInterceptors: [
- (response: AxiosResponse) => {
- const { status, data } = response || {};
- if (status >= 200 && status < 300) {
- if (data && (data instanceof Blob || data.code === 200)) {
- return response;
- } else if (data && data.code === 401) {
- clearSessionToken();
- setRemoteMenu(null);
- gotoLoginPage(false);
- message.error('请重新登录');
- return Promise.reject(response);
+ [
+ (response: AxiosResponse) => {
+ const { status, data } = response || {};
+ console.log(message, data);
+ if (status >= 200 && status < 300) {
+ if (data && (data instanceof Blob || data.code === 200)) {
+ return response;
+ } else if (data && data.code === 401) {
+ clearSessionToken();
+ setRemoteMenu(null);
+ gotoLoginPage(false);
+ popupError('请重新登录');
+ return Promise.reject(response);
+ } else {
+ popupError(data?.msg ?? '请求失败');
+ return Promise.reject(response);
+ }
} else {
- console.log(message, data);
- message.error(data?.msg ?? '请求失败');
+ popupError('请求失败');
return Promise.reject(response);
}
- } else {
- message.error('请求失败');
- return Promise.reject(response);
- }
- },
+ },
+ (error: Error) => {
+ popupError(error.message ?? '请求失败');
+ return Promise.reject(error);
+ },
+ ],
],
};
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/ExperimentInstanceStatusTask.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/ExperimentInstanceStatusTask.java
index 91ed6847..4680285e 100644
--- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/ExperimentInstanceStatusTask.java
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/ExperimentInstanceStatusTask.java
@@ -34,7 +34,7 @@ public class ExperimentInstanceStatusTask {
private ModelDependencyDao modelDependencyDao;
private List
experimentIds = new ArrayList<>();
- @Scheduled(cron = "0/30 * * * * ?") // 每30S执行一次
+ @Scheduled(cron = "0/14 * * * * ?") // 每30S执行一次
public void executeExperimentInsStatus() throws IOException {
// 首先查到所有非终止态的实验实例
List experimentInsList = experimentInsService.queryByExperimentIsNotTerminated();
@@ -49,7 +49,7 @@ public class ExperimentInstanceStatusTask {
}catch (Exception e){
experimentIns.setStatus("Failed");
}
- if (!StringUtils.equals(oldStatus,experimentIns.getStatus())){
+// if (!StringUtils.equals(oldStatus,experimentIns.getStatus())){
experimentIns.setUpdateTime(new Date());
// 线程安全的添加操作
synchronized (experimentIds) {
@@ -57,7 +57,7 @@ public class ExperimentInstanceStatusTask {
}
updateList.add(experimentIns);
- }
+// }
// experimentInsDao.update(experimentIns);
}
@@ -105,7 +105,7 @@ public class ExperimentInstanceStatusTask {
}
}
- @Scheduled(cron = "0/30 * * * * ?") // / 每30S执行一次
+ @Scheduled(cron = "0/17 * * * * ?") // / 每30S执行一次
public void executeExperimentStatus() throws IOException {
if (experimentIds.size()==0){
return;