|
@@ -7,6 +7,7 @@ import {
|
|
|
Select,
|
|
Select,
|
|
|
Radio,
|
|
Radio,
|
|
|
FormInstance,
|
|
FormInstance,
|
|
|
|
|
+ message,
|
|
|
} from 'antd';
|
|
} from 'antd';
|
|
|
import { useIntl, FormattedMessage } from 'react-intl';
|
|
import { useIntl, FormattedMessage } from 'react-intl';
|
|
|
import { registerFormFields } from '@/validation/patient/registerSchema';
|
|
import { registerFormFields } from '@/validation/patient/registerSchema';
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
pregnancyStatusOptions,
|
|
pregnancyStatusOptions,
|
|
|
} from '@/domain/patient/pregnancyStatus';
|
|
} from '@/domain/patient/pregnancyStatus';
|
|
|
import { getGenderOptions } from '@/domain/patient/genderOptions';
|
|
import { getGenderOptions } from '@/domain/patient/genderOptions';
|
|
|
|
|
+import { processQRCodeData, transformToFormData } from '@/domain/qrcode/qrCodeDataProcessor';
|
|
|
interface BasicInfoFormProps {
|
|
interface BasicInfoFormProps {
|
|
|
style?: React.CSSProperties;
|
|
style?: React.CSSProperties;
|
|
|
form?: FormInstance;
|
|
form?: FormInstance;
|
|
@@ -46,6 +48,12 @@ const BasicInfoForm: React.FC<BasicInfoFormProps> = ({
|
|
|
useState(false);
|
|
useState(false);
|
|
|
const initializedRef = useRef(false);
|
|
const initializedRef = useRef(false);
|
|
|
|
|
|
|
|
|
|
+ // 扫码枪扫码相关状态
|
|
|
|
|
+ const scanInputRef = useRef<HTMLInputElement>(null);
|
|
|
|
|
+ const [isComposing, setIsComposing] = useState(false);
|
|
|
|
|
+ const bufferRef = useRef('');
|
|
|
|
|
+ const timerRef = useRef<number | null>(null);
|
|
|
|
|
+
|
|
|
// 同步初始表单值到 Redux store,并生成登记号
|
|
// 同步初始表单值到 Redux store,并生成登记号
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
if (!initializedRef.current) {
|
|
if (!initializedRef.current) {
|
|
@@ -147,6 +155,116 @@ const BasicInfoForm: React.FC<BasicInfoFormProps> = ({
|
|
|
console.log('出生日期变化,更新年龄:', newAge);
|
|
console.log('出生日期变化,更新年龄:', newAge);
|
|
|
}
|
|
}
|
|
|
}, [patient_dob, form]);
|
|
}, [patient_dob, form]);
|
|
|
|
|
+
|
|
|
|
|
+ // 处理扫码枪扫码后的数据:解析文本并自动填充到注册表单
|
|
|
|
|
+ const processScannedData = React.useCallback(async (scannedText: string) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 添加调试日志,输出实际接收到的扫码文本
|
|
|
|
|
+ console.log('[扫码枪调试] 得到的扫码数据是:', JSON.stringify(scannedText));
|
|
|
|
|
+ console.log('[扫码枪调试] 扫码数据长度:', scannedText.length);
|
|
|
|
|
+
|
|
|
|
|
+ // 使用二维码数据处理器解析扫码文本(复用现有逻辑)
|
|
|
|
|
+ const result = processQRCodeData(scannedText);
|
|
|
|
|
+ if (!result.success) {
|
|
|
|
|
+ message.error(result.error || '扫码枪数据处理失败');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 将解析后的数据转换为表单格式
|
|
|
|
|
+ const formData = transformToFormData(result.data!);
|
|
|
|
|
+
|
|
|
|
|
+ // 自动填充表单
|
|
|
|
|
+ form?.setFieldsValue(formData);
|
|
|
|
|
+
|
|
|
|
|
+ // 同步更新 Redux store
|
|
|
|
|
+ dispatch(setFormData(formData));
|
|
|
|
|
+
|
|
|
|
|
+ // 显示成功提示
|
|
|
|
|
+ message.success('扫码枪数据已自动填充到表单');
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('扫码枪数据处理失败:', error);
|
|
|
|
|
+ message.error('扫码枪数据处理失败');
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [form, dispatch]);
|
|
|
|
|
+
|
|
|
|
|
+ // 扫码枪扫码监听器:页面级 keydown 监听,自动切换焦点到隐藏input
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ const handleKeyDown = (e: KeyboardEvent) => {
|
|
|
|
|
+ const target = e.target as HTMLElement;
|
|
|
|
|
+
|
|
|
|
|
+ const isEditable =
|
|
|
|
|
+ target &&
|
|
|
|
|
+ (target.tagName === 'INPUT' ||
|
|
|
|
|
+ target.tagName === 'TEXTAREA' ||
|
|
|
|
|
+ target.isContentEditable);
|
|
|
|
|
+
|
|
|
|
|
+ // 如果当前没有可编辑元素获得焦点,认为是扫码枪输入
|
|
|
|
|
+ if (!isEditable) {
|
|
|
|
|
+ scanInputRef.current?.focus();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 扫码枪通常以 Enter 结束
|
|
|
|
|
+ if (e.key === 'Enter') {
|
|
|
|
|
+ if (bufferRef.current) {
|
|
|
|
|
+ processScannedData(bufferRef.current);
|
|
|
|
|
+ bufferRef.current = '';
|
|
|
|
|
+ if (scanInputRef.current) {
|
|
|
|
|
+ scanInputRef.current.value = '';
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // 阻止Enter键的默认行为(如表单提交)
|
|
|
|
|
+ e.preventDefault();
|
|
|
|
|
+ e.stopPropagation();
|
|
|
|
|
+ } else if (e.key === 'Tab') {
|
|
|
|
|
+ // 完全控制Tab键行为,防止焦点切换
|
|
|
|
|
+ e.preventDefault();
|
|
|
|
|
+ e.stopPropagation();
|
|
|
|
|
+
|
|
|
|
|
+ // 手动将Tab字符插入到隐藏input中
|
|
|
|
|
+ const input = scanInputRef.current;
|
|
|
|
|
+ if (input) {
|
|
|
|
|
+ const start = input.selectionStart || 0;
|
|
|
|
|
+ const end = input.selectionEnd || 0;
|
|
|
|
|
+ const value = input.value;
|
|
|
|
|
+
|
|
|
|
|
+ // 在光标位置插入Tab字符
|
|
|
|
|
+ input.value = value.substring(0, start) + '\t' + value.substring(end);
|
|
|
|
|
+
|
|
|
|
|
+ // 更新光标位置
|
|
|
|
|
+ input.selectionStart = input.selectionEnd = start + 1;
|
|
|
|
|
+
|
|
|
|
|
+ // 手动触发input事件,确保React能检测到变化
|
|
|
|
|
+ input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // 其他字符不阻止,让它们自然输入到隐藏input中
|
|
|
|
|
+ // input会自动过滤掉无效字符,只保留有效的文本
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ window.addEventListener('keydown', handleKeyDown);
|
|
|
|
|
+ return () => window.removeEventListener('keydown', handleKeyDown);
|
|
|
|
|
+ }, [processScannedData]);
|
|
|
|
|
+
|
|
|
|
|
+ /** input 事件:真正可靠地拿中文 */
|
|
|
|
|
+ const handleScanInput = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
|
|
|
+ if (isComposing) return;
|
|
|
|
|
+
|
|
|
|
|
+ bufferRef.current = e.target.value;
|
|
|
|
|
+
|
|
|
|
|
+ // 防止没有 Enter 的扫码枪(用时间判断)
|
|
|
|
|
+ if (timerRef.current) {
|
|
|
|
|
+ clearTimeout(timerRef.current);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ timerRef.current = window.setTimeout(() => {
|
|
|
|
|
+ if (bufferRef.current) {
|
|
|
|
|
+ processScannedData(bufferRef.current);
|
|
|
|
|
+ bufferRef.current = '';
|
|
|
|
|
+ e.target.value = '';
|
|
|
|
|
+ }
|
|
|
|
|
+ }, 300); // 300ms 内无新输入,认为扫码结束
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
return (
|
|
return (
|
|
|
<Form
|
|
<Form
|
|
|
form={form}
|
|
form={form}
|
|
@@ -611,6 +729,25 @@ const BasicInfoForm: React.FC<BasicInfoFormProps> = ({
|
|
|
})}
|
|
})}
|
|
|
/>
|
|
/>
|
|
|
</Form.Item>
|
|
</Form.Item>
|
|
|
|
|
+
|
|
|
|
|
+ {/* 扫码枪兜底 input(隐藏) */}
|
|
|
|
|
+ <input
|
|
|
|
|
+ ref={scanInputRef}
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ style={{
|
|
|
|
|
+ position: 'fixed',
|
|
|
|
|
+ opacity: 0,
|
|
|
|
|
+ pointerEvents: 'none',
|
|
|
|
|
+ left: -9999,
|
|
|
|
|
+ }}
|
|
|
|
|
+ onCompositionStart={() => setIsComposing(true)}
|
|
|
|
|
+ onCompositionEnd={(e) => {
|
|
|
|
|
+ setIsComposing(false);
|
|
|
|
|
+ bufferRef.current = e.currentTarget.value;
|
|
|
|
|
+ }}
|
|
|
|
|
+ onInput={handleScanInput}
|
|
|
|
|
+ autoComplete="off"
|
|
|
|
|
+ />
|
|
|
</Form>
|
|
</Form>
|
|
|
);
|
|
);
|
|
|
};
|
|
};
|