Files
FT541-WMS/RuoYi-App-master/pages/in/bindMany.vue
2026-01-08 10:29:44 +08:00

261 lines
9.9 KiB
Vue
Raw 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="container">
<view class="form card">
<view class="field">
<text class="input-label">仓库选择</text>
<uni-data-select
v-model="selectedWarehouseId"
:localdata="warehouseOptions"
placeholder="请选择仓库"
class="select-input"
/>
</view>
<view class="field">
<text class="label">托盘条码</text>
<uni-easyinput ref="trayInput" v-model="trayCode" @input="onTrayInput" placeholder="请输入托盘条码" :disabled="loadingTray" :clearable="true">
<template #right>
<text class="scan-icon" @click="handleScanTray">📷</text>
</template>
</uni-easyinput>
</view>
<view class="field">
<text class="label">物料条码</text>
<uni-easyinput v-model="materialCode" @input="onMaterialInput" placeholder="请输入物料条码" :clearable="true">
<template #right>
<text class="scan-icon" @click="handleScanMaterial">📷</text>
</template>
</uni-easyinput>
</view>
</view>
<view class="tray-info card" v-if="trayMaterials.length">
<text class="section-title">托盘已绑定物料</text>
<view class="list">
<view class="item" v-for="(m, idx) in trayMaterials" :key="idx">
<view class="item-header">
<text class="name">{{ m.productName || '未知物料' }}</text>
<text class="qty-tag">× {{ m.quantity }}</text>
</view>
<!-- <text class="meta">编码:{{ m.productId}}</text> -->
</view>
</view>
</view>
<view class="tray-info card" v-else>
<text class="section-title">托盘已绑定物料</text>
<view class="empty">暂无数据</view>
</view>
<uni-popup ref="detailPopup" type="bottom">
<view class="popup">
<text class="popup-title">物料详情</text>
<view v-if="materialDetail">
<view class="row"><text class="row-label">物料名称</text><text class="row-value">{{ materialDetail.productName }}</text></view>
<view class="row"><text class="row-label">物料编码</text><text class="row-value">{{ materialDetail.productId }}</text></view>
<view class="row"><text class="row-label">可用数量</text><text class="row-value">{{ materialDetail.quantity }}</text></view>
<view class="row">
<text class="row-label">绑定数量</text>
<uni-easyinput v-model="bindQty" type="number" placeholder="输入数量" />
</view>
<view class="actions">
<button class="btn primary" @click="handleBindTray" :disabled="isBinding">绑定托盘</button>
<button class="btn secondary" @click="closePopup">取消</button>
</view>
</view>
</view>
</uni-popup>
<!-- 页脚操作区:确认入库 -->
<view class="footer-actions">
<button class="confirm-btn" @click="handleConfirmIn" :disabled="!trayCode || isConfirming">
{{ isConfirming ? '入库中...' : '确认入库' }}
</button>
</view>
</view>
</template>
<script>
import { getTrayInfo, bindTray, getOrderGoods, addInJob } from '@/api/in/order.js'
export default {
data() {
return {
trayCode: '',
materialCode: '',
trayMaterials: [],
loadingTray: false,
materialDetail: null,
bindQty: null,
isBinding: false,
// 入库确认相关
isConfirming: false,
selectedEntrance: '',
warehouseOptions:[
{ value: '01', text: '三联' },
{ value: '02', text: '二联' }
],
selectedWarehouseId:""
}
},
methods: {
mapTrayMaterials(res) {
const list = Array.isArray(res.data) ? res.data : (Array.isArray(res.rows) ? res.rows : (res.data.rows || []))
return list.map(item => ({
productId: item.productId ,
materialDesc: item.productName ,
quantity: item.quantity,
}))
},
async onTrayInput(val) {
this.trayCode = String(val || '').trim()
if (!this.trayCode) { this.trayMaterials = []; return }
try {
this.loadingTray = true
const res = await getTrayInfo(this.trayCode)
this.trayMaterials = res.data
} catch (e) {
console.error('getTrayInfo error', e)
uni.showToast({ title: '查询托盘失败', icon: 'none' })
} finally {
this.loadingTray = false
}
},
// 确认入库
async handleConfirmIn() {
if (!this.trayCode) {
uni.showToast({ title: '请先输入托盘条码', icon: 'none' })
return
}
if (!this.selectedWarehouseId) {
uni.showToast({ title: '请选择仓库', icon: 'none' })
return
}
try {
this.isConfirming = true
const payload = {
trayCode: this.trayCode,
warehouseId:this.selectedWarehouseId
}
const resp = await addInJob(payload)
if (resp && resp.code === 200) {
uni.showToast({ title: '入库成功', icon: 'success' })
// 清空托盘与列表,便于下一次操作
this.trayCode = ''
this.trayMaterials = []
} else {
uni.showToast({ title: (resp && resp.msg) || '入库失败', icon: 'none' })
}
} catch (e) {
console.error('addInJob error', e)
uni.showToast({ title: '入库失败', icon: 'none' })
} finally {
this.isConfirming = false
}
},
extractMaterialDetail(res) {
const pickFirst = (r) => {
if (Array.isArray(r)) return r[0]
if (Array.isArray(r?.rows)) return r.rows[0]
if (Array.isArray(r?.data)) return r.data[0]
if (Array.isArray(r?.data?.rows)) return r.data.rows[0]
return r
}
const raw = pickFirst(res)
if (!raw) return null
return {
stockinDetailId: raw.stockinDetailId || raw.id || raw.detailId,
productName: raw.productName || raw.materialName || raw.extraField?.materialDesc || '',
productId: raw.productId || raw.materialId || raw.productCode || raw.extraField?.productId || '',
quantity: raw.quantity ?? raw.availableQty ?? raw.inQuantity ?? 0,
}
},
async onMaterialInput(val) {
this.materialCode = String(val || '').trim()
if (!this.materialCode) return
try {
const res = await getOrderGoods({ stockinDetailId: this.materialCode })
const detail = res.data
if (!detail) {
uni.showToast({ title: '未找到物料信息', icon: 'none' })
return
}
this.materialDetail = detail
this.bindQty = detail.quantityX
this.$refs.detailPopup.open()
} catch (e) {
console.error('get material detail error', e)
uni.showToast({ title: '查询物料失败', icon: 'none' })
}
},
async handleBindTray() {
if (!this.trayCode) { uni.showToast({ title: '请先输入托盘条码', icon: 'none' }); return }
if (!this.materialDetail?.stockinDetailId) { uni.showToast({ title: '物料信息缺失', icon: 'none' }); return }
try {
this.isBinding = true
const payload = { trayCode: this.trayCode, stockinDetailId: this.materialCode, inQuantity: this.bindQty }
const resp = await bindTray(payload)
if (resp.code === 200) {
uni.showToast({ title: '绑定成功', icon: 'success' })
this.$refs.detailPopup.close()
this.materialCode = ''
await this.onTrayInput(this.trayCode)
} else {
uni.showToast({ title: resp.msg || '绑定失败', icon: 'none' })
}
} catch (e) {
console.error('bindTray error', e)
uni.showToast({ title: resp.msg, icon: 'none' })
} finally {
this.isBinding = false
}
},
closePopup() { this.$refs.detailPopup.close() },
handleScanTray() {
uni.scanCode({
success: (res) => {
this.trayCode = res.result || ''
this.onTrayInput(this.trayCode)
}
})
},
handleScanMaterial() {
uni.scanCode({
success: (res) => {
this.materialCode = res.result || ''
this.onMaterialInput(this.materialCode)
}
})
}
}
}
</script>
<style>
.container { padding: 12px; background: #f6f7fb; min-height: 100vh; }
.card { background: #fff; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.06); padding: 12px; margin-bottom: 12px; }
.form { display: flex; flex-direction: column; gap: 12px; }
.field { display: flex; flex-direction: column; gap: 6px; }
.label { color: #666; font-size: 12px; }
.scan-icon { font-size: 16px; padding: 0 8px; color: #2979ff; }
.section-title { font-size: 14px; font-weight: 600; margin-bottom: 8px; }
.list { margin-top: 4px; display: flex; flex-direction: column; gap: 10px; }
.item { padding: 10px; border: 1px solid #eee; border-radius: 8px; }
.item-header { display: flex; justify-content: space-between; align-items: center; }
.name { font-size: 14px; color: #333; }
.qty-tag { background: #eef3ff; color: #2979ff; padding: 2px 6px; border-radius: 12px; font-size: 12px; }
.meta { color: #888; font-size: 12px; margin-top: 6px; }
.empty { color: #999; font-size: 12px; padding: 12px; text-align: center; }
.popup { padding: 16px; background: #fff; border-top-left-radius: 12px; border-top-right-radius: 12px; }
.popup-title { font-weight: 600; font-size: 16px; margin-bottom: 10px; }
.row { margin: 8px 0; display: flex; justify-content: space-between; }
.row-label { color: #666; }
.row-value { color: #333; }
.actions { display: flex; gap: 10px; margin-top: 12px; }
.btn { flex: 1; padding: 10px; border-radius: 8px; }
.primary { background: #2979ff; color: #fff; }
.secondary { background: #f1f1f1; color: #333; }
/* 页脚操作区 */
.footer-actions { position: fixed; left: 0; right: 0; bottom: 0; padding: 10px 12px; background: #fff; box-shadow: 0 -2px 10px rgba(0,0,0,0.06); }
.confirm-btn { width: 100%; padding: 12px; border-radius: 8px; background: #07c160; color: #fff; font-size: 16px; }
</style>