可拖拽流程图组件开发

效果

在这里插入图片描述

说在前面

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

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

组件设计

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

参数

整体参数
参数描述
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参数版本&…

12-数据结构-排序

排序算法 一.比较类排序 1.交换排序 (1).冒泡 #include <stdio.h>void PrintContext(char sort[], int size) {for(int i0;i<size;i){printf("%d ", sort[i]);}printf("\n"); }//冒泡排序 //Reverse 0:正向排序 //Reverse 1:反向排序 void Order…

【数据库原理】(2)数据库管理系统(DBMS)介绍

数据库管理系统&#xff08;Database Management System, DBMS&#xff09;是一种特殊的软件&#xff0c;它充当用户和电脑操作系统之间的中间层。它的主要任务是帮助用户轻松地创建、使用和维护数据库&#xff0c;同时确保数据的安全、完整性&#xff0c;以及支持多用户同时访…

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(上)

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

7.优化前后端交互的秘诀

注释规约 【强制】类、类属性、类方法的注释必须使用 Javadoc 规范&#xff0c;使用/*内容/格式&#xff0c;不得使用 // xxx 方式。 说明&#xff1a;在 IDE 编辑窗口中&#xff0c;Javadoc 方式会提示相关注释&#xff0c;生成 Javadoc 可以正确输出相应注释&#xff1b;在 I…

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 原理 四、总结 前…

Impala中kudu基础理论详解(超详细)

文章目录 前言一、概述1. Impala简介2. kudu简介3. kudu架构(1) Master节点(2) Tablet服务器&#xff08;TServer&#xff09;(3) 表&#xff08;Table&#xff09;(4) 数据模型 4. kudu特性与优势(1) 快速随机访问(2) 列式存储与压缩(3) 数据一致性保证 5. kudu应用场景6. Kud…

鸟瞰uml(中)

21.依赖和关联之间的区别 依赖&#xff08;Dependency&#xff09;&#xff1a; 依赖关系是一种较弱的关系&#xff0c;它表示一个类在某些条件下使用了另一个类。一个类的变化可以影响到依赖它的类&#xff0c;但不必须反过来&#xff0c;即依赖关系是一个单向的。依赖关系通…

【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;可…

golang的大杀器协程goroutine

在Golang中&#xff0c;协程&#xff08;Goroutine&#xff09;是轻量级的执行单元&#xff0c;用于实现并发编程。它是Golang语言的重要组成部分&#xff0c;提供了简洁、高效的方式来处理并发任务。 特点&#xff1a; 1&#xff09;轻量级&#xff1a;Go语言的协程是轻量级…

Golang leetcode206 翻转链表 迭代 递归 双指针

文章目录 翻转链表 leetcode206题解第一版 直接建立新的链表双指针迭代递归法 翻转链表 leetcode206 至此走到这里&#xff0c;我们对于链表的结构应当已经比较熟悉&#xff0c;下面的就是敢于操作 题解第一版 直接建立新的链表 时间复杂度太高 // 初始方法&#xff0c;建立…

简单搭建 SSM 项目实现登录

目录 项目简介项目层级介绍&#xff0c;导入依赖添加配置业务逻辑实现前端 JSP 页面实现 项目简介 SSM&#xff08;Spring、Spring MVC、MyBatis&#xff09;框架&#xff0c;是一种 Java Web 应用程序开发框架的集合&#xff0c;可以帮助开发者快速搭建 Java Web 应用程序。想…

React16源码: Suspense与lazy源码实现

Suspense 与 lazy 1 &#xff09;概述 Suspense 是在 react16.6 提供的新 feature用于加载的缓冲的内置组件 2 &#xff09;示例程序 lazy.js // lazy.js import React from react export default () > <p>Lazy Comp</p>主程序 import React, { Suspense, …