基于layui,js-xlsx的前台数据复杂表头导出到excel文件中
前言 :
前台表格数据导出到excel 依赖于 js-xlsx 的excel.js(Version: v1.4) , 本文实现了复杂多级表头的生成,包含
动态的实现'表格合并',以及 基础表格样式 (背景色、边框)。 如有不足之处还望指正,多谢!
layui table 加载
(版本2.2.45以前具体版本忘了)
function initDataGrid(){
dataList = [
{type : 'numbers',width : 30,rowspan:2 /* 行号 */
},
{title:'增加时间', field:'createTimeStr',type:'string',minWidth:83,rowspan:2,align:'center',fixed:'left'},
{title:'名称', field:'name',type:'string',minWidth:80,rowspan:2,align:'center',fixed:'left',render:function(row){
return row.name;
}},
{title:'项目名称', field:'projectName',type:'string',minWidth:130,rowspan:2,align:'center',fixed:'left',render:function(row){
return row.projectName;
}
},
{title:'编号', field:'code',type:'string',minWidth:130,rowspan:2,align:'center',fixed:'left'},
{title:'课题名称', field:'techName',type:'string',minWidth:140,rowspan:2,align:'center',fixed:'left'},
{title:'课题类别', field:'techType',type:'string',minWidth:80,rowspan:2,align:'center'},
{title:'时间',align:'center',colspan:2},
{title:'预算',align:'center',minWidth:160,colspan:maxYear - minYear+2}
];
if(organizationWrite){ // 指定位置插入列
dataList.splice(3,0,{title:'项目部名称', field:'organizationName',type:'string',minWidth:130,rowspan:2,align:'center',fixed:'left'})
};
var eachYear = [];
for (var m = 0; m <= maxYear - minYear; m++) { // 动态行数据添加
var yeartitle = minYear + m;
var yearAmount = m+1;
eachYear.push({
title : yeartitle + '年',
field : 'budgetAmount',
type : 'string',
minWidth : 60,
align : 'center',
render: function(row) {
var ret = "";
if(!changeRow){
changeRow = row.id;
}else if(changeRow != row.id){
// 一条数据遍历完, 对数据进行归零处理
changeRow = row.id;
fork = 0;
fort = 0
}
var rowBudgets = row.budgets;
if(!!rowBudgets){
let f = fort;
if(fort == yearAmount){
f = 0;fort=0;
}
for (; f < yearAmount; f++) { // fort 列下标
for (let fork = 0; fork < rowBudgets.length; fork++) { // fork 数据下标
if (rowBudgets[fork].budgetTime == minYear + fort ) {
var returnStr = rowBudgets[fork].budgetAmount;
ret = returnStr;
break;
}
}
}
}
fort ++;
if(row.totalRow) return "<span style='color:red'>" +ret+"</span>"; // 合计行标红
return ret;
}
})
}
dataList.push.apply(dataList,[
{title:'名单', field:'techList',type:'string',minWidth:180,rowspan:2,align:'center'},
{title:'证据',align:'center',colspan:6},
{title:'已核算费用',colspan:2,align:'center'},
{title:'核算费用比例(%)', field:'costRatio',type:'string',width:153,rowspan:2,align:'center'},
{title:'状态', field:'techStatus',hide:true,width:70,rowspan:2,align:'center'},
{title:'审核状态', field:'auditStatus',type:'string',minWidth:110,rowspan:2,align:'center'},
{title: '操作', fixed:'right',field:'hideExcel', minWidth: 190 ,align:"center", allowHide: false,render: function (row){
return " 操作 删除、编辑、修改 等";
}}
]);
dataListSec = [
{title:'起始日期', field:'startTime',type:'string',minWidth:80,align:'center'},
{title:'完成日期', field:'endTime',type:'string',minWidth:80,align:'center'},
{title:'合计', field:'totalAmount',type:'string',minWidth:80,align:'center',render:function(row){
return "计算出来的合计数";
}
}
]
dataListSec.push.apply(dataListSec,eachYear);
dataListSec.push.apply(dataListSec,[
{title:'数量'1, field:'zlCount',type:'int',width:70,align:'center'},
{title:'数量2', field:'gfCount',type:'int',width:70,align:'center'},
{title:'数量3', field:'cxCount',type:'int',width:70,align:'center'},
{title:'数量4', field:'jdCount',type:'int',width:70,align:'center'},
{title:'数量5', field:'lwCount',type:'int',width:70,align:'center'},
{title:'其他', field:'qtCount',type:'int',width:50,align:'center'},
{title:'核算日期(年月)', field:'accountionDate',type:'string',width:128,align:'center'},
{title:'核算研发费用', field:'accountionExp',type:'string',width:100,align:'center'}]);
var url = "www.baidu.com/后台请求数据url";
grid1 = laytable.render({
id:'maingrid1',
elem:'#maingrid1',
url: url,
delayLoad : true, //true:在初始化表格时不加载数据
cols: [/*sort:false,fixed:false|'right',rowspan:1,colspan:1,render:function(row, rowindex, rows) */
dataList,dataListSec
],
page : true,
limit: 30,
heightDiff : 0,
even: false, //关闭隔行背景,
trackSelectedRow:true,//选中行高亮显示 .layui-table-row-active
onLoadData : function(data){//当从后台加载到数据的时候触发
},
onRowClick : function(index, row){//当点击一行的时候触发
//console.log(index+':'+JSON.stringify(row));
},
onRowDblClick : function(index, row){//当双击一行的时候触发
//console.log(index+':'+JSON.stringify(row));
},
done : function(res, curr, count) { // 数据加载完成后 执行
}
});
}
layui 表头样式
## 2. 复杂表头数据组装
// 复杂表头表格导出
function exportFile(id) {
// 根据传入tableID获取table内容
var bodys = $("div[class='layui-form layui-border-box layui-table-view'] .layui-table-box table").get(1); // 获取数据
var btrs = Array.from(bodys.querySelectorAll("tr"))
var btdslength = Array.from(btrs[0].querySelectorAll("td")).length;
// 根据传入tableID获取表头
var headers = $("div[class='layui-form layui-border-box layui-table-view'] .layui-table-box table").get(0); // 获取表头
var htrs = Array.from(headers.querySelectorAll('tr'));
var bodysArr = new Array();
var point =new Array(); // 行,列
for(var pi =0; pi<=htrs.length; pi++){
point[pi] = new Array();
}
point[0][0] ="qd"; // 起点
var mergeArr = [];
for (var j = 0; j < htrs.length; j++) { // 遍历tr
var titles = [];
var hths = Array.from(htrs[j].querySelectorAll("th"));
var titleAll = {};
var pointIndex = 0;
var pindx = 0; // 起点遍历位置
for (var i = 0; i < hths.length; i++) { // 遍历 th
// if(hths[i].textContent !="课题资料" && hths[i].textContent !="操作" ){ // 排除 不需要导出的列,数据td 需要同步 排除
var clazz = hths[i].getAttributeNode('class');
var colspan = hths[i].getAttributeNode('colspan'); // 表头占用列数
var rowspan = hths[i].getAttributeNode('rowspan'); //,表头占用行数
if(!colspan){
colspan = 1;
}else{
colspan = parseInt(colspan.value);
}
if(!rowspan){
rowspan = 1;
}else{
rowspan = parseInt(rowspan.value);
}
// 判断数据起始填写位置
for(;pindx < btdslength; pindx ++){
if(j == 0 || point[j][pindx] == "qd"){
titles.push(hths[i].innerText);
for(var temp = 0; temp < colspan-1;temp++){
titles.push(null);
}
mergeArr.push({s:{r:j,c:pindx},e:{r:j+rowspan-1,c:pindx+colspan-1}}); // 添加合并数据参数 r的差R表示向下扩展R个单元格,c 的差C表示想右扩展C个单元格
for(var qdi = 0; qdi<colspan ;qdi ++){
point[j+rowspan][pindx+qdi] = "qd"; // 添加完数据 ,添加起点记录
}
pindx = pindx+colspan;
break;
}else{
titles.push(""); // 不能为null, 为null 会影响表格样式的设置
}
}
// }
}
// bodysArr.unshift(titles);// 将标题行置顶添加到数组
bodysArr.push(titles);
}
var widthArr = [];
for (var j = 0; j < btrs.length; j++) {
var contents = [];
var btds = Array.from(btrs[j].querySelectorAll("td"));
for (var i = 0; i < btds.length; i++) {
// if(btds[i].dataset.field !="attachment" && btds[i].dataset.field !="hideExcel" ){ // 排除 不需要导出的列,titles 需要同步 排除 ,也可通过控制 cols属性来隐藏、显示
contents.push(btds[i].innerText);
if(j == 0){ //只跑一圈
widthArr.push({wpx:btds[i].scrollWidth});
}
// }
}
bodysArr.push(contents)
}
var styleCell = {
font: { // 定义样式 居中,背景色,字体大小,边框
name:'宋体',
sz: 14,
bold: true,
family:1
},
fill:{
bgColor: {
rgb: 'FFfd9a80' // 没有效果,不知道哪里除了问题
}
,
fgColor: { // 背景颜色
rgb:'94fba7'
}
},
alignment: {
vertical: 'center'
,horizontal: 'center'
// ,wrapText: true
}
,border:{
top: {
style: 'thin'
},
bottom: {
style: 'thin'
},
left: {
style: 'thin'
},
right: {
style: 'thin'
}
}
};
var sheet = XLSX.utils.aoa_to_sheet(bodysArr);
for(var item in sheet){ // 遍历sheet 对象,给需要的对象 添加样式
let aa = sheet[item];
let rindex = item.replace(/[^0-9]/ig,""); // excel 行下标
// let cindex = item.replace(/[^0-9]/ig,""); // excel 列下标
if(rindex <= htrs.length){ // 判断是不是 表头
aa.s = styleCell; // 样式设置
}else{ // 数据内容 样式
}
}
sheet['!merges'] = mergeArr;
sheet['!rows'] = [{hpx:30},{hpx:28}]; // 设置行高
sheet['!cols'] = widthArr;
openDownloadDialog(sheet2blob(sheet), '高新课题历史数据导出'+new Date().toLocaleString()+'.xlsx');
}
4. 封装之后的sheet
!cols
!rows
! merges
5. 样式文档
参数 | 参数子参数 | 值 |
---|---|---|
fill | patternType | “solid” or “none”” |
fgColor | COLOR_SPEC --背景颜色 | |
bgColor | COLOR_SPEC ? | |
font | name | “Calibri” // default |
sz | “11” // font size in points | |
color | COLOR_SPEC | |
bold | true or false | |
underline | true or false | |
italic | true or false | |
strike | true or false | |
outline | true or false | |
shadow | true or false | |
vertAlign | true or false | |
numFmt | “0” // integer index to built in formats, see StyleBuilder.SSF property | |
“0.00%” // string matching a built-in format, see StyleBuilder.SSF | ||
“0.0%” // string specifying a custom format | ||
“0.00%;(0.00%);-;@” // string specifying a custom format, escaping special characters | ||
“m/dd/yy” // string a date format using Excel’s format notation | ||
alignment | vertical | “bottom” or “center” or “top” |
horizontal | “bottom” or “center” or “top” | |
wrapText | true or false | |
readingOrder | 2 // for right-to-left | |
textRotation | Number from 0 to 180 or 255 (default is 0) | |
90 is rotated up 90 degrees | ||
45 is rotated up 45 degrees | ||
135 is rotated down 45 degrees | ||
180 is rotated down 180 degrees | ||
255 is special, aligned vertically | ||
border | top | { 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 | true or false | |
diagonalDown | true or false |
COLOR_SPEC: 填充、字体和边框的颜色对象:
{ auto: 1}指定自动值
{ rgb: “FF4dAA00” } 指定16进制的ARGB // rgb:“4dAA00” ,会自动添加FF变成 FF4dAA00
{ theme: “1”, tint: “-0.25”} 指定主题颜色和色调的整数索引(默认值为0)
{ indexed: 64} 默认值 fill.bgColor
BORDER_STYLE: 边框样式是一个字符串值,它可以是以下值之一:
thin
medium
thick
dotted
hair
dashed
mediumDashed
dashDot
mediumDashDot
dashDotDot
mediumDashDotDot
slantDashDot
3.参考文献
https://www.cnblogs.com/liuxianan/p/js-excel.html