美文网首页
关于AntV-X6在Vue里面是使用

关于AntV-X6在Vue里面是使用

作者: andcen | 来源:发表于2022-08-04 12:07 被阅读0次
    写在前面

    因为工作需要,写了一个拖拽流程图生成组件。用的是AntV-X6。以前用的jsplumb,后面发现了这个蛮好用的,记录一下。

    组件功能

    • 可以拖拽添加节点,修改编辑节点样式(默认四个链接桩)
    • 可以添加连线,修改连线的样式。给线条添加动态(蚂蚁线)。可以手动调整线条(添加编辑工具)
    • 可以添加图片节点。自定义设置图片。
    • 导出与反显
    项目地址

    antv-x6: AntV-X6 图编辑引擎在vue2里面的使用。 (gitee.com)

    npm install
    npm run serve
    
    项目界面展示
    1232.png

    项目过程

    1,引入

    npm install @antv/x6 --save
    

    2,建立一个初始化默认设置的antvSetting.js,方便调用。

    // 画布基本设置(这些例子上面都有)
    export const configSetting = (Shape) => {
      return {
        grid: true,
        autoResize: true,
        translating: { restrict: true },
        mousewheel: {
          enabled: true,
          zoomAtMousePosition: true,
          modifiers: 'ctrl',
          minScale: 0.5,
          maxScale: 3,
        },
        connecting: {
          router: {
            name: 'manhattan',
            args: {
              padding: 1,
            },
          },
          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
          },
        },
        onToolItemCreated({ tool }) {
          const handle = tool
          const options = handle.options
          if (options && options.index % 2 === 1) {
            tool.setAttrs({ fill: 'red' })
          }
        },
        highlighting: {
          magnetAdsorbed: {
            name: 'stroke',
            args: {
              attrs: {
                fill: '#5F95FF',
                stroke: '#5F95FF',
              },
            },
          },
        },
        resizing: true,
        rotating: true,
        selecting: {
          enabled: true,
          rubberband: true,
          showNodeSelectionBox: true,
        },
        snapline: true,
        keyboard: true,
        clipboard: true
      }
    }
    
    /**
    * 节点预设类型 
    * 0椭圆形: defaultOval, 
    * 1方形: defaultSquare, 
    * 2圆角矩形: defaultYSquare,
    * 3菱形: defaultRhombus, 
    * 4平行四边形: defaultRhomboid, 
    * 5圆形: defaultCircle, 
    * 6图片: otherImage
    * 到时候通过传入的type===通过匹配 data里面设置的type获取到相应的节点设置内容
    * 编辑的时候也可以通过节点里面的data.type 获取到到底是什么节点进行响应设设置
    */
    export const configNodeShape = (type) => {
      const nodeShapeList =  [{
          label: '椭圆形',
          /**
           * 
           *  加入data里面的标识type是为了方便编辑的时候找到相对应的类型进行不同的编辑处理
           *  另外获取初始对应的设置
          */
          data: {
            type: 'defaultOval'
          },
          shape: 'rect',
          width: 100,
          height: 50,
          attrs: {
            body: {
              rx: 20,
              ry: 26,
              fill: '#fff',
              stroke: '#333'
            },
            label: {
              text: '椭圆形',
              fontSize: 16,
              fill: '#333'
            }
          }
        },
        {
          label: '方形',
          data: {
            type: 'defaultSquare',
          },
          shape: 'rect',
          width: 100,
          height: 50,
          attrs: {
            label: {
              text: '方形',
              fontSize: 16,
              fill: '#333'
            },
            body: {
              fill: '#fff',
              stroke: '#333'
            }
          },
        },
        {
          label: '圆角矩形',
          data: {
            type: 'defaultYSquare'
          },
          shape: 'rect',
          width: 100,
          height: 50,
          attrs: {
            body: {
              rx: 6,
              ry: 6,
              fill: '#fff',
              stroke: '#333'
            },
            label: {
              text: '圆角矩形',
              fontSize: 16,
              fill: '#333'
            }
          },
        },
        {
          label: '菱形',
          data: {
            type: 'defaultRhombus'
          },
          shape: 'polygon',
          width: 120,
          height: 50,
          attrs: {
            body: {
              refPoints: '0,10 10,0 20,10 10,20',
              fill: '#fff',
              stroke: '#333'
            },
            label: {
              text: '菱形',
              fontSize: 16,
              fill: '#333'
            }
          },
        },
        {
          label: '平行四边形',
          data: {
            type: 'defaultRhomboid'
          },
          shape: 'polygon',
          width: 120,
          height: 50,
          attrs: {
            body: {
              refPoints: '10,0 40,0 30,20 0,20',
              fill: '#fff',
              stroke: '#333'
            },
            label: {
              text: '平行四边形',
              fontSize: 16,
              fill: '#333'
            }
          }
        },
        {
          label: '圆形',
          data: {
            type: 'defaultCircle'
          },
          shape: 'circle',
          width: 80,
          height: 80,
          attrs: {
            label: {
              text: '圆形',
              fontSize: 16,
              fill: '#333'
            },
            body: {
              fill: '#fff',
              stroke: '#333'
            }
          }
        },
        {
          label: "图片",
          data: {
            type: 'otherImage'
          },
          shape: 'rect',
          width: 80,
          height: 80,
          markup: [
            {
              tagName: 'rect',
              selector: 'body',
            },
            {
              tagName: 'image',
            },
            {
              tagName: 'text',
              selector: 'label',
            },
          ],
          attrs: {
            body: {
              stroke: '#5F95FF',
              fill: '#5F95FF',
            },
            image: {
              width: 80,
              height: 80,
              refX: 0,
              refY: 0,
              xlinkHref: 'https://gw.alipayobjects.com/zos/bmw-prod/2010ac9f-40e7-49d4-8c4a-4fcf2f83033b.svg',
            },
            label: {
              fontSize: 14,
              fill: '#fff',
              text: '图片'
            },
          },
        }
      ]
      if(type) {
        const obj = nodeShapeList.find(item => {return item.data.type === type})
        return obj || nodeShapeList
      }
      return nodeShapeList
    }
    
    /**
    * 节点连接桩设置
    * 这里设置了上下左右四个
    * 并且给style设置了 visibility: 'hidden',默认是隐藏的。
    * 如果设置了隐藏记得在画布里面设置鼠标经过显示。
    * graph.on('node:mouseenter', () => {
            const container = document.getElementById('wrapper')
            const ports = container.querySelectorAll('.x6-port-body')
            for (let i = 0, len = ports.length; i < len; i = i + 1) {
              ports[i].style.visibility = val ? 'visible' : 'hidden'
            }
        })
    * 如果需要常显去掉每个链接桩里面的
      style: {
            visibility: 'hidden',
         },
    * 就可以了
    */
    export const configNodePorts = () => {
      return {
        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',
          },
        ]
      }
    }
    
    // 连线 label 设置
    export const configEdgeLabel = (labelText, fontColor, fill, stroke) => {
      if(!labelText) return { attrs: { labelText: { text: '' }, labelBody: { fill: '', stroke: '' } } }
      return {
          markup: [
            {
              tagName: 'rect',
              selector: 'labelBody',
            },
            {
              tagName: 'text',
              selector: 'labelText',
            },
          ],
          attrs: {
            labelText: {
              text: labelText || '',
              fill: fontColor || '#333',
              textAnchor: 'middle',
              textVerticalAnchor: 'middle',
            },
            labelBody: {
              ref: 'labelText',
              refX: -8,
              refY: -5,
              refWidth: '100%',
              refHeight: '100%',
              refWidth2: 16,
              refHeight2: 10,
              stroke: stroke || '#555',
              fill: fill || '#fff',
              strokeWidth: 2,
              rx: 5,
              ry: 5,
            },
          }
        }
    }
    
    // 键盘事件
    export const graphBindKey = (graph) => {
        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.history.canUndo()) {
            graph.history.undo()
          }
          return false
        })
        graph.bindKey(['meta+shift+z', 'ctrl+shift+z'], () => {
          if (graph.history.canRedo()) {
            graph.history.redo()
          }
          return false
        })
        // select all
        graph.bindKey(['meta+a', 'ctrl+a'], () => {
          const nodes = graph.getNodes()
          if (nodes) {
            graph.select(nodes)
          }
        })
        //delete
        /*
        graph.bindKey('delete', () => {
          const cells = graph.getSelectedCells()
          if (cells.length) {
            graph.removeCells(cells)
          }
        })
        */
        // zoom
        graph.bindKey(['ctrl+1', 'meta+1'], () => {
          const zoom = graph.zoom()
          if (zoom < 1.5) {
            graph.zoom(0.1)
          }
        })
        graph.bindKey(['ctrl+2', 'meta+2'], () => {
          const zoom = graph.zoom()
          if (zoom > 0.5) {
            graph.zoom(-0.1)
          }
        })
        return graph
    }
    

    3,页面上的使用

    <template>
      <div class="all">
        <div class="antv-content">
          <div class="antv-menu">
            <h3> 基础图形列表 </h3>
            <ul class="menu-list">
              <li draggable="true" @drag="menuDrag('defaultOval')"> <i class="icon-oval"></i> <strong>椭圆形</strong></li>
              <li draggable="true" @drag="menuDrag('defaultSquare')"><i class="icon-square"></i><strong>矩形</strong></li>
              <li draggable="true" @drag="menuDrag('defaultYSquare')"><i class="icon-ysquare"></i><strong>圆角矩形</strong></li>
              <li draggable="true" @drag="menuDrag('defaultRhombus')"><i class="icon-rhombus"></i><strong>菱形</strong></li>
              <li draggable="true" @drag="menuDrag('defaultRhomboid')"><i class="icon-rhomboid"></i><strong>平行四边形</strong></li>
              <li draggable="true" @drag="menuDrag('defaultCircle')"><i class="icon-circle"></i><strong>圆形</strong></li>
              <li draggable="true" @drag="menuDrag('otherImage')"><i class="el-icon-picture"></i><strong>图片</strong></li>
            </ul>
            <div class="wrapper-btn" v-if="isChange">
              <el-button type="success" @click="handlerSend">保存当前方案</el-button>
            </div>
          </div>
          <div class="antv-wrapper">
            <div class="wrapper-canvas" :style="{height: height}" id="wrapper" @drop="drop($event)" @dragover.prevent></div>
            <div class="wrapper-tips">
              <div class="wrapper-tips-item">
                <el-switch v-model="isPortsShow" @change="changePortsShow"></el-switch>
                <span>链接桩常显</span>
              </div>
            </div>
          </div>
          <div v-if="editDrawer" class="edit-main">
            <div class="edit-main-title">
              <h3>{{editTitle}} </h3>
              <i class="el-icon-close" @click="closeEditForm"></i>
            </div>
            <div v-if="editTitle === '编辑节点'" class="form-main">
              <el-form ref="nodeForm" :model="form" label-width="80px">
                <el-form-item label="节点文本">
                  <el-input v-model="form.labelText" size="small" @input="changeNode('labelText', form.labelText)"></el-input>
                </el-form-item>
                <el-form-item label="字体大小">
                  <el-input v-model="form.fontSize" size="small" @input="changeNode('fontSize', form.fontSize)"></el-input>
                </el-form-item>
                <el-form-item label="字体颜色">
                  <el-color-picker v-model="form.fontFill" @change="changeNode('fontFill', form.fontFill)"></el-color-picker>
                </el-form-item>
                <el-form-item label="节点背景">
                  <el-color-picker v-model="form.fill" @change="changeNode('fill', form.fill)"></el-color-picker>
                </el-form-item>
                <el-form-item label="边框颜色">
                  <el-color-picker v-model="form.stroke" @change="changeNode('stroke', form.stroke)"></el-color-picker>
                </el-form-item>
                 <div class="see-box">
                    <h5>预览</h5>
                    <div class="see-item" :style="{ 'background': form.fill, 'color': form.fontFill, 'border-color': form.stroke, 'font-size': form.fontSize + 'px' }">{{form.labelText}}</div>
                  </div>
              </el-form>
            </div>
            <div v-if="editTitle === '编辑图片节点'" class="form-main">
              <el-form ref="imageForm" :model="form" label-width="80px">
                <el-form-item label="节点文本">
                  <el-input v-model="form.labelText" size="small" @input="changeImageNode('labelText', form.labelText)"></el-input>
                </el-form-item>
                <el-form-item label="字体颜色">
                  <el-color-picker v-model="form.labelFill" @change="changeImageNode('labelFill', form.labelFill)"></el-color-picker>
                </el-form-item>
                <el-form-item label="节点背景">
                  <el-color-picker v-model="form.fill" @change="changeImageNode('fill', form.fill)"></el-color-picker>
                </el-form-item>
                <el-form-item label="图片地址">
                  <el-input v-model="form.xlinkHref" size="small" placeholder="图片地址" @input="changeImageNode('xlinkHref', form.xlinkHref)"></el-input>
                  <el-image :src="form.xlinkHref" style="width: 80px; height: 80px; background: #f2f2f2" fit="fill"></el-image>
                </el-form-item>
                <el-form-item label="图片尺寸">
                  <span style="font-size: 14px; padding-right: 5px; color: #888;">宽</span><el-input-number v-model="form.width" :min="0" label="宽" size="mini" @change="changeImageNode('width', form.width)"></el-input-number>
                  <span style="font-size: 14px; padding-right: 5px; color: #888;">高</span><el-input-number v-model="form.height" :min="0" label="高" size="mini" @change="changeImageNode('height', form.height)"></el-input-number>
                </el-form-item>
              </el-form>
            </div>
            <div v-if="editTitle === '编辑连线'" class="form-main">
              <el-form ref="edgeForm" :model="form" label-width="80px">
                <el-form-item label="标签内容">
                  <el-input v-model="form.label" size="small" placeholder="标签文字,空则没有" @input="changeEdgeLabel(form.label, labelForm.fontColor, labelForm.fill, labelForm.stroke)"></el-input>
                  <div v-if="form.label" class="label-style">
                    <p>字体颜色:<el-color-picker v-model="labelForm.fontColor" size="mini" @change="changeEdgeLabel(form.label, labelForm.fontColor, labelForm.fill, labelForm.stroke)"></el-color-picker></p>
                    <p>背景颜色:<el-color-picker v-model="labelForm.fill" size="mini" @change="changeEdgeLabel(form.label, labelForm.fontColor, labelForm.fill, labelForm.stroke)"></el-color-picker></p>
                    <p>描边颜色:<el-color-picker v-model="labelForm.stroke" size="mini" @change="changeEdgeLabel(form.label, labelForm.fontColor, labelForm.fill, labelForm.stroke)"></el-color-picker></p>
                  </div>
                </el-form-item>
                <el-form-item label="线条颜色">
                  <el-color-picker v-model="form.stroke" size="small" @change="changeEdgeStroke"></el-color-picker>
                </el-form-item>
                <el-form-item label="线条样式">
                  <el-select v-model="form.connector" size="small" placeholder="请选择" @change="changeEdgeConnector">
                    <el-option label="直角" value="normal"></el-option>
                    <el-option label="圆角" value="rounded"></el-option>
                    <el-option label="平滑" value="smooth"></el-option>
                    <el-option label="跳线(两线交叉)" value="jumpover"></el-option>
                  </el-select>
                </el-form-item>
                <el-form-item label="线条宽度">
                  <el-input-number v-model="form.strokeWidth" size="small" @change="changeEdgeStrokeWidth" :min="2" :step="2" :max="6" label="线条宽度"></el-input-number>
                </el-form-item>
                <el-form-item label="双向箭头">
                  <el-switch v-model="form.isArrows" @change="changeEdgeArrows"></el-switch>
                </el-form-item>
                <el-form-item label="流动线条">
                  <el-switch v-model="form.isAnit" @change="changeEdgeAnit"></el-switch>
                </el-form-item>
                <el-form-item label="调整线条">
                  <el-switch v-model="form.isTools" @change="changeEdgeTools"></el-switch>
                </el-form-item>
              </el-form>
            </div>
            <div class="edit-btn">
              <el-button type="danger" @click="handlerDel" style="width:100%">删除此{{editTitle === '编辑节点' ? '节点' : '连线'}}</el-button>
            </div>
          </div>
        </div>
      </div>
    </template>
    <script>
      import { Graph, Shape } from '@antv/x6'
      import { configSetting, configNodeShape, configNodePorts, configEdgeLabel, graphBindKey } from '@/utils/antvSetting'
      export default {
        name: "AntV6X",
        /**
         * 这个是作为子组件分别接受了两个数据一个是高度height,一个是反显图表数据tempGroupJson
         * 作为子组件例子 <AntVXSix v-model="tempGroupJson" height="720px" />
         * 
         */ 
        props: {
          height: {
            type: String,
            default: '100vh' //'720px'
          },
          value: {
            type: String,
            default: ''
          }
        },
        data() {
          return {
            graph: null,
            isChange: false,
            isPortsShow: false,
            menuItem: '',
            selectCell: '',
            editDrawer: false,
            editTitle: '',
            form:{},
            labelForm: {
              fontColor: '#333',
              fill: '#FFF',
              stroke: '#555'
            }
          }
        },
        created() {
        },
        watch:{
          value:{
            handler: function(){
              if(this.graph){
                this.isChange = false
                this.isPortsShow = false
                this.menuItem = ''
                this.selectCell = ''
                this.editDrawer = false
                this.graph.dispose()
                this.initGraph()
              }
            },
            deep: true,
            immediate: true
          }
        },
        mounted() {
          this.initGraph()
        },
        beforeDestroy() {
          this.graph.dispose()
        },
        methods: {
          // 链接桩的显示与隐藏,主要是照顾菱形
          changePortsShow(val){
            const container = document.getElementById('wrapper')
            const ports = container.querySelectorAll('.x6-port-body')
            for (let i = 0, len = ports.length; i < len; i = i + 1) {
              ports[i].style.visibility = val ? 'visible' : 'hidden'
            }
          },
          // 初始化渲染画布
          initGraph(){
            const graph = new Graph({
              container: document.getElementById('wrapper'),
              ...configSetting(Shape)
            })
            // 画布事件
            graph.on('node:mouseenter', () => {
              this.changePortsShow(true)
            })
            graph.on('node:mouseleave', () => {
              if(this.isPortsShow) return
              this.changePortsShow(false)
            })
            // 点击编辑
            graph.on('cell:click', ({ cell }) => {
              this.editForm(cell)
            })
            // 画布键盘事件
            graphBindKey(graph)
            // 删除
            graph.bindKey(['delete','backspace'], () => {
              this.handlerDel()
            })
            // 赋值
            this.graph = graph
            // 返现方法
            if(this.value && JSON.parse(this.value).length){
              const resArr = JSON.parse(this.value)
              // 导出的时候删除了链接桩设置加回来
              const portsGroups = configNodePorts().groups
              if(resArr.length){
                const jsonTemp = resArr.map(item=>{
                  if(item.ports) item.ports.groups = portsGroups 
                  return item
                })
                graph.fromJSON(jsonTemp)
              }
            }
            // 画布有变化
            graph.on('cell:changed', () => { 
              this.isChangeValue()
            })
          },
          // 画布是否有变动
          isChangeValue(){
            if(!this.isChange) {
              this.isChange = true
              this.$emit('cellChanged', true)
            }
          },
          menuDrag(type){
            // 根据type获取到不同节点的预设参数
            this.menuItem = configNodeShape(type)
          },
          drop(event){
             // 节点预设 ,添加位置信息和链接桩信息组合成完整的节点
            const nodeItem = {
              ...this.menuItem,
              x: event.offsetX - (this.menuItem.width / 2),
              y: event.offsetY - (this.menuItem.height / 2),
              ports: configNodePorts()
            }
            // 创建节点
            this.graph.addNode(nodeItem)
            this.isChangeValue()
          },
           // 点击编辑更具不同的内容获取编辑数据
          editForm(cell){
            if(this.selectCell) this.selectCell.removeTools() // 删除修改线的工具
            this.selectCell = cell
            // 编辑node节点
            if(cell.isNode() && cell.data.type && cell.data.type.includes('default')){
              this.editTitle = '编辑节点'
              const body = cell.attrs.body || cell.attrs.rect || cell.attrs.polygon || cell.attrs.circle
              this.form = {
                labelText: cell.attrs.label.text || '',
                fontSize: cell.attrs.label.fontSize || 14,
                fontFill: cell.attrs.label.fill || '',
                fill: body.fill|| '',
                stroke: body.stroke || ''
              }
              return this.editDrawer = true
            }
            // 编辑图片节点
            if(cell.isNode() && cell.data.type && cell.data.type === 'otherImage'){
              this.editTitle = '编辑图片节点'
              const attrs = cell.attrs || { body:{fill: ''}, label: {text: '', fill: ''}, image:{xlinkHref: '', height:  80, width: 80} }
              this.form = {
                fill: attrs.body.fill,
                labelText: attrs.label.text,
                labelFill: attrs.label.fill,
                height: (attrs.image && attrs.image.height) || 80,
                width: (attrs.image && attrs.image.width) || 80,
                xlinkHref: (attrs.xlinkHref && attrs.image.xlinkHref) || 'https://gw.alipayobjects.com/zos/bmw-prod/2010ac9f-40e7-49d4-8c4a-4fcf2f83033b.svg'
              }
              return this.editDrawer = true
            }
            // 编辑连线
            if(!cell.isNode() && cell.shape === 'edge'){
              this.editTitle = '编辑连线'
              this.form = {
                label: (cell.labels && cell.labels[0]) ? cell.labels[0].attrs.labelText.text : '',
                stroke: cell.attrs.line.stroke || '',
                connector: 'rounded',
                strokeWidth: cell.attrs.line.strokeWidth || '',
                isArrows: cell.attrs.line.sourceMarker ? true : false,
                isAnit: cell.attrs.line.strokeDasharray ? true : false,
                isTools: false
              }
              // 看连线上是否有label
              const edgeCellLabel = cell.labels && cell.labels[0] && cell.labels[0].attrs || false
              if(this.form.label && edgeCellLabel){
                this.labelForm = {
                  fontColor: edgeCellLabel.labelText.fill || '#333',
                  fill: edgeCellLabel.labelBody.fill || '#fff',
                  stroke: edgeCellLabel.labelBody.stroke || '#555'
                }
              } else {
                this.labelForm = { fontColor: '#333', fill: '#FFF', stroke: '#555' }
              }
              return this.editDrawer = true
            }
          },
          closeEditForm(){
            this.editDrawer = false
            if(this.selectCell) this.selectCell.removeTools()
          },
          // 修改一般节点
          changeNode(type, value){
            switch (type) {
              case 'labelText':
                this.selectCell.attr('label/text', value)
                break;
              case 'fontSize':
                this.selectCell.attr('label/fontSize', value)
                break;
              case 'fontFill':
                this.selectCell.attr('label/fill', value)
                break;
              case 'fill':
                this.selectCell.attr('body/fill', value)
                break;
              case 'stroke':
                this.selectCell.attr('body/stroke', value)
                break;
            }
          },
          // 修改图片节点
          changeImageNode(type, value){
            switch (type) {
              case 'labelText':
                this.selectCell.attr('label/text', value)
                break;
              case 'labelFill':
                this.selectCell.attr('label/fill', value)
                break;
              case 'fill':
                this.selectCell.attr('body/fill', value)
                break;
              case 'xlinkHref':
                this.selectCell.attr('image/xlinkHref', value)
                break;
              case 'height':
                this.selectCell.attr('image/height', value)
                break;
              case 'width':
                this.selectCell.attr('image/width', value)
                break;
            }
          },
          // 修改边label属性
          changeEdgeLabel(label, fontColor, fill, stroke){
            this.selectCell.setLabels([configEdgeLabel(label, fontColor, fill, stroke)])
            if(!label) this.labelForm = { fontColor: '#333', fill: '#FFF', stroke: '#555' }
          },
          // 修改边的颜色
          changeEdgeStroke(val){
            this.selectCell.attr('line/stroke', val)
          },
          // 边的样式
          changeEdgeConnector(val){
            switch (val) {
              case 'normal':
                this.selectCell.setConnector(val)
              break;
              case 'smooth':
                this.selectCell.setConnector(val)
              break;
                case 'rounded':
                this.selectCell.setConnector(val, { radius: 20 })
              break;
                case 'jumpover':
                this.selectCell.setConnector(val, { radius: 20 })
              break;
            }
          },
          // 边的宽度
          changeEdgeStrokeWidth(val){
            if(this.form.isArrows){
              this.selectCell.attr({
                line: {
                  strokeWidth: val,
                  sourceMarker: {
                    width: 12 * (val / 2) || 12,
                    height: 8 * (val / 2) || 8
                  },
                  targetMarker: {
                    width: 12 * (val / 2) || 12,
                    height: 8 * (val / 2) || 8
                  }
                }
              })
    
            } else {
              this.selectCell.attr({
                line: {
                  strokeWidth: val,
                  targetMarker: {
                    width: 12 * (val / 2) || 12,
                    height: 8 * (val / 2) || 8
                  }
                }
              })
            }
            
          },
          // 边的箭头
          changeEdgeArrows(val){
            if(val){
              this.selectCell.attr({
                line: {
                  sourceMarker: {
                    name: 'block',
                    width: 12 * (this.form.strokeWidth / 2) || 12,
                    height: 8 * (this.form.strokeWidth / 2) || 8
                  },
                  targetMarker: {
                    name: 'block',
                    width: 12 * (this.form.strokeWidth / 2) || 12,
                    height: 8 * (this.form.strokeWidth / 2) || 8
                  },
                }
              })
            } else {
              this.selectCell.attr({
                line: {
                  sourceMarker: '',
                  targetMarker: {
                    name: 'block',
                    size: 10 * (this.form.strokeWidth / 2) || 10
                  },
                }
              })
            }
          },
          // 边的添加蚂蚁线
          changeEdgeAnit(val){
            if(val){
              this.selectCell.attr({
                line: {
                  strokeDasharray: 5,
                  style: {
                    animation: 'ant-line 30s infinite linear',
                  }
                }
              })
            } else {
              this.selectCell.attr({
                line: {
                  strokeDasharray: 0,
                  style: {
                    animation: '',
                  }
                }
              })
            }
          },
          // 给线添加调节工具
          changeEdgeTools(val){
            if(val) this.selectCell.addTools(['vertices', 'segments'])
            else this.selectCell.removeTools()
          },
          // 删除节点
          handlerDel(){
            this.$confirm(`此操作将永久删除此${this.editTitle === '编辑节点' ? '节点' : '连线'}, 是否继续?`, '提示', {
              confirmButtonText: '确定',
              cancelButtonText: '取消',
              type: 'warning'
            }).then(() => {
              const cells = this.graph.getSelectedCells()
              if (cells.length) {
                this.graph.removeCells(cells)
                this.form = {}
                this.editDrawer = false
                this.$message({type: 'success',message: '删除成功!'})
              }
            }).catch(() => {})
          },
          // 导出
          handlerSend(){
            // 我在这里删除了链接桩的设置,和工具(为了减少数据),反显的时候要把删除的链接桩加回来
            const {cells: jsonArr} = this.graph.toJSON()
            const tempGroupJson = jsonArr.map(item=>{
              if(item.ports && item.ports.groups) delete item.ports.groups
              if(item.tools) delete item.tools
              return item
            })
            if(this.selectCell){
              this.selectCell.removeTools()
              this.selectCell = ''
            }
            this.$emit('finish', JSON.stringify(tempGroupJson))
            console.log(JSON.stringify(tempGroupJson))
          },
        }
      }
    </script>
    <style lang="scss">
    @keyframes ant-line {
      to {
          stroke-dashoffset: -1000
      }
    }
    </style>
    <style lang="scss" scoped="scoped">
    .all{
      border-radius: 8px;
      overflow: hidden;
    }
    .antv-content{
      background: #fff;
      display: flex;
      overflow: hidden;
      position: relative;
      .antv-menu{
        width: 200px;
        border-right: 1px solid #d5d5d5;
        padding: 10px;
        h3{
          padding: 10px;
        };
        li{
          padding: 10px;
          border-radius: 8px;
          border: 1px solid #555;
          background: #fff;
          margin: 5px 10px;
          font-size: 12px;
          display: flex;
          align-items: center;
          cursor: pointer;
          transition: all 0.5s ease;
          &:hover{
            box-shadow: 0 0 5px rgba($color: #000000, $alpha: 0.3);
          }
          i{
            font-size: 18px;
            margin-right: 10px;
          }
          strong{
            flex: 1;
          }
        }
      }
      .antv-wrapper{
        flex: 1;
        position: relative;
        .wrapper-canvas{
          position: relative;
          height: 100vh;
          min-height: 720px;
        }
        .wrapper-tips{
          padding: 10px;
          display: flex;
          align-items: center;
          position: absolute;
          top: 0;
          left: 0;
          .wrapper-tips-item{
            span{
              padding-left: 10px;
              font-size: 12px;
            }
          }
        }
      }
    }
    i.icon-oval{
        display: inline-block;
        width: 16px;
        height: 10px;
        border-radius: 10px;
        border: 2px solid #555;
    }
    i.icon-square{
        display: inline-block;
        width: 16px;
        height: 10px;
        border: 2px solid #555;
    }
    i.icon-ysquare{
       display: inline-block;
        width: 16px;
        height: 10px;
        border-radius: 4px;
        border: 2px solid #555;
    }
    i.icon-rhombus{
       display: inline-block;
        width: 10px;
        height: 10px;
        border: 2px solid #555;
        transform: rotate(45deg);
    }
    i.icon-rhomboid{
       display: inline-block;
        width: 10px;
        height: 10px;
        border: 2px solid #555;
        transform: skew(-30deg);
    }
    i.icon-circle{
       display: inline-block;
        width: 16px;
        height: 16px;
        border-radius: 16px;
        border: 2px solid #555;
    }
    .edit-main{
      position: absolute;
      right: 0;
      top: 0;
      height: 100%;
      width: 280px;
      border-left: 1px solid #f2f2f2;
      box-shadow: 0 -10px 10px rgba($color: #000000, $alpha: 0.3);
      padding: 20px;
      background: #fff;
      box-sizing: border-box;
      .edit-main-title{
        display: flex;
        justify-content: space-between;
        align-items: center;
        h3{
          flex: 1;
        }
        i{
          cursor: pointer;
          font-size: 20px;
          opacity: 0.7;
          &:hover{
            opacity: 1;
          }
        }
      }
      
      .form-main{
        padding: 20px 0;
        .label-style{
          background: #f2f2f2;
          padding: 0 10px;
          p{
            display: flex;
            align-items: center;
            font-size: 12px;
          }
        }
      }
      .edit-btn{
      }
      .see-box{
      padding: 20px;
        background: #f2f2f2;
        h5{
          padding-bottom: 10px;
        }
        .see-item{
          padding: 10px 30px;
          border: 2px solid #333;
          text-align: center;
        }
      }
    }
    .wrapper-btn{
      text-align: center;
      padding: 20px;
      button{
        width: 100%;
      }
    }
    </style>
    
    

    展示页面(反显页面)

    <template>
      <div class="antv-wrapper">
        <div class="wrapper-canvas" id="wrapper"></div>
      </div>
    </template>
    <script>
      import { Graph, Shape } from '@antv/x6'
      import { configNodePorts } from '@/utils/antvSetting'
      // 反显数据
      const resData = [{"shape":"edge","attrs":{"line":{"stroke":"#05C13A","strokeWidth":4,"targetMarker":{"name":"block","width":24,"height":16},"strokeDasharray":5,"style":{"animation":"ant-line 30s infinite linear"}}},"id":"390b4bc1-4945-464c-a34d-eeb14acba1a1","zIndex":0,"source":{"cell":"c9ba8f28-9335-447a-a4c0-1dbe34afc815","port":"9daae234-935b-4634-aae4-62fe6e1a763a"},"target":{"cell":"e78740b8-f27f-4f3d-b1d4-11e6e810c76a","port":"122093c7-de48-435b-95aa-8ef9cf58a9ca"},"connector":{"name":"normal"},"vertices":[{"x":640,"y":134}]},{"shape":"edge","attrs":{"line":{"stroke":"#0074FF","targetMarker":{"name":"block","width":12,"height":8},"sourceMarker":{"name":"block","width":12,"height":8}}},"id":"4fa2ff4a-a78e-4f4c-a8fa-0b1c536cbe1b","zIndex":0,"source":{"cell":"e78740b8-f27f-4f3d-b1d4-11e6e810c76a","port":"7ae5d159-3823-4b7f-9e2b-994405071dc8"},"target":{"cell":"9e97d5af-ae22-47e0-bb9f-a0ebf8db0910","port":"40ce97eb-f258-4b8c-b3bc-b798b4662b77"},"vertices":[{"x":1240,"y":250},{"x":1240,"y":400}]},{"shape":"edge","attrs":{"line":{"stroke":"#E36600","strokeWidth":4,"targetMarker":{"name":"block","width":24,"height":16}}},"id":"c0874682-8817-4797-b6e9-5022fd6238b3","zIndex":0,"source":{"cell":"c9ba8f28-9335-447a-a4c0-1dbe34afc815","port":"4b5b6dd0-c31a-42bd-ae81-74b26f67b3eb"},"target":{"cell":"9e97d5af-ae22-47e0-bb9f-a0ebf8db0910","port":"ef42ef10-d01d-49b1-95da-2f19189adf29"},"connector":{"name":"smooth"},"vertices":[{"x":290,"y":290},{"x":380,"y":400}]},{"position":{"x":170,"y":130},"size":{"width":100,"height":50},"attrs":{"text":{"text":"椭圆形"},"body":{"rx":20,"ry":26,"fill":"#08D34F","stroke":"#028222"},"label":{"text":"椭圆形","fontSize":16,"fill":"#FFFFFF"}},"visible":true,"shape":"rect","id":"c9ba8f28-9335-447a-a4c0-1dbe34afc815","data":{"type":"defaultOval"},"ports":{"items":[{"group":"top","id":"e49da871-6793-4a8e-909d-becd630de7cc"},{"group":"right","id":"9daae234-935b-4634-aae4-62fe6e1a763a"},{"group":"bottom","id":"4b5b6dd0-c31a-42bd-ae81-74b26f67b3eb"},{"group":"left","id":"1a7be646-1507-4122-94e0-0ad9d0ab0554"}]},"zIndex":1},{"position":{"x":590,"y":350},"size":{"width":480,"height":100},"attrs":{"text":{"text":"平行四边形"},"body":{"refPoints":"10,0 40,0 30,20 0,20","fill":"#7A0289","stroke":"#49007A"},"label":{"text":"平行四边形","fontSize":"40","fill":"#FFFFFF"}},"visible":true,"shape":"polygon","id":"9e97d5af-ae22-47e0-bb9f-a0ebf8db0910","data":{"type":"defaultRhomboid"},"ports":{"items":[{"group":"top","id":"c6a92550-ceb3-411c-8c4c-7848aa064b76"},{"group":"right","id":"40ce97eb-f258-4b8c-b3bc-b798b4662b77"},{"group":"bottom","id":"a47a0d67-e30b-4149-91b7-527c47043f0a"},{"group":"left","id":"ef42ef10-d01d-49b1-95da-2f19189adf29"}]},"zIndex":2},{"position":{"x":940,"y":10},"size":{"width":80,"height":80},"attrs":{"text":{"text":"圆形"},"body":{"fill":"#CD0000","stroke":"#950000"},"label":{"text":"圆形","fontSize":16,"fill":"#FFFFFF"}},"visible":true,"shape":"circle","id":"e78740b8-f27f-4f3d-b1d4-11e6e810c76a","data":{"type":"defaultCircle"},"ports":{"items":[{"group":"top","id":"11a5b89d-2e19-44db-ad6b-2f03983ea097"},{"group":"right","id":"7ae5d159-3823-4b7f-9e2b-994405071dc8"},{"group":"bottom","id":"358110af-4dd6-4806-8b24-46b2b34b2f7c"},{"group":"left","id":"122093c7-de48-435b-95aa-8ef9cf58a9ca"}]},"zIndex":3}]
      export default {
        name: "jsplumb",
        data() {
          return {
            
          }
        },
        created() {
        },
        mounted () {
          this.initGraph()
        },
        methods: {
          // 初始化渲染画布
          initGraph(){
            const graph = new Graph({
              container: document.getElementById('wrapper'),
              grid: true,
              autoResize: true,
              interacting: false,
              connecting: {
                router: {
                  name: 'manhattan',
                  args: {
                    padding: 1,
                  },
                },
                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,
                  })
                }
              },
            })
            // 返现方法
            const portsGroups = configNodePorts().groups
            if(resData.length){
              const jsonTemp = resData.map(item=>{
                if(item.ports) item.ports.groups = portsGroups 
                return item
              })
              graph.fromJSON(jsonTemp)
            }
            graph.centerContent()
          }
        }
      }
    </script>
    <style lang="scss">
    @keyframes ant-line {
      to {
          stroke-dashoffset: -1000
      }
    }
    </style>
    <style lang="scss" scoped="scoped">
      .antv-wrapper{
        position: relative;
        height: 100vh;
        flex: 1;
        .wrapper-canvas{
          height: 100%;
          width: 100%;
          position: relative;
        }
        .wrapper-tips{
          padding: 10px;
          display: flex;
          align-items: center;
          position: absolute;
          top: 0;
          left: 0;
          .wrapper-tips-item{
            span{
              padding-left: 10px;
              font-size: 12px;
            }
          }
        }
      }
    </style>
    
    

    相关文章

      网友评论

          本文标题:关于AntV-X6在Vue里面是使用

          本文链接:https://www.haomeiwen.com/subject/igcpwrtx.html