perf: replace client-side pagination with server-side pagination in stock selection dialog and fix duplicate variable in semi.vue
This commit is contained in:
@ -106,7 +106,8 @@
|
||||
</div>
|
||||
<el-table
|
||||
ref="manualTableRef"
|
||||
:data="paginatedStockData"
|
||||
:data="stockList"
|
||||
v-loading="stockLoading"
|
||||
height="500"
|
||||
border
|
||||
row-key="uniqueKey"
|
||||
@ -145,12 +146,12 @@
|
||||
style="margin-top: 12px; justify-content: flex-end; display: flex;"
|
||||
v-model:current-page="stockPage"
|
||||
v-model:page-size="stockPageSize"
|
||||
:total="filteredStockData.length"
|
||||
:total="stockTotal"
|
||||
:page-sizes="[20, 50, 100, 200]"
|
||||
layout="total, prev, pager, next"
|
||||
background
|
||||
@size-change="() => { stockPage = 1 }"
|
||||
@current-change="() => {}"
|
||||
@size-change="handleStockSizeChange"
|
||||
@current-change="handleStockPageChange"
|
||||
/>
|
||||
<template #footer>
|
||||
<span style="float: left; line-height: 32px; color: #909399;">
|
||||
@ -331,7 +332,7 @@
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { Printer, Search, Plus, Download, List } from '@element-plus/icons-vue'
|
||||
import { ElMessage, ElTable, ElMessageBox } from 'element-plus'
|
||||
import { getAllStock, printSelectionList } from '@/api/inbound/stock'
|
||||
import { getAllStock, getStockList, printSelectionList } from '@/api/inbound/stock'
|
||||
import { getBomList, getBomDetail } from '@/api/bom'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
@ -346,11 +347,14 @@ const exportLoading = ref(false)
|
||||
const printLoading = ref(false)
|
||||
|
||||
const allStockData = ref<any[]>([])
|
||||
const filteredStockData = ref<any[]>([])
|
||||
const searchKeyword = ref('')
|
||||
const tempSelection = ref<any[]>([])
|
||||
const stockList = ref<any[]>([]) // 服务端分页数据
|
||||
const stockTotal = ref(0)
|
||||
const stockPage = ref(1)
|
||||
const stockPageSize = ref(20)
|
||||
const stockLoading = ref(false)
|
||||
const searchKeyword = ref('')
|
||||
const tempSelection = ref<any[]>([])
|
||||
let stockSearchTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
// 表格引用
|
||||
const manualTableRef = ref<InstanceType<typeof ElTable>>()
|
||||
@ -427,12 +431,6 @@ const shortageList = computed(() => {
|
||||
|
||||
const hasShortage = computed(() => shortageList.value.length > 0 && bomSets.value > maxBuildableSets.value)
|
||||
|
||||
// ★ 出库选单分页数据(固定 height="500" 时仍需分页减轻渲染压力)
|
||||
const paginatedStockData = computed(() => {
|
||||
const start = (stockPage.value - 1) * stockPageSize.value
|
||||
return filteredStockData.value.slice(start, start + stockPageSize.value)
|
||||
})
|
||||
|
||||
// --- 辅助方法 ---
|
||||
const getTypeTag = (type: string) => {
|
||||
switch (type) {
|
||||
@ -445,52 +443,76 @@ const getTypeTag = (type: string) => {
|
||||
|
||||
// --- 核心逻辑 1:手动添加库存 ---
|
||||
|
||||
// 服务端加载库存列表
|
||||
const loadStockList = async () => {
|
||||
stockLoading.value = true
|
||||
try {
|
||||
const res: any = await getStockList({
|
||||
page: stockPage.value,
|
||||
pageSize: stockPageSize.value,
|
||||
keyword: searchKeyword.value.trim()
|
||||
})
|
||||
stockList.value = res.data?.list || []
|
||||
stockTotal.value = res.data?.total || 0
|
||||
} catch (e) {
|
||||
ElMessage.error('加载库存列表失败')
|
||||
} finally {
|
||||
stockLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 手动选库存弹窗:加载服务端分页数据 + BOM 用全量数据
|
||||
const openManualSelect = async () => {
|
||||
manualDialogVisible.value = true
|
||||
stockPage.value = 1
|
||||
searchKeyword.value = ''
|
||||
await loadStockList()
|
||||
|
||||
// 仅在 BOM 关联查询需要时加载全量(一次性缓存)
|
||||
if (allStockData.value.length === 0) {
|
||||
try {
|
||||
const res: any = await getAllStock()
|
||||
const rawMaterials = (res.materials || []).map((i: any) => ({ ...i, type: 'material', typeLabel: '采购件' }))
|
||||
const rawSemis = (res.semis || []).map((i: any) => ({ ...i, type: 'semi', typeLabel: '半成品' }))
|
||||
const rawProducts = (res.products || []).map((i: any) => ({ ...i, type: 'product', typeLabel: '成品' }))
|
||||
|
||||
const list = [...rawMaterials, ...rawSemis, ...rawProducts]
|
||||
|
||||
allStockData.value = list.map((i: any) => ({
|
||||
...i,
|
||||
name: i.name || i.material_name || i.product_name || '未知名称',
|
||||
standard: i.standard || i.spec_model || '',
|
||||
// ★ 确保读取库位字段,如果没有则为空
|
||||
warehouse_location: i.warehouse_location || (i.warehouse_loc) || '',
|
||||
uniqueKey: `${i.type}_${i.id}`,
|
||||
available_quantity: parseFloat(i.available_quantity) || 0,
|
||||
export_quantity: 1
|
||||
}))
|
||||
|
||||
filteredStockData.value = allStockData.value
|
||||
} catch (e) {
|
||||
ElMessage.error('加载库存数据失败')
|
||||
ElMessage.error('加载全量库存数据失败(BOM 功能可能受影响)')
|
||||
}
|
||||
} else {
|
||||
searchKeyword.value = ''
|
||||
allStockData.value.forEach(item => item.export_quantity = 0)
|
||||
filteredStockData.value = allStockData.value
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索框防抖触发服务端过滤
|
||||
const filterStock = () => {
|
||||
if (stockSearchTimer) clearTimeout(stockSearchTimer)
|
||||
stockSearchTimer = setTimeout(() => {
|
||||
stockPage.value = 1
|
||||
loadStockList()
|
||||
}, 350)
|
||||
}
|
||||
|
||||
// 分页切换
|
||||
const handleStockPageChange = (page: number) => {
|
||||
stockPage.value = page
|
||||
loadStockList()
|
||||
}
|
||||
|
||||
const handleStockSizeChange = (size: number) => {
|
||||
stockPageSize.value = size
|
||||
stockPage.value = 1
|
||||
const kw = searchKeyword.value.trim().toLowerCase()
|
||||
if (!kw) {
|
||||
filteredStockData.value = allStockData.value
|
||||
return
|
||||
}
|
||||
filteredStockData.value = allStockData.value.filter(i =>
|
||||
(i.name && i.name.toLowerCase().includes(kw)) ||
|
||||
(i.standard && i.standard.toLowerCase().includes(kw))
|
||||
)
|
||||
loadStockList()
|
||||
}
|
||||
}
|
||||
|
||||
const handleStockSelection = (val: any[]) => { tempSelection.value = val }
|
||||
|
||||
Reference in New Issue
Block a user