# refactor(product): 「添加团队成员」角色弹窗改为 staff_type 字典动态获取

**日期**：2026-05-21
**PR**：无（后端零改动，字典与字典查询接口均已就绪）
**工单**：[#2790](https://git.1814.love:8443/wx/HL/issues/2790)（同步关闭 hl-api-changelog [#7](https://git.1814.love:8443/wx/hl-api-changelog/issues/7)）
**影响端**：管理后台前端（hl-ui）

---

## 背景

产品编辑页 → 「添加团队成员」弹窗（`hl-ui/src/views/product/edit/components/modals/StaffPickerModal.vue:23-28`）的「选择角色」区域当前是 4 个 **写死** 的标签：

```js
const roleOptions = [
  { label: '主领队',  value: 'LEADER',         limit: 1 },
  { label: '副领队',  value: 'DEPUTY_LEADER',  limit: 3 },  // 后端字典无此值
  { label: '摄影师',  value: 'PHOTOGRAPHER',   limit: 2 },
  { label: '司机',    value: 'DRIVER',         limit: 5 },  // 业务上不属于服务人员
]
```

**问题**：
1. 后端 `staff_type` 字典里**根本没有 DEPUTY_LEADER**，前端保存后字典翻译不出中文，是孤儿值。
2. 业务确认：**服务人员不包括司机**，DRIVER 不应出现在团队成员候选里。
3. 后端 `staff_type` 字典已扩展到 7 项（导游 / 导游助理 / 摄影师 / 领队 / 其他 / 研学老师 / 生活老师），前端写死 4 项导致「导游 / 导游助理 / 研学老师 / 生活老师」都选不到——hl-api-changelog#7 已反馈过同一问题。

---

## 业务决策（wx 已确认）

- **角色列表来源**：直接复用 **服务人员管理页「人员类型」那列对应的 `staff_type` 字典**（不要再造新字典）
- **主/副领队合并为 LEADER**：同一团里可加多个 LEADER，不在角色层区分主副
- **司机不出现在候选**：司机另由车辆/驾驶员模块管理
- **后续可扩展**：业务再加 staff_type 字典值时前端零改动自动出现

---

## 字典数据（staff_type，测试服已落库）

通过 `GET /admin/dict/data/staff_type` 拉取：

| sortOrder | dictValue | dictLabel | 说明 |
|-----------|-----------|-----------|------|
| 1 | `GUIDE` | 导游 | |
| 2 | `GUIDE_ASSISTANT` | 导游助理 | |
| 3 | `PHOTOGRAPHER` | 摄影师 | |
| 4 | `LEADER` | 领队 | |
| 5 | `OTHER` | 其他 | |
| — | (业务追加) | 研学老师 / 生活老师 / ... | 字典动态扩展，前端零改动 |

> 实际项数以接口返回为准（后续业务可能继续追加），不要在前端写死个数或枚举值。

---

## 🚨 前端需要做的事

### 1. 删除硬编码

文件：`hl-ui/src/views/product/edit/components/modals/StaffPickerModal.vue`

把第 23-28 行的 `roleOptions` 硬编码删掉。

### 2. 改成调字典接口

调用：`GET /admin/dict/data/staff_type`（已存在的通用字典 API）

**Header**：需要 admin token

**响应结构**：

```json
{
  "code": 200,
  "message": "成功",
  "data": [
    { "dictValue": "GUIDE",           "dictLabel": "导游",     "sortOrder": 1, "status": "ACTIVE" },
    { "dictValue": "GUIDE_ASSISTANT", "dictLabel": "导游助理", "sortOrder": 2, "status": "ACTIVE" },
    { "dictValue": "PHOTOGRAPHER",    "dictLabel": "摄影师",   "sortOrder": 3, "status": "ACTIVE" },
    { "dictValue": "LEADER",          "dictLabel": "领队",     "sortOrder": 4, "status": "ACTIVE" },
    { "dictValue": "OTHER",           "dictLabel": "其他",     "sortOrder": 5, "status": "ACTIVE" }
    // 业务追加的值按字典实际数量返回
  ]
}
```

### 3. 下拉/标签组绑定动态数据

```js
// 推荐：直接复用全局 dictStore（如已有）
import { useDictStore } from '@/stores/dict'
const dictStore = useDictStore()
const roleOptions = computed(() =>
  dictStore.getDictData('staff_type').map(d => ({
    label: d.dictLabel,
    value: d.dictValue,
  }))
)
```

如果暂时没全局字典 store，组件级 `onMounted` 拉一次即可（字典后端有 5 分钟 Redis 缓存）。

### 4. 联动 `getStaffPage`

弹窗下半段调 `getStaffPage({ staffType: selectedRole.value, ... })` 时，`staffType` 参数直接传字典 `dictValue`（无需做映射，前后端值一致）。

### 5. `limit`（人数上限）

业务暂无强制需求，前端可：
- A. 不限制（推荐，简化逻辑）
- B. 前端约定一份固定 map（不影响后端）
- C. 后续如需在字典 `remark` 放 `{"limit":N}` JSON，后端可补，前端解析

---

## 关联清理（mmg 请一并自查）

`hl-api-changelog#7` 还提到："请扫描代码中是否有其他地方也硬编码了这套角色枚举（订单成员配置、薪酬结算、小程序"服务团队"展示等）"。

请 mmg 一并核查以下位置是否硬编码同一份枚举，如有请同步改造为字典化：
- 订单详情/列表的成员展示
- 小程序「服务团队」展示
- 团队成员编辑/批量编辑组件
- 各页面 staff_type 标签颜色 map

---

## 历史数据

后端有零代码改动，但需关注产品快照里是否已有 `DEPUTY_LEADER` / `DRIVER` 孤儿值：

```sql
-- 测试服执行核查（hl_product_service_v2 数据库）
SELECT DISTINCT staff_role, COUNT(*)
FROM group_batch_staff
GROUP BY staff_role;
```

- 如有 `DEPUTY_LEADER`：评估迁移为 `LEADER`
- 如有 `DRIVER`：评估删除或归 `OTHER`
- 如查询结果都是 `LEADER` / `PHOTOGRAPHER` / `GUIDE` 等正常值：无需迁移

后端将由 wx 在 [#2790](https://git.1814.love:8443/wx/HL/issues/2790) 关单 comment 中给出实际数据扫描结果。

---

## 兼容性

- ✅ 后端零改动，无回归风险
- ✅ 字典服务挂掉时前端可 fallback 到 staff_type 字典已有的固定 5 项
- ✅ 字典本身有 Redis 5 分钟缓存，QPS 无压力

---

## 联系人

后端：wx（呼籁旅行）
前端：mmg

如有疑问或字典接口路径需要确认，可在 [#2790](https://git.1814.love:8443/wx/HL/issues/2790) 评论区留言。
