| | |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="类别:" prop="inspectType"> |
| | | <el-select v-model="form.inspectType"> |
| | | <el-option label="原材料检验" :value="0" /> |
| | | <el-option label="过程检验" :value="1" /> |
| | | <el-option label="出厂检验" :value="2" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="产品名称:" prop="productId"> |
| | | <el-tree-select |
| | | v-if="operationType !== 'edit'" |
| | | v-model="form.productId" |
| | | placeholder="请选择" |
| | | clearable |
| | |
| | | :render-after-expand="false" |
| | | style="width: 100%" |
| | | /> |
| | | <!-- 编辑态:不依赖下拉选项回显,直接展示文本 --> |
| | | <el-input |
| | | v-else |
| | | v-model="form.productName" |
| | | disabled |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="规格型号:" prop="model"> |
| | | <el-select v-model="form.model" 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 label="规格型号:" prop="productModelId"> |
| | | <el-select |
| | | v-if="operationType !== 'edit'" |
| | | v-model="form.productModelId" |
| | | placeholder="请选择" |
| | | clearable |
| | | filterable |
| | | readonly |
| | | @change="handleChangeModel" |
| | | > |
| | | <el-option |
| | | v-for="item in modelOptions" |
| | | :key="item.id" |
| | | :label="item.model" |
| | | :value="item.id" |
| | | /> |
| | | </el-select> |
| | | <!-- 编辑态:不展示规格型号列表,直接展示文本 --> |
| | | <el-input |
| | | v-else |
| | | v-model="form.model" |
| | | disabled |
| | | style="width: 100%" |
| | | /> |
| | | </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-input v-model="form.unit" disabled/> |
| | | </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="批号:" prop="batchNo"> |
| | | <el-input style="width: 100%" v-model="form.batchNo" placeholder="请输入" clearable/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检验类型:" prop="checkType"> |
| | | <el-select v-model="form.checkType"> |
| | | <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="checkResult"> |
| | | <el-select v-model="form.checkResult"> |
| | | <el-option label="合格" :value="1"/> |
| | | <el-option label="不合格" :value="0"/> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> --> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检验员:" prop="checkName"> |
| | | <el-input v-model="form.checkName" placeholder="请输入" clearable/> |
| | | <el-select v-model="form.checkName" placeholder="请选择" clearable filterable style="width: 100%"> |
| | | <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" |
| | | :value="item.nickName"/> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="处理结果:" prop="dealResult"> |
| | | <el-select v-model="form.dealResult" placeholder="请选择" clearable> |
| | | <el-option :label="item.label" :value="item.value" v-for="item in rejection_handling" :key="item.value" /> |
| | | <el-select v-model="form.dealResult" placeholder="报废" disabled> |
| | | <el-option |
| | | :label="item.label" |
| | | :value="item.value" |
| | | v-for="item in rejection_handling" |
| | | :key="item.value" |
| | | :disabled="String(item.label) !== '报废'" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="处理人:" prop="dealName"> |
| | | <el-input v-model="form.dealName" placeholder="请输入" clearable/> |
| | | <el-select v-model="form.dealName" placeholder="请选择" clearable filterable style="width: 100%"> |
| | | <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" |
| | | :value="item.nickName"/> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref} from "vue"; |
| | | import {ref, reactive, toRefs, getCurrentInstance} from "vue"; |
| | | import {modelList, productTreeList} from "@/api/basicData/product.js"; |
| | | import { |
| | | getQualityUnqualifiedInfo, |
| | | qualityUnqualifiedAdd, |
| | | qualityUnqualifiedUpdate |
| | | } from "@/api/qualityManagement/nonconformingManagement.js"; |
| | | import {userListNoPage} from "@/api/system/user.js"; |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref('') |
| | | const { rejection_handling } = proxy.useDict("rejection_handling") |
| | | |
| | | const getScrapDealResultValue = () => { |
| | | const list = rejection_handling?.value || rejection_handling || []; |
| | | const scrap = (list || []).find((it) => String(it?.label ?? "") === "报废"); |
| | | return scrap?.value ?? ""; |
| | | }; |
| | | const data = reactive({ |
| | | form: { |
| | | checkTime: "", |
| | | process: "", |
| | | checkName: "", |
| | | productName: "", |
| | | productId: "", |
| | | productModelId: "", |
| | | model: "", |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | batchNo: "", |
| | | checkType: undefined, |
| | | checkResult: "", |
| | | inspectType: '', |
| | | defectivePhenomena: '', |
| | | dealResult: '', |
| | | dealName: "", |
| | | dealTime: "", |
| | | }, |
| | | rules: { |
| | | checkTime: [{ required: false, message: "请输入", trigger: "blur" },], |
| | | process: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | checkName: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | productId: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | model: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | unit: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | checkCompany: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | checkResult: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | checkTime: [{ required: true, message: "请选择检测日期", trigger: "change" }], |
| | | checkName: [{ required: true, message: "请选择检验员", trigger: "change" }], |
| | | productId: [{ required: true, message: "请选择产品名称", trigger: "change" }], |
| | | productModelId: [{ required: true, message: "请选择规格型号", trigger: "change" }], |
| | | batchNo: [{ required: true, message: "请输入批号", trigger: "blur" }], |
| | | checkType: [{ required: true, message: "请选择检验类型", trigger: "change" }], |
| | | checkResult: [{ required: true, message: "请选择检测结果", trigger: "change" }], |
| | | dealResult: [{ required: true, message: "处理结果默认为报废", trigger: "change" }], |
| | | dealTime: [{ required: true, message: "请选择处理日期", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | const userList = ref([]); |
| | | const productOptions = ref([]); |
| | | const modelOptions = ref([]) |
| | | |
| | | // 打开弹框 |
| | | const openDialog = (type, row) => { |
| | | const openDialog = async (type, row) => { |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | form.value = {} |
| | | getProductOptions(); |
| | | |
| | | // 编辑态不校验规格型号(prop 仍绑定 productModelId,但编辑态改为文本展示) |
| | | data.rules.productModelId = [ |
| | | { |
| | | required: type !== "edit", |
| | | message: "请选择规格型号", |
| | | trigger: "change", |
| | | }, |
| | | ]; |
| | | |
| | | // 先加载下拉选项,确保编辑数据可以正确匹配回显 |
| | | const userRes = await userListNoPage(); |
| | | userList.value = userRes.data || []; |
| | | |
| | | await getProductOptions(); |
| | | |
| | | // 处理结果默认“报废”,且不可选择其它项 |
| | | form.value.dealResult = getScrapDealResultValue(); |
| | | if (operationType.value === 'edit') { |
| | | getQualityUnqualifiedInfo(row.id).then(res => { |
| | | const { inspectState, ...rest } = (res.data || {}) |
| | | form.value = { ...rest } |
| | | }) |
| | | const fallback = row || {}; |
| | | const res = await getQualityUnqualifiedInfo(fallback.id); |
| | | const { inspectState, ...rest } = res.data || {}; |
| | | |
| | | // 先用列表行数据把“必回显字段”直接填上,避免详情接口字段名不一致导致全空。 |
| | | const productName = rest?.productName ?? fallback?.productName; |
| | | const modelName = rest?.model ?? fallback?.model; |
| | | |
| | | const checkTypeValue = rest?.checkType ?? fallback?.checkType; |
| | | const checkNameValue = |
| | | rest?.checkName ?? |
| | | rest?.checkUserName ?? |
| | | fallback?.checkName ?? |
| | | fallback?.checkUserName; |
| | | |
| | | const productId = |
| | | rest?.productId ?? |
| | | findProductIdByLabel(productOptions.value, productName); |
| | | |
| | | // 先回填字段(productModelId 需要依赖 modelOptions,稍后再补) |
| | | const normalizedProductId = normalizeProductIdByOptions(productId); |
| | | |
| | | // 编辑态产品名称展示只展示 label,避免树组件回显依赖 value 匹配 |
| | | const productNameLabel = |
| | | rest?.productName ?? |
| | | fallback?.productName ?? |
| | | findNodeById(productOptions.value, normalizedProductId) ?? |
| | | productName; |
| | | form.value = { |
| | | ...rest, |
| | | productName: productNameLabel, |
| | | productId: normalizedProductId, |
| | | productModelId: rest?.productModelId ?? undefined, |
| | | model: rest?.model ?? fallback?.model, |
| | | unit: rest?.unit ?? fallback?.unit, |
| | | batchNo: rest?.batchNo ?? fallback?.batchNo ?? "", |
| | | checkType: |
| | | checkTypeValue === undefined || checkTypeValue === null |
| | | ? undefined |
| | | : Number(checkTypeValue), |
| | | checkName: checkNameValue ?? "", |
| | | checkTime: rest?.checkTime ?? fallback?.checkTime ?? "", |
| | | defectivePhenomena: |
| | | rest?.defectivePhenomena ?? fallback?.defectivePhenomena ?? "", |
| | | dealName: rest?.dealName ?? fallback?.dealName ?? "", |
| | | dealTime: rest?.dealTime ?? fallback?.dealTime ?? "", |
| | | dealResult: getScrapDealResultValue(), |
| | | }; |
| | | |
| | | // 规格型号下拉需要依赖 productId |
| | | await loadModelsForProductId(form.value.productId); |
| | | |
| | | // 规格型号回显(如详情没给 productModelId,就用 model 名称反查) |
| | | if (!form.value.productModelId) { |
| | | form.value.productModelId = findModelIdByModel( |
| | | modelOptions.value, |
| | | modelName |
| | | ); |
| | | } |
| | | |
| | | // 根据 productModelId 回填 model/unit |
| | | if (form.value.productModelId) { |
| | | form.value.productModelId = normalizeModelIdByOptions(form.value.productModelId); |
| | | handleChangeModel(form.value.productModelId); |
| | | } else if (modelName) { |
| | | // productModelId 仍然拿不到时,至少保证 model/unit 文本回显 |
| | | const matched = |
| | | (modelOptions.value || []).find((m) => { |
| | | const model = String(m?.model ?? ""); |
| | | const id = String(m?.id ?? ""); |
| | | const target = String(modelName ?? ""); |
| | | return model === target || id === target; |
| | | }) ?? null; |
| | | if (matched) { |
| | | form.value.model = matched.model ?? form.value.model; |
| | | form.value.unit = matched.unit ?? form.value.unit; |
| | | } else { |
| | | form.value.model = modelName; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | const getProductOptions = () => { |
| | | productTreeList().then((res) => { |
| | | productOptions.value = convertIdToValue(res); |
| | | }); |
| | | const getProductOptions = async () => { |
| | | const res = await productTreeList(); |
| | | productOptions.value = convertIdToValue(res); |
| | | }; |
| | | const getModels = (value) => { |
| | | form.value.productName = findNodeById(productOptions.value, value); |
| | | form.value.productModelId = undefined; |
| | | form.value.model = ""; |
| | | form.value.unit = ""; |
| | | modelOptions.value = []; |
| | | modelList({ id: value }).then((res) => { |
| | | modelOptions.value = res; |
| | | }) |
| | | }; |
| | | |
| | | // 编辑模式/或任意需要时:只拉取规格型号列表,不清空已回填的字段 |
| | | const loadModelsForProductId = async (productId) => { |
| | | if (!productId) return; |
| | | const res = await modelList({ id: productId }); |
| | | modelOptions.value = res || []; |
| | | |
| | | // 让单位/型号等字段保持与当前 productModelId 一致 |
| | | if (form.value.productModelId) { |
| | | form.value.productModelId = normalizeModelIdByOptions(form.value.productModelId); |
| | | handleChangeModel(form.value.productModelId); |
| | | } |
| | | }; |
| | | |
| | | 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 || ''; |
| | | }; |
| | | |
| | | // 解决回显时类型不一致导致 el-tree-select / el-select 只显示 value(id) |
| | | const normalizeProductIdByOptions = (productId) => { |
| | | if (productId === undefined || productId === null) return productId; |
| | | const target = String(productId); |
| | | const stack = Array.isArray(productOptions.value) ? [...productOptions.value] : []; |
| | | while (stack.length) { |
| | | const node = stack.shift(); |
| | | if (node && String(node?.value ?? "") === target) return node?.value; |
| | | if (node?.children?.length) stack.push(...node.children); |
| | | } |
| | | return productId; |
| | | }; |
| | | |
| | | const normalizeModelIdByOptions = (modelId) => { |
| | | if (modelId === undefined || modelId === null) return modelId; |
| | | const target = String(modelId); |
| | | return (modelOptions.value || []).find((m) => String(m?.id ?? "") === target)?.id ?? modelId; |
| | | }; |
| | | const findNodeById = (nodes, productId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | |
| | | } |
| | | return null; // 没有找到节点,返回null |
| | | }; |
| | | |
| | | // 根据树节点 label 回填 value(编辑回显兜底用) |
| | | const findProductIdByLabel = (nodes, label) => { |
| | | const target = String(label ?? ""); |
| | | if (!target) return undefined; |
| | | |
| | | const stack = Array.isArray(nodes) ? [...nodes] : []; |
| | | while (stack.length) { |
| | | const node = stack.shift(); |
| | | if (node && String(node?.label ?? "") === target) return node?.value; |
| | | if (node?.children?.length) stack.push(...node.children); |
| | | } |
| | | return undefined; |
| | | }; |
| | | |
| | | // 根据规格型号名称反查 id(编辑回显兜底用) |
| | | const findModelIdByModel = (models, model) => { |
| | | const target = String(model ?? ""); |
| | | if (!target) return undefined; |
| | | return ( |
| | | (models || []).find((m) => { |
| | | const mModel = String(m?.model ?? ""); |
| | | const mId = String(m?.id ?? ""); |
| | | return mModel === target || mId === target; |
| | | })?.id |
| | | ); |
| | | }; |
| | | function convertIdToValue(data) { |
| | | return data.map((item) => { |
| | | const { id, children, ...rest } = item; |