|
|
<template> <div> <div class="pda-container"> <div class="status-bar"> <div class="goBack" @click="$router.back()"><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"> <!-- 搜索框 --> <div class="search-container"> <el-input clearable class="compact-input" v-model="scanCode" placeholder="请扫描HandlingUnit条码" prefix-icon="el-icon-search" @keyup.enter.native="handleScan" ref="scanInput" /> <div class="mode-switch"> <el-switch class="custom-switch" v-model="isRemoveMode" active-color="#ff4949" inactive-color="#13ce66"> </el-switch> <span v-if="isRemoveMode" class="switch-text">{{ '移除' }}</span> <span v-else class="switch-text2">{{ '添加' }}</span> </div> </div>
<!-- 移库信息卡片 --> <div class="material-info-card"> <div class="input-form"> <div class="form-row"> <div class="form-item"> <label class="form-label">目标库位</label> <el-input v-model="moveForm.targetLocationId" placeholder="请扫描或输入目标库位" size="small" @keyup.enter.native="handleLocationScan" ref="locationInput"> </el-input> </div> </div> </div> </div>
<!-- 移库信息确认标题 --> <div class="section-title"> <div class="title-left"> <i class="el-icon-box"></i> <span>移库信息确认</span> </div> </div>
<!-- 扫描的HandlingUnit明细列表 --> <div class="scanned-items" v-if="scannedItems.length > 0" style="margin: 2px;"> <div class="label-list"> <el-form label-position="top" style="margin: 3px;"> <el-row :gutter="5" v-for="(label, index) in scannedItems" :key="label.id" :class="index < scannedItems.length - 1 ? 'bottom-line-row' : ''" style="border: 1px solid #e0e0e0; border-radius: 4px; margin-bottom: 8px; padding: 8px;"> <el-col :span="16"> <el-form-item label="HandlingUnit"> <span>{{ label.unitId }}</span> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="物料编码"> <span>{{ label.partNo }}</span> </el-form-item> </el-col> <el-col :span="24" v-if="label.partDesc"> <el-form-item label="物料描述"> <span>{{ label.partDesc }}</span> </el-form-item> </el-col> <el-col :span="10" v-if="label.batchNo"> <el-form-item label="批次号"> <span>{{ label.batchNo }}</span> </el-form-item> </el-col> <el-col :span="10" v-if="label.locationId"> <el-form-item label="当前库位"> <span>{{ label.locationId }}</span> </el-form-item> </el-col> <el-col :span="4"> <el-form-item label="数量"> <span>{{ label.qty }} {{ label.unit || '个' }}</span> </el-form-item> </el-col> <el-col :span="12" v-if="label.expiredDate"> <el-form-item label="过期日期"> <span>{{ formatDate(label.expiredDate) }}</span> </el-form-item> </el-col> </el-row> </el-form> </div> </div>
<!-- 空状态 --> <div v-if="scannedItems.length === 0" class="empty-labels"> <div style="text-align: center; padding: 20px;"> <p style="color: #999; margin: 0;">暂无扫描HandlingUnit</p> </div> </div>
<!-- 底部操作按钮 --> <div class="bottom-actions" v-if="scannedItems.length > 0"> <button class="action-btn primary" @click="confirmMove" :disabled="loading"> <i v-if="loading" class="el-icon-loading"></i> {{ loading ? '移库中...' : '确认移库' }} </button> <button class="action-btn secondary" style="margin-left: 10px;" @click="cancelProcess" :disabled="loading"> 取消 </button> </div> </div> </div> </div></template>
<script>import { scanHandlingUnitLabel, confirmInventoryMove } from '@/api/po/po.js';
export default { data() { return { scanCode: '', isRemoveMode: false, // 默认为添加模式
moveForm: { moveReason: '', targetLocationId: '' }, scannedItems: [], site: localStorage.getItem('site') || 'SITE01', loading: false }; }, methods: { // 处理扫描
handleScan() { if (!this.scanCode.trim()) { return; }
if (this.isRemoveMode) { this.removeLabelByCode(this.scanCode.trim()); } else { this.validateAndAddLabel(this.scanCode.trim()); } this.scanCode = ''; },
// 处理库位扫描
handleLocationScan() { if (!this.moveForm.targetLocationId.trim()) { return; }
// 失去光标
if (this.$refs.locationInput) { this.$refs.locationInput.blur(); }
// 定位到页面最底部
this.$nextTick(() => { // 方法1:尝试滚动到底部操作按钮
const bottomActions = document.querySelector('.bottom-actions'); if (bottomActions) { bottomActions.scrollIntoView({ behavior: 'smooth', block: 'end' }); return; }
// 方法2:查找滚动容器并滚动
const scrollContainer = document.querySelector('.pda-container > div[style*="overflow-y"]') || document.querySelector('.pda-container > div:nth-child(2)');
if (scrollContainer) { scrollContainer.scrollTo({ top: scrollContainer.scrollHeight, behavior: 'smooth' }); return; }
// 方法3:备用方案 - 滚动整个页面
setTimeout(() => { window.scrollTo({ top: document.documentElement.scrollHeight, behavior: 'smooth' }); }, 100); }); },
// 验证标签并添加到列表
validateAndAddLabel(unitId) { const params = { unitId: unitId, site: this.site, };
scanHandlingUnitLabel(params).then(({ data }) => { if (data && data.code === 0 && data.data) { const huInfo = data.data; this.processHandlingUnit(unitId, huInfo); } else { this.$message.error(data.msg || 'HandlingUnit不存在或查询失败'); } }).catch(error => { this.$message.error('扫描失败'); }); },
// 处理HandlingUnit数据
processHandlingUnit(unitId, huInfo) { // 检查是否已经扫描过
const exists = this.scannedItems.find(item => item.unitId === unitId); if (exists) { this.$message.warning('该HandlingUnit已扫描,请勿重复扫描'); return; }
// 校验HU是否属于当前站点
if (huInfo.site && huInfo.site !== this.site) { this.$message.error('HandlingUnit站点不匹配'); return; }
/* // 校验HU是否在库 if (huInfo.inStockFlag !== 'Y') { this.$message.error('HandlingUnit不在库,无法移库'); return; }*/
// 校验过期日期一致性
if (this.scannedItems.length > 0) { const firstItemExpiryDate = this.scannedItems[0].expiredDate; const currentExpiryDate = huInfo.expiredDate; // 如果两个日期都存在且不相等,则不允许添加
if (firstItemExpiryDate && currentExpiryDate) { const firstDate = new Date(firstItemExpiryDate).toDateString(); const currentDate = new Date(currentExpiryDate).toDateString(); if (firstDate !== currentDate) { this.$message.error(`HandlingUnit的过期日期不一致,无法一起移库。已扫描的过期日期:${firstDate},当前扫描的过期日期:${currentDate}`); return; } } // 如果一个有过期日期,另一个没有,也不允许
else if (firstItemExpiryDate || currentExpiryDate) { this.$message.error('HandlingUnit的过期日期不一致,部分有过期日期,部分没有,无法一起移库'); return; } }
// 添加到列表
this.scannedItems.push({ id: Date.now(), unitId: unitId, partNo: huInfo.partNo, partDesc: huInfo.partDesc, qty: huInfo.qty, unit: huInfo.unit, batchNo: huInfo.batchNo, locationId: huInfo.locationId, wdr: huInfo.wdr, expiredDate: huInfo.expiredDate });
this.$message.success('扫描成功'); },
// 通过条码移除标签
removeLabelByCode(unitId) { const index = this.scannedItems.findIndex(item => item.unitId === unitId); if (index !== -1) { this.scannedItems.splice(index, 1); this.$message.success('移除成功'); } else { this.$message.warning('未找到该HandlingUnit'); } },
// 确认移库
confirmMove() { // 验证必填字段
if (!this.moveForm.targetLocationId) { this.$message.error('请输入目标库位'); return; }
if (this.scannedItems.length === 0) { this.$message.warning('请先扫描HandlingUnit'); return; }
// 验证目标库位与当前库位不同
const sameLocationItems = this.scannedItems.filter(item => item.locationId === this.moveForm.targetLocationId ); if (sameLocationItems.length > 0) { this.$message.error('目标库位不能与当前库位相同'); return; }
this.$confirm('确认要进行移库操作吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { this.submitConfirmMove(); }).catch(() => { // 用户取消
}); },
// 提交确认移库
submitConfirmMove() { const handlingUnitIds = this.scannedItems.map(item => item.unitId);
const params = { site: this.site, moveReason: this.moveForm.moveReason, targetLocationId: this.moveForm.targetLocationId, handlingUnitIds: handlingUnitIds, scannedItems: this.scannedItems };
// 开始loading
this.loading = true;
// 调用后端API
confirmInventoryMove(params).then(({ data }) => { this.loading = false; if (data && data.code === 0) { this.$message({ message: '移库成功!处理单元: ' + handlingUnitIds.length + '个', type: 'success', duration: 3000 });
// 清空页面内容,留在当前页面
this.clearPageContent(); } else { this.$message.error(data.msg || '移库失败'); } }).catch(error => { this.loading = false; console.error('移库失败:', error); this.$message.error('移库失败,请重试'); }); },
// 清空页面内容
clearPageContent() { // 清空表单数据
this.moveForm = { moveReason: '', targetLocationId: '' }; // 清空已扫描的物料列表
this.scannedItems = []; // 清空扫描框
this.scanCode = ''; // 重置为添加模式
this.isRemoveMode = false; // 让扫描框重新获得焦点
this.$nextTick(() => { if (this.$refs.scanInput) { this.$refs.scanInput.focus(); } }); },
// 取消处理
cancelProcess() { if (this.scannedItems.length > 0) { this.$confirm('取消后将清空已扫描的HandlingUnit,确定取消吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '继续操作', type: 'warning' }).then(() => { this.$router.back(); }).catch(() => { // 用户选择继续操作
}); } else { this.$router.back(); } },
// 初始化表单数据
initFormData() { // 可以在这里设置其他默认值
},
// 格式化日期显示
formatDate(dateString) { if (!dateString) return ''; try { const date = new Date(dateString); return date.toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' }); } catch (error) { return dateString; } } },
mounted() { // 初始化表单数据
this.initFormData();
// 聚焦扫描框
this.$nextTick(() => { if (this.$refs.scanInput) { this.$refs.scanInput.focus(); } }); }};</script>
<style scoped>/* 复用other-inbound.vue的样式 */.pda-container { width: 100%; height: 100vh; display: flex; flex-direction: column; overflow: hidden;}
.status-bar { background: #17B3A3; color: white; padding: 8px 16px; display: flex; justify-content: space-between; align-items: center; height: 40px; min-height: 40px;}
.goBack { cursor: pointer; font-size: 16px; font-weight: 500;}
/* 搜索容器 */.search-container { padding: 12px 16px; background: white; display: flex; align-items: center; gap: 12px;}
.search-container .el-input { width: 240px; margin-right: 12px;}
/* 紧凑型输入框样式 */.compact-input ::v-deep .el-input__inner { height: 36px; padding: 0 12px 0 35px; font-size: 14px;}
.compact-input ::v-deep .el-input__prefix { left: 10px;}
.compact-input ::v-deep .el-input__suffix { right: 30px;}
/* 模式切换开关 */.mode-switch { position: relative; display: inline-block;}
.custom-switch { transform: scale(1.3);}/* 中间文字 */.switch-text { position: absolute; left: 25%; transform: translateX(-50%); top: 50%; transform: translateY(-50%) translateX(-50%); font-size: 12px; font-weight: 500; color: #606266; white-space: nowrap; pointer-events: none; z-index: 1; top: 53%; transform: translate(-50%, -50%); font-size: 12px; font-weight: bold; color: white; pointer-events: none; z-index: 2;}
.switch-text2 { position: absolute; left: 75%; transform: translateX(-50%); top: 50%; transform: translateY(-50%) translateX(-50%); font-size: 12px; font-weight: 500; color: #606266; white-space: nowrap; pointer-events: none; z-index: 1; top: 53%; transform: translate(-50%, -50%); font-size: 12px; font-weight: bold; color: white; pointer-events: none; z-index: 2;}
/* 调整 switch 尺寸以便容纳文字 */.custom-switch ::v-deep .el-switch__core { width: 60px; height: 28px;}
/* 物料信息卡片 */.material-info-card { background: white; margin: 4px 16px; padding: 6px 20px; border-radius: 8px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); border: 1px solid #f0f0f0;}
.card-title { margin-bottom: 8px; display: flex; align-items: center; gap: 8px;}
.title-label { font-size: 12px; color: #666; font-weight: 500;}
.title-value { font-size: 16px; font-weight: bold; color: #333; line-height: 1.2;}
/* 表单样式 */.input-form { margin-top: 8px;}
.form-row { display: flex; gap: 12px; margin-bottom: 12px;}
.form-row:last-child { margin-bottom: 0;}
.form-item { flex: 1; display: flex; flex-direction: column;}
.form-label { font-size: 11px; color: #666; font-weight: 500; margin-bottom: 4px; display: block;}
.form-item .el-input { width: 100%;}
.form-item .el-date-editor { width: 100%;}
/* 区域标题 */.section-title { display: flex; align-items: center; justify-content: space-between; padding: 6px 8px; background: white; margin: 0 16px; margin-top: 4px; border-radius: 8px 8px 0 0; border-bottom: 2px solid #17B3A3;}
.title-left { display: flex; align-items: center;}
.title-left i { color: #17B3A3; font-size: 16px; margin-right: 8px;}
.title-left span { color: #17B3A3; font-size: 14px; font-weight: 500;}
/* 标签列表 */.label-list { background: white; margin: 0 16px 12px; border-radius: 0 0 8px 8px; overflow: hidden;}
.label-list .el-form-item { margin-bottom: 1px;}
.label-list .el-form-item__label { padding-bottom: 1px; margin-bottom: 0; line-height: 1.1; font-size: 11px;}
.label-list .el-form-item__content { line-height: 1.2; font-size: 12px;}
.bottom-line-row { border-bottom: 1px solid #f0f0f0; margin-bottom: 8px; padding-bottom: 8px;}
.empty-labels { padding: 20px; text-align: center; color: #999; background: white; margin: 0 16px; border-radius: 8px;}
/* 底部操作按钮 */.bottom-actions { display: flex; padding: 16px; gap: 20px; background: white; margin-top: auto;}
.action-btn { flex: 1; padding: 12px; border: 1px solid #17B3A3; background: #17B3A3; color: white; border-radius: 20px; font-size: 14px; cursor: pointer; transition: all 0.2s ease;}
.action-btn.secondary { background: white; color: #17B3A3;}
.action-btn:hover { background: #0d8f7f; border-color: #0d8f7f;}
.action-btn.secondary:hover { background: #17B3A3; color: white;}
.action-btn:active { transform: scale(0.98);}
.action-btn:disabled { background: #c0c4cc !important; border-color: #c0c4cc !important; color: white !important; cursor: not-allowed !important; transform: none !important; box-shadow: none !important;}
.action-btn:disabled:hover { background: #c0c4cc !important; transform: none !important; box-shadow: none !important;}
.action-btn.secondary:disabled { background: #f5f7fa !important; color: #c0c4cc !important; border-color: #e4e7ed !important;}
/* 响应式设计 */@media (max-width: 360px) { .status-bar { padding: 8px 12px; }
.search-container { padding: 8px 12px; }
.material-info-card { margin: 4px 12px; padding: 6px 16px; }
.item-list { margin: 0 12px 8px; }
.form-row { gap: 8px; margin-bottom: 8px; }
.form-label { font-size: 10px; margin-bottom: 2px; }}</style>
|