流程图编辑器-拖拉拽-antV X6流程可视化-VUE

8,860次阅读
没有评论

共计 7845 个字符,预计需要花费 20 分钟才能阅读完成。

前言:

       上个我记录的 relation-graph 关系图本来是以后有需要我可以翻阅自己的博客可以当作备胎,使用的时候看一下让自己也知道还有个这么个不错的库,新的项目要有一个流程编辑器,relation-graph 提供例子,无论动画效果还是操作手感都挺不错,唯一的遗憾是阅读示例代码要钱(上一个文章 C 友留言说 VIP 的,我本以为可以一直白嫖 B 站视频,没想到要再次白嫖的时候,一个都找不到了),所以我选择了这个,还是阿里实在,不搞 vip 文档那些。

1. X6·图编辑引擎

        X6官方是这么介绍它的:X6 是基于 HTML 和 SVG 的图编辑引擎,提供低成本的定制能力和开箱即用的内置扩展,方便我们快速搭建 DAG 图、ER 图、流程图、血缘图等应用。

 2. X6— 官方地址
点击此处进入 -antV X6 官方地址流程图编辑器 - 拖拉拽 -antV X6 流程可视化 -VUEhttps://x6.antv.antgroup.com/

3. 选择 AntV-X6 的理由

  • 主要是 免费、免费、免费(这次真免费,没有 vip 文档,全部随意查阅)😵
  • 支持 Vue3、React
  • 完全自定义,可随意定制样式

4. 项目中引用 relation-graph

        4.1 下载命令

# npm

$ npm install @antv/x6 –save

# yarn

$ yarn add @antv/x6

5. 简单的实例代码

## 下载好后直接复制下面代码到你的 vue/react 项目中,先看看运行效果(来源官网示例)

import {Graph, Shape} from '@antv/x6'
import {Stencil} from '@antv/x6-plugin-stencil'
import {Transform} from '@antv/x6-plugin-transform'
import {Selection} from '@antv/x6-plugin-selection'
import {Snapline} from '@antv/x6-plugin-snapline'
import {Keyboard} from '@antv/x6-plugin-keyboard'
import {Clipboard} from '@antv/x6-plugin-clipboard'
import {History} from '@antv/x6-plugin-history'
import insertCss from 'insert-css'

// 为了协助代码演示
preWork()

// #region 初始化画布
const graph = new Graph({container: document.getElementById('graph-container')!,
  grid: true,
  mousewheel: {
    enabled: true,
    zoomAtMousePosition: true,
    modifiers: 'ctrl',
    minScale: 0.5,
    maxScale: 3,
  },
  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: '#A2B1C3',
            strokeWidth: 2,
            targetMarker: {
              name: 'block',
              width: 12,
              height: 8,
            },
          },
        },
        zIndex: 0,
      })
    },
    validateConnection({targetMagnet}) {return !!targetMagnet},
  },
  highlighting: {
    magnetAdsorbed: {
      name: 'stroke',
      args: {
        attrs: {
          fill: '#5F95FF',
          stroke: '#5F95FF',
        },
      },
    },
  },
})
// #endregion

// #region 使用插件
graph
  .use(
    new Transform({
      resizing: true,
      rotating: true,
    }),
  )
  .use(
    new Selection({
      rubberband: true,
      showNodeSelectionBox: true,
    }),
  )
  .use(new Snapline())
  .use(new Keyboard())
  .use(new Clipboard())
  .use(new History())
// #endregion

// #region 初始化 stencil
const stencil = new Stencil({
  title: '流程图',
  target: graph,
  stencilGraphWidth: 200,
  stencilGraphHeight: 180,
  collapsable: true,
  groups: [
    {
      title: '基础流程图',
      name: 'group1',
    },
    {
      title: '系统设计图',
      name: 'group2',
      graphHeight: 250,
      layoutOptions: {rowHeight: 70,},
    },
  ],
  layoutOptions: {
    columns: 2,
    columnWidth: 80,
    rowHeight: 55,
  },
})
document.getElementById('stencil')!.appendChild(stencil.container)
// #endregion

// #region 快捷键与事件
graph.bindKey(['meta+c', 'ctrl+c'], () => {const cells = graph.getSelectedCells()
  if (cells.length) {graph.copy(cells)
  }
  return false
})
graph.bindKey(['meta+x', 'ctrl+x'], () => {const cells = graph.getSelectedCells()
  if (cells.length) {graph.cut(cells)
  }
  return false
})
graph.bindKey(['meta+v', 'ctrl+v'], () => {if (!graph.isClipboardEmpty()) {const cells = graph.paste({ offset: 32})
    graph.cleanSelection()
    graph.select(cells)
  }
  return false
})

// undo redo
graph.bindKey(['meta+z', 'ctrl+z'], () => {if (graph.canUndo()) {graph.undo()
  }
  return false
})
graph.bindKey(['meta+shift+z', 'ctrl+shift+z'], () => {if (graph.canRedo()) {graph.redo()
  }
  return false
})

// select all
graph.bindKey(['meta+a', 'ctrl+a'], () => {const nodes = graph.getNodes()
  if (nodes) {graph.select(nodes)
  }
})

// delete
graph.bindKey('backspace', () => {const cells = graph.getSelectedCells()
  if (cells.length) {graph.removeCells(cells)
  }
})

// zoom
graph.bindKey(['ctrl+1', 'meta+1'], () => {const zoom = graph.zoom()
  if (zoom  {const zoom = graph.zoom()
  if (zoom> 0.5) {graph.zoom(-0.1)
  }
})

// 控制连接桩显示 / 隐藏
const showPorts = (ports: NodeListOf, show: boolean) => {
  for (let i = 0, len = ports.length; i  {const container = document.getElementById('graph-container')!
  const ports = container.querySelectorAll('.x6-port-body',) as NodeListOf
  showPorts(ports, true)
})
graph.on('node:mouseleave', () => {const container = document.getElementById('graph-container')!
  const ports = container.querySelectorAll('.x6-port-body',) as NodeListOf
  showPorts(ports, false)
})
// #endregion

// #region 初始化图形
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',},
  ],
}

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,
)

const r1 = graph.createNode({
  shape: 'custom-rect',
  label: '开始',
  attrs: {
    body: {
      rx: 20,
      ry: 26,
    },
  },
})
const r2 = graph.createNode({
  shape: 'custom-rect',
  label: '过程',
})
const r3 = graph.createNode({
  shape: 'custom-rect',
  attrs: {
    body: {
      rx: 6,
      ry: 6,
    },
  },
  label: '可选过程',
})
const r4 = graph.createNode({
  shape: 'custom-polygon',
  attrs: {
    body: {refPoints: '0,10 10,0 20,10 10,20',},
  },
  label: '决策',
})
const r5 = graph.createNode({
  shape: 'custom-polygon',
  attrs: {
    body: {refPoints: '10,0 40,0 30,20 0,20',},
  },
  label: '数据',
})
const r6 = graph.createNode({
  shape: 'custom-circle',
  label: '连接',
})
stencil.load([r1, r2, r3, r4, r5, r6], 'group1')

const imageShapes = [
  {
    label: 'Client',
    image:
      'https://gw.alipayobjects.com/zos/bmw-prod/687b6cb9-4b97-42a6-96d0-34b3099133ac.svg',
  },
  {
    label: 'Http',
    image:
      'https://gw.alipayobjects.com/zos/bmw-prod/dc1ced06-417d-466f-927b-b4a4d3265791.svg',
  },
  {
    label: 'Api',
    image:
      'https://gw.alipayobjects.com/zos/bmw-prod/c55d7ae1-8d20-4585-bd8f-ca23653a4489.svg',
  },
  {
    label: 'Sql',
    image:
      'https://gw.alipayobjects.com/zos/bmw-prod/6eb71764-18ed-4149-b868-53ad1542c405.svg',
  },
  {
    label: 'Clound',
    image:
      'https://gw.alipayobjects.com/zos/bmw-prod/c36fe7cb-dc24-4854-aeb5-88d8dc36d52e.svg',
  },
  {
    label: 'Mq',
    image:
      'https://gw.alipayobjects.com/zos/bmw-prod/2010ac9f-40e7-49d4-8c4a-4fcf2f83033b.svg',
  },
]
const imageNodes = imageShapes.map((item) =>
  graph.createNode({
    shape: 'custom-image',
    label: item.label,
    attrs: {
      image: {'xlink:href': item.image,},
    },
  }),
)
stencil.load(imageNodes, 'group2')
// #endregion

function preWork() {
  // 这里协助演示的代码,在实际项目中根据实际情况进行调整
  const container = document.getElementById('container')!
  const stencilContainer = document.createElement('div')
  stencilContainer.id = 'stencil'
  const graphContainer = document.createElement('div')
  graphContainer.id = 'graph-container'
  container.appendChild(stencilContainer)
  container.appendChild(graphContainer)

  insertCss(`
    #container {
      display: flex;
      border: 1px solid #dfe3e8;
    }
    #stencil {
      width: 180px;
      height: 100%;
      position: relative;
      border-right: 1px solid #dfe3e8;
    }
    #graph-container {width: calc(100% - 180px);
      height: 100%;
    }
    .x6-widget-stencil  {background-color: #fff;}
    .x6-widget-stencil-title {background-color: #fff;}
    .x6-widget-stencil-group-title {background-color: #fff !important;}
    .x6-widget-transform {
      margin: -1px 0 0 -1px;
      padding: 0px;
      border: 1px solid #239edd;
    }
    .x6-widget-transform > div {border: 1px solid #239edd;}
    .x6-widget-transform > div:hover {background-color: #3dafe4;}
    .x6-widget-transform-active-handle {background-color: #3dafe4;}
    .x6-widget-transform-resize {border-radius: 0;}
    .x6-widget-selection-inner {border: 1px solid #239edd;}
    .x6-widget-selection-box {opacity: 0;}
  `)
}

  5.1 运行效果截图

流程图编辑器 - 拖拉拽 -antV X6 流程可视化 -VUE

当然,这只是最简单的示例,前面说过可高度自定义,所以可以实现如下图的效果

原文地址: 流程图编辑器 - 拖拉拽 -antV X6 流程可视化 -VUE

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于2024-11-01发表,共计7845字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)