同时渲染多个antv G6 tree

290 阅读3分钟

根据后台返回的数据结构同时渲染多个tree. 给树容器设置动态高度,自适应。使用antv G6渲染树 image.png

<!--
 * @Date: 2024-03-19 15:53:26
 * @LastEditors: mingongze (andersonmingz@gmail.com)
 * @LastEditTime: 2024-04-16 19:19:02
 * @FilePath: /datagov_web/src/views/metadata/blood/index.vue
-->
<script setup lang="ts">
import { onMounted, ref, reactive, watch } from 'vue'
import {
  fetchDatabaseList,
  fetchBloodTableList,
  fetchBloodTree,
} from '@/api/metaData'
import projectSelect from '@/components/reusable/projectSelect.vue'
import G6 from '@antv/g6'
import treeG6Config from '@/utils/treeG6Config'
import bloodBranch from '@/components/views/metadata/blood/bloodBranch.vue'

const form = reactive({
  projectId: '',
  dataBaseName: '',
  tableName: '',
  pageNumber: 1,
  pageSize: 10,
})
let isTreeShow = ref(true)

const arr2 = ref([])
const tableList = ref([])
const graphList = ref<any[]>([])
let defaultProjectId = ref()
const onProjectLoad = val => {
  defaultProjectId.value = val
  onProjectChange(val)
  getData()
}

const getTableList = () => {
  fetchBloodTableList({
    projectId: form.projectId,
    dataBaseName: form.dataBaseName,
  }).then(res => {
    tableList.value = res.data
  })
}

let clickNode = ref()

let arr = [
  {
    id: '11',
    tableId: 28, // --表ID
    dataBaseName: 'ces88', //  --数据库名称
    tableName: 'dept_partition', // --表名称
    creator: 'ssitg', //--创建人
    createTime: '2024-04-11 15:06:08', // --创建时间
    children: [
      // --下一级
      {
        id: '12',
        tableId: 27,
        dataBaseName: 'ces88',
        tableName: 'ces_eee_ggg',
        creator: 'ssitg',
        createTime: '2024-04-11 15:01:42',
        children: [],
      },
    ],
  },
  {
    id: '13',
    tableId: 29,
    dataBaseName: 'ces88',
    tableName: 'maping',
    creator: 'ssitg',
    createTime: '2024-04-11 15:08:29',
    children: [
      {
        id: '131',
        tableId: 27,
        dataBaseName: 'ces88',
        tableName: 'ces_eee_ggg',
        creator: 'ssitg',
        createTime: '2024-04-11 15:01:42',
        children: [],
      },
    ],
  },
  {
    id: '132',
    tableId: 18,
    dataBaseName: 'test',
    tableName: 'test',
    creator: 'ssitg',
    createTime: '2024-03-25 11:28:32',
    children: [
      {
        id: '134',
        tableId: 19,
        dataBaseName: 'test',
        tableName: 'aaaaa',
        creator: 'ssitg',
        createTime: '2024-03-25 11:30:03',
        children: [],
      },
    ],
  },
  {
    id: '1332',
    tableId: 18,
    dataBaseName: 'test',
    tableName: 'test',
    creator: 'ssitg',
    createTime: '2024-03-25 11:28:32',
    children: [
      {
        id: '1234',
        tableId: 19,
        dataBaseName: 'test',
        tableName: 'aaaaa',
        creator: 'ssitg',
        createTime: '2024-03-25 11:30:03',
        children: [],
      },
    ],
  },
]
let bloodList = ref(arr)

G6.registerNode('card-node', treeG6Config)

const initialRender = (containerId, data) => {
  const container = document.getElementById(containerId)
  const width = container.scrollWidth || 1024
  const height = container.scrollHeight || 300
  console.log('containerId, data', containerId, data)

  const graphInstance = new G6.TreeGraph({
    container: containerId,
    width,
    height,
    // plugins: [tooltip],
    modes: {
      default: ['drag-canvas'],
    },
    defaultNode: {
      type: 'card-node',
      size: [100, 40],
    },
    defaultEdge: {
      type: 'cubic-horizontal',
      style: {
        endArrow: true,
      },
    },
    layout: {
      type: 'indented',
      direction: 'LR',
      dropCap: false,
      indent: 200,
      getHeight: () => {
        return 60
      },
    },
  })
  graphList.value.push(graphInstance)

  graphInstance.data(data)
  graphInstance.render()

  graphInstance.on('node:click', e => {
    if (e.target.get('name') === 'collapse-icon') {
      e.item.getModel().collapsed = !e.item.getModel().collapsed
      graphInstance.setItemState(
        e.item,
        'collapsed',
        e.item.getModel().collapsed
      )
      graphInstance.layout()
    }
    const item = e.item // 被操作的节点 item
    const target = e.target // 被操作的具体图形
    // console.log('yellow----', item?._cfg)
    clickNode.value = item?._cfg?.model
    isTreeShow.value = false
    // 点击节点 看有没有下级节点 有就新增 没有就提示
  })

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

// 把tableId转为id字段
const convertData = data => {
  data.forEach(d => {
    d.id = String(d.tableId)
    if (d.children && d.children.length) {
      convertData(d.children)
    }
  })
}

const getData = () => {
  if (form.projectId && form.dataBaseName) {
    fetchBloodTree({
      projectId: form.projectId,
      dataBaseName: form.dataBaseName,
    }).then(res => {
      if (res?.data?.bloodList?.length) {
        convertData(res.data.bloodList)
        bloodList.value = res.data.bloodList
      } else {
        bloodList.value = []
      }
      console.log('nice----', bloodList.value)
    })
  }
}

// 数据库下拉
const getDatabaseOptions = () => {
  fetchDatabaseList({ projectId: form.projectId }).then(res => {
    arr2.value = res.data
    form.dataBaseName = res.data[0]
    getData()
    getTableList()
  })
}

let projectSelectRef = ref()

const onProjectChange = val => {
  form.projectId = val
  getDatabaseOptions()
}

const onHiveChange = () => {
  getTableList()
  getData() // 获取树下拉
}
let activeTableId = ref()

const onTableChange = val => {
  let item = tableList.value.find(item => item.tableId === val)
  projectSelectRef.value.projectList
  isTreeShow.value = false
  clickNode.value = item
}

const handleSearch = () => {
  getData()
}
const handleReset = () => {}

watch(
  () => bloodList.value,
  () => {
    // 每次渲染前判断 如果有图形 就销毁 重新开始
    if (graphList.value?.length) {
      graphList.value.forEach(item => {
        item.destroy()
      })
    }
    if (bloodList.value.length) {
      nextTick(() => {
        graphList.value = []
        bloodList.value.forEach((item, itemIndex) => {
          initialRender('tree' + itemIndex, item)
        })
      })
    }
  }
)

onMounted(() => {})
</script>
<template>
  <div class="bgwhite min-h-lvh">
    <section class="search_section u-f u-fw" v-show="isTreeShow">
      <div class="list u-f u-f-ac">
        <span class="label">项目:</span>
        <project-select
          @projectChange="onProjectChange"
          @load="onProjectLoad"
          ref="projectSelectRef"
        />
      </div>
      <div class="list u-f u-f-ac">
        <span class="label">Hive库:</span>
        <el-select
          v-model="form.dataBaseName"
          filterable
          clearable
          placeholder="请选择"
          @change="onHiveChange"
        >
          <el-option
            v-for="item in arr2"
            :label="item"
            :value="item"
            :key="item"
          ></el-option>
        </el-select>
      </div>
      <div class="list u-f u-f-ac">
        <span class="label">表名:</span>
        <el-select
          v-model="form.tableName"
          filterable
          clearable
          placeholder="请选择"
          @change="onTableChange"
        >
          <el-option
            v-for="item in tableList"
            :label="item.tableName"
            :value="item.tableId"
            :key="item"
          ></el-option>
        </el-select>
      </div>
      <el-button class="btn" type="primary" @click="handleSearch"
        >查询</el-button
      >
      <el-button class="btn" @click="handleReset">重置</el-button>
    </section>
    <div
      class="jsmind pt-[10px]"
      :style="`height: ${bloodList.length * 200}px`"
      v-show="isTreeShow"
    >
      <div
        v-for="(tree, index) in bloodList"
        :key="index"
        class="tree-container"
      >
        <div :id="'tree' + index" class="tree"></div>
      </div>
    </div>
    <div v-show="!isTreeShow" class="p-4">
      <blood-branch :clickNode @backClick="isTreeShow = true" />
    </div>
  </div>
</template>
<style lang="less" scoped>
.jsmind {
  width: 1024px;
  height: 300px;
  background-color: #fff;
  // border: solid 1px #ccc;
  .tree-container {
    width: 1024px;
    height: 200px;
  }
}
</style>

treeG6Config.js
/*
 * @Date: 2024-04-16 16:52:36
 * @LastEditors: mingongze (andersonmingz@gmail.com)
 * @LastEditTime: 2024-04-16 17:05:05
 * @FilePath: /datagov_web/src/utils/treeG6Config.js
 */
import G6 from '@antv/g6'

const CustomNode = {
  draw: function drawShape(cfg, group) {
    const r = 12
    const color = '#5B8FF9'
    const w = cfg.size[0]
    const h = cfg.size[1]
    const shape = group.addShape('rect', {
      attrs: {
        x: w / 2 + 310,
        y: h / 2,
        width: w,
        height: h,
        stroke: color,
        radius: r,
        fill: '#fff',
      },
      name: 'main-box',
      draggable: true,
    })

    group.addShape('text', {
      attrs: {
        textBaseline: 'middle',
        x: w / 2 + 310,
        y: h,
        lineHeight: 120,
        cursor: 'pointer',
        text: cfg.tableName,
        fontSize: 19,
        fill: '#333',
      },
      name: 'title',
    })

    if (cfg.children) {
      group.addShape('marker', {
        attrs: {
          x: w + w / 2 + 310,
          y: h,
          r: 6,
          cursor: 'pointer',
          symbol: cfg.collapsed ? G6.Marker.expand : G6.Marker.collapse,
          stroke: '#666',
          lineWidth: 1,
          fill: '#fff',
        },
        name: 'collapse-icon',
      })
    }

    return shape
  },

  setState(name, value, item) {
    if (name === 'collapsed') {
      const marker = item
        .get('group')
        .find(ele => ele.get('name') === 'collapse-icon')
      const icon = value ? G6.Marker.expand : G6.Marker.collapse
      marker.attr('symbol', icon)
    }
  },
}

export default CustomNode