Explorar o código

宝时快上-换一换增加库位类型选择

zhaohuanhuan hai 1 semana
pai
achega
4ae556f81f

+ 9 - 0
src/api/basic/index.ts

@@ -45,6 +45,15 @@ export function findSysParamByKey(params: { paramKey: string }) {
   })
   })
 }
 }
 
 
+/** 库位类型列表 */
+export function getLocationTypeList(params: { warehouse: string }) {
+  return request({
+    url: '/api/basic/location/type/list',
+    method: 'get',
+    params,
+  })
+}
+
 
 
 
 
 /**
 /**

+ 5 - 1
src/types/haikang.ts

@@ -90,9 +90,13 @@ export interface getRecommendedLocationTypeNew {
    */
    */
   scene?: string;
   scene?: string;
   /**
   /**
-   * 库位类型
+   * 上架区域
    */
    */
   locationUse?: string;
   locationUse?: string;
+  /**
+   * 库位类型名称
+   */
+  locationType?: string;
   [property: string]: any;
   [property: string]: any;
 }
 }
 export interface setBindAllocateWallType {
 export interface setBindAllocateWallType {

+ 2 - 0
src/views/inbound/putaway/components/LocationList.vue

@@ -40,6 +40,8 @@ const locationType = {
   'SS': '理货站',
   'SS': '理货站',
   'ST': '过渡库位',
   'ST': '过渡库位',
   'WB': '组装工作区',
   'WB': '组装工作区',
+  'ZZ': '周转区',
+  'DZ': '叠装区',
 }
 }
 const props = defineProps({
 const props = defineProps({
   locationList: Array,
   locationList: Array,

+ 221 - 0
src/views/inbound/putaway/components/LocationTypePicker.vue

@@ -0,0 +1,221 @@
+<template>
+  <van-dialog
+    v-model:show="visible"
+    class="location-type-dialog-wrap"
+    :title="description"
+    :show-cancel-button="false"
+    :show-confirm-button="false"
+    :close-on-click-overlay="false"
+  >
+    <div class="location-type-dialog">
+      <div v-if="loading" class="location-type-dialog__loading">
+        <van-loading type="spinner" size="18px" color="#1989fa">加载中</van-loading>
+      </div>
+      <van-radio-group v-else v-model="selectedIndex" class="location-type-dialog__list">
+        <template v-if="fetchedList.length">
+          <label
+            v-for="(item, index) in fetchedList"
+            :key="index"
+            class="location-type-dialog__card"
+            :class="{ 'location-type-dialog__card--active': selectedIndex === index }"
+            @click="selectedIndex = index"
+          >
+            <span class="card-size">宽{{ item.width }} 深{{ item.length }} 高{{ item.height }}</span>
+            <span class="card-qty">数量{{ item.qty }}</span>
+            <van-radio class="card-radio" :name="index" icon-size="18px" />
+          </label>
+        </template>
+        <van-empty v-else class="location-type-dialog__empty" image-size="48" description="暂无数据" />
+      </van-radio-group>
+    </div>
+    <template #footer>
+      <div class="location-type-dialog__footer">
+        <van-button plain type="primary" size="small" @click="onPickAll">全部库位类型</van-button>
+        <van-button type="primary" size="small" @click="onConfirmClick">确定</van-button>
+      </div>
+    </template>
+  </van-dialog>
+</template>
+
+<script setup>
+import { ref } from 'vue'
+import { showToast } from 'vant'
+import { getLocationTypeList } from '@/api/basic/index'
+
+const props = defineProps({
+  description: { type: String, default: '选择库位类型' },
+  warehouse: { type: String, default: '' },
+})
+
+const emit = defineEmits(['confirm', 'cancel', 'loaded'])
+
+const visible = ref(false)
+const loading = ref(false)
+const fetchedList = ref([])
+const selectedIndex = ref(-1)
+const loadedWarehouse = ref('')
+
+const loadTypes = async (warehouse) => {
+  if (!warehouse) {
+    fetchedList.value = []
+    loadedWarehouse.value = ''
+    return
+  }
+  if (loadedWarehouse.value === warehouse) return
+
+  loading.value = true
+  try {
+    const res = await getLocationTypeList({ warehouse })
+    fetchedList.value = res.data || []
+    loadedWarehouse.value = warehouse
+    emit('loaded', fetchedList.value)
+  } catch (e) {
+    console.error(e)
+    fetchedList.value = []
+    loadedWarehouse.value = ''
+    showToast({ duration: 2000, message: '库位类型加载失败' })
+  } finally {
+    loading.value = false
+  }
+}
+
+const clearCache = () => {
+  fetchedList.value = []
+  loadedWarehouse.value = ''
+}
+
+const onPick = (item) => {
+  visible.value = false
+  emit('confirm', item)
+}
+
+const onPickAll = () => {
+  onPick({ value: '', label: '全部库位类型' })
+}
+
+const onConfirmClick = () => {
+  if (selectedIndex.value < 0) {
+    showToast({ duration: 2000, message: '请选择库位类型' })
+    return
+  }
+  const item = fetchedList.value[selectedIndex.value]
+  if (item) onPick({ value: item.typeName, label: item.typeName })
+}
+
+const show = async (warehouse) => {
+  selectedIndex.value = -1
+  visible.value = true
+  await loadTypes(warehouse || props.warehouse)
+}
+
+defineExpose({
+  show,
+  clearCache,
+  reload: () => {
+    loadedWarehouse.value = ''
+    return loadTypes(props.warehouse)
+  },
+})
+</script>
+
+<style scoped lang="sass">
+$active: #f5f9ff
+$border-active: #a8d4ff
+
+.location-type-dialog
+  text-align: left
+
+  &__loading
+    display: flex
+    justify-content: center
+    padding: 12px 0
+
+  &__list
+    max-height: 34vh
+    overflow-y: auto
+    padding: 4px
+    background: #f5f6f8
+    border-radius: 8px
+
+  &__card
+    display: flex
+    align-items: center
+    gap: 8px
+    width: 100%
+    height: 38px
+    margin-bottom: 6px
+    padding: 0 8px 0 12px
+    background: #fff
+    border: 1px solid #e8e8e8
+    border-radius: 6px
+    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04)
+    white-space: nowrap
+    cursor: pointer
+    box-sizing: border-box
+
+    &:last-child
+      margin-bottom: 0
+
+    &--active
+      border-color: $border-active
+      background: $active
+      box-shadow: 0 1px 3px rgba(25, 137, 250, 0.12)
+
+  .card-size
+    flex: 1
+    min-width: 0
+    font-size: 13px
+    color: #323233
+    overflow: hidden
+    text-overflow: ellipsis
+
+  .card-qty
+    flex-shrink: 0
+    font-size: 13px
+    font-weight: 600
+    color: #1989fa
+
+  .card-radio
+    flex-shrink: 0
+    margin-left: auto
+
+    :deep(.van-radio__label)
+      display: none
+
+  &__footer
+    display: flex
+    gap: 8px
+    box-sizing: border-box
+    width: 100%
+    padding: 10px 12px 12px
+
+    :deep(.van-button)
+      flex: 1
+      min-width: 0
+      height: 36px
+      margin: 0
+      padding: 0 4px
+      font-size: 13px
+      border-radius: 8px
+
+      :deep(.van-button__text)
+        overflow: hidden
+        text-overflow: ellipsis
+        white-space: nowrap
+
+:deep(.location-type-dialog-wrap.van-dialog)
+  width: 90%
+  max-width: 340px
+  border-radius: 12px
+  overflow: hidden
+
+  .van-dialog__header
+    padding: 16px 16px 10px
+    font-weight: 600
+
+  .van-dialog__content
+    padding: 0 12px
+
+  .van-dialog__footer
+    padding: 0
+</style>

+ 41 - 21
src/views/inbound/putaway/task/index.vue

@@ -111,7 +111,7 @@
           </van-search>
           </van-search>
         </div>
         </div>
         <div class="barcode-input location-type-row" v-if="systemForcePublishEnabled" >
         <div class="barcode-input location-type-row" v-if="systemForcePublishEnabled" >
-          <van-cell title="上架区域:" :value="locationTypeLabel"  is-link @click="locationTypeSheetShow = true" />
+          <van-cell title="上架区域:" :value="locationTypeLabel" is-link @click="locationTypeSheetShow = true" />
         </div>
         </div>
       </div>
       </div>
       <div class="take-lot" v-if="barcodeActiveList.length>0">
       <div class="take-lot" v-if="barcodeActiveList.length>0">
@@ -154,7 +154,7 @@
       </van-cell>
       </van-cell>
     </van-cell-group>
     </van-cell-group>
   </van-action-sheet>
   </van-action-sheet>
-  <!--  库位类型选择-->
+  <!--  上架区域-->
   <van-action-sheet v-model:show="locationTypeSheetShow" cancel-text="取消" description="上架区域"
   <van-action-sheet v-model:show="locationTypeSheetShow" cancel-text="取消" description="上架区域"
                     close-on-click-action>
                     close-on-click-action>
     <van-cell-group>
     <van-cell-group>
@@ -163,8 +163,17 @@
       <van-cell title="存储区" @click="onSelectLocationType('STORAGE')" />
       <van-cell title="存储区" @click="onSelectLocationType('STORAGE')" />
       <van-cell title="退货区" @click="onSelectLocationType('RETURN')" />
       <van-cell title="退货区" @click="onSelectLocationType('RETURN')" />
       <van-cell title="挂装区" @click="onSelectLocationType('HANGING')" />
       <van-cell title="挂装区" @click="onSelectLocationType('HANGING')" />
+      <van-cell title="周转区" @click="onSelectLocationType('ZZ')" />
+      <van-cell title="叠装区" @click="onSelectLocationType('DZ')" />
     </van-cell-group>
     </van-cell-group>
   </van-action-sheet>
   </van-action-sheet>
+  <!--  库位类型-->
+  <location-type-picker
+    ref="changeLocationTypePickerRef"
+    :warehouse="warehouse"
+    description="选择库位类型"
+    @confirm="doChangeLocationWithType"
+  />
   <!--  组合商品上架数量-->
   <!--  组合商品上架数量-->
   <barcode-combine ref="barcodeCombineRef" @setCombine="setPutawayCombine" @cancel="onCombineCancel" :matched-sku="combineMatchedSku" />
   <barcode-combine ref="barcodeCombineRef" @setCombine="setPutawayCombine" @cancel="onCombineCancel" :matched-sku="combineMatchedSku" />
 </template>
 </template>
@@ -175,6 +184,7 @@ import { androidFocus, getHeader, goBack, scanError, scanSuccess } from '@/utils
 import InputBarcode from '@/views/outbound/picking/components/InputBarcode.vue'
 import InputBarcode from '@/views/outbound/picking/components/InputBarcode.vue'
 import LocationList from '@/views/inbound/putaway/components/LocationList.vue'
 import LocationList from '@/views/inbound/putaway/components/LocationList.vue'
 import BarcodeCombine from '@/views/inbound/putaway/components/BarcodeCombine.vue'
 import BarcodeCombine from '@/views/inbound/putaway/components/BarcodeCombine.vue'
+import LocationTypePicker from '@/views/inbound/putaway/components/LocationTypePicker.vue'
 import { openListener,closeListener,scanInit } from '@/utils/keydownListener.js'
 import { openListener,closeListener,scanInit } from '@/utils/keydownListener.js'
 import { useRouter } from 'vue-router'
 import { useRouter } from 'vue-router'
 import { closeLoading, showLoading } from '@/utils/loading'
 import { closeLoading, showLoading } from '@/utils/loading'
@@ -351,6 +361,7 @@ const setBarcode = (code, type) => {
     formattedTime.value = '00:00:00'
     formattedTime.value = '00:00:00'
     totalSeconds.value = 0
     totalSeconds.value = 0
     locationType.value = ''
     locationType.value = ''
+    changeLocationTypePickerRef.value?.clearCache()
   }
   }
   const params = { warehouseId:warehouse, containerId: code }
   const params = { warehouseId:warehouse, containerId: code }
   getWaitPutawayListNew(params).then(res => {
   getWaitPutawayListNew(params).then(res => {
@@ -458,6 +469,7 @@ const switchTask = () => {
   back.value = false
   back.value = false
   if (systemForcePublishEnabled.value) excludedLocations.value = {}
   if (systemForcePublishEnabled.value) excludedLocations.value = {}
   locationType.value = ''
   locationType.value = ''
+  changeLocationTypePickerRef.value?.clearCache()
   inputBarcodeRef.value?.show('', `请扫描容器号`, '')
   inputBarcodeRef.value?.show('', `请扫描容器号`, '')
 }
 }
 
 
@@ -473,11 +485,13 @@ const combineMatchedSku = ref([])
 const excludedLocations = ref({})
 const excludedLocations = ref({})
 // 换一换按钮 loading
 // 换一换按钮 loading
 const changeLocationLoading = ref(false)
 const changeLocationLoading = ref(false)
-// 库位类型,默认全部
+// 上架区域,默认全部
 const locationType = ref('')
 const locationType = ref('')
 const locationTypeSheetShow = ref(false)
 const locationTypeSheetShow = ref(false)
+const changeLocationTypePickerRef = ref(null)
 const locationTypeLabel = computed(() => {
 const locationTypeLabel = computed(() => {
-  const map = { PICKING: '拣货位', STORAGE: '存储位', RETURN: '退货区', HANGING: '挂装区' }
+  const map = { PICKING: '拣货位', STORAGE: '存储位', RETURN: '退货区', HANGING: '挂装区',ZZ: '周转区', DZ: '叠装区'
+}
   return locationType.value ? map[locationType.value] : '全部'
   return locationType.value ? map[locationType.value] : '全部'
 })
 })
 const onSelectLocationType = async (value = '') => {
 const onSelectLocationType = async (value = '') => {
@@ -631,6 +645,7 @@ const _getRecommendedLocation = async (item, options = {}) => {
     const total = barcodeQuantity(barcodeActiveList.value)
     const total = barcodeQuantity(barcodeActiveList.value)
     const params = { warehouse, lotNum: lotNumber, owner, sku, qty: total, lotAtt08 }
     const params = { warehouse, lotNum: lotNumber, owner, sku, qty: total, lotAtt08 }
     if (locationType.value) params.locationUse = locationType.value
     if (locationType.value) params.locationUse = locationType.value
+    if (options.changeTypeName) params.locationType = options.changeTypeName
     if (fromChangeLocation && uniqueLocationIds) params.excludedLocations = uniqueLocationIds
     if (fromChangeLocation && uniqueLocationIds) params.excludedLocations = uniqueLocationIds
     const res = await getRecommendedLocationNew(params)
     const res = await getRecommendedLocationNew(params)
     if (res.data) {
     if (res.data) {
@@ -656,25 +671,30 @@ const _getRecommendedLocation = async (item, options = {}) => {
   }
   }
 }
 }
 
 
-// 换一换:请求新的推荐库位,排除当前批次已推荐过的
-const onChangeLocation = async () => {
-  if (barcodeActiveList.value.length > 0) {
-    changeLocationLoading.value = true
-    try {
-      const item = barcodeActiveList.value[0]
-      const lotNumber = item.lotNumber
-      const currentLoc = locationList.value?.[0]
-      if (currentLoc) {
-        const lotExcluded = excludedLocations.value[lotNumber] || []
-        excludedLocations.value = {
-          ...excludedLocations.value,
-          [lotNumber]: [...lotExcluded, currentLoc]
-        }
+// 换一换:先选库位类型,再请求新的推荐库位
+const onChangeLocation = () => {
+  if (barcodeActiveList.value.length === 0) return
+  changeLocationTypePickerRef.value?.show(warehouse)
+}
+const doChangeLocationWithType = async (item) => {
+  if (barcodeActiveList.value.length === 0) return
+  changeLocationLoading.value = true
+  try {
+    const batchItem = barcodeActiveList.value[0]
+    const lotNumber = batchItem.lotNumber
+    const currentLoc = locationList.value?.[0]
+    if (currentLoc) {
+      const lotExcluded = excludedLocations.value[lotNumber] || []
+      excludedLocations.value = {
+        ...excludedLocations.value,
+        [lotNumber]: [...lotExcluded, currentLoc]
       }
       }
-      await _getRecommendedLocation(item, { fromChangeLocation: true })
-    } finally {
-      changeLocationLoading.value = false
     }
     }
+    const reqOpts = { fromChangeLocation: true }
+    if (item?.value) reqOpts.changeTypeName = item.value
+    await _getRecommendedLocation(batchItem, reqOpts)
+  } finally {
+    changeLocationLoading.value = false
   }
   }
 }
 }
 const numberRef = ref(null)
 const numberRef = ref(null)