feat: 完善WCS调度、库存追溯和出库分配

This commit is contained in:
zgw
2026-07-15 16:24:07 +08:00
parent 9d3aa7a3bd
commit 66ac268524
101 changed files with 12596 additions and 669 deletions

View File

@@ -23,12 +23,13 @@
<input class="form-input" v-model="trayCode" placeholder="请扫描托盘号" />
</view>
<view class="form-tip" v-if="!warehouseId || !trayCode">请先选择仓库和扫描托盘号</view>
<view class="form-tip" v-else-if="isDenseWarehouse">密集库入口号由 WCS 获取作业时分配</view>
<view class="form-item" v-else>
<text class="form-label">入库点位</text>
<picker class="form-picker" mode="selector" :range="stationPointOptions" range-key="pointName"
:value="stationPointIndex" :disabled="bindSuccess" @change="handleStationPointChange">
<view class="picker-value" :class="{ placeholder: !stationPointId }">
{{ selectedStationPointName || '请扫描入库点位' }}
{{ selectedStationPointName || '请选择入库点位' }}
</view>
</picker>
</view>
@@ -129,6 +130,29 @@
</view>
</view>
<!-- 绑盘成功结果弹层:承载长业务结果,避免系统 Toast 挤压换行。 -->
<view class="success-dialog-mask" v-if="showSuccessDialog">
<view class="success-dialog" @click.stop>
<view class="success-mark" aria-label="成功">
<view class="success-check"></view>
</view>
<text class="success-kicker">INBOUND COMPLETE</text>
<text class="success-title">绑盘及作业生成完成</text>
<text class="success-description">{{ successDescription }}</text>
<view class="success-summary">
<view class="success-summary-row">
<text class="success-summary-label">托盘条码</text>
<text class="success-summary-value">{{ trayCode }}</text>
</view>
<view class="success-summary-row">
<text class="success-summary-label">目标仓库</text>
<text class="success-summary-value">{{ selectedWarehouseName || warehouseId }}</text>
</view>
</view>
<button class="success-action" @click="continueBinding">继续绑盘</button>
</view>
</view>
<!-- 已添加物料列表 -->
<view class="list-section" v-if="items.length > 0">
<view class="section-header">
@@ -162,8 +186,10 @@
<!-- 操作按钮 -->
<view class="btn-section">
<button class="btn btn-primary" :disabled="items.length === 0 || !trayCode || !warehouseId || !stationPointId || bindSuccess" @click="submitBindTrayAction">
{{ bindSuccess ? '已生成入库作业' : '确认绑盘并生成作业(' + items.length + ' 种物料)' }}
<button class="btn btn-primary"
:disabled="items.length === 0 || !trayCode || !warehouseId || (!isDenseWarehouse && !stationPointId) || bindSuccess || submitting"
@click="submitBindTrayAction">
{{ submitting ? '正在生成作业…' : (bindSuccess ? '已生成入库作业' : '确认绑盘并生成作业(' + items.length + ' 种物料)') }}
</button>
<button class="btn btn-default" @click="resetAll">全部重置</button>
</view>
@@ -194,6 +220,9 @@
currentRemainQty: 0,
currentQty: '',
showQuantityPopup: false,
showSuccessDialog: false,
successDescription: '',
submitting: false,
// 已完成绑盘并生成入库作业时保持页面状态,便于操作人员核对结果。
bindSuccess: false,
// 当前主单下待绑盘子单,操作人员选择子单后再修改本次绑定数量。
@@ -217,8 +246,11 @@
var selected = this.stationPointOptions[this.stationPointIndex]
return selected && selected.pointId === this.stationPointId ? (selected.pointName || selected.pointId) : ''
},
availableDetailList() {
return this.detailList.filter(function(detail) {
isDenseWarehouse() {
return this.warehouseId === '03'
},
availableDetailList() {
return this.detailList.filter(function(detail) {
return this.getAvailableRemainQty(detail) > 0
}.bind(this))
}
@@ -228,10 +260,10 @@
if (this.bindSuccess) {
return
}
this.stationPointId = ''
this.stationPointName = ''
this.stationPointIndex = 0
this.loadStationPointOptions()
this.clearStationPoint()
if (!this.isDenseWarehouse) {
this.loadStationPointOptions()
}
}
},
onLoad(options) {
@@ -254,7 +286,7 @@
this.stationPointId = decodeURIComponent(options.stationPointId)
}
this.loadWarehouseOptions()
if (this.warehouseId) {
if (this.warehouseId && this.trayCode && !this.isDenseWarehouse) {
this.loadStationPointOptions()
}
if (this.stockinId) {
@@ -271,7 +303,7 @@
getWarehouseOptions().then(function(res) {
that.warehouseOptions = res.data || res.rows || []
that.syncWarehouseIndex()
if (that.warehouseId) {
if (that.warehouseId && that.trayCode && !that.isDenseWarehouse) {
that.loadStationPointOptions()
}
}).catch(function() {
@@ -305,29 +337,30 @@
this.warehouseIndex = index
this.warehouseId = selected.warehouseId
this.warehouseName = selected.warehouseName
this.clearStationPoint()
if (!this.isDenseWarehouse) {
this.loadStationPointOptions()
}
},
clearStationPoint() {
this.stationPointId = ''
this.stationPointName = ''
this.stationPointIndex = 0
this.loadStationPointOptions()
this.stationPointOptions = []
},
loadStationPointOptions() {
var that = this
if (!this.warehouseId || !this.trayCode) {
this.stationPointOptions = []
this.stationPointId = ''
this.stationPointName = ''
this.stationPointIndex = 0
if (this.isDenseWarehouse || !this.warehouseId || !this.trayCode) {
this.clearStationPoint()
return
}
getInboundStationPointOptions(this.warehouseId, this.trayCode).then(function(res) {
that.stationPointOptions = res.data || res.rows || []
that.syncStationPointIndex()
}).catch(function() {
that.stationPointOptions = []
that.stationPointId = ''
that.stationPointName = ''
that.stationPointIndex = 0
that.clearStationPoint()
})
},
@@ -525,7 +558,7 @@
this.$modal.msg('请选择仓库')
return
}
if (!this.stationPointId) {
if (!this.isDenseWarehouse && !this.stationPointId) {
this.$modal.msg('请选择入库点位')
return
}
@@ -536,7 +569,6 @@
var data = {
trayCode: this.trayCode,
warehouseId: this.warehouseId,
stationPointId: this.stationPointId,
items: this.items.map(function(i) {
return {
stockinDetailId: i.stockinDetailId,
@@ -545,31 +577,47 @@
}
})
}
if (!this.isDenseWarehouse) {
data.stationPointId = this.stationPointId
}
var that = this
this.$modal.confirm('是否确认提交绑盘信息?').then(function() {
return that.$modal.confirm('确认后将立即生成入库作业,是否继续?')
}).then(function() {
var confirmContent = this.isDenseWarehouse
? '确认将当前 ' + this.items.length + ' 种物料绑定到托盘 ' + this.trayCode + ',并生成密集库待执行作业?'
: '确认将当前 ' + this.items.length + ' 种物料绑定到托盘 ' + this.trayCode + ',并生成入库作业?'
this.$modal.confirm(confirmContent, '确认绑盘').then(function() {
that.submitting = true
return submitBindTrayAndAddInJob(data)
}).then(function(res) {
that.$modal.msgSuccess(res.msg || '绑盘并生成入库作业成功')
that.resetBindPayload()
that.submitting = false
that.bindSuccess = true
that.successDescription = that.isDenseWarehouse
? '密集库入库作业已生成,当前为待执行状态,等待 WCS 主动获取。'
: '托盘绑定完成,入库作业已生成并进入仓库执行流程。'
that.showSuccessDialog = true
if (that.stockinId) {
that.loadDetailList()
}
}).catch(function() {})
}).catch(function() {
that.submitting = false
})
},
continueBinding() {
this.showSuccessDialog = false
this.resetBindPayload()
},
resetBindPayload() {
this.stationPointId = ''
this.stationPointName = ''
this.stationPointIndex = 0
this.stationPointOptions = []
this.clearStationPoint()
this.trayCode = ''
this.barcode = ''
this.currentMaterial = null
this.currentRemainQty = 0
this.currentQty = ''
this.showQuantityPopup = false
this.showSuccessDialog = false
this.successDescription = ''
this.submitting = false
this.bindSuccess = false
this.items = []
},
@@ -581,16 +629,16 @@
this.warehouseId = ''
this.warehouseName = ''
this.warehouseIndex = 0
this.stationPointId = ''
this.stationPointName = ''
this.stationPointIndex = 0
this.stationPointOptions = []
this.clearStationPoint()
this.trayCode = ''
this.barcode = ''
this.currentMaterial = null
this.currentRemainQty = 0
this.currentQty = ''
this.showQuantityPopup = false
this.showSuccessDialog = false
this.successDescription = ''
this.submitting = false
this.bindSuccess = false
this.detailList = []
this.items = []
@@ -1098,4 +1146,169 @@
color: #fff;
background: #1890ff;
}
.success-dialog-mask {
position: fixed;
inset: 0;
z-index: 1200;
display: flex;
align-items: center;
justify-content: center;
padding: 44rpx;
background: rgba(14, 25, 38, 0.62);
backdrop-filter: blur(8rpx);
box-sizing: border-box;
animation: success-mask-in 180ms ease-out both;
}
.success-dialog {
position: relative;
width: 100%;
max-width: 620rpx;
overflow: hidden;
padding: 54rpx 42rpx 38rpx;
border: 1rpx solid rgba(255, 255, 255, 0.88);
border-radius: 28rpx;
background:
linear-gradient(145deg, rgba(236, 252, 243, 0.92) 0, rgba(255, 255, 255, 0) 42%),
#fff;
box-shadow: 0 32rpx 90rpx rgba(5, 18, 31, 0.28);
box-sizing: border-box;
animation: success-card-in 260ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
.success-dialog::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 8rpx;
background: linear-gradient(90deg, #12a66a 0%, #46c68b 55%, #bcefd4 100%);
}
.success-mark {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 112rpx;
height: 112rpx;
margin: 0 auto 28rpx;
border-radius: 50%;
background: #0f9f63;
box-shadow: 0 14rpx 30rpx rgba(15, 159, 99, 0.24);
}
.success-mark::after {
content: '';
position: absolute;
inset: -14rpx;
border: 2rpx solid rgba(15, 159, 99, 0.14);
border-radius: 50%;
}
.success-check {
width: 48rpx;
height: 25rpx;
margin-top: -8rpx;
border-left: 8rpx solid #fff;
border-bottom: 8rpx solid #fff;
transform: rotate(-45deg);
}
.success-kicker,
.success-title,
.success-description {
display: block;
text-align: center;
}
.success-kicker {
margin-bottom: 12rpx;
color: #0f9f63;
font-size: 20rpx;
font-weight: 700;
letter-spacing: 3rpx;
}
.success-title {
color: #132235;
font-size: 36rpx;
font-weight: 700;
line-height: 1.35;
}
.success-description {
margin: 18rpx auto 30rpx;
color: #607083;
font-size: 26rpx;
line-height: 1.7;
}
.success-summary {
margin-bottom: 30rpx;
padding: 6rpx 24rpx;
border: 1rpx solid #e6eee9;
border-radius: 16rpx;
background: rgba(247, 250, 248, 0.9);
}
.success-summary-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 24rpx;
padding: 18rpx 0;
}
.success-summary-row + .success-summary-row {
border-top: 1rpx solid #e6eee9;
}
.success-summary-label {
flex-shrink: 0;
color: #8692a0;
font-size: 24rpx;
}
.success-summary-value {
color: #213247;
font-size: 25rpx;
font-weight: 600;
text-align: right;
word-break: break-all;
}
.success-action {
height: 82rpx;
line-height: 82rpx;
border: 0;
border-radius: 14rpx;
color: #fff;
background: #132235;
box-shadow: 0 12rpx 24rpx rgba(19, 34, 53, 0.18);
font-size: 28rpx;
font-weight: 600;
}
.success-action::after {
border: 0;
}
@keyframes success-mask-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes success-card-in {
from {
opacity: 0;
transform: translateY(28rpx) scale(0.96);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
</style>