|
|
@@ -75,7 +75,7 @@
|
|
|
>
|
|
|
<template #right-icon>
|
|
|
<van-button
|
|
|
- v-if="barcodeActiveList.length > 0"
|
|
|
+ v-if="systemForcePublishEnabled && barcodeActiveList.length > 0"
|
|
|
type="primary"
|
|
|
size="mini"
|
|
|
plain
|
|
|
@@ -112,7 +112,7 @@
|
|
|
</template>
|
|
|
</van-search>
|
|
|
</div>
|
|
|
- <div class="barcode-input location-type-row">
|
|
|
+ <div class="barcode-input location-type-row" v-if="systemForcePublishEnabled" >
|
|
|
<van-cell title="上架区域:" :value="locationTypeLabel" is-link @click="locationTypeSheetShow = true" />
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -136,7 +136,7 @@
|
|
|
<div class="btn" type="primary" size="large" round style="height: 36px" @click="onConfirm">上架</div>
|
|
|
</div>
|
|
|
<div>
|
|
|
- <location-list :locationList="locationList" />
|
|
|
+ <location-list :locationList="locationList" :legacy="!systemForcePublishEnabled" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -167,13 +167,6 @@
|
|
|
<van-cell title="挂装区" @click="onSelectLocationType('HANGING')" />
|
|
|
</van-cell-group>
|
|
|
</van-action-sheet>
|
|
|
- <!-- 推荐库位列表-->
|
|
|
- <van-action-sheet v-model:show="locationTrueFalseBy" cancel-text="取消" description="推荐库位列表"
|
|
|
- close-on-click-action>
|
|
|
- <div style="max-height: 60vh;overflow: auto;">
|
|
|
- <location-list :locationList="locationList" />
|
|
|
- </div>
|
|
|
- </van-action-sheet>
|
|
|
<!-- 组合商品上架数量-->
|
|
|
<barcode-combine ref="barcodeCombineRef" @setCombine="setPutawayCombine" @cancel="onCombineCancel" :matched-sku="combineMatchedSku" />
|
|
|
</template>
|
|
|
@@ -193,7 +186,8 @@ import { getCurrentTime } from '@/utils/date'
|
|
|
import { getWaitPutawayListNew, setPutawayNew } from '@/api/putaway/index'
|
|
|
import { getListCombineSku } from '@/api/picking'
|
|
|
import { barcodeToUpperCase } from '@/utils/dataType.js'
|
|
|
-import { getRecommendedLocationNew } from '@/api/haikang/index'
|
|
|
+import { getRecommendedLocation, getRecommendedLocationNew } from '@/api/haikang/index'
|
|
|
+import { findSysParamByKey } from '@/api/basic/index'
|
|
|
import { getOwnerList } from '@/hooks/basic/index'
|
|
|
|
|
|
const router = useRouter()
|
|
|
@@ -204,12 +198,75 @@ try {
|
|
|
} catch (error) {
|
|
|
router.push('/login')
|
|
|
}
|
|
|
-/** 精准推荐是否开启 */
|
|
|
-const forcePublishEnabled = ref(true)
|
|
|
+// 精准推荐内是否禁止强制上架(false = 强制上架,库位必须在推荐列表内)
|
|
|
+const forbidForcePutaway = ref(true)
|
|
|
+// 系统参数:是否开启精准推荐(开=getRecommendedLocationNew,关=getRecommendedLocation)
|
|
|
+const systemForcePublishEnabled = ref(false)
|
|
|
+async function loadForcePublishParam() {
|
|
|
+ try {
|
|
|
+ const res = await findSysParamByKey({ paramKey: 'FORCE_PUBLISH_ENABLED' })
|
|
|
+ systemForcePublishEnabled.value = res?.data === 'true'
|
|
|
+ } catch (e) {
|
|
|
+ console.error(e)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// --- 库位校验(精准推荐 + 强制上架) ---
|
|
|
+// 扫描步骤:2=商品条码 3=库位 4=上架数量
|
|
|
+const SCAN_TYPE = { BARCODE: 2, LOCATION: 3, QUANTITY: 4 }
|
|
|
+
|
|
|
+// 从推荐库位项取编码(精准推荐接口字段为 locationId)
|
|
|
+const getRecommendedLocationCode = (item) => barcodeToUpperCase(item?.locationId || '')
|
|
|
+
|
|
|
+// 是否需要校验扫描库位在推荐列表内(仅精准推荐开启且未禁止强制上架时校验)
|
|
|
+const shouldForceLocationMatch = () => {
|
|
|
+ if (!systemForcePublishEnabled.value) return false
|
|
|
+ if (!barcodeActiveList.value.length || !locationList.value.length) return false
|
|
|
+ const { lotAtt02 } = barcodeActiveList.value[0]
|
|
|
+ if (lotAtt02) return false // 有失效日期时不强制匹配推荐库位
|
|
|
+ return !forbidForcePutaway.value
|
|
|
+}
|
|
|
+
|
|
|
+// 判断扫描库位是否在推荐列表内
|
|
|
+const isLocationInRecommendedList = (locationCode) => {
|
|
|
+ if (!shouldForceLocationMatch()) return true
|
|
|
+ const scanned = barcodeToUpperCase(locationCode)
|
|
|
+ return locationList.value.some(item => getRecommendedLocationCode(item) === scanned)
|
|
|
+}
|
|
|
+
|
|
|
+// 从已排除库位记录中提取 locationId 列表(换一换时使用)
|
|
|
+const getExcludedLocationIds = (listByLot) => {
|
|
|
+ if (!listByLot.length) return undefined
|
|
|
+ return [...new Set(listByLot.map(loc => (typeof loc === 'string' ? loc : loc?.locationId)).filter(Boolean))]
|
|
|
+}
|
|
|
+
|
|
|
+// 单一批次命中:获取推荐库位并进入库位扫描
|
|
|
+const _activateSingleBatch = (batchItem, { showCombineDialog = false } = {}) => {
|
|
|
+ barcodeActiveList.value = batchItem
|
|
|
+ if (showCombineDialog) {
|
|
|
+ _showPutawayCombineDialog(batchItem)
|
|
|
+ } else {
|
|
|
+ searchCount.value = 1
|
|
|
+ }
|
|
|
+ _getRecommendedLocation(batchItem[0])
|
|
|
+ scanType.value = SCAN_TYPE.LOCATION
|
|
|
+ scanSuccess()
|
|
|
+}
|
|
|
+
|
|
|
+// 多批次命中:清空当前状态并弹出批次选择
|
|
|
+const _openMultiBatchPicker = () => {
|
|
|
+ locationList.value = []
|
|
|
+ barcodeActiveList.value = []
|
|
|
+ searchCount.value = ''
|
|
|
+ searchLocation.value = ''
|
|
|
+ lotBarcodeTrueFalseBy.value = true
|
|
|
+}
|
|
|
+
|
|
|
// 页面初始化
|
|
|
onMounted(async () => {
|
|
|
openListener()
|
|
|
scanInit(_handlerScan)
|
|
|
+ await loadForcePublishParam()
|
|
|
loadData()
|
|
|
})
|
|
|
const warehouse = store.warehouse
|
|
|
@@ -220,7 +277,6 @@ const dataList = ref([])
|
|
|
//
|
|
|
const dataMap = ref({})
|
|
|
//库位列表
|
|
|
-const locationTrueFalseBy = ref(false)
|
|
|
const locationList = ref([])
|
|
|
//商品条码
|
|
|
const searchBarcode = ref('')
|
|
|
@@ -232,7 +288,7 @@ const searchCount = ref('')
|
|
|
const taskInfo = ref({})
|
|
|
//开始时间
|
|
|
const currentTime = ref('--')
|
|
|
-const scanType = ref(2)
|
|
|
+const scanType = ref(SCAN_TYPE.BARCODE)
|
|
|
const putweayType=ref('def')
|
|
|
const lotAttributes = {
|
|
|
lotAtt01: { title: '生产日期' },
|
|
|
@@ -301,7 +357,7 @@ const setBarcode = (code, type) => {
|
|
|
const params = { warehouseId:warehouse, containerId: code }
|
|
|
getWaitPutawayListNew(params).then(res => {
|
|
|
reset()
|
|
|
- scanType.value=2
|
|
|
+ scanType.value = SCAN_TYPE.BARCODE
|
|
|
back.value = true
|
|
|
if (!type) {//切换任务成功重启计时器
|
|
|
currentTime.value = getCurrentTime()
|
|
|
@@ -354,9 +410,6 @@ const setBarcode = (code, type) => {
|
|
|
dataList.value = [...asnToShelfList,...noAsnToShelfList]
|
|
|
dataMap.value = groupedData(dataList.value)
|
|
|
containerNo.value = code
|
|
|
- // if (!type && code?.includes('TH-')) {
|
|
|
- // locationType.value = 'RETURN'
|
|
|
- // }
|
|
|
scanSuccess()
|
|
|
} else {
|
|
|
reset()
|
|
|
@@ -405,7 +458,7 @@ const matchingBarcodeItem = (data, barcode) => {
|
|
|
const switchTask = () => {
|
|
|
inputBarcodeType.value = 'switchTask'
|
|
|
back.value = false
|
|
|
- excludedLocations.value = {}
|
|
|
+ if (systemForcePublishEnabled.value) excludedLocations.value = {}
|
|
|
locationType.value = ''
|
|
|
inputBarcodeRef.value?.show('', `请扫描容器号`, '')
|
|
|
}
|
|
|
@@ -448,6 +501,8 @@ const reset = () => {
|
|
|
putawayCombineData.value = null
|
|
|
combineMatchedSku.value = []
|
|
|
excludedLocations.value = {}
|
|
|
+ forbidForcePutaway.value = true // 重置为默认不强制,待下次推荐接口返回后再更新
|
|
|
+ locationType.value = ''
|
|
|
}
|
|
|
// 组合商品上架数量弹框
|
|
|
const _showPutawayCombineDialog = (batchItem) => {
|
|
|
@@ -475,18 +530,15 @@ const onCombineCancel = () => {
|
|
|
}
|
|
|
// 选择单据
|
|
|
const onDetailActive = (item) => {
|
|
|
- barcodeActiveList.value = item
|
|
|
lotBarcodeTrueFalseBy.value = false
|
|
|
if (putawayCombineData.value) {
|
|
|
+ barcodeActiveList.value = item
|
|
|
_showPutawayCombineDialog(item)
|
|
|
_getRecommendedLocation(item[0])
|
|
|
- scanType.value = 3
|
|
|
+ scanType.value = SCAN_TYPE.LOCATION
|
|
|
return
|
|
|
}
|
|
|
- searchCount.value = 1
|
|
|
- scanType.value = 3
|
|
|
- _getRecommendedLocation(item[0])
|
|
|
- scanSuccess()
|
|
|
+ _activateSingleBatch(item)
|
|
|
}
|
|
|
const onAsnCancel = () => {
|
|
|
if (searchBarcode.value === '' || (oldSearchBarcode.value.length != searchBarcode.value.length && oldSearchBarcode.value != '')) {
|
|
|
@@ -515,16 +567,9 @@ const _handlePutawayCombineProduct = (code) => {
|
|
|
closeLoading()
|
|
|
scanSuccess()
|
|
|
if (lotBarcodeList.value.length === 1) {
|
|
|
- barcodeActiveList.value = lotBarcodeList.value[0]
|
|
|
- _showPutawayCombineDialog(lotBarcodeList.value[0])
|
|
|
- _getRecommendedLocation(barcodeActiveList.value[0])
|
|
|
- scanType.value = 3
|
|
|
+ _activateSingleBatch(lotBarcodeList.value[0], { showCombineDialog: true })
|
|
|
} else {
|
|
|
- locationList.value = []
|
|
|
- barcodeActiveList.value = []
|
|
|
- searchCount.value = ''
|
|
|
- searchLocation.value = ''
|
|
|
- lotBarcodeTrueFalseBy.value = true
|
|
|
+ _openMultiBatchPicker()
|
|
|
}
|
|
|
})
|
|
|
.catch(() => {
|
|
|
@@ -537,7 +582,7 @@ const _handlePutawayCombineProduct = (code) => {
|
|
|
|
|
|
// 扫描条码监听
|
|
|
const _handlerScan = (code) => {
|
|
|
- if (scanType.value == 2) {
|
|
|
+ if (scanType.value === SCAN_TYPE.BARCODE) {
|
|
|
excludedLocations.value = {}
|
|
|
searchBarcode.value = code
|
|
|
oldSearchBarcode.value = code
|
|
|
@@ -545,50 +590,45 @@ const _handlerScan = (code) => {
|
|
|
if (lotBarcodeList.value.length > 0) {
|
|
|
putawayCombineData.value = null
|
|
|
combineMatchedSku.value = []
|
|
|
- if (lotBarcodeList.value.length == 1) {
|
|
|
- barcodeActiveList.value = lotBarcodeList.value[0]
|
|
|
- _getRecommendedLocation(barcodeActiveList.value[0])
|
|
|
- scanType.value = 3
|
|
|
- scanSuccess()
|
|
|
- } else if (lotBarcodeList.value.length > 1) {
|
|
|
- locationList.value = []
|
|
|
- barcodeActiveList.value = []
|
|
|
- searchCount.value = ''
|
|
|
- searchLocation.value = ''
|
|
|
- lotBarcodeTrueFalseBy.value = true
|
|
|
+ if (lotBarcodeList.value.length === 1) {
|
|
|
+ _activateSingleBatch(lotBarcodeList.value[0])
|
|
|
+ } else {
|
|
|
+ _openMultiBatchPicker()
|
|
|
}
|
|
|
} else {
|
|
|
_handlePutawayCombineProduct(code)
|
|
|
}
|
|
|
- } else if (scanType.value == 3) {
|
|
|
+ } else if (scanType.value === SCAN_TYPE.LOCATION) {
|
|
|
const scannedLocation = barcodeToUpperCase(code)
|
|
|
- if (!forcePublishEnabled.value) {
|
|
|
- const { lotAtt02 } = barcodeActiveList.value[0]
|
|
|
- if (locationList.value.length > 0 && !lotAtt02) {
|
|
|
- const recommendedLocations = locationList.value.map(item => barcodeToUpperCase(item.locationId || ''))
|
|
|
- if (!recommendedLocations.includes(scannedLocation)) {
|
|
|
- showNotify({ type: 'warning', duration: 3000, message: `扫描库位${scannedLocation}与推荐库位不一致,请确认` })
|
|
|
- searchLocation.value=''
|
|
|
- scanError()
|
|
|
- return
|
|
|
- }
|
|
|
- }
|
|
|
+ if (!isLocationInRecommendedList(scannedLocation)) {
|
|
|
+ showNotify({ type: 'warning', duration: 3000, message: `扫描库位${scannedLocation}与推荐库位不一致,请确认` })
|
|
|
+ searchLocation.value = ''
|
|
|
+ scanError()
|
|
|
+ return
|
|
|
}
|
|
|
searchLocation.value = scannedLocation
|
|
|
- scanType.value = 4
|
|
|
+ scanType.value = SCAN_TYPE.QUANTITY
|
|
|
if (!searchCount.value) searchCount.value = 1
|
|
|
scanSuccess()
|
|
|
}
|
|
|
}
|
|
|
-// 获取推荐库位
|
|
|
+// 获取推荐库位(根据 systemForcePublishEnabled 分流新旧接口)
|
|
|
const _getRecommendedLocation = async (item, options = {}) => {
|
|
|
+ const { lotNumber, owner, sku, lotAtt08 } = item
|
|
|
+ // 未开启精准推荐:走旧接口,仅展示推荐列表,不校验库位一致性
|
|
|
+ if (!systemForcePublishEnabled.value) {
|
|
|
+ try {
|
|
|
+ const res = await getRecommendedLocation({ warehouse, lotNum: lotNumber, owner })
|
|
|
+ locationList.value = res.data || []
|
|
|
+ } catch (err) {
|
|
|
+ console.error(err)
|
|
|
+ }
|
|
|
+ searchCount.value = 1
|
|
|
+ return
|
|
|
+ }
|
|
|
const { fromChangeLocation = false } = options
|
|
|
- const { lotNumber, owner } = item
|
|
|
- const { sku, quantity, lotAtt08 } = item
|
|
|
const listByLot = fromChangeLocation ? (excludedLocations.value[lotNumber] || []) : []
|
|
|
- const uniqueLocationIds = listByLot.length > 0
|
|
|
- ? [...new Set(listByLot.map(loc => loc.locationId).filter(Boolean))]
|
|
|
- : undefined
|
|
|
+ const uniqueLocationIds = getExcludedLocationIds(listByLot)
|
|
|
try {
|
|
|
const total = barcodeQuantity(barcodeActiveList.value)
|
|
|
const params = { warehouse, lotNum: lotNumber, owner, sku, qty: total, lotAtt08 }
|
|
|
@@ -596,7 +636,7 @@ const _getRecommendedLocation = async (item, options = {}) => {
|
|
|
if (fromChangeLocation && uniqueLocationIds) params.excludedLocations = uniqueLocationIds
|
|
|
const res = await getRecommendedLocationNew(params)
|
|
|
if (res.data) {
|
|
|
- forcePublishEnabled.value = res.data.forbidForcePutaway
|
|
|
+ forbidForcePutaway.value = res.data.forbidForcePutaway // 货主策略:是否禁止强制上架
|
|
|
const loc = res.data.locationList
|
|
|
if (fromChangeLocation) {
|
|
|
// 按批次维度存储已推荐库位(与接口 locationList 项一致,含 locationId),用于后续换一换排除
|
|
|
@@ -611,8 +651,8 @@ const _getRecommendedLocation = async (item, options = {}) => {
|
|
|
searchCount.value = 1
|
|
|
}
|
|
|
} catch (err) {
|
|
|
- forcePublishEnabled.value=true
|
|
|
- locationList.value=[]
|
|
|
+ forbidForcePutaway.value = true
|
|
|
+ locationList.value = []
|
|
|
scanError()
|
|
|
console.error(err)
|
|
|
}
|
|
|
@@ -625,7 +665,7 @@ const onChangeLocation = async () => {
|
|
|
try {
|
|
|
const item = barcodeActiveList.value[0]
|
|
|
const lotNumber = item.lotNumber
|
|
|
- const currentLoc = locationList.value?.[0]?.location ?? locationList.value?.[0]
|
|
|
+ const currentLoc = locationList.value?.[0]
|
|
|
if (currentLoc) {
|
|
|
const lotExcluded = excludedLocations.value[lotNumber] || []
|
|
|
excludedLocations.value = {
|
|
|
@@ -649,22 +689,17 @@ const isCheck = () => {
|
|
|
showToast({ duration: 3000, message: '请先扫描库位编号' })
|
|
|
return false
|
|
|
}
|
|
|
- if (!forcePublishEnabled.value) {
|
|
|
- if(barcodeActiveList.value.length ==0) {
|
|
|
- showToast({ duration: 3000, message: '数据异常请重新扫描' })
|
|
|
- scanError()
|
|
|
- return
|
|
|
- }
|
|
|
- const { lotAtt02 } = barcodeActiveList.value[0]
|
|
|
- if (locationList.value.length > 0 && !lotAtt02) {
|
|
|
- const recommendedLocations = locationList.value.map(item => barcodeToUpperCase(item.locationId || ''))
|
|
|
- if (!recommendedLocations.includes(barcodeToUpperCase(searchLocation.value))) {
|
|
|
- locationRef.value?.focus()
|
|
|
- scanError()
|
|
|
- showToast({ duration: 3000, message: '库位与推荐库位不一致,无法上架' })
|
|
|
- return false
|
|
|
- }
|
|
|
- }
|
|
|
+ if (barcodeActiveList.value.length === 0) {
|
|
|
+ showToast({ duration: 3000, message: '数据异常请重新扫描' })
|
|
|
+ scanError()
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ // 扫描库位与提交上架共用同一套强制匹配规则
|
|
|
+ if (!isLocationInRecommendedList(searchLocation.value)) {
|
|
|
+ locationRef.value?.focus()
|
|
|
+ scanError()
|
|
|
+ showToast({ duration: 3000, message: '库位与推荐库位不一致,无法上架' })
|
|
|
+ return false
|
|
|
}
|
|
|
if (searchCount.value == '') {
|
|
|
numberRef.value?.focus()
|
|
|
@@ -728,7 +763,7 @@ const onConfirm = () => {
|
|
|
} else {
|
|
|
showNotify({ type: 'success', message: '上架成功,请继续扫描商品进行上架', duration: 3000 })
|
|
|
setBarcode(containerNo.value, 'success')
|
|
|
- scanType.value = 2
|
|
|
+ scanType.value = SCAN_TYPE.BARCODE
|
|
|
}
|
|
|
reset()
|
|
|
scanSuccess()
|
|
|
@@ -739,9 +774,9 @@ const onConfirm = () => {
|
|
|
})
|
|
|
}
|
|
|
}
|
|
|
-const refresh=()=>{
|
|
|
+const refresh = () => {
|
|
|
reset()
|
|
|
- scanType.value=2
|
|
|
+ scanType.value = SCAN_TYPE.BARCODE
|
|
|
loadData()
|
|
|
}
|
|
|
|
|
|
@@ -852,30 +887,9 @@ window.onRefresh = loadData
|
|
|
font-size: 16px
|
|
|
font-weight: bold
|
|
|
|
|
|
- ::v-deep(.van-search__label),
|
|
|
- ::v-deep(.van-field__label)
|
|
|
- width: 6.5em
|
|
|
- min-width: 6.5em
|
|
|
- max-width: 6.5em
|
|
|
- flex: none
|
|
|
+ ::v-deep(.van-search__label)
|
|
|
font-size: 16px
|
|
|
font-weight: bold
|
|
|
- margin-right: 0
|
|
|
- text-align: left
|
|
|
-
|
|
|
- &.location-type-row
|
|
|
- ::v-deep(.van-cell__title)
|
|
|
- width: 6.5em
|
|
|
- min-width: 6.5em
|
|
|
- max-width: 6.5em
|
|
|
- flex: none
|
|
|
- font-size: 16px
|
|
|
- font-weight: bold
|
|
|
-
|
|
|
- ::v-deep(.van-cell__value)
|
|
|
- font-size: 14px
|
|
|
- font-weight: bold
|
|
|
- color: #323233
|
|
|
|
|
|
.search-input-barcode
|
|
|
::v-deep(.van-search__field)
|