可拖拽流程图组件开发

效果

在这里插入图片描述

说在前面

流程图在技术领域是一种常见的可视化工具,用于展示系统、应用或业务流程的各个步骤以及它们之间的关系。它们可以帮助开发人员和项目团队更好地理解和规划复杂的流程,从而提高工作效率和准确性。但是,传统的静态流程图有时无法满足用户的需求,因此实现可拖拽的流程图组件成为了一个重要的需求。

实现可拖拽的流程图组件的目的和意义是为了提供一种交互性更强、用户体验更好的流程图展示方式。通过该组件,用户可以轻松地拖拽节点进行重新排序,自由调整流程图的结构和布局,从而更好地满足项目需求并提高工作效率。

组件设计

首先需要设计好配置参数,然后就是讲配置参数融入到样式的设计处理上,最后是实现组件拖动并实时展示效果。

参数

整体参数
参数描述
title标题(String)
dragAble是否可拖拽(Boolean)
width图标最小宽度(number)
radius是否圆角(Boolean)
data流程项(Array)
data流程项参数

结构如下

[{icon:require('@/assets/logo.png'),//图标text:'准备'//文字},{icon:require('@/assets/1.png'),text:'开始'},
]

功能

拖拽事件监听

通过监听鼠标的按下、移动和抬起等事件,实现节点的拖拽功能。

if (this.chartData.dragAble) {document.getElementById("flow-chart").addEventListener("mouseup", this.handleMouseup);document.getElementById("flow-chart").addEventListener("mouseover", this.handleMouseover);document.getElementById("flow-chart").addEventListener("touchend", this.handleMouseup);document.getElementById("flow-chart").addEventListener("touchmove", this.handleMouseover);
}
阻止默认事件

为了确保拖拽功能正常运作,需要在拖拽过程中阻止浏览器默认的拖拽行为。

//阻止默认事件
preventEvent() {document.getElementById("flow-chart").ondragstart = function () {return false;};document.getElementById("flow-chart").onselectstart = function () {return false;};
},
初始化样式和数据

在组件加载时,需要初始化节点的样式和位置,以及计算每个节点的宽度和每行显示的数量。

//初始化样式变量
initStyle() {let chartContent = this.$refs.chartContent;let width = chartContent.offsetWidth - 40;let itemWidth = Math.max(20, Math.floor(width / 7));if (this.chartData.width) {itemWidth = this.chartData.width;}this.itemWidth = itemWidth;this.itemNum = Math.floor(width / (itemWidth + itemWidth / 5));
},
//初始化数据
initData() {let data = this.vChartDataList;let res = [],flag = true,temp = [];for (let i = 1; i <= data.length; i++) {data[i - 1].id = "item" + "-" + res.length + "-" + (i - 1);if (flag) temp.push(data[i - 1]);else temp.unshift(data[i - 1]);if (i % this.itemNum == 0 || i == data.length) {res.push([...temp]);temp = [];flag = !flag;}}this.chartDataList = res;
},
//重组class
getClass(res, str) {if (this.chartData[str]) res += " " + str;return res;
},
//重组行样式
getColumnStyle(index) {let res = {};if (index < this.chartDataList.lenth - 1 || index % 2 == 0)return this.styleConcat(res);res["margin-left"] = "auto";res["margin-right"] = -this.itemWidth / 5 + "px;";return this.styleConcat(res);
},
//重组每个item的样式
getItemStyle(item = "") {let res = {};if (item != "") {if (item.opacity) {res.opacity = item.opacity;}return this.styleConcat(res);}res.width = this.itemWidth + "px;";res["margin-right"] = this.itemWidth / 5 + "px;";return this.styleConcat(res);
},
//重组每个item的icon的样式
getIconStyle(str) {let res = {};res.width = this.itemWidth - 5 + "px;";res.height = this.itemWidth - 5 + "px";if (str == "text") {res["line-height"] = this.itemWidth - 5 + "px";res["font-size"] = "large";res["border"] = "1px solid blue";res["background-color"] = "skyblue";}return this.styleConcat(res);
},
//获取连接线样式
getLineStyle(index, index1, flag) {if (index1 == this.chartDataList.length - 1 &&index == this.chartDataList[index1].length - 1)return "";let res = {};res["border-top"] = "1px solid black";res.width = this.itemWidth / 3 + "px";if (flag == "right")res["margin-right"] = -this.itemWidth / 3 + "px;";else {res["margin-left"] = -this.itemWidth / 3 + "px;";res["border-left"] = "1px solid black";}res["margin-top"] = this.itemWidth / 2 + "px;";if (index == this.chartDataList[0].length - 1 &&index1 < this.chartDataList.length - 1) {if (index1 % 2 == 0) {res["border-right"] = "1px solid black";}}if (index1 % 2 == 1) {if (index == this.chartDataList[index1].length - 1) return "";}return this.styleConcat(res);
},
//json变量转换为style字符串
styleConcat(obj) {let res = "";for (let k in obj) {res += k + ":" + obj[k] + ";";}return res;
},
处理鼠标抬起事件

当鼠标抬起时,将拖拽的节点插入到新的位置,并更新节点的样式和位置。

//鼠标抬起时
handleMouseup(event) {const chartContent = document.getElementById("chartContent");if (this.vChartDataList[this.oldInd])this.vChartDataList[this.oldInd].opacity = 1;chartContent.style.border = "none";this.operateDom = null;this.operateDomNum = null;this.oldInd = null;
},
处理鼠标移动事件

在拖拽过程中,根据鼠标的位置计算节点的新样式和位置,实现拖拽时的效果。

handleMouseover(event) {if (this.vChartDataList.length < this.chartData.data.length) {this.vChartDataList.unshift({ ...this.chartData.data[0] });}if (this.operateDom != null) {const w = this.operateDom.offsetWidth,h = this.operateDom.offsetHeight;let x = event.pageX,y = event.pageY;this.operateDom.style.position = "fixed";this.operateDom.style.opacity = "0.5";this.operateDom.style.left = x - w / 2 - window.scrollX + "px";this.operateDom.style.top = y - h / 2 - window.scrollY + "px";let { tx, ty } = this.getItemCoords(x, y);let oldInd = this.oldInd;if (oldInd >= 0) {this.vChartDataList.splice(oldInd, 1);this.initData();}let nty =parseInt(ty) % 2 == 0? parseInt(tx): this.itemNum - parseInt(tx);nty = Math.min(nty, this.itemNum);nty = Math.max(nty, 0);oldInd = parseInt(ty) * this.itemNum + nty;oldInd = Math.min(this.chartData.data.length - 1, oldInd);oldInd = Math.max(0, oldInd);this.oldInd = oldInd;if (oldInd < 0) return;this.vChartDataList.splice(oldInd, 0, { ...this.selectedItem });this.initData();}
},
//获取当前移动到的坐标
getItemCoords(x, y) {let d = document.getElementById("chartContent");let left = d.offsetLeft;let top = d.offsetTop;(x = x - left), (y = y - top);let itemNum = this.itemNum;let w = d.offsetWidth;let h = d.offsetHeight;let moveDiv = document.getElementById("moveDiv");let th = moveDiv.offsetHeight;w = Math.ceil(w / itemNum);(x = Math.floor(x / w)), (y = Math.floor(y / th));return { tx: x, ty: y };
},

源码

Gitee地址:https://gitee.com/zheng_yongtao/jyeontu-component-warehouse

预览地址

组件文档:http://jyeontu.xyz/jvuewheel/#/flowChartView

公众号

关注公众号『前端也能这么有趣』,获取更多有趣内容。

说在后面

🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。

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

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

相关文章

《深入理解C++11:C++11新特性解析与应用》笔记七

第七章 为改变思考方式而改变 7.1 指针空值--nullptr 7.1.1 指针空值&#xff1a;从0到NULL&#xff0c;再到nullptr 传统C头文件里NULL是一个宏定义&#xff1a; 在函数重载同时出现int和char *参数版本的函数时&#xff0c;使用NULL作为参数调用函数会调用int参数版本&…

elasticsearch系列九:异地容灾-CCR跨集群复制

概述 起初只在部分业务中采用es存储数据&#xff0c;在主中心搭建了个集群&#xff0c;随着es在我们系统中的地位越来越重要&#xff0c;数据也越来越多&#xff0c;针对它的安全性问题也越发重要&#xff0c;那如何对es做异地容灾呢&#xff1f; 今天咱们就一起看下官方提供的…

【电商项目实战】商品详情显示与Redis存储购物车信息

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《电商项目实战》。&#x1f3af;&#x1f3af; &am…

合伙企业有哪些分类

合伙企业分为&#xff1a;普通合伙企业和有限合伙企业。其中&#xff0c;普通合伙企业又包含特殊的普通合伙企业。 1、普通合伙企业由2人以上普通合伙人(没有上限规定)组成。 普通合伙企业中&#xff0c;合伙人对合伙企业债务承担无限连带责任。 特殊的普通合伙企业中&#xf…

软件测试/测试开发丨Python 封装 学习笔记

封装的概念 封装&#xff08;Encapsulation&#xff09; 隐藏&#xff1a;属性和实现细节&#xff0c;不允许外部直接访问暴露&#xff1a;公开方法&#xff0c;实现对内部信息的操作和访问 封装的作用 限制安全的访问和操作&#xff0c;提高数据安全性可进行数据检查&#x…

『番外篇九』SwiftUI 实战:打造一款“五脏俱全”的网络图片显示 App(上)

概览 俗话说得好:“读书破万卷,下笔如有神”。不过如果把这句话放到编程的学习上可就不那么贴切了。 要想熟练掌握一门编程语言,光看书是绝对不够的。我们还需尽可能的多撸码、早撸码,撸到无路可退、海枯石烂才有可能一窥门径。 在本篇和续篇博文中,我们将和小伙伴们一起…

c++_09_继承

1 继承 C的继承是弱继承 继承的语法&#xff1a; class 子类 : 继承方式1 基类1, 继承方式2 基类2, ... { ... }; 继承方式&#xff1a; 共有继承 public 保护继承 protected 私有继承 private 2 继承的基本属性&#xff08;3种继承方式均有&#xff09; 继承所…

Javaweb之JDBC的详细解析

2. JDBC介绍(了解) 2.1 介绍 通过Mybatis的快速入门&#xff0c;我们明白了&#xff0c;通过Mybatis可以很方便的进行数据库的访问操作。但是大家要明白&#xff0c;其实java语言操作数据库呢&#xff0c;只能通过一种方式&#xff1a;使用sun公司提供的 JDBC 规范。 Mybatis…

从 MySQL 的事务 到 锁机制 再到 MVCC

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、事务 1.1 含义 1.2 ACID 二、锁机制 2.1 锁分类 2.2 隔离级别 三、MVCC 3.1 介绍 3.2 隔离级别 3.3 原理 四、总结 前…

【2023年度总结】 何其有幸 年岁并进 一元复始 万象更新

&#x1f319; 新年将至&#xff0c;万物更新&#xff0c;旧疾当愈。 今年极度焦虑。发生太多事情&#xff0c;做出很多改变。 自律。早起、拍照、运动、读书、学习、认识了很多厉害的朋友&#xff0c;尝试影响周围的人。这是我生活正向能量的来源。 学习。今年依然是把大量…

C#上位机与欧姆龙PLC的通信06---- HostLink协议(FINS版)

1、介绍 对于上位机开发来说&#xff0c;欧姆龙PLC支持的主要的协议有Hostlink协议&#xff0c;FinsTcp/Udp协议&#xff0c;EtherNetIP协议&#xff0c;本项目使用Hostlink协议。 Hostlink协议是欧姆龙PLC与上位机链接的公开协议。上位机通过发送Hostlink命令&#xff0c;可…

python实现Ethernet/IP协议的客户端(二)

Ethernet/IP是一种工业自动化领域中常用的网络通信协议&#xff0c;它是基于标准以太网技术的应用层协议。作为工业领域的通信协议之一&#xff0c;Ethernet/IP 提供了一种在工业自动化设备之间实现通信和数据交换的标准化方法。python要实现Ethernet/IP的客户端&#xff0c;可…

【Linux】socket基础API

目录 1. 创建socket&#xff08;TCP/UDP&#xff0c;客户端服务器&#xff09; 1.1 第一个参数——domain 1.2 第二个参数——type 1.3 第三个参数——protocol 2. 绑定socket地址&#xff08;TCP/UDP&#xff0c;服务器&#xff09; 2.1 字节序及转换函数 2.2 IP地址及…

消融实验(ablation study)——全网最全解读

消融实验&#xff08;ablation study&#xff09; 是什么优势与劣势案例总结 是什么 消融实验是一种科学研究方法&#xff0c;用于确定一个条件或参数对结果的影响程度。当研究者提出了一个新的方案或方法时&#xff0c;消融实验通过逐一控制一个条件或参数&#xff0c;来观察…

6个火爆全网的AI开源项目,用上月10万+

标题月10万可能说的有点夸张和含糊&#xff0c;10万具体指的是你可以利用这些开源项目实现&#xff1a; 访问量10万 收入10万 用户10万 …… 开源项目只是免费的工具&#xff0c;具体怎么实现还需要你根据自己需求去深入运营。这里只是给你推荐一些比较热门的开源项目&…

基于QT开发的温室气体数据记录软件

1、概述 温室气体分析仪数据记录软件用于实现温室气体分析仪数据的获取与存储&#xff0c;阀箱数据的获取与存储、冷阱数据的获取与存储、采样单元数据的获取与存储、阀箱和采样单元的远程操作以及系统功能的管理。其主操作界面如下&#xff1a; 上述软件界面分为2各区域&…

用html,js和layui写一个简单的点击打怪小游戏

介绍&#xff1a; 一个简单的打怪小游戏&#xff0c;点击开始游戏后&#xff0c;出现攻击按钮&#xff0c;击败怪物后可以选择继续下一关和结束游戏。 继续下一个怪兽的血量会增加5点&#xff0c;攻击按钮会随机变色。 效果图&#xff1a; html代码&#xff1a; <!DOCTYPE…

QDialog

属性方法 样式表 background-color: qlineargradient(spread:reflect, x1:0.999896, y1:0.494136, x2:1, y2:1, stop:0 rgba(0, 0, 0, 255), stop:1 rgba(255, 255, 255, 255));border: 1px groove rgb(232, 232, 232);border-radius: 20px; QDialog 的常用方法&#xff1a; e…

前端 js 基础(1)

js 结果输出 &#xff08;点击按钮修改文字 &#xff09; <!DOCTYPE html> <html> <head></head><body><h2>Head 中的 JavaScript</h2><p id"demo">一个段落。</p><button type"button" onclic…

基于PHP的校园代购商城系统

有需要请加文章底部Q哦 可远程调试 基于PHP的校园代购商城系统 一 介绍 此校园代购商城系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统角色分为用户和管理员。(附带参考设计文档) 技术栈&#xff1a;phpmysqlbootstrapphpstudyvscode 二 功能 …