Files
wq-wms-java/pda/pda/src/views/ReceiveGroup.vue
2026-06-12 13:24:00 +08:00

298 lines
7.5 KiB
Vue

<template>
<div class="page-container">
<van-nav-bar
:title="t('receiveGroup.title')"
left-arrow
@click-left="router.push('/')"
/>
<div class="page-content">
<van-form ref="formRef">
<van-field
v-model="barcode"
:label="t('receiveGroup.barcode')"
:placeholder="t('receiveGroup.barcodePlaceholder')"
clearable
@blur="onBarcodeBlur"
@keyup.enter="onBarcodeBlur"
>
<template #right-icon>
<van-icon name="scan" class="scan-icon" @click="onScanBarcode" />
</template>
</van-field>
<van-field
v-model="motherTray"
:label="t('receiveGroup.motherTray')"
:placeholder="t('receiveGroup.motherTrayPlaceholder')"
:rules="[{ required: true, message: t('receiveGroup.motherTrayRequired') }]"
name="motherTray"
clearable
>
<template #right-icon>
<van-icon name="scan" class="scan-icon" @click="onScanMotherTray" />
</template>
</van-field>
<van-field
v-model="sectCode"
:label="t('receiveGroup.sectCode')"
readonly
is-link
/>
</van-form>
<div class="detail-section">
<div class="detail-header">
<span class="detail-header-text">{{ t('receiveGroup.total', detailList.length) }}</span>
</div>
<div v-if="!detailList.length" class="detail-empty">
<van-empty :description="t('receiveGroup.noData')" image="search" />
</div>
<div v-else class="card-list">
<div
v-for="(item, idx) in detailList"
:key="idx"
class="detail-card"
>
<div class="card-header">
<span class="card-index">#{{ idx + 1 }}</span>
<van-icon
name="delete-o"
class="card-delete"
@click="onDeleteItem(idx)"
/>
</div>
<div class="card-body">
<div class="card-row">
<span class="card-label">{{ t('receiveGroup.skuCode') }}</span>
<span class="card-value">{{ item.sku_code }}</span>
</div>
<div class="card-row">
<span class="card-label">{{ t('receiveGroup.skuName') }}</span>
<span class="card-value">{{ item.sku_name }}</span>
</div>
<div class="card-row">
<span class="card-label">{{ t('receiveGroup.orderNo') }}</span>
<span class="card-value">{{ item.order_no }}</span>
</div>
<div class="card-row">
<span class="card-label">{{ t('receiveGroup.itemNo') }}</span>
<span class="card-value">{{ item.item_no }}</span>
</div>
<div class="card-row">
<span class="card-label">{{ t('receiveGroup.qty') }}</span>
<span class="card-value card-value--num">{{ item.qty }}</span>
</div>
<div class="card-row">
<span class="card-label">{{ t('receiveGroup.batchNo') }}</span>
<span class="card-value">{{ item.batch_no }}</span>
</div>
<div class="card-row">
<span class="card-label">{{ t('receiveGroup.categoryCode') }}</span>
<span class="card-value">{{ item.category_code }}</span>
</div>
<div class="card-row">
<span class="card-label">{{ t('receiveGroup.categoryName') }}</span>
<span class="card-value">{{ item.category_name }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<BottomButton :text="t('receiveGroup.submit')" :loading="submitting" @click="onSubmit" />
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { showToast, showDialog } from 'vant'
import { useI18n } from '@/i18n'
import BottomButton from '@/components/BottomButton.vue'
import request from '@/utils/request'
const router = useRouter()
const { t } = useI18n()
const barcode = ref('')
const motherTray = ref('')
const sectCode = ref('')
const detailList = ref([])
const formRef = ref()
const submitting = ref(false)
const billId = ref('')
async function fetchInboundInfo(code) {
if (!code) return
try {
const res = await request.get('/api/pda/inbound/getInfoByCode', {
params: { code },
})
const data = res.data || []
if (!data.length) {
showToast({ message: t('receiveGroup.fetchFail'), type: 'fail' })
return
}
if (!billId.value && data[0].bill_id) {
billId.value = data[0].bill_id
}
if (!sectCode.value && data[0].sect_code) {
sectCode.value = data[0].sect_code
}
data.forEach((item) => {
detailList.value.push({ ...item })
})
} catch {
showToast({ message: t('receiveGroup.fetchFail'), type: 'fail' })
}
}
function onBarcodeBlur() {
const code = barcode.value.trim()
if (!code) return
fetchInboundInfo(code)
}
function onScanBarcode() {
onBarcodeBlur()
}
function onScanMotherTray() {
motherTray.value = 'TP' + Date.now().toString().slice(-6)
}
function onDeleteItem(idx) {
showDialog({
title: '',
message: t('receiveGroup.deleteConfirm'),
showCancelButton: true,
}).then(() => {
detailList.value.splice(idx, 1)
}).catch(() => {})
}
async function onSubmit() {
try {
await formRef.value?.validate('motherTray')
} catch {
return
}
if (!detailList.value.length) {
showToast({ message: t('receiveGroup.noData'), type: 'fail' })
return
}
const detailData = detailList.value.map((item) => ({
order_no: item.order_no,
sku_code: item.sku_code,
batch_no: item.batch_no,
qty: item.qty,
}))
submitting.value = true
try {
const res = await request.post('/api/pda/inbound/groupPlate', {
bill_id: billId.value,
vehicle_code: motherTray.value,
sect_code: sectCode.value,
detailList: detailData,
})
showToast({
message: res.message || t('receiveGroup.submitSuccess'),
type: 'success',
})
detailList.value = []
billId.value = ''
sectCode.value = ''
barcode.value = ''
motherTray.value = ''
} catch {
// error already handled by interceptor
} finally {
submitting.value = false
}
}
</script>
<style scoped>
.detail-section {
margin-top: 12px;
}
.detail-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 0;
}
.detail-header-text {
font-size: 14px;
color: #666;
font-weight: 600;
}
.detail-empty {
padding: 20px 0;
}
.card-list {
display: flex;
flex-direction: column;
gap: 10px;
padding-bottom: 10px;
}
.detail-card {
background: #fff;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
background: var(--primary-gradient);
color: #fff;
}
.card-index {
font-size: 14px;
font-weight: 600;
}
.card-delete {
font-size: 20px;
cursor: pointer;
}
.card-body {
padding: 10px 12px;
}
.card-row {
display: flex;
align-items: flex-start;
padding: 4px 0;
font-size: 14px;
line-height: 1.5;
}
.card-label {
flex-shrink: 0;
width: 80px;
color: #999;
}
.card-value {
flex: 1;
color: #333;
word-break: break-all;
}
.card-value--num {
color: var(--primary-color);
font-weight: 600;
}
</style>