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)"
>
-
-
-
-
- 确认
-
-
@@ -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)"
>
-
-
-
-
- {{ 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;
-}