diff --git a/.vercel/project.json b/.vercel/project.json new file mode 100644 index 0000000..bdfcecc --- /dev/null +++ b/.vercel/project.json @@ -0,0 +1 @@ +{"projectId":"prj_d4Snf7Qo4GMchiUVyezwVqJieNi6","orgId":"team_hyS3Eg5TitOtyQ1YVgGLzKUz","projectName":"trae_table_ls74","neverMindDeployCard":true} \ No newline at end of file diff --git a/.vercelignore b/.vercelignore new file mode 100644 index 0000000..a48e5d3 --- /dev/null +++ b/.vercelignore @@ -0,0 +1,7 @@ +node_modules +build +dist +.git +.trae +.log +.figma \ No newline at end of file diff --git a/static/app.js b/static/app.js index e46e8e3..b8aef4c 100644 --- a/static/app.js +++ b/static/app.js @@ -14,6 +14,8 @@ const closeScanner = qs('#closeScanner'); let scannerInst = null; let videoEl = null; let mediaStream = null; +const scannerMessage = qs('#scannerMessage'); +const captureInput = qs('#captureInput'); // 无分页模式 let currentTab = 'search'; @@ -81,7 +83,15 @@ input.addEventListener('keydown', e => { if (e.key === 'Enter') search(); }); async function openScanner(){ scannerModal.classList.remove('hidden'); + scannerMessage.textContent = '正在请求摄像头权限...'; + scannerMessage.classList.remove('hidden'); + const isSecure = location.protocol === 'https:' || location.hostname === 'localhost'; try{ + if(!isSecure){ + scannerMessage.textContent = '非安全上下文,已切换为拍照识别方式'; + captureInput.click(); + return; + } if('BarcodeDetector' in window){ const formats = ['ean_13','ean_8','code_128','code_39','upc_a','upc_e']; const detector = new window.BarcodeDetector({ formats }); @@ -94,6 +104,7 @@ async function openScanner(){ host.innerHTML = ''; host.appendChild(videoEl); await videoEl.play(); + scannerMessage.classList.add('hidden'); const loop = async()=>{ try{ const codes = await detector.detect(videoEl); @@ -110,10 +121,12 @@ async function openScanner(){ }; requestAnimationFrame(loop); } else { - scannerModal.classList.add('hidden'); + scannerMessage.textContent = '浏览器不支持实时扫码,已切换为拍照识别方式'; + captureInput.click(); } }catch(e){ - scannerModal.classList.add('hidden'); + scannerMessage.textContent = '无法启用摄像头,已切换为拍照识别方式'; + captureInput.click(); } } @@ -121,9 +134,39 @@ function closeScannerFn(){ try{ if(scannerInst){ scannerInst.stop().then(()=>scannerInst.clear()); } }catch(_){ } try{ if(mediaStream){ mediaStream.getTracks().forEach(t=>t.stop()); mediaStream=null; } }catch(_){ } try{ const host = document.getElementById('scanner'); if(host){ host.innerHTML=''; } }catch(_){ } + try{ if(scannerMessage){ scannerMessage.textContent=''; scannerMessage.classList.add('hidden'); } }catch(_){ } scannerModal.classList.add('hidden'); } +captureInput.addEventListener('change', async ()=>{ + const file = captureInput.files?.[0]; + if(!file){ return; } + try{ + if('BarcodeDetector' in window){ + const img = await createImageBitmap(file); + const detector = new window.BarcodeDetector({ formats: ['ean_13','ean_8','code_128','code_39','upc_a','upc_e'] }); + const codes = await detector.detect(img); + if(codes && codes.length){ + const text = codes[0].rawValue; + input.value = text; + currentQuery = text; + fetchResults(); + } else { + scannerMessage.textContent = '未识别到条码,请重试或手动输入'; + scannerMessage.classList.remove('hidden'); + } + } else { + scannerMessage.textContent = '当前浏览器不支持条码识别,请手动输入'; + scannerMessage.classList.remove('hidden'); + } + }catch(_){ + scannerMessage.textContent = '识别失败,请重试或手动输入'; + scannerMessage.classList.remove('hidden'); + } finally { + captureInput.value = ''; + } +}); + scanBtn.addEventListener('click', openScanner); closeScanner.addEventListener('click', closeScannerFn); diff --git a/static/index.html b/static/index.html index 208e78a..3897922 100644 --- a/static/index.html +++ b/static/index.html @@ -18,7 +18,13 @@