記一次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;
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章