d7ab1dc6 by lihua

添加关系网和桑基图

1 parent d98bd724
......@@ -28,6 +28,7 @@ declare module '@vue/runtime-core' {
FileUpload: typeof import('./src/components/FileUpload/index.vue')['default']
FixedActionBar: typeof import('./src/components/FixedActionBar/index.vue')['default']
Form: typeof import('./src/components/Form/index.vue')['default']
GraphTopbar: typeof import('./src/components/RelationNetwork/graphTopbar.vue')['default']
Hour: typeof import('./src/components/Schedule/component/hour.vue')['default']
ImagePreview: typeof import('./src/components/ImagePreview/index.vue')['default']
ImagesUpload: typeof import('./src/components/ImagesUpload/index.vue')['default']
......@@ -42,6 +43,7 @@ declare module '@vue/runtime-core' {
PageNav: typeof import('./src/components/PageNav/index.vue')['default']
PcasCascader: typeof import('./src/components/PcasCascader/index.vue')['default']
Popover: typeof import('./src/components/Popover/index.vue')['default']
RelationNetwork: typeof import('./src/components/RelationNetwork/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Schedule: typeof import('./src/components/Schedule/index.vue')['default']
......
<script lang="ts" setup name="topbar">
import { ref, watch } from 'vue';
const props = defineProps({
isGraphDisplay: {
type: Boolean,
default: true
},
});
const isGraph = ref(false);
watch(() => props.isGraphDisplay, (val) => {
isGraph.value = val;
}, {
immediate: true
})
const emits = defineEmits(["displaySwitchChange"]);
const switchChange = (val) => {
isGraph.value = val
emits('displaySwitchChange', val);
}
</script>
<template>
<div className='g6-component-topbar-content'>
<div :class="isGraph ? 'selected g6-component-topbar-item' : 'g6-component-topbar-item'" @click="switchChange(true)">
关系网
</div>
<div :class="!isGraph ? 'selected g6-component-topbar-item' : 'g6-component-topbar-item'" @click="switchChange(false)">
桑基图
</div>
</div>
</template>
<style scoped lang="scss">
.g6-component-topbar-content {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background: #fff;
border: 1px solid var(--el-color-primary);
border-radius: 32px;
padding: 4px;
width: 138px;
height: 32px;
}
.g6-component-topbar-item {
width: 50%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
color: #999999;
cursor: pointer;
&.selected {
background: #4FA1A4;
border-radius: 32px;
color: #fff;
}
}
</style>
\ No newline at end of file
<template>
<div ref="containerRef" className='canvas-wrapper'>
<div class="main" ref="tooltip1Ref" style="display: none;position: absolute;" v-loading="detailLoading">
<div class="title">{{ detailInfo.name }}</div>
<div class="row" v-for="item in Object.keys(detailInfo)">
<span>{{ item + ':' }}</span>
<span>{{ detailInfo[item] }}</span>
</div>
</div>
</div>
</template>
<script lang="ts" setup name="RelationNetwork">
import { ref, onMounted } from 'vue'
import G6 from '@antv/g6';
import insertCss from 'insert-css';
const props = defineProps({
treeData: {
type: Object,
default: {}
}
})
const emits = defineEmits([
'nodeItemClick',
'contextMenu'
]);
const detailLoading = ref(false);
const detailInfo: any = ref({
guid: '1',
'标识符': '124',
name: '字段12345字段字段字段字段字段字段字段字段字段字段',
'中文名称': '字段12345字段字段字段字段字段字段字段字段字段字段',
'英文名称': 'fieldName'
});
const containerRef = ref();
const graphRef = ref();
const resizeObserver = ref();
watch(() => props.treeData, (val) => {
tooltip1Ref.value.style.display = 'none';
if (lastSelectNode.value) {
graphRef.value.updateItem(lastSelectNode.value, {
labelCfg: {
color: '#212121'
},
style: {
stroke: '#4fa1a4',
fill: '#ebf6f7',
cursor: 'pointer'
}
});
}
lastSelectNode.value = null;
if (val) {
renderGraph(graphRef.value, val);
}
})
const renderGraph = (graph: any, lineageData: any) => {
if (!graph || !lineageData) return;
graph.setMinZoom(0.5);
graph.setMaxZoom(1);
graph.data(lineageData);
graph.render();
graph.fitView(40, { direction: 'both' });
graph.fitCenter();
graph.setMinZoom(0.5);
graph.setMaxZoom(5);
};
const detectLanguage = (text) => {
if (!text) {
return 'English';
}
let chineseCount = 0;
let englishCount = 0;
for (let char of text) {
if (/[\u4e00-\u9fa5]/.test(char)) {
chineseCount++;
} else if (/[a-zA-Z]/.test(char)) {
englishCount++;
}
}
if (chineseCount > englishCount) {
return 'Chinese';
} else if (englishCount > chineseCount) {
return 'English';
}
return 'English';
}
const handleLabelLength = (label: string) => {
if (detectLanguage(label) == 'English') {
return label?.length > 30 ? label.slice(0, 30) + '...' : label;
}
return label?.length > 16 ? label.slice(0, 16) + '...' : label;
};
insertCss(`
.g6-component-contextmenu {
padding: 8px 0px;
background-color: #fff;
}
.context-menu {
display: flex;
flex-direction: column;
}
.menu-item {
line-height: 32px;
color: #212121;
cursor: pointer;
padding: 0 12px;
}
.menu-item:hover {
background-color: #f5f5f5;
}
`);
const tooltip = ref();
onMounted(() => {
nextTick(() => {
if (!graphRef.value) {
const container: any = containerRef.value;
const width = container.clientWidth;
const height = container.clientHeight - 10;
tooltip.value = new G6.Tooltip({
offsetX: 10,
offsetY: 10,
trigger: 'mouseenter',
// 允许出现 tooltip 的 item 类型
itemTypes: ['node'],
// 自定义 tooltip 内容
shouldBegin: (evt: any) => {
const { item, target } = evt;
const currentAnchor = target.get('name');
const name = item._cfg.model?.name;
if (currentAnchor == 'text-shape') {
if (detectLanguage(name) == 'English') {
return name?.length > 30;
}
return name?.length > 16;
}
return false;
},
getContent: (e: any) => {
const { item, target } = e;
const currentAnchor = target.get('name');
const outDiv = document.createElement('div');
outDiv.className = 'node';
outDiv.style.width = 'fit-content';
const name = item._cfg.model.name;
if (currentAnchor == 'text-shape') {
outDiv.innerHTML = `<h4>${name}</h4>`
}
return outDiv;
},
});
// tooltip1.value = new G6.Tooltip({
// offsetX: 10,
// offsetY: 10,
// trigger: 'click',
// // 允许出现 tooltip 的 item 类型
// itemTypes: ['node'],
// // 自定义 tooltip 内容
// shouldBegin: (evt: any) => {
// const { item, target } = evt;
// let model = item._cfg.model;
// if (model.isField) {
// return true
// }
// return false;
// },
// getContent: async (e: any) => {
// const { item, target } = e;
// let model = item._cfg.model;
// if (!model.isField) {
// return '';
// }
// const template = ` <div class="main" v-loading="detailLoading">
// <div class="title">{{ titleName + ':' }}</div>
// <div class="row" v-for="item in Object.keys(detailInfo)">
// <span>{{ item }}</span>
// <span>{{ detailInfo[item] }}</span>
// </div>
// </div>`
// await setTimeout(() => {
// }, 200);
// const outDiv = document.createElement('div');
// outDiv.className = 'tooltip-main';
// outDiv.style.width = '320px';
// for (const key in detailInfo.value) {
// const domRow = document.createElement('div');
// domRow.className = 'row';
// let span1 = document.createElement('span');
// span1.innerHTML = key + ':';
// let span2 = document.createElement('span');
// span2.innerHTML = detailInfo[key];
// domRow.appendChild(span1);
// domRow.appendChild(span2);
// outDiv.appendChild(domRow);
// }
// return outDiv;
// },
// });
const contextMenu = new G6.Menu({
getContent(evt: any) {
const { item, target } = evt;
let model = item._cfg.model;
return `
<div class='context-menu'>
<span class='menu-item'>引用标准新建数据集</span>
</div>`
},
shouldBegin: (evt: any) => {
const { item, target } = evt;
let model = item._cfg.model;
if (model && !model.isField && !model.children?.length) {
return true;
}
return false;
},
handleMenuClick: (target, item: any) => {
let model = item._cfg?.model;
if (!model) {
return;
}
emits('contextMenu', model);
},
// offsetX and offsetY include the padding of the parent container
// 需要加上父级容器的 padding-left 16 与自身偏移量 10
offsetX: 16,
// 需要加上父级容器的 padding-top 24 、画布兄弟元素高度、与自身偏移量 10
offsetY: 0,
// the types of items that allow the menu show up
// 在哪些类型的元素上响应
itemTypes: ['node'],
});
const graph = new G6.TreeGraph({
container: container,
width,
height,
plugins: [contextMenu, tooltip.value],
fitCenter: true,
fitView: true,
fitViewPadding: 40,
minZoom: 0.5,
maxZoom: 1,
modes: {
default: [
{
type: 'collapse-expand',
onChange: (item, collapsed) => {
if (!item) {
return;
}
const data = item.getModel();
data.collapsed = collapsed;
return true;
},
shouldBegin: (e) => {
// 若当前操作的节点 id 为 'node1',则不发生 collapse-expand
if (e.item && e.item.getModel().id === 'node1') return false;
return true;
},
},
'drag-canvas',
'zoom-canvas',
],
},
defaultNode: {
size: 24,
anchorPoints: [
[0, 0.5],
[1, 0.5],
],
style: {
// stroke: '#4fa1a4',
fill: '#ebf6f7',
cursor: 'pointer'
},
},
defaultEdge: {
type: 'cubic-horizontal',
},
layout: {
type: 'compactBox',
direction: 'LR',
getId: function getId(d) {
return d.id;
},
getHeight: function getHeight() {
return 16;
},
getWidth: function getWidth() {
return 16;
},
getVGap: function getVGap() {
return 30;
},
getHGap: function getHGap() {
return 120;
},
},
});
graphRef.value = graph;
graph.node((node) => {
return {
id: node.guid as string,
label: handleLabelLength(node.name as string),
collapsed: node.children?.length ? false : true,
labelCfg: {
offset: 10,
style: {
fontSize: 16,
fill: '#212121',
},
position: !node.isField ? 'left' : 'right', //只有字段是最后一层级,不需要展开
},
style: {
stroke: '#4fa1a4',
cursor: 'pointer'
}
};
});
// data不是数组,第一级是根节点
graph.data(props.treeData);
graph.render();
graph.fitView(40, { direction: 'both' });
graph.fitCenter();
graph.setMinZoom(0.5);
graph.setMaxZoom(5);
}
observeResize();
bindEvents();
})
})
const observeResize = () => {
resizeObserver.value = new ResizeObserver(() => {
let domWidth = document.documentElement.clientWidth;
if (domWidth < 992) {//根据setting.ts里的设置,小于992,会隐藏左边的菜单栏,
setTimeout(() => {
const container: any = containerRef.value;
const width = container.clientWidth;
const height = container.clientHeight - 10;
if (!width) {//会把隐藏的给消失。
return;
}
graphRef.value.changeSize(width, height);
graphRef.value.setMinZoom(0.5);
graphRef.value.setMaxZoom(1);
graphRef.value.fitView(40, { direction: 'both' });
graphRef.value.fitCenter();
graphRef.value.setMinZoom(0.5);
graphRef.value.setMaxZoom(5);
}, 500)
}
const container: any = containerRef.value;
const width = container.clientWidth;
const height = container.clientHeight - 10;
if (!width) {//会把隐藏的给消失。
return;
}
graphRef.value.changeSize(width, height);
graphRef.value.setMinZoom(0.5);
graphRef.value.setMaxZoom(1);
graphRef.value.fitView(40, { direction: 'both' });
graphRef.value.fitCenter();
graphRef.value.setMinZoom(0.5);
graphRef.value.setMaxZoom(5);
});
resizeObserver.value.observe(containerRef.value);
}
const tooltip1Ref = ref();
// 更新tooltip的位置
function updateTooltipPosition(evt) {
var width = graphRef.value.get("width");
var height = graphRef.value.get("height");
var offsetX = 10;
var offsetY = 10;
var point = graphRef.value.getPointByClient(evt.clientX, evt.clientY);
var _a2 = graphRef.value.getCanvasByPoint(point.x, point.y), x4 = _a2.x, y4 = _a2.y;
var graphContainer = graphRef.value.getContainer();
var res = {
x: x4 + graphContainer.offsetLeft + offsetX,
y: y4 + graphContainer.offsetTop + offsetY
};
let bboxHeight = tooltip1Ref.value.getBoundingClientRect().height;
if (x4 + 320 + offsetX > width) {
res.x -= 320 + offsetX;
}
if (y4 + bboxHeight + offsetY > height) {
res.y -= bboxHeight + offsetY;
if (res.y < 0) {
res.y = 0;
}
}
tooltip1Ref.value.style.left = `${res.x}px`;
tooltip1Ref.value.style.top = `${res.y}px`;
tooltip1Ref.value.style.display = 'block';
}
const lastSelectNode = ref(null);
const bindEvents = () => {
let graph = graphRef.value;
graph.on('node:click', function (evt) {
const item = evt.item;
if (!item) {
return;
}
const nodeId = item.get('guid');
const model = <any>item.getModel();
if (lastSelectNode.value) {
if (lastSelectNode.value == item) {
return;
}
tooltip1Ref.value.style.display = 'none';
graphRef.value.updateItem(lastSelectNode.value, {
labelCfg: {
style: {
fill: '#212121',
}
},
style: {
stroke: '#4fa1a4',
fill: '#ebf6f7',
cursor: 'pointer'
}
});
lastSelectNode.value = null;
}
const children = model.children;
if (children?.length && !model.isField) { //是字段级别,就不需要再展开了
return;
}
if (model.isField) {
graphRef.value.updateItem(item, {
labelCfg: {
style: {
fill: '#4fa1a4'
}
},
style: {
stroke: '#4fa1a4',
fill: '#4fa1a4',
cursor: 'pointer'
}
});
lastSelectNode.value = item;
detailInfo.value.guid = model.guid;
updateTooltipPosition(evt);
detailLoading.value = true;
setTimeout(() => {
detailLoading.value = false;
}, 500);
return;
}
emits('nodeItemClick', graph, item);
evt.preventDefault();
evt.stopPropagation();
});
graph.on('dragstart', (evt: any) => {
if (evt.item?.getType() == 'node') {
return;
}
// 清除状态
if (lastSelectNode.value) {
tooltip1Ref.value.style.display = 'none';
graphRef.value.updateItem(lastSelectNode.value, {
labelCfg: {
style: {
fill: '#212121',
},
},
style: {
stroke: '#4fa1a4',
fill: '#ebf6f7',
cursor: 'pointer'
}
});
}
lastSelectNode.value = null;
})
graph.on('click', (evt: any) => {
if (evt.item?.getType() == 'node') {
return;
}
// 清除状态
if (lastSelectNode.value) {
tooltip1Ref.value.style.display = 'none';
graphRef.value.updateItem(lastSelectNode.value, {
labelCfg: {
style: {
fill: '#212121',
},
},
style: {
stroke: '#4fa1a4',
fill: '#ebf6f7',
cursor: 'pointer'
}
});
}
lastSelectNode.value = null;
});
graph.on('canvas:click', (evt: any) => {
// 清除状态
if (lastSelectNode.value) {
tooltip1Ref.value.style.display = 'none';
graphRef.value.updateItem(lastSelectNode.value, {
labelCfg: {
style: {
fill: '#212121',
},
},
style: {
stroke: '#4fa1a4',
fill: '#ebf6f7',
cursor: 'pointer'
}
});
}
lastSelectNode.value = null;
});
}
const destroy = () => {
graphRef.value?.destroy();
}
defineExpose({
destroy,
});
</script>
<style lang="scss" scoped>
.canvas-wrapper {
width: 100%;
height: 100%;
position: relative;
}
.canvas-wrapper:-webkit-full-screen {
background-color: white;
}
.canvas-wrapper:-moz-full-screen {
background-color: white;
}
.canvas-wrapper:-ms-fullscreen {
background-color: white;
}
.canvas-wrapper:fullscreen {
background-color: white;
}
.main {
padding: 16px;
box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.08);
background-color: #fff;
max-width: 320px;
max-height: 400px;
overflow-y: auto;
.title {
font-size: 16px;
color: #212121;
line-height: 24px;
font-weight: 600;
}
.row {
margin-top: 8px;
font-size: 14px;
color: #666666;
line-height: 21px;
}
}
</style>
\ No newline at end of file
......@@ -28,6 +28,7 @@ declare module '@vue/runtime-core' {
FileUpload: typeof import('./../components/FileUpload/index.vue')['default']
FixedActionBar: typeof import('./../components/FixedActionBar/index.vue')['default']
Form: typeof import('./../components/Form/index.vue')['default']
GraphTopbar: typeof import('./../components/RelationNetwork/graphTopbar.vue')['default']
Hour: typeof import('./../components/Schedule/component/hour.vue')['default']
ImagePreview: typeof import('./../components/ImagePreview/index.vue')['default']
ImagesUpload: typeof import('./../components/ImagesUpload/index.vue')['default']
......@@ -42,6 +43,7 @@ declare module '@vue/runtime-core' {
PageNav: typeof import('./../components/PageNav/index.vue')['default']
PcasCascader: typeof import('./../components/PcasCascader/index.vue')['default']
Popover: typeof import('./../components/Popover/index.vue')['default']
RelationNetwork: typeof import('./../components/RelationNetwork/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Schedule: typeof import('./../components/Schedule/index.vue')['default']
......
<template>
<div ref="containerRef" class='canvas-wrapper'>
</div>
</template>
<script lang="ts" setup name="topbar">
import { ref, watch } from 'vue';
import * as echarts from "echarts";
const props = defineProps({
treeData: {
type: Array<any>,
default: [
{
source: 'a',
target: 'a1',
value: 5
},
{
source: 'a',
target: 'a2',
value: 3
},
{
source: 'b',
target: 'b1',
value: 8
},
{
source: 'a',
target: 'b1',
value: 3
},
{
source: 'b1',
target: 'a1',
value: 1
},
{
source: 'b1',
target: 'c',
value: 2
}
]
},
names: {
type: Array<any>,
default: [
{
name: 'a'
},
{
name: 'b'
},
{
name: 'a1'
},
{
name: 'a2'
},
{
name: 'b1'
},
{
name: 'c'
}
]
}
})
watch(() => props.treeData, (val) => {
setChartsOption();
})
const sankeyInstance: any = ref();
const containerRef = ref();
const setChartsOption = () => {
let option = {
tooltip: {
trigger: 'item'
},
series: [
{
type: 'sankey',
top: 80,
bottom: 40,
left: 50,
right: 50,
data: props.names,
links: props.treeData,
lineStyle: {
color: 'source',
curveness: 0.5
},
}
]
}
sankeyInstance.value.setOption(option);
sankeyInstance.value.resize();
}
const resizeObserver = ref();
const observeResize = () => {
resizeObserver.value = new ResizeObserver(() => {
sankeyInstance.value?.resize();
});
resizeObserver.value.observe(containerRef.value);
}
onMounted(() => {
nextTick(() => {
sankeyInstance.value = echarts.init(containerRef.value);
setChartsOption();
observeResize();
})
})
</script>
<style lang="scss" scoped>
.canvas-wrapper {
width: 100%;
height: 100%;
position: relative;
}
</style>
\ No newline at end of file
<route lang="yaml">
name: metadataStandardQuery
</route>
<script lang="ts" setup name="metadataStandardQuery">
import { ref } from 'vue';
import { ElMessage, ElMessageBox } from "element-plus";
import Sankey from './components/Sankey.vue';
import Tree from '@/components/Tree/index.vue';
import RelationNetwork from '@/components/RelationNetwork/index.vue';
import {
} from '@/api/modules/dataMetaService';
import { useRouter, useRoute } from "vue-router";
import useDataMetaStore from "@/store/modules/dataMeta"
const router = useRouter();
const route = useRoute()
const switchGraphDisplay = ref(true); //true表示关系网,false表示桑基图
const { proxy } = getCurrentInstance() as any;
const relationNetworkRef = ref();
const treeInfo = ref({
id: "data-meta-standard-tree",
filter: true,
queryValue: "",
queryPlaceholder: "输入库/表名称搜索",
props: {
label: "name",
value: "guid",
isLeaf: "isLeaf",
},
nodeKey: 'guid',
expandedKey: ['1'],
currentNodeKey: '',
expandOnNodeClick: false,
data: <any>[],
loading: false
});
/** 获取左侧树数据. */
const getTreeData = async () => {
treeInfo.value.loading = true
// let params = {}
// const res: any = await getMetaTreeData(params)
// if (res.code == proxy.$passCode) {
// const data = res.data || [];
// let treeData: any = Object.keys(data).length ? [data] : [];
// treeInfo.value.data = treeData;
// allTreeData.value = treeData;
// if (treeData.length) {
// treeInfo.value.currentNodeKey = treeData[0].guid;
// treeInfo.value.expandedKey = <any>[treeData[0].guid];
// }
// } else {
// ElMessage.error(res.msg);
// }
treeInfo.value.data = [{
guid: '1',
name: '第一级1',
children: [{
guid: '1-1',
name: '第二级1-1',
children: [{
guid: '1-1-1',
name: '第三级1-1-1'
}]
}, {
guid: '1-2',
name: '第二级1-2'
}, {
guid: '1-3',
name: '第二级1-3'
}]
}, {
guid: '2',
name: '第一级2',
children: [{
guid: '2-1',
name: '第二级1-1'
}, {
guid: '2-2',
name: '第二级1-2'
}]
}];
treeInfo.value.loading = false
treeInfo.value.currentNodeKey = '1';
nodeClick(treeInfo.value.data[0])
}
/** 左侧树的的组件引用. */
const treeInfoRef = ref();
/** 当前选中的树节点数据data */
const lastClickNode: any = ref({});
const treeDataLoading = ref(false);
/** 数据血缘关系图组件 */
const lineageGraph: any = ref();
/** 点击左侧树节点,更新对应的血缘关系图. */
const nodeClick = (data) => {
console.log(data);
const ele = <HTMLElement>document.querySelector(".g6-component-contextmenu")
if (ele) {
ele.style.display = "none"
}
nextTick(() => {
lineageGraph.value?.tooltip1.hide()
})
treeInfo.value.currentNodeKey = data.guid;
treeInfo.value.expandedKey = <any>[data.guid];
lastClickNode.value = data;
}
/** 选中树节点后自动滚动到可视范围内. */
const scrollToNode = (nodeId) => {
nextTick(() => {
const nodeElement = treeInfoRef.value.treeRef.$el.querySelector(`[data-key="${nodeId}"]`);
if (nodeElement) {
nodeElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
});
}
/** 处理从详情处跳转而来的默认展示. */
const processRouter = () => {
const { guid, databaseName, tableName, databaseChName, databaseGuid, fieldGuid, fieldEnName, set } = useDataMetaStore()
let isFL = useDataMetaStore().isFieldLineage;
if (fieldGuid) {//查看字段血缘的
nextTick(() => {
treeInfo.value.expandedKey = <any>[databaseGuid, guid];
treeInfo.value.currentNodeKey = fieldGuid as string;
scrollToNode(fieldGuid);
treeInfoRef.value.setCurrentKey(fieldGuid);
})
lastClickNode.value = { guid: fieldGuid, tableGuid: guid, databaseGuid: databaseGuid, enName: fieldEnName, tableName, databaseName, type: 4, isLeaf: true, databaseChName };
// getTableFieldLineageMap();
set()
} else if (guid) {
treeInfo.value.currentNodeKey = guid as string;
treeInfo.value.expandedKey = <any>[databaseGuid, guid];
lastClickNode.value = { guid: guid, tableName, databaseName, type: 3, databaseChName };
scrollToNode(guid);
switchGraphDisplay.value = isFL;
// if (isFL) {
// getAllTableFieldLineageMap();
// } else {
// getTableLineageMap()
// }
set()
}
}
onActivated(() => {
processRouter();
});
onBeforeMount(async () => {
await getTreeData()
processRouter();
})
onMounted(() => { })
const isGraphDisplay = ref(true);
const displaySwitchChange = (val) => {
isGraphDisplay.value = val;
}
const handleNodeItemClick = (graph, nodeItem) => {
const nodeId = nodeItem.get('id');
const parentData = graph.findDataById(nodeId);
if (!parentData.children) {
parentData.children = [];
}
treeDataLoading.value = true;
let childData = [{
guid: '33',
isField: true,
name: '字段1'
}, {
guid: '44',
isField: true,
name: '字段2字段2字段2字段2字段2字段2字段2字段2字段2xx2字段22字段22字段22字段22字段22字段2'
}, {
guid: '55',
isField: true,
name: '字段3'
}]
// parentData.collapsed = true;
// nodeItem.getModel().collapsed = true;
parentData.children = childData;
setTimeout(() => {
treeDataLoading.value = false;
graph.changeData();
// parentData.collapsed = true;
// graph.updateChildren(childData, parentData.id);
// graph.changeData(lastClickNode.value);
//graph.data(lastClickNode.value);
}, 2000)
}
const handleContextMenu = (nodeData) => {
//TODO,新建引用数据集
}
onBeforeUnmount(() => {
relationNetworkRef.value.destroy();
})
</script>
<template>
<div>元数据标准查询</div>
<div class="container_wrap full flex">
<div class="aside_wrap">
<div class="aside_title">数据库目录列表</div>
<Tree ref="treeInfoRef" :treeInfo="treeInfo" @nodeClick="nodeClick" />
</div>
<div class="main_wrap">
<div className='g6-component-topbar'>
<graphTopbar ref="topBarRef" @displaySwitchChange="displaySwitchChange" :isGraphDisplay="isGraphDisplay" />
</div>
<RelationNetwork v-show="isGraphDisplay" ref="relationNetworkRef" :tree-data="lastClickNode"
v-loading="treeDataLoading" @nodeItemClick="handleNodeItemClick" @contextMenu="handleContextMenu">
</RelationNetwork>
<Sankey v-show="!isGraphDisplay"></Sankey>
<!-- <div v-show="lastClickNode && lastClickNode?.type !== 3 && lastClickNode?.type !== 4" class="main-placeholder">
<img src="../../assets/images/no-data.png" :style="{ width: '96px', height: '96px' }" />
<div class="empty-text">暂无标准数据</div>
</div> -->
</div>
</div>
</template>
<script>
export default {
<style scoped lang="scss">
.container_wrap {
.aside_wrap {
width: 200px;
margin-right: 1px;
}
.main_wrap {
position: relative;
:deep(.canvas-wrapper) {
background-color: #f7f7f9;
}
.main-placeholder {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.empty-text {
font-size: 14px;
color: #b2b2b2;
}
}
}
}
</script>
<style>
.g6-component-topbar {
position: absolute;
left: 24px;
bottom: unset;
top: 14px;
padding: 0;
text-align: center;
z-index: 999;
}
.container_wrap.flex .main_wrap {
padding: 0px;
}
.tree_panel {
height: calc(100% - 36px);
padding-top: 0;
:deep(.el-tree) {
margin: 0;
overflow: hidden auto;
}
}
.card-noData {
height: 100%;
width: 100%;
background: #fafafa;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #909399;
font-size: 14px;
}
:deep(.el-form .el-form-item) {
width: calc(100%);
// margin-right: 8px;
}
:deep(.el-message) {
position: fixed;
/* 使用fixed或absolute定位 */
z-index: 10000;
/* 设置一个较高的z-index值确保在最上层显示 */
}
</style>
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!