| | |
| | | import type { FormType } from '../data'; |
| | | |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesWmProductReceiptApi } from '#/api/mes/wm/productreceipt'; |
| | | import type { MesWmProductReceiptDetailApi } from '#/api/mes/wm/productreceipt/detail'; |
| | | import type { MesWmProductReceiptLineApi } from '#/api/mes/wm/productreceipt/line'; |
| | | |
| | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { BarcodeBizTypeEnum } from '@vben/constants'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | import { Button, Checkbox, message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { submitProductReceiptStock } from '#/api/mes/wm/productreceipt'; |
| | | import { getProductReceiptDetailListByLineId } from '#/api/mes/wm/productreceipt/detail'; |
| | | import { |
| | | deleteProductReceiptLine, |
| | |
| | | const props = defineProps<{ |
| | | formType: FormType; |
| | | receiptId: number; |
| | | autoGenerated?: boolean; // 是否由报工自动生成(自动生成的行不可手工增删改) |
| | | }>(); |
| | | |
| | | const isEditable = computed(() => |
| | | // 是否可编辑明细行 |
| | | ['create', 'update'].includes(props.formType), |
| | | ); |
| | | const canEditLine = computed( |
| | | // 自动生成的入库单行不可手工增删改 |
| | | () => isEditable.value && !props.autoGenerated, |
| | | ); |
| | | const isStock = computed(() => props.formType === 'stock'); // 是否为上架模式 |
| | | const detailMap = reactive< |
| | | Record<number, MesWmProductReceiptDetailApi.ProductReceiptDetail[]> |
| | | >({}); // 已展开行的上架明细缓存 |
| | | const checkedMap = reactive<Record<number, boolean>>({}); // 行是否入库勾选状态缓存 |
| | | const barcodeDetailRef = ref(); // 条码详情弹窗实例 |
| | | |
| | | const [LineFormModal, lineFormModalApi] = useVbenModal({ |
| | |
| | | function handleRefresh() { |
| | | for (const id of Object.keys(detailMap)) { |
| | | delete detailMap[Number(id)]; |
| | | } |
| | | for (const id of Object.keys(checkedMap)) { |
| | | delete checkedMap[Number(id)]; |
| | | } |
| | | gridApi.query(); |
| | | } |
| | |
| | | row.itemCode, |
| | | row.itemName, |
| | | ); |
| | | } |
| | | |
| | | /** 行是否入库勾选状态(未手动勾选时回退到行自带值) */ |
| | | function isLineStock(row: MesWmProductReceiptLineApi.ProductReceiptLine) { |
| | | return checkedMap[row.id!] ?? row.isStock ?? false; |
| | | } |
| | | |
| | | /** 切换行是否入库 */ |
| | | function setLineStock( |
| | | row: MesWmProductReceiptLineApi.ProductReceiptLine, |
| | | checked: boolean, |
| | | ) { |
| | | checkedMap[row.id!] = checked; |
| | | } |
| | | |
| | | /** 保存上架分配:收集所有行的勾选状态与仓库/库区/库位分配,调用批量上架接口 */ |
| | | async function handleSaveStock() { |
| | | if (!props.receiptId) { |
| | | return; |
| | | } |
| | | const hideLoading = message.loading({ |
| | | content: '正在保存上架分配...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | const { list } = await getProductReceiptLinePage({ |
| | | pageNo: 1, |
| | | pageSize: 10000, |
| | | receiptId: props.receiptId, |
| | | }); |
| | | const rows: MesWmProductReceiptApi.StockRow[] = []; |
| | | for (const line of list) { |
| | | const isStock = isLineStock(line); |
| | | const row: MesWmProductReceiptApi.StockRow = { |
| | | lineId: line.id!, |
| | | isStock, |
| | | }; |
| | | if (isStock) { |
| | | let details = detailMap[line.id!]; |
| | | if (!details) { |
| | | details = await getProductReceiptDetailListByLineId(line.id!); |
| | | detailMap[line.id!] = details; |
| | | } |
| | | row.details = details.map((detail) => ({ |
| | | warehouseId: detail.warehouseId!, |
| | | locationId: detail.locationId!, |
| | | areaId: detail.areaId!, |
| | | quantity: detail.quantity!, |
| | | })); |
| | | } |
| | | rows.push(row); |
| | | } |
| | | await submitProductReceiptStock({ receiptId: props.receiptId, rows }); |
| | | message.success('上架分配保存成功'); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** 打开上架明细表单 */ |
| | |
| | | <LineFormModal @success="handleRefresh" /> |
| | | <DetailFormModal @success="handleDetailSuccess" /> |
| | | <Grid table-title="物料信息"> |
| | | <template v-if="isEditable" #toolbar-tools> |
| | | <template v-if="canEditLine" #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template v-if="isStock" #toolbar-tools> |
| | | <Button type="primary" @click="handleSaveStock">保存上架分配</Button> |
| | | </template> |
| | | <template #detail="{ row }"> |
| | | <DetailList |
| | | :details="getExpandedDetails(row)" |
| | |
| | | @edit="(detailId) => openDetailForm(row.id!, row.itemId, detailId)" |
| | | @refresh="loadLineDetails(row.id!)" |
| | | /> |
| | | </template> |
| | | <template #isStock="{ row }"> |
| | | <div class="flex justify-center"> |
| | | <Checkbox |
| | | :checked="isLineStock(row)" |
| | | @update:checked="(checked: boolean) => setLineStock(row, checked)" |
| | | /> |
| | | </div> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <div class="flex items-center justify-center"> |
| | |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | ifShow: isEditable, |
| | | ifShow: canEditLine, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | ifShow: isEditable, |
| | | ifShow: canEditLine, |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.itemName]), |
| | | confirm: handleDelete.bind(null, row), |