feat(报表分析): 新增签署总览等四个报表菜单并拆分演示数据开关
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* CSV 生成与浏览器下载。
|
||||
*
|
||||
* 抽成公共实现的直接原因:签署记录查询与报表分析都要「按当前条件导出全量」,
|
||||
* 两边各写一份 CSV 转义迟早会分叉 —— 一边处理了换行一边没处理,
|
||||
* 导出的文件里就会多出一行被 Excel 解析成错位的数据,而且很难一眼看出来。
|
||||
*
|
||||
* ⚠️ 前端导出是**临时手段**:导出行为、导出人、导出范围都该写入审计日志,
|
||||
* 浏览器里做不到。后端提供导出任务接口后,这里应整体替换为服务端导出。
|
||||
*/
|
||||
|
||||
export interface CsvColumn<T> {
|
||||
label: string
|
||||
value: (row: T) => string
|
||||
}
|
||||
|
||||
/** 含逗号、引号或换行的字段必须整体加引号,内部引号翻倍 */
|
||||
export function escapeCsvField(value: string): string {
|
||||
return /[",\n\r]/.test(value) ? `"${value.replace(/"/g, '""')}"` : value
|
||||
}
|
||||
|
||||
/** 文件名时间戳:`YYYYMMDDHHmm` */
|
||||
export function fileStamp(date = new Date()): string {
|
||||
const pad = (value: number) => String(value).padStart(2, '0')
|
||||
|
||||
return `${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}${pad(date.getHours())}${pad(date.getMinutes())}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发浏览器下载。
|
||||
*
|
||||
* 用 `setTimeout(revoke, 0)` 而不是立刻 revoke:立刻释放会让部分浏览器
|
||||
* 来不及读取 blob 就中断下载。
|
||||
*/
|
||||
export function saveBlob(blob: Blob, fileName: string): void {
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
|
||||
link.href = url
|
||||
link.download = fileName
|
||||
link.click()
|
||||
window.setTimeout(() => URL.revokeObjectURL(url), 0)
|
||||
}
|
||||
|
||||
/** 按列定义把行数据拼成 CSV 文本(含表头,行分隔用 CRLF) */
|
||||
export function toCsv<T>(columns: readonly CsvColumn<T>[], rows: readonly T[]): string {
|
||||
return [
|
||||
columns.map((column) => escapeCsvField(column.label)).join(','),
|
||||
...rows.map((row) => columns.map((column) => escapeCsvField(column.value(row))).join(',')),
|
||||
].join('\r\n')
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成并下载 CSV。
|
||||
*
|
||||
* 带 BOM:Excel 直接打开 UTF-8 CSV 时,没有 BOM 会把中文显示成乱码。
|
||||
*/
|
||||
export function downloadCsv<T>(
|
||||
columns: readonly CsvColumn<T>[],
|
||||
rows: readonly T[],
|
||||
fileName: string,
|
||||
): void {
|
||||
saveBlob(
|
||||
new Blob([`\uFEFF${toCsv(columns, rows)}`], { type: 'text/csv;charset=utf-8' }),
|
||||
fileName,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user