Files
FTC25009/pda/pages/warehouse/outbound/confirm.vue

434 lines
10 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="page-container">
<!-- 任务信息 -->
<view class="task-info" v-if="taskNo">
<text class="task-label">出库任务编号</text>
<text class="task-value">{{ taskNo }}</text>
</view>
<!-- 扫码区域 -->
<view class="scan-section">
<view class="scan-header">
<text class="scan-title">扫描托盘条码</text>
<view class="scan-btn" @click="startScan">
<uni-icons type="scan" size="18" color="#1890ff"></uni-icons>
<text class="scan-btn-text">扫码</text>
</view>
</view>
<view class="input-wrap">
<input class="scan-input" v-model="barcode" placeholder="请输入或扫描托盘条码"
@confirm="handleBarcode" focus />
</view>
</view>
<!-- 出库信息 -->
<view class="form-section" v-if="outboundInfo">
<text class="form-title">出库信息</text>
<view class="info-row">
<text class="info-label">托盘号</text>
<text class="info-value">{{ outboundInfo.trayCode }}</text>
</view>
<view class="info-row">
<text class="info-label">库位</text>
<text class="info-value">{{ outboundInfo.locationCode }}</text>
</view>
<view class="info-row">
<text class="info-label">出库数量</text>
<text class="info-value highlight">{{ outboundInfo.quantity }}</text>
</view>
<view class="info-row">
<text class="info-label">出库口</text>
<text class="info-value">{{ outboundInfo.portName }}</text>
</view>
</view>
<!-- 托盘物料信息 -->
<view class="form-section" v-if="outboundInfo">
<text class="form-title">托盘物料信息</text>
<view class="material-card" v-for="(item, index) in outboundInfo.materials" :key="item.jobDetailId || index">
<view class="material-header">
<text class="material-code">{{ item.materialCode }}</text>
<text class="material-qty">出库 {{ item.quantity }}</text>
</view>
<view class="material-name">{{ item.materialName }}</view>
<view class="material-meta">
<text>批次:{{ item.lotNo || '-' }}</text>
<text>库存:{{ item.stockQuantity || 0 }}</text>
<text>剩余:{{ item.residualQuantity || 0 }}</text>
</view>
<view class="material-confirm">
<text class="material-confirm-label">实际出库</text>
<input class="material-input" v-model="item.actualQuantity" type="number"
:placeholder="'计划 ' + item.quantity" />
</view>
</view>
</view>
<!-- 确认信息 -->
<view class="form-section" v-if="outboundInfo">
<text class="form-title">确认信息</text>
<view class="form-item">
<text class="form-label">实际总数</text>
<text class="info-value highlight">{{ calculateConfirmQty() }}</text>
</view>
<view class="form-item">
<text class="form-label">备注</text>
<input class="form-input" v-model="form.remark" placeholder="选填" />
</view>
</view>
<!-- 操作按钮 -->
<view class="btn-section" v-if="outboundInfo">
<button class="btn btn-primary" @click="confirmOutbound">确认出库</button>
<button class="btn btn-default" @click="resetForm">重置</button>
</view>
</view>
</template>
<script>
import { getOutboundDetail, confirmOutbound } from '@/api/warehouse/outbound'
export default {
data() {
return {
taskNo: '',
barcode: '',
outboundInfo: null,
confirmQty: '',
form: {
remark: ''
}
}
},
onLoad(options) {
if (options.taskNo) {
this.taskNo = options.taskNo
}
},
methods: {
startScan() {
var that = this
uni.scanCode({
onlyFromCamera: false,
scanType: ['barCode', 'qrCode'],
success: function(res) {
that.barcode = res.result
that.handleBarcode()
}
})
},
handleBarcode() {
if (!this.barcode) {
this.$modal.msg('请输入托盘条码')
return
}
var that = this
var params = { taskNo: this.taskNo, barcode: this.barcode }
getOutboundDetail(params).then(function(res) {
var data = res.data || {}
var materials = (data.materials || []).map(function(item) {
return Object.assign({}, item, {
actualQuantity: String(item.quantity || '')
})
})
that.outboundInfo = Object.assign({}, data, { materials: materials })
that.confirmQty = that.calculateConfirmQty()
that.$modal.msgSuccess('出库信息获取成功')
}).catch(function() {
that.outboundInfo = null
})
},
confirmOutbound() {
if (!this.outboundInfo || !this.outboundInfo.materials || this.outboundInfo.materials.length === 0) {
this.$modal.msg('未获取到出库物料明细')
return
}
var missing = this.outboundInfo.materials.find(function(item) {
return !item.actualQuantity || Number(item.actualQuantity) <= 0
})
if (missing) {
this.$modal.msg('请输入每个物料的实际出库数量')
return
}
this.confirmQty = this.calculateConfirmQty()
var data = {
taskNo: this.taskNo,
barcode: this.barcode,
trayCode: this.barcode,
quantity: this.confirmQty,
materials: this.outboundInfo.materials.map(function(item) {
return {
jobDetailId: item.jobDetailId,
stockDetailId: item.stockDetailId,
quantity: item.actualQuantity
}
}),
remark: this.form.remark
}
var that = this
confirmOutbound(data).then(function() {
that.$modal.msgSuccess('出库确认成功')
that.resetForm()
})
},
resetForm() {
this.barcode = ''
this.outboundInfo = null
this.confirmQty = ''
this.form = {
remark: ''
}
},
calculateConfirmQty() {
if (!this.outboundInfo || !this.outboundInfo.materials) {
return ''
}
var total = this.outboundInfo.materials.reduce(function(sum, item) {
return sum + Number(item.actualQuantity || 0)
}, 0)
return String(total)
}
}
}
</script>
<style lang="scss" scoped>
page {
background-color: #f5f7fa;
min-height: 100%;
}
.page-container {
padding: 24rpx;
padding-bottom: 60rpx;
}
.task-info {
background: #e6f7ff;
border-radius: 12rpx;
padding: 20rpx 24rpx;
margin-bottom: 24rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.task-label {
font-size: 26rpx;
color: #1890ff;
}
.task-value {
font-size: 26rpx;
color: #333;
font-weight: 500;
}
.scan-section {
background: #fff;
border-radius: 16rpx;
padding: 28rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
}
.scan-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.scan-title {
font-size: 30rpx;
font-weight: 500;
color: #333;
}
.scan-btn {
display: flex;
align-items: center;
padding: 8rpx 20rpx;
border: 1rpx solid #1890ff;
border-radius: 8rpx;
}
.scan-btn-text {
font-size: 24rpx;
color: #1890ff;
margin-left: 8rpx;
}
.input-wrap {
border: 1rpx solid #e8e8e8;
border-radius: 8rpx;
padding: 4rpx;
}
.scan-input {
width: 100%;
font-size: 28rpx;
padding: 16rpx;
word-break: break-all;
min-width: 0;
height: auto;
line-height: 1.4;
box-sizing: border-box;
}
.form-section {
background: #fff;
border-radius: 16rpx;
padding: 28rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
}
.form-title {
display: block;
font-size: 30rpx;
font-weight: 500;
color: #333;
margin-bottom: 24rpx;
padding-bottom: 16rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.form-item {
display: flex;
align-items: center;
padding: 16rpx 0;
}
.form-label {
font-size: 28rpx;
color: #666;
width: 160rpx;
flex-shrink: 0;
}
.form-input {
flex: 1;
width: 0;
min-width: 0;
box-sizing: border-box;
font-size: 28rpx;
border: 1rpx solid #e8e8e8;
border-radius: 8rpx;
padding: 16rpx;
word-break: break-all;
height: auto;
line-height: 1.4;
}
.info-row {
display: flex;
justify-content: space-between;
padding: 16rpx 0;
border-bottom: 1rpx solid #f5f5f5;
}
.info-label {
font-size: 28rpx;
color: #999;
}
.info-value {
font-size: 28rpx;
color: #333;
}
.info-value.highlight {
color: #1890ff;
font-weight: 500;
}
.material-card {
padding: 18rpx 0;
border-bottom: 1rpx solid #f5f5f5;
}
.material-card:last-child {
border-bottom: 0;
}
.material-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10rpx;
}
.material-code {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.material-qty {
font-size: 26rpx;
color: #1890ff;
font-weight: 500;
}
.material-name {
font-size: 26rpx;
color: #666;
margin-bottom: 10rpx;
}
.material-meta {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
font-size: 24rpx;
color: #999;
}
.material-confirm {
display: flex;
align-items: center;
margin-top: 16rpx;
}
.material-confirm-label {
width: 140rpx;
font-size: 26rpx;
color: #666;
}
.material-input {
flex: 1;
width: 0;
min-width: 0;
box-sizing: border-box;
font-size: 26rpx;
border: 1rpx solid #e8e8e8;
border-radius: 8rpx;
padding: 12rpx 16rpx;
height: auto;
line-height: 1.4;
}
.btn-section {
padding: 8rpx 0 24rpx;
}
.btn {
font-size: 30rpx;
border-radius: 12rpx;
margin-bottom: 20rpx;
}
.btn-primary {
background: #1890ff;
color: #fff;
}
.btn-default {
background: #fff;
color: #666;
border: 1rpx solid #d9d9d9;
}
</style>