downloadCVS.ts 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. // 1) 安全地把对象数组转成 CSV 文本
  2. export function toCSV<T extends Record<string, any>>(
  3. rows: T[],
  4. opts?: {
  5. headers?: string[] // 指定列顺序与表头;不传则用 rows[0] 的键
  6. delimiter?: string // 默认逗号 ","
  7. includeHeader?: boolean // 默认 true
  8. },
  9. ): string {
  10. const delimiter = opts?.delimiter ?? ','
  11. const includeHeader = opts?.includeHeader ?? true
  12. if (!rows?.length) return ''
  13. const headers = opts?.headers ?? Object.keys(rows[0])
  14. // 处理每个单元格:转为字符串、转义双引号、若含分隔符/换行/引号则整体再包一层引号
  15. const esc = (val: any) => {
  16. let s = val == null ? '' : String(val)
  17. s = s.replace(/"/g, '""')
  18. if (s.includes('"') || s.includes(delimiter) || s.includes('\n') || s.includes('\r')) {
  19. s = `"${s}"`
  20. }
  21. return s
  22. }
  23. const lines: string[] = []
  24. if (includeHeader) {
  25. lines.push(headers.map((h) => esc(h)).join(delimiter))
  26. }
  27. for (const row of rows) {
  28. lines.push(headers.map((h) => esc(row[h])).join(delimiter))
  29. }
  30. // Windows/Excel 友好:使用 \r\n 作为换行
  31. return lines.join('\r\n')
  32. }
  33. // 2) 触发浏览器下载(含 UTF-8 BOM,Excel 更稳)
  34. export function downloadCSV(filename: string, csv: string) {
  35. // 加 BOM,避免 Excel 中文乱码
  36. const BOM = '\uFEFF'
  37. const blob = new Blob([BOM + csv], { type: 'text/csv;charset=utf-8;' })
  38. const url = URL.createObjectURL(blob)
  39. const a = document.createElement('a')
  40. a.href = url
  41. a.download = filename.endsWith('.csv') ? filename : `${filename}.csv`
  42. document.body.appendChild(a)
  43. a.click()
  44. document.body.removeChild(a)
  45. URL.revokeObjectURL(url)
  46. }
  47. // 3) 使用示例
  48. // type Row = { id: number; name: string; note?: string };
  49. // const data: Row[] = [
  50. // { id: 1, name: "张三", note: "带,逗号" },
  51. // { id: 2, name: "李四", note: "含\"引号\"与换行\n第二行" },
  52. // ];
  53. // const csv = toCSV(data, {
  54. // headers: ["id", "name", "note"], // 指定列顺序(可选)
  55. // includeHeader: true,
  56. // });
  57. // downloadCSV("示例数据.csv", csv);