Ext Grid 的合计行解决

在实际的应用中,我们的用户经常会要求需要在展现数据的网格上,增加合计行这样的功能,Ext.Grid.GridPanel本身并不支持合计行,在EXT的官方论坛上,有一些plugin可以实现合计行的功能。但其解决的个人觉得不太好用,于是,只有自己手动来做了。
首先,我们要搞清楚,grid控件,是与ColumnModel,Store,GridView等对象一起协作地,所以要分析需要从什么地方入手解决问题。经过分析,我们不难看出,要解决我们的合计行,需要从以下几方面入手:
1.改变表格VIEW的header模板,增加我们的合计行元素
2.增加对表格数据的求和计算
3.考虑表格中数据变化时,刷新数据视图的问题
4.修改renderHeader方法,以支持合计行数据的渲染
以下为新增的CSS样式:
.x-grid3-row-sum {
    /*border:1px solid #dddddd;
    background: #efefef url(../images/default/grid/grid3-hrow-sum.gif) repeat-x left top;
    */
    border:1px solid #dddddd;
    background: url(../images/default/grid/grid3-hrow-sum.gif) repeat-x left top;
}

https://p-blog.csdn.net/images/p_blog_csdn_net/ynzhangyao/EntryImages/20081017/grid3-hrow-sum.gif

示例图:
https://p-blog.csdn.net/images/p_blog_csdn_net/ynzhangyao/EntryImages/20081017/ext_grid_sum.JPG

 

  1. /*
  2.  * Ext JS Library 2.1
  3.  * Copyright(c) 2006-2008, Ext JS, LLC.
  4.  * licensing@extjs.com
  5.  * 
  6.  * http://extjs.com/license
  7.  */
  8. /**
  9.  * @class Ext.grid.EditorGridPanelEx
  10.  * @extends Ext.grid.EditorGridPanel
  11.  * @author 张尧伟
  12.  * @version 1.0
  13.  * <p>此类是为增加合计行而设计的</p>
  14.  * <br><br>Usage:
  15.  * <pre><code>
  16.      function createPreplanDetailGrid()
  17.     {
  18.          var preplanDetailStore = new Ext.data.DWRStore({id:'preplanDetailStore',fn:UIPreContractReport.getPreContractDetail,
  19.             fields: [
  20.                {name: 'bar'},
  21.                {name: 'itemName'},
  22.                {name: 'storeAmount', type: 'float'},
  23.                {name: 'endAmount',type:'float'},
  24.                {name: 'currentRate',type:'float'},
  25.                {name: 'weekPercent',type:'float'},
  26.                {name: 'confirmAmount',type:'float'},
  27.                {name: 'accPreAmount',type:'float'},
  28.                {name: 'surplusTargetAmount',type:'int'},
  29.                {name:'surplusHyearAmount',type:'float'}
  30.             ]
  31.         });
  32.         
  33.         var sm = new Ext.grid.CheckboxSelectionModel();
  34.         var preplanDetailGrid = new Ext.grid.EditorGridPanelEx({
  35.             id:'preplanDetailGrid',
  36.             title:'卷烟规格明细',
  37.             store: preplanDetailStore,
  38.             cm: new Ext.grid.ColumnModelEx({sumheader:true,columns:[
  39.                 sm,
  40.                 {header: "卷烟条码", width:90,  sortable: true, dataIndex: 'bar',sumcaption:'合计'},
  41.                 {header: "卷烟规格",  width:110,sortable: true,  dataIndex: 'itemName'},
  42.                 {header: "预计划量",  width:100,sortable: true, dataIndex: 'endAmount',editor:new Ext.form.NumberField(),align:'right',renderer:Util.rmbMoney,issum:true},
  43.                 {header: "商业库存",  width:100,sortable: true, dataIndex: 'storeAmount',align:'right',renderer:Util.rmbMoney},
  44.                 {header: "到货存销比",  width:100,sortable: true, dataIndex: 'currentRate',align:'right',renderer:Util.rmbMoney},
  45.                 {header: "周进度",  width:100,sortable: true, dataIndex: 'weekPercent',align:'right'},
  46.                 {header: "商业请求量",  width:100,sortable: true, dataIndex: 'confirmAmount',align:'right',renderer:Util.rmbMoney,issum:true},
  47.                 {header: "累计预计划量",  width:100,sortable: true, dataIndex: 'accPreAmount',align:'right',renderer:Util.rmbMoney},
  48.                 {header: "剩余指标量",  width:100,sortable: true, dataIndex: 'surplusTargetAmount',align:'right',renderer:Util.rmbMoney,issum:true},
  49.                 {header: "剩余协议量",  width:100,sortable: true, dataIndex: 'surplusHyearAmount',align:'right',renderer:Util.rmbMoney,issum:true}
  50.             ]}),
  51.             sm:sm,
  52.             stripeRows: true,
  53.             height:Util.getH(0.4),
  54.             bbar:[
  55.                 {pressed:true,text:'删除',handler:deletePreplanDetail},{xtype:'tbseparator'},
  56.                 {pressed:true,text:'保存',handler:saveModified}
  57.                 ]
  58.         });
  59.     
  60.         return preplanDetailGrid;
  61.     }
  62.  </code></pre>
  63.  * <b>Notes:</b> <br/>
  64.  * - Ext.grid.ColumnModel需要增加属性:sumheader:true以指示需要使用合计行
  65.  * - Ext.grid.ColumnModel的columns属性中,需要增加sumcaption:'合计'属性,以指示需要显示的合计标题名称
  66.  * - Ext.grid.ColumnModel的columns属性中,需要增加issum:true属性,以指示该列需要计算合计
  67.  * - Ext.grid.ColumnModel的columns属性中,可选增加sfn:function(sumvalue,colvalue,record){return 计算结果;},
  68.  * 以指示该列需要使用用户自定义函数进行计算sumvalue:此列当前的合计值,colvalue当前计算的列值,record当前记录对象
  69.  * <br>
  70.  * @constructor
  71.  * @param {Object} config The config object
  72.  */
  73. Ext.grid.GridPanelEx = Ext.extend(Ext.grid.GridPanel, {
  74.   getView : function() {
  75.         if (!this.view) {
  76.             this.view = new Ext.grid.GridViewEx(this.viewConfig);
  77.         }
  78.         return this.view;
  79.     },
  80.   initComponent : function() {
  81.     if (!this.cm) {
  82.         
  83.         this.cm = new Ext.grid.ColumnModelEx(this.columns);
  84.         this.colModel = this.cm;
  85.         delete this.columns;
  86.     }
  87.     if(!this.groupedHeaders)
  88.     {
  89.         this.groupedHeaders = this.groupedHeaders;
  90.     }
  91.     //在编辑完成后,需要更新合计列
  92.     //this.on('afteredit',this.onAfteredit,this);
  93.     Ext.grid.GridPanelEx.superclass.initComponent.call(this);
  94.   },
  95.   
  96.   getGroupedHeaders : function(){
  97.         return this.groupedHeaders;
  98.   },
  99.   /**
  100.    * 处理修改后,更新合计
  101.    * @param {} event
  102.    * grid - This grid
  103.    * record - The record being edited
  104.    * field - The field name being edited
  105.    * value - The value being set
  106.    * originalValue - The original value for the field, before the edit.
  107.    * row - The grid row index
  108.    * column - The grid column index
  109.    */
  110.   onAfteredit:function(event){
  111.     var grid = event.grid;
  112.     var cm = grid.getColumnModel();
  113.     if(false == cm.sumheader)
  114.     {
  115.         return;
  116.     }
  117.     var value = event.value;
  118.     var origValue = event.originalValue;
  119.     
  120.     cm.config[event.column].sumvalue -= origValue;
  121.     cm.config[event.column].sumvalue += value;
  122.     grid.getView().updateHeaders();
  123.   },
  124.   /**
  125.    * 重新计算合计列
  126.    */
  127.   recalculation:function(){
  128.     var cm = this.getColumnModel();
  129.     if(true != cm.sumheader)
  130.     {
  131.         return;
  132.     }   
  133.     var view = this.getView();
  134.     view.recalculation(this.getStore());
  135.   }
  136. });
  137. Ext.grid.EditorGridPanelEx = Ext.extend(Ext.grid.EditorGridPanel, {
  138.   getView : function() {
  139.         if (!this.view) {
  140.             this.view = new Ext.grid.GridViewEx(this.viewConfig);
  141.         }
  142.         return this.view;
  143.     },
  144.   initComponent : function() {
  145.     if (!this.cm) {
  146.         
  147.         this.cm = new Ext.grid.ColumnModelEx(this.columns);
  148.         this.colModel = this.cm;
  149.         delete this.columns;
  150.     }
  151.     if(!this.groupedHeaders)
  152.     {
  153.         this.groupedHeaders = this.groupedHeaders;
  154.     }
  155.     //在编辑完成后,需要更新合计列
  156.     this.on('afteredit',this.onAfteredit,this);
  157.     Ext.grid.EditorGridPanelEx.superclass.initComponent.call(this);
  158.   },
  159.   
  160.   getGroupedHeaders : function(){
  161.         return this.groupedHeaders;
  162.   },
  163.   /**
  164.    * 处理修改后,更新合计
  165.    * @param {} event
  166.    * grid - This grid
  167.    * record - The record being edited
  168.    * field - The field name being edited
  169.    * value - The value being set
  170.    * originalValue - The original value for the field, before the edit.
  171.    * row - The grid row index
  172.    * column - The grid column index
  173.    */
  174.   onAfteredit:function(event){
  175.     var grid = event.grid;
  176.     var cm = grid.getColumnModel();
  177.     if(false == cm.sumheader)
  178.     {
  179.         return;
  180.     }
  181.     var value = event.value;
  182.     var origValue = event.originalValue;
  183.     
  184.     cm.config[event.column].sumvalue -= origValue;
  185.     cm.config[event.column].sumvalue += value;
  186.     grid.getView().updateHeaders();
  187.   },
  188.   /**
  189.    * 重新计算合计列
  190.    */
  191.   recalculation:function(){
  192.     var cm = this.getColumnModel();
  193.     if(true != cm.sumheader)
  194.     {
  195.         return;
  196.     }   
  197.     var view = this.getView();
  198.     view.recalculation(this.getStore());
  199.   }
  200. });
  201. Ext.grid.GridViewEx = function(config) {
  202.     Ext.apply(this, config);
  203.     if (!this.templates) this.templates = {};
  204.     
  205.     
  206.     //增加合计行模板
  207.     if(!this.templates.header){
  208.             this.templates.header = new Ext.Template(
  209.                     '<table border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
  210.                     '<thead><tr class="x-grid3-hd-row">{cells}</tr><tr class="x-grid3-hd-row x-grid3-row-sum">{sumcells}</tr></thead>',
  211.                     "</table>"
  212.                     );
  213.     }
  214.     
  215.     if(!this.templates.sumcells){
  216.             this.templates.sumcells = new Ext.Template(
  217.                     '<td class="x-grid3-hd x-grid3-cell x-grid3-td-{id}" style="{style}"><div {tooltip} {attr} class="x-grid3-hd-inner x-grid3-hd-{id}" unselectable="on" style="{istyle}">''',
  218.                     '{value}''''',
  219.                     "</div></td>"
  220.                     );
  221.     }
  222.     
  223.     
  224.     Ext.grid.GridViewEx.superclass.constructor.call(this);
  225. };
  226. Ext.extend(Ext.grid.GridViewEx, Ext.grid.GridView, {
  227.     
  228.     insertRows : function(ds, firstRow, lastRow, isUpdate){
  229.         if(!isUpdate && firstRow === 0 && lastRow == ds.getCount()-1){
  230.             this.refresh();
  231.         }else{
  232.             if(!isUpdate){
  233.                 this.fireEvent("beforerowsinserted"this, firstRow, lastRow);
  234.             }
  235.             var html = this.renderRows(firstRow, lastRow);
  236.             var before = this.getRow(firstRow);
  237.             if(before){
  238.                 Ext.DomHelper.insertHtml('beforeBegin', before, html);
  239.             }else{
  240.                 Ext.DomHelper.insertHtml('beforeEnd'this.mainBody.dom, html);
  241.             }
  242.             if(!isUpdate){
  243.                 this.fireEvent("rowsinserted"this, firstRow, lastRow);
  244.                 this.processRows(firstRow);
  245.             }
  246.         }
  247.     },
  248.     onUpdate : function(ds, record){
  249.         this.refreshRow(record);
  250.     },
  251.         // private
  252.     refreshRow : function(record){
  253.         var ds = this.ds, index;
  254.         if(typeof record == 'number'){
  255.             index = record;
  256.             record = ds.getAt(index);
  257.         }else{
  258.             index = ds.indexOf(record);
  259.         }
  260.         var cls = [];
  261.         this.insertRows(ds, index, index, true);
  262.         this.getRow(index).rowIndex = index;
  263.         this.onRemove(ds, record, index+1, true);
  264.         this.fireEvent("rowupdated"this, index, record);
  265.         /*
  266.         if(true == record.dirty && true == this.cm.sumheader){
  267.             var cm = this.cm;
  268.             var colCount =  cm.getColumnCount();
  269.             for(var i=0; i<colCount;++i)
  270.             {
  271.                 var c = cm.config[i];
  272.                 if( true == c.issum && typeof record.modified[c.dataIndex] !== 'undefined'){
  273.                     var value = record.get(c.dataIndex);
  274.                     var origValue = record.modified[c.dataIndex];
  275.                     if(origValue){
  276.                         cm.config[i].sumvalue -= origValue;
  277.                         cm.config[i].sumvalue += value;
  278.                     }
  279.                 }
  280.             }
  281.             this.updateHeaders();
  282.         }*/
  283.     },
  284.     onRemove : function(ds, record, index, isUpdate){
  285.         if(isUpdate !== true){
  286.             this.fireEvent("beforerowremoved"this, index, record);
  287.         }
  288.         this.removeRow(index);
  289.         if(isUpdate !== true){
  290.             this.processRows(index);
  291.             this.applyEmptyText();
  292.             this.fireEvent("rowremoved"this, index, record);
  293.             
  294.             //处理删除行时的合计行问题
  295.             this.processSumForRemoved(ds,record,index);
  296.         }
  297.     },
  298.     
  299.     //Template has changed and we need a few other pointers to keep track
  300.     doRender : function(cs, rs, ds, startRow, colCount, stripe){
  301.         var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount-1;
  302.         var tstyle = 'width:'+this.getTotalWidth()+';';
  303.         // buffers
  304.         var buf = [], cb, c, p = {}, rp = {tstyle: tstyle}, r;
  305.         //如果不是在编辑状态下,则需要将合计列的数据清0        
  306.         if(false == rs[0].dirty && true ==(rs.length == ds.getCount())){
  307.             this.clearSumZero();
  308.         }
  309.         for(var j = 0, len = rs.length; j < len; j++){
  310.             r = rs[j]; cb = [];
  311.             var rowIndex = (j+startRow);
  312.             for(var i = 0; i < colCount; i++){
  313.                 c = cs[i];
  314.                 p.id = c.id;
  315.                 p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
  316.                 p.attr = p.cellAttr = "";
  317.                 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
  318.                 p.style = c.style;
  319.                 if(p.value == undefined || p.value === "") p.value = " ";
  320.                 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
  321.                     p.css += ' x-grid3-dirty-cell';
  322.                 }
  323.                 cb[cb.length] = ct.apply(p);
  324.                 
  325.                 //处理求和问题
  326.                 if(true == this.cm.sumheader)
  327.                 {
  328.                     if(true == this.cm.config[i].issum && false == r.dirty)
  329.                     {//如果是求和列且不为脏数据
  330.                         if(!this.cm.config[i].sumvalue){
  331.                                 this.cm.config[i].sumvalue = 0.0;
  332.                         }
  333.                         if(this.cm.config[i].sfn)
  334.                         {//如果存在求和函数,则调用
  335.                             this.cm.config[i].sumvalue = this.cm.config[i].sfn(this.cm.config[i].sumvalue,r.get(c.name),r);
  336.                         }
  337.                         else
  338.                         {//否则,直接累计
  339.                             if(r.data[c.name]){
  340.                                 this.cm.config[i].sumvalue += r.data[c.name];
  341.                             }
  342.                         }
  343.                     }
  344.                 }
  345.                 
  346.             }
  347.             var alt = [];
  348.             if(stripe && ((rowIndex+1) % 2 == 0)){
  349.                 alt[0] = "x-grid3-row-alt";
  350.             }
  351.             if(r.dirty){
  352.                 alt[1] = " x-grid3-dirty-row";
  353.             }
  354.             rp.cols = colCount;
  355.             if(this.getRowClass){
  356.                 alt[2] = this.getRowClass(r, rowIndex, rp, ds);
  357.             }
  358.             rp.alt = alt.join(" ");
  359.             rp.cells = cb.join("");
  360.             buf[buf.length] =  rt.apply(rp);
  361.         }
  362.         
  363.         //如果有合计行,则需要重新渲染表头
  364.         if(true == this.cm.sumheader)
  365.         {
  366.             this.updateHeaders();
  367.         }
  368.         return buf.join("");
  369.     },
  370.      /**
  371.      * 将求和的列清0
  372.      */
  373.     clearSumZero:function(){
  374.         this.hasSumColumn = false;
  375.         var cm = this.cm;
  376.         for(var i=0; i<cm.config.length;++i)
  377.         {
  378.             if(cm.config[i].issum)
  379.             {
  380.                 this.hasSumColumn = true;
  381.                 this.cm.config[i].sumvalue = 0.0;
  382.             }
  383.         }
  384.     },
  385.     renderHeaders : function(){
  386.         var cm = this.cm, ts = this.templates;
  387.         var ct = ts.hcell;
  388.         var cb = [], sb = [],lb = [], lsb = [], p = {};
  389.         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
  390.             p.id = cm.getColumnId(i);
  391.             p.value = cm.getColumnHeader(i) || "";
  392.             p.style = this.getColumnStyle(i, true);
  393.             p.tooltip = this.getColumnTooltip(i);
  394.             if(cm.config[i].align == 'right'){
  395.                 p.istyle = 'padding-right:16px';
  396.             } else {
  397.                 delete p.istyle;
  398.             }
  399.             cb[cb.length] = ct.apply(p);
  400.         }
  401.         
  402.         //处理合计行表头
  403.         if(true == cm.sumheader)
  404.         {
  405.             sb = this.buildSumHeaders(cm,ts);
  406.         }
  407.         
  408.         return ts.header.apply({cells: cb.join(""), sumcells: sb.join(""),tstyle:'width:'+this.getTotalWidth()+';'});
  409.         
  410.         //return [ts.header.apply({cells: cb.join(""), sumcells: sb.join(""), tstyle:'width:'+(tw-lw)+';'}),
  411.         //ts.header.apply({cells: lb.join(""), sumcells: lsb.join(""), tstyle:'width:'+(lw)+';'})];
  412.         
  413.         //return ts.header.apply({cells: cb.join(""), tstyle:'width:'+this.getTotalWidth()+';'});
  414.     },
  415.     
  416.     
  417.     /**
  418.      * 生成合计表头
  419.      * @param {} cm
  420.      * @param {} ts
  421.      * @param {} tw
  422.      * @param {} lw
  423.      * @return {}array
  424.      */
  425.     buildSumHeaders:function(cm,ts){
  426.         var ct = ts.hcell;
  427.         var sb = [], sp = {};
  428.         for (var i = 0, len = cm.getColumnCount(); i < len; i++) {
  429.             sp.id = 'sum' + cm.getColumnId(i);
  430.             if(cm.config[i].sumcaption){
  431.                 sp.value = cm.config[i].sumcaption;
  432.             }else{
  433.                 sp.value = " ";
  434.             }
  435.             
  436.             //cm.config[i].sumcaption = " ";
  437.             if (true == cm.config[i].issum && typeof cm.config[i].sumvalue == 'number'){
  438.                 if(cm.config[i].renderer)
  439.                 {
  440.                     sp.value = cm.config[i].renderer(cm.config[i].sumvalue);
  441.                 }else{
  442.                     sp.value = cm.config[i].sumvalue;
  443.                 }
  444.             }
  445.             
  446.             
  447.             sp.style = this.getColumnStyle(i, false);
  448.             sp.tooltip = this.getColumnTooltip(i);
  449.             
  450.             if (cm.config[i].align == 'right') {
  451.                 sp.istyle = 'padding-right:16px';
  452.             }
  453.              sb[sb.length] = ct.apply(sp);
  454.         }
  455.         return sb;
  456.     },
  457.     /**
  458.      * 处理删除行时,更新合计行的数据
  459.      * @param {} ds
  460.      * @param {} record
  461.      * @param {} index
  462.      */
  463.     processSumForRemoved: function(ds,record,index){
  464.         var cm = this.cm;
  465.         if(true != cm.sumheader){
  466.             return;
  467.         }
  468.         
  469.         var cfg = cm.config;
  470.         for(var i=0; i<cfg.length; ++i){
  471.             if(true == cfg[i].issum && cfg[i].sumvalue){
  472.                 var val = record.get(cfg[i].dataIndex);
  473.                 if(val){
  474.                     cfg[i].sumvalue -= val;
  475.                 }
  476.             }
  477.         }
  478.         this.updateHeaders();
  479.     },
  480.     /**
  481.      * 重新计算合计列
  482.      */
  483.     recalculation:function(ds){
  484.         
  485.         this.clearSumZero();
  486.         if(true != this.hasSumColumn){
  487.             return;
  488.         }
  489.         var colCount = this.cm.getColumnCount();
  490.         var records = ds.getRange();
  491.         for(var k=0; k<records.length; ++k){
  492.             record = records[k];
  493.             for(var i=0; i<this.cm.config.length; ++i){
  494.                 if(true == this.cm.config[i].issum){
  495.                     var val = record.get(this.cm.config[i].dataIndex);
  496.                     if(typeof val == 'number'){
  497.                         this.cm.config[i].sumvalue += val;
  498.                     }
  499.                 }
  500.             }
  501.         };
  502.         
  503.         this.updateHeaders();
  504.         
  505.     }
  506.     
  507. });
  508. Ext.grid.ColumnModelEx = function(config) {
  509.     
  510.     Ext.grid.ColumnModelEx.superclass.constructor.call(this, config);
  511.     //alert('config.groupedHeaders ' + config.groupedHeaders);
  512.     //this.groupedHeaders = config.groupedHeaders;
  513.     
  514. };
  515. Ext.extend(Ext.grid.ColumnModelEx, Ext.grid.ColumnModel, {
  516.     /**
  517.      * Returns true if the specified column menu is disabled.
  518.      * @param {Number} col The column index
  519.      * @return {Boolean}
  520.      */
  521.     isMenuDisabled : function(col){
  522.         if(-1 == col){
  523.             return true;
  524.         }else{
  525.             return !!this.config[col].menuDisabled;
  526.         }
  527.     }
  528. });
  529. Ext.reg('gridex', Ext.grid.GridPanelEx);
  530. Ext.reg('editorgridex', Ext.grid.EditorGridPanelEx);

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值