Просмотр исходного кода

Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev

“shanwenxin” 1 неделя назад
Родитель
Сommit
b342a2e65d
100 измененных файлов с 6474 добавлено и 338 удалено
  1. 2 2
      app/android/app/build.gradle
  2. 4 0
      app/devtools_options.yaml
  3. 64 76
      app/lib/main.dart
  4. 1 4
      app/lib/router/component/app_service_provider.dart
  5. 15 11
      melos.yaml
  6. 4 0
      packages/cpt_auth/devtools_options.yaml
  7. 231 0
      packages/cpt_auth/lib/modules/auth_login/auth_login_page.dart
  8. 70 0
      packages/cpt_auth/lib/modules/auth_login/auth_login_state.dart
  9. 131 0
      packages/cpt_auth/lib/modules/auth_login/auth_login_view_model.dart
  10. 3 3
      packages/cpt_auth/lib/modules/auth_login/vm/auth_login_view_model.g.dart
  11. 0 34
      packages/cpt_auth/lib/modules/auth_login/page/Profile_edit_page.dart
  12. 182 0
      packages/cpt_auth/lib/modules/forgot_input/forgot_input_page.dart
  13. 50 0
      packages/cpt_auth/lib/modules/forgot_input/forgot_input_state.dart
  14. 91 0
      packages/cpt_auth/lib/modules/forgot_input/forgot_input_view_model.dart
  15. 27 0
      packages/cpt_auth/lib/modules/forgot_input/forgot_input_view_model.g.dart
  16. 232 0
      packages/cpt_auth/lib/modules/forgot_verify/forgot_verify_page.dart
  17. 78 0
      packages/cpt_auth/lib/modules/forgot_verify/forgot_verify_state.dart
  18. 155 0
      packages/cpt_auth/lib/modules/forgot_verify/forgot_verify_view_model.dart
  19. 27 0
      packages/cpt_auth/lib/modules/forgot_verify/forgot_verify_view_model.g.dart
  20. 205 0
      packages/cpt_auth/lib/modules/select_estate/select_estate_page.dart
  21. 33 0
      packages/cpt_auth/lib/modules/select_estate/select_estate_state.dart
  22. 24 0
      packages/cpt_auth/lib/modules/select_estate/select_estate_view_model.dart
  23. 27 0
      packages/cpt_auth/lib/modules/select_estate/select_estate_view_model.g.dart
  24. 64 0
      packages/cpt_auth/lib/modules/select_role/screen_owner.dart
  25. 57 0
      packages/cpt_auth/lib/modules/select_role/screen_tenant.dart
  26. 115 0
      packages/cpt_auth/lib/modules/select_role/select_role_page.dart
  27. 15 0
      packages/cpt_auth/lib/modules/select_role/select_role_state.dart
  28. 23 0
      packages/cpt_auth/lib/modules/select_role/select_role_view_model.dart
  29. 27 0
      packages/cpt_auth/lib/modules/select_role/select_role_view_model.g.dart
  30. 294 0
      packages/cpt_auth/lib/modules/select_unit/select_unit_page.dart
  31. 47 0
      packages/cpt_auth/lib/modules/select_unit/select_unit_state.dart
  32. 24 0
      packages/cpt_auth/lib/modules/select_unit/select_unit_view_model.dart
  33. 27 0
      packages/cpt_auth/lib/modules/select_unit/select_unit_view_model.g.dart
  34. 304 0
      packages/cpt_auth/lib/modules/sign_up/sign_up_page.dart
  35. 107 0
      packages/cpt_auth/lib/modules/sign_up/sign_up_state.dart
  36. 198 0
      packages/cpt_auth/lib/modules/sign_up/sign_up_view_model.dart
  37. 10 10
      packages/cpt_property/lib/modules/property/vm/property_view_model.g.dart
  38. 92 0
      packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_full_screen.dart
  39. 38 0
      packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_page.dart
  40. 90 0
      packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_small_screen.dart
  41. 15 0
      packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_view_model.dart
  42. 27 0
      packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_view_model.g.dart
  43. 169 0
      packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_page.dart
  44. 8 0
      packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_state.dart
  45. 31 0
      packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_view_model.dart
  46. 27 0
      packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_view_model.g.dart
  47. 160 0
      packages/cpt_auth/lib/modules/tenant_doc/tenant_doc_page.dart
  48. 22 0
      packages/cpt_auth/lib/modules/tenant_doc/tenant_doc_state.dart
  49. 32 0
      packages/cpt_auth/lib/modules/tenant_doc/tenant_doc_view_model.dart
  50. 27 0
      packages/cpt_auth/lib/modules/tenant_doc/tenant_doc_view_model.g.dart
  51. 3 6
      packages/cpt_auth/lib/router/component/auth_component_service.dart
  52. 20 2
      packages/cpt_auth/lib/router/page/auth_page_router.dart
  53. 232 1
      packages/cpt_auth/lib/router/page/auth_page_router.gr.dart
  54. 3 0
      packages/cpt_auth/pubspec.yaml
  55. 4 0
      packages/cpt_community/devtools_options.yaml
  56. 4 0
      packages/cpt_facility/devtools_options.yaml
  57. 4 0
      packages/cpt_form/devtools_options.yaml
  58. 4 0
      packages/cpt_main/devtools_options.yaml
  59. 365 0
      packages/cpt_main/lib/modules/home/home_page.dart
  60. 103 0
      packages/cpt_main/lib/modules/home/home_state.dart
  61. 73 0
      packages/cpt_main/lib/modules/home/home_view_model.dart
  62. 26 0
      packages/cpt_main/lib/modules/home/home_view_model.g.dart
  63. 24 0
      packages/cpt_main/lib/modules/home/item_home_category.dart
  64. 60 0
      packages/cpt_main/lib/modules/home/item_home_last_news.dart
  65. 72 0
      packages/cpt_main/lib/modules/home/item_home_last_trans.dart
  66. 53 0
      packages/cpt_main/lib/modules/home/item_home_manage_guide.dart
  67. 49 0
      packages/cpt_main/lib/modules/home/item_home_property_news.dart
  68. 0 112
      packages/cpt_main/lib/modules/home/page/home_page.dart
  69. 117 0
      packages/cpt_main/lib/modules/main/main_page.dart
  70. 86 0
      packages/cpt_main/lib/modules/main/main_state.dart
  71. 15 0
      packages/cpt_main/lib/modules/main/main_view_model.dart
  72. 26 0
      packages/cpt_main/lib/modules/main/main_view_model.g.dart
  73. 0 66
      packages/cpt_main/lib/modules/main/page/main_page.dart
  74. 0 0
      packages/cpt_main/lib/modules/me/me_page.dart
  75. 7 3
      packages/cpt_main/lib/modules/notification/notification_page.dart
  76. 4 2
      packages/cpt_main/lib/modules/visitor/page/visitor_page.dart
  77. 0 0
      packages/cpt_main/lib/modules/visitor/visitor_state.dart
  78. 0 0
      packages/cpt_main/lib/modules/visitor/visitor_view_model.dart
  79. 0 0
      packages/cpt_main/lib/modules/visitor/visitor_view_model.g.dart
  80. 1 1
      packages/cpt_main/lib/router/component/main_component_service_impl.dart
  81. 4 4
      packages/cpt_main/lib/router/page/main_page_router.dart
  82. 4 0
      packages/cpt_notice_board/devtools_options.yaml
  83. 4 0
      packages/cpt_payment/devtools_options.yaml
  84. 4 0
      packages/cpt_profile/devtools_options.yaml
  85. 271 0
      packages/cpt_profile/lib/modules/change_mobile/change_mobile_page.dart
  86. 79 0
      packages/cpt_profile/lib/modules/change_mobile/change_mobile_state.dart
  87. 161 0
      packages/cpt_profile/lib/modules/change_mobile/change_mobile_view_model.dart
  88. 27 0
      packages/cpt_profile/lib/modules/change_mobile/change_mobile_view_model.g.dart
  89. 1 1
      packages/cpt_profile/lib/modules/profile_edit/page/Profile_edit_page.dart
  90. 0 0
      packages/cpt_profile/lib/modules/profile_edit/profile_edit_view_model.dart
  91. 0 0
      packages/cpt_profile/lib/modules/profile_edit/profile_edit_view_model.g.dart
  92. 282 0
      packages/cpt_profile/lib/modules/reset_password/reset_password_page.dart
  93. 85 0
      packages/cpt_profile/lib/modules/reset_password/reset_password_state.dart
  94. 158 0
      packages/cpt_profile/lib/modules/reset_password/reset_password_view_model.dart
  95. 27 0
      packages/cpt_profile/lib/modules/reset_password/reset_password_view_model.g.dart
  96. 133 0
      packages/cpt_profile/lib/modules/setting/dialog/account_deactivation_dialog.dart
  97. 105 0
      packages/cpt_profile/lib/modules/setting/setting_page.dart
  98. 15 0
      packages/cpt_profile/lib/modules/setting/setting_state.dart
  99. 53 0
      packages/cpt_profile/lib/modules/setting/setting_view_model.dart
  100. 0 0
      packages/cpt_profile/lib/modules/setting/setting_view_model.g.dart

+ 2 - 2
app/android/app/build.gradle

@@ -49,7 +49,7 @@ android {
     }
 
     defaultConfig {
-        applicationId "com.hongyegroup.property_management"
+        applicationId "com.hongyegroup.proanperty_management"
         minSdkVersion 21
         targetSdkVersion 34
         versionCode 100          //Android打包上线记得要加固并重新签名再传
@@ -59,7 +59,7 @@ android {
 
         ndk {
             //选择要添加的对应 cpu 类型的 .so 库。
-//            abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
+            abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
             abiFilters 'armeabi-v7a', 'arm64-v8a'
 //            abiFilters 'armeabi-v7a'
 //            abiFilters 'arm64-v8a'

+ 4 - 0
app/devtools_options.yaml

@@ -0,0 +1,4 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
+  - provider: true

+ 64 - 76
app/lib/main.dart

@@ -107,7 +107,7 @@ class MyApp extends HookConsumerWidget {
         usePenetrate: false,
       )
       ..loading = SmartConfigLoading(
-        backDismiss: true,
+        // backDismiss: true,
         clickMaskDismiss: true,
       )
       ..toast = SmartConfigToast(
@@ -136,17 +136,11 @@ class MyApp extends HookConsumerWidget {
           systemUiOverlayStyle = ThemeConfig.systemUiOverlayStyleDarkTheme;
           break;
         default:
-          Brightness currentBrightness = MediaQuery.of(context).platformBrightness;
-          if (currentBrightness == Brightness.dark) {
-            Log.d("main.dart - 跟随系统模式-暗色模式");
-            systemUiOverlayStyle = ThemeConfig.systemUiOverlayStyleDarkTheme;
-          } else {
-            Log.d("main.dart - 跟随系统模式-亮色模式");
-            systemUiOverlayStyle = ThemeConfig.systemUiOverlayStyleLightThemeBlack;
-          }
+          systemUiOverlayStyle = ThemeConfig.getSystemUiOverlayStyleByTheme(context);
           break;
       }
     }
+
     SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
 
     useEffect(() {
@@ -160,78 +154,72 @@ class MyApp extends HookConsumerWidget {
       };
     }, []);
 
-    //全局入口
-    // return AnnotatedRegion<SystemUiOverlayStyle>(
-    //   value: systemUiOverlayStyle,
-    //   child:
-
-   return   KeyboardVisibilityBuilder(builder: (context, isKeyboardVisible) {
-        return KeyboardDismissOnTap(
-          dismissOnCapturedTaps: false,
-          child: MaterialApp.router(
-            title: 'PropertyManagementSystem',
-            debugShowCheckedModeBanner: true,
+    return KeyboardVisibilityBuilder(builder: (context, isKeyboardVisible) {
+      return KeyboardDismissOnTap(
+        dismissOnCapturedTaps: false,
+        child: MaterialApp.router(
+          title: 'PropertyManagementSystem',
+          debugShowCheckedModeBanner: true,
 
-            //主题配置
-            theme: ThemeData(
-              colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF4161D0)),
-              useMaterial3: false,
-            ).copyWith(extensions: [
-              AppColorsTheme.light(),
-            ]),
-            darkTheme: ThemeData(
-              colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF4161D0), brightness: Brightness.dark),
-              useMaterial3: false,
-            ).copyWith(extensions: [
-              AppColorsTheme.dark(),
-            ]),
-            themeMode: themeMode == ThemeMode.system ? ThemeMode.system : themeMode,
+          //主题配置
+          theme: ThemeData(
+            colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF4161D0)),
+            useMaterial3: false,
+          ).copyWith(extensions: [
+            AppColorsTheme.light(),
+          ]),
+          darkTheme: ThemeData(
+            colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF4161D0), brightness: Brightness.dark),
+            useMaterial3: false,
+          ).copyWith(extensions: [
+            AppColorsTheme.dark(),
+          ]),
+          themeMode: themeMode == ThemeMode.system ? ThemeMode.system : themeMode,
 
-            //国际化配置
-            localizationsDelegates: const [
-              S.delegate,
-              GlobalMaterialLocalizations.delegate,
-              GlobalCupertinoLocalizations.delegate,
-              GlobalWidgetsLocalizations.delegate,
-            ],
-            //国际化英语为首选项
-            supportedLocales: [const Locale('en', ''), ...S.delegate.supportedLocales],
-            localeResolutionCallback: (locale, supportLocales) {
-              // 中文 简繁体处理
-              if (locale?.languageCode == 'zh') {
-                if (locale?.scriptCode == 'Hant') {
-                  return const Locale('zh', 'HK'); //繁体
-                } else {
-                  return const Locale('zh', 'CN'); //简体
-                }
+          //国际化配置
+          localizationsDelegates: const [
+            S.delegate,
+            GlobalMaterialLocalizations.delegate,
+            GlobalCupertinoLocalizations.delegate,
+            GlobalWidgetsLocalizations.delegate,
+          ],
+          //国际化英语为首选项
+          supportedLocales: [const Locale('en', ''), ...S.delegate.supportedLocales],
+          localeResolutionCallback: (locale, supportLocales) {
+            // 中文 简繁体处理
+            if (locale?.languageCode == 'zh') {
+              if (locale?.scriptCode == 'Hant') {
+                return const Locale('zh', 'HK'); //繁体
+              } else {
+                return const Locale('zh', 'CN'); //简体
               }
-              return null;
+            }
+            return null;
+          },
+          //AutoRouter的配置
+          routerConfig: appRouter.config(
+            navigatorObservers: () => [
+              MyNavigatorObserver(),
+            ],
+          ),
+          //SmartDialog初始化默认Loading与Toast
+          builder: FlutterSmartDialog.init(
+            toastBuilder: (String msg) {
+              return CustomToastWidget(msg: msg);
             },
-            //AutoRouter的配置
-            routerConfig: appRouter.config(
-              navigatorObservers: () => [
-                MyNavigatorObserver(),
-              ],
-            ),
-            //SmartDialog初始化默认Loading与Toast
-            builder: FlutterSmartDialog.init(
-              toastBuilder: (String msg) {
-                return CustomToastWidget(msg: msg);
-              },
-              loadingBuilder: (String msg) {
-                return CustomLoadingWidget(msg: msg == 'loading...' ? 'Loading...' : msg);
-              },
-              notifyStyle: FlutterSmartNotifyStyle(
-                successBuilder: (String msg) => CustomSuccessWidget(msg: msg),
-                failureBuilder: (String msg) => CustomFailureWidget(msg: msg),
-                errorBuilder: (String msg) => CustomErrorWidget(msg: msg),
-                alertBuilder: (String msg) => CustomErrorWidget(msg: msg),
-                warningBuilder: (String msg) => CustomErrorWidget(msg: msg),
-              ),
+            loadingBuilder: (String msg) {
+              return CustomLoadingWidget(msg: msg == 'loading...' ? 'Loading...' : msg);
+            },
+            notifyStyle: FlutterSmartNotifyStyle(
+              successBuilder: (String msg) => CustomSuccessWidget(msg: msg),
+              failureBuilder: (String msg) => CustomFailureWidget(msg: msg),
+              errorBuilder: (String msg) => CustomErrorWidget(msg: msg),
+              alertBuilder: (String msg) => CustomErrorWidget(msg: msg),
+              warningBuilder: (String msg) => CustomErrorWidget(msg: msg),
             ),
           ),
-        );
-      });
-    // );
+        ),
+      );
+    });
   }
 }

+ 1 - 4
app/lib/router/component/app_service_provider.dart

@@ -1,15 +1,12 @@
-import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:riverpod_annotation/riverpod_annotation.dart';
 import 'package:router/componentRouter/app_service.dart';
-import 'package:router/componentRouter/main_service.dart';
 
 import 'app_component_service_impl.dart';
 
-
 part 'app_service_provider.g.dart';
 
 @Riverpod(keepAlive: true)
 AppService appService(Ref ref) {
   return AppComponentServiceImpl();
-}
+}

+ 15 - 11
melos.yaml

@@ -44,47 +44,51 @@ scripts:
         - build_runner
 
   build_runner_auth:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_auth" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_auth" && dart run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in auth module
 
   build_runner_community:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_community" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_community" && dart run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in community module
 
   build_runner_facility:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_facility" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_facility" && dart run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in facility module
 
   build_runner_form:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_form" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_form" && dart run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in form module
 
   build_runner_main:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_main" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_main" && dart run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in main module
 
   build_runner_notice_board:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_notice_board" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_notice_board" && dart run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in notice_board module
 
   build_runner_payment:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_payment" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_payment" && dart run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in payment module
 
   build_runner_profile:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_profile" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_profile" && dart run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in profile module
 
   build_runner_property:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_property" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_property" && flutter pub run build_runner build --delete-conflicting-outputs --verbose
+    description: Run `dart run build_runner build` in property module
+
+  watch_runner_property:
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_property" && flutter pub run build_runner watch --delete-conflicting-outputs --verbose
     description: Run `dart run build_runner build` in property module
 
   build_runner_rewards:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_rewards" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_rewards" && dart run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in rewards module
 
   build_runner_services:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_services" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_services" && dart run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in services module
 
   clean_all:

+ 4 - 0
packages/cpt_auth/devtools_options.yaml

@@ -0,0 +1,4 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
+  - provider: true

+ 231 - 0
packages/cpt_auth/lib/modules/auth_login/auth_login_page.dart

@@ -0,0 +1,231 @@
+import 'package:cpt_auth/modules/auth_login/auth_login_state.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_field.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/shatter/custom_check_box.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../router/page/auth_page_router.dart';
+import 'auth_login_view_model.dart';
+
+/*
+ * 用户的登录页面
+ */
+@RoutePage()
+class AuthLoginPage extends HookConsumerWidget {
+  const AuthLoginPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.navigate(const AuthLoginPageRoute());
+    } else {
+      appRouter.navigate(const AuthLoginPageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.read(authLoginViewModelProvider.notifier);
+    final state = ref.watch(authLoginViewModelProvider);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, "", showBackButton: false),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: SingleChildScrollView(
+        scrollDirection: Axis.vertical,
+        physics: const BouncingScrollPhysics(),
+        child: Container(
+          margin: const EdgeInsets.symmetric(horizontal: 38),
+          width: double.infinity,
+          child: Column(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              //顶部Logo
+              const MyAssetImage(
+                Assets.assetsYyHomeLogo,
+                width: 85.5,
+                height: 85.5,
+              ).marginOnly(top: 45, bottom: 45),
+
+              // 登录表单 - 账号
+              _buildInputLayout(
+                context,
+                state,
+                "account",
+                textInputAction: TextInputAction.next,
+                errorText: state.accountErrorText,
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  FocusScope.of(context).requestFocus(state.formData['password']!['focusNode']);
+                },
+              ),
+
+              // 登录表单 - 密码
+              _buildInputLayout(
+                context,
+                state,
+                "password",
+                marginTop: 15,
+                obscureText: !state.pwdVisibility,
+                errorText: state.passwordErrorText,
+                showRightIcon: true,
+                rightWidget: IconButton(
+                  highlightColor: Colors.transparent,
+                  splashColor: Colors.transparent,
+                  icon: state.pwdVisibility
+                      ? const MyAssetImage(
+                          Assets.authPasswordHide,
+                          width: 22.5,
+                          height: 16.5,
+                        )
+                      : const MyAssetImage(
+                          Assets.authPasswordShow,
+                          width: 22.5,
+                          height: 16.5,
+                        ),
+                  onPressed: () {
+                    viewModel.switchPwdVisibility();
+                  },
+                ),
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  viewModel.doLogin();
+                },
+              ),
+
+              //登录按钮
+              MyButton(
+                onPressed: viewModel.doLogin,
+                text: S.current.login,
+                isEnabled: state.isLoginBtnEnable,
+                textColor: Colors.white,
+                disabledTextColor: Colors.white,
+                backgroundColor: context.appColors.btnBgDefault,
+                disabledBackgroundColor: context.appColors.btnBgDefault.withOpacity(0.2),
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                minHeight: 50,
+                radius: 5,
+              ).marginOnly(top: 20, bottom: 23),
+
+              //忘记密码
+              MyTextView(
+                S.current.forgot_password,
+                isFontMedium: true,
+                fontSize: 16,
+                textColor: context.appColors.textPrimary,
+                onClick: viewModel.gotoForgotPage,
+              ),
+
+              //创建账户-注册
+              MyButton(
+                onPressed: viewModel.gotoSignUpPage,
+                text: S.current.create_new_yy_home_account,
+                textColor: Colors.white,
+                backgroundColor: context.appColors.btnBgDefault,
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                minHeight: 50,
+                radius: 5,
+              ).marginOnly(top: 28),
+
+              //同意协议
+              Row(
+                mainAxisSize: MainAxisSize.min,
+                children: [
+                  MyAssetImage(
+                    state.isAgreeTerms ? Assets.baseServiceCheckBoxChecked : Assets.baseServiceCheckBoxUncheck,
+                    width: 15.5,
+                    height: 15.5,
+                  ).onTap(viewModel.switchAgreeTerms, padding: 10),
+                  RichText(
+                    text: TextSpan(
+                      children: [
+                        TextSpan(
+                          text: S.current.agree_to,
+                          style: TextStyle(color: context.appColors.textDarkGray, fontWeight: FontWeight.w500, fontSize: 15), // 灰色文本
+                        ),
+                        const TextSpan(
+                          text: " ",
+                        ),
+                        TextSpan(
+                          text: S.current.terms_of_service,
+                          style: TextStyle(color: context.appColors.textPrimary, fontWeight: FontWeight.w500, fontSize: 15), // 蓝色文本
+                          recognizer: TapGestureRecognizer()..onTap = viewModel.gotoTermsPage,
+                        ),
+                      ],
+                    ),
+                  ),
+                ],
+              ).marginOnly(top: 30, bottom: 30),
+
+              //结束
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  /// 输入框 账号与密码
+  Widget _buildInputLayout(
+    BuildContext context,
+    LoginState state,
+    String key, {
+    double marginTop = 0,
+    bool? showRightIcon = false, //是否展示右侧的布局
+    Widget? rightWidget, //右侧的布局
+    TextInputType textInputType = TextInputType.text,
+    String? errorText,
+    bool obscureText = false,
+    TextInputAction textInputAction = TextInputAction.done,
+    Function? onSubmit,
+  }) {
+    return IgnoreKeyboardDismiss(
+      child: MyTextField(
+        key,
+        fillBackgroundColor: context.appColors.authFiledBG,
+        state.formData[key]!['value'],
+        hintText: state.formData[key]!['hintText'],
+        hintStyle: TextStyle(
+          color: context.appColors.authFiledHint,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        controller: state.formData[key]!['controller'],
+        focusNode: state.formData[key]!['focusNode'],
+        margin: EdgeInsets.only(top: marginTop),
+        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
+        showDivider: false,
+        height: 44,
+        style: TextStyle(
+          color: context.appColors.authFiledText,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        inputType: textInputType,
+        textInputAction: textInputAction,
+        onSubmit: onSubmit,
+        cursorColor: context.appColors.authFiledText,
+        obscureText: obscureText,
+        errorText: errorText,
+        showLeftIcon: true,
+        showRightIcon: showRightIcon,
+        rightWidget: rightWidget,
+      ),
+    );
+  }
+}

+ 70 - 0
packages/cpt_auth/lib/modules/auth_login/auth_login_state.dart

@@ -0,0 +1,70 @@
+/*
+ * 登录的状态类
+ */
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:flutter/material.dart';
+
+class LoginState {
+  //表单的校验与数据
+  final Map<String, Map<String, dynamic>> formData;
+
+  //表单的错误信息展示
+  String? accountErrorText;
+  String? passwordErrorText;
+
+  //是否明文展示密码
+  bool pwdVisibility;
+
+  //登录按钮是否可用
+  bool isLoginBtnEnable;
+
+  bool isAgreeTerms;
+
+  // ===================================  Begin  ↓  ===================================
+
+  LoginState({
+    Map<String, Map<String, dynamic>>? formData,
+    this.accountErrorText,
+    this.passwordErrorText,
+    this.pwdVisibility = false,
+    this.isLoginBtnEnable = false,
+    this.isAgreeTerms = false,
+  }) : formData = formData ??
+            {
+              'account': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.phone_email,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'password': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.password,
+                'focusNode': FocusNode(),
+                'obsecure': true,
+              },
+            };
+
+  LoginState copyWith({
+    String? accountErrorText,
+    String? passwordErrorText,
+    String? account,
+    String? password,
+    bool? pwdVisibility,
+    bool? isLoginBtnEnable,
+    bool? isAgreeTerms,
+  }) {
+    return LoginState(
+      formData: this.formData,
+      accountErrorText: accountErrorText,
+      passwordErrorText: passwordErrorText,
+      pwdVisibility: pwdVisibility ?? this.pwdVisibility,
+      isLoginBtnEnable: isLoginBtnEnable ?? this.isLoginBtnEnable,
+      isAgreeTerms: isAgreeTerms ?? this.isLoginBtnEnable,
+    );
+  }
+
+
+}

+ 131 - 0
packages/cpt_auth/lib/modules/auth_login/auth_login_view_model.dart

@@ -0,0 +1,131 @@
+import 'package:cpt_auth/modules/sign_up/sign_up_page.dart';
+import 'package:flutter/material.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/util.dart';
+
+import '../forgot_input/forgot_input_page.dart';
+import 'auth_login_state.dart';
+
+part 'auth_login_view_model.g.dart';
+
+@riverpod
+class AuthLoginViewModel extends _$AuthLoginViewModel {
+  @override
+  LoginState build() {
+    final state = LoginState();
+
+    initListener(state);
+    ref.onDispose(() {
+      onDispose(state);
+    });
+    return state;
+  }
+
+  //切换是否隐藏密码
+  void switchPwdVisibility() {
+    state = state.copyWith(pwdVisibility: !state.pwdVisibility);
+  }
+
+  //执行用户的登录
+  void doLogin() {
+    state = state.copyWith(accountErrorText: null, passwordErrorText: null);
+
+    final FocusNode accountFocusNode = state.formData['account']!['focusNode'];
+    final FocusNode passwordFocusNode = state.formData['password']!['focusNode'];
+    final TextEditingController accountController = state.formData['account']!['controller'];
+    final TextEditingController passwordController = state.formData['password']!['controller'];
+
+    accountFocusNode.unfocus();
+    passwordFocusNode.unfocus();
+
+    final account = accountController.text;
+    final password = passwordController.text;
+
+    Log.d('当前待提交的 账号:${account} password:${password}');
+
+    if (Utils.isEmpty(account)) {
+      state = state.copyWith(accountErrorText: "Phone/Email cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(password)) {
+      state = state.copyWith(passwordErrorText: "Password cannot be empty!");
+      return;
+    }
+
+    //执行密码登录
+    ToastEngine.show('准备执行请求 账号:${account} password:${password}');
+  }
+
+  //去协议页面
+  void gotoTermsPage() {
+    ToastEngine.show("去协议页面");
+  }
+
+  //去忘记密码页面
+  void gotoForgotPage() {
+    ForgotInputPage.startInstance();
+  }
+
+  //去注册页面
+  void gotoSignUpPage() {
+    SignUpPage.startInstance();
+  }
+
+  //切换同意按钮
+  void switchAgreeTerms(){
+    state = state.copyWith(isAgreeTerms: !state.isAgreeTerms);
+  }
+
+  //初始化监听
+  void initListener(LoginState initState) {
+    final FocusNode accountFocusNode = initState.formData['account']!['focusNode'];
+    final FocusNode passwordFocusNode = initState.formData['password']!['focusNode'];
+    final TextEditingController accountController = initState.formData['account']!['controller'];
+    final TextEditingController passwordController = initState.formData['password']!['controller'];
+
+    accountFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (accountFocusNode.hasFocus) {
+        state = state.copyWith(accountErrorText: null);
+      }
+    });
+
+    passwordFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (passwordFocusNode.hasFocus) {
+        state = state.copyWith(passwordErrorText: null);
+      }
+    });
+
+    //文本框的监听输入
+    accountController.addListener(() {
+      _updateButtonState(accountController, passwordController);
+    });
+
+    passwordController.addListener(() {
+      _updateButtonState(accountController, passwordController);
+    });
+  }
+
+  //当 Account 和 Password 都有值才能按钮可用
+  void _updateButtonState(TextEditingController accountController, TextEditingController passwordController) {
+    state = state.copyWith(isLoginBtnEnable: accountController.text.isNotEmpty && passwordController.text.isNotEmpty);
+  }
+
+  //销毁资源
+  void onDispose(LoginState initState) {
+    final FocusNode accountFocusNode = initState.formData['account']!['focusNode'];
+    final FocusNode passwordFocusNode = initState.formData['password']!['focusNode'];
+    final TextEditingController accountController = initState.formData['account']!['controller'];
+    final TextEditingController passwordController = initState.formData['password']!['controller'];
+    accountFocusNode.dispose();
+    passwordFocusNode.dispose();
+    accountController.dispose();
+    passwordController.dispose();
+    Log.d("LoginViewModel 销毁 onDispose");
+  }
+
+}

+ 3 - 3
packages/cpt_auth/lib/modules/auth_login/vm/auth_login_view_model.g.dart

@@ -7,12 +7,12 @@ part of 'auth_login_view_model.dart';
 // **************************************************************************
 
 String _$authLoginViewModelHash() =>
-    r'10a37f5f6fead251c1df197123d10ce07fe0594c';
+    r'45f0dfb96b894b4df90cd94bbd921c26344d3a96';
 
 /// See also [AuthLoginViewModel].
 @ProviderFor(AuthLoginViewModel)
 final authLoginViewModelProvider =
-    AutoDisposeNotifierProvider<AuthLoginViewModel, void>.internal(
+    AutoDisposeNotifierProvider<AuthLoginViewModel, LoginState>.internal(
   AuthLoginViewModel.new,
   name: r'authLoginViewModelProvider',
   debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
@@ -22,6 +22,6 @@ final authLoginViewModelProvider =
   allTransitiveDependencies: null,
 );
 
-typedef _$AuthLoginViewModel = AutoDisposeNotifier<void>;
+typedef _$AuthLoginViewModel = AutoDisposeNotifier<LoginState>;
 // ignore_for_file: type=lint
 // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 0 - 34
packages/cpt_auth/lib/modules/auth_login/page/Profile_edit_page.dart

@@ -1,34 +0,0 @@
-
-import 'package:flutter/material.dart';
-import 'package:auto_route/auto_route.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:router/ext/auto_router_extensions.dart';
-
-import '../../../router/page/auth_page_router.dart';
-import '../vm/auth_login_view_model.dart';
-
-@RoutePage()
-class AuthLoginPage extends HookConsumerWidget {
-  const AuthLoginPage({Key? key}) : super(key: key);
-
-  //启动当前页面
-  static void startInstance({BuildContext? context}) {
-    if (context != null) {
-      context.router.push(const AuthLoginPageRoute());
-    } else {
-      appRouter.push(const AuthLoginPageRoute());
-    }
-  }
-
-  @override
-  Widget build(BuildContext context, WidgetRef ref) {
-    final _viewModel = ref.read(authLoginViewModelProvider.notifier);
-
-    return Scaffold(
-      appBar: AppBar(title: Text("登录")),
-      body: Center(
-        child: Text("用户登录页面"),
-      ),
-    );
-  }
-}

+ 182 - 0
packages/cpt_auth/lib/modules/forgot_input/forgot_input_page.dart

@@ -0,0 +1,182 @@
+import 'package:cpt_auth/modules/auth_login/auth_login_page.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_field.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../router/page/auth_page_router.dart';
+import 'forgot_input_state.dart';
+import 'forgot_input_view_model.dart';
+
+@RoutePage()
+class ForgotInputPage extends HookConsumerWidget {
+  const ForgotInputPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(const ForgotInputPageRoute());
+    } else {
+      appRouter.push(const ForgotInputPageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.read(forgotInputViewModelProvider.notifier);
+    final state = ref.watch(forgotInputViewModelProvider);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, ""),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: SingleChildScrollView(
+        scrollDirection: Axis.vertical,
+        physics: const BouncingScrollPhysics(),
+        child: Container(
+          margin: const EdgeInsets.symmetric(horizontal: 38),
+          width: double.infinity,
+          child: Column(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              //顶部图片
+              const MyAssetImage(
+                Assets.authForgotInputEmailPhoneImg,
+                width: 260,
+                height: 170,
+              ).marginOnly(top: 25, bottom: 29),
+
+              //提示文本
+              MyTextView(
+                S.current.forgot_text,
+                fontSize: 24,
+                marginBottom: 23,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              //邮箱输入框
+              _buildInputLayout(
+                context,
+                state,
+                "email",
+                textInputType: TextInputType.emailAddress,
+                textInputAction: TextInputAction.next,
+                errorText: state.emailErrorText,
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  FocusScope.of(context).requestFocus(state.formData['phone']!['focusNode']);
+                },
+              ),
+
+              //电话号码
+              Row(
+                mainAxisSize: MainAxisSize.max,
+                crossAxisAlignment: CrossAxisAlignment.center,
+                children: [
+                  const MyAssetImage(
+                    Assets.authCountrySg,
+                    width: 45,
+                    height: 30,
+                  ),
+                  MyTextView(
+                    "+65",
+                    textColor: context.appColors.textBlack,
+                    fontSize: 18.5,
+                    marginLeft: 15,
+                    marginRight: 12,
+                    isFontMedium: true,
+                  ),
+                  //电话输入框
+                  _buildInputLayout(
+                    context,
+                    state,
+                    "phone",
+                    textInputType: TextInputType.number,
+                    textInputAction: TextInputAction.done,
+                    errorText: state.phoneErrorText,
+                    onSubmit: (formKey, value) {
+                      state.formData[formKey]!['focusNode'].unfocus();
+                      viewModel.submitInput();
+                    },
+                  ).expanded(),
+                ],
+              ).marginOnly(top: 15),
+
+              MyButton(
+                onPressed: viewModel.submitInput,
+                text: S.current.next,
+                textColor: Colors.white,
+                backgroundColor: context.appColors.btnBgDefault,
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                fontSize: 16,
+                minHeight: 50,
+                radius: 5,
+              ).marginOnly(top: 25, bottom: 30),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  /// 输入框
+  Widget _buildInputLayout(
+    BuildContext context,
+    ForgotInputState state,
+    String key, {
+    double marginTop = 0,
+    bool? showRightIcon = false, //是否展示右侧的布局
+    Widget? rightWidget, //右侧的布局
+    TextInputType textInputType = TextInputType.text,
+    String? errorText,
+    bool obscureText = false,
+    TextInputAction textInputAction = TextInputAction.done,
+    Function? onSubmit,
+  }) {
+    return IgnoreKeyboardDismiss(
+      child: MyTextField(
+        key,
+        fillBackgroundColor: context.appColors.authFiledBG,
+        state.formData[key]!['value'],
+        hintText: state.formData[key]!['hintText'],
+        hintStyle: TextStyle(
+          color: context.appColors.authFiledHint,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        controller: state.formData[key]!['controller'],
+        focusNode: state.formData[key]!['focusNode'],
+        margin: EdgeInsets.only(top: marginTop),
+        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
+        showDivider: false,
+        height: 44,
+        style: TextStyle(
+          color: context.appColors.authFiledText,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        inputType: textInputType,
+        textInputAction: textInputAction,
+        onSubmit: onSubmit,
+        cursorColor: context.appColors.authFiledText,
+        obscureText: obscureText,
+        errorText: errorText,
+        showLeftIcon: true,
+        showRightIcon: showRightIcon,
+        rightWidget: rightWidget,
+      ),
+    );
+  }
+}

+ 50 - 0
packages/cpt_auth/lib/modules/forgot_input/forgot_input_state.dart

@@ -0,0 +1,50 @@
+/*
+ * 登录的状态类
+ */
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:flutter/material.dart';
+
+class ForgotInputState {
+  //表单的校验与数据
+  final Map<String, Map<String, dynamic>> formData;
+
+  //表单的错误信息展示
+  String? emailErrorText;
+  String? phoneErrorText;
+
+
+  // ===================================  Begin  ↓  ===================================
+
+  ForgotInputState({
+    Map<String, Map<String, dynamic>>? formData,
+    this.emailErrorText,
+    this.phoneErrorText,
+  }) : formData = formData ??
+      {
+        'email': {
+          'value': '',
+          'controller': TextEditingController(),
+          'hintText': S.current.email,
+          'focusNode': FocusNode(),
+          'obsecure': false,
+        },
+        'phone': {
+          'value': '',
+          'controller': TextEditingController(),
+          'hintText': S.current.mobile_phone,
+          'focusNode': FocusNode(),
+          'obsecure': false,
+        },
+      };
+
+  ForgotInputState copyWith({
+    String? emailErrorText,
+    String? phoneErrorText,
+  }) {
+    return ForgotInputState(
+      formData: this.formData,
+      emailErrorText: emailErrorText,
+      phoneErrorText: phoneErrorText,
+    );
+  }
+}

+ 91 - 0
packages/cpt_auth/lib/modules/forgot_input/forgot_input_view_model.dart

@@ -0,0 +1,91 @@
+import 'package:cpt_auth/modules/forgot_verify/forgot_verify_page.dart';
+import 'package:flutter/material.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/util.dart';
+
+import 'forgot_input_state.dart';
+
+part 'forgot_input_view_model.g.dart';
+
+@riverpod
+class ForgotInputViewModel extends _$ForgotInputViewModel {
+  @override
+  ForgotInputState build() {
+    final state = ForgotInputState();
+    initListener(state);
+    ref.onDispose(() {
+      onDispose(state);
+    });
+    return state;
+  }
+
+  //提交忘记密码表单,发送验证码
+  void submitInput() {
+    state = state.copyWith(emailErrorText: null, phoneErrorText: null);
+
+    final FocusNode emailFocusNode = state.formData['email']!['focusNode'];
+    final FocusNode phoneFocusNode = state.formData['phone']!['focusNode'];
+    final TextEditingController emailController = state.formData['email']!['controller'];
+    final TextEditingController phoneController = state.formData['phone']!['controller'];
+
+    emailFocusNode.unfocus();
+    phoneFocusNode.unfocus();
+
+    final email = emailController.text;
+    final phone = phoneController.text;
+
+    Log.d('当前待提交的 邮箱:${email} 电话:${phone}');
+
+    // if (Utils.isEmpty(email)) {
+    //   state = state.copyWith(emailErrorText: "Email cannot be empty!");
+    //   return;
+    // }
+    //
+    // if (Utils.isEmpty(phone)) {
+    //   state = state.copyWith(phoneErrorText: "Phone cannot be empty!");
+    //   return;
+    // }
+
+    //执行密码登录
+    ToastEngine.show('准备执行请求发送验证码 邮箱:${email} 电话:${phone}');
+
+    gotoForgotVerifyPage();
+  }
+
+  //去忘记密码验证页面
+  void gotoForgotVerifyPage() {
+    ForgotVerifyPage.startInstance();
+  }
+
+  //初始化监听
+  void initListener(ForgotInputState initState) {
+    final FocusNode emailFocusNode = initState.formData['email']!['focusNode'];
+    final FocusNode phoneFocusNode = initState.formData['phone']!['focusNode'];
+
+    emailFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (emailFocusNode.hasFocus) {
+        state = state.copyWith(emailErrorText: null);
+      }
+    });
+
+    phoneFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (phoneFocusNode.hasFocus) {
+        state = state.copyWith(phoneErrorText: null);
+      }
+    });
+  }
+
+  //销毁资源
+  void onDispose(ForgotInputState initState) {
+    final FocusNode emailFocusNode = initState.formData['email']!['focusNode'];
+    final FocusNode phoneFocusNode = initState.formData['phone']!['focusNode'];
+    emailFocusNode.dispose();
+    phoneFocusNode.dispose();
+    Log.d("ForgotInputViewModel 销毁 onDispose");
+  }
+
+}

+ 27 - 0
packages/cpt_auth/lib/modules/forgot_input/forgot_input_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'forgot_input_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$forgotInputViewModelHash() =>
+    r'5998f4831003ebb7e6ebe822bc002580f13b1962';
+
+/// See also [ForgotInputViewModel].
+@ProviderFor(ForgotInputViewModel)
+final forgotInputViewModelProvider = AutoDisposeNotifierProvider<
+    ForgotInputViewModel, ForgotInputState>.internal(
+  ForgotInputViewModel.new,
+  name: r'forgotInputViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$forgotInputViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$ForgotInputViewModel = AutoDisposeNotifier<ForgotInputState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 232 - 0
packages/cpt_auth/lib/modules/forgot_verify/forgot_verify_page.dart

@@ -0,0 +1,232 @@
+import 'package:cpt_auth/modules/forgot_verify/forgot_verify_state.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_field.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../router/page/auth_page_router.dart';
+import 'forgot_verify_view_model.dart';
+
+@RoutePage()
+class ForgotVerifyPage extends HookConsumerWidget {
+  const ForgotVerifyPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(const ForgotVerifyPageRoute());
+    } else {
+      appRouter.push(const ForgotVerifyPageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.read(forgotVerifyViewModelProvider.notifier);
+    final state = ref.watch(forgotVerifyViewModelProvider);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, ""),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: SingleChildScrollView(
+        scrollDirection: Axis.vertical,
+        physics: const BouncingScrollPhysics(),
+        child: Container(
+          margin: const EdgeInsets.symmetric(horizontal: 38),
+          width: double.infinity,
+          child: Column(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              //顶部图片
+              const MyAssetImage(
+                Assets.authForgotResetPasswordImg,
+                width: 260,
+                height: 170,
+              ).marginOnly(top: 25, bottom: 29),
+
+              //提示文本
+              MyTextView(
+                S.current.mobile_phone,
+                fontSize: 15,
+                marginBottom: 23,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              MyTextView(
+                "+65 88249621",
+                fontSize: 23,
+                marginBottom: 23,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              // 表单 - 验证码
+              _buildInputLayout(
+                context,
+                state,
+                "code",
+                textInputType: TextInputType.number,
+                textInputAction: TextInputAction.next,
+                errorText: state.codeErrorText,
+                showRightIcon: true,
+                rightWidget: MyTextView(
+                  state.isCounting? "${state.countdownTime} s": S.current.get_code,
+                  textAlign: TextAlign.center,
+                  textColor: context.appColors.textPrimary,
+                  fontSize: 15,
+                  paddingRight: 5,
+                  isFontMedium: true,
+                  onClick: state.isCounting ? null : () => viewModel.showVerifyCodedDialog(),
+                ).paddingOnly(top: 15, bottom: 15),
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  FocusScope.of(context).requestFocus(state.formData['password']!['focusNode']);
+                },
+              ),
+
+              // 表单 - 重置密码
+              _buildInputLayout(
+                context,
+                state,
+                "password",
+                marginTop: 15,
+                obscureText: !state.pwdVisibility,
+                errorText: state.passwordErrorText,
+                textInputAction: TextInputAction.next,
+                showRightIcon: true,
+                rightWidget: IconButton(
+                  highlightColor: Colors.transparent,
+                  splashColor: Colors.transparent,
+                  icon: state.pwdVisibility
+                      ? const MyAssetImage(
+                          Assets.authPasswordHide,
+                          width: 22.5,
+                          height: 16.5,
+                        )
+                      : const MyAssetImage(
+                          Assets.authPasswordShow,
+                          width: 22.5,
+                          height: 16.5,
+                        ),
+                  onPressed: () {
+                    viewModel.switchPwdVisibility();
+                  },
+                ),
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  FocusScope.of(context).requestFocus(state.formData['confirm_password']!['focusNode']);
+                },
+              ),
+
+              // 表单 - 确认密码
+              _buildInputLayout(
+                context,
+                state,
+                "confirm_password",
+                marginTop: 15,
+                obscureText: !state.confirmPwdVisibility,
+                errorText: state.confirmPasswordErrorText,
+                showRightIcon: true,
+                rightWidget: IconButton(
+                  highlightColor: Colors.transparent,
+                  splashColor: Colors.transparent,
+                  icon: state.confirmPwdVisibility
+                      ? const MyAssetImage(
+                          Assets.authPasswordHide,
+                          width: 22.5,
+                          height: 16.5,
+                        )
+                      : const MyAssetImage(
+                          Assets.authPasswordShow,
+                          width: 22.5,
+                          height: 16.5,
+                        ),
+                  onPressed: () {
+                    viewModel.switchConfirmPwdVisibility();
+                  },
+                ),
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  viewModel.submitVerify();
+                },
+              ),
+
+              MyButton(
+                onPressed: viewModel.submitVerify,
+                text: S.current.next,
+                textColor: Colors.white,
+                backgroundColor: context.appColors.btnBgDefault,
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                fontSize: 16,
+                minHeight: 50,
+                radius: 5,
+              ).marginOnly(top: 25, bottom: 30),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  /// 输入框
+  Widget _buildInputLayout(
+    BuildContext context,
+    ForgotVerifyState state,
+    String key, {
+    double marginTop = 0,
+    bool? showRightIcon = false, //是否展示右侧的布局
+    Widget? rightWidget, //右侧的布局
+    TextInputType textInputType = TextInputType.text,
+    String? errorText,
+    bool obscureText = false,
+    TextInputAction textInputAction = TextInputAction.done,
+    Function? onSubmit,
+  }) {
+    return IgnoreKeyboardDismiss(
+      child: MyTextField(
+        key,
+        fillBackgroundColor: context.appColors.authFiledBG,
+        state.formData[key]!['value'],
+        hintText: state.formData[key]!['hintText'],
+        hintStyle: TextStyle(
+          color: context.appColors.authFiledHint,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        controller: state.formData[key]!['controller'],
+        focusNode: state.formData[key]!['focusNode'],
+        margin: EdgeInsets.only(top: marginTop),
+        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
+        showDivider: false,
+        height: 44,
+        style: TextStyle(
+          color: context.appColors.authFiledText,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        inputType: textInputType,
+        textInputAction: textInputAction,
+        onSubmit: onSubmit,
+        cursorColor: context.appColors.authFiledText,
+        obscureText: obscureText,
+        errorText: errorText,
+        showLeftIcon: true,
+        showRightIcon: showRightIcon,
+        rightWidget: rightWidget,
+      ),
+    );
+  }
+}

+ 78 - 0
packages/cpt_auth/lib/modules/forgot_verify/forgot_verify_state.dart

@@ -0,0 +1,78 @@
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:flutter/material.dart';
+
+class ForgotVerifyState {
+  //表单的校验与数据
+  final Map<String, Map<String, dynamic>> formData;
+
+  //表单的错误信息展示
+  String? codeErrorText;
+  String? passwordErrorText;
+  String? confirmPasswordErrorText;
+
+  //是否明文展示密码
+  bool pwdVisibility;
+
+  bool confirmPwdVisibility;
+
+  //获取验证码的倒计时
+  bool isCounting;
+  int countdownTime;
+
+  // ===================================  Begin  ↓  ===================================
+
+  ForgotVerifyState({
+    Map<String, Map<String, dynamic>>? formData,
+    this.pwdVisibility = false,
+    this.confirmPwdVisibility = false,
+    this.isCounting = false,
+    this.countdownTime = 0,
+    this.codeErrorText,
+    this.passwordErrorText,
+    this.confirmPasswordErrorText,
+  }) : formData = formData ??
+            {
+              'code': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.verification_code,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'password': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.reset_password,
+                'focusNode': FocusNode(),
+                'obsecure': true,
+              },
+              'confirm_password': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.confirm_password,
+                'focusNode': FocusNode(),
+                'obsecure': true,
+              },
+            };
+
+  ForgotVerifyState copyWith({
+    String? codeErrorText,
+    String? passwordErrorText,
+    String? confirmPasswordErrorText,
+    bool? pwdVisibility,
+    bool? confirmPwdVisibility,
+    bool? isCounting,
+    int? countdownTime,
+  }) {
+    return ForgotVerifyState(
+      formData: this.formData,
+      pwdVisibility: pwdVisibility ?? this.pwdVisibility,
+      confirmPwdVisibility: confirmPwdVisibility ?? this.confirmPwdVisibility,
+      isCounting: isCounting ?? this.isCounting,
+      countdownTime: countdownTime ?? this.countdownTime,
+      codeErrorText: codeErrorText,
+      passwordErrorText: passwordErrorText,
+      confirmPasswordErrorText: confirmPasswordErrorText,
+    );
+  }
+}

+ 155 - 0
packages/cpt_auth/lib/modules/forgot_verify/forgot_verify_view_model.dart

@@ -0,0 +1,155 @@
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/util.dart';
+
+import '../auth_login/auth_login_page.dart';
+import 'forgot_verify_state.dart';
+
+part 'forgot_verify_view_model.g.dart';
+
+@riverpod
+class ForgotVerifyViewModel extends _$ForgotVerifyViewModel {
+  @override
+  ForgotVerifyState build() {
+    final state = ForgotVerifyState();
+    initListener(state);
+    ref.onDispose(() {
+      onDispose(state);
+    });
+    return state;
+  }
+
+  //提交重置密码的校验
+  void submitVerify() {
+    state = state.copyWith(codeErrorText: null, passwordErrorText: null, confirmPasswordErrorText: null);
+
+    final FocusNode codeFocusNode = state.formData['code']!['focusNode'];
+    final FocusNode passwordFocusNode = state.formData['password']!['focusNode'];
+    final FocusNode confirmPasswordFocusNode = state.formData['confirm_password']!['focusNode'];
+
+    final TextEditingController codeController = state.formData['code']!['controller'];
+    final TextEditingController passwordController = state.formData['password']!['controller'];
+    final TextEditingController confirmPasswordController = state.formData['confirm_password']!['controller'];
+
+    codeFocusNode.unfocus();
+    passwordFocusNode.unfocus();
+    confirmPasswordFocusNode.unfocus();
+
+    final code = codeController.text;
+    final password = passwordController.text;
+    final confirmPassword = confirmPasswordController.text;
+
+    Log.d('当前待提交的 code:$code password:$password confirmPassword:$confirmPassword');
+
+    if (Utils.isEmpty(code)) {
+      state = state.copyWith(codeErrorText: "Verification Code cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(password)) {
+      state = state.copyWith(passwordErrorText: "Password cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(confirmPassword)) {
+      state = state.copyWith(confirmPasswordErrorText: "Confirm Password cannot be empty!");
+      return;
+    }
+
+    if (confirmPassword != password) {
+      state = state.copyWith(confirmPasswordErrorText: "Password mismatch, please check password");
+      return;
+    }
+
+    //执行密码登录
+    ToastEngine.show('准备执行请求发送验证码 code:$code password:$password confirmPassword:$confirmPassword');
+
+    gotoLoginPage();
+  }
+
+  //去登录页面
+  void gotoLoginPage() {
+    AuthLoginPage.startInstance();
+  }
+
+  //切换隐藏显示密码
+  void switchPwdVisibility() {
+    state = state.copyWith(pwdVisibility: !state.pwdVisibility);
+  }
+
+  void switchConfirmPwdVisibility() {
+    state = state.copyWith(confirmPwdVisibility: !state.confirmPwdVisibility);
+  }
+
+  // 验证弹窗
+  void showVerifyCodedDialog() {
+    _startCountDown();
+  }
+
+  Timer? countdownTimer;
+
+  /// 开启倒计时
+  void _startCountDown() {
+    //60秒倒计时
+    state = state.copyWith(isCounting: true, countdownTime: 60);
+
+    //每秒的倒计时
+    countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
+      int time = state.countdownTime;
+      Log.d('倒计时-->$time');
+      if (time > 0) {
+        time--;
+        state = state.copyWith(isCounting: true, countdownTime: time);
+      } else {
+        state = state.copyWith(isCounting: false, countdownTime: 0);
+        countdownTimer?.cancel(); // 取消计时器
+      }
+    });
+  }
+
+  //初始化监听
+  void initListener(ForgotVerifyState initState) {
+    final FocusNode codeFocusNode = initState.formData['code']!['focusNode'];
+    final FocusNode passwordFocusNode = initState.formData['password']!['focusNode'];
+    final FocusNode confirmPasswordFocusNode = initState.formData['confirm_password']!['focusNode'];
+
+    codeFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (codeFocusNode.hasFocus) {
+        state = state.copyWith(codeErrorText: null);
+      }
+    });
+
+    passwordFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (passwordFocusNode.hasFocus) {
+        state = state.copyWith(passwordErrorText: null);
+      }
+    });
+
+    confirmPasswordFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (confirmPasswordFocusNode.hasFocus) {
+        state = state.copyWith(confirmPasswordErrorText: null);
+      }
+    });
+  }
+
+  //销毁资源
+  void onDispose(ForgotVerifyState initState) {
+    final FocusNode codeFocusNode = initState.formData['code']!['focusNode'];
+    final FocusNode passwordFocusNode = initState.formData['password']!['focusNode'];
+    final FocusNode confirmPasswordFocusNode = initState.formData['confirm_password']!['focusNode'];
+    codeFocusNode.dispose();
+    passwordFocusNode.dispose();
+    confirmPasswordFocusNode.dispose();
+
+    countdownTimer?.cancel();
+
+    Log.d("ForgotVerifyViewModel 销毁 onDispose");
+  }
+}

+ 27 - 0
packages/cpt_auth/lib/modules/forgot_verify/forgot_verify_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'forgot_verify_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$forgotVerifyViewModelHash() =>
+    r'9ca96e6da48e893d90ed8b0dc939ebae4c1fb940';
+
+/// See also [ForgotVerifyViewModel].
+@ProviderFor(ForgotVerifyViewModel)
+final forgotVerifyViewModelProvider = AutoDisposeNotifierProvider<
+    ForgotVerifyViewModel, ForgotVerifyState>.internal(
+  ForgotVerifyViewModel.new,
+  name: r'forgotVerifyViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$forgotVerifyViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$ForgotVerifyViewModel = AutoDisposeNotifier<ForgotVerifyState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 205 - 0
packages/cpt_auth/lib/modules/select_estate/select_estate_page.dart

@@ -0,0 +1,205 @@
+import 'package:cpt_auth/modules/select_estate/select_estate_state.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_field.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../router/page/auth_page_router.dart';
+import 'select_estate_view_model.dart';
+
+@RoutePage()
+class SelectEstatePage extends HookConsumerWidget {
+  SelectEstatePage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(SelectEstatePageRoute());
+    } else {
+      appRouter.push(SelectEstatePageRoute());
+    }
+  }
+
+  // 为需要测量的控件创建 GlobalKey
+  final GlobalKey _appbarKey = GlobalKey();
+  final GlobalKey _topImageKey = GlobalKey();
+  final GlobalKey _titleKey = GlobalKey();
+  final GlobalKey _inputKey = GlobalKey();
+  final GlobalKey _descriptionKey = GlobalKey();
+  final GlobalKey _buttonKey = GlobalKey();
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.watch(selectEstateViewModelProvider.notifier);
+    final state = ref.watch(selectEstateViewModelProvider);
+
+    // 获取屏幕高度
+    final screenHeight = MediaQuery.of(context).size.height;
+    final statusBarHeight = MediaQuery.of(context).padding.top;
+    final navigationBarHeight = MediaQuery.of(context).padding.bottom;
+
+    useEffect(() {
+      double usedHeight = 0;
+      // 组件挂载时执行,获取控件高度
+      WidgetsBinding.instance.addPostFrameCallback((_) {
+        // 获取各个控件的高度
+        usedHeight += _appbarKey.currentContext?.size?.height ?? 0;
+        usedHeight += _topImageKey.currentContext?.size?.height ?? 0;
+        usedHeight += _titleKey.currentContext?.size?.height ?? 0;
+        usedHeight += _inputKey.currentContext?.size?.height ?? 0;
+        usedHeight += _descriptionKey.currentContext?.size?.height ?? 0;
+        usedHeight += _buttonKey.currentContext?.size?.height ?? 0;
+
+        // 计算剩余空间
+        double remainingSpace = screenHeight - statusBarHeight - navigationBarHeight - usedHeight - 38 - 28 - 20 - 19;
+
+        Log.d("计算剩余空间:$remainingSpace");
+
+        if (remainingSpace > 0) {
+          // 设置一个状态来存储剩余空间的高度
+          viewModel.setRemainingSpace(remainingSpace);
+        }
+      });
+      return () {
+        // 组件卸载时执行
+      };
+    }, []);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, S.current.yy_home_accounts, key: _appbarKey),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: SingleChildScrollView(
+        scrollDirection: Axis.vertical,
+        physics: const BouncingScrollPhysics(),
+        child: Container(
+          padding: const EdgeInsets.symmetric(horizontal: 38),
+          width: double.infinity,
+          child: Column(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              //顶部图片
+              MyAssetImage(
+                key: _topImageKey,
+                Assets.authChooseEstateBuilding,
+                width: 267,
+                height: 158,
+              ).marginOnly(top: 28, bottom: 38),
+
+              MyTextView(
+                key: _titleKey,
+                S.current.estate_or_building_name,
+                fontSize: 23,
+                marginBottom: 20,
+                textAlign: TextAlign.center,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              //输入资产的名称
+              _buildInputLayout(
+                context,
+                state,
+                "estate",
+                key: _inputKey,
+                textInputAction: TextInputAction.done,
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                },
+              ),
+
+              MyTextView(
+                key: _descriptionKey,
+                S.current.estate_name_desc,
+                fontSize: 15,
+                marginTop: 19,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              SizedBox(
+                height: state.remainingSpace, // 使用剩余空间的值
+              ),
+
+              MyButton(
+                key: _buttonKey,
+                onPressed: viewModel.submitEstate,
+                text: S.current.next,
+                textColor: Colors.white,
+                backgroundColor: context.appColors.btnBgDefault,
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                fontSize: 16,
+                minHeight: 50,
+                radius: 5,
+              ).marginOnly(top: 40, bottom: 30),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  /// 输入框
+  Widget _buildInputLayout(
+    BuildContext context,
+    SelectEstateState state,
+    String formKey, {
+    Key? key,
+    double marginTop = 0,
+    bool? showRightIcon = false, //是否展示右侧的布局
+    Widget? rightWidget, //右侧的布局
+    TextInputType textInputType = TextInputType.text,
+    String? errorText,
+    bool obscureText = false,
+    TextInputAction textInputAction = TextInputAction.done,
+    Function? onSubmit,
+  }) {
+    return IgnoreKeyboardDismiss(
+      child: MyTextField(
+        formKey,
+        key: key,
+        fillBackgroundColor: context.appColors.authFiledBG,
+        state.formData[formKey]!['value'],
+        hintText: state.formData[formKey]!['hintText'],
+        hintStyle: TextStyle(
+          color: context.appColors.authFiledHint,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        controller: state.formData[formKey]!['controller'],
+        focusNode: state.formData[formKey]!['focusNode'],
+        margin: EdgeInsets.only(top: marginTop),
+        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
+        showDivider: false,
+        height: 44,
+        style: TextStyle(
+          color: context.appColors.authFiledText,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        inputType: textInputType,
+        textInputAction: textInputAction,
+        onSubmit: onSubmit,
+        cursorColor: context.appColors.authFiledText,
+        obscureText: obscureText,
+        errorText: errorText,
+        showLeftIcon: true,
+        showRightIcon: showRightIcon,
+        rightWidget: rightWidget,
+      ),
+    );
+  }
+}

+ 33 - 0
packages/cpt_auth/lib/modules/select_estate/select_estate_state.dart

@@ -0,0 +1,33 @@
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:flutter/material.dart';
+
+class SelectEstateState {
+  //表单的校验与数据
+  final Map<String, Map<String, dynamic>> formData;
+
+  double remainingSpace;
+
+  // ===================================  Begin  ↓  ===================================
+
+  SelectEstateState({
+    this.remainingSpace = 0,
+    Map<String, Map<String, dynamic>>? formData,
+  }) : formData = formData ??
+            {
+              'estate': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.type_here,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+            };
+
+  SelectEstateState copyWith({
+    double? remainingSpace,
+  }) {
+    return SelectEstateState(
+      remainingSpace: remainingSpace ?? this.remainingSpace,
+    );
+  }
+}

+ 24 - 0
packages/cpt_auth/lib/modules/select_estate/select_estate_view_model.dart

@@ -0,0 +1,24 @@
+
+import 'package:cpt_auth/modules/select_estate/select_estate_state.dart';
+import 'package:cpt_auth/modules/select_unit/select_unit_page.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+part 'select_estate_view_model.g.dart';
+
+@riverpod
+class SelectEstateViewModel extends _$SelectEstateViewModel {
+
+  @override
+  SelectEstateState build(){
+    return SelectEstateState();
+  }
+
+  /// 提交并进入下一步
+  void submitEstate() {
+    SelectUnitPage.startInstance();
+  }
+
+  void setRemainingSpace(double remainingSpace) {
+    state = state.copyWith(remainingSpace: remainingSpace);
+  }
+}

+ 27 - 0
packages/cpt_auth/lib/modules/select_estate/select_estate_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'select_estate_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$selectEstateViewModelHash() =>
+    r'44d0829cc1f4d48b2fa0991838a8217e77898786';
+
+/// See also [SelectEstateViewModel].
+@ProviderFor(SelectEstateViewModel)
+final selectEstateViewModelProvider = AutoDisposeNotifierProvider<
+    SelectEstateViewModel, SelectEstateState>.internal(
+  SelectEstateViewModel.new,
+  name: r'selectEstateViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$selectEstateViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$SelectEstateViewModel = AutoDisposeNotifier<SelectEstateState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 64 - 0
packages/cpt_auth/lib/modules/select_role/screen_owner.dart

@@ -0,0 +1,64 @@
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:widgets/my_text_view.dart';
+
+class ScreenOwner extends StatelessWidget {
+  const ScreenOwner({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return SingleChildScrollView(
+      scrollDirection: Axis.vertical,
+      physics: const BouncingScrollPhysics(),
+      child: Column(
+        mainAxisSize: MainAxisSize.max,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          MyTextView(
+            S.current.who_are_owners,
+            fontSize: 23.5,
+            marginTop: 25,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+          MyTextView(
+            S.current.owners_desc1,
+            fontSize: 15,
+            marginTop: 22,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+          MyTextView(
+            S.current.owners_desc2,
+            fontSize: 15,
+            marginTop: 22,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+          MyTextView(
+            S.current.owners_desc3,
+            fontSize: 15,
+            marginTop: 22,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+          MyTextView(
+            S.current.owners_desc4,
+            fontSize: 15,
+            marginTop: 22,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+          MyTextView(
+            S.current.owners_desc5,
+            fontSize: 15,
+            marginTop: 22,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 57 - 0
packages/cpt_auth/lib/modules/select_role/screen_tenant.dart

@@ -0,0 +1,57 @@
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:widgets/my_text_view.dart';
+
+class ScreenTenant extends StatelessWidget {
+  const ScreenTenant({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return SingleChildScrollView(
+      scrollDirection: Axis.vertical,
+      physics: const BouncingScrollPhysics(),
+      child: Column(
+        mainAxisSize: MainAxisSize.max,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          MyTextView(
+            S.current.who_are_tenants,
+            fontSize: 23.5,
+            marginTop: 25,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+          MyTextView(
+            S.current.tenants_desc1,
+            fontSize: 15,
+            marginTop: 22,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+          MyTextView(
+            S.current.tenants_desc2,
+            fontSize: 15,
+            marginTop: 22,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+          MyTextView(
+            S.current.tenants_desc3,
+            fontSize: 15,
+            marginTop: 22,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+          MyTextView(
+            S.current.tenants_desc4,
+            fontSize: 15,
+            marginTop: 22,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 115 - 0
packages/cpt_auth/lib/modules/select_role/select_role_page.dart

@@ -0,0 +1,115 @@
+import 'package:cpt_auth/modules/select_role/screen_owner.dart';
+import 'package:cpt_auth/modules/select_role/screen_tenant.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_text_view.dart';
+
+import '../../router/page/auth_page_router.dart';
+import 'select_role_view_model.dart';
+
+@RoutePage()
+class SelectRolePage extends HookConsumerWidget {
+  const SelectRolePage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(const SelectRolePageRoute());
+    } else {
+      appRouter.push(const SelectRolePageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.read(selectRoleViewModelProvider.notifier);
+    final state = ref.watch(selectRoleViewModelProvider);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, ""),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: Container(
+        padding: const EdgeInsets.symmetric(horizontal: 15),
+        width: double.infinity,
+        child: Column(
+          mainAxisSize: MainAxisSize.max,
+          crossAxisAlignment: CrossAxisAlignment.center,
+          children: [
+            MyTextView(
+              S.current.owner_or_tenant,
+              fontSize: 23.5,
+              marginTop: 25,
+              marginBottom: 25,
+              textAlign: TextAlign.center,
+              isFontMedium: true,
+              textColor: context.appColors.textBlack,
+            ),
+
+            //顶部的Tab切换
+            Row(
+              children: [
+                MyTextView(
+                  S.current.owner,
+                  textColor: state.selectedIndex == 0 ? context.appColors.tabTextSelectedPrimary : context.appColors.tabTextUnSelectedPrimary,
+                  fontSize: 16,
+                  isFontMedium: true,
+                  paddingTop: 16,
+                  paddingBottom: 16,
+                  cornerRadius: 5,
+                  textAlign: TextAlign.center,
+                  onClick: () {
+                    viewModel.selectRole(0);
+                  },
+                  backgroundColor: state.selectedIndex == 0 ? context.appColors.tabBgSelectedPrimary : context.appColors.tabBgUnSelectedPrimary,
+                ).expanded(),
+                MyTextView(
+                  S.current.tenant,
+                  textColor: state.selectedIndex == 1 ? context.appColors.tabTextSelectedPrimary : context.appColors.tabTextUnSelectedPrimary,
+                  fontSize: 16,
+                  marginLeft: 15,
+                  paddingTop: 16,
+                  paddingBottom: 16,
+                  textAlign: TextAlign.center,
+                  isFontMedium: true,
+                  cornerRadius: 5,
+                  onClick: () {
+                    viewModel.selectRole(1);
+                  },
+                  backgroundColor: state.selectedIndex == 1 ? context.appColors.tabBgSelectedPrimary : context.appColors.tabBgUnSelectedPrimary,
+                ).expanded(),
+              ],
+            ),
+
+            IndexedStack(
+              index: state.selectedIndex,
+              children: const [
+                ScreenOwner(),
+                ScreenTenant(),
+              ],
+            ).expanded(),
+
+            //底部的按钮
+            MyButton(
+              onPressed: viewModel.submitRole,
+              text: S.current.next,
+              textColor: Colors.white,
+              backgroundColor: context.appColors.btnBgDefault,
+              fontWeight: FontWeight.w500,
+              type: ClickType.throttle,
+              fontSize: 16,
+              minHeight: 50,
+              radius: 5,
+            ).marginOnly(top: 30, bottom: 30, left: 18, right: 18),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 15 - 0
packages/cpt_auth/lib/modules/select_role/select_role_state.dart

@@ -0,0 +1,15 @@
+class SelectRoleState {
+  int selectedIndex;
+
+  SelectRoleState({
+    this.selectedIndex = 0,
+  });
+
+  SelectRoleState copyWith({
+    int? selectedIndex,
+  }) {
+    return SelectRoleState(
+      selectedIndex: selectedIndex ?? this.selectedIndex,
+    );
+  }
+}

+ 23 - 0
packages/cpt_auth/lib/modules/select_role/select_role_view_model.dart

@@ -0,0 +1,23 @@
+import 'package:cpt_auth/modules/select_role/select_role_state.dart';
+import 'package:cpt_auth/modules/tenant_doc/tenant_doc_page.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+part 'select_role_view_model.g.dart';
+
+@riverpod
+class SelectRoleViewModel extends _$SelectRoleViewModel {
+  @override
+  SelectRoleState build() {
+    return SelectRoleState();
+  }
+
+  //进入下一步
+  void submitRole() {
+    TenantDocPage.startInstance();
+  }
+
+  //选择角色
+  void selectRole(int index) {
+    state = state.copyWith(selectedIndex: index);
+  }
+}

+ 27 - 0
packages/cpt_auth/lib/modules/select_role/select_role_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'select_role_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$selectRoleViewModelHash() =>
+    r'a42d4ba4dd03754e74e58b1a320793449326bef4';
+
+/// See also [SelectRoleViewModel].
+@ProviderFor(SelectRoleViewModel)
+final selectRoleViewModelProvider =
+    AutoDisposeNotifierProvider<SelectRoleViewModel, SelectRoleState>.internal(
+  SelectRoleViewModel.new,
+  name: r'selectRoleViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$selectRoleViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$SelectRoleViewModel = AutoDisposeNotifier<SelectRoleState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 294 - 0
packages/cpt_auth/lib/modules/select_unit/select_unit_page.dart

@@ -0,0 +1,294 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_field.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../router/page/auth_page_router.dart';
+import 'select_unit_state.dart';
+import 'select_unit_view_model.dart';
+
+@RoutePage()
+class SelectUnitPage extends HookConsumerWidget {
+  SelectUnitPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(SelectUnitPageRoute());
+    } else {
+      appRouter.push(SelectUnitPageRoute());
+    }
+  }
+
+  // 为需要测量的控件创建 GlobalKey
+  final GlobalKey _appbarKey = GlobalKey();
+  final GlobalKey _topImageKey = GlobalKey();
+  final GlobalKey _inputKey = GlobalKey();
+  final GlobalKey _description1Key = GlobalKey();
+  final GlobalKey _description2Key = GlobalKey();
+  final GlobalKey _description3Key = GlobalKey();
+  final GlobalKey _buttonKey = GlobalKey();
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.watch(selectUnitViewModelProvider.notifier);
+    final state = ref.watch(selectUnitViewModelProvider);
+
+    // 获取屏幕高度
+    final screenHeight = MediaQuery.of(context).size.height;
+    final statusBarHeight = MediaQuery.of(context).padding.top;
+    final navigationBarHeight = MediaQuery.of(context).padding.bottom;
+
+    useEffect(() {
+      double usedHeight = 0;
+      // 组件挂载时执行,获取控件高度
+      WidgetsBinding.instance.addPostFrameCallback((_) {
+        // 获取各个控件的高度
+        usedHeight += _appbarKey.currentContext?.size?.height ?? 0;
+        usedHeight += _topImageKey.currentContext?.size?.height ?? 0;
+        usedHeight += _inputKey.currentContext?.size?.height ?? 0;
+        usedHeight += _description1Key.currentContext?.size?.height ?? 0;
+        usedHeight += _description2Key.currentContext?.size?.height ?? 0;
+        usedHeight += _description3Key.currentContext?.size?.height ?? 0;
+        usedHeight += _buttonKey.currentContext?.size?.height ?? 0;
+
+        // 计算剩余空间
+        double remainingSpace = screenHeight - statusBarHeight - navigationBarHeight - usedHeight - 28 - 18 - 25 - 20 - 20;
+
+        Log.d("计算剩余空间:$remainingSpace");
+
+        if (remainingSpace > 0) {
+          // 设置一个状态来存储剩余空间的高度
+          viewModel.setRemainingSpace(remainingSpace);
+        }
+      });
+      return () {
+        // 组件卸载时执行
+      };
+    }, []);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, S.current.yy_home_accounts, key: _appbarKey),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: SingleChildScrollView(
+        scrollDirection: Axis.vertical,
+        physics: const BouncingScrollPhysics(),
+        child: Container(
+          padding: const EdgeInsets.symmetric(horizontal: 20),
+          width: double.infinity,
+          child: Column(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              //顶部图片
+              MyAssetImage(
+                key: _topImageKey,
+                Assets.authSignUpUnitImg,
+                width: 266.5,
+                height: 162,
+              ).marginOnly(top: 28, bottom: 18),
+
+              Row(
+                key: _inputKey,
+                mainAxisSize: MainAxisSize.min,
+                children: [
+                  //街区
+                  Column(
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    children: [
+                      MyTextView(
+                        S.current.block,
+                        marginBottom: 9,
+                        textColor: context.appColors.textBlack,
+                        fontSize: 16,
+                        isFontMedium: true,
+                      ),
+
+                      // 表单 - 街区
+                      _buildInputLayout(
+                        context,
+                        state,
+                        "block",
+                        textInputAction: TextInputAction.next,
+                        onSubmit: (formKey, value) {
+                          state.formData[formKey]!['focusNode'].unfocus();
+                          FocusScope.of(context).requestFocus(state.formData['unit']!['focusNode']);
+                        },
+                      ).constrained(width: 88),
+                    ],
+                  ),
+
+                  MyTextView(
+                    "#",
+                    marginTop: 20,
+                    marginLeft: 8.5,
+                    marginRight: 8.5,
+                    textColor: context.appColors.textBlack,
+                    fontSize: 16,
+                    isFontMedium: true,
+                  ),
+
+                  //单元
+                  Column(
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    children: [
+                      MyTextView(
+                        S.current.unit_number,
+                        marginBottom: 9,
+                        textColor: context.appColors.textBlack,
+                        fontSize: 16,
+                        isFontMedium: true,
+                      ),
+                      Row(
+                        children: [
+                          // 表单 - 单元
+                          _buildInputLayout(
+                            context,
+                            state,
+                            "unit",
+                            textInputAction: TextInputAction.next,
+                            onSubmit: (formKey, value) {
+                              state.formData[formKey]!['focusNode'].unfocus();
+                              FocusScope.of(context).requestFocus(state.formData['room']!['focusNode']);
+                            },
+                          ).constrained(width: 83),
+
+                          MyTextView(
+                            "-",
+                            textColor: context.appColors.textBlack,
+                            marginLeft: 4,
+                            marginRight: 4,
+                            isFontMedium: true,
+                          ),
+
+                          // 表单 - 房号
+                          _buildInputLayout(
+                            context,
+                            state,
+                            "room",
+                            textInputAction: TextInputAction.done,
+                            onSubmit: (formKey, value) {
+                              state.formData[formKey]!['focusNode'].unfocus();
+                            },
+                          ).constrained(width: 83),
+                        ],
+                      ),
+                    ],
+                  ),
+                ],
+              ),
+
+              MyTextView(
+                key: _description1Key,
+                S.current.block_desc,
+                fontSize: 15,
+                marginTop: 25,
+                textAlign: TextAlign.center,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              MyTextView(
+                key: _description2Key,
+                S.current.block_example,
+                fontSize: 15,
+                marginTop: 20,
+                textAlign: TextAlign.center,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              MyTextView(
+                key: _description3Key,
+                S.current.block_example_desc,
+                fontSize: 15,
+                marginTop: 20,
+                textAlign: TextAlign.center,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              SizedBox(
+                height: state.remainingSpace, // 使用剩余空间的值
+              ),
+
+              MyButton(
+                key: _buttonKey,
+                onPressed: viewModel.submitUnit,
+                text: S.current.next,
+                textColor: Colors.white,
+                backgroundColor: context.appColors.btnBgDefault,
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                fontSize: 16,
+                minHeight: 50,
+                radius: 5,
+              ).marginOnly(top: 40, bottom: 30, left: 18, right: 18),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  /// 输入框
+  Widget _buildInputLayout(
+    BuildContext context,
+    SelectUnitState state,
+    String key, {
+    double marginTop = 0,
+    bool? showRightIcon = false, //是否展示右侧的布局
+    Widget? rightWidget, //右侧的布局
+    TextInputType textInputType = TextInputType.number,
+    String? errorText,
+    bool obscureText = false,
+    TextInputAction textInputAction = TextInputAction.done,
+    Function? onSubmit,
+  }) {
+    return IgnoreKeyboardDismiss(
+      child: MyTextField(
+        key,
+        fillBackgroundColor: context.appColors.authFiledBG,
+        state.formData[key]!['value'],
+        hintText: state.formData[key]!['hintText'],
+        hintStyle: TextStyle(
+          color: context.appColors.authFiledHint,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        controller: state.formData[key]!['controller'],
+        focusNode: state.formData[key]!['focusNode'],
+        margin: EdgeInsets.only(top: marginTop),
+        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
+        showDivider: false,
+        height: 44,
+        style: TextStyle(
+          color: context.appColors.authFiledText,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        inputType: textInputType,
+        textInputAction: textInputAction,
+        onSubmit: onSubmit,
+        cursorColor: context.appColors.authFiledText,
+        obscureText: obscureText,
+        errorText: errorText,
+        showLeftIcon: true,
+        showRightIcon: showRightIcon,
+        rightWidget: rightWidget,
+      ),
+    );
+  }
+}

+ 47 - 0
packages/cpt_auth/lib/modules/select_unit/select_unit_state.dart

@@ -0,0 +1,47 @@
+import 'package:flutter/material.dart';
+
+class SelectUnitState {
+  //表单的校验与数据
+  final Map<String, Map<String, dynamic>> formData;
+
+  double remainingSpace;
+
+  // ===================================  Begin  ↓  ===================================
+
+  SelectUnitState({
+    this.remainingSpace = 0,
+    Map<String, Map<String, dynamic>>? formData,
+  }) : formData = formData ??
+            {
+              'block': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': '',
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'unit': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': '',
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'room': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': '',
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+            };
+
+  SelectUnitState copyWith({
+    double? remainingSpace,
+  }) {
+    return SelectUnitState(
+      remainingSpace: remainingSpace ?? this.remainingSpace,
+    );
+  }
+
+}

+ 24 - 0
packages/cpt_auth/lib/modules/select_unit/select_unit_view_model.dart

@@ -0,0 +1,24 @@
+import 'package:cpt_auth/modules/select_role/select_role_page.dart';
+import 'package:cpt_auth/modules/select_unit/select_unit_state.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+part 'select_unit_view_model.g.dart';
+
+@riverpod
+class SelectUnitViewModel extends _$SelectUnitViewModel {
+
+  @override
+  SelectUnitState build() {
+    return SelectUnitState();
+  }
+
+  /// 提交选择的街道与单元
+  void submitUnit() {
+    SelectRolePage.startInstance();
+  }
+
+  void setRemainingSpace(double remainingSpace) {
+    state = state.copyWith(remainingSpace: remainingSpace);
+  }
+
+}

+ 27 - 0
packages/cpt_auth/lib/modules/select_unit/select_unit_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'select_unit_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$selectUnitViewModelHash() =>
+    r'f31ffde34c823cf1b3ab7477ddcfda312ccce307';
+
+/// See also [SelectUnitViewModel].
+@ProviderFor(SelectUnitViewModel)
+final selectUnitViewModelProvider =
+    AutoDisposeNotifierProvider<SelectUnitViewModel, SelectUnitState>.internal(
+  SelectUnitViewModel.new,
+  name: r'selectUnitViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$selectUnitViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$SelectUnitViewModel = AutoDisposeNotifier<SelectUnitState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 304 - 0
packages/cpt_auth/lib/modules/sign_up/sign_up_page.dart

@@ -0,0 +1,304 @@
+
+import 'package:cpt_auth/modules/sign_up/sign_up_state.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_field.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../router/page/auth_page_router.dart';
+import 'sign_up_view_model.dart';
+
+@RoutePage()
+class SignUpPage extends HookConsumerWidget {
+  const SignUpPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(const SignUpPageRoute());
+    } else {
+      appRouter.push(const SignUpPageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.read(signUpViewModelProvider.notifier);
+    final state = ref.watch(signUpViewModelProvider);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, ""),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: SingleChildScrollView(
+        scrollDirection: Axis.vertical,
+        physics: const BouncingScrollPhysics(),
+        child: Container(
+          margin: const EdgeInsets.symmetric(horizontal: 38),
+          width: double.infinity,
+          child: Column(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              //顶部图片
+              const MyAssetImage(
+                Assets.authSignUpInputImg,
+                width: 260,
+                height: 170,
+              ).marginOnly(top: 22, bottom: 34),
+
+              // 表单 - 名
+              _buildInputLayout(
+                context,
+                state,
+                "first_name",
+                textInputType: TextInputType.text,
+                textInputAction: TextInputAction.next,
+                errorText: state.firstNameErrorText,
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  FocusScope.of(context).requestFocus(state.formData['last_name']!['focusNode']);
+                },
+              ),
+
+              // 表单 - 姓
+              _buildInputLayout(
+                context,
+                state,
+                "last_name",
+                marginTop: 15,
+                textInputType: TextInputType.text,
+                textInputAction: TextInputAction.next,
+                errorText: state.lastNameErrorText,
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  FocusScope.of(context).requestFocus(state.formData['email']!['focusNode']);
+                },
+              ),
+
+              // 表单 - 邮箱
+              _buildInputLayout(
+                context,
+                state,
+                "email",
+                marginTop: 15,
+                textInputType: TextInputType.emailAddress,
+                textInputAction: TextInputAction.next,
+                errorText: state.emailErrorText,
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  FocusScope.of(context).requestFocus(state.formData['phone']!['focusNode']);
+                },
+              ),
+
+              // 表单 - 电话
+              Row(
+                mainAxisSize: MainAxisSize.max,
+                crossAxisAlignment: CrossAxisAlignment.center,
+                children: [
+                  const MyAssetImage(
+                    Assets.authCountrySg,
+                    width: 45,
+                    height: 30,
+                  ),
+                  MyTextView(
+                    "+65",
+                    textColor: context.appColors.textBlack,
+                    fontSize: 18.5,
+                    marginLeft: 15,
+                    marginRight: 12,
+                    isFontMedium: true,
+                  ),
+                  //电话输入框
+                  _buildInputLayout(
+                    context,
+                    state,
+                    "phone",
+                    marginTop: 15,
+                    textInputType: TextInputType.number,
+                    textInputAction: TextInputAction.next,
+                    errorText: state.phoneErrorText,
+                    onSubmit: (formKey, value) {
+                      state.formData[formKey]!['focusNode'].unfocus();
+                      FocusScope.of(context).requestFocus(state.formData['password']!['focusNode']);
+                    },
+                  ).expanded(),
+                ],
+              ),
+
+              // 表单 - 重置密码
+              _buildInputLayout(
+                context,
+                state,
+                "password",
+                marginTop: 15,
+                obscureText: !state.pwdVisibility,
+                errorText: state.passwordErrorText,
+                textInputAction: TextInputAction.next,
+                showRightIcon: true,
+                rightWidget: IconButton(
+                  highlightColor: Colors.transparent,
+                  splashColor: Colors.transparent,
+                  icon: state.pwdVisibility
+                      ? const MyAssetImage(
+                    Assets.authPasswordHide,
+                    width: 22.5,
+                    height: 16.5,
+                  )
+                      : const MyAssetImage(
+                    Assets.authPasswordShow,
+                    width: 22.5,
+                    height: 16.5,
+                  ),
+                  onPressed: () {
+                    viewModel.switchPwdVisibility();
+                  },
+                ),
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  FocusScope.of(context).requestFocus(state.formData['confirm_password']!['focusNode']);
+                },
+              ),
+
+              // 表单 - 确认密码
+              _buildInputLayout(
+                context,
+                state,
+                "confirm_password",
+                marginTop: 15,
+                obscureText: !state.confirmPwdVisibility,
+                errorText: state.confirmPasswordErrorText,
+                showRightIcon: true,
+                rightWidget: IconButton(
+                  highlightColor: Colors.transparent,
+                  splashColor: Colors.transparent,
+                  icon: state.confirmPwdVisibility
+                      ? const MyAssetImage(
+                    Assets.authPasswordHide,
+                    width: 22.5,
+                    height: 16.5,
+                  )
+                      : const MyAssetImage(
+                    Assets.authPasswordShow,
+                    width: 22.5,
+                    height: 16.5,
+                  ),
+                  onPressed: () {
+                    viewModel.switchConfirmPwdVisibility();
+                  },
+                ),
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  viewModel.submitSignUp();
+                },
+              ),
+
+              MyButton(
+                onPressed: viewModel.submitSignUp,
+                text: S.current.next,
+                textColor: Colors.white,
+                backgroundColor: context.appColors.btnBgDefault,
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                fontSize: 16,
+                minHeight: 50,
+                radius: 5,
+              ).marginOnly(top: 25, bottom: 25),
+
+              //同意协议
+              Row(
+                mainAxisSize: MainAxisSize.min,
+                children: [
+                  MyAssetImage(
+                    state.isAgreeTerms ? Assets.baseServiceCheckBoxChecked : Assets.baseServiceCheckBoxUncheck,
+                    width: 15.5,
+                    height: 15.5,
+                  ).onTap(viewModel.switchAgreeTerms, padding: 10),
+                  RichText(
+                    text: TextSpan(
+                      children: [
+                        TextSpan(
+                          text: S.current.agree_to,
+                          style: TextStyle(color: context.appColors.textDarkGray, fontWeight: FontWeight.w500, fontSize: 15), // 灰色文本
+                        ),
+                        const TextSpan(
+                          text: " ",
+                        ),
+                        TextSpan(
+                          text: S.current.terms_of_service,
+                          style: TextStyle(color: context.appColors.textPrimary, fontWeight: FontWeight.w500, fontSize: 15), // 蓝色文本
+                          recognizer: TapGestureRecognizer()..onTap = viewModel.gotoTermsPage,
+                        ),
+                      ],
+                    ),
+                  ),
+                ],
+              ).marginOnly(bottom: 35),
+
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  /// 输入框
+  Widget _buildInputLayout(
+      BuildContext context,
+      SignUpState state,
+      String key, {
+        double marginTop = 0,
+        bool? showRightIcon = false, //是否展示右侧的布局
+        Widget? rightWidget, //右侧的布局
+        TextInputType textInputType = TextInputType.text,
+        String? errorText,
+        bool obscureText = false,
+        TextInputAction textInputAction = TextInputAction.done,
+        Function? onSubmit,
+      }) {
+    return IgnoreKeyboardDismiss(
+      child: MyTextField(
+        key,
+        fillBackgroundColor: context.appColors.authFiledBG,
+        state.formData[key]!['value'],
+        hintText: state.formData[key]!['hintText'],
+        hintStyle: TextStyle(
+          color: context.appColors.authFiledHint,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        controller: state.formData[key]!['controller'],
+        focusNode: state.formData[key]!['focusNode'],
+        margin: EdgeInsets.only(top: marginTop),
+        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
+        showDivider: false,
+        height: 44,
+        style: TextStyle(
+          color: context.appColors.authFiledText,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        inputType: textInputType,
+        textInputAction: textInputAction,
+        onSubmit: onSubmit,
+        cursorColor: context.appColors.authFiledText,
+        obscureText: obscureText,
+        errorText: errorText,
+        showLeftIcon: true,
+        showRightIcon: showRightIcon,
+        rightWidget: rightWidget,
+      ),
+    );
+  }
+
+}

+ 107 - 0
packages/cpt_auth/lib/modules/sign_up/sign_up_state.dart

@@ -0,0 +1,107 @@
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:flutter/material.dart';
+
+class SignUpState {
+  //表单的校验与数据
+  final Map<String, Map<String, dynamic>> formData;
+
+  //表单的错误信息展示
+  String? firstNameErrorText;
+  String? lastNameErrorText;
+  String? emailErrorText;
+  String? phoneErrorText;
+  String? passwordErrorText;
+  String? confirmPasswordErrorText;
+
+  //是否明文展示密码
+  bool pwdVisibility;
+
+  bool confirmPwdVisibility;
+
+  bool isAgreeTerms;
+
+  // ===================================  Begin  ↓  ===================================
+
+  SignUpState({
+    Map<String, Map<String, dynamic>>? formData,
+    this.pwdVisibility = false,
+    this.confirmPwdVisibility = false,
+    this.isAgreeTerms = false,
+    this.firstNameErrorText,
+    this.lastNameErrorText,
+    this.emailErrorText,
+    this.phoneErrorText,
+    this.passwordErrorText,
+    this.confirmPasswordErrorText,
+  }) : formData = formData ??
+            {
+              'first_name': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.first_name,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'last_name': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.last_name,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'email': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.email,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'phone': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.mobile_phone,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'password': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.password,
+                'focusNode': FocusNode(),
+                'obsecure': true,
+              },
+              'confirm_password': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.confirm_password,
+                'focusNode': FocusNode(),
+                'obsecure': true,
+              },
+            };
+
+  SignUpState copyWith({
+    String? firstNameErrorText,
+    String? lastNameErrorText,
+    String? emailErrorText,
+    String? phoneErrorText,
+    String? passwordErrorText,
+    String? confirmPasswordErrorText,
+    bool? pwdVisibility,
+    bool? confirmPwdVisibility,
+    bool? isAgreeTerms,
+  }) {
+    return SignUpState(
+      formData: this.formData,
+      isAgreeTerms: isAgreeTerms ?? this.isAgreeTerms,
+      pwdVisibility: pwdVisibility ?? this.pwdVisibility,
+      confirmPwdVisibility: confirmPwdVisibility ?? this.confirmPwdVisibility,
+      firstNameErrorText: firstNameErrorText,
+      lastNameErrorText: lastNameErrorText,
+      emailErrorText: emailErrorText,
+      phoneErrorText: phoneErrorText,
+      passwordErrorText: passwordErrorText,
+      confirmPasswordErrorText: confirmPasswordErrorText,
+    );
+  }
+
+}

+ 198 - 0
packages/cpt_auth/lib/modules/sign_up/sign_up_view_model.dart

@@ -0,0 +1,198 @@
+import 'package:cpt_auth/modules/sing_up_verify/sign_up_verify_page.dart';
+import 'package:flutter/material.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/util.dart';
+
+import 'sign_up_state.dart';
+
+part 'sign_up_view_model.g.dart';
+
+@riverpod
+class SignUpViewModel extends _$SignUpViewModel {
+  @override
+  SignUpState build() {
+    final state = SignUpState();
+    initListener(state);
+    ref.onDispose(() {
+      onDispose(state);
+    });
+    return state;
+  }
+
+  //提交注册表单
+  void submitSignUp() {
+    state = state.copyWith(
+      firstNameErrorText: null,
+      lastNameErrorText: null,
+      emailErrorText: null,
+      phoneErrorText: null,
+      passwordErrorText: null,
+      confirmPasswordErrorText: null,
+    );
+
+    final FocusNode firstNameFocusNode = state.formData['first_name']!['focusNode'];
+    final FocusNode lastNameFocusNode = state.formData['last_name']!['focusNode'];
+    final FocusNode emailFocusNode = state.formData['email']!['focusNode'];
+    final FocusNode phoneFocusNode = state.formData['phone']!['focusNode'];
+    final FocusNode passwordFocusNode = state.formData['password']!['focusNode'];
+    final FocusNode confirmPasswordFocusNode = state.formData['confirm_password']!['focusNode'];
+
+    firstNameFocusNode.unfocus();
+    lastNameFocusNode.unfocus();
+    emailFocusNode.unfocus();
+    phoneFocusNode.unfocus();
+    passwordFocusNode.unfocus();
+    confirmPasswordFocusNode.unfocus();
+
+    final TextEditingController firstNameController = state.formData['first_name']!['controller'];
+    final TextEditingController lastNameController = state.formData['last_name']!['controller'];
+    final TextEditingController emailController = state.formData['email']!['controller'];
+    final TextEditingController phoneController = state.formData['phone']!['controller'];
+    final TextEditingController passwordController = state.formData['password']!['controller'];
+    final TextEditingController confirmPasswordController = state.formData['confirm_password']!['controller'];
+
+    final firstName = firstNameController.text;
+    final lastName = lastNameController.text;
+    final email = emailController.text;
+    final phone = phoneController.text;
+    final password = passwordController.text;
+    final confirmPassword = confirmPasswordController.text;
+
+    Log.d('当前待提交的 firstName:$firstName lastName:$lastName email:$email phone:$phone password:$password confirmPassword:$confirmPassword');
+
+    if (Utils.isEmpty(firstName)) {
+      state = state.copyWith(firstNameErrorText: "First Name cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(lastName)) {
+      state = state.copyWith(lastNameErrorText: "Last Name cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(email)) {
+      state = state.copyWith(emailErrorText: "Email cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(phone)) {
+      state = state.copyWith(phoneErrorText: "Email cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(password)) {
+      state = state.copyWith(passwordErrorText: "Password cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(confirmPassword)) {
+      state = state.copyWith(confirmPasswordErrorText: "Confirm Password cannot be empty!");
+      return;
+    }
+
+    if (confirmPassword != password) {
+      state = state.copyWith(confirmPasswordErrorText: "Password mismatch, please check password");
+      return;
+    }
+
+    //执行密码登录
+    ToastEngine.show('准备执行请求发送验证码 firstName:$firstName lastName:$lastName email:$email phone:$phone password:$password confirmPassword:$confirmPassword');
+
+    gotoSignUpVerifyPage();
+  }
+
+  //去注册的短信校验页面
+  void gotoSignUpVerifyPage() {
+    SignUpVerifyPage.startInstance();
+  }
+
+  //切换隐藏显示密码
+  void switchPwdVisibility() {
+    state = state.copyWith(pwdVisibility: !state.pwdVisibility);
+  }
+
+  void switchConfirmPwdVisibility() {
+    state = state.copyWith(confirmPwdVisibility: !state.confirmPwdVisibility);
+  }
+
+  //切换同意按钮
+  void switchAgreeTerms() {
+    state = state.copyWith(isAgreeTerms: !state.isAgreeTerms);
+  }
+
+  void gotoTermsPage() {
+    ToastEngine.show("去协议页面");
+  }
+
+  //初始化监听
+  void initListener(SignUpState initState) {
+    final FocusNode firstNameFocusNode = initState.formData['first_name']!['focusNode'];
+    final FocusNode lastNameFocusNode = initState.formData['last_name']!['focusNode'];
+    final FocusNode emailFocusNode = initState.formData['email']!['focusNode'];
+    final FocusNode phoneFocusNode = initState.formData['phone']!['focusNode'];
+    final FocusNode passwordFocusNode = initState.formData['password']!['focusNode'];
+    final FocusNode confirmPasswordFocusNode = initState.formData['confirm_password']!['focusNode'];
+
+    firstNameFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (firstNameFocusNode.hasFocus) {
+        state = state.copyWith(firstNameErrorText: null);
+      }
+    });
+
+    lastNameFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (lastNameFocusNode.hasFocus) {
+        state = state.copyWith(lastNameErrorText: null);
+      }
+    });
+
+    emailFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (emailFocusNode.hasFocus) {
+        state = state.copyWith(emailErrorText: null);
+      }
+    });
+
+    phoneFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (phoneFocusNode.hasFocus) {
+        state = state.copyWith(phoneErrorText: null);
+      }
+    });
+
+    passwordFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (passwordFocusNode.hasFocus) {
+        state = state.copyWith(passwordErrorText: null);
+      }
+    });
+
+    confirmPasswordFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (confirmPasswordFocusNode.hasFocus) {
+        state = state.copyWith(confirmPasswordErrorText: null);
+      }
+    });
+  }
+
+  //销毁资源
+  void onDispose(SignUpState initState) {
+    final FocusNode firstNameFocusNode = initState.formData['first_name']!['focusNode'];
+    final FocusNode lastNameFocusNode = initState.formData['last_name']!['focusNode'];
+    final FocusNode emailFocusNode = initState.formData['email']!['focusNode'];
+    final FocusNode phoneFocusNode = initState.formData['phone']!['focusNode'];
+    final FocusNode passwordFocusNode = initState.formData['password']!['focusNode'];
+    final FocusNode confirmPasswordFocusNode = initState.formData['confirm_password']!['focusNode'];
+    firstNameFocusNode.dispose();
+    lastNameFocusNode.dispose();
+    emailFocusNode.dispose();
+    phoneFocusNode.dispose();
+    passwordFocusNode.dispose();
+    confirmPasswordFocusNode.dispose();
+
+    Log.d("SignUpViewModel 销毁 onDispose");
+  }
+}

+ 10 - 10
packages/cpt_property/lib/modules/property/vm/property_view_model.g.dart

@@ -1,26 +1,26 @@
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
-part of 'property_view_model.dart';
+part of 'sign_up_view_model.dart';
 
 // **************************************************************************
 // RiverpodGenerator
 // **************************************************************************
 
-String _$propertyViewModelHash() => r'5b26fb8c848e6e1bc2527fd4ec1799929f095866';
+String _$signUpViewModelHash() => r'9953c49f83eace3fa24d85f5bc8cf95212175ff9';
 
-/// See also [PropertyViewModel].
-@ProviderFor(PropertyViewModel)
-final propertyViewModelProvider =
-    AutoDisposeNotifierProvider<PropertyViewModel, void>.internal(
-  PropertyViewModel.new,
-  name: r'propertyViewModelProvider',
+/// See also [SignUpViewModel].
+@ProviderFor(SignUpViewModel)
+final signUpViewModelProvider =
+    AutoDisposeNotifierProvider<SignUpViewModel, SignUpState>.internal(
+  SignUpViewModel.new,
+  name: r'signUpViewModelProvider',
   debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
       ? null
-      : _$propertyViewModelHash,
+      : _$signUpViewModelHash,
   dependencies: null,
   allTransitiveDependencies: null,
 );
 
-typedef _$PropertyViewModel = AutoDisposeNotifier<void>;
+typedef _$SignUpViewModel = AutoDisposeNotifier<SignUpState>;
 // ignore_for_file: type=lint
 // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 92 - 0
packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_full_screen.dart

@@ -0,0 +1,92 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+import '../../router/page/auth_page_router.dart';
+import 'sign_up_success_view_model.dart';
+
+//全面屏手机
+class SignUpSuccessFullScreen extends HookConsumerWidget {
+  const SignUpSuccessFullScreen({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.watch(signUpSuccessViewModelProvider.notifier);
+
+    return LayoutBuilder(
+      builder: (context, constraints) {
+        return Container(
+          padding: const EdgeInsets.symmetric(horizontal: 20),
+          width: double.infinity,
+          height: constraints.maxHeight, // 占满父容器的高度
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              //顶部图片
+              const MyAssetImage(
+                Assets.authYyHomeSuccess,
+                width: 264,
+                height: 180,
+              ).alignCenter().marginOnly(top: 12),
+
+              MyTextView(
+                S.current.welcome_name("Hu yu"),
+                fontSize: 23,
+                marginTop: 30,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              MyTextView(
+                S.current.sign_up_success_txt1,
+                fontSize: 15,
+                marginTop: 25,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              MyTextView(
+                S.current.sign_up_success_txt2,
+                fontSize: 15,
+                marginTop: 20,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              MyTextView(
+                S.current.sign_up_success_txt3,
+                fontSize: 15,
+                marginTop: 20,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              const Spacer(),
+
+              MyButton(
+                onPressed: viewModel.gotoSelectEstatePage,
+                text: S.current.get_started,
+                textColor: Colors.white,
+                backgroundColor: context.appColors.btnBgDefault,
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                fontSize: 16,
+                minHeight: 50,
+                radius: 5,
+              ).marginOnly(top: 50, bottom: 50, left: 18, right: 18),
+            ],
+          ),
+        );
+      },
+    );
+  }
+}

+ 38 - 0
packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_page.dart

@@ -0,0 +1,38 @@
+import 'package:cpt_auth/modules/sing_up_success/sign_up_success_full_screen.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/responsive_widget.dart';
+import '../../router/page/auth_page_router.dart';
+import 'sign_up_success_small_screen.dart';
+
+@RoutePage()
+class SignUpSuccessPage extends HookConsumerWidget {
+  const SignUpSuccessPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.popAndPush(const SignUpSuccessPageRoute());
+    } else {
+      appRouter.popAndPush(const SignUpSuccessPageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, ""),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: const ResponsiveWidget(
+        smallScreen: SignUpSuccessSmallScreen(),
+        smallFullScreen: SignUpSuccessFullScreen(),
+      ),
+    );
+  }
+}

+ 90 - 0
packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_small_screen.dart

@@ -0,0 +1,90 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+import '../../router/page/auth_page_router.dart';
+import 'sign_up_success_view_model.dart';
+
+//小屏手机
+class SignUpSuccessSmallScreen extends HookConsumerWidget {
+  const SignUpSuccessSmallScreen({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.watch(signUpSuccessViewModelProvider.notifier);
+
+    return SingleChildScrollView(
+      scrollDirection: Axis.vertical,
+      physics: const BouncingScrollPhysics(),
+      child: Container(
+        padding: const EdgeInsets.symmetric(horizontal: 20),
+        width: double.infinity,
+        child: Column(
+          mainAxisSize: MainAxisSize.max,
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            //顶部图片
+            const MyAssetImage(
+              Assets.authYyHomeSuccess,
+              width: 264,
+              height: 180,
+            ).alignCenter().marginOnly(top: 12),
+
+            MyTextView(
+              S.current.welcome_name("Huyu"),
+              fontSize: 23,
+              marginTop: 30,
+              isFontMedium: true,
+              textColor: context.appColors.textBlack,
+            ),
+
+            MyTextView(
+              S.current.sign_up_success_txt1,
+              fontSize: 15,
+              marginTop: 25,
+              isFontMedium: true,
+              textColor: context.appColors.textBlack,
+            ),
+
+            MyTextView(
+              S.current.sign_up_success_txt2,
+              fontSize: 15,
+              marginTop: 20,
+              isFontMedium: true,
+              textColor: context.appColors.textBlack,
+            ),
+
+            MyTextView(
+              S.current.sign_up_success_txt3,
+              fontSize: 15,
+              marginTop: 20,
+              isFontMedium: true,
+              textColor: context.appColors.textBlack,
+            ),
+
+            MyButton(
+              onPressed: viewModel.gotoSelectEstatePage,
+              text: S.current.get_started,
+              textColor: Colors.white,
+              backgroundColor: context.appColors.btnBgDefault,
+              fontWeight: FontWeight.w500,
+              type: ClickType.throttle,
+              fontSize: 16,
+              minHeight: 50,
+              radius: 5,
+            ).marginOnly(top: 50, bottom: 50, left: 18, right: 18),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 15 - 0
packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_view_model.dart

@@ -0,0 +1,15 @@
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+part 'sign_up_success_view_model.g.dart';
+
+@riverpod
+class SignUpSuccessViewModel extends _$SignUpSuccessViewModel {
+  @override
+  void build() {}
+
+  //去选择社区的页面
+  void gotoSelectEstatePage() {
+    ToastEngine.show("去选择社区的页面");
+  }
+}

+ 27 - 0
packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'sign_up_success_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$signUpSuccessViewModelHash() =>
+    r'715f2ae03093839694f7336d2cf8f0b57ce65d95';
+
+/// See also [SignUpSuccessViewModel].
+@ProviderFor(SignUpSuccessViewModel)
+final signUpSuccessViewModelProvider =
+    AutoDisposeNotifierProvider<SignUpSuccessViewModel, void>.internal(
+  SignUpSuccessViewModel.new,
+  name: r'signUpSuccessViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$signUpSuccessViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$SignUpSuccessViewModel = AutoDisposeNotifier<void>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 169 - 0
packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_page.dart

@@ -0,0 +1,169 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:pinput/pinput.dart';
+import '../../router/page/auth_page_router.dart';
+import 'sign_up_verify_view_model.dart';
+
+@RoutePage()
+class SignUpVerifyPage extends HookConsumerWidget {
+  const SignUpVerifyPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.popAndPush(const SignUpVerifyPageRoute());
+    } else {
+      appRouter.popAndPush(const SignUpVerifyPageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.watch(signUpVerifyViewModelProvider.notifier);
+    final state = ref.watch(signUpVerifyViewModelProvider);
+
+    const length = 4;
+    final borderColor = context.appColors.textPrimary;
+    const errorColor = Colors.redAccent;
+    final fillColor = context.appColors.authFiledBG;
+    final defaultPinTheme = PinTheme(
+      width: 50,
+      height: 50,
+      textStyle: TextStyle(color: context.appColors.textPrimary, fontWeight: FontWeight.w500, fontSize: 15),
+      decoration: BoxDecoration(
+        color: fillColor,
+        borderRadius: BorderRadius.circular(5),
+        border: Border.all(color: Colors.transparent),
+      ),
+    );
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, ""),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: SingleChildScrollView(
+        scrollDirection: Axis.vertical,
+        physics: const BouncingScrollPhysics(),
+        child: Container(
+          margin: const EdgeInsets.symmetric(horizontal: 38),
+          width: double.infinity,
+          child: Column(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              //顶部图片
+              const MyAssetImage(
+                Assets.authSmsVerifyImg,
+                width: 260,
+                height: 180,
+              ).marginOnly(top: 12, bottom: 24),
+
+              MyTextView(
+                S.current.sign_up_verify_txt,
+                fontSize: 15,
+                marginBottom: 24,
+                textAlign: TextAlign.center,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              SizedBox(
+                height: 50,
+                child: Pinput(
+                  length: length,
+                  controller: state.controller,
+                  focusNode: state.focusNode,
+                  defaultPinTheme: defaultPinTheme,
+                  onCompleted: (pin) {
+                    ToastEngine.show("输入的验证码为:$pin");
+                  },
+                  focusedPinTheme: defaultPinTheme.copyWith(
+                    height: 50,
+                    width: 50,
+                    decoration: defaultPinTheme.decoration!.copyWith(
+                      border: Border.all(color: borderColor),
+                    ),
+                  ),
+                  errorPinTheme: defaultPinTheme.copyWith(
+                    decoration: BoxDecoration(
+                      color: errorColor,
+                      borderRadius: BorderRadius.circular(5),
+                    ),
+                  ),
+                ),
+              ),
+
+              MyTextView(
+                S.current.did_not_receive,
+                fontSize: 15,
+                marginTop: 24,
+                marginBottom: 16,
+                textAlign: TextAlign.center,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              MyButton(
+                onPressed: viewModel.verifySignUpInput,
+                text: S.current.resend_code,
+                textColor: Colors.white,
+                backgroundColor: context.appColors.orangeBG,
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                fontSize: 16,
+                minHeight: 42,
+                radius: 5,
+              ).constrained(width: 140),
+
+              Row(
+                mainAxisSize: MainAxisSize.min,
+                children: [
+                  RichText(
+                    text: TextSpan(
+                      children: [
+                        TextSpan(
+                          text: S.current.you_have,
+                          style: TextStyle(color: context.appColors.textBlack, fontWeight: FontWeight.w500, fontSize: 15),
+                        ),
+                        TextSpan(
+                          text: " 2 ",
+                          style: TextStyle(color: context.appColors.textPrimary, fontWeight: FontWeight.w500, fontSize: 15),
+                        ),
+                        TextSpan(
+                          text: S.current.tries_left,
+                          style: TextStyle(color: context.appColors.textBlack, fontWeight: FontWeight.w500, fontSize: 15),
+                        ),
+                      ],
+                    ),
+                  ),
+                ],
+              ).marginOnly(top: 16),
+
+              MyButton(
+                onPressed: viewModel.verifySignUpInput,
+                text: S.current.next,
+                textColor: Colors.white,
+                backgroundColor: context.appColors.btnBgDefault,
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                fontSize: 16,
+                minHeight: 50,
+                radius: 5,
+              ).marginOnly(top: 50, bottom: 50),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+}

+ 8 - 0
packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_state.dart

@@ -0,0 +1,8 @@
+import 'package:flutter/material.dart';
+
+class SignUpVerifyState{
+
+  final controller = TextEditingController();
+  final focusNode = FocusNode();
+
+}

+ 31 - 0
packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_view_model.dart

@@ -0,0 +1,31 @@
+import 'package:cpt_auth/modules/sing_up_success/sign_up_success_page.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:shared/utils/log_utils.dart';
+
+import 'sign_up_verify_state.dart';
+
+part 'sign_up_verify_view_model.g.dart';
+
+@riverpod
+class SignUpVerifyViewModel extends _$SignUpVerifyViewModel {
+  @override
+  SignUpVerifyState build() {
+    final state = SignUpVerifyState();
+    ref.onDispose(() {
+      onDispose(state);
+    });
+    return state;
+  }
+
+  //校验SMS短信验证码
+  void verifySignUpInput() {
+    SignUpSuccessPage.startInstance();
+  }
+
+  //销毁资源
+  void onDispose(SignUpVerifyState initState) {
+
+    Log.d("SignUpVerifyViewModel 销毁 onDispose");
+  }
+
+}

+ 27 - 0
packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'sign_up_verify_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$signUpVerifyViewModelHash() =>
+    r'3ed4563220e5e1ae25ff7650c8d005e496e68334';
+
+/// See also [SignUpVerifyViewModel].
+@ProviderFor(SignUpVerifyViewModel)
+final signUpVerifyViewModelProvider = AutoDisposeNotifierProvider<
+    SignUpVerifyViewModel, SignUpVerifyState>.internal(
+  SignUpVerifyViewModel.new,
+  name: r'signUpVerifyViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$signUpVerifyViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$SignUpVerifyViewModel = AutoDisposeNotifier<SignUpVerifyState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 160 - 0
packages/cpt_auth/lib/modules/tenant_doc/tenant_doc_page.dart

@@ -0,0 +1,160 @@
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:plugin_platform/platform_export.dart';
+
+import '../../router/page/auth_page_router.dart';
+import 'tenant_doc_view_model.dart';
+
+@RoutePage()
+class TenantDocPage extends HookConsumerWidget {
+  TenantDocPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(TenantDocPageRoute());
+    } else {
+      appRouter.push(TenantDocPageRoute());
+    }
+  }
+
+  // 为需要测量的控件创建 GlobalKey
+  final GlobalKey _appbarKey = GlobalKey();
+  final GlobalKey _titleKey = GlobalKey();
+  final GlobalKey _descriptionKey = GlobalKey();
+  final GlobalKey _description1Key = GlobalKey();
+  final GlobalKey _description2Key = GlobalKey();
+  final GlobalKey _nineGridKey = GlobalKey();
+  final GlobalKey _buttonKey = GlobalKey();
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.read(tenantDocViewModelProvider.notifier);
+    final state = ref.watch(tenantDocViewModelProvider);
+
+    // 获取屏幕高度
+    final screenHeight = MediaQuery.of(context).size.height;
+    final statusBarHeight = MediaQuery.of(context).padding.top;
+    final navigationBarHeight = MediaQuery.of(context).padding.bottom;
+
+    useEffect(() {
+      double usedHeight = 0;
+      // 组件挂载时执行,获取控件高度
+      WidgetsBinding.instance.addPostFrameCallback((_) {
+        // 获取各个控件的高度
+        usedHeight += _appbarKey.currentContext?.size?.height ?? 0;
+        usedHeight += _titleKey.currentContext?.size?.height ?? 0;
+        usedHeight += _descriptionKey.currentContext?.size?.height ?? 0;
+        usedHeight += _description1Key.currentContext?.size?.height ?? 0;
+        usedHeight += _description2Key.currentContext?.size?.height ?? 0;
+        usedHeight += _nineGridKey.currentContext?.size?.height ?? 0;
+        usedHeight += _buttonKey.currentContext?.size?.height ?? 0;
+
+        // 计算剩余空间
+        double remainingSpace = screenHeight - statusBarHeight - navigationBarHeight - usedHeight - 23 - 21;
+
+        Log.d("计算剩余空间:$remainingSpace");
+
+        if (remainingSpace > 0) {
+          // 设置一个状态来存储剩余空间的高度
+          viewModel.setRemainingSpace(remainingSpace);
+        }
+      });
+      return () {
+        // 组件卸载时执行
+      };
+    }, []);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, "", key: _appbarKey),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: Container(
+        padding: const EdgeInsets.symmetric(horizontal: 15),
+        width: double.infinity,
+        child: Column(
+          mainAxisSize: MainAxisSize.max,
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            MyTextView(
+              key: _titleKey,
+              S.current.upload_documents,
+              fontSize: 23.5,
+              marginTop: 23,
+              marginBottom: 21,
+              textAlign: TextAlign.center,
+              isFontMedium: true,
+              textColor: context.appColors.textBlack,
+            ),
+
+            MyTextView(
+              key: _descriptionKey,
+              S.current.upload_doc_desc,
+              fontSize: 15,
+              isFontMedium: true,
+              textColor: context.appColors.textBlack,
+            ),
+
+            MyTextView(
+              key: _description1Key,
+              S.current.upload_doc_desc1,
+              fontSize: 15,
+              marginTop: 22,
+              isFontMedium: true,
+              textColor: context.appColors.textBlack,
+            ),
+
+            MyTextView(
+              key: _description2Key,
+              S.current.upload_doc_desc2,
+              fontSize: 15,
+              marginTop: 22,
+              marginBottom: 24,
+              isFontMedium: true,
+              textColor: context.appColors.textBlack,
+            ),
+
+            ImageNineGrid(
+              key: _nineGridKey,
+              isSelectEnable: true,
+              maxImages: 3,
+              spacing: 10,
+              aspectRatio: 108 / 80,
+              initialImages: [],
+              onImagesChanged: (list) {
+                viewModel.setDocList(list);
+              },
+            ),
+
+            SizedBox(
+              height: state.remainingSpace, // 使用剩余空间的值
+            ),
+
+            //底部的按钮
+            MyButton(
+              key: _buttonKey,
+              onPressed: viewModel.submitDoc,
+              text: S.current.next,
+              textColor: Colors.white,
+              backgroundColor: context.appColors.btnBgDefault,
+              fontWeight: FontWeight.w500,
+              type: ClickType.throttle,
+              fontSize: 16,
+              minHeight: 50,
+              radius: 5,
+            ).marginOnly(top: 30, bottom: 30, left: 18, right: 18),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 22 - 0
packages/cpt_auth/lib/modules/tenant_doc/tenant_doc_state.dart

@@ -0,0 +1,22 @@
+class TenantDocState {
+
+  final List<String> docList;
+
+  final double remainingSpace;
+
+  const TenantDocState({
+    required this.docList,
+    this.remainingSpace = 0,
+  });
+
+  TenantDocState copyWith({
+    List<String>? docList,
+    double? remainingSpace,
+  }) {
+    return TenantDocState(
+      docList: docList ?? this.docList,
+      remainingSpace: remainingSpace ?? this.remainingSpace,
+    );
+  }
+
+}

+ 32 - 0
packages/cpt_auth/lib/modules/tenant_doc/tenant_doc_view_model.dart

@@ -0,0 +1,32 @@
+
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+import 'tenant_doc_state.dart';
+
+part 'tenant_doc_view_model.g.dart';
+
+@riverpod
+class TenantDocViewModel extends _$TenantDocViewModel {
+
+  @override
+  TenantDocState build(){
+    return const TenantDocState(docList: []);
+  }
+
+  //设置已选中的文件数组
+  void setDocList(List<String> list){
+    state = state.copyWith(docList: list);
+  }
+
+  //提交文件
+  void submitDoc() {
+
+    ToastEngine.show("请求接口上传文件:${state.docList}");
+  }
+
+  void setRemainingSpace(double remainingSpace) {
+    state = state.copyWith(remainingSpace: remainingSpace);
+  }
+
+}

+ 27 - 0
packages/cpt_auth/lib/modules/tenant_doc/tenant_doc_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'tenant_doc_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$tenantDocViewModelHash() =>
+    r'8283e1970743d66a871e1145e15ffd3177d3a42b';
+
+/// See also [TenantDocViewModel].
+@ProviderFor(TenantDocViewModel)
+final tenantDocViewModelProvider =
+    AutoDisposeNotifierProvider<TenantDocViewModel, TenantDocState>.internal(
+  TenantDocViewModel.new,
+  name: r'tenantDocViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$tenantDocViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$TenantDocViewModel = AutoDisposeNotifier<TenantDocState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 3 - 6
packages/cpt_auth/lib/router/component/auth_component_service.dart

@@ -1,13 +1,12 @@
 /*
  * Auth 组件的组件路由
  */
-import 'package:flutter/cupertino.dart';
 import 'package:router/componentRouter/auth_service.dart';
-import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:riverpod_annotation/riverpod_annotation.dart';
 
-import '../../modules/auth_login/page/Profile_edit_page.dart';
+import '../../modules/auth_login/auth_login_page.dart';
+import '../../modules/select_estate/select_estate_page.dart';
 
 part 'auth_component_service.g.dart';
 
@@ -17,7 +16,6 @@ AuthService authService(Ref ref) {
 }
 
 class AuthComponentService extends AuthService {
-
   @override
   void startLoginPage() {
     AuthLoginPage.startInstance();
@@ -25,7 +23,6 @@ class AuthComponentService extends AuthService {
 
   @override
   void startResetPasswordPage() {
-
+    SelectEstatePage.startInstance();
   }
-
 }

+ 20 - 2
packages/cpt_auth/lib/router/page/auth_page_router.dart

@@ -3,7 +3,17 @@ import 'package:flutter/material.dart';
 import 'package:router/ext/auto_router_extensions.dart';
 import 'package:router/path/router_path.dart';
 
-import '../../modules/auth_login/page/Profile_edit_page.dart';
+import '../../modules/auth_login/auth_login_page.dart';
+import '../../modules/forgot_input/forgot_input_page.dart';
+import '../../modules/forgot_verify/forgot_verify_page.dart';
+import '../../modules/sign_up/sign_up_page.dart';
+import '../../modules/sing_up_verify/sign_up_verify_page.dart';
+import '../../modules/sing_up_success/sign_up_success_page.dart';
+import '../../modules/select_estate/select_estate_page.dart';
+import '../../modules/select_unit/select_unit_page.dart';
+import '../../modules/select_role/select_role_page.dart';
+import '../../modules/tenant_doc/tenant_doc_page.dart';
+
 
 
 part 'auth_page_router.gr.dart';
@@ -15,7 +25,15 @@ part 'auth_page_router.gr.dart';
 class AuthPageRouter extends _$AuthPageRouter {
   @override
   List<AutoRoute> get routes => [
-
     CustomRoute(page: AuthLoginPageRoute.page, path: RouterPath.authLogin, transitionsBuilder: applySlideTransition),
+    CustomRoute(page: ForgotInputPageRoute.page, path: RouterPath.authForgotInput, transitionsBuilder: applySlideTransition),
+    CustomRoute(page: ForgotVerifyPageRoute.page, path: RouterPath.authForgotVerify, transitionsBuilder: applySlideTransition),
+    CustomRoute(page: SignUpPageRoute.page, path: RouterPath.authSignUp, transitionsBuilder: applySlideTransition),
+    CustomRoute(page: SignUpVerifyPageRoute.page, path: RouterPath.authSignUpVerify, transitionsBuilder: applySlideTransition),
+    CustomRoute(page: SignUpSuccessPageRoute.page, path: RouterPath.authSignUpSuccess, transitionsBuilder: applySlideTransition),
+    CustomRoute(page: SelectEstatePageRoute.page, path: RouterPath.authSelectEstate, transitionsBuilder: applySlideTransition),
+    CustomRoute(page: SelectUnitPageRoute.page, path: RouterPath.authSelectUnit, transitionsBuilder: applySlideTransition),
+    CustomRoute(page: SelectRolePageRoute.page, path: RouterPath.authSelectRole, transitionsBuilder: applySlideTransition),
+    CustomRoute(page: TenantDocPageRoute.page, path: RouterPath.authTenantDoc, transitionsBuilder: applySlideTransition),
   ];
 }

+ 232 - 1
packages/cpt_auth/lib/router/page/auth_page_router.gr.dart

@@ -20,7 +20,67 @@ abstract class _$AuthPageRouter extends RootStackRouter {
         routeData: routeData,
         child: const AuthLoginPage(),
       );
-    }
+    },
+    ForgotInputPageRoute.name: (routeData) {
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: const ForgotInputPage(),
+      );
+    },
+    ForgotVerifyPageRoute.name: (routeData) {
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: const ForgotVerifyPage(),
+      );
+    },
+    SelectEstatePageRoute.name: (routeData) {
+      final args = routeData.argsAs<SelectEstatePageRouteArgs>(
+          orElse: () => const SelectEstatePageRouteArgs());
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: SelectEstatePage(key: args.key),
+      );
+    },
+    SelectRolePageRoute.name: (routeData) {
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: const SelectRolePage(),
+      );
+    },
+    SelectUnitPageRoute.name: (routeData) {
+      final args = routeData.argsAs<SelectUnitPageRouteArgs>(
+          orElse: () => const SelectUnitPageRouteArgs());
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: SelectUnitPage(key: args.key),
+      );
+    },
+    SignUpPageRoute.name: (routeData) {
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: const SignUpPage(),
+      );
+    },
+    SignUpSuccessPageRoute.name: (routeData) {
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: const SignUpSuccessPage(),
+      );
+    },
+    SignUpVerifyPageRoute.name: (routeData) {
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: const SignUpVerifyPage(),
+      );
+    },
+    TenantDocPageRoute.name: (routeData) {
+      final args = routeData.argsAs<TenantDocPageRouteArgs>(
+          orElse: () => const TenantDocPageRouteArgs());
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: TenantDocPage(key: args.key),
+      );
+    },
   };
 }
 
@@ -37,3 +97,174 @@ class AuthLoginPageRoute extends PageRouteInfo<void> {
 
   static const PageInfo<void> page = PageInfo<void>(name);
 }
+
+/// generated route for
+/// [ForgotInputPage]
+class ForgotInputPageRoute extends PageRouteInfo<void> {
+  const ForgotInputPageRoute({List<PageRouteInfo>? children})
+      : super(
+          ForgotInputPageRoute.name,
+          initialChildren: children,
+        );
+
+  static const String name = 'ForgotInputPageRoute';
+
+  static const PageInfo<void> page = PageInfo<void>(name);
+}
+
+/// generated route for
+/// [ForgotVerifyPage]
+class ForgotVerifyPageRoute extends PageRouteInfo<void> {
+  const ForgotVerifyPageRoute({List<PageRouteInfo>? children})
+      : super(
+          ForgotVerifyPageRoute.name,
+          initialChildren: children,
+        );
+
+  static const String name = 'ForgotVerifyPageRoute';
+
+  static const PageInfo<void> page = PageInfo<void>(name);
+}
+
+/// generated route for
+/// [SelectEstatePage]
+class SelectEstatePageRoute extends PageRouteInfo<SelectEstatePageRouteArgs> {
+  SelectEstatePageRoute({
+    Key? key,
+    List<PageRouteInfo>? children,
+  }) : super(
+          SelectEstatePageRoute.name,
+          args: SelectEstatePageRouteArgs(key: key),
+          initialChildren: children,
+        );
+
+  static const String name = 'SelectEstatePageRoute';
+
+  static const PageInfo<SelectEstatePageRouteArgs> page =
+      PageInfo<SelectEstatePageRouteArgs>(name);
+}
+
+class SelectEstatePageRouteArgs {
+  const SelectEstatePageRouteArgs({this.key});
+
+  final Key? key;
+
+  @override
+  String toString() {
+    return 'SelectEstatePageRouteArgs{key: $key}';
+  }
+}
+
+/// generated route for
+/// [SelectRolePage]
+class SelectRolePageRoute extends PageRouteInfo<void> {
+  const SelectRolePageRoute({List<PageRouteInfo>? children})
+      : super(
+          SelectRolePageRoute.name,
+          initialChildren: children,
+        );
+
+  static const String name = 'SelectRolePageRoute';
+
+  static const PageInfo<void> page = PageInfo<void>(name);
+}
+
+/// generated route for
+/// [SelectUnitPage]
+class SelectUnitPageRoute extends PageRouteInfo<SelectUnitPageRouteArgs> {
+  SelectUnitPageRoute({
+    Key? key,
+    List<PageRouteInfo>? children,
+  }) : super(
+          SelectUnitPageRoute.name,
+          args: SelectUnitPageRouteArgs(key: key),
+          initialChildren: children,
+        );
+
+  static const String name = 'SelectUnitPageRoute';
+
+  static const PageInfo<SelectUnitPageRouteArgs> page =
+      PageInfo<SelectUnitPageRouteArgs>(name);
+}
+
+class SelectUnitPageRouteArgs {
+  const SelectUnitPageRouteArgs({this.key});
+
+  final Key? key;
+
+  @override
+  String toString() {
+    return 'SelectUnitPageRouteArgs{key: $key}';
+  }
+}
+
+/// generated route for
+/// [SignUpPage]
+class SignUpPageRoute extends PageRouteInfo<void> {
+  const SignUpPageRoute({List<PageRouteInfo>? children})
+      : super(
+          SignUpPageRoute.name,
+          initialChildren: children,
+        );
+
+  static const String name = 'SignUpPageRoute';
+
+  static const PageInfo<void> page = PageInfo<void>(name);
+}
+
+/// generated route for
+/// [SignUpSuccessPage]
+class SignUpSuccessPageRoute extends PageRouteInfo<void> {
+  const SignUpSuccessPageRoute({List<PageRouteInfo>? children})
+      : super(
+          SignUpSuccessPageRoute.name,
+          initialChildren: children,
+        );
+
+  static const String name = 'SignUpSuccessPageRoute';
+
+  static const PageInfo<void> page = PageInfo<void>(name);
+}
+
+/// generated route for
+/// [SignUpVerifyPage]
+class SignUpVerifyPageRoute extends PageRouteInfo<void> {
+  const SignUpVerifyPageRoute({List<PageRouteInfo>? children})
+      : super(
+          SignUpVerifyPageRoute.name,
+          initialChildren: children,
+        );
+
+  static const String name = 'SignUpVerifyPageRoute';
+
+  static const PageInfo<void> page = PageInfo<void>(name);
+}
+
+/// generated route for
+/// [TenantDocPage]
+class TenantDocPageRoute extends PageRouteInfo<TenantDocPageRouteArgs> {
+  TenantDocPageRoute({
+    Key? key,
+    List<PageRouteInfo>? children,
+  }) : super(
+          TenantDocPageRoute.name,
+          args: TenantDocPageRouteArgs(key: key),
+          initialChildren: children,
+        );
+
+  static const String name = 'TenantDocPageRoute';
+
+  static const PageInfo<TenantDocPageRouteArgs> page =
+      PageInfo<TenantDocPageRouteArgs>(name);
+}
+
+class TenantDocPageRouteArgs {
+  const TenantDocPageRouteArgs({this.key});
+
+  final Key? key;
+
+  @override
+  String toString() {
+    return 'TenantDocPageRouteArgs{key: $key}';
+  }
+}

+ 3 - 0
packages/cpt_auth/pubspec.yaml

@@ -48,6 +48,9 @@ dependencies:
   # Hooks 简化 Riverpod 获取
   hooks_riverpod: ^2.5.1
 
+  # 验证码的插件
+  pinput: ^5.0.0
+
 dev_dependencies:
   flutter_test:
     sdk: flutter

+ 4 - 0
packages/cpt_community/devtools_options.yaml

@@ -0,0 +1,4 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
+  - provider: true

+ 4 - 0
packages/cpt_facility/devtools_options.yaml

@@ -0,0 +1,4 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
+  - provider: true

+ 4 - 0
packages/cpt_form/devtools_options.yaml

@@ -0,0 +1,4 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
+  - provider: true

+ 4 - 0
packages/cpt_main/devtools_options.yaml

@@ -0,0 +1,4 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
+  - provider: true

+ 365 - 0
packages/cpt_main/lib/modules/home/home_page.dart

@@ -0,0 +1,365 @@
+import 'package:cpt_main/modules/home/home_state.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+import 'item_home_category.dart';
+import 'home_view_model.dart';
+import 'item_home_last_news.dart';
+import 'item_home_last_trans.dart';
+import 'item_home_manage_guide.dart';
+import 'item_home_property_news.dart';
+
+@RoutePage()
+class HomePage extends HookConsumerWidget {
+  const HomePage({super.key});
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.read(homeViewModelProvider.notifier);
+    final state = ref.watch(homeViewModelProvider);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, "Good Afternoon,Mike",
+          backgroundColor: context.appColors.whiteBG,
+          actions: [
+            Center(
+                child: Stack(
+                  clipBehavior: Clip.none, // 不裁剪超出边界的内容
+                  alignment: Alignment.topLeft,
+                  children: <Widget>[
+                    // 通知图标
+                    const MyAssetImage(Assets.mainHomeNotificationIcon, width: 19, height: 20),
+
+                    //未读消息
+                    Positioned(
+                      left: 0,
+                      top: 0,
+                      child: Transform.translate(
+                        offset: const Offset(-10, -5),
+                        child: MyTextView(
+                          "99",
+                          boxWidth: 20.0,
+                          textColor: Colors.white,
+                          fontSize: 10,
+                          isFontLight: true,
+                          backgroundColor: context.appColors.redDefault,
+                          cornerRadius: 8,
+                          paddingTop: 2,
+                          paddingBottom: 2,
+                          textAlign: TextAlign.center,
+                        ),
+                      ),
+                    ),
+                  ],
+                ).onTap(viewModel.gotoNotificationPage))
+                .marginOnly(right: 15),
+          ],
+          showBottomDivider: true),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: CustomScrollView(
+        scrollDirection: Axis.vertical,
+        physics: const BouncingScrollPhysics(),
+        slivers: [
+          //支付与奖励
+          _buildPaymentAndRewardsWidget(context, ref),
+
+          //间距
+          _buildSliverSpace(20),
+
+          //九宫选项组 (固定)
+          _buildCategoryWidget(context, ref),
+
+          //轮播图片 (动态)
+          _buildBannerImage(),
+
+          //最新的新闻(动态)
+          _buildLastNews(context, ref),
+
+          //最新的交易
+          SliverToBoxAdapter(
+            child: MyTextView(
+              marginTop: 14,
+              marginLeft: 15,
+              marginBottom: 14,
+              S.current.latest_transactions,
+              textColor: context.appColors.textPrimary,
+              fontSize: 16,
+              isFontMedium: true,
+            ),
+          ),
+
+          //最新交易列表 (动态)
+          _buildLastTransaction(context, state),
+
+          //房产新闻
+          SliverToBoxAdapter(
+            child: MyTextView(
+              marginTop: 14,
+              marginLeft: 15,
+              marginBottom: 14,
+              onClick: viewModel.gotoPropertyNewsPage,
+              S.current.property_news,
+              textColor: context.appColors.textPrimary,
+              fontSize: 16,
+              isFontMedium: true,
+            ),
+          ),
+
+          //房产新闻列表 (动态)
+          _buildPropertyNews(context, state),
+
+          //管理员介绍 (固定)
+          _buildManagementGuides(context, ref),
+
+          //间距
+          _buildSliverSpace(15),
+        ],
+      ),
+    );
+  }
+
+  //顶部的支付与奖励的布局
+  Widget _buildPaymentAndRewardsWidget(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.read(homeViewModelProvider.notifier);
+
+    return SliverToBoxAdapter(
+      child: Container(
+        color: context.appColors.whiteBG,
+        width: double.infinity,
+        height: 45,
+        child: Column(
+          children: [
+            Row(
+              children: [
+                Row(
+                  mainAxisSize: MainAxisSize.max,
+                  mainAxisAlignment: MainAxisAlignment.center,
+                  children: [
+                    const MyAssetImage(Assets.mainHomePaymentIcon, width: 16.5, height: 18).marginOnly(left: 20),
+                    MyTextView(
+                      S.current.payment,
+                      textColor: context.appColors.textBlack,
+                      fontSize: 15,
+                      isFontMedium: true,
+                    ).paddingOnly(left: 13, right: 13).expanded(),
+                    const MyAssetImage(Assets.mainHomeMoreIcon, width: 6, height: 8.5).marginOnly(right: 15),
+                  ],
+                ).onTap(viewModel.gotoPaymentPage).expanded(),
+                Container(color: context.appColors.dividerDefault, width: 0.5, height: double.infinity),
+                Row(
+                  mainAxisSize: MainAxisSize.max,
+                  mainAxisAlignment: MainAxisAlignment.center,
+                  children: [
+                    const MyAssetImage(Assets.mainHomeRewardsIcon, width: 16.5, height: 17).marginOnly(left: 20),
+                    MyTextView(
+                      S.current.rewards,
+                      textColor: context.appColors.textBlack,
+                      fontSize: 15,
+                      isFontMedium: true,
+                    ).paddingOnly(left: 13, right: 13).expanded(),
+                    const MyAssetImage(Assets.mainHomeMoreIcon, width: 6, height: 8.5).marginOnly(right: 15),
+                  ],
+                ).onTap(viewModel.gotoRewardsPage).expanded(),
+              ],
+            ).expanded(),
+            //底部分割线
+            Container(color: context.appColors.dividerDefault, height: 0.5, width: double.infinity),
+          ],
+        ),
+      ),
+    );
+  }
+
+  Widget _buildSliverSpace(double size) {
+    return SliverToBoxAdapter(
+      child: SizedBox(height: size),
+    );
+  }
+
+  //九宫格选项组
+  Widget _buildCategoryWidget(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.read(homeViewModelProvider.notifier);
+    final state = ref.watch(homeViewModelProvider);
+
+    return SliverGrid(
+      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
+        crossAxisCount: 3, // 三列
+        mainAxisSpacing: 14.0, // 主轴(上下)的间距
+        crossAxisSpacing: 0.0, // 交叉轴(左右)的间距
+        childAspectRatio: 9 / 7, // 子组件的宽高比
+      ),
+      delegate: SliverChildBuilderDelegate(
+            (BuildContext context, int index) {
+          return HomeCategoryItem(
+            category: state.homeCategory[index],
+          ).onTap(() {
+            viewModel.switchCategory(index);
+          }); // 生成每个网格项
+        },
+        childCount: state.homeCategory.length, // 总共的网格项数
+      ),
+    );
+  }
+
+  //Banner的布局
+  Widget _buildBannerImage() {
+    return SliverToBoxAdapter(
+      child: Center(
+        child: MyLoadImage(
+          "https://t11.baidu.com/it/u=1326770860,192430039&fm=30&app=106&f=JPEG?w=640&h=427&s=33B5BFAA6A165BCA182937620300D077",
+          width: 345,
+          height: 152.5,
+          cornerRadius: 5,
+        ),
+      ).marginOnly(top: 21),
+    );
+  }
+
+  //最新新闻
+  Widget _buildLastNews(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.read(homeViewModelProvider.notifier);
+    final state = ref.watch(homeViewModelProvider);
+
+    return SliverToBoxAdapter(
+        child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          mainAxisAlignment: MainAxisAlignment.start,
+          children: [
+            MyTextView(
+              S.current.latest_news,
+              fontSize: 16,
+              marginTop: 14,
+              marginBottom: 14,
+              isFontMedium: true,
+              onClick: viewModel.gotoLastNewsPage,
+              textColor: context.appColors.textPrimary,
+            ),
+            SingleChildScrollView(
+              scrollDirection: Axis.horizontal,
+              physics: const BouncingScrollPhysics(),
+              clipBehavior: Clip.none,
+              child: Row(
+                children: List.generate(state.lastNews.length, (index) {
+                  return LastNewsItem(
+                    lastNews: state.lastNews[index],
+                  );
+                }),
+              ),
+            )
+          ],
+        ).paddingOnly(left: 15, right: 15));
+  }
+
+  //最新的交易列表
+  Widget _buildLastTransaction(BuildContext context, HomeState state) {
+    return SliverPadding(
+      padding: const EdgeInsets.symmetric(horizontal: 15),
+      sliver: DecoratedSliver(
+        decoration: BoxDecoration(
+          color: context.appColors.whiteBG,
+          borderRadius: BorderRadius.circular(5.0),
+          boxShadow: [
+            BoxShadow(
+              color: const Color(0xFF656565).withOpacity(0.1),
+              offset: const Offset(0, 1.5),
+              blurRadius: 2.5,
+              spreadRadius: 1.5,
+            ),
+          ],
+        ),
+        sliver: SliverPadding(
+          padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 20),
+          sliver: SliverList(
+            delegate: SliverChildBuilderDelegate(
+                  (BuildContext context, int index) {
+                return Padding(
+                  padding: const EdgeInsets.symmetric(vertical: 10),
+                  child: LastTransItem(
+                    lastTrans: state.lastTrans[index],
+                  ),
+                );
+              },
+              childCount: state.lastTrans.length,
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+
+  //房产新闻的双列表
+  Widget _buildPropertyNews(BuildContext context, HomeState state) {
+    return SliverList(
+      delegate: SliverChildListDelegate(
+        [
+          // PropertyNews(),
+
+          // 第一个水平滑动列表
+          _buildPropertyNewsHorizontalList(),
+
+          const SizedBox(height: 10),
+
+          // // 第二个水平滑动列表
+          _buildPropertyNewsHorizontalList(),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildPropertyNewsHorizontalList() {
+    return SingleChildScrollView(
+      scrollDirection: Axis.horizontal,
+      physics: const BouncingScrollPhysics(),
+      clipBehavior: Clip.none,
+      child: Row(
+        children: List.generate(5, (index) {
+          return PropertyNews();
+        }),
+      ).marginOnly(left: 15, right: 15),
+    );
+  }
+
+  //管理员介绍
+  Widget _buildManagementGuides(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.read(homeViewModelProvider.notifier);
+    final state = ref.watch(homeViewModelProvider);
+
+    return SliverToBoxAdapter(
+        child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          mainAxisAlignment: MainAxisAlignment.start,
+          children: [
+            MyTextView(
+              S.current.strata_management_guides,
+              fontSize: 16,
+              marginTop: 14,
+              marginBottom: 14,
+              onClick: viewModel.gotoManageGuidePage,
+              isFontMedium: true,
+              textColor: context.appColors.textPrimary,
+            ),
+            SingleChildScrollView(
+              scrollDirection: Axis.horizontal,
+              physics: const BouncingScrollPhysics(),
+              clipBehavior: Clip.none,
+              child: Row(
+                children: List.generate(state.manage_guide.length, (index) {
+                  return ManageGuideItem(
+                    manageGuide: state.manage_guide[index],
+                  );
+                }),
+              ),
+            )
+          ],
+        ).paddingOnly(left: 15, right: 15));
+  }
+}

+ 103 - 0
packages/cpt_main/lib/modules/home/home_state.dart

@@ -0,0 +1,103 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+
+class HomeState {
+  final homeCategory = [
+    {
+      'category_name': S.current.facility,
+      'category_icon': Assets.mainHomeFacilityIcon,
+    },
+    {
+      'category_name': S.current.form,
+      'category_icon': Assets.mainHomeFormsIcon,
+    },
+    {
+      'category_name': S.current.notice_board,
+      'category_icon': Assets.mainHomeBoticeBoardIcon,
+    },
+    {
+      'category_name': S.current.property,
+      'category_icon': Assets.mainHomePropertyIcon,
+    },
+    {
+      'category_name': S.current.service,
+      'category_icon': Assets.mainHomeServiceIcon,
+    },
+    {
+      'category_name': S.current.community,
+      'category_icon': Assets.mainHomeCommunityIcon,
+    }
+  ];
+
+  final lastNews = [
+    {
+      'category_name': S.current.property,
+      'category_icon': Assets.mainHomePropertyNews,
+      'icon_width': 38.5,
+      'icon_height': 39.5,
+      'margin_right': 13.0,
+    },
+    {
+      'category_name': S.current.internal,
+      'category_icon': Assets.mainHomeInternalNews,
+      'icon_width': 33.5,
+      'icon_height': 41.0,
+      'margin_right': 11.0,
+    },
+    {
+      'category_name': S.current.info,
+      'category_icon': Assets.mainHomeInfoNews,
+      'icon_width': 44.0,
+      'icon_height': 42.0,
+      'margin_right': 6.0,
+    },
+    {
+      'category_name': S.current.publish,
+      'category_icon': Assets.mainHomePublishNews,
+      'icon_width': 40.5,
+      'icon_height': 42.5,
+      'margin_right': 11.0,
+    }
+  ];
+
+
+  final manage_guide = [
+    {
+      'category_name': S.current.introduction_to_info_pack,
+      'category_icon': Assets.mainIntroductionGuide,
+      'icon_width': 118.5,
+      'icon_height': 73.5,
+      'margin_top': 12.0,
+    },
+    {
+      'category_name': S.current.your_roles_responsibilities,
+      'category_icon': Assets.mainRolesGuide,
+      'icon_width': 116,
+      'icon_height': 78,
+      'margin_top': 8.5,
+    },
+    {
+      'category_name': S.current.property_guide,
+      'category_icon': Assets.mainPropertyGuide,
+      'icon_width': 117.0,
+      'icon_height': 76.5,
+      'margin_top': 10.0,
+    },
+  ];
+
+  //模拟数据
+  final lastTrans = [
+    {
+      'title': '18 Sep 2024 Blk 39 #09-XX',
+      'price': '1,001 - 1,337 sqft',
+      'total': '1.338 M',
+      'unit': '',
+    },
+    {
+      'title': 'Jul 2024  Blk XX #XX to XX',
+      'price': '1,100 - 1,200 sqft',
+      'total': '4,000 M',
+      'unit': 'per month',
+    },
+  ];
+}

+ 73 - 0
packages/cpt_main/lib/modules/home/home_view_model.dart

@@ -0,0 +1,73 @@
+import 'package:cpt_main/modules/notification/notification_page.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:router/componentRouter/community_service.dart';
+import 'package:router/componentRouter/component_service_manager.dart';
+
+import 'home_state.dart';
+
+part 'home_view_model.g.dart';
+
+@riverpod
+class HomeViewModel extends _$HomeViewModel {
+  @override
+  HomeState build() {
+    return HomeState();
+  }
+
+  /// 根据索引触发点击的事件
+  void switchCategory(int index) {
+    switch (index) {
+      case 0:
+        //设备
+        ComponentServiceManager().facilityService.startFacilityPage();
+        break;
+      case 1:
+        //表单
+        ComponentServiceManager().formService.startFormPage();
+        break;
+      case 2:
+        //公告板
+        ComponentServiceManager().noticeBoardService.startNoticeBoardPage();
+        break;
+      case 3:
+        //房产
+        ComponentServiceManager().propertyService.startPropertyPage();
+        break;
+      case 4:
+        //广告服务
+        ComponentServiceManager().servicesService.startServicePage();
+        break;
+      case 5:
+        //社区
+        ComponentServiceManager().communityService.startCommunityPage();
+        break;
+    }
+  }
+
+  //去消息通知页面
+  void gotoNotificationPage() {
+    NotificationPage.startInstance();
+  }
+
+  void gotoPaymentPage() {
+    ComponentServiceManager().paymentService.startPaymentPage();
+  }
+
+  void gotoRewardsPage() {
+    ComponentServiceManager().rewardsService.startRewardsPage();
+  }
+
+  void gotoLastNewsPage(){
+    ToastEngine.show("去最近新闻的页面");
+  }
+
+  void gotoPropertyNewsPage(){
+    ToastEngine.show("去房产新闻的页面");
+  }
+
+  void gotoManageGuidePage(){
+    ToastEngine.show("去管理员指引的页面");
+  }
+
+}

+ 26 - 0
packages/cpt_main/lib/modules/home/home_view_model.g.dart

@@ -0,0 +1,26 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'home_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$homeViewModelHash() => r'9b4974389335a1d56e6b345f83e7c626e3ba0d28';
+
+/// See also [HomeViewModel].
+@ProviderFor(HomeViewModel)
+final homeViewModelProvider =
+    AutoDisposeNotifierProvider<HomeViewModel, HomeState>.internal(
+  HomeViewModel.new,
+  name: r'homeViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$homeViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$HomeViewModel = AutoDisposeNotifier<HomeState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 24 - 0
packages/cpt_main/lib/modules/home/item_home_category.dart

@@ -0,0 +1,24 @@
+import 'package:cpt_main/modules/feedback/page/feedback_page.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+class HomeCategoryItem extends StatelessWidget {
+  final Map<String, dynamic> category;
+
+  HomeCategoryItem({required this.category});
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      mainAxisAlignment: MainAxisAlignment.center,
+      crossAxisAlignment: CrossAxisAlignment.center,
+      children: <Widget>[
+        MyAssetImage(category['category_icon'], width: 70, height: 70),
+        const SizedBox(height: 5),
+        MyTextView(category['category_name'], fontSize: 15, isFontMedium: true, textColor: context.appColors.textBlack),
+      ],
+    );
+  }
+}

+ 60 - 0
packages/cpt_main/lib/modules/home/item_home_last_news.dart

@@ -0,0 +1,60 @@
+import 'package:cpt_main/modules/feedback/page/feedback_page.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+class LastNewsItem extends StatelessWidget {
+  final Map<String, dynamic> lastNews;
+
+  LastNewsItem({required this.lastNews});
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      mainAxisAlignment: MainAxisAlignment.start,
+      children: [
+        MyTextView(
+          lastNews['category_name'],
+          marginLeft: 9,
+          marginTop: 9,
+          fontSize: 14,
+          isFontMedium: true,
+          textColor: context.appColors.tabTextUnSelectedPrimary,
+        ),
+        const Spacer(),
+        Row(
+          mainAxisAlignment: MainAxisAlignment.end, // 对齐到右侧
+          children: [
+            Padding(
+              padding: EdgeInsets.only(right: lastNews['margin_right']), // 控制边距
+              child: MyAssetImage(
+                lastNews['category_icon'],
+                width: (lastNews['icon_width'] as num).toDouble(),
+                height: (lastNews['icon_height'] as num).toDouble(),
+                fit: BoxFit.cover, // 调整 fit 参数
+              ),
+            ),
+          ],
+        ),
+      ],
+      // ),
+    )
+        .decorated(
+          color: context.appColors.whiteBG,
+          borderRadius: BorderRadius.circular(5.0), // 5个圆角
+          boxShadow: [
+            BoxShadow(
+              color: const Color(0xFF656565).withOpacity(0.1), // 阴影颜色,并且设置透明度
+              offset: const Offset(0, 1.5), // 阴影的偏移量
+              blurRadius: 2.5, // 模糊半径
+              spreadRadius: 1.5, // 扩散半径
+            ),
+          ],
+        )
+        .marginOnly(right: 5)
+        .constrained(width: 86.5, height: 78.5);
+  }
+}

+ 72 - 0
packages/cpt_main/lib/modules/home/item_home_last_trans.dart

@@ -0,0 +1,72 @@
+import 'package:cpt_main/modules/feedback/page/feedback_page.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:shared/utils/util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+class LastTransItem extends StatelessWidget {
+  final Map<String, dynamic> lastTrans;
+
+  LastTransItem({required this.lastTrans});
+
+  @override
+  Widget build(BuildContext context) {
+    return Row(
+      mainAxisSize: MainAxisSize.max,
+      crossAxisAlignment: CrossAxisAlignment.center,
+      mainAxisAlignment: MainAxisAlignment.start,
+      children: [
+        Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            MyTextView(
+              lastTrans['title'],
+              fontSize: 15,
+              textColor: context.appColors.textBlack,
+              isFontMedium: true,
+            ),
+            MyTextView(
+              lastTrans['price'],
+              fontSize: 15,
+              marginTop: 5,
+              textColor: context.appColors.textBlack,
+              isFontMedium: true,
+            ),
+          ],
+        ).expanded(),
+
+        Container(
+          width: 91.5,
+          margin: const EdgeInsets.only(left: 10),
+          height: 45,
+          decoration: BoxDecoration(
+            color: context.appColors.lightPurpleBg,
+            borderRadius: BorderRadius.circular(5.0), // 5个圆角
+          ),
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.center,
+            mainAxisAlignment: MainAxisAlignment.center,
+            children: [
+              MyTextView(
+               "\$${lastTrans['total']}",
+                fontSize: 17,
+                textColor: context.appColors.textBlack,
+                isFontMedium: true,
+              ),
+              Visibility(
+                  visible: Utils.isNotEmpty(lastTrans['unit']),
+                  child: MyTextView(
+                    lastTrans['unit'],
+                    fontSize: 12,
+                    textColor: context.appColors.textBlack,
+                    isFontRegular: true,
+                  ))
+            ],
+          ),
+        ),
+      ],
+    );
+  }
+}

+ 53 - 0
packages/cpt_main/lib/modules/home/item_home_manage_guide.dart

@@ -0,0 +1,53 @@
+import 'package:cpt_main/modules/feedback/page/feedback_page.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+class ManageGuideItem extends StatelessWidget {
+  final Map<String, dynamic> manageGuide;
+
+  ManageGuideItem({required this.manageGuide});
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      mainAxisAlignment: MainAxisAlignment.start,
+      children: [
+        MyAssetImage(
+          manageGuide['category_icon'],
+          width: (manageGuide['icon_width'] as num).toDouble(),
+          height: (manageGuide['icon_height'] as num).toDouble(),
+          fit: BoxFit.cover, // 调整 fit 参数
+        ).marginOnly(top: manageGuide['margin_top']),
+
+        MyTextView(
+          manageGuide['category_name'],
+          marginLeft: 9,
+          marginTop: 10,
+          maxLines: 2,
+          fontSize: 14,
+          isFontMedium: true,
+          textColor: context.appColors.tabTextUnSelectedPrimary,
+        ),
+      ],
+      // ),
+    )
+        .decorated(
+          color: context.appColors.lightBlueBg,
+          borderRadius: BorderRadius.circular(5.0), // 5个圆角
+          boxShadow: [
+            BoxShadow(
+              color: const Color(0xFF656565).withOpacity(0.1), // 阴影颜色,并且设置透明度
+              offset: const Offset(0, 1.5), // 阴影的偏移量
+              blurRadius: 2.5, // 模糊半径
+              spreadRadius: 1.5, // 扩散半径
+            ),
+          ],
+        )
+        .marginOnly(right: 10)
+        .constrained(width: 130, height: 142.5);
+  }
+}

+ 49 - 0
packages/cpt_main/lib/modules/home/item_home_property_news.dart

@@ -0,0 +1,49 @@
+import 'package:cpt_main/modules/feedback/page/feedback_page.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:shared/utils/util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+class PropertyNews extends StatelessWidget {
+  PropertyNews();
+
+  @override
+  Widget build(BuildContext context) {
+    return Row(
+      crossAxisAlignment: CrossAxisAlignment.center,
+      mainAxisAlignment: MainAxisAlignment.start,
+      children: [
+        MyLoadImage(
+          "https://t11.baidu.com/it/u=4142771387,220552469&fm=30&app=106&f=JPEG?w=564&h=604&s=FA2FB04406270B0D68AE05910300509B",
+          width: 80,
+          height: 80,
+        ),
+        MyTextView(
+          "Key Appeals and Performance of Nonlan-ded Properties in District 19",
+          fontSize: 15,
+          marginLeft: 12.5,
+          marginRight: 12.5,
+          maxLines: 4,
+          textColor: context.appColors.textBlack,
+          isFontMedium: true,
+        ).expanded(),
+      ],
+    )
+        .decorated(
+          color: context.appColors.whiteBG,
+          borderRadius: BorderRadius.circular(5.0), // 5个圆角
+          boxShadow: [
+            BoxShadow(
+              color: const Color(0xFF656565).withOpacity(0.1), // 阴影颜色,并且设置透明度
+              offset: const Offset(0, 1.5), // 阴影的偏移量
+              blurRadius: 2.5, // 模糊半径
+              spreadRadius: 1.5, // 扩散半径
+            ),
+          ],
+        )
+        .marginOnly(right: 10)
+        .constrained(height: 80, width: 270);
+  }
+}

+ 0 - 112
packages/cpt_main/lib/modules/home/page/home_page.dart

@@ -1,112 +0,0 @@
-import 'package:cpt_main/modules/visitor/page/visitor_page.dart';
-import 'package:cs_resources/generated/l10n.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter/src/widgets/framework.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:auto_route/auto_route.dart';
-import 'package:router/componentRouter/component_service_manager.dart';
-import 'package:widgets/ext/ex_widget.dart';
-
-@RoutePage()
-class HomePage extends HookConsumerWidget {
-  const HomePage({super.key});
-
-  @override
-  Widget build(BuildContext context, WidgetRef ref) {
-    return Scaffold(
-      appBar: AppBar(title: Text("Home")),
-      body: Center(
-        child: Column(
-          children: [
-            Row(
-              mainAxisAlignment: MainAxisAlignment.spaceAround,
-              children: [
-                ElevatedButton(
-                  onPressed: () {
-                    ComponentServiceManager().paymentService.startPaymentPage();
-                  },
-                  child: Text(S.of(context).payment),  //国际化中英文适配
-                ),
-                ElevatedButton(
-                  onPressed: () {
-                    ComponentServiceManager().rewardsService.startRewardsPage();
-                  },
-                  child: Text(S.of(context).rewards),
-                ),
-                ElevatedButton(
-                  onPressed: () {},
-                  child: Text(S.of(context).notification),
-                ),
-              ],
-            ).marginOnly(top: 10),
-            Row(
-              mainAxisAlignment: MainAxisAlignment.spaceAround,
-              children: [
-                ElevatedButton(
-                  onPressed: () {
-                    ComponentServiceManager().facilityService.startFacilityPage();
-                  },
-                  child: Text(S.of(context).facility),
-                ),
-                ElevatedButton(
-                  onPressed: () {
-                    ComponentServiceManager().formService.startFormPage();
-                  },
-                  child: Text(S.of(context).form),
-                ),
-                ElevatedButton(
-                  onPressed: () {
-                    ComponentServiceManager().noticeBoardService.startNoticeBoardPage();
-                  },
-                  child: Text(S.of(context).notice_board),
-                ),
-              ],
-            ).marginOnly(top: 10),
-            Row(
-              mainAxisAlignment: MainAxisAlignment.spaceAround,
-              children: [
-                ElevatedButton(
-                  onPressed: () {
-                    ComponentServiceManager().propertyService.startPropertyPage();
-                  },
-                  child: Text('Property'),
-                ),
-                ElevatedButton(
-                  onPressed: () {
-                    ComponentServiceManager().servicesService.startServicePage();
-                  },
-                  child: Text('Service'),
-                ),
-                ElevatedButton(
-                  onPressed: () {
-                    ComponentServiceManager().communityService.startCommunityPage();
-                  },
-                  child: Text('Community'),
-                ),
-              ],
-            ).marginOnly(top: 10),
-            Row(
-              mainAxisAlignment: MainAxisAlignment.spaceAround,
-              children: [
-                ElevatedButton(
-                  onPressed: () {
-                    ComponentServiceManager().authService.startLoginPage();
-                  },
-                  child: Text('Login'),
-                ),
-                ElevatedButton(
-                  onPressed: () {},
-                  child: Text('Forgot'),
-                ),
-                ElevatedButton(
-                  onPressed: () {},
-                  child: Text('Setting'),
-                ),
-              ],
-            ).marginOnly(top: 10),
-          ],
-        ),
-      ),
-    );
-  }
-}

+ 117 - 0
packages/cpt_main/lib/modules/main/main_page.dart

@@ -0,0 +1,117 @@
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:flutter/material.dart';
+import 'package:shared/utils/device_utils.dart';
+import '../../router/page/main_page_router.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+
+import 'main_view_model.dart';
+
+@RoutePage()
+class MainPage extends HookConsumerWidget {
+  const MainPage({super.key});
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.popUntilRoot();
+      context.router.replace(const MainPageRoute());
+    } else {
+      appRouter.popUntilRoot();
+      appRouter.replace(const MainPageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.watch(mainViewModelProvider.notifier);
+    final state = ref.watch(mainViewModelProvider);
+
+    // return AutoTabsRouter(
+    //   routes: const [
+    //     HomePageRoute(),
+    //     VisitorPageRoute(),
+    //     FeedbackPageRoute(),
+    //     MePageRoute(),
+    //   ],
+    //   transitionBuilder: (context, child, animation) => FadeTransition(
+    //     opacity: animation,
+    //     child: child,
+    //   ),
+    //   builder: (context, child) {
+    //     final tabsRouter = AutoTabsRouter.of(context);
+    //     return AnnotatedRegion<SystemUiOverlayStyle>(
+    //         value: ThemeConfig.getSystemUiOverlayStyleByTheme(context),
+    //         child: Scaffold(
+    //           body: child,
+    //           bottomNavigationBar: BottomNavigationBar(
+    //             backgroundColor: context.theme.primaryColor,
+    //             selectedItemColor: context.theme.colorScheme.secondary,
+    //             unselectedItemColor: Colors.grey[500],
+    //             elevation: 20,
+    //             type: BottomNavigationBarType.fixed,
+    //             items: const <BottomNavigationBarItem>[
+    //               BottomNavigationBarItem(
+    //                 icon: Icon(Icons.home),
+    //                 label: 'Home',
+    //               ),
+    //               BottomNavigationBarItem(
+    //                 icon: Icon(Icons.card_giftcard),
+    //                 label: 'Visitor',
+    //               ),
+    //               BottomNavigationBarItem(
+    //                 icon: Icon(Icons.feed),
+    //                 label: 'Feedback',
+    //               ),
+    //               BottomNavigationBarItem(
+    //                 icon: Icon(Icons.person),
+    //                 label: 'Me',
+    //               ),
+    //             ],
+    //             onTap: tabsRouter.setActiveIndex,
+    //             currentIndex: tabsRouter.activeIndex,
+    //           ),
+    //         ));
+    //   },
+    // );
+
+    return AutoTabsScaffold(
+      routes: const [
+        HomePageRoute(),
+        VisitorPageRoute(),
+        FeedbackPageRoute(),
+        MePageRoute(),
+      ],
+      transitionBuilder: (context, child, animation) => FadeTransition(
+        opacity: animation,
+        child: child,
+      ),
+      bottomNavigationBuilder: (context, tabsRouter) {
+        return BottomNavigationBar(
+          elevation: 10,
+          backgroundColor: context.appColors.whiteBG,
+          type: BottomNavigationBarType.fixed,
+          currentIndex: tabsRouter.activeIndex,
+          onTap: tabsRouter.setActiveIndex,
+          unselectedLabelStyle: const TextStyle(color: AppColorsTheme.color666666, fontWeight: FontWeight.w400),
+          selectedLabelStyle: TextStyle(color: context.appColors.textPrimary, fontWeight: FontWeight.w400),
+          unselectedFontSize: 12,
+          selectedFontSize: 12,
+          items: () {
+            var items = <BottomNavigationBarItem>[];
+            state.bottomMap.forEach((k, v) {
+              items.add(BottomNavigationBarItem(
+                label: k,
+                icon: v[0],
+                activeIcon: DeviceUtils.isDarkMode(context) ? v[2] : v[1],
+              ));
+            });
+            return items;
+          }(),
+        );
+      },
+    );
+  }
+}

+ 86 - 0
packages/cpt_main/lib/modules/main/main_state.dart

@@ -0,0 +1,86 @@
+import 'package:cpt_main/modules/feedback/page/feedback_page.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:flutter/material.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_load_image.dart';
+
+class MainState {
+//底部数据Map
+  final Map bottomMap = {
+    S.current.home: [
+      const MyAssetImage(
+        //默认
+        Assets.mainTabHomeUnselected,
+        width: 20,
+        height: 20,
+      ).marginOnly(bottom: 4),
+      const MyAssetImage(
+        Assets.mainTabHomeSelected,
+        width: 20,
+        height: 20,
+      ).marginOnly(bottom: 4),
+      const MyAssetImage(
+        //暗色
+        Assets.mainTabHomeSelected,
+        width: 20,
+        height: 20,
+        color: Colors.white,
+      ).marginOnly(bottom: 4)
+    ],
+    S.current.visitor: [
+      const MyAssetImage(
+        Assets.mainTabVisitorUnselected,
+        width: 20,
+        height: 20,
+      ).marginOnly(bottom: 4),
+      const MyAssetImage(
+        Assets.mainTabVisitorSelected,
+        width: 20,
+        height: 20,
+      ).marginOnly(bottom: 4),
+      const MyAssetImage(
+        Assets.mainTabVisitorSelected,
+        width: 20,
+        height: 20,
+        color: Colors.white,
+      ).marginOnly(bottom: 4)
+    ],
+    S.current.feedback: [
+      const MyAssetImage(
+        Assets.mainTabFeedbackUnselected,
+        width: 20,
+        height: 20,
+      ).marginOnly(bottom: 4),
+      const MyAssetImage(
+        Assets.mainTabFeedbackSelected,
+        width: 20,
+        height: 20,
+      ).marginOnly(bottom: 4),
+      const MyAssetImage(
+        Assets.mainTabFeedbackSelected,
+        width: 20,
+        height: 20,
+        color: Colors.white,
+      ).marginOnly(bottom: 4)
+    ],
+    S.current.me: [
+      const MyAssetImage(
+        Assets.mainTabMeUnselected,
+        width: 20,
+        height: 20,
+      ).marginOnly(bottom: 4),
+      const MyAssetImage(
+        Assets.mainTabMeSelected,
+        width: 20,
+        height: 20,
+      ).marginOnly(bottom: 4),
+      const MyAssetImage(
+        Assets.mainTabMeSelected,
+        width: 20,
+        height: 20,
+        color: Colors.white,
+      ).marginOnly(bottom: 4),
+    ],
+  };
+}

+ 15 - 0
packages/cpt_main/lib/modules/main/main_view_model.dart

@@ -0,0 +1,15 @@
+
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+import 'main_state.dart';
+part 'main_view_model.g.dart';
+
+@riverpod
+class MainViewModel extends _$MainViewModel {
+
+  @override
+  MainState build(){
+    return MainState();
+  }
+
+}

+ 26 - 0
packages/cpt_main/lib/modules/main/main_view_model.g.dart

@@ -0,0 +1,26 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'main_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$mainViewModelHash() => r'90a50899c1c9b358ab052eae6102c1666d59de8e';
+
+/// See also [MainViewModel].
+@ProviderFor(MainViewModel)
+final mainViewModelProvider =
+    AutoDisposeNotifierProvider<MainViewModel, MainState>.internal(
+  MainViewModel.new,
+  name: r'mainViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$mainViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$MainViewModel = AutoDisposeNotifier<MainState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 0 - 66
packages/cpt_main/lib/modules/main/page/main_page.dart

@@ -1,66 +0,0 @@
-import 'package:flutter/src/widgets/framework.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:auto_route/auto_route.dart';
-import 'package:flutter/material.dart';
-import 'package:shared/utils/ext_dart.dart';
-import '../../../router/page/main_page_router.dart';
-import 'package:router/ext/auto_router_extensions.dart';
-
-@RoutePage()
-class MainPage extends HookConsumerWidget {
-  const MainPage({super.key});
-
-  //启动当前页面
-  static void startInstance({BuildContext? context}) {
-    if (context != null) {
-      context.router.popUntilRoot();
-      context.router.replace(const MainPageRoute());
-    } else {
-      appRouter.popUntilRoot();
-      appRouter.replace(const MainPageRoute());
-    }
-  }
-
-  @override
-  Widget build(BuildContext context, WidgetRef ref) {
-    return AutoTabsScaffold(
-      routes: const [
-        HomePageRoute(),
-        VisitorPageRoute(),
-        FeedbackPageRoute(),
-        MePageRoute(),
-      ],
-      transitionBuilder: (context, child, animation) => FadeTransition(
-        opacity: animation,
-        child: child,
-      ),
-      bottomNavigationBuilder: (context, tabsRouter) {
-        return BottomNavigationBar(
-          unselectedItemColor: Colors.grey[500],
-          elevation: 20,
-          type: BottomNavigationBarType.fixed,
-          currentIndex: tabsRouter.activeIndex,
-          onTap: tabsRouter.setActiveIndex,
-          items: const [
-            BottomNavigationBarItem(
-              icon: Icon(Icons.home),
-              label: 'Home',
-            ),
-            BottomNavigationBarItem(
-              icon: Icon(Icons.card_giftcard),
-              label: 'Visitor',
-            ),
-            BottomNavigationBarItem(
-              icon: Icon(Icons.feed),
-              label: 'Feedback',
-            ),
-            BottomNavigationBarItem(
-              icon: Icon(Icons.person),
-              label: 'Me',
-            ),
-          ],
-        );
-      },
-    );
-  }
-}

packages/cpt_main/lib/modules/me/page/me_page.dart → packages/cpt_main/lib/modules/me/me_page.dart


+ 7 - 3
packages/cpt_main/lib/modules/notification/notification_page.dart

@@ -1,15 +1,19 @@
 import 'package:cpt_main/router/page/main_page_router.dart';
 import 'package:flutter/material.dart';
 import 'package:auto_route/auto_route.dart';
+import 'package:router/ext/auto_router_extensions.dart';
 
 @RoutePage()
 class NotificationPage extends StatelessWidget {
 
   const NotificationPage({Key? key}) : super(key: key);
 
-  //启动当前页面
-  static void startInstance({required BuildContext context}) {
-    context.router.push(const NotificationPageRoute());
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(const NotificationPageRoute());
+    } else {
+      appRouter.push(const NotificationPageRoute());
+    }
   }
 
   @override

+ 4 - 2
packages/cpt_main/lib/modules/visitor/page/visitor_page.dart

@@ -1,5 +1,5 @@
-import 'package:cpt_main/modules/visitor/vm/visitor_view_model.dart';
 import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:cs_resources/theme/theme_config.dart';
 import 'package:cs_resources/theme/theme_notifier.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
@@ -23,6 +23,8 @@ import 'package:widgets/picker/date_picker_util.dart';
 import 'package:widgets/picker/option_pick_util.dart';
 import 'package:plugin_platform/platform_export.dart';
 
+import 'visitor_view_model.dart';
+
 @RoutePage()
 class VisitorPage extends HookConsumerWidget {
   const VisitorPage({Key? key}) : super(key: key);
@@ -35,7 +37,7 @@ class VisitorPage extends HookConsumerWidget {
     Log.d("VisitorPage - build 了");
 
     return Scaffold(
-      appBar: AppBar(title: Text("Visitor")),
+      appBar: AppBar(title: Text("Visitor"),systemOverlayStyle: ThemeConfig.getSystemUiOverlayStyleByTheme(context)),
       backgroundColor: context.appColors.backgroundDefault,
       body: Center(
         child: Column(

packages/cpt_main/lib/modules/visitor/vm/visitor_state.dart → packages/cpt_main/lib/modules/visitor/visitor_state.dart


packages/cpt_main/lib/modules/visitor/vm/visitor_view_model.dart → packages/cpt_main/lib/modules/visitor/visitor_view_model.dart


packages/cpt_main/lib/modules/visitor/vm/visitor_view_model.g.dart → packages/cpt_main/lib/modules/visitor/visitor_view_model.g.dart


+ 1 - 1
packages/cpt_main/lib/router/component/main_component_service_impl.dart

@@ -1,7 +1,7 @@
 /*
  * Main 组件的组件路由
  */
-import 'package:cpt_main/modules/main/page/main_page.dart';
+import 'package:cpt_main/modules/main/main_page.dart';
 import 'package:flutter/material.dart';
 import 'package:router/componentRouter/main_service.dart';
 

+ 4 - 4
packages/cpt_main/lib/router/page/main_page_router.dart

@@ -4,11 +4,11 @@ import 'package:router/ext/auto_router_extensions.dart';
 import 'package:router/path/router_path.dart';
 
 import '../../modules/feedback/page/feedback_page.dart';
-import '../../modules/home/page/home_page.dart';
-import '../../modules/main/page/main_page.dart';
-import '../../modules/me/page/me_page.dart';
+import '../../modules/home/home_page.dart';
+import '../../modules/main/main_page.dart';
+import '../../modules/me/me_page.dart';
 import '../../modules/notification/notification_page.dart';
-import '../../modules/visitor/page/visitor_page.dart';
+import '../../modules/visitor/visitor_page.dart';
 
 
 part 'main_page_router.gr.dart';

+ 4 - 0
packages/cpt_notice_board/devtools_options.yaml

@@ -0,0 +1,4 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
+  - provider: true

+ 4 - 0
packages/cpt_payment/devtools_options.yaml

@@ -0,0 +1,4 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
+  - provider: true

+ 4 - 0
packages/cpt_profile/devtools_options.yaml

@@ -0,0 +1,4 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
+  - provider: true

+ 271 - 0
packages/cpt_profile/lib/modules/change_mobile/change_mobile_page.dart

@@ -0,0 +1,271 @@
+import 'package:cpt_profile/modules/change_mobile/change_mobile_state.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_field.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+import '../../router/page/profile_page_router.dart';
+import 'change_mobile_view_model.dart';
+
+@RoutePage()
+class ChangeMobilePage extends HookConsumerWidget {
+  const ChangeMobilePage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(const ChangeMobilePageRoute());
+    } else {
+      appRouter.push(const ChangeMobilePageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.watch(changeMobileViewModelProvider.notifier);
+    final state = ref.watch(changeMobileViewModelProvider);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, S.current.change_mobile_phone),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: SingleChildScrollView(
+        scrollDirection: Axis.vertical,
+        physics: const BouncingScrollPhysics(),
+        child: Container(
+          margin: const EdgeInsets.symmetric(horizontal: 15),
+          width: double.infinity,
+          child: Column(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              //旧手机
+              MyTextView(
+                S.current.old_mobile_phone,
+                fontSize: 16.5,
+                marginTop: 38,
+                marginBottom: 15,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              //旧的电话号码
+              Row(
+                mainAxisSize: MainAxisSize.max,
+                crossAxisAlignment: CrossAxisAlignment.center,
+                children: [
+                  const MyAssetImage(
+                    Assets.authCountrySg,
+                    width: 45,
+                    height: 30,
+                  ),
+                  MyTextView(
+                    "+65",
+                    textColor: context.appColors.textBlack,
+                    fontSize: 18.5,
+                    marginLeft: 15,
+                    marginRight: 12,
+                    isFontMedium: true,
+                  ),
+                  //电话输入框
+                  _buildInputLayout(
+                    context,
+                    state,
+                    "old",
+                    textInputType: TextInputType.number,
+                    textInputAction: TextInputAction.next,
+                    onSubmit: (formKey, value) {
+                      state.formData[formKey]!['focusNode'].unfocus();
+                      FocusScope.of(context).requestFocus(state.formData['old_code']!['focusNode']);
+                    },
+                  ).expanded(),
+                ],
+              ),
+
+              //旧手机的验证码
+              MyTextView(
+                S.current.verification_code,
+                fontSize: 16.5,
+                marginTop: 13,
+                marginBottom: 15,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              // 表单 - 旧的电话号码验证码
+              _buildInputLayout(
+                context,
+                state,
+                "old_code",
+                textInputType: TextInputType.number,
+                textInputAction: TextInputAction.next,
+                errorText: state.oldCodeErrorText,
+                showRightIcon: true,
+                rightWidget: MyTextView(
+                  state.isOldCounting ? "${state.oldCountdownTime} s" : S.current.get_code,
+                  textAlign: TextAlign.center,
+                  textColor: context.appColors.textPrimary,
+                  fontSize: 15,
+                  paddingRight: 5,
+                  isFontMedium: true,
+                  onClick: state.isOldCounting ? null : () => viewModel.showVerifyCodedDialog(false),
+                ).paddingOnly(top: 15, bottom: 15),
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  FocusScope.of(context).requestFocus(state.formData['new']!['focusNode']);
+                },
+              ),
+
+              //新电话
+              MyTextView(
+                S.current.new_mobile_phone,
+                fontSize: 16.5,
+                marginTop: 38,
+                marginBottom: 15,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              //新的电话号码
+              Row(
+                mainAxisSize: MainAxisSize.max,
+                crossAxisAlignment: CrossAxisAlignment.center,
+                children: [
+                  const MyAssetImage(
+                    Assets.authCountrySg,
+                    width: 45,
+                    height: 30,
+                  ),
+                  MyTextView(
+                    "+65",
+                    textColor: context.appColors.textBlack,
+                    fontSize: 18.5,
+                    marginLeft: 15,
+                    marginRight: 12,
+                    isFontMedium: true,
+                  ),
+                  //电话输入框
+                  _buildInputLayout(
+                    context,
+                    state,
+                    "new",
+                    textInputType: TextInputType.number,
+                    textInputAction: TextInputAction.next,
+                    onSubmit: (formKey, value) {
+                      state.formData[formKey]!['focusNode'].unfocus();
+                      FocusScope.of(context).requestFocus(state.formData['new_code']!['focusNode']);
+                    },
+                  ).expanded(),
+                ],
+              ),
+
+              //新手机的验证码
+              MyTextView(
+                S.current.verification_code,
+                fontSize: 16.5,
+                marginTop: 13,
+                marginBottom: 15,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              // 表单 - 新的电话号码验证码
+              _buildInputLayout(
+                context,
+                state,
+                "new_code",
+                textInputType: TextInputType.number,
+                textInputAction: TextInputAction.done,
+                errorText: state.newCodeErrorText,
+                showRightIcon: true,
+                rightWidget: MyTextView(
+                  state.isNewCounting ? "${state.newCountdownTime} s" : S.current.get_code,
+                  textAlign: TextAlign.center,
+                  textColor: context.appColors.textPrimary,
+                  fontSize: 15,
+                  paddingRight: 5,
+                  isFontMedium: true,
+                  onClick: state.isNewCounting ? null : () => viewModel.showVerifyCodedDialog(true),
+                ).paddingOnly(top: 15, bottom: 15),
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  viewModel.submitChangeMobile();
+                },
+              ),
+
+              //提交按钮
+              MyButton(
+                onPressed: viewModel.submitChangeMobile,
+                text: S.current.submit,
+                textColor: Colors.white,
+                backgroundColor: context.appColors.btnBgDefault,
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                fontSize: 16,
+                minHeight: 50,
+                radius: 5,
+              ).marginOnly(top: 50, bottom: 30),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  /// 输入框
+  Widget _buildInputLayout(
+    BuildContext context,
+    ChangeMobileState state,
+    String key, {
+    double marginTop = 0,
+    bool? showRightIcon = false, //是否展示右侧的布局
+    Widget? rightWidget, //右侧的布局
+    TextInputType textInputType = TextInputType.text,
+    String? errorText,
+    bool obscureText = false,
+    TextInputAction textInputAction = TextInputAction.done,
+    Function? onSubmit,
+  }) {
+    return IgnoreKeyboardDismiss(
+      child: MyTextField(
+        key,
+        fillBackgroundColor: context.appColors.authFiledBG,
+        state.formData[key]!['value'],
+        hintText: state.formData[key]!['hintText'],
+        hintStyle: TextStyle(
+          color: context.appColors.authFiledHint,
+          fontSize: 15.0,
+          fontWeight: FontWeight.w500,
+        ),
+        controller: state.formData[key]!['controller'],
+        focusNode: state.formData[key]!['focusNode'],
+        margin: EdgeInsets.only(top: marginTop),
+        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
+        showDivider: false,
+        height: 44,
+        style: TextStyle(
+          color: context.appColors.authFiledText,
+          fontSize: 15.0,
+          fontWeight: FontWeight.w500,
+        ),
+        inputType: textInputType,
+        textInputAction: textInputAction,
+        onSubmit: onSubmit,
+        cursorColor: context.appColors.authFiledText,
+        obscureText: obscureText,
+        errorText: errorText,
+        showLeftIcon: true,
+        showRightIcon: showRightIcon,
+        rightWidget: rightWidget,
+      ),
+    );
+  }
+}

+ 79 - 0
packages/cpt_profile/lib/modules/change_mobile/change_mobile_state.dart

@@ -0,0 +1,79 @@
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:flutter/material.dart';
+
+class ChangeMobileState {
+  //表单的校验与数据
+  final Map<String, Map<String, dynamic>> formData;
+
+  //获取验证码的倒计时
+  bool isOldCounting;
+  int oldCountdownTime;
+
+  bool isNewCounting;
+  int newCountdownTime;
+
+  //表单的错误信息展示
+  String? oldCodeErrorText;
+  String? newCodeErrorText;
+
+  // ===================================  Begin  ↓  ===================================
+
+  ChangeMobileState({
+    Map<String, Map<String, dynamic>>? formData,
+    this.isOldCounting = false,
+    this.oldCountdownTime = 0,
+    this.isNewCounting = false,
+    this.newCountdownTime = 0,
+    this.oldCodeErrorText,
+    this.newCodeErrorText,
+  }) : formData = formData ??
+            {
+              'old': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.mobile_phone,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'old_code': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.verification_code,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'new': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.mobile_phone,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'new_code': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.verification_code,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+            };
+
+  ChangeMobileState copyWith({
+    bool? isOldCounting,
+    int? oldCountdownTime,
+    bool? isNewCounting,
+    int? newCountdownTime,
+    String? oldCodeErrorText,
+    String? newCodeErrorText,
+  }) {
+    return ChangeMobileState(
+      formData: this.formData,
+      isOldCounting: isOldCounting ?? this.isOldCounting,
+      oldCountdownTime: oldCountdownTime ?? this.oldCountdownTime,
+      isNewCounting: isNewCounting ?? this.isNewCounting,
+      newCountdownTime: newCountdownTime ?? this.newCountdownTime,
+      oldCodeErrorText: oldCodeErrorText,
+      newCodeErrorText: newCodeErrorText,
+    );
+  }
+}

+ 161 - 0
packages/cpt_profile/lib/modules/change_mobile/change_mobile_view_model.dart

@@ -0,0 +1,161 @@
+import 'dart:async';
+
+import 'package:cpt_profile/modules/setting/setting_page.dart';
+import 'package:flutter/material.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/util.dart';
+
+import 'change_mobile_state.dart';
+
+part 'change_mobile_view_model.g.dart';
+
+@riverpod
+class ChangeMobileViewModel extends _$ChangeMobileViewModel {
+  @override
+  ChangeMobileState build() {
+    final state = ChangeMobileState();
+    initListener(state);
+    ref.onDispose(() {
+      onDispose(state);
+    });
+    return state;
+  }
+
+  /// 提交表单修改电话号码
+  void submitChangeMobile() {
+    state = state.copyWith(oldCodeErrorText: null, newCodeErrorText: null);
+
+    final FocusNode oldMobileFocusNode = state.formData['old']!['focusNode'];
+    final FocusNode oldCodeFocusNode = state.formData['old_code']!['focusNode'];
+    final FocusNode newMobileFocusNode = state.formData['new']!['focusNode'];
+    final FocusNode newCodeFocusNode = state.formData['new_code']!['focusNode'];
+
+    final TextEditingController oldMobileController = state.formData['old']!['controller'];
+    final TextEditingController oldCodeController = state.formData['old_code']!['controller'];
+    final TextEditingController newMobileController = state.formData['new']!['controller'];
+    final TextEditingController newCodeController = state.formData['new_code']!['controller'];
+
+    oldMobileFocusNode.unfocus();
+    oldCodeFocusNode.unfocus();
+    newMobileFocusNode.unfocus();
+    newCodeFocusNode.unfocus();
+
+    final oldMobile = oldMobileController.text;
+    final oldCode = oldCodeController.text;
+    final newMobile = newMobileController.text;
+    final newCode = newCodeController.text;
+
+    Log.d('当前待提交的 oldMobile:$oldMobile oldCode:$oldCode newMobile:$newMobile newCode:$newCode');
+
+    if (Utils.isEmpty(oldMobile)) {
+      ToastEngine.show("Enter Old Mobile Phone");
+      return;
+    }
+
+    if (Utils.isEmpty(oldCode)) {
+      state = state.copyWith(oldCodeErrorText: "Old mobile phone verification code cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(newMobile)) {
+      ToastEngine.show("Enter New Mobile Phone");
+      return;
+    }
+
+    if (Utils.isEmpty(newCode)) {
+      state = state.copyWith(newCodeErrorText: "New mobile phone verification code cannot be empty!");
+      return;
+    }
+
+    //执行密码登录
+    ToastEngine.show('准备执行请求发送验证码 oldMobile:$oldMobile oldCode:$oldCode newMobile:$newMobile newCode:$newCode');
+
+    //返回设置页面
+    SettingPage.startInstance();
+  }
+
+  //展示发送验证码弹窗
+  showVerifyCodedDialog(bool isNewMobile) {
+    if (isNewMobile) {
+      _startNewCountDown();
+    } else {
+      _startOldCountDown();
+    }
+  }
+
+  Timer? oldCountdownTimer;
+  Timer? newCountdownTimer;
+
+  /// 开启倒计时 - Old
+  void _startOldCountDown() {
+    //60秒倒计时
+    state = state.copyWith(isOldCounting: true, oldCountdownTime: 60);
+
+    //每秒的倒计时
+    oldCountdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
+      int time = state.oldCountdownTime;
+      Log.d('Old倒计时-->$time');
+      if (time > 0) {
+        time--;
+        state = state.copyWith(isOldCounting: true, oldCountdownTime: time);
+      } else {
+        state = state.copyWith(isOldCounting: false, oldCountdownTime: 0);
+        oldCountdownTimer?.cancel(); // 取消计时器
+      }
+    });
+  }
+
+  /// 开启倒计时 - New
+  void _startNewCountDown() {
+    //60秒倒计时
+    state = state.copyWith(isNewCounting: true, newCountdownTime: 60);
+
+    //每秒的倒计时
+    newCountdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
+      int time = state.newCountdownTime;
+      Log.d('Old倒计时-->$time');
+      if (time > 0) {
+        time--;
+        state = state.copyWith(isNewCounting: true, newCountdownTime: time);
+      } else {
+        state = state.copyWith(isNewCounting: false, newCountdownTime: 0);
+        newCountdownTimer?.cancel(); // 取消计时器
+      }
+    });
+  }
+
+  //初始化监听
+  void initListener(ChangeMobileState initState) {
+    final FocusNode oldCodeFocusNode = initState.formData['old_code']!['focusNode'];
+    final FocusNode newCodeFocusNode = initState.formData['new_code']!['focusNode'];
+
+    oldCodeFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (oldCodeFocusNode.hasFocus) {
+        state = state.copyWith(oldCodeErrorText: null);
+      }
+    });
+
+    newCodeFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (newCodeFocusNode.hasFocus) {
+        state = state.copyWith(newCodeErrorText: null);
+      }
+    });
+  }
+
+  //销毁资源
+  void onDispose(ChangeMobileState initState) {
+    final FocusNode oldCodeFocusNode = initState.formData['old_code']!['focusNode'];
+    final FocusNode newCodeFocusNode = initState.formData['new_code']!['focusNode'];
+    oldCodeFocusNode.dispose();
+    newCodeFocusNode.dispose();
+
+    oldCountdownTimer?.cancel();
+    newCountdownTimer?.cancel();
+
+    Log.d("ChangeMobileViewModel 销毁 onDispose");
+  }
+}

+ 27 - 0
packages/cpt_profile/lib/modules/change_mobile/change_mobile_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'change_mobile_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$changeMobileViewModelHash() =>
+    r'b7c9fe3ec47e96841ff1389e6b13f83be7d7fe9a';
+
+/// See also [ChangeMobileViewModel].
+@ProviderFor(ChangeMobileViewModel)
+final changeMobileViewModelProvider = AutoDisposeNotifierProvider<
+    ChangeMobileViewModel, ChangeMobileState>.internal(
+  ChangeMobileViewModel.new,
+  name: r'changeMobileViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$changeMobileViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$ChangeMobileViewModel = AutoDisposeNotifier<ChangeMobileState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 1 - 1
packages/cpt_profile/lib/modules/profile_edit/page/Profile_edit_page.dart

@@ -1,4 +1,4 @@
-import 'package:cpt_profile/modules/profile_edit/vm/profile_edit_view_model.dart';
+import 'package:cpt_profile/modules/profile_edit/profile_edit_view_model.dart';
 import 'package:cpt_profile/router/page/profile_page_router.dart';
 import 'package:flutter/material.dart';
 import 'package:auto_route/auto_route.dart';

packages/cpt_profile/lib/modules/profile_edit/vm/profile_edit_view_model.dart → packages/cpt_profile/lib/modules/profile_edit/profile_edit_view_model.dart


packages/cpt_profile/lib/modules/profile_edit/vm/profile_edit_view_model.g.dart → packages/cpt_profile/lib/modules/profile_edit/profile_edit_view_model.g.dart


+ 282 - 0
packages/cpt_profile/lib/modules/reset_password/reset_password_page.dart

@@ -0,0 +1,282 @@
+
+import 'package:cpt_profile/modules/reset_password/reset_password_state.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_field.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../router/page/profile_page_router.dart';
+import 'reset_password_view_model.dart';
+
+@RoutePage()
+class ResetPasswordPage extends HookConsumerWidget {
+  const ResetPasswordPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(const ResetPasswordPageRoute());
+    } else {
+      appRouter.push(const ResetPasswordPageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.watch(resetPasswordViewModelProvider.notifier);
+    final state = ref.watch(resetPasswordViewModelProvider);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, S.current.reset_password),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: SingleChildScrollView(
+        scrollDirection: Axis.vertical,
+        physics: const BouncingScrollPhysics(),
+        child: Container(
+          margin: const EdgeInsets.symmetric(horizontal: 15),
+          width: double.infinity,
+          child: Column(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              //手机号码
+              MyTextView(
+                S.current.mobile_phone,
+                fontSize: 16.5,
+                marginTop: 38,
+                marginBottom: 15,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              //电话号码
+              Row(
+                mainAxisSize: MainAxisSize.max,
+                crossAxisAlignment: CrossAxisAlignment.center,
+                children: [
+                  const MyAssetImage(
+                    Assets.authCountrySg,
+                    width: 45,
+                    height: 30,
+                  ),
+                  MyTextView(
+                    "+65",
+                    textColor: context.appColors.textBlack,
+                    fontSize: 18.5,
+                    marginLeft: 15,
+                    marginRight: 12,
+                    isFontMedium: true,
+                  ),
+                  //电话输入框
+                  _buildInputLayout(
+                    context,
+                    state,
+                    "phone",
+                    textInputType: TextInputType.number,
+                    textInputAction: TextInputAction.next,
+                    onSubmit: (formKey, value) {
+                      state.formData[formKey]!['focusNode'].unfocus();
+                      FocusScope.of(context).requestFocus(state.formData['code']!['focusNode']);
+                    },
+                  ).expanded(),
+                ],
+              ),
+
+              //手机的验证码
+              MyTextView(
+                S.current.verification_code,
+                fontSize: 16.5,
+                marginTop: 14,
+                marginBottom: 16,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              // 表单 - 电话号码验证码
+              _buildInputLayout(
+                context,
+                state,
+                "code",
+                textInputType: TextInputType.number,
+                textInputAction: TextInputAction.next,
+                errorText: state.codeErrorText,
+                showRightIcon: true,
+                rightWidget: MyTextView(
+                  state.isCounting ? "${state.countdownTime} s" : S.current.get_code,
+                  textAlign: TextAlign.center,
+                  textColor: context.appColors.textPrimary,
+                  fontSize: 15,
+                  paddingRight: 5,
+                  isFontMedium: true,
+                  onClick: state.isCounting ? null : () => viewModel.showVerifyCodedDialog(),
+                ).paddingOnly(top: 15, bottom: 15),
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  FocusScope.of(context).requestFocus(state.formData['password']!['focusNode']);
+                },
+              ),
+
+              //新密码
+              MyTextView(
+                S.current.new_password,
+                fontSize: 16.5,
+                marginTop: 14,
+                marginBottom: 16,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              // 表单 - 新密码
+              _buildInputLayout(
+                context,
+                state,
+                "password",
+                obscureText: !state.pwdVisibility,
+                errorText: state.passwordErrorText,
+                textInputAction: TextInputAction.next,
+                showRightIcon: true,
+                rightWidget: IconButton(
+                  highlightColor: Colors.transparent,
+                  splashColor: Colors.transparent,
+                  icon: state.pwdVisibility
+                      ? const MyAssetImage(
+                    Assets.authPasswordHide,
+                    width: 22.5,
+                    height: 16.5,
+                  )
+                      : const MyAssetImage(
+                    Assets.authPasswordShow,
+                    width: 22.5,
+                    height: 16.5,
+                  ),
+                  onPressed: () {
+                    viewModel.switchPwdVisibility();
+                  },
+                ),
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  FocusScope.of(context).requestFocus(state.formData['confirm_password']!['focusNode']);
+                },
+              ),
+
+              //重复密码
+              MyTextView(
+                S.current.confirm_new_password,
+                fontSize: 16.5,
+                marginTop: 14,
+                marginBottom: 16,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              // 表单 - 确认密码
+              _buildInputLayout(
+                context,
+                state,
+                "confirm_password",
+                obscureText: !state.confirmPwdVisibility,
+                errorText: state.confirmPasswordErrorText,
+                showRightIcon: true,
+                rightWidget: IconButton(
+                  highlightColor: Colors.transparent,
+                  splashColor: Colors.transparent,
+                  icon: state.confirmPwdVisibility
+                      ? const MyAssetImage(
+                    Assets.authPasswordHide,
+                    width: 22.5,
+                    height: 16.5,
+                  )
+                      : const MyAssetImage(
+                    Assets.authPasswordShow,
+                    width: 22.5,
+                    height: 16.5,
+                  ),
+                  onPressed: () {
+                    viewModel.switchConfirmPwdVisibility();
+                  },
+                ),
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  viewModel.submitResetPassword();
+                },
+              ),
+
+              //提交按钮
+              MyButton(
+                onPressed: viewModel.submitResetPassword,
+                text: S.current.submit,
+                textColor: Colors.white,
+                backgroundColor: context.appColors.btnBgDefault,
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                fontSize: 16,
+                minHeight: 50,
+                radius: 5,
+              ).marginOnly(top: 50, bottom: 30),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  /// 输入框
+  Widget _buildInputLayout(
+      BuildContext context,
+      ResetPasswordState state,
+      String key, {
+        double marginTop = 0,
+        bool? showRightIcon = false, //是否展示右侧的布局
+        Widget? rightWidget, //右侧的布局
+        TextInputType textInputType = TextInputType.text,
+        String? errorText,
+        bool obscureText = false,
+        TextInputAction textInputAction = TextInputAction.done,
+        Function? onSubmit,
+      }) {
+    return IgnoreKeyboardDismiss(
+      child: MyTextField(
+        key,
+        fillBackgroundColor: context.appColors.authFiledBG,
+        state.formData[key]!['value'],
+        hintText: state.formData[key]!['hintText'],
+        hintStyle: TextStyle(
+          color: context.appColors.authFiledHint,
+          fontSize: 15.0,
+          fontWeight: FontWeight.w500,
+        ),
+        controller: state.formData[key]!['controller'],
+        focusNode: state.formData[key]!['focusNode'],
+        margin: EdgeInsets.only(top: marginTop),
+        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
+        showDivider: false,
+        height: 44,
+        style: TextStyle(
+          color: context.appColors.authFiledText,
+          fontSize: 15.0,
+          fontWeight: FontWeight.w500,
+        ),
+        inputType: textInputType,
+        textInputAction: textInputAction,
+        onSubmit: onSubmit,
+        cursorColor: context.appColors.authFiledText,
+        obscureText: obscureText,
+        errorText: errorText,
+        showLeftIcon: true,
+        showRightIcon: showRightIcon,
+        rightWidget: rightWidget,
+      ),
+    );
+  }
+
+}

+ 85 - 0
packages/cpt_profile/lib/modules/reset_password/reset_password_state.dart

@@ -0,0 +1,85 @@
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:flutter/material.dart';
+
+class ResetPasswordState{
+  //表单的校验与数据
+  final Map<String, Map<String, dynamic>> formData;
+
+  //表单的错误信息展示
+  String? codeErrorText;
+  String? passwordErrorText;
+  String? confirmPasswordErrorText;
+
+  //是否明文展示密码
+  bool pwdVisibility;
+
+  bool confirmPwdVisibility;
+
+  //获取验证码的倒计时
+  bool isCounting;
+  int countdownTime;
+
+  // ===================================  Begin  ↓  ===================================
+
+  ResetPasswordState({
+    Map<String, Map<String, dynamic>>? formData,
+    this.pwdVisibility = false,
+    this.confirmPwdVisibility = false,
+    this.isCounting = false,
+    this.countdownTime = 0,
+    this.codeErrorText,
+    this.passwordErrorText,
+    this.confirmPasswordErrorText,
+  }) : formData = formData ??
+      {
+        'phone': {
+          'value': '',
+          'controller': TextEditingController(),
+          'hintText': S.current.mobile_phone,
+          'focusNode': FocusNode(),
+          'obsecure': false,
+        },
+        'code': {
+          'value': '',
+          'controller': TextEditingController(),
+          'hintText': S.current.verification_code,
+          'focusNode': FocusNode(),
+          'obsecure': false,
+        },
+        'password': {
+          'value': '',
+          'controller': TextEditingController(),
+          'hintText': S.current.password_format,
+          'focusNode': FocusNode(),
+          'obsecure': true,
+        },
+        'confirm_password': {
+          'value': '',
+          'controller': TextEditingController(),
+          'hintText': S.current.password_format,
+          'focusNode': FocusNode(),
+          'obsecure': true,
+        },
+      };
+
+  ResetPasswordState copyWith({
+    String? codeErrorText,
+    String? passwordErrorText,
+    String? confirmPasswordErrorText,
+    bool? pwdVisibility,
+    bool? confirmPwdVisibility,
+    bool? isCounting,
+    int? countdownTime,
+  }) {
+    return ResetPasswordState(
+      formData: this.formData,
+      pwdVisibility: pwdVisibility ?? this.pwdVisibility,
+      confirmPwdVisibility: confirmPwdVisibility ?? this.confirmPwdVisibility,
+      isCounting: isCounting ?? this.isCounting,
+      countdownTime: countdownTime ?? this.countdownTime,
+      codeErrorText: codeErrorText,
+      passwordErrorText: passwordErrorText,
+      confirmPasswordErrorText: confirmPasswordErrorText,
+    );
+  }
+}

+ 158 - 0
packages/cpt_profile/lib/modules/reset_password/reset_password_view_model.dart

@@ -0,0 +1,158 @@
+import 'dart:async';
+
+import 'package:cpt_profile/modules/reset_password/reset_password_state.dart';
+import 'package:flutter/material.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/util.dart';
+
+part 'reset_password_view_model.g.dart';
+
+@riverpod
+class ResetPasswordViewModel extends _$ResetPasswordViewModel {
+  @override
+  ResetPasswordState build() {
+    final state = ResetPasswordState();
+    initListener(state);
+    ref.onDispose(() {
+      onDispose(state);
+    });
+    return state;
+  }
+
+  /// 提交重置密码请求
+  void submitResetPassword() {
+    state = state.copyWith(codeErrorText: null, passwordErrorText: null, confirmPasswordErrorText: null);
+
+    final FocusNode phoneFocusNode = state.formData['phone']!['focusNode'];
+    final FocusNode codeFocusNode = state.formData['code']!['focusNode'];
+    final FocusNode passwordFocusNode = state.formData['password']!['focusNode'];
+    final FocusNode confirmPasswordFocusNode = state.formData['confirm_password']!['focusNode'];
+
+    final TextEditingController phoneController = state.formData['phone']!['controller'];
+    final TextEditingController codeController = state.formData['code']!['controller'];
+    final TextEditingController passwordController = state.formData['password']!['controller'];
+    final TextEditingController confirmPasswordController = state.formData['confirm_password']!['controller'];
+
+    phoneFocusNode.unfocus();
+    codeFocusNode.unfocus();
+    passwordFocusNode.unfocus();
+    confirmPasswordFocusNode.unfocus();
+
+    final phone = phoneController.text;
+    final code = codeController.text;
+    final password = passwordController.text;
+    final confirmPassword = confirmPasswordController.text;
+
+    Log.d('当前待提交的 phone:$phone code:$code password:$password confirmPassword:$confirmPassword');
+
+    if (Utils.isEmpty(phone)) {
+      ToastEngine.show("Enter Mobile Phone");
+      return;
+    }
+
+    if (Utils.isEmpty(code)) {
+      state = state.copyWith(codeErrorText: "Verification Code cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(password)) {
+      state = state.copyWith(passwordErrorText: "Password cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(confirmPassword)) {
+      state = state.copyWith(confirmPasswordErrorText: "Confirm Password cannot be empty!");
+      return;
+    }
+
+    if (confirmPassword != password) {
+      state = state.copyWith(confirmPasswordErrorText: "Password mismatch, please check password");
+      return;
+    }
+
+    //执行密码登录
+    ToastEngine.show('准备执行请求发送验证码 phone:$phone code:$code password:$password confirmPassword:$confirmPassword');
+
+    //页面返回
+    appRouter.maybePop();
+  }
+
+  //切换隐藏显示密码
+  void switchPwdVisibility() {
+    state = state.copyWith(pwdVisibility: !state.pwdVisibility);
+  }
+
+  void switchConfirmPwdVisibility() {
+    state = state.copyWith(confirmPwdVisibility: !state.confirmPwdVisibility);
+  }
+
+  void showVerifyCodedDialog() {
+    _startCountDown();
+  }
+
+  Timer? countdownTimer;
+
+  /// 开启倒计时
+  void _startCountDown() {
+    //60秒倒计时
+    state = state.copyWith(isCounting: true, countdownTime: 60);
+
+    //每秒的倒计时
+    countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
+      int time = state.countdownTime;
+      Log.d('倒计时-->$time');
+      if (time > 0) {
+        time--;
+        state = state.copyWith(isCounting: true, countdownTime: time);
+      } else {
+        state = state.copyWith(isCounting: false, countdownTime: 0);
+        countdownTimer?.cancel(); // 取消计时器
+      }
+    });
+  }
+
+  //初始化监听
+  void initListener(ResetPasswordState initState) {
+    final FocusNode codeFocusNode = initState.formData['code']!['focusNode'];
+    final FocusNode passwordFocusNode = initState.formData['password']!['focusNode'];
+    final FocusNode confirmPasswordFocusNode = initState.formData['confirm_password']!['focusNode'];
+
+    codeFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (codeFocusNode.hasFocus) {
+        state = state.copyWith(codeErrorText: null);
+      }
+    });
+
+    passwordFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (passwordFocusNode.hasFocus) {
+        state = state.copyWith(passwordErrorText: null);
+      }
+    });
+
+    confirmPasswordFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (confirmPasswordFocusNode.hasFocus) {
+        state = state.copyWith(confirmPasswordErrorText: null);
+      }
+    });
+  }
+
+  //销毁资源
+  void onDispose(ResetPasswordState initState) {
+    final FocusNode codeFocusNode = initState.formData['code']!['focusNode'];
+    final FocusNode passwordFocusNode = initState.formData['password']!['focusNode'];
+    final FocusNode confirmPasswordFocusNode = initState.formData['confirm_password']!['focusNode'];
+    codeFocusNode.dispose();
+    passwordFocusNode.dispose();
+    confirmPasswordFocusNode.dispose();
+
+    countdownTimer?.cancel();
+
+    Log.d("ForgotVerifyViewModel 销毁 onDispose");
+  }
+}

+ 27 - 0
packages/cpt_profile/lib/modules/reset_password/reset_password_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'reset_password_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$resetPasswordViewModelHash() =>
+    r'4b6b7ea5f180d679389a10da92758d39ea9f86e3';
+
+/// See also [ResetPasswordViewModel].
+@ProviderFor(ResetPasswordViewModel)
+final resetPasswordViewModelProvider = AutoDisposeNotifierProvider<
+    ResetPasswordViewModel, ResetPasswordState>.internal(
+  ResetPasswordViewModel.new,
+  name: r'resetPasswordViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$resetPasswordViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$ResetPasswordViewModel = AutoDisposeNotifier<ResetPasswordState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 133 - 0
packages/cpt_profile/lib/modules/setting/dialog/account_deactivation_dialog.dart

@@ -0,0 +1,133 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+class AccountDeactivationDialog extends StatelessWidget {
+  VoidCallback confirmAction;
+
+  AccountDeactivationDialog({
+    required this.confirmAction,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.center,
+      mainAxisAlignment: MainAxisAlignment.center,
+      children: [
+        Container(
+          width: double.infinity,
+          height: 55,
+          decoration: BoxDecoration(
+            color: context.appColors.btnBgDefault,
+            borderRadius: const BorderRadius.only(
+              topRight: Radius.circular(15),
+              topLeft: Radius.circular(15),
+            ),
+          ),
+          child: Row(
+            children: [
+              const SizedBox(width: 45),
+              MyTextView(
+                S.current.account_deactivation,
+                fontSize: 18,
+                textAlign: TextAlign.center,
+                isFontMedium: true,
+                textColor: Colors.white,
+              ).expanded(),
+              const MyAssetImage(
+                Assets.baseServiceDialogDeleteIcon,
+                width: 25,
+                height: 25.5,
+              ).onTap(() {
+                onCancel();
+              }, padding: 10)
+            ],
+          ),
+        ),
+        Container(
+          width: double.infinity,
+          padding: const EdgeInsets.only(top: 22),
+          decoration: BoxDecoration(
+            color: context.appColors.whiteSecondBG,
+            borderRadius: const BorderRadius.only(
+              bottomLeft: Radius.circular(15),
+              bottomRight: Radius.circular(15),
+            ),
+          ),
+          child: Column(
+            children: [
+              const MyAssetImage(Assets.mainAccountDeactivationImg, width: 296.5, height: 158.5),
+
+              MyTextView(
+                S.current.account_deactivate_alert,
+                textAlign: TextAlign.center,
+                textColor: context.appColors.textBlack,
+                marginTop: 24,
+                paddingLeft: 30,
+                paddingRight: 30,
+                fontSize: 15,
+                isFontMedium: true,
+              ),
+
+              Row(
+                children: [
+                  const SizedBox(width: 18),
+                  Expanded(
+                      flex: 1,
+                      child: InkWell(
+                        onTap: () {
+                          onCancel();
+                        },
+                        child: MyTextView(
+                          S.current.no,
+                          fontSize: 16,
+                          isFontMedium: true,
+                          paddingTop: 13,
+                          marginRight: 15,
+                          paddingBottom: 13,
+                          textAlign: TextAlign.center,
+                          textColor: Colors.white,
+                          backgroundColor: context.appColors.orangeBG,
+                          cornerRadius: 7,
+                        ),
+                      )),
+                  Expanded(
+                      flex: 1,
+                      child: InkWell(
+                        onTap: () async {
+                          onCancel();
+                          confirmAction();
+                        },
+                        child: MyTextView(
+                          S.current.yes,
+                          fontSize: 16,
+                          paddingTop: 13,
+                          paddingBottom: 13,
+                          isFontMedium: true,
+                          textAlign: TextAlign.center,
+                          textColor: Colors.white,
+                          backgroundColor: context.appColors.btnBgDefault,
+                          cornerRadius: 7,
+                        ),
+                      )),
+                  const SizedBox(width: 18),
+                ],
+              ).marginOnly(bottom: 30, top: 28),
+            ],
+          ),
+        ),
+      ],
+    ).constrained(width: 340);
+  }
+
+//取消弹框
+  void onCancel() async {
+    SmartDialog.dismiss();
+  }
+}

+ 105 - 0
packages/cpt_profile/lib/modules/setting/setting_page.dart

@@ -0,0 +1,105 @@
+import 'package:cpt_profile/modules/setting/setting_view_model.dart';
+import 'package:cpt_profile/router/page/profile_page_router.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:plugin_basic/provider/app_config/app_config_service.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/shatter/setting_item_container.dart';
+
+@RoutePage()
+class SettingPage extends HookConsumerWidget {
+  const SettingPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.navigate(const SettingPageRoute());
+    } else {
+      appRouter.navigate(const SettingPageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.watch(settingViewModelProvider.notifier);
+    final state = ref.watch(settingViewModelProvider);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, S.current.settings, backgroundColor: context.appColors.whiteBG),
+      backgroundColor: context.appColors.backgroundDark,
+      body: SingleChildScrollView(
+        scrollDirection: Axis.vertical,
+        physics: const BouncingScrollPhysics(),
+        child: Column(
+          mainAxisSize: MainAxisSize.max,
+          crossAxisAlignment: CrossAxisAlignment.center,
+          children: [
+            const SizedBox(height: 10),
+
+            //允许通知
+            SettingItemContainer(
+              title: S.current.enable_notification,
+              rightWidget: CupertinoSwitch(
+                activeColor: context.appColors.textPrimary,
+                value: state.enbaleNofitication,
+                onChanged: (value) {
+                  viewModel.changeEnableNotification(value);
+                },
+              ),
+            ),
+
+            //修改手机号码
+            SettingItemContainer(title: S.current.change_mobile_phone).onTap(viewModel.gotoChangeMobilePage),
+
+            //重置密码
+            SettingItemContainer(title: S.current.reset_password).onTap(viewModel.gotoResetPasswordPage),
+
+            //隐私协议
+            SettingItemContainer(title: S.current.privacy_policy),
+
+            //服务条款
+            SettingItemContainer(title: S.current.terms_conditions),
+
+            //删除账号
+            SettingItemContainer(title: S.current.account_deactivation).onTap(viewModel.doDeleteAccount),
+
+            //评价我们
+            SettingItemContainer(title: S.current.rate_us),
+
+            //版本号
+            SettingItemContainer(
+              title: S.current.version_no,
+              rightWidget: MyTextView(
+                AppConfigService.getState().version,
+                textColor: context.appColors.textPrimary,
+                fontSize: 15,
+                isFontMedium: true,
+              ),
+            ),
+
+            //提交按钮
+            MyButton(
+              onPressed: viewModel.doLogout,
+              text: S.current.logout,
+              textColor: Colors.white,
+              backgroundColor: context.appColors.btnBgDefault,
+              fontWeight: FontWeight.w500,
+              type: ClickType.throttle,
+              fontSize: 16,
+              minHeight: 50,
+              radius: 5,
+            ).marginOnly(top: 22, bottom: 25, left: 15, right: 15),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 15 - 0
packages/cpt_profile/lib/modules/setting/setting_state.dart

@@ -0,0 +1,15 @@
+class SettingState {
+  final bool enbaleNofitication;  //是否允许通知
+
+  const SettingState({
+    this.enbaleNofitication = true,
+  });
+
+  SettingState copyWith({
+    bool? enbaleNofitication,
+  }) {
+    return SettingState(
+      enbaleNofitication: enbaleNofitication ?? this.enbaleNofitication,
+    );
+  }
+}

+ 53 - 0
packages/cpt_profile/lib/modules/setting/setting_view_model.dart

@@ -0,0 +1,53 @@
+import 'package:cpt_profile/modules/change_mobile/change_mobile_page.dart';
+import 'package:cpt_profile/modules/reset_password/reset_password_page.dart';
+import 'package:cpt_profile/modules/setting/dialog/account_deactivation_dialog.dart';
+import 'package:cpt_profile/modules/setting/setting_state.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:plugin_platform/engine/dialog/dialog_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:widgets/dialog/app_default_dialog.dart';
+
+part 'setting_view_model.g.dart';
+
+@riverpod
+class SettingViewModel extends _$SettingViewModel {
+  @override
+  SettingState build() {
+    return SettingState();
+  }
+
+  void changeEnableNotification(bool value) {
+    state = state.copyWith(enbaleNofitication: value);
+  }
+
+  //去修改手机号码的页面
+  void gotoChangeMobilePage() {
+    ChangeMobilePage.startInstance();
+  }
+
+  void gotoResetPasswordPage() {
+    ResetPasswordPage.startInstance();
+  }
+
+  // 退出登录
+  void doLogout() {
+    DialogEngine.show(
+        widget: AppDefaultDialog(
+      message: "Are you sure you want to logout?",
+      confirmAction: () {
+        ToastEngine.show("点击了确定");
+      },
+    ));
+  }
+
+  //展示删除账号的提示弹窗
+  void doDeleteAccount() {
+    DialogEngine.show(widget: AccountDeactivationDialog(
+      confirmAction: () {
+        ToastEngine.show("点击了确定");
+      },
+    ));
+  }
+
+}

+ 0 - 0
packages/cpt_profile/lib/modules/setting/setting_view_model.g.dart


Некоторые файлы не были показаны из-за большого количества измененных файлов