AntvG6 实现多对一 一对多树图布局

406 阅读3分钟

AntvG6 实现多对一 一对多树图布局,一般的树图都是一对多,利用Dagre布局实现。点击一个节点,根据节点查询上下级节点,如果有,就渲染,没有就提示。

image.png

// blood/index.vue
<div v-if="!isTreeShow" class="p-4">
  <blood-branch :clickNode @backClick="isTreeShow = true" />
</div>

<!--
 * @Date: 2024-04-16 19:09:30
 * @LastEditors: mingongze
 * @LastEditTime: 2024-04-16 19:17:18
 * @FilePath: /datagov_web/src/components/views/metadata/blood/bloodBranch.vue
-->
<script setup lang="ts">
import { fetchBloodTableLevel } from '@/api/metaData'
import { ref, onUnmounted, reactive, watchEffect, nextTick } from 'vue'
import G6 from '@antv/g6'

const props = defineProps({
  clickNode: {
    type: Object,
    default: null,
  },
})

const emits = defineEmits(['backClick'])
const { proxy } = getCurrentInstance()
const chartData = {
  downCount: 1,
  upCount: 1,
  upList: [null],
  downList: [
    {
      tableId: 35,
      dataBaseName: 'hf_test1',
      tableName: 'testmysql',
      creator: 'ys_hf',
      createTime: '2024-04-26 15:24:01',
      children: null,
    },
  ],
  tableId: 34,
  dataBaseName: 'hf_test1',
  tableName: 'stg_test_testmysql',
}

let chartData1 = {
  downCount: 0, // 直接下游层数
  upCount: 1, // 直接上游层数
  upList: [
    // -- 上游列表
    {
      tableId: 18,
      dataBaseName: 'test',
      tableName: '上游表1',
      creator: 'ssitg',
      createTime: '2024-03-25 11:28:32',
      children: null,
    },
    {
      tableId: 19,
      dataBaseName: 'test',
      tableName: '上游表2',
      creator: 'ssitg',
      createTime: '2024-03-25 11:28:32',
      children: null,
    },
    {
      tableId: 20,
      dataBaseName: 'test',
      tableName: '上游表3',
      creator: 'ssitg',
      createTime: '2024-03-25 11:28:32',
      children: null,
    },
  ],
  downList: [
    {
      tableId: 11,
      dataBaseName: 'test',
      tableName: '下游表1',
      creator: 'ssitg',
      createTime: '2024-03-25 11:28:32',
      children: null,
    },
  ], // --下游列表
  tableName: 'aaaaa',
  tableId: 1,
  dataBaseName: 'test',
  creator: 'ssitg',
  createTime: '2024-03-25 11:28:32',
  children: null,
}

type Node = {
  id: string
  label: string
}
type Edge = {
  source: string
  target: string
}

let dagreData = reactive({
  nodes: [] as Node[],
  edges: [] as Edge[],
})

const getNodeData = chartData => {
  let arr = []
  arr.push({
    id: String(chartData.tableId),
    label: chartData.tableName,
  })
  const setNode = data => {
    data.forEach(d => {
      arr.push({
        id: String(d.tableId),
        label: d.tableName,
      })
      if (d.children?.length) {
        setNode(d.children)
      }
    })
  }
  if (chartData.upList?.length) {
    setNode(chartData.upList)
  }
  if (chartData.downList?.length) {
    setNode(chartData.downList)
  }
  console.log('aaaa-', arr)
  dagreData.nodes = arr
  let edges = [] as Edge[]
  if (chartData.upList?.length) {
    chartData.upList.forEach(up => {
      edges.push({
        source: String(up.tableId),
        target: String(chartData.tableId),
      })
    })
  }
  if (chartData.downList?.length) {
    chartData.downList.forEach(d => {
      edges.push({
        source: String(chartData.tableId),
        target: String(d.tableId),
      })
    })
  }
  dagreData.edges = edges
}

let chartObj = reactive({
  upCount: 0,
  downCount: 0,
})

// getNodeData(chartData)

const data = {
  nodes: [
    {
      id: '0',
      label: '0',
    },
    {
      id: '1',
      label: '1',
    },
    {
      id: '2',
      label: '2',
    },
    {
      id: '3',
      label: '3',
    },
    {
      id: '4',
      label: '4',
    },
    {
      id: '5',
      label: '5',
    },
    {
      id: '6',
      label: '6',
    },
    {
      id: '7',
      label: '7',
    },
    {
      id: '8',
      label: '8',
    },
    {
      id: '9',
      label: '9',
    },
  ],
  edges: [
    {
      source: '0',
      target: '1',
    },
    {
      source: '0',
      target: '2',
    },
    {
      source: '1',
      target: '4',
    },
    {
      source: '0',
      target: '3',
    },
    {
      source: '3',
      target: '4',
    },
    {
      source: '4',
      target: '5',
    },
    {
      source: '4',
      target: '6',
    },
    {
      source: '5',
      target: '7',
    },
    {
      source: '5',
      target: '8',
    },
    {
      source: '8',
      target: '9',
    },
    {
      source: '2',
      target: '9',
    },
    {
      source: '3',
      target: '9',
    },
  ],
}

let graph = null
const renderDagre = () => {
  const container = document.getElementById('container')
  const width = container.scrollWidth || 1024
  const height = container.scrollHeight || 500
  graph = new G6.Graph({
    container: 'container',
    width,
    height,
    fitView: true,
    modes: {
      default: ['drag-canvas', 'drag-node'],
    },
    layout: {
      type: 'dagre',
      rankdir: 'LR',
      align: 'UL',
      controlPoints: true,
      nodesepFunc: () => 1,
      ranksepFunc: () => 1,
    },
    defaultNode: {
      size: [30, 20],
      type: 'rect',
      style: {
        lineWidth: 2,
        stroke: '#5B8FF9',
        fill: '#fff', // '#C6E5FF',
        radius: 4,
      },
    },
    defaultEdge: {
      type: 'polyline',
      size: 1,
      color: '#e2e2e2',
      style: {
        endArrow: {
          path: 'M 0,0 L 8,4 L 8,-4 Z',
          fill: '#e2e2e2',
        },
        radius: 20,
      },
    },
  })
  graph.data(dagreData)
  graph.render()

  graph.on('node:click', e => {
    console.log('node-----click', e)
    const item = e.item // 被操作的节点 item
    // 点击节点 看有没有上下级节点 有就新增 没有就提示
    if (item?._cfg?.model.id) {
      fetchBloodTableLevel({
        tableId: item?._cfg?.model.id,
      }).then(re => {
        let res = re.data
        if (res.upList?.length) {
          res.upList = res.upList.filter(item => item !== null)
        }
        if (res.downList?.length) {
          res.downList = res.downList.filter(item => item !== null)
        }
        console.log('res---', res, item?._cfg?.model)
        // let res = {
        //   downCount: 0, // 直接下游层数
        //   upCount: 1, // 直接上游层数
        //   upList: [
        //     // -- 上游列表
        //     {
        //       tableId: 118,
        //       dataBaseName: 'test',
        //       tableName: '上游表11',
        //       creator: 'ssitg',
        //       createTime: '2024-03-25 11:28:32',
        //       children: null,
        //     },
        //     {
        //       tableId: 119,
        //       dataBaseName: 'test',
        //       tableName: '上游表12',
        //       creator: 'ssitg',
        //       createTime: '2024-03-25 11:28:32',
        //       children: null,
        //     },
        //     {
        //       tableId: 120,
        //       dataBaseName: 'test',
        //       tableName: '上游表13',
        //       creator: 'ssitg',
        //       createTime: '2024-03-25 11:28:32',
        //       children: null,
        //     },
        //   ],
        //   downList: [
        //     {
        //       tableId: 1,
        //       dataBaseName: 'test',
        //       tableName: '下游表1',
        //       creator: 'ssitg',
        //       createTime: '2024-03-25 11:28:32',
        //       children: null,
        //     },
        //   ], // --下游列表
        //   tableName: 'aaaaa',
        //   tableId: 20,
        //   dataBaseName: 'test',
        //   creator: 'ssitg',
        //   createTime: '2024-03-25 11:28:32',
        //   children: null,
        // }
        let idList = dagreData.nodes.map(item => item.id)
        console.log('idList', idList)
        if (res.upList?.length > 0) {
          res.upList.forEach(item => {
            if (!idList.includes(String(item.tableId))) {
              dagreData.nodes.push({
                id: String(item.tableId),
                label: item.tableName,
              })
              dagreData.edges.push({
                source: String(item.tableId),
                target: String(res.tableId),
              })
            }
          })
        } else {
          proxy.$message({
            message: `表名${item?._cfg?.model.label}没有上游表`,
            duration: 1000,
            type: 'warning',
          })
        }
        if (res.downList?.length > 0) {
          res.downList.forEach(item => {
            if (!idList.includes(String(item.tableId))) {
              dagreData.nodes.push({
                id: String(item.tableId),
                label: item.tableName,
              })
              dagreData.edges.push({
                source: String(res.tableId),
                target: String(item.tableId),
              })
            }
          })
        } else {
          proxy.$message({
            message: `表名${item?._cfg?.model.label}没有下游表`,
            duration: 1000,
            type: 'warning',
          })
          return
        }
      })
      graph.data(dagreData)
      graph.render()
    }
  })

  if (typeof window !== 'undefined')
    window.onresize = () => {
      if (!graph || graph.get('destroyed')) return
      if (!container || !container.scrollWidth || !container.scrollHeight)
        return
      graph.changeSize(container.scrollWidth, container.scrollHeight)
    }
}

const back = () => {
  emits('backClick')
}

onUnmounted(() => {
  // renderDagre()
  if (graph) {
    graph.destroy()
    graph = null
  }
})

watchEffect(() => {
  if (props.clickNode?.tableId) {
    fetchBloodTableLevel({
      tableId: props.clickNode.tableId,
    }).then(res => {
      let dd = {
        downCount: 0, //--直接下游层数
        upCount: 1, //--直接上游层数
        upList: [
          //--下游列表
          {
            tableId: 18,
            dataBaseName: 'test',
            tableName: 'test',
            creator: 'ssitg',
            createTime: '2024-03-25 11:28:32',
            children: null,
          },
        ],
        downList: [], // --上游列表
        tableName: 'aaaaa', //--表名称
      }
      if (res.data) {
        let chartData = Object.assign(res.data, props.clickNode)
        chartObj.upCount = chartData.upCount
        chartObj.downCount = chartData.downCount
        if (chartData.upList?.length) {
          chartData.upList = chartData.upList.filter(item => item !== null)
        }
        if (chartData.downList?.length) {
          chartData.downList = chartData.downList.filter(item => item !== null)
        }
        console.log('chartData', chartData)
        getNodeData(chartData)

        nextTick(() => {
          renderDagre()
        })
      }
    })
  }
})
</script>
<template>
  <div>
    <div class="flex">
      <div class="mr-[24px]">表名:{{ clickNode.tableName }}</div>
      <div class="mr-[24px]">直接上游表数:{{ chartObj.upCount }}</div>
      <div class="mr-[4px]">直接下游表数:{{ chartObj.downCount }}</div>
    </div>
    <el-button class="btn absolute right-[20px]" @click="back">返回</el-button>
    <div id="container"></div>
  </div>
</template>
<style lang="less"></style>