You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

537 lines
22 KiB

<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="扫描PO条码或输入PO号"
inputmode="none"
autocomplete="off"
autocorrect="off"
spellcheck="false"
@keyup.enter.native="searchPoList" ref="scanCodeRef" />
</div>
<div class="item-list" v-if="poList.length > 0" style="margin: 2px;">
<el-form label-position="top" style="margin: 3px;">
<el-row :gutter="5" @click.native="recvLine(poDetail)"
v-for="(poDetail, index) in poList" :key="index" :class="index < poList.length - 1 ? 'bottom-line-row' : ''">
<el-col :span="8">
<el-form-item label="商品编码"><span>{{ poDetail.partNo }}</span></el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="行号/下达号"><span>{{ poDetail.lineNo }}/{{ poDetail.wdr || '*' }}</span></el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="">
<el-button type="text" class="recvButton" @click="recvLine(poDetail)"
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>{{ poDetail.description }}</span></el-form-item>
</el-col>
<el-col :span="6" :class="{ mt10: getTextWidth(poDetail.description) > 34 }">
<el-form-item label="订单数量"><span>{{ poDetail.purchaseQty }}</span></el-form-item>
</el-col>
<el-col :span="6" :class="{ mt10: getTextWidth(poDetail.description) > 34 }">
<el-form-item label="待收数量"><span>{{ poDetail.qtyToReceive }}</span></el-form-item>
</el-col>
<el-col :span="6" :class="{ mt10: getTextWidth(poDetail.description) > 34 }">
<el-form-item label="计划数量"><span>{{ poDetail.invQtyToReceive }}</span></el-form-item>
</el-col>
<el-col :span="6" :class="{ mt10: getTextWidth(poDetail.description) > 34 }">
<el-form-item style="margin-left: 20px" label="单位"><span>{{ poDetail.purchaseUOM }}</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="PO号码"><el-input v-model="recvItem.orderNo" disabled /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="行号/下达号"><el-input v-model="displayLineWdr" disabled /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="商品编码"><el-input v-model="recvItem.partNo" disabled /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="计量单位"><el-input v-model="recvItem.purchaseUOM" disabled /></el-form-item></el-col>
<el-col :span="24"><el-form-item label="商品名称"><el-input v-model="recvItem.description" disabled /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="订单数量"><el-input v-model="recvItem.purchaseQty" disabled /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="待收数量"><el-input v-model="recvItem.qtyToReceive" disabled /></el-form-item></el-col>
<el-col :span="12">
<el-form-item label="此次接收数量">
<el-input v-model="recvItem.transQty" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label=" ">
<!-- <el-button type="text" @click.stop="handlingUnitStep" :disabled="recvItem.needHandlingUnit !== 'Y'"
:class="{ 'disabled-button': recvItem.needHandlingUnit !== 'Y' }"
style="font-size: 16px" size="small">包装记录</el-button>-->
<el-button type="text" @click.stop="handlingUnitStep"
style="font-size: 16px" size="small">包装记录</el-button>
</el-form-item>
</el-col>
<el-col :span="12"><el-form-item label="制造日期">
<el-date-picker v-model="recvItem.manufactureDate"
type="date" format="yyyy-MM-dd" value-format="yyyy-MM-dd"
placeholder="选择制造日期" style="width: 100%"
inputmode="none"
autocomplete="off"
autocorrect="off"
disabled
spellcheck="false" />
</el-form-item></el-col>
<el-col :span="12"><el-form-item label="供应商批次">
<el-input v-model="recvItem.supplierBatchNo" disabled placeholder="请输入供应商批次" />
</el-form-item></el-col>
<el-col :span="12"><el-form-item label="WDR">
<el-input v-model="recvItem.wdr" placeholder="请输入WDR" />
</el-form-item></el-col>
<el-col :span="12"><el-form-item label="到达日期">
<el-date-picker v-model="recvItem.arrivalDate" type="date" format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择到达日期" style="width: 100%"
inputmode="none"
autocomplete="off"
autocorrect="off"
spellcheck="false" />
</el-form-item></el-col>
<el-col :span="12"><el-form-item label="库位"><el-input v-model="recvItem.locationNo" placeholder="请输入库位" @blur="validateLocation" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="批号"><el-input v-model="recvItem.batchNo" placeholder="请输入批号" /></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: 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="receivePo">保存</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="recvItem.partNo" disabled /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="计量单位"><el-input v-model="recvItem.purchaseUOM" disabled /></el-form-item></el-col>
<el-col :span="24"><el-form-item label="商品名称"><el-input v-model="recvItem.description" disabled /></el-form-item></el-col>
<el-col :span="8"><el-form-item label="单包装数量"><el-input v-model="hanlingItem.perQty" /></el-form-item></el-col>
<el-col :span="8"><el-form-item label="包装数"><el-input v-model="hanlingItem.packageQty" /></el-form-item></el-col>
<el-col :span="8" style="margin-top: 24px"><el-form-item>
<el-button type="text" @click="createHandlingUnit" 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="操作">
<template slot-scope="scope">
<a @click="removeItem(scope.$index)">删除</a>
</template>
</el-table-column>
</el-table>
<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 { getPoList, receivePo, printLabel, getNextItemNo, validateLocationForReceiveCase } from "@/api/po/po.js";
export default {
data() {
return {
processFlag: 1,
scanCode: '',
poList: [],
recvItem: {},
handlingUnit: [],
hanlingItem: { code: '', qty: '', perQty: '', packageQty: '' },
site:localStorage.getItem('site'),
warehouseId:localStorage.getItem('selectedWarehouse'),
fullscreenLoading: false, // 控制全屏loading
loadingText: '加载中...' // 动态loading文本
};
},
computed: {
totalQty() {
const sum = this.handlingUnit.reduce((sum, item) => sum + Number(item.qty), 0);
this.recvItem.transQty = sum;
return sum;
},
huKey() {
return `hu_${this.recvItem.poNumber}_${this.recvItem.lineNo}`;
},
displayLineWdr() {
return `${this.recvItem.lineNo || ''}/${this.recvItem.wdr || '*'}`;
}
},
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;
},
searchPoList() {
if (!this.scanCode) return this.poList = [];
// 开始搜索时显示loading
this.loadingText = '搜索中...';
this.fullscreenLoading = true;
getPoList({ poNumber: this.scanCode,site: this.site }).then(({ data }) => {
if (data.code === 0) {
this.poList = data.rows
} else {
this.$message.error(data.msg || '操作失败');
}
// 搜索完成后让输入框失去焦点
this.$nextTick(() => {
if (this.$refs.scanCodeRef) {
this.$refs.scanCodeRef.blur();
}
});
}).catch(error => {
console.error('搜索失败:', error);
this.$message.error('搜索失败,请重试');
}).finally(() => {
// 搜索完成后关闭loading
this.fullscreenLoading = false;
});
},
async recvLine(row) {
if (row.authorizationRequired=='TRUE') {
return this.$message.warning("该采购订单需要审核,无法接收");
}
if (row.receiveCaseDB!='INVDIR' && row.receiveCaseDB!='QAINV' && row.receiveCaseDB!='ARRINV') {
return this.$message.warning("该采购订单行的收货方式为"+row.receiveCase+",无法接收");
}
if (row.poStatus === 'Stopped' || row.poStatus === 'Closed' || row.poStatus === 'Cancelled' || row.poStatus === 'Planned') {
return this.$message.warning("该采购订单状态为"+row.poStatus+",无法接收");
}
if (row.status === 'Stopped' || row.status === 'Closed' || row.status === 'Cancelled') {
return this.$message.warning("该采购订单行状态为"+row.status+",无法接收");
}
if (row.convFactor !== 1) {
return this.$message.warning("采购计量单位和库存计量单位不一致,无法接收");
}
// 获取下一个itemNo
let nextItemNo = 1;
try {
const { data } = await getNextItemNo({
orderNo: row.orderNo,
lineNo: row.lineNo,
releaseNo: row.releaseNo || ''
});
if (data.code === 0) {
nextItemNo = data.data;
}
} catch (error) {
console.error('获取itemNo失败:', error);
// 失败时使用默认值1
}
this.recvItem = {
...row,
poNo: row.orderNo || this.scanCode,
dueinQty: row.qtyToReceive || row.invQtyToReceive,
transQty: '',
itemNo: nextItemNo,
batchNo: row.orderNo+'-'+row.lineNo+'-'+row.releaseNo+'-'+nextItemNo,
deliveryDate: row.plannedDeliveryDate || '',
arrivalDate: this.getCurrentDate(),
supplierBatchNo: '',
wdr:"*"
};
this.processFlag = 2;
},
// 获取当前日期
getCurrentDate() {
const now = new Date();
return now.getFullYear() + '-' +
String(now.getMonth() + 1).padStart(2, '0') + '-' +
String(now.getDate()).padStart(2, '0') + ' ' +
String(now.getHours()).padStart(2, '0') + ':' +
String(now.getMinutes()).padStart(2, '0') + ':' +
String(now.getSeconds()).padStart(2, '0');
},
handlingUnitStep() {
this.processFlag = 3;
const saved = localStorage.getItem(this.huKey);
this.handlingUnit = saved ? JSON.parse(saved) : [];
},
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));
// 创建HU后清空包装数和单包装数量
this.hanlingItem.perQty = '';
this.hanlingItem.packageQty = '';
},
removeItem(index) {
this.handlingUnit.splice(index, 1);
localStorage.setItem(this.huKey, JSON.stringify(this.handlingUnit));
},
// 清除所有handlingUnit缓存
clearAllHandlingUnitCache() {
this.hanlingItem = { code: '', qty: '', perQty: '', packageQty: '' };
const keys = Object.keys(localStorage);
keys.forEach(key => {
if (key.startsWith('hu_')) {
localStorage.removeItem(key);
}
});
},
// 校验库位
async validateLocation() {
if (!this.recvItem.locationNo || !this.recvItem.receiveCaseDB) {
return;
}
try {
const { data } = await validateLocationForReceiveCase({
site: this.site,
locationId: this.recvItem.locationNo,
receiveCaseDB: this.recvItem.receiveCaseDB
});
if (data.code !== 0) {
this.$message.error(data.msg || '库位校验失败');
// 清空库位输入
this.recvItem.locationNo = '';
}
} catch (error) {
console.error('库位校验失败:', error);
this.$message.error('库位校验失败,请重试');
// 清空库位输入
this.recvItem.locationNo = '';
}
},
async receivePo() {
if (this.fullscreenLoading) return; // 防止重复点
this.loadingText = '提交中...';
this.fullscreenLoading = true;
const item = this.recvItem;
if (!item.transQty || !item.locationNo || !item.batchNo) {
this.fullscreenLoading = false;
return this.$message.error("请填写完整信息");
}
// 提交前再次校验库位
try {
const { data: validationData } = await validateLocationForReceiveCase({
site: this.site,
locationId: item.locationNo,
receiveCaseDB: item.receiveCaseDB
});
if (validationData.code !== 0) {
this.fullscreenLoading = false;
return this.$message.error(validationData.msg || '库位校验失败');
}
} catch (error) {
console.error('库位校验失败:', error);
this.fullscreenLoading = false;
return this.$message.error('库位校验失败,请重试');
}
// 构建符合服务端TransDetailDto结构的数据
const receiveData = {
// 基本字段
site: this.site,
warehouseId: this.warehouseId,
partNo: item.partNo,
partDesc: item.description,
transQty: item.transQty,
batchNo: item.batchNo,
locationNo: item.locationNo,
itemNo: item.itemNo,
wdr: item.wdr || '*',
deliveryDate: item.deliveryDate,
arrivalDate: item.arrivalDate,
supplierBatchNo: item.supplierBatchNo,
samplePercent: item.samplePercent || 0,
sampleQty: item.sampleQty || 0,
// PO相关字段
poNo: item.poNumber || item.poNo,
orderNo: item.orderNo,
lineNo: item.lineNo,
releaseNo: item.releaseNo,
receiptNo: item.receiptNo,
orderRef1: item.orderNo,
supplierNo: item.supplierNo,
purchaseUOM: item.purchaseUOM,
receiveCase: item.receiveCase,
receiveCaseDB: item.receiveCaseDB,
inventoryPartDB: item.inventoryPartDB,
// 业务控制字段
needHandlingUnit: item.needHandlingUnit ,
needCheck: item.needCheck ,
warehouseType: item.warehouseType ,
// 日期字段
manufactureDate: item.manufactureDate,
// 处理单元列表
handlingUnitList: this.handlingUnit.map(hu => ({
perQty: hu.perQty,
packageQty: hu.packageQty
}))
};
receivePo(receiveData).then(({ data }) => {
if (data.code === 0) {
this.$message.success("操作成功");
this.clearAllHandlingUnitCache();
this.printViaServer(data.data,item.needCheck); // 调用打印
this.processFlag = 1;
this.scanCode = '';
this.poList = [];
this.recvItem = {};
this.handlingUnit = [];
} else {
this.$message.error(data.msg || '操作失败');
}
}).catch(error => {
console.error('接收失败:', error);
this.$message.error('网络错误,请重试');
}).finally(() => {
this.fullscreenLoading = false; // 结束后关闭loading
});
},
/**
* 通过服务器打印
*/
async printViaServer(receiptNo,needCheck) {
this.$emit('print-start')
try {
const printRequest = {
reportId: this.reportId,
zplCode: this.zplCode,
paperSize: this.paperSize,
orientation: this.orientation,
dpi: this.dpi,
userId: localStorage.getItem('userName'),
username: localStorage.getItem('userName'),
site: localStorage.getItem('site'),
receiptNo: receiptNo,
needCheck:needCheck,
printLabel:"BIL标签"
}
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: 12px;
}
.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>