/** * 将数据导出为 CSV 文件并触发浏览器下载。 * * @param filename 下载文件名(如 "users.csv") * @param columns 列定义,每项包含 key(数据字段名)和 label(表头) * @param data 要导出的数据行 */ export function exportToCsv>( filename: string, columns: ReadonlyArray<{ key: keyof T & string; label: string }>, data: ReadonlyArray ): void { const escape = (value: unknown): string => { const str = value === null || value === undefined ? "" : String(value) if (str.includes(",") || str.includes('"') || str.includes("\n")) { return `"${str.replace(/"/g, '""')}"` } return str } const header = columns.map((col) => escape(col.label)).join(",") const rows = data.map((row) => columns.map((col) => escape(row[col.key])).join(",") ) const csv = [header, ...rows].join("\n") const blob = new Blob(["\uFEFF" + csv], { type: "text/csv;charset=utf-8" }) const url = URL.createObjectURL(blob) const link = document.createElement("a") link.href = url link.download = filename link.click() URL.revokeObjectURL(url) }