使用antv/x6绘制vue流程表单

Antv/X6介绍

简介

X6 是基于 HTML 和 SVG 的图编辑引擎,提供低成本的定制能力和开箱即用的内置扩展,方便我们快速搭建 DAG 图、ER 图、流程图、血缘图等应用。 图编辑核心能力:节点、连线与画布,可使用React、Vue、Angular组件节点

开始使用

创建画布

容器 复制代码
<div ref="containerRef" class="x6-container"></div>
csharp 复制代码
import { ref, watch, onMounted, onUnmounted } from "vue"
import { Graph } from "@antv/x6";
const containerRef = ref(ref)
const graph = new Graph({
  container: containerRef.value
})
    
onUnmounted(() => {
    graph.dispose() // 销毁
})

尺寸:初始创建可使用width:1200和height:600属性指定画布大小,默认是容器宽高

js 复制代码
const ports = {
    groups: {
      top: {
        position: "top",
        attrs: {
          circle: {
            r: 4,
            magnet: true,
            stroke: "#5F95FF",
            strokeWidth: 1,
            fill: "#fff",
            style: {
              visibility: "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"
      }
    ]
  }
  
  new Graph({
  container: containerRef.value,
  ports: {
      ...ports
  }
})

使用ports指定连接桩内置有上下左右点,按照节点尺寸设置,也可使用position: [88, 62]具体坐标添加连接桩,节点内部组件可以溢出节点显示(组件尺寸比节点指定尺寸大),可指定位置保证连接桩在显示区域边上

js 复制代码
  const nodeMouseenter = () => {
    const container = containerRef.value
    const ports = container.querySelectorAll(".x6-port-body")
    showPorts(ports, true)
  }
  const nodeMouseleave = () => {
    const container = containerRef.value
    const ports = container.querySelectorAll(".x6-port-body")
    showPorts(ports, false)
  }
    graph.on("node:mouseenter", nodeMouseenter)
    graph.on("node:mouseleave", nodeMouseleave)

连接桩默认隐藏,使用画布监听事件控制显示隐藏

js 复制代码
new Graph({
    connecting:{
        router: "manhattan",
        connector: {
          name: "rounded",
          args: {
            radius: 8
          }
        },
        anchor: "center",
        connectionPoint: "anchor",
        allowBlank: false,
        snap: {
          radius: 20
        },
        createEdge() {
          return new Shape.Edge({
            attrs: {
              line: {
                stroke: "#1890FF",
                strokeWidth: 2,
                targetMarker: {
                  name: "block",
                  width: 12,
                  height: 8
                }
              }
            },
            zIndex: 0
          })
        },
        validateConnection({ targetMagnet }) {
          return !!targetMagnet
        }
      }
  }

Edge(边/连线)使用connecting属性设置,画布手动连接的线会使用此配置,在创建Edge时需要单独设置,边的路径类型,颜色、箭头都可以设置

js 复制代码
import { Dnd } from "@antv/x6-plugin-dnd"
dnd = new Dnd({
  target: graph,
  scaled: false,
  dndContainer: nodeContainerRef.value,
  validateNode: (cls, node) => {
    ...
    return false
  }
})

X6通过Dnd组件内置节点拖拽功能,指定节点拖拽容器和画布,validateNode为拖拽回调,可在此控制节点添加行为或者使用内置的Stencil插件创建拖拽部分

js 复制代码
import { Scroller } from "@antv/x6-plugin-scroller"
 graph.use(
   new Scroller({
     enabled: true,
     pannable: false
   })
 )

X6内置了滚动功能,使用插件开启,可滚动和鼠标拖动,鼠标操作可能会有冲突按使用场景控制

js 复制代码
graph = new Graph({
      container: containerRef.value,
      interacting: () => {
        return false
      },
      ...
    })
javascript 复制代码
translating: {
        restrict(view) {
          if (view) {
            const cell = view.cell
            if (cell.isNode()) {
              const parent = cell.getParent()
              if (parent) {
                return parent.getBBox()
              }
            }
          }
          return null
        },
      },

X6内置多种方法可以控制画布节点拖动行为,可以使用tanslating设置控制,设置{ restrict: false/true }或者指定函数控制(可指定[x,y]范围控制),实际使用中控制节点完全不允许移动,实践下来translating单节点没有问题,Group添加子节点之后父节点移动控制有问题,姿势不对或者是bug,使用interacting属性可以控制节点完全不移动

js 复制代码
graph.on("node:mouseenter", ({ node }) => {
      node.addTools([
        {
          name: "button-remove",
          args: {
            x: node.shape === "node-select-point" ? 176 : 260,
            y: 0,
            onClick({ cell }) {
                graph.removeNode(cell)
            }
          }
        }
      ])
    })
    graph.on("node:mouseleave", ({ node }) => {
      if (node.hasTool("button-remove")) {
        node.removeTool("button-remove")
      }
    })

X6有内置小组件,可动态添加删除按钮删除节点,可使用onClick指定删除行为或者不指定使用默认删除

Node节点

js 复制代码
    graph.createNode({
      shape: 'rect',
      data: {
      },
      ...
    })

可以使用createNode方法或者addNode方法创建节点使用shape指定类型返回值为节点

js 复制代码
<template>
<TeleportContainer></TeleportContainer>
</template>
import { register, getTeleport } from "@antv/x6-vue-shape"
const TeleportContainer = getTeleport()

register({
      shape: '...',
      width: 100,
      height: 100,
      component: ...,
      ports: {
        ...ports
      }
    })
graph.createNode({
  shape: '...',
  data: {
    parent: null,
    children: []
  }
})

使用Vue组件节点需要先使用register注册shape,使用时创建节点指定shape名称即可

js 复制代码
    graph.on("cell:added", ({ cell, index, options }) => {
    })
    graph.on("cell:removed", ({ cell, index, options }) => {
    }

可监听事件回调,处理对用逻辑

X6节点有群组概念,可使用节点addChild方法添加子节点建立父子关系,可使用graph.getNodes方法获取节点(注:使用toJSON方法获取的节点没有cell类等继承方法),使用getNodes获取的都是节点类型,Group通过children获取的子节点包含节点和边

Edge边/连线

js 复制代码
graph.createEdge({
      source: { cell: id, port: id : id },
      target: { cell: id, port: id},
      router: "normal",
      attrs: {
        line: {
          stroke: "#586C90",
          strokeWidth: 2,
          targetMarker: {
            name: "block",
            width: 12,
            height: 8
          }
        }
      }
    })
    graph.addEdge(edge)

可使用createEdge或者addEdge方法创建边/连接线,指定连接点可使用节点和连接桩id建立连接或者直接指定连接坐标[x,y]来连线,手动创建连线需要指定连线配置,不共用画布连接线设置。

流程表单

使用Vue节点创建的组件和普通Vue节点没本质区别,但是其是由X6托管,和项目Vue组件之间没有关联关系,内部数据交互需要使用pinia/vuex或者node节点data来管理

js 复制代码
export default defineComponent({
  name: 'ProgressNode',
  inject: ['getNode'],
  data() {
    return {
      percentage: 80,
    }
  },
  mounted() {
    const node = (this as any).getNode() as Node
    node.on('change:data', ({ current }) => {
      const { progress } = current
      this.percentage = progress
    })
  },
})

Vue节点内部可使用inject("getNode")获取节点,通过监听节点方法来获取节点数据

js 复制代码
 import { useStore } from "@/store/..."
 const store = useStore()
 const getNode = inject("getNode")
  const node = getNode()
  const id = node.id

节点可使用节点id通过pinia来管理节点内部表单数据,外部组件和X6Vue节点交互都需要通过pinia来进行。数据变动通过watch监听pinia变动来更新。

可以通过toJSON和fromJSON方法来保存流程图和还原流程图,X6托管的Vue节点没有生命周期回调,还原表单数据是需要注意等Vue节点都mounted之后再触发pinia数据更新触发内部watch回填表单数据

相关推荐
程序员凡尘41 分钟前
完美解决 Array 方法 (map/filter/reduce) 不按预期工作 的正确解决方法,亲测有效!!!
前端·javascript·vue.js
Bug缔造者6 小时前
Element-ui el-table 全局表格排序
前端·javascript·vue.js
xnian_7 小时前
解决ruoyi-vue-pro-master框架引入报错,启动报错问题
前端·javascript·vue.js
罗政7 小时前
[附源码]超简洁个人博客网站搭建+SpringBoot+Vue前后端分离
vue.js·spring boot·后端
阿树梢8 小时前
【Vue】VueRouter路由
前端·javascript·vue.js
随笔写9 小时前
vue使用关于speak-tss插件的详细介绍
前端·javascript·vue.js
陈小唬11 小时前
html页面整合vue2或vue3
前端·vue.js·html
花下的晚风11 小时前
Vue实用操作篇-1-第一个 Vue 程序
前端·javascript·vue.js
我是Superman丶12 小时前
【前端UI框架】VUE ElementUI 离线文档 可不联网打开
前端·vue.js·elementui
growdu_real13 小时前
pandoc自定义过滤器
vue.js