|
|
|
|
@ -31,15 +31,16 @@
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<view v-if="relateTask && taskId" class="form-field">
|
|
|
|
|
<text class="form-label">{{ t('productInbound.taskProduct') }}<text class="required-star">*</text></text>
|
|
|
|
|
<text class="form-label">{{ t('productInbound.product') }}<text class="required-star">*</text></text>
|
|
|
|
|
<view class="task-product-search-row">
|
|
|
|
|
<input
|
|
|
|
|
id="product-inbound-task-product-scan-input"
|
|
|
|
|
v-model="taskProductScanInput"
|
|
|
|
|
class="task-product-scan-input"
|
|
|
|
|
type="text"
|
|
|
|
|
:placeholder="t('productInbound.searchTaskProductPlaceholder')"
|
|
|
|
|
:placeholder="t('productInbound.selectProduct')"
|
|
|
|
|
confirm-type="done"
|
|
|
|
|
@input="onTaskProductScanInput"
|
|
|
|
|
@confirm="onTaskProductScanConfirm"
|
|
|
|
|
/>
|
|
|
|
|
<view class="task-product-select-field" @click="goSelectTaskProduct">
|
|
|
|
|
@ -56,8 +57,9 @@
|
|
|
|
|
v-model="productScanInput"
|
|
|
|
|
class="task-product-scan-input"
|
|
|
|
|
type="text"
|
|
|
|
|
:placeholder="t('productInbound.searchProductPlaceholder')"
|
|
|
|
|
:placeholder="t('productInbound.selectProduct')"
|
|
|
|
|
confirm-type="done"
|
|
|
|
|
@input="onProductScanInput"
|
|
|
|
|
@confirm="onProductScanConfirm"
|
|
|
|
|
/>
|
|
|
|
|
<view class="task-product-select-field" @click="goSelectProduct">
|
|
|
|
|
@ -206,6 +208,11 @@ const warehouseOptions = ref([])
|
|
|
|
|
const warehouseAreaMap = ref({})
|
|
|
|
|
|
|
|
|
|
const taskProductList = ref([])
|
|
|
|
|
const PRODUCT_CATEGORY_TYPE = 1
|
|
|
|
|
const PRODUCT_PAGE_SIZE = 10
|
|
|
|
|
const SCAN_AUTO_SEARCH_DELAY = 300
|
|
|
|
|
let taskProductScanTimer = null
|
|
|
|
|
let productScanTimer = null
|
|
|
|
|
|
|
|
|
|
const productName = computed(() => selectedTaskProduct.value?.productName || product.value.name || '')
|
|
|
|
|
const productId = computed(() => selectedTaskProduct.value?.productId || product.value.id)
|
|
|
|
|
@ -375,27 +382,25 @@ function goSelectTaskProduct() {
|
|
|
|
|
uni.showToast({ title: t('productInbound.selectTaskFirst'), icon: 'none' })
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (!taskProductList.value.length) {
|
|
|
|
|
uni.showToast({ title: t('productInbound.emptyTaskProducts'), icon: 'none' })
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
getApp().globalData._productInboundTaskProductList = [...taskProductList.value]
|
|
|
|
|
const suffix = selectedTaskProduct.value?.productId ? `?selectedId=${selectedTaskProduct.value.productId}` : ''
|
|
|
|
|
uni.navigateTo({ url: `/pages_function/pages/productInbound/taskProductSelect${suffix}` })
|
|
|
|
|
uni.navigateTo({ url: `/pages_function/pages/productInbound/productSelect${suffix}` })
|
|
|
|
|
}
|
|
|
|
|
function normalizeScanValue(value) {
|
|
|
|
|
return String(value || '').trim().toLowerCase()
|
|
|
|
|
}
|
|
|
|
|
function getProductScanKeywords(value) {
|
|
|
|
|
const rawText = String(value || '').trim()
|
|
|
|
|
const raw = normalizeScanValue(rawText)
|
|
|
|
|
if (!raw) return []
|
|
|
|
|
const candidates = [raw, raw.replace(/^product[-:]/i, ''), raw.replace(/^task_product[-:]/i, '')]
|
|
|
|
|
try {
|
|
|
|
|
const parsed = JSON.parse(rawText)
|
|
|
|
|
candidates.push(parsed?.id, parsed?.productId, parsed?.code, parsed?.productCode, parsed?.barCode)
|
|
|
|
|
} catch (e) {}
|
|
|
|
|
return [...new Set(candidates.map(normalizeScanValue).filter(Boolean))]
|
|
|
|
|
function getProductMaterialScanCode(value) {
|
|
|
|
|
const text = String(value || '').trim()
|
|
|
|
|
const match = text.match(/^PRODUCTMATERIAL[-:]?\s*(.+)$/i)
|
|
|
|
|
return match ? match[1].trim() : ''
|
|
|
|
|
}
|
|
|
|
|
function getProductIdValue(item) {
|
|
|
|
|
return item?.id || item?.productId
|
|
|
|
|
}
|
|
|
|
|
function getProductNameValue(item) {
|
|
|
|
|
return item?.name || item?.productName || ''
|
|
|
|
|
}
|
|
|
|
|
function getProductCodeValue(item) {
|
|
|
|
|
return item?.barCode || item?.code || item?.productCode || item?.productBarCode || ''
|
|
|
|
|
}
|
|
|
|
|
function getTaskProductScanValues(item) {
|
|
|
|
|
return [
|
|
|
|
|
@ -408,73 +413,130 @@ function getTaskProductScanValues(item) {
|
|
|
|
|
item?.name
|
|
|
|
|
].map(normalizeScanValue).filter(Boolean)
|
|
|
|
|
}
|
|
|
|
|
function onTaskProductScanConfirm() {
|
|
|
|
|
const keywords = getProductScanKeywords(taskProductScanInput.value)
|
|
|
|
|
if (!keywords.length) return
|
|
|
|
|
if (!taskId.value) {
|
|
|
|
|
uni.showToast({ title: t('productInbound.selectTaskFirst'), icon: 'none' })
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (!taskProductList.value.length) {
|
|
|
|
|
uni.showToast({ title: t('productInbound.emptyTaskProducts'), icon: 'none' })
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const matched = taskProductList.value.find((item) => {
|
|
|
|
|
const values = getTaskProductScanValues(item)
|
|
|
|
|
return keywords.some((keyword) => values.includes(keyword))
|
|
|
|
|
})
|
|
|
|
|
if (!matched) {
|
|
|
|
|
uni.showToast({ title: t('productInbound.emptyTaskProducts'), icon: 'none' })
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
selectedTaskProduct.value = matched
|
|
|
|
|
selectedPallets.value = []
|
|
|
|
|
taskProductScanInput.value = ''
|
|
|
|
|
}
|
|
|
|
|
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(value) {
|
|
|
|
|
const keywords = getProductScanKeywords(value)
|
|
|
|
|
const primary = keywords[0] || ''
|
|
|
|
|
if (!primary) return null
|
|
|
|
|
const productId = keywords.find((keyword) => /^\d+$/.test(keyword))
|
|
|
|
|
if (productId) {
|
|
|
|
|
function pickMatchedProduct(list, keyword) {
|
|
|
|
|
const normalized = normalizeScanValue(keyword)
|
|
|
|
|
return list.find((item) => {
|
|
|
|
|
const values = [getProductIdValue(item), getProductCodeValue(item), getProductNameValue(item)].map(normalizeScanValue).filter(Boolean)
|
|
|
|
|
return values.includes(normalized)
|
|
|
|
|
}) || list[0]
|
|
|
|
|
}
|
|
|
|
|
async function getProductDetailOrSelf(item) {
|
|
|
|
|
const id = getProductIdValue(item)
|
|
|
|
|
if (!id) return item
|
|
|
|
|
try {
|
|
|
|
|
const res = await getProduct(id)
|
|
|
|
|
return { ...item, ...(res?.data || res || {}) }
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return item
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
async function findProductByScanCode(scanCode) {
|
|
|
|
|
const keyword = String(scanCode || '').trim()
|
|
|
|
|
if (!keyword) return null
|
|
|
|
|
const codeRes = await getProductPage({ pageNo: 1, pageSize: PRODUCT_PAGE_SIZE, categoryType: PRODUCT_CATEGORY_TYPE, barCode: keyword })
|
|
|
|
|
const codeList = normalizeProductPageList(codeRes)
|
|
|
|
|
let matched = pickMatchedProduct(codeList, keyword)
|
|
|
|
|
|
|
|
|
|
if (!matched) {
|
|
|
|
|
const nameRes = await getProductPage({ pageNo: 1, pageSize: PRODUCT_PAGE_SIZE, categoryType: PRODUCT_CATEGORY_TYPE, name: keyword })
|
|
|
|
|
const nameList = normalizeProductPageList(nameRes)
|
|
|
|
|
matched = pickMatchedProduct(nameList, keyword)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!matched && /^\d+$/.test(keyword)) {
|
|
|
|
|
try {
|
|
|
|
|
const res = await getProduct(productId)
|
|
|
|
|
const res = await getProduct(keyword)
|
|
|
|
|
const detail = res?.data || res
|
|
|
|
|
if (detail?.id) return detail
|
|
|
|
|
if (detail?.id && (!detail.categoryType || Number(detail.categoryType) === PRODUCT_CATEGORY_TYPE)) matched = 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) => {
|
|
|
|
|
const values = [item?.id, item?.barCode, item?.code, item?.name].map(normalizeScanValue).filter(Boolean)
|
|
|
|
|
return keywords.some((keyword) => values.includes(keyword))
|
|
|
|
|
})
|
|
|
|
|
if (matched) {
|
|
|
|
|
try {
|
|
|
|
|
const detailRes = await getProduct(matched.id)
|
|
|
|
|
return { ...matched, ...(detailRes?.data || detailRes || {}) }
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return matched
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return matched ? getProductDetailOrSelf(matched) : null
|
|
|
|
|
}
|
|
|
|
|
function getTaskProductByProduct(productItem) {
|
|
|
|
|
const productValues = [getProductIdValue(productItem), getProductCodeValue(productItem), getProductNameValue(productItem)].map(normalizeScanValue).filter(Boolean)
|
|
|
|
|
return taskProductList.value.find((item) => {
|
|
|
|
|
const values = getTaskProductScanValues(item)
|
|
|
|
|
return productValues.some((value) => values.includes(value))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
function buildTaskProductSelection(productItem) {
|
|
|
|
|
const taskProduct = getTaskProductByProduct(productItem)
|
|
|
|
|
return {
|
|
|
|
|
...productItem,
|
|
|
|
|
...(taskProduct || {}),
|
|
|
|
|
productId: getProductIdValue(productItem),
|
|
|
|
|
productName: getProductNameValue(productItem),
|
|
|
|
|
productCode: getProductCodeValue(productItem),
|
|
|
|
|
packagingSchemes: taskProduct?.packagingSchemes || productItem?.packagingSchemes || []
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
function scheduleTaskProductScanSearch(value) {
|
|
|
|
|
if (!getProductMaterialScanCode(value)) return
|
|
|
|
|
if (taskProductScanTimer) clearTimeout(taskProductScanTimer)
|
|
|
|
|
taskProductScanTimer = setTimeout(() => {
|
|
|
|
|
taskProductScanTimer = null
|
|
|
|
|
onTaskProductScanConfirm()
|
|
|
|
|
}, SCAN_AUTO_SEARCH_DELAY)
|
|
|
|
|
}
|
|
|
|
|
function scheduleProductScanSearch(value) {
|
|
|
|
|
if (!getProductMaterialScanCode(value)) return
|
|
|
|
|
if (productScanTimer) clearTimeout(productScanTimer)
|
|
|
|
|
productScanTimer = setTimeout(() => {
|
|
|
|
|
productScanTimer = null
|
|
|
|
|
onProductScanConfirm()
|
|
|
|
|
}, SCAN_AUTO_SEARCH_DELAY)
|
|
|
|
|
}
|
|
|
|
|
function onTaskProductScanInput(e) {
|
|
|
|
|
scheduleTaskProductScanSearch(e?.detail?.value ?? taskProductScanInput.value)
|
|
|
|
|
}
|
|
|
|
|
function onProductScanInput(e) {
|
|
|
|
|
scheduleProductScanSearch(e?.detail?.value ?? productScanInput.value)
|
|
|
|
|
}
|
|
|
|
|
async function onTaskProductScanConfirm() {
|
|
|
|
|
if (taskProductScanTimer) {
|
|
|
|
|
clearTimeout(taskProductScanTimer)
|
|
|
|
|
taskProductScanTimer = null
|
|
|
|
|
}
|
|
|
|
|
const scanCode = getProductMaterialScanCode(taskProductScanInput.value)
|
|
|
|
|
if (!scanCode) return
|
|
|
|
|
if (!taskId.value) {
|
|
|
|
|
uni.showToast({ title: t('productInbound.selectTaskFirst'), icon: 'none' })
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
uni.showLoading({ title: t('productInbound.loading'), mask: true })
|
|
|
|
|
const matched = await findProductByScanCode(scanCode)
|
|
|
|
|
uni.hideLoading()
|
|
|
|
|
if (!matched?.id) {
|
|
|
|
|
uni.showToast({ title: '\u6ca1\u6709\u8fd9\u4e2a\u4ea7\u54c1', icon: 'none' })
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (list.length < 100) break
|
|
|
|
|
selectedTaskProduct.value = buildTaskProductSelection(matched)
|
|
|
|
|
selectedPallets.value = []
|
|
|
|
|
taskProductScanInput.value = ''
|
|
|
|
|
} catch (e) {
|
|
|
|
|
uni.hideLoading()
|
|
|
|
|
uni.showToast({ title: t('productInbound.loadFailed'), icon: 'none' })
|
|
|
|
|
}
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
async function onProductScanConfirm() {
|
|
|
|
|
const keywords = getProductScanKeywords(productScanInput.value)
|
|
|
|
|
if (!keywords.length) return
|
|
|
|
|
if (productScanTimer) {
|
|
|
|
|
clearTimeout(productScanTimer)
|
|
|
|
|
productScanTimer = null
|
|
|
|
|
}
|
|
|
|
|
const scanCode = getProductMaterialScanCode(productScanInput.value)
|
|
|
|
|
if (!scanCode) return
|
|
|
|
|
try {
|
|
|
|
|
uni.showLoading({ title: t('productInbound.loading'), mask: true })
|
|
|
|
|
const matched = await findProductByScanCode(productScanInput.value)
|
|
|
|
|
const matched = await findProductByScanCode(scanCode)
|
|
|
|
|
uni.hideLoading()
|
|
|
|
|
if (!matched?.id) {
|
|
|
|
|
uni.showToast({ title: t('productInbound.emptyProduct'), icon: 'none' })
|
|
|
|
|
uni.showToast({ title: '\u6ca1\u6709\u8fd9\u4e2a\u4ea7\u54c1', icon: 'none' })
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
product.value = matched
|
|
|
|
|
@ -633,17 +695,16 @@ onShow(async () => {
|
|
|
|
|
uni.showToast({ title: t('productInbound.taskProductLoadFailed'), icon: 'none' })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const taskProductResult = gd?._productInboundTaskProductSelectResult
|
|
|
|
|
if (taskProductResult) {
|
|
|
|
|
selectedTaskProduct.value = taskProductResult
|
|
|
|
|
taskProductScanInput.value = ''
|
|
|
|
|
selectedPallets.value = []
|
|
|
|
|
gd._productInboundTaskProductSelectResult = null
|
|
|
|
|
}
|
|
|
|
|
const productResult = gd?._productInboundProductSelectResult
|
|
|
|
|
if (productResult) {
|
|
|
|
|
product.value = productResult
|
|
|
|
|
productScanInput.value = ''
|
|
|
|
|
if (relateTask.value) {
|
|
|
|
|
selectedTaskProduct.value = buildTaskProductSelection(productResult)
|
|
|
|
|
taskProductScanInput.value = ''
|
|
|
|
|
} else {
|
|
|
|
|
selectedTaskProduct.value = null
|
|
|
|
|
productScanInput.value = ''
|
|
|
|
|
}
|
|
|
|
|
selectedPallets.value = []
|
|
|
|
|
gd._productInboundProductSelectResult = null
|
|
|
|
|
}
|
|
|
|
|
|