index.dart 14 KB

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