1315 lines
35 KiB
Vue
1315 lines
35 KiB
Vue
<template>
|
||
<view class="page-container">
|
||
<!-- 任务信息 -->
|
||
<view class="task-info" v-if="stockinId || taskNo">
|
||
<text class="task-label">入库主单</text>
|
||
<text class="task-value">{{ documentN || stockinId || taskNo }}</text>
|
||
</view>
|
||
|
||
<!-- 托盘信息 -->
|
||
<view class="form-section">
|
||
<text class="form-title">托盘信息</text>
|
||
<view class="form-item">
|
||
<text class="form-label">仓库</text>
|
||
<picker class="form-picker" mode="selector" :range="warehouseOptions" range-key="warehouseName"
|
||
:value="warehouseIndex" :disabled="bindSuccess" @change="handleWarehouseChange">
|
||
<view class="picker-value" :class="{ placeholder: !warehouseId }">
|
||
{{ selectedWarehouseName || '请选择仓库' }}
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
<view class="form-item">
|
||
<text class="form-label">托盘号</text>
|
||
<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 || '请选择入库点位' }}
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 绑盘状态提示 -->
|
||
<view class="bind-status" v-if="bindSuccess">
|
||
<view class="bind-status-title">入库作业已生成</view>
|
||
<view class="bind-status-desc">物料已写入托盘绑定信息,并已生成入库作业。</view>
|
||
</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" />
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 主单下待绑盘子单 -->
|
||
<view class="list-section" v-if="stockinId">
|
||
<view class="section-header">
|
||
<text class="section-title">子单物料({{ availableDetailList.length }} 条)</text>
|
||
</view>
|
||
<view class="detail-empty" v-if="availableDetailList.length === 0">
|
||
<text class="empty-text">当前主单暂无待绑盘子单</text>
|
||
</view>
|
||
<view class="detail-card" v-for="(detail, index) in availableDetailList" :key="detail.stockinDetailId || index"
|
||
@click="selectDetail(detail)">
|
||
<view class="detail-row">
|
||
<text class="detail-label">子单号</text>
|
||
<text class="detail-value">{{ detail.stockinDetailId }}</text>
|
||
</view>
|
||
<view class="detail-row">
|
||
<text class="detail-label">物料编码</text>
|
||
<text class="detail-value">{{ detail.productId }}</text>
|
||
</view>
|
||
<view class="detail-row">
|
||
<text class="detail-label">物料名称</text>
|
||
<text class="detail-value">{{ detail.productName }}</text>
|
||
</view>
|
||
<view class="detail-row">
|
||
<text class="detail-label">待绑数量</text>
|
||
<text class="detail-value highlight">{{ getAvailableRemainQty(detail) }}</text>
|
||
</view>
|
||
<view class="detail-action">选择并填写数量</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 数量填写弹窗 -->
|
||
<view class="quantity-modal-mask" v-if="showQuantityPopup" @click="closeQuantityPopup">
|
||
<view class="quantity-modal" @click.stop>
|
||
<view class="modal-header">
|
||
<text class="modal-title">选择并填写数量</text>
|
||
<text class="modal-close" @click="closeQuantityPopup">×</text>
|
||
</view>
|
||
<view class="modal-body" v-if="currentMaterial">
|
||
<view class="info-row">
|
||
<text class="info-label">物料编码</text>
|
||
<text class="info-value">{{ currentMaterial.productId }}</text>
|
||
</view>
|
||
<view class="info-row">
|
||
<text class="info-label">物料名称</text>
|
||
<text class="info-value">{{ currentMaterial.productName }}</text>
|
||
</view>
|
||
<view class="info-row">
|
||
<text class="info-label">子单号</text>
|
||
<text class="info-value">{{ currentMaterial.stockinDetailId }}</text>
|
||
</view>
|
||
<view class="info-row">
|
||
<text class="info-label">入库单号</text>
|
||
<text class="info-value">{{ currentMaterial.stockinId || '-' }}</text>
|
||
</view>
|
||
<view class="info-row">
|
||
<text class="info-label">批次</text>
|
||
<text class="info-value">{{ currentMaterial.lotNo || '-' }}</text>
|
||
</view>
|
||
<view class="info-row">
|
||
<text class="info-label">待绑数量</text>
|
||
<text class="info-value highlight">{{ currentRemainQty }}</text>
|
||
</view>
|
||
<view class="modal-form-item">
|
||
<text class="modal-form-label">本次绑定数量</text>
|
||
<input class="modal-form-input" v-model="currentQty" type="number" placeholder="请修改本次绑定数量" />
|
||
</view>
|
||
</view>
|
||
<view class="modal-actions">
|
||
<button class="modal-btn modal-btn-cancel" @click="closeQuantityPopup">取消</button>
|
||
<button class="modal-btn modal-btn-confirm" @click="addItem">加入列表</button>
|
||
</view>
|
||
</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">
|
||
<text class="section-title">已绑定物料({{ items.length }} 种)</text>
|
||
</view>
|
||
<view class="item-card" v-for="(item, index) in items" :key="index">
|
||
<view class="item-body">
|
||
<view class="item-row">
|
||
<text class="item-label">物料编码</text>
|
||
<text class="item-value">{{ item.productId }}</text>
|
||
</view>
|
||
<view class="item-row">
|
||
<text class="item-label">物料名称</text>
|
||
<text class="item-value">{{ item.productName }}</text>
|
||
</view>
|
||
<view class="item-row">
|
||
<text class="item-label">绑定数量</text>
|
||
<text class="item-value highlight">{{ item.quantity }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="item-delete" :class="{ disabled: bindSuccess }" @click="removeItem(index)">
|
||
<uni-icons type="trash" size="20" color="#f5222d"></uni-icons>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 空状态提示 -->
|
||
<view class="empty-state" v-if="items.length === 0">
|
||
<text class="empty-text">请扫码添加物料</text>
|
||
</view>
|
||
|
||
<!-- 操作按钮 -->
|
||
<view class="btn-section">
|
||
<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>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { getInboundDetailList, getInboundStationPointOptions, getMaterialByDetailId, getWarehouseOptions, submitBindTrayAndAddInJob } from '@/api/warehouse/inbound'
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
taskNo: '',
|
||
stockinId: '',
|
||
documentN: '',
|
||
warehouseId: '',
|
||
warehouseName: '',
|
||
warehouseIndex: 0,
|
||
warehouseOptions: [],
|
||
stationPointId: '',
|
||
stationPointName: '',
|
||
stationPointIndex: 0,
|
||
stationPointOptions: [],
|
||
trayCode: '',
|
||
// 当前扫码
|
||
barcode: '',
|
||
currentMaterial: null,
|
||
currentRemainQty: 0,
|
||
currentQty: '',
|
||
showQuantityPopup: false,
|
||
showSuccessDialog: false,
|
||
successDescription: '',
|
||
submitting: false,
|
||
// 已完成绑盘并生成入库作业时保持页面状态,便于操作人员核对结果。
|
||
bindSuccess: false,
|
||
// 当前主单下待绑盘子单,操作人员选择子单后再修改本次绑定数量。
|
||
detailList: [],
|
||
// 已加入列表的物料
|
||
items: []
|
||
}
|
||
},
|
||
computed: {
|
||
selectedWarehouseName() {
|
||
if (this.warehouseName) {
|
||
return this.warehouseName
|
||
}
|
||
var selected = this.warehouseOptions[this.warehouseIndex]
|
||
return selected && selected.warehouseId === this.warehouseId ? selected.warehouseName : ''
|
||
},
|
||
selectedStationPointName() {
|
||
if (this.stationPointName) {
|
||
return this.stationPointName
|
||
}
|
||
var selected = this.stationPointOptions[this.stationPointIndex]
|
||
return selected && selected.pointId === this.stationPointId ? (selected.pointName || selected.pointId) : ''
|
||
},
|
||
isDenseWarehouse() {
|
||
return this.warehouseId === '03'
|
||
},
|
||
availableDetailList() {
|
||
return this.detailList.filter(function(detail) {
|
||
return this.getAvailableRemainQty(detail) > 0
|
||
}.bind(this))
|
||
}
|
||
},
|
||
watch: {
|
||
trayCode: function() {
|
||
if (this.bindSuccess) {
|
||
return
|
||
}
|
||
this.clearStationPoint()
|
||
if (!this.isDenseWarehouse) {
|
||
this.loadStationPointOptions()
|
||
}
|
||
}
|
||
},
|
||
onLoad(options) {
|
||
if (options.taskNo) {
|
||
this.taskNo = options.taskNo
|
||
}
|
||
if (options.stockinId) {
|
||
this.stockinId = decodeURIComponent(options.stockinId)
|
||
}
|
||
if (options.documentN) {
|
||
this.documentN = decodeURIComponent(options.documentN)
|
||
}
|
||
if (options.warehouseId) {
|
||
this.warehouseId = decodeURIComponent(options.warehouseId)
|
||
}
|
||
if (options.warehouseName) {
|
||
this.warehouseName = decodeURIComponent(options.warehouseName)
|
||
}
|
||
if (options.stationPointId) {
|
||
this.stationPointId = decodeURIComponent(options.stationPointId)
|
||
}
|
||
this.loadWarehouseOptions()
|
||
if (this.warehouseId && this.trayCode && !this.isDenseWarehouse) {
|
||
this.loadStationPointOptions()
|
||
}
|
||
if (this.stockinId) {
|
||
this.loadDetailList()
|
||
}
|
||
if (options.detailId) {
|
||
this.barcode = decodeURIComponent(options.detailId)
|
||
this.handleBarcode()
|
||
}
|
||
},
|
||
methods: {
|
||
loadWarehouseOptions() {
|
||
var that = this
|
||
getWarehouseOptions().then(function(res) {
|
||
that.warehouseOptions = res.data || res.rows || []
|
||
that.syncWarehouseIndex()
|
||
if (that.warehouseId && that.trayCode && !that.isDenseWarehouse) {
|
||
that.loadStationPointOptions()
|
||
}
|
||
}).catch(function() {
|
||
that.warehouseOptions = []
|
||
})
|
||
},
|
||
|
||
syncWarehouseIndex() {
|
||
if (!this.warehouseId || this.warehouseOptions.length === 0) {
|
||
return
|
||
}
|
||
for (var i = 0; i < this.warehouseOptions.length; i++) {
|
||
if (this.warehouseOptions[i].warehouseId === this.warehouseId) {
|
||
this.warehouseIndex = i
|
||
this.warehouseName = this.warehouseOptions[i].warehouseName
|
||
return
|
||
}
|
||
}
|
||
},
|
||
|
||
handleWarehouseChange(e) {
|
||
if (this.bindSuccess) {
|
||
this.$modal.msg('托盘已绑定,不能修改仓库')
|
||
return
|
||
}
|
||
var index = Number(e.detail.value)
|
||
var selected = this.warehouseOptions[index]
|
||
if (!selected) {
|
||
return
|
||
}
|
||
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.stationPointOptions = []
|
||
},
|
||
|
||
loadStationPointOptions() {
|
||
var that = this
|
||
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.clearStationPoint()
|
||
})
|
||
},
|
||
|
||
syncStationPointIndex() {
|
||
if (!this.stationPointId || this.stationPointOptions.length === 0) {
|
||
return
|
||
}
|
||
for (var i = 0; i < this.stationPointOptions.length; i++) {
|
||
if (this.stationPointOptions[i].pointId === this.stationPointId) {
|
||
this.stationPointIndex = i
|
||
this.stationPointName = this.stationPointOptions[i].pointName || this.stationPointOptions[i].pointId
|
||
return
|
||
}
|
||
}
|
||
this.stationPointId = ''
|
||
this.stationPointName = ''
|
||
this.stationPointIndex = 0
|
||
},
|
||
|
||
handleStationPointChange(e) {
|
||
if (this.bindSuccess) {
|
||
this.$modal.msg('托盘已绑定,不能修改入库点位')
|
||
return
|
||
}
|
||
var index = Number(e.detail.value)
|
||
var selected = this.stationPointOptions[index]
|
||
if (!selected) {
|
||
return
|
||
}
|
||
this.stationPointIndex = index
|
||
this.stationPointId = selected.pointId
|
||
this.stationPointName = selected.pointName || selected.pointId
|
||
},
|
||
|
||
loadDetailList() {
|
||
var that = this
|
||
getInboundDetailList({ stockinId: this.stockinId }).then(function(res) {
|
||
that.detailList = res.rows || []
|
||
}).catch(function() {
|
||
that.detailList = []
|
||
})
|
||
},
|
||
|
||
selectDetail(detail) {
|
||
if (this.bindSuccess) {
|
||
this.$modal.msg('托盘已绑定,不能再选择子单')
|
||
return
|
||
}
|
||
this.currentMaterial = detail
|
||
this.barcode = detail.stockinDetailId
|
||
this.currentRemainQty = this.getAvailableRemainQty(detail)
|
||
if (this.currentRemainQty <= 0) {
|
||
this.$modal.msg('该子单待绑数量已满')
|
||
return
|
||
}
|
||
this.currentQty = this.currentRemainQty > 0 ? String(this.currentRemainQty) : ''
|
||
this.showQuantityPopup = true
|
||
},
|
||
|
||
closeQuantityPopup() {
|
||
this.showQuantityPopup = false
|
||
this.currentMaterial = null
|
||
this.currentRemainQty = 0
|
||
this.currentQty = ''
|
||
},
|
||
|
||
// 调用摄像头扫码
|
||
startScan() {
|
||
var that = this
|
||
uni.scanCode({
|
||
onlyFromCamera: false,
|
||
scanType: ['barCode', 'qrCode'],
|
||
success: function(res) {
|
||
that.barcode = res.result
|
||
that.handleBarcode()
|
||
}
|
||
})
|
||
},
|
||
|
||
// 根据入库明细ID查询物料
|
||
handleBarcode() {
|
||
if (!this.barcode) {
|
||
this.$modal.msg('请输入入库子单号')
|
||
return
|
||
}
|
||
var that = this
|
||
getMaterialByDetailId(this.barcode).then(function(res) {
|
||
that.currentMaterial = res.data
|
||
if (res.data && res.data.stockinId && !that.stockinId) {
|
||
that.stockinId = res.data.stockinId
|
||
}
|
||
that.currentRemainQty = that.getAvailableRemainQty(res.data)
|
||
if (that.currentRemainQty <= 0) {
|
||
that.currentMaterial = null
|
||
that.currentQty = ''
|
||
that.showQuantityPopup = false
|
||
that.$modal.msg('该子单待绑数量已满')
|
||
return
|
||
}
|
||
that.currentQty = that.currentRemainQty > 0 ? String(that.currentRemainQty) : ''
|
||
that.showQuantityPopup = true
|
||
that.$modal.msgSuccess('物料信息获取成功')
|
||
}).catch(function() {
|
||
that.currentMaterial = null
|
||
that.currentRemainQty = 0
|
||
that.currentQty = ''
|
||
that.showQuantityPopup = false
|
||
that.$modal.msg('未找到该入库子单信息')
|
||
})
|
||
},
|
||
|
||
getRemainQty(material) {
|
||
var quantity = Number(material && material.quantity) || 0
|
||
var scanNum = Number(material && material.scanNum) || 0
|
||
var assignNum = Number(material && material.assignNum) || 0
|
||
var usedNum = scanNum > 0 ? scanNum : assignNum
|
||
var remainQty = quantity - usedNum
|
||
return remainQty > 0 ? remainQty : 0
|
||
},
|
||
|
||
getLocalBindQty(stockinDetailId) {
|
||
if (this.bindSuccess || !stockinDetailId) {
|
||
return 0
|
||
}
|
||
return this.items.reduce(function(total, item) {
|
||
if (item.stockinDetailId === stockinDetailId) {
|
||
return total + (Number(item.quantity) || 0)
|
||
}
|
||
return total
|
||
}, 0)
|
||
},
|
||
|
||
getAvailableRemainQty(material) {
|
||
var remainQty = this.getRemainQty(material)
|
||
var localBindQty = this.getLocalBindQty(material && material.stockinDetailId)
|
||
var availableQty = remainQty - localBindQty
|
||
return availableQty > 0 ? availableQty : 0
|
||
},
|
||
|
||
// 将当前物料加入绑定列表
|
||
addItem() {
|
||
if (!this.currentMaterial) {
|
||
this.$modal.msg('请先扫码查询物料')
|
||
return
|
||
}
|
||
if (!this.currentQty || Number(this.currentQty) <= 0) {
|
||
this.$modal.msg('请输入有效的绑定数量')
|
||
return
|
||
}
|
||
if (Number(this.currentQty) > Number(this.currentRemainQty)) {
|
||
this.$modal.msg('绑定数量不能超过待绑数量')
|
||
return
|
||
}
|
||
var currentQty = Number(this.currentQty)
|
||
var exists = this.items.find(function(i) {
|
||
return i.stockinDetailId === this.currentMaterial.stockinDetailId
|
||
}.bind(this))
|
||
if (exists) {
|
||
exists.quantity = Number(exists.quantity || 0) + currentQty
|
||
} else {
|
||
this.items.push({
|
||
stockinDetailId: this.currentMaterial.stockinDetailId,
|
||
stockinId: this.currentMaterial.stockinId,
|
||
productId: this.currentMaterial.productId,
|
||
productName: this.currentMaterial.productName,
|
||
lotNo: this.currentMaterial.lotNo,
|
||
quantity: currentQty
|
||
})
|
||
}
|
||
// 清空当前扫码区,继续扫下一个
|
||
this.barcode = ''
|
||
this.currentMaterial = null
|
||
this.currentRemainQty = 0
|
||
this.currentQty = ''
|
||
this.showQuantityPopup = false
|
||
this.$modal.msgSuccess('已加入列表')
|
||
},
|
||
|
||
// 从列表中移除某条物料
|
||
removeItem(index) {
|
||
if (this.bindSuccess) {
|
||
this.$modal.msg('托盘已绑定,不能再移除物料')
|
||
return
|
||
}
|
||
this.items.splice(index, 1)
|
||
},
|
||
|
||
// 提交绑盘
|
||
submitBindTrayAction() {
|
||
if (!this.trayCode) {
|
||
this.$modal.msg('请扫描托盘号')
|
||
return
|
||
}
|
||
if (!this.warehouseId) {
|
||
this.$modal.msg('请选择仓库')
|
||
return
|
||
}
|
||
if (!this.isDenseWarehouse && !this.stationPointId) {
|
||
this.$modal.msg('请选择入库点位')
|
||
return
|
||
}
|
||
if (this.items.length === 0) {
|
||
this.$modal.msg('请至少添加一种物料')
|
||
return
|
||
}
|
||
var data = {
|
||
trayCode: this.trayCode,
|
||
warehouseId: this.warehouseId,
|
||
items: this.items.map(function(i) {
|
||
return {
|
||
stockinDetailId: i.stockinDetailId,
|
||
productId: i.productId,
|
||
quantity: i.quantity
|
||
}
|
||
})
|
||
}
|
||
if (!this.isDenseWarehouse) {
|
||
data.stationPointId = this.stationPointId
|
||
}
|
||
var that = this
|
||
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.submitting = false
|
||
that.bindSuccess = true
|
||
that.successDescription = that.isDenseWarehouse
|
||
? '密集库入库作业已生成,当前为待执行状态,等待 WCS 主动获取。'
|
||
: '托盘绑定完成,入库作业已生成并进入仓库执行流程。'
|
||
that.showSuccessDialog = true
|
||
if (that.stockinId) {
|
||
that.loadDetailList()
|
||
}
|
||
}).catch(function() {
|
||
that.submitting = false
|
||
})
|
||
},
|
||
|
||
continueBinding() {
|
||
this.showSuccessDialog = false
|
||
this.resetBindPayload()
|
||
},
|
||
|
||
resetBindPayload() {
|
||
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 = []
|
||
},
|
||
|
||
// 全部重置
|
||
resetAll() {
|
||
this.stockinId = ''
|
||
this.documentN = ''
|
||
this.warehouseId = ''
|
||
this.warehouseName = ''
|
||
this.warehouseIndex = 0
|
||
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 = []
|
||
}
|
||
}
|
||
}
|
||
</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 {
|
||
font-size: 28rpx;
|
||
padding: 16rpx;
|
||
word-break: break-all;
|
||
width: 100%;
|
||
height: auto;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
/* 通用 form-section */
|
||
.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 {
|
||
font-size: 30rpx;
|
||
font-weight: 500;
|
||
color: #333;
|
||
margin-bottom: 24rpx;
|
||
padding-bottom: 16rpx;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
display: block;
|
||
}
|
||
|
||
.form-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 16rpx 0;
|
||
}
|
||
|
||
.form-tip {
|
||
padding: 8rpx 0 16rpx 160rpx;
|
||
font-size: 24rpx;
|
||
color: #909399;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.form-label {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
width: 160rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.form-input {
|
||
flex: 1;
|
||
font-size: 28rpx;
|
||
border: 1rpx solid #e8e8e8;
|
||
border-radius: 8rpx;
|
||
padding: 16rpx;
|
||
word-break: break-all;
|
||
min-width: 0;
|
||
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;
|
||
}
|
||
|
||
/* 加入列表按钮 */
|
||
.btn-add {
|
||
margin-top: 24rpx;
|
||
background: #1890ff;
|
||
color: #fff;
|
||
border-radius: 12rpx;
|
||
font-size: 28rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 20rpx 0;
|
||
border: none;
|
||
}
|
||
|
||
.btn-add-text {
|
||
font-size: 28rpx;
|
||
color: #fff;
|
||
margin-left: 8rpx;
|
||
}
|
||
|
||
/* 已添加物料列表 */
|
||
.list-section {
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.section-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 30rpx;
|
||
font-weight: 500;
|
||
color: #333;
|
||
}
|
||
|
||
.item-card {
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
padding: 24rpx 28rpx;
|
||
margin-bottom: 16rpx;
|
||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.item-body {
|
||
flex: 1;
|
||
margin-right: 20rpx;
|
||
}
|
||
|
||
.item-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 8rpx 0;
|
||
}
|
||
|
||
.item-label {
|
||
font-size: 26rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.item-value {
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.item-value.highlight {
|
||
color: #1890ff;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.item-delete {
|
||
padding: 8rpx;
|
||
}
|
||
|
||
/* 空状态 */
|
||
.empty-state {
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
padding: 60rpx 0;
|
||
margin-bottom: 24rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.empty-text {
|
||
font-size: 28rpx;
|
||
color: #ccc;
|
||
}
|
||
|
||
/* 底部按钮 */
|
||
.btn-section {
|
||
padding: 8rpx 0 24rpx;
|
||
}
|
||
|
||
.btn {
|
||
font-size: 30rpx;
|
||
border-radius: 12rpx;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: #1890ff;
|
||
color: #fff;
|
||
}
|
||
|
||
.btn-success {
|
||
background: #0fe825;
|
||
color: #fff;
|
||
}
|
||
|
||
.btn-primary[disabled] {
|
||
background: #b0d4f5;
|
||
color: #fff;
|
||
}
|
||
|
||
.btn-default {
|
||
background: #fff;
|
||
color: #666;
|
||
border: 1rpx solid #d9d9d9;
|
||
}
|
||
.form-picker {
|
||
flex: 1;
|
||
min-height: 72rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.picker-value {
|
||
width: 100%;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
padding: 16rpx;
|
||
border: 1rpx solid #e8e8e8;
|
||
border-radius: 8rpx;
|
||
background: #fff;
|
||
}
|
||
|
||
.picker-value.placeholder {
|
||
color: #c0c4cc;
|
||
}
|
||
|
||
.bind-status {
|
||
background: #f6ffed;
|
||
border: 1rpx solid #b7eb8f;
|
||
border-radius: 16rpx;
|
||
padding: 24rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.bind-status-title {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #389e0d;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.bind-status-desc {
|
||
font-size: 26rpx;
|
||
color: #52c41a;
|
||
}
|
||
|
||
.detail-empty {
|
||
padding: 40rpx 0;
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
|
||
.detail-card {
|
||
background: #fafafa;
|
||
border: 1rpx solid #f0f0f0;
|
||
border-radius: 12rpx;
|
||
padding: 22rpx;
|
||
margin-bottom: 18rpx;
|
||
}
|
||
|
||
.detail-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 6rpx 0;
|
||
}
|
||
|
||
.detail-label {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.detail-value {
|
||
flex: 1;
|
||
margin-left: 20rpx;
|
||
text-align: right;
|
||
font-size: 24rpx;
|
||
color: #333;
|
||
word-break: break-all;
|
||
}
|
||
|
||
.detail-action {
|
||
margin-top: 14rpx;
|
||
padding-top: 14rpx;
|
||
border-top: 1rpx solid #f0f0f0;
|
||
font-size: 24rpx;
|
||
color: #1890ff;
|
||
text-align: right;
|
||
}
|
||
|
||
.item-delete.disabled {
|
||
opacity: 0.45;
|
||
}
|
||
/* 仓库下拉和普通输入框在同一表单行内保持等宽。 */
|
||
.form-picker,
|
||
.form-input {
|
||
flex: 1;
|
||
width: 0;
|
||
min-width: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.picker-value {
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
}
|
||
/* uni-app H5 会把 picker 内部再包一层无 class 节点,边框放在外层 picker 才能和输入框等宽。 */
|
||
.form-item .form-picker,
|
||
.form-item uni-picker.form-picker {
|
||
flex: 1;
|
||
width: 0;
|
||
min-width: 0;
|
||
min-height: 72rpx;
|
||
padding: 16rpx;
|
||
border: 1rpx solid #e8e8e8;
|
||
border-radius: 8rpx;
|
||
background: #fff;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.form-item .form-picker .picker-value,
|
||
.form-item uni-picker.form-picker .picker-value {
|
||
display: flex;
|
||
align-items: center;
|
||
width: auto !important;
|
||
min-width: 0;
|
||
padding: 0 !important;
|
||
border: 0 !important;
|
||
background: transparent !important;
|
||
box-sizing: border-box;
|
||
}
|
||
.quantity-modal-mask {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
top: 0;
|
||
bottom: 0;
|
||
z-index: 999;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 32rpx;
|
||
background: rgba(0, 0, 0, 0.45);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.quantity-modal {
|
||
width: 100%;
|
||
max-width: 680rpx;
|
||
max-height: 86vh;
|
||
overflow-y: auto;
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.18);
|
||
}
|
||
|
||
.modal-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 28rpx;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
}
|
||
|
||
.modal-title {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
|
||
.modal-close {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
line-height: 48rpx;
|
||
text-align: center;
|
||
font-size: 40rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.modal-body {
|
||
padding: 24rpx 28rpx;
|
||
}
|
||
|
||
.modal-form-item {
|
||
margin-top: 24rpx;
|
||
}
|
||
|
||
.modal-form-label {
|
||
display: block;
|
||
margin-bottom: 12rpx;
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.modal-form-input {
|
||
width: 100%;
|
||
height: 72rpx;
|
||
padding: 0 20rpx;
|
||
border: 1rpx solid #d9d9d9;
|
||
border-radius: 8rpx;
|
||
font-size: 28rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.modal-actions {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
padding: 20rpx 28rpx 28rpx;
|
||
}
|
||
|
||
.modal-btn {
|
||
flex: 1;
|
||
height: 72rpx;
|
||
line-height: 72rpx;
|
||
border-radius: 8rpx;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.modal-btn-cancel {
|
||
color: #666;
|
||
background: #f5f5f5;
|
||
}
|
||
|
||
.modal-btn-confirm {
|
||
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>
|