easyui DataGrid表體單元格跨列rowspan


最近做項目用到了jquery easyui,其中一組DataGrid做的報表是給客戶大領導看的,客戶要求報表樣式跟他們原有系統的一模一樣(如下圖1)。

DataGrid樣式好調,只是城市名稱單元格跨行這個難度稍大,本着用戶體驗無尺度的用戶價值觀,無尺度的修改了easyui源代碼

 
圖1 – “城市”和“名稱”列跨行的處理效果

一、分析准備:

1、jquery版本:1.8.0;easyui版本:1.3.2
2、首先讀easyui文檔,只有關於表頭rowspan和colspan的配置的方式,表體裝載數據的配置並沒有介紹有關rowspan和colspan的,
再仔細讀,發現文檔中有如下內容:

 
圖2 –DataGrid View文檔描述

初步判定render方法應該就是裝載DataGrid數據的方法吧。

3、打開jquery.easyui.min.js,代碼格式明顯很亂,為了提高可讀性,可以使用工具格式化一下,Eclipse或者Visual Studio都有相應的快捷工具。

搜索render方法,發現由於太多沒有辦法確定具體是哪一個,繼續看文檔,renderRow的描述“This is an option function and will be called by render function.”,說明renderRow是被render調用的。搜索renderRow,直接就可以跟蹤到了,它定義在一個叫“_5ef”變量中(jquery.easyui.min.js的變量或者方法命名都是3位長度的16進制數,在文件的8276行,格式化代碼之后大約在10564行,直接搜索var _5ef就能找到它了),會發現DataGrid View的所有方法都在這里。

4、讀render和renderRow方法,結合easyui文檔介紹,文檔和源碼對比:

  Render有三個參數分別是:target, container, frozen,對應源代碼就是render : function(_5f0, _5f1, _5f2)
  renderRow 的五個參數如下:target, fields, frozen, rowIndex, rowData也就是對應renderRow : function(_5fe, _5ff, _600, _601, _602)
其中render方法是裝載datagrid數據的主方法,render方法首先把數據根據記錄分行,然后逐行調用renderRow方法,把每一條記錄裝載為datagrid表格的一行數據。
附renderRow的五個參數解釋:
  _5fe:datagrid對象,翻譯過來基本上是JSON格式的字符串,需要裝載的數據保存在這里面
  _5ff:表頭Field集合,也就是頁面中定義Datagrid的每一列的Field都保存在_5ff中
  _600: frozen顧名思義,是一個凍結標記。在本方法中,和opts.rownumbers組合成一個&&運算,用來標記是否顯示行號
  _601:是行的索引,以0為初始下標
  _602:是對應行的數據,裝載datagrid的數據
  其中render的功能是根據條件逐行裝載需要裝載的數據,應該說_5f5.push(this.renderRow.call(this, _5f0, _5f4, _5f2, i,rows[i]))調用就是裝載每一行數據。
renderRow的功能是把裝載的行裝載到DataGrid對應的table中,cc.push("<td field=\"" + _604 + "\" " + _607 + ">");就是裝載<td>的代碼。
因此,我們可以有如下結論:

renderRow方法中的cc.push("<td field=\"" + _604 + "\" " + _607 + ">");修改為cc.push("<td rowspan=\"" + rownumber + "\" field=\"" + _604 + "\" " + _607 + ">");的形式,就一切OK

我們可以在需要處理的<td>標簽中增加一些能夠顯示的數據來驗證我們的思路是否正確。驗證思路通過,那下一步就要處理它了

二、初步修改

我們可以確定,我們要把表格處理成如下圖3右所示的樣式

          
    圖3左                圖3右

1、獲取要跨行的單元格和數量:

我們要把一個城市名稱都顯示的列(如上-左)做成如上(右)所示的效果,首先要明確我們在哪一行(城市如重慶,出現的第一行)需要rowspan來處理,既然有rowspan,那肯定要有rowspan的數量。因此,需要兩個變量來統計跨行的那一行以及跨越的數量

2、在起初,我們由_5fe入參可以獲得所有的數據_5fe1 = $.data(_5fe, "datagrid").data.rows;,以行為記錄對象的二維數組,循環rows,就可以得到每一個單元格的數據了。例:rows [i][_5ff[3]]就是獲取i行對應第3(四)列的數據了,_5ff是rows對列的特殊處理
在renderRow方法中加入以下代碼:

var _5fe1 = $.data(_5fe, "datagrid");
var rows = _5fe1.data.rows;

確定兩個數組變量以及針對它們的處理:

//記錄需要跨列的單元格行號
var city = [];
//記錄需要跨列的單元格跨列數量
var cityRowspanCount = [];
//因為我處理城市列數據是倒序處理的,對於第1行數據可能會處理不到,所以初始化city[0]和定義cityCount,
city[0] = 1;
var cityCount = 1;
//處理city序列
for (var i = rows.length - 1; i > 0; i--){
    if (rows[i][_5ff[3]] != rows[i - 1][_5ff[3]]){
        city[i] = 1;
    }
}
//處理city對應的數量cityRowspanCount
for (var j = rows.length - 1; j > 0; j--){
    if (rows[j][_5ff[3]] != rows[j - 1][_5ff[3]]){
        cityRowspanCount[j] = cityCount;
        cityCount = 1;
    }
    else{
        cityCount++;
    }
    if (j == 1){
        cityRowspanCount[0] = cityCount;
    }
}

通過兩個for循環,得到city和cityRowspanCount 兩個數組,
我們以圖3的處理效果,打印城市的處理結果
    city : [1,1,,1,,,,,,1]
    cityRowspanCount : [1,2,,6,,,,,,5]
這樣,就可以用以上兩個數組處理表格了,接着讀renderRow方法剩下的內容,if的功能是加載表格的行號,for循環的作用就是對當前行的數據依次讀取
  if (_600 && opts.rownumbers)
  for ( var i = 0; i < _5ff.length; i++)
分析或者打印_5ff,我們可以發現它是所有列field屬性的集合。確定是哪一列需要跨行處理,注意隱藏列也要算上,以0下標為初始。

3、修改:

// 讀取表中內容
for ( var i =  0; i < _5ff.length; i++) {
    //第四(3)列需要用rowspan合並,因此重新處理
    if (i == 3)
    {
        //對於需要裝載的單元格,執行if中的語句
        if (city[_601])
        {
            var _604 = _5ff[i];
            var col = $(_5fe).datagrid("getColumnOption", _604);
            if (col) {
                var _605 = _602[_604];
                var _606 = col.styler ? (col.styler(_605, _602, _601) || "") : "";
                var _607 = col.hidden ? "style=\"display:none;" + _606 + "\"" : (_606 ? "style=\"" + _606 + "\"" : "");
                //需要裝載的單元格處理rowspan為cityRowspanCount對應的數量
                cc.push("<td rowspan=\"" + cityRowspanCount[_601] + "\" field=\"" + _604 + "\" " + _607 + ">");
                if (col.checkbox) {
                    //省略…
                }
                cc.push("<div style=\"" + _607 + "\" ");
                if (col.checkbox) {
                    //省略…
                }
                cc.push("\">");
                if (col.checkbox) {
                    //省略…
                }
                cc.push("</div>");
                cc.push("</td>");
            }
        }    
    }
    else{
        //其它列的內容按照正常模式裝載
    }
}

通過以上修改,Datagrid單元格跨列的功能就大體實現了。

三、完善&優化

  通過修改renderRow方法,基本實現了單元格跨列的功能,但是現在處理city和cityRowspanCount是在renderRow方法里面處理的,由於renderRow方法每裝載一行數據就要執行一次,city和cityRowspanCount就要重新生成一次,不僅不符合設計邏輯,還會造成資源浪費等問題,因此把city和cityRowspanCount的定義可以放在render方法里面,提高代碼執行效率(注:可能用IE8的朋友會出現表格高度出錯的現象,不要着急,后面會告訴大家怎么處理)。

1、首先在render方法中定義citycityRowspanCount並實現功能,同時為了區別renderRow方法,定義renderRowNew方法,
參數:this.renderRowNew.call(this, _5f0, _5f4, _5f2, i, rows[i],city,cityRowspanCount)

render : function(_5f0, _5f1, _5f2) {
    var _5f3 = $.data(_5f0, "datagrid");
    var opts = _5f3.options;
    var rows = _5f3.data.rows;
    var _5f4 = $(_5f0).datagrid("getColumnFields", _5f2);
    //begin 初始化需要rowspan的行以及數量
    var city = [];
    city[0] = 1;
    var cityRowspanCount = [];
    var cityCount = 1;
    for (var i = rows.length - 1; i > 0; i--){
        if (rows[i][_5f4[3]] != rows[i - 1][_5f4[3]]){
            city[i] = 1;
        }
    }
    for (var j = rows.length - 1; j > 0; j--){
        if (rows[j][_5f4[3]] != rows[j - 1][_5f4[3]]){
            cityRowspanCount[j] = cityCount;
            cityCount = 1;
        }
        else{
            cityCount++;
        }
        if (j == 1){
            cityRowspanCount[0] = cityCount;
        }
    }
    //end
    if (_5f2) {
        if (!(opts.rownumbers || (opts.frozenColumns && opts.frozenColumns.length))) {
            return;
        }
    }
    var _5f5 = [ "<table class=\"datagrid-btable\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\"><tbody>" ];
    for ( var i = 0; i < rows.length; i++) {
        var cls = (i % 2 && opts.striped) ? "class=\"datagrid-row datagrid-row-alt\""
                : "class=\"datagrid-row\"";
        var _5f6 = opts.rowStyler ? opts.rowStyler.call(_5f0, i, rows[i]) : "";
        var _5f7 = _5f6 ? "style=\"" + _5f6 + "\"" : "";
        var _5f8 = _5f3.rowIdPrefix + "-" + (_5f2 ? 1 : 2) + "-" + i;
        
        _5f5.push("<tr id=\"" + _5f8 + "\" datagrid-row-index=\"" + i + "\" " + cls + " " + _5f7 + ">");
     //調用新寫的renderRowNew並注釋原來的renderRow方法 _5f5.push(
this.renderRowNew.call(this, _5f0, _5f4, _5f2, i, rows[i],city,cityRowspanCount)); //_5f5.push(this.renderRow.call(this, _5f0, _5f4, _5f2, i, rows[i])); _5f5.push("</tr>"); } _5f5.push("</tbody></table>"); $(_5f1).html(_5f5.join("")); }

2、編寫renderRowNew方法:

renderRowNew : function(_5fe, _5ff, _600, _601, _602,_601a,_601b,_601c,_601d) {
    var opts = $.data(_5fe, "datagrid").options;
    var cc = [];
    // 裝載行號
    if (_600 && opts.rownumbers) {
        // _603表格當前行
        var _603 = _601 + 1;
        if (opts.pagination) {
            _603 += (opts.pageNumber - 1) * opts.pageSize;
        }
        cc.push("<td class=\"datagrid-td-rownumber\"><div class=\"datagrid-cell-rownumber\">" + _603
                + "</div></td>");
    }
    // 裝載當前行數據
    for ( var i =  0; i < _5ff.length; i++) {
        //第3列需要用rowspan合並,因此重新寫
        if (i == 3){
            //對於需要展示的單元格,才能執行if中的語句
            if (_601a[_601]){
                var _604 = _5ff[i];
                var col = $(_5fe).datagrid("getColumnOption", _604);
                if (col) {
                    var _605 = _602[_604];
                    var _606 = col.styler ? (col.styler(_605, _602, _601) || "") : "";
                    var _607 = col.hidden ? "style=\"display:none;" + _606 + "\"" : (_606 ? "style=\"" + _606 + "\"": "");
                    cc.push("<td rowspan=\"" + _601b[_601] + "\" field=\"" + _604 + "\" " + _607 + ">");
                    if (col.checkbox) {
                        //省略…
                    }
                    cc.push("<div style=\"" + _607 + "\" ");
                    if (col.checkbox) {
                        //省略…
                    }
                    cc.push("\">");
                    if (col.checkbox) {
                        //省略…
                    }
                    cc.push("</div>");
                    cc.push("</td>");
                }
            }    
        }
        else{
            //省略…
        }
    }
    return cc.join("");
}

3、通過以上的處理,就大功告成了,有問題繼續看“問題處理”

四、問題處理

用IE8的朋友會出現表格高度不一致的現象,IE8兼容性不管怎么調都是不好用,原因是datagrid在裝載完畢之后,會根據高度自動修改table樣式,
這個是在function _474(_475, _476, _477)中進行處理的,把以下幾行代碼注釋就可以了

//_47b += c._outerHeight();
 
//_479.height(_47b);
//_47a.height(_47b);

//var _47d = Math.max(tr1.height(), tr2.height());
//tr1.css("height", _47d);
//tr2.css("height", _47d);

 

 

--------------------------------------------
參考資料:
Jquery.easyui官方文檔:http://www.jeasyui.com/documentation/index.php#

轉載請注明出處http://www.cnblogs.com/panzhaohui


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM