マルチレベルのデータをツリーで表示する方法(またはツリー表示と呼ぶ)(一)

10412 ワード

私たちは仕事の中でいつもいろいろな問題に直面して、いくつかは繰り返しの仕事かもしれないし、いくつかはあなたから見ればでたらめかもしれませんが、やはり実現しなければなりません.BOSSはそこにあるので、業績もそこで待っています.この間、会議が終わった後にまたいくつかの新しい需要を受け取って、実は1つのページですべてのデータを展示して、これらのデータは機械の状態を含みます:メモリ、ディスク、CPU、および応用自身の情報です:インタフェースの要求時間、95 line、99 line、エラー率、エラーなどの情況を報告します.どのように展示するかについては、私も知りません.それはいいでしょう.需要はいつもはっきりしていません.1版を作ってから、これは私が望んでいるもので、あるいは、これは私が望んでいるものではありません.私の印象では、ElementUIにはtreeコントロールが直接木の形で展示されていますが、それ以外に何がありますか?baidu、google、その他の技術類のウェブサイトに感謝して、検索を通じて知って、ディレクトリのVUEに基づいて少なくとも使うことができます:1.Elementuiのtreeコンポーネント2.d 3樹形展示3.echartsにおけるグラフィック表示タイプ「tree」4.東方富図譜で使われているjsMindの上のこれらの中d 3、echartsは子結点に対して、ユーザーのインタラクションはあまりよくなくて、少なくともこの時良い方法を見つけて実現していません.element uiの中のtreeコンポーネントの表現形式も大物の味に合わないと感じています.東方富は終わってから何気なく発見したので、上の4種類は私には役に立ちません.私はtableをtreeに変換するという方法で実現しているので、tableはtableをセットしたり、実際には物を使ったりすることができて、使いやすいので、後で何かを増やすのがもっと簡単です.やはりいくつかの実現に対して少し待っています:1.Elementuiのtreeコンポーネントの例は公式サイトが多いです
        2.d 3ツリーディスプレイ、packageをインストールし、プロジェクトで参照


/**
 *    
 */
//  
const dataset = {
    name:"  ",
    children:[
        {
            name:"  ",
            children:[
                {name:"  " ,value:100},
                {name:"  ",value:100},
                {name:"  ",value:100},
                {name:"  ",value:100}
            ]
        },
        {
            name:"  " , 
            children:
            [
                {name:"    "},
                {name:"    "},
                {name:"   "},
                {name:"  "}
            ]
        },
        {
            name:"  " , 
            children:
            [
                {name:"    "},
                {name:"    "},
                {name:"   "},
                {name:"  "}
            ]
        }
    ]
}
import * as d3 from 'd3'
export default {
    name: 'Scale',
    data() {
        return {
            id: '',
            zoom: null,
            index: 0,
            duration: 750,
            root: null,
            nodes: [],
            links: [],
            dTreeData: null,
            transform: null,
            margin: { top: 20, right: 90, bottom: 30, left: 120 }
        }
    },
    methods: {
        uuid () {
            function s4 () {
                return Math.floor((1 + Math.random()) * 0x10000)
                    .toString(16)
                    .substring(1)
            }
            return (
                s4() + s4() + '-' + s4() + '-' + s4() +  '-' + s4() + '-' + s4() + s4() + s4()
            )
        },
        /**
         * @description        
         */
        getRoot () {
            let root = d3.hierarchy(dataset, d => { 
                return d.children
            })
            root.x0 = this.height / 2
            root.y0 = 0
            return root
        },
        /**
         * @description     ,  or  
         */
        clickNode (d) {
            if (!d._children && !d.children)
                return
            if (d.children) {
                this.$set(d, '_children', d.children)          
                d.children = null
            } else {
                this.$set(d, 'children', d._children)
                d._children = null
            }
            this.$nextTick(
                () => {
                    this.update(d)                
                }
            )
        },
        diagonal (s, d) {
            return `M ${s.y} ${s.x}
                    C ${(s.y + d.y) / 2} ${s.x},
                    ${(s.y + d.y) / 2} ${d.x},
                    ${d.y} ${d.x}`
        },
        /**
         * @description      node   link  
         */
        getNodesAndLinks () {
            // treemap generate new x、y coordinate according to root node, 
            // so can‘t use computed propter of vue
            this.dTreeData = this.treemap(this.root)
            this.nodes = this.dTreeData.descendants()
            this.links = this.dTreeData.descendants().slice(1)
        },
        /**
         *   Node  text  
         */
        clickText(d)    {
            console.log(d)
            window.open("http://www.baidu.com","_blank")

        },

        /** 
         * @description    Dom    
         */
        update (source) {
            this.getNodesAndLinks()
            this.nodes.forEach(d => { 
                d.y = d.depth * 180
            })
            // *************************** Nodes section *************************** //
            // Update the nodes...
            const svg = d3.select(this.$el).select('svg.d3-tree')
            const container = svg.select('g.container')
            let node = container.selectAll('g.node')
                .data(this.nodes, d => {
                    return d.id || (d.id = ++this.index)
                }) 
            // Enter any new sources at the parent's previous position.
            let nodeEnter = node.enter().append('g')
                .attr('class', 'node')
                .on('click', this.clickNode)
                .attr('transform', d => {
                    return 'translate(' + source.y0 + ',' + source.x0 + ')'
                })  
            nodeEnter.append("circle")
                .attr("r", 10)
                .style("fill", function(d) { return d.children || d._children ? "lightsteelblue" : "#fff"; });

            nodeEnter.append("a")
                .attr("xlink:href", function(d) { return d.data.name; })
                .append("text")             
                .attr("x", function(d) { return d.children|| d._children ? -15 : 15; })
                .attr("dy", ".35em")
                .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
                .text(function(d) { 
                    if(d.data.value){
                        return d.data.name + d.data.value
                    } else {
                        return d.data.name
                    }
                })
                
            

                .style("fill-opacity", 1e-6);
            // Transition nodes to their new position.
            let nodeUpdate = nodeEnter.merge(node)
                .transition()
                .duration(this.duration)
                .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });

            nodeUpdate.select("circle")
                .attr("r", 10)
                .attr("fill", "white")
                .attr("stroke", "blue")
                .attr("stroke-width", 1)
                .style("fill", function(d) { return d.children || d._children ? "lightsteelblue" : "#fff"; });

            nodeUpdate.select("text")
                .style("fill-opacity", 1);

            // Transition exiting nodes to the parent's new position.
            let nodeExit = node.exit()
                .transition()
                .duration(this.duration)
                .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
                .remove();

            nodeExit.select("circle")
                .attr("r", 1e-6);

            nodeExit.select("text")
                .style("fill-opacity", 1e-6);

            // *************************** Links section *************************** //
            // Update the links…
            let link = container.selectAll('path.link')
                .data(this.links, d => { return d.id })
            
            // Enter any new links at the parent's previous position.
            let linkEnter = link.enter().insert("path", "g")
                .attr("class", "link")
                .attr("d", d => {
                    let o = {x: source.x0, y: source.y0};
                    return this.diagonal(o, o)
                })
                .attr("fill", 'none')
                .attr("stroke-width", 1)
                .attr('stroke', '#ccc')
            // Transition links to their new position.
            let linkUpdate = linkEnter.merge(link)
            linkUpdate.transition()
                .duration(this.duration)
                .attr('d', d => { return this.diagonal(d, d.parent) })

            // Transition exiting nodes to the parent's new position.
            link.exit().transition()
                .duration(this.duration)
                .attr("d", d => {
                    let o = {x: source.x, y: source.y};
                    return this.diagonal(o, o)
                })
                .remove();

            // Stash the old positions for transition.
            this.nodes.forEach(d => {
                d.x0 = d.x
                d.y0 = d.y
            })
        },
        /** 
         * @description control the canvas zoom to up or down
         */
        zoomed () {
            d3.select(this.$el).select('g.container').attr('transform', d3.event.transform)
        }
    },
    created() {
        this.id = this.uuid()
    },
    mounted () {
        //  svg  
        this.width = document.getElementById(this.id).clientWidth
        this.height = document.getElementById(this.id).clientHeight
        const svg = d3.select(this.$el).select('svg.d3-tree')
            .attr('width', this.width)
            .attr('height', this.height)
        const transform = d3.zoomIdentity.translate(this.margin.left, this.margin.top).scale(1)    
        const container = svg.select('g.container')
        // init zoom behavior, which is both an object and function
        this.zoom = d3.zoom()
            .scaleExtent([1 / 2, 8])
            .on('zoom', this.zoomed)
        container.transition().duration(750).call(this.zoom.transform, transform)
        svg.call(this.zoom)
        this.root = this.getRoot()
        this.update(this.root)
    },
    computed: {
        treemap () {
            return d3.tree().size([this.height, this.width])
        }
    }
}