溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

vue+jsplumb實(shí)現(xiàn)工作流程圖的方法

發(fā)布時間:2022-04-15 15:46:15 來源:億速云 閱讀:211 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹“vue+jsplumb實(shí)現(xiàn)工作流程圖的方法”的相關(guān)知識,小編通過實(shí)際案例向大家展示操作過程,操作方法簡單快捷,實(shí)用性強(qiáng),希望這篇“vue+jsplumb實(shí)現(xiàn)工作流程圖的方法”文章能幫助大家解決問題。

先寫了一個demo,大概樣子如下:

vue+jsplumb實(shí)現(xiàn)工作流程圖的方法

官網(wǎng)文檔Home | jsPlumb Toolkit Documentation

先安裝插件

npm install jsplumb --save

安裝panzoom,主要用于鼠標(biāo)滾輪縮放流程圖

npm install panzoom --save

在需要的頁面引入插件

import panzoom from 'panzoom'
import { jsPlumb } from 'jsplumb'

接下來先寫布局

父組件

<template>
  <div class="workflow">
    <div class="flow_region">
      <div id="flowWrap" ref="flowWrap" class="flow-wrap" @drop="drop($event)" @dragover="allowDrop($event)">
        <div id="flow">
 
          <flowNode v-for="item in data.nodeList" :id="item.id" :key="item.id" :node="item" @setNodeName="setNodeName" @changeLineState="changeLineState" />
        </div>
      </div>
    </div>
  </div>
</template>

flowNode是子組件

<template>
  <div
    ref="node"
    class="node-item"
    :class="{
      isStart: node.type === 'start',
      isEnd: node.type === 'end',
      'common-circle-node':node.type === 'start' || node.type === 'end' || node.type === 'event',
      'common-rectangle-node':node.type === 'common' || node.type === 'freedom' || node.type === 'child-flow',
      'common-diamond-node':node.type === 'gateway',
      'common-x-lane-node':node.type === 'x-lane',
      'common-y-lane-node':node.type === 'y-lane'
    }"
    :style="{
      top: node.y + 'px',
      left: node.x + 'px'
    }"
    @click="setNotActive"
    @mouseenter="showAnchor"
    @mouseleave="hideAnchor"
  >
    <div class="nodeName">{{ node.nodeName }}</div>
   
  </div>
</template>

樣式主要是子組件的,父組件樣式隨意就行

<style lang="less" scoped>
@labelColor: #409eff;
@nodeSize: 20px;
@viewSize: 10px;
.node-item {
  position: absolute;
  display: flex;
  height: 40px;
  width: 120px;
  justify-content: center;
  align-items: center;
  border: 1px solid #b7b6b6;
  border-radius: 4px;
  cursor: move;
  box-sizing: content-box;
  font-size: 12px;
  z-index: 9995;
  &:hover {
    z-index: 9998;
    .delete-btn{
      display: block;
    }
  }
  .log-wrap{
    width: 40px;
    height: 40px;
    border-right: 1px solid  #b7b6b6;
  }
  .nodeName {
    flex-grow: 1;
    width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-align: center;
  }
  .node-anchor {
    display: flex;
    position: absolute;
    width: @nodeSize;
    height: @nodeSize;
    align-items: center;
    justify-content: center;
    border-radius: 10px;
    cursor: crosshair;
    z-index: 9999;
    background: -webkit-radial-gradient(sandybrown 10%, white 30%, #9a54ff 60%);
  }
  .anchor-top{
    top: calc((@nodeSize / 2)*-1);
    left: 50%;
    margin-left: calc((@nodeSize/2)*-1);
  }
  .anchor-right{
    top: 50%;
    right: calc((@nodeSize / 2)*-1);
    margin-top: calc((@nodeSize / 2)*-1);
  }
  .anchor-bottom{
    bottom: calc((@nodeSize / 2)*-1);
    left: 50%;
    margin-left: calc((@nodeSize / 2)*-1);
  }
  .anchor-left{
    top: 50%;
    left: calc((@nodeSize / 2)*-1);
    margin-top: calc((@nodeSize / 2)*-1);
  }
}
.active{
  border: 1px dashed @labelColor;
  box-shadow: 0px 5px 9px 0px rgba(0,0,0,0.5);
}
.common-circle-node{
  border-radius: 50%;
  height: 60px;
  width: 60px;
}
</style>

頁面樣式寫完,接下來寫插件配置

jsPlumb.ready() 是一個鉤子函數(shù),它會在 jsPlumb 準(zhǔn)備完畢時執(zhí)行。

連接線的建立是通過 jsPlumb.connect() 方法實(shí)現(xiàn)的。該方法接受一個對象作為配置項(xiàng)。其中包含了與上述概念一一對應(yīng)的配置項(xiàng),以及一些額外的樣式。

source: 源對象,可以是對象的 id 屬性、Element 對象或者 Endpoint 對象。

target: 目標(biāo)對象,可以是對象的 id 屬性、Element 對象或者 Endpoint 對象。

anchor: 是一個數(shù)組,數(shù)組中每一項(xiàng)定義一個錨點(diǎn)。

初始化方法

init() {
    this.jsPlumb.ready(() => {
      // 導(dǎo)入默認(rèn)配置
      this.jsPlumb.importDefaults(this.jsplumbSetting)
      // 完成連線前的校驗(yàn)
      this.jsPlumb.bind('beforeDrop', evt => {
        const res = () => { } // 此處可以添加是否創(chuàng)建連接的校驗(yàn), 返回 false 則不添加;
        return res
      })
 
      this.loadEasyFlow()
      // 會使整個jsPlumb立即重繪。
      this.jsPlumb.setSuspendDrawing(false, true)
    })
    this.initPanZoom()
  },
 // 加載流程圖
  loadEasyFlow() {
    // 初始化節(jié)點(diǎn)
    for (let i = 0; i < this.data.nodeList.length; i++) {
      const node = this.data.nodeList[i]
      // 設(shè)置源點(diǎn),可以拖出線連接其他節(jié)點(diǎn)
      this.jsPlumb.makeSource(node.id, this.jsplumbSourceOptions)
      // // 設(shè)置目標(biāo)點(diǎn),其他源點(diǎn)拖出的線可以連接該節(jié)點(diǎn)
      this.jsPlumb.makeTarget(node.id, this.jsplumbTargetOptions)
      // this.jsPlumb.draggable(node.id);
      this.draggableNode(node.id)
    }
 
    // 初始化連線
    this.jsPlumb.unbind('connection') // 取消連接事件
    console.log(this.data.lineList)
    for (let i = 0; i < this.data.lineList.length; i++) {
      const line = this.data.lineList[i]
      const conn = this.jsPlumb.connect(
        {
          source: line.sourceId,
          target: line.targetId,
          paintStyle: {
            stroke: line.cls.linkColor,
            strokeWidth: 2
            // strokeWidth: line.cls.linkThickness
          }
        },
        this.jsplumbConnectOptions
      )
      conn.setLabel({
        label: line.label,
        cssClass: `linkLabel ${line.id}`
      })
    }
    
  },

this.data 是需要渲染的數(shù)據(jù),放在文章末尾,具體數(shù)據(jù)按照接口實(shí)際返回的來寫

this.jsplumbSourceOptions 是jsplumb配置信息,新建一個文件編寫,具體如下:

export const jsplumbSetting = {
  grid: [10, 10],
  // 動態(tài)錨點(diǎn)、位置自適應(yīng)
  anchor: ['TopCenter', 'RightMiddle', 'BottomCenter', 'LeftMiddle'],
  Container: 'flow',
  // 連線的樣式 StateMachine、Flowchart,有四種默認(rèn)類型:Bezier(貝塞爾曲線),Straight(直線),F(xiàn)lowchart(流程圖),State machine(狀態(tài)機(jī))
  Connector: ['Flowchart', { cornerRadius: 5, alwaysRespectStubs: true, stub: 5 }],
  // 鼠標(biāo)不能拖動刪除線
  ConnectionsDetachable: false,
  // 刪除線的時候節(jié)點(diǎn)不刪除
  DeleteEndpointsOnDetach: false,
  // 連線的端點(diǎn)
  // Endpoint: ["Dot", {radius: 5}],
  Endpoint: [
    'Rectangle',
    {
      height: 10,
      width: 10
    }
  ],
  // 線端點(diǎn)的樣式
  EndpointStyle: {
    fill: 'rgba(255,255,255,0)',
    outlineWidth: 1
  },
  LogEnabled: false, // 是否打開jsPlumb的內(nèi)部日志記錄
  // 繪制線
  PaintStyle: {
    stroke: '#409eff',
    strokeWidth: 2
  },
  HoverPaintStyle: { stroke: '#409eff' },
  // 繪制箭頭
  Overlays: [
    [
      'Arrow',
      {
        width: 8,
        length: 8,
        location: 1
      }
    ]
  ],
  RenderMode: 'svg'
}
 
// jsplumb連接參數(shù)
export const jsplumbConnectOptions = {
  isSource: true,
  isTarget: true,
  // 動態(tài)錨點(diǎn)、提供了4個方向 Continuous、AutoDefault
  anchor: ['TopCenter', 'RightMiddle', 'BottomCenter', 'LeftMiddle']
}
 
export const jsplumbSourceOptions = {
  filter: '.node-anchor', // 觸發(fā)連線的區(qū)域
  /* "span"表示標(biāo)簽,".className"表示類,"#id"表示元素id*/
  filterExclude: false,
  anchor: ['TopCenter', 'RightMiddle', 'BottomCenter', 'LeftMiddle'],
  allowLoopback: false
}
 
export const jsplumbTargetOptions = {
  filter: '.node-anchor',
  /* "span"表示標(biāo)簽,".className"表示類,"#id"表示元素id*/
  filterExclude: false,
  anchor: ['TopCenter', 'RightMiddle', 'BottomCenter', 'LeftMiddle'],
  allowLoopback: false
}

 在父組件引入配置文件和方法

import { jsplumbSetting, jsplumbConnectOptions, jsplumbSourceOptions, jsplumbTargetOptions } from './config/commonConfig'

接下來在上面說的初始化方法文件里面配置鼠標(biāo)滾輪縮放插件的方法 this.initPanZoom():

// 鼠標(biāo)滾動放大縮小
  initPanZoom() {
    const mainContainer = this.jsPlumb.getContainer()
    const mainContainerWrap = mainContainer.parentNode
    const pan = panzoom(mainContainer, {
      smoothScroll: false,
      bounds: true,
      // autocenter: true,
      zoomDoubleClickSpeed: 1,
      minZoom: 0.5,
      maxZoom: 2,
      // 設(shè)置滾動縮放的組合鍵,默認(rèn)不需要組合鍵
      beforeWheel: (e) => {
        // console.log(e)
        // let shouldIgnore = !e.ctrlKey
        // return shouldIgnore
      },
      beforeMouseDown: function(e) {
        // allow mouse-down panning only if altKey is down. Otherwise - ignore
        var shouldIgnore = e.ctrlKey
        return shouldIgnore
      }
    })
    this.jsPlumb.mainContainerWrap = mainContainerWrap
    this.jsPlumb.pan = pan
    // 縮放時設(shè)置jsPlumb的縮放比率
    pan.on('zoom', e => {
      const { scale } = e.getTransform()
      this.jsPlumb.setZoom(scale)
    })
    pan.on('panend', (e) => {
 
    })
 
    // 平移時設(shè)置鼠標(biāo)樣式
    mainContainerWrap.style.cursor = 'grab'
    mainContainerWrap.addEventListener('mousedown', function wrapMousedown() {
      this.style.cursor = 'grabbing'
      mainContainerWrap.addEventListener('mouseout', function wrapMouseout() {
        this.style.cursor = 'grab'
      })
    })
    mainContainerWrap.addEventListener('mouseup', function wrapMouseup() {
      this.style.cursor = 'grab'
    })
  },

大功告成,data的數(shù)據(jù)放在這里,測試使用:

 {
  "FlowJson": {
      "nodeList": [
          {
              "type": "start",
              "nodeName": "已新建",
              "id": "start-HiXWf8wsAcrWXjAAXVWc6AQk00000001",
              "node_code": "已新建",
              "trigger_event": "",
              "branch_flow": "",
              "icon": "play-circle",
              "x": 175,
              "y": 60,
              "width": 50,
              "height": 50
          },
          {
              "type": "freedom",
              "nodeName": "待審批",
              "id": "freedom-YakFJzZ5VSp3Gec6ZULD2JDK00000004",
              "node_code": "待審批",
              "trigger_event": "",
              "branch_flow": "",
              "icon": "sync",
              "x": 330,
              "y": 160,
              "width": 50,
              "height": 120
          },
          {
              "type": "end",
              "nodeName": "已通過",
              "id": "end-JjRvtD5J2GIJKCn8MF7IYwxh00000999",
              "node_code": "已通過",
              "trigger_event": "",
              "branch_flow": "",
              "icon": "stop",
              "x": 330,
              "y": 360,
              "width": 50,
              "height": 50
          },
          {
              "type": "end",
              "nodeName": "審批拒絕",
              "id": "end-J1DMScH5YjSKyk0HeNkbt62F00010001",
              "node_code": "審批拒絕",
              "trigger_event": "",
              "branch_flow": "",
              "icon": "stop",
              "x": 500,
              "y": 350,
              "width": 50,
              "height": 50
          }
      ],
      "linkList": [
          {
              "type": "link",
              "id": "link-BpI6ZuX1bJywz5SEi3R5QaWoi7g3QiSr",
              "sourceId": "start-HiXWf8wsAcrWXjAAXVWc6AQk00000001",
              "targetId": "freedom-YakFJzZ5VSp3Gec6ZULD2JDK00000004",
              "label": "LINE000000",
              "role": [],
              "organize": [],
              "audit_role": [],
              "audit_organize": [],
              "audit_organize_same": "0",
              "audit_dealer_same": "0",
              "audit_dealers": [],
              "notice": "0",
              "plug": "",
              "pass_option": "pass",
              "row_par_json": "",
              "judge_fields": "",
              "auth_at": "",
              "auth_user": "",
              "auth_stat": "",
              "auth_mark": "",
              "cls": {
                  "linkType": "Flowchart",
                  "linkColor": "#008000",
                  "linkThickness": 4
              }
          },
          {
              "type": "link",
              "id": "link-5xJWzGlkIpUCsjmpfgesJxAOMHwkPlno",
              "sourceId": "freedom-YakFJzZ5VSp3Gec6ZULD2JDK00000004",
              "targetId": "end-J1DMScH5YjSKyk0HeNkbt62F00010001",
              "label": "LINE000001",
              "role": [],
              "organize": [],
              "audit_role": [
                  "PROJECT_SUPPORT_PLAN_CODE"
              ],
              "audit_organize": [],
              "audit_organize_same": "0",
              "audit_dealer_same": "0",
              "audit_dealers": [],
              "notice": "0",
              "plug": "",
              "pass_option": "reject",
              "row_par_json": "",
              "judge_fields": "",
              "auth_at": "",
              "auth_user": "",
              "auth_stat": "",
              "auth_mark": "",
              "cls": {
                  "linkType": "Flowchart",
                  "linkColor": "#808080",
                  "linkThickness": 1
              }
          },
          {
              "type": "link",
              "id": "link-g05V3usXa86wAtpcMkvGzybdBlpasMjU",
              "sourceId": "freedom-YakFJzZ5VSp3Gec6ZULD2JDK00000004",
              "targetId": "end-JjRvtD5J2GIJKCn8MF7IYwxh00000999",
              "label": "LINE000002",
              "role": [],
              "organize": [],
              "audit_role": [
                  "PROJECT_SUPPORT_PLAN_CODE"
              ],
              "audit_organize": [],
              "audit_organize_same": "0",
              "audit_dealer_same": "0",
              "audit_dealers": [],
              "notice": "0",
              "plug": "",
              "pass_option": "approve",
              "row_par_json": "",
              "judge_fields": "",
              "auth_at": "",
              "auth_user": "",
              "auth_stat": "",
              "auth_mark": "",
              "cls": {
                  "linkType": "Flowchart",
                  "linkColor": "#808080",
                  "linkThickness": 1
              }
          }
      ]
  }
  
}

關(guān)于“vue+jsplumb實(shí)現(xiàn)工作流程圖的方法”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識,可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點(diǎn)。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI