Jelajahi Sumber

fix: 修复拖拽节点位置偏差,节点库调整

jiaxing.liao 1 bulan lalu
induk
melakukan
2af0855b2c

+ 24 - 3
apps/web/src/features/nodeLibary/index.vue

@@ -24,17 +24,39 @@ interface NodeGroup {
 	nodes: NodeItem[]
 }
 
+const props = defineProps<{
+	hideStart?: boolean
+	parentNodeType?: string
+	ignoreDrag?: boolean
+}>()
+
 const emit = defineEmits<{
 	'add-node': [value: { type: string }]
 }>()
 
 const { onDragStart } = useDragAndDrop()
 
+const canAddNodeType = (nodeType: string) => {
+	const isLoopContainer = ['loop', 'iteration'].includes(props.parentNodeType || '')
+
+	if (nodeType === 'loop-end') {
+		return isLoopContainer
+	}
+
+	if (isLoopContainer && ['loop', 'iteration'].includes(nodeType)) {
+		return false
+	}
+
+	return true
+}
+
 const materials = computed<NodeGroup[]>(() => {
 	const groupMap = new Map<string, NodeGroup>()
 
 	nodes
-		.filter((node) => !node.hideInLibary) // 过滤掉 hideInLibary 的节点
+		.filter((node) => !node.hideInLibary)
+		.filter((node) => node.group !== '开始' || !props.hideStart)
+		.filter((node) => canAddNodeType(node.schema.nodeType))
 		.forEach((node) => {
 			const groupName = node.group || '其他'
 
@@ -59,7 +81,6 @@ const materials = computed<NodeGroup[]>(() => {
 			})
 		})
 
-	// 保持分组顺序稳定
 	return Array.from(groupMap.values())
 })
 
@@ -90,7 +111,7 @@ const onAddNode = (value: NodeItem) => {
 						class="tool mb-3 flex items-center"
 						v-for="item in group.nodes"
 						:key="item.id"
-						:draggable="true"
+						:draggable="!props.ignoreDrag"
 						@click="onAddNode(item)"
 						@dragstart="onDragStart($event, item.type)"
 					>

+ 1 - 1
apps/web/src/nodes/src/start/setter.vue

@@ -35,7 +35,7 @@
 							<div class="variable-subtitle">
 								<span>{{ getFormTypeLabel(variable.formType) }}</span>
 								<span v-if="supportsMaxLength(variable.formType)">
-									最大长度 {{ variable.max_length || 0 }}
+									最大长度:{{ variable.max_length ?? '无' }}
 								</span>
 								<span v-if="variable.is_hide">已隐藏</span>
 							</div>

+ 46 - 8
apps/web/src/views/editor/NodeView.vue

@@ -56,7 +56,13 @@
 		width="360px"
 		virtual-triggering
 	>
-		<NodeLibary @add-node="handleNodeCreate" @mouseleave="onHideNodeLibary" />
+		<NodeLibary
+			@add-node="handleNodeCreate"
+			@mouseleave="onHideNodeLibary"
+			:parent-node-type="nodeLibaryParentType"
+			hide-start
+			ignore-drag
+		/>
 	</el-popover>
 </template>
 
@@ -140,17 +146,17 @@ const removeNodeLibaryPopoverAnchor = () => {
 	nodeLibaryPopoverAnchorRef.value = undefined
 }
 
-const createNodeLibaryPopoverAnchor = (event?: MouseEvent) => {
+const createNodeLibaryPopoverAnchor = (position?: { x: number; y: number }) => {
 	removeNodeLibaryPopoverAnchor()
 
-	if (!event) {
+	if (!position) {
 		return undefined
 	}
 
 	const anchor = document.createElement('div')
 	anchor.style.position = 'fixed'
-	anchor.style.left = `${event.clientX}px`
-	anchor.style.top = `${event.clientY}px`
+	anchor.style.left = `${position.x}px`
+	anchor.style.top = `${position.y}px`
 	anchor.style.width = '1px'
 	anchor.style.height = '1px'
 	anchor.style.pointerEvents = 'none'
@@ -269,6 +275,17 @@ const workflowWithExecutionState = computed(() => {
 	} as IWorkflow
 })
 
+const getNodeTypeById = (id?: string) => {
+	if (!id) {
+		return ''
+	}
+
+	const node = props.workflow?.nodes?.find((item) => item.id === id)
+	return (node as any)?.nodeType || (node as any)?.data?.nodeType || ''
+}
+
+const nodeLibaryParentType = computed(() => getNodeTypeById(peddingHandlePayload.value?.parentId))
+
 const toolbarRunNodes = computed(() => {
 	return (props.workflow?.nodes || [])
 		.filter((node) => {
@@ -540,15 +557,30 @@ const handleNodeCreate = (value: { type: string; position?: XYPosition } | strin
 
 	const nodeToAdd = nodeMap[value.type]?.schema
 	const viewport = workflowRef.value?.getVueFlow()?.viewport
+	const parentNodeType = getNodeTypeById(peddingHandlePayload.value?.parentId)
+	const isLoopContainer = ['loop', 'iteration'].includes(parentNodeType)
+
+	if (value.type === 'loop-end' && !isLoopContainer) {
+		ElMessage.warning('退出循环节点只能在循环或迭代节点内部添加')
+		onHideNodeLibary()
+		return
+	}
+
+	if (isLoopContainer && ['loop', 'iteration'].includes(value.type)) {
+		ElMessage.warning('循环或迭代节点内部不允许再添加循环或迭代节点')
+		onHideNodeLibary()
+		return
+	}
 
 	if (nodeToAdd) {
+		const hasExplicitPosition = !!value.position
 		const newNodeParam: any = {
 			...nodeToAdd,
 			appAgentId: props.workflow?.id || '',
 			position: value.position
 		}
 
-		if (newNodeParam.position && viewport) {
+		if (!hasExplicitPosition && !peddingHandlePayload.value && viewport) {
 			newNodeParam.position = {
 				x: (-viewport.value.x + window.innerWidth / 2) / viewport.value.zoom,
 				y: (-viewport.value.y + window.innerHeight / 2) / viewport.value.zoom
@@ -800,7 +832,10 @@ const onConnectionOpenNodeLibary = async (payload: {
 	parentId?: string
 }) => {
 	await nextTick()
-	libaryRefferenceRef.value = createNodeLibaryPopoverAnchor(payload.event)
+	libaryRefferenceRef.value = createNodeLibaryPopoverAnchor({
+		x: payload.event.clientX,
+		y: payload.event.clientY
+	})
 	showNodeLibary.value = true
 	peddingHandlePayload.value = payload
 }
@@ -828,7 +863,10 @@ const handleClickConectionAdd = (connection: Connection & { id: string }, parent
 			y: rect.top
 		})
 
-		libaryRefferenceRef.value = el
+		libaryRefferenceRef.value = createNodeLibaryPopoverAnchor({
+			x: rect.left,
+			y: rect.top
+		})
 		showNodeLibary.value = true
 		peddingHandlePayload.value = {
 			by: 'edge',