FE-03 用户列表与查询 — 实现规格(前端)
阶段:前端(frontend)。作用域限定
frontend/下的页面 / 组件 / 路由 / store / api / 样式。 SSoT 引用:需求卡片docs/01-需求清单/USR-用户管理/REQ-USR-003.md;原型prototype/erp.html(<section id="screen-userlist">区域:.toolbar/.filterbar/.table-shell .grid-table#user-table/.pager,布局与交互权威);API 契约docs/05-API接口契约.md§ REQ-USR-003(GET /api/usr/users);技术规范docs/04-技术规范.md§ 零(技术栈)/ § 2.1 目录约定 / § 2.3 接口通信 / § 2.4 状态与错误 / § 3.2 分页查询;Design Tokenssrc/styles/tokens.css;承载外壳 FE-02 规格docs/superpowers/specs/2026-06-01-FE-02.md(AppLayout+ 标签栈 +RequireAuth守卫);登录态/请求基建 FE-01 规格docs/superpowers/specs/2026-06-01-FE-01.md(authSlice+request.ts+ token 持久化键xly_erp_token)。 本规格只消费已锁定事实。查询条件解析、文本模糊/精确匹配、分页越界回退、密码与敏感字段过滤等业务逻辑全部在后端(见 REQ-USR-003 后端规格),前端只负责采集筛选条件、发起分页查询、依据响应渲染表格 / 分页 / 空态 / 错误态。本 FE 为只读查询,不产生任何写副作用。
1. 关联 REQ + 关联原型
| 维度 | 内容 |
|---|---|
| 业务功能 | FE-03 用户列表与查询(工具栏刷新/导出 + 筛选条件 + 用户表格 + 分页,对接 GET /api/usr/users) |
| 关联 REQ | REQ-USR-003 查询用户(主)。输入表 1:查询字段(下拉单选,默认「用户名」)/ 匹配方式(下拉单选,默认「包含」)/ 查询值(手工输入,空为全部);输出表 1:序号 / 用户名 / 员工名 / 用户号 / 部门 / 用户类型 / 语言 / 作废 / 登录日期 / 制单人 / 制单日期 |
| 关联原型 |
prototype/erp.html → <section id="screen-userlist">:.toolbar(刷新 / 新增 / 导出Excel / 设置齿轮)、.filterbar(下拉 + 文本框 + 搜索 / 清空)、.table-shell > table.grid-table#user-table(表头 + tbody#user-tbody)、.pager(统计文案 + 上/下页 + 当前页 + 每页条数下拉) |
| 路由 |
/usr/users(React Router v6,受保护区子路由,由 FE-02 AppLayout + RequireAuth 包裹)。从 FE-02 主页「常用操作 > 用户列表」或导航总览「用户管理 > 用户列表 ★」进入并在顶栏打开「用户列表」标签(标签栈逻辑属 FE-02) |
| 落地组件目录 | 页面 frontend/src/pages/usr/UserList/(index.tsx + 子组件 UserToolbar.tsx / UserFilterBar.tsx / UserTable.tsx);接口走 frontend/src/api/usrApi.ts(新增 listUsers 方法)+ frontend/src/api/request.ts(FE-01 已建);类型集中 frontend/src/api/types.ts(UserVO / PageResult<T> / UserListQuery);列表查询态就近用页面 hook(不进全局 store,见 § 8 决策 D6) |
原型
#screen-userlist为纯静态 HTML + 内联 demo 脚本:users数组直接渲染tbody、filterbar的下拉/输入无真实查询、.pager文案为写死 demo(「共37个单据 共37条记录」「10000 条/页」)、行dblclick调goTo('userdetail')切到用户单据屏、工具栏「新增」data-add-user切到新增单据。本规格按 React + AntD 5 复刻其布局与交互语义,但表格数据、筛选、分页改为真实对接GET /api/usr/users;「新增」「行双击进单据」为跳转到 FE-04 的导航动作(目标页内容属 FE-04,本页只发起navigate)。
2. 组件树(按区域分块,推导自 prototype DOM)
页面根 UserListPage(路由 /usr/users 挂载,渲染于 FE-02 AppLayout 的 <Outlet/> 内),纵向结构对应原型 #screen-userlist(工具栏 / 筛选栏 / 表格壳 / 分页栏):
UserListPage(页面容器,对应 #screen-userlist:纵向布局,内容区填充外壳 Outlet)
├── UserToolbar(对应 .toolbar:深色工具条,左侧动作按钮 + 右侧设置齿轮占位)
│ ├── BtnRefresh(对应 .toolbar 刷新:重新拉取当前查询条件 + 当前页,见 § 5 BR8)
│ ├── BtnAdd(对应 .tb-btn#btn-add[data-add-user]:跳转 FE-04 新增用户 `/usr/users/new`;本页仅 navigate)
│ ├── BtnExportExcel(对应 .toolbar「导出Excel」:导出当前查询结果,见 § 5 BR9 / § 8 决策 D5)
│ └── GearSetting(对应 .toolbar .gear「⚙」:列设置占位,无后端,见 § 8 决策 D7)
├── UserFilterBar(对应 .filterbar:查询条件行)
│ ├── ScopeSelect(对应首个 <select>「全部用户」:用户范围下拉,原型 demo 项,见 § 8 决策 D2 —— 默认不映射后端参数)
│ ├── QueryFieldSelect(对应第 2 个 <select>「用户名」:查询字段下拉单选,options=用户名/员工名/用户号/部门/用户类型/作废/登录日期/制单人,默认「用户名」→ 提交 queryField)
│ ├── MatchTypeSelect(对应第 3 个 <select>「包含」:匹配方式下拉单选,options=包含/不包含/等于,默认「包含」→ 提交 matchType)
│ ├── QueryValueInput(对应 .filterbar <input>:查询值文本框,空为查询全部 → 提交 queryValue)
│ ├── FilterMoreToggle(对应 .filterbar .down「▾」:更多条件占位,见 § 8 决策 D3)
│ ├── BtnSearch(对应 .filterbar .btn「搜索」:以当前条件回到第 1 页发起查询,见 § 5 BR7)
│ └── BtnClear(对应 .filterbar .btn.ghost「⊗ 清空」:重置筛选为默认值并回到第 1 页全量查询,见 § 5 BR10)
├── UserTable(对应 .table-shell > table.grid-table#user-table:可横向滚动用户表格)
│ ├── RadioColumn(对应 thead 首列空 + tbody .radio-cell .radio-dot:行选择单选标记,见 § 8 决策 D8)
│ ├── 列:序号 / 用户名 / 员工名 / 用户号 / 部门 / 用户类型 / 语言 / 作废 / 登录日期 / 制单人 / 制单日期(对应 REQ 输出表 1 + 原型 thead)
│ └── Row[](对应 tbody#user-tbody 各 <tr>;双击行 → 跳转 FE-04 修改单据 `/usr/users/:id`,见 § 5 BR12)
└── UserPager(对应 .pager:右对齐分页条)
├── PageSummary(对应「共 N 条记录」统计文案,来源 PageResult.total)
├── Pager(对应 ‹ / 当前页 / › :上一页 / 当前页 / 下一页)
└── PageSizeSelect(对应 .pager <select>「条/页」:每页条数下拉,见 § 8 决策 D4)
- 控件选型(依据
docs/04 § 零frontend.ui_lib = Ant Design 5.x):- 工具栏 → 自定义深色
div条 + AntDButton(type="text",白字图标按钮,复刻原型.tb-btn),「刷新」配ReloadOutlined、「新增」PlusCircleOutlined、「导出Excel」FileExcelOutlined、设置SettingOutlined。 - 筛选栏 → AntD
Select(查询字段 / 匹配方式 / 用户范围,单选)+Input(查询值,支持回车触发搜索onPressEnter)+Button(搜索type="primary"配SearchOutlined/ 清空type="default")。可用 AntDForm承载(layout="inline"),亦可受控本地态,取舍登记 § 8 D6。 - 用户表格 → AntD
Table(rowKey="id",columns对应输出列,服务端分页pagination受控;loading绑加载态;scroll={{ x }}复刻原型横向滚动white-space:nowrap;行onRow.onDoubleClick→ 跳 FE-04)。「作废」列用 AntDTag或只读Checkbox(iIsVoid0/1 → 否/是,复刻原型tbody复选框单元,本页只读不可勾,见 § 5 BR6)。 - 分页 → 用 AntD
Table内置pagination(受控current/pageSize/total/showSizeChanger/showTotal),复刻原型.pager「共 N 条记录 + 上/下页 + 当前页 + 每页条数」;不另造分页组件。 - 行选择标记 → AntD
Table的rowSelection={{ type:'radio' }}(单选,复刻原型.radio-dot),选择仅用于「双击/选中后进单据」语义,不参与查询(见 § 8 D8)。
- 工具栏 → 自定义深色
- 页面在受保护区内渲染(无 token 由 FE-02
RequireAuth拦截重定向,本页不重复守卫)。顶栏 / 标签栈属 FE-02,本页只提供内容区。
3. 页面状态机(≥5 态)
列表查询态以「首次加载 / 加载中 / 正常有数据 / 空结果 / 错误 / 导出中」表达;查询参数(queryField/matchType/queryValue/pageNum/pageSize)由页面 hook 持有,任一变更触发取数:
| 状态 | 触发时机 | UI 表现 |
|---|---|---|
idle/initialLoading(首次加载 / loading) |
页面挂载即以默认条件(queryField=用户名、matchType=包含、queryValue 空、pageNum=1、pageSize=见 § 8 D4)调 GET /api/usr/users(REQ 输入表「预加载=页面加载时」) |
表格区 AntD Table 置 loading(骨架 / Spin 遮罩),分页与工具栏可见但表体显加载占位;筛选栏可操作 |
loading(查询/翻页/刷新进行中) |
点击「搜索」/「清空」/「刷新」/ 切页 / 改每页条数后重新取数 |
Table loading=true,保留上次行直至新数据返回(避免闪烁);「搜索」「刷新」按钮置 loading/禁用防重复提交 |
success(正常有数据) |
接口返回 code=0 且 data.records 非空 |
渲染表格行(序号按当前页 (pageNum-1)*pageSize + index + 1 生成,见 § 5 BR1);分页显示真实 total/pageNum/pageSize;统计文案「共 {total} 条记录」 |
empty(无匹配结果) |
接口返回 code=0 且 data.records 为空数组(含「条件无匹配」与「全量为空」) |
Table 渲染 AntD Empty「暂无匹配的用户」占位,不报错(REQ 验收:无匹配返回空列表而非报错);分页 total=0,统计「共 0 条记录」 |
error(查询失败) |
接口返回非 0 code(40001/42201)或网络/超时/5xx |
表格区显错误占位(AntD Result/Empty + 「加载失败,点击重试」)+ message.error 文案(见 § 4);保留筛选条件可重试;被动 401 由 request.ts 拦截器统一跳 /login(docs/04 § 2.4,不在本页处理) |
exporting(导出 Excel 进行中) |
点击「导出Excel」 | 「导出Excel」按钮置 loading 并禁用;完成后 message.success("导出成功") / 失败 message.error("导出失败")(导出实现见 § 8 D5) |
状态以页面本地 hook(
useState/自定义useUserList)表达:{ list, total, loading, error, query:{queryField,matchType,queryValue,pageNum,pageSize}, exporting };不进全局store(列表数据为页面就近态,docs/04 § 2.2「跨页面共享的才进 store」,见 § 8 D6)。authResolving/unauthenticated由 FE-02 守卫承担,本页不重复建态。
4. 消费的后端端点(对齐 docs/05)
| 端点 | 方法 | 触发时机 | 请求参数 | 成功响应 | 失败处理 |
|---|---|---|---|---|---|
/api/usr/users |
GET | 页面挂载(默认条件)/ 点击搜索 / 清空 / 刷新 / 切页 / 改每页条数 | query:{ queryField?(用户名/员工名/用户号/部门/用户类型/作废/登录日期/制单人,默认用户名), matchType?(包含/不包含/等于,默认包含), queryValue?(空为全部), pageNum(默认1), pageSize(默认10,最大100) }(对齐 docs/05 § REQ-USR-003) |
Result<PageResult<UserVO>>,code=0;PageResult={ records:UserVO[], total, pageNum, pageSize };UserVO={ id, sUserName, 员工名, sUserNo, 部门, sUserType, sLanguage, iIsVoid, tLastLoginDate, sCreator, tCreateDate }(密码与敏感字段后端不返回) |
非 0 code 弹 message.error(见错误码表);表格回退到空/错误占位 + 重试 |
请求 / 响应约定(依据 docs/04 § 2.3 / § 2.4 / § 3.2):
- 统一走
frontend/src/api/request.ts的 Axios 实例(FE-01 已建:baseURL=/api,开发期经 Vite proxy 转发到后端http://localhost:5172,端口取config-vars.yaml backend.http_port=5172;请求拦截器注入Authorization: Bearer <token>,token 取 localStorage 键xly_erp_token)。 - 响应拦截器统一拆
Result:code=0取data;非 0code抛业务错误并弹message.error;401统一跳/login(docs/04 § 2.4)。本页方法集中在usrApi.ts的listUsers(query): Promise<PageResult<UserVO>>,页面不直接散用 axios(docs/04 § 2.3)。 - 分页对齐 docs/04 § 3.2:
pageNum/pageSize传后端,回显PageResult的total/pageNum/pageSize;文本条件模糊匹配、枚举/外键条件精确匹配、空条件全量分页均由后端裁决(前端不预判匹配语义,原样传matchType)。
错误码表(对齐 docs/05 § REQ-USR-003)
| code | 含义 | 前端文案 | 处理 |
|---|---|---|---|
0 |
成功 | —(渲染结果或空态) | 正常渲染 |
42201 |
分页参数非法(pageNum<1 或 pageSize 超上限 100) | 「分页参数有误,已重置为第 1 页」 |
message.warning + 前端把分页重置为合法值(pageNum=1,pageSize 收敛至 ≤100)后重查;正常情况下前端分页组件已约束,此为兜底 |
40001 |
查询参数校验失败 | 「查询条件有误,请检查后重试」 |
message.error + 保留条件不自动重查 |
| 网络/超时/5xx | 请求异常 | 「加载失败,请稍后重试」 | 响应拦截器兜底 message.error + 表格错误占位「点击重试」 |
401 |
登录失效(被动) | 「登录已失效,请重新登录」 | 由 request.ts 拦截器统一跳 /login(docs/04 § 2.4),本页不单独处理 |
5. 业务规则前端复刻清单(逐条)
| # | 规则 | 触发时机 | 前端报错 / 反馈文案 | 来源 |
|---|---|---|---|---|
| BR1 | 「序号」列为系统生成的连续行号,按当前分页计算 (pageNum-1)*pageSize + 行内索引 + 1
|
每次渲染表格行 | —(纯展示,无报错) | REQ-USR-003 输出表 1「序号 = 系统生成」;原型 renderUsers 用 i+1
|
| BR2 | 查询字段默认「用户名」、匹配方式默认「包含」、查询值默认空 | 页面挂载初始化筛选栏 | —(默认值预填,对应原型下拉默认选中项) | REQ-USR-003 输入表 1「默认值=用户名 / 包含」「预加载=页面加载时」 |
| BR3 | 查询值为空时按「选择全部」处理(不传或传空 queryValue,返回全量分页) |
搜索 / 清空 / 默认加载时 queryValue 为空 | —(无报错,全量分页) | REQ-USR-003 输入表 1「查询值…空为选择全部」+ docs/04 § 3.2「空条件返回全量分页」 |
| BR4 | 查询字段与匹配方式取值受限于固定枚举(字段:用户名/员工名/用户号/部门/用户类型/作废/登录日期/制单人;匹配:包含/不包含/等于) | 渲染下拉 options | —(Select 仅提供合法选项,无自由输入) |
REQ-USR-003 输入表 1「显示来源」枚举列表 |
| BR5 | 查询为只读,不产生写副作用 | 任何查询 / 翻页 / 刷新 | —(仅 GET,不改任何数据) | REQ-USR-003 边界「查询为只读,不产生写副作用」 |
| BR6 | 「作废」列只读展示 iIsVoid(0=否 / 1=是),不可在列表中编辑勾选 |
渲染「作废」单元 | —(只读 Tag/禁用 Checkbox,点击无效) |
REQ-USR-003 输出表 1「作废=布尔」;原型 tbody 复选框为 demo 展示(本页只读) |
| BR7 | 点击「搜索」以当前筛选条件回到第 1 页发起查询 | 点击「搜索」或查询值框回车 | —(成功渲染 / 失败按 § 4 文案) | 原型 .filterbar .btn「搜索」;docs/04 § 3.2 分页查询 |
| BR8 | 点击「刷新」以当前已生效的查询条件 + 当前页重新取数(不重置条件,不回第 1 页) | 点击工具栏「刷新」 | —(刷新成功 / 失败按 § 4) | 原型 .toolbar 刷新图标语义(重载当前视图) |
| BR9 | 「导出Excel」导出当前查询条件命中的结果(非仅当前页),导出过程禁用按钮 | 点击「导出Excel」 | 成功 message.success("导出成功") / 失败 message.error("导出失败")(实现见 § 8 D5) |
原型 .toolbar「导出Excel」 |
| BR10 | 点击「清空」重置查询字段=用户名、匹配方式=包含、查询值=空、范围=全部,回到第 1 页全量查询 | 点击「清空」 | —(重置后自动重查全量) | 原型 .filterbar .btn.ghost「⊗ 清空」 |
| BR11 | 分页参数变更(切页 / 改每页条数)即重新取数;改每页条数回到第 1 页 | 点击上/下页、选每页条数 | —(成功渲染) | docs/04 § 3.2 分页;AntD Table 受控分页惯例 |
| BR12 | 双击表格行 → 跳转 FE-04 修改用户单据(/usr/users/:id,携该行 id) |
双击任一数据行 | —(navigate('/usr/users/'+row.id),目标内容属 FE-04) |
原型 tr.addEventListener('dblclick', ()=>goTo('userdetail'))
|
| BR13 | 「新增」→ 跳转 FE-04 新增用户单据(/usr/users/new) |
点击工具栏「新增」 | —(navigate('/usr/users/new'),目标内容属 FE-04) |
原型 .tb-btn#btn-add[data-add-user] → setUserDetailMode('new') + openTab('userdetail')
|
| BR14 | 无匹配结果时展示空态而非错误 | 接口返回空 records
|
AntD Empty「暂无匹配的用户」(无 message.error) |
REQ-USR-003 验收「无匹配时返回空列表而非报错」 |
| BR15 | 分页越界由后端回退到最后一页,前端按响应回显实际 pageNum
|
请求的 pageNum 超出总页数 |
—(前端以响应 PageResult.pageNum 同步分页当前页,不报错) |
REQ-USR-003 验收「分页参数越界时返回最后一页」(前端信任后端回显) |
本页只做条件采集 + 分页查询 + 只读展示 + 导航跳转;查询匹配语义、敏感字段过滤、越界回退等真伪裁决全部由后端在
GET /api/usr/users内完成,前端不复制后端逻辑、不预判结果、不杜撰端点。
6. 列定义与字段映射(对齐 REQ 输出表 1 + UserVO)
| 列(中文表头,对应原型 thead / REQ 输出表 1) | 数据字段(UserVO) | 渲染说明 |
|---|---|---|
| 序号 | —(前端生成) |
(pageNum-1)*pageSize + index + 1(BR1) |
| 用户名 | sUserName |
文本,可作为「双击进单据」主标识 |
| 员工名 |
员工名(来自职员表关联) |
文本,可空(原型有空值行如 admin/李丹) |
| 用户号 | sUserNo |
文本,可空 |
| 部门 |
部门(来自职员表关联) |
文本,可空 |
| 用户类型 | sUserType |
文本(普通用户 / 超级管理员,已为中文) |
| 语言 | sLanguage |
文本(中文 / 英文 / 繁体) |
| 作废 | iIsVoid |
只读布尔:0→否、1→是(BR6,Tag/禁用 Checkbox) |
| 登录日期 | tLastLoginDate |
日期时间文本,可空(原型有空值,如 lzj 行) |
| 制单人 | sCreator |
文本 |
| 制单日期 | tCreateDate |
日期时间文本 |
列顺序与表头文案以原型
#user-tablethead + REQ 输出表 1 为准(原型表头「作」为「作废」的窄列简写,本规格列名用「作废」全称)。员工名/部门在 docs/05UserVO中以中文键名给出(后端关联职员表派生字段),前端类型按响应实际键名映射(见 § 8 D9)。
7. Design Tokens 引用清单(src/styles/tokens.css,仅 var(--color-*))
约束:组件样式只用
var(--color-*),禁止硬编码 hex/rgba;色值冲突时tokens.css优先于prototype/(原型内联:root变量为 demo 私有,不作色值 SSoT)。AntD 主题色经 FE-02/FE-01 已配置的ConfigProvider对齐--color-primary。
| 用途 | Token | 备注 |
|---|---|---|
| 主操作(搜索按钮 / 当前页码高亮 / 链接强调) | var(--color-primary) |
对应原型 .filterbar .btn / .pager .pgcur 蓝;同时作为 AntD colorPrimary
|
| 页面 / 内容区基础背景 | var(--color-bg-base) |
内容区浅灰底(外壳 Outlet 内) |
| 筛选栏 / 表格 / 分页栏背景(白) | var(--color-form-bg-edit) |
对应原型 .filterbar / .grid-table / .pager 白底(原型 --panel #fff) |
| 表头背景 | var(--color-table-header-bg) |
对应原型 .grid-table thead th 表头底(原型 --header-bg) |
| 表头文字 | var(--color-table-header-fg) |
列头文字 |
| 表格行文字 | var(--color-table-row-fg) |
用户名 / 部门等单元文字 |
| 行 hover 背景 | var(--color-table-row-bg-hover) |
行悬停高亮(对应原型 .grid-table tbody tr:hover 浅蓝,映射到行 hover token) |
| 选中行背景 | var(--color-table-row-bg-selected) |
单选行选中高亮(rowSelection 选中行,对应原型 .radio-dot 选中语义) |
| 表单输入框背景(查询值框 / 下拉) | var(--color-form-bg-edit) |
筛选栏 Input/Select 可编辑底(原型 --field-bg/白底映射到表单可编辑 token) |
| 下拉项 hover 背景 | var(--color-form-bg-hover) |
Select 选项悬停(tokens 注「仅下拉框使用」) |
| 通用文字 / 统计文案 | var(--color-text) |
分页「共 N 条记录」、列内容文本 |
| 次要文字 / 占位 / 空态提示 | var(--color-text-secondary) |
空态「暂无匹配的用户」、占位图标(齿轮 / 更多) |
| 边框 / 分隔线 / 表格网格线 | var(--color-border) |
筛选栏下边线、表格单元边框、分页栏上边线(原型 --border) |
| 错误 / 失败提示 | var(--color-error) |
message.error(查询失败 / 导出失败)、错误占位强调(原型 --danger) |
| 警告提示 | var(--color-warning) |
message.warning(分页参数越界兜底 42201) |
| 成功提示 | var(--color-success) |
message.success("导出成功") |
顶栏
.toolbar的深色底(原型--toolbar-bg:#2c2f36)在tokens.css中无对应语义 token(与 FE-02 顶栏深色处理一致);本规格将用户列表工具条深色底作为页面局部装饰样式保留在UserList的 scoped 样式里(不新增全局 token、不挪用语义 token,与 FE-02 § 8 D9 一致,见 § 8 决策 D10)。该深色仅承载工具条容器视觉,不承载状态语义,不违反「语义色只用 token」约束。
8. 自主决策记录(decisions)
| # | 问题 | 选择 | 依据 | 置信度 |
|---|---|---|---|---|
| D1 | 列表数据 / 筛选 / 分页是真实对接后端还是沿用原型静态 users 数组 |
真实对接 GET /api/usr/users,原型 users demo 数组仅作列结构参照,不写入前端 |
docs/05 § REQ-USR-003 明确定义该分页查询端点;REQ-USR-003 为真实查询功能;原型静态数据是 demo,不应固化到生产代码 | high |
| D2 | 筛选栏首个下拉「全部用户」(原型 demo 项)映射到哪个后端参数 | 作为「用户范围」前端筛选项,默认「全部用户」时不向后端传额外参数(仅 queryField/matchType/queryValue + 分页参与查询);保留控件位以复刻原型布局 | docs/05 § REQ-USR-003 请求参数仅含 queryField/matchType/queryValue/pageNum/pageSize,无「范围」参数;原型「全部用户」为单项 demo(仅一个 <option>),无其他范围语义可锁;不杜撰后端不存在的参数 |
high |
| D3 | 筛选栏「▾」更多条件按钮(原型 .filterbar .down)行为 |
视觉占位(高级筛选预留),点击不触发额外后端参数;MVP 仅复刻表 1 的 3 个查询输入 | REQ-USR-003 输入表 1 仅定义查询字段/匹配方式/查询值 3 项;原型 .down 无对应交互脚本(纯 demo);占位化是最小且不杜撰功能的处理 |
medium |
| D4 | 每页条数 pageSize 默认值与可选项(原型写死「10000 条/页」) |
默认 pageSize=10(对齐 docs/05 默认),showSizeChanger 可选 [10,20,50,100](上限对齐 docs/05 最大 100);不采用原型 demo 的 10000 |
docs/05 § REQ-USR-003「pageSize 默认10,最大100」+ REQ 边界「单页最大条数受限(默认100)」;原型 10000 为 demo 写死值,超出后端上限 100,不可沿用 | high |
| D5 | 「导出Excel」如何实现(docs/05 无导出端点) | MVP 阶段前端导出:拉取当前查询条件命中的结果(按 total 在上限内一次或分批取),用前端库(如 xlsx/SheetJS)生成 .xlsx 下载;不杜撰后端导出端点 |
docs/05 接口清单无 /export 端点,不得编造后端端点(硬约束);原型「导出Excel」为工具栏动作但无后端脚本;前端导出当前结果集是不依赖新端点的可行实现;若结果量超单次查询上限,按分页循环取(受 pageSize≤100 约束)。实现期如后端补导出端点可切换 |
medium |
| D6 | 列表查询态放页面本地 hook 还是全局 store | 页面本地 hook(useUserList,含 list/total/loading/error/query),不进全局 store
|
docs/04 § 2.2「服务端数据优先就近在页面用 hooks 拉取,跨页面共享的才进 store」;列表数据仅本页用,无跨页共享需求 | high |
| D7 | 工具栏设置齿轮「⚙」(原型 .gear)行为 |
视觉占位(列显隐/偏好设置预留),无后端动作 | 原型 .gear 无交互脚本(demo);docs/05 无对应端点;占位化最小侵入 |
medium |
| D8 | 表格行选择标记(原型 .radio-dot 单选圆点)是否参与查询 |
用 AntD rowSelection={{type:'radio'}} 复刻单选语义,仅用于「选中行 → 可进单据」交互,不参与查询条件、不向后端传选中项 |
原型行选择为单选圆点(无多选/批量动作脚本);REQ-USR-003 查询参数无「选中行」语义;选择仅服务于进入 FE-04 单据,不污染查询 | medium |
| D9 |
UserVO 中 员工名/部门 为中文键名,前端类型如何映射 |
前端 UserVO 类型按 docs/05 响应实际键名定义(员工名/部门 作为对象键,或在 api 层做一次到 employeeName/departmentName 的别名映射后供组件用);列渲染以 docs/05 给定键名为准 |
docs/05 § REQ-USR-003 UserVO 显式列出中文键名(后端关联职员表派生);前端不擅自改后端响应键名,按契约消费;是否做内部别名属实现细节,列渲染语义不变 |
medium |
| D10 | 工具栏深色底色值来源(tokens.css 无对应工具条深色 token) | 作为页面局部装饰样式保留在 UserList scoped 样式,不新增全局 token、不挪用语义 token;语义色(主操作/文字/边框/错误等)严格走 token |
tokens.css 仅定义语义/状态色,无工具条品牌深色 token;深色底纯装饰无状态语义,局部化最小侵入;与 FE-02 § 8 D9 处理一致 | medium |
本规格不含后端实现细节(查询解析 / 匹配语义 / 敏感字段过滤 / 越界回退 / 数据访问 / 库表迁移等均不在前端作用域),亦不杜撰任何后端端点(导出按 D5 前端实现);登录态 / 请求基建 / 路由守卫复用 FE-01 + FE-02 已落地资产,所有查询真伪由后端在
GET /api/usr/users调用时裁决。