<script lang="ts" setup>
|
import type { VbenFormSchema } from "#/adapter/form";
|
|
import { markRaw, ref } from "vue";
|
|
import { useVbenModal } from "@vben/common-ui";
|
|
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[] = [
|
{
|
fieldName: "itemId",
|
label: "品种",
|
component: markRaw(MdmItemSelect),
|
componentProps: {
|
placeholder: "请选择品种(产品品种)",
|
},
|
rules: "selectRequired",
|
},
|
{
|
fieldName: "count",
|
label: "袋码数量",
|
component: "InputNumber",
|
componentProps: {
|
class: "!w-full",
|
min: 1,
|
placeholder: "请输入需要生成的袋码数量",
|
precision: 0,
|
},
|
rules: "required",
|
},
|
{
|
fieldName: "remark",
|
label: "备注",
|
component: "Textarea",
|
componentProps: {
|
placeholder: "请输入备注",
|
rows: 2,
|
},
|
},
|
];
|
|
const [Form, formApi] = useVbenForm({
|
commonConfig: {
|
componentProps: {
|
class: "w-full",
|
},
|
labelWidth: 100,
|
},
|
layout: "horizontal",
|
schema,
|
showDefaultActions: false,
|
});
|
|
const [Modal, modalApi] = useVbenModal({
|
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 {
|
if (!asyncStarted) modalApi.unlock();
|
}
|
},
|
async onOpenChange(isOpen: boolean) {
|
if (!isOpen) {
|
stopGenerateTaskPolling();
|
return;
|
}
|
resetGenerateTask();
|
requestId.value = newRequestId();
|
await formApi.setValues({
|
itemId: undefined,
|
count: undefined,
|
remark: undefined,
|
});
|
},
|
});
|
|
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">
|
<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>
|