大家好,欢迎来到IT知识分享网。
准备:
最终效果图展示:
实现思路:
1、将树结构看做是上下两部分:以【北京麻六记餐饮管理有限公司】为根节点,分为上游股东和下游对外投资
2、绘制两颗树,将根节点重合,及得到效果图
右侧实现的功能也在js里了,同企查查页面功能,全屏用的其他查件
主界面vue文件:
<!-- 股权穿透图 --> <template> <div id="borrow" style="width: 100%;height: 100%;background-color: #fff;"> <div id="mountNode" style="width: 100%;height: 100%;"></div> <ToolBox @maoScale="maoScale" @simpleChange="simpleChange" @editChange="editChange" @refresh="refresh" @exportImg="exportImg" @screenfullChange="toggleFullScreen" /> </div> </template> <script> import ToolBox from './components/Equity/ToolBox.vue' import {
drawing, simpleChange1, zoomClick, editChange1 } from './components/Equity/index.js' import {
D3Mixin } from '@/mixin/D3Mixin' export default {
components: {
Header, ToolBox }, mixins: [D3Mixin], data() {
return {
}; }, mounted() {
drawing() }, methods: {
simpleChange(val) {
simpleChange1(val.value) }, maoScale(val) {
zoomClick(val) }, editChange(val) {
editChange1(val.value) }, refresh() {
drawing() }, exportImg() {
this.downloadImpByChart('股权穿透图谱', '北京马六级餐饮') } } }; </script> <style lang="scss" scoped> .downwardNode text, .upwardNode text {
font: 10px sans-serif; } .downwardLink {
fill: none; stroke-width: 1px; /* opacity: 0.5; */ } .upwardLink {
fill: none; stroke-width: 1px; /* opacity: 0.5; */ } ::v-deep .downLine {
stroke: #128bed; stroke-dasharray: 6, 2; stroke-dashoffset: 20; animation: 1s down-lines infinite linear; z-index: 999; opacity: 1; stroke-width: 2px; } ::v-deep .upLine {
stroke: #128bed; stroke-dasharray: 6, 2; stroke-dashoffset: 20; animation: 1s up-lines infinite linear; z-index: 999; opacity: 1; stroke-width: 2px; } @keyframes down-lines {
0% {
stroke-dashoffset: 10; } 100% {
stroke-dashoffset: -10; } } @keyframes up-lines {
0% {
stroke-dashoffset: -10; } 100% {
stroke-dashoffset: 10; } } ::v-deep .isExpand {
dominant-baseline: middle; text-anchor: middle; } ::v-deep .linkname {
text-anchor: middle; } </style>
数据json文件 EquityJson.json:
{
"data": {
"short_name": "北京麻六记餐饮管", "p_trees": [ {
"short_name": "宋娜", "level": "1", "isKey": true, "amount": "512.0", "has_problem": "0", "percent": "0.64", "pid": "77bd1a4a4459cafea271c2261a", "sh_type": "工商股东", "children": [], "eid": "", "identifier": "2", "name": "宋娜", "type": "P" }, {
"short_name": "北京食通达科技发", "level": "1", "amount": "288.0", "has_problem": "0", "percent": "0.36", "sh_type": "工商股东", "children": [ {
"short_name": "菲特兰装饰设计", "level": "2", "amount": "640.0", "has_problem": "0", "percent": "0.64", "sh_type": "工商股东", "children": [ {
"short_name": "汪玺", "level": "3", "amount": "9.9", "has_problem": "0", "percent": "0.99", "pid": "1421feeaf56724cdfb6e4f12e5", "sh_type": "工商股东", "children": [], "eid": "", "identifier": "5", "name": "汪玺", "type": "P" }, {
"short_name": "安勇", "level": "3", "amount": "0.1", "has_problem": "0", "percent": "0.01", "pid": "0d33371f31e7ce4734f75c575daf1a0b", "sh_type": "工商股东", "children": [], "eid": "", "identifier": "6", "name": "安勇", "type": "P" } ], "eid": "50aeb7cb-8e99-4def-b1d8-b17fbdd0d41b", "identifier": "4", "name": "北京菲特兰装饰设计有限公司", "type": "E" }, {
"short_name": "MSA CHINA FUND II L.P", "level": "2", "amount": "200.0", "has_problem": "0", "percent": "0.2", "sh_type": "工商股东", "children": [], "eid": "b3baec3812d0a3e83e96dbc492c74802", "identifier": "7", "name": "MSA CHINA FUND II L.P", "type": "UE" }, {
"short_name": "五鼎通达(上海)", "level": "2", "amount": "160.0", "has_problem": "0", "percent": "0.16", "sh_type": "工商股东", "children": [ {
"short_name": "安勇", "level": "3", "amount": "6.25", "has_problem": "0", "percent": "0.625", "pid": "0d33371f31e7ce4734f75c575daf1a0b", "sh_type": "工商股东", "children": [], "eid": "", "identifier": "9", "name": "安勇", "type": "P" }, {
"short_name": "杨洋", "level": "3", "amount": "1.25", "has_problem": "0", "percent": "0.125", "pid": "05c5376b47c1daf0eb39fa0ec9", "sh_type": "工商股东", "children": [], "eid": "", "identifier": "10", "name": "杨洋", "type": "P" }, {
"short_name": "李萍", "level": "3", "amount": "1.25", "has_problem": "0", "percent": "0.125", "pid": "de2f8e59a3d7dbc0e782fec491", "sh_type": "工商股东", "children": [], "eid": "", "identifier": "11", "name": "李萍", "type": "P" }, {
"short_name": "钱秀琴", "level": "3", "amount": "0.625", "has_problem": "0", "percent": "0.0625", "pid": "ae318e35f6e54b6bfefccf1c913e2564", "sh_type": "工商股东", "children": [], "eid": "", "identifier": "12", "name": "钱秀琴", "type": "P" }, {
"short_name": "田金鹭", "level": "3", "amount": "0.625", "has_problem": "0", "percent": "0.0625", "pid": "1b37d55ec0623cda2b0f5bf173b060a7", "sh_type": "工商股东", "children": [], "eid": "", "identifier": "13", "name": "田金鹭", "type": "P" } ], "eid": "bb713bd6-6999-4335-8b18-1e4e4cd92276", "identifier": "8", "name": "五鼎通达(上海)食品科技中心(有限合伙)", "type": "E" } ], "eid": "6b38d70c-e01d-4548-92e6-0c3df677ac80", "identifier": "3", "name": "北京食通达科技发展有限公司", "type": "E" } ], "c_count": 10, "c_trees": [ {
"short_name": "青岛曜石麻六记酒", "level": "1", "amount": "100.0", "has_problem": "0", "percent": "1.0", "sh_type": "工商股东", "children": [], "eid": "dbc5122c-d56f-4fab-aa05-e8dfa8eae46f", "identifier": "15", "name": "青岛曜石麻六记酒店管理有限公司", "type": "E" }, {
"short_name": "成都麻六记餐饮管", "level": "1", "amount": "100.0", "has_problem": "0", "percent": "1.0", "sh_type": "工商股东", "children": [], "eid": "fc60ec15-db71-4252-b31d-1e4dd", "identifier": "16", "name": "成都麻六记餐饮管理有限公司", "type": "E" }, {
"short_name": "太原麻六记酒店管", "level": "1", "amount": "100.0", "has_problem": "0", "percent": "1.0", "sh_type": "工商股东", "children": [], "eid": "63d94664-fd80-40fd-93a0-ec9ebd", "identifier": "17", "name": "太原麻六记酒店管理有限公司", "type": "E" }, {
"short_name": "北京麻六记酒店管", "level": "1", "amount": "100.0", "has_problem": "0", "percent": "1.0", "sh_type": "工商股东", "children": [ {
"short_name": "北京亦庄麻六记酒", "level": "2", "amount": "100.0", "has_problem": "0", "percent": "1.0", "sh_type": "工商股东", "children": [], "eid": "4e0b5c24-bba4-4bee-a056-53d83d6264a0", "identifier": "19", "name": "北京亦庄麻六记酒店管理有限公司", "type": "E" }, {
"short_name": "北京乐堤港麻六记", "level": "2", "amount": "100.0", "has_problem": "0", "percent": "1.0", "sh_type": "工商股东", "children": [], "eid": "7f258c98-c68c-4916-9af5-26b74f3c1e92", "identifier": "20", "name": "北京乐堤港麻六记酒店管理有限公司", "type": "E" }, {
"short_name": "北京银泰麻六记酒", "level": "2", "amount": "55.0", "has_problem": "0", "percent": "0.55", "sh_type": "工商股东", "children": [], "eid": "3fb166e6-b780-4963-b1a4-0ffe94185d6b", "identifier": "21", "name": "北京银泰麻六记酒店管理有限公司", "type": "E" }, {
"short_name": "北京西直门麻六记", "level": "2", "amount": "55.0", "has_problem": "0", "percent": "0.55", "sh_type": "工商股东", "children": [], "eid": "106dafe9-9c1a-485e-8580-76d87c7a8750", "identifier": "22", "name": "北京西直门麻六记酒店管理有限公司", "type": "E" } ], "eid": "980ff820-9473-40ba-b105-998ceec35823", "identifier": "18", "name": "北京麻六记酒店管理有限公司", "type": "E" }, {
"short_name": "上海麻六记餐饮管", "level": "1", "amount": "100.0", "has_problem": "0", "percent": "1.0", "sh_type": "工商股东", "children": [ {
"short_name": "上海守味麻六记餐", "level": "2", "amount": "100.0", "has_problem": "0", "percent": "1.0", "sh_type": "工商股东", "children": [], "eid": "0fd17482-1465-11ed-8e23-83cf331ae46e", "identifier": "24", "name": "上海守味麻六记餐饮管理有限公司", "type": "E" } ], "eid": "21dfeb49-c277-4931-9717-aad14b", "identifier": "23", "name": "上海麻六记餐饮管理有限公司", "type": "E" } ], "eid": "f6114a80-0f5b-4059-83f1-65dd0bdc6397", "p_count": 11, "has_problem": "0", "identifier": "1", "name": "北京麻六记餐饮管理有限公司", "type": "E" }, "sign": "86f52c7430fe4797a0f28bb7f", "status": "200", "message": "操作成功" }
js文件
import d3 from './d3.min.js' import EquityJson from '@/api/EquityJson.json' import deepcopy from 'deepcopy' // 是否全称 let shortNameShow = false // 是否显示编辑按钮 let editShow = false var zoom,treeG /* 如果需要将页面的整体高度拉伸 *(折线的高度拉伸,公司长方体的块也的Y距也需要调整及所有标签Y轴都需要调整, * 始终保证折线的端点在长方形的中心点) */ // json数据结构改变 var rootData = {
downward: {
"direction": "downward", "name": "origin", "children": deepcopy(EquityJson.data.c_trees) }, upward: {
"direction": "upward", "name": "origin", "children": deepcopy(EquityJson.data.p_trees) } } var rootName = EquityJson.data.name; let width = 0 let height = 0 var _this = this; var rootRectWidth = 0; //根节点rect的宽度 var forUpward = true // 数据重置 function resizeData() {
shortNameShow = false editShow = false // 重新获取json数据 rootData = {
downward: {
"direction": "downward", "name": "origin", "children": deepcopy(EquityJson.data.c_trees) }, upward: {
"direction": "upward", "name": "origin", "children": deepcopy(EquityJson.data.p_trees) } } rootName = EquityJson.data.name } export function drawing() {
if (d3.select('#svg')) {
d3.select('#svg').remove(); resizeData() } width = document.getElementById('mountNode').scrollWidth height = document.getElementById('mountNode').scrollHeight var d3GenerationChart = new treeChart(d3); d3GenerationChart.drawChart(); } // 简称 全称切换 export function simpleChange1(val) {
shortNameShow = val if (shortNameShow) {
d3.selectAll(".text-g").attr('visibility', 'hidden'); d3.selectAll(".short-text-g").attr('visibility', 'visible'); } else {
d3.selectAll(".text-g").attr('visibility', 'visible'); d3.selectAll(".short-text-g").attr('visibility', 'hidden'); } } // 编辑 export function editChange1(val) {
editShow = val if (editShow) {
d3.selectAll(".edit").attr('visibility', 'visible'); } else {
d3.selectAll(".edit").attr('visibility', 'hidden'); } } // 缩放 export function zoomClick(type) {
// var clicked = d3.event.target, var direction = 1, factor = 0.3, 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 = type === 1 ? 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]; // var d3GenerationChart = new treeChart(d3); // d3GenerationChart.drawChart(); interpolateZoom([view.x, view.y], view.k); } // 重置刷新 export function refresh() {
drawing() } // d3.select("#reset").on("click", function(d) {
// interpolateZoom([0, 0], 1); // }); function interpolateZoom(translate, scale) {
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 redraw() {
treeG.attr('transform', 'translate(' + zoom.translate() + ')' + ' scale(' + zoom.scale() + ')'); } 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] = rootData[direction]; }); rootRectWidth = rootName.length * 15; self.graphTree(self.getTreeConfig()); }; treeChart.prototype.getTreeConfig = function() {
var treeConfig = {
}; treeConfig.chartWidth = width treeConfig.chartHeight = height 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.d.source; var t = obj.d.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)); // return ("M" + s.x + "," + (s.y + 20) + "L" + s.x + "," + (s.y + 20 + (t.y - s.y) / 2) + "L" + t.x + "," + (s.y + 20 + (t.y - s.y) / 2) + "L" + t.x + "," + (t.y + 20)); }; zoom = d3.behavior.zoom().scaleExtent([0.5, 2]).on('zoom', redraw); var svg = d3.select('#mountNode') .append('svg') .attr('id', 'svg') .attr('width', '100%') .attr('height', '100%') .attr('viewBox', '0 0 ' + config.chartWidth + ' ' + config.chartHeight) .attr('xmlns', 'http://www.w3.org/2000/svg') // .on('mousedown', disableRightClick) .call(zoom) .on('dblclick.zoom', null); treeG = svg.append('g') .attr('class', 'gbox') .attr('transform', 'translate(0,0)'); 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 == "upward"; var node_class = direction + "Node"; var link_class = direction + "Link"; var downwardSign = forUpward ? -1 : 1; var isExpand = false; var nodeSpace = 200; var tree = d3.layout.tree().sort(sortByDate).nodeSize([nodeSpace, 0]); var nodes = tree.nodes(originalData); var links = tree.links(nodes); var offsetX = -config.centralWidth; nodes.forEach(function(d) {
d.y = downwardSign * (d.depth * linkLength) + config.centralHeight; d.x = d.x - offsetX; if (d.name == "origin") {
d.x = config.centralWidth; d.y += downwardSign * 0; // 上下两树图根节点之间的距离 } }); // Update the node. var node = g.selectAll('g.' + node_class) .data(nodes, function(d) {
return d.id || (d.id = ++id); }) var nodeEnter = node.enter().append('g') .attr('class', node_class) .attr('transform', function(d) {
return 'translate(' + source.x0 + ',' + source.y0 + ')'; }) .style('cursor', function(d) {
return d.name == "origin" ? "" : d.children || d._children ? "pointer" : ""; }) .on('click', d => {
if (d.direction == 'upward' || d.direction == 'downward') {
// _this.$router.push({
// path: '/search/detail', // name: 'search-detail', // query: {
// companyId: d.direction == 'upward' ? d.beijingCrid : d.pbeijingCrid // } // }) } }) .on("mouseenter", nodeHover) .on("mouseleave", nodeOut); const rectG = nodeEnter.append("g") // 公司或股东长方形样式 const rect = rectG.append("svg:rect") .attr("x", function(d) {
return d.name == 'origin' ? -(rootRectWidth / 2) : -90; }) .attr("y", function(d) {
return d.name == "origin" ? -20 : forUpward ? -31 : -40; }) .attr("width", function(d) {
return d.name == 'origin' ? rootRectWidth : 180; }) .attr("height", function(d) {
return d.name == 'origin' ? 40 : 70; }) .attr("rx", 0) .style('cursor', "pointer") // 边框线 .style("stroke", "#128bed") // 字体填充颜色 .style("fill", function(d) {
if (d.name == 'origin') {
return "#128bed" } else {
return "#fff" } }) .on("mouseenter", nodeHover); // 公司名称文本第一行 全称 const textG = rectG.append("g") .attr('class', 'text-g') .attr('visibility', 'visible') textG.append("text") .attr("x", 0) .attr('dy', function(d) {
if (d.name == 'origin') {
return '.35em' } else if (forUpward) {
return d.name.length > 10 ? '-1' : '10'; } else {
return d.name.length > 10 ? '-10' : '0'; } // 后续方框底部有融资轮次,样式需要用这个 // else if (d.financingRound) {
// if (forUpward) {
// return d.name.length > 10 ? '0' : '0'; // } else {
// return d.name.length > 10 ? '-20' : '-10'; // } // } else {
// if (forUpward) {
// return d.name.length > 10 ? '-1' : '10'; // } else {
// return d.name.length > 10 ? '-10' : '0'; // } // } }) .attr("text-anchor", "middle") .attr("fill", "#333") .text(function(d) {
if (d.name == "origin") {
return rootName; } if (d.repeated) {
return "[Recurring] " + d.name; } return d.name.length > 10 ? d.name.substr(0, 10) : d.name; }) .style({
'fill': function(d) {
if (d.name == 'origin') {
return '#fff'; } }, 'font-size': 14, 'cursor': "pointer" }) .on('click', Change_modal) .append('svg:title').text(d => d.name) // 公司名称文本第二行 全称 textG.append("text") .attr("x", "0") .attr("dy", function(d) {
if (d.name == 'origin') {
return '.35em' } else if (forUpward) {
return d.name.length > 10 ? '20' : '40'; } else {
return d.name.length > 10 ? '10' : '40'; } // 后续方框底部有融资轮次,样式需要用这个 // else if (d.financingRound) {
// if (forUpward) {
// return d.name.length > 10 ? '-40' : '40'; // } else {
// return d.name.length > 10 ? '0' : '40'; // } // } else {
// if (forUpward) {
// return d.name.length > 10 ? '20' : '40'; // } else {
// return d.name.length > 10 ? '10' : '40'; // } // } }) .attr("text-anchor", "middle") .text(function(d) {
return d.name.length > 20 ? d.name.substr(10, 10) + '...' : d.name.substr(10, d.name.length); }) .style({
'fill': "#333", // 第二行字体颜色 "font-size": 14, 'cursor': "pointer" }) .append('svg:title').text(d => d.name) // 公司名称文本第一行 简称 const shortTextG = rectG.append("g") .attr('class', 'short-text-g') .attr('visibility', 'hidden') shortTextG.append("text") .attr("x", "0") .attr("dy", function(d) {
if (d.name == 'origin') {
return '.35em' } else {
return forUpward ? 10 : 0 } // 后续方框底部有融资轮次,样式需要用这个 // else if (d.financingRound) {
// if (forUpward) {
// return d.name.length > 10 ? '-40' : '40'; // } else {
// return d.name.length > 10 ? '0' : '40'; // } // } else {
// if (forUpward) {
// return d.name.length > 10 ? '20' : '40'; // } else {
// return d.name.length > 10 ? '10' : '40'; // } // } }) .attr("text-anchor", "middle") .text(function (d) {
return d.name == 'origin' ? rootName : d.short_name.length > 10 ? d.short_name.substr(0, 10) + '...' : d.short_name.substr(0, d.short_name.length); }) .style({
'fill': function (d) {
return d.name == 'origin' ? '#fff' : "#333" }, "font-size": 14, 'cursor': "pointer", }) .append('svg:title').text(d => d.name) // 最终受益人红色背景框 isKey const personTopRect = rectG.append("svg:rect") .attr("x", -40) .attr("y", -56) .attr("width", 80) .attr("height", function(d) {
return d.isKey ? 18 : 0; }) .attr("rx", 2) .style("stroke", "#FA6B64") .style("fill", "#FA6B64") // 最终受益人红色小三角形 isKey rectG .append("svg:path") .attr("fill", d => {
return d.isKey ? "#FA6B64" : "" }) .attr("d", function(d) {
return d.isKey ? "M0 -33 L-5 -38 L5 -38 Z" : "" }) // 最终受益人 文本 isKey rectG.append("text") .attr("x", 0) .attr("dy", -43) .attr("text-anchor", "middle") .style("fill", "#fff") .style('font-size', 10) .text(function(d) {
return d.isKey ? "最终受益人" : ''; }); // 融资轮次背景框 financingRound const financingRoundRect = rectG.append('svg:rect') .attr("x", "-89") .attr("y", function(d) {
return forUpward ? 18 : 9; }) .attr("rx", 2) .attr("width", function(d) {
return d.financingRound ? 178 : 0; }) .attr("height", function(d) {
return d.financingRound ? 20 : 0; }) .style("fill", "#e9f3ff") // 融资轮次文字 financingRound rectG.append("text") .attr("x", "0") .attr("dy", () => {
return forUpward ? 29 : 20 }) .attr("text-anchor", "middle") .attr("dominant-baseline", "middle") .style("fill", "#128bed") .style('font-size', 12) .text(function(d) {
return d.financingRound ? "融资轮次:天使轮" : ""; }); // 注销红色标签背景 state const holdingCompanyRect = rectG.append('svg:rect') .attr("x", "55") .attr("y", function(d) {
return d.name == 'origin' ? '.35em' : forUpward ? '-40' : '-50'; }) .attr("rx", 2) .attr("width", function(d) {
return d.state ? 30 : 0; }) .attr("height", function(d) {
return d.state ? 18 : 0; }) .style("fill", "#FFEEE5") // 注销红色标签文字 rectG.append("text") .attr("x", "70") .attr("dy", function(d) {
return (d.name == 'origin') ? '.35em' : forUpward ? '-27' : '-37'; }) .attr("text-anchor", "middle") .style("fill", "#FF722D") .style('font-size', 11) .text(function(d) {
return d.state ? "注销" : ""; }); // 持资占比文字 rectG .append("text") .attr("x", "18") .attr("dy", function(d) {
return d.name == "origin" ? ".35em" : forUpward ? "50" : "-48"; }) .attr("text-anchor", "start") .attr("class", "linkname") .style("fill", "#128bed") //比例颜色(55%) .style("font-size", 10) .text(function(d) {
if (d.percent != "0" && d.percent != "") {
return d.name == "origin" ? "" : parseInt(parseFloat(d.percent) * 100) + '%'; } }) .on("mouseenter", nodeOut) .on("mouseleave", nodeOut); // 加减号圈 rectG .append("circle") .attr('r', function(d) {
return d.name == 'origin' ? 0 : (d.children && d.children.length || d._children && d._children .length) ? 10 : 0 }) .attr('cy', function(d) {
return (d.name == 'origin') ? -20 : (forUpward) ? -42 : 41; }) .style('fill', function(d) {
return (d.children && d.children.length || d._children && d._children.length) ? "#fff" : "" //展开按钮背景颜色 }) .style("stroke", function(d) {
// +号的外圈颜色 展开按钮背景颜色 return hasChildNodeArr.indexOf(d) != -1 ? "#128bed" : "" }) .style('stroke-width', function(d) {
if (d.repeated) {
return 5; } }) .on("mouseenter", nodeOut) .on("mouseleave", nodeOut); // + - 号样式及绑定点击事件 rectG .append("svg:text") .attr("class", "isExpand") .attr("x", "0") .attr("dy", function(d) {
return forUpward ? -40 : 42.5; }) .attr("text-anchor", "middle") .style("fill", function(d) {
return d.type != "P" ? "#128bed" : "#FF4D4D"; }) .style('cursor', 'pointer') .style('font-size', 20) //+、-字体颜色 .text(function(d) {
if (d.name == "origin") {
return ""; } return d._children ? "+" : '' }) .on("click", click) .on("mouseenter", nodeOut) .on("mouseleave", nodeOut); // 编辑按钮圈 const treeC = rectG.append("g") .attr('class', 'edit') .attr('visibility', 'hidden') treeC.append("circle") .attr('r', function(d) {
return d.name == 'origin' ? 0 : 10 }) .attr('cx', function (d) {
return (d.name == 'origin') ? 0 : 90; }) .attr('cy', function(d) {
return (d.name == 'origin') ? -20 : (forUpward) ? -35 : -40; }) .style('fill', "#FF6060") .on("mouseenter", nodeOut) .on("mouseleave", nodeOut); // 编辑按钮×号样式及绑定点击事件 treeC .append("svg:text") .attr("class", "isExpand") .attr("x", "0") .attr("dx", 90) .attr("dy", function(d) {
return forUpward ? -33.5 : -38; }) .attr("text-anchor", "middle") .style("fill", '#fff') .style('cursor', 'pointer') .style('font-size', 20) .text(function(d) {
return d.name == "origin"? '':'×' }) .on("click", editBtnClick) .on("mouseenter", nodeOut) .on("mouseleave", nodeOut); // Transition nodes to their new position.原有节点更新到新位置 var nodeUpdate = node.transition() .duration(duration) .attr('transform', function(d) {
return 'translate(' + d.x + ',' + d.y + ')'; }); nodeUpdate.select('text').style('fill-opacity', 1) 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; }); //箭头(下半部分) var markerDown = svg .append("marker") .attr("id", "resolvedDown") .attr("markerUnits", "strokeWidth") //设置为strokeWidth箭头会随着线的粗细发生变化 .attr("markerUnits", "userSpaceOnUse") .attr("viewBox", "0 -5 10 10") //坐标系的区域 .attr("refX", 51) //箭头坐标 .attr("refY", 0) .attr("markerWidth", 10) //标识的大小 .attr("markerHeight", 10) .attr("orient", "90") //绘制方向,可设定为:auto(自动确认方向)和 角度值 .attr("stroke-width", 2) //箭头宽度 .append("path") .attr("d", "M0,-5L10,0L0,5") //箭头的路径 .attr("fill", "#128bed") .attr("fill-opacity", 1); //箭头颜色 //箭头(上半部分) var markerUp = svg .append("marker") .attr("id", "resolvedUp") .attr("markerUnits", "strokeWidth") //设置为strokeWidth箭头会随着线的粗细发生变化 .attr("markerUnits", "userSpaceOnUse") .attr("viewBox", "0 -5 10 10") //坐标系的区域 .attr("refX", -50) //箭头坐标 .attr("refY", 0) .attr("markerWidth", 10) //标识的大小 .attr("markerHeight", 10) .attr("orient", "90") //绘制方向,可设定为:auto(自动确认方向)和 角度值 .attr("stroke-width", 2) //箭头宽度 .append("path") .attr("d", "M0,-5L10,0L0,5") //箭头的路径 .attr("fill", "#128bed") .attr("fill-opacity", 1); //箭头颜色 // 折线及三角形样式 link.enter() .insert("path", "g") .attr("class", link_class) .attr("stroke", function(d) {
return "#bbb"; }) .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, d, }); }) .attr("marker-end", function(d) {
return forUpward ? "url(#resolvedUp)" : "url(#resolvedDown)"; }) //根据箭头标记的id号标记箭头; .attr("id", function(d, i) {
return "mypath" + i; }); link.transition() .duration(duration) .attr('d', function(d) {
return diagonal({
d }); }); link.exit().transition() .duration(duration) .attr('d', function(d) {
var o = {
x: source.x, y: source.y }; return diagonal({
source: o, target: o, d }); }) .remove(); nodes.forEach(function(d) {
d.x0 = d.x; d.y0 = d.y; }); //添加动态关系线 function nodeHover(d, i) {
if (d.name != "origin") {
if (d.parent.direction == "downward") {
var links = d3.selectAll(".downwardLink")[0]; //当前节点的子级节点 links.map((item, index) => {
if (item.__data__.source.id == d.id) {
if (d.children) {
d.children.forEach((citem) => {
if (item.__data__.target.id == citem.id) {
d3.select(item).attr( "class", "downwardLink downLine" ); } }); } } else if ( item.__data__.source.id == d.parent.id && item.__data__.target.id == d.id ) {
d3.select(item).attr("class", "downwardLink downLine"); } }); //递归处理当前节点的祖先节点 function changeUpLink(d) {
links.map((item, index) => {
if (d.name != "origin") {
if ( item.__data__.source.id == d.parent.id && item.__data__.target.id == d.id ) {
d3.select(item).attr("class", "downwardLink downLine"); } } }); if (d.depth > 1) {
if (!d.parent) {
return; } else {
changeUpLink(d.parent); } } } // changeUpLink(d, true); } else {
var links = d3.selectAll(".upwardLink")[0]; for (let i = 0; i < links.length; i++) {
let item = links[i]; if (item.__data__.source.id == d.id) {
if (d.children) {
d.children.forEach((citem) => {
if (item.__data__.target.id == citem.id) {
// console.log(item) d3.select(item).attr("class", "upwardLink upLine"); } }); } } else if ( item.__data__.source.id == d.parent.id && item.__data__.target.id == d.id ) {
d3.select(item).attr("class", "upwardLink upLine"); } } function cancelUpLink(d) {
for (let i = 0; i < links.length; i++) {
let item = links[i]; if (d.name != "origin") {
if ( item.__data__.source.id == d.parent.id && item.__data__.target.id == d.id ) {
d3.select(item).attr("class", "upwardLink upLine"); } } } if (d.parent) {
cancelUpLink(d.parent); } } // cancelUpLink(d); } } } function nodeOut(d, i) {
if (d.name != "origin") {
if (d.parent.direction == "downward") {
var links = d3.selectAll(".downwardLink")[0]; for (let i = 0; i < links.length; i++) {
let item = links[i]; if ( d3.select(item).attr("class").indexOf("downLine") != "-1" ) {
d3.select(item).attr("class", "downwardLink"); } } } else {
var links = d3.selectAll(".upwardLink")[0]; for (let i = 0; i < links.length; i++) {
let item = links[i]; if (d3.select(item).attr("class").indexOf("upLine") != "-1") {
d3.select(item).attr("class", "upwardLink"); } } } } } 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); simpleChange1(shortNameShow) editChange1(editShow) } else {
// gqctt({
// beijingCrid: d.direction == 'upward' ? d.beijingCrid : d.pbeijingCrid, // direction: d.direction // }).then(res => {
// if (res.code === 0) {
// if (d.direction == 'upward') {
// d.children = res.result.investorList // d.children.map(item => {
// item.amount = Number(item.subConAm).toFixed(2) // item.isKey = true // item.percent = 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.isKey = item.subComBl >= 0.25 && item.type == 1 // item.direction = direction // item.holderPercent = item.percent // }) // } else {
// d.children = res.result.holderList // d.children.map(item => {
// item.amount = Number(item.subConAm).toFixed(2) // item.isKey = true // item.percent = 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.isKey = item.subComBl >= 0.25 && item.type == 1 // item.direction = direction // item.holderPercent = item.percent // }) // } // d._children = null; // if (d.name == 'origin') {
// d.children.forEach(expand); // } // d3.select(this).text('-') // update(d, originalData, g) // } // }) } } } function editBtnClick(d) {
if (d.name == 'origin') {
return; } else {
const filterId = (data, id) => {
if (!Array.isArray(data)) {
return data } return data.filter(item => {
if ('children' in item) {
item.children = filterId(item.children, id) } else if ('_children' in item) {
item._children = filterId(item._children, id) } return (item.id === undefined || item.id !== id) }) } const filtredData = filterId([originalData], d.id) update(d, filtredData[0], 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 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); } };
混入的jsD3Mixin.js
/ * 全屏 toggleFullScreen * 保存 downloadImpByChart */ export const D3Mixin = {
data() {
return {
isFullscreen: true, } }, methods: {
downloadSvgFn(svg, width, height, chartName, rootName) {
var serializer = new XMLSerializer() var source = '<?xml version="1.0" standalone="no"?>\r\n' + serializer.serializeToString(svg) var image = new Image() image.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(source) image.onload = function () {
var canvas = document.createElement('canvas') canvas.width = width + 40; canvas.height = height + 40; var context = canvas.getContext('2d'); context.rect(0, 0, width + 40, height + 40); context.fillStyle = '#fff'; context.fill(); context.drawImage(image, 20, 20); var url = canvas.toDataURL("image/png"); var a = document.createElement("a"); a.download = chartName + '-' + rootName + ".png"; a.href = url; a.click(); return } }, downloadImpByChart(chartName, rootName, zoomClassName = '') {
//得到svg的真实大小 let box = document.querySelector('svg').getBBox(), x = box.x, y = box.y, width = box.width, height = box.height; if (zoomClassName) {
//查找zoomObj var zoomObj = svg.getElementsByClassName(zoomClassName.replace(/\./g, ''))[0]; if (!zoomObj) {
return false; } /*------这里是处理svg缩放的--------*/ var transformMath = zoomObj.getAttribute('transform'), scaleMath = zoomObj.getAttribute('transform'); if (transformMath || scaleMath) {
var transformObj = transformMath.match(/translate\(([^,]*),([^,)]*)\)/), scaleObj = scaleMath.match(/scale\((.*)\)/); if (transformObj || scaleObj) {
//匹配到缩放 var translateX = transformObj[1], translateY = transformObj[2], scale = scaleObj[1]; x = (x - translateX) / scale; y = (y - translateY) / scale; width = width / scale; height = height / scale; } } } //克隆svg var node = svg.cloneNode(true); //重新设置svg的width,height,viewbox node.setAttribute('width', width); node.setAttribute('height', height); node.setAttribute('viewBox', [x, y, width, height]); if (zoomClassName) {
var zoomObj = node.getElementsByClassName(zoomClassName.replace(/\./g, ''))[0]; /*-------------清楚缩放元素的缩放-------------*/ zoomObj.setAttribute('transform', 'translate(0,0) scale(1)'); } this.downloadSvgFn(node, width, height, chartName, rootName); }, 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://haidsoft.com/126959.html