fix(web): loop/iteration edge
This commit is contained in:
@@ -34,9 +34,12 @@ const PortClickHandler: React.FC<PortClickHandlerProps> = ({ graph }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('port:click', handlePortClick as EventListener);
|
window.addEventListener('port:click', handlePortClick as EventListener);
|
||||||
|
const handleBlankClick = () => handlePopoverClose();
|
||||||
|
window.addEventListener('blank:click', handleBlankClick);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('port:click', handlePortClick as EventListener);
|
window.removeEventListener('port:click', handlePortClick as EventListener);
|
||||||
|
window.removeEventListener('blank:click', handleBlankClick);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ export const useWorkflowGraph = ({
|
|||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
name,
|
name,
|
||||||
data: { ...node, ...nodeLibraryConfig},
|
data: { ...node, ...nodeLibraryConfig },
|
||||||
...position,
|
...position,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,7 +294,8 @@ export const useWorkflowGraph = ({
|
|||||||
x: nodeWidth,
|
x: nodeWidth,
|
||||||
y: portItemArgsY + portItemArgsY,
|
y: portItemArgsY + portItemArgsY,
|
||||||
},
|
},
|
||||||
id: 'ERROR', attrs: { text: { text: t('workflow.config.http-request.errorBranch'), ...portTextAttrs }}}
|
id: 'ERROR', attrs: { text: { text: t('workflow.config.http-request.errorBranch'), ...portTextAttrs } }
|
||||||
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -444,9 +445,17 @@ export const useWorkflowGraph = ({
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (graphRef.current) {
|
if (graphRef.current) {
|
||||||
graphRef.current.centerContent()
|
graphRef.current.centerContent()
|
||||||
// graphRef.current.getNodes().forEach(node => node.toFront());
|
graphRef.current.getNodes().forEach(node => {
|
||||||
|
if (!node.getData()?.cycle) node.toFront();
|
||||||
|
});
|
||||||
// Bring edges to front first, then child nodes above edges; parent nodes stay behind
|
// Bring edges to front first, then child nodes above edges; parent nodes stay behind
|
||||||
graphRef.current.getEdges().forEach(edge => edge.toFront());
|
graphRef.current.getEdges().forEach(edge => {
|
||||||
|
const sourceCell = graphRef.current?.getCellById(edge.getSourceCellId());
|
||||||
|
const targetCell = graphRef.current?.getCellById(edge.getTargetCellId());
|
||||||
|
if (sourceCell?.getData()?.cycle || targetCell?.getData()?.cycle) {
|
||||||
|
edge.toFront();
|
||||||
|
}
|
||||||
|
});
|
||||||
graphRef.current.getNodes().forEach(node => {
|
graphRef.current.getNodes().forEach(node => {
|
||||||
if (node.getData()?.cycle) node.toFront();
|
if (node.getData()?.cycle) node.toFront();
|
||||||
});
|
});
|
||||||
@@ -575,6 +584,7 @@ export const useWorkflowGraph = ({
|
|||||||
clearEdgeSelect();
|
clearEdgeSelect();
|
||||||
graphRef.current?.cleanSelection();
|
graphRef.current?.cleanSelection();
|
||||||
setSelectedNode(null);
|
setSelectedNode(null);
|
||||||
|
window.dispatchEvent(new CustomEvent('blank:click'));
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* Handle canvas scale/zoom event
|
* Handle canvas scale/zoom event
|
||||||
@@ -975,7 +985,6 @@ export const useWorkflowGraph = ({
|
|||||||
if (!port) return;
|
if (!port) return;
|
||||||
const portData = node.getPort(port);
|
const portData = node.getPort(port);
|
||||||
if (portData?.group !== 'right') return;
|
if (portData?.group !== 'right') return;
|
||||||
node.toFront();
|
|
||||||
node.setPortProp(port, 'attrs/body/opacity', 0);
|
node.setPortProp(port, 'attrs/body/opacity', 0);
|
||||||
node.setPortProp(port, 'attrs/hoverBody/opacity', 1);
|
node.setPortProp(port, 'attrs/hoverBody/opacity', 1);
|
||||||
node.setPortProp(port, 'attrs/label/opacity', 1);
|
node.setPortProp(port, 'attrs/label/opacity', 1);
|
||||||
@@ -1028,33 +1037,28 @@ export const useWorkflowGraph = ({
|
|||||||
graphRef.current.on('scale', scaleEvent);
|
graphRef.current.on('scale', scaleEvent);
|
||||||
// Listen to node move event
|
// Listen to node move event
|
||||||
graphRef.current.on('node:moved', nodeMoved);
|
graphRef.current.on('node:moved', nodeMoved);
|
||||||
// When parent (isGroup) node position changes, move children with it
|
|
||||||
graphRef.current.on('node:change:position', ({ node, current, previous }: { node: Node; current: { x: number; y: number }; previous: { x: number; y: number } }) => {
|
|
||||||
|
|
||||||
if (!(node.getData()?.type === 'iteration' && node.getData()?.type === 'loop') || !current || !previous) return;
|
|
||||||
|
|
||||||
const dx = current.x - previous.x;
|
|
||||||
const dy = current.y - previous.y;
|
|
||||||
const parentId = node.getData()?.id || node.id;
|
|
||||||
graphRef.current?.getNodes().forEach(child => {
|
|
||||||
if (child.getData()?.cycle === parentId) {
|
|
||||||
const cp = child.getPosition();
|
|
||||||
child.setPosition(cp.x + dx, cp.y + dy, { silent: true });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
graphRef.current.on('node:removed', blankClick)
|
graphRef.current.on('node:removed', blankClick)
|
||||||
// When edge connected, bring connected nodes' ports to front
|
// When edge connected, bring connected nodes' ports to front
|
||||||
graphRef.current.on('edge:connected', ({ isNew, edge }) => {
|
graphRef.current.on('edge:connected', ({ isNew }) => {
|
||||||
// Bring edge to front first, then bring child nodes above edges
|
// Bring edge to front first, then bring child nodes above edges
|
||||||
// Parent (loop/iteration) nodes stay behind to avoid covering edges
|
// Parent (loop/iteration) nodes stay behind to avoid covering edges
|
||||||
edge.toFront();
|
|
||||||
graphRef.current?.getNodes().forEach(node => {
|
|
||||||
if (node.getData()?.cycle) node.toFront();
|
|
||||||
});
|
|
||||||
// Reset any port hover state left from dragging
|
// Reset any port hover state left from dragging
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
graphRef.current?.getNodes().forEach(node => {
|
graphRef.current?.getNodes().forEach(node => {
|
||||||
|
if (!node.getData()?.cycle) node.toFront();
|
||||||
|
});
|
||||||
|
graphRef.current?.getEdges().forEach(edge => {
|
||||||
|
const sourceCell = graphRef.current?.getCellById(edge.getSourceCellId());
|
||||||
|
const targetCell = graphRef.current?.getCellById(edge.getTargetCellId());
|
||||||
|
if (sourceCell?.getData()?.cycle || targetCell?.getData()?.cycle) {
|
||||||
|
edge.toFront();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
graphRef.current?.getNodes().forEach(node => {
|
||||||
|
graphRef.current?.getNodes().forEach(node => {
|
||||||
|
if (node.getData()?.cycle) node.toFront();
|
||||||
|
});
|
||||||
node.getPorts().filter(p => p.group === 'right').forEach(p => {
|
node.getPorts().filter(p => p.group === 'right').forEach(p => {
|
||||||
node.setPortProp(p.id!, 'attrs/body/opacity', 1);
|
node.setPortProp(p.id!, 'attrs/body/opacity', 1);
|
||||||
node.setPortProp(p.id!, 'attrs/hoverBody/opacity', 0);
|
node.setPortProp(p.id!, 'attrs/hoverBody/opacity', 0);
|
||||||
@@ -1100,7 +1104,6 @@ export const useWorkflowGraph = ({
|
|||||||
// Enter new
|
// Enter new
|
||||||
if (found) {
|
if (found) {
|
||||||
const { node, portId } = found;
|
const { node, portId } = found;
|
||||||
node.toFront();
|
|
||||||
node.setPortProp(portId, 'attrs/body/opacity', 0);
|
node.setPortProp(portId, 'attrs/body/opacity', 0);
|
||||||
node.setPortProp(portId, 'attrs/hoverBody/opacity', 1);
|
node.setPortProp(portId, 'attrs/hoverBody/opacity', 1);
|
||||||
node.setPortProp(portId, 'attrs/label/opacity', 1);
|
node.setPortProp(portId, 'attrs/label/opacity', 1);
|
||||||
@@ -1327,7 +1330,7 @@ export const useWorkflowGraph = ({
|
|||||||
|
|
||||||
// Filter invalid edges: source or target node doesn't exist, or is add-node type
|
// Filter invalid edges: source or target node doesn't exist, or is add-node type
|
||||||
if (!sourceCell?.getData()?.id || !targetCell?.getData()?.id ||
|
if (!sourceCell?.getData()?.id || !targetCell?.getData()?.id ||
|
||||||
sourceCell?.getData()?.type === 'add-node' || targetCell?.getData()?.type === 'add-node') {
|
sourceCell?.getData()?.type === 'add-node' || targetCell?.getData()?.type === 'add-node') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1371,34 +1374,34 @@ export const useWorkflowGraph = ({
|
|||||||
target: targetCell?.getData().id,
|
target: targetCell?.getData().id,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter(edge => edge !== null)
|
.filter(edge => edge !== null)
|
||||||
.filter((edge, index, arr) => {
|
.filter((edge, index, arr) => {
|
||||||
// Deduplication: For if-else and question-classifier nodes, different ports can connect to same node
|
// Deduplication: For if-else and question-classifier nodes, different ports can connect to same node
|
||||||
return arr.findIndex(e => {
|
return arr.findIndex(e => {
|
||||||
if (!e || !edge) return false;
|
if (!e || !edge) return false;
|
||||||
const sourceCell = graphRef.current?.getCellById(e.source);
|
const sourceCell = graphRef.current?.getCellById(e.source);
|
||||||
const sourceType = sourceCell?.getData()?.type;
|
const sourceType = sourceCell?.getData()?.type;
|
||||||
const isMultiPortNode = sourceType === 'question-classifier' || sourceType === 'if-else';
|
const isMultiPortNode = sourceType === 'question-classifier' || sourceType === 'if-else';
|
||||||
|
|
||||||
if (isMultiPortNode) {
|
if (isMultiPortNode) {
|
||||||
// Multi-port nodes need to compare source, target and label
|
// Multi-port nodes need to compare source, target and label
|
||||||
return e.source === edge.source && e.target === edge.target && e.label === edge.label;
|
return e.source === edge.source && e.target === edge.target && e.label === edge.label;
|
||||||
} else {
|
} else {
|
||||||
// Other nodes only compare source and target
|
// Other nodes only compare source and target
|
||||||
return e.source === edge.source && e.target === edge.target;
|
return e.source === edge.source && e.target === edge.target;
|
||||||
}
|
}
|
||||||
}) === index;
|
}) === index;
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
saveWorkflowConfig(config.app_id, params as WorkflowConfig)
|
saveWorkflowConfig(config.app_id, params as WorkflowConfig)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (flag) {
|
if (flag) {
|
||||||
message.success({ content: t('common.saveSuccess'), duration: 1 })
|
message.success({ content: t('common.saveSuccess'), duration: 1 })
|
||||||
}
|
}
|
||||||
resolve(res)
|
resolve(res)
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
reject(error)
|
reject(error)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user