import 'dart:convert'; import 'package:fis_common/logger/logger.dart'; import 'package:fis_jsonrpc/rpc.dart'; import 'package:fis_measure/interfaces/process/workspace/exam_info.dart'; import 'package:fis_measure/process/language/measure_language.dart'; import 'package:fis_measure/process/workspace/measure_controller.dart'; import 'package:fis_measure/process/workspace/measure_data_controller.dart'; import 'package:fis_measure/process/workspace/measure_handler.dart'; import 'package:fis_measure/view/measure/measure_images_bar.dart'; export 'package:fis_lib_business_components/index.dart'; import 'package:fis_ui/index.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; class MeasureImagesBar extends StatefulWidget implements FWidget { const MeasureImagesBar({ Key? key, this.hasQualityControlPermission = false, }) : super(key: key); final bool hasQualityControlPermission; @override State createState() => _MeasureImagesBarState(); } class _MeasureImagesBarState extends State with WidgetsBindingObserver { /// 图片滑动控制器 ScrollController scrollController = ScrollController(); /// 数据 final measureData = Get.find(); /// 全部图片list final measureController = Get.find(); /// 当前可滑动的范围宽度 late double width = 0; ///Listview item的宽度 late double itemWidth = 160; /// 是否滑到最后 bool _isEnd = false; /// 计算下标 int _currentPage = 0; /// 左边能否滑动 bool get isLeftCanScroll => _currentPage <= 0; /// 右边能否滑动 bool get isRightCanScroll => _isEnd; /// 滑动区域的key值 final GlobalKey globalKey = GlobalKey(); double get devicePixelRatio => kIsMobile ? 1 : MediaQuery.of(context).devicePixelRatio; ///当前分辨率下ListView里面的item个数 int _itemsPerPage = 0; /// 图片左划事件 void onLeftMoveImage() async { _currentPage--; await scrollController.animateTo( (160 * _currentPage * _itemsPerPage).toDouble(), duration: const Duration(milliseconds: 300), curve: Curves.linear, ); } /// 图片右划事件 void onRightMoveImage() async { if (!isRightCanScroll) { _currentPage++; await scrollController.animateTo( (160 * _currentPage * _itemsPerPage).toDouble(), duration: const Duration(milliseconds: 300), curve: Curves.linear, ); } } @override Widget build(BuildContext context) { return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { return FContainer( height: 120 / devicePixelRatio, margin: EdgeInsets.symmetric( vertical: devicePixelRatio > 1.25 ? 5 : devicePixelRatio > 1 ? 10 : 15, ), child: FRow( children: [ _buildImageListIcon( FIcons.fis_left_arrow, isLeftCanScroll, onLeftMoveImage, ), FExpanded( child: ScrollableImageList( scrollController: scrollController, globalKey: globalKey, remedicalList: measureData.remedicalList, hasQualityControlPermission: widget.hasQualityControlPermission, ), ), _buildImageListIcon( FIcons.fis_right_arrow, isRightCanScroll, onRightMoveImage, ), ], ), ); }, ); } @override void didChangeMetrics() { width = globalKey.currentContext!.size!.width; print('width:$width,itemWidth:$itemWidth'); _isEnd = width >= measureData.remedicalList.length * itemWidth; _itemsPerPage = (width / itemWidth).toInt(); var currentIndex = measureController.examInfo.selectedImageIndex; if (currentIndex * itemWidth > scrollController.position.maxScrollExtent) { currentIndex = scrollController.position.maxScrollExtent ~/ itemWidth; } scrollController.animateTo( (itemWidth * currentIndex).toDouble(), duration: const Duration(milliseconds: 300), curve: Curves.linear, ); setState( () {}, ); } @override void initState() { WidgetsBinding.instance.addObserver(this); scrollController.addListener( () async { // 如果滑动到最右边 if (scrollController.position.pixels == scrollController.position.maxScrollExtent) { setState(() { _isEnd = true; }); } else { setState( () { _isEnd = false; }, ); } }, ); super.initState(); } @override void didChangeDependencies() { super.didChangeDependencies(); WidgetsBinding.instance.addPostFrameCallback( (mag) { width = globalKey.currentContext!.size!.width; setState( () { _isEnd = width >= measureData.remedicalList.length * itemWidth; _itemsPerPage = (width / itemWidth).toInt(); var currentIndex = measureController.examInfo.selectedImageIndex; if (currentIndex * itemWidth > scrollController.position.maxScrollExtent) { currentIndex = scrollController.position.maxScrollExtent ~/ itemWidth; } scrollController.animateTo( (itemWidth * currentIndex).toDouble(), duration: const Duration(milliseconds: 300), curve: Curves.linear, ); }, ); }, ); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } FWidget _buildImageListIcon( IconData iconsData, bool isCanClick, Function onIconTap) { return FInkWell( onTap: () => onIconTap.call(), child: FContainer( width: 100 / devicePixelRatio, child: FIcon( iconsData, color: isCanClick ? Colors.grey : Colors.white, size: 30 / devicePixelRatio, ), ), ); } } /// 可滑动的图片组,显示当前测量组的所有图片 class ScrollableImageList extends StatefulWidget implements FWidget { const ScrollableImageList({ required this.scrollController, required this.globalKey, required this.remedicalList, this.hasQualityControlPermission = false, Key? key, this.description = '', }) : super(key: key); /// 图片滑动控制器 final ScrollController scrollController; final GlobalKey globalKey; final List remedicalList; final String? description; final bool hasQualityControlPermission; @override State createState() => _ScrollableImageListState(); } class _ScrollableImageListState extends State { /// 当前测量的图片 // final measureCurrentImage = Get.put(MeasureGetCurrentImage()); double get devicePixelRatio => kIsMobile ? 1 : MediaQuery.of(context).devicePixelRatio; /// 测量AI数据 final measureData = Get.find(); MeasureController measureController = Get.find(); late final measureHandler = Get.put(MeasureHandler()); /// 测量语言包 final measureLanguage = MeasureLanguage(); /// 图像数据列表 List remedicalList = []; /// 获取图片地址 void onChangeImage( String imageUrl, String remedicalCode, ) async { final selectedIndex = measureController.examInfo.images.indexWhere( (element) => element.remedicalCode == remedicalCode, ); if (selectedIndex < 0) { return; } if (measureData.itemCurrentImage == imageUrl && measureController.examInfo.selectedImageIndex == selectedIndex) { return; } measureData.itemCurrentImage = imageUrl; // remedicalAISelectedInfoCode是因为选择ai编辑过的图片code和remedicalCode等级一样 measureHandler.newImageLoading = true; measureData.measureInfoData = MeasureInfoData( patientCode: measureData.measureInfoData.patientCode, recordCode: measureData.measureInfoData.recordCode, remedicalCode: remedicalCode, remedicalAISelectedInfoCode: remedicalCode, ); measureHandler.currSelectedImage = CurrImageInfo( imageUrl, remedicalCode, ); measureController.examInfo.selectedImageIndex = selectedIndex; setState(() {}); } /// 获取图像Code来更新图像 void onChangeImageByRemedicalCode(_, String e) { final url = findImageUrlByRemedicalCode(e); onChangeImage(url, e); } /// 更新图像集合 void onChangeImageList(_, e) { measureController = Get.find(); setState(() { remedicalList = e; }); } String findImageUrlByRemedicalCode(String remedicalCode) { final item = remedicalList.firstWhere((e) => e.remedicalCode == remedicalCode); final imgInfo = item.terminalImages!; return measureData.chooseImageUrl(imgInfo); } @override void initState() { remedicalList = widget.remedicalList; measureHandler.changeImageByRemedicalCode .addListener(onChangeImageByRemedicalCode); measureHandler.changeImageList.addListener(onChangeImageList); super.initState(); } @override void dispose() { measureHandler.changeImageByRemedicalCode .removeListener(onChangeImageByRemedicalCode); measureHandler.changeImageList.removeListener(onChangeImageList); super.dispose(); } @override FWidget build(BuildContext context) { return FContainer( key: ValueKey(measureData.itemCurrentImage), alignment: Alignment.topCenter, child: _buildImageList(remedicalList), ); } ///翻译图片描述 String _translateDescription(RemedicalInfoDTO remedicalInfo) { String description = ''; try { if (remedicalInfo.application != null) { //判断是否是老数据,老数据里面没有applicationCategory的字段 if (remedicalInfo.applicationCategory != null) { //如果不是老数据 description = _getDescription( remedicalInfo.application ?? '', remedicalInfo.applicationCategory ?? '', ); } else { //如果是老数据 description = "Old data"; } } } on Exception catch (e) { logger.e("Picture translation exception" + e.toString()); } catch (e) { logger.e("Picture translation exception" + e.toString()); } return description; } /// 翻译图片位置的描述 String _translateLocationDescription(RemedicalInfoDTO remedicalInfo) { String description = ''; try { description = _getLocationDescription( remedicalInfo.imageLocation?.quadrant, remedicalInfo.imageLocation?.position); } on Exception catch (e) { logger.e("Picture translation exception" + e.toString()); } catch (e) { logger.e("Picture translation exception" + e.toString()); } return description; } /// 获取翻译值 String _getLanguageValue(String type, String code) { return measureLanguage.t(type, code); } String _getDescription(String application, String applicationCategory) { String description = ''; if ([application, applicationCategory].contains('FromSonopost')) { description = _getLanguageValue('application', 'FromSonopost'); } else { if (application.isNotEmpty || applicationCategory.isNotEmpty) { description = _getLanguageValue('application', applicationCategory) + "-" + _getLanguageValue('application', application); } else { description = ''; } } return description; } String _getLocationDescription(String? quadrant, String? position) { String description = ''; if (quadrant != '' || quadrant != '') { description = _getLanguageValue('location', quadrant ?? '') + "-" + _getLanguageValue('location', position ?? ''); } else { description = ''; } return description; } FWidget _buildImageList(List remedicalItemList) { var currentIndex = measureController.examInfo.selectedImageIndex; return FContainer( key: widget.globalKey, child: FListView.builder( scrollDirection: Axis.horizontal, shrinkWrap: true, controller: widget.scrollController, itemCount: remedicalItemList.toList().length, itemBuilder: (BuildContext context, int index) { final item = remedicalItemList[index]; FWidget image = FContentImage( remedicalInfo: item, isMeasure: true, onTap: () { final code = item.remedicalCode!; final url = findImageUrlByRemedicalCode(code); onChangeImage(url, code); }, serialNo: index + 1, description: _translateDescription(item), locationDescription: _translateLocationDescription(item), isQualityControlled: widget.hasQualityControlPermission && item.isQualityControlled, ); bool isCurrentSelect = index == currentIndex; return FContainer( key: ValueKey(measureData.itemCurrentImage), width: 160 / devicePixelRatio, alignment: Alignment.center, decoration: BoxDecoration( border: Border.all( width: 3, color: isCurrentSelect ? Colors.blue : Colors.grey, ), ), child: image, ); }, ), ); } }