D3 天眼查 股权穿透 股权结构

效果图如上 大致效果就是仿照天眼查股权穿透图 

曲线会出现节点位置不对(曲线的算法是在不会 技术菜解决不了)   最后刀放到产品脖子他同意用折线代替 

在下面已经补充 JSON数据  把请求换成请求本地json  复制下来自己跑 如果不行 加我qq

写的比较乱 有我没写清楚的地方  加我QQ 2557551314 

2020.6.8 

天眼查 各种d3图均已实现,不在csdn公布 有需要可以加qq,之前自己做的简版的这个图 被发表到个个插件网 恶心到我了。

2019.9.18补

图片导出保存

代码贴在最下方  做了个简版的股权穿透图本地直接能跑起来的 有需要加我

已实现 (拖拽 缩放。 解决元素强制90°横屏touch事件方向问题,pc监听屏幕大小重绘图片,以及SVG图片转img全图下载功能),下面贴代码。

<template>
	<div id="layout">
		<div id="product_tree"></div>
		<!-- <div id='download' @click="downloadfun()">⬇</div> -->
		<div id="rtbtn" onclick="javascript:history.go(-1);">
			<img src="../../assets/rtbtn1.png" alt="">
		</div>
	</div>
</template>
<script>
	let d3 = require('../../../static/js/d3.min.js');
	import $ from 'jquery'
	export default {
		name: 'tree',
		data() {
			let _this = this;
			return {
				proCorpid: this.$route.query.corpid,
				Slide: 0,
				self: '',
				screenWidth: document.body.clientWidth,
				arr: [],
				tree: document.getElementById('product_tree')
			};
		},
		watch: {
			screenWidth(val) { //监听屏幕高宽度 重新绘制图片
				if (!this.timer) {
					this.screenWidth = val
					this.timer = true
					let that = this
					setTimeout(function() {
						// console.log(that.screenWidth)
						let tree = document.getElementById('product_tree')
						tree.style.width = that.screenWidth + "px";
						tree.innerHTML = ''
						that.drawing()
						that.timer = false
					}, 500) //延时
				}
			}
		},
		mounted: function() {
			var _this = this;
			_this.self = _this;
			var tree = document.getElementById('product_tree')

			function resize() { //设置tree的宽度为屏幕的高度 高度为屏幕的宽度
				var html = document.getElementsByTagName('html')[0];
				var width = html.clientWidth;
				var height = html.clientHeight;
				var max = width > height ? width : height;
				var min = width > height ? height : width;
				tree.style.width = max + "px";
				tree.style.height = min + "px";
				tree.style.backgroundColor = "#FFF"
			}
			resize();
			//禁止浏览器body多的部分滚动 若页面内容过长 将不能滑动		
			tree.addEventListener('touchmove', (e) => {
				e.preventDefault();
				e.stopPropagation();
			}, {
				passive: false
			})
			window.addEventListener("resize", resize)
			this.drawing()
			window.onresize = () => { //监听屏幕宽度
				return (() => {
					window.screenWidth = document.body.clientWidth
					_this.screenWidth = window.screenWidth
				})()
			};
		},
		methods: {
			downloadfun() { //canvas图片转img下载功能
				// 				//获取svg内容
				var _this = this;
				var html = document.getElementsByTagName('html')[0]; //获取可视区域宽
				
				var Bwidth = html.clientWidth;//转换屏幕宽高
				var Bheight = html.clientHeight;
				var Bmax = Bwidth > Bheight ? Bwidth : Bheight;
				var Bmin = Bwidth > Bheight ? Bheight : Bwidth;
				

				var canvas = document.createElement("canvas");
				var g = document.getElementsByTagName('g')[0].getBBox()
				var svgbox = $('#product_tree svg')
				var gbox = document.getElementsByClassName('gbox')[0];
				var x = (g.width / 2 - html.clientWidth / 2) //计算偏移位置
				var y = 0;
				g.y < 0 ? y = Math.abs(g.y) : y = 0
				// gbox.style.transform = "translate(" + x + 'px' + "," + (y-60) + "px" + ")  scale(1)"; //偏移位置
				gbox.style.transform = "translate(" + x + 'px' + "," + y + "px" + ")  scale(1)"; //偏移位置
				svgbox.attr('width', g.width)
				svgbox.attr('height', g.height)
				var svg = document.getElementById('product_tree').innerHTML;
				var c = canvas.getContext('2d');
				//新建Image对象
				var img = new Image();
				//svg内容
				img.src = 'data:image/svg+xml,' + unescape(encodeURIComponent(svg)); //svg内容中可以有中文字符
				img.src = 'data:image/svg+xml,' + svg; //svg内容中不能有中文字符
				//图片初始化完成后调用
				var cwidth = g.width;
				var imgfile = null;
				img.onload = function() {
					//将canvas的宽高设置为图像的宽高
					canvas.width = cwidth;
					canvas.height = g.height +60;
					//canvcas画图片
					c.fillStyle = "#fff";
					c.fillRect(0, 0, canvas.width, canvas.height);
					c.drawImage(img, 0, 30);
					var a = document.createElement("a");
					a.download = "fallback";
					a.herf = canvas.toDataURL("image/png");
					imgfile = a.herf
					if(imgfile.length<100){
						alert('图大')
					}else{
						_this.$http.post(_this.BASEURL +'/profund/mail/file',JSON.stringify({
								'image':imgfile
							})).then(res=>{
								console.log(res)
							}).catch(res=>{
								
						})
					}

				}
				//图片转换为base64后 传给后端 发邮件
				
				gbox.style.transform = ''
				svgbox.attr('width', Bmax)
				svgbox.attr('height', Bmin)
			},
			drawing() {
				var  treeData = {};
				var _obj = {};
				var rootName = ''; //根节点的名字
				var selfthis = this.self;
				var ajaxurl = this.BASEURL //请求地址
				var corpid = this.proCorpid;
				var rootRectWidth = 0; //根节点rect的宽度
				var downwardLength = 0,
					upwardLength = 0;
				var tree = document.getElementById('product_tree')
				var Slidenum = this.Slide
				var treeChart = function(d3Object) {
					this.d3 = d3Object;
					this.directions = ['upward', 'downward'];
				};
				treeChart.prototype.drawChart = function() {	
					var newTree = {}
					var self = this;

					selfthis.$http.post(ajaxurl + 'profund/familysearch', JSON.stringify({
						"companyType": 0,
						"corpid": corpid
					})).then(response => {
						var res=response.data;
						if (res.status === 'success') {
							treeData = JSON.parse(JSON.stringify(res.data))		
							console.log(treeData)
							rootName = res.data.downward.name; //获取根节点名称
							rootRectWidth = rootName.length * 16; //设置第一个节点的宽度
							//获得upward第一级节点的个数
							upwardLength = res.data.upward.children.length;
							//获得downward第一级节点的个数
							downwardLength = res.data.downward.children.length;
							self.graphTree(self.getTreeConfig());

						} else if (res.status === 'error' && res.error === 10328) {
							alert('暂无数据')
							history.go(-1)

						}
					});
				};

				treeChart.prototype.getTreeConfig = function() {
					var treeConfig = {
						'margin': {
							'top': 10,
							'right': 5,
							'bottom': 0,
							'left': 30
						}
					}

					treeConfig.chartWidth = tree.clientWidth;
					treeConfig.chartHeight = tree.clientHeight;
					treeConfig.centralHeight = treeConfig.chartHeight / 2;
					treeConfig.centralWidth = treeConfig.chartWidth / 2;
					treeConfig.linkLength = 150;
					treeConfig.duration = 0; //动画时间
					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;

					var diagonal = function(obj) { //折线
						// console.log(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;
					}

					var zoom = d3.behavior.zoom().scaleExtent([0.2, 1.8]).on('zoom', redraw) //缩放

					var svg = d3.select('#product_tree') //定义svg大小
						.append('svg')
						.attr("class", "linkG")

						.attr('xmlns', 'http://www.w3.org/2000/svg')
						.attr('width', config.chartWidth + config.margin.right + config.margin.left)
						// .attr('width',3200)
						.attr('height', config.chartHeight + config.margin.top + config.margin.bottom)
						.on('mousedown', disableRightClick)
						.call(zoom)
						.on('dblclick.zoom', null)
						.on('doubleTap.zoom', null);

					// 					var treeG = svg.append('g') //添加g元素
					// 						.attr('transform', 'translate(' + config.margin.left + ',' + config.margin.top + ')');
					var treeG = svg.append('g') //添加g元素
						.attr('class', 'gbox')
						.attr('transform', 'translate(' + 0 + ',' + 0 + ') scale(1)');
					//初始化树节点并更新图表。
					for (var d = 0; d < this.directions.length; d++) {
						var direction = this.directions[d];
						var data = treeData[direction];

						data.x0 = config.centralWidth;
						data.y0 = config.centralHeight;
						data.children.forEach(collapse);
						update(data, data, treeG);
					}

					function update(source, originalData, g) {
						var direction = originalData['direction'];
						var forUpward = direction == 'upward';
						var node_class = direction + 'Node';
						var link_class = direction + 'Link';
						var downwardSign = (forUpward) ? -1 : 1; //上下树
						var nodeColor = (forUpward) ? '#37592b' : '#8b4513';

						var statusUp = true;
						var statusDown = true;
						var nodeSpace = 140;
						var tree = d3.layout.tree().sort(sortByDate).nodeSize([nodeSpace, 10]);
						var nodes = tree.nodes(originalData);
						var links = tree.links(nodes);
						var offsetX = -config.centralWidth;


						//过滤调不展示的类型  开始 1-风险P2P关联方
						// //('类型字典: ; 2-科创板;3-香港企业;4-上市公司; 
						// 5-事业单位;6-保险公司;7-央企控股;8-国企控股;10-新三板;  12-P2P公司 ...待填写',) 


						nodes.forEach(i => { //遍历所有数据
							i.corptag = i.corptag.filter(item => { //找出数组中要展示的类型其他都过滤掉    返回到原本数组
								return item == '2' || item == '3' || item == '4' || item == '5' || item == '6' || item == '7' || item ==
									'8' || item == '10' ||item =='12'
							})
						})
						//过滤调不展示的类型 开始


						nodes.forEach(function(d) {
							d.y = downwardSign * (d.depth * linkLength) + config.centralHeight;
							d.x = d.x - offsetX;
							if (d.grade == '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('id', function(d) { //给g元素添加id属性
								return 'g' + d.corpid
							})
							.attr('transform', function(d) {
								return 'translate(' + source.x0 + ',' + source.y0 + ')';
							})
							.style('cursor', function(d) {
								return (d.grade == 'origin') ? '' : (d.children || d._children) ? 'pointer' : '';
							})

						// 添加科创等标签开始  
						// 条件 1 标签两字  2标签自身不能折行 多个标签要换换行 
						// 		// 
// 							nodes.map(item => {
// 								return	item.corptag = [7,3,6,12]
// 							}) 
							// 测试时候添加的假数据
												
						//动态添加节点
						d3.selectAll('g').attr('ces', function(d) {

							if (d && d.corpid) { //判断有corpid的节点
								let newId = '#g' + d.corpid
								let arr = d.corptag
								let glist = d3.select(newId)

								//因tree形会绘制两次 
								//声明的全局遍历 判断之前是否同id添加过 如果是false就添加 添加完以后改为true 二次渲染不会再进来
								if (!_obj[d.corpid]) {
									_obj[d.corpid] = true;
									setTimeout(() => {
										for (let i = 0; i < arr.length; i++) { //根据数据量去添加对于元素
											glist.append('rect')
												.attr('class', 'itembox')
												.attr('width', 26)
												.attr('height', 12)
												.style('fill', colorget(arr[i])) //动态设置颜色
												.style('x', getX(i)) //动态设置x
												.style('y', getY(i, forUpward, arr)) //动态设置Y
												.attr('rx', 2)

											glist.append('text')
												.attr('x', arr[i] == 10 ? getX(i) + 1.5 : getX(i) + 5)
												.attr('y', getY(i, forUpward, arr) + 9)
												.text(getName(arr[i]))
												.style('font-size', '7')
												.style('fill', textColor(arr[i]))
												.style('letter-spacing', '1px')
										}
									})
								}

							}
						})

						//('类型标签: 1-风险P2P关联方 ; 2-科创板;3-香港企业;4-上市公司; 
						// 5-事业单位;6-保险公司;7-央企控股;8-国企控股;10-新三板;...待填写',) 
						//标签方法
						function textColor(num) { //标签文字色号
							num = Number.parseInt(num)
							var a = ''
							switch (num) {
								case 2:
									a = '#2954A3' //科创
									break
								case 3:
									a = '#F26A3A' //香港
									break
								case 4:
									a = '#5AA9DE' //上市
									break
								case 5:
									a = '#88B764' //事业
									break
								case 6:
									a = '#815BDE' //保险
									break
								case 7:
									a = '#33DA9F' //央企
									break
								case 8:
									a = '#F0B822' //国企
									break
								case 10:
									a = '#F022A2' //新三板
									break
								case 12:
									a = '#AA2929'//p2p
							}
							return a
						}

						function getName(num) { //标签文本名字
							num = Number.parseInt(num)
							var a = ''
							switch (num) {
								case 2:
									a = '科创' //科创
									break
								case 3:
									a = '香港' //香港
									break
								case 4:
									a = '上市' //上市
									break
								case 5:
									a = '事业' //事业
									break
								case 6:
									a = '保险' //保险
									break
								case 7:
									a = '央企' //央企
									break
								case 8:
									a = '国企' //国企
									break
								case 10:
									a = '新三板' //新三板
									break
								case 12 :
									a = 'P2P'
							}
							return a
						}

						function getY(i, forUpward, listlength) { // 数组下标 方向 以及数组
							let num = 0;
							let a = 0;
							if (listlength) {
								a = listlength.length //获取长度
							} else {
								a = 0
							}

							if (forUpward) { //上
								if (i < 4) {
									num = -15
								} else {
									num = 0
								}
							} else { //下 
								if (a > 4) { //down时候 长度小于4的时候全部-30位置的y
									if (i < 4) { //大于4的时候
										num = -45
									} else {
										num = -30
									}
								} else {
									num = -30
								}
							}
							return num
						}

						function getX(i) { //X位置
							if (i == 0 || i == 4) {
								i = -58
							} else if (i == 1 || i == 5) {
								i = -28
							} else if (i == 2 || i == 6) {
								i = 2
							} else if (i == 3 || i == 7) {
								i = 32
							}

							return i
						}

						function colorget(num) { //颜色库
							num = Number.parseInt(num)
							var a = ''
							switch (num) {
								case 2:
									a = '#E9F3FF' //科创
									break
								case 3:
									a = '#FEECE6' //香港
									break
								case 4:
									a = '#DCF3FF' //上市
									break
								case 5:
									a = '#EBFFDC' //事业
									break
								case 6:
									a = '#EBE2FF' //保险
									break
								case 7:
									a = '#D6FFF1' //央企
									break
								case 8:
									a = '#FFF2CC' //国企
									break
								case 10:
									a = '#FFD9F1' //新三板
									break
								case 12:
									a = '#FFC4C4' //p2p
							}
							return a
						}
						// 添加标签结束
						function addHeight(d) { //添加位置距离整数 
							if (d.corptag.length > 0 && d.corptag.length <= 4) {
								return 15
							} else if (d.corptag.length > 4) {
								return 30
							} else {
								return 0
							}

						}

						function deviation(d) { //矩形以及元素domn方向y轴改变
							let num = 0;
							if (forUpward) {
								num = 0;
							} else {
								if (d.corptag.length > 4) {
									num = -30
								} else if (d.corptag.length <= 4 && d.corptag.length > 0) {
									num = -15
								} else {
									num = 0
								}
							}
							return num
						}

						function transformY(d) { //up方向元素偏移
							let num = 0;
							if (!forUpward) { //down方向时候不动
								num = 0;
							} else {
								if (d.corptag.length > 0 && d.corptag.length <= 4) {
									num = 15
								} else if (d.corptag.length > 4) {
									num = 30
								} else {
									num = 0
								}
							}
							return 'translateY(' + num + 'px' + ')'
						}

						///开始设置节点 
						nodeEnter.append("svg:rect")
							.attr("x", function(d) {
								return (d.grade == 'origin') ? (-rootRectWidth / 2) : -60;
							})
							.attr("y", (d) => {

								return (d.grade == 'origin') ? -20 : forUpward ? -20 : deviation(d) + -20; //有标签时候 down方向的矩形y轴偏移

							})
							.attr("width", (d) => {
								return (d.grade == 'origin') ? rootRectWidth : 120;
							})
							.attr("height", (d) => {
								return (d.grade == 'origin') ? "40" : (d.type == '2') ? 40 : 50 + addHeight(d) //设置节点高度
							})
							.attr("rx", 5) //圆角
							.style("stroke", (d) => {
								return (d.grade == 'origin') ? "rgb(233,233,233)" : "rgb(230,230,230)";
							})
							.style('stroke-width', "1.2")

							.style('fill', (d) => {
								return (d.grade == 'origin') ? "rgb(83,99,187)" : (d.type == '2') ? "rgb(230,234,248)" : "#FFF";
							})



						nodeEnter.append('circle')
							.attr('r', 1e-6)


						nodeEnter.append("text") //上面一层文字
							.attr("class", "linkname")
							.attr("x", function(d) {
								return (d.grade == 'origin') ? '0' : "-55";
							})
							.attr('dy', function(d) {
								return (d.grade == 'origin') ? '.35em' : forUpward ? -5 : -5;
							})
							.attr("text-anchor", function(d) {
								return (d.grade == 'origin') ? 'middle' : "start";
							})
							.text(function(d) {
								if (d.grade == 'origin') {
									// return ((forUpward) ? '根节点TOP' : '根节点Bottom');
									return rootName;
								}
								if (d.repeated) {
									// console.log(d.repeated)
									return '[Recurring] ' + d.name;
								}
								return (d.name.length > 10) ? d.name.substr(0, 10) : d.name;
							})
							.style('cursor', "pointer")
							.style({
								'fill-opacity': 1e-6,

								'fill': function(d) {
									return (d.grade == 'origin') ? '#fff' : getcolor(d);
								},
								'font-size': function(d) {
									return (d.grade == 'origin') ? 14 : 11;
								}
							})
							.style("letter-spacing", (d) => {
								return (d.grade == 'origin') ? "1px" : '0'
							})
							.style('transform', transformY) //up方向偏移方法
							.on('click', routeType)

						nodeEnter.append("text") //中间一层 上一层的折行
							.attr("class", "linkname")
							.attr("x", "-55")
							.attr("dy", function(d) {
								return (d.grade == 'origin') ? '.35em' : forUpward ? (d.type != 2) ? "8" : "0" : '8';
							})
							.attr("text-anchor", function() {
								return (d.grade == 'origin') ? 'middle' : "start";
							})
							.text(function(d) {
								return (d.grade == 'origin') ? '' : d.name.length > 17 ? d.name.substr(10, 7) + '...' : d.name.substr(10,
									7);
							})
							.style({
								// 'fill': "#337ab7",
								'font-size': function(d) {
									return (d.grade == 'origin') ? 14 : 11;
								},
								'fill': function(d) {
									return (d.grade == 'origin') ? '#fff' : getcolor(d);
								},
								'cursor': "pointer"
							})
							.style('transform', transformY) //up方向偏移方法
							.on('click', routeType) //路由跳转

						nodeEnter.append("text") //认缴金额一层
							.attr("x", "-55")
							.attr("dy", function(d) {
								return (d.grade == 'origin') ? '.35em' : forUpward ? ((d.type == 2) ? "10" : "24") : '23';
							})
							.attr("text-anchor", "start")
							.attr("class", "linkname")
							.style("fill", "#000")
							.style('font-size', 10)
							.text(function(d) {
								var str = (d.grade == 'origin') ? '' : (d.amount != 0 && d.amount != null) ? "认缴金额:" + d.amount + "万元" :
									"认缴金额:未公开"; //金额
								return (str.length > 14) ? str.substr(0, 14) + ".." : str;
							})
							.style('transform', transformY) //up方向偏移方法


						nodeEnter.append("text") //股比
							.attr("x", "10")
							.attr("dy", function(d) {
								return (d.grade == 'origin') ? '.35em' : forUpward ? ((d.type == 2) ? "32" : "42") : deviation(d) + -23;
							})
							.attr("text-anchor", "start")
							.attr("class", "linkname")
							.style("fill", "rgb(0,132,255)")
							.style('font-size', 10)
							.text(function(d) {
								return (d.grade == 'origin') ? "" : (d.proportion) ? d.proportion + '%' : ''; //股比
							})
							.style('transform', transformY) //up方向偏移方法 

						// 原有节点更新到新位置
						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.grade == 'origin') ? 0 : (d.number > 0) ? 6 : 0;
							})
							.attr('cy', function(d) {
								return (d.grade == 'origin') ? -20 : (forUpward) ? -28 : 38;
							})
							.style('fill', function(d) {
								return d.number > 0 ? "rgb(83,99,187)" : "";
							})

						//代表是否展开的+-号

						nodeEnter.append("svg:text")
							.attr("class", "isExpand")
							.attr("x", "0")
							.attr("dy", function(d) {
								return forUpward ? -22 : 44;
							})
							.attr("text-anchor", "middle")
							.style("fill", "#fff")

							.style('font-size', '18px')
							.text(function(d) {
								if (d.grade == 'origin') {
									return '';
								}
								return d.number > 0 ? "+" : "";
							})
							.on('click', click)

						nodeUpdate.select('text').style('fill-opacity', 1)
						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;
							})
							.attr('fill', 'none')


						//******************************************私募管理人标签 start******************************************//
						//提示框
						var tsk = nodeEnter.append("svg:rect")
							.attr("x", -64)
							.attr("y", function(d) {
								return forUpward ? -42 : -42 + deviation(d);
							})
							.attr('rx', 2)
							.attr('ry', 2)
							.attr("width", function(d) {
								if (d.name == 'origin') {
									return 0
								} else {
									return (d.shareholdertype == 'GP') ? "55" : d.corptype == 1 ? '48' : d.prodid ? '40' : '0' //私募管理人标签
								}
							})
							.attr("height", 15)

							.style("fill", function(d) {
								return (d.shareholdertype == 'GP') ? "#0073ff" : d.corptype == 1 ? '#ffbc00' : d.prodid ? '#2ca111' : ''
							})
						//三角形
						nodeEnter.append("svg:path")
							.attr("fill", (d) => {
								return (d.shareholdertype == 'GP') ? "#0073ff" : d.corptype == 1 ? '#ffbc00' : d.prodid ? '#2ca111' : ''
							})
							.attr("d", function(d) {
								if (d.name == 'origin') {
									return ""
								} else {
									return (d.shareholdertype == 'GP' || d.corptype == 1 || d.prodid) ? "M-53 -20 L-60 -30 L-46 -30 Z" : ""; //私募管理人标签
								}
							})
							.style('transform', (d) => { //为down时候 看是否存在标签 更改三角定位 使用translatey做y轴偏移
								let num = 0;
								if (forUpward) {
									num = 0;
								} else {
									if (d.corptag.length > 0 && d.corptag.length <= 4) {
										num = -15
									} else if (d.corptag.length > 4) {
										num = -30
									} else {
										num = 0
									}
								}
								return 'translateY(' + num + 'px' + ')'
							})

						nodeEnter.append("svg:text") //三角形中的文字
							.attr("x", -60)
							.attr("dy", function(d) {
								return forUpward ? "-32" : -32 + deviation(d);
							})
							.attr("text-anchor", "start")
							.style("fill", "#fff")
							.style("font-size", 7)
							.style('letter-spacing', 1)
							.text(function(d) {
								// console.log(d)
								return (d.shareholdertype == 'GP') ? "本基金管理人" : d.corptype == 1 ? '私募管理人' : d.prodid ? '私募产品' : ''
							});
						//******************************************私募管理人标签 end******************************************//

						link.enter().insert('path', 'g')
							.attr('class', link_class)
							.attr('d', function(d) {
								var o = {
									x: source.x0,
									y: source.y0,
								};
								return diagonal({
									source: o,
									target: o,
								});

							})
							.attr("id", function(d, i) {
								return "mypath" + i;
							})
							.attr('fill', 'none')
							.attr('stroke', 'rgb(51, 51, 51)')


						link.transition()
							.duration(duration)
							.attr('d', diagonal)


						link.exit().transition()
							.duration(duration)
							.attr('d', function(d) {
								var o = {
									x: source.x0,
									y: source.y0,
								};
								return diagonal({
									source: o,
									target: o,
								});
							})
							.remove();
						nodes.forEach(function(d) {
							d.x0 = d.x;
							d.y0 = d.y;
						});

					function routeType(d) { //路由跳转 
							//'产品类型:1-私募基金;2-基金专户;3-证券公司资管计划;4-期货公司资管计划;5-信托计划;6-保险公司资管计划;7-银行理财计划;8-券商直投基金;9-资产支持专项计划(ABS);10-公募基金;',
							// 机构类型字典 '1-私募管理人;2-期货公司;3-证券公司;4-基金子公司;5-信托公司;6-银行;7-公募基金公司;8-上市公司;9-新三板上市公司;10-律师事务所;11-会计师事务所;12-支付机构;99-其他机构;', 
							console.log(window.location.href)
							// d为点击的当前元素 
							if (d.corpid && d.corptype == '1') {
								window.location.href = '/ManagersDetails' + '?corpid=' + d.corpid
								return false
							} else if (d.prodid) {
								window.location.href = '/ProBaseInfo' + '?prodid=' + d.prodid
								return false
							} else {
								return false
							}
						}

					function getcolor(d) { //设置跳转颜色区分 
							let Color = ''
							if (d.corpid && d.corptype == '1') {
								Color = '#0066cc'
							} else if (d.prodid) {
								Color = '#0066cc'
							} else {
								Color = '#333333'
							}
							return Color
						}

						function getNode(type, id) {

							selfthis.$http.post(ajaxurl + 'profund/familysearch', JSON.stringify(comment)).then(response => {
								var res = response.data;

								if (res.status === 'success') {
									if (type == 1) {
										mynodes = res.data.upward
									} else if (type == -1) {
										mynodes = res.data.downward
									}
								}
							})
							return mynodes;
						}

					function click(d) {
							var companyType //请求参数
							if (forUpward) { //定义请求参数的值 1 = 股东      -1 = 对外投资
								companyType = 1; //参数类型
								_obj = {}//重新渲染前 清空 判断标签的条件 
							} else {
								companyType = -1; //参数类型
							}
							if (d.children) {
								d._children = d.children;
								d.children = null;			
								
								update(d, originalData, g); //重新渲染数据
								d3.select(this).text("+").style({
									'font-weight': '400',
									'font-size': '18px',
									
								})
								
							} else {
								
								d.children = d._children;
								d._children = null;
								selfthis.$http.post(ajaxurl + 'profund/familysearch', JSON.stringify({
									'companyType': companyType,
									'corpid': d.corpid
								})).then(response => {

									var res = response.data;
									if (res.status === 'success') {
										if (companyType == 1) {
											d.children = res.data.upward.children;
										} else if (companyType == -1) {
											d.children = res.data.downward.children;
										}
										
										update(d, originalData, g); //重新渲染数据
										d3.select(this).text("-").style({
											'font-weight': '600',
											'font-size': '23px'
										})
										// expand all if it's the first node
										if (d.name == 'origin') {
											d.children.forEach(expand);
										}
									}
								})



							}

						}
					}

					function expand(d) {
						if (d._children) {
							d.children = d._children;
							d.children.forEach(expand);
							d._children = null;
						}
					}


					function collapse(d) {
						if (d.children && d.children.length != 0) {
							d._children = d.children;
							d._children.forEach(collapse);
							d.children = null;
							hasChildNodeArr.push(d);
						}
					}

					function redraw() {
						//强制横屏后 手机端出现touch事件方向 
						//错误问题 以及ios 安卓兼容性问题
						//根据浏览器内核判断手机和pc 重新计算方向 顺时针九十度x,y规则 a[0] = b[1]  a[1] = -b[0]
						var html = document.getElementsByTagName('html')[0];
						var width = html.clientWidth;
						var height = html.clientHeight;
						var a = []
						var u = navigator.userAgent;
						var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android终端
						var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
						if (isAndroid) {
							a = d3.event.translate; //是安卓
						} else if (isiOS) {
							if (window.orientation == 90 || window.orientation == -90) { //判断屏幕方向
								// console.log("横屏")
								a = d3.event.translate;
							} else {
								var b = d3.event.translate;
								a[0] = b[1]
								a[1] = -b[0]
							}
						} else { //pc端
							if (width < height) {
								var b = d3.event.translate;
								a[0] = b[1]
								a[1] = -b[0]
							} else {
								a = d3.event.translate;
							}

						}

						treeG.attr('transform', 'translate(' + a + ')' +
							' scale(' + d3.event.scale + ')');
					}


					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 src="../../css/producttree.css" scoped="scoped"></style>

下面贴css

#product_tree {
	touch-action: none;
	z-index: 999;

}

#rtbtn {
	position: fixed;
	width: 25px;
	height: 25px;
	top: 20px;
	border-radius: 4px;
	box-sizing: content-box;
	background: #fff;
	border: 1px solid rgb(83, 99, 187);
	z-index: 9999;
	text-align: center;
	right: 30px;
}

#rtbtn img {
	width: 26px;
}

#download {
	position: fixed;
	width: 25px;
	height: 25px;
	top: 20px;
	border-radius: 4px;
	background: #fff;
	border: 1px solid rgb(83, 99, 187);
	z-index: 9999;
	text-align: center;
	right: 80px;
}

@media screen and (orientation:portrait) {

	/*屏幕旋转90*/
	#product_tree {
		transform-origin: 0 0;
		transform: rotateZ(90deg) translateY(-100%);
	}
}

#product_tree>.centralText {
	font: 23spx sans-serif;
	fill: #222;
}

#product_tree>.downwardNode text,
#product_tree>.upwardNode text {
	font: 15px sans-serif;
}

json数据格式 

{
    "data": {
        "downward": {
            "children": [
                {
                    "number": 0,
                    "amount": 0.0000,
                    "proportion": null,
                    "corpid": 9510033851,
                    "corptype": 95,
                    "name": "璞盈(上海)资产管理合伙企业(有限合伙)",
                    "prodid": null,
                    "corptag": []
                }
            ],
            "grade": "origin",
            "name": "上海璞盈投资管理有限公司",
            "corptag": [],
            "direction": "downward"
        },
        "upward": {
            "children": [
                {
                    "number": 0,
                    "amount": 300.0000,
                    "proportion": 30.00,
                    "corpid": null,
                    "corptype": null,
                    "name": "陈其平",
                    "shareholdertype": "GENERAL",
                    "prodid": null,
                    "type": "2",
                    "corptag": []
                },
                {
                    "number": 0,
                    "amount": 600.0000,
                    "proportion": 60.00,
                    "corpid": null,
                    "corptype": null,
                    "name": "杨淋",
                    "shareholdertype": "GENERAL",
                    "prodid": null,
                    "type": "2",
                    "corptag": []
                },
                {
                    "number": 0,
                    "amount": 100.0000,
                    "proportion": 10.00,
                    "corpid": null,
                    "corptype": null,
                    "name": "刘俊伟",
                    "shareholdertype": "GENERAL",
                    "prodid": null,
                    "type": "2",
                    "corptag": []
                }
            ],
            "grade": "origin",
            "name": "上海璞盈投资管理有限公司",
            "corptag": [],
            "direction": "upward"
        }
    },
    "status": "success"
}

此处贴下载方法 2019.9.18更

const down = document.getElementById('downLond');
			down.onclick = function(){
				var _this = this;
				var html = document.getElementsByTagName('html')[0]; //获取可视区域宽
        		var g = document.getElementsByTagName('g')[0].getBBox()
				var gbox = document.getElementsByClassName('gbox')[0];
				var x = -g.x;//计算偏移位置
				var y = -g.y;
				// g.y < 0 ? y = Math.abs(g.y) : y = 0
				// gbox.style.transform = "translate(" + x + 'px' + "," + (y-60) + "px" + ")  scale(1)"; //偏移位置
				gbox.style.transform = "translate(" + x + 'px' + "," + y + "px" + ")  scale(1)"; //偏移位置
				var svgbox = $('#box svg')
				var boxwidth = svgbox.width;
				var boxheight = svgbox.height;
				svgbox.attr('width', g.width)
				svgbox.attr('height', g.height)
				var canvas = document.createElement("canvas");
				var c = canvas.getContext('2d');
				//新建Image对象
				//svg内容
				var svg = document.getElementById('box').innerHTML;

				var img = new Image();
				
				img.src = 'data:image/svg+xml,' + unescape(encodeURIComponent(svg));//svg内容中可以有中文字符
				img.src = 'data:image/svg+xml,' + svg;//svg内容中不能有中文字符
				img.src = 'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(svg)));//svg内容中可以有中文字符

				//图片初始化完成后调用
				 img.onload = function() {
					//将canvas的宽高设置为图像的宽高
					canvas.width = img.width;
					canvas.height = img.height+60;
					c.fillStyle = "#fff";
					c.fillRect(0, 0, canvas.width, canvas.height);
					c.drawImage(img, 0, 30);
					//canvas画图片

					var a = document.createElement("a");
					a.download = "png";
					a.href= canvas.toDataURL("image/png");
					a.click();
				}
				//图片转换为base64后 传给后端 发邮件	
				gbox.style.transform = ''
				svgbox.attr('width',boxwidth)
				svgbox.attr('height',boxheight)
			}

写的不好的地方希望大家指点

评论的 看到我会回复 

评论 72
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值