import * as XLSX from 'xlsx'; /** * Excel 导出的唯一实现。 * * 此前仓库里有 4 份各自拼装 workbook 的代码(assets 内联、mileage 内联、 * hydrogen 的两份 aoa/json helper),产物后缀与 sheet 名截断规则也不一致。 * 这里只收敛"组装与写出"这一层原语;各调用方仍自行决定列宽、冻结与数字格式, * 以免改变已验收的导出样式。 */ export type AoaRow = Array; /** 统一产物后缀:内部一律写出 .xlsx。 */ export function ensureXlsxFilename(name: string | undefined): string { const raw = String(name || 'export').trim() || 'export'; return `${raw.replace(/\.(csv|xls|xlsx)$/i, '')}.xlsx`; } /** 由二维数组构建 sheet。 */ export function buildAoaSheet(rows: AoaRow[]): XLSX.WorkSheet { return XLSX.utils.aoa_to_sheet(rows); } /** 由对象数组构建 sheet(首行为字段名)。 */ export function buildJsonSheet(rows: Record[]): XLSX.WorkSheet { return XLSX.utils.json_to_sheet(rows); } /** Excel 的 sheet 名上限是 31 字符,空名回退为 Sheet1。 */ function safeSheetName(name: string): string { return (name || 'Sheet1').slice(0, 31) || 'Sheet1'; } /** 写出一本工作簿。调用方可先给 sheet 设置 `!cols` / `!freeze` / 单元格格式。 */ export function writeWorkbook( sheets: Array<{ name: string; sheet: XLSX.WorkSheet }>, fileName: string, ): void { const workbook = XLSX.utils.book_new(); for (const { name, sheet } of sheets) { XLSX.utils.book_append_sheet(workbook, sheet, safeSheetName(name)); } XLSX.writeFile(workbook, ensureXlsxFilename(fileName)); } /** 便捷:单个二维数组 sheet 直接下载。 */ export function exportAoaSheet(rows: AoaRow[], fileName: string, sheetName = 'Sheet1'): void { writeWorkbook([{ name: sheetName, sheet: buildAoaSheet(rows) }], fileName); } /** 便捷:单个对象数组 sheet 直接下载。 */ export function exportJsonSheet( rows: Record[], fileName: string, sheetName = 'Sheet1', ): void { writeWorkbook([{ name: sheetName, sheet: buildJsonSheet(rows) }], fileName); }