react使用AntvX6实现流程图

如何使用AntV X6在react中插入流程图功能?

一、创建画布

首先要先创建一个id为container的div盒子,然后

this.graph = new Graph({
      container: document.getElementById('container')!,//获取盒子
      width: 800,
      height: 600,
      // 网格
      grid: {
        size: 10,
        visible: true,
        type: 'dot',//默认是点状,也可以选择mesh
        args: [
          {
            color: '#a0a0a0',//网格的颜色
            thickness: 1.5,//网格宽度
          },
        ],
      },
      panning: {
        enabled: true,
        eventTypes: ['leftMouseDown', 'rightMouseDown', 'mouseWheel'],
        modifiers: 'ctrl',
      },
      // 鼠标滚轮的默认行为是滚动页面
      mousewheel: {
        enabled: true,
        zoomAtMousePosition: true,
        modifiers: 'ctrl',
        minScale: 0.5,
        maxScale: 3,
      },
      // 节点连接
      connecting: {
        router: 'orth', //manhattan
        connector: {
          name: 'rounded',
          args: {
            radius: 8,
          },
        },
        anchor: 'center',
        connectionPoint: 'anchor',
        snap: true, // 自动吸附
        allowBlank: false, // 是否允许连接到画布空白位置的点
        allowLoop: false, // 是否允许创建循环连线,即边的起始节点和终止节点为同一节点
        allowNode: false, // 是否允许边链接到节点(非节点上的链接桩)
        createEdge() {
          return new Shape.Edge({
            attrs: {
              line: {
                stroke: '#A2B1C3',
                strokeWidth: 1,
                targetMarker: {
                  name: 'block',
                  width: 12,
                  height: 8,
                },}
            },
            zIndex: 0,
          })
        },
        validateConnection({ targetMagnet }) {
          return !!targetMagnet
        },
      },
      // 高亮
      highlighting: {
        magnetAdsorbed: {
          name: 'stroke',
          args: {
            attrs: {
              fill: '#D06269',
              stroke: '#D06269',
            },
          },
        },
      },
      resizing: true, // 缩放节点,默认禁用
      rotating: true, // 旋转节点,默认禁用
      // 启动选择节点
      selecting: {
        enabled: true,
        rubberband: true,
        showNodeSelectionBox: true,
      },
      snapline: true, // 对齐线
      keyboard: true, // 键盘快捷键,默认禁用
      history: true, // 启动历史记录
      // 小地图,默认禁用
      minimap: {
        enabled: true,
        container: document.getElementById('minimap')!,
        width: 198,
        height: 198,
        padding: 10,
      },
      clipboard: true, // 剪切板,默认禁用
    })

在画布中可以定义画布的大小、网格类型(默认是点状,可以选择珊格,还可以自定义)、背景颜色等等

二、创建左侧流程图画板

  private static initStencil() {
    const self = this
    this.stencil = new Addon.Stencil({
      title: 'FlowGraph',
      target: this.graph,
      stencilGraphWidth: 214,
      stencilGraphHeight: document.body.offsetHeight - 96,//-96
      layoutOptions: {
        columns: 2,
        columnWidth: 80,
        rowHeight: 60,
        marginY: 30,
        marginX: 10
      },
      
      //获取拖拉节点
      getDropNode(node: any) {
        const size = node.size()
        const { type } = node.store.data
        const label: String = self.getLabel(type)
        return node.clone().size(size.width * 1, size.height * 1).attr('label/text', label) //右侧获取label,这里是从画板上拖拉下来的节点的大小,这里*1表示,大小不变,与画板上的节点大小一致
        // return node.clone().size(size.width * 1, size.height * 1)
      }
      
    })

    const stencilContainer = document.querySelector('#stencil')!
    if (stencilContainer) {
      stencilContainer.appendChild(this.stencil.container)
    }
  }
其中getLabel:
  private static getLabel(type: String) {
    let label: String = ''
    
    switch(type) {
      case 'rect':
        label = '矩形节点'
        break
      case 'rect-radius':
        label = '圆角矩形节点'
        break
      case 'polygon-rhombus':
        label = '菱形节点'
        break
      case 'polygon-rhomboid':
        label = '四边形节点'
        break
      case 'circle':
        label = '圆形节点'
        break
    }

    return label
  }

三、给左侧画板添加流程图节点样式

  private static initShape() {
    const graph = this.graph

    // #region 初始化图形
    //定义连接桩
    const ports = {
    groups: {
    top: {
      position: 'top',
      attrs: {
        circle: {
          r: 4,
          magnet: true,
          stroke: '#5F95FF',
          strokeWidth: 1,
          fill: '#fff',
          style: {
            visibility: 'hidden', //hidden
          },
        },
      },
    },
    right: {
      position: 'right',
      attrs: {
        circle: {
          r: 4,
          magnet: true,
          stroke: '#5F95FF',
          strokeWidth: 1,
          fill: '#fff',
          style: {
            visibility: 'hidden',
          },
        },
      },
    },
    bottom: {
      position: 'bottom',
      attrs: {
        circle: {
          r: 4,
          magnet: true,
          stroke: '#5F95FF',
          strokeWidth: 1,
          fill: '#fff',
          style: {
            visibility: 'hidden',
          },
        },
      },
    },
    left: {
      position: 'left',
      attrs: {
        circle: {
          r: 4,
          magnet: true,
          stroke: '#5F95FF',
          strokeWidth: 1,
          fill: '#fff',
          style: {
            visibility: 'hidden',
          },
        },
      },
    },
    },
    items: [
    {
      group: 'top',
    },
    {
      group: 'right',
    },
    {
      group: 'bottom',
    },
    {
      group: 'left',
    },
    ],
    }
    //定义节点
    Graph.registerNode(
  'custom-rect',
  {
    inherit: 'rect',
    width: 66,
    height: 36,
    attrs: {
      body: {
        strokeWidth: 1,
        stroke: '#5F95FF',
        fill: '#EFF4FF',
      },
      text: {
        fontSize: 12,
        fill: '#262626',
      },
    },
    ports: { ...ports },
  },
  true,
    )
    Graph.registerNode(
  'custom-polygon',
  {
    inherit: 'polygon',
    width: 66,
    height: 36,
    attrs: {
      body: {
        strokeWidth: 1,
        stroke: '#5F95FF',
        fill: '#EFF4FF',
      },
      text: {
        fontSize: 12,
        fill: '#262626',
      },
    },
    ports: {
      ...ports,
      items: [
        {
          group: 'top',
        },
        {
          group: 'bottom',
        },
      ],
    },
  },
  true,
    )
    Graph.registerNode(
  'custom-circle',
  {
    inherit: 'circle',
    width: 45,
    height: 45,
    attrs: {
      body: {
        strokeWidth: 1,
        stroke: '#5F95FF',
        fill: '#EFF4FF',
      },
      text: {
        fontSize: 12,
        fill: '#262626',
      },
    },
    ports: { ...ports },
  },
  true,
    )
    Graph.registerNode(
  'custom-image',
  {
    inherit: 'rect',
    width: 52,
    height: 52,
    markup: [
      {
        tagName: 'rect',
        selector: 'body',
      },
      {
        tagName: 'image',
      },
      {
        tagName: 'text',
        selector: 'label',
      },
    ],
    attrs: {
      body: {
        stroke: '#5F95FF',
        fill: '#5F95FF',
      },
      image: {
        width: 26,
        height: 26,
        refX: 13,
        refY: 16,
      },
      label: {
        refX: 3,
        refY: 2,
        textAnchor: 'left',
        textVerticalAnchor: 'top',
        fontSize: 12,
        fill: '#fff',
      },
    },
    ports: { ...ports },
  },
  true,
    )
    //定义边的样式
    // Graph.registerEdge(
    //   'custom-edge',{
    //     inherit: 'edge',
    //     attrs: {
    //       line: {
    //         stroke: '#A2B1C3',
    //         strokeWidth: 1,
    //         targetMarker: {
    //           name: 'block',
    //           width: 12,
    //           height: 8,
    //         },
    //       },
    //     },
    //     ports: { ...ports }
    //   }
    // )
    const r1 = graph.createNode({
      shape: 'custom-rect',
      label: '开始',
      attrs: {
        body: {
          rx: 20,
          ry: 26,
        },
      },
      ports: { ...ports }
    })
    const r2 = graph.createNode({
      shape: 'custom-rect',
      label: '过程',
      ports: { ...ports }
    })
    const r3 = graph.createNode({
      shape: 'custom-rect',
      attrs: {
        body: {
          rx: 6,
          ry: 6,
        },
      },
      label: '可选过程',
      ports: { ...ports }
    })
    const r4 = graph.createNode({
      shape: 'custom-polygon',
      attrs: {
        body: {
          refPoints: '0,10 10,0 20,10 10,20',
        },
      },
      label: '决策',
      ports: { ...ports }
    })
    const r5 = graph.createNode({
      shape: 'custom-polygon',
      attrs: {
        body: {
          refPoints: '10,0 40,0 30,20 0,20',
        },
      },
      label: '数据',
      ports: { ...ports }
    })
    const r6 = graph.createNode({
      shape: 'custom-circle',
      label: '连接',
      ports: { ...ports }
    })
    this.stencil.load([r1, r2, r3, r4, r5, r6])
  }

在这里可以注册节点和边的样式,节点和边以及连接桩的样式均可以自定义。连接桩就是节点的连接点,对流程图而言一般默认是上下左右四个。

四、初始化注册事件

  private static initEvent() {
    const graph = this.graph
    const container = document.getElementById('container')!

    // 节点鼠标移入
    graph.on('node:mouseenter', FunctionExt.debounce((nodeAttr: any) => {
      // 显示连接点
      const ports = container.querySelectorAll(
        '.x6-port-body',
      ) as NodeListOf<SVGElement>
      this.showPorts(ports, true)
      
      // 添加删除
      const { node } = nodeAttr
      const { width } = node.store.data.size
      node.addTools({
        name: 'button-remove',
        args: {
          x: 0,
          y: 0,
          offset: { x: 0, y: 0 },
        },
      })
      
    }), 500)
    // 节点鼠标移出
    graph.on('node:mouseleave', ({ node }) => {
      const ports = container.querySelectorAll(
        '.x6-port-body',
      ) as NodeListOf<SVGElement>
      this.showPorts(ports, false)

      // 移除删除
      node.removeTools()
    })
    // 连接线鼠标移入
    graph.on('edge:mouseenter', ({ edge }) => {
      // 添加删除
      edge.addTools([
        'source-arrowhead',
        'target-arrowhead',
        {
          name: 'button-remove',
          args: {
            distance: -30,
          }
        }
      ])
    })
    graph.on('edge:mouseleave', ({ edge }) => {
      // 移除删除
      edge.removeTools()
    })
  }

设置节点和边的鼠标事件。

至此基础功能就可以实现了。

后面还可以添加toolbar实现保存、撤销、重做、删除、放大缩小等功能。官网有具体的实现方法。

AntV X6有提供graph.fromJSON()和toJSON()的方法可以将图转换为数据,再将数据转换为图。可以使用这个实现在localStorage中的保存。

另外,节点可以添加修改功能编辑节点的名称。

参考:react-antvx6-app: react typescript 运用 @antv-x6、antd、@antv/x6-react-components 实现基本流程图编辑

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值