直接上代码
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>