index.dart 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. // ignore_for_file: must_be_immutable
  2. import 'package:date_format/date_format.dart';
  3. import 'package:fis_common/extensions/string.dart';
  4. import 'package:fis_jsonrpc/rpc.dart';
  5. import 'package:flutter/foundation.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:get/get.dart';
  8. import 'package:vitalapp/architecture/app_parameters.dart';
  9. import 'package:vitalapp/architecture/utils/prompt_box.dart';
  10. import 'package:vitalapp/components/alert_dialog.dart';
  11. import 'package:vitalapp/components/dialog_date.dart';
  12. import 'package:vitalapp/components/dialog_input.dart';
  13. import 'package:vitalapp/components/dialog_select.dart';
  14. import 'package:vitalapp/components/input.dart';
  15. import 'package:vitalapp/helper/id_card_helper.dart';
  16. import 'package:vitalapp/pages/medical_checkup_station/registration/controller.dart';
  17. import 'package:vitalapp/pages/medical_checkup_station/registration/state/list.dart';
  18. import 'package:vitalapp/store/store.dart';
  19. class RegistrationFormDialog extends StatefulWidget {
  20. final RegistrationInfoModel? patient;
  21. final bool? isEdit;
  22. RegistrationFormDialog({Key? key, this.patient, this.isEdit = false})
  23. : super(key: key);
  24. @override
  25. _RegistrationFormDialogState createState() => _RegistrationFormDialogState();
  26. }
  27. class _RegistrationFormDialogState extends State<RegistrationFormDialog> {
  28. var controller = Get.find<RegistrationController>();
  29. @override
  30. Widget build(BuildContext context) {
  31. return GetBuilder<RegistrationController>(
  32. id: "registration_Form",
  33. builder: (_) {
  34. return VAlertDialog(
  35. title: widget.isEdit! ? "编辑登记信息" : "体检信息录入",
  36. width: 900,
  37. content: _buildView(),
  38. onConfirm: _onSubmitIdentityVerify,
  39. );
  40. },
  41. );
  42. }
  43. Widget _buildView() {
  44. return Container(
  45. child: ListView(
  46. shrinkWrap: true,
  47. children: [
  48. Column(
  49. children: [
  50. _buildInputItem(
  51. "身份证号",
  52. value: widget.patient?.cardNo ?? '',
  53. onTap: () async {
  54. var result = await VDialogInput(
  55. initialValue: widget.patient?.cardNo ?? '',
  56. title: "身份证号",
  57. onConfirmVerification: (v) {
  58. bool isValid = IdCardHelper.validateIDCard(v);
  59. if (!isValid) {
  60. PromptBox.showToast("请填写正确的证件号");
  61. }
  62. return isValid;
  63. },
  64. showCancel: true,
  65. ).show();
  66. if (result == null) {
  67. return;
  68. }
  69. if (widget.patient == null) {
  70. return;
  71. }
  72. result = result.toString().toUpperCase();
  73. final patient = widget.patient!;
  74. patient.cardNo = result;
  75. patient.birthday = _getBirthDayofIdNumber(result);
  76. patient.patientGender = _checkGenderFromID(result);
  77. final patientDto = await controller.getPatientByID(result);
  78. if (patientDto != null) {
  79. patient.patientName = patientDto.patientName;
  80. patient.phone = patientDto.phone;
  81. patient.patientAddress = patientDto.patientAddress;
  82. }
  83. controller.update(["registration_Form"]);
  84. },
  85. ),
  86. _buildInputItem(
  87. "姓名",
  88. value: widget.patient?.patientName ?? '',
  89. onTap: () async {
  90. var result = await VDialogInput(
  91. initialValue: widget.patient?.patientName ?? '',
  92. title: "姓名",
  93. ).show();
  94. if (result != null) {
  95. widget.patient?.patientName = result;
  96. controller.update(["registration_Form"]);
  97. }
  98. },
  99. ),
  100. _buildInputItem(
  101. "性别",
  102. value: controller.formController.selectGenderList
  103. .firstWhereOrNull((element) =>
  104. element.code == widget.patient?.patientGender)
  105. ?.name,
  106. onTap: () async {
  107. String? result =
  108. await VDialogSelect<VSelectGenderEnumModel, String>(
  109. source: controller.formController.selectGenderList,
  110. labelGetter: (data) => data.name,
  111. valueGetter: (data) => data.code.index.toString(),
  112. initialValue:
  113. widget.patient?.patientGender?.index.toString(),
  114. title: "性别",
  115. ).show();
  116. if (result != null) {
  117. widget.patient?.patientGender =
  118. GenderEnum.values[int.parse(result)];
  119. controller.update(["registration_Form"]);
  120. }
  121. },
  122. suffixIcon: Icon(
  123. Icons.chevron_right_rounded,
  124. size: 30,
  125. ),
  126. ),
  127. _buildInputItem(
  128. "生日",
  129. value: getBirthDay(widget.patient?.birthday),
  130. onTap: () async {
  131. DateTime? result;
  132. bool _isLocalStation = AppParameters.data.isLocalStation;
  133. if (kIsWeb || _isLocalStation) {
  134. result = await showDatePicker(
  135. context: Get.context!,
  136. initialDate: widget.patient?.birthday ?? DateTime.now(),
  137. firstDate: DateTime(1900),
  138. lastDate: DateTime(2100),
  139. );
  140. } else {
  141. result = await VDialogDate(
  142. initialValue: widget.patient?.birthday,
  143. title: "生日",
  144. ).show();
  145. }
  146. if (result != null) {
  147. widget.patient?.birthday = result;
  148. controller.update(["registration_Form"]);
  149. }
  150. },
  151. suffixIcon: Icon(
  152. Icons.chevron_right_rounded,
  153. size: 30,
  154. ),
  155. ),
  156. _buildInputItem(
  157. "手机号",
  158. value: widget.patient?.phone,
  159. onTap: () async {
  160. var result = await VDialogInput(
  161. initialValue: widget.patient?.phone ?? '',
  162. title: "手机号",
  163. showCancel: true,
  164. onConfirmVerification: (v) {
  165. var isValid =
  166. (v.length == 11 && IdCardHelper.isNumeric(v));
  167. if (!isValid) {
  168. PromptBox.showToast("请填写正确的手机号");
  169. }
  170. return isValid;
  171. },
  172. ).show();
  173. if (result != null) {
  174. widget.patient?.phone = result;
  175. controller.update(["registration_Form"]);
  176. }
  177. },
  178. ),
  179. _buildInputItem(
  180. "家庭住址",
  181. value: widget.patient?.patientAddress ?? '',
  182. onTap: () async {
  183. var result = await VDialogInput(
  184. initialValue: widget.patient?.patientAddress ?? '',
  185. title: "家庭住址",
  186. ).show();
  187. if (result != null) {
  188. widget.patient?.patientAddress = result;
  189. controller.update(["registration_Form"]);
  190. }
  191. },
  192. ),
  193. SizedBox(
  194. height: 40,
  195. )
  196. ],
  197. ),
  198. ],
  199. ),
  200. );
  201. }
  202. Widget _buildInputItem(String title,
  203. {String? value, Function()? onTap, Widget? suffixIcon, int? maxLines}) {
  204. return Container(
  205. padding: const EdgeInsets.only(
  206. left: 80,
  207. right: 110,
  208. ),
  209. child: Row(
  210. children: [
  211. Container(
  212. width: 150,
  213. padding: const EdgeInsets.symmetric(vertical: 16),
  214. child: Text(
  215. title,
  216. style: const TextStyle(fontSize: 22),
  217. ),
  218. ),
  219. Expanded(
  220. child: VInput(
  221. initialValue: value,
  222. radius: 4,
  223. readOnly: true,
  224. suffixIcon: suffixIcon,
  225. maxLines: maxLines,
  226. onTap: () {
  227. onTap?.call();
  228. },
  229. ),
  230. )
  231. ],
  232. ),
  233. );
  234. }
  235. void _onSubmitIdentityVerify() async {
  236. String? isValidate = await _validateForm(widget.patient);
  237. if (isValidate != null) {
  238. PromptBox.toast(isValidate);
  239. return;
  240. } else {
  241. final patientDto =
  242. await controller.getPatientByID(widget.patient!.cardNo!, false);
  243. if (patientDto != null) {
  244. if (patientDto.currentOrgCode != Store.user.organizationCode) {
  245. PromptBox.toast("该居民已被其他医院建档");
  246. return;
  247. }
  248. }
  249. Get.back(result: widget.patient);
  250. }
  251. }
  252. Future<String?> _validateForm(RegistrationInfoModel? patient) async {
  253. if (patient == null) {
  254. return "当前不存在病人";
  255. }
  256. if (patient.patientName == null || patient.patientName!.isEmpty) {
  257. return "请填写姓名";
  258. }
  259. if (patient.cardNo == null || patient.cardNo!.isEmpty) {
  260. return "请填写身份证";
  261. }
  262. if (patient.patientGender == null) {
  263. return "请填写性别";
  264. }
  265. bool isNotIDCard = IdCardHelper.validateIDCard(patient.cardNo ?? '');
  266. if (!isNotIDCard) {
  267. return "请填写正确的身份证号";
  268. }
  269. if (patient.phone.isNotNullOrEmpty) {
  270. if (patient.phone!.length != 11 || !isNumeric(patient.phone!)) {
  271. return "请填写正确的联系电话";
  272. }
  273. }
  274. /// TODO 需求变更暂时删除
  275. // final selectedNormalCodes = crowdLabelsController.state.selectedNormalCodes;
  276. // if (selectedNormalCodes.length > 1) {
  277. // return "人群分类:一般人群、儿童、孕妇、老年人,只可选择其一!";
  278. // }
  279. // final crowdLabelCodes = crowdLabelsController.state.selectedCodes;
  280. // if (crowdLabelCodes.isEmpty) {
  281. // return "请选择人群分类";
  282. // }
  283. // if (state.gender == GenderEnum.Male &&
  284. // crowdLabelCodes.contains('RQFL_YF')) {
  285. // return "当前居民性别为“男”,人群分类不可选择孕妇!";
  286. // }
  287. return null;
  288. }
  289. bool isNumeric(String str) {
  290. if (str.isEmpty) {
  291. return false;
  292. }
  293. return double.tryParse(str) != null;
  294. }
  295. DateTime? _getBirthDayofIdNumber(String idCardNumber) {
  296. if (idCardNumber.isEmpty) return DateTime(1970, 1, 1);
  297. final idCardRegex = RegExp(r'^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})(\d|X)$');
  298. final match = idCardRegex.firstMatch(idCardNumber);
  299. if (match != null) {
  300. final year = int.parse(match.group(2)!);
  301. final month = int.parse(match.group(3)!);
  302. final day = int.parse(match.group(4)!);
  303. final birthDate = DateTime(year, month, day);
  304. return birthDate;
  305. }
  306. return null; // 返回一个默认值,表示无法提取出生日期
  307. }
  308. GenderEnum _checkGenderFromID(String idNumber) {
  309. if (idNumber.isNullOrEmpty) {
  310. return GenderEnum.Unspecified;
  311. }
  312. int secondLastDigit = int.parse(idNumber[idNumber.length - 2]);
  313. if (secondLastDigit % 2 == 0) {
  314. return GenderEnum.Female;
  315. } else {
  316. return GenderEnum.Male;
  317. }
  318. }
  319. String getBirthDay(DateTime? birthDay) {
  320. if (birthDay == null) {
  321. return "";
  322. }
  323. return formatDate(birthDay, [yyyy, "-", mm, "-", dd]);
  324. }
  325. }
  326. // 由于 PatientDTO 的不可控,故创建自有模型,用于创建新记录
  327. class RegistrationInfoModel {
  328. String? code;
  329. String? patientName;
  330. String? phone;
  331. String? cardNo;
  332. String? nationality;
  333. DateTime? birthday;
  334. GenderEnum? patientGender;
  335. String? patientAddress;
  336. String? permanentResidenceAddress;
  337. RegistrationInfoModel({
  338. this.code,
  339. this.patientName,
  340. this.phone,
  341. this.cardNo,
  342. this.nationality,
  343. this.birthday,
  344. this.patientGender,
  345. this.patientAddress,
  346. });
  347. factory RegistrationInfoModel.fromPatientDTO(PatientDTO patient) {
  348. return RegistrationInfoModel(
  349. code: patient.code,
  350. patientName: patient.patientName,
  351. phone: patient.phone,
  352. cardNo: patient.cardNo,
  353. nationality: patient.nationality,
  354. birthday: patient.birthday,
  355. patientGender: patient.patientGender,
  356. patientAddress: patient.patientAddress,
  357. );
  358. }
  359. PatientDTO addToPatientDTO(PatientDTO patient) {
  360. patient.code = code;
  361. patient.patientName = patientName;
  362. patient.phone = phone;
  363. patient.cardNo = cardNo;
  364. patient.nationality = nationality;
  365. patient.birthday = birthday;
  366. patient.patientGender = patientGender ?? GenderEnum.Unspecified;
  367. patient.patientAddress = patientAddress;
  368. patient.permanentResidenceAddress = permanentResidenceAddress;
  369. return patient;
  370. }
  371. static GenderEnum getGenderEnum(String? genderString) {
  372. switch (genderString) {
  373. case "男":
  374. return GenderEnum.Male;
  375. case "女":
  376. return GenderEnum.Female;
  377. case "未知的性别":
  378. return GenderEnum.Unknown;
  379. case "未说明的性别":
  380. return GenderEnum.Unspecified;
  381. default:
  382. return GenderEnum.Unspecified;
  383. }
  384. }
  385. static String getGenderString(GenderEnum? enumValue) {
  386. switch (enumValue) {
  387. case GenderEnum.Male:
  388. return "男";
  389. case GenderEnum.Female:
  390. return "女";
  391. case GenderEnum.Unknown:
  392. return "未知的性别";
  393. case GenderEnum.Unspecified:
  394. return "未说明的性别";
  395. default:
  396. return "未说明的性别";
  397. }
  398. }
  399. }