使用d3制作上下结构的股权穿透图

<template>
  <div id="borrow">
    <div id="Main">
      <div class="tupu" id="tupu">
        <div class="tupu-toolbar">
          <ul>
            <li id="zoomOut">
              <span class="big"></span>放大
            </li>
            <li id="zoomIn">
              <span class="small"></span>缩小
            </li>
            <li id="reset">
              <span class="refresh"></span>重置
            </li>
            <li @click="toggleFullScreen($event)">
              <span class="screen"></span>
              {{isFullscreen?"全屏":"退出"}}
            </li>
            <li id="TrSave" @click="downloadImpByChart('股权穿透图')">
              <span class="save"></span>保存
            </li>
          </ul>
        </div>
        <div id="mountNode"></div>
      </div>
    </div>
    <div class="back" @click="onJumpReport">
      <!-- <img src="~@/assets/relation/back.svg" alt /> -->
      {{companyName}}
    </div>
    <div class="back2 back" @click="onJumpReport">
      <img src="~@/assets/relation/back.svg" alt />返回
    </div>
  </div>
</template>

  

export default {
    components: {},
    mixins: [D3Mixin],
    name: 'relation2',
    data() {
        return {
            companyName: this.$route.query.companyName,
            isFullscreen: true,
            rootData: {
                "downward": {
                    "direction": "down",
                    "name": "origin",
                    "children": [
                        {
                            "name": "...有限公司",
                            "amount": "100",
                            "ratio": "55%",
                            "hasHumanholding":true,
                            "hasChildren":true,
                            "isExpand": false,
                            "type": 2,
                            "hasNode": 1,
                            "children": [
                            {
                                "name": "公司名字",
                                "hasHumanholding":false,
                                "hasChildren":true,
                                "amount": "100",
                                "isHoldingCompany": true,
                                "ratio": "55%",
                                "type": 2,
                                "children": []
                            },
                            {
                                "name": "公司名字",
                                "hasHumanholding":false,
                                "hasChildren":true,
                                "amount": "100",
                                "isHoldingCompany": true,
                                "ratio": "55%",
                                "type": 2,
                                "children": []
                            }
                            ]
                        },
                        {
                            "name": "...有限公司11",
                            "amount": "100",
                            "isHoldingCompany": true,
                            "ratio": "55%",
                            "hasHumanholding":true,
                            "hasChildren":true,
                            "isExpand": false,
                            "type": 2,
                            "hasNode": 1,
                            "children": [
                                {
                                    "name": "公司名字",
                                    "hasHumanholding":false,
                                    "hasChildren":true,
                                    "amount": "100",
                                    "isHoldingCompany": true,
                                    "ratio": "55%",
                                    "type": 2,
                                    "children": []
                                },
                                {
                                    "name": "公司名字",
                                    "hasHumanholding":false,
                                    "hasChildren":true,
                                    "amount": "100",
                                    "isHoldingCompany": true,
                                    "ratio": "55%",
                                    "type": 2,
                                    "children": []
                                }
                            ]
                        },
                        {
                            "name": "...有限公司",
                            "amount": "100",
                            "isHoldingCompany": true,
                            "ratio": "55%",
                            "hasHumanholding":true,
                            "hasChildren":true,
                            "isExpand": false,
                            "type": 2,
                            "hasNode": 1,
                            "children": [
                                {
                                    "name": "公司名字",
                                    "hasHumanholding":false,
                                    "hasChildren":true,
                                    "amount": "100",
                                    "isHoldingCompany": true,
                                    "ratio": "55%",
                                    "type": 2,
                                    "children": []
                                },
                                {
                                    "name": "公司名字",
                                    "hasHumanholding":false,
                                    "hasChildren":true,
                                    "amount": "100",
                                    "ratio": "55%",
                                    "type": 2,
                                    "children": []
                                }
                            ]
                        },
                        {
                            "name": "...有限公司",
                            "hasHumanholding":false,
                            "hasChildren":true,
                            "amount": "100",
                            "ratio": "55%",
                            "type": 2,
                            "children": []
                        },
                        {
                            "name": "...有限公司",
                            "hasHumanholding":false,
                            "hasChildren":true,
                            "isExpand": false,
                            "amount": "100",
                            "ratio": "55%",
                            "type": 2,
                            "hasNode": 1,
                            "children": [
                                {
                                    "name": "公司或股东名字",
                                    "hasHumanholding":false,
                                    "amount": "100",
                                    "ratio": "55%",
                                    "type": 2,
                                    "children": []
                                },
                                {
                                    "name": "公司或股东名字",
                                    "hasHumanholding":false,
                                    "amount": "100",
                                    "ratio": "55%",
                                    "type": 2,
                                    "children": []
                                },
                                {
                                    "name": "公司或股东名字",
                                    "hasHumanholding":false,
                                    "amount": "100",
                                    "ratio": "55%",
                                    "type": 2,
                                    "children": []
                                },
                                {
                                    "name": "公司或股东名字",
                                    "hasHumanholding":false,
                                    "amount": "100",
                                    "ratio": "55%",
                                    "type": 2,
                                    "children": []
                                }
                            ]
                        }
                    ]
                },
                "upward": {
                    "direction": "up",
                    "name": "origin",
                    "children": [
                    {
                        "name": "...(有限合伙)",
                        "hasHumanholding":false,
                        "amount": "100",
                        "isHoldingCompany": true,
                        "ratio": "55%",
                        "type": 2,
                    },
                    {
                        "name": "...(有限合伙)",
                        "hasHumanholding":false,
                        "amount": "100",
                        "isHoldingCompany": true,
                        "ratio": "55%",
                        "type": 2,
                        "hasNode": 1,
                        "children": [
                        {
                            "name": "公司或股东名字",
                            "hasHumanholding":false,
                            "amount": "100",
                            "isHoldingCompany": true,
                            "ratio": "55%",
                            "type": 1,
                            "children": [],
                            "isFHolder": true,
                            "holderPercent": '5%',
                        },
                        {
                            "name": "公司或股东名字",
                            "hasHumanholding":false,
                            "isExpand": false,
                            "amount": "100",
                            "isHoldingCompany": true,
                            "ratio": "55%",
                            "type": 2,
                            "hasNode": 1,
                            "children": [
                            {
                                "name": "公司或股东名字",
                                "hasHumanholding":false,
                                "amount": "100",
                                "isHoldingCompany": true,
                                "ratio": "55%",
                                "type": 1,
                                "isFHolder": true,
                                "isActualController": true,
                                "holderPercent": '95%',
                                "children": []
                            },
                            {
                                "name": "公司或股东名字",
                                "hasHumanholding":false,
                                "amount": "100",
                                "ratio": "55%",
                                "type": 2,
                                "children": []
                            }
                            ]
                        },
                        {
                            "name": "公司或股东名字",
                            "hasHumanholding":false,
                            "amount": "100",
                            "ratio": "55%",
                            "type": 1,
                            "children": []
                        }
                        ]
                    }
                    ]
                }
            },
            rootName: '...有限公司',
            // beijingCrid: '98682337095518809'
            beijingCrid: this.$route.query.companyId ? decodeURIComponent(this.$route.query.companyId) : '98682337095519887'
        }
    },
    created () {
        
    },
    mounted () {
        this.getInitData()
        // this.drawing()
        window.addEventListener('resize',function(){
            const svg = document.getElementById('svg')
            svg.setAttribute('height',window.innerHeight)
        })
    },
    methods: {
        getInitData () {
            gqctt({
                beijingCrid: this.beijingCrid,
                direction: ''
            }).then(res => {
                if (res.code === 0) {
                    this.rootData.upward.children = res.result.investorList
                    this.rootData.downward.children = res.result.holderList
                    this.rootData.upward.children.map(item => {
                        item.amount = Number(item.subConAm).toFixed(2)
                        
                        item.ratio = item.subComBl.length > 6 ? 
                            calculation.accMul(item.subComBl,100).toFixed(2) + '%' : 
                            calculation.accMul(item.subComBl,100) + '%'
                      
                        item.name = item.entName
                        item.type = item.bz === '企业' ? 2 : 1
                        item.isHoldingCompany = item.isHolding == 1 ? true : false
                        item.isFHolder = item.subComBl >= 0.25 && item.type == 1
                        item.holderPercent = item.ratio
                        item.direction = 'up'
                    })
                    this.rootData.downward.children.map(item => {
                        item.amount = Number(item.subConAm).toFixed(2)
                       
                        item.ratio = item.subComBl.length > 6 ? 
                            calculation.accMul(item.subComBl,100).toFixed(2) + '%' : 
                            calculation.accMul(item.subComBl,100) + '%'
                       
                        item.name = item.pentName
                        item.type = item.bz === '企业' ? 2 : 1
                        item.direction = 'down'
                    })
                    this.rootName = res.result.name
                    this.drawing()
                    console.log(this.rootData)
                }
            })
        },
        addChildren (obj,beijingCrid,direction,cb) {
            gqctt({
                beijingCrid: beijingCrid,
                direction: direction
            }).then(res => {
                if (res.code === 0) {
                    if (direction == 'up') {
                        obj.children = res.result.investorList
                        obj.children.map(item => {
                            item.amount = Number(item.subConAm).toFixed(2)
                            item.isFHolder = true
                       
                            item.ratio = item.subComBl.length > 6 ? 
                            calculation.accMul(item.subComBl,100).toFixed(2) + '%' : 
                            calculation.accMul(item.subComBl,100) + '%'
                          
                            item.name = item.entName
                            item.type = item.bz === '企业' ? 2 : 1
                            item.isHoldingCompany = item.isHolding == 1 ? true : false
                            item.isFHolder = item.subComBl >= 0.25 && item.type == 1
                            item.direction = direction
                            item.holderPercent = item.ratio
                        })
                    } else {
                        obj.children = res.result.holderList
                            obj.children.map(item => {
                            item.amount = Number(item.subConAm).toFixed(2)
                            item.isFHolder = true

                            item.ratio = item.subComBl.length > 6 ? 
                            calculation.accMul(item.subComBl,100).toFixed(2) + '%' : 
                            calculation.accMul(item.subComBl,100) + '%'
              
                            item.name = item.pentName
                            item.type = item.bz === '企业' ? 2 : 1
                            item.isHoldingCompany = item.isHolding == 1 ? true : false
                            item.isFHolder = item.subComBl >= 0.25 && item.type == 1
                            item.direction = direction
                            item.holderPercent = item.ratio
                        })
                    }
                    
                    cb()
                }
            })
        },
        drawing () {
            const width = document.getElementById('mountNode').scrollWidth
            const height = document.getElementById('mountNode').scrollHeight || 600
            const strokeColor = {
                isActualController: '#FBC6C3',
                isFHolder: '#F3DDB6',
                person: '#0084FF',
                default: '#CCC'
            }
            const lineColor = {
                isActualController: '#FA6B64',
                isFHolder: '#F1B03A',
                person: '#0084FF',
                default: '#919191'
            }
            const fillColor = {
                isActualController: "#FFFBFB",
                isFHolder: '#FEFBF3',
                person: '#F5FAFF',
                default: '#FFF'
            }
            var _this = this;
            // var rootName = ''; //根节点的名字
            var rootRectWidth = 0; //根节点rect的宽度
            var downwardLength = 0,
            upwardLength = 0;
            var forUpward = true

            var treeChart = function(d3Object) {
                this.d3 = d3Object;
                this.directions = ['upward', 'downward'];
            };
 
 
            treeChart.prototype.drawChart = function() {
                // First get tree data for both directions.
                this.treeData = {};
                var self = this;
                self.directions.forEach(function(direction) {
                    self.treeData[direction] = _this.rootData[direction];
                });
                // rootName = '上海冰鉴信息科技有限公司';
                rootRectWidth = _this.rootName.length * 15 + 60;
                //获得upward第一级节点的个数
                upwardLength = _this.rootData.upward.children.length;
                //获得downward第一级节点的个数
                downwardLength = _this.rootData.downward.children.length;
                self.graphTree(self.getTreeConfig());
            };
            treeChart.prototype.getTreeConfig = function() {
                var treeConfig = {
                    'margin': {
                        'top': 10,
                        'right': 5,
                        'bottom': 0,
                        'left': 30
                    }
                }
                treeConfig.chartWidth = (width - treeConfig.margin.right - treeConfig.margin.left);
                treeConfig.chartHeight = (height - treeConfig.margin.top - treeConfig.margin.bottom);
                treeConfig.centralHeight = treeConfig.chartHeight / 2;
                treeConfig.centralWidth = treeConfig.chartWidth / 2;
                treeConfig.linkLength = 160;
                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;
                var diagonal = function(obj) {
                    //折线

                    var s = obj.source;
                    var t = obj.target;
                    let multiplier = s.x > t.x ? -1 : s.x < t.x ? 1 : 0
                    let path = s.y > t.y ? (s.y / 3 + t.y * 2 / 3) : (s.y / 3 + t.y * 2 / 3 + 15)
                    return (
                        "M" +
                        s.x +
                        "," +
                        (s.y + 12) +
                        "L" +
                        s.x +
                        "," +
                        path +
                        "L" +
                        t.x +
                        "," +
                        path +
                        // pathA+
                        "L" +
                        t.x +
                        "," +
                        t.y
                    );
                };
                var zoom = d3.behavior.zoom()
                    .scaleExtent([0.5, 2])
                    .on('zoom', redraw);

                var svg = d3.select('#mountNode')
                    .append('svg')
                    .attr('id','svg')
                    .attr('width', config.chartWidth + config.margin.right + config.margin.left)
                    .attr('height', config.chartHeight + config.margin.top + config.margin.bottom)
                    .attr('xmlns','http://www.w3.org/2000/svg')
                    .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 + ')');
                
                d3.select("#reset").on("click", function(d){
                    interpolateZoom([0, 0], 1);
                });
                function interpolateZoom(translate, scale) {
                    var self = this;
                    return d3
                        .transition()
                        .duration(350)
                        .tween("zoom", function() {
                            var iTranslate = d3.interpolate(zoom.translate(), translate),
                                iScale = d3.interpolate(zoom.scale(), scale);
                            return function(t) {
                                zoom.scale(iScale(t)).translate(iTranslate(t));
                                redraw();
                            };
                        });
                }

                function zoomClick() {
                    var clicked = d3.event.target,
                        direction = 1,
                        factor = 0.2,
                        target_zoom = 1,
                        center = [width / 2, height / 2],
                        extent = zoom.scaleExtent(),
                        translate = zoom.translate(),
                        translate0 = [],
                        l = [],
                        view = { x: translate[0], y: translate[1], k: zoom.scale() };

                    d3.event.preventDefault();
                    direction = this.id === "zoomOut" ? 1 : -1;

                    target_zoom = Number(zoom.scale()  + factor * direction).toFixed(1)
          
                    if (target_zoom === extent[0] || target_zoom === extent[1]) {
                        return false
                    }
                    if (target_zoom < extent[0]) {
                        target_zoom = extent[0]
                    }
                    if (target_zoom > extent[1]) {
                        target_zoom = extent[1]
                    }
                    translate0 = [(center[0] - view.x) / view.k, (center[1] - view.y) / view.k];
                    view.k = target_zoom;
                    l = [translate0[0] * view.k + view.x, translate0[1] * view.k + view.y];

                    view.x += center[0] - l[0];
                    view.y += center[1] - l[1];

                    interpolateZoom([view.x, view.y], view.k);
                }
                d3.select("#zoomIn").on("click", zoomClick);
                d3.select("#zoomOut").on("click", zoomClick);
                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) {
                    var direction = originalData['direction'];
                    forUpward = direction == 'up';
                    var node_class = direction + 'Node';
                    var link_class = direction + 'Link';
                    var downwardSign = (forUpward) ? -1 : 1;
                    var nodeColor = (forUpward) ? '#37592b' : '#8b4513';
            
                    var isExpand = false;
                    var statusUp = true;
                    var statusDown = true;
                    var nodeSpace = 210;
                    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; // 上下两树图根节点之间的距离
                        }
                    });

                    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',d => {
                            if (d.type == 2) {
                                if ((d.direction == 'up' && d.beijingCrid) || (d.direction == 'down' && d.pbeijingCrid)) {
                                    _this.$router.push({
                                        path: '/search/detail',
                                        name: 'search-detail',
                                        query: {
                                            companyId: d.direction == 'up' ? d.beijingCrid : d.pbeijingCrid
                                        }
                                    })
                                }
                            }
                        })
                        .on('mouseover',d => {
                            if (d.name === 'origin') {
                                return false
                            }
                            let hoverLinkArr = [d.id]
                            let sColor = strokeColor.default
                            let lColor = lineColor.default
                            if (d.isActualController) {
                                sColor = strokeColor.isActualController
                                lColor = lineColor.isActualController
                            } else if (d.isFHolder) {
                                sColor = strokeColor.isFHolder
                                lColor = lineColor.isFHolder
                            } else {
                                sColor = strokeColor.person
                                lColor = lineColor.person
                            }
                            getRelationLink(d.id,lColor)
                            d3.selectAll('marker#resolved'+d.id).selectAll('path').attr('fill',lColor)
                            let link
                            if (d.direction == 'up') {
                                link = d3.selectAll('.upLink')
                            } else {
                                link = d3.selectAll('.downLink')
                            }
                            link.sort(function(a,b){
                                if (a.target.id === d.id) {
                                    return 1
                                } else {
                                    return -1
                                }
                            })
                            rect.style('stroke-width',function(r) {
                                if (r.id === d.id) {
                                    return '2'
                                }
                            }).style('stroke', function(r) {
                                if (r.id === d.id) {
                                    if (r.type == 2) {
                                        return '#0084FF'
                                    } else {
                                        if (r.type == 1) {
                                            if (r.isActualController) {
                                                return "#FA6B64"
                                            } else if (r.isFHolder) {
                                                return '#F1B03A'
                                            } else {
                                                return '#0084FF'
                                            }
                                            
                                        } else{
                                            return "#CCC"
                                        }
                                    }
                                } else {
                                    if (r.type == 1) {
                                        if (r.isActualController) {
                                            return "#FBC6C3"
                                        } else if (r.isFHolder) {
                                            return '#F3DDB6'
                                        } else {
                                            return '#83C6FF'
                                        }
                                    } else if (r.name === 'origin') {
                                        return '#0E78DB'
                                    } else{
                                        return "#CCC"
                                    }
                                }
                            })
                        })
                        .on('mouseout',d => {
                            if (d.name === 'origin') {
                                return false
                            }
                            d3.selectAll('marker#resolved'+d.id).selectAll('path').attr('fill',lineColor.default)
                            link.style('stroke',function(l){
                                return '#919191'
                            }).style('stroke-width',function(l){
                                return 1
                            })
                            rect.style('stroke-width',function(r) {
                                if (r.id === d.id) {
                                    return '1'
                                }
                            }).style('stroke', function(r) {
                                if (r.type == 1) {
                                    if (r.isActualController) {
                                        return "#FBC6C3"
                                    } else if (r.isFHolder) {
                                        return '#F3DDB6'
                                    } else {
                                        return '#83C6FF'
                                    }
                                } else if (r.name === 'origin') {
                                    return '#0E78DB'
                                } else {
                                    return "#CCC"
                                }
                            })
                        })
                    function getRelationLink (id,color) {
                        if (id == 1) {
                            return false
                        }
                        let sourceId = ''
                        link.style('stroke',function(l){
                            if (id === l.target.id) {
                                sourceId = l.source.id
                                return color
                            }
                        }).style('stroke-width',function(l){
                            if (id === l.target.id) {
                                return 2
                            }
                        })
                        
                    }
                    const rect = nodeEnter.append("svg:rect")
                        .attr("x", function(d) {
                            return(d.name == 'origin') ? -(rootRectWidth / 2) : -100;
                        })
                        .attr("y", function(d) {
                            return(d.name == 'origin') ? -20 : forUpward ? -52 : 12;
                        })
                        .attr("width", function(d) {
                            return(d.name == 'origin') ? rootRectWidth : 200;
                        })
                        .attr("height", function(d) {
                            return(d.name == 'origin') ? 40 : 80;
                        })
                        .attr("rx", 0)
                        .style('cursor', "pointer")
                        .style("stroke", function(d) {
                            if (d.name == 'origin') {
                                return "#0E78DB"
                            } else if (d.type == 1) {
                                if (d.isActualController) {
                                    return "#FBC6C3"
                                } else if (d.isFHolder) {
                                    return '#F3DDB6'
                                } else {
                                    return '#83C6FF'
                                }
                            } else{
                                return "#CCC"
                            }
                        })
                        .style("fill", function(d) {
                            if (d.name == 'origin') {
                                return "#0084FF"
                            } else if (d.type == 1) {
                                if (d.isActualController) {
                                    return "#FFFBFB"
                                } else if (d.isFHolder) {
                                    return '#FEFBF3'
                                } else {
                                    return '#F5FAFF'
                                }
                            } else{
                                return "#fff"
                            }
                        })
                    
                    const personTopRect = nodeEnter.append("svg:rect")
                        .attr("x", function(d) {
                            return -100;
                        })
                        .attr("y", function(d) {
                            return !d.isFHolder ? 0 : d.isActualController ? -102 : -82;
                        })
                        .attr("width", function(d) {
                            return d.isFHolder ? 200 : 0;
                        })
                        .attr("height", function(d) {
                            return !d.isFHolder ? 0 : d.isActualController ? 50 : 30;
                        })
                        .attr("rx", 2)
                        .style("stroke", function(d) {
                            return d.isActualController ? "#FA6B64" : "#F1B03A";
                        })
                        .style("fill", function(d) {
                            return d.isActualController ? "#FA6B64" : "#F1B03A";   //节点背景色
                        })
                    nodeEnter
                        .append("svg:path")
                        .attr("fill", d => {
                            return d.isActualController ? "#FA6B64" : "#F1B03A"
                        })
                        .attr("d", function(d) {
                            if (d.isFHolder) {
                                return "M0 -44 L-10 -54 L10 -54 Z"
                            } else {
                                return ""; 
                            }
                        })
                    const holdingCompanyRect = nodeEnter.append('svg:rect')
                        .attr("x", "-40")
                        .attr("y", function(d) {
                            return(d.name == 'origin') ? '.35em' : forUpward ? '31' : '-9';
                        })
                        .attr("rx",2)
                        .attr("width", function(d) {
                            return d.isHoldingCompany ? 30 : 0;
                        })
                        .attr("height", function(d) {
                            return d.isHoldingCompany ? 20 : 0;
                        })
                        .style("fill", "#EBF5FF")
                    nodeEnter.append("text")
                        .attr("x", "-35")
                        .attr("dy", function(d) {
                            return(d.name == 'origin') ? '.35em' : forUpward ? '45' : '5';
                        })
                        .attr("text-anchor", "start")
                        .style("fill", "#0084FF")
                        .style('font-size', 10)
                        .text(function(d) {
                            return d.isHoldingCompany ? "控股" : "";
                        });
                    nodeEnter.append('circle')
                        .attr('r', 1e-6);
                    nodeEnter.append("text")
                        .attr("x", function(d) {
                            return '0'
                        })
                        .attr('dy', function(d) {
                            return(d.name == 'origin') ? '.35em' : forUpward ? '-24' : '40';
                        })
                        .attr("text-anchor", function(d) {
                            return 'middle'
                        })
                        .text(function(d) {
                            if(d.name == 'origin') {
                                return _this.rootName;
                            } 
                            if(d.repeated) {
                                return '[Recurring] ' + d.name;
                            }
                            return(d.name.length > 12) ? d.name.substr(0, 12) : d.name;
                        })
                        .style({
                            'fill': function(d) {
                                if(d.name == 'origin') {
                                    return '#fff';
                                } else {
                                    return '#333'
                                }
                            },
                            'font-size': function(d) {
                                return(d.name == 'origin') ? 16 : 14;
                            },
                            'cursor': "pointer"
                        })
                        .on('click', Change_modal)
                        .append('svg:title').text(d=>d.name)
                    nodeEnter.append("text")
                        .attr("x", "0")
                        .attr("dy", function(d) {
                            return(d.name == 'origin') ? '.35em' : forUpward ? '-5' : '59';
                        })
                        .attr("text-anchor", function() {
                            return 'middle'
                        })
                        .attr('fill', '#333')
                        .text(function(d) {
                            return d.name.length > 24 ? d.name.substr(12, 12) + '...' : d.name.substr(12, d.name.length);
                        })
                        .style({
                            'font-size': function(d) {
                                return(d.name == 'origin') ? 16 : 14;
                            },
                            'cursor': "pointer"
                        })
                        .append('svg:title').text(d=>d.name)

                    nodeEnter.append("text")
                        .attr("x", "0")
                        .attr("dy", function(d) {
                            if (d.name === 'origin') {
                                return '.35em'
                            } else {
                                if (forUpward) {
                                    if (d.name.length > 12) {
                                        return '16'
                                    } else {
                                        return '6'
                                    }
                                } else {
                                    if (d.name.length > 12) {
                                        return '80'
                                    } else {
                                        return '70'
                                    }
                                }
                            }
                        })
                        .attr("text-anchor", "middle")
                        .attr("class", "linkname")
                        .style("fill", "#666")
                        .style('cursor', "pointer")
                        .style('font-size', 12)
                        .text(function(d) {
                            var str = (d.name == 'origin') ? '' : "认缴金额:"+ d.amount +"万人民币";
                            return(str.length > 18) ? str.substr(0, 18) + ".." : str;
                        });
                    nodeEnter.append("text")
                        .attr("x", "10")
                        .attr("dy", function(d) {
                            return(d.name == 'origin') ? '.35em' : forUpward ? '45' : '5';
                        })
                        .attr("text-anchor", "start")
                        .attr("class", "linkname")
                        .style("fill", "#0084FF")
                        .style('font-size', 10)
                        .text(function(d) {
                            return(d.name == 'origin') ? "" : d.ratio;
                        });
                    nodeEnter.append("text")
                        .attr("x", "0")
                        .attr("dy", function(d) {
                            return d.isActualController ? -87 : -72;
                        })
                        .attr("text-anchor", "middle")
                        .style("fill", "#fff")
                        .style('font-size', 12)
                        .text(function(d) {
                            return d.isActualController ? '疑似实际控制人' : '';
                        });
                    nodeEnter.append("text")
                        .attr("x", "0")
                        .attr("dy", function(d) {
                            return d.isActualController ? -65 : -63;
                        })
                        .attr("text-anchor", "middle")
                        .style("fill", "#fff")
                        .style('font-size', 12)
                        .text(function(d) {
                            return d.isFHolder ? "最终受益人:" + d.holderPercent : '';
                        });
                    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.hasNode == 1 ? 10 : 0
                        })
                        .attr('cy', function(d) {
                            return(d.name == 'origin') ? -20 : (forUpward) ? -63 : 103;
                        })
                        .style('fill', function(d) {
                            return d.hasNode == 1 ? "#9CD0FF" : ""
                        })
                        .style('stroke', function(d) {
                            return d.hasNode == 1 ? "#9CD0FF" : ""
                        })
                        .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 ? -57 : 109;
                        })
                        .attr("text-anchor", "middle")
                        .style("fill", "#fff")
                        .style('font-size','20px')
                        .style('cursor','pointer')
                        .text(function(d) {
                            if(d.name == 'origin') {
                                return '';
                            }
                            return d.hasNode == 1 ? "+" : ''
                        })
                        .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;
                        });

                    link.enter().insert('path', 'g')
                        .attr('class', link_class)
                        .attr('stroke',function(d){
                            // return '#8b4513'
                            return '#919191'
                        })
                        .attr('fill',"none")
                        .attr('stroke-width','1px')
                        // .attr('opacity', 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, i) { 
                            return forUpward ? getMarkerUpArrow(d,i) : getMarkerDownArrow(d,i)
                        })
                        .attr("id", function(d, i) {
                            return "mypath" + "-" + d.source.id + "-" + d.target.id;
                        })
                    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;
                    });
                    function getMarkerUpArrow(d,i) {
                        svg.append("defs").append("marker")   //箭头
                            .attr("id", "resolved"+d.target.id)
                            .attr("markerUnits", "strokeWidth") //设置为strokeWidth箭头会随着线的粗细发生变化
                            .attr("markerUnits", "userSpaceOnUse")
                            .attr("viewBox", "0 -5 10 10") //坐标系的区域
                            .attr("refX", -28) //箭头坐标
                            .attr("refY", 0)
                            .attr("markerWidth", 12) //标识的大小
                            .attr("markerHeight", 12)
                            .attr("orient", "90") //绘制方向,可设定为:auto(自动确认方向)和 角度值
                            .attr("stroke-width", 2) //箭头宽度
                            .append("path")
                            .attr("d", "M0,-5L10,0L0,5") //箭头的路径
                            .attr('fill', '#919191'); //箭头颜色 
                        return "url(#resolved" + d.target.id + ")";
                    }
                    function getMarkerDownArrow(d,i) {
                        svg.append("defs").append("marker")   //箭头
                            .attr("id", "resolved"+d.target.id)
                            .attr("markerUnits", "strokeWidth") //设置为strokeWidth箭头会随着线的粗细发生变化
                            .attr("markerUnits", "userSpaceOnUse")
                            .attr("viewBox", "0 -5 10 10") //坐标系的区域
                            .attr("refX", 0) //箭头坐标
                            .attr("refY", 0)
                            .attr("markerWidth", 12) //标识的大小
                            .attr("markerHeight", 12)
                            .attr("orient", "90") //绘制方向,可设定为:auto(自动确认方向)和 角度值
                            .attr("stroke-width", 2) //箭头宽度
                            .append("path")
                            .attr("d", "M0,-5L10,0L0,5") //箭头的路径
                            .attr('fill','#919191');
                        return "url(#resolved" + d.target.id + ")";
                    }
                    function Change_modal () {
                        _this.Modal = true
                    }
                    function click(d) {
                        event.stopPropagation()
                        if(forUpward) {
            
                        } else {
                            if(d._children) {
                                console.log('对外投资--ok')
                            } else {
                                console.log('对外投资--no')
                            }
                        }
                        isExpand = !isExpand;
                        if(d.name == 'origin') {
                            return;
                        }
                        if(d.children) {
                            d._children = d.children;
                            d.children = null;
                            d3.select(this).text('+')
                            update(d, originalData, g);
                        } else {
                            if (d._children && d._children.length > 0) {
                                d.children = d._children;
                                d._children = null;
                                if(d.name == 'origin') {
                                    d.children.forEach(expand);
                                }
                                d3.select(this).text('-')
                                update(d, originalData, g);
                            } else {
                                gqctt({
                                    beijingCrid: d.direction == 'up' ? d.beijingCrid : d.pbeijingCrid,
                                    direction: d.direction
                                }).then(res => {
                                    if (res.code === 0) {
                                        if (d.direction == 'up') {
                                            d.children = res.result.investorList
                                            d.children.map(item => {
                                                item.amount = Number(item.subConAm).toFixed(2)
                                                item.isFHolder = true
                                                item.ratio = item.subComBl.length > 6 ? 
                            calculation.accMul(item.subComBl,100).toFixed(2) + '%' : 
                            calculation.accMul(item.subComBl,100) + '%'
                                                item.name = item.entName
                                                item.type = item.bz === '企业' ? 2 : 1
                                                item.isHoldingCompany = item.isHolding == 1 ? true : false
                                                item.isFHolder = item.subComBl >= 0.25 && item.type == 1
                                                item.direction = direction
                                                item.holderPercent = item.ratio
                                            })
                                        } else {
                                            d.children = res.result.holderList
                                            d.children.map(item => {
                                                item.amount = Number(item.subConAm).toFixed(2)
                                                item.isFHolder = true
                                                item.ratio = item.subComBl.length > 6 ? 
                            calculation.accMul(item.subComBl,100).toFixed(2) + '%' : 
                            calculation.accMul(item.subComBl,100) + '%'
                                                item.name = item.pentName
                                                item.type = item.bz === '企业' ? 2 : 1
                                                item.isHoldingCompany = item.isHolding == 1 ? true : false
                                                item.isFHolder = item.subComBl >= 0.25 && item.type == 1
                                                item.direction = direction
                                                item.holderPercent = item.ratio
                                            })
                                        }
                                        d._children = null;
                                        if(d.name == 'origin') {
                                            d.children.forEach(expand);
                                        }
                                        d3.select(this).text('-')
                                        update(d, originalData, g)
                                    }
                                })
                            }
                        }
                    }
                }
                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() {
                    treeG.attr('transform', 'translate(' + zoom.translate() + ')' +
                        ' scale(' + zoom.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();
        },
        onJumpReport() {
            this.$router.push(...)
        }
    }
}
.g6-tooltip {
  border: 1px solid #e2e2e2;
  border-radius: 4px;
  font-size: 12px;
  color: #545454;
  background-color: rgba(255, 255, 255, 0.9);
  padding: 10px 8px;
  box-shadow: rgb(174, 174, 174) 0px 0px 10px;
}
.borrow{
    height: 1000px;
    position: relative;
    top: -66px;
}

D3Mixin.js

/**
 * 全屏 toggleFullScreen
 * 保存 downloadImpByChart
 */
export const D3Mixin = {
  data() {
    return {
      isFullscreen: true,
    }
  },
  methods: {
    downloadImpByChart(chartName) {
      var _this = this;
      var html = document.getElementsByTagName("html")[0]; //获取可视区域宽

      var Bwidth = html.clientWidth + 20; //转换屏幕宽高
      var Bheight = html.clientHeight + 150;
      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 = d3.select('#mountNode svg');
      var gbox = document.getElementsByClassName("gbox")[0] || document.getElementsByClassName("container")[0];
      var x = g.width / 2 - html.clientWidth / 2 + 20; //计算偏移位置
      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 + 20);
      svgbox.attr("height", g.height + 150);
      var svg = document.getElementById("mountNode").innerHTML;
      var c = canvas.getContext("2d");
      //新建Image对象
      var img = new Image();
      //svg内容
      img.src = "data:image/svg+xml," + encodeURIComponent(svg); //svg内容中可以有中文字符
      // img.src = "data:image/svg+xml," + svg; //svg内容中不能有中文字符
      //图片初始化完成后调用
      var cwidth = g.width;
      img.onload = function () {
        //将canvas的宽高设置为图像的宽高
        canvas.width = cwidth + 20;
        canvas.height = g.height + 150;
        //canvcas画图片
        c.fillStyle = "#fff";
        c.fillRect(0, 0, canvas.width, canvas.height);
        c.drawImage(img, 0, 30);
        var a = document.createElement("a");
        a.download = `${chartName}-${_this.companyName}.png`;
        a.href = canvas.toDataURL("image/png");
        a.click();
      };
      //图片转换为base64后 传给后端 发邮件
      gbox.style.transform = "";
      svgbox.attr("width", Bmax);
      svgbox.attr("height", Bmin);
    },
    checkFull() {
      var isFull =
        document.fullscreenEnabled || window.fullScreen || document.webkitIsFullScreen || document.msFullscreenEnabled
      if (isFull === undefined) {
        isFull = false
      }
      return isFull
    },
    FullScreen(el) {
      if (this.isFullscreen) {
        //退出全屏
        if (document.exitFullscreen) {
          document.exitFullscreen()
        } else if (document.mozCancelFullScreen) {
          document.mozCancelFullScreen()
        } else if (document.webkitExitFullscreen) {
          document.webkitExitFullscreen()
        } else if (!document.msRequestFullscreen) {
          document.msExitFullscreen()
        }
      } else {
        //进入全屏
        if (el.requestFullscreen) {
          el.requestFullscreen()
        } else if (el.mozRequestFullScreen) {
          el.mozRequestFullScreen()
        } else if (el.webkitRequestFullscreen) {
          //改变平面图在google浏览器上面的样式问题
          el.webkitRequestFullscreen()
        } else if (el.msRequestFullscreen) {
          this.isFullscreen = true
          el.msRequestFullscreen()
        }
      }
    },
    toggleFullScreen(e) {
      this.isFullscreen = !this.isFullscreen
      this.FullScreen(document.getElementById('borrow'))
    }
  },
  mounted() {
    window.addEventListener('resize', () => {
      let that = this
      if (!that.checkFull()) {
        //要执行的动作
        that.isFullscreen = true
      }
    })
  }
}
原文地址:https://www.cnblogs.com/snowRock/p/14715368.html