前端绘制流程节点数据
根据数据结构和节点的层级、子节点id,前端自己绘制节点位置和关联关系、指向、已完成节点等
<template><div><div>通过后端节点和层级,绘制出节点以及关联关系等</div><div class="container" ref="container"><div v-for="(item, index) in nodeList" :key="index" class="point":style="{ position: 'absolute', top: item.y + 'px', left: item.x + 'px', }">{{ item.name }}-{{ item.isDone }}</div></div></div>
</template><script>export default {data() {return {// 节点数据 level代表层级 lastIds代表与其关联的下一级节点的idnodeList: [{ name: '点1', id: 1, level: 1, lastIds: [2, 3] },{ name: '点2', id: 2, level: 2, lastIds: [4] },{ name: '点3', id: 3, level: 2, lastIds: [5, 10, 6] },{ name: '点4', id: 4, level: 3, lastIds: [7] },{ name: '点5', id: 5, level: 3, lastIds: [7] },{ name: '点10', id: 10, level: 3, lastIds: [9] },{ name: '点6', id: 6, level: 3, lastIds: [8] },{ name: '点7', id: 7, level: 4, lastIds: [9] },{ name: '点8', id: 8, level: 4, lastIds: [9] },{ name: '点9', id: 9, level: 5, lastIds: [] },],lineList: [], // 两两连接线关系的数据项pathsList: [], // 首位相连的完整链路num: 7, // 当前节点idsSet: [], //当前节点及已路过的节点集合};},mounted() {const container = document.getElementsByClassName('container')[0]// 计算定位节点const addCoordinates = (nodes, width) => {// 按 level 分组节点const groupedNodes = nodes.reduce((acc, node) => {if (!acc[node.level]) {acc[node.level] = [];}acc[node.level].push(node);return acc;}, {});// 处理每个 level 的节点Object.keys(groupedNodes).forEach(level => {const group = groupedNodes[level];const count = group.length;const spacing = width / (count + 1); // 间距group.forEach((node, index) => {node.x = spacing * (index + 1);node.y = node.level * 66;});});// 返回处理后的节点数组return nodes;};// 查找存在连接关系的项,并将信息存入 lineList 数组const findConnectedNodes = (nodes) => {const lineList = [];nodes.forEach(node => {node.lastIds.forEach(lastId => {// 根据 id 找到相应的节点const connectedNode = nodes.find(item => item.id === lastId);// 将节点信息存入 lineList 数组,确保起点是当前节点,终点是连接的节点if (connectedNode) {lineList.push({x1: node.x,y1: node.y,x2: connectedNode.x,y2: connectedNode.y,lineAB: [node.id, connectedNode.id]});}});});return lineList;};this.nodeList = addCoordinates(this.nodeList, 600);console.log('nodeList 节点定位', this.nodeList);// 查找存在连接关系的项this.lineList = findConnectedNodes(this.nodeList);console.log('lineList 两两关联', this.lineList);// 绘制线段const drawLine = (x1, y1, x2, y2, isDone) => {// console.log(x1, y1, x2, y2, isDone);const length = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);const angle = Math.atan2(y2 - y1, x2 - x1) * (180 / Math.PI);const line = document.createElement('div');line.className = 'line';line.style.width = `${length}px`;line.style.transform = `rotate(${angle}deg)`;line.style.transformOrigin = '0 0';line.style.position = 'absolute';line.style.top = `${y1}px`;line.style.left = `${x1}px`;line.style.backgroundColor = 'black';line.style.height = '2px';line.style.backgroundColor = isDone ? '#1fff' : 'black';// 创建箭头const arrow = document.createElement('div');arrow.className = 'arrow';arrow.style.position = 'absolute';arrow.style.width = '0';arrow.style.height = '0';arrow.style.borderLeft = '5px solid transparent';arrow.style.borderRight = '5px solid transparent';arrow.style.borderTop = '15px solid black';arrow.style.borderTopColor = isDone ? '#1fff' : 'black';// 计算箭头位置arrow.style.top = `${y2 - 10}px`;arrow.style.left = `${x2 - 6}px`;arrow.style.transform = `rotate(${angle + 270}deg)`;arrow.style.transformOrigin = 'center center';if (container) {container.appendChild(line);container.appendChild(arrow);}};// 找到完整链路const getPath = (arr) => {let pathsList = [];// 构建图的邻接表表示let graph = {};arr.forEach(({ lineAB: [start, end] }) => {if (!graph[start]) {graph[start] = [];}graph[start].push(end);});// 深度优先搜索函数function dfs(node, path) {path.push(node);if (!graph[node] || graph[node].length === 0) {pathsList.push([...path]);} else {for (let neighbor of graph[node]) {dfs(neighbor, path);}}path.pop();}// 找到所有的起点(即那些不作为任何其他点的终点的点)let allPoints = new Set(arr.flatMap(({ lineAB }) => lineAB));let endPoints = new Set(arr.map(({ lineAB: [, end] }) => end));let startPoints = [...allPoints].filter(point => !endPoints.has(point));// 从每个起点开始搜索完整路径startPoints.forEach(start => {dfs(start, []);});return pathsList}this.pathsList = getPath(this.lineList)console.log('pathsList完整链路', this.pathsList);let that = thisfunction updateNodeListWithDoneStatus(nodeList, pathsList, num) {let idsSet = new Set();// 找到所有包含 num 的路径,并提取 num 之前的节点pathsList.forEach(path => {let index = path.indexOf(num);if (index !== -1) {for (let i = 0; i <= index; i++) {idsSet.add(path[i]);}}});idsSet.forEach(val => {that.idsSet.push(val)});console.log('当前及链路上的节点', that.idsSet);// 更新 nodeList,添加 isDone 属性nodeList.forEach(node => {if (idsSet.has(node.id)) {node.isDone = true;} else {node.isDone = false;}});// 更新 lineList 添加 isDone 属性that.lineList.forEach(line => {// 如果 lineAB 中的任意一个节点在 idsSet 中,则标记为已完成// line.isDone = idsSet.has(line.lineAB[0]) && idsSet.has(line.lineAB[1]);line.isDone = line.lineAB.every(item => that.idsSet.includes(item))});return nodeList;}// 示例:查找当前几点之前的链路ids集合并更新nodeListlet updatedNodeList = updateNodeListWithDoneStatus(this.nodeList, this.pathsList, this.num);this.nodeList = updatedNodeList// 遍历绘制线段for (let index = 0; index < this.lineList.length; index++) {let element = this.lineList[index]setTimeout(() => {drawLine(element.x1, element.y1, element.x2, element.y2, element.isDone)}, 110);}this.$forceUpdate()console.log('最终节点数据', this.nodeList);console.log('最终两两连接线关系的数据', this.lineList);},};
</script><style lang="less" scoped>
.container {position: relative;width: 600px;height: 600px;border: 1px solid #000;
}.point {background-color: red;
}.line {background-color: black;height: 2px;position: absolute;pointer-events: none; // 防止影响鼠标事件
}
</style>相关文章:
前端绘制流程节点数据
根据数据结构和节点的层级、子节点id,前端自己绘制节点位置和关联关系、指向、已完成节点等 <template><div><div>通过后端节点和层级,绘制出节点以及关联关系等</div><div class"container" ref"container&…...
2024年顶级算法-黑翅鸢优化算法(BKA)-详细原理(附matlab代码)
黑翅鸢是一种上半身蓝灰色,下半身白色的小型鸟类。它们的显著特征包括迁徙和捕食行为。它们以小型哺乳动物、爬行动物、鸟类和昆虫为食,具有很强的悬停能力,能够取得非凡的狩猎成功。受其狩猎技能和迁徙习惯的启发,该算法作者建立…...
Linux 内核开发 28 内核模块文件ko文件介绍
Linux 内核开发 28 内核模块文件ko文件介绍 1. ELF格式简介 内核模块文件ko文件,格式为elf格式, ELF(Executable and Linkable Format)可执行链接格式,是一种用于存储可执行程序、目标代码、共享库和内核模块的标准文件…...
DDR5—新手入门学习(一)【1-5】
目录 1、DDR背景 (1)SDR SDRAM时代 : (2)DDR SDRAM的创新 : (3)DDR技术的演进 : (4)需求推动: 2、了解内存 (1&…...
力扣HOT100 - 138. 随机链表的复制
解题思路: class Solution {public Node copyRandomList(Node head) {if(headnull) return null;Node p head;//第一步,在每个原节点后面创建一个新节点//1->1->2->2->3->3while(p!null) {Node newNode new Node(p.val);newNode.next …...
深入分析 Android Activity (五)
深入分析 Android Activity (五) 1. Activity 的进程和线程模型 在 Android 中,Activity 默认在主线程(也称为 UI 线程)中运行。理解进程和线程模型对于开发响应迅速且无阻塞的应用程序至关重要。 1.1 主线程与 UI 操作 所有 UI 操作必须…...
Kubernetes 应用滚动更新
Kubernetes 应用版本号 在 Kubernetes 里,版本更新使用的不是 API 对象,而是两个命令:kubectl apply 和 kubectl rollout,当然它们也要搭配部署应用所需要的 Deployment、DaemonSet 等 YAML 文件。 在 Kubernetes 里应用都是以 …...
五分钟”手撕“图书管理系统
前言: 图书馆管理系统需要结合JavaSE的绝大部分知识,是一个很好的训练项目。 为了让大家更加方便的查阅与学习,我把代码放开头,供大家查询。 还有对代码的分析,我将以类为单位分开讲解。 目录 全部代码 Main类 Us…...
8个实用网站和软件,收藏起来一定不后悔~
整理了8个日常生活中经常能用得到的网站和软件,收藏起来一定不会后悔~ 1.ZLibrary zh.zlibrary-be.se/这个网站收录了超千万的书籍和文章资源,国内外的各种电子书资源都可以在这里搜索,98%以上都可以在网站内找到,并且支持免费下…...
电商内卷时代,视频号小店凭借一己之力“脱颖而出”
大家好,我是电商笨笨熊 今年618各大电商平台花样百出; 某宝更是直接取消了“预售”,从5月就开始进入618预热期; 不少玩家既开心又难过,市场如此内卷,618确实是个爆发期,但更多的需要不断压低…...
【论文笔记】| 定制化生成PuLID
PuLID: Pure and Lightning ID Customization via Contrastive Alignment ByteDance, arXiv:2404.16022v1 Theme: Customized generation 原文链接:https://arxiv.org/pdf/2404.16022 Main Work 提出了 Pure 和 Lightning ID 定制 (PuLID),这是一种用于…...
P1638 逛画展
题目描述 博览馆正在展出由世上最佳的 𝑚 位画家所画的图画。 游客在购买门票时必须说明两个数字,𝑎 和 𝑏,代表他要看展览中的第 𝑎 幅至第 𝑏 幅画(包含 𝑎,…...
Linux(centos)常用命令
Linux(Centos)常用命令使用说明文档 切换到/home目录下 使用cd命令切换目录,例如: cd /home列出/home目录下的所有文件 使用ls命令列出目录下的文件和子目录,例如: ls /home新建目录dir1 使用mkdir命…...
从入门到精通:掌握Scrapy框架的关键技巧
在当今信息爆炸的时代,获取并利用网络数据成为了许多行业的核心竞争力之一。而作为一名数据分析师、网络研究者或者是信息工作者,要想获取网络上的大量数据,离不开网络爬虫工具的帮助。而Scrapy框架作为Python语言中最为强大的网络爬虫框架之…...
Vue3按顺序调用新增和查询接口
Vue3按顺序调用新增和查询接口 一、前言1、代码 一、前言 如果你想将两个调用接口的操作封装在不同的方法中,你可以考虑将这两个方法分别定义为异步函数,并在需要时依次调用它们。以下是一个示例代码: 1、代码 <template><div>…...
sizeof的了解
32位编译器 qDebug() << "int:" << sizeof(int);qDebug() << "char:" << sizeof(char);qDebug() << "char*:" << sizeof(char*); 字节数: int: 4 char: 1 char*: 4 64位编译器 字节数&#…...
PostgreSQL 教程
## PostgreSQL 教程 ### 1. PostgreSQL 概述 PostgreSQL 是一个开源的对象关系型数据库管理系统(ORDBMS),以其高扩展性和合规性闻名,支持 SQL 和 JSON 查询。 ### 2. 安装与配置 - **下载与安装**:从 PostgreSQL 官方…...
《基于Jmeter的性能测试框架搭建》改进一
《基于Jmeter的性能测试框架搭建》文末笔者提到了不少待改进之处,如下所示。 Grafana性能图表实时展现,测试过程中需实时截图形成测试报告,不够人性化。解决方案:自动生成测试报告并邮件通知。 Grafana性能图表需测试人员实时监控…...
计算机二进制表示和存储各种数据
目录 计算机二进制是什么 计算机中二进制数作用 不同数据的表示和存储 数字 文字 图片 音频 视频 计算机的中数据的显示和存储 计算机二进制是什么 计算机二进制数:计算机里存储的一切都是以二进制的0和1来表示。二进制是计算机使用的数字编码系统&#x…...
玩机社区 - 2024年最美社区源码开源
玩机社区 - 2024年最美社区源码开源 教程源码文档都内置到压缩包了 https://pan.baidu.com/s/1xwcscTne-JMbmKEntiuAuA?pwd78oi...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...
2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
