树形流程图-G6

前几天接到了个需求,做一个树形的流程图,大概形状类似于antV-G6里面的自定义树图

antv-G6自定义树图链接:定制树图元素样式 | G6

但是需求:父节点和子节点能够展开和关闭,还要有这个功能

emmm..对于每接触过G6的小白来说...真的难哭了

我在网上一顿找..发现并没有合适的可以直接用的..只能自己去改造,但是改造的前提的我得先学G6,所以这篇是我对G6给的树图的学习--大家可以参考 

首先肯定是要下载G6了--官网

 npm install --save @antv/g6

注意:下载之后启动,如果报错core.js的问题,可以下载一下他的相对最新版

npm install [email protected]

下面就是对G6各个配置项的学习注释了

import G6 from '@antv/g6'

// 折叠图标函数
const COLLAPSE_ICON = function COLLAPSE_ICON (x, y, r) {
  return [
    ['M', x - r, y - r],
    ['a', r, r, 0, 1, 0, r * 2, 0],
    ['a', r, r, 0, 1, 0, -r * 2, 0],
    ['M', x + 2 - r, y - r],
    ['L', x + r - 2, y - r]
  ]
}
// 展开图标函数
const EXPAND_ICON = function EXPAND_ICON (x, y, r) {
  return [
    ['M', x - r, y - r],
    ['a', r, r, 0, 1, 0, r * 2, 0],
    ['a', r, r, 0, 1, 0, -r * 2, 0],
    ['M', x + 2 - r, y - r],
    ['L', x + r - 2, y - r],
    ['M', x, y - 2 * r + 2],
    ['L', x, y - 2]
  ]
}
// 树形图数据
const data = {
  id: 'root',
  label: 'root',
  children: [
    {
      id: 'c1',
      label: 'c1',
      children: [
        {
          id: 'c1-1',
          label: 'c1-1'
        },
        {
          id: 'c1-2',
          label: 'c1-2',
          children: [
            {
              id: 'c1-2-1',
              label: 'c1-2-1'
            },
            {
              id: 'c1-2-2',
              label: 'c1-2-2'
            }
          ]
        }
      ]
    },
    {
      id: 'c2',
      label: 'c2'
    },
    {
      id: 'c3',
      label: 'c3',
      children: [
        {
          id: 'c3-1',
          label: 'c3-1'
        },
        {
          id: 'c3-2',
          label: 'c3-2',
          children: [
            {
              id: 'c3-2-1',
              label: 'c3-2-1'
            },
            {
              id: 'c3-2-2',
              label: 'c3-2-2'
            },
            {
              id: 'c3-2-3',
              label: 'c3-2-3'
            }
          ]
        },
        {
          id: 'c3-3',
          label: 'c3-3'
        }
      ]
    }
  ]
}
// 深度优先遍历树数据 从根到子
G6.Util.traverseTree(data, (d) => {
  d.leftIcon = {
    style: {
      fill: '#e6fffb',
      stroke: '#e6fffb'
    },
    img: 'https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*Q_FQT6nwEC8AAAAAAAAAAABkARQnAQ'
  }
  return true
})

// 自定义元素 G6.registerX
// 自定义节点-当内置节点不满足需求时,可以通过 G6.registerNode(nodeName, options, extendedNodeName) 方法自定义节点。
// nodeName 自定义节点的名称 唯一性  必须
// options,自定义节点的配置项是个对象 必须
// extendedNodeName 可以基于内置节点自定义节点
G6.registerNode(
  'icon-node',
  {
    options: {
      size: [60, 20], // 宽高
      stroke: '#91d5ff', // 变颜色
      fill: '#91d5ff'// 填充色
    },
    // draw是绘制后的附加操作-节点的配置项  图形分组,节点中图形对象的容器
    draw (cfg, group) {
      // 获取节点的配置
      const styles = this.getShapeStyle(cfg)
      // 解构赋值
      const { labelCfg = {} } = cfg

      const w = styles.width
      const h = styles.height
      // 向分组中添加新的图形 图形 配置 rect矩形 xy 代表左上角坐标 w h是宽高
      const keyShape = group.addShape('rect', {
        attrs: {
          ...styles,
          x: -w / 2,
          y: -h / 2
        }
      })

      console.log('cfg.leftIcon', cfg.leftIcon)
      // 如果有左侧图标 向分组中添加新的图形的配置
      if (cfg.leftIcon) {
        const { style, img } = cfg.leftIcon
        group.addShape('rect', {
          attrs: {
            x: 1 - w / 2,
            y: 1 - h / 2,
            width: 38,
            height: styles.height - 2,
            fill: '#8c8c8c',
            ...style
          }
        })

        group.addShape('image', {
          attrs: {
            x: 8 - w / 2,
            y: 8 - h / 2,
            width: 24,
            height: 24,
            img:
              img ||
              'https://g.alicdn.com/cm-design/arms-trace/1.0.155/styles/armsTrace/images/TAIR.png'
          },
          name: 'image-shape'
        })
      }

      // 如果不需要动态增加或删除元素,则不需要 add 这两个 marker
      group.addShape('marker', {
        attrs: {
          x: 40 - w / 2,
          y: 52 - h / 2,
          r: 6,
          stroke: '#73d13d',
          cursor: 'pointer',
          symbol: EXPAND_ICON
        },
        name: 'add-item'
      })

      group.addShape('marker', {
        attrs: {
          x: 80 - w / 2,
          y: 52 - h / 2,
          r: 6,
          stroke: '#ff4d4f',
          cursor: 'pointer',
          symbol: COLLAPSE_ICON
        },
        name: 'remove-item'
      })
      // 文本文字的配置
      if (cfg.label) {
        group.addShape('text', {
          attrs: {
            ...labelCfg.style,
            text: cfg.label,
            x: 50 - w / 2,
            y: 25 - h / 2
          }
        })
      }

      return keyShape
    },
    // 更新节点后的操作,一般同 afterDraw 配合使用
    update: undefined
  },
  'rect'
)

// 自定义边
// 参数1 自定义边的名称 参数2 自定义边时的配置项(对象) 参数3 自定义边时可基于内置边进行定义(选填)
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, // 边框的样式
        endArrow: style.endArrow, // 结束箭头
        // 路径
        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
  }
})

// 默认的鼠标悬停会加粗,边框颜色改变
const defaultStateStyles = {
  hover: {
    stroke: '#1890ff',
    lineWidth: 2
  }
}

// 默认节点的颜色 边 圆角的配置
const defaultNodeStyle = {
  fill: '#91d5ff',
  stroke: '#40a9ff',
  radius: 5
}

// 默认边的颜色 末尾箭头
const defaultEdgeStyle = {
  stroke: '#91d5ff',
  endArrow: {
    path: 'M 0,0 L 12, 6 L 9,0 L 12, -6 Z',
    fill: '#91d5ff',
    d: -20
  }
}

// 默认布局
// compactBox 紧凑树布局
// 从根节点开始,同一深度的节点在同一层,并且布局时会将节点大小考虑进去。
const defaultLayout = {
  type: 'compactBox', // 布局类型树
  direction: 'TB', // TB 根节点在上,往下布局
  getId: function getId (d) { // 节点 id 的回调函数
    return d.id
  },
  getHeight: function getHeight () { // 节点高度的回调函数
    return 16
  },
  getWidth: function getWidth () { // 节点宽度的回调函数
    return 16
  },
  getVGap: function getVGap () { // 节点纵向间距的回调函数
    return 40
  },
  getHGap: function getHGap () { // 节点横向间距的回调函数
    return 70
  }
}

// 文本配置项
const defaultLabelCfg = {
  style: {
    fill: '#000',
    fontSize: 12
  }
}

// 获取容器
const container = document.getElementById('container')
// 获取容器的宽高
const width = container.scrollWidth
const height = container.scrollHeight || 500

// 实例化 Grid 插件-小地图
const minimap = new G6.Minimap({
  size: [150, 100]
})
// 实例化 Menu 插件-右击
const menu = new G6.Menu({
  offsetX: 6,
  offsetY: 10,
  itemTypes: ['node'],
  getContent (e) {
    const outDiv = document.createElement('div')
    outDiv.style.width = '180px'
    outDiv.innerHTML = `<ul>
        <li>测试01</li>
        <li>测试01</li>
        <li>测试01</li>
        <li>测试01</li>
        <li>测试01</li>
      </ul>`
    return outDiv
  }
})

// Graph 是 G6 图表的载体-实例化
const graph = new G6.TreeGraph({
  container: 'container', // 图的 DOM 容器
  width,
  height,
  linkCenter: true, // 指定边是否连入节点的中心
  plugins: [minimap, menu], // 插件
  modes: { // 交互模式
    // default 模式中包含点击选中节点行为和拖拽画布行为;
    default: ['drag-canvas', 'zoom-canvas']
  },
  // 默认状态下节点的配置
  defaultNode: {
    type: 'icon-node',
    size: [120, 40],
    style: defaultNodeStyle,
    labelCfg: defaultLabelCfg
  },
  // 默认状态下边的配置,
  defaultEdge: {
    type: 'flow-line',
    style: defaultEdgeStyle
  },
  // 各个状态下节点的样式-,例如 hover、selected,3.1 版本新增。
  nodeStateStyles: defaultStateStyles,

  // 各个状态下边的样式-,例如 hover、selected,3.1 版本新增。
  edgeStateStyles: defaultStateStyles,
  // 布局配置项
  layout: defaultLayout
})
// 初始化的图数据,是一个包括 nodes 数组和 edges 数组的对象

graph.data(data)

// 根据提供的数据渲染视图。
graph.render()
// 让画布内容适应视口。
graph.fitView()
// 改变视口的缩放比例,在当前画布比例下缩放,是相对比例。
graph.zoom(1)

// 鼠标移入元素范围内触发
graph.on('node:mouseenter', (evt) => {
  const { item } = evt
  graph.setItemState(item, 'hover', true)
})
// 鼠标移出目标元素后触发
graph.on('node:mouseleave', (evt) => {
  const { item } = evt// item表示触发元素
  graph.setItemState(item, 'hover', false)
})

// 单击鼠标左键或者按下回车键时触发
graph.on('node:click', (evt) => {
// item: 事件的触发元素(节点/边/ Combo)
// target: 事件的触发图形 Shape 或画布对象
  const { item, target } = evt
  // type: 事件类型
  // name: 事件名称
  const targetType = target.get('type')
  const name = target.get('name')

  // 增加元素
  if (targetType === 'marker') {
    const model = item.getModel()
    if (name === 'add-item') {
      if (!model.children) {
        model.children = []
      }
      const id = `n-${Math.random()}`
      model.children.push({
        id,
        label: id.substr(0, 8),
        leftIcon: {
          style: {
            fill: '#e6fffb',
            stroke: '#e6fffb'
          },
          img:
            'https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*Q_FQT6nwEC8AAAAAAAAAAABkARQnAQ'
        }
      })
      graph.updateChild(model, model.id)
    } else if (name === 'remove-item') {
      // // 移除节点
      graph.removeChild(model.id)
    }
  }
})

if (typeof window !== 'undefined') {
  window.onresize = () => {
    if (!graph || graph.get('destroyed')) return
    if (!container || !container.scrollWidth || !container.scrollHeight) return
    graph.changeSize(container.scrollWidth, container.scrollHeight)
  }
}

下面的是我根据G6树图的改造的 (在vue组件中写的)--效果图

 

<template>
  <div id="container">
  </div>
</template>

<script>
// 引入antv-G6
import G6 from '@antv/g6'
// 树图的数据
import { treedata } from '@/common/treedata'
// G6的配置项
G6.registerNode(
  'icon-node',
  {
    options: {
      size: [60, 20], // 宽高
      stroke: '#91d5ff', // 变颜色
      fill: '#fff'// 填充色
    },
    // draw是绘制后的附加操作-节点的配置项  图形分组,节点中图形对象的容器
    draw (cfg, group) {
      // 获取节点的配置
      const styles = this.getShapeStyle(cfg)
      // 解构赋值
      const { labelCfg = {} } = cfg

      const w = styles.width
      const h = styles.height
      // 向分组中添加新的图形 图形 配置 rect矩形 xy 代表左上角坐标 w h是宽高
      const keyShape = group.addShape('rect', {
        attrs: {
          ...styles,
          x: -w / 2,
          y: -h / 2
        }
      })

      // 文本文字的配置
      if (cfg.label) {
        group.addShape('text', {
          attrs: {
            ...labelCfg.style,
            text: cfg.label,
            x: 50 - w / 2,
            y: 25 - h / 2
          }
        })
      }

      return keyShape
    },
    // 更新节点后的操作,一般同 afterDraw 配合使用
    update: undefined
  },
  'rect'
)
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, // 边框的样式
        endArrow: style.endArrow, // 结束箭头
        // 路径
        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
  }
})
// 默认的鼠标悬停会加粗,边框颜色改变
const defaultStateStyles = {
  hover: {
    stroke: '#1890ff',
    lineWidth: 2
  }
}

// 默认节点的颜色 边 圆角的配置
const defaultNodeStyle = {
  fill: '#fff',
  stroke: '#ccc',
  radius: 5
}

// 默认边的颜色 末尾箭头
const defaultEdgeStyle = {
  stroke: '#ccc'
}

// 默认布局
// compactBox 紧凑树布局
// 从根节点开始,同一深度的节点在同一层,并且布局时会将节点大小考虑进去。
const defaultLayout = {
  type: 'compactBox', // 布局类型树
  direction: 'TB', // TB 根节点在上,往下布局
  getId: function getId (d) { // 节点 id 的回调函数
    return d.id
  },
  getHeight: function getHeight () { // 节点高度的回调函数
    return 16
  },
  getWidth: function getWidth () { // 节点宽度的回调函数
    return 16
  },
  getVGap: function getVGap () { // 节点纵向间距的回调函数
    return 40
  },
  getHGap: function getHGap () { // 节点横向间距的回调函数
    return 70
  }
}

// 文本配置项
const defaultLabelCfg = {
  style: {
    fill: '#000',
    fontSize: 15
  }
}

export default {
  name: 'Home',

  data () {
    return {
      // 树形图的数据
    //   treedata: treedata
    }
  },
  methods: {
    G6init () {
      if (typeof window !== 'undefined') {
        window.onresize = () => {
          if (!graph || graph.get('destroyed')) return
          if (!container || !container.scrollWidth || !container.scrollHeight) return
          graph.changeSize(container.scrollWidth, container.scrollHeight)
        }
      }
      // 获取容器
      const container = document.getElementById('container')
      // 获取容器的宽高
      const width = container.scrollWidth
      const height = container.scrollHeight || 500

      const menu = new G6.Menu({
        offsetX: 10,
        offsetY: 10,
        itemTypes: ['node'],
        getContent (e) {
          const outDiv = document.createElement('div')
          outDiv.style.width = '100px'
          outDiv.innerHTML = `<ul>
           <li>增加节点</li>
           <li>删除节点</li>
           <li>查看节点</li>
           <li>编辑节点</li>
           </ul>`
          return outDiv
        }
      })

      // Graph 是 G6 图表的载体-实例化
      const graph = new G6.TreeGraph({
        container: 'container', // 图的 DOM 容器
        width,
        height,
        linkCenter: true, // 指定边是否连入节点的中心
        plugins: [menu], // 插件  minimap
        modes: { // 交互模式
          // default 模式中包含点击选中节点行为和拖拽画布行为;
          default: [
            {
            // 这个是可以展开可以收起
              type: 'collapse-expand',
              onChange: function onChange (item, collapsed) {
                const data = item.get('model')
                data.collapsed = collapsed
                return true
              }
            },
            'drag-canvas',
            'zoom-canvas'
          ]
        },
        // 默认状态下节点的配置
        defaultNode: {
          type: 'icon-node',
          size: [120, 40],
          style: defaultNodeStyle,
          labelCfg: defaultLabelCfg
        },
        // 默认状态下边的配置,
        defaultEdge: {
          type: 'flow-line',
          style: defaultEdgeStyle
        },
        // 各个状态下节点的样式-,例如 hover、selected,3.1 版本新增。
        nodeStateStyles: defaultStateStyles,

        // 各个状态下边的样式-,例如 hover、selected,3.1 版本新增。
        edgeStateStyles: defaultStateStyles,
        // 布局配置项
        layout: defaultLayout
      })

      graph.data(treedata)

      // 根据提供的数据渲染视图。
      graph.render()
      // 让画布内容适应视口。
      graph.fitView()
      // 改变视口的缩放比例,在当前画布比例下缩放,是相对比例。
      graph.zoom(1)
    }
  },
  mounted () {
    this.G6init()
  }
}
</script>
<style scoped>
#container{
    height: 100%;
    width: 100%;
}
</style>

数据用的假数据 treedata.js

// 属性图的数据
export const treedata = {
  id: 'root',
  label: '根',
  children: [
    {
      id: '1',
      label: '一级',
      children: [
        {
          id: '1-1',
          label: '二级'
        },
        {
          id: '1-2',
          label: '二级',
          children: [
            {
              id: '1-2-1',
              label: '三级'
            },
            {
              id: '1-2-2',
              label: '三级'
            }
          ]
        }
      ]
    },
    {
      id: '2',
      label: '一级'
    },
    {
      id: '3',
      label: '一级',
      children: [
        {
          id: '3-1',
          label: '二级'
        },
        {
          id: '3-2',
          label: '二级',
          children: [
            {
              id: '3-2-1',
              label: '三级'
            },
            {
              id: '3-2-2',
              label: '三级'
            },
            {
              id: '3-2-3',
              label: '三级'
            }
          ]
        },
        {
          id: '3-3',
          label: '二级'
        }
      ]
    }
  ]
}

               哎,就是玩~    小白入手G6树第一步!

[添加,删除,修改,查看功能还没写~慢慢来]

猜你喜欢

转载自blog.csdn.net/lilikk1314/article/details/120482873
今日推荐