feat:产品出库-产品添加扫码录入功能

master
黄伟杰 13 hours ago
parent f1b6d2d3ec
commit 86c656b02f

@ -4,14 +4,20 @@
<view class="top-action-bar">
<view class="scan-input-row">
<input
id="product-outbound-scan-input"
class="scan-input"
v-model="scanCodeInput"
placeholder="&#32418;&#22806;&#25195;&#30721;&#25110;&#36755;&#20837;&#20135;&#21697;&#30721;"
confirm-type="done"
@confirm="onScanInputConfirm"
/>
<view class="scan-input-wrap">
<input
id="product-outbound-scan-input"
class="scan-input"
v-model="scanCodeInput"
placeholder="请选择产品"
confirm-type="done"
@input="onScanInput"
@confirm="onScanInputConfirm"
/>
<view class="scan-input-icon">
<uni-icons type="scan" size="20" color="#9ca3af"></uni-icons>
</view>
</view>
</view>
<view class="scan-btn" @click="handleAddProduct">
<text class="btn-text">&#36873;&#25321;&#20135;&#21697;</text>
@ -160,7 +166,7 @@ import { onReady, onShow } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import NavBar from '@/components/common/NavBar.vue'
import { createProductOutbound } from '@/api/mes/productOutbound'
import { getProduct, getProductPage } from '@/api/erp/productInfo'
import { getProduct } from '@/api/erp/productInfo'
import { setDefaultOperatorFromCurrentUser } from '@/utils/currentUser'
const { t } = useI18n()
@ -173,6 +179,9 @@ const attachmentList = ref([])
const scanCodeInput = ref('')
const focusNoKeyboardRef = ref(null)
const keywordInputSelector = '#product-outbound-scan-input input, input#product-outbound-scan-input'
const PRODUCT_CATEGORY_TYPE = 1
const SCAN_AUTO_SEARCH_DELAY = 300
let productScanTimer = null
const totalPalletCount = computed(() => itemList.value.reduce((sum, item) => sum + getPalletCount(item), 0))
const totalPackageCount = computed(() => itemList.value.reduce((sum, item) => sum + (Number(item.packageCount) || 0), 0))
@ -211,52 +220,46 @@ function goSelectOperator() {
getApp().globalData._productOutboundUserFrom = 'outbound'
uni.navigateTo({ url: '/pages_function/pages/moldRepair/userSelect?field=operator&from=productOutbound' })
}
function getProductMaterialScanCode(value) {
const text = String(value || '').trim()
const match = text.match(/^PRODUCTMATERIAL[-:]?\s*(.+)$/i)
return match ? match[1].trim() : ''
}
function scheduleProductScanSearch(value) {
if (!getProductMaterialScanCode(value)) return
if (productScanTimer) clearTimeout(productScanTimer)
productScanTimer = setTimeout(() => {
productScanTimer = null
onScanInputConfirm()
}, SCAN_AUTO_SEARCH_DELAY)
}
function onScanInput(e) {
scheduleProductScanSearch(e?.detail?.value ?? scanCodeInput.value)
}
function onScanInputConfirm() {
const code = scanCodeInput.value.trim()
if (productScanTimer) {
clearTimeout(productScanTimer)
productScanTimer = null
}
const code = getProductMaterialScanCode(scanCodeInput.value)
if (!code) return
handleScanCode(code)
}
function normalizeProductPageList(res) {
const root = res && res.data !== undefined ? res.data : res
return Array.isArray(root) ? root : (root?.list || root?.rows || root?.records || [])
}
async function findProductByScanCode(code) {
const rawCode = String(code || '').trim()
const productId = rawCode.toUpperCase().startsWith('PRODUCT-')
? rawCode.replace(/PRODUCT-/i, '')
: (/^\d+$/.test(rawCode) ? rawCode : null)
if (productId) {
try {
const res = await getProduct(productId)
const detail = res?.data || res
if (detail?.id) return detail
} catch (e) {}
}
for (let pageNo = 1; pageNo <= 5; pageNo += 1) {
const res = await getProductPage({ pageNo, pageSize: 100, categoryType: 1 })
const list = normalizeProductPageList(res)
const matched = list.find((item) =>
String(item.id || '') === rawCode ||
String(item.barCode || '') === rawCode ||
String(item.code || '') === rawCode
)
if (matched) {
try {
const detailRes = await getProduct(matched.id)
return { ...matched, ...(detailRes?.data || detailRes || {}) }
} catch (e) {
return matched
}
}
if (list.length < 100) break
}
return null
const id = String(code || '').trim()
if (!/^\d+$/.test(id)) return null
const res = await getProduct(id)
const detail = res?.data || res
if (!detail?.id) return null
if (detail.categoryType && Number(detail.categoryType) !== PRODUCT_CATEGORY_TYPE) return null
return detail
}
async function handleScanCode(code) {
try {
const detail = await findProductByScanCode(code)
if (detail?.id) {
getApp().globalData._productOutboundProductSelectResult = detail
scanCodeInput.value = ''
uni.navigateTo({ url: '/pages_function/pages/productOutbound/productConfirm' })
} else {
uni.showToast({ title: '\u672a\u627e\u5230\u4ea7\u54c1: ' + code, icon: 'none' })
@ -427,8 +430,10 @@ onShow(() => {
<style lang="scss" scoped>
.page-container { min-height: 100vh; background: #f5f7fb; }
.top-action-bar { display: flex; align-items: center; gap: 16rpx; padding: 20rpx 24rpx; background: #ffffff; border-bottom: 1rpx solid #eef2f7; }
.scan-input-row { flex: 1; }
.scan-input { width: 100%; height: 76rpx; padding: 0 20rpx; font-size: 26rpx; color: #333; background: #f8fafc; border: 1rpx solid #e5e7eb; border-radius: 14rpx; box-sizing: border-box; }
.scan-input-row { flex: 1; min-width: 0; }
.scan-input-wrap { position: relative; width: 100%; }
.scan-input { width: 100%; height: 76rpx; padding: 0 64rpx 0 20rpx; font-size: 26rpx; color: #333; background: #f8fafc; border: 1rpx solid #e5e7eb; border-radius: 14rpx; box-sizing: border-box; }
.scan-input-icon { position: absolute; right: 20rpx; top: 0; bottom: 0; display: flex; align-items: center; justify-content: center; pointer-events: none; }
.scan-btn { display: flex; align-items: center; justify-content: center; height: 76rpx; padding: 0 24rpx; border-radius: 14rpx; background: #1f4b79; white-space: nowrap; flex-shrink: 0; }
.btn-text { font-size: 26rpx; font-weight: 600; color: #fff; }
.detail-scroll { height: calc(100vh - 172rpx - 116rpx); }

Loading…
Cancel
Save