Browse Source

库存并库init

zh 3 months ago
parent
commit
b11e8898e1
2 changed files with 276 additions and 33 deletions
  1. 12 0
      src/api/location/merge.ts
  2. 264 33
      src/views/robot/merge/index.vue

+ 12 - 0
src/api/location/merge.ts

@@ -56,3 +56,15 @@ export function getBoxSplitCode(warehouse: string, locations: string[]) {
     data: locations
   })
 }
+
+/**
+ * 执行料箱回库(呼唤机器人)
+ * @param warehouse 仓库
+ */
+export function boxInbound(warehouse: string) {
+  return request<[string, string][]>({
+    url: '/api/wms/location/merge/boxInbound',
+    method: 'post',
+    params: { warehouse }
+  })
+}

+ 264 - 33
src/views/robot/merge/index.vue

@@ -1,11 +1,30 @@
 <template>
   <div class="merge-container">
-    <!-- 扫描输入框 -->
+    <!-- 扫描输入框区域 -->
     <div class="scan-section">
       <van-field
+        ref="boxCodeInputRef"
         v-model="boxCode"
         placeholder="请扫描料箱号"
         clearable
+        @click="onBoxCodeClick"
+        @keyup.enter="onBoxCodeEnter"
+      />
+      <van-field
+        ref="sourceLocationInputRef"
+        v-model="sourceLocation"
+        placeholder="请扫描源库位"
+        clearable
+        @click="onSourceLocationClick"
+        @keyup.enter="onSourceLocationEnter"
+      />
+      <van-field
+        ref="barcodeInputRef"
+        v-model="scanBarcode"
+        placeholder="请扫描商品条码"
+        clearable
+        @click="onBarcodeClick"
+        @keyup.enter="onBarcodeEnter"
       />
     </div>
 
@@ -174,14 +193,30 @@
 
 <script setup lang="ts">
 import { closeListener, openListener, scanInit } from '@/utils/keydownListener'
-import { onMounted, onUnmounted, ref, reactive, watch, nextTick } from 'vue'
+import { onMounted, onUnmounted, ref, reactive, nextTick } from 'vue'
 import { showToast, showLoadingToast, closeToast } from 'vant'
 import { useStore } from '@/store/modules/user'
-import { getWorkingDetailsByBox, getBoxSplitCode, type BoxRelatedMergeDetailsVO, type LocationMergeDetails } from '@/api/location/merge'
+import { getWorkingDetailsByBox, getBoxSplitCode, boxInbound, type BoxRelatedMergeDetailsVO, type LocationMergeDetails } from '@/api/location/merge'
+import { getInventory, inventoryMovement } from '@/api/inventory'
+import { showConfirmDialog } from 'vant'
 
 const store = useStore()
 const warehouse = store.warehouse
 
+// 扫描类型: 1=料箱号, 2=源库位, 3=商品条码
+const scanType = ref(1)
+
+// 输入框引用
+const boxCodeInputRef = ref<any>(null)
+const sourceLocationInputRef = ref<any>(null)
+const barcodeInputRef = ref<any>(null)
+
+// 扫描料箱号
+const boxCode = ref('')
+// 源库位
+const sourceLocation = ref('')
+// 商品条码
+const scanBarcode = ref('')
 
 // 页面初始化
 onMounted(() => {
@@ -189,7 +224,7 @@ onMounted(() => {
   scanInit(_handlerScan)
   // 获取焦点
   nextTick(() => {
-    focusScanInput()
+    focusBoxCodeInput()
   })
 })
 
@@ -197,32 +232,171 @@ onUnmounted(() => {
   closeListener()
 })
 
-// 设置扫描输入框焦点
-const focusScanInput = () => {
-  const input = document.querySelector('.scan-section input') as HTMLInputElement
-  if (input) {
-    input.focus()
-  }
+// 设置料箱号输入框焦点
+const focusBoxCodeInput = () => {
+  nextTick(() => {
+    boxCodeInputRef.value?.focus()
+  })
+}
+
+// 设置源库位输入框焦点
+const focusSourceLocationInput = () => {
+  nextTick(() => {
+    sourceLocationInputRef.value?.focus()
+  })
 }
 
-// 扫描条码监听
+// 设置商品条码输入框焦点
+const focusBarcodeInput = () => {
+  nextTick(() => {
+    barcodeInputRef.value?.focus()
+  })
+}
+
+// 扫描监听
 const _handlerScan = (code: string) => {
-  if (code) {
+  if (!code) return
+  
+  if (scanType.value === 1) {
+    // 扫描料箱号
     boxCode.value = code
     loadBoxData(code)
+  } else if (scanType.value === 2) {
+    // 扫描源库位
+    sourceLocation.value = code
+    onSourceLocationEnter()
+  } else if (scanType.value === 3) {
+    // 扫描商品条码
+    scanBarcode.value = code
+    onBarcodeEnter()
   }
 }
 
-// 扫描料箱号
-const boxCode = ref('')
+// 料箱号输入框点击 - 重置所有数据
+const onBoxCodeClick = () => {
+  resetAllData()
+}
 
-// 监听 boxCode 变化,当有值时调用接口
-watch(boxCode, (newVal) => {
-  if (newVal && newVal.length > 5) {
-    // 防抖处理,避免输入过程中频繁调用
-    loadBoxData(newVal)
+// 料箱号回车
+const onBoxCodeEnter = () => {
+  if (boxCode.value && boxCode.value.length > 5) {
+    loadBoxData(boxCode.value)
   }
-})
+}
+
+// 源库位输入框点击 - 重置除料箱号外的数据
+const onSourceLocationClick = () => {
+  resetExceptBoxCode()
+}
+
+// 源库位回车
+const onSourceLocationEnter = () => {
+  if (!sourceLocation.value) return
+  
+  // 清空商品信息
+  resetProductInfo()
+  
+  // 切换到扫描商品条码
+  scanType.value = 3
+  focusBarcodeInput()
+}
+
+// 商品条码输入框点击 - 只重置商品信息
+const onBarcodeClick = () => {
+  resetProductInfo()
+}
+
+// 当前选中的库存数据(用于提交移库)
+const currentInventoryData = ref<any>(null)
+
+// 商品条码回车 - 调用getInventory获取商品信息
+const onBarcodeEnter = async () => {
+  if (!scanBarcode.value) return
+  if (!sourceLocation.value) {
+    showToast('请先扫描源库位')
+    return
+  }
+  
+  try {
+    showLoadingToast({ message: '查询中...', forbidClick: true })
+    
+    const params = {
+      warehouse,
+      barcode: scanBarcode.value,
+      location: sourceLocation.value,
+      locationRegexp: '^(?!STAGE_|SORTATION_).*$'
+    }
+    
+    const res = await getInventory(params)
+    closeToast()
+    
+    if (res.data && res.data.length > 0) {
+      const inventoryData = res.data[0]
+      // 保存完整的库存数据
+      currentInventoryData.value = inventoryData
+      // 填充商品信息
+      productInfo.targetLocation = sourceLocation.value
+      productInfo.stockQty = inventoryData.quantityAvailable || inventoryData.quantity || ''
+      productInfo.productName = inventoryData.productName || inventoryData.skuName || ''
+      productInfo.barcode = inventoryData.barcode || scanBarcode.value
+      productInfo.qualityStatus = inventoryData.qualityStatus || ''
+      productInfo.warehouseType = inventoryData.warehouseType || ''
+      productInfo.batchNo = inventoryData.lotNum || ''
+      productInfo.productionDate = inventoryData.productionDate || ''
+      productInfo.expiryDate = inventoryData.expiryDate || ''
+      productInfo.moveQty = inventoryData.quantityAvailable || inventoryData.quantity || ''
+      
+      showToast('商品信息获取成功')
+    } else {
+      showToast('未找到库存信息')
+      currentInventoryData.value = null
+      resetProductInfo()
+    }
+  } catch (error: any) {
+    closeToast()
+    showToast(error.message || '查询失败')
+  }
+}
+
+// 重置所有数据(回到最开始状态)
+const resetAllData = () => {
+  boxCode.value = ''
+  sourceLocation.value = ''
+  scanBarcode.value = ''
+  selectedBox.value = null
+  mergeDataList.value = []
+  clickableLocationsMap.value = new Map()
+  resetProductInfo()
+  initStations()
+  scanType.value = 1
+  focusBoxCodeInput()
+}
+
+// 重置除料箱号外的数据
+const resetExceptBoxCode = () => {
+  sourceLocation.value = ''
+  scanBarcode.value = ''
+  selectedBox.value = null
+  resetProductInfo()
+  scanType.value = 2
+  focusSourceLocationInput()
+}
+
+// 重置商品信息
+const resetProductInfo = () => {
+  scanBarcode.value = ''
+  currentInventoryData.value = null
+  productInfo.targetLocation = ''
+  productInfo.stockQty = ''
+  productInfo.productName = ''
+  productInfo.barcode = ''
+  productInfo.qualityStatus = ''
+  productInfo.warehouseType = ''
+  productInfo.batchNo = ''
+  productInfo.productionDate = ''
+  productInfo.expiryDate = ''
+  productInfo.moveQty = ''
+}
 
 // 加载料箱数据
 const loadBoxData = async (code: string) => {
@@ -268,6 +442,10 @@ const loadBoxData = async (code: string) => {
     }
 
     closeToast()
+    
+    // 加载完成后focus到源库位输入框
+    scanType.value = 2
+    focusSourceLocationInput()
   } catch (error: any) {
     closeToast()
     showToast(error.message || '加载失败')
@@ -285,7 +463,7 @@ const buildClickableLocationsMap = (boxDetailsList: BoxRelatedMergeDetailsVO[])
     boxDetail.mergeDetails?.forEach((detail: LocationMergeDetails) => {
       const sourceLocation = detail.sourceLocation
       const targetLocation = detail.targetLocation
-      const quantity = detail.moveQty || 0
+      const quantity = detail.quantity || 0
 
       // 处理源库位(推荐清空库位)
       if (sourceLocation) {
@@ -594,20 +772,21 @@ const confirmSelectLocation = () => {
 }
 
 // 呼唤机器人
-const callRobot = () => {
-  showToast('正在呼唤机器人...')
+const callRobot = async () => {
+  try {
+    showLoadingToast({ message: '正在呼唤机器人...', forbidClick: true })
+    await boxInbound(warehouse)
+    closeToast()
+    showToast('呼唤机器人成功')
+  } catch (error: any) {
+    closeToast()
+    showToast(error.message || '呼唤机器人失败')
+  }
 }
 
-// 重新输入
+// 重新输入(不重置料箱号)
 const resetInput = () => {
-  boxCode.value = ''
-  selectedBox.value = null
-  mergeDataList.value = []
-  clickableLocationsMap.value = new Map()
-  initStations()
-  nextTick(() => {
-    focusScanInput()
-  })
+  resetExceptBoxCode()
 }
 
 // 提交移库
@@ -616,7 +795,59 @@ const submitMove = () => {
     showToast('请先扫描料箱号')
     return
   }
-  showToast('提交移库成功')
+  if (!sourceLocation.value) {
+    showToast('请先扫描源库位')
+    return
+  }
+  if (!productInfo.barcode) {
+    showToast('请先扫描商品条码')
+    return
+  }
+  if (!productInfo.targetLocationNew) {
+    showToast('请先选择目标库位')
+    return
+  }
+  if (!productInfo.moveQty || Number(productInfo.moveQty) <= 0) {
+    showToast('请输入有效的移库数量')
+    return
+  }
+  if (Number(productInfo.moveQty) > Number(productInfo.stockQty)) {
+    showToast('移库数量不能大于库存数量')
+    return
+  }
+
+  showConfirmDialog({
+    title: '移库确认',
+    message: `${productInfo.barcode}从"${sourceLocation.value}"移动至"${productInfo.targetLocationNew}"共:${productInfo.moveQty}件`
+  })
+    .then(() => {
+      const { traceId, lotNum, lotNumber, ownerCode, owner, sku } = currentInventoryData.value || {}
+      const data = {
+        fmLocation: sourceLocation.value,
+        fmContainer: traceId || boxCode.value,
+        owner: ownerCode || owner || '',
+        sku: sku || productInfo.barcode,
+        lotNum: lotNum || lotNumber || productInfo.batchNo || '',
+        warehouse,
+        quantity: Number(productInfo.moveQty),
+        toLocation: productInfo.targetLocationNew
+      }
+      showLoadingToast({ message: '提交中...', forbidClick: true })
+      inventoryMovement(data)
+        .then(() => {
+          closeToast()
+          showToast('提交移库成功')
+          // 重置除料箱号外的数据,继续下一个移库
+          resetExceptBoxCode()
+        })
+        .catch((err: any) => {
+          closeToast()
+          showToast(err.message || '提交移库失败')
+        })
+    })
+    .catch(() => {
+      // 用户取消
+    })
 }
 </script>