fix(admin-worker): 实时补丁同步筛选统计,并锁住"按工单号搜索结果被删行"的回归
线上现象:按工单号搜到的那一单,状态一变(实时 SSE),列表就变成「暂无数据」,而上方 筛选统计仍显示「共 1 单」;整页刷新后才恢复。 成因在前端实时快照补丁:旧逻辑里的 matchesWorkOrderKeyword 只比对「商品名 + 平台单号」, 不比对「工单号」,所以按 WO… 搜出来的行一旦有实时变更就被判成不匹配,直接从缓存里删掉; 而 statistics 是首次响应里带出来的、没被同步,于是出现"统计说有、列表没有"的自相矛盾。 (删行本身已由上一条提交改为:关键词筛选归入「不可证真」→ 只原地更新。) 本条补齐剩下的一致性: 1. 实时补丁增删/原地更新行时,同步维护 data.statistics 的 totalCount 与 statusCounts (statusCounts 按运营态口径记账,与后端 getWorkOrderStatistics 一致),列表与统计不再打架; 2. 补两个回归测试:按工单号搜到的行在状态变化后必须原地保留;插入/移除/原地改状态三种 情况下统计都要跟得上。 验证:前端 80 项单测全绿(新增 2 项),lint / typecheck / build 通过。
This commit is contained in:
@@ -178,7 +178,45 @@ export function shouldPrependAdminWorkOrder(items: readonly unknown[], snapshot:
|
||||
}
|
||||
|
||||
/**
|
||||
* 按工单号原地增删列表行,并同步分页总数(结构与后端 `{ data: { items, pagination } }` 一致)。
|
||||
* 同步筛选统计卡(`data.statistics`)。
|
||||
*
|
||||
* 统计与列表是**同一个响应**里下发的,两边必须一起改:只改 items 不改 statistics,
|
||||
* 就会出现"统计说还有 1 单、列表却是暂无数据"的自相矛盾界面(线上就是这样暴露的)。
|
||||
* 注意 statusCounts 用的是**运营态**口径(后端 statistics 走 adminOperationalStatusExpression),
|
||||
* 所以要按快照的 status 而非 rawStatus 记账。
|
||||
*/
|
||||
function adjustWorkOrderStatistics(
|
||||
statistics: unknown,
|
||||
change: { previousRow?: unknown; nextRow?: unknown; totalDelta: number },
|
||||
): unknown {
|
||||
if (!isRecord(statistics)) return statistics
|
||||
const previousStatus = String(
|
||||
(change.previousRow as { status?: unknown } | null | undefined)?.status || '',
|
||||
)
|
||||
const nextStatus = String(
|
||||
(change.nextRow as { status?: unknown } | null | undefined)?.status || '',
|
||||
)
|
||||
|
||||
const statusCounts = isRecord(statistics.statusCounts) ? { ...statistics.statusCounts } : null
|
||||
if (statusCounts && previousStatus !== nextStatus) {
|
||||
if (previousStatus) {
|
||||
statusCounts[previousStatus] = Math.max(0, toNumber(statusCounts[previousStatus]) - 1)
|
||||
}
|
||||
if (nextStatus) {
|
||||
statusCounts[nextStatus] = toNumber(statusCounts[nextStatus]) + 1
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...statistics,
|
||||
totalCount: Math.max(0, toNumber(statistics.totalCount) + change.totalDelta),
|
||||
...(statusCounts ? { statusCounts } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按工单号原地增删列表行,并同步分页总数与筛选统计
|
||||
* (结构与后端 `{ data: { items, pagination, statistics } }` 一致)。
|
||||
* 行已存在时永远只做原地替换;只有 insert 为真、且列表里确实没有这一行时才插到顶部。
|
||||
*/
|
||||
export function patchAdminWorkOrderListItems(
|
||||
@@ -194,7 +232,12 @@ export function patchAdminWorkOrderListItems(
|
||||
if (id <= 0) return current
|
||||
|
||||
let nextItems: unknown[]
|
||||
let previousRow: unknown = null
|
||||
let nextRow: unknown = null
|
||||
if (options.remove) {
|
||||
previousRow = items.find(
|
||||
(item) => toNumber((item as { workOrderId?: unknown })?.workOrderId) === id,
|
||||
)
|
||||
nextItems = items.filter(
|
||||
(item) => toNumber((item as { workOrderId?: unknown })?.workOrderId) !== id,
|
||||
)
|
||||
@@ -203,12 +246,14 @@ export function patchAdminWorkOrderListItems(
|
||||
(item) => toNumber((item as { workOrderId?: unknown })?.workOrderId) === id,
|
||||
)
|
||||
if (index >= 0) {
|
||||
previousRow = items[index]
|
||||
nextItems = items.map((item, itemIndex) => (itemIndex === index ? snapshot : item))
|
||||
} else if (options.insert) {
|
||||
nextItems = [snapshot, ...items]
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
nextRow = snapshot
|
||||
}
|
||||
|
||||
const delta = nextItems.length - items.length
|
||||
@@ -226,6 +271,15 @@ export function patchAdminWorkOrderListItems(
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(isRecord(current.data.statistics)
|
||||
? {
|
||||
statistics: adjustWorkOrderStatistics(current.data.statistics, {
|
||||
previousRow,
|
||||
nextRow,
|
||||
totalDelta: delta,
|
||||
}),
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user