首先:在vue项目中安装jsplumb

执行 npm install jsplumb --save

绘制拓扑图

1、创建jsplumb实例

import {jsPlumb} from 'jsplumb'
export default {
  data() {
    return {
      jsplumbInstance: null, // jsplumb实例
    }
  },
  methods: {
    initData() {
      // 创建jsplumb实例
      this.jsplumbInstance = jsPlumb.getInstance();
    }
  }
}

2、jsplumb设置拓扑节点可以进行拖动。eleId表示当前拖动节点的id。默认情况下,节点可以拖动到画布区域外面,设置containment: 'parent',表示节点只能在自己的父元素内进行拖动,在我代码里,父元素代表画布区域。

makeTopoNodeDrag(eleId) {
  this.jsplumbInstance.draggable(eleId, {containment: 'parent'});
}

3、jsplumb给每一个节点添加端点。addEndpoint方法可以用来添加端点。

addEndPoints(eleId) {
  let style = {
    endpoint: 'Dot',
    // 将isSource和isTarget设置为true,可以在拖动时自动创建连接
    isSource: true,
    isTarget: true,
    connector: ['Flowchart', { cornerRadius: 10 }],
    // 默认情况下,maxConnections为1,表示一个端点只能有一条连线。设置为-1,不限制连线数量
    maxConnections: -1,
    // 端点样式
    paintStyle: {
      fill: 'rgba(54,54,54,0)',
    },

    // 鼠标悬浮端点样式
    hoverPaintStyle: {
      fill: 'rgba(64,54,54,0.5)',
    },

    // 拖拽连线样式
    connectorStyle: {
      stroke: 'rgba(54, 61, 63, 0.5)',
      strokeWidth: 3,
    },
    connectorHoverStyle: {
      stroke: 'rgba(54, 61, 63, 0.5)',
      strokeWidth: 5,
    },

    // 设置箭头
    connectorOverlays: [['Arrow', { width: 10, location: 1, id: 'arrow' }]],
    
  }
  this.jsplumbInstance.addEndpoint(
    {eleId, {anchors: 'TopCenter'}, style};
    {eleId, {anchors: 'BottomCenter'}, style};
    {eleId, {anchors: 'LeftMiddle'}, style};
    {eleId, {anchors: 'RightMiddle'}, style};
  )
}

4、基于上述步骤,节点元素已经放置在画布上,并且为节点添加了上下左右4个锚点,在锚点上进行拖拽连线:

在连线之前,有时会根据一些特定条件判断是否进行连接,不符合条件时,不能进行连接,可以使用beforeDrop事件,具体参数info,可以查看https://www.jianshu.com/p/c2c4f7fdffa1(别人的文章)

// 监听连线事件
monitorLineConnection() {
  this.jsPlumbInstance.bind('beforeDrop', info => {
    // 禁止在同一端点上连接
    if(info.sourceId === info.targetId) {
      return false; // return false连接不会建立,必须为false
    }
    
    // 可以在连接前再添加判断条件,例如一个节点上的同一个端点只能连接一次等等。。省略代码
    return true; // 连接自动建立
  })
}

5、第3步给每一个节点添加了端点,可以使用removeAllEndpoints方法删除节点上的端点。

// eleId表示当前节点的id
this.jsplumbInstance.removeAllEndpoints(eleId);

6、删除连线,deleteConnection方法删除连线

// 添加右键事件,当右键点击在连线上时,info就是当前的连线信息,deleteConnection方法可以删除当前连线
this.jsPlumbInstance.bind('contextmenu', info => {
  let lineInfo = info;
  this.jsPlumbInstance.deleteConnection(lineInfo);
})

以上步骤基本上就可以实现简单的拖拽连线功能。

 

通常情况下,可能需要根据现有的数据直接使用jsplumb将拓扑图绘制出来:this.jsPlumbInstance.connect()

export default {
  data() {
    return {
      topoData: [], // 拓扑数据
    }
  },
  methods: {
    initData() {
      this.jsPlumbInstance.ready(() => {
        this.topoData.forEach(item => {
          item.target.forEach(target => {
            this.jsPlumbInstance.connect(
              {
                source: item.id,
                target: target.id,
                anchors: target.anchors,
              }, style
            )
          })
        })
      })
    },
  },
  mounted() {
    this.initData();
  }
}

代码中的style表示连线的样式:

let style = {
  connector: ['Flowchart', { cornerRadius: 10 }],

  // 一般情况下再次拖动连线时,连接会断开,设置 detachable: false 可以阻止这种行为
  detachable: false,

  // 线段样式
  paintStyle: {
    stroke: '#000',
    strokeWidth: 3,
  },

  // 鼠标经过线段样式
  hoverPaintStyle: {
    stroke: '#000',
    strokeWidth: 5,
  },

  endpointStyle: { fill: 'none' }, // 端点样式

  // 设置箭头,Label等
  overlays: [
    [
      'Arrow',
      {
        width: 10,
        location: 1,
        id: 'arrow',
      },
    ],
  ],
}

拓扑数据的格式是:

{
  "data": [
    {
      "name": "第一个元素",
      "id": "1",
      "top": "112", // top,left表示元素在画布上位置
      "left": "298",
      "target": [ // target数组表示当前节点所连接的节点及连接锚点位置信息
        {
          "id": "2",
          "anchors": [
            "BottomCenter",
            "TopCenter"
          ]
        }
      ]
    },
    {
      "name": "第二个元素",
      "id": "2",
      "top": "259",
      "left": "298",
      "target": [
        {
          "id": "3",
          "anchors": [
            "LeftMiddle",
            "TopCenter"
          ]
        }
      ]
    },
    {
      "name": "第三个元素",
      "id": "3",
      "top": "442",
      "left": "298",
      "target": [
        {
          "id": "4",
          "anchors": [
            "BottomCenter",
            "TopCenter"
          ]
        }
      ]
    }
  ]
}