直接上代码
npm i d3 --save
<template>
<div class="d3">
<div :id="id" class="d3-content"></div>
</div>
</template>
<script>
import * as d3 from "d3";
export default {
props: {
data: Object,
nodeWidth: {
type: Number,
default: 340,
},
nodeHeight: {
type: Number,
default: 40,
},
active: {
type: String,
default: "",
},
},
data() {
return {
id: "TreeMap" + randomString(4),
deep: 0,
treeData: null,
show: true,
demoData: {
label: "中国",
url: "https://baike.baidu.com/item/%E4%B8%AD%E5%9B%BD/1122445?fr=aladdin",
children: [
{
label: "浙江45468761321",
disabled: true,
children: [
{ label: "杭州999999999" },
{ label: "宁波" },
{ label: "温州" },
{ label: "绍兴" },
],
},
{
label: "广西",
children: [
{
label: "桂林56465465465464",
children: [
{ label: "秀峰区" },
{ label: "叠彩区" },
{ label: "象山区" },
{ label: "七星区" },
],
},
{ label: "南宁" },
{ label: "柳州" },
{ label: "防城港" },
],
},
],
},
};
},
mounted() {
this.$nextTick(() => {
this.drawMap();
window.handleCustom=this.handleCustom;
});
},
methods: {
drawMap() {
let that = this;
let data = {};
if (this.data && JSON.stringify(this.data) !== "{}") {
data = this.data;
} else {
data = this.demoData;
}
if (!this.treeData) {
this.treeData = data;
} else {
d3.select("#" + this.id)
.selectAll("svg")
.remove();
}
let leafList = [];
getTreeLeaf(data, leafList);
let leafNum = leafList.length;
let TreeDeep = getDepth(data);
let mapPaddingLR = 10;
let mapPaddingTB = 0;
let mapWidth = this.nodeWidth * TreeDeep + mapPaddingLR * 2;
let mapHeight = (this.nodeHeight - 4) * leafNum + mapPaddingTB * 2;
let svgMap = d3
.select("#" + this.id)
.append("svg")
.attr("width", mapWidth)
.attr("height", mapHeight)
.style("margin", "0px");
let treeMap = svgMap
.append("g")
.attr(
"transform",
"translate(" +
mapPaddingLR +
"," +
(mapHeight / 2 - mapPaddingTB) +
")"
);
let treeData = d3
.tree()
.nodeSize(
[this.nodeHeight, this.nodeWidth]
)
.separation(function (a, b) {
let rate =
(a.parent == b.parent
? b.children
? b.children.length / 2
: 1
: 2) / a.depth;
if (rate < 0.7) {
rate = 0.7;
}
return rate;
})(
d3.hierarchy(data).sum(function (node) {
return node.value;
})
);
let Bézier_curve_generator = d3
.linkHorizontal()
.x(function (d) {
return d.y;
})
.y(function (d) {
return d.x;
});
treeMap
.selectAll("path")
.data(treeData.links())
.enter()
.append("path")
.attr("d", function (d) {
var start = {
x: d.source.x,
y:
d.source.y +
10 +
(d.source.data.link ? getPXwidth(d.source.data.link) + 10 : 0) +
getPXwidth(d.source.data.label) +
(!d.source.data.children?82:0) +
20,
};
var end = { x: d.target.x, y: d.target.y };
return Bézier_curve_generator({ source: start, target: end });
})
.attr("fill", "none")
.attr("stroke", "#00AB6B")
.attr("stroke-width", 1);
let groups = treeMap
.selectAll("g")
.data(treeData.descendants())
.enter()
.append("g")
.attr("transform", function (d) {
var cx = d.x;
var cy = d.y;
return "translate(" + cy + "," + cx + ")";
});
groups
.append("circle")
.on("click", function (event, node) {
let data = node.data;
if (data.children) {
data.childrenTemp = data.children;
data.children = null;
} else {
data.children = data.childrenTemp;
data.childrenTemp = null;
}
that.drawMap();
})
.attr("cursor", "pointer")
.attr("r", 4)
.attr("fill", function (d) {
if (d.data.childrenTemp) {
return "#00AB6B";
} else {
return "white";
}
})
.attr("stroke", "#00AB6B")
.attr("stroke-width", 1);
groups
.append("rect")
.attr("x", 8)
.attr("y", -10)
.attr("width", function (d) {
return d.data.link ? getPXwidth(d.data.link) + 10 : 0;
})
.attr("height", 22)
.attr("fill", "red")
.attr("border", "blue")
.attr("rx", 4);
groups
.append("text")
.attr("x", 12)
.attr("y", -5)
.attr("dy", 10)
.attr("fill", "white")
.attr("font-size", 12)
.text(function (d) {
return d.data.link;
});
groups
.append("text")
.on("click", function (event, node) {
let data = node.data;
if (data.disabled) {
return;
}
if (data.url) {
window.open(data.url);
that.$emit("activeChange", "map");
return;
}
if (data.dicType) {
that.$emit("dicTypeChange", data.dicType);
}
if (data.prop) {
that.$emit("activeChange", data.prop);
}
})
.attr("x", function (d) {
return 12 + (d.data.link ? getPXwidth(d.data.link) + 10 : 0);
})
.attr("fill", function (d) {
if (d.data.prop === that.active) {
return "#409EFF";
}
})
.attr("font-weight", function (d) {
if (d.data.prop === that.active) {
return "bold";
}
})
.attr("font-size", 14)
.attr("cursor", function (d) {
if (d.data.disabled) {
return "not-allowed";
} else {
return "pointer";
}
})
.attr("y", -5)
.attr("dy", 10)
.attr("slot", function (d) {
return d.data.prop;
});
groups
.append("foreignObject")
.attr("width", (d) => {
return getPXwidth(d.data.label) + 22 + (!d.data.children?82:0);
})
.attr("height", 100)
.attr("x", function (d) {
return 12 + (d.data.link ? getPXwidth(d.data.link) + 10 : 0);
})
.on("click", function (event, node) {
})
.attr("y", -10)
.append("xhtml:div")
.style("font", '14px "Helvetica Neue"')
.html((d) => {
let _html = `
<div class="custom-html">
<div>${d.data.label}</div>
</div>`;
if(!d.data.children){
_html = `
<div class="custom-html">
<div>${d.data.label}</div>
<div οnclick="handleCustom(${1})"><i class="iconfont"></i>视频课</div>
</div>`;
}
return _html
});
},
handleCustom(data){
debugger
}
},
};
function getDepth(json) {
var arr = [];
arr.push(json);
var depth = 0;
while (arr.length > 0) {
var temp = [];
for (var i = 0; i < arr.length; i++) {
temp.push(arr[i]);
}
arr = [];
for (var i = 0; i < temp.length; i++) {
if (temp[i].children && temp[i].children.length > 0) {
for (var j = 0; j < temp[i].children.length; j++) {
arr.push(temp[i].children[j]);
}
}
}
if (arr.length >= 0) {
depth++;
}
}
return depth;
}
function getTreeLeaf(treeData, leafList) {
if (Array.isArray(treeData)) {
treeData.forEach((item) => {
if (item.children && item.children.length > 0) {
getTreeLeaf(item.children, leafList);
} else {
leafList.push(item);
}
});
} else {
if (treeData.children && treeData.children.length > 0) {
getTreeLeaf(treeData.children, leafList);
} else {
leafList.push(treeData);
}
}
}
function getStringSizeLength(string) {
return string.replace(/[\u0391-\uFFE5]/g, "aa").length;
}
function randomString(strLength) {
strLength = strLength || 32;
let strLib = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz";
let n = "";
for (let i = 0; i < strLength; i++) {
n += strLib.charAt(Math.floor(Math.random() * strLib.length));
}
return n;
}
function getPXwidth(str, fontSize = "12px", fontFamily = "Microsoft YaHei") {
var span = document.createElement("span");
var result = {};
result.width = span.offsetWidth;
result.height = span.offsetHeight;
span.style.visibility = "hidden";
span.style.fontSize = fontSize;
span.style.fontFamily = fontFamily;
span.style.display = "inline-block";
document.body.appendChild(span);
if (typeof span.textContent != "undefined") {
span.textContent = str;
} else {
span.innerText = str;
}
result.width = parseFloat(window.getComputedStyle(span).width) - result.width;
return result.width;
}
</script>
<style lang="scss" scoped>
.d3 {
position: relative;
overflow: hidden;
width: calc(100%);
min-height: 500px;
overflow-x: scroll;
.d3-content {
position: absolute;
width: max-content;
::v-deep .custom-html {
display: flex;
div {
i {
font-size: 12px;
margin-right: 4px;
}
&:nth-child(2) {
margin-left: 10px;
background: #f2faf7;
border: 0.5px solid #c3e7da;
border-radius: 4px;
color: #00ab6b;
font-size: 12px;
padding: 0 4px;
height: 20px;
cursor: pointer;
}
}
}
}
}
</style>