js
复制代码
import dayjs from "dayjs";
import * as XLSX from "xlsx";
import XLSXS from "xlsx-style";
export function export2Excel(info: {
// 文件名(工作簿名)
title: string;
// 数据
data: any[] | Record<string, any>[];
// 表格列
header: {
title: any;
key: any;
}[];
}) {
const { title, data, header } = info;
// title重命名
const tableTitle = `${title}_评论数据_${dayjs().format(
"YYYY-MM-DD_HH_mm_ss"
)}`;
// 创建一个工作簿
const wb = XLSX.utils.book_new();
// 添加sheet到工作簿中
addSheet({
xl: XLSX,
wb,
data,
header,
tableTitle,
});
// 文件存储成blob流数据(二进制流)
const tmpDown = new Blob([
s2ab(
// 为了解决样式丢失问题只能用XLSXD来进行转换
XLSXS.write(wb, {
bookType: "xlsx",
type: "binary",
})
) as any,
]);
// 下载二进制流(title是下载文件的名字)
downExcel(tmpDown, tableTitle + ".xlsx");
}
/**
* 添加sheet页
* @param info
* wb: 工作簿
* xl: XSLX库
* data: 表格数据
* header: 表格头
* title: 表格标题
*
*/
const addSheet = (info: {
wb: any;
xl: any;
data: any[] | Record<string, any>[];
header: {
title: any;
key: any;
}[];
tableTitle: string;
}) => {
const { wb, xl, data, header, tableTitle } = info;
const keyArr = header.map((item: any) => item.key);
const nameArr = header.map((item: any) => item.title);
const excelData = [nameArr];
data.forEach((item: any) => {
const dataArr: any = [];
keyArr.forEach((key: any) => {
dataArr.push(item[key]);
});
excelData.push(dataArr);
});
// excelData:第一行是表头其他行是数据
// xl.utils.aoa_to_sheet: 将js数据转换成excel数据,类型是A1,A2,A3,B1,B2,B3......,并创建sheet
const tableData = xl.utils.aoa_to_sheet(excelData); // 此处excelData为表格的数据
// tableData格式:[excel对应的行列:{v:值,s:表格显示配置}]
// tableData['!cols']:定义导出表格每列的宽度,从左到右依次对应header中从左到右的列
tableData["!cols"] = [];
tableData["!rows"] = [{ hpx: 40 }, { hpx: 40 }, { hpx: 40 }];
// 给导出的excel做样式处理
header.forEach((col: any, colIndex: any) => {
// 设置列宽
switch (col.key) {
case "commentTime":
tableData["!cols"].push({ wch: 18 });
break;
case "connect":
tableData["!cols"].push({ wch: 25 });
break;
default:
tableData["!cols"].push({ wch: 15 });
break;
}
// 格式化tableData数据格式,添加样式
excelData.forEach((row, rowIndex) => {
// 该数据对应的单元格位置
const cellPosition = createPosition(colIndex, rowIndex);
if (tableData[cellPosition]) {
// 单元格的样式/主题(如果适用)
tableData[cellPosition].s = {
// 数据水平垂直居中
alignment: {
horizontal: "left",
vertical: "center",
},
fill: {
// 背景色
// fgColor: { rgb: "FEE8D9" },
},
border: {
top: { style: "thin", color: { rgb: "ebebeb" } },
bottom: { style: "thin", color: { rgb: "ebebeb" } },
left: { style: "thin", color: { rgb: "ebebeb" } },
right: { style: "thin", color: { rgb: "ebebeb" } },
},
// border: {
// right: {
// style: "dashed",
// color: { rgb: "FFFFFF" },
// },
// },
};
// 第一行
if (rowIndex === 0) {
tableData[cellPosition].s.fill = {
// 背景色
// fgColor: { rgb: "fb731a" },
};
tableData[cellPosition].s.font = {
// 字体
sz: "12",
// color: { rgb: "FFFFFF" },
bold: true,
shadow: true,
};
} else if (
typeof row[colIndex] === "string" &&
row[colIndex].includes("%")
) {
// 格式化百分号数据为excel中的number类型
let value = tableData[cellPosition].v;
value = Number(value.replace("%", ""));
tableData[cellPosition].v = (value / 100).toFixed(4);
// 单元格展示类型
tableData[cellPosition].z = XLSX.SSF._table[10];
// 单元格数据类型
tableData[cellPosition].t = "n";
}
}
});
});
// 将sheet追加到工作簿, wb:工作簿,tableData:新的sheet,title:sheet名称
// sheet 名称 不能 超过 31 位,否则 会报错
xl.utils.book_append_sheet(wb, tableData, tableTitle.substring(0, 31)); // title为自定义的sheet表名
};
// 自定义下载文件方法
/**
* 使用a标签将文件进行下载
* @param obj blob流数据
* @param fileName 文件名
*/
const downExcel = (obj: any, fileName: string) => {
const a_node = document.createElement("a");
a_node.download = fileName;
if ("msSaveOrOpenBlob" in navigator) {
// msSaveOrOpenBlob 方法允许用户在客户端上下载文件
(navigator as any).msSaveOrOpenBlob(obj, fileName);
} else {
// createObjectURL根据传入的参数创建一个指向该参数对象的URL.
a_node.href = URL.createObjectURL(obj);
}
a_node.click();
//
setTimeout(() => {
// 释放一个通过URL.createObjectURL()创建的对象URL
URL.revokeObjectURL(obj);
}, 2000);
};
/**
* 文件流转换:将二进制字符串转换成对应的Unicode编码数组
* @param s 文件的二进制字符串
* @returns 返回存储文件数据的buffer或数组
*/
const s2ab = (s: any) => {
if (typeof ArrayBuffer !== "undefined") {
// buffer存储
// 在内存中分配s.length个的字节空间
const buf = new ArrayBuffer(s.length);
// Uint8Array 数组类型表示一个 8 位无符号整型数组,创建时内容被初始化为 0
const view = new Uint8Array(buf);
// 将二进制字符串转换成Unicode编码存储
for (let i = 0; i != s.length; ++i) {
view[i] = s.charCodeAt(i) & 0xff;
}
return buf;
} else {
// 数组存储
const buf = new Array(s.length);
for (let i = 0; i != s.length; ++i) {
buf[i] = s.charCodeAt(i) & 0xff;
}
return buf;
}
};
// 行数 需要 小于 26 * 26
/**
*
* @param colIndex 该数据对应列数
* @param rowIndex 该数据对应的行数
* @returns 返回的是该数据对应的单单元个位置
*/
const createPosition = (colIndex: any, rowIndex: any) => {
rowIndex++;
if (colIndex >= 26) {
const n1 = Math.floor(colIndex / 26) - 1;
const n2 = colIndex % 26;
const s1 = String.fromCharCode(n1 + 65);
const s2 = String.fromCharCode(n2 + 65);
return s1 + s2 + rowIndex;
} else {
// fromCharCode:将 Unicode 编码转为一个字符 (65对应A)
return String.fromCharCode(colIndex + 65) + rowIndex;
}
};