<script lang="ts" setup>
|
import type { VbenFormSchema } from '#/adapter/form';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { MesWmProductIssueApi } from '#/api/mes/wm/productissue';
|
import type { MesWmProductIssueLineApi } from '#/api/mes/wm/productissue/line';
|
|
import { computed, h, markRaw, ref } from 'vue';
|
|
import {
|
DICT_TYPE,
|
MesAutoCodeRuleCode,
|
MesProWorkOrderStatusEnum,
|
MesWmProductIssueStatusEnum,
|
} from '@vben/constants';
|
|
import { Button, Divider, message, Popconfirm } from 'ant-design-vue';
|
|
import { useVbenForm } from '#/adapter/form';
|
import { confirm, useVbenModal } from '@vben/common-ui';
|
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
import { generateAutoCode } from '#/api/mes/md/autocode/record';
|
import {
|
checkProductIssueQuantity,
|
getProductIssue,
|
stockProductIssue,
|
submitProductIssue,
|
updateProductIssue,
|
} from '#/api/mes/wm/productissue';
|
import {
|
deleteProductIssueLine,
|
getProductIssueLinePage,
|
} from '#/api/mes/wm/productissue/line';
|
import { MdWorkstationSelect } from '#/views/mes/md/workstation/components';
|
import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components';
|
|
import ProductIssueLineFormModal from './ProductIssueLineFormModal.vue';
|
|
const emit = defineEmits(['success']);
|
const formData = ref<MesWmProductIssueApi.ProductIssue>();
|
|
const canSubmit = computed(
|
() => formData.value?.status === MesWmProductIssueStatusEnum.PREPARE,
|
);
|
|
/** 是否可执行拣货(审批中状态) */
|
const canStock = computed(
|
() => formData.value?.status === MesWmProductIssueStatusEnum.APPROVING,
|
);
|
|
/** 构建编辑表单 schema */
|
function buildFormSchema(): VbenFormSchema[] {
|
return [
|
{
|
fieldName: 'id',
|
component: 'Input',
|
dependencies: { triggerFields: [''], show: () => false },
|
},
|
{
|
fieldName: 'status',
|
component: 'Input',
|
dependencies: { triggerFields: [''], show: () => false },
|
},
|
{
|
fieldName: 'code',
|
label: '领料单编号',
|
component: 'Input',
|
componentProps: { placeholder: '请输入领料单编号' },
|
rules: 'required',
|
suffix: () =>
|
h(
|
Button,
|
{
|
type: 'default',
|
size: 'small',
|
onClick: async () => {
|
const code = await generateAutoCode(
|
MesAutoCodeRuleCode.WM_PRODUCT_ISSUE_CODE,
|
);
|
await formApi.setFieldValue('code', code);
|
},
|
},
|
{ default: () => '生成' },
|
),
|
},
|
{
|
fieldName: 'name',
|
label: '领料单名称',
|
component: 'Input',
|
componentProps: { placeholder: '请输入领料单名称' },
|
rules: 'required',
|
},
|
{
|
fieldName: 'requiredTime',
|
label: '需求时间',
|
component: 'DatePicker',
|
componentProps: {
|
class: '!w-full',
|
format: 'YYYY-MM-DD HH:mm:ss',
|
placeholder: '请选择需求时间',
|
showTime: true,
|
valueFormat: 'x',
|
},
|
},
|
{
|
fieldName: 'workOrderId',
|
label: '生产订单',
|
component: markRaw(ProWorkOrderSelect),
|
componentProps: {
|
status: MesProWorkOrderStatusEnum.CONFIRMED,
|
},
|
},
|
{
|
fieldName: 'workstationId',
|
label: '工作站',
|
component: markRaw(MdWorkstationSelect),
|
componentProps: { placeholder: '请选择工作站' },
|
},
|
{
|
fieldName: 'remark',
|
label: '备注',
|
component: 'Textarea',
|
formItemClass: 'col-span-3',
|
componentProps: { placeholder: '请输入备注', rows: 2 },
|
},
|
];
|
}
|
|
const [Form, formApi] = useVbenForm({
|
commonConfig: {
|
componentProps: { class: 'w-full' },
|
formItemClass: 'col-span-1',
|
labelWidth: 100,
|
},
|
layout: 'horizontal',
|
schema: buildFormSchema(),
|
showDefaultActions: false,
|
wrapperClass: 'grid-cols-3',
|
});
|
|
// --- 物料行列表 ---
|
const lineGridColumns: VxeTableGridOptions<MesWmProductIssueLineApi.ProductIssueLine>['columns'] = [
|
{ field: 'itemCode', title: '物料编码', minWidth: 120 },
|
{ field: 'itemName', title: '物料名称', minWidth: 140 },
|
{ field: 'specification', title: '规格型号', minWidth: 120 },
|
{ field: 'unitMeasureName', title: '单位', width: 80 },
|
{ field: 'quantity', title: '领料数量', width: 100 },
|
{ title: '操作', width: 140, fixed: 'right', slots: { default: 'lineActions' } },
|
];
|
|
const [LineGrid, lineGridApi] = useVbenVxeGrid({
|
gridOptions: {
|
columns: lineGridColumns,
|
height: 260,
|
keepSource: true,
|
proxyConfig: {
|
ajax: {
|
query: async ({ page }) => {
|
if (!formData.value?.id) return { list: [], total: 0 };
|
return await getProductIssueLinePage({
|
issueId: formData.value.id,
|
pageNo: page.currentPage,
|
pageSize: page.pageSize,
|
});
|
},
|
},
|
},
|
rowConfig: { keyField: 'id', isHover: true },
|
toolbarConfig: { refresh: true },
|
pagerConfig: { pageSize: 10 },
|
} as VxeTableGridOptions<MesWmProductIssueLineApi.ProductIssueLine>,
|
});
|
|
/** 添加物料行 */
|
function handleCreateLine() {
|
lineFormModalApi.setData({ issueId: formData.value?.id }).open();
|
}
|
|
/** 编辑物料行 */
|
function handleEditLine(row: MesWmProductIssueLineApi.ProductIssueLine) {
|
lineFormModalApi.setData({ id: row.id, issueId: formData.value?.id }).open();
|
}
|
|
/** 删除物料行 */
|
async function handleDeleteLine(row: MesWmProductIssueLineApi.ProductIssueLine) {
|
try {
|
await deleteProductIssueLine(row.id!);
|
message.success('删除成功');
|
lineGridApi.query();
|
} catch {
|
message.error('删除失败');
|
}
|
}
|
|
/** 物料行操作成功 */
|
function onLineSuccess() {
|
lineGridApi.query();
|
}
|
|
const [LineFormModal, lineFormModalApi] = useVbenModal({
|
connectedComponent: ProductIssueLineFormModal,
|
destroyOnClose: true,
|
});
|
|
/** 提交领料单 */
|
async function handleSubmit() {
|
if (!formData.value?.id) return;
|
modalApi.lock();
|
try {
|
// 先保存表单
|
const data =
|
(await formApi.getValues()) as MesWmProductIssueApi.ProductIssue;
|
await updateProductIssue({ ...formData.value, ...data });
|
// 再提交
|
await submitProductIssue(formData.value.id);
|
message.success('提交成功');
|
await modalApi.close();
|
emit('success');
|
} finally {
|
modalApi.unlock();
|
}
|
}
|
|
/** 执行拣货:领料数量与拣货数量不一致时二次确认 */
|
async function handleStock() {
|
if (!formData.value?.id) return;
|
const quantityMatch = await checkProductIssueQuantity(formData.value.id);
|
if (!quantityMatch) {
|
try {
|
await confirm('领料数量与拣货数量不一致,确认执行拣货?');
|
} catch {
|
return;
|
}
|
}
|
modalApi.lock();
|
try {
|
await stockProductIssue(formData.value.id);
|
message.success('拣货成功');
|
await modalApi.close();
|
emit('success');
|
} finally {
|
modalApi.unlock();
|
}
|
}
|
|
const [Modal, modalApi] = useVbenModal({
|
async onConfirm() {
|
const { valid } = await formApi.validate();
|
if (!valid) return;
|
modalApi.lock();
|
try {
|
const data =
|
(await formApi.getValues()) as MesWmProductIssueApi.ProductIssue;
|
await updateProductIssue({ ...formData.value, ...data });
|
formData.value = { ...formData.value, ...data };
|
emit('success');
|
message.success('保存成功');
|
await modalApi.close();
|
} finally {
|
modalApi.unlock();
|
}
|
},
|
async onOpenChange(isOpen: boolean) {
|
if (!isOpen) {
|
formData.value = undefined;
|
return;
|
}
|
const data = modalApi.getData<{ id: number }>();
|
if (data?.id) {
|
modalApi.lock();
|
try {
|
formData.value = await getProductIssue(data.id);
|
await formApi.setValues(formData.value);
|
// 加载后刷新物料行
|
lineGridApi.query();
|
} finally {
|
modalApi.unlock();
|
}
|
}
|
},
|
});
|
</script>
|
|
<template>
|
<Modal title="编辑领料单" class="w-3/5">
|
<Form class="mx-4" />
|
<!-- 物料信息 -->
|
<template v-if="formData?.id">
|
<Divider style="margin: 12px 0">
|
<span style="font-weight: 600; color: #1890ff;">物料信息</span>
|
</Divider>
|
<div class="mx-4" style="height: 300px">
|
<LineGrid table-title="物料列表">
|
<template #toolbar-tools>
|
<TableAction
|
:actions="[
|
{
|
label: '添加物料',
|
type: 'primary',
|
icon: ACTION_ICON.ADD,
|
onClick: handleCreateLine,
|
},
|
]"
|
/>
|
</template>
|
<template #lineActions="{ row }">
|
<TableAction
|
:actions="[
|
{
|
label: '编辑',
|
type: 'link',
|
icon: ACTION_ICON.EDIT,
|
onClick: handleEditLine.bind(null, row),
|
},
|
{
|
label: '删除',
|
type: 'link',
|
danger: true,
|
icon: ACTION_ICON.DELETE,
|
popConfirm: {
|
title: `确认删除物料 ${row.itemName}?`,
|
confirm: handleDeleteLine.bind(null, row),
|
},
|
},
|
]"
|
/>
|
</template>
|
</LineGrid>
|
</div>
|
</template>
|
<template #prepend-footer>
|
<div class="flex flex-auto items-center gap-2">
|
<Popconfirm
|
v-if="canSubmit"
|
title="确认提交该领料单?【提交后将不能修改】"
|
@confirm="handleSubmit"
|
>
|
<Button type="primary">提交</Button>
|
</Popconfirm>
|
<Button v-if="canStock" type="primary" @click="handleStock">
|
执行拣货
|
</Button>
|
</div>
|
</template>
|
</Modal>
|
<LineFormModal @success="onLineSuccess" />
|
</template>
|