|
|
|
@ -146,11 +146,21 @@ |
|
|
|
<div class="col-position">位置</div> |
|
|
|
<div class="col-layer">层数</div> |
|
|
|
<div class="col-serial">标签号</div> |
|
|
|
<div v-if="showSplitAction" class="col-action">操作</div> |
|
|
|
</div> |
|
|
|
<div v-for="(item, index) in detailList" :key="index" class="table-row"> |
|
|
|
<div v-for="(item, index) in detailList" :key="'main-' + index" class="table-row"> |
|
|
|
<div class="col-position">{{ item.position }}</div> |
|
|
|
<div class="col-layer">{{ item.layer }}</div> |
|
|
|
<div class="col-serial">{{ item.serialNo }}</div> |
|
|
|
<div v-if="showSplitAction" class="col-action"> |
|
|
|
<button |
|
|
|
class="table-split-btn" |
|
|
|
:disabled="interfaceBusy" |
|
|
|
@click="showSplitDialog(item)" |
|
|
|
> |
|
|
|
拆分 |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div v-if="detailList.length === 0" class="table-row empty-row"> |
|
|
|
<div class="empty-hint">暂无分拣明细数据</div> |
|
|
|
@ -174,11 +184,21 @@ |
|
|
|
<div class="col-position">位置</div> |
|
|
|
<div class="col-layer">层数</div> |
|
|
|
<div class="col-serial">标签号</div> |
|
|
|
<div v-if="showSplitAction" class="col-action">操作</div> |
|
|
|
</div> |
|
|
|
<div v-for="(item, index) in detailList" :key="index" class="table-row"> |
|
|
|
<div v-for="(item, index) in detailList" :key="'browse-' + index" class="table-row"> |
|
|
|
<div class="col-position">{{ item.position }}</div> |
|
|
|
<div class="col-layer">{{ item.layer }}</div> |
|
|
|
<div class="col-serial">{{ item.serialNo }}</div> |
|
|
|
<div v-if="showSplitAction" class="col-action"> |
|
|
|
<button |
|
|
|
class="table-split-btn" |
|
|
|
:disabled="interfaceBusy" |
|
|
|
@click="showSplitDialog(item)" |
|
|
|
> |
|
|
|
拆分 |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div v-if="detailList.length === 0" class="table-row empty-row"> |
|
|
|
<div class="empty-hint">暂无分拣明细数据</div> |
|
|
|
@ -190,6 +210,56 @@ |
|
|
|
</div> |
|
|
|
</el-dialog> |
|
|
|
|
|
|
|
<!-- 手工分拣拆分弹窗:交互对齐 /labelQuery,仅工作盘显示;被分拣源盘不走这里 - rqrq --> |
|
|
|
<el-dialog |
|
|
|
title="标签拆分" |
|
|
|
:visible.sync="splitDialogVisible" |
|
|
|
width="90%" |
|
|
|
:close-on-click-modal="false" |
|
|
|
:append-to-body="true" |
|
|
|
:show-close="false" |
|
|
|
> |
|
|
|
<div class="split-dialog-content"> |
|
|
|
<div class="split-info-row"> |
|
|
|
<span class="split-label">原标签编码:</span> |
|
|
|
<span class="split-value">{{ splitRow ? splitRow.serialNo : '' }}</span> |
|
|
|
</div> |
|
|
|
<div class="split-info-row"> |
|
|
|
<span class="split-label">当前数量:</span> |
|
|
|
<span class="split-value">{{ splitRow ? formatQty(splitRow.qty) : 0 }}</span> |
|
|
|
</div> |
|
|
|
<div class="split-input-group"> |
|
|
|
<label class="split-label required">拆分数量:</label> |
|
|
|
<el-input |
|
|
|
v-model="splitQty" |
|
|
|
type="number" |
|
|
|
placeholder="请输入拆分数量" |
|
|
|
class="split-input" |
|
|
|
ref="splitInput" |
|
|
|
:disabled="splitLoading" |
|
|
|
/> |
|
|
|
<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" :disabled="splitLoading">取消</button> |
|
|
|
<button |
|
|
|
class="dialog-btn confirm" |
|
|
|
@click="confirmSplit" |
|
|
|
:disabled="splitLoading || !isValidSplitQty" |
|
|
|
> |
|
|
|
{{ splitLoading ? '处理中...' : '确定拆分' }} |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
</el-dialog> |
|
|
|
|
|
|
|
<!-- 扫码模态框:网格层数与原PDA页面保持一致 - rqrq --> |
|
|
|
<el-dialog |
|
|
|
title="扫描标签" |
|
|
|
@ -289,7 +359,8 @@ import { |
|
|
|
getAvailablePositionsForLayer, |
|
|
|
completePalletAssemblyForFenJian, |
|
|
|
finishSorting, |
|
|
|
getSortingList |
|
|
|
getSortingList, |
|
|
|
splitLabel |
|
|
|
} from '../../../api/haianAutoWarehouse/palletPacking' |
|
|
|
|
|
|
|
export default { |
|
|
|
@ -340,6 +411,11 @@ export default { |
|
|
|
palletScanLoading: false, |
|
|
|
palletConfigLoading: false, |
|
|
|
scanRequestLoading: false, |
|
|
|
// 手工分拣拆分弹窗状态:被分拣源盘不打开 - rqrq |
|
|
|
splitDialogVisible: false, |
|
|
|
splitQty: '', |
|
|
|
splitLoading: false, |
|
|
|
splitRow: null |
|
|
|
} |
|
|
|
}, |
|
|
|
computed: { |
|
|
|
@ -366,9 +442,30 @@ export default { |
|
|
|
} |
|
|
|
return this.positionGrid.map((p) => ({ ...p, isPlaceholder: false })) |
|
|
|
}, |
|
|
|
// 仅手工分拣工作盘显示拆分列;被分拣源盘 sortedSourceFlag=true 时隐藏 - rqrq |
|
|
|
showSplitAction() { |
|
|
|
return this.palletScanned && !this.sortedSourceFlag |
|
|
|
}, |
|
|
|
// 拆分数量合法:大于0且小于原标签当前数量 - rqrq |
|
|
|
isValidSplitQty() { |
|
|
|
if (!this.splitQty || !this.splitRow) { |
|
|
|
return false |
|
|
|
} |
|
|
|
const split = Number(this.splitQty) |
|
|
|
const current = Number(this.splitRow.qty) || 0 |
|
|
|
return split > 0 && split < current |
|
|
|
}, |
|
|
|
// 拆分后原标签剩余量,非法输入时回0 - rqrq |
|
|
|
calculateRemainQty() { |
|
|
|
if (!this.splitRow || !this.splitQty) { |
|
|
|
return this.splitRow ? this.formatQty(this.splitRow.qty) : 0 |
|
|
|
} |
|
|
|
const remain = (Number(this.splitRow.qty) || 0) - (Number(this.splitQty) || 0) |
|
|
|
return remain > 0 ? this.formatQty(remain) : 0 |
|
|
|
}, |
|
|
|
// 页面接口忙碌态:任一按钮触发接口未返回时视为忙碌,统一禁用关键入口 - rqrq |
|
|
|
interfaceBusy() { |
|
|
|
return this.palletScanLoading || this.palletConfigLoading || this.scanRequestLoading || this.submitSortingLoading || this.finishSortingLoading |
|
|
|
return this.palletScanLoading || this.palletConfigLoading || this.scanRequestLoading || this.submitSortingLoading || this.finishSortingLoading || this.splitLoading |
|
|
|
} |
|
|
|
}, |
|
|
|
methods: { |
|
|
|
@ -379,6 +476,82 @@ export default { |
|
|
|
} |
|
|
|
return true |
|
|
|
}, |
|
|
|
// 数量去尾零,默认0,避免拆分弹窗展示无效小数 - rqrq |
|
|
|
formatQty(qty) { |
|
|
|
const num = Number(qty) |
|
|
|
if (!Number.isFinite(num)) { |
|
|
|
return 0 |
|
|
|
} |
|
|
|
return parseFloat(String(num)) |
|
|
|
}, |
|
|
|
// 手工分拣工作盘打开拆分弹窗;被分拣源盘不允许进入 - rqrq |
|
|
|
showSplitDialog(item) { |
|
|
|
if (!this.showSplitAction) { |
|
|
|
return |
|
|
|
} |
|
|
|
if (!item || !item.serialNo) { |
|
|
|
this.$alert('没有可拆分的标签', '提示', { confirmButtonText: '确定' }) |
|
|
|
return |
|
|
|
} |
|
|
|
this.splitRow = item |
|
|
|
this.splitQty = '' |
|
|
|
this.splitDialogVisible = true |
|
|
|
this.$nextTick(() => { |
|
|
|
if (this.$refs.splitInput) { |
|
|
|
this.$refs.splitInput.focus() |
|
|
|
} |
|
|
|
}) |
|
|
|
}, |
|
|
|
// 保存拆分:复用老 /pda/label/split;site 用标签 subSite,避免 54 大site 查不到 54A 标签 - rqrq |
|
|
|
confirmSplit() { |
|
|
|
if (!this.ensureSite() || this.splitLoading) { |
|
|
|
return |
|
|
|
} |
|
|
|
if (this.sortedSourceFlag) { |
|
|
|
this.$alert('被分拣栈板不允许拆分标签', '提示', { confirmButtonText: '确定' }) |
|
|
|
return |
|
|
|
} |
|
|
|
if (!this.splitRow || !this.splitRow.serialNo) { |
|
|
|
this.$alert('没有可拆分的标签', '提示', { confirmButtonText: '确定' }) |
|
|
|
return |
|
|
|
} |
|
|
|
if (!this.splitQty) { |
|
|
|
this.$alert('请输入拆分数量', '提示', { confirmButtonText: '确定' }) |
|
|
|
return |
|
|
|
} |
|
|
|
const splitQty = Number(this.splitQty) |
|
|
|
const currentQty = Number(this.splitRow.qty) || 0 |
|
|
|
if (!(splitQty > 0)) { |
|
|
|
this.$alert('拆分数量必须大于0', '提示', { confirmButtonText: '确定' }) |
|
|
|
return |
|
|
|
} |
|
|
|
if (!(splitQty < currentQty)) { |
|
|
|
this.$alert('拆分数量必须小于当前数量', '提示', { confirmButtonText: '确定' }) |
|
|
|
return |
|
|
|
} |
|
|
|
const labelSite = (this.splitRow.subSite || this.site || '').trim() |
|
|
|
this.splitLoading = true |
|
|
|
splitLabel({ |
|
|
|
site: labelSite, |
|
|
|
unitId: this.splitRow.serialNo, |
|
|
|
splitQty: splitQty, |
|
|
|
operatorName: localStorage.getItem('userName') || '' |
|
|
|
}).then(({ data }) => { |
|
|
|
if (data && data.code === 0) { |
|
|
|
this.$message.success('拆分成功') |
|
|
|
this.splitDialogVisible = false |
|
|
|
this.splitRow = null |
|
|
|
this.splitQty = '' |
|
|
|
this.refreshTable() |
|
|
|
} else { |
|
|
|
this.$alert((data && data.msg) || '拆分失败', '错误', { confirmButtonText: '确定' }) |
|
|
|
} |
|
|
|
}).catch((error) => { |
|
|
|
this.$message.error(error.message || '拆分失败') |
|
|
|
}).finally(() => { |
|
|
|
this.splitLoading = false |
|
|
|
}) |
|
|
|
}, |
|
|
|
handleBack() { |
|
|
|
if (this.interfaceBusy) { |
|
|
|
return |
|
|
|
@ -431,6 +604,10 @@ export default { |
|
|
|
this.palletScanLoading = false |
|
|
|
this.palletConfigLoading = false |
|
|
|
this.scanRequestLoading = false |
|
|
|
this.splitDialogVisible = false |
|
|
|
this.splitQty = '' |
|
|
|
this.splitLoading = false |
|
|
|
this.splitRow = null |
|
|
|
if (needFocus) { |
|
|
|
this.$nextTick(() => { |
|
|
|
if (this.$refs.palletInput) { |
|
|
|
@ -990,11 +1167,112 @@ export default { |
|
|
|
} |
|
|
|
|
|
|
|
.col-serial { |
|
|
|
flex: 4; |
|
|
|
flex: 3; |
|
|
|
text-align: center; |
|
|
|
word-break: break-all; |
|
|
|
} |
|
|
|
|
|
|
|
.col-action { |
|
|
|
flex: 1.4; |
|
|
|
text-align: center; |
|
|
|
} |
|
|
|
|
|
|
|
.table-split-btn { |
|
|
|
min-height: 36px; |
|
|
|
padding: 6px 10px; |
|
|
|
border: none; |
|
|
|
border-radius: 4px; |
|
|
|
background: #17B3A3; |
|
|
|
color: #fff; |
|
|
|
font-size: 13px; |
|
|
|
cursor: pointer; |
|
|
|
} |
|
|
|
|
|
|
|
.table-split-btn:disabled { |
|
|
|
background: #ccc; |
|
|
|
cursor: not-allowed; |
|
|
|
} |
|
|
|
|
|
|
|
.split-dialog-content { |
|
|
|
padding: 8px 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: 16px 0; |
|
|
|
} |
|
|
|
|
|
|
|
.split-input-group .split-label { |
|
|
|
display: block; |
|
|
|
margin-bottom: 8px; |
|
|
|
} |
|
|
|
|
|
|
|
.split-input { |
|
|
|
width: 100%; |
|
|
|
} |
|
|
|
|
|
|
|
.split-hint { |
|
|
|
margin-top: 6px; |
|
|
|
color: #e6a23c; |
|
|
|
font-size: 12px; |
|
|
|
} |
|
|
|
|
|
|
|
.dialog-btn { |
|
|
|
flex: 1; |
|
|
|
padding: 12px 24px; |
|
|
|
border: none; |
|
|
|
border-radius: 6px; |
|
|
|
font-size: 16px; |
|
|
|
font-weight: 500; |
|
|
|
cursor: pointer; |
|
|
|
min-height: 44px; |
|
|
|
} |
|
|
|
|
|
|
|
.dialog-btn.confirm { |
|
|
|
background: #17B3A3; |
|
|
|
color: white; |
|
|
|
} |
|
|
|
|
|
|
|
.dialog-btn.confirm:disabled { |
|
|
|
background: #ccc; |
|
|
|
cursor: not-allowed; |
|
|
|
} |
|
|
|
|
|
|
|
.dialog-btn.cancel { |
|
|
|
background: #f5f5f5; |
|
|
|
color: #333; |
|
|
|
border: 1px solid #ddd; |
|
|
|
} |
|
|
|
|
|
|
|
.dialog-btn.cancel:disabled { |
|
|
|
opacity: 0.6; |
|
|
|
cursor: not-allowed; |
|
|
|
} |
|
|
|
|
|
|
|
.empty-row { |
|
|
|
justify-content: center; |
|
|
|
align-items: center; |
|
|
|
|