Merge pull request #835 from SuanmoSuanyangTechnology/feature/ui_upgrade_zy

Feature/UI upgrade zy
This commit is contained in:
yingzhao
2026-04-09 11:50:58 +08:00
committed by GitHub
4 changed files with 110 additions and 104 deletions

View File

@@ -94,7 +94,7 @@ const CreateDataset = () => {
const [processingMethod, setProcessingMethod] = useState<ProcessingMethod>('directBlock');
const [parameterSettings, setParameterSettings] = useState<ParameterSettings>('defaultSettings');
const [pdfEnhancementEnabled, setPdfEnhancementEnabled] = useState<boolean>(true);
const [pdfEnhancementMethod, setPdfEnhancementMethod] = useState<string>('deepdoc');
const [pdfEnhancementMethod, setPdfEnhancementMethod] = useState<string>('mineru');
const fileType = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'csv', 'md', 'htm', 'html', 'json', 'ppt', 'pptx', 'txt','png','jpg','mp3','mp4','mov','wav']
const steps = useMemo(
() => [

View File

@@ -50,7 +50,7 @@ const CommandPlugin = () => {
// Create and insert the variable node
const tagNode = $createVariableNode(payload.data);
const spaceNode = $createTextNode(' ');
const spaceNode = $createTextNode('');
anchorNode.insertAfter(tagNode);
tagNode.insertAfter(spaceNode);

View File

@@ -34,9 +34,12 @@ const PortClickHandler: React.FC<PortClickHandlerProps> = ({ graph }) => {
};
window.addEventListener('port:click', handlePortClick as EventListener);
const handleBlankClick = () => handlePopoverClose();
window.addEventListener('blank:click', handleBlankClick);
return () => {
window.removeEventListener('port:click', handlePortClick as EventListener);
window.removeEventListener('blank:click', handleBlankClick);
};
}, []);

View File

@@ -94,10 +94,10 @@ export const useWorkflowGraph = ({
const { message } = App.useApp();
const { t } = useTranslation()
const { user } = useUser();
// Refs
const graphRef = useRef<Graph>();
// State
const [selectedNode, setSelectedNode] = useState<Node | null>(null);
const [zoomLevel, setZoomLevel] = useState(1);
@@ -134,7 +134,7 @@ export const useWorkflowGraph = ({
useEffect(() => {
initWorkflow()
}, [config, graphRef.current])
/**
* Initialize workflow graph with nodes and edges from configuration
*/
@@ -211,7 +211,7 @@ export const useWorkflowGraph = ({
id,
type,
name,
data: { ...node, ...nodeLibraryConfig},
data: { ...node, ...nodeLibraryConfig },
...position,
}
@@ -221,11 +221,11 @@ export const useWorkflowGraph = ({
if (w) nodeConfig.width = w as number;
if (h) nodeConfig.height = h as number;
}
// Generate ports dynamically for if-else node based on cases
if (type === 'if-else' && config.cases && Array.isArray(config.cases)) {
const totalPorts = config.cases.length + 1; // IF/ELIF + ELSE
const portItems: PortMetadata[] = [
defaultPortItems[0],
];
@@ -240,24 +240,24 @@ export const useWorkflowGraph = ({
},
});
}
nodeConfig.ports = {
groups: defaultAbsolutePortGroups,
items: portItems
};
nodeConfig.height = calcConditionNodeTotalHeight(config.cases);
}
// Generate ports dynamically for question-classifier node based on categories
if (type === 'question-classifier' && config.categories && Array.isArray(config.categories)) {
const categoryCount = config.categories.length;
const newHeight = conditionNodeHeight + (categoryCount - 2) * conditionNodeItemHeight;
const portItems: PortMetadata[] = [
defaultPortItems[0]
];
// Add category ports
config.categories.forEach((_category: any, index: number) => {
portItems.push({
@@ -269,15 +269,15 @@ export const useWorkflowGraph = ({
},
});
});
nodeConfig.ports = {
groups: defaultAbsolutePortGroups,
items: portItems
};
nodeConfig.height = newHeight;
}
// Check error_handle.method config for http-request node
if (type === 'http-request' && (nodeConfig as any).error_handle?.method === 'branch') {
nodeConfig.ports = {
@@ -294,21 +294,22 @@ export const useWorkflowGraph = ({
x: nodeWidth,
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 } }
}
]
};
}
return nodeConfig
})
// Separate parent nodes and child nodes
const parentNodes = nodeList.filter(node => !node.data.cycle)
const childNodes = nodeList.filter(node => node.data.cycle)
// Add parent nodes first
graphRef.current?.addNodes(parentNodes)
// Then process child nodes, use addChild to add to corresponding parent node
childNodes.forEach(childNode => {
const cycleId = childNode.data.cycle
@@ -322,14 +323,14 @@ export const useWorkflowGraph = ({
}
}
})
// Adjust parent node size to fit child nodes
setTimeout(() => {
const parentNodesWithChildren = parentNodes.filter(parentNode => {
const parentId = parentNode.data.id
return childNodes.some(child => child.data.cycle === parentId)
})
parentNodesWithChildren.forEach(parentNodeConfig => {
const parentNode = graphRef.current?.getCellById(parentNodeConfig.data.id)
if (parentNode) {
@@ -340,18 +341,18 @@ export const useWorkflowGraph = ({
const minY = Math.min(...childBounds.map(b => b.y))
const maxX = Math.max(...childBounds.map(b => b.x + b.width))
const maxY = Math.max(...childBounds.map(b => b.y + b.height))
const padding = 24
const headerHeight = 50
const parentBBox = parentNode.getBBox()
const newWidth = Math.max(parentBBox.width, maxX - minX + padding * 2)
const newHeight = Math.max(parentBBox.height, maxY - minY + padding * 2 + headerHeight)
console.log('newWidth', newHeight, newWidth)
parentNode.prop('size', { width: newWidth, height: newHeight })
// Update x position of right group ports
const ports = (parentNode as Node).getPorts()
ports.forEach(port => {
@@ -371,7 +372,7 @@ export const useWorkflowGraph = ({
const sourceCell = graphRef.current?.getCellById(e.source);
const sourceType = sourceCell?.getData()?.type;
const isMultiPortNode = sourceType === 'question-classifier' || sourceType === 'if-else';
if (isMultiPortNode) {
// Multi-port nodes need to compare source, target and label
return e.source === edge.source && e.target === edge.target && e.label === edge.label;
@@ -381,18 +382,18 @@ export const useWorkflowGraph = ({
}
}) === index;
});
const edgeList = uniqueEdges.map(edge => {
const { source, target, label } = edge
const sourceCell = graphRef.current?.getCellById(source)
const targetCell = graphRef.current?.getCellById(target)
if (sourceCell && targetCell) {
const sourcePorts = (sourceCell as Node).getPorts()
const targetPorts = (targetCell as Node).getPorts()
let sourcePort = sourcePorts.find((port: any) => port.group === 'right')?.id || 'right';
// If if-else node has label, match corresponding port by label
if (sourceCell.getData()?.type === 'if-else' && label) {
// Find matching port ID
@@ -401,7 +402,7 @@ export const useWorkflowGraph = ({
sourcePort = label;
}
}
// If question-classifier node has label, match corresponding port by label
if (sourceCell.getData()?.type === 'question-classifier' && label) {
const matchingPort = sourcePorts.find((port: any) => port.id === label);
@@ -409,7 +410,7 @@ export const useWorkflowGraph = ({
sourcePort = label;
}
}
// If http-request node has label, match corresponding port by label
if (sourceCell.getData()?.type === 'http-request' && label) {
const matchingPort = sourcePorts.find((port: any) => port.id === label);
@@ -417,7 +418,7 @@ export const useWorkflowGraph = ({
sourcePort = label;
}
}
const edgeConfig = {
source: {
cell: sourceCell.id,
@@ -438,15 +439,23 @@ export const useWorkflowGraph = ({
})
graphRef.current.addEdges(edgeList.filter(vo => vo !== null))
}
// Initialize after completion, display nodes in visible area
if (nodes.length > 0 || edges.length > 0) {
setTimeout(() => {
if (graphRef.current) {
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
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 => {
if (node.getData()?.cycle) node.toFront();
});
@@ -575,6 +584,7 @@ export const useWorkflowGraph = ({
clearEdgeSelect();
graphRef.current?.cleanSelection();
setSelectedNode(null);
window.dispatchEvent(new CustomEvent('blank:click'));
};
/**
* Handle canvas scale/zoom event
@@ -595,26 +605,26 @@ export const useWorkflowGraph = ({
// Get parent node and child node bounding boxes
const parentBBox = parentNode.getBBox();
const childBBox = node.getBBox();
// Calculate parent node padding
const padding = 24;
const headerHeight = 50;
// Calculate minimum and maximum positions allowed for child node
const minX = parentBBox.x + padding;
const minY = parentBBox.y + padding + headerHeight;
const maxX = parentBBox.x + parentBBox.width - padding - childBBox.width;
const maxY = parentBBox.y + parentBBox.height - padding - childBBox.height;
// Restrict child node movement within parent node
let newX = childBBox.x;
let newY = childBBox.y;
if (newX < minX) newX = minX;
if (newY < minY) newY = minY;
if (newX > maxX) newX = maxX;
if (newY > maxY) newY = maxY;
// If child node position is restricted, update its position
if (newX !== childBBox.x || newY !== childBBox.y) {
node.setPosition(newX, newY);
@@ -706,7 +716,7 @@ export const useWorkflowGraph = ({
}
// Add child node to deletion list
cells.push(nodeToDelete);
}
}
// Check if it's LoopNode, IterationNode or SubGraphNode
else if (nodeToDelete.shape === 'loop-node' || nodeToDelete.shape === 'iteration-node' || nodeToDelete.shape === 'subgraph-node') {
// Find all child nodes with cycle equal to current node id
@@ -718,7 +728,7 @@ export const useWorkflowGraph = ({
});
// Add parent node to deletion list
cells.push(nodeToDelete);
}
}
// Normal node
else {
cells.push(nodeToDelete);
@@ -726,7 +736,7 @@ export const useWorkflowGraph = ({
});
blankClick();
}
// Delete all collected nodes and edges
if (cells.length > 0) {
graphRef.current?.removeCells(cells);
@@ -873,19 +883,19 @@ export const useWorkflowGraph = ({
const targetGroup = targetMagnet ? getPortGroup(targetMagnet) : null;
if (sourceGroup === 'left' || targetGroup === 'right') return false;
// Node cannot connect to itself
if (sourceCell?.id === targetCell?.id) return false;
const targetType = targetCell?.getData()?.type;
// Start node cannot be connection target
if (targetType === 'start') return false;
// Get source node and target node parent IDs
const sourceParentId = sourceCell?.getData()?.cycle;
const targetParentId = targetCell?.getData()?.cycle;
// Validate parent-child relationship:
// 1. If both nodes have parent IDs, they must be same to connect
// 2. If both have no parent ID, can connect normally
@@ -975,7 +985,6 @@ export const useWorkflowGraph = ({
if (!port) return;
const portData = node.getPort(port);
if (portData?.group !== 'right') return;
node.toFront();
node.setPortProp(port, 'attrs/body/opacity', 0);
node.setPortProp(port, 'attrs/hoverBody/opacity', 1);
node.setPortProp(port, 'attrs/label/opacity', 1);
@@ -1028,33 +1037,28 @@ export const useWorkflowGraph = ({
graphRef.current.on('scale', scaleEvent);
// Listen to node move event
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)
// 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
// 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
if (isNew) {
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.setPortProp(p.id!, 'attrs/body/opacity', 1);
node.setPortProp(p.id!, 'attrs/hoverBody/opacity', 0);
@@ -1100,7 +1104,6 @@ export const useWorkflowGraph = ({
// Enter new
if (found) {
const { node, portId } = found;
node.toFront();
node.setPortProp(portId, 'attrs/body/opacity', 0);
node.setPortProp(portId, 'attrs/hoverBody/opacity', 1);
node.setPortProp(portId, 'attrs/label/opacity', 1);
@@ -1173,7 +1176,7 @@ export const useWorkflowGraph = ({
if (!graph) return;
const point = graphRef.current.clientToLocal(event.clientX, event.clientY);
// Get original config from node library to avoid config data chaining
let nodeLibraryConfig = [...nodeLibrary]
.flatMap(category => category.nodes)
@@ -1326,11 +1329,11 @@ export const useWorkflowGraph = ({
const sourcePortId = edge.getSourcePortId();
// Filter invalid edges: source or target node doesn't exist, or is add-node type
if (!sourceCell?.getData()?.id || !targetCell?.getData()?.id ||
sourceCell?.getData()?.type === 'add-node' || targetCell?.getData()?.type === 'add-node') {
if (!sourceCell?.getData()?.id || !targetCell?.getData()?.id ||
sourceCell?.getData()?.type === 'add-node' || targetCell?.getData()?.type === 'add-node') {
return null;
}
// If if-else node right port connection, add label
if (sourceCell?.getData()?.type === 'if-else' && sourcePortId?.startsWith('CASE')) {
return {
@@ -1339,7 +1342,7 @@ export const useWorkflowGraph = ({
label: sourcePortId,
};
}
// If question-classifier node right port connection, add label
if (sourceCell?.getData()?.type === 'question-classifier' && sourcePortId?.startsWith('CASE')) {
return {
@@ -1348,7 +1351,7 @@ export const useWorkflowGraph = ({
label: sourcePortId,
};
}
// If http-request node right port connection, add label
if (sourceCell?.getData()?.type === 'http-request') {
if (sourcePortId === 'ERROR') {
@@ -1365,40 +1368,40 @@ export const useWorkflowGraph = ({
};
}
}
return {
source: sourceCell?.getData().id,
target: targetCell?.getData().id,
};
})
.filter(edge => edge !== null)
.filter((edge, index, arr) => {
// Deduplication: For if-else and question-classifier nodes, different ports can connect to same node
return arr.findIndex(e => {
if (!e || !edge) return false;
const sourceCell = graphRef.current?.getCellById(e.source);
const sourceType = sourceCell?.getData()?.type;
const isMultiPortNode = sourceType === 'question-classifier' || sourceType === 'if-else';
if (isMultiPortNode) {
// Multi-port nodes need to compare source, target and label
return e.source === edge.source && e.target === edge.target && e.label === edge.label;
} else {
// Other nodes only compare source and target
return e.source === edge.source && e.target === edge.target;
}
}) === index;
}),
.filter(edge => edge !== null)
.filter((edge, index, arr) => {
// Deduplication: For if-else and question-classifier nodes, different ports can connect to same node
return arr.findIndex(e => {
if (!e || !edge) return false;
const sourceCell = graphRef.current?.getCellById(e.source);
const sourceType = sourceCell?.getData()?.type;
const isMultiPortNode = sourceType === 'question-classifier' || sourceType === 'if-else';
if (isMultiPortNode) {
// Multi-port nodes need to compare source, target and label
return e.source === edge.source && e.target === edge.target && e.label === edge.label;
} else {
// Other nodes only compare source and target
return e.source === edge.source && e.target === edge.target;
}
}) === index;
}),
}
saveWorkflowConfig(config.app_id, params as WorkflowConfig)
.then((res) => {
if (flag) {
message.success({ content: t('common.saveSuccess'), duration: 1 })
}
resolve(res)
}).catch(error => {
reject(error)
})
.then((res) => {
if (flag) {
message.success({ content: t('common.saveSuccess'), duration: 1 })
}
resolve(res)
}).catch(error => {
reject(error)
})
})
}