antV G6流程圖在Vue中的使用

原文首發(fā)于我的博客,歡迎點(diǎn)擊查看獲得更好的閱讀體驗(yàn)~

更新內(nèi)容

最近我司項(xiàng)目中需要加入流程圖制作功能,于是乎百度各種找可視化繪制拓?fù)鋱D的輪子,大部分都是國(guó)外的,看文檔太吃力,不過(guò)好在最終讓我發(fā)現(xiàn)了AntV G6流程圖圖表庫(kù),最新版為2.0,不過(guò)編輯器在2.0版本還沒(méi)有進(jìn)行開(kāi)源,所以只能退而求其次,使用了1.2.8版本。希望2.0版本的編輯器盡早開(kāi)源,在交互方面1.2.8版本還是差了一些。

該組件并非開(kāi)箱即食,需要根據(jù)自己的業(yè)務(wù)進(jìn)行修改,右側(cè)屬性表單部分如果有時(shí)間考慮改為插槽形式,方便以后復(fù)用~

技術(shù)棧

效果圖

工作流圖.gif

引入

index.html中進(jìn)行了全局引用


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

實(shí)例代碼

仿照2.0版本的編輯器將G6作為了一個(gè)組件使用,代碼:

<template>
  <div id="flowChart">
    <div class="operating">
      <div class="btn-group">
        <div class="btn" @click="addCircle" title="起始節(jié)點(diǎn)">
          <i class="iconfont icon-circle-oeps"></i>
        </div>
        <div class="btn" @click="addRect" title="常規(guī)節(jié)點(diǎn)">
          <i class="iconfont icon-square-oeps"></i>
        </div>
        <div class="btn" @click="addRhombus" title="條件節(jié)點(diǎn)">
          <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="請(qǐng)輸入流圖名稱(chēng)..."></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">網(wǎng)格對(duì)齊</el-checkbox>
        <el-form v-else label-position="left" label-width="60px">
          <el-form-item v-if="isNode !== true" label="動(dòng)作">
            <el-select v-model="action" size="mini" filterable placeholder="綁定動(dòng)作" 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="名稱(chēng)">
            <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="賬號(hào)">
            <el-select v-model="account" size="mini" filterable multiple
                       collapse-tags placeholder="綁定賬號(hào)" 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="類(lèi)型">
            <el-select v-model="nodeType" size="mini" filterable placeholder="請(qǐng)選擇類(lèi)型" 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: '普通節(jié)點(diǎn)'},
            {id: 1, label: '入口節(jié)點(diǎn)'},
            {id: 2, label: '出口節(jié)點(diǎn)'}
          ]
        }
      }
    },
    data() {
      return {
        action: '',
        name: '',
        func: '',
        account: '',
        workflow: '',
        nodeType: 0,
        color: '',

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

        /**
         *點(diǎn)擊空白處
         */
        self.net.on('click', (ev) => {
          if (!self.Util.isNull(ev.item)) {
            self.isBlank = false
          } else {
            self.isBlank = true;
            self.infoTitle = '畫(huà)布'
          }
        });
        /**
         *點(diǎn)擊節(jié)點(diǎn)
         */
        self.net.on('itemclick', function (ev) {
          self.isNode = self.Util.isNode(ev.item);   //是否為Node
          self.activation = ev.item;
          if (self.isNode) {
            /* 激活節(jié)點(diǎn)后節(jié)點(diǎn)名稱(chēng)input聚焦*/
            self.$nextTick(()=>{
              self.$refs.inputFocus.$el.querySelector('input').focus();
            });
            self.infoTitle = '節(jié)點(diǎn)';
            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;
        });
        /**
         * 鼠標(biāo)移入移出事件改變顏色
         */
        self.net.on('itemmouseenter', ev => {
          const item = ev.item;
          self.oldColor = item.get('model').color;     //獲取節(jié)點(diǎn)顏色
          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);*/  //加載資源數(shù)據(jù)
        self.net.render();
      },
      addCircle() {
        this.net.beginAdd('node', {
          shape: 'circle',
          nodeType: 0
        })
      },//添加起始節(jié)點(diǎn)
      addRect() {
        this.net.beginAdd('node', {
          shape: 'rect',
          nodeType: 0
        })
      },//添加常規(guī)節(jié)點(diǎn)
      addRhombus() {
        this.net.beginAdd('node', {
          shape: 'rhombus',
          nodeType: 0
        })
      }, //添加條件節(jié)點(diǎn)
      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() {
        /* 驗(yàn)證流圖名稱(chēng)*/
        if (this.workflowName !== '') {
          let data = this.net.save();
          if (data.source.nodes.length === 0) {
            this.$message({type: 'error', message: '流圖內(nèi)容不能為空'});
            return false
          }
          /* 驗(yàn)證節(jié)點(diǎn)名稱(chēng)*/
          for (let item of data.source.nodes) {
            if (item.label === '' || item.label === null || item.label === undefined) {
              this.$message({type: 'error', message: '節(jié)點(diǎn)名稱(chēng)不能為空'});
              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: '流圖名稱(chēng)不能為空'})
        }
        /*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 {
          /* 根據(jù)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
          });
        }
      },  //更新節(jié)點(diǎn)
      clearView() {
        this.type = '';
        this.workflowName = '';
        this.net.changeData()
      },   //清空視圖
      source(nodes, edges, name, type) {
        this.type = type;
        this.workflowName = name;
        this.net.changeData(nodes, edges)
      },  //更新數(shù)據(jù)
    },
    watch: {
      /**
       * 監(jiān)聽(tīng)輸入框
       */
      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()
      },
      /**
       * 網(wǎng)格切換
       */
      checked: function () {
        let _saveData = this.net.save();
        this.net.destroy();  //銷(xiāo)毀畫(huà)布
        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>

流圖屬性

參數(shù) 說(shuō)明 類(lèi)型 可選值 默認(rèn)值
actionList 動(dòng)作數(shù)據(jù) Array —— []
funcList 功能數(shù)據(jù) Array —— []
accountList 賬號(hào)數(shù)據(jù) Array —— []
workflowList 流圖數(shù)據(jù) Array —— []
nodeTypeList 節(jié)點(diǎn)類(lèi)型數(shù)據(jù) Array —— [{id: 0, label: '普通節(jié)點(diǎn)'},{id: 1, label: '入口節(jié)點(diǎn)'},{id: 2, label: '出口節(jié)點(diǎn)'}]

所有屬性接收的數(shù)據(jù)格式需要與nodeTypeList的默認(rèn)值相同

流圖事件

事件名 說(shuō)明 參數(shù)
saveData 當(dāng)用戶手動(dòng)點(diǎn)擊保存觸發(fā)事件 source,type

參數(shù)type可為空,在此項(xiàng)目中主要用來(lái)區(qū)分新建編輯

流圖方法

方法名 說(shuō)明 參數(shù)
clearView 清空當(dāng)前視圖 ——
source 渲染數(shù)據(jù) nodes, edges, name, type

參數(shù)type與事件中相同,參數(shù)name的作用是用來(lái)取流圖名

參考文檔

使用 G6關(guān)系圖類(lèi)庫(kù) 開(kāi)發(fā)流程圖工具

舊版本G6 1.x API 文檔

新版本G6 2.x API 文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容