7d40597e by lihua

查看关系网接口联调

1 parent 4d217cc9
...@@ -741,6 +741,7 @@ onMounted(() => { ...@@ -741,6 +741,7 @@ onMounted(() => {
741 toolRef.value.isFull = true 741 toolRef.value.isFull = true
742 } 742 }
743 }, 500) 743 }, 500)
744 return;
744 } 745 }
745 const container: any = containerRef.value; 746 const container: any = containerRef.value;
746 const width = container.clientWidth; 747 const width = container.clientWidth;
......
1 <template> 1 <template>
2 <div ref="containerRef" className='canvas-wrapper'> 2 <div ref="containerRef" className='canvas-wrapper'>
3 <div class="main" ref="tooltip1Ref" style="display: none;position: absolute;" v-loading="detailLoading"> 3 <div class="main" ref="tooltip1Ref" style="display: none;position: absolute;" v-loading="detailLoading">
4 <div class="title">{{ detailInfo.name }}</div> 4 <div class="title">{{ detailInfoLabel }}</div>
5 <div class="row" v-for="item in Object.keys(detailInfo)"> 5 <div class="row" v-for="item in Object.keys(detailInfo)">
6 <span>{{ item + ':' }}</span> 6 <span>{{ item + ':' }}</span>
7 <span>{{ detailInfo[item] }}</span> 7 <span>{{ detailInfo[item] }}</span>
...@@ -14,11 +14,19 @@ ...@@ -14,11 +14,19 @@
14 import { ref, onMounted } from 'vue' 14 import { ref, onMounted } from 'vue'
15 import G6 from '@antv/g6'; 15 import G6 from '@antv/g6';
16 import insertCss from 'insert-css'; 16 import insertCss from 'insert-css';
17 import {
18 getMetaStandardFieldDetail
19 } from '@/api/modules/dataMetaService';
20 import { ElMessage } from 'element-plus';
17 21
18 const props = defineProps({ 22 const props = defineProps({
19 treeData: { 23 treeData: {
20 type: Object, 24 type: Object,
21 default: {} 25 default: {}
26 },
27 noContextMenu: {
28 type: Boolean,
29 default: false
22 } 30 }
23 }) 31 })
24 32
...@@ -27,15 +35,13 @@ const emits = defineEmits([ ...@@ -27,15 +35,13 @@ const emits = defineEmits([
27 'contextMenu' 35 'contextMenu'
28 ]); 36 ]);
29 37
38 const { proxy } = getCurrentInstance() as any;
39
30 const detailLoading = ref(false); 40 const detailLoading = ref(false);
31 41
32 const detailInfo: any = ref({ 42 const detailInfo: any = ref({});
33 guid: '1', 43
34 '标识符': '124', 44 const detailInfoLabel = ref('');
35 name: '字段12345字段字段字段字段字段字段字段字段字段字段',
36 '中文名称': '字段12345字段字段字段字段字段字段字段字段字段字段',
37 '英文名称': 'fieldName'
38 });
39 45
40 const containerRef = ref(); 46 const containerRef = ref();
41 47
...@@ -44,6 +50,13 @@ const graphRef = ref(); ...@@ -44,6 +50,13 @@ const graphRef = ref();
44 const resizeObserver = ref(); 50 const resizeObserver = ref();
45 51
46 watch(() => props.treeData, (val) => { 52 watch(() => props.treeData, (val) => {
53 if (!graphRef.value && val?.guid) {
54 initGraph();
55 return;
56 }
57 if (!graphRef.value) {
58 return;
59 }
47 tooltip1Ref.value.style.display = 'none'; 60 tooltip1Ref.value.style.display = 'none';
48 if (lastSelectNode.value) { 61 if (lastSelectNode.value) {
49 graphRef.value.updateItem(lastSelectNode.value, { 62 graphRef.value.updateItem(lastSelectNode.value, {
...@@ -130,232 +143,191 @@ insertCss(` ...@@ -130,232 +143,191 @@ insertCss(`
130 143
131 const tooltip = ref(); 144 const tooltip = ref();
132 145
133 onMounted(() => { 146 const initGraph = () => {
134 nextTick(() => { 147 const container: any = containerRef.value;
135 if (!graphRef.value) { 148 const width = container.clientWidth;
136 const container: any = containerRef.value; 149 const height = container.clientHeight - 10;
137 const width = container.clientWidth; 150
138 const height = container.clientHeight - 10; 151 tooltip.value = new G6.Tooltip({
139 152 offsetX: 10,
140 tooltip.value = new G6.Tooltip({ 153 offsetY: 10,
141 offsetX: 10, 154 trigger: 'mouseenter',
142 offsetY: 10, 155 // 允许出现 tooltip 的 item 类型
143 trigger: 'mouseenter', 156 itemTypes: ['node'],
144 // 允许出现 tooltip 的 item 类型 157 // 自定义 tooltip 内容
145 itemTypes: ['node'], 158 shouldBegin: (evt: any) => {
146 // 自定义 tooltip 内容 159 const { item, target } = evt;
147 shouldBegin: (evt: any) => { 160 const currentAnchor = target.get('name');
148 const { item, target } = evt; 161 const name = item._cfg.model?.label;
149 const currentAnchor = target.get('name'); 162 if (currentAnchor == 'text-shape') {
150 const name = item._cfg.model?.name; 163 if (detectLanguage(name) == 'English') {
151 if (currentAnchor == 'text-shape') { 164 return name?.length > 30;
152 if (detectLanguage(name) == 'English') { 165 }
153 return name?.length > 30; 166 return name?.length > 16;
154 } 167 }
155 return name?.length > 16; 168 return false;
156 } 169 },
157 return false; 170 getContent: (e: any) => {
158 }, 171
159 getContent: (e: any) => { 172 const { item, target } = e;
160 173 const currentAnchor = target.get('name');
161 const { item, target } = e; 174 const outDiv = document.createElement('div');
162 const currentAnchor = target.get('name'); 175 outDiv.className = 'node';
163 const outDiv = document.createElement('div'); 176 outDiv.style.width = 'fit-content';
164 outDiv.className = 'node'; 177 const name = item._cfg.model.label;
165 outDiv.style.width = 'fit-content'; 178 if (currentAnchor == 'text-shape') {
166 const name = item._cfg.model.name; 179 outDiv.innerHTML = `<h4>${name}</h4>`
167 if (currentAnchor == 'text-shape') { 180 }
168 outDiv.innerHTML = `<h4>${name}</h4>` 181 return outDiv;
169 } 182 },
170 return outDiv; 183 });
171 },
172 });
173 184
174 // tooltip1.value = new G6.Tooltip({ 185 const contextMenu = new G6.Menu({
175 // offsetX: 10, 186 getContent(evt: any) {
176 // offsetY: 10, 187 const { item, target } = evt;
177 // trigger: 'click', 188 let model = item._cfg.model;
178 // // 允许出现 tooltip 的 item 类型 189 return `
179 // itemTypes: ['node'],
180 // // 自定义 tooltip 内容
181 // shouldBegin: (evt: any) => {
182 // const { item, target } = evt;
183 // let model = item._cfg.model;
184 // if (model.isField) {
185 // return true
186 // }
187 // return false;
188 // },
189 // getContent: async (e: any) => {
190 // const { item, target } = e;
191 // let model = item._cfg.model;
192 // if (!model.isField) {
193 // return '';
194 // }
195 // const template = ` <div class="main" v-loading="detailLoading">
196 // <div class="title">{{ titleName + ':' }}</div>
197 // <div class="row" v-for="item in Object.keys(detailInfo)">
198 // <span>{{ item }}</span>
199 // <span>{{ detailInfo[item] }}</span>
200 // </div>
201 // </div>`
202 // await setTimeout(() => {
203 // }, 200);
204 // const outDiv = document.createElement('div');
205
206 // outDiv.className = 'tooltip-main';
207 // outDiv.style.width = '320px';
208
209 // for (const key in detailInfo.value) {
210 // const domRow = document.createElement('div');
211 // domRow.className = 'row';
212 // let span1 = document.createElement('span');
213 // span1.innerHTML = key + ':';
214 // let span2 = document.createElement('span');
215 // span2.innerHTML = detailInfo[key];
216 // domRow.appendChild(span1);
217 // domRow.appendChild(span2);
218 // outDiv.appendChild(domRow);
219 // }
220 // return outDiv;
221
222 // },
223 // });
224
225 const contextMenu = new G6.Menu({
226 getContent(evt: any) {
227 const { item, target } = evt;
228 let model = item._cfg.model;
229 return `
230 <div class='context-menu'> 190 <div class='context-menu'>
231 <span class='menu-item'>引用标准新建数据集</span> 191 <span class='menu-item'>引用标准新建数据集</span>
232 </div>` 192 </div>`
233 }, 193 },
234 shouldBegin: (evt: any) => { 194 shouldBegin: (evt: any) => {
235 const { item, target } = evt; 195 if (props.noContextMenu) {
236 let model = item._cfg.model; 196 return false;
237 if (model && !model.isField && !model.children?.length) { 197 }
238 return true; 198 const { item, target } = evt;
239 } 199 let model = item._cfg.model;
240 return false; 200 if (model && !model.isField && !model.children?.length) {
241 }, 201 return true;
242 handleMenuClick: (target, item: any) => { 202 }
243 let model = item._cfg?.model; 203 if (model.children?.length && model.children[0].isField) {
244 if (!model) { 204 return true;
245 return; 205 }
246 } 206 return false;
247 emits('contextMenu', model); 207 },
248 }, 208 handleMenuClick: (target, item: any) => {
249 // offsetX and offsetY include the padding of the parent container 209 let model = item._cfg?.model;
250 // 需要加上父级容器的 padding-left 16 与自身偏移量 10 210 if (!model) {
251 offsetX: 16, 211 return;
252 // 需要加上父级容器的 padding-top 24 、画布兄弟元素高度、与自身偏移量 10 212 }
253 offsetY: 0, 213 emits('contextMenu', model);
254 // the types of items that allow the menu show up 214 },
255 // 在哪些类型的元素上响应 215 // offsetX and offsetY include the padding of the parent container
256 itemTypes: ['node'], 216 // 需要加上父级容器的 padding-left 16 与自身偏移量 10
257 }); 217 offsetX: 16,
218 // 需要加上父级容器的 padding-top 24 、画布兄弟元素高度、与自身偏移量 10
219 offsetY: 0,
220 // the types of items that allow the menu show up
221 // 在哪些类型的元素上响应
222 itemTypes: ['node'],
223 });
258 224
259 const graph = new G6.TreeGraph({ 225 const graph = new G6.TreeGraph({
260 container: container, 226 container: container,
261 width, 227 width,
262 height, 228 height,
263 plugins: [contextMenu, tooltip.value], 229 plugins: [contextMenu, tooltip.value],
264 fitCenter: true, 230 fitCenter: true,
265 fitView: true, 231 fitView: true,
266 fitViewPadding: 40, 232 fitViewPadding: 40,
267 minZoom: 0.5, 233 minZoom: 0.5,
268 maxZoom: 1, 234 maxZoom: 1,
269 modes: { 235 modes: {
270 default: [ 236 default: [
271 { 237 {
272 type: 'collapse-expand', 238 type: 'collapse-expand',
273 onChange: (item, collapsed) => { 239 onChange: (item, collapsed) => {
274 if (!item) { 240 debugger
275 return; 241 if (!item) {
276 } 242 return;
277 const data = item.getModel(); 243 }
278 data.collapsed = collapsed; 244 const data = item.getModel();
279 return true; 245 data.collapsed = collapsed;
280 }, 246 return true;
281 shouldBegin: (e) => {
282 // 若当前操作的节点 id 为 'node1',则不发生 collapse-expand
283 if (e.item && e.item.getModel().id === 'node1') return false;
284 return true;
285 },
286 },
287 'drag-canvas',
288 'zoom-canvas',
289 ],
290 },
291 defaultNode: {
292 size: 24,
293 anchorPoints: [
294 [0, 0.5],
295 [1, 0.5],
296 ],
297 style: {
298 // stroke: '#4fa1a4',
299 fill: '#ebf6f7',
300 cursor: 'pointer'
301 },
302 },
303 defaultEdge: {
304 type: 'cubic-horizontal',
305 },
306 layout: {
307 type: 'compactBox',
308 direction: 'LR',
309 getId: function getId(d) {
310 return d.id;
311 },
312 getHeight: function getHeight() {
313 return 16;
314 },
315 getWidth: function getWidth() {
316 return 16;
317 },
318 getVGap: function getVGap() {
319 return 30;
320 }, 247 },
321 getHGap: function getHGap() { 248 shouldBegin: (e) => {
322 return 120; 249 debugger
250 // 若当前操作的节点 id 为 'node1',则不发生 collapse-expand
251 if (e.item && e.item.getModel().isLoading == true) return false;
252 return true;
323 }, 253 },
324 }, 254 },
325 }); 255 'drag-canvas',
326 graphRef.value = graph; 256 'zoom-canvas',
327 graph.node((node) => { 257 ],
328 return { 258 },
329 id: node.guid as string, 259 defaultNode: {
330 label: handleLabelLength(node.name as string), 260 size: 24,
331 collapsed: node.children?.length ? false : true, 261 anchorPoints: [
332 labelCfg: { 262 [0, 0.5],
333 offset: 10, 263 [1, 0.5],
334 style: { 264 ],
335 fontSize: 16, 265 style: {
336 fill: '#212121', 266 // stroke: '#4fa1a4',
337 }, 267 fill: '#ebf6f7',
338 position: !node.isField ? 'left' : 'right', //只有字段是最后一层级,不需要展开 268 cursor: 'pointer'
339 }, 269 },
340 style: { 270 },
341 stroke: '#4fa1a4', 271 defaultEdge: {
342 cursor: 'pointer' 272 type: 'cubic-horizontal',
343 } 273 },
344 }; 274 layout: {
345 }); 275 type: 'compactBox',
346 // data不是数组,第一级是根节点 276 direction: 'LR',
347 graph.data(props.treeData); 277 getId: function getId(d) {
348 graph.render(); 278 return d.id;
349 graph.fitView(40, { direction: 'both' }); 279 },
350 graph.fitCenter(); 280 getHeight: function getHeight() {
351 281 return 16;
352 graph.setMinZoom(0.5); 282 },
353 graph.setMaxZoom(5); 283 getWidth: function getWidth() {
354 } 284 return 16;
285 },
286 getVGap: function getVGap() {
287 return 30;
288 },
289 getHGap: function getHGap() {
290 return 120;
291 },
292 },
293 });
294 graphRef.value = graph;
295 graph.node((node) => {
296 return {
297 id: node.guid as string,
298 label: handleLabelLength((node.isField ? node.metaStandardId : node.standardName) as string),
299 collapsed: node.children?.length ? false : true,
300 labelCfg: {
301 offset: 10,
302 style: {
303 fontSize: 16,
304 fill: '#212121',
305 },
306 position: !node.isField ? 'left' : 'right', //只有字段是最后一层级,不需要展开
307 },
308 style: {
309 stroke: '#4fa1a4',
310 cursor: 'pointer'
311 }
312 };
313 });
314 // data不是数组,第一级是根节点
315 props.treeData?.guid && graph.data(props.treeData);
316 graph.render();
317 graph.fitView(40, { direction: 'both' });
318 graph.fitCenter();
355 319
356 observeResize(); 320 graph.setMinZoom(0.5);
321 graph.setMaxZoom(5);
322 bindEvents();
323 observeResize();
324 }
357 325
358 bindEvents(); 326 onMounted(() => {
327 nextTick(() => {
328 if (!graphRef.value && props.treeData?.guid) {
329 initGraph();
330 }
359 }) 331 })
360 332
361 }) 333 })
...@@ -379,6 +351,7 @@ const observeResize = () => { ...@@ -379,6 +351,7 @@ const observeResize = () => {
379 graphRef.value.setMinZoom(0.5); 351 graphRef.value.setMinZoom(0.5);
380 graphRef.value.setMaxZoom(5); 352 graphRef.value.setMaxZoom(5);
381 }, 500) 353 }, 500)
354 return;
382 } 355 }
383 const container: any = containerRef.value; 356 const container: any = containerRef.value;
384 const width = container.clientWidth; 357 const width = container.clientWidth;
...@@ -413,7 +386,8 @@ function updateTooltipPosition(evt) { ...@@ -413,7 +386,8 @@ function updateTooltipPosition(evt) {
413 x: x4 + graphContainer.offsetLeft + offsetX, 386 x: x4 + graphContainer.offsetLeft + offsetX,
414 y: y4 + graphContainer.offsetTop + offsetY 387 y: y4 + graphContainer.offsetTop + offsetY
415 }; 388 };
416 let bboxHeight = tooltip1Ref.value.getBoundingClientRect().height; 389 let bbox = tooltip1Ref.value.getBoundingClientRect();
390 let bboxHeight = bbox.height;
417 if (x4 + 320 + offsetX > width) { 391 if (x4 + 320 + offsetX > width) {
418 res.x -= 320 + offsetX; 392 res.x -= 320 + offsetX;
419 } 393 }
...@@ -432,6 +406,9 @@ const lastSelectNode = ref(null); ...@@ -432,6 +406,9 @@ const lastSelectNode = ref(null);
432 406
433 const bindEvents = () => { 407 const bindEvents = () => {
434 let graph = graphRef.value; 408 let graph = graphRef.value;
409 if (!graph) {
410 return;
411 }
435 graph.on('node:click', function (evt) { 412 graph.on('node:click', function (evt) {
436 const item = evt.item; 413 const item = evt.item;
437 if (!item) { 414 if (!item) {
...@@ -459,10 +436,10 @@ const bindEvents = () => { ...@@ -459,10 +436,10 @@ const bindEvents = () => {
459 lastSelectNode.value = null; 436 lastSelectNode.value = null;
460 } 437 }
461 const children = model.children; 438 const children = model.children;
462 if (children?.length && !model.isField) { //是字段级别,就不需要再展开了 439 if (children?.length && !model.isField) {
463 return; 440 return;
464 } 441 }
465 if (model.isField) { 442 if (model.isField) { //是字段级别,就不需要再展开了
466 graphRef.value.updateItem(item, { 443 graphRef.value.updateItem(item, {
467 labelCfg: { 444 labelCfg: {
468 style: { 445 style: {
...@@ -478,16 +455,23 @@ const bindEvents = () => { ...@@ -478,16 +455,23 @@ const bindEvents = () => {
478 lastSelectNode.value = item; 455 lastSelectNode.value = item;
479 detailInfo.value.guid = model.guid; 456 detailInfo.value.guid = model.guid;
480 updateTooltipPosition(evt); 457 updateTooltipPosition(evt);
481 detailLoading.value = true;
482 458
483 setTimeout(() => { 459 detailLoading.value = true;
484 detailLoading.value = false; 460 getMetaStandardFieldDetail(model.guid).then((res: any) => {
485 }, 500); 461 if (res?.code == proxy.$passCode) {
462 detailInfo.value = res.data?.metaStandardValue || {};
463 detailInfoLabel.value = model.label;
464 detailLoading.value = false;
465 } else {
466 ElMessage.error(res.msg);
467 }
468 })
486 return; 469 return;
487 } 470 }
488 emits('nodeItemClick', graph, item);
489 evt.preventDefault(); 471 evt.preventDefault();
490 evt.stopPropagation(); 472 evt.stopPropagation();
473 model.isLoading = true;
474 emits('nodeItemClick', graph, item);
491 }); 475 });
492 graph.on('dragstart', (evt: any) => { 476 graph.on('dragstart', (evt: any) => {
493 if (evt.item?.getType() == 'node') { 477 if (evt.item?.getType() == 'node') {
...@@ -591,7 +575,8 @@ defineExpose({ ...@@ -591,7 +575,8 @@ defineExpose({
591 padding: 16px; 575 padding: 16px;
592 box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.08); 576 box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.08);
593 background-color: #fff; 577 background-color: #fff;
594 max-width: 320px; 578 width: 320px;
579 min-height: 100px;
595 max-height: 400px; 580 max-height: 400px;
596 overflow-y: auto; 581 overflow-y: auto;
597 582
......
...@@ -263,6 +263,22 @@ const routes: RouteRecordRaw[] = [ ...@@ -263,6 +263,22 @@ const routes: RouteRecordRaw[] = [
263 } 263 }
264 }, 264 },
265 { 265 {
266 path: 'standard-query-view',
267 name: 'metadataStandardQueryView',
268 component: () => import('@/views/data_meta/standard-query-view.vue'),
269 meta: {
270 title: '元数据标准查看',
271 breadcrumb: false,
272 cache: true,
273 activeMenu: '/data-meta/metadata-standard'
274 },
275 beforeEnter: (to, from) => {
276 if (to.query.name) {
277 to.meta.title = `元数据标准查看-${to.query.name}`;
278 }
279 }
280 },
281 {
266 path: 'standard-codetable', 282 path: 'standard-codetable',
267 name: 'metadataStandardCodetable', 283 name: 'metadataStandardCodetable',
268 component: () => import('@/views/data_meta/standard-codetable.vue'), 284 component: () => import('@/views/data_meta/standard-codetable.vue'),
......
...@@ -80,15 +80,23 @@ const containerRef = ref(); ...@@ -80,15 +80,23 @@ const containerRef = ref();
80 const setChartsOption = () => { 80 const setChartsOption = () => {
81 let option = { 81 let option = {
82 tooltip: { 82 tooltip: {
83 trigger: 'item' 83 trigger: 'item',
84 formatter: (params) => {
85 if (params.data.name) {
86 return null;
87 }
88 return params.data.source + ' --> ' + params.data.target
89 }
84 }, 90 },
91 color: ["#3DBCBE", "#6b67d1", "#7BBCE0", "#2B8EF3", "#51dca2", "#E19D46"],
85 series: [ 92 series: [
86 { 93 {
87 type: 'sankey', 94 type: 'sankey',
88 top: 80, 95 top: 80,
89 bottom: 40, 96 bottom: 40,
90 left: 50, 97 draggable: false,
91 right: 50, 98 left: 40,
99 right: 80,
92 data: props.names, 100 data: props.names,
93 links: props.treeData, 101 links: props.treeData,
94 lineStyle: { 102 lineStyle: {
......
1 <route lang="yaml">
2 name: metadataStandardQueryView
3 </route>
4
5 <template>
6 <div class="main_wrap" v-loading="graphDataLoading">
7 <div className='g6-component-topbar'>
8 <graphTopbar ref="topBarRef" @displaySwitchChange="displaySwitchChange" :isGraphDisplay="isGraphDisplay" />
9 </div>
10 <RelationNetwork v-show="graphTreeData?.guid && isGraphDisplay" ref="relationNetworkRef" :tree-data="graphTreeData" :noContextMenu="true"
11 @nodeItemClick="handleNodeItemClick">
12 </RelationNetwork>
13 <Sankey v-show="!isGraphDisplay && (sankeyNames?.length || sankeyDataLoading)" v-loading="sankeyDataLoading"
14 :tree-data="sankeyData" :names="sankeyNames">
15 </Sankey>
16 <div v-show="(isGraphDisplay ? !graphTreeData?.guid : (!sankeyDataLoading && !sankeyNames?.length))"
17 class="main-placeholder">
18 <img src="../../assets/images/no-data.png" :style="{ width: '96px', height: '96px' }" />
19 <div class="empty-text">暂无数据</div>
20 </div>
21 </div>
22 </template>
23
24 <script lang="ts" setup name="metadataStandardQueryView">
25 import {
26 getMetaStandardField,
27 getSankeyData,
28 getMetaStandardTreeList
29 } from '@/api/modules/dataMetaService';
30 import Sankey from './components/Sankey.vue';
31 import { ElMessage } from 'element-plus';
32
33 const router = useRouter();
34 const route = useRoute()
35
36 const metaGuid = ref(route.query.guid);
37
38 const { proxy } = getCurrentInstance() as any;
39
40 const relationNetworkRef = ref();
41
42 const graphDataLoading = ref(false);
43
44 /** 关系网树形数据 */
45 const graphTreeData: any = ref({});
46
47 onBeforeUnmount(() => {
48 relationNetworkRef.value.destroy();
49 })
50
51 const sankeyDataLoading = ref(false);
52
53 const sankeyData: any = ref([]);
54
55 const sankeyNames: any = ref([]);
56
57 const isGraphDisplay = ref(true);
58
59 const displaySwitchChange = (val) => {
60 if (val == isGraphDisplay.value) {
61 return;
62 }
63 isGraphDisplay.value = val;
64 if (!val) {
65 getSankeyDataList();
66 }
67 }
68
69 const getSankeyDataList = () => {
70 sankeyDataLoading.value = true;
71 getSankeyData(metaGuid.value).then((res: any) => {
72 sankeyDataLoading.value = false;
73 if (res?.code == proxy.$passCode) {
74 sankeyData.value = res.data?.links || [];
75 sankeyNames.value = res.data?.data || [];
76 } else {
77 ElMessage.error(res.msg);
78 }
79 })
80 }
81
82 const handleNodeItemClick = (graph, nodeItem) => {
83 const nodeId = nodeItem.get('id');
84 const parentData = graph.findDataById(nodeId);
85 if (!parentData.children) {
86 parentData.children = [];
87 }
88 if (parentData.isHaveData == 'N') {
89 ElMessage.warning('没有可展开的下级字段');
90 return;
91 }
92 graphDataLoading.value = true;
93 getMetaStandardField(nodeId).then((res: any) => {
94 graphDataLoading.value = false;
95 if (res?.code == proxy.$passCode) {
96 const data = res.data || [];
97 parentData.children = [];
98 if (!data?.length) {
99 parentData.isHaveData = 'N';
100 ElMessage.warning('没有可展开的下级字段');
101 return;
102 }
103 data.forEach(d => {
104 parentData.children.push(d);
105 })
106 parentData.collapsed = false;
107 setTimeout(() => {
108 parentData.isLoading = false;
109 graph.setMaxZoom(1);
110 graph.changeData(graphTreeData.value);
111 graph.setMaxZoom(5);
112 }, 100);
113 } else {
114 parentData.isLoading = false;
115 ElMessage.error(res.msg);
116 }
117 })
118 }
119
120 onBeforeMount(() => {
121 graphDataLoading.value = true
122 getMetaStandardTreeList(metaGuid.value).then((res: any) => {
123 graphDataLoading.value = false;
124 if (res?.code == proxy.$passCode) {
125 const data = res.data || [];
126 graphTreeData.value = data?.[0] || {};
127 } else {
128 ElMessage.error(res.msg);
129 }
130 })
131 })
132
133 </script>
134
135 <style lang="scss" scoped>
136 .main_wrap {
137 height: 100%;
138 width: 100%;
139 position: relative;
140
141 :deep(.canvas-wrapper) {
142 background-color: #f7f7f9;
143 }
144
145 .main-placeholder {
146 height: 100%;
147 display: flex;
148 justify-content: center;
149 align-items: center;
150 flex-direction: column;
151
152 .empty-text {
153 font-size: 14px;
154 color: #b2b2b2;
155 }
156 }
157 }
158
159 .g6-component-topbar {
160 position: absolute;
161 left: 24px;
162 bottom: unset;
163 top: 14px;
164 padding: 0;
165 text-align: center;
166 z-index: 999;
167 }
168 </style>
...\ No newline at end of file ...\ No newline at end of file
...@@ -9,16 +9,17 @@ import Sankey from './components/Sankey.vue'; ...@@ -9,16 +9,17 @@ import Sankey from './components/Sankey.vue';
9 import Tree from '@/components/Tree/index.vue'; 9 import Tree from '@/components/Tree/index.vue';
10 import RelationNetwork from '@/components/RelationNetwork/index.vue'; 10 import RelationNetwork from '@/components/RelationNetwork/index.vue';
11 import { 11 import {
12 getMetaStandardTreeList,
13 getMetaStandardField,
14 getSankeyData
12 } from '@/api/modules/dataMetaService'; 15 } from '@/api/modules/dataMetaService';
13
14 import { useRouter, useRoute } from "vue-router"; 16 import { useRouter, useRoute } from "vue-router";
15 import useDataMetaStore from "@/store/modules/dataMeta" 17 import useDataMetaStore from "@/store/modules/dataMeta"
18 import { cloneDeep } from 'lodash-es';
16 19
17 const router = useRouter(); 20 const router = useRouter();
18 const route = useRoute() 21 const route = useRoute()
19 22
20 const switchGraphDisplay = ref(true); //true表示关系网,false表示桑基图
21
22 const { proxy } = getCurrentInstance() as any; 23 const { proxy } = getCurrentInstance() as any;
23 24
24 const relationNetworkRef = ref(); 25 const relationNetworkRef = ref();
...@@ -27,14 +28,14 @@ const treeInfo = ref({ ...@@ -27,14 +28,14 @@ const treeInfo = ref({
27 id: "data-meta-standard-tree", 28 id: "data-meta-standard-tree",
28 filter: true, 29 filter: true,
29 queryValue: "", 30 queryValue: "",
30 queryPlaceholder: "输入库/表名称搜索", 31 queryPlaceholder: "请输入关键字搜索",
31 props: { 32 props: {
32 label: "name", 33 label: "standardName",
33 value: "guid", 34 value: "guid",
34 isLeaf: "isLeaf", 35 isLeaf: "isLeaf",
35 }, 36 },
36 nodeKey: 'guid', 37 nodeKey: 'guid',
37 expandedKey: ['1'], 38 expandedKey: [],
38 currentNodeKey: '', 39 currentNodeKey: '',
39 expandOnNodeClick: false, 40 expandOnNodeClick: false,
40 data: <any>[], 41 data: <any>[],
...@@ -44,51 +45,20 @@ const treeInfo = ref({ ...@@ -44,51 +45,20 @@ const treeInfo = ref({
44 /** 获取左侧树数据. */ 45 /** 获取左侧树数据. */
45 const getTreeData = async () => { 46 const getTreeData = async () => {
46 treeInfo.value.loading = true 47 treeInfo.value.loading = true
47 // let params = {} 48 getMetaStandardTreeList('').then((res: any) => {
48 // const res: any = await getMetaTreeData(params) 49 treeInfo.value.loading = false;
49 // if (res.code == proxy.$passCode) { 50 if (res?.code == proxy.$passCode) {
50 // const data = res.data || []; 51 const data = res.data || [];
51 // let treeData: any = Object.keys(data).length ? [data] : []; 52 treeInfo.value.data = data;
52 // treeInfo.value.data = treeData; 53 if (data.length) {
53 // allTreeData.value = treeData; 54 treeInfo.value.currentNodeKey = data[0].guid;
54 // if (treeData.length) { 55 treeInfo.value.expandedKey = <any>[data[0].guid];
55 // treeInfo.value.currentNodeKey = treeData[0].guid; 56 nodeClick(treeInfo.value.data[0])
56 // treeInfo.value.expandedKey = <any>[treeData[0].guid]; 57 }
57 // } 58 } else {
58 // } else { 59 ElMessage.error(res.msg);
59 // ElMessage.error(res.msg); 60 }
60 // } 61 })
61 treeInfo.value.data = [{
62 guid: '1',
63 name: '第一级1',
64 children: [{
65 guid: '1-1',
66 name: '第二级1-1',
67 children: [{
68 guid: '1-1-1',
69 name: '第三级1-1-1'
70 }]
71 }, {
72 guid: '1-2',
73 name: '第二级1-2'
74 }, {
75 guid: '1-3',
76 name: '第二级1-3'
77 }]
78 }, {
79 guid: '2',
80 name: '第一级2',
81 children: [{
82 guid: '2-1',
83 name: '第二级1-1'
84 }, {
85 guid: '2-2',
86 name: '第二级1-2'
87 }]
88 }];
89 treeInfo.value.loading = false
90 treeInfo.value.currentNodeKey = '1';
91 nodeClick(treeInfo.value.data[0])
92 } 62 }
93 /** 左侧树的的组件引用. */ 63 /** 左侧树的的组件引用. */
94 const treeInfoRef = ref(); 64 const treeInfoRef = ref();
...@@ -98,22 +68,18 @@ const lastClickNode: any = ref({}); ...@@ -98,22 +68,18 @@ const lastClickNode: any = ref({});
98 68
99 const treeDataLoading = ref(false); 69 const treeDataLoading = ref(false);
100 70
101 /** 数据血缘关系图组件 */
102 const lineageGraph: any = ref();
103
104 /** 点击左侧树节点,更新对应的血缘关系图. */ 71 /** 点击左侧树节点,更新对应的血缘关系图. */
105 const nodeClick = (data) => { 72 const nodeClick = (data) => {
106 console.log(data);
107 const ele = <HTMLElement>document.querySelector(".g6-component-contextmenu") 73 const ele = <HTMLElement>document.querySelector(".g6-component-contextmenu")
108 if (ele) { 74 if (ele) {
109 ele.style.display = "none" 75 ele.style.display = "none"
110 } 76 }
111 nextTick(() => {
112 lineageGraph.value?.tooltip1.hide()
113 })
114 treeInfo.value.currentNodeKey = data.guid; 77 treeInfo.value.currentNodeKey = data.guid;
115 treeInfo.value.expandedKey = <any>[data.guid]; 78 treeInfo.value.expandedKey = <any>[data.guid];
116 lastClickNode.value = data; 79 lastClickNode.value = cloneDeep(data);
80 if (!isGraphDisplay.value) {
81 getSankeyDataList();
82 }
117 } 83 }
118 84
119 /** 选中树节点后自动滚动到可视范围内. */ 85 /** 选中树节点后自动滚动到可视范围内. */
...@@ -145,7 +111,6 @@ const processRouter = () => { ...@@ -145,7 +111,6 @@ const processRouter = () => {
145 treeInfo.value.expandedKey = <any>[databaseGuid, guid]; 111 treeInfo.value.expandedKey = <any>[databaseGuid, guid];
146 lastClickNode.value = { guid: guid, tableName, databaseName, type: 3, databaseChName }; 112 lastClickNode.value = { guid: guid, tableName, databaseName, type: 3, databaseChName };
147 scrollToNode(guid); 113 scrollToNode(guid);
148 switchGraphDisplay.value = isFL;
149 // if (isFL) { 114 // if (isFL) {
150 // getAllTableFieldLineageMap(); 115 // getAllTableFieldLineageMap();
151 // } else { 116 // } else {
...@@ -156,20 +121,45 @@ const processRouter = () => { ...@@ -156,20 +121,45 @@ const processRouter = () => {
156 } 121 }
157 122
158 onActivated(() => { 123 onActivated(() => {
159 processRouter(); 124 //processRouter();
160 }); 125 });
161 126
162 onBeforeMount(async () => { 127 onBeforeMount(async () => {
163 await getTreeData() 128 await getTreeData()
164 processRouter(); 129 // processRouter();
165 }) 130 })
166 131
167 onMounted(() => { }) 132 onMounted(() => { })
168 133
134 const sankeyDataLoading = ref(false);
135
136 const sankeyData: any = ref([]);
137
138 const sankeyNames: any = ref([]);
139
169 const isGraphDisplay = ref(true); 140 const isGraphDisplay = ref(true);
170 141
171 const displaySwitchChange = (val) => { 142 const displaySwitchChange = (val) => {
143 if (val == isGraphDisplay.value) {
144 return;
145 }
172 isGraphDisplay.value = val; 146 isGraphDisplay.value = val;
147 if (!val) {
148 getSankeyDataList();
149 }
150 }
151
152 const getSankeyDataList = () => {
153 sankeyDataLoading.value = true;
154 getSankeyData(lastClickNode.value.guid).then((res: any) => {
155 sankeyDataLoading.value = false;
156 if (res?.code == proxy.$passCode) {
157 sankeyData.value = res.data?.links || [];
158 sankeyNames.value = res.data?.data || [];
159 } else {
160 ElMessage.error(res.msg);
161 }
162 })
173 } 163 }
174 164
175 const handleNodeItemClick = (graph, nodeItem) => { 165 const handleNodeItemClick = (graph, nodeItem) => {
...@@ -178,35 +168,41 @@ const handleNodeItemClick = (graph, nodeItem) => { ...@@ -178,35 +168,41 @@ const handleNodeItemClick = (graph, nodeItem) => {
178 if (!parentData.children) { 168 if (!parentData.children) {
179 parentData.children = []; 169 parentData.children = [];
180 } 170 }
171 if (parentData.noData) {
172 ElMessage.warning('没有可展开的下级字段');
173 return;
174 }
181 treeDataLoading.value = true; 175 treeDataLoading.value = true;
182 let childData = [{ 176 getMetaStandardField(nodeId).then((res: any) => {
183 guid: '33',
184 isField: true,
185 name: '字段1'
186 }, {
187 guid: '44',
188 isField: true,
189 name: '字段2字段2字段2字段2字段2字段2字段2字段2字段2xx2字段22字段22字段22字段22字段22字段2'
190 }, {
191 guid: '55',
192 isField: true,
193 name: '字段3'
194 }]
195 // parentData.collapsed = true;
196 // nodeItem.getModel().collapsed = true;
197 parentData.children = childData;
198 setTimeout(() => {
199 treeDataLoading.value = false; 177 treeDataLoading.value = false;
200 graph.changeData(); 178 if (res?.code == proxy.$passCode) {
201 // parentData.collapsed = true; 179 const data = res.data || [];
202 // graph.updateChildren(childData, parentData.id); 180 parentData.children = [];
203 // graph.changeData(lastClickNode.value); 181 if (!data?.length) {
204 //graph.data(lastClickNode.value); 182 parentData.noData = true;
205 }, 2000) 183 ElMessage.warning('没有可展开的下级字段');
184 return;
185 }
186 data.forEach(d => {
187 parentData.children.push(d);
188 })
189 parentData.collapsed = false;
190 setTimeout(() => {
191 parentData.isLoading = false;
192 graph.setMaxZoom(1);
193 graph.changeData(lastClickNode.value);
194 graph.setMaxZoom(5);
195 }, 100);
196 } else {
197 parentData.isLoading = false;
198 ElMessage.error(res.msg);
199 }
200 })
206 } 201 }
207 202
208 const handleContextMenu = (nodeData) => { 203 const handleContextMenu = (nodeData) => {
209 //TODO,新建引用数据集 204 //TODO,新建引用数据集
205 window.open('');
210 } 206 }
211 207
212 onBeforeUnmount(() => { 208 onBeforeUnmount(() => {
...@@ -218,21 +214,23 @@ onBeforeUnmount(() => { ...@@ -218,21 +214,23 @@ onBeforeUnmount(() => {
218 <template> 214 <template>
219 <div class="container_wrap full flex"> 215 <div class="container_wrap full flex">
220 <div class="aside_wrap"> 216 <div class="aside_wrap">
221 <div class="aside_title">数据库目录列表</div> 217 <div class="aside_title">元数据标准列表</div>
222 <Tree ref="treeInfoRef" :treeInfo="treeInfo" @nodeClick="nodeClick" /> 218 <Tree ref="treeInfoRef" :treeInfo="treeInfo" @nodeClick="nodeClick" />
223 </div> 219 </div>
224 <div class="main_wrap"> 220 <div class="main_wrap">
225 <div className='g6-component-topbar'> 221 <div className='g6-component-topbar'>
226 <graphTopbar ref="topBarRef" @displaySwitchChange="displaySwitchChange" :isGraphDisplay="isGraphDisplay" /> 222 <graphTopbar ref="topBarRef" @displaySwitchChange="displaySwitchChange" :isGraphDisplay="isGraphDisplay" />
227 </div> 223 </div>
228 <RelationNetwork v-show="isGraphDisplay" ref="relationNetworkRef" :tree-data="lastClickNode" 224 <RelationNetwork v-show="lastClickNode?.guid && isGraphDisplay" ref="relationNetworkRef"
229 v-loading="treeDataLoading" @nodeItemClick="handleNodeItemClick" @contextMenu="handleContextMenu"> 225 :tree-data="lastClickNode" v-loading="treeDataLoading" @nodeItemClick="handleNodeItemClick"
226 @contextMenu="handleContextMenu">
230 </RelationNetwork> 227 </RelationNetwork>
231 <Sankey v-show="!isGraphDisplay"></Sankey> 228 <Sankey v-show="lastClickNode?.guid && !isGraphDisplay && (sankeyNames?.length || sankeyDataLoading)" v-loading="sankeyDataLoading" :tree-data="sankeyData" :names="sankeyNames">
232 <!-- <div v-show="lastClickNode && lastClickNode?.type !== 3 && lastClickNode?.type !== 4" class="main-placeholder"> 229 </Sankey>
230 <div v-show="!lastClickNode?.guid && !treeInfo.data?.length || (!isGraphDisplay && !sankeyDataLoading && !sankeyNames?.length)" class="main-placeholder">
233 <img src="../../assets/images/no-data.png" :style="{ width: '96px', height: '96px' }" /> 231 <img src="../../assets/images/no-data.png" :style="{ width: '96px', height: '96px' }" />
234 <div class="empty-text">暂无标准数据</div> 232 <div class="empty-text">暂无数据</div>
235 </div> --> 233 </div>
236 </div> 234 </div>
237 </div> 235 </div>
238 </template> 236 </template>
......
...@@ -416,6 +416,17 @@ function exportData () { ...@@ -416,6 +416,17 @@ function exportData () {
416 onBeforeMount(() => { 416 onBeforeMount(() => {
417 getTree() 417 getTree()
418 }) 418 })
419
420 const viewGraph = () => {
421 router.push({
422 name: 'metadataStandardQueryView',
423 query: {
424 guid: treeInfo.value.currentObj?.guid,
425 name: treeInfo.value.currentObj?.standardName,
426 }
427 });
428 }
429
419 </script> 430 </script>
420 431
421 <template> 432 <template>
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!