ExtJS 给GridToExcel.js 添加分组GridPanel导出功能

原帖: 

ExtJs Grid导出到Excel(修正版)

http://extjs.org.cn/node/324

解决Ext Grid导出Excel在JSP等环境中文乱码问题(支持Windows和Aix)

http://hi.baidu.com/wangkai99/item/cb174511f84892071994ec6a

对里面的excel的xml格式也进行了一点修改, 以后主要需要改改样式, 参考的msdn

http://msdn.microsoft.com/en-us/library/office/aa140066(v=office.10).aspx


对于上传到服务器的那部分代码, 没有测试, 第2个帖子里讲了很多

我是直接chrome根据本地数据产生xls文件的, 


代码如下, 主要是添加了两段, 产生<ss:Cell>的代码, 各位有兴趣可以改良一下, 再加上对summaryType为max和min的部分

363行, 计算导出excel的总行数, 用到了上一篇帖子里扩展的获取分组后组数的方法 groupCount = this.store.getGroupCount(); 

后面产生<ss:Cell>的代码则是比较原始的比较

/**
 * allows for downloading of grid data (store) directly into excel
 * Method: extracts data of gridPanel store, uses columnModel to construct XML excel document,
 * converts to Base64, then loads everything into a data URL link.
 *
 * @author		Animal		<extjs support team>
 *
 */

/**
 * base64 encode / decode
 *
 * @location 	http://www.webtoolkit.info/
 *
 */

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.override(Ext.grid.GridPanel, {
	exportExcel: function(includeHidden, specTitle, serverUrl){
		var vExportContent = this.getExcelXml(includeHidden, specTitle);
		console.log(vExportContent);
		if (Ext.isIE6 || Ext.isIE7 || Ext.isIE8 || Ext.isSafari || Ext.isSafari2 || Ext.isSafari3) {
			//创建一个表单提交
/*			if (!Ext.fly('frmDummy')) {
				var frm = document.createElement('form');
				frm.id = 'frmDummy';
				frm.name = id;
				frm.className = 'x-hidden';
				document.body.appendChild(frm);
			}
			Ext.Ajax.request({
				url : serverUrl,
				method : 'POST',
				form : Ext.fly('frmDummy'),
				callback : function(o, s, r) {
					// alert(r.responseText);
				},
				//上传数据
				isUpload : true,
				params : {
					exportContent : vExportContent
				}
			})*/
			
			var fd=Ext.get('frmDummy');  
            if (!fd) {  
                fd=Ext.DomHelper.append(Ext.getBody(),{tag:'form',method:'post',id:'frmDummy',action:exportExcelUrl, target:'_blank',name:'frmDummy',cls:'x-hidden',cn:[  
                    {tag:'input',name:'fileName',id:'fileName',type:'hidden'},  
                    {tag:'input',name:'exportContent',id:'exportContent',type:'hidden'}  
                ]},true);  
            }  
            fd.child('#fileName').set({value:specTitle + ".xls"});  
            fd.child('#exportContent').set({value:vExportContent});  
            fd.dom.submit();  
		} else {
			//怎么指定下载的文件名??
//			headers=Content-Disposition;attachment;filename="' + specTitle + '.xls";' +
			document.location = 'data:application/vnd.ms-excel;base64,' + Base64.encode(vExportContent);
		}
	}, 
	getExcelXml: function(includeHidden, specTitle) {
      var worksheet = this.createWorksheet(includeHidden, specTitle);
		var title;
		if (specTitle) {
			title = specTitle;
		} else {
			title = this.title;
		}
		var totalWidth = this.getColumnModel().getTotalWidth(includeHidden);
		return '<xml version="1.0" encoding="utf-8">' +
            '<ss:Workbook xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:o="urn:schemas-microsoft-com:office:office">' +
            '<o:DocumentProperties><o:Title>' + title + '</o:Title></o:DocumentProperties>' +
            '<ss:ExcelWorkbook>' +
            '<ss:WindowHeight>' + worksheet.height + '</ss:WindowHeight>' +
            '<ss:WindowWidth>' + worksheet.width + '</ss:WindowWidth>' +
            '<ss:ProtectStructure>False</ss:ProtectStructure>' +
            '<ss:ProtectWindows>False</ss:ProtectWindows>' +
            '</ss:ExcelWorkbook>' +
            '<ss:Styles>' +
	            //默认Style, 
	            '<ss:Style ss:ID="Default">' +
	            '<ss:Alignment ss:Vertical="Top" ss:WrapText="1" />' +
	            '<ss:Font ss:FontName="arial" ss:Size="10" />' +
	            '<ss:Borders>' +
	            '<ss:Border ss:Color="#e4e4e4" ss:Weight="1" ss:LineStyle="Continuous" ss:Position="Top" />' +
	            '<ss:Border ss:Color="#e4e4e4" ss:Weight="1" ss:LineStyle="Continuous" ss:Position="Bottom" />' +
	            '<ss:Border ss:Color="#e4e4e4" ss:Weight="1" ss:LineStyle="Continuous" ss:Position="Left" />' +
	            '<ss:Border ss:Color="#e4e4e4" ss:Weight="1" ss:LineStyle="Continuous" ss:Position="Right" />' +
	            '</ss:Borders>' +
	            '<ss:Interior />' +
	            '<ss:NumberFormat />' +
	            '<ss:Protection />' +
	            '</ss:Style>' +
	            
	            '<ss:Style ss:ID="title">' +
		            '<ss:Borders />' +
		            '<ss:Font />' +
		            '<ss:Alignment ss:WrapText="1" ss:Vertical="Center" ss:Horizontal="Center" />' +
		            '<ss:NumberFormat ss:Format="@" />' +
	            '</ss:Style>' +
	            
	            '<ss:Style ss:ID="headercell">' +
		            '<ss:Font ss:Bold="1" ss:Size="10" />' +
		            '<ss:Alignment ss:WrapText="1" ss:Horizontal="Center" />' +
	            //'<ss:Interior ss:Pattern="Solid" ss:Color="#A3C9F1" />' +
	            '</ss:Style>' +
	            
	            '<ss:Style ss:ID="even">' +		
	//            '<ss:Interior ss:Pattern="Solid" ss:Color="#CCFFFF" />' +
	            '</ss:Style>' +
	//            ss:Parent="even" 
	            '<ss:Style ss:ID="evendate">' +
	            	'<ss:NumberFormat ss:Format="yyyy-mm-dd" />' +
	            '</ss:Style>' +
	//            ss:Parent="even" 
	            '<ss:Style ss:ID="evenint">' +
		            '<ss:Alignment ss:Horizontal="Right" />' + 
		            '<ss:NumberFormat ss:Format="0" />' +
	            '</ss:Style>' +
	//            ss:Parent="even" 
	            '<ss:Style ss:ID="evenfloat">' +
		            '<ss:Alignment ss:Horizontal="Right" />' + 
		            '<ss:NumberFormat ss:Format="0.00" />' +
	            '</ss:Style>' +
	            
	            '<ss:Style ss:ID="int">' +
		            '<ss:Alignment ss:Horizontal="Right" />' + 
		            '<ss:NumberFormat ss:Format="0" />' +
	            '</ss:Style>' +
	            
	            '<ss:Style ss:ID="float">' +
		            '<ss:Alignment ss:Horizontal="Right" />' + 
		            '<ss:NumberFormat ss:Format="0.00" />' +
	            '</ss:Style>' +
	            
	            '<ss:Style ss:ID="odd">' +
	//            '<ss:Interior ss:Pattern="Solid" ss:Color="#CCCCFF"/>' +
	            '</ss:Style>' +
	//            ss:Parent="odd" 
	            '<ss:Style ss:ID="odddate">' +
	           		'<ss:NumberFormat ss:Format="yyyy-mm-dd" />' +
	            '</ss:Style>' +
	//            ss:Parent="odd" 
	            '<ss:Style ss:ID="oddint">' +
		            '<ss:Alignment ss:Horizontal="Right" />' + 
		            '<ss:NumberFormat ss:Format="0" />' +
	            '</ss:Style>' +
	//            ss:Parent="odd" 
	            '<ss:Style ss:ID="oddfloat">' +
		            '<ss:Alignment ss:Horizontal="Right" />' + 
		            '<ss:NumberFormat ss:Format="0.00" />' +
	            '</ss:Style>' +
	            
	           	'<ss:Style ss:ID="summary">' +		
	            '</ss:Style>' +
	            
	            '<ss:Style ss:ID="summarydate">' +
	            	'<ss:NumberFormat ss:Format="yyyy-mm-dd" />' +
	            '</ss:Style>' +
	            
	            '<ss:Style ss:ID="summaryint">' +
		            '<ss:Alignment ss:Horizontal="Right" />' + 
		            '<ss:NumberFormat ss:Format="0" />' +
	            '</ss:Style>' +
           
	            '<ss:Style ss:ID="summaryfloat">' +
		            '<ss:Alignment ss:Horizontal="Right" />' + 
		            '<ss:NumberFormat ss:Format="0.00" />' +
	            '</ss:Style>' +
	            
            	/*'<ss:Style ss:ID="Default">' +
	            '<ss:Alignment ss:Vertical="Top" ss:WrapText="1" />' +
	            '<ss:Font ss:FontName="arial" ss:Size="10" />' +
	            '<ss:Borders>' +
	            '<ss:Border ss:Color="#e4e4e4" ss:Weight="1" ss:LineStyle="Continuous" ss:Position="Top" />' +
	            '<ss:Border ss:Color="#e4e4e4" ss:Weight="1" ss:LineStyle="Continuous" ss:Position="Bottom" />' +
	            '<ss:Border ss:Color="#e4e4e4" ss:Weight="1" ss:LineStyle="Continuous" ss:Position="Left" />' +
	            '<ss:Border ss:Color="#e4e4e4" ss:Weight="1" ss:LineStyle="Continuous" ss:Position="Right" />' +
	            '</ss:Borders>' +
	            '<ss:Interior />' +
	            '<ss:NumberFormat />' +
	            '<ss:Protection />' +
	            '</ss:Style>' +
	            '<ss:Style ss:ID="title">' +
	            '<ss:Borders />' +
	            '<ss:Font />' +
	            '<ss:Alignment ss:WrapText="1" ss:Vertical="Center" ss:Horizontal="Center" />' +
	            '<ss:NumberFormat ss:Format="@" />' +
	            '</ss:Style>' +
	            '<ss:Style ss:ID="headercell">' +
	            '<ss:Font ss:Bold="1" ss:Size="10" />' +
	            '<ss:Alignment ss:WrapText="1" ss:Horizontal="Center" />' +
	            '<ss:Interior ss:Pattern="Solid" ss:Color="#A3C9F1" />' +
	            '</ss:Style>' +
	            '<ss:Style ss:ID="even">' +
	            '<ss:Interior ss:Pattern="Solid" ss:Color="#CCFFFF" />' +
	            '</ss:Style>' +
	            '<ss:Style ss:Parent="even" ss:ID="evendate">' +
	            '<ss:NumberFormat ss:Format="yyyy-mm-dd" />' +
	            '</ss:Style>' +
	            '<ss:Style ss:Parent="even" ss:ID="evenint">' +
	            '<ss:NumberFormat ss:Format="0" />' +
	            '</ss:Style>' +
	            '<ss:Style ss:Parent="even" ss:ID="evenfloat">' +
	            '<ss:NumberFormat ss:Format="0.00" />' +
	            '</ss:Style>' +
	            '<ss:Style ss:ID="odd">' +
	            '<ss:Interior ss:Pattern="Solid" ss:Color="#CCCCFF" />' +
	            '</ss:Style>' +
	            '<ss:Style ss:Parent="odd" ss:ID="odddate">' +
	            '<ss:NumberFormat ss:Format="yyyy-mm-dd" />' +
	            '</ss:Style>' +
	            '<ss:Style ss:Parent="odd" ss:ID="oddint">' +
	            '<ss:Alignment ss:Horizontal="Right" />' + 
	            '<ss:NumberFormat ss:Format="0" />' +
	            '</ss:Style>' +
	            '<ss:Style ss:Parent="odd" ss:ID="oddfloat">' +
	            '<ss:Alignment ss:Horizontal="Right" />' + 
	            '<ss:NumberFormat ss:Format="0.00" />' +
	            '</ss:Style>' +*/
            
            '</ss:Styles>' +
            worksheet.xml +
            '</ss:Workbook>';
    },

	createWorksheet: function(includeHidden, specTitle) {
		var title;
    	if (specTitle) {
			title = specTitle;
		} else {
			title = this.title;
		}
		
      // Calculate cell data types and extra class names which affect
		// formatting
        var cellType = [];
        var cellTypeClass = [];
        var cm = this.getColumnModel();
        var totalWidthInPixels = 0;
        var colXml = '';
        var headerXml = '';
        var visibleColumnCountReduction = 0;
        var colCount = cm.getColumnCount();
        for (var i = 0; i < colCount; i++) {
        	//cellType和cellTypeClass有可能小于colCount
            if ((cm.getDataIndex(i) != '') && (includeHidden || !cm.isHidden(i))) {
                var w = cm.getColumnWidth(i)		//宽度就是像素?
                totalWidthInPixels += w;
                if (cm.getColumnHeader(i) === ""){
                	cellType.push("None");
                	cellTypeClass.push("");
                	++visibleColumnCountReduction;
                }
                else
                {
                    colXml += '<ss:Column ss:AutoFitWidth="1" ss:Width="' + w + '" />';
                    headerXml += '<ss:Cell ss:StyleID="headercell">' +
                        '<ss:Data ss:Type="String">' + cm.getColumnHeader(i) + '</ss:Data>' +
                        '<ss:NamedCell ss:Name="Print_Titles" /></ss:Cell>';
                    //取的Store的Record的Field的Type, 而不是ColumnModel里Column定义的类型
                    var fld = this.store.recordType.prototype.fields.get(cm.getDataIndex(i));
                    //断点查看fld.type是Object, 再一次才是字符串类型的type值
                    switch(fld.type.type) {
                        case "int":
                            cellType.push("Number");
                            cellTypeClass.push("int");
                            break;
                        case "float":
                            cellType.push("Number");
                            cellTypeClass.push("float");
                            break;
                        case "bool":
                        case "boolean":
                            cellType.push("String");
                            cellTypeClass.push("");
                            break;
                        case "date":
                            cellType.push("DateTime");
                            cellTypeClass.push("date");
                            break;
                        default:
                            cellType.push("String");
                            cellTypeClass.push("");
                            break;
                    }
                }
            }
        }
        //这应该是invisibleColumnCount
        var visibleColumnCount = cellType.length - visibleColumnCountReduction;

        var result = {
            height: 9000,
            width: Math.floor(totalWidthInPixels * 30) + 50
        };
        
        // GroupingView?
      var groupField;
      var groupFieldValue = "";
      var groupIdArray = new Array();
      var groupId = "";
      var groupRowIndex = new Array();
      var groupCount = 0;
		if (this.getView().constructor == Ext.grid.GroupingView) {
			groupField = this.store.groupField;
			groupCount = this.store.getGroupCount();
		}

        // Generate worksheet header details.
        // 如果觉得用title命名Sheet不好, 就用Sheet1吧
        var t = '<ss:Worksheet ss:Name="' + title + '">' +
            '<ss:Names>' +
            '<ss:NamedRange ss:Name="Print_Titles" ss:RefersTo="=\'' + title + '\'!R1:R2" />' +
            '</ss:Names>' +
            '<ss:Table x:FullRows="1" x:FullColumns="1"' +
            ' ss:ExpandedColumnCount="' + (visibleColumnCount + 2) +
            //如果分组 这里还要加上分组的行数 不分组的话是store的行数+title和header
            '" ss:ExpandedRowCount="' + (this.store.getCount() + 2 + groupCount) + '">' +
            colXml +
            '<ss:Row ss:Height="38">' +
            '<ss:Cell ss:StyleID="title" ss:MergeAcross="' + (visibleColumnCount - 1) + '">' +
            '<ss:Data xmlns:html="http://www.w3.org/TR/REC-html40" ss:Type="String">' +
            '<html:B>' + title + '</html:B></ss:Data><ss:NamedCell ss:Name="Print_Titles" />' +
            '</ss:Cell>' +
            '</ss:Row>' +
            '<ss:Row ss:AutoFitHeight="1">' +
            headerXml +
            '</ss:Row>';

      // Generate the data rows from the data in the Store
		
      for (var i = 0, it = this.store.data.items, l = it.length; i < l; i++) {
			r = it[i].data;
			
			if (groupField) {
				groupFieldValue = r[groupField];
				var currentGroupId = this.getView().getGroupId(groupFieldValue);
				if(currentGroupId != groupId){
					groupIdArray.push(currentGroupId);
					groupRowIndex[currentGroupId] = i;	//记录前一组的起始行索引
					if(groupIdArray.length > 1){
						t += '<ss:Row>';
						//此时groupId是需要添加Summary信息的行
						for (var j = 0, k = 0; j < colCount; j++) {
							if ((cm.getDataIndex(j) != '') && (includeHidden || !cm.isHidden(j))) {
								var column = cm.getColumnById(cm.getColumnId(j));
								var v = "";
								if(column.summaryType == "sum"){
									v = this.getStore().sum(cm.getDataIndex(j), groupRowIndex[groupId], groupRowIndex[currentGroupId] - 1);
								}else if(column.summaryType == "avg"){
									v = this.getStore().sum(cm.getDataIndex(j), groupRowIndex[groupId], groupRowIndex[currentGroupId] - 1) / (groupRowIndex[currentGroupId] - groupRowIndex[groupId]);
								}
								if (cellType[k] !== "None") {
									t += '<ss:Cell ss:StyleID="' + "summary" + cellTypeClass[k] + '"><ss:Data ss:Type="' + cellType[k] + '">';
									if (cellType[k] == 'DateTime') {
										t += v.format('Y-m-d');
									} else {
										t += v;
									}
									t += '</ss:Data></ss:Cell>';
								}
								k++;
							}
						}
						t += '</ss:Row>';
					}
					groupId = currentGroupId;
				}
			}
			
			t += '<ss:Row>';
			// cellClass决定了数据行的样式
			var cellClass = (i & 1) ? 'odd' : 'even';
			
			for (var j = 0, k = 0; j < colCount; j++) {
				if ((cm.getDataIndex(j) != '') && (includeHidden || !cm.isHidden(j))) {
					var v = r[cm.getDataIndex(j)];
					if (cellType[k] !== "None") {
						// 背景色在定义StyleID里修改掉就是了
						t += '<ss:Cell ss:StyleID="' + cellClass + cellTypeClass[k] + '"><ss:Data ss:Type="' + cellType[k] + '">';
						if (cellType[k] == 'DateTime') {
							t += v.format('Y-m-d');
						} else {
							t += v;
						}
						t += '</ss:Data></ss:Cell>';
					}
					k++;
				}
			}
			t += '</ss:Row>';
			
			groupFieldValue = r[groupField];
			groupId = this.getView().getGroupId(groupFieldValue);
		}
		
		if(groupField){
			t += '<ss:Row>';
			for (var j = 0, k = 0; j < colCount; j++) {
				if ((cm.getDataIndex(j) != '') && (includeHidden || !cm.isHidden(j))) {
					var column = cm.getColumnById(cm.getColumnId(j));
					var v = "";
					if(column.summaryType == "sum"){
						v = this.getStore().sum(cm.getDataIndex(j), 0, this.getStore().getCount() - 1);
					}else if(column.summaryType == "avg"){
						v = this.getStore().sum(cm.getDataIndex(j), 0, this.getStore().getCount() - 1) / (this.getStore().getCount());
					}
					if (cellType[k] !== "None") {
						t += '<ss:Cell ss:StyleID="' + "summary" + cellTypeClass[k] + '"><ss:Data ss:Type="' + cellType[k] + '">';
						if (cellType[k] == 'DateTime') {
							t += v.format('Y-m-d');
						} else {
							t += v;
						}
						t += '</ss:Data></ss:Cell>';
					}
					k++;
				}
			}
			t += '</ss:Row>';
		}

        result.xml = t + '</ss:Table>' +
            '<x:WorksheetOptions>' +
            '<x:PageSetup>' +
            '<x:Layout x:CenterHorizontal="1" x:Orientation="Landscape" />' +
            '<x:Footer x:Data="Page &P of &N" x:Margin="0.5" />' +
            '<x:PageMargins x:Top="0.5" x:Right="0.5" x:Left="0.5" x:Bottom="0.8" />' +
            '</x:PageSetup>' +
            '<x:FitToPage />' +
            '<x:Print>' +
            '<x:PrintErrors>Blank</x:PrintErrors>' +
            '<x:FitWidth>1</x:FitWidth>' +
            '<x:FitHeight>32767</x:FitHeight>' +
            '<x:ValidPrinterInfo />' +
            '<x:VerticalResolution>600</x:VerticalResolution>' +
            '</x:Print>' +
            '<x:Selected />' +
            '<x:DoNotDisplayGridlines />' +
            '<x:ProtectObjects>False</x:ProtectObjects>' +
            '<x:ProtectScenarios>False</x:ProtectScenarios>' +
            '</x:WorksheetOptions>' +
            '</ss:Worksheet>';
        return result;
    }
});


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值