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

news2024/9/17 9:07:19

背景

希望实现批量删除和复制节点,因为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])
      })  
   },

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/948793.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CSS 实现平面圆点绕椭圆动画

前言 &#x1f44f;CSS实现平面圆点绕椭圆动画,速速来Get吧~ &#x1f947;文末分享源代码。记得点赞关注收藏&#xff01; 1.实现效果 2.实现原理 transform-style&#xff1a;CSS 属性 transform-style 设置元素的子元素是位于 3D 空间中还是平面中。如果选择平面&#xf…

Python GUI应用程序开发之wxPython使用详解

概要 wxPython是一个强大的跨平台GUI工具包&#xff0c;它使用Python编程语言开发&#xff0c;提供了丰富的控件功能。如果你是一名Python开发者&#xff0c;而且希望创建一个功能齐全的桌面应用程序&#xff0c;那么wxPython是一个值得考虑的选择。 什么是wxPython wxPython…

【论文阅读】你看不见我:对基于激光雷达的自动驾驶汽车驾驶框架的物理移除攻击

文章目录 AbstractIntroduction Abstract 自动驾驶汽车(AVs)越来越多地使用基于激光雷达的物体检测系统来感知道路上的其他车辆和行人。目前&#xff0c;针对基于激光雷达的自动驾驶架构的攻击主要集中在降低自动驾驶物体检测模型的置信度&#xff0c;以诱导障碍物误检测&…

宏观经济和风电预测误差分析(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【LeetCode-中等题】199. 二叉树的右视图

文章目录 题目方法一&#xff1a;层序遍历取每一层最后一个元素方法二&#xff1a;深度优先搜索 题目 方法一&#xff1a;层序遍历取每一层最后一个元素 // 方法一 &#xff1a;层序 集合(取每层子集合最后一个元素)// List<List<Integer>> Rlist new ArrayList…

运动耳机哪款最好、热门运动耳机推荐

对于当代年轻人而言&#xff0c;运动健身已成为最流行的压力释放方式。然而&#xff0c;我们不得不承认&#xff0c;运动本身确实是一项沉闷而艰苦的挑战&#xff0c;需要我们付出大量汗水和持之以恒的坚持。 在运动过程中&#xff0c;许多人会借助音乐的律动来激励自己不断向前…

「2024」预备研究生mem-数学进阶卷(二)8.27 比较易错的难题,存在特殊方法

一共12道题&#xff1a; 回忆&#xff1a; 不错&#xff1a; 男生 没有要求顺序 &#xff0c;就不用A55了

得帆CTO徐翔轩:AIGC X 低代码,解锁无限可能

AIGC与低代码 在Open AI的ChatGPT的带动下&#xff0c;2023年已经成为公认的AI之年&#xff0c;Chat-GPT、Notion AI、Copilot等杀手级AI应用更是让大家认知到了AIGC的能力与潜力。AIGC在创新、效率等方面带来的颠覆性&#xff0c;让越来越多的数字化从业者感受到冲击与期待&a…

Android OTA 相关工具(六) 使用 lpmake 打包生成 super.img

我在 《Android 动态分区详解(二) 核心模块和相关工具介绍》 介绍过 lpmake 工具&#xff0c;这款工具用于将多个分区镜像打包生成一个 Android 专用的动态分区镜像&#xff0c;一般称为 super.img。Android 编译时&#xff0c;系统会自动调用 lpmake 并传入相关参数来生成 sup…

傅里叶变换(FFT)笔记存档

参考博客&#xff1a;https://www.luogu.com.cn/blog/command-block/fft-xue-xi-bi-ji 目录&#xff1a; FFT引入复数相关知识单位根及其相关性质DFT过程&#xff08;难点&#xff09;DFT结论&#xff08;重要&#xff09;IDFT结论&#xff08;重要&#xff09;IDFT结论证明&…

c++ lambda

Lambda Lambda 表达式一般用于定义匿名函数&#xff0c;使得代码更加灵活简洁&#xff0c;优点&#xff1a; 声明式编程风格&#xff1a;就地匿名定义目标函数或函数对象&#xff0c;不需要额外写一个命名函数或者函数对象。以更直接的方式去写程序&#xff0c;好的可读性和可…

cinder的配额参数说明

概述 openstack默认为了防止用户随意使用存储空间&#xff0c;针对cinder做了限制。cinder的quota有一个专门的驱动去完成。当超过quota时&#xff0c;使用cinder将会失败。 cinder中quota的默认配置 quota_drivercinder.quota.DbQuotaDriver quota的驱动&#xf…

C#获取屏幕缩放比例

现在1920x1080以上分辨率的高分屏电脑渐渐普及了。我们会在Windows的显示设置里看到缩放比例的设置。在Windows桌面客户端的开发中&#xff0c;有时会想要精确计算窗口的面积或位置。然而在默认情况下&#xff0c;无论WinForms的Screen.Bounds.Width属性还是WPF中SystemParamet…

【git】合并分支代码失败fix conficts and then commit the result

目录 一、合并代码 二、打开冲突文件 三、手动处理冲突文件 四、将修改文件添加提交 前言&#xff1a;合并分支代码到主干报错fix conficts and then commit the result 一、合并代码 切换到要合并到的分支&#xff0c;例如主干 git merge 被合并分支的文件名 如果没有冲…

Redis.conf 配置文件详解

1、units 单位 配置大小单位&#xff0c;开头定义了一些基本的度量单位&#xff0c;只支持 bytes&#xff0c;不支持bit&#xff0c;并且对大小写 不敏感。 2、INCLUDES 包含 类似于 Spring 配置文件&#xff0c;可以通过 includes 包含&#xff0c;redis.conf 可以作为总文件…

机器学习:争取被遗忘的权利

随着越来越多的人意识到他们通过他们经常访问的无数应用程序和网站共享了多少个人信息&#xff0c;数据保护和隐私一直在不断讨论。看到您与朋友谈论的产品或您在 Google 上搜索的音乐会迅速作为广告出现在您的社交媒体提要中&#xff0c;这不再那么令人惊讶。这让很多人感到担…

响应式营销型H5建站平台系统源码 可视化后台+自助建站+搭建部署教程

分享一个响应式营销型H5建站平台系统源码&#xff0c;含700多套多行业模板&#xff0c;含完整代码包和详细的搭建部署教程。 自助建站是响应式营销型H5建站平台系统的特色功能之一&#xff0c;用户可以通过简单的操作&#xff0c;自主搭建网站。常规自助建站的步骤&#xff1a…

Flutter 苹果审核被拒2.1

1、拒绝原因 Guideline 2.1 - Performance - App Completeness We were unable to review your app as it crashed on launch. We have attached detailed crash logs to help troubleshoot this issue. Review device details: Device type: iPadOS version: iOS 16.6 Nex…

Invalid bound statement (not found) 报错

常规的问题都检查了&#xff0c;还是报错。 用mp代码生成器的目录结构如下&#xff1a; xml文件没有放在resources路径下 这样会导致xml文件不在target目录下&#xff0c;解决的方式是在pom.xml文件中加入&#xff1a; <build><resources><resource><…