From 8bf439233b1e527f96178f50b78f1149a4eaec91 Mon Sep 17 00:00:00 2001 From: luomgf Date: Wed, 13 May 2026 22:40:26 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E4=BE=9D=E8=B5=96=E4=B8=8E=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修复侧边栏菜单项 index 可能为 undefined 导致的 Element Plus 类型错。 --- .../fa-sidebar-menu/widget/SidebarSubmenu.vue | 15 +++- .../components/WorkflowDesignDrawer.vue | 80 ++++++++++--------- 2 files changed, 58 insertions(+), 37 deletions(-) diff --git a/frontend/web/src/components/layouts/fa-menus/fa-sidebar-menu/widget/SidebarSubmenu.vue b/frontend/web/src/components/layouts/fa-menus/fa-sidebar-menu/widget/SidebarSubmenu.vue index acf2e830e..d158adc96 100755 --- a/frontend/web/src/components/layouts/fa-menus/fa-sidebar-menu/widget/SidebarSubmenu.vue +++ b/frontend/web/src/components/layouts/fa-menus/fa-sidebar-menu/widget/SidebarSubmenu.vue @@ -26,7 +26,7 @@ @@ -192,6 +192,19 @@ const isExternalLink = (item: AppRouteRecord): boolean => { return !!(item.meta.link && !item.meta.isIframe); }; +/** + * 生成叶子菜单的索引 + * @param item 菜单项数据 + * @param index 索引 + * @returns 菜单索引 + */ +const getLeafMenuIndex = (item: AppRouteRecord, index: number): string => { + if (isExternalLink(item)) { + return item.meta.link || item.path || `external-${props.level}-${index}`; + } + return item.path || item.meta.title || `menu-${props.level}-${index}`; +}; + /** * 生成唯一的 key * 使用 path、title 和 index 组合确保唯一性 diff --git a/frontend/web/src/views/module_task/workflow/components/WorkflowDesignDrawer.vue b/frontend/web/src/views/module_task/workflow/components/WorkflowDesignDrawer.vue index 499547178..f58da1705 100644 --- a/frontend/web/src/views/module_task/workflow/components/WorkflowDesignDrawer.vue +++ b/frontend/web/src/views/module_task/workflow/components/WorkflowDesignDrawer.vue @@ -209,6 +209,9 @@ import WorkflowDefinitionAPI, { } from "@/api/module_task/workflow/definition"; import WorkflowNodeTypeAPI from "@/api/module_task/workflow/node-type"; +type FlowNode = Node; +type FlowEdge = Edge; + defineOptions({ name: "WorkflowCreateDrawer", inheritAttrs: false, @@ -435,8 +438,8 @@ const handleFormatCanvas = () => { ElMessage.success("画布格式化完成"); }; -const nodes = ref([]); -const edges = ref([]); +const nodes = ref([]); +const edges = ref([]); const searchKeyword = ref(""); @@ -482,12 +485,12 @@ const getCategoryText = (category: string) => { const nodeTypesRegistry = ref>({}); const updateState = ref(""); -const selectedEdge = ref(); -const selectedNode = ref(); +const selectedEdge = ref(); +const selectedNode = ref(); const loading = ref(false); -const getNodes = () => getNodesRef.value; -const getEdges = () => +const getNodes = (): FlowNode[] => getNodesRef.value as FlowNode[]; +const getEdges = (): FlowEdge[] => getEdgesRef.value.map((edge) => ({ id: edge.id, source: edge.source, @@ -497,7 +500,7 @@ const getEdges = () => animated: edge.animated, style: edge.style, data: edge.data, - })); + })) as FlowEdge[]; const loadNodeTypes = async () => { loading.value = true; @@ -540,14 +543,14 @@ onInit((vueFlowInstance) => { if (res.data && res.data.data) { nodes.value = res.data.data.nodes || []; edges.value = res.data.data.edges || []; - saveToHistory(nodes.value, edges.value); + pushHistoryFromCanvas(); } } catch { ElMessage.error("流程加载失败"); } })(); } else { - saveToHistory(nodes.value, edges.value); + pushHistoryFromCanvas(); } }); @@ -557,7 +560,7 @@ onConnect((connection) => { type: edgeStyle.value, animated: edgeAnimated.value, }); - saveToHistory(nodes.value, edges.value); + pushHistoryFromCanvas(); }); function handleValidate() { @@ -571,8 +574,8 @@ function handleValidate() { errors.push("流程中没有节点"); } - const nodeIds = new Set(allNodesList.map((n: Node) => n.id)); - allEdgesList.forEach((edge: Edge) => { + const nodeIds = new Set(allNodesList.map((n: FlowNode) => n.id)); + allEdgesList.forEach((edge: FlowEdge) => { if (!nodeIds.has(edge.source)) { errors.push(`连线 ${edge.label || edge.id} 的源节点不存在`); } @@ -582,12 +585,13 @@ function handleValidate() { }); const orphanNodes = allNodesList.filter( - (node: Node) => !allEdgesList.some((e: Edge) => e.source === node.id || e.target === node.id) + (node: FlowNode) => + !allEdgesList.some((e: FlowEdge) => e.source === node.id || e.target === node.id) ); if (orphanNodes.length > 0) { warnings.push( - `有 ${orphanNodes.length} 个孤立节点: ${orphanNodes.map((n: Node) => n.data.label).join(", ")}` + `有 ${orphanNodes.length} 个孤立节点: ${orphanNodes.map((n: FlowNode) => n.data.label).join(", ")}` ); } @@ -662,9 +666,9 @@ function handleClosePanel() { function handleSaveNode(data: any) { if (!selectedNode.value) return; const nodeId = selectedNode.value!.id; - if (nodeId && updateNodeData(nodeId, data, getNodes, setNodes)) { - saveToHistory(nodes.value, edges.value); - } + if (!nodeId) return; + if (!updateNodeData(nodeId, data, getNodes, setNodes)) return; + pushHistoryFromCanvas(); } function handleDeleteNode() { @@ -682,7 +686,7 @@ function handleDeleteNode() { deleteNode(nodeId, getNodes, setNodes, getEdges, setEdges); ElMessage.success("节点删除成功"); handleClosePanel(); - saveToHistory(nodes.value, edges.value); + pushHistoryFromCanvas(); } catch { // 用户取消 } @@ -692,9 +696,9 @@ function handleDeleteNode() { function handleSaveEdge(data: any) { if (!selectedEdge.value) return; const edgeId = selectedEdge.value!.id; - if (edgeId && updateEdgeData(edgeId, data, getEdges, setEdges)) { - saveToHistory(nodes.value, edges.value); - } + if (!edgeId) return; + if (!updateEdgeData(edgeId, data, getEdges, setEdges)) return; + pushHistoryFromCanvas(); } function handleDeleteEdge() { @@ -712,7 +716,7 @@ function handleDeleteEdge() { deleteEdge(edgeId, getEdges, setEdges); ElMessage.success("连线删除成功"); handleClosePanel(); - saveToHistory(nodes.value, edges.value); + pushHistoryFromCanvas(); } catch { // 用户取消 } @@ -788,15 +792,19 @@ const handleClose = () => { }; // 历史记录管理 -const history = ref<{ nodes: Node[]; edges: Edge[] }[]>([]); +const history = ref<{ nodes: unknown[]; edges: unknown[] }[]>([]); const historyIndex = ref(-1); -function saveToHistory(nodesData: Node[], edgesData: Edge[]) { +function saveToHistory(nodesData: FlowNode[], edgesData: FlowEdge[]) { history.value = history.value.slice(0, historyIndex.value + 1); history.value.push({ nodes: nodesData, edges: edgesData }); historyIndex.value = history.value.length - 1; } +function pushHistoryFromCanvas() { + saveToHistory([...nodes.value] as FlowNode[], [...edges.value] as FlowEdge[]); +} + // 拖拽相关函数 function onDragStart(event: DragEvent, node: LoadedNodeType) { if (event.dataTransfer) { @@ -820,8 +828,8 @@ function handleNodeDrop( event: DragEvent, screenToFlowCoordinate: (position: { x: number; y: number }) => { x: number; y: number }, onNodesInitialized: (callback: () => void) => void, - updateNode: (id: string, node: Partial) => void, - addNodes: (nodes: Node[]) => void + updateNode: (id: string, node: Partial) => void, + addNodes: (nodes: FlowNode[]) => void ) { const data = event.dataTransfer?.getData("application/vueflow"); if (!data) return; @@ -829,7 +837,7 @@ function handleNodeDrop( const nodeType = JSON.parse(data); const position = screenToFlowCoordinate({ x: event.clientX, y: event.clientY }); - const newNode: Node = { + const newNode: FlowNode = { id: `node-${Date.now()}`, type: nodeType.type, position, @@ -849,8 +857,8 @@ function handleNodeDrop( function updateNodeData( nodeId: string, data: any, - getNodes: () => Node[], - setNodes: (nodes: Node[]) => void + getNodes: () => FlowNode[], + setNodes: (nodes: FlowNode[]) => void ) { const currentNodes = getNodes(); const nodeIndex = currentNodes.findIndex((n) => n.id === nodeId); @@ -870,10 +878,10 @@ function updateNodeData( function deleteNode( nodeId: string, - getNodes: () => Node[], - setNodes: (nodes: Node[]) => void, - getEdges: () => Edge[], - setEdges: (edges: Edge[]) => void + getNodes: () => FlowNode[], + setNodes: (nodes: FlowNode[]) => void, + getEdges: () => FlowEdge[], + setEdges: (edges: FlowEdge[]) => void ) { const currentNodes = getNodes(); const currentEdges = getEdges(); @@ -887,8 +895,8 @@ function deleteNode( function updateEdgeData( edgeId: string, data: any, - getEdges: () => Edge[], - setEdges: (edges: Edge[]) => void + getEdges: () => FlowEdge[], + setEdges: (edges: FlowEdge[]) => void ) { const currentEdges = getEdges(); const edgeIndex = currentEdges.findIndex((e) => e.id === edgeId); @@ -907,7 +915,7 @@ function updateEdgeData( return true; } -function deleteEdge(edgeId: string, getEdges: () => Edge[], setEdges: (edges: Edge[]) => void) { +function deleteEdge(edgeId: string, getEdges: () => FlowEdge[], setEdges: (edges: FlowEdge[]) => void) { const currentEdges = getEdges(); const filteredEdges = currentEdges.filter((e) => e.id !== edgeId); setEdges(filteredEdges); From 17702d3ea5bd24d49d20b6735d0fbccda9798e40 Mon Sep 17 00:00:00 2001 From: luomgf Date: Wed, 13 May 2026 22:03:53 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=BC=BA=E5=B0=91?= =?UTF-8?q?vue-img-cutter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/web/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/web/package.json b/frontend/web/package.json index 790e95b0f..d64e67fb3 100755 --- a/frontend/web/package.json +++ b/frontend/web/package.json @@ -109,6 +109,7 @@ "vue": "^3.5.21", "vue-draggable-plus": "^0.6.0", "vue-i18n": "^11.1.10", + "vue-img-cutter": "^3.1.1", "vue-json-pretty": "^2.5.0", "vue-router": "^4.5.1", "vue-web-terminal": "^3.4.1",