|
|
@ -110,6 +110,12 @@ |
|
|
|
|
|
|
|
|
<!-- 底部操作按钮 --> |
|
|
<!-- 底部操作按钮 --> |
|
|
<div v-if="labelInfo" class="bottom-actions"> |
|
|
<div v-if="labelInfo" class="bottom-actions"> |
|
|
|
|
|
<button |
|
|
|
|
|
class="action-btn primary" |
|
|
|
|
|
@click="showSplitDialog" |
|
|
|
|
|
> |
|
|
|
|
|
拆分 |
|
|
|
|
|
</button> |
|
|
<button |
|
|
<button |
|
|
class="action-btn secondary" |
|
|
class="action-btn secondary" |
|
|
@click="clearData" |
|
|
@click="clearData" |
|
|
@ -120,11 +126,66 @@ |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 拆分对话框 --> |
|
|
|
|
|
<el-dialog |
|
|
|
|
|
title="标签拆分" |
|
|
|
|
|
:visible.sync="splitDialogVisible" |
|
|
|
|
|
width="90%" |
|
|
|
|
|
:close-on-click-modal="false" |
|
|
|
|
|
:append-to-body="true" |
|
|
|
|
|
:modal-append-to-body="true" |
|
|
|
|
|
> |
|
|
|
|
|
<div class="split-dialog-content"> |
|
|
|
|
|
<div class="split-info-row"> |
|
|
|
|
|
<span class="split-label">原标签编码:</span> |
|
|
|
|
|
<span class="split-value">{{ labelInfo ? labelInfo.unitId : '' }}</span> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="split-info-row"> |
|
|
|
|
|
<span class="split-label">当前数量:</span> |
|
|
|
|
|
<span class="split-value">{{ labelInfo ? labelInfo.qty : 0 }}</span> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="split-input-group"> |
|
|
|
|
|
<label class="split-label required">拆分数量:</label> |
|
|
|
|
|
<el-input |
|
|
|
|
|
v-model="splitQty" |
|
|
|
|
|
type="number" |
|
|
|
|
|
placeholder="请输入拆分数量(必须小于100)" |
|
|
|
|
|
class="split-input" |
|
|
|
|
|
ref="splitInput" |
|
|
|
|
|
/> |
|
|
|
|
|
<div class="split-hint" v-if="splitQty && !isValidSplitQty"> |
|
|
|
|
|
<i class="el-icon-warning"></i> |
|
|
|
|
|
拆分数量必须大于0且小于当前数量 |
|
|
|
|
|
</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="split-info-row"> |
|
|
|
|
|
<span class="split-label">拆分后原标签剩余:</span> |
|
|
|
|
|
<span class="split-value split-remain">{{ calculateRemainQty }}</span> |
|
|
|
|
|
</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div slot="footer" class="dialog-footer"> |
|
|
|
|
|
<button class="dialog-btn cancel" @click="splitDialogVisible = false"> |
|
|
|
|
|
取消 |
|
|
|
|
|
</button> |
|
|
|
|
|
<button |
|
|
|
|
|
class="dialog-btn confirm" |
|
|
|
|
|
@click="confirmSplit" |
|
|
|
|
|
:disabled="splitLoading || !isValidSplitQty" |
|
|
|
|
|
> |
|
|
|
|
|
{{ splitLoading ? '处理中...' : '确定拆分' }} |
|
|
|
|
|
</button> |
|
|
|
|
|
</div> |
|
|
|
|
|
</el-dialog> |
|
|
</div> |
|
|
</div> |
|
|
</template> |
|
|
</template> |
|
|
|
|
|
|
|
|
<script> |
|
|
<script> |
|
|
import { queryLabelInfo } from '@/api/inventory/label' |
|
|
|
|
|
|
|
|
import { queryLabelInfo, splitLabel } from '@/api/inventory/label' |
|
|
import { printLabelCommon } from '@/api/production/production-inbound.js' |
|
|
import { printLabelCommon } from '@/api/production/production-inbound.js' |
|
|
|
|
|
|
|
|
export default { |
|
|
export default { |
|
|
@ -136,9 +197,40 @@ export default { |
|
|
labelCode: '', |
|
|
labelCode: '', |
|
|
labelInfo: null, |
|
|
labelInfo: null, |
|
|
loading: false, |
|
|
loading: false, |
|
|
printLoading: false |
|
|
|
|
|
|
|
|
printLoading: false, |
|
|
|
|
|
splitDialogVisible: false, |
|
|
|
|
|
splitQty: '', |
|
|
|
|
|
splitLoading: false |
|
|
}; |
|
|
}; |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
computed: { |
|
|
|
|
|
/** |
|
|
|
|
|
* 计算拆分后原标签剩余数量 |
|
|
|
|
|
*/ |
|
|
|
|
|
calculateRemainQty() { |
|
|
|
|
|
if (!this.labelInfo || !this.splitQty) { |
|
|
|
|
|
return this.labelInfo ? this.labelInfo.qty : 0; |
|
|
|
|
|
} |
|
|
|
|
|
const currentQty = parseFloat(this.labelInfo.qty) || 0; |
|
|
|
|
|
const split = parseFloat(this.splitQty) || 0; |
|
|
|
|
|
const remain = currentQty - split; |
|
|
|
|
|
return remain >= 0 ? remain : 0; |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 验证拆分数量是否有效 |
|
|
|
|
|
*/ |
|
|
|
|
|
isValidSplitQty() { |
|
|
|
|
|
if (!this.splitQty || !this.labelInfo) { |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
const split = parseFloat(this.splitQty); |
|
|
|
|
|
const current = parseFloat(this.labelInfo.qty) || 0; |
|
|
|
|
|
|
|
|
|
|
|
return split > 0 && split < current; |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
methods: { |
|
|
methods: { |
|
|
/** |
|
|
/** |
|
|
@ -281,6 +373,94 @@ export default { |
|
|
} finally { |
|
|
} finally { |
|
|
this.printLoading = false; |
|
|
this.printLoading = false; |
|
|
} |
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 显示拆分对话框 |
|
|
|
|
|
*/ |
|
|
|
|
|
showSplitDialog() { |
|
|
|
|
|
if (!this.labelInfo) { |
|
|
|
|
|
this.$message.error('没有可拆分的标签'); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.splitQty = ''; |
|
|
|
|
|
this.splitDialogVisible = true; |
|
|
|
|
|
|
|
|
|
|
|
// 聚焦到拆分数量输入框 |
|
|
|
|
|
this.$nextTick(() => { |
|
|
|
|
|
if (this.$refs.splitInput) { |
|
|
|
|
|
this.$refs.splitInput.focus(); |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 确认拆分 |
|
|
|
|
|
*/ |
|
|
|
|
|
async confirmSplit() { |
|
|
|
|
|
// 验证拆分数量 |
|
|
|
|
|
if (!this.splitQty) { |
|
|
|
|
|
this.$message.error('请输入拆分数量'); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const splitQty = parseFloat(this.splitQty); |
|
|
|
|
|
const currentQty = parseFloat(this.labelInfo.qty); |
|
|
|
|
|
|
|
|
|
|
|
if (splitQty <= 0) { |
|
|
|
|
|
this.$message.error('拆分数量必须大于0'); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (splitQty >= currentQty) { |
|
|
|
|
|
this.$message.error('拆分数量必须小于当前数量'); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.splitLoading = true; |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
const { data } = await splitLabel({ |
|
|
|
|
|
site: this.site, |
|
|
|
|
|
unitId: this.labelInfo.unitId, |
|
|
|
|
|
splitQty: splitQty, |
|
|
|
|
|
operatorName: localStorage.getItem('userName') |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
if (data && data.code === 0) { |
|
|
|
|
|
const result = data.data; |
|
|
|
|
|
|
|
|
|
|
|
this.$message.success('拆分成功!'); |
|
|
|
|
|
|
|
|
|
|
|
// 关闭对话框 |
|
|
|
|
|
this.splitDialogVisible = false; |
|
|
|
|
|
|
|
|
|
|
|
// 确定标签类型 |
|
|
|
|
|
let printLabelType; |
|
|
|
|
|
if (this.labelInfo.partNo && this.labelInfo.partNo.startsWith("80")) { |
|
|
|
|
|
printLabelType = '库存成品标签'; |
|
|
|
|
|
} else { |
|
|
|
|
|
printLabelType = 'BIL标签'; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 打印原标签和新标签 |
|
|
|
|
|
console.log('打印标签:', [result.originalUnitId, result.newUnitId]); |
|
|
|
|
|
await this.printViaServer([result.originalUnitId, result.newUnitId], printLabelType); |
|
|
|
|
|
|
|
|
|
|
|
// 刷新标签信息(显示更新后的数量) |
|
|
|
|
|
this.labelCode = this.labelInfo.unitId; |
|
|
|
|
|
this.handleLabelScan(); |
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
this.$message.error(data.msg || '拆分失败'); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error('拆分标签失败:', error); |
|
|
|
|
|
this.$message.error('拆分异常: ' + (error.message || error)); |
|
|
|
|
|
} finally { |
|
|
|
|
|
this.splitLoading = false; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
@ -500,6 +680,106 @@ export default { |
|
|
background: #e8e8e8; |
|
|
background: #e8e8e8; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* 拆分对话框样式 */ |
|
|
|
|
|
.split-dialog-content { |
|
|
|
|
|
padding: 16px 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.split-info-row { |
|
|
|
|
|
display: flex; |
|
|
|
|
|
justify-content: space-between; |
|
|
|
|
|
align-items: center; |
|
|
|
|
|
padding: 12px 0; |
|
|
|
|
|
border-bottom: 1px solid #f0f0f0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.split-label { |
|
|
|
|
|
font-size: 14px; |
|
|
|
|
|
color: #666; |
|
|
|
|
|
font-weight: 500; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.split-value { |
|
|
|
|
|
font-size: 15px; |
|
|
|
|
|
font-weight: 600; |
|
|
|
|
|
color: #333; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.split-value.split-remain { |
|
|
|
|
|
color: #17B3A3; |
|
|
|
|
|
font-size: 16px; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.split-input-group { |
|
|
|
|
|
margin: 20px 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.split-input-group .split-label { |
|
|
|
|
|
display: block; |
|
|
|
|
|
margin-bottom: 8px; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.split-input { |
|
|
|
|
|
width: 100%; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* 对话框底部按钮 */ |
|
|
|
|
|
.dialog-footer { |
|
|
|
|
|
display: flex; |
|
|
|
|
|
gap: 12px; |
|
|
|
|
|
padding-top: 16px; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.dialog-btn { |
|
|
|
|
|
flex: 1; |
|
|
|
|
|
padding: 12px 24px; |
|
|
|
|
|
border: none; |
|
|
|
|
|
border-radius: 6px; |
|
|
|
|
|
font-size: 16px; |
|
|
|
|
|
font-weight: 500; |
|
|
|
|
|
cursor: pointer; |
|
|
|
|
|
transition: all 0.2s; |
|
|
|
|
|
min-height: 44px; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.dialog-btn.confirm { |
|
|
|
|
|
background: #17B3A3; |
|
|
|
|
|
color: white; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.dialog-btn.confirm:hover:not(:disabled) { |
|
|
|
|
|
background: #15a394; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.dialog-btn.confirm:disabled { |
|
|
|
|
|
background: #ccc; |
|
|
|
|
|
cursor: not-allowed; |
|
|
|
|
|
opacity: 0.6; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.dialog-btn.cancel { |
|
|
|
|
|
background: #f5f5f5; |
|
|
|
|
|
color: #333; |
|
|
|
|
|
border: 1px solid #ddd; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.dialog-btn.cancel:hover { |
|
|
|
|
|
background: #e8e8e8; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* 强制对话框显示在最上层 */ |
|
|
|
|
|
::v-deep .el-dialog__wrapper { |
|
|
|
|
|
z-index: 9999 !important; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
::v-deep .el-dialog { |
|
|
|
|
|
z-index: 10000 !important; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
::v-deep .v-modal { |
|
|
|
|
|
z-index: 9998 !important; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/* 响应式设计 */ |
|
|
/* 响应式设计 */ |
|
|
@media screen and (max-width: 480px) { |
|
|
@media screen and (max-width: 480px) { |
|
|
.header-bar { |
|
|
.header-bar { |
|
|
|