✨ feat(select): 增加批次和仓库过滤功能,优化库存选择器

pull/871/MERGE
YunaiV 2026-04-08 09:56:32 +08:00
parent 606a9ed6e8
commit 55cb175f5a
12 changed files with 105 additions and 31 deletions

View File

@ -12,6 +12,7 @@ export interface WmMaterialStockVO {
batchId: number batchId: number
batchCode: string batchCode: string
warehouseId: number warehouseId: number
warehouseCode: string
warehouseName: string warehouseName: string
locationId: number locationId: number
locationName: string locationName: string

View File

@ -1,8 +1,6 @@
<!-- <!--
MES 生产任务弹窗选择器(支持单选/多选) MES 生产任务弹窗选择器(支持单选/多选)
搜索字段、展示字段对齐 KTG TaskSelect
Props: Props:
multiple — true 多选(checkbox),false 单选(radio);默认 true multiple — true 多选(checkbox),false 单选(radio);默认 true
Events: Events:

View File

@ -1,8 +1,6 @@
<!-- <!--
MES 质检指标弹窗选择器(支持单选/多选) MES 质检指标弹窗选择器(支持单选/多选)
搜索字段、展示字段对齐 KTG qcindexSelect/single.vue
Props: Props:
multiple — true 多选(checkbox),false 单选(radio);默认 true multiple — true 多选(checkbox),false 单选(radio);默认 true
Events: Events:

View File

@ -1,9 +1,6 @@
<!-- <!--
MES 到货通知单行弹窗选择器(单选模式) MES 到货通知单行弹窗选择器(单选模式)
对齐 KTG noticeSelect/lineSingle.vue:
展示字段:物料编码、物料名称、规格型号、单位、到货数量、是否检验、合格数量、检验单号、备注
Props: Props:
noticeId — 到货通知单编号(必传,按此筛选行数据) noticeId — 到货通知单编号(必传,按此筛选行数据)
Events: Events:

View File

@ -3,10 +3,13 @@
交互:显示为只读 el-input,点击打开弹窗(单选模式)进行选择 交互:显示为只读 el-input,点击打开弹窗(单选模式)进行选择
Props: Props:
modelValue — 绑定的批次 ID(v-model) modelValue — 绑定的批次 ID(v-model)
itemId — 默认过滤的物料 ID(打开弹窗时预设物料过滤条件) itemId — 默认过滤的物料 ID(打开弹窗时预设物料过滤条件)
disabled — 是否禁用 clientId — 默认过滤的客户 ID
clearable — 是否允许清空(鼠标悬停时显示清除图标) vendorId — 默认过滤的供应商 ID
salesOrderCode — 默认过滤的销售订单编号
disabled — 是否禁用
clearable — 是否允许清空(鼠标悬停时显示清除图标)
placeholder — 占位文字 placeholder — 占位文字
Events: Events:
update:modelValue — v-model 更新 update:modelValue — v-model 更新
@ -61,6 +64,7 @@ const props = withDefaults(
itemId?: number // 默认过滤的物料 ID itemId?: number // 默认过滤的物料 ID
clientId?: number // 默认过滤的客户 ID clientId?: number // 默认过滤的客户 ID
vendorId?: number // 默认过滤的供应商 ID vendorId?: number // 默认过滤的供应商 ID
salesOrderCode?: string // 默认过滤的销售订单编号
disabled?: boolean // 是否禁用 disabled?: boolean // 是否禁用
clearable?: boolean // 是否允许清空 clearable?: boolean // 是否允许清空
placeholder?: string // 占位文字 placeholder?: string // 占位文字
@ -141,7 +145,7 @@ const handleClick = (e: MouseEvent) => {
} }
// 打开弹窗,传入当前选中 ID 用于预选高亮,传入 itemId 用于默认过滤 // 打开弹窗,传入当前选中 ID 用于预选高亮,传入 itemId 用于默认过滤
const selectedIds = props.modelValue != null ? [props.modelValue] : [] const selectedIds = props.modelValue != null ? [props.modelValue] : []
dialogRef.value.open(selectedIds, props.itemId, props.clientId, props.vendorId) dialogRef.value.open(selectedIds, props.itemId, props.clientId, props.vendorId, props.salesOrderCode)
} }
/** 弹窗选中回调 */ /** 弹窗选中回调 */

View File

@ -13,7 +13,7 @@
<Dialog title="批次选择" v-model="dialogVisible" width="75%"> <Dialog title="批次选择" v-model="dialogVisible" width="75%">
<ContentWrap> <ContentWrap>
<el-alert <el-alert
v-if="externalClientId != null || externalVendorId != null" v-if="showAlert"
:title="alertTitle" :title="alertTitle"
type="info" type="info"
:closable="false" :closable="false"
@ -402,7 +402,7 @@ const resetQuery = () => {
queryParams.workstationId = undefined queryParams.workstationId = undefined
queryParams.toolId = undefined queryParams.toolId = undefined
queryParams.moldId = undefined queryParams.moldId = undefined
queryParams.salesOrderCode = undefined queryParams.salesOrderCode = externalSalesOrderCode.value
queryParams.purchaseOrderCode = undefined queryParams.purchaseOrderCode = undefined
queryParams.lotNumber = undefined queryParams.lotNumber = undefined
queryParams.qualityStatus = undefined queryParams.qualityStatus = undefined
@ -432,31 +432,51 @@ const confirmSelect = () => {
const externalClientId = ref<number | undefined>() // 记录外部传入的 clientId const externalClientId = ref<number | undefined>() // 记录外部传入的 clientId
const externalVendorId = ref<number | undefined>() // 记录外部传入的 vendorId const externalVendorId = ref<number | undefined>() // 记录外部传入的 vendorId
const externalSalesOrderCode = ref<string | undefined>() // 记录外部传入的 salesOrderCode
/** 拼装 el-alert 提示文字 */ /** 拼装 el-alert 提示文字 */
const alertTitle = computed(() => { const alertTitle = computed(() => {
const parts: string[] = [] const parts: string[] = []
if (externalClientId.value != null) parts.push('客户') if (externalClientId.value != null) {
if (externalVendorId.value != null) parts.push('供应商') parts.push('客户')
}
if (externalVendorId.value != null) {
parts.push('供应商')
}
if (externalSalesOrderCode.value != null) {
parts.push('销售订单')
}
return `已按${parts.join('/')}预过滤` return `已按${parts.join('/')}预过滤`
}) })
/** 是否显示 alert 提示 */
const showAlert = computed(() => {
return (
externalClientId.value != null ||
externalVendorId.value != null ||
externalSalesOrderCode.value != null
)
})
/** /**
* 打开弹窗 * 打开弹窗
* @param selectedIds 已选 ID,用于预选高亮 * @param selectedIds 已选 ID,用于预选高亮
* @param itemId 默认过滤的物料 ID * @param itemId 默认过滤的物料 ID
* @param clientId 默认过滤的客户 ID * @param clientId 默认过滤的客户 ID
* @param vendorId 默认过滤的供应商 ID * @param vendorId 默认过滤的供应商 ID
* @param salesOrderCode 默认过滤的销售订单编号
*/ */
const open = async ( const open = async (
selectedIds?: number[], selectedIds?: number[],
itemId?: number, itemId?: number,
clientId?: number, clientId?: number,
vendorId?: number vendorId?: number,
salesOrderCode?: string
) => { ) => {
dialogVisible.value = true dialogVisible.value = true
externalClientId.value = clientId externalClientId.value = clientId
externalVendorId.value = vendorId externalVendorId.value = vendorId
externalSalesOrderCode.value = salesOrderCode
// 重置查询条件 + 页码 // 重置查询条件 + 页码
queryParams.code = undefined queryParams.code = undefined
queryParams.itemId = itemId ?? undefined queryParams.itemId = itemId ?? undefined
@ -467,7 +487,7 @@ const open = async (
queryParams.workstationId = undefined queryParams.workstationId = undefined
queryParams.toolId = undefined queryParams.toolId = undefined
queryParams.moldId = undefined queryParams.moldId = undefined
queryParams.salesOrderCode = undefined queryParams.salesOrderCode = salesOrderCode ?? undefined
queryParams.purchaseOrderCode = undefined queryParams.purchaseOrderCode = undefined
queryParams.lotNumber = undefined queryParams.lotNumber = undefined
queryParams.qualityStatus = undefined queryParams.qualityStatus = undefined

View File

@ -3,11 +3,14 @@
交互:显示为只读 el-input,点击打开弹窗(单选模式)进行选择 交互:显示为只读 el-input,点击打开弹窗(单选模式)进行选择
Props: Props:
modelValue — 绑定的库存 ID(v-model) modelValue — 绑定的库存 ID(v-model)
itemId — 按物料 ID 过滤库存(可选,透传给弹窗) itemId — 按物料 ID 过滤库存(可选,透传给弹窗)
disabled — 是否禁用 batchId — 按批次 ID 过滤库存(可选,透传给弹窗)
clearable — 是否允许清空(鼠标悬停时显示清除图标) warehouseId — 按仓库 ID 过滤库存(可选,透传给弹窗)
placeholder — 占位文字 excludeVirtual — 是否排除虚拟线边仓库存(默认 true)
disabled — 是否禁用
clearable — 是否允许清空(鼠标悬停时显示清除图标)
placeholder — 占位文字
Events: Events:
update:modelValue — v-model 更新 update:modelValue — v-model 更新
change(item) — 选中库存变化时触发,传递完整 WmMaterialStockVO(清空时为 undefined) change(item) — 选中库存变化时触发,传递完整 WmMaterialStockVO(清空时为 undefined)
@ -47,6 +50,9 @@
ref="dialogRef" ref="dialogRef"
:multiple="false" :multiple="false"
:item-id="itemId" :item-id="itemId"
:batch-id="batchId"
:warehouse-id="warehouseId"
:exclude-virtual="excludeVirtual"
@selected="handleSelected" @selected="handleSelected"
/> />
</template> </template>
@ -66,11 +72,15 @@ const props = withDefaults(
defineProps<{ defineProps<{
modelValue?: number // 绑定的库存 ID modelValue?: number // 绑定的库存 ID
itemId?: number // 按物料 ID 过滤 itemId?: number // 按物料 ID 过滤
batchId?: number // 按批次 ID 过滤
warehouseId?: number // 按仓库 ID 过滤
excludeVirtual?: boolean // 是否排除虚拟线边仓库存(默认 true)
disabled?: boolean // 是否禁用 disabled?: boolean // 是否禁用
clearable?: boolean // 是否允许清空 clearable?: boolean // 是否允许清空
placeholder?: string // 占位文字 placeholder?: string // 占位文字
}>(), }>(),
{ {
excludeVirtual: true,
disabled: false, disabled: false,
clearable: true, clearable: true,
placeholder: '请选择库存' placeholder: '请选择库存'

View File

@ -2,8 +2,11 @@
MES 库存弹窗选择器(支持单选/多选) MES 库存弹窗选择器(支持单选/多选)
Props: Props:
multiple — true 多选(checkbox),false 单选(radio);默认 true multiple — true 多选(checkbox),false 单选(radio);默认 true
itemId — 按物料 ID 过滤库存(可选) itemId — 按物料 ID 过滤库存(可选)
batchId — 按批次 ID 过滤库存(可选)
warehouseId — 按仓库 ID 过滤库存(可选)
excludeVirtual — 是否排除虚拟线边仓库存(默认 true)
Events: Events:
selected(rows: WmMaterialStockVO[]) — 确认选择后触发,单选时数组长度为 1 selected(rows: WmMaterialStockVO[]) — 确认选择后触发,单选时数组长度为 1
Expose: Expose:
@ -20,8 +23,16 @@
</el-col> </el-col>
<!-- 右侧:搜索 + 表格 --> <!-- 右侧:搜索 + 表格 -->
<el-col :span="20" :xs="24"> <el-col :span="20" :xs="24">
<!-- 搜索 --> <!-- 预过滤提示 -->
<ContentWrap> <ContentWrap>
<el-alert
v-if="showAlert"
:title="alertTitle"
type="info"
:closable="false"
show-icon
class="!mb-10px"
/>
<el-form :inline="true" :model="queryParams" label-width="68px"> <el-form :inline="true" :model="queryParams" label-width="68px">
<el-form-item label="物料"> <el-form-item label="物料">
<MdItemSelect v-model="queryParams.itemId" class="!w-220px" /> <MdItemSelect v-model="queryParams.itemId" class="!w-220px" />
@ -155,9 +166,13 @@ const props = withDefaults(
defineProps<{ defineProps<{
multiple?: boolean // true 多选(checkbox),false 单选(radio) multiple?: boolean // true 多选(checkbox),false 单选(radio)
itemId?: number // 按物料 ID 过滤 itemId?: number // 按物料 ID 过滤
batchId?: number // 按批次 ID 过滤
warehouseId?: number // 按仓库 ID 过滤
excludeVirtual?: boolean // 是否排除虚拟线边仓库存(默认 true)
}>(), }>(),
{ {
multiple: true multiple: true,
excludeVirtual: true
} }
) )
@ -227,6 +242,7 @@ const queryParams = reactive({
itemId: undefined as number | undefined, // 物料编号 itemId: undefined as number | undefined, // 物料编号
vendorId: undefined as number | undefined, // 供应商编号 vendorId: undefined as number | undefined, // 供应商编号
batchCode: undefined as string | undefined, // 批次号 batchCode: undefined as string | undefined, // 批次号
batchId: undefined as number | undefined, // 批次 ID
warehouseId: undefined as number | undefined, // 仓库编号 warehouseId: undefined as number | undefined, // 仓库编号
locationId: undefined as number | undefined, // 库区编号 locationId: undefined as number | undefined, // 库区编号
areaId: undefined as number | undefined, // 库位编号 areaId: undefined as number | undefined, // 库位编号
@ -249,7 +265,14 @@ const getList = async () => {
loading.value = true loading.value = true
try { try {
const data = await WmMaterialStockApi.getMaterialStockPage(queryParams) const data = await WmMaterialStockApi.getMaterialStockPage(queryParams)
list.value = data.list // 前端过滤虚拟线边仓(非虚拟仓查询时排除 warehouseCode 含 WIP_VIRTUAL 的记录)
if (props.excludeVirtual) {
list.value = data.list.filter(
(row: WmMaterialStockVO) => !row.warehouseCode?.includes('WIP_VIRTUAL')
)
} else {
list.value = data.list
}
total.value = data.total total.value = data.total
await nextTick() await nextTick()
applyPreSelection() applyPreSelection()
@ -294,7 +317,8 @@ const resetQuery = () => {
queryParams.itemId = props.itemId // 保持 props 传入的物料过滤 queryParams.itemId = props.itemId // 保持 props 传入的物料过滤
queryParams.vendorId = undefined queryParams.vendorId = undefined
queryParams.batchCode = undefined queryParams.batchCode = undefined
queryParams.warehouseId = undefined queryParams.batchId = props.batchId // 保持 props 传入的批次过滤
queryParams.warehouseId = props.warehouseId // 保持 props 传入的仓库过滤
queryParams.locationId = undefined queryParams.locationId = undefined
queryParams.areaId = undefined queryParams.areaId = undefined
typeTreeRef.value?.reset() typeTreeRef.value?.reset()
@ -328,12 +352,13 @@ const open = async (selectedIds?: number[]) => {
queryParams.itemTypeId = undefined queryParams.itemTypeId = undefined
queryParams.vendorId = undefined queryParams.vendorId = undefined
queryParams.batchCode = undefined queryParams.batchCode = undefined
queryParams.warehouseId = undefined
queryParams.locationId = undefined queryParams.locationId = undefined
queryParams.areaId = undefined queryParams.areaId = undefined
queryParams.pageNo = 1 queryParams.pageNo = 1
// 固定 itemId 过滤条件(从 props 传入) // 固定过滤条件(从 props 传入)
queryParams.itemId = props.itemId queryParams.itemId = props.itemId
queryParams.batchId = props.batchId
queryParams.warehouseId = props.warehouseId
// 默认只查未冻结 // 默认只查未冻结
queryParams.frozen = false queryParams.frozen = false
// 重置分类树(清高亮 + 清搜索词) // 重置分类树(清高亮 + 清搜索词)
@ -349,6 +374,22 @@ const open = async (selectedIds?: number[]) => {
await getList() await getList()
} }
defineExpose({ open }) defineExpose({ open })
// ==================== 预过滤提示 ====================
/** 拼装 el-alert 提示文字 */
const alertTitle = computed(() => {
const parts: string[] = []
if (props.batchId != null) parts.push('批次')
if (props.warehouseId != null) parts.push('仓库')
if (props.excludeVirtual) parts.push('排除虚拟仓')
return `已按${parts.join('/')}预过滤`
})
/** 是否显示 alert 提示 */
const showAlert = computed(() => {
return props.batchId != null || props.warehouseId != null
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -78,6 +78,7 @@
v-model="formData.materialStockId" v-model="formData.materialStockId"
placeholder="请选择库存" placeholder="请选择库存"
class="!w-1/1" class="!w-1/1"
:exclude-virtual="false"
@change="handleStockChange" @change="handleStockChange"
/> />
</el-form-item> </el-form-item>

View File

@ -20,6 +20,7 @@
<WmMaterialStockSelect <WmMaterialStockSelect
v-model="formData.materialStockId" v-model="formData.materialStockId"
:item-id="formData.itemId" :item-id="formData.itemId"
:batch-id="formData.batchId"
@change="handleStockChange" @change="handleStockChange"
/> />
</el-form-item> </el-form-item>

View File

@ -92,6 +92,7 @@
:return-id="formData.id" :return-id="formData.id"
:form-type="formType" :form-type="formType"
:client-id="formData.clientId" :client-id="formData.clientId"
:sales-order-code="formData.salesOrderCode"
/> />
</template> </template>
<template #footer> <template #footer>

View File

@ -104,6 +104,7 @@
v-model="formData.batchId" v-model="formData.batchId"
:item-id="formData.itemId" :item-id="formData.itemId"
:client-id="props.clientId" :client-id="props.clientId"
:sales-order-code="props.salesOrderCode"
placeholder="请选择批次" placeholder="请选择批次"
class="!w-1/1" class="!w-1/1"
/> />
@ -151,6 +152,7 @@ const props = defineProps<{
returnId: number returnId: number
formType: string formType: string
clientId?: number // 客户 ID,用于透传给批次选择器过滤 clientId?: number // 客户 ID,用于透传给批次选择器过滤
salesOrderCode?: string // 销售订单编号,用于透传给批次选择器过滤
}>() }>()
const { t } = useI18n() // 国际化 const { t } = useI18n() // 国际化