controller.dart 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. import 'dart:ui';
  2. import 'package:fis_jsonrpc/rpc.dart';
  3. import 'package:flutter/foundation.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter_printer_plus/flutter_printer_plus.dart'
  6. if (dart.library.html) "package:vitalapp/pages/medical_checkup_station/usb_print/web/flutter_printer_plus.dart";
  7. import 'package:get/get.dart';
  8. import 'package:vitalapp/managers/interfaces/appointment.dart';
  9. import 'package:vitalapp/managers/interfaces/patient.dart';
  10. import 'package:vitalapp/managers/interfaces/registration.dart';
  11. import 'package:vitalapp/pages/controllers/blue_location_mixin.dart';
  12. import 'package:vitalapp/pages/medical_checkup_station/registration/controller/form.dart';
  13. import 'package:vitalapp/pages/medical_checkup_station/registration/controller/list.dart';
  14. import 'package:vitalapp/pages/medical_checkup_station/registration/state/list.dart';
  15. import 'package:vitalapp/pages/medical_checkup_station/usb_print/module/printer_info.dart';
  16. import 'package:barcode_image/barcode_image.dart';
  17. import 'package:flutter_svg/flutter_svg.dart';
  18. /// 登记控制器
  19. class RegistrationController extends GetxController
  20. with BluetoothAndLocationMixin {
  21. late final RegistrationFormController formController;
  22. late final RegistrationListController listController;
  23. final appointmentManager = Get.find<IAppointmentManager>();
  24. final registrationManager = Get.find<IRegistrationManager>();
  25. final _patientManager = Get.find<IPatientManager>();
  26. final state = ListState();
  27. RegistrationController() {
  28. listController = RegistrationListController(this);
  29. formController = RegistrationFormController(this);
  30. }
  31. /// 登记
  32. ResidentModel resident = ResidentModel(
  33. idNumber: '',
  34. );
  35. /// 预约列表
  36. List<ResidentModel> residentList = [];
  37. /// 预约列表的总数
  38. int appointmentModelListLength = 0;
  39. /// 表格loading
  40. bool tableLoading = true;
  41. /// 当前表格页数
  42. int currPageIndex = 1;
  43. PrinterInfo? printInfo;
  44. List<Uint8List> barCodeList = [];
  45. //查询本地USB打印机列表
  46. Future<List<PrinterInfo>> queryLocalUSBPrinter() {
  47. return FlutterPrinterFinder.queryUsbPrinter().then(
  48. (value) => value.map((e) => PrinterInfo.fromUsbDevice(e)).toList(),
  49. );
  50. }
  51. _initData() async {
  52. var now = DateTime.now();
  53. listController.state.startTime = new DateTime(now.year, now.month, now.day);
  54. listController.state.endTime = listController.state.startTime!
  55. .add(Duration(hours: 23, minutes: 59, seconds: 59));
  56. await listController.getRegisterInfoPage();
  57. if (!kIsWeb) {
  58. List<PrinterInfo>? printerInfoList = await queryLocalUSBPrinter();
  59. if (printerInfoList.length == 0) {
  60. return;
  61. }
  62. printInfo = printerInfoList.first;
  63. }
  64. }
  65. Future<void> getExamLabelsByExamNoAsync(ResidentModel rowData) async {
  66. // await drawBarcode(HealthExamLabelDTO(uniquedCode: "123"), rowData);
  67. // Future.delayed(Duration(milliseconds: 2000));
  68. var labels = await registrationManager.getExamLabelsByExamNoAsync(
  69. physicalExamNumber: rowData.physicalExamNumber!);
  70. barCodeList.clear();
  71. for (var i = 0; i < labels.eaxmLabels!.length; i++) {
  72. final element = labels.eaxmLabels![i];
  73. if (i == 0) {
  74. var qrCode = await drawQRcode(element, rowData);
  75. barCodeList.add(qrCode);
  76. continue;
  77. }
  78. var barCode = Uint8List(0);
  79. barCode = await drawBarcode(element, rowData);
  80. if (barCode.isNotEmpty) {
  81. barCodeList.add(barCode);
  82. }
  83. }
  84. }
  85. Future<Uint8List> drawBarcode(
  86. HealthExamLabelDTO labelDto, ResidentModel resident) async {
  87. const size = Size(360, 240);
  88. final svg = Barcode.code128().toSvg(
  89. labelDto.uniquedCode!,
  90. width: 350,
  91. height: 120,
  92. fontFamily: "NotoSansSC",
  93. );
  94. final PictureInfo pictureInfo =
  95. await vg.loadPicture(SvgStringLoader(svg), null);
  96. var a = await _capturePainterToImage(
  97. InfoPainter(labelDto, resident), BarcodePainter(pictureInfo), size);
  98. return a!;
  99. }
  100. Future<Uint8List> drawQRcode(
  101. HealthExamLabelDTO labelDto, ResidentModel resident) async {
  102. const size = Size(360, 240);
  103. final svg = Barcode.qrCode(
  104. typeNumber: 1,
  105. errorCorrectLevel: BarcodeQRCorrectionLevel.low,
  106. ).toSvg(
  107. labelDto.uniquedCode!,
  108. width: 190,
  109. height: 190,
  110. fontFamily: "NotoSansSC",
  111. );
  112. final PictureInfo pictureInfo =
  113. await vg.loadPicture(SvgStringLoader(svg), null);
  114. var a = await _capturePainterToImage(
  115. QRInfoPainter(labelDto, resident), QrcodePainter(pictureInfo), size);
  116. return a!;
  117. }
  118. /// 将CustomPainter绘制的内容转换为图片
  119. Future<Uint8List?> _capturePainterToImage(
  120. CustomPainter painter, CustomPainter codePainter, Size size) async {
  121. final bounds = Offset.zero & size;
  122. // final bounds2 = Offset.zero & Size(size.width - 20, size.height - 20);
  123. final picture = PictureRecorder();
  124. final pictureCanvas = Canvas(picture);
  125. // 给Canvas设置绘制范围
  126. pictureCanvas.clipRect(bounds);
  127. painter.paint(pictureCanvas, size);
  128. // 在Canvas上进行绘制
  129. codePainter.paint(pictureCanvas, size);
  130. // 结束绘制
  131. final recordedPicture = picture.endRecording();
  132. final image =
  133. await recordedPicture.toImage(size.width.toInt(), size.height.toInt());
  134. // 转换为字节数组
  135. final byteData = await image.toByteData(format: ImageByteFormat.png);
  136. final bytes = byteData?.buffer.asUint8List();
  137. return bytes;
  138. }
  139. void onTap() {}
  140. @override
  141. void onReady() {
  142. _initData();
  143. super.onReady();
  144. }
  145. // void on
  146. @override
  147. void onClose() {}
  148. /// 根据身份证号获取居民信息
  149. Future<PatientDTO?> getPatientByID(String idNum,
  150. [bool isValidOperationDoctor = true]) async {
  151. final patient = await _patientManager.getDetail(
  152. idNum,
  153. isValidOperationDoctor: isValidOperationDoctor,
  154. );
  155. return patient;
  156. }
  157. }
  158. class InfoPainter extends CustomPainter {
  159. HealthExamLabelDTO labelDto;
  160. ResidentModel resident;
  161. InfoPainter(this.labelDto, this.resident);
  162. @override
  163. void paint(Canvas canvas, Size size) {
  164. canvas.save();
  165. Paint backgroundPaint = Paint()..color = Colors.white;
  166. canvas.drawRect(
  167. Rect.fromLTWH(0, 0, size.width, size.height), backgroundPaint);
  168. canvas.drawLine(
  169. Offset(0, 95), Offset(360, 95), Paint()..color = Colors.black);
  170. // Simulate drawing the content
  171. TextSpan titleSpan = TextSpan(
  172. text: '${labelDto.title ?? ''}',
  173. style: TextStyle(
  174. fontSize: 40,
  175. height: 1,
  176. color: Colors.black,
  177. fontFamily: "NotoSansSC",
  178. ),
  179. );
  180. TextPainter titleTp = TextPainter(
  181. text: titleSpan,
  182. textAlign: TextAlign.left,
  183. textDirection: TextDirection.ltr,
  184. );
  185. titleTp.layout();
  186. titleTp.paint(canvas, Offset(10, 8));
  187. TextSpan subTitleSpanName = TextSpan(
  188. text: '${resident.name} ',
  189. style: TextStyle(
  190. fontSize: 26,
  191. height: 1,
  192. color: Colors.black,
  193. fontFamily: "NotoSansSC",
  194. ),
  195. );
  196. TextPainter subTitleTp1 = TextPainter(
  197. text: subTitleSpanName,
  198. textAlign: TextAlign.left,
  199. textDirection: TextDirection.ltr,
  200. );
  201. subTitleTp1.layout();
  202. subTitleTp1.paint(canvas, Offset(10, 65));
  203. TextSpan subTitleSpanAge = TextSpan(
  204. text: '${resident.age} 岁',
  205. style: TextStyle(
  206. fontSize: 26,
  207. height: 1,
  208. color: Colors.black,
  209. fontFamily: "NotoSansSC",
  210. ),
  211. );
  212. TextPainter subTitleTpAge = TextPainter(
  213. text: subTitleSpanAge,
  214. textAlign: TextAlign.left,
  215. textDirection: TextDirection.ltr,
  216. );
  217. subTitleTpAge.layout();
  218. subTitleTpAge.paint(canvas, Offset(150, 65));
  219. TextSpan subTitleSpanGender = TextSpan(
  220. text: '${resident.sex}',
  221. style: TextStyle(
  222. fontSize: 26,
  223. height: 1,
  224. color: Colors.black,
  225. fontFamily: "NotoSansSC",
  226. ),
  227. );
  228. TextPainter subTitleTpGender = TextPainter(
  229. text: subTitleSpanGender,
  230. textAlign: TextAlign.left,
  231. textDirection: TextDirection.ltr,
  232. );
  233. subTitleTpGender.layout();
  234. subTitleTpGender.paint(canvas, Offset(300, 65));
  235. canvas.restore();
  236. }
  237. @override
  238. bool shouldRepaint(covariant CustomPainter oldDelegate) {
  239. return false;
  240. }
  241. }
  242. class BarcodePainter extends CustomPainter {
  243. PictureInfo pictureInfo;
  244. BarcodePainter(this.pictureInfo);
  245. @override
  246. void paint(Canvas canvas, Size size) {
  247. canvas.save();
  248. canvas.translate(5, 100);
  249. canvas.scale(1, 1);
  250. canvas.drawPicture(pictureInfo.picture);
  251. canvas.restore(); // 恢复之前保存的绘制状态
  252. }
  253. @override
  254. bool shouldRepaint(covariant CustomPainter oldDelegate) {
  255. return false;
  256. }
  257. }
  258. class QRInfoPainter extends CustomPainter {
  259. HealthExamLabelDTO labelDto;
  260. ResidentModel resident;
  261. QRInfoPainter(this.labelDto, this.resident);
  262. @override
  263. void paint(Canvas canvas, Size size) {
  264. canvas.save();
  265. Paint backgroundPaint = Paint()..color = Colors.white;
  266. canvas.drawRect(
  267. Rect.fromLTWH(0, 0, size.width, size.height), backgroundPaint);
  268. // canvas.drawLine(
  269. // Offset(0, 95), Offset(360, 95), Paint()..color = Colors.black);
  270. // Simulate drawing the content
  271. TextSpan titleSpan = TextSpan(
  272. text: '${labelDto.title ?? ''}',
  273. style: TextStyle(
  274. fontSize: 40,
  275. height: 1,
  276. color: Colors.black,
  277. fontFamily: "NotoSansSC",
  278. ),
  279. );
  280. TextPainter titleTp = TextPainter(
  281. text: titleSpan,
  282. textAlign: TextAlign.left,
  283. textDirection: TextDirection.ltr,
  284. );
  285. titleTp.layout();
  286. titleTp.paint(canvas, Offset(10, 8));
  287. TextSpan subTitleSpanName = TextSpan(
  288. text: '${resident.name} ',
  289. style: TextStyle(
  290. fontSize: 30,
  291. height: 1,
  292. color: Colors.black,
  293. fontFamily: "NotoSansSC",
  294. ),
  295. );
  296. TextPainter subTitleTp1 = TextPainter(
  297. text: subTitleSpanName,
  298. textAlign: TextAlign.left,
  299. textDirection: TextDirection.ltr,
  300. );
  301. subTitleTp1.layout();
  302. subTitleTp1.paint(canvas, Offset(10, 70));
  303. TextSpan subTitleSpanAge = TextSpan(
  304. text: '${resident.age} 岁',
  305. style: TextStyle(
  306. fontSize: 30,
  307. height: 1,
  308. color: Colors.black,
  309. fontFamily: "NotoSansSC",
  310. ),
  311. );
  312. TextPainter subTitleTpAge = TextPainter(
  313. text: subTitleSpanAge,
  314. textAlign: TextAlign.left,
  315. textDirection: TextDirection.ltr,
  316. );
  317. subTitleTpAge.layout();
  318. subTitleTpAge.paint(canvas, Offset(10, 120));
  319. TextSpan subTitleSpanGender = TextSpan(
  320. text: '${resident.sex}',
  321. style: TextStyle(
  322. fontSize: 30,
  323. height: 1,
  324. color: Colors.black,
  325. fontFamily: "NotoSansSC",
  326. ),
  327. );
  328. TextPainter subTitleTpGender = TextPainter(
  329. text: subTitleSpanGender,
  330. textAlign: TextAlign.left,
  331. textDirection: TextDirection.ltr,
  332. );
  333. subTitleTpGender.layout();
  334. subTitleTpGender.paint(canvas, Offset(10, 170));
  335. TextSpan uniqueCode = TextSpan(
  336. text: '${labelDto.uniquedCode}',
  337. style: TextStyle(
  338. fontSize: 24,
  339. height: 1,
  340. color: Colors.black,
  341. fontFamily: "NotoSansSC",
  342. ),
  343. );
  344. TextPainter uniqueCodePainter = TextPainter(
  345. text: uniqueCode,
  346. textAlign: TextAlign.left,
  347. textDirection: TextDirection.ltr,
  348. );
  349. uniqueCodePainter.layout();
  350. uniqueCodePainter.paint(canvas, Offset(150, 195));
  351. canvas.restore();
  352. }
  353. @override
  354. bool shouldRepaint(covariant CustomPainter oldDelegate) {
  355. return false;
  356. }
  357. }
  358. class QrcodePainter extends CustomPainter {
  359. PictureInfo pictureInfo;
  360. QrcodePainter(this.pictureInfo);
  361. @override
  362. void paint(Canvas canvas, Size size) {
  363. canvas.save();
  364. canvas.translate(150, 0);
  365. canvas.scale(1, 1);
  366. canvas.drawPicture(pictureInfo.picture);
  367. canvas.restore(); // 恢复之前保存的绘制状态
  368. }
  369. @override
  370. bool shouldRepaint(covariant CustomPainter oldDelegate) {
  371. return false;
  372. }
  373. }