| | |
| | | return requestClient.post<number>('/mes/wm/material-stock/adjust-out', data); |
| | | } |
| | | |
| | | /** åºåç§»åº */ |
| | | export interface MaterialStockMoveParams { |
| | | materialStockId: number; // åºåè®°å½ID |
| | | toWarehouseId: number; // ç®æ ä»åºID |
| | | toLocationId: number; // ç®æ åºåºID |
| | | toAreaId: number; // ç®æ åºä½ID |
| | | quantity: number; // ç§»åºæ°é |
| | | remark?: string; // 夿³¨ |
| | | } |
| | | |
| | | /** åºåç§»åº */ |
| | | export function moveMaterialStock(data: MaterialStockMoveParams) { |
| | | return requestClient.post<boolean>('/mes/wm/material-stock/move', data); |
| | | } |
| | | |
| | | /** åºåè°æ´ï¼æ£æ°=å
¥åºï¼è´æ°=åºåºï¼- æ§æ¥å£ï¼ä¿çå
¼å®¹ */ |
| | | export interface MaterialStockAdjustParams { |
| | | itemId: number; // ç©æID |
| | |
| | | export { default as WmMaterialStockSelectDialog } from './select-dialog.vue'; |
| | | export { default as WmMaterialStockSelect } from './select.vue'; |
| | | export { default as WmStockAdjustModal } from './stock-adjust-modal.vue'; |
| | | export { default as WmStockMoveModal } from './move-modal.vue'; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock'; |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | |
| | | import { markRaw, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { z } from '#/adapter/form'; |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { moveMaterialStock } from '#/api/mes/wm/materialstock'; |
| | | import { |
| | | WmWarehouseAreaSelect, |
| | | WmWarehouseLocationSelect, |
| | | WmWarehouseSelect, |
| | | } from '#/views/wls/warehouse/components'; |
| | | |
| | | defineOptions({ name: 'WmStockMoveModal' }); |
| | | |
| | | const emit = defineEmits<{ success: [] }>(); |
| | | |
| | | const currentRow = ref<MesWmMaterialStockApi.MaterialStock>(); |
| | | |
| | | function useFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'itemCode', |
| | | label: 'ç©æç¼ç ', |
| | | component: 'Input', |
| | | componentProps: { disabled: true }, |
| | | }, |
| | | { |
| | | fieldName: 'itemName', |
| | | label: 'ç©æåç§°', |
| | | component: 'Input', |
| | | componentProps: { disabled: true }, |
| | | }, |
| | | { |
| | | fieldName: 'batchCode', |
| | | label: 'æ¹æ¬¡å·', |
| | | component: 'Input', |
| | | componentProps: { disabled: true }, |
| | | }, |
| | | { |
| | | fieldName: 'warehouseName', |
| | | label: 'å½åä»åº', |
| | | component: 'Input', |
| | | componentProps: { disabled: true }, |
| | | }, |
| | | { |
| | | fieldName: 'locationName', |
| | | label: 'å½ååºåº', |
| | | component: 'Input', |
| | | componentProps: { disabled: true }, |
| | | }, |
| | | { |
| | | fieldName: 'areaName', |
| | | label: 'å½ååºä½', |
| | | component: 'Input', |
| | | componentProps: { disabled: true }, |
| | | }, |
| | | { |
| | | fieldName: 'quantity', |
| | | label: 'å½åæ°é', |
| | | component: 'InputNumber', |
| | | componentProps: { disabled: true, class: '!w-full', precision: 2 }, |
| | | }, |
| | | { |
| | | fieldName: 'toWarehouseId', |
| | | label: 'ç®æ ä»åº', |
| | | component: markRaw(WmWarehouseSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ç®æ ä»åº', |
| | | onChange: async () => { |
| | | await formApi.setFieldValue('toLocationId', undefined); |
| | | await formApi.setFieldValue('toAreaId', undefined); |
| | | }, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'toLocationId', |
| | | label: 'ç®æ åºåº', |
| | | component: markRaw(WmWarehouseLocationSelect), |
| | | componentProps: { placeholder: 'è¯·éæ©ç®æ åºåº' }, |
| | | dependencies: { |
| | | triggerFields: ['toWarehouseId'], |
| | | componentProps: (values) => ({ |
| | | warehouseId: values.toWarehouseId, |
| | | placeholder: 'è¯·éæ©ç®æ åºåº', |
| | | onChange: async () => { |
| | | await formApi.setFieldValue('toAreaId', undefined); |
| | | }, |
| | | }), |
| | | trigger: (values, api) => { |
| | | if (values.toLocationId !== undefined) { |
| | | void api.setFieldValue('toLocationId', undefined); |
| | | void api.setFieldValue('toAreaId', undefined); |
| | | } |
| | | }, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'toAreaId', |
| | | label: 'ç®æ åºä½', |
| | | component: markRaw(WmWarehouseAreaSelect), |
| | | componentProps: { placeholder: 'è¯·éæ©ç®æ åºä½' }, |
| | | dependencies: { |
| | | triggerFields: ['toLocationId'], |
| | | componentProps: (values) => ({ |
| | | locationId: values.toLocationId, |
| | | placeholder: 'è¯·éæ©ç®æ åºä½', |
| | | }), |
| | | trigger: (values, api) => { |
| | | if (values.toAreaId !== undefined) { |
| | | void api.setFieldValue('toAreaId', undefined); |
| | | } |
| | | }, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'moveQuantity', |
| | | label: 'ç§»åºæ°é', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0.01, |
| | | placeholder: '请è¾å
¥ç§»åºæ°é', |
| | | precision: 2, |
| | | }, |
| | | rules: z.number().positive('ç§»åºæ°éå¿
须大äº0'), |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | 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, |
| | | }, |
| | | layout: 'horizontal', |
| | | schema: useFormSchema(), |
| | | showDefaultActions: false, |
| | | wrapperClass: 'grid-cols-2', |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) return; |
| | | |
| | | const row = currentRow.value; |
| | | if (!row?.id) return; |
| | | |
| | | const values = await formApi.getValues(); |
| | | const moveQuantity = values.moveQuantity as number; |
| | | const quantity = (row.quantity || 0) as number; |
| | | const availableQuantity = (row.availableQuantity || 0) as number; |
| | | |
| | | if (moveQuantity > quantity) { |
| | | message.error(`ç§»åºæ°éä¸è½è¶
è¿å½ååºåæ°éï¼${quantity.toFixed(2)}ï¼`); |
| | | return; |
| | | } |
| | | |
| | | if (moveQuantity > availableQuantity) { |
| | | message.error(`ç§»åºæ°éä¸è½è¶
è¿å¯ç¨éï¼${availableQuantity.toFixed(2)}ï¼ï¼å¯ç¨é = å¨åºæ°é - å»ç»æ°é - å ç¨é`); |
| | | return; |
| | | } |
| | | |
| | | if (row.warehouseId === values.toWarehouseId && row.locationId === values.toLocationId && row.areaId === values.toAreaId) { |
| | | message.error('ç®æ åºä½ä¸å½ååºä½ç¸åï¼ä¸å
许移åº'); |
| | | return; |
| | | } |
| | | |
| | | modalApi.lock(); |
| | | try { |
| | | await moveMaterialStock({ |
| | | materialStockId: row.id, |
| | | quantity: moveQuantity, |
| | | remark: values.remark, |
| | | toAreaId: values.toAreaId, |
| | | toLocationId: values.toLocationId, |
| | | toWarehouseId: values.toWarehouseId, |
| | | }); |
| | | message.success('ç§»åºæå'); |
| | | emit('success'); |
| | | await modalApi.close(); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | currentRow.value = undefined; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<MesWmMaterialStockApi.MaterialStock>(); |
| | | if (!data?.id) return; |
| | | currentRow.value = data; |
| | | |
| | | if (data.frozen) { |
| | | message.warning('该åºåè®°å½å·²å»ç»ï¼ä¸å
许移åº'); |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | |
| | | await formApi.setValues({ |
| | | areaName: data.areaName, |
| | | batchCode: data.batchCode, |
| | | itemCode: data.itemCode, |
| | | itemName: data.itemName, |
| | | locationName: data.locationName, |
| | | quantity: data.quantity, |
| | | warehouseName: data.warehouseName, |
| | | }); |
| | | }, |
| | | title: 'åºåç§»åº', |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal class="w-3/5"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| | |
| | | newFrozen: boolean, |
| | | row: MesWmMaterialStockApi.MaterialStock, |
| | | ) => Promise<boolean | undefined>, |
| | | onMove?: (row: MesWmMaterialStockApi.MaterialStock) => void, |
| | | ): VxeTableGridOptions<MesWmMaterialStockApi.MaterialStock>['columns'] { |
| | | const { hasAccessByCodes } = useAccess(); |
| | | return [ |
| | |
| | | }, |
| | | }, |
| | | }, |
| | | ...(onMove |
| | | ? [ |
| | | { |
| | | title: 'æä½', |
| | | width: 80, |
| | | fixed: 'right' as const, |
| | | slots: { default: 'actions' } as const, |
| | | }, |
| | | ] |
| | | : []), |
| | | ]; |
| | | } |
| | | |
| | |
| | | import { MdItemTypeTree } from '#/views/mes/md/item/type/components'; |
| | | import { WmBatchDetail } from '#/views/wls/batch/components'; |
| | | import AreaForm from '#/views/wls/warehouse/area/modules/form.vue'; |
| | | import { WmStockAdjustModal } from './components'; |
| | | import { WmStockAdjustModal, WmStockMoveModal } from './components'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | |
| | |
| | | |
| | | const [StockAdjustModal, stockAdjustModalApi] = useVbenModal({ |
| | | connectedComponent: WmStockAdjustModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [StockMoveModal, stockMoveModalApi] = useVbenModal({ |
| | | connectedComponent: WmStockMoveModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æå¼ç§»åºå¼¹çª */ |
| | | function handleOpenStockMove(row: MesWmMaterialStockApi.MaterialStock) { |
| | | stockMoveModalApi.setData(row).open(); |
| | | } |
| | | |
| | | /** ç§»åºæååè° */ |
| | | function handleStockMoveSuccess() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æå¼æ¹æ¬¡è¯¦æ
å¼¹çª */ |
| | | function handleOpenBatchDetail(row: MesWmMaterialStockApi.MaterialStock) { |
| | | if (!row.batchId) { |
| | |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(handleFrozenChange), |
| | | columns: useGridColumns(handleFrozenChange, handleOpenStockMove), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | |
| | | <Page auto-content-height><AreaModal /> |
| | | <WmBatchDetail ref="batchDetailRef" /> |
| | | <StockAdjustModal @success="handleStockAdjustSuccess" /> |
| | | <StockMoveModal @success="handleStockMoveSuccess" /> |
| | | |
| | | <div class="flex h-full w-full"> |
| | | <!-- å·¦ä¾§ç©æåç±»æ --> |
| | |
| | | </Button> |
| | | <span v-else>-</span> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'ç§»åº', |
| | | type: 'link', |
| | | auth: ['mes:wm-material-stock:move'], |
| | | ifShow: !row.frozen, |
| | | onClick: handleOpenStockMove.bind(null, row), |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </div> |
| | | </div> |