Phase 2a: 用户详情页(资料卡片、游戏统计、答题历史、章节进度) Phase 2b: 反馈管理页面(类型/状态筛选、详情弹窗、状态变更) Phase 2c: 订阅等级管理(等级变更对话框、权益对比) Phase 2d: CSV 导出(用户/反馈/题目列表,Excel 兼容 BOM)
36 lines
1.1 KiB
TypeScript
36 lines
1.1 KiB
TypeScript
/**
|
||
* 将数据导出为 CSV 文件并触发浏览器下载。
|
||
*
|
||
* @param filename 下载文件名(如 "users.csv")
|
||
* @param columns 列定义,每项包含 key(数据字段名)和 label(表头)
|
||
* @param data 要导出的数据行
|
||
*/
|
||
export function exportToCsv<T extends Record<string, unknown>>(
|
||
filename: string,
|
||
columns: ReadonlyArray<{ key: keyof T & string; label: string }>,
|
||
data: ReadonlyArray<T>
|
||
): 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)
|
||
}
|