register.form.tsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. import React, { useEffect, useRef } from 'react';
  2. import {
  3. Form,
  4. Input,
  5. DatePicker,
  6. InputNumber,
  7. Select,
  8. Radio,
  9. FormInstance,
  10. } from 'antd';
  11. import { useIntl, FormattedMessage } from 'react-intl';
  12. import { registerFormFields } from '@/validation/patient/registerSchema';
  13. import NumberWithUnit from '@/components/NumberWithUnit';
  14. import dayjs, { Dayjs } from 'dayjs';
  15. import { useSelector } from 'react-redux';
  16. import { RootState } from '@/states/store';
  17. import { PregnancyStatus, pregnancyStatusOptions } from '@/domain/patient/pregnancyStatus';
  18. interface BasicInfoFormProps {
  19. style?: React.CSSProperties;
  20. form?: FormInstance;
  21. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  22. onValuesChange?: (changedValues: any, allValues: any) => void;
  23. }
  24. const BasicInfoForm: React.FC<BasicInfoFormProps> = ({
  25. style,
  26. form,
  27. onValuesChange,
  28. }) => {
  29. // 使用 ref 记录更新源,防止循环触发
  30. const updateSourceRef = useRef<'age' | 'dob' | null>(null);
  31. const patient_age = Form.useWatch('patient_age', form);
  32. const patient_dob = Form.useWatch('patient_dob', form);
  33. const productName = useSelector(
  34. (state: RootState) => state.product.productName
  35. );
  36. const intl = useIntl();
  37. const genderOptions = [
  38. {
  39. value: 'M',
  40. label: intl.formatMessage({ id: 'register.gender.male_pet', defaultMessage: 'Male' }),
  41. },
  42. {
  43. value: 'F',
  44. label: intl.formatMessage({ id: 'register.gender.female_pet', defaultMessage: 'Female' }),
  45. },
  46. {
  47. value: 'O',
  48. label: intl.formatMessage({ id: 'register.gender.other_pet', defaultMessage: 'Other' }),
  49. },
  50. {
  51. value: 'U',
  52. label: intl.formatMessage({ id: 'register.gender.unknown_pet', defaultMessage: 'Unknown' }),
  53. },
  54. ];
  55. // 根据年龄计算出生日期
  56. const calculateDobFromAge = (age: {
  57. number: number;
  58. unit: 'D' | 'M' | 'Y';
  59. }): Dayjs => {
  60. const now = dayjs();
  61. switch (age.unit) {
  62. case 'D':
  63. return now.subtract(age.number, 'day');
  64. case 'M':
  65. return now.subtract(age.number, 'month');
  66. case 'Y':
  67. return now.subtract(age.number, 'year');
  68. default:
  69. return now;
  70. }
  71. };
  72. // 根据出生日期计算年龄
  73. const calculateAgeFromDob = (
  74. dob: Dayjs
  75. ): { number: number; unit: 'D' | 'M' | 'Y' } => {
  76. const now = dayjs();
  77. const years = now.diff(dob, 'year');
  78. if (years >= 1) {
  79. return { number: years, unit: 'Y' };
  80. }
  81. const months = now.diff(dob, 'month');
  82. if (months >= 1) {
  83. return { number: months, unit: 'M' };
  84. }
  85. const days = now.diff(dob, 'day');
  86. return { number: Math.max(0, days), unit: 'D' };
  87. };
  88. // 监听年龄变化
  89. useEffect(() => {
  90. // 如果是出生日期联动更新的年龄,跳过
  91. if (updateSourceRef.current === 'dob') {
  92. updateSourceRef.current = null;
  93. return;
  94. }
  95. // 如果年龄有效,计算出生日期
  96. if (patient_age && patient_age.number >= 0 && form) {
  97. updateSourceRef.current = 'age';
  98. const newDob = calculateDobFromAge(patient_age);
  99. form.setFieldsValue({ patient_dob: newDob });
  100. console.log('年龄变化,更新出生日期:', newDob.format('YYYY-MM-DD'));
  101. }
  102. }, [patient_age, form]);
  103. // 监听出生日期变化
  104. useEffect(() => {
  105. // 如果是年龄联动更新的出生日期,跳过
  106. if (updateSourceRef.current === 'age') {
  107. updateSourceRef.current = null;
  108. return;
  109. }
  110. // 如果出生日期有效,计算年龄
  111. if (patient_dob && dayjs.isDayjs(patient_dob) && form) {
  112. updateSourceRef.current = 'dob';
  113. const newAge = calculateAgeFromDob(patient_dob);
  114. form.setFieldsValue({ patient_age: newAge });
  115. console.log('出生日期变化,更新年龄:', newAge);
  116. }
  117. }, [patient_dob, form]);
  118. return (
  119. <Form
  120. form={form}
  121. layout="vertical"
  122. style={style}
  123. className="px-2"
  124. initialValues={
  125. {
  126. // patient_dob: dayjs(),
  127. }
  128. }
  129. onValuesChange={onValuesChange}
  130. >
  131. {/** 宠物专用 */}
  132. {productName === 'VETDROS' && (
  133. <Form.Item
  134. label={
  135. <FormattedMessage
  136. id="register.owner_name"
  137. defaultMessage="register.owner_name"
  138. />
  139. }
  140. name="owner_name"
  141. required={registerFormFields.owner_name.required}
  142. validateTrigger={registerFormFields.owner_name.trigger}
  143. rules={registerFormFields.owner_name.validation}
  144. >
  145. <Input
  146. placeholder={intl.formatMessage({
  147. id: 'register.owner_name.placeholder',
  148. defaultMessage: 'register.owner_name.placeholder',
  149. })}
  150. />
  151. </Form.Item>
  152. )}
  153. <Form.Item
  154. label={
  155. <FormattedMessage
  156. id="register.accessionNumber"
  157. defaultMessage="register.accessionNumber"
  158. />
  159. }
  160. name="accession_number"
  161. required={registerFormFields.accession_number.required}
  162. validateTrigger={registerFormFields.accession_number.trigger}
  163. rules={registerFormFields.accession_number.validation}
  164. >
  165. <Input
  166. placeholder={intl.formatMessage({
  167. id: 'register.accessionNumber.placeholder',
  168. defaultMessage: 'register.accessionNumber.placeholder',
  169. })}
  170. />
  171. </Form.Item>
  172. <Form.Item
  173. label={
  174. <FormattedMessage
  175. id="register.patientId"
  176. defaultMessage="register.patientId"
  177. />
  178. }
  179. name="patient_id"
  180. required={registerFormFields.patient_id.required}
  181. validateTrigger={registerFormFields.patient_id.trigger}
  182. rules={registerFormFields.patient_id.validation}
  183. >
  184. <Input
  185. placeholder={intl.formatMessage({
  186. id: 'register.patientId.placeholder',
  187. defaultMessage: 'register.patientId.placeholder',
  188. })}
  189. />
  190. </Form.Item>
  191. <Form.Item
  192. label={
  193. <FormattedMessage
  194. id="register.patientName"
  195. defaultMessage="register.patientName"
  196. />
  197. }
  198. name="patient_name"
  199. required={registerFormFields.patient_name.required}
  200. validateTrigger={registerFormFields.patient_name.trigger}
  201. rules={registerFormFields.patient_name.validation}
  202. >
  203. <Input
  204. placeholder={intl.formatMessage({
  205. id: 'register.patientName.placeholder',
  206. defaultMessage: 'register.patientName.placeholder',
  207. })}
  208. />
  209. </Form.Item>
  210. <Form.Item
  211. label={
  212. <FormattedMessage
  213. id="register.patientSize"
  214. defaultMessage="register.patientSize"
  215. />
  216. }
  217. name="patient_size"
  218. required={registerFormFields.patient_size.required}
  219. validateTrigger={registerFormFields.patient_size.trigger}
  220. rules={registerFormFields.patient_size.validation}
  221. initialValue="Medium"
  222. >
  223. <Select
  224. placeholder={intl.formatMessage({
  225. id: 'register.patientSize.placeholder',
  226. defaultMessage: 'register.patientSize.placeholder',
  227. })}
  228. defaultValue="Medium"
  229. >
  230. <Select.Option value="Large">Large</Select.Option>
  231. <Select.Option value="Medium">Medium</Select.Option>
  232. <Select.Option value="Small">Small</Select.Option>
  233. </Select>
  234. </Form.Item>
  235. <Form.Item
  236. label={
  237. <FormattedMessage id="register.age" defaultMessage="register.age" />
  238. }
  239. name="patient_age"
  240. required={registerFormFields.patient_age.required}
  241. validateTrigger={registerFormFields.patient_age.trigger}
  242. rules={registerFormFields.patient_age.validation}
  243. initialValue={{ number: 0, unit: 'Y' }}
  244. >
  245. <NumberWithUnit
  246. align="baseline"
  247. defaultUnit="Y"
  248. defaultNumber={0}
  249. unitClassName="w-full"
  250. numberClassName="w-full"
  251. className="w-full"
  252. options={[
  253. { label: '天', value: 'D' },
  254. { label: '月', value: 'M' },
  255. { label: '年', value: 'Y' },
  256. ]}
  257. />
  258. </Form.Item>
  259. <Form.Item
  260. label={
  261. <FormattedMessage
  262. id="register.dateOfBirth"
  263. defaultMessage="register.dateOfBirth"
  264. />
  265. }
  266. name="patient_dob"
  267. required={registerFormFields.patient_dob.required}
  268. validateTrigger={registerFormFields.patient_dob.trigger}
  269. rules={registerFormFields.patient_dob.validation}
  270. >
  271. <DatePicker format="YYYY-MM-DD" style={{ width: '100%' }} />
  272. </Form.Item>
  273. <Form.Item
  274. label={
  275. <FormattedMessage
  276. id="register.gender"
  277. defaultMessage="register.gender"
  278. />
  279. }
  280. name="patient_sex"
  281. required={registerFormFields.patient_sex.required}
  282. validateTrigger={registerFormFields.patient_sex.trigger}
  283. rules={registerFormFields.patient_sex.validation}
  284. >
  285. <Select options={genderOptions} />
  286. </Form.Item>
  287. {/** 宠物专用 */}
  288. {productName === 'VETDROS' && (
  289. <Form.Item
  290. label={
  291. <FormattedMessage
  292. id="register.sexNeutered"
  293. defaultMessage="register.sexNeutered"
  294. />
  295. }
  296. name="sex_neutered"
  297. required={registerFormFields.sex_neutered.required}
  298. validateTrigger={registerFormFields.sex_neutered.trigger}
  299. rules={registerFormFields.sex_neutered.validation}
  300. >
  301. <Select
  302. defaultValue={'UNALTERED'}
  303. placeholder={intl.formatMessage({
  304. id: 'register.sexNeutered.placeholder',
  305. defaultMessage: 'register.sexNeutered.placeholder',
  306. })}
  307. >
  308. <Select.Option value="ALTERED">ALTERED (绝育)</Select.Option>
  309. <Select.Option value="UNALTERED">UNALTERED (未绝育)</Select.Option>
  310. </Select>
  311. </Form.Item>
  312. )}
  313. {/** 人类专用 */}
  314. {productName === 'DROS' && (
  315. <Form.Item
  316. label={
  317. <FormattedMessage
  318. id="register.pregnancyStatus"
  319. defaultMessage="register.pregnancyStatus"
  320. />
  321. }
  322. name="pregnancy_status"
  323. required={registerFormFields.pregnancy_status.required}
  324. validateTrigger={registerFormFields.pregnancy_status.trigger}
  325. rules={registerFormFields.pregnancy_status.validation}
  326. initialValue={PregnancyStatus.UNKNOWN}
  327. >
  328. <Radio.Group
  329. options={pregnancyStatusOptions}
  330. optionType="button"
  331. buttonStyle="solid"
  332. />
  333. </Form.Item>
  334. )}
  335. {/** 宠物专用 */}
  336. {productName === 'VETDROS' && (
  337. <Form.Item
  338. label={
  339. <FormattedMessage
  340. id="register.chipNumber"
  341. defaultMessage="register.chipNumber"
  342. />
  343. }
  344. name="chip_number"
  345. required={registerFormFields.chip_number.required}
  346. validateTrigger={registerFormFields.chip_number.trigger}
  347. rules={registerFormFields.chip_number.validation}
  348. >
  349. <Input
  350. placeholder={intl.formatMessage({
  351. id: 'register.chipNumber.placeholder',
  352. defaultMessage: 'register.chipNumber.placeholder',
  353. })}
  354. />
  355. </Form.Item>
  356. )}
  357. {/** 宠物专用 */}
  358. {productName === 'VETDROS' && (
  359. <Form.Item
  360. label={
  361. <FormattedMessage
  362. id="register.variety"
  363. defaultMessage="register.variety"
  364. />
  365. }
  366. name="variety"
  367. required={registerFormFields.variety.required}
  368. validateTrigger={registerFormFields.variety.trigger}
  369. rules={registerFormFields.variety.validation}
  370. >
  371. <Input
  372. placeholder={intl.formatMessage({
  373. id: 'register.variety.placeholder',
  374. defaultMessage: 'register.variety.placeholder',
  375. })}
  376. />
  377. </Form.Item>
  378. )}
  379. {/* <Form.Item
  380. label={
  381. <FormattedMessage
  382. id="register.patientType"
  383. defaultMessage="register.patientType"
  384. />
  385. }
  386. name="patient_type"
  387. required={registerFormFields.patient_type.required}
  388. validateTrigger={registerFormFields.patient_type.trigger}
  389. rules={registerFormFields.patient_type.validation}
  390. >
  391. <Input
  392. placeholder={intl.formatMessage({
  393. id: 'register.patientType.placeholder',
  394. defaultMessage: 'register.patientType.placeholder',
  395. })}
  396. />
  397. </Form.Item> */}
  398. <Form.Item
  399. label={
  400. <FormattedMessage
  401. id="register.referringPhysician"
  402. defaultMessage="register.referringPhysician"
  403. />
  404. }
  405. name="ref_physician"
  406. required={registerFormFields.ref_physician.required}
  407. validateTrigger={registerFormFields.ref_physician.trigger}
  408. rules={registerFormFields.ref_physician.validation}
  409. >
  410. <Input
  411. placeholder={intl.formatMessage({
  412. id: 'register.referringPhysician.placeholder',
  413. defaultMessage: 'register.referringPhysician.placeholder',
  414. })}
  415. />
  416. </Form.Item>
  417. <Form.Item
  418. label={
  419. <FormattedMessage
  420. id="register.operatorId"
  421. defaultMessage="register.operatorId"
  422. />
  423. }
  424. name="operator_id"
  425. required={registerFormFields.operator_id.required}
  426. validateTrigger={registerFormFields.operator_id.trigger}
  427. rules={registerFormFields.operator_id.validation}
  428. >
  429. <Input
  430. placeholder={intl.formatMessage({
  431. id: 'register.operatorId.placeholder',
  432. defaultMessage: 'register.operatorId.placeholder',
  433. })}
  434. />
  435. </Form.Item>
  436. <Form.Item
  437. label={
  438. <FormattedMessage
  439. id="register.weight"
  440. defaultMessage="register.weight"
  441. />
  442. }
  443. name="weight"
  444. required={registerFormFields.weight.required}
  445. validateTrigger={registerFormFields.weight.trigger}
  446. rules={registerFormFields.weight.validation}
  447. >
  448. <InputNumber min={0} addonAfter="kg" style={{ width: '100%' }} />
  449. </Form.Item>
  450. <Form.Item
  451. label={
  452. <FormattedMessage
  453. id="register.thickness"
  454. defaultMessage="register.thickness"
  455. />
  456. }
  457. name="thickness"
  458. required={registerFormFields.thickness.required}
  459. validateTrigger={registerFormFields.thickness.trigger}
  460. rules={registerFormFields.thickness.validation}
  461. >
  462. <InputNumber min={0} addonAfter="cm" style={{ width: '100%' }} />
  463. </Form.Item>
  464. <Form.Item
  465. label={
  466. <FormattedMessage
  467. id="register.height"
  468. defaultMessage="register.height"
  469. />
  470. }
  471. name="length"
  472. required={registerFormFields.length.required}
  473. validateTrigger={registerFormFields.length.trigger}
  474. rules={registerFormFields.length.validation}
  475. >
  476. <InputNumber min={0} addonAfter="cm" style={{ width: '100%' }} />
  477. </Form.Item>
  478. <Form.Item
  479. label={
  480. <FormattedMessage
  481. id="register.comment"
  482. defaultMessage="register.comment"
  483. />
  484. }
  485. name="comment"
  486. required={registerFormFields.comment.required}
  487. validateTrigger={registerFormFields.comment.trigger}
  488. rules={registerFormFields.comment.validation}
  489. >
  490. <Input
  491. placeholder={intl.formatMessage({
  492. id: 'register.comment.placeholder',
  493. defaultMessage: 'register.comment.placeholder',
  494. })}
  495. />
  496. </Form.Item>
  497. </Form>
  498. );
  499. };
  500. export default BasicInfoForm;