MVC项目中怎样用JS导出EasyUI DataGrid为Excel
在做一中考评项目的时候,遇到了这么一个需求。就是把评教后得到的老师的成绩导出为Excel。事实上需求非常普通。实现起来有些复杂。由于老师考评不但有固定的考核项,还有额外加分项。于是我们就抽出来了一个表。专门存放评教后的成绩,导出的时候就直接读数据库然后导出Excel。后来需求变了,要求额外加分项动态加入。也就是说一个老师有几个额外加分项是管理员导入的,这样成绩表就不能用了,由于额外加分项是不固定的。
所以才有了以下这样的解决的方法,那就是直接从界面读数据。导出到Excel。
实现原理
从界面DataGrid导出Excel的原理是,将DataGrid的数据导入到XML文件里,然后将XML文件传回Controller,在Controller中转化为Excel文件,然后下载下来。
实现效果
页面DataGrid
导出的Excel
代码实现
1、首先加入一个DataGrid的扩展方法。能够将代码放到单独的js文件里
$.extend($.fn.datagrid.methods, { getExcelXml: function (jq, param) { var worksheet = this.createWorksheet(jq, param); //alert($(jq).datagrid(\'getColumnFields\')); var totalWidth = 0; var cfs = $(jq).datagrid(\'getColumnFields\'); for (var i = 1; i < cfs.length; i++) { totalWidth += $(jq).datagrid(\'getColumnOption\', cfs[i]).width; } //var totalWidth = this.getColumnModel().getTotalWidth(includeHidden); return \'<?xml version="1.0" encoding="utf-8"?>\' +//xml申明有问题,以修正。注意是utf-8编码。假设是gb2312。须要修修改态页文件的写入编码 \'<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>\' + param.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>\' + \'<ss:Style ss:ID="Default">\' + \'<ss:Alignment ss:Vertical="Top" />\' + \'<ss:Font ss:FontName="arial" ss:Size="10" />\' + \'<ss:Borders>\' + \'<ss:Border ss:Weight="1" ss:LineStyle="Continuous" ss:Position="Top" />\' + \'<ss:Border ss:Weight="1" ss:LineStyle="Continuous" ss:Position="Bottom" />\' + \'<ss:Border ss:Weight="1" ss:LineStyle="Continuous" ss:Position="Left" />\' + \'<ss:Border 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: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:Horizontal="Center" />\' + \'<ss:Interior ss:Pattern="Solid" />\' + \'</ss:Style>\' + \'<ss:Style ss:ID="even">\' + \'<ss:Interior ss:Pattern="Solid" />\' + \'</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: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:NumberFormat ss:Format="0" />\' + \'</ss:Style>\' + \'<ss:Style ss:Parent="odd" ss:ID="oddfloat">\' + \'<ss:NumberFormat ss:Format="0.00" />\' + \'</ss:Style>\' + \'</ss:Styles>\' + worksheet.xml + \'</ss:Workbook>\'; }, createWorksheet: function (jq, param) { // 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 cfs = $(jq).datagrid(\'getColumnFields\'); var colCount = cfs.length; for (var i = 1; i < colCount; i++) { if (cfs[i] != \'\') { var w = $(jq).datagrid(\'getColumnOption\', cfs[i]).width; totalWidthInPixels += w; if (cfs[i] === "") { cellType.push("None"); cellTypeClass.push(""); ++visibleColumnCountReduction; } else { colXml += \'<ss:Column ss:AutoFitWidth="1" ss:Width="130" />\'; headerXml += \'<ss:Cell ss:StyleID="headercell">\' + \'<ss:Data ss:Type="String">\' + $(jq).datagrid(\'getColumnOption\', cfs[i]).title + \'</ss:Data>\' + \'<ss:NamedCell ss:Name="Print_Titles" /></ss:Cell>\'; cellType.push("String"); cellTypeClass.push(""); } } } var visibleColumnCount = cellType.length - visibleColumnCountReduction; var result = { height: 9000, width: Math.floor(totalWidthInPixels * 30) + 50 }; var rows = $(jq).datagrid(\'getRows\'); // Generate worksheet header details. var t = \'<ss:Worksheet ss:Name="\' + param.title + \'">\' + \'<ss:Names>\' + \'<ss:NamedRange ss:Name="Print_Titles" ss:RefersTo="=\\'\' + param.title + \'\\'!R1:R2" />\' + \'</ss:Names>\' + \'<ss:Table x:FullRows="1" x:FullColumns="1"\' + \' ss:ExpandedColumnCount="\' + (visibleColumnCount + 2) + \'" ss:ExpandedRowCount="\' + (rows.length + 2) + \'">\' + colXml + \'<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++) { for (var i = 0, it = rows, l = it.length; i < l; i++) { t += \'<ss:Row>\'; var cellClass = (i & 1) ?\'odd\' : \'even\'; r = it[i]; var k = 0; for (var j = 1; j < colCount; j++) { //if ((cm.getDataIndex(j) != \'\') if (cfs[j] != \'\') { //var v = r[cm.getDataIndex(j)]; var v = r[cfs[j]]; if (cellType[k] !== "None") { 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>\'; } 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; } });
2、然后加入导出Excelbutton的Js方法,能够加入在页面上,也能够放在js引用文件里
function OutputExcel() { //getExcelXML有一个JSON对象的配置,配置项看了下仅仅有title配置,为excel文档的标题 var data = $(\'#tt\').datagrid(\'getExcelXml\', { title: \'教职工成绩\' }); //获取datagrid数据相应的excel须要的xml格式的内容 //用ajax发动到动态页动态写入xls文件里 //var url = ; //假设为asp注意改动后缀 $.ajax({ url: "/QueryScores/ExportExcelNew", //自己的Controller的名字,以及Controller中进行转化的方法,也就是提交的URL data: { data: data }, type: \'POST\', dataType: \'text\', success: function (fn) { $("#Dlg-Export_xiazai").html("<a class=\'easyui-linkbutton\' iconCls=\'icon-ok\' href=\'" + fn + "\' target=\'_blank\' style=\'border:1px solid black;padding:0px 0px 0px 0px \' >点击下载导出的教职工成绩</a>"); }, error: function (xhr) { $("#Dlg-Export_xiazai").html(\'动态页有问题\nstatus:\' + xhr.status + \'\nresponseText:\' + xhr.responseText) } }); }
3、接下来是Controller中的方法
[ValidateInput(false)] //<span style="color:#ff0000;">这里代码是针对一个错误加入的,以下会介绍</span> public ActionResult ExportExcelNew(string data) { //获取前台post提交的数据 //定义生成文件的文件夹,获取绝对地址 string pathToFiles = Server.MapPath("/ExcelFile/UploadFile"); //定义生成文件的名称 string fn = DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".xls"; //组合成文件的路径 string path = @"" + pathToFiles + "\\" + fn; //推断是否已经存在文件 if (!System.IO.File.Exists(path)) { //新建文件,并写入数据 System.IO.File.WriteAllText(path, data, Encoding.UTF8); } else { //文件已存在,加入写入数据 System.IO.File.AppendAllText(path, data, Encoding.UTF8);//假设是gb2312的xml申明。第三个编码參数改动为Encoding.GetEncoding(936) } return Content("/ExcelFile/UploadFile/" + fn);//返回文件名称提供下载 }
4、以下看HTML部分的代码
<div style="width: 180px" id="export"> <input type="button" onclick="OutputExcel()" value="一键导出Excel" /> <div id="Dlg-Export_xiazai" style="float:left"></div> </div> <div> <table id="tt"></table> </div>
这是页面上的HTML代码,包含DataGrid表格的部分,也包含一键导出Excel按钮的部分。
出现的问题
1、从client(data=”<?xml version=”1.0″…”)中检測到有潜在危急的Request.Form值。
这个问题的解决办法是默认情况下,ValidateInput属性是true,也就是说会做输入验证,这样的错误会报在从View端向Controller传数据时。能够把这个错误放在百度上。百度一下,有非常多相关的搜索。解决方式是:在Web.Config中的<system.web>节点中配置<httpRuntime requestValidationMode=”2.0″ />,而且在方法前加入标签[ValidateInput(false)],如上面代码。
2、未能找到路径“E:\…”的一部分
这个问题有两个原因。一是这个路径下没有这个文件,二是路径错误。我这里犯的错误是路径错误。由于我的目录是UploadFile,可是代码中却错写成了UploadFiles。多了一个s。
总结
方法总比问题多,遇到问题之后,一定要相信自己能解决。这样才干鼓舞自己去找思路找方法。对于解决一个问题,有不同的思路,就有不同的解决方式,一条路走不通。那就换个思路找还有一条。仅仅要不抛弃不放弃。肯定可以解决掉。