根据后台返回的数据结构同时渲染多个tree. 给树容器设置动态高度,自适应。使用antv G6渲染树
<!--
* @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