<script lang="ts" setup>
|
import type {
|
MaterialStockAdjustInParams,
|
MaterialStockAdjustOutParams,
|
} from '#/api/mes/wm/materialstock';
|
import type { MdmItemApi } from '#/api/mdm/item';
|
import type { VbenFormSchema } from '#/adapter/form';
|
|
import { ref } from 'vue';
|
|
import { useVbenModal } from '@vben/common-ui';
|
import { z } from '#/adapter/form';
|
import { useVbenForm } from '#/adapter/form';
|
import { message } from 'ant-design-vue';
|
import { markRaw } from 'vue';
|
|
import {
|
adjustMaterialStockIn,
|
adjustMaterialStockOut,
|
} from '#/api/mes/wm/materialstock';
|
import { getItem } from '#/api/mdm/item';
|
import { MdmItemSelect } from '#/views/basicData/mdm/components';
|
import { ErpSupplierSelect } from '#/views/erp/purchase/supplier/components';
|
import BatchConfigDisplay from './batch-config-display.vue';
|
import {
|
WmWarehouseAreaSelect,
|
WmWarehouseLocationSelect,
|
WmWarehouseSelect,
|
} from '#/views/wls/warehouse/components';
|
|
const emit = defineEmits<{
|
success: [];
|
}>();
|
|
// 操作类型:1=入库,2=出库
|
const operateType = ref<1 | 2>(1);
|
const selectedItem = ref<MdmItemApi.Item | null>(null);
|
const selectedWarehouse = ref<any>(null);
|
const selectedLocation = ref<any>(null);
|
|
// 是否启用批次管理
|
const batchEnabled = ref(false);
|
// 是否手动填写批次号
|
const manualBatchEntry = ref(false);
|
// 批次配置展示组件引用
|
const batchConfigDisplayRef = ref<InstanceType<typeof BatchConfigDisplay>>();
|
|
// 标题
|
const modalTitle = ref('库存调整');
|
|
/** 更新批次字段显示(根据物料数据中的批次配置展示已启用的属性) */
|
async function updateBatchFields() {
|
const enabled = selectedItem.value?.isBatchManaged === true;
|
batchEnabled.value = enabled;
|
|
if (!enabled) {
|
formApi.updateSchema([
|
{ fieldName: 'batchCode', dependencies: { show: () => false } },
|
{ fieldName: 'manualBatchEntry', dependencies: { show: () => false } },
|
{ fieldName: 'produceDate', dependencies: { show: () => false } },
|
{ fieldName: 'expireDate', dependencies: { show: () => false } },
|
{ fieldName: 'receiptDate', dependencies: { show: () => false } },
|
{ fieldName: 'vendorId', dependencies: { show: () => true } },
|
{ fieldName: 'purchaseOrderCode', dependencies: { show: () => false } },
|
{ fieldName: 'lotNumber', dependencies: { show: () => false } },
|
{ fieldName: 'qualityStatus', dependencies: { show: () => false } },
|
]);
|
return;
|
}
|
|
const item = selectedItem.value;
|
const isIn = operateType.value === 1;
|
|
formApi.updateSchema([
|
{ fieldName: 'batchCode', dependencies: { show: () => manualBatchEntry.value } },
|
{ fieldName: 'manualBatchEntry', dependencies: { show: () => true } },
|
{ fieldName: 'produceDate', dependencies: { show: () => isIn && !!item?.produceDateFlag } },
|
{ fieldName: 'expireDate', dependencies: { show: () => isIn && !!item?.expireDateFlag } },
|
{ fieldName: 'receiptDate', dependencies: { show: () => isIn && !!item?.receiptDateFlag } },
|
{ fieldName: 'vendorId', dependencies: { show: () => !isIn || !!item?.vendorFlag } },
|
{ fieldName: 'purchaseOrderCode', dependencies: { show: () => isIn && !!item?.purchaseOrderCodeFlag } },
|
{ fieldName: 'lotNumber', dependencies: { show: () => isIn && !!item?.lotNumberFlag } },
|
{ fieldName: 'qualityStatus', dependencies: { show: () => isIn && !!item?.qualityStatusFlag } },
|
]);
|
}
|
|
/** 物料选择变化 */
|
async function handleItemChange(item: MdmItemApi.Item | undefined) {
|
selectedItem.value = item || null;
|
if (item?.id) {
|
const itemDetail = await getItem(item.id);
|
selectedItem.value = itemDetail;
|
// 根据物料数据更新批次字段
|
await updateBatchFields();
|
} else {
|
batchEnabled.value = false;
|
}
|
// 清空批次相关值
|
await formApi.setFieldValue('batchCode', undefined);
|
await formApi.setFieldValue('produceDate', undefined);
|
await formApi.setFieldValue('expireDate', undefined);
|
await formApi.setFieldValue('manualBatchEntry', false);
|
}
|
|
/** 仓库选择变化 */
|
async function handleWarehouseChange(item: any) {
|
selectedWarehouse.value = item;
|
await formApi.setFieldValue('locationId', undefined);
|
await formApi.setFieldValue('areaId', undefined);
|
}
|
|
/** 库区选择变化 */
|
async function handleLocationChange(item: any) {
|
selectedLocation.value = item;
|
await formApi.setFieldValue('areaId', undefined);
|
}
|
|
/** 手动填写批次号变化 */
|
async function handleManualBatchEntryChange(value: boolean) {
|
manualBatchEntry.value = value;
|
await updateBatchFields();
|
if (!value) {
|
await formApi.setFieldValue('batchCode', undefined);
|
}
|
}
|
/** 操作类型变化 */
|
async function handleOperateTypeChange(type: 1 | 2) {
|
operateType.value = type;
|
modalTitle.value = type === 1 ? '库存入库' : '库存出库';
|
await formApi.setFieldValue('operateType', type);
|
// 更新批次字段显示
|
await updateBatchFields();
|
}
|
|
/** 获取表单 schema */
|
function getSchema(): VbenFormSchema[] {
|
return [
|
{
|
fieldName: 'operateType',
|
label: '操作类型',
|
component: 'RadioGroup',
|
defaultValue: 1,
|
componentProps: {
|
options: [
|
{ label: '入库', value: 1 },
|
{ label: '出库', value: 2 },
|
],
|
onChange: handleOperateTypeChange,
|
},
|
rules: 'required',
|
},
|
{
|
fieldName: 'itemId',
|
label: '物料',
|
component: markRaw(MdmItemSelect),
|
componentProps: {
|
placeholder: '请选择物料',
|
onChange: handleItemChange,
|
},
|
rules: 'selectRequired',
|
},
|
{
|
fieldName: 'warehouseId',
|
label: '仓库',
|
component: markRaw(WmWarehouseSelect),
|
componentProps: {
|
placeholder: '请选择仓库',
|
onChange: handleWarehouseChange,
|
},
|
rules: 'selectRequired',
|
},
|
{
|
fieldName: 'locationId',
|
label: '库区',
|
component: markRaw(WmWarehouseLocationSelect),
|
componentProps: {
|
placeholder: '请选择库区',
|
warehouseId: selectedWarehouse.value?.id,
|
onChange: handleLocationChange,
|
},
|
dependencies: {
|
triggerFields: ['warehouseId'],
|
componentProps: (values: any) => ({
|
placeholder: '请选择库区',
|
warehouseId: values.warehouseId,
|
onChange: handleLocationChange,
|
}),
|
trigger: (values: any, formApi: any) => {
|
if (values.locationId !== undefined) {
|
void formApi.setFieldValue('locationId', undefined);
|
void formApi.setFieldValue('areaId', undefined);
|
}
|
},
|
},
|
rules: 'selectRequired',
|
},
|
{
|
fieldName: 'areaId',
|
label: '库位',
|
component: markRaw(WmWarehouseAreaSelect),
|
componentProps: {
|
placeholder: '请选择库位',
|
locationId: selectedLocation.value?.id,
|
},
|
dependencies: {
|
triggerFields: ['locationId'],
|
componentProps: (values: any) => ({
|
placeholder: '请选择库位',
|
locationId: values.locationId,
|
}),
|
trigger: (values: any, formApi: any) => {
|
if (values.areaId !== undefined) {
|
void formApi.setFieldValue('areaId', undefined);
|
}
|
},
|
},
|
rules: 'selectRequired',
|
},
|
// 是否手动填写批次号(启用批次管理时显示)
|
{
|
fieldName: 'manualBatchEntry',
|
label: '手动填写批次号',
|
component: 'Switch',
|
defaultValue: false,
|
componentProps: {
|
class: 'w-auto',
|
checkedChildren: '是',
|
unCheckedChildren: '否',
|
onChange: handleManualBatchEntryChange,
|
},
|
dependencies: {
|
triggerFields: ['itemId'],
|
show: () => false, // 默认隐藏,启用批次管理时显示
|
},
|
},
|
// 批次号(手动填写批次号时显示)
|
{
|
fieldName: 'batchCode',
|
label: '批次号',
|
component: 'Input',
|
componentProps: {
|
placeholder: '请输入批次号',
|
},
|
dependencies: {
|
triggerFields: ['itemId', 'manualBatchEntry'],
|
show: () => false, // 默认隐藏,根据手动填写设置显示
|
},
|
// 根据配置决定是否必填
|
rules: z.string().optional(),
|
},
|
// 生产日期(根据批次配置显示,仅入库)
|
{
|
fieldName: 'produceDate',
|
label: '生产日期',
|
component: 'DatePicker',
|
componentProps: {
|
placeholder: '请选择生产日期',
|
format: 'YYYY-MM-DD',
|
valueFormat: 'YYYY-MM-DD',
|
},
|
dependencies: {
|
triggerFields: ['itemId'],
|
show: () => false, // 默认隐藏
|
},
|
rules: 'required',
|
},
|
// 有效期(根据批次配置显示,仅入库)
|
{
|
fieldName: 'expireDate',
|
label: '有效期至',
|
component: 'DatePicker',
|
componentProps: {
|
placeholder: '请选择有效期',
|
format: 'YYYY-MM-DD',
|
valueFormat: 'YYYY-MM-DD',
|
},
|
dependencies: {
|
triggerFields: ['itemId'],
|
show: () => false,
|
},
|
},
|
// 入库日期(根据批次配置显示,仅入库)
|
{
|
fieldName: 'receiptDate',
|
label: '入库日期',
|
component: 'DatePicker',
|
componentProps: {
|
placeholder: '请选择入库日期',
|
format: 'YYYY-MM-DD',
|
valueFormat: 'YYYY-MM-DD',
|
},
|
dependencies: {
|
triggerFields: ['itemId'],
|
show: () => false,
|
},
|
},
|
{
|
fieldName: 'vendorId',
|
label: '供应商',
|
component: markRaw(ErpSupplierSelect),
|
componentProps: {
|
placeholder: '请选择供应商',
|
},
|
rules: 'selectRequired',
|
},
|
// 采购订单号(根据批次配置显示,仅入库)
|
{
|
fieldName: 'purchaseOrderCode',
|
label: '采购订单号',
|
component: 'Input',
|
componentProps: {
|
placeholder: '请输入采购订单号',
|
},
|
dependencies: {
|
triggerFields: ['itemId'],
|
show: () => false,
|
},
|
},
|
// 生产批号(根据批次配置显示,仅入库)
|
{
|
fieldName: 'lotNumber',
|
label: '生产批号',
|
component: 'Input',
|
componentProps: {
|
placeholder: '请输入生产批号',
|
},
|
dependencies: {
|
triggerFields: ['itemId'],
|
show: () => false,
|
},
|
},
|
// 质量状态(根据批次配置显示,仅入库)
|
{
|
fieldName: 'qualityStatus',
|
label: '质量状态',
|
component: 'Select',
|
componentProps: {
|
placeholder: '请选择质量状态',
|
allowClear: true,
|
options: [
|
{ label: '合格', value: 'QUALIFIED' },
|
{ label: '不合格', value: 'UNQUALIFIED' },
|
{ label: '待检', value: 'PENDING' },
|
],
|
},
|
dependencies: {
|
triggerFields: ['itemId'],
|
show: () => false,
|
},
|
},
|
{
|
fieldName: 'quantity',
|
label: '数量',
|
component: 'InputNumber',
|
componentProps: {
|
class: '!w-full',
|
placeholder: '请输入数量',
|
precision: 2,
|
min: 0.01,
|
},
|
rules: z.number().positive('数量必须大于0'),
|
},
|
{
|
fieldName: 'reason',
|
label: '原因',
|
component: 'Textarea',
|
formItemClass: 'col-span-2',
|
componentProps: {
|
placeholder: '请输入原因',
|
rows: 3,
|
},
|
},
|
];
|
}
|
|
const [Form, formApi] = useVbenForm({
|
commonConfig: {
|
componentProps: {
|
class: 'w-full',
|
},
|
formItemClass: 'col-span-1',
|
labelWidth: 100,
|
},
|
wrapperClass: 'grid-cols-2',
|
layout: 'horizontal',
|
schema: getSchema(),
|
showDefaultActions: false,
|
});
|
|
const [Modal, modalApi] = useVbenModal({
|
fullscreen: false,
|
onCancel: () => {
|
modalApi.close();
|
},
|
onConfirm: async () => {
|
await handleSubmit();
|
},
|
onOpenChange: async (isOpen: boolean) => {
|
if (!isOpen) {
|
selectedItem.value = null;
|
selectedWarehouse.value = null;
|
selectedLocation.value = null;
|
batchEnabled.value = false;
|
operateType.value = 1;
|
modalTitle.value = '库存调整';
|
return;
|
}
|
// 重置表单
|
await formApi.resetForm();
|
// 默认隐藏批次相关字段
|
formApi.updateSchema([
|
{ fieldName: 'batchCode', dependencies: { show: () => false } },
|
{ fieldName: 'manualBatchEntry', dependencies: { show: () => false } },
|
{ fieldName: 'produceDate', dependencies: { show: () => false } },
|
{ fieldName: 'expireDate', dependencies: { show: () => false } },
|
{ fieldName: 'receiptDate', dependencies: { show: () => false } },
|
{ fieldName: 'purchaseOrderCode', dependencies: { show: () => false } },
|
{ fieldName: 'lotNumber', dependencies: { show: () => false } },
|
{ fieldName: 'qualityStatus', dependencies: { show: () => false } },
|
]);
|
// 重置手动填写批次号状态
|
manualBatchEntry.value = false;
|
// 如果有传入物料信息,回填表单
|
const data = modalApi.getData();
|
if (data?.itemId) {
|
await formApi.setValues({
|
itemId: data.itemId,
|
warehouseId: data.warehouseId,
|
locationId: data.locationId,
|
areaId: data.areaId,
|
});
|
if (data.itemId) {
|
const itemDetail = await getItem(data.itemId);
|
selectedItem.value = itemDetail;
|
await updateBatchFields();
|
}
|
}
|
},
|
title: modalTitle,
|
});
|
|
/** 提交表单 */
|
async function handleSubmit() {
|
const { valid } = await formApi.validate();
|
if (!valid) {
|
return;
|
}
|
|
const values = await formApi.getValues();
|
|
// 启用批次管理时的校验
|
if (batchEnabled.value) {
|
if (manualBatchEntry.value && !values.batchCode) {
|
message.error('请输入批次号');
|
return;
|
}
|
if (operateType.value === 1) {
|
const item = selectedItem.value;
|
if (item?.produceDateFlag && !values.produceDate) {
|
message.error('请选择生产日期');
|
return;
|
}
|
if (item?.expireDateFlag && !values.expireDate) {
|
message.error('请选择有效期');
|
return;
|
}
|
if (item?.receiptDateFlag && !values.receiptDate) {
|
message.error('请选择入库日期');
|
return;
|
}
|
if (item?.purchaseOrderCodeFlag && !values.purchaseOrderCode) {
|
message.error('请输入采购订单号');
|
return;
|
}
|
if (item?.lotNumberFlag && !values.lotNumber) {
|
message.error('请输入生产批号');
|
return;
|
}
|
if (item?.qualityStatusFlag && !values.qualityStatus) {
|
message.error('请选择质量状态');
|
return;
|
}
|
}
|
}
|
|
try {
|
const type = operateType.value;
|
|
if (type === 1) {
|
// 入库
|
const params: MaterialStockAdjustInParams = {
|
itemId: values.itemId,
|
warehouseId: values.warehouseId,
|
locationId: values.locationId,
|
areaId: values.areaId,
|
batchCode: values.batchCode,
|
produceDate: values.produceDate,
|
expireDate: values.expireDate,
|
receiptDate: values.receiptDate,
|
vendorId: values.vendorId,
|
purchaseOrderCode: values.purchaseOrderCode,
|
lotNumber: values.lotNumber,
|
qualityStatus: values.qualityStatus,
|
quantity: values.quantity,
|
reason: values.reason,
|
};
|
await adjustMaterialStockIn(params);
|
message.success('入库成功');
|
} else {
|
// 出库
|
const params: MaterialStockAdjustOutParams = {
|
itemId: values.itemId,
|
warehouseId: values.warehouseId,
|
locationId: values.locationId,
|
areaId: values.areaId,
|
batchCode: values.batchCode,
|
vendorId: values.vendorId,
|
quantity: values.quantity,
|
reason: values.reason,
|
};
|
await adjustMaterialStockOut(params);
|
message.success('出库成功');
|
}
|
|
emit('success');
|
modalApi.close();
|
} catch (error) {
|
// 错误由请求拦截器处理
|
}
|
}
|
</script>
|
|
<template>
|
<Modal class="w-3/5">
|
<Form class="mx-4" />
|
<BatchConfigDisplay
|
ref="batchConfigDisplayRef"
|
:item="selectedItem"
|
/>
|
</Modal>
|
</template>
|