修复手机唤醒相机
This commit is contained in:
parent
fcbcdb7f95
commit
116ee0d54a
1
.vercel/project.json
Normal file
1
.vercel/project.json
Normal file
@ -0,0 +1 @@
|
||||
{"projectId":"prj_d4Snf7Qo4GMchiUVyezwVqJieNi6","orgId":"team_hyS3Eg5TitOtyQ1YVgGLzKUz","projectName":"trae_table_ls74","neverMindDeployCard":true}
|
||||
7
.vercelignore
Normal file
7
.vercelignore
Normal file
@ -0,0 +1,7 @@
|
||||
node_modules
|
||||
build
|
||||
dist
|
||||
.git
|
||||
.trae
|
||||
.log
|
||||
.figma
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -18,7 +18,13 @@
|
||||
<div class="search">
|
||||
<div class="search-input">
|
||||
<input id="q" type="text" placeholder="输入条码或名称,支持前缀/后缀/包含" />
|
||||
<button id="scanBtn" class="scan-btn" aria-label="扫码">📷</button>
|
||||
<button id="scanBtn" class="scan-btn" aria-label="扫码" title="扫码">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||
<path d="M4 7a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H7a3 3 0 0 1-3-3V7z" stroke="#444" stroke-width="1.5" fill="#fff"/>
|
||||
<rect x="9" y="9" width="6" height="6" rx="3" stroke="#444" stroke-width="1.5" fill="#fff"/>
|
||||
<path d="M7 4l2 3h6l2-3" stroke="#444" stroke-width="1.5"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<button id="searchBtn">查询</button>
|
||||
</div>
|
||||
@ -36,10 +42,12 @@
|
||||
</div>
|
||||
<div id="scannerModal" class="modal hidden">
|
||||
<div class="modal-content">
|
||||
<div id="scannerMessage" class="scanner-message hidden"></div>
|
||||
<div id="scanner"></div>
|
||||
<button id="closeScanner">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
<input id="captureInput" type="file" accept="image/*" capture="environment" class="hidden" />
|
||||
<script src="/static/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -11,7 +11,10 @@ h1{font-size:20px;margin:8px 0 16px}
|
||||
input{flex:1;padding:10px;border:1px solid #ccc;border-radius:6px;background:#fff;color:#111}
|
||||
.search-input{position:relative;flex:1}
|
||||
.search-input input{width:100%;padding-right:44px}
|
||||
.scan-btn{position:absolute;right:8px;top:50%;transform:translateY(-50%);border:0;background:#f0f0f0;color:#111;border-radius:6px;padding:6px 10px}
|
||||
.scan-btn{position:absolute;right:8px;top:50%;transform:translateY(-50%);border:1px solid #ddd;background:#fafafa;color:#111;border-radius:6px;padding:6px 10px;display:flex;align-items:center;justify-content:center;cursor:pointer}
|
||||
.scan-btn:hover{background:#f0f0f0}
|
||||
.scan-btn:active{background:#e6e6e6}
|
||||
.scan-btn svg{display:block}
|
||||
button{padding:10px 14px;border:0;border-radius:6px;background:#1976d2;color:#fff}
|
||||
.results{display:flex;flex-direction:column;gap:12px}
|
||||
.card{border:1px solid #eee;border-radius:10px;padding:12px;box-shadow:0 1px 2px rgba(0,0,0,0.04)}
|
||||
@ -27,6 +30,8 @@ mark{background:#ffec99}
|
||||
.loading{color:#1976d2}
|
||||
.status{margin-top:8px;color:#555}
|
||||
.import{margin-top:12px;display:flex;gap:8px;align-items:center}
|
||||
.scanner-message{margin-bottom:8px;color:#555}
|
||||
.scanner-message.hidden{display:none}
|
||||
.modal{position:fixed;inset:0;background:rgba(0,0,0,0.4);display:flex;align-items:center;justify-content:center}
|
||||
.modal.hidden{display:none}
|
||||
.modal-content{background:#fff;border-radius:10px;padding:12px;width:92%;max-width:480px}
|
||||
|
||||
1
vercel.json
Normal file
1
vercel.json
Normal file
@ -0,0 +1 @@
|
||||
{"rewrites":[{"source":"/(.*)","destination":"/index.html"}]}
|
||||
Loading…
Reference in New Issue
Block a user