antv-G6使用笔记

  1. 有一个需求实现类似于企查查之类的企业图谱功能。调研了几个工具后,决定使用g6绘图来实现。
    具体使用方式查看G6官网文档
    下面附上写的一个demo代码。涵盖了节点位置,文字,颜色,节点事件等。

父组件

<template>
  <div id="graphContainer">
    <div
      id="container-map"
      ref="main-left"
      v-loading="mapLoading"
    />
  </div>
</template>
<script>
import G6 from "@antv/g6";
import { register } from './register-node'
import registorEdge from './register-edge'
export default {
  name: 'scenceCompany',
  data () {
    return {
      graph: '',
      mapLoading: false,
      pageSize: 8,
      // 记录6个一级分类
      pageNum1: 1,
      pageNum2: 1,
      pageNum3: 1,
      pageNum4: 1,
      pageNum5: 1,
      pageNum6: 1,
    }
  },
  mounted () {
    this.initG6()
    this.getMapData()
  },
  methods: {
    // 图谱数据
    getMapData () {
      this.mapLoading = true
      // 设置默认
      this.pageNum1 = 1
      this.pageNum2 = 1
      this.pageNum3 = 1
      this.pageNum4 = 1
      this.pageNum5 = 1
      this.pageNum6 = 1
      // this.$http.get('').then(res => {
        let res = {
          data: {
            id: '1',
            label: 'xx银行',
            companyCode: '600000',
            children: [
              {
                id: '1-1',
                label: '股东',
                location: 'left',
                childrenSize: 9,
                children: [
                  {
                    id: '1-1-1',
                    label: 'xx国际集团',
                    post: '债',
                    edgesLabel: '21.57%',
                    companyType: '股东',
                    location: 'left',
                  },
                  {
                    id: '1-1-2',
                    companyType: '股东',
                    post: '债',
                    label: 'xx集团广东',
                    location: 'left',
                  },
                  {
                    id: '1-1-3',
                    label: 'xx国际集团',
                    post: '债',
                    edgesLabel: '21.57%',
                    companyType: '股东',
                    location: 'left',
                  },
                  {
                    id: '1-1-4',
                    companyType: '股东',
                    post: '债',
                    label: 'xxx集团广东',
                    location: 'left',
                  },
                  {
                    id: '1-1-5',
                    label: 'xx国际集团',
                    edgesLabel: '21.57%',
                    companyType: '股东',
                    location: 'left',
                  },
                  {
                    id: '1-1-6',
                    companyType: '股东',
                    label: 'xx集团广东',
                    location: 'left',
                  },
                  {
                    id: '1-1-7',
                    companyType: '股东',
                    label: 'xx集团广东',
                    location: 'left',
                  },
                  {
                    id: '1-1-8',
                    companyType: '股东',
                    label: 'xx集团广东',
                    location: 'left',
                  }
                ]
              },
              {
                id: '1-2',
                label: '高管',
                location: 'right',
                childrenSize: 10,
                children: [
                  {
                    id: '1-2-1',
                    label: 'xx',
                    post: '董事长、执行董事',
                    location: 'right',
                    companyType: '高管'
                  },
                  {
                    id: '1-2-2',
                    label: 'xxx',
                    post: '副董事长、执行董事、行长',
                    location: 'right',
                    companyType: '高管'
                  },
                  {
                    id: '1-2-3',
                    label: 'xxx',
                    post: '副行长、执行董事、首席风险官',
                    location: 'right',
                    companyType: '高管'
                  },
                  {
                    id: '1-2-4',
                    label: 'xx',
                    post: '非执行董事',
                    location: 'right',
                    companyType: '高管'
                  },
                  {
                    id: '1-2-5',
                    label: 'xx',
                    post: '非执行董事',
                    location: 'right',
                    companyType: '高管'
                  },
                  {
                    id: '1-2-6',
                    label: 'xx',
                    post: '非执行董事',
                    location: 'right',
                    companyType: '高管'
                  },
                  {
                    id: '1-2-7',
                    label: 'xx',
                    post: '非执行董事',
                    location: 'right',
                    companyType: '高管'
                  },
                  {
                    id: '1-2-8',
                    label: 'xx',
                    post: '非执行董事',
                    location: 'right',
                    companyType: '高管'
                  },
                ]
              },
              {
                id: '1-3',
                label: '对外投资',
                location: 'left',
                childrenSize: 2,
                children: [
                  {
                    id: '1-3-1',
                    label: 'xx发展基金',
                    edgesLabel: '21.57%',
                    post: '债',
                    companyType: '对外投资',
                    location: 'left',
                  },
                  {
                    id: '1-3-2',
                    companyType: '对外投资',
                    label: 'xx理财',
                    post: '债',
                    location: 'left',
                  },
                  {
                    id: '1-3-3',
                    label: 'xx发展基金',
                    post: '债',
                    edgesLabel: '21.57%',
                    companyType: '对外投资',
                    location: 'left',
                  },
                  {
                    id: '1-3-4',
                    companyType: '对外投资',
                    label: 'xx理财',
                    location: 'left',
                  },
                ]
              },
              {
                id: '1-4',
                label: '客户',
                location: 'right',
                childrenSize: 3,
                children: [
                  {
                    id: '1-4-1',
                    label: 'xx投资(北京)',
                    post: '三板',
                    location: 'right',
                  },
                  {
                    id: '1-4-2',
                    label: 'xx集团',
                    post: '债',
                    location: 'right',
                  },
                  {
                    id: '1-4-3',
                    label: 'xx凯',
                    post: '688079',
                    location: 'right',
                  },
                ]
              },
              {
                id: '1-5',
                label: '供应商',
                location: 'right',
                childrenSize: 3,
                children: [
                  {
                    id: '1-5-1',
                    label: 'xx信息',
                    post: '300380',
                    location: 'right',
                  },
                  {
                    id: '1-5-2',
                    label: 'xx金融',
                    post: '430656',
                    location: 'right',
                  },
                  {
                    id: '1-5-3',
                    label: 'xx经营发展啊啊啊啊',
                    post: '债',
                    location: 'right',
                  },
                ]
              },
              {
                id: '1-6',
                label: '实控人',
                location: 'left',
                childrenSize: 2,
                children: [
                  {
                    id: '1-6-1',
                    label: '无实际控制人',
                    location: 'left',
                  },
                ]
              },
            ]
          }
        }
        this.mapLoading = false
        if (res.data.children.length) {
          for (let item of res.data.children) {
              if (item.childrenSize > this.pageSize) { // 添加更多按钮
                item.children.push({
                  id: item.label + 'more',
                  companyType: item.label,
                  label: `更多(${item.childrenSize - this.pageSize})`,
                  location: item.children[0].location,
                  num: this.pageSize > (item.childrenSize - this.pageSize) ? item.childrenSize - this.pageSize : this.pageSize
                })
              }
          }
        }
        this.graph.data(res.data)
        this.graph.render()
      // }).catch(() => {
      //   this.mapLoading = false
      // })
    },
    async getMoreCompanyData (model) {
      let pageNum = 0
      switch (model.companyType) {
        case '股东':
          this.pageNum1++
          pageNum = this.pageNum1
          break
        case '高管':
          this.pageNum2++
          pageNum = this.pageNum2
          break
        case '对外投资':
          this.pageNum3++
          pageNum = this.pageNum3
          break
        case '客户':
          this.pageNum4++
          pageNum = this.pageNum4
          break
        case '实控人':
          this.pageNum5++
          pageNum = this.pageNum5
          break
        case '供应商':
          this.pageNum6++
          pageNum = this.pageNum6
          break
      }
      let data = {}
      console.log(model)
      if (model.companyType == '股东') {
        data = {
          data: {
            isShow:false,
            total: 9,
            list: [
              {
                id: '1-1-9',
                companyType: '股东',
                label: 'xx集团广东',
                location: 'left',
              },
            ]
          }
        }         
      } else if (model.companyType == '高管') {
        data = {
          data: {
            isShow: false,
            total: 10,
            list: [
              {
                id: '1-2-9',
                label: 'xx',
                post: '董事长、执行董事',
                location: 'right',
                companyType: '高管'
              },
              {
                id: '1-2-10',
                label: 'xxx',
                post: '副董事长、执行董事、行长',
                location: 'right',
                companyType: '高管'
              },
            ]
          }
        }
      }
      return data.data
    },
    initG6 () {
      register(G6)
      registorEdge(G6)
      const ele = document.getElementById('container-map')
      const width = ele.clientWidth
      const height = ele.clientHeight
      const graph = new G6.TreeGraph({
        container: 'container-map',
        width: width,
        height: height,
        defaultNode: {
          type: "tree-node",
          anchorPoints: [
            [1, 0.5],
            [0, 0.5],
          ],
          // 节点样式
          style: {
            cursor: "pointer",
            radius: 4,
          },
        },
        // 配置边的属性
        defaultEdge: {
          type: "kaimo-line",
        },
        // 布局配置项
        layout: {
          type: "mindmap", // 脑图树布局
          direction: "H", 
          // 节点 id 的回调函数
          getId: function getId(d) {
            return d.id;
          },
          // 下面都是一些控制节点与节点间距离的回调函数,具体可以试着修改一下值。
          // 节点高度的回调函数
          getHeight: function getHeight() {
            return 16;
          },
          // 节点宽度的回调函数
          getWidth: function getWidth(cfg) {
            return 60;
          },
          // 节点纵向间距的回调函数
          getVGap: function getVGap() {
            return 20;
          },
          // 节点横向间距的回调函数
          getHGap: function getHGap(val) {
            return 70;
          },
          getSide: (node) => {
            if (node.data.location === "left") {
              return "left";
            }
            return "right";
          },
        },
        modes: {
          default: [
            'drag-canvas',
            'zoom-canvas'
          ]
        },
        fitView: true,
        fitCenter: true,
        // minZoom: 0.5,
        maxZoom: 1
      })
      this.graph = graph
      // 植入事件
      this.bindEvents()
      this.graph.get('canvas').set('localRefresh', false)
    },
    bindEvents () {
      const that = this
      this.graph.on('node:click', async e => {
        console.log('item', e)
        const model = e.item.getModel()
        const timeFlag = this.stopClik()
        if (timeFlag) return
        if (model.label.includes('更多')) {
          let newChildrenData = []
          const data = await that.getMoreCompanyData(model)
          console.log(123321, data)
          // 获取原有的子节点
          let oldChildren = e.item._cfg.parent._cfg.children
          oldChildren.forEach(ele => {
            const eleModel = ele.getModel()
            if (!eleModel.label.includes('更多')) newChildrenData.push(ele.getModel())
          })
          newChildrenData = [...newChildrenData, ...data.list]
          if (data.isShow) {
            newChildrenData.push({
              id: data.list[0].companyType + data.list[0].id + 'more',
              label: `更多(${data.total - newChildrenData.length})`,
              location: data.list[0].location,
              companyType: data.list[0].companyType,
              num: data.total - newChildrenData.length
            })
          }
          // 更新父节点下面的子节点
          this.graph.updateItem(e.item._cfg.parent._cfg.id, {
            children: JSON.parse(JSON.stringify(newChildrenData))
          })
          this.graph.layout()
          return
        }
      })
    },
     // 防止连续点击
     stopClik () {
      let nowName = new Date().getTime()
      let filterDataTime = Number(window.localStorage.getItem('filterDataTime'))
      if (filterDataTime) {
        if (nowName - filterDataTime < 2000) { // 防止重复点击
          window.localStorage.setItem('filterDataTime', filterDataTime)
          return true
        } else {
          window.localStorage.setItem('filterDataTime', nowName)
        }
      } else {
        window.localStorage.setItem('filterDataTime', nowName)
      }
    },
  }
}
</script>
<style lang="scss" scoped>
  #graphContainer {
    width: 100%;
    height: 100%;
    #container-map {
      width: 100%;
      height: 100%;
    }
  }
</style>

register-edge.js

export default G6 => {
  G6.registerEdge("kaimo-line", {
    /**
     * 绘制边,包含文本
     * @param  {Object} cfg 边的配置项
     * @param  {G.Group} group 图形分组,边中的图形对象的容器
     * @return {G.Shape} 绘制的图形,通过 node.get('keyShape') 可以获取到
     */
    draw(cfg, group) {
      const startPoint = cfg.startPoint;
      const endPoint = cfg.endPoint;
      const target = cfg.targetNode.getModel()
      const source = cfg.sourceNode.getModel()
      let shape;
      shape = group.addShape("path", {
        //线条
        attrs: {
          stroke: "#E4E7ED",
          path: [
            ["M", startPoint.x, startPoint.y],
            ["L", endPoint.x / 3 + (2 / 3) * startPoint.x, startPoint.y],
            ["L", endPoint.x / 3 + (2 / 3) * startPoint.x, endPoint.y],
            ["L", endPoint.x, endPoint.y],
          ],
          endArrow: {
            path: G6.Arrow.triangle(5, 5, 0), // 使用内置箭头路径函数,参数为箭头的 宽度、长度、偏移量(默认为 0,与 d 对应)
            d: 0,
            fill: "#E4E7ED",
            opacity: 0.5,
            lineWidth: 1,
          },
        },
        // must be assigned in G6 3.3 and later versions. it can be any value you want
        name: "path-shape",
      });
      // 关系描述
      if (target.edgesLabel) {
        group.addShape('text', {
          attrs: {
            x: endPoint.x + 10,
            y: endPoint.y,
            fontSize: 12,
            text: target.edgesLabel,
            fill: '#666'
          }
        })
      }
      return shape;
    },
  });
}

register-node.js

import G6 from '@antv/g6'
// 文本超出隐藏 (字段, 最大长度, 字体大小)
const fittingString = (str, maxWidth, fontSize) => {
  const ellipsis = "..."
  const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0]
  let currentWidth = 0
  let res = str
  const pattern = new RegExp("[\u4E00-\u9FA5]+")
  str.split("").forEach((letter, i) => {
    if (currentWidth > maxWidth - ellipsisLength) return
    if (pattern.test(letter)) {
      currentWidth += fontSize
    } else {
      currentWidth += G6.Util.getLetterWidth(letter, fontSize)
    }
    if (currentWidth > maxWidth - ellipsisLength) {
      res = `${str.substr(0, i)}${ellipsis}`
    }
  })
  return res
};
// 获取文本的长度
const getTextSize = (str, maxWidth, fontSize) => {
  let width = G6.Util.getTextSize(str, fontSize)[0]
  return width > maxWidth ? maxWidth : width
}
const getItemBakColor = (str) => {
  if (str === '股东') return '#FD924E'
  if (str === '高管') return '#7685E4'
  if (str === '客户') return '#3FCDC1'
  if (str === '对外投资') return '#F3AF17'
  if (str === '供应商') return '#FA9B2D'
  if (str === '实控人') return '#FA7370'
}

const register = G6 => {
  G6.registerNode("tree-node", {
    draw(cfg, group) {
      let rect;
      if (cfg.depth === 0) {
        rect = group.addShape("circle", {
          attrs: {
            x: 100, // x 轴移动距离
            y: 20, // y 轴移动距离
            r: 40,
            fill: "#0D85EE",
            stroke: "#0D85EE", // 边框色
          },
          name: "big-circle-shape",
        });
        group.addShape("image", {
          attrs: {
            x: 75, // x 轴移动距离
            y: -8, // y 轴移动距离
            width: 50,
            height: 50,
            img: require('@/assets/img/fangzi.png'),
          },
          name: "big-image-shape",
        });
        group.addShape("text", {
          attrs: {
            text: fittingString(cfg.label, 90, 16),
            x: 100,
            // x: getTextSize(cfg.label, 90, 16) + 35,
            y: 90,
            fontSize: 18,
            textAlign: "center",
            fontWeight: 700,
            textBaseline: "middle",
            fill: "#0D85EE",
          },
          name: "text-shape",
        });
        group.addShape("text", {
          attrs: {
            text: `[${cfg.companyCode}]`,
            x: 100,
            y: 110,
            fontSize: 18,
            textAlign: "center",
            fontWeight: 700,
            textBaseline: "middle",
            fill: "#0D85EE",
          },
          name: "text-shape",
        });
      }
      if (cfg.location === "left") {
        if (cfg.depth === 1) {
          rect = group.addShape("circle", {
            attrs: {
              x: 160, // x 轴移动距离
              y: 20, // y 轴移动距离
              r: 35,
              fill: getItemBakColor(cfg.label),
              stroke: getItemBakColor(cfg.label), // 边框色
            },
            name: "first-circle-shape",
          });
          if (cfg.label === '对外投资') {
            group.addShape("text", {
              attrs: {
                text: '对外',
                x: 160,
                y: 12,
                fontSize: 18,
                textAlign: "center",
                textBaseline: "middle",
                fill: "#fff",
              },
              name: "text-shape",
            });
            group.addShape("text", {
              attrs: {
                text: '投资',
                x: 160,
                y: 32,
                fontSize: 18,
                textAlign: "center",
                textBaseline: "middle",
                fill: "#fff",
              },
              name: "text-shape",
            });
          } else {
            group.addShape("text", {
              attrs: {
                text: cfg.label,
                x: 160,
                y: 20,
                fontSize: 18,
                textAlign: "center",
                textBaseline: "middle",
                fill: "#fff",
              },
              name: "text-shape",
            });
          }
        } else {
          if (cfg.label.indexOf('更多') > -1) {
            rect = group.addShape("rect", {
              attrs: {
                x: cfg.post ? 160 - getTextSize(cfg.label, 210, 18) - getTextSize(cfg.post, 210, 18) + 50 : 160 - getTextSize(cfg.label, 210, 18) + 50, // x 轴移动距离
                y: 0, // y 轴移动距离
                width: cfg.post ? getTextSize(cfg.label, 210, 18) + getTextSize(cfg.post, 210, 18) + 30 : getTextSize(cfg.label, 210, 18) + 30, // 宽
                height: 40, // 高
                fontSize: 18,
                radius: 4,
              },
              name: "big-rect-shape",
            });
            group.addShape("text", {
              attrs: {
                text: fittingString(cfg.label, 210, 18),
                x: 160 - getTextSize(cfg.label, 210, 18) + 80,
                y: 20,
                fontSize: 18,
                textAlign: "left",
                textBaseline: "middle",
                fill: '#0D85EE',
                cursor: 'pointer'
              },
              name: "text-shape",
            });
          } else {
            rect = group.addShape("rect", {
              attrs: {
                x: cfg.post ? 160 - getTextSize(cfg.label, 210, 18) - getTextSize(cfg.post, 210, 18) + 50 : 160 - getTextSize(cfg.label, 210, 18) + 50, // x 轴移动距离
                y: 0, // y 轴移动距离
                width: cfg.post ? getTextSize(cfg.label, 210, 18) + getTextSize(cfg.post, 210, 18) + 30 : getTextSize(cfg.label, 210, 18) + 30, // 宽
                height: 40, // 高
                fontSize: 18,
                // stroke: "#666",
                // fontWeight: 600,
                radius: 4,
              },
              name: "big-rect-shape",
            });
            // 左文本点
            group.addShape("text", {
              attrs: {
                text: fittingString(cfg.label, 210, 18),
                x: cfg.post ? 160 - getTextSize(cfg.label, 210, 18) + 50 : 160 - getTextSize(cfg.label, 210, 18) + 80,
                y: 20,
                fontSize: 18,
                textAlign: "left",
                textBaseline: "middle",
                fill: document.body.className.indexOf('custom-f4c46d')>-1?'#fff' :'#333',
              },
              name: "text-shape",
            });
            if (cfg.post) {
              group.addShape("text", {
                attrs: {
                  text: `[${cfg.post}]`,
                  x: 195 + getTextSize(cfg.post, 210, 18),
                  y: 20,
                  fontSize: 18,
                  textAlign: "left",
                  textBaseline: "middle",
                  fill: '#666',
                },
                name: "text-shape",
              });
            }
          }
        }
      } 
      if (cfg.location === "right") {
        if (cfg.depth === 1) {
          rect = group.addShape("circle", {
            attrs: {
              x: 35, // x 轴移动距离
              y: 20, // y 轴移动距离
              r: 35,
              fill: getItemBakColor(cfg.label),
              stroke: getItemBakColor(cfg.label), // 边框色
            },
            name: "first-circle-shape",
          });
          group.addShape("text", {
            attrs: {
              text: cfg.label,
              x: 35,
              y: 20,
              fontSize: 18,
              textAlign: "center",
              textBaseline: "middle",
              fill: "#fff",
            },
            name: "text-shape",
          });
        } else {
          if (cfg.label.indexOf('更多') > -1) {
            rect = group.addShape("text", {
              attrs: {
                text: fittingString(cfg.label, 210, 18),
                x: 10,
                y: 20,
                fontSize: 18,
                textAlign: "left",
                textBaseline: "middle",
                fill: '#0D85EE',
                cursor: 'pointer'
              },
              name: "text-shape",
            });
          } else {
            rect = group.addShape("text", {
              attrs: {
                text: cfg.companyType === '高管' ? cfg.label : fittingString(cfg.label, 210, 18),
                x: 10,
                y: 20,
                fontSize: 18,
                textAlign: "left",
                textBaseline: "middle",
                fill: document.body.className.indexOf('custom-f4c46d')>-1?'#fff' :'#333',
              },
              name: "text-shape",
            });
            if (cfg.post) {
              group.addShape("text", {
                attrs: {
                  text: `[${cfg.post}]`,
                  x: getTextSize(cfg.label, 210, 18) + 15,
                  y: 20,
                  fontSize: 18,
                  textAlign: "left",
                  textBaseline: "middle",
                  fill: '#666',
                },
                name: "text-shape",
              });
            }
          }
        }
      }
      return rect
    }
  })
}
export { register }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值