import React, { useState, useEffect } from 'react'; import { Tooltip, message } from 'antd'; import { ScanOutlined, CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons'; import { useIntl } from 'react-intl'; interface BarcodeScannerIndicatorProps { style?: React.CSSProperties; className?: string; inline?: boolean; // 是否为内联模式(非浮动) } const BarcodeScannerIndicator: React.FC = ({ style, className, inline = false }) => { const intl = useIntl(); const [hasFocusedInput, setHasFocusedInput] = useState(false); // 检测元素是否为可编辑元素(排除隐藏的扫码枪input) const isEditableElement = (element: HTMLElement | null): boolean => { if (!element) return false; // 排除隐藏的扫码枪input(通过样式判断) if (element.tagName === 'INPUT') { const input = element as HTMLInputElement; const style = window.getComputedStyle(input); // 如果是隐藏的input(opacity为0或position为fixed且left为负值),则不算作可编辑元素 if ( style.opacity === '0' || (style.position === 'fixed' && parseInt(style.left) < -1000) ) { return false; } } return ( element.tagName === 'INPUT' || element.tagName === 'TEXTAREA' || element.isContentEditable || element.closest('.ant-select') !== null || element.closest('.ant-picker') !== null || element.closest('.ant-input-number') !== null ); }; // 监听焦点变化 useEffect(() => { const handleFocusIn = (e: FocusEvent) => { const target = e.target as HTMLElement; setHasFocusedInput(isEditableElement(target)); }; const handleFocusOut = () => { // 延迟检查,确保新焦点已设置 setTimeout(() => { const activeElement = document.activeElement as HTMLElement; setHasFocusedInput(isEditableElement(activeElement)); }, 10); }; window.addEventListener('focusin', handleFocusIn); window.addEventListener('focusout', handleFocusOut); // 初始化检查 const activeElement = document.activeElement as HTMLElement; setHasFocusedInput(isEditableElement(activeElement)); return () => { window.removeEventListener('focusin', handleFocusIn); window.removeEventListener('focusout', handleFocusOut); }; }, []); // 点击处理:使所有输入框失焦 const handleClick = () => { const activeElement = document.activeElement as HTMLElement; if (activeElement && activeElement.blur) { activeElement.blur(); } // 确保焦点移到 body document.body.focus(); // 显示提示 message.success( intl.formatMessage({ id: 'scanner.indicator.activated', defaultMessage: '扫码枪已就绪,请扫描二维码' }) ); }; const isReady = !hasFocusedInput; const tooltipText = intl.formatMessage({ id: isReady ? 'scanner.indicator.tooltip.ready' : 'scanner.indicator.tooltip.notReady', defaultMessage: isReady ? '扫码枪已就绪,可以扫描二维码' : '扫码枪不可用,点击此处激活' }); return (
{ if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleClick(); } }} style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '4px 15px', backgroundColor: isReady ? '#52c41a' : '#d9d9d9', border: `1px solid ${isReady ? '#52c41a' : '#d9d9d9'}`, borderRadius: '6px', color: 'white', cursor: 'pointer', transition: 'all 0.3s ease', height: '100%', minHeight: '32px', outline: 'none', userSelect: 'none', ...style }} className={className} onMouseEnter={(e) => { e.currentTarget.style.opacity = '0.85'; }} onMouseLeave={(e) => { e.currentTarget.style.opacity = '1'; }} >
{isReady ? ( ) : ( )}
); }; export default BarcodeScannerIndicator;