完成节点遍历
This commit is contained in:
parent
cf66dfec5a
commit
0c58ae4d3d
@ -1,16 +1,96 @@
|
|||||||
import { ReactNode, useContext } from 'react';
|
import { ReactNode, useContext } from 'react';
|
||||||
import { Handle, Position, NodeProps } from '@xyflow/react';
|
import { Handle, Position, NodeProps, useReactFlow} from '@xyflow/react';
|
||||||
import { AppNode, SettingsContext } from '../types/graph';
|
import { AppNode, AppEdge, SettingsContext, NodeData } from '../types/graph';
|
||||||
import './CustomNode.css';
|
import './CustomNode.css';
|
||||||
import toast from 'react-hot-toast';
|
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<string, string | undefined> = {[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<string, string[] | undefined> = {};
|
||||||
|
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({
|
export default function CustomNode({
|
||||||
data,
|
data,
|
||||||
selected
|
selected
|
||||||
}: NodeProps<AppNode>): ReactNode {
|
}: NodeProps<AppNode>): ReactNode {
|
||||||
const handleGenerate = (e: React.MouseEvent) => {
|
const { getNode, getEdges } = useReactFlow<AppNode, AppEdge>();
|
||||||
e.stopPropagation();
|
|
||||||
toast.success('保存成功!');
|
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);
|
const settings = useContext(SettingsContext);
|
||||||
@ -38,13 +118,16 @@ export default function CustomNode({
|
|||||||
|
|
||||||
{(!settings.ipv4Subnet && !settings.ipv6Subnet) && (
|
{(!settings.ipv4Subnet && !settings.ipv6Subnet) && (
|
||||||
<div className="info-item">
|
<div className="info-item">
|
||||||
<span className="label">未设置任何子网,请在设置中设置</span>
|
<span className="label">未设置任何子网</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="node-actions">
|
<div className="node-actions">
|
||||||
<button className="gen-btn" onClick={handleGenerate} onDoubleClick={e => e.stopPropagation()}>生成配置并复制</button>
|
<button className="gen-btn" onClick={e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleGenerate(data);
|
||||||
|
}} onDoubleClick={e => e.stopPropagation()}>生成配置并复制</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{[Position.Top, Position.Bottom, Position.Right, Position.Left].map((position) => (
|
{[Position.Top, Position.Bottom, Position.Right, Position.Left].map((position) => (
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user