register.form.tsx 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  1. import React, { useEffect, useRef, useMemo, useState } from 'react';
  2. import {
  3. Form,
  4. Input,
  5. DatePicker,
  6. InputNumber,
  7. Select,
  8. Radio,
  9. FormInstance,
  10. message,
  11. } from 'antd';
  12. import { useIntl, FormattedMessage } from 'react-intl';
  13. import { registerFormFields } from '@/validation/patient/registerSchema';
  14. import NumberWithUnit from '@/components/NumberWithUnit';
  15. import dayjs, { Dayjs } from 'dayjs';
  16. import { useSelector, useDispatch } from 'react-redux';
  17. import { RootState } from '@/states/store';
  18. import { setFormData } from '@/states/patient/register/formSlice';
  19. import {
  20. PregnancyStatus,
  21. pregnancyStatusOptions,
  22. } from '@/domain/patient/pregnancyStatus';
  23. import { getGenderOptions } from '@/domain/patient/genderOptions';
  24. import { processQRCodeData, transformToFormData } from '@/domain/qrcode/qrCodeDataProcessor';
  25. interface BasicInfoFormProps {
  26. style?: React.CSSProperties;
  27. form?: FormInstance;
  28. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  29. onValuesChange?: (changedValues: any, allValues: any) => void;
  30. }
  31. const BasicInfoForm: React.FC<BasicInfoFormProps> = ({
  32. style,
  33. form,
  34. onValuesChange,
  35. }) => {
  36. // 使用 ref 记录更新源,防止循环触发
  37. const updateSourceRef = useRef<'age' | 'dob' | null>(null);
  38. const patient_age = Form.useWatch('patient_age', form);
  39. const patient_dob = Form.useWatch('patient_dob', form);
  40. const productName = useSelector(
  41. (state: RootState) => state.product.productName
  42. );
  43. const formData = useSelector((state: RootState) => state.form.formData);
  44. const dispatch = useDispatch();
  45. const [pregnancyStatusFieldVisible, setPregnancyStatusFieldVisible] =
  46. useState(false);
  47. const initializedRef = useRef(false);
  48. // 扫码枪扫码相关状态
  49. const scanInputRef = useRef<HTMLInputElement>(null);
  50. const [isComposing, setIsComposing] = useState(false);
  51. const bufferRef = useRef('');
  52. const timerRef = useRef<number | null>(null);
  53. // 同步初始表单值到 Redux store,并生成登记号
  54. useEffect(() => {
  55. if (!initializedRef.current) {
  56. // 生成登记号(如果不存在)
  57. let updatedFormData = { ...formData };
  58. if (!updatedFormData.accession_number) {
  59. const now = dayjs();
  60. updatedFormData.accession_number = now.format('YYYYMMDDHHmmss');
  61. }
  62. const allValues = form?.getFieldsValue();
  63. if (allValues && Object.keys(allValues).length > 0) {
  64. const merged = { ...allValues ,...updatedFormData};
  65. dispatch(setFormData(merged));
  66. form?.setFieldsValue(merged)
  67. }
  68. initializedRef.current = true;
  69. }
  70. }, [form, dispatch, formData]);
  71. const intl = useIntl();
  72. // 动态获取性别选项
  73. const genderOptions = useMemo(() => {
  74. return getGenderOptions(productName);
  75. }, [productName]);
  76. // 根据年龄计算出生日期
  77. const calculateDobFromAge = (age: {
  78. number: number;
  79. unit: 'D' | 'M' | 'Y';
  80. }): Dayjs => {
  81. const now = dayjs();
  82. switch (age.unit) {
  83. case 'D':
  84. return now.subtract(age.number, 'day');
  85. case 'M':
  86. return now.subtract(age.number, 'month');
  87. case 'Y':
  88. return now.subtract(age.number, 'year');
  89. default:
  90. return now;
  91. }
  92. };
  93. // 根据出生日期计算年龄
  94. const calculateAgeFromDob = (
  95. dob: Dayjs
  96. ): { number: number; unit: 'D' | 'M' | 'Y' } => {
  97. const now = dayjs();
  98. const years = now.diff(dob, 'year');
  99. if (years >= 1) {
  100. return { number: years, unit: 'Y' };
  101. }
  102. const months = now.diff(dob, 'month');
  103. if (months >= 1) {
  104. return { number: months, unit: 'M' };
  105. }
  106. const days = now.diff(dob, 'day');
  107. return { number: Math.max(0, days), unit: 'D' };
  108. };
  109. // 监听年龄变化
  110. useEffect(() => {
  111. // 如果是出生日期联动更新的年龄,跳过
  112. if (updateSourceRef.current === 'dob') {
  113. updateSourceRef.current = null;
  114. return;
  115. }
  116. // 如果年龄有效,计算出生日期
  117. if (patient_age && patient_age.number >= 0 && form) {
  118. updateSourceRef.current = 'age';
  119. const newDob = calculateDobFromAge(patient_age);
  120. form.setFieldsValue({ patient_dob: newDob });
  121. //人工同步更新 Redux store
  122. dispatch(setFormData(form.getFieldsValue()))
  123. console.log('年龄变化,更新出生日期:', newDob.format('YYYY-MM-DD'));
  124. }
  125. }, [patient_age, form]);
  126. // 监听出生日期变化
  127. useEffect(() => {
  128. // 如果是年龄联动更新的出生日期,跳过
  129. if (updateSourceRef.current === 'age') {
  130. updateSourceRef.current = null;
  131. return;
  132. }
  133. // 如果出生日期有效,计算年龄
  134. if (patient_dob && dayjs.isDayjs(patient_dob) && form) {
  135. updateSourceRef.current = 'dob';
  136. const newAge = calculateAgeFromDob(patient_dob);
  137. form.setFieldsValue({ patient_age: newAge });
  138. console.log('出生日期变化,更新年龄:', newAge);
  139. }
  140. }, [patient_dob, form]);
  141. // 处理扫码枪扫码后的数据:解析文本并自动填充到注册表单
  142. const processScannedData = React.useCallback(async (scannedText: string) => {
  143. try {
  144. // 添加调试日志,输出实际接收到的扫码文本
  145. console.log('[扫码枪调试] 得到的扫码数据是:', JSON.stringify(scannedText));
  146. console.log('[扫码枪调试] 扫码数据长度:', scannedText.length);
  147. // 使用二维码数据处理器解析扫码文本(复用现有逻辑)
  148. const result = processQRCodeData(scannedText);
  149. if (!result.success) {
  150. message.error(result.error || '扫码枪数据处理失败');
  151. return;
  152. }
  153. // 将解析后的数据转换为表单格式
  154. const formData = transformToFormData(result.data!);
  155. // 自动填充表单
  156. form?.setFieldsValue(formData);
  157. // 同步更新 Redux store
  158. dispatch(setFormData(formData));
  159. // 显示成功提示
  160. message.success('扫码枪数据已自动填充到表单');
  161. } catch (error) {
  162. console.error('扫码枪数据处理失败:', error);
  163. message.error('扫码枪数据处理失败');
  164. }
  165. }, [form, dispatch]);
  166. // 扫码枪扫码监听器:页面级 keydown 监听,自动切换焦点到隐藏input
  167. useEffect(() => {
  168. const handleKeyDown = (e: KeyboardEvent) => {
  169. const target = e.target as HTMLElement;
  170. const isEditable =
  171. target &&
  172. (target.tagName === 'INPUT' ||
  173. target.tagName === 'TEXTAREA' ||
  174. target.isContentEditable);
  175. // 如果当前没有可编辑元素获得焦点,认为是扫码枪输入
  176. if (!isEditable) {
  177. scanInputRef.current?.focus();
  178. }
  179. // 扫码枪通常以 Enter 结束
  180. if (e.key === 'Enter') {
  181. if (bufferRef.current) {
  182. processScannedData(bufferRef.current);
  183. bufferRef.current = '';
  184. if (scanInputRef.current) {
  185. scanInputRef.current.value = '';
  186. }
  187. }
  188. // 阻止Enter键的默认行为(如表单提交)
  189. e.preventDefault();
  190. e.stopPropagation();
  191. } else if (e.key === 'Tab') {
  192. // 完全控制Tab键行为,防止焦点切换
  193. e.preventDefault();
  194. e.stopPropagation();
  195. // 手动将Tab字符插入到隐藏input中
  196. const input = scanInputRef.current;
  197. if (input) {
  198. const start = input.selectionStart || 0;
  199. const end = input.selectionEnd || 0;
  200. const value = input.value;
  201. // 在光标位置插入Tab字符
  202. input.value = value.substring(0, start) + '\t' + value.substring(end);
  203. // 更新光标位置
  204. input.selectionStart = input.selectionEnd = start + 1;
  205. // 手动触发input事件,确保React能检测到变化
  206. input.dispatchEvent(new Event('input', { bubbles: true }));
  207. }
  208. }
  209. // 其他字符不阻止,让它们自然输入到隐藏input中
  210. // input会自动过滤掉无效字符,只保留有效的文本
  211. };
  212. window.addEventListener('keydown', handleKeyDown);
  213. return () => window.removeEventListener('keydown', handleKeyDown);
  214. }, [processScannedData]);
  215. /** input 事件:真正可靠地拿中文 */
  216. const handleScanInput = (e: React.ChangeEvent<HTMLInputElement>) => {
  217. if (isComposing) return;
  218. bufferRef.current = e.target.value;
  219. // 防止没有 Enter 的扫码枪(用时间判断)
  220. if (timerRef.current) {
  221. clearTimeout(timerRef.current);
  222. }
  223. timerRef.current = window.setTimeout(() => {
  224. if (bufferRef.current) {
  225. processScannedData(bufferRef.current);
  226. bufferRef.current = '';
  227. e.target.value = '';
  228. }
  229. }, 300); // 300ms 内无新输入,认为扫码结束
  230. };
  231. return (
  232. <Form
  233. form={form}
  234. layout="vertical"
  235. style={style}
  236. className="px-2"
  237. initialValues={
  238. {
  239. // patient_dob: dayjs(),
  240. }
  241. }
  242. onValuesChange={onValuesChange}
  243. >
  244. {/** 宠物专用 */}
  245. {productName === 'VETDROS' && (
  246. <Form.Item
  247. label={
  248. <FormattedMessage
  249. id="register.owner_name"
  250. defaultMessage="register.owner_name"
  251. />
  252. }
  253. name="owner_name"
  254. required={registerFormFields.owner_name.required}
  255. validateTrigger={registerFormFields.owner_name.trigger}
  256. rules={registerFormFields.owner_name.validation}
  257. >
  258. <Input
  259. placeholder={intl.formatMessage({
  260. id: 'register.owner_name.placeholder',
  261. defaultMessage: 'register.owner_name.placeholder',
  262. })}
  263. />
  264. </Form.Item>
  265. )}
  266. <Form.Item
  267. label={
  268. <FormattedMessage
  269. id={
  270. productName === 'VETDROS'
  271. ? 'animal.register.patientName'
  272. : 'register.patientName'
  273. }
  274. defaultMessage={
  275. productName === 'VETDROS'
  276. ? 'animal.register.patientName'
  277. : 'register.patientName'
  278. }
  279. />
  280. }
  281. name="patient_name"
  282. required={registerFormFields.patient_name.required}
  283. validateTrigger={registerFormFields.patient_name.trigger}
  284. rules={registerFormFields.patient_name.validation}
  285. >
  286. <Input
  287. placeholder={intl.formatMessage({
  288. id:
  289. productName === 'VETDROS'
  290. ? 'animal.register.patientName.placeholder'
  291. : 'register.patientName.placeholder',
  292. defaultMessage:
  293. productName === 'VETDROS'
  294. ? 'animal.register.patientName.placeholder'
  295. : 'register.patientName.placeholder',
  296. })}
  297. />
  298. </Form.Item>
  299. <Form.Item
  300. label={
  301. <FormattedMessage
  302. id="register.accessionNumber"
  303. defaultMessage="register.accessionNumber"
  304. />
  305. }
  306. name="accession_number"
  307. required={registerFormFields.accession_number.required}
  308. validateTrigger={registerFormFields.accession_number.trigger}
  309. rules={registerFormFields.accession_number.validation}
  310. >
  311. <Input
  312. placeholder={intl.formatMessage({
  313. id: 'register.accessionNumber.placeholder',
  314. defaultMessage: 'register.accessionNumber.placeholder',
  315. })}
  316. />
  317. </Form.Item>
  318. <Form.Item
  319. label={
  320. <FormattedMessage
  321. id={
  322. productName === 'VETDROS'
  323. ? 'animal.register.patientId'
  324. : 'register.patientId'
  325. }
  326. defaultMessage={
  327. productName === 'VETDROS'
  328. ? 'animal.register.patientId'
  329. : 'register.patientId'
  330. }
  331. />
  332. }
  333. name="patient_id"
  334. required={registerFormFields.patient_id.required}
  335. validateTrigger={registerFormFields.patient_id.trigger}
  336. rules={registerFormFields.patient_id.validation}
  337. >
  338. <Input
  339. placeholder={intl.formatMessage({
  340. id:
  341. productName === 'VETDROS'
  342. ? 'animal.register.patientId.placeholder'
  343. : 'register.patientId.placeholder',
  344. defaultMessage:
  345. productName === 'VETDROS'
  346. ? 'animal.register.patientId.placeholder'
  347. : 'register.patientId.placeholder',
  348. })}
  349. />
  350. </Form.Item>
  351. <Form.Item
  352. label={
  353. <FormattedMessage
  354. id={
  355. productName === 'VETDROS'
  356. ? 'animal.register.patientSize'
  357. : 'register.patientSize'
  358. }
  359. defaultMessage={
  360. productName === 'VETDROS'
  361. ? 'animal.register.patientSize'
  362. : 'register.patientSize'
  363. }
  364. />
  365. }
  366. name="patient_size"
  367. required={registerFormFields.patient_size.required}
  368. validateTrigger={registerFormFields.patient_size.trigger}
  369. rules={registerFormFields.patient_size.validation}
  370. initialValue="Medium"
  371. >
  372. <Select
  373. placeholder={intl.formatMessage({
  374. id:
  375. productName === 'VETDROS'
  376. ? 'animal.register.patientSize.placeholder'
  377. : 'register.patientSize.placeholder',
  378. defaultMessage:
  379. productName === 'VETDROS'
  380. ? 'animal.register.patientSize.placeholder'
  381. : 'register.patientSize.placeholder',
  382. })}
  383. defaultValue="Medium"
  384. >
  385. <Select.Option value="Large">
  386. {intl.formatMessage({ id: 'Large', defaultMessage: 'Large' })}
  387. </Select.Option>
  388. <Select.Option value="Medium">
  389. {intl.formatMessage({ id: 'Medium', defaultMessage: 'Medium' })}
  390. </Select.Option>
  391. <Select.Option value="Small">
  392. {intl.formatMessage({ id: 'Small', defaultMessage: 'Small' })}
  393. </Select.Option>
  394. </Select>
  395. </Form.Item>
  396. <Form.Item
  397. label={
  398. <FormattedMessage id="register.age" defaultMessage="register.age" />
  399. }
  400. name="patient_age"
  401. required={registerFormFields.patient_age.required}
  402. validateTrigger={registerFormFields.patient_age.trigger}
  403. rules={registerFormFields.patient_age.validation}
  404. initialValue={{ number: 0, unit: 'Y' }}
  405. >
  406. <NumberWithUnit
  407. align="baseline"
  408. defaultUnit="Y"
  409. defaultNumber={0}
  410. unitClassName="w-full"
  411. numberClassName="w-full"
  412. className="w-full"
  413. options={[
  414. { label: '天', value: 'D' },
  415. { label: '月', value: 'M' },
  416. { label: '年', value: 'Y' },
  417. ]}
  418. />
  419. </Form.Item>
  420. <Form.Item
  421. label={
  422. <FormattedMessage
  423. id="register.dateOfBirth"
  424. defaultMessage="register.dateOfBirth"
  425. />
  426. }
  427. name="patient_dob"
  428. required={registerFormFields.patient_dob.required}
  429. validateTrigger={registerFormFields.patient_dob.trigger}
  430. rules={registerFormFields.patient_dob.validation}
  431. initialValue={dayjs()}
  432. >
  433. <DatePicker
  434. format="YYYY-MM-DD"
  435. style={{ width: '100%' }}
  436. defaultValue={dayjs()}
  437. disabledDate={(current) => current && current > dayjs().endOf('day')}
  438. />
  439. </Form.Item>
  440. <Form.Item
  441. label={
  442. <FormattedMessage
  443. id="register.gender"
  444. defaultMessage="register.gender"
  445. />
  446. }
  447. name="patient_sex"
  448. required={registerFormFields.patient_sex.required}
  449. validateTrigger={registerFormFields.patient_sex.trigger}
  450. rules={registerFormFields.patient_sex.validation}
  451. >
  452. <Select
  453. options={genderOptions}
  454. onChange={(e) => {
  455. const _PregnancyStatusFieldVisible = e === 'M';
  456. setPregnancyStatusFieldVisible(_PregnancyStatusFieldVisible);
  457. if (_PregnancyStatusFieldVisible) {
  458. // 更新表单
  459. form?.setFieldValue('pregnancy_status', PregnancyStatus.UNKNOWN);
  460. // 手动同步更新 Redux store
  461. const currentValues = form?.getFieldsValue();
  462. dispatch(
  463. setFormData({
  464. ...currentValues,
  465. pregnancy_status: PregnancyStatus.UNKNOWN,
  466. })
  467. );
  468. }
  469. }}
  470. />
  471. </Form.Item>
  472. {/** 宠物专用 */}
  473. {productName === 'VETDROS' && (
  474. <Form.Item
  475. label={
  476. <FormattedMessage
  477. id="register.sexNeutered"
  478. defaultMessage="register.sexNeutered"
  479. />
  480. }
  481. name="sex_neutered"
  482. required={registerFormFields.sex_neutered.required}
  483. validateTrigger={registerFormFields.sex_neutered.trigger}
  484. rules={registerFormFields.sex_neutered.validation}
  485. >
  486. <Select
  487. defaultValue={'UNALTERED'}
  488. placeholder={intl.formatMessage({
  489. id: 'register.sexNeutered.placeholder',
  490. defaultMessage: 'register.sexNeutered.placeholder',
  491. })}
  492. >
  493. <Select.Option value="ALTERED">
  494. {intl.formatMessage({
  495. id: 'register.sexNeutered.altered',
  496. defaultMessage: 'ALTERED',
  497. })}
  498. </Select.Option>
  499. <Select.Option value="UNALTERED">
  500. {intl.formatMessage({
  501. id: 'register.sexNeutered.unaltered',
  502. defaultMessage: 'UNALTERED',
  503. })}
  504. </Select.Option>
  505. </Select>
  506. </Form.Item>
  507. )}
  508. {/** 人类专用 */}
  509. {productName === 'DROS' && form?.getFieldValue('patient_sex') && (
  510. <Form.Item
  511. hidden={pregnancyStatusFieldVisible}
  512. label={
  513. <FormattedMessage
  514. id="register.pregnancyStatus"
  515. defaultMessage="register.pregnancyStatus"
  516. />
  517. }
  518. name="pregnancy_status"
  519. required={registerFormFields.pregnancy_status.required}
  520. validateTrigger={registerFormFields.pregnancy_status.trigger}
  521. rules={registerFormFields.pregnancy_status.validation}
  522. initialValue={PregnancyStatus.UNKNOWN}
  523. >
  524. <Radio.Group
  525. options={pregnancyStatusOptions}
  526. optionType="button"
  527. buttonStyle="solid"
  528. />
  529. </Form.Item>
  530. )}
  531. {/** 宠物专用 */}
  532. {productName === 'VETDROS' && (
  533. <Form.Item
  534. label={
  535. <FormattedMessage
  536. id="register.chipNumber"
  537. defaultMessage="register.chipNumber"
  538. />
  539. }
  540. name="chip_number"
  541. required={registerFormFields.chip_number.required}
  542. validateTrigger={registerFormFields.chip_number.trigger}
  543. rules={registerFormFields.chip_number.validation}
  544. >
  545. <Input
  546. placeholder={intl.formatMessage({
  547. id: 'register.chipNumber.placeholder',
  548. defaultMessage: 'register.chipNumber.placeholder',
  549. })}
  550. />
  551. </Form.Item>
  552. )}
  553. {/** 宠物专用 */}
  554. {productName === 'VETDROS' && (
  555. <Form.Item
  556. label={
  557. <FormattedMessage
  558. id="register.variety"
  559. defaultMessage="register.variety"
  560. />
  561. }
  562. name="variety"
  563. required={registerFormFields.variety.required}
  564. validateTrigger={registerFormFields.variety.trigger}
  565. rules={registerFormFields.variety.validation}
  566. >
  567. <Input
  568. placeholder={intl.formatMessage({
  569. id: 'register.variety.placeholder',
  570. defaultMessage: 'register.variety.placeholder',
  571. })}
  572. />
  573. </Form.Item>
  574. )}
  575. {/* <Form.Item
  576. label={
  577. <FormattedMessage
  578. id="register.patientType"
  579. defaultMessage="register.patientType"
  580. />
  581. }
  582. name="patient_type"
  583. required={registerFormFields.patient_type.required}
  584. validateTrigger={registerFormFields.patient_type.trigger}
  585. rules={registerFormFields.patient_type.validation}
  586. >
  587. <Input
  588. placeholder={intl.formatMessage({
  589. id: 'register.patientType.placeholder',
  590. defaultMessage: 'register.patientType.placeholder',
  591. })}
  592. />
  593. </Form.Item> */}
  594. <Form.Item
  595. label={
  596. <FormattedMessage
  597. id="register.referringPhysician"
  598. defaultMessage="register.referringPhysician"
  599. />
  600. }
  601. name="ref_physician"
  602. required={registerFormFields.ref_physician.required}
  603. validateTrigger={registerFormFields.ref_physician.trigger}
  604. rules={registerFormFields.ref_physician.validation}
  605. >
  606. <Input
  607. placeholder={intl.formatMessage({
  608. id: 'register.referringPhysician.placeholder',
  609. defaultMessage: 'register.referringPhysician.placeholder',
  610. })}
  611. />
  612. </Form.Item>
  613. <Form.Item
  614. label={
  615. <FormattedMessage
  616. id="register.operatorId"
  617. defaultMessage="register.operatorId"
  618. />
  619. }
  620. name="operator_id"
  621. required={registerFormFields.operator_id.required}
  622. validateTrigger={registerFormFields.operator_id.trigger}
  623. rules={registerFormFields.operator_id.validation}
  624. >
  625. <Input
  626. placeholder={intl.formatMessage({
  627. id: 'register.operatorId.placeholder',
  628. defaultMessage: 'register.operatorId.placeholder',
  629. })}
  630. />
  631. </Form.Item>
  632. <Form.Item
  633. label={
  634. <FormattedMessage
  635. id="register.weight"
  636. defaultMessage="register.weight"
  637. />
  638. }
  639. name="weight"
  640. required={registerFormFields.weight.required}
  641. validateTrigger={registerFormFields.weight.trigger}
  642. rules={registerFormFields.weight.validation}
  643. >
  644. <InputNumber min={0} addonAfter="kg" style={{ width: '100%' }} />
  645. </Form.Item>
  646. <Form.Item
  647. label={
  648. <FormattedMessage
  649. id="register.thickness"
  650. defaultMessage="register.thickness"
  651. />
  652. }
  653. name="thickness"
  654. required={registerFormFields.thickness.required}
  655. validateTrigger={registerFormFields.thickness.trigger}
  656. rules={registerFormFields.thickness.validation}
  657. >
  658. <InputNumber min={0} addonAfter="cm" style={{ width: '100%' }} />
  659. </Form.Item>
  660. <Form.Item
  661. label={
  662. <FormattedMessage
  663. id="register.height"
  664. defaultMessage="register.height"
  665. />
  666. }
  667. name="length"
  668. required={registerFormFields.length.required}
  669. validateTrigger={registerFormFields.length.trigger}
  670. rules={registerFormFields.length.validation}
  671. >
  672. <InputNumber min={0} addonAfter="cm" style={{ width: '100%' }} />
  673. </Form.Item>
  674. <Form.Item
  675. label={
  676. <FormattedMessage
  677. id="register.comment"
  678. defaultMessage="register.comment"
  679. />
  680. }
  681. name="comment"
  682. required={registerFormFields.comment.required}
  683. validateTrigger={registerFormFields.comment.trigger}
  684. rules={registerFormFields.comment.validation}
  685. >
  686. <Input
  687. placeholder={intl.formatMessage({
  688. id: 'register.comment.placeholder',
  689. defaultMessage: 'register.comment.placeholder',
  690. })}
  691. />
  692. </Form.Item>
  693. {/* 扫码枪兜底 input(隐藏) */}
  694. <input
  695. ref={scanInputRef}
  696. type="text"
  697. style={{
  698. position: 'fixed',
  699. opacity: 0,
  700. pointerEvents: 'none',
  701. left: -9999,
  702. }}
  703. onCompositionStart={() => setIsComposing(true)}
  704. onCompositionEnd={(e) => {
  705. setIsComposing(false);
  706. bufferRef.current = e.currentTarget.value;
  707. }}
  708. onInput={handleScanInput}
  709. autoComplete="off"
  710. />
  711. </Form>
  712. );
  713. };
  714. export default BasicInfoForm;