如何自己实现一个丝滑的流程图绘制工具(七)bpmn-js 批量删除、复制节点

背景

希望实现批量删除和复制节点,因为bpmn-js是canvas画的,所以不能像平时页面上的复制一样直接选择范围,会变成移动画布。

思路是:

绘制一个选择的效果框,这样才可以看出来选的节点有哪些。
在这里插入图片描述
上面的选中范围框效果也是用canvas画出来的
因为bpmn-js对鼠标直接选取范围进行了拦截。所以我加了一个辅助按键进行选择

一、 以下是绘制选择框范围的代码:
     * @param {MouseEvent} e
     */
    onMousedown(e) {
      this.removeActiveClass()
      this.batchSelectedList = []   
      if (!e.metaKey && !e.altKey && !e.ctrlKey) {
        return
      }
      e.target.addEventListener('mouseup', this.onMouseup)

      if (!this.rectSelect) {
        const rect = this.$refs.canvas.getBoundingClientRect()
        this.startX = e.clientX - rect.left
        this.startY = e.clientY - rect.top

        const g = this.$el.getElementsByTagName('svg')[1]
        this.rectSelect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
        this.rectSelect.setAttribute('fill', 'rgba(93,148,231,0.2)')
        this.rectSelect.setAttribute('stroke-width', '2px')
        this.rectSelect.setAttribute('stroke', 'rgba(93,148,231,0.2)')
        g.append(this.rectSelect)
      }
    },
    /**
     * @param {MouseEvent} e
     */
    onMousemove(e) {
      if (!this.enableBatchSelect) return
      this.currentMouseEvent = e

      if (!this.rectSelect) {
        return
      }
      e.stopPropagation()
      const canvasRect = this.$refs.canvas.getBoundingClientRect()
      const w = e.clientX - this.startX - canvasRect.left
      const h = e.clientY - this.startY - canvasRect.top
      const x = this.startX
      const y = this.startY
      this.rectSelect.setAttribute('x', w < 0 ? e.clientX - canvasRect.left : this.startX)
      this.rectSelect.setAttribute('y', h < 0 ? e.clientY - canvasRect.top : this.startY)
      this.rectSelect.setAttribute('width', `${Math.abs(w)}`)
      this.rectSelect.setAttribute('height', `${Math.abs(h)}`)

      const elementRegistry = this.bpmnModeler.get('elementRegistry')
      const canvas = this.bpmnModeler.get('canvas')
      const box = canvas.viewbox()

      const elementList = elementRegistry.getAll()
      const nodeList = elementList.filter(f => f.type === 'bpmn:Task')

      this.connectLineList = elementList.filter(f => f.type === 'bpmn:SequenceFlow')
      const boxX = box.x
      const boxY = box.y
      this.batchSelectedList = nodeList.filter(item => {
        const x1 = -(boxX - item.x) * box.scale
        const y1 = -(boxY - item.y) * box.scale

        const pointers = [
          { x: x, y: y },
          { x: x + w, y: y },
          { x: x + w, y: y + h },
          { x: x, y: y + h }
        ]
        return inRect(x1, y1, pointers)
      })
    },
    onMouseup(e) {
      if (this.rectSelect) {
        this.rectSelect.remove()
      }
      this.rectSelect = null
      e.target.removeEventListener('mouseup', this.onMouseup)
      const elementRegistry = this.bpmnModeler.get('elementRegistry')
      this.batchSelectedList.forEach(item => {
        const id = item.id
        const el = elementRegistry._elements[id]?.gfx
        if (el) {
          el.classList.add('batch-selected')
          this.activeIdList.push(id)
        }
      })
    },
    removeActiveClass() {
      const elementRegistry = this.bpmnModeler.get('elementRegistry')
      this.activeIdList.forEach(id => {
        const el = elementRegistry._elements[id]?.gfx
        if (el) {
          el.classList.remove('batch-selected')
        }
      })
      this.activeIdList = []
    }
<style>
.djs-element.batch-selected .djs-outline {
  stroke: rgb(54, 147, 255) !important;
  visibility: visible !important;
}
</style>
二、然后是把选中的数据放入剪贴板
 async onCopy(isShowMessage = true) {
      if (this.copyData.length === 0) return
      try {
        await navigator.clipboard.writeText(JSON.stringify(copyData))
        isShowMessage && this.$message.success(`已复制${copyData.length}个节点`)
      } catch (e) {
        this.$message.error('写入剪切板失败')
        console.error(e)
      }
    },
三、粘贴的操作
    /**
     * 粘贴
     * @param {KeyboardEvent} e
     */
    async onPaste(e) {
      const text = await navigator.clipboard.readText()
      try {
        const copyData = JSON.parse(text)
        const canvas = this.bpmnModeler.get('canvas')
        const box = canvas.viewbox()
        const elementFactory = this.bpmnModeler.get('elementFactory')
        const elementRegistry = this.bpmnModeler.get('elementRegistry')
        const parent = elementRegistry.find(el => el.type === 'bpmn:Process')
        const modeling = this.bpmnModeler.get('modeling')

        if (!copyData.nodes.length) return

        const rect = this.$el.getBoundingClientRect()
        const mouseX = this.currentMouseEvent.clientX - rect.x
        const mouseY = this.currentMouseEvent.clientY - rect.y

        // 计算第0个元素和当前鼠标所在位置的差值
        const first = copyData.nodes[0]
        const diffX = first.x - mouseX
        const diffY = first.y - mouseY

        copyData.nodes.forEach(item => {
          const x = item.x + box.x - diffX
          const y = item.y + box.y - diffY

          const shape = elementFactory.createShape({
            type: 'bpmn:Task',
            x: x,
            y: y
          })
          modeling.createShape(shape, { x: x, y: y }, parent)
          shape.data = item.data
          if (item.data.name) {
            this.createLabel(shape, item.data.name)
          }
          this.pateShapeMap[item.id] = shape
        })

        copyData.lines.forEach(line => {
          const startShape = this.pateShapeMap[line.sourceId]
          const targetShape = this.pateShapeMap[line.targetId]
          if (startShape && targetShape) {
            const lines = modeling.connect(startShape, targetShape)
            lines.data = line.data
            if (lines.data.name) {
              this.createLabel(lines, lines.data.name)
            }
            this.pateLineMap[lines.id] = lines
          }
        })
       } catch (e) {
        console.error(e)
      }
    },

在这里插入图片描述

以上就是批量复制的步骤

批量删除
在这里插入图片描述

  batchDelete() {
      const bpmnModeling = this.bpmnModeler.get('modeling')
      this.activeIdList.forEach(nodeId => {
        const element = this.bpmnModeler.get('elementRegistry').get(nodeId)
        bpmnModeling.removeElements([element])
      })  
   },
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

那就可爱多一点点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值