背景
穿透图谱也出来了,
技术点
使用d3主要需要掌握svg,jquery和d3这几个知识点。
本次使用的是 v3版本的。
案例
company.png实现效果
ct.png实现代码
HTML
<template>
<div class="tree03">
<div class="seeTree-page" id="app">
<div id="box"></div>
</div>
</div>
</template>
JS
<script>
import d3 from "./js/d3";
import $ from "./js/jquery-3.1.1.min";
import ct01 from "./D3JSON/ct01.json";
import checkApi from "@/api/check.js";
export default {
data() {
return {};
},
created() {
localStorage.setItem("ct", this.$route.query.entid);
},
mounted() {
this.tree03();
},
computed: {},
watch: {},
methods: {
tree03() {
var rootName = ""; //根节点的名字
var rootRectWidth = 0; //根节点rect的宽度
var downwardLength = 0;
var upwardLength = 0;
var forUpward = true;
var treeChart = function (d3Object) {
this.d3 = d3Object;
this.directions = ["upward", "downward"];
};
treeChart.prototype.drawChart = async function () {
// First get tree data for both directions.
this.treeData = {};
var self = this;
// d3.json('data.json', function(error, allData) {
try {
let params = {
id: window.localStorage.ct,
};
// console.log(params, "paramsparams");
// self.$toLoading.show();
let res = await checkApi.atlasct(params);
console.log(JSON.stringify(res), "穿透图谱");
// console.log(allData, "原始数据");
let allData = res.code == 0 && res.data;
if(!allData) return;
self.directions.forEach(function (direction) {
self.treeData[direction] = allData[direction];
});
rootName = res.data.rootName;
rootRectWidth = rootName.length * 15;
//获得upward第一级节点的个数
upwardLength = allData.upward.children.length;
//获得downward第一级节点的个数
downwardLength = allData.downward.children.length;
self.graphTree(self.getTreeConfig());
// self.$toLoading.hide();
} catch (error) {
// self.$toLoading.hide();
console.log(error, "error");
}
// });
};
treeChart.prototype.getTreeConfig = function () {
var width = document.body.clientWidth,
height = document.body.clientHeight;
var treeConfig = {
margin: {
top: 0,
right: 20,
bottom: -20,
left: width / 5,
},
};
treeConfig.chartWidth =
$(window).width() - treeConfig.margin.right - treeConfig.margin.left;
treeConfig.chartHeight =
$(window).height() - treeConfig.margin.top - treeConfig.margin.bottom;
treeConfig.centralHeight = treeConfig.chartHeight / 2; //中心坐标
treeConfig.centralWidth = treeConfig.chartWidth / 3; //中心坐标
treeConfig.linkLength = 120;
treeConfig.duration = 500; //动画时间
return treeConfig;
};
treeChart.prototype.graphTree = function (config) {
var self = this;
var d3 = this.d3;
var linkLength = config.linkLength;
var duration = config.duration;
var hasChildNodeArr = [];
var id = 0;
// 曲线-----------start---
// var diagonal = d3.svg.diagonal().source(function(d) {
// // console.log(d)
// return {
// "x": d.source.x,
// "y": d.source.name == 'origin' ? (forUpward ? d.source.y :d.source.y+20 ) : (forUpward ? d.source.y-50: d.source.y+40)
// };
// })
// .target(function(d) {
// return {
// "x": d.target.x,
// // "y": d.target.y,
// "y": d.target.name == 'origin' ? (forUpward ? d.target.y :d.target.y ) : (forUpward ? d.target.y: d.target.y-15)
// };
// })
// .projection(function(d) {
// return [d.x, d.y];
// });
// 曲线----------end----
var diagonal = function (obj) {
//折线
var s = obj.source;
var t = obj.target;
return (
"M" +
s.x +
"," +
s.y +
"L" +
s.x +
"," +
(s.y + (t.y - s.y) / 2) +
"L" +
t.x +
"," +
(s.y + (t.y - s.y) / 2) +
"L" +
t.x +
"," +
t.y
);
};
// 缩放y
var zoom = d3.behavior.zoom().scaleExtent([0.5, 2]).on("zoom", redraw);
var svg = d3
.select("#box")
.append("svg")
.attr("xmlns", "http://www.w3.org/2000/svg")
.attr(
"width",
config.chartWidth + config.margin.right + config.margin.left
)
.attr(
"height",
config.chartHeight + config.margin.top + config.margin.bottom
)
.on("mousedown", disableRightClick)
.call(zoom)
.on("dblclick.zoom", null);
var treeG = svg
.append("g")
.attr("class", "gbox")
.attr(
"transform",
"translate(" +
config.margin.left +
"," +
config.margin.top +
")scale(1)"
);
//箭头(下半部分)
var markerDown = svg
.append("marker")
.attr("id", "resolvedDown")
.attr("markerUnits", "strokeWidth") //设置为strokeWidth箭头会随着线的粗细发生变化
.attr("markerUnits", "userSpaceOnUse")
.attr("viewBox", "0 0 12 12") //坐标系的区域
.attr("refX", 45) //箭头坐标
.attr("refY", 6)
.attr("markerWidth", 12) //标识的大小
.attr("markerHeight", 12)
.attr("orient", "90") //绘制方向,可设定为:auto(自动确认方向)和 角度值
.attr("stroke-width", 2) //箭头宽度
.append("path")
.attr("d", "M2,2 L12,6 L2,10 L4,6 L2,2") //箭头的路径
.attr("fill", "#128BED"); //箭头颜色
//箭头(上半部分)
var markerUp = svg
.append("marker")
.attr("id", "resolvedUp")
.attr("markerUnits", "strokeWidth") //设置为strokeWidth箭头会随着线的粗细发生变化
.attr("markerUnits", "userSpaceOnUse")
.attr("viewBox", "0 0 12 12") //坐标系的区域
.attr("refX", -45) //箭头坐标
.attr("refY", 6)
.attr("markerWidth", 12) //标识的大小
.attr("markerHeight", 12)
.attr("orient", "90") //绘制方向,可设定为:auto(自动确认方向)和 角度值
.attr("stroke-width", 2) //箭头宽度
.append("path")
.attr("d", "M2,2 L12,6 L2,10 L4,6 L2,2") //箭头的路径
.attr("fill", "#128BED"); //箭头颜色
// Initialize the tree nodes and update chart.
for (var d in this.directions) {
var direction = this.directions[d];
var data = self.treeData[direction];
data.x0 = config.centralWidth;
data.y0 = config.centralHeight;
data.children.forEach(collapse);
update(data, data, treeG);
}
function update(source, originalData, g) {
// console.log(source, originalData, g, "update msg");
var direction = originalData["direction"];
forUpward = direction == "upward";
var node_class = direction + "Node";
var link_class = direction + "Link";
var downwardSign = forUpward ? -1 : 1;
var nodeColor = forUpward ? "#D6D6D6" : "#D6D6D6";
var isExpand = false;
var statusUp = true;
var statusDown = true;
var nodeSpace = 180;
var tree = d3.layout.tree().sort(sortByDate).nodeSize([nodeSpace, 0]);
var nodes = tree.nodes(originalData);
var links = tree.links(nodes);
var offsetX = -config.centralWidth;
nodes.forEach(function (d) {
d.y = downwardSign * (d.depth * linkLength) + config.centralHeight;
d.x = d.x - offsetX;
if (d.name == "origin") {
d.x = config.centralWidth;
d.y += downwardSign * 0; // 上下两树图根节点之间的距离
}
});
// Update the node.
var node = g.selectAll("g." + node_class).data(nodes, function (d) {
return d.id || (d.id = ++id);
});
var nodeEnter = node
.enter()
.append("g")
.attr("class", node_class)
.attr("transform", function (d) {
return "translate(" + source.x0 + "," + source.y0 + ")";
})
.style("cursor", function (d) {
return d.name == "origin"
? ""
: d.children || d._children
? "pointer"
: "";
})
.on("click", click);
//画矩形节点
nodeEnter
.append("svg:rect")
.attr("x", function (d) {
return d.name == "origin" ? -(rootRectWidth / 2) : -70;
})
.attr("y", function (d) {
return d.name == "origin" ? -20 : forUpward ? -26 : -30;
})
.attr("width", function (d) {
return d.name == "origin" ? rootRectWidth : 140;
})
.attr("height", function (d) {
return d.name == "origin" ? 40 : 60;
})
.attr("rx", 2)
.style("stroke", function (d) {
return d.name == "origin"
? "rgb(18, 139, 237)"
: "rgb(18, 139, 237)";
})
.style("fill", function (d) {
return d.name == "origin" ? "#0080E3" : "#FFF"; //节点背景色
});
//画圆
nodeEnter.append("circle").attr("r", 1e-6);
nodeEnter
.append("text")
.attr("class", "linkname")
.attr("x", function (d) {
return d.name == "origin" ? "0" : "-55";
})
.attr("dy", function (d) {
return d.name == "origin" ? ".35em" : forUpward ? "-10" : "-10";
})
.attr("text-anchor", function (d) {
return d.name == "origin" ? "middle" : "start";
})
.attr("fill", "#000")
.text(function (d) {
if (d.name == "origin") {
// return ((forUpward) ? '根节点TOP' : '根节点Bottom');
return rootName;
}
if (d.repeated) {
return "[Recurring] " + d.name;
}
return d.name.length > 10 ? d.name.substr(0, 10) : d.name;
})
.style({
"fill-opacity": 1e-6,
fill: function (d) {
if (d.name == "origin") {
return "#fff";
}
},
"font-size": function (d) {
return d.name == "origin" ? 14 : 11;
},
cursor: "pointer",
})
.on("click", function () {});
//添加文字
nodeEnter
.append("text")
.attr("class", "linkname")
.attr("x", "-55")
.attr("dy", function (d) {
return d.name == "origin" ? ".35em" : forUpward ? "8" : "8";
})
.attr("text-anchor", function () {
return d.name == "origin" ? "middle" : "start";
})
.text(function (d) {
return d.name.substr(10, d.name.length);
})
.style({
fill: "#337ab7",
"font-size": function (d) {
return d.name == "origin" ? 14 : 11;
},
cursor: "pointer",
})
.on("click", function () {});
nodeEnter
.append("text")
.attr("x", "-55")
.attr("dy", function (d) {
return d.name == "origin" ? ".35em" : forUpward ? "25" : "23";
})
.attr("text-anchor", "start")
.attr("class", "linkname")
.style("fill", "red")
.style("font-size", 10)
.text(function (d) {
var str = d.name == "origin" ? "" : d.sign || "";
return str.length > 13 ? str.substr(0, 13) + ".." : str;
});
nodeEnter
.append("text")
.attr("x", "10")
.attr("dy", function (d) {
return d.name == "origin" ? ".35em" : forUpward ? "48" : "-33";
})
.attr("text-anchor", "start")
.attr("class", "linkname")
.style("fill", "green")
.style("font-size", 10)
.text(function (d) {
return d.name == "origin" ? "" : d.ratio || "";
});
// Transition nodes to their new position.原有节点更新到新位置
var nodeUpdate = node
.transition()
.duration(duration)
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
nodeUpdate
.select("circle")
.attr("r", function (d) {
return d.name == "origin"
? 0
: hasChildNodeArr.indexOf(d) == -1
? 0
: 6;
})
.attr("cy", function (d) {
return d.name == "origin" ? -20 : forUpward ? -38 : 39;
})
.style("fill", function (d) {
return hasChildNodeArr.indexOf(d) != -1 ? "#fff" : "";
// if (d._children || d.children) { return "#fff"; } else { return "rgba(0,0,0,0)"; }
})
.style("stroke", function (d) {
return hasChildNodeArr.indexOf(d) != -1
? "rgb(18, 139, 237)"
: "";
// if (d._children || d.children) { return "#D6D6D6"; } else { return "rgba(0,0,0,0)"; }
})
.style("fill-opacity", function (d) {
if (d.children) {
return 0.35;
}
})
// Setting summary node style as class as mass style setting is
// not compatible to circles.
.style("stroke-width", function (d) {
if (d.repeated) {
return 5;
}
});
//代表是否展开的+-号
nodeEnter
.append("svg:text")
.attr("class", "isExpand")
.attr("x", "0")
.attr("dy", function (d) {
return forUpward ? -35 : 42;
})
.attr("text-anchor", "middle")
.style("fill", "rgb(18, 139, 237)")
.text(function (d) {
if (d.name == "origin") {
return "";
}
return hasChildNodeArr.indexOf(d) != -1 ? "+" : "";
// if (d._children || d.children) {
// return "+";
// }
});
nodeUpdate.select("text").style("fill-opacity", 1);
//******************************************最终受益人 start******************************************//
//提示框
var tsk = nodeEnter
.append("svg:rect")
.attr("x", -60)
.attr("y", function (d) {
return forUpward ? -86 : -68;
})
.attr("width", function (d) {
if (d.name == "origin") {
return 0;
} else {
return d.hasHumanholding ? 120 : 0; //如果有最终受益人
}
})
.attr("height", 20)
.style("stroke", function (d) {
return "#1078AF";
})
.style("fill", function (d) {
return "#46A2D2";
});
//三角形
nodeEnter
.append("svg:path")
.attr("fill", "#1078AF")
.attr("d", function (d) {
if (d.name == "origin") {
return "";
} else {
return d.hasHumanholding
? forUpward
? "M-60 -66 L-40 -66 L-50 -52 Z"
: "M-60 -48 L-40 -48 L-50 -38 Z"
: ""; //如果有最终受益人
}
});
nodeEnter
.append("svg:text")
.attr("x", "-58")
.attr("dy", function (d) {
return forUpward ? "-73" : "-55";
})
.attr("text-anchor", "start")
.style("fill", "#fff")
.style("font-size", 10)
.text(function (d) {
var str =
"我是最终受益人".length > 6
? "我是最终受益人".substr(0, 6) + ".."
: "我是最终受益人";
return d.hasHumanholding ? "最终受益人:" + str : ""; //如果有最终受益人
});
//******************************************最终受益人 end******************************************//
var nodeExit = node
.exit()
.transition()
.duration(duration)
.attr("transform", function (d) {
return "translate(" + source.x + "," + source.y + ")";
})
.remove();
nodeExit.select("circle").attr("r", 1e-6);
nodeExit.select("text").style("fill-opacity", 1e-6);
var link = g
.selectAll("path." + link_class)
.data(links, function (d) {
return d.target.id;
});
link
.enter()
.insert("path", "g")
.attr("class", link_class)
.attr("stroke", function (d) {
return "#D6D6D6";
})
.attr("fill", "none")
.attr("stroke-width", 0.5)
.attr("d", function (d) {
var o = {
x: source.x0,
y: source.y0,
};
return diagonal({
source: o,
target: o,
});
})
.attr("marker-end", function (d) {
return forUpward ? "url(#resolvedUp)" : "url(#resolvedDown)";
}) //根据箭头标记的id号标记箭头;
.attr("id", function (d, i) {
return "mypath" + i;
});
link.transition().duration(duration).attr("d", diagonal);
link
.exit()
.transition()
.duration(duration)
.attr("d", function (d) {
var o = {
x: source.x,
y: source.y,
};
return diagonal({
source: o,
target: o,
});
})
.remove();
nodes.forEach(function (d) {
d.x0 = d.x;
d.y0 = d.y;
});
async function click(d) {
let params = {
id: d.entid,
parameter: d.parameter,
};
// console.log(params, "穿透参数");
let res = await checkApi.atlasct(params);
let dataChildren = res.code == 0 && res.data;
if (!dataChildren) return;
console.log(dataChildren, "穿透反参");
if (forUpward) {
if (d._children) {
// console.log(dataChildren,"股东--ok");
// if(!dataChildren.length) d.hasChildren = false;
d.children = dataChildren;
collapse(d);
} else {
// if(!dataChildren.length) d.hasChildren = false;
d.children = dataChildren;
// console.log("股东--no");
}
} else {
if (d._children) {
// console.log(dataChildren.length,"对外投资-ok");
// if(!dataChildren.length) d.hasChildren = false;
d.children = dataChildren;
collapse(d);
} else {
// if(!dataChildren.length) d.hasChildren = false;
d.children = dataChildren;
// console.log(dataChildren.length,"对外投资--no");
}
}
isExpand = !isExpand;
if (isExpand) {
d3.select(this).select(".isExpand").text("-");
} else {
d3.select(this).select(".isExpand").text("+");
}
if (d.name == "origin") {
return;
}
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
// expand all if it's the first node
if (d.name == "origin") {
d.children.forEach(expand);
}
}
update(d, originalData, g);
}
}
function expand(d) {
if (d._children) {
d.children = d._children;
d.children.forEach(expand);
d._children = null;
}
}
function collapse(d) {
// let isChildren = d.children && d.children.length;
// console.log(isChildren,"isChildren")
// console.log(d,"是否有子集")
// if(isChildren){
// d.hasChildren = true
// }else{
// d.hasChildren = false
// }
// console.log(d.hasChildren,"是否有子集")
// console.log(hasChildNodeArr,"vhasChildNodeArr")
// if (d.children && d.children.length != 0) {
// 是否有子集
if (d.hasChildren) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
hasChildNodeArr.push(d);
}
// }
}
function redraw() {
treeG.attr(
"transform",
"translate(" +
(d3.event.translate[0] + config.margin.left) +
"," +
(d3.event.translate[1] + config.margin.top) +
")" +
" scale(" +
d3.event.scale * 1 +
")"
);
}
function disableRightClick() {
// stop zoom
if (d3.event.button == 2) {
console.log("No right click allowed");
d3.event.stopImmediatePropagation();
}
}
function sortByDate(a, b) {
var aNum = a.name.substr(a.name.lastIndexOf("(") + 1, 4);
var bNum = b.name.substr(b.name.lastIndexOf("(") + 1, 4);
return (
d3.ascending(aNum, bNum) ||
d3.ascending(a.name, b.name) ||
d3.ascending(a.id, b.id)
);
}
};
var d3GenerationChart = new treeChart(d3);
d3GenerationChart.drawChart();
},
},
};
</script>
style
.tree03 {
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";
}
总结
写这个文档主要是为了记录一下,方便日后查看。
持续更新。。。
网友评论