|
|
|
@ -28,11 +28,11 @@ |
|
|
|
</el-input> |
|
|
|
</el-form-item> |
|
|
|
<el-form-item label="批次号"> |
|
|
|
<el-input v-model="searchData.batchNo" clearable style="width: 100px"> |
|
|
|
<el-input v-model="searchData.batchNo" clearable style="width: 80px"> |
|
|
|
</el-input> |
|
|
|
</el-form-item> |
|
|
|
<el-form-item label="状态"> |
|
|
|
<el-select v-model="searchData.statusList" style="width: 200px" multiple collapse-tags clearable placeholder="请选择状态"> |
|
|
|
<el-select v-model="searchData.statusList" style="width: 150px" multiple collapse-tags clearable placeholder="请选择状态"> |
|
|
|
<el-option label="待检" value="待检"></el-option> |
|
|
|
<el-option label="待入" value="待入"></el-option> |
|
|
|
<el-option label="在库" value="在库"></el-option> |
|
|
|
@ -254,7 +254,132 @@ |
|
|
|
</div> |
|
|
|
</el-dialog> |
|
|
|
|
|
|
|
<!-- 属性变动对话框 --> |
|
|
|
<!-- 扫描标签对话框(新增) --> |
|
|
|
<el-dialog |
|
|
|
:close-on-click-modal="false" |
|
|
|
v-drag |
|
|
|
top="10vh" |
|
|
|
:visible.sync="scanLabelDialogVisible" |
|
|
|
width="1100px" |
|
|
|
class="scan-label-dialog"> |
|
|
|
<div slot="title" class="dialog-title-custom"> |
|
|
|
<i class="el-icon-edit-outline"></i> |
|
|
|
<span>属性变动</span> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="scan-label-content"> |
|
|
|
<!-- 扫描输入区域 --> |
|
|
|
<div class="scan-input-section"> |
|
|
|
<div class="input-wrapper"> |
|
|
|
<label class="input-label">标签条码</label> |
|
|
|
<el-input |
|
|
|
ref="scanInput" |
|
|
|
v-model="scanLabelNo" |
|
|
|
@keyup.enter.native="handleScanLabel" |
|
|
|
clearable |
|
|
|
placeholder="请扫描标签条码" |
|
|
|
prefix-icon="el-icon-barcode" |
|
|
|
class="scan-input"> |
|
|
|
</el-input> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="mode-switch-wrapper"> |
|
|
|
<el-switch |
|
|
|
v-model="isRemoveMode" |
|
|
|
active-color="#ff4949" |
|
|
|
inactive-color="#67C23A" |
|
|
|
class="mode-switch"> |
|
|
|
</el-switch> |
|
|
|
<span class="mode-text" :class="{ 'remove-mode': isRemoveMode }"> |
|
|
|
{{ isRemoveMode ? '移除' : '添加' }} |
|
|
|
</span> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="info-badges"> |
|
|
|
<div class="badge-item"> |
|
|
|
<i class="el-icon-document"></i> |
|
|
|
<span class="badge-label">标签张数</span> |
|
|
|
<span class="badge-value">{{ scannedLabelList.length }}</span> |
|
|
|
</div> |
|
|
|
<div class="badge-item"> |
|
|
|
<i class="el-icon-box"></i> |
|
|
|
<span class="badge-label">物料总数</span> |
|
|
|
<span class="badge-value">{{ totalScannedQty }}</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 扫描的标签列表 --> |
|
|
|
<div class="table-wrapper"> |
|
|
|
<el-table |
|
|
|
:data="scannedLabelList" |
|
|
|
height="420" |
|
|
|
stripe |
|
|
|
class="scan-table"> |
|
|
|
<el-table-column |
|
|
|
type="index" |
|
|
|
label="NO." |
|
|
|
width="60" |
|
|
|
align="center"> |
|
|
|
</el-table-column> |
|
|
|
<el-table-column |
|
|
|
prop="rollNo" |
|
|
|
label="标签条码" |
|
|
|
header-align="center" |
|
|
|
align="left" |
|
|
|
min-width="120" |
|
|
|
show-overflow-tooltip> |
|
|
|
</el-table-column> |
|
|
|
<el-table-column |
|
|
|
prop="qtyOnHand" |
|
|
|
label="标签数量" |
|
|
|
header-align="center" |
|
|
|
align="right" |
|
|
|
width="100"> |
|
|
|
</el-table-column> |
|
|
|
<el-table-column |
|
|
|
prop="partNo" |
|
|
|
label="物料编码" |
|
|
|
header-align="center" |
|
|
|
align="left" |
|
|
|
min-width="130" |
|
|
|
show-overflow-tooltip> |
|
|
|
</el-table-column> |
|
|
|
<el-table-column |
|
|
|
prop="partDescription" |
|
|
|
label="物料名称" |
|
|
|
header-align="center" |
|
|
|
align="left" |
|
|
|
min-width="200" |
|
|
|
show-overflow-tooltip> |
|
|
|
</el-table-column> |
|
|
|
<el-table-column |
|
|
|
prop="parentRollNo" |
|
|
|
label="上机标签号" |
|
|
|
header-align="center" |
|
|
|
align="left" |
|
|
|
min-width="120" |
|
|
|
show-overflow-tooltip> |
|
|
|
</el-table-column> |
|
|
|
</el-table> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div slot="footer" class="dialog-footer-custom"> |
|
|
|
<el-button |
|
|
|
type="success" |
|
|
|
@click="confirmScanLabels()" |
|
|
|
:disabled="scannedLabelList.length === 0" |
|
|
|
icon="el-icon-check"> |
|
|
|
保存 |
|
|
|
</el-button> |
|
|
|
<el-button @click="closeScanLabelDialog()" icon="el-icon-close"> |
|
|
|
取消 |
|
|
|
</el-button> |
|
|
|
</div> |
|
|
|
</el-dialog> |
|
|
|
|
|
|
|
<!-- 属性变动对话框(原有的) --> |
|
|
|
<el-dialog title="属性变动" :close-on-click-modal="false" v-drag :visible.sync="attributeChangeDialogVisible" width="450px"> |
|
|
|
<el-form :model="attributeChangeForm" label-position="top" style="margin-top: -5px;"> |
|
|
|
<el-row :gutter="20"> |
|
|
|
@ -724,6 +849,11 @@ export default { |
|
|
|
dataList: [], |
|
|
|
addModelFlag: false, |
|
|
|
dataListLoading: false, |
|
|
|
// 扫描标签对话框相关(新增) |
|
|
|
scanLabelDialogVisible: false, |
|
|
|
scanLabelNo: '', |
|
|
|
scannedLabelList: [], |
|
|
|
isRemoveMode: false, // 默认为添加模式 |
|
|
|
// 属性变动相关 |
|
|
|
attributeChangeDialogVisible: false, |
|
|
|
attributeChangeForm: { |
|
|
|
@ -746,6 +876,14 @@ export default { |
|
|
|
}, |
|
|
|
components: { |
|
|
|
|
|
|
|
}, |
|
|
|
computed: { |
|
|
|
// 计算物料总数(标签数量总和) |
|
|
|
totalScannedQty() { |
|
|
|
return this.scannedLabelList.reduce((sum, item) => { |
|
|
|
return sum + (parseFloat(item.qtyOnHand) || 0); |
|
|
|
}, 0); |
|
|
|
} |
|
|
|
}, |
|
|
|
mounted() { |
|
|
|
this.$nextTick(() => { |
|
|
|
@ -999,20 +1137,139 @@ export default { |
|
|
|
kuCunLabelPrint(this.selectionDataList,"A") |
|
|
|
}, |
|
|
|
|
|
|
|
// 打开属性变动对话框 |
|
|
|
// 打开属性变动对话框(修改:不需要选择标签也可以打开) |
|
|
|
openAttributeChangeDialog() { |
|
|
|
if(this.selectionDataList.length === 0){ |
|
|
|
this.$message.warning('请先勾选要变动属性的标签!'); |
|
|
|
return false; |
|
|
|
// 初始化扫描标签列表为已勾选的标签 |
|
|
|
this.scannedLabelList = JSON.parse(JSON.stringify(this.selectionDataList)); |
|
|
|
this.scanLabelNo = ''; |
|
|
|
this.isRemoveMode = false; // 重置为添加模式 |
|
|
|
|
|
|
|
// 打开扫描标签对话框 |
|
|
|
this.scanLabelDialogVisible = true; |
|
|
|
|
|
|
|
// 自动聚焦到输入框 |
|
|
|
this.$nextTick(() => { |
|
|
|
if (this.$refs.scanInput) { |
|
|
|
this.$refs.scanInput.focus(); |
|
|
|
} |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
// 处理扫描标签(根据模式添加或移除) |
|
|
|
async handleScanLabel() { |
|
|
|
if (!this.scanLabelNo || this.scanLabelNo.trim() === '') { |
|
|
|
this.$message.warning('请输入或扫描标签条码!'); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const rollNo = this.scanLabelNo.trim(); |
|
|
|
|
|
|
|
if (this.isRemoveMode) { |
|
|
|
// 移除模式 |
|
|
|
this.removeLabelByCode(rollNo); |
|
|
|
} else { |
|
|
|
// 添加模式 |
|
|
|
this.addLabelByCode(rollNo); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 添加标签 |
|
|
|
async addLabelByCode(rollNo) { |
|
|
|
// 检查是否已经存在 |
|
|
|
const existingIndex = this.scannedLabelList.findIndex(item => item.rollNo === rollNo); |
|
|
|
if (existingIndex !== -1) { |
|
|
|
this.$message.warning(`标签 ${rollNo} 已存在列表中`); |
|
|
|
this.scanLabelNo = ''; |
|
|
|
this.$nextTick(() => { |
|
|
|
if (this.$refs.scanInput) { |
|
|
|
this.$refs.scanInput.focus(); |
|
|
|
} |
|
|
|
}); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
// 调用查询接口获取标签信息(使用主信息的查询SQL) |
|
|
|
const params = { |
|
|
|
userName: this.$store.state.user.name, |
|
|
|
rollNo: rollNo, |
|
|
|
page: 1, |
|
|
|
limit: 1 |
|
|
|
}; |
|
|
|
|
|
|
|
const {data} = await getKuCunLabelData(params); |
|
|
|
|
|
|
|
if (data && data.code === 0 && data.page && data.page.list && data.page.list.length > 0) { |
|
|
|
const labelData = data.page.list[0]; |
|
|
|
|
|
|
|
// 添加到列表最前面(倒排) |
|
|
|
this.scannedLabelList.unshift(labelData); |
|
|
|
this.$message.success(`标签 ${rollNo} 添加成功`); |
|
|
|
} else { |
|
|
|
this.$message.error(`未找到标签 ${rollNo}`); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
this.$message.error(`查询标签失败: ${error.message || error}`); |
|
|
|
} |
|
|
|
|
|
|
|
// 清空输入框并聚焦 |
|
|
|
this.scanLabelNo = ''; |
|
|
|
this.$nextTick(() => { |
|
|
|
if (this.$refs.scanInput) { |
|
|
|
this.$refs.scanInput.focus(); |
|
|
|
} |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
// 移除标签 |
|
|
|
removeLabelByCode(rollNo) { |
|
|
|
const existingIndex = this.scannedLabelList.findIndex(item => item.rollNo === rollNo); |
|
|
|
|
|
|
|
if (existingIndex === -1) { |
|
|
|
this.$message.warning(`未找到标签 ${rollNo}`); |
|
|
|
} else { |
|
|
|
this.scannedLabelList.splice(existingIndex, 1); |
|
|
|
this.$message.success(`标签 ${rollNo} 已移除`); |
|
|
|
} |
|
|
|
|
|
|
|
// 清空输入框并聚焦 |
|
|
|
this.scanLabelNo = ''; |
|
|
|
this.$nextTick(() => { |
|
|
|
if (this.$refs.scanInput) { |
|
|
|
this.$refs.scanInput.focus(); |
|
|
|
} |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
// 确认扫描标签(保存按钮) |
|
|
|
confirmScanLabels() { |
|
|
|
if (this.scannedLabelList.length === 0) { |
|
|
|
this.$message.warning('请至少扫描一个标签!'); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 重置表单 |
|
|
|
|
|
|
|
// 关闭扫描对话框 |
|
|
|
this.scanLabelDialogVisible = false; |
|
|
|
|
|
|
|
// 将扫描的标签列表赋值给 selectionDataList |
|
|
|
this.selectionDataList = JSON.parse(JSON.stringify(this.scannedLabelList)); |
|
|
|
|
|
|
|
// 重置属性变动表单 |
|
|
|
this.attributeChangeForm = { |
|
|
|
batchNo: '', |
|
|
|
remark: '' |
|
|
|
}; |
|
|
|
|
|
|
|
// 打开属性变动对话框 |
|
|
|
this.attributeChangeDialogVisible = true; |
|
|
|
}, |
|
|
|
|
|
|
|
// 关闭扫描标签对话框 |
|
|
|
closeScanLabelDialog() { |
|
|
|
this.scanLabelDialogVisible = false; |
|
|
|
this.scanLabelNo = ''; |
|
|
|
}, |
|
|
|
|
|
|
|
// 保存属性变动 |
|
|
|
async saveAttributeChange() { |
|
|
|
// 构建更新数据列表 |
|
|
|
@ -1247,4 +1504,267 @@ export default { |
|
|
|
border-color: #409EFF; |
|
|
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1); |
|
|
|
} |
|
|
|
|
|
|
|
/* 扫描标签对话框样式 - 简约高端 */ |
|
|
|
.scan-label-dialog { |
|
|
|
/deep/ .el-dialog { |
|
|
|
border-radius: 8px; |
|
|
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12); |
|
|
|
} |
|
|
|
|
|
|
|
/deep/ .el-dialog__header { |
|
|
|
padding: 0; |
|
|
|
border-bottom: 1px solid #e8e8e8; |
|
|
|
} |
|
|
|
|
|
|
|
/deep/ .el-dialog__body { |
|
|
|
padding: 0; |
|
|
|
} |
|
|
|
|
|
|
|
/deep/ .el-dialog__footer { |
|
|
|
padding: 16px 24px; |
|
|
|
border-top: 1px solid #e8e8e8; |
|
|
|
background-color: #fafafa; |
|
|
|
} |
|
|
|
|
|
|
|
.dialog-title-custom { |
|
|
|
padding: 20px 24px; |
|
|
|
font-size: 18px; |
|
|
|
font-weight: 600; |
|
|
|
color: #303133; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
|
|
|
|
i { |
|
|
|
font-size: 20px; |
|
|
|
margin-right: 10px; |
|
|
|
color: #409EFF; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.scan-label-content { |
|
|
|
padding: 24px; |
|
|
|
|
|
|
|
.scan-input-section { |
|
|
|
display: flex; |
|
|
|
align-items: flex-end; |
|
|
|
gap: 20px; |
|
|
|
margin-bottom: 20px; |
|
|
|
padding-bottom: 20px; |
|
|
|
border-bottom: 1px solid #f0f0f0; |
|
|
|
|
|
|
|
.input-wrapper { |
|
|
|
flex: 1; |
|
|
|
|
|
|
|
.input-label { |
|
|
|
display: block; |
|
|
|
font-size: 14px; |
|
|
|
color: #606266; |
|
|
|
margin-bottom: 2px; |
|
|
|
font-weight: 500; |
|
|
|
} |
|
|
|
|
|
|
|
.scan-input { |
|
|
|
/deep/ .el-input__inner { |
|
|
|
height: 42px; |
|
|
|
line-height: 42px; |
|
|
|
font-size: 14px; |
|
|
|
border-radius: 4px; |
|
|
|
border: 1px solid #dcdfe6; |
|
|
|
transition: all 0.3s; |
|
|
|
|
|
|
|
&:focus { |
|
|
|
border-color: #409EFF; |
|
|
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/deep/ .el-input__prefix { |
|
|
|
left: 10px; |
|
|
|
font-size: 16px; |
|
|
|
color: #909399; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.mode-switch-wrapper { |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
gap: 10px; |
|
|
|
padding: 0 16px; |
|
|
|
height: 42px; |
|
|
|
//background: #f5f7fa; |
|
|
|
border-radius: 4px; |
|
|
|
|
|
|
|
//.mode-switch { |
|
|
|
// /deep/ .el-switch__core { |
|
|
|
// width: 44px !important; |
|
|
|
// height: 22px; |
|
|
|
// border-radius: 11px; |
|
|
|
// |
|
|
|
// &::after { |
|
|
|
// width: 18px; |
|
|
|
// height: 18px; |
|
|
|
// top: 1px; |
|
|
|
// left: 1px; |
|
|
|
// } |
|
|
|
// } |
|
|
|
// |
|
|
|
// /deep/ .el-switch.is-checked .el-switch__core::after { |
|
|
|
// left: calc(100% - 2px); |
|
|
|
// margin-left: -18px; |
|
|
|
// } |
|
|
|
//} |
|
|
|
|
|
|
|
.mode-text { |
|
|
|
font-size: 14px; |
|
|
|
font-weight: 600; |
|
|
|
color: #67C23A; |
|
|
|
transition: color 0.3s; |
|
|
|
min-width: 40px; |
|
|
|
|
|
|
|
&.remove-mode { |
|
|
|
color: #ff4949; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.info-badges { |
|
|
|
display: flex; |
|
|
|
gap: 16px; |
|
|
|
|
|
|
|
.badge-item { |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
gap: 8px; |
|
|
|
padding: 10px 16px; |
|
|
|
background: linear-gradient(135deg, #f5f7fa 0%, #e8eaf0 100%); |
|
|
|
border-radius: 4px; |
|
|
|
border: 1px solid #e4e7ed; |
|
|
|
|
|
|
|
i { |
|
|
|
font-size: 18px; |
|
|
|
color: #409EFF; |
|
|
|
} |
|
|
|
|
|
|
|
.badge-label { |
|
|
|
font-size: 13px; |
|
|
|
color: #909399; |
|
|
|
} |
|
|
|
|
|
|
|
.badge-value { |
|
|
|
font-size: 18px; |
|
|
|
font-weight: 700; |
|
|
|
color: #303133; |
|
|
|
min-width: 32px; |
|
|
|
text-align: right; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.table-wrapper { |
|
|
|
position: relative; |
|
|
|
|
|
|
|
.scan-table { |
|
|
|
/deep/ .el-table__header-wrapper { |
|
|
|
th { |
|
|
|
background-color: #f5f7fa; |
|
|
|
color: #606266; |
|
|
|
font-weight: 600; |
|
|
|
font-size: 13px; |
|
|
|
padding: 12px 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/deep/ .el-table__body-wrapper { |
|
|
|
.el-table__row { |
|
|
|
transition: background-color 0.2s; |
|
|
|
|
|
|
|
&:hover { |
|
|
|
background-color: #f5f7fa; |
|
|
|
} |
|
|
|
|
|
|
|
td { |
|
|
|
padding: 10px 0; |
|
|
|
font-size: 13px; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/deep/ .el-table--striped .el-table__body tr.el-table__row--striped td { |
|
|
|
background-color: #fafafa; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.empty-state { |
|
|
|
position: absolute; |
|
|
|
top: 50%; |
|
|
|
left: 50%; |
|
|
|
transform: translate(-50%, -50%); |
|
|
|
text-align: center; |
|
|
|
color: #909399; |
|
|
|
|
|
|
|
i { |
|
|
|
font-size: 64px; |
|
|
|
color: #dcdfe6; |
|
|
|
margin-bottom: 16px; |
|
|
|
} |
|
|
|
|
|
|
|
p { |
|
|
|
font-size: 14px; |
|
|
|
margin: 0; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.dialog-footer-custom { |
|
|
|
display: flex; |
|
|
|
justify-content: center; |
|
|
|
gap: 12px; |
|
|
|
|
|
|
|
.el-button { |
|
|
|
min-width: 100px; |
|
|
|
height: 38px; |
|
|
|
font-size: 14px; |
|
|
|
border-radius: 4px; |
|
|
|
transition: all 0.3s; |
|
|
|
|
|
|
|
&.el-button--success { |
|
|
|
background: #67C23A; |
|
|
|
border-color: #67C23A; |
|
|
|
|
|
|
|
&:hover { |
|
|
|
background: #85ce61; |
|
|
|
border-color: #85ce61; |
|
|
|
transform: translateY(-1px); |
|
|
|
box-shadow: 0 4px 12px rgba(103, 194, 58, 0.3); |
|
|
|
} |
|
|
|
|
|
|
|
&:active { |
|
|
|
transform: translateY(0); |
|
|
|
} |
|
|
|
|
|
|
|
&.is-disabled { |
|
|
|
background: #b3d8a1; |
|
|
|
border-color: #b3d8a1; |
|
|
|
transform: none; |
|
|
|
box-shadow: none; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
&:not(.el-button--success) { |
|
|
|
&:hover { |
|
|
|
transform: translateY(-1px); |
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); |
|
|
|
} |
|
|
|
|
|
|
|
&:active { |
|
|
|
transform: translateY(0); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
</style> |