接近过年,被一大堆excel报表烦死的我,遇到要求前端导出excel的后端,差点猝死的我拼命学习中,整理出这篇文章,希望看到这篇文章的你有所收获,也希望能收到大佬们的指点
之前用c#,.net弄过导出word,excel,可以点击查看.NET使用Aspose控件生成Word(可构建自定义表格)、Excel这篇文章
话不多说,进入正题,前端导出excel,使用插件 file-saver、xlsx、xlsx-style,xlsx-style 主要用来优化excel的样式
准备
安装插件
npm install file-saver xlsx xlsx-style --save
引入xlsx-style,发现导出样式依然没反应。需要在\node_modules\xlsx-style\dist\cpexcel.js 807行 的
var cpt = require(’./cpt’ + ‘able’);
改成
var cpt = cptable;
但是如果每次打包之前去修改包里的代码很麻烦,如果是多人合作,别人打包的适合也可能不知道直接打包了。所以我去node_modules包里直接修改了
xlsx.full.min.js
,然后吧文件复制到静态文件夹中,在index.html页面引入即可
这一步很重要,笔者就是在这个问题上耗费了整整一下午,非常惨痛的代价
创建导出execl工具文件exportExecl.js
1.table数据导出execl文件
/**
* table数据导出execl文件
* @param {*} id:table元素id
* @param {*} fileName :导出文件名称
*/functionexportExecl(id, fileName,HEADERRANGE,TABLERANGE,REDRANGE=false){let excelName = fileName||'导出表格.xlsx'var xlsxParam ={raw:false}// 导出数据是否未加工let tables=document.getElementById(id)if(!tables)return
tables = document.getElementById(id).cloneNode(true)// 移除固定列,防止重复生成表格if(tables.querySelector('.el-table__fixed')!==null){
tables.removeChild(tables.querySelector('.el-table__fixed'))}let table_book = utils.table_to_book(tables, xlsxParam)
table_book.Sheets.Sheet1 =setTitleClass(table_book.Sheets.Sheet1)
table_book.Sheets.Sheet1 =setCellStyle(table_book.Sheets.Sheet1,HEADERRANGE, headerStyle)
table_book.Sheets.Sheet1 =setCellStyle(table_book.Sheets.Sheet1,TABLERANGE, contentStyle)if(REDRANGE){
table_book.Sheets.Sheet1 =setCellStyle(table_book.Sheets.Sheet1,REDRANGE, redFont)}
table_book.Sheets.Sheet1 =addRangeBorder(table_book.Sheets.Sheet1['!merges'], table_book.Sheets.Sheet1)// 合并项添加边框var table_write =XLSX.write(table_book,{bookSST:false,type:'binary',bookType:'xlsx'})try{
FileSaver.saveAs(newBlob([s2ab(table_write)],{type:'application/octet-stream'}),
excelName
)}catch(e){
console.log(e, table_write)}return table_write
}functions2ab(s){var buf =newArrayBuffer(s.length)var view =newUint8Array(buf)for(var i =0; i != s.length;++i) view[i]= s.charCodeAt(i)&0xFF;return buf
}
2.为合并项添加边框
// 为合并项添加边框functionaddRangeBorder(range, ws){let arr =['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']if(range){
range.forEach((item)=>{let startColNumber =Number(item.s.r),
endColNumber =Number(item.e.r);let startRowNumber =Number(item.s.c),
endRowNumber =Number(item.e.c);const test = ws[arr[startRowNumber]+(startColNumber +1)];for(let col = startColNumber; col <= endColNumber; col++){for(let row = startRowNumber; row <= endRowNumber; row++){
ws[arr[row]+(col +1)]= test;}}});}return ws;}
3.根据需求,我将样式设置提出来作为单独的方法. 可以根据自己的需求加工
const commonStyle ={font:{name:'宋体',sz:11,bold:false,color:{rgb:'000000'}},alignment:{horizontal:'center',vertical:'center',wrapText:false}}const titleStyle ={font:{name:'黑体',sz:18,bold:false,color:{rgb:'000000'}},alignment:{horizontal:'center',vertical:'center',wrapText:false}}const headerStyle ={font:{name:'黑体',sz:11,bold:false,color:{rgb:'000000'}},alignment:{horizontal:'center',vertical:'center',wrapText:false},border:{top:{style:'thin'},bottom:{style:'thin'},left:{style:'thin'},right:{style:'thin'}}// fill: {// fgColor: { rgb: 'ebebeb' }// 设置标题单元格的背景颜色// }}const redFont ={font:{name:'宋体',sz:11,bold:false,color:{rgb:'FF0000'// 十六进制,不带#}},alignment:{horizontal:'left',vertical:'center',wrapText:false}}const contentStyle ={font:{name:'宋体',sz:11,bold:false},alignment:{horizontal:'center',vertical:'center',wrapText:false},border:{top:{style:'thin'},bottom:{style:'thin'},left:{style:'thin'},right:{style:'thin'}}}// 设置 标题及公共样式functionsetTitleClass(worksheet){// titlefor(const key in worksheet){if(key.indexOf('!')===-1&& worksheet[key].v){if(key =='A1'){// title
worksheet[key].s = titleStyle
}else{
worksheet[key].s = commonStyle
}}}return worksheet
}// 根据范围和style设置单元格样式functionsetCellStyle(worksheet, range,RANGESTYLE){const headerRange = utils.decode_range(range)for(let col = headerRange.s.c; col <= headerRange.e.c; col++){for(let row = headerRange.s.r; row <= headerRange.e.r; row++){const headerCell = utils.encode_cell({r: row,c: col })if( worksheet[headerCell]!=undefined){
worksheet[headerCell].s =RANGESTYLE}}}return worksheet
}
在 xlsx-style 中,单元格样式共有五个顶级的属性,分别是:
fill
,
font
,
numFmt
,
alignment
和
border
。
顶级属性子属性描述类型或可选值默认值fillpatternType填充模式“solid” or “none”-fgColor前景色COLOR_SPEC-bgColor背景色COLOR_SPEC{ indexed: 64}fontname字体名称string“Calibri”sz字体大小number12color字体颜色COLOR_SPEC-bold加粗boolean-underline下划线boolean-italic斜体boolean-strike瞄边boolean-outline轮廓boolean-shadow阴影boolean-vertAlign垂直对齐boolean-numFmt-数字格式化“0” // 内置格式的整数索引 “0.00%” // 匹配内置格式的字符串,见下文 “0.0%” // 格式化为自定义格式的字符串 “0.00%;(0.00%);-;@” // 格式化的时候转义特殊字符 “m/dd/yy” // 格式化为日期-alignmentvertical垂直对齐“bottom”、“center”、“top”-horizontal水平对齐“left”、“center”、“right”-wrapText换行boolean-readingOrder文字方向1、2 // for right-to-left-textRotation旋转Number from 0 to 180 or 2550bordertop上边框{ style: BORDER_STYLE, color: COLOR_SPEC }-bottom下边框{ style: BORDER_STYLE, color: COLOR_SPEC }-left左边框{ style: BORDER_STYLE, color: COLOR_SPEC }-right右边框{ style: BORDER_STYLE, color: COLOR_SPEC }-diagonal对角线{ style: BORDER_STYLE, color: COLOR_SPEC }-diagonalUp上对角线boolean-diagonalDown下对角线boolean-
exportExecl.js
文件全代码
import FileSaver from'file-saver'import{ utils }from'xlsx'const commonStyle ={font:{name:'宋体',sz:11,bold:false,color:{rgb:'000000'}},alignment:{horizontal:'center',vertical:'center',wrapText:false}}const titleStyle ={font:{name:'黑体',sz:18,bold:false,color:{rgb:'000000'}},alignment:{horizontal:'center',vertical:'center',wrapText:false}}const headerStyle ={font:{name:'黑体',sz:11,bold:false,color:{rgb:'000000'}},alignment:{horizontal:'center',vertical:'center',wrapText:false},border:{top:{style:'thin'},bottom:{style:'thin'},left:{style:'thin'},right:{style:'thin'}}// fill: {// fgColor: { rgb: 'ebebeb' }// 设置标题单元格的背景颜色// }}const contentStyle ={font:{name:'宋体',sz:11,bold:false},alignment:{horizontal:'center',vertical:'center',wrapText:false},border:{top:{style:'thin'},bottom:{style:'thin'},left:{style:'thin'},right:{style:'thin'}}}const redFont ={font:{name:'宋体',sz:11,bold:false,color:{rgb:'FF0000'// 十六进制,不带#}},alignment:{horizontal:'left',vertical:'center',wrapText:false}}/**
* table数据导出execl文件
* @param {*} id:table元素id
* @param {*} fileName :导出文件名称
*/functionexportExecl(id, fileName,HEADERRANGE,TABLERANGE,REDRANGE=false){let excelName = fileName||'导出表格.xlsx'var xlsxParam ={raw:false}// 导出数据是否未加工let tables=document.getElementById(id)if(!tables)return
tables = document.getElementById(id).cloneNode(true)// 移除固定列,防止重复生成表格if(tables.querySelector('.el-table__fixed')!==null){
tables.removeChild(tables.querySelector('.el-table__fixed'))}let table_book = utils.table_to_book(tables, xlsxParam)
table_book.Sheets.Sheet1 =setTitleClass(table_book.Sheets.Sheet1)
table_book.Sheets.Sheet1 =setCellStyle(table_book.Sheets.Sheet1,HEADERRANGE, headerStyle)
table_book.Sheets.Sheet1 =setCellStyle(table_book.Sheets.Sheet1,TABLERANGE, contentStyle)if(REDRANGE){
table_book.Sheets.Sheet1 =setCellStyle(table_book.Sheets.Sheet1,REDRANGE, redFont)}
table_book.Sheets.Sheet1 =addRangeBorder(table_book.Sheets.Sheet1['!merges'], table_book.Sheets.Sheet1)// 合并项添加边框var table_write =XLSX.write(table_book,{bookSST:false,type:'binary',bookType:'xlsx'})try{
FileSaver.saveAs(newBlob([s2ab(table_write)],{type:'application/octet-stream'}),
excelName
)}catch(e){
console.log(e, table_write)}return table_write
}functionsetCellStyle(worksheet, range,RANGESTYLE){const headerRange = utils.decode_range(range)for(let col = headerRange.s.c; col <= headerRange.e.c; col++){for(let row = headerRange.s.r; row <= headerRange.e.r; row++){const headerCell = utils.encode_cell({r: row,c: col })if( worksheet[headerCell]!=undefined){
worksheet[headerCell].s =RANGESTYLE}}}return worksheet
}functionsetTitleClass(worksheet){// titlefor(const key in worksheet){if(key.indexOf('!')===-1&& worksheet[key].v){if(key =='A1'){// title
worksheet[key].s = titleStyle
}else{
worksheet[key].s = commonStyle
}}}return worksheet
}// 为合并项添加边框functionaddRangeBorder(range, ws){let arr =['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']if(range){
range.forEach((item)=>{let startColNumber =Number(item.s.r),
endColNumber =Number(item.e.r);let startRowNumber =Number(item.s.c),
endRowNumber =Number(item.e.c);const test = ws[arr[startRowNumber]+(startColNumber +1)];for(let col = startColNumber; col <= endColNumber; col++){for(let row = startRowNumber; row <= endRowNumber; row++){
ws[arr[row]+(col +1)]= test;}}});}return ws;}functions2ab(s){var buf =newArrayBuffer(s.length)var view =newUint8Array(buf)for(var i =0; i != s.length;++i) view[i]= s.charCodeAt(i)&0xFF;return buf
}exportdefault exportExecl
vue文件引入
由于页面展示的excel五花八门,导出的excel又需要同客户给的excel一致,这里就用了原生的表格。element的表格也是支持导出的
页面展示
导出结果
目前导出还未实现自定义行高列宽,若有大佬有方案,欢迎留言指点,感谢
版权归原作者 依米__ 所有, 如有侵权,请联系我们删除。