<script lang="ts" setup>
|
import type { QcReportTemplateApi } from '#/api/mes/qc/report/template';
|
|
import { ref } from 'vue';
|
import { useRouter } from 'vue-router';
|
|
import { useVbenModal } from '@vben/common-ui';
|
|
import { Alert, Button, Empty, Radio, Spin, Tag } from 'ant-design-vue';
|
|
import { generateInstanceFromQc } from '#/api/mes/qc/report/instance';
|
import { getSelectableTemplates } from '#/api/mes/qc/report/template';
|
|
/**
|
* 质检类型 → 报告落库用的 businessType(就是 MES 表名)。
|
* <p>
|
* 与 mes_qc_type 字典的数值不是一回事,反查报告时用的就是这四个字符串。
|
*/
|
const BUSINESS_TYPE_BY_QC_TYPE: Record<number, string> = {
|
1: 'mes_qc_iqc',
|
2: 'mes_qc_ipqc',
|
3: 'mes_qc_oqc',
|
4: 'mes_qc_rqc',
|
};
|
|
/** 打开弹窗时由质检单列表页带进来 */
|
interface ReportModalData {
|
/** 质检类型:1 来料 2 过程 3 出货 4 退货 */
|
qcType: number;
|
/** 质检单编号 */
|
qcId: number;
|
/** 单据编号,仅用于标题展示 */
|
qcNo?: string;
|
}
|
|
/** 出件结果,只留弹窗要展示的字段 */
|
interface ReportResult {
|
id: number;
|
reportNo: string;
|
templateVersion: string;
|
itemCount: number;
|
undecidableCount: number;
|
warnings: string[];
|
errors: string[];
|
}
|
|
defineOptions({ name: 'QcReportGenerateModal' });
|
|
const router = useRouter();
|
|
const modalData = ref<ReportModalData>();
|
const templates = ref<QcReportTemplateApi.Template[]>([]);
|
const templateId = ref<number>();
|
const loadingTemplates = ref(false);
|
const generating = ref(false);
|
const result = ref<ReportResult>();
|
|
const [Modal, modalApi] = useVbenModal({
|
footer: false,
|
onOpenChange(isOpen: boolean) {
|
if (!isOpen) {
|
modalData.value = undefined;
|
templates.value = [];
|
templateId.value = undefined;
|
result.value = undefined;
|
return;
|
}
|
const data = modalApi.getData<ReportModalData>();
|
modalData.value = data;
|
result.value = undefined;
|
templateId.value = undefined;
|
templates.value = [];
|
if (data?.qcType) {
|
loadTemplates(data.qcType);
|
}
|
},
|
});
|
|
/**
|
* 只列同类型、已启用、且当前版本已发布的模板。
|
* <p>
|
* 这三重过滤全在后端做(/qc-report/template/selectable),前端不再自己推导:
|
* report_type 存的是 mes_qc_type 的数值字符串("1"~"4"),拿「IQC」这类英文名去比会一个都查不到;
|
* 且 currentVersion 非空 ≠ 能用——停用版本不会清空模板的 currentVersion,
|
* 这种模板选中后出件必报「模板版本未发布」。
|
*/
|
async function loadTemplates(qcType: number) {
|
loadingTemplates.value = true;
|
try {
|
templates.value = await getSelectableTemplates(String(qcType));
|
} catch {
|
templates.value = [];
|
} finally {
|
loadingTemplates.value = false;
|
}
|
}
|
|
/** 生成报告:数据全部由后端从质检单取,这里只决定「哪张单据 + 哪张模板」 */
|
async function handleGenerate() {
|
const data = modalData.value;
|
const chosen = templateId.value;
|
if (!data || !chosen || generating.value) {
|
return;
|
}
|
generating.value = true;
|
try {
|
const resp = await generateInstanceFromQc({
|
qcType: data.qcType,
|
qcId: data.qcId,
|
templateId: chosen,
|
});
|
result.value = {
|
id: resp.id,
|
reportNo: resp.reportNo,
|
templateVersion: resp.templateVersion,
|
itemCount: resp.itemCount,
|
undecidableCount: resp.undecidableCount,
|
warnings: resp.warnings ?? [],
|
errors: resp.errors ?? [],
|
};
|
} catch {
|
// 失败报文已由请求拦截器弹出(五道校验按业务优先级只报第一处),这里不重复提示。
|
// 失败都是「数据或选择不对」,重试无用,按报文提示去改数据或换模板。
|
} finally {
|
generating.value = false;
|
}
|
}
|
|
/** 生成完跳到报告实例列表,并把单据过滤条件带过去,直接看到这份报告 */
|
function handleGoReportList() {
|
const data = modalData.value;
|
modalApi.close();
|
if (!data) {
|
return;
|
}
|
router.push({
|
path: '/qc/report-instance',
|
query: {
|
businessType: BUSINESS_TYPE_BY_QC_TYPE[data.qcType],
|
businessId: String(data.qcId),
|
},
|
});
|
}
|
</script>
|
|
<template>
|
<Modal
|
:title="`出报告 - ${modalData?.qcNo ?? ''}`"
|
class="w-3/5"
|
:mask-closable="false"
|
>
|
<!-- 选模板阶段 -->
|
<template v-if="!result">
|
<Spin :spinning="loadingTemplates">
|
<div class="px-1 py-2">
|
<div class="mb-3 text-sm text-gray-600">
|
报告数据由后端从该质检单读取,只需选一张模板;模板类型必须与单据类型一致。
|
</div>
|
<div v-if="templates.length === 0 && !loadingTemplates">
|
<Empty description="没有可用的同类型模板,请先到「报告模板设计」里配好并发布一个版本" />
|
</div>
|
<Radio.Group v-else v-model:value="templateId" class="w-full">
|
<div class="flex flex-col gap-2">
|
<Radio v-for="item in templates" :key="item.id" :value="item.id">
|
<span>{{ item.templateName }}</span>
|
<Tag class="ml-2" color="green">{{ item.currentVersion }}</Tag>
|
<span v-if="item.industry" class="ml-2 text-xs text-gray-400">
|
{{ item.industry }}
|
</span>
|
</Radio>
|
</div>
|
</Radio.Group>
|
</div>
|
</Spin>
|
<div class="mt-4 flex justify-end gap-2">
|
<Button @click="modalApi.close()">取消</Button>
|
<Button
|
type="primary"
|
:loading="generating"
|
:disabled="!templateId"
|
@click="handleGenerate"
|
>
|
生成报告
|
</Button>
|
</div>
|
</template>
|
|
<!-- 结果阶段:告警必须展示,不要静默吞掉 -->
|
<template v-else>
|
<div class="px-1 py-2">
|
<Alert type="success" show-icon>
|
<template #message>
|
报告已生成:<b>{{ result.reportNo }}</b>
|
</template>
|
<template #description>
|
<div>
|
模板版本 {{ result.templateVersion }};写入检验项
|
{{ result.itemCount }} 条
|
</div>
|
</template>
|
</Alert>
|
|
<Alert
|
v-if="result.undecidableCount > 0"
|
class="mt-3"
|
type="warning"
|
show-icon
|
:message="`本次有 ${result.undecidableCount} 项没有判定结论`"
|
description="缺规格上下限又没规则的项标为「无判定规则」,不计入合格率也不参与报告结论;规则执行失败的项标为「待判定」,仍计入合格率。两项都计入总数。如需判定,请先在质检单上补规格上下限,或修好模板里的规则后重新出件。"
|
/>
|
<Alert
|
v-else
|
class="mt-3"
|
type="info"
|
show-icon
|
message="全部检验项均已判定,没有无法判定的项"
|
/>
|
|
<div v-if="result.warnings.length > 0" class="mt-3">
|
<div class="mb-1 text-sm font-medium text-gray-700">
|
数据取舍提示({{ result.warnings.length }})
|
</div>
|
<Alert type="warning">
|
<ul class="m-0 list-disc pl-4">
|
<li v-for="(w, i) in result.warnings" :key="i">{{ w }}</li>
|
</ul>
|
</Alert>
|
</div>
|
|
<div v-if="result.errors.length > 0" class="mt-3">
|
<div class="mb-1 text-sm font-medium text-gray-700">
|
模板字段缺口({{ result.errors.length }})
|
</div>
|
<Alert type="error">
|
<ul class="m-0 list-disc pl-4">
|
<li v-for="(e, i) in result.errors" :key="i">{{ e }}</li>
|
</ul>
|
</Alert>
|
</div>
|
|
<div class="mt-3 text-xs text-gray-400">
|
以上提示只在本次响应里出现,不落库;事后回看历史报告时看不到,需要留痕请自行记录。
|
</div>
|
</div>
|
<div class="mt-4 flex justify-end gap-2">
|
<Button @click="modalApi.close()">关闭</Button>
|
<Button type="primary" @click="handleGoReportList">查看报告列表</Button>
|
</div>
|
</template>
|
</Modal>
|
</template>
|