给Extjs的GridPanel增加“合计”行

奇怪,也算玩了2年Ext,怎么还没有一篇关于Ext的博文?这一篇算是第一篇了。 

要给Extjs的GridPanel增加“合计”行,有这个想法的童鞋想必不在少数。 

我首先看了官网的例子,没有“合计”。 

再Google,找到一个看似写的比较好的  http://www.cnblogs.com/over140/archive/2009/05/06/1449892.html 

期间主要部分也是借鉴官方论坛上的东西,效果也很漂亮。 

然后又看到一篇,提到了3个方法,并做了比较( http://www.cnblogs.com/over140/archive/2010/06/28/1766608.html) 

而且,把之前提到的那一个归结为“很明显是最复杂的,基本可以被淘汰。” 

而我确有不同的看法,我恰恰认为这种“最复杂”的方法,有很多优点: 

1. 这种sum计算,理应交给前台,减轻服务器压力。我用硬翻页,也不存在总合计的问题,后台也只能得到“当页合计” 

2. 总体虽然复杂,但接口简洁,对业务画面代码干扰很小 

3. 效果漂亮,合计始终保持在最下行显示,不与滚动条联动 

4. 可以分别计算多列的合计 

5. 除了“合计”,还可以计算 总数、最大值、最小值、平均值 


于是,按照第一个URL中的方法开始,但始终有小Bug,下载了他的代码,倒是没有运行,但比看帖子里的片段,更清晰了。 

最后,我找出了几处Bug和多余之处,改善了接口的友好度,降低了耦合度,总结分享一下: 

1. 无需对Extjs的JsonReader.js做重载。真的不用override readRecords方法 

2. 自行扩展的GridSummary类的onLayout方法中有一句 
  
Java代码   收藏代码
  1. this.scroller.setHeight(vh - this.summary.getHeight());  

   报错:没有this.summary 
   也确实是没有定义,官网论坛上的那个版本中,也没有这一句 

   但,这句代码,确实有意义,让滚动条的管辖范围高度让出地下的合计行。也似乎应该有一个“控件”在哪儿,取其高度,减之。但暂时没找到,临时用 
Java代码   收藏代码
  1. this.scroller.setHeight(vh - 30);  

代替。 

    纠正:此处其实不存在这个Bug,是我一开始搞错了 

3. 业务画面中实例化 JsonReader 时,参数 
Java代码   收藏代码
  1. dataSum: 'dataSum'  

   显得比较突兀。其实,在第一点中已经说了,不需要override JsonReader的readRecords方法,这个JsonReader 的属性,其实也是不需要的。 

4. 渲染器里 
Java代码   收藏代码
  1. var renderSummary = function(o, cs, cm) {  
  2.         return '合计:'+jr.dataSum;  
  3.     }  

    这个jr,即刚才实例化的JsonReader,两者有了耦合,这一点不好。其实,这里函数的第一个参数,即o,其实已经是合计数量了。 

    进一步美化一下,自己写个Renderer,放在自己的工具类里就行了。 
Java代码   收藏代码
  1. summaryRenderer : function(format){  
  2.        return function(v){  
  3.            var val = Ext.util.Format.number(v, format);  
  4.            if(v >= 0){  
  5.                return '<span style="color:black;font-weight:bold;">' +  '本页合计 : ' + val + '</span>';  
  6.            }else{  
  7.                return '<span style="color:red;font-weight:bold;">' + '本页合计 : ' + val + '</span>';  
  8.            }  
  9.        };  
  10.    }  


    业务画面这样用: 
Java代码   收藏代码
  1. summaryRenderer: MyExt.util.Renderer.summaryRenderer('0,0')  

    可以格式化(3位一个逗号),负数变红色,整体黑体显示 

     注:“本页合计:”总是在合计列中,感觉有2点不妥:1. 本身合计位数就大,这样很容易要求列宽要加大才能显示下;2. 如果两列以上显示合计,“本页合计:”出现多次似乎也怪怪的。 
    所以,我改进了一点点UI,具体请见下面补充部分
 

5. 原作者似乎忘记了,他在GridSummary类里还定义了类中类Ext.ux.grid.GridSummary.Calculations 
   这个是几个不同算法的实现,来实现上面所说的几个优点中的最后一个:除了“合计”,还可以计算 总数、最大值、最小值、平均值。 

   但,在业务画面中,我们需要在定义ColumnModel时,声明这一列要算 合计 还是 平均值...而原作者去忘了这个。(我没有运行他的代码,不知道那代码能不能有结果) 

    总结一下,经过我的改造。业务画面只需2处修改: 
1. 在定义ColumnModel时,增加2个属性即可: 
Java代码   收藏代码
  1. summaryRenderer: MyExt.util.Renderer.summaryRenderer('0,0'),  
  2. summaryType: 'sum' // 这个不写的话,默认是'sum'  


2. 在GridPanel实例化参数中增加一个plugin 
Java代码   收藏代码
  1. plugins: new Ext.ux.grid.GridSummary(),  


    其实,我们绝大部分时间都是用“合计”其他几个很少用,但又舍不得丢弃这些功能。那么就给GridSummary增加一个默认 summaryType: 'sum' 功能呗,原文calculate 方法中,把
Java代码   收藏代码
  1. if (cf.summaryType) {  

    改成 
Java代码   收藏代码
  1. if (cf.summaryRenderer){  
  2.    if(!cf.summaryType) {  
  3.     f.summaryType = 'sum';  // default to Sum, you can define 'count', 'max', 'min', 'average' in the Column defination   
  4.     }  

    即可。 



【最后在总结一下,放上整个代码】 

业务画面只需 

1. 在定义ColumnModel时,增加1个属性: 
Java代码   收藏代码
  1. summaryRenderer: MyExt.util.Renderer.summaryRenderer('0,0')  


如: 
Java代码   收藏代码
  1. },{  
  2.     hidden : false,  
  3.     header : '数量',  
  4.     dataIndex : 'qty',  
  5.     align: 'right',  
  6.     sortable : true,  
  7.     renderer: Ext.util.Format.numberRenderer('0,0'),   
  8.     summaryRenderer: MyExt.util.Renderer.summaryRenderer('0,0')  
  9. },{  


2. 在GridPanel实例化参数中增加一个plugin 
Java代码   收藏代码
  1. return new Ext.grid.GridPanel({  
  2.     store: ds,  
  3.     columns: gridColums,  
  4.     plugins: new Ext.ux.grid.GridSummary()  


另外增加一个GridSummary.js类 
Java代码   收藏代码
  1. Ext.ns('Ext.ux.grid');  
  2.   
  3. Ext.ux.grid.GridSummary = function(config) {  
  4.         Ext.apply(this, config);  
  5. };  
  6.   
  7. Ext.extend(Ext.ux.grid.GridSummary, Ext.util.Observable, {  
  8.     init : function(grid) {  
  9.         this.grid = grid;  
  10.         this.cm = grid.getColumnModel();  
  11.         this.view = grid.getView();  
  12.   
  13.         var v = this.view;  
  14.   
  15.         // override GridView's onLayout() method  
  16.         v.onLayout = this.onLayout;  
  17.   
  18.         v.afterMethod('render'this.refreshSummary, this);  
  19.         v.afterMethod('refresh'this.refreshSummary, this);  
  20.         v.afterMethod('syncScroll'this.syncSummaryScroll, this);  
  21.         v.afterMethod('onColumnWidthUpdated'this.doWidth, this);  
  22.         v.afterMethod('onAllColumnWidthsUpdated'this.doAllWidths, this);  
  23.         v.afterMethod('onColumnHiddenUpdated'this.doHidden, this);  
  24.   
  25.         // update summary row on store's add/remove/clear/update events  
  26.         grid.store.on({  
  27.             add: this.refreshSummary,  
  28.             remove: this.refreshSummary,  
  29.             clear: this.refreshSummary,  
  30.             update: this.refreshSummary,  
  31.             scope: this  
  32.         });  
  33.   
  34.         if (!this.rowTpl) {  
  35.             this.rowTpl = new Ext.Template(  
  36.                 '<div class="x-grid3-summary-row x-grid3-gridsummary-row-offset">',  
  37.                     '<table class="x-grid3-summary-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',  
  38.                         '<tbody><tr>{cells}</tr></tbody>',  
  39.                     '</table>',  
  40.                 '</div>'  
  41.             );  
  42.             this.rowTpl.disableFormats = true;  
  43.         }  
  44.         this.rowTpl.compile();  
  45.   
  46.         if (!this.cellTpl) {  
  47.             this.cellTpl = new Ext.Template(  
  48.                 '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}">',  
  49.                     '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on" {attr}>{value}</div>',  
  50.                 "</td>"  
  51.             );  
  52.             this.cellTpl.disableFormats = true;  
  53.         }  
  54.         this.cellTpl.compile();  
  55.     },  
  56.   
  57.     calculate : function(rs, cm) {  
  58.         var data = {}, cfg = cm.config;  
  59.         for (var i = 0, len = cfg.length; i < len; i++) { // loop through all columns in ColumnModel  
  60.             var cf = cfg[i], // get column's configuration  
  61.                 cname = cf.dataIndex; // get column dataIndex  
  62.   
  63.             // initialise grid summary row data for  
  64.             // the current column being worked on  
  65.             data[cname] = 0;  
  66.               
  67.             if (cf.summaryRenderer){  
  68.                 if(!cf.summaryType) {  
  69.                     cf.summaryType = 'sum'// default to Sum, you can define 'count', 'max', 'min', 'average' in the Column defination   
  70.                 }  
  71.                 for (var j = 0, jlen = rs.length; j < jlen; j++) {  
  72.                     var r = rs[j]; // get a single Record  
  73.                     data[cname] = Ext.ux.grid.GridSummary.Calculations[cf.summaryType](r.get(cname), r, cname, data, j);  
  74.                 }  
  75.             }              
  76.         }  
  77.   
  78.         return data;  
  79.     },  
  80.   
  81.     onLayout : function(vw, vh) {  
  82.         if (Ext.type(vh) != 'number') { // handles grid's height:'auto' config  
  83.             return;  
  84.         }  
  85.         // note: this method is scoped to the GridView  
  86.         if (!this.grid.getGridEl().hasClass('x-grid-hide-gridsummary')) {  
  87.             // readjust gridview's height only if grid summary row is visible  
  88.             //this.scroller.setHeight(vh - this.summary.getHeight());  
  89.             this.scroller.setHeight(vh - 30);  
  90.         }  
  91.     },  
  92.   
  93.     syncSummaryScroll : function() {  
  94.         var mb = this.view.scroller.dom;  
  95.   
  96.         this.view.summaryWrap.dom.scrollLeft = mb.scrollLeft;  
  97.         this.view.summaryWrap.dom.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)  
  98.     },  
  99.   
  100.     doWidth : function(col, w, tw) {  
  101.         var s = this.view.summary.dom;  
  102.   
  103.         s.firstChild.style.width = tw;  
  104.         s.firstChild.rows[0].childNodes[col].style.width = w;  
  105.     },  
  106.   
  107.     doAllWidths : function(ws, tw) {  
  108.         var s = this.view.summary.dom, wlen = ws.length;  
  109.   
  110.         s.firstChild.style.width = tw;  
  111.   
  112.         var cells = s.firstChild.rows[0].childNodes;  
  113.   
  114.         for (var j = 0; j < wlen; j++) {  
  115.             cells[j].style.width = ws[j];  
  116.         }  
  117.     },  
  118.   
  119.     doHidden : function(col, hidden, tw) {  
  120.         var s = this.view.summary.dom,  
  121.             display = hidden ? 'none' : '';  
  122.   
  123.         s.firstChild.style.width = tw;  
  124.         s.firstChild.rows[0].childNodes[col].style.display = display;  
  125.     },  
  126.   
  127.     renderSummary : function(o, cs, cm) {  
  128.         cs = cs || this.view.getColumnData();  
  129.         var cfg = cm.config,  
  130.             buf = [],  
  131.             last = cs.length - 1;  
  132.   
  133.         for (var i = 0, len = cs.length; i < len; i++) {  
  134.             var c = cs[i], cf = cfg[i], p = {};  
  135.   
  136.             p.id = c.id;  
  137.             p.style = c.style;  
  138.             p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');  
  139.   
  140.             if (cf.summaryType || cf.summaryRenderer) {  
  141.                 p.value = (cf.summaryRenderer || c.renderer)(o.data[c.name], p, o);  
  142.             } else {  
  143.                 p.value = '';  
  144.             }  
  145.             //此处设置默认不显示时用什么符号标记  
  146.             if (p.value == undefined || p.value === "") p.value = "-";  
  147.             buf[buf.length] = this.cellTpl.apply(p);  
  148.         }  
  149.   
  150.         return this.rowTpl.apply({  
  151.             tstyle: 'width:' + this.view.getTotalWidth() + ';',  
  152.             cells: buf.join('')  
  153.         });  
  154.     },  
  155.   
  156.     refreshSummary : function() {  
  157.         var g = this.grid, ds = g.store,  
  158.             cs = this.view.getColumnData(),  
  159.             cm = this.cm,  
  160.             rs = ds.getRange(),  
  161.             data = this.calculate(rs, cm),  
  162.             buf = this.renderSummary({data: data}, cs, cm);  
  163.   
  164.         if (!this.view.summaryWrap) {  
  165.             this.view.summaryWrap = Ext.DomHelper.insertAfter(this.view.scroller, {  
  166.                 tag: 'div',  
  167.                 cls: 'x-grid3-gridsummary-row-inner'  
  168.             }, true);  
  169.         }  
  170.         this.view.summary = this.view.summaryWrap.update(buf).first();  
  171.     },  
  172.   
  173.     toggleSummary : function(visible) { // true to display summary row  
  174.         var el = this.grid.getGridEl();  
  175.   
  176.         if (el) {  
  177.             if (visible === undefined) {  
  178.                 visible = el.hasClass('x-grid-hide-gridsummary');  
  179.             }  
  180.             el[visible ? 'removeClass' : 'addClass']('x-grid-hide-gridsummary');  
  181.   
  182.             this.view.layout(); // readjust gridview height  
  183.         }  
  184.     },  
  185.   
  186.     getSummaryNode : function() {  
  187.         return this.view.summary  
  188.     }  
  189. });  
  190. Ext.reg('gridsummary', Ext.ux.grid.GridSummary);  
  191.   
  192. /* 
  193.  * all Calculation methods are called on each Record in the Store 
  194.  * with the following 5 parameters: 
  195.  * 
  196.  * v - cell value 
  197.  * record - reference to the current Record 
  198.  * colName - column name (i.e. the ColumnModel's dataIndex) 
  199.  * data - the cumulative data for the current column + summaryType up to the current Record 
  200.  * rowIdx - current row index 
  201.  */  
  202. Ext.ux.grid.GridSummary.Calculations = {  
  203.     sum : function(v, record, colName, data, rowIdx) {  
  204.         return data[colName] + Ext.num(v, 0);  
  205.     },  
  206.   
  207.     count : function(v, record, colName, data, rowIdx) {  
  208.         return rowIdx + 1;  
  209.     },  
  210.   
  211.     max : function(v, record, colName, data, rowIdx) {  
  212.         return Math.max(Ext.num(v, 0), data[colName]);  
  213.     },  
  214.   
  215.     min : function(v, record, colName, data, rowIdx) {  
  216.         return Math.min(Ext.num(v, 0), data[colName]);  
  217.     },  
  218.   
  219.     average : function(v, record, colName, data, rowIdx) {  
  220.         var t = data[colName] + Ext.num(v, 0), count = record.store.getCount();  
  221.         return rowIdx == count - 1 ? (t / count) : t;  
  222.     }  
  223. }  


最后附上效果图 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值