From f78e1bd0772608c7cd59264ab94f87ef94012c0b Mon Sep 17 00:00:00 2001 From: wujingjing <gersonwu@qq.com> Date: 星期五, 21 三月 2025 09:56:15 +0800 Subject: [PATCH] 工作流 --- src/components/vue-flow/MainCanvas.vue | 268 +++++++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 218 insertions(+), 50 deletions(-) diff --git a/src/components/vue-flow/MainCanvas.vue b/src/components/vue-flow/MainCanvas.vue index ad45f8d..8ee2b13 100644 --- a/src/components/vue-flow/MainCanvas.vue +++ b/src/components/vue-flow/MainCanvas.vue @@ -1,11 +1,101 @@ <template> <div class="relative h-full w-full" id="main-canvas" @drop="handleOnDrop" @dragover="handleOnDragOver"> - <VueFlow v-model="elements" :node-types="nodeTypes" :connection-mode="ConnectionMode.Loose"> - <template #edge-custom="customEdgeProps"> - <CustomEdge v-bind="customEdgeProps" /> + <VueFlow v-model="elements" :node-types="nodeTypes" :connection-mode="ConnectionMode.Strict"> + <template #node-start="startNodeProps"> + <StartNode + ref="nodeRef" + v-bind="startNodeProps" + :isViewMode="isViewMode" + @register="(ref) => registerNodeRef(startNodeProps.id, ref)" + /> </template> - <Controls /> + <template #node-condition="conditionNodeProps"> + <ConditionNode + ref="nodeRef" + v-bind="conditionNodeProps" + :isViewMode="isViewMode" + @register="(ref) => registerNodeRef(conditionNodeProps.id, ref)" + /> + </template> + <template #node-output_msg="outputNodeProps"> + <OutputNode + ref="nodeRef" + v-bind="outputNodeProps" + :isViewMode="isViewMode" + @register="(ref) => registerNodeRef(outputNodeProps.id, ref)" + /> + </template> + + <template #node-end="endNodeProps"> + <EndNode ref="nodeRef" v-bind="endNodeProps" :isViewMode="isViewMode" /> + </template> + + <template #edge-custom="customEdgeProps"> + <CustomEdge v-bind="customEdgeProps" :isViewMode="isViewMode" /> + </template> + + <template #node-agent="agentNodeProps"> + <AgentNode + ref="nodeRef" + v-bind="agentNodeProps" + :agentNames="agentNames" + :isViewMode="isViewMode" + @register="(ref) => registerNodeRef(agentNodeProps.id, ref)" + /> + </template> + <template #node-func="funcNodeProps"> + <FuncNode ref="nodeRef" v-bind="funcNodeProps" :funcNames="funcNames" :isViewMode="isViewMode" /> + </template> + + <template #node-code="codeNodeProps"> + <CodeNode + ref="nodeRef" + v-bind="codeNodeProps" + :isViewMode="isViewMode" + @register="(ref) => registerNodeRef(codeNodeProps.id, ref)" + /> + </template> + <template #node-python_code="pythonCodeNodeProps"> + <PythonCodeNode + ref="nodeRef" + v-bind="pythonCodeNodeProps" + :isViewMode="isViewMode" + @register="(ref) => registerNodeRef(pythonCodeNodeProps.id, ref)" + /> + </template> + <template #node-n8n="n8nNodeProps"> + <N8nNode + :workflowList="n8nWorkflowList" + ref="nodeRef" + v-bind="n8nNodeProps" + :isViewMode="isViewMode" + @register="(ref) => registerNodeRef(n8nNodeProps.id, ref)" + /> + </template> + <template #node-text_resource="textResourceNodeProps"> + <TextResourceNode + ref="nodeRef" + v-bind="textResourceNodeProps" + :isViewMode="isViewMode" + @register="(ref) => registerNodeRef(textResourceNodeProps.id, ref)" + /> + </template> + + <template #node-analysis="analysisNodeProps"> + <AnalysisNode ref="nodeRef" v-bind="analysisNodeProps" :llmInfoList="llmInfoList" :isViewMode="isViewMode" /> + </template> + + <template #node-LLM="llmNodeProps"> + <LLMNode + v-bind="llmNodeProps" + :llmInfoList="llmInfoList" + :isViewMode="isViewMode" + @register="(ref) => registerNodeRef(llmNodeProps.id, ref)" + /> + </template> + <Controls :showInteractive="false" /> <Background /> + <MiniMap pannable zoomable /> </VueFlow> </div> </template> @@ -13,53 +103,98 @@ <script setup lang="ts"> import { Background } from '@vue-flow/background'; import { Controls } from '@vue-flow/controls'; -import { Dimensions, Elements, useVueFlow, VueFlow, ConnectionMode, MarkerType } from '@vue-flow/core'; -import { markRaw, nextTick, ref, watch } from 'vue'; -import { Test_data } from './testData'; -import StartNode from './ui/nodes/StartNode.vue'; -import EndNode from './ui/nodes/EndNode.vue'; -import { VueFlowHelper } from './VueFlowHelper'; -import { NodeType, nodeTypeMap } from './vueFlowEnum'; -import ConditionNode from './ui/nodes/ConditionNode.vue'; -import CustomEdge from './ui/edges/CustomEdge.vue'; -const nodeTypes = { - start: markRaw(StartNode), - end: markRaw(EndNode), - condition: markRaw(ConditionNode), - // LLM: markRaw(LLMNode), - // code: markRaw(CodeNode), - // knowledge: markRaw(KnowledgeNode), - // api: markRaw(ApiNode), -}; -const elements = ref<Elements>(); -const { findNode, nodes, addNodes, addEdges, project, vueFlowRef, onConnect, setNodes, setEdges, setViewport } = useVueFlow({ - nodes: [ - { - id: '1', - type: 'start', - data: VueFlowHelper.getDefaultData(NodeType.Start), +import { MiniMap } from '@vue-flow/minimap'; - position: { x: 25, y: 400 }, - }, - { - id: '2', - type: 'end', - data: VueFlowHelper.getDefaultData(NodeType.End), - position: { x: 1000, y: 400 }, - }, - ], - edges: [ - // { - // id: 'test', - // source: '1', - // target: '2', - // }, - ], -}); +import type { Dimensions, Elements, FlowProps } from '@vue-flow/core'; +import { ConnectionMode, MarkerType, useVueFlow, VueFlow } from '@vue-flow/core'; +import { nextTick, onMounted, ref, watch } from 'vue'; +import { Test_data } from './testData'; +import CustomEdge from './ui/edges/CustomEdge.vue'; +import AgentNode from './ui/nodes/AgentNode.vue'; +import AnalysisNode from './ui/nodes/AnalysisNode.vue'; +import CodeNode from './ui/nodes/CodeNode.vue'; +import ConditionNode from './ui/nodes/ConditionNode.vue'; +import EndNode from './ui/nodes/EndNode.vue'; +import FuncNode from './ui/nodes/FuncNode.vue'; +import LLMNode from './ui/nodes/LLMNode.vue'; +import OutputNode from './ui/nodes/OutputNode.vue'; +import PythonCodeNode from './ui/nodes/PythonCodeNode.vue'; +import StartNode from './ui/nodes/StartNode.vue'; +import TextResourceNode from './ui/nodes/TextResourceNode.vue'; +import { NodeType, nodeTypeMap } from './vueFlowEnum'; +import { VueFlowHelper } from './VueFlowHelper'; +import N8nNode from './ui/nodes/N8nNode.vue'; +import { GetN8nWorkflowList } from '/@/api/n8n'; +const props = defineProps(['flowJson', 'agentNames', 'funcNames', 'llmInfoList', 'isViewMode']); + +const n8nWorkflowList = ref([]); +const getN8nWorkflowList = async () => { + const res = await GetN8nWorkflowList({ + active: true, + }); + n8nWorkflowList.value = res?.data ?? []; +}; + +const nodeTypes = {}; +const elements = ref<Elements>(); +const { + findNode, + nodes, + addNodes, + addEdges, + project, + vueFlowRef, + onConnect, + setNodes, + setEdges, + setViewport, + fitView, + setInteractive, +} = useVueFlow( + Object.keys(props.flowJson).length > 0 + ? props.flowJson + : ({ + nodes: [ + { + id: '1', + type: 'start', + data: VueFlowHelper.getDefaultData(NodeType.Start), + + position: { x: 25, y: 400 }, + }, + ], + edges: [], + } as FlowProps) +); onConnect((params) => { + addEdges({ + id: VueFlowHelper.genGeometryId(), - addEdges(params); + source: params.source, + target: params.target, + sourceHandle: params.sourceHandle, + targetHandle: params.targetHandle, + type: 'custom', + markerEnd: { + type: MarkerType.ArrowClosed, + width: 40, + height: 70, + }, + }); + + // addEdges()); +}); +const initFlowStatus = () => { + fitView(); + + setInteractive(!props.isViewMode); +}; +onMounted(async () => { + getN8nWorkflowList(); + setTimeout(() => { + initFlowStatus(); + }, 30); }); function handleOnDrop(event: DragEvent) { @@ -114,18 +249,51 @@ event.dataTransfer.dropEffect = 'move'; } } + +const nodeRefs = ref(new Map()); + +const registerNodeRef = (nodeId: string, ref: any) => { + nodeRefs.value.set(nodeId, ref); +}; + +const validateForm = async () => { + const validPromises = []; + for (const nodeRef of nodeRefs.value.values()) { + if (nodeRef?.validateForm) { + validPromises.push(nodeRef.validateForm()); + } + } + + const results = await Promise.all(validPromises); + return results; +}; + +defineExpose({ + validateForm, +}); </script> <style lang="scss"> @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'; +@import '@vue-flow/node-resizer/dist/style.css'; #main-canvas { --vf-handle: #2563eb; - + --handle-size: 13px; + --resize-handle-size: 8px; .vue-flow__handle { - width: 18px; - height: 18px; + width: var(--handle-size); + height: var(--handle-size); } + + // .vue-flow__resize-control.handle { + // width: var(--resize-handle-size); + // height: var(--resize-handle-size); + // // visibility: hidden; + // } } </style> + +<style lang="scss" scoped></style> -- Gitblit v1.9.3