vue2使用antv x6案例(功能齐全)

因为是把已经他当作组件来使用,所以大家使用的时候有些需要改一下,大部分的功能都在里面了,希望可以帮到大家。目前有时候使用会出现部分bug,vue-contextmenujs这个组件是用来实现右击显示菜单的,我也是网上找的,效果还可以。

参考:VUE右键菜单 vue-contextmenujs的使用_~Serendipity~的博客-CSDN博客

<template>
    <div>
        <el-row>
            <el-button type="primary" @click="toSvg()">导出SVG</el-button>
            <el-button type="success" @click="toJpg()">导出PNG</el-button>
            <el-button type="danger" @click="graphSave()">保存</el-button>
        </el-row>
      <!-- 创建容器 -->
      <div ref="container" id="container" style="margin-top: 20px;"></div>
    </div>
  </template>
  <script>
  // 准备数据
  import { Graph } from '@antv/x6'
  import { Export } from '@antv/x6-plugin-export'
  import { Transform } from '@antv/x6-plugin-transform'
  import { Snapline } from '@antv/x6-plugin-snapline'
  import { Clipboard } from '@antv/x6-plugin-clipboard'
  import { Keyboard } from '@antv/x6-plugin-keyboard'
  import { Selection } from '@antv/x6-plugin-selection'
  import { History } from '@antv/x6-plugin-history'
  import { Dnd } from '@antv/x6-plugin-dnd'

  export default {
    /**
     * 待优化:
     * 1.添加节点按钮
     * 3.多个画布之间调换,确认是否保存内容
     * 6.数据的流通,保存到库
     * 7.树的按钮不显示
     */
    data() {
      return {
        graph: null,
        dnd: null,
        data: {
          // 初始化展示的节点和边
          // nodes: [
          //   {
          //     id: 'node1', // String,可选,节点的唯一标识
          //     shape: 'custom-node',
          //     x: 40, // Number,必选,节点位置的 x 值
          //     y: 40, // Number,必选,节点位置的 y 值
          //     width: 100, // Number,可选,节点大小的 width 值
          //     height: 40, // Number,可选,节点大小的 height 值
          //     label: 'hello', // String,节点标签
          //     ports: {
          //       groups: {
          //         top: {
          //           position: 'top',
          //           attrs: {
          //             circle: {
          //               magnet: true,
          //               stroke: 'black',
          //               r: 5,
          //             },
          //           },
          //         },
          //         bottom: {
          //           position: 'bottom',
          //           attrs: {
          //             circle: {
          //               magnet: true,
          //               stroke: 'black',
          //               r: 5,
          //             },
          //           },
          //         },
          //         left: {
          //           position: 'left',
          //           attrs: {
          //             circle: {
          //               magnet: true,
          //               stroke: 'black',
          //               r: 5,
          //             },
          //           },
          //         },
          //         right: {
          //           position: 'right',
          //           attrs: {
          //             circle: {
          //               magnet: true,
          //               stroke: 'black',
          //               r: 5,
          //             },
          //           },
          //         },
          //       },
          //       items: [
          //         {
          //           id: 'node1_port_1',
          //           group: 'bottom',
          //         },{
          //           id: 'node1_port_2',
          //           group: 'top',
          //         },{
          //           id: 'node1_port_3',
          //           group: 'left',
          //         },{
          //           id: 'node1_port_4',
          //           group: 'right',
          //         }
          //       ],
          //     },
          //   },
          //   {
          //     id: 'node2',
          //     shape: 'custom-node',
          //     x: 160,
          //     y: 180,
          //     width: 100,
          //     height: 40,
          //     label: 'world',
          //     ports: {
          //       groups: {
          //       top: {
          //         position: 'top',
          //         attrs: {
          //           circle: {
          //             magnet: true,
          //             stroke: 'black',
          //             r: 5,
          //           },
          //         },
          //       },
          //       bottom: {
          //         position: 'bottom',
          //         attrs: {
          //           circle: {
          //             magnet: true,
          //             stroke: 'black',
          //             r: 5,
          //           },
          //         },
          //       },
          //       left: {
          //         position: 'left',
          //         attrs: {
          //           circle: {
          //             magnet: true,
          //             stroke: 'black',
          //             r: 5,
          //           },
          //         },
          //       },
          //       right: {
          //         position: 'right',
          //         attrs: {
          //           circle: {
          //             magnet: true,
          //             stroke: 'black',
          //             r: 5,
          //           },
          //         },
          //       },
          //     },
          //       items: [
          //         {
          //           id: 'node2_port_1',
          //           group: 'bottom',
          //         },{
          //           id: 'node2_port_2',
          //           group: 'top',
          //         },{
          //           id: 'node2_port_3',
          //           group: 'left',
          //         },{
          //           id: 'node2_port_4',
          //           group: 'right',
          //         }
          //       ],
          //     },
          //   }
          // ],
          // edge: {
          //   source: 'node1',
          //   target: 'node2',
          //   router: 'orth',
          //   attrs: {
          //     line: {
          //       stroke: '#8f8f8f',
          //       strokeWidth: 1,
          //     },
          //   },
          // }
        },
        saveData: [],
        saveId: '',
        saveVal:'',
        canRedo: false, // 恢复
        canUndo: false, // 撤销
      }
    },
    created() {},
    mounted() {
      this.initGraph()
    },
    methods: {
      // 初始化画布
      initGraph () {
        // 渲染画布
        this.graph = new Graph({
          container: this.$refs.container,
          // 画布背景
          background: {
            color: '#F2F7FA',
          },
          // 画布的线条样式
          grid: {
            visible: true, // 渲染网格背景
            size: 15, // 网格大小 10px
          },
          width: 600,
          height: 495,
          // 画布平移
          panning: {
            enabled: true,
            //  modifiers:拖拽可能和其他操作冲突,,
            // 设置修饰键后需要按下修饰键并点击鼠标才能触发画布拖拽。
            modifiers: 'Ctrl',
            // eventTypes:设置触发画布拖拽的行为,支持 leftMouseDown、 rightMouseDown、mouseWheel,
            // 默认为 ['leftMouseDown']
            eventTypes: ['leftMouseDown']
          },
          mousewheel: { // 放大
            enabled: true,
            modifiers: 'Ctrl',
            maxScale: 4,
            minScale: 0.2,
          },
          // 连接线
          connecting: {
            allowBlank: false, // 是否允许连接到画布空白位置的点
            allowLoop: false, // 是否允许创建循环连线,即边的起始节点和终止节点为同一节点
            allowNode: true, // 是否允许边连接到节点(非节点上的连接桩)
            allowEdge: false, // 是否允许边连接到另一个边
            allowPort: true, // 是否允许边连接到连接桩
            allowMulti: true, //是否允许在相同的起始节点和终止之间创建多条边
            highlight: true,
            createEdge() {
              return this.createEdge({
                shape: 'edge',
                router: {
                  name: 'manhattan',
                  args: {
                    startDirections: ['top', 'right', 'bottom', 'left'],
                    endDirections: ['top', 'right', 'bottom', 'left'],
                  },
                },
                connector: {
                  name: 'rounded',
                  // args: { radius: 10, },
                },
                attrs: {
                  line: {
                    stroke: '#8f8f8f',
                    strokeWidth: 2,
                  },
                },
                tools: ['edge-editor'], // 文本编辑器
              })
            },
            
          },
          // 连接桩样式
          highlighting: {
            // 连接桩吸附连线时在连接桩外围围渲染一个包围框
            magnetAdsorbed: {
              name: 'stroke',
              args: {
                attrs: {
                  fill: '#8f8f8f',
                  fillOpacity: '0.3',
                  stroke: '#fff',
                  strokeOpacity: '0'
                },
              },
            },
          },
          // interacting: {
          //   nodeMovable: true, //节点是否可以移动
          //   edgeMovable: true, //边是否可以被移动
          // },
        })
        // 自定义节点
        Graph.registerNode('custom-node',
          {
            inherit: 'rect', // 继承于 rect 节点
            markup: [
            {
                tagName: 'rect', // 标签名称
                selector: 'body', // 选择器
            },
            {
                tagName: 'image',
                selector: 'img',
            },
            {
                tagName: 'text',
                selector: 'label',
            },
            ],
            attrs: {
              body: {
                  stroke: '#8f8f8f',
                  strokeWidth: 1,
                  fill: '#fff',
                  rx: 6,
                  ry: 6
              },
              // img: {
              //     'xlink:href': 'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png',
              //     width: 16,
              //     height: 16,
              //     x: 12,
              //     y: 12,
              // },
            },
            ports: {
              groups: {
                top: {
                  position: 'top',
                  attrs: {
                    circle: {
                      magnet: true,
                      stroke: 'black',
                      r: 4,
                    },
                  },
                },
                bottom: {
                  position: 'bottom',
                  attrs: {
                    circle: {
                      magnet: true,
                      stroke: 'black',
                      r: 4,
                    },
                  },
                },
                left: {
                  position: 'left',
                  attrs: {
                    circle: {
                      magnet: true,
                      stroke: 'black',
                      r: 4,
                    },
                  },
                },
                right: {
                  position: 'right',
                  attrs: {
                    circle: {
                      magnet: true,
                      stroke: 'black',
                      r: 4,
                    },
                  },
                },
              },
              items: [
                {
                  id: 'port_1',
                  group: 'bottom',
                },{
                  id: 'port_2',
                  group: 'top',
                },{
                  id: 'port_3',
                  group: 'left',
                },{
                  id: 'port_4',
                  group: 'right',
                }
              ],
            },
          },true)
        
          // 图形变换
        this.graph.use(
          new Transform({
            // 调整大小
            resizing: {
              enabled: true
            },
            // 旋转角度
            rotating: {
              enabled: true
            }
          }),
        )
        // 导出
        this.graph.use(new Export())
        // 框选
        this.graph.use(
          new Selection({
            enabled: true,
            multiple: true,
            rubberband: true,
            movable: true,
            showNodeSelectionBox: true,
            showEdgeSelectionBox: true,
            pointerEvents: 'none'
          }),
        )
        // 快捷键
        this.graph.use(
          new Keyboard({
            enabled: true
          }),
        )
        // 复制粘贴
        this.graph.use(
          new Clipboard({
            enabled: true,
            global: false //是否为全局键盘事件
          }),
        )
        //撤销重做
        this.graph.use(
          new History({
            enabled: true,
          }),
        )
        // 对齐线
        this.graph.use(
          new Snapline({
            enabled: true,
            clean: false, // 如果为 true,则在 3s 后清除对齐线,为 false,不会清除,如果为数字(ms),则在指定时间后清除对齐线
          }),
        )
        
        // Dnd
        this.dnd = new Dnd({
          target: this.graph,
          scaled: false,
          // dndContainer: container
        })

        this.graph.fromJSON(this.data)

        // 双击节点
        this.graph.on('cell:dblclick', ({ e, x, y, cell, view }) => {
          // this.$emit(dbclickAlert,true)
          // this.$parent.dbclickAlert(true)
          if(this.graph.isNode(cell)){
            console.log('data',cell.getData())
            this.$parent.$parent.$parent.dbclickAlert(cell,true)
          }
        })
        // 历史改变
        this.graph.on('history:change', () => {
          this.canRedo = this.graph.canRedo(),
          this.canUndo = this.graph.canUndo()
        })
        // 右键菜单
        this.graph.on('node:contextmenu',({ e, x, y, cell, view }) => {
          this.onContextmenu(e)
        })
        this.graph.on('edge:contextmenu',({ e, x, y, cell, view }) => {
          this.onContextmenu(e)
        })
        this.graph.on('blank:contextmenu',({ e, x, y, cell, view }) => {
          this.onContextmenu(e)
        })
        this.graph.on('node:mouseenter', () => {
          const container = document.getElementById('container')
          const ports = container.querySelectorAll(
              '.x6-port-body',
          )
          this.showPorts(ports, true)
        })
        this.graph.on('node:mouseleave', () => {
          const container = document.getElementById('container')
          const ports = container.querySelectorAll(
              '.x6-port-body',
          )
          this.showPorts(ports, false)
        })
        
        // 复制
        this.graph.bindKey('ctrl+c', () => {
          const cells = this.graph.getSelectedCells()
          if (cells.length) {
            this.graph.copy(cells)
          }
          return false
        })
        // 粘贴
        this.graph.bindKey('ctrl+v', () => {
          if (!this.graph.isClipboardEmpty()) {
            const cells = this.graph.paste({ offset: 32 })
            this.graph.cleanSelection()
            this.graph.select(cells)
          }
          return false
        })
        // 撤销
        this.graph.bindKey('ctrl+z', () => {
          this.graph.undo()
        })
        // 恢复
        this.graph.bindKey('ctrl+y', () => {
          this.graph.redo()
        })
        // 剪切
        this.graph.bindKey('ctrl+x', () => {
          const cells = this.graph.getSelectedCells()
          if (cells.length) {
            this.graph.copy(cells)
            this.graph.removeCells(cells)
          }
        })
        // 删除
        this.graph.bindKey('Backspace', () => {
          const cells = this.graph.getSelectedCells()
          if (cells.length) {
            this.graph.removeCells(cells)
          }
          return false
        })

      },
      // 导出svg
      toSvg () {
        this.graph.exportSVG('导出svg格式')
      },
      // 导出png
      toJpg () {
        this.graph.exportPNG('导出png格式')
      },
      // 销毁画布
      graphDispose () {
        this.graph.dispose()
      },
      // 将画布中元素居中展示
      graphCenter(){
        this.graph.centerContent() 
      },
      // 添加节点
      graphAddNode () {
        this.graph.addNode({
          shape: 'custom-node',
          x: 10,
          y: 10,
          width: 100,
          height: 40,
          label: 'hello',
        })
      },
      // 修改节点
      graphUpdate () {
        const nodes = this.graph.getNodes()
        nodes.forEach((node) => {
          const width = 100 + Math.floor(Math.random() * 50)
          const height = 40 + Math.floor(Math.random() * 10)
          node.prop('size', { width, height })
          const color = this.color16()
          node.attr('body/fill', color)
        })
      },
      // 十六进制颜色随机
      color16 () {
        var r = Math.floor(Math.random() * 256);
        var g = Math.floor(Math.random() * 256);
        var b = Math.floor(Math.random() * 256);
        var color = "#" + r.toString(16) + g.toString(16) + b.toString(16);
        return color;
      },
      // 保存
      graphSave () {
        this.saveData = this.graph.toJSON()
        const saveDataTrans = JSON.stringify(this.saveData)
        // 存到库中
        this.$http({
          url: this.$http.adornUrl('/basedata/transrelation/update'),
          method: 'post',
          data: this.$http.adornData({
            'id': this.saveId,
            'chartCode': saveDataTrans
          })
        }).then(({data}) => {
          if (data && data.code === 0) {
            this.$emit('reassign',this.saveVal)
            this.$message({
              message: '保存成功',
              type: 'success'
            })
          } else {
            this.$message.error(data.msg)
          }
        })
      },
      // 重新渲染
      graphRecreate (val) {
        if(val === undefined || val === null || val === ''){
          this.initGraph()
        }else{
          this.saveData = JSON.parse(val.chartCode)
          this.saveId = val.id
          this.saveVal = val
          this.graph.fromJSON(this.saveData)
          this.$message({
            message: '渲染成功',
            type: 'success'
          })
        }
      },
      // 撤销
      undo () {
        this.graph.undo()
      },
      // 恢复
      redo (){
        this.graph.redo()
      },
      // Dnd拖拽
      startDrag (node, e) {
        console.log(node, e)
        const node1 = this.graph.createNode({
          shape: 'custom-node',
          width: 120,
          height: 50,
          label: node.data.name+"\n"+"(0%~0%)",
          data: {
            minNum: 0,
            maxNum: 0
          }
        })
        this.dnd.start(node1, e)
      },
      // 右击菜单事件
      onContextmenu (event) {
        const cells = this.graph.getSelectedCells()
        const isRedo = this.canRedo
        const isUndo = this.canUndo
        this.$contextmenu({
          items: [
            {
              label: "复制(Ctrl+Z)",
              disabled: cells.length===0?true:false,
              onClick: () => {
                if (cells.length) {
                  this.graph.copy(cells)
                }
              }
            },
            { 
              label: "粘贴(Ctrl+V)",
              disabled: this.graph.isClipboardEmpty(),
              onClick: () => {
                if (!this.graph.isClipboardEmpty()) {
                  const cells = this.graph.paste({ offset: 32 })
                  this.graph.cleanSelection()
                  this.graph.select(cells)
                }
              }
            },
            { 
              label: "剪切(Ctrl+X)",
              divided: true,
              disabled: cells.length===0?true:false,
              onClick: () => {
                if (cells.length) {
                  this.graph.copy(cells)
                  this.graph.removeCells(cells)
                }
              }
            },
            { 
              label: "撤销(Ctrl+Z)",
              disabled: !isUndo,
              onClick: () => {
                this.undo()
              }
            },
            { 
              label: "恢复(Ctrl+Y)",
              disabled: !isRedo,
              divided: true,
              onClick: () => {
                this.redo()
              }
            },
            { 
              label: "删除(Backspace)",
              disabled: cells.length===0?true:false,
              onClick: () => {
                if (cells.length) {
                  this.graph.removeCells(cells)
                }
              }
            }
          ],
          event, // 鼠标事件信息
          customClass: "custom-class", // 自定义菜单 class
          zIndex: 100, // 菜单样式 z-index
          minWidth: 150 // 主菜单最小宽度
        });
        return false;
      },
      // 弹框赋值
      updateByAlert (min, max, cell) {
        if(min===""){
          min=0
        }
        if(max===""){
          max=0
        }
        cell.setData({minNum:min,maxNum:max},{ overwrite: true })
        cell.label = cell.label.substr(0,cell.label.lastIndexOf("\n"))+"\n("+min+"%~"+max+"%)"
      },
      // 控制连接桩显示/隐藏
      showPorts (ports, show) {
        for (let i = 0, len = ports.length; i < len; i = i + 1) {
            ports[i].style.visibility = show ? 'visible' : 'hidden'
        }
      },
    }
  }
  </script>
  <style scoped>
  </style>
  
  

--------------------------2023.10.31-------------------------

1. 在实际使用中我发现了一些bug,在点击保存时会调用graphRecreate重新渲染,之后如果你单击或右击任意节点,可能会出现没有按住鼠标但节点会自己移动,并且框选的框子会与节点分离的情况,也有点击其他页面再点回来画布不会自动伸缩resize,这时候可以在graphRecreate方法里先使用this.graph.dispose()来清空画布,this.initGraph()重新渲染即可。

2. 使用时发现缩放页面,画布不会自动伸缩,需要在this.initGraph()方法中定义画布时加入autoResize: true即可 ,this.graph = new Graph({ autoResize: true ])

-------------------------2023.11.30---------------------------

需求:获取从根节点到叶子节点的所有路径(前提是没有形成环)

//this.allNodes保存了所有路径
animate() {
      // this.graph.getSuccessors(element)
      // this.graph.getLeafNodes()
      //获取所有根节点(没有入边的节点)
      var roots = this.graph.getRootNodes();
      roots.forEach(element => {
          this.stack.push(element);
          this.getNeighbor(element);
          this.stack.pop();
      });
}
//递归获取子节点
getNeighbor(cell) {
      //获取该节点的所有相邻节点(出边所指的节点)
      var neighbors = this.graph.getNeighbors(cell, { outgoing: true });
      if (neighbors.length == 0) {
        var s = [];
        this.stack.forEach(element1 => {
          s.push(element1);
        });
        this.allNodes.push(s);
      }
      neighbors.forEach(element => {
        this.stack.push(element);
        this.getNeighbor(element);
        this.stack.pop();
      });
},

-------------------------2024.01.22---------------------------

左边的树是使用了elementui的树组件,代码如下:

<el-tree
      :data="menus"
      :expand-on-click-node="expandOnClickNode === undefined ? false : expandOnClickNode"
      :default-expanded-keys="defaultExpendKey"
      :highlight-current="true"
      :current-node-key="defaultKey"
      ref="tree2"
      node-key="id"
      :show-checkbox="showCheckbox === undefined ? false : showCheckbox"
      :props="defaultProps"
      @node-click="handleNodeClick"
      @check="handleCheckChange"
      :default-checked-keys="multipleCheckData"
      :filter-node-method="filterNode"
      :draggable="draggable === undefined ? false : draggable"
      @node-drag-start="startDrag"
    >

//拖拽方法,回调node和event参数,传给Dnd拖拽方法
startDrag (node, event) {
    this.$emit('startDrag', node, event)
},

  • 5
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 21
    评论
要在Vue3中使用AntV X6实现右键菜单功能,你可以按照以下步骤操作: 1. 安装依赖:在Vue项目中安装AntV X6ant-design-vue库。 ``` npm install @antv/x6 ant-design-vue --save ``` 2. 创建右键菜单组件:创建一个Vue组件,作为X6图形编辑器右键菜单的容器。 ```vue <template> <a-dropdown :trigger="['contextmenu']" @visible-change="onVisibleChange"> <a-menu slot="overlay" :style="{ width: '120px' }"> <a-menu-item @click="deleteNode">删除节点</a-menu-item> </a-menu> <div class="x6-contextmenu" ref="container"></div> </a-dropdown> </template> <script> import { defineComponent } from 'vue'; import { Dropdown, Menu, message } from 'ant-design-vue'; export default defineComponent({ name: 'X6ContextMenu', components: { Dropdown, Menu, MenuItem: Menu.Item, }, emits: ['deleteNode'], mounted() { this.menu = this.$refs.container; this.menu.addEventListener('contextmenu', (e) => { e.preventDefault(); e.stopPropagation(); }); }, methods: { onVisibleChange(visible) { if (visible) { this.$emit('contextmenu', this.menu); } }, deleteNode() { this.$emit('deleteNode'); }, }, }); </script> ``` 3. 在X6图形编辑器中添加右键菜单:在X6图形编辑器中添加右键菜单功能。 ```vue <template> <div class="x6-editor"> <x6-contextmenu @contextmenu="onContextMenu" @deleteNode="deleteNode"></x6-contextmenu> <div class="x6-graph" ref="container"></div> </div> </template> <script> import { defineComponent } from 'vue'; import { Graph, Node } from '@antv/x6'; import X6ContextMenu from './X6ContextMenu.vue'; export default defineComponent({ name: 'X6Editor', components: { X6ContextMenu, }, data() { return { graph: null, }; }, mounted() { this.graph = new Graph({ container: this.$refs.container, grid: true, // 其他配置 }); // 添加节点 const node = this.graph.addNode({ // 节点配置 }); // 右键菜单事件 this.graph.on('contextmenu', ({ x, y }) => { this.$refs.contextmenu.show(x, y); }); }, methods: { onContextMenu(menu) { // 清空菜单 menu.innerHTML = ''; // 添加菜单项 const deleteMenuItem = document.createElement('a-menu-item'); deleteMenuItem.innerHTML = '删除节点'; deleteMenuItem.addEventListener('click', () => { this.deleteNode(); }); menu.appendChild(deleteMenuItem); }, deleteNode() { // 删除节点 this.graph.removeNode(node); }, }, }); </script> ``` 这样就可以在Vue3中使用AntV X6实现右键菜单功能了。
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值