123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480 |
- import 'dart:developer';
- import 'package:fis_common/index.dart';
- 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/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_data_helper.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: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,
- this.customWidgets = const [],
- this.hasQualityControlPermission = false,
- this.dispose,
- 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<bool>? onLoopPlaybackEnableChanged;
- final String? remedicalAISelectedInfoCode;
- final List<FWidget> customWidgets;
- final bool hasQualityControlPermission;
- final void Function()? dispose;
- @override
- final String pageName = 'MeasureMainPage';
- @override
- State<StatefulWidget> createState() => _MeasureMainPageState();
- }
- class _MeasureMainPageState extends State<MeasureMainPage> {
- /// 病人及图像信息
- // String _curToken = '';
- String _curPatientCode = '';
- String _curRemedicalCode = '';
- String _curRecordCode = '';
- String? _curRemedicalAISelectedInfoCode = '';
- bool? _curNeedRouterBack;
- /// 数据
- final measureData = Get.find<MeasureDataController>();
- /// webview 控制器
- final measure3DViewController = Get.find<Measure3DViewController>();
- /// 测量项控制器
- final measureMetaController = Get.put(MeasureMetaController());
- double get devicePixelRatio =>
- kIsMobile ? 1 : MediaQuery.of(context).devicePixelRatio;
- /// 隐藏全屏loadding
- bool _hideFullScreenLoading = false;
- /// 图片loadding
- late bool imageLoading = false;
- late final measureHandler = Get.find<MeasureHandler>();
- /// 测量控制器
- late MeasureController measureController;
- /// 响应图片切换事件通知
- void _onNewImageLoadStatueChanged(sender, e) {
- imageLoading = e;
- setState(() {});
- }
- /// 响应全屏状态切换事件
- void _onChangeFullScreenState(sender, e) {
- setState(() {});
- }
- /// 响应工具面板状态切换事件
- void _onToolPanelStateChanged(sender, e) {
- setState(() {});
- }
- /// 在此处动态刷新测量窗口,而不是重置窗口
- void _onUpdateState(sender, Map<String, String> parameters) async {
- _curNeedRouterBack = false;
- bool needUpdate = false;
- if (_curRecordCode == (parameters['recordCode'] ?? '')) {
- 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<RemedicalInfoDTO> remedicals = await _getCurRemedicals();
- _initMouseModuel();
- _initCarotidModuel(remedicals);
- measureData.remedicalList = remedicals;
- var remedicalInfo = await MeasureDataHelper.getImageInfo
- .call(_curRemedicalCode, _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.measureInfoData = MeasureInfoData(
- patientCode: _curPatientCode,
- recordCode: _curRecordCode,
- remedicalCode: _curRemedicalCode,
- remedicalAISelectedInfoCode: _curRemedicalAISelectedInfoCode,
- );
- }
- /// 先加载图像布局配置,然后加载全局数据
- void _loadLayoutConfig() async {
- await LayoutConfiguration.ins.loadData();
- setState(() {
- _initData();
- });
- }
- @override
- void initState() {
- _curPatientCode = widget.patientCode;
- _curRemedicalCode = widget.remedicalCode;
- _curRecordCode = widget.recordCode;
- _curNeedRouterBack = widget.needRouterBack;
- _curRemedicalAISelectedInfoCode = widget.remedicalAISelectedInfoCode;
- _hideFullScreenLoading = false;
- _loadLayoutConfig();
- _setCurImageData();
- measureData.curMeasureDataChanged.addListener(_onUpdateState);
- measureHandler.onNewImageLoadStatueChanged
- .addListener(_onNewImageLoadStatueChanged);
- measureHandler.onChangeFullScreenState
- .addListener(_onChangeFullScreenState);
- measureHandler.onToolPanelStateChanged
- .addListener(_onToolPanelStateChanged);
- super.initState();
- }
- @override
- void dispose() {
- measureData.curMeasureDataChanged.removeListener(_onUpdateState);
- measureHandler.onNewImageLoadStatueChanged
- .removeListener(_onNewImageLoadStatueChanged);
- measureHandler.onChangeFullScreenState
- .removeListener(_onChangeFullScreenState);
- measureHandler.onToolPanelStateChanged
- .removeListener(_onToolPanelStateChanged);
- widget.dispose?.call();
- 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: MeasureToolPanel(
- customWidgets: widget.customWidgets,
- ),
- offstage: measureHandler.fullScreenState ||
- !measureHandler.toolPanelState),
- const FVerticalDivider(),
- FExpanded(
- child: FColumn(
- mainAxisSize: MainAxisSize.max,
- children: [
- FExpanded(
- child: FStack(
- children: [
- if (!imageLoading)
- MeasureRightBoard(
- key: ValueKey(measureData.itemCurrentImage),
- ),
- if (imageLoading)
- 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 / devicePixelRatio,
- padding:
- EdgeInsets.only(bottom: 20 / devicePixelRatio),
- child: FIconButton(
- businessParent: widget,
- name: "back",
- onPressed: () {
- Get.back();
- },
- icon: const FIcon(
- Icons.arrow_back,
- color: Colors.white,
- size: 36,
- ),
- ),
- )
- : const FSizedBox()
- ],
- ),
- FExpanded(
- child: MeasureImagesBar(
- hasQualityControlPermission:
- widget.hasQualityControlPermission,
- ),
- ),
- ]),
- offstage: measureHandler.fullScreenState),
- ]);
- }
- return FCenter(
- child: FContainer(
- color: _hideFullScreenLoading
- ? const Color.fromRGBO(70, 70, 70, 1)
- : Colors.black,
- child: body,
- ),
- );
- }
- /// 获取当前状态下的图像集
- Future<List<RemedicalInfoDTO>> _getCurRemedicals() async {
- List<RemedicalInfoDTO> remedicals = [];
- var value = await MeasureDataHelper.getRemedicalList(
- _curPatientCode,
- _curRecordCode,
- );
- for (var remedicalItemList in value) {
- remedicals.addAll(remedicalItemList.remedicalList ?? []);
- }
- return remedicals;
- }
- /// 初始化颈动脉模块
- /// [Carotid] ✅遍历出颈动脉信息列表,传给壳子
- void _initCarotidModuel(List<RemedicalInfoDTO> remedicals) {
- List<Ultra3DResourceInfo> ultra3DResourceInfos = [];
- for (var remedical in remedicals) {
- if (remedical.carotidResult != null) {
- ultra3DResourceInfos.add(Ultra3DResourceInfo(remedical.carotidResult!));
- }
- }
- measure3DViewController.ultra3DResourceInfoList = ultra3DResourceInfos;
- measure3DViewController.recordId = _curRecordCode;
- measure3DViewController.notifyShellLoadAllModel();
- }
- /// 初始化卡尺样式部分
- Future<void> _initMouseModuel() async {
- final mouseState = Get.put<IMouseState>(MouseState());
- MeasureSystemSettingDTO result = MeasureSystemSettingDTO(
- cursorSize: 16,
- shapeCursorSize: 30,
- showResultWindow: true,
- fontSize: 14,
- showCursorLine: true,
- showDepthGuideline: true,
- showBriefAnnotation: true,
- autoSnapDistance: '20',
- minCursorDistance: '20',
- annotationFontSize: 14,
- distanceUnit: Unit.cm,
- areaUnit: Unit.cm2,
- velocityUnit: Unit.cms,
- timeUnit: Unit.s,
- );
- try {
- result = await MeasureDataHelper.getMeasureSystemSettingAsync();
- } catch (e) {
- log("getMeasureSystemSettingAsync error: $e");
- }
- measureData.measureSystemSetting = result;
- measureData.checkErrorUnitConfig();
- measureData.checkErrorStyleConfig();
- mouseState.cursorType =
- getMeasureSystemSettingCursorType(result.cursorType);
- mouseState.cursorSize = result.cursorSize.toDouble();
- }
- /// 测量页全局数据首次初始化
- void _initData() async {
- List<RemedicalInfoDTO> remedicals = await _getCurRemedicals();
- _initMouseModuel();
- _initCarotidModuel(remedicals);
- measureData.remedicalList = remedicals;
- var remedicalInfo = await MeasureDataHelper.getImageInfo(
- _curRemedicalCode, _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<RemedicalInfoDTO> remedicals) async {
- /// 检查中图片信息表
- List<ExamImageInfo> examImageInfoList = remedicals.map((e) {
- final imgInfo = e.terminalImages!;
- final vidUrl = measureData.chooseImageUrl(imgInfo);
- return ExamImageInfo(
- vidUrl,
- imgInfo.previewUrl!,
- remedicalCode: e.remedicalCode,
- );
- }).toList();
- Get.delete<MeasureController>();
- 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 MeasureDataHelper.getImageInfo(
- currentImage.remedicalCode ?? '',
- e!.remedicalCode,
- );
- if (remedicalInfo != null) {
- measureData.aiResults = remedicalInfo.diagnosisResult ?? '';
- measure3DViewController.initParams();
- /// ai 良恶性 判断是否有ai
- measureData.diagnosisConclusion = remedicalInfo.diagnosisConclusion;
- var surfaceFile = remedicalInfo.carotidResult?.surfaceFile;
- var cDNMdlFile = remedicalInfo.carotidResult?.cDNMdlFile;
- if (remedicalInfo.carotidResult != null &&
- surfaceFile.isNotNullOrEmpty &&
- cDNMdlFile.isNotNullOrEmpty) {
- /// [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.newImageLoading = false;
- });
- }
- } catch (error) {
- setState(() {
- _hideFullScreenLoading = true;
- measureHandler.newImageLoading = false;
- });
- }
- }
- }
- }
- }
|