效果图如上 大致效果就是仿照天眼查股权穿透图
曲线会出现节点位置不对(曲线的算法是在不会 技术菜解决不了) 最后刀放到产品脖子他同意用折线代替
在下面已经补充 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)
}
写的不好的地方希望大家指点
评论的 看到我会回复