perf(stocktake): 应盘清单分页 + 扫码置顶高亮

性能修复(解决打开费劲):
- get_all_stocktake_items 改为分页返回 {items(当前页), total, total_scanned}
  不再三次 .all() 全量加载 + 内存排序
- 前端 fetchAllStockItems 分页拉取(每页200),stats 用后端返回的 total
  不依赖全量数组长度

体验优化:
- 扫码成功后将物品置顶到当前表格第一行并浅绿高亮3秒
  库管无需翻页找刚扫的物品,'嘀'一下确认数量即可继续
- 主表格行高亮样式 .just-scanned-row
This commit is contained in:
yueli
2026-08-31 14:59:22 +08:00
parent f5aa2f481b
commit c6e8c887ec
2 changed files with 77 additions and 23 deletions

View File

@ -240,6 +240,7 @@
border
row-key="uniqueKey"
style="width: 100%"
:row-class-name="(row: any) => row._justScanned ? 'just-scanned-row' : ''"
>
<el-table-column prop="sku" label="SKU" width="140" show-overflow-tooltip />
<el-table-column prop="material_name" label="名称" min-width="120" show-overflow-tooltip />
@ -494,14 +495,19 @@ const listTotalFiltered = ref(0) // 过滤后的总数
const currentSessionId = ref<string>('')
// 获取应盘物资清单(盘点基数)
const fetchAllStockItems = async () => {
const fetchAllStockItems = async (page = 1) => {
try {
// ★ 必须传递 session_id用于隔离会话
const res: any = await getAllStocktakeItems({ session_id: currentSessionId.value })
// ★ 分页拉取:默认每页 200 条,避免全量加载卡顿
const res: any = await getAllStocktakeItems({
session_id: currentSessionId.value,
page,
pageSize: 200
})
if (res && res.code === 200) {
allStockItems.value = res.data.items || []
// ★ 使用返回的 total 获取真实总数,而不是受限的数组长度
// ★ 使用返回的 total 获取真实总数,而不是数组长度
totalStockCount.value = res.data.total || allStockItems.value.length
totalScannedCount.value = res.data.total_scanned || 0
}
} catch (e) {
console.error('获取应盘物资清单失败', e)
@ -511,11 +517,9 @@ const fetchAllStockItems = async () => {
// 过滤后的列表数据(直接使用已过滤的 listData
const filteredListData = computed(() => listData.value)
// 统计信息:从全量数据中计算(脱离视图依赖
// 统计信息:用后端返回的真实总数(不依赖全量数组长度
const stats = computed(() => {
const total = allStockItems.value.length
if (total === 0) return { total: 0, scanned: 0, varianceItems: 0 }
const total = totalStockCount.value || allStockItems.value.length
return {
total,
scanned: totalScannedCount.value,
@ -890,12 +894,36 @@ const syncToBackend = (uuid: string, quantity: number, remark: string) => {
syncStatus.value = 'success'
// 静默刷新统计数字
fetchInventoryList(true)
// ★ 扫码成功:该物品置顶到当前视图第一行并高亮
pinScannedItem(uuid)
})
.catch(() => {
syncStatus.value = 'failed'
})
}
// ★ 扫码成功置顶高亮:把刚扫的物品移到列表顶部,方便确认
const pinScannedItem = (uuid: string) => {
// 1. 置顶到主表格merged-list 当前页 listData
const listIdx = listData.value.findIndex(it =>
(it.uuid && it.uuid === uuid) || (it.sku && it.sku === uuid) ||
(it.barcode && it.barcode === uuid)
)
if (listIdx > -1) {
const item = listData.value.splice(listIdx, 1)[0]
item._justScanned = true
listData.value.unshift(item)
setTimeout(() => { item._justScanned = false }, 3000)
}
// 2. 同步置顶到 allStockItems应盘基数
const idx = allStockItems.value.findIndex(it => it.uuid === uuid || it.sku === uuid)
if (idx > -1) {
const item = allStockItems.value.splice(idx, 1)[0]
allStockItems.value.unshift(item)
}
}
}
const updateAndSync = async (item: StockItem, quantity: number, remark: string = '') => {
// 直接保存到后端,不使用本地缓存
item.scanned = true
@ -1246,6 +1274,14 @@ const goToVarianceReview = () => {
}
.drawer-footer { margin-top: 10px; flex-shrink: 0; }
/* ★ 扫码成功置顶行高亮(浅绿背景) */
:deep(.just-scanned-row) {
background: #f0f9eb !important;
}
:deep(.just-scanned-row td) {
background: #f0f9eb !important;
}
.qty-content { padding: 10px 0; }
.item-info { background: #f5f7fa; padding: 10px; border-radius: 6px; margin-bottom: 20px; }
.info-row { display: flex; justify-content: space-between; margin-bottom: 8px; font-size: 14px; }