// import dagre from '@dagrejs/dagre' import dagre from 'dagre' import { Position, useVueFlow } from '@vue-flow/core' import { ref } from 'vue' /** * Composable to run the layout algorithm on the graph. * It uses the `dagre` library to calculate the layout of the nodes and edges. */ export function useLayout() { const { findNode } = useVueFlow() const graph = ref(new dagre.graphlib.Graph()) const previousDirection = ref('LR') function layout(nodes, edges, direction = 'LR') { // 验证和规范化方向参数 const validDirections = ['TB', 'BT', 'LR', 'RL'] const layoutDirection = validDirections.includes(direction) ? direction : 'LR' // we create a new graph instance, in case some nodes/edges were removed, otherwise dagre would act as if they were still there const dagreGraph = new dagre.graphlib.Graph() graph.value = dagreGraph dagreGraph.setDefaultEdgeLabel(() => ({})) // 根据方向判断是否为水平布局 const isHorizontal = layoutDirection === 'LR' || layoutDirection === 'RL' dagreGraph.setGraph({ rankdir: layoutDirection }) previousDirection.value = layoutDirection for (const node of nodes) { // if you need width+height of nodes for your layout, you can use the dimensions property of the internal node (`GraphNode` type) const graphNode = findNode(node.id) dagreGraph.setNode(node.id, { width: graphNode.dimensions.width || 150, height: graphNode.dimensions.height || 50 }) } for (const edge of edges) { dagreGraph.setEdge(edge.source, edge.target) } dagre.layout(dagreGraph) // set nodes with updated positions return nodes.map((node) => { const nodeWithPosition = dagreGraph.node(node.id) // 根据方向动态计算连接点位置 let targetPosition, sourcePosition switch (layoutDirection) { case 'BT': // 从上到下 (Top to Bottom) targetPosition = Position.Bottom // 目标节点连接点在下方 sourcePosition = Position.Top // 源节点连接点在上方 break case 'TB': // 从下到上 (Bottom to Top) targetPosition = Position.Top // 目标节点连接点在上方 sourcePosition = Position.Bottom // 源节点连接点在下方 break case 'LR': // 从左到右 (Left to Right) targetPosition = Position.Left sourcePosition = Position.Right break case 'RL': // 从右到左 (Right to Left) targetPosition = Position.Right sourcePosition = Position.Left break default: targetPosition = Position.Top sourcePosition = Position.Bottom } return { ...node, targetPosition, sourcePosition, position: { x: nodeWithPosition.x, y: nodeWithPosition.y }, } }) } return { graph, layout, previousDirection } }