antV G6流程圖在Vue中的使用

原文首發於個人博客,歡迎點擊查看得到更好的閱讀體驗~

更新內容

最近我司項目中須要加入流程圖製做功能,因而乎百度各類找可視化繪製拓撲圖的輪子,大部分都是國外的,看文檔太吃力,不過好在最終讓我發現了AntV G6流程圖圖表庫,最新版爲2.0,不過編輯器在2.0版本尚未進行開源,因此只能退而求其次,使用了1.2.8版本。但願2.0版本的編輯器儘早開源,在交互方面1.2.8版本仍是差了一些。

該組件並非開箱即食,須要根據本身的業務進行修改,右側屬性表單部分若是有時間考慮改成插槽形式,方便之後複用~node

技術棧

效果圖

工做流圖.gif

引入

index.html中進行了全局引用git

<script src="./static/plugin/g6.min.js"></script>

實例代碼

仿照2.0版本的編輯器將G6做爲了一個組件使用,代碼:github

<template>
  <div id="flowChart">
    <div class="operating">
      <div class="btn-group">
        <div class="btn" @click="addCircle" title="起始節點">
          <i class="iconfont icon-circle-oeps"></i>
        </div>
        <div class="btn" @click="addRect" title="常規節點">
          <i class="iconfont icon-square-oeps"></i>
        </div>
        <div class="btn" @click="addRhombus" title="條件節點">
          <i class="iconfont icon-square-ling"></i>
        </div>
      </div>
      <div class="btn-group">
        <div class="btn" @click="addLine" title="直線">
          <i class="iconfont icon-zhixian"></i>
        </div>
        <div class="btn" @click="addSmooth" title="曲線">
          <i class="iconfont icon-quxian"></i>
        </div>
        <div class="btn" @click="addArrowLine" title="箭頭直線">
          <i class="iconfont icon-jiantouzhixian"></i>
        </div>
        <div class="btn" @click="addArrowSmooth" title="箭頭曲線">
          <i class="iconfont icon-jiantouquxian"></i>
        </div>
      </div>
      <div class="btn-group">
        <div class="btn" @click="changeMode('edit')" title="選擇模式">
          <i class="iconfont icon-chose"></i>
        </div>
        <div class="btn" @click="changeMode('drag')" title="拖拽模式">
          <i class="iconfont icon-move"></i>
        </div>
      </div>
      <div class="btn-group">
        <div class="btn" @click="del" style="margin-top: 5px;" title="刪除">
          <i class="el-icon-delete"></i>
        </div>
        <div class="btn" @click="save" title="保存">
          <i class="iconfont icon-baocun"></i>
        </div>
      </div>
      <div class="btn-group">
        <el-input size="mini" v-model="workflowName" placeholder="請輸入流圖名稱..."></el-input>
      </div>
    </div>
    <div class="info">
      <div class="title">
        <span>{{infoTitle}}屬性</span>
      </div>
      <div class="content">
        <el-checkbox v-if="isBlank === true" v-model="checked">網格對齊</el-checkbox>
        <el-form v-else label-position="left" label-width="60px">
          <el-form-item v-if="isNode !== true" label="動做">
            <el-select v-model="action" size="mini" filterable placeholder="綁定動做" value="">
              <el-option
                v-for="item in actionList"
                :key="item.id"
                :label="item.label"
                :value="item.id">
              </el-option>
            </el-select>
          </el-form-item>   <!-- 線-->
          <el-form-item v-if="isNode === true" label="名稱">
            <el-input size="mini" v-model="name"></el-input>
          </el-form-item>
          <el-form-item v-if="isNode === true" label="功能">
            <el-select v-model="func" size="mini" filterable placeholder="綁定功能" value="">
              <el-option
                v-for="item in funcList"
                :key="item.id"
                :label="item.label"
                :value="item.id">
              </el-option>
            </el-select>
          </el-form-item>
          <el-form-item v-if="isNode === true" label="帳號">
            <el-select v-model="account" size="mini" filterable multiple
                       collapse-tags placeholder="綁定帳號" value="">
              <el-option
                v-for="item in accountList"
                :key="item.id"
                :label="item.label"
                :value="item.id">
              </el-option>
            </el-select>
          </el-form-item>
          <el-form-item v-if="isNode === true" label="流圖">
            <el-select v-model="workflow" size="mini" filterable clearable placeholder="綁定流圖" value="">
              <el-option
                v-for="item in workflowList"
                :key="item.id"
                :label="item.label"
                :value="item.id">
              </el-option>
            </el-select>
          </el-form-item>
          <el-form-item v-if="isNode === true" label="類型">
            <el-select v-model="nodeType" size="mini" filterable placeholder="請選擇類型" value="">
              <el-option
                v-for="item in nodeTypeList"
                :key="item.id"
                :label="item.label"
                :value="item.id">
              </el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="顏色">
            <el-color-picker v-model="color"></el-color-picker>
          </el-form-item>
        </el-form>
      </div>
    </div>
  </div>
</template>

<script>

  export default {
    name: "index",
    components: {},
    mounted() {
      this.initG6();
    },
    props: {
      actionList: {
        type: Array, default: []
      },
      funcList: {
        type: Array, default: []
      },
      accountList: {
        type: Array, default: []
      },
      workflowList: {
        type: Array, default: []
      },
      nodeTypeList: {
        type: Array, default: () => {
          return [
            {id: 0, label: '普通節點'},
            {id: 1, label: '入口節點'},
            {id: 2, label: '出口節點'}
          ]
        }
      }
    },
    data() {
      return {
        action: '',
        name: '',
        func: '',
        account: '',
        workflow: '',
        nodeType: 0,
        color: '',

        net: '',
        Util: '',
        workflowName: '',
        activation: '', //當前激活的節點
        isNode: false, //當前是節點
        isBlank: true,   //當前是空白區
        checked: true,  //網格對齊
        infoTitle: '畫布',//屬性標題
        oldColor: '',    //獲取節點自己顏色
        type: '',        //有值爲編輯狀態
      }
    },
    methods: {
      initG6() {
        let self = this;
        self.Util = G6.Util;
        let grid;
        if (self.checked) {
          grid = {
            forceAlign: true, // 是否支持網格對齊
            cell: 25,         // 網格大小
          };
        } else {
          grid = null;
        }
        self.net = new G6.Net({
          id: 'flowChart',      // 容器ID
          mode: 'edit',
          grid: grid,
          /*width: 500,    // 畫布寬*/
          height: 800    // 畫布高
        });
        /*self.net.tooltip({
          title: '信息', // @type {String} 標題
          split: ':',  // @type {String} 分割符號
          dx: 0,       // @type {Number} 水平偏移
          dy: 0        // @type {Number} 豎直偏移
        });*/

        /**
         *點擊空白處
         */
        self.net.on('click', (ev) => {
          if (!self.Util.isNull(ev.item)) {
            self.isBlank = false
          } else {
            self.isBlank = true;
            self.infoTitle = '畫布'
          }
        });
        /**
         *點擊節點
         */
        self.net.on('itemclick', function (ev) {
          self.isNode = self.Util.isNode(ev.item);   //是否爲Node
          self.activation = ev.item;
          if (self.isNode) {
            /* 激活節點後節點名稱input聚焦*/
            self.$nextTick(()=>{
              self.$refs.inputFocus.$el.querySelector('input').focus();
            });
            self.infoTitle = '節點';
            self.name = ev.item.get('model').label;
            self.func = ev.item.get('model').func;
            self.account = ev.item.get('model').account || [];
            self.workflow = ev.item.get('model').workflow;
            self.nodeType = ev.item.get('model').nodeType;
          } else {
            self.infoTitle = '邊';
            self.action = ev.item.get('model').action;
          }
          self.color = self.oldColor;
        });
        /**
         * 鼠標移入移出事件改變顏色
         */
        self.net.on('itemmouseenter', ev => {
          const item = ev.item;
          self.oldColor = item.get('model').color;     //獲取節點顏色
          self.net.update(item, {
            color: '#108EE9',
          });
          self.net.refresh();
        });
        self.net.on('itemmouseleave', ev => {
          const item = ev.item;
          self.net.update(item, {
            color: self.oldColor
          });
          self.net.refresh();
        });
        /**
         * 提示信息
         */
       /* self.net.node().tooltip(['label', 'func', 'role', 'color']);
        self.net.edge().tooltip(['label', 'color']);*/
        /**
         * 渲染
         */
        /*self.net.source(self.nodes, self.edges);*/  //加載資源數據
        self.net.render();
      },
      addCircle() {
        this.net.beginAdd('node', {
          shape: 'circle',
          nodeType: 0
        })
      },//添加起始節點
      addRect() {
        this.net.beginAdd('node', {
          shape: 'rect',
          nodeType: 0
        })
      },//添加常規節點
      addRhombus() {
        this.net.beginAdd('node', {
          shape: 'rhombus',
          nodeType: 0
        })
      }, //添加條件節點
      addLine() {
        this.net.beginAdd('edge', {
          shape: 'line'
        });
      }, //添加直線
      addSmooth() {
        this.net.beginAdd('edge', {
          shape: 'smooth'
        })
      },  //添加曲線
      addArrowSmooth() {
        this.net.beginAdd('edge', {
          shape: 'smoothArrow'
        })
      }, //添加箭頭曲線
      addArrowLine() {
        this.net.beginAdd('edge', {
          shape: 'arrow'
        });
      }, //添加箭頭直線
      addPolyLine() {
        this.net.beginAdd('edge', {
          shape: 'polyLineFlow'
        });
      }, //添加折線
      changeMode(mode) {
        this.net.changeMode(mode)
      }, //拖拽與編輯模式的切換
      del() {
        this.net.del()
      },//刪除
      save() {
        /* 驗證流圖名稱*/
        if (this.workflowName !== '') {
          let data = this.net.save();
          if (data.source.nodes.length === 0) {
            this.$message({type: 'error', message: '流圖內容不能爲空'});
            return false
          }
          /* 驗證節點名稱*/
          for (let item of data.source.nodes) {
            if (item.label === '' || item.label === null || item.label === undefined) {
              this.$message({type: 'error', message: '節點名稱不能爲空'});
              return false
            }
          }
          data.source['name'] = this.workflowName;
          /*let json = JSON.stringify(data, null, 2);*/
          this.$emit('saveData', data.source, this.type);
        } else {
          this.$message({type: 'error', message: '流圖名稱不能爲空'})
        }
        /*console.log(saveData, json);*/
      },//保存
      update() {
        if (this.activation.get('type') === 'node') {
          this.net.update(this.activation, {
            label: this.name,
            func: this.func,
            account: this.account,
            workflow: this.workflow,
            nodeType: this.nodeType,
            color: this.color
          });
        } else {
          /* 根據ID取出label*/
          let label = this.actionList.map(item => {
            if (item.id === this.action) {
              return item.label
            }
          }).join('');
          this.net.update(this.activation, {
            label: label,
            color: this.color,
            action: this.action
          });
        }
      },  //更新節點
      clearView() {
        this.type = '';
        this.workflowName = '';
        this.net.changeData()
      },   //清空視圖
      source(nodes, edges, name, type) {
        this.type = type;
        this.workflowName = name;
        this.net.changeData(nodes, edges)
      },  //更新數據
    },
    watch: {
      /**
       * 監聽輸入框
       */
      action: function () {
        this.update()
      },
      name: function () {
        this.update()
      },
      func: function () {
        this.update()
      },
      account: function () {
        this.update()
      },
      workflow: function () {
        this.update()
      },
      nodeType: function () {
        this.update()
      },
      color: function () {
        this.update()
      },
      /**
       * 網格切換
       */
      checked: function () {
        let _saveData = this.net.save();
        this.net.destroy();  //銷燬畫布
        this.initG6();
        this.net.read(_saveData);
        this.net.render()
      }
    }
  }
</script>

<style rel="stylesheet/scss" lang="scss" scoped>
  #flowChart {
    border: 1px solid #ebeef5;
    position: relative;
    overflow: hidden;
  }

  .operating {
    position: absolute;
    z-index: 99;
    background-color: #ffffff;
    padding: 20px 10px;
    box-shadow: 1px 1px 4px 0 #0a0a0a2e;
  }

  .info {
    position: absolute;
    right: 0;
    z-index: 99;
    box-shadow: 1px 1px 4px 0 #0a0a0a2e;
    .title {
      height: 40px;
      padding-left: 10px;
      border-top: 1px solid #DCE3E8;
      border-bottom: 1px solid #DCE3E8;
      border-left: 1px solid #DCE3E8;
      background: rgb(235, 238, 242);
      line-height: 40px;
      span {
        font-size: 14px;
      }
    }
    .content {
      background: rgba(247, 249, 251, 0.45);
      width: 200px;
      height: 800px;
      border-left: 1px solid #E6E9ED;
      padding: 10px;
    }
  }

  .btn-group {
    border-right: 1px solid #efefef;
    display: inline-block;
    padding-left: 10px;
    padding-right: 14px;
    &:last-of-type {
      border-right: 0;
    }
    .btn {
      display: inline-block;
      margin: 2px;
      width: 30px;
      height: 30px;
      line-height: 30px;
      text-align: center;
      cursor: pointer;
      border: 1px solid rgba(233, 233, 233, 0);
      i {
        font-size: 20px;
      }
      &:hover {
        border: 1px solid #E9E9E9;
        color: #767A85;
        border-radius: 2px;
        background: #FAFAFE;
      }
    }
    .el-form-item {
      margin-bottom: 0 !important;
    }
  }
</style>

流圖屬性

參數 說明 類型 可選值 默認值
actionList 動做數據 Array —— []
funcList 功能數據 Array —— []
accountList 帳號數據 Array —— []
workflowList 流圖數據 Array —— []
nodeTypeList 節點類型數據 Array —— [{id: 0, label: '普通節點'},{id: 1, label: '入口節點'},{id: 2, label: '出口節點'}]
全部屬性接收的數據格式須要與 nodeTypeList的默認值相同

流圖事件

事件名 說明 參數
saveData 當用戶手動點擊保存觸發事件 source,type
參數 type可爲空,在此項目中主要用來區分 新建編輯

流圖方法

方法名 說明 參數
clearView 清空當前視圖 ——
source 渲染數據 nodes, edges, name, type
參數 type與事件中相同,參數 name的做用是用來取流圖名

參考文檔

使用 G6關係圖類庫 開發流程圖工具npm

舊版本G6 1.x API 文檔json

新版本G6 2.x API 文檔api

相關文章
相關標籤/搜索