import 'dart:ui'; import 'package:fis_jsonrpc/rpc.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_printer_plus/flutter_printer_plus.dart' if (dart.library.html) "package:vitalapp/pages/medical_checkup_station/usb_print/web/flutter_printer_plus.dart"; import 'package:get/get.dart'; import 'package:vitalapp/managers/interfaces/appointment.dart'; import 'package:vitalapp/managers/interfaces/patient.dart'; import 'package:vitalapp/managers/interfaces/registration.dart'; import 'package:vitalapp/pages/controllers/blue_location_mixin.dart'; import 'package:vitalapp/pages/medical_checkup_station/registration/controller/form.dart'; import 'package:vitalapp/pages/medical_checkup_station/registration/controller/list.dart'; import 'package:vitalapp/pages/medical_checkup_station/registration/state/list.dart'; import 'package:vitalapp/pages/medical_checkup_station/usb_print/module/printer_info.dart'; import 'package:barcode_image/barcode_image.dart'; import 'package:flutter_svg/flutter_svg.dart'; /// 登记控制器 class RegistrationController extends GetxController with BluetoothAndLocationMixin { late final RegistrationFormController formController; late final RegistrationListController listController; final appointmentManager = Get.find(); final registrationManager = Get.find(); final _patientManager = Get.find(); final state = ListState(); RegistrationController() { listController = RegistrationListController(this); formController = RegistrationFormController(this); } /// 登记 ResidentModel resident = ResidentModel( idNumber: '', ); /// 预约列表 List residentList = []; /// 预约列表的总数 int appointmentModelListLength = 0; /// 表格loading bool tableLoading = true; /// 当前表格页数 int currPageIndex = 1; PrinterInfo? printInfo; List barCodeList = []; //查询本地USB打印机列表 Future> queryLocalUSBPrinter() { return FlutterPrinterFinder.queryUsbPrinter().then( (value) => value.map((e) => PrinterInfo.fromUsbDevice(e)).toList(), ); } _initData() async { var now = DateTime.now(); listController.state.startTime = new DateTime(now.year, now.month, now.day); listController.state.endTime = listController.state.startTime! .add(Duration(hours: 23, minutes: 59, seconds: 59)); await listController.getRegisterInfoPage(); if (!kIsWeb) { List? printerInfoList = await queryLocalUSBPrinter(); if (printerInfoList.length == 0) { return; } printInfo = printerInfoList.first; } } Future getExamLabelsByExamNoAsync(ResidentModel rowData) async { // await drawBarcode(HealthExamLabelDTO(uniquedCode: "123"), rowData); // Future.delayed(Duration(milliseconds: 2000)); var labels = await registrationManager.getExamLabelsByExamNoAsync( physicalExamNumber: rowData.physicalExamNumber!); barCodeList.clear(); for (var i = 0; i < labels.eaxmLabels!.length; i++) { final element = labels.eaxmLabels![i]; if (i == 0) { var qrCode = await drawQRcode(element, rowData); barCodeList.add(qrCode); continue; } var barCode = Uint8List(0); barCode = await drawBarcode(element, rowData); if (barCode.isNotEmpty) { barCodeList.add(barCode); } } } Future drawBarcode( HealthExamLabelDTO labelDto, ResidentModel resident) async { const size = Size(360, 240); final svg = Barcode.code128().toSvg( labelDto.uniquedCode!, width: 350, height: 120, fontFamily: "NotoSansSC", ); final PictureInfo pictureInfo = await vg.loadPicture(SvgStringLoader(svg), null); var a = await _capturePainterToImage( InfoPainter(labelDto, resident), BarcodePainter(pictureInfo), size); return a!; } Future drawQRcode( HealthExamLabelDTO labelDto, ResidentModel resident) async { const size = Size(360, 240); final svg = Barcode.qrCode( typeNumber: 1, errorCorrectLevel: BarcodeQRCorrectionLevel.low, ).toSvg( labelDto.uniquedCode!, width: 190, height: 190, fontFamily: "NotoSansSC", ); final PictureInfo pictureInfo = await vg.loadPicture(SvgStringLoader(svg), null); var a = await _capturePainterToImage( QRInfoPainter(labelDto, resident), QrcodePainter(pictureInfo), size); return a!; } /// 将CustomPainter绘制的内容转换为图片 Future _capturePainterToImage( CustomPainter painter, CustomPainter codePainter, Size size) async { final bounds = Offset.zero & size; // final bounds2 = Offset.zero & Size(size.width - 20, size.height - 20); final picture = PictureRecorder(); final pictureCanvas = Canvas(picture); // 给Canvas设置绘制范围 pictureCanvas.clipRect(bounds); painter.paint(pictureCanvas, size); // 在Canvas上进行绘制 codePainter.paint(pictureCanvas, size); // 结束绘制 final recordedPicture = picture.endRecording(); final image = await recordedPicture.toImage(size.width.toInt(), size.height.toInt()); // 转换为字节数组 final byteData = await image.toByteData(format: ImageByteFormat.png); final bytes = byteData?.buffer.asUint8List(); return bytes; } void onTap() {} @override void onReady() { _initData(); super.onReady(); } // void on @override void onClose() {} /// 根据身份证号获取居民信息 Future getPatientByID(String idNum, [bool isValidOperationDoctor = true]) async { final patient = await _patientManager.getDetail( idNum, isValidOperationDoctor: isValidOperationDoctor, ); return patient; } } class InfoPainter extends CustomPainter { HealthExamLabelDTO labelDto; ResidentModel resident; InfoPainter(this.labelDto, this.resident); @override void paint(Canvas canvas, Size size) { canvas.save(); Paint backgroundPaint = Paint()..color = Colors.white; canvas.drawRect( Rect.fromLTWH(0, 0, size.width, size.height), backgroundPaint); canvas.drawLine( Offset(0, 95), Offset(360, 95), Paint()..color = Colors.black); // Simulate drawing the content TextSpan titleSpan = TextSpan( text: '${labelDto.title ?? ''}', style: TextStyle( fontSize: 40, height: 1, color: Colors.black, fontFamily: "NotoSansSC", ), ); TextPainter titleTp = TextPainter( text: titleSpan, textAlign: TextAlign.left, textDirection: TextDirection.ltr, ); titleTp.layout(); titleTp.paint(canvas, Offset(10, 8)); TextSpan subTitleSpanName = TextSpan( text: '${resident.name} ', style: TextStyle( fontSize: 26, height: 1, color: Colors.black, fontFamily: "NotoSansSC", ), ); TextPainter subTitleTp1 = TextPainter( text: subTitleSpanName, textAlign: TextAlign.left, textDirection: TextDirection.ltr, ); subTitleTp1.layout(); subTitleTp1.paint(canvas, Offset(10, 65)); TextSpan subTitleSpanAge = TextSpan( text: '${resident.age} 岁', style: TextStyle( fontSize: 26, height: 1, color: Colors.black, fontFamily: "NotoSansSC", ), ); TextPainter subTitleTpAge = TextPainter( text: subTitleSpanAge, textAlign: TextAlign.left, textDirection: TextDirection.ltr, ); subTitleTpAge.layout(); subTitleTpAge.paint(canvas, Offset(150, 65)); TextSpan subTitleSpanGender = TextSpan( text: '${resident.sex}', style: TextStyle( fontSize: 26, height: 1, color: Colors.black, fontFamily: "NotoSansSC", ), ); TextPainter subTitleTpGender = TextPainter( text: subTitleSpanGender, textAlign: TextAlign.left, textDirection: TextDirection.ltr, ); subTitleTpGender.layout(); subTitleTpGender.paint(canvas, Offset(300, 65)); canvas.restore(); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return false; } } class BarcodePainter extends CustomPainter { PictureInfo pictureInfo; BarcodePainter(this.pictureInfo); @override void paint(Canvas canvas, Size size) { canvas.save(); canvas.translate(5, 100); canvas.scale(1, 1); canvas.drawPicture(pictureInfo.picture); canvas.restore(); // 恢复之前保存的绘制状态 } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return false; } } class QRInfoPainter extends CustomPainter { HealthExamLabelDTO labelDto; ResidentModel resident; QRInfoPainter(this.labelDto, this.resident); @override void paint(Canvas canvas, Size size) { canvas.save(); Paint backgroundPaint = Paint()..color = Colors.white; canvas.drawRect( Rect.fromLTWH(0, 0, size.width, size.height), backgroundPaint); // canvas.drawLine( // Offset(0, 95), Offset(360, 95), Paint()..color = Colors.black); // Simulate drawing the content TextSpan titleSpan = TextSpan( text: '${labelDto.title ?? ''}', style: TextStyle( fontSize: 40, height: 1, color: Colors.black, fontFamily: "NotoSansSC", ), ); TextPainter titleTp = TextPainter( text: titleSpan, textAlign: TextAlign.left, textDirection: TextDirection.ltr, ); titleTp.layout(); titleTp.paint(canvas, Offset(10, 8)); TextSpan subTitleSpanName = TextSpan( text: '${resident.name} ', style: TextStyle( fontSize: 30, height: 1, color: Colors.black, fontFamily: "NotoSansSC", ), ); TextPainter subTitleTp1 = TextPainter( text: subTitleSpanName, textAlign: TextAlign.left, textDirection: TextDirection.ltr, ); subTitleTp1.layout(); subTitleTp1.paint(canvas, Offset(10, 70)); TextSpan subTitleSpanAge = TextSpan( text: '${resident.age} 岁', style: TextStyle( fontSize: 30, height: 1, color: Colors.black, fontFamily: "NotoSansSC", ), ); TextPainter subTitleTpAge = TextPainter( text: subTitleSpanAge, textAlign: TextAlign.left, textDirection: TextDirection.ltr, ); subTitleTpAge.layout(); subTitleTpAge.paint(canvas, Offset(10, 120)); TextSpan subTitleSpanGender = TextSpan( text: '${resident.sex}', style: TextStyle( fontSize: 30, height: 1, color: Colors.black, fontFamily: "NotoSansSC", ), ); TextPainter subTitleTpGender = TextPainter( text: subTitleSpanGender, textAlign: TextAlign.left, textDirection: TextDirection.ltr, ); subTitleTpGender.layout(); subTitleTpGender.paint(canvas, Offset(10, 170)); TextSpan uniqueCode = TextSpan( text: '${labelDto.uniquedCode}', style: TextStyle( fontSize: 24, height: 1, color: Colors.black, fontFamily: "NotoSansSC", ), ); TextPainter uniqueCodePainter = TextPainter( text: uniqueCode, textAlign: TextAlign.left, textDirection: TextDirection.ltr, ); uniqueCodePainter.layout(); uniqueCodePainter.paint(canvas, Offset(150, 195)); canvas.restore(); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return false; } } class QrcodePainter extends CustomPainter { PictureInfo pictureInfo; QrcodePainter(this.pictureInfo); @override void paint(Canvas canvas, Size size) { canvas.save(); canvas.translate(150, 0); canvas.scale(1, 1); canvas.drawPicture(pictureInfo.picture); canvas.restore(); // 恢复之前保存的绘制状态 } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return false; } }