库:Excel.js(版本4.3.0) 和 FileSaver(版本2.0.5)
CDN地址:
<script src="https://cdn.bootcdn.net/ajax/libs/exceljs/4.3.0/exceljs.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/FileSaver.js/2.0.5/FileSaver.js"></script>
Excel.js 中文文档:https://gitee.com/alan_scut/exceljs
先看一下效果吧
代码:
使用方法: exportExcal()
// 住宿安排(动态列)
var stay_arr = ['27日晚', '28日晚'];
// 内容数据
var test_data = [{"guest": "生态伙伴","name": "姓名一","sex": "男","company": "AAAAAA股份有限公司","job": "业务部部长","phone": "12345678912","stay": ["28日晚"]},{"guest": "生态伙伴","name": "姓名二","sex": "女","company": "AAAAAA股份有限公司","job": "业务部部长","phone": "12345678912","stay": ["27日晚","28日晚",]}
];
// 导出Excel
function exportExcal() {// 文件名称var fileName = 'simple.xlsx';// 固定内容的列数var basic_col_num = 7;// 总列数(固定列+住宿动态列的长度)var all_column_num = basic_col_num + stay_arr.length;// 表单数据数组var data_arr = [];// 数据单元格起始行数var rowStart = 4;// 基础单元格边框样式var borderStyle = {top: { style: "thin", color: { argb: "FF000000" } },left: { style: "thin", color: { argb: "FF000000" } },bottom: { style: "thin", color: { argb: "FF000000" } },right: { style: "thin", color: { argb: "FF000000" } },}// 基础内容对齐方式var basicAlignment = { vertical: 'middle', horizontal: 'center', wrapText: true };// 新建工作簿var wb = new ExcelJS.Workbook();// 设置工作簿属性wb.creator = 'Me'; // 作者wb.lastModifiedBy = 'Her'; // 最后修改人wb.created = new Date(2023, 10, 10); // 创建时间wb.modified = new Date(); // 修改时间wb.lastPrinted = new Date(2023, 10, 10); // 上次打印文档时间wb.properties.date1904 = true; // 将工作簿日期设置为1904日期系统// 向新的工作簿中增加一张工作表var ws = wb.addWorksheet('邀约名单');// 设置默认行高ws.properties.defaultRowHeight = 20;// 也可以在添加工作表的时候直接设置参数// var ws = wb.addWorksheet('邀约名单', {properties:{defaultRowHeight:20}});// 表头部分开始// 设置单元格第一行// 获取单元格内第一行var collectRow = ws.getRow(1);// 设置单行行高collectRow.height = 40;// 设置整行的文字样式collectRow.style.font = { name: 'Microsoft YaHei', size: 16, bold: true, color: { argb: "ffffffff" } };// 设置整行内容对齐方式collectRow.alignment = basicAlignment;// 获取A1单元格var collectcell = ws.getCell(`A1`);// 表格第一行标题内容,A1合并后填充的内容collectcell.value = `参会信息统计表`;// 设置单个单元格的背景色(这里不能设置整行,因为会超出数据的宽度)collectcell.fill = {type: "pattern",pattern: "solid",fgColor: { argb: "ff538dd5" },}// 设置单个单元格的边框样式collectcell.border = {top: { style: "thin", color: { argb: "FF000000" } },left: { style: "thin", color: { argb: "FF000000" } },right: { style: "thin", color: { argb: "FF000000" } },};// 合并单元格,将A1与H1合并(ws.mergeCells(`A1:H1`);)ws.mergeCells(`A1:${getLetter(all_column_num)}1`);// 设置单元格第二行// 获取单元格内第二行var collectRow = ws.getRow(2);// 设置单行行高collectRow.height = 26;// 获取A2单元格var collectcell2 = ws.getCell(`A2`);// 表格第二行标题内容,A2合并后填充的内容collectcell2.value = `活动时间:2023年10月10日 活动地点:XXXXXXX会议中心 住宿酒店:XXXX酒店`;// 单独设置单元格的文字样式collectcell2.font = { name: 'Microsoft YaHei', size: 10, bold: false, color: { argb: "ffffffff" } }; // 字体// 单独设置单元格的背景色collectcell2.fill = {type: "pattern",pattern: "solid",fgColor: { argb: "ff538dd5" },}// 单独设置单元格的内容对齐方式collectcell2.alignment = basicAlignment;// 单独设置单元格的边框样式collectcell2.border = {left: { style: "thin", color: { argb: "FF000000" } },bottom: { style: "thin", color: { argb: "FF000000" } },right: { style: "thin", color: { argb: "FF000000" } },};// 合并单元格,将A2与H2合并(ws.mergeCells(`A2:H2`);)ws.mergeCells(`A2:${getLetter(all_column_num)}2`);// 处理表单第三行第四行每个单元格表头内容for (let i = 1; i <= all_column_num; i++) {let r3cell = ws.getCell(`${getLetter(i)}3`); // 获取第三行的单元格let r4cell = ws.getCell(`${getLetter(i)}4`); // 获取第三行的单元格// 设置内容对齐方式r3cell.alignment = basicAlignment;r4cell.alignment = basicAlignment;// 前面部分固定内容的表头设置if (i <= basic_col_num) {// 设置单元格样式r3cell.border = borderStyle;r3cell.fill = {type: "pattern",pattern: "solid",fgColor: { argb: "ff808080" },}r3cell.font = { name: 'Microsoft YaHei', size: 11, bold: true, color: { argb: "ffffffff" } };switch (i) {case 1:ws.getCell(`${getLetter(i)}3`).value = "序号";// 设置单元格宽度(宽度单位是字符宽度,而不是像素宽度,中文是两个字符)ws.getColumn(i).width = 6;break;case 2:ws.getCell(`${getLetter(i)}3`).value = "嘉宾类别";ws.getColumn(i).width = 16;break;case 3:ws.getCell(`${getLetter(i)}3`).value = "姓名";ws.getColumn(i).width = 10;break;case 4:ws.getCell(`${getLetter(i)}3`).value = "性别";ws.getColumn(i).width = 6;break;case 5:ws.getCell(`${getLetter(i)}3`).value = "单位";ws.getColumn(i).width = 40;break;case 6:ws.getCell(`${getLetter(i)}3`).value = "职务";ws.getColumn(i).width = 24;break;case 7:ws.getCell(`${getLetter(i)}3`).value = "手机号";ws.getColumn(i).width = 15;break;}// 合并上下对应的单元格ws.mergeCells(`${getLetter(i)}3:${getLetter(i)}4`);} else {// 这部分是动态的单元格// 设置单元格宽度ws.getColumn(i).width = 12;// 住宿部分表头样式需要分开设置(因为不合并)r3cell.border = borderStyle;r3cell.fill = {type: "pattern",pattern: "solid",fgColor: { argb: "ffebf1de" },}r4cell.border = borderStyle;r4cell.fill = {type: "pattern",pattern: "solid",fgColor: { argb: "ffebf1de" },}r4cell.font = { name: 'Microsoft YaHei', size: 11, bold: true, color: { argb: "ff595959" } };// 住宿第三行的内容if (i == (basic_col_num + 1)) {ws.getCell(`${getLetter(i)}3`).value = "行程安排";// 合并单元格(行程安排)ws.mergeCells(`${getLetter(i)}3:${getLetter(i + stay_arr.length - 1)}3`);}// 设置住宿列第四行的动态内容ws.getCell(`${getLetter(i)}4`).value = stay_arr[i - basic_col_num - 1];}}// 处理表单数据for (let i = 0; i < test_data.length; i++) {// 序号,嘉宾类别,姓名,性别,单位,职务,手机号data_arr[i] = [i + 1,test_data[i].guest,test_data[i].name,test_data[i].sex,test_data[i].company,test_data[i].job,test_data[i].phone]// 动态添加住宿(循环住宿内容数组)for (let j = 0; j < stay_arr.length; j++) {if (test_data[i].stay) {// 判断该值是否是列表中数据内存在的值if (test_data[i].stay.indexOf(stay_arr[j]) != -1) {data_arr[i].push("是");} else {data_arr[i].push("");}} else {// 如果没有住宿,直接添加空data_arr[i].push("");}}}// 将表单输入插入行for (let i = 0; i < data_arr.length; i++) {// 获取数据行let r_data = ws.getRow(i + rowStart + 1);// 设置整行单元格的内容对其方式r_data.alignment = basicAlignment;// 字体r_data.font = { name: 'Microsoft YaHei', size: 11, bold: false, color: { argb: "ff000000" } };// 设置整行的数据内容r_data.values = data_arr[i];// 循环本行插入的每个单元格,然后给单元格设置边框样式(注意,一定要插入数据后,才能循环设置)// 这里不能用 设置整行边框,因为单元格会超出,不好看了r_data.eachCell({ includeEmpty: true }, function (cell, colNumber) {ws.getCell(`${getLetter(colNumber)}${i + rowStart + 1}`).border = borderStyle});}// node端才能使用// wb.xlsx.writeFile(fileName).then(() => {// console.log('file created');// }).catch(err => {// console.log(err.message);// });wb.xlsx.writeBuffer().then(buffer => {// 这里之前是 FileSaver.saveAs 因为报错换成 window.saveAs 原因文章后面有说window.saveAs(new Blob([buffer], { type: 'application/octet-stream' }), `${fileName || 'excel.xlsx'}`);})
}// 获取第N个字母
function getLetter(num) {return String.fromCharCode(64 + num);
}
代码中用到的方法总结:
新建工作簿 & 向工作簿中增加一张工作表
var workbook = new ExcelJS.Workbook();
var worksheet = workbook.addWorksheet(‘邀约名单’);
合并单元格(将A1与H1合并)
worksheet.mergeCells(‘A1:H1’);
获取行(第一行)
var collectRow = worksheet.getRow(1);
获取列(B或者3)
var collectcell = worksheet.getColumn(‘B’);
var collectcell = worksheet.getColumn(3);
获取单元格(A2)
var collectcell = worksheet.getCell(‘A2’);
设置单元格内容(行、列、单元格均可设置。行、列是赋值数组)
var collectcell = worksheet.getCell(‘A2’);
collectcell.value = ‘活动时间’;
设置单元格宽度(列、单元格均可设置。宽度单位是字符宽度,而不是像素宽度,中文是两个字符。)
var collectcell = worksheet.getCell(‘A2’);
collectcell.width = 6;
设置工作表的默认行高
worksheet.properties.defaultRowHeight = 20;
设置工作表的默认列宽
worksheet.properties.defaultColWidth = 50;
设置文字样式(行,列,单元格均可设置)
var collectcell = worksheet.getCell(‘A2’);
collectcell.font = { name: ‘Microsoft YaHei’, size: 10, bold: false, color: { argb: “ffffffff” } };
设置背景色(行,列,单元格均可设置)
var collectcell = worksheet.getCell(‘A2’);
collectcell.fill = { type: “pattern”, pattern: “solid”, fgColor: { argb: “ff538dd5” } };
设置内容对齐方式(行,列,单元格均可设置)
var collectcell = worksheet.getCell(‘A2’);
collectcell.alignment = {
top: { style: “thin”, color: { argb: “FF000000” } },
left: { style: “thin”, color: { argb: “FF000000” } },
bottom: { style: “thin”, color: { argb: “FF000000” } },
right: { style: “thin”, color: { argb: “FF000000” } },
}
循环行内的每一个单元格(包括空单元格)
let r_data = ws.getRow(i + rowStart + 1);
// 循环本行插入的每个单元格,然后给单元格设置边框样式(注意,一定要插入数据后,才能循环设置)
r_data.eachCell({ includeEmpty: true }, function (cell, colNumber) {
console.log('Cell ’ + colNumber + ’ = ’ + cell.value);
});
遇见的一些问题:
1. 报错:FileSaver.saveAs is not a function.saveAs is not a function
解决方法: 将 FileSaver.saveAs
改成 window.saveAs
即可(原因是 FileSaver 是全局引用了)
2. 为什么不直接用 wb.xlsx.writeFile
答:因为 wb.xlsx.writeFile
只能 node
用,所以需要用 FileSaver
。
3. eachCell
为什么会无效(不进入循环)
答:一定要插入数据后,才能循环,没有插入数据的时候是无法进入循环的。