From 4e1cd284e93b70abf25f85138c59bad656887443 Mon Sep 17 00:00:00 2001 From: 2910410219 <2910410219@qq.com> Date: Mon, 10 Aug 2026 15:02:10 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=8A=82=E7=82=B9=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E7=82=B9=E5=B7=A6=E5=8F=B3=E6=A0=B7=E5=BC=8F=E5=92=8C?= =?UTF-8?q?=E8=89=B2=E5=BD=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../settings/workflow/component/FlowNode.vue | 114 ++++++++++++++++++ src/views/settings/workflow/index.vue | 30 +++-- 2 files changed, 136 insertions(+), 8 deletions(-) create mode 100644 src/views/settings/workflow/component/FlowNode.vue diff --git a/src/views/settings/workflow/component/FlowNode.vue b/src/views/settings/workflow/component/FlowNode.vue new file mode 100644 index 0000000..a6fbb23 --- /dev/null +++ b/src/views/settings/workflow/component/FlowNode.vue @@ -0,0 +1,114 @@ + + + + + diff --git a/src/views/settings/workflow/index.vue b/src/views/settings/workflow/index.vue index ff6aca2..3083050 100644 --- a/src/views/settings/workflow/index.vue +++ b/src/views/settings/workflow/index.vue @@ -43,12 +43,12 @@ :default-viewport="{ zoom: 1 }" :nodes-connectable="true" :edges-updatable="true" + :node-types="nodeTypes" @connect="onConnect" @node-click="onNodeClick" > - @@ -86,7 +86,6 @@ import { ref, computed, onMounted } from 'vue'; import { VueFlow, useVueFlow } from '@vue-flow/core'; import { Background } from '@vue-flow/background'; import { Controls } from '@vue-flow/controls'; -import { MiniMap } from '@vue-flow/minimap'; import { ElMessage, ElMessageBox } from 'element-plus'; import type { Node, Edge, Connection } from '@vue-flow/core'; import { @@ -104,6 +103,7 @@ import WorkflowListPanel from './component/WorkflowListPanel.vue'; import SaveWorkflowDialog from './component/SaveWorkflowDialog.vue'; import ModelSelector from './component/ModelSelector.vue'; import SkillSelector from './component/SkillSelector.vue'; +import FlowNode from './component/FlowNode.vue'; interface NodeData { label?: string; @@ -123,6 +123,9 @@ const { addNodes, addEdges, findNode, removeNodes, getNodes, updateNode } = useV const START_NODE_CODE = '__start__'; const JUDGE_KEYWORDS = ['判断', 'judge', 'condition', 'if', 'branch', 'gateway']; +// 自定义节点类型:默认节点内置 DefaultNode 只有上下两个 Handle,自定义组件支持左右连接 +const nodeTypes = { default: FlowNode, input: FlowNode }; + // 节点库相关状态 const nodeLibraryGroups = ref([]); const nodeLibraryCollapsed = ref(false); @@ -232,6 +235,8 @@ const onConnect = (connection: Connection) => { id: `edge-${connection.source}-${connection.target}`, source: connection.source, target: connection.target, + sourceHandle: connection.sourceHandle, + targetHandle: connection.targetHandle, type: 'smoothstep', animated: true, style: { stroke: '#3b82f6', strokeWidth: 2 }, @@ -474,7 +479,6 @@ const addNodeFromLibrary = (nodeCode: string, nodeName: string) => { type: 'default', position: { x: spawnX, y: spawnY }, data: { label: nodeName, nodeCode, desc: '', patchLayout: false }, - style: { background: '#fff', border: '2px solid #3b82f6', borderRadius: '8px', padding: '10px 20px' }, }, ]); ElMessage.success(`已添加${nodeName}`); @@ -502,7 +506,6 @@ const addDefaultStartNode = () => { type: 'input', position: { x: 200, y: 200 }, data: { label: '开始', nodeCode: '__start__' }, - style: { background: '#10b981', color: '#fff', border: '2px solid #059669', borderRadius: '8px', padding: '10px 20px' }, }; addNodes([startNode]); nodes.value.push(startNode); @@ -674,9 +677,6 @@ const loadWorkflowFromDsl = (dsl: any) => { isSaveFile: Boolean(n.isSaveFile), preTool: n.preTool ?? null, }, - style: isStart - ? { background: '#10b981', color: '#fff', border: '2px solid #059669', borderRadius: '8px', padding: '10px 20px' } - : { background: '#fff', border: '2px solid #3b82f6', borderRadius: '8px', padding: '10px 20px' }, }; }); @@ -684,6 +684,8 @@ const loadWorkflowFromDsl = (dsl: any) => { id: e.id, source: e.from, target: e.to, + sourceHandle: e.sourceHandle || 'source-bottom', + targetHandle: e.targetHandle || 'target-top', type: 'smoothstep', animated: true, style: { stroke: '#3b82f6', strokeWidth: 2 }, @@ -764,6 +766,8 @@ const confirmSaveWorkflow = async () => { id: e.id, from: e.source, to: e.target, + sourceHandle: e.sourceHandle || undefined, + targetHandle: e.targetHandle || undefined, })), }; @@ -932,7 +936,6 @@ onMounted(async () => { @import '@vue-flow/core/dist/style.css'; @import '@vue-flow/core/dist/theme-default.css'; @import '@vue-flow/controls/dist/style.css'; -@import '@vue-flow/minimap/dist/style.css'; /* 连接点样式优化 - 更大尺寸,hover 放大但不位移 */ .vue-flow__handle { @@ -976,6 +979,17 @@ onMounted(async () => { cursor: move; } +/* 覆盖 VueFlow 默认主题对节点外层的固定宽/padding/背景,让自定义 FlowNode 渐变背景完全贴合节点边缘 */ +.vue-flow__node-default, +.vue-flow__node-input, +.vue-flow__node-output { + width: auto !important; + padding: 0 !important; + background: transparent !important; + border: none !important; + border-radius: 0 !important; +} + .vue-flow__node:hover { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important; }