|
|
|
@ -32,22 +32,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
<view v-if="relateTask && taskId" class="form-field">
|
|
|
|
<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.taskProduct') }}<text class="required-star">*</text></text>
|
|
|
|
<view :class="['select-field', selectedTaskProduct ? 'selected' : '']" @click="goSelectTaskProduct">
|
|
|
|
<view class="task-product-search-row">
|
|
|
|
<view class="select-content">
|
|
|
|
<input
|
|
|
|
<text :class="selectedTaskProduct ? 'select-value' : 'select-placeholder'">{{ selectedTaskProduct ? textValue(selectedTaskProduct.productName) : t('productInbound.selectTaskProduct') }}</text>
|
|
|
|
id="product-inbound-task-product-scan-input"
|
|
|
|
|
|
|
|
v-model="taskProductScanInput"
|
|
|
|
|
|
|
|
class="task-product-scan-input"
|
|
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
|
|
:placeholder="t('productInbound.searchTaskProductPlaceholder')"
|
|
|
|
|
|
|
|
confirm-type="done"
|
|
|
|
|
|
|
|
@confirm="onTaskProductScanConfirm"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
<view class="task-product-select-field" @click="goSelectTaskProduct">
|
|
|
|
|
|
|
|
<text class="task-product-select-text">{{ t('productInbound.selectProductTitle') }}</text>
|
|
|
|
</view>
|
|
|
|
</view>
|
|
|
|
<uni-icons type="right" size="18" color="#9ca3af"></uni-icons>
|
|
|
|
|
|
|
|
</view>
|
|
|
|
</view>
|
|
|
|
</view>
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view v-if="!relateTask" class="form-field">
|
|
|
|
<view v-if="!relateTask" class="form-field">
|
|
|
|
<text class="form-label">{{ t('productInbound.product') }}<text class="required-star">*</text></text>
|
|
|
|
<text class="form-label">{{ t('productInbound.product') }}<text class="required-star">*</text></text>
|
|
|
|
<view :class="['select-field', productName ? 'selected' : '']" @click="goSelectProduct">
|
|
|
|
<view class="task-product-search-row">
|
|
|
|
<view class="select-content">
|
|
|
|
<input
|
|
|
|
<text :class="productName ? 'select-value' : 'select-placeholder'">{{ productName || t('productInbound.selectProduct') }}</text>
|
|
|
|
id="product-inbound-product-scan-input"
|
|
|
|
<text v-if="productBarCode" class="select-subtext">{{ textValue(productBarCode) }}</text>
|
|
|
|
v-model="productScanInput"
|
|
|
|
|
|
|
|
class="task-product-scan-input"
|
|
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
|
|
:placeholder="t('productInbound.searchProductPlaceholder')"
|
|
|
|
|
|
|
|
confirm-type="done"
|
|
|
|
|
|
|
|
@confirm="onProductScanConfirm"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
<view class="task-product-select-field" @click="goSelectProduct">
|
|
|
|
|
|
|
|
<text class="task-product-select-text">{{ t('productInbound.selectProductTitle') }}</text>
|
|
|
|
</view>
|
|
|
|
</view>
|
|
|
|
<uni-icons type="right" size="18" color="#9ca3af"></uni-icons>
|
|
|
|
|
|
|
|
</view>
|
|
|
|
</view>
|
|
|
|
</view>
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
@ -172,6 +187,7 @@ import { onLoad, onShow } from '@dcloudio/uni-app'
|
|
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
import NavBar from '@/components/common/NavBar.vue'
|
|
|
|
import NavBar from '@/components/common/NavBar.vue'
|
|
|
|
import { getTaskDefaultPackagingSchemes } from '@/api/mes/productInbound'
|
|
|
|
import { getTaskDefaultPackagingSchemes } from '@/api/mes/productInbound'
|
|
|
|
|
|
|
|
import { getProduct, getProductPage } from '@/api/erp/productInfo'
|
|
|
|
import { getWarehouseAreaSimpleList, getWarehouseSimpleList } from '@/api/mes/moldget'
|
|
|
|
import { getWarehouseAreaSimpleList, getWarehouseSimpleList } from '@/api/mes/moldget'
|
|
|
|
|
|
|
|
|
|
|
|
const { t } = useI18n()
|
|
|
|
const { t } = useI18n()
|
|
|
|
@ -183,6 +199,8 @@ const selectedTaskData = ref(null)
|
|
|
|
const selectedTaskProduct = ref(null)
|
|
|
|
const selectedTaskProduct = ref(null)
|
|
|
|
const selectedPallets = ref([])
|
|
|
|
const selectedPallets = ref([])
|
|
|
|
const editingIndex = ref(null)
|
|
|
|
const editingIndex = ref(null)
|
|
|
|
|
|
|
|
const taskProductScanInput = ref('')
|
|
|
|
|
|
|
|
const productScanInput = ref('')
|
|
|
|
|
|
|
|
|
|
|
|
const warehouseOptions = ref([])
|
|
|
|
const warehouseOptions = ref([])
|
|
|
|
const warehouseAreaMap = ref({})
|
|
|
|
const warehouseAreaMap = ref({})
|
|
|
|
@ -337,6 +355,8 @@ function setRelateTask(value) {
|
|
|
|
relateTask.value = value
|
|
|
|
relateTask.value = value
|
|
|
|
product.value = {}
|
|
|
|
product.value = {}
|
|
|
|
selectedTaskProduct.value = null
|
|
|
|
selectedTaskProduct.value = null
|
|
|
|
|
|
|
|
taskProductScanInput.value = ''
|
|
|
|
|
|
|
|
productScanInput.value = ''
|
|
|
|
taskId.value = null
|
|
|
|
taskId.value = null
|
|
|
|
taskCode.value = ''
|
|
|
|
taskCode.value = ''
|
|
|
|
selectedTaskData.value = null
|
|
|
|
selectedTaskData.value = null
|
|
|
|
@ -363,6 +383,109 @@ function goSelectTaskProduct() {
|
|
|
|
const suffix = selectedTaskProduct.value?.productId ? `?selectedId=${selectedTaskProduct.value.productId}` : ''
|
|
|
|
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/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() {
|
|
|
|
function goSelectPallet() {
|
|
|
|
if (!productId.value) {
|
|
|
|
if (!productId.value) {
|
|
|
|
uni.showToast({ title: t('productInbound.selectProductFirst'), icon: 'none' })
|
|
|
|
uni.showToast({ title: t('productInbound.selectProductFirst'), icon: 'none' })
|
|
|
|
@ -496,6 +619,7 @@ onShow(async () => {
|
|
|
|
taskId.value = taskResult.id
|
|
|
|
taskId.value = taskResult.id
|
|
|
|
taskCode.value = taskResult.code || taskResult.taskName || ''
|
|
|
|
taskCode.value = taskResult.code || taskResult.taskName || ''
|
|
|
|
selectedTaskProduct.value = null
|
|
|
|
selectedTaskProduct.value = null
|
|
|
|
|
|
|
|
taskProductScanInput.value = ''
|
|
|
|
selectedPallets.value = []
|
|
|
|
selectedPallets.value = []
|
|
|
|
gd._productInboundTaskSelectResult = null
|
|
|
|
gd._productInboundTaskSelectResult = null
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
@ -512,12 +636,14 @@ onShow(async () => {
|
|
|
|
const taskProductResult = gd?._productInboundTaskProductSelectResult
|
|
|
|
const taskProductResult = gd?._productInboundTaskProductSelectResult
|
|
|
|
if (taskProductResult) {
|
|
|
|
if (taskProductResult) {
|
|
|
|
selectedTaskProduct.value = taskProductResult
|
|
|
|
selectedTaskProduct.value = taskProductResult
|
|
|
|
|
|
|
|
taskProductScanInput.value = ''
|
|
|
|
selectedPallets.value = []
|
|
|
|
selectedPallets.value = []
|
|
|
|
gd._productInboundTaskProductSelectResult = null
|
|
|
|
gd._productInboundTaskProductSelectResult = null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const productResult = gd?._productInboundProductSelectResult
|
|
|
|
const productResult = gd?._productInboundProductSelectResult
|
|
|
|
if (productResult) {
|
|
|
|
if (productResult) {
|
|
|
|
product.value = productResult
|
|
|
|
product.value = productResult
|
|
|
|
|
|
|
|
productScanInput.value = ''
|
|
|
|
selectedPallets.value = []
|
|
|
|
selectedPallets.value = []
|
|
|
|
gd._productInboundProductSelectResult = null
|
|
|
|
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-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-placeholder { font-size: 28rpx; color: #9ca3af; }
|
|
|
|
.select-subtext { font-size: 24rpx; color: #6b7280; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
|
|
.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-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-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; }
|
|
|
|
.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; }
|
|
|
|
|