Ver Fonte

1、新增随访检测需求

guanxinyi há 1 ano atrás
pai
commit
3b2ad16634

+ 3 - 0
lib/managers/follow_up.dart

@@ -1,3 +1,4 @@
+import 'package:fis_common/event/event_type.dart';
 import 'package:fis_jsonrpc/rpc.dart';
 import 'package:vitalapp/managers/interfaces/follow_up.dart';
 import 'package:vitalapp/rpc.dart';
@@ -78,4 +79,6 @@ class FollowUpManager implements IFollowUpManager {
       return null;
     }
   }
+
+  final FEventHandler<String> onFollowMedicalData = FEventHandler<String>();
 }

+ 3 - 0
lib/managers/interfaces/follow_up.dart

@@ -1,8 +1,11 @@
+import 'package:fis_common/event/event_type.dart';
 import 'package:fis_jsonrpc/rpc.dart';
 import 'package:vitalapp/managers/interfaces/base.dart';
 
 /// 随访管理
 abstract class IFollowUpManager implements IManager {
+  final FEventHandler<String> onFollowMedicalData = FEventHandler<String>();
+
   /// 创建随访
   Future<String> createFollowUp(CreateFollowUpRequest createFollowUpRequest);
 

+ 1 - 1
lib/pages/check/follow_up/view.dart

@@ -69,7 +69,7 @@ class FollowUpPage extends GetView<FollowUpController> {
               await controller.createFollowUp(key, templateCode, data);
           return result;
         },
-        followUpWidget: const FollowUpFrom(),
+        followUpWidget: FollowUpFrom(cardKey: key),
       ),
       transition: Transition.rightToLeft,
     );

+ 35 - 1
lib/pages/check/follow_up/widgets/follow_up_from.dart

@@ -1,3 +1,5 @@
+import 'dart:convert';
+
 import 'package:fis_jsonrpc/rpc.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
@@ -6,19 +8,29 @@ import 'package:image_picker/image_picker.dart';
 import 'package:intl/intl.dart';
 import 'package:vitalapp/architecture/app_parameters.dart';
 import 'package:vitalapp/architecture/storage/storage.dart';
+import 'package:vitalapp/components/appbar.dart';
+import 'package:vitalapp/components/button.dart';
 import 'package:vitalapp/components/cell.dart';
 import 'package:vitalapp/components/dialog_date.dart';
 import 'package:vitalapp/components/dialog_select.dart';
 import 'package:vitalapp/consts/rpc_enum_labels.dart';
 import 'package:vitalapp/consts/styles.dart';
+import 'package:vitalapp/managers/interfaces/follow_up.dart';
 import 'package:vitalapp/pages/check/follow_up/controller.dart';
+import 'package:vitalapp/pages/medical/views/follow_medical.dart';
 import 'package:vitalapp/rpc.dart';
 import 'package:vitalapp/store/store.dart';
 
 import '../models/select_model.dart';
 
 class FollowUpFrom extends GetView<FollowUpController> {
-  const FollowUpFrom({super.key});
+  FollowUpFrom({
+    super.key,
+    required this.cardKey,
+  });
+  final String cardKey;
+
+  final _followUpManager = Get.find<IFollowUpManager>();
 
   @override
   Widget build(BuildContext context) {
@@ -42,6 +54,28 @@ class FollowUpFrom extends GetView<FollowUpController> {
             child: Column(
               children: [
                 _buildFrom(),
+                VButton(
+                  onTap: () async {
+                    Map<String, dynamic>? result =
+                        await Get.dialog<Map<String, dynamic>>(
+                      Scaffold(
+                        body: FollowMedicalPage(cardKey: cardKey),
+                        appBar: VAppBar(
+                          titleText: "检测",
+                        ),
+                      ),
+                    );
+                    _followUpManager.onFollowMedicalData
+                        .emit(this, jsonEncode(result!));
+
+                    // /// TODO BAKA 需要优化
+                    // controller.followUpMedicalData = result;
+                  },
+                  child: Text(
+                    '检测',
+                    style: TextStyle(fontSize: 26),
+                  ),
+                ),
               ],
             )),
       ),

+ 11 - 4
lib/pages/check/follow_up_record/controller.dart

@@ -14,6 +14,7 @@ import 'package:vitalapp/pages/check/follow_up/controller.dart';
 import 'package:vitalapp/pages/check/follow_up/widgets/follow_up_from.dart';
 import 'package:vitalapp/pages/check/follow_up_record/state.dart';
 import 'package:vitalapp/pages/check/widgets/configurable_card.dart';
+import 'package:vitalapp/pages/medical/controller.dart';
 import 'package:vitalapp/store/store.dart';
 
 class FollowUpRecordController extends FControllerBase {
@@ -23,13 +24,13 @@ class FollowUpRecordController extends FControllerBase {
   final String followUpType;
   final state = FollowUpRecordState();
   final _followUpManager = Get.find<IFollowUpManager>();
+  final followUpController = Get.put(FollowUpController());
+
   late String patientCode;
   late String patientName;
   late FollowUpController _followUpController;
   List<List> offlineSyncTemp = [];
 
-  final followUpController = Get.put(FollowUpController());
-
   @override
   void onReady() {
     super.onReady();
@@ -231,14 +232,18 @@ class FollowUpRecordController extends FControllerBase {
   }
 
   toCheckPage(FollowUpRecordDataDTO dataDTO) async {
+    await Get.put(MedicalController());
+
     _followUpController.state.followUpTime = dataDTO.followUpTime;
     _followUpController.state.nextFollowUpTime = dataDTO.nextFollowUpTime;
     _followUpController.state.followUpMode = dataDTO.followUpMode;
-    Get.to(
+    await Get.to(
       ConfigurableCard(
         cardKey: dataDTO.key!,
         examData: dataDTO.followUpData,
-        followUpWidget: const FollowUpFrom(),
+        followUpWidget: FollowUpFrom(
+          cardKey: dataDTO.key!,
+        ),
         patientCode: patientCode,
         callBack: (key, code, data) async {
           await updateFollowUp(key, dataDTO.code, data);
@@ -248,6 +253,8 @@ class FollowUpRecordController extends FControllerBase {
       ),
       transition: Transition.rightToLeft,
     );
+    await Get.find<MedicalController>().initRecordDataState();
+    await Get.delete<MedicalController>();
   }
 
   final Map<String, String> followUpKeyValue = {

+ 9 - 2
lib/pages/check/follow_up_record/view.dart

@@ -9,6 +9,7 @@ import 'package:vitalapp/global.dart';
 import 'package:vitalapp/pages/check/follow_up/widgets/follow_up_from.dart';
 import 'package:vitalapp/pages/check/follow_up_record/controller.dart';
 import 'package:vitalapp/pages/check/widgets/configurable_card.dart';
+import 'package:vitalapp/pages/medical/controller.dart';
 import 'package:vitalapp/pages/patient/list/widgets/status.dart';
 import 'package:vitalapp/pages/widgets/record_common_item.dart';
 
@@ -18,7 +19,6 @@ class FollowUpRecordPage extends GetView<FollowUpRecordController> {
     required this.followUpType,
   }) : super(key: key);
   final String followUpType;
-
   @override
   Widget build(BuildContext context) {
     return GetBuilder(
@@ -63,6 +63,8 @@ class FollowUpRecordPage extends GetView<FollowUpRecordController> {
   }
 
   void _changePage(String key) async {
+    /// TODO BAKA 急需求 后面改掉
+    await Get.put(MedicalController());
     controller.followUpController.state.followUpTime = DateTime.now();
     controller.followUpController.state.nextFollowUpTime = null;
     controller.followUpController.state.followUpMode =
@@ -76,11 +78,16 @@ class FollowUpRecordPage extends GetView<FollowUpRecordController> {
               .createFollowUp(key, templateCode, data);
           return result;
         },
-        followUpWidget: const FollowUpFrom(),
+        followUpWidget: FollowUpFrom(
+          cardKey: key,
+        ),
       ),
       transition: Transition.rightToLeft,
     );
     await controller.getFollowUpRecordList();
+    await Get.find<MedicalController>().initRecordDataState();
+
+    await Get.delete<MedicalController>();
   }
 
   Widget _buildDiagram() {

+ 44 - 4
lib/pages/check/widgets/configurable_card.dart

@@ -8,9 +8,9 @@ import 'package:vitalapp/components/dialog_input.dart';
 import 'package:vitalapp/components/dialog_number.dart';
 import 'package:vitalapp/components/dynamic_drawer.dart';
 import 'package:vitalapp/managers/interfaces/cachedRecord.dart';
+import 'package:vitalapp/managers/interfaces/follow_up.dart';
 import 'package:vitalapp/managers/interfaces/template.dart';
 import 'package:vitalapp/pages/check/models/form.dart';
-import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_blood_pressure.dart';
 import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_blood_sugar.dart';
 import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_body_temperature.dart';
 import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_body_weight.dart';
@@ -27,6 +27,7 @@ import 'package:vitalapp/pages/check/widgets/device_controller.dart';
 import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_table.dart';
 import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_toxic_substance.dart';
 import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_urinalys.dart';
+import 'package:vitalapp/pages/check/widgets/exam_configurable/follow_blood_pressure.dart';
 import 'package:vitalapp/pages/check/widgets/exam_table/homecare_bed_history_from.dart';
 import 'package:vitalapp/pages/check/widgets/exam_table/hospitalization_history_from.dart';
 import 'package:vitalapp/pages/check/widgets/exam_table/inoculate_history_from.dart';
@@ -67,6 +68,9 @@ class _ConfigurableFormState extends State<ConfigurableCard> {
 
   final _templateManager = Get.find<ITemplateManager>();
   final _cachedRecordManager = Get.find<ICachedRecordManager>();
+  final _followUpManager = Get.find<IFollowUpManager>();
+
+  // _followUpManager.onFollowMedicalData
   final arrowHeight = math.tan(120 / 180) * 19;
   List<String> deviceList = ['Temp', 'GLU', 'NIBP', 'SpO2', 'BMI'];
   Map<String, dynamic> deviceCached = {};
@@ -87,17 +91,18 @@ class _ConfigurableFormState extends State<ConfigurableCard> {
   @override
   void initState() {
     Get.put(DeviceController());
-
-    super.initState();
     WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
       if (mounted) {
         initTemplate();
       }
     });
+    _followUpManager.onFollowMedicalData.addListener(_setFollowUpData);
+    super.initState();
   }
 
   @override
   void dispose() {
+    _followUpManager.onFollowMedicalData.removeListener(_setFollowUpData);
     super.dispose();
   }
 
@@ -396,6 +401,41 @@ class _ConfigurableFormState extends State<ConfigurableCard> {
     );
   }
 
+  void _setFollowUpData(sender, e) {
+    if (mounted) {
+      print(e);
+      Map<String, dynamic> followUpData = jsonDecode(e);
+      followUpData.forEach((key, value) {
+        if (key == "BMI") {
+          formValue.addAll(value);
+        }
+        if (key == "NIBP") {
+          /// 之前区分左右,后面重新设计
+          List bloodValue = [];
+          if (value["Sbp"] != null &&
+              value["Dbp"] != null &&
+              value["Pulse_Beat"] != null) {
+            bloodValue.addAll([value["Sbp"], value["Dbp"]]);
+            Map<String, dynamic> nibpData = {
+              "Blood": {"Blood": jsonEncode(bloodValue)},
+              "Heart_Rate": value["Pulse_Beat"],
+            };
+            formValue.addAll(nibpData);
+          }
+        }
+        if (key == "GLU") {
+          Map<String, dynamic> sugar = {
+            "Blood_Sugar": value["sugar"],
+          };
+          formValue.addAll(sugar);
+        }
+      });
+      setState(() {});
+
+      print(e);
+    }
+  }
+
   /// title标签
   Widget _buildTitleList() {
     return Wrap(
@@ -719,7 +759,7 @@ class _ConfigurableFormState extends State<ConfigurableCard> {
       // setState(() {});
     }
 
-    return ExamBloodPressure(
+    return FollowBloodPressure(
       currentValue: currentValue,
       bloodPressure: bloodPressure,
     );

+ 314 - 0
lib/pages/check/widgets/exam_configurable/follow_blood_pressure.dart

@@ -0,0 +1,314 @@
+import 'dart:convert';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:get/get.dart';
+
+import 'package:vitalapp/components/alert_dialog.dart';
+import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_card.dart';
+import 'package:vitalapp/pages/medical/models/item.dart';
+import 'package:vnote_device_plugin/models/exams/nibp.dart';
+
+/// 小弹窗输入
+class VDialogBloodPressure extends StatelessWidget {
+  /// 标题
+  final String? title;
+
+  /// 描述
+  final String? description;
+
+  /// 输入占位符
+  final String? placeholder;
+
+  /// 初始值
+  final List<String>? initialValue;
+
+  const VDialogBloodPressure({
+    super.key,
+    this.title,
+    this.description,
+    this.placeholder,
+    this.initialValue,
+  });
+
+  Future<String?> show<String>() => VAlertDialog.showDialog<String>(this);
+
+  @override
+  Widget build(BuildContext context) {
+    final controller1 = TextEditingController(text: initialValue?.first);
+    final controller2 = TextEditingController(text: initialValue?.last);
+    return VAlertDialog(
+      title: title,
+      width: 440,
+      contentPadding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24),
+      content: _buildContent(context, controller1, controller2),
+      showCancel: true,
+      onConfirm: () {
+        Get.back(result: json.encode([controller1.text, controller2.text]));
+      },
+    );
+  }
+
+  Widget _buildContent(
+    BuildContext context,
+    TextEditingController controller1,
+    TextEditingController controller2,
+  ) {
+    final children = <Widget>[];
+    if (description != null) {
+      children.add(
+        Padding(
+          padding: const EdgeInsets.symmetric(horizontal: 4),
+          child: Text(
+            description!,
+            style: const TextStyle(color: Colors.black87, fontSize: 18),
+          ),
+        ),
+      );
+      children.add(const SizedBox(height: 8));
+    } else {
+      children.add(const SizedBox(height: 12));
+    }
+    children.add(_buildInputWidget(context, controller1, controller2));
+    return SingleChildScrollView(
+      child: Column(
+        mainAxisSize: MainAxisSize.min,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: children,
+      ),
+    );
+  }
+
+  Widget _buildInputWidget(
+    BuildContext context,
+    TextEditingController controller1,
+    TextEditingController controller2,
+  ) {
+    const fontSize = 20.0;
+    return Row(
+      children: [
+        _buildItem(context, controller1, '高压'),
+        const Text(
+          '/',
+          style: TextStyle(fontSize: fontSize),
+        ),
+        _buildItem(context, controller2, '低压'),
+      ],
+    );
+  }
+
+  Widget _buildItem(
+    BuildContext context,
+    TextEditingController controller,
+    String hintText,
+  ) {
+    const fontSize = 20.0;
+    const height = 56.0;
+    return Expanded(
+      child: SizedBox(
+        height: height,
+        child: TextField(
+          controller: controller,
+          readOnly: false,
+          autofocus: true,
+          keyboardType: const TextInputType.numberWithOptions(
+              decimal: true), // 允许输入数字和小数点
+          inputFormatters: [
+            FilteringTextInputFormatter.allow(
+              RegExp(r'^\d+\.?\d{0,2}'),
+            ), // 只允许输入数字和小数点,俩位小数
+          ],
+          style: const TextStyle(fontSize: fontSize),
+          decoration: InputDecoration(
+            border: const UnderlineInputBorder(
+              borderRadius: BorderRadius.zero,
+              borderSide: BorderSide(),
+            ),
+            enabledBorder: const UnderlineInputBorder(
+              borderRadius: BorderRadius.zero,
+              borderSide: BorderSide(
+                color: Colors.black54,
+              ),
+            ),
+            focusedBorder: UnderlineInputBorder(
+              borderRadius: BorderRadius.zero,
+              borderSide: BorderSide(
+                color: Theme.of(context).primaryColor.withOpacity(.4),
+              ),
+            ),
+            filled: true,
+            fillColor: Colors.white,
+            contentPadding: const EdgeInsets.symmetric(
+              vertical: (height - fontSize * 1.2) / 2,
+              horizontal: 8,
+            ),
+            hintStyle: const TextStyle(fontSize: fontSize),
+            labelStyle: const TextStyle(fontSize: fontSize),
+            hintText: hintText,
+            isCollapsed: true,
+          ),
+          onSubmitted: (value) {
+            print(value);
+            Get.back(result: value);
+          },
+        ),
+      ),
+    );
+  }
+}
+
+/// TODO 优化血压组件,存储需要重新设计
+// ignore: must_be_immutable
+class FollowBloodPressure extends StatefulWidget {
+  FollowBloodPressure({
+    super.key,
+    required this.currentValue,
+    required this.bloodPressure,
+  });
+  Map currentValue;
+  Function(Map) bloodPressure;
+  @override
+  State<FollowBloodPressure> createState() => _FollowBloodPressureState();
+}
+
+class _FollowBloodPressureState extends State<FollowBloodPressure> {
+  PressureStatus pressureStatus = PressureStatus.left;
+
+  NibpExamValue? value;
+  NibpExamValue? nibpExamValue;
+
+  @override
+  void initState() {
+    if (widget.currentValue['Blood'] != null) {
+      String currentValue = widget.currentValue['Blood'];
+      nibpExamValue = NibpExamValue(
+        systolicPressure: int.parse(jsonDecode(currentValue).first.toString()),
+        diastolicPressure: int.parse(jsonDecode(currentValue).last.toString()),
+        pulse: 0,
+      );
+    }
+
+    super.initState();
+  }
+
+  @override
+  void dispose() {
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Stack(
+      children: [
+        ExamCard(
+          content: Column(
+            mainAxisAlignment: MainAxisAlignment.start,
+            children: [
+              InkWell(
+                child: _SideBar(
+                  title: '血压',
+                  value: _buildResult(nibpExamValue),
+                  unit: 'mmHg',
+                ),
+                onTap: () async {
+                  String? result = await VDialogBloodPressure(
+                    title: '血压',
+                    initialValue: [
+                      nibpExamValue?.systolicPressure.toString() ?? '',
+                      nibpExamValue?.diastolicPressure.toString() ?? '',
+                    ],
+                  ).show();
+                  if (result != null) {
+                    nibpExamValue = NibpExamValue(
+                      diastolicPressure: int.parse(jsonDecode(result).last),
+                      systolicPressure: int.parse(jsonDecode(result).first),
+                      pulse: 0,
+                    );
+                    widget.bloodPressure({
+                      "Blood": result,
+                    });
+                  }
+                  setState(() {});
+                },
+              ),
+            ],
+          ),
+        ),
+      ],
+    );
+  }
+
+  Widget _buildResult(NibpExamValue? nibpExamValue) {
+    const textStyle = TextStyle(fontSize: 26, color: Colors.black);
+
+    return Row(
+      children: [
+        Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          mainAxisSize: MainAxisSize.min,
+          crossAxisAlignment: CrossAxisAlignment.center,
+          children: [
+            Align(
+              alignment: Alignment.centerLeft,
+              child: Text(
+                nibpExamValue?.systolicPressure.toString() ?? '',
+                style: textStyle,
+              ),
+            ),
+            Align(
+              alignment: Alignment.centerRight,
+              child: Text(
+                nibpExamValue?.diastolicPressure.toString() ?? '',
+                style: textStyle,
+              ),
+            ),
+          ],
+        ),
+      ],
+    );
+  }
+}
+
+class _SideBar extends StatelessWidget {
+  final String title;
+  final Widget value;
+  final String unit;
+
+  const _SideBar({
+    required this.title,
+    required this.value,
+    required this.unit,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Row(
+      mainAxisAlignment: MainAxisAlignment.spaceBetween,
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        Container(
+          // padding: const EdgeInsets.symmetric(horizontal: 30),
+          child: Text(
+            title,
+            style: const TextStyle(
+              fontSize: 26,
+            ),
+          ),
+        ),
+        Container(
+          alignment: Alignment.bottomRight,
+          padding: const EdgeInsets.only(
+            right: 30,
+          ),
+          child: Row(
+            mainAxisAlignment: MainAxisAlignment.end,
+            crossAxisAlignment: CrossAxisAlignment.end,
+            children: [
+              value,
+            ],
+          ),
+        ),
+      ],
+    );
+  }
+}

+ 261 - 0
lib/pages/medical/views/follow_medical.dart

@@ -0,0 +1,261 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:vitalapp/pages/medical/models/item.dart';
+import 'package:vitalapp/pages/medical/widgets/heart_rate.dart';
+import 'package:vitalapp/pages/medical/widgets/twelve_ecg.dart';
+import 'package:vitalapp/pages/medical/widgets/urinalysis.dart';
+import 'package:vnote_device_plugin/consts/types.dart';
+import 'package:vitalapp/pages/medical/controller.dart';
+import 'package:vitalapp/pages/medical/widgets/blood_pressure.dart';
+import 'package:vitalapp/pages/medical/widgets/blood_sugar.dart';
+import 'package:vitalapp/pages/medical/widgets/body_temperature.dart';
+import 'package:vitalapp/pages/medical/widgets/body_bmi.dart';
+import 'package:vitalapp/pages/medical/widgets/bool_oxygen.dart';
+import 'package:vitalapp/store/store.dart';
+
+class FollowMedicalPage extends GetView<MedicalController> {
+  const FollowMedicalPage({
+    super.key,
+    required this.cardKey,
+  });
+  final String cardKey;
+
+  @override
+  Widget build(BuildContext context) {
+    /// TODO 暂时写死
+    if (cardKey == 'TNB') {
+      controller.state.currentTab = DeviceTypes.SUGAR;
+    } else {
+      controller.state.currentTab = DeviceTypes.NIBP;
+    }
+    return Scaffold(
+      resizeToAvoidBottomInset: false,
+      body: SizedBox(height: double.maxFinite, child: _buildMedical()),
+      floatingActionButton: _buildSaveButton(context),
+    );
+  }
+
+  Widget _buildMedical() {
+    return Row(
+      children: [
+        _buildMedicalMenus(),
+        Obx(
+          () => _buildDeviceImage(controller.state.currentTab),
+        ),
+        Obx(
+          () => _buildMedicalInput(controller.state.currentTab),
+        ),
+      ],
+    );
+  }
+
+  Widget _buildMedicalMenus() {
+    return Expanded(
+      flex: 3,
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.center,
+        children: [
+          ListView(
+            shrinkWrap: true,
+            children: [
+              if (cardKey == 'TNB')
+                MedicalItem(key: DeviceTypes.SUGAR, diagnosticItem: '血糖'),
+              MedicalItem(key: DeviceTypes.NIBP, diagnosticItem: '血压'),
+              MedicalItem(key: DeviceTypes.WEIGHTHEIGHT, diagnosticItem: 'BMI'),
+            ]
+                .map(
+                  (e) => Material(
+                    borderRadius: const BorderRadius.only(
+                      topRight: Radius.circular(30),
+                      bottomRight: Radius.circular(30),
+                    ),
+                    child: Ink(
+                      decoration: const BoxDecoration(
+                        borderRadius: BorderRadius.only(
+                          topRight: Radius.circular(30),
+                          bottomRight: Radius.circular(30),
+                        ),
+                      ),
+                      child: InkWell(
+                          borderRadius: const BorderRadius.only(
+                            topRight: Radius.circular(30),
+                            bottomRight: Radius.circular(30),
+                          ),
+                          onTap: () {
+                            controller.state.currentTab = e.key;
+                          },
+                          child: Obx(
+                            () => _SideBar(
+                              title: e.diagnosticItem,
+                              isActive: controller.state.currentTab == e.key,
+                            ),
+                          )),
+                    ),
+                  ),
+                )
+                .toList(),
+          ),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildMedicalInput(String? currentTab) {
+    return Expanded(
+      flex: currentTab == DeviceTypes.TWELVEHEART ? 18 : 11,
+      child: Stack(
+        children: [
+          Container(
+            padding: const EdgeInsets.all(16),
+            child: Column(
+              children: [
+                _buildContent(),
+              ],
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+
+  String _deviceImageUrl(String? currentTab) {
+    switch (currentTab) {
+      case DeviceTypes.TEMP:
+        return 'assets/images/healthCheck/temp.png';
+      case DeviceTypes.SUGAR:
+        return 'assets/images/healthCheck/sugar.png';
+      case DeviceTypes.NIBP:
+        return 'assets/images/healthCheck/nibp.png';
+      case DeviceTypes.SPO2:
+        return 'assets/images/healthCheck/spo2.png';
+      case DeviceTypes.WEIGHTHEIGHT:
+        return 'assets/images/healthCheck/bmi.png';
+      case DeviceTypes.URINE:
+        return 'assets/images/healthCheck/urine.png';
+
+      default:
+        return 'assets/images/exam/normalMeasurementChart.png';
+    }
+  }
+
+  Widget _buildDeviceImage(String? currentTab) {
+    if (currentTab == DeviceTypes.TWELVEHEART) {
+      return const SizedBox();
+    }
+    return Expanded(
+      flex: 7,
+      child: Container(
+        alignment: Alignment.topCenter,
+        margin: const EdgeInsets.all(16).copyWith(top: 10),
+        child: Obx(
+          () => ClipRect(
+            child: Align(
+              alignment: Alignment.bottomCenter,
+              heightFactor: 0.8,
+              child: controller.state.currentTab != null
+                  ? Image.asset(
+                      _deviceImageUrl(controller.state.currentTab),
+                      height: double.infinity,
+                      fit: BoxFit.contain, // 设置图像的适应方式
+                    )
+                  : Container(),
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+
+  Widget _buildSaveButton(BuildContext context) {
+    return Obx(() {
+      if (Store.user.currentSelectPatientInfo == null) {
+        return const SizedBox();
+      }
+      return FloatingActionButton.extended(
+        backgroundColor: Theme.of(context).primaryColor,
+        onPressed: () async {
+          Get.back(result: controller.diagnosisDataValue);
+        },
+        label: const SizedBox(
+          width: 240,
+          height: 60,
+          child: Center(
+            child: Text(
+              '提交',
+              style: TextStyle(
+                fontSize: 26,
+                color: Colors.white,
+              ),
+            ),
+          ),
+        ),
+      );
+    });
+  }
+
+  Widget _buildContent() {
+    return Obx(() {
+      switch (controller.state.currentTab) {
+        case DeviceTypes.TEMP:
+          return const BodyTemperature();
+        case DeviceTypes.SUGAR:
+          return const BloodSugar();
+        case DeviceTypes.NIBP:
+          return const BloodPressure();
+        case DeviceTypes.SPO2:
+          return const BloodOxygen();
+        case DeviceTypes.WEIGHTHEIGHT:
+          return const BodyWeight();
+        case DeviceTypes.URINE:
+          return const Urinalysis();
+        case DeviceTypes.HEART:
+          return const HeartRate();
+        case DeviceTypes.TWELVEHEART:
+          return const TwelveHeartRate();
+
+        default:
+          return const SizedBox();
+      }
+    });
+  }
+}
+
+class _SideBar extends StatelessWidget {
+  const _SideBar({
+    required this.title,
+    this.isActive,
+  });
+
+  final String title;
+  final bool? isActive;
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      alignment: Alignment.centerLeft,
+      width: 156,
+      child: Container(
+        margin: const EdgeInsets.only(bottom: 2),
+        decoration: BoxDecoration(
+          color: isActive!
+              ? Theme.of(context).primaryColor
+              : Theme.of(context).primaryColor.withOpacity(.2),
+          borderRadius: const BorderRadius.only(
+            topRight: Radius.circular(30),
+            bottomRight: Radius.circular(30),
+          ),
+        ),
+        alignment: Alignment.center,
+        width: 156,
+        height: 60,
+        child: Text(
+          title,
+          style: TextStyle(
+            fontSize: 26,
+            color: isActive! ? Colors.white : Colors.black,
+          ),
+        ),
+      ),
+    );
+  }
+}

+ 6 - 4
lib/pages/medical/widgets/body_bmi.dart

@@ -63,10 +63,12 @@ class _ExamBodyWeightState extends State<BodyWeight> {
   }
 
   void releaseListeners() {
-    worker!.connectErrorEvent.removeListener(_onConnectFail);
-    worker!.connectedEvent.removeListener(_onConnectSuccess);
-    worker!.successEvent.removeListener(_onSuccess);
-    worker!.disconnectedEvent.removeListener(_onDisconnected);
+    if (worker != null) {
+      worker!.connectErrorEvent.removeListener(_onConnectFail);
+      worker!.connectedEvent.removeListener(_onConnectSuccess);
+      worker!.successEvent.removeListener(_onSuccess);
+      worker!.disconnectedEvent.removeListener(_onDisconnected);
+    }
   }
 
   @override

+ 1 - 1
lib/pages/medical/widgets/side_bar.dart

@@ -30,7 +30,7 @@ class SideBar extends StatelessWidget {
         onTap: () => onTap?.call(),
         child: Container(
           margin: const EdgeInsets.only(top: 10),
-          padding: const EdgeInsets.symmetric(vertical: 20),
+          padding: const EdgeInsets.symmetric(vertical: 10),
           child: Row(
             mainAxisAlignment: MainAxisAlignment.spaceBetween,
             crossAxisAlignment: CrossAxisAlignment.start,