excelExport 导出封装 做笔记
js
import ExcelJS from "exceljs";
import { saveAs } from "file-saver";
/**
*
* @param headers 表头,[表头1,表头2,...] 必填
* @param tableData 内容 [data1,data2,...] 必填
* @param imageIndex 图片下标列 [0, 1, 2 , ...], 非必填
* @returns {Promise<void>}
*/
export const exportToExcel = async (
headers,
tableData,
imageIndex = [],
batchSize = 1000
) => {
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet("Sheet1");
// 添加表头
const headerRow = worksheet.addRow(headers);
// 设置表头样式
headerRow.eachCell((cell) => {
cell.font = {
bold: true,
color: { argb: "FFFFFFFF" },
size: 12,
};
cell.fill = {
type: "pattern",
pattern: "solid",
fgColor: { argb: "898989" },
};
cell.alignment = {
horizontal: "center",
vertical: "middle",
};
});
// 设置列宽
headers.forEach((_, colIndex) => {
let width = 20; // 默认列宽
if (imageIndex.includes(colIndex)) {
width = 30; // 图片列更宽
}
worksheet.getColumn(colIndex + 1).width = width;
});
// 分批处理数据
const totalBatches = Math.ceil(tableData.length / batchSize);
for (let batchNum = 0; batchNum < totalBatches; batchNum++) {
const batchStart = batchNum * batchSize;
const batchEnd = Math.min(batchStart + batchSize, tableData.length);
const batch = tableData.slice(batchStart, batchEnd);
console.log(`正在处理第 ${batchNum + 1}/${totalBatches} 批数据...`);
// 处理数据行(修改为异步)
// 批量处理行数据
const processRow = async (rowIdx) => {
const row = batch[rowIdx];
const newRow =
imageIndex.length > 0
? row.map((cell, idx) => (imageIndex.includes(idx) ? "" : cell))
: row;
worksheet.addRow(newRow);
worksheet.getRow(batchStart + rowIdx + 2).height = 25;
if (imageIndex.length > 0) {
await processRowImages(batchStart + rowIdx, row);
worksheet.getRow(batchStart + rowIdx + 2).height = 80;
}
worksheet.getRow(batchStart + rowIdx + 2).alignment = {
vertical: "middle",
horizontal: "center",
};
};
// 处理行中的图片
const processRowImages = async (rowIdx, row) => {
await Promise.all(
imageIndex.map(async (colIdx) => {
const imgUrl = row[colIdx];
if (!imgUrl) return;
try {
const parsed = await parseImageData(imgUrl);
if (!parsed) return;
const imageId = workbook.addImage({
base64: parsed.base64,
extension: parsed.extension,
});
worksheet.addImage(imageId, {
tl: { col: colIdx, row: rowIdx + 1 },
ext: { width: 200, height: 100 },
editAs: "oneCell",
});
console.log(`图片处理完成(行${rowIdx + 1}列${colIdx + 1})`);
} catch (error) {
console.error(
`图片处理失败(行${rowIdx + 1}列${colIdx + 1}):`,
error
);
}
})
);
};
// 处理所有行
for (let rowIdx = 0; rowIdx < batch.length; rowIdx++) {
await processRow(rowIdx);
}
if (batchNum < totalBatches - 1) {
await new Promise((resolve) => setTimeout(resolve, 0));
console.log("回收");
// if (global.gc) global.gc(); // 显式调用垃圾回收(如果可用)
}
}
// 生成文件
const buffer = await workbook.xlsx.writeBuffer();
saveAs(new Blob([buffer]), `数据导出_${new Date().getTime()}.xlsx`);
};
// 列索引转Excel字母(如:0 -> A, 1 -> B)
const getExcelColumnLetter = (column) => {
let letter = "";
let col = column + 1;
while (col > 0) {
const remainder = (col - 1) % 26;
letter = String.fromCharCode(65 + remainder) + letter;
col = Math.floor((col - 1) / 26);
}
return letter;
};
// 新增:将图片URL转换为base64
const urlToBase64 = async (url) => {
try {
const response = await fetch(url);
const blob = await response.blob();
return new Promise((resolve) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.readAsDataURL(blob);
});
} catch (error) {
console.error("图片转换失败:", error);
return null;
}
};
// 修改后的解析函数
const parseImageData = async (imgData) => {
if (!imgData) return null;
// 处理URL情况
if (imgData.startsWith("http")) {
const dataURL = await urlToBase64(imgData);
if (!dataURL) return null;
const [header, base64] = dataURL.split(";base64,");
const extension = header.split("/")[1];
return { base64, extension };
}
// 原有处理dataURL的逻辑
if (imgData.startsWith("data:image")) {
const [header, base64] = imgData.split(";base64,");
if (!header || !base64) return null;
const extension = header.split("/")[1];
return { base64, extension };
}
return null;
};
前端使用
js
<template>
<button @click="handleExport">导出Excel</button>
</template>
<script setup>
import { exportDataToExcel } from '@/utils/excelExport';
const headers = ['名称', '价格', '主图'];
const tableData = [
['商品A', 99.9, 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg'],
['商品B', 199.9, '']
];
const imageIndex = [2]; // 第三列为图片列
const handleExport = async () => {
await exportDataToExcel(headers, tableData, imageIndex);
};
</script>