Vue3项目使用G6可视化组件实现一个树形机构图

简介: 在Vue 3项目中使用G6可视化组件库实现树形机构图的构建和展示。

前言

之前一直使用Echarts或Highcharts实现图表,也有听说G2/G6这些玩意,于是在项目中使用看看效果,还不错的样子。

一、示例代码

(1)/src/views/Example/AntcG6/index_tree.vue

<template>
  <div class="index">
    <div id="org-tree"></div>
  </div>
</template>
<script>

/**
  一、SVG中,path的指令有以下几种:
  1. M/m:移动到指定点
  2. L/l:从当前点画一条直线到指定点
  3. H/h:从当前点画一条水平线到指定x坐标
  4. V/v:从当前点画一条垂直线到指定y坐标
  5. C/c:从当前点画一条三次贝塞尔曲线到指定点
  6. S/s:从当前点画一条光滑的三次贝塞尔曲线到指定点
  7. Q/q:从当前点画一条二次贝塞尔曲线到指定点
  8. T/t:从当前点画一条光滑的二次贝塞尔曲线到指定点
  9. A/a:从当前点画一条椭圆弧到指定点
  10. Z/z:闭合路径

  二、在SVG中,path是用来绘制各种形状的元素,其中M、L、H、Z和V是path中的指令,具体含义如下:
  - M:移动到指定的坐标点,不画线。例如,M10 10表示将当前点移动到坐标(10,10)。
  - L:从当前点画一条直线到指定的坐标点。例如,L50 50表示从当前点画一条直线到坐标(50,50)。
  - H:从当前点画一条水平线到指定的x坐标点。例如,H100表示从当前点画一条水平线到x坐标为100的点。
  - Z:闭合路径,即从当前点画一条直线到路径起点,形成一个封闭的图形。例如,Z表示从当前点画一条直线到路径起点,形成一个封闭的图形。
  - V:从当前点画一条垂直线到指定的y坐标点。例如,V100表示从当前点画一条垂直线到y坐标为100的点。
  这些指令可以组合使用,用来绘制各种复杂的形状。
 */
import G6 from '@antv/g6'
export default {
   
   
  data() {
   
   
    return {
   
   
      treeData: {
   
   
        id: "root",
        label: "新机场建设项目",
        children: [
          {
   
   
            id: "c1",
            label: "航站区",
            children: [
              {
   
   
                id: "c1-1",
                label: "旅客航站楼",
                children: [
                  {
   
   
                    id: "c1-1-1",
                    label: "主航站楼",
                  },
                  {
   
   
                    id: "c1-1-2",
                    label: "连接楼",
                  },
                  {
   
   
                    id: "c1-1-3",
                    label: "登机楼",
                  },
                ],
              },
              {
   
   
                id: "c1-2",
                label: "塔台",
              },
              {
   
   
                id: "c1-3",
                label: "机场酒店、停车楼",
              },
              {
   
   
                id: "c1-4",
                label: "大关小学",
              },
            ],
          },
          {
   
   
            id: "c2",
            label: "飞行区",
            children: [
              {
   
   
                id: "c2-1",
                label: "跑道",
                children: [
                  {
   
   
                    id: "c2-1-1",
                    label: "一号跑道",
                  },
                  {
   
   
                    id: "c2-1-2",
                    label: "二号跑道",
                  },
                ],
              },
              {
   
   
                id: "c2-2",
                label: "滑行道",
                children: [
                  {
   
   
                    id: "c2-2-1",
                    label: "滑行道入口",
                  },
                  {
   
   
                    id: "c2-2-2",
                    label: "滑行道出口",
                  },
                ],
              },
              {
   
   
                id: "c2-3",
                label: "停机坪",
                children: [
                  {
   
   
                    id: "c2-3-1",
                    label: "客机坪",
                  },
                  {
   
   
                    id: "c2-3-2",
                    label: "货机坪",
                  },
                  {
   
   
                    id: "c2-3-3",
                    label: "商务机坪",
                  },
                ],
              },
            ],
          },
          {
   
   
            id: "c3",
            label: "工作区",
            children: [
              {
   
   
                id: "c3-1",
                label: "飞机维修区",
              },
              {
   
   
                id: "c3-2",
                label: "航空食品区",
              },
              {
   
   
                id: "c3-3",
                label: "油库区",
              },
              {
   
   
                id: "c3-4",
                label: "环保设施区",
              },
              {
   
   
                id: "c3-5",
                label: "机场当局、航空公司办公楼",
              },
            ],
          },
        ],
      },
    }
  },
  mounted() {
   
   
    this.init_G6_OrgTree()
  },
  methods: {
   
   
    /**
     * 初始化一个 G6 树图
     */
    init_G6_OrgTree() {
   
   
      // 自定义 icon-node 节点
      G6.registerNode(
        'icon-node',
        {
   
   
          draw(cfg, group) {
   
   
            const styles = this.getShapeStyle(cfg)
            const {
   
    labelCfg = {
   
   } } = cfg
            const w = styles.width
            const h = styles.height
            const keyShape = group.addShape('rect', {
   
   
              attrs: {
   
   
                ...styles,
              },
            })
            if (cfg.label) {
   
   
              group.addShape('text', {
   
   
                attrs: {
   
   
                  ...labelCfg.style,
                  text: cfg.label.split('').join('\n'), // 换行
                  y: 108- h / 2+ cfg.label.length * 8,
                },
              })
            }
            return keyShape
          },
          update: undefined,
        },
        'rect'
      )

      // 自定义 flow-line 线
      G6.registerEdge('flow-line', {
   
   
        draw(cfg, group) {
   
   
          const startPoint = cfg.startPoint
          const endPoint = cfg.endPoint
          const {
   
    style } = cfg
          const shape = group.addShape('path', {
   
   
            attrs: {
   
   
              stroke: style.stroke,
              lineWidth: 2,// 描边粗细
              lineCap: 'round', // 设置线条的结束端点样式
              lineJoin: 'round', // 两条线相交时,所创建的拐角形状(bevel斜角、round圆角、miter尖角)
              endArrow: style.endArrow,
              path: [
                ['M', startPoint.x, startPoint.y],
                ['V', (startPoint.y) + 150], // 画一条垂直线到指定x坐标
                ['H', endPoint.x - 0],
                ['L', endPoint.x, endPoint.y],
              ],
              // path: [
              //   ['M', startPoint.x, startPoint.y],
              //   ['L', startPoint.x, (startPoint.y + endPoint.y) / 2],
              //   ['L', endPoint.x, (startPoint.y + endPoint.y) / 2],
              //   ['L', endPoint.x, endPoint.y],
              // ],
            },
          })
          return shape
        },
      })

      // 机构树 DOM 节点
      const container = document.getElementById("org-tree")
      const width = container.scrollWidth
      const height = container.scrollHeight

      // 实例化树图
      const that = this
      that.graph = new G6.TreeGraph({
   
   
        container: 'org-tree',
        width,
        height,
        linkCenter: true, // 设置边连入节点的中心,以保证美观性
        modes: {
   
   
          default: ['drag-canvas', 'zoom-canvas'],
          // default: ['drag-canvas', 'drag-node'],
        }, // 树图模式

        // 节点总览:https://g6.antv.antgroup.com/manual/middle/elements/nodes/default-node
        defaultNode: {
   
   
          type: 'icon-node', // 指定节点类型 icon-node、circle、rect
          // size: [120, 40],
          size: [40, 216], // 节点的大小 [120, 40]、[48, 216]
          style: {
   
   
            fill: '#000',
            stroke: 'l(30) 0:#ffffff 0.5:#7ec2f3 1:#1890ff', // #40a9ff、l(270) 0:#ffffff 0.5:#7ec2f3 1:#1890ff
            radius: 4, // 描边圆角
            lineWidth: 2,// 描边粗细
            // lineDash: [3, 5], // 描边虚线,数组代表实、虚长度
          },
          labelCfg: {
   
   
            style: {
   
   
              fill: '#fff',
              fontSize: 14,
              fontWeight: 'bolder',
              textAlign:'center',
            },
          }, // 文本配置项
        },

        // 边总览:https://g6.antv.antgroup.com/manual/middle/elements/edges/default-edge
        defaultEdge: {
   
   
          type: 'flow-line', // 指定边的类型 line、flow-line
          style: {
   
   
            stroke: 'l(45) 0:#ffffff 0.5:#27bda8 1:#5e7ce0', // #5294FF、l(270) 0:#ffffff 0.5:#7ec2f3 1:#1890ff
            fillOpacity: 0.5,
            lineOpacity: 0.1,
            shadowBlur: 10,
            // endArrow: {
   
   
            //   path: "M 0,0 L 12, 6 L 9,0 L 12, -6 Z",
            //   fill: "#5294FF",
            //   d: -110,
            // }, // 结束箭头
          }, // 边的样式属性
        },

        // 节点状态:https://g6.antv.antgroup.com/manual/middle/states/state
        nodeStateStyles: {
   
   
          type: 'polyline-edge',
          hover: {
   
   
            stroke: "#1890ff",
            lineWidth: 2,
          },
        },

        // 边状态:https://g6.antv.antgroup.com/manual/middle/states/state
        edgeStateStyles: {
   
   },

        layout: {
   
   
          type: 'compactBox', // 布局类型,支持 dendrogram、compactBox、mindmap、indeted
          direction: 'TB', // 布局方向,有  LR , RL , TB , BT , H , V ,说明(L:左;R:右;T:上;B:下;H:垂直;V:水平)
          getId: (d) => {
   
   return d.id},
          getWidth: () => {
   
   return 80}, // 每个节点的宽度
          getHeight: () => {
   
   return 200}, // 每个节点的高度
          getVGap: () => {
   
   return 50}, // 每个节点的水平间隙
          getHGap: () => {
   
   return 40}, // 每个节点的垂直间隙
        },
      })

      that.graph.data(this.treeData) // 载入数据
      that.graph.render() // 渲染视图
      that.graph.fitView() // 自适应画布

      // 监听鼠标移入事件
      that.graph.on('node:mouseenter', (evt) => {
   
   
        const {
   
    item } = evt
        that.graph.setItemState(item, 'hover', true)
      })

      // 监听鼠标移出事件
      that.graph.on('node:mouseleave', (evt) => {
   
   
        const {
   
    item } = evt
        that.graph.setItemState(item, 'hover', false)
      })

      // 监听画布缩放事件
      if (typeof window !== 'undefined')
        window.addEventListener('resize', () => {
   
   
          if (!that.graph || that.graph.get('destroyed'))
            return

          if (!container || !container.scrollWidth || !container.scrollHeight)
            return

          that.graph.changeSize(0, 0)
          that.graph.changeSize(container.scrollWidth, container.scrollHeight-3)
          that.graph.fitView()
        })
    },
  },
}
</script>

<style lang="less" scoped>
.index {
   
   
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background-color: #000;

  #org-tree {
   
   
    width: 100%;
    height: 100%;
  }
}
</style>

二、运行效果

目录
相关文章
|
3天前
|
JavaScript 容器
乾坤qiankun框架搭建 主应用为vue3的项目。
乾坤qiankun框架搭建 主应用为vue3的项目。
19 2
|
2天前
|
缓存 JavaScript 前端开发
《基础篇第4章:vue2基础》:使用vue脚手架创建项目
《基础篇第4章:vue2基础》:使用vue脚手架创建项目
10 3
|
3天前
|
JavaScript
vue3 生命周期
【10月更文挑战第14天】vue3 生命周期
|
4天前
|
JavaScript 前端开发
Vue 2 和 Vue 3 之间响应式区别
10月更文挑战第7天
16 2
|
5天前
|
存储 前端开发 中间件
vue3之vite配置vite-plugin-mock使用mock轻松创建模拟数据提高开发效率
vue3之vite配置vite-plugin-mock使用mock轻松创建模拟数据提高开发效率
26 0
|
2天前
|
JavaScript 前端开发 API
深入探索挖掘vue3 生命周期
【10月更文挑战第10天】
11 0
|
5天前
|
API
vue3中父子组件的双向绑定defineModel详细使用方法
vue3中父子组件的双向绑定defineModel详细使用方法
45 0
|
4天前
|
JavaScript 前端开发 开发者
Vue v-for 进阶指南:in 与 of 的区别及应用场景 | 笔记
Vue.js 中的 v-for 是强大的遍历指令,但其中的 in 和 of 关键字往往被开发者忽视。尽管它们的用法相似,但适用的场景和数据结构却各有不同。本文将详细探讨 v-for 中 in 和 of 的区别、适用场景以及在实际开发中的最佳使用时机。通过理解它们的差异,你将能够编写更加高效、简洁的 Vue.js 代码,灵活应对各种数据结构的遍历需求。
40 6
|
2天前
|
缓存 JavaScript
Vue 中 computed 与 method 的区别
【10月更文挑战第15天】computed 和 method 是 Vue 中两个重要的选项,它们在功能和特点上存在着明显的区别。理解并合理运用它们的区别,可以帮助我们构建更高效、更具可维护性的 Vue 应用。在实际开发中,要根据具体情况灵活选择使用,以满足不同的需求。
5 2
|
2天前
|
JavaScript 搜索推荐 UED
vue的自定义指令
【10月更文挑战第14天】Vue 自定义指令为我们提供了一种强大的工具,使我们能够更灵活地控制和扩展 Vue 应用的行为。通过合理地使用自定义指令,可以提高开发效率,增强应用的功能和用户体验。