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