"use client"; import QrScanner from "qr-scanner"; import { useRouter } from "next/navigation"; import { startTransition, useEffect, useEffectEvent, useRef, useState, } from "react"; function extractDeviceUuid(rawValue: string) { const trimmedValue = rawValue.trim(); if (!trimmedValue) { return null; } try { const parsedUrl = new URL(trimmedValue); return ( parsedUrl.searchParams.get("deviceUuid") || parsedUrl.searchParams.get("device") || null ); } catch { return trimmedValue; } } export function ScanClient() { const router = useRouter(); const videoRef = useRef(null); const scannerRef = useRef(null); const [statusText, setStatusText] = useState("正在准备镜头……"); const [errorText, setErrorText] = useState(null); const handleDecode = useEffectEvent(async (result: { data: string }) => { const scannedValue = extractDeviceUuid(result.data); if (!scannerRef.current || !scannedValue) { return; } scannerRef.current.stop(); setErrorText(null); setStatusText("识别到了设备码,正在建立连接……"); try { const response = await fetch("/api/family/bind", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ deviceUuid: scannedValue }), }); const payload = (await response.json().catch(() => null)) as | { error?: string; deviceUuid?: string } | null; const deviceUuid = payload?.deviceUuid; if (!response.ok || !deviceUuid) { throw new Error(payload?.error || "绑定失败,请重新扫描一次。"); } startTransition(() => { router.replace( `/bind?deviceUuid=${encodeURIComponent(deviceUuid)}&source=scan`, ); }); } catch (error) { setErrorText( error instanceof Error ? error.message : "绑定失败,请重新扫描一次。", ); setStatusText("请再把二维码对准镜头中央。"); await scannerRef.current.start().catch(() => null); } }); useEffect(() => { let cancelled = false; async function startScanner() { if (!videoRef.current) { return; } try { const hasCamera = await QrScanner.hasCamera(); if (!hasCamera) { setErrorText("当前设备没有可用摄像头,请改用系统扫码或手动粘贴设备码。"); setStatusText("无法打开镜头"); return; } const scanner = new QrScanner( videoRef.current, (result) => { void handleDecode(result as { data: string }); }, { preferredCamera: "environment", highlightCodeOutline: true, highlightScanRegion: true, returnDetailedScanResult: true, onDecodeError: () => {}, }, ); scannerRef.current = scanner; await scanner.start(); if (!cancelled) { setStatusText("请把老人的二维码放到取景框中央。"); } } catch (error) { setErrorText( error instanceof Error ? error.message : "镜头启动失败,请检查权限后重试。", ); setStatusText("镜头启动失败"); } } void startScanner(); return () => { cancelled = true; scannerRef.current?.destroy(); scannerRef.current = null; }; }, []); return (