修改扫码时间间隔分辨率以及添加语音播报,对边框进行缩减由210px修改为180px

This commit is contained in:
dxc
2026-02-10 10:32:59 +08:00
parent 1edd9a95c6
commit bccbeaadce
2 changed files with 67 additions and 21 deletions

View File

@ -12,7 +12,7 @@
<div class="focus-tip success" v-if="isPaused"> <div class="focus-tip success" v-if="isPaused">
<div class="scan-text-success"> <div class="scan-text-success">
<el-icon><CircleCheckFilled /></el-icon> <el-icon><CircleCheckFilled /></el-icon>
扫描成功3秒后继续... 扫描成功2秒后继续...
</div> </div>
</div> </div>
@ -50,6 +50,43 @@ const zoomMin = ref(1)
const zoomMax = ref(5) const zoomMax = ref(5)
const currentZoom = ref(1) const currentZoom = ref(1)
// 音频上下文
let audioCtx: AudioContext | null = null;
// 提示音播放函数
const playBeep = () => {
try {
const AudioContext = window.AudioContext || (window as any).webkitAudioContext;
if (!AudioContext) return;
if (!audioCtx) {
audioCtx = new AudioContext();
}
if (audioCtx.state === 'suspended') {
audioCtx.resume();
}
const oscillator = audioCtx.createOscillator();
const gainNode = audioCtx.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
// 三角波,清脆响亮
oscillator.type = 'triangle';
oscillator.frequency.value = 1500;
gainNode.gain.setValueAtTime(1.0, audioCtx.currentTime);
oscillator.start();
oscillator.stop(audioCtx.currentTime + 0.1);
} catch (e) {
console.error("播放提示音失败:", e);
}
};
const startScanning = async () => { const startScanning = async () => {
try { try {
html5QrCode = new Html5Qrcode(scannerElementId, { html5QrCode = new Html5Qrcode(scannerElementId, {
@ -63,16 +100,16 @@ const startScanning = async () => {
const config = { const config = {
fps: 20, fps: 20,
// ★★★ 核心修改点 2移除了 qrbox 属性 ★★★
// 移除后,库默认会对每一帧的“全画面”进行解析,不再局限于中间区域
// qrbox: { width: 300, height: 100 },
disableFlip: false, disableFlip: false,
videoConstraints: { videoConstraints: {
facingMode: "environment", facingMode: "environment",
// 保持高分辨率以支持微小条码 // ★★★ 核心修改:设置为 2K (QHD) 分辨率 ★★★
width: { min: 1280, ideal: 3840, max: 3840 }, // min: 1280x720 (保证低端机能启动)
height: { min: 720, ideal: 2160, max: 2160 }, // ideal: 2560x1440 (2K QHD清晰度与性能的平衡点)
width: { min: 1280, ideal: 2560, max: 3840 },
height: { min: 720, ideal: 1440, max: 2160 },
// 16:9 的比例
aspectRatio: { ideal: 1.7777777778 },
focusMode: "continuous", focusMode: "continuous",
advanced: [{ focusMode: "macro" }, { zoom: 2.0 }] advanced: [{ focusMode: "macro" }, { zoom: 2.0 }]
} }
@ -84,14 +121,18 @@ const startScanning = async () => {
(decodedText) => { (decodedText) => {
if (isPaused.value) return if (isPaused.value) return
console.log(`Scan: ${decodedText}`) console.log(`Scan: ${decodedText}`)
isPaused.value = true isPaused.value = true
playBeep();
emit('decode', decodedText) emit('decode', decodedText)
if (navigator.vibrate) navigator.vibrate(200); if (navigator.vibrate) navigator.vibrate(200);
setTimeout(() => { setTimeout(() => {
isPaused.value = false isPaused.value = false
}, 3000) }, 2000)
}, },
(errorMessage) => { (errorMessage) => {
// ignore // ignore
@ -103,12 +144,14 @@ const startScanning = async () => {
} catch (err: any) { } catch (err: any) {
let msg = '无法启动摄像头' let msg = '无法启动摄像头'
console.error("Scanner Error:", err) console.error("Scanner Error:", err)
if (err.name === 'OverconstrainedError') {
msg = '摄像头不支持 2K 分辨率,请尝试降低配置'
}
errorMsg.value = msg errorMsg.value = msg
emit('error', msg) emit('error', msg)
} }
} }
// 检测硬件变焦能力
const checkZoomCapability = () => { const checkZoomCapability = () => {
if (!html5QrCode) return if (!html5QrCode) return
@ -130,7 +173,6 @@ const checkZoomCapability = () => {
} }
} }
// 处理滑块拖动
const handleZoom = () => { const handleZoom = () => {
if (!html5QrCode) return if (!html5QrCode) return
@ -154,9 +196,19 @@ const stopScanning = async () => {
console.error("Stop failed", e) console.error("Stop failed", e)
} }
} }
if (audioCtx) {
audioCtx.close();
audioCtx = null;
}
} }
onMounted(() => { onMounted(() => {
try {
const AudioContext = window.AudioContext || (window as any).webkitAudioContext;
if (AudioContext) audioCtx = new AudioContext();
} catch(e) {}
setTimeout(() => { setTimeout(() => {
startScanning() startScanning()
}, 500) }, 500)
@ -178,7 +230,6 @@ onUnmounted(() => {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
overflow: hidden; overflow: hidden;
/* 如果是全屏模式这里不需要圆角或者保持圆角视你的UI设计而定 */
border-radius: 0; border-radius: 0;
} }
@ -214,14 +265,12 @@ onUnmounted(() => {
z-index: 20; z-index: 20;
} }
/* --- ★ 修改点 3视觉层 CSS 更新 --- */
.focus-tip { .focus-tip {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
/* 移除了 border 和 box-shadow不再显示红框和黑色遮罩 */
pointer-events: none; pointer-events: none;
z-index: 10; z-index: 10;
display: flex; display: flex;
@ -230,23 +279,21 @@ onUnmounted(() => {
} }
.focus-tip.success { .focus-tip.success {
background: rgba(103, 194, 58, 0.2); /* 成功时全屏微微泛绿 */ background: rgba(103, 194, 58, 0.2);
} }
/* 扫描线改为全屏宽度 */
.scan-line { .scan-line {
width: 100%; width: 100%;
height: 2px; height: 2px;
background: rgba(255, 0, 0, 0.5); background: rgba(255, 0, 0, 0.5);
box-shadow: 0 0 4px rgba(255, 0, 0, 0.8); box-shadow: 0 0 4px rgba(255, 0, 0, 0.8);
position: absolute; position: absolute;
/* 动画范围从 10% 到 90% */
animation: scan-move 2.5s infinite linear; animation: scan-move 2.5s infinite linear;
} }
.scan-text { .scan-text {
position: absolute; position: absolute;
bottom: 150px; /* 调整文字位置 */ bottom: 150px;
color: rgba(255, 255, 255, 0.8); color: rgba(255, 255, 255, 0.8);
font-size: 14px; font-size: 14px;
text-shadow: 0 1px 3px rgba(0,0,0,0.8); text-shadow: 0 1px 3px rgba(0,0,0,0.8);
@ -275,7 +322,6 @@ onUnmounted(() => {
100% { top: 100%; opacity: 0; } 100% { top: 100%; opacity: 0; }
} }
/* 变焦控制器 */
.zoom-control { .zoom-control {
position: absolute; position: absolute;
bottom: 80px; bottom: 80px;

View File

@ -22,7 +22,7 @@ import AppMain from './components/AppMain.vue'
} }
.sidebar-container { .sidebar-container {
width: 210px; /* 固定侧边栏宽度 */ width: 180px; /* 固定侧边栏宽度 */
height: 100%; height: 100%;
background-color: #304156; /* 侧边栏背景色 */ background-color: #304156; /* 侧边栏背景色 */
flex-shrink: 0; /* 防止被挤压 */ flex-shrink: 0; /* 防止被挤压 */
@ -37,7 +37,7 @@ import AppMain from './components/AppMain.vue'
flex-direction: column; flex-direction: column;
overflow-y: auto; /* 关键:页面内容过多时,只在右侧区域滚动 */ overflow-y: auto; /* 关键:页面内容过多时,只在右侧区域滚动 */
background-color: #f0f2f5; /* 右侧灰色背景,让白色卡片更明显 */ background-color: #f0f2f5; /* 右侧灰色背景,让白色卡片更明显 */
padding: 20px; /* 给内部页面留出边距 */ padding: 10px; /* 给内部页面留出边距 */
box-sizing: border-box; box-sizing: border-box;
} }
</style> </style>