Capture backend export infrastructure, ListExportDropdown UX, and per-resource /export conventions for filtered and selected rows. Co-authored-by: Cursor <cursoragent@cursor.com>
6.3 KiB
6.3 KiB
web-admin 列表统一后端导出
日期: 2026-08-21
状态: 已确认
范围: web-admin 所有带筛选/分页的业务列表(系统管理、物流、报销、CRM、地磅等);小程序不在本期。
背景
多数列表页缺少导出;少数页(CRM 等)已有后端 Excel 导出,Demo 商品等则用前端 ArtExcelExport 分页拉全量再生成。筛选结果达数万行时,前端全量拉取不合理。需要统一为后端生成 xlsx、前端下载,并在工具栏提供一致交互。
决策摘要
| 项 | 结论 |
|---|---|
| 覆盖范围 | 所有带筛选/分页的业务列表;已有导出逻辑保留,仅对齐交互 |
| 实现方式 | 后端导出(方案 2:共享基建 + 各页注册) |
| 导出列 | 与当前列表可见列一致;中文表头;状态等为文案非 code |
| 主操作 | 导出筛选结果 |
| 次操作 | 导出已选(仅有多选勾选的列表;下拉菜单) |
| 无多选列表 | 仅单个「导出」按钮(等同导出筛选结果) |
| 大数据 | 同步流式下载;硬上限 50000 行 |
| 非目标 | 异步任务、导出历史、邮件、导入、小程序 |
架构
后端
- 复用并扩展
backend/app/utils/excel_export.py(ExportColumn、DictLabelResolver、export_rows_to_xlsx_response)。 - 各资源仍暴露独立
POST …/export(已有 GET 的保持兼容);不做全局万能导出 API。 - 共享约定:
- 查询复用对应列表(租户隔离、数据权限、软删、筛选条件一致)
- 可选
ids:有则只导出这些行(仍叠加权限过滤) - 可选
columns:前端传入当前可见列prop;缺省用服务端默认列(对齐列表默认可见列) - 硬上限 50000;0 条返回业务错误,不生成空文件
- 二进制响应路径须在
API_ENCRYPT_EXCLUDE_PATHS中(与现有 CRM 导出一致)。
前端
- 新组件
ListExportDropdown(中性btn-ghost+Download图标):hasSelection=true:下拉——主按钮「导出筛选结果」,菜单项「导出已选」(无选中则禁用)hasSelection=false:单按钮「导出」
- 位置:列表工具栏主操作之后、
ListToolbarEndActions之前。 - 统一 blob 下载工具(解析
Content-Disposition、loading、错误提示)。 - 有列显隐时,请求携带当前
visibleColumns。
数据流
- 导出筛选结果:当前筛选参数 + 可选
columns,不传ids→ 后端查全量筛选结果 → xlsx → 下载。 - 导出已选:同上 +
ids: selectedRowIds;未选中时前端禁用,不发请求。
[工具栏 ListExportDropdown]
│ filterParams + columns? + ids?
▼
[POST /api/.../export] ──同列表权限──► [查询行] ──ExportColumn──► [openpyxl] ──► StreamingResponse
▲ │
└──────────────────────── blob download + Message ──────────────────────────────┘
接口约定
请求
{
// 与对应列表相同的筛选字段
ids?: string[] // 导出已选
columns?: string[] // 当前可见列 prop;省略则服务端默认列
}
响应
- 成功:xlsx 流;
Content-Disposition含中文文件名(沿用workbook_to_streaming_response)。 - 失败(建议):
| 场景 | 处理 |
|---|---|
| 0 条可导出 | 400「没有可导出的数据」 |
| 超过 50000 | 400「导出超过 50000 条,请缩小筛选范围」 |
| 无权限 | 与列表一致 401/403 |
columns 含未知 prop |
忽略未知项;仅导出已登记字段 |
ids 权限过滤后为空 |
同 0 条 |
列定义
- 使用
ExportColumn(title, field, dict_type?, formatter?)。 - 树形路径等展示规则与列表/规范一致(全路径、
/分隔)。 - 「可见列」:有
usePersistedListColumns时跟当前勾选;否则跟页面表格列定义(排除 selection/index/操作列)。
已有导出
- 已有后端导出(CRM 等):保留既有列与查询;按需补
ids/columns;前端改为ListExportDropdown(或等价交互)。 - 现有前端
ArtExcelExport页(如 Demo 商品):批处理时迁为后端/export+ListExportDropdown,不再用浏览器拉全量生成。
前端组件草案
ListExportDropdown props
| Prop | 说明 |
|---|---|
exportFn |
(payload) => Promise<Blob> |
getFilterParams |
() => Record<string, any> |
selectedIds |
已选 id;无多选可不传 |
hasSelection |
是否支持多选 UI |
visibleColumns |
可选可见列 |
filename |
兜底文件名(优先响应头) |
disabled / loading |
外部控制 |
页面接入:声明导出 API、列与表格对齐、挂组件;有勾选则传 selectedIds。
落地节奏
- 基建:后端上限/空数据约定文档化进工具函数;前端组件 + 下载工具。
- 样板页:选一标准列表(如
system/user或demo/unit)端到端打通。 - 批处理:按模块为缺导出页补
/export+ 挂组件;已有导出页对齐下拉。 - 回归:筛选一致性、可见列、已选、空数据、超限、加密排除路径。
权限与安全
- 导出 SHALL 与列表同一鉴权、租户、数据权限;MUST NOT 因导出绕过范围。
idsMUST 仍叠加租户 + 数据权限,禁止仅凭 id 跨范围导出。columns仅允许该资源已登记 field。
验收标准
- 目标列表工具栏均有导出入口(中性按钮 + 图标)。
- 有多选:下拉含「导出筛选结果」「导出已选」;未选中时后者禁用。
- 无多选:仅「导出」单按钮。
- 导出结果与当前筛选一致;可见列与 Excel 列一致;状态为中文文案。
- 0 条 / 超 50000 有明确提示且不下载损坏文件。
- 已有导出页交互对齐,业务列含义不回退。
- 导出路径不受 API 加密中间件破坏二进制。
明确不做
- 前端拉全量再生成 Excel(新接入页)
- 异步导出任务、导出历史、邮件通知
- 自定义列模板编辑器、导入
- 小程序端导出