nuxt、vue树形图d3.js

news2024/11/27 4:14:34

在这里插入图片描述

直接上代码

//安装
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;
      // 定义画布—— 外边距 10px
      let 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为自定义html
            y:
              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;
          }
          // 标准节点—— 传出 prop
          if (data.dicType) {
            that.$emit("dicTypeChange", data.dicType);
          }
          // 标准节点—— 传出 prop
          if (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.coloradmin.cn/o/1897477.html

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

相关文章

MongoDB-社区版-本地安装

系统&#xff1a;win10 1. 下载server:Download MongoDB Community Server | MongoDB 我选的zip包 2. 下载shell&#xff1a;MongoDB Shell Download | MongoDB 我选的zip包 3. 启动server 4. 启动shell, 完成

【基础算法】UE中实现轮播

本期作者&#xff1a;尼克 易知微3D引擎技术负责人 当前N 总数M 从0到M-1 从1到M 感谢阅读&#xff0c;以上内容均由易知微3D引擎团队原创设计&#xff0c;以及易知微版权所有&#xff0c;转载请注明出处&#xff0c;违者必究&#xff0c;谢谢您的合作。申请转载授权后台回复【…

堆叠的作用

一、为什么要堆叠 传统的园区网络采用设备和链路冗余来保证高可靠性&#xff0c;但其链路利用率低、网络维护成本高&#xff0c;堆叠技术将多台交换机虚拟成一台交换机&#xff0c;达到简化网络部署和降低网络维护工作量的目的。 二、堆叠优势 1、提高可靠性 堆叠系统多台成…

【深度学习】图形模型基础(5):线性回归模型第二部分:单变量线性回归模型

1.引言 在统计学与机器学习的广阔领域中&#xff0c;线性回归作为一种基础而强大的预测技术&#xff0c;其核心在于通过输入变量&#xff08;或称预测器、自变量&#xff09;来估计输出变量&#xff08;响应变量、因变量&#xff09;的连续值。本章聚焦于线性回归的一个基本但…

数据洞察:从零到一的数据仓库与Navicat连接全攻略【实训Day04】[完结篇]

一、数据分析 1 实现数据仓库(在hadoop101上) 1) 创建jobdata数据库 # cd $HIVE_HOME # bin/hive hive>create database jobdata; hive>use jobdata; 2) 创建原始职位数据事实表ods_jobdata_orgin(在hadoop101上) create table ods_jobdata_origin( city string CO…

Python爬虫零基础实战,简洁实用!

1.爬虫简介 简单来讲&#xff0c;爬虫就是一个探测机器&#xff0c;它的基本操作就是模拟人的行为去各个网站溜达&#xff0c;点点按钮&#xff0c;查查数据&#xff0c;或者把看到的信息背回来。就像一只虫子在一幢楼里不知疲倦地爬来爬去。 你可以简单地想象&#xff1a;每个…

Stream的获取、中间方法、终结方法

1、获取Stream流 单列集合&#xff1a;foreach完整版 双列集合通过Ketset()、entryset() 数组的&#xff1a;通过Arrays Stream流的中间方法&#xff1a;链式编程&#xff0c;原stream流只能使用一次 filter&#xff1a; limit、skip&#xff1a; distinct(有自定义对象需要重写…

MYSQL 四、mysql进阶 6(索引的创建与设计原则)

一、索引的声明和使用 1.1 索引的分类 MySQL的索引包括普通索引、唯一性索引、全文索引、单列索引、多列索引和空间索引等。 从 功能逻辑 上说&#xff0c;索引主要有 4 种&#xff0c;分别是普通索引、唯一索引、主键索引、全文索引。 按照 物理实现方式 &#xff0c;索引可…

计算机网络之令牌总线

上文内容&#xff1a;什么是以太网 1.令牌总线工作原理 在总线的基础上&#xff0c;通过在网络结点之间有序地传递令牌来分配各结点对共享型总线的访问权利&#xff0c;形成闭合的逻辑环路。 完全采用半双工的操作方式&#xff0c;只有获得令牌的结点才能发送信息&#xff…

【matlab 项目工期优化】基于NSGA2/3的项目工期多目标优化(时间-成本-质量-安全)

一 背景介绍 本文分享了一个通用的项目工期优化的案例&#xff0c;决策变量是每个子项目的工期&#xff0c;优化目标是项目的完成时间最小&#xff0c;项目的总成本现值最小&#xff0c;项目的总安全水平最高&#xff0c;项目的总质量水平最高。采用的算法是NSGA2和NSGA3算法。…

YOLOV++ 详解 | 网络结构、代码解析、YOLOV 论文阅读、初识 VID

前言 代码地址&#xff1a;https://github.com/YuHengsss/YOLOV 本文网络结构按 YOLOV SwinTiny 绘制&#xff0c;不同的模型主要差异在于 Backbone&#xff0c;VID 相关的部分基本相同。 Predict Input 代码基于 vid_demo。首先会读取视频中的所有帧&#xff08;只能用短视频…

kafka系列之消费后不提交offset情况的分析总结

概述 每当我们调用Kafka的poll()方法或者使用KafkaListener(其实底层也是poll()方法)时&#xff0c;它都会返回之前被写入Kafka的记录&#xff0c;即我们组中的消费者还没有读过的记录。 这意味着我们有一种方法可以跟踪该组消费者读取过的记录。 如前所述&#xff0c;Kafka的一…

自闭症儿童的治疗方法有哪些?

身为星贝育园自闭症儿童康复学校的资深教育者&#xff0c;我深知自闭症谱系障碍&#xff08;ASD&#xff09;儿童的教育与治疗需要一个全面、个性化的方案。在星贝育园&#xff0c;我们致力于为孩子们提供一个充满爱与理解的环境&#xff0c;采用多种科学验证的教育方法&#x…

【Linux】动态库的制作与使用

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

asp.net公交司机管理系统-计算机毕业设计源码96696

摘 要 公交司机是公交运输系统中的重要组成部分&#xff0c;他们的管理和运营对于公交运输的正常运行和服务质量起着至关重要的作用。本文提出了一种基于C#&#xff08;asp.net&#xff09;的公交司机管理系统。该系统利用计算机技术和网络通信技术&#xff0c;实现了公交司机信…

Ollama:本地大模型运行指南_ollama运行本地模型

Ollama 简介 Ollama 是一个基于 Go 语言开发的可以本地运行大模型的开源框架。 官网&#xff1a;ollama.com/ GitHub 地址&#xff1a;github.com/ollama/olla… Ollama 安装 【一一AGI大模型学习 所有资源获取处一一】 ①人工智能/大模型学习路线 ②AI产品经理入门指南 ③…

【 香橙派 AIpro评测】大语言模型实战教程:香橙派 AIpro部署LLMS大模型实站(保姆级教学)

引言 OrangePi AIpro 这块板子作为业界首款基于昇腾深度研发的AI开发板&#xff0c;一经发布本博主就火速去关注了&#xff0c;其配备的 8/20TOPS澎湃算力是目前开发板市场中所具备的最大算力&#xff0c;可谓是让我非常眼馋啊&#xff01;这么好的板子那必须拿来用用&#xff…

Java面试八股之如何提高MySQL的insert性能

如何提高MySQL的insert性能 提高MySQL的INSERT性能可以通过多种策略实现&#xff0c;以下是一些常见的优化技巧&#xff1a; 批量插入&#xff1a; 而不是逐条插入&#xff0c;可以使用单个INSERT语句插入多行数据。例如&#xff1a; INSERT INTO table_name (col1, col2) V…

用Python轻松转换PDF为CSV

数据的可访问性和可操作性是数据管理的核心要素。PDF格式因其跨平台兼容性和版面固定性&#xff0c;在文档分享和打印方面表现出色&#xff0c;尤其适用于报表、调查结果等数据的存储。然而&#xff0c;PDF的非结构化特性限制了其在数据分析领域的应用。相比之下&#xff0c;CS…

AI时代下 AI搜索成“兵家必争之地”

当下&#xff0c;海量信息爆发性增长&#xff0c;用户的搜索需求也从找不到信息转变成找不到“需要的”信息。不过随着AI技术的迅速发展&#xff0c;这个需求将会得到解决&#xff0c;AI搜索也将成为“兵家必争之地”。 在全球范围内&#xff0c;谷歌作为全球最大的搜索引擎公司…