添加关系网和桑基图
Showing
6 changed files
with
1127 additions
and
5 deletions
| ... | @@ -27,6 +27,7 @@ declare module '@vue/runtime-core' { | ... | @@ -27,6 +27,7 @@ declare module '@vue/runtime-core' { |
| 27 | FileUpload: typeof import('./src/components/FileUpload/index.vue')['default'] | 27 | FileUpload: typeof import('./src/components/FileUpload/index.vue')['default'] |
| 28 | FixedActionBar: typeof import('./src/components/FixedActionBar/index.vue')['default'] | 28 | FixedActionBar: typeof import('./src/components/FixedActionBar/index.vue')['default'] |
| 29 | Form: typeof import('./src/components/Form/index.vue')['default'] | 29 | Form: typeof import('./src/components/Form/index.vue')['default'] |
| 30 | GraphTopbar: typeof import('./src/components/RelationNetwork/graphTopbar.vue')['default'] | ||
| 30 | Hour: typeof import('./src/components/Schedule/component/hour.vue')['default'] | 31 | Hour: typeof import('./src/components/Schedule/component/hour.vue')['default'] |
| 31 | ImagePreview: typeof import('./src/components/ImagePreview/index.vue')['default'] | 32 | ImagePreview: typeof import('./src/components/ImagePreview/index.vue')['default'] |
| 32 | ImagesUpload: typeof import('./src/components/ImagesUpload/index.vue')['default'] | 33 | ImagesUpload: typeof import('./src/components/ImagesUpload/index.vue')['default'] |
| ... | @@ -41,6 +42,7 @@ declare module '@vue/runtime-core' { | ... | @@ -41,6 +42,7 @@ declare module '@vue/runtime-core' { |
| 41 | PageNav: typeof import('./src/components/PageNav/index.vue')['default'] | 42 | PageNav: typeof import('./src/components/PageNav/index.vue')['default'] |
| 42 | PcasCascader: typeof import('./src/components/PcasCascader/index.vue')['default'] | 43 | PcasCascader: typeof import('./src/components/PcasCascader/index.vue')['default'] |
| 43 | Popover: typeof import('./src/components/Popover/index.vue')['default'] | 44 | Popover: typeof import('./src/components/Popover/index.vue')['default'] |
| 45 | RelationNetwork: typeof import('./src/components/RelationNetwork/index.vue')['default'] | ||
| 44 | RouterLink: typeof import('vue-router')['RouterLink'] | 46 | RouterLink: typeof import('vue-router')['RouterLink'] |
| 45 | RouterView: typeof import('vue-router')['RouterView'] | 47 | RouterView: typeof import('vue-router')['RouterView'] |
| 46 | Schedule: typeof import('./src/components/Schedule/index.vue')['default'] | 48 | Schedule: typeof import('./src/components/Schedule/index.vue')['default'] | ... | ... |
| 1 | <script lang="ts" setup name="topbar"> | ||
| 2 | import { ref, watch } from 'vue'; | ||
| 3 | |||
| 4 | const props = defineProps({ | ||
| 5 | isGraphDisplay: { | ||
| 6 | type: Boolean, | ||
| 7 | default: true | ||
| 8 | }, | ||
| 9 | }); | ||
| 10 | |||
| 11 | const isGraph = ref(false); | ||
| 12 | |||
| 13 | watch(() => props.isGraphDisplay, (val) => { | ||
| 14 | isGraph.value = val; | ||
| 15 | }, { | ||
| 16 | immediate: true | ||
| 17 | }) | ||
| 18 | |||
| 19 | const emits = defineEmits(["displaySwitchChange"]); | ||
| 20 | |||
| 21 | const switchChange = (val) => { | ||
| 22 | isGraph.value = val | ||
| 23 | emits('displaySwitchChange', val); | ||
| 24 | } | ||
| 25 | |||
| 26 | </script> | ||
| 27 | |||
| 28 | <template> | ||
| 29 | <div className='g6-component-topbar-content'> | ||
| 30 | <div :class="isGraph ? 'selected g6-component-topbar-item' : 'g6-component-topbar-item'" @click="switchChange(true)"> | ||
| 31 | 关系网 | ||
| 32 | </div> | ||
| 33 | <div :class="!isGraph ? 'selected g6-component-topbar-item' : 'g6-component-topbar-item'" @click="switchChange(false)"> | ||
| 34 | 桑基图 | ||
| 35 | </div> | ||
| 36 | </div> | ||
| 37 | </template> | ||
| 38 | |||
| 39 | <style scoped lang="scss"> | ||
| 40 | .g6-component-topbar-content { | ||
| 41 | display: flex; | ||
| 42 | flex-direction: row; | ||
| 43 | justify-content: center; | ||
| 44 | align-items: center; | ||
| 45 | background: #fff; | ||
| 46 | border: 1px solid var(--el-color-primary); | ||
| 47 | border-radius: 32px; | ||
| 48 | padding: 4px; | ||
| 49 | width: 138px; | ||
| 50 | height: 32px; | ||
| 51 | } | ||
| 52 | |||
| 53 | .g6-component-topbar-item { | ||
| 54 | width: 50%; | ||
| 55 | height: 100%; | ||
| 56 | display: flex; | ||
| 57 | align-items: center; | ||
| 58 | justify-content: center; | ||
| 59 | font-size: 14px; | ||
| 60 | color: #999999; | ||
| 61 | cursor: pointer; | ||
| 62 | |||
| 63 | &.selected { | ||
| 64 | background: #4FA1A4; | ||
| 65 | border-radius: 32px; | ||
| 66 | color: #fff; | ||
| 67 | } | ||
| 68 | } | ||
| 69 | </style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/components/RelationNetwork/index.vue
0 → 100644
| 1 | <template> | ||
| 2 | <div ref="containerRef" className='canvas-wrapper'> | ||
| 3 | <div class="main" ref="tooltip1Ref" style="display: none;position: absolute;" v-loading="detailLoading"> | ||
| 4 | <div class="title">{{ detailInfo.name }}</div> | ||
| 5 | <div class="row" v-for="item in Object.keys(detailInfo)"> | ||
| 6 | <span>{{ item + ':' }}</span> | ||
| 7 | <span>{{ detailInfo[item] }}</span> | ||
| 8 | </div> | ||
| 9 | </div> | ||
| 10 | </div> | ||
| 11 | </template> | ||
| 12 | |||
| 13 | <script lang="ts" setup name="RelationNetwork"> | ||
| 14 | import { ref, onMounted } from 'vue' | ||
| 15 | import G6 from '@antv/g6'; | ||
| 16 | import insertCss from 'insert-css'; | ||
| 17 | |||
| 18 | const props = defineProps({ | ||
| 19 | treeData: { | ||
| 20 | type: Object, | ||
| 21 | default: {} | ||
| 22 | } | ||
| 23 | }) | ||
| 24 | |||
| 25 | const emits = defineEmits([ | ||
| 26 | 'nodeItemClick', | ||
| 27 | 'contextMenu' | ||
| 28 | ]); | ||
| 29 | |||
| 30 | const detailLoading = ref(false); | ||
| 31 | |||
| 32 | const detailInfo: any = ref({ | ||
| 33 | guid: '1', | ||
| 34 | '标识符': '124', | ||
| 35 | name: '字段12345字段字段字段字段字段字段字段字段字段字段', | ||
| 36 | '中文名称': '字段12345字段字段字段字段字段字段字段字段字段字段', | ||
| 37 | '英文名称': 'fieldName' | ||
| 38 | }); | ||
| 39 | |||
| 40 | const containerRef = ref(); | ||
| 41 | |||
| 42 | const graphRef = ref(); | ||
| 43 | |||
| 44 | const resizeObserver = ref(); | ||
| 45 | |||
| 46 | watch(() => props.treeData, (val) => { | ||
| 47 | tooltip1Ref.value.style.display = 'none'; | ||
| 48 | if (lastSelectNode.value) { | ||
| 49 | graphRef.value.updateItem(lastSelectNode.value, { | ||
| 50 | labelCfg: { | ||
| 51 | color: '#212121' | ||
| 52 | }, | ||
| 53 | style: { | ||
| 54 | stroke: '#4fa1a4', | ||
| 55 | fill: '#ebf6f7', | ||
| 56 | cursor: 'pointer' | ||
| 57 | } | ||
| 58 | }); | ||
| 59 | } | ||
| 60 | lastSelectNode.value = null; | ||
| 61 | if (val) { | ||
| 62 | renderGraph(graphRef.value, val); | ||
| 63 | } | ||
| 64 | }) | ||
| 65 | |||
| 66 | const renderGraph = (graph: any, lineageData: any) => { | ||
| 67 | if (!graph || !lineageData) return; | ||
| 68 | graph.setMinZoom(0.5); | ||
| 69 | graph.setMaxZoom(1); | ||
| 70 | graph.data(lineageData); | ||
| 71 | graph.render(); | ||
| 72 | |||
| 73 | graph.fitView(40, { direction: 'both' }); | ||
| 74 | graph.fitCenter(); | ||
| 75 | |||
| 76 | graph.setMinZoom(0.5); | ||
| 77 | graph.setMaxZoom(5); | ||
| 78 | |||
| 79 | }; | ||
| 80 | |||
| 81 | const detectLanguage = (text) => { | ||
| 82 | if (!text) { | ||
| 83 | return 'English'; | ||
| 84 | } | ||
| 85 | let chineseCount = 0; | ||
| 86 | let englishCount = 0; | ||
| 87 | |||
| 88 | for (let char of text) { | ||
| 89 | if (/[\u4e00-\u9fa5]/.test(char)) { | ||
| 90 | chineseCount++; | ||
| 91 | } else if (/[a-zA-Z]/.test(char)) { | ||
| 92 | englishCount++; | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | if (chineseCount > englishCount) { | ||
| 97 | return 'Chinese'; | ||
| 98 | } else if (englishCount > chineseCount) { | ||
| 99 | return 'English'; | ||
| 100 | } | ||
| 101 | return 'English'; | ||
| 102 | } | ||
| 103 | |||
| 104 | const handleLabelLength = (label: string) => { | ||
| 105 | if (detectLanguage(label) == 'English') { | ||
| 106 | return label?.length > 30 ? label.slice(0, 30) + '...' : label; | ||
| 107 | } | ||
| 108 | return label?.length > 16 ? label.slice(0, 16) + '...' : label; | ||
| 109 | }; | ||
| 110 | |||
| 111 | insertCss(` | ||
| 112 | .g6-component-contextmenu { | ||
| 113 | padding: 8px 0px; | ||
| 114 | background-color: #fff; | ||
| 115 | } | ||
| 116 | .context-menu { | ||
| 117 | display: flex; | ||
| 118 | flex-direction: column; | ||
| 119 | } | ||
| 120 | .menu-item { | ||
| 121 | line-height: 32px; | ||
| 122 | color: #212121; | ||
| 123 | cursor: pointer; | ||
| 124 | padding: 0 12px; | ||
| 125 | } | ||
| 126 | .menu-item:hover { | ||
| 127 | background-color: #f5f5f5; | ||
| 128 | } | ||
| 129 | `); | ||
| 130 | |||
| 131 | const tooltip = ref(); | ||
| 132 | |||
| 133 | onMounted(() => { | ||
| 134 | nextTick(() => { | ||
| 135 | if (!graphRef.value) { | ||
| 136 | const container: any = containerRef.value; | ||
| 137 | const width = container.clientWidth; | ||
| 138 | const height = container.clientHeight - 10; | ||
| 139 | |||
| 140 | tooltip.value = new G6.Tooltip({ | ||
| 141 | offsetX: 10, | ||
| 142 | offsetY: 10, | ||
| 143 | trigger: 'mouseenter', | ||
| 144 | // 允许出现 tooltip 的 item 类型 | ||
| 145 | itemTypes: ['node'], | ||
| 146 | // 自定义 tooltip 内容 | ||
| 147 | shouldBegin: (evt: any) => { | ||
| 148 | const { item, target } = evt; | ||
| 149 | const currentAnchor = target.get('name'); | ||
| 150 | const name = item._cfg.model?.name; | ||
| 151 | if (currentAnchor == 'text-shape') { | ||
| 152 | if (detectLanguage(name) == 'English') { | ||
| 153 | return name?.length > 30; | ||
| 154 | } | ||
| 155 | return name?.length > 16; | ||
| 156 | } | ||
| 157 | return false; | ||
| 158 | }, | ||
| 159 | getContent: (e: any) => { | ||
| 160 | |||
| 161 | const { item, target } = e; | ||
| 162 | const currentAnchor = target.get('name'); | ||
| 163 | const outDiv = document.createElement('div'); | ||
| 164 | outDiv.className = 'node'; | ||
| 165 | outDiv.style.width = 'fit-content'; | ||
| 166 | const name = item._cfg.model.name; | ||
| 167 | if (currentAnchor == 'text-shape') { | ||
| 168 | outDiv.innerHTML = `<h4>${name}</h4>` | ||
| 169 | } | ||
| 170 | return outDiv; | ||
| 171 | }, | ||
| 172 | }); | ||
| 173 | |||
| 174 | // tooltip1.value = new G6.Tooltip({ | ||
| 175 | // offsetX: 10, | ||
| 176 | // offsetY: 10, | ||
| 177 | // trigger: 'click', | ||
| 178 | // // 允许出现 tooltip 的 item 类型 | ||
| 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'> | ||
| 231 | <span class='menu-item'>引用标准新建数据集</span> | ||
| 232 | </div>` | ||
| 233 | }, | ||
| 234 | shouldBegin: (evt: any) => { | ||
| 235 | const { item, target } = evt; | ||
| 236 | let model = item._cfg.model; | ||
| 237 | if (model && !model.isField && !model.children?.length) { | ||
| 238 | return true; | ||
| 239 | } | ||
| 240 | return false; | ||
| 241 | }, | ||
| 242 | handleMenuClick: (target, item: any) => { | ||
| 243 | let model = item._cfg?.model; | ||
| 244 | if (!model) { | ||
| 245 | return; | ||
| 246 | } | ||
| 247 | emits('contextMenu', model); | ||
| 248 | }, | ||
| 249 | // offsetX and offsetY include the padding of the parent container | ||
| 250 | // 需要加上父级容器的 padding-left 16 与自身偏移量 10 | ||
| 251 | offsetX: 16, | ||
| 252 | // 需要加上父级容器的 padding-top 24 、画布兄弟元素高度、与自身偏移量 10 | ||
| 253 | offsetY: 0, | ||
| 254 | // the types of items that allow the menu show up | ||
| 255 | // 在哪些类型的元素上响应 | ||
| 256 | itemTypes: ['node'], | ||
| 257 | }); | ||
| 258 | |||
| 259 | const graph = new G6.TreeGraph({ | ||
| 260 | container: container, | ||
| 261 | width, | ||
| 262 | height, | ||
| 263 | plugins: [contextMenu, tooltip.value], | ||
| 264 | fitCenter: true, | ||
| 265 | fitView: true, | ||
| 266 | fitViewPadding: 40, | ||
| 267 | minZoom: 0.5, | ||
| 268 | maxZoom: 1, | ||
| 269 | modes: { | ||
| 270 | default: [ | ||
| 271 | { | ||
| 272 | type: 'collapse-expand', | ||
| 273 | onChange: (item, collapsed) => { | ||
| 274 | if (!item) { | ||
| 275 | return; | ||
| 276 | } | ||
| 277 | const data = item.getModel(); | ||
| 278 | data.collapsed = collapsed; | ||
| 279 | return true; | ||
| 280 | }, | ||
| 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 | }, | ||
| 321 | getHGap: function getHGap() { | ||
| 322 | return 120; | ||
| 323 | }, | ||
| 324 | }, | ||
| 325 | }); | ||
| 326 | graphRef.value = graph; | ||
| 327 | graph.node((node) => { | ||
| 328 | return { | ||
| 329 | id: node.guid as string, | ||
| 330 | label: handleLabelLength(node.name as string), | ||
| 331 | collapsed: node.children?.length ? false : true, | ||
| 332 | labelCfg: { | ||
| 333 | offset: 10, | ||
| 334 | style: { | ||
| 335 | fontSize: 16, | ||
| 336 | fill: '#212121', | ||
| 337 | }, | ||
| 338 | position: !node.isField ? 'left' : 'right', //只有字段是最后一层级,不需要展开 | ||
| 339 | }, | ||
| 340 | style: { | ||
| 341 | stroke: '#4fa1a4', | ||
| 342 | cursor: 'pointer' | ||
| 343 | } | ||
| 344 | }; | ||
| 345 | }); | ||
| 346 | // data不是数组,第一级是根节点 | ||
| 347 | graph.data(props.treeData); | ||
| 348 | graph.render(); | ||
| 349 | graph.fitView(40, { direction: 'both' }); | ||
| 350 | graph.fitCenter(); | ||
| 351 | |||
| 352 | graph.setMinZoom(0.5); | ||
| 353 | graph.setMaxZoom(5); | ||
| 354 | } | ||
| 355 | |||
| 356 | observeResize(); | ||
| 357 | |||
| 358 | bindEvents(); | ||
| 359 | }) | ||
| 360 | |||
| 361 | }) | ||
| 362 | |||
| 363 | const observeResize = () => { | ||
| 364 | resizeObserver.value = new ResizeObserver(() => { | ||
| 365 | let domWidth = document.documentElement.clientWidth; | ||
| 366 | if (domWidth < 992) {//根据setting.ts里的设置,小于992,会隐藏左边的菜单栏, | ||
| 367 | setTimeout(() => { | ||
| 368 | const container: any = containerRef.value; | ||
| 369 | const width = container.clientWidth; | ||
| 370 | const height = container.clientHeight - 10; | ||
| 371 | if (!width) {//会把隐藏的给消失。 | ||
| 372 | return; | ||
| 373 | } | ||
| 374 | graphRef.value.changeSize(width, height); | ||
| 375 | graphRef.value.setMinZoom(0.5); | ||
| 376 | graphRef.value.setMaxZoom(1); | ||
| 377 | graphRef.value.fitView(40, { direction: 'both' }); | ||
| 378 | graphRef.value.fitCenter(); | ||
| 379 | graphRef.value.setMinZoom(0.5); | ||
| 380 | graphRef.value.setMaxZoom(5); | ||
| 381 | }, 500) | ||
| 382 | } | ||
| 383 | const container: any = containerRef.value; | ||
| 384 | const width = container.clientWidth; | ||
| 385 | const height = container.clientHeight - 10; | ||
| 386 | if (!width) {//会把隐藏的给消失。 | ||
| 387 | return; | ||
| 388 | } | ||
| 389 | graphRef.value.changeSize(width, height); | ||
| 390 | graphRef.value.setMinZoom(0.5); | ||
| 391 | graphRef.value.setMaxZoom(1); | ||
| 392 | graphRef.value.fitView(40, { direction: 'both' }); | ||
| 393 | graphRef.value.fitCenter(); | ||
| 394 | graphRef.value.setMinZoom(0.5); | ||
| 395 | graphRef.value.setMaxZoom(5); | ||
| 396 | }); | ||
| 397 | resizeObserver.value.observe(containerRef.value); | ||
| 398 | } | ||
| 399 | |||
| 400 | |||
| 401 | const tooltip1Ref = ref(); | ||
| 402 | |||
| 403 | // 更新tooltip的位置 | ||
| 404 | function updateTooltipPosition(evt) { | ||
| 405 | var width = graphRef.value.get("width"); | ||
| 406 | var height = graphRef.value.get("height"); | ||
| 407 | var offsetX = 10; | ||
| 408 | var offsetY = 10; | ||
| 409 | var point = graphRef.value.getPointByClient(evt.clientX, evt.clientY); | ||
| 410 | var _a2 = graphRef.value.getCanvasByPoint(point.x, point.y), x4 = _a2.x, y4 = _a2.y; | ||
| 411 | var graphContainer = graphRef.value.getContainer(); | ||
| 412 | var res = { | ||
| 413 | x: x4 + graphContainer.offsetLeft + offsetX, | ||
| 414 | y: y4 + graphContainer.offsetTop + offsetY | ||
| 415 | }; | ||
| 416 | let bboxHeight = tooltip1Ref.value.getBoundingClientRect().height; | ||
| 417 | if (x4 + 320 + offsetX > width) { | ||
| 418 | res.x -= 320 + offsetX; | ||
| 419 | } | ||
| 420 | if (y4 + bboxHeight + offsetY > height) { | ||
| 421 | res.y -= bboxHeight + offsetY; | ||
| 422 | if (res.y < 0) { | ||
| 423 | res.y = 0; | ||
| 424 | } | ||
| 425 | } | ||
| 426 | tooltip1Ref.value.style.left = `${res.x}px`; | ||
| 427 | tooltip1Ref.value.style.top = `${res.y}px`; | ||
| 428 | tooltip1Ref.value.style.display = 'block'; | ||
| 429 | } | ||
| 430 | |||
| 431 | const lastSelectNode = ref(null); | ||
| 432 | |||
| 433 | const bindEvents = () => { | ||
| 434 | let graph = graphRef.value; | ||
| 435 | graph.on('node:click', function (evt) { | ||
| 436 | const item = evt.item; | ||
| 437 | if (!item) { | ||
| 438 | return; | ||
| 439 | } | ||
| 440 | const nodeId = item.get('guid'); | ||
| 441 | const model = <any>item.getModel(); | ||
| 442 | if (lastSelectNode.value) { | ||
| 443 | if (lastSelectNode.value == item) { | ||
| 444 | return; | ||
| 445 | } | ||
| 446 | tooltip1Ref.value.style.display = 'none'; | ||
| 447 | graphRef.value.updateItem(lastSelectNode.value, { | ||
| 448 | labelCfg: { | ||
| 449 | style: { | ||
| 450 | fill: '#212121', | ||
| 451 | } | ||
| 452 | }, | ||
| 453 | style: { | ||
| 454 | stroke: '#4fa1a4', | ||
| 455 | fill: '#ebf6f7', | ||
| 456 | cursor: 'pointer' | ||
| 457 | } | ||
| 458 | }); | ||
| 459 | lastSelectNode.value = null; | ||
| 460 | } | ||
| 461 | const children = model.children; | ||
| 462 | if (children?.length && !model.isField) { //是字段级别,就不需要再展开了 | ||
| 463 | return; | ||
| 464 | } | ||
| 465 | if (model.isField) { | ||
| 466 | graphRef.value.updateItem(item, { | ||
| 467 | labelCfg: { | ||
| 468 | style: { | ||
| 469 | fill: '#4fa1a4' | ||
| 470 | } | ||
| 471 | }, | ||
| 472 | style: { | ||
| 473 | stroke: '#4fa1a4', | ||
| 474 | fill: '#4fa1a4', | ||
| 475 | cursor: 'pointer' | ||
| 476 | } | ||
| 477 | }); | ||
| 478 | lastSelectNode.value = item; | ||
| 479 | detailInfo.value.guid = model.guid; | ||
| 480 | updateTooltipPosition(evt); | ||
| 481 | detailLoading.value = true; | ||
| 482 | |||
| 483 | setTimeout(() => { | ||
| 484 | detailLoading.value = false; | ||
| 485 | }, 500); | ||
| 486 | return; | ||
| 487 | } | ||
| 488 | emits('nodeItemClick', graph, item); | ||
| 489 | evt.preventDefault(); | ||
| 490 | evt.stopPropagation(); | ||
| 491 | }); | ||
| 492 | graph.on('dragstart', (evt: any) => { | ||
| 493 | if (evt.item?.getType() == 'node') { | ||
| 494 | return; | ||
| 495 | } | ||
| 496 | // 清除状态 | ||
| 497 | if (lastSelectNode.value) { | ||
| 498 | tooltip1Ref.value.style.display = 'none'; | ||
| 499 | graphRef.value.updateItem(lastSelectNode.value, { | ||
| 500 | labelCfg: { | ||
| 501 | style: { | ||
| 502 | fill: '#212121', | ||
| 503 | }, | ||
| 504 | }, | ||
| 505 | style: { | ||
| 506 | stroke: '#4fa1a4', | ||
| 507 | fill: '#ebf6f7', | ||
| 508 | cursor: 'pointer' | ||
| 509 | } | ||
| 510 | }); | ||
| 511 | } | ||
| 512 | lastSelectNode.value = null; | ||
| 513 | }) | ||
| 514 | graph.on('click', (evt: any) => { | ||
| 515 | if (evt.item?.getType() == 'node') { | ||
| 516 | return; | ||
| 517 | } | ||
| 518 | // 清除状态 | ||
| 519 | if (lastSelectNode.value) { | ||
| 520 | tooltip1Ref.value.style.display = 'none'; | ||
| 521 | graphRef.value.updateItem(lastSelectNode.value, { | ||
| 522 | labelCfg: { | ||
| 523 | style: { | ||
| 524 | fill: '#212121', | ||
| 525 | }, | ||
| 526 | }, | ||
| 527 | style: { | ||
| 528 | stroke: '#4fa1a4', | ||
| 529 | fill: '#ebf6f7', | ||
| 530 | cursor: 'pointer' | ||
| 531 | } | ||
| 532 | }); | ||
| 533 | } | ||
| 534 | lastSelectNode.value = null; | ||
| 535 | }); | ||
| 536 | graph.on('canvas:click', (evt: any) => { | ||
| 537 | // 清除状态 | ||
| 538 | if (lastSelectNode.value) { | ||
| 539 | tooltip1Ref.value.style.display = 'none'; | ||
| 540 | graphRef.value.updateItem(lastSelectNode.value, { | ||
| 541 | labelCfg: { | ||
| 542 | style: { | ||
| 543 | fill: '#212121', | ||
| 544 | }, | ||
| 545 | }, | ||
| 546 | style: { | ||
| 547 | stroke: '#4fa1a4', | ||
| 548 | fill: '#ebf6f7', | ||
| 549 | cursor: 'pointer' | ||
| 550 | } | ||
| 551 | }); | ||
| 552 | } | ||
| 553 | lastSelectNode.value = null; | ||
| 554 | }); | ||
| 555 | } | ||
| 556 | |||
| 557 | const destroy = () => { | ||
| 558 | graphRef.value?.destroy(); | ||
| 559 | } | ||
| 560 | |||
| 561 | defineExpose({ | ||
| 562 | destroy, | ||
| 563 | }); | ||
| 564 | |||
| 565 | </script> | ||
| 566 | |||
| 567 | <style lang="scss" scoped> | ||
| 568 | .canvas-wrapper { | ||
| 569 | width: 100%; | ||
| 570 | height: 100%; | ||
| 571 | position: relative; | ||
| 572 | } | ||
| 573 | |||
| 574 | .canvas-wrapper:-webkit-full-screen { | ||
| 575 | background-color: white; | ||
| 576 | } | ||
| 577 | |||
| 578 | .canvas-wrapper:-moz-full-screen { | ||
| 579 | background-color: white; | ||
| 580 | } | ||
| 581 | |||
| 582 | .canvas-wrapper:-ms-fullscreen { | ||
| 583 | background-color: white; | ||
| 584 | } | ||
| 585 | |||
| 586 | .canvas-wrapper:fullscreen { | ||
| 587 | background-color: white; | ||
| 588 | } | ||
| 589 | |||
| 590 | .main { | ||
| 591 | padding: 16px; | ||
| 592 | box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.08); | ||
| 593 | background-color: #fff; | ||
| 594 | max-width: 320px; | ||
| 595 | max-height: 400px; | ||
| 596 | overflow-y: auto; | ||
| 597 | |||
| 598 | .title { | ||
| 599 | font-size: 16px; | ||
| 600 | color: #212121; | ||
| 601 | line-height: 24px; | ||
| 602 | font-weight: 600; | ||
| 603 | } | ||
| 604 | |||
| 605 | .row { | ||
| 606 | margin-top: 8px; | ||
| 607 | font-size: 14px; | ||
| 608 | color: #666666; | ||
| 609 | line-height: 21px; | ||
| 610 | } | ||
| 611 | } | ||
| 612 | </style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| ... | @@ -27,6 +27,7 @@ declare module '@vue/runtime-core' { | ... | @@ -27,6 +27,7 @@ declare module '@vue/runtime-core' { |
| 27 | FileUpload: typeof import('./../components/FileUpload/index.vue')['default'] | 27 | FileUpload: typeof import('./../components/FileUpload/index.vue')['default'] |
| 28 | FixedActionBar: typeof import('./../components/FixedActionBar/index.vue')['default'] | 28 | FixedActionBar: typeof import('./../components/FixedActionBar/index.vue')['default'] |
| 29 | Form: typeof import('./../components/Form/index.vue')['default'] | 29 | Form: typeof import('./../components/Form/index.vue')['default'] |
| 30 | GraphTopbar: typeof import('./../components/RelationNetwork/graphTopbar.vue')['default'] | ||
| 30 | Hour: typeof import('./../components/Schedule/component/hour.vue')['default'] | 31 | Hour: typeof import('./../components/Schedule/component/hour.vue')['default'] |
| 31 | ImagePreview: typeof import('./../components/ImagePreview/index.vue')['default'] | 32 | ImagePreview: typeof import('./../components/ImagePreview/index.vue')['default'] |
| 32 | ImagesUpload: typeof import('./../components/ImagesUpload/index.vue')['default'] | 33 | ImagesUpload: typeof import('./../components/ImagesUpload/index.vue')['default'] |
| ... | @@ -41,6 +42,7 @@ declare module '@vue/runtime-core' { | ... | @@ -41,6 +42,7 @@ declare module '@vue/runtime-core' { |
| 41 | PageNav: typeof import('./../components/PageNav/index.vue')['default'] | 42 | PageNav: typeof import('./../components/PageNav/index.vue')['default'] |
| 42 | PcasCascader: typeof import('./../components/PcasCascader/index.vue')['default'] | 43 | PcasCascader: typeof import('./../components/PcasCascader/index.vue')['default'] |
| 43 | Popover: typeof import('./../components/Popover/index.vue')['default'] | 44 | Popover: typeof import('./../components/Popover/index.vue')['default'] |
| 45 | RelationNetwork: typeof import('./../components/RelationNetwork/index.vue')['default'] | ||
| 44 | RouterLink: typeof import('vue-router')['RouterLink'] | 46 | RouterLink: typeof import('vue-router')['RouterLink'] |
| 45 | RouterView: typeof import('vue-router')['RouterView'] | 47 | RouterView: typeof import('vue-router')['RouterView'] |
| 46 | Schedule: typeof import('./../components/Schedule/index.vue')['default'] | 48 | Schedule: typeof import('./../components/Schedule/index.vue')['default'] | ... | ... |
src/views/data_meta/components/Sankey.vue
0 → 100644
| 1 | <template> | ||
| 2 | <div ref="containerRef" class='canvas-wrapper'> | ||
| 3 | |||
| 4 | </div> | ||
| 5 | </template> | ||
| 6 | |||
| 7 | <script lang="ts" setup name="topbar"> | ||
| 8 | import { ref, watch } from 'vue'; | ||
| 9 | import * as echarts from "echarts"; | ||
| 10 | |||
| 11 | const props = defineProps({ | ||
| 12 | treeData: { | ||
| 13 | type: Array<any>, | ||
| 14 | default: [ | ||
| 15 | { | ||
| 16 | source: 'a', | ||
| 17 | target: 'a1', | ||
| 18 | value: 5 | ||
| 19 | }, | ||
| 20 | { | ||
| 21 | source: 'a', | ||
| 22 | target: 'a2', | ||
| 23 | value: 3 | ||
| 24 | }, | ||
| 25 | { | ||
| 26 | source: 'b', | ||
| 27 | target: 'b1', | ||
| 28 | value: 8 | ||
| 29 | }, | ||
| 30 | { | ||
| 31 | source: 'a', | ||
| 32 | target: 'b1', | ||
| 33 | value: 3 | ||
| 34 | }, | ||
| 35 | { | ||
| 36 | source: 'b1', | ||
| 37 | target: 'a1', | ||
| 38 | value: 1 | ||
| 39 | }, | ||
| 40 | { | ||
| 41 | source: 'b1', | ||
| 42 | target: 'c', | ||
| 43 | value: 2 | ||
| 44 | } | ||
| 45 | ] | ||
| 46 | }, | ||
| 47 | names: { | ||
| 48 | type: Array<any>, | ||
| 49 | default: [ | ||
| 50 | { | ||
| 51 | name: 'a' | ||
| 52 | }, | ||
| 53 | { | ||
| 54 | name: 'b' | ||
| 55 | }, | ||
| 56 | { | ||
| 57 | name: 'a1' | ||
| 58 | }, | ||
| 59 | { | ||
| 60 | name: 'a2' | ||
| 61 | }, | ||
| 62 | { | ||
| 63 | name: 'b1' | ||
| 64 | }, | ||
| 65 | { | ||
| 66 | name: 'c' | ||
| 67 | } | ||
| 68 | ] | ||
| 69 | } | ||
| 70 | }) | ||
| 71 | |||
| 72 | watch(() => props.treeData, (val) => { | ||
| 73 | setChartsOption(); | ||
| 74 | }) | ||
| 75 | |||
| 76 | const sankeyInstance: any = ref(); | ||
| 77 | |||
| 78 | const containerRef = ref(); | ||
| 79 | |||
| 80 | const setChartsOption = () => { | ||
| 81 | let option = { | ||
| 82 | tooltip: { | ||
| 83 | trigger: 'item' | ||
| 84 | }, | ||
| 85 | series: [ | ||
| 86 | { | ||
| 87 | type: 'sankey', | ||
| 88 | top: 80, | ||
| 89 | bottom: 40, | ||
| 90 | left: 50, | ||
| 91 | right: 50, | ||
| 92 | data: props.names, | ||
| 93 | links: props.treeData, | ||
| 94 | lineStyle: { | ||
| 95 | color: 'source', | ||
| 96 | curveness: 0.5 | ||
| 97 | }, | ||
| 98 | } | ||
| 99 | ] | ||
| 100 | } | ||
| 101 | sankeyInstance.value.setOption(option); | ||
| 102 | sankeyInstance.value.resize(); | ||
| 103 | } | ||
| 104 | |||
| 105 | const resizeObserver = ref(); | ||
| 106 | |||
| 107 | const observeResize = () => { | ||
| 108 | resizeObserver.value = new ResizeObserver(() => { | ||
| 109 | sankeyInstance.value?.resize(); | ||
| 110 | }); | ||
| 111 | resizeObserver.value.observe(containerRef.value); | ||
| 112 | } | ||
| 113 | |||
| 114 | onMounted(() => { | ||
| 115 | nextTick(() => { | ||
| 116 | sankeyInstance.value = echarts.init(containerRef.value); | ||
| 117 | setChartsOption(); | ||
| 118 | observeResize(); | ||
| 119 | }) | ||
| 120 | }) | ||
| 121 | |||
| 122 | </script> | ||
| 123 | |||
| 124 | <style lang="scss" scoped> | ||
| 125 | .canvas-wrapper { | ||
| 126 | width: 100%; | ||
| 127 | height: 100%; | ||
| 128 | position: relative; | ||
| 129 | } | ||
| 130 | </style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | <route lang="yaml"> | ||
| 2 | name: metadataStandardQuery | ||
| 3 | </route> | ||
| 4 | |||
| 5 | <script lang="ts" setup name="metadataStandardQuery"> | ||
| 6 | import { ref } from 'vue'; | ||
| 7 | import { ElMessage, ElMessageBox } from "element-plus"; | ||
| 8 | import Sankey from './components/Sankey.vue'; | ||
| 9 | import Tree from '@/components/Tree/index.vue'; | ||
| 10 | import RelationNetwork from '@/components/RelationNetwork/index.vue'; | ||
| 11 | import { | ||
| 12 | } from '@/api/modules/dataMetaService'; | ||
| 13 | |||
| 14 | import { useRouter, useRoute } from "vue-router"; | ||
| 15 | import useDataMetaStore from "@/store/modules/dataMeta" | ||
| 16 | |||
| 17 | const router = useRouter(); | ||
| 18 | const route = useRoute() | ||
| 19 | |||
| 20 | const switchGraphDisplay = ref(true); //true表示关系网,false表示桑基图 | ||
| 21 | |||
| 22 | const { proxy } = getCurrentInstance() as any; | ||
| 23 | |||
| 24 | const relationNetworkRef = ref(); | ||
| 25 | |||
| 26 | const treeInfo = ref({ | ||
| 27 | id: "data-meta-standard-tree", | ||
| 28 | filter: true, | ||
| 29 | queryValue: "", | ||
| 30 | queryPlaceholder: "输入库/表名称搜索", | ||
| 31 | props: { | ||
| 32 | label: "name", | ||
| 33 | value: "guid", | ||
| 34 | isLeaf: "isLeaf", | ||
| 35 | }, | ||
| 36 | nodeKey: 'guid', | ||
| 37 | expandedKey: ['1'], | ||
| 38 | currentNodeKey: '', | ||
| 39 | expandOnNodeClick: false, | ||
| 40 | data: <any>[], | ||
| 41 | loading: false | ||
| 42 | }); | ||
| 43 | |||
| 44 | /** 获取左侧树数据. */ | ||
| 45 | const getTreeData = async () => { | ||
| 46 | treeInfo.value.loading = true | ||
| 47 | // let params = {} | ||
| 48 | // const res: any = await getMetaTreeData(params) | ||
| 49 | // if (res.code == proxy.$passCode) { | ||
| 50 | // const data = res.data || []; | ||
| 51 | // let treeData: any = Object.keys(data).length ? [data] : []; | ||
| 52 | // treeInfo.value.data = treeData; | ||
| 53 | // allTreeData.value = treeData; | ||
| 54 | // if (treeData.length) { | ||
| 55 | // treeInfo.value.currentNodeKey = treeData[0].guid; | ||
| 56 | // treeInfo.value.expandedKey = <any>[treeData[0].guid]; | ||
| 57 | // } | ||
| 58 | // } else { | ||
| 59 | // ElMessage.error(res.msg); | ||
| 60 | // } | ||
| 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 | } | ||
| 93 | /** 左侧树的的组件引用. */ | ||
| 94 | const treeInfoRef = ref(); | ||
| 95 | |||
| 96 | /** 当前选中的树节点数据data */ | ||
| 97 | const lastClickNode: any = ref({}); | ||
| 98 | |||
| 99 | const treeDataLoading = ref(false); | ||
| 100 | |||
| 101 | /** 数据血缘关系图组件 */ | ||
| 102 | const lineageGraph: any = ref(); | ||
| 103 | |||
| 104 | /** 点击左侧树节点,更新对应的血缘关系图. */ | ||
| 105 | const nodeClick = (data) => { | ||
| 106 | console.log(data); | ||
| 107 | const ele = <HTMLElement>document.querySelector(".g6-component-contextmenu") | ||
| 108 | if (ele) { | ||
| 109 | ele.style.display = "none" | ||
| 110 | } | ||
| 111 | nextTick(() => { | ||
| 112 | lineageGraph.value?.tooltip1.hide() | ||
| 113 | }) | ||
| 114 | treeInfo.value.currentNodeKey = data.guid; | ||
| 115 | treeInfo.value.expandedKey = <any>[data.guid]; | ||
| 116 | lastClickNode.value = data; | ||
| 117 | } | ||
| 118 | |||
| 119 | /** 选中树节点后自动滚动到可视范围内. */ | ||
| 120 | const scrollToNode = (nodeId) => { | ||
| 121 | nextTick(() => { | ||
| 122 | const nodeElement = treeInfoRef.value.treeRef.$el.querySelector(`[data-key="${nodeId}"]`); | ||
| 123 | if (nodeElement) { | ||
| 124 | nodeElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); | ||
| 125 | } | ||
| 126 | }); | ||
| 127 | } | ||
| 128 | |||
| 129 | /** 处理从详情处跳转而来的默认展示. */ | ||
| 130 | const processRouter = () => { | ||
| 131 | const { guid, databaseName, tableName, databaseChName, databaseGuid, fieldGuid, fieldEnName, set } = useDataMetaStore() | ||
| 132 | let isFL = useDataMetaStore().isFieldLineage; | ||
| 133 | if (fieldGuid) {//查看字段血缘的 | ||
| 134 | nextTick(() => { | ||
| 135 | treeInfo.value.expandedKey = <any>[databaseGuid, guid]; | ||
| 136 | treeInfo.value.currentNodeKey = fieldGuid as string; | ||
| 137 | scrollToNode(fieldGuid); | ||
| 138 | treeInfoRef.value.setCurrentKey(fieldGuid); | ||
| 139 | }) | ||
| 140 | lastClickNode.value = { guid: fieldGuid, tableGuid: guid, databaseGuid: databaseGuid, enName: fieldEnName, tableName, databaseName, type: 4, isLeaf: true, databaseChName }; | ||
| 141 | // getTableFieldLineageMap(); | ||
| 142 | set() | ||
| 143 | } else if (guid) { | ||
| 144 | treeInfo.value.currentNodeKey = guid as string; | ||
| 145 | treeInfo.value.expandedKey = <any>[databaseGuid, guid]; | ||
| 146 | lastClickNode.value = { guid: guid, tableName, databaseName, type: 3, databaseChName }; | ||
| 147 | scrollToNode(guid); | ||
| 148 | switchGraphDisplay.value = isFL; | ||
| 149 | // if (isFL) { | ||
| 150 | // getAllTableFieldLineageMap(); | ||
| 151 | // } else { | ||
| 152 | // getTableLineageMap() | ||
| 153 | // } | ||
| 154 | set() | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | onActivated(() => { | ||
| 159 | processRouter(); | ||
| 160 | }); | ||
| 161 | |||
| 162 | onBeforeMount(async () => { | ||
| 163 | await getTreeData() | ||
| 164 | processRouter(); | ||
| 165 | }) | ||
| 166 | |||
| 167 | onMounted(() => { }) | ||
| 168 | |||
| 169 | const isGraphDisplay = ref(true); | ||
| 170 | |||
| 171 | const displaySwitchChange = (val) => { | ||
| 172 | isGraphDisplay.value = val; | ||
| 173 | } | ||
| 174 | |||
| 175 | const handleNodeItemClick = (graph, nodeItem) => { | ||
| 176 | const nodeId = nodeItem.get('id'); | ||
| 177 | const parentData = graph.findDataById(nodeId); | ||
| 178 | if (!parentData.children) { | ||
| 179 | parentData.children = []; | ||
| 180 | } | ||
| 181 | treeDataLoading.value = true; | ||
| 182 | let childData = [{ | ||
| 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; | ||
| 200 | graph.changeData(); | ||
| 201 | // parentData.collapsed = true; | ||
| 202 | // graph.updateChildren(childData, parentData.id); | ||
| 203 | // graph.changeData(lastClickNode.value); | ||
| 204 | //graph.data(lastClickNode.value); | ||
| 205 | }, 2000) | ||
| 206 | } | ||
| 207 | |||
| 208 | const handleContextMenu = (nodeData) => { | ||
| 209 | //TODO,新建引用数据集 | ||
| 210 | } | ||
| 211 | |||
| 212 | onBeforeUnmount(() => { | ||
| 213 | relationNetworkRef.value.destroy(); | ||
| 214 | }) | ||
| 215 | |||
| 216 | </script> | ||
| 217 | |||
| 1 | <template> | 218 | <template> |
| 2 | <div>元数据标准查询</div> | 219 | <div class="container_wrap full flex"> |
| 220 | <div class="aside_wrap"> | ||
| 221 | <div class="aside_title">数据库目录列表</div> | ||
| 222 | <Tree ref="treeInfoRef" :treeInfo="treeInfo" @nodeClick="nodeClick" /> | ||
| 223 | </div> | ||
| 224 | <div class="main_wrap"> | ||
| 225 | <div className='g6-component-topbar'> | ||
| 226 | <graphTopbar ref="topBarRef" @displaySwitchChange="displaySwitchChange" :isGraphDisplay="isGraphDisplay" /> | ||
| 227 | </div> | ||
| 228 | <RelationNetwork v-show="isGraphDisplay" ref="relationNetworkRef" :tree-data="lastClickNode" | ||
| 229 | v-loading="treeDataLoading" @nodeItemClick="handleNodeItemClick" @contextMenu="handleContextMenu"> | ||
| 230 | </RelationNetwork> | ||
| 231 | <Sankey v-show="!isGraphDisplay"></Sankey> | ||
| 232 | <!-- <div v-show="lastClickNode && lastClickNode?.type !== 3 && lastClickNode?.type !== 4" class="main-placeholder"> | ||
| 233 | <img src="../../assets/images/no-data.png" :style="{ width: '96px', height: '96px' }" /> | ||
| 234 | <div class="empty-text">暂无标准数据</div> | ||
| 235 | </div> --> | ||
| 236 | </div> | ||
| 237 | </div> | ||
| 3 | </template> | 238 | </template> |
| 4 | 239 | ||
| 5 | <script> | 240 | <style scoped lang="scss"> |
| 6 | export default { | 241 | .container_wrap { |
| 242 | |||
| 243 | .aside_wrap { | ||
| 244 | width: 200px; | ||
| 245 | margin-right: 1px; | ||
| 246 | } | ||
| 247 | |||
| 248 | .main_wrap { | ||
| 249 | position: relative; | ||
| 250 | |||
| 251 | :deep(.canvas-wrapper) { | ||
| 252 | background-color: #f7f7f9; | ||
| 253 | } | ||
| 254 | |||
| 255 | .main-placeholder { | ||
| 256 | height: 100%; | ||
| 257 | display: flex; | ||
| 258 | justify-content: center; | ||
| 259 | align-items: center; | ||
| 260 | flex-direction: column; | ||
| 261 | |||
| 262 | .empty-text { | ||
| 263 | font-size: 14px; | ||
| 264 | color: #b2b2b2; | ||
| 265 | } | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 7 | } | 269 | } |
| 8 | </script> | ||
| 9 | 270 | ||
| 10 | <style> | 271 | .g6-component-topbar { |
| 272 | position: absolute; | ||
| 273 | left: 24px; | ||
| 274 | bottom: unset; | ||
| 275 | top: 14px; | ||
| 276 | padding: 0; | ||
| 277 | text-align: center; | ||
| 278 | z-index: 999; | ||
| 279 | } | ||
| 280 | |||
| 281 | .container_wrap.flex .main_wrap { | ||
| 282 | padding: 0px; | ||
| 283 | } | ||
| 284 | |||
| 285 | .tree_panel { | ||
| 286 | height: calc(100% - 36px); | ||
| 287 | padding-top: 0; | ||
| 288 | |||
| 289 | :deep(.el-tree) { | ||
| 290 | margin: 0; | ||
| 291 | overflow: hidden auto; | ||
| 292 | } | ||
| 293 | } | ||
| 294 | |||
| 295 | .card-noData { | ||
| 296 | height: 100%; | ||
| 297 | width: 100%; | ||
| 298 | background: #fafafa; | ||
| 299 | display: flex; | ||
| 300 | flex-direction: column; | ||
| 301 | justify-content: center; | ||
| 302 | align-items: center; | ||
| 303 | color: #909399; | ||
| 304 | font-size: 14px; | ||
| 305 | } | ||
| 306 | |||
| 307 | :deep(.el-form .el-form-item) { | ||
| 308 | width: calc(100%); | ||
| 309 | // margin-right: 8px; | ||
| 310 | } | ||
| 311 | |||
| 312 | :deep(.el-message) { | ||
| 313 | position: fixed; | ||
| 314 | /* 使用fixed或absolute定位 */ | ||
| 315 | z-index: 10000; | ||
| 316 | /* 设置一个较高的z-index值确保在最上层显示 */ | ||
| 317 | } | ||
| 11 | </style> | 318 | </style> | ... | ... |
-
Please register or sign in to post a comment