库:Excel.js(版本4.3.0) 和 FileSaver(版本2.0.5)
CDN地址:

1
2
<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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<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>

</head>

<body>
<script>
// 住宿安排(动态列)
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
exportExcal()
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);
// });

// 添加第二个工作表
const sheet2 = wb.addWorksheet('Sheet2');
// 往第二个工作表添加数据
sheet2.addRow(['D1', 'E1', 'F1']);
sheet2.addRow(['D2', 'E2', 'F2']);

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);
}

</script>
</body>

</html>

代码中用到的方法总结:

新建工作簿 & 向工作簿中增加一张工作表

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 为什么会无效(不进入循环)
    答:一定要插入数据后,才能循环,没有插入数据的时候是无法进入循环的。

另外说一下

有兴趣的朋友也可以试试 SheetJS ,感觉好像功能更多一些,下次我再需要用到excel的时候也会尝试一下的。
SheetJS中文文档:https://github.com/rockboom/SheetJS-docs-zh-CN

尝试完成,回来记录一下:

SheetJS 文档会更丰富一些,但是他分社区版(免费)和专业版(收费)。社区版不支持导出文件的样式修改,专业版才可以。
我决定还是继续用 Excel.js

原文链接:https://blog.csdn.net/qq_17627195/article/details/133945359