1 changed files with 653 additions and 0 deletions
@ -0,0 +1,653 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<div class="pda-container"> |
||||
|
<!-- 头部栏 --> |
||||
|
<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: calc(100vh - 60px); overflow-y: auto;"> |
||||
|
<div class="main-content form-section"> |
||||
|
<!-- 标签扫描输入框 --> |
||||
|
<div class="input-group"> |
||||
|
<el-input |
||||
|
v-model="labelCode" |
||||
|
placeholder="请扫描标签编码" |
||||
|
class="form-input" |
||||
|
clearable |
||||
|
inputmode="none" |
||||
|
autocomplete="off" |
||||
|
autocorrect="off" |
||||
|
spellcheck="false" |
||||
|
@keyup.enter.native="handleLabelScan" |
||||
|
ref="labelInput" |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<!-- WMS标签信息显示 (扫描后显示) --> |
||||
|
<div v-if="labelInfo" class="info-section"> |
||||
|
<div class="info-title"> |
||||
|
<i class="el-icon-document"></i> |
||||
|
<span>WMS标签信息</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="info-row"> |
||||
|
<span class="info-label">标签编码:</span> |
||||
|
<span class="info-value">{{ labelInfo.unitId || '-' }}</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="info-row"> |
||||
|
<span class="info-label">物料编码:</span> |
||||
|
<span class="info-value">{{ labelInfo.partNo || '-' }}</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="info-row"> |
||||
|
<span class="info-label">批次号:</span> |
||||
|
<span class="info-value">{{ labelInfo.batchNo || '-' }}</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="info-row"> |
||||
|
<span class="info-label">数量:</span> |
||||
|
<span class="info-value">{{ labelInfo.qty || '0' }}</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="info-row"> |
||||
|
<span class="info-label">库位:</span> |
||||
|
<span class="info-value highlight">{{ labelInfo.locationId || '-' }}</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="info-row"> |
||||
|
<span class="info-label">WDR:</span> |
||||
|
<span class="info-value highlight">{{ labelInfo.wdr || '-' }}</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="info-row"> |
||||
|
<span class="info-label">失效日期:</span> |
||||
|
<span class="info-value highlight">{{ formatDate(labelInfo.expiredDate) }}</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="info-row"> |
||||
|
<span class="info-label">版本号:</span> |
||||
|
<span class="info-value highlight">{{ labelInfo.engChgLevel || '-' }}</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="info-row"> |
||||
|
<span class="info-label">可用控制:</span> |
||||
|
<span class="info-value highlight">{{ labelInfo.availabilityControlId || '-' }}</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- IFS库存列表 (扫描后显示) --> |
||||
|
<div v-if="labelInfo && matchedIfsList && matchedIfsList.length > 0" class="ifs-section"> |
||||
|
<div class="info-title"> |
||||
|
<i class="el-icon-s-data"></i> |
||||
|
<span>IFS库存列表(同物料+同批次)</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="ifs-list"> |
||||
|
<div |
||||
|
v-for="(item, index) in matchedIfsList" |
||||
|
:key="index" |
||||
|
class="ifs-item" |
||||
|
:class="{ 'selected': selectedIndex === index }" |
||||
|
@click="selectIfsItem(index)" |
||||
|
> |
||||
|
<div class="ifs-item-header"> |
||||
|
<span class="ifs-index">#{{ index + 1 }}</span> |
||||
|
<span v-if="selectedIndex === index" class="ifs-selected-tag">已选择</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="ifs-item-row"> |
||||
|
<span class="ifs-label">库位:</span> |
||||
|
<span class="ifs-value">{{ item.LocationNo || item.locationNo || '-' }}</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="ifs-item-row"> |
||||
|
<span class="ifs-label">WDR:</span> |
||||
|
<span class="ifs-value">{{ item.WaivDevRejNo || item.waivDevRejNo || '-' }}</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="ifs-item-row"> |
||||
|
<span class="ifs-label">失效日期:</span> |
||||
|
<span class="ifs-value">{{ item.ExpirationDate || item.expirationDate || '-' }}</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="ifs-item-row"> |
||||
|
<span class="ifs-label">版本号:</span> |
||||
|
<span class="ifs-value">{{ item.EngChgLevel || item.engChgLevel || '-' }}</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="ifs-item-row"> |
||||
|
<span class="ifs-label">可用控制:</span> |
||||
|
<span class="ifs-value">{{ item.AvailabilityControlId || item.availabilityControlId || '-' }}</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="ifs-item-row"> |
||||
|
<span class="ifs-label">在库数量:</span> |
||||
|
<span class="ifs-value qty">{{ item.QtyOnhand || item.qtyOnhand || '0' }}</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 无匹配数据提示 --> |
||||
|
<div v-if="labelInfo && (!matchedIfsList || matchedIfsList.length === 0)" class="no-data-section"> |
||||
|
<i class="el-icon-warning-outline"></i> |
||||
|
<span>IFS中未找到匹配的库存记录</span> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 底部操作按钮 --> |
||||
|
<div v-if="labelInfo" class="bottom-actions"> |
||||
|
<button |
||||
|
class="action-btn primary" |
||||
|
@click="handleCalibrate" |
||||
|
:disabled="calibrateLoading || selectedIndex === null" |
||||
|
> |
||||
|
{{ calibrateLoading ? '纠正中...' : '纠正' }} |
||||
|
</button> |
||||
|
<button |
||||
|
class="action-btn secondary" |
||||
|
@click="clearData" |
||||
|
:disabled="calibrateLoading" |
||||
|
> |
||||
|
清空 |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { queryLabelCalibration, calibrateLabel } from '@/api/inventory/label' |
||||
|
|
||||
|
export default { |
||||
|
name: 'LabelCalibration', |
||||
|
|
||||
|
data() { |
||||
|
return { |
||||
|
site: localStorage.getItem('site'), |
||||
|
labelCode: '', |
||||
|
labelInfo: null, |
||||
|
matchedIfsList: [], |
||||
|
selectedIndex: null, |
||||
|
loading: false, |
||||
|
calibrateLoading: false |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
/** |
||||
|
* 返回上一页 - rqrq |
||||
|
*/ |
||||
|
handleBack() { |
||||
|
this.$router.back() |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 处理标签扫描 - rqrq |
||||
|
*/ |
||||
|
handleLabelScan() { |
||||
|
if (!this.labelCode.trim()) { |
||||
|
this.$message.error('请扫描有效的标签编码') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
this.loading = true |
||||
|
this.labelInfo = null |
||||
|
this.matchedIfsList = [] |
||||
|
this.selectedIndex = null |
||||
|
|
||||
|
queryLabelCalibration({ |
||||
|
site: this.site, |
||||
|
unitId: this.labelCode.trim() |
||||
|
}).then(({ data }) => { |
||||
|
this.loading = false |
||||
|
|
||||
|
if (data && data.code === 0) { |
||||
|
this.labelInfo = data.row |
||||
|
this.matchedIfsList = data.row.matchedIfsList || [] |
||||
|
this.$message.success('查询成功') |
||||
|
} else { |
||||
|
this.$message.error(data.msg || '查询失败') |
||||
|
this.labelCode = '' |
||||
|
this.focusLabelInput() |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.loading = false |
||||
|
console.error('查询标签失败:', error) |
||||
|
this.$message.error('查询异常') |
||||
|
this.labelCode = '' |
||||
|
this.focusLabelInput() |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 选择IFS库存项 - rqrq |
||||
|
*/ |
||||
|
selectIfsItem(index) { |
||||
|
if (this.selectedIndex === index) { |
||||
|
this.selectedIndex = null |
||||
|
} else { |
||||
|
this.selectedIndex = index |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 执行标签校准纠正 - rqrq |
||||
|
*/ |
||||
|
handleCalibrate() { |
||||
|
if (this.selectedIndex === null) { |
||||
|
this.$message.warning('请先选择要纠正成的IFS库存') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
const selectedItem = this.matchedIfsList[this.selectedIndex] |
||||
|
|
||||
|
this.$confirm( |
||||
|
`确定要将标签校准为以下信息吗?<br/><br/>` + |
||||
|
`库位: ${selectedItem.LocationNo || selectedItem.locationNo || '-'}<br/>` + |
||||
|
`WDR: ${selectedItem.WaivDevRejNo || selectedItem.waivDevRejNo || '-'}<br/>` + |
||||
|
`失效日期: ${selectedItem.ExpirationDate || selectedItem.expirationDate || '-'}<br/>` + |
||||
|
`版本号: ${selectedItem.EngChgLevel || selectedItem.engChgLevel || '-'}`, |
||||
|
'确认纠正', |
||||
|
{ |
||||
|
confirmButtonText: '确定', |
||||
|
cancelButtonText: '取消', |
||||
|
dangerouslyUseHTMLString: true, |
||||
|
type: 'warning' |
||||
|
} |
||||
|
).then(() => { |
||||
|
this.doCalibrate(selectedItem) |
||||
|
}).catch(() => { |
||||
|
// 用户取消 |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 执行纠正操作 - rqrq |
||||
|
*/ |
||||
|
doCalibrate(selectedItem) { |
||||
|
this.calibrateLoading = true |
||||
|
|
||||
|
calibrateLabel({ |
||||
|
site: this.site, |
||||
|
unitId: this.labelInfo.unitId, |
||||
|
username: localStorage.getItem('userName'), |
||||
|
selectedLocationNo: selectedItem.LocationNo || selectedItem.locationNo, |
||||
|
selectedWaivDevRejNo: selectedItem.WaivDevRejNo || selectedItem.waivDevRejNo, |
||||
|
selectedExpirationDate: selectedItem.ExpirationDate || selectedItem.expirationDate, |
||||
|
selectedEngChgLevel: selectedItem.EngChgLevel || selectedItem.engChgLevel, |
||||
|
selectedAvailabilityControlId: selectedItem.AvailabilityControlId || selectedItem.availabilityControlId |
||||
|
}).then(({ data }) => { |
||||
|
this.calibrateLoading = false |
||||
|
|
||||
|
if (data && data.code === 0) { |
||||
|
this.$message.success('标签校准成功!') |
||||
|
// 刷新标签信息 |
||||
|
this.labelInfo = data.row |
||||
|
this.selectedIndex = null |
||||
|
// 重新查询匹配的IFS库存 |
||||
|
this.handleLabelScan() |
||||
|
} else { |
||||
|
this.$message.error(data.msg || '校准失败') |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.calibrateLoading = false |
||||
|
console.error('标签校准失败:', error) |
||||
|
this.$message.error('校准异常: ' + (error.message || error)) |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 清空数据 - rqrq |
||||
|
*/ |
||||
|
clearData() { |
||||
|
this.labelCode = '' |
||||
|
this.labelInfo = null |
||||
|
this.matchedIfsList = [] |
||||
|
this.selectedIndex = null |
||||
|
this.focusLabelInput() |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 聚焦标签输入框 - rqrq |
||||
|
*/ |
||||
|
focusLabelInput() { |
||||
|
this.$nextTick(() => { |
||||
|
if (this.$refs.labelInput) { |
||||
|
this.$refs.labelInput.focus() |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 格式化日期 - rqrq |
||||
|
*/ |
||||
|
formatDate(date) { |
||||
|
if (!date) return '-' |
||||
|
|
||||
|
const d = new Date(date) |
||||
|
const year = d.getFullYear() |
||||
|
const month = String(d.getMonth() + 1).padStart(2, '0') |
||||
|
const day = String(d.getDate()).padStart(2, '0') |
||||
|
|
||||
|
return `${year}-${month}-${day}` |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
mounted() { |
||||
|
// 页面加载后自动聚焦标签输入框 - rqrq |
||||
|
this.focusLabelInput() |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.input-group { |
||||
|
margin-bottom: 12px !important; |
||||
|
} |
||||
|
|
||||
|
/* PDA容器样式 - rqrq */ |
||||
|
.pda-container { |
||||
|
width: 100vw; |
||||
|
height: 100vh; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
background: #f5f5f5; |
||||
|
font-family: 'Arial', sans-serif; |
||||
|
} |
||||
|
|
||||
|
/* 头部栏样式 - rqrq */ |
||||
|
.header-bar { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding: 8px 16px; |
||||
|
background: #17B3A3; |
||||
|
color: white; |
||||
|
height: 40px; |
||||
|
min-height: 40px; |
||||
|
max-height: 40px; |
||||
|
} |
||||
|
|
||||
|
.header-left { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
.header-left i { |
||||
|
margin-right: 8px; |
||||
|
font-size: 18px; |
||||
|
} |
||||
|
|
||||
|
.header-left span { |
||||
|
font-size: 16px; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
|
||||
|
.header-right { |
||||
|
cursor: pointer; |
||||
|
font-size: 14px; |
||||
|
padding: 4px 8px; |
||||
|
border-radius: 4px; |
||||
|
} |
||||
|
|
||||
|
/* 主要内容区 - rqrq */ |
||||
|
.table-body { |
||||
|
flex: 1; |
||||
|
overflow-y: auto; |
||||
|
} |
||||
|
|
||||
|
.main-content { |
||||
|
padding: 16px; |
||||
|
} |
||||
|
|
||||
|
/* 输入框样式 - rqrq */ |
||||
|
.form-input { |
||||
|
width: 100%; |
||||
|
height: 44px; |
||||
|
} |
||||
|
|
||||
|
/* 信息展示区 - rqrq */ |
||||
|
.info-section { |
||||
|
background: white; |
||||
|
border-radius: 8px; |
||||
|
padding: 16px; |
||||
|
margin-bottom: 12px; |
||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.info-title { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
font-size: 16px; |
||||
|
font-weight: bold; |
||||
|
color: #17B3A3; |
||||
|
margin-bottom: 12px; |
||||
|
padding-bottom: 8px; |
||||
|
border-bottom: 2px solid #17B3A3; |
||||
|
} |
||||
|
|
||||
|
.info-title i { |
||||
|
margin-right: 8px; |
||||
|
font-size: 18px; |
||||
|
} |
||||
|
|
||||
|
.info-row { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: flex-start; |
||||
|
padding: 8px 0; |
||||
|
border-bottom: 1px solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.info-row:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.info-label { |
||||
|
font-size: 14px; |
||||
|
color: #666; |
||||
|
min-width: 80px; |
||||
|
flex-shrink: 0; |
||||
|
} |
||||
|
|
||||
|
.info-value { |
||||
|
font-size: 14px; |
||||
|
font-weight: 500; |
||||
|
color: #333; |
||||
|
flex: 1; |
||||
|
text-align: right; |
||||
|
word-break: break-all; |
||||
|
} |
||||
|
|
||||
|
.info-value.highlight { |
||||
|
color: #E6A23C; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
/* IFS库存列表区 - rqrq */ |
||||
|
.ifs-section { |
||||
|
background: white; |
||||
|
border-radius: 8px; |
||||
|
padding: 16px; |
||||
|
margin-bottom: 12px; |
||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.ifs-list { |
||||
|
max-height: 300px; |
||||
|
overflow-y: auto; |
||||
|
} |
||||
|
|
||||
|
.ifs-item { |
||||
|
background: #fafafa; |
||||
|
border: 2px solid #e0e0e0; |
||||
|
border-radius: 8px; |
||||
|
padding: 12px; |
||||
|
margin-bottom: 10px; |
||||
|
cursor: pointer; |
||||
|
transition: all 0.2s; |
||||
|
} |
||||
|
|
||||
|
.ifs-item:hover { |
||||
|
border-color: #17B3A3; |
||||
|
background: #f0faf9; |
||||
|
} |
||||
|
|
||||
|
.ifs-item.selected { |
||||
|
border-color: #17B3A3; |
||||
|
background: #e6f7f5; |
||||
|
box-shadow: 0 2px 8px rgba(23, 179, 163, 0.3); |
||||
|
} |
||||
|
|
||||
|
.ifs-item-header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
margin-bottom: 8px; |
||||
|
padding-bottom: 8px; |
||||
|
border-bottom: 1px dashed #ddd; |
||||
|
} |
||||
|
|
||||
|
.ifs-index { |
||||
|
font-size: 14px; |
||||
|
font-weight: bold; |
||||
|
color: #666; |
||||
|
} |
||||
|
|
||||
|
.ifs-selected-tag { |
||||
|
background: #17B3A3; |
||||
|
color: white; |
||||
|
padding: 2px 8px; |
||||
|
border-radius: 4px; |
||||
|
font-size: 12px; |
||||
|
} |
||||
|
|
||||
|
.ifs-item-row { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
padding: 4px 0; |
||||
|
} |
||||
|
|
||||
|
.ifs-label { |
||||
|
font-size: 13px; |
||||
|
color: #888; |
||||
|
} |
||||
|
|
||||
|
.ifs-value { |
||||
|
font-size: 13px; |
||||
|
color: #333; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
|
||||
|
.ifs-value.qty { |
||||
|
color: #67C23A; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
/* 无数据提示 - rqrq */ |
||||
|
.no-data-section { |
||||
|
background: #fff8e6; |
||||
|
border: 1px solid #ffc107; |
||||
|
border-radius: 8px; |
||||
|
padding: 20px; |
||||
|
margin-bottom: 12px; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
color: #856404; |
||||
|
} |
||||
|
|
||||
|
.no-data-section i { |
||||
|
font-size: 20px; |
||||
|
margin-right: 8px; |
||||
|
} |
||||
|
|
||||
|
/* 底部按钮区 - rqrq */ |
||||
|
.bottom-actions { |
||||
|
display: flex; |
||||
|
gap: 12px; |
||||
|
padding-top: 16px; |
||||
|
} |
||||
|
|
||||
|
.action-btn { |
||||
|
flex: 1; |
||||
|
padding: 14px 24px; |
||||
|
border: none; |
||||
|
border-radius: 6px; |
||||
|
font-size: 16px; |
||||
|
font-weight: 500; |
||||
|
cursor: pointer; |
||||
|
transition: all 0.2s; |
||||
|
min-height: 48px; |
||||
|
} |
||||
|
|
||||
|
.action-btn.primary { |
||||
|
background: #17B3A3; |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
.action-btn.primary:hover:not(:disabled) { |
||||
|
background: #15a394; |
||||
|
} |
||||
|
|
||||
|
.action-btn.primary:disabled { |
||||
|
background: #ccc; |
||||
|
cursor: not-allowed; |
||||
|
} |
||||
|
|
||||
|
.action-btn.secondary { |
||||
|
background: #f5f5f5; |
||||
|
color: #333; |
||||
|
border: 1px solid #ddd; |
||||
|
} |
||||
|
|
||||
|
.action-btn.secondary:hover:not(:disabled) { |
||||
|
background: #e8e8e8; |
||||
|
} |
||||
|
|
||||
|
.action-btn.secondary:disabled { |
||||
|
background: #f0f0f0; |
||||
|
color: #999; |
||||
|
cursor: not-allowed; |
||||
|
} |
||||
|
|
||||
|
/* 响应式设计 - rqrq */ |
||||
|
@media screen and (max-width: 480px) { |
||||
|
.header-bar { |
||||
|
padding: 8px 12px; |
||||
|
} |
||||
|
|
||||
|
.main-content { |
||||
|
padding: 12px; |
||||
|
} |
||||
|
|
||||
|
.info-section, .ifs-section { |
||||
|
padding: 12px; |
||||
|
} |
||||
|
|
||||
|
.info-label, .ifs-label { |
||||
|
font-size: 12px; |
||||
|
} |
||||
|
|
||||
|
.info-value, .ifs-value { |
||||
|
font-size: 12px; |
||||
|
} |
||||
|
|
||||
|
.action-btn { |
||||
|
padding: 12px 16px; |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue