diff --git a/changelogs/2026-04/2026-04-17_frontend-bug_step4-pricing-tier-tabs.md b/changelogs/2026-04/2026-04-17_frontend-bug_step4-pricing-tier-tabs.md new file mode 100644 index 0000000..cfaf6d1 --- /dev/null +++ b/changelogs/2026-04/2026-04-17_frontend-bug_step4-pricing-tier-tabs.md @@ -0,0 +1,153 @@ +# 前端 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 + + + :key="seq" + :type="currentTierSeq === seq ? 'primary' : 'default'" + @click="currentTierSeq = seq" + > + {{ getTierLabel(seq) }} + + +``` + +相关变量定义(大约 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 + + + {{ tier.tierName || `档位${tier.tierSeq}` }} + + +``` + +### 同步注意 +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 会让多档产品的定价流程完全走不通(除了第一次创建时设置的档位外,其余档位永远加不了价格)。建议尽快修复。