<template>
|
<div class="picking-container">
|
<!-- 出库单列表 -->
|
<div class="card mb-4" v-for="order in pickingOrders" :key="order.outStockOrderId">
|
<div class="card-header bg-light d-flex justify-content-between align-items-center">
|
<div>
|
<h5 class="mb-0">出库单: {{ order.orderNumber }}</h5>
|
<small class="text-muted">
|
需求: {{ order.totalRequiredQuantity }} |
|
已锁定: {{ order.totalLockedQuantity }}
|
</small>
|
</div>
|
<button @click="confirmOrder(order)" class="btn btn-primary btn-sm"
|
:disabled="!canConfirmOrder(order)">
|
<i class="fas fa-check"></i> 确认整单出库
|
</button>
|
</div>
|
|
<div class="card-body p-0">
|
<div class="table-responsive">
|
<table class="table table-hover mb-0">
|
<thead>
|
<tr>
|
<th width="5%">#</th>
|
<th>产品名称</th>
|
<th>批号/条码</th>
|
<th>出库数量</th>
|
<th>库存数量</th>
|
<th>库位</th>
|
<th width="20%">操作</th>
|
</tr>
|
</thead>
|
<tbody>
|
<tr v-for="(item, index) in order.pickingItems" :key="item.outStockLockId"
|
:class="{
|
'table-success': item.status === 'Confirmed',
|
'table-warning': needsSplit(item)
|
}">
|
<td>{{ index + 1 }}</td>
|
<td>{{ item.productName }}</td>
|
<td>
|
<div>
|
<strong>{{ item.batchNumber }}</strong>
|
<br>
|
<small class="text-muted">{{ item.barCode }}</small>
|
</div>
|
</td>
|
<td>
|
<span class="font-weight-bold text-primary">
|
{{ item.outStockQuantity }}
|
</span>
|
<small class="text-muted ml-1">{{ item.packageUnit }}</small>
|
</td>
|
<td>
|
<span :class="{
|
'text-success': item.isWholePackage,
|
'text-warning': needsSplit(item)
|
}">
|
{{ item.stockQuantity }} {{ item.packageUnit }}
|
</span>
|
</td>
|
<td>{{ item.location }}</td>
|
<td>
|
<!-- 整包直接确认 -->
|
<button v-if="item.isWholePackage && item.status === 'Pending'"
|
@click="confirmWholePicking(item)"
|
class="btn btn-success btn-sm"
|
:disabled="loading">
|
<i class="fas fa-check"></i> 确认
|
</button>
|
|
<!-- 需要拆包的情况 -->
|
<div v-else-if="needsSplit(item) && item.status === 'Pending'">
|
<button @click="showSplitModal(item)"
|
class="btn btn-warning btn-sm mb-1"
|
:disabled="loading">
|
<i class="fas fa-cube"></i> 拆包
|
</button>
|
<div class="small text-muted">
|
需拆包: {{ item.outStockQuantity }}/{{ item.stockQuantity }}
|
</div>
|
</div>
|
|
<!-- 已确认状态 -->
|
<span v-else-if="item.status === 'Confirmed'" class="text-success">
|
<i class="fas fa-check-circle"></i> 已确认
|
</span>
|
</td>
|
</tr>
|
</tbody>
|
</table>
|
</div>
|
</div>
|
</div>
|
|
<!-- 拆包模态框 -->
|
<div class="modal fade" id="splitModal" tabindex="-1">
|
<div class="modal-dialog">
|
<div class="modal-content">
|
<div class="modal-header">
|
<h5 class="modal-title">拆包操作</h5>
|
<button type="button" class="close" data-dismiss="modal">
|
<span>×</span>
|
</button>
|
</div>
|
<div class="modal-body">
|
<div v-if="currentSplitItem">
|
<div class="form-group">
|
<label>产品名称</label>
|
<input type="text" class="form-control" :value="currentSplitItem.productName" readonly>
|
</div>
|
<div class="form-group">
|
<label>原条码</label>
|
<input type="text" class="form-control" :value="currentSplitItem.barCode" readonly>
|
</div>
|
<div class="row">
|
<div class="col-6">
|
<div class="form-group">
|
<label>原库存数量</label>
|
<input type="text" class="form-control" :value="currentSplitItem.stockQuantity" readonly>
|
</div>
|
</div>
|
<div class="col-6">
|
<div class="form-group">
|
<label>出库数量</label>
|
<input type="text" class="form-control" :value="currentSplitItem.outStockQuantity" readonly>
|
</div>
|
</div>
|
</div>
|
<div class="form-group">
|
<label>拆包数量</label>
|
<input type="number" class="form-control" v-model="splitQuantity"
|
:max="currentSplitItem.stockQuantity - 0.01" step="0.01" min="0.01">
|
<small class="form-text text-muted">
|
拆出数量: {{ splitQuantity }} |
|
剩余数量: {{ (currentSplitItem.stockQuantity - splitQuantity).toFixed(2) }}
|
</small>
|
</div>
|
</div>
|
</div>
|
<div class="modal-footer">
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
|
<button type="button" class="btn btn-warning" @click="confirmSplit"
|
:disabled="!splitQuantity || splitQuantity <= 0">
|
<i class="fas fa-cube"></i> 确认拆包
|
</button>
|
</div>
|
</div>
|
</div>
|
</div>
|
|
<!-- 操作提示 -->
|
<div v-if="message" :class="['alert', messageType === 'success' ? 'alert-success' : 'alert-danger', 'mt-3']">
|
{{ message }}
|
</div>
|
</div>
|
</template>
|
|
<script>
|
export default {
|
data() {
|
return {
|
pickingOrders: [],
|
loading: false,
|
message: '',
|
messageType: 'success',
|
currentSplitItem: null,
|
splitQuantity: 0
|
}
|
},
|
mounted() {
|
this.loadPickingList();
|
// 初始化模态框
|
$('#splitModal').on('hidden.bs.modal', () => {
|
this.currentSplitItem = null;
|
this.splitQuantity = 0;
|
});
|
},
|
methods: {
|
async loadPickingList() {
|
try {
|
this.loading = true;
|
const response = await this.$http.get('/Picking/PickingList');
|
this.pickingOrders = response.data;
|
} catch (error) {
|
this.showMessage('加载拣选列表失败', 'error');
|
} finally {
|
this.loading = false;
|
}
|
},
|
|
// 判断是否需要拆包
|
needsSplit(item) {
|
return !item.isWholePackage && item.outStockQuantity <= item.stockQuantity;
|
},
|
|
// 整包确认
|
async confirmWholePicking(item) {
|
try {
|
this.loading = true;
|
const response = await this.$http.post('/Picking/ConfirmWholePicking', {
|
outStockLockId: item.outStockLockId
|
});
|
|
if (response.data.success) {
|
this.showMessage(response.data.message, 'success');
|
this.loadPickingList();
|
} else {
|
this.showMessage(response.data.message, 'error');
|
}
|
} catch (error) {
|
this.showMessage('操作失败', 'error');
|
} finally {
|
this.loading = false;
|
}
|
},
|
|
// 显示拆包模态框
|
showSplitModal(item) {
|
this.currentSplitItem = item;
|
this.splitQuantity = item.outStockQuantity; // 默认拆出数量为出库数量
|
$('#splitModal').modal('show');
|
},
|
|
// 确认拆包
|
async confirmSplit() {
|
if (!this.splitQuantity || this.splitQuantity <= 0) {
|
this.showMessage('请输入有效的拆包数量', 'error');
|
return;
|
}
|
|
try {
|
this.loading = true;
|
const response = await this.$http.post('/Picking/SplitPackage', {
|
outStockLockId: this.currentSplitItem.outStockLockId,
|
splitQuantity: this.splitQuantity
|
});
|
|
if (response.data.success) {
|
this.showMessage(`拆包成功!新条码: ${response.data.data.newBarCode}`, 'success');
|
$('#splitModal').modal('hide');
|
this.loadPickingList();
|
} else {
|
this.showMessage(response.data.message, 'error');
|
}
|
} catch (error) {
|
this.showMessage('拆包失败', 'error');
|
} finally {
|
this.loading = false;
|
}
|
},
|
|
// 确认整单出库
|
async confirmOrder(order) {
|
if (!confirm(`确定要确认整个出库单 ${order.orderNumber} 吗?`)) return;
|
|
try {
|
this.loading = true;
|
const response = await this.$http.post('/Picking/ConfirmOutStockOrder', {
|
outStockOrderId: order.outStockOrderId
|
});
|
|
if (response.data.success) {
|
this.showMessage(response.data.message, 'success');
|
this.loadPickingList();
|
} else {
|
this.showMessage(response.data.message, 'error');
|
}
|
} catch (error) {
|
this.showMessage('操作失败', 'error');
|
} finally {
|
this.loading = false;
|
}
|
},
|
|
// 检查是否可以确认整单
|
canConfirmOrder(order) {
|
return order.pickingItems.every(item =>
|
item.status === 'Pending' &&
|
(item.isWholePackage || this.needsSplit(item))
|
);
|
},
|
|
showMessage(msg, type) {
|
this.message = msg;
|
this.messageType = type;
|
setTimeout(() => {
|
this.message = '';
|
}, 3000);
|
}
|
}
|
}
|
</script>
|
|
<style scoped>
|
.picking-container {
|
max-width: 1400px;
|
margin: 0 auto;
|
}
|
|
.table td {
|
vertical-align: middle;
|
}
|
|
.btn-sm {
|
min-width: 80px;
|
}
|
|
.badge {
|
font-family: monospace;
|
}
|
</style>
|