measure_images_bar.dart 11 KB

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