import 'package:fis_jsonrpc/rpc.dart'; import 'package:fis_measure/interfaces/process/workspace/exam_info.dart'; import 'package:fis_measure/interfaces/process/workspace/measure_3d_view_controller.dart'; import 'package:fis_measure/process/language/measure_language.dart'; import 'package:fis_measure/process/layout/configuration.dart'; import 'package:fis_measure/process/workspace/measure_controller.dart'; import 'package:fis_measure/process/workspace/measure_data_controller.dart'; import 'package:fis_measure/process/workspace/measure_handler.dart'; import 'package:fis_measure/process/workspace/measure_3d_view_controller.dart'; import 'package:fis_measure/view/gesture/positioned_cursor.dart'; import 'package:fis_measure/view/measure/measure_config/widgets/measure_configuration_style.dart'; import 'package:fis_measure/view/measure/measure_images_bar.dart'; import 'package:fis_measure/view/measure/measure_player.dart'; import 'package:fis_measure/view/measure/measure_tool_panel.dart'; import 'package:fis_measure/view/measure/measure_view_controller.dart'; import 'package:fis_measure/view/player/control_board/operate_bar.dart'; import 'package:fis_ui/index.dart'; import 'package:fis_ui/interface/interactive_container.dart'; import 'package:fis_ui/widgets/layout/offstage.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; /// 测量主页面 class MeasureMainPage extends StatefulWidget implements FWidget, FInteractiveContainer { const MeasureMainPage( this.token, this.recordCode, this.patientCode, this.remedicalCode, { this.needRouterBack, this.enableLoopPlayback = false, this.onLoopPlaybackEnableChanged, this.remedicalAISelectedInfoCode, Key? key, }) : super(key: key); final String token; final String patientCode; final String remedicalCode; final String recordCode; final bool? needRouterBack; // 需要返回按钮【一版用于返回到报告编辑】 final bool enableLoopPlayback; final ValueCallback? onLoopPlaybackEnableChanged; final String? remedicalAISelectedInfoCode; @override final String pageName = 'MeasureMainPage'; @override State createState() => _MeasureMainPageState(); } class _MeasureMainPageState extends State { /// 病人及图像信息 String _curToken = ''; String _curPatientCode = ''; String _curRemedicalCode = ''; String _curRecordCode = ''; String? _curRemedicalAISelectedInfoCode = ''; bool? _curNeedRouterBack; /// 数据 final measureData = Get.find(); /// webview 控制器 final measure3DViewController = Get.find(); /// 测量项控制器 final measureMetaController = Get.put(MeasureMetaController()); /// 隐藏全屏loadding bool _hideFullScreenLoading = false; /// 图片loadding late bool imageLoaded = false; late final measureHandler = Get.find(); /// 测量控制器 late MeasureController measureController; /// 响应图片切换事件通知 void _onChangeImage(sender, e) { imageLoaded = e; setState(() {}); } /// 响应全屏状态切换事件 void _onChangeFullScreenState(sender, e) { setState(() {}); } /// 响应工具面板状态切换事件 void _onToolPanelStateChanged(sender, e) { setState(() {}); } /// 在此处动态刷新测量窗口,而不是重置窗口 void _onUpdateState(sender, Map parameters) async { _curNeedRouterBack = false; bool needUpdate = false; if (_curPatientCode == (parameters['patientCode'] ?? '')) { if (_curRemedicalCode == (parameters['remedicalCode'] ?? '')) { } else { measureHandler.changeImageByRemedicalCode .emit(this, parameters['remedicalCode'] ?? ''); } } else { needUpdate = true; } _curToken = parameters['token'] ?? ''; _curPatientCode = parameters['patientCode'] ?? ''; _curRemedicalCode = parameters['remedicalCode'] ?? ''; _curRecordCode = parameters['recordCode'] ?? ''; _curRemedicalAISelectedInfoCode = parameters['remedicalAISelectedInfoCode'] ?? ''; if (needUpdate) { await LayoutConfiguration.ins.loadData(); List remedicals = await _getCurRemedicals(); _initMouseModuel(); _initCarotidModuel(remedicals); measureData.remedicalList = remedicals; var remedicalInfo = await measureData.getImageInfo .call(_curRemedicalCode, _curToken, _curRemedicalAISelectedInfoCode); if (remedicalInfo != null) { measureData.aiResults = remedicalInfo.diagnosisResult ?? ''; if (remedicalInfo.terminalImages != null) { _hideFullScreenLoading = true; measureData.itemCurrentImage = measureData.chooseImageUrl(remedicalInfo.terminalImages!); getExamImageInfoList(remedicals); measureHandler.changeImageList.emit(this, remedicals); } } measureController.imageLoaded.removeListener(_onImageLoaded); measureController.imageLoaded.addListener(_onImageLoaded); } _setCurImageData(); } /// 将当前的图像信息同步到 measureData 中 void _setCurImageData() { measureData.measureImageData = MeasureImageData( patientCode: _curPatientCode, recordCode: _curRecordCode, remedicalCode: _curRemedicalCode, remedicalAISelectedInfoCode: _curRemedicalAISelectedInfoCode, ); } /// 先加载图像布局配置,然后加载全局数据 void _loadLayoutConfig() async { await LayoutConfiguration.ins.loadData(); setState(() { _initData(); }); } @override void initState() { _curToken = widget.token; _curPatientCode = widget.patientCode; _curRemedicalCode = widget.remedicalCode; _curRecordCode = widget.recordCode; _curNeedRouterBack = widget.needRouterBack; _curRemedicalAISelectedInfoCode = widget.remedicalAISelectedInfoCode; _hideFullScreenLoading = false; _loadLayoutConfig(); _setCurImageData(); measureData.curMeasureDataChanged.addListener(_onUpdateState); measureHandler.onChangeImageLoaded.addListener(_onChangeImage); measureHandler.onChangeFullScreenState .addListener(_onChangeFullScreenState); measureHandler.onToolPanelStateChanged .addListener(_onToolPanelStateChanged); super.initState(); } @override void dispose() { measureData.curMeasureDataChanged.removeListener(_onUpdateState); measureHandler.onChangeImageLoaded.removeListener(_onChangeImage); measureHandler.onChangeFullScreenState .removeListener(_onChangeFullScreenState); measureHandler.onToolPanelStateChanged .removeListener(_onToolPanelStateChanged); super.dispose(); } @override FWidget build(BuildContext context) { FWidget body; if (!_hideFullScreenLoading) { const loadingWidget = FCenter(child: FCircularProgressIndicator()); body = FRow( children: const [ FExpanded( child: loadingWidget, ), ], ); } else { body = FColumn(children: [ FExpanded( child: FRow( children: [ FOffstage( child: const MeasureToolPanel(), offstage: measureHandler.fullScreenState || !measureHandler.toolPanelState), const FVerticalDivider(), FExpanded( child: FColumn( mainAxisSize: MainAxisSize.max, children: [ FExpanded( child: FStack( children: [ if (!imageLoaded) MeasureRightBoard( key: ValueKey(measureData.itemCurrentImage), ), if (imageLoaded) FContainer( color: Colors.black, child: const FCenter( child: FCircularProgressIndicator(), ), ), ], )), // if (!measureHandler.fullScreenState) const MeasureImagesBar(), ], ), ), ], ), ), FOffstage( child: FRow(children: [ FColumn(children: [ const OperateBar(), _curNeedRouterBack ?? false ? FContainer( width: 300, padding: const EdgeInsets.only(bottom: 20), child: FIconButton( businessParent: widget, name: "back", onPressed: () { Get.back(); }, icon: const FIcon( Icons.arrow_back, color: Colors.white, size: 36, ), ), ) : const FSizedBox() ]), const FExpanded(child: MeasureImagesBar()) ]), offstage: measureHandler.fullScreenState), ]); } return FCenter( child: FContainer( color: _hideFullScreenLoading ? const Color.fromRGBO(70, 70, 70, 1) : Colors.black, child: body, ), ); } /// 获取当前状态下的图像集 Future> _getCurRemedicals() async { List remedicals = []; var value = await measureData.getRemedicalList.call( _curPatientCode, _curRecordCode, _curToken, ); for (var remedicalItemList in value) { remedicals.addAll(remedicalItemList.remedicalList ?? []); } return remedicals; } /// 初始化颈动脉模块 /// [Carotid] ✅遍历出颈动脉信息列表,传给壳子 void _initCarotidModuel(List remedicals) { List ultra3DResourceInfos = []; for (var remedical in remedicals) { if (remedical.carotidResult != null) { ultra3DResourceInfos.add(Ultra3DResourceInfo(remedical.carotidResult!)); } } measure3DViewController.ultra3DResourceInfoList = ultra3DResourceInfos; measure3DViewController.recordId = _curRecordCode; measure3DViewController.notifyShellLoadAllModel(); } /// 初始化卡尺样式部分 Future _initMouseModuel() async { final mouseState = Get.put(MouseState()); final result = await measureData.getMeasureSystemSettingAsync() as MeasureSystemSettingDTO; measureData.measureSystemSetting = result; mouseState.cursorType = getMeasureSystemSettingCursorType(result.cursorType); mouseState.cursorSize = result.cursorSize.toDouble(); } /// 测量页全局数据首次初始化 void _initData() async { List remedicals = await _getCurRemedicals(); _initMouseModuel(); _initCarotidModuel(remedicals); measureData.remedicalList = remedicals; var remedicalInfo = await measureData.getImageInfo .call(_curRemedicalCode, _curToken, _curRemedicalAISelectedInfoCode); if (remedicalInfo != null) { measureData.aiResults = remedicalInfo.diagnosisResult ?? ''; if (remedicalInfo.terminalImages != null) { _hideFullScreenLoading = true; final imgInfo = remedicalInfo.terminalImages!; measureData.itemCurrentImage = measureData.chooseImageUrl(imgInfo); getExamImageInfoList(remedicals); } } measureController.imageLoaded.removeListener(_onImageLoaded); measureController.imageLoaded.addListener(_onImageLoaded); } /// 获取测量图片所需的图片组 并且写入控制器中 加载 void getExamImageInfoList(List remedicals) async { /// 检查中图片信息表 List examImageInfoList = remedicals.map((e) { final imgInfo = e.terminalImages!; final vidUrl = measureData.chooseImageUrl(imgInfo); return ExamImageInfo( vidUrl, imgInfo.previewUrl!, remedicalAISelectedInfoCode: e.remedicalCode, ); }).toList(); Get.delete(); measureController = Get.put(MeasureController( "", imagesFetchFunc: (code) async { return examImageInfoList; }, enableLoopPlayback: widget.enableLoopPlayback, onLoopPlaybackEnableChanged: widget.onLoopPlaybackEnableChanged, )); await measureController.load(); final selectedImageIndex = examImageInfoList.indexWhere( (element) => element.url == measureData.itemCurrentImage, ); if (selectedImageIndex > -1) { // 命中当前选择图像 measureController.examInfo.selectedImageIndex = selectedImageIndex; } // MeasureLanguage.load(measureData.measureLanguage); } /// vid 切换时会触发到这里 void _onImageLoaded(Object sender, ExamImageInfo? e) async { // measureHandler.changeImageLoaded = true; if (!mounted) return; final currentImage = measureData.remedicalList.firstWhereOrNull( (element) => element.terminalImages!.imageUrl == e!.url || element.terminalImages!.originImageUrl == e.url, ); if (currentImage != null) { /// 获取图片详细信息 var remedicalInfo = await measureData.getImageInfo( currentImage.remedicalCode ?? '', _curToken, e!.remedicalAISelectedInfoCode, ); if (remedicalInfo != null) { measureData.aiResults = remedicalInfo.diagnosisResult ?? ''; measure3DViewController.initParams(); /// ai 良恶性 判断是否有ai measureData.diagnosisConclusion = remedicalInfo.diagnosisConclusion; if (remedicalInfo.carotidResult != null) { /// [Carotid] ✅详情传入测量信息 measure3DViewController.carotidResult = remedicalInfo.carotidResult!; /// [Carotid] ✅需要在此通知 controller 存在颈动脉信息 measure3DViewController.exist3DData = true; measure3DViewController .handleChangeCarotid2DImage(remedicalInfo.recordCode!); } else { measure3DViewController.exist3DData = false; } // [Carotid] ✅只要更换图片都要切换到Vid 2D模式 if (measure3DViewController.curMeasureMode != MeasureMode.vidMode) { measure3DViewController.backToVidMode(); } try { if (e != null) { Future.delayed(const Duration(milliseconds: 100), () { measureController.playerController.play(); }); setState(() { _hideFullScreenLoading = true; measureHandler.changeImageLoaded = false; }); } } catch (error) { setState(() { _hideFullScreenLoading = true; measureHandler.changeImageLoaded = false; }); } } } } }