exportUtils.ts 2.03 KB
// REQ-USR-003: 前端零依赖 CSV 导出(UTF-8 BOM + Blob + <a download>,D-PLAN-1)
import type { UserVO } from '../../../api/types';

/** 导出列定义:中文表头 + 从 UserVO 取值(与列定义语义一致,作废 0/1→否/是;不含序号列) */
const EXPORT_COLUMNS: { header: string; pick: (row: UserVO) => string }[] = [
  { header: '用户名', pick: (r) => r.sUserName ?? '' },
  { header: '员工名', pick: (r) => r.employeeName ?? '' },
  { header: '用户号', pick: (r) => r.sUserNo ?? '' },
  { header: '部门', pick: (r) => r.departmentName ?? '' },
  { header: '用户类型', pick: (r) => r.sUserType ?? '' },
  { header: '语言', pick: (r) => r.sLanguage ?? '' },
  { header: '作废', pick: (r) => (r.iIsVoid === 1 ? '是' : '否') },
  { header: '登录日期', pick: (r) => r.tLastLoginDate ?? '' },
  { header: '制单人', pick: (r) => r.sCreator ?? '' },
  { header: '制单日期', pick: (r) => r.tCreateDate ?? '' },
];

/** CSV 单元转义:含逗号 / 引号 / 换行时用双引号包裹并转义内部引号 */
function escapeCell(value: string): string {
  if (/[",\n\r]/.test(value)) {
    return `"${value.replace(/"/g, '""')}"`;
  }
  return value;
}

/** 按列定义顺序与中文表头生成 CSV 文本(含表头行;空值→空串;作废 0/1→否/是) */
export function buildUserCsv(rows: UserVO[]): string {
  const header = EXPORT_COLUMNS.map((c) => escapeCell(c.header)).join(',');
  const body = rows.map((row) =>
    EXPORT_COLUMNS.map((c) => escapeCell(c.pick(row))).join(','),
  );
  return [header, ...body].join('\n');
}

/** 前置 UTF-8 BOM → Blob → createObjectURL → 触发 <a download> 下载 */
export function downloadCsv(filename: string, csv: string): void {
  const BOM = '';
  const blob = new Blob([BOM + csv], { type: 'text/csv;charset=utf-8;' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}