| | |
| | | @change="getModels" |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | :disabled="operationType === 'edit'" |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="规格型号:" prop="model"> |
| | | <el-input v-model="form.model" placeholder="请输入" clearable/> |
| | | <el-form-item label="规格型号:" prop="productModelId"> |
| | | <el-select v-model="form.productModelId" placeholder="请选择" clearable :disabled="operationType === 'edit'" |
| | | filterable readonly @change="handleChangeModel"> |
| | | <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="指标选择:" prop="testStandardId"> |
| | | <el-select |
| | | v-model="form.testStandardId" |
| | | placeholder="请选择指标" |
| | | clearable |
| | | @change="handleTestStandardChange" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="item in testStandardOptions" |
| | | :key="item.id" |
| | | :label="item.standardName || item.standardNo" |
| | | :value="item.id" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="单位:" prop="unit"> |
| | | <el-input v-model="form.unit" placeholder="请输入" disabled/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="单位:" prop="unit"> |
| | | <el-input v-model="form.unit" placeholder="请输入" clearable/> |
| | | <el-form-item label="批号:" prop="batchNo"> |
| | | <el-input |
| | | v-model="form.batchNo" |
| | | placeholder="请输入" |
| | | clearable |
| | | :disabled="operationType === 'edit'" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="数量:" prop="quantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请输入" clearable :precision="2"/> |
| | | <el-form-item label="UID码:" prop="uidNo"> |
| | | <el-input v-model="form.uidNo" placeholder="请输入" disabled/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检品数量:" prop="inspectedQuantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.inspectedQuantity" placeholder="请输入" clearable :precision="2"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="总数量:" prop="quantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请输入" clearable :precision="2" :disabled="quantityDisabled"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | |
| | | <el-col :span="12"> |
| | | <el-form-item label="生产日期:" prop="productionDate"> |
| | | <el-date-picker |
| | | v-model="form.productionDate" |
| | | type="date" |
| | | placeholder="请选择日期" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | @change="calculateValidityDate" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="有效期:" prop="validityDate"> |
| | | <el-date-picker |
| | | v-model="form.validityDate" |
| | | type="date" |
| | | placeholder="自动计算或手动选择" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检验用粉剂/液情况:" prop="inspectMaterialConditionId"> |
| | | <el-tree-select |
| | | v-model="form.inspectMaterialConditionId" |
| | | placeholder="请选择" |
| | | clearable |
| | | check-strictly |
| | | :data="inspectProductOptions" |
| | | :render-after-expand="false" |
| | | style="width: 100%" |
| | | @change="getInspectModels" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="规格型号(检验):" prop="inspectProductModelId"> |
| | | <el-select |
| | | v-model="form.inspectProductModelId" |
| | | placeholder="请选择" |
| | | clearable |
| | | filterable |
| | | @change="getInspectProductModel" |
| | | > |
| | | <el-option v-for="item in inspectModelOptions" :key="item.id" :label="item.model" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="UID码(检验):" prop="inspectUidNo"> |
| | | <el-input v-model="form.inspectUidNo" placeholder="自动带出" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="批号(检验):" prop="inspectBatchNo"> |
| | | <el-select |
| | | v-model="form.inspectBatchNo" |
| | | placeholder="请选择" |
| | | clearable |
| | | filterable |
| | | style="width: 100%" |
| | | @change="handleInspectBatchNoChange" |
| | | > |
| | | <el-option |
| | | v-for="item in inspectBatchNoOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="供应商(检验):" prop="inspectSupplier"> |
| | | <el-select |
| | | v-model="form.inspectSupplier" |
| | | placeholder="请选择" |
| | | clearable |
| | | filterable |
| | | style="width: 100%" |
| | | :disabled="!inspectSupplierOptions.length" |
| | | @change="handleInspectSupplierChange" |
| | | > |
| | | <el-option |
| | | v-for="item in inspectSupplierOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="生产日期(检验):" prop="inspectProductionDate"> |
| | | <el-input v-model="form.inspectProductionDate" placeholder="自动带出" disabled/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="有效期(检验):" prop="inspectValidityDate"> |
| | | <el-input v-model="form.inspectValidityDate" placeholder="自动计算" disabled/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <PIMTable |
| | | rowKey="id" |
| | |
| | | :tableLoading="tableLoading" |
| | | height="400" |
| | | > |
| | | <template #slot="{ row }"> |
| | | <el-input v-model="row.testValue" clearable/> |
| | | </template> |
| | | <template #instrument="{ row }"> |
| | | <el-select |
| | | v-model="row.instrument" |
| | | placeholder="请选择或输入" |
| | | filterable |
| | | allow-create |
| | | default-first-option |
| | | clearable |
| | | style="width: 100%" |
| | | @change="handleInstrumentChange(row)" |
| | | > |
| | | <el-option label="目测" value="目测" /> |
| | | <el-option |
| | | v-for="item in deviceList" |
| | | :key="item.id" |
| | | :label="item.deviceName + (item.deviceModel ? ' / ' + item.deviceModel : '')" |
| | | :value="item.deviceName" |
| | | /> |
| | | </el-select> |
| | | </template> |
| | | <template #deviceStatus="{ row }"> |
| | | <el-select |
| | | v-model="row.deviceStatus" |
| | | placeholder="请选择" |
| | | default-first-option |
| | | clearable |
| | | style="width: 100%" |
| | | > |
| | | <el-option label="正常" value="正常" /> |
| | | <el-option label="停机" value="停机" /> |
| | | <el-option label="运行" value="运行" /> |
| | | <el-option label="维修" value="维修" /> |
| | | <el-option label="/" value="/" /> |
| | | </el-select> |
| | | </template> |
| | | <template #result="{ row }"> |
| | | <el-input v-model="row.result" placeholder="请输入" clearable /> |
| | | </template> |
| | | <template #resultJudgment="{ row }"> |
| | | <el-select v-model="row.resultJudgment" placeholder="请选择" clearable style="width: 100%"> |
| | | <el-option label="合格" value="合格" /> |
| | | <el-option label="不合格" value="不合格" /> |
| | | <el-option label="/" value="/" /> |
| | | </el-select> |
| | | </template> |
| | | </PIMTable> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref} from "vue"; |
| | | import {ref, reactive, toRefs, computed, getCurrentInstance, nextTick} from "vue"; |
| | | import {getOptions} from "@/api/procurementManagement/procurementLedger.js"; |
| | | import {productTreeList} from "@/api/basicData/product.js"; |
| | | import {modelList, productTreeList} from "@/api/basicData/product.js"; |
| | | import {qualityInspectAdd, qualityInspectUpdate} from "@/api/qualityManagement/rawMaterialInspection.js"; |
| | | import {userListNoPage} from "@/api/system/user.js"; |
| | | import {qualityInspectDetailByProductId} from "@/api/qualityManagement/metricMaintenance.js"; |
| | | import { getStockInventoryAll } from "@/api/inventoryManagement/stockInventory.js"; |
| | | import {qualityInspectDetailByProductId, getQualityTestStandardParamByTestStandardId} from "@/api/qualityManagement/metricMaintenance.js"; |
| | | import {qualityInspectParamInfo} from "@/api/qualityManagement/qualityInspectParam.js"; |
| | | import {deviceList as qualityInspectParamDeviceList} from "@/api/energyManagement/index.js"; |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref('') |
| | | |
| | | const validateBatchNo = (rule, value, callback) => { |
| | | if (value === undefined || value === null || String(value).trim() === '') { |
| | | callback(new Error('请输入批号')); |
| | | return; |
| | | } |
| | | callback(); |
| | | }; |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | | checkTime: "", |
| | |
| | | checkName: "", |
| | | productName: "", |
| | | productId: "", |
| | | productModelId: "", |
| | | model: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | uidNo: "", |
| | | batchNo: "", |
| | | inspectedQuantity: "", |
| | | quantity: "", |
| | | productionDate: "", |
| | | validityDate: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | // 检验用粉剂/液情况相关新字段 |
| | | inspectMaterialConditionId: "", |
| | | inspectMaterialCondition: "", |
| | | inspectProductModelId : "", |
| | | inspectBatchNo: "", |
| | | inspectSupplier: "", |
| | | inspectProductModel: "", |
| | | inspectUidNo: "", |
| | | inspectProductionDate: "", |
| | | inspectValidityDate: "", |
| | | inspectValidityPeriod: "", |
| | | }, |
| | | rules: { |
| | | checkTime: [{ required: false, message: "请输入", trigger: "blur" },], |
| | | checkTime: [{ required: true, message: "请输入", trigger: "blur" },], |
| | | process: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | checkName: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | productId: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | model: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | productModelId: [{ required: true, message: "请选择", trigger: "change" }], |
| | | testStandardId: [{required: false, message: "请选择指标", trigger: "change"}], |
| | | unit: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | inspectedQuantity: [ |
| | | { required: true, message: "请输入检品数量", trigger: "blur" }, |
| | | { |
| | | validator: (rule, value, callback) => { |
| | | if (value !== '' && value !== null && value !== undefined) { |
| | | const qty = Number(form.value.quantity); |
| | | const inspectedQty = Number(value); |
| | | if (!isNaN(qty) && !isNaN(inspectedQty) && inspectedQty > qty) { |
| | | callback(new Error("检品数量不能大于总数量")); |
| | | } else { |
| | | callback(); |
| | | } |
| | | } else { |
| | | callback(); |
| | | } |
| | | }, |
| | | trigger: "blur" |
| | | } |
| | | ], |
| | | quantity: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | checkCompany: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | batchNo: [{ required: true, validator: validateBatchNo, trigger: "blur" }], |
| | | checkResult: [{ required: true, message: "请输入", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | // 编辑时:productMainId 或 purchaseLedgerId 任一有值则数量置灰 |
| | | const quantityDisabled = computed(() => { |
| | | const v = form.value || {}; |
| | | return !!(v.productMainId != null || v.purchaseLedgerId != null); |
| | | }); |
| | | const supplierList = ref([]); |
| | | const productOptions = ref([]); |
| | | const inspectProductOptions = ref([]); |
| | | const inspectModelOptions = ref([]); |
| | | const inspectBatchNoOptions = ref([]); |
| | | const inspectSupplierOptions = ref([]); |
| | | let inspectStockInventoryAllTree = []; |
| | | let inspectBatchNodeByBatchNo = new Map(); |
| | | |
| | | const normalizeInspectStockTree = (nodes = []) => { |
| | | const normalizeNodeValue = (node) => { |
| | | if (node?.id !== null && node?.id !== undefined) return String(node.id); |
| | | if (node?.nodeType === "batch") return String(node.batchNo ?? node.label ?? ""); |
| | | if (node?.nodeType === "customer") return String(node.customer ?? node.label ?? ""); |
| | | if (node?.nodeType === "model") return String(node.productModelId ?? node.model ?? node.label ?? ""); |
| | | return String(node.productName ?? node.label ?? ""); |
| | | }; |
| | | |
| | | const normalized = (list) => |
| | | (list || []).map((n) => { |
| | | const value = normalizeNodeValue(n); |
| | | const label = n.label ?? n.productName ?? n.model ?? n.batchNo ?? n.customer ?? ""; |
| | | return { |
| | | ...n, |
| | | value, |
| | | label, |
| | | children: normalized(n.children), |
| | | }; |
| | | }); |
| | | |
| | | return normalized(nodes); |
| | | }; |
| | | |
| | | // 检验用粉剂/液树形下拉:不展示型号、批号(由右侧「规格型号」「批号」单独选择) |
| | | function filterInspectTreeForPowderSelect(nodes = []) { |
| | | return (nodes || []) |
| | | .filter( |
| | | (n) => |
| | | n && |
| | | n.nodeType !== "customer" && |
| | | n.nodeType !== "model" && |
| | | n.nodeType !== "batch" |
| | | ) |
| | | .map((n) => ({ |
| | | ...n, |
| | | children: filterInspectTreeForPowderSelect(n.children || []), |
| | | })); |
| | | } |
| | | |
| | | /** 在产品子树内递归收集型号节点(型号可能在第 3 层及更深,而非产品的直接子节点) */ |
| | | function collectInspectSubtreeModels(node) { |
| | | const out = []; |
| | | const walk = (n) => { |
| | | if (!n) return; |
| | | if (n.nodeType === "model") { |
| | | out.push(n); |
| | | return; |
| | | } |
| | | for (const c of n.children || []) walk(c); |
| | | }; |
| | | walk(node); |
| | | return out; |
| | | } |
| | | |
| | | const findInspectNodeObjByValue = (nodes = [], value) => { |
| | | for (let i = 0; i < (nodes || []).length; i++) { |
| | | const node = nodes[i]; |
| | | if (String(node?.value) === String(value)) return node; |
| | | const children = node?.children || []; |
| | | if (children.length) { |
| | | const found = findInspectNodeObjByValue(children, value); |
| | | if (found) return found; |
| | | } |
| | | } |
| | | return null; |
| | | }; |
| | | |
| | | function findInspectNodeIdByLabel(nodes, label) { |
| | | if (!label) return null; |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | const node = nodes[i]; |
| | | if (node.label === label) return node.value; |
| | | if (node.children && node.children.length > 0) { |
| | | const found = findInspectNodeIdByLabel(node.children, label); |
| | | if (found !== null && found !== undefined) return found; |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "指标", |
| | | label: "检测项目", |
| | | prop: "parameterItem", |
| | | width: 150 |
| | | }, |
| | | { |
| | | label: "单位", |
| | | prop: "unit", |
| | | }, |
| | | { |
| | | label: "标准值", |
| | | label: "标准要求", |
| | | prop: "standardValue", |
| | | width: 180 |
| | | }, |
| | | { |
| | | label: "单位", |
| | | prop: "unit", |
| | | width: 70 |
| | | }, |
| | | { |
| | | label: "检测器具", |
| | | prop: "instrument", |
| | | dataType: 'slot', |
| | | slot: 'instrument', |
| | | width: 220 |
| | | }, |
| | | { |
| | | label: "设备状态", |
| | | prop: "deviceStatus", |
| | | dataType: 'slot', |
| | | slot: 'deviceStatus', |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "检测结果", |
| | | prop: "result", |
| | | dataType: 'slot', |
| | | slot: 'result', |
| | | width: 150 |
| | | }, |
| | | { |
| | | label: "内控值", |
| | | prop: "controlValue", |
| | | }, |
| | | { |
| | | label: "检验值", |
| | | prop: "testValue", |
| | | label: "结果判断", |
| | | prop: "resultJudgment", |
| | | dataType: 'slot', |
| | | slot: 'slot', |
| | | slot: 'resultJudgment', |
| | | width: 120 |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | const userList = ref([]); |
| | | const currentProductId = ref(0); |
| | | |
| | | const testStandardOptions = ref([]); // 指标选择下拉框数据 |
| | | const modelOptions = ref([]); |
| | | const deviceList = ref([]); |
| | | // 打开弹框 |
| | | const openDialog = async (type, row) => { |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | getOptions().then((res) => { |
| | | supplierList.value = res.data; |
| | | }); |
| | | let userLists = await userListNoPage(); |
| | | userList.value = userLists.data; |
| | | form.value = {} |
| | | getProductOptions(); |
| | | // 先清空表单验证状态,避免闪烁 |
| | | await nextTick(); |
| | | proxy.$refs.formRef?.clearValidate(); |
| | | // 加载设备台账列表 |
| | | loadDeviceList(); |
| | | |
| | | // 并行加载基础数据 |
| | | const [userListsRes] = await Promise.all([ |
| | | userListNoPage(), |
| | | getMainProductTree(), |
| | | getInspectStockTree(), |
| | | getOptions().then((res) => { |
| | | supplierList.value = res.data; |
| | | }) |
| | | ]); |
| | | userList.value = userListsRes.data; |
| | | |
| | | form.value = {} |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | |
| | | if (operationType.value === 'edit') { |
| | | form.value = {...row} |
| | | currentProductId.value = row.productId || 0 |
| | | getQualityInspectParamList(row.id) |
| | | // 先保存 testStandardId,避免被清空 |
| | | const savedTestStandardId = row.testStandardId; |
| | | // 先设置表单数据,但暂时清空 testStandardId,等选项加载完成后再设置 |
| | | form.value = {...row, testStandardId: ''} |
| | | currentProductId.value = row.productId || 0 |
| | | // 清空验证状态,避免数据加载过程中的校验闪烁 |
| | | nextTick(() => { |
| | | proxy.$refs.formRef?.clearValidate(); |
| | | }); |
| | | |
| | | // 编辑模式下,并行加载规格型号和指标选项 |
| | | if (currentProductId.value) { |
| | | // 设置产品名称 |
| | | form.value.productName = findNodeById(productOptions.value, currentProductId.value); |
| | | |
| | | // 并行加载规格型号和指标选项 |
| | | const params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 2 |
| | | }; |
| | | |
| | | Promise.all([ |
| | | modelList({ id: currentProductId.value }), |
| | | qualityInspectDetailByProductId(params) |
| | | ]).then(([modelRes, testStandardRes]) => { |
| | | // 设置规格型号选项 |
| | | modelOptions.value = modelRes || []; |
| | | // 如果表单中已有 productModelId,设置对应的 model 和 unit |
| | | if (form.value.productModelId && modelOptions.value.length > 0) { |
| | | const selectedModel = modelOptions.value.find(item => item.id == form.value.productModelId); |
| | | if (selectedModel) { |
| | | form.value.model = selectedModel.model || ''; |
| | | form.value.unit = selectedModel.unit || ''; |
| | | form.value.uidNo = selectedModel.uidNo || ''; |
| | | } |
| | | } |
| | | |
| | | // 设置指标选项 |
| | | testStandardOptions.value = testStandardRes.data || []; |
| | | |
| | | // 设置 testStandardId 并加载参数列表 |
| | | nextTick(async () => { |
| | | if (savedTestStandardId) { |
| | | // 确保类型匹配(item.id 可能是数字或字符串) |
| | | const matchedOption = testStandardOptions.value.find(item => |
| | | item.id == savedTestStandardId || String(item.id) === String(savedTestStandardId) |
| | | ); |
| | | if (matchedOption) { |
| | | // 确保使用匹配项的 id(保持类型一致) |
| | | form.value.testStandardId = matchedOption.id; |
| | | } else { |
| | | // 如果找不到匹配项,尝试直接使用原值 |
| | | console.warn('未找到匹配的指标选项,testStandardId:', savedTestStandardId, '可用选项:', testStandardOptions.value); |
| | | form.value.testStandardId = savedTestStandardId; |
| | | } |
| | | } |
| | | await restoreInspectMaterialFromRow(row); |
| | | // 编辑场景保留已有检验值,直接拉取原参数数据 |
| | | getQualityInspectParamList(row.id); |
| | | }); |
| | | }); |
| | | } else { |
| | | nextTick(async () => { |
| | | await restoreInspectMaterialFromRow(row); |
| | | getQualityInspectParamList(row.id); |
| | | }); |
| | | } |
| | | } |
| | | } |
| | | const getProductOptions = () => { |
| | | productTreeList().then((res) => { |
| | | const getMainProductTree = () => { |
| | | return productTreeList().then((res) => { |
| | | productOptions.value = convertIdToValue(res); |
| | | }); |
| | | }; |
| | | const getModels = (value) => { |
| | | currentProductId.value = value |
| | | form.value.productName = findNodeById(productOptions.value, value); |
| | | if (currentProductId) { |
| | | getList(); |
| | | } |
| | | |
| | | const getInspectStockTree = async () => { |
| | | const res = await getStockInventoryAll(); |
| | | const data = res?.data || []; |
| | | inspectStockInventoryAllTree = normalizeInspectStockTree(data); |
| | | inspectProductOptions.value = filterInspectTreeForPowderSelect(inspectStockInventoryAllTree); |
| | | return inspectProductOptions.value; |
| | | }; |
| | | const getModels = (value) => { |
| | | form.value.productModelId = undefined; |
| | | form.value.unit = undefined; |
| | | form.value.uidNo = undefined; |
| | | form.value.batchNo = ""; |
| | | modelOptions.value = []; |
| | | currentProductId.value = value |
| | | form.value.productName = findNodeById(productOptions.value, value); |
| | | modelList({ id: value }).then((res) => { |
| | | modelOptions.value = res; |
| | | }) |
| | | if (currentProductId.value) { |
| | | getList(); |
| | | } |
| | | }; |
| | | |
| | | const handleChangeModel = (value) => { |
| | | form.value.model = modelOptions.value.find(item => item.id == value)?.model || ''; |
| | | form.value.unit = modelOptions.value.find(item => item.id == value)?.unit || ''; |
| | | form.value.uidNo = modelOptions.value.find(item => item.id == value)?.uidNo || ''; |
| | | // 选择规格型号后,如果已有生产日期则重新计算有效期 |
| | | if (form.value.productionDate) { |
| | | calculateValidityDate(); |
| | | } |
| | | } |
| | | |
| | | const findNodeById = (nodes, productId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | if (nodes[i].value === productId) { |
| | |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | } |
| | | |
| | | |
| | | return newItem; |
| | | }); |
| | | } |
| | |
| | | }) |
| | | } |
| | | const getList = () => { |
| | | qualityInspectDetailByProductId(currentProductId.value).then(res => { |
| | | tableData.value = res.data; |
| | | if (!currentProductId.value) { |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | return; |
| | | } |
| | | let params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 2 |
| | | } |
| | | qualityInspectDetailByProductId(params).then(res => { |
| | | // 保存下拉框选项数据 |
| | | testStandardOptions.value = res.data || []; |
| | | // 清空表格数据,等待用户选择指标 |
| | | tableData.value = []; |
| | | // 清空指标选择 |
| | | form.value.testStandardId = ''; |
| | | }) |
| | | } |
| | | |
| | | // 指标选择变化处理 |
| | | const handleTestStandardChange = (testStandardId) => { |
| | | if (!testStandardId) { |
| | | tableData.value = []; |
| | | return; |
| | | } |
| | | tableLoading.value = true; |
| | | getQualityTestStandardParamByTestStandardId(testStandardId).then(res => { |
| | | tableData.value = res.data || []; |
| | | }).catch(error => { |
| | | console.error('获取标准参数失败:', error); |
| | | tableData.value = []; |
| | | }).finally(() => { |
| | | tableLoading.value = false; |
| | | }) |
| | | } |
| | | const getQualityInspectParamList = (id) => { |
| | | qualityInspectParamInfo(id).then(res => { |
| | | tableData.value = res.data; |
| | | tableData.value = (res.data || []).map(item => ({...item})); |
| | | }) |
| | | } |
| | | |
| | | // 计算有效期(生产日期 + 规格型号中的有效期) |
| | | const calculateValidityDate = async () => { |
| | | if (!form.value.productionDate) { |
| | | form.value.validityDate = ''; |
| | | return; |
| | | } |
| | | // 获取规格型号的有效期 |
| | | const selectedModel = modelOptions.value.find(item => item.id == form.value.productModelId); |
| | | if (selectedModel && selectedModel.validityPeriod) { |
| | | const productionDate = new Date(form.value.productionDate); |
| | | const validityPeriod = parseFloat(selectedModel.validityPeriod); |
| | | const validityDate = new Date(productionDate); |
| | | validityDate.setFullYear(validityDate.getFullYear() + Math.floor(validityPeriod)); |
| | | validityDate.setMonth(validityDate.getMonth() + Math.round((validityPeriod % 1) * 12)); |
| | | form.value.validityDate = validityDate.toISOString().split('T')[0]; |
| | | } |
| | | }; |
| | | |
| | | // 库存树(与销售台账一致):选产品 → 规格型号 → 批号 → UID / 日期 |
| | | const getInspectModels = async (value) => { |
| | | if (value === null || value === undefined || value === "") { |
| | | form.value.inspectMaterialCondition = ""; |
| | | form.value.inspectProductModelId = null; |
| | | form.value.inspectProductModel = ""; |
| | | form.value.inspectUidNo = ""; |
| | | form.value.inspectBatchNo = ""; |
| | | form.value.inspectSupplier = ""; |
| | | form.value.inspectProductionDate = ""; |
| | | form.value.inspectValidityDate = ""; |
| | | form.value.inspectValidityPeriod = ""; |
| | | inspectModelOptions.value = []; |
| | | inspectBatchNoOptions.value = []; |
| | | inspectSupplierOptions.value = []; |
| | | inspectBatchNodeByBatchNo = new Map(); |
| | | return; |
| | | } |
| | | |
| | | const node = findInspectNodeObjByValue(inspectStockInventoryAllTree, value); |
| | | if (!node || node.nodeType === "model" || node.nodeType === "batch") return; |
| | | |
| | | const modelNodesPreview = collectInspectSubtreeModels(node); |
| | | if (!modelNodesPreview.length) return; |
| | | |
| | | form.value.inspectMaterialCondition = node.label; |
| | | form.value.inspectProductModelId = null; |
| | | form.value.inspectProductModel = ""; |
| | | form.value.inspectUidNo = ""; |
| | | form.value.inspectBatchNo = ""; |
| | | form.value.inspectSupplier = ""; |
| | | form.value.inspectProductionDate = ""; |
| | | form.value.inspectValidityDate = ""; |
| | | form.value.inspectValidityPeriod = ""; |
| | | |
| | | inspectModelOptions.value = modelNodesPreview.map((m) => ({ |
| | | id: m.value, |
| | | model: m.model ?? m.label ?? "", |
| | | unit: m.unit ?? "", |
| | | uidNo: m.uidNo ?? m.identifierCode ?? "", |
| | | })); |
| | | |
| | | inspectBatchNoOptions.value = []; |
| | | inspectSupplierOptions.value = []; |
| | | inspectBatchNodeByBatchNo = new Map(); |
| | | |
| | | try { |
| | | const list = (await modelList({ id: value })) || []; |
| | | inspectModelOptions.value = inspectModelOptions.value.map((m) => { |
| | | const hit = list.find((x) => String(x.id) === String(m.id)); |
| | | return hit |
| | | ? { |
| | | ...m, |
| | | validityPeriod: hit.validityPeriod, |
| | | uidNo: hit.uidNo ?? m.uidNo, |
| | | unit: hit.unit ?? m.unit, |
| | | } |
| | | : m; |
| | | }); |
| | | } catch (e) { |
| | | console.error("加载检验用规格型号失败", e); |
| | | } |
| | | }; |
| | | |
| | | const getInspectProductModel = (value) => { |
| | | if (value === null || value === undefined || value === "") { |
| | | form.value.inspectProductModel = ""; |
| | | form.value.inspectUidNo = ""; |
| | | form.value.inspectBatchNo = ""; |
| | | form.value.inspectSupplier = ""; |
| | | form.value.inspectProductionDate = ""; |
| | | form.value.inspectValidityDate = ""; |
| | | inspectBatchNoOptions.value = []; |
| | | inspectSupplierOptions.value = []; |
| | | inspectBatchNodeByBatchNo = new Map(); |
| | | return; |
| | | } |
| | | |
| | | const modelNode = findInspectNodeObjByValue(inspectStockInventoryAllTree, value); |
| | | if (!modelNode || modelNode.nodeType !== "model") return; |
| | | |
| | | const prevBatchNo = form.value.inspectBatchNo; |
| | | |
| | | form.value.inspectProductModelId = modelNode.value; |
| | | form.value.inspectProductModel = modelNode.model ?? modelNode.label ?? ""; |
| | | |
| | | const nextUid = modelNode.uidNo ?? modelNode.identifierCode ?? ""; |
| | | if (String(nextUid ?? "").trim() !== "") { |
| | | form.value.inspectUidNo = nextUid; |
| | | } |
| | | |
| | | const batchNodes = (modelNode.children || []).filter((b) => b.nodeType === "batch"); |
| | | inspectBatchNodeByBatchNo = new Map( |
| | | batchNodes.map((b) => { |
| | | const key = String(b.batchNo ?? b.value ?? b.label ?? "").trim(); |
| | | return [key, b]; |
| | | }) |
| | | ); |
| | | inspectBatchNoOptions.value = batchNodes.map((b) => ({ |
| | | label: String(b.batchNo ?? b.label ?? "").trim(), |
| | | value: String(b.batchNo ?? b.value ?? b.label ?? "").trim(), |
| | | })); |
| | | |
| | | const batchValues = new Set(inspectBatchNoOptions.value.map((x) => x.value)); |
| | | if (!prevBatchNo || !batchValues.has(prevBatchNo)) { |
| | | form.value.inspectBatchNo = ""; |
| | | } |
| | | |
| | | if (form.value.inspectBatchNo) { |
| | | handleInspectBatchNoChange(form.value.inspectBatchNo); |
| | | } else { |
| | | form.value.inspectProductionDate = ""; |
| | | form.value.inspectValidityDate = ""; |
| | | } |
| | | }; |
| | | |
| | | const handleInspectBatchNoChange = (batchNo) => { |
| | | const safeBatchNo = String(batchNo ?? "").trim(); |
| | | if (!safeBatchNo || !inspectBatchNodeByBatchNo.size) { |
| | | form.value.inspectProductionDate = ""; |
| | | form.value.inspectValidityDate = ""; |
| | | form.value.inspectValidityPeriod = ""; |
| | | form.value.inspectSupplier = ""; |
| | | inspectSupplierOptions.value = []; |
| | | return; |
| | | } |
| | | |
| | | const batchNode = inspectBatchNodeByBatchNo.get(String(safeBatchNo)); |
| | | if (!batchNode) { |
| | | form.value.inspectProductionDate = ""; |
| | | form.value.inspectValidityDate = ""; |
| | | form.value.inspectValidityPeriod = ""; |
| | | form.value.inspectSupplier = ""; |
| | | inspectSupplierOptions.value = []; |
| | | return; |
| | | } |
| | | |
| | | const nextUid = batchNode.uidNo ?? batchNode.identifierCode ?? batchNode.uid ?? ""; |
| | | if (String(nextUid ?? "").trim() !== "") { |
| | | form.value.inspectUidNo = nextUid; |
| | | } |
| | | |
| | | // 批号变化:先生成可选供应商;生产日期/有效期在“供应商”选择后带出 |
| | | const customers = (batchNode.children || []) |
| | | .filter((c) => c.nodeType === "customer") |
| | | .map((c) => c.customer ?? c.label ?? "") |
| | | .filter(Boolean); |
| | | const uniq = Array.from(new Set(customers)); |
| | | inspectSupplierOptions.value = uniq.map((s) => ({ label: s, value: s })); |
| | | |
| | | form.value.inspectSupplier = ""; |
| | | form.value.inspectProductionDate = ""; |
| | | form.value.inspectValidityDate = ""; |
| | | form.value.inspectValidityPeriod = ""; |
| | | |
| | | // 不需要用户再选供应商:直接取第一条 customer 计算生产日期/有效期 |
| | | if (inspectSupplierOptions.value.length) { |
| | | const first = inspectSupplierOptions.value[0].value; |
| | | form.value.inspectSupplier = first; |
| | | handleInspectSupplierChange(first); |
| | | } |
| | | }; |
| | | |
| | | const calculateInspectValidityDateByYears = (productionDate, years) => { |
| | | if (!productionDate) return ""; |
| | | const y = Number(years); |
| | | if (Number.isNaN(y) || y <= 0) return ""; |
| | | const d = new Date(productionDate); |
| | | d.setFullYear(d.getFullYear() + Math.floor(y)); |
| | | d.setMonth(d.getMonth() + Math.round((y % 1) * 12)); |
| | | return d.toISOString().split("T")[0]; |
| | | }; |
| | | |
| | | const handleInspectSupplierChange = (supplier) => { |
| | | const safeSupplier = String(supplier ?? "").trim(); |
| | | if (!safeSupplier || !inspectBatchNodeByBatchNo.size) { |
| | | form.value.inspectProductionDate = ""; |
| | | form.value.inspectValidityDate = ""; |
| | | form.value.inspectValidityPeriod = ""; |
| | | return; |
| | | } |
| | | |
| | | const safeBatchNo = String(form.value.inspectBatchNo ?? "").trim(); |
| | | const batchNode = inspectBatchNodeByBatchNo.get(safeBatchNo); |
| | | if (!batchNode) { |
| | | form.value.inspectProductionDate = ""; |
| | | form.value.inspectValidityDate = ""; |
| | | form.value.inspectValidityPeriod = ""; |
| | | return; |
| | | } |
| | | |
| | | const customerNode = (batchNode.children || []).find( |
| | | (c) => c.nodeType === "customer" && String(c.customer ?? c.label ?? "").trim() === safeSupplier |
| | | ); |
| | | if (!customerNode) { |
| | | form.value.inspectProductionDate = ""; |
| | | form.value.inspectValidityDate = ""; |
| | | form.value.inspectValidityPeriod = ""; |
| | | return; |
| | | } |
| | | |
| | | const rawProd = |
| | | customerNode.productionDate ?? |
| | | customerNode.production_date ?? |
| | | batchNode.productionDate ?? |
| | | batchNode.production_date; |
| | | form.value.inspectProductionDate = rawProd ? String(rawProd).slice(0, 10) : ""; |
| | | |
| | | // 有效期年数:优先取供应商节点,其次回退到规格型号有效期 |
| | | const validityYears = |
| | | customerNode.validityPeriod ?? |
| | | customerNode.validity_period ?? |
| | | customerNode.validityYears ?? |
| | | null; |
| | | |
| | | if (validityYears == null || validityYears === "") { |
| | | const selectedModel = inspectModelOptions.value.find( |
| | | (item) => String(item.id) === String(form.value.inspectProductModelId) |
| | | ); |
| | | form.value.inspectValidityPeriod = selectedModel?.validityPeriod ?? ""; |
| | | form.value.inspectValidityDate = calculateInspectValidityDateByYears( |
| | | form.value.inspectProductionDate, |
| | | selectedModel?.validityPeriod ?? "" |
| | | ); |
| | | } else { |
| | | form.value.inspectValidityPeriod = validityYears; |
| | | form.value.inspectValidityDate = calculateInspectValidityDateByYears( |
| | | form.value.inspectProductionDate, |
| | | validityYears |
| | | ); |
| | | } |
| | | }; |
| | | |
| | | const restoreInspectMaterialFromRow = async (row) => { |
| | | try { |
| | | if (!inspectProductOptions.value?.length) { |
| | | await getInspectStockTree(); |
| | | } |
| | | let productKey = row.inspectMaterialConditionId; |
| | | if (!productKey && row.inspectMaterialCondition) { |
| | | productKey = findInspectNodeIdByLabel(inspectProductOptions.value, row.inspectMaterialCondition); |
| | | } |
| | | if (!productKey) return; |
| | | |
| | | const categoryNode = findInspectNodeObjByValue(inspectStockInventoryAllTree, productKey); |
| | | if (!categoryNode || categoryNode.nodeType === "model" || categoryNode.nodeType === "batch") return; |
| | | |
| | | await getInspectModels(productKey); |
| | | form.value.inspectMaterialConditionId = productKey; |
| | | form.value.inspectMaterialCondition = row.inspectMaterialCondition ?? categoryNode.label; |
| | | const savedSupplier = row.inspectSupplier ?? row.supplier ?? row.inspectCustomer ?? ""; |
| | | |
| | | const merged = inspectModelOptions.value; |
| | | const targetSpec = String(row.inspectProductModel ?? "").trim(); |
| | | const currentModel = |
| | | merged.find((m) => String(m.model ?? "").trim() === targetSpec) || |
| | | merged.find((m) => String(m.id) === String(row.inspectProductModelId)); |
| | | |
| | | if (currentModel) { |
| | | form.value.inspectProductModelId = currentModel.id; |
| | | getInspectProductModel(currentModel.id); |
| | | await nextTick(); |
| | | form.value.inspectBatchNo = row.inspectBatchNo ?? ""; |
| | | if (form.value.inspectBatchNo) { |
| | | handleInspectBatchNoChange(form.value.inspectBatchNo); |
| | | if (savedSupplier) { |
| | | form.value.inspectSupplier = savedSupplier; |
| | | handleInspectSupplierChange(savedSupplier); |
| | | } |
| | | } |
| | | } else if (row.inspectProductModelId) { |
| | | form.value.inspectProductModelId = row.inspectProductModelId; |
| | | getInspectProductModel(row.inspectProductModelId); |
| | | await nextTick(); |
| | | form.value.inspectBatchNo = row.inspectBatchNo ?? ""; |
| | | if (form.value.inspectBatchNo) { |
| | | handleInspectBatchNoChange(form.value.inspectBatchNo); |
| | | if (savedSupplier) { |
| | | form.value.inspectSupplier = savedSupplier; |
| | | handleInspectSupplierChange(savedSupplier); |
| | | } |
| | | } |
| | | } |
| | | |
| | | form.value.inspectUidNo = row.inspectUidNo ?? form.value.inspectUidNo ?? ""; |
| | | } catch (e) { |
| | | console.error("回显检验用粉剂/液失败", e); |
| | | } |
| | | }; |
| | | |
| | | // 获取设备台账列表 |
| | | const loadDeviceList = () => { |
| | | qualityInspectParamDeviceList().then(res => { |
| | | deviceList.value = res.data || []; |
| | | }); |
| | | }; |
| | | |
| | | // 设备状态颜色映射 |
| | | const getDeviceStatusType = (status) => { |
| | | const map = { |
| | | '正常': 'success', |
| | | '运行': 'primary', |
| | | '停机': 'warning', |
| | | '维修': 'danger' |
| | | }; |
| | | return map[status] || 'info'; |
| | | }; |
| | | |
| | | // 检测器具变化时,自动填充设备状态 |
| | | const handleInstrumentChange = (row) => { |
| | | if (row.instrument === '目测') { |
| | | row.deviceId = null; |
| | | row.deviceName = '目测'; |
| | | row.deviceStatus = ''; |
| | | return; |
| | | } |
| | | const device = deviceList.value.find(d => d.deviceName === row.instrument); |
| | | if (device) { |
| | | row.deviceId = device.id; |
| | | row.deviceName = device.deviceName; |
| | | row.deviceStatus = device.status || ''; |
| | | } else { |
| | | row.deviceId = null; |
| | | row.deviceName = row.instrument || ''; |
| | | row.deviceStatus = ''; |
| | | } |
| | | }; |
| | | |
| | | // 关闭弹框 |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | tableData.value = []; |
| | | testStandardOptions.value = []; |
| | | inspectModelOptions.value = []; |
| | | inspectBatchNoOptions.value = []; |
| | | inspectSupplierOptions.value = []; |
| | | inspectBatchNodeByBatchNo = new Map(); |
| | | form.value.testStandardId = ''; |
| | | form.value.inspectSupplier = ''; |
| | | form.value.inspectBatchNo = ''; |
| | | form.value.inspectProductionDate = ''; |
| | | form.value.inspectValidityDate = ''; |
| | | form.value.inspectValidityPeriod = ''; |
| | | dialogFormVisible.value = false; |
| | | emit('close') |
| | | }; |