• 给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: '' + 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. }  



    最后附上效果图 




    -------------------------------------------------------------------------------- 


    【补充】 

    很快我就发现了横滚动条的Bug,网友 @lihao312 也看出来这个Bug了,并推荐给我官方的groupsummery。 

    我看了官方的groupsummery的例子,感觉2点: 

    1. 它很强大,不仅仅可以用来做合计,还有更强大的分组功能,只用它来做合计,有点大炮打蚊子了 

    2. 它要求调用方(业务画面)的代码与之的耦合度较高,为了使用它,还要引入几个类,并声明、实例化不少配置和实体 

    因此,我没有选择groupsummery,而是继续解决本文方法的横滚动条Bug 

    看代码,其实是有处理滚动条的代码的 

    Java代码  收藏代码
    1. syncSummaryScroll : function() {    
    2.        var mb = this.view.scroller.dom;    
    3.    
    4.        this.view.summaryWrap.dom.scrollLeft = mb.scrollLeft;    
    5.        this.view.summaryWrap.dom.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)    
    6.    },   



    看到了吧,不仅有,还写了两遍,原出处注释是因为仅执行一遍IE无效果,所以要执行两遍。 

    跟踪显示,即便执行2次,这个this.view.summaryWrap.dom.scrollLeft即便被赋值为某个数后,但查看其值仍然是0 

    经过一番研究后发现,原因是 this.view.summaryWrap.dom.scrollLeftMax == 0 

    正当我在为如何改变scrollLeftMax的值(这个也赋值后仍为0)而一筹莫展时,搜到了一个帖子(http://blog.sina.com.cn/s/blog_4e5922c901011gm5.html

    虽然这个帖子的内容和我之前参考的那个(本文第一个链接)一模一样,幸亏我耐心地看了整个页面,才发现在留言中 @guxin 网友给出的提示,实验一下果真见效。 

    我将其代码优化了一下: 
    GridSummary.js 
    1. 删除原有的syncSummaryScroll 方法,及其监听 
    2. 在refreshSummary函数最后加上 

    Java代码  收藏代码
    1. this.view.scroller.setStyle('overflow-x', 'hidden');  
    2. var gridView= this.view;  
    3. this.view.summary.setStyle('overflow-x', 'scroll');  
    4. this.view.summary.on("scroll", function(){  
    5.     gridView.scroller.scrollTo('Left', gridView.summary.getScroll().left);  
    6.   
    7. }  


    至此,不仅横滚联动,而且横滚动条是在合计行下方的,UI效果更好 


    另一点更新,是上面红字提到的,把“本页合计:”放到第一列或指定列中显示,合计所在列仅显示合计的数值。 

    GridSummary.js的 renderSummary 方法 倒数第二行 

    Java代码  收藏代码
    1. if (p.value == undefined || p.value === '') p.value = '-';  



    改成 

    Java代码  收藏代码
    1. if (p.value == undefined || p.value === ''){  
    2.  //"本页合计:"这个title默认放置在第一列,也可以在配置项【summaryTitleColumn】中指定  
    3.  if((cf.dataIndex === this.summaryTitleColumn) || (!this.summaryTitleColumn && i === 1)){  
    4.     p.value = '本页合计:';  
    5.  }else{  
    6.     p.value = '';  
    7.  }  
    8. }  



    业务画面实例化GridPanel时,plgins配置项的GridSummary构造函数可以指定title的显示列 

    Java代码  收藏代码
    1. plugins: new Ext.ux.grid.GridSummary() // "本页合计:"固定在第一列  
    2. //plugins: new Ext.ux.grid.GridSummary({summaryTitleColumn:'name'}) // "本页合计:"在'name'列(可跟着'name'列被拖拽位置)  



    这样一来,上文中提到的 MyExt.util.Renderer.summaryRenderer 也用不着了,换成 

    Java代码  收藏代码
    1. summaryRenderer : Ext.util.Format.numberRenderer('0,0')  


    就行了 


    源码地址:http://dl2.iteye.com/upload/attachment/0106/2146/c93ad8ad-49f8-375d-9c82-83e5a1206140.rar

    转自:http://tonylian.iteye.com/blog/1735525

  • 相关阅读:
    AndroidのActivity启动模式
    Android 垃圾回收,用软引用建立缓存
    如何在eclipse的配置文件里指定jdk路径
    Chrome浏览器扩展开发系列之二:Google Chrome浏览器扩展的调试
    Chrome浏览器扩展开发系列之三:Google Chrome浏览器扩展的架构
    Chrome浏览器扩展开发系列之四:Browser Action类型的Chrome浏览器扩展
    Chrome浏览器扩展开发系列之五:Page Action类型的Chrome浏览器扩展
    Chrome浏览器扩展开发系列之六:options 页面
    手把手教你开发Chrome扩展三:关于本地存储数据
    手把手教你开发Chrome扩展二:为html添加行为
  • 原文地址:https://www.cnblogs.com/jiqiyoudu/p/5081556.html
Copyright © 2020-2023  润新知