欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

关系图关系图使用技巧

最编程 2024-07-07 16:07:09
...

一、relation-graph简介

一个Vue的关系图谱组件,使用非常方便

relation-graph官网(上面有文档介绍和案例展示)

二、使用步骤

引入relation-graph

npm install --save relation-graph

使用组件

<RelationGraph
      ref="seeksRelationGraph"
      :options="graphOptions"
      :on-node-click="onNodeClick"
      :on-line-click="onLineClick"
      :on-node-expand="onNodeExpand"
  />
</div>

三、参数配置

1.Graph 图谱

配置图谱的一些默认样式,工具栏等

graphOptions: {
  allowSwitchLineShape: true,
  isMoveByParentNode:true,
  disableLineClickEffect:true,
  'layouts': [
    // {
    //   'label': '布局1',
    //   'layoutName': 'center',
    //   'layoutClassName': 'seeks-layout-center',
    //   useLayoutStyleOptions: true,
    //   'defaultNodeWidth': '50',
    //   'defaultNodeHeight': '50',
    //   defaultNodeBorderWidth: 0,
    //   defaultNodeColor: 'rgba(238, 178, 94, 1)',
    //   defaultLineShape: 1
    // },
    // {
    //   'label': '布局2',
    //   'layoutName': 'tree',
    //   'layoutClassName': 'seeks-layout-center',
    //   useLayoutStyleOptions: true,
    //   'from': 'top',
    //   'defaultNodeWidth': '30',
    //   'defaultNodeHeight': '100',
    //   'defaultJunctionPoint': 'tb',
    //   'defaultNodeShape': 1,
    //   'defaultLineShape': 4,
    //   'defaultNodeBorderWidth': 0,
    //   'defaultLineColor': 'rgba(0, 186, 189, 1)',
    //   'defaultNodeColor': 'rgba(0, 206, 209, 1)',
    //   'min_per_width': 40,
    //   'max_per_width': 70,
    //   'min_per_height': 200
    // },
    {
      'label': '布局3',
      'layoutName': 'tree',
      'layoutClassName': 'seeks-layout-center',
      useLayoutStyleOptions: true,
      hideNodeContentByZoom:true,
      'from': 'left',
      'defaultNodeWidth': '100',
      'defaultNodeHeight': '30',
      'defaultJunctionPoint': 'lr',
      'defaultNodeShape': 1,
      'defaultLineShape': 6,
      'defaultNodeBorderWidth': 0,
      'defaultLineColor': '#c0c0c0',
      'defaultNodeColor': '#ffa00b',
      'min_per_width': 200,
      'max_per_width': 400,
      'min_per_height': 40,
      'max_per_height': 70,
      'defaultExpandHolderPosition':'right',
      'defaultLineWidth':3
    }
  ],
  defaultJunctionPoint: 'border'
  // 这里可以参考"Graph 图谱"中的参数进行设置
}

参数具体参考文档,看你需要什么样式或者开始什么功能,按照文档上来配置,layouts里可以配置多种布局,具体看是否需要布局切换

2.Node 节点

nodes': [
  { 'id': 'a', 'text': 'a' },
  { 'id': 'b', 'text': 'b-固定数据展开/关闭' },
  { 'id': 'b1', 'text': 'b1' },
  { 'id': 'b1-1', 'text': 'b1-1' },
  { 'id': 'b1-2', 'text': 'b1-2' },
  { 'id': 'b1-3', 'text': 'b1-3' },
  { 'id': 'b1-4', 'text': 'b1-4' },
  { 'id': 'b1-5', 'text': 'b1-5' },
  { 'id': 'b1-6', 'text': 'b1-6' },
  { 'id': 'b2', 'text': 'b2' },
  { 'id': 'b2-1', 'text': 'b2-1' },
  { 'id': 'b2-2', 'text': 'b2-2' },
  { 'id': 'c', 'text': 'c-动态数据展开/关闭' },
  { 'id': 'c1', 'text': 'c1-动态获取子节点', expandHolderPosition: 'right', expanded: false, data: { isNeedLoadDataFromRemoteServer: true, childrenLoaded: false }},
  { 'id': 'c2', 'text': 'c2-动态获取子节点', expandHolderPosition: 'right', expanded: false, data: { isNeedLoadDataFromRemoteServer: true, childrenLoaded: false }},
  { 'id': 'c3', 'text': 'c3-动态获取子节点', expandHolderPosition: 'right', expanded: false, data: { isNeedLoadDataFromRemoteServer: true, childrenLoaded: false }}]

node 节点参数也按照文档上来配置,我这里是需要用到动态获取子节点的,动态获取子节点代码

onNodeExpand(node,e){
  console.log('onNodeExpand:', node);
  // 根据具体的业务需要决定是否需要从后台加载数据
  if (!node.data.isNeedLoadDataFromRemoteServer) {
    console.log('这个节点的子节点已经加载过了');
    // this.$refs.seeksRelationGraph.refresh();
    return;
  }
  // 判断是否已经动态加载数据了
  if (node.data.childrenLoaded) {
    console.log('这个节点的子节点已经加载过了');
    // this.$refs.seeksRelationGraph.refresh();
    return;
  }
  this.g_loading = true;
  node.data.childrenLoaded = true;
  this.loadChildNodesFromRemoteServer(node, new_data => {
    this.g_loading = false;
    this.$refs.seeksRelationGraph.getInstance().appendJsonData(new_data, (graphInstance) => {
      // 这些写上当图谱初始化完成后需要执行的代码
    });
  });
}

3.Link 关系

links是指节点之间的关系(link),图谱会根据这些关系来生成线条(Line)

'lines': [
  { 'from': 'a', 'to': 'b' },
  { 'from': 'b', 'to': 'b1' },
  { 'from': 'b1', 'to': 'b1-1' },
  { 'from': 'b1', 'to': 'b1-2' },
  { 'from': 'b1', 'to': 'b1-3' },
  { 'from': 'b1', 'to': 'b1-4' },
  { 'from': 'b1', 'to': 'b1-5' },
  { 'from': 'b1', 'to': 'b1-6' },
  { 'from': 'b', 'to': 'b2' },
  { 'from': 'b2', 'to': 'b2-1' },
  { 'from': 'b2', 'to': 'b2-2' },
  { 'from': 'a', 'to': 'c' },
  { 'from': 'c', 'to': 'c1' },
  { 'from': 'c', 'to': 'c2' },
  { 'from': 'c', 'to': 'c3' }]

四、特殊处理

因为我这里需要用到点击节点展示该节点的祖宗和子孙关系,还有展示点击某个关系展示该关系的祖宗和子孙节点,需要实现on-node-click和on-line-click事件

//节点点击函数
onNodeClick(nodeObject, $event) {
  console.log('onNodeClick:', nodeObject);
  const allLinks = this.$refs.seeksRelationGraph.getLinks();
  allLinks.forEach(link => { // 还原所有样式
    link.relations.forEach(line => {
      line.color = "#c0c0c0";
      line.lineWidth = 3;
    });
  });

  let fromNodeList = []
  let toNodeList = []
  this.getFromNode(allLinks,nodeObject,fromNodeList)
  this.getToNode(allLinks,nodeObject,toNodeList)
  // fromNodeList.forEach(item=>{
  //   console.log(item.fromNode.id + "->"+item.toNode.id)
  // })
  // toNodeList.forEach(item=>{
  //   console.log(item.fromNode.id + "->"+item.toNode.id)
  // })

  toNodeList.forEach(item=>{
    allLinks.filter(link => (link.fromNode === item.fromNode && link.toNode === item.toNode)).forEach(link => {
      console.log(link.fromNode)
      link.relations.forEach(line => {
        line.color = "#ffa00b"
        line.lineWidth = 5;
      });
    });
  })
  fromNodeList.forEach(item=>{
    allLinks.filter(link => (link.fromNode === item.fromNode && link.toNode === item.toNode)).forEach(link => {
      console.log(link.fromNode)
      link.relations.forEach(line => {
        line.color = "#ffa00b"
        line.lineWidth = 5;
      });
    });
  })


  // 让与{nodeObject}相关的所有连线高亮
  // allLinks.filter(link => (link.fromNode === nodeObject || link.toNode === nodeObject)).forEach(link => {
  //   console.log(link.fromNode)
  //   link.relations.forEach(line => {
  //     console.log('line:', line);
  //     line.data.orignColor = line.color;
  //     line.data.orignFontColor = line.fontColor || line.color;
  //     line.data.orignLineWidth = line.lineWidth || 1;
  //     line.lineWidth = 3;
  //   });
  // });
  // // 有时候更改一些属性后,并不能马上同步到视图,这需要以下方法让视图强制根据数据同步到最新
  this.$refs.seeksRelationGraph.getInstance().dataUpdated();
},
//获取祖宗节点(递归)
getFromNode(allLinks,node,fromNodeList){
   let linkList = allLinks.filter(link => (link.toNode === node))
    if(linkList.length > 0){
      linkList.forEach(link =>{
        fromNodeList.push({fromNode:link.fromNode,toNode:node})
        this.getFromNode(allLinks,link.fromNode,fromNodeList)
      })
    }
},
//获取子孙节点(递归)
getToNode(allLinks,node,toNodeList){
  let linkList = allLinks.filter(link => (link.fromNode === node))
  if(linkList.length > 0){
    linkList.forEach(link =>{
      toNodeList.push({fromNode:node,toNode:link.toNode})
      this.getToNode(allLinks,link.toNode,toNodeList)
    })
  }
},

//关系惦记函数
onLineClick(lineObject, linkObject, $event) {
  console.log('onLineClick:', linkObject);

  const allLinks = this.$refs.seeksRelationGraph.getLinks();
  allLinks.forEach(link => { // 还原所有样式
    link.relations.forEach(line => {
      line.color = "#c0c0c0";
      line.lineWidth = 3;
    });
  });

  let fromNodeList = []
  let toNodeList = []
  this.getFromNode(allLinks,linkObject.fromNode,fromNodeList)
  this.getToNode(allLinks,linkObject.toNode,toNodeList)
  toNodeList.push({fromNode:linkObject.fromNode,toNode:linkObject.toNode})
  fromNodeList.forEach(item=>{
    console.log(item.fromNode.id + "->"+item.toNode.id)
  })
  toNodeList.forEach(item=>{
    console.log(item.fromNode.id + "->"+item.toNode.id)
  })

  toNodeList.forEach(item=>{
    allLinks.filter(link => (link.fromNode === item.fromNode && link.toNode === item.toNode)).forEach(link => {
      link.relations.forEach(line => {
        line.color = "#ffa00b"
        line.lineWidth = 5;
      });
    });
  })
  fromNodeList.forEach(item=>{
    allLinks.filter(link => (link.fromNode === item.fromNode && link.toNode === item.toNode)).forEach(link => {
      console.log(link.fromNode)
      link.relations.forEach(line => {
        line.color = "#ffa00b"
        line.lineWidth = 5;
      });
    });
  })
  this.$refs.seeksRelationGraph.getInstance().dataUpdated();
}

五、完整代码

<template>
  <div v-loading="g_loading">
    <div style="height:calc(100vh - 50px);">
      <RelationGraph
          ref="seeksRelationGraph"
          :options="graphOptions"
          :on-node-click="onNodeClick"
          :on-line-click="onLineClick"
          :on-node-expand="onNodeExpand"
      />
    </div>
    <el-button type="success" class="c-show-code-button">
      <el-link
          href="https://github.com/seeksdream/relation-graph/blob/master/examples/views/seeks-graph-docs/demo/Demo4AdvMultiLayout.vue"
          target="_blank" style="color: #ffffff;">查看代码
      </el-link>
    </el-button>
    <el-drawer
        title="node option:"
        direction="rtl"
        size="50%"
        custom-class="c-drawer-window"
        :modal="false"
        :visible.sync="isShowCodePanel"
        :with-header="false"
    >
      <iframe src="/relation-graph-codes/Demo4Logo.html" width="100%" height="100%" frameborder="0" scrolling="auto"
              style=""/>
    </el-drawer>
  </div>
</template>

<script>
// 如果您没有在main.js文件中使用Vue.use(RelationGraph); 就需要使用下面这一行代码来引入relation-graph
// import RelationGraph from 'relation-graph';
import test from './test.json'

export default {
  name: 'Demo',
  components: {},
  data() {
    return {
      g_loading:false,
      isShowCodePanel: false,
      graphOptions: {
        allowSwitchLineShape: true,
        isMoveByParentNode:true,
        disableLineClickEffect:true,
        'layouts': [
          // {
          //   'label': '布局1',
          //   'layoutName': 'center',
          //   'layoutClassName': 'seeks-layout-center',
          //   useLayoutStyleOptions: true,
          //   'defaultNodeWidth': '50',
          //   'defaultNodeHeight': '50',
          //   defaultNodeBorderWidth: 0,
          //   defaultNodeColor: 'rgba(238, 178, 94, 1)',
          //   defaultLineShape: 1
          // },
          // {
          //   'label': '布局2',
          //   'layoutName': 'tree',
          //   'layoutClassName': 'seeks-layout-center',
          //   useLayoutStyleOptions: true,
          //   'from': 'top',
          //   'defaultNodeWidth': '30',
          //   'defaultNodeHeight': '100',
          //   'defaultJunctionPoint': 'tb',
          //   'defaultNodeShape': 1,
          //   'defaultLineShape': 4,
          //   'defaultNodeBorderWidth': 0,
          //   'defaultLineColor': 'rgba(0, 186, 189, 1)',
          //   'defaultNodeColor': 'rgba(0, 206, 209, 1)',
          //   'min_per_width': 40,
          //   'max_per_width': 70,
          //   'min_per_height': 200
          // },
          {
            'label': '布局3',
            'layoutName': 'tree',
            'layoutClassName': 'seeks-layout-center',
            useLayoutStyleOptions: true,
            hideNodeContentByZoom:true,
            'from': 'left',
            'defaultNodeWidth': '100',
            'defaultNodeHeight': '30',
            'defaultJunctionPoint': 'lr',
            'defaultNodeShape': 1,
            'defaultLineShape': 6,
            'defaultNodeBorderWidth': 0,
            'defaultLineColor': '#c0c0c0',
            'defaultNodeColor': '#ffa00b',
            'min_per_width': 200,
            'max_per_width': 400,
            'min_per_height': 40,
            'max_per_height': 70,
            'defaultExpandHolderPosition':'right',
            'defaultLineWidth':3
          }
        ],
        defaultJunctionPoint: 'border'
        // 这里可以参考"Graph 图谱"中的参数进行设置
      }
    };
  },
  mounted() {
    // this.$notify({
    //   title: '提示:',
    //   message: '点击右侧工具栏中的"布局"按钮来切换',
    //   type: 'success',
    //   duration: 10000
    // });
    this.showSeeksGraph();
  },
  methods: {
    showSeeksGraph() {
      const __graph_json_data = {
        'rootId': 'a',
        'nodes': [
          { 'id': 'a', 'text': 'a' },
          { 'id': 'b', 'text': 'b-固定数据展开/关闭' },
          { 'id': 'b1', 'text': 'b1' },
          { 'id': 'b1-1', 'text': 'b1-1' },
          { 'id': 'b1-2', 'text': 'b1-2' },
          { 'id': 'b1-3', 'text': 'b1-3' },
          { 'id': 'b1-4', 'text': 'b1-4' },
          { 'id': 'b1-5', 'text': 'b1-5' },
          { 'id': 'b1-6', 'text': 'b1-6' },
          { 'id': 'b2', 'text': 'b2' },
          { 'id': 'b2-1', 'text': 'b2-1' },
          { 'id': 'b2-2', 'text': 'b2-2' },
          { 'id': 'c', 'text': 'c-动态数据展开/关闭' },
          { 'id': 'c1', 'text': 'c1-动态获取子节点', expandHolderPosition: 'right', expanded: false, data: { isNeedLoadDataFromRemoteServer: true, childrenLoaded: false }},
          { 'id': 'c2', 'text': 'c2-动态获取子节点', expandHolderPosition: 'right', expanded: false, data: { isNeedLoadDataFromRemoteServer: true, childrenLoaded: false }},
          { 'id': 'c3', 'text': 'c3-动态获取子节点', expandHolderPosition: 'right', expanded: false, data: { isNeedLoadDataFromRemoteServer: true, childrenLoaded: false }}],
        'lines': [
          { 'from': 'a', 'to': 'b' },
          { 'from': 'b', 'to': 'b1' },
          { 'from': 'b1', 'to': 'b1-1' },
          { 'from': 'b1', 'to': 'b1-2' },
          { 'from': 'b1', 'to': 'b1-3' },
          { 'from': 'b1', 'to': 'b1-4' },
          { 'from': 'b1', 'to': 'b1-5' },
          { 'from': 'b1', 'to': 'b1-6' },
          { 'from': 'b', 'to': 'b2' },
          { 'from': 'b2', 'to': 'b2-1' },
          { 'from': 'b2', 'to': 'b2-2' },
          { 'from': 'a', 'to': 'c' },
          { 'from': 'c', 'to': 'c1' },
          { 'from': 'c', 'to': 'c2' },
          { 'from': 'c', 'to': 'c3' }]
      }
      // const __graph_json_data = test
      this.$refs.seeksRelationGraph.setJsonData(__graph_json_data, (graphInstance) => {
        // 这些写上当图谱初始化完成后需要执行的代码
        // console.log(this.$refs.seeksRelationGraph.getInstance().getLinks())
      });
    },
    onNodeClick(nodeObject, $event) {
      console.log('onNodeClick:', nodeObject);
      const allLinks = this.$refs.seeksRelationGraph.getLinks();
      allLinks.forEach(link => { // 还原所有样式
        link.relations.forEach(line => {
          line.color = "#c0c0c0";
          line.lineWidth = 3;
        });
      });

      let fromNodeList = []
      let toNodeList = []
      this.getFromNode(allLinks,nodeObject,fromNodeList)
      this.getToNode(allLinks,nodeObject,toNodeList)
      // fromNodeList.forEach(item=>{
      //   console.log(item.fromNode.id + "->"+item.toNode.id)
      // })
      // toNodeList.forEach(item=>{
      //   console.log(item.fromNode.id + "->"+item.toNode.id)
      // })

      toNodeList.forEach(item=>{
        allLinks.filter(link => (link.fromNode === item.fromNode && link.toNode === item.toNode)).forEach(link => {
          console.log(link.fromNode)
          link.relations.forEach(line => {
            line.color = "#ffa00b"
            line.lineWidth = 5;
          });
        });
      })
      fromNodeList.forEach(item=>{
        allLinks.filter(link => (link.fromNode === item.fromNode && link.toNode === item.toNode)).forEach(link => {
          console.log(link.fromNode)
          link.relations.forEach(line => {
            line.color = "#ffa00b"
            line.lineWidth = 5;
          });
        });
      })


      // 让与{nodeObject}相关的所有连线高亮
      // allLinks.filter(link => (link.fromNode === nodeObject || link.toNode === nodeObject)).forEach(link => {
      //   console.log(link.fromNode)
      //   link.relations.forEach(line => {
      //     console.log('line:', line);
      //     line.data.orignColor = line.color;
      //     line.data.orignFontColor = line.fontColor || line.color;
      //     line.data.orignLineWidth = line.lineWidth || 1;
      //     line.lineWidth = 3;
      //   });
      // });
      // // 有时候更改一些属性后,并不能马上同步到视图,这需要以下方法让视图强制根据数据同步到最新
      this.$refs.seeksRelationGraph.getInstance().dataUpdated();
    },

    getFromNode(allLinks,node,fromNodeList){
       let linkList = allLinks.filter(link => (link.toNode === node))
        if(linkList.length > 0){
          linkList.forEach(link =>{
            fromNodeList.push({fromNode:link.fromNode,toNode:node})
            this.getFromNode(allLinks,link.fromNode,fromNodeList)
          })
        }
    },

    getToNode(allLinks,node,toNodeList){
      let linkList = allLinks.filter(link => (link.fromNode === node))
      if(linkList.length > 0){
        linkList.forEach(link =>{
          toNodeList.push({fromNode:node,toNode:link.toNode})
          this.getToNode(allLinks,link.toNode,toNodeList)
        })
      }
    },

    onLineClick(lineObject, linkObject, $event) {
      console.log('onLineClick:', linkObject);

      const allLinks = this.$refs.seeksRelationGraph.getLinks();
      allLinks.forEach(link => { // 还原所有样式
        link.relations.forEach(line => {
          line.color = "#c0c0c0";
          line.lineWidth = 3;
        });
      });

      let fromNodeList = []
      let toNodeList = []
      this.getFromNode(allLinks,linkObject.fromNode,fromNodeList)
      this.getToNode(allLinks,linkObject.toNode,toNodeList)
      toNodeList.push({fromNode:linkObject.fromNode,toNode:linkObject.toNode})
      fromNodeList.forEach(item=>{
        console.log(item.fromNode.id + "->"+item.toNode.id)
      })
      toNodeList.forEach(item=>{
        console.log(item.fromNode.id + "->"+item.toNode.id)
      })

      toNodeList.forEach(item=>{
        allLinks.filter(link => (link.fromNode === item.fromNode && link.toNode === item.toNode)).forEach(link => {
          link.relations.forEach(line => {
            line.color = "#ffa00b"
            line.lineWidth = 5;
          });
        });
      })
      fromNodeList.forEach(item=>{
        allLinks.filter(link => (link.fromNode === item.fromNode && link.toNode === item.toNode)).forEach(link => {
          console.log(link.fromNode)
          link.relations.forEach(line => {
            line.color = "#ffa00b"
            line.lineWidth = 5;
          });
        });
      })
      this.$refs.seeksRelationGraph.getInstance().dataUpdated();
    },
    onNodeExpand(node,e){
      console.log('onNodeExpand:', node);
      // 根据具体的业务需要决定是否需要从后台加载数据
      if (!node.data.isNeedLoadDataFromRemoteServer) {
        console.log('这个节点的子节点已经加载过了');
        // this.$refs.seeksRelationGraph.refresh();
        return;
      }
      // 判断是否已经动态加载数据了
      if (node.data.childrenLoaded) {
        console.log('这个节点的子节点已经加载过了');
        // this.$refs.seeksRelationGraph.refresh();
        return;
      }
      this.g_loading = true;
      node.data.childrenLoaded = true;
      this.loadChildNodesFromRemoteServer(node, new_data => {
        this.g_loading = false;
        this.$refs.seeksRelationGraph.getInstance().appendJsonData(new_data, (graphInstance) => {
          // 这些写上当图谱初始化完成后需要执行的代码
        });
      });
    },
    loadChildNodesFromRemoteServer(node, callback) {
      setTimeout(function() {
        const _new_json_data = {
          nodes: [
            { id: node.id + '-child-1', text: node.id + '-的动态子节点1', width: 150 },
            { id: node.id + '-child-2', text: node.id + '-的动态子节点2', width: 150 },
            { id: node.id + '-child-3', text: node.id + '-的动态子节点3', width: 150 }
          ],
          lines: [
            { from: node.id, to: node.id + '-child-1' },
            { from: node.id, to: node.id + '-child-2' },
            { from: node.id, to: node.id + '-child-3'}
          ]
        };
        callback(_new_json_data);
      }, 1000);
    }
  }
};
</script>

<!--<style lang="scss">-->
<!--</style>-->

<!--<style lang="scss" scoped>-->
<!--</style>-->

六、运行结果

image.png

推荐阅读