<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.Strict">
|
<template #edge-custom="customEdgeProps">
|
<CustomEdge v-bind="customEdgeProps" />
|
</template>
|
|
<template #node-agent="agentNodeProps">
|
<AgentNode v-bind="agentNodeProps" :agentNames="agentNames" />
|
</template>
|
<template #node-func="funcNodeProps">
|
<FuncNode v-bind="funcNodeProps" :funcNames="funcNames" />
|
</template>
|
<Controls />
|
<Background />
|
<MiniMap pannable zoomable />
|
</VueFlow>
|
</div>
|
</template>
|
|
<script setup lang="ts">
|
import { Background } from '@vue-flow/background';
|
import { Controls } from '@vue-flow/controls';
|
import { MiniMap } from '@vue-flow/minimap';
|
|
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 LLMNode from './ui/nodes/LLMNode.vue';
|
import OutputNode from './ui/nodes/OutputNode.vue';
|
import AgentNode from './ui/nodes/AgentNode.vue';
|
import FuncNode from './ui/nodes/FuncNode.vue';
|
|
import CustomEdge from './ui/edges/CustomEdge.vue';
|
import { onMounted } from 'vue';
|
|
const props = defineProps(['flowJson', 'agentNames','funcNames']);
|
|
const nodeTypes = {
|
start: markRaw(StartNode),
|
end: markRaw(EndNode),
|
condition: markRaw(ConditionNode),
|
LLM: markRaw(LLMNode),
|
// output:markRaw(OutputNode),
|
// agent: markRaw(AgentNode),
|
[NodeType.Output]: markRaw(OutputNode),
|
// 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, fitView } = 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: [],
|
}
|
);
|
|
onConnect((params) => {
|
addEdges({
|
id: VueFlowHelper.genGeometryId(),
|
|
source: params.source,
|
target: params.target,
|
sourceHandle: params.sourceHandle,
|
targetHandle: params.targetHandle,
|
type: 'custom',
|
markerEnd: {
|
type: MarkerType.ArrowClosed,
|
width: 40,
|
height: 70,
|
},
|
});
|
|
// addEdges());
|
});
|
|
onMounted(() => {
|
setTimeout(() => {
|
fitView();
|
}, 30);
|
});
|
|
function handleOnDrop(event: DragEvent) {
|
const type = event.dataTransfer?.getData('application/vueflow') as NodeType;
|
if (type === 'workflow') {
|
const { nodes, edges, position, zoom } = Test_data;
|
const [x = 0, y = 0] = position;
|
setNodes(nodes);
|
setEdges(edges);
|
setViewport({ x, y, zoom: zoom || 0 });
|
return;
|
}
|
|
const { left, top } = vueFlowRef.value!.getBoundingClientRect();
|
|
const position = project({
|
x: event.clientX - left,
|
y: event.clientY - top,
|
});
|
|
const newNode = {
|
id: VueFlowHelper.genGeometryId(),
|
type,
|
position,
|
label: nodeTypeMap[type],
|
data: VueFlowHelper.getDefaultData(type),
|
};
|
|
addNodes([newNode]);
|
|
nextTick(() => {
|
const node = findNode(newNode.id);
|
const stop = watch(
|
() => node!.dimensions,
|
(dimensions: Dimensions) => {
|
if (dimensions.width > 0 && dimensions.height > 0 && node) {
|
node.position = {
|
x: node.position.x - node.dimensions.width / 2,
|
y: node.position.y - node.dimensions.height / 2,
|
};
|
stop();
|
}
|
},
|
{ deep: true, flush: 'post' }
|
);
|
});
|
}
|
function handleOnDragOver(event: DragEvent) {
|
event.preventDefault();
|
|
if (event.dataTransfer) {
|
event.dataTransfer.dropEffect = 'move';
|
}
|
}
|
</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;
|
.vue-flow__handle {
|
width: var(--handle-size);
|
height: var(--handle-size);
|
}
|
}
|
</style>
|
|
<style lang="scss" scoped></style>
|