导出前必须确保使用前端筛选后的数据或后端重执行查询。需检查数据是否真实过滤、避免依赖UI组件原始数据源、用SheetJS导出精简后的数组、后端校验参数并流式响应、动态生成文件名、统一前后端时间与状态语义。
导出前先确认查询结果是否已真实加载
很多用户以为点击“筛选”后导出的就是当前视图数据,其实前端表格常做虚拟滚动或懒加载——export 按钮调用的可能是原始全量数据,而非你看到的那几十行。尤其在使用 ant design table、element plus eltable 或某些低代码平台时,这个坑特别隐蔽。
实操建议:
- 检查控制台是否有类似
data.length: 1000但页面只渲染50行的日志,说明数据未真正过滤到内存 - 优先在 JS 层拿到过滤后的数组再导出,而不是依赖 UI 组件的
getDataSource()或getData()方法(它们常返回原始源) - 若用
SQL 查询 + 后端导出,确保导出接口接收的是完整 WHERE 条件,而非仅传个前端分页参数
用 SheetJS(xlsx)导出筛选后数组最稳
浏览器端导出 CSV/Excel,xlsx 库比手拼 CSV 字符串靠谱得多——它自动处理逗号、换行、中文乱码、日期格式等。关键点在于:你得把筛选后的 data 数组交到它手上,而不是让库去读 DOM 表格节点。
实操建议:
- 别用
document.querySelector('table')抓 HTML 表格再转 Excel,DOM 结构和业务数据常不一致(比如隐藏列、格式化后的金额) - 导出前对字段做精简:
const exportData = filteredList.map(({ id, name, status, createdAt }) => ({ id, name, status, createdAt })) - 日期字段记得转为 JS Date 对象或 ISO 字符串,否则
xlsx可能导出成数字时间戳 - 示例核心调用:
await import('xlsx').then(xlsx => xlsx.writeFile(workbook, 'report.xlsx'))
后端导出要防条件绕过和超时
用户点“导出”时,前端传筛选参数给后端,但常见错误是:参数校验缺失、SQL 注入风险、没加 LIMIT 保护、导出大表卡死进程。更麻烦的是,有人会手动改请求里的 status=active 成 status=all,直接绕过前端筛选逻辑。
实操建议:
- 后端必须重新执行完整查询逻辑,不能信任前端传来的
data数组(它可能被篡改或截断) - 所有字符串参数做白名单校验,比如
status只允许['active', 'inactive', 'pending'] - 导出接口默认加
timeout: 60000,并用流式响应(res.write()分块写入),避免内存爆掉 - 敏感导出加权限判断,比如
canExportFullData权限位,不能仅靠角色名
导出文件名和格式别硬编码
用户需要知道这文件是谁、什么时候、按什么条件导出的,但很多人写死 data.xlsx,导致下载一堆同名文件后根本分不清。
实操建议:
- 文件名拼接规则建议:
`${reportType}_${filterStatus}_${Date.now().toString().slice(-6)}.xlsx` - 如果支持多种格式(CSV/Excel/PDF),别让用户自己猜区别——CSV 用逗号分隔、无样式、兼容性最好;Excel 支持多 sheet 和格式;PDF 适合归档但无法再编辑
- HTTP 响应头必须设对:
Content-Disposition: attachment; filename="xxx.xlsx",否则 Safari 可能不触发下载 - 导出按钮文案建议带条件提示,例如:“导出 24 条【状态=启用】数据”
真正容易被忽略的,是前后端对“筛选条件”的语义一致性——前端选了“最近7天”,后端却按 UTC 时间算;前端显示“已审核”,后端数据库字段却是 review_status = 2。这种错位不会报错,只会默默导出错的数据。

















