Browse Source

1、完善体检表单,优化右侧病人名组件

guanxinyi 1 year ago
parent
commit
f40b9e9a4c

+ 2 - 0
lib/architecture/types/index.dart

@@ -1 +1,3 @@
 export 'kv.dart';
+
+typedef ValueCallback<T> = void Function(T value);

+ 0 - 27
lib/components/floating_window/click_notification.dart

@@ -1,27 +0,0 @@
-/*
- * @Descripttion: 
- * @version: 
- * @Author: guanxiaoxin
- * @Date: 2023-10-18 15:33:09
- * @LastEditors: guanxiaoxin
- * @LastEditTime: 2023-10-18 15:33:25
- * @FilePath: \VNoteApp\lib\components\floating_window\click_notification.dart
- */
-import 'package:flutter/material.dart';
-
-/// [ClickNotification]列表项点击事件通知类
-class ClickNotification extends Notification {
-  ClickNotification(
-      {this.deletedIndex = -1,
-      this.clickIndex = -1,
-      this.changeWidget = false});
-
-  /// 触发了关闭事件的列表项索引
-  int deletedIndex = -1;
-
-  /// 触发了点击事件的列表项索引
-  int clickIndex = -1;
-
-  /// 是否触发了改变形态的操作
-  bool changeWidget = false;
-}

+ 0 - 423
lib/components/floating_window/floating_button.dart

@@ -1,423 +0,0 @@
-/*
- * @Descripttion: 
- * @version: 
- * @Author: guanxiaoxin
- * @Date: 2023-10-18 15:34:43
- * @LastEditors: guanxiaoxin
- * @LastEditTime: 2023-10-18 16:23:12
- * @FilePath: \VNoteApp\lib\components\floating_window\floating_button.dart
- */
-import 'dart:async';
-import 'dart:math';
-import 'dart:ui' as ui;
-
-import 'package:flutter/material.dart';
-import 'package:get/get.dart';
-import 'package:vnoteapp/components/floating_window/click_notification.dart';
-import 'package:vnoteapp/components/floating_window/floating_window_shared_data_widget.dart';
-
-class FloatingButton extends StatefulWidget {
-  const FloatingButton({super.key});
-
-  @override
-  _FloatingButtonState createState() => _FloatingButtonState();
-}
-
-class _FloatingButtonState extends State<FloatingButton>
-    with TickerProviderStateMixin {
-  /// [isPress] 按钮是否被按下
-  bool isPress = false;
-
-  /// [_controller] 返回动画控制器
-  AnimationController? _controller;
-
-  /// [_animation] 返回动画
-  Animation? _animation;
-
-  @override
-  Widget build(BuildContext context) {
-    /// 获取悬浮窗共享数据
-    var windowModel = FloatingWindowSharedDataWidget.of(context)!.data;
-    return Positioned(
-      left: windowModel.left,
-      top: windowModel.top,
-      child: Listener(
-        /// 按下后设[isPress]为true,绘制选中阴影
-        onPointerDown: (details) {
-          setState(() {
-            isPress = true;
-          });
-        },
-
-        /// 按下后设isPress为false,不绘制阴影
-        /// 放下后根据当前x坐标与1/2屏幕宽度比较,判断屏幕在屏幕左侧或右侧,设置返回边缘动画
-        /// 动画结束后设置isLeft的值,根据值绘制左/右边缘按钮
-        onPointerUp: (e) async {
-          setState(() {
-            isPress = false;
-          });
-
-          /// 获取屏幕信息
-          var pixelDetails = MediaQuery.of(context).size; //获取屏幕信息
-
-          /// 点击按钮,触发Widget改变事件
-          if (windowModel.isLeft &&
-              e.position.dx <= 50.0 &&
-              windowModel.isEdge) {
-            ClickNotification(changeWidget: true).dispatch(context);
-            return;
-          } else if (!windowModel.isLeft &&
-              e.position.dx >= pixelDetails.width - 50.0 &&
-              windowModel.isEdge) {
-            ClickNotification(changeWidget: true).dispatch(context);
-            return;
-          }
-
-          /// 触发返回动画
-          if (e.position.dx <= pixelDetails.width / 2) {
-            /// 申请动画资源
-            _controller = AnimationController(
-                vsync: this,
-                duration: const Duration(milliseconds: 100)); //0.1s动画
-            _animation =
-                Tween(begin: e.position.dx, end: 0.0).animate(_controller!)
-                  ..addListener(() {
-                    setState(() {
-                      /// 更新x坐标
-                      windowModel.left = _animation!.value;
-                    });
-                  });
-
-            /// 等待动画结束
-            await _controller!.forward();
-            _controller!.dispose();
-
-            /// 释放动画资源
-            setState(() {
-              windowModel.isLeft = true;
-
-              /// 按钮在屏幕左侧
-            });
-          } else {
-            /// 申请动画资源
-            _controller = AnimationController(
-                vsync: this,
-                duration: const Duration(milliseconds: 100)); //0.1动画
-            _animation =
-                Tween(begin: e.position.dx, end: pixelDetails.width - 50)
-                    .animate(_controller!) //返回右侧坐标需要减去自身宽度及50,因坐标以图形左上角为基点
-                  ..addListener(() {
-                    setState(() {
-                      windowModel.left = _animation!.value;
-
-                      /// 动画更新x坐标
-                    });
-                  });
-            await _controller!.forward();
-
-            /// 等待动画结束
-            _controller!.dispose();
-
-            /// 释放动画资源
-            setState(() {
-              windowModel.isLeft = false;
-
-              /// 按钮在屏幕右侧
-            });
-          }
-
-          setState(() {
-            windowModel.isEdge = true;
-
-            /// 按钮返回至边缘,更新按钮状态
-          });
-        },
-        child: GestureDetector(
-          /// 拖拽更新
-          onPanUpdate: (details) {
-            var pixelDetails = MediaQuery.of(context).size;
-
-            /// 获取屏幕信息
-            /// 拖拽后更新按钮信息,是否处于边缘
-            if (windowModel.left + details.delta.dx > 0 &&
-                windowModel.left + details.delta.dx < pixelDetails.width - 50) {
-              setState(() {
-                windowModel.isEdge = false;
-              });
-            } else {
-              setState(() {
-                windowModel.isEdge = true;
-              });
-            }
-
-            /// 拖拽更新坐标
-            setState(() {
-              windowModel.left += details.delta.dx;
-              windowModel.top += details.delta.dy;
-            });
-          },
-          child: FutureBuilder(
-            future: loadImageByProvider(
-                AssetImage(windowModel.dataList[0]['imageUrl'] ?? '')),
-            builder: (context, snapshot) => CustomPaint(
-              size: const Size(50.0, 50.0),
-              painter: FloatingButtonPainter(
-                  isLeft: windowModel.isLeft,
-                  isEdge: windowModel.isEdge,
-                  isPress: isPress,
-                  buttonImage: Image.asset('assets/images/avatar.png')),
-            ),
-          ),
-        ),
-      ),
-    );
-  }
-
-  /// 通过ImageProvider获取ui.image
-  Future<ui.Image> loadImageByProvider(
-    ImageProvider provider, {
-    ImageConfiguration config = ImageConfiguration.empty,
-  }) async {
-    Completer<ui.Image> completer = Completer<ui.Image>(); //完成的回调
-    ImageStreamListener listener;
-    ImageStream stream = provider.resolve(config); //获取图片流
-    listener = ImageStreamListener((ImageInfo frame, bool sync) {
-//监听
-      final ui.Image image = frame.image;
-      completer.complete(image); //完成
-      // stream.removeListener(listener); //移除监听
-    });
-    stream.addListener(listener); //添加监听
-    return completer.future; //返回
-  }
-}
-
-class FloatingButtonPainter extends CustomPainter {
-  FloatingButtonPainter(
-      {Key? key,
-      required this.isLeft,
-      required this.isEdge,
-      required this.isPress,
-      required this.buttonImage});
-
-  /// 按钮是否在屏幕左侧,屏幕宽度 / 2
-  final bool isLeft;
-
-  /// 按钮是否在屏幕边界,左/右边界
-  final bool isEdge;
-
-  /// 按钮是否被按下
-  final bool isPress;
-
-  /// 内按钮图片 ui.image
-  final Image buttonImage;
-
-  @override
-  void paint(Canvas canvas, Size size) {
-// TODO: implement paint
-    /// 按钮是否在边缘
-    if (isEdge) {
-      /// 按钮在屏幕左边或右边
-      if (isLeft) {
-        paintLeftEdgeButton(canvas, size);
-      } else {
-        paintRightEdgeButton(canvas, size);
-      }
-
-      /// 绘制右边缘按钮
-    } else {
-      paintCenterButton(canvas, size);
-
-      /// 绘制中心按钮
-    }
-  }
-
-  ///绘制左边界悬浮按钮
-  void paintLeftEdgeButton(Canvas canvas, Size size) {
-    ///绘制按钮内层
-    var paint = Paint()
-      ..isAntiAlias = false
-      ..style = PaintingStyle.fill
-      ..color = const Color.fromRGBO(0xF3, 0xF3, 0xF3, 0.9);
-//..color = Color.fromRGBO(0xDA,0xDA,0xDA,0.9);
-
-    /// path : 按钮内边缘路径
-    var path = Path()..moveTo(size.width / 2, size.height - 1.5);
-    path.lineTo(0.0, size.height - 1.5);
-    path.lineTo(0.0, 1.5);
-    path.lineTo(size.width / 2, 1.5);
-    Rect rect = Rect.fromCircle(
-        center: Offset(size.width / 2, size.height / 2), radius: 23.5);
-    path.arcTo(rect, pi * 1.5, pi, true);
-    canvas.drawPath(path, paint);
-
-    /// edgePath: 按钮外边缘路径,黑色线条
-    var edgePath = Path()..moveTo(size.width / 2, size.height);
-    edgePath.lineTo(0.0, size.height);
-    edgePath.lineTo(0.0, 0.0);
-    edgePath.lineTo(size.width / 2, 0.0);
-    Rect rect1 = Rect.fromCircle(
-        center: Offset(size.width / 2, size.height / 2), radius: 25);
-    edgePath.arcTo(rect1, pi * 1.5, pi, true);
-
-    paint
-      ..isAntiAlias = true
-      ..strokeWidth = 0.75
-      ..strokeCap = StrokeCap.round
-      ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 0.25)
-
-      /// 线条模糊
-      ..style = PaintingStyle.stroke
-      ..color = const Color.fromRGBO(0xCF, 0xCF, 0xCF, 1);
-    canvas.drawPath(edgePath, paint);
-
-    /// 按下则画阴影,表示选中
-    if (isPress) {
-      canvas.drawShadow(
-          edgePath, const Color.fromRGBO(0xDA, 0xDA, 0xDA, 0.3), 0, false);
-    }
-
-    if (buttonImage.isNull) return;
-
-    /// 绘制中间图标
-    paint = Paint();
-    canvas.save();
-
-    /// 剪裁前保存图层
-    RRect imageRRect = RRect.fromRectAndRadius(
-        Rect.fromLTWH(size.width / 2 - 17.5, size.width / 2 - 17.5, 35, 35),
-        const Radius.circular(17.5));
-    canvas.clipRRect(imageRRect);
-
-    /// 图片为圆形,圆形剪裁
-    canvas.drawColor(Colors.white, BlendMode.srcOver);
-
-    // // / 设置填充颜色为白色
-    // Rect srcRect = Rect.fromLTWH(
-    //     0.0, 0.0, buttonImage.width!.toDouble(), buttonImage.height!.toDouble());
-    // Rect dstRect =
-    //     Rect.fromLTWH(size.width / 2 - 17.5, size.height / 2 - 17.5, 35, 35);
-    // canvas.drawImageRect(buttonImage, srcRect, dstRect, paint);
-    canvas.restore();
-
-    /// 图片绘制完毕恢复图层
-  }
-
-  /// 绘制右边界按钮
-  void paintRightEdgeButton(Canvas canvas, Size size) {
-    var paint = Paint()
-      ..isAntiAlias = false
-      ..style = PaintingStyle.fill
-      ..color = const Color.fromRGBO(0xF3, 0xF3, 0xF3, 0.9);
-
-    var path = Path()..moveTo(size.width / 2, 1.5);
-    path.lineTo(size.width, 1.5);
-    path.lineTo(size.width, size.height - 1.5);
-    path.lineTo(size.width / 2, size.height - 1.5);
-
-    Rect rect = Rect.fromCircle(
-        center: Offset(size.width / 2, size.height / 2), radius: 23.5);
-    path.arcTo(rect, pi * 0.5, pi, true);
-
-    canvas.drawPath(path, paint);
-
-    /// 绘制
-
-    /// edgePath: 按钮外边缘路径
-    var edgePath = Path()..moveTo(size.width / 2, 0.0);
-    edgePath.lineTo(size.width, 0.0);
-    edgePath.lineTo(size.width, size.height);
-    edgePath.lineTo(size.width / 2, size.height);
-    Rect edgeRect = Rect.fromCircle(
-        center: Offset(size.width / 2, size.height / 2), radius: 25);
-    edgePath.arcTo(edgeRect, pi * 0.5, pi, true);
-
-    paint
-      ..isAntiAlias = true
-      ..strokeWidth = 0.75
-      ..strokeCap = StrokeCap.round
-      ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 0.25)
-      ..style = PaintingStyle.stroke
-      ..color = const Color.fromRGBO(0xCF, 0xCF, 0xCF, 1);
-    canvas.drawPath(edgePath, paint);
-
-    /// 如果按下则绘制阴影
-    if (isPress) {
-      canvas.drawShadow(
-          path, const Color.fromRGBO(0xDA, 0xDA, 0xDA, 0.3), 0, false);
-    }
-
-    /// 防止传入null
-    if (buttonImage.isNull) return;
-
-    /// 绘制中间图标
-    paint = Paint();
-    canvas.save();
-
-    /// 剪裁前保存图层
-    RRect imageRRect = RRect.fromRectAndRadius(
-        Rect.fromLTWH(size.width / 2 - 17.5, size.width / 2 - 17.5, 35, 35),
-        const Radius.circular(17.5));
-    canvas.clipRRect(imageRRect);
-
-    /// 图片为圆形,圆形剪裁
-    canvas.drawColor(Colors.white, BlendMode.srcOver);
-
-    // /// 设置填充颜色为白色
-    // Rect srcRect = Rect.fromLTWH(
-    //     0.0, 0.0, buttonImage.width.toDouble(), buttonImage.height.toDouble());
-    // Rect dstRect =
-    //     Rect.fromLTWH(size.width / 2 - 17.5, size.height / 2 - 17.5, 35, 35);
-    // canvas.drawImageRect(buttonImage, srcRect, dstRect, paint);
-    canvas.restore();
-
-    /// 图片绘制完毕恢复图层
-  }
-
-  /// 绘制中心按钮
-  void paintCenterButton(Canvas canvas, Size size) {
-    /// 绘制按钮内层
-    var paint = Paint()
-      ..isAntiAlias = false
-      ..style = PaintingStyle.fill
-      ..color = const Color.fromRGBO(0xF3, 0xF3, 0xF3, 0.9);
-    canvas.drawCircle(Offset(size.width / 2, size.height / 2), 23.5, paint);
-
-    /// 绘制按钮外层边线
-    paint
-      ..isAntiAlias = true
-      ..style = PaintingStyle.stroke
-      ..strokeWidth = 0.75
-      ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 0.25)
-      ..color = const Color.fromRGBO(0xCF, 0xCF, 0xCF, 1);
-    canvas.drawCircle(Offset(size.width / 2, size.height / 2), 25, paint);
-
-    /// 如果按下则绘制阴影
-    if (isPress) {
-      var circleRect = Rect.fromCircle(
-          center: Offset(size.width / 2, size.height / 2), radius: 25);
-      var circlePath = Path()..moveTo(size.width / 2, size.height / 2);
-      circlePath.arcTo(circleRect, 0, 2 * 3.14, true);
-      canvas.drawShadow(
-          circlePath, const Color.fromRGBO(0xCF, 0xCF, 0xCF, 0.3), 0.5, false);
-    }
-
-    if (buttonImage.isNull) return;
-
-    /// 绘制中间图标
-    paint = Paint();
-    canvas.save();
-
-    /// 图片剪裁前保存图层
-    RRect imageRRect = RRect.fromRectAndRadius(
-        Rect.fromLTWH(size.width / 2 - 17.5, size.width / 2 - 17.5, 35, 35),
-        const Radius.circular(35));
-    canvas.clipRRect(imageRRect);
-
-    /// 图片为圆形,圆形剪裁
-    canvas.drawColor(Colors.white, BlendMode.color);
-  }
-
-  @override
-  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
-}

+ 0 - 264
lib/components/floating_window/floating_item.dart

@@ -1,264 +0,0 @@
-// ignore_for_file: must_be_immutable
-
-import 'dart:async';
-import 'package:flutter/material.dart';
-import 'dart:ui' as ui;
-import 'package:vnoteapp/components/floating_window/click_notification.dart';
-
-/// [FloatingItem]一个单独功能完善的列表项类
-class FloatingItem extends StatefulWidget {
-  FloatingItem({
-    super.key,
-    required this.top,
-    required this.isLeft,
-    required this.title,
-    required this.imageProvider,
-    required this.index,
-    required this.left,
-    required this.isEntering,
-    this.width,
-  });
-
-  /// [index] 列表项的索引值
-  int index;
-
-  /// [top]列表项的y坐标值
-  double top;
-
-  /// [left]列表项的x坐标值
-  double left;
-
-  ///[isLeft] 列表项是否在左侧,否则是右侧
-  bool isLeft;
-
-  /// [title] 列表项的文字说明
-  String title;
-
-  ///[imageProvider] 列表项Logo的imageProvider
-  ImageProvider imageProvider;
-
-  ///[width] 屏幕宽度的 1 / 2
-  double? width;
-
-  ///[isEntering] 列表项是否触发进场动画
-  bool isEntering;
-
-  @override
-  _FloatingItemState createState() => _FloatingItemState();
-
-  /// 全部列表项执行退场动画
-  static void reverse() {
-    for (int i = 0; i < _FloatingItemState.animationControllers.length; ++i) {
-      if (!_FloatingItemState.animationControllers[i]
-          .toString()
-          .contains('DISPOSED')) {
-        _FloatingItemState.animationControllers[i].reverse();
-      }
-    }
-  }
-
-  /// 全部列表项执行进场动画
-  static void forward() {
-    for (int i = 0; i < _FloatingItemState.animationControllers.length; ++i) {
-      if (!_FloatingItemState.animationControllers[i]
-          .toString()
-          .contains('DISPOSED')) {
-        _FloatingItemState.animationControllers[i].forward();
-      }
-    }
-  }
-
-  /// 每次更新时释放所有动画资源,清空动画控制器列表
-  static void resetList() {
-    for (int i = 0; i < _FloatingItemState.animationControllers.length; ++i) {
-      if (!_FloatingItemState.animationControllers[i]
-          .toString()
-          .contains('DISPOSED')) {
-        _FloatingItemState.animationControllers[i].dispose();
-      }
-    }
-    _FloatingItemState.animationControllers.clear();
-    _FloatingItemState.animationControllers = [];
-  }
-}
-
-class _FloatingItemState extends State<FloatingItem>
-    with TickerProviderStateMixin {
-  /// [isPress] 列表项是否被按下
-  bool isPress = false;
-
-  ///[image] 列表项Logo的[ui.Image]对象,用于绘制Logo
-  ui.Image? image;
-
-  /// [animationController] 列表关闭动画的控制器
-  AnimationController? animationController;
-
-  /// [animationController] 所有列表项的动画控制器列表
-  static List<AnimationController> animationControllers = [];
-
-  /// [animation] 列表项的关闭动画
-  Animation? animation;
-
-  @override
-  void initState() {
-// TODO: implement initState
-    isPress = false;
-
-    /// 获取Logo的ui.Image对象
-    loadImageByProvider(widget.imageProvider).then((value) {
-      setState(() {
-        image = value;
-      });
-    });
-    super.initState();
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return Positioned(
-      left: widget.left,
-      top: widget.top,
-      child: GestureDetector(
-        /// 监听按下事件,在点击区域内则将[isPress]设为true,若在关闭区域内则不做任何操作
-        onPanDown: (details) {
-          if (widget.isLeft) {
-            /// 点击区域内
-            if (details.globalPosition.dx < widget.width!) {
-              setState(() {
-                isPress = true;
-              });
-            }
-          } else {
-            /// 点击区域内
-            if (details.globalPosition.dx < widget.width! * 2 - 50) {
-              setState(() {
-                isPress = true;
-              });
-            }
-          }
-        },
-
-        /// 监听抬起事件
-        onTapUp: (details) async {
-          /// 通过左右列表项来决定关闭的区域,以及选中区域,触发相应的关闭或选中事件
-          if (widget.isLeft) {
-            /// 位于关闭区域
-            if (details.globalPosition.dx >= widget.width! && !isPress) {
-              /// 等待关闭动画执行完毕
-              await animationController?.reverse();
-
-              /// 通知父级触发关闭事件
-              ClickNotification(deletedIndex: widget.index).dispatch(context);
-            } else {
-              /// 通知父级触发相应的点击事件
-              ClickNotification(clickIndex: widget.index).dispatch(context);
-            }
-          } else {
-            /// 位于关闭区域
-            if (details.globalPosition.dx >= widget.width! * 2 - 50.0 &&
-                !isPress) {
-              /// 设置从中间返回至边缘的关闭动画
-              await animationController?.reverse();
-
-              /// 通知父级触发关闭事件
-              ClickNotification(deletedIndex: widget.index).dispatch(context);
-            } else {
-              /// 通知父级触发选中事件
-              ClickNotification(clickIndex: widget.index).dispatch(context);
-            }
-          }
-
-          /// 抬起后取消选中
-          setState(() {
-            isPress = false;
-          });
-        },
-        onTapCancel: () {
-          /// 超出范围取消选中
-          setState(() {
-            isPress = false;
-          });
-        },
-        child: Container(
-          color: Colors.red,
-        ),
-      ),
-    );
-    //  CustomPaint(
-    //     size: Size(widget.width! + 50.0, 50.0),
-    //     painter: FloatingItemPainter(
-    //       title: widget.title,
-    //       isLeft: widget.isLeft,
-    //       isPress: isPress,
-    //       image: image,
-    //     ))));
-  }
-
-  /// 通过ImageProvider获取ui.image
-  Future<ui.Image> loadImageByProvider(
-    ImageProvider provider, {
-    ImageConfiguration config = ImageConfiguration.empty,
-  }) async {
-    Completer<ui.Image> completer = Completer<ui.Image>(); //完成的回调
-    ImageStreamListener listener;
-    ImageStream stream = provider.resolve(config); //获取图片流
-    listener = ImageStreamListener((ImageInfo frame, bool sync) {
-//监听
-      final ui.Image image = frame.image;
-      completer.complete(image); //完成
-      // stream.removeListener(listener); //移除监听
-    });
-    stream.addListener(listener); //添加监听
-    return completer.future; //返回
-  }
-
-  @override
-  void didUpdateWidget(FloatingItem oldWidget) {
-// TODO: implement didUpdateWidget
-    animationController = AnimationController(
-        vsync: this, duration: const Duration(milliseconds: 100));
-
-    /// 初始化进场动画
-    if (widget.isLeft) {
-      animation = Tween<double>(begin: -(widget.width! + 50.0), end: 0.0)
-          .animate(animationController!)
-        ..addListener(() {
-          setState(() {
-            widget.left = animation!.value;
-          });
-        });
-    } else {
-      animation =
-          Tween<double>(begin: widget.width! * 2, end: widget.width! - 50.0)
-              .animate(animationController!)
-            ..addListener(() {
-              setState(() {
-                widget.left = animation!.value;
-              });
-            });
-    }
-    animationControllers.add(animationController!);
-
-    /// 执行进场动画
-    if (animationController!.status == AnimationStatus.dismissed &&
-        widget.isEntering) {
-      animationController!.forward();
-    }
-
-    /// 无需执行进场动画,将列表项置于动画末尾
-    else {
-      animationController!.forward(from: 100.0);
-    }
-    super.didUpdateWidget(oldWidget);
-  }
-
-  @override
-  void dispose() {
-// TODO: implement dispose
-    /// 释放动画资源,避免内存泄漏
-    if (!animationController.toString().toString().contains('DISPOSED')) {
-      animationController!.dispose();
-    }
-    super.dispose();
-  }
-}

+ 0 - 55
lib/components/floating_window/floating_item_animated_widget.dart

@@ -1,55 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:vnoteapp/components/floating_window/floating_item.dart';
-import 'package:vnoteapp/components/floating_window/floating_window_shared_data_widget.dart';
-
-/// [FloatingItemAnimatedWidget] 列表项进行动画类封装,方便传入平移向上动画
-class FloatingItemAnimatedWidget extends AnimatedWidget {
-  const FloatingItemAnimatedWidget(
-      {Key? key,
-      required Animation<double> upAnimation,
-      required this.index,
-      required this.isEntering})
-      : super(key: key, listenable: upAnimation);
-
-  /// [index] 列表项索引
-  final int index;
-
-  /// [isEntering] 列表项是否需要执行进场动画
-  final bool isEntering;
-
-  @override
-  Widget build(BuildContext context) {
-// TODO: implement build
-    /// 获取列表数据
-    var data = FloatingWindowSharedDataWidget.of(context)!.data;
-
-    /// 监听动画
-    final Animation<double> animation = listenable as Animation<double>;
-
-    /// 获取屏幕信息
-    double width = MediaQuery.of(context).size.width / 2;
-    double left = 0.0;
-    if (data.isLeft) {
-      if (isEntering) {
-        left = -(width + 50.0);
-      } else {
-        left = 0.0;
-      }
-    } else {
-      if (isEntering) {
-        left = (width * 2);
-      } else {
-        left = width - 50.0;
-      }
-    }
-    return FloatingItem(
-        top: animation.value,
-        isLeft: data.isLeft,
-        title: data.dataList[index]['title'] ?? '',
-        imageProvider: AssetImage(data.dataList[index]['imageUrl'] ?? ''),
-        index: index,
-        width: width,
-        left: left,
-        isEntering: isEntering);
-  }
-}

+ 0 - 103
lib/components/floating_window/floating_items.dart

@@ -1,103 +0,0 @@
-// ignore_for_file: must_be_immutable
-
-import 'package:flutter/material.dart';
-import 'package:vnoteapp/components/floating_window/floating_item_animated_widget.dart';
-import 'package:vnoteapp/components/floating_window/floating_window_shared_data_widget.dart';
-
-/// [FloatingItems] 列表
-class FloatingItems extends StatefulWidget {
-  FloatingItems({Key? key, required this.isEntering}) : super(key: key);
-  @override
-  _FloatingItemsState createState() => _FloatingItemsState();
-
-  ///[isEntering] 是否具有进场动画
-  bool isEntering = true;
-}
-
-class _FloatingItemsState extends State<FloatingItems>
-    with TickerProviderStateMixin {
-  /// [_controller] 列表项动画的控制器
-  AnimationController? _controller;
-
-  /// 动态生成列表
-  /// 其中一项触发关闭事件后,索引在该项后的列表项执行向上平移的动画。
-  List<Widget> getItems(BuildContext context) {
-    /// 释放和申请新的动画资源
-    if (_controller != null) {
-      _controller!.dispose();
-      _controller = AnimationController(
-          vsync: this, duration: const Duration(milliseconds: 100));
-    }
-
-    /// widget列表
-    List<Widget> widgetList = [];
-
-    /// 获取共享数据
-    var data = FloatingWindowSharedDataWidget.of(context)!.data;
-
-    /// 列表数据
-    var dataList = data.dataList;
-
-    /// 确定列表项位置
-    double top = data.top + 70.0;
-    if (data.top + 70.0 * (dataList.length + 1) >
-        MediaQuery.of(context).size.height - 20.0) {
-      top = data.top - 70.0 * (dataList.length + 1);
-    }
-
-    /// 遍历数据生成列表项
-    for (int i = 0; i < dataList.length; ++i) {
-      /// 在触发关闭事件列表项的索引之后的列表项传入向上平移动画
-      if (data.deleteIndex != -1 && i >= data.deleteIndex) {
-        Animation<double> animation;
-        animation =
-            Tween<double>(begin: top + (70.0 * (i + 1)), end: top + 70.0 * (i))
-                .animate(_controller!);
-        widgetList.add(FloatingItemAnimatedWidget(
-          upAnimation: animation,
-          index: i,
-          isEntering: widget.isEntering,
-        ));
-      }
-
-      /// 在触发关闭事件列表项的索引之前的列表项则位置固定
-      else {
-        Animation<double> animation;
-        animation =
-            Tween<double>(begin: top + (70.0 * (i)), end: top + 70.0 * (i))
-                .animate(_controller!);
-        widgetList.add(FloatingItemAnimatedWidget(
-          upAnimation: animation,
-          index: i,
-          isEntering: widget.isEntering,
-        ));
-      }
-    }
-
-    /// 重置deletedIndex
-    if (data.deleteIndex != -1) {
-      data.deleteIndex = -1;
-    }
-
-    /// 执行动画
-    if (_controller != null) _controller!.forward();
-
-    /// 返回列表
-    return widgetList;
-  }
-
-  @override
-  void initState() {
-// TODO: implement initState
-    super.initState();
-    _controller = AnimationController(
-        vsync: this, duration: const Duration(milliseconds: 100));
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return Stack(
-      children: getItems(context),
-    );
-  }
-}

+ 0 - 104
lib/components/floating_window/floating_window.dart

@@ -1,104 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:vnoteapp/components/floating_window/click_notification.dart';
-import 'package:vnoteapp/components/floating_window/floating_button.dart';
-import 'package:vnoteapp/components/floating_window/floating_item.dart';
-import 'package:vnoteapp/components/floating_window/floating_items.dart';
-import 'package:vnoteapp/components/floating_window/floating_window_model.dart';
-import 'package:vnoteapp/components/floating_window/floating_window_shared_data_widget.dart';
-
-/// [FloatingWindow] 悬浮窗
-class FloatingWindow extends StatefulWidget {
-  const FloatingWindow({super.key});
-
-  @override
-  _FloatingWindowState createState() => _FloatingWindowState();
-}
-
-class _FloatingWindowState extends State<FloatingWindow> {
-  List<Map<String, String>> ls = [
-    {'title': "测试一下", "imageUrl": "assets/images/no_data.png"},
-  ];
-
-  /// 悬浮窗共享数据
-  FloatingWindowModel? windowModel;
-
-  /// [isEntering] 列表项是否拥有进场动画
-  bool isEntering = true;
-  @override
-  void initState() {
-    super.initState();
-    windowModel = FloatingWindowModel(dataList: ls, isLeft: true);
-    isEntering = true;
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return FloatingWindowSharedDataWidget(
-      data: windowModel!,
-      child: windowModel!.isEmpty
-          ? Container()
-          : Stack(
-              fit: StackFit.expand,
-              children: [
-                /// 列表项遮盖层,增加淡化切换动画
-                AnimatedSwitcher(
-                  duration: const Duration(milliseconds: 100),
-                  child: windowModel!.isButton
-                      ? Container()
-                      : GestureDetector(
-                          onTap: () {
-                            FloatingItem.reverse();
-                            Future.delayed(const Duration(milliseconds: 110),
-                                () {
-                              setState(() {
-                                windowModel!.isButton = true;
-                              });
-                            });
-                          },
-                          child: Container(
-                            decoration: const BoxDecoration(
-                                color: Color.fromRGBO(0xEF, 0xEF, 0xEF, 0.9)),
-                          ),
-                        ),
-                ),
-                NotificationListener<ClickNotification>(
-                  onNotification: (notification) {
-                    /// 列表项关闭事件
-                    if (notification.deletedIndex != -1) {
-                      windowModel?.deleteIndex = notification.deletedIndex;
-                      setState(() {
-                        FloatingItem.resetList();
-                        windowModel?.dataList
-                            .removeAt(notification.deletedIndex);
-                        isEntering = false;
-                      });
-                    }
-
-                    /// 列表点击事件
-                    if (notification.clickIndex != -1) {
-                      print(notification.clickIndex);
-                    }
-
-                    /// 悬浮按钮点击Widget改变事件
-                    if (notification.changeWidget) {
-                      setState(() {
-                        /// 释放列表进出场动画资源
-                        FloatingItem.resetList();
-                        windowModel?.isButton = false;
-                        isEntering = true;
-                      });
-                    }
-
-                    return false;
-                  },
-                  child: windowModel!.isButton
-                      ? const FloatingButton()
-                      : FloatingItems(
-                          isEntering: isEntering,
-                        ),
-                )
-              ],
-            ),
-    );
-  }
-}

+ 0 - 31
lib/components/floating_window/floating_window_model.dart

@@ -1,31 +0,0 @@
-class FloatingWindowModel {
-  FloatingWindowModel({
-    this.isLeft = true,
-    this.top = 100.0,
-    List<Map<String, String>>? dataList,
-  }) : dataList = dataList!;
-
-  /// [isEmpty] 列表是非为空
-  get isEmpty => dataList.isEmpty;
-
-  /// [isLeft]:悬浮窗位于屏幕左侧/右侧
-  bool isLeft;
-
-  /// [isEdge] 悬浮窗是否在边缘
-  bool isEdge = true;
-
-  /// [isButton]
-  bool isButton = true;
-
-  /// [top] 悬浮窗纵坐标
-  double top;
-
-  /// [left] 悬浮窗横坐标
-  double left = 0.0;
-
-  /// [dataList] 列表数据
-  List<Map<String, String>> dataList;
-
-  /// 删除的列表项索引
-  int deleteIndex = -1;
-}

+ 0 - 24
lib/components/floating_window/floating_window_shared_data_widget.dart

@@ -1,24 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:vnoteapp/components/floating_window/floating_window_model.dart';
-
-/// [FloatingWindowSharedDataWidget]悬浮窗数据共享Widget
-class FloatingWindowSharedDataWidget extends InheritedWidget {
-  const FloatingWindowSharedDataWidget(
-      {super.key, required this.data, required Widget child})
-      : super(child: child);
-
-  ///[data]悬浮窗共享数据
-  final FloatingWindowModel data;
-
-  /// 静态方法[of]方便直接调用获取共享数据
-  static FloatingWindowSharedDataWidget? of(BuildContext context) {
-    return context
-        .dependOnInheritedWidgetOfExactType<FloatingWindowSharedDataWidget>();
-  }
-
-  @override
-  bool updateShouldNotify(FloatingWindowSharedDataWidget oldWidget) {
-    /// 数据发生变化则发布通知
-    return oldWidget.data != data && data.deleteIndex != -1;
-  }
-}

+ 31 - 23
lib/components/floating_window/index.dart

@@ -17,7 +17,7 @@ class HoveringPatientCard extends StatefulWidget {
     this.fit = StackFit.loose,
     this.clipBehavior = Clip.hardEdge,
     this.padding = EdgeInsets.zero,
-    this.childSize = const Size(100, 100),
+    this.childSize = const Size(80, 80),
   }) : super(key: key);
 
   final AlignmentGeometry alignment;
@@ -48,26 +48,29 @@ class _HoveringPatientCardState extends State<HoveringPatientCard> {
   late ValueNotifier _topVN = ValueNotifier(0.0);
   late ValueNotifier _leftVN = ValueNotifier(0.0);
   late final ValueNotifier<bool> _panStart = ValueNotifier(false);
+  bool _isOpenDialog = false;
   @override
   Widget build(BuildContext context) {
-    return LayoutBuilder(builder: (context, constraints) {
-      _topVN = ValueNotifier(0.0);
-      _leftVN = ValueNotifier(constraints.maxWidth - widget.childSize.width);
-      return Stack(
-        alignment: widget.alignment,
-        textDirection: widget.textDirection,
-        fit: widget.fit,
-        clipBehavior: widget.clipBehavior,
-        children: [
-          widget.bgChild,
-          buildSuspension(
-            child: widget.child,
-            maxWidth: constraints.maxWidth,
-            maxHeight: constraints.maxHeight,
-          ),
-        ],
-      );
-    });
+    return Material(
+      child: LayoutBuilder(builder: (context, constraints) {
+        _topVN = ValueNotifier(86.0);
+        _leftVN = ValueNotifier(constraints.maxWidth - widget.childSize.width);
+        return Stack(
+          alignment: widget.alignment,
+          textDirection: widget.textDirection,
+          fit: widget.fit,
+          clipBehavior: widget.clipBehavior,
+          children: [
+            widget.bgChild,
+            buildSuspension(
+              child: widget.child,
+              maxWidth: constraints.maxWidth,
+              maxHeight: constraints.maxHeight,
+            ),
+          ],
+        );
+      }),
+    );
   }
 
   @override
@@ -91,7 +94,12 @@ class _HoveringPatientCardState extends State<HoveringPatientCard> {
           top: _topVN.value,
           left: _leftVN.value,
           child: GestureDetector(
-            onTap: () => _onChangePatint(),
+            onTap: () {
+              if (!_isOpenDialog) {
+                _onChangePatint();
+              }
+              _isOpenDialog = true;
+            },
             onPanStart: (details) {
               _panStart.value = true;
             },
@@ -154,13 +162,12 @@ class _HoveringPatientCardState extends State<HoveringPatientCard> {
     );
   }
 
-  _onChangePatint() {
+  Future<void> _onChangePatint() async {
     Get.lazyPut(() => CrowdLabelsController());
     Get.lazyPut(() => CreatePatientController());
     Get.lazyPut(() => PatientListController());
     final controller = Get.find<PatientListController>();
-
-    Get.dialog(
+    await Get.dialog(
       Scaffold(
         key: controller.scaffoldKey,
         backgroundColor: Colors.transparent,
@@ -178,6 +185,7 @@ class _HoveringPatientCardState extends State<HoveringPatientCard> {
         ),
       ),
     );
+    _isOpenDialog = false;
   }
 
   _onPanUpdate(DragUpdateDetails detail, double maxHeight, double maxWidth) {

+ 58 - 8
lib/main.dart

@@ -4,6 +4,8 @@ import 'package:flutter/material.dart';
 import 'package:fis_common/logger/logger.dart';
 import 'package:flutter_easyloading/flutter_easyloading.dart';
 import 'package:get/get.dart';
+import 'package:vnoteapp/components/floating_window/index.dart';
+import 'package:vnoteapp/store/store.dart';
 
 import 'global.dart';
 import 'routes/routes.dart';
@@ -59,17 +61,65 @@ class _App extends StatelessWidget {
           const designWidth = 1280.0; // 设计尺寸宽度:1280
           final size = MediaQuery.of(context).size;
           final scale = size.width / designWidth; // 计算缩放比例
-          return FittedBox(
-            fit: BoxFit.fitWidth,
-            child: SizedBox(
-              width: designWidth,
-              height: size.height / scale,
-              child: Center(child: widget ?? const SizedBox()),
-            ),
-          );
+          return Obx(() {
+            if (Store.user.token?.isEmpty ?? true) {
+              return FittedBox(
+                fit: BoxFit.fitWidth,
+                child: SizedBox(
+                  width: designWidth,
+                  height: size.height / scale,
+                  child: Center(child: widget ?? const SizedBox()),
+                ),
+              );
+            }
+            return HoveringPatientCard(
+              bgChild: FittedBox(
+                fit: BoxFit.fitWidth,
+                child: SizedBox(
+                  width: designWidth,
+                  height: size.height / scale,
+                  child: Center(child: widget ?? const SizedBox()),
+                ),
+              ),
+              child: Container(
+                width: 80,
+                height: 80,
+                decoration: BoxDecoration(
+                  color: Colors.blue.withOpacity(0.5),
+                  border: Border.all(color: Colors.blue),
+                  borderRadius: const BorderRadius.all(
+                    Radius.circular(50),
+                  ),
+                ),
+                child: Center(
+                  child: _buildPatient(),
+                ),
+              ),
+            );
+          });
+
           // return widget ?? const SizedBox();
         },
       ),
     );
   }
+
+  Widget _buildPatient() {
+    if (Store.user.currentSelectPatientInfo?.patientName?.isNotEmpty ?? false) {
+      return Text(
+        Store.user.currentSelectPatientInfo?.patientName ?? '',
+        style: const TextStyle(
+          color: Colors.white,
+          fontSize: 26,
+        ),
+        overflow: TextOverflow.ellipsis,
+      );
+    } else {
+      return const Icon(
+        Icons.add,
+        size: 40,
+        color: Colors.white,
+      );
+    }
+  }
 }

+ 2 - 0
lib/managers/contract/index.dart

@@ -12,6 +12,8 @@ class ContractUtils {
         return "已过期";
       case ContractStateEnum.Signed:
         return "已签约";
+      case ContractStateEnum.Voided:
+        return "已拒签";
       default:
         return "";
     }

+ 133 - 0
lib/pages/check/models/form.dart

@@ -119,3 +119,136 @@ class MenuItem {
 
   MenuItem({this.label = '', this.value, this.checked = false});
 }
+
+/// 住院史表单数据类型
+class Admission {
+  String? id;
+  String? admissionDate;
+  String? dischargeDate;
+  String? reason;
+  String? nameOfMedicalInstitution;
+  String? patientNumber;
+
+  Admission({
+    this.id,
+    this.admissionDate,
+    this.dischargeDate,
+    this.reason,
+    this.nameOfMedicalInstitution,
+    this.patientNumber,
+  });
+
+  factory Admission.fromJson(Map<String, dynamic> json) {
+    return Admission(
+      id: json['id'],
+      admissionDate: json['Admission_Date'],
+      dischargeDate: json['Discharge_Date'],
+      reason: json['Reason'],
+      nameOfMedicalInstitution: json['Name_Of_Medical_Institution'],
+      patientNumber: json['Patient_Number'],
+    );
+  }
+
+  Map<String, dynamic> toJson() {
+    return {
+      'id': id ?? '',
+      'Admission_Date': admissionDate ?? '',
+      'Discharge_Date': dischargeDate ?? '',
+      'Reason': reason ?? '',
+      'Name_Of_Medical_Institution': nameOfMedicalInstitution ?? '',
+      'Patient_Number': patientNumber ?? '',
+    };
+  }
+}
+
+/// 家庭病床史
+class BedConstruction {
+  String? id;
+  String? bedConstructionDate;
+  String? dischargeDate;
+  String? reason;
+  String? nameOfMedicalInstitution;
+  String? patientNumber;
+
+  BedConstruction({
+    this.id,
+    this.bedConstructionDate,
+    this.dischargeDate,
+    this.reason,
+    this.nameOfMedicalInstitution,
+    this.patientNumber,
+  });
+
+  factory BedConstruction.fromJson(Map<String, dynamic> json) {
+    return BedConstruction(
+      id: json['id'],
+      bedConstructionDate: json['Bed_Construction_Date'],
+      dischargeDate: json['Discharge_Date'],
+      reason: json['Reason'],
+      nameOfMedicalInstitution: json['Name_Of_Medical_Institution'],
+      patientNumber: json['Patient_Number'],
+    );
+  }
+
+  Map<String, dynamic> toJson() {
+    return {
+      'id': id ?? '',
+      'Admission_Date': bedConstructionDate ?? '',
+      'Discharge_Date': dischargeDate ?? '',
+      'Reason': reason ?? '',
+      'Name_Of_Medical_Institution': nameOfMedicalInstitution ?? '',
+      'Patient_Number': patientNumber ?? '',
+    };
+  }
+}
+
+/// 主要用药情况
+class MainMedication {
+  String? id;
+  String? medicineName;
+  String? medicineUsage;
+  String? medicineDosage;
+  String? medicineTime;
+  String? medicineCompliance;
+
+  MainMedication({
+    this.id,
+    this.medicineName,
+    this.medicineUsage,
+    this.medicineDosage,
+    this.medicineTime,
+    this.medicineCompliance,
+  });
+
+  factory MainMedication.fromJson(Map<String, dynamic> json) {
+    return MainMedication(
+      id: json['id'],
+      medicineName: json['Medicine_Name'],
+      medicineUsage: json['Medicine_Usage'],
+      medicineDosage: json['Medicine_Dosage'],
+      medicineTime: json['Medicine_Time'],
+      medicineCompliance: json['Medicine_Compliance'],
+    );
+  }
+
+  Map<String, dynamic> toJson() {
+    return {
+      'id': id ?? '',
+      'Medicine_Name': medicineName ?? '',
+      'Medicine_Usage': medicineUsage ?? '',
+      'Medicine_Dosage': medicineDosage ?? '',
+      'Medicine_Time': medicineTime ?? '',
+      'Medicine_Compliance': medicineCompliance ?? '',
+    };
+  }
+}
+
+class EditTableValue {
+  int? id;
+  Map<String, dynamic>? value;
+
+  EditTableValue({
+    this.id,
+    this.value,
+  });
+}

+ 117 - 105
lib/pages/check/widgets/configurable_card.dart

@@ -3,11 +3,7 @@ import 'dart:convert';
 import 'package:fis_jsonrpc/rpc.dart';
 import 'package:flutter/material.dart';
 import 'package:get/get.dart';
-import 'package:vnoteapp/architecture/utils/datetime.dart';
-import 'package:vnoteapp/components/alert_dialog.dart';
 import 'package:vnoteapp/components/button.dart';
-import 'package:vnoteapp/components/cell.dart';
-import 'package:vnoteapp/components/dialog_date.dart';
 import 'package:vnoteapp/components/dialog_input.dart';
 import 'package:vnoteapp/components/dialog_number.dart';
 import 'package:vnoteapp/components/dynamic_drawer.dart';
@@ -30,6 +26,9 @@ import 'package:vnoteapp/pages/check/widgets/exam_configurable/exam_radio_score.
 import 'package:vnoteapp/pages/check/widgets/device_controller.dart';
 import 'package:vnoteapp/pages/check/widgets/exam_configurable/exam_table.dart';
 import 'package:vnoteapp/pages/check/widgets/exam_configurable/exam_toxic_substance.dart';
+import 'package:vnoteapp/pages/check/widgets/exam_table/homecare_bed_history_from.dart';
+import 'package:vnoteapp/pages/check/widgets/exam_table/hospitalization_history_from.dart';
+import 'package:vnoteapp/pages/check/widgets/exam_table/main_medication_status_from.dart';
 import 'package:vnoteapp/pages/check/widgets/title_clip_path.dart';
 
 class ConfigurableCard extends StatefulWidget {
@@ -69,7 +68,13 @@ class _ConfigurableFormState extends State<ConfigurableCard> {
   final arrowHeight = math.tan(120 / 180) * 19;
   List<String> deviceList = ['Temp', 'GLU', 'NIBP', 'SpO2', 'BMI'];
   Map<String, dynamic> deviceCached = {};
+  Map currentTable = {};
 
+  // /// TODO 换位置
+  // final Rx<Map> currentTable = Rx({});
+
+  // Map get currentTable => _currentTable.value;
+  // set currentTable(Map val) => _currentTable.value = val;
   @override
   void initState() {
     Get.put(DeviceController());
@@ -160,8 +165,10 @@ class _ConfigurableFormState extends State<ConfigurableCard> {
 
     formValue.forEach(
       (key, value) {
-        if (value is List) {
+        if (value is List<String>) {
           formValue[key] = List<String>.from(formValue[key]);
+        } else if (value is List<Map>) {
+          formValue[key] = List<Map>.from(formValue[key]);
         }
       },
     );
@@ -534,7 +541,7 @@ class _ConfigurableFormState extends State<ConfigurableCard> {
   ///中医药健康指导多选框组件
   Widget _buildHealthGuidanceCheckBox(FormObject currentFormObject) {
     List<Option> options = currentFormObject.options ?? [];
-    List<dynamic> currentSelectedCheckBox =
+    List<String> currentSelectedCheckBox =
         formValue[currentFormObject.key!] ?? [];
     var templateFromObj = currentTemplate.firstWhere(
         (element) => element.label!.contains(currentFormObject.label!));
@@ -766,20 +773,30 @@ class _ConfigurableFormState extends State<ConfigurableCard> {
 
   Widget _buildToxicSubstance(FormObject currentFormObject) {
     List<Option> options = currentFormObject.options ?? [];
+    String currentValue = formValue[currentFormObject.childrenKey!.first] ?? "";
     String currentSelected =
-        formValue[currentFormObject.childrenKey!.first] ?? "";
-    String currentScore = formValue[currentFormObject.childrenKey!.last] ?? "";
+        formValue[currentFormObject.childrenKey!.last] ?? "";
     void selectRaidoChange(Option e) {
       currentSelected = e.value ?? '';
-      formValue[currentFormObject.childrenKey!.first] = currentSelected;
+      formValue[currentFormObject.childrenKey!.last] = currentSelected;
+      setState(() {});
+    }
+
+    void selecValueChange(String e) {
+      currentValue = e;
+      formValue[currentFormObject.childrenKey!.first] = currentValue;
+      print(currentFormObject.childrenKey!.first);
+      print(formValue[currentFormObject.childrenKey!.first]);
       setState(() {});
     }
 
     return ExamToxicSubstance(
       currentFormObject: currentFormObject,
       currentSelected: currentSelected,
+      currentValue: currentValue,
       options: options,
       selectRaidoChange: selectRaidoChange,
+      selecValueChange: selecValueChange,
     );
   }
 
@@ -802,137 +819,132 @@ class _ConfigurableFormState extends State<ConfigurableCard> {
 
   /// 住院史
   Widget _buildMedicalHistory(FormObject currentFormObject) {
-    List<Map> currentValue = formValue[currentFormObject.key!] ?? [];
+    List<dynamic> currentValue = formValue[currentFormObject.key!] ?? [];
     int currentId = currentValue.length + 1;
-    Map currentTable = {
-      'id': currentId.toString(),
-    };
+    void resultChange(Map result) {
+      currentValue.add(result);
+      formValue[currentFormObject.key!] = currentValue;
+      setState(() {});
+    }
+
+    void editResult(EditTableValue result) {
+      currentValue[result.id! - 1] = result.value;
+      formValue[currentFormObject.key!] = currentValue;
+      setState(() {});
+    }
 
     Future<void> addTableData() async {
-      await VAlertDialog.showDialog(
-        VAlertDialog(
-          title: "住院史填写",
-          width: 460,
-          content: Container(
-            height: 350,
-            padding: const EdgeInsets.symmetric(horizontal: 24),
-            alignment: Alignment.center,
-            child: VListFormCellGroup(
-              children: [
-                VListFormCell(
-                  label: '入院日期',
-                  content: DataTimeUtils.formatDateString(
-                    currentTable['Admission_Date'],
-                  ),
-                  onTap: () async {
-                    final result = await VDialogDate(
-                      title: '入院日期',
-                      initialValue: currentTable['Admission_Date'],
-                    ).show();
-                    currentTable['Admission_Date'] = result;
-                    print(result);
-                    // setState(() {});
-                  },
-                ),
-                VListFormCell(
-                  label: '出院日期',
-                  onTap: () async {
-                    final result = await VDialogDate(
-                      title: '出院日期',
-                      initialValue: DateTime.now(),
-                    ).show();
-                  },
-                ),
-                VListFormCell(
-                  label: '原因',
-                  onTap: () {},
-                ),
-                VListFormCell(
-                  label: '医疗机构名称',
-                  onTap: () {},
-                ),
-                VListFormCell(
-                  label: '病案号',
-                  onTap: () {},
-                ),
-              ],
-            ),
-          ),
-          onConfirm: () {
-            Get.back();
-            currentValue.add({
-              'id': currentId.toString(),
-              'Admission_Date': '',
-              'Discharge_Date': '',
-              'Reason': '',
-              'Name_Of_Medical_Institution': '',
-              'Patient_Number': ''
-            });
-            formValue[currentFormObject.key!] = currentValue;
-            setState(() {});
-            // controller.logOut();
-          },
+      Get.dialog(
+        HospitalizationHistoryForm(
+          fromResult: (result) => resultChange(result),
+          currentId: currentId,
+        ),
+      );
+    }
+
+    Future<void> editTableData(EditTableValue editTableValue) async {
+      Get.dialog(
+        HospitalizationHistoryForm(
+          fromResult: (result) => editResult(result),
+          currentId: editTableValue.id!,
+          admissionJson: editTableValue.value,
         ),
       );
     }
 
     return ExamTable(
-      tableThList: const ['序号', '入院日期', '出院日期', '原因', '医疗机构名称', '病案号'],
+      tableThList: const ['序号', '入院日期', '出院日期', '原因', '医疗机构名称', '病案号', '操作'],
       currentFormObject: currentFormObject,
       currentValue: currentValue,
       addTableData: addTableData,
+      editTableData: (value) => editTableData(value),
     );
   }
 
   /// 家庭病床史
   Widget _buildHomecareBedHistory(FormObject currentFormObject) {
-    List<Map> currentValue = formValue[currentFormObject.key!] ?? [];
+    List<dynamic> currentValue = formValue[currentFormObject.key!] ?? [];
     int currentId = currentValue.length + 1;
-    void addTableData() {
-      currentValue.add({
-        'id': currentId.toString(),
-        'Bed_Construction_Date': '',
-        'Discharge_Date': '',
-        'Reason': '',
-        'Name_Of_Medical_Institution': '',
-        'Patient_Number': ''
-      });
+    void resultChange(Map result) {
+      currentValue.add(result);
+      formValue[currentFormObject.key!] = currentValue;
+      setState(() {});
+    }
+
+    void editResult(EditTableValue result) {
+      currentValue[result.id! - 1] = result.value;
       formValue[currentFormObject.key!] = currentValue;
       setState(() {});
     }
 
+    Future<void> addTableData() async {
+      Get.dialog(
+        HomecareBedHistoryFrom(
+          fromResult: (result) => resultChange(result),
+          currentId: currentId,
+        ),
+      );
+    }
+
+    Future<void> editTableData(EditTableValue editTableValue) async {
+      Get.dialog(
+        HomecareBedHistoryFrom(
+          fromResult: (result) => editResult(result),
+          currentId: editTableValue.id!,
+          admissionJson: editTableValue.value,
+        ),
+      );
+    }
+
     return ExamTable(
-      tableThList: const ['序号', '建床日期', '撤床日期', '原因', '医疗机构名称', '病案号'],
+      tableThList: const ['序号', '建床日期', '撤床日期', '原因', '医疗机构名称', '病案号', '操作'],
       currentFormObject: currentFormObject,
       currentValue: currentValue,
       addTableData: addTableData,
+      editTableData: (value) => editTableData(value),
     );
   }
 
-  Widget _buildMainMedicationHistory(FormObject currentFromObject) {
-    List<Map> currentValue = formValue[currentFromObject.key!] ?? [];
+  Widget _buildMainMedicationHistory(FormObject currentFormObject) {
+    List<dynamic> currentValue = formValue[currentFormObject.key!] ?? [];
     int currentId = currentValue.length + 1;
-    Future<void> addTableData() async {
-      await VAlertDialog.showDialog(Container(
-        child: const Text(''),
-      ));
-      currentValue.add({
-        'id': currentId.toString(),
-        'Bed_Construction_Date': '',
-        'Discharge_Date': '',
-        'Reason': '',
-        'Name_Of_Medical_Institution': '',
-        'Patient_Number': ''
-      });
-      formValue[currentFromObject.key!] = currentValue;
+    void resultChange(Map result) {
+      currentValue.add(result);
+      formValue[currentFormObject.key!] = currentValue;
+      setState(() {});
+    }
+
+    void editResult(EditTableValue result) {
+      currentValue[result.id! - 1] = result.value;
+      formValue[currentFormObject.key!] = currentValue;
       setState(() {});
     }
 
+    Future<void> addTableData() async {
+      Get.dialog(
+        MainMedicationStatusFrom(
+          fromResult: (result) => resultChange(result),
+          currentId: currentId,
+        ),
+      );
+    }
+
+    Future<void> editTableData(EditTableValue editTableValue) async {
+      Get.dialog(
+        MainMedicationStatusFrom(
+          fromResult: (result) => editResult(result),
+          currentId: editTableValue.id!,
+          admissionJson: editTableValue.value,
+        ),
+      );
+    }
+
     return ExamTable(
-      tableThList: const ['序号', '药物名称', '用法', '用量', '用药时间', '服药依从性'],
-      currentFormObject: currentFromObject,
+      tableThList: const ['序号', '药物名称', '用法', '用量', '用药时间', '服药依从性', '操作'],
+      currentFormObject: currentFormObject,
       currentValue: currentValue,
       addTableData: addTableData,
+      editTableData: (value) => editTableData(value),
     );
   }
 

+ 77 - 32
lib/pages/check/widgets/exam_configurable/exam_table.dart

@@ -1,4 +1,6 @@
 import 'package:flutter/material.dart';
+import 'package:vnoteapp/architecture/types/index.dart';
+import 'package:vnoteapp/components/button.dart';
 import 'package:vnoteapp/pages/check/models/form.dart';
 import 'package:vnoteapp/pages/check/widgets/exam_configurable/exam_card.dart';
 
@@ -9,12 +11,14 @@ class ExamTable extends StatelessWidget {
     required this.currentFormObject,
     this.currentValue,
     this.addTableData,
+    this.editTableData,
   });
 
   final List<String> tableThList;
   final FormObject currentFormObject;
-  final List<Map>? currentValue;
+  final List<dynamic>? currentValue;
   final Function? addTableData;
+  final ValueCallback? editTableData;
   @override
   Widget build(BuildContext context) {
     return Stack(
@@ -32,52 +36,93 @@ class ExamTable extends StatelessWidget {
             child: SingleChildScrollView(
                 scrollDirection: Axis.horizontal,
                 child: DataTable(
-                  columnSpacing: 120,
+                  columnSpacing: 80,
+                  headingRowColor: MaterialStateProperty.all<Color?>(
+                      Theme.of(context).primaryColor),
+                  dataRowHeight: 60,
                   headingTextStyle: const TextStyle(
-                    fontSize: 20,
-                    color: Colors.black,
+                    fontSize: 24,
+                    color: Colors.white,
                   ),
-                  dataTextStyle: const TextStyle(
-                    fontSize: 20,
-                    color: Colors.black,
+                  dataTextStyle: TextStyle(
+                    fontSize: 22,
+                    color: Colors.black.withOpacity(0.9),
                   ),
                   columns: tableThList
                       .map(
-                        (e) => DataColumn(label: Text(e)),
-                      )
-                      .toList(),
-                  rows: currentValue!
-                      .map(
-                        (c) => DataRow(
-                          cells: c.entries
-                              .map(
-                                (a) => DataCell(
-                                  Text(
-                                    a.value,
-                                  ),
-                                  onTap: () {},
-                                ),
-                              )
-                              .toList(),
+                        (e) => DataColumn(
+                          numeric: false,
+                          label: Text(
+                            e,
+                          ),
                         ),
                       )
                       .toList(),
+                  rows: currentValue!.map((c) {
+                    List<DataCell> cells = (c as Map)
+                        .entries
+                        .map(
+                          (a) => DataCell(
+                            Container(
+                              constraints: const BoxConstraints(
+                                maxWidth: 150,
+                              ),
+                              child: Text(
+                                a.value ?? '',
+                                overflow: TextOverflow.ellipsis,
+                              ),
+                            ),
+                          ),
+                        )
+                        .toList();
+                    cells.add(
+                      DataCell(
+                        Text(
+                          '编辑',
+                          style: TextStyle(
+                            fontSize: 22,
+                            color: Theme.of(context).primaryColor,
+                          ),
+                        ),
+                        onTap: () {
+                          editTableData!.call(
+                            EditTableValue(
+                              id: int.parse(c['id']),
+                              value: currentValue![int.parse(c['id']) - 1],
+                            ),
+                          );
+                        },
+                      ),
+                    );
+                    return DataRow(
+                      cells: cells,
+                    );
+                  }).toList(),
                 )),
           ),
         ),
         Positioned(
           right: 16,
           top: 8,
-          child: IconButton(
-            icon: const Icon(
-              Icons.add,
-              size: 40,
+          child: SizedBox(
+            width: 130,
+            height: 54,
+            child: VButton(
+              // label: '新增',
+              child: const Row(
+                mainAxisAlignment: MainAxisAlignment.center,
+                children: [
+                  Icon(Icons.add, size: 24),
+                  SizedBox(
+                    width: 8,
+                  ),
+                  Text("新增", style: TextStyle(fontSize: 20)),
+                ],
+              ),
+              onTap: () {
+                addTableData!();
+              },
             ),
-            onPressed: () {
-              addTableData!();
-              // aList.add(a);
-              // setState(() {});
-            },
           ),
         ),
       ],

+ 24 - 6
lib/pages/check/widgets/exam_configurable/exam_toxic_substance.dart

@@ -1,4 +1,5 @@
 import 'package:flutter/material.dart';
+import 'package:vnoteapp/components/dialog_input.dart';
 import 'package:vnoteapp/pages/check/models/form.dart';
 import 'package:vnoteapp/pages/check/widgets/exam_configurable/exam_card.dart';
 
@@ -10,15 +11,15 @@ class ExamToxicSubstance extends StatelessWidget {
     required this.options,
     required this.selectRaidoChange,
     required this.currentSelected,
-    // required this.changeScore,
-    // required this.currentScore,
+    required this.selecValueChange,
+    required this.currentValue,
   });
   final FormObject currentFormObject;
   final List<Option> options;
   final Function selectRaidoChange;
   final String currentSelected;
-  // final Function changeScore;
-  // final String currentScore;
+  final Function selecValueChange;
+  final String currentValue;
 
   @override
   Widget build(BuildContext context) {
@@ -32,9 +33,15 @@ class ExamToxicSubstance extends StatelessWidget {
             ),
             child: TextField(
               readOnly: true,
-              controller: TextEditingController(text: ''),
+              controller: TextEditingController(text: currentValue),
               style: const TextStyle(fontSize: 30),
-              onTap: () => {},
+              onTap: () async {
+                final result = await _showInputDialog(
+                  title: currentFormObject.label ?? '',
+                  initialValue: currentValue,
+                );
+                selecValueChange.call(result);
+              },
             ),
           ),
           Container(
@@ -124,4 +131,15 @@ class ExamToxicSubstance extends StatelessWidget {
       ),
     );
   }
+
+  Future<String?> _showInputDialog({
+    required String title,
+    required String initialValue,
+  }) async {
+    final result = await VDialogInput(
+      title: title,
+      initialValue: initialValue,
+    ).show();
+    return result;
+  }
 }

+ 175 - 0
lib/pages/check/widgets/exam_table/homecare_bed_history_from.dart

@@ -0,0 +1,175 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:intl/intl.dart';
+import 'package:vnoteapp/architecture/types/index.dart';
+import 'package:vnoteapp/components/alert_dialog.dart';
+import 'package:vnoteapp/components/cell.dart';
+import 'package:vnoteapp/components/dialog_date.dart';
+import 'package:vnoteapp/components/dialog_input.dart';
+import 'package:vnoteapp/pages/check/models/form.dart';
+
+/// 家庭病床史
+class HomecareBedHistoryFrom extends StatefulWidget {
+  const HomecareBedHistoryFrom({
+    Key? key,
+    required this.fromResult,
+    required this.currentId,
+    this.admissionJson,
+  }) : super(key: key);
+
+  final ValueCallback fromResult;
+  final int currentId;
+  final Map<String, dynamic>? admissionJson;
+
+  @override
+  _HomecareBedHistoryFromState createState() => _HomecareBedHistoryFromState();
+}
+
+class _HomecareBedHistoryFromState extends State<HomecareBedHistoryFrom> {
+  BedConstruction bedConstruction = BedConstruction();
+
+  @override
+  void initState() {
+    if (widget.admissionJson != null) {
+      bedConstruction = BedConstruction.fromJson(widget.admissionJson!);
+    }
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return VAlertDialog(
+      title: "家庭病床史填写",
+      width: 600,
+      content: Container(
+        height: 350,
+        padding: const EdgeInsets.symmetric(horizontal: 24),
+        alignment: Alignment.center,
+        child: VListFormCellGroup(
+          children: [
+            _buildFormCell(
+              label: '建床日期',
+              content: bedConstruction.bedConstructionDate,
+              onTap: () async {
+                final result = await _showDateDialog(
+                  title: '建床日期',
+                  initialValue: bedConstruction.bedConstructionDate,
+                );
+                if (result != null) {
+                  bedConstruction.bedConstructionDate =
+                      DateFormat("yyyy-MM-dd").format(result);
+                }
+
+                setState(() {});
+              },
+            ),
+            _buildFormCell(
+              label: '撤床日期',
+              content: bedConstruction.dischargeDate,
+              onTap: () async {
+                final result = await _showDateDialog(
+                  title: '撤床日期',
+                  initialValue: bedConstruction.dischargeDate,
+                );
+                if (result != null) {
+                  bedConstruction.dischargeDate =
+                      DateFormat("yyyy-MM-dd").format(result);
+                }
+                setState(() {});
+              },
+            ),
+            _buildFormCell(
+              label: '原因',
+              content: bedConstruction.reason,
+              onTap: () async {
+                final result = await _showInputDialog(
+                  title: '原因',
+                  initialValue: bedConstruction.reason ?? '',
+                );
+
+                bedConstruction.reason = result;
+                setState(() {});
+              },
+            ),
+            _buildFormCell(
+              label: '医疗机构名称',
+              content: bedConstruction.nameOfMedicalInstitution,
+              onTap: () async {
+                final result = await _showInputDialog(
+                  title: '医疗机构名称',
+                  initialValue: bedConstruction.nameOfMedicalInstitution ?? '',
+                );
+
+                bedConstruction.nameOfMedicalInstitution = result;
+                setState(() {});
+              },
+            ),
+            _buildFormCell(
+              label: '病案号',
+              content: bedConstruction.patientNumber,
+              onTap: () async {
+                final result = await _showInputDialog(
+                  title: '病案号',
+                  initialValue: bedConstruction.patientNumber ?? '',
+                );
+
+                bedConstruction.patientNumber = result;
+                setState(() {});
+              },
+            ),
+          ],
+        ),
+      ),
+      onConfirm: () {
+        if (widget.admissionJson != null) {
+          widget.fromResult.call(
+            EditTableValue(
+              id: widget.currentId,
+              value: bedConstruction.toJson(),
+            ),
+          );
+        } else {
+          bedConstruction.id = widget.currentId.toString();
+          widget.fromResult.call(bedConstruction.toJson());
+        }
+
+        Get.back();
+      },
+    );
+  }
+
+  Future<DateTime?> _showDateDialog({
+    required String title,
+    String? initialValue,
+  }) async {
+    final result = await VDialogDate(
+      title: title,
+      initialValue:
+          initialValue == null ? DateTime.now() : DateTime.parse(initialValue),
+    ).show();
+    return result;
+  }
+
+  Future<String?> _showInputDialog({
+    required String title,
+    required String initialValue,
+  }) async {
+    final result = await VDialogInput(
+      title: title,
+      initialValue: initialValue,
+    ).show();
+    return result;
+  }
+
+  Widget _buildFormCell({
+    required String label,
+    String? content,
+    required VoidCallback onTap,
+  }) {
+    return VListFormCell(
+      label: label,
+      content: content ?? '',
+      onTap: onTap,
+    );
+  }
+}

+ 177 - 0
lib/pages/check/widgets/exam_table/hospitalization_history_from.dart

@@ -0,0 +1,177 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:intl/intl.dart';
+import 'package:vnoteapp/architecture/types/index.dart';
+import 'package:vnoteapp/components/alert_dialog.dart';
+import 'package:vnoteapp/components/cell.dart';
+import 'package:vnoteapp/components/dialog_date.dart';
+import 'package:vnoteapp/components/dialog_input.dart';
+import 'package:vnoteapp/pages/check/models/form.dart';
+
+/// 住院史
+class HospitalizationHistoryForm extends StatefulWidget {
+  const HospitalizationHistoryForm({
+    Key? key,
+    required this.fromResult,
+    required this.currentId,
+    this.admissionJson,
+  }) : super(key: key);
+
+  final ValueCallback fromResult;
+  final int currentId;
+  final Map<String, dynamic>? admissionJson;
+
+  @override
+  _HospitalizationHistoryFormState createState() =>
+      _HospitalizationHistoryFormState();
+}
+
+class _HospitalizationHistoryFormState
+    extends State<HospitalizationHistoryForm> {
+  Admission admission = Admission();
+
+  @override
+  void initState() {
+    if (widget.admissionJson != null) {
+      admission = Admission.fromJson(widget.admissionJson!);
+    }
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return VAlertDialog(
+      title: "住院史填写",
+      width: 600,
+      content: Container(
+        height: 350,
+        padding: const EdgeInsets.symmetric(horizontal: 24),
+        alignment: Alignment.center,
+        child: VListFormCellGroup(
+          children: [
+            _buildFormCell(
+              label: '入院日期',
+              content: admission.admissionDate,
+              onTap: () async {
+                final result = await _showDateDialog(
+                  title: '入院日期',
+                  initialValue: admission.admissionDate,
+                );
+                if (result != null) {
+                  admission.admissionDate =
+                      DateFormat("yyyy-MM-dd").format(result);
+                }
+
+                setState(() {});
+              },
+            ),
+            _buildFormCell(
+              label: '出院日期',
+              content: admission.dischargeDate,
+              onTap: () async {
+                final result = await _showDateDialog(
+                  title: '出院日期',
+                  initialValue: admission.dischargeDate,
+                );
+                if (result != null) {
+                  admission.dischargeDate =
+                      DateFormat("yyyy-MM-dd").format(result);
+                }
+                setState(() {});
+              },
+            ),
+            _buildFormCell(
+              label: '原因',
+              content: admission.reason,
+              onTap: () async {
+                final result = await _showInputDialog(
+                  title: '原因',
+                  initialValue: admission.reason ?? '',
+                );
+
+                admission.reason = result;
+                setState(() {});
+              },
+            ),
+            _buildFormCell(
+              label: '医疗机构名称',
+              content: admission.nameOfMedicalInstitution,
+              onTap: () async {
+                final result = await _showInputDialog(
+                  title: '医疗机构名称',
+                  initialValue: admission.nameOfMedicalInstitution ?? '',
+                );
+
+                admission.nameOfMedicalInstitution = result;
+                setState(() {});
+              },
+            ),
+            _buildFormCell(
+              label: '病案号',
+              content: admission.patientNumber,
+              onTap: () async {
+                final result = await _showInputDialog(
+                  title: '病案号',
+                  initialValue: admission.patientNumber ?? '',
+                );
+
+                admission.patientNumber = result;
+                setState(() {});
+              },
+            ),
+          ],
+        ),
+      ),
+      onConfirm: () {
+        if (widget.admissionJson != null) {
+          widget.fromResult.call(
+            EditTableValue(
+              id: widget.currentId,
+              value: admission.toJson(),
+            ),
+          );
+        } else {
+          admission.id = widget.currentId.toString();
+          widget.fromResult.call(admission.toJson());
+        }
+
+        Get.back();
+      },
+    );
+  }
+
+  Future<DateTime?> _showDateDialog({
+    required String title,
+    String? initialValue,
+  }) async {
+    final result = await VDialogDate(
+      title: title,
+      initialValue:
+          initialValue == null ? DateTime.now() : DateTime.parse(initialValue),
+    ).show();
+    return result;
+  }
+
+  Future<String?> _showInputDialog({
+    required String title,
+    required String initialValue,
+  }) async {
+    final result = await VDialogInput(
+      title: title,
+      initialValue: initialValue,
+    ).show();
+    return result;
+  }
+
+  Widget _buildFormCell({
+    required String label,
+    String? content,
+    required VoidCallback onTap,
+  }) {
+    return VListFormCell(
+      label: label,
+      content: content ?? '',
+      onTap: onTap,
+    );
+  }
+}

+ 170 - 0
lib/pages/check/widgets/exam_table/main_medication_status_from.dart

@@ -0,0 +1,170 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:vnoteapp/architecture/types/index.dart';
+import 'package:vnoteapp/components/alert_dialog.dart';
+import 'package:vnoteapp/components/cell.dart';
+import 'package:vnoteapp/components/dialog_date.dart';
+import 'package:vnoteapp/components/dialog_input.dart';
+import 'package:vnoteapp/pages/check/models/form.dart';
+
+/// 住院史
+class MainMedicationStatusFrom extends StatefulWidget {
+  const MainMedicationStatusFrom({
+    Key? key,
+    required this.fromResult,
+    required this.currentId,
+    this.admissionJson,
+  }) : super(key: key);
+
+  final ValueCallback fromResult;
+  final int currentId;
+  final Map<String, dynamic>? admissionJson;
+
+  @override
+  _HospitalizationHistoryFormState createState() =>
+      _HospitalizationHistoryFormState();
+}
+
+class _HospitalizationHistoryFormState extends State<MainMedicationStatusFrom> {
+  MainMedication mainMedication = MainMedication();
+
+  @override
+  void initState() {
+    if (widget.admissionJson != null) {
+      mainMedication = MainMedication.fromJson(widget.admissionJson!);
+    }
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return VAlertDialog(
+      title: "主要用药情况填写",
+      width: 600,
+      content: Container(
+        height: 350,
+        padding: const EdgeInsets.symmetric(horizontal: 24),
+        alignment: Alignment.center,
+        child: VListFormCellGroup(
+          children: [
+            _buildFormCell(
+              label: '药物名称',
+              content: mainMedication.medicineName,
+              onTap: () async {
+                final result = await _showInputDialog(
+                  title: '药物名称',
+                  initialValue: mainMedication.medicineName ?? '',
+                );
+
+                mainMedication.medicineName = result;
+                setState(() {});
+              },
+            ),
+            _buildFormCell(
+              label: '用法',
+              content: mainMedication.medicineUsage,
+              onTap: () async {
+                final result = await _showInputDialog(
+                  title: '用法',
+                  initialValue: mainMedication.medicineUsage ?? '',
+                );
+
+                mainMedication.medicineUsage = result;
+                setState(() {});
+              },
+            ),
+            _buildFormCell(
+              label: '用量',
+              content: mainMedication.medicineDosage,
+              onTap: () async {
+                final result = await _showInputDialog(
+                  title: '用量',
+                  initialValue: mainMedication.medicineDosage ?? '',
+                );
+
+                mainMedication.medicineDosage = result;
+                setState(() {});
+              },
+            ),
+            _buildFormCell(
+              label: '用药时间',
+              content: mainMedication.medicineTime,
+              onTap: () async {
+                final result = await _showInputDialog(
+                  title: '用药时间',
+                  initialValue: mainMedication.medicineTime ?? '',
+                );
+
+                mainMedication.medicineTime = result;
+                setState(() {});
+              },
+            ),
+            _buildFormCell(
+              label: '服药依从性',
+              content: mainMedication.medicineCompliance,
+              onTap: () async {
+                final result = await _showInputDialog(
+                  title: '服药依从性',
+                  initialValue: mainMedication.medicineCompliance ?? '',
+                );
+
+                mainMedication.medicineCompliance = result;
+                setState(() {});
+              },
+            ),
+          ],
+        ),
+      ),
+      onConfirm: () {
+        if (widget.admissionJson != null) {
+          widget.fromResult.call(
+            EditTableValue(
+              id: widget.currentId,
+              value: mainMedication.toJson(),
+            ),
+          );
+        } else {
+          mainMedication.id = widget.currentId.toString();
+          widget.fromResult.call(mainMedication.toJson());
+        }
+
+        Get.back();
+      },
+    );
+  }
+
+  Future<DateTime?> _showDateDialog({
+    required String title,
+    String? initialValue,
+  }) async {
+    final result = await VDialogDate(
+      title: title,
+      initialValue:
+          initialValue == null ? DateTime.now() : DateTime.parse(initialValue),
+    ).show();
+    return result;
+  }
+
+  Future<String?> _showInputDialog({
+    required String title,
+    required String initialValue,
+  }) async {
+    final result = await VDialogInput(
+      title: title,
+      initialValue: initialValue,
+    ).show();
+    return result;
+  }
+
+  Widget _buildFormCell({
+    required String label,
+    String? content,
+    required VoidCallback onTap,
+  }) {
+    return VListFormCell(
+      label: label,
+      content: content ?? '',
+      onTap: onTap,
+    );
+  }
+}

+ 70 - 51
lib/pages/patient/list/view.dart

@@ -41,26 +41,28 @@ class PatientListPage extends GetView<PatientListController> {
               mainAxisSize: MainAxisSize.max,
               children: [
                 Obx(
-                  () => TabButtonGroup([
-                    TabButton(
-                      tabTitle: '新建档案',
-                      index: 0,
-                      activeIndex: controller.state.currentTabIndex,
-                      onCallbackTap: () {
-                        controller.state.currentTabIndex = 0;
-                      },
-                      width: 150,
-                    ),
-                    TabButton(
-                      tabTitle: '选择病人',
-                      index: 1,
-                      activeIndex: controller.state.currentTabIndex,
-                      onCallbackTap: () {
-                        controller.state.currentTabIndex = 1;
-                      },
-                      width: 150,
-                    ),
-                  ]),
+                  () => TabButtonGroup(
+                    [
+                      TabButton(
+                        tabTitle: '新建档案',
+                        index: 0,
+                        activeIndex: controller.state.currentTabIndex,
+                        onCallbackTap: () {
+                          controller.state.currentTabIndex = 0;
+                        },
+                        width: 150,
+                      ),
+                      TabButton(
+                        tabTitle: '病人列表',
+                        index: 1,
+                        activeIndex: controller.state.currentTabIndex,
+                        onCallbackTap: () {
+                          controller.state.currentTabIndex = 1;
+                        },
+                        width: 150,
+                      ),
+                    ],
+                  ),
                 ),
                 const SizedBox(
                   height: 8,
@@ -282,23 +284,6 @@ class _HeaderWidget extends GetView<PatientListController> {
             ),
           ),
           const SizedBox(width: 8),
-          // SizedBox(
-          //   width: 180,
-          //   height: 70,
-          //   child: VButton(
-          //     child: const Row(
-          //       mainAxisAlignment: MainAxisAlignment.center,
-          //       children: [
-          //         Icon(Icons.note_add_outlined, size: 24),
-          //         Text("新建档案", style: TextStyle(fontSize: 20)),
-          //       ],
-          //     ),
-          //     onTap: () {
-          //       controller.onCreateClicked();
-          //     },
-          //   ),
-          // ),
-          // const SizedBox(width: 8),
           SizedBox(
             width: 180,
             height: 70,
@@ -346,6 +331,10 @@ class _PatientCard extends StatelessWidget {
               ),
               const SizedBox(height: 12),
               _buildClassTags(),
+              const SizedBox(height: 6),
+              _buildPhone(),
+              const SizedBox(height: 6),
+              _buildCardNo(),
             ],
           ),
         ),
@@ -368,6 +357,7 @@ class _PatientCard extends StatelessWidget {
         child: InkWell(
           borderRadius: GlobalStyles.borderRadius,
           onTap: () {
+            Get.back();
             Get.find<PatientListController>().gotoDetail(dto.code!);
           },
           child: body,
@@ -384,12 +374,12 @@ class _PatientCard extends StatelessWidget {
       mainAxisAlignment: MainAxisAlignment.spaceBetween,
       children: [
         SizedBox(
-          width: 100,
+          width: 110,
           child: Text(
             dto.patientName!,
             style: const TextStyle(
               color: Colors.black,
-              fontSize: 20,
+              fontSize: 26,
               fontWeight: FontWeight.bold,
             ),
           ),
@@ -426,20 +416,49 @@ class _PatientCard extends StatelessWidget {
   }
 
   Widget _buildClassTags() {
-    fn(String x) => Text(
-          x,
-          style: const TextStyle(color: Colors.grey, fontSize: 18),
-        );
-    return ConstrainedBox(
-      constraints: const BoxConstraints(minWidth: double.infinity),
-      child: Wrap(
-        alignment: WrapAlignment.start,
-        spacing: 20,
-        runSpacing: 8,
-        children: (dto.labelNames ?? []).map((e) => fn(e)).toList(),
-      ),
+    return Column(
+      children: [
+        ConstrainedBox(
+          constraints: const BoxConstraints(
+            minWidth: double.infinity,
+            maxHeight: 50,
+          ),
+          child: Text(
+            dto.labelNames?.join('   ') ?? '',
+            style: const TextStyle(color: Colors.grey, fontSize: 18),
+            maxLines: 2,
+            overflow: TextOverflow.ellipsis,
+          ),
+        ),
+      ],
     );
   }
+
+  Widget _buildPhone() {
+    String phone = '';
+    if (dto.phone != null && dto.phone != '') {
+      phone = '手机号:${dto.phone!}';
+      return Text(
+        phone,
+        style: const TextStyle(color: Colors.grey, fontSize: 18),
+      );
+    } else {
+      return const SizedBox();
+    }
+  }
+
+  Widget _buildCardNo() {
+    String cardNo = '';
+    if (dto.cardNo != null && dto.cardNo != '') {
+      cardNo = '身份证号:${dto.cardNo!}';
+      return Text(
+        cardNo,
+        style: const TextStyle(color: Colors.grey, fontSize: 18),
+      );
+    } else {
+      return const SizedBox();
+    }
+  }
 }
 
 class _PatientSignStatusTag extends StatelessWidget {