Files
pda-uniapp/pages/scanner-test/scanner-test.vue
2026-07-30 18:04:37 +08:00

152 lines
7.5 KiB
Vue

<template>
<view v-if="user" class="page-shell portal-page scanner-test-page">
<PortalHeader title="广播扫码测试" :user="user" />
<view class="scanner-test-content">
<view class="scanner-status" :class="`scanner-status-${status}`" role="status">
<view class="scanner-status-dot"></view>
<view>
<text class="scanner-status-title">{{ statusTitle }}</text>
<text class="scanner-status-detail">{{ statusDetail }}</text>
</view>
</view>
<view class="scanner-result" :class="{ 'scanner-result-received': lastValue }">
<text class="scanner-result-label">最近收到</text>
<text class="scanner-result-value">{{ lastValue || '等待扫描' }}</text>
<text class="scanner-result-meta">{{ lastValue ? `${lastTime} · 本页第 ${scanCount} 次` : '按下 PDA 硬件扫描键' }}</text>
</view>
<view class="scanner-config">
<view class="scanner-config-row"><text class="scanner-config-label">Action</text><text class="scanner-config-value">{{ action }}</text></view>
<view class="scanner-config-row"><text class="scanner-config-label">Extra 标签</text><text class="scanner-config-value">{{ extra }}</text></view>
</view>
<view class="scanner-history-head">
<text class="scanner-history-title">接收记录</text>
<button class="scanner-clear" :disabled="!history.length" @tap="clearHistory">清空</button>
</view>
<view v-if="history.length" class="scanner-history">
<view v-for="item in history" :key="item.id" class="scanner-history-item">
<text class="scanner-history-index">{{ item.index }}</text>
<view class="scanner-history-main"><text class="scanner-history-value">{{ item.value }}</text><text class="scanner-history-time">{{ item.time }}</text></view>
</view>
</view>
<view v-else class="scanner-empty"><text>暂无广播数据</text></view>
</view>
</view>
</template>
<script>
import PortalHeader from '@/components/PortalHeader.vue'
import { session, requireSession } from '@/stores/session.js'
import { SCAN_BROADCAST_ACTION, SCAN_BROADCAST_EXTRA, startScanBroadcast } from '@/utils/scan-broadcast.js'
export default {
components: { PortalHeader },
data() {
return {
user: session.currentUser,
action: SCAN_BROADCAST_ACTION,
extra: SCAN_BROADCAST_EXTRA,
status: 'waiting',
lastValue: '',
lastTime: '',
scanCount: 0,
history: []
}
},
computed: {
statusTitle() {
if (this.status === 'error') return '接收器启动失败'
if (this.status === 'unsupported') return '当前环境不支持广播'
return this.lastValue ? '已收到扫描广播' : '接收器已启动'
},
statusDetail() {
if (this.status === 'error') return '请检查 PDA 扫描服务设置后重新进入页面'
if (this.status === 'unsupported') return '请在 Android App 真机中测试'
return this.lastValue ? '继续扫描可验证连续接收' : '正在等待硬件扫描结果'
}
},
onShow() {
if (!requireSession()) return
this.user = session.currentUser
this.startScanner()
},
onHide() { this.stopScanner() },
onUnload() { this.stopScanner() },
beforeUnmount() { this.stopScanner() },
methods: {
startScanner() {
this.stopScanner()
this.status = 'waiting'
// #ifndef APP-PLUS
this.status = 'unsupported'
// #endif
this._stopScanner = startScanBroadcast({
onScan: (value) => this.recordScan(value),
onError: () => { this.status = 'error' }
})
},
stopScanner() {
if (typeof this._stopScanner !== 'function') return
this._stopScanner()
this._stopScanner = null
},
recordScan(value) {
const now = new Date()
const time = [now.getHours(), now.getMinutes(), now.getSeconds()].map((part) => String(part).padStart(2, '0')).join(':')
this.scanCount += 1
this.lastValue = value
this.lastTime = time
this.status = 'received'
this.history = [{ id: `${Date.now()}-${this.scanCount}`, index: this.scanCount, value, time }, ...this.history].slice(0, 20)
},
clearHistory() {
this.lastValue = ''
this.lastTime = ''
this.scanCount = 0
this.history = []
this.status = 'waiting'
}
}
}
</script>
<style scoped>
.scanner-test-content { padding: 16px 14px calc(24px + env(safe-area-inset-bottom)); }
.scanner-status { display: flex; align-items: center; min-height: 64px; padding: 12px 14px; border-radius: 8px; color: #20405f; background: #e7f1ff; }
.scanner-status-dot { width: 12px; height: 12px; margin-right: 12px; border-radius: 50%; background: #1769e8; }
.scanner-status-title, .scanner-status-detail, .scanner-result-label, .scanner-result-value, .scanner-result-meta, .scanner-config-label, .scanner-config-value, .scanner-history-title, .scanner-history-value, .scanner-history-time { display: block; }
.scanner-status-title { font-size: 15px; font-weight: 700; }
.scanner-status-detail { margin-top: 3px; font-size: 12px; line-height: 18px; }
.scanner-status-received { color: #145c38; background: #ddf5e8; }
.scanner-status-received .scanner-status-dot { background: #168552; }
.scanner-status-error { color: #912018; background: #fee7e5; }
.scanner-status-error .scanner-status-dot { background: #d92d20; }
.scanner-status-unsupported { color: #664d10; background: #fff1c7; }
.scanner-status-unsupported .scanner-status-dot { background: #b7791f; }
.scanner-result { min-height: 168px; margin-top: 14px; padding: 18px 16px; border: 2px dashed #aebdce; border-radius: 8px; background: #ffffff; }
.scanner-result-received { border-style: solid; border-color: #168552; }
.scanner-result-label { color: #63758a; font-size: 12px; font-weight: 700; }
.scanner-result-value { margin-top: 26px; color: #27384d; font-size: 24px; font-weight: 800; line-height: 32px; overflow-wrap: anywhere; text-align: center; }
.scanner-result-received .scanner-result-value { color: #11643c; }
.scanner-result-meta { margin-top: 15px; color: #63758a; font-size: 12px; text-align: center; }
.scanner-config { margin-top: 14px; border-radius: 8px; background: #ffffff; }
.scanner-config-row { padding: 13px 14px; border-bottom: 1px solid #e4e9ef; }
.scanner-config-row:last-child { border-bottom: 0; }
.scanner-config-label { margin-bottom: 5px; color: #63758a; font-size: 11px; font-weight: 700; }
.scanner-config-value { color: #27384d; font-size: 13px; line-height: 19px; overflow-wrap: anywhere; }
.scanner-history-head { display: flex; align-items: center; justify-content: space-between; margin-top: 22px; }
.scanner-history-title { font-size: 16px; font-weight: 800; }
.scanner-clear { width: 64px; height: 48px; margin: 0; padding: 0; color: #1769e8; font-size: 13px; line-height: 48px; background: transparent; }
.scanner-clear[disabled] { color: #9ba8b7; }
.scanner-history { margin-top: 8px; border-radius: 8px; background: #ffffff; }
.scanner-history-item { display: flex; align-items: center; min-height: 62px; padding: 9px 13px; border-bottom: 1px solid #e4e9ef; }
.scanner-history-item:last-child { border-bottom: 0; }
.scanner-history-index { display: flex; align-items: center; justify-content: center; width: 30px; height: 30px; margin-right: 11px; border-radius: 50%; color: #ffffff; font-size: 12px; font-weight: 700; background: #1769e8; }
.scanner-history-main { min-width: 0; flex: 1; }
.scanner-history-value { color: #27384d; font-size: 15px; font-weight: 700; overflow-wrap: anywhere; }
.scanner-history-time { margin-top: 3px; color: #718095; font-size: 11px; }
.scanner-empty { display: flex; align-items: center; justify-content: center; height: 96px; margin-top: 8px; border-radius: 8px; color: #718095; font-size: 13px; background: #ffffff; }
</style>