liu
5 天以前 ee7ac8df8bce38add1f75baf876b49de94b9e00a
src/views/mes/pro/pallet/index.vue
@@ -5,25 +5,36 @@
  import { Page, useVbenModal } from "@vben/common-ui";
  import { downloadFileFromBlobPart } from "@vben/utils";
  import { message } from "ant-design-vue";
  import { Button, message, Modal, Tag } from "ant-design-vue";
  import { ACTION_ICON, TableAction, useVbenVxeGrid } from "#/adapter/vxe-table";
  import {
    deletePallet,
    exportPalletExcel,
    exportPalletQr,
    exportPalletQrByIds,
    exportPalletBusinessQr,
    generatePalletBusinessCodes,
    getPalletPage,
  } from "#/api/mes/pro/pallet";
  import { IconifyIcon } from "@vben/icons";
  import { $t } from "#/locales";
  import { useGridColumns, useGridFormSchema } from "./data";
  import BindBatchModal from "./modules/bind-batch.vue";
  import GenerateModal from "./modules/generate-form.vue";
  import QrTraceModal from "../components/qr-trace-modal.vue";
  import BusinessQrModal from "../components/business-qr.vue";
  defineOptions({ name: "MesProPallet" });
  const [GenerateFormModal, generateFormModalApi] = useVbenModal({
    connectedComponent: GenerateModal,
    destroyOnClose: true,
  });
  const [BindBatchFormModal, bindBatchFormModalApi] = useVbenModal({
    connectedComponent: BindBatchModal,
    destroyOnClose: true,
  });
@@ -33,13 +44,45 @@
  });
  /** 刷新表格 */
  const [BusinessQrViewModal, businessQrApi] = useVbenModal({ connectedComponent: BusinessQrModal, destroyOnClose: true });
  function handleBusinessQr(row: MesProPalletApi.Pallet) {
    businessQrApi.setData({ code: row.businessCode }).open();
  }
  function getBusinessIds() {
    const records = [...(gridApi.grid.getCheckboxReserveRecords?.() || []), ...(gridApi.grid.getCheckboxRecords?.() || [])] as MesProPalletApi.Pallet[];
    const ids = [...new Set(records.map(row => row.id).filter((id): id is number => id != null))];
    if (!ids.length || ids.length > 200) { message.warning('请勾选 1 至 200 条托盘记录'); return; }
    if (records.some(row => row.status === 20)) { message.warning('已作废托盘不可操作业务码'); return; }
    return ids;
  }
  async function handleGenerateBusiness() {
    const ids = getBusinessIds(); if (!ids) return;
    const count = await generatePalletBusinessCodes(ids);
    message.success(`已补生成 ${count} 份业务码,已有码值保持不变`); handleRefresh();
  }
  async function handleExportBusiness() {
    const ids = getBusinessIds(); if (!ids) return;
    const data = await exportPalletBusinessQr(ids);
    downloadFileFromBlobPart({ fileName: '托盘业务二维码.zip', source: data });
  }
  function handleRefresh() {
    gridApi.query();
  }
  /** 批量生成托盘码 */
  /** 批量预生成托盘码 */
  function handleCreate() {
    generateFormModalApi.open();
  }
  /** 预生成成功:刷新列表并展示生成的临时托盘码 */
  function handlePreGenerateSuccess(result: { id: number; tempCode: string }[]) {
    handleRefresh();
    Modal.success({
      title: "预生成临时托盘码成功",
      content: result.map((item) => item.tempCode).join("\n"),
      centered: true,
    });
  }
  /** 扫码追溯 */
@@ -47,9 +90,44 @@
    qrTraceQueryModalApi.open();
  }
  /** 查看二维码(行内预填码值) */
  /** 获取托盘显示用的码值(正式托盘码优先,其次临时托盘码) */
  function getDisplayCode(row: MesProPalletApi.Pallet) {
    return row.code || row.tempCode || "";
  }
  /** 查看二维码(行内预填码值,临时托盘用临时托盘码) */
  function handleViewQr(row: MesProPalletApi.Pallet) {
    qrTraceQueryModalApi.setData({ code: row.code }).open();
    qrTraceQueryModalApi.setData({ code: getDisplayCode(row) }).open();
  }
  /** 获取勾选的临时托盘记录 */
  function getSelectedTempPallets(): MesProPalletApi.Pallet[] | undefined {
    const records = [
      ...(gridApi.grid.getCheckboxReserveRecords?.() ?? []),
      ...(gridApi.grid.getCheckboxRecords?.() ?? []),
    ] as MesProPalletApi.Pallet[];
    const uniqueRecords = [
      ...new Map(records.map((record) => [record.id, record])).values(),
    ];
    const tempRecords = uniqueRecords.filter((record) => !record.batchId);
    if (uniqueRecords.length === 0) {
      message.warning("请至少勾选一条托盘码记录");
      return undefined;
    }
    if (tempRecords.length === 0) {
      message.warning("勾选的记录均已绑定批次,无需绑定");
      return undefined;
    }
    return tempRecords;
  }
  /** 绑定批次 */
  function handleBindBatch() {
    const records = getSelectedTempPallets();
    if (!records) return;
    bindBatchFormModalApi
      .setData({ ids: records.map((record) => record.id!) })
      .open();
  }
  /** 导出二维码 ZIP(按当前查询条件的批次) */
@@ -63,10 +141,19 @@
    downloadFileFromBlobPart({ fileName: "托盘码二维码.zip", source: data });
  }
  /** 导出单个托盘码二维码 ZIP(支持未绑定批次的临时托盘) */
  async function handleExportQrById(row: MesProPalletApi.Pallet) {
    const data = await exportPalletQrByIds([row.id!]);
    downloadFileFromBlobPart({
      fileName: `托盘码二维码-${getDisplayCode(row)}.zip`,
      source: data,
    });
  }
  /** 删除托盘码 */
  async function handleDelete(row: MesProPalletApi.Pallet) {
    await deletePallet(row.id!);
    message.success($t("ui.actionMessage.deleteSuccess", [row.code]));
    message.success($t("ui.actionMessage.deleteSuccess", [getDisplayCode(row)]));
    handleRefresh();
  }
@@ -81,6 +168,10 @@
      schema: useGridFormSchema(),
    },
    gridOptions: {
      checkboxConfig: {
        highlight: true,
        reserve: true,
      },
      columns: useGridColumns(),
      height: "auto",
      keepSource: true,
@@ -109,46 +200,51 @@
<template>
  <Page auto-content-height>
    <GenerateFormModal @success="handleRefresh" />
    <GenerateFormModal @success="handlePreGenerateSuccess" />
    <BindBatchFormModal @success="handleRefresh" />
    <QrTraceQueryModal />
    <BusinessQrViewModal />
    <Grid table-title="托盘码列表">
      <template #table-title><span class="text-[1rem] font-bold" style="white-space:nowrap">托盘码列表</span></template>
      <template #toolbar-tools>
        <TableAction :actions="[
            {
              label: '批量生成',
              type: 'primary',
              icon: ACTION_ICON.ADD,
              auth: ['mes:pro-pallet:create'],
              onClick: handleCreate,
            },
            {
              label: '扫码追溯',
              icon: ACTION_ICON.SEARCH,
              auth: ['mes:pro-pallet:query'],
              onClick: handleTrace,
            },
            {
              label: '导出二维码',
              icon: ACTION_ICON.DOWNLOAD,
              auth: ['mes:pro-pallet:export'],
              onClick: handleExportQr,
            },
            {
              label: $t('ui.actionTitle.export'),
              type: 'primary',
              icon: ACTION_ICON.DOWNLOAD,
              auth: ['mes:pro-pallet:export'],
              onClick: handleExport,
            },
          ]" />
        <div class="mes-code-toolbar">
          <TableAction :actions="[{ label: '预生成二维码', type: 'primary', icon: ACTION_ICON.ADD, auth: ['mes:pro-pallet:create'], onClick: handleCreate },
          { label: '绑定批次', auth: ['mes:pro-pallet:update'], onClick: handleBindBatch },
          { label: '扫码追溯', icon: ACTION_ICON.SEARCH, auth: ['mes:pro-pallet:query'], onClick: handleTrace }]" />
          <TableAction :dropdown-trigger="['click']" :drop-down-actions="[{ label: '追溯二维码(按批次)', auth: ['mes:pro-pallet:export'], onClick: handleExportQr },
          { label: '业务二维码(选中记录)', auth: ['mes:pro-pallet:export'], onClick: handleExportBusiness },
          { label: 'Excel 数据', auth: ['mes:pro-pallet:export'], onClick: handleExport }]">
            <template #more><Button>导出 <IconifyIcon icon="lucide:chevron-down" /></Button></template>
          </TableAction>
          <TableAction :dropdown-trigger="['click']" :drop-down-actions="[{ label: '补生成业务码(选中记录)', auth: ['mes:pro-pallet:create'], onClick: handleGenerateBusiness }]">
            <template #more><Button>更多操作 <IconifyIcon icon="lucide:chevron-down" /></Button></template>
          </TableAction>
        </div>
      </template>
      <template #code="{ row }">
        <span>{{ getDisplayCode(row) }}</span>
        <Tag v-if="!row.code" color="warning">未绑定批次</Tag>
        <Tag v-if="row.autoGenerated" color="processing">自动生成</Tag>
      </template>
      <template #batchCode="{ row }">
        <span v-if="row.batchCode">{{ row.batchCode }}</span>
        <Tag v-else color="warning">未绑定批次</Tag>
      </template>
      <template #actions="{ row }">
        <TableAction :actions="[
            {
              label: '查看二维码',
              label: '追溯二维码',
              type: 'link',
              auth: ['mes:pro-pallet:query'],
              onClick: handleViewQr.bind(null, row),
            },
            { label: '业务二维码', type: 'link', auth: ['mes:pro-pallet:query'], ifShow: !!row.businessCode, onClick: handleBusinessQr.bind(null, row) },
            {
              label: '导出二维码',
              type: 'link',
              icon: ACTION_ICON.DOWNLOAD,
              auth: ['mes:pro-pallet:export'],
              onClick: handleExportQrById.bind(null, row),
            },
            {
              label: $t('common.delete'),
@@ -158,7 +254,7 @@
              auth: ['mes:pro-pallet:delete'],
              ifShow: !row.bagCount,
              popConfirm: {
                title: $t('ui.actionMessage.deleteConfirm', [row.code]),
                title: $t('ui.actionMessage.deleteConfirm', [getDisplayCode(row)]),
                confirm: handleDelete.bind(null, row),
              },
            },
@@ -167,3 +263,9 @@
    </Grid>
  </Page>
</template>
<style scoped>
.mes-code-toolbar { display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:8px; }
.mes-code-toolbar :deep(.ant-btn) { white-space:nowrap; }
.mes-code-toolbar :deep(.ant-btn > .iconify) { display:inline-block;width:14px;height:14px;vertical-align:middle; }
</style>