Quellcode durchsuchen

Merge branch 'master' of http://git.ius.plus/Project-VNOTE/VNoteApp

guanxinyi vor 1 Jahr
Ursprung
Commit
c371a03aaa

+ 14 - 6
lib/components/appbar.dart

@@ -12,22 +12,30 @@ class VAppBar extends AppBar {
   /// 右侧组件组
   final List<Widget>? actions;
 
+  final BuildContext? context;
+
   VAppBar({
     super.key,
     this.titleText,
     this.titleWidget,
     this.actions,
+    this.context,
   }) : super(
           toolbarHeight: 90,
-          backgroundColor: Colors.blue,
+          backgroundColor: Colors.white,
           foregroundColor: Colors.green,
           flexibleSpace: Container(
-            decoration: const BoxDecoration(
+            decoration: BoxDecoration(
               gradient: LinearGradient(
-                colors: [
-                  Color.fromRGBO(59, 188, 255, 1),
-                  Color.fromRGBO(44, 120, 229, 1),
-                ],
+                colors: context != null
+                    ? [
+                        Theme.of(context).primaryColor.withOpacity(.6),
+                        Theme.of(context).primaryColor,
+                      ]
+                    : const [
+                        Color.fromRGBO(59, 188, 255, 1),
+                        Color.fromRGBO(44, 120, 229, 1),
+                      ],
                 begin: Alignment.topCenter,
                 end: Alignment.bottomCenter,
               ),

+ 41 - 27
lib/components/button.dart

@@ -33,35 +33,49 @@ class VButton extends StatelessWidget {
     final colors = _findBgColor(context, type);
     final bgColor = colors.$1;
     final textColor = colors.$2;
-    return SizedBox(
-      width: 270,
-      height: 60,
-      child: ElevatedButton(
-        onPressed: () {
-          onTap?.call();
-        },
-        style: ButtonStyle(
-          foregroundColor: MaterialStatePropertyAll(textColor),
-          backgroundColor: MaterialStatePropertyAll(bgColor),
-          elevation: const MaterialStatePropertyAll(0),
-          padding: const MaterialStatePropertyAll(
-            EdgeInsets.symmetric(horizontal: 14, vertical: 8),
-          ),
-          alignment: Alignment.center,
-          shape: MaterialStatePropertyAll(
-            RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
-          ),
-        ),
-        child: child ??
-            Text(
-              label!,
-              style: TextStyle(
-                color: textColor,
-                fontSize: 18,
+
+    return LayoutBuilder(builder: (context, c) {
+      double width = c.maxWidth;
+      if (width > 1200) {
+        width = 270;
+      }
+      double height = c.maxHeight;
+      if (height > 720) {
+        height = 60;
+      }
+      double radius = height / 2;
+      return SizedBox(
+        width: 270,
+        height: height,
+        child: ElevatedButton(
+          onPressed: () {
+            onTap?.call();
+          },
+          style: ButtonStyle(
+            foregroundColor: MaterialStatePropertyAll(textColor),
+            backgroundColor: MaterialStatePropertyAll(bgColor),
+            elevation: const MaterialStatePropertyAll(0),
+            padding: MaterialStatePropertyAll(
+              EdgeInsets.symmetric(horizontal: radius, vertical: 8),
+            ),
+            alignment: Alignment.center,
+            shape: MaterialStatePropertyAll(
+              RoundedRectangleBorder(
+                borderRadius: BorderRadius.circular(radius),
               ),
             ),
-      ),
-    );
+          ),
+          child: child ??
+              Text(
+                label!,
+                style: TextStyle(
+                  color: textColor,
+                  fontSize: 18,
+                ),
+              ),
+        ),
+      );
+    });
   }
 
   (Color c, Color b) _findBgColor(BuildContext context, VButtonType type) {

+ 11 - 6
lib/components/checkbox_button.dart

@@ -29,7 +29,11 @@ class _VCheckBoxGroupState<T, TValue>
 
   @override
   void initState() {
-    _checkedValues = widget.values ?? [];
+    if (widget.values != null) {
+      _checkedValues = List.from(widget.values!);
+    } else {
+      _checkedValues = [];
+    }
     super.initState();
   }
 
@@ -39,14 +43,16 @@ class _VCheckBoxGroupState<T, TValue>
     final length = widget.source.length;
     for (var i = 0; i < length; i++) {
       final e = widget.source[i];
+      final value = widget.valueGetter(e);
+      final isChecked = _checkedValues.contains(value);
       children.add(
         SizedBox(
           width: widget.itemWidth ?? 160,
           child: VCheckBoxButton(
             label: widget.labelGetter(e),
-            isChecked: false,
-            onChanged: (value) {
-              _onItemChanged(e, value);
+            isChecked: isChecked,
+            onChanged: (status) {
+              _onItemChanged(value, status);
             },
           ),
         ),
@@ -59,8 +65,7 @@ class _VCheckBoxGroupState<T, TValue>
     );
   }
 
-  void _onItemChanged(T data, bool isChecked) {
-    final value = widget.valueGetter(data);
+  void _onItemChanged(TValue value, bool isChecked) {
     if (isChecked) {
       if (_checkedValues.contains(value) == false) {
         _checkedValues.add(value);

+ 124 - 0
lib/components/dynamic_drawer.dart.dart

@@ -0,0 +1,124 @@
+import 'package:flutter/material.dart';
+
+final _builderMap = <GlobalKey<ScaffoldState>, WidgetBuilder>{};
+
+class VDrawer extends StatelessWidget {
+  final Widget? child;
+  final double? width;
+  final String? title;
+  final GlobalKey<ScaffoldState>? scaffoldKey;
+  final VoidCallback? onConfirm;
+
+  const VDrawer({
+    super.key,
+    this.child,
+    this.width,
+    this.title,
+    this.scaffoldKey,
+    this.onConfirm,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Drawer(
+      shadowColor: Colors.black.withOpacity(.1),
+      backgroundColor: Colors.white,
+      shape: RoundedRectangleBorder(
+        borderRadius: BorderRadius.circular(16),
+      ),
+      elevation: 0,
+      width: width,
+      child: Container(
+        alignment: Alignment.topLeft,
+        padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 12),
+        child: Column(
+          mainAxisSize: MainAxisSize.min,
+          children: _buildChildren(context),
+        ),
+      ),
+    );
+  }
+
+  List<Widget> _buildChildren(BuildContext context) {
+    final children = <Widget>[];
+    final header = _buildHeader();
+    if (header != null) {
+      children.add(header);
+    }
+    if (child != null) {
+      children.add(Expanded(child: child!));
+    }
+    return children;
+  }
+
+  Widget? _buildHeader() {
+    final children = <Widget>[];
+    const btnTextStyle = TextStyle(fontSize: 20);
+    if (scaffoldKey != null) {
+      children.add(
+        TextButton(
+          onPressed: () {
+            scaffoldKey!.currentState?.closeEndDrawer();
+          },
+          child: const Text("取消", style: btnTextStyle),
+        ),
+      );
+    }
+    if (title != null) {
+      children.add(Text(
+        title!,
+        style: TextStyle(color: Colors.grey.shade700, fontSize: 20),
+      ));
+    }
+    if (onConfirm != null) {
+      children.add(
+        TextButton(
+          onPressed: () {
+            onConfirm?.call();
+          },
+          child: const Text("确定", style: btnTextStyle),
+        ),
+      );
+    }
+
+    if (children.isEmpty) return null;
+
+    return Row(
+      mainAxisAlignment: MainAxisAlignment.spaceBetween,
+      crossAxisAlignment: CrossAxisAlignment.center,
+      children: children,
+    );
+  }
+}
+
+class VDynamicDrawerWrapper extends StatefulWidget {
+  final GlobalKey<ScaffoldState> scaffoldKey;
+
+  const VDynamicDrawerWrapper({super.key, required this.scaffoldKey});
+
+  @override
+  State<StatefulWidget> createState() => _VDynamicDrawerWrapperState();
+
+  static void show({
+    required GlobalKey<ScaffoldState> scaffoldKey,
+    required WidgetBuilder builder,
+  }) {
+    _builderMap[scaffoldKey] = builder;
+    scaffoldKey.currentState?.openEndDrawer();
+  }
+
+  static void hide({required GlobalKey<ScaffoldState> scaffoldKey}) {
+    scaffoldKey.currentState?.closeEndDrawer();
+  }
+}
+
+class _VDynamicDrawerWrapperState extends State<VDynamicDrawerWrapper> {
+  @override
+  Widget build(BuildContext context) {
+    final builder = _builderMap[widget.scaffoldKey];
+    if (builder != null) {
+      return builder(context);
+    }
+    return const SizedBox();
+  }
+}

+ 36 - 27
lib/components/search_input.dart

@@ -15,33 +15,42 @@ class VSearchInput extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    final controller = TextEditingController();
-    const border = OutlineInputBorder(
-      borderRadius: BorderRadius.all(Radius.circular(8)),
-      borderSide: BorderSide.none,
-    );
-    return TextField(
-      controller: controller,
-      decoration: InputDecoration(
-        enabledBorder: border,
-        focusedBorder: border,
-        fillColor: Colors.grey.shade400,
-        filled: true,
-        hintText: placeholder,
-        hintStyle: const TextStyle(
-          fontSize: 20,
-          color: Colors.black54,
-        ),
-        // contentPadding: EdgeInsets.symmetric(horizontal: 40, vertical: 24),
-        // isDense: true,
-        isCollapsed: false,
-        prefixIcon: IconButton(
-          icon: const Icon(Icons.search, size: 36),
-          onPressed: () {
-            onSearch?.call(controller.text);
-          },
-        ),
-      ),
+    return LayoutBuilder(
+      builder: (_, c) {
+        final controller = TextEditingController();
+        final radius = c.maxHeight / 2;
+        final border = OutlineInputBorder(
+          borderRadius: BorderRadius.all(Radius.circular(radius)),
+          borderSide: BorderSide.none,
+        );
+        return TextField(
+          controller: controller,
+          decoration: InputDecoration(
+            enabledBorder: border,
+            focusedBorder: border,
+            fillColor: Colors.grey.shade400,
+            filled: true,
+            hintText: placeholder,
+            hintStyle: const TextStyle(
+              fontSize: 20,
+              color: Colors.black54,
+            ),
+            contentPadding: EdgeInsets.symmetric(
+              horizontal: radius * 1.2,
+              vertical: (c.maxHeight - 24) / 2,
+            ),
+            // isDense: true,
+            isCollapsed: false,
+            prefixIcon: IconButton(
+              padding: const EdgeInsets.symmetric(horizontal: 18),
+              icon: const Icon(Icons.search, size: 36),
+              onPressed: () {
+                onSearch?.call(controller.text);
+              },
+            ),
+          ),
+        );
+      },
     );
   }
 }

+ 8 - 3
lib/main.dart

@@ -33,9 +33,14 @@ class _App extends StatelessWidget {
     return GetMaterialApp(
       title: "家医一体机",
       theme: ThemeData(
-        colorScheme: ColorScheme.fromSeed(
-          seedColor: const Color.fromRGBO(44, 119, 229, 1),
-        ),
+        // primaryColor: const Color.fromRGBO(0, 178, 237, 1),
+        primaryColor: Colors.blue,
+        // colorScheme: ColorScheme.fromSeed(
+        //   // seedColor: const Color.fromRGBO(44, 119, 229, 1),
+        //   // seedColor: Colors.lightBlue,
+        //   seedColor: const Color.fromRGBO(0, 178, 237, 1),
+        // ),
+        // colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
         useMaterial3: true,
       ),
       debugShowCheckedModeBanner: false,

+ 20 - 0
lib/pages/controllers/crowd_labels.dart

@@ -147,4 +147,24 @@ class CrowdLablesState {
     array.addAll(_selectedSpecialCareCodes);
     return array;
   }
+
+  /// 所有 已选择标签名称集合
+  List<String> get selectedNames {
+    final array = <String>[];
+    for (var code in _selectedNormalCodes) {
+      array.add(_getNameByCode(_normalOptions, code)!);
+    }
+    for (var code in _selectedDiseaseCodes) {
+      array.add(_getNameByCode(_diseaseOptions, code)!);
+    }
+    for (var code in _selectedSpecialCareCodes) {
+      array.add(_getNameByCode(_specialCareOptions, code)!);
+    }
+    return array;
+  }
+
+  static String? _getNameByCode(List<LabelDTO> source, String code) {
+    final dto = source.firstWhereOrNull((e) => e.code == code);
+    return dto?.labelName;
+  }
 }

+ 8 - 0
lib/pages/controllers/home_nav_mixin.dart

@@ -0,0 +1,8 @@
+import 'package:flutter/material.dart';
+import 'package:vnoteapp/architecture/defines.dart';
+
+final _homeGlobalScaffoldKey = GlobalKey<ScaffoldState>();
+
+mixin HomeNavMixin on FControllerBase {
+  GlobalKey<ScaffoldState> get homeScaffoldKey => _homeGlobalScaffoldKey;
+}

+ 3 - 3
lib/pages/dashboard/demo.dart

@@ -47,9 +47,9 @@ class DashboardDemoView extends StatelessWidget {
             Container(
               height: 40,
               padding: const EdgeInsets.symmetric(horizontal: 14),
-              decoration: const BoxDecoration(
-                color: Color.fromRGBO(0, 178, 237, 1),
-                borderRadius: BorderRadius.only(
+              decoration: BoxDecoration(
+                color: Theme.of(context).primaryColor,
+                borderRadius: const BorderRadius.only(
                   topLeft: borderRadius,
                   topRight: borderRadius,
                 ),

+ 2 - 1
lib/pages/home/controller.dart

@@ -5,12 +5,13 @@ import 'package:get/get.dart';
 import 'package:vnoteapp/architecture/defines.dart';
 import 'package:vnoteapp/architecture/utils/prompt_box.dart';
 import 'package:vnoteapp/managers/interfaces/account.dart';
+import 'package:vnoteapp/pages/controllers/home_nav_mixin.dart';
 import 'package:vnoteapp/pages/home/models/menu.dart';
 import 'package:vnoteapp/store/store.dart';
 
 import 'state.dart';
 
-class HomeController extends FControllerBase {
+class HomeController extends FControllerBase with HomeNavMixin {
   final state = HomeState();
 
   /// 登出

+ 15 - 6
lib/pages/home/view.dart

@@ -1,5 +1,6 @@
 import 'package:flutter/material.dart';
 import 'package:get/get.dart';
+import 'package:vnoteapp/components/dynamic_drawer.dart.dart';
 import 'package:vnoteapp/pages/home/controller.dart';
 
 import 'widgets/avatar.dart';
@@ -20,14 +21,18 @@ class HomePage extends GetView<HomeController> {
       child: LayoutBuilder(
         builder: (context, c) {
           return Scaffold(
+            key: controller.homeScaffoldKey,
             // resizeToAvoidBottomInset: false,
             backgroundColor: const Color.fromRGBO(238, 238, 238, 1),
+            endDrawer: VDynamicDrawerWrapper(
+              scaffoldKey: controller.homeScaffoldKey,
+            ),
             body: Column(
               mainAxisSize: MainAxisSize.max,
               children: [
                 SizedBox(
                   height: 90,
-                  child: _buildHeader(),
+                  child: _buildHeader(context),
                 ),
                 const SizedBox(height: 2),
                 // Expanded(child: _buildBody(context)),
@@ -50,7 +55,8 @@ class HomePage extends GetView<HomeController> {
       children: [
         Container(
           width: 120,
-          color: const Color.fromRGBO(34, 164, 211, 1),
+          // color: const Color.fromRGBO(34, 164, 211, 1),
+          color: Theme.of(context).primaryColor.withOpacity(.8),
           child: Stack(
             children: [
               Positioned(
@@ -99,15 +105,18 @@ class HomePage extends GetView<HomeController> {
     );
   }
 
-  Widget _buildHeader() {
+  Widget _buildHeader(BuildContext context) {
+    final themeData = Theme.of(context);
     return Container(
       alignment: Alignment.center,
       padding: const EdgeInsets.symmetric(horizontal: 20),
-      decoration: const BoxDecoration(
+      decoration: BoxDecoration(
         gradient: LinearGradient(
           colors: [
-            Color.fromRGBO(59, 188, 255, 1),
-            Color.fromRGBO(44, 120, 229, 1),
+            // Color.fromRGBO(59, 188, 255, 1),
+            // Color.fromRGBO(44, 120, 229, 1),
+            themeData.primaryColor.withOpacity(.6),
+            themeData.primaryColor,
           ],
           begin: Alignment.topCenter,
           end: Alignment.bottomCenter,

+ 2 - 1
lib/pages/patient/create/controller.dart

@@ -3,11 +3,12 @@ import 'package:get/get.dart';
 import 'package:vnoteapp/architecture/defines.dart';
 import 'package:vnoteapp/managers/interfaces/patient.dart';
 import 'package:vnoteapp/pages/controllers/crowd_labels.dart';
+import 'package:vnoteapp/pages/controllers/home_nav_mixin.dart';
 import 'package:vnoteapp/pages/home/controller.dart';
 import 'package:vnoteapp/pages/patient/create/state.dart';
 import 'package:vnoteapp/pages/patient/list/controller.dart';
 
-class CreatePatientController extends FControllerBase {
+class CreatePatientController extends FControllerBase with HomeNavMixin {
   final _patientManager = Get.find<IPatientManager>();
 
   final state = CreatePatientState();

+ 2 - 0
lib/pages/patient/create/view.dart

@@ -13,6 +13,8 @@ import 'package:vnoteapp/consts/rpc_enum_labels.dart';
 import 'package:vnoteapp/pages/controllers/crowd_labels.dart';
 import 'package:vnoteapp/pages/patient/create/controller.dart';
 
+export './view_new.dart';
+
 class CreatePatientPage extends GetView<CreatePatientController> {
   const CreatePatientPage({super.key});
 

+ 101 - 0
lib/pages/patient/create/view_new.dart

@@ -0,0 +1,101 @@
+import 'package:fis_jsonrpc/rpc.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:vnoteapp/components/cell.dart';
+import 'package:vnoteapp/components/checkbox_button.dart';
+import 'package:vnoteapp/components/dynamic_drawer.dart.dart';
+import 'package:vnoteapp/pages/controllers/crowd_labels.dart';
+import 'package:vnoteapp/pages/patient/create/controller.dart';
+
+class CreatePatientPageNew extends GetView<CreatePatientController> {
+  const CreatePatientPageNew({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      alignment: Alignment.topCenter,
+      child: SizedBox(
+        width: 800,
+        child: Scrollbar(
+          child: ListView(
+            shrinkWrap: true,
+            children: [
+              _CrowdLabelPanel(),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+}
+
+class _CrowdLabelPanel extends GetView<CrowdLabelsController> {
+  @override
+  Widget build(BuildContext context) {
+    final createController = Get.find<CreatePatientController>();
+    return VListFormCellGroup(
+      children: [
+        VListFormCell(
+          label: "人群分类",
+          contentWidget: _buildContent(context),
+          onTap: () {
+            VDynamicDrawerWrapper.show(
+              scaffoldKey: createController.homeScaffoldKey,
+              builder: (_) => _buildDrawer(),
+            );
+          },
+        ),
+      ],
+    );
+  }
+
+  Widget _buildDrawer() {
+    final state = controller.state;
+    return VDrawer(
+      width: 800,
+      child: Column(
+        children: [
+          VCheckBoxButtonGroup<LabelDTO, String>(
+            source: state.normalOptions,
+            values: state.selectedNormalCodes,
+            labelGetter: (LabelDTO data) => data.labelName!,
+            valueGetter: (LabelDTO data) => data.code!,
+            onChanged: (value) {
+              state.selectedNormalCodes = value;
+            },
+          ),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildContent(BuildContext context) {
+    return Obx(() {
+      final themeData = Theme.of(context);
+      final color = themeData.secondaryHeaderColor;
+      final names = controller.state.selectedNames;
+      const itemHeight = 32.0;
+      const itemRadius = itemHeight / 2;
+      final itemTextStyle = TextStyle(
+        color: themeData.primaryColor,
+        fontSize: 14,
+      );
+      return Wrap(
+        children: names.map(
+          (e) {
+            return Container(
+              height: itemHeight,
+              alignment: Alignment.center,
+              decoration: BoxDecoration(
+                color: color,
+                border: Border.all(style: BorderStyle.none),
+                borderRadius: BorderRadius.circular(itemRadius),
+              ),
+              child: Text(e, style: itemTextStyle),
+            );
+          },
+        ).toList(),
+      );
+    });
+  }
+}

+ 1 - 0
lib/pages/patient/detail/view.dart

@@ -14,6 +14,7 @@ class PatientDetailPage extends GetView<PatientDetailController> {
       backgroundColor: const Color.fromRGBO(238, 238, 238, 1),
       // appBar: VAppBar(titleText: "${controller.state.name}档案详情"),
       appBar: VAppBar(
+        context: context,
         titleWidget: Obx(
           () => Text(
             "${controller.state.name}档案详情",

+ 3 - 3
lib/pages/patient/list/view.dart

@@ -116,7 +116,7 @@ class _HeaderWidget extends GetView<PatientListController> {
           ),
           const SizedBox(width: 8),
           SizedBox(
-            width: 140,
+            width: 180,
             height: 70,
             child: VButton(
               child: const Row(
@@ -133,7 +133,7 @@ class _HeaderWidget extends GetView<PatientListController> {
           ),
           const SizedBox(width: 8),
           SizedBox(
-            width: 110,
+            width: 150,
             height: 70,
             child: VButton(
               child: const Row(
@@ -290,7 +290,7 @@ class _PatientSignStatusTag extends StatelessWidget {
       height: 36,
       alignment: Alignment.center,
       decoration: BoxDecoration(
-        color: Colors.blue,
+        color: Theme.of(context).primaryColor,
         borderRadius: BorderRadius.only(
           topRight: radius,
           bottomLeft: radius,

+ 1 - 1
lib/pages/splash/view.dart

@@ -58,7 +58,7 @@ class _ImageAnimationState extends State<ImageAnimation>
   @override
   Widget build(BuildContext context) {
     return Scaffold(
-      backgroundColor: Colors.blue,
+      backgroundColor: Theme.of(context).primaryColor,
       body: Center(
         child: AnimatedBuilder(
           animation: _animation,

+ 2 - 1
lib/routes/routes.dart

@@ -91,7 +91,8 @@ class Routes {
       name: "/patient/create",
       // participatesInRootNavigator: false,
       // preventDuplicates: true,
-      page: () => const CreatePatientPage(),
+      page: () => const CreatePatientPageNew(),
+      // page: () => const CreatePatientPage(),
       binding: BindingsBuilder(
         () {
           Get.put(CrowdLabelsController());