123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405 |
- <template>
- <div
- style="height:100%; position: relative; font-size: 16px;"
- v-loading="loading"
- class="d-flex align-center justify-center"
- >
- <div class="legend d-flex pa-3 justify-space-between">
- <div class="select pointerEvents">
- <v-select
- v-model="type"
- :items="items"
- dense
- outlined
- hide-details
- style="background-color: #fff;"
- @change="changeType"
- ></v-select>
- </div>
- <div class="d-flex align-center">
- <div
- v-for="item in legend"
- :key="item.title"
- class="d-flex ml-5"
- >
- <div class="pa-3 mr-3 rounded-circle" :style="`background-color: ${item.color};`"></div>
- {{ item.title }}
- </div>
- <v-switch
- class="pointerEvents mt-0 ml-3"
- hide-details
- v-model="showMeta"
- :false-value="false"
- :true-value="true"
- label="显示元数据"
- @change="handleChangeMeta"
- ></v-switch>
- </div>
- </div>
- <MEmpty v-if="empty"></MEmpty>
- <relation-graph
- v-show="!empty"
- ref="graphRef"
- :options="graphOptions"
- :on-node-click="onNodeClick"
- :on-line-click="onLineClick"
- >
- <template #node="{node}">
- <div @contextmenu.prevent="handleContextmenu($event, node)">
- <div
- :style="{ width: node.width + 'px', height: node.height + 'px' }"
- :class="{ 'node-active': menu.activeId === +node.id }"
- class="rounded-circle"
- >
- </div>
- <div
- :style="{ 'background-color': node.color + '44', width: 20 * node.text.length + 'px' }"
- class="node-text"
- :class="{ 'node-active': menu.activeId === +node.id }"
- >
- {{ node.text }}
- </div>
- </div>
- </template>
- <template #graph-plug>
- <v-menu v-model="menu.show" attach :position-x="menu.x" :position-y="menu.y" absolute offset-y min-width="200">
- <v-list dense>
- <v-list-item v-for="(k, i) in menu.items" :key="i" @click="k.handle">
- <v-list-item-title>{{ k.title }}</v-list-item-title>
- </v-list-item>
- </v-list>
- </v-menu>
- </template>
- </relation-graph>
- </div>
- </template>
- <script>
- import RelationGraph from 'relation-graph'
- import MEmpty from '@/components/Common/empty'
- import { api } from '@/api/dataGovernance'
- const defaultNodeColor = 'rgba(238, 178, 94, 1)'
- const NODES_SIZE = {
- width: 30,
- height: 30
- }
- const LINE_COLOR_MAP = {
- 包含: '#F44336', // red
- 影响: '#E91E63', // pink
- 依赖: '#3F51B5', // indigo
- 来源: '#4CAF50', // green
- 引用: '#9C27B0', // purple
- 继承: '#673AB7', // deep-purple
- 标记: '#2196F3', // blue
- 使用: '#03A9F4', // light-blue
- 关联: '#00BCD4', // cyan
- 拥有: '#009688', // teal
- 下级: '#8BC34A', // light-green
- 上级: '#CDDC39', // lime
- 联动: '#FF9800' // orange
- }
- export default {
- name: 'details-graph',
- components: { RelationGraph, MEmpty },
- props: {
- toApi: {
- type: Function,
- default: api.getResourceGraph
- },
- meta: {
- type: Boolean,
- default: false
- },
- query: {
- type: Object,
- default: () => ({})
- }
- },
- data () {
- return {
- menu: {
- x: 0,
- y: 0,
- show: false,
- items: [
- { title: '查看', handle: this.handleView }
- ],
- activeId: +this.$route.params.id,
- item: {}
- },
- empty: true,
- items: [
- { text: '全链关系', value: 'all' },
- { text: '血缘关系', value: 'kinship' },
- { text: '影响关系', value: 'impact' }
- ],
- type: 'all',
- loading: false,
- graphOptions: {
- defaultJunctionPoint: 'lr',
- // 这里可以参考"Graph 图谱"中的参数进行设置 https://www.relation-graph.com/#/docs/graph
- debug: false, // 是否开始调试模式,调试模式下会在控制台打印额外的日志信息
- showDebugPanel: false, // 是否显示调试按钮,通过此按钮可以打印配置、数据等
- backgroundImage: '', // 图谱水印url,如:https://ssl.relation-graph.com/images/relatioon-graph-canvas-bg.png
- downloadImageFileName: '', // 下载图片时,图片的名称
- disableZoom: false, // 是否禁用图谱的缩放功能
- disableDragNode: false, // 是否禁用图谱中节点的拖动
- moveToCenterWhenRefresh: true, // 当图谱刷新后(调用setJsonData或refresh方法都会触发),让图谱根据节点居中(图片会默认将根节点作为中心展示,此选项会根据节点分布寻找中心)
- zoomToFitWhenRefresh: true, // 当图谱刷新后(调用setJsonData或refresh方法都会触发),是否让图谱缩放到适合可见区域大小,此选项不适用于fixed和force布局
- useAnimationWhenRefresh: true, // 当图谱刷新后(调用setJsonData或refresh方法都会触发),使用动画让图居中、缩放
- useAnimationWhenExpanded: true,
- defaultFocusRootNode: true, // 默认为根节点添加一个被选中的样式
- disableNodeClickEffect: false, // 是否禁用节点默认的点击效果(选中、闪烁)
- disableLineClickEffect: false, // 是否禁用线条默认的点击效果(选中、闪烁)
- allowShowZoomMenu: true, // 是否在右侧菜单栏显示放大缩小的按钮,此设置和disableZoom不冲突
- allowAutoLayoutIfSupport: true, // 是否在工具栏中显示【自动布局】按钮(只有在布局支持且此选项为true时才会显示的按钮)
- allowShowRefreshButton: true, // 是否在工具栏中显示【刷新】按钮
- allowShowDownloadButton: true, // 是否在工具栏中显示【下载图片】按钮
- backgroundImageNoRepeat: false, // 只在右下角显示水印,不重复显示水印
- allowSwitchLineShape: true, // 是否在工具栏中显示切换线条形状的按钮
- allowSwitchJunctionPoint: true, // 是否在工具栏中显示切换连接点位置的按钮
- isMoveByParentNode: false, // 是否在拖动节点后让子节点跟随
- defaultExpandHolderPosition: 'hide', // 默认的节点展开/关闭按钮位置(left/top/right/bottom/hide)
- defaultNodeColor, // 默认的节点背景颜色
- checkedLineColor: '#FD8B37', // 当线条被选中时的颜色
- defaultNodeFontColor: '#ffffff', // 默认的节点文字颜色
- defaultNodeBorderColor: '#90EE90', // 默认的节点边框颜色
- defaultNodeBorderWidth: 0, // 默认的节点边框粗细(像素)
- defaultLineColor: '#cccccc', // 默认的线条颜色
- defaultLineWidth: 2, // 默认的线条粗细(像素)
- defaultLineShape: 2, // 默认的线条样式(1:直线/2:样式2/3:样式3/4:折线/5:样式5/6:样式6)使用示例
- defaultNodeShape: 0, // 默认的节点形状,0:圆形;1:矩形
- defaultShowLineLabel: true, // 默认是否显示连线文字,v2版本此选项已无效,主要是这个选项没什么用
- hideNodeContentByZoom: true, // 是否根据缩放比例隐藏节点内容
- // disableDragCanvas: false,
- // lineUseTextPath: false,
- defaultLineMarker: { // 默认的线条箭头样式,示例参考:配置工具中的选项:连线箭头样式
- markerWidth: 24,
- markerHeight: 24,
- refX: 6,
- refY: 6,
- data: 'M2,2 L10,6 L2,10 L6,6 L2,2'
- },
- layouts: [
- {
- label: '自动布局',
- layoutName: 'tree', // 布局方式(tree树状布局/center中心布局/force自动布局)
- from: 'left',
- maxLayoutTimes: 20,
- layoutClassName: 'seeks-layout-force',
- useLayoutStyleOptions: false,
- defaultNodeColor: '#FFC5A6',
- defaultNodeFontColor: '#000000',
- defaultNodeBorderColor: '#efefef',
- defaultNodeBorderWidth: 1,
- defaultLineColor: '#FD8B37',
- defaultLineWidth: 1,
- defaultShowLineLabel: true,
- defaultLineMarker: {
- markerWidth: 12,
- markerHeight: 12,
- refX: 6,
- refY: 6,
- data: 'M2,2 L10,6 L2,10 L6,6 L2,2'
- }
- }
- ]
- },
- config: {
- DataResource: { // 资源
- color: '#9FA8DA',
- title: '数据资源',
- className: 'sourceNode',
- ...NODES_SIZE
- },
- DataModel: { // 模型
- color: '#EF9A9A',
- title: '数据模型',
- className: 'modelNode',
- ...NODES_SIZE
- },
- DataMetric: { // 指标
- color: '#00BCD4',
- title: '数据指标',
- className: 'metricNode',
- ...NODES_SIZE
- },
- // standard: { // 标准
- // color: '#009688',
- // title: '数据标准',
- // className: 'standardNode',
- // ...NODES_SIZE
- // },
- DataLabel: { // 标签
- color: '#9C27B0',
- title: '数据标签',
- className: 'labelNode',
- ...NODES_SIZE
- },
- DataMeta: { // 元数据
- color: defaultNodeColor,
- title: '元数据',
- className: '',
- ...NODES_SIZE
- }
- },
- showMeta: this.meta
- }
- },
- computed: {
- legend () {
- return Object.values(this.config)
- }
- },
- mounted () {
- this.init()
- },
- methods: {
- handleContextmenu (v, node) {
- const { left, top } = this.$refs.graphRef.$el.getBoundingClientRect()
- this.menu.x = v.clientX - left
- this.menu.y = v.clientY - top
- this.menu.item = node
- this.menu.show = true
- },
- handleView () {
- this.draw({ id: +this.menu.item.id }, () => {
- this.menu.activeId = +this.menu.item.id
- })
- },
- handleChangeMeta (val) {
- this.showMeta = val
- this.init()
- },
- changeType () {
- this.init()
- },
- handleClick (node) {
- console.log(node)
- },
- async init () {
- const query = {
- ...this.query
- }
- if (this.$route.params.id) {
- Object.assign(query, {
- id: +this.$route.params.id
- })
- }
- this.draw(query)
- },
- async draw (query, successCallback = () => {}) {
- // 清空再渲染
- this.loading = true
- this.empty = false
- try {
- const { data } = await this.toApi({
- ...query,
- type: this.type,
- meta: this.showMeta
- })
- if (!data.nodes || !data.nodes.length) {
- this.empty = true
- this.loading = false
- return
- }
- this.graphOptions.downloadImageFileName = data.rootId ?? ''
- data.nodes.forEach(ele => {
- if (!this.config[ele.node_type]) {
- return
- }
- Object.assign(ele, this.config[ele.node_type])
- })
- data.lines.forEach(ele => {
- ele.color = LINE_COLOR_MAP[ele.text]
- })
- this.$nextTick(() => {
- this.$refs.graphRef.setOptions(this.graphOptions, async (graphInstance) => {
- this.$refs.graphRef.setJsonData(data, async (_graphInstance) => {
- await _graphInstance.setZoom(75)
- successCallback()
- this.loading = false
- })
- })
- })
- } catch (error) {
- this.empty = true
- this.$snackbar.error(error)
- }
- },
- onNodeClick (nodeObject, $event) {
- console.log('onNodeClick:', nodeObject)
- },
- onLineClick (lineObject, $event) {
- console.log('onLineClick:', lineObject)
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- .legend {
- pointer-events: none;
- width: 100%;
- user-select: none;
- -moz-user-select: none;
- -webkit-user-select: none;
- -ms-user-select: none;
- position: absolute;
- top: 0;
- z-index: 10;
- color: #666;
- .rounded-circle {
- width: 24px;
- height: 24px;
- }
- }
- .pointerEvents {
- pointer-events: auto;
- }
- // ::v-deep .sourceNode .rel-node-checked {
- // box-shadow: 0 0 0 8px #C5CAE9 !important;
- // }
- // ::v-deep .modelNode .rel-node-checked {
- // box-shadow: 0 0 0 8px #ffd9d9 !important;
- // }
- // ::v-deep .metricNode .rel-node-checked {
- // box-shadow: 0 0 0 8px #a7f3fd !important;
- // }
- // ::v-deep .standardNode .rel-node-checked {
- // box-shadow: 0 0 0 8px #58c1b7 !important;
- // }
- // ::v-deep .labelNode .rel-node-checked {
- // box-shadow: 0 0 0 8px #f4bdff !important;
- // }
- ::v-deep .rel-node-checked {
- box-shadow: unset !important;
- }
- .node-active {
- box-shadow: 0 0 0px 6px #f3f900 !important;
- // background: #000 !important;
- // border: 2px solid #000;
- }
- .node-text {
- color: #000;
- font-size: 16px;
- position: absolute;
- height:25px;
- transform: translate(-50%, 0);
- line-height: 25px;
- left: 50%;
- margin-top:5px;
- text-align: center;
- }
- ::v-deep .rel-toolbar {
- background-color: #f39930;
- color: #ffffff;
- .c-current-zoom {
- color: #ffffff;
- }
- }
- </style>
|