123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681 |
- import 'dart:math';
- import 'package:fis_i18n/i18n.dart';
- import 'package:fis_jsonrpc/rpc.dart';
- import 'package:fis_lib_business_components/index.dart';
- import 'package:fis_lib_report/pages/theme.dart';
- import 'package:fis_lib_report/report_edit.dart';
- import 'package:fis_theme/theme.dart';
- import 'package:fis_ui/base_define/page.dart';
- import 'package:fis_ui/index.dart';
- import 'package:flutter/gestures.dart';
- import 'package:flutter/material.dart';
- import 'package:get/get.dart';
- import 'package:fis_common/index.dart';
- import 'package:fis_measure/process/workspace/rpc_bridge.dart';
- import 'package:vitalapp/pages/consultation_record_view/widgets/button_group.dart';
- import 'package:vitalapp/pages/image_report_inner_view/widgets/cloud_image_item.dart';
- import 'package:vitalapp/pages/patient/list/widgets/tab_button.dart';
- import 'package:vitalapp/pages/patient/list/widgets/tab_button_group.dart';
- import 'package:vitalapp/pages/report_edit/controller.dart';
- import 'package:vitalapp/pages/report_edit/widgets/vocabulary_entry_report.dart';
- import 'package:vitalapp/store/store.dart';
- class ReporteditPage extends GetView<ReportEditController> implements FPage {
- const ReporteditPage({this.pageName = 'ReporteditPage', Key? key})
- : super(key: key);
- @override
- final String pageName;
- @override
- Widget build(BuildContext context) {
- return _DesktopLayout(this);
- }
- }
- class _DesktopLayout extends StatefulWidget {
- ///父级节点
- final FPage businessParent;
- _DesktopLayout(this.businessParent);
- @override
- State<_DesktopLayout> createState() => _DesktopLayoutState();
- }
- class _DesktopLayoutState extends State<_DesktopLayout> {
- final controller = Get.find<ReportEditController>();
- final ScrollController _scrollController = ScrollController();
- double _scrollSensitivityFactor = 1.0;
- @override
- void initState() {
- if (kIsMobile) {
- _scrollSensitivityFactor = 10.0;
- }
- initFTheme();
- super.initState();
- }
- void initFTheme() async {
- final theme = AppTheme(themeMode: ThemeMode.system);
- await FTheme.init(theme);
- }
- @override
- Widget build(BuildContext context) {
- if (Store.user.token?.isEmpty ?? true) {
- controller.initParameters();
- }
- final body = _buildBodyView();
- return Scaffold(
- body: Stack(
- children: [
- body,
- Positioned(
- right: 0,
- top: 0,
- bottom: 60,
- child: Obx(() {
- if (controller.state.isEntryDisplayed) {
- return VocabularyEntryReport(onClose: () {
- controller.state.isEntryDisplayed = false;
- });
- } else {
- return FSizedBox();
- }
- }),
- ),
- ],
- ),
- );
- }
- ///报告内容视图
- Widget _buildReportContentView(BuildContext context) {
- final state = controller.state;
- var body = Obx(() {
- final jsonStr =
- controller.state.selectedTemplate.reportTemplateJson ?? '';
- if (jsonStr.isEmpty) {
- return FSizedBox(
- width: kIsMobile ? 540 : 810,
- );
- }
- return Container(
- color: Colors.white,
- margin: EdgeInsets.only(top: 60),
- child: Listener(
- onPointerSignal: (pointerSignal) {
- if (pointerSignal is PointerScrollEvent) {
- final newOffset = _scrollController.offset +
- pointerSignal.scrollDelta.dy * _scrollSensitivityFactor;
- if (newOffset <= _scrollController.position.maxScrollExtent &&
- newOffset >= _scrollController.position.minScrollExtent) {
- _scrollController.jumpTo(newOffset);
- }
- }
- },
- child: ListView(
- controller: _scrollController,
- shrinkWrap: true,
- children: [
- FReportEditPage(
- reporter: Store.user.displayName,
- reportDate: DateTime.now(),
- jsonStr: jsonStr,
- onSelect: controller.onSelect,
- patinentAge: state.patientAge,
- patinentName: state.patientName,
- patinentSex: state.patientSex,
- selectEntry: i18nBook.remedical.selectWord.t,
- revoke: i18nBook.common.revoke.t,
- selectImageHint: i18nBook.remedical.clickAndSelectImage.t,
- ),
- ],
- ),
- ),
- );
- });
- return Stack(
- children: [
- body,
- Positioned(
- left: 15,
- top: 15,
- child: Obx(() {
- ///编辑报告不支持修改模板
- if (controller.state.templates.isEmpty ||
- controller.state.reportCode.isNotEmpty) {
- return FSizedBox();
- }
- return FSelect<FSelectOptionModel, String>(
- source: controller.state.templates,
- width: 320,
- optionValueExtractor: (t) {
- return t.value;
- },
- optionLabelExtractor: (t) {
- return t.title;
- },
- textColor: Colors.black,
- value: controller.state.selectedTemplate.reportTemplateCode,
- onSelectChanged: (value, index) async {
- controller.onSelectedTemplateChange(value);
- },
- );
- }),
- ),
- ],
- );
- }
- ///图像展示
- Widget _buildImagsView(BuildContext context) {
- return Obx(
- () {
- if (controller.state.selectImageTabIndex == 0) {
- return _buildCloudImagesView(context);
- } else if (controller.state.selectImageTabIndex == 1) {
- if (controller.state.remedicalMeasured.length == 0) {
- return FExpanded(
- child: NoDataWidget(),
- );
- }
- return _buildMeasureImageOrAiImageContentView(context);
- } else if (controller.state.selectImageTabIndex == 3) {
- if (controller.state.remedicalAISelectedInfos.length == 0) {
- return FExpanded(
- child: NoDataWidget(),
- );
- }
- return _buildAiImagesView(context);
- }
- return SizedBox();
- },
- );
- }
- ///一排操作按钮(提交、预览、返回)
- Widget _buildOperateLine() {
- var buttonStyle = ButtonStyle(
- padding: MaterialStateProperty.all(EdgeInsets.symmetric(vertical: 15)),
- backgroundColor: MaterialStateProperty.all(Colors.white),
- foregroundColor: MaterialStateProperty.all(Colors.black),
- );
- final mediaQuery = MediaQuery.of(context);
- ///屏幕缩放比例
- final devicePixelRatio = mediaQuery.devicePixelRatio;
- final textStyle = TextStyle(
- fontSize: (devicePixelRatio > 1 && !i18nBook.isCurrentChinese) ? 10 : 14,
- );
- return SizedBox(
- height: 50,
- child: ButtonGroup(
- buttonPadding: 25,
- children: [
- ElevatedButton(
- onPressed: () async {
- await controller.submitReport();
- },
- style: ButtonStyle(
- padding: MaterialStateProperty.all(
- EdgeInsets.symmetric(vertical: 15),
- ),
- ),
- child: FText(
- i18nBook.common.submit.t,
- style: textStyle,
- ),
- ),
- ElevatedButton(
- onPressed: () async {
- await controller.refreshImages();
- setState(() {});
- },
- style: buttonStyle,
- child: FText(
- i18nBook.common.refresh.t,
- style: textStyle,
- ),
- ),
- ElevatedButton(
- onPressed: () async {
- Get.back();
- },
- style: buttonStyle,
- child: FText(
- i18nBook.common.back.t,
- style: textStyle,
- ),
- ),
- ],
- ),
- );
- }
- ///云端图像
- FWidget _buildCloudImagesView(BuildContext context) {
- final leftWidth = (MediaQuery.of(context).size.width - 90) / 3;
- final imageWidth = kIsMobile ? 300.0 : (leftWidth / 3 + 10);
- bool carotidLeftIsNotEmpty =
- controller.state.cloudCarotidLeftImages.isNotEmpty;
- bool carotidRightIsNotEmpty =
- controller.state.cloudCarotidRightImages.isNotEmpty;
- List<FWidget> children = [];
- if (controller.state.remedicalItemList.isNotEmpty) {
- children.add(
- _buildGridCloudImagesView(
- imageWidth, controller.state.remedicalItemList),
- );
- }
- if (carotidLeftIsNotEmpty || carotidRightIsNotEmpty) {
- children.add(_buildCarotidTabs());
- if (controller.state.carotidVAS == CarotidScanTypeEnum.CarotidLeft) {
- children.add(_buildGridCloudImagesView(
- imageWidth, controller.state.cloudCarotidLeftImages));
- } else if (controller.state.carotidVAS ==
- CarotidScanTypeEnum.CarotidRight) {
- children.add(_buildGridCloudImagesView(
- imageWidth, controller.state.cloudCarotidRightImages));
- }
- }
- if (children.length == 0) {
- return FExpanded(
- child: NoDataWidget(),
- );
- }
- return FExpanded(
- child: FContainer(
- width: leftWidth,
- color: Colors.white,
- child: FListView(
- shrinkWrap: true,
- children: children,
- ),
- ),
- );
- }
- FWidget _buildCarotidTabs() {
- bool carotidLeftIsNotEmpty =
- controller.state.cloudCarotidLeftImages.isNotEmpty;
- bool carotidRightIsNotEmpty =
- controller.state.cloudCarotidRightImages.isNotEmpty;
- return FContainer(
- margin: EdgeInsets.only(bottom: 5, top: 15, left: 10),
- child: FRow(
- children: [
- if (carotidLeftIsNotEmpty) ...[
- _buildInkWell(
- CarotidScanTypeEnum.CarotidLeft,
- ),
- ],
- if (carotidRightIsNotEmpty) ...[
- _buildInkWell(
- CarotidScanTypeEnum.CarotidRight,
- )
- ],
- ],
- ),
- );
- }
- FWidget _buildInkWell(
- CarotidScanTypeEnum carotidScanType,
- ) {
- final isChooseLeftCarotid = controller.state.carotidVAS == carotidScanType;
- final isLeftCarotid = carotidScanType == CarotidScanTypeEnum.CarotidLeft;
- return FInkWell(
- onTap: () {
- controller.state.carotidVAS = carotidScanType;
- },
- child: FContainer(
- decoration: BoxDecoration(
- border: Border.all(
- width: 0.5,
- color: Colors.grey,
- ),
- color: isChooseLeftCarotid
- ? const Color(0xff2c77e5)
- : Colors.transparent,
- borderRadius: isLeftCarotid
- ? BorderRadius.only(
- topLeft: Radius.circular(4),
- bottomLeft: Radius.circular(4),
- )
- : BorderRadius.only(
- topRight: Radius.circular(4),
- bottomRight: Radius.circular(4),
- ),
- ),
- padding: EdgeInsets.symmetric(
- horizontal: 20,
- vertical: 5,
- ),
- child: FText(
- isLeftCarotid
- ? i18nBook.remedical.leftNeck.t
- : i18nBook.remedical.rightNeck.t,
- style: TextStyle(
- color: isChooseLeftCarotid ? Colors.white : Colors.black,
- ),
- ),
- ),
- );
- }
- void _handleDoubleClick(
- String imageUrl,
- int index,
- String remedicalCode,
- String? remedicalAISelectedInfoCode, {
- VidImageSource vidImageSource = VidImageSource.Remedical,
- }) {
- controller.enterVidMeasurePage(
- imageUrl,
- index,
- remedicalCode,
- remedicalAISelectedInfoCode,
- );
- controller.getRemedicalList();
- }
- ///测量图像
- Widget _buildMeasureImageOrAiImageContentView(BuildContext context,
- [bool? isAiImage = false]) {
- final leftWidth = (MediaQuery.of(context).size.width - 90) * 7 / 15;
- final imageWidth = kIsMobile ? 300.0 : (leftWidth / 3 - 40);
- return Expanded(
- child: Container(
- width: leftWidth,
- child: Obx(() {
- return GridView.builder(
- controller: ScrollController(),
- itemCount: isAiImage!
- ? controller.state.remedicalAISelectedInfos.length
- : controller.state.remedicalMeasured.length,
- gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
- crossAxisCount: 2,
- ),
- itemBuilder: (BuildContext context, int index) {
- return _buildMeasurelImage(
- index,
- imageWidth,
- );
- },
- );
- }),
- ),
- );
- }
- /// AI图像
- Widget _buildAiImagesView(BuildContext context) {
- final leftWidth = (MediaQuery.of(context).size.width - 90) * 7 / 15;
- final imageWidth = leftWidth / 3 + 10;
- return Expanded(
- child: Container(
- width: leftWidth,
- color: Colors.white,
- child: Obx(
- () => FGridView.builder(
- controller: ScrollController(),
- itemCount: controller.state.remedicalAISelectedInfos.length,
- gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
- crossAxisCount: 2,
- ),
- itemBuilder: (BuildContext context, int index) {
- var imageRemedicalAISelectedInfo =
- controller.state.remedicalAISelectedInfos[index];
- var imageInfo = RemedicalInfoDTO(
- recordCode: imageRemedicalAISelectedInfo.recordCode,
- remedicalCode:
- imageRemedicalAISelectedInfo.remedicalAISelectedInfoCode,
- diagnosisConclusion:
- imageRemedicalAISelectedInfo.diagnosisConclusion,
- diagnosisOrgans: imageRemedicalAISelectedInfo.diagnosisOrgans,
- terminalImages: TerminalImageDTO(
- previewUrl: imageRemedicalAISelectedInfo.previewFileToken,
- imageUrl: imageRemedicalAISelectedInfo.aICdnFileToken,
- coverImageUrl: imageRemedicalAISelectedInfo.aIFileToken,
- originImageUrl: imageRemedicalAISelectedInfo.orginalFileToken,
- ),
- );
- var terminalImage =
- imageInfo.terminalImages ?? TerminalImageDTO();
- return CloudImageItem(
- index,
- imageInfo,
- onTap: () {
- var coverImageUrl = terminalImage.coverImageUrl ?? '';
- if (coverImageUrl.isNullOrEmpty) {
- coverImageUrl = terminalImage.previewUrl ?? '';
- }
- controller.onSelect.emit(this, coverImageUrl);
- final selectedImages = controller.state.selectedImages;
- if (selectedImages.contains(coverImageUrl)) {
- controller.state.selectedImages.remove(coverImageUrl);
- } else {
- controller.state.selectedImages.add(coverImageUrl);
- }
- },
- onDoubleTap: () {
- _handleDoubleClick(
- terminalImage.imageUrl ?? '',
- index,
- imageInfo.remedicalCode ?? '',
- imageRemedicalAISelectedInfo.remedicalAISelectedInfoCode,
- vidImageSource: VidImageSource.AiResultModifier,
- );
- },
- imageWidth: imageWidth,
- // description: translation_delegate_helper.translateDescription(
- // imageInfo.application,
- // imageInfo.applicationCategory,
- // ),
- isSelected: controller.state.selectedImages
- .contains(terminalImage.coverImageUrl),
- onSelectedInputChange: controller.onSelectedInputChange,
- );
- },
- ),
- ),
- ),
- );
- }
- /// 单个图像
- Widget _buildMeasurelImage(
- int index,
- double imageWidth,
- ) {
- return Obx(() {
- final measureImage = controller.state.remedicalMeasured[index];
- final fileToken = measureImage.measuredFileToken ?? '';
- final previewFileToken = measureImage.previewFileToken ?? '';
- bool _isSelect = controller.state.selectedImages.contains(fileToken);
- return FStack(
- children: [
- FMouseRegion(
- cursor: SystemMouseCursors.click,
- child: FGestureDetector(
- businessParent: this.widget.businessParent,
- name: "measureImage:$index",
- onTap: () {
- controller.onMersureImageTap(fileToken, index);
- },
- child: FContainer(
- decoration: BoxDecoration(
- border: Border.all(
- width: 4,
- color: _isSelect
- ? const Color(0xff2c77e5)
- : Colors.transparent),
- color: Colors.black,
- ),
- width: imageWidth,
- height: imageWidth,
- margin: const EdgeInsets.all(10),
- child: FImage.network(
- previewFileToken,
- fit: BoxFit.fitWidth,
- width: imageWidth,
- height: imageWidth,
- ),
- ),
- ),
- ),
- FPositioned(
- left: 20,
- top: 15,
- child: FText(
- (index + 1).toString(),
- style: TextStyle(color: Colors.white),
- ),
- ),
- ],
- );
- });
- }
- FWidget _buildGridCloudImagesView(
- double imageWidth, List<RemedicalInfoDTO> remedicalItemList) {
- return FGridView.builder(
- itemCount: remedicalItemList.length,
- shrinkWrap: true,
- gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
- crossAxisCount: 2,
- ),
- itemBuilder: (BuildContext context, int index) {
- var imageInfo = remedicalItemList[index];
- var terminalImage =
- remedicalItemList[index].terminalImages ?? TerminalImageDTO();
- return Obx(() {
- bool isSelected = controller.state.selectedImages
- .contains(terminalImage.coverImageUrl);
- return CloudImageItem(
- index,
- imageInfo,
- onTap: () {
- var coverImageUrl = terminalImage.coverImageUrl ?? '';
- if (coverImageUrl.isNullOrEmpty) {
- coverImageUrl = terminalImage.previewUrl ?? '';
- }
- controller.onSelect.emit(this, coverImageUrl);
- final selectedImages = controller.state.selectedImages;
- if (selectedImages.contains(coverImageUrl)) {
- controller.state.selectedImages.remove(coverImageUrl);
- } else {
- controller.state.selectedImages.add(coverImageUrl);
- }
- },
- onDoubleTap: () {
- _handleDoubleClick(
- terminalImage.imageUrl ?? '',
- index,
- imageInfo.remedicalCode ?? '',
- imageInfo.remedicalCode ?? '',
- );
- },
- imageWidth: imageWidth,
- // description: translation_delegate_helper.translateDescription(
- // imageInfo.application,
- // imageInfo.applicationCategory,
- // ),
- isSelected: isSelected,
- onSelectedInputChange: controller.onSelectedInputChange,
- );
- });
- },
- );
- }
- Widget _buildBodyView() {
- var padding = EdgeInsets.symmetric(vertical: 8, horizontal: 0);
- final allWidth = MediaQuery.of(context).size.width;
- // 930 = 左侧菜单栏(90)+ 报告编辑区域(540)+ 分割线(30)
- final fittedWidth = allWidth - (kIsMobile ? 670 : 930);
- // 600 是图像区域最大宽度限制,防止在大分辨率下,图像区域过大造成不美观
- const maxWidth = 400.0;
- final imagePoolWidth = min(fittedWidth, maxWidth);
- return Container(
- decoration: BoxDecoration(
- border: Border.all(
- width: 1,
- color: Colors.grey,
- )),
- child: Row(
- children: [
- Expanded(child: _buildReportContentView(context)),
- const VerticalDivider(
- width: 1,
- color: Colors.grey,
- ),
- const SizedBox(width: 10),
- Container(
- width: imagePoolWidth,
- child: Column(
- children: [
- const SizedBox(height: 10),
- Container(
- child: Obx(
- () {
- return TabButtonGroup(
- [
- TabButton(
- padding: padding,
- tabTitle: i18nBook.remedical.cloudImageShort.t,
- index: 0,
- activeIndex: controller.state.selectImageTabIndex,
- onCallbackTap: () {
- controller.state.selectImageTabIndex = 0;
- RPCBridge.ins.source = VidImageSource.Remedical;
- },
- ),
- FSizedBox(width: 10),
- TabButton(
- padding: padding,
- tabTitle: i18nBook.measure.measureImageShort.t,
- index: 1,
- activeIndex: controller.state.selectImageTabIndex,
- onCallbackTap: () {
- controller.remedicalsController
- .findRemedicalMeasuredInfoAsync();
- controller.state.selectImageTabIndex = 1;
- },
- ),
- if (controller.state.consultationCode.isNotEmpty) ...[
- FSizedBox(width: 10),
- TabButton(
- padding: padding,
- tabTitle: i18nBook.realTimeConsultation
- .consultationImageShort.t,
- index: 2,
- activeIndex: controller.state.selectImageTabIndex,
- onCallbackTap: () {
- controller.state.selectImageTabIndex = 2;
- },
- )
- ],
- ],
- );
- },
- ),
- ),
- _buildImagsView(context),
- const SizedBox(height: 5),
- _buildOperateLine(),
- const SizedBox(height: 15),
- ],
- ),
- ),
- ],
- ),
- );
- }
- }
|