From 723367cf431fc7bcd70118f8def01acf1dfcc431 Mon Sep 17 00:00:00 2001 From: hwj Date: Fri, 26 Jun 2026 17:45:27 +0800 Subject: [PATCH] =?UTF-8?q?style=EF=BC=9A=E4=BA=A7=E5=93=81=E3=80=81?= =?UTF-8?q?=E7=89=A9=E6=96=99=E3=80=81=E5=A4=87=E4=BB=B6=E4=BA=A4=E4=BA=92?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/materialInbound/materialSelect.vue | 57 +------ .../pages/productInbound/productConfirm.vue | 148 ++++++++++++++++-- .../pages/productOutbound/create.vue | 100 +++++++++++- .../pages/productOutbound/productConfirm.vue | 21 +-- .../pages/productOutbound/productSelect.vue | 48 ++---- .../sparepartInbound/sparepartSelect.vue | 60 ++----- 6 files changed, 271 insertions(+), 163 deletions(-) diff --git a/src/pages_function/pages/materialInbound/materialSelect.vue b/src/pages_function/pages/materialInbound/materialSelect.vue index 283c9d3..abba085 100644 --- a/src/pages_function/pages/materialInbound/materialSelect.vue +++ b/src/pages_function/pages/materialInbound/materialSelect.vue @@ -17,8 +17,7 @@ v-for="item in filteredList" :key="item.id" class="material-card" - :class="{ active: selectedId === item.id }" - @click="selectedId = item.id" + @tap="handleSelectItem(item)" > {{ textValue(item.name) }} @@ -57,13 +56,6 @@ 加载中... 暂无物料数据 - - - - - 确认 - - @@ -74,7 +66,6 @@ import NavBar from '@/components/common/NavBar.vue' import { getMaterialSimpleList } from '@/api/mes/sparepart' const materialList = ref([]) -const selectedId = ref(null) const searchText = ref('') const loading = ref(false) const fromSource = ref('inbound') @@ -135,12 +126,7 @@ async function loadMaterials() { } } -function handleConfirm() { - if (!selectedId.value) { - uni.showToast({ title: '请选择物料', icon: 'none' }) - return - } - const item = materialList.value.find((d) => d.id === selectedId.value) +function handleSelectItem(item) { if (!item) return getApp().globalData._materialBeforeConfirm = item const from = fromSource.value || getApp().globalData._materialSelectFrom || 'inbound' @@ -148,7 +134,11 @@ function handleConfirm() { ? '/pages_function/pages/materialOutbound/materialConfirm' : '/pages_function/pages/materialInbound/materialConfirm' uni.navigateTo({ - url + url, + fail: (err) => { + console.error('[materialSelect] navigateTo failed', err) + uni.showToast({ title: err?.errMsg || 'Navigate failed', icon: 'none' }) + } }) } @@ -165,7 +155,6 @@ onShow(async () => { .page-container { min-height: 100vh; background: #f5f6f8; - padding-bottom: 140rpx; } /* 搜索栏 */ @@ -216,11 +205,6 @@ onShow(async () => { padding: 24rpx; margin-bottom: 16rpx; border: 2rpx solid transparent; - - &.active { - border-color: #2563eb; - background: #f0f5ff; - } } .material-card-header { @@ -273,31 +257,4 @@ onShow(async () => { font-size: 28rpx; color: #999; } - -/* 底部确认按钮 */ -.bottom-actions { - position: fixed; - left: 0; - right: 0; - bottom: 0; - padding: 18rpx 24rpx calc(18rpx + env(safe-area-inset-bottom)); - background: #fff; - box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.06); - z-index: 99; -} - -.bottom-btn { - width: 100%; - height: 84rpx; - line-height: 84rpx; - text-align: center; - border-radius: 16rpx; - font-size: 30rpx; - font-weight: 600; -} - -.confirm-btn { - background: #1f4b79; - color: #fff; -} diff --git a/src/pages_function/pages/productInbound/productConfirm.vue b/src/pages_function/pages/productInbound/productConfirm.vue index a139477..8b9f733 100644 --- a/src/pages_function/pages/productInbound/productConfirm.vue +++ b/src/pages_function/pages/productInbound/productConfirm.vue @@ -32,22 +32,37 @@ {{ t('productInbound.taskProduct') }}* - - - {{ selectedTaskProduct ? textValue(selectedTaskProduct.productName) : t('productInbound.selectTaskProduct') }} + + + + {{ t('productInbound.selectProductTitle') }} - {{ t('productInbound.product') }}* - - - {{ productName || t('productInbound.selectProduct') }} - {{ textValue(productBarCode) }} + + + + {{ t('productInbound.selectProductTitle') }} - @@ -172,6 +187,7 @@ import { onLoad, onShow } from '@dcloudio/uni-app' import { useI18n } from 'vue-i18n' import NavBar from '@/components/common/NavBar.vue' import { getTaskDefaultPackagingSchemes } from '@/api/mes/productInbound' +import { getProduct, getProductPage } from '@/api/erp/productInfo' import { getWarehouseAreaSimpleList, getWarehouseSimpleList } from '@/api/mes/moldget' const { t } = useI18n() @@ -183,6 +199,8 @@ const selectedTaskData = ref(null) const selectedTaskProduct = ref(null) const selectedPallets = ref([]) const editingIndex = ref(null) +const taskProductScanInput = ref('') +const productScanInput = ref('') const warehouseOptions = ref([]) const warehouseAreaMap = ref({}) @@ -337,6 +355,8 @@ function setRelateTask(value) { relateTask.value = value product.value = {} selectedTaskProduct.value = null + taskProductScanInput.value = '' + productScanInput.value = '' taskId.value = null taskCode.value = '' selectedTaskData.value = null @@ -363,6 +383,109 @@ function goSelectTaskProduct() { const suffix = selectedTaskProduct.value?.productId ? `?selectedId=${selectedTaskProduct.value.productId}` : '' uni.navigateTo({ url: `/pages_function/pages/productInbound/taskProductSelect${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 getTaskProductScanValues(item) { + return [ + item?.productId, + item?.productCode, + item?.productBarCode, + item?.barCode, + item?.code, + item?.productName, + 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) { + 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) => { + 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 + } + } + if (list.length < 100) break + } + return null +} +async function onProductScanConfirm() { + const keywords = getProductScanKeywords(productScanInput.value) + if (!keywords.length) return + try { + uni.showLoading({ title: t('productInbound.loading'), mask: true }) + const matched = await findProductByScanCode(productScanInput.value) + uni.hideLoading() + if (!matched?.id) { + uni.showToast({ title: t('productInbound.emptyProduct'), icon: 'none' }) + return + } + product.value = matched + selectedTaskProduct.value = null + selectedPallets.value = [] + productScanInput.value = '' + } catch (e) { + uni.hideLoading() + uni.showToast({ title: t('productInbound.loadFailed'), icon: 'none' }) + } +} function goSelectPallet() { if (!productId.value) { uni.showToast({ title: t('productInbound.selectProductFirst'), icon: 'none' }) @@ -496,6 +619,7 @@ onShow(async () => { taskId.value = taskResult.id taskCode.value = taskResult.code || taskResult.taskName || '' selectedTaskProduct.value = null + taskProductScanInput.value = '' selectedPallets.value = [] gd._productInboundTaskSelectResult = null try { @@ -512,12 +636,14 @@ onShow(async () => { 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 = '' selectedPallets.value = [] gd._productInboundProductSelectResult = null } @@ -554,6 +680,10 @@ onShow(async () => { .select-value { font-size: 28rpx; font-weight: 600; color: #1f2937; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .select-placeholder { font-size: 28rpx; color: #9ca3af; } .select-subtext { font-size: 24rpx; color: #6b7280; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.task-product-search-row { display: flex; align-items: center; gap: 16rpx; } +.task-product-scan-input { flex: 1; min-width: 0; height: 70rpx; padding: 0 22rpx; background: #f8fafc; border: 1rpx solid #e5e7eb; border-radius: 14rpx; box-sizing: border-box; font-size: 28rpx; color: #1f2937; } +.task-product-select-field { flex-shrink: 0; width: 160rpx; min-height: 70rpx; padding: 0 18rpx; background: #1f4b79; border-radius: 14rpx; box-sizing: border-box; display: flex; align-items: center; justify-content: center; } +.task-product-select-text { flex: 1; min-width: 0; font-size: 26rpx; font-weight: 600; color: #ffffff; text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .info-panel { margin-top: 22rpx; padding: 20rpx; background: #f8fafc; border: 1rpx solid #e8eef6; border-radius: 16rpx; } .info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0; background: #ffffff; border: 1rpx solid #eef2f7; border-radius: 14rpx; overflow: hidden; } .info-item { min-width: 0; display: flex; flex-direction: column; gap: 8rpx; padding: 18rpx 20rpx; border-right: 1rpx solid #f1f5f9; border-bottom: 1rpx solid #f1f5f9; } diff --git a/src/pages_function/pages/productOutbound/create.vue b/src/pages_function/pages/productOutbound/create.vue index 0eb1032..0042299 100644 --- a/src/pages_function/pages/productOutbound/create.vue +++ b/src/pages_function/pages/productOutbound/create.vue @@ -2,6 +2,22 @@ + + + + + + 选择产品 + + + @@ -134,15 +150,17 @@ {{ t('productOutbound.cancel') }} {{ t('productOutbound.confirmOutbound') }} + diff --git a/src/pages_function/pages/sparepartInbound/sparepartSelect.vue b/src/pages_function/pages/sparepartInbound/sparepartSelect.vue index e22b1a3..e8e5123 100644 --- a/src/pages_function/pages/sparepartInbound/sparepartSelect.vue +++ b/src/pages_function/pages/sparepartInbound/sparepartSelect.vue @@ -17,8 +17,7 @@ v-for="item in filteredList" :key="item.id" class="sparepart-card" - :class="{ active: selectedId === item.id }" - @click="selectedId = item.id" + @tap="handleSelectItem(item)" > {{ textValue(item.name) }} @@ -57,13 +56,6 @@ {{ t('functionCommon.loading') }} {{ t('sparepartInbound.noSparepartData') }} - - - - - {{ t('functionCommon.confirm') }} - - @@ -77,7 +69,6 @@ import { getSparepartSimpleList } from '@/api/mes/sparepart' const { t } = useI18n() const sparepartList = ref([]) -const selectedId = ref(null) const searchText = ref('') const loading = ref(false) @@ -137,20 +128,20 @@ async function loadSpareparts() { } } -function handleConfirm() { - if (!selectedId.value) { - uni.showToast({ title: t('sparepartInbound.validatorSparepartRequired'), icon: 'none' }) - return - } - const item = sparepartList.value.find((d) => d.id === selectedId.value) +function handleSelectItem(item) { if (!item) return - // 存入 globalData 后跳转到对应确认页 getApp().globalData._sparepartBeforeConfirm = item const from = getApp().globalData._sparepartSelectFrom || 'inbound' const url = from === 'outbound' ? '/pages_function/pages/sparepartOutbound/sparepartConfirm' : '/pages_function/pages/sparepartInbound/sparepartConfirm' - uni.navigateTo({ url }) + uni.navigateTo({ + url, + fail: (err) => { + console.error('[sparepartSelect] navigateTo failed', err) + uni.showToast({ title: err?.errMsg || 'Navigate failed', icon: 'none' }) + } + }) } onShow(async () => { @@ -162,7 +153,6 @@ onShow(async () => { .page-container { min-height: 100vh; background: #f5f6f8; - padding-bottom: 140rpx; } /* 搜索栏 */ @@ -213,11 +203,6 @@ onShow(async () => { padding: 24rpx; margin-bottom: 16rpx; border: 2rpx solid transparent; - - &.active { - border-color: #2563eb; - background: #f0f5ff; - } } .sparepart-card-header { @@ -270,31 +255,4 @@ onShow(async () => { font-size: 28rpx; color: #999; } - -/* 底部确认按钮 */ -.bottom-actions { - position: fixed; - left: 0; - right: 0; - bottom: 0; - padding: 18rpx 24rpx calc(18rpx + env(safe-area-inset-bottom)); - background: #fff; - box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.06); - z-index: 99; -} - -.bottom-btn { - width: 100%; - height: 84rpx; - line-height: 84rpx; - text-align: center; - border-radius: 16rpx; - font-size: 30rpx; - font-weight: 600; -} - -.confirm-btn { - background: #1f4b79; - color: #fff; -}