From be8c526a454aa9081000d15f26bebdef8c183aea Mon Sep 17 00:00:00 2001
From: wsj <593216709@qq.com>
Date: Mon, 26 Oct 2026 23:34:23 +0800
Subject: [PATCH] feat: 提交

---
 src/pages/system/roles/index.tsx                     |    1 
 src/pages/system/users/components/UserFormDrawer.tsx |  211 +++++
 src/api/api-tenant.md                                | 2013 +++++++++++++-----------------------------------
 src/pages/system/users/types.ts                      |    2 
 src/pages/system/users/index.tsx                     |  232 ----
 5 files changed, 799 insertions(+), 1,660 deletions(-)

diff --git a/src/api/api-tenant.md b/src/api/api-tenant.md
index 95909ce..afdb31c 100644
--- a/src/api/api-tenant.md
+++ b/src/api/api-tenant.md
@@ -1,1502 +1,599 @@
-# SaaS 系统管理服务 API(租户版)
+# SaaS 系统管理服务 API(租户管理服务)
 
 > **版本**: 1.0.0  
-> **描述**: 多租户 SaaS 平台 — 用户、角色、权限、租户管理接口  
-> **License**: Apache 2.0  
-> **Contact**: SaaS Platform Team
+> **OpenAPI**: 3.0.1  
+> **Base Path**: `/`  
+> **认证方式**: Bearer JWT  
+> **许可证**: Apache 2.0  
+
+**描述**: 多租户 SaaS 平台 — 用户、角色、权限、租户管理接口
+
+**Swagger 源地址**:  
+http://121.41.54.56:9090/webjars/swagger-ui/index.html?urls.primaryName=%E7%A7%9F%E6%88%B7%E7%AE%A1%E7%90%86%E6%9C%8D%E5%8A%A1
+
+**OpenAPI JSON**:  
+http://121.41.54.56:9090/tenant/v3/api-docs
 
 ---
 
-## 基本信息
+## 通用说明
 
-| 项目 | 说明 |
-|------|------|
-| Base URL | `http://121.41.54.56:9090` |
-| OpenAPI | 3.0.1 |
-| 认证方式 | Bearer JWT(Header: `Authorization: Bearer <token>`) |
-| 文档源 | `/tenant/v3/api-docs` |
-| Swagger UI | http://121.41.54.56:9090/webjars/swagger-ui/index.html?urls.primaryName=%E7%A7%9F%E6%88%B7%E7%AE%A1%E7%90%86%E6%9C%8D%E5%8A%A1 |
+### 认证
 
----
+所有接口默认需要在请求头中携带 JWT Token:
 
-## 通用响应结构
-
-几乎所有接口返回统一包装:
-
-```json
-{
-  "code": 0,
-  "message": "success",
-  "data": { ... },
-  "timestamp": 1690000000000,
-  "success": true
-}
+```http
+Authorization: Bearer <your-jwt-token>
 ```
+
+### 统一响应结构
+
+绝大多数接口返回统一的 `Result` 封装:
 
 | 字段 | 类型 | 说明 |
 |------|------|------|
-| `code` | integer | 业务状态码,通常 `0` 表示成功 |
+| `code` | integer | 业务状态码 |
 | `message` | string | 提示信息 |
-| `data` | object | 业务数据(见各接口返回模型) |
-| `timestamp` | long | 毫秒时间戳 |
+| `data` | object / array / null | 业务数据 |
+| `timestamp` | long | 时间戳 |
 | `success` | boolean | 是否成功 |
+
+### 分页结构 `PageResult`
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| `page` | long | 当前页 |
+| `size` | long | 每页条数 |
+| `total` | long | 总记录数 |
+| `totalPages` | long | 总页数 |
+| `records` | array | 数据列表 |
+
+### 通用错误码
+
+| HTTP Status | 说明 |
+|-------------|------|
+| 200 | 成功 |
+| 400 | 请求参数错误 |
+| 405 | 方法不允许 |
+| 500 | 服务器内部错误 |
 
 ---
 
 ## 接口列表
 
-### 租户角色管理
+### 1. 员工管理
 
-#### `GET` `/tenant/roles`
+#### 1.1 员工分页列表
 
-**当前租户角色列表(含预设角色)**
+```http
+GET /tenant/employees
+```
 
-- OperationId: `list`
+**说明**: 按条件分页查询本租户员工。需 `TENANT_ADMIN` 或平台管理员权限。
 
-**响应 (Response)**
+**Query 参数** (`EmployeePageQuery`):
 
-**`200` OK**
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| page | integer | 否 | 页码 |
+| size | integer | 否 | 每页条数 |
+| sortBy | string | 否 | 排序字段 |
+| sortDir | string | 否 | 排序方向 |
+| orgId | long | 否 | 所属组织 ID |
+| roleId | long | 否 | 角色 ID |
+| status | string | 否 | 状态:`ACTIVE` / `DISABLED` |
+| keyword | string | 否 | 关键词(姓名/手机号模糊匹配) |
 
-返回包装类型: [`ResultListRoleVO`](#model-resultlistrolevo)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: `array\<RoleVO\>`
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-#### `GET` `/tenant/roles/{id}`
-
-**获取角色详情**
-
-- OperationId: `getById`
-
-**请求参数**
-
-| 名称 | 位置 | 类型 | 必填 | 默认值 | 说明 |
-|------|------|------|------|--------|------|
-| `id` | path | integer (int64) | 是 |  | 角色 id |
-
-**响应 (Response)**
-
-**`200` OK**
-
-返回包装类型: [`ResultRoleVO`](#model-resultrolevo)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: [`RoleVO`](#model-rolevo)
-
-> 角色响应体
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `id` | integer (int64) | 否 | 主键 id |  |
-| `createdAt` | string (date-time) | 否 | 创建时间 |  |
-| `updatedAt` | string (date-time) | 否 | 更新时间 |  |
-| `createdBy` | string | 否 | 创建人(操作人标识,如 Sa-Token loginId 字符串或 system) |  |
-| `updatedBy` | string | 否 | 更新人 |  |
-| `tenantId` | string | 否 | 所属租户 id(平台管理员等跨租户场景为 null) |  |
-| `deleted` | integer (int32) | 否 | 软删除标记(0=未删除,1=已删除) |  |
-| `roleName` | string | 否 | 角色名称 | 租户管理员 |
-| `roleCode` | string | 否 | 角色编码(预置角色常量如 TENANT_ADMIN/PLATFORM_ADMIN;自建角色为自定义) | TENANT_ADMIN |
-| `description` | string | 否 | 角色描述 | 企业管理者 |
-| `roleType` | string<br>枚举: PRESET / CUSTOM | 否 | 角色类型(PRESET 预置,全租户共享 / CUSTOM 自建,本租户) | PRESET |
-| `status` | string<br>枚举: ENABLED / DISABLED / DELETED | 否 | 状态(ENABLED 启用 / DISABLED 停用 / DELETED 已删除) | ENABLED |
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-#### `PUT` `/tenant/roles/{id}/toggle-status`
-
-**启用/停用角色**
-
-- OperationId: `toggleStatus`
-
-**请求参数**
-
-| 名称 | 位置 | 类型 | 必填 | 默认值 | 说明 |
-|------|------|------|------|--------|------|
-| `id` | path | integer (int64) | 是 |  | 角色 id |
-
-**响应 (Response)**
-
-**`200` OK**
-
-返回包装类型: [`ResultVoid`](#model-resultvoid)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: `object`
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-### 组织管理
-
-#### `POST` `/tenant/organizations`
-
-**创建组织**
-
-- OperationId: `create`
-
-**请求体 (Request Body)**
-
-Content-Type: `application/json`
-
-类型: [`OrgCreateDTO`](#model-orgcreatedto)
-
-> 创建组织请求体
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `orgName` | string | 是 | 组织名称 | 华东分公司 |
-| `orgType` | string<br>枚举: HQ / BRANCH / DEALER / AGENT / DEPT / TEAM | 否 | 组织类型(HQ 公司本部 / BRANCH 分公司 / DEALER 经销商 / AGENT 代理商 / DEPT 部门 / TEAM 团队);默认 DEPT | BRANCH |
-| `parentId` | integer (int64) | 否 | 上级组织 id;根组织留空 |  |
-| `leaderId` | integer (int64) | 否 | 负责人员工 id;可空 |  |
-| `region` | string | 否 | 所在地区;可空 | 上海 |
-| `sortOrder` | integer (int32) | 否 | 同级内显示排序(数字越小越靠前);可空 | 1 |
-
-**响应 (Response)**
-
-**`200` OK**
-
-返回包装类型: [`ResultOrgVO`](#model-resultorgvo)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: [`OrgVO`](#model-orgvo)
-
-> 组织响应体
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `id` | integer (int64) | 否 | 主键 id |  |
-| `createdAt` | string (date-time) | 否 | 创建时间 |  |
-| `updatedAt` | string (date-time) | 否 | 更新时间 |  |
-| `createdBy` | string | 否 | 创建人(操作人标识,如 Sa-Token loginId 字符串或 system) |  |
-| `updatedBy` | string | 否 | 更新人 |  |
-| `tenantId` | string | 否 | 所属租户 id(平台管理员等跨租户场景为 null) |  |
-| `deleted` | integer (int32) | 否 | 软删除标记(0=未删除,1=已删除) |  |
-| `orgName` | string | 否 | 组织名称 | 华东分公司 |
-| `orgType` | string<br>枚举: HQ / BRANCH / DEALER / AGENT / DEPT / TEAM | 否 | 组织类型(HQ 公司本部 / BRANCH 分公司 / DEALER 经销商 / AGENT 代理商 / DEPT 部门 / TEAM 团队) | BRANCH |
-| `parentId` | integer (int64) | 否 | 上级组织 id(根组织为 null) |  |
-| `orgPath` | string | 否 | 组织路径(从根到本节点的全路径,如 /默认租户/华东分公司) |  |
-| `leaderId` | integer (int64) | 否 | 负责人员工 id(可空) |  |
-| `region` | string | 否 | 所在地区 | 上海 |
-| `sortOrder` | integer (int32) | 否 | 同级内显示排序(数字越小越靠前) |  |
-| `status` | boolean | 否 | 启用状态(true 启用 / false 停用) |  |
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-#### `GET` `/tenant/organizations/tree`
-
-**获取组织树**
-
-- OperationId: `getTree`
-
-**响应 (Response)**
-
-**`200` OK**
-
-返回包装类型: [`ResultListOrgVO`](#model-resultlistorgvo)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: `array\<OrgVO\>`
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-#### `PUT` `/tenant/organizations/{id}`
-
-**更新组织**
-
-- OperationId: `update`
-
-**请求参数**
-
-| 名称 | 位置 | 类型 | 必填 | 默认值 | 说明 |
-|------|------|------|------|--------|------|
-| `id` | path | integer (int64) | 是 |  | 组织 id |
-
-**请求体 (Request Body)**
-
-Content-Type: `application/json`
-
-类型: [`OrgCreateDTO`](#model-orgcreatedto)
-
-> 创建组织请求体
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `orgName` | string | 是 | 组织名称 | 华东分公司 |
-| `orgType` | string<br>枚举: HQ / BRANCH / DEALER / AGENT / DEPT / TEAM | 否 | 组织类型(HQ 公司本部 / BRANCH 分公司 / DEALER 经销商 / AGENT 代理商 / DEPT 部门 / TEAM 团队);默认 DEPT | BRANCH |
-| `parentId` | integer (int64) | 否 | 上级组织 id;根组织留空 |  |
-| `leaderId` | integer (int64) | 否 | 负责人员工 id;可空 |  |
-| `region` | string | 否 | 所在地区;可空 | 上海 |
-| `sortOrder` | integer (int32) | 否 | 同级内显示排序(数字越小越靠前);可空 | 1 |
-
-**响应 (Response)**
-
-**`200` OK**
-
-返回包装类型: [`ResultOrgVO`](#model-resultorgvo)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: [`OrgVO`](#model-orgvo)
-
-> 组织响应体
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `id` | integer (int64) | 否 | 主键 id |  |
-| `createdAt` | string (date-time) | 否 | 创建时间 |  |
-| `updatedAt` | string (date-time) | 否 | 更新时间 |  |
-| `createdBy` | string | 否 | 创建人(操作人标识,如 Sa-Token loginId 字符串或 system) |  |
-| `updatedBy` | string | 否 | 更新人 |  |
-| `tenantId` | string | 否 | 所属租户 id(平台管理员等跨租户场景为 null) |  |
-| `deleted` | integer (int32) | 否 | 软删除标记(0=未删除,1=已删除) |  |
-| `orgName` | string | 否 | 组织名称 | 华东分公司 |
-| `orgType` | string<br>枚举: HQ / BRANCH / DEALER / AGENT / DEPT / TEAM | 否 | 组织类型(HQ 公司本部 / BRANCH 分公司 / DEALER 经销商 / AGENT 代理商 / DEPT 部门 / TEAM 团队) | BRANCH |
-| `parentId` | integer (int64) | 否 | 上级组织 id(根组织为 null) |  |
-| `orgPath` | string | 否 | 组织路径(从根到本节点的全路径,如 /默认租户/华东分公司) |  |
-| `leaderId` | integer (int64) | 否 | 负责人员工 id(可空) |  |
-| `region` | string | 否 | 所在地区 | 上海 |
-| `sortOrder` | integer (int32) | 否 | 同级内显示排序(数字越小越靠前) |  |
-| `status` | boolean | 否 | 启用状态(true 启用 / false 停用) |  |
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-#### `DELETE` `/tenant/organizations/{id}`
-
-**删除组织**
-
-- OperationId: `delete`
-
-**请求参数**
-
-| 名称 | 位置 | 类型 | 必填 | 默认值 | 说明 |
-|------|------|------|------|--------|------|
-| `id` | path | integer (int64) | 是 |  | 组织 id |
-
-**响应 (Response)**
-
-**`200` OK**
-
-返回包装类型: [`ResultVoid`](#model-resultvoid)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: `object`
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-#### `PUT` `/tenant/organizations/{id}/toggle-status`
-
-**启用/停用组织**
-
-- OperationId: `toggleStatus_1`
-
-**请求参数**
-
-| 名称 | 位置 | 类型 | 必填 | 默认值 | 说明 |
-|------|------|------|------|--------|------|
-| `id` | path | integer (int64) | 是 |  | 组织 id |
-
-**响应 (Response)**
-
-**`200` OK**
-
-返回包装类型: [`ResultVoid`](#model-resultvoid)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: `object`
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-### 员工管理
-
-#### `GET` `/tenant/employees`
-
-**员工分页列表**
-
-- OperationId: `list_1`
-
-**请求参数**
-
-| 名称 | 位置 | 类型 | 必填 | 默认值 | 说明 |
-|------|------|------|------|--------|------|
-| `query` | query | EmployeePageQuery | 是 |  |  |
-
-**响应 (Response)**
-
-**`200` OK**
-
-返回包装类型: [`ResultPageResultEmployeeVO`](#model-resultpageresultemployeevo)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: [`PageResultEmployeeVO`](#model-pageresultemployeevo)
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `page` | integer (int64) | 否 |  |  |
-| `size` | integer (int64) | 否 |  |  |
-| `total` | integer (int64) | 否 |  |  |
-| `totalPages` | integer (int64) | 否 |  |  |
-| `records` | array\<EmployeeVO\> | 否 |  |  |
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-#### `POST` `/tenant/employees`
-
-**创建员工**
-
-- OperationId: `create_1`
-
-**请求体 (Request Body)**
-
-Content-Type: `application/json`
-
-类型: [`EmployeeCreateDTO`](#model-employeecreatedto)
-
-> 创建员工请求体
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `name` | string | 是 | 员工姓名 | 张三 |
-| `phone` | string | 是 | 登录/联系手机号(同租户内唯一建议) | 13800138000 |
-| `orgId` | integer (int64) | 是 | 所属组织 id | 1 |
-| `roleId` | integer (int64) | 是 | 角色 id(关联 crm_role.id;2 为 TENANT_ADMIN 预置角色) | 2 |
-| `position` | string | 否 | 职位 | 销售主管 |
-| `email` | string | 否 | 邮箱 | zhangsan@example.com |
-| `avatarUrl` | string | 否 | 头像 URL |  |
-| `holdLimit` | integer (int32) | 否 | 客户持有上限(可负责客户数) | 50 |
-| `region` | string | 否 | 负责区域 | 华东 |
-| `productTags` | string | 否 | 擅长产品标签(逗号分隔自由文本) | 汽车,零售 |
-
-**响应 (Response)**
-
-**`200` OK**
-
-返回包装类型: [`ResultEmployeeVO`](#model-resultemployeevo)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: [`EmployeeVO`](#model-employeevo)
-
-> 员工响应体
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `id` | integer (int64) | 否 | 主键 id |  |
-| `createdAt` | string (date-time) | 否 | 创建时间 |  |
-| `updatedAt` | string (date-time) | 否 | 更新时间 |  |
-| `createdBy` | string | 否 | 创建人(操作人标识,如 Sa-Token loginId 字符串或 system) |  |
-| `updatedBy` | string | 否 | 更新人 |  |
-| `tenantId` | string | 否 | 所属租户 id(平台管理员等跨租户场景为 null) |  |
-| `deleted` | integer (int32) | 否 | 软删除标记(0=未删除,1=已删除) |  |
-| `name` | string | 否 | 员工姓名 | 张三 |
-| `phone` | string | 否 | 登录/联系手机号 | 13800138000 |
-| `orgId` | integer (int64) | 否 | 所属组织 id |  |
-| `roleId` | integer (int64) | 否 | 角色 id(关联 crm_role.id;为 2 即 TENANT_ADMIN 预置角色) |  |
-| `position` | string | 否 | 职位 | 销售主管 |
-| `email` | string | 否 | 邮箱 | zhangsan@example.com |
-| `avatarUrl` | string | 否 | 头像 URL |  |
-| `status` | string<br>枚举: ACTIVE / DISABLED | 否 | 状态(ACTIVE 在职 / DISABLED 停用) | ACTIVE |
-| `holdLimit` | integer (int32) | 否 | 客户持有上限(可负责的客户数) |  |
-| `region` | string | 否 | 负责区域 | 华东 |
-| `productTags` | string | 否 | 擅长产品标签(逗号分隔自由文本) | 汽车,零售 |
-| `hireDate` | string (date) | 否 | 入职日期 |  |
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-#### `GET` `/tenant/employees/by-org/{orgId}`
-
-**按组织查询员工列表**
-
-- OperationId: `getByOrgId`
-
-**请求参数**
-
-| 名称 | 位置 | 类型 | 必填 | 默认值 | 说明 |
-|------|------|------|------|--------|------|
-| `orgId` | path | integer (int64) | 是 |  | 组织 id |
-
-**响应 (Response)**
-
-**`200` OK**
-
-返回包装类型: [`ResultListEmployeeVO`](#model-resultlistemployeevo)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: `array\<EmployeeVO\>`
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-#### `GET` `/tenant/employees/by-role/{roleId}`
-
-**按角色查询员工列表**
-
-- OperationId: `getByRoleId`
-
-**请求参数**
-
-| 名称 | 位置 | 类型 | 必填 | 默认值 | 说明 |
-|------|------|------|------|--------|------|
-| `roleId` | path | integer (int64) | 是 |  | 角色 id |
-
-**响应 (Response)**
-
-**`200` OK**
-
-返回包装类型: [`ResultListEmployeeVO`](#model-resultlistemployeevo)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: `array\<EmployeeVO\>`
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-#### `GET` `/tenant/employees/count-by-role/{roleId}`
-
-**统计角色关联的员工数**
-
-- OperationId: `countByRoleId`
-
-**请求参数**
-
-| 名称 | 位置 | 类型 | 必填 | 默认值 | 说明 |
-|------|------|------|------|--------|------|
-| `roleId` | path | integer (int64) | 是 |  | 角色 id |
-
-**响应 (Response)**
-
-**`200` OK**
-
-返回包装类型: [`ResultLong`](#model-resultlong)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: `integer (int64)`
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-#### `GET` `/tenant/employees/export`
-
-**导出员工(Excel)**
-
-- OperationId: `exportEmployees`
-
-**请求参数**
-
-| 名称 | 位置 | 类型 | 必填 | 默认值 | 说明 |
-|------|------|------|------|--------|------|
-| `orgId` | query | integer (int64) | 否 |  | 按组织 id 过滤;留空=全部 |
-
-**响应 (Response)**
-
-- `200` OK
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-#### `POST` `/tenant/employees/import`
-
-**批量导入员工(Excel)**
-
-- OperationId: `importEmployees`
-
-**请求体 (Request Body)**
-
-> ⚠️ 规范中可能标注为 `application/json` + binary,实际调用请使用 `multipart/form-data` 上传 Excel,字段名 `file`。
-
-Content-Type: `multipart/form-data`
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `file` | string (binary) | 是 | Excel 文件(multipart 字段名 file) |  |
-
-**响应 (Response)**
-
-**`200` OK**
-
-返回包装类型: [`ResultInteger`](#model-resultinteger)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: `integer (int32)`
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-#### `GET` `/tenant/employees/{id}`
-
-**获取员工详情**
-
-- OperationId: `getById_1`
-
-**请求参数**
-
-| 名称 | 位置 | 类型 | 必填 | 默认值 | 说明 |
-|------|------|------|------|--------|------|
-| `id` | path | integer (int64) | 是 |  | 员工 id |
-
-**响应 (Response)**
-
-**`200` OK**
-
-返回包装类型: [`ResultEmployeeVO`](#model-resultemployeevo)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: [`EmployeeVO`](#model-employeevo)
-
-> 员工响应体
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `id` | integer (int64) | 否 | 主键 id |  |
-| `createdAt` | string (date-time) | 否 | 创建时间 |  |
-| `updatedAt` | string (date-time) | 否 | 更新时间 |  |
-| `createdBy` | string | 否 | 创建人(操作人标识,如 Sa-Token loginId 字符串或 system) |  |
-| `updatedBy` | string | 否 | 更新人 |  |
-| `tenantId` | string | 否 | 所属租户 id(平台管理员等跨租户场景为 null) |  |
-| `deleted` | integer (int32) | 否 | 软删除标记(0=未删除,1=已删除) |  |
-| `name` | string | 否 | 员工姓名 | 张三 |
-| `phone` | string | 否 | 登录/联系手机号 | 13800138000 |
-| `orgId` | integer (int64) | 否 | 所属组织 id |  |
-| `roleId` | integer (int64) | 否 | 角色 id(关联 crm_role.id;为 2 即 TENANT_ADMIN 预置角色) |  |
-| `position` | string | 否 | 职位 | 销售主管 |
-| `email` | string | 否 | 邮箱 | zhangsan@example.com |
-| `avatarUrl` | string | 否 | 头像 URL |  |
-| `status` | string<br>枚举: ACTIVE / DISABLED | 否 | 状态(ACTIVE 在职 / DISABLED 停用) | ACTIVE |
-| `holdLimit` | integer (int32) | 否 | 客户持有上限(可负责的客户数) |  |
-| `region` | string | 否 | 负责区域 | 华东 |
-| `productTags` | string | 否 | 擅长产品标签(逗号分隔自由文本) | 汽车,零售 |
-| `hireDate` | string (date) | 否 | 入职日期 |  |
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-#### `PUT` `/tenant/employees/{id}`
-
-**更新员工**
-
-- OperationId: `update_1`
-
-**请求参数**
-
-| 名称 | 位置 | 类型 | 必填 | 默认值 | 说明 |
-|------|------|------|------|--------|------|
-| `id` | path | integer (int64) | 是 |  | 员工 id |
-
-**请求体 (Request Body)**
-
-Content-Type: `application/json`
-
-类型: [`EmployeeUpdateDTO`](#model-employeeupdatedto)
-
-> 编辑员工请求体
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `name` | string | 否 | 员工姓名 | 张三 |
-| `phone` | string | 否 | 登录/联系手机号 | 13800138000 |
-| `orgId` | integer (int64) | 否 | 所属组织 id |  |
-| `roleId` | integer (int64) | 否 | 角色 id(关联 crm_role.id) |  |
-| `position` | string | 否 | 职位 | 销售主管 |
-| `email` | string | 否 | 邮箱 | zhangsan@example.com |
-| `holdLimit` | integer (int32) | 否 | 客户持有上限 | 50 |
-| `region` | string | 否 | 负责区域 | 华东 |
-| `productTags` | string | 否 | 擅长产品标签(逗号分隔自由文本) | 汽车,零售 |
-
-**响应 (Response)**
-
-**`200` OK**
-
-返回包装类型: [`ResultEmployeeVO`](#model-resultemployeevo)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: [`EmployeeVO`](#model-employeevo)
-
-> 员工响应体
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `id` | integer (int64) | 否 | 主键 id |  |
-| `createdAt` | string (date-time) | 否 | 创建时间 |  |
-| `updatedAt` | string (date-time) | 否 | 更新时间 |  |
-| `createdBy` | string | 否 | 创建人(操作人标识,如 Sa-Token loginId 字符串或 system) |  |
-| `updatedBy` | string | 否 | 更新人 |  |
-| `tenantId` | string | 否 | 所属租户 id(平台管理员等跨租户场景为 null) |  |
-| `deleted` | integer (int32) | 否 | 软删除标记(0=未删除,1=已删除) |  |
-| `name` | string | 否 | 员工姓名 | 张三 |
-| `phone` | string | 否 | 登录/联系手机号 | 13800138000 |
-| `orgId` | integer (int64) | 否 | 所属组织 id |  |
-| `roleId` | integer (int64) | 否 | 角色 id(关联 crm_role.id;为 2 即 TENANT_ADMIN 预置角色) |  |
-| `position` | string | 否 | 职位 | 销售主管 |
-| `email` | string | 否 | 邮箱 | zhangsan@example.com |
-| `avatarUrl` | string | 否 | 头像 URL |  |
-| `status` | string<br>枚举: ACTIVE / DISABLED | 否 | 状态(ACTIVE 在职 / DISABLED 停用) | ACTIVE |
-| `holdLimit` | integer (int32) | 否 | 客户持有上限(可负责的客户数) |  |
-| `region` | string | 否 | 负责区域 | 华东 |
-| `productTags` | string | 否 | 擅长产品标签(逗号分隔自由文本) | 汽车,零售 |
-| `hireDate` | string (date) | 否 | 入职日期 |  |
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-#### `DELETE` `/tenant/employees/{id}`
-
-**删除员工**
-
-- OperationId: `delete_1`
-
-**请求参数**
-
-| 名称 | 位置 | 类型 | 必填 | 默认值 | 说明 |
-|------|------|------|------|--------|------|
-| `id` | path | integer (int64) | 是 |  | 员工 id |
-
-**响应 (Response)**
-
-**`200` OK**
-
-返回包装类型: [`ResultVoid`](#model-resultvoid)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: `object`
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-### 客户管理(后台 · 只读)
-
-#### `GET` `/tenant/customers`
-
-**本租户客户列表(只读)**
-
-- OperationId: `list_2`
-
-**请求参数**
-
-| 名称 | 位置 | 类型 | 必填 | 默认值 | 说明 |
-|------|------|------|------|--------|------|
-| `query` | query | TenantCustomerPageQuery | 是 |  |  |
-
-**响应 (Response)**
-
-**`200` OK**
-
-返回包装类型: [`ResultPageResultCustomerVO`](#model-resultpageresultcustomervo)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: [`PageResultCustomerVO`](#model-pageresultcustomervo)
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `page` | integer (int64) | 否 |  |  |
-| `size` | integer (int64) | 否 |  |  |
-| `total` | integer (int64) | 否 |  |  |
-| `totalPages` | integer (int64) | 否 |  |  |
-| `records` | array\<CustomerVO\> | 否 |  |  |
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-#### `GET` `/tenant/customers/{id}`
-
-**客户详情(只读)**
-
-- OperationId: `detail`
-
-**请求参数**
-
-| 名称 | 位置 | 类型 | 必填 | 默认值 | 说明 |
-|------|------|------|------|--------|------|
-| `id` | path | integer (int64) | 是 |  | 客户 id |
-
-**响应 (Response)**
-
-**`200` OK**
-
-返回包装类型: [`ResultCustomerVO`](#model-resultcustomervo)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: [`CustomerVO`](#model-customervo)
-
-> 客户响应体(后台 · 只读)
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `id` | integer (int64) | 否 | 主键 id |  |
-| `createdAt` | string (date-time) | 否 | 创建时间 |  |
-| `updatedAt` | string (date-time) | 否 | 更新时间 |  |
-| `createdBy` | string | 否 | 创建人(操作人标识,如 Sa-Token loginId 字符串或 system) |  |
-| `updatedBy` | string | 否 | 更新人 |  |
-| `tenantId` | string | 否 | 所属租户 id(平台管理员等跨租户场景为 null) |  |
-| `deleted` | integer (int32) | 否 | 软删除标记(0=未删除,1=已删除) |  |
-| `name` | string | 否 | 客户名称(企业名或个人名) | 上海特斯拉 |
-| `customerType` | string<br>枚举: ENTERPRISE / PERSONAL | 否 | 客户类型(ENTERPRISE 企业 / PERSONAL 个人) | ENTERPRISE |
-| `industry` | string | 否 | 所属行业 | 汽车行业 |
-| `region` | string | 否 | 所在地区 | 上海 |
-| `contactName` | string | 否 | 主要联系人姓名 | 张经理 |
-| `contactPhone` | string | 否 | 主要联系电话(手机) | 13800138000 |
-| `contactPosition` | string | 否 | 主要联系人职位 | 采购经理 |
-| `contactTel` | string | 否 | 备用/座机电话 | 021-12345678 |
-| `email` | string | 否 | 客户邮箱 | contact@example.com |
-| `address` | string | 否 | 客户地址 | 上海市浦东新区 |
-| `source` | string<br>枚举: MINI_PROGRAM / SALES_INPUT / EXHIBITION / REFERRAL | 否 | 客户来源(小程序注册/销售录入/展会/转介绍) | SALES_INPUT |
-| `level` | string<br>枚举: VIP / A / B / C | 否 | 客户级别(VIP/A/B/C) | VIP |
-| `tags` | string | 否 | 标签(逗号分隔的自由文本) | 重点,高意向 |
-| `orgId` | integer (int64) | 否 | 所属组织 id(可空) |  |
-| `ownerId` | integer (int64) | 否 | 客户归属人(销售员)员工 id |  |
-| `stage` | string<br>枚举: NEW_LEAD / PENDING_ASSIGN / CONTACTING / NEEDS_COMM / QUOTING / NEGOTIATING / DEAL / LOST | 否 | 当前客户阶段(NEW_LEAD新线索 / PENDING_ASSIGN待分配 / CONTACTING接洽中 / NEEDS_COMM需求沟通 / QUOTING报价中 / NEGOTIATING议价中 / DEAL成交 / LOST流失) | NEW_LEAD |
-| `lastFollowAt` | string (date-time) | 否 | 最近一次跟进时间 |  |
-| `nextFollowAt` | string (date-time) | 否 | 下次计划跟进时间 |  |
-| `dealAt` | string (date-time) | 否 | 成交时间(阶段到 DEAL 时写入) |  |
-| `lostAt` | string (date-time) | 否 | 流失时间(阶段到 LOST 时写入) |  |
-| `lostReason` | string | 否 | 流失原因 |  |
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
-
-#### `GET` `/tenant/customers/{id}/follow-ups`
-
-**客户跟进历史(只读)**
-
-- OperationId: `followUps`
-
-**请求参数**
-
-| 名称 | 位置 | 类型 | 必填 | 默认值 | 说明 |
-|------|------|------|------|--------|------|
-| `id` | path | integer (int64) | 是 |  | 客户 id |
-| `query` | query | FollowUpPageQuery | 是 |  |  |
-
-**响应 (Response)**
-
-**`200` OK**
-
-返回包装类型: [`ResultPageResultFollowUpVO`](#model-resultpageresultfollowupvo)
-
-**统一包装字段:**
-
-> 统一响应包装字段
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-**`data` 业务字段:**
-
-类型: [`PageResultFollowUpVO`](#model-pageresultfollowupvo)
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `page` | integer (int64) | 否 |  |  |
-| `size` | integer (int64) | 否 |  |  |
-| `total` | integer (int64) | 否 |  |  |
-| `totalPages` | integer (int64) | 否 |  |  |
-| `records` | array\<FollowUpVO\> | 否 |  |  |
-- `400` Bad Request → [`ResultVoid`](#model-resultvoid)
-- `405` Method Not Allowed → [`ResultVoid`](#model-resultvoid)
-- `500` Internal Server Error → [`ResultVoid`](#model-resultvoid)
+**响应**: `ResultPageResultEmployeeVO`
 
 ---
 
-## 数据模型完整参考
+#### 1.2 创建员工
 
-> 接口章节已内联展示 Request / Response 字段说明,此处便于全局查阅。
+```http
+POST /tenant/employees
+```
 
-### EmployeeCreateDTO
+**说明**: 在当前租户下创建员工,`tenant_id` 由服务端自动填充。
 
-<a id="model-employeecreatedto"></a>
+**请求体** (`EmployeeCreateDTO`):
 
-> 创建员工请求体
+| 字段 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| name | string | ✅ | 员工姓名 |
+| phone | string | ✅ | 登录/联系手机号(同租户建议唯一) |
+| orgId | long | ✅ | 所属组织 ID |
+| roleId | long | ✅ | 角色 ID(预置 `TENANT_ADMIN` = 2) |
+| position | string | 否 | 职位 |
+| email | string | 否 | 邮箱 |
+| avatarUrl | string | 否 | 头像 URL |
+| holdLimit | integer | 否 | 客户持有上限 |
+| region | string | 否 | 负责区域 |
 
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `name` | string | 是 | 员工姓名 | 张三 |
-| `phone` | string | 是 | 登录/联系手机号(同租户内唯一建议) | 13800138000 |
-| `orgId` | integer (int64) | 是 | 所属组织 id | 1 |
-| `roleId` | integer (int64) | 是 | 角色 id(关联 crm_role.id;2 为 TENANT_ADMIN 预置角色) | 2 |
-| `position` | string | 否 | 职位 | 销售主管 |
-| `email` | string | 否 | 邮箱 | zhangsan@example.com |
-| `avatarUrl` | string | 否 | 头像 URL |  |
-| `holdLimit` | integer (int32) | 否 | 客户持有上限(可负责客户数) | 50 |
-| `region` | string | 否 | 负责区域 | 华东 |
-| `productTags` | string | 否 | 擅长产品标签(逗号分隔自由文本) | 汽车,零售 |
-
-### EmployeePageQuery
-
-<a id="model-employeepagequery"></a>
-
-> 员工分页查询条件
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `page` | integer (int32) | 否 |  |  |
-| `size` | integer (int32) | 否 |  |  |
-| `sortBy` | string | 否 |  |  |
-| `sortDir` | string | 否 |  |  |
-| `orgId` | integer (int64) | 否 | 按所属组织 id 过滤;留空=不按组织过滤 |  |
-| `roleId` | integer (int64) | 否 | 按角色 id 过滤;留空=不按角色过滤 |  |
-| `status` | string<br>枚举: ACTIVE / DISABLED | 否 | 按员工状态过滤(ACTIVE 在职 / DISABLED 停用);留空=全部 |  |
-| `keyword` | string | 否 | 关键词(模糊匹配员工姓名/手机号等);可空 | 张三 |
-| `offset` | integer (int32) | 否 |  |  |
-
-### EmployeeUpdateDTO
-
-<a id="model-employeeupdatedto"></a>
-
-> 编辑员工请求体
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `name` | string | 否 | 员工姓名 | 张三 |
-| `phone` | string | 否 | 登录/联系手机号 | 13800138000 |
-| `orgId` | integer (int64) | 否 | 所属组织 id |  |
-| `roleId` | integer (int64) | 否 | 角色 id(关联 crm_role.id) |  |
-| `position` | string | 否 | 职位 | 销售主管 |
-| `email` | string | 否 | 邮箱 | zhangsan@example.com |
-| `holdLimit` | integer (int32) | 否 | 客户持有上限 | 50 |
-| `region` | string | 否 | 负责区域 | 华东 |
-| `productTags` | string | 否 | 擅长产品标签(逗号分隔自由文本) | 汽车,零售 |
-
-### FollowUpPageQuery
-
-<a id="model-followuppagequery"></a>
-
-> 客户跟进历史分页查询条件(后台只读)
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `page` | integer (int32) | 否 |  |  |
-| `size` | integer (int32) | 否 |  |  |
-| `sortBy` | string | 否 |  |  |
-| `sortDir` | string | 否 |  |  |
-| `offset` | integer (int32) | 否 |  |  |
-
-### OrgCreateDTO
-
-<a id="model-orgcreatedto"></a>
-
-> 创建组织请求体
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `orgName` | string | 是 | 组织名称 | 华东分公司 |
-| `orgType` | string<br>枚举: HQ / BRANCH / DEALER / AGENT / DEPT / TEAM | 否 | 组织类型(HQ 公司本部 / BRANCH 分公司 / DEALER 经销商 / AGENT 代理商 / DEPT 部门 / TEAM 团队);默认 DEPT | BRANCH |
-| `parentId` | integer (int64) | 否 | 上级组织 id;根组织留空 |  |
-| `leaderId` | integer (int64) | 否 | 负责人员工 id;可空 |  |
-| `region` | string | 否 | 所在地区;可空 | 上海 |
-| `sortOrder` | integer (int32) | 否 | 同级内显示排序(数字越小越靠前);可空 | 1 |
-
-### TenantCustomerPageQuery
-
-<a id="model-tenantcustomerpagequery"></a>
-
-> 本租户客户分页查询条件(后台只读)
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `page` | integer (int32) | 否 |  |  |
-| `size` | integer (int32) | 否 |  |  |
-| `sortBy` | string | 否 |  |  |
-| `sortDir` | string | 否 |  |  |
-| `ownerId` | integer (int64) | 否 | 按客户归属人(销售员)员工 id 过滤;留空=全部 |  |
-| `offset` | integer (int32) | 否 |  |  |
-
-### CustomerVO
-
-<a id="model-customervo"></a>
-
-> 客户响应体(后台 · 只读)
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `id` | integer (int64) | 否 | 主键 id |  |
-| `createdAt` | string (date-time) | 否 | 创建时间 |  |
-| `updatedAt` | string (date-time) | 否 | 更新时间 |  |
-| `createdBy` | string | 否 | 创建人(操作人标识,如 Sa-Token loginId 字符串或 system) |  |
-| `updatedBy` | string | 否 | 更新人 |  |
-| `tenantId` | string | 否 | 所属租户 id(平台管理员等跨租户场景为 null) |  |
-| `deleted` | integer (int32) | 否 | 软删除标记(0=未删除,1=已删除) |  |
-| `name` | string | 否 | 客户名称(企业名或个人名) | 上海特斯拉 |
-| `customerType` | string<br>枚举: ENTERPRISE / PERSONAL | 否 | 客户类型(ENTERPRISE 企业 / PERSONAL 个人) | ENTERPRISE |
-| `industry` | string | 否 | 所属行业 | 汽车行业 |
-| `region` | string | 否 | 所在地区 | 上海 |
-| `contactName` | string | 否 | 主要联系人姓名 | 张经理 |
-| `contactPhone` | string | 否 | 主要联系电话(手机) | 13800138000 |
-| `contactPosition` | string | 否 | 主要联系人职位 | 采购经理 |
-| `contactTel` | string | 否 | 备用/座机电话 | 021-12345678 |
-| `email` | string | 否 | 客户邮箱 | contact@example.com |
-| `address` | string | 否 | 客户地址 | 上海市浦东新区 |
-| `source` | string<br>枚举: MINI_PROGRAM / SALES_INPUT / EXHIBITION / REFERRAL | 否 | 客户来源(小程序注册/销售录入/展会/转介绍) | SALES_INPUT |
-| `level` | string<br>枚举: VIP / A / B / C | 否 | 客户级别(VIP/A/B/C) | VIP |
-| `tags` | string | 否 | 标签(逗号分隔的自由文本) | 重点,高意向 |
-| `orgId` | integer (int64) | 否 | 所属组织 id(可空) |  |
-| `ownerId` | integer (int64) | 否 | 客户归属人(销售员)员工 id |  |
-| `stage` | string<br>枚举: NEW_LEAD / PENDING_ASSIGN / CONTACTING / NEEDS_COMM / QUOTING / NEGOTIATING / DEAL / LOST | 否 | 当前客户阶段(NEW_LEAD新线索 / PENDING_ASSIGN待分配 / CONTACTING接洽中 / NEEDS_COMM需求沟通 / QUOTING报价中 / NEGOTIATING议价中 / DEAL成交 / LOST流失) | NEW_LEAD |
-| `lastFollowAt` | string (date-time) | 否 | 最近一次跟进时间 |  |
-| `nextFollowAt` | string (date-time) | 否 | 下次计划跟进时间 |  |
-| `dealAt` | string (date-time) | 否 | 成交时间(阶段到 DEAL 时写入) |  |
-| `lostAt` | string (date-time) | 否 | 流失时间(阶段到 LOST 时写入) |  |
-| `lostReason` | string | 否 | 流失原因 |  |
-
-### EmployeeVO
-
-<a id="model-employeevo"></a>
-
-> 员工响应体
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `id` | integer (int64) | 否 | 主键 id |  |
-| `createdAt` | string (date-time) | 否 | 创建时间 |  |
-| `updatedAt` | string (date-time) | 否 | 更新时间 |  |
-| `createdBy` | string | 否 | 创建人(操作人标识,如 Sa-Token loginId 字符串或 system) |  |
-| `updatedBy` | string | 否 | 更新人 |  |
-| `tenantId` | string | 否 | 所属租户 id(平台管理员等跨租户场景为 null) |  |
-| `deleted` | integer (int32) | 否 | 软删除标记(0=未删除,1=已删除) |  |
-| `name` | string | 否 | 员工姓名 | 张三 |
-| `phone` | string | 否 | 登录/联系手机号 | 13800138000 |
-| `orgId` | integer (int64) | 否 | 所属组织 id |  |
-| `roleId` | integer (int64) | 否 | 角色 id(关联 crm_role.id;为 2 即 TENANT_ADMIN 预置角色) |  |
-| `position` | string | 否 | 职位 | 销售主管 |
-| `email` | string | 否 | 邮箱 | zhangsan@example.com |
-| `avatarUrl` | string | 否 | 头像 URL |  |
-| `status` | string<br>枚举: ACTIVE / DISABLED | 否 | 状态(ACTIVE 在职 / DISABLED 停用) | ACTIVE |
-| `holdLimit` | integer (int32) | 否 | 客户持有上限(可负责的客户数) |  |
-| `region` | string | 否 | 负责区域 | 华东 |
-| `productTags` | string | 否 | 擅长产品标签(逗号分隔自由文本) | 汽车,零售 |
-| `hireDate` | string (date) | 否 | 入职日期 |  |
-
-### FollowUpVO
-
-<a id="model-followupvo"></a>
-
-> 跟进记录响应体(后台 · 只读)
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `id` | integer (int64) | 否 | 主键 id |  |
-| `createdAt` | string (date-time) | 否 | 创建时间 |  |
-| `updatedAt` | string (date-time) | 否 | 更新时间 |  |
-| `createdBy` | string | 否 | 创建人(操作人标识,如 Sa-Token loginId 字符串或 system) |  |
-| `updatedBy` | string | 否 | 更新人 |  |
-| `tenantId` | string | 否 | 所属租户 id(平台管理员等跨租户场景为 null) |  |
-| `deleted` | integer (int32) | 否 | 软删除标记(0=未删除,1=已删除) |  |
-| `customerId` | integer (int64) | 否 | 关联客户 id |  |
-| `followerId` | integer (int64) | 否 | 跟进人(销售员)员工 id |  |
-| `followAt` | string (date-time) | 否 | 本次跟进实际发生时间 |  |
-| `followMethod` | string<br>枚举: PHONE / WECHAT / VISIT / EMAIL / MEETING | 否 | 跟进方式(PHONE 电话 / WECHAT 微信 / VISIT 上门拜访 / EMAIL 邮件 / MEETING 会议) | PHONE |
-| `content` | string | 否 | 跟进内容/沟通纪要 |  |
-| `nextFollowAt` | string (date-time) | 否 | 下次计划跟进时间(可空) |  |
-| `nextFollowPlan` | string | 否 | 下次跟进计划/备注(可空) |  |
-| `followStatus` | string<br>枚举: DONE / PENDING / OVERDUE | 否 | 跟进状态(DONE 已完成 / PENDING 待跟进 / OVERDUE 已超时) | DONE |
-
-### OrgVO
-
-<a id="model-orgvo"></a>
-
-> 组织响应体
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `id` | integer (int64) | 否 | 主键 id |  |
-| `createdAt` | string (date-time) | 否 | 创建时间 |  |
-| `updatedAt` | string (date-time) | 否 | 更新时间 |  |
-| `createdBy` | string | 否 | 创建人(操作人标识,如 Sa-Token loginId 字符串或 system) |  |
-| `updatedBy` | string | 否 | 更新人 |  |
-| `tenantId` | string | 否 | 所属租户 id(平台管理员等跨租户场景为 null) |  |
-| `deleted` | integer (int32) | 否 | 软删除标记(0=未删除,1=已删除) |  |
-| `orgName` | string | 否 | 组织名称 | 华东分公司 |
-| `orgType` | string<br>枚举: HQ / BRANCH / DEALER / AGENT / DEPT / TEAM | 否 | 组织类型(HQ 公司本部 / BRANCH 分公司 / DEALER 经销商 / AGENT 代理商 / DEPT 部门 / TEAM 团队) | BRANCH |
-| `parentId` | integer (int64) | 否 | 上级组织 id(根组织为 null) |  |
-| `orgPath` | string | 否 | 组织路径(从根到本节点的全路径,如 /默认租户/华东分公司) |  |
-| `leaderId` | integer (int64) | 否 | 负责人员工 id(可空) |  |
-| `region` | string | 否 | 所在地区 | 上海 |
-| `sortOrder` | integer (int32) | 否 | 同级内显示排序(数字越小越靠前) |  |
-| `status` | boolean | 否 | 启用状态(true 启用 / false 停用) |  |
-
-### ResultListRoleVO
-
-<a id="model-resultlistrolevo"></a>
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `data` | array\<RoleVO\> | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-### ResultRoleVO
-
-<a id="model-resultrolevo"></a>
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `data` | RoleVO | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-### RoleVO
-
-<a id="model-rolevo"></a>
-
-> 角色响应体
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `id` | integer (int64) | 否 | 主键 id |  |
-| `createdAt` | string (date-time) | 否 | 创建时间 |  |
-| `updatedAt` | string (date-time) | 否 | 更新时间 |  |
-| `createdBy` | string | 否 | 创建人(操作人标识,如 Sa-Token loginId 字符串或 system) |  |
-| `updatedBy` | string | 否 | 更新人 |  |
-| `tenantId` | string | 否 | 所属租户 id(平台管理员等跨租户场景为 null) |  |
-| `deleted` | integer (int32) | 否 | 软删除标记(0=未删除,1=已删除) |  |
-| `roleName` | string | 否 | 角色名称 | 租户管理员 |
-| `roleCode` | string | 否 | 角色编码(预置角色常量如 TENANT_ADMIN/PLATFORM_ADMIN;自建角色为自定义) | TENANT_ADMIN |
-| `description` | string | 否 | 角色描述 | 企业管理者 |
-| `roleType` | string<br>枚举: PRESET / CUSTOM | 否 | 角色类型(PRESET 预置,全租户共享 / CUSTOM 自建,本租户) | PRESET |
-| `status` | string<br>枚举: ENABLED / DISABLED / DELETED | 否 | 状态(ENABLED 启用 / DISABLED 停用 / DELETED 已删除) | ENABLED |
-
-### PageResultCustomerVO
-
-<a id="model-pageresultcustomervo"></a>
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `page` | integer (int64) | 否 |  |  |
-| `size` | integer (int64) | 否 |  |  |
-| `total` | integer (int64) | 否 |  |  |
-| `totalPages` | integer (int64) | 否 |  |  |
-| `records` | array\<CustomerVO\> | 否 |  |  |
-
-### PageResultEmployeeVO
-
-<a id="model-pageresultemployeevo"></a>
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `page` | integer (int64) | 否 |  |  |
-| `size` | integer (int64) | 否 |  |  |
-| `total` | integer (int64) | 否 |  |  |
-| `totalPages` | integer (int64) | 否 |  |  |
-| `records` | array\<EmployeeVO\> | 否 |  |  |
-
-### PageResultFollowUpVO
-
-<a id="model-pageresultfollowupvo"></a>
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `page` | integer (int64) | 否 |  |  |
-| `size` | integer (int64) | 否 |  |  |
-| `total` | integer (int64) | 否 |  |  |
-| `totalPages` | integer (int64) | 否 |  |  |
-| `records` | array\<FollowUpVO\> | 否 |  |  |
-
-### ResultCustomerVO
-
-<a id="model-resultcustomervo"></a>
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `data` | CustomerVO | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-### ResultEmployeeVO
-
-<a id="model-resultemployeevo"></a>
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `data` | EmployeeVO | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-### ResultInteger
-
-<a id="model-resultinteger"></a>
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `data` | integer (int32) | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-### ResultListEmployeeVO
-
-<a id="model-resultlistemployeevo"></a>
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `data` | array\<EmployeeVO\> | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-### ResultListOrgVO
-
-<a id="model-resultlistorgvo"></a>
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `data` | array\<OrgVO\> | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-### ResultLong
-
-<a id="model-resultlong"></a>
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `data` | integer (int64) | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-### ResultOrgVO
-
-<a id="model-resultorgvo"></a>
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `data` | OrgVO | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-### ResultPageResultCustomerVO
-
-<a id="model-resultpageresultcustomervo"></a>
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `data` | PageResultCustomerVO | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-### ResultPageResultEmployeeVO
-
-<a id="model-resultpageresultemployeevo"></a>
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `data` | PageResultEmployeeVO | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-### ResultPageResultFollowUpVO
-
-<a id="model-resultpageresultfollowupvo"></a>
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `data` | PageResultFollowUpVO | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
-
-### ResultVoid
-
-<a id="model-resultvoid"></a>
-
-| 字段 | 类型 | 必填 | 说明 | 示例 |
-|------|------|------|------|------|
-| `code` | integer (int32) | 否 |  |  |
-| `message` | string | 否 |  |  |
-| `data` | object | 否 |  |  |
-| `timestamp` | integer (int64) | 否 |  |  |
-| `success` | boolean | 否 |  |  |
+**响应**: `ResultEmployeeVO`
 
 ---
 
-## 枚举值汇总
+#### 1.3 获取员工详情
 
-| 模型.字段 | 枚举值 | 说明 |
-|-----------|--------|------|
-| `CustomerVO.customerType` | `ENTERPRISE` · `PERSONAL` | 客户类型(ENTERPRISE 企业 / PERSONAL 个人) |
-| `CustomerVO.level` | `VIP` · `A` · `B` · `C` | 客户级别(VIP/A/B/C) |
-| `CustomerVO.source` | `MINI_PROGRAM` · `SALES_INPUT` · `EXHIBITION` · `REFERRAL` | 客户来源(小程序注册/销售录入/展会/转介绍) |
-| `CustomerVO.stage` | `NEW_LEAD` · `PENDING_ASSIGN` · `CONTACTING` · `NEEDS_COMM` · `QUOTING` · `NEGOTIATING` · `DEAL` · `LOST` | 当前客户阶段(NEW_LEAD新线索 / PENDING_ASSIGN待分配 / CONTACTING接洽中 / NEEDS_COMM需求沟通 / QUOTING报价中 / NEGOTIATING议价中 / DEAL成交 / LOST流失) |
-| `EmployeePageQuery.status` | `ACTIVE` · `DISABLED` | 按员工状态过滤(ACTIVE 在职 / DISABLED 停用);留空=全部 |
-| `EmployeeVO.status` | `ACTIVE` · `DISABLED` | 状态(ACTIVE 在职 / DISABLED 停用) |
-| `FollowUpVO.followMethod` | `PHONE` · `WECHAT` · `VISIT` · `EMAIL` · `MEETING` | 跟进方式(PHONE 电话 / WECHAT 微信 / VISIT 上门拜访 / EMAIL 邮件 / MEETING 会议) |
-| `FollowUpVO.followStatus` | `DONE` · `PENDING` · `OVERDUE` | 跟进状态(DONE 已完成 / PENDING 待跟进 / OVERDUE 已超时) |
-| `OrgCreateDTO.orgType` | `HQ` · `BRANCH` · `DEALER` · `AGENT` · `DEPT` · `TEAM` | 组织类型(HQ 公司本部 / BRANCH 分公司 / DEALER 经销商 / AGENT 代理商 / DEPT 部门 / TEAM 团队);默认 DEPT |
-| `OrgVO.orgType` | `HQ` · `BRANCH` · `DEALER` · `AGENT` · `DEPT` · `TEAM` | 组织类型(HQ 公司本部 / BRANCH 分公司 / DEALER 经销商 / AGENT 代理商 / DEPT 部门 / TEAM 团队) |
-| `RoleVO.roleType` | `PRESET` · `CUSTOM` | 角色类型(PRESET 预置,全租户共享 / CUSTOM 自建,本租户) |
-| `RoleVO.status` | `ENABLED` · `DISABLED` · `DELETED` | 状态(ENABLED 启用 / DISABLED 停用 / DELETED 已删除) |
+```http
+GET /tenant/employees/{id}
+```
+
+**路径参数**:
+
+| 参数 | 类型 | 说明 |
+|------|------|------|
+| id | long | 员工 ID |
+
+**响应**: `ResultEmployeeVO`
 
 ---
 
-## 注意事项
+#### 1.4 更新员工
 
-1. **认证**:所有接口需在 Header 中携带 `Authorization: Bearer <JWT>`。
-2. **多租户**:路径统一以 `/tenant/` 开头,租户上下文一般从 JWT 解析。
-3. **统一响应**:请以 `success` / `code` 判断业务结果,不要只依赖 HTTP 状态码。
-4. **分页**:分页参数默认 `page=1`,`size=20`。
-5. **员工导入**:实际请使用 `multipart/form-data` 上传 Excel,字段名 `file`。
-6. **员工导出**:返回 Excel 文件流。
-7. **字段说明**:Request / Response 字段的「说明」「示例」均来自 OpenAPI `description` 与 `example`。
+```http
+PUT /tenant/employees/{id}
+```
+
+**说明**: 更新员工基本信息(不包含 password / avatarUrl / hireDate)。
+
+**请求体** (`EmployeeUpdateDTO`):
+
+| 字段 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| name | string | 否 | 员工姓名 |
+| phone | string | 否 | 登录/联系手机号 |
+| orgId | long | 否 | 所属组织 ID |
+| roleId | long | 否 | 角色 ID |
+| position | string | 否 | 职位 |
+| email | string | 否 | 邮箱 |
+| holdLimit | integer | 否 | 客户持有上限 |
+| region | string | 否 | 负责区域 |
+
+**响应**: `ResultEmployeeVO`
 
 ---
 
-*文档由 OpenAPI 规范自动生成(租户版)  
-源地址: `http://121.41.54.56:9090/tenant/v3/api-docs`*
+#### 1.5 删除员工
+
+```http
+DELETE /tenant/employees/{id}
+```
+
+**路径参数**:
+
+| 参数 | 类型 | 说明 |
+|------|------|------|
+| id | long | 员工 ID |
+
+**响应**: `ResultVoid`
+
+---
+
+#### 1.6 按组织查员工分页
+
+```http
+GET /tenant/employees/by-org/{orgId}
+```
+
+**说明**: 分页返回指定组织下的**直接**员工(不含子孙组织)。
+
+**路径参数**:
+
+| 参数 | 类型 | 说明 |
+|------|------|------|
+| orgId | long | 组织 ID |
+
+**Query 参数** (`EmployeeSubListQuery`): page / size / sortBy / sortDir
+
+**响应**: `ResultPageResultEmployeeVO`
+
+---
+
+#### 1.7 按角色查员工分页
+
+```http
+GET /tenant/employees/by-role/{roleId}
+```
+
+**路径参数**:
+
+| 参数 | 类型 | 说明 |
+|------|------|------|
+| roleId | long | 角色 ID |
+
+**Query 参数** (`EmployeeSubListQuery`): page / size / sortBy / sortDir
+
+**响应**: `ResultPageResultEmployeeVO`
+
+---
+
+#### 1.8 统计角色关联的员工数
+
+```http
+GET /tenant/employees/count-by-role/{roleId}
+```
+
+**说明**: 返回该角色下本租户员工数,可用于删除角色前校验。
+
+**响应**: `ResultLong`
+
+---
+
+#### 1.9 导出员工(Excel)
+
+```http
+GET /tenant/employees/export
+```
+
+**说明**: 导出员工为 Excel 文件流(非标准 Result 封装)。
+
+**Query 参数**:
+
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| orgId | long | 否 | 不填导出全部,填则仅导出指定组织 |
+
+**响应**: Excel 文件流
+
+---
+
+#### 1.10 批量导入员工(Excel)
+
+```http
+POST /tenant/employees/import
+```
+
+**说明**: 上传 Excel 文件批量创建员工。
+
+**请求**: `multipart/form-data`,字段名 `file`
+
+**响应**: `ResultInteger`(成功导入行数)
+
+---
+
+### 2. 组织管理
+
+#### 2.1 创建组织
+
+```http
+POST /tenant/organizations
+```
+
+**说明**: 创建组织节点。`orgPath` 由服务端根据 `parentId` 自动生成,`status` 初始为 `true`。
+
+**请求体** (`OrgCreateDTO`):
+
+| 字段 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| orgName | string | ✅ | 组织名称 |
+| orgType | string | 否 | 类型:`HQ` / `BRANCH` / `DEALER` / `AGENT` / `DEPT` / `TEAM`(默认 `DEPT`) |
+| parentId | long | 否 | 上级组织 ID(根组织留空) |
+| leaderId | long | 否 | 负责人员工 ID |
+| region | string | 否 | 所在地区 |
+| sortOrder | integer | 否 | 同级排序(数字越小越靠前) |
+
+**响应**: `ResultOrgVO`
+
+---
+
+#### 2.2 获取组织树
+
+```http
+GET /tenant/organizations/tree
+```
+
+**说明**: 返回当前租户组织嵌套树。需 `TENANT_ADMIN` 或平台管理员。
+
+**Query 参数**:
+
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| orgId | long | 否 | 传入则返回以该节点为根的子树;不传返回所有根组织 |
+
+**响应**: `ResultListOrgVO`
+
+---
+
+#### 2.3 更新组织
+
+```http
+PUT /tenant/organizations/{id}
+```
+
+**说明**: 更新组织基本信息。`parentId` 变更后 `orgPath` 由服务端重算。
+
+**请求体**: `OrgCreateDTO`(同创建)
+
+**响应**: `ResultOrgVO`
+
+---
+
+#### 2.4 删除组织
+
+```http
+DELETE /tenant/organizations/{id}
+```
+
+**说明**: 删除组织。若存在子组织可能被拒绝。
+
+**响应**: `ResultVoid`
+
+---
+
+#### 2.5 启用/停用组织
+
+```http
+PUT /tenant/organizations/{id}/toggle-status
+```
+
+**说明**: 在启用/停用之间切换。
+
+**响应**: `ResultVoid`
+
+---
+
+### 3. 租户角色管理
+
+#### 3.1 当前租户角色列表(含预设角色)
+
+```http
+GET /tenant/roles
+```
+
+**说明**: 返回本租户自建角色 + SYSTEM 全租户共享的预置角色(如 `TENANT_ADMIN` / `PLATFORM_ADMIN`)。预置角色置前。需 `TENANT_ADMIN` 登录。
+
+**响应**: `ResultListRoleVO`
+
+---
+
+#### 3.2 获取角色详情
+
+```http
+GET /tenant/roles/{id}
+```
+
+**路径参数**:
+
+| 参数 | 类型 | 说明 |
+|------|------|------|
+| id | long | 角色 ID |
+
+**响应**: `ResultRoleVO`
+
+---
+
+#### 3.3 启用/停用角色
+
+```http
+PUT /tenant/roles/{id}/toggle-status
+```
+
+**说明**:  
+- 仅允许租户管理员操作本租户的**自建角色**  
+- 预置角色(`roleType=PRESET`)及跨租户角色会被拒绝(403)
+
+**响应**: `ResultVoid`
+
+---
+
+### 4. 客户管理(后台 · 只读)
+
+#### 4.1 本租户客户列表(只读)
+
+```http
+GET /tenant/customers
+```
+
+**说明**: 分页返回本租户所有客户(只读)。`ownerId` 不填=全部。
+
+**Query 参数** (`TenantCustomerPageQuery`):
+
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| page | integer | 否 | 页码 |
+| size | integer | 否 | 每页条数 |
+| ownerId | long | 否 | 按归属销售员过滤 |
+
+**响应**: `ResultPageResultCustomerVO`
+
+---
+
+#### 4.2 客户详情(只读)
+
+```http
+GET /tenant/customers/{id}
+```
+
+**说明**: 租户管理员可见本租户任意客户(未做归属校验)。
+
+**响应**: `ResultCustomerVO`
+
+---
+
+#### 4.3 客户跟进历史(只读)
+
+```http
+GET /tenant/customers/{id}/follow-ups
+```
+
+**说明**: 分页返回该客户的跟进历史,按 `followAt` 倒序。
+
+**Query 参数** (`FollowUpPageQuery`): page / size / sortBy / sortDir
+
+**响应**: `ResultPageResultFollowUpVO`
+
+---
+
+### 5. 审计日志(租户端)
+
+#### 5.1 本租户审计日志分页查询
+
+```http
+GET /tenant/audit-logs
+```
+
+**说明**: 强制按当前登录租户查询。需 `TENANT_ADMIN` 或平台管理员。
+
+**Query 参数** (`AuditLogPageQuery`):
+
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| page | integer | 否 | 页码 |
+| size | integer | 否 | 每页条数 |
+| module | string | 否 | 模块过滤(EMPLOYEE / ORG / ROLE / CUSTOMER / FOLLOW_UP / TENANT / SYSTEM 等) |
+| action | string | 否 | 操作动作(CREATE / UPDATE / DELETE / IMPORT 等) |
+| entityType | string | 否 | 实体类型 |
+| resultStatus | string | 否 | 结果状态(SUCCESS / FAILURE) |
+| operatorId | long | 否 | 操作人 ID |
+| start | date-time | 否 | 开始时间(yyyy-MM-dd HH:mm) |
+| end | date-time | 否 | 结束时间(yyyy-MM-dd HH:mm) |
+
+**响应**: `ResultPageResultAuditLogVO`
+
+---
+
+## 数据模型(Schemas)
+
+### EmployeeVO(员工响应体)
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| id | long | 主键 |
+| createdAt | date-time | 创建时间 |
+| updatedAt | date-time | 更新时间 |
+| createdBy | string | 创建人 |
+| updatedBy | string | 更新人 |
+| tenantId | string | 所属租户 ID |
+| deleted | integer | 软删除标记(0=未删除,1=已删除) |
+| name | string | 员工姓名 |
+| phone | string | 手机号 |
+| orgId | long | 所属组织 ID |
+| roleId | long | 角色 ID |
+| position | string | 职位 |
+| email | string | 邮箱 |
+| avatarUrl | string | 头像 URL |
+| status | string | `ACTIVE` / `DISABLED` |
+| holdLimit | integer | 客户持有上限 |
+| region | string | 负责区域 |
+| hireDate | date | 入职日期 |
+
+---
+
+### OrgVO(组织响应体)
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| id | long | 主键 |
+| orgName | string | 组织名称 |
+| orgType | string | `HQ` / `BRANCH` / `DEALER` / `AGENT` / `DEPT` / `TEAM` |
+| parentId | long | 上级组织 ID |
+| orgPath | string | 组织路径(如 `/默认租户/华东分公司`) |
+| leaderId | long | 负责人 ID |
+| region | string | 所在地区 |
+| sortOrder | integer | 排序 |
+| status | boolean | 启用状态 |
+| children | array\<OrgVO\> | 直接子节点列表 |
+
+---
+
+### RoleVO(角色响应体)
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| id | long | 主键 |
+| roleName | string | 角色名称 |
+| roleCode | string | 角色编码(预置如 `TENANT_ADMIN`) |
+| description | string | 描述 |
+| roleType | string | `PRESET`(预置) / `CUSTOM`(自建) |
+| status | string | `ENABLED` / `DISABLED` / `DELETED` |
+
+---
+
+### CustomerVO(客户响应体)
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| id | long | 主键 |
+| name | string | 客户名称 |
+| customerType | string | `ENTERPRISE` / `PERSONAL` |
+| industry | string | 所属行业 |
+| region | string | 所在地区 |
+| contactName | string | 主要联系人 |
+| contactPhone | string | 主要联系电话 |
+| contactPosition | string | 职位 |
+| contactTel | string | 备用电话 |
+| email | string | 邮箱 |
+| address | string | 地址 |
+| demand | string | 需求描述 |
+| source | string | `MINI_PROGRAM` / `SALES_INPUT` / `EXHIBITION` / `REFERRAL` / `WEBSITE` |
+| level | string | `VIP` / `A` / `B` / `C` |
+| tags | string | 标签(逗号分隔) |
+| orgId | long | 所属组织 |
+| ownerId | long | 归属销售员 ID |
+| stage | string | `NEW_LEAD` / `PENDING_ASSIGN` / `CONTACTING` / `NEEDS_COMM` / `QUOTING` / `NEGOTIATING` / `DEAL` / `LOST` |
+| lastFollowAt | date-time | 最近跟进时间 |
+| nextFollowAt | date-time | 下次计划跟进时间 |
+| dealAt | date-time | 成交时间 |
+| lostAt | date-time | 流失时间 |
+| lostReason | string | 流失原因 |
+
+---
+
+### FollowUpVO(跟进记录响应体)
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| id | long | 主键 |
+| customerId | long | 关联客户 ID |
+| followerId | long | 跟进人 ID |
+| followAt | date-time | 跟进实际时间 |
+| followMethod | string | `PHONE` / `WECHAT` / `VISIT` / `EMAIL` / `MEETING` |
+| content | string | 跟进内容 |
+| nextFollowAt | date-time | 下次计划时间 |
+| nextFollowPlan | string | 下次计划备注 |
+| followStatus | string | `DONE` / `PENDING` / `OVERDUE` |
+| recordType | string | `FOLLOW` / `PLAN` / `STAGE_CHANGE` |
+| stageFrom | string | 阶段变化来源 |
+| stageTo | string | 阶段变化目标 |
+
+---
+
+### AuditLogVO(审计日志响应体)
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| logId | long | 日志主键 |
+| tenantId | string | 租户 ID |
+| operatorId | long | 操作人 ID |
+| operatorName | string | 操作人姓名 |
+| operatorRole | string | 操作人角色 |
+| module | string | 业务模块 |
+| action | string | 操作动作 |
+| operationDesc | string | 操作描述(中文摘要) |
+| entityType | string | 实体类型 |
+| entityId | string | 实体 ID |
+| entityName | string | 实体名称 |
+| changeDetail | string | 变更明细 |
+| requestIp | string | 请求 IP |
+| userAgent | string | User-Agent |
+| requestPath | string | 请求路径 |
+| requestMethod | string | 请求方法 |
+| resultStatus | string | `SUCCESS` / `FAILURE` |
+| errorMessage | string | 失败错误消息 |
+| createdAt | date-time | 创建时间 |
+
+---
+
+## 权限说明
+
+| 角色/权限 | 可访问接口 |
+|-----------|------------|
+| TENANT_ADMIN | 本租户员工、组织、角色、客户(只读)、审计日志 |
+| 平台管理员 | 以上所有 + 跨租户能力 |
+| 普通员工 | 根据业务接口权限控制 |
+
+---
+
+*文档根据 OpenAPI 规范自动生成,内容以实际接口返回为准。*
diff --git a/src/pages/system/roles/index.tsx b/src/pages/system/roles/index.tsx
index 28342f5..1d6f6ac 100644
--- a/src/pages/system/roles/index.tsx
+++ b/src/pages/system/roles/index.tsx
@@ -31,7 +31,6 @@
   const handleToggle = useCallback(
     (record: RoleVO) => {
       const nextAction = record.status === 'ENABLED' ? '禁用' : '启用';
-
       modal.confirm({
         title: `${nextAction}角色`,
         content: `确定要${nextAction}角色「${record.roleName}」吗?`,
diff --git a/src/pages/system/users/components/UserFormDrawer.tsx b/src/pages/system/users/components/UserFormDrawer.tsx
new file mode 100644
index 0000000..aac230f
--- /dev/null
+++ b/src/pages/system/users/components/UserFormDrawer.tsx
@@ -0,0 +1,211 @@
+import { useCallback, useEffect, useState } from 'react';
+import {
+  App as AntdApp,
+  Button,
+  Drawer,
+  Form,
+  Input,
+  InputNumber,
+  Select,
+  Space,
+  Spin,
+} from 'antd';
+import { createEmployee, getEmployeeDetail, updateEmployee } from '../server';
+import type { EmployeeFormValues, EmployeeVO, SelectOption } from '../types';
+
+export type UserFormMode = 'create' | 'edit' | 'detail';
+
+interface UserFormDrawerProps {
+  open: boolean;
+  mode: UserFormMode;
+  /** 编辑 / 详情对象;新增时为空。 */
+  record: EmployeeVO | null;
+  orgOptions: SelectOption[];
+  roleOptions: SelectOption[];
+  onClose: () => void;
+  onSuccess: () => void;
+}
+
+const toFormValues = (employee: EmployeeVO): EmployeeFormValues => ({
+  name: employee.name,
+  phone: employee.phone,
+  orgId: employee.orgId,
+  roleId: employee.roleId,
+  position: employee.position,
+  email: employee.email,
+  holdLimit: employee.holdLimit,
+  region: employee.region,
+});
+
+const UserFormDrawer = ({
+  open,
+  mode,
+  record,
+  orgOptions,
+  roleOptions,
+  onClose,
+  onSuccess,
+}: UserFormDrawerProps) => {
+  const [form] = Form.useForm<EmployeeFormValues>();
+  const [submitting, setSubmitting] = useState(false);
+  const [loading, setLoading] = useState(false);
+  const { message } = AntdApp.useApp();
+  const isDetail = mode === 'detail';
+
+  useEffect(() => {
+    if (!open) return;
+
+    let active = true;
+
+    const initForm = async () => {
+      form.resetFields();
+
+      if (mode === 'detail') {
+        if (!record) return;
+
+        setLoading(true);
+        try {
+          const data = await getEmployeeDetail({ id: record.id });
+          if (active) form.setFieldsValue(toFormValues(data));
+        } catch {
+          // 错误已由 request 统一提示
+          if (active) onClose();
+        } finally {
+          if (active) setLoading(false);
+        }
+        return;
+      }
+
+      if (mode === 'edit' && record) {
+        form.setFieldsValue(toFormValues(record));
+      }
+    };
+
+    void initForm();
+
+    return () => {
+      active = false;
+    };
+  }, [form, mode, onClose, open, record]);
+
+  const handleSubmit = useCallback(async () => {
+    if (isDetail) return;
+
+    let values: EmployeeFormValues;
+    try {
+      values = await form.validateFields();
+    } catch {
+      return;
+    }
+
+    setSubmitting(true);
+    try {
+      if (mode === 'edit' && record) {
+        await updateEmployee({ id: record.id, ...values });
+        message.success('员工信息已更新');
+      } else {
+        await createEmployee(values);
+        message.success('员工创建成功');
+      }
+
+      onSuccess();
+    } catch {
+      // 错误已由 request 统一提示
+    } finally {
+      setSubmitting(false);
+    }
+  }, [form, isDetail, message, mode, onSuccess, record]);
+
+  const title = mode === 'create' ? '新增员工' : mode === 'edit' ? '编辑员工' : '员工详情';
+
+  return (
+    <Drawer
+      title={title}
+      width={420}
+      open={open}
+      forceRender
+      onClose={onClose}
+      footer={
+        <Space style={{ display: 'flex', justifyContent: 'flex-end' }}>
+          <Button onClick={onClose}>{isDetail ? '关闭' : '取消'}</Button>
+          {!isDetail && (
+            <Button type="primary" loading={submitting} onClick={handleSubmit}>
+              {mode === 'edit' ? '保存' : '创建'}
+            </Button>
+          )}
+        </Space>
+      }
+    >
+      <Spin spinning={loading}>
+        <Form form={form} layout="vertical" preserve={false}>
+          <Form.Item
+            name="name"
+            label="姓名"
+            rules={[{ required: true, message: '请输入姓名' }]}
+          >
+            <Input placeholder="请输入员工姓名" disabled={isDetail} />
+          </Form.Item>
+          <Form.Item
+            name="phone"
+            label="手机号"
+            rules={[
+              { required: true, message: '请输入手机号' },
+              { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号' },
+            ]}
+          >
+            <Input placeholder="请输入手机号" maxLength={11} disabled={isDetail} />
+          </Form.Item>
+          <Form.Item
+            name="orgId"
+            label="所属组织"
+            rules={[{ required: true, message: '请选择所属组织' }]}
+          >
+            <Select
+              showSearch
+              optionFilterProp="label"
+              options={orgOptions}
+              placeholder="请选择所属组织"
+              disabled={isDetail}
+            />
+          </Form.Item>
+          <Form.Item
+            name="roleId"
+            label="角色"
+            rules={[{ required: true, message: '请选择角色' }]}
+          >
+            <Select
+              showSearch
+              optionFilterProp="label"
+              options={roleOptions}
+              placeholder="请选择角色"
+              disabled={isDetail}
+            />
+          </Form.Item>
+          <Form.Item name="position" label="职位">
+            <Input placeholder="请输入职位" disabled={isDetail} />
+          </Form.Item>
+          <Form.Item name="region" label="负责区域">
+            <Input placeholder="请输入负责区域" disabled={isDetail} />
+          </Form.Item>
+          <Form.Item name="holdLimit" label="客户持有上限">
+            <InputNumber
+              min={0}
+              precision={0}
+              style={{ width: '100%' }}
+              disabled={isDetail}
+            />
+          </Form.Item>
+          <Form.Item
+            name="email"
+            label="邮箱"
+            rules={[{ type: 'email', message: '请输入正确的邮箱' }]}
+          >
+            <Input placeholder="请输入邮箱" disabled={isDetail} />
+          </Form.Item>
+        </Form>
+      </Spin>
+    </Drawer>
+  );
+};
+
+export default UserFormDrawer;
diff --git a/src/pages/system/users/index.tsx b/src/pages/system/users/index.tsx
index 15711c8..36d9d32 100644
--- a/src/pages/system/users/index.tsx
+++ b/src/pages/system/users/index.tsx
@@ -7,35 +7,20 @@
 } from '@ant-design/icons';
 import { ProTable } from '@ant-design/pro-components';
 import type { ActionType } from '@ant-design/pro-components';
-import {
-  App as AntdApp,
-  Button,
-  Descriptions,
-  Drawer,
-  Form,
-  Input,
-  InputNumber,
-  Modal,
-  Select,
-  Space,
-  Tag,
-  Upload,
-} from 'antd';
+import { App as AntdApp, Button, Modal, Upload } from 'antd';
 import type { UploadFile } from 'antd';
-import { createUserColumns, getEmployeeStatusMeta } from './columns';
+import { createUserColumns } from './columns';
+import UserFormDrawer from './components/UserFormDrawer';
+import type { UserFormMode } from './components/UserFormDrawer';
 import {
-  createEmployee,
   deleteEmployee,
   exportEmployees,
-  getEmployeeDetail,
   getEmployeeList,
   getOrgTree,
   getRoleOptions,
   importEmployees,
-  updateEmployee,
 } from './server';
 import type {
-  EmployeeFormValues,
   EmployeeListQuery,
   EmployeeVO,
   OrgTreeNode,
@@ -54,15 +39,11 @@
   const actionRef = useRef<ActionType>();
   const lastQueryRef = useRef<TenantEmployeeListRequest>({});
   const { message, modal } = AntdApp.useApp();
-  const [form] = Form.useForm<EmployeeFormValues>();
   const [orgOptions, setOrgOptions] = useState<SelectOption[]>([]);
   const [roleOptions, setRoleOptions] = useState<SelectOption[]>([]);
   const [formOpen, setFormOpen] = useState(false);
-  const [editing, setEditing] = useState<EmployeeVO | null>(null);
-  const [submitting, setSubmitting] = useState(false);
-  const [detail, setDetail] = useState<EmployeeVO | null>(null);
-  const [detailOpen, setDetailOpen] = useState(false);
-  const [detailLoading, setDetailLoading] = useState(false);
+  const [formMode, setFormMode] = useState<UserFormMode>('create');
+  const [activeEmployee, setActiveEmployee] = useState<EmployeeVO | null>(null);
   const [importOpen, setImportOpen] = useState(false);
   const [importFileList, setImportFileList] = useState<UploadFile[]>([]);
   const [importing, setImporting] = useState(false);
@@ -101,48 +82,31 @@
   );
 
   const openCreate = useCallback(() => {
-    setEditing(null);
-    form.resetFields();
+    setActiveEmployee(null);
+    setFormMode('create');
     setFormOpen(true);
-  }, [form]);
+  }, []);
 
   const closeForm = useCallback(() => {
     setFormOpen(false);
-    setEditing(null);
+    setActiveEmployee(null);
   }, []);
 
-  const openEdit = useCallback(
-    (record: EmployeeVO) => {
-      setEditing(record);
-      form.setFieldsValue({
-        name: record.name,
-        phone: record.phone,
-        orgId: record.orgId,
-        roleId: record.roleId,
-        position: record.position,
-        email: record.email,
-        holdLimit: record.holdLimit,
-        region: record.region,
-      });
-      setFormOpen(true);
-    },
-    [form],
-  );
+  const handleFormSuccess = useCallback(() => {
+    closeForm();
+    actionRef.current?.reload();
+  }, [closeForm]);
 
-  const handleDetail = useCallback(async (record: EmployeeVO) => {
-    setDetail(null);
-    setDetailOpen(true);
-    setDetailLoading(true);
+  const openEdit = useCallback((record: EmployeeVO) => {
+    setActiveEmployee(record);
+    setFormMode('edit');
+    setFormOpen(true);
+  }, []);
 
-    try {
-      const data = await getEmployeeDetail({ id: record.id });
-      setDetail(data);
-    } catch {
-      // 错误已由 request 统一提示
-      setDetailOpen(false);
-    } finally {
-      setDetailLoading(false);
-    }
+  const openDetail = useCallback((record: EmployeeVO) => {
+    setActiveEmployee(record);
+    setFormMode('detail');
+    setFormOpen(true);
   }, []);
 
   const handleDelete = useCallback(
@@ -162,35 +126,6 @@
     },
     [message, modal],
   );
-
-  const handleSubmit = useCallback(async () => {
-    let values: EmployeeFormValues;
-    try {
-      values = await form.validateFields();
-    } catch {
-      return;
-    }
-
-    setSubmitting(true);
-    try {
-      if (editing) {
-        await updateEmployee({ id: editing.id, ...values });
-        message.success('员工信息已更新');
-      } else {
-        await createEmployee(values);
-        message.success('员工创建成功');
-      }
-
-      setFormOpen(false);
-      setEditing(null);
-      form.resetFields();
-      actionRef.current?.reload();
-    } catch {
-      // 错误已由 request 统一提示
-    } finally {
-      setSubmitting(false);
-    }
-  }, [editing, form, message]);
 
   const handleExport = useCallback(async () => {
     setExporting(true);
@@ -240,11 +175,11 @@
         roleOptions,
         orgNameMap,
         roleNameMap,
-        onDetail: handleDetail,
+        onDetail: openDetail,
         onEdit: openEdit,
         onDelete: handleDelete,
       }),
-    [orgOptions, roleOptions, orgNameMap, roleNameMap, handleDetail, openEdit, handleDelete],
+    [orgOptions, roleOptions, orgNameMap, roleNameMap, openDetail, openEdit, handleDelete],
   );
 
   return (
@@ -307,118 +242,15 @@
         }}
       />
 
-      <Drawer
-        title={editing ? '编辑员工' : '新增员工'}
-        width={420}
+      <UserFormDrawer
         open={formOpen}
-        forceRender
+        mode={formMode}
+        record={activeEmployee}
+        orgOptions={orgOptions}
+        roleOptions={roleOptions}
         onClose={closeForm}
-        footer={
-          <Space style={{ display: 'flex', justifyContent: 'flex-end' }}>
-            <Button onClick={closeForm}>取消</Button>
-            <Button type="primary" loading={submitting} onClick={handleSubmit}>
-              {editing ? '保存' : '创建'}
-            </Button>
-          </Space>
-        }
-      >
-        <Form form={form} layout="vertical" preserve={false}>
-          <Form.Item
-            name="name"
-            label="姓名"
-            rules={[{ required: true, message: '请输入姓名' }]}
-          >
-            <Input placeholder="请输入员工姓名" />
-          </Form.Item>
-          <Form.Item
-            name="phone"
-            label="手机号"
-            rules={[
-              { required: true, message: '请输入手机号' },
-              { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号' },
-            ]}
-          >
-            <Input placeholder="请输入手机号" maxLength={11} />
-          </Form.Item>
-          <Form.Item
-            name="orgId"
-            label="所属组织"
-            rules={[{ required: true, message: '请选择所属组织' }]}
-          >
-            <Select
-              showSearch
-              optionFilterProp="label"
-              options={orgOptions}
-              placeholder="请选择所属组织"
-            />
-          </Form.Item>
-          <Form.Item
-            name="roleId"
-            label="角色"
-            rules={[{ required: true, message: '请选择角色' }]}
-          >
-            <Select
-              showSearch
-              optionFilterProp="label"
-              options={roleOptions}
-              placeholder="请选择角色"
-            />
-          </Form.Item>
-          <Form.Item name="position" label="职位">
-            <Input placeholder="请输入职位" />
-          </Form.Item>
-          <Form.Item name="region" label="负责区域">
-            <Input placeholder="请输入负责区域" />
-          </Form.Item>
-          <Form.Item name="holdLimit" label="客户持有上限">
-            <InputNumber min={0} precision={0} style={{ width: '100%' }} />
-          </Form.Item>
-          <Form.Item
-            name="email"
-            label="邮箱"
-            rules={[{ type: 'email', message: '请输入正确的邮箱' }]}
-          >
-            <Input placeholder="请输入邮箱" />
-          </Form.Item>
-        </Form>
-      </Drawer>
-
-      <Drawer
-        title="员工详情"
-        width={480}
-        open={detailOpen}
-        loading={detailLoading}
-        onClose={() => {
-          setDetailOpen(false);
-          setDetail(null);
-        }}
-      >
-        {detail && (
-          <Descriptions column={1} bordered size="small">
-            <Descriptions.Item label="姓名">{detail.name}</Descriptions.Item>
-            <Descriptions.Item label="手机号">{detail.phone}</Descriptions.Item>
-            <Descriptions.Item label="所属组织">
-              {orgNameMap.get(detail.orgId) ?? `#${detail.orgId}`}
-            </Descriptions.Item>
-            <Descriptions.Item label="角色">
-              {roleNameMap.get(detail.roleId) ?? `#${detail.roleId}`}
-            </Descriptions.Item>
-            <Descriptions.Item label="职位">{detail.position || '-'}</Descriptions.Item>
-            <Descriptions.Item label="邮箱">{detail.email || '-'}</Descriptions.Item>
-            <Descriptions.Item label="负责区域">{detail.region || '-'}</Descriptions.Item>
-            <Descriptions.Item label="客户持有上限">
-              {detail.holdLimit ?? '-'}
-            </Descriptions.Item>
-            <Descriptions.Item label="状态">
-              <Tag color={getEmployeeStatusMeta(detail.status).color} bordered={false}>
-                {getEmployeeStatusMeta(detail.status).text}
-              </Tag>
-            </Descriptions.Item>
-            <Descriptions.Item label="入职日期">{detail.hireDate || '-'}</Descriptions.Item>
-            <Descriptions.Item label="创建时间">{detail.createdAt || '-'}</Descriptions.Item>
-          </Descriptions>
-        )}
-      </Drawer>
+        onSuccess={handleFormSuccess}
+      />
 
       <Modal
         title="导入员工"
diff --git a/src/pages/system/users/types.ts b/src/pages/system/users/types.ts
index 1e08839..1b91cdb 100644
--- a/src/pages/system/users/types.ts
+++ b/src/pages/system/users/types.ts
@@ -148,7 +148,7 @@
   roleId?: number;
 }
 
-/** 新增 / 编辑员工表单值。 */
+/** 新增 / 编辑 / 详情表单值。 */
 export interface EmployeeFormValues {
   name: string;
   phone: string;

--
Gitblit v1.9.3