# 职位管理列表:名称快捷链接 + 关联人员展示 **日期:** 2026-08-05 **范围:** `web-admin` 职位管理页 + `GET /web/admin/posts` 列表 enrichment **状态:** 设计已确认,待实现 ## 背景 职位管理(`/#/system-manage/system/post`)已有查看弹窗(`SystemViewDialog`)与「关联用户」能力,但列表中: - 「职位名称」为纯文本,需经操作菜单才能查看; - 无法一眼看到该职位已关联的人员。 ## 目标 1. 列表「职位名称」改为快捷链接,点击打开已有【查看】弹窗。 2. 列表新增「关联人员」列,展示该职位全部关联用户姓名;超宽时换行,不省略。 ## 非目标 - 不改查看弹窗结构与加载逻辑(仍可按需拉绑定用户/角色)。 - 不改关联用户 / 角色配置弹窗交互(仅保存后刷新列表以同步人名)。 - Create/Update 请求体不写入关联人员字段。 ## 方案选择 采用 **列表接口批量 enrichment**: - `list_posts` 对本页职位一次性查询 `sys_user_post` + `sys_user`,在每行附加只读字段 `boundUserNames: string[]`。 - 避免前端对每行 N+1 请求 `/posts/{id}/users`。 姓名取值:优先 `nick_name`,为空则回退 `user_no`;过滤已删除用户(`del_flag=0`)。排序:与绑定表查询顺序一致即可(稳定、可预期)。 ## 后端设计 ### 响应字段 在职位列表项(及 `_row_to_dict` 用于列表场景)增加: | 字段 | 类型 | 说明 | |------|------|------| | `boundUserNames` | `string[]` | 关联用户显示名;无人则为 `[]` | 单条 create/update 成功返回可不强制带该字段;前端以列表刷新为准。 ### 实现要点 - 文件:`backend/app/crud/post.py`(或与 `user.py` 协作的批量查询函数)。 - 在 `list_posts` 取得本页 `SysPost` 行后,按 `post_id IN (...)` + `tenant_no` 批量查绑定与用户昵称,再写入各行 dict。 - 不信任客户端传入的 `boundUserNames`;写接口忽略该字段。 ## 前端设计 ### 页面 `web-admin/src/views/system/post/index.vue` 1. **职位名称列** - 模板:`el-button link type="primary"`,文案为 `row.postName`。 - `@click` → 已有 `handleView(row)`。 - 行双击查看保持不变。 2. **关联人员列** - 位置:职位编码与状态之间。 - 展示:`boundUserNames` 用顿号 `、` 拼接;空数组显示 `—`(`formatDisplayOrDash` 或等价)。 - 样式:单元格内文案 `white-space: normal`、`word-break: break-word`,全部显示、超宽换行;**不**使用 `show-overflow-tooltip` 省略。 3. **刷新** - `handlePostBindSubmit` 成功后调用 `loadData()`,保证人名与绑定一致。 ### 类型 `web-admin/src/types/api.d.ts` → `Api.Post.PostItem` 增加可选或必填: ```ts boundUserNames?: string[] ``` 列表接口保证返回数组时,前端按 `row.boundUserNames || []` 防御即可。 ## 验收标准 - 点击职位名称打开与操作菜单「查看」相同的详情弹窗。 - 列表显示该职位全部关联人员姓名,多人顿号分隔,列宽不足时换行可见全文。 - 无人关联时显示 `—`。 - 在「关联用户」中增删保存后,列表人名无需整页硬刷新即可更新(提交后 `loadData`)。 ## 风险与约束 - 单职位关联用户极多时行高变大:可接受(产品明确要求全部显示并换行)。 - 列表分页仍按职位分页;enrichment 仅对本页职位批量查询,性能可控。