|
|
@@ -2,33 +2,47 @@
|
|
|
<div class="merge-container">
|
|
|
<!-- 扫描输入框区域 -->
|
|
|
<div class="scan-section">
|
|
|
- <van-search
|
|
|
- ref="boxCodeInputRef"
|
|
|
- v-model.lazy="boxCode"
|
|
|
- placeholder="请扫描料箱号"
|
|
|
- left-icon=""
|
|
|
- :class="['scan-input', scanType === 1 ? 'scan-input-active' : '']"
|
|
|
- @search="onBoxCodeEnter"
|
|
|
- @focus="onBoxCodeClick"
|
|
|
- autocomplete="off"
|
|
|
- />
|
|
|
+ <div class="scan-display" @dblclick="editBoxCode">
|
|
|
+ <template v-if="isEditingBoxCode">
|
|
|
+ <van-search
|
|
|
+ ref="boxCodeInputRef"
|
|
|
+ v-model.lazy="boxCode"
|
|
|
+ placeholder="请扫描料箱号"
|
|
|
+ left-icon=""
|
|
|
+ class="scan-input"
|
|
|
+ @search="confirmBoxCode"
|
|
|
+ @blur="confirmBoxCode"
|
|
|
+ autocomplete="off"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <span v-if="boxCode">{{ boxCode }}</span>
|
|
|
+ <span v-else class="placeholder">双击扫描料箱号</span>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 信息展示表格 -->
|
|
|
<div class="info-table">
|
|
|
<div class="table-row">
|
|
|
<div class="cell label">源库位</div>
|
|
|
- <div class="cell value input-cell">
|
|
|
- <van-search
|
|
|
- ref="sourceLocationInputRef"
|
|
|
- v-model.lazy="sourceLocation"
|
|
|
- placeholder="请扫描源库位"
|
|
|
- left-icon=""
|
|
|
- class="table-search-input"
|
|
|
- @search="onSourceLocationEnter"
|
|
|
- @focus="onSourceLocationClick"
|
|
|
- autocomplete="off"
|
|
|
- />
|
|
|
+ <div class="cell value input-cell" @dblclick="editSourceLocation">
|
|
|
+ <template v-if="isEditingSourceLocation">
|
|
|
+ <van-search
|
|
|
+ ref="sourceLocationInputRef"
|
|
|
+ v-model.lazy="sourceLocation"
|
|
|
+ placeholder="请扫描源库位"
|
|
|
+ left-icon=""
|
|
|
+ class="table-search-input"
|
|
|
+ @search="confirmSourceLocation"
|
|
|
+ @blur="confirmSourceLocation"
|
|
|
+ autocomplete="off"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <span v-if="sourceLocation">{{ sourceLocation }}</span>
|
|
|
+ <span v-else class="placeholder">双击编辑</span>
|
|
|
+ </template>
|
|
|
</div>
|
|
|
<div class="cell label">库存数量</div>
|
|
|
<div class="cell value">{{ productInfo.stockQty }}</div>
|
|
|
@@ -39,17 +53,23 @@
|
|
|
</div>
|
|
|
<div class="table-row">
|
|
|
<div class="cell label">商品条码</div>
|
|
|
- <div class="cell value span-2 input-cell">
|
|
|
- <van-search
|
|
|
- ref="barcodeInputRef"
|
|
|
- v-model.lazy="scanBarcode"
|
|
|
- placeholder="请扫描商品条码"
|
|
|
- left-icon=""
|
|
|
- class="table-search-input"
|
|
|
- @search="onBarcodeEnter"
|
|
|
- @focus="onBarcodeClick"
|
|
|
- autocomplete="off"
|
|
|
- />
|
|
|
+ <div class="cell value span-2 input-cell" @dblclick="editBarcode">
|
|
|
+ <template v-if="isEditingBarcode">
|
|
|
+ <van-search
|
|
|
+ ref="barcodeInputRef"
|
|
|
+ v-model.lazy="scanBarcode"
|
|
|
+ placeholder="请扫描商品条码"
|
|
|
+ left-icon=""
|
|
|
+ class="table-search-input"
|
|
|
+ @search="confirmBarcode"
|
|
|
+ @blur="confirmBarcode"
|
|
|
+ autocomplete="off"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <span v-if="scanBarcode">{{ scanBarcode }}</span>
|
|
|
+ <span v-else class="placeholder">双击编辑</span>
|
|
|
+ </template>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="table-row row-small">
|
|
|
@@ -68,17 +88,23 @@
|
|
|
</div>
|
|
|
<div class="table-row">
|
|
|
<div class="cell label">目标库位</div>
|
|
|
- <div class="cell value input-cell input-wide">
|
|
|
- <van-search
|
|
|
- ref="targetLocationInputRef"
|
|
|
- v-model.lazy="productInfo.targetLocationNew"
|
|
|
- placeholder="请扫描目标库位"
|
|
|
- left-icon=""
|
|
|
- class="table-search-input"
|
|
|
- @search="onTargetLocationEnter"
|
|
|
- @focus="onTargetLocationClick"
|
|
|
- autocomplete="off"
|
|
|
- />
|
|
|
+ <div class="cell value input-cell input-wide" @dblclick="editTargetLocation">
|
|
|
+ <template v-if="isEditingTargetLocation">
|
|
|
+ <van-search
|
|
|
+ ref="targetLocationInputRef"
|
|
|
+ v-model.lazy="productInfo.targetLocationNew"
|
|
|
+ placeholder="请扫描目标库位"
|
|
|
+ left-icon=""
|
|
|
+ class="table-search-input"
|
|
|
+ @search="confirmTargetLocation"
|
|
|
+ @blur="confirmTargetLocation"
|
|
|
+ autocomplete="off"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <span v-if="productInfo.targetLocationNew">{{ productInfo.targetLocationNew }}</span>
|
|
|
+ <span v-else class="placeholder">双击编辑</span>
|
|
|
+ </template>
|
|
|
</div>
|
|
|
<div class="cell label">移库数量</div>
|
|
|
<div class="cell value editable" @dblclick="editMoveQty">
|
|
|
@@ -147,7 +173,7 @@
|
|
|
</div>
|
|
|
|
|
|
<!-- 底部按钮 -->
|
|
|
- <div class="footer-buttons">
|
|
|
+ <div class="footer-buttons" v-show="!isAnyInputFocused">
|
|
|
<van-button class="btn-robot" size="small" @click="callRobot">呼唤机器人</van-button>
|
|
|
<div class="btn-right">
|
|
|
<van-button type="primary" class="btn-reset" size="small" @click="resetInput">重新输入</van-button>
|
|
|
@@ -204,7 +230,7 @@
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
import { closeListener, openListener, scanInit } from '@/utils/keydownListener'
|
|
|
-import { onMounted, onUnmounted, ref, reactive, nextTick } from 'vue'
|
|
|
+import { onMounted, onUnmounted, ref, reactive, nextTick, computed } from 'vue'
|
|
|
import { showToast, showLoadingToast, closeToast } from 'vant'
|
|
|
import { useStore } from '@/store/modules/user'
|
|
|
import { getWorkingDetailsByBox, getBoxSplitCode, boxInbound, reissueTask, getBoxStatus, type BoxRelatedMergeDetailsVO, type LocationMergeDetails } from '@/api/location/merge'
|
|
|
@@ -249,34 +275,17 @@ onMounted(() => {
|
|
|
}
|
|
|
openListener()
|
|
|
scanInit(_handlerScan)
|
|
|
- // 获取焦点
|
|
|
+ // 默认进入料箱号编辑状态
|
|
|
nextTick(() => {
|
|
|
- focusBoxCodeInput()
|
|
|
+ editBoxCode()
|
|
|
})
|
|
|
- // 防止键盘弹出时底部按钮上移
|
|
|
- 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(() => {
|
|
|
@@ -312,40 +321,27 @@ const _handlerScan = (code: string) => {
|
|
|
if (scanType.value === 1) {
|
|
|
// 扫描料箱号
|
|
|
boxCode.value = code
|
|
|
+ isEditingBoxCode.value = false
|
|
|
loadBoxData(code)
|
|
|
} else if (scanType.value === 2) {
|
|
|
// 扫描源库位
|
|
|
sourceLocation.value = code
|
|
|
- onSourceLocationEnter()
|
|
|
+ isEditingSourceLocation.value = false
|
|
|
+ resetProductInfo()
|
|
|
+ scanType.value = 3
|
|
|
} else if (scanType.value === 3) {
|
|
|
// 扫描商品条码
|
|
|
scanBarcode.value = code
|
|
|
+ isEditingBarcode.value = false
|
|
|
onBarcodeEnter()
|
|
|
} else if (scanType.value === 4) {
|
|
|
// 扫描目标库位
|
|
|
productInfo.targetLocationNew = code
|
|
|
- onTargetLocationEnter()
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// 料箱号输入框点击 - 重置所有数据
|
|
|
-const onBoxCodeClick = () => {
|
|
|
- scanType.value = 1
|
|
|
-}
|
|
|
-
|
|
|
-// 料箱号回车
|
|
|
-const onBoxCodeEnter = () => {
|
|
|
- scanType.value = 1
|
|
|
- if (boxCode.value && boxCode.value.length > 5) {
|
|
|
- loadBoxData(boxCode.value)
|
|
|
+ isEditingTargetLocation.value = false
|
|
|
+ showToast(`已输入目标库位: ${code}`)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 源库位输入框点击 - 重置除料箱号外的数据
|
|
|
-const onSourceLocationClick = () => {
|
|
|
- scanType.value = 2
|
|
|
-}
|
|
|
-
|
|
|
// 源库位回车
|
|
|
const onSourceLocationEnter = () => {
|
|
|
if (!sourceLocation.value) return
|
|
|
@@ -355,17 +351,6 @@ const onSourceLocationEnter = () => {
|
|
|
|
|
|
// 切换到扫描商品条码
|
|
|
scanType.value = 3
|
|
|
- focusBarcodeInput()
|
|
|
-}
|
|
|
-
|
|
|
-// 商品条码输入框点击 - 只重置商品信息
|
|
|
-const onBarcodeClick = () => {
|
|
|
- scanType.value = 3
|
|
|
-}
|
|
|
-
|
|
|
-// 目标库位输入框点击
|
|
|
-const onTargetLocationClick = () => {
|
|
|
- scanType.value = 4
|
|
|
}
|
|
|
|
|
|
// 目标库位回车
|
|
|
@@ -417,8 +402,11 @@ const onBarcodeEnter = async () => {
|
|
|
scanSuccess()
|
|
|
showToast('商品信息获取成功')
|
|
|
// 切换到扫描目标库位
|
|
|
+ isEditingBarcode.value = false
|
|
|
scanType.value = 4
|
|
|
- focusTargetLocationInput()
|
|
|
+ nextTick(() => {
|
|
|
+ editTargetLocation()
|
|
|
+ })
|
|
|
} else {
|
|
|
scanError()
|
|
|
showToast('未找到库存信息')
|
|
|
@@ -442,8 +430,17 @@ const resetAllData = () => {
|
|
|
clickableLocationsMap.value = new Map()
|
|
|
resetProductInfo()
|
|
|
initStations()
|
|
|
+ // 关闭所有编辑状态
|
|
|
+ isEditingBoxCode.value = false
|
|
|
+ isEditingSourceLocation.value = false
|
|
|
+ isEditingBarcode.value = false
|
|
|
+ isEditingTargetLocation.value = false
|
|
|
+ isEditingMoveQty.value = false
|
|
|
scanType.value = 1
|
|
|
- focusBoxCodeInput()
|
|
|
+ // 进入料箱号编辑
|
|
|
+ nextTick(() => {
|
|
|
+ editBoxCode()
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
// 重置除料箱号外的数据
|
|
|
@@ -452,8 +449,16 @@ const resetExceptBoxCode = () => {
|
|
|
scanBarcode.value = ''
|
|
|
selectedBox.value = null
|
|
|
resetProductInfo()
|
|
|
+ // 关闭编辑状态
|
|
|
+ isEditingSourceLocation.value = false
|
|
|
+ isEditingBarcode.value = false
|
|
|
+ isEditingTargetLocation.value = false
|
|
|
+ isEditingMoveQty.value = false
|
|
|
scanType.value = 2
|
|
|
- focusSourceLocationInput()
|
|
|
+ // 进入源库位编辑
|
|
|
+ nextTick(() => {
|
|
|
+ editSourceLocation()
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
// 重置商品信息
|
|
|
@@ -524,9 +529,12 @@ const loadBoxData = async (code: string) => {
|
|
|
startPolling()
|
|
|
}
|
|
|
|
|
|
- // 加载完成后focus到源库位输入框
|
|
|
+ // 加载完成后进入源库位编辑状态
|
|
|
+ isEditingBoxCode.value = false
|
|
|
scanType.value = 2
|
|
|
- focusSourceLocationInput()
|
|
|
+ nextTick(() => {
|
|
|
+ editSourceLocation()
|
|
|
+ })
|
|
|
} catch (error: any) {
|
|
|
closeToast()
|
|
|
scanError()
|
|
|
@@ -618,6 +626,77 @@ const confirmMoveQty = () => {
|
|
|
isEditingMoveQty.value = false
|
|
|
}
|
|
|
|
|
|
+// 料箱号编辑
|
|
|
+const isEditingBoxCode = ref(false)
|
|
|
+const editBoxCode = () => {
|
|
|
+ isEditingBoxCode.value = true
|
|
|
+ scanType.value = 1
|
|
|
+ nextTick(() => {
|
|
|
+ boxCodeInputRef.value?.focus()
|
|
|
+ })
|
|
|
+}
|
|
|
+const confirmBoxCode = () => {
|
|
|
+ isEditingBoxCode.value = false
|
|
|
+ if (boxCode.value && boxCode.value.length > 5) {
|
|
|
+ loadBoxData(boxCode.value)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 源库位编辑
|
|
|
+const isEditingSourceLocation = ref(false)
|
|
|
+const editSourceLocation = () => {
|
|
|
+ isEditingSourceLocation.value = true
|
|
|
+ scanType.value = 2
|
|
|
+ nextTick(() => {
|
|
|
+ sourceLocationInputRef.value?.focus()
|
|
|
+ })
|
|
|
+}
|
|
|
+const confirmSourceLocation = () => {
|
|
|
+ isEditingSourceLocation.value = false
|
|
|
+ if (sourceLocation.value) {
|
|
|
+ resetProductInfo()
|
|
|
+ scanType.value = 3
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 商品条码编辑
|
|
|
+const isEditingBarcode = ref(false)
|
|
|
+const editBarcode = () => {
|
|
|
+ isEditingBarcode.value = true
|
|
|
+ scanType.value = 3
|
|
|
+ nextTick(() => {
|
|
|
+ barcodeInputRef.value?.focus()
|
|
|
+ })
|
|
|
+}
|
|
|
+const confirmBarcode = () => {
|
|
|
+ isEditingBarcode.value = false
|
|
|
+ if (scanBarcode.value) {
|
|
|
+ onBarcodeEnter()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 目标库位编辑
|
|
|
+const isEditingTargetLocation = ref(false)
|
|
|
+const editTargetLocation = () => {
|
|
|
+ isEditingTargetLocation.value = true
|
|
|
+ scanType.value = 4
|
|
|
+ nextTick(() => {
|
|
|
+ targetLocationInputRef.value?.focus()
|
|
|
+ })
|
|
|
+}
|
|
|
+const confirmTargetLocation = () => {
|
|
|
+ isEditingTargetLocation.value = false
|
|
|
+ if (productInfo.targetLocationNew) {
|
|
|
+ showToast(`已输入目标库位: ${productInfo.targetLocationNew}`)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 是否有任何输入框处于编辑状态(用于隐藏底部按钮)
|
|
|
+const isAnyInputFocused = computed(() => {
|
|
|
+ return isEditingBoxCode.value || isEditingSourceLocation.value ||
|
|
|
+ isEditingBarcode.value || isEditingTargetLocation.value || isEditingMoveQty.value
|
|
|
+})
|
|
|
+
|
|
|
// 站台数据结构
|
|
|
interface SubLocation {
|
|
|
id: string
|
|
|
@@ -977,7 +1056,9 @@ const confirmSelectLocation = () => {
|
|
|
showToast(`已选择源库位: ${currentLocation.id}`)
|
|
|
// 切换到扫描商品条码
|
|
|
scanType.value = 3
|
|
|
- focusBarcodeInput()
|
|
|
+ nextTick(() => {
|
|
|
+ editBarcode()
|
|
|
+ })
|
|
|
} else {
|
|
|
// 推荐保留库位,填入目标库位
|
|
|
productInfo.targetLocationNew = currentLocation.id
|
|
|
@@ -1087,25 +1168,41 @@ const submitMove = () => {
|
|
|
.scan-section {
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
+ .scan-display {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 4px;
|
|
|
+ padding: 10px 12px;
|
|
|
+ min-height: 24px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ cursor: pointer;
|
|
|
+
|
|
|
+ .placeholder {
|
|
|
+ color: #999;
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+
|
|
|
+ span:not(.placeholder) {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #333;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
.scan-input {
|
|
|
padding: 0;
|
|
|
background: #fff;
|
|
|
border-radius: 4px;
|
|
|
+ width: 100%;
|
|
|
|
|
|
:deep(.van-search__content) {
|
|
|
background: #fff;
|
|
|
- padding-left: 12px;
|
|
|
+ padding-left: 0;
|
|
|
}
|
|
|
|
|
|
:deep(.van-field__control) {
|
|
|
font-size: 14px;
|
|
|
}
|
|
|
-
|
|
|
- &.scan-input-active {
|
|
|
- :deep(.van-search__content) {
|
|
|
- border-bottom: 2px solid #1989fa;
|
|
|
- }
|
|
|
- }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -1154,15 +1251,22 @@ const submitMove = () => {
|
|
|
color: #333;
|
|
|
|
|
|
&.input-cell {
|
|
|
- padding: 0;
|
|
|
+ padding: 0 6px;
|
|
|
+ cursor: pointer;
|
|
|
+
|
|
|
+ .placeholder {
|
|
|
+ color: #999;
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
|
|
|
.table-search-input {
|
|
|
padding: 0;
|
|
|
background: transparent;
|
|
|
+ width: 100%;
|
|
|
|
|
|
:deep(.van-search__content) {
|
|
|
background: transparent;
|
|
|
- padding-left: 6px;
|
|
|
+ padding-left: 0;
|
|
|
}
|
|
|
|
|
|
:deep(.van-field__control) {
|
|
|
@@ -1348,19 +1452,20 @@ const submitMove = () => {
|
|
|
background: #fff;
|
|
|
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
|
|
|
z-index: 100;
|
|
|
- /* 防止键盘弹出时上移 */
|
|
|
- transform: translateZ(0);
|
|
|
|
|
|
.btn-right {
|
|
|
display: flex;
|
|
|
- gap: 12px; /* 增加按钮间距 */
|
|
|
+ /* 使用 margin 替代 gap,兼容安卓 WebView */
|
|
|
+ .van-button + .van-button {
|
|
|
+ margin-left: 12px;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.van-button {
|
|
|
height: 32px;
|
|
|
font-size: 13px;
|
|
|
- padding: 0 12px;
|
|
|
- min-width: 70px; /* 确保按钮最小宽度 */
|
|
|
+ padding: 0 14px;
|
|
|
+ min-width: 75px;
|
|
|
}
|
|
|
|
|
|
.btn-robot {
|