measure_tool.dart 14 KB

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