measure_view.dart 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. import 'package:fis_i18n/i18n.dart';
  2. import 'package:fis_jsonrpc/rpc.dart';
  3. import 'package:fis_measure/interfaces/enums/annotation.dart';
  4. import 'package:fis_measure/interfaces/process/player/play_controller.dart';
  5. import 'package:fis_measure/interfaces/process/workspace/application.dart';
  6. import 'package:fis_measure/interfaces/process/workspace/exam_info.dart';
  7. import 'package:fis_measure/interfaces/process/workspace/measure_3d_view_controller.dart';
  8. import 'package:fis_measure/process/language/measure_language.dart';
  9. import 'package:fis_measure/process/layout/configuration.dart';
  10. import 'package:fis_measure/process/workspace/measure_controller.dart';
  11. import 'package:fis_measure/process/workspace/measure_data_controller.dart';
  12. import 'package:fis_measure/process/workspace/measure_handler.dart';
  13. import 'package:fis_measure/process/workspace/measure_3d_view_controller.dart';
  14. import 'package:fis_measure/view/cursor.dart';
  15. import 'package:fis_measure/view/gesture/positioned_cursor.dart';
  16. import 'package:fis_measure/view/measure/measure_images_bar.dart';
  17. import 'package:fis_measure/view/measure/measure_left_annotation.dart';
  18. import 'package:fis_measure/view/measure/measure_player.dart';
  19. import 'package:fis_measure/view/measure/measure_search_input.dart';
  20. import 'package:fis_measure/view/measure/measure_tool.dart';
  21. import 'package:fis_measure/view/measure/carotid_measure_tool.dart';
  22. import 'package:fis_measure/view/measure/measure_tools_title.dart';
  23. import 'package:fis_measure/view/measure/measure_view_controller.dart';
  24. import 'package:fis_measure/view/measure/tool_chest_title.dart';
  25. import 'package:fis_measure/view/player/control_board/operate_bar.dart';
  26. import 'package:fis_ui/index.dart';
  27. import 'package:flutter/material.dart';
  28. import 'package:get/get.dart';
  29. /// 测量主页面
  30. class MeasureMainPage extends StatefulWidget implements FWidget {
  31. const MeasureMainPage(
  32. this.token, this.recordCode, this.patientCode, this.remedicalCode,
  33. {Key? key})
  34. : super(key: key);
  35. final String token;
  36. final String patientCode;
  37. final String remedicalCode;
  38. final String recordCode;
  39. @override
  40. State<StatefulWidget> createState() => _MeasureMainPageState();
  41. }
  42. class _MeasureMainPageState extends State<MeasureMainPage> {
  43. /// 数据
  44. final measureData = Get.find<MeasureDataController>();
  45. /// webview 控制器
  46. final measure3DViewController = Get.find<Measure3DViewController>();
  47. final mouseState = Get.put<IMouseState>(MouseState());
  48. /// 测量项控制器
  49. final measureMetaController = Get.put(MeasureMetaController());
  50. /// 页面loadding
  51. bool loaded = false;
  52. // /// 图片loadding
  53. late bool imageLoaded = false;
  54. ///检查图片信息表
  55. List<ExamImageInfo> examImageInfoList = [];
  56. late final measureHandler = Get.find<MeasureHandler>();
  57. /// 测量控制器
  58. late MeasureController measureController = Get.put(MeasureController(
  59. "",
  60. imagesFetchFunc: (code) async {
  61. return examImageInfoList;
  62. },
  63. ));
  64. /// 获取测量图片所需的图片组 并且写入控制器中 加载
  65. void getExamImageInfoList(List<RemedicalInfoDTO> remedicals) async {
  66. for (var element in remedicals) {
  67. examImageInfoList.add(
  68. ExamImageInfo(
  69. element.terminalImages!.imageUrl!,
  70. element.terminalImages!.previewUrl!,
  71. ),
  72. );
  73. }
  74. measureController = Get.put(MeasureController(
  75. "",
  76. imagesFetchFunc: (code) async {
  77. return examImageInfoList;
  78. },
  79. ));
  80. await measureController.load();
  81. int selectedImageIndex = -1;
  82. if (examImageInfoList
  83. .any((element) => element.url == measureData.itemCurrentImage)) {
  84. ExamImageInfo selectedImage = examImageInfoList.firstWhere(
  85. (element) => element.url == measureData.itemCurrentImage,
  86. );
  87. selectedImageIndex = examImageInfoList.indexOf(selectedImage);
  88. measureController.examInfo.selectedImageIndex = selectedImageIndex;
  89. }
  90. MeasureLanguage.load(measureData.measureLanguage);
  91. }
  92. void changeImage(sender, e) {
  93. imageLoaded = e;
  94. setState(() {});
  95. }
  96. void changeFullScreenState(sender, e) {
  97. setState(() {});
  98. }
  99. @override
  100. void initState() {
  101. loadLayoutConfig();
  102. getImageInfo();
  103. measureHandler.onChangeImageLoaded.addListener(changeImage);
  104. measureHandler.onChangeFullScreenState.addListener(changeFullScreenState);
  105. super.initState();
  106. }
  107. void getImageInfo() {
  108. measureData.measureImageData = MeasureImageData(
  109. patientCode: widget.patientCode,
  110. recordCode: widget.recordCode,
  111. remedicalCode: widget.remedicalCode,
  112. );
  113. }
  114. /// 加载图像布局配置
  115. void loadLayoutConfig() async {
  116. await LayoutConfiguration.ins.loadData();
  117. setState(() {
  118. // 加载图像数据
  119. _initData();
  120. });
  121. }
  122. void onImageLoaded(Object sender, ExamImageInfo? e) async {
  123. // measureHandler.changeImageLoaded = true;
  124. if (!mounted) return;
  125. final currentImage = measureData.remedicalList.firstWhereOrNull(
  126. (element) => element.terminalImages!.imageUrl == e!.url,
  127. );
  128. if (currentImage != null) {
  129. /// 获取图片详细信息
  130. var remedicalInfo = await measureData.getImageInfo(
  131. currentImage.remedicalCode ?? '',
  132. widget.token,
  133. );
  134. if (remedicalInfo != null) {
  135. measureData.aiResults = remedicalInfo.diagnosisResult ?? '';
  136. measure3DViewController.initParams();
  137. /// ai 良恶性 判断是否有ai
  138. measureData.diagnosisConclusion = remedicalInfo.diagnosisConclusion;
  139. if (remedicalInfo.carotidResult != null) {
  140. /// [Carotid] ✅详情传入测量信息
  141. measure3DViewController.carotidResult = remedicalInfo.carotidResult!;
  142. /// [Carotid] ✅需要在此通知 controller 存在颈动脉信息
  143. measure3DViewController.exist3DData = true;
  144. measure3DViewController
  145. .handleChangeCarotid2DImage(remedicalInfo.recordCode!);
  146. } else {
  147. measure3DViewController.exist3DData = false;
  148. }
  149. // [Carotid] ✅只要更换图片都要切换到Vid 2D模式
  150. measure3DViewController.backToVidMode();
  151. try {
  152. if (e != null) {
  153. Future.delayed(const Duration(milliseconds: 100), () {
  154. measureController.playerController.play();
  155. });
  156. setState(() {
  157. loaded = true;
  158. measureHandler.changeImageLoaded = false;
  159. });
  160. }
  161. } catch (error) {
  162. setState(() {
  163. loaded = true;
  164. measureHandler.changeImageLoaded = false;
  165. });
  166. }
  167. }
  168. }
  169. }
  170. @override
  171. void dispose() {
  172. measureHandler.onChangeImageLoaded.removeListener(changeImage);
  173. measureHandler.onChangeFullScreenState
  174. .removeListener(changeFullScreenState);
  175. super.dispose();
  176. }
  177. @override
  178. FWidget build(BuildContext context) {
  179. FWidget body;
  180. if (!loaded) {
  181. const loadingWidget = FCenter(child: FCircularProgressIndicator());
  182. body = FRow(
  183. children: const [
  184. FExpanded(
  185. child: loadingWidget,
  186. ),
  187. ],
  188. );
  189. } else {
  190. body = FRow(
  191. children: [
  192. if (!measureHandler.fullScreenState) _MeasureLeftBoard(),
  193. const FVerticalDivider(),
  194. FExpanded(
  195. child: FColumn(
  196. mainAxisSize: MainAxisSize.max,
  197. children: [
  198. FExpanded(
  199. child: FStack(
  200. children: [
  201. if (!imageLoaded)
  202. MeasureRightBoard(
  203. key: ValueKey(measureData.itemCurrentImage),
  204. ),
  205. if (imageLoaded)
  206. const FCenter(
  207. child: FCircularProgressIndicator(),
  208. ),
  209. ],
  210. )),
  211. if (!measureHandler.fullScreenState) const MeasureImagesBar(),
  212. ],
  213. ),
  214. ),
  215. ],
  216. );
  217. }
  218. return FCenter(
  219. child: FContainer(
  220. color: const Color.fromRGBO(70, 70, 70, 1),
  221. child: body,
  222. ),
  223. );
  224. }
  225. MeasureCursorType _getMeasureSystemSettingCursorType(
  226. CursorTypeEnum cursorType,
  227. ) {
  228. switch (cursorType) {
  229. case CursorTypeEnum.CursorType1Icon:
  230. return MeasureCursorType.cursor01;
  231. case CursorTypeEnum.CursorType2Icon:
  232. return MeasureCursorType.cursor02;
  233. case CursorTypeEnum.CursorType3Icon:
  234. return MeasureCursorType.cursor03;
  235. case CursorTypeEnum.CursorType4Icon:
  236. return MeasureCursorType.cursor04;
  237. case CursorTypeEnum.CursorType5Icon:
  238. return MeasureCursorType.cursor05;
  239. default:
  240. return MeasureCursorType.cursor01;
  241. }
  242. }
  243. /// 初始化卡尺样式部分
  244. Future<void> _getMeasureSystemSetting() async {
  245. final result = await measureData.getMeasureSystemSettingAsync();
  246. measureData.measureSystemSetting = result as MeasureSystemSettingDTO;
  247. mouseState.cursorType = MeasureCursorType.cursor01;
  248. mouseState.cursorSize = 16.0;
  249. }
  250. void _initData() async {
  251. List<RemedicalInfoDTO> remedicals = [];
  252. List<Ultra3DResourceInfo> ultra3DResourceInfos = [];
  253. loaded = false;
  254. var value = await measureData.getRemedicalList.call(
  255. widget.patientCode,
  256. widget.recordCode,
  257. widget.token,
  258. );
  259. for (var remedicalItemList in value) {
  260. remedicals.addAll(remedicalItemList.remedicalList ?? []);
  261. }
  262. /// 获取样式
  263. _getMeasureSystemSetting();
  264. /// [Carotid] ✅遍历出颈动脉信息列表,传给壳子
  265. for (var remedical in remedicals) {
  266. if (remedical.carotidResult != null) {
  267. ultra3DResourceInfos.add(Ultra3DResourceInfo(remedical.carotidResult!));
  268. }
  269. }
  270. measure3DViewController.ultra3DResourceInfoList = ultra3DResourceInfos;
  271. measure3DViewController.recordId = widget.recordCode;
  272. measure3DViewController.notifyShellLoadAllModel();
  273. measureData.remedicalList = remedicals;
  274. var remedicalInfo =
  275. await measureData.getImageInfo.call(widget.remedicalCode, widget.token);
  276. if (remedicalInfo != null) {
  277. measureData.aiResults = remedicalInfo.diagnosisResult ?? '';
  278. if (remedicalInfo.terminalImages != null) {
  279. loaded = true;
  280. measureData.itemCurrentImage =
  281. remedicalInfo.terminalImages!.imageUrl ?? '';
  282. getExamImageInfoList(remedicals);
  283. }
  284. }
  285. measureController.imageLoaded.removeListener(onImageLoaded);
  286. measureController.imageLoaded.addListener(onImageLoaded);
  287. }
  288. }
  289. /// 测量左边操作页面
  290. class _MeasureLeftBoard extends StatefulWidget implements FWidget {
  291. @override
  292. State<_MeasureLeftBoard> createState() => _MeasureLeftBoardState();
  293. }
  294. class _MeasureLeftBoardState extends State<_MeasureLeftBoard> {
  295. final measureHandler = Get.find<MeasureHandler>();
  296. final playerController = Get.find<IPlayerController>();
  297. final measure3DViewController = Get.find<Measure3DViewController>();
  298. /// 数据
  299. late final measureData = Get.find<MeasureDataController>();
  300. /// 测量项控制器
  301. final measureMetaController = Get.find<MeasureMetaController>();
  302. /// 是否显示颈动脉2D指定的测量项
  303. bool showCarotid2DSelectMeasure = false;
  304. /// 是否显示测量项
  305. bool showMeasureItems = true;
  306. bool isMeasureTool = true;
  307. bool get isArrowMeasureAnnotationType =>
  308. measureHandler.changedAnnotationType == AnnotationType.arrow;
  309. void _onChangedTab(
  310. Object sender,
  311. TagEnum? e,
  312. ) {
  313. if (e == TagEnum.MeasureTool) {
  314. measureMetaController.setAvailableModes(measureData.currentMode);
  315. measureMetaController.updateItemList();
  316. // application.currentOperateType = MeasureOperateType.measure;
  317. } else {
  318. final application = Get.find<IApplication>();
  319. application.switchAnnotation(
  320. AnnotationType.label, measureData.getCommentsList[0]);
  321. }
  322. setState(() {});
  323. }
  324. void _onCurItemMetaListChanged(sender, e) {
  325. if (mounted) {
  326. setState(() {});
  327. }
  328. }
  329. @override
  330. void initState() {
  331. measureHandler.onChangedTab.addListener(_onChangedTab);
  332. measure3DViewController.updatePlayerMode.addListener(_onModeChanged);
  333. measureData.curItemMetaListChanged.addListener(_onCurItemMetaListChanged);
  334. super.initState();
  335. }
  336. @override
  337. void dispose() {
  338. measureHandler.onChangedTab.removeListener(_onChangedTab);
  339. measure3DViewController.updatePlayerMode.removeListener(_onModeChanged);
  340. measureData.curItemMetaListChanged
  341. .removeListener(_onCurItemMetaListChanged);
  342. super.dispose();
  343. }
  344. /// 模式改变触发更新
  345. void _onModeChanged(Object s, MeasureMode mode) {
  346. switch (mode) {
  347. case MeasureMode.vidMode:
  348. setState(() {
  349. showCarotid2DSelectMeasure = false;
  350. showMeasureItems = true;
  351. });
  352. break;
  353. case MeasureMode.carotid2DMode:
  354. setState(() {
  355. showCarotid2DSelectMeasure = true;
  356. showMeasureItems = true;
  357. });
  358. break;
  359. case MeasureMode.carotid3DMode:
  360. setState(() {
  361. showCarotid2DSelectMeasure = true;
  362. showMeasureItems = false;
  363. });
  364. break;
  365. }
  366. }
  367. @override
  368. FWidget build(BuildContext context) {
  369. return FColumn(
  370. mainAxisSize: MainAxisSize.max,
  371. crossAxisAlignment: CrossAxisAlignment.start,
  372. children: [
  373. if (showMeasureItems) ...[
  374. LeftSiderHold(ifHideConfig: showCarotid2DSelectMeasure)
  375. ],
  376. if (!(measureHandler.changedTab == TagEnum.MeasureTool)) ...[
  377. FContainer(
  378. width: 300,
  379. key: UniqueKey(),
  380. child: LeftSelectInput(),
  381. ),
  382. FExpanded(
  383. child: FContainer(
  384. width: 300,
  385. key: UniqueKey(),
  386. child: const MeasureLeftAnnotation(),
  387. ),
  388. ),
  389. _MeasureArrow(),
  390. ] else ...[
  391. if (showMeasureItems) ...[const LeftMeasureTools()],
  392. FExpanded(
  393. child: showMeasureItems
  394. ? FContainer(
  395. width: 300,
  396. child: showCarotid2DSelectMeasure
  397. ? const CarotidLeftSiderSelectMeasure()
  398. : const LeftSiderSelectMeasure(),
  399. )
  400. : FContainer(),
  401. ),
  402. const OperateBar(),
  403. ]
  404. ],
  405. );
  406. }
  407. }
  408. class _MeasureArrow extends FStatefulWidget {
  409. @override
  410. FState<_MeasureArrow> createState() => _MeasureArrowState();
  411. }
  412. class _MeasureArrowState extends FState<_MeasureArrow> {
  413. final measureHandler = Get.find<MeasureHandler>();
  414. bool get isArrowMeasureAnnotationType =>
  415. measureHandler.changedAnnotationType == AnnotationType.arrow;
  416. void onChangedAnnotationType(
  417. Object sender,
  418. AnnotationType? e,
  419. ) {
  420. setState(() {});
  421. }
  422. @override
  423. void initState() {
  424. measureHandler.onChangedAnnotationType.addListener(onChangedAnnotationType);
  425. super.initState();
  426. }
  427. @override
  428. void dispose() {
  429. measureHandler.onChangedAnnotationType
  430. .removeListener(onChangedAnnotationType);
  431. super.dispose();
  432. }
  433. @override
  434. FWidget build(BuildContext context) {
  435. return FContainer(
  436. height: 100,
  437. width: 300,
  438. child: FCenter(
  439. child: FColumn(
  440. children: [
  441. FInk(
  442. child: FInkWell(
  443. onTap: () {
  444. measureHandler.changedAnnotationType = AnnotationType.arrow;
  445. final application = Get.find<IApplication>();
  446. application.switchAnnotation(AnnotationType.arrow);
  447. },
  448. child: FIcon(
  449. Icons.call_made_rounded,
  450. color:
  451. isArrowMeasureAnnotationType ? Colors.blue : Colors.white,
  452. size: 30,
  453. ),
  454. ),
  455. ),
  456. FText(
  457. i18nBook.measure.arrow.t,
  458. style: isArrowMeasureAnnotationType
  459. ? const TextStyle(
  460. color: Colors.blue,
  461. fontSize: 14,
  462. )
  463. : const TextStyle(
  464. color: Colors.white,
  465. fontSize: 14,
  466. ),
  467. ),
  468. ],
  469. ),
  470. ),
  471. );
  472. }
  473. }