feat(生产报工): 报工弹窗按四类数量录入并展示分类去向
- 工单管理报工弹窗:合格/轻微返工/严重返工/报废四个数量录入 + 计数合计实时展示与校验
- 报工台账、生产报工列表增加轻微/严重返工数量列
- 过程检验、成品检验增加报工单号筛选与「来源报工单号」列
- 不合格品管理增加「来源报工单号」筛选与列
- 生产报工列表增加产品大类筛选
Co-Authored-By: Claude <noreply@anthropic.com>
| | |
| | | <el-option label="报废" :value="3" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="产品大类:"> |
| | | <el-select v-model="searchForm.majorCategory" |
| | | placeholder="请选择" |
| | | clearable |
| | | style="width: 200px;" |
| | | @change="handleQuery"> |
| | | <el-option label="小提琴" value="小提琴" /> |
| | | <el-option label="大提琴" value="大提琴" /> |
| | | <el-option label="贝斯" value="贝斯" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="handleQuery">搜索</el-button> |
| | |
| | | workOrderNo: "", |
| | | workOrderStatus: "", |
| | | reportType: "", |
| | | majorCategory: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "产品大类", |
| | | prop: "majorCategory", |
| | | width: 110, |
| | | }, |
| | | { |
| | | label: "产品名称", |
| | | prop: "productName", |
| | | width: 120, |
| | |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "产出数量", |
| | | label: "合格数量", |
| | | prop: "quantity", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "轻微返工数量", |
| | | prop: "reworkMinorQty", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "严重返工数量", |
| | | prop: "reworkMajorQty", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "报废数量", |
| | | prop: "scrapQty", |
| | | width: 120, |
| | |
| | | </el-select> |
| | | </div> |
| | | <div class="search-item"> |
| | | <span class="search_title">产品大类:</span> |
| | | <el-select v-model="searchForm.majorCategory" |
| | | style="width: 180px" |
| | | clearable |
| | | placeholder="请选择" |
| | | @change="handleQuery"> |
| | | <el-option label="小提琴" value="小提琴" /> |
| | | <el-option label="大提琴" value="大提琴" /> |
| | | <el-option label="贝斯" value="贝斯" /> |
| | | </el-select> |
| | | </div> |
| | | <div class="search-item"> |
| | | <el-button type="primary" @click="handleQuery">搜索</el-button> |
| | | </div> |
| | | </div> |
| | |
| | | workOrderNo: "", |
| | | nickName: "", |
| | | reportType: "", |
| | | majorCategory: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | |
| | | const tableColumn = [ |
| | | { label: "报工单号", prop: "productNo", width: 140 }, |
| | | { label: "报工人员", prop: "nickName", width: 120 }, |
| | | { label: "产品大类", prop: "majorCategory", width: 110 }, |
| | | { label: "工序", prop: "process", width: 140 }, |
| | | { label: "工单编号", prop: "workOrderNo", width: 140 }, |
| | | { label: "产出数量", prop: "quantity", width: 120 }, |
| | | { label: "报废数量", prop: "scrapQty", width: 120 }, |
| | | { label: "合格数量", prop: "quantity", width: 110 }, |
| | | { label: "轻微返工数量", prop: "reworkMinorQty", width: 120 }, |
| | | { label: "严重返工数量", prop: "reworkMajorQty", width: 120 }, |
| | | { label: "报废数量", prop: "scrapQty", width: 110 }, |
| | | { label: "工时(h)", prop: "workHour", width: 100 }, |
| | | { label: "报工结果", prop: "reportTypeName", width: 100 }, |
| | | { label: "审核状态", prop: "auditStatusName", width: 100 }, |
| | |
| | | readonly |
| | | style="width: 300px" /> |
| | | </el-form-item> |
| | | <el-form-item label="生产合格数量" |
| | | <el-form-item label="合格数量" |
| | | prop="quantity"> |
| | | <el-input v-model.number="reportForm.quantity" |
| | | type="number" |
| | | min="0" |
| | | step="0.001" |
| | | style="width: 300px" |
| | | placeholder="请输入生产合格数量" |
| | | placeholder="请输入合格零件数量" |
| | | @input="handleQuantityInput" /> |
| | | </el-form-item> |
| | | <el-form-item label="轻微返工数量" |
| | | prop="reworkMinorQty"> |
| | | <el-input v-model.number="reportForm.reworkMinorQty" |
| | | type="number" |
| | | min="0" |
| | | step="1" |
| | | style="width: 300px" |
| | | placeholder="请输入轻微返工零件数量" |
| | | @input="handleNonNegativeIntInput('reworkMinorQty', $event)" /> |
| | | </el-form-item> |
| | | <el-form-item label="严重返工数量" |
| | | prop="reworkMajorQty"> |
| | | <el-input v-model.number="reportForm.reworkMajorQty" |
| | | type="number" |
| | | min="0" |
| | | step="1" |
| | | style="width: 300px" |
| | | placeholder="请输入严重返工零件数量" |
| | | @input="handleNonNegativeIntInput('reworkMajorQty', $event)" /> |
| | | </el-form-item> |
| | | <el-form-item label="报废数量" |
| | | prop="scrapQty"> |
| | |
| | | min="0" |
| | | step="1" |
| | | style="width: 300px" |
| | | placeholder="请输入报废数量" |
| | | placeholder="请输入报废零件数量" |
| | | @input="handleScrapQtyInput" /> |
| | | </el-form-item> |
| | | <el-form-item label="报工类型" |
| | | prop="reportType"> |
| | | <el-select v-model="reportForm.reportType" |
| | | style="width: 300px" |
| | | placeholder="请选择报工类型"> |
| | | <el-option label="合格" :value="0" /> |
| | | <el-option label="轻微返工" :value="1" /> |
| | | <el-option label="严重返工" :value="2" /> |
| | | <el-option label="报废" :value="3" /> |
| | | </el-select> |
| | | <el-form-item label="计数合计"> |
| | | <span class="counted-total">{{ countedTotal }}</span> |
| | | <span class="counted-total-tip">(合格 + 轻微返工 + 严重返工 + 报废,完成后分类进入质检入库)</span> |
| | | </el-form-item> |
| | | <el-form-item label="班组信息"> |
| | | <el-select v-model="reportForm.userId" |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { onMounted, ref, nextTick } from "vue"; |
| | | import { onMounted, ref, nextTick, computed } from "vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import dayjs from "dayjs"; |
| | | import { |
| | |
| | | const reportForm = reactive({ |
| | | planQuantity: 0, |
| | | quantity: null, |
| | | reworkMinorQty: null, |
| | | reworkMajorQty: null, |
| | | scrapQty: null, |
| | | reportType: 0, |
| | | userName: "", |
| | | workOrderId: "", |
| | | reportWork: "", |
| | |
| | | // 生产合格数量验证规则 |
| | | const validateQuantity = (rule, value, callback) => { |
| | | if (value === null || value === undefined || value === "") { |
| | | callback(new Error("请输入生产合格数量")); |
| | | callback(); |
| | | return; |
| | | } |
| | | const num = Number(value); |
| | | // 整数且大于等于1 |
| | | if (isNaN(num) || num < 0) { |
| | | callback(new Error("生产合格数量必须大于等于0")); |
| | | callback(new Error("合格数量必须大于等于0")); |
| | | return; |
| | | } |
| | | callback(); |
| | | }; |
| | | |
| | | // 报废数量验证规则 |
| | | const validateScrapQty = (rule, value, callback) => { |
| | | // 分类数量(返工/报废)验证规则:选填,非负 |
| | | const validateOptionalCount = label => (rule, value, callback) => { |
| | | if (value === null || value === undefined || value === "") { |
| | | callback(); |
| | | return; |
| | | } |
| | | const num = Number(value); |
| | | // 整数且大于等于0 |
| | | if (isNaN(num) || num < 0) { |
| | | callback(new Error("报废数量必须大于等于0")); |
| | | callback(new Error(`${label}必须大于等于0`)); |
| | | return; |
| | | } |
| | | callback(); |
| | |
| | | |
| | | // 验证规则 |
| | | const reportFormRules = { |
| | | quantity: [{ required: true, validator: validateQuantity, trigger: "blur" }], |
| | | scrapQty: [{ validator: validateScrapQty, trigger: "blur" }], |
| | | quantity: [{ validator: validateQuantity, trigger: "blur" }], |
| | | reworkMinorQty: [{ validator: validateOptionalCount("轻微返工数量"), trigger: "blur" }], |
| | | reworkMajorQty: [{ validator: validateOptionalCount("严重返工数量"), trigger: "blur" }], |
| | | scrapQty: [{ validator: validateOptionalCount("报废数量"), trigger: "blur" }], |
| | | }; |
| | | |
| | | // 报工计数合计 = 合格 + 轻微返工 + 严重返工 + 报废 |
| | | const countedTotal = computed(() => { |
| | | const total = [ |
| | | reportForm.quantity, |
| | | reportForm.reworkMinorQty, |
| | | reportForm.reworkMajorQty, |
| | | reportForm.scrapQty, |
| | | ].reduce((sum, value) => { |
| | | const num = Number(value); |
| | | return sum + (isNaN(num) || num < 0 ? 0 : num); |
| | | }, 0); |
| | | return Math.round(total * 1000) / 1000; |
| | | }); |
| | | |
| | | // 处理生产合格数量输入,限制必须大于等于0 |
| | | const handleQuantityInput = value => { |
| | |
| | | return; |
| | | } |
| | | reportForm.quantity = num; |
| | | }; |
| | | |
| | | // 返工分类数量输入:非负整数 |
| | | const handleNonNegativeIntInput = (field, value) => { |
| | | if (value === "" || value === null || value === undefined) { |
| | | reportForm[field] = null; |
| | | return; |
| | | } |
| | | const num = Number(value); |
| | | if (isNaN(num) || num < 0) { |
| | | reportForm[field] = null; |
| | | return; |
| | | } |
| | | reportForm[field] = Number.isInteger(num) ? num : Math.floor(num); |
| | | }; |
| | | |
| | | // 处理报废数量 |
| | |
| | | reportForm.planQuantity = remainingQuantity; |
| | | reportForm.quantity = |
| | | row.quantity !== undefined && row.quantity !== null ? row.quantity : null; |
| | | reportForm.reportType = row.reportType ?? 0; |
| | | reportForm.reworkMinorQty = row.reworkMinorQty ?? null; |
| | | reportForm.reworkMajorQty = row.reworkMajorQty ?? null; |
| | | reportForm.productProcessRouteItemId = row.productProcessRouteItemId; |
| | | reportForm.workOrderId = row.id; |
| | | reportForm.reportWork = row.reportWork; |
| | |
| | | return; |
| | | } |
| | | |
| | | // 验证生产合格数量 |
| | | if ( |
| | | reportForm.quantity === null || |
| | | reportForm.quantity === undefined || |
| | | reportForm.quantity === "" |
| | | ) { |
| | | ElMessageBox.alert("请输入生产合格数量", "提示", { |
| | | confirmButtonText: "确定", |
| | | }); |
| | | // 分类数量取值:空或非法按0处理 |
| | | const toCount = value => { |
| | | const num = Number(value); |
| | | return isNaN(num) || num < 0 ? 0 : num; |
| | | }; |
| | | const quantity = toCount(reportForm.quantity); |
| | | const reworkMinorQty = toCount(reportForm.reworkMinorQty); |
| | | const reworkMajorQty = toCount(reportForm.reworkMajorQty); |
| | | const scrapQty = toCount(reportForm.scrapQty); |
| | | |
| | | // 对计数的零件做区分:四类数量合计必须大于0 |
| | | if (quantity + reworkMinorQty + reworkMajorQty + scrapQty <= 0) { |
| | | ElMessageBox.alert( |
| | | "请按合格/轻微返工/严重返工/报废分类填写零件数量,且合计需大于0", |
| | | "提示", |
| | | { confirmButtonText: "确定" } |
| | | ); |
| | | return; |
| | | } |
| | | |
| | | const quantity = Number(reportForm.quantity); |
| | | |
| | | if (isNaN(quantity) || quantity < 0) { |
| | | ElMessageBox.alert("生产合格数量必须大于等于0", "提示", { |
| | | confirmButtonText: "确定", |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // if (quantity > reportForm.planQuantity) { |
| | | // ElMessageBox.alert("本次生产数量不能超过待生产数量", "提示", { |
| | | // confirmButtonText: "确定", |
| | | // }); |
| | | // return; |
| | | // } |
| | | |
| | | // 验证报废数量 |
| | | const scrapQty = Number(reportForm.scrapQty); |
| | | if (!isNaN(scrapQty) && scrapQty < 0) { |
| | | ElMessageBox.alert("报废数量不能小于0", "提示", { |
| | | confirmButtonText: "确定", |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // if (!isNaN(scrapQty) && scrapQty > quantity) { |
| | | // ElMessageBox.alert("报废数量不能大于本次生产数量", "提示", { |
| | | // confirmButtonText: "确定", |
| | | // }); |
| | | // return; |
| | | // } |
| | | |
| | | const productionOperationParamList = params.value.map(param => ({ |
| | | ...param, |
| | |
| | | |
| | | const submitParams = { |
| | | quantity: quantity, |
| | | scrapQty: isNaN(scrapQty) ? 0 : scrapQty, |
| | | reportType: reportForm.reportType, |
| | | reworkMinorQty: reworkMinorQty, |
| | | reworkMajorQty: reworkMajorQty, |
| | | scrapQty: scrapQty, |
| | | userId: reportForm.userId, |
| | | userName: reportForm.userName, |
| | | productionOperationTaskId: reportForm.workOrderId, |
| | |
| | | font-size: 12px; |
| | | min-width: 30px; |
| | | } |
| | | .counted-total { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #409eff; |
| | | } |
| | | .counted-total-tip { |
| | | margin-left: 8px; |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | </style> |
| | | |
| | | <style lang="scss"> |
| | |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="报工单号" |
| | | prop="reportNo"> |
| | | <el-input v-model="searchForm.reportNo" |
| | | style="width: 240px" |
| | | placeholder="请输入报工单号搜索" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <!-- 按钮 --> |
| | | <el-col :span="4"> |
| | | <el-form-item> |
| | |
| | | productName: "", |
| | | salesContractNo: "", |
| | | workOrderNo: "", |
| | | reportNo: "", |
| | | entryDate: undefined, // 录入日期 |
| | | entryDateStart: undefined, |
| | | entryDateEnd: undefined, |
| | |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "来源报工单号", |
| | | prop: "reportNo", |
| | | width: 140, |
| | | }, |
| | | { |
| | | label: "检验员", |
| | | prop: "checkName", |
| | | }, |
| | |
| | | :prefix-icon="Search" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="来源报工单号:"> |
| | | <el-input |
| | | v-model="searchForm.reportNo" |
| | | style="width: 200px" |
| | | placeholder="请输入报工单号搜索" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="检测日期:"> |
| | | <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" |
| | | style="width: 300px" |
| | |
| | | inspectType: "", |
| | | inspectState: "", |
| | | productName: "", |
| | | reportNo: "", |
| | | entryDate: undefined, // 录入日期 |
| | | entryDateStart: undefined, |
| | | entryDateEnd: undefined, |
| | |
| | | }, |
| | | }, |
| | | { |
| | | label: "来源报工单号", |
| | | prop: "reportNo", |
| | | width: 140, |
| | | }, |
| | | { |
| | | label: "检验员", |
| | | prop: "checkName", |
| | | }, |
| | |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" /> |
| | | <span style="margin-left: 10px" |
| | | class="search_title">报工单号:</span> |
| | | <el-input v-model="searchForm.reportNo" |
| | | style="width: 240px" |
| | | placeholder="请输入报工单号搜索" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" /> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | | style="margin-left: 10px">搜索</el-button> |
| | |
| | | process: "", |
| | | entryDate: undefined, // 录入日期 |
| | | workOrderNo: "", |
| | | reportNo: "", |
| | | entryDateStart: undefined, |
| | | entryDateEnd: undefined, |
| | | }, |
| | |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "来源报工单号", |
| | | prop: "reportNo", |
| | | width: 140, |
| | | }, |
| | | { |
| | | label: "工序", |
| | | prop: "process", |
| | | width: 230, |