<template>
|
<div class="picking-confirm">
|
<div class="page-header">
|
<el-page-header @back="goBack">
|
<template #content>
|
<span class="title">出库拣选确认 - {{ this.$route.query.orderNo }}</span>
|
</template>
|
</el-page-header>
|
</div>
|
|
<div class="content-layout">
|
<!-- 左侧:扫码区域 -->
|
<div class="left-section">
|
<div class="scan-section">
|
<el-alert
|
title="请使用扫码枪扫描托盘码和物料条码,扫码枪带回车功能,扫完物料条码自动确认"
|
type="info"
|
:closable="false"
|
class="scan-alert"
|
/>
|
|
<el-form :model="scanForm" label-width="100px" class="scan-form">
|
<el-form-item label="托盘码" required>
|
<el-input
|
ref="palletInput"
|
v-model="scanForm.palletCode"
|
placeholder="请扫描托盘码"
|
@keyup.enter="handlePalletScan"
|
@blur="loadPalletSummary"
|
clearable
|
/>
|
</el-form-item>
|
|
<el-form-item label="物料条码" required>
|
<el-input
|
ref="materialInput"
|
v-model="scanForm.materialBarcode"
|
placeholder="请扫描物料条码"
|
:disabled="!scanForm.palletCode"
|
@keyup.enter="handleMaterialScan"
|
clearable
|
/>
|
</el-form-item>
|
</el-form>
|
|
<!-- 托盘拣货统计 -->
|
<div v-if="palletSummary" class="pallet-summary">
|
<el-card header="托盘拣货统计">
|
<el-descriptions :column="3" border>
|
<el-descriptions-item label="托盘号">
|
{{ scanForm.palletCode }}
|
</el-descriptions-item>
|
<el-descriptions-item label="未拣货条数">
|
<el-text type="warning">{{ palletSummary.unpickedCount }}</el-text>
|
</el-descriptions-item>
|
<el-descriptions-item label="未拣货总数">
|
<el-text type="danger">{{ palletSummary.unpickedTotal }}</el-text>
|
</el-descriptions-item>
|
</el-descriptions>
|
</el-card>
|
</div>
|
|
<div class="action-buttons">
|
<el-button type="primary" @click="handleConfirm" :loading="confirmLoading">
|
手动确认
|
</el-button>
|
<el-button @click="handleReset">重置</el-button>
|
<el-button @click="$emit('close')">取消</el-button>
|
</div>
|
</div>
|
</div>
|
|
<!-- 右侧:出库详情列表 -->
|
<div class="right-section">
|
<el-card class="outbound-details-card" header="出库详情">
|
<vol-table
|
ref="outboundTable"
|
:table-config="outboundTableConfig"
|
:height="300"
|
/>
|
</el-card>
|
</div>
|
</div>
|
|
<!-- 已分拣记录列表 -->
|
<div class="picked-records">
|
<el-card header="已分拣记录">
|
<vol-table
|
ref="pickedTable"
|
:table-config="pickedTableConfig"
|
:height="300"
|
/>
|
</el-card>
|
</div>
|
</div>
|
</template>
|
|
<script>
|
import http from '@/api/http.js'
|
import { ref, defineComponent } from "vue";
|
import { ElMessage } from "element-plus";
|
import { useRoute } from 'vue-router'
|
|
export default defineComponent({
|
name: 'PickingConfirm',
|
components: {
|
|
},
|
props: {
|
orderNo: {
|
type: String,
|
required: true
|
}
|
},
|
emits: ['confirm', 'close'],
|
data() {
|
return {
|
scanForm: {
|
palletCode: '',
|
materialBarcode: ''
|
},
|
palletSummary: null,
|
confirmLoading: false,
|
pickedTableConfig: {
|
url: '/api/outbound/getPickingRecords',
|
query: { orderNo: this.orderNo },
|
columns: [
|
{ prop: 'TaskNo', label: '任务号', width: 150 },
|
{ prop: 'Barcode', label: '物料条码', width: 150 },
|
{ prop: 'MaterielName', label: '物料名称', width: 150 },
|
{ prop: 'PickQuantity', label: '拣货数量', width: 100 },
|
{ prop: 'LocationCode', label: '货位', width: 120 },
|
{ prop: 'CreateTime', label: '拣货时间', width: 180 }
|
]
|
},
|
// 出库详情表格配置
|
outboundTableConfig: {
|
url: '/api/outbound/getOutboundDetails',
|
query: { orderNo: this.orderNo },
|
columns: [
|
{ prop: 'OrderNo', label: '出库单号', width: 150 },
|
{ prop: 'MaterialCode', label: '物料编号', width: 120 },
|
{ prop: 'MaterialBarcode', label: '物料条码', width: 150 },
|
{ prop: 'BatchNo', label: '批次号', width: 120 },
|
{ prop: 'AssignQuantity', label: '分配出库量', width: 100 },
|
{ prop: 'PalletCode', label: '托盘编号', width: 120 },
|
{ prop: 'Unit', label: '单位', width: 80 }
|
]
|
},
|
orderInfo: {orderNo:''}
|
}
|
},
|
mounted() {
|
this.loadOrderInfo();
|
this.$nextTick(() => {
|
if (this.$refs.palletInput) {
|
this.$refs.palletInput.focus()
|
}
|
})
|
},
|
methods: {
|
loadOrderInfo() {
|
const orderId = this.$route.query.orderId
|
if (!orderId) return
|
|
try {
|
this.http.get(`/api/OutboundOrder/GetById?id=${orderId}`).then(response => {debugger;
|
if (response.status) {
|
this.orderInfo = response.data
|
|
}
|
})
|
} catch (error) {
|
ElMessage.error('加载出库单信息失败')
|
}
|
},
|
goBack() {
|
this.$router.back()
|
},
|
async handlePalletScan() {
|
if (this.scanForm.palletCode) {
|
ElMessage.success(`已扫描托盘: ${this.scanForm.palletCode}`)
|
await this.loadPalletSummary()
|
|
this.$nextTick(() => {
|
if (this.$refs.materialInput) {
|
this.$refs.materialInput.focus()
|
}
|
})
|
}
|
},
|
async handleMaterialScan() {
|
if (!this.scanForm.palletCode) {
|
ElMessage.warning('请先扫描托盘码')
|
this.$refs.palletInput.focus()
|
return
|
}
|
|
if (!this.scanForm.materialBarcode) {
|
ElMessage.warning('请扫描物料条码')
|
return
|
}
|
|
await this.executePickingConfirm()
|
},
|
async loadPalletSummary() {
|
if (!this.scanForm.palletCode) {
|
this.palletSummary = null
|
return
|
}
|
|
try {
|
const result = await http.get('/api/outbound/getPalletPickingSummary', {
|
params: {
|
orderNo: this.orderNo,
|
palletCode: this.scanForm.palletCode
|
}
|
})
|
|
if (result.success) {
|
// 处理统计信息
|
const summary = result.data
|
const assigned = summary.find(x => x.Status === '已分配') || { TotalAssignQty: 0, TotalPickedQty: 0 }
|
const picked = summary.find(x => x.Status === '已拣选') || { TotalPickedQty: 0 }
|
|
this.palletSummary = {
|
unpickedCount: assigned.TotalAssignQty > 0 ? 1 : 0, // 简化计算
|
unpickedTotal: assigned.TotalAssignQty - assigned.TotalPickedQty
|
}
|
}
|
} catch (error) {
|
console.error('加载托盘统计失败:', error)
|
}
|
},
|
async handleConfirm() {
|
if (!this.scanForm.palletCode || !this.scanForm.materialBarcode) {
|
ElMessage.warning('请填写完整的扫码信息')
|
return
|
}
|
|
await this.executePickingConfirm()
|
},
|
async executePickingConfirm() {
|
this.confirmLoading = true
|
|
try {
|
// 先找到对应的出库锁定信息
|
const lockInfoResult = await this.http.get('/api/outbound/getOutStockLockInfo', {
|
params: {
|
orderNo: this.orderNo,
|
palletCode: this.scanForm.palletCode,
|
materialBarcode: this.scanForm.materialBarcode
|
}
|
})
|
|
if (!lockInfoResult.success || !lockInfoResult.data || lockInfoResult.data.length === 0) {
|
ElMessage.error('未找到对应的出库锁定信息')
|
return
|
}
|
|
const lockInfo = lockInfoResult.data[0]
|
|
const request = {
|
outStockLockId: lockInfo.Id,
|
taskNo: `TASK_${Date.now()}`,
|
palletCode: this.scanForm.palletCode,
|
materialBarcode: this.scanForm.materialBarcode,
|
locationCode: lockInfo.LocationCode
|
}
|
|
const result = await this.http.post('/api/outbound/pickingConfirm', request)
|
|
if (result.success) {
|
ElMessage.success('分拣确认成功')
|
this.handleReset()
|
this.$emit('confirm')
|
|
// 刷新表格
|
if (this.$refs.pickedTable) {
|
this.$refs.pickedTable.refresh()
|
}
|
|
// 刷新出库详情表格
|
if (this.$refs.outboundTable) {
|
this.$refs.outboundTable.refresh()
|
}
|
|
// 重新加载托盘统计
|
await this.loadPalletSummary()
|
} else {
|
ElMessage.error(result.ElMessage)
|
}
|
} catch (error) {
|
ElMessage.error('分拣确认失败')
|
} finally {
|
this.confirmLoading = false
|
}
|
},
|
handleReset() {
|
this.scanForm.materialBarcode = ''
|
this.$nextTick(() => {
|
if (this.$refs.materialInput) {
|
this.$refs.materialInput.focus()
|
}
|
})
|
}
|
}
|
})
|
</script>
|
|
<style scoped>
|
.picking-confirm {
|
display: flex;
|
flex-direction: column;
|
height: 70vh;
|
}
|
|
.content-layout {
|
display: flex;
|
gap: 16px;
|
margin-bottom: 16px;
|
flex: 1;
|
min-height: 0; /* 重要:防止flex子元素溢出 */
|
}
|
|
.left-section {
|
flex: 1;
|
display: flex;
|
flex-direction: column;
|
}
|
|
.right-section {
|
flex: 1;
|
display: flex;
|
flex-direction: column;
|
}
|
|
.scan-section {
|
flex-shrink: 0;
|
}
|
|
.scan-alert {
|
margin-bottom: 16px;
|
}
|
|
.scan-form {
|
max-width: 500px;
|
}
|
|
.pallet-summary {
|
margin: 16px 0;
|
}
|
|
.action-buttons {
|
margin-top: 16px;
|
}
|
|
.outbound-details-card {
|
height: 100%;
|
display: flex;
|
flex-direction: column;
|
}
|
|
.outbound-details-card :deep(.el-card__body) {
|
flex: 1;
|
padding: 0;
|
}
|
|
.picked-records {
|
flex-shrink: 0;
|
height: 300px;
|
}
|
|
.picked-records :deep(.el-card__body) {
|
padding: 0;
|
}
|
</style>
|