|
|
@@ -2,13 +2,15 @@
|
|
|
<div class="merge-container">
|
|
|
<!-- 扫描输入框区域 -->
|
|
|
<div class="scan-section">
|
|
|
- <van-field
|
|
|
+ <van-search
|
|
|
ref="boxCodeInputRef"
|
|
|
- v-model="boxCode"
|
|
|
+ v-model.lazy="boxCode"
|
|
|
placeholder="请扫描料箱号"
|
|
|
- clearable
|
|
|
- @click="onBoxCodeClick"
|
|
|
- @keyup.enter="onBoxCodeEnter"
|
|
|
+ left-icon=""
|
|
|
+ :class="['scan-input', scanType === 1 ? 'scan-input-active' : '']"
|
|
|
+ @search="onBoxCodeEnter"
|
|
|
+ @focus="onBoxCodeClick"
|
|
|
+ autocomplete="off"
|
|
|
/>
|
|
|
</div>
|
|
|
|
|
|
@@ -17,13 +19,15 @@
|
|
|
<div class="table-row">
|
|
|
<div class="cell label">源库位</div>
|
|
|
<div class="cell value input-cell">
|
|
|
- <van-field
|
|
|
+ <van-search
|
|
|
ref="sourceLocationInputRef"
|
|
|
- v-model="sourceLocation"
|
|
|
+ v-model.lazy="sourceLocation"
|
|
|
placeholder="请扫描源库位"
|
|
|
- clearable
|
|
|
- @click="onSourceLocationClick"
|
|
|
- @keyup.enter="onSourceLocationEnter"
|
|
|
+ left-icon=""
|
|
|
+ class="table-search-input"
|
|
|
+ @search="onSourceLocationEnter"
|
|
|
+ @focus="onSourceLocationClick"
|
|
|
+ autocomplete="off"
|
|
|
/>
|
|
|
</div>
|
|
|
<div class="cell label">库存数量</div>
|
|
|
@@ -36,13 +40,15 @@
|
|
|
<div class="table-row">
|
|
|
<div class="cell label">商品条码</div>
|
|
|
<div class="cell value span-2 input-cell">
|
|
|
- <van-field
|
|
|
+ <van-search
|
|
|
ref="barcodeInputRef"
|
|
|
- v-model="scanBarcode"
|
|
|
+ v-model.lazy="scanBarcode"
|
|
|
placeholder="请扫描商品条码"
|
|
|
- clearable
|
|
|
- @click="onBarcodeClick"
|
|
|
- @keyup.enter="onBarcodeEnter"
|
|
|
+ left-icon=""
|
|
|
+ class="table-search-input"
|
|
|
+ @search="onBarcodeEnter"
|
|
|
+ @focus="onBarcodeClick"
|
|
|
+ autocomplete="off"
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -63,13 +69,15 @@
|
|
|
<div class="table-row">
|
|
|
<div class="cell label">目标库位</div>
|
|
|
<div class="cell value input-cell input-wide">
|
|
|
- <van-field
|
|
|
+ <van-search
|
|
|
ref="targetLocationInputRef"
|
|
|
- v-model="productInfo.targetLocationNew"
|
|
|
+ v-model.lazy="productInfo.targetLocationNew"
|
|
|
placeholder="请扫描目标库位"
|
|
|
- clearable
|
|
|
- @click="onTargetLocationClick"
|
|
|
- @keyup.enter="onTargetLocationEnter"
|
|
|
+ left-icon=""
|
|
|
+ class="table-search-input"
|
|
|
+ @search="onTargetLocationEnter"
|
|
|
+ @focus="onTargetLocationClick"
|
|
|
+ autocomplete="off"
|
|
|
/>
|
|
|
</div>
|
|
|
<div class="cell label">移库数量</div>
|
|
|
@@ -245,13 +253,30 @@ onMounted(() => {
|
|
|
nextTick(() => {
|
|
|
focusBoxCodeInput()
|
|
|
})
|
|
|
+ // 防止键盘弹出时底部按钮上移
|
|
|
+ if (window.visualViewport) {
|
|
|
+ window.visualViewport.addEventListener('resize', handleViewportResize)
|
|
|
+ }
|
|
|
})
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
closeListener()
|
|
|
stopPolling()
|
|
|
+ // 移除viewport监听
|
|
|
+ if (window.visualViewport) {
|
|
|
+ window.visualViewport.removeEventListener('resize', handleViewportResize)
|
|
|
+ }
|
|
|
})
|
|
|
|
|
|
+// 处理viewport变化(键盘弹出/收起)
|
|
|
+const handleViewportResize = () => {
|
|
|
+ // 保持页面滚动位置,防止键盘弹出时页面跳动
|
|
|
+ const scrollTop = document.documentElement.scrollTop || document.body.scrollTop
|
|
|
+ requestAnimationFrame(() => {
|
|
|
+ window.scrollTo(0, scrollTop)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
// 设置料箱号输入框焦点
|
|
|
const focusBoxCodeInput = () => {
|
|
|
nextTick(() => {
|
|
|
@@ -283,7 +308,7 @@ const focusTargetLocationInput = () => {
|
|
|
// 扫描监听
|
|
|
const _handlerScan = (code: string) => {
|
|
|
if (!code) return
|
|
|
-
|
|
|
+
|
|
|
if (scanType.value === 1) {
|
|
|
// 扫描料箱号
|
|
|
boxCode.value = code
|
|
|
@@ -305,11 +330,12 @@ const _handlerScan = (code: string) => {
|
|
|
|
|
|
// 料箱号输入框点击 - 重置所有数据
|
|
|
const onBoxCodeClick = () => {
|
|
|
- resetAllData()
|
|
|
+ scanType.value = 1
|
|
|
}
|
|
|
|
|
|
// 料箱号回车
|
|
|
const onBoxCodeEnter = () => {
|
|
|
+ scanType.value = 1
|
|
|
if (boxCode.value && boxCode.value.length > 5) {
|
|
|
loadBoxData(boxCode.value)
|
|
|
}
|
|
|
@@ -317,16 +343,16 @@ const onBoxCodeEnter = () => {
|
|
|
|
|
|
// 源库位输入框点击 - 重置除料箱号外的数据
|
|
|
const onSourceLocationClick = () => {
|
|
|
- resetExceptBoxCode()
|
|
|
+ scanType.value = 2
|
|
|
}
|
|
|
|
|
|
// 源库位回车
|
|
|
const onSourceLocationEnter = () => {
|
|
|
if (!sourceLocation.value) return
|
|
|
-
|
|
|
+
|
|
|
// 清空商品信息
|
|
|
resetProductInfo()
|
|
|
-
|
|
|
+
|
|
|
// 切换到扫描商品条码
|
|
|
scanType.value = 3
|
|
|
focusBarcodeInput()
|
|
|
@@ -334,7 +360,7 @@ const onSourceLocationEnter = () => {
|
|
|
|
|
|
// 商品条码输入框点击 - 只重置商品信息
|
|
|
const onBarcodeClick = () => {
|
|
|
- resetProductInfo()
|
|
|
+ scanType.value = 3
|
|
|
}
|
|
|
|
|
|
// 目标库位输入框点击
|
|
|
@@ -358,36 +384,36 @@ const onBarcodeEnter = async () => {
|
|
|
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.lotAtt08 || inventoryData.qualityStatus || ''
|
|
|
- productInfo.warehouseType = inventoryData.lotAtt05 || inventoryData.warehouseType || ''
|
|
|
- productInfo.batchNo = inventoryData.lotNumber || inventoryData.lotNum || ''
|
|
|
- productInfo.productionDate = inventoryData.lotAtt01 || inventoryData.productionDate || ''
|
|
|
- productInfo.expiryDate = inventoryData.lotAtt02 || inventoryData.expiryDate || ''
|
|
|
- productInfo.moveQty = inventoryData.quantityAvailable || inventoryData.quantity || ''
|
|
|
-
|
|
|
+ productInfo.stockQty = inventoryData.quantity
|
|
|
+ productInfo.productName = inventoryData.productName
|
|
|
+ productInfo.barcode = inventoryData.barcode || inventoryData.barcode2
|
|
|
+ productInfo.qualityStatus = inventoryData.lotAtt08
|
|
|
+ productInfo.warehouseType = inventoryData.lotAtt05
|
|
|
+ productInfo.batchNo = inventoryData.lotNumber
|
|
|
+ productInfo.productionDate = inventoryData.lotAtt01
|
|
|
+ productInfo.expiryDate = inventoryData.lotAtt02
|
|
|
+ productInfo.moveQty = inventoryData.quantityAvailable + inventoryData.quantityAvailable
|
|
|
+
|
|
|
scanSuccess()
|
|
|
showToast('商品信息获取成功')
|
|
|
// 切换到扫描目标库位
|
|
|
@@ -492,12 +518,12 @@ const loadBoxData = async (code: string) => {
|
|
|
|
|
|
closeToast()
|
|
|
scanSuccess()
|
|
|
-
|
|
|
+
|
|
|
// 加载完成后检查是否需要启动轮询
|
|
|
if (hasWaitingBox()) {
|
|
|
startPolling()
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 加载完成后focus到源库位输入框
|
|
|
scanType.value = 2
|
|
|
focusSourceLocationInput()
|
|
|
@@ -808,19 +834,19 @@ const refreshBoxStatus = async () => {
|
|
|
try {
|
|
|
const res = await getBoxStatus(warehouse, boxCodeList)
|
|
|
const statusMap = res.data || {}
|
|
|
-
|
|
|
+
|
|
|
// 更新站台列表中的状态
|
|
|
stationList.value = stationList.value.map(station => {
|
|
|
if (!station.boxCode || !statusMap.hasOwnProperty(station.boxCode)) {
|
|
|
return station
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
const newBoxStatus = statusMap[station.boxCode]
|
|
|
const inventoryLocations = station.inventoryLocations || []
|
|
|
-
|
|
|
+
|
|
|
let status: StationItem['status'] = 'offline'
|
|
|
let label: string | undefined = undefined
|
|
|
-
|
|
|
+
|
|
|
if (newBoxStatus === 0 || newBoxStatus === 10) {
|
|
|
status = 'waiting'
|
|
|
label = '等待调箱'
|
|
|
@@ -837,10 +863,10 @@ const refreshBoxStatus = async () => {
|
|
|
status = 'error'
|
|
|
label = '异'
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return { ...station, status, label }
|
|
|
})
|
|
|
-
|
|
|
+
|
|
|
// 检查是否需要继续轮询
|
|
|
if (hasWaitingBox()) {
|
|
|
startPolling()
|
|
|
@@ -944,9 +970,20 @@ const selectSubLocation = (station: StationItem, sub: SubLocation) => {
|
|
|
|
|
|
// 确认选择库位
|
|
|
const confirmSelectLocation = () => {
|
|
|
- productInfo.targetLocationNew = currentLocation.id
|
|
|
- showLocationPopup.value = false
|
|
|
- showToast(`已选择库位: ${currentLocation.id}`)
|
|
|
+ if (currentLocation.recommendType === 'clear') {
|
|
|
+ // 推荐清空库位,填入源库位
|
|
|
+ sourceLocation.value = currentLocation.id
|
|
|
+ showLocationPopup.value = false
|
|
|
+ showToast(`已选择源库位: ${currentLocation.id}`)
|
|
|
+ // 切换到扫描商品条码
|
|
|
+ scanType.value = 3
|
|
|
+ focusBarcodeInput()
|
|
|
+ } else {
|
|
|
+ // 推荐保留库位,填入目标库位
|
|
|
+ productInfo.targetLocationNew = currentLocation.id
|
|
|
+ showLocationPopup.value = false
|
|
|
+ showToast(`已选择目标库位: ${currentLocation.id}`)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// 呼唤机器人
|
|
|
@@ -957,6 +994,8 @@ const callRobot = async () => {
|
|
|
closeToast()
|
|
|
scanSuccess()
|
|
|
showToast('呼唤机器人成功')
|
|
|
+ // 重置页面数据并聚焦到料箱输入框
|
|
|
+ resetAllData()
|
|
|
} catch (error: any) {
|
|
|
closeToast()
|
|
|
scanError()
|
|
|
@@ -1050,8 +1089,25 @@ const submitMove = () => {
|
|
|
.scan-section {
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
- :deep(.van-field) {
|
|
|
+ .scan-input {
|
|
|
+ padding: 0;
|
|
|
+ background: #fff;
|
|
|
border-radius: 4px;
|
|
|
+
|
|
|
+ :deep(.van-search__content) {
|
|
|
+ background: #fff;
|
|
|
+ padding-left: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.van-field__control) {
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.scan-input-active {
|
|
|
+ :deep(.van-search__content) {
|
|
|
+ border-bottom: 2px solid #1989fa;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -1098,23 +1154,25 @@ const submitMove = () => {
|
|
|
|
|
|
&.value {
|
|
|
color: #333;
|
|
|
-
|
|
|
+
|
|
|
&.input-cell {
|
|
|
padding: 0;
|
|
|
-
|
|
|
- :deep(.van-field) {
|
|
|
- padding: 4px 6px;
|
|
|
-
|
|
|
- .van-field__body {
|
|
|
- height: 24px;
|
|
|
+
|
|
|
+ .table-search-input {
|
|
|
+ padding: 0;
|
|
|
+ background: transparent;
|
|
|
+
|
|
|
+ :deep(.van-search__content) {
|
|
|
+ background: transparent;
|
|
|
+ padding-left: 6px;
|
|
|
}
|
|
|
-
|
|
|
- .van-field__control {
|
|
|
+
|
|
|
+ :deep(.van-field__control) {
|
|
|
font-size: 13px;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
&.editable {
|
|
|
cursor: pointer;
|
|
|
min-height: 20px;
|
|
|
@@ -1288,18 +1346,28 @@ const submitMove = () => {
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
padding: 10px 12px;
|
|
|
+ padding-bottom: calc(10px + env(safe-area-inset-bottom));
|
|
|
background: #fff;
|
|
|
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
|
|
|
+ z-index: 100;
|
|
|
+ /* 防止键盘弹出时上移 */
|
|
|
+ transform: translate3d(0, 0, 0);
|
|
|
+ backface-visibility: hidden;
|
|
|
+ -webkit-backface-visibility: hidden;
|
|
|
|
|
|
.btn-right {
|
|
|
display: flex;
|
|
|
- gap: 8px;
|
|
|
+ /* 使用 margin 替代 gap,兼容安卓 WebView */
|
|
|
+ .van-button + .van-button {
|
|
|
+ margin-left: 12px;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.van-button {
|
|
|
height: 32px;
|
|
|
font-size: 13px;
|
|
|
- padding: 0 12px;
|
|
|
+ padding: 0 14px;
|
|
|
+ min-width: 75px;
|
|
|
}
|
|
|
|
|
|
.btn-robot {
|