在使用 vueflow 包和各种示例(drag-n-drop 和 自定义节点)时,我很难连接自定义节点。一些使用的组件来自Vuetify。
StartNode 按预期呈现。将 InputNode 从侧面拖到 VueFlow 中也可以按预期工作。但随后问题就开始了。有多个问题,但也许一个可以回答另一个问题,如果没有,我会问另一个问题。从 StartNode 拖动到 InputNode 时出现以下错误:
错误:
属性 d:预期数量<path>
从 InputNode 拖动到 StartNode 时,不会给出错误,但会渲染一个新的“默认节点”。
我希望有人能为我指出如何连接自定义节点的正确方向。
主文件
<template>
<v-row>
<v-col cols="9" style="height: 500px" @drop="onDrop">
<VueFlow
v-model:nodes="nodes"
v-model:edges="edges"
:node-types="types"
:connection-mode="ConnectionMode.Strict"
@dragover="onDragOver"
@dragleave="onDragLeave"
/>
</v-col>
<v-col cols="3">
<InputNode
:draggable="true"
@dragstart="onDragStart( $event, 'input' )"
/>
</v-col>
</v-row>
</template>
<script setup>
import { ref, markRaw } from 'vue';
import { VueFlow, useVueFlow, ConnectionMode } from '@vue-flow/core';
import useDragAndDrop from './drag-n-drop.js';
import StartNode from './nodes/StartNode.vue';
import InputNode from './nodes/InputNode.vue';
const { onConnect, addEdges } = useVueFlow();
const { onDragOver, onDrop, onDragLeave, onDragStart } = useDragAndDrop()
const nodes = ref( [
{
id: 'start-node',
type: 'start',
position: { x: 0, y: 50 },
dimensions: { width: '150px', height: '50px' },
},
] );
const edges = ref( [] );
const types = {
start: markRaw( StartNode ),
input: markRaw( InputNode ),
};
onConnect( addEdges );
</script>
<style>
@import '@vue-flow/core/dist/style.css';
@import '@vue-flow/core/dist/theme-default.css';
</style>
起始节点:
<template>
<div>
<v-card color="green" :width="props.dimensions?.width ?? '150px'" :height="props.dimensions?.height ?? '50px'">
<v-card-text>Start</v-card-text>
</v-card>
<Handle id="start" type="source" :position="Position.Right" :connectable="handleConnectable" />
</div>
</template>
<script setup>
import { Position, Handle } from '@vue-flow/core';
const props = defineProps( {
id: {
type: String,
required: true,
},
data: {
type: Object,
required: true,
},
dimensions: {
type: Object,
required: false,
},
} );
function handleConnectable( node, connectedEdges )
{
return connectedEdges.length <= 1;
}
</script>
输入节点:
<template>
<div
>
<Handle v-if="data" id="input-target" type="target" :position="Position.Right" :connectable="handleConnectable" />
<v-card
:draggable="props.draggable"
:width="'300px'"
:height="'104px'"
>
<v-card-title>Input</v-card-title>
<v-card-text>
<v-text-field
label="Input"
outlined
hide-details
density="compact"
/>
</v-card-text>
</v-card>
<Handle v-if="data" id="input-source" type="source" :position="Position.Left" :connectable="handleConnectable" />
</div>
</template>
<script setup>
import { Handle, Position } from '@vue-flow/core';
const props = defineProps( {
id: {
type: String,
required: false,
},
data: {
type: Object,
required: false,
},
dimensions: {
type: Object,
required: false,
},
draggable: {
type: Boolean,
required: false,
default: true,
},
} );
function handleConnectable( node, connectedEdges )
{
return connectedEdges.length <= 1;
}
</script>
drag-n-drop.js(取自示例):
import { useVueFlow } from '@vue-flow/core'
import { ref, watch } from 'vue'
let id = 0
/**
* @returns {string} - A unique id.
*/
function getId() {
return `dndnode_${id++}`
}
/**
* In a real world scenario you'd want to avoid creating refs in a global scope like this as they might not be cleaned up properly.
* @type {{draggedType: Ref<string|null>, isDragOver: Ref<boolean>, isDragging: Ref<boolean>}}
*/
const state = {
/**
* The type of the node being dragged.
*/
draggedType: ref(null),
isDragOver: ref(false),
isDragging: ref(false),
}
export default function useDragAndDrop() {
const { draggedType, isDragOver, isDragging } = state
const { addNodes, screenToFlowCoordinate, onNodesInitialized, updateNode } = useVueFlow()
watch(isDragging, (dragging) => {
document.body.style.userSelect = dragging ? 'none' : ''
})
function onDragStart(event, type) {
if (event.dataTransfer) {
event.dataTransfer.setData('application/vueflow', type)
event.dataTransfer.effectAllowed = 'move'
}
draggedType.value = type
isDragging.value = true
document.addEventListener('drop', onDragEnd)
}
/**
* Handles the drag over event.
*
* @param {DragEvent} event
*/
function onDragOver(event) {
event.preventDefault()
if (draggedType.value) {
isDragOver.value = true
if (event.dataTransfer) {
event.dataTransfer.dropEffect = 'move'
}
}
}
function onDragLeave() {
isDragOver.value = false
}
function onDragEnd() {
isDragging.value = false
isDragOver.value = false
draggedType.value = null
document.removeEventListener('drop', onDragEnd)
}
/**
* Handles the drop event.
*
* @param {DragEvent} event
*/
function onDrop(event) {
const position = screenToFlowCoordinate({
x: event.clientX,
y: event.clientY,
})
const nodeId = getId()
const newNode = {
id: nodeId,
type: draggedType.value,
position,
data: { label: nodeId },
}
/**
* Align node position after drop, so it's centered to the mouse
*
* We can hook into events even in a callback, and we can remove the event listener after it's been called.
*/
const { off } = onNodesInitialized(() => {
updateNode(nodeId, (node) => ({
position: { x: node.position.x - node.dimensions.width / 2, y: node.position.y - node.dimensions.height / 2 },
}))
off()
})
addNodes(newNode)
}
return {
draggedType,
isDragOver,
isDragging,
onDragStart,
onDragLeave,
onDragOver,
onDrop,
}
}
有几个问题。第一个问题是句柄源和目标位置交换了。
StartNode
中的第二期是动态width
和height
。
第三个问题是在
InputNode
上,:draggable="props.draggable"
上声明的v-card
导致了添加默认节点的问题。