diff --git a/.env.development b/.env.development index b734738..a22c22e 100644 --- a/.env.development +++ b/.env.development @@ -1,2 +1,3 @@ -VITE_APP_BASE_URL=http://192.168.43.233:48081 +VITE_APP_BASE_URL=http://192.168.5.37:48081 +# VITE_APP_BASE_URL=http://192.168.43.233:48081 # VITE_APP_BASE_URL=http://47.106.185.127:8089 \ No newline at end of file diff --git a/src/pages.json b/src/pages.json index 03fe503..f382966 100644 --- a/src/pages.json +++ b/src/pages.json @@ -465,13 +465,6 @@ "navigationStyle": "custom" } }, - { - "path": "productInbound/taskProductSelect", - "style": { - "navigationBarTitleText": "选择任务产品", - "navigationStyle": "custom" - } - }, { "path": "productOutbound/index", "style": { diff --git a/src/pages_function/pages/productInbound/productConfirm.vue b/src/pages_function/pages/productInbound/productConfirm.vue index 8b9f733..5019eaa 100644 --- a/src/pages_function/pages/productInbound/productConfirm.vue +++ b/src/pages_function/pages/productInbound/productConfirm.vue @@ -31,15 +31,16 @@ - {{ t('productInbound.taskProduct') }}* + {{ t('productInbound.product') }}* @@ -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" /> @@ -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 } diff --git a/src/pages_function/pages/productInbound/productSelect.vue b/src/pages_function/pages/productInbound/productSelect.vue index ebf5a59..ef9c1e1 100644 --- a/src/pages_function/pages/productInbound/productSelect.vue +++ b/src/pages_function/pages/productInbound/productSelect.vue @@ -3,35 +3,46 @@ - - - - - + + + + + + + + + + + + + + - + - - {{ textValue(item.name) }} - {{ getCategoryName(item) }} - - {{ textValue(item.barCode || item.code) }} + {{ textValue(getProductName(item)) }} + {{ textValue(getProductCode(item)) }} @@ -39,12 +50,16 @@ - {{ t('productInbound.spec') }} - {{ textValue(item.standard || item.deviceSpec) }} + {{ t('productInbound.packagingScheme') }} + {{ textValue(getDefaultScheme(item)?.packagingSchemeName) }} - {{ t('productInbound.unit') }} - {{ textValue(item.unitName) }} + {{ t('productInbound.palletPackageQuantity') }} + {{ textValue(getDefaultScheme(item)?.palletPackageQuantity) }} + + + {{ t('productInbound.packageQuantity') }} + {{ textValue(getDefaultScheme(item)?.packageQuantity) }} @@ -70,8 +85,15 @@ import { getProduct, getProductPage } from '@/api/erp/productInfo' const { t } = useI18n() const productList = ref([]) const selectedId = ref(null) -const searchText = ref('') +const searchCode = ref('') +const searchName = ref('') const loading = ref(false) +const PRODUCT_CATEGORY_TYPE = 1 +const PRODUCT_PAGE_SIZE = 10 +const SEARCH_DELAY = 300 +let searchTimer = null + +const filteredList = computed(() => productList.value) onLoad((options) => { selectedId.value = options?.selectedId ? String(options.selectedId) : null @@ -83,59 +105,105 @@ function textValue(v) { const s = String(v).trim() return s || '-' } -const filteredList = computed(() => { - const keyword = searchText.value.trim().toLowerCase() - if (!keyword) return productList.value - return productList.value.filter((item) => - String(item.name || '').toLowerCase().includes(keyword) || - String(item.barCode || item.code || '').toLowerCase().includes(keyword) || - String(item.standard || item.deviceSpec || '').toLowerCase().includes(keyword) || - String(getCategoryName(item)).toLowerCase().includes(keyword) - ) -}) -function isSelected(item) { - return String(selectedId.value) === String(item.id) +function normalizeValue(value) { + return String(value || '').trim().toLowerCase() +} +function getDefaultScheme(item) { + const schemes = Array.isArray(item?.packagingSchemes) ? item.packagingSchemes : [] + return schemes.find((scheme) => Number(scheme?.defaultStatus) === 1) || schemes[0] || {} +} +function getProductId(item) { + return item?.id || item?.productId +} +function getProductName(item) { + return item?.name || item?.productName || '' +} +function getProductCode(item) { + return item?.barCode || item?.code || item?.productCode || '' } function getCategoryName(item) { return item?.subCategoryName || item?.categoryName || item?.category?.name || '' } -function clearSearch() { - searchText.value = '' +function isSelected(item) { + return String(selectedId.value) === String(getProductId(item)) } function normalizePageList(res) { const root = res && res.data !== undefined ? res.data : res return Array.isArray(root) ? root : (root?.list || root?.rows || root?.records || []) } -async function loadProducts() { +function getSearchParams(params = {}) { + return { + code: String((params.code ?? searchCode.value) || '').trim(), + name: String((params.name ?? searchName.value) || '').trim() + } +} +async function queryProducts(params = {}) { + const { code, name } = getSearchParams(params) + const queryParams = { pageNo: 1, pageSize: PRODUCT_PAGE_SIZE, categoryType: PRODUCT_CATEGORY_TYPE } + if (code) queryParams.barCode = code + if (name) queryParams.name = name + const res = await getProductPage(queryParams) + return normalizePageList(res) +} +async function loadProducts(params = {}) { loading.value = true try { - const raw = [] - for (let pageNo = 1; pageNo <= 5; pageNo += 1) { - const res = await getProductPage({ pageNo, pageSize: 100, categoryType: 1 }) - const list = normalizePageList(res) - if (!list.length) break - raw.push(...list) - if (list.length < 100) break - } - productList.value = raw + productList.value = await queryProducts(params) } catch (e) { uni.showToast({ title: t('productInbound.loadFailed'), icon: 'none' }) } finally { loading.value = false } } +function handleSearch() { + if (searchTimer) { + clearTimeout(searchTimer) + searchTimer = null + } + loadProducts() +} +function scheduleSearch(params) { + if (searchTimer) clearTimeout(searchTimer) + searchTimer = setTimeout(() => { + searchTimer = null + loadProducts(params) + }, SEARCH_DELAY) +} +function onCodeInput(e) { + scheduleSearch({ code: e?.detail?.value ?? searchCode.value }) +} +function onNameInput(e) { + scheduleSearch({ name: e?.detail?.value ?? searchName.value }) +} +function clearSearchCode() { + if (searchTimer) { + clearTimeout(searchTimer) + searchTimer = null + } + searchCode.value = '' + loadProducts({ code: '' }) +} +function clearSearchName() { + if (searchTimer) { + clearTimeout(searchTimer) + searchTimer = null + } + searchName.value = '' + loadProducts({ name: '' }) +} async function handleConfirm() { if (!selectedId.value) { uni.showToast({ title: t('productInbound.selectProduct'), icon: 'none' }) return } - let item = productList.value.find((d) => String(d.id) === String(selectedId.value)) + const item = productList.value.find((d) => String(getProductId(d)) === String(selectedId.value)) if (!item) return + let detail = item try { - const res = await getProduct(item.id) - item = { ...item, ...(res?.data || res || {}) } + const res = await getProduct(getProductId(item)) + detail = { ...item, ...(res?.data || res || {}) } } catch (e) {} - getApp().globalData._productInboundProductSelectResult = item + getApp().globalData._productInboundProductSelectResult = detail uni.navigateBack() } onShow(loadProducts) @@ -144,7 +212,8 @@ onShow(loadProducts)