controller.dart 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. import 'dart:convert';
  2. import 'dart:ui';
  3. import 'package:barcode/barcode.dart';
  4. import 'package:fis_jsonrpc/rpc.dart';
  5. import 'package:flutter/foundation.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:flutter/services.dart';
  8. import 'package:flutter_inappwebview/flutter_inappwebview.dart';
  9. import 'package:get/get.dart';
  10. import 'package:vitalapp/architecture/defines.dart';
  11. import 'package:vitalapp/architecture/storage/text_storage.dart';
  12. import 'package:vitalapp/architecture/utils/datetime.dart';
  13. import 'package:vitalapp/architecture/utils/prompt_box.dart';
  14. import 'package:vitalapp/global.dart';
  15. import 'package:vitalapp/managers/interfaces/exam.dart';
  16. import 'package:vitalapp/managers/interfaces/health_check_record.dart';
  17. import 'package:vitalapp/managers/interfaces/registration.dart';
  18. import 'package:vitalapp/pages/check/health_check_record/components/health_check.dart';
  19. import 'package:vitalapp/pages/check/health_check_record/state.dart';
  20. import 'package:vitalapp/pages/check/models/form.dart';
  21. import 'package:vitalapp/pages/medical_checkup_station/registration/controller.dart';
  22. import 'package:vitalapp/pages/medical_checkup_station/registration/state/list.dart';
  23. import 'package:vitalapp/pages/medical_checkup_station/usb_print/module/printer_info.dart';
  24. import 'package:vitalapp/rpc.dart';
  25. import 'package:vitalapp/store/store.dart';
  26. import 'package:webviewx/webviewx.dart';
  27. import 'package:barcode_image/barcode_image.dart';
  28. import 'package:flutter_svg/flutter_svg.dart';
  29. import 'controllers/list_controller.dart';
  30. class HealthCheckRecordController extends FControllerBase {
  31. late final ListController listController;
  32. /// 表格loading
  33. bool tableLoading = true;
  34. /// 当前表格页数
  35. int currPageIndex = 1;
  36. /// 预约列表的总数
  37. int appointmentModelListLength = 0;
  38. /// 体检列表
  39. List<ResidentModel> residentList = [];
  40. HealthCheckRecordController() {
  41. listController = ListController(this);
  42. }
  43. final state = HealthCheckRecordState();
  44. final _healthCheckRecordManager = Get.find<IHealthCehckRecordManager>();
  45. final _examManager = Get.find<IExamManager>();
  46. final registrationManager = Get.find<IRegistrationManager>();
  47. late String patientCode;
  48. PrinterInfo? printInfo;
  49. List<MenuItem> menuList = [
  50. MenuItem(label: "一般状况", value: 'ZZYBZK'),
  51. MenuItem(label: "辅助检查", value: 'FZJC'),
  52. MenuItem(label: "生活方式", value: 'SHFS'),
  53. MenuItem(label: "脏器及查体", value: 'ZQCT'),
  54. MenuItem(label: "现存主要健康问题", value: 'XCZYWT'),
  55. MenuItem(label: "住院及用药情况", value: 'ZYYYFMYGHYFJZS'),
  56. MenuItem(label: "健康评价及指导", value: 'JKPJJZD'),
  57. ];
  58. @override
  59. void onReady() {
  60. super.onReady();
  61. initData();
  62. }
  63. Future<void> initData() async {
  64. update(["contract_records"]);
  65. patientCode = Store.user.currentSelectPatientInfo?.code ?? '';
  66. await _getExamRecordList();
  67. await getRegisterInfoPage(keyword: patientCode);
  68. }
  69. Future<void> _getExamRecordList() async {
  70. try {
  71. var result =
  72. await _healthCheckRecordManager.getExamRecordList(patientCode);
  73. if (result != null) {
  74. state.examRecordDTOList = result;
  75. }
  76. } catch (e) {
  77. print(e);
  78. return;
  79. }
  80. }
  81. Future<void> updateExamByBatchNumberAsync(key, batchNumber, data) async {
  82. if (!kIsOnline) {
  83. // TODO: 暂不支持离线体检
  84. PromptBox.toast("请检查网络连接");
  85. return;
  86. }
  87. TextStorage template = TextStorage(
  88. fileName: key,
  89. directory: "patient/$patientCode/exam",
  90. );
  91. final result = await _examManager
  92. .updateExamByBatchNumberAsync(UpdateExamByBatchNumberRequest(
  93. key: key,
  94. batchNumber: batchNumber,
  95. examData: data,
  96. patientCode: patientCode,
  97. ));
  98. if (result ?? false) {
  99. try {
  100. template.save(data);
  101. PromptBox.toast('保存成功');
  102. await _getExamRecordList();
  103. } catch (err) {
  104. PromptBox.toast('保存失败');
  105. }
  106. }
  107. }
  108. ///读取静态Html
  109. Future<String> loadingLocalAsset() async {
  110. ///加载
  111. String htmlData = await rootBundle.loadString('assets/docs/examTable.html');
  112. print("加载数据完成 $htmlData");
  113. return htmlData;
  114. }
  115. Future<void> toExamDetailPage(int index, ExamRecordDTO dto) async {
  116. var initialData = await loadingLocalAsset();
  117. var examRecordDatas = state.examRecordDTOList[index].examRecordDatas;
  118. Map<String, dynamic> examData = {};
  119. examRecordDatas?.forEach((element) {
  120. examData.addAll(jsonDecode(element.examData ?? '{}'));
  121. });
  122. // 将数据转换为 JSON 字符串
  123. var jsonData = jsonEncode({
  124. "patientInfo": {
  125. "patientName": dto.patientName,
  126. },
  127. "examData": examData,
  128. "docterName": dto.examDoctor,
  129. "examTime":
  130. DataTimeUtils.formatDateString(dto.examTime ?? DateTime.now()),
  131. });
  132. _openExamDetail(initialData, jsonData);
  133. }
  134. Future<void> toCheckPage(
  135. ExamRecordDTO dto,
  136. List<ExamRecordDataDTO>? dataDTO,
  137. int index,
  138. ) async {
  139. Get.dialog(
  140. HealthCheckDialog(
  141. examRecord: dto,
  142. examRecordDatas: dataDTO,
  143. index: index,
  144. ),
  145. );
  146. }
  147. String getHealthCheckRecordType(String key) {
  148. switch (key) {
  149. case 'ZZYBZK':
  150. return '症状、一般状况';
  151. case 'SHFS':
  152. return '生活方式';
  153. case 'ZQCT':
  154. return '脏器、查体';
  155. case 'FZJC':
  156. return '辅助检查';
  157. case 'XCZYWT':
  158. return '现存主要问题';
  159. case 'ZYYYFMYGHYFJZS':
  160. return '住院、用药、非免疫规划预防接种史';
  161. case 'JKPJJZD':
  162. return '健康评价及指导';
  163. default:
  164. return "";
  165. }
  166. }
  167. String examStateTransition(ExamStateEnum state) {
  168. switch (state) {
  169. case ExamStateEnum.Unchecked:
  170. return "未体检";
  171. case ExamStateEnum.Invalid:
  172. return "已作废";
  173. case ExamStateEnum.Inspected:
  174. return "体检中";
  175. default:
  176. return "";
  177. }
  178. }
  179. MaterialColor examStateColors(ExamStateEnum state) {
  180. switch (state) {
  181. case ExamStateEnum.Unchecked:
  182. return Colors.grey;
  183. case ExamStateEnum.Invalid:
  184. return Colors.red;
  185. case ExamStateEnum.Inspected:
  186. return Colors.green;
  187. default:
  188. return Colors.blue;
  189. }
  190. }
  191. void _openExamDetail(
  192. String initialData,
  193. String jsonData,
  194. ) {
  195. // 将 JSON 字符串作为参数传递给 evaluateJavascript 方法
  196. Get.dialog(Column(
  197. children: [
  198. Container(
  199. width: 900,
  200. alignment: Alignment.centerRight,
  201. padding: const EdgeInsets.only(right: 20, top: 10),
  202. color: Colors.white,
  203. child: IconButton(
  204. onPressed: () {
  205. Get.back();
  206. },
  207. icon: const Icon(
  208. Icons.close,
  209. ),
  210. ),
  211. ),
  212. Expanded(
  213. child: Container(
  214. color: Colors.white,
  215. width: 900,
  216. height: double.maxFinite,
  217. child: _buildWebView(initialData, jsonData),
  218. ),
  219. ),
  220. ],
  221. ));
  222. }
  223. Widget _buildAppWebView(
  224. String initialData,
  225. String jsonData,
  226. ) {
  227. return InAppWebView(
  228. initialData: InAppWebViewInitialData(
  229. data: '<body style="padding:30px 100px;">$initialData </body>',
  230. mimeType: 'text/html',
  231. encoding: 'utf-8',
  232. ),
  233. onWebViewCreated: (controller) {
  234. controller.evaluateJavascript(source: "window.globalData = $jsonData;");
  235. },
  236. onConsoleMessage: (controller, consoleMessage) {
  237. // print(consoleMessage);
  238. },
  239. );
  240. }
  241. Widget _buildWebwebview(
  242. String initialData,
  243. String jsonData,
  244. ) {
  245. return WebViewX(
  246. height: MediaQuery.of(Get.context!).size.height,
  247. width: MediaQuery.of(Get.context!).size.width,
  248. initialSourceType: SourceType.html,
  249. initialContent:
  250. '<html><body style="padding:30px 100px;">$initialData </body></html>',
  251. javascriptMode: JavascriptMode.unrestricted,
  252. onWebResourceError: (p0) {},
  253. onWebViewCreated: (controller) async {
  254. await controller.evalRawJavascript(
  255. "window.globalData = $jsonData;",
  256. inGlobalContext: true,
  257. );
  258. },
  259. );
  260. // return FutureBuilder<String>(
  261. // future: _loadLocalHtml(context,
  262. // '<html><body style="padding: 100px;">${controller.state.templateContent}</body></html>'),
  263. // builder: (context, snapshot) {
  264. // if (!snapshot.hasData) {
  265. // return const Center(child: CircularProgressIndicator());
  266. // // return FCenter(child: FText("${i18nBook.common.loading.t}..."));
  267. // } else {
  268. // return WebViewX(
  269. // height: MediaQuery.of(Get.context!).size.height,
  270. // width: MediaQuery.of(Get.context!).size.width,
  271. // initialSourceType: SourceType.html,
  272. // initialContent:
  273. // '<html><body style="padding: 100px;">${controller.state.templateContent}</body></html>',
  274. // javascriptMode: JavascriptMode.unrestricted,
  275. // onWebResourceError: (p0) {},
  276. // onWebViewCreated: (controller) {
  277. // // webviewControllerManager.initWebviewController(controller);
  278. // },
  279. // );
  280. // }
  281. // },
  282. // );
  283. }
  284. Widget _buildWebView(
  285. String initialData,
  286. String jsonData,
  287. ) {
  288. if (kIsWeb) {
  289. return _buildWebwebview(initialData, jsonData);
  290. } else {
  291. return _buildAppWebView(initialData, jsonData);
  292. }
  293. }
  294. // void getExamLabelsByExamNoAsync(ResidentModel rowData) async {
  295. // List<String> barCodes = [];
  296. // var labels = await registrationManager.getExamLabelsByExamNoAsync(
  297. // physicalExamNumber: rowData.physicalExamNumber!);
  298. // // for (var element in labels.eaxmLabels!) {}
  299. // for (var i = 0; i < labels.eaxmLabels!.length; i++) {
  300. // final element = labels.eaxmLabels![i];
  301. // var barCode = "";
  302. // if (i == 0) {
  303. // barCode = await drawQRcode(element, rowData);
  304. // } else {
  305. // barCode = await drawBarcode(element, rowData);
  306. // }
  307. // if (barCode.isNotEmpty) {
  308. // barCodes.add(barCode);
  309. // }
  310. // }
  311. // if (barCodes.length > 0) {
  312. // rpc.platform.printLabels(barCodes);
  313. // return;
  314. // }
  315. // }
  316. // Future<String> drawBarcode(
  317. // HealthExamLabelDTO labelDto, ResidentModel rowData) async {
  318. // const size = Size(360, 240);
  319. // final svg = Barcode.code128().toSvg(labelDto.uniquedCode!, height: 80);
  320. // final PictureInfo pictureInfo =
  321. // await vg.loadPicture(SvgStringLoader(svg), null);
  322. // var a = await _capturePainterToImage(
  323. // InfoPainter(labelDto, rowData), BarcodePainter(pictureInfo), size);
  324. // String base64Image = base64Encode(a!);
  325. // print(base64Image);
  326. // return base64Image;
  327. // }
  328. // Future<String> drawQRcode(
  329. // HealthExamLabelDTO labelDto, ResidentModel rowData) async {
  330. // const size = Size(360, 240);
  331. // final svg = Barcode.code128().toSvg(labelDto.uniquedCode!, height: 80);
  332. // final qrcode =
  333. // Barcode.qrCode().toSvg(labelDto.uniquedCode!, height: 50, width: 50);
  334. // final PictureInfo pictureInfo =
  335. // await vg.loadPicture(SvgStringLoader(svg), null);
  336. // final PictureInfo qrcodeInfo =
  337. // await vg.loadPicture(SvgStringLoader(qrcode), null);
  338. // var a = await _capturePainterToImage(
  339. // InfoPainter(labelDto, rowData), QrcodePainter(qrcodeInfo), size);
  340. // String base64Image = base64Encode(a!);
  341. // print(base64Image);
  342. // return base64Image;
  343. // }
  344. // /// 将CustomPainter绘制的内容转换为图片
  345. // Future<Uint8List?> _capturePainterToImage(
  346. // CustomPainter painter, CustomPainter codePainter, Size size) async {
  347. // final bounds = Offset.zero & size;
  348. // // final bounds2 = Offset.zero & Size(size.width - 20, size.height - 20);
  349. // final picture = PictureRecorder();
  350. // final pictureCanvas = Canvas(picture);
  351. // // 给Canvas设置绘制范围
  352. // pictureCanvas.clipRect(bounds);
  353. // painter.paint(pictureCanvas, size);
  354. // // 在Canvas上进行绘制
  355. // codePainter.paint(pictureCanvas, size);
  356. // // 在Canvas上进行绘制
  357. // // 结束绘制
  358. // final recordedPicture = picture.endRecording();
  359. // final image =
  360. // await recordedPicture.toImage(size.width.toInt(), size.height.toInt());
  361. // // 转换为字节数组
  362. // final byteData = await image.toByteData(format: ImageByteFormat.png);
  363. // final bytes = byteData?.buffer.asUint8List();
  364. // return bytes;
  365. // }
  366. Future<void> getRegisterInfoPage({
  367. int? pageSize = 10,
  368. int? pageIndex = 1,
  369. String? keyword = "",
  370. DateTime? startTime,
  371. DateTime? endTime,
  372. }) async {
  373. tableLoading = true;
  374. currPageIndex = pageIndex!;
  375. var result = await registrationManager.getRegisterInfoPageAsync(
  376. pageSize: pageSize,
  377. pageIndex: pageIndex,
  378. keyword: keyword,
  379. startTime: startTime,
  380. endTime: endTime,
  381. );
  382. List<ResidentModel> _residentList = [];
  383. if (result?.pageData != null) {
  384. for (RegisterInfoDTO i in result!.pageData!) {
  385. String statusStr = '';
  386. ExamStateEnum status = i.state;
  387. switch (status) {
  388. case ExamStateEnum.Unchecked:
  389. statusStr = "未体检";
  390. break;
  391. case ExamStateEnum.Invalid:
  392. statusStr = "已作废";
  393. break;
  394. case ExamStateEnum.Inspected:
  395. statusStr = "体检中";
  396. break;
  397. case ExamStateEnum.Reported:
  398. statusStr = "已报告";
  399. break;
  400. }
  401. _residentList.add(
  402. ResidentModel(
  403. name: i.name ?? '',
  404. idNumber: i.iDCardNo ?? '',
  405. code: i.code ?? '',
  406. homeAddress: i.adress,
  407. age: getAgeOfIdNumber(i.iDCardNo ?? ''),
  408. physicalExamNumber: i.physicalExamNumber,
  409. phone: i.phone,
  410. finishedExamKeys: i.finishedExamKeys,
  411. physicalExamStatus: statusStr,
  412. birthDay: i.birthday?.toLocal(),
  413. sex: i.sex,
  414. batchNumber: i.batchNumber,
  415. createTime: i.createTime?.toLocal(),
  416. ),
  417. );
  418. }
  419. appointmentModelListLength = result.totalCount;
  420. }
  421. residentList = _residentList;
  422. tableLoading = false;
  423. update(["HealCheckRecord"]);
  424. }
  425. String getAgeOfIdNumber(String idNumber) {
  426. if (idNumber.isEmpty) return '';
  427. final idCardRegex = RegExp(r'^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})(\d|X)$');
  428. final match = idCardRegex.firstMatch(idNumber);
  429. if (match != null) {
  430. final year = int.parse(match.group(2)!);
  431. final month = int.parse(match.group(3)!);
  432. final day = int.parse(match.group(4)!);
  433. final birthDate = DateTime(year, month, day);
  434. String age = DataTimeUtils.calculateAge(birthDate);
  435. return age;
  436. }
  437. return ''; // 返回一个默认值
  438. }
  439. }