ExamStudentEditModal.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. import { toValueEnum } from '@/common/converter';
  2. import { validateIdCard } from '@/common/validator';
  3. import { MovableModalForm } from '@/components';
  4. import ExamStudentController from '@/services/apis/ExamStudentController';
  5. import { CertificateType } from '@/services/enums';
  6. import { FormInstance, ProFormDigit, ProFormRadio, ProFormSelect, ProFormText, ProFormTextArea } from '@ant-design/pro-components';
  7. import { useModel } from '@umijs/max';
  8. import { App, Col, Row } from 'antd';
  9. import { useEffect, useRef, useState } from 'react';
  10. export type GradeBranchData = {
  11. examGrades: API.ExamGradeOutput[];
  12. branches: API.SysOrgLiteOutput[];
  13. hasNceeCourseComb: boolean;
  14. };
  15. /** 修改监测学生信息 */
  16. const ExamStudentEditModal: React.FC<{
  17. examPlanId: number;
  18. data: Partial<API.ExamStudentOutput>;
  19. gradeBranch: GradeBranchData;
  20. onFinish: () => void;
  21. onClose?: () => void;
  22. }> = ({ examPlanId, data, gradeBranch, onFinish, onClose }) => {
  23. const [open, setOpen] = useState<boolean>(true);
  24. const handleClose = () => { setOpen(false); setTimeout(() => onClose?.(), 300); };
  25. const formRef = useRef<FormInstance>();
  26. const { message } = App.useApp();
  27. const { getDictValueEnum, } = useModel('useDict');
  28. const { baseData } = useModel('useBaseData');
  29. const [certType, setCertType] = useState(data.certificateType);
  30. const [grade, setGrade] = useState<API.ExamGradeOutput>();
  31. useEffect(() => {
  32. if (!data.examGradeId) {
  33. return;
  34. }
  35. const g = gradeBranch?.examGrades.find(t => t.id === data.examGradeId);
  36. setGrade(g);
  37. }, []);
  38. return (
  39. <>
  40. <MovableModalForm<API.ExamStudentOutput>
  41. title={`${data.id === '0' ? '添加' : '修改'}监测学生信息`}
  42. width={800}
  43. open={open}
  44. formRef={formRef}
  45. initialValues={{
  46. ...data,
  47. examGradeId: data.examGradeId !== undefined ? data.examGradeId : undefined,
  48. sysOrgBranchId: data.sysOrgBranchId !== undefined ? data.sysOrgBranchId : undefined,
  49. certificateType: data.certificateType !== undefined ? `${data.certificateType}` : undefined,
  50. gender: data.gender !== undefined ? `${data.gender}` : undefined,
  51. nceeCourseCombId: data.nceeCourseCombId !== undefined ? `${data.nceeCourseCombId}` : undefined,
  52. }}
  53. modalProps={{
  54. centered: true,
  55. maskClosable: false,
  56. onCancel: () => {
  57. formRef?.current?.resetFields();
  58. handleClose();
  59. },
  60. }}
  61. onFinish={async (values) => {
  62. const { examGradeId, certificateType, gender, ...restValues } = values;
  63. const grade = gradeBranch.examGrades.find(t => t.id === examGradeId);
  64. if (!grade) {
  65. return;
  66. }
  67. let p: API.AddExamStudentInput = {
  68. ...restValues,
  69. examPlanId,
  70. examGradeId,
  71. gradeId: grade.gradeId,
  72. certificateType: JSON.parse(certificateType as any),
  73. gender: JSON.parse(gender as any),
  74. };
  75. if (data.id !== '0') {
  76. const up = { id: data.id ?? 0, ...p } as API.UpdateExamStudentInput;
  77. await ExamStudentController.update(up);
  78. }
  79. else {
  80. await ExamStudentController.add(p);
  81. }
  82. message.success('已保存');
  83. onFinish();
  84. handleClose();
  85. }}
  86. >
  87. <Row gutter={[24, 0]}>
  88. <Col span={12}>
  89. <ProFormSelect
  90. label="年级"
  91. name="examGradeId"
  92. options={gradeBranch?.examGrades?.sort((a, b) => a.grade.gradeNumber - b.grade.gradeNumber)?.map(t => ({ label: `${t.grade.fullName}(${t.gradeBeginName})`, value: t.id }))}
  93. required
  94. rules={[{ required: true }]}
  95. disabled={data.id !== '0'}
  96. onChange={(e: number) => {
  97. const g = gradeBranch?.examGrades.find(t => t.id === e);
  98. setGrade(g);
  99. formRef.current?.validateFields(['examNumber']);
  100. }}
  101. />
  102. </Col>
  103. {gradeBranch.branches && gradeBranch.branches.length > 0 &&
  104. <Col span={12}>
  105. <ProFormSelect
  106. label="校区"
  107. name="sysOrgBranchId"
  108. options={gradeBranch.branches.map(t => ({ label: t.name, value: t.id }))}
  109. required
  110. disabled={data.id !== '0'}
  111. rules={[{ required: true }]}
  112. />
  113. </Col>
  114. }
  115. <Col span={12}>
  116. <ProFormDigit
  117. label="班级"
  118. tooltip="班级最小1,最大35"
  119. name="classNumber"
  120. min={1}
  121. max={35}
  122. required
  123. rules={[{ required: true }]}
  124. />
  125. </Col>
  126. <Col span={12}>
  127. <ProFormText
  128. label="姓名"
  129. name="name"
  130. required
  131. rules={[{ required: true, min: 2, max: 100 }]}
  132. fieldProps={{
  133. minLength: 2,
  134. maxLength: 100,
  135. showCount: true,
  136. }}
  137. />
  138. </Col>
  139. <Col span={12}>
  140. <ProFormSelect
  141. label="证件类型"
  142. name="certificateType"
  143. rules={[{ required: true }]}
  144. valueEnum={getDictValueEnum('certificate_type')}
  145. onChange={(v) => setCertType(JSON.parse(v as string))}
  146. />
  147. </Col>
  148. <Col span={12}>
  149. <ProFormText
  150. label="证件号码"
  151. name="idNumber"
  152. dependencies={['certificateType']}
  153. fieldProps={{
  154. maxLength: 18,
  155. showCount: true,
  156. }}
  157. required={certType !== CertificateType.NONE}
  158. rules={[
  159. ({ getFieldValue, setFieldsValue }) => ({
  160. validator: (_, value) => {
  161. const ct = JSON.parse(getFieldValue("certificateType"));
  162. if (ct === CertificateType.NONE) {
  163. return Promise.resolve();
  164. }
  165. if (ct === CertificateType.ID_CARD) {
  166. const idc = validateIdCard(value);
  167. if (idc.success) {
  168. setFieldsValue({ gender: `${idc.gender}` });
  169. return Promise.resolve();
  170. }
  171. else {
  172. return Promise.reject(`身份证号${idc.errorMessage}有误`);
  173. }
  174. }
  175. if (value.length > 0) {
  176. return Promise.resolve();
  177. }
  178. return Promise.reject('证件号码必须输入');
  179. },
  180. }),
  181. ]}
  182. />
  183. </Col>
  184. <Col span={12}>
  185. <ProFormRadio.Group
  186. label="性别"
  187. name="gender"
  188. valueEnum={getDictValueEnum('gender')}
  189. required
  190. rules={[{ required: true }]}
  191. />
  192. </Col>
  193. <Col span={12}>
  194. <ProFormText
  195. // label="自编监测号"
  196. label={`自编监测号${grade?.isRequiredSelfExamNumber ? `(当前年级必录,长度为${grade?.selfExamNumberLength}位)` : ''}`}
  197. name="examNumber"
  198. // tooltip="仅有明确要求时填写"
  199. fieldProps={{
  200. maxLength: grade?.isRequiredSelfExamNumber ? grade?.selfExamNumberLength : 20,
  201. showCount: true
  202. }}
  203. required={grade?.isRequiredSelfExamNumber}
  204. rules={[
  205. () => ({
  206. validator: (_, value) => {
  207. if (!grade?.isRequiredSelfExamNumber) {
  208. return Promise.resolve();
  209. }
  210. if ((value ?? '').length !== grade?.selfExamNumberLength) {
  211. return Promise.reject(`当前年级必录,长度为${grade?.selfExamNumberLength}位`);
  212. }
  213. return Promise.resolve();
  214. },
  215. }),
  216. ]}
  217. />
  218. </Col>
  219. {gradeBranch.hasNceeCourseComb &&
  220. <Col span={12}>
  221. <ProFormSelect
  222. label="选科组合"
  223. name="nceeCourseCombId"
  224. tooltip="仅高中学段填写"
  225. valueEnum={toValueEnum(baseData?.nceeCourseCombs?.map(t => ({ id: t.id, name: t.shortName })) ?? [])}
  226. />
  227. </Col>
  228. }
  229. <Col span={12}>
  230. <ProFormText
  231. label="考场号"
  232. name="roomNumber"
  233. fieldProps={{
  234. maxLength: 20,
  235. showCount: true
  236. }}
  237. />
  238. </Col>
  239. <Col span={12}>
  240. <ProFormText
  241. label="座位号"
  242. name="seatNumber"
  243. fieldProps={{
  244. maxLength: 20,
  245. showCount: true
  246. }}
  247. />
  248. </Col>
  249. </Row>
  250. <ProFormTextArea
  251. label="备注"
  252. name="remark"
  253. fieldProps={{
  254. maxLength: 200,
  255. rows: 4,
  256. showCount: true,
  257. }}
  258. />
  259. </MovableModalForm >
  260. </>
  261. );
  262. };
  263. export default ExamStudentEditModal;