• 基于vue2.x+antv x6实现可拖拽编辑拓流程拓扑图


    最近公司要求做一个拓扑流程图,在网上搜寻了一些可行性方案之后,发现好一点的可视化拓扑图都是要收费的,于是决定自己在阿里的antv x6基础上做出一款简单的产品,以便于后期进行修改和操作

     项目主要版本号:

    vue版本:2.x,

    antv:@antv/x6": "^1.17.3",   "@antv/x6-vue-shape": "^1.2.0", 

    css预编译器: "less": "^3.10.3", 

    ui框架:iview ,"view-design": "^4.1.1",

    项目主要贴图gif:

    具体的项目代码下载地址,欢迎点赞+关注:https://gitee.com/yanggengzhen/vue-antvx6-demo/tree/master

    贴上部分代码:

    <template>
        <div class="container_warp">
          <div id="containerChart"></div>
          <RightDrawer class="right_drawer" :drawerType="type" :selectCell="selectCell" :graph="graph" :grid="grid" @deleteNode="deleteNode"></RightDrawer>
          <div class="operating">
            <div class="btn-group">
              <div class="btn" title="圆形节点" @mousedown="startDrag('Circle',$event)">
                <i class="iconfont icon-circle"></i>
              </div>
              <div class="btn" title="正方形节点" @mousedown="startDrag('Rect',$event)">
                <i class="iconfont icon-square"></i>
              </div>
              <div class="btn" title="条件节点">
                <i class="iconfont icon-square rotate-square" @mousedown="startDrag('polygon',$event)"></i>
              </div>
              <div class="btn-group_tips" v-if="showTips">
                拖拽生成</br>资产拓扑图形
              </div>
            </div>
            <div class="btn-group">
              <Tooltip content="直线箭头" placement="bottom">
                  <div :class=" ['btn',currentArrow === 1?'currentArrow':'']" @click="changeEdgeType('normal')">
                    <i class="iconfont icon-ai28"></i>
                  </div>
              </Tooltip>
              <Tooltip content="曲线箭头" placement="bottom">
                  <div :class=" ['btn',currentArrow === 2?'currentArrow':'']" @click="changeEdgeType('smooth')">
                    <i class="iconfont icon-Down-Right"></i>
                  </div>
              </Tooltip>
              <Tooltip content="直角箭头" placement="bottom">
                  <div :class=" ['btn',currentArrow === 3?'currentArrow':'']" @click="changeEdgeType()">
                    <i class="iconfont icon-jiantou"></i>
                  </div>
              </Tooltip>
            </div>
            <div class="btn-group">
              <Tooltip content="删除" placement="bottom">
                  <div class="btn" @click="deleteNode()" style="margin-top: 5px;">
                    <i class="iconfont icon-shanchu"></i>
                  </div>
              </Tooltip>
              <Tooltip content="保存PNG" placement="bottom">
                  <div class="btn" @click="saveToPNG()" title="保存">
                    <i class="iconfont icon-baocun"></i>
                  </div>
              </Tooltip>
            </div>
          </div>
        </div>
        
    </template>
    <script>
    import '@antv/x6-vue-shape'
    import { Graph,Shape,Addon,FunctionExt,DataUri} from '@antv/x6';
    import RightDrawer from './components/RightDrawer';
    import insertCss from 'insert-css';
    import {startDragToGraph} from './Graph/methods.js'
    const data = {};
    export default {
        data() {
          return {
            graph:'',
            value1: true,
            type:'grid',
            selectCell:'',
            connectEdgeType:{  //连线方式
              connector: 'normal',
              router: {
                name: ''
              }
            },
            showTips:false,
            currentArrow:1,
            grid:{ // 网格设置
              size: 20,      // 网格大小 10px
              visible: true, // 渲染网格背景
              type: 'mesh',
              args: {
                color: '#D0D0D0',
                thickness: 1,     // 网格线宽度/网格点大小
                factor: 10, 
              },
            }
          }
        },
        components:{
          RightDrawer
        },
        methods: {
            initX6(){
                var _that = this
                this.graph = new Graph({
                    container: document.getElementById('containerChart'),
                     1700,
                    height: '100%',
                    grid: _that.grid,
                    resizing: { //调整节点宽高
                      enabled: true,
                      orthogonal:false,
                    }, 
                    selecting: true, //可选
                    snapline:  true,
                    interacting: {
                      edgeLabelMovable: true
                    },
                    connecting: { // 节点连接
                      anchor: 'center',
                      connectionPoint: 'anchor',
                      allowBlank: false,
                      snap: true,
                      createEdge () {
                        return new Shape.Edge({
                          attrs: {
                            line: {
                              stroke: '#1890ff',
                              strokeWidth: 1,
                              targetMarker: {
                                name: 'classic',
                                size: 8
                              },
                              strokeDasharray: 0, //虚线
                              style: {
                                animation: 'ant-line 30s infinite linear',
                              },
                            },
                          },
                          label: {
                            text:''
                          },
                          connector: _that.connectEdgeType.connector,
                          router: {
                            name: _that.connectEdgeType.router.name || ''
                          },
                          zIndex: 0
                        })
                      },
                    },
                    highlighting: {
                      magnetAvailable: {
                        name: 'stroke',
                        args: {
                          padding: 4,
                          attrs: {
                            strokeWidth: 4,
                            stroke: '#6a6c8a'
                          }
                        }
                      }
                    },
                });
                insertCss(`
                  @keyframes ant-line {
                    to {
                        stroke-dashoffset: -1000
                    }
                  }
                `)
                this.graph.fromJSON(data)
                this.graph.history.redo()
                this.graph.history.undo()
                // 鼠标移入移出节点
                this.graph.on('node:mouseenter',FunctionExt.debounce(() => {
                    const container =  document.getElementById('containerChart')
                    const ports = container.querySelectorAll(
                      '.x6-port-body'
                    )
                    this.showPorts(ports, true)
                  }),
                  500
                )
                this.graph.on('node:mouseleave', () => {
                  const container =  document.getElementById('containerChart')
                  const ports = container.querySelectorAll(
                    '.x6-port-body'
                  )
                  this.showPorts(ports, false)
                })
                this.graph.on('blank:click', () => {
                  this.type = 'grid'
                })
                this.graph.on('cell:click', ({ cell }) => {
                  this.type = cell.isNode() ? 'node' : 'edge'
                })
                this.graph.on('selection:changed', (args) => {
                  args.added.forEach(cell => {
                    this.selectCell = cell
                    if(cell.isEdge()){
                     cell.isEdge() && cell.attr('line/strokeDasharray', 5) //虚线蚂蚁线
                     cell.addTools([
                        {
                          name: 'vertices',
                          args: {
                            padding: 4,
                            attrs: {
                              strokeWidth: 0.1,
                              stroke: '#2d8cf0',
                              fill: '#ffffff',
                            }
                          },
                        },
                      ])
                    }
                  })
                  args.removed.forEach(cell => {
                    cell.isEdge() && cell.attr('line/strokeDasharray', 0)  //正常线
                    cell.removeTools()
                  })
                })
            },
            showPorts (ports, show) {
              for (let i = 0, len = ports.length; i < len; i = i + 1) {
                ports[i].style.visibility = show ? 'visible' : 'hidden'
              }
            },
            // 拖拽生成正方形或者圆形
            startDrag(type,e){
              startDragToGraph(this.graph,type,e)
            },
            // 删除节点
            deleteNode(){
              const cell = this.graph.getSelectedCells()
              this.graph.removeCells(cell)
              this.type = 'grid'
            },
            // 保存png
            saveToPNG(){
              this.$nextTick(()=>{
                this.graph.toPNG((dataUri) => {
                // 下载
                  DataUri.downloadDataUri(dataUri, '资产拓扑图.png')
                },{
                  backgroundColor: 'white',
                  padding: {
                    top: 50,
                    right: 50,
                    bottom: 50,
                    left: 50
                  },
                  quality: 1,
                  copyStyles:false
                })
              })
              
            },
            // 改变边形状
            changeEdgeType(e){
              if(e === 'normal'){
                this.connectEdgeType = {
                  connector: 'normal',
                  router: {name: ''}
                }
                this.currentArrow = 1
              }else if (e === 'smooth'){
                this.connectEdgeType = {
                  connector: 'smooth',
                  router: {name: ''}
                }
                this.currentArrow = 2
              }else{
                this.connectEdgeType = {
                  connector: 'normal',
                  router: {name: 'manhattan'}
                }
                this.currentArrow = 3
              }
            }
        },
        mounted(){
            this.initX6()
            setTimeout(()=>{
              this.showTips = true
            },1000)
            setTimeout(()=>{
              this.showTips = false
            },5000)
        }
    };
    </script>
    <style lang="less">
      @import '../assets/iconfont.css';
      @import './index.less';
    </style>
    <template>
        <div class="drawer_container">
            <div v-if="drawerType === 'grid'">
                <div class="drawer_title">画布背景设置</div>
                <div class="drawer_wrap">
                    <Form label-position="left" :label-width="85">
                        <FormItem label="是否显示网格" :label-width="100">
                            <i-switch v-model="showGrid" @on-change="changeGrid" />
                        </FormItem>
                        <div v-show="showGrid">
                            <FormItem label="网格类型">
                                <RadioGroup v-model="grid.type" @on-change="changeGridType">
                                    <Radio v-for="item in gridTypeList" :label="item.value" :key="item.value">
                                        <span>{{item.label}}</span>
                                    </Radio>
                                </RadioGroup>
                            </FormItem>
                            <FormItem label="网格大小">
                                <Slider v-model="grid.size" :min="0" :max="30" @on-change="changeGrid"></Slider>
                            </FormItem>
                            <FormItem label="网格颜色">
                                <ColorPicker v-model="grid.args.color" @on-change="changeGrid"/>
                            </FormItem>
                            <FormItem label="网格线宽度">
                                <Slider v-model="grid.args.thickness" :min="0" :max="20" @on-change="changeGrid"></Slider>
                            </FormItem>
                        </div>
                        
                    </Form>
                </div>
            </div>
            <div v-if="drawerType === 'node'">
                <div class="drawer_title">节点设置</div>
                <div class="drawer_wrap">
                    <Form label-position="left" :label-width="80">
                        <FormItem label="节点文本">
                            <Input v-model="drawerNode.nodeText" @on-change="changeNodeText"></Input>
                        </FormItem>
                        <FormItem label="节点背景">
                            <ColorPicker v-model="drawerNode.fill" @on-change="changeFill"/>
                        </FormItem>
                        <FormItem label="字体大小">
                            <Slider v-model="drawerNode.fontSize" :min="10" :max="20" @on-change="changefontSize"></Slider>
                        </FormItem>
                        <FormItem label="字体颜色">
                             <ColorPicker v-model="drawerNode.fontFill" @on-change="changeFontFill"/>
                        </FormItem>
                        <FormItem label="边框宽度">
                            <Slider v-model="drawerNode.strokeWidth" :min="0" :max="10" @on-change="changeStrokeWidth"></Slider>
                        </FormItem>
                        <FormItem label="边框颜色">
                            <ColorPicker v-model="drawerNode.stroke" @on-change="changeStroke"/>
                        </FormItem>
                        <FormItem label="功能">
                            <Button type="primary" icon="md-trending-up" @click="toTopZIndex">置顶</Button>
                            <Button type="error" class="margin-left-10" icon="md-trash" @click="deleteNode">删除</Button>
                        </FormItem>
                    </Form>
                </div>
            </div>
            <div v-if="drawerType === 'edge'">
                <div class="drawer_title">线条设置</div>
                <div class="drawer_wrap">
                    <Form label-position="left" :label-width="80">
                        <FormItem label="线条文本">
                            <Input v-model="drawerEdge.EdgeText" @on-change="changeEdgeText"></Input>
                        </FormItem>
                        <FormItem label="线条宽度">
                            <Slider v-model="drawerEdge.edgeWidth" :min="1" :max="10" @on-change="changeEdgeWidth"></Slider>
                        </FormItem>
                        <FormItem label="线条颜色">
                            <ColorPicker v-model="drawerEdge.edgeColor" @on-change="changeEdgeColor"/>
                        </FormItem>
                        <FormItem label="功能">
                            <Button type="primary" icon="md-trending-up" @click="toTopZIndex">置顶</Button>
                            <Button type="error" class="margin-left-10" icon="md-trash" @click="deleteNode">删除</Button>
                        </FormItem>
                    </Form>
                </div>
            </div>
        </div>
    </template>
    
    <script>
    export default {
        name:'RightDrawer',
        data() {
            return {
                gridTypeList:[
                    {
                        label:'四边网格',
                        value:'mesh'
                    },
                    {
                        label:'点状网格',
                        value:'dot'
                    }
                ],
                showGrid:true,
                drawerNode:{
                    fill:'',
                    nodeText:'',
                    fontSize:null,
                    fontFill:'',
                    strokeWidth:null,
                    stroke:''
                },
                drawerEdge:{
                    EdgeText:'',
                    edgeWidth:null,
                    edgeColor:''
                },
            };
        },
        props:{
            drawerType: {
                type: String
            },
            selectCell:{
                type: String | Object
            },
            graph:{
                type: String | Object
            },
            grid:{
                type: Object
            }
        },
        created() {
            
        },
        mounted() {
        },
        watch:{
            selectCell:{
                handler(val) {
                    if(val){
                        if(val.isNode()){ //节点
                            this.drawerNode.fill = val.store.data.attrs.body.fill
                            this.drawerNode.nodeText = val.store.data.attrs.label.text
                            this.drawerNode.fontFill = val.store.data.attrs.label.fill
                            this.drawerNode.fontSize = Number(val.store.data.attrs.label.fontSize)
                            this.drawerNode.strokeWidth = Number(val.store.data.attrs.body.strokeWidth)
                            this.drawerNode.stroke = val.store.data.attrs.body.stroke
                        }else{ //
                            this.drawerEdge.EdgeText = val.store.data.labels?val.store.data.labels[0].text:''
                            this.drawerEdge.edgeWidth = Number(val.store.data.attrs.line.strokeWidth)
                            this.drawerEdge.edgeColor = val.store.data.attrs.line.stroke
                        }
                    }
                },
                immediate: true,
                deep: false
            },
        },
        methods: {
            // 网格设置
            changeGrid(){
                this.showGrid?this.graph.showGrid():this.graph.hideGrid()
            },
            changeGridType(e){
                this.grid.type = e
                this.changeGrid()
            },
            changeGrid(){
                this.graph.drawGrid({
                    ...this.grid
                })
            },
            // 节点设置
            changeStrokeWidth(val){
                this.selectCell.attr('body/strokeWidth', val)
            },
            changefontSize(val){
                this.selectCell.attr('label/fontSize',val)
            },
            changeNodeText(){
                this.selectCell.attr('label/text', this.drawerNode.nodeText)
            },
            changeStroke(val){
                this.drawerNode.stroke = val
                this.selectCell.attr('body/stroke', this.drawerNode.stroke)
            },
            changeFontFill(val){
                this.drawerNode.fontFill = val
                this.selectCell.attr('label/fill', this.drawerNode.fontFill)
            },
            changeFill(val){
                this.drawerNode.fill = val
                this.selectCell.attr('body/fill', val)
            },
            // 边设置
            changeEdgeText(){
                console.log(this.drawerEdge.EdgeText);
                this.selectCell.setLabels( 
                    [{attrs:{label:{text:this.drawerEdge.EdgeText}}}]
                )
            },
            changeEdgeWidth(val){
                this.drawerEdge.edgeWidth = val
                this.selectCell.attr('line/strokeWidth', this.drawerEdge.edgeWidth)
            },
            changeEdgeColor(val){
                this.drawerEdge.stroke = val
                this.selectCell.attr('line/stroke', this.drawerEdge.stroke)
            },
            // 置顶
            toTopZIndex(){
                this.selectCell.toFront()
            },
            // 删除
            deleteNode(){   
                this.$emit('deleteNode')
            },
        },
    };
    </script>
    
    <style lang="less" scoped>
        .drawer_container {
            max- 300px;
            min- 300px;
            .drawer_title {
                border-bottom: 1px solid #e8eaec;
                box-sizing: border-box;
                padding: 14px 16px;
                color: #333;
                font-size: 16px;
            }
            .drawer_wrap {
                box-sizing: border-box;
                padding: 20px 10px 20px 20px;
            }
        }
    </style>
  • 相关阅读:
    Qt Examples Qt实例汇总
    [转帖] VS集成Qt环境搭建
    GTKmm 学习资料
    Programming with gtkmm 3
    CvMat and cv::Mat
    [LeetCode] Longest Consecutive Sequence 求最长连续序列
    [转帖] CvMat,Mat和IplImage之间的转化和拷贝
    [LeetCode] Sum Root to Leaf Numbers 求根到叶节点数字之和
    [LeetCode] Palindrome Partitioning II 拆分回文串之二
    [LeetCode] Palindrome Partitioning 拆分回文串
  • 原文地址:https://www.cnblogs.com/gengzhen/p/14652385.html
Copyright © 2020-2023  润新知