在web开发中,导出是很常见的一个功能,当我进行个人项目练习的时候,导出的时候无法控制列宽以及居中样式,后续发现导出插件无法进行修改,整个插件较为简便易懂的同时,对于EX的控制较为简陋,很多东西并不能设置,根据我的搜索,更改为ExcelJS插件来控制样式以及列宽,截止于现在,我们可以通过引用
<script src="https://cdn.jsdelivr.net/npm/exceljs@4.4.0/dist/exceljs.min.js"></script>
来获得对应的方法进行使用
在实现导出功能之前,我们需要对一些配置进行初始化。下面是配置对象的内容,其中包括了导出字段、表头映射、状态映射和样式配置等:
// 导出配置
const exportConfig = {
// 选择导出的字段路径
selectedHeaders: [
"id",
"user.nickname",
"card",
"tel",
"depart.name",
"doctor_name",
"money",
"status",
"visit_status",
"order_code",
"create_time",
"yes_time",
],
// 表头映射关系
customHeaders: {
id: "订单ID",
"user.nickname": "就诊人",
card: "就诊卡号",
tel: "手机号",
"depart.name": "挂号科室",
doctor_name: "医生姓名",
money: "支付金额",
status: "缴费状态",
visit_status: "就诊状态",
order_code: "订单号",
create_time: "创建时间",
yes_time: "最后缴费时间",
},
// 状态映射配置
statusMappings: {
status: {
2: "欠费",
1: "缴费完成",
null: "未知",
undefined: "未知",
},
visit_status: {
2: "就诊中",
1: "就诊完成",
null: "未知",
undefined: "未知",
},
},
// 样式配置
columnWidth: 18,
styles: {
header: {
fill: {
type: "pattern",
pattern: "solid",
fgColor: { argb: "FFD3D3D3" },
},
font: {
bold: true,
name: "微软雅黑",
size: 12,
},
alignment: {
vertical: "middle",
horizontal: "center",
},
},
body: {
font: {
name: "微软雅黑",
size: 11,
},
alignment: {
vertical: "middle",
horizontal: "center",
},
},
},
};
- selectedHeaders: 这是一个包含了需要导出的字段路径的数组。每个路径指向数据对象中的一个字段,可以是嵌套的字段(例如:"user.nickname")。
- customHeaders: 这是字段的映射,用于将字段路径映射到Excel表格中的实际表头名称。
- statusMappings: 配置字段状态的映射,如"缴费状态"和"就诊状态"的数值映射为具体的状态描述。
- styles: 设置Excel表格的样式,如字体、对齐方式、填充色等。
工具函数
为了从数据源中提取嵌套值,我们编写了以下工具函数:
getNestedValue
该函数用于从对象中根据路径提取嵌套的值:
function getNestedValue(obj, path) {
return path.split(".").reduce((acc, key) => {
if (acc === null || acc === undefined) return undefined;
return acc[key];
}, obj);
}
formatDate
该函数将时间戳转换为易读的日期格式:
数据处理与Excel生成
在数据导出的核心函数exportData
中,我们首先对数据源进行有效性检查和预处理。数据的每一项都会通过配置进行映射和处理,特别是数字格式、日期格式和状态映射。
async function exportData(dataSource) {
try {
if (!dataSource || !Array.isArray(dataSource)) {
throw new Error("无效的数据源");
}
if (dataSource.length === 0) {
showAlerts(".ErrorModal", "5%", "警告:没有可导出的数据");
return;
}
const processedData = dataSource.map((item) => {
const rowData = {};
exportConfig.selectedHeaders.forEach((fieldPath) => {
const displayName = exportConfig.customHeaders[fieldPath];
let rawValue = getNestedValue(item, fieldPath);
// 状态映射处理
if (exportConfig.statusMappings[fieldPath]) {
rawValue =
exportConfig.statusMappings[fieldPath][rawValue] ?? "未知状态";
}
// 特殊字段处理
if (fieldPath === "money") {
const numericString = String(rawValue).replace(/[^0-9.]/g, "");
const numericValue = parseFloat(numericString);
if (!isNaN(numericValue) && isFinite(numericValue)) {
rawValue = numericValue;
} else {
rawValue = "金额异常";
showAlerts(".ErrorModal", "5%", "金额异常");
}
}
if (["create_time", "yes_time"].includes(fieldPath)) {
rawValue = formatDate(rawValue);
}
// 默认空值处理
if ([null, undefined, ""].includes(rawValue)) {
rawValue = fieldPath.includes("time") ? "时间未记录" : "未知";
}
rowData[displayName] = rawValue;
});
return rowData;
});
// 创建Excel工作簿
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet("门诊缴费记录", {
properties: { tabColor: { argb: "FF00FF00" } },
});
// 添加表头
const headers = Object.values(exportConfig.customHeaders);
const headerRow = worksheet.addRow(headers);
headerRow.eachCell((cell) => {
cell.style = exportConfig.styles.header;
});
// 添加数据行
processedData.forEach((dataItem) => {
const rowValues = headers.map((header) => dataItem[header]);
const dataRow = worksheet.addRow(rowValues);
dataRow.eachCell((cell) => {
cell.style = exportConfig.styles.body;
});
});
// 设置列宽
worksheet.columns = headers.map(() => ({
width: 20,
style: { numFmt: "@" },
}));
// 生成文件
const buffer = await workbook.xlsx.writeBuffer();
const blob = new Blob([buffer], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
const downloadLink = document.createElement("a");
downloadLink.href = URL.createObjectURL(blob);
downloadLink.download = `门诊缴费记录_${new Date().toISOString().slice(0, 10)}_${hours}:${minutes}:${seconds}.xlsx`;
document.body.appendChild(downloadLink);
downloadLink.click();
// 清理资源
setTimeout(() => {
URL.revokeObjectURL(downloadLink.href);
downloadLink.remove();
}, 1000);
} catch (error) {
console.error("导出过程中发生错误:", error);
alert(`导出失败: ${error.message}`);
}
}
核心步骤
- 数据预处理: 根据配置映射数据字段,并处理特殊字段(如金额、日期等)。
- Excel生成: 使用ExcelJS创建工作簿,并设置表头和数据行的样式。
- 导出功能: 生成Excel文件并通过Blob触发下载。
用户交互与事件处理
在页面交互中,我们提供了全选、反选和导出选中项/全部数据的功能。用户可以根据需求选择需要导出的数据,或选择导出全部数据。
事件处理代码
$(document).ready(function () {
$(".select-all").on("change", function () {
$(".row-checkbox").prop("checked", this.checked);
});
$(".choice_export").on("click", async function () {
const selectedIndexes = $(".notice .checkbox_id input:checked")
.map(function () {
return $(this).attr("dataIndex");
})
.get();
if (selectedIndexes.length === 0) {
showAlerts(".ErrorModal", "5%", "请至少选择一条有效记录");
return;
}
try {
const selectedData = globalData.filter((item) =>
selectedIndexes.includes(item.id.toString())
);
await exportData(selectedData);
showAlerts(".correctModal", "5%", `成功导出 ${selectedData.length} 条数据`, "success");
} catch (error) {
showAlerts(".ErrorModal", "5%", `导出失败: ${error.message}`, "error");
}
});
$(".all_export").on("click", async function () {
if (!globalData || globalData.length === 0) {
showAlerts(".ErrorModal", "5%", "当前没有可导出的数据");
return;
}
try {
await exportData(globalData);
showAlerts(".correctModal", "5%", "成功导出全部数据");
} catch (error) {
showAlerts(".ErrorModal", "5%", `导出失败: ${error.message}`, "error");
}
});
});
本文介绍了如何使用ExcelJS库进行数据导出,涵盖了如何处理数据映射、设置自定义表头、实现状态映射、格式化日期和金额、以及如何生成和下载Excel文件。通过这些步骤,你可以轻松实现复杂的Excel导出功能,并根据不同的业务需求进行调整和扩展。