file-saver xlsx前端匯出excel檔案
阿新 • • 發佈:2022-05-30
// 首先安裝 npm install file-saver xlsx -S // 在utils檔案下新建excel檔案 import { saveAs } from 'file-saver' import { WorkBook, WorkSheet, utils, SSF, write } from 'xlsx' interface CellInterface { v: Date | number | boolean | string t: string z: string } class Workbook implements WorkBook { SheetNames: string[] = [] Sheets: { [sheet: string]: WorkSheet } = {} } const generateArray = (table: HTMLElement) => { const out = [] const rows = table.querySelectorAll('tr') const ranges = [] for (let R = 0; R < rows.length; ++R) { const outRow = [] const row = rows[R] const columns = row.querySelectorAll('td') for (let C = 0; C < columns.length; ++C) { const cell = columns[C] const colspanStr = cell.getAttribute('colspan') const rowspanStr = cell.getAttribute('rowspan') let colspan let rowspan if (colspanStr) { colspan = parseInt(colspanStr) } if (rowspanStr) { rowspan = parseInt(rowspanStr) } const cellValue = cell.innerText // Skip ranges ranges.forEach(function (range) { if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) { for (let i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null) } }) // Handle Row Span if (rowspan || colspan) { rowspan = rowspan || 1 colspan = colspan || 1 ranges.push({ s: { r: R, c: outRow.length }, e: { r: R + rowspan - 1, c: outRow.length + colspan - 1 } }) } // Handle Value outRow.push(cellValue !== '' ? cellValue : null) // Handle Colspan if (colspan) { for (let k = 0; k < colspan - 1; ++k) { outRow.push(null) } } } out.push(outRow) } return [out, ranges] } const datenum = (date: Date) => { return (+date - +new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000) } const sheetFromDataArray = (data: any) => { const ws: { [key: string]: any } = {} const range = { s: { c: 10000000, r: 10000000 }, e: { c: 0, r: 0 } } for (let R = 0; R !== data.length; ++R) { for (let C = 0; C !== data[R].length; ++C) { if (range.s.r > R) range.s.r = R if (range.s.c > C) range.s.c = C if (range.e.r < R) range.e.r = R if (range.e.c < C) range.e.c = C const cell: CellInterface = { v: data[R][C], t: '', z: '' } if (cell.v == null) continue const cellRef = utils.encode_cell({ c: C, r: R }) if (typeof cell.v === 'number') cell.t = 'n' else if (typeof cell.v === 'boolean') cell.t = 'b' else if (cell.v instanceof Date) { cell.t = 'n' cell.z = SSF.get_table()[14] cell.v = datenum(cell.v) } else cell.t = 's' ws[cellRef] = cell } } if (range.s.c < 10000000) { ws['!ref'] = utils.encode_range(range) } return ws } const s2ab = (s: string) => { const buf = new ArrayBuffer(s.length) const view = new Uint8Array(buf) for (let i = 0; i !== s.length; ++i) { view[i] = s.charCodeAt(i) & 0xff } return buf } export const exportTable2Excel = (id: string) => { const theTable = document.getElementById(id) if (theTable) { const oo = generateArray(theTable) const ranges = oo[1] /* original data */ const data = oo[0] const wsName = 'SheetJS' const wb = new Workbook() const ws = sheetFromDataArray(data) /* add ranges to worksheet */ // ws['!cols'] = ['apple', 'banan'] ws['!merges'] = ranges /* add worksheet to workbook */ wb.SheetNames.push(wsName) wb.Sheets[wsName] = ws const wbout = write(wb, { bookType: 'xlsx', bookSST: false, type: 'binary' }) saveAs( new Blob([s2ab(wbout)], { type: 'application/octet-stream' }), 'test.xlsx' ) } } export const exportJson2Excel = ( header: string[], data: any, filename = 'excel-list', multiHeader: string[][] = [], merges: any[] = [], autoWidth = true, bookType = 'xlsx' ) => { data = [...data] data.unshift(header) for (let i = multiHeader.length - 1; i > -1; i--) { data.unshift(multiHeader[i]) } const wsName = 'SheetJS' const wb = new Workbook() const ws = sheetFromDataArray(data) if (merges.length > 0) { if (!ws['!merges']) { ws['!merges'] = [] } merges.forEach((item) => { ws['!merges'].push(utils.decode_range(item)) }) } if (autoWidth) { // 設定worksheet每列的最大寬度 const colWidth = data.map((row: any) => row.map((val: any) => { // 先判斷是否為 null/undefined if (val == null) { return { wch: 10 } // 再判斷是否為中文 } else if (val.toString().charCodeAt(0) > 255) { return { wch: val.toString().length * 2 } } else { return { wch: val.toString().length } } }) ) // 以第一行為初始值 const result = colWidth[0] for (let i = 1; i < colWidth.length; i++) { for (let j = 0; j < colWidth[i].length; j++) { if (result[j].wch < colWidth[i][j].wch) { result[j].wch = colWidth[i][j].wch } } } ws['!cols'] = result } // Add worksheet to workbook wb.SheetNames.push(wsName) wb.Sheets[wsName] = ws const wbout = write(wb, { bookType: bookType as any, bookSST: false, type: 'binary' }) saveAs( new Blob([s2ab(wbout)], { type: 'application/octet-stream' }), `${filename}.${bookType}` ) } const parseTime = (time?: object | string | number | null, cFormat?: string): string | null => { if (time === undefined || !time) { return null } const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}' let date: Date if (typeof time === 'object') { date = time as Date } else { if (typeof time === 'string') { if (/^[0-9]+$/.test(time)) { // support "1548221490638" time = parseInt(time) } else { // support safari // https://stackoverflow.com/questions/4310953/invalid-date-in-safari time = time.replace(new RegExp(/-/gm), '/') } } if (typeof time === 'number' && time.toString().length === 10) { time = time * 1000 } date = new Date(time) } const formatObj: { [key: string]: number } = { y: date.getFullYear(), m: date.getMonth() + 1, d: date.getDate(), h: date.getHours(), i: date.getMinutes(), s: date.getSeconds(), a: date.getDay() } const timeStr = format.replace(/{([ymdhisa])+}/g, (result, key) => { const value = formatObj[key] // Note: getDay() returns 0 on Sunday if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] } return value.toString().padStart(2, '0') }) return timeStr } export const formatJson = (filterKeys: any, jsonData: any) => jsonData.map((data: any) => filterKeys.map((key: string) => { if (key === 'timestamp') { return parseTime(data[key]) } else { return data[key] } }) ) // 在檔案中直接使用 import { formatJson, exportJson2Excel } from '@/utils/excel' const handleDownload = () => { const tHeader = ['table_name', 'entity_id', 'entity_name', 'type'] const filterVal = ['table_name', 'entity_id', 'entity_name', 'type'] const data = formatJson(filterVal, detectData.value.errors) exportJson2Excel(tHeader, data, '錯誤名單資料') }