4 changed files with 601 additions and 0 deletions
-
16src/api/automatedWarehouse/palletMerge.js
-
1src/router/index.js
-
6src/views/main.vue
-
578src/views/modules/automatedWarehouse/palletMerge.vue
@ -0,0 +1,16 @@ |
|||||
|
import { createAPI } from "@/utils/httpRequest.js"; |
||||
|
|
||||
|
// ========== 合托相关 ========== - rqrq
|
||||
|
|
||||
|
// 校验栈板(通用,用于来源/目标托盘)- rqrq
|
||||
|
export const checkPalletForMerge = data => createAPI(`/wcsIntegration/checkPalletForMerge`, 'post', data) |
||||
|
|
||||
|
// 校验目标栈板是否为混装托盘 - rqrq
|
||||
|
export const checkTargetPalletType = data => createAPI(`/wcsIntegration/checkTargetPalletType`, 'post', data) |
||||
|
|
||||
|
// 查询栈板明细(根据site+palletId查询pallet_detail)- rqrq
|
||||
|
export const getPalletDetailForMerge = data => createAPI(`/wcsIntegration/getPalletDetailForMerge`, 'post', data) |
||||
|
|
||||
|
// 执行合托操作(将来源栈板的物料转移到目标栈板)- rqrq
|
||||
|
export const executePalletMerge = data => createAPI(`/wcsIntegration/executePalletMerge`, 'post', data) |
||||
|
|
||||
@ -0,0 +1,578 @@ |
|||||
|
<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="$router.push({ path: '/' })"> |
||||
|
首页 |
||||
|
</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/automatedWarehouse/palletMerge' |
||||
|
|
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
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: { |
||||
|
// 返回上一页 - rqrq |
||||
|
handleBack() { |
||||
|
this.$router.back() |
||||
|
}, |
||||
|
|
||||
|
// 处理托盘编码(去掉末尾R/L)- rqrq |
||||
|
processPalletCode(palletCode) { |
||||
|
if (palletCode && palletCode.length > 0) { |
||||
|
const lastChar = palletCode.charAt(palletCode.length - 1) |
||||
|
if (lastChar === 'R' || lastChar === 'L' || lastChar === 'r' || lastChar === 'l') { |
||||
|
return palletCode.substring(0, palletCode.length - 1) |
||||
|
} |
||||
|
} |
||||
|
return palletCode |
||||
|
}, |
||||
|
|
||||
|
// 来源托盘扫描 - rqrq |
||||
|
handleSourcePalletScan() { |
||||
|
if (!this.sourcePalletCode.trim()) { |
||||
|
this.$message.error('请输入来源托盘编码') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 处理托盘编码 - rqrq |
||||
|
const processedCode = this.processPalletCode(this.sourcePalletCode.trim()) |
||||
|
this.sourcePalletCode = processedCode |
||||
|
|
||||
|
// 校验来源托盘 - rqrq |
||||
|
checkPalletForMerge({ |
||||
|
site: this.site, |
||||
|
palletId: processedCode |
||||
|
}).then(({ data }) => { |
||||
|
if (data && data.code === 0) { |
||||
|
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() { |
||||
|
if (!this.targetPalletCode.trim()) { |
||||
|
this.$message.error('请输入目标托盘编码') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 处理托盘编码 - rqrq |
||||
|
const processedCode = this.processPalletCode(this.targetPalletCode.trim()) |
||||
|
this.targetPalletCode = processedCode |
||||
|
|
||||
|
// 不能和来源托盘相同 - rqrq |
||||
|
if (this.sourcePalletCode && processedCode === this.sourcePalletCode) { |
||||
|
this.$message.error('目标托盘不能与来源托盘相同') |
||||
|
this.targetPalletCode = '' |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 先校验目标托盘是否存在 - rqrq |
||||
|
checkPalletForMerge({ |
||||
|
site: this.site, |
||||
|
palletId: processedCode |
||||
|
}).then(({ data }) => { |
||||
|
if (data && data.code === 0) { |
||||
|
const palletType = data.palletType || '' |
||||
|
const typeDesc = data.typeDesc || '' |
||||
|
|
||||
|
// 再校验目标托盘是否为混装托盘(wcs_auto_sort = 'N')- rqrq |
||||
|
checkTargetPalletType({ |
||||
|
site: this.site, |
||||
|
palletType: palletType |
||||
|
}).then(({ data: typeData }) => { |
||||
|
if (typeData && typeData.code === 0) { |
||||
|
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.$message.error('请先扫描来源托盘和目标托盘') |
||||
|
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() |
||||
|
} else { |
||||
|
this.$alert(data.msg || '合托失败', '错误', { |
||||
|
confirmButtonText: '确定' |
||||
|
}) |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
console.error('合托失败:', error) |
||||
|
this.$alert(error.message || '合托失败', '错误', { |
||||
|
confirmButtonText: '确定' |
||||
|
}) |
||||
|
}).finally(() => { |
||||
|
this.mergeLoading = false |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
// 重置页面 - rqrq |
||||
|
resetPage() { |
||||
|
this.handleSourceClear() |
||||
|
this.handleTargetClear() |
||||
|
|
||||
|
// 聚焦到来源托盘输入框 - rqrq |
||||
|
this.$nextTick(() => { |
||||
|
if (this.$refs.sourcePalletInput) { |
||||
|
this.$refs.sourcePalletInput.focus() |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
// 页面加载后聚焦到来源托盘输入框 - rqrq |
||||
|
this.$nextTick(() => { |
||||
|
if (this.$refs.sourcePalletInput) { |
||||
|
this.$refs.sourcePalletInput.focus() |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
</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