From 0c58ae4d3d1a8157f4e0be8a8ee8f67c71013f99 Mon Sep 17 00:00:00 2001 From: fengfeng Date: Wed, 11 Feb 2026 13:34:21 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E8=8A=82=E7=82=B9=E9=81=8D?= =?UTF-8?q?=E5=8E=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/CustomNode.tsx | 97 ++++++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 7 deletions(-) diff --git a/src/components/CustomNode.tsx b/src/components/CustomNode.tsx index 0b5ad3a..dd5812b 100644 --- a/src/components/CustomNode.tsx +++ b/src/components/CustomNode.tsx @@ -1,16 +1,96 @@ import { ReactNode, useContext } from 'react'; -import { Handle, Position, NodeProps } from '@xyflow/react'; -import { AppNode, SettingsContext } from '../types/graph'; +import { Handle, Position, NodeProps, useReactFlow} from '@xyflow/react'; +import { AppNode, AppEdge, SettingsContext, NodeData } from '../types/graph'; import './CustomNode.css'; import toast from 'react-hot-toast'; +class ConfigResult { + constructor( + public success: boolean, + public config?: string, + public error?: string + ) {} +} + +function generateConfig( + data : NodeData, + getEdges : () => AppEdge[], + getNode : (id: string) => AppNode | undefined) : ConfigResult { + + const getNearEdges = (node: AppNode) : AppEdge[] => { + return getEdges().filter(edge => edge.source === node.id || edge.target === node.id); + }; + + const getNextNode = (edge: AppEdge, node: AppNode) : AppNode | undefined => { + const nextNodeId = edge.source === node.id ? edge.target : edge.source; + return getNode(nextNodeId); + }; + + const node = getNode(data.id); + if(!node) { + return new ConfigResult(false, undefined, "节点未找到"); + } + + const belongsToEdge : Record = {[node.id]: node.id}; + + const queue : AppNode[] = []; + const nearEdges = getNearEdges(node); + nearEdges.forEach(edge => { + const nextNode = getNextNode(edge, node); + if(nextNode) { + belongsToEdge[nextNode.id] = edge.id; + queue.push(nextNode); + } + }); + + while(queue.length > 0) { + const currentNode = queue.shift()!; + const fromEdgeId = belongsToEdge[currentNode.id]; + if(!fromEdgeId) continue; + + getNearEdges(currentNode).forEach(edge => { + const nextNode = getNextNode(edge, currentNode); + if(nextNode && !belongsToEdge[nextNode.id]) { + belongsToEdge[nextNode.id] = fromEdgeId; + queue.push(nextNode); + } + }); + } + + const groupedByEdge: Record = {}; + for (const nodeId in belongsToEdge) { + const edgeId = belongsToEdge[nodeId]; + if(edgeId === nodeId) continue; // 跳过起始节点 + if(!edgeId) continue; + + if (!groupedByEdge[edgeId]) { + groupedByEdge[edgeId] = []; + } + + groupedByEdge[edgeId].push(nodeId); + } + + + +} + export default function CustomNode({ data, selected }: NodeProps): ReactNode { - const handleGenerate = (e: React.MouseEvent) => { - e.stopPropagation(); - toast.success('保存成功!'); + const { getNode, getEdges } = useReactFlow(); + + const handleGenerate = (node : NodeData) => { + const result = generateConfig(node, getEdges, getNode); + if(result.success && result.config) { + navigator.clipboard.writeText(result.config).then(() => { + toast.success("配置已复制到剪贴板"); + }).catch(() => { + toast.error("复制失败,请手动复制"); + }); + } else { + toast.error("配置生成失败:" + (result.error || "未知错误")); + } }; const settings = useContext(SettingsContext); @@ -38,13 +118,16 @@ export default function CustomNode({ {(!settings.ipv4Subnet && !settings.ipv6Subnet) && (
- 未设置任何子网,请在设置中设置 + 未设置任何子网
)}
- +
{[Position.Top, Position.Bottom, Position.Right, Position.Left].map((position) => (