gaoluyang
6 小时以前 20c16e10941221357b80a014b792286e4071555e
银川
1.库存现有量添加移库功能
已添加1个文件
已修改4个文件
297 ■■■■■ 文件已修改
src/api/mes/wm/materialstock/index.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/materialstock/components/index.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/materialstock/components/move-modal.vue 237 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/materialstock/data.ts 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/materialstock/index.vue 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/wm/materialstock/index.ts
@@ -106,6 +106,21 @@
  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
src/views/wls/materialstock/components/index.ts
@@ -1,3 +1,4 @@
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';
src/views/wls/materialstock/components/move-modal.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,237 @@
<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>
src/views/wls/materialstock/data.ts
@@ -83,6 +83,7 @@
    newFrozen: boolean,
    row: MesWmMaterialStockApi.MaterialStock,
  ) => Promise<boolean | undefined>,
  onMove?: (row: MesWmMaterialStockApi.MaterialStock) => void,
): VxeTableGridOptions<MesWmMaterialStockApi.MaterialStock>['columns'] {
  const { hasAccessByCodes } = useAccess();
  return [
@@ -189,6 +190,16 @@
        },
      },
    },
    ...(onMove
      ? [
          {
            title: '操作',
            width: 80,
            fixed: 'right' as const,
            slots: { default: 'actions' } as const,
          },
        ]
      : []),
  ];
}
src/views/wls/materialstock/index.vue
@@ -19,7 +19,7 @@
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';
@@ -30,6 +30,11 @@
const [StockAdjustModal, stockAdjustModalApi] = useVbenModal({
  connectedComponent: WmStockAdjustModal,
  destroyOnClose: true,
});
const [StockMoveModal, stockMoveModalApi] = useVbenModal({
  connectedComponent: WmStockMoveModal,
  destroyOnClose: true,
});
@@ -74,6 +79,16 @@
  gridApi.query();
}
/** æ‰“开移库弹窗 */
function handleOpenStockMove(row: MesWmMaterialStockApi.MaterialStock) {
  stockMoveModalApi.setData(row).open();
}
/** ç§»åº“成功回调 */
function handleStockMoveSuccess() {
  gridApi.query();
}
/** æ‰“开批次详情弹窗 */
function handleOpenBatchDetail(row: MesWmMaterialStockApi.MaterialStock) {
  if (!row.batchId) {
@@ -108,7 +123,7 @@
    schema: useGridFormSchema(),
  },
  gridOptions: {
    columns: useGridColumns(handleFrozenChange),
    columns: useGridColumns(handleFrozenChange, handleOpenStockMove),
    height: 'auto',
    keepSource: true,
    proxyConfig: {
@@ -139,6 +154,7 @@
  <Page auto-content-height><AreaModal />
    <WmBatchDetail ref="batchDetailRef" />
    <StockAdjustModal @success="handleStockAdjustSuccess" />
    <StockMoveModal @success="handleStockMoveSuccess" />
    <div class="flex h-full w-full">
      <!-- å·¦ä¾§ç‰©æ–™åˆ†ç±»æ ‘ -->
@@ -192,6 +208,19 @@
            </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>