| | |
| | | prefix-icon="Search" /> |
| | | </div> |
| | | <div class="search-item"> |
| | | <span class="search_title">生产订单号:</span> |
| | | <el-input v-model="searchForm.productOrderNpsNo" |
| | | style="width: 240px" |
| | | placeholder="请输入" |
| | | @change="handleQuery" |
| | | clearable |
| | | prefix-icon="Search" /> |
| | | </div> |
| | | <div class="search-item"> |
| | | <el-button type="primary" |
| | | @click="handleQuery">搜索</el-button> |
| | | </div> |
| | |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <!-- 流转卡弹窗 --> |
| | | <el-dialog v-model="transferCardVisible" |
| | | title="流转卡" |
| | |
| | | @click="printTransferCard">打印流转卡</el-button> |
| | | </div> |
| | | </el-dialog> |
| | | |
| | | <!-- 报工弹窗 --> |
| | | <el-dialog v-model="reportDialogVisible" |
| | | title="报工" |
| | |
| | | :value="user.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <div v-if="params.length > 0" |
| | | class="param-grid" |
| | | v-loading="paramLoading"> |
| | | <el-form-item v-for="param in params" |
| | | :key="param.id" |
| | | :label="param.paramName" |
| | | :label-width="120" |
| | | class="param-item"> |
| | | <template v-if="param.paramType == '1'"> |
| | | <div class="param-input-group"> |
| | | <el-input-number v-model="reportForm.paramGroups[param.id]" |
| | | controls-position="right" |
| | | :key="param.id" |
| | | style="width: 250px" |
| | | class="param-input" /> |
| | | <span v-if="param.unit && param.unit != '/'" |
| | | class="param-unit">{{ param.unit }}</span> |
| | | </div> |
| | | </template> |
| | | <template v-else-if="param.paramType == '2'"> |
| | | <div class="param-input-group"> |
| | | <el-input v-model="reportForm.paramGroups[param.id]" |
| | | :key="param.id" |
| | | style="width: 250px" |
| | | class="param-input" /> |
| | | <span v-if="param.unit && param.unit != '/'" |
| | | class="param-unit">{{ param.unit }}</span> |
| | | </div> |
| | | </template> |
| | | <template v-else-if="param.paramType == '3'"> |
| | | <div class="param-input-group"> |
| | | <el-select v-model="reportForm.paramGroups[param.id]" |
| | | placeholder="请选择" |
| | | :key="param.id" |
| | | class="param-select" |
| | | style="width: 250px"> |
| | | <el-option v-for="option in dictOptions[param.paramFormat] || []" |
| | | :key="option.dictLabel" |
| | | :label="option.dictLabel" |
| | | :value="option.dictLabel" /> |
| | | </el-select> |
| | | <span v-if="param.unit && param.unit != '/'" |
| | | class="param-unit">{{ param.unit }}</span> |
| | | </div> |
| | | </template> |
| | | <template v-else-if="param.paramType == '4'"> |
| | | <div class="param-input-group"> |
| | | <el-date-picker :value-format="param.paramFormat.replace('yyyy', 'YYYY').replace('dd', 'DD')" |
| | | :format="param.paramFormat.replace('yyyy', 'YYYY').replace('dd', 'DD')" |
| | | :key="param.id" |
| | | :type="param.paramFormat=='yyyy-MM-dd'?'date':'datetime'" |
| | | v-model="reportForm.paramGroups[param.id]" |
| | | class="param-input" |
| | | style="width: 250px" /> |
| | | <span v-if="param.unit && param.unit != '/'" |
| | | class="param-unit">{{ param.unit }}</span> |
| | | </div> |
| | | </template> |
| | | <template v-else> |
| | | <div class="param-input-group"> |
| | | <el-input v-model="reportForm.paramGroups[param.id]" |
| | | :key="param.id" |
| | | class="param-input" /> |
| | | <span v-if="param.unit && param.unit != '/'" |
| | | class="param-unit">{{ param.unit }}</span> |
| | | </div> |
| | | </template> |
| | | </el-form-item> |
| | | </div> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <el-dialog v-model="materialDialogVisible" |
| | | title="物料" |
| | | width="1200px" |
| | | @close="handleCloseMaterialDialog"> |
| | | <el-table v-loading="materialTableLoading" |
| | | :data="materialTableData" |
| | | border |
| | | row-key="id"> |
| | | <el-table-column label="工序名称" |
| | | prop="processName" |
| | | min-width="140" /> |
| | | <el-table-column label="原料名称" |
| | | prop="materialName" |
| | | min-width="140" /> |
| | | <el-table-column label="原料型号" |
| | | prop="materialModel" |
| | | min-width="140" /> |
| | | <el-table-column label="计量单位" |
| | | prop="unit" |
| | | min-width="100" /> |
| | | <el-table-column label="领用数量" |
| | | prop="pickQty" |
| | | min-width="100" /> |
| | | <el-table-column label="补料数量" |
| | | prop="supplementQty" |
| | | min-width="100" /> |
| | | <el-table-column label="退料数量" |
| | | prop="returnQty" |
| | | min-width="100" /> |
| | | <el-table-column label="实际数量" |
| | | prop="actualQty" |
| | | min-width="100" /> |
| | | <el-table-column label="操作" |
| | | align="center" |
| | | fixed="right" |
| | | width="220"> |
| | | <template #default="{ row }"> |
| | | <el-button type="primary" |
| | | link |
| | | @click="openSupplementDialog(row)">补料</el-button> |
| | | <el-button type="warning" |
| | | link |
| | | @click="openReturnDialog(row)">退料</el-button> |
| | | <el-button type="info" |
| | | link |
| | | @click="openSupplementRecordDialog(row)">补料记录</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-dialog> |
| | | |
| | | <FormDialog v-model="supplementDialogVisible" |
| | | title="补料" |
| | | width="500px" |
| | | @confirm="handleSubmitSupplement"> |
| | | <el-form ref="supplementFormRef" |
| | | :model="supplementForm" |
| | | :rules="supplementRules" |
| | | label-width="100px"> |
| | | <el-form-item label="补料数量" |
| | | prop="supplementQty"> |
| | | <el-input-number v-model="supplementForm.supplementQty" |
| | | :min="0.001" |
| | | :precision="3" |
| | | :step="1" |
| | | style="width: 100%;" /> |
| | | </el-form-item> |
| | | <el-form-item label="补料原因" |
| | | prop="supplementReason"> |
| | | <el-input v-model="supplementForm.supplementReason" |
| | | type="textarea" |
| | | :rows="3" |
| | | maxlength="200" |
| | | show-word-limit |
| | | placeholder="请输入补料原因" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | :loading="supplementSubmitting" |
| | | @click="handleSubmitSupplement">确定</el-button> |
| | | <el-button @click="supplementDialogVisible = false">取消</el-button> |
| | | </span> |
| | | </template> |
| | | </FormDialog> |
| | | |
| | | <FormDialog v-model="returnDialogVisible" |
| | | title="退料" |
| | | width="500px" |
| | | @confirm="handleSubmitReturn"> |
| | | <el-form ref="returnFormRef" |
| | | :model="returnForm" |
| | | :rules="returnRules" |
| | | label-width="120px"> |
| | | <el-form-item label="退料数量" |
| | | prop="returnQty"> |
| | | <el-input-number v-model="returnForm.returnQty" |
| | | :min="0.001" |
| | | :precision="3" |
| | | :step="1" |
| | | style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | :loading="returnSubmitting" |
| | | @click="handleSubmitReturn">确定</el-button> |
| | | <el-button @click="returnDialogVisible = false">取消</el-button> |
| | | </span> |
| | | </template> |
| | | </FormDialog> |
| | | |
| | | <el-dialog v-model="supplementRecordDialogVisible" |
| | | title="补料记录" |
| | | width="900px"> |
| | | <el-table v-loading="supplementRecordLoading" |
| | | :data="supplementRecordTableData" |
| | | border |
| | | row-key="id"> |
| | | <el-table-column label="补料数量" |
| | | prop="supplementQty" |
| | | min-width="100" /> |
| | | <el-table-column label="补料原因" |
| | | prop="supplementReason" |
| | | min-width="200" /> |
| | | <el-table-column label="补料人" |
| | | prop="supplementUserName" |
| | | min-width="120" /> |
| | | <el-table-column label="补料日期" |
| | | prop="supplementTime" |
| | | min-width="160" /> |
| | | </el-table> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="supplementRecordDialogVisible = false">关闭</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <MaterialDialog v-model="materialDialogVisible" |
| | | :row-data="currentMaterialOrderRow" |
| | | @refresh="getList" /> |
| | | <FilesDia ref="workOrderFilesRef" /> |
| | | </div> |
| | | </template> |
| | |
| | | productWorkOrderPage, |
| | | addProductMain, |
| | | downProductWorkOrder, |
| | | listWorkOrderMaterialLedger, |
| | | addWorkOrderMaterialSupplement, |
| | | addWorkOrderMaterialReturn, |
| | | listWorkOrderMaterialSupplementRecord, |
| | | } from "@/api/productionManagement/workOrder.js"; |
| | | import { findProcessParamListOrder } from "@/api/productionManagement/productProcessRoute.js"; |
| | | import { getUserProfile, userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | import { getDicts } from "@/api/system/dict/data"; |
| | | import QRCode from "qrcode"; |
| | | import { getCurrentInstance, reactive, toRefs } from "vue"; |
| | | import FilesDia from "./components/filesDia.vue"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import MaterialDialog from "./components/MaterialDialog.vue"; |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const tableColumn = ref([ |
| | |
| | | }, |
| | | { |
| | | label: "生产订单号", |
| | | prop: "productOrderNpsNo", |
| | | prop: "npsNo", |
| | | width: "140", |
| | | }, |
| | | { |
| | |
| | | }, |
| | | { |
| | | label: "工序名称", |
| | | prop: "processName", |
| | | prop: "operationName", |
| | | width: "100", |
| | | }, |
| | | { |
| | | label: "需求数量", |
| | |
| | | openWorkOrderFiles(row); |
| | | }, |
| | | }, |
| | | // { |
| | | // name: "物料", |
| | | // clickFun: row => { |
| | | // openMaterialDialog(row); |
| | | // }, |
| | | // }, |
| | | { |
| | | name: "报工", |
| | | clickFun: row => { |
| | |
| | | }, |
| | | disabled: row => row.planQuantity <= 0, |
| | | }, |
| | | { |
| | | name: "物料", |
| | | clickFun: row => { |
| | | openMaterialDialog(row); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | |
| | | |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | const transferCardVisible = ref(false); |
| | |
| | | productProcessRouteItemId: "", |
| | | userId: "", |
| | | productMainId: null, |
| | | productionOrderRoutingOperationId: "", |
| | | productionOrderId: "", |
| | | paramGroups: {}, |
| | | }); |
| | | |
| | | const params = ref({}); |
| | | const dictOptions = ref({}); |
| | | const paramLoading = ref(false); |
| | | |
| | | // 本次生产数量验证规则 |
| | | const validateQuantity = (rule, value, callback) => { |
| | |
| | | const reportFormRules = { |
| | | quantity: [{ required: true, validator: validateQuantity, trigger: "blur" }], |
| | | scrapQty: [{ validator: validateScrapQty, trigger: "blur" }], |
| | | }; |
| | | const supplementRules = { |
| | | supplementQty: [{ required: true, message: "请输入补料数量", trigger: "blur" }], |
| | | supplementReason: [{ required: true, message: "请输入补料原因", trigger: "blur" }], |
| | | }; |
| | | const returnRules = { |
| | | returnQty: [{ required: true, message: "请输入退料数量", trigger: "blur" }], |
| | | }; |
| | | |
| | | // 处理本次生产数量输入,限制必须大于等于1 |
| | |
| | | // 有效的非负整数(包括0) |
| | | reportForm.scrapQty = num; |
| | | }; |
| | | |
| | | |
| | | const currentReportRowData = ref(null); |
| | | const materialDialogVisible = ref(false); |
| | | const materialTableLoading = ref(false); |
| | | const materialTableData = ref([]); |
| | | const currentMaterialRow = ref(null); |
| | | const currentMaterialOrderRow = ref(null); |
| | | const supplementDialogVisible = ref(false); |
| | | const supplementSubmitting = ref(false); |
| | | const supplementFormRef = ref(null); |
| | | const supplementForm = reactive({ |
| | | supplementQty: null, |
| | | supplementReason: "", |
| | | }); |
| | | const returnDialogVisible = ref(false); |
| | | const returnSubmitting = ref(false); |
| | | const returnFormRef = ref(null); |
| | | const returnForm = reactive({ |
| | | returnQty: null, |
| | | }); |
| | | const supplementRecordDialogVisible = ref(false); |
| | | const supplementRecordLoading = ref(false); |
| | | const supplementRecordTableData = ref([]); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | workOrderNo: "", |
| | | productOrderNpsNo: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | |
| | | const pagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | |
| | | |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const params = { ...searchForm.value, ...page }; |
| | |
| | | reportForm.productMainId = row.productMainId; |
| | | reportForm.scrapQty = |
| | | row.scrapQty !== undefined && row.scrapQty !== null ? row.scrapQty : null; |
| | | reportForm.productionOrderRoutingOperationId = |
| | | row.productionOrderRoutingOperationId; |
| | | reportForm.productionOrderId = row.productionOrderId; |
| | | nextTick(() => { |
| | | reportFormRef.value?.clearValidate(); |
| | | if (row.productionOrderRoutingOperationId && row.productionOrderId) { |
| | | loadParams(row.productionOrderRoutingOperationId, row.productionOrderId); |
| | | } |
| | | }); |
| | | // 获取当前登录用户信息,设置为默认选中 |
| | | getUserProfile() |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | |
| | | reportDialogVisible.value = true; |
| | | }; |
| | | |
| | | const openMaterialDialog = async row => { |
| | | const openMaterialDialog = row => { |
| | | currentMaterialOrderRow.value = row; |
| | | materialDialogVisible.value = true; |
| | | materialTableLoading.value = true; |
| | | materialTableData.value = []; |
| | | try { |
| | | const res = await listWorkOrderMaterialLedger({ |
| | | workOrderId: row.id, |
| | | processId: row.processId, |
| | | productProcessRouteItemId: row.productProcessRouteItemId, |
| | | }); |
| | | materialTableData.value = res.data || []; |
| | | } catch (e) { |
| | | console.error("获取物料台账失败", e); |
| | | proxy.$modal.msgError("获取物料台账失败"); |
| | | } finally { |
| | | materialTableLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | const handleCloseMaterialDialog = () => { |
| | | materialTableData.value = []; |
| | | currentMaterialRow.value = null; |
| | | currentMaterialOrderRow.value = null; |
| | | }; |
| | | |
| | | const openSupplementDialog = row => { |
| | | currentMaterialRow.value = row; |
| | | supplementForm.supplementQty = null; |
| | | supplementForm.supplementReason = ""; |
| | | supplementDialogVisible.value = true; |
| | | nextTick(() => { |
| | | supplementFormRef.value?.clearValidate(); |
| | | }); |
| | | }; |
| | | |
| | | const handleSubmitSupplement = () => { |
| | | supplementFormRef.value?.validate(async valid => { |
| | | if (!valid) return; |
| | | if (!currentMaterialRow.value?.id) { |
| | | proxy.$modal.msgWarning("缺少物料明细ID"); |
| | | return; |
| | | } |
| | | supplementSubmitting.value = true; |
| | | try { |
| | | await addWorkOrderMaterialSupplement({ |
| | | materialLedgerId: currentMaterialRow.value.id, |
| | | supplementQty: Number(supplementForm.supplementQty), |
| | | supplementReason: supplementForm.supplementReason, |
| | | workOrderId: currentMaterialOrderRow.value?.id, |
| | | }); |
| | | proxy.$modal.msgSuccess("补料成功"); |
| | | supplementDialogVisible.value = false; |
| | | await openMaterialDialog(currentMaterialOrderRow.value); |
| | | } catch (e) { |
| | | console.error("补料失败", e); |
| | | proxy.$modal.msgError("补料失败"); |
| | | } finally { |
| | | supplementSubmitting.value = false; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const openReturnDialog = row => { |
| | | currentMaterialRow.value = row; |
| | | returnForm.returnQty = null; |
| | | returnDialogVisible.value = true; |
| | | nextTick(() => { |
| | | returnFormRef.value?.clearValidate(); |
| | | }); |
| | | }; |
| | | |
| | | const handleSubmitReturn = () => { |
| | | returnFormRef.value?.validate(async valid => { |
| | | if (!valid) return; |
| | | if (!currentMaterialRow.value?.id) { |
| | | proxy.$modal.msgWarning("缺少物料明细ID"); |
| | | return; |
| | | } |
| | | const returnQty = Number(returnForm.returnQty); |
| | | const minQty = |
| | | Number(currentMaterialRow.value.pickQty || 0) + |
| | | Number(currentMaterialRow.value.supplementQty || 0); |
| | | if (returnQty < minQty) { |
| | | proxy.$modal.msgWarning(`退料数量不能低于领用数量+补料数量(${minQty})`); |
| | | return; |
| | | } |
| | | returnSubmitting.value = true; |
| | | try { |
| | | await addWorkOrderMaterialReturn({ |
| | | materialLedgerId: currentMaterialRow.value.id, |
| | | returnQty, |
| | | workOrderId: currentMaterialOrderRow.value?.id, |
| | | }); |
| | | proxy.$modal.msgSuccess("退料成功"); |
| | | returnDialogVisible.value = false; |
| | | await openMaterialDialog(currentMaterialOrderRow.value); |
| | | } catch (e) { |
| | | console.error("退料失败", e); |
| | | proxy.$modal.msgError("退料失败"); |
| | | } finally { |
| | | returnSubmitting.value = false; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const openSupplementRecordDialog = async row => { |
| | | supplementRecordDialogVisible.value = true; |
| | | supplementRecordLoading.value = true; |
| | | supplementRecordTableData.value = []; |
| | | try { |
| | | const res = await listWorkOrderMaterialSupplementRecord({ |
| | | materialLedgerId: row.id, |
| | | }); |
| | | supplementRecordTableData.value = res.data || []; |
| | | } catch (e) { |
| | | console.error("获取补料记录失败", e); |
| | | proxy.$modal.msgError("获取补料记录失败"); |
| | | } finally { |
| | | supplementRecordLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | const handleReport = () => { |
| | |
| | | return; |
| | | } |
| | | |
| | | const params = { |
| | | const productionOperationParamList = params.value.map(param => ({ |
| | | ...param, |
| | | inputValue: reportForm.paramGroups[param.id] ?? "", |
| | | })); |
| | | |
| | | const submitParams = { |
| | | quantity: quantity, |
| | | scrapQty: isNaN(scrapQty) ? 0 : scrapQty, |
| | | userId: reportForm.userId, |
| | | userName: reportForm.userName, |
| | | workOrderId: reportForm.workOrderId, |
| | | productionOperationTaskId: reportForm.workOrderId, |
| | | productProcessRouteItemId: reportForm.productProcessRouteItemId, |
| | | reportWork: reportForm.reportWork, |
| | | productMainId: reportForm.productMainId, |
| | | productionOrderRoutingOperationId: |
| | | reportForm.productionOrderRoutingOperationId, |
| | | productionOrderId: reportForm.productionOrderId, |
| | | productionOperationParamList: productionOperationParamList, |
| | | }; |
| | | |
| | | addProductMain(params) |
| | | addProductMain(submitParams) |
| | | .then(res => { |
| | | proxy.$modal.msgSuccess("报工成功"); |
| | | reportDialogVisible.value = false; |
| | |
| | | const handleUserChange = val => { |
| | | const user = userOptions.value.find(item => item.userId === val); |
| | | reportForm.userName = user ? user.nickName : ""; |
| | | }; |
| | | |
| | | const getDictOptions = async dictType => { |
| | | if (!dictType) return []; |
| | | if (dictOptions.value[dictType]) return dictOptions.value[dictType]; |
| | | try { |
| | | const res = await getDicts(dictType); |
| | | if (res.code === 200) { |
| | | dictOptions.value[dictType] = res.data; |
| | | return res.data; |
| | | } |
| | | return []; |
| | | } catch (error) { |
| | | console.error("获取字典数据失败:", error); |
| | | return []; |
| | | } |
| | | }; |
| | | |
| | | const loadParams = (productionOrderRoutingOperationId, productionOrderId) => { |
| | | paramLoading.value = true; |
| | | findProcessParamListOrder({ |
| | | productionOrderRoutingOperationId, |
| | | productionOrderId, |
| | | }) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | const paramList = res.data || []; |
| | | params.value = paramList; |
| | | reportForm.paramGroups = {}; |
| | | paramList.forEach(param => { |
| | | if (!reportForm.paramGroups[param.id]) { |
| | | reportForm.paramGroups[param.id] = ""; |
| | | } |
| | | if (param.paramType == "3" && param.paramFormat) { |
| | | getDictOptions(param.paramFormat); |
| | | } |
| | | }); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | console.error("获取工序参数失败:", err); |
| | | }) |
| | | .finally(() => { |
| | | paramLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | |
| | | .print-button-container { |
| | | text-align: center; |
| | | margin-top: 20px; |
| | | } |
| | | .param-grid { |
| | | margin-top: 10px; |
| | | border-top: 1px solid #ebe9f3; |
| | | padding-top: 10px; |
| | | } |
| | | .param-item { |
| | | margin-bottom: 12px; |
| | | } |
| | | .param-input-group { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | .param-input { |
| | | flex: 1; |
| | | } |
| | | .param-select { |
| | | flex: 1; |
| | | } |
| | | .param-unit { |
| | | color: #909399; |
| | | font-size: 12px; |
| | | min-width: 30px; |
| | | } |
| | | </style> |
| | | |
| | |
| | | height: 140px !important; |
| | | } |
| | | } |
| | | </style> |
| | | </style> |