fix(monitor): harden mobile entry dialog
This commit is contained in:
@@ -106,8 +106,19 @@ function MonitorVehicleTable({ rows, total, page, totalPages, limit, loading, on
|
||||
function MobileEntry({ onClose }: { onClose: () => void }) {
|
||||
const [qr, setQr] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const [copyState, setCopyState] = useState<'idle' | 'copied' | 'error'>('idle');
|
||||
const [attempt, setAttempt] = useState(0);
|
||||
const url = `${window.location.origin}/monitor`;
|
||||
useEffect(() => {
|
||||
const closeOnEscape = (event: KeyboardEvent) => { if (event.key === 'Escape') onClose(); };
|
||||
window.addEventListener('keydown', closeOnEscape);
|
||||
return () => window.removeEventListener('keydown', closeOnEscape);
|
||||
}, [onClose]);
|
||||
useEffect(() => {
|
||||
if (copyState === 'idle') return;
|
||||
const timer = window.setTimeout(() => setCopyState('idle'), 1_800);
|
||||
return () => window.clearTimeout(timer);
|
||||
}, [copyState]);
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
setQr('');
|
||||
@@ -118,7 +129,16 @@ function MobileEntry({ onClose }: { onClose: () => void }) {
|
||||
.catch(() => { if (!cancelled) setError('二维码生成失败,请检查浏览器环境后重试'); });
|
||||
return () => { cancelled = true; };
|
||||
}, [attempt, url]);
|
||||
return <div className="v2-monitor-qr-backdrop" role="dialog" aria-modal="true" aria-label="手机端入口"><section><button type="button" onClick={onClose} aria-label="关闭手机端入口"><IconClose /></button><IconQrCode /><h3>手机端全局监控</h3><p>扫码后使用现有账号鉴权,不在二维码中保存 Token</p>{qr ? <img src={qr} alt="全局监控手机端二维码" /> : error ? <div className="v2-monitor-qr-error" role="alert"><span>{error}</span><button type="button" onClick={() => setAttempt((value) => value + 1)}>重新生成</button></div> : <span className="v2-spinner" role="status" aria-label="正在生成二维码" />}<code>{url}</code><button type="button" onClick={() => void navigator.clipboard?.writeText(url)}>复制访问地址</button></section></div>;
|
||||
const copyURL = async () => {
|
||||
try {
|
||||
if (!navigator.clipboard?.writeText) throw new Error('clipboard unavailable');
|
||||
await navigator.clipboard.writeText(url);
|
||||
setCopyState('copied');
|
||||
} catch {
|
||||
setCopyState('error');
|
||||
}
|
||||
};
|
||||
return <div className="v2-monitor-qr-backdrop" role="dialog" aria-modal="true" aria-label="手机端入口" onMouseDown={(event) => { if (event.target === event.currentTarget) onClose(); }}><section><button type="button" autoFocus onClick={onClose} aria-label="关闭手机端入口"><IconClose /></button><IconQrCode /><h3>手机端全局监控</h3><p>扫码后使用现有账号鉴权,不在二维码中保存 Token</p>{qr ? <img src={qr} alt="全局监控手机端二维码" /> : error ? <div className="v2-monitor-qr-error" role="alert"><span>{error}</span><button type="button" onClick={() => setAttempt((value) => value + 1)}>重新生成</button></div> : <span className="v2-spinner" role="status" aria-label="正在生成二维码" />}<code>{url}</code><button type="button" aria-live="polite" onClick={() => void copyURL()}>{copyState === 'copied' ? '已复制访问地址' : copyState === 'error' ? '复制失败,重试' : '复制访问地址'}</button></section></div>;
|
||||
}
|
||||
|
||||
const VehicleRow = memo(function VehicleRow({ vehicle, selected, onSelect }: { vehicle: VehicleRealtimeRow; selected: boolean; onSelect: (vin: string) => void }) {
|
||||
|
||||
Reference in New Issue
Block a user