基于jquery的树形结构图

502 阅读1分钟

//2018-01-05 基于jquery的树形结构图

function Tree(options){
    var opts = $.extend({
        node : null,
        data : null,
        dataType : 'json',
        type : 'get',
        callback : null,
        createBack : null,
        clickBack : null
    },options);
    this.node = opts.node;
    this.orgData = opts.data;
    this.dataType = opts.dataType;
    this.type = opts.type;
    this.data = $.extend(true,[],opts.data);
    this.createBack = opts.createBack;
    this.clickBack = opts.clickBack;
    this.callback = opts.callback;
    this.hang = false;
};
Tree.prototype = {
    constructor : Tree,
    trigger : function(){
        var _this = this;
        if(!_this.node || !_this.node.length || !_this.data){return}
        _this.initData(_this.data);
    },
    //初始化数据
    initData:function(data,parent){
        var _this = this;
        for(var i = 0,len = data.length; i < len; i++){
            var curData = data[i];
            //curData.id += + new Date();
            //设置父级
            if(parent){
                curData.parent = parent;
            }
            //设置路由
            _this.setRoute(curData,i);
            //创建dom
            _this.createDom(curData);
            //是否存在子集
            if(curData.children){
                _this.initData(curData.children,curData);
            }
        }
    },
    //创建dom
    createDom : function(data){
        var _this = this;
        var html = [];
        var level = data.route.split('-').length;
        html.push("<div class='group-panel' data-node='group' id='" + data.id + "' data-route='" + data.route + "'>");
        html.push("<div class='text-panel " + "level-" + level + "' data-node='txt'>");
        html.push("<span title='" + data.txt + "'>"+ data.txt +"</span>");
        html.push("<b class='x-line vo-null'>y</b>");
        html.push("<a href='javascript:;' data-node='btn' class='btn vo-null'>按钮</a>");
        html.push("</div>");
        html.push("<div class='children-panel " + "level-" + level + "'  data-node='children'></div>");
        html.push("<div class='operate-panel'>");
        html.push("<b class='y-line vo-null'>y</b>");
        html.push("</div>");
        html.push("</div>");
        var dom = $(html.join(''));
        if(typeof _this.createBack  == 'function'){
            _this.createBack.call(dom.get(0),data);
        };
        //是否隐藏分支
        if(!data.children){
            dom.children("[data-node='txt']").find("[data-node='btn']").hide();
        }
        //插入
        if(!data.parent){
            _this.node.append(dom);
        }else{
            $('#' + data.parent.id).children("[data-node='children']").append(dom);
            $('#' + data.parent.id).children("[data-node='txt']").find("[data-node='btn']").show();
        }
        //绑定点击事件
        _this.bindClickEve(dom);
    },
    //设置路由
    setRoute : function(data,index){
        data.index = index;
        if(!data.parent){
            data.route = '' + index;
            return;
        }
        var route = '',
            parent = data.parent;
        while(parent){
            route = parent.index + '-' + route;
            parent = parent.parent;
        }
        route = route + index;
        data.route = route;
    },
    //获取当前data
    getCurData : function(route){
        var _this = this;
        var data = null;
        for(var i = 0,len = route.length; i < len; i++){
            data = data ? data.children[route[i]] : _this.data[route[i]];
        }
        return data;
    },
    //点击文本
    handleClickTxt : function(){
        var _this = this;
        return function(){
            var $this = $(this);
            var $parent = $this.parents("[data-node='group']").eq(0);
            var route =  $parent.attr('data-route').split('-'),
                id = $parent.attr('id');
            //查询原始数据
            var curData = _this.getCurData(route);
            //校验数据
            if(!curData || (curData.id + '') != id){
                alert('查询错误');
                return false;
            }
            //是否为跳转连接
            if(curData.link){
                window.location.href = curData.link;
                return false;
            }
            //判断是否为展开状态
            var isVisible = $parent.children("[data-node='children']").is(':visible');
            //展开点击的回调
            if(!isVisible && typeof _this.clickBack == 'function'){
                _this.clickBack.call($parent.get(0),curData);
            }
            //是否请求异步数据
            if(curData.asyn){
                _this.handleAjax(curData);
            }
            //判断是否展开
            var node = $('#' + curData.id);
            if(isVisible){
                _this.handleClose(node);
            }else{
                _this.handleSelected(route);
            }
            //设置选中
            
            return false;
        };
    },
    //选中
    handleSelected : function(route){
        var _this = this;
        var data = null;
        _this.node.find('.on').removeClass('on');
        for(var i = 0,len = route.length; i < len; i++){
            data = data ? data.children[route[i]] : _this.data[route[i]];
            //关闭同级
            var siblings = $('#' + data.id).siblings();
            _this.handleClose(siblings);
            if(i == len - 1){
                var node = $('#' + data.id);
                if(data.children){
                    _this.handleOpen(node);
                }
                node.children("[data-node='txt']").addClass('on');
            }
        }
    },
    //绑定点击事件
    bindClickEve : function(node){
        var _this = this;
        node.find("[data-node='txt']").on('click',_this.handleClickTxt());
    },
    //ajax
    handleAjax : function(parent){
        var _this = this;
        //挂起状态或者已存在子集不发送请求
        if(_this.hang || parent.children){return}
        _this.hang = true;
        $.ajax({
            url : parent.asyn + '?id=' + parent.id,
            type : _this.type,
            dataType : _this.dataType,
            data : _this.node.serialize(),
            success : function(rdata){
                if(typeof _this.callback == 'function'){
                    _this.callback.call(_this,parent,rdata);
                }
                _this.hang = false;
            },
            error : function(){
                _this.hang = false;
            }
        });
    },
    //展开
    handleOpen :function(node){
        if(!node || !node.length){return}
        if(node.children("[data-node='children']").is(':visible')){
            return;
        }
        node.children("[data-node='children']").show(300);
        node.children("[data-node='txt']").addClass('open');
    },
    //关闭
    handleClose : function(node){
        if(!node || !node.length){return}
        if(!node.children("[data-node='children']").is(':visible')){
            return;
        }
        node.find("[data-node='children']").hide(300);
        node.find("[data-node='txt']").removeClass('open');
    }
};

//******针对jquery拓展******
$.fn.tree = function(options){
    if(!$(this).length){return}
    var opts = $.extend({
        data : null,
        dataType : 'json',
        type : 'get',
        createBack : null,
        clickBack : null,
        callback : null
    },options);
    if(!opts.data){return}
    //循环实例化
    $(this).each(function(){
        new Tree({
            node : $(this),
            dataType : opts.dataType,
            type : opts.type,
            data : opts.data,
            createBack : opts.createBack,
            clickBack : opts.clickBack,
            callback : opts.callback
        }).trigger();
    });
};

sCss:

.vo-tree{
    width: 220px;
    height: 500px;
    overflow: auto;
    .group-panel{
        position: relative;
        .text-panel{
            display: block;
            background: #ffffff;
            width: 80px;
            height: 24px;
            line-height: 24px;
            color: #999;
            margin: 0 0 5px;
            font-size: 14px;
            position: relative;
            z-index: 10;
            span{
                display: block;
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
                cursor: pointer;
            }
            .x-line{
                position: absolute;
                left: -15px;
                top: 50%;
                height: 1px;
                width: 12px;
                background: url(../../../image/virgo/tree-line.png) 0 0 repeat-x;
            }
            .btn{
                position: absolute;
                left: -20px;
                top: 50%;
                width: 9px;
                height: 9px;
                margin: -4px 0 0;
                background: url(../../../image/virgo/tree-icon.png) 0 0 repeat-y;
            }
            &.open{
                .btn{
                    background-position: -18px 0;
                }
            }
            &.level-1{
                width: 218px;
                height: 70px;
                line-height: 70px;
                border: 1px solid #ececec;
                text-indent: 3em;
                font-size: 16px;
                .x-line{
                    display: none;
                }
            }
            &.level-2{
                width: 198px;
                height: 36px;
                line-height: 36px;
                background: #91b0c4;
                text-indent: 2em;
                color: #ffffff;
                font-size: 16px;
            }
            &:hover{
                text-decoration: underline;
            }
        }
        .children-panel{
            padding: 5px 0 0;
            margin: -5px 0 0 20px;
            display: none;
        }
        .operate-panel{
            .y-line{
                position: absolute;
                left: 4px;
                top: 0px;
                height: 100%;
                width: 1px;
                background: url(../../../image/virgo/tree-line.png) 0 0 repeat-y;
            }
        }
    }
}

使用:

<div class="vo-tree"></div>
<script type="text/javascript">
    var data = [
        {
            id : 1,
            name : 'aaa',
            txt : '标题1',
            asyn : '../request/tree.json',
            children:[
                {
                    id : 11,
                    name : 'aaa',
                    txt : '标题1',
                    asyn : '../request/tree.json',
                    children:[
                        {
                            id : 12,
                            name : 'aaa',
                            txt : '标题1',
                            asyn : '../request/tree.json',
                            children:[
                                {
                                    id : 13,
                                    name : 'aaa',
                                    txt : '标题1'
                                },
                                {
                                    id : 23,
                                    name : 'bbb',
                                    txt : '标题2'
                                },
                                {
                                    id : 33,
                                    name : 'ddd',
                                    txt : '标题3'
                                }
                            ]
                        },
                        {
                            id : 22,
                            name : 'bbb',
                            txt : '标题2',
                            asyn : '../request/tree.json'
                        },
                        {
                            id : 32,
                            name : 'ddd',
                            txt : '标题3',
                            asyn : '../request/tree.json'
                        }
                    ]
                },
                {
                    id : 21,
                    name : 'bbb',
                    txt : '标题2',
                    asyn : '../request/tree.json'
                },
                {
                    id : 31,
                    name : 'ddd',
                    txt : '标题3',
                    asyn : '../request/tree.json'
                }
            ]
        },
        {
            id : 2,
            name : 'bbb',
            txt : '标题2',
            asyn : '../request/tree.json'
        },
        {
            id : 3,
            name : 'ddd',
            txt : '标题3',
            asyn : '../request/tree.json'
        }
    ]
    //调用
    $(".vo-tree").tree({
        data : data,
        createBack : function(data){
            //创建完成回调
            console.log('创建完成回调')
            console.log(this)
            console.log(data)
            
        },
        clickBack : function(data){
            //点击回调
            console.log('点击回调')
            console.log(this);
            console.log(data);
        },
        callback : function(parent,rdata){
            var _this = this;
            parent.children = rdata.data;
            //初始化数据
            _this.initData(rdata.data,parent);
            //执行打开
            _this.handleOpen($('#' + parent.id));
        }
    });
</script>