perf(stocktake): 库位树按公司前缀后端裁剪,树与推荐并行请求
【后端】/tree 支持 ?prefixes=Y 或 ?prefixes=C,L(逗号分隔) 只在**顶层**按 name / full_path 前缀过滤,命中即整棵子树保留 —— 不递归裁剪,避免把子树打散导致前端勾选语义错乱。不传则全量。 刻意不用懒加载:setCheckedKeys / getCheckedNodes 依赖全树已构建。 实测节点数(含子树): 全量 3371 → IRIS (Y) 500(↓85%)→ LICA (C,L) 2871(↓15%) IRIS 收益很大;LICA 的前缀覆盖了树的大部分分支,故提升有限。 【前端】 - getWarehouseTree(prefixes?) 透传前缀,loadLocationTree 从 getAllowedLocPrefixes(selectedCompany) 取;后端已做过滤, 前端不再重复过滤,删掉冗余的 filterTreeByCompany。 - fetchRecommendLocations 改为 Promise.all 并行拉树与推荐, 取代原来的串行 await(两段网络等待不再叠加); setCheckedKeys 前仍保留 await nextTick() 等树渲染完。 【顺带修一个上一轮引入的 bug】 右侧自 leafOnly 改造后只存末级路径,而推荐返回的是库存级路径、可能是 非末级,原来的逐字比对必然对不上,会把正常勾选的库位误报成 「未能勾选」。改为按「自身或其祖先」判定覆盖。 实测: prefixes 过滤正确(IRIS 8 个顶层 / LICA 25 个 / 不传 33 个)
This commit is contained in:
@ -1,10 +1,13 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 获取库位树形结构
|
||||
export function getWarehouseTree() {
|
||||
// prefixes 可选:只返回顶层命中这些前缀的根节点及其完整子树(后端过滤),
|
||||
// 用于按公司精简拉取(IRIS 传 ['Y'],LICA 传 ['C','L']);不传则全量
|
||||
export function getWarehouseTree(prefixes?: string[]) {
|
||||
return request({
|
||||
url: '/v1/warehouse/tree',
|
||||
method: 'get'
|
||||
method: 'get',
|
||||
params: prefixes && prefixes.length ? { prefixes: prefixes.join(',') } : undefined
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -791,22 +791,16 @@ const showCreateForm = computed(() =>
|
||||
// 抽盘配置区是左右双栏,400px 的窄容器装不下,此时把欢迎页放宽
|
||||
const idleWide = computed(() => showCreateForm.value && newScopeType.value === 'active')
|
||||
|
||||
// 库位树按公司前缀过滤(IRIS 只看 Y,LICA 看 C/L;未配置的公司不过滤)
|
||||
const filterTreeByCompany = (nodes: any[], company: string) => {
|
||||
const prefixes = getAllowedLocPrefixes(company)
|
||||
if (!prefixes.length) return nodes
|
||||
return nodes.filter((n: any) =>
|
||||
prefixes.some((p: string) => String(n?.name || '').toUpperCase().startsWith(p.toUpperCase()))
|
||||
)
|
||||
}
|
||||
|
||||
const loadLocationTree = async () => {
|
||||
if (locTreeData.value.length) return
|
||||
treeLoading.value = true
|
||||
try {
|
||||
const res: any = await getWarehouseTree()
|
||||
const raw = res?.data || []
|
||||
locTreeData.value = filterTreeByCompany(raw, selectedCompany.value)
|
||||
// ★ 按公司前缀**后端过滤**顶层节点(子树完整保留):IRIS 只拉 Y*,
|
||||
// LICA 只拉 C*/L*,显著减少节点数与传输量。
|
||||
// 注意不能改成懒加载 —— setCheckedKeys / getCheckedNodes 依赖全树已构建。
|
||||
const prefixes = getAllowedLocPrefixes(selectedCompany.value)
|
||||
const res: any = await getWarehouseTree(prefixes)
|
||||
locTreeData.value = res?.data || []
|
||||
} catch (e) {
|
||||
console.error('获取库位树失败', e)
|
||||
ElMessage.error('获取库位树失败')
|
||||
@ -837,16 +831,21 @@ const fetchRecommendLocations = async () => {
|
||||
}
|
||||
recLoading.value = true
|
||||
try {
|
||||
// 先把树准备好,否则 setCheckedKeys 对尚未渲染的节点无效
|
||||
await loadLocationTree()
|
||||
// ★ 树与推荐**并行**:两者互不依赖,串行会把两段网络等待直接叠加。
|
||||
// loadLocationTree 内部有「已加载则短路返回」,重复调用无额外开销。
|
||||
const [, res] = await Promise.all([
|
||||
loadLocationTree(),
|
||||
request({
|
||||
url: '/v1/inbound/stock/stocktake/recommend-locations',
|
||||
method: 'get',
|
||||
params: withCompany({ days: recommendDays.value, top_n: recTopN.value })
|
||||
})
|
||||
])
|
||||
|
||||
// 等树渲染完再勾选,否则 setCheckedKeys 对尚未渲染的节点无效
|
||||
await nextTick()
|
||||
|
||||
const res: any = await request({
|
||||
url: '/v1/inbound/stock/stocktake/recommend-locations',
|
||||
method: 'get',
|
||||
params: withCompany({ days: recommendDays.value, top_n: recTopN.value })
|
||||
})
|
||||
const locs: string[] = res?.data?.locations || []
|
||||
const locs: string[] = (res as any)?.data?.locations || []
|
||||
if (!locs.length) {
|
||||
ElMessage.warning(`最近 ${recommendDays.value} 天没有找到活跃库位`)
|
||||
return
|
||||
@ -858,9 +857,12 @@ const fetchRecommendLocations = async () => {
|
||||
syncSelectedPaths()
|
||||
|
||||
// 推荐里有、但树上勾不到的(不在当前公司前缀范围内等)要如实告知,
|
||||
// 否则这些库位会静默落选 —— 工人以为盘到了,其实没进范围
|
||||
const checkedPaths = new Set(selectedLocationPaths.value)
|
||||
const missing = locs.filter(l => !checkedPaths.has(l))
|
||||
// 否则这些库位会静默落选 —— 工人以为盘到了,其实没进范围。
|
||||
// ★ 注意不能逐字比对:右侧只存**末级**路径,而推荐返回的是库存级路径,
|
||||
// 可能是非末级(勾它会级联勾中其子节点),故需按「自身或其祖先」判定覆盖。
|
||||
const isCovered = (rec: string) =>
|
||||
selectedLocationPaths.value.some(p => p === rec || p.startsWith(rec + '/'))
|
||||
const missing = locs.filter(l => !isCovered(l))
|
||||
if (missing.length) {
|
||||
ElMessage.warning(`推荐中 ${missing.length} 个库位不在当前公司的库位树上,未能勾选`)
|
||||
console.warn('未能勾选的推荐库位:', missing)
|
||||
|
||||
Reference in New Issue
Block a user