云
2 天以前 12250a63a8f65a37451fd49392a3cc90facbceaa
src/views/mes/pro/bagCode/modules/raw-generate-form.vue
@@ -1,19 +1,30 @@
<script lang="ts" setup>
  import type { VbenFormSchema } from "#/adapter/form";
  import { markRaw } from "vue";
  import { markRaw, ref } from "vue";
  import { useVbenModal } from "@vben/common-ui";
  import { message } from "ant-design-vue";
  import { Button, message, Progress } from "ant-design-vue";
  import { useVbenForm } from "#/adapter/form";
  import { generateRawBagCodes } from "#/api/mes/pro/bagCode";
  import { MdmItemSelect } from "#/views/basicData/mdm/components";
  import { isFinalTaskStatus, useGenerateTask } from "./use-generate-task";
  defineOptions({ name: "MesProBagCodeRawGenerateForm" });
  const emit = defineEmits(["success"]);
  /** 幂等键:打开弹窗时生成,网络重试复用;失败后重新生成 */
  const requestId = ref("");
  function newRequestId() {
    return `${Date.now()}-${Math.random().toString(36).slice(2)}-${Math.random()
      .toString(36)
      .slice(2)}`;
  }
  const schema: VbenFormSchema[] = [
    {
@@ -64,23 +75,47 @@
    async onConfirm() {
      const { valid } = await formApi.validate();
      if (!valid) return;
      if (task.value && !isFinalTaskStatus(task.value.status)) return;
      modalApi.lock();
      let asyncStarted = false;
      try {
        const values = await formApi.getValues();
        const result = await generateRawBagCodes({
          itemId: values.itemId,
          count: values.count,
          remark: values.remark,
          requestId: requestId.value,
        });
        if (result.async && result.taskId) {
          asyncStarted = true;
          modalApi.unlock();
          // 大批量已转后台:保持弹窗打开,展示进度并轮询
          startGenerateTask({
            id: result.taskId,
            status: 10,
            statusName: "排队中",
            progress: 0,
            totalCount: result.totalCount,
            finishedCount: 0,
            palletCount: result.palletCount,
            uuid: result.uuid,
          });
          return;
        }
        message.success(`已成功生成 ${result.totalCount} 条原始袋码`);
        emit("success");
        await modalApi.close();
      } finally {
        modalApi.unlock();
        if (!asyncStarted) modalApi.unlock();
      }
    },
    async onOpenChange(isOpen: boolean) {
      if (!isOpen) return;
      if (!isOpen) {
        stopGenerateTaskPolling();
        return;
      }
      resetGenerateTask();
      requestId.value = newRequestId();
      await formApi.setValues({
        itemId: undefined,
        count: undefined,
@@ -88,13 +123,63 @@
      });
    },
  });
  const {
    task,
    canceling: generateTaskCanceling,
    cancel: cancelGenerateTask,
    reset: resetGenerateTask,
    stopPolling: stopGenerateTaskPolling,
    start: startGenerateTask,
  } = useGenerateTask(modalApi, {
    onSuccess: () => emit("success"),
    onFailed: () => {
      // 失败后换新的幂等键,允许用户直接重新提交
      requestId.value = newRequestId();
    },
  });
</script>
<template>
  <Modal title="生成原始袋码" class="w-2/5">
    <Form class="mx-4" />
    <div class="mx-4 mb-2 rounded bg-gray-50 px-3 py-2 text-sm text-gray-500">
      原始袋码仅需选择品种并输入数量,不换算计量单位类型、不生成托盘;后续可正常录入批次或绑定托盘
    </div>
    <template v-if="task">
      <div class="mx-4 mb-2">
        <p class="mb-1 text-sm">
          任务 #{{ task.id }} · {{ task.statusName }}
        </p>
        <p class="mb-2 text-sm text-gray-500">
          已生成 {{ task.finishedCount ?? 0 }} / {{ task.totalCount ?? 0 }} 条
        </p>
        <Progress
          :percent="task.progress ?? 0"
          :status="
            task.status === 50
              ? 'exception'
              : task.status === 30
                ? 'success'
                : 'active'
          "
        />
        <p v-if="task.errorMsg" class="mt-2 text-sm text-red-500">
          {{ task.errorMsg }}
        </p>
        <div class="mt-4 flex justify-end">
          <Button
            danger
            size="small"
            :loading="generateTaskCanceling"
            @click="cancelGenerateTask"
          >
            取消生成
          </Button>
        </div>
      </div>
    </template>
    <template v-else>
      <Form class="mx-4" />
      <div class="mx-4 mb-2 rounded bg-gray-50 px-3 py-2 text-sm text-gray-500">
        原始袋码仅需选择品种并输入数量,不换算计量单位类型、不生成托盘;后续可正常录入批次或绑定托盘
      </div>
    </template>
  </Modal>
</template>