原文:Converting an Ext 5 Grid to Excel Spreadsheet
稍微迟来的礼物――Ext JS Grid转为Excel代码,现在支持Ext JS 5!
功能包括:
- 支持分组
- 数字的处理 VS 字符串数据类型
- 对于不支持客户端下载的浏览器会提交回服务器
Enjoy!
/* Excel.js - convert an ExtJS 5 grid into an Excel spreadsheet using nothing but javascript and good intentions. By: Steve Drucker Dec 26, 2014 Original Ext 3 Implementation by: Nige "Animal" White? Contact Info: e. [email protected] blog: druckit.wordpress.com linkedin: www.linkedin.com/in/uberfig git: http://github.com/sdruckerfig company: Fig Leaf Software (http://www.figleaf.com / http://training.figleaf.com) Invocation: grid.downloadExcelXml(includeHiddenColumns,title) Upgraded for ExtJS5 on Dec 26, 2014 */var Base64 = (function() { // Private property var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; // Private method for UTF-8 encoding function utf8Encode(string) { string = string.replace(/\r\n/g, "\n"); var utftext = ""; for (var n = 0; n < string.length; n++) { var c = string.charCodeAt(n); if (c < 128) { utftext += String.fromCharCode(c); } else if ((c > 127) && (c < 2048)) { utftext += String.fromCharCode((c >> 6) | 192); utftext += String.fromCharCode((c & 63) | 128); } else { utftext += String.fromCharCode((c >> 12) | 224); utftext += String.fromCharCode(((c >> 6) & 63) | 128); utftext += String.fromCharCode((c & 63) | 128); } } return utftext; } // Public method for encoding return { encode: (typeof btoa == 'function') ? function(input) { return btoa(utf8Encode(input)); } : function(input) { var output = ""; var chr1, chr2, chr3, enc1, enc2, enc3, enc4; var i = 0; input = utf8Encode(input); while (i < input.length) { chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4); } return output; } }; })(); Ext.define('MyApp.overrides.view.Grid', { override: 'Ext.grid.GridPanel', requires: 'Ext.form.action.StandardSubmit', /* Kick off process */ downloadExcelXml: function(includeHidden, title) { if (!title) title = this.title; var vExportContent = this.getExcelXml(includeHidden, title); /* dynamically create and anchor tag to force download with suggested filename note: download attribute is Google Chrome specific */ if (Ext.isChrome) { var gridEl = this.getEl(); var location = 'data:application/vnd.ms-excel;base64,' + Base64.encode(vExportContent); var el = Ext.DomHelper.append(gridEl, { tag: "a", download: title + "-" + Ext.Date.format(new Date(), 'Y-m-d Hi') + '.xls', href: location }); el.click(); Ext.fly(el).destroy(); } else { var form = this.down('form#uploadForm'); if (form) { form.destroy(); } form = this.add({ xtype: 'form', itemId: 'uploadForm', hidden: true, standardSubmit: true, url: 'http://webapps.figleaf.com/dataservices/Excel.cfc?method=echo&mimetype=application/vnd.ms-excel&filename=' + escape(title + ".xls"), items: [{ xtype: 'hiddenfield', name: 'data', value: vExportContent }] }); form.getForm().submit(); } }, /* Welcome to XML Hell See: http://msdn.microsoft.com/en-us/library/office/aa140066(v=office.10).aspx for more details */ getExcelXml: function(includeHidden, title) { var theTitle = title || this.title; var worksheet = this.createWorksheet(includeHidden, theTitle); if (this.columnManager.columns) { var totalWidth = this.columnManager.columns.length; } else { var totalWidth = this.columns.length; } return ''.concat( '<?xml version="1.0"?>', '<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40">', '<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office"><Title>' + theTitle + '</Title></DocumentProperties>', '<OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office"><AllowPNG/></OfficeDocumentSettings>', '<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">', '<WindowHeight>' + worksheet.height + '</WindowHeight>', '<WindowWidth>' + worksheet.width + '</WindowWidth>', '<ProtectStructure>False</ProtectStructure>', '<ProtectWindows>False</ProtectWindows>', '</ExcelWorkbook>', '<Styles>', '<Style ss:ID="Default" ss:Name="Normal">', '<Alignment ss:Vertical="Bottom"/>', '<Borders/>', '<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000"/>', '<Interior/>', '<NumberFormat/>', '<Protection/>', '</Style>', '<Style ss:ID="title">', '<Borders />', '<Font ss:Bold="1" ss:Size="18" />', '<Alignment ss:Horizontal="Center" ss:Vertical="Center" ss:WrapText="1" />', '<NumberFormat ss:Format="@" />', '</Style>', '<Style ss:ID="headercell">', '<Font ss:Bold="1" ss:Size="10" />', '<Alignment ss:Horizontal="Center" ss:WrapText="1" />', '<Interior ss:Color="#A3C9F1" ss:Pattern="Solid" />', '</Style>', '<Style ss:ID="even">', '<Interior ss:Color="#CCFFFF" ss:Pattern="Solid" />', '</Style>', '<Style ss:ID="evendate" ss:Parent="even">', '<NumberFormat ss:Format="yyyy-mm-dd" />', '</Style>', '<Style ss:ID="evenint" ss:Parent="even">', '<Numberformat ss:Format="0" />', '</Style>', '<Style ss:ID="evenfloat" ss:Parent="even">', '<Numberformat ss:Format="0.00" />', '</Style>', '<Style ss:ID="odd">', '<Interior ss:Color="#CCCCFF" ss:Pattern="Solid" />', '</Style>', '<Style ss:ID="groupSeparator">', '<Interior ss:Color="#D3D3D3" ss:Pattern="Solid" />', '</Style>', '<Style ss:ID="odddate" ss:Parent="odd">', '<NumberFormat ss:Format="yyyy-mm-dd" />', '</Style>', '<Style ss:ID="oddint" ss:Parent="odd">', '<NumberFormat Format="0" />', '</Style>', '<Style ss:ID="oddfloat" ss:Parent="odd">', '<NumberFormat Format="0.00" />', '</Style>', '</Styles>', worksheet.xml, '</Workbook>' ); }, /* Support function to return field info from store based on fieldname */ getModelField: function(fieldName) { var fields = this.store.model.getFields(); for (var i = 0; i < fields.length; i++) { if (fields[i].name === fieldName) { return fields[i]; } } }, /* Convert store into Excel Worksheet */ generateEmptyGroupRow: function(dataIndex, value, cellTypes, includeHidden) { var cm = this.columnManager.columns; var colCount = cm.length; var rowTpl = '<Row ss:AutoFitHeight="0"><Cell ss:StyleID="groupSeparator" ss:MergeAcross="{0}"><Data ss:Type="String"><html:b>{1}</html:b></Data></Cell></Row>'; var visibleCols = 0; // rowXml += '<Cell ss:StyleID="groupSeparator">' for (var j = 0; j < colCount; j++) { if (cm[j].xtype != 'actioncolumn' && (cm[j].dataIndex != '') && (includeHidden || !cm[j].hidden)) { // rowXml += '<Cell ss:StyleID="groupSeparator"/>'; visibleCols++; } } // rowXml += "</Row>"; return Ext.String.format(rowTpl, visibleCols - 1, Ext.String.htmlEncode(value)); }, createWorksheet: function(includeHidden, theTitle) { // Calculate cell data types and extra class names which affect formatting var cellType = []; var cellTypeClass = []; console.log(this); if (this.columnManager.columns) { var cm = this.columnManager.columns; } else { var cm = this.columns; } console.log(cm); var colCount = cm.length; var totalWidthInPixels = 0; var colXml = ''; var headerXml = ''; var visibleColumnCountReduction = 0; for (var i = 0; i < cm.length; i++) { if (cm[i].xtype != 'actioncolumn' && (cm[i].dataIndex != '') && (includeHidden || !cm[i].hidden)) { var w = cm[i].getEl().getWidth(); totalWidthInPixels += w; if (cm[i].text === "") { cellType.push("None"); cellTypeClass.push(""); ++visibleColumnCountReduction; } else { colXml += '<Column ss:AutoFitWidth="1" ss:Width="' + w + '" />'; headerXml += '<Cell ss:StyleID="headercell">' + '<Data ss:Type="String">' + cm[i].text.replace("<br>"," ") + '</Data>' + '<NamedCell ss:Name="Print_Titles"></NamedCell></Cell>'; var fld = this.getModelField(cm[i].dataIndex); switch (fld.$className) { case "Ext.data.field.Integer": cellType.push("Number"); cellTypeClass.push("int"); break; case "Ext.data.field.Number": cellType.push("Number"); cellTypeClass.push("float"); break; case "Ext.data.field.Boolean": cellType.push("String"); cellTypeClass.push(""); break; case "Ext.data.field.Date": cellType.push("DateTime"); cellTypeClass.push("date"); break; default: cellType.push("String"); cellTypeClass.push(""); break; } } } } var visibleColumnCount = cellType.length - visibleColumnCountReduction; var result = { height: 9000, width: Math.floor(totalWidthInPixels * 30) + 50 }; // Generate worksheet header details. // determine number of rows var numGridRows = this.store.getCount() + 2; if ((this.store.groupField &&!Ext.isEmpty(this.store.groupField)) || (this.store.groupers && this.store.groupers.items.length > 0)) { numGridRows = numGridRows + this.store.getGroups().length; } // create header for worksheet var t = ''.concat( '<Worksheet ss:Name="' + theTitle + '">', '<Names>', '<NamedRange ss:Name="Print_Titles" ss:RefersTo="=\'' + theTitle + '\'!R1:R2">', '</NamedRange></Names>', '<Table ss:ExpandedColumnCount="' + (visibleColumnCount + 2), '" ss:ExpandedRowCount="' + numGridRows + '" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="15">', colXml, '<Row ss:Height="38">', '<Cell ss:MergeAcross="' + (visibleColumnCount - 1) + '" ss:StyleID="title">', '<Data ss:Type="String" xmlns:html="http://www.w3.org/TR/REC-html40">', '<html:b>' + theTitle + '</html:b></Data><NamedCell ss:Name="Print_Titles">', '</NamedCell></Cell>', '</Row>', '<Row ss:AutoFitHeight="1">', headerXml + '</Row>' ); // Generate the data rows from the data in the Store var groupVal = ""; var groupField = ""; if (this.store.groupers && this.store.groupers.keys.length > 0) { groupField = this.store.groupers.keys[0]; } else if (this.store.groupField != '') { groupField = this.store.groupField; } for (var i = 0, it = this.store.data.items, l = it.length; i < l; i++) { if (!Ext.isEmpty(groupField)) { if (groupVal != this.store.getAt(i).get(groupField)) { groupVal = this.store.getAt(i).get(groupField); t += this.generateEmptyGroupRow(groupField, groupVal, cellType, includeHidden); } } t += '<Row>'; var cellClass = (i & 1) ? 'odd' : 'even'; r = it[i].data; var k = 0; for (var j = 0; j < colCount; j++) { if (cm[j].xtype != 'actioncolumn' && (cm[j].dataIndex != '') && (includeHidden || !cm[j].hidden)) { var v = r[cm[j].dataIndex]; if (cellType[k] !== "None") { t += '<Cell ss:StyleID="' + cellClass + cellTypeClass[k] + '"><Data ss:Type="' + cellType[k] + '">'; if (cellType[k] == 'DateTime') { t += Ext.Date.format(v, 'Y-m-d'); } else { t += Ext.String.htmlEncode(v); } t += '</Data></Cell>'; } k++; } } t += '</Row>'; } result.xml = t.concat( '</Table>', '<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">', '<PageLayoutZoom>0</PageLayoutZoom>', '<Selected/>', '<Panes>', '<Pane>', '<Number>3</Number>', '<ActiveRow>2</ActiveRow>', '</Pane>', '</Panes>', '<ProtectObjects>False</ProtectObjects>', '<ProtectScenarios>False</ProtectScenarios>', '</WorksheetOptions>', '</Worksheet>' ); return result; } });123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
附:在原文底部有Ext JS 4版本的链接