Files
mom-web/src/views/mes/quality/productquality/components/QualityDialog.vue

1247 lines
44 KiB
Vue
Raw Normal View History

2026-03-05 16:52:12 +08:00
<template>
<el-dialog
:title="title"
v-model="dialogVisible"
width="800px"
append-to-body
:close-on-click-modal="false"
@closed="handleClosed"
>
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
<!-- 批次信息 -->
<el-form-item label="批次编号" prop="workOrderCode">
<el-select
v-model="form.workOrderCode"
placeholder="请选择或输入批次编号"
filterable
allow-create
default-first-option
:disabled="type === 'view' || type === 'edit'"
@change="handleWorkOrderChange"
>
<el-option
v-for="item in workOrderOptions"
:key="item.id"
:label="`${item.code} - ${item.productName}`"
:value="item.code"
/>
</el-select>
</el-form-item>
<!-- 产品信息 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="产品编号" prop="productCode">
<el-input
v-model="form.productCode"
:disabled="type === 'view'"
placeholder="请输入产品编号"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="产品名称" prop="productName">
<el-input
v-model="form.productName"
:disabled="type === 'view'"
placeholder="请输入产品名称"
/>
</el-form-item>
</el-col>
</el-row>
<!-- 质检信息 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="质检状态" prop="status">
<el-select
v-model="form.status"
placeholder="请选择质检状态"
:disabled="type === 'view'"
>
<el-option label="待检" :value="0" />
<el-option label="检验中" :value="1" />
<el-option label="已完成" :value="2" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="质检时间" prop="inspectionTime">
<el-date-picker
v-model="form.inspectionTime"
type="datetime"
placeholder="选择质检时间"
:disabled="type === 'view'"
value-format="YYYY-MM-DDTHH:mm:ss"
/>
</el-form-item>
</el-col>
</el-row>
<!-- 公共重量 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="重量" prop="weight">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.weight"
:min="0"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
<span style="white-space: nowrap">kg</span>
</div>
</el-form-item>
</el-col>
</el-row>
<!-- 质检结果 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="是否合格" prop="qualified">
<el-radio-group v-model="form.qualified" :disabled="type === 'view'">
<el-radio :label="1">合格</el-radio>
<el-radio :label="0">不合格</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="质检员" prop="inspectorName">
<el-input v-model="form.inspectorName" :disabled="type === 'view'" />
</el-form-item>
</el-col>
</el-row>
<!-- 不合格原因 -->
<el-form-item label="不合格原因" prop="unqualifiedReason" v-if="!form.qualified">
<el-input
v-model="form.unqualifiedReason"
type="textarea"
:rows="3"
placeholder="请输入不合格原因"
:disabled="type === 'view'"
/>
</el-form-item>
<!-- 番茄类产品附加检验 -->
<template v-if="isTomatoProduct">
<el-divider>番茄类产品附加检验</el-divider>
<!-- 工艺冷破/热破 -->
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="工艺" prop="tomatoProcess">
<el-radio-group v-model="form.tomatoProcess" :disabled="type === 'view'">
<el-radio label="冷破">冷破</el-radio>
<el-radio label="热破">热破</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<!-- 浓度 -->
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="浓度">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.tomatoConcentration"
:min="0"
:step="0.1"
:precision="4"
:disabled="type === 'view'"
/>
<span style="white-space: nowrap">%</span>
</div>
</el-form-item>
</el-col>
</el-row>
<!-- 数量 -->
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="数量">
<el-input-number
v-model="form.tomatoQuantity"
:min="0"
:step="1"
:precision="0"
:disabled="type === 'view'"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="感官">
<el-input
v-model="form.tomatoSensory"
type="textarea"
:rows="3"
:disabled="type === 'view'"
/>
</el-form-item>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="可溶性固形物">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.tomatoSolubleSolidsPercent"
:min="0"
:step="0.1"
:precision="4"
:disabled="type === 'view'"
/>
<span style="white-space: nowrap">% (20阿贝折射仪)</span>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="粘稠度">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.tomatoViscosityCmPer"
:min="0"
:step="0.1"
:precision="4"
:disabled="type === 'view'"
/>
<span style="white-space: nowrap">cm/30s (20固形物12.5%)</span>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="番茄红素">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.tomatoLycopeneMgPer"
:min="0"
:step="0.1"
:precision="4"
:disabled="type === 'view'"
/>
<span style="white-space: nowrap">mg/100g</span>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="总酸">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.tomatoTotalAcidPercent"
:min="0"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
<span style="white-space: nowrap">% (以无水柠檬酸计)</span>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="PH value">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.tomatoPhValue"
:min="0"
:max="14"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="色差 a/b">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.tomatoColorDifference"
:min="0"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
<span style="white-space: nowrap">(固形物12.5%)</span>
</div>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="霉菌">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.tomatoMouldPositivePercent"
:min="0"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
<span>% 阳性视野无致病菌及微生物作用引起的腐败现象</span>
</div>
</el-form-item>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="评定">
<el-radio-group v-model="form.tomatoAssessConformance" :disabled="type === 'view'">
<el-radio :label="1">品质符合</el-radio>
<el-radio :label="0">品质不符合</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="评定说明">
<el-input
v-model="form.tomatoAssessRemark"
placeholder="填写规定要求等说明"
:disabled="type === 'view'"
/>
</el-form-item>
</el-col>
</el-row>
</template>
<!-- 甜菊糖产品附加检验 -->
<template v-if="isSteviaProduct">
<el-divider>甜菊糖产品附加检验</el-divider>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="标签">
<div style="display: flex; align-items: center; gap: 8px">
<el-input
v-model="form.steviaLabel"
:disabled="type === 'view'"
type="textarea"
:autosize="{ minRows: 10, maxRows: 30 }"
style="flex: 1; min-width: 300px"
/>
<el-tooltip
content="应标注内容:添加剂名称,配料清单、净含量、制造商名称,地址、生产日期、储存说明、产品标准号、生产许可证号,产品介绍、使用方法和范围、用途、有添加剂字样"
placement="top"
>
<Icon icon="ep:warning-filled" />
</el-tooltip>
<el-checkbox v-model="form.steviaLabelOk" :disabled="type === 'view'"
>合格</el-checkbox
>
</div>
</el-form-item>
</el-col>
</el-row>
<!-- <el-row :gutter="20">-->
<!-- <el-col :span="24">-->
<!-- <el-form-item label="色泽">-->
<!-- <div style="display: flex; align-items: center; gap: 8px">-->
<!-- <el-input v-model="form.steviaColor" :disabled="type === 'view'" />-->
<!-- <el-tooltip content="白色至浅黄色" placement="top">-->
<!-- <Icon icon="ep:warning-filled" />-->
<!-- </el-tooltip>-->
<!-- <el-checkbox v-model="form.steviaColorOk" :disabled="type === 'view'"-->
<!-- >合格</el-checkbox-->
<!-- >-->
<!-- </div>-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- </el-row>-->
<!-- <el-row :gutter="20">-->
<!-- <el-col :span="24">-->
<!-- <el-form-item label="状态">-->
<!-- <div style="display: flex; align-items: center; gap: 8px">-->
<!-- <el-input v-model="form.steviaState" :disabled="type === 'view'" />-->
<!-- <el-tooltip content="粉末、晶体、颗粒或片状" placement="top">-->
<!-- <Icon icon="ep:warning-filled" />-->
<!-- </el-tooltip>-->
<!-- <el-checkbox v-model="form.steviaStateOk" :disabled="type === 'view'"-->
<!-- >合格</el-checkbox-->
<!-- >-->
<!-- </div>-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- </el-row>-->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="甜菊糖苷含量">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.steviaGlycosidesPercent"
:min="0"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
<span style="white-space: nowrap">%</span>
<el-tooltip content="≥ 80.0" placement="top">
<Icon icon="ep:warning-filled" />
</el-tooltip>
<el-checkbox v-model="form.steviaGlycosidesPercentOk" :disabled="type === 'view'"
>合格</el-checkbox
>
</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="灰分">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.steviaAshPercent"
:min="0"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
<span style="white-space: nowrap">%</span>
<el-tooltip content="≤ 1.0" placement="top">
<Icon icon="ep:warning-filled" />
</el-tooltip>
<el-checkbox v-model="form.steviaAshPercentOk" :disabled="type === 'view'"
>合格</el-checkbox
>
</div>
</el-form-item>
</el-col>
</el-row>
<!-- <el-row :gutter="20">-->
<!-- <el-col :span="24">-->
<!-- <el-form-item label="干燥减重">-->
<!-- <div style="display: flex; align-items: center; gap: 8px">-->
<!-- <el-input-number-->
<!-- v-model="form.steviaLossOnDryingPercent"-->
<!-- :min="0"-->
<!-- :step="0.01"-->
<!-- :precision="4"-->
<!-- :disabled="type === 'view'"-->
<!-- />-->
<!-- <span style="white-space: nowrap">%</span>-->
<!-- <el-tooltip content="≤ 6.0" placement="top">-->
<!-- <Icon icon="ep:warning-filled" />-->
<!-- </el-tooltip>-->
<!-- <el-checkbox-->
<!-- v-model="form.steviaLossOnDryingPercentOk"-->
<!-- :disabled="type === 'view'"-->
<!-- >合格</el-checkbox-->
<!-- >-->
<!-- </div>-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- </el-row>-->
<!-- <el-row :gutter="20">-->
<!-- <el-col :span="24">-->
<!-- <el-form-item label="甲醇">-->
<!-- <div style="display: flex; align-items: center; gap: 8px">-->
<!-- <el-input-number-->
<!-- v-model="form.steviaMethanolMgPerKg"-->
<!-- :min="0"-->
<!-- :step="0.01"-->
<!-- :precision="4"-->
<!-- :disabled="type === 'view'"-->
<!-- />-->
<!-- <span style="white-space: nowrap">mg/kg</span>-->
<!-- <el-tooltip content="≤ 200" placement="top">-->
<!-- <Icon icon="ep:warning-filled" />-->
<!-- </el-tooltip>-->
<!-- <el-checkbox v-model="form.steviaMethanolMgPerKgOk" :disabled="type === 'view'"-->
<!-- >合格</el-checkbox-->
<!-- >-->
<!-- </div>-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- </el-row>-->
<!-- <el-row :gutter="20">-->
<!-- <el-col :span="24">-->
<!-- <el-form-item label="乙醇">-->
<!-- <div style="display: flex; align-items: center; gap: 8px">-->
<!-- <el-input-number-->
<!-- v-model="form.steviaEthanolMgPerKg"-->
<!-- :min="0"-->
<!-- :step="0.01"-->
<!-- :precision="4"-->
<!-- :disabled="type === 'view'"-->
<!-- />-->
<!-- <span style="white-space: nowrap">mg/kg</span>-->
<!-- <el-tooltip content="≤ 5000" placement="top">-->
<!-- <Icon icon="ep:warning-filled" />-->
<!-- </el-tooltip>-->
<!-- <el-checkbox v-model="form.steviaEthanolMgPerKgOk" :disabled="type === 'view'"-->
<!-- >合格</el-checkbox-->
<!-- >-->
<!-- </div>-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- </el-row>-->
<!-- <el-row :gutter="20">-->
<!-- <el-col :span="24">-->
<!-- <el-form-item label="铅">-->
<!-- <div style="display: flex; align-items: center; gap: 8px">-->
<!-- <el-input-number-->
<!-- v-model="form.steviaLeadMgPerKg"-->
<!-- :min="0"-->
<!-- :step="0.01"-->
<!-- :precision="4"-->
<!-- :disabled="type === 'view'"-->
<!-- />-->
<!-- <span style="white-space: nowrap">mg/kg</span>-->
<!-- <el-tooltip content="≤ 1.0" placement="top">-->
<!-- <Icon icon="ep:warning-filled" />-->
<!-- </el-tooltip>-->
<!-- <el-checkbox v-model="form.steviaLeadMgPerKgOk" :disabled="type === 'view'"-->
<!-- >合格</el-checkbox-->
<!-- >-->
<!-- </div>-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- </el-row>-->
<!-- <el-row :gutter="20">-->
<!-- <el-col :span="24">-->
<!-- <el-form-item label="总砷">-->
<!-- <div style="display: flex; align-items: center; gap: 8px">-->
<!-- <el-input-number-->
<!-- v-model="form.steviaTotalArsenicMgPerKg"-->
<!-- :min="0"-->
<!-- :step="0.01"-->
<!-- :precision="4"-->
<!-- :disabled="type === 'view'"-->
<!-- />-->
<!-- <span style="white-space: nowrap">mg/kg</span>-->
<!-- <el-tooltip content="≤ 1.0" placement="top">-->
<!-- <Icon icon="ep:warning-filled" />-->
<!-- </el-tooltip>-->
<!-- <el-checkbox-->
<!-- v-model="form.steviaTotalArsenicMgPerKgOk"-->
<!-- :disabled="type === 'view'"-->
<!-- >合格</el-checkbox-->
<!-- >-->
<!-- </div>-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- </el-row>-->
<!-- 新增字段 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="RD">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.steviaRd"
:min="0"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
<el-checkbox v-model="form.steviaRdOk" :disabled="type === 'view'"
>合格</el-checkbox
>
</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="RA">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.steviaRa"
:min="0"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
<el-checkbox v-model="form.steviaRaOk" :disabled="type === 'view'"
>合格</el-checkbox
>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="STV">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.steviaStv"
:min="0"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
<el-checkbox v-model="form.steviaStvOk" :disabled="type === 'view'"
>合格</el-checkbox
>
</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="RF">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.steviaRf"
:min="0"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
<el-checkbox v-model="form.steviaRfOk" :disabled="type === 'view'"
>合格</el-checkbox
>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="RC">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.steviaRc"
:min="0"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
<el-checkbox v-model="form.steviaRcOk" :disabled="type === 'view'"
>合格</el-checkbox
>
</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="DA">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.steviaDa"
:min="0"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
<el-checkbox v-model="form.steviaDaOk" :disabled="type === 'view'"
>合格</el-checkbox
>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="RV">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.steviaRv"
:min="0"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
<el-checkbox v-model="form.steviaRvOk" :disabled="type === 'view'"
>合格</el-checkbox
>
</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="RB">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.steviaRb"
:min="0"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
<el-checkbox v-model="form.steviaRbOk" :disabled="type === 'view'"
>合格</el-checkbox
>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="SB">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.steviaSb"
:min="0"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
<el-checkbox v-model="form.steviaSbOk" :disabled="type === 'view'"
>合格</el-checkbox
>
</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="水分">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.steviaMoisture"
:min="0"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
<span style="white-space: nowrap">%</span>
<el-checkbox v-model="form.steviaMoistureOk" :disabled="type === 'view'"
>合格</el-checkbox
>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="比吸光">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.steviaSpecificAbsorbance"
:min="0"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
<el-checkbox v-model="form.steviaSpecificAbsorbanceOk" :disabled="type === 'view'"
>合格</el-checkbox
>
</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="透光率">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.steviaTransmittance"
:min="0"
:max="100"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
<span style="white-space: nowrap">%</span>
<el-checkbox v-model="form.steviaTransmittanceOk" :disabled="type === 'view'"
>合格</el-checkbox
>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="PH value">
<div style="display: flex; align-items: center; gap: 8px">
<el-input-number
v-model="form.steviaPhValue"
:min="0"
:max="14"
:step="0.01"
:precision="4"
:disabled="type === 'view'"
/>
<el-tooltip content="4.5-7.0" placement="top">
<Icon icon="ep:warning-filled" />
</el-tooltip>
<el-checkbox v-model="form.steviaPhValueOk" :disabled="type === 'view'"
>合格</el-checkbox
>
</div>
</el-form-item>
</el-col>
</el-row>
<!-- <el-row :gutter="20">-->
<!-- <el-col :span="12">-->
<!-- <el-form-item label="产量">-->
<!-- <div style="display: flex; align-items: center; gap: 8px">-->
<!-- <el-input-number-->
<!-- v-model="form.steviaYield"-->
<!-- :min="0"-->
<!-- :step="0.01"-->
<!-- :precision="4"-->
<!-- :disabled="type === 'view'"-->
<!-- />-->
<!-- <span style="white-space: nowrap">kg</span>-->
<!-- <el-checkbox v-model="form.steviaYieldOk" :disabled="type === 'view'"-->
<!-- >合格</el-checkbox-->
<!-- >-->
<!-- </div>-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- </el-row>-->
<el-form-item label="甜菊糖备注">
<el-input
v-model="form.steviaRemark"
type="textarea"
:rows="3"
placeholder="请输入甜菊糖相关备注"
:disabled="type === 'view'"
/>
</el-form-item>
</template>
<!-- 备注 -->
<el-form-item label="备注" prop="remark">
<el-input
v-model="form.remark"
type="textarea"
:rows="3"
placeholder="请输入备注"
:disabled="type === 'view'"
/>
</el-form-item>
<!-- 附件 -->
<el-form-item label="附件" prop="attachmentUrls">
<UploadFile
v-model="form.attachmentUrls"
:disabled="type === 'view'"
:limit="1"
:file-size="10"
:file-type="['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xls', 'xlsx']"
:is-show-tip="true"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button v-if="type !== 'view'" type="primary" @click="submitForm"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, reactive, watch, defineProps, defineEmits, computed } from 'vue'
import { ElMessage } from 'element-plus'
import { YVHgetWorkOrderSimpleList } from '@/api/mes/production/workorder'
import {
createFinishedProductQuality,
updateFinishedProductQuality
} from '@/api/mes/quality/productquality/index'
import UploadFile from '@/components/UploadFile/src/UploadFile.vue'
import { Icon } from '@/components/Icon'
import { useUserStore } from '@/store/modules/user'
const props = defineProps({
visible: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'create'
},
title: {
type: String,
default: ''
},
data: {
type: Object,
default: () => ({})
}
})
const emit = defineEmits(['update:visible', 'submit', 'closed'])
// 用户store
const userStore = useUserStore()
// 批次选项
const workOrderOptions = ref<any[]>([])
// 获取当前时间字符串
const getCurrentTimeString = () => {
const now = new Date()
const y = now.getFullYear()
const m = String(now.getMonth() + 1).padStart(2, '0')
const d = String(now.getDate()).padStart(2, '0')
const hh = String(now.getHours()).padStart(2, '0')
const mm = String(now.getMinutes()).padStart(2, '0')
const ss = String(now.getSeconds()).padStart(2, '0')
return `${y}-${m}-${d}T${hh}:${mm}:${ss}`
}
// 获取中文日期YYYY 年 MM 月 DD 日)
const getCurrentDateCN = () => {
const now = new Date()
const year = now.getFullYear()
const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0')
return `${year}${month}${day}`
}
// 番茄类默认值
const getTomatoDefaults = () => ({
tomatoProcess: '冷破',
tomatoConcentration: undefined as number | undefined,
tomatoQuantity: undefined as number | undefined,
tomatoSensory:
'色泽、气滋味、组织、形态均正常,无恶性杂质\n[sensory analysis: color and luster, flavor,tissue; form are normal, without impurity]',
tomatoSolubleSolidsPercent: undefined as number | undefined,
tomatoViscosityCmPer: undefined as number | undefined,
tomatoLycopeneMgPer: undefined as number | undefined,
tomatoTotalAcidPercent: undefined as number | undefined,
tomatoPhValue: undefined as number | undefined,
tomatoColorDifference: undefined as number | undefined,
tomatoMouldPositivePercent: undefined as number | undefined,
tomatoAssessConformance: 1 as 1 | 0,
tomatoAssessRemark: ''
})
// 甜菊糖默认值
const getSteviaDefaults = () => ({
steviaLabel: `食品添加剂名称: 甜菊糖苷\n配料清单: 从甜叶菊中天然提取\n净含量: 每袋 10kg\n生产商: 甘肃西域阳光管理平台食品有限公司\n地址: 甘肃金塔县金塔镇解放路42号\n储存说明: 常温保存 24 个月\n用途: 食品添加剂,使用方法和范围见国家标准规定\n生产许可证号: SC10962092100105\n产品标准: GB1886.355-2022\n生产日期: ${getCurrentDateCN()}`,
steviaColor: '',
steviaState: '',
steviaGlycosidesPercent: undefined as number | undefined,
steviaAshPercent: undefined as number | undefined,
steviaLossOnDryingPercent: undefined as number | undefined,
steviaMethanolMgPerKg: undefined as number | undefined,
steviaEthanolMgPerKg: undefined as number | undefined,
steviaLeadMgPerKg: undefined as number | undefined,
steviaTotalArsenicMgPerKg: undefined as number | undefined,
steviaPhValue: undefined as number | undefined,
// 合格勾选项默认值
steviaLabelOk: true,
steviaColorOk: true,
steviaStateOk: true,
steviaGlycosidesPercentOk: true,
steviaAshPercentOk: true,
steviaLossOnDryingPercentOk: true,
steviaMethanolMgPerKgOk: true,
steviaEthanolMgPerKgOk: true,
steviaLeadMgPerKgOk: true,
steviaTotalArsenicMgPerKgOk: true,
steviaPhValueOk: true,
steviaRd: undefined as number | undefined,
steviaRdOk: true,
steviaRa: undefined as number | undefined,
steviaRaOk: true,
steviaStv: undefined as number | undefined,
steviaStvOk: true,
steviaRf: undefined as number | undefined,
steviaRfOk: true,
steviaRc: undefined as number | undefined,
steviaRcOk: true,
steviaDa: undefined as number | undefined,
steviaDaOk: true,
steviaRv: undefined as number | undefined,
steviaRvOk: true,
steviaRb: undefined as number | undefined,
steviaRbOk: true,
steviaSb: undefined as number | undefined,
steviaSbOk: true,
steviaMoisture: undefined as number | undefined,
steviaMoistureOk: true,
steviaSpecificAbsorbance: undefined as number | undefined,
steviaSpecificAbsorbanceOk: true,
steviaTransmittance: undefined as number | undefined,
steviaTransmittanceOk: true,
steviaYield: undefined as number | undefined,
steviaYieldOk: true,
steviaRemark: ''
})
// 表单数据
const form = reactive({
code: '',
workOrderId: undefined as number | undefined,
workOrderCode: '',
productId: undefined as number | undefined,
productCode: '',
productName: '',
status: 0,
inspectionTime: getCurrentTimeString(),
weight: undefined as number | undefined,
qualified: 1,
unqualifiedReason: '',
inspectorName: userStore.getUser.nickname || '',
attachmentUrls: '',
remark: '',
...getTomatoDefaults(),
...getSteviaDefaults()
})
// 计算:是否番茄类产品
const isTomatoProduct = computed(() => {
const code = (form.workOrderCode || '').toString().toUpperCase()
return code.startsWith('X8')
})
// 计算是否甜菊糖产品X 开头但排除 X8
const isSteviaProduct = computed(() => {
const code = (form.workOrderCode || '').toString().toUpperCase()
return code.startsWith('X') && !code.startsWith('X8')
})
// 甜菊糖字段阈值配置
const steviaThresholds = {
steviaGlycosidesPercent: { min: 0, max: 1000, label: '甜菊糖苷含量' },
steviaAshPercent: { min: 0, max: 1000, label: '灰分' },
steviaPhValue: { min: 0, max: 1000, label: 'pH值' },
steviaStv: { min: 0, max: 1000, label: 'STV' },
steviaMoisture: { min: 0, max: 1000, label: '水分' },
steviaSpecificAbsorbance: { min: 0, max: 1000, label: '比吸光' },
steviaTransmittance: { min: 0, max: 1000, label: '透光率' }
} as const
// const steviaThresholds = {
// steviaGlycosidesPercent: { min: 80, max: 100, label: '甜菊糖苷含量' },
// steviaAshPercent: { min: 0.01, max: 0.1, label: '灰分' },
// steviaPhValue: { min: 5, max: 7, label: 'pH值' },
// steviaStv: { min: 10, max: 20, label: 'STV' },
// steviaMoisture: { min: 4, max: 6, label: '水分' },
// steviaSpecificAbsorbance: { min: 0.01, max: 0.2, label: '比吸光' },
// steviaTransmittance: { min: 96, max: 100, label: '透光率' }
// } as const
const autoUnqualifiedReasonPrefix = '以下指标超出阈值范围:'
// 检查甜菊糖字段是否在阈值范围内
const checkSteviaThreshold = (fieldName: keyof typeof steviaThresholds, value?: number) => {
if (value === undefined || value === null) return true // 空值不检查
const threshold = steviaThresholds[fieldName]
return value >= threshold.min && value <= threshold.max
}
const setSteviaOkFlag = (fieldName: keyof typeof steviaThresholds, pass: boolean) => {
const okFieldName = `${fieldName}Ok`
if (okFieldName in form) {
;(form as any)[okFieldName] = pass
}
}
// 获取所有不合格的甜菊糖指标
const getUnqualifiedSteviaFields = () => {
const unqualifiedFields: string[] = []
;(Object.keys(steviaThresholds) as Array<keyof typeof steviaThresholds>).forEach((fieldName) => {
const value = (form as any)[fieldName] as number | undefined
if (!checkSteviaThreshold(fieldName, value)) {
unqualifiedFields.push(steviaThresholds[fieldName].label)
}
})
return unqualifiedFields
}
const updateSteviaWarnings = () => {
;(Object.keys(steviaThresholds) as Array<keyof typeof steviaThresholds>).forEach((fieldName) => {
const value = (form as any)[fieldName] as number | undefined
const pass = checkSteviaThreshold(fieldName, value)
setSteviaOkFlag(fieldName, pass)
})
}
// 自动检查并更新合格状态
const autoCheckQualified = () => {
if (!isSteviaProduct.value) return
const unqualifiedFields = getUnqualifiedSteviaFields()
if (unqualifiedFields.length > 0) {
form.qualified = 0
form.unqualifiedReason = `${autoUnqualifiedReasonPrefix}${unqualifiedFields.join('、')}`
} else {
if (form.unqualifiedReason.startsWith(autoUnqualifiedReasonPrefix)) {
form.unqualifiedReason = ''
if (form.qualified === 0) {
form.qualified = 1
}
}
}
}
// 表单校验规则
const rules = {
workOrderCode: [{ required: true, message: '请选择批次', trigger: 'change' }],
weight: [{ required: true, message: '请输入重量', trigger: 'change' }],
status: [{ required: true, message: '请选择质检状态', trigger: 'change' }],
inspectionTime: [{ required: true, message: '请选择质检时间', trigger: 'change' }],
tomatoProcess: [{ required: true, message: '请选择工艺', trigger: 'change' }],
qualified: [{ required: true, message: '请选择是否合格', trigger: 'change' }],
inspectorName: [{ required: true, message: '请输入质检员姓名', trigger: 'blur' }],
unqualifiedReason: [
{
required: true,
message: '请输入不合格原因',
trigger: 'blur',
validator: (_rule: any, value: string, callback: Function) => {
if (!form.qualified && !value) {
callback(new Error('请输入不合格原因'))
} else {
callback()
}
}
}
]
}
// 表单引用
const formRef = ref()
// 对话框可见性
const dialogVisible = ref(false)
// 监听props.visible变化
watch(
() => props.visible,
(val) => {
dialogVisible.value = val
if (val) {
// 打开对话框时获取批次列表
getWorkOrderList()
// 如果是编辑或查看,则填充表单数据
if (props.type === 'edit' || props.type === 'view') {
Object.assign(form, getTomatoDefaults(), getSteviaDefaults(), props.data)
} else if (props.type === 'create') {
// 重置表单
Object.assign(form, {
code: '',
workOrderId: undefined,
workOrderCode: '',
productId: undefined,
productCode: '',
productName: '',
status: 0,
inspectionTime: getCurrentTimeString(),
weight: undefined,
qualified: 1,
unqualifiedReason: '',
inspectorName: userStore.getUser.nickname || '',
attachmentUrls: '',
remark: '',
...getTomatoDefaults(),
...getSteviaDefaults()
})
}
}
}
)
// 监听dialogVisible变化
watch(
() => dialogVisible.value,
(val) => {
emit('update:visible', val)
}
)
// 监听甜菊糖相关字段,超出阈值时自动预警
watch(
[
() => isSteviaProduct.value,
() => form.steviaGlycosidesPercent,
() => form.steviaAshPercent,
() => form.steviaPhValue,
() => form.steviaStv,
() => form.steviaMoisture,
() => form.steviaSpecificAbsorbance,
() => form.steviaTransmittance
],
() => {
updateSteviaWarnings()
if (isSteviaProduct.value) {
autoCheckQualified()
}
},
{ immediate: true }
)
// 获取批次列表
const getWorkOrderList = async () => {
try {
const res = await YVHgetWorkOrderSimpleList({ status: 4 }) // 只获取已完成的批次
// 由于axios响应拦截器的处理res直接就是data数组
if (Array.isArray(res)) {
workOrderOptions.value = res
} else {
console.error('获取批次列表返回格式错误:', res)
ElMessage.error('获取批次列表返回格式错误')
}
} catch (error) {
console.error('获取批次列表失败', error)
ElMessage.error('获取批次列表失败')
}
}
// 批次选择变更处理
const handleWorkOrderChange = (value: string) => {
const selectedWorkOrder = workOrderOptions.value.find((item) => item.code === value)
if (selectedWorkOrder) {
// 如果是从下拉列表中选择的批次
form.workOrderId = selectedWorkOrder.id
form.productId = selectedWorkOrder.productId
form.productCode = selectedWorkOrder.productCode
form.productName = selectedWorkOrder.productName
form.weight = selectedWorkOrder.producedQuantity
} else {
// 如果是手动输入的批次编号
form.workOrderId = undefined
form.productId = undefined
form.productCode = ''
form.productName = ''
}
}
// 提交表单
const submitForm = async () => {
if (!formRef.value) return
await formRef.value.validate(async (valid: boolean) => {
if (valid) {
try {
const submitData = { ...form }
// 确保日期是ISO格式
if (submitData.inspectionTime) {
const date = new Date(submitData.inspectionTime)
if (!isNaN(date.getTime())) {
submitData.inspectionTime = date.toISOString()
}
}
// 处理附件 URL后端要求字符串
if (submitData.attachmentUrls && typeof submitData.attachmentUrls !== 'string') {
try {
submitData.attachmentUrls = JSON.stringify(submitData.attachmentUrls)
} catch (e) {
submitData.attachmentUrls = ''
}
}
// 调用 API
const api =
props.type === 'create' ? createFinishedProductQuality : updateFinishedProductQuality
const res = await api(submitData)
// 统一处理成功情况
if (res) {
ElMessage.success('操作成功')
dialogVisible.value = false
emit('submit')
} else {
ElMessage.error(res.msg || '操作失败')
}
} catch (error: any) {
ElMessage.error(error.message || '操作失败')
}
}
})
}
// 对话框关闭事件
const handleClosed = () => {
emit('closed')
}
</script>