xlsx-js-style导出Excel文件
分类: JavaScript 414 0
安装 xlsx-js-style
导出
excel 较常见的 js 库是之一是 xlsx
,xlsx
算是基础版本,不能对单元格进行样式对齐方式、文字颜色、背景颜色等)的修饰,如果需要修饰单元格,可使用 xlsx-js-style
-
下载引入
npm install xlsx-js-style
import XLSXS from 'xlsx-js-style'; -
数据
// 一般我们拿到的是从接口中请求到的对象数组,在使用是需要转成二维数组,下面有介绍 const data = [ { name: '商品01', mb_num: 50, mb_sum: 5000, pc_num: 30, pc_sum: 3000, total_num: 80, total_sum: 8000 }, { name: '商品02', mb_num: 50, mb_sum: 5000, pc_num: 30, pc_sum: 3000, total_num: 80, total_sum: 8000 }, { name: '商品03', mb_num: 50, mb_sum: 5000, pc_num: 30, pc_sum: 3000, total_num: 80, total_sum: 8000 }, ]
-
数据需要转化成二维数组
const body = data.map(x => ([x.name, x.mb_num, x.mb_sum, x.pc_num, x.pc_sum, x.total_num, x.total_sum])) // 转换后的数据为一个二维数组 [ ['商品01', 50, 5000, 30, 3000, 80, 8000] ['商品02', 50, 5000, 30, 3000, 80, 8000] ['商品03', 50, 5000, 30, 3000, 80, 8000] ]
-
定义表头并 将定义好的表头添加到 body 中
/* 定义表头,每一行即是一个数组,数组中的元素即是一个个单元格内容。 如果单元格不需要样式,那么每个元素类型为字符串即可。如:['一月(2022年01月)'], 如果单元格需要样式,那么数组中的元素就需要为一个个对象,在此对象中定义单元格的样式等等。 */ const header = [ // 第一行,需要样式,则数组中元素为对象,进行定义样式。 [ { v: '一月(2023年01月)', t: 's', s: { // font 字体属性 font: { bold: true, sz: 14, name: '宋体', }, // alignment 对齐方式 alignment: { vertical: 'center', // 垂直居中 horizontal: 'center', // 水平居中 }, // border 边框属性 border: { top: { style: 'thin' }, bottom: { style: 'thin' }, left: { style: 'thin' }, right: { style: 'thin' } }, // fill 颜色填充属性 fill: { fgColor: { rgb: '87CEEB' }, }, }, }, ], ['商品名称', '手机客户端', '', '电脑客户端', '', '总计', ''], ['', '销售数量', '销售金额', '销售数量', '销售金额', '销售数量', '销售金额'] ] body.unshift(...header);
-
将二维数组转成 sheet 工作薄
// 这里我们举例是用 aoa_to_sheet ,所以是需要将数据源转成一个二维数组 const sheet = XLSXS.utils.aoa_to_sheet(body); // aoa_to_sheet 是将【一个二维数组】转化成 sheet // json_to_sheet 是将【由对象组成的数组】转化成sheet // table_to_sheet 是将【table的dom】直接转成sheet
-
!merges 设置单元格合并
如果需要设置单元格合并,则定义好
merges
,添加到sheet
中。merges 为一个对象数组,每个对象设定了单元格合并的规则。
{ s: { r: 0, c: 0 }, e: { r: 0, c: 2 } }, 即为一个规则,s:开始位置, e:结束位置, r:行, c:列
const merges = [ { s: { r: 0, c: 0 }, e: { r: 0, c: 6 } }, { s: { r: 1, c: 1 }, e: { r: 1, c: 2 } }, { s: { r: 1, c: 3 }, e: { r: 1, c: 4 } }, { s: { r: 1, c: 5 }, e: { r: 1, c: 6 } }, { s: { r: 1, c: 0 }, e: { r: 2, c: 0 } }, ] sheet['!merges'] = merges; // 添加到sheet中 // 方法一:通过 decode_range 设置范围合并单元格 ws['!merges'].push(utils.decode_range('A1:C1')) // 方法二:手动设置 A1-C1 的单元格合并 // merges 为一个对象数组,每个对象设定了单元格合并的规则 // s:开始位置, e:结束位置, r:行, c:列 // ws['!merges'] = [ // { s: { r: 0, c: 0 }, e: { r: 0, c: 2 } }, // ]
-
!cols 设置列宽
cols 为一个对象数组,依次表示每一列的宽度。
const cols = [ { wch: 10 }, { wch: 10 }, { wch: 10 }, { wch: 10 }, { wch: 10 }, { wch: 10 }, { wch: 10 } ]; sheet['!cols'] = cols; // 添加到sheet中
-
!rows 设置行高
rows 为一个对象数组,依次表示每一行的高度
const rows = [ { hpx: 20 }, { hpx: 16 }, { hpx: 18 } ] sheet['!rows'] = rows; // 添加到sheet中
-
创建虚拟的 workbook
整个表格可称为
workbook
。里面的每张表分别是sheet
const workbook = xlsx.utils.book_new();
-
向 workbook 中添加 sheet
XLSXS.utils.book_append_sheet(workbook, sheet, 'sheet名称'); // 一个 workbook 允许添加多个 sheet,即可以同时创建多个表 // xlsx.utils.book_append_sheet(workbook, sheet2, 'sheet名称2');
-
导出
// 注意:定义导出 excel 的名称时需要加上后缀 .xlsx xlsx.writeFile(workbook, 'excel名称.xlsx');
-
创建虚拟的 workbook
、将数组转成 sheet
、向workbook中添加sheet
和导出workbook
,这四个步骤是必要的。 -
设置合并单元格
、设置列宽
、设置行高
是可选的,根据需求进行添加。 -
如果添加样式需要
border
则需要在被合并的单元格位置进行占位。 -
常用方法封装
import { isArray } from 'lodash-es'; import XLSX from 'xlsx-js-style'; // columns:表头数据 data:表格数据 excelName:导出的文件名 autoWidth: 自适应列宽 sheetTitle:sheet名称,excel打开以后,的底部切换名称 wrapText:是否自动换行 export interface exportExcelTypes { columns: any[] data: any[] excelName?: string autoWidth?: boolean sheetTitle?: string wrapText?: '' } // 导出表头默认样式 const THeaderStyle = { // font 字体属性 font: { bold: true, sz: 12 }, // alignment 对齐方式 alignment: { vertical: 'center', // 垂直居中 horizontal: 'center', // 水平居中 }, // fill 颜色填充属性 fill: { fgColor: { rgb: 'e1e2e3' }, }, // border style border: { top: { style: 'thin', color: { rgb: '909399' } }, right: { style: 'thin', color: { rgb: '909399' } }, bottom: { style: 'thin', color: { rgb: '909399' } }, left: { style: 'thin', color: { rgb: '909399' } } } } const TBodyStyle = { alignment: {// 文字居中 //字体水平居中、垂直居中、自动换行 font: {// 字体设置 sz: 12, bold: false, }, horizontal: 'left', //居中 vertical: 'center', //垂直居中 wrapText: true }, // numFmt:'@' } /** * 自适应列宽 * @param {*} data * @returns {*} {*} */ const autoWidthFn = (data: any): any => { // 设置worksheet每列的最大宽度 const colWidth = data.map((row: any) => row.map((val: { toString: () => { (): any; new(): any; charCodeAt: { (arg0: number): number; new(): any; }; length: number; }; } | null) => { // 先判断是否为null/undefined if (val == null) { return { 'width': 10 } } // 再判断是否为中文 else if (val.toString().charCodeAt(0) > 255) { return { 'width': val.toString().length * 2 + 6 } } else { return { 'width': val.toString().length + 2 + 1 } } })) // 以第一行为初始值 const result = colWidth[0] for (let i = 1; i < colWidth.length; i++) { for (let j = 0; j < colWidth[i].length; j++) { if (result[j]['width'] < colWidth[i][j]['width']) { result[j]['width'] = colWidth[i][j]['width'] > 480 ? 480 : colWidth[i][j]['width'] } } } return result } /** * 是否自动换行 * @param {*} sheet * @returns {*} {*} */ const wrapTextFn = (sheet: any): any => { for (const key in sheet) { if (key.indexOf('!') === -1) { // 排除带!的字段,只要单元格字段 if (!sheet[key].s && sheet[key].z != 'm/d/yy') { sheet[key].t = 's' sheet[key].s = TBodyStyle } } } return sheet } /** * json数据导出excel * @param {exportExcelTypes} params */ export const exportExcel = (params: exportExcelTypes) => { const { columns, data, excelName, autoWidth = true, sheetTitle = 'sheet名称', wrapText = false } = params if (!isArray(columns) || !isArray(data)) { return } const THeader: any = [], filterVal: any = []; columns.forEach((item: { label: any; prop: any; }) => { THeader.push({ v: item.label, t: 's', s: THeaderStyle }) filterVal.push(item.prop) }) const TData = data.map(v => filterVal.map((j: string | number) => v[j])) const sheetData = [[...THeader], ...TData] const sheet = XLSX.utils.aoa_to_sheet(sheetData);// aoa_to_sheet 将二维数组转成 sheet // 自适应列宽 if (autoWidth) { sheet['!cols'] = autoWidthFn(sheetData) } // 自动换行 if (wrapText) { wrapTextFn(sheet) } // 设置表头行高 const rows = [{ hpx: 20 }] sheet['!rows'] = rows; // 将rows添加到sheet中,设置行高 // 创建虚拟的 workbook const workbook = XLSX.utils.book_new(); // 向 workbook 中添加 sheet XLSX.utils.book_append_sheet(workbook, sheet, sheetTitle); // 导出 workbook XLSX.writeFile(workbook,
${excelName ? (excelName.includes('.xlsx') ? excelName : excelName + '.xlsx') : 'excel名称.xlsx'}
); } /** * json数据导出excel 导出多个 * @param {{ params: exportExcelTypes[], excelName: string }} dataParams * @returns */ export const exportMultipleExcel = (dataParams: { params: exportExcelTypes[], excelName: string }) => { const { params, excelName } = dataParams if (!isArray(params)) { return } // 创建虚拟的 workbook const workbook = XLSX.utils.book_new(); params.forEach((item: any) => { const { columns, data, autoWidth = true, sheetTitle = 'sheet名称', wrapText = false } = item if (!isArray(columns) || !isArray(data)) { return } const THeader: any = [], filterVal: any = []; columns.forEach((item: { label: any; prop: any; }) => { THeader.push({ v: item.label, t: 's', s: THeaderStyle }) filterVal.push(item.prop) }) const TData = data.map(v => filterVal.map((j: string | number) => v[j])) const sheetData = [[...THeader], ...TData] const sheet = XLSX.utils.aoa_to_sheet(sheetData);// aoa_to_sheet 将二维数组转成 sheet // 自适应列宽 if (autoWidth) { sheet['!cols'] = autoWidthFn(sheetData) } // 自动换行 if (wrapText) { wrapTextFn(sheet) } // 设置表头行高 const rows = [{ hpx: 20 }] sheet['!rows'] = rows; // 将rows添加到sheet中,设置行高 // 向 workbook 中添加 sheet XLSX.utils.book_append_sheet(workbook, sheet, sheetTitle); }) // 导出 workbook XLSX.writeFile(workbook,${excelName ? (excelName.includes('.xlsx') ? excelName : excelName + '.xlsx') : 'excel名称.xlsx'}
); } /** * 使用插件内的方法,自动使用dom转换为数据 * 通过将一个table dom直接转成sheet,会自动识别colspan和rowspan并将其转成对应的单元格合并 * @param {{ id: string, excelName: string, autoWidth: boolean, sheetTitle: string, wrapText: boolean}} dataParams * @returns */ export const exportDomExcel = (dataParams: { id: string, excelName: string, autoWidth: boolean, sheetTitle: string, wrapText?: boolean }) => { const { id, excelName, autoWidth = true, sheetTitle = 'sheet名称', wrapText = false } = dataParams const table = document.querySelector(id); // Generate the sheet data and add styles: const sheet = XLSX.utils.table_to_sheet(table); //将一个table对象转换成一个sheet对象 const headerRange = XLSX.utils.decode_range(sheet['!ref']); // 把table sheet对象转换成二进制数据 const jsonData = XLSX.utils.sheet_to_json(sheet, { header: 1 }); const arrayData: any = []; jsonData.forEach((row: any) => { const rowData: any = []; Object.values(row).forEach((cell) => { rowData.push(cell); }); arrayData.push(rowData); }); // 设置表头样式 for (let col = headerRange.s.c; col <= headerRange.e.c; col++) { const cellInfo = XLSX.utils.encode_cell({ r: 0, c: col }); const cell = sheet[cellInfo]; cell.s = THeaderStyle; } // 自适应列宽 if (autoWidth) { sheet['!cols'] = autoWidthFn(arrayData) } // 自动换行 if (wrapText) { wrapTextFn(sheet) } // 设置表头行高 const rows = [{ hpx: 20 }] sheet['!rows'] = rows; // 将rows添加到sheet中,设置行高 // 创建虚拟的 workbook const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, sheet, sheetTitle); XLSX.writeFile(workbook,${excelName ? (excelName.includes('.xlsx') ? excelName : excelName + '.xlsx') : 'excel名称.xlsx'}
); return } /** * 手动把table内的值,获取到组装成所需要的数组 如果不需要过滤下载的表格列,用第一个更好,这个主要是为了过滤列,报告里需要过滤掉需要跟操作两列 * 通过将一个table dom直接转成sheet,会自动识别colspan和rowspan并将其转成对应的单元格合并 * @param {{ id: string, excelName: string, autoWidth: boolean, sheetTitle: string, wrapText: boolean,filterTHeaderTextArr:string[]}} dataParams * @returns */ export const exportDomExcelFilter = (dataParams: { id: string, excelName: string, autoWidth: boolean, sheetTitle: string, wrapText?: boolean, filterTHeaderTextArr?: string[] }) => { const { id, excelName, autoWidth = true, sheetTitle = 'sheet名称', wrapText = false, filterTHeaderTextArr = [] } = dataParams const table: any = document.querySelector(id); // Generate the sheet data and add styles: const rows = Array.from(table.getElementsByTagName('tr')); const tableData = rows.map((row: any) => Array.from(row.getElementsByTagName('td')).map((cell: any) => cell.textContent.trim())).filter(item => item.length > 0); const THeader = rows.map((row: any) => Array.from(row.getElementsByTagName('th')).map((cell: any) => cell.textContent.trim())).filter(item => item.length > 0); if (THeader.length) { THeader.forEach((item: any) => { item.forEach((it: any, ind: number) => { if (filterTHeaderTextArr.includes(it)) { item.splice(ind, 1) tableData.forEach(datas => { datas.splice(ind, 1) }) } }) }) } const sheetData = [...THeader, ...tableData] const sheet = XLSX.utils.aoa_to_sheet(sheetData);// aoa_to_sheet 将二维数组转成 sheet const headerRange = XLSX.utils.decode_range(sheet['!ref']); // 设置表头样式 for (let col = headerRange.s.c; col <= headerRange.e.c; col++) { const cellInfo = XLSX.utils.encode_cell({ r: 0, c: col }); const cell = sheet[cellInfo]; cell.s = THeaderStyle; } // 自适应列宽 if (autoWidth) { sheet['!cols'] = autoWidthFn(sheetData) } // 自动换行 if (wrapText) { wrapTextFn(sheet) } // 设置表头行高 const rowstyle = [{ hpx: 20 }] sheet['!rows'] = rowstyle; // 将rows添加到sheet中,设置行高 // 创建虚拟的 workbook const workbook = XLSX.utils.book_new(); // 向 workbook 中添加 sheet XLSX.utils.book_append_sheet(workbook, sheet, sheetTitle); // 导出 workbook XLSX.writeFile(workbook,${excelName ? (excelName.includes('.xlsx') ? excelName : excelName + '.xlsx') : 'excel名称.xlsx'}
); }
共 0 条评论关于 “xlsx-js-style导出Excel文件”