measure_tool.dart 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. import 'package:fis_common/logger/logger.dart';
  2. import 'package:fis_i18n/i18n.dart';
  3. import 'package:fis_measure/interfaces/process/items/item.dart';
  4. import 'package:fis_measure/interfaces/process/items/item_metas.dart';
  5. import 'package:fis_measure/interfaces/process/items/terms.dart';
  6. import 'package:fis_measure/interfaces/process/items/types.dart';
  7. import 'package:fis_measure/interfaces/process/player/play_controller.dart';
  8. import 'package:fis_measure/interfaces/process/workspace/application.dart';
  9. import 'package:fis_measure/process/language/measure_language.dart';
  10. import 'package:fis_measure/process/workspace/measure_data_controller.dart';
  11. import 'package:fis_measure/process/workspace/measure_handler.dart';
  12. import 'package:fis_measure/view/measure/combo_widget.dart';
  13. import 'package:fis_measure/view/measure/measure_view_controller.dart';
  14. import 'package:fis_measure/view/player/controller.dart';
  15. import 'package:fis_ui/index.dart';
  16. import 'package:fis_ui/interface/interactive_container.dart';
  17. import 'package:flutter/material.dart';
  18. import 'package:get/get.dart';
  19. import 'package:vid/us/vid_us_image.dart';
  20. /// 测量项页面
  21. class LeftSiderSelectMeasure extends FStatefulWidget
  22. implements FInteractiveContainer {
  23. const LeftSiderSelectMeasure({Key? key}) : super(key: key);
  24. @override
  25. final String pageName = 'LeftSiderSelectMeasure';
  26. @override
  27. FState<LeftSiderSelectMeasure> createState() => LeftSiderSelectMeasureState();
  28. }
  29. class LeftSiderSelectMeasureState extends FState<LeftSiderSelectMeasure> {
  30. /// 数据
  31. IApplication get application => Get.find<IApplication>();
  32. VidPlayerController get playerController =>
  33. Get.find<IPlayerController>() as VidPlayerController;
  34. final measureHandler = Get.find<MeasureHandler>();
  35. final measureData = Get.find<MeasureDataController>();
  36. final measureMetaController = Get.find<MeasureMetaController>();
  37. /// 测量语言包
  38. final measureLanguage = MeasureLanguage();
  39. /// 当前选中的测量项下标
  40. int activeItemIndex = 0;
  41. /// 当前选中的子测量项下标
  42. int activeChildItemIndex = 0;
  43. /// 上一个测量项
  44. int lastActiveItemIndex = 0;
  45. /// Styles
  46. late String fontFamily;
  47. static const Color buttonBackgroundColor = Color.fromRGBO(70, 70, 70, 1);
  48. static const Color buttonBorderColor = Color.fromRGBO(124, 124, 124, 1);
  49. static const Color buttonBorderHighlight = Color.fromRGBO(124, 124, 124, 1);
  50. static const Color buttonTextColor = Colors.white;
  51. static const Color childButtonHighlight = Color.fromRGBO(84, 144, 249, 1);
  52. static const Color childContainerBackground = Color.fromRGBO(60, 60, 60, 1);
  53. static const double buttonBorderHighlightWidth = 1.0;
  54. /// 触发测量项选中事件
  55. void selectItem(int selectIndex) {
  56. if (selectIndex == activeItemIndex) return;
  57. final itemMeta = measureData.curItemMetaList[selectIndex];
  58. changeItem(itemMeta);
  59. setState(() {
  60. activeItemIndex = selectIndex;
  61. activeChildItemIndex = 0;
  62. });
  63. }
  64. /// 触发子测量项选中事件
  65. void selectChildItem(int selectIndex) {
  66. if (selectIndex == activeChildItemIndex) return;
  67. final item = application.activeMeasureItem!;
  68. if (item is ITopMeasureItem) {
  69. item.switchChild(selectIndex);
  70. activeChildItemIndex = selectIndex;
  71. }
  72. setState(() {});
  73. }
  74. /// 切换测量项
  75. void changeItem(ItemMeta itemMeta) {
  76. try {
  77. application.switchItem(itemMeta);
  78. final item = application.activeMeasureItem!;
  79. if (item is ITopMeasureItem) {
  80. item.switchChild(0);
  81. item.workingChildChanged.addListener(_onWorkingChildChanged);
  82. }
  83. } catch (e) {
  84. logger.e("changeItem failed", e);
  85. }
  86. if (mounted) {
  87. setState(() {});
  88. }
  89. }
  90. /// 子测量项变化事件监听
  91. void _onWorkingChildChanged(sender, int e) {
  92. setState(() {
  93. activeChildItemIndex = e;
  94. });
  95. }
  96. void _onItemMetaListChanged(Object s, dynamic e) {
  97. try {
  98. if (e != null) {
  99. setState(() {});
  100. }
  101. } catch (e) {
  102. debugPrint("ItemMetaListChanged fail " + e.toString());
  103. }
  104. }
  105. /// 首帧加载完成事件监听: 获取当前图像的测量项,从第一帧取
  106. void _onFirstFrameLoaded(Object sender, VidUsImage e) async {
  107. if (!mounted) return;
  108. _getMeasureItemsList(e);
  109. _getAnnotationList();
  110. }
  111. //初始化时尝试加载第一项测量项(如果有的话)
  112. void _loadFirstItem() {
  113. if (measureData.curItemMetaList.isNotEmpty) {
  114. final itemMeta = measureData.curItemMetaList.first;
  115. changeItem(itemMeta);
  116. setState(() {
  117. activeItemIndex = 0;
  118. activeChildItemIndex = 0;
  119. });
  120. }
  121. }
  122. /// 注释获取
  123. void _getAnnotationList() async {
  124. List<String> annotationList = [];
  125. var measureCommentItemResult =
  126. await measureData.getCommentsByApplicationAsync(
  127. application.applicationName,
  128. application.categoryName,
  129. );
  130. measureData.measureCommentItemResult =
  131. measureCommentItemResult?.commentItems ?? [];
  132. measureCommentItemResult?.commentItems?.forEach((element) {
  133. annotationList.add(element.text ?? '');
  134. });
  135. measureData.annotationList = annotationList;
  136. measureHandler.onAnnotationsLoaded();
  137. }
  138. void _getMeasureItemsList(VidUsImage e) async {
  139. List<String> getModes = [];
  140. for (var element in e.visuals[0].modes) {
  141. getModes.add(element.type.toString().split('.')[1]);
  142. }
  143. measureData.applicationModes = e.visuals[0].modes;
  144. measureData.currentMode = e.visuals[0].modes.first.type.name;
  145. var measureModeSelection = MeasureModeSelection(
  146. application.applicationName,
  147. application.categoryName,
  148. application.isThirdPart ? ['TPPTissue'] : getModes,
  149. );
  150. measureHandler.measureModeChanged = measureModeSelection;
  151. var measureApplicationDTO =
  152. await measureData.getMeasureApplication.call(measureModeSelection);
  153. if (measureApplicationDTO != null) {
  154. measureData.measureApplicationVersion =
  155. measureApplicationDTO.version ?? '';
  156. measureData.availableModes = measureApplicationDTO.availableModes ?? [];
  157. if (application.isThirdPart) {
  158. measureData.currentMode = 'TPPTissue';
  159. }
  160. measureMetaController.setAvailableModes(measureData.currentMode);
  161. setState(() {});
  162. }
  163. }
  164. /// 测量项列表变化事件监听
  165. void _onCurItemMetaListChanged(sender, e) {
  166. if (measureData.curItemMetaList.isNotEmpty) {
  167. changeItem(measureData.curItemMetaList[0]);
  168. }
  169. setState(() {
  170. activeItemIndex = 0;
  171. activeChildItemIndex = 0;
  172. });
  173. }
  174. /// 是否显示测量项翻译事件变化
  175. void _onShowItemTransStateChanged(_, e) {
  176. setState(() {});
  177. }
  178. /// 鼠标右键结束测量事件【即切换到空测量项】,此时缓存被结束的测量项,如果再次收到右击事件,恢复被结束的测量项
  179. void _onRightClickFinishMeasure(_, e) {
  180. if (activeItemIndex == -1) {
  181. changeItem(measureData.curItemMetaList[lastActiveItemIndex]);
  182. setState(() {
  183. activeItemIndex = lastActiveItemIndex;
  184. activeChildItemIndex = 0;
  185. });
  186. } else {
  187. lastActiveItemIndex = activeItemIndex;
  188. final emptyItem = ItemMeta(
  189. "EmptyItem",
  190. measureType: MeasureTypes.Empty,
  191. description: "",
  192. outputs: [],
  193. );
  194. changeItem(emptyItem);
  195. setState(() {
  196. activeItemIndex = -1;
  197. activeChildItemIndex = 0;
  198. });
  199. }
  200. }
  201. @override
  202. void initState() {
  203. measureData.curItemMetaListChanged.addListener(_onCurItemMetaListChanged);
  204. measureData.itemMetaListChanged.addListener(_onItemMetaListChanged);
  205. measureData.showItemTransStateChanged
  206. .addListener(_onShowItemTransStateChanged);
  207. measureHandler.onRightClickFinishMeasure
  208. .addListener(_onRightClickFinishMeasure);
  209. WidgetsBinding.instance.addPostFrameCallback((call) {
  210. _loadFirstItem();
  211. playerController.firstFrameLoaded.addListener(_onFirstFrameLoaded);
  212. });
  213. super.initState();
  214. }
  215. @override
  216. dispose() {
  217. super.dispose();
  218. playerController.firstFrameLoaded.removeListener(_onFirstFrameLoaded);
  219. measureData.curItemMetaListChanged
  220. .removeListener(_onCurItemMetaListChanged);
  221. measureData.itemMetaListChanged.removeListener(_onItemMetaListChanged);
  222. measureData.showItemTransStateChanged
  223. .removeListener(_onShowItemTransStateChanged);
  224. measureHandler.onRightClickFinishMeasure
  225. .removeListener(_onRightClickFinishMeasure);
  226. final item = application.activeMeasureItem;
  227. if (item != null && item is ITopMeasureItem) {
  228. item.workingChildChanged.removeListener(_onWorkingChildChanged);
  229. }
  230. }
  231. @override
  232. FWidget build(BuildContext context) {
  233. fontFamily = Theme.of(context).textTheme.labelLarge!.fontFamily!;
  234. return buildItemsListView(measureData.curItemMetaList);
  235. }
  236. /// 构建测量项列表
  237. FWidget buildItemsListView(List<ItemMeta> itemMetaList) {
  238. return FListView(
  239. shrinkWrap: true,
  240. controller: ScrollController(),
  241. children: List.generate(itemMetaList.length, (i) {
  242. return _buildItemContainer(i);
  243. }));
  244. }
  245. /// 构建测量项容器
  246. FWidget _buildItemContainer(int itemMetaIndex) {
  247. final itemMeta = measureData.curItemMetaList[itemMetaIndex];
  248. final isActived = itemMetaIndex == activeItemIndex; // 是否选中
  249. final hasChildItem = itemMeta.childItems.isNotEmpty; //是否为组合测量项
  250. return FContainer(
  251. decoration: BoxDecoration(
  252. borderRadius: BorderRadius.circular(5),
  253. border: isActived && hasChildItem
  254. ? Border.all(
  255. color: buttonBorderHighlight, width: buttonBorderHighlightWidth)
  256. : null,
  257. ),
  258. margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 5),
  259. child: FColumn(children: [
  260. if (isActived && hasChildItem) ...[
  261. _buildItemTitle(itemMetaIndex),
  262. _buildChildItemContainer(itemMeta),
  263. ] else
  264. FContainer(
  265. width: double.infinity,
  266. child: _buildItemButton(itemMetaIndex),
  267. ),
  268. ]),
  269. );
  270. }
  271. // 构建测量项按钮
  272. FWidget _buildItemButton(int itemIndex) {
  273. final itemMeta = measureData.curItemMetaList[itemIndex];
  274. final transValue = measureLanguage.t('measure', itemMeta.description);
  275. // 中文环境下显示中英文,否则只显示英文
  276. final ifShowCN = i18nBook.isCurrentChinese &&
  277. transValue != itemMeta.description &&
  278. measureData.measureSystemSetting.showAnnotation;
  279. final isActived = itemIndex == activeItemIndex;
  280. return FElevatedButton(
  281. name: itemMeta.name,
  282. businessParent: widget,
  283. onPressed: () => selectItem(itemIndex),
  284. child: FColumn(
  285. mainAxisAlignment: MainAxisAlignment.center,
  286. children: [
  287. if (ifShowCN)
  288. FText(
  289. transValue,
  290. maxLines: 1,
  291. overflow: TextOverflow.ellipsis,
  292. style: TextStyle(
  293. color: isActived ? null : buttonTextColor,
  294. fontFamily: fontFamily,
  295. fontSize: 12,
  296. ),
  297. ),
  298. FText(
  299. itemMeta.description,
  300. style: TextStyle(
  301. color: isActived ? null : buttonTextColor,
  302. ),
  303. ),
  304. ],
  305. ),
  306. style: ElevatedButton.styleFrom(
  307. backgroundColor: isActived ? null : buttonBackgroundColor,
  308. fixedSize: const Size.fromHeight(
  309. 50,
  310. ),
  311. side: BorderSide(
  312. color: isActived ? Colors.transparent : buttonBorderColor,
  313. ),
  314. ),
  315. );
  316. }
  317. // 构建测量项标签【用于组合测量项顶部】
  318. FWidget _buildItemTitle(int itemIndex) {
  319. final itemMeta = measureData.curItemMetaList[itemIndex];
  320. final transValue = measureLanguage.t('measure', itemMeta.description);
  321. // 中文环境下显示中英文,否则只显示英文
  322. final ifShowCN = i18nBook.isCurrentChinese &&
  323. transValue != itemMeta.description &&
  324. measureData.measureSystemSetting.showAnnotation;
  325. return FContainer(
  326. decoration: const BoxDecoration(
  327. border: Border(
  328. bottom: BorderSide(
  329. color: buttonBorderHighlight, width: buttonBorderHighlightWidth),
  330. ),
  331. ),
  332. child: FSizedBox(
  333. height: 50,
  334. width: double.infinity,
  335. child: FColumn(
  336. mainAxisAlignment: MainAxisAlignment.center,
  337. children: [
  338. if (ifShowCN)
  339. FText(
  340. transValue,
  341. maxLines: 1,
  342. overflow: TextOverflow.ellipsis,
  343. style: TextStyle(
  344. color: buttonTextColor,
  345. fontFamily: fontFamily,
  346. fontSize: 12,
  347. ),
  348. ),
  349. FText(
  350. itemMeta.description,
  351. style: const TextStyle(
  352. color: buttonTextColor,
  353. ),
  354. ),
  355. ],
  356. ),
  357. ),
  358. );
  359. }
  360. /// 子测量项容器
  361. FWidget _buildChildItemContainer(ItemMeta itemMeta) {
  362. final activeName = itemMeta.description;
  363. final isSpecial = MeasurespecialsupportedTerms.items.contains(activeName);
  364. if (isSpecial) {
  365. //是否为特殊组合测量项
  366. return const SpecialItemHR();
  367. } else {
  368. return FContainer(
  369. decoration: const BoxDecoration(
  370. color: childContainerBackground,
  371. borderRadius: BorderRadius.only(
  372. bottomLeft: Radius.circular(5), bottomRight: Radius.circular(5)),
  373. ),
  374. padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 5),
  375. child: FColumn(
  376. children: (itemMeta.childItems)
  377. .asMap()
  378. .entries
  379. .map(
  380. (e) => _buildChildItemButton(
  381. e.value,
  382. e.key,
  383. ),
  384. )
  385. .toList(),
  386. ),
  387. );
  388. }
  389. }
  390. // 构建子测量项按钮
  391. FWidget _buildChildItemButton(ItemMeta itemMeta, int childItemIndex) {
  392. final isActived = childItemIndex == activeChildItemIndex;
  393. return FContainer(
  394. width: double.infinity,
  395. margin: const EdgeInsetsDirectional.all(5),
  396. child: FElevatedButton(
  397. businessParent: widget,
  398. name: "selectChildItemIdnex:$childItemIndex",
  399. onPressed: () {
  400. selectChildItem(childItemIndex);
  401. },
  402. child: FText(
  403. itemMeta.description,
  404. ),
  405. style: ElevatedButton.styleFrom(
  406. backgroundColor: isActived ? null : buttonBackgroundColor,
  407. side: BorderSide(
  408. color: isActived ? Colors.transparent : buttonBorderColor,
  409. ),
  410. ),
  411. ));
  412. }
  413. }