记一次ztree和treegrid改造异步方法

在我们项目中,一般用ztree和treegrid插件来展示分层级的部门树(表),有一次客户的部门体量比较大(数十万),导致ztree和treegrid都会直接卡死,于是有了改造为异步的需求;
直接看改造

首先是ztree

首先是ztree,ztree插件原生是支持异步使用的,只是初始化方法和同步略有区别;详细描述和api可以查看官网文档:http://www.treejs.cn/v3/api.php
官网demo示例:
在这里插入图片描述
代码:

//设置参数
var setting = {
	data: {
		simpleData: {
			enable: true,
			idKey: "id",
			pIdKey: "parentId",
			rootPId: -1
		},
		key: {
			url:"nourl"
		}
	},
    async: {
        enable: true,
        url: baseURL + "sys/region/selectAsync",
        dataType: "json",
        autoParam: ["id"],
        //regionList才是真正节点
        dataFilter: function(treeId, parentNode, childNodes) {
            return childNodes.regionList;
        }
    },
    callback: {
        onAsyncSuccess: function () {
            ztree = $.fn.zTree.getZTreeObj("regionTree");
            var node = ztree.getNodeByParam("id", vm.region.parentId);
            //ztree.selectNode(node);
            if(node!=null){
                vm.region.parentName = node.name;
            }
            //分行管理员默认选中城市
            if((vm.user.userLevel == 2 || vm.user.userLevel == 4) && vm.region.method == "add") { 
                var zNodes = ztree.getNodes();
                vm.region.parentId = zNodes[0].id;
                vm.region.parentName = zNodes[0].name;
                vm.city.parentId = zNodes[0].cityId;
                vm.getcity();
            }
        }
    }
};
//初始化
getRegion: function(){
            $.fn.zTree.init($("#regionTree"), setting);
			//加载分行树(同步)
			/*$.get(baseURL + "sys/region/select", function(r){
				ztree = $.fn.zTree.init($("#regionTree"), setting, r.regionList);
				var node = ztree.getNodeByParam("id", vm.region.parentId);
				ztree.selectNode(node);
                if(node!=null){
                    vm.region.parentName = node.name;
                }
			})*/
		},

然后是treegrid

treegrid目前官方是没有提供异步使用方法的,所以它的改造要复杂很多;
需要自己重写扩展代码jquery.treegrid.extension.js文件,重些行加载流程,逻辑,点击事件等等;
直接上代码留底,为了保证项目中其他同步的树表正常使用,我重新写了一个扩展命名为 jquery.treegrid.ajax.js,在不通的页面(有的不需要异步)调用不同的扩展进行初始化就可以了。

(function($) {
    "use strict";

    $.fn.ajaxTreeTable = function(options, param) {
        // 如果是调用方法
        if (typeof options == 'string') {
            return $.fn.ajaxTreeTable.methods[options](this, param);
        }

        // 如果是初始化组件
        options = $.extend({}, $.fn.ajaxTreeTable.defaults, options || {});
        // 是否有radio或checkbox
        var hasSelectItem = false;
        var target = $(this);
        // 在外层包装一下div,样式用的bootstrap-table的
        var _main_div = $("<div class='fixed-table-container'></div>");
        target.before(_main_div);
        _main_div.append(target);
        target.addClass("table table-hover treegrid-table table-bordered");
        if (options.striped) {
            target.addClass('table-striped');
        }
        // 工具条在外层包装一下div,样式用的bootstrap-table的
        if(options.toolbar){
            var _tool_div = $("<div class='fixed-table-toolbar' style='display:none;'></div>");
            var _tool_left_div = $("<div class='bs-bars pull-left'></div>");
            _tool_left_div.append($(options.toolbar));
            _tool_div.append(_tool_left_div);
            _main_div.before(_tool_div);
        }
        var j = 0;
        target.renderChildRows = function(data, parentNode, parentIndex, tbody){
            $.each(data, function(i, item) {
                var tr = $('<tr></tr>');
                tr.addClass('treegrid-' + parentIndex + '_' + i);
                tr.addClass('treegrid-parent-' + parentIndex);
                //tr.addClass('unknow');
                if(item[options.isParent] != true && item[options.isParent] != 'true'){
                    tr.attr('isLeaf','true');
                }
                tr.attr('trid',item[options.id]);
                target.renderRow(tr,item);
                item.isShow = true;
                tbody.after(tr);
            });
        };
        // 绘制行
        target.renderRow = function(tr,item){
            var treeColumn = options.expandColumn?options.expandColumn:(hasSelectItem?1:0)
            $.each(options.columns, function(index, column) {
                // 判断有没有选择列
                if(index==0&&column.field=='selectItem'){
                    hasSelectItem = true;
                    var td = $('<td style="text-align:center;width:36px"></td>');
                    if(column.radio){
                        var _ipt = $('<input name="select_item" type="radio" value="'+item[options.id]+'"></input>');
                        td.append(_ipt);
                    }
                    if(column.checkbox){
                        var _ipt = $('<input name="select_item" type="checkbox" value="'+item[options.id]+'"></input>');
                        td.append(_ipt);
                    }
                    tr.append(td);
                }else{
                    var td = $('<td mainid='+item[options.id]+' style="text-align:'+column.align+';'+((column.width)?('width:'+column.width):'')+'"></td>');
                    // 增加formatter渲染
                    if (column.formatter) {
                        td.html(column.formatter.call(this, item, index));
                    } else {
                        td.text(item[column.field]);
                    }
                    if(index == treeColumn){
                        var indent = '';
                        var strs = tr.attr('class').split(" ")[0].split("_");
                        var level = strs.length - 1;
                        for(var i=0; i<level-1; i++){
                            indent = indent + '<span class="treegrid-indent"></span>';
                        }
                        var span = indent + '<span class="treegrid-expander"></span>';
                        td.html(span+item[column.field]);
                    }
                    tr.append(td);
                }
                //绘制展开图标
                target.painExpend(tr);
                //添加行内点击事件
                target.addTrEvent(tr);
            });
        }
        // 加载数据
        target.load = function(parms){
            // 加载数据前先清空
            target.html("");
            // 构造表头
            var thr = $('<tr></tr>');
            $.each(options.columns, function(i, item) {
                var th = null;
                // 判断有没有选择列
                if(i==0&&item.field=='selectItem'){
                    hasSelectItem = true;
                    th = $('<th style="text-align:'+item.valign+';width:36px"></th>');
                }else{
                    th = $('<th style="text-align:'+item.valign+';padding:10px;'+((item.width)?('width:'+item.width):'')+'"></th>');
                }
                th.text(item.title);
                thr.append(th);
            });
            var thead = $('<thead class="treegrid-thead"></thead>');
            thead.append(thr);
            target.append(thead);
            // 构造表体
            var tbody = $('<tbody class="treegrid-tbody"></tbody>');
            target.append(tbody);
            // 添加加载loading
            var _loading = '<tr><td colspan="'+options.columns.length+'"><div style="display: block;text-align: center;">正在努力地加载数据中,请稍候……</div></td></tr>'
            tbody.html(_loading);
            // 默认高度
            if(options.height){
                tbody.css("height",options.height);
            }
            $.ajax({
                type : options.type,
                url : options.url,
                data : parms?parms:options.ajaxParams,
                dataType : "JSON",
                success : function(data, textStatus, jqXHR) {
                    // 加载完数据先清空
                    tbody.html("");
                    if(!data||data.length<=0){
                        var _empty = '<tr><td colspan="'+options.columns.length+'"><div style="display: block;text-align: center;">没有记录</div></td></tr>'
                        tbody.html(_empty);
                        return;
                    }
                    $.each(data, function(i, item) {
                        var tr = $('<tr></tr>');
                        tr.addClass('treegrid-' + (j + "_" + i));
                        //tr.attr('load','false');
                        //tr.addClass('unknow');
                        if(item[options.isParent] != true && item[options.isParent] != 'true'){
                            tr.attr('isLeaf','true');
                        }
                        tr.attr('trid', item[options.id]);
                        target.renderRow(tr,item);
                        item.isShow = true;
                        tbody.append(tr);
                    });
                    target.append(tbody);

                },
                error:function(xhr,textStatus){
                    var _errorMsg = '<tr><td colspan="'+options.columns.length+'"><div style="display: block;text-align: center;">'+xhr.responseText+'</div></td></tr>'
                    tbody.html(_errorMsg);
                    debugger;
                },
            });
        }

        /***  绘制展开图标 ***/
        /*target.painExpends = function(trs) {
            $.each(trs, function (index, tr) {
                if (tr.attr('isLeaf') != 'true' && tr.attr('load') == 'true') {   //$(item).hasClass('know')
                    tr.find("span:last-child").addClass('glyphicon-chevron-down').addClass('glyphicon');
                } else if (tr.attr('isLeaf') != 'true' && tr.hasClass('unknow')) {
                    tr.find("span:last-child").addClass('glyphicon-chevron-right').addClass('glyphicon');
                }

                if (tr.attr('isLeaf') != 'true' && tr.attr('load') != 'true') {
                    tr.find("span.glyphicon-chevron-right").unbind();
                    tr.find("span.glyphicon-chevron-right").click(function () {
                            target.loadChilds(tr,
                                {
                                    "id": tr.attr('trid'),
                                    "parentIndex": tr.attr('class').split(" ")[0].split("-")[1]
                                });
                        }
                    );
                }
            });
        }*/
        /***  绘制展开图标(单个) ***/
        target.painExpend = function(tr) {
            if (tr.attr('isLeaf') != 'true'){
                var span = tr.find("span.treegrid-expander");
                span.addClass('glyphicon-chevron-right').addClass('glyphicon');
                span.unbind().click(function () {
                        target.loadChilds(tr,
                            {
                                "id": tr.attr('trid'),
                                "parentIndex": tr.attr('class').split(" ")[0].split("-")[1]
                            });
                    })
            }
        }
        //添加行内点击事件
        target.addTrEvent = function(tr){
            tr.click(function(){
                if(hasSelectItem){
                    var _ipt = $(this).find("input[name='select_item']");
                    if(_ipt.attr("type")=="radio"){
                        _ipt.prop('checked',true);
                        target.find("tbody").find("tr.treegrid-selected").removeClass("treegrid-selected");
                        $(this).addClass("treegrid-selected");
                    }else{
                        if(_ipt.prop('checked')){
                            _ipt.prop('checked',false);
                            $(this).removeClass("treegrid-selected");
                        }else{
                            _ipt.prop('checked',true);
                            $(this).addClass("treegrid-selected");
                        }
                    }
                }
            });
        }

        //隐藏子节点
        target.hideChild = function(tr){
            var parentIndex = tr.attr('class').split(" ")[0].split("-")[1];
            var classArrtr = 'treegrid-parent-' + parentIndex;
            var trs = target.find("tr."+classArrtr);
            if(trs.length>=0){
                $.each(trs, function (index, tr) {
                    $(tr).css("display","none");
                    target.hideChild($(tr));
                });
            }
            var span = tr.find("span.glyphicon-chevron-down");
            span.removeClass('glyphicon-chevron-down');
            span.addClass('glyphicon-chevron-right');
            span.unbind();
            span.click(function() {
                target.showChild(tr)
            });
        }

        //展开子节点
        target.showChild = function(tr){
            var parentIndex = tr.attr('class').split(" ")[0].split("-")[1];
            var classArrtr = 'treegrid-parent-' + parentIndex;
            var trs = target.find("tr."+classArrtr);
            if(trs.length>=0){
                $.each(trs, function (index, tr) {
                    $(tr).css("display","");
                });
            }
            var span = tr.find("span.glyphicon-chevron-right");
            span.removeClass('glyphicon-chevron-right');
            span.addClass('glyphicon-chevron-down');
            span.unbind();
            span.click(function() {
                target.hideChild(tr)
            });
        }

        /****** 加载子节点数据 start ***************/
        target.loadChilds = function(parentTR,parms){
            console.log("~~~~loadChilds~~~~~~");
            $.ajax({
                type : options.type,
                url : options.url,
                data : parms?parms:options.ajaxParams,
                dataType : "JSON",
                success : function(data, textStatus, jqXHR) {
                    //parentTR.removeClass('unknow').addClass('know');
                    //parentTR.attr('load','true');
                    //处理图标 和 点击事件
                    var parentSpan = parentTR.find("span.glyphicon-chevron-right");
                    parentSpan.removeClass('glyphicon-chevron-right').addClass('glyphicon-chevron-down');
                    parentSpan.unbind().click(function () {
                        target.hideChild(parentTR);
                    });
                    if(!data||data.length<=0){
                        return;
                    }
                    target.renderChildRows(data, {"mainId":parms.id}, parms.parentIndex, parentTR);
                },
                error:function(xhr,textStatus){
                    var _errorMsg = '<tr><td colspan="'+options.columns.length+'"><div style="display: block;text-align: center;">'+xhr.responseText+'</div></td></tr>'
                    tbody.html(_errorMsg);
                    debugger;
                },
            });
        }
        /****** 加载子节点数据 end ***************/
        if (options.url) {
            target.load();
        } else {
            // 也可以通过defaults里面的data属性通过传递一个数据集合进来对组件进行初始化....有兴趣可以自己实现,思路和上述类似
        }

        return target;
    };

    // 组件方法封装........
    $.fn.ajaxTreeTable.methods = {
        // 返回选中记录的id(返回的id由配置中的id属性指定)
        // 为了兼容bootstrap-table的写法,统一返回数组,这里只返回了指定的id
        getSelections : function(target, data) {
            // 所有被选中的记录input
            var _ipt = target.find("tbody").find("tr").find("input[name='select_item']:checked");
            var chk_value =[];
            // 如果是radio
            if(_ipt.attr("type")=="radio"){
                chk_value.push({id:_ipt.val()});
            }else{
                _ipt.each(function(_i,_item){
                    chk_value.push({id:$(_item).val()});
                });
            }
            return chk_value;
        },
        // 刷新记录
        refresh : function(target, parms) {
            console.log("~~~~refresh~~~~~");
            if(parms){
                target.load(parms);
            }else{
                target.load();
            }
        },
        // 重置表格视图
        resetHeight : function(target, height) {
            target.find("tbody").css("height", height + 'px');
        }
        // 组件的其他方法也可以进行类似封装........
    };

    $.fn.ajaxTreeTable.defaults = {
        id : 'menuId',// 选取记录返回的值
        code : 'menuId',// 用于设置父子关系
        parentCode : 'parentId',// 用于设置父子关系
        rootCodeValue: null,//设置根节点code值----可指定根节点,默认为null,"",0,"0"
        data : [], // 构造table的数据集合
        type : "GET", // 请求数据的ajax类型
        url : null, // 请求数据的ajax的url
        ajaxParams : {}, // 请求数据的ajax的data属性
        expandColumn : null,// 在哪一列上面显示展开按钮
        expandAll : true, // 是否全部展开
        striped : false, // 是否各行渐变色
        columns : [],
        toolbar: null,//顶部工具条
        height: 0,
        expanderExpandedClass : 'glyphicon glyphicon-chevron-down',// 展开的按钮的图标
        expanderCollapsedClass : 'glyphicon glyphicon-chevron-right',// 缩起的按钮的图标
        isParent: 'isParent' //是否有子节点的标识
    };
})(jQuery);

然后是基于tree.table.js改造的tree.table.ajax.js,增加了一些标记(isParent),切换扩展为刚才我们写的ajax.js;

/**
 * 初始化 Tree Table 的封装
 */
(function () {
    var TreeTable = function (tableId, url, columns) {
        this.btInstance = null;					//jquery和ajaxTreeTable绑定的对象
        this.bstableId = tableId;
        this.url = url;
        this.method = "GET";
        this.columns = columns;
        this.data = {};// ajax的参数
        this.expandColumn = null;// 展开显示的列 
        this.id = 'menuId';// 选取记录返回的值
        this.code = 'menuId';// 用于设置父子关系
        this.parentCode = 'parentId';// 用于设置父子关系
        this.expandAll = false;// 是否默认全部展开
        this.toolbarId = tableId + "Toolbar";
        this.height = 430;
        this.isParent = 'isParent'; //是否有子节点的标识
    };

    TreeTable.prototype = {
        /**
         * 初始化bootstrap table
         */
        init: function () {
            var tableId = this.bstableId;
            this.btInstance =
                $('#'+tableId).ajaxTreeTable({
                    id: this.id,// 选取记录返回的值
                    code: this.code,// 用于设置父子关系
                    parentCode: this.parentCode,// 用于设置父子关系
                    rootCodeValue: this.rootCodeValue,//设置根节点code值----可指定根节点,默认为null,"",0,"0"
                    type: this.method, //请求数据的ajax类型
                    url: this.url,   //请求数据的ajax的url
                    ajaxParams: this.data, //请求数据的ajax的data属性
                    expandColumn: this.expandColumn,//在哪一列上面显示展开按钮,从0开始
                    striped: true,   //是否各行渐变色
                    expandAll: this.expandAll,  //是否全部展开
                    columns: this.columns,		//列数组
                    toolbar: "#" + this.toolbarId,//顶部工具条
                    height: this.height,
                    isParent: this.isParent  //是否有子节点的标识
                });
            return this;
        },

        /**
         * 设置在哪一列上面显示展开按钮,从0开始
         */
        setExpandColumn: function (expandColumn) {
            this.expandColumn = expandColumn;
        },
        /**
         * 设置记录返回的id值
         */
        setIdField: function (id) {
            this.id = id;
        },
        /**
         * 设置记录分级的字段
         */
        setCodeField: function (code) {
            this.code = code;
        },
        /**
         * 设置记录分级的父级字段
         */
        setParentCodeField: function (parentCode) {
            this.parentCode = parentCode;
        },
        /**
         * 设置根节点code值----可指定根节点,默认为null,"",0,"0"
         */
        setRootCodeValue: function (rootCodeValue) {
            this.rootCodeValue = rootCodeValue;
        },
        /**
         * 设置是否默认全部展开
         */
        setExpandAll: function (expandAll) {
            this.expandAll = expandAll;
        },
        /**
         * 设置表格高度
         */
        setHeight: function (height) {
            this.height = height;
        },
        /**
         * 设置ajax post请求时候附带的参数
         */
        set: function (key, value) {
            if (typeof key == "object") {
                for (var i in key) {
                    if (typeof i == "function")
                        continue;
                    this.data[i] = key[i];
                }
            } else {
                this.data[key] = (typeof value == "undefined") ? $("#" + key).val() : value;
            }
            return this;
        },

        /**
         * 设置ajax get请求时候附带的参数
         */
        setData: function (data) {
            this.data = data;
            return this;
        },

        /**
         * 清空ajax post请求参数
         */
        clear: function () {
            this.data = {};
            return this;
        },

        /**
         * 刷新表格
         */
        refresh: function (parms) {
            if (typeof parms != "undefined") {
                this.btInstance.ajaxTreeTable('refresh', parms.query);// 为了兼容bootstrap-table的写法
            } else {
                this.btInstance.ajaxTreeTable('refresh');
            }
        }
    };

    window.TreeTable = TreeTable;

}());

最后是页面js中的引用


var Region = {
	id: "regionTable",
	table: null,
	layerIndex: -1
};
/**
 * 初始化表格的列
 */
Region.initColumn = function () {
	var columns = [
		{field: 'selectItem', radio: true},
		// {title: '分行id', field: 'id', visible: false, align: 'center', valign: 'middle', width: '80px'},
		{title: '分行名称', field: 'name', align: 'left', valign: 'middle', sortable: true, width: '200px'},
		{title: '分行号', field: 'branchNo', align: 'center', valign: 'middle', sortable: true, width: '100px'},
		{title: '上级归属', field: 'parentName', align: 'center', valign: 'middle', sortable: true, width: '100px'},
		{title: '简称', field: 'simpleName', align: 'center', valign: 'middle', sortable: true, width: '100px'},
		{title: '负责人', field: 'chargeMan', align: 'center', valign: 'middle', sortable: true, width: '100px'},
		{title: '联系电话', field: 'phone', align: 'center', valign: 'middle', sortable: true, width: '100px'}];
	return columns;
}
$(function () {
	$.get(baseURL + "sys/region/info", function(r){
		var colunms = Region.initColumn();
		var table = new TreeTable(Region.id, baseURL + "sys/region/listAsync", colunms);
		table.setRootCodeValue(r.id);
		table.setExpandColumn(1);
		table.setIdField("id");
		table.setCodeField("id");
		table.setParentCodeField("parentId");
		table.setExpandAll(false);
		table.init();
		Region.table = table;
	});
});

//获取已选行的id
function getRegionId () {
	var selected = $('#regionTable').ajaxTreeTable('getSelections');
	if (selected.length == 0) {
		alertSuccess("请选择一条记录");
		return;
	} else {
		return selected[0].id;
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章