# P10-NS4-06:表格组件的统一规范 ## 简要结论 - 状态:⚠️ 部分解决 - tenant-admin 各页面表格在分页大小、loading 状态、筛选器位置上已形成事实一致的模式,但缺少显式的统一配置抽象层;排序交互完全缺失;空数据展示未统一;部分表格分页大小不一致(20 vs 10)。 ## 详细审查 ### 前端代码 #### 1. 分页大小 | 页面/组件 | 默认 pageSize | showSizeChanger | showTotal | 后端分页 | |-----------|:---:|:---:|:---:|:---:| | UserApproval | 20 | ✅ | ✅ `共 N 条` | ✅ page/page_size | | UserManagement | 20 | ✅ | ✅ `共 N 条` | ✅ page/page_size | | ExcelUpload — 上传记录 | 20 | ✅ | ✅ `共 N 条` | ✅ page/page_size | | ExcelUpload — 校验详情 | **10** | ❌ | ❌ | 前端分页 | | RetentionClues — 客户搜索 | `false` | — | — | 无分页(LIMIT 50) | | RetentionClues — 线索列表 | **10** | ❌ | ❌ | 无分页(全量返回) | | DiffTable — 冲突表 | `false` | — | — | 前端数据 | 结论:主列表页统一为 20 条/页 + showSizeChanger + showTotal,但子表格(校验详情、线索列表)使用 10 条/页且缺少 showSizeChanger 和 showTotal,**不一致**。 #### 2. 排序交互 所有 5 个表格组件均未配置 `sorter` 属性,前端不支持列排序。后端 SQL 统一使用 `ORDER BY created_at DESC`(固定倒序),无动态排序参数。 结论:**完全缺失**。用户无法按任意列排序。 #### 3. 筛选器位置 | 页面 | 筛选器位置 | 筛选方式 | |------|-----------|---------| | UserApproval | 表格上方独立区域 | Select(状态筛选) | | UserManagement | 表格上方独立区域 | Select(角色)+ Input.Search(关键词) | | ExcelUpload — 上传记录 | 无筛选 | — | | RetentionClues | Card title 区域 + Card extra 区域 | Input.Search + SiteSelector(上方);Select×2(Card extra 右侧) | 结论:筛选器统一放在表格上方(非表格内 column filter),**位置一致**。但 RetentionClues 的线索筛选器放在 Card extra 右侧,与其他页面的左对齐布局略有差异。 #### 4. loading 状态 所有带后端请求的表格均通过 `loading={loading}` 传入 Ant Design Table 的 loading 属性,**统一且正确**。 #### 5. 空数据展示 | 页面 | 空数据处理 | |------|-----------| | UserApproval | Ant Design Table 默认空状态(无自定义) | | UserManagement | Ant Design Table 默认空状态(无自定义) | | ExcelUpload | Ant Design Table 默认空状态(无自定义) | | RetentionClues — 客户搜索 | `` ✅ 自定义 | | RetentionClues — 线索列表 | Ant Design Table 默认空状态(无自定义) | | DiffTable | Ant Design Table 默认空状态(无自定义) | 结论:仅 RetentionClues 客户搜索有自定义空状态文案,其余均依赖 Ant Design 默认的英文 "No Data"。**未统一**,且未做中文化。 对比 admin-web:`TaskManager` 页面使用了 `locale={{ emptyText: }}` 自定义空状态,tenant-admin 未跟进。 #### 6. 统一配置抽象 - tenant-admin 中**不存在**统一的表格配置文件、常量定义或封装组件 - 每个页面独立定义 `pageSize`、`pagination` 配置、`handleTableChange` 等 - 分页响应结构 `{ items, total, page, pageSize }` 在前后端已统一,但属于隐式约定 ### 后端代码 #### 分页参数统一性 | 路由 | 参数名 | 默认值 | 范围约束 | |------|--------|--------|---------| | `GET /applications` | `page` + `page_size` | 1 / 20 | ge=1 / ge=1,le=100 | | `GET /users` | `page` + `page_size` | 1 / 20 | ge=1 / ge=1,le=100 | | `GET /excel/logs` | `page` + `page_size` | 1 / 20 | ge=1 / ge=1,le=100 | | `GET /customers/search` | 无分页 | — | LIMIT 50 硬编码 | | `GET /customers/{id}/clues` | 无分页 | — | 全量返回 | 结论:三个主列表接口的分页参数**完全统一**(`page`/`page_size`,默认 20,上限 100)。客户搜索和线索列表不分页,属于业务设计选择(数据量可控),但线索列表在数据量增长后可能需要补充分页。 响应格式统一为 `{ items: T[], total: int, page: int, pageSize: int }`,**一致**。 ### 差距分析 与 P10 标杆文件要求的差距: | 规范项 | P10 要求(推断) | 当前状态 | 差距 | |--------|-----------------|---------|------| | 默认分页大小 | 统一值(如 20) | 主表 20,子表 10 | 🟡 子表不一致 | | showSizeChanger | 所有分页表格启用 | 仅主表启用 | 🟡 子表缺失 | | showTotal | 所有分页表格显示 | 仅主表显示 | 🟡 子表缺失 | | 排序交互 | 至少关键列支持排序 | 完全缺失 | 🔴 缺失 | | 筛选器位置 | 统一在表格上方 | 基本一致 | ✅ | | loading 状态 | 统一 loading 属性 | 全部已配置 | ✅ | | 空数据展示 | 统一中文空状态 | 仅 1 处自定义,其余默认 | 🟡 未统一 | | 统一配置抽象 | 提取公共 Table 配置 | 不存在 | 🔴 缺失 | ### 建议 1. **提取统一表格配置常量**(优先级:高) 在 `apps/tenant-admin/src/constants/table.ts` 中定义: ```ts export const DEFAULT_PAGE_SIZE = 20; export const PAGE_SIZE_OPTIONS = ['10', '20', '50']; export const TABLE_LOCALE = { emptyText: '暂无数据', }; export const defaultPagination = (total: number, page: number, pageSize: number) => ({ current: page, pageSize, total, showSizeChanger: true, showTotal: (t: number) => `共 ${t} 条`, pageSizeOptions: PAGE_SIZE_OPTIONS, }); ``` 2. **统一空数据展示**(优先级:高) 通过 Ant Design ConfigProvider 全局设置中文 locale 和空状态: ```tsx import zhCN from 'antd/locale/zh_CN'; }> ``` 3. **子表格分页对齐**(优先级:中) ExcelUpload 校验详情和 RetentionClues 线索列表的 pageSize 改为 20,并补充 showSizeChanger 和 showTotal。 4. **排序交互**(优先级:低) 对时间列(申请时间、上传时间、记录时间)添加前端 `sorter` 支持。如数据量增长需后端排序,可在 API 中增加 `sort_by` + `sort_order` 参数。当前数据量下前端排序即可。 5. **线索列表补充分页**(优先级:低) `GET /customers/{member_id}/clues` 当前全量返回,建议在数据量可能超过 50 条时补充后端分页。