8 changed files with 1642 additions and 0 deletions
-
12src/api/haianAutoWarehouse/cancelWcsPallet.js
-
15src/api/haianAutoWarehouse/emptyPalletAssembly.js
-
15src/api/haianAutoWarehouse/palletMerge.js
-
3src/router/index.js
-
18src/views/main.vue
-
516src/views/modules/haianAutoWarehouse/haianCancelWcsPallet.vue
-
467src/views/modules/haianAutoWarehouse/haianEmptyPalletAssembly.vue
-
596src/views/modules/haianAutoWarehouse/haianPalletMerge.vue
@ -0,0 +1,12 @@ |
|||||
|
import { createAPI } from "@/utils/httpRequest.js"; |
||||
|
|
||||
|
// ========== 海安-取消WCS组盘相关接口 ========== - rqrq
|
||||
|
|
||||
|
// 海安-检查栈板组盘状态(返回callingFlag/canOperate/isEmpty)- rqrq
|
||||
|
export const checkPalletWcsStatus = data => createAPI(`/haiAnWcsIntegration/checkPalletWcsStatus`, 'post', data) |
||||
|
|
||||
|
// 海安-取消组盘 - rqrq
|
||||
|
export const cancelWcsPallet = data => createAPI(`/haiAnWcsIntegration/cancelWcsPallet`, 'post', data) |
||||
|
|
||||
|
// 海安-移出全部物料 - rqrq
|
||||
|
export const removeAllPalletDetails = data => createAPI(`/haiAnWcsIntegration/removeAllPalletDetails`, 'post', data) |
||||
@ -0,0 +1,15 @@ |
|||||
|
import { createAPI } from "@/utils/httpRequest.js"; |
||||
|
|
||||
|
// ========== 海安-空托盘组盘相关接口 ========== - rqrq
|
||||
|
|
||||
|
// 海安-检查是否为空托盘(haian_pallet_detail无数据)- rqrq
|
||||
|
export const checkEmptyPallet = data => createAPI(`/haiAnWcsIntegration/checkEmptyPallet`, 'post', data) |
||||
|
|
||||
|
// 海安-获取托盘类型列表(兼容旧页面接口名)- rqrq
|
||||
|
export const getPalletTypes = data => createAPI(`/haiAnWcsIntegration/getPalletTypes`, 'post', data) |
||||
|
|
||||
|
// 海安-更新托盘类型与自动分拣标识 - rqrq
|
||||
|
export const updatePalletTypeAndAutoSort = data => createAPI(`/haiAnWcsIntegration/updatePalletTypeAndAutoSort`, 'post', data) |
||||
|
|
||||
|
// 海安-空托盘通知入库(transportFlag=N仅入库,Y入库并运输)- rqrq
|
||||
|
export const notifyEmptyPalletInbound = data => createAPI(`/haiAnWcsIntegration/notifyEmptyPalletInbound`, 'post', data) |
||||
@ -0,0 +1,15 @@ |
|||||
|
import { createAPI } from "@/utils/httpRequest.js"; |
||||
|
|
||||
|
// ========== 海安-合托相关接口 ========== - rqrq
|
||||
|
|
||||
|
// 海安-校验托盘(来源/目标通用)- rqrq
|
||||
|
export const checkPalletForMerge = data => createAPI(`/haiAnWcsIntegration/checkPalletForMerge`, 'post', data) |
||||
|
|
||||
|
// 海安-校验目标托盘类型是否为混装托盘 - rqrq
|
||||
|
export const checkTargetPalletType = data => createAPI(`/haiAnWcsIntegration/checkTargetPalletType`, 'post', data) |
||||
|
|
||||
|
// 海安-查询托盘明细(用于合托查看)- rqrq
|
||||
|
export const getPalletDetailForMerge = data => createAPI(`/haiAnWcsIntegration/getPalletDetailForMerge`, 'post', data) |
||||
|
|
||||
|
// 海安-执行合托(来源托盘明细并入目标托盘)- rqrq
|
||||
|
export const executePalletMerge = data => createAPI(`/haiAnWcsIntegration/executePalletMerge`, 'post', data) |
||||
@ -0,0 +1,516 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<div class="pda-container"> |
||||
|
<!-- 头部栏 - rqrq --> |
||||
|
<div class="header-bar"> |
||||
|
<div class="header-left" @click="handleBack"> |
||||
|
<i class="el-icon-arrow-left"></i> |
||||
|
<span>取消WCS组盘任务</span> |
||||
|
</div> |
||||
|
<div class="header-right" @click="goHome"> |
||||
|
首页 |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="table-body" style="max-height: 600px; overflow-y: auto;"> |
||||
|
<div class="main-content form-section"> |
||||
|
<!-- 栈板扫描 - rqrq --> |
||||
|
<div class="input-group"> |
||||
|
<label class="input-label">栈板编码</label> |
||||
|
<el-input |
||||
|
v-model="palletCode" |
||||
|
placeholder="请扫描栈板编码" |
||||
|
class="form-input" |
||||
|
clearable |
||||
|
@keyup.enter.native="handlePalletScan" |
||||
|
inputmode="none" |
||||
|
autocomplete="off" |
||||
|
autocorrect="off" |
||||
|
spellcheck="false" |
||||
|
ref="palletInput" |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 栈板信息显示(扫描栈板后显示)- rqrq --> |
||||
|
<div v-if="palletScanned" class="pallet-info-section"> |
||||
|
|
||||
|
<div class="info-row"> |
||||
|
<label class="info-label">是否被调用:</label> |
||||
|
<span class="info-value" :style="{color: palletInfo.callingFlag === 'Y' ? '#F56C6C' : '#67C23A'}"> |
||||
|
{{ palletInfo.callingFlag === 'Y' ? '是' : '否' }} |
||||
|
</span> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 是否发送组盘任务 - rqrq --> |
||||
|
<div class="info-row"> |
||||
|
<label class="info-label">是否已组盘:</label> |
||||
|
<span class="info-value" :style="{color: hasSentWcsTask ? '#F56C6C' : '#67C23A'}"> |
||||
|
{{ hasSentWcsTask ? '是' : '否' }} |
||||
|
</span> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 浏览明细按钮 - rqrq --> |
||||
|
<div class="button-row"> |
||||
|
<button class="action-btn primary" @click="showDetailModal" style="width: 100%;"> |
||||
|
浏览明细 ({{ detailList.length }}条) |
||||
|
</button> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 取消组盘按钮(只有发送了组盘任务才显示)- rqrq --> |
||||
|
<div class="button-row"> |
||||
|
<button |
||||
|
class="action-btn primary" |
||||
|
@click="handleCancelWcsPallet" |
||||
|
style="width: 100%;"> |
||||
|
{{ cancelLoading ? '取消中...' : '取消组盘' }} |
||||
|
</button> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 移出全部物料按钮(只有未发送组盘任务且未被调用且有明细才显示)- rqrq --> |
||||
|
<div v-if="showRemoveAllButton" class="button-row"> |
||||
|
<button |
||||
|
class="action-btn warning" |
||||
|
@click="handleRemoveAllMaterials" |
||||
|
:disabled="removeAllLoading" |
||||
|
style="width: 100%;"> |
||||
|
{{ removeAllLoading ? '移出中...' : '移出全部物料' }} |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 浏览明细弹窗 - rqrq --> |
||||
|
<el-dialog |
||||
|
:title="'栈板明细 (共'+detailList.length+'条)'" |
||||
|
:visible.sync="detailModalVisible" |
||||
|
width="90%" |
||||
|
:close-on-click-modal="false" |
||||
|
:show-close="false" |
||||
|
:modal="true" |
||||
|
:modal-append-to-body="true" |
||||
|
:append-to-body="true" |
||||
|
> |
||||
|
<div class="table-body" style="max-height: 400px; overflow-y: auto;"> |
||||
|
<div class="detail-table"> |
||||
|
<div class="table-header"> |
||||
|
<div class="col-position">位置</div> |
||||
|
<div class="col-layer">层数</div> |
||||
|
<div class="col-serial">标签号</div> |
||||
|
</div> |
||||
|
<div |
||||
|
v-for="(detail, index) in detailList" |
||||
|
:key="index" |
||||
|
class="table-row" |
||||
|
> |
||||
|
<div class="col-position">{{ detail.position }}</div> |
||||
|
<div class="col-layer">{{ detail.layer }}</div> |
||||
|
<div class="col-serial">{{ detail.serialNo }}</div> |
||||
|
</div> |
||||
|
<!-- 暂无数据提示 --> |
||||
|
<div v-if="detailList.length === 0" class="table-row empty-row"> |
||||
|
<div class="empty-hint">暂无栈板明细数据</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div slot="footer" class="dialog-footer"> |
||||
|
<button class="action-btn secondary" @click="detailModalVisible=false">关闭</button> |
||||
|
</div> |
||||
|
</el-dialog> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { |
||||
|
getPalletDetails |
||||
|
} from '../../../api/haianAutoWarehouse/palletPacking' |
||||
|
import { |
||||
|
checkPalletWcsStatus, |
||||
|
cancelWcsPallet, |
||||
|
removeAllPalletDetails |
||||
|
} from '../../../api/haianAutoWarehouse/cancelWcsPallet' |
||||
|
|
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
// 海安PDA前端site唯一来源为store用户对象,兼容旧缓存兜底 - rqrq |
||||
|
site: (this.$store && this.$store.state && this.$store.state.user && this.$store.state.user.site) || localStorage.getItem('site'), |
||||
|
|
||||
|
// 栈板信息 - rqrq |
||||
|
palletCode: '', |
||||
|
palletScanned: false, |
||||
|
palletInfo: { |
||||
|
callingFlag: '', |
||||
|
canOperate: '', |
||||
|
isEmpty: '' |
||||
|
}, |
||||
|
|
||||
|
// 按钮显示控制 - rqrq |
||||
|
hasSentWcsTask: false, |
||||
|
showCancelButton: false, |
||||
|
showRemoveAllButton: false, |
||||
|
|
||||
|
// 栈板明细 - rqrq |
||||
|
detailList: [], |
||||
|
detailModalVisible: false, |
||||
|
|
||||
|
// 加载状态 - rqrq |
||||
|
cancelLoading: false, |
||||
|
removeAllLoading: false |
||||
|
}; |
||||
|
}, |
||||
|
methods: { |
||||
|
// 返回上一页并清空页面状态,避免keep-alive导致脏数据残留 - rqrq |
||||
|
handleBack() { |
||||
|
this.resetPage(false); |
||||
|
this.$router.back(); |
||||
|
}, |
||||
|
|
||||
|
// 回首页前清理状态,保证再次进入页面从干净态开始 - rqrq |
||||
|
goHome() { |
||||
|
this.resetPage(false); |
||||
|
this.$router.push({ path: '/' }); |
||||
|
}, |
||||
|
|
||||
|
// 扫描栈板 - rqrq |
||||
|
handlePalletScan() { |
||||
|
if (!this.palletCode.trim()) { |
||||
|
this.$alert('请输入栈板编码', '提示', { |
||||
|
confirmButtonText: '确定' |
||||
|
}); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
checkPalletWcsStatus({ |
||||
|
site: this.site, |
||||
|
palletId: this.palletCode |
||||
|
}).then(({ data }) => { |
||||
|
if (data.code === 0) { |
||||
|
// 后端返回正确的6位栈板码,赋值给输入框 - rqrq |
||||
|
this.palletCode = data.row.palletId; |
||||
|
|
||||
|
// 保存栈板信息 - rqrq |
||||
|
this.palletInfo = data.row; |
||||
|
this.palletScanned = true; |
||||
|
|
||||
|
// 解析按钮显示逻辑 - rqrq |
||||
|
// canOperate字段存储showCancelButton(是否显示取消组盘按钮)- rqrq |
||||
|
this.showCancelButton = data.row.canOperate === 'Y'; |
||||
|
|
||||
|
// isEmpty字段存储showRemoveAllButton(是否显示移出全部物料按钮)- rqrq |
||||
|
this.showRemoveAllButton = data.row.isEmpty === 'Y'; |
||||
|
|
||||
|
// 计算hasSentWcsTask(是否发送组盘任务)用于显示 - rqrq |
||||
|
this.hasSentWcsTask = this.showCancelButton; |
||||
|
|
||||
|
// 加载栈板明细 - rqrq |
||||
|
this.loadPalletDetails(); |
||||
|
|
||||
|
} else { |
||||
|
this.$alert(data.msg || '栈板不存在', '错误', { |
||||
|
confirmButtonText: '确定', |
||||
|
callback: () => { |
||||
|
this.palletCode = ''; |
||||
|
this.$nextTick(() => { |
||||
|
if (this.$refs.palletInput) { |
||||
|
this.$refs.palletInput.focus(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
console.error('查询栈板失败:', error); |
||||
|
this.$alert(error.message || '查询栈板失败', '错误', { |
||||
|
confirmButtonText: '确定', |
||||
|
callback: () => { |
||||
|
this.palletCode = ''; |
||||
|
this.$nextTick(() => { |
||||
|
if (this.$refs.palletInput) { |
||||
|
this.$refs.palletInput.focus(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 加载栈板明细 - rqrq |
||||
|
loadPalletDetails() { |
||||
|
getPalletDetails({ |
||||
|
site: this.site, |
||||
|
palletId: this.palletCode, |
||||
|
position: '', |
||||
|
layer: '' |
||||
|
}).then(({ data }) => { |
||||
|
if (data.code === 0) { |
||||
|
this.detailList = data.details || []; |
||||
|
} else { |
||||
|
this.detailList = []; |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
console.error('获取栈板明细失败:', error); |
||||
|
this.detailList = []; |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 显示明细弹窗 - rqrq |
||||
|
showDetailModal() { |
||||
|
this.detailModalVisible = true; |
||||
|
}, |
||||
|
|
||||
|
// 取消组盘 - rqrq |
||||
|
handleCancelWcsPallet() { |
||||
|
this.$confirm('确定要取消组盘吗?取消后需要重新组盘才能推送到WCS。', '确认操作', { |
||||
|
confirmButtonText: '确定', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning' |
||||
|
}).then(() => { |
||||
|
this.cancelLoading = true; |
||||
|
|
||||
|
cancelWcsPallet({ |
||||
|
site: this.site, |
||||
|
palletId: this.palletCode |
||||
|
}).then(({ data }) => { |
||||
|
if (data && data.code === 0) { |
||||
|
this.$message.success('取消组盘成功'); |
||||
|
// 重新扫描刷新信息 - rqrq |
||||
|
this.handlePalletScan(); |
||||
|
} else { |
||||
|
this.$alert(data.msg || '取消组盘失败', '错误', { |
||||
|
confirmButtonText: '确定' |
||||
|
}); |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
console.error('取消组盘失败:', error); |
||||
|
// 网络错误也弹出提示框 - rqrq |
||||
|
this.$alert(error.message || '取消组盘失败', '错误', { |
||||
|
confirmButtonText: '确定' |
||||
|
}); |
||||
|
}).finally(() => { |
||||
|
this.cancelLoading = false; |
||||
|
}); |
||||
|
}).catch(() => { |
||||
|
// 用户取消操作 |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 移出全部物料 - rqrq |
||||
|
handleRemoveAllMaterials() { |
||||
|
this.$confirm('确定要移出栈板上的全部物料吗?此操作不可恢复!', '确认操作', { |
||||
|
confirmButtonText: '确定', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning' |
||||
|
}).then(() => { |
||||
|
this.removeAllLoading = true; |
||||
|
|
||||
|
removeAllPalletDetails({ |
||||
|
site: this.site, |
||||
|
palletId: this.palletCode |
||||
|
}).then(({ data }) => { |
||||
|
if (data && data.code === 0) { |
||||
|
this.$message.success('移出全部物料成功'); |
||||
|
// 重新扫描刷新信息 - rqrq |
||||
|
this.handlePalletScan(); |
||||
|
} else { |
||||
|
this.$alert(data.msg || '移出全部物料失败', '错误', { |
||||
|
confirmButtonText: '确定' |
||||
|
}); |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
console.error('移出全部物料失败:', error); |
||||
|
// 网络错误也弹出提示框 - rqrq |
||||
|
this.$alert(error.message || '移出全部物料失败', '错误', { |
||||
|
confirmButtonText: '确定' |
||||
|
}); |
||||
|
}).finally(() => { |
||||
|
this.removeAllLoading = false; |
||||
|
}); |
||||
|
}).catch(() => { |
||||
|
// 用户取消操作 |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 重置页面并按需恢复扫码焦点,统一海安PDA页面生命周期清理行为 - rqrq |
||||
|
resetPage(needFocus) { |
||||
|
this.palletCode = ''; |
||||
|
this.palletScanned = false; |
||||
|
this.palletInfo = { |
||||
|
callingFlag: '', |
||||
|
canOperate: '', |
||||
|
isEmpty: '' |
||||
|
}; |
||||
|
this.hasSentWcsTask = false; |
||||
|
this.showCancelButton = false; |
||||
|
this.showRemoveAllButton = false; |
||||
|
this.detailList = []; |
||||
|
this.detailModalVisible = false; |
||||
|
|
||||
|
if (needFocus) { |
||||
|
this.$nextTick(() => { |
||||
|
if (this.$refs.palletInput) { |
||||
|
this.$refs.palletInput.focus(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.$nextTick(() => { |
||||
|
if (this.$refs.palletInput) { |
||||
|
this.$refs.palletInput.focus(); |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
activated() { |
||||
|
this.resetPage(true); |
||||
|
}, |
||||
|
beforeRouteLeave(to, from, next) { |
||||
|
this.resetPage(false); |
||||
|
next(); |
||||
|
} |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* 栈板信息区域 - rqrq */ |
||||
|
.pallet-info-section { |
||||
|
margin-top: 16px; |
||||
|
padding: 12px; |
||||
|
background: #f5f7fa; |
||||
|
border-radius: 6px; |
||||
|
} |
||||
|
|
||||
|
.info-row { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
padding: 8px 0; |
||||
|
border-bottom: 1px solid #e4e7ed; |
||||
|
} |
||||
|
|
||||
|
.info-row:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.info-label { |
||||
|
font-weight: bold; |
||||
|
color: #606266; |
||||
|
width: 140px; |
||||
|
flex-shrink: 0; |
||||
|
} |
||||
|
|
||||
|
.info-value { |
||||
|
color: #303133; |
||||
|
font-size: 14px; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
.button-row { |
||||
|
margin-top: 12px; |
||||
|
} |
||||
|
|
||||
|
/* 警告按钮 - rqrq */ |
||||
|
.action-btn.warning { |
||||
|
background-color: #E6A23C; |
||||
|
color: white; |
||||
|
border: none; |
||||
|
padding: 10px 20px; |
||||
|
border-radius: 4px; |
||||
|
cursor: pointer; |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
|
||||
|
.action-btn.warning:hover { |
||||
|
background-color: #d99525; |
||||
|
} |
||||
|
|
||||
|
.action-btn.warning:disabled { |
||||
|
background-color: #f5dab1; |
||||
|
cursor: not-allowed; |
||||
|
} |
||||
|
|
||||
|
/* 表格样式 - rqrq */ |
||||
|
.detail-table { |
||||
|
background: white; |
||||
|
border-radius: 6px; |
||||
|
overflow: hidden; |
||||
|
border: 1px solid #e0e0e0; |
||||
|
} |
||||
|
|
||||
|
.table-header, |
||||
|
.table-row { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
padding: 8px; |
||||
|
border-bottom: 1px solid #e0e0e0; |
||||
|
} |
||||
|
|
||||
|
.table-header { |
||||
|
background: #f5f5f5; |
||||
|
font-weight: bold; |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
|
||||
|
.table-row { |
||||
|
font-size: 13px; |
||||
|
} |
||||
|
|
||||
|
.table-row:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.col-position { |
||||
|
flex: 1; |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
.col-layer { |
||||
|
flex: 1; |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
.col-serial { |
||||
|
flex: 4; |
||||
|
text-align: center; |
||||
|
word-break: break-all; |
||||
|
} |
||||
|
|
||||
|
/* 空数据提示 - rqrq */ |
||||
|
.empty-hint { |
||||
|
text-align: center; |
||||
|
color: #999; |
||||
|
padding: 20px; |
||||
|
background: white; |
||||
|
border-radius: 6px; |
||||
|
} |
||||
|
|
||||
|
/* 空数据行样式 - rqrq */ |
||||
|
.empty-row { |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
padding: 20px; |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.empty-row .empty-hint { |
||||
|
text-align: center; |
||||
|
color: #999; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.dialog-footer { |
||||
|
display: flex; |
||||
|
gap: 8px; |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
/* 修复模态框层级问题 - rqrq */ |
||||
|
::v-deep .el-dialog__wrapper { |
||||
|
z-index: 2000 !important; |
||||
|
} |
||||
|
|
||||
|
::v-deep .el-overlay { |
||||
|
z-index: 2000 !important; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,467 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<div class="pda-container"> |
||||
|
<!-- 头部栏 - rqrq --> |
||||
|
<div class="header-bar"> |
||||
|
<div class="header-left" @click="handleBack"> |
||||
|
<i class="el-icon-arrow-left"></i> |
||||
|
<span>空托盘组盘</span> |
||||
|
</div> |
||||
|
<div class="header-right" @click="goHome"> |
||||
|
首页 |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="table-body" style="max-height: 600px; overflow-y: auto;"> |
||||
|
<div class="main-content form-section"> |
||||
|
<!-- 栈板扫描 - rqrq --> |
||||
|
<div class="input-group"> |
||||
|
<label class="input-label">托盘编码</label> |
||||
|
<el-input |
||||
|
v-model="palletCode" |
||||
|
placeholder="请扫描托盘编码" |
||||
|
class="form-input" |
||||
|
clearable |
||||
|
@keyup.enter.native="handlePalletScan" |
||||
|
ref="palletInput" |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 托盘信息显示(扫描托盘后显示)- rqrq --> |
||||
|
<div v-if="palletScanned" class="pallet-info-section"> |
||||
|
<!-- 托盘类型选择 - rqrq --> |
||||
|
<div class="input-group"> |
||||
|
<label class="input-label">托盘类型</label> |
||||
|
<el-select |
||||
|
v-model="currentPalletType" |
||||
|
placeholder="请选择托盘类型" |
||||
|
style="width: 100%;" |
||||
|
@change="handlePalletTypeChange" |
||||
|
> |
||||
|
<el-option |
||||
|
v-for="type in palletTypeOptions" |
||||
|
:key="type.palletType" |
||||
|
:label="`${type.palletType} - ${type.typeDesc}`" |
||||
|
:value="type.palletType" |
||||
|
/> |
||||
|
</el-select> |
||||
|
</div> |
||||
|
|
||||
|
<div class="input-group"> |
||||
|
<label class="input-label">空托数量</label> |
||||
|
<el-select v-model="count"> |
||||
|
<el-option value=1 label=1></el-option> |
||||
|
<el-option value=2 label=2></el-option> |
||||
|
<el-option value=3 label=3></el-option> |
||||
|
<el-option value=4 label=4></el-option> |
||||
|
<el-option value=5 label=5></el-option> |
||||
|
<el-option value=6 label=6></el-option> |
||||
|
</el-select> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 当前站点信息 - rqrq --> |
||||
|
<div class="info-row"> |
||||
|
<label class="info-label">当前站点ID:</label> |
||||
|
<span class="info-value">{{ palletInfo.currentStationId || '-' }}</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="info-row"> |
||||
|
<label class="info-label">当前站点编码:</label> |
||||
|
<span class="info-value">{{ palletInfo.currentStationCode || '-' }}</span> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 操作按钮 - rqrq --> |
||||
|
<div class="button-row" style="display: flex; gap: 8px; margin-top: 16px;"> |
||||
|
<button |
||||
|
class="action-btn primary" |
||||
|
@click="handleNotifyInbound" |
||||
|
:disabled="notifyLoading || !currentPalletType" |
||||
|
style="flex: 1;"> |
||||
|
{{ notifyLoading ? '处理中...' : '通知入库' }} |
||||
|
</button> |
||||
|
<button |
||||
|
class="action-btn primary" |
||||
|
@click="handleInboundAndTransport" |
||||
|
:disabled="transportLoading || !currentPalletType" |
||||
|
style="flex: 1;"> |
||||
|
{{ transportLoading ? '处理中...' : '入库并运输' }} |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 通知入库口选择弹窗:必须明确选择入库口1/2后才允许提交 - rqrq --> |
||||
|
<el-dialog |
||||
|
title="请选择入库口" |
||||
|
:visible.sync="inboundPortDialogVisible" |
||||
|
width="90%" |
||||
|
:close-on-click-modal="false" |
||||
|
:show-close="false" |
||||
|
:append-to-body="true" |
||||
|
> |
||||
|
<div class="input-group"> |
||||
|
<label class="input-label">入库口</label> |
||||
|
<el-select v-model="selectedInboundPort" placeholder="请选择入库口" style="width: 100%;"> |
||||
|
<el-option |
||||
|
v-for="option in inboundPortOptions" |
||||
|
:key="option.value" |
||||
|
:label="option.label" |
||||
|
:value="option.value" |
||||
|
/> |
||||
|
</el-select> |
||||
|
</div> |
||||
|
<div slot="footer" class="dialog-footer"> |
||||
|
<button class="action-btn secondary" @click="cancelInboundPortDialog">取消</button> |
||||
|
<button class="action-btn primary" @click="confirmNotifyInbound">确认</button> |
||||
|
</div> |
||||
|
</el-dialog> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { |
||||
|
checkEmptyPallet, |
||||
|
getPalletTypes, |
||||
|
updatePalletTypeAndAutoSort, |
||||
|
notifyEmptyPalletInbound |
||||
|
} from '../../../api/haianAutoWarehouse/emptyPalletAssembly' |
||||
|
|
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
// 海安PDA前端site唯一来源为store用户对象,兼容旧缓存兜底 - rqrq |
||||
|
site: (this.$store && this.$store.state && this.$store.state.user && this.$store.state.user.site) || localStorage.getItem('site'), |
||||
|
|
||||
|
// 托盘信息 - rqrq |
||||
|
palletCode: '', |
||||
|
palletScanned: false, |
||||
|
palletInfo: { |
||||
|
currentStationId: '', |
||||
|
currentStationCode: '', |
||||
|
palletType: '', |
||||
|
area: '' |
||||
|
}, |
||||
|
|
||||
|
// 托盘类型 - rqrq |
||||
|
currentPalletType: '', |
||||
|
palletTypeOptions: [], |
||||
|
count: 1, |
||||
|
// 通知入库口选择 - rqrq |
||||
|
inboundPortDialogVisible: false, |
||||
|
selectedInboundPort: '', |
||||
|
inboundPortOptions: [ |
||||
|
{ label: '入库口1', value: 'INBOUND_PORT_1' }, |
||||
|
{ label: '入库口2', value: 'INBOUND_PORT_2' } |
||||
|
], |
||||
|
// 加载状态 - rqrq |
||||
|
notifyLoading: false, |
||||
|
transportLoading: false |
||||
|
}; |
||||
|
}, |
||||
|
methods: { |
||||
|
// 返回上一页并清空页面状态,避免keep-alive导致脏数据残留 - rqrq |
||||
|
handleBack() { |
||||
|
this.resetPage(false); |
||||
|
this.$router.back(); |
||||
|
}, |
||||
|
|
||||
|
// 回首页前清理状态,保证再次进入页面从干净态开始 - rqrq |
||||
|
goHome() { |
||||
|
this.resetPage(false); |
||||
|
this.$router.push({ path: '/' }); |
||||
|
}, |
||||
|
|
||||
|
// 扫描托盘 - rqrq |
||||
|
handlePalletScan() { |
||||
|
if (!this.palletCode.trim()) { |
||||
|
this.$alert('请输入托盘编码', '提示', { |
||||
|
confirmButtonText: '确定' |
||||
|
}); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
checkEmptyPallet({ |
||||
|
site: this.site, |
||||
|
palletId: this.palletCode |
||||
|
}).then(({ data }) => { |
||||
|
if (data.code === 0) { |
||||
|
// 后端返回正确的6位栈板码,赋值给输入框 - rqrq |
||||
|
this.palletCode = data.row.palletId; |
||||
|
|
||||
|
// 托盘信息 - rqrq |
||||
|
this.palletInfo = data.row; |
||||
|
this.currentPalletType = data.row.palletType || ''; |
||||
|
this.palletScanned = true; |
||||
|
|
||||
|
// 根据托盘的palletFamily加载托盘类型选项 - rqrq |
||||
|
this.loadPalletTypes(data.row.palletFamily); |
||||
|
|
||||
|
} else { |
||||
|
this.$alert(data.msg || '托盘验证失败', '错误', { |
||||
|
confirmButtonText: '确定', |
||||
|
callback: () => { |
||||
|
this.palletCode = ''; |
||||
|
this.$nextTick(() => { |
||||
|
if (this.$refs.palletInput) { |
||||
|
this.$refs.palletInput.focus(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
console.error('查询托盘失败:', error); |
||||
|
this.$alert(error.message || '查询托盘失败', '错误', { |
||||
|
confirmButtonText: '确定', |
||||
|
callback: () => { |
||||
|
this.palletCode = ''; |
||||
|
this.$nextTick(() => { |
||||
|
if (this.$refs.palletInput) { |
||||
|
this.$refs.palletInput.focus(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 加载托盘类型选项(根据palletFamily过滤)- rqrq |
||||
|
loadPalletTypes(palletFamily) { |
||||
|
getPalletTypes({ |
||||
|
site: this.site, |
||||
|
palletFamily: palletFamily || '' |
||||
|
}).then(({ data }) => { |
||||
|
if (data.code === 0) { |
||||
|
this.palletTypeOptions = data.rows || []; |
||||
|
} else { |
||||
|
this.palletTypeOptions = []; |
||||
|
this.$alert('获取托盘类型失败', '错误', { |
||||
|
confirmButtonText: '确定' |
||||
|
}); |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
console.error('获取托盘类型失败:', error); |
||||
|
this.palletTypeOptions = []; |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 托盘类型变更事件 - rqrq |
||||
|
handlePalletTypeChange() { |
||||
|
// 保存到数据库 - rqrq |
||||
|
updatePalletTypeAndAutoSort({ |
||||
|
site: this.site, |
||||
|
palletId: this.palletCode, |
||||
|
palletType: this.currentPalletType, |
||||
|
autoSort: 'N', |
||||
|
forceFullPalletOut: false |
||||
|
}).then(({ data }) => { |
||||
|
if (data.code === 0) { |
||||
|
this.$message.success('更新成功'); |
||||
|
} else { |
||||
|
this.$alert(data.msg || '更新失败', '错误', { |
||||
|
confirmButtonText: '确定' |
||||
|
}); |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
console.error('更新失败:', error); |
||||
|
this.$alert(error.message || '更新失败', '错误', { |
||||
|
confirmButtonText: '确定' |
||||
|
}); |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 通知入库 - rqrq |
||||
|
handleNotifyInbound() { |
||||
|
if (!this.currentPalletType) { |
||||
|
this.$alert('请先选择托盘类型', '提示', { |
||||
|
confirmButtonText: '确定' |
||||
|
}); |
||||
|
return; |
||||
|
} |
||||
|
// 左按钮“通知入库”必须先选入库口,后端再把入库口映射为固定站点号 - rqrq |
||||
|
this.selectedInboundPort = ''; |
||||
|
this.inboundPortDialogVisible = true; |
||||
|
}, |
||||
|
|
||||
|
// 关闭入库口弹窗并清理选择状态,避免上次选择串到本次提交 - rqrq |
||||
|
cancelInboundPortDialog() { |
||||
|
this.inboundPortDialogVisible = false; |
||||
|
this.selectedInboundPort = ''; |
||||
|
}, |
||||
|
|
||||
|
// 确认通知入库:先校验入库口,再调用通知入库接口 - rqrq |
||||
|
confirmNotifyInbound() { |
||||
|
if (!this.selectedInboundPort) { |
||||
|
this.$alert('请选择入库口', '提示', { |
||||
|
confirmButtonText: '确定' |
||||
|
}); |
||||
|
return; |
||||
|
} |
||||
|
this.inboundPortDialogVisible = false; |
||||
|
this.$confirm('确定要执行通知入库操作吗?', '确认操作', { |
||||
|
confirmButtonText: '确定', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning' |
||||
|
}).then(() => { |
||||
|
this.notifyLoading = true; |
||||
|
|
||||
|
notifyEmptyPalletInbound({ |
||||
|
site: this.site, |
||||
|
palletId: this.palletCode, |
||||
|
transportFlag: 'N', |
||||
|
count: Number(this.count), |
||||
|
inboundPort: this.selectedInboundPort, |
||||
|
}).then(({ data }) => { |
||||
|
if (data && data.code === 0) { |
||||
|
this.$message.success('通知入库成功'); |
||||
|
// 清空页面重新扫描 - rqrq |
||||
|
this.resetPage(true); |
||||
|
} else { |
||||
|
this.$alert(data.msg || '通知入库失败', '错误', { |
||||
|
confirmButtonText: '确定' |
||||
|
}); |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
console.error('通知入库失败:', error); |
||||
|
this.$alert(error.message || '通知入库失败', '错误', { |
||||
|
confirmButtonText: '确定' |
||||
|
}); |
||||
|
}).finally(() => { |
||||
|
this.notifyLoading = false; |
||||
|
}); |
||||
|
}).catch(() => { |
||||
|
// 用户取消操作,不提交通知入库 - rqrq |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 入库并运输 - rqrq |
||||
|
handleInboundAndTransport() { |
||||
|
if (!this.currentPalletType) { |
||||
|
this.$alert('请先选择托盘类型', '提示', { |
||||
|
confirmButtonText: '确定' |
||||
|
}); |
||||
|
return; |
||||
|
} |
||||
|
// 右按钮“入库并运输”前置校验:栈板必须绑定area,否则当前位置禁止空托入库 - rqrq |
||||
|
if (!this.palletInfo.area || !String(this.palletInfo.area).trim()) { |
||||
|
this.$alert('当前位置不允许空托入库', '提示', { |
||||
|
confirmButtonText: '确定' |
||||
|
}); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
this.$confirm('确定要执行入库并运输操作吗?', '确认操作', { |
||||
|
confirmButtonText: '确定', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning' |
||||
|
}).then(() => { |
||||
|
this.transportLoading = true; |
||||
|
|
||||
|
notifyEmptyPalletInbound({ |
||||
|
site: this.site, |
||||
|
palletId: this.palletCode, |
||||
|
transportFlag: 'Y', |
||||
|
count: Number(this.count), |
||||
|
}).then(({ data }) => { |
||||
|
if (data && data.code === 0) { |
||||
|
this.$message.success('入库并运输成功'); |
||||
|
// 清空页面重新扫描 - rqrq |
||||
|
this.resetPage(true); |
||||
|
} else { |
||||
|
this.$alert(data.msg || '入库并运输失败', '错误', { |
||||
|
confirmButtonText: '确定' |
||||
|
}); |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
console.error('入库并运输失败:', error); |
||||
|
this.$alert(error.message || '入库并运输失败', '错误', { |
||||
|
confirmButtonText: '确定' |
||||
|
}); |
||||
|
}).finally(() => { |
||||
|
this.transportLoading = false; |
||||
|
}); |
||||
|
}).catch(() => { |
||||
|
// 用户取消操作 |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 重置页面并按需恢复扫码焦点,统一海安PDA页面生命周期清理行为 - rqrq |
||||
|
resetPage(needFocus) { |
||||
|
this.palletCode = ''; |
||||
|
this.palletScanned = false; |
||||
|
this.palletInfo = { |
||||
|
currentStationId: '', |
||||
|
currentStationCode: '', |
||||
|
palletType: '', |
||||
|
area: '' |
||||
|
}; |
||||
|
this.currentPalletType = ''; |
||||
|
this.palletTypeOptions = []; |
||||
|
this.count = 1; |
||||
|
this.inboundPortDialogVisible = false; |
||||
|
this.selectedInboundPort = ''; |
||||
|
|
||||
|
if (needFocus) { |
||||
|
this.$nextTick(() => { |
||||
|
if (this.$refs.palletInput) { |
||||
|
this.$refs.palletInput.focus(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.$nextTick(() => { |
||||
|
if (this.$refs.palletInput) { |
||||
|
this.$refs.palletInput.focus(); |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
activated() { |
||||
|
this.resetPage(true); |
||||
|
}, |
||||
|
beforeRouteLeave(to, from, next) { |
||||
|
this.resetPage(false); |
||||
|
next(); |
||||
|
} |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* 托盘信息区域 - rqrq */ |
||||
|
.pallet-info-section { |
||||
|
margin-top: 16px; |
||||
|
padding: 12px; |
||||
|
background: #f5f7fa; |
||||
|
border-radius: 6px; |
||||
|
} |
||||
|
|
||||
|
.info-row { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
padding: 8px 0; |
||||
|
border-bottom: 1px solid #e4e7ed; |
||||
|
} |
||||
|
|
||||
|
.info-row:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.info-label { |
||||
|
font-weight: bold; |
||||
|
color: #606266; |
||||
|
width: 120px; |
||||
|
flex-shrink: 0; |
||||
|
} |
||||
|
|
||||
|
.info-value { |
||||
|
color: #303133; |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
|
||||
|
.button-row { |
||||
|
margin-top: 12px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,596 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<div class="pda-container"> |
||||
|
<!-- 头部栏 - rqrq --> |
||||
|
<div class="header-bar"> |
||||
|
<div class="header-left" @click="handleBack"> |
||||
|
<i class="el-icon-arrow-left"></i> |
||||
|
<span>合托</span> |
||||
|
</div> |
||||
|
<div class="header-right" @click="goHome"> |
||||
|
首页 |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="table-body" style="max-height: 500px; overflow-y: auto;"> |
||||
|
<div class="main-content form-section"> |
||||
|
<!-- 第一行:来源托盘 - rqrq --> |
||||
|
<div class="input-group"> |
||||
|
<label class="input-label">来源托盘</label> |
||||
|
<div style="display: flex; gap: 8px;"> |
||||
|
<el-input |
||||
|
v-model="sourcePalletCode" |
||||
|
placeholder="请扫描来源托盘编码" |
||||
|
class="form-input" |
||||
|
style="flex: 0.7;" |
||||
|
clearable |
||||
|
@keyup.enter.native="handleSourcePalletScan" |
||||
|
@clear="handleSourceClear" |
||||
|
inputmode="none" |
||||
|
autocomplete="off" |
||||
|
autocorrect="off" |
||||
|
spellcheck="false" |
||||
|
ref="sourcePalletInput" |
||||
|
/> |
||||
|
<button |
||||
|
class="action-btn secondary" |
||||
|
style="flex: 0.3; margin: 0;" |
||||
|
:disabled="!sourceValidated" |
||||
|
@click="showSourceDetail" |
||||
|
> |
||||
|
查看明细 |
||||
|
</button> |
||||
|
</div> |
||||
|
<!-- 来源托盘信息显示 - rqrq --> |
||||
|
<div v-if="sourceValidated" class="pallet-info"> |
||||
|
<span class="info-item">类型: {{ sourcePalletType }}{{ sourceTypeDesc ? ' - ' + sourceTypeDesc : '' }}</span> |
||||
|
<span class="info-item">数量: {{ sourceDetailCount }}</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 第二行:目标托盘 - rqrq --> |
||||
|
<div class="input-group"> |
||||
|
<label class="input-label">目标托盘</label> |
||||
|
<div style="display: flex; gap: 8px;"> |
||||
|
<el-input |
||||
|
v-model="targetPalletCode" |
||||
|
placeholder="请扫描目标托盘编码" |
||||
|
class="form-input" |
||||
|
style="flex: 0.7;" |
||||
|
clearable |
||||
|
@keyup.enter.native="handleTargetPalletScan" |
||||
|
@clear="handleTargetClear" |
||||
|
inputmode="none" |
||||
|
autocomplete="off" |
||||
|
autocorrect="off" |
||||
|
spellcheck="false" |
||||
|
ref="targetPalletInput" |
||||
|
/> |
||||
|
<button |
||||
|
class="action-btn secondary" |
||||
|
style="flex: 0.3; margin: 0;" |
||||
|
:disabled="!targetValidated" |
||||
|
@click="showTargetDetail" |
||||
|
> |
||||
|
查看明细 |
||||
|
</button> |
||||
|
</div> |
||||
|
<!-- 目标托盘信息显示 - rqrq --> |
||||
|
<div v-if="targetValidated" class="pallet-info"> |
||||
|
<span class="info-item">类型: {{ targetPalletType }}{{ targetTypeDesc ? ' - ' + targetTypeDesc : '' }}</span> |
||||
|
<span class="info-item">数量: {{ targetDetailCount }}</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 第三行:操作按钮 - rqrq --> |
||||
|
<div class="input-group" style="margin-top: 20px;"> |
||||
|
<button |
||||
|
class="action-btn primary full-width" |
||||
|
:disabled="!canMerge || mergeLoading" |
||||
|
@click="handleMerge" |
||||
|
> |
||||
|
{{ mergeLoading ? '合托中...' : '合托' }} |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 查看明细弹窗 - rqrq --> |
||||
|
<el-dialog |
||||
|
:title="detailDialogTitle" |
||||
|
:visible.sync="detailDialogVisible" |
||||
|
width="90%" |
||||
|
:close-on-click-modal="false" |
||||
|
:show-close="false" |
||||
|
:modal="true" |
||||
|
:modal-append-to-body="true" |
||||
|
:append-to-body="true" |
||||
|
> |
||||
|
<div class="table-body" style="max-height: 300px; overflow-y: auto;"> |
||||
|
<div class="detail-table"> |
||||
|
<div class="table-header"> |
||||
|
<div class="col-seq">序号</div> |
||||
|
<div class="col-partno">物料</div> |
||||
|
<div class="col-serial">条码号</div> |
||||
|
</div> |
||||
|
<div |
||||
|
v-for="(detail, index) in currentDetailList" |
||||
|
:key="index" |
||||
|
class="table-row" |
||||
|
> |
||||
|
<div class="col-seq">{{ index + 1 }}</div> |
||||
|
<div class="col-partno">{{ detail.partNo }}</div> |
||||
|
<div class="col-serial">{{ detail.serialNo }}</div> |
||||
|
</div> |
||||
|
<!-- 暂无数据提示 - rqrq --> |
||||
|
<div v-if="currentDetailList.length === 0" class="table-row empty-row"> |
||||
|
<div class="empty-hint">暂无明细数据</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div slot="footer" class="dialog-footer"> |
||||
|
<button class="action-btn secondary" @click="detailDialogVisible=false">关闭</button> |
||||
|
</div> |
||||
|
</el-dialog> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { |
||||
|
checkPalletForMerge, |
||||
|
checkTargetPalletType, |
||||
|
getPalletDetailForMerge, |
||||
|
executePalletMerge |
||||
|
} from '../../../api/haianAutoWarehouse/palletMerge' |
||||
|
|
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
// 海安PDA前端site唯一来源为store用户对象,兼容旧缓存兜底 - rqrq |
||||
|
site: (this.$store && this.$store.state && this.$store.state.user && this.$store.state.user.site) || localStorage.getItem('site'), |
||||
|
|
||||
|
// 来源托盘 - rqrq |
||||
|
sourcePalletCode: '', |
||||
|
sourceValidated: false, |
||||
|
sourcePalletType: '', |
||||
|
sourceTypeDesc: '', |
||||
|
sourceDetailCount: 0, |
||||
|
sourceDetailList: [], |
||||
|
|
||||
|
// 目标托盘 - rqrq |
||||
|
targetPalletCode: '', |
||||
|
targetValidated: false, |
||||
|
targetPalletType: '', |
||||
|
targetTypeDesc: '', |
||||
|
targetDetailCount: 0, |
||||
|
targetDetailList: [], |
||||
|
|
||||
|
// 明细弹窗 - rqrq |
||||
|
detailDialogVisible: false, |
||||
|
detailDialogTitle: '', |
||||
|
currentDetailList: [], |
||||
|
|
||||
|
// 合托按钮loading - rqrq |
||||
|
mergeLoading: false |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
// 是否可以合托:两个托盘都校验通过 - rqrq |
||||
|
canMerge() { |
||||
|
return this.sourceValidated && this.targetValidated |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
// 返回上一页并清空页面状态,避免keep-alive导致脏数据残留 - rqrq |
||||
|
handleBack() { |
||||
|
this.resetPage(false) |
||||
|
this.$router.back() |
||||
|
}, |
||||
|
|
||||
|
// 回首页前清理状态,保证再次进入页面从干净态开始 - rqrq |
||||
|
goHome() { |
||||
|
this.resetPage(false) |
||||
|
this.$router.push({ path: '/' }) |
||||
|
}, |
||||
|
|
||||
|
// 处理扫描输入(去掉所有空格)- rqrq |
||||
|
processScanInput(input) { |
||||
|
if (input) { |
||||
|
// 去掉所有空格(包括中间的空格)- rqrq |
||||
|
return input.replace(/\s+/g, '') |
||||
|
} |
||||
|
return input |
||||
|
}, |
||||
|
|
||||
|
// 来源托盘扫描 - rqrq |
||||
|
handleSourcePalletScan() { |
||||
|
// 去掉所有空格 - rqrq |
||||
|
const scannedCode = this.processScanInput(this.sourcePalletCode) |
||||
|
if (!scannedCode) { |
||||
|
this.$alert('请输入来源托盘编码', '错误', { confirmButtonText: '确定' }) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 校验来源托盘(后台会处理LR并返回真实托盘号)- rqrq |
||||
|
checkPalletForMerge({ |
||||
|
site: this.site, |
||||
|
palletId: scannedCode |
||||
|
}).then(({ data }) => { |
||||
|
if (data && data.code === 0) { |
||||
|
// 使用后台返回的真实托盘号更新输入框 - rqrq |
||||
|
this.sourcePalletCode = data.palletId || scannedCode |
||||
|
this.sourceValidated = true |
||||
|
this.sourcePalletType = data.palletType || '' |
||||
|
this.sourceTypeDesc = data.typeDesc || '' |
||||
|
|
||||
|
// 查询来源托盘明细 - rqrq |
||||
|
this.loadSourceDetail() |
||||
|
|
||||
|
// 聚焦到目标托盘输入框 - rqrq |
||||
|
this.$nextTick(() => { |
||||
|
if (this.$refs.targetPalletInput) { |
||||
|
this.$refs.targetPalletInput.focus() |
||||
|
} |
||||
|
}) |
||||
|
} else { |
||||
|
this.handleSourceError(data.msg || '来源托盘校验失败') |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
console.error('来源托盘校验失败:', error) |
||||
|
this.handleSourceError(error.message || '来源托盘校验失败') |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
// 加载来源托盘明细 - rqrq |
||||
|
loadSourceDetail() { |
||||
|
getPalletDetailForMerge({ |
||||
|
site: this.site, |
||||
|
palletId: this.sourcePalletCode |
||||
|
}).then(({ data }) => { |
||||
|
if (data && data.code === 0) { |
||||
|
this.sourceDetailList = data.rows || [] |
||||
|
this.sourceDetailCount = this.sourceDetailList.length |
||||
|
} else { |
||||
|
this.sourceDetailList = [] |
||||
|
this.sourceDetailCount = 0 |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
console.error('查询来源托盘明细失败:', error) |
||||
|
this.sourceDetailList = [] |
||||
|
this.sourceDetailCount = 0 |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
// 来源托盘校验失败处理 - rqrq |
||||
|
handleSourceError(errorMsg) { |
||||
|
if (errorMsg.length > 100) { |
||||
|
errorMsg = errorMsg.substring(0, 100) + '...' |
||||
|
} |
||||
|
this.$alert(errorMsg, '错误', { |
||||
|
confirmButtonText: '确定', |
||||
|
callback: () => { |
||||
|
this.handleSourceClear() |
||||
|
this.$nextTick(() => { |
||||
|
if (this.$refs.sourcePalletInput) { |
||||
|
this.$refs.sourcePalletInput.focus() |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
// 清空来源托盘 - rqrq |
||||
|
handleSourceClear() { |
||||
|
this.sourcePalletCode = '' |
||||
|
this.sourceValidated = false |
||||
|
this.sourcePalletType = '' |
||||
|
this.sourceTypeDesc = '' |
||||
|
this.sourceDetailCount = 0 |
||||
|
this.sourceDetailList = [] |
||||
|
}, |
||||
|
|
||||
|
// 目标托盘扫描 - rqrq |
||||
|
handleTargetPalletScan() { |
||||
|
// 去掉所有空格 - rqrq |
||||
|
const scannedCode = this.processScanInput(this.targetPalletCode) |
||||
|
if (!scannedCode) { |
||||
|
this.$alert('请输入目标托盘编码', '错误', { confirmButtonText: '确定' }) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 先校验目标托盘是否存在(后台会处理LR并返回真实托盘号)- rqrq |
||||
|
checkPalletForMerge({ |
||||
|
site: this.site, |
||||
|
palletId: scannedCode |
||||
|
}).then(({ data }) => { |
||||
|
if (data && data.code === 0) { |
||||
|
// 使用后台返回的真实托盘号 - rqrq |
||||
|
const realPalletId = data.palletId || scannedCode |
||||
|
const palletType = data.palletType || '' |
||||
|
const typeDesc = data.typeDesc || '' |
||||
|
|
||||
|
// 不能和来源托盘相同(使用真实托盘号比较)- rqrq |
||||
|
if (this.sourcePalletCode && realPalletId === this.sourcePalletCode) { |
||||
|
this.$alert('目标托盘不能与来源托盘相同', '错误', { confirmButtonText: '确定' }) |
||||
|
this.targetPalletCode = '' |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 再校验目标托盘是否为混装托盘(wcs_auto_sort = 'N')- rqrq |
||||
|
checkTargetPalletType({ |
||||
|
site: this.site, |
||||
|
palletType: palletType |
||||
|
}).then(({ data: typeData }) => { |
||||
|
if (typeData && typeData.code === 0) { |
||||
|
// 更新输入框为真实托盘号 - rqrq |
||||
|
this.targetPalletCode = realPalletId |
||||
|
this.targetValidated = true |
||||
|
this.targetPalletType = palletType |
||||
|
this.targetTypeDesc = typeDesc |
||||
|
|
||||
|
// 查询目标托盘明细 - rqrq |
||||
|
this.loadTargetDetail() |
||||
|
|
||||
|
this.$message.success('目标托盘校验通过') |
||||
|
} else { |
||||
|
this.handleTargetError(typeData.msg || '目标栈板必须是混装托盘才能合托') |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
console.error('目标托盘类型校验失败:', error) |
||||
|
this.handleTargetError('目标栈板必须是混装托盘才能合托') |
||||
|
}) |
||||
|
} else { |
||||
|
this.handleTargetError(data.msg || '目标托盘校验失败') |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
console.error('目标托盘校验失败:', error) |
||||
|
this.handleTargetError(error.message || '目标托盘校验失败') |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
// 加载目标托盘明细 - rqrq |
||||
|
loadTargetDetail() { |
||||
|
getPalletDetailForMerge({ |
||||
|
site: this.site, |
||||
|
palletId: this.targetPalletCode |
||||
|
}).then(({ data }) => { |
||||
|
if (data && data.code === 0) { |
||||
|
this.targetDetailList = data.rows || [] |
||||
|
this.targetDetailCount = this.targetDetailList.length |
||||
|
} else { |
||||
|
this.targetDetailList = [] |
||||
|
this.targetDetailCount = 0 |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
console.error('查询目标托盘明细失败:', error) |
||||
|
this.targetDetailList = [] |
||||
|
this.targetDetailCount = 0 |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
// 目标托盘校验失败处理 - rqrq |
||||
|
handleTargetError(errorMsg) { |
||||
|
if (errorMsg.length > 100) { |
||||
|
errorMsg = errorMsg.substring(0, 100) + '...' |
||||
|
} |
||||
|
this.$alert(errorMsg, '错误', { |
||||
|
confirmButtonText: '确定', |
||||
|
callback: () => { |
||||
|
this.handleTargetClear() |
||||
|
this.$nextTick(() => { |
||||
|
if (this.$refs.targetPalletInput) { |
||||
|
this.$refs.targetPalletInput.focus() |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
// 清空目标托盘 - rqrq |
||||
|
handleTargetClear() { |
||||
|
this.targetPalletCode = '' |
||||
|
this.targetValidated = false |
||||
|
this.targetPalletType = '' |
||||
|
this.targetTypeDesc = '' |
||||
|
this.targetDetailCount = 0 |
||||
|
this.targetDetailList = [] |
||||
|
}, |
||||
|
|
||||
|
// 查看来源托盘明细 - rqrq |
||||
|
showSourceDetail() { |
||||
|
this.detailDialogTitle = `来源托盘明细 (${this.sourcePalletCode})` |
||||
|
this.currentDetailList = this.sourceDetailList |
||||
|
this.detailDialogVisible = true |
||||
|
}, |
||||
|
|
||||
|
// 查看目标托盘明细 - rqrq |
||||
|
showTargetDetail() { |
||||
|
this.detailDialogTitle = `目标托盘明细 (${this.targetPalletCode})` |
||||
|
this.currentDetailList = this.targetDetailList |
||||
|
this.detailDialogVisible = true |
||||
|
}, |
||||
|
|
||||
|
// 执行合托 - rqrq |
||||
|
handleMerge() { |
||||
|
if (!this.canMerge) { |
||||
|
this.$alert('请先扫描来源托盘和目标托盘', '错误', { confirmButtonText: '确定' }) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 确认对话框 - rqrq |
||||
|
this.$confirm( |
||||
|
`确定将来源托盘【${this.sourcePalletCode}】的${this.sourceDetailCount}条物料转移到目标托盘【${this.targetPalletCode}】吗?`, |
||||
|
'合托确认', |
||||
|
{ |
||||
|
confirmButtonText: '确定', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning' |
||||
|
} |
||||
|
).then(() => { |
||||
|
this.doMerge() |
||||
|
}).catch(() => { |
||||
|
// 用户取消 - rqrq |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
// 执行合托操作 - rqrq |
||||
|
doMerge() { |
||||
|
this.mergeLoading = true |
||||
|
|
||||
|
executePalletMerge({ |
||||
|
site: this.site, |
||||
|
sourcePalletId: this.sourcePalletCode, |
||||
|
targetPalletId: this.targetPalletCode |
||||
|
}).then(({ data }) => { |
||||
|
if (data && data.code === 0) { |
||||
|
this.$message.success('合托成功') |
||||
|
// 重置页面 - rqrq |
||||
|
this.resetPage(true) |
||||
|
} else { |
||||
|
this.$alert(data.msg || '合托失败', '错误', { |
||||
|
confirmButtonText: '确定' |
||||
|
}) |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
console.error('合托失败:', error) |
||||
|
this.$alert(error.message || '合托失败', '错误', { |
||||
|
confirmButtonText: '确定' |
||||
|
}) |
||||
|
}).finally(() => { |
||||
|
this.mergeLoading = false |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
// 重置页面并按需恢复扫码焦点,统一海安PDA页面生命周期清理行为 - rqrq |
||||
|
resetPage(needFocus) { |
||||
|
this.handleSourceClear() |
||||
|
this.handleTargetClear() |
||||
|
this.detailDialogVisible = false |
||||
|
this.detailDialogTitle = '' |
||||
|
this.currentDetailList = [] |
||||
|
|
||||
|
if (needFocus) { |
||||
|
this.$nextTick(() => { |
||||
|
if (this.$refs.sourcePalletInput) { |
||||
|
this.$refs.sourcePalletInput.focus() |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
// 页面加载后聚焦到来源托盘输入框 - rqrq |
||||
|
this.$nextTick(() => { |
||||
|
if (this.$refs.sourcePalletInput) { |
||||
|
this.$refs.sourcePalletInput.focus() |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
activated() { |
||||
|
this.resetPage(true) |
||||
|
}, |
||||
|
beforeRouteLeave(to, from, next) { |
||||
|
this.resetPage(false) |
||||
|
next() |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* 托盘信息显示 - rqrq */ |
||||
|
.pallet-info { |
||||
|
display: flex; |
||||
|
gap: 16px; |
||||
|
margin-top: 8px; |
||||
|
padding: 8px 12px; |
||||
|
background-color: #f0f9eb; |
||||
|
border-radius: 4px; |
||||
|
font-size: 13px; |
||||
|
color: #67c23a; |
||||
|
} |
||||
|
|
||||
|
.info-item { |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
/* 全宽按钮 - rqrq */ |
||||
|
.full-width { |
||||
|
width: 100%; |
||||
|
height: 48px; |
||||
|
font-size: 18px; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
/* 表格样式 - rqrq */ |
||||
|
.detail-table { |
||||
|
background: white; |
||||
|
border-radius: 6px; |
||||
|
overflow: hidden; |
||||
|
border: 1px solid #e0e0e0; |
||||
|
} |
||||
|
|
||||
|
.table-header, |
||||
|
.table-row { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
padding: 8px; |
||||
|
border-bottom: 1px solid #e0e0e0; |
||||
|
} |
||||
|
|
||||
|
.table-header { |
||||
|
background: #f5f5f5; |
||||
|
font-weight: bold; |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
|
||||
|
.table-row { |
||||
|
font-size: 13px; |
||||
|
} |
||||
|
|
||||
|
.table-row:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.col-seq { |
||||
|
flex: 0.5; |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
.col-partno { |
||||
|
flex: 2; |
||||
|
text-align: center; |
||||
|
word-break: break-all; |
||||
|
} |
||||
|
|
||||
|
.col-serial { |
||||
|
flex: 3; |
||||
|
text-align: center; |
||||
|
word-break: break-all; |
||||
|
} |
||||
|
|
||||
|
/* 空数据提示 - rqrq */ |
||||
|
.empty-hint { |
||||
|
text-align: center; |
||||
|
color: #999; |
||||
|
padding: 20px; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.empty-row { |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
/* 弹窗底部按钮 - rqrq */ |
||||
|
.dialog-footer { |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
/* 按钮禁用状态样式 - rqrq */ |
||||
|
.action-btn:disabled { |
||||
|
opacity: 0.6; |
||||
|
cursor: not-allowed; |
||||
|
background-color: #ccc !important; |
||||
|
border-color: #ccc !important; |
||||
|
} |
||||
|
</style> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue