|
|
@@ -6,9 +6,27 @@
|
|
|
<van-icon name="arrow-left" size="25" />
|
|
|
<div style="color: #fff">返回</div>
|
|
|
</template>
|
|
|
- <!-- <template #right>-->
|
|
|
- <!-- <div class="nav-right" @click="onClickRight">提交任务</div>-->
|
|
|
- <!-- </template>-->
|
|
|
+ <template #right>
|
|
|
+ <div
|
|
|
+ v-if="bluetoothConnected && connectedBluetoothDevice"
|
|
|
+ @click="handleDisconnectBluetooth"
|
|
|
+ class="nav-bluetooth-connected"
|
|
|
+ >
|
|
|
+ <div class="bluetooth-device-info">
|
|
|
+ <van-icon name="bluetooth" size="14px" />
|
|
|
+ <span class="device-name">{{ connectedBluetoothDevice.name }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="bluetooth-disconnect-text">蓝牙断开</div>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ v-else
|
|
|
+ @click="handleOpenBluetoothScan"
|
|
|
+ class="nav-bluetooth-scan"
|
|
|
+ >
|
|
|
+ <van-icon name="bluetooth" size="16px" />
|
|
|
+ <span>蓝牙扫描</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
</van-nav-bar>
|
|
|
<div class="activity">
|
|
|
<div class="wave-title">
|
|
|
@@ -21,12 +39,33 @@
|
|
|
<div class="scan-barcode">
|
|
|
<van-field v-model.lazy="scanBarcode" label-align="left" placeholder="请扫描商品条码/SKU" label="商品条码:"
|
|
|
class="input-barcode" autocomplete="off" @keydown.enter="_handlerScan(scanBarcode)" />
|
|
|
- <van-field v-model.lazy="totalWeight" ref="weightRef" label-align="left" required placeholder="请输入商品重量KG" label="重 量:"
|
|
|
- autocomplete="off" type="number" >
|
|
|
- <template #button>
|
|
|
- <div>KG</div>
|
|
|
- </template>
|
|
|
- </van-field>
|
|
|
+ <div class="weight-input-wrapper">
|
|
|
+ <van-field v-model.lazy="totalWeight" ref="weightRef" label-align="left" required placeholder="请输入商品重量KG" label="重 量:"
|
|
|
+ autocomplete="off" type="number" @input="handleWeightInput" @focus="handleWeightFocus" @keydown="handleWeightKeydown"
|
|
|
+ :class="{ 'weight-confirmed': weightConfirmed }">
|
|
|
+ <template #button>
|
|
|
+ <div>KG</div>
|
|
|
+ </template>
|
|
|
+ </van-field>
|
|
|
+ <van-button
|
|
|
+ v-if="bluetoothConnected"
|
|
|
+ type="warning"
|
|
|
+ size="small"
|
|
|
+ class="weight-reset-btn"
|
|
|
+ @click="resetWeight"
|
|
|
+ >
|
|
|
+ 重置
|
|
|
+ </van-button>
|
|
|
+ <van-button
|
|
|
+ v-if="bluetoothConnected"
|
|
|
+ type="primary"
|
|
|
+ size="small"
|
|
|
+ class="weight-confirm-btn"
|
|
|
+ @click="confirmWeight"
|
|
|
+ >
|
|
|
+ 确认
|
|
|
+ </van-button>
|
|
|
+ </div>
|
|
|
<!-- :class="[totalWeight>0?'success-input-barcode':'error-input-barcode']"-->
|
|
|
</div>
|
|
|
<div class="order-detail">
|
|
|
@@ -88,17 +127,19 @@
|
|
|
</tr>
|
|
|
</thead>
|
|
|
<tbody>
|
|
|
- <tr v-for="(item, index) in orderList" :key="index" v-if="orderList.length>0" :style="rowStyle(item)" >
|
|
|
- <td>{{ item.barcode }} <van-tag type="success" v-if="item.universalCode">万用</van-tag></td>
|
|
|
- <td>{{item.qty}}/{{item.qtyOrdered}}</td>
|
|
|
- <td>{{ item.qty }}</td>
|
|
|
- <td>{{ statusMap[item.status] || '' }}</td>
|
|
|
- <td v-if="isUniqueCode || isQualityCheck">
|
|
|
- <van-tag v-if="item.uniqueRegExp" type="warning" >唯一码</van-tag>
|
|
|
- <van-tag v-if="item.imeiRegExp" type="primary" >IMEI码</van-tag>
|
|
|
- <van-tag v-if="item.qualityCheck" type="warning" >质检</van-tag>
|
|
|
- </td>
|
|
|
- </tr>
|
|
|
+ <template v-if="orderList.length>0">
|
|
|
+ <tr v-for="(item, index) in orderList" :key="index" :style="rowStyle(item)" >
|
|
|
+ <td>{{ item.barcode }} <van-tag type="success" v-if="item.universalCode">万用</van-tag></td>
|
|
|
+ <td>{{item.qty}}/{{item.qtyOrdered}}</td>
|
|
|
+ <td>{{ item.qty }}</td>
|
|
|
+ <td>{{ statusMap[item.status] || '' }}</td>
|
|
|
+ <td v-if="isUniqueCode || isQualityCheck">
|
|
|
+ <van-tag v-if="item.uniqueRegExp" type="warning" >唯一码</van-tag>
|
|
|
+ <van-tag v-if="item.imeiRegExp" type="primary" >IMEI码</van-tag>
|
|
|
+ <van-tag v-if="item.qualityCheck" type="warning" >质检</van-tag>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ </template>
|
|
|
<tr v-else>
|
|
|
<td colspan="4">
|
|
|
<div>暂无数据</div>
|
|
|
@@ -121,6 +162,8 @@
|
|
|
<related-materia ref="relatedMateriaRef" @cut-barcode="cutBarcode" />
|
|
|
<!-- 复核组合商品-->
|
|
|
<check-barcode-combine ref="checkBarcodeCombineRef" @cutBarcode="cutBarcode" />
|
|
|
+ <!-- 蓝牙扫描-->
|
|
|
+ <bluetooth-scan :model-value="showBluetoothScan" @update:model-value="showBluetoothScan = $event" @connected="handleBluetoothConnected" @disconnected="handleBluetoothDisconnected" ref="bluetoothScanRef" />
|
|
|
</div>
|
|
|
</template>
|
|
|
<script setup>
|
|
|
@@ -139,22 +182,192 @@ import orderListTable from '@/views/outbound/check/components/OrderListTable.vue
|
|
|
import ReversePicking from '@/views/outbound/check/components/ReversePicking.vue'
|
|
|
import RelatedMateria from '@/views/outbound/check/components/RelatedMateria.vue'
|
|
|
import CheckBarcodeCombine from '@/views/outbound/check/components/CheckBarcodeCombine.vue'
|
|
|
+import BluetoothScan from '@/views/outbound/check/components/BluetoothScan.vue'
|
|
|
const store = useStore()
|
|
|
try {
|
|
|
getHeader()
|
|
|
androidFocus()
|
|
|
} catch (error) {
|
|
|
}
|
|
|
+// 打开蓝牙扫描(立即开始扫描)
|
|
|
+const handleOpenBluetoothScan = () => {
|
|
|
+ showBluetoothScan.value = true
|
|
|
+ // 延迟一下确保弹窗已打开,然后开始扫描
|
|
|
+ setTimeout(() => {
|
|
|
+ if (bluetoothScanRef.value) {
|
|
|
+ bluetoothScanRef.value.startScan()
|
|
|
+ }
|
|
|
+ }, 300)
|
|
|
+}
|
|
|
+
|
|
|
+// 蓝牙连接处理
|
|
|
+const handleBluetoothConnected = (device) => {
|
|
|
+ bluetoothConnected.value = true
|
|
|
+ connectedBluetoothDevice.value = device
|
|
|
+ weightConfirmed.value = false // 连接成功时重置确认状态,允许接收新的重量数据
|
|
|
+ // showNotify({ type: 'success', message: `已连接: ${device.name}` })
|
|
|
+}
|
|
|
+
|
|
|
+// 蓝牙断开处理
|
|
|
+const handleBluetoothDisconnected = () => {
|
|
|
+ bluetoothConnected.value = false
|
|
|
+ connectedBluetoothDevice.value = null
|
|
|
+ weightConfirmed.value = false // 断开时重置确认状态
|
|
|
+}
|
|
|
+
|
|
|
+// 停止接收蓝牙重量(用户输入或确认时调用)
|
|
|
+const stopReceivingBluetoothWeight = () => {
|
|
|
+ if (bluetoothConnected.value) {
|
|
|
+ weightConfirmed.value = true
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 处理重量输入(手动输入时立即停止接收蓝牙重量)
|
|
|
+const handleWeightInput = () => {
|
|
|
+ stopReceivingBluetoothWeight()
|
|
|
+}
|
|
|
+
|
|
|
+// 处理重量输入框获得焦点(用户开始输入时立即停止接收蓝牙重量)
|
|
|
+const handleWeightFocus = () => {
|
|
|
+ stopReceivingBluetoothWeight()
|
|
|
+}
|
|
|
+
|
|
|
+// 处理键盘输入(按下任何键时立即停止接收蓝牙重量)
|
|
|
+const handleWeightKeydown = () => {
|
|
|
+ stopReceivingBluetoothWeight()
|
|
|
+}
|
|
|
+
|
|
|
+// 重置重量(重置后继续接收蓝牙重量)
|
|
|
+const resetWeight = () => {
|
|
|
+ weightConfirmed.value = false
|
|
|
+ totalWeight.value = ''
|
|
|
+ showNotify({ type: 'success', duration: 2000, message: '已重置,继续接收蓝牙重量' })
|
|
|
+}
|
|
|
+
|
|
|
+// 确认重量(确认后不再接收蓝牙重量)
|
|
|
+const confirmWeight = () => {
|
|
|
+ if (!totalWeight.value || Number(totalWeight.value) <= 0) {
|
|
|
+ showNotify({ type: 'warning', duration: 3000, message: '请输入有效的重量值' })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ stopReceivingBluetoothWeight()
|
|
|
+ showNotify({ type: 'success', duration: 2000, message: '重量已确认' })
|
|
|
+}
|
|
|
+
|
|
|
+// 蓝牙重量数据回调(仅在未输入且未确认时接收蓝牙重量)
|
|
|
+const handleBluetoothWeight = (weight, unit) => {
|
|
|
+ console.log(weight,unit,'handleBluetoothWeight')
|
|
|
+ // 只有在未确认重量(未输入且未点击确认)时才自动更新
|
|
|
+ if (!weightConfirmed.value && weight !== null && weight !== undefined) {
|
|
|
+ totalWeight.value = weight.toString()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 蓝牙连接状态回调
|
|
|
+const handleBluetoothConnectionState = (isConnected) => {
|
|
|
+ bluetoothConnected.value = isConnected
|
|
|
+ if (bluetoothScanRef.value) {
|
|
|
+ bluetoothScanRef.value.handleConnectionState(isConnected)
|
|
|
+ }
|
|
|
+ if (!isConnected) {
|
|
|
+ // 断开连接时清除状态
|
|
|
+ handleBluetoothDisconnected()
|
|
|
+ // 确保组件内的状态也被清除
|
|
|
+ if (bluetoothScanRef.value) {
|
|
|
+ bluetoothScanRef.value.clearConnectedDevice()
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 连接成功时,从组件获取设备信息
|
|
|
+ const saved = localStorage.getItem('bluetooth-device')
|
|
|
+ if (saved) {
|
|
|
+ try {
|
|
|
+ const device = JSON.parse(saved)
|
|
|
+ handleBluetoothConnected(device)
|
|
|
+ } catch (error) {
|
|
|
+ console.error('解析保存的设备失败:', error)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 蓝牙错误回调
|
|
|
+const handleBluetoothError = (errorMessage) => {
|
|
|
+ showNotify({ type: 'danger', message: `蓝牙错误: ${errorMessage}` })
|
|
|
+}
|
|
|
+
|
|
|
+// 初始化蓝牙回调
|
|
|
+const initBluetoothCallbacks = () => {
|
|
|
+ // 连接状态回调
|
|
|
+ window.onScaleConnectionState = handleBluetoothConnectionState
|
|
|
+
|
|
|
+ // 重量数据回调
|
|
|
+ window.onScaleWeight = handleBluetoothWeight
|
|
|
+
|
|
|
+ // 错误回调
|
|
|
+ window.onScaleError = handleBluetoothError
|
|
|
+}
|
|
|
+
|
|
|
+// 断开蓝牙连接
|
|
|
+const disconnectBluetooth = () => {
|
|
|
+ if (window.AndroidScale && bluetoothConnected.value) {
|
|
|
+ try {
|
|
|
+ window.AndroidScale.disconnect()
|
|
|
+ // 立即清除主页面状态
|
|
|
+ handleBluetoothDisconnected()
|
|
|
+ // 立即清除组件内的状态
|
|
|
+ if (bluetoothScanRef.value) {
|
|
|
+ bluetoothScanRef.value.clearConnectedDevice()
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('断开连接失败:', error)
|
|
|
+ showNotify({ type: 'danger', message: '断开连接失败' })
|
|
|
+ // 即使出错也清除状态
|
|
|
+ handleBluetoothDisconnected()
|
|
|
+ if (bluetoothScanRef.value) {
|
|
|
+ bluetoothScanRef.value.clearConnectedDevice()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 如果没有 AndroidScale 或未连接,也清除状态
|
|
|
+ handleBluetoothDisconnected()
|
|
|
+ if (bluetoothScanRef.value) {
|
|
|
+ bluetoothScanRef.value.clearConnectedDevice()
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 处理断开连接(带确认)
|
|
|
+const handleDisconnectBluetooth = () => {
|
|
|
+ if (!bluetoothConnected.value) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ showConfirmDialog({
|
|
|
+ title: '温馨提示',
|
|
|
+ message: '确定要断开蓝牙连接吗?',
|
|
|
+ }).then(() => {
|
|
|
+ disconnectBluetooth()
|
|
|
+ }).catch(() => {
|
|
|
+ // 取消操作
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
// 页面初始化
|
|
|
onMounted(() => {
|
|
|
openListener()
|
|
|
scanInit(_handlerScan)
|
|
|
+ initBluetoothCallbacks()
|
|
|
loadData()
|
|
|
+ // 延迟加载已保存的蓝牙设备,确保组件已挂载
|
|
|
+ setTimeout(() => {
|
|
|
+ if (bluetoothScanRef.value) {
|
|
|
+ bluetoothScanRef.value.loadSavedDevice()
|
|
|
+ }
|
|
|
+ }, 100)
|
|
|
})
|
|
|
const warehouse = store.warehouse
|
|
|
//收货容器号
|
|
|
// JH-WH99-990
|
|
|
-const waveNo = ref('')
|
|
|
+const waveNo = ref('JH-WH99-990')
|
|
|
const isUniqueCode=ref(false)
|
|
|
const isQualityCheck=ref(false)
|
|
|
// 错误提示
|
|
|
@@ -163,6 +376,11 @@ const tips = ref('')
|
|
|
const back = ref(true)
|
|
|
const scanBarcode=ref('')
|
|
|
const totalWeight=ref('')
|
|
|
+const showBluetoothScan=ref(false)
|
|
|
+const bluetoothConnected=ref(false)
|
|
|
+const bluetoothScanRef=ref(null)
|
|
|
+const connectedBluetoothDevice=ref(null)
|
|
|
+const weightConfirmed=ref(false) // 重量是否已确认
|
|
|
const containerNoMap={
|
|
|
'WH01':'FJ-WH01-20',
|
|
|
'WH02':'FJ-WH02-20',
|
|
|
@@ -342,11 +560,13 @@ const reset=()=>{
|
|
|
waveNo.value=''
|
|
|
scanBarcode.value=''
|
|
|
totalWeight.value=''
|
|
|
+ weightConfirmed.value = false // 重置确认状态
|
|
|
orderDetail.value={}
|
|
|
orderMap.value={}
|
|
|
dataList.value=[]
|
|
|
matchBarcodeList.value=[]
|
|
|
tips.value='请扫描商品条码'
|
|
|
+ // 注意:不自动断开蓝牙连接,保持连接状态
|
|
|
inputBarcodeRef.value?.show('', '请扫描波次/容器号', '')
|
|
|
})
|
|
|
}
|
|
|
@@ -410,7 +630,7 @@ const endCheck=()=>{
|
|
|
return
|
|
|
}
|
|
|
const lastNumber=Object.keys(orderMap.value.dataGroup).length
|
|
|
- if (totalWeight.value<=0 && lastNumber>0) {
|
|
|
+ if (Number(totalWeight.value)<=0 && lastNumber>0) {
|
|
|
tips.value = '请输入重量';
|
|
|
scanError()
|
|
|
showNotify({ type: 'warning', duration: 3000, message: '请输入重量' });
|
|
|
@@ -564,6 +784,7 @@ const setBarcode = (code) => {
|
|
|
matchBarcodeList.value=[]
|
|
|
scanBarcode.value=''
|
|
|
totalWeight.value=''
|
|
|
+ weightConfirmed.value = false // 切换波次时重置确认状态
|
|
|
successNumber.value=0
|
|
|
}
|
|
|
}).catch(err => {
|
|
|
@@ -628,6 +849,11 @@ const loadData = () => {
|
|
|
}
|
|
|
onUnmounted(() => {
|
|
|
closeListener()
|
|
|
+ // 清理蓝牙回调
|
|
|
+ disconnectBluetooth()
|
|
|
+ window.onScaleConnectionState = null
|
|
|
+ window.onScaleWeight = null
|
|
|
+ window.onScaleError = null
|
|
|
})
|
|
|
|
|
|
window.onRefresh = loadData
|
|
|
@@ -667,6 +893,31 @@ window.onRefresh = loadData
|
|
|
font-size: 16px
|
|
|
display: flex
|
|
|
align-items: center
|
|
|
+
|
|
|
+ .weight-input-wrapper
|
|
|
+ display: flex
|
|
|
+ align-items: center
|
|
|
+ gap: 8px
|
|
|
+ width: 100%
|
|
|
+
|
|
|
+ ::v-deep(.van-field)
|
|
|
+ flex: 1
|
|
|
+
|
|
|
+ ::v-deep(.weight-confirmed)
|
|
|
+ .van-field__control
|
|
|
+ border-bottom: 2px solid #1ca600 !important
|
|
|
+ color: #1ca600
|
|
|
+
|
|
|
+ .weight-reset-btn
|
|
|
+ flex-shrink: 0
|
|
|
+ height: 36px
|
|
|
+ padding: 0 16px
|
|
|
+
|
|
|
+ .weight-confirm-btn
|
|
|
+ flex-shrink: 0
|
|
|
+ height: 36px
|
|
|
+ padding: 0 16px
|
|
|
+ margin-right: 15px
|
|
|
.input-barcode
|
|
|
::v-deep(.van-field__control)
|
|
|
border-bottom: 2px solid #0077ff
|
|
|
@@ -754,4 +1005,68 @@ window.onRefresh = loadData
|
|
|
.task-table-bin tbody
|
|
|
background: #cde7ff
|
|
|
|
|
|
+ .nav-bluetooth-scan
|
|
|
+ display: flex
|
|
|
+ align-items: center
|
|
|
+ gap: 4px
|
|
|
+ padding: 6px 12px
|
|
|
+ background: rgba(255, 255, 255, 0.2)
|
|
|
+ border: 1px solid rgba(255, 255, 255, 0.3)
|
|
|
+ border-radius: 16px
|
|
|
+ font-size: 13px
|
|
|
+ color: #fff
|
|
|
+ cursor: pointer
|
|
|
+ transition: all 0.3s ease
|
|
|
+ user-select: none
|
|
|
+ white-space: nowrap
|
|
|
+
|
|
|
+ &:active
|
|
|
+ background: rgba(255, 255, 255, 0.3)
|
|
|
+ transform: scale(0.95)
|
|
|
+
|
|
|
+ .van-icon
|
|
|
+ color: #4fc3f7
|
|
|
+
|
|
|
+ .nav-bluetooth-connected
|
|
|
+ display: flex
|
|
|
+ flex-direction: column
|
|
|
+ align-items: flex-end
|
|
|
+ gap: 2px
|
|
|
+ padding: 4px 10px
|
|
|
+ background: rgba(76, 175, 80, 0.2)
|
|
|
+ border: 1px solid rgba(76, 175, 80, 0.4)
|
|
|
+ border-radius: 12px
|
|
|
+ cursor: pointer
|
|
|
+ transition: all 0.3s ease
|
|
|
+ user-select: none
|
|
|
+ min-width: 80px
|
|
|
+
|
|
|
+ &:active
|
|
|
+ background: rgba(76, 175, 80, 0.3)
|
|
|
+ transform: scale(0.95)
|
|
|
+
|
|
|
+ .bluetooth-device-info
|
|
|
+ display: flex
|
|
|
+ align-items: center
|
|
|
+ gap: 4px
|
|
|
+ font-size: 12px
|
|
|
+ color: #fff
|
|
|
+ font-weight: 500
|
|
|
+ line-height: 1.2
|
|
|
+
|
|
|
+ .van-icon
|
|
|
+ color: #4caf50
|
|
|
+ flex-shrink: 0
|
|
|
+
|
|
|
+ .device-name
|
|
|
+ max-width: 100px
|
|
|
+ overflow: hidden
|
|
|
+ text-overflow: ellipsis
|
|
|
+ white-space: nowrap
|
|
|
+
|
|
|
+ .bluetooth-disconnect-text
|
|
|
+ font-size: 10px
|
|
|
+ color: rgba(255, 255, 255, 0.8)
|
|
|
+ line-height: 1.2
|
|
|
+
|
|
|
</style>
|