import ExcelJS from 'exceljs' /* 导出的参数 */ export interface IExcel { // 文件名称 fileName?: string // 表头数据 rows: Array> // 表格数据 data: Array> // 表格列宽 width?: Array // 需要合并的表格 merge?: Array } export const useExcel = () => { /** * 导入excel * @param file 传入文件 * @param fileMaxSize number 最大文件大小 单位 MB */ const importExcel = (file: File, fileMaxSize: number = 20) => { return new Promise((resolve, reject) => { if ( !['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'].includes( file.type ) ) { reject(new Error('只能选择 excel 文件')) return } if (file.size / 1024 / 1024 > fileMaxSize) { reject(new Error('大小不能超过 20MB')) return } const reader = new FileReader() reader.onload = e => { const workbook = new ExcelJS.Workbook() workbook.xlsx.load(e.target?.result as any).then(() => { const title: string[] = [] const list: Record[] = [] workbook.eachSheet(sheet => { list.splice(0, list.length) sheet.eachRow({ includeEmpty: true }, row => { title.splice(0, title.length) const item: Record = {} row.eachCell({ includeEmpty: true }, cell => { const key = cell.address.slice(0, -String(cell.row).length) title.push(key) const isMerged = cell.isMerged && cell.address !== cell.master.address item[key] = { value: cell.value, colspan: isMerged ? 0 : 1, rowspan: isMerged ? 0 : 1 } }) list.push(item) }) // 生成表格的跨行跨列 sheet.eachRow({ includeEmpty: true }, row => { row.eachCell({ includeEmpty: true }, cell => { const master = cell.master if (cell.isMerged && cell.address !== master.address) { const mk = master.address.slice(0, -String(master.row).length) const mItem = list[Number(master.row) - 1][mk] const rs = Math.abs(Number(cell.row) - Number(master.row)) + 1 rs > mItem.rowspan && (mItem.rowspan = rs) const cs = Math.abs(Number(cell.col) - Number(master.col)) + 1 cs > mItem.colspan && (mItem.colspan = cs) } }) }) }) resolve({ title, list }) }) } reader.readAsArrayBuffer(file) }) } /** * 导出excel ToDo:增加传入接口地址选项,如果存在接口地址,则直接请求接口 */ const exportExcel = (options: IExcel) => { return new Promise((resolve, reject) => { options = Object.assign( { fileName: options.fileName || Date.now() }, options ) const workbook = new ExcelJS.Workbook() const sheet = workbook.addWorksheet('Sheet1') if (!(Array.isArray(options.rows) && Array.isArray(options.rows[0]))) { reject(new Error('表头数据必须为二维数组')) return } if (!Array.isArray(options.data)) { reject(new Error('表格数据必须为数组')) return } // 设置表格宽度 const widths = Array.isArray(options.width) && options.width.length ? options.width : Array.from(Array(options.rows[0].length).keys()).map(() => 16) // 设置表头 options.rows.forEach(row => { sheet.addRow(row) }) // 设置列宽 options.data.forEach(item => { sheet.addRow(item) }) // 设置列宽 widths.forEach((width, index) => { sheet.getColumn(index + 1).width = width }) // 设置样式 sheet.eachRow({ includeEmpty: true }, (row, rowIndex) => { row.height = 20 row.eachCell({ includeEmpty: true }, cell => { cell.border = { top: { style: 'thin' }, left: { style: 'thin' }, bottom: { style: 'thin' }, right: { style: 'thin' } } cell.alignment = { vertical: 'middle', horizontal: 'center' } cell.font = { size: 12, bold: rowIndex < options.rows.length } }) }) // 合并单元格 if (Array.isArray(options.merge)) { options.merge.forEach(item => { sheet.mergeCells(item) }) } // 下载文件 workbook.xlsx.writeBuffer().then(data => { download(data, `${options.fileName}.xlsx`) }) }) } /* 转为二维数组 */ const toTwoArray = (list: Record[]) => { const arr: Array> = [] list.forEach((item: any) => { const brr = Object.values(item).map((ite: any) => ite.value) arr.push(brr) }) return arr } /** * 下载文件 ToDo:如果传入是http地址,则直接下载 * @param data 二进制数据 * @param name 文件名 * @param type 文件类型 */ const download = (data: Blob | ArrayBuffer | string, name: string, type?: string) => { const blob = new Blob([data], { type: type || 'application/octet-stream' }) const url = window.URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = name a.style.display = 'none' document.body.appendChild(a) a.click() document.body.removeChild(a) URL.revokeObjectURL(url) } return { importExcel, toTwoArray, exportExcel } }