nuxt、vue树形图d3.js

在这里插入图片描述

直接上代码

//安装
npm i d3 --save
<template><div class="d3"><div :id="id" class="d3-content"></div></div>
</template>
<script>
import * as d3 from "d3";export default {props: {data: Object,nodeWidth: {type: Number,default: 340,},nodeHeight: {type: Number,default: 40,},active: {type: String,default: "",},},data() {return {id: "TreeMap" + randomString(4),deep: 0,treeData: null,show: true,demoData: {label: "中国",// link: "demo",url: "https://baike.baidu.com/item/%E4%B8%AD%E5%9B%BD/1122445?fr=aladdin",children: [{label: "浙江45468761321",// link: "isClick",disabled: true,children: [{ label: "杭州999999999" },{ label: "宁波" },{ label: "温州" },{ label: "绍兴" },],},{label: "广西",children: [{label: "桂林56465465465464",children: [{ label: "秀峰区" },{ label: "叠彩区" },{ label: "象山区" },{ label: "七星区" },],},{ label: "南宁" },{ label: "柳州" },{ label: "防城港" },],},],},};},mounted() {this.$nextTick(() => {this.drawMap();window.handleCustom=this.handleCustom;});},methods: {drawMap() {let that = this;// 源数据let data = {};// 判断data是否为空对象if (this.data && JSON.stringify(this.data) !== "{}") {data = this.data;} else {data = this.demoData;}if (!this.treeData) {this.treeData = data;} else {// 清空画布d3.select("#" + this.id).selectAll("svg").remove();}let leafList = [];getTreeLeaf(data, leafList);let leafNum = leafList.length;let TreeDeep = getDepth(data);// 左右内边距let mapPaddingLR = 10;// 上下内边距let mapPaddingTB = 0;let mapWidth = this.nodeWidth * TreeDeep + mapPaddingLR * 2;let mapHeight = (this.nodeHeight - 4) * leafNum + mapPaddingTB * 2;// 定义画布—— 外边距 10pxlet svgMap = d3.select("#" + this.id).append("svg").attr("width", mapWidth).attr("height", mapHeight).style("margin", "0px");// 定义树状图画布let treeMap = svgMap.append("g").attr("transform","translate(" +mapPaddingLR +"," +(mapHeight / 2 - mapPaddingTB) +")");// 将源数据转换为可以生成树状图的数据(有节点 nodes 和连线 links )let treeData = d3.tree()// 设置每个节点的尺寸.nodeSize(// 节点包含后方的连接线 [节点高度,节点宽度][this.nodeHeight, this.nodeWidth])// 设置树状图节点之间的垂直间隔.separation(function (a, b) {// 样式一:节点间等间距// return (a.parent == b.parent ? 1: 2) / a.depth;// 样式二:根据节点子节点的数量,动态调整节点间的间距let rate =(a.parent == b.parent? b.children? b.children.length / 2: 1: 2) / a.depth;// 间距比例不能小于0.7,避免间距太小而重叠if (rate < 0.7) {rate = 0.7;}return rate;})(// 创建层级布局,对源数据进行数据转换d3.hierarchy(data).sum(function (node) {// 函数执行的次数,为树节点的总数,node为每个节点return node.value;}));// 贝塞尔曲线生成器let Bézier_curve_generator = d3.linkHorizontal().x(function (d) {return d.y;}).y(function (d) {return d.x;});//绘制边treeMap.selectAll("path")// 节点的关系 links.data(treeData.links()).enter().append("path").attr("d", function (d) {// 根据name值的长度调整连线的起点var start = {x: d.source.x,// 连线起点的x坐标// 第1个10为与红圆圈的间距,第2个10为link内文字与边框的间距,第3个10为标签文字与连线起点的间距,60为自定义htmly:d.source.y +10 +(d.source.data.link ? getPXwidth(d.source.data.link) + 10 : 0) +getPXwidth(d.source.data.label) +(!d.source.data.children?82:0) +20,};var end = { x: d.target.x, y: d.target.y };return Bézier_curve_generator({ source: start, target: end });}).attr("fill", "none").attr("stroke", "#00AB6B")// 虚线// .attr("stroke-dasharray", "8").attr("stroke-width", 1);// 创建分组——节点+文字let groups = treeMap.selectAll("g")// 节点 nodes.data(treeData.descendants()).enter().append("g").attr("transform", function (d) {var cx = d.x;var cy = d.y;return "translate(" + cy + "," + cx + ")";});//绘制节点(节点前的圆圈)groups.append("circle")// 树的展开折叠.on("click", function (event, node) {let data = node.data;if (data.children) {data.childrenTemp = data.children;data.children = null;} else {data.children = data.childrenTemp;data.childrenTemp = null;}that.drawMap();}).attr("cursor", "pointer").attr("r", 4).attr("fill", function (d) {if (d.data.childrenTemp) {return "#00AB6B";} else {return "white";}}).attr("stroke", "#00AB6B").attr("stroke-width", 1);//绘制标注(节点前的矩形)groups.append("rect").attr("x", 8).attr("y", -10).attr("width", function (d) {return d.data.link ? getPXwidth(d.data.link) + 10 : 0;}).attr("height", 22).attr("fill", "red").attr("border", "blue")// 添加圆角.attr("rx", 4);//绘制链接方式groups.append("text").attr("x", 12).attr("y", -5).attr("dy", 10).attr("fill", "white").attr("font-size", 12).text(function (d) {return d.data.link;});//绘制文字groups.append("text").on("click", function (event, node) {let data = node.data;// 被禁用的节点,点击无效if (data.disabled) {return;}// 有外链的节点,打开新窗口后恢复到思维导图页面if (data.url) {window.open(data.url);that.$emit("activeChange", "map");return;}// 标准节点—— 传出 propif (data.dicType) {that.$emit("dicTypeChange", data.dicType);}// 标准节点—— 传出 propif (data.prop) {that.$emit("activeChange", data.prop);}}).attr("x", function (d) {return 12 + (d.data.link ? getPXwidth(d.data.link) + 10 : 0);}).attr("fill", function (d) {if (d.data.prop === that.active) {return "#409EFF";}}).attr("font-weight", function (d) {if (d.data.prop === that.active) {return "bold";}}).attr("font-size", 14).attr("cursor", function (d) {if (d.data.disabled) {return "not-allowed";} else {return "pointer";}}).attr("y", -5).attr("dy", 10).attr("slot", function (d) {return d.data.prop;});// .text(function (d) {//   return d.data.label;// });groups.append("foreignObject").attr("width", (d) => {return getPXwidth(d.data.label) + 22 + (!d.data.children?82:0);}).attr("height", 100).attr("x", function (d) {return 12 + (d.data.link ? getPXwidth(d.data.link) + 10 : 0);}).on("click", function (event, node) {}).attr("y", -10).append("xhtml:div").style("font", '14px "Helvetica Neue"').html((d) => {let _html = `<div class="custom-html"><div>${d.data.label}</div></div>`;if(!d.data.children){_html = `<div class="custom-html"><div>${d.data.label}</div><div οnclick="handleCustom(${1})"><i class="iconfont">&#xe648;</i>视频课</div></div>`;}return _html});},handleCustom(data){debugger}},
};// 获取树的深度
function getDepth(json) {var arr = [];arr.push(json);var depth = 0;while (arr.length > 0) {var temp = [];for (var i = 0; i < arr.length; i++) {temp.push(arr[i]);}arr = [];for (var i = 0; i < temp.length; i++) {if (temp[i].children && temp[i].children.length > 0) {for (var j = 0; j < temp[i].children.length; j++) {arr.push(temp[i].children[j]);}}}if (arr.length >= 0) {depth++;}}return depth;
}// 提取树的子节点,最终所有树的子节点都会存入传入的leafList数组中
function getTreeLeaf(treeData, leafList) {// 判断是否为数组if (Array.isArray(treeData)) {treeData.forEach((item) => {if (item.children && item.children.length > 0) {getTreeLeaf(item.children, leafList);} else {leafList.push(item);}});} else {if (treeData.children && treeData.children.length > 0) {getTreeLeaf(treeData.children, leafList);} else {leafList.push(treeData);}}
}// 获取包含汉字的字符串的长度
function getStringSizeLength(string) {//先把中文替换成两个字节的英文,再计算长度return string.replace(/[\u0391-\uFFE5]/g, "aa").length;
}// 生成随机的字符串
function randomString(strLength) {strLength = strLength || 32;let strLib = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz";let n = "";for (let i = 0; i < strLength; i++) {n += strLib.charAt(Math.floor(Math.random() * strLib.length));}return n;
}// 获取字符串的像素宽度
function getPXwidth(str, fontSize = "12px", fontFamily = "Microsoft YaHei") {var span = document.createElement("span");var result = {};result.width = span.offsetWidth;result.height = span.offsetHeight;span.style.visibility = "hidden";span.style.fontSize = fontSize;span.style.fontFamily = fontFamily;span.style.display = "inline-block";document.body.appendChild(span);if (typeof span.textContent != "undefined") {span.textContent = str;} else {span.innerText = str;}result.width = parseFloat(window.getComputedStyle(span).width) - result.width;// 字符串的显示高度// result.height = parseFloat(window.getComputedStyle(span).height) - result.height;return result.width;
}
</script>
<style lang="scss" scoped>
.d3 {position: relative;overflow: hidden;width: calc(100%);min-height: 500px;overflow-x: scroll;.d3-content {position: absolute;width: max-content;::v-deep .custom-html {display: flex;div {i {font-size: 12px;margin-right: 4px;}&:nth-child(2) {margin-left: 10px;background: #f2faf7;border: 0.5px solid #c3e7da;border-radius: 4px;color: #00ab6b;font-size: 12px;padding: 0 4px;height: 20px;cursor: pointer;}}}}
}
</style>

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

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

相关文章

【康复学习--LeetCode每日一题】3033. 修改矩阵

题目&#xff1a; 给你一个下标从 0 开始、大小为 m x n 的整数矩阵 matrix &#xff0c;新建一个下标从 0 开始、名为 answer 的矩阵。使 answer 与 matrix 相等&#xff0c;接着将其中每个值为 -1 的元素替换为所在列的 最大 元素。 返回矩阵 answer 。 示例 1&#xff1a;…

基于.NET开源游戏框架MonoGame实现的开源项目合集

前言 今天分享一些基于.NET开源游戏框架MonoGame实现的开源项目合集。 MonoGame项目介绍 MonoGame是一个简单而强大的.NET框架&#xff0c;使用C#编程语言可以创建桌面PC、视频游戏机和移动设备游戏。它已成功用于创建《怒之铁拳4》、《食肉者》、《超凡蜘蛛侠》、《星露谷物…

MySql主从同步延迟怎么办?

文章目录 什么是MySQL主从架构主从架构的组成工作原理主从复制的步骤主从架构的优点主从架构的缺点 什么是主从同步延迟为什么会导致主从延迟主从延时的排查和解决如果发现主从数据不一致怎么办&#xff1f; 我们常说的业务量越来越大&#xff0c;I/O访问频率过高&#xff0c;单…

Vue + SpringBoot:el-upload组件单文件、多文件上传实战解析

文章目录 单文件上传后端前端 多文件上传后端前端 单文件上传 后端 PostMapping("/uploadDxfFile") public R uploadDxfFile(RequestParam(value "file", required true) MultipartFile multipartFile) throws Exception {// 文件校验工作if (multipar…

redis 如何使用 scan, go语言

建议用方案乙 文章目录 场景方案方案甲方案乙 拓展 场景 redis 中存在大量 key。 其中有一部分是用户登陆的 session_id&#xff0c; 结构是 &#xff1a; session_id:1session_id:2session_id:3需求&#xff1a; 有多少用户在线 方案 方案甲 keys session_id:*这种方式简…

Linux运维:MySQL备份,物理冷备份,热备,完备+二进制日志

备份类型 完全备份、增量备份、差异备份 完全备份&#xff1a;整个数据集都备份 增量备份&#xff1a;仅备份最近一次完全备份或增量备份&#xff08;如果存在增量&#xff09;以来变化的数据&#xff0c;备份较快&#xff0c;还原复杂。 差异备份&#xff1a;对比前一次备…

《Windows API每日一练》8.3 scrollbar控件

在第三章SYSMETS2.C实例中&#xff0c;我们是通过CreateWindow函数创建窗口的参数窗口样式中添加垂直或水平滚动条。本节我们将讲述作为子窗口控件的滚动条。 本节必须掌握的知识点&#xff1a; 滚动条类 滚动条控件和着色 8.3.1 滚动条类 ■窗口滚动条与滚动条控件的异同 …

纯javascript实现图片批量压缩打包zip下载后端ThinkPHP多国语言切换国际站

最近在做一个多国语言的工具站&#xff0c;需要实现多国语言切换&#xff0c;说到多国语言站&#xff0c;肯定是有2种方式&#xff0c;第一是子域名&#xff0c;第二就是子目录。根据自己的需要来确定。 后台配置如下&#xff1a; 前台显示&#xff1a; 前端纯javascript实现…

人工智能开发中的数据隐私

人工智能开发中的数据隐私对于建立用户信任和遵守严格法规至关重要。保护敏感信息可确保合乎道德的人工智能使用并防止有害的数据泄露。 为什么在人工智能开发中优先考虑数据隐私至关重要 人工智能的迅猛发展开启了一个前所未有的技术进步时代&#xff0c;彻底改变了各行各业&…

Python创建MySQL数据库

一、使用Docker部署本地MySQL数据库 docker run --restartalways -p 3307:3306 --name mysql -e MYSOL_ROOT_PASSWORDlms123456 -d mysql:8.0.25 参数解析: 用户名:root 密码:lms123456 端口:3307 二、在Pycharm开发工具中配置连接MySQL数据库 三、安装zdppy_mysql pip inst…

全新UI自助图文打印系统小程序源码 PHP后端 附教程

最新自助图文打印系统和证件照云打印小程序源码PHP后端&#xff0c;为用户用户自助打印的服务&#xff0c;包括但不限于文档、图片、表格等多种格式的文件。此外&#xff0c;它们还提供了诸如美颜、换装、文档打印等功能&#xff0c;以及后台管理系统&#xff0c;方便管理员对打…

SpringBoot之内容协商

现象演示 假设有一个需求是根据终端的不同&#xff0c;返回不同形式的数据&#xff0c;比如 PC 端需要以 HTML 格式返回数据&#xff0c;APP、小程序端需要以 JSON 格式返回数据。这时我们是 coding 几个相似的接口&#xff1f;还是在一个接口里面做复杂判断处理&#xff1f;两…

步进电机改伺服电机

步进电机&#xff1a; 42&#xff1a;轴径5mm 57&#xff1a;轴径8mm 86&#xff1a;轴径14mm 【86CME120闭环】// 12牛米 伺服电机&#xff1a; 40&#xff1a; 60&#xff1a; 80&#xff1a; 86&#xff1a; ECMA——C 1 0910 R S 4.25A 轴径…

树状数组实现 查找逆序对

题意&#xff1a; 输入一个整数n。 接下来输入一行n个整数 。 1< < n ,且每个数字只会出现一次 题解&#xff1a; 按每个数字的大小存入树状数组 #include<bits/stdc.h> using namespace std; #define ll long long const int N10000; int arr[N]; ll a[N];…

Ros2中goal_handle状态SUCCEED及ACCEPTED在rclpy中的死循环

动作通信开发中&#xff0c;我使用rclpy编写代码&#xff0c;进行feedback等操作&#xff0c;前期实现的较为顺利&#xff0c;但所有逻辑均编写完后&#xff0c;却无法将goal_handle提交为succeed状态&#xff0c;以下是我的部分代码&#xff1a; self.server ActionServer(se…

利用Redis bitmap 实现签到案例

数据库实现 设计签到功能对应的数据库表 CREATE TABLE sign_record (id bigint NOT NULL AUTO_INCREMENT COMMENT 主键,user_id bigint NOT NULL COMMENT 用户id,year year NOT NULL COMMENT 签到年份,month tinyint NOT NULL COMMENT 签到月份,date date NOT NULL COMMENT 签…

GDP播放器 驱动视频播放器 PHP 系统源码 v4.4.3

最重要的是我们自己开发了源代码&#xff0c;因此无论您在使用此工具时遇到什么问题&#xff0c;我们都会快速解决。这个版本演示 分别支持PHP7.4/8.1/8.2三个版本 演示地址

8、Python中的字符串:字符串的基本操作及再论对象是否可变

引言 基于Python系列前面的几篇文章中&#xff0c;应该已经在脑海中对Python中的核心理念&#xff0c;有了一些认知&#xff0c;由于比较关键&#xff0c;这里再来回顾一下&#xff1a; 1、Python中一切皆对象2、对象分为可变对象和不可变对象3、区分重新赋值操作&#xff0c…

Dos(命令符窗口)命令

目录 1. 常用Windows组合键 2. 常用DOS命令 3. 复制操作 4. 当前路径 5. 查看电脑ip地址 6. 切换盘符: 7. 目录 8. 自动补齐 8. 进入某路径&#xff1a;cd 路径 9. 直接进入某个位置 10. 新建文件 11. 查看文件内容 12. 关机 13. 强行终止命令的执行&#xff1a;C…

java基于ssm+vue 旅游信息资源平台

1前台首页功能模块 旅游资源网站 &#xff0c;在系统首页可以查看首页、景点信息、酒店信息、客房信息、交流论坛、红色文化、个人中心、后台管理、客服等内容&#xff0c;如图1所示。 图1系统功能界面图 用户登录、用户注册&#xff0c;在注册页面可以填写用户名、密码、姓名…