diff --git a/docs/entity_picker_standard.md b/docs/entity_picker_standard.md
new file mode 100644
index 00000000..eacbc87b
--- /dev/null
+++ b/docs/entity_picker_standard.md
@@ -0,0 +1,165 @@
+# GERP 实体选择器(EntityPicker)标准 — 设计文档
+
+> 适用范围:GERP 前端所有「选人 / 选部门 / 选职务 / 选应用实体」控件。
+> 目的:**一个数据源一份配置**,禁止"功能相同、两套代码"(历史上出过:同一份
+> 「部门员工关联」配置在后端写了两遍、批量职务弹窗又在前端自己分页拼了一次部门树)。
+
+---
+
+## 一、结论速览(先看这个)
+
+| 场景 | 用什么 | 配置写在哪 |
+|------|--------|-----------|
+| 表单里选单个实体(列 `type='entityPicker'`) | `EntityPicker.vue` | 后端列定义 `form_service.py`(`ENTITY_PICKER_MAP` 或就地 `entityPicker` 字段) |
+| 表单里选多个实体(列 `multiValue=true`,如职务) | `MultiEntityPicker.vue` | 同上 |
+| 需要"按部门树 → 成员"两级浏览面板 | `EntityPicker` + `config.treeGroup` | 复用 `DEPT_TREE_GROUP`(后端) |
+| 需要"部门员工关联"(成员 + 所属部门) | `DEPT_EMPLOYEE_PICKER` / `..._BY_USER_ROLE` | 后端 `form_service.py` 常量,勿复制 |
+| 页面自绘 `el-tree` 勾选(如批量调整职务) | `composables/useDeptEmployeeTree.ts` | 前端 composable(内部调 `DeptEmployeeSearch`) |
+| 页面里直接用 `EntityPicker` 组件(非列驱动,如菜单编辑器选公司) | `api/entityPickerPresets.ts` 的预设 | 前端预设,勿就地写 api/labelField/valueField |
+
+---
+
+## 二、分层与单一来源
+
+| 层 | 位置 | 职责 |
+|----|------|------|
+| 渲染层 | `GERP/src/components/EntityPicker.vue` | 搜索下拉 + 分页加载更多 + 浏览面板 + `treeGroup` 两级面板 + 多选暂存 |
+| 渲染层 | `GERP/src/components/MultiEntityPicker.vue` | 多值(标签 + 下拉 + 面板多选),支持 `treeGroup` |
+| 承载层 | `GERP/src/components/CrudFormPanel.vue` | 按列定义渲染表单控件(含 `required` 校验、级联参数透传) |
+| 承载层 | `GERP/src/components/CrudTable.vue` | 列表页新增/编辑;`picker-multi` → 批量新增 |
+| 配置层(**权威**) | 后端 `gvsds/apps/gerp/form_service.py` | `ENTITY_PICKER_MAP`(通用实体预设)、`DEPT_TREE_GROUP`、`DEPT_EMPLOYEE_PICKER`、`DEPT_EMPLOYEE_PICKER_BY_USER_ROLE` |
+| 配置层 | 前端 `GERP/src/api/entityPickerPresets.ts` | 页面直接使用组件时的 6 类实体预设 + `epColumn()` |
+| 自绘树数据源 | `GERP/src/composables/useDeptEmployeeTree.ts` | `el-tree` 场景的"部门树 → 成员"组装(同源 `DeptEmployeeSearch`) |
+| 后端数据源 | `gvsds/apps/gerp/views/form_api.py` | `DeptEmployeeSearchView` 等 Search 接口 |
+
+**为什么配置放后端**:列定义由 `/API/FormAPI/Columns/
` 下发,前端只负责渲染 ——
+同一张表在列表页、详情页、弹窗里形态天然一致;改一处即全站生效。
+
+---
+
+## 三、数据契约(Search 接口)
+
+### 3.1 响应结构
+
+```jsonc
+{ "code": 0, "data": {
+ "list": [ { /* 行 */ } ],
+ "has_more": true,
+ "tree": [ { "uuid": "…", "name": "…", "parentUuid": "…" } ] // 传 tree=1 时返回
+} }
+```
+
+- 分页:`offset`(步长 20,前端"加载更多"/自动拉全)
+- 关键字:`keyword`(同时匹配多个字段,见各接口 docstring)
+- 公司过滤:`CompanyUUID`(显式传入强制该公司;不传则回退当前角色公司;无公司上下文返回空列表)
+- 去重:`Dedup=UserRole`(`DeptEmployeeSearch` 专用:同一"用户-角色对"多部门只留一条)
+
+### 3.2 接口清单
+
+| 接口 | 值字段(valueField) | 备注 |
+|------|---------------------|------|
+| `/API/FormAPI/UserSearch` | `UserUUID` | 副文本 `UserEmail` |
+| `/API/FormAPI/RoleSearch` | `RoleUUID` | 支持 `dependsOn=UserUUID` 级联 |
+| `/API/FormAPI/CompanySearch` | `CompanyUUID` | |
+| `/API/FormAPI/TenantSearch` | `TenantUUID` | |
+| `/API/FormAPI/DepartmentSearch` | `DeptUUID` | |
+| `/API/FormAPI/WorkgroupSearch` | `WorkgroupUUID` | |
+| `/API/FormAPI/PermissionSearch` | `PermUUID` | 副文本 `PermCode` |
+| `/API/FormAPI/PositionTemplateSearch` | `PositionTemplateUUID` | |
+| `/API/FormAPI/PositionSearch` | `PositionUUID` | 带 `tree`(通用部门树),groupField `CategoryUUID` |
+| `/API/FormAPI/PositionCategorySearch` | `PositionCategoryUUID` | |
+| `/API/FormAPI/MenuSearch` | `MenuUUID` | |
+| `/API/FormAPI/ShopSearch` | `ShopUUID` | 本地 7 库无表,预留 |
+| `/API/FormAPI/UserRoleSearch` | `UserRoleUUID` | 用户-角色对;带 `tree`(部门树) |
+| `/API/FormAPI/DeptEmployeeSearch` | `UserRoleDepartmentUUID` / `UserRoleUUID` | **部门员工关联**(部门 + 用户-角色对);带 `tree`、`DeptEmployeeLabel`、`RoleName`;支持 `Dedup=UserRole`、`DeptUUID` 过滤 |
+
+---
+
+## 四、配置字段(`EntityPickerConfig`)
+
+定义见 `GERP/src/types/api.ts`。
+
+| 字段 | 说明 |
+|------|------|
+| `api` / `method` | 搜索接口(默认 GET) |
+| `labelField` / `valueField` | 显示字段 / **取值字段(必须等于落库列)** |
+| `secondaryField` | 副文本字段(如 `RoleName`、`UserEmail`) |
+| `placeholder` | 占位文案 |
+| `fetchOnOpen` / `paged` | 打开即加载前 20 条 / 底部"加载更多"(默认均开启) |
+| `searchParam` / `resultPath` | 关键字参数名(默认 `keyword`)/ 响应数据路径(默认 `data`) |
+| `dependsOn` | 级联:请求时附带表单中该字段的当前值(如 `RoleUUID` 依赖 `UserUUID`) |
+| `dependCompany` | 请求时附带表单 `CompanyUUID`(表单无该字段则不发送,后端回退当前角色公司) |
+| `treeGroup` | 浏览面板改为"树分组 + 成员"两级结构,见下 |
+
+### `treeGroup` 子字段
+
+| 子字段 | 说明 |
+|--------|------|
+| `groupField` | 记录上的归组字段(如 `DeptUUID`);配置后前端自动附带 `tree=1` |
+| `treeField` | 响应中树节点数组字段名(默认 `tree`) |
+| `ungroupedTitle` | 未归组记录的兜底分组标题(默认"未分组") |
+| `multiSelect` | 开启后浏览面板出现"多选"开关,成员进暂存区、底部"全部提交"批量返回(`submit-multi` → `CrudTable` 批量新增) |
+| `roleTypeOptions` | 暂存区成员类型下拉(如 成员/负责人),值随 `submit-multi` 返回写入 `RoleType` |
+
+---
+
+## 五、「部门员工关联」选择器(单一来源示例)
+
+后端常量(`gvsds/apps/gerp/form_service.py` 顶部,含完整注释):
+
+| 常量 | valueField | 附加行为 | 使用点 |
+|------|-----------|---------|--------|
+| `DEPT_TREE_GROUP` | —(公共 treeGroup) | 部门树分组 + 未分配部门兜底 | 下列两处 + `UserRoleDepartment.UserRoleUUID` |
+| `DEPT_EMPLOYEE_PICKER` | `UserRoleDepartmentUUID` | 单选,`secondaryField=RoleName` | `EmployeeProfile.UserRoleDepartmentUUID`(eHRM 员工档案) |
+| `DEPT_EMPLOYEE_PICKER_BY_USER_ROLE` | `UserRoleUUID` | `Dedup=UserRole` + 多选暂存 + 成员/负责人 | `UserRoleWorkgroup.UserRoleUUID`(工作组新增成员) |
+
+**取值与落库的对应关系**(改取值列必须同步后端解析):
+
+- `UserRoleUUID` → 直接落 `UserRoleWorkgroup.UserRoleUUID`(关系表只存用户-角色对,**不含部门**;部门仅用于选择器分组展示)
+- `UserRoleDepartmentUUID` → 由 `FormService._ResolveDeptEmployee()` 解析出
+ `UserUUID` / `DeptUUID` / `DeptName` 再落 `EmployeeProfile`
+
+**唯一性约束**:员工档案一人一份 —— 新增与改绑均由 `form_service.add_record/modify_record` 拦截
+(前端在已有档案时隐藏新增入口)。
+
+---
+
+## 六、`el-tree` 自绘场景
+
+批量调整职务弹窗(`components/BatchPositionDialog.vue`)需要"树 + 勾选",无法直接复用下拉形态的
+`EntityPicker`,因此抽 `composables/useDeptEmployeeTree.ts`:
+
+- 数据源与 `EntityPicker(treeGroup)` **同源**(`/API/FormAPI/DeptEmployeeSearch`)
+- 分页拉全 → 用服务端 `tree` 组装 `el-tree` 数据 → 成员按 `DeptUUID` 归组,未挂部门进"未分组"
+- 单部门模式(传 `DeptUUID`,部门详情页)返回单个分组节点
+- 返回 `{ tree, groupIds }`:`groupIds` 用于提交时过滤掉分组节点,只提交成员 PK
+- 成员叶子取值列由 `ValueField` 指定(公司/部门列表页 `UserRoleUUID`;部门详情页 `UserRoleDepartmentUUID`)
+
+对应提交表:`UserRole`(公司页)/ `UserRoleDepartment`(部门页)/ `UserRoleWorkgroup`(工作组页),
+统一走 `formApi.batchPositions()` → 后端 `FormService.batch_positions`。
+
+---
+
+## 七、约定(必须遵守)
+
+1. **禁止就地复制 `entityPicker` 配置**:能命中 `ENTITY_PICKER_MAP` 或上述部门常量就复用;
+ 确有新形态时,先把配置抽成常量(后端)或预设(前端),再在两个以上使用点引用。
+2. **`valueField` 必须等于落库列**;改取值列要同步后端解析/校验逻辑与唯一性约束。
+3. **"部门树分组"一律走 `DeptEmployeeSearch` + `DEPT_TREE_GROUP`**,不要另拉 `UserRoleSearch`
+ 在前端自己拼树(`UserRoleSearch` 仅用于"用户-角色对"场景)。
+4. **`el-tree` 自绘场景用 `useDeptEmployeeTree`**,不要自己写分页 + 拼树 + 标签组装。
+5. **前端预设 `entityPickerPresets.ts` 只在页面直接使用 `EntityPicker` 组件时使用**;
+ 列驱动场景一律由后端下发配置。
+6. 改动任何一处共享配置后,**先 grep 使用点做回归**,再以 `GERP\deploy.ps1`
+ (`vue-tsc -b` + `vite build`)与 `python manage.py check` 作为验收。
+
+---
+
+## 八、变更检查清单
+
+- [ ] 新配置是否复用了既有常量/预设?没有 → 是否应该抽成常量?
+- [ ] `valueField` 与落库列一致?后端解析/唯一性约束已同步?
+- [ ] `treeGroup` 是否用了公共 `DEPT_TREE_GROUP`?数据源是否 `DeptEmployeeSearch`?
+- [ ] 自绘树是否走了 `useDeptEmployeeTree`?
+- [ ] `grep -rn "DeptEmployeeSearch\|treeGroup\|entityPicker:" GERP/src gvsds/apps/gerp` 是否出现新的重复定义?
+- [ ] 前端 `deploy.ps1` 全绿;后端 `manage.py check` 0 issue;涉及 Python 改动记得重启服务