import { h } from '@ui-arch/frame'; import type { RowModel } from '@ui-arch/table-core'; import { button } from './button.js'; import type { TableColumn } from './types.js'; /*** 单元格转义:含逗号 / 引号 / 换行时整体加引号,内部引号翻倍 ***/ function cell(v: unknown): string { const s = v == null ? '' : String(v); return /[",\r\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s; } /*** 导出 CSV:前置 BOM + CRLF 换行,Excel 打开中文不乱码 ***/ export function downloadCsv(name: string, header: string[], rows: unknown[][]): void { const text = '\ufeff' + [header, ...rows].map((r) => r.map(cell).join(',')).join('\r\n') + '\r\n'; const url = URL.createObjectURL(new Blob([text], { type: 'text/csv;charset=utf-8' })); const a = h('a', { href: url, download: name }) as HTMLAnchorElement; document.body.appendChild(a); // 挂进文档再点击:Safari 对游离锚点不触发下载 a.click(); a.remove(); // 立即摘除,不留残留节点 URL.revokeObjectURL(url); } /*** 导出按钮:取筛选 + 排序后的全量行(不分页),列序与表头一致、单元格走 format;列可由读取器惰性给出,跟随列显隐 ***/ export function exportButton( table: { getAllRows(): RowModel; getCellValue(row: T, key: string): unknown }, columns: TableColumn[] | (() => TableColumn[]), name: string, ): HTMLElement { const run = (): void => { const cols = typeof columns === 'function' ? columns() : columns; // 点导出时才取列:隐藏列变化后导出跟着变 const rows = table.getAllRows().rows; const header = cols.map((c) => String(c.header ?? c.key)); const body = rows.map((row) => cols.map((c) => { const raw = table.getCellValue(row, c.key); return c.format ? c.format(raw, row) : raw; // 与单元格显示同源,导出内容与看到的一致 })); downloadCsv(name, header, body); }; return button({ label: '导出 CSV', variant: 'ghost', onClick: run }); }