可拖拽流程图组件开发

效果

在这里插入图片描述

说在前面

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

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

组件设计

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

参数

整体参数
参数描述
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.mfbz.cn/a/284136.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Vulnhub 靶场】【Looz: 1】【简单】【20210802】

1、环境介绍 靶场介绍&#xff1a;https://www.vulnhub.com/entry/looz-1,732/ 靶场下载&#xff1a;https://download.vulnhub.com/looz/Looz.zip 靶场难度&#xff1a;简单 发布日期&#xff1a;2021年08月02日 文件大小&#xff1a;2.1 GB 靶场作者&#xff1a;mhz_cyber &…

侯捷C++ 2.0 新特性

关键字 nullptr and std::nullptr_t auto 一致性初始化&#xff1a;Uniform Initialization 11之前&#xff0c;初始化方法包括&#xff1a;小括号、大括号、赋值号&#xff0c;这让人困惑。基于这个原因&#xff0c;给他来个统一&#xff0c;即&#xff0c;任何初始化都能够…

文献速递:人工智能医学影像分割---高效的MR引导CT网络训练,用于CT图像中前列腺分割

01 文献速递介绍 如今&#xff0c;根据国家癌症研究所的报告&#xff0c;美国约有9.9%的男性患有前列腺癌。1 此外&#xff0c;根据美国癌症协会的数据&#xff0c;预计2019年将有174,650个新病例被诊断出前列腺癌&#xff0c;与此同时大约有31,620名男性将死于前列腺癌。因此…

YOLOv8改进 | 检测头篇 | ASFF改进YOLOv8检测头(全网首发)

一、本文介绍 本文给大家带来的改进机制是利用ASFF改进YOLOv8的检测头形成新的检测头Detect_ASFF&#xff0c;其主要创新是引入了一种自适应的空间特征融合方式&#xff0c;有效地过滤掉冲突信息&#xff0c;从而增强了尺度不变性。经过我的实验验证&#xff0c;修改后的检测头…

计算机组成原理——中央处理器cpu21-40

18、某计算机的指令流水线由4个功能段组成&#xff0c;指令流经各功能段的时间&#xff08;忽略各功能段之间的缓存时间&#xff09;分别为90ns、80ns、70ns和60ns&#xff0c;则该计算机的CPU时钟周期至少是多少。A A、 90ns     B、 80ns C、 70ns     D、 60ns …

匿名短信发送网站搭建教程

​​​​​​​​​​​​​​详细教程链接 软件功能&#xff1a; 主要功能是可以去帮助一些用户发送匿名短信&#xff0c;不带你的真实号码&#xff0c;比如热恋中闹脾气的小情侣&#xff0c;短信的成本0.1&#xff0c;卖别人假设价格设置1元/条&#xff0c;利润就有80% 当…

Debezium日常分享系列之:Debezium 通知

Debezium日常分享系列之&#xff1a;Debezium 通知 一、概论二、Debezium通知格式三、Debezium 有关初始快照状态的通知四、Debezium 有关增量快照进度的通知五、启用 Debezium 通知六、访问 Debezium JMX 通知七、自定义通知渠道八、应用案例 一、概论 Debezium 通知提供了一…

MySql——1146 - Table‘mysql.proc‘doesn‘t exit是这个

项目场景&#xff1a; 做自己的小项目需要连接mysql数据库 问题描述 点击数据库时报错 1146 - Table’mysql.proc’doesn’t exit 原因分析&#xff1a; 误删原生的mysql数据库 解决方案&#xff1a; 重新安装装部署mysql就好了 注意不要轻易删除原生的东西

JMeter(十六)-JMeter断言

十六、JMeter断言 1.简介 断言组件用来对服务器的响应数据做验证&#xff0c;常用的断言是响应断言&#xff0c;其支持正则表达式。虽然我们的通过响应断言能够完成绝大多数的结果验证工作&#xff0c;但是JMeter还是为我们提供了适合多个场景的断言元件&#xff0c;辅助我们来…

[Vulnhub靶机] DriftingBlues: 1

[Vulnhub靶机] DriftingBlues: 1靶机渗透思路及方法&#xff08;个人分享&#xff09; 靶机下载地址&#xff1a; https://download.vulnhub.com/driftingblues/driftingblues.ova 靶机地址&#xff1a;192.168.67.20 攻击机地址&#xff1a;192.168.67.3 一、信息收集 1.使…

采用环形首尾互联互控的雪崩效应极好的Hash算法/杂凑函数RING-512设计原理详解

RING-512密码杂凑算法 黄金龙&#xff08;QQ1435271638&#xff09; 什么是Hash算法&#xff1f; Hash算法&#xff0c;又称为哈希算法、杂凑函数、散列函数、消息摘要算法。它可以将相当长&#xff08;一般不大于2^64Bit&#xff09;的输入数据经过计算生成固定长度的Hash值…

保护Word或Excel的几种方法,总有一种满足你的需求

你已经在Microsoft Word或Excel中创建了一个重要或机密文件,你希望将其保密或至少保持安全。也许你想确保只有你和某些人可以阅读或编辑它。也许你想限制某人可以对文件进行的修改类型。你甚至可以向读者保证这是最终版本。如果你知道在Word和Excel中使用哪些工具以及它们是如…

openGauss学习笔记-182 openGauss 数据库运维-升级-升级前准备与检查

文章目录 openGauss学习笔记-182 openGauss 数据库运维-升级-升级前准备与检查182.1 升级前准备与检查清单182.2 收集节点信息182.3 备份数据182.4 获取升级包182.5 健康检查182.5.1 前提条件182.5.2 操作步骤 182.6 检查数据库节点磁盘使用率182.7 检查数据库状态182.7.1 验证…

JavaWeb——前端之AjaxVue

6. 前后端交互 6.1 Ajax&#xff08;原生的&#xff09; 概念&#xff1a; Asynchronous JavaScript And XML&#xff08;异步的JavaScript和XML&#xff09; 作用&#xff1a; 数据交互&#xff1a;通过Ajax可以给服务器发送请求&#xff0c;并获取服务器响应的数据异步交…

用 print 太慢了!强烈推荐这款Python Debug工具~

作为程序员&#xff0c;我们都深知调试&#xff08;Debug&#xff09;在编程过程中的重要性。 然而&#xff0c;使用传统的"print"语句进行调试可能效率较低&#xff0c;今天&#xff0c;笔者将推荐一款独具一格的Python调试工具——Reloadium。 Reloadium为IDE添加…

Java技术栈 —— Redis的雪崩、穿透与击穿

Java技术栈 —— Redis的雪崩、穿透与击穿 〇、实验的先导条件&#xff08;NginxJmeter&#xff09;一、Redis缓存雪崩、缓存穿透、缓存击穿1.1 雪崩1.2 穿透1.3 击穿 二、Redis应用场景——高并发2.1 单机部署的高并发问题与解决&#xff08;JVM级别锁&#xff09;2.2 集群部署…

数据结构:基于数组的环形队列(循环队列)实现

1 前言 队列是一种先进先出的线性表&#xff0c;简称为FIFO。它只允许在队尾插入成员&#xff0c;在队头删除成员&#xff0c;就像现实生活中排队上车一样。 队列的实现可以通过链表或数组完成&#xff0c;一般来说都推荐使用链表来实现队列&#xff0c;使用数组实现队列时每次…

SpreadJS 集成使用案例

SpreadJS 集成案例 介绍&#xff1a; SpreadJS 基于 HTML5 标准&#xff0c;支持跨平台开发和集成&#xff0c;支持所有主流浏览器&#xff0c;无需预装任何插件或第三方组件&#xff0c;以原生的方式嵌入各类应用&#xff0c;可以与各类后端技术框架相结合。SpreadJS 以 纯前…

Java日期和时间(二)

新增的日期和时间 为什么要学习新增的日期和时间 1、代替Calendar LocalDate&#xff1a;年、月、日 LocalTime&#xff1a;时、分、秒 LocalDateTime&#xff1a;年、月、日、时、分、秒 ZoneId&#xff1a;时区 ZoneldDatetime&#xff1a;带时区的时间 2、代替Date Instan…

使用flutter开发一个简单的轮播图带指示器的组件

使用PageView开发一个带指示器的轮播图组件&#xff0c;当轮播图切换的时候&#xff0c;指示器也会跟着切换&#xff0c;切换到当前轮播图所在的索引时&#xff0c;指示器的背景色会变成蓝色&#xff0c;否则是灰色。使用了一个curIndex变量来记录当前激活的轮播图索引。并使用…