d3.js 实现企业图谱(基于vue)
背景
前段时间接到公司需求,参考企查查开发=>企业图谱。最开始定的是使用Relation Graph实现,用了一段时间后发现这个插件对于一些个性化的需求支持不足,无奈只能另辟新径,后来发现了d3.js这个新大陆。
由于这是第一次接触d3,刚开始真是一脸懵,后来通过翻阅资料,先是了解了一下svg,然后又把d3.js常用的一些api看了看算是有个初步的了解。
技术点
使用d3主要需要掌握svg,jquery和d3这几个知识点。
本次使用的是 d3.v5版本的。
案例
实现效果
实现代码
HTML
<template><div class="tree04"><div class="seeTree-page" id="app"><div id="treeRoot"></div></div></div>
</template>
JS
<script>
import companyJson from "./DLJSON/companyJson.json";
import $ from "./js/jquery-3.1.1.min";
import * as d3 from "./js/d3.v5";
let svg;
let dirRight;
let forUpward;
let leng;
// var u = navigator.userAgent
// app = navigator.appVersion;
// var isAndroid = u.indexOf("Android") > -1 || u.indexOf("Linux") > -1; //g
// var isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
// var ua = navigator.userAgent.toLowerCase();
// var ualower = navigator.userAgent.toLocaleLowerCase();
// //var treeData =
// var width = document.body.clientWidth,
// height = document.body.clientHeight;
let toggle = true;
let circlewidth1;
let circlewidth2;
let circlewidth3;
let margin1 = { top: 50, right: 20, bottom: -20, left: 0 };
export default {data() {return {container: null, //容器svg>gduration: 500, //动画持续时间scaleRange: [0.2, 2], //container缩放范围direction: ["r", "l"], //分为左右2个方向centralPoint: [0, 0], //画布中心点坐标x,yroot: { r: {}, l: {} }, //左右2块数据源rootNodeLength: 0, //根节点名称长度rootName: ["昆仑数智", ""], //根节点名称textSpace: 15, //多行文字间距themeColor: "#2196F3", //主色nodeSize: [50, 100], //节点间距(高/水平)fontSize: 14, //字体大小,也是单字所占宽高rectMinWidth: 50, //节点方框默认最小,textPadding: 5, //文字与方框间距,注:固定值5circleR: 5, //圆圈半径dirRight: "",treeData: [],};},created() {},mounted() {this.getData();},computed: {treeMap() {return d3.tree().nodeSize(this.nodeSize).separation((a, b) => {let result =a.parent === b.parent && !a.children && !b.children ? 1 : 2;return result;});},},watch: {},methods: {getData() {// var mynodes;// $.ajax({// url: "first.json",// contentType: "application/json;charset=UTF-8",// type: "POST",// dataType: "json",// success: function (data) {this.rootName = [companyJson.data.rootName, ""];let data = companyJson.data;console.log(data, "原始数据");let left = data.l;let right = data.r;let mynodes;for (var i = 0; i < left.children.length; i++) {if (left.children[i].children.length > 2) {mynodes = left.children[i].children;left.children[i].children = left.children[i].children.slice(0, 2);left.children[i].children[2] = {name: "更多",type: -1,val: mynodes.length - 2,childrend: mynodes.slice(0, mynodes.length),};}}for (var i = 0; i < right.children.length; i++) {if (right.children[i].children.length > 2) {mynodes = right.children[i].children;right.children[i].children = right.children[i].children.slice(0, 2);right.children[i].children[2] = {name: "更多",type: -1,val: mynodes.length - 2,childrend: mynodes.slice(0, mynodes.length),};}}// console.log(data, "初始化");this.treeInit(data);// }// })},uuid() {function s4() {return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);}return (s4() +s4() +"-" +s4() +"-" +s4() +"-" +s4() +"-" +s4() +s4() +s4());},//初始化treeInit(data) {const margin = { top: 0, right: 0, bottom: 0, left: 0 };let treeWidth = d3.select("#treeRoot")._parents[0].clientWidth;let treeHeight = d3.select("#treeRoot")._parents[0].clientHeight;// const treeWidth = document.body.clientWidth - margin.left - margin.right; //tree容器宽// const treeHeight = document.body.clientHeight - margin.top - margin.bottom; //tree容器高const centralY = treeWidth / 2 + margin.left;const centralX = treeHeight / 2 + margin.top;this.centralPoint = [centralX, centralY]; //中心点坐标//根节点字符所占宽度this.rootNodeLength = this.rootName[0].length * this.fontSize + 50;//svg标签svg = d3.select("#treeRoot").append("svg").attr("class", "tree-svg").attr("xmlns", "http://www.w3.org/2000/svg").attr("width", treeWidth).attr("height", treeHeight).attr("font-size", this.fontSize).attr("fill", "#555");//g标签this.container = svg.append("g").attr("class", "container1").attr("transform","translate(" + margin1.left + "," + margin1.top + ")scale(0.8)");//画出根节点this.drawRoot();//指定缩放范围const zoom = d3.zoom().scaleExtent(this.scaleRange).on("zoom", this.zoomFn);//动画持续时间this.container.transition().duration(this.duration).call(zoom.transform, d3.zoomIdentity);svg.call(zoom);//数据处理// console.log(data, "数据处理");this.dealData(data);},zoomFn() {var weizhi = document.getElementsByClassName("container1")[0];weizhi.style.transform = ""; //偏移位置let zoom = d3.event.transform;return this.container.attr("transform","translate(" +(Number(zoom.x) + Number(margin1.left)) +"," +(zoom.y + margin1.top) +")scale(" +zoom.k * 0.8 +")");},//数据重组DataReor(d, direction, source, appendData) {var setDepth = function (node, num, appendLeaf) {//重新设置depthnode.depth = num;if (node.children && node.children.length) {//遍历childrennode.children.forEach(function (item) {setDepth(item, num + 1, appendLeaf);});} else {appendLeaf.push(node);}};var setHeight = function (arr, num) {//重新设置heightvar parent = [];arr.forEach(function (node) {node.height = Math.max(num, node.height);if (node.parent && parent.indexOf(node.parent) == -1) {parent.push(node.parent);}});if (parent.length) {setHeight(parent, num + 1);}};var appendLeaf = []; //增加的叶子节点if (appendData.children.length) {d.children = [];appendData.children.forEach(function (item, index) {var newNode = d3.hierarchy(item);newNode.parent = d;newNode.height = -1;setDepth(newNode, d.depth + 1, appendLeaf);d.children.push(newNode);});}if (appendLeaf.length) {setHeight(appendLeaf, 0);}if (source.data.name == "更多") {source.parent.descendants().forEach((d) => {d._children = d.children;d.id = direction + this.uuid();});} else {source.descendants().forEach((d) => {d._children = d.children;d.id = direction + this.uuid();});}this.update(d, direction);},getNode(d, direction, source, type) {let mynodes;// 注释ly---start// if (!d.data.isNextNode && d.data.type != -1) {// return;// }// 注释ly---end// console.log("重组数据");if (d.data.name == "更多") {var num = d.data.val / 5;if (num <= 5) {var arr = d.data.childrend;} else {var arr = d.data.childrend.slice(0, d.parent.children.length + 4);arr[d.parent.children.length + 4] = {nodeName: "更多",type: -1,val: d.data.childrend.length - d.parent.children.length + 4,childrend: d.data.childrend,};}var appendData = {children: arr,};this.DataReor(d.parent, direction, source, appendData);} else if (type == 1) {var appendData = {children: [],};this.DataReor(d, direction, source, appendData);} else {//请求数据// console.log(d.data, "请求数据参数 update");$("#loading,.shaow").show();let data = [];$("#loading,.shaow").hide();if (data.length == 0) {html ='<div class="tk"><p style="font-size: 14px;color: #359ffb">' +d.data.name +"</p><span>暂无数据</span></div>";$(".shaow").show();$("body").append(html);return;}mynodes = data;if (data.length > 10) {data = data.slice(0, 10);data[11] = {name: "更多",type: -1,val: mynodes.length - 10,childrend: mynodes.slice(0, mynodes.length),};}var appendData = {children: data,};this.DataReor(d, direction, source, appendData);}},//数据处理dealData(data) {this.direction.forEach((item) => {this.root[item] = d3.hierarchy(data[item]);this.root[item].x0 = this.centralPoint[0]; //根节点x坐标this.root[item].y0 = this.centralPoint[1]; //根节点Y坐标this.porData(this.root[item], item);});},//遍历porData(obj, item) {obj.descendants().forEach((d) => {d._children = d.children;d.id = item + this.uuid();});this.update(obj, item);},//画根节点drawRoot() {const title = this.container.append("g").attr("id", "rootTitle").attr("transform",`translate(${this.centralPoint[1]},${this.centralPoint[0]})`);title.append("svg:rect").attr("class", "rootTitle").attr("y", 0).attr("x", -this.rootNodeLength / 2).attr("width", this.rootNodeLength).attr("height", 0).attr("rx", 5) //圆角.style("fill", this.themeColor);this.rootName.forEach((name, index) => {title.append("text").attr("fill", "white").attr("y", function () {return 5;}).attr("text-anchor", "middle").style("font-size", function () {if (index == 1) {return "10px";} else {return "15px";}}).text(name);let lineHeight = (index + 2) * this.textSpace;d3.select("#rootTitle rect").attr("height", lineHeight).attr("y", -lineHeight / 2);});},seat(newArr, dirRight, data) {for (var j = 0; j < newArr.length; j++) {if (j != 0) {for (var i = 0; i < data[j].length; i++) {data[j][i].y = data[j - 1][0].y + newArr[j - 1] - 40;}}}},testLength(data, dirRight) {var level = [],width1,width2;for (var i = 0; i < data.length; i++) {var newArr = new Array();for (var j = 0; j < data[i].length; j++) {if (data[i][j].data.attName) {svg.append("text").style("font-size", this.fontSize).text((d) => "(" + data[i][j].data.attName + ")").attr("class", "test").attr("width", function (d) {width3 = d3.select(this.getComputedTextLength())._groups[0][0];console.log(width3,"width3");});}if (data[i][j].data.ratio) {svg.append("text").style("font-size", this.fontSize).text(function (d) {const len = data[i][j].data.name.length;if (len > 20) {return data[i][j].data.name.substring(0, 20) + "...";} else {return data[i][j].data.name;}}).attr("class", "test").attr("width", function (d) {width1 = d3.select(this.getComputedTextLength())._groups[0][0];});svg.append("text").style("font-size", this.fontSize).text((d) => data[i][j].data.ratio).attr("class", "test").attr("width", function (d) {width2 = d3.select(this.getComputedTextLength())._groups[0][0];});$(".test").remove();if (data[i][j].data.attName) {if (Number(width1) + Number(width3) > Number(width2)) {newArr.push(Number(width1) + Number(width3) + 100);} else {newArr.push(Number(width2) + 100);}} else {if (Number(width1) > Number(width2)) {newArr.push(Number(width1) + 100);} else {newArr.push(Number(width2) + 100);}}} else {svg.append("text").style("font-size", this.fontSize).text(function (d) {if (data[i][j].data.name) {const len = data[i][j].data.name.length;if (len > 20) {return data[i][j].data.name.substring(0, 20) + "...";} else {return data[i][j].data.name;}}}).attr("class", "test").attr("width", function (d) {width1 = d3.select(this.getComputedTextLength())._groups[0];newArr.push(Number(width1) + 100);data.width1 = d3.select(this.getComputedTextLength())._groups[0];});$(".test").remove();}}level.push(Math.max.apply(null, newArr));}this.seat(level, dirRight, data);},//开始绘图update(source, direction) {// console.log(source, direction, "update");let that = this;dirRight = direction === "r" ? 1 : -1; //方向为右/左forUpward = direction == "r";let className = `${direction}gNode`;let tree = this.treeMap(this.root[direction]);let nodes = tree.descendants();let links = tree.links();var data = [];if (nodes.length > 1) {for (var i = 0; i < nodes.length; i++) {if (!data[nodes[i].depth]) {var arr = [];arr.push(nodes[i]);data[nodes[i].depth] = arr;} else {data[nodes[i].depth].push(nodes[i]);}}//检测最大长度this.testLength(data, dirRight);}nodes.forEach((d) => {d.y = dirRight * (d.y + this.rootNodeLength / 2) + this.centralPoint[1];d.x = d.x + this.centralPoint[0];});//根据class名称获取左或者右的g节点,达到分块更新const node = this.container.selectAll(`g.${className}`).data(nodes, (d) => d.id);//新增节点,tree会根据数据内的children扩展相关节点const nodeEnter = node.enter().append("g").attr("id", (d) => `g${d.id}`).attr("class", className).attr("transform", (d) => `translate(${source.y0},${source.x0})`).attr("fill-opacity", 0).attr("stroke-opacity", 0).on("click", function (d) {d3.select(this).selectAll(".node-circle .node-circle-vertical").transition().duration(that.duration).attr("stroke-width", function (d) {if (d.children) {return 1;} else {return 0;}});if (d.data.name == "更多") {return that.clickNode(d, direction, source);} else if (d.depth !== 0) {return that.clickNode(d, direction, source);}});nodeEnter.each((d) => {if (d.depth > 0) {this.drawText(`g${d.id}`, dirRight);if (d.data.attName) {this.drawCodeText(`g${d.id}`, dirRight);}if (d.data.ratio) {this.drawTsText(`g${d.id}`, dirRight);}this.drawRect(`g${d.id}`, dirRight);this.marker(`g${d.id}`, dirRight);}if (d.depth > 0) {const width = Math.min(d.data.name.length * 14, this.rectMinWidth);let right = dirRight > 0;let xDistance = right ? width : -width;this.drawCircle(`g${d.id}`, dirRight, source, direction);this.drawSign(`g${d.id}`, dirRight); //画标记}//画节点数量// console.log(d, "画节点数量");if (d.data && d.data.type == -1) {this.drawLength(`g${d.id}`, dirRight);}});// 更新节点:节点enter和exit时都会触发tree更新const nodeUpdate = node.merge(nodeEnter).transition().duration(this.duration).attr("transform", function (d) {if (d.data && d.data.isNextNode) {d3.select(this).selectAll(".node-circle .node-circle-vertical").transition().duration(this.duration).attr("stroke-width", function (d) {if (d.children) {return 0;} else {return 1;}});}var index = 0;return "translate(" + d.y + "," + d.x + ")";}).attr("fill-opacity", 1).attr("stroke-opacity", 1);// 移除节点:tree移除掉数据内不包含的节点(即,children = false)const nodeExit = node.exit().transition().duration(this.duration).remove().attr("transform", (d) => `translate(${source.y},${source.x})`).attr("fill-opacity", 0).attr("stroke-opacity", 0);// Update the links 根据 className来实现分块更新const link = this.container.selectAll(`path.${className}`).data(links, (d) => d.target.id);// Enter any new links at the parent's previous position.//insert是在g标签前面插入,防止连接线挡住G节点内容const linkEnter = link.enter().insert("path", "g").attr("class", className).attr("d", (d) => {const o = { x: source.x0, y: source.y0 };return this.diagonal({ source: o, target: o });}).attr("fill", "none").attr("stroke-width", 0.5).attr("stroke", "#D8D8D8");// Transition links to their new position.link.merge(linkEnter).transition().duration(this.duration).attr("d", this.diagonal);// Transition exiting nodes to the parent's new position.link.exit().transition().duration(this.duration).remove().attr("d", (d) => {const o = { x: source.x, y: source.y };return this.diagonal({ source: o, target: o });});// Stash the old positions for transition.this.root[direction].eachBefore((d) => {d.x0 = d.x;d.y0 = d.y;});},//画连接线diagonal({ source, target }) {let s = source,d = target;if (forUpward) {return ("M" +s.y +"," +s.x +"L" +(s.y + (d.y - s.y) - 20) +"," +s.x +"L" +(s.y + (d.y - s.y) - 20) +"," +d.x +"L" +d.y +"," +d.x);} else {return ("M" +s.y +"," +s.x +"L" +(s.y + (d.y - s.y) + 20) +"," +s.x +"L" +(s.y + (d.y - s.y) + 20) +"," +d.x +"L" +d.y +"," +d.x);}},//画箭头marker(id, dirRight) {let gMark = d3.select(`#${id}`).append("g").attr("transform",dirRight > 0 ? "translate(-20,0)" : "translate(12,0)");return gMark.insert("path", "text").attr("d", function (d) {if (d.data.nodeType == 0 && dirRight > 0) {return "M0,0L0,3L9,0L0,-3Z";} else if (d.data.nodeType == 0 && dirRight < 0) {return "M0,0L9,-3L9,3Z";}}).style("fill", (d) => this.getRectStorke(d.data.name));},//华标记drawSign(id, dirRight) {return d3.select(`#${id}`).insert("circle", "text").attr("cx", dirRight > 0 ? -5 : 5).attr("y", -2.5).attr("r", function (d) {if (d.data.nodeType == 0) {return 4;}}).style("fill", (d) => this.getRectStorke(d.data.name));},//画文本drawText(id, dirRight) {let that = this;dirRight = dirRight > 0; //右为1,左为-1return d3.select(`#${id}`).append("text").attr("y", function (d) {if (d.data.ratio) {return that.textPadding - 8;} else {return that.textPadding;}}).attr("x", (d) => (dirRight ? that.textPadding : -that.textPadding)).attr("text-anchor", dirRight ? "start" : "end").style("font-size", that.fontSize).text(function (d) {const len = d.data.name.length;if (len > 20) {return d.data.name.substring(0, 20) + "...";} else {return d.data.name;}}).attr("fill", function (d) {if (d.data.nodeType == -1) {return "rgb(33, 150, 243)";} else if (d.data.nodeType == 0 || d.data.attName) {return "#000";} else {return "#333";}}).attr("width", function (d) {circlewidth1 = d3.select(this.getComputedTextLength())._groups[0][0];return d3.select(this.getComputedTextLength())._groups[0][0];});},//画子文本drawTsText(id, dirRight) {let that = this;return d3.select(`#${id} text`).append("tspan").attr("fill", (d) => (d.data.attName ? "#fff" : "#999")).attr("y", function (d) {if (d.data.ratio) {return that.textPadding + 8;}}).attr("x", function (d) {if (dirRight > 0) {return that.textPadding;} else {return -5;}}).style("font-size", "11px").text(function (d) {return d.data.ratio;}).attr("width", function (d) {circlewidth2 = d3.select(this.getComputedTextLength())._groups[0][0];return d3.select(this.getComputedTextLength())._groups[0][0];});},//画股票代码drawCodeText(id, dirRight) {return d3.select(`#${id} text`).append("tspan").attr("fill", (d) => "#fff").attr("y", function (d) {if (d.data.ratio) {return this.textPadding + 8;}}).style("font-size", "11px").attr("x", function (d) {if (dirRight > 0) {return this.textPadding;} else {return -5;}}).text(function (d) {return d.data.attName + " ";}).attr("width", function (d) {circlewidth3 = d3.select(this.getComputedTextLength())._groups[0][0];return d3.select(this.getComputedTextLength())._groups[0][0];});},//节点数量drawLength(id) {return d3.select(`#${id} text`).append("tspan").attr("fill", (d) => "#999").text(function (d) {if (d.data.type == -1) {return " (" + d.data.val + ")";}return " [" + d.data.ratio + "]";}).attr("fill", "rgb(33, 150, 243)").attr("width", function (d) {return d3.select(this.getComputedTextLength())._groups[0];});},//画方框阴影drawFilter(id) {return d3.select(`#${id}`).insert("defs", "rect").append("filter").attr("id", `f${id}`).attr("x", 0).attr("y", 0).append("feGaussianBlur").attr("in", "SourceGraphic").attr("stdDeviation", "5");},//画方框drawRect(id, dirRight) {let that = this;let realw = document.getElementById(id).getBBox().width + 25; //获取g实际宽度后,设置rect宽度if (document.getElementById(id).getBBox().width > 400) {realw = 400;}return d3.select(`#${id}`).insert("rect", "text").attr("x", function (d) {if (dirRight < 0) {return -realw;} else {0;}}).attr("y", function (d) {if (d.data.ratio) {return -that.textSpace + that.textPadding - 11;} else {return -that.textSpace + that.textPadding - 4;}}).attr("width", function (d) {// if (d.data.isNextNode) {//判断是否有下级节点// return realw// } else {// return realw - 15// }return realw;}).attr("height", function (d) {if (d.data.ratio) {return that.textSpace + that.textPadding + 22;} else {return that.textSpace + that.textPadding + 8;}}).attr("stroke-width", (d) =>d.data.nodeType == 0 || d.data.type == -1 || d.data.attName ? 0 : 0.5).attr("rx", 2) //圆角.style("stroke", (d) =>d.data.nodeType == -1 ? "" : that.getRectStorke(d.parent.data.name)).style("fill", function (d) {if (d.data.nodeType == -1) {return "rgb(241, 241, 241)";} else if (d.data.nodeType == 0) {return that.getRectStorke(d.data.name);} else if (d.data.attName) {return "rgb(33, 150, 243)";} else {return "#fff";}});},//画circledrawCircle(id, dirRight, source, direction) {let gMark = d3.select(`#${id}`).append("g").attr("class", "node-circle").attr("stroke", "rgb(153, 153, 153)").attr("transform", function (d) {if (d.data.ratio) {if (circlewidth1 > circlewidth2) {leng = Number(circlewidth1) + 15;} else {leng = Number(circlewidth2) + 15;}//leng = Number(circlewidth1) + Number(circlewidth2) + 25;if (leng > 390) {leng = 390;}if (dirRight == 1) {return "translate(" + leng + ",0)";} else {return "translate(" + -leng + ",0)";}} else {leng = Number(circlewidth1) + 15;if (dirRight == 1) {return "translate(" + leng + ",0)";} else {return "translate(" + -leng + ",0)";}}})// .attr('stroke-width', d => d.data.isNextNode ? 1 : 0); 判断是否有下级节点.attr("stroke-width", (d) => (d.data.type == "-1" ? 0 : 1));gMark.append("circle").attr("fill", "none").attr("r", function (d) {if (d.data.type == "-1") {return 0;}return 5;}) //根节点不设置圆圈.attr("fill", "#ffffff");let padding = this.circleR - 2;gMark.append("path").attr("d", `m -${padding} 0 l ${2 * padding} 0`); //横线gMark.append("path") //竖线,根据展开/收缩动态控制显示.attr("d", `m 0 -${padding} l 0 ${2 * padding}`).attr("stroke-width", function (d) {if (d.data.type == "-1") {return 0;}return 1;}).attr("class", "node-circle-vertical");return gMark;},//点击某个节点// clickNode(d, direction, source) {// if (d.children) {// d._children = d.children;// d.children = null;// this.getNode(d, direction, source, 1);// } else {// d.children = d._children;// d._children = [];// this.getNode(d, direction, source); //原注// }// if (d.data.name == "更多") {// this.getNode(d, direction, source);// return;// }// this.update(d, direction); //原注// },expand(d, direction, source) {if (d.data.name == "更多") {this.getNode(d, direction, source);return;}if (d._children) {d.children = d._children;d._children = null;this.update(d, direction);}},collapse(d, direction, obj) {if (d.children) {d._children = d.children;d.children = null;console.log(d._children, "_children");}if (obj == 1) {this.update(d, direction);}},//点击某个节点lyclickNode(d, direction, source) {if (d.children || d.children) {this.collapse(d, direction, 1);} else {this.expand(d, direction, source);}},getTsTextColor(name) {switch (name) {case "股东":return "darkgray";case "供应商":return "#FF9800";case "合伙人":return "green";default:return "black";}},//末 节点 边框颜色getRectStorke(name) {switch (name) {case "分支机构":return "rgb(255, 234, 218)";case "对外投资":return "rgb(215, 236, 255)";case "股东":return "rgb(211, 234, 241)";case "高管":return "rgb(237, 227, 244)";default:return "rgb(133, 165, 255)";}},isNull(val) {return val ? val : "";},},
};
</script>
JSON数据结构
{"code": 0,"msg": "操作成功!","data": {"rootName": "昆仑数智科技有限责任公司","r": {"name": "origin","children": [{"name": "对外投资","nodeType": 0,"children": [{"name": "成都川油瑞飞科技有限责任公司","ratio": "51.00%","hasChildren": true,"children": []},{"name": "深圳市鲲鹏快付科技有限公司","ratio": "40.00%","hasChildren": true,"children": []},{"name": "北京市凌怡科技有限公司","ratio": "100.00%","hasChildren": true,"children": []}]},{"name": "分支机构","nodeType": 0,"children": [{"name": "天津市河西区市场监督管理局","ratio": "","hasChildren": true,"children": []},{"name": "北京中油瑞飞信息技术有限责任公司河北分公司","ratio": "","hasChildren": true,"children": []},{"name": "昆仑数智科技有限责任公司海南分公司","ratio": "","hasChildren": true,"children": []},{"name": "昆仑数智科技有限责任公司成都分公司","ratio": "","hasChildren": true,"children": []},{"name": "昆仑数智科技有限责任公司西安分公司","ratio": "","hasChildren": true,"children": []}]}]},"l": {"name": "origin","children": [{"name": "股东","nodeType": 0,"children": [{"name": "中国石油集团东方地球物理勘探有限责任公司","ratio": "30.00%","hasChildren": true,"children": []},{"name": "中国石油集团工程有限公司","ratio": "10.00%","hasChildren": true,"children": []},{"name": "中国石油集团资本有限责任公司","ratio": "10.00%","hasChildren": true,"children": []},{"name": "中国石油天然气股份有限公司","ratio": "15.00%","hasChildren": true,"children": []},{"name": "中国石油天然气集团有限公司","ratio": "35.00%","hasChildren": true,"children": []}]},{"name": "高管","nodeType": 0,"children": [{"name": "元红萍","ratio": "['监事']","hasChildren": true,"children": []},{"name": "陈朝晖","ratio": "['负责人']","hasChildren": true,"children": []},{"name": "于海蓉","ratio": "['监事']","hasChildren": true,"children": []},{"name": "张银河","ratio": "['董事']","hasChildren": true,"children": []},{"name": "杨剑锋","ratio": "['董事', '经理']","hasChildren": true,"children": []},{"name": "窦红庆","ratio": "['监事']","hasChildren": true,"children": []},{"name": "陈元鹏","ratio": "['副董事长']","hasChildren": true,"children": []},{"name": "刘晓","ratio": "['监事']","hasChildren": true,"children": []},{"name": "刘哲生","ratio": "['董事']","hasChildren": true,"children": []}]}]}}
}
style
.tree04 {background: #fff;touch-action: none;padding: 0;margin: 0;height: 100%;max-width: 100%;overflow: hidden;font-family: "PingFangSC-Regular", "PingFangSC-Light", "PingFang SC",sans-serif, "Microsoft YaHei";
}
总结
本次开发使得对于d3有一个进一步的了解,虽然主要复杂的功能是参考别人的,但是通过此次使用过后对于一些细枝末节的地方还是可以持续更新优化!
写这个文档主要是为了记录一下过程,并且日后也方便查看。
持续更新。。。
参考网站
svg
d3中文网站
d3.js 实现企业图谱(基于vue)相关推荐
- d3.js实现股权穿透图(vue+d3.js)
d3.js实现股权穿透图 业务需求 1.实现的效果图 2.安装依赖 3.全部代码(复制粘贴即可实现) 参考git地址 业务需求 前段时间写过一篇使用relation-graph插件实现的股权穿透图效果 ...
- vue.js动态计时器_基于Vue.js的Pomodoro技术计时器
vue.js动态计时器 番茄 (pomodoro) Timer for Pomodoro Technique built on Vue.js 2.0 with PWA 基于PWA的Vue.js 2.0 ...
- vue框架可以配合php做企业站,基于Vue和PHP打造前后端分离的通用管理系统(一)...
实用人群 适合小白入门,高手请绕行 技术栈 javascript vuejs webpack babel eslint nodejs axios php mysql redis git vagrant ...
- D3.js 教程: 使用 JavaScript 创建可交互的柱状图
原文链接:D3.js Tutorial: Building Interactive Bar Charts with JavaScript 译者:OFED 最近,我们有幸参与了一个机器学习项目,该项目涉 ...
- 深入理解 D3.js 可视化库之力导向图原理与实现
大厂技术 坚持周更 精选好文 简介 D3.js[1] 是一个基于 web 标准的 JS 可视化库,它借助 SVG.Canvas 和 HTML 进行数据可视化.在数据可视化中,我们很多时候会使用图来 ...
- D3.js树图tree 组织机构分布图(基于vue)
@[TOC](D3.js树图tree 组织机构分布图(基于vue)) 背景 前段时间接到一个需求,仿照企业查查->企业族谱 使用d3画出企业关系树图.这也是本人第一次接触d3.js,总体来说就是 ...
- 基于vue的组织架构树组件_Vue组件基于D3.js布局显示树
基于vue的组织架构树组件 Vue.D3.tree (Vue.D3.tree) Update documentationVue components to display graphics based ...
- 知识图谱可视化vue.js + d3.js
Github 前端 vue.js + d3.js. 需求 上部区域 展示所有的概念类别.以及画布内他们的个数 展示所有关系类别,以及画布内他们的个数 显示所有的概念场景(scenes 字段).关系场景 ...
- 基于原生JS项目使用Vue3 + Surely Vue Table组件
Js & Surely Vue Table 本文主要说明,基于原生JS项目如何使用Surely Vue Table组件. Surely Vue Surely Vue Table 是 Ant D ...
最新文章
- 王豪:HW265到“HW266”
- elasticsearch建立索引操作的API
- nginx内存占用高---内存池使用思考
- Redis 使用 10 个小技巧,请收下!
- div+css的布局方式进行设计成品作业_原创响应式php企业成品网站,清晰风格版
- verilog case语句_浅谈Design Compiler -- Verilog语言结构到门级的映射
- office 2007 oracle 9,office2007win10版下载
- 本地读取服务器Xml文件及本地读本地的xml
- git subtree 管理项目子模块
- mysql 交集_面试官:来说说MySQL的常见优化方案……
- 谁说导航一定要用地图?谷歌DeepMind的强化学习模型靠街景认路
- Golang map 三板斧第一式:快速上手
- python越学越不懂_为什么那么多自学Python的后来都放弃了,总结以下原因
- Spring-外部配置的值是如何通过@Value注解获取的?
- 解决idea使用jdbc连接数据库失败的方法(针对驱动导入失败)
- 情人节能有什么好作品?教你用Scratch写一个!
- 致远OA漏洞学习——A6版本test.jsp 文件上传
- 有效括号 python_1111. 有效括号的嵌套深度
- 单片机入门学习笔记6:新唐单片机N76E003
- 各种OJ网站,刷题必备
热门文章
- 微信的账号连接服务器失败怎么回事,微信无法连接到服务器的原因和6个解决方法...
- 现如今99%的区块链都是空气项目
- 讯飞智能录音笔SR502:七夕值得入手的资料备忘好物
- SQL Round 取整千 整百 整万
- 汽车厂商 API数据接口
- 【agc004e】Salvage Robots
- VS 【】处有未经处理的异常(在【程序名】.exe 中): 0xC00000FD: Stack overflow (参数: 0x00000000,0x008D2000)。
- 驼峰命名法等命名规范
- cocos2d-x 添加自定义字体---中文,英文
- Unity 实现游戏中多相机屏幕CutIn效果