Vue3 + @vue-flow/core,tree结构数据渲染,node的position设置(第二版)

本文介绍了如何在VueFlow中处理不同层级的节点数据结构,特别是针对第三层、第四层和第五层的节点位置调整。通过CustomNode.vue组件,使用自定义节点类型和计算逻辑来确保节点布局的合理性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 官网地址:  Vue Flow

第二版本 处理 末级节点不相同level的数据结构(如3层4层5层)位置问题

 CustomNode.vue

<script setup lang="ts">
import type { NodeProps } from '@vue-flow/core'
import { Position, Handle } from '@vue-flow/core'

const props = defineProps<NodeProps>()
</script>

<template>
  <div class="custom-node">
    <Handle type="target" :position="Position.Left" />
    <div>{{ props.data.name }}</div>
    <Handle type="source" :position="Position.Right" />
  </div>
</template>
<style lang="less" scoped>
.custom-node {
  width: 100px;
  height: 50px;
}
</style>

flow.vue

<script setup lang="ts">
import '@vue-flow/core/dist/style.css'
import '@vue-flow/core/dist/theme-default.css'
import '@vue-flow/controls/dist/style.css'
import { VueFlow, Position, useVueFlow } from '@vue-flow/core'
import { Background } from '@vue-flow/background'
import { Controls } from '@vue-flow/controls'
import { MiniMap } from '@vue-flow/minimap'
import CustomNode from './CustomNode.vue'

const { onNodeClick } = useVueFlow()
const treeData = [
  {
    id: '1',
    children: [
      { id: '2', children: [{ id: '4' }, { id: '5' }, { id: '9' }] },
      { id: '3', children: [{ id: '6' }, { id: '10', children: [{ id: '11' }, { id: '12' }, { id: '13' }] }] },
      { id: '7', children: [{ id: '8', children: [{ id: '14' }, { id: '15' }, { id: '16' }] }] }
    ]
  }
]
const buildNodes = (tree: any, parentNode: any, level: any, levels: any, mark: any) => {
  const nodes = [] as any
  const edges = [] as any
  tree.forEach((node: any) => {
    if (level == 2) { mark = mark + 1 }
    // levels 记录 递归过程中 每层级的node数量 初始{}
    // mark 记录 循环level2时的次数 避免各支路level数不一致,导致布局混乱的问题
    // 在level1只有一个的情况下,level2第一个迭代链路结束levels会存在已有的层级对应的个数
    // 用mark 区分非第一个链路,如果在后续链路中出现新的层级,就会给新的level加上一层的计数 并给父节点添加一个新增层级的标签 isNewLevel
    levels[level] = mark > 1 && !levels[level] ? (parentNode['isNewLevel'] = true, (levels[level - 1])) : ((levels[level] || 0) + 1)
    // 如果子集存在 递归 并合并递归函数返回结果
    if (node.children?.length) {
      const childs = buildNodes(node.children, node, level + 1, levels, mark)
      nodes.push(...childs.nodes)
      edges.push(...childs.edges)
    }
    // 计算在当前递归level时,最长(最多node)的level的node数量
    const top = Math.max(...Object.keys(levels).map(cur => {
      return levels[cur]
    }))
    // 当前节点是否有子节点
    const childLength = node?.children?.length || 0
    // const more = childLength ? (node.children[childLength - 1].position.y - node.children[0].position.y) / 2 : 0
    // console.log(node.id, top, '---', more, '---mark', mark)
    // 当前node数据处理 业务数据放置data中(customNode接收的node的props有过滤,放入data可带入)
    const temp: any = {
      id: node.id,
      data: { ...node, level },
      type: 'custom',
      // 手动指定位置 位置计算规则:
      // x:间隔(leveln-1) 
      // y: 递归计算当前level最大node数top,(递归线路上 leveln时的top < level1时的top),故而,从0向坐标轴反方向增加y -间隔(top-1)
      // position: { x: 350 * (level - 1), y: -200 * (top - 1) - more },
      // 第二版位置设置 根据子节点位置设置居中位置,无子节点,位置照旧设置
      position: { x: 350 * (level - 1), y: childLength ? (node.children[childLength - 1].position.y + node.children[0].position.y) / 2 : -200 * (top - 1) },
      label: String(node.id),
      sourcePosition: Position.Right,
      targetPosition: Position.Left
    }
    // 记录节点位置于原始数据链 便于父节点递归回去后取值 设置自己的position
    node.position = temp.position
    // 连线处理
    if (parentNode) {
      temp.parentId = parentNode.id
      const edge = {
        id: parentNode.id + '-' + temp.id,
        source: parentNode.id,
        target: temp.id,
        type: 'smoothstep',
        animated: true
      }
      edges.push(edge)
    }
    nodes.push(temp)

  })
  return {
    nodes,
    edges
  }
}
const mainData = reactive({
  nodes: [],
  edges: []
})
onNodeClick((event: any) => {
  console.log(event)
})
onMounted(() => {
  Object.assign(mainData, buildNodes(treeData, null, 1, {}, 0))
})
</script>

<template>
  <VueFlow :nodes="mainData.nodes" :edges="mainData.edges" fitViewOnInit :nodes-draggable="true">
    <Controls :show-interactive="false" />
    <Background />
    <template #node-custom="customNodeProps">
      <CustomNode v-bind="customNodeProps" />
    </template>
    <MiniMap pannable zoomable />
  </VueFlow>
</template>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值