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

 

/*
 * Ext JS Library 2.1
 * Copyright(c) 2006-2008, Ext JS, LLC.
 * licensing@extjs.com
 * 
 * http://extjs.com/license
 */

/**
 * @class Ext.grid.EditorGridPanelEx
 * @extends Ext.grid.EditorGridPanel
 * @author 张尧伟
 * @version 1.0
 * <p>此类是为增加合计行而设计的</p>
 * <br><br>Usage:
 * <pre><code>
     function createPreplanDetailGrid()
    {
         var preplanDetailStore = new Ext.data.DWRStore({id:'preplanDetailStore',fn:UIPreContractReport.getPreContractDetail,
            fields: [
               {name: 'bar'},
               {name: 'itemName'},
               {name: 'storeAmount', type: 'float'},
               {name: 'endAmount',type:'float'},
               {name: 'currentRate',type:'float'},
               {name: 'weekPercent',type:'float'},
               {name: 'confirmAmount',type:'float'},
               {name: 'accPreAmount',type:'float'},
               {name: 'surplusTargetAmount',type:'int'},
               {name:'surplusHyearAmount',type:'float'}
            ]
        });
        
        var sm = new Ext.grid.CheckboxSelectionModel();
        var preplanDetailGrid = new Ext.grid.EditorGridPanelEx({
            id:'preplanDetailGrid',
            title:'卷烟规格明细',
            store: preplanDetailStore,
            cm: new Ext.grid.ColumnModelEx({sumheader:true,columns:[
                sm,
                {header: "卷烟条码", width:90,  sortable: true, dataIndex: 'bar',sumcaption:'合计'},
                {header: "卷烟规格",  width:110,sortable: true,  dataIndex: 'itemName'},
                {header: "预计划量",  width:100,sortable: true, dataIndex: 'endAmount',editor:new Ext.form.NumberField(),align:'right',renderer:Util.rmbMoney,issum:true},
                {header: "商业库存",  width:100,sortable: true, dataIndex: 'storeAmount',align:'right',renderer:Util.rmbMoney},
                {header: "到货存销比",  width:100,sortable: true, dataIndex: 'currentRate',align:'right',renderer:Util.rmbMoney},
                {header: "周进度",  width:100,sortable: true, dataIndex: 'weekPercent',align:'right'},
                {header: "商业请求量",  width:100,sortable: true, dataIndex: 'confirmAmount',align:'right',renderer:Util.rmbMoney,issum:true},
                {header: "累计预计划量",  width:100,sortable: true, dataIndex: 'accPreAmount',align:'right',renderer:Util.rmbMoney},
                {header: "剩余指标量",  width:100,sortable: true, dataIndex: 'surplusTargetAmount',align:'right',renderer:Util.rmbMoney,issum:true},
                {header: "剩余协议量",  width:100,sortable: true, dataIndex: 'surplusHyearAmount',align:'right',renderer:Util.rmbMoney,issum:true}
            ]}),
            sm:sm,
            stripeRows: true,
            height:Util.getH(0.4),
            bbar:[
                {pressed:true,text:'删除',handler:deletePreplanDetail},{xtype:'tbseparator'},
                {pressed:true,text:'保存',handler:saveModified}
                ]
        });
    
        return preplanDetailGrid;
    }
 </code></pre>
 * <b>Notes:</b> <br/>
 * - Ext.grid.ColumnModel需要增加属性:sumheader:true以指示需要使用合计行
 * - Ext.grid.ColumnModel的columns属性中,需要增加sumcaption:'合计'属性,以指示需要显示的合计标题名称
 * - Ext.grid.ColumnModel的columns属性中,需要增加issum:true属性,以指示该列需要计算合计
 * - Ext.grid.ColumnModel的columns属性中,可选增加sfn:function(sumvalue,colvalue,record){return 计算结果;},
 * 以指示该列需要使用用户自定义函数进行计算sumvalue:此列当前的合计值,colvalue当前计算的列值,record当前记录对象
 * <br>
 * @constructor
 * @param {Object} config The config object
 */

Ext.grid.GridPanelEx = Ext.extend(Ext.grid.GridPanel, {
  getView : function() {
        if (!this.view) {
            this.view = new Ext.grid.GridViewEx(this.viewConfig);
        }
        return this.view;
    },
  initComponent : function() {
    if (!this.cm) {
        
        this.cm = new Ext.grid.ColumnModelEx(this.columns);
        this.colModel = this.cm;
        delete this.columns;
    }
    if(!this.groupedHeaders)
    {
        this.groupedHeaders = this.groupedHeaders;
    }
    //在编辑完成后,需要更新合计列
    //this.on('afteredit',this.onAfteredit,this);
    Ext.grid.GridPanelEx.superclass.initComponent.call(this);
  },
  
  getGroupedHeaders : function(){
        return this.groupedHeaders;
  },
  /**
   * 处理修改后,更新合计
   * @param {} event
   * grid - This grid
   * record - The record being edited
   * field - The field name being edited
   * value - The value being set
   * originalValue - The original value for the field, before the edit.
   * row - The grid row index
   * column - The grid column index
   */
  onAfteredit:function(event){
    var grid = event.grid;
    var cm = grid.getColumnModel();
    if(false == cm.sumheader)
    {
        return;
    }

    var value = event.value;
    var origValue = event.originalValue;
    
    cm.config[event.column].sumvalue -= origValue;
    cm.config[event.column].sumvalue += value;
    grid.getView().updateHeaders();
  },
  /**
   * 重新计算合计列
   */
  recalculation:function(){
    var cm = this.getColumnModel();
    if(true != cm.sumheader)
    {
        return;
    }   
    var view = this.getView();
    view.recalculation(this.getStore());
  }
});


Ext.grid.EditorGridPanelEx = Ext.extend(Ext.grid.EditorGridPanel, {
  getView : function() {
        if (!this.view) {
            this.view = new Ext.grid.GridViewEx(this.viewConfig);
        }
        return this.view;
    },
  initComponent : function() {
    if (!this.cm) {
        
        this.cm = new Ext.grid.ColumnModelEx(this.columns);
        this.colModel = this.cm;
        delete this.columns;
    }
    if(!this.groupedHeaders)
    {
        this.groupedHeaders = this.groupedHeaders;
    }
    //在编辑完成后,需要更新合计列
    this.on('afteredit',this.onAfteredit,this);
    Ext.grid.EditorGridPanelEx.superclass.initComponent.call(this);
  },
  
  getGroupedHeaders : function(){
        return this.groupedHeaders;
  },
  /**
   * 处理修改后,更新合计
   * @param {} event
   * grid - This grid
   * record - The record being edited
   * field - The field name being edited
   * value - The value being set
   * originalValue - The original value for the field, before the edit.
   * row - The grid row index
   * column - The grid column index
   */
  onAfteredit:function(event){
    var grid = event.grid;
    var cm = grid.getColumnModel();
    if(false == cm.sumheader)
    {
        return;
    }

    var value = event.value;
    var origValue = event.originalValue;
    
    cm.config[event.column].sumvalue -= origValue;
    cm.config[event.column].sumvalue += value;
    grid.getView().updateHeaders();
  },
  /**
   * 重新计算合计列
   */
  recalculation:function(){
    var cm = this.getColumnModel();
    if(true != cm.sumheader)
    {
        return;
    }   
    var view = this.getView();
    view.recalculation(this.getStore());
  }
});

Ext.grid.GridViewEx = function(config) {
    Ext.apply(this, config);
    if (!this.templates) this.templates = {};
    
    
    //增加合计行模板
    if(!this.templates.header){
            this.templates.header = new Ext.Template(
                    '<table border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
                    '<thead><tr class="x-grid3-hd-row">{cells}</tr><tr class="x-grid3-hd-row x-grid3-row-sum">{sumcells}</tr></thead>',
                    "</table>"
                    );
    }
    
    if(!this.templates.sumcells){
            this.templates.sumcells = new Ext.Template(
                    '<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}">', '',
                    '{value}', '', '',
                    "</div></td>"
                    );
    }
    
    
    Ext.grid.GridViewEx.superclass.constructor.call(this);
};

Ext.extend(Ext.grid.GridViewEx, Ext.grid.GridView, {
    
    insertRows : function(ds, firstRow, lastRow, isUpdate){
        if(!isUpdate && firstRow === 0 && lastRow == ds.getCount()-1){
            this.refresh();
        }else{
            if(!isUpdate){
                this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
            }
            var html = this.renderRows(firstRow, lastRow);
            var before = this.getRow(firstRow);
            if(before){
                Ext.DomHelper.insertHtml('beforeBegin', before, html);
            }else{
                Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html);
            }
            if(!isUpdate){
                this.fireEvent("rowsinserted", this, firstRow, lastRow);
                this.processRows(firstRow);
            }
        }
    },
    onUpdate : function(ds, record){
        this.refreshRow(record);
    },
        // private
    refreshRow : function(record){
        var ds = this.ds, index;
        if(typeof record == 'number'){
            index = record;
            record = ds.getAt(index);
        }else{
            index = ds.indexOf(record);
        }
        var cls = [];
        this.insertRows(ds, index, index, true);
        this.getRow(index).rowIndex = index;
        this.onRemove(ds, record, index+1, true);
        this.fireEvent("rowupdated", this, index, record);
        /*
        if(true == record.dirty && true == this.cm.sumheader){
            var cm = this.cm;
            var colCount =  cm.getColumnCount();
            for(var i=0; i<colCount;++i)
            {
                var c = cm.config[i];
                if( true == c.issum && typeof record.modified[c.dataIndex] !== 'undefined'){
                    var value = record.get(c.dataIndex);
                    var origValue = record.modified[c.dataIndex];
                    if(origValue){
                        cm.config[i].sumvalue -= origValue;
                        cm.config[i].sumvalue += value;
                    }
                }
            }
            this.updateHeaders();
        }*/
    },
    onRemove : function(ds, record, index, isUpdate){
        if(isUpdate !== true){
            this.fireEvent("beforerowremoved", this, index, record);
        }
        this.removeRow(index);
        if(isUpdate !== true){
            this.processRows(index);
            this.applyEmptyText();
            this.fireEvent("rowremoved", this, index, record);
            
            //处理删除行时的合计行问题
            this.processSumForRemoved(ds,record,index);
        }
    },
    
    //Template has changed and we need a few other pointers to keep track
    doRender : function(cs, rs, ds, startRow, colCount, stripe){
        var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount-1;
        var tstyle = 'width:'+this.getTotalWidth()+';';
        // buffers
        var buf = [], cb, c, p = {}, rp = {tstyle: tstyle}, r;
        //如果不是在编辑状态下,则需要将合计列的数据清0        
        if(false == rs[0].dirty && true ==(rs.length == ds.getCount())){
            this.clearSumZero();
        }
        for(var j = 0, len = rs.length; j < len; j++){
            r = rs[j]; cb = [];
            var rowIndex = (j+startRow);
            for(var i = 0; i < colCount; i++){
                c = cs[i];
                p.id = c.id;
                p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
                p.attr = p.cellAttr = "";
                p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
                p.style = c.style;
                if(p.value == undefined || p.value === "") p.value = " ";
                if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
                    p.css += ' x-grid3-dirty-cell';
                }
                cb[cb.length] = ct.apply(p);
                
                //处理求和问题
                if(true == this.cm.sumheader)
                {
                    if(true == this.cm.config[i].issum && false == r.dirty)
                    {//如果是求和列且不为脏数据
                        if(!this.cm.config[i].sumvalue){
                                this.cm.config[i].sumvalue = 0.0;
                        }
                        if(this.cm.config[i].sfn)
                        {//如果存在求和函数,则调用
                            this.cm.config[i].sumvalue = this.cm.config[i].sfn(this.cm.config[i].sumvalue,r.get(c.name),r);
                        }
                        else
                        {//否则,直接累计
                            if(r.data[c.name]){
                                this.cm.config[i].sumvalue += r.data[c.name];
                            }
                        }
                    }
                }
                
            }
            var alt = [];
            if(stripe && ((rowIndex+1) % 2 == 0)){
                alt[0] = "x-grid3-row-alt";
            }
            if(r.dirty){
                alt[1] = " x-grid3-dirty-row";
            }
            rp.cols = colCount;
            if(this.getRowClass){
                alt[2] = this.getRowClass(r, rowIndex, rp, ds);
            }
            rp.alt = alt.join(" ");
            rp.cells = cb.join("");
            buf[buf.length] =  rt.apply(rp);
        }
        
        //如果有合计行,则需要重新渲染表头
        if(true == this.cm.sumheader)
        {
            this.updateHeaders();
        }
        return buf.join("");
    },
     /**
     * 将求和的列清0
     */
    clearSumZero:function(){
        this.hasSumColumn = false;
        var cm = this.cm;
        for(var i=0; i<cm.config.length;++i)
        {
            if(cm.config[i].issum)
            {
                this.hasSumColumn = true;
                this.cm.config[i].sumvalue = 0.0;
            }
        }
    },
    renderHeaders : function(){
        var cm = this.cm, ts = this.templates;
        var ct = ts.hcell;

        var cb = [], sb = [],lb = [], lsb = [], p = {};

        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
            p.id = cm.getColumnId(i);
            p.value = cm.getColumnHeader(i) || "";
            p.style = this.getColumnStyle(i, true);
            p.tooltip = this.getColumnTooltip(i);
            if(cm.config[i].align == 'right'){
                p.istyle = 'padding-right:16px';
            } else {
                delete p.istyle;
            }
            cb[cb.length] = ct.apply(p);
        }
        
        //处理合计行表头
        if(true == cm.sumheader)
        {
            sb = this.buildSumHeaders(cm,ts);
        }
        
        return ts.header.apply({cells: cb.join(""), sumcells: sb.join(""),tstyle:'width:'+this.getTotalWidth()+';'});
        
        //return [ts.header.apply({cells: cb.join(""), sumcells: sb.join(""), tstyle:'width:'+(tw-lw)+';'}),
        //ts.header.apply({cells: lb.join(""), sumcells: lsb.join(""), tstyle:'width:'+(lw)+';'})];
        
        //return ts.header.apply({cells: cb.join(""), tstyle:'width:'+this.getTotalWidth()+';'});
    },
    
    
    /**
     * 生成合计表头
     * @param {} cm
     * @param {} ts
     * @param {} tw
     * @param {} lw
     * @return {}array
     */
    buildSumHeaders:function(cm,ts){
        var ct = ts.hcell;

        var sb = [], sp = {};

        for (var i = 0, len = cm.getColumnCount(); i < len; i++) {

            sp.id = 'sum' + cm.getColumnId(i);
            if(cm.config[i].sumcaption){
                sp.value = cm.config[i].sumcaption;
            }else{
                sp.value = " ";
            }
            
            //cm.config[i].sumcaption = " ";
            if (true == cm.config[i].issum && typeof cm.config[i].sumvalue == 'number'){
                if(cm.config[i].renderer)
                {
                    sp.value = cm.config[i].renderer(cm.config[i].sumvalue);
                }else{
                    sp.value = cm.config[i].sumvalue;
                }
            }
            
            
            sp.style = this.getColumnStyle(i, false);
            sp.tooltip = this.getColumnTooltip(i);
            
            if (cm.config[i].align == 'right') {
                sp.istyle = 'padding-right:16px';
            }

             sb[sb.length] = ct.apply(sp);

        }
        return sb;
    },
    /**
     * 处理删除行时,更新合计行的数据
     * @param {} ds
     * @param {} record
     * @param {} index
     */
    processSumForRemoved: function(ds,record,index){
        var cm = this.cm;
        if(true != cm.sumheader){
            return;
        }
        
        var cfg = cm.config;
        for(var i=0; i<cfg.length; ++i){
            if(true == cfg[i].issum && cfg[i].sumvalue){
                var val = record.get(cfg[i].dataIndex);
                if(val){
                    cfg[i].sumvalue -= val;
                }
            }
        }
        this.updateHeaders();
    },
    /**
     * 重新计算合计列
     */
    recalculation:function(ds){
        
        this.clearSumZero();
        if(true != this.hasSumColumn){
            return;
        }

        var colCount = this.cm.getColumnCount();
        var records = ds.getRange();
        for(var k=0; k<records.length; ++k){
            record = records[k];
            for(var i=0; i<this.cm.config.length; ++i){
                if(true == this.cm.config[i].issum){
                    var val = record.get(this.cm.config[i].dataIndex);
                    if(typeof val == 'number'){
                        this.cm.config[i].sumvalue += val;
                    }
                }
            }
        };
        
        this.updateHeaders();
        
    }
    
});


Ext.grid.ColumnModelEx = function(config) {
    
    Ext.grid.ColumnModelEx.superclass.constructor.call(this, config);
    //alert('config.groupedHeaders ' + config.groupedHeaders);
    //this.groupedHeaders = config.groupedHeaders;
    
};

Ext.extend(Ext.grid.ColumnModelEx, Ext.grid.ColumnModel, {
    /**
     * Returns true if the specified column menu is disabled.
     * @param {Number} col The column index
     * @return {Boolean}
     */
    isMenuDisabled : function(col){
        if(-1 == col){
            return true;
        }else{
            return !!this.config[col].menuDisabled;
        }
    }
});
Ext.reg('gridex', Ext.grid.GridPanelEx);
Ext.reg('editorgridex', Ext.grid.EditorGridPanelEx);





本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/ynzhangyao/archive/2008/10/17/3091432.aspx

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值