src/views/wls/productreceipt/modules/line-list.vue
@@ -2,6 +2,7 @@
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';
@@ -10,9 +11,10 @@
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,
@@ -29,16 +31,22 @@
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({
@@ -55,6 +63,9 @@
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();
}
@@ -99,6 +110,64 @@
    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();
  }
}
/** 打开上架明细表单 */
@@ -185,7 +254,7 @@
    <LineFormModal @success="handleRefresh" />
    <DetailFormModal @success="handleDetailSuccess" />
    <Grid table-title="物料信息">
      <template v-if="isEditable" #toolbar-tools>
      <template v-if="canEditLine" #toolbar-tools>
        <TableAction
          :actions="[
            {
@@ -197,6 +266,9 @@
          ]"
        />
      </template>
      <template v-if="isStock" #toolbar-tools>
        <Button type="primary" @click="handleSaveStock">保存上架分配</Button>
      </template>
      <template #detail="{ row }">
        <DetailList
          :details="getExpandedDetails(row)"
@@ -204,6 +276,14 @@
          @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">
@@ -213,7 +293,7 @@
                label: $t('common.edit'),
                type: 'link',
                icon: ACTION_ICON.EDIT,
                ifShow: isEditable,
                ifShow: canEditLine,
                onClick: handleEdit.bind(null, row),
              },
              {
@@ -221,7 +301,7 @@
                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),