前端 Excel 导出实战:基于 ExcelJS 实现复杂样式表格导出
在企业级后台系统开发中,Excel 文件导出是高频刚需功能,尤其需要支持自定义表头、单元格合并、样式美化、冻结窗格、数据格式化等复杂场景。
本文将基于ExcelJS(前端 Excel 处理神器),手把手带你实现一份带复杂样式、动态数据、格式化导出的资金使用计划 Excel 文件,代码可直接复用,适配 Vue/React 等所有前端框架
try {
const headers = ['序号', '项目名称'];
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('sheet1');
worksheet.views = [{ state: 'frozen', ySplit: 3 }];
worksheet.columns = [
{ width: 10 },
{ width: 28 },
{ width: 16 },
{ width: 18 },
{ width: 18 },
{ width: 20 },
{ width: 18 },
{ width: 16 },
];
worksheet.mergeCells('A1:H1');
worksheet.getCell('A1').value = this.plan.name || '资金使用计划';
worksheet.getCell('A1').font = { name: 'Arial', size: 16, bold: true };
worksheet.getCell('A1').alignment = { vertical: 'middle', horizontal: 'center' };
worksheet.mergeCells('A2:H2');
worksheet.getCell('A2').value = `制表日期:${this.plan.createTime || ''} 单位:${this.form.unit || ''}`;
worksheet.getCell('A2').alignment = { vertical: 'middle', horizontal: 'right' };
worksheet.getCell('A2').font = { name: 'Arial', size: 12 };
headers.forEach((h, idx) => {
const cell = worksheet.getCell(3, idx + 1);
cell.value = h;
cell.font = { bold: true, color: { argb: 'FF303133' } };
cell.alignment = { vertical: 'middle', horizontal: 'center' };
cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF99CCFF' } };
});
const borderThin = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' },
};
const rows = this.tableData || [];
let excelRow = 4;
rows.forEach((r) => {
const item = r.item || {};
const rowValues = r.rowType === 'ITEM'
? [
item.seq || '',
item.itemName || '',
toNumberOrNull(item.deptReportedAmount) !== null ? roundTo4(toNumberOrNull(item.deptReportedAmount)) : '',
item.expensePeriod || '',
item.budgetSubject || '',
item.otherUnit || '',
toNumberOrNull(item.accountBalanceAmount) !== null ? roundTo4(toNumberOrNull(item.accountBalanceAmount)) : '',
item.reimburseDate || '',
]
: [
'',
r.label || '',
r.rowType === 'DEPARTMENTSUMMARY' ? this.officeTotals.deptReportedAmount : '',
'',
'',
'',
r.rowType === 'DEPARTMENTSUMMARY' ? this.officeTotals.accountBalanceAmount : '',
'',
];
const fillColor = r.rowType === 'CATEGORY' || r.rowType === 'DEPARTMENTSUMMARY' ? 'FFBFBFBF' : 'FFFFFFFF';
for (let c = 0; c < headers.length; c++) {
const cell = worksheet.getCell(excelRow, c + 1);
cell.value = rowValues[c];
cell.border = borderThin;
cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: fillColor } };
cell.alignment = {
vertical: 'middle',
horizontal: c === 1 ? 'left' : 'center',
};
if (r.rowType !== 'ITEM') {
cell.font = { bold: true };
}
}
excelRow++;
});
const buffer = await workbook.xlsx.writeBuffer();
const blob = new Blob([buffer], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
});
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `${this.plan.name || '资金使用计划'}.xlsx`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);