measure_tool.dart 13 KB

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