diff --git a/frontend/src/pages/Workflow/Definition/Design/index.tsx b/frontend/src/pages/Workflow/Definition/Design/index.tsx index 6eb1e34a..5f78dc90 100644 --- a/frontend/src/pages/Workflow/Definition/Design/index.tsx +++ b/frontend/src/pages/Workflow/Definition/Design/index.tsx @@ -181,6 +181,7 @@ const WorkflowDesign: React.FC = () => { snapline: true, keyboard: { enabled: true, + global: false, }, panning: { enabled: true, @@ -370,6 +371,42 @@ const WorkflowDesign: React.FC = () => { registerEventHandlers(graph); + // 在 registerEventHandlers 中添加画布的快捷键处理 + // 添加画布的快捷键处理 + graphContainerRef.current?.addEventListener('keydown', (e: KeyboardEvent) => { + // 只有当画布或其子元素被聚焦时才处理快捷键 + if (!graphContainerRef.current?.contains(document.activeElement)) { + return; + } + + // Ctrl+A 或 Command+A (Mac) + if ((e.ctrlKey || e.metaKey) && e.key === 'a') { + e.preventDefault(); // 阻止浏览器默认的全选行为 + if (!graph) return; + + const cells = graph.getCells(); + if (cells.length > 0) { + graph.resetSelection(); + graph.select(cells); + // 为选中的元素添加高亮样式 + cells.forEach(cell => { + if (cell.isNode()) { + cell.setAttrByPath('body/stroke', '#1890ff'); + cell.setAttrByPath('body/strokeWidth', 3); + cell.setAttrByPath('body/strokeDasharray', '5 5'); + } else if (cell.isEdge()) { + cell.setAttrByPath('line/stroke', '#1890ff'); + cell.setAttrByPath('line/strokeWidth', 3); + cell.setAttrByPath('line/strokeDasharray', '5 5'); + } + }); + } + } + }); + + // 确保画布可以接收键盘事件 + graphContainerRef.current?.setAttribute('tabindex', '0'); + return graph; }; @@ -922,6 +959,35 @@ const WorkflowDesign: React.FC = () => { graph.on('edge:unselected', ({ edge }) => { edge.removeTools(); }); + + // 在 registerEventHandlers 函数中更新选择状态的视觉反馈 + graph.on('selection:changed', ({ selected, removed }) => { + // 处理新选中的元素 + selected.forEach(cell => { + if (cell.isNode()) { + cell.setAttrByPath('body/stroke', '#1890ff'); + cell.setAttrByPath('body/strokeWidth', 3); + cell.setAttrByPath('body/strokeDasharray', '5 5'); + } else if (cell.isEdge()) { + cell.setAttrByPath('line/stroke', '#1890ff'); + cell.setAttrByPath('line/strokeWidth', 3); + cell.setAttrByPath('line/strokeDasharray', '5 5'); + } + }); + + // 处理取消选中的元素 + removed.forEach(cell => { + if (cell.isNode()) { + cell.setAttrByPath('body/stroke', '#5F95FF'); + cell.setAttrByPath('body/strokeWidth', 1); + cell.setAttrByPath('body/strokeDasharray', null); + } else if (cell.isEdge()) { + cell.setAttrByPath('line/stroke', '#5F95FF'); + cell.setAttrByPath('line/strokeWidth', 1); + cell.setAttrByPath('line/strokeDasharray', null); + } + }); + }); }; // 处理复制操作 @@ -1037,6 +1103,7 @@ const WorkflowDesign: React.FC = () => { cell: targetNode.id, port: targetPort, }, + vertices: edge?.vertices || [], // 恢复顶点信息 attrs: { line: { stroke: '#5F95FF', @@ -1055,11 +1122,10 @@ const WorkflowDesign: React.FC = () => { } }] }); - console.log(response.graph) + // 设置边的条件属性 if (edge.config?.condition) { newEdge.setProp('condition', edge.config.condition); - console.log('Setting edge condition:', edge.config.condition); } } }); @@ -1217,6 +1283,7 @@ const WorkflowDesign: React.FC = () => { const edges = graph.getEdges().map(edge => { const sourceNode = graph.getCellById(edge.getSourceCellId()); const condition = edge.getProp('condition'); + const vertices = edge.getVertices(); // 获取边的顶点信息 return { id: edge.id, @@ -1226,7 +1293,8 @@ const WorkflowDesign: React.FC = () => { config: { type: 'sequence', condition: condition || undefined - } + }, + vertices: vertices // 保存顶点信息 }; }); @@ -1370,36 +1438,28 @@ const WorkflowDesign: React.FC = () => {