<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <link rel="Stylesheet" href="resources/css/ext-all.css" mce_href="resources/css/ext-all.css" /> <mce:script type="text/javascript" src="ext/ext-base.js" mce_src="ext/ext-base.js"></mce:script> <mce:script type="text/javascript" src="ext/ext-all.js" mce_src="ext/ext-all.js"></mce:script> <mce:script type="text/javascript" src="js/jsonReader.js" mce_src="js/jsonReader.js"></mce:script> <mce:script type="text/javascript" src="js/GroupSummary.js" mce_src="js/GroupSummary.js"></mce:script> <mce:script type="text/javascript" src="js/pagingMemoryProxy.js" mce_src="js/pagingMemoryProxy.js"></mce:script> <mce:script type="text/javascript"><!-- Ext.onReady(function() { var sm = new Ext.grid.CheckboxSelectionModel(); var renderSummary = function(o, cs, cm) { var sum = 0; grid.store.each(function(record) { sum += Number(record.data.js); }); document.getElementById("zjs").value = sum; return '合计:' + sum; } var cm = new Ext.grid.ColumnModel([ sm, { header: '编号', dataIndex: 'id', width: 50, sortable: true, editor: new Ext.grid.GridEditor(new Ext.form.TextField({ allowBlank: false })) }, { header: '货物名称', dataIndex: 'hwmc', width: 60, sortable: true, editor: new Ext.grid.GridEditor(new Ext.form.TextField({ allowBlank: false })) }, { header: '件数', dataIndex: 'js', summaryRenderer: renderSummary, width: 200, sortable: true, editor: new Ext.grid.GridEditor(new Ext.form.TextField({ allowBlank: false })) } ]); var data = [ ['1', 'name1', '1'], ['2', 'name2', '2'], ['3', 'name3', '3'], ['4', 'name4', '4'], ['5', 'name5', '5'] ]; var Record = Ext.data.Record.create([ { name: 'id', type: 'string' }, { name: 'hwmc', type: 'string' }, { name: 'js', type: 'int' } ]); var ds = new Ext.data.Store({ proxy: new Ext.data.MemoryProxy(data), reader: new Ext.data.ArrayReader({}, [ { name: 'id' }, { name: 'hwmc' }, { name: 'js' } ]) //reader:jr }); ds.load(); var summary = new Ext.ux.grid.GridSummary(); var grid = new Ext.grid.EditorGridPanel({ plugins: summary, el: 'grid', width: 600, height: 200, sm: new Ext.grid.RowSelectionModel({ singleSelect: true }), ds: ds, cm: cm, frame: true, tbar: [{ text: "增加一行", handler: function() { var e = new Record({ id: '', hwmc: '', js: '' }); grid.stopEditing(); ds.insert(0, e); //grid.startEditing(0, 0); } }, { text: "删除一行", handler: function() { selectedRow = grid.getSelectionModel().getSelected(); if (selectedRow) { ds.remove(selectedRow); } //GridSum(); } }, { text: "保存", handler: function() { //GridSum(); } }] }); grid.render(); // var GridSum = function() { // var sum = 0; // grid.store.each(function(record) { // sum += Number(record.data.js); // }); // document.getElementById("zjs").value = sum; // } //GridSum(); }); // --></mce:script> </head> <body> <form id="form1"> <div id="grid"> </div> 总件数<input type="text" id="zjs"/> </form> </body> <html> GroupSummary.js Ext.ns('Ext.ux.grid'); Ext.ux.grid.GridSummary = function(config) { Ext.apply(this, config); }; Ext.extend(Ext.ux.grid.GridSummary, Ext.util.Observable, { init: function(grid) { this.grid = grid; this.cm = grid.getColumnModel(); this.view = grid.getView(); var v = this.view; // override GridView's onLayout() method v.onLayout = this.onLayout; v.afterMethod('render', this.refreshSummary, this); v.afterMethod('refresh', this.refreshSummary, this); v.afterMethod('syncScroll', this.syncSummaryScroll, this); v.afterMethod('onColumnWidthUpdated', this.doWidth, this); v.afterMethod('onAllColumnWidthsUpdated', this.doAllWidths, this); v.afterMethod('onColumnHiddenUpdated', this.doHidden, this); // update summary row on store's add/remove/clear/update events grid.store.on({ add: this.refreshSummary, remove: this.refreshSummary, clear: this.refreshSummary, update: this.refreshSummary, scope: this }); if (!this.rowTpl) { this.rowTpl = new Ext.Template( '<div class="x-grid3-summary-row x-grid3-gridsummary-row-offset">', '<table class="x-grid3-summary-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}" mce_style="{tstyle}">', '<tbody><tr>{cells}</tr></tbody>', '</table>', '</div>' ); this.rowTpl.disableFormats = true; } this.rowTpl.compile(); if (!this.cellTpl) { this.cellTpl = new Ext.Template( '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" mce_style="{style}">', '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on" {attr}>{value}</div>', "</td>" ); this.cellTpl.disableFormats = true; } this.cellTpl.compile(); }, calculate: function(rs, cm) { var data = {}, cfg = cm.config; for (var i = 0, len = cfg.length; i < len; i++) { // loop through all columns in ColumnModel var cf = cfg[i], // get column's configuration cname = cf.dataIndex; // get column dataIndex // initialise grid summary row data for // the current column being worked on data[cname] = 0; if (cf.summaryType) { for (var j = 0, jlen = rs.length; j < jlen; j++) { var r = rs[j]; // get a single Record data[cname] = Ext.ux.grid.GridSummary.Calculations[cf.summaryType](r.get(cname), r, cname, data, j); } } } return data; }, onLayout: function(vw, vh) { if (Ext.type(vh) != 'number') { // handles grid's height:'auto' config return; } // note: this method is scoped to the GridView if (!this.grid.getGridEl().hasClass('x-grid-hide-gridsummary')) { // readjust gridview's height only if grid summary row is visible this.scroller.setHeight(vh - this.summary.getHeight()); } }, syncSummaryScroll: function() { var mb = this.view.scroller.dom; this.view.summaryWrap.dom.scrollLeft = mb.scrollLeft; this.view.summaryWrap.dom.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore) }, doWidth: function(col, w, tw) { var s = this.view.summary.dom; s.firstChild.style.width = tw; s.firstChild.rows[0].childNodes[col].style.width = w; }, doAllWidths: function(ws, tw) { var s = this.view.summary.dom, wlen = ws.length; s.firstChild.style.width = tw; var cells = s.firstChild.rows[0].childNodes; for (var j = 0; j < wlen; j++) { cells[j].style.width = ws[j]; } }, doHidden: function(col, hidden, tw) { var s = this.view.summary.dom, display = hidden ? 'none' : ''; s.firstChild.style.width = tw; s.firstChild.rows[0].childNodes[col].style.display = display; }, renderSummary: function(o, cs, cm) { cs = cs || this.view.getColumnData(); var cfg = cm.config, buf = [], last = cs.length - 1; for (var i = 0, len = cs.length; i < len; i++) { var c = cs[i], cf = cfg[i], p = {}; p.id = c.id; p.style = c.style; p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : ''); if (cf.summaryType || cf.summaryRenderer) { p.value = (cf.summaryRenderer || c.renderer)(o.data[c.name], p, o); } else { p.value = ''; } //此处设置默认不显示时用什么符号标记 if (p.value == undefined || p.value === "") p.value = "-"; buf[buf.length] = this.cellTpl.apply(p); } return this.rowTpl.apply({ tstyle: 'width:' + this.view.getTotalWidth() + ';', cells: buf.join('') }); }, refreshSummary: function() { var g = this.grid, ds = g.store, cs = this.view.getColumnData(), cm = this.cm, rs = ds.getRange(), data = this.calculate(rs, cm), buf = this.renderSummary({ data: data }, cs, cm); if (!this.view.summaryWrap) { this.view.summaryWrap = Ext.DomHelper.insertAfter(this.view.scroller, { tag: 'div', cls: 'x-grid3-gridsummary-row-inner' }, true); } this.view.summary = this.view.summaryWrap.update(buf).first(); }, toggleSummary: function(visible) { // true to display summary row var el = this.grid.getGridEl(); if (el) { if (visible === undefined) { visible = el.hasClass('x-grid-hide-gridsummary'); } el[visible ? 'removeClass' : 'addClass']('x-grid-hide-gridsummary'); this.view.layout(); // readjust gridview height } }, getSummaryNode: function() { return this.view.summary } }); Ext.reg('gridsummary', Ext.ux.grid.GridSummary); /* * all Calculation methods are called on each Record in the Store * with the following 5 parameters: * * v - cell value * record - reference to the current Record * colName - column name (i.e. the ColumnModel's dataIndex) * data - the cumulative data for the current column + summaryType up to the current Record * rowIdx - current row index */ Ext.ux.grid.GridSummary.Calculations = { sum: function(v, record, colName, data, rowIdx) { return data[colName] + Ext.num(v, 0); }, count: function(v, record, colName, data, rowIdx) { return rowIdx + 1; }, max: function(v, record, colName, data, rowIdx) { return Math.max(Ext.num(v, 0), data[colName]); }, min: function(v, record, colName, data, rowIdx) { return Math.min(Ext.num(v, 0), data[colName]); }, average: function(v, record, colName, data, rowIdx) { var t = data[colName] + Ext.num(v, 0), count = record.store.getCount(); return rowIdx == count - 1 ? (t / count) : t; } } JsonReader.js /* * Ext JS Library 3.0 RC1 * Copyright(c) 2006-2009, Ext JS, LLC. * licensing@extjs.com * * http://extjs.com/license */ /** * @class Ext.data.JsonReader * @extends Ext.data.DataReader * Data reader class to create an Array of {@link Ext.data.Record} objects from a JSON response * based on mappings in a provided {@link Ext.data.Record} constructor.<br> * <p> * Example code: * <pre><code> var Employee = Ext.data.Record.create([ {name: 'firstname'}, // map the Record's "firstname" field to the row object's key of the same name {name: 'job', mapping: 'occupation'} // map the Record's "job" field to the row object's "occupation" key ]); var myReader = new Ext.data.JsonReader( { // The metadata property, with configuration options: totalProperty: "results", // the property which contains the total dataset size (optional) root: "rows", // the property which contains an Array of row objects idProperty: "id" // the property within each row object that provides an ID for the record (optional) }, Employee // {@link Ext.data.Record} constructor that provides mapping for JSON object ); </code></pre> * <p>This would consume a JSON data object of the form:</p><pre><code> { results: 2, // Reader's configured totalProperty rows: [ // Reader's configured root { id: 1, firstname: 'Bill', occupation: 'Gardener' }, // a row object { id: 2, firstname: 'Ben' , occupation: 'Horticulturalist' } // another row object ] } </code></pre> * <p><b><u>Automatic configuration using metaData</u></b> * <p>It is possible to change a JsonReader's metadata at any time by including a <b><tt>metaData</tt></b> * property in the JSON data object. If the JSON data object has a <b><tt>metaData</tt></b> property, a * {@link Ext.data.Store Store} object using this Reader will reconfigure itself to use the newly provided * field definition and fire its {@link Ext.data.Store#metachange metachange} event. The metachange event * handler may interrogate the <b><tt>metaData</tt></b> property to perform any configuration required. * Note that reconfiguring a Store potentially invalidates objects which may refer to Fields or Records * which no longer exist.</p> * <p>The <b><tt>metaData</tt></b> property in the JSON data object may contain:</p> * <div class="mdetail-params"><ul> * <li>any of the configuration options for this class</li> * <li>a <b><tt>{@link Ext.data.Record#fields fields}</tt></b> property which the JsonReader will * use as an argument to the {@link Ext.data.Record#create data Record create method} in order to * configure the layout of the Records it will produce.</li> * <li>a <b><tt>{@link Ext.data.Store#sortInfo sortInfo}</tt></b> property which the JsonReader will * use to set the {@link Ext.data.Store}'s {@link Ext.data.Store#sortInfo sortInfo} property</li> * <li>any user-defined properties needed</li> * </ul></div> * <p>To use this facility to send the same data as the example above (without having to code the creation * of the Record constructor), you would create the JsonReader like this:</p><pre><code> var myReader = new Ext.data.JsonReader(); </code></pre> * <p>The first data packet from the server would configure the reader by containing a * <b><tt>metaData</tt></b> property <b>and</b> the data. For example, the JSON data object might take * the form:</p> <pre><code> { metaData: { idProperty: 'id', root: 'rows', totalProperty: 'results', fields: [ {name: 'name'}, {name: 'job', mapping: 'occupation'} ], sortInfo: {field: 'name', direction:'ASC'}, // used by store to set its sortInfo foo: 'bar' // custom property }, results: 2, rows: [ { 'id': 1, 'name': 'Bill', occupation: 'Gardener' }, { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ] } </code></pre> * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being * paged from the remote server. * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms. * @cfg {String} root name of the property which contains the Array of row objects. * @cfg {String} idProperty Name of the property within a row object that contains a record identifier value. * @constructor * Create a new JsonReader * @param {Object} meta Metadata configuration options. * @param {Object} recordType Either an Array of field definition objects as passed to * {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record} constructor created using {@link Ext.data.Record#create}. */ Ext.data.JsonReader = function(meta, recordType){ meta = meta || {}; this.dataSum = 0; Ext.data.JsonReader.superclass.constructor.call(this, meta, recordType || meta.fields); }; Ext.extend(Ext.data.JsonReader, Ext.data.DataReader, { /** * This JsonReader's metadata as passed to the constructor, or as passed in * the last data packet's <b><tt>metaData</tt></b> property. * @type Mixed * @property meta */ /** * This method is only used by a DataProxy which has retrieved data from a remote server. * @param {Object} response The XHR object which contains the JSON data in its responseText. * @return {Object} data A data block which is used by an Ext.data.Store object as * a cache of Ext.data.Records. */ read : function(response){ var json = response.responseText; var o = Ext.decode(json); if(!o) { throw {message: "JsonReader.read: Json object not found"}; } return this.readRecords(o); }, // private function a store will implement onMetaChange : function(meta, recordType, o){ }, /** * @ignore */ simpleAccess: function(obj, subsc) { return obj[subsc]; }, /** * @ignore */ getJsonAccessor: function(){ var re = /[/[/.]/; return function(expr) { try { return(re.test(expr)) ? new Function("obj", "return obj." + expr) : function(obj){ return obj[expr]; }; } catch(e){} return Ext.emptyFn; }; }(), /** * Create a data block containing Ext.data.Records from a JSON object. * @param {Object} o An object which contains an Array of row objects in the property specified * in the config as 'root, and optionally a property, specified in the config as 'totalProperty' * which contains the total size of the dataset. * @return {Object} data A data block which is used by an Ext.data.Store object as * a cache of Ext.data.Records. */ readRecords : function(o){ /** * After any data loads, the raw JSON data is available for further custom processing. If no data is * loaded or there is a load exception this property will be undefined. * @type Object */ this.jsonData = o; if(o.metaData){ delete this.ef; this.meta = o.metaData; this.recordType = Ext.data.Record.create(o.metaData.fields); this.onMetaChange(this.meta, this.recordType, o); } var s = this.meta, Record = this.recordType, f = Record.prototype.fields, fi = f.items, fl = f.length; // Generate extraction functions for the totalProperty, the root, the id, and for each field if (!this.ef) { // over 2009-5-3 if(s.dataSum){ this.dataSum = o.dataSum; } if(s.totalProperty) { this.getTotal = this.getJsonAccessor(s.totalProperty); } if(s.successProperty) { this.getSuccess = this.getJsonAccessor(s.successProperty); } this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;}; if (s.id || s.idProperty) { var g = this.getJsonAccessor(s.id || s.idProperty); this.getId = function(rec) { var r = g(rec); return (r === undefined || r === "") ? null : r; }; } else { this.getId = function(){return null;}; } this.ef = []; for(var i = 0; i < fl; i++){ f = fi[i]; var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name; this.ef[i] = this.getJsonAccessor(map); } } var root = this.getRoot(o), c = root.length, totalRecords = c, success = true; //add if(s.dataSum){ this.dataSum = o.dataSum; } if(s.totalProperty){ var v = parseInt(this.getTotal(o), 10); if(!isNaN(v)){ totalRecords = v; } } if(s.successProperty){ var v = this.getSuccess(o); if(v === false || v === 'false'){ success = false; } } var records = []; for(var i = 0; i < c; i++){ var n = root[i]; var values = {}; var id = this.getId(n); for(var j = 0; j < fl; j++){ f = fi[j]; var v = this.ef[j](n); values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, n); } var record = new Record(values, id); record.json = n; records[i] = record; } return { success : success, records : records, totalRecords : totalRecords }; }, /** * readResponse * decodes a json response from server * @param {Object} response */ readResponse : function(response) { var json = response.responseText; var o = eval("("+json+")"); if(!o) { throw {message: "JsonReader.read: Json object not found"}; } return o; } }); PagingMemoryProxy.js /* * Ext JS Library 2.0 * Copyright(c) 2006-2007, Ext JS, LLC. * licensing@extjs.com * * http://extjs.com/license */ /* Fix for Opera, which does not seem to include the map function on Array's */ if(!Array.prototype.map){ Array.prototype.map = function(fun){ var len = this.length; if(typeof fun != "function"){ throw new TypeError(); } var res = new Array(len); var thisp = arguments[1]; for(var i = 0; i < len; i++){ if(i in this){ res[i] = fun.call(thisp, this[i], i, this); } } return res; }; } /* Paging Memory Proxy, allows to use paging grid with in memory dataset */ Ext.data.PagingMemoryProxy = function(data) { Ext.data.PagingMemoryProxy.superclass.constructor.call(this); this.data = data; }; Ext.extend(Ext.data.PagingMemoryProxy, Ext.data.MemoryProxy, { load : function(params, reader, callback, scope, arg) { params = params || {}; var result; try { result = reader.readRecords(this.data); }catch(e){ this.fireEvent("loadexception", this, arg, null, e); callback.call(scope, null, arg, false); return; } // filtering if (params.filter!==undefined) { result.records = result.records.filter(function(el){ if (typeof(el)=="object"){ var att = params.filterCol || 0; return String(el.data[att]).match(params.filter)?true:false; } else { return String(el).match(params.filter)?true:false; } }); result.totalRecords = result.records.length; } // sorting if (params.sort!==undefined) { // use integer as params.sort to specify column, since arrays are not named // params.sort=0; would also match a array without columns var dir = String(params.dir).toUpperCase() == "DESC" ? -1 : 1; var fn = function(r1, r2){ return r1 < r2; }; result.records.sort(function(a, b) { var v = 0; if (typeof(a)=="object"){ v = fn(a.data[params.sort], b.data[params.sort]) * dir; } else { v = fn(a, b) * dir; } if (v==0) { v = (a.index < b.index ? -1 : 1); } return v; }); } // paging (use undefined cause start can also be 0 (thus false)) if (params.start!==undefined && params.limit!==undefined) { result.records = result.records.slice(params.start, params.start+params.limit); } callback.call(scope, result, arg, true); } });