import 'dart:convert'; import 'package:fis_common/index.dart'; import 'package:fis_common/logger/logger.dart'; import 'package:fis_jsonrpc/rpc.dart'; import 'package:fis_jsonrpc/rpc.dart' as r; import 'package:fis_jsonrpc/rpc.dart'; import 'package:fis_lib_report/report/report_template_document.dart'; import 'package:fis_lib_report/report_info/report_info.dart'; import 'package:fis_ui/index.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_sms/flutter_sms.dart'; import 'package:fluwx/fluwx.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:vitalapp/architecture/utils/prompt_box.dart'; import 'package:vitalapp/pages/measure_home/controller.dart'; import 'package:vitalapp/pages/measure_home/view.dart'; import 'package:vitalapp/pages/report_edit/controller.dart'; import 'package:vitalapp/pages/report_edit/view.dart'; import 'package:vitalapp/pages/report_edit/widgets/report_share_dialog.dart'; import 'package:vitalapp/pages/report_preview/controller.dart'; import 'package:vitalapp/pages/report_preview/view.dart'; import 'package:vitalapp/pages/widgets/row_separator.dart'; import 'package:vitalapp/rpc.dart'; import 'interfaces/base_manager.dart'; import 'interfaces/models/report_element_info.dart'; import 'interfaces/models/report_tag_key.dart'; import 'interfaces/report.dart'; import 'package:fis_common/event/event_type.dart'; import 'package:fis_i18n/i18n.dart'; class ReportManager extends BaseManager implements IReportManager { final onEditReport = FEventHandler(); ///提交报告事件 final onSubmitReport = FEventHandler(); ///手机端提交报告 final onMobileSubmitReport = FEventHandler(); ///直接打印文档 @override Future directlyPrintPDF(String reportCode, String fileName) async { try { await _initPreviewReport(reportCode); try {} catch (e) { logger.e("print report error:", e); } } catch (e) { logger.e('InspectionReportController _initPreviewReport ex:', e); } } /// 直接打印图片 @override Future directlyPrintPDFJpges( List? images, String fileName) async { try {} catch (e) { logger.e("print report error:", e); } } ///打印文件(通过下载地址) @override Future directlyPrintPDFFile(String fileUrl, String fileName) async { try {} catch (e) { logger.e("print report error:", e); } } @override List convertReportData(String jsonData) { List result = []; final infos = jsonDecode(jsonData) as List; for (var element in infos) { result.add( FReportElementInfo( key: element['Key'], tagId: element['TagId'], name: element['Name'], value: element['Value'], ), ); } return result; } ///语音识别 Future commitASRInfoAsync(String token, String extension) async { try { var audioConvertResult = await rpc.aSR.commitASRInfoAsync( CommitASRInfoRequest( token: token, url: token, fileType: extension, ), ); return audioConvertResult.content ?? ''; } catch (e) { logger.e('ReportEditController commitASRInfoAsync ex:', e); } return ''; } ///直接下载图像PDF @override Future directlyDownloadPDFJpges( List? images, String fileName) async { try {} catch (e) { logger.e("export report error:", e); } } ///直接下载 PDF 文件,通过 URL @override Future directlyDownloadPDFFile(String fileUrl, String fileName) async { try {} catch (e) { logger.e("export report error:", e); } } ///直接下载文档报告 @override Future directlyDownloadPDF(String reportCode, String fileName) async { try { await _initPreviewReport(reportCode); try {} catch (e) { logger.e("export report error:", e); } } catch (e) { logger.e('InspectionReportController _initPreviewReport ex:', e); } } ///获取默认模板 @override Future getReportTemplate() async { return ''; } /// 打开报告分享窗口 /// /// [reportUrlToShare] 待分享的报告code @override Future openReportShareDialog(String? reportToShare) async { if (reportToShare == null || reportToShare.isEmpty) { return false; } var reportUrlToShare = await rpc.report.findReportShareUrlAsync(FindReportShareUrlRequest( languageCode: 'zh-CN', reportCode: reportToShare, token: token, )); if (reportUrlToShare.isEmpty) { PromptBox.toast("报告分享地址为空"); return false; } Get.dialog( ReportShareDialog( reportUrlToShare, reportToShare, ), ); return true; } ///提交报告标签 Future submitReportTags( String reportCode, List selectedTags) async { try { final result = await rpc.report.modifyReportLabelsAsync(ModifyReportLabelsRequest( token: token, reportCode: reportCode, reportLabels: selectedTags, )); return result; } catch (e) { logger.e('AddReportTagController submitReportTags ex:', e); } return false; } ///初始化标签所需数据 /// /// [reportType] 默认值远程诊断 Future>> getReportTagDatas( String recordCode, [ ReportTypeEnum? reportType, ]) async { Map> allReportTags = {}; try { final labels = await rpc.report.findReportLabelsAsync( FindReportLabelsRequest( token: token, recordCode: recordCode, reportType: reportType ?? ReportTypeEnum.RemoteDiagnosis, ), ); labels.forEach((label) { final tagKey = ReportTagKey(label.reportLabelCode ?? '', label.labelName ?? ''); if (!allReportTags.containsKey(tagKey)) { allReportTags[tagKey] = label.labelItems ?? []; } else { logger.e( 'AddReportTagController _initDatas contains duplicate labels :' + tagKey.code); } }); } catch (e) { logger.e('ReportManager getReportTagDatas ex:', e); } return allReportTags; } @override Future> findReportsAsync(String recordCode) async { try { var reportInfo = await rpc.ultrasoundReport.vitalFindReportsAsync( FindReportsRequest( token: token, recordCode: recordCode, ), ); return reportInfo; } catch (e) { logger.e('ReportManager findReportsAsync ex:$e'); return []; } } ///获取报告提交内容信息Json String getMobileReportDatasJson({ String? patientName, String? patientSex, String? age, List? imageInfos, String? des, String? summary, String? creatorName, String? fullName, String? customDoctor, String? customOrganzation, String? deviceOrganzationName, }) { return ""; } /// 格式化时间 /// /// [time] 后端返回的时间 @override String getTime(String time) { String stringTime = ''; if (time.isNotEmpty) { stringTime = DateFormat("yyyy-MM-dd HH:mm:ss").format( DateTime.fromMillisecondsSinceEpoch( DateTime.tryParse(time)?.millisecondsSinceEpoch as int, ), ); } return stringTime; } ///打开报告编辑页面 @override Future openReportEdit( String patientCode, { String consultationCode = '', String recordCode = '', String reportCode = '', int patientTab = 0, String referralRecordCode = '', }) async { _openReportEditPage(token, patientCode, referralRecordCode, reportCode, consultationCode, recordCode); } /// 根据扫查记录获取转诊扫查图像详情 @override Future queryReferralRemedicalListByRecordInfoAsync( String recordCode, String referralRecordCode) async { try { final getRecordsPage = await rpc.remedical.queryReferralRemedicalListByRecordInfoAsync( QueryReferralExamReportRequest( token: token, recordCode: recordCode, referralRecordCode: referralRecordCode, ), ); return getRecordsPage; } catch (e) { return RemedicalListResult(); } } ///打开报告编辑页 void _openReportEditPage( String token, String patientCode, String referralRecordCode, String reportCode, String consultationCode, String recordCode) { var arguments = { 'token': token, 'patientCode': patientCode, 'referralRecordCode': referralRecordCode, 'reportCode': reportCode, 'consultationCode': consultationCode, 'recordCode': recordCode, }; Get.to( () => ReporteditPage(), binding: BindingsBuilder(() { Get.put(ReportEditController()); }), arguments: arguments, ); ///SP60全屏显示撰写报告 // router.to( // RouteNames.Remedical.ReportEdit, // id: NavIds.HOME, // parameters: arguments, // ); } ///进入测量页面 void enterVidMeasurePage({ String imageUrl = '', int imageindex = 0, String remedicalCode = '', String patientCode = '', String recordCode = '', String? remedicalAISelectedInfoCode = '', bool needRouterBack = false, bool reportPageEnter = false, }) { _openMeasurePage( patientCode, remedicalCode, recordCode, 0, remedicalAISelectedInfoCode, needRouterBack: needRouterBack, reportPageEnter: reportPageEnter, ); } @override Future getRemedicalListByRecordInfoAsync( String recordCode) async { try { final result = await rpc.remoteUltrasound.vitalGetRemedicalListByRecordInfoAsync( QueryRecordRequest( token: token, recordCode: recordCode, ), ); return result; } catch (e) { return RemedicalListResult(); } } ///注册微信分享api @override void initFluwx() async { try { if (kIsMobile) { await registerWxApi( appId: "wx2167274095118ddb", doOnAndroid: true, doOnIOS: true, universalLink: "https://flyinsono.com/", ); } } catch (e) { logger.e('ReportManager initFluwx', e); } } /// 获取测量图像 @override Future> findRemedicalMeasuredInfoAsync( String recordCode, { BusinessTypeEnum businessTypeEnum = BusinessTypeEnum.RemoteDiagnosis, }) async { List remedicalMeasured = []; try { final result = await rpc.remoteUltrasound.vitalFindRemedicalMeasuredInfoAsync( FindRemedicalMeasuredInfoRequest( token: token, recordCode: recordCode, businessType: businessTypeEnum, ), ); remedicalMeasured = result; } catch (e) { remedicalMeasured = []; printError( info: "findRemedicalMeasuredInfoAsync exception:" + e.toString()); logger.e("findRemedicalMeasuredInfoAsync exception:", e); } return remedicalMeasured; } ///跟据reportCode获取报告详情 @override Future findReportByCodeAsync(String reportCode) async { final reportInfo = await rpc.ultrasoundReport.vitalFindReportByCodeAsync( FindReportByCodeRequest( token: token, reportCode: reportCode, ), ); return reportInfo; } ///手机端底部弹窗选择分享方式 @override Future showBottomSheet(r.ReportDTO reportInfo) async { bool result = false; var reportCode = reportInfo.reportCode ?? ''; final url = await findReportShareUrlAsync(reportCode); String smsContent = "【杏聆荟】${reportInfo.patientName}的检查报告已生成,可点击${url}查看报告。"; smsContent = smsContent.replaceAll('#', '%23'); await showModalBottomSheet( context: Get.context!, backgroundColor: Colors.white, //背景颜色 shape: RoundedRectangleBorder( borderRadius: BorderRadius.only( topLeft: Radius.circular(8), topRight: Radius.circular(8))), isScrollControlled: false, isDismissible: true, builder: (BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, // 设置最小的弹出 children: [ ListTile( leading: Icon(Icons.wechat), title: Text('朋友圈'), onTap: () async { _shareWX(url, reportInfo.patientName); Get.back(); }, ), const RowSeparator(), ListTile( leading: const Icon(Icons.message), title: const Text("短信分享"), onTap: () async { _sendSMS(smsContent, []); Get.back(); }, ), const RowSeparator(), ListTile( leading: const Icon(Icons.copy), title: const Text('复制链接'), onTap: () { if (FPlatform.isWindows || FPlatform.isMacOS) { rpc.platform.copyToClipboard(url); } else { Clipboard.setData(ClipboardData(text: url)); } PromptBox.toast('复制成功'); Get.back(); }, ), ], ); }); return result; } ///微信分享 Future _shareWX(String url, String? patientName) async { var result = await isWeChatInstalled; if (!result) { PromptBox.toast('无法打开微信 请检查是否安装了微信'); return; } //分享后打开的图文连接 String linkUrl = url; //分享的小图片 String imageUrl = "https://flyinsono-bj-1300984704.cos.ap-beijing.myqcloud.com/FlyinsonoIcon.png"; /// 分享到好友 WeChatShareBaseModel model = WeChatShareWebPageModel( //链接 linkUrl, //标题 title: '超声报告', //描述 description: patientName ?? " ", //小图 thumbnail: WeChatImage.network(imageUrl), //微信消息 scene: WeChatScene.SESSION, ); final rst = await shareToWeChat(model); logger.i("ReportManager - share by wechat|$rst|$linkUrl"); } void _sendSMS(String message, List recipents) async { if (FPlatform.isAndroid) { // await launchUrl(Uri.parse('sms:?body=$message')); await launch('sms:?body=$message'); // 修正乱码问题 } else { String _result = await sendSMS(message: message, recipients: recipents) .catchError((onError) { print(onError); }); print(_result); } } @override Future> getReportInfoAsync(String recordCode) async { try { var result = await findReportsAsync(recordCode); return result; } catch (e) { logger.e('RealTimeConsultationReportController getFollowUpVisitInfo ex:' + e.toString()); return []; } } ///获取分享报告链接 @override Future findReportShareUrlAsync(String reportCode) async { var reportUrlToShare = await rpc.report.findReportShareUrlAsync( FindReportShareUrlRequest(reportCode: reportCode, token: token)); return reportUrlToShare; } ///打开报告预览页面 @override Future openReportPreviewPage( String recordCode, String referralRecordCode, { String reportCode = '', }) async { try { final reports = await findReportsAsync(recordCode); if (reports.isEmpty) { PromptBox.showToast( '暂无报告', maskType: EasyLoadingMaskType.none, // 设置为none以允许用户操作其他控件 ); return; } _openReportPreview(token, reportCode, recordCode); } catch (e) { printError( info: "getRemedicalListByRecordInfoAsync exception:" + e.toString()); logger.e("getRemedicalListByRecordInfoAsync exception:", e); } } void _openReportPreview(String token, String reportCode, String recordCode) { var arguments = { 'token': token, 'reportCode': reportCode, 'recordCode': recordCode, }; Get.to( () => ReportPreviewPage(), binding: BindingsBuilder(() { Get.put(ReportPreviewController()); }), arguments: arguments, ); } Future _initPreviewReport(String reportCode) async { try { if (reportCode.isNotEmpty) { final reportInfo = await rpc.ultrasoundReport.vitalFindReportByCodeAsync( FindReportByCodeRequest( token: token, reportCode: reportCode, ), ); final jsonStr = reportInfo.reportDatasJson; final measureJsonStr = reportInfo.reportMeasureDatasJson; if (jsonStr != null && jsonStr.isNotEmpty) { final jsonItems = jsonDecode(jsonStr) as List; if (measureJsonStr.isNotNullOrEmpty) { final measureJsonItems = jsonDecode(measureJsonStr!) as List; jsonItems.addAll(measureJsonItems); } var reportTemplate = reportInfo.reportTemplateJson; var reportTempalteDoc = ReportTemplateDocument.fromJson(jsonDecode(reportTemplate!)); FReportInfo.instance.init( reportTempalteDoc, DateTime.now(), "", revoke: i18nBook.common.revoke.t, selectEntry: i18nBook.remedical.selectWord.t, selectImageHint: i18nBook.remedical.clickAndSelectImage.t, ); FReportInfo.instance.fromJson(jsonItems); } } } catch (e) { print(e); } } void _openMeasurePage( String patientCode, String remedicalCode, String recordCode, int type, String? remedicalAISelectedInfoCode, { bool needRouterBack = false, bool reportPageEnter = false, }) async { var parasmeters = { "page": "measure", "patientCode": patientCode, "remedicalCode": remedicalCode, "recordCode": recordCode, "source": type.toString(), "token": token, "remedicalAISelectedInfoCode": remedicalAISelectedInfoCode ?? '', }; /// parasmeters 添加参数 needBack parasmeters["needRouterBack"] = needRouterBack.toString(); Get.to( () => MeasureHomePage(), binding: BindingsBuilder(() { Get.put(MeasureHomeController()); }), arguments: parasmeters, ); // await router.to( // RouteNames.Remedical.Measure, // id: (kIsMobile || (reportPageEnter && needRouterBack)) // ? null // : NavIds.HOME, // parameters: parasmeters, // ); } } // enum VidImageSource { // Remedical, // Consultation, // Laboratory, // AiResultModifier, // }