# 前端 Bug 通知 — Step4 定价管理 顶部档位 Tab 只显示已有价格的档位

- **日期**: 2026-04-17
- **类型**: 前端 Bug 修复指南（**不涉及后端代码改动，后端无需重启**）
- **涉及端**: 管理后台 hl-ui（产品编辑页 Step4 定价管理）
- **后端服务**: hl-product-service-v2（**后端已核实数据完全正确，无需改动**）

---

## 一、问题描述

在产品编辑页 Step4「定价管理」顶部档位切换区域：

- **预期**：显示产品 Step1 配置的**全部档位 tab**（例如 5 档：`经济 / 舒适 / 轻奢 / 高端 / 豪华`），每个档位都有独立 tab 可点击切换，用户可以给任何档位添加价格
- **实际**：**只显示已有价格数据的档位 tab**。若用户只给其中 1 档（如"舒适"）设置过价格，顶部就只显示 1 个灰色 tag「舒适」，其他 4 档没有 tab → **用户无法切换到那 4 档去添加价格**，定价页陷入"单档死循环"

**复现路径**：
- 环境：测试服 `192.168.100.160`
- 产品 ID：`2045018534152478721`
- 页面：产品编辑 → Step4 定价管理
- 前置：Step1 配置了 5 档（经济/舒适/轻奢/高端/豪华），且只对其中某 1 档设置过价格

**截图现象**：
- 顶部只有 1 个 tag「舒适」（对应 tierSeq=2 的价格记录）
- 价格列表显示 ¥7,777 / ¥3,333 / ¥500 / ¥500
- **其他 4 个档位的 tab 完全缺失**

---

## 二、根因分析（后端已核实无误）

### 1. 后端接口返回数据 100% 正确

调用 `GET /admin/product/item/2045018534152478721`，响应：
```json
{
  "code": 200,
  "data": {
    "tiers": [
      { "tierSeq": 1, "tierName": "经济", "tierDescription": "快捷酒店/双床标间" },
      { "tierSeq": 2, "tierName": "舒适", "tierDescription": "四星酒店/河景房" },
      { "tierSeq": 3, "tierName": "轻奢", "tierDescription": "五星酒店/高级套房" },
      { "tierSeq": 4, "tierName": "高端", "tierDescription": "精品度假酒店/江景套房" },
      { "tierSeq": 5, "tierName": "豪华", "tierDescription": "奢华度假庄园/别墅" }
    ]
  }
}
```

调用 `GET /admin/product/item/2045018534152478721/price-calendar?month=2026-04`，返回 30 条记录，全部 `tierSeq=2`（对应"舒适"档用户历史设置的价格）。

所以：
- 顶部显示「舒适」✓ **与 DB 真实数据完全对应**（DB 里只有 tierSeq=2 的记录）
- 其他档位没 tab 是因为 DB 里没有对应 tierSeq 的价格记录

### 2. Bug 所在代码

**文件**：`src/views/product/edit/components/steps/PricingStep.vue`

**问题代码**（大约 218-227 行，顶部档位切换 NButtonGroup 渲染处）：
```vue
<NButtonGroup v-if="isDual" size="small">
  <NButton
    v-for="seq in detectedTierSeqs"   <!-- ❌ 错！只遍历已有价格的档位 -->
    :key="seq"
    :type="currentTierSeq === seq ? 'primary' : 'default'"
    @click="currentTierSeq = seq"
  >
    {{ getTierLabel(seq) }}
  </NButton>
</NButtonGroup>
```

相关变量定义（大约 39-43 行）：
```js
const tiers = computed(() => store.productData.tiers || [])           // Step1 配置的全部档位（5 档）
const isDual = computed(() => tiers.value.length > 1 || detectedTierSeqs.value.length > 1)
const currentTierSeq = ref(1)
const detectedTierSeqs = ref([])  // 从价格日历数据中反向检测到的档位
```

`detectedTierSeqs` 在 `loadCalendar` 里从已有价格数据反向 collect（大约 106 行）：
```js
const seqs = [...new Set(rawCalendarData.map(d => d.tierSeq).filter(Boolean))].sort()
detectedTierSeqs.value = seqs
```

→ 当 DB 里只有 tierSeq=2 的记录，`detectedTierSeqs = [2]` → 顶部只渲染 1 个 tab 「舒适」。

---

## 三、修复方案（前端改动）

### 核心改动
**顶部档位 tab 应遍历 `tiers`（Step1 配置的全部档位），而不是 `detectedTierSeqs`（从数据反推的档位）**。

### 具体修改

**文件**：`src/views/product/edit/components/steps/PricingStep.vue`

**改法**（`v-for` 的集合从 `detectedTierSeqs` 改为 `tiers`）：
```vue
<NButtonGroup v-if="isDual" size="small">
  <NButton
    v-for="tier in tiers"
    :key="tier.tierSeq"
    :type="currentTierSeq === tier.tierSeq ? 'primary' : 'default'"
    @click="currentTierSeq = tier.tierSeq"
  >
    {{ tier.tierName || `档位${tier.tierSeq}` }}
  </NButton>
</NButtonGroup>
```

### 同步注意
1. **`isDual` 判断条件保留**：保持 `tiers.value.length > 1 || detectedTierSeqs.value.length > 1`（某些老数据可能没 tiers 但有价格，兜底用 detectedTierSeqs 判 isDual）
2. **`currentTierSeq` 初始化**：建议在 `loadCalendar` 之后做一次校正，如果 `currentTierSeq` 不在 `tiers` 列表里，fallback 到 `tiers[0].tierSeq`，避免默认的 `ref(1)` 在"档位 1 没配置"的产品上访问不到对应价格
3. **`filterAndAggregate` 过滤条件**：当前判断 `detectedTierSeqs.length > 1` 才 filter，改后建议改成 `tiers.length > 1`（因为切换 tab 是按 Step1 档位切，不按检测出来的档位切）
4. **无需修改 `PriceIntervalEditor.vue`**：该组件里 `tierOptions` 已经是 `store.productData.tiers` 动态生成（弹窗内 5 个档位按钮显示正常）

### 回归测试点
- 多档产品（tiers=5）且只给 1 档配过价格 → 顶部应显示 5 个 tab，可自由切换
- 多档产品且 5 档都配过价格 → 顶部 5 个 tab，每个切换后显示对应档位的价格区间
- 单档产品（tiers=1）→ 顶部不显示 NButtonGroup（isDual=false）
- 切换档位 tab 后：
  - 价格区间列表过滤显示该档位的数据
  - 新增/编辑价格区间时默认 tierSeq = 当前选中的 tab（不是硬编码的 1）
  - 删除价格时 `deletePriceCalendar(..., { tierSeq: currentTierSeq.value })` 能正确删除

---

## 四、后端是否有配合改动

**无**。

后端数据结构、接口行为、字段定义**完全不变**。本 bug 是纯前端渲染逻辑问题。

---

## 五、相关接口清单（不变，仅作参考）

| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/admin/product/item/{id}` | 产品详情（返回完整 tiers 列表） |
| GET | `/admin/product/item/{id}/price-calendar?month=YYYY-MM` | 查询某月价格日历（返回含 tierSeq 的价格记录列表） |
| POST | `/admin/product/item/{id}/price-calendar/batch` | 批量设置价格（请求体含 tierSeq） |
| DELETE | `/admin/product/item/{id}/price-calendar` | 按 tierSeq + 日期范围删除价格 |

---

## 六、优先级

**P1** — 此 bug 会让多档产品的定价流程完全走不通（除了第一次创建时设置的档位外，其余档位永远加不了价格）。建议尽快修复。
