团期详情返回列表弹「参数 groupBatchId 格式错误」前端缺陷交接(确认预检以空团期 ID 重发)
changelog-filename-gate / validate (push) Failing after 1s
changelog-filename-gate / validate (push) Failing after 1s
#8410 在团期详情新增的 confirm-check 监听器缺 isDetailActive 与空 id 守卫, 离开页面那一拍以空串重发,拼出 group-batch//confirm-check 落进团期详情接口报 400。 后端零改动;与 28_8478 交接清单不冲突。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
这个提交包含在:
@@ -0,0 +1,182 @@
|
||||
---
|
||||
schema: hl-changelog/v2
|
||||
ticket: "frontend"
|
||||
title: "团期详情「配置」节点点浏览器返回,列表页弹「参数 groupBatchId 格式错误」:确认预检监听器缺空值守卫,离开页面瞬间以空团期 ID 重发 confirm-check"
|
||||
consumer: admin
|
||||
author: "jw(GIT)"
|
||||
change_type: "前端缺陷"
|
||||
backend_status: "not_required"
|
||||
gateway_status: "not_required"
|
||||
frontend_status: "pending"
|
||||
frontend_owner: ""
|
||||
frontend_ref: ""
|
||||
target_release: ""
|
||||
verified_at: ""
|
||||
status_note: "2026-09-28 jw 在 TEST(web.test.1814.love)从团期列表「进入团期」打开 jw测试产品 第1期(状态:配置),点浏览器返回,回到列表页即弹 400「参数 groupBatchId 格式错误,请检查后重试」。根因在 hl-ui:#8410(c37fd8ad)在团期详情页新增的 watch([canConfirmBatch, groupBatchId]) 没有照同页其它监听器写 isDetailActive 与空 id 两道守卫;离开详情页时路由先变、onDeactivated 后执行,中间这一拍 groupBatchId 塌缩为空串而页面仍算激活,该监听器随即调 loadConfirmCheck(),拼出 GET /v3/admin/order/group-batch//confirm-check,连续斜杠被合并后落进团期详情接口的 groupBatchId 位,confirm-check 转 Long 失败返 400。后端接口与数据均正常,契约不变。修法:该监听器补两道守卫(守卫放在清空 confirmCheck 之前),loadConfirmCheck 入口补空 id 兜底;与 28_8478 前端交接清单(保留 loadConfirmCheck)不冲突,可同批改。"
|
||||
updated_at: "2026-09-28"
|
||||
base: dev-v3
|
||||
---
|
||||
|
||||
# 团期详情点返回弹「参数 groupBatchId 格式错误」:确认预检用空团期 ID 重发了一次
|
||||
|
||||
> **服务**: hl-order-service-v3(**后端零改动,接口与数据均正常**)
|
||||
> **页面**: 管理后台 → 团期订单 → 进入团期(团期详情)→ 离开详情页(浏览器返回 / 点顶部页签 / 点侧边栏菜单)
|
||||
> **接口**: `GET /v3/admin/order/group-batch/{groupBatchId}/confirm-check`(#8410 确认预检)
|
||||
> **日期**: 2026-09-28
|
||||
> **影响范围**: 仅前端。状态为「配置」的团期,每次从详情页离开都会多弹一次 400 提示;详情页签未关(页面仍在缓存)时再回到同一团期,「确认」按钮的预检置灰会失效
|
||||
|
||||
---
|
||||
|
||||
## 一、现象
|
||||
|
||||
1. 团期列表点某个「配置」状态团期的「进入团期」。
|
||||
2. 点浏览器返回,回到 `/order-v2/batch`。
|
||||
3. 页面顶部弹出:`参数 groupBatchId 格式错误,请检查后重试`。
|
||||
|
||||
- 只有**状态为「配置」**的团期会出现;招募、确认、出行等状态的团期来回进出不报。
|
||||
- 不限于浏览器返回,从这类团期详情跳到任何**非团期详情**的页面都会弹(顶部页签、侧边栏菜单、点子订单进入订单详情同理)。直接从一个团期跳到另一个团期不报。
|
||||
- 除了这一次提示,页面数据和按钮当下都正常,没有产生任何写入。
|
||||
|
||||
复现团期:jw测试产品 第1期(jw测试1期),`/order-v2/batch/detail/2101506167098511362`,状态 `RESOURCE_PREPARING`(配置)。
|
||||
|
||||
---
|
||||
|
||||
## 二、根因(对 hl-ui `origin/v2.1` = `501c4258` 源码实读)
|
||||
|
||||
**① 离开详情页的那一拍,groupBatchId 会变成空串**
|
||||
|
||||
`src/views/order-v2/batch/detail/index.vue`:
|
||||
|
||||
```js
|
||||
// :513
|
||||
const routeBatchId = computed(() => decodeURIComponent(String(route.params.code || '')))
|
||||
// :519-529 keep-alive 失活冻结(896cbbec,09-22)
|
||||
const isDetailActive = ref(true)
|
||||
onActivated(() => { isDetailActive.value = true })
|
||||
onDeactivated(() => { isDetailActive.value = false })
|
||||
const groupBatchId = computed(() =>
|
||||
isDetailActive.value ? routeBatchId.value : frozenBatchId.value
|
||||
)
|
||||
```
|
||||
|
||||
`route` 是全局路由。点返回后路由立刻变成 `/order-v2/batch`,`route.params.code` 为 `undefined`,`routeBatchId` 变成空串 `''`。
|
||||
09-22 的失活冻结要等 `onDeactivated` 执行后才生效,而 `onDeactivated` 在本轮渲染**之后**才跑;监听器(默认 `flush: 'pre'`)在渲染**之前**就执行了。
|
||||
所以中间有一拍:**页面仍算激活(`isDetailActive === true`),`groupBatchId` 却已经是 `''`**。
|
||||
|
||||
**② 同页其它监听器都挡住了这一拍,#8410 新加的这个没挡**
|
||||
|
||||
同页主拉取监听器(`:584-596`)和 Tab 监听器(`:597`)都有两道守卫:
|
||||
|
||||
```js
|
||||
if (!isDetailActive.value) return
|
||||
if (!id) return
|
||||
```
|
||||
|
||||
#8410(`c37fd8ad`,09-27)新增的确认预检监听器(`:922-929`)一道都没有:
|
||||
|
||||
```js
|
||||
watch(
|
||||
[canConfirmBatch, groupBatchId],
|
||||
([can]) => {
|
||||
confirmCheck.value = null
|
||||
if (can) loadConfirmCheck() // 这一拍 groupBatchId === ''
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
```
|
||||
|
||||
`canConfirmBatch`(`:896`)= `detail.batchStatus === 'RESOURCE_PREPARING'`。离开时 store 里的详情还是这个团,所以「配置」状态的团 `can` 为 `true`,于是 `loadConfirmCheck()`(`:905`)拿空串发请求。
|
||||
其它状态的团 `can` 为 `false`,不发请求,**这就是只有「配置」团才报的原因**。
|
||||
|
||||
**③ 空串拼进路径后,落到了另一个接口上**
|
||||
|
||||
`src/api/orderV2GroupBatch.js:177`:
|
||||
|
||||
```js
|
||||
http.get(`${BASE}/${String(groupBatchId)}/confirm-check`, …)
|
||||
// groupBatchId = '' → GET /v3/admin/order/group-batch//confirm-check
|
||||
```
|
||||
|
||||
`//` 在到达接口匹配前被合并成 `/`,实际命中的是团期详情接口 `GET /v3/admin/order/group-batch/{groupBatchId}`,`confirm-check` 这个字面量被当成团期 ID 去转数字,于是返回 400「参数 groupBatchId 格式错误」。
|
||||
|
||||
**④ 次生问题:回到同一团期后确认按钮不再置灰(按源码推演,未在浏览器实测)**
|
||||
|
||||
离开那一拍,监听器先执行了 `confirmCheck.value = null`,空 ID 请求失败后 `catch` 不回填,预检结果就一直是 `null`。
|
||||
详情页签没关时,页面还在缓存里;再次进入**同一个**团期,`groupBatchId` 前后相同,监听器不会再触发,预检不会重拉:
|
||||
|
||||
- `confirmBtnDisabled`(`:931`)在 `confirmCheck == null` 时为 `false`,本该置灰的「确认」按钮变成可点;
|
||||
- 缺项横幅(`showConfirmCheckBanner`,`:936`)也不再显示。
|
||||
|
||||
点「确认」前 `onConfirmBatch`(`:939`)还会再预检一次,所以**不会误提交**,只是提前置灰这层提示失效了。刷新页面可恢复。
|
||||
|
||||
**⑤ 这是 09-22 同类问题的第四个监听器**
|
||||
|
||||
09-22 的 `22_frontend_团期详情页切走后重放请求groupBatchId塌缩-前端缺陷-管理后台.md` 已按这个机制修过三个子组件(`load()` 首行补空值守卫)。
|
||||
`GroupVehicleRequirementSection.vue:455-458` 的注释写得很清楚:「空串拼进 URL 会让路径段消失/后缀滑位(400 参数类型错误或 404),空 id 直接不发请求」。
|
||||
#8410 是在那之后新加的监听器,没有带上这道守卫。
|
||||
|
||||
---
|
||||
|
||||
## 三、后端与数据侧的核查结果(均正常,供排除用)
|
||||
|
||||
2026-09-28 经 `web.test.1814.love`(与浏览器同一入口)用 admin token 实测:
|
||||
|
||||
| 请求 | 结果 |
|
||||
|---|---|
|
||||
| `GET /v3/admin/order/group-batch/2101506167098511362`(团期详情) | `200`,`batchStatus=RESOURCE_PREPARING`、`batchLabel=1`、`batchName=jw测试1期` |
|
||||
| `GET …/2101506167098511362/confirm-check`(真实 ID) | `200`,`ready=false`、`statusConfirmable=true`、`checkedHouseholdCount=12` |
|
||||
| `GET …/group-batch//confirm-check`(空 ID,即前端实际拼出的地址) | `code 400`「参数 groupBatchId 格式错误,请检查后重试」 |
|
||||
| `GET …/group-batch/confirm-check`(单斜杠对照) | 同上,与双斜杠返回逐字一致 |
|
||||
| `GET …/group-batch/undefined/confirm-check`(对照) | 同上 |
|
||||
|
||||
结论:带真实 ID 时接口正常;报错只由空 ID 触发,后端不需要改。
|
||||
|
||||
---
|
||||
|
||||
## 四、修复建议
|
||||
|
||||
**① 监听器补两道守卫**(`index.vue:922-929`),守卫要放在清空 `confirmCheck` **之前**:
|
||||
|
||||
```js
|
||||
watch(
|
||||
[canConfirmBatch, groupBatchId],
|
||||
([can, id]) => {
|
||||
// 离开页面那一拍:路由已变、onDeactivated 未执行,id 为空串;失活期间也不重拉
|
||||
if (!isDetailActive.value || !id) return
|
||||
confirmCheck.value = null
|
||||
if (can) loadConfirmCheck()
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
```
|
||||
|
||||
守卫放在前面,离开时就不会把预检结果清空,第二节第④条的次生问题也一起消失。
|
||||
|
||||
**② `loadConfirmCheck()` 入口补兜底**(`index.vue:905`),挡住以后新增的其它调用点:
|
||||
|
||||
```js
|
||||
async function loadConfirmCheck() {
|
||||
const batchId = groupBatchId.value
|
||||
if (!batchId || confirmCheckLoading.value) return
|
||||
const seq = ++confirmCheckSeq
|
||||
…
|
||||
}
|
||||
```
|
||||
|
||||
`28_8478_团期详情新增分叉进度条并替代确认缺项横幅-修改接口-管理后台.md` 第四节第 6 条要在 `onReporterSaved` 里补调 `loadConfirmCheck()`,有这道兜底后同样安全。
|
||||
|
||||
**③ 建议补的回归**(`batch/detail/__tests__/index.spec.js` 的 `#8410` 用例组):
|
||||
|
||||
- 详情 `batchStatus=RESOURCE_PREPARING`、页面激活时,把 `route.params.code` 改为 `undefined`(模拟返回列表)→ 断言 `getGroupBatchConfirmCheck` 没有以 `''` 被调用;
|
||||
- 再触发 `onDeactivated` / `onActivated` 回到同一 ID → 断言 `confirmCheck` 仍是离开前的结果(`ready=false` 时按钮仍置灰)。
|
||||
|
||||
现有 `#8410` 用例只覆盖了进入配置节点和点「确认」前两个时机,没有模拟路由离开,所以测不出来。
|
||||
|
||||
---
|
||||
|
||||
## 五、业务边界
|
||||
|
||||
- 与 `28_8478` 前端交接清单**不冲突**:8478 要去掉页头缺项横幅,但明确**保留** `loadConfirmCheck` 预检(确认按钮置灰靠它)。本条改的正是它的触发监听器,可以同批改。
|
||||
- 修复后预检的调用时机不变:进入配置节点时调一次,点「确认」前再调一次;非配置节点不调。
|
||||
- `confirm-check` 的入参、出参、权限码(`group-batch:confirm`)都不变,见 `27_8410_团期确认只读预检-新增接口-管理后台.md`。
|
||||
- 本条不涉及后端改动,接口契约不变。
|
||||
在新工单中引用
屏蔽一个用户