measure_images_bar.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. import 'dart:convert';
  2. import 'package:fis_common/logger/logger.dart';
  3. import 'package:fis_jsonrpc/rpc.dart';
  4. import 'package:fis_measure/interfaces/process/workspace/exam_info.dart';
  5. import 'package:fis_measure/process/language/measure_language.dart';
  6. import 'package:fis_measure/process/workspace/measure_controller.dart';
  7. import 'package:fis_measure/process/workspace/measure_data_controller.dart';
  8. import 'package:fis_measure/process/workspace/measure_handler.dart';
  9. import 'package:fis_measure/view/measure/measure_images_bar.dart';
  10. export 'package:fis_lib_business_components/index.dart';
  11. import 'package:fis_ui/index.dart';
  12. import 'package:flutter/material.dart';
  13. import 'package:get/get.dart';
  14. class MeasureImagesBar extends StatefulWidget implements FWidget {
  15. const MeasureImagesBar({Key? key}) : super(key: key);
  16. @override
  17. State<MeasureImagesBar> createState() => _MeasureImagesBarState();
  18. }
  19. class _MeasureImagesBarState extends State<MeasureImagesBar>
  20. with WidgetsBindingObserver {
  21. /// 图片滑动控制器
  22. ScrollController scrollController = ScrollController();
  23. /// 数据
  24. final measureData = Get.find<MeasureDataController>();
  25. /// 全部图片list
  26. final measureController = Get.find<MeasureController>();
  27. /// 当前可滑动的范围宽度
  28. late double width = 0;
  29. ///Listview item的宽度
  30. late double itemWidth = 160;
  31. /// 是否滑到最后
  32. bool _isEnd = false;
  33. /// 计算下标
  34. int _currentPage = 0;
  35. /// 左边能否滑动
  36. bool get isLeftCanScroll => _currentPage <= 0;
  37. /// 右边能否滑动
  38. bool get isRightCanScroll => _isEnd;
  39. /// 滑动区域的key值
  40. final GlobalKey globalKey = GlobalKey();
  41. double get devicePixelRatio => MediaQuery.of(context).devicePixelRatio;
  42. ///当前分辨率下ListView里面的item个数
  43. int _itemsPerPage = 0;
  44. /// 图片左划事件
  45. void onLeftMoveImage() async {
  46. _currentPage--;
  47. await scrollController.animateTo(
  48. (160 * _currentPage * _itemsPerPage).toDouble(),
  49. duration: const Duration(milliseconds: 300),
  50. curve: Curves.linear,
  51. );
  52. }
  53. /// 图片右划事件
  54. void onRightMoveImage() async {
  55. if (!isRightCanScroll) {
  56. _currentPage++;
  57. await scrollController.animateTo(
  58. (160 * _currentPage * _itemsPerPage).toDouble(),
  59. duration: const Duration(milliseconds: 300),
  60. curve: Curves.linear,
  61. );
  62. }
  63. }
  64. @override
  65. Widget build(BuildContext context) {
  66. return LayoutBuilder(
  67. builder: (BuildContext context, BoxConstraints constraints) {
  68. return FContainer(
  69. height: 120 / devicePixelRatio,
  70. margin: EdgeInsets.symmetric(
  71. vertical: devicePixelRatio > 1.25
  72. ? 5
  73. : devicePixelRatio > 1
  74. ? 10
  75. : 15,
  76. ),
  77. child: FRow(
  78. children: [
  79. _buildImageListIcon(
  80. FIcons.fis_left_arrow,
  81. isLeftCanScroll,
  82. onLeftMoveImage,
  83. ),
  84. FExpanded(
  85. child: ScrollableImageList(
  86. scrollController: scrollController,
  87. globalKey: globalKey,
  88. remedicalList: measureData.remedicalList,
  89. ),
  90. ),
  91. _buildImageListIcon(
  92. FIcons.fis_right_arrow,
  93. isRightCanScroll,
  94. onRightMoveImage,
  95. ),
  96. ],
  97. ),
  98. );
  99. },
  100. );
  101. }
  102. @override
  103. void didChangeMetrics() {
  104. width = globalKey.currentContext!.size!.width;
  105. print('width:$width,itemWidth:$itemWidth');
  106. _isEnd = width >= measureData.remedicalList.length * itemWidth;
  107. _itemsPerPage = (width / itemWidth).toInt();
  108. var currentIndex = measureController.examInfo.selectedImageIndex;
  109. if (currentIndex * itemWidth > scrollController.position.maxScrollExtent) {
  110. currentIndex = scrollController.position.maxScrollExtent ~/ itemWidth;
  111. }
  112. scrollController.animateTo(
  113. (itemWidth * currentIndex).toDouble(),
  114. duration: const Duration(milliseconds: 300),
  115. curve: Curves.linear,
  116. );
  117. setState(
  118. () {},
  119. );
  120. }
  121. @override
  122. void initState() {
  123. WidgetsBinding.instance.addObserver(this);
  124. scrollController.addListener(
  125. () async {
  126. // 如果滑动到最右边
  127. if (scrollController.position.pixels ==
  128. scrollController.position.maxScrollExtent) {
  129. setState(() {
  130. _isEnd = true;
  131. });
  132. } else {
  133. setState(
  134. () {
  135. _isEnd = false;
  136. },
  137. );
  138. }
  139. },
  140. );
  141. super.initState();
  142. }
  143. @override
  144. void didChangeDependencies() {
  145. super.didChangeDependencies();
  146. WidgetsBinding.instance.addPostFrameCallback(
  147. (mag) {
  148. width = globalKey.currentContext!.size!.width;
  149. setState(
  150. () {
  151. _isEnd = width >= measureData.remedicalList.length * itemWidth;
  152. _itemsPerPage = (width / itemWidth).toInt();
  153. var currentIndex = measureController.examInfo.selectedImageIndex;
  154. if (currentIndex * itemWidth >
  155. scrollController.position.maxScrollExtent) {
  156. currentIndex =
  157. scrollController.position.maxScrollExtent ~/ itemWidth;
  158. }
  159. scrollController.animateTo(
  160. (itemWidth * currentIndex).toDouble(),
  161. duration: const Duration(milliseconds: 300),
  162. curve: Curves.linear,
  163. );
  164. },
  165. );
  166. },
  167. );
  168. }
  169. @override
  170. void dispose() {
  171. WidgetsBinding.instance.removeObserver(this);
  172. super.dispose();
  173. }
  174. FWidget _buildImageListIcon(
  175. IconData iconsData, bool isCanClick, Function onIconTap) {
  176. return FInkWell(
  177. onTap: () => onIconTap.call(),
  178. child: FContainer(
  179. width: 100 / devicePixelRatio,
  180. child: FIcon(
  181. iconsData,
  182. color: isCanClick ? Colors.grey : Colors.white,
  183. size: 30 / devicePixelRatio,
  184. ),
  185. ),
  186. );
  187. }
  188. }
  189. /// 可滑动的图片组,显示当前测量组的所有图片
  190. class ScrollableImageList extends StatefulWidget implements FWidget {
  191. const ScrollableImageList({
  192. required this.scrollController,
  193. required this.globalKey,
  194. required this.remedicalList,
  195. Key? key,
  196. this.description = '',
  197. }) : super(key: key);
  198. /// 图片滑动控制器
  199. final ScrollController scrollController;
  200. final GlobalKey globalKey;
  201. final List<RemedicalInfoDTO> remedicalList;
  202. final String? description;
  203. @override
  204. State<ScrollableImageList> createState() => _ScrollableImageListState();
  205. }
  206. class _ScrollableImageListState extends State<ScrollableImageList> {
  207. /// 当前测量的图片
  208. // final measureCurrentImage = Get.put(MeasureGetCurrentImage());
  209. double get devicePixelRatio => MediaQuery.of(context).devicePixelRatio;
  210. /// 测量AI数据
  211. final measureData = Get.find<MeasureDataController>();
  212. MeasureController measureController = Get.find<MeasureController>();
  213. late final measureHandler = Get.put(MeasureHandler());
  214. /// 测量语言包
  215. final measureLanguage = MeasureLanguage();
  216. /// 图像数据列表
  217. List<RemedicalInfoDTO> remedicalList = [];
  218. /// 获取图片地址
  219. void onChangeImage(
  220. String imageUrl,
  221. String remedicalCode,
  222. ) async {
  223. final selectedIndex = measureController.examInfo.images.indexWhere(
  224. (element) => element.remedicalCode == remedicalCode,
  225. );
  226. if (selectedIndex < 0) {
  227. return;
  228. }
  229. if (measureData.itemCurrentImage == imageUrl &&
  230. measureController.examInfo.selectedImageIndex == selectedIndex) {
  231. return;
  232. }
  233. measureData.itemCurrentImage = imageUrl;
  234. // remedicalAISelectedInfoCode是因为选择ai编辑过的图片code和remedicalCode等级一样
  235. measureHandler.changeImageLoaded = true;
  236. measureData.measureImageData = MeasureImageData(
  237. patientCode: measureData.measureImageData.patientCode,
  238. recordCode: measureData.measureImageData.recordCode,
  239. remedicalCode: remedicalCode,
  240. remedicalAISelectedInfoCode: remedicalCode,
  241. );
  242. measureHandler.currSelectedImage = CurrImageInfo(
  243. imageUrl,
  244. remedicalCode,
  245. );
  246. measureController.examInfo.selectedImageIndex = selectedIndex;
  247. setState(() {});
  248. }
  249. /// 获取图像Code来更新图像
  250. void onChangeImageByRemedicalCode(_, String e) {
  251. final url = findImageUrlByRemedicalCode(e);
  252. onChangeImage(url, e);
  253. }
  254. /// 更新图像集合
  255. void onChangeImageList(_, e) {
  256. measureController = Get.find<MeasureController>();
  257. setState(() {
  258. remedicalList = e;
  259. });
  260. }
  261. String findImageUrlByRemedicalCode(String remedicalCode) {
  262. final item =
  263. remedicalList.firstWhere((e) => e.remedicalCode == remedicalCode);
  264. final imgInfo = item.terminalImages!;
  265. return measureData.chooseImageUrl(imgInfo);
  266. }
  267. @override
  268. void initState() {
  269. remedicalList = widget.remedicalList;
  270. measureHandler.changeImageByRemedicalCode
  271. .addListener(onChangeImageByRemedicalCode);
  272. measureHandler.changeImageList.addListener(onChangeImageList);
  273. super.initState();
  274. }
  275. @override
  276. void dispose() {
  277. measureHandler.changeImageByRemedicalCode
  278. .removeListener(onChangeImageByRemedicalCode);
  279. measureHandler.changeImageList.removeListener(onChangeImageList);
  280. super.dispose();
  281. }
  282. @override
  283. FWidget build(BuildContext context) {
  284. return FContainer(
  285. key: ValueKey(measureData.itemCurrentImage),
  286. alignment: Alignment.topCenter,
  287. child: _buildImageList(remedicalList),
  288. );
  289. }
  290. ///翻译图片描述
  291. String _translateDescription(RemedicalInfoDTO remedicalInfo) {
  292. String description = '';
  293. try {
  294. if (remedicalInfo.application != null) {
  295. //判断是否是老数据,老数据里面没有applicationCategory的字段
  296. if (remedicalInfo.applicationCategory != null) {
  297. //如果不是老数据
  298. description = _getDescription(
  299. remedicalInfo.application ?? '',
  300. remedicalInfo.applicationCategory ?? '',
  301. );
  302. } else {
  303. //如果是老数据
  304. description = "Old data";
  305. }
  306. }
  307. } on Exception catch (e) {
  308. logger.e("Picture translation exception" + e.toString());
  309. } catch (e) {
  310. logger.e("Picture translation exception" + e.toString());
  311. }
  312. return description;
  313. }
  314. /// 翻译图片位置的描述
  315. String _translateLocationDescription(RemedicalInfoDTO remedicalInfo) {
  316. String description = '';
  317. try {
  318. description = _getLocationDescription(
  319. remedicalInfo.imageLocation?.quadrant,
  320. remedicalInfo.imageLocation?.position);
  321. } on Exception catch (e) {
  322. logger.e("Picture translation exception" + e.toString());
  323. } catch (e) {
  324. logger.e("Picture translation exception" + e.toString());
  325. }
  326. return description;
  327. }
  328. /// 获取翻译值
  329. String _getLanguageValue(String type, String code) {
  330. return measureLanguage.t(type, code);
  331. }
  332. String _getDescription(String application, String applicationCategory) {
  333. String description = '';
  334. if ([application, applicationCategory].contains('FromSonopost')) {
  335. description = _getLanguageValue('application', 'FromSonopost');
  336. } else {
  337. if (application.isNotEmpty || applicationCategory.isNotEmpty) {
  338. description = _getLanguageValue('application', applicationCategory) +
  339. "-" +
  340. _getLanguageValue('application', application);
  341. } else {
  342. description = '';
  343. }
  344. }
  345. return description;
  346. }
  347. String _getLocationDescription(String? quadrant, String? position) {
  348. String description = '';
  349. if (quadrant != '' || quadrant != '') {
  350. description = _getLanguageValue('location', quadrant ?? '') +
  351. "-" +
  352. _getLanguageValue('location', position ?? '');
  353. } else {
  354. description = '';
  355. }
  356. return description;
  357. }
  358. FWidget _buildImageList(List<RemedicalInfoDTO> remedicalItemList) {
  359. var currentIndex = measureController.examInfo.selectedImageIndex;
  360. return FContainer(
  361. key: widget.globalKey,
  362. child: FListView.builder(
  363. scrollDirection: Axis.horizontal,
  364. shrinkWrap: true,
  365. controller: widget.scrollController,
  366. itemCount: remedicalItemList.toList().length,
  367. itemBuilder: (BuildContext context, int index) {
  368. final item = remedicalItemList[index];
  369. FWidget image = FContentImage(
  370. remedicalInfo: item,
  371. isMeasure: true,
  372. onTap: () {
  373. final code = item.remedicalCode!;
  374. final url = findImageUrlByRemedicalCode(code);
  375. onChangeImage(url, code);
  376. },
  377. serialNo: index + 1,
  378. description: _translateDescription(item),
  379. locationDescription: _translateLocationDescription(item),
  380. );
  381. bool isCurrentSelect = index == currentIndex;
  382. return FContainer(
  383. key: ValueKey(measureData.itemCurrentImage),
  384. width: 160 / devicePixelRatio,
  385. alignment: Alignment.center,
  386. decoration: BoxDecoration(
  387. border: Border.all(
  388. width: 3,
  389. color: isCurrentSelect ? Colors.blue : Colors.grey,
  390. ),
  391. ),
  392. child: image,
  393. );
  394. },
  395. ),
  396. );
  397. }
  398. }