|
|
<template> <div> <div class="pda-container" v-loading.fullscreen.lock="fullscreenLoading" element-loading-background="rgba(255, 255, 255, 0.3)" element-loading-spinner="el-icon-loading" :element-loading-text="loadingText"> <div class="status-bar"> <div class="goBack" @click="handleBack"><i class="el-icon-arrow-left"></i>上一页</div> <div class="goBack">生产入库</div> <div class="network" style="color: #fff" @click="$router.push({ path: '/' })">🏠首页</div> </div> <div style="overflow-y: auto"> <!-- Step 1: 扫描工单号 --> <div v-if="processFlag === 1"> <div class="scan-box" style="margin: 2px;"> <el-input clearable v-model="scanCode" placeholder="扫描或输入工单号" inputmode="none" autocomplete="off" autocorrect="off" spellcheck="false" @keyup.enter.native="searchShopOrder" ref="scanCodeRef" /> </div> <div class="item-list" v-if="shopOrderList.length > 0" style="margin: 2px;"> <el-form label-position="top" style="margin: 3px;"> <el-row :gutter="5" v-for="(orderDetail, index) in shopOrderList" :key="index" :class="index < shopOrderList.length - 1 ? 'bottom-line-row' : ''"> <el-col :span="8"> <el-form-item label="料号"><span>{{ orderDetail.partNo }}</span></el-form-item> </el-col> <el-col :span="8"> <el-form-item label="行号/下达号"><span>{{ orderDetail.releaseNo }}/{{ orderDetail.sequenceNo }}</span></el-form-item> </el-col> <el-col :span="8"> <el-form-item label=""> <el-button type="text" class="recvButton" @click="selectShopOrder(orderDetail)" style="margin-top: 10px;margin-left: 20px" size="small">入库</el-button> </el-form-item> </el-col> <el-col :span="24"> <el-form-item label="物料描述"><span>{{ orderDetail.partDesc }}</span></el-form-item> </el-col> <el-col :span="6" :class="{ mt10: getTextWidth(orderDetail.partDesc) > 34 }"> <el-form-item label="订单数量"><span>{{ orderDetail.lotSize }}</span></el-form-item> </el-col> <el-col :span="6" :class="{ mt10: getTextWidth(orderDetail.partDesc) > 34 }"> <el-form-item label="已完成数"><span>{{ orderDetail.qtyComplete }}</span></el-form-item> </el-col> <el-col :span="6" :class="{ mt10: getTextWidth(orderDetail.partDesc) > 34 }"> <el-form-item label="状态"> <el-tag :type="orderDetail.objstate === 'Started' ? 'success' : 'warning'" size="mini"> {{ orderDetail.objstate }} </el-tag> </el-form-item> </el-col> <el-col :span="6" :class="{ mt10: getTextWidth(orderDetail.partDesc) > 34 }"> <el-form-item style="margin-left: 20px" label="单位"><span>{{ orderDetail.uom }}</span></el-form-item> </el-col> </el-row> </el-form> </div> </div>
<!-- Step 2: 入库明细 --> <div v-if="processFlag === 2"> <el-form label-position="top" class="form-section" style="margin: 5px;"> <el-row :gutter="20"> <el-col :span="12"><el-form-item label="工单号"><el-input v-model="inboundItem.orderNo" disabled /></el-form-item></el-col> <el-col :span="12"><el-form-item label="下达号/序列号"><el-input v-model="displayReleaseSeq" disabled /></el-form-item></el-col> <el-col :span="12"><el-form-item label="料号"><el-input v-model="inboundItem.partNo" disabled /></el-form-item></el-col> <el-col :span="12"><el-form-item label="计量单位"><el-input v-model="inboundItem.uom" disabled /></el-form-item></el-col> <el-col :span="24"><el-form-item label="物料描述"><el-input v-model="inboundItem.partDesc" disabled /></el-form-item></el-col> <el-col :span="12"><el-form-item label="订单数量"><el-input v-model="inboundItem.lotSize" disabled /></el-form-item></el-col> <el-col :span="12"><el-form-item label="已完成数"><el-input v-model="inboundItem.qtyComplete" disabled /></el-form-item></el-col> <el-col :span="12"> <el-form-item label="此次入库数量"> <el-input v-model="inboundItem.transQty" type="number" @blur="checkQuantity" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label=" "> <el-button type="text" @click.stop="handlingUnitStep" style="font-size: 16px;margin-top: -5px" size="small">包装记录</el-button> </el-form-item> </el-col> <el-col :span="12"><el-form-item label="库位"> <el-input v-model="inboundItem.locationNo" placeholder="请输入库位" /> </el-form-item></el-col> <el-col :span="12"><el-form-item label="批号"> <el-input v-model="inboundItem.batchNo" placeholder="请输入批号" /> </el-form-item></el-col> <el-col :span="12"><el-form-item label="WDR"> <el-input v-model="inboundItem.wdr" placeholder="请输入WDR" /> </el-form-item></el-col> <!-- 勾选框 --> <el-col :span="12" style="margin-top: 24px"> <el-form-item> <el-checkbox v-model="inboundItem.ifsAutoReport">Auto Report of Operation</el-checkbox> </el-form-item> </el-col> <el-col :span="24" style="margin-top: 5px"> <el-form-item> <el-checkbox v-model="inboundItem.ifsSimplifiedMaterial">Simplified Material Check</el-checkbox> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="8" style="margin-top: 10px"><el-form-item><el-button type="text" style="font-size: 16px;margin-left: 30px" @click="processFlag = 1">回退</el-button></el-form-item></el-col> <el-col :span="8" style="margin-top: 10px"><el-form-item><el-button type="text" style="font-size: 16px;margin-left: 20px" @click="confirmInbound">确认</el-button></el-form-item></el-col> <el-col :span="8" style="margin-top: 10px"><el-form-item><el-button type="text" style="font-size: 16px;margin-left: 10px" @click="$router.push('/')">退出</el-button></el-form-item></el-col> </el-row> </el-form> </div>
<!-- Step 3: 包装记录 --> <div v-if="processFlag === 3"> <el-form label-position="top" class="form-section" style="margin: 5px;"> <el-row :gutter="20"> <el-col :span="12"><el-form-item label="料号"><el-input v-model="inboundItem.partNo" disabled /></el-form-item></el-col> <el-col :span="12"><el-form-item label="未完成数量"><el-input v-model="inboundItem.unCompleteQty" disabled /></el-form-item></el-col> <el-col :span="24"><el-form-item label="物料描述"><el-input v-model="inboundItem.partDesc" disabled /></el-form-item></el-col>
<!-- 自动计算开关 --> <el-col :span="24" style="margin-bottom: 10px;"> <el-form-item> <el-switch v-model="autoCalculate" active-text="自动计算模式" inactive-text="手动输入模式" active-color="#17B3A3" style="display: block;" > </el-switch> <span v-if="autoCalculate" style="font-size: 13px; color: #17B3A3; margin-top: 5px; display: block; font-weight: bold;"> 💡 输入单包装数量后,按回车键或点击确定按钮 </span> <span v-else style="font-size: 12px; color: #999; margin-top: 5px; display: block;"> 手动输入单包装数量和包装数,然后点击"创建"按钮 </span> </el-form-item> </el-col>
<el-col :span="8" style="margin-top: 5px"><el-form-item label="单包装数量"> <el-input v-model="hanlingItem.perQty" type="number" :placeholder="autoCalculate ? '输入完按回车或失去焦点' : '请输入单包装数量'" @blur="autoCalculate && autoCreateHandlingUnits()" @keyup.enter.native="autoCalculate && autoCreateHandlingUnits()" /> </el-form-item></el-col> <el-col :span="8" style="margin-top: 5px" v-if="!autoCalculate"><el-form-item label="包装数"> <el-input v-model="hanlingItem.packageQty" type="number" placeholder="请输入包装数" /> </el-form-item></el-col> <el-col :span="8" v-if="!autoCalculate" style="margin-top: 16px"><el-form-item> <el-button type="text" @click="createHandlingUnit" style="font-size: 16px">创建</el-button></el-form-item></el-col> <el-col :span="8" v-else style="margin-top: 16px"><el-form-item> <el-button type="text" style="font-size: 16px">确定</el-button></el-form-item></el-col> <el-table :data="handlingUnit" :row-style="{ height: '30px' }" style="width: 94%; margin-left: 10px;" highlight-current-row> <el-table-column prop="code" label="序号" /> <el-table-column prop="packageQty" label="包装数" width="80" /> <el-table-column prop="perQty" label="单包装数量" width="100" /> <el-table-column label="操作" v-if="!autoCalculate"> <template slot-scope="scope"> <a @click="removeItem(scope.$index)">删除</a> </template> </el-table-column> </el-table> <el-col :span="24" v-if="autoCalculate && handlingUnit.length > 0" style="margin-top: 10px;"> <span style="font-size: 12px; color: #999;"> 💡 如需修改,请重新输入单包装数量 </span> </el-col> <el-col :span="24"><el-form-item><span>合计</span><span style="margin-left: 38%">{{ totalQty }}</span></el-form-item></el-col> <el-col :span="12"><el-form-item><el-button type="text" @click="processFlag = 2" style="font-size: 18px;margin-left: 60px">回退</el-button></el-form-item></el-col> <el-col :span="12"><el-form-item><el-button type="text" @click="processFlag = 2" style="font-size: 18px;">确定</el-button></el-form-item></el-col> </el-row> </el-form> </div> </div> </div> </div></template>
<script>import { getShopOrderFromIfs, getNextSequenceNo, submitShopOrderInbound, validateMaterialIssued, printLabel } from "@/api/production/production-inbound.js";
export default { data() { return { processFlag: 1, scanCode: '', shopOrderList: [], inboundItem: {}, handlingUnit: [], hanlingItem: { code: '', qty: '', perQty: '', packageQty: '' }, site: localStorage.getItem('site'), warehouseId: localStorage.getItem('selectedWarehouse'), fullscreenLoading: false, loadingText: '加载中...', autoCalculate: false }; }, computed: { totalQty() { const sum = this.handlingUnit.reduce((sum, item) => sum + Number(item.qty), 0); this.inboundItem.transQty = sum; return sum; }, huKey() { return `hu_shop_${this.inboundItem.orderNo}_${this.inboundItem.releaseNo}`; }, displayReleaseSeq() { return `${this.inboundItem.releaseNo || ''}/${this.inboundItem.sequenceNo || ''}`; } }, watch: { autoCalculate(newVal, oldVal) { this.hanlingItem.perQty = ''; this.hanlingItem.packageQty = ''; this.handlingUnit = []; localStorage.setItem(this.huKey, JSON.stringify(this.handlingUnit));
if (newVal) { this.$message.success('已切换到自动创建HU模式'); } else { this.$message.success('已切换到手动输入模式'); } } }, methods: { // 计算"显示宽度"
getTextWidth(text) { if (!text) return 0 let len = 0 for (let char of text) { if (/[\u4e00-\u9fa5\u3000-\u303F\uFF00-\uFFEF]/.test(char)) { len += 2 } else { len += 1 } } return len }, handleBack() { if (this.processFlag === 1) this.$router.back(); else if (this.processFlag === 3) this.processFlag = 2; else this.processFlag = 1; }, // 搜索工单
searchShopOrder() { if (!this.scanCode) return this.shopOrderList = [];
this.loadingText = '搜索中...'; this.fullscreenLoading = true;
// 解析工单号:支持 "3038851" 或 "3038851-1-2" 格式
let orderNo = this.scanCode.trim(); let releaseNo = "*"; let sequenceNo = "*";
// 如果包含"-",则按照 orderNo-releaseNo-sequenceNo 格式解析
const parts = orderNo.split('-'); if (parts.length >= 2) { orderNo = parts[0]; releaseNo = parts[1]; if (parts.length >= 3) { sequenceNo = parts[2]; } }
const requestData = { ifsDBName: "IFST", domainUserID: "CCL_WMS", ifsSiteID: this.site, ifsOrderNo: orderNo, ifsReleaseNo: releaseNo, ifsSequenceNo: sequenceNo };
console.log('工单查询参数:', requestData);
getShopOrderFromIfs(requestData).then(({ data }) => { if (data.code === 0) { this.shopOrderList = data.data || [] if (this.shopOrderList.length === 0) { this.$message.warning('未找到匹配的工单'); } } else { this.$message.error(data.msg || '查询失败'); this.shopOrderList = [] } this.$nextTick(() => { if (this.$refs.scanCodeRef) { this.$refs.scanCodeRef.blur(); } }); }).catch(error => { console.error('搜索失败:', error); this.$message.error('搜索失败,请重试'); this.shopOrderList = [] }).finally(() => { this.fullscreenLoading = false; }); }, // 选择工单
async selectShopOrder(row) { // 校验状态
if (row.objstate !== 'Started') { return this.$message.warning(`工单状态为 ${row.objstate},只有Started状态才能入库`); }
this.fullscreenLoading = true; this.loadingText = '检查物料发料状态...';
try { // 1. 检查物料是否已发料
const validateResult = await validateMaterialIssued({ site: this.site, orderNo: row.orderNo, releaseNo: row.releaseNo || '*', sequenceNo: row.sequenceNo || '*' });
if (validateResult.data.code !== 0) { this.$alert(validateResult.data.msg || '物料发料检查失败', '操作提示', { confirmButtonText: '确定', type: 'warning' }); return; }
this.loadingText = '加载中...';
// 2. 获取下一个序列号
let nextSequence = 1; try { const { data } = await getNextSequenceNo({ orderNo: row.orderNo, releaseNo: row.releaseNo || '*', sequenceNo: row.sequenceNo || '*' }); if (data.code === 0) { nextSequence = data.data; } } catch (error) { console.error('获取序列号失败:', error); }
this.inboundItem = { ...row, transQty: '', unCompleteQty: parseFloat(row.lotSize) - parseFloat(row.qtyComplete), sequenceNoNew: nextSequence, batchNo: `${row.orderNo}-${row.releaseNo}-${row.sequenceNo}-${nextSequence}`, locationNo: '', serialNo: '*', wdr: '*', engChgLevel: '1', ifsAutoReport: false, // Auto Report of Operation
ifsSimplifiedMaterial: true // Simplified Material Check默认选中
}; this.processFlag = 2; } catch (error) { console.error('操作失败:', error); this.$message.error(error.message || '操作失败'); } finally { this.fullscreenLoading = false; } }, // 检查数量
checkQuantity() { const transQty = parseFloat(this.inboundItem.transQty) || 0; const qtyComplete = parseFloat(this.inboundItem.qtyComplete) || 0; const lotSize = parseFloat(this.inboundItem.lotSize) || 0;
if (qtyComplete + transQty > lotSize) { this.$confirm( `当前已完成数量(${qtyComplete}) + 本次入库数量(${transQty}) = ${qtyComplete + transQty},超过订单数量(${lotSize}),是否继续?`, '数量超出提示', { confirmButtonText: '继续', cancelButtonText: '取消', type: 'warning' } ).catch(() => { this.inboundItem.transQty = ''; }); } }, handlingUnitStep() { this.processFlag = 3; this.autoCalculate = false; // 默认使用自动计算模式
const saved = localStorage.getItem(this.huKey); this.handlingUnit = saved ? JSON.parse(saved) : []; }, /** * 自动创建HU列表(自动模式) */ autoCreateHandlingUnits() { const perQty = this.hanlingItem.perQty;
if (!perQty || isNaN(perQty) || parseFloat(perQty) <= 0) { this.$message.warning('请输入有效的单包装数量'); return; }
const qtyToReceive = parseFloat(this.inboundItem.transQty) || 0; if (qtyToReceive <= 0) { this.$message.warning('入库数量为0,无法创建HU'); this.hanlingItem.perQty = ''; return; }
const perQtyValue = parseFloat(perQty); const packageQty = Math.floor(qtyToReceive / perQtyValue); const remainder = qtyToReceive - (packageQty * perQtyValue);
this.handlingUnit = [];
if (packageQty > 0) { for (let i = 1; i <= packageQty; i++) { const qty = perQtyValue; const code = String(i); this.handlingUnit.push({ code: code, perQty: perQtyValue, packageQty: 1, qty: qty }); } }
if (remainder > 0) { const code = String(this.handlingUnit.length + 1); this.handlingUnit.push({ code: code, perQty: remainder, packageQty: 1, qty: remainder }); }
localStorage.setItem(this.huKey, JSON.stringify(this.handlingUnit)); this.hanlingItem.perQty = ''; this.hanlingItem.packageQty = '';
this.$message.success(`自动创建了${this.handlingUnit.length}个HU,合计数量:${qtyToReceive}`); }, /** * 手动创建HU(手动模式) */ createHandlingUnit() { const { perQty, packageQty } = this.hanlingItem; if (!perQty || !packageQty || isNaN(perQty) || isNaN(packageQty)) { return this.$message.warning("请填写有效的包装信息"); } const qty = parseFloat(perQty) * parseInt(packageQty); const code = String(this.handlingUnit.length + 1); const newItem = { ...this.hanlingItem, qty, code }; this.handlingUnit.push(newItem); localStorage.setItem(this.huKey, JSON.stringify(this.handlingUnit));
this.hanlingItem.perQty = ''; this.hanlingItem.packageQty = ''; }, removeItem(index) { this.handlingUnit.splice(index, 1); localStorage.setItem(this.huKey, JSON.stringify(this.handlingUnit)); }, clearAllHandlingUnitCache() { this.hanlingItem = { code: '', qty: '', perQty: '', packageQty: '' }; const keys = Object.keys(localStorage); keys.forEach(key => { if (key.startsWith('hu_shop_')) { localStorage.removeItem(key); } }); }, // 确认入库
async confirmInbound() { if (this.fullscreenLoading) return;
const item = this.inboundItem; if (!item.transQty || !item.locationNo || !item.batchNo) { return this.$message.error("请填写完整信息"); }
// 检查数量是否超出
const transQty = parseFloat(item.transQty) || 0; const qtyComplete = parseFloat(item.qtyComplete) || 0; const lotSize = parseFloat(item.lotSize) || 0;
if (qtyComplete + transQty > lotSize) { try { await this.$confirm( `当前已完成数量(${qtyComplete}) + 本次入库数量(${transQty}) = ${qtyComplete + transQty},超过订单数量(${lotSize}),是否继续?`, '数量超出提示', { confirmButtonText: '继续', cancelButtonText: '取消', type: 'warning' } ); } catch (error) { // 用户取消
return; } }
this.loadingText = '提交中...'; this.fullscreenLoading = true;
// 构建提交数据
const submitData = { // 基本字段
site: this.site, warehouseId: this.warehouseId, orderNo: item.orderNo, releaseNo: item.releaseNo, sequenceNo: item.sequenceNo, partNo: item.partNo, partDesc: item.partDesc, transQty: item.transQty, batchNo: item.batchNo, locationNo: item.locationNo, serialNo: item.serialNo || '*', engChgLevel: item.engChgLevel || '1', uom: item.uom, lotSize: item.lotSize, qtyComplete: item.qtyComplete, // IFS参数
ifsAutoReport: item.ifsAutoReport ? 'Yes' : 'No', ifsSimplifiedMaterial: item.ifsSimplifiedMaterial ? 'Yes' : 'No', ifsAutoClose:item.closeCodeDb==='Y'?'TRUE':'FALSE', // 处理单元列表
handlingUnitList: this.handlingUnit.map(hu => ({ perQty: hu.perQty, packageQty: hu.packageQty })) };
submitShopOrderInbound(submitData).then(({ data }) => { if (data.code === 0) { this.$message.success("入库成功"); this.clearAllHandlingUnitCache(); this.printViaServer(data.data); this.processFlag = 1; this.scanCode = ''; this.shopOrderList = []; this.inboundItem = {}; this.handlingUnit = []; } else { this.$message.error(data.msg || '入库失败'); } }).catch(error => { console.error('入库失败:', error); this.$message.error('网络错误,请重试'); }).finally(() => { this.fullscreenLoading = false; }); }, /** * 通过服务器打印 */ async printViaServer(transNo) { this.$emit('print-start') try { const printRequest = { userId: localStorage.getItem('userName'), username: localStorage.getItem('userName'), site: localStorage.getItem('site'), transNo: transNo, labelType: "库存成品标签" } const { data } = await printLabel(printRequest) if (data.code === 200) { this.$message.success(`打印任务已发送!`) } } catch (error) { console.error('服务器打印失败:', error) this.$message.error(`打印失败: ${error.message || error}`) } }, }, mounted() { this.$nextTick(() => this.$refs.scanCodeRef.focus()); },
};</script>
<style scoped>.mt10 { margin-top: 10px;}.scan-box input { width: 100%; padding: 12px; font-size: 16px;}.item-list { flex: 1; overflow-y: auto; margin: 10px 0; border: 1px solid rgba(200, 200, 200, 0.8);}.item-list span { color: #000; font-size: 15px;}.bottom-line-row { border-bottom: 1px solid rgba(200, 200, 200, 0.8);}.recvButton { font-size: 16px; border-radius: 3px; color: #17b3a3;}.item-list .el-row { cursor: pointer; transition: background 0.3s;}.item-list .el-row:hover { background: #f5f7fa;}.disabled-button { color: #ccc !important; cursor: not-allowed !important;}.form-section >>> .el-col { margin-bottom: 2px;}.status-bar { display: flex; justify-content: space-between; align-items: center; background: #17b3a3; color: white;}
/* 自定义loading样式 */.pda-container >>> .el-loading-mask { background-color: rgba(255, 255, 255, 0.3) !important;}
.pda-container >>> .el-loading-spinner { margin-top: -25px;}
.pda-container >>> .el-loading-spinner .circular { width: 35px; height: 35px;}
.pda-container >>> .el-loading-text { color: #17b3a3 !important; font-size: 14px; font-weight: 500; margin-top: 10px;}</style>
|