Преглед изворни кода

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

“shanwenxin” пре 3 месеци
родитељ
комит
3d7251ca88
100 измењених фајлова са 6294 додато и 2968 уклоњено
  1. 2 5
      app/lib/modules/splash/vm/splash_view_model.dart
  2. 11 0
      packages/cpt_auth/lib/modules/auth_login/auth_login_page.dart
  3. 21 7
      packages/cpt_auth/lib/modules/auth_login/auth_login_view_model.dart
  4. 1 1
      packages/cpt_auth/lib/modules/auth_login/auth_login_view_model.g.dart
  5. 63 110
      packages/cpt_auth/lib/modules/select_estate/select_estate_page.dart
  6. 0 3
      packages/cpt_auth/lib/modules/select_estate/select_estate_view_model.dart
  7. 1 1
      packages/cpt_auth/lib/modules/select_estate/select_estate_view_model.g.dart
  8. 1 1
      packages/cpt_auth/lib/modules/select_role/select_role_page.dart
  9. 151 199
      packages/cpt_auth/lib/modules/select_unit/select_unit_page.dart
  10. 0 4
      packages/cpt_auth/lib/modules/select_unit/select_unit_view_model.dart
  11. 1 1
      packages/cpt_auth/lib/modules/select_unit/select_unit_view_model.g.dart
  12. 23 7
      packages/cpt_auth/lib/modules/sign_up/sign_up_view_model.dart
  13. 1 1
      packages/cpt_auth/lib/modules/sign_up/sign_up_view_model.g.dart
  14. 0 92
      packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_full_screen.dart
  15. 79 11
      packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_page.dart
  16. 0 90
      packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_small_screen.dart
  17. 3 1
      packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_view_model.dart
  18. 1 1
      packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_view_model.g.dart
  19. 80 13
      packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_page.dart
  20. 26 4
      packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_state.dart
  21. 102 4
      packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_view_model.dart
  22. 1 1
      packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_view_model.g.dart
  23. 53 100
      packages/cpt_auth/lib/modules/tenant_doc/tenant_doc_page.dart
  24. 0 4
      packages/cpt_auth/lib/modules/tenant_doc/tenant_doc_view_model.dart
  25. 3 3
      packages/cpt_auth/lib/router/component/auth_component_service.dart
  26. 75 5
      packages/cpt_auth/lib/router/page/auth_page_router.gr.dart
  27. 139 0
      packages/cpt_community/lib/components/comments_dialog.dart
  28. 241 0
      packages/cpt_community/lib/components/garage_card.dart
  29. 0 69
      packages/cpt_community/lib/components/garage_card_content.dart
  30. 0 111
      packages/cpt_community/lib/components/garage_card_footer.dart
  31. 0 149
      packages/cpt_community/lib/components/garage_card_header.dart
  32. 4 3
      packages/cpt_community/lib/components/newfeed_card_header.dart
  33. 115 45
      packages/cpt_community/lib/components/newsfeed_card_content.dart
  34. 48 26
      packages/cpt_community/lib/components/newsfeed_card_footer.dart
  35. 217 179
      packages/cpt_community/lib/modules/community/community_page.dart
  36. 7 0
      packages/cpt_community/lib/modules/community/community_pageview_idx_data.dart
  37. 37 9
      packages/cpt_community/lib/modules/community/community_state.dart
  38. 156 19
      packages/cpt_community/lib/modules/community/community_vm.dart
  39. 1 1
      packages/cpt_community/lib/modules/community/community_vm.g.dart
  40. 2 2
      packages/cpt_community/lib/modules/community/customSilverHeaderTabs.dart
  41. 156 5
      packages/cpt_community/lib/modules/community/following/following_page.dart
  42. 39 14
      packages/cpt_community/lib/modules/community/following/following_state.dart
  43. 192 4
      packages/cpt_community/lib/modules/community/following/following_vm.dart
  44. 1 1
      packages/cpt_community/lib/modules/community/following/following_vm.g.dart
  45. 141 17
      packages/cpt_community/lib/modules/community/foryou/foryou_page.dart
  46. 22 18
      packages/cpt_community/lib/modules/community/foryou/foryou_state.dart
  47. 142 46
      packages/cpt_community/lib/modules/community/foryou/foryou_vm.dart
  48. 1 1
      packages/cpt_community/lib/modules/community/foryou/foryou_vm.g.dart
  49. 133 99
      packages/cpt_community/lib/modules/community/news/news_page.dart
  50. 23 18
      packages/cpt_community/lib/modules/community/news/news_state.dart
  51. 196 123
      packages/cpt_community/lib/modules/community/news/news_vm.dart
  52. 1 1
      packages/cpt_community/lib/modules/community/news/news_vm.g.dart
  53. 79 0
      packages/cpt_community/lib/modules/community/newsfeed_detail/comments_input.dart
  54. 348 96
      packages/cpt_community/lib/modules/community/newsfeed_detail/newsfeed_detail_page.dart
  55. 56 6
      packages/cpt_community/lib/modules/community/newsfeed_detail/newsfeed_detail_state.dart
  56. 239 18
      packages/cpt_community/lib/modules/community/newsfeed_detail/newsfeed_detail_vm.dart
  57. 1 1
      packages/cpt_community/lib/modules/community/newsfeed_detail/newsfeed_detail_vm.g.dart
  58. 0 38
      packages/cpt_community/lib/modules/community/newsfeed_myposts/newsfeed_myposts_page.dart
  59. 1 1
      packages/cpt_community/lib/modules/community/newsfeed_post/newsfeed_post_page.dart
  60. 69 14
      packages/cpt_community/lib/modules/community/newsfeed_tabs.dart
  61. 101 36
      packages/cpt_community/lib/modules/garage/for_rent/for_rent_page.dart
  62. 25 14
      packages/cpt_community/lib/modules/garage/for_rent/for_rent_state.dart
  63. 172 132
      packages/cpt_community/lib/modules/garage/for_rent/for_rent_vm.dart
  64. 1 1
      packages/cpt_community/lib/modules/garage/for_rent/for_rent_vm.g.dart
  65. 94 84
      packages/cpt_community/lib/modules/garage/for_sale/for_sale_page.dart
  66. 26 14
      packages/cpt_community/lib/modules/garage/for_sale/for_sale_state.dart
  67. 187 113
      packages/cpt_community/lib/modules/garage/for_sale/for_sale_vm.dart
  68. 1 1
      packages/cpt_community/lib/modules/garage/for_sale/for_sale_vm.g.dart
  69. 0 278
      packages/cpt_community/lib/modules/garage/garage_page.dart
  70. 0 71
      packages/cpt_community/lib/modules/garage/garage_repository.dart
  71. 0 67
      packages/cpt_community/lib/modules/garage/garage_state.dart
  72. 0 167
      packages/cpt_community/lib/modules/garage/garage_vm.dart
  73. 207 7
      packages/cpt_community/lib/modules/garage/garagesale_detail/garagesale_detail_page.dart
  74. 36 0
      packages/cpt_community/lib/modules/garage/garagesale_detail/garagesale_detail_state.dart
  75. 109 0
      packages/cpt_community/lib/modules/garage/garagesale_detail/garagesale_detail_vm.dart
  76. 27 0
      packages/cpt_community/lib/modules/garage/garagesale_detail/garagesale_detail_vm.g.dart
  77. 0 38
      packages/cpt_community/lib/modules/garage/garagesale_myposts/garagesale_myposts_page.dart
  78. 104 28
      packages/cpt_community/lib/modules/garage/garagesale_post/garagesale_post_page.dart
  79. 33 1
      packages/cpt_community/lib/modules/garage/garagesale_post/garagesale_post_state.dart
  80. 88 2
      packages/cpt_community/lib/modules/garage/garagesale_post/garagesale_post_vm.dart
  81. 1 1
      packages/cpt_community/lib/modules/garage/garagesale_post/garagesale_post_vm.g.dart
  82. 92 0
      packages/cpt_community/lib/modules/my_following/components/item_following.dart
  83. 64 0
      packages/cpt_community/lib/modules/my_following/components/search_cmp.dart
  84. 167 0
      packages/cpt_community/lib/modules/my_following/my_follow/my_follow_page.dart
  85. 39 0
      packages/cpt_community/lib/modules/my_following/my_follow/my_follow_state.dart
  86. 163 0
      packages/cpt_community/lib/modules/my_following/my_follow/my_follow_vm.dart
  87. 25 0
      packages/cpt_community/lib/modules/my_following/my_follow/my_follow_vm.g.dart
  88. 167 0
      packages/cpt_community/lib/modules/my_following/my_follower/my_follower_page.dart
  89. 39 0
      packages/cpt_community/lib/modules/my_following/my_follower/my_follower_state.dart
  90. 169 0
      packages/cpt_community/lib/modules/my_following/my_follower/my_follower_vm.dart
  91. 25 0
      packages/cpt_community/lib/modules/my_following/my_follower/my_follower_vm.g.dart
  92. 131 0
      packages/cpt_community/lib/modules/my_following/my_following_page.dart
  93. 39 0
      packages/cpt_community/lib/modules/my_following/my_following_state.dart
  94. 127 110
      packages/cpt_community/lib/modules/garage/garage_tabs.dart
  95. 90 0
      packages/cpt_community/lib/modules/my_following/my_following_vm.dart
  96. 26 0
      packages/cpt_community/lib/modules/my_following/my_following_vm.g.dart
  97. 139 0
      packages/cpt_community/lib/modules/my_posts/my_posts_forrent/my_posts_forrent_page.dart
  98. 0 0
      packages/cpt_community/lib/modules/my_posts/my_posts_forrent/my_posts_forrent_respository.dart
  99. 74 0
      packages/cpt_community/lib/modules/my_posts/my_posts_forrent/my_posts_forrent_state.dart
  100. 0 0
      packages/cpt_community/lib/modules/my_posts/my_posts_forrent/my_posts_forrent_vm.dart

+ 2 - 5
app/lib/modules/splash/vm/splash_view_model.dart

@@ -47,18 +47,15 @@ class SplashViewModel extends _$SplashViewModel {
     // String routerName = await AppChannel.getNativeRouterName();
     // Log.d('SplashController - 查询原生平台有没有保存需要跳转的子路由:$routerName');
 
-    String? token = SPUtil.getString(AppConstant.storageToken);
-    final userConfigService = UserConfigService.getInstance();
-    userConfigService.setToken(token);
+    UserConfigService.getInstance();
 
     if (UserConfigService.getState().haslogin == true) {
-      //去Attendance页面签到
       Log.d("已经登录,去首页页面");
       ComponentServiceManager().mainService.startMainPage();
     } else {
       //去登录页面
       Log.d("没有登录,去登录页面");
-      ComponentServiceManager().mainService.startMainPage();
+      ComponentServiceManager().authService.startAndPopAllLoginPage();
     }
   }
 }

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

@@ -35,6 +35,17 @@ class AuthLoginPage extends HookConsumerWidget {
     }
   }
 
+  //替换之前的页面
+  static void startAndPopAll({BuildContext? context}) {
+    if (context != null) {
+      context.router.popUntilRoot();
+      context.router.replace(const AuthLoginPageRoute());
+    } else {
+      appRouter.popUntilRoot();
+      appRouter.replace(const AuthLoginPageRoute());
+    }
+  }
+
   @override
   Widget build(BuildContext context, WidgetRef ref) {
     final viewModel = ref.read(authLoginViewModelProvider.notifier);

+ 21 - 7
packages/cpt_auth/lib/modules/auth_login/auth_login_view_model.dart

@@ -1,7 +1,12 @@
 import 'package:cpt_auth/modules/sign_up/sign_up_page.dart';
+import 'package:domain/repository/auth_repository.dart';
 import 'package:flutter/material.dart';
+import 'package:plugin_basic/constants/app_constant.dart';
+import 'package:plugin_basic/provider/user_config/user_config_service.dart';
+import 'package:plugin_platform/engine/notify/notify_engine.dart';
 import 'package:plugin_platform/engine/toast/toast_engine.dart';
 import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:router/componentRouter/component_service_manager.dart';
 import 'package:shared/utils/log_utils.dart';
 import 'package:shared/utils/util.dart';
 
@@ -12,10 +17,13 @@ part 'auth_login_view_model.g.dart';
 
 @riverpod
 class AuthLoginViewModel extends _$AuthLoginViewModel {
+  late final AuthRepository authRepository;
+
   @override
   LoginState build() {
-    final state = LoginState();
+    authRepository = ref.read(authRepositoryProvider);
 
+    final state = LoginState();
     initListener(state);
     ref.onDispose(() {
       onDispose(state);
@@ -29,7 +37,7 @@ class AuthLoginViewModel extends _$AuthLoginViewModel {
   }
 
   //执行用户的登录
-  void doLogin() {
+  void doLogin() async {
     state = state.copyWith(accountErrorText: null, passwordErrorText: null);
 
     final FocusNode accountFocusNode = state.formData['account']!['focusNode'];
@@ -43,7 +51,7 @@ class AuthLoginViewModel extends _$AuthLoginViewModel {
     final account = accountController.text;
     final password = passwordController.text;
 
-    Log.d('当前待提交的 账号:${account} password:${password}');
+    Log.d('当前待提交的 账号:$account password:$password');
 
     if (Utils.isEmpty(account)) {
       state = state.copyWith(accountErrorText: "Phone/Email cannot be empty!");
@@ -55,8 +63,15 @@ class AuthLoginViewModel extends _$AuthLoginViewModel {
       return;
     }
 
-    //执行密码登录
-    ToastEngine.show('准备执行请求 账号:${account} password:${password}');
+    final result = await authRepository.authLogin(AppConstant.countryCode + account, password);
+
+    //请求成功去首页
+    if (result.isSuccess) {
+      UserConfigService.getInstance().setToken(result.data?.token);
+      ComponentServiceManager().mainService.startMainPage();
+    } else {
+      ToastEngine.show(result.errorMsg ?? "UnKnow Error");
+    }
   }
 
   //去协议页面
@@ -75,7 +90,7 @@ class AuthLoginViewModel extends _$AuthLoginViewModel {
   }
 
   //切换同意按钮
-  void switchAgreeTerms(){
+  void switchAgreeTerms() {
     state = state.copyWith(isAgreeTerms: !state.isAgreeTerms);
   }
 
@@ -127,5 +142,4 @@ class AuthLoginViewModel extends _$AuthLoginViewModel {
     passwordController.dispose();
     Log.d("LoginViewModel 销毁 onDispose");
   }
-
 }

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

@@ -7,7 +7,7 @@ part of 'auth_login_view_model.dart';
 // **************************************************************************
 
 String _$authLoginViewModelHash() =>
-    r'45f0dfb96b894b4df90cd94bbd921c26344d3a96';
+    r'afff62eb84d983159db7ca8c4d7b1a9dde73ce08';
 
 /// See also [AuthLoginViewModel].
 @ProviderFor(AuthLoginViewModel)

+ 63 - 110
packages/cpt_auth/lib/modules/select_estate/select_estate_page.dart

@@ -4,10 +4,8 @@ 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';
@@ -32,121 +30,76 @@ class SelectEstatePage extends HookConsumerWidget {
     }
   }
 
-  // 为需要测量的控件创建 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),
+      appBar: MyAppBar.appBar(context, S.current.yy_home_accounts),
       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),
-            ],
-          ),
+      body: Container(
+        padding: const EdgeInsets.symmetric(horizontal: 38),
+        width: double.infinity,
+        child: Column(
+          mainAxisSize: MainAxisSize.max,
+          crossAxisAlignment: CrossAxisAlignment.center,
+          children: [
+            SingleChildScrollView(
+                scrollDirection: Axis.vertical,
+                physics: const BouncingScrollPhysics(),
+                child: Column(
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  children: [
+                    //顶部图片
+                    const MyAssetImage(
+                      Assets.authChooseEstateBuilding,
+                      width: 267,
+                      height: 158,
+                    ).marginOnly(top: 28, bottom: 38),
+
+                    MyTextView(
+                      S.current.estate_or_building_name,
+                      fontSize: 23,
+                      marginBottom: 20,
+                      textAlign: TextAlign.center,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+
+                    //输入资产的名称
+                    _buildInputLayout(
+                      context,
+                      state,
+                      "estate",
+                      textInputAction: TextInputAction.done,
+                      onSubmit: (formKey, value) {
+                        state.formData[formKey]!['focusNode'].unfocus();
+                      },
+                    ),
+
+                    MyTextView(
+                      S.current.estate_name_desc,
+                      fontSize: 15,
+                      marginTop: 19,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+                  ],
+                )).expanded(),
+
+            MyButton(
+              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: 50, bottom: 50),
+          ],
         ),
       ),
     );

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

@@ -18,7 +18,4 @@ class SelectEstateViewModel extends _$SelectEstateViewModel {
     SelectUnitPage.startInstance();
   }
 
-  void setRemainingSpace(double remainingSpace) {
-    state = state.copyWith(remainingSpace: remainingSpace);
-  }
 }

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

@@ -7,7 +7,7 @@ part of 'select_estate_view_model.dart';
 // **************************************************************************
 
 String _$selectEstateViewModelHash() =>
-    r'44d0829cc1f4d48b2fa0991838a8217e77898786';
+    r'5f5009ecf893acd0a8814a57432abeab66b05325';
 
 /// See also [SelectEstateViewModel].
 @ProviderFor(SelectEstateViewModel)

+ 1 - 1
packages/cpt_auth/lib/modules/select_role/select_role_page.dart

@@ -106,7 +106,7 @@ class SelectRolePage extends HookConsumerWidget {
               fontSize: 16,
               minHeight: 50,
               radius: 5,
-            ).marginOnly(top: 30, bottom: 30, left: 18, right: 18),
+            ).marginOnly(top: 50, bottom: 50, left: 18, right: 18),
           ],
         ),
       ),

+ 151 - 199
packages/cpt_auth/lib/modules/select_unit/select_unit_page.dart

@@ -32,212 +32,164 @@ class SelectUnitPage extends HookConsumerWidget {
     }
   }
 
-  // 为需要测量的控件创建 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),
+      appBar: MyAppBar.appBar(context, S.current.yy_home_accounts),
       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),
-            ],
-          ),
+      body: Container(
+        padding: const EdgeInsets.symmetric(horizontal: 20),
+        width: double.infinity,
+        child: Column(
+          mainAxisSize: MainAxisSize.max,
+          crossAxisAlignment: CrossAxisAlignment.center,
+          children: [
+            SingleChildScrollView(
+                scrollDirection: Axis.vertical,
+                physics: const BouncingScrollPhysics(),
+                child: Column(
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  children: [
+                    //顶部图片
+                    const MyAssetImage(
+                      Assets.authSignUpUnitImg,
+                      width: 266.5,
+                      height: 162,
+                    ).marginOnly(top: 28, bottom: 18),
+
+                    Row(
+                      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(
+                      S.current.block_desc,
+                      fontSize: 15,
+                      marginTop: 25,
+                      textAlign: TextAlign.center,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+
+                    MyTextView(
+                      S.current.block_example,
+                      fontSize: 15,
+                      marginTop: 20,
+                      textAlign: TextAlign.center,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+
+                    MyTextView(
+                      S.current.block_example_desc,
+                      fontSize: 15,
+                      marginTop: 20,
+                      textAlign: TextAlign.center,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+                  ],
+                )).expanded(),
+
+            MyButton(
+              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: 50, bottom: 50, left: 18, right: 18),
+          ],
         ),
       ),
     );

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

@@ -17,8 +17,4 @@ class SelectUnitViewModel extends _$SelectUnitViewModel {
     SelectRolePage.startInstance();
   }
 
-  void setRemainingSpace(double remainingSpace) {
-    state = state.copyWith(remainingSpace: remainingSpace);
-  }
-
 }

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

@@ -7,7 +7,7 @@ part of 'select_unit_view_model.dart';
 // **************************************************************************
 
 String _$selectUnitViewModelHash() =>
-    r'f31ffde34c823cf1b3ab7477ddcfda312ccce307';
+    r'f4f8001a0fa6e406b06fe7a8ecaba8e6309e21c5';
 
 /// See also [SelectUnitViewModel].
 @ProviderFor(SelectUnitViewModel)

+ 23 - 7
packages/cpt_auth/lib/modules/sign_up/sign_up_view_model.dart

@@ -1,5 +1,7 @@
 import 'package:cpt_auth/modules/sing_up_verify/sign_up_verify_page.dart';
 import 'package:flutter/material.dart';
+import 'package:plugin_basic/dialog/verify_code_dialog.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:shared/utils/log_utils.dart';
@@ -97,17 +99,31 @@ class SignUpViewModel extends _$SignUpViewModel {
       return;
     }
 
-    //执行密码登录
-    ToastEngine.show('准备执行请求发送验证码 firstName:$firstName lastName:$lastName email:$email phone:$phone password:$password confirmPassword:$confirmPassword');
 
-    gotoSignUpVerifyPage();
-  }
+    //短信验证码之前的图片验证码弹窗
+    DialogEngine.show(
+      onDismiss: () {},
+      widget: VerifyCodeDialog(
+        confirmAction: (key, code) {
+          //去新页面发送短信验证码并校验和注册用户
+          SignUpVerifyPage.startInstance(
+            firstName: firstName,
+            lastName: lastName,
+            email: email,
+            phone: phone,
+            password: password,
+            confirmPassword: confirmPassword,
+            verifyKey: key,
+            verifyCode: code,
+          );
+        },
+      ),
+    );
+
 
-  //去注册的短信校验页面
-  void gotoSignUpVerifyPage() {
-    SignUpVerifyPage.startInstance();
   }
 
+
   //切换隐藏显示密码
   void switchPwdVisibility() {
     state = state.copyWith(pwdVisibility: !state.pwdVisibility);

+ 1 - 1
packages/cpt_auth/lib/modules/sign_up/sign_up_view_model.g.dart

@@ -6,7 +6,7 @@ part of 'sign_up_view_model.dart';
 // RiverpodGenerator
 // **************************************************************************
 
-String _$signUpViewModelHash() => r'9953c49f83eace3fa24d85f5bc8cf95212175ff9';
+String _$signUpViewModelHash() => r'47850a34e661455a7b752fc95c1ddb0ba30c3b4c';
 
 /// See also [SignUpViewModel].
 @ProviderFor(SignUpViewModel)

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

@@ -1,92 +0,0 @@
-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),
-            ],
-          ),
-        );
-      },
-    );
-  }
-}

+ 79 - 11
packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_page.dart

@@ -1,4 +1,4 @@
-import 'package:cpt_auth/modules/sing_up_success/sign_up_success_full_screen.dart';
+import 'package:cpt_auth/modules/sing_up_success/sign_up_success_view_model.dart';
 import 'package:cs_resources/generated/assets.dart';
 import 'package:cs_resources/generated/l10n.dart';
 import 'package:cs_resources/theme/app_colors_theme.dart';
@@ -6,10 +6,12 @@ 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/responsive_widget.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_small_screen.dart';
 
 @RoutePage()
 class SignUpSuccessPage extends HookConsumerWidget {
@@ -17,21 +19,87 @@ class SignUpSuccessPage extends HookConsumerWidget {
 
   //启动当前页面
   static void startInstance({BuildContext? context}) {
-    if (context != null) {
-      context.router.popAndPush(const SignUpSuccessPageRoute());
-    } else {
-      appRouter.popAndPush(const SignUpSuccessPageRoute());
-    }
+    appRouter.pushAndPopUntil(
+      const SignUpSuccessPageRoute(),
+      predicate: (route) {
+        return route.settings.name == 'AuthLoginPageRoute';
+      },
+    );
   }
 
   @override
   Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.watch(signUpSuccessViewModelProvider.notifier);
+
     return Scaffold(
       appBar: MyAppBar.appBar(context, ""),
       backgroundColor: context.appColors.backgroundDefault,
-      body: const ResponsiveWidget(
-        smallScreen: SignUpSuccessSmallScreen(),
-        smallFullScreen: SignUpSuccessFullScreen(),
+      body: Container(
+        padding: const EdgeInsets.symmetric(horizontal: 20),
+        width: double.infinity,
+        child: Column(
+          mainAxisSize: MainAxisSize.max,
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            SingleChildScrollView(
+                scrollDirection: Axis.vertical,
+                physics: const BouncingScrollPhysics(),
+                child: Column(
+                  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,
+                    ),
+                  ],
+                )).expanded(),
+
+            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),
+          ],
+        ),
       ),
     );
   }

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

@@ -1,90 +0,0 @@
-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),
-          ],
-        ),
-      ),
-    );
-  }
-}

+ 3 - 1
packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_view_model.dart

@@ -1,6 +1,8 @@
 import 'package:plugin_platform/engine/toast/toast_engine.dart';
 import 'package:riverpod_annotation/riverpod_annotation.dart';
 
+import '../select_estate/select_estate_page.dart';
+
 part 'sign_up_success_view_model.g.dart';
 
 @riverpod
@@ -10,6 +12,6 @@ class SignUpSuccessViewModel extends _$SignUpSuccessViewModel {
 
   //去选择社区的页面
   void gotoSelectEstatePage() {
-    ToastEngine.show("去选择社区的页面");
+    SelectEstatePage.startInstance();
   }
 }

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

@@ -7,7 +7,7 @@ part of 'sign_up_success_view_model.dart';
 // **************************************************************************
 
 String _$signUpSuccessViewModelHash() =>
-    r'715f2ae03093839694f7336d2cf8f0b57ce65d95';
+    r'7c137417584f468299cce7cf59fc08a28f038227';
 
 /// See also [SignUpSuccessViewModel].
 @ProviderFor(SignUpSuccessViewModel)

+ 80 - 13
packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_page.dart

@@ -3,6 +3,7 @@ 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:plugin_platform/engine/toast/toast_engine.dart';
 import 'package:router/ext/auto_router_extensions.dart';
@@ -17,15 +18,48 @@ import 'sign_up_verify_view_model.dart';
 
 @RoutePage()
 class SignUpVerifyPage extends HookConsumerWidget {
-  const SignUpVerifyPage({Key? key}) : super(key: key);
+  final String firstName;
+  final String lastName;
+  final String email;
+  final String phone;
+  final String password;
+  final String confirmPassword;
+  final String? verifyKey;
+  final String? verifyCode;
+
+  const SignUpVerifyPage({
+    Key? key,
+    required this.firstName,
+    required this.lastName,
+    required this.email,
+    required this.phone,
+    required this.password,
+    required this.confirmPassword,
+    required this.verifyKey,
+    required this.verifyCode,
+  }) : super(key: key);
 
   //启动当前页面
-  static void startInstance({BuildContext? context}) {
-    if (context != null) {
-      context.router.popAndPush(const SignUpVerifyPageRoute());
-    } else {
-      appRouter.popAndPush(const SignUpVerifyPageRoute());
-    }
+  static void startInstance({
+    required String firstName,
+    required String lastName,
+    required String email,
+    required String phone,
+    required String password,
+    required String confirmPassword,
+    required String? verifyKey,
+    required String? verifyCode,
+  }) {
+    appRouter.push(SignUpVerifyPageRoute(
+      firstName: firstName,
+      lastName: lastName,
+      email: email,
+      phone: phone,
+      password: password,
+      confirmPassword: confirmPassword,
+      verifyKey: verifyKey,
+      verifyCode: verifyCode,
+    ));
   }
 
   @override
@@ -33,7 +67,17 @@ class SignUpVerifyPage extends HookConsumerWidget {
     final viewModel = ref.watch(signUpVerifyViewModelProvider.notifier);
     final state = ref.watch(signUpVerifyViewModelProvider);
 
-    const length = 4;
+    useEffect(() {
+      Future.microtask(() {
+        //默认进来就发短信
+        viewModel.sendSMS(phone, verifyKey, verifyCode);
+      });
+      return () {
+      };
+    }, []);
+
+    String? smsCode;  //已输入的短信验证码
+    const length = 6;
     final borderColor = context.appColors.textPrimary;
     const errorColor = Colors.redAccent;
     final fillColor = context.appColors.authFiledBG;
@@ -85,7 +129,16 @@ class SignUpVerifyPage extends HookConsumerWidget {
                   focusNode: state.focusNode,
                   defaultPinTheme: defaultPinTheme,
                   onCompleted: (pin) {
-                    ToastEngine.show("输入的验证码为:$pin");
+                    smsCode = pin;
+                    viewModel.verifySignUpInput(
+                      firstName: firstName,
+                      lastName: lastName,
+                      email: email,
+                      phone: phone,
+                      password: password,
+                      confirmPassword: confirmPassword,
+                      smsCode: pin,
+                    );
                   },
                   focusedPinTheme: defaultPinTheme.copyWith(
                     height: 50,
@@ -114,10 +167,14 @@ class SignUpVerifyPage extends HookConsumerWidget {
               ),
 
               MyButton(
-                onPressed: viewModel.verifySignUpInput,
-                text: S.current.resend_code,
+                onPressed: () {
+                  if (!state.isCounting) {
+                    viewModel.showVerifyCodedDialog(phone);
+                  }
+                },
+                text: state.isCounting ? "${state.countdownTime} s" : S.current.resend_code,
                 textColor: Colors.white,
-                backgroundColor: context.appColors.orangeBG,
+                backgroundColor: state.isCounting ? context.appColors.disEnableGray : context.appColors.orangeBG,
                 fontWeight: FontWeight.w500,
                 type: ClickType.throttle,
                 fontSize: 16,
@@ -150,7 +207,17 @@ class SignUpVerifyPage extends HookConsumerWidget {
               ).marginOnly(top: 16),
 
               MyButton(
-                onPressed: viewModel.verifySignUpInput,
+                onPressed: () {
+                  viewModel.verifySignUpInput(
+                    firstName: firstName,
+                    lastName: lastName,
+                    email: email,
+                    phone: phone,
+                    password: password,
+                    confirmPassword: confirmPassword,
+                    smsCode: smsCode,
+                  );
+                },
                 text: S.current.next,
                 textColor: Colors.white,
                 backgroundColor: context.appColors.btnBgDefault,

+ 26 - 4
packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_state.dart

@@ -1,8 +1,30 @@
 import 'package:flutter/material.dart';
 
-class SignUpVerifyState{
+class SignUpVerifyState {
+  final TextEditingController controller;
+  final FocusNode focusNode;
 
-  final controller = TextEditingController();
-  final focusNode = FocusNode();
+  // 获取验证码的倒计时
+  bool isCounting;
+  int countdownTime;
 
-}
+  SignUpVerifyState({
+    this.isCounting = false,
+    this.countdownTime = 0,
+    TextEditingController? controller,
+    FocusNode? focusNode,
+  })  : controller = controller ?? TextEditingController(),
+        focusNode = focusNode ?? FocusNode();
+
+  SignUpVerifyState copyWith({
+    bool? isCounting,
+    int? countdownTime,
+  }) {
+    return SignUpVerifyState(
+      isCounting: isCounting ?? this.isCounting,
+      countdownTime: countdownTime ?? this.countdownTime,
+      controller: this.controller,
+      focusNode: this.focusNode,
+    );
+  }
+}

+ 102 - 4
packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_view_model.dart

@@ -1,6 +1,17 @@
+import 'dart:async';
+
 import 'package:cpt_auth/modules/sing_up_success/sign_up_success_page.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:domain/repository/auth_repository.dart';
+import 'package:plugin_basic/constants/app_constant.dart';
+import 'package:plugin_basic/dialog/verify_code_dialog.dart';
+import 'package:plugin_basic/provider/user_config/user_config_service.dart';
+import 'package:plugin_platform/engine/dialog/dialog_engine.dart';
+import 'package:plugin_platform/engine/notify/notify_engine.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_verify_state.dart';
 
@@ -8,8 +19,12 @@ part 'sign_up_verify_view_model.g.dart';
 
 @riverpod
 class SignUpVerifyViewModel extends _$SignUpVerifyViewModel {
+  late final AuthRepository authRepository;
+
   @override
   SignUpVerifyState build() {
+    authRepository = ref.read(authRepositoryProvider);
+
     final state = SignUpVerifyState();
     ref.onDispose(() {
       onDispose(state);
@@ -17,15 +32,98 @@ class SignUpVerifyViewModel extends _$SignUpVerifyViewModel {
     return state;
   }
 
-  //校验SMS短信验证码
-  void verifySignUpInput() {
-    SignUpSuccessPage.startInstance();
+  //校验SMS短信验证码,并完成用户注册
+  void verifySignUpInput({
+    required String firstName,
+    required String lastName,
+    required String email,
+    required String phone,
+    required String password,
+    required String confirmPassword,
+    required String? smsCode,
+  }) async {
+    if (Utils.isEmpty(smsCode)) {
+      ToastEngine.show('Enter SMS Code');
+      return;
+    }
+
+    final result = await authRepository.userRegister(
+      firstName: firstName,
+      lastName: lastName,
+      email: email,
+      countryCode: AppConstant.countryCode,
+      phone: phone,
+      password: password,
+      confirmPassword: confirmPassword,
+      smsCode: smsCode,
+    );
+
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess(S.current.successful);
+      //存入Token
+      UserConfigService.getInstance().setToken(result.data?.token);
+      //去成功页面
+      SignUpSuccessPage.startInstance();
+    } else {
+      ToastEngine.show(result.errorMsg ?? "UnKnow Error");
+    }
   }
 
   //销毁资源
   void onDispose(SignUpVerifyState initState) {
+    state.controller.dispose();
+    state.focusNode.dispose();
+    countdownTimer?.cancel();
+  }
 
-    Log.d("SignUpVerifyViewModel 销毁 onDispose");
+  //展示验证码的弹窗
+  showVerifyCodedDialog(String phone) {
+    DialogEngine.show(
+      onDismiss: () {},
+      widget: VerifyCodeDialog(
+        confirmAction: (key, code) {
+          //发送短信验证码
+          sendSMS(phone, key, code);
+        },
+      ),
+    );
   }
 
+  /// 调用接口发送短信验证码
+  void sendSMS(String phone, String? key, String? code) async {
+    final result = await authRepository.sendSMS(
+      countryCode: AppConstant.countryCode,
+      phone: phone,
+      captchaKey: key,
+      captchaValue: code,
+    );
+
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess(S.current.send_sms_successful);
+      _startCountDown();
+    } else {
+      ToastEngine.show(result.errorMsg ?? "UnKnow Error");
+    }
+  }
+
+  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(); // 取消计时器
+      }
+    });
+  }
 }

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

@@ -7,7 +7,7 @@ part of 'sign_up_verify_view_model.dart';
 // **************************************************************************
 
 String _$signUpVerifyViewModelHash() =>
-    r'3ed4563220e5e1ae25ff7650c8d005e496e68334';
+    r'853c61774b0353652744df67e42d7fefe16f41e7';
 
 /// See also [SignUpVerifyViewModel].
 @ProviderFor(SignUpVerifyViewModel)

+ 53 - 100
packages/cpt_auth/lib/modules/tenant_doc/tenant_doc_page.dart

@@ -2,10 +2,8 @@ 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';
@@ -28,55 +26,16 @@ class TenantDocPage extends HookConsumerWidget {
     }
   }
 
-  // 为需要测量的控件创建 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),
+      appBar: MyAppBar.appBar(
+        context,
+        "",
+      ),
       backgroundColor: context.appColors.backgroundDefault,
       body: Container(
         padding: const EdgeInsets.symmetric(horizontal: 15),
@@ -85,63 +44,57 @@ class TenantDocPage extends HookConsumerWidget {
           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, // 使用剩余空间的值
-            ),
+            SingleChildScrollView(
+                scrollDirection: Axis.vertical,
+                physics: const BouncingScrollPhysics(),
+                child: Column(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      S.current.upload_documents,
+                      fontSize: 23.5,
+                      marginTop: 23,
+                      marginBottom: 21,
+                      textAlign: TextAlign.center,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+                    MyTextView(
+                      S.current.upload_doc_desc,
+                      fontSize: 15,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+                    MyTextView(
+                      S.current.upload_doc_desc1,
+                      fontSize: 15,
+                      marginTop: 22,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+                    MyTextView(
+                      S.current.upload_doc_desc2,
+                      fontSize: 15,
+                      marginTop: 22,
+                      marginBottom: 24,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+                    ImageNineGrid(
+                      isSelectEnable: true,
+                      maxImages: 3,
+                      spacing: 10,
+                      aspectRatio: 108 / 80,
+                      initialImages: [],
+                      onImagesChanged: (list) {
+                        viewModel.setDocList(list);
+                      },
+                    ),
+                  ],
+                )).expanded(),
 
             //底部的按钮
             MyButton(
-              key: _buttonKey,
               onPressed: viewModel.submitDoc,
               text: S.current.next,
               textColor: Colors.white,
@@ -151,7 +104,7 @@ class TenantDocPage extends HookConsumerWidget {
               fontSize: 16,
               minHeight: 50,
               radius: 5,
-            ).marginOnly(top: 30, bottom: 30, left: 18, right: 18),
+            ).marginOnly(top: 50, bottom: 50, left: 18, right: 18),
           ],
         ),
       ),

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

@@ -21,12 +21,8 @@ class TenantDocViewModel extends _$TenantDocViewModel {
 
   //提交文件
   void submitDoc() {
-
     ToastEngine.show("请求接口上传文件:${state.docList}");
   }
 
-  void setRemainingSpace(double remainingSpace) {
-    state = state.copyWith(remainingSpace: remainingSpace);
-  }
 
 }

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

@@ -22,12 +22,12 @@ class AuthComponentService extends AuthService {
   }
 
   @override
-  void startResetPasswordPage() {
+  void startSelectEstatePage() {
     SelectEstatePage.startInstance();
   }
 
   @override
-  void startSelectEstatePage() {
-    SelectEstatePage.startInstance();
+  void startAndPopAllLoginPage() {
+    AuthLoginPage.startAndPopAll();
   }
 }

+ 75 - 5
packages/cpt_auth/lib/router/page/auth_page_router.gr.dart

@@ -68,9 +68,20 @@ abstract class _$AuthPageRouter extends RootStackRouter {
       );
     },
     SignUpVerifyPageRoute.name: (routeData) {
+      final args = routeData.argsAs<SignUpVerifyPageRouteArgs>();
       return AutoRoutePage<dynamic>(
         routeData: routeData,
-        child: const SignUpVerifyPage(),
+        child: SignUpVerifyPage(
+          key: args.key,
+          firstName: args.firstName,
+          lastName: args.lastName,
+          email: args.email,
+          phone: args.phone,
+          password: args.password,
+          confirmPassword: args.confirmPassword,
+          verifyKey: args.verifyKey,
+          verifyCode: args.verifyCode,
+        ),
       );
     },
     TenantDocPageRoute.name: (routeData) {
@@ -228,16 +239,75 @@ class SignUpSuccessPageRoute extends PageRouteInfo<void> {
 
 /// generated route for
 /// [SignUpVerifyPage]
-class SignUpVerifyPageRoute extends PageRouteInfo<void> {
-  const SignUpVerifyPageRoute({List<PageRouteInfo>? children})
-      : super(
+class SignUpVerifyPageRoute extends PageRouteInfo<SignUpVerifyPageRouteArgs> {
+  SignUpVerifyPageRoute({
+    Key? key,
+    required String firstName,
+    required String lastName,
+    required String email,
+    required String phone,
+    required String password,
+    required String confirmPassword,
+    required String? verifyKey,
+    required String? verifyCode,
+    List<PageRouteInfo>? children,
+  }) : super(
           SignUpVerifyPageRoute.name,
+          args: SignUpVerifyPageRouteArgs(
+            key: key,
+            firstName: firstName,
+            lastName: lastName,
+            email: email,
+            phone: phone,
+            password: password,
+            confirmPassword: confirmPassword,
+            verifyKey: verifyKey,
+            verifyCode: verifyCode,
+          ),
           initialChildren: children,
         );
 
   static const String name = 'SignUpVerifyPageRoute';
 
-  static const PageInfo<void> page = PageInfo<void>(name);
+  static const PageInfo<SignUpVerifyPageRouteArgs> page =
+      PageInfo<SignUpVerifyPageRouteArgs>(name);
+}
+
+class SignUpVerifyPageRouteArgs {
+  const SignUpVerifyPageRouteArgs({
+    this.key,
+    required this.firstName,
+    required this.lastName,
+    required this.email,
+    required this.phone,
+    required this.password,
+    required this.confirmPassword,
+    required this.verifyKey,
+    required this.verifyCode,
+  });
+
+  final Key? key;
+
+  final String firstName;
+
+  final String lastName;
+
+  final String email;
+
+  final String phone;
+
+  final String password;
+
+  final String confirmPassword;
+
+  final String? verifyKey;
+
+  final String? verifyCode;
+
+  @override
+  String toString() {
+    return 'SignUpVerifyPageRouteArgs{key: $key, firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, password: $password, confirmPassword: $confirmPassword, verifyKey: $verifyKey, verifyCode: $verifyCode}';
+  }
 }
 
 /// generated route for

+ 139 - 0
packages/cpt_community/lib/components/comments_dialog.dart

@@ -0,0 +1,139 @@
+import 'package:flutter/material.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/no_shadow_scroll_behavior.dart';
+import 'package:widgets/widget_export.dart';
+
+class CommentsDialog extends StatelessWidget {
+  String? title;
+  Widget? Function(BuildContext)? messageBuilder;
+  VoidCallback confirmAction;
+  VoidCallback? cancelAction;
+  bool isShowCancelBtn;
+  bool isShowConfirmBtn;
+  String? confirmTxt;
+  String? cancelTxt;
+  double? height = 0.0;
+  double? minHeight = 0.0;
+  double? maxHeight;
+  double? maxWidth = 300;
+
+  CommentsDialog({
+    this.title,
+    Widget Function(BuildContext)? this.messageBuilder,
+    required this.confirmAction,
+    this.cancelAction,
+    this.isShowCancelBtn = true,
+    this.isShowConfirmBtn = true,
+    this.confirmTxt,
+    this.cancelTxt,
+    height,
+    minHeight,
+    maxHeight,
+    maxWidth,
+    Key? key,
+  }): height = height ?? 0.0,
+        minHeight = minHeight ?? 0.0,
+        maxHeight = maxHeight ?? 5000,
+        maxWidth = maxWidth ?? 500,
+        super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    final keyboardHeight = MediaQuery.of(context).viewInsets.bottom;
+
+    return Container(
+        width: double.infinity,
+        constraints: BoxConstraints(
+          minHeight: minHeight!,
+          maxHeight: maxHeight!,
+          maxWidth: maxWidth!,
+          minWidth: 0.0,
+        ),
+        decoration: BoxDecoration(
+          color: context.appColors.whiteSecondBG,
+          borderRadius: const BorderRadius.only(
+            topLeft: Radius.circular(0),
+            topRight: Radius.circular(0),
+          ),
+        ),
+        child: SingleChildScrollView(
+            reverse: true, // 反向滚动,确保输入框在键盘上方
+            child: Column(
+                mainAxisSize: MainAxisSize.min,
+                crossAxisAlignment: CrossAxisAlignment.center,
+                children: [
+                    messageBuilder!(context) ?? Container(), // 处理 null 情况
+                    Visibility(
+                      visible: isShowConfirmBtn && isShowCancelBtn,
+                      child: Row(
+                        children: [
+                          const SizedBox(width: 18),
+                          Visibility(
+                            visible: isShowCancelBtn,
+                            child: Expanded(
+                              flex: 1,
+                              child: InkWell(
+                                onTap: () {
+                                  onCancel();
+                                  cancelAction?.call();
+                                },
+                                child: MyTextView(
+                                  cancelTxt ?? 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: Visibility(
+                              visible: isShowConfirmBtn,
+                              child: InkWell(
+                                onTap: () async {
+                                  onCancel();
+                                  confirmAction();
+                                },
+                                child: MyTextView(
+                                  confirmTxt ?? 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),
+                    ),
+                ],
+            ),
+        ).marginOnly(
+          bottom: keyboardHeight + 10,
+          top: 5,
+        ),
+      );
+  }
+
+  // 取消弹框
+  void onCancel() async {
+    SmartDialog.dismiss();
+  }
+}
+   

+ 241 - 0
packages/cpt_community/lib/components/garage_card.dart

@@ -0,0 +1,241 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:shared/utils/color_utils.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+import '../modules/garage/for_sale/for_sale_vm.dart';
+
+// 'id':1,
+// 'goods_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+// 'title': 'Electronic keyboard',
+// 'price': '\$66',
+// 'isCollection': true,
+// 'collection_num': '12',
+// 'publisher': 'William Jefferson',
+// 'publish_time': 'June 17,2016 at 7:23 p.m.',
+// 'publisher_avator': Assets.communityCamera,'
+
+
+// 定义一个 使用场景的 枚举
+enum GarageCardUseType {
+  // 默认
+  forSale,
+  forRent,
+  myPostsForSale,
+  myPostsForRent,
+}
+class GarageCard extends StatelessWidget {
+  GarageCardUseType? useType;
+  Map<String, dynamic> itemObj;
+  double? cardHeight;
+  final Function()? onTap;
+  final Function(dynamic)? onClickColleciotn;
+
+  GarageCard({
+    Key? key,
+    this.useType = GarageCardUseType.forSale,
+    required this.itemObj,
+    this.onTap,
+    this.onClickColleciotn,
+    double? cardHeight,
+  }) : super(key: key) {
+    this.cardHeight ??= 214;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      children: [
+        // 图片
+        Row(
+          mainAxisAlignment: MainAxisAlignment.center,
+          crossAxisAlignment: CrossAxisAlignment.center,
+          children: [
+            Expanded(
+              child: ClipRRect(
+                borderRadius: const BorderRadius.only(topLeft: Radius.circular(8), topRight: Radius.circular(8),),
+                child: MyLoadImage(
+                  itemObj['goods_img'],
+                  width: 166.5,
+                  height: 102.5,
+                  isCircle: false,
+                  fit: BoxFit.cover,
+                ).onTap(() {
+                  // 点击头像
+                  // onTap?.call();
+                }),
+              ),
+            ),
+          ],
+        ),
+        // 标题
+        Padding(
+          padding: const EdgeInsets.only(left: 10, right: 10, top: 12, bottom: 12),
+          child: Row(
+            mainAxisAlignment: MainAxisAlignment.center,
+            children: [
+              Expanded(
+                child: MyTextView(
+                  itemObj['title'],
+                  maxLines: 1,
+                  isTextEllipsis: true,
+                  textAlign: TextAlign.left,
+                  textColor: context.appColors.textBlack,
+                  fontSize: 16,
+                  isFontRegular: true,
+                ),
+              ),
+            ],
+          ),
+        ),
+        // 价格 及 收藏
+        Padding(
+          padding: const EdgeInsets.only(left: 10, right: 10,top: 10, bottom: 10),
+          child: Row(
+            mainAxisAlignment: MainAxisAlignment.spaceAround,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              Expanded(
+                child: MyTextView(
+                  itemObj['price'],
+                  maxLines: 1,
+                  isTextEllipsis: true,
+                  textAlign: TextAlign.start,
+                  textColor: ColorUtils.string2Color('#4161D0'),
+                  fontSize: 18,
+                  isFontMedium: true,
+                ),
+              ),
+              // 动态的 收藏数
+              CollectionWidget(
+                  collectionNum: itemObj['collection_num'],
+                  isCollection: itemObj['isCollection'],
+                  onClickColleciotn: onClickColleciotn,
+              ),
+            ],
+          ),
+        ),
+        // 发布人信息
+        Expanded(
+          child: Padding(
+            padding: EdgeInsets.only(left: (useType == GarageCardUseType.forSale || useType == GarageCardUseType.forRent)?10:0, right: 10),
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.spaceAround,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+                if (useType == GarageCardUseType.forSale || useType == GarageCardUseType.forRent)
+                  MyLoadImage(
+                    itemObj['publisher_avator'],
+                    width: 30,
+                    height: 30,
+                    isCircle: true,
+                  )
+                else
+                  const SizedBox.shrink(),
+                Expanded(
+                  child: Column(
+                    mainAxisAlignment: MainAxisAlignment.center,
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    mainAxisSize: MainAxisSize.max,
+                    children: [
+                      if (useType == GarageCardUseType.forSale || useType == GarageCardUseType.forRent)
+                        MyTextView(
+                          itemObj['publisher'],
+                          maxLines: 1,
+                          isTextEllipsis: true,
+                          textAlign: TextAlign.start,
+                          marginLeft: 13,
+                          fontSize: 12,
+                          textColor: ColorUtils.string2Color('#2956B7'),
+                          isFontRegular: true,
+                        )
+                      else
+                        const SizedBox.shrink(),
+                      MyTextView(
+                        itemObj['publish_time'],
+                        maxLines: 1,
+                        isTextEllipsis: true,
+                        textAlign: TextAlign.start,
+                        marginLeft: 13,
+                        marginTop: 5,
+                        fontSize: 10,
+                        textColor: context.appColors.textBlack,
+                        isFontRegular: true,
+                      ),
+                    ]
+                  )
+                ),
+              ]
+            )
+          ),
+        )
+      ],
+    );
+  }
+}
+
+
+
+class CollectionWidget extends HookConsumerWidget {
+  int collectionNum = 0;
+  bool isCollection = false;
+  final Function(dynamic)? onClickColleciotn;
+  CollectionWidget({
+    Key? key,
+    required this.collectionNum,
+    required this.isCollection,
+    this.onClickColleciotn,
+  }) : super(key: key);
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final collectionNumState = useState(collectionNum);
+    final isCollectionState = useState(isCollection);
+    return Container(
+      width: 60,
+      height: 30,
+      alignment: Alignment.center,
+      // decoration: BoxDecoration(
+      //   color: ColorUtils.string2Color('#E5E5E5'),
+      //   borderRadius: BorderRadius.circular(15),
+      // ),
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.center,
+        children: [
+          MyTextView(
+            '${collectionNumState.value}',
+            textColor: context.appColors.textBlack,
+            fontSize: 14,
+            isFontRegular: true,
+            marginRight: 7,
+          ),
+          MyLoadImage(
+            isCollectionState.value? Assets.communityLikeActive: Assets.communityLike,
+            width: 15,
+            height: 14,
+          )
+        ]
+        // 点击 收餐/取消收藏
+    ).onTap((){
+        // Log.d("点击了收藏按钮  ${isCollectionState.value}");
+        // ToastEngine.show("点击了收藏按钮 ${isCollectionState.value}");
+        bool result = onClickColleciotn?.call(isCollectionState.value);
+        if(result){
+          isCollectionState.value = !isCollectionState.value;
+          if(isCollectionState.value){
+            ToastEngine.show("Collect Success");
+          }else {
+            ToastEngine.show("Cancel Collect Success");
+          }
+        }
+      })
+    );
+  }
+}

+ 0 - 69
packages/cpt_community/lib/components/garage_card_content.dart

@@ -1,69 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter/widgets.dart';
-import 'package:shared/utils/color_utils.dart';
-import 'package:widgets/my_text_view.dart';
-
-// 'id':1,
-// 'avator': Assets.communityCamera,
-// 'title': 'William Jefferson',
-// 'isFollow': false,
-// 'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-// 'imageUrls': ['https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg'],
-// 'time': 'June 17,2016 at 7:23 p.m.',
-// 'likeno': 12
-
-class GarageCardContent extends StatelessWidget {
-  const GarageCardContent({
-    Key? key,
-    required this.content,
-    this.imageUrls,
-  }) : super(key: key);
-
-  final String content;
-  final List? imageUrls;
-
-  @override
-  Widget build(BuildContext context) {
-    return Container(
-      padding: const EdgeInsets.symmetric(horizontal: 16),
-      child: Column(
-          children: [
-            Expanded(
-              child: Container(
-                // color: Colors.red,
-                child: MyTextView(
-                  content,
-                  textColor: ColorUtils.string2Color('#000000'),
-                  fontSize: 15,
-                  maxLines: 3,
-                  isTextEllipsis: true,
-                ),
-              ),
-            ),
-            const SizedBox(height: 12),
-            // 图片
-            if (imageUrls != null && imageUrls!.isNotEmpty)
-              Container(
-                width: double.infinity,
-                height: 87,
-                child: Row(
-                  mainAxisAlignment: MainAxisAlignment.center,
-                  children: [
-                    for (var item in imageUrls!)
-                      Expanded(
-                        child: Container(
-                          // width: 87,
-                          // height: 87,
-                          // margin: const EdgeInsets.only(right: 30),
-                          margin: const EdgeInsets.only(left: 15,right: 18),
-                          color: ColorUtils.string2Color("#F2F3F6"),
-                        ),
-                      )
-                  ],
-                ),
-              )
-          ]
-      ),
-    );
-  }
-}

+ 0 - 111
packages/cpt_community/lib/components/garage_card_footer.dart

@@ -1,111 +0,0 @@
-import 'package:cpt_community/components/newfeed_card_header.dart';
-import 'package:cs_resources/generated/assets.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter/widgets.dart';
-import 'package:shared/utils/color_utils.dart';
-import 'package:shared/utils/log_utils.dart';
-import 'package:widgets/ext/ex_widget.dart';
-import 'package:widgets/my_load_image.dart';
-import 'package:widgets/my_text_view.dart';
-
-// 'id':1,
-// 'avator': Assets.communityCamera,
-// 'title': 'William Jefferson',
-// 'isFollow': false,
-// 'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-// 'imageUrls': ['https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg'],
-// 'time': 'June 17,2016 at 7:23 p.m.',
-// 'likeno': 12
-
-class GarageCardFooter extends StatelessWidget {
-  final bool isLike;
-  final VoidCallback? onLike;
-  final VoidCallback? onComment;
-  final VoidCallback? onShare;
-
-  const GarageCardFooter({
-    Key? key,
-    required this.isLike,
-    this.onLike,
-    this.onComment,
-    this.onShare
-  })
-      : super(key: key);
-
-  @override
-  Widget build(BuildContext context) {
-    return Container(
-        height: 40,
-        width: double.infinity,
-        padding: const EdgeInsets.symmetric(horizontal: 16),
-        // color: Colors.red,
-        child: Expanded(
-          child:  Row(
-              mainAxisAlignment: MainAxisAlignment.spaceBetween,
-              children: [
-                Container(
-                  height: 40,
-                  padding: const EdgeInsets.all(8),
-                  child: Row(
-                    children: [
-                      const MyAssetImage(Assets.communityLike, width: 16,height: 16,),
-                      MyTextView(
-                        'Like',
-                        textColor: ColorUtils.string2Color('#767676'),
-                        fontSize: 14,
-                        isFontRegular: true,
-                        textAlign: TextAlign.left,
-                        marginLeft: 8,
-                      ),
-                    ],
-                  ),
-                ).onTap((){
-                  Log.d("点击了like");
-                  onLike?.call();
-                }),
-                Container(
-                  height: 40,
-                  padding: const EdgeInsets.all(8),
-                  child: Row(
-                    children: [
-                      const MyAssetImage(Assets.communityComments, width: 16,height: 16,),
-                      MyTextView(
-                        'Comments',
-                        textColor: ColorUtils.string2Color('#767676'),
-                        fontSize: 14,
-                        isFontRegular: true,
-                        textAlign: TextAlign.left,
-                        marginLeft: 8,
-                      ),
-                    ],
-                  ),
-                ).onTap((){
-                  Log.d("点击了comments");
-                  onComment?.call();
-                }),
-                Container(
-                  height: 40,
-                  padding: const EdgeInsets.all(8),
-                  child: Row(
-                    children: [
-                      const MyAssetImage(Assets.communityShare, width: 16,height: 16,),
-                      MyTextView(
-                        'Share',
-                        textColor: ColorUtils.string2Color('#767676'),
-                        fontSize: 14,
-                        isFontRegular: true,
-                        textAlign: TextAlign.left,
-                        marginLeft: 8,
-                      ),
-                    ],
-                  ),
-                ).onTap((){
-                  Log.d("点击了share");
-                  onShare?.call();
-                }),
-              ]
-          ),
-        )
-    );
-  }
-}

+ 0 - 149
packages/cpt_community/lib/components/garage_card_header.dart

@@ -1,149 +0,0 @@
-import 'package:cs_resources/generated/assets.dart';
-import 'package:cs_resources/theme/app_colors_theme.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter/widgets.dart';
-import 'package:flutter_hooks/flutter_hooks.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:shared/utils/color_utils.dart';
-import 'package:shared/utils/log_utils.dart';
-import 'package:widgets/ext/ex_widget.dart';
-import 'package:widgets/my_load_image.dart';
-import 'package:widgets/my_text_view.dart';
-
-import '../modules/garage/for_sale/for_sale_vm.dart';
-
-// 'id':1,
-// 'goods_img':  'https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg',
-// 'title': 'Electronic keyboard',
-// 'price': '\$66',
-// 'isCollection': true,
-// 'collection_num': '12',
-// 'publisher': 'William Jefferson',
-// 'publish_time': 'June 17,2016 at 7:23 p.m.',
-// 'publisher_avator': Assets.communityCamera,'
-
-
-
-
-
-class GarageCardHeader extends StatelessWidget {
-  Map<String, dynamic> itemObj;
-  final Function()? onTap;
-
-  GarageCardHeader({
-    Key? key,
-    required this.itemObj,
-    this.onTap,
-  }) : super(key: key);
-
-
-
-  @override
-  Widget build(BuildContext context) {
-    return Column(
-      children: [
-        Row(
-          mainAxisAlignment: MainAxisAlignment.center,
-          crossAxisAlignment: CrossAxisAlignment.center,
-          children: [
-            Expanded(
-              child: MyLoadImage(
-                itemObj['goods_img'],
-                width: 166.5,
-                height: 102.5,
-                isCircle: false,
-                fit: BoxFit.cover,
-              ).onTap(() {
-                // 点击头像
-                // onTap?.call();
-              }),
-            ),
-          ],
-        ),
-        Row(
-          mainAxisAlignment: MainAxisAlignment.center,
-          children: [
-            Expanded(
-              child: MyTextView(
-                itemObj['title'],
-                maxLines: 1,
-                isTextEllipsis: true,
-                textAlign: TextAlign.center,
-                textColor: context.appColors.textBlack,
-                fontSize: 16,
-                isFontRegular: true,
-                marginLeft: 12,
-                marginRight: 12,
-              ),
-            ),
-          ],
-        ),
-        Row(
-          mainAxisAlignment: MainAxisAlignment.spaceAround,
-          crossAxisAlignment: CrossAxisAlignment.center,
-          children: [
-            Expanded(
-              child: MyTextView(
-                itemObj['price'],
-                maxLines: 1,
-                isTextEllipsis: true,
-                textAlign: TextAlign.start,
-                textColor: ColorUtils.string2Color('#4161D0'),
-                fontSize: 18,
-                isFontMedium: true,
-              ),
-            ),
-            // 动态的 收藏数
-            CollectionWidget(collectionNum: itemObj['collection_num'], isCollection: itemObj['isCollection']),
-          ],
-        ),
-      ],
-    );
-  }
-}
-
-
-
-class CollectionWidget extends HookConsumerWidget {
-  int collectionNum = 0;
-  bool isCollection = false;
-  CollectionWidget({
-    Key? key,
-    required this.collectionNum,
-    required this.isCollection,
-  }) : super(key: key);
-  @override
-  Widget build(BuildContext context, WidgetRef ref) {
-    final collectionNumState = useState(collectionNum);
-    final isCollectionState = useState(isCollection);
-
-    return Container(
-      width: 60,
-      height: 30,
-      alignment: Alignment.center,
-      decoration: BoxDecoration(
-        color: ColorUtils.string2Color('#E5E5E5'),
-        borderRadius: BorderRadius.circular(15),
-      ),
-      child: Row(
-        mainAxisAlignment: MainAxisAlignment.center,
-        children: [
-          MyTextView(
-            '${collectionNumState.value}',
-            textColor: ColorUtils.string2Color('#999999'),
-            fontSize: 14,
-            isFontRegular: true,
-          ),
-          MyLoadImage(
-            isCollectionState.value? Assets.communityLikeActive: Assets.communityLike,
-            width: 14,
-            height: 14,
-          )
-        ]
-      ).onTap((){
-        // 点击 收餐/取消收藏
-        Log.d("点击了收藏按钮");
-      })
-    );
-  }
-}

+ 4 - 3
packages/cpt_community/lib/components/newfeed_card_header.dart

@@ -1,3 +1,4 @@
+import 'package:cs_resources/theme/app_colors_theme.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/widgets.dart';
 import 'package:shared/utils/color_utils.dart';
@@ -10,7 +11,7 @@ import 'package:widgets/my_text_view.dart';
 // 'title': 'William Jefferson',
 // 'isFollow': false,
 // 'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-// 'imageUrls': ['https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg'],
+// 'imageUrls': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
 // 'time': 'June 17,2016 at 7:23 p.m.',
 // 'likeno': 12
 
@@ -33,7 +34,7 @@ class NewsFeedCardHeader extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     return Container(
-      padding: const EdgeInsets.only(left: 16,right: 60),
+      padding: const EdgeInsets.only(left: 16,right: 60,),
       child: Row(
         mainAxisAlignment: MainAxisAlignment.start,
         crossAxisAlignment: CrossAxisAlignment.start,
@@ -60,7 +61,7 @@ class NewsFeedCardHeader extends StatelessWidget {
                     title,
                     isFontMedium: true,
                     fontSize: 18,
-                    textColor: ColorUtils.string2Color('#000000'),
+                    textColor: context.appColors.textBlack,
                     maxLines: 1,
                     isTextEllipsis: true,
                   ),

+ 115 - 45
packages/cpt_community/lib/components/newsfeed_card_content.dart

@@ -1,6 +1,17 @@
+import 'package:auto_route/src/route/page_route_info.dart';
+import 'package:cpt_community/components/comments_dialog.dart';
+import 'package:cpt_community/modules/community/community_page.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/widgets.dart';
+import 'package:plugin_basic/modules/global_web_page.dart';
+import 'package:plugin_basic/modules/preview_photo_page.dart';
+import 'package:router/componentRouter/component_service_manager.dart';
+import 'package:router/ext/auto_router_extensions.dart';
 import 'package:shared/utils/color_utils.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_load_image.dart';
 import 'package:widgets/my_text_view.dart';
 
 // 'id':1,
@@ -8,62 +19,121 @@ import 'package:widgets/my_text_view.dart';
 // 'title': 'William Jefferson',
 // 'isFollow': false,
 // 'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-// 'imageUrls': ['https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg'],
+// 'imageUrls': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
 // 'time': 'June 17,2016 at 7:23 p.m.',
 // 'likeno': 12
 
 class NewsFeedCardContent extends StatelessWidget {
-  const NewsFeedCardContent({
+  final String content;
+  final List<dynamic>? imageUrls;
+  int? rowMaxImageNum=3;
+  int? textMaxLines = 3;
+  bool? isPreviewImage = true;
+  NewsFeedCardContent({
     Key? key,
     required this.content,
-    this.imageUrls,
+    required List<dynamic> this.imageUrls,
+    this.rowMaxImageNum = 3,
+    this.textMaxLines = 3,
+    this.isPreviewImage = true,
   }) : super(key: key);
 
-  final String content;
-  final List? imageUrls;
 
   @override
   Widget build(BuildContext context) {
-    return Container(
-      padding: const EdgeInsets.symmetric(horizontal: 16),
-      child: Column(
-        children: [
-          Expanded(
-            child: Container(
-              // color: Colors.red,
-              child: MyTextView(
-                content,
-                textColor: ColorUtils.string2Color('#000000'),
-                fontSize: 15,
-                maxLines: 3,
-                isTextEllipsis: true,
-              ),
-            ),
-          ),
-          const SizedBox(height: 12),
-          // 图片
-          if (imageUrls != null && imageUrls!.isNotEmpty)
-             Container(
-               width: double.infinity,
-               height: 87,
-               child: Row(
-                 mainAxisAlignment: MainAxisAlignment.center,
-                 children: [
-                   for (var item in imageUrls!)
-                     Expanded(
-                       child: Container(
-                         // width: 87,
-                         // height: 87,
-                         // margin: const EdgeInsets.only(right: 30),
-                         margin: const EdgeInsets.only(left: 15,right: 18),
-                         color: ColorUtils.string2Color("#F2F3F6"),
-                       ),
-                     )
-                 ],
-               ),
-             )
-        ]
-      ),
+    List imageUrlsList = [];
+    if(imageUrls != null && imageUrls!.isNotEmpty){
+      // dart 取出 imageUrls 里面的前 rowMaxImageNum 张图片
+      imageUrlsList = imageUrls!.take(rowMaxImageNum!).toList();
+    }
+    int totalImageCount = imageUrls!.length;
+    int otherImageCount = imageUrls!.length - rowMaxImageNum!;
+    int rowActualImageCount = imageUrls!.length > rowMaxImageNum! ? rowMaxImageNum! : imageUrls!.length;
+    return LayoutBuilder(
+        builder: (BuildContext context, BoxConstraints constraints) {
+          final maxHeight = constraints.maxHeight;
+          final minHeight = constraints.minHeight;
+          final maxWidth = constraints.maxWidth;
+          Log.d("---maxHeight-----$maxHeight-- $minHeight  $maxWidth--");
+          return Container(
+            width: double.infinity,
+            // color: Colors.red,
+            padding: const EdgeInsets.symmetric(horizontal: 15),
+            child: Column(
+                children: [
+                  MyTextView(
+                    content,
+                    textColor: context.appColors.textBlack,
+                    fontSize: 15,
+                    maxLines: textMaxLines,
+                    isTextEllipsis: true,
+                  ),
+                  const SizedBox(height: 12),
+                  // 图片
+                  _buildImageSection(context, isPreviewImage!, maxWidth - 15, imageUrlsList, imageUrls, totalImageCount,otherImageCount),
+                ]
+            )
+          );
+      }
     );
   }
+
+
+  Widget _buildImageSection(BuildContext context,bool isPreviewImage, double maxWidth, List? imageUrls,List? totalImageUrls, int totalImageCount, int otherImageCount) {
+    if (imageUrls != null && imageUrls!.isNotEmpty) {
+      final imageCount = imageUrls.length;
+      return SizedBox(
+        width: maxWidth,
+        height: 87,
+        child: Row(
+          mainAxisAlignment: MainAxisAlignment.start,
+          children: List.generate(
+              imageUrls.length,
+              (index) => Container(
+                height: 87,
+                width: maxWidth/imageCount - 16,
+                margin: EdgeInsets.only(right: (index!=imageCount-1) ? 16 : 0 ),
+                color: ColorUtils.string2Color("#F2F3F6"),
+                child:Stack(
+                    children: [
+                      MyLoadImage(
+                        imageUrls[index],
+                        height: 87,
+                        width: maxWidth/imageCount - 16,
+                        fit: BoxFit.cover,
+                        // fit:BoxFit.fitWidth,
+                        cornerRadius: 5,
+                        onClick: (){
+                          if(isPreviewImage){
+                            // 点击图片预览
+                            // 过滤掉非字符串类型的元素
+                            List<String> filteredImages = totalImageUrls?.whereType<String>().toList() ?? [];
+                            PreviewPhotoPage.startInstance(context: context, images: filteredImages, position: index);
+                          }
+                        },
+                      ),
+                      otherImageCount > 0 && index == imageCount-1 ?
+                        Positioned(
+                        bottom: 0,
+                        right: 0,
+                        child: Container(
+                          padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2),
+                          color: context.appColors.textBlack.withOpacity(0.5),
+                          child: MyTextView(
+                            "+$otherImageCount",
+                            textColor: Colors.white,
+                            fontSize: 12,
+                          ),
+                        ),
+                      ): const SizedBox.shrink(),
+                    ]
+                  )
+              ),
+          )
+        )
+            );
+      } else {
+        return const SizedBox.shrink();
+      }
+    }
 }

+ 48 - 26
packages/cpt_community/lib/components/newsfeed_card_footer.dart

@@ -7,13 +7,17 @@ import 'package:shared/utils/log_utils.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/my_like_button.dart';
+
+
+import '../modules/community/community_page.dart';
 
 // 'id':1,
 // 'avator': Assets.communityCamera,
 // 'title': 'William Jefferson',
 // 'isFollow': false,
 // 'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-// 'imageUrls': ['https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg'],
+// 'imageUrls': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
 // 'time': 'June 17,2016 at 7:23 p.m.',
 // 'likeno': 12
 
@@ -22,33 +26,45 @@ class NewsFeedCardFooter extends StatelessWidget {
   final VoidCallback? onLike;
   final VoidCallback? onComment;
   final VoidCallback? onShare;
+  final bool showShare;
+  final GlobalKey<MyLikeButtonState> likeButtonKey;
 
-  const NewsFeedCardFooter({
+  NewsFeedCardFooter({
     Key? key,
+     GlobalKey<MyLikeButtonState>? likeButtonKey,
     required this.isLike,
     this.onLike,
     this.onComment,
-    this.onShare
+    this.onShare,
+    this.showShare = false,
   })
-  : super(key: key);
+  : likeButtonKey = likeButtonKey ?? GlobalKey<MyLikeButtonState>(),
+         super(key: key);
 
   @override
   Widget build(BuildContext context) {
     return Container(
       height: 40,
       width: double.infinity,
-      padding: const EdgeInsets.symmetric(horizontal: 16),
-      // color: Colors.red,
-      child: Expanded(
-        child:  Row(
-          mainAxisAlignment: MainAxisAlignment.spaceBetween,
-          children: [
-            Container(
-              height: 40,
-              padding: const EdgeInsets.all(8),
+      // padding: const EdgeInsets.symmetric(horizontal: 16),
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.spaceBetween,
+        crossAxisAlignment: CrossAxisAlignment.center,
+        children: [
+          Expanded(
+            child: Container(
+              padding: const EdgeInsets.all(10),
               child: Row(
+                mainAxisAlignment: MainAxisAlignment.center,
                 children: [
-                  const MyAssetImage(Assets.communityLike, width: 16,height: 16,),
+                  MyLikeButton(
+                    key: likeButtonKey,
+                    isLiked: false,
+                    isCustomIcon: true,
+                    onLike: () {
+                      Log.d('点击了like button');
+                    },
+                  ),
                   MyTextView(
                     'Like',
                     textColor: ColorUtils.string2Color('#767676'),
@@ -56,17 +72,19 @@ class NewsFeedCardFooter extends StatelessWidget {
                     isFontRegular: true,
                     textAlign: TextAlign.left,
                     marginLeft: 8,
-                  ),
+                  )
                 ],
               ),
             ).onTap((){
-              Log.d("点击了like");
-              onLike?.call();
+              final state = likeButtonKey.currentState;
+              state?.triggerTap();
             }),
-            Container(
-              height: 40,
+          ),
+          Expanded(
+            child: Container(
               padding: const EdgeInsets.all(8),
               child: Row(
+                mainAxisAlignment: MainAxisAlignment.center,
                 children: [
                   const MyAssetImage(Assets.communityComments, width: 16,height: 16,),
                   MyTextView(
@@ -80,13 +98,17 @@ class NewsFeedCardFooter extends StatelessWidget {
                 ],
               ),
             ).onTap((){
-              Log.d("点击了comments");
+              // Log.d("点击了comments   ${tabsRouterKey.currentState?.controller?.activeIndex}");
+              // tabsRouterKey.currentState?.controller?.setActiveIndex(2);
               onComment?.call();
             }),
-            Container(
-              height: 40,
+          ),
+          if(showShare)
+            Expanded(
+              child: Container(
               padding: const EdgeInsets.all(8),
               child: Row(
+                mainAxisAlignment: MainAxisAlignment.center,
                 children: [
                   const MyAssetImage(Assets.communityShare, width: 16,height: 16,),
                   MyTextView(
@@ -99,12 +121,12 @@ class NewsFeedCardFooter extends StatelessWidget {
                   ),
                 ],
               ),
-            ).onTap((){
+                        ).onTap((){
               Log.d("点击了share");
               onShare?.call();
-            }),
-          ]
-        ),
+                        }),
+            ),
+        ]
       )
     );
   }

+ 217 - 179
packages/cpt_community/lib/modules/community/community_page.dart

@@ -16,10 +16,14 @@ import 'package:cs_resources/theme/app_colors_theme.dart';
 import 'package:widgets/widget_export.dart';
 
 import '../../router/page/community_page_router.dart';
+import '../my_posts/my_posts_page.dart';
 import 'newsfeed_tabs.dart';
 import 'community_vm.dart';
 import 'customSilverHeaderTabs.dart';
 
+final tabsRouterKey = GlobalKey<AutoTabsRouterState>();
+
+
 @RoutePage()
 class CommunityPage extends HookConsumerWidget {
     const CommunityPage({Key? key}) : super(key: key);
@@ -33,172 +37,157 @@ class CommunityPage extends HookConsumerWidget {
       }
     }
 
-    bool _isPreventScroll(ScrollNotification notification, WidgetRef ref){
-      // 检查当前页面是否是可见的
-      bool isDownOrUp = notification.metrics.axis == Axis.vertical;
-      if (notification is UserScrollNotification) {
-        // 检查滚动方向
-        switch (notification.direction) {
-          case ScrollDirection.forward:
-            print('Scrolling down');
-            break;
-          case ScrollDirection.reverse:
-            print('Scrolling up');
-            break;
-          case ScrollDirection.idle:
-            print('Scrolling stopped');
-            break;
-        }
-      } else if (notification is ScrollUpdateNotification) {
-        // 检查滚动位置变化
-        double currentScrollPosition = notification.metrics.pixels;
-        double maxScrollExtent = notification.metrics.maxScrollExtent;
-
-        // 判断是否满足某个条件
-        if (currentScrollPosition > 0 && currentScrollPosition < maxScrollExtent) {
-          print('Current scroll position: $currentScrollPosition');
-          // 在这里添加你的条件判断逻辑
-        }
-
-        // 只有当上下滚动时才拦截通知
-        if (isDownOrUp) {
-          final tabsRouter = ref.watch(communityVmProvider).tabsRouter;
-        }
-      }
-      return false;
-    }
-
     @override
     Widget build(BuildContext context, WidgetRef ref) {
         final vm = ref.read(communityVmProvider.notifier);
         final state = ref.watch(communityVmProvider);
-        final stateTabsRouter = ref.watch(communityVmProvider).tabsRouter;
-        // final state = ref.watch(communityVmProvider);
-        BuildContext contextPage = context;
-        // 创建 GlobalKey
-        final _tabsRouterKey = GlobalKey<AutoTabsRouterState>();
 
-        // useEffect((){
-        //   Log.d("CommunityPage initState");
-        //   return () {
-        //     Log.d("CommunityPage dispose");
-        //   };
-        // },[]);
+        useEffect((){
+          Log.d("CommunityPage initState");
+          // 延迟监听
+          WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
+            if(tabsRouterKey.currentState?.controller != null){
+              tabsRouterKey.currentState?.controller?.addListener((){
+                vm.tabsRouterChange();
+              });
+            }
+          });
+
+          return (){
+            Log.d("CommunityPage dispose");
+            tabsRouterKey.currentState?.controller?.removeListener(vm.tabsRouterChange);
+          };
+        },[]);
 
         return Scaffold(
-            appBar: MyAppBar.appBar(
+            appBar: MyAppBar.searchAppBar(
               context,
-              "Community",
-              backgroundColor: context.appColors.whiteBG,
+              actions: [
+                 const MyAssetImage(
+                  Assets.communityLikeActive,
+                  width: 21.5,
+                  height: 21.5,
+                ).onTap((){
+                  vm.handlerClickNavbarLikeBtn(context);
+                 }),
+                SizedBox(width: state.currentCategoryIdx ==0 ? 15:20),
+                state.currentCategoryIdx ==1 ?
+                  const MyAssetImage(
+                    Assets.communityFillterIcon,
+                    width: 21,
+                    height: 21,
+                  ).onTap((){
+                    vm.handlerClickNavbarFilterBtn(context);
+                  }) : const SizedBox.shrink(),
+                const SizedBox(width: 15),
+              ],
+              backgroundColor: context.appColors.backgroundWhite,
             ),
-            backgroundColor: context.appColors.backgroundDefault,
-            body:  NestedScrollView(
-              headerSliverBuilder: (context, innerBoxIsScrolled) {
-                return [
-                  SliverToBoxAdapter(
-                      child: Consumer(
-                        builder: (context, ref, _) {
-                          return _buildTopSection(context, ref, vm);
-                        },
-                      ),
-                  ),
-                  // SliverPersistentHeader(
-                  //   pinned: true, // 吸顶
-                  //   delegate: CustomSliverPersistentHeaderDelegate(
-                  //     child: _buildTabsSection(contextPage, ref),
-                  //   ),
-                  // ),
-                ];
-              },
-              body: NotificationListener<ScrollNotification>(
-                onNotification: (ScrollNotification notification) {
-                  // 是否拦截滚动  false 表示不拦截通知
-                  // return _isPreventScroll(notification, ref);
-                  return false;
+          backgroundColor: context.appColors.backgroundDefault,
+          body: AutoTabsRouter.pageView(
+            key: tabsRouterKey,
+            routes: const [
+              NewsPageRoute(),
+              FollowingPageRoute(),
+              ForyouPageRoute(),
+              ForsalePageRoute(),
+              ForrentPageRoute(),
+            ],
+            // physics: const NeverScrollableScrollPhysics(), // 禁止滚动
+            builder: (context, child, pageController) {
+              final tabsRouter = AutoTabsRouter.of(context);
+
+              return NestedScrollView(
+                headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
+                  return [
+                    // top 组件,转换为 Sliver 组件
+                    SliverToBoxAdapter(
+                      child: _buildTopSection(context, ref, vm, state),
+                    ),
+                    // tab 组件,使用 SliverPersistentHeader 实现吸顶
+                    // SliverPersistentHeader(
+                    //   pinned: true,
+                    //   delegate: CustomSliverPersistentHeaderDelegate(
+                    //     child: _buildTabsSection(context, ref, tabsRouter, vm, state),
+                    //   ),
+                    // ),
+                    // SliverToBoxAdapter(
+                    //   child: _buildPostSection(context, ref, vm, state),
+                    // ),
+                  ];
                 },
-                child: AutoTabsRouter.pageView(
-                  key: _tabsRouterKey,
-                  routes: const [
-                    NewsPageRoute(),
-                    FollowingPageRoute(),
-                    ForyouPageRoute()
+                body: Column(
+                  mainAxisSize: MainAxisSize.max,
+                  children: [
+                    _buildTabsSection(context, ref, tabsRouter, vm, state),
+                    _buildPostSection(context, ref, vm, state),
+                    Expanded(
+                        child: child
+                    ),
                   ],
-                  builder: (context, child, pageController) {
-                    final tabsRouter = AutoTabsRouter.of(context);
-                    // Log.d("autotabsrouter build  ${tabsRouter.activeIndex}");
-                    // Log.d("communityVmProvider.notifier state.tabsRouter.tabsRouter:  ${ref.read(communityVmProvider.notifier).state.tabsRouter}");
-                    // ref.read(communityVmProvider.notifier).state.tabsRouter = tabsRouter;
-                    // ref.read(communityVmProvider.notifier).state.pageController = pageController;
-                    // Log.d("communityVmProvider.notifier state.tabsRouter.tabsRouter.activeIndex:  ${ref.read(communityVmProvider.notifier).state.tabsRouter.activeIndex}");
-
-                    return Column(
-                      children: [
-                        // tab 组件
-                        _buildTabsSection(contextPage, ref, tabsRouter),
-                        // post 组件
-                        _buildPostSection(context, ref, vm),
-                        Expanded(
-                          child: child,
-                        ),
-                      ],
-                    );
-                  },
-                ),
-              ),
-            )
+                ), // post 组件和其他内容
+              );
+            },
+          ),
         );
     }
 
-     Widget _buildTopSection(BuildContext context, WidgetRef ref, vm) {
+
+     Widget _buildTopSection(BuildContext context, WidgetRef ref, vm, state) {
         final topSectionsData = vm.topSectionsData;
+        // final currentPageIdx = tabsRouterKey.currentState?.controller?.activeIndex ?? 0;
         int curTagIdx = 0;
+        int currentPageIdx = state.currentPageViewIdx;
+        int newsfeedTabCount = state.newsFeedTabsList.length;
+        if(currentPageIdx >= newsfeedTabCount){
+          curTagIdx = 1;
+        }else {
+          curTagIdx = 0;
+        }
         return Container(
-          color: Colors.white,
-          padding: const EdgeInsets.only(top: 30, bottom: 30),
+          color: context.appColors.whiteBG,
+          padding: const EdgeInsets.only(top: 23, bottom: 30),
           child: Center(
             child: Row(
+              mainAxisSize: MainAxisSize.max,
               mainAxisAlignment: MainAxisAlignment.center,
               crossAxisAlignment: CrossAxisAlignment.center,
               children: List.generate(topSectionsData.length, (index) {
                 final item = topSectionsData[index];
-                return Flexible(
-                  flex: 1,
-                  child: Column(
-                    children: [
-                      Container(
+                return Column(
+                  children: [
+                    Container(
+                      width: MediaQuery.of(context).size.width / topSectionsData.length - 36,
+                      height: 70,
+                      decoration: BoxDecoration(
+                        shape: BoxShape.circle, // 设置为圆形
+                        boxShadow: index == curTagIdx ? [
+                          BoxShadow(
+                            color: context.appColors.tabLightBlueShadow, // 设置阴影颜色
+                            blurRadius: 5, // 设置模糊半径
+                            spreadRadius: 0.05, // 控制阴影扩散
+                            offset: const Offset(0, 4), // 设置阴影偏移量
+                          ),] : [],// 未选中时无阴影,
+                      ),
+                      child: MyAssetImage(
+                        item['icon'],
                         width: MediaQuery.of(context).size.width / topSectionsData.length - 36,
                         height: 70,
-                        decoration: BoxDecoration(
-                          // color: currentTabIdx == index ? ColorUtils.string2Color('#E6F2FF') : Colors.white,
-                          shape: BoxShape.circle, // 设置为圆形
-                          boxShadow: index == curTagIdx ? [
-                            BoxShadow(
-                              color: context.appColors.tabLightBlueShadow, // 设置阴影颜色
-                              blurRadius: 5, // 设置模糊半径
-                              spreadRadius: 0.05, // 控制阴影扩散
-                              offset: const Offset(0, 4), // 设置阴影偏移量
-                            ),                          ] : [],// 未选中时无阴影,
-                        ),
-                        child: MyAssetImage(
-                          item['icon'],
-                          width: MediaQuery.of(context).size.width / topSectionsData.length - 36,
-                          height: 70,
-                        ).onTap(() {
-                            vm.handlerChangeCommunityType(context,index);
-                          },
-                          type: ClickType.throttle,
-                        ),
-                      ),
-                      SizedBox.fromSize(size: const Size(0, 9)),
-                      MyTextView(
-                        item['title'],
-                        fontSize: 15,
-                        textColor: index == curTagIdx ? ColorUtils.string2Color('#4161D0'):Colors.black,
-                        textAlign: TextAlign.center,
-                        isFontMedium: true,
+                      ).onTap(() {
+                          vm.handlerSwitchNewsfeedOrGaragesale(index, context, null);
+                        },
+                        type: ClickType.throttle,
                       ),
-                    ],
-                  ),
+                    ),
+                    SizedBox.fromSize(size: const Size(0, 9)),
+                    MyTextView(
+                      item['title'],
+                      fontSize: 15,
+                      textColor: index == curTagIdx ? ColorUtils.string2Color('#4161D0'): context.appColors.textBlack,
+                      textAlign: TextAlign.center,
+                      isFontMedium: true,
+                    ),
+                  ],
                 ).marginOnly(left: 18, right: 18, top: 10, bottom: 10);
               }),
             ),
@@ -206,25 +195,50 @@ class CommunityPage extends HookConsumerWidget {
         );
     }
 
-     Widget _buildTabsSection(BuildContext context, WidgetRef ref, tabsRouter){
-       final vm = ref.read(communityVmProvider.notifier);
-       final state = ref.watch(communityVmProvider);
-       final tabsList = ref.watch(communityVmProvider.select((state) => state.tabsList));
+     Widget _buildTabsSection(BuildContext context, WidgetRef ref, tabsRouter, vm, state){
+       int currentPageIndex = tabsRouter!.activeIndex ?? 0;
+       int newsfeedTabCount = state.newsFeedTabsList.length;
+
+       List<String> tabsList;
+       if(currentPageIndex < newsfeedTabCount){
+         // news feed
+         tabsList = state.newsFeedTabsList ?? [];
+       }else {
+         tabsList = state.garageSaleTabsList ?? [];
+       }
        return Container(
         width: double.infinity,
-        padding: const EdgeInsets.only(left: 15, right: 15,top: 14,bottom: 14),
+        padding: const EdgeInsets.only(left: 15, right: 15,top: 14,bottom: 12),
+        color: ColorUtils.string2Color('#F2F3F6'),
         child: NewsfeedTabs(
           key: UniqueKey(),
-          tabsList: tabsList!,
+          tabsList: tabsList,
             tabsRouter: tabsRouter,
-          onClickAction:(activeTabIdx){
-            vm.handlerChangeTab(activeTabIdx, tabsRouter);
+          onClickAction:(Map<String, dynamic>? params){
+            if (params != null) {
+              // 解构 params
+              final int? currentCatgoryIdx = params['currentCatgoryIdx'] as int?;
+              final int? tabIdx = params['tabIdx'] as int?;
+              vm.handlerChangeTab(tabIdx, tabsRouter, currentCatgoryIdx);
+            }
           }
         ),
       );
     }
 
-      Widget _buildPostSection(BuildContext context, WidgetRef ref, vm){
+      Widget _buildPostSection(BuildContext context, WidgetRef ref, vm, state){
+        int currentPageIndex = state.currentPageViewIdx;
+        int newsfeedTabCount = state.newsFeedTabsList.length;
+        if(currentPageIndex < newsfeedTabCount){
+          // news feed
+          return  _buildNewsFeedPost(context, ref, vm, state);
+        }else {
+          return  _buildGarageSalePost(context, ref, vm, state);
+        }
+
+      }
+
+      Widget _buildNewsFeedPost(BuildContext context, WidgetRef ref, vm, state){
         return Container(
           height: 65.5,
           width: double.infinity,
@@ -233,39 +247,63 @@ class CommunityPage extends HookConsumerWidget {
           child: Row(
             children: [
               const MyAssetImage(Assets.communityNesFeed, width: 45,height: 45,),
-             Expanded(
-                child: Row(
-                  children: [
-                    Expanded(
-                      child: Container(
-                        // height: 65.5,
-                        // color: Colors.blue,
-                        child: MyTextView(
-                          "What’s on your mind?",
-                          textColor: ColorUtils.string2Color('#000000'),
-                          fontSize: 15,
-                          marginLeft: 15,
-                          alignment: Alignment.centerLeft,
-                          textAlign: TextAlign.left,
-                          backgroundColor: ColorUtils.string2Color('#ffffff'),
-                          maxLines: 1,
-                          isFontMedium: true,
-                        ),
-                      ),
-                    ),
-                    const MyAssetImage(
-                      Assets.communityCamera,
-                      width: 21,
-                      height: 16.5,
-                    ),
-                  ],
-                ).onTap((){
-                  vm.handlerGotoPost(context);
-                }),
+              Expanded(
+                child: MyTextView(
+                  "What’s on your mind?",
+                  textColor: context.appColors.textBlack,
+                  fontSize: 15,
+                  marginLeft: 15,
+                  alignment: Alignment.centerLeft,
+                  textAlign: TextAlign.left,
+                  backgroundColor: ColorUtils.string2Color('#ffffff'),
+                  maxLines: 1,
+                  isFontMedium: true,
+                ),
+              ),
+              const MyAssetImage(
+                Assets.communityCamera,
+                width: 21,
+                height: 16.5,
               ),
             ],
-          ),
+          ).onTap((){
+            vm.handlerGotoNewsfeedPost(context);
+          }),
         );
       }
 
+      Widget _buildGarageSalePost(BuildContext context, WidgetRef ref, vm, state){
+        return Container(
+          height: 65.5,
+          width: double.infinity,
+          padding: const EdgeInsets.only(left: 20, right: 20),
+          color: Colors.white,
+          child: Row(
+            children: [
+              const MyAssetImage(Assets.communityNesFeed, width: 45,height: 45,),
+              Expanded(
+                child: MyTextView(
+                  "Sell Item",
+                  textColor: context.appColors.textBlack,
+                  fontSize: 15,
+                  marginLeft: 15,
+                  alignment: Alignment.centerLeft,
+                  textAlign: TextAlign.left,
+                  backgroundColor: ColorUtils.string2Color('#ffffff'),
+                  maxLines: 1,
+                  isFontMedium: true,
+                ),
+              ),
+              const MyAssetImage(
+                Assets.communityCamera,
+                width: 21,
+                height: 16.5,
+              ),
+            ],
+          ).onTap((){
+            vm.handlerGotoGaragePost(context);
+          }),
+        );
+      }
 }
+

+ 7 - 0
packages/cpt_community/lib/modules/community/community_pageview_idx_data.dart

@@ -0,0 +1,7 @@
+class CommunityPageViewIdxData {
+  static const int news = 0;
+  static const int following = 1;
+  static const int forYou = 2;
+  static const int forSale = 3;
+  static const int forRent = 4;
+}

+ 37 - 9
packages/cpt_community/lib/modules/community/community_state.dart

@@ -1,44 +1,72 @@
 import 'package:cpt_community/modules/community/community_page.dart';
 import 'package:cs_resources/generated/assets.dart';
 
-import '../garage/garage_page.dart';
 
 class CommunityVmState {
   List<Map<String, dynamic>>? topSectionsData;
-  List? tabsList = ["News", "Following", "For You"];
+  List<String>? newsFeedTabsList;
+  List<String>? garageSaleTabsList;
+  int currentCategoryIdx = 0;   // 0: news feed, 1: garage sale
+  int currentPageViewIdx = 0;
+  int lastNewsfeedTabIdx = 0; // 上一次newsfeed 的 tabIdx
+  int lastGarageTabIdx = 0; // 上一次garagesale 的 tabIdx
   dynamic? tabsRouter;
   dynamic? pageController;
 
 
   CommunityVmState({
     List<Map<String, dynamic>>? topSectionsData,
-    this.tabsList,
+    required this.currentPageViewIdx,
+    currentCategoryIdx,
+    lastNewsfeedTabIdx,
+    lastGarageTabIdx,
+    newsFeedTabsList,
+    garageSaleTabsList,
     this.tabsRouter,
     this.pageController,
   }) : topSectionsData = topSectionsData?? [
     {
       "title": "News Feed",
       "icon": Assets.communityNesFeed,
-      "pageStartInstanceFn": CommunityPage.startInstance,
-      "page": const CommunityPage(),
     },
     {
       "title": "Garage Sale",
       "icon": Assets.communityGarageSale,
-      "pageStartInstanceFn": GaragePage.startInstance,
-      "page": const GaragePage(),
     },
+  ],
+        currentCategoryIdx = currentCategoryIdx?? 0,
+        lastGarageTabIdx = lastGarageTabIdx?? 0,
+        lastNewsfeedTabIdx = lastNewsfeedTabIdx?? 0,
+  newsFeedTabsList = newsFeedTabsList?? [
+    "News",
+    "Following",
+
+    "For You",
+  ],
+  garageSaleTabsList = garageSaleTabsList?? [
+    "For Sale",
+    "For Rent",
   ];
 
   CommunityVmState copyWith({
     List<Map<String, dynamic>>? topSectionsData,
-    List? tabsList,
+    List<String>? newsFeedTabsList,
+    List<String>? garageSaleTabsList,
+    int? currentCategoryIdx,
+    int? currentPageViewIdx,
+    int? lastNewsfeedTabIdx,
+    int? lastGarageTabIdx,
     dynamic? tabsRouter,
     dynamic? pageController,
   }) {
     return CommunityVmState(
       topSectionsData: topSectionsData ?? this.topSectionsData,
-      tabsList: tabsList ?? this.tabsList,
+      newsFeedTabsList: newsFeedTabsList ?? this.newsFeedTabsList,
+      garageSaleTabsList: garageSaleTabsList ?? this.garageSaleTabsList,
+      currentCategoryIdx: currentCategoryIdx ?? this.currentCategoryIdx,
+      currentPageViewIdx: currentPageViewIdx ?? this.currentPageViewIdx,
+      lastNewsfeedTabIdx: lastNewsfeedTabIdx ?? this.lastNewsfeedTabIdx,
+      lastGarageTabIdx: lastGarageTabIdx ?? this.lastGarageTabIdx,
       tabsRouter: tabsRouter ?? this.tabsRouter,
       pageController: pageController ?? this.pageController,
     );

+ 156 - 19
packages/cpt_community/lib/modules/community/community_vm.dart

@@ -1,14 +1,22 @@
 
-import 'package:cpt_community/modules/garage/garage_page.dart';
+import 'package:cpt_community/components/comments_dialog.dart';
 import 'package:cpt_community/router/page/community_page_router.dart';
 import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
 import 'package:flutter/cupertino.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:router/path/router_path.dart';
 import 'package:shared/utils/log_utils.dart';
 import 'package:auto_route/auto_route.dart';
+import 'package:widgets/dialog/app_custom_dialog.dart';
+import 'package:widgets/my_checkbox_group.dart';
 
+import '../garage/garagesale_post/garagesale_post_page.dart';
+import '../my_following/my_following_page.dart';
+import '../my_posts/my_posts_page.dart';
+import 'community_page.dart';
 import 'community_state.dart';
 import 'newsfeed_post/newsfeed_post_page.dart';
 
@@ -16,21 +24,31 @@ part 'community_vm.g.dart';
 
 @riverpod
 class CommunityVm extends _$CommunityVm {
-  late PageController _pageController;
   get topSectionsData => state.topSectionsData;
 
+
   CommunityVmState initState() {
     return CommunityVmState(
-      tabsList: ["News", "Following", "For You"],
+      currentCategoryIdx: 0,
+      currentPageViewIdx: 0,
+      lastGarageTabIdx: 0,
+      lastNewsfeedTabIdx: 0,
+      newsFeedTabsList: [
+        "News",
+        "Following",
+        "For You",
+      ],
+      garageSaleTabsList: [
+        "For Sale",
+        "For Rent",
+      ],
     );
   }
 
   @override
   CommunityVmState build(){
-
     final state = initState();
 
-
     // 第一帧渲染完成
 
     WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
@@ -41,32 +59,151 @@ class CommunityVm extends _$CommunityVm {
     return state;
   }
 
+  tabsRouterChange(){
+    Log.d("----tabsRouterChange---${tabsRouterKey.currentState?.controller?.activeIndex}-");
+    state = state.copyWith(currentPageViewIdx: tabsRouterKey.currentState?.controller?.activeIndex ?? 0);
+  }
 
   // 切换tab
-  handlerChangeTab(int index, tabsRouter) {
+  handlerChangeTab(int tabIndex, TabsRouter? tabsRouter, int? categoryIdx) {
+    tabsRouter = (tabsRouter?? tabsRouterKey.currentState?.controller)!;
+    categoryIdx = categoryIdx ?? state.currentCategoryIdx;
+    if(categoryIdx == 0){
+      tabsRouter.setActiveIndex(tabIndex);
+    }else {
+      tabsRouter.setActiveIndex(state.newsFeedTabsList!.length + tabIndex);
+    }
+  }
+
+  // 切换news feed和garage sale
+  handlerSwitchNewsfeedOrGaragesale( int categoryIdx, BuildContext? context, TabsRouter? tabsRouter){
+    tabsRouter = (tabsRouter?? tabsRouterKey.currentState?.controller)!;
+    categoryIdx = categoryIdx;
+    if(categoryIdx == 0){
+      tabsRouter.setActiveIndex(state.lastNewsfeedTabIdx);
+    }else if (categoryIdx == 1){
+      tabsRouter.setActiveIndex(state.newsFeedTabsList!.length + state.lastGarageTabIdx);
+    }
+  }
+
+  // 设置当前的cat类型
+  setCurrentCategoryIdx(BuildContext? context, int categoryIdx, int? lastNewsfeedTabIdx, int? lastGarageTabIdx){
+    state = state.copyWith(
+        currentCategoryIdx: categoryIdx,
+        lastNewsfeedTabIdx: lastNewsfeedTabIdx?? state.lastNewsfeedTabIdx,
+        lastGarageTabIdx: lastGarageTabIdx?? state.lastGarageTabIdx
+    );
+  }
+
+  // 判断当前pageview 页面正处于显示状态
+  Future isCurrentPageViewShowing(int pageViewIdx) async{
+    // 延迟获取结果
+    bool isShowing = await Future.delayed(const Duration(milliseconds: 500), (){
+      return state.currentPageViewIdx == pageViewIdx;
+    });
+    return isShowing;
+  }
 
-    tabsRouter.setActiveIndex(index);
-    // state = state.copyWith(tabsRouter: tabsRouter ,activeTabIdx: index);
-    Log.d("community_vm handlerChangeTab--index:    $index");
+  // 选择 garage sale 导航栏点击 选择分类
+  handlerChooseGarageCategory(BuildContext context) async {
+    List<Map<String, dynamic>> categoryList = [
+      {
+        'id': '1',
+        'label': 'Kids',
+      },
+      {
+        'id': '2',
+        'label': 'Homeware',
+      },
+      {
+        'id': '3',
+        'label': 'Fashion',
+      },
+      {
+        'id': '4',
+        'label': 'Electronics',
+      },
+      {
+        'id': '5',
+        'label': 'Sports',
+      },
+      {
+        'id': '6',
+        'label': 'Furniture',
+      },
+      {
+        'id': '7',
+        'label': 'Others',
+      },
+    ];
+    await DialogEngine.show(
+        tag: "chooseGarageSaleCategory",
+        position: DialogPosition.center,
+        widget: AppCustomDialog(
+          message: '',
+          dialogWidth: MediaQuery.of(context).size.width * 0.8,
+          // contentBoxMaxHeight: 350,
+          // contentBoxMinHeight: 300,
+          confirmTxt: "Ok",
+          messageBuilder: (BuildContext context){
+            return Container(
+              color: context.appColors.textWhite,
+              child: Column(
+                mainAxisAlignment: MainAxisAlignment.start,
+                crossAxisAlignment: CrossAxisAlignment.start,
+                children: [
+                  MyCheckboxGroup(
+                      isSingleSelect: false,
+                      labelStyle: const TextStyle(
+                        fontSize: 16,
+                        fontWeight: FontWeight.w500,
+                      ),
+                      items: categoryList,
+                      onChanged: (List<Map<String, dynamic>> selectedItems){
+                        Log.d("----MyCheckboxGroup onChanged  $selectedItems");
+                      }
+                  )
+                ],
+              ),
+            );
+          },
+          isShowCancelBtn:false,
+          confirmAction: (){
+
+          },
+        )
+    );
   }
 
+  // 点击了导航栏的 like btn
+  handlerClickNavbarLikeBtn(BuildContext? context){
+    if(state.currentCategoryIdx ==0){
+      //
+      ToastEngine.show("点击了 newsfeed like");
+    }else if(state.currentCategoryIdx == 1){
+      //
+      ToastEngine.show("点击了 garagesale like");
 
-  handlerChangeCommunityType(BuildContext context, int index){
-    if(index == 1){
-      Log.d("去往garage 页面");
-      // 跳转到 garage sale 页面
-      GaragePage.startInstance(context: context);
-      // AutoRouter.of(context).push(const GaragePageRoute());
     }
   }
 
-  // 点击发布的按钮 跳转到发布的页面
-  void handlerGotoPost(context){
-    // ComponentServiceManager().communityService.startCommunityPage();
+  // 点击了导航栏的 filter btn
+  handlerClickNavbarFilterBtn(BuildContext? context,){
+    ToastEngine.show("点击 filter");
+    handlerChooseGarageCategory(context!);
+  }
 
+  // 点击发布的按钮 跳转到 newsfeed 发布的页面
+  void handlerGotoNewsfeedPost(context){
     // AutoRouter.of(context).pushNamed(RouterPath.newsFeedPost);
-
     NewsfeedPostPage.startInstance();
+    // MyPostsPage.startInstance();
+    // MyFollowingPage.startInstance();
+  }
+
+  // 点击发布的按钮 跳转到garagesale 发布的页面
+  void handlerGotoGaragePost(context){
+    GaragesalePostPage.startInstance();
   }
 }
 

+ 1 - 1
packages/cpt_community/lib/modules/community/community_vm.g.dart

@@ -6,7 +6,7 @@ part of 'community_vm.dart';
 // RiverpodGenerator
 // **************************************************************************
 
-String _$communityVmHash() => r'5d1bae55bf25ed93006f6e35a20a27627183f458';
+String _$communityVmHash() => r'9d2a5247072e1a6032db058cc0f21591bc739d07';
 
 /// See also [CommunityVm].
 @ProviderFor(CommunityVm)

+ 2 - 2
packages/cpt_community/lib/modules/community/customSilverHeaderTabs.dart

@@ -6,10 +6,10 @@ class CustomSliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegat
    CustomSliverPersistentHeaderDelegate({required this.child});
 
    @override
-   double get minExtent => 50.0; // 最小高度
+   double get minExtent => 100.0; // 最小高度
 
    @override
-   double get maxExtent => 50.0; // 最大高度
+   double get maxExtent => 100.0; // 最大高度
 
    @override
    Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {

+ 156 - 5
packages/cpt_community/lib/modules/community/following/following_page.dart

@@ -6,6 +6,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:router/ext/auto_router_extensions.dart';
 import 'package:shared/utils/color_utils.dart';
 import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_button.dart';
 import 'package:widgets/my_load_image.dart';
 import 'package:widgets/ext/ex_widget.dart';
 import 'package:widgets/my_text_view.dart';
@@ -13,6 +15,9 @@ import 'package:widgets/my_appbar.dart';
 import 'package:cs_resources/theme/app_colors_theme.dart';
 import 'package:widgets/widget_export.dart';
 
+import '../../../components/newfeed_card_header.dart';
+import '../../../components/newsfeed_card_content.dart';
+import '../../../components/newsfeed_card_footer.dart';
 import '../../../router/page/community_page_router.dart';
 import 'following_vm.dart';
 
@@ -31,18 +36,164 @@ class FollowingPage extends HookConsumerWidget {
 
   @override
   Widget build(BuildContext context, WidgetRef ref) {
-    return const Scaffold(
+    final vm = ref.read(followingVmProvider.notifier);
+    final state = ref.watch(followingVmProvider);
+    useEffect((){
+      // 组件挂载时执行 - 执行接口请求
+      Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+      };
+    }, []);
+    return Scaffold(
       // appBar: MyAppBar.appBar(
       //   context,
       //   "following",
       //   backgroundColor: context.appColors.whiteBG,
       // ),
       // backgroundColor: ColorUtils.string2Color("#F2F3F6"),
-      body: Column(
+        body: Column(
+          mainAxisSize: MainAxisSize.max,
+          children: [
+            Expanded(
+              child: EasyRefresh(
+                controller: vm.refreshController,
+                key: UniqueKey(),
+                onLoad: () async{
+                  Log.d("----onLoad");
+                  vm.loadMore();
+                },
+                onRefresh: () async{
+                  Log.d("----onRefresh");
+                  vm.onRefresh();
+                },
+                child: LoadStateLayout(
+                  state: state.loadingState,
+                  errorMessage: state.errorMessage,
+                  errorRetry: () {
+                    vm.retryRequest();
+                  },
+                  successSliverWidget: [
+                    SliverList(
+                      delegate: SliverChildBuilderDelegate(
+                              (context, index){
+                            return _buildNewsItem(context, ref, state.list![index], vm);
+                          },
+                          childCount: state.list!.length
+                      ),
+                    )
+                  ],
+                ),
+              ),
+            )
+          ],
+        )
+    );
+  }
+
+  Widget _buildNewsItem(BuildContext context, WidgetRef ref, item, vm){
+    return Container(
+      margin: const EdgeInsets.only(left: 15, right: 15,top: 14,bottom: 14),
+      padding: const EdgeInsets.only(left: 15, right: 15,top: 17,bottom: 0),
+      // height: 280,
+      decoration: BoxDecoration(
+          color: context.appColors.textWhite,
+          borderRadius: BorderRadius.circular(10),
+          boxShadow: [
+            BoxShadow(
+              color: ColorUtils.string2Color("#E4E7EB").withOpacity(0.5),
+              spreadRadius: 0,
+              blurRadius: 4,
+              offset: const Offset(0, 4), // changes position of shadow
+            ),
+          ]
+      ),
+      child: Stack(
         children: [
-          Text("newspage-following"),
+          Column(
+              mainAxisAlignment: MainAxisAlignment.center,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+                // 卡片头部(头像 标题 时间)
+                Container(
+                  child: NewsFeedCardHeader(
+                    key: UniqueKey(),
+                    title: item['title'],
+                    avator: item['avator'],
+                    time: item['time'],
+                  ),
+                ),
+                const SizedBox(height: 15),
+                // 卡片中间 (文字和图片)
+                NewsFeedCardContent(
+                  key: UniqueKey(),
+                  content: item['content'],
+                  imageUrls: item['imageUrls'],
+                ),
+                const SizedBox(height: 16),
+                // // 卡片底部 (点赞 评论 分享)
+                Container(
+                  decoration: BoxDecoration(
+                    // color: Colors.white,
+                      border: Border(
+                        top: BorderSide(color: context.appColors.dividerDefault, width: 0.5),
+                      )
+                  ),
+                  padding: const EdgeInsets.only(top: 10, bottom: 15),
+                  child: NewsFeedCardFooter(
+                    key: UniqueKey(),
+                    isLike: item['isLike'],
+                    onLike: (){
+                      vm.handlerClickActionBtn('like', item);
+                    },
+                    onComment: (){
+                      vm.handlerClickActionBtn('comments', item);
+                    },
+                    onShare: (){
+                      vm.handlerClickActionBtn('share', item);
+                    },
+                  ),
+                ),
+              ]
+          ),
+          // 右上角 关注/取消关注 按钮
+          Visibility(
+            visible: !item['isFollow'],
+            child: Positioned(
+                right: 10,
+                top: -5,
+                child: Container(
+                  width: 83.5,
+                  height: 45.5,
+                  alignment: Alignment.center,
+                  // decoration: BoxDecoration(
+                  //   color:  ColorUtils.string2Color('#4161D0'),
+                  //   borderRadius: BorderRadius.circular(5),
+                  // ),
+                  child: MyButton(
+                    text: '+Follow',
+                    textColor: Colors.white,
+                    backgroundColor: ColorUtils.string2Color('#4161D0'),
+                    radius: 8,
+                    minHeight: 27.5,
+                    padding: const EdgeInsets.only(left: 5, right: 5,top:9,bottom:9),
+                    fontWeight: FontWeight.w400,
+                    fontSize: 14,
+                    onPressed: (){
+                      // Navigator.pop(context);
+                    },
+                  ),
+                )
+            ),
+          )
         ],
-      )
-    );
+      ),
+    ).constrained(
+      width: double.infinity,
+      // height: 580,
+      // minHeight: 300,
+    ).onTap((){
+      vm.handlerClickActionBtn(null, item);
+    });
   }
 }

+ 39 - 14
packages/cpt_community/lib/modules/community/following/following_state.dart

@@ -1,35 +1,60 @@
+import 'package:widgets/load_state_layout.dart';
+
 class FollowingState {
-  int? activeTabIndex =0;
-  int? curPage =0;
-  int? pageSize =10;
-  int? filterCount =0;
-  List<String>? tabsList = ['News', 'Following', 'For You'];
+  //页面 LoadView 状态的展示
+  LoadState loadingState;
+  String? errorMessage;
+  int? curPage;
+  int? pageSize = 10;
+  int? filterCount = 0;
   List<Map<String, dynamic>>? list = [];
 
   FollowingState({
-    this.activeTabIndex,
-    this.curPage,
-    this.pageSize,
-    this.filterCount,
-    this.tabsList,
+    this.loadingState = LoadState.State_Loading,
+    String? errorMessage,
+    this.curPage = 1,
+    this.pageSize = 10,
+    this.filterCount = 0,
     this.list,
   });
 
   FollowingState copyWith({
-    int? activeTabIndex,
+    LoadState? loadingState,
+    String? errorMessage,
     int? curPage,
     int? pageSize,
     int? filterCount,
-    List<String>? tabsList,
     List<Map<String, dynamic>>? list,
   }) {
     return FollowingState(
-      activeTabIndex: activeTabIndex ?? this.activeTabIndex,
+      loadingState: loadingState ?? this.loadingState,
+      errorMessage: errorMessage ?? this.errorMessage,
       curPage: curPage ?? this.curPage,
       pageSize: pageSize ?? this.pageSize,
       filterCount: filterCount ?? this.filterCount,
-      tabsList: tabsList ?? this.tabsList,
       list: list ?? this.list,
     );
   }
+
+  Map<String, dynamic> toMap() {
+    return {
+      'loadingState': this.loadingState,
+      'errorMessage': this.errorMessage,
+      'curPage': this.curPage,
+      'pageSize': this.pageSize,
+      'filterCount': this.filterCount,
+      'list': this.list,
+    };
+  }
+
+  factory FollowingState.fromMap(Map<String, dynamic> map) {
+    return FollowingState(
+      loadingState: map['loadingState'] as LoadState,
+      errorMessage: map['errorMessage'] as String,
+      curPage: map['curPage'] as int,
+      pageSize: map['pageSize'] as int,
+      filterCount: map['filterCount'] as int,
+      list: map['list'] as List<Map<String, dynamic>>,
+    );
+  }
 }

+ 192 - 4
packages/cpt_community/lib/modules/community/following/following_vm.dart

@@ -2,14 +2,27 @@ import 'package:cs_resources/generated/assets.dart';
 import 'package:flutter/cupertino.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:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
 
+import '../../../router/page/community_page_router.dart';
+import '../community_pageview_idx_data.dart';
+import '../community_vm.dart';
 import 'following_state.dart';
 
 part 'following_vm.g.dart';
 
 @riverpod
 class FollowingVm extends _$FollowingVm {
+  bool _needShowPlaceholder = true; //是否展示LoadingView
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,  //允许刷新
+    controlFinishLoad: true,   //允许加载
+  );
 
   FollowingState initState() {
     return FollowingState(
@@ -20,7 +33,7 @@ class FollowingVm extends _$FollowingVm {
             'title': 'William Jefferson',
             'isFollow': false,
             'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-            'imageUrls': ['https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg'],
+            'imageUrls': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
             'time': 'June 17,2016 at 7:23 p.m.',
               'isLike': true,
             'likeno': 12
@@ -103,8 +116,183 @@ class FollowingVm extends _$FollowingVm {
     return state;
   }
 
-   // 设置当前的 tabsRouter 和 pageController
-  Future setTabsRouterAndPageController(dynamic tabsRouter, dynamic pageController) async{
-    Log.d("setTabsRouterAndPageController---:$tabsRouter");
+  //刷新页面状态
+  void changeLoadingState(LoadState loadState, String? errorMsg) {
+    state = state.copyWith(
+        loadingState: loadState,
+        errorMessage: errorMsg
+    );
+  }
+
+  // 初始化页面数据
+  initPageData() {
+    Log.d("----following_vm-----initPageData   ${state.loadingState}");
+    onRefresh();
+  }
+
+  // 上拉加载 更多
+  Future loadMore() async {
+    bool isShowing = await ref.read(communityVmProvider.notifier).isCurrentPageViewShowing(CommunityPageViewIdxData.following);
+    if(isShowing){
+      Log.d("----following_vm-----loadMore");
+      // await Future.delayed(const Duration(seconds: 2));
+      // if(state.list.length >= state.filterCount){
+      //   return;
+      // }else {
+      //   int curPage = state.curPage + 1;
+      //   state = state.copyWith(curPage: curPage,);
+      //   getListData();
+      // }
+      // 检查 curPage 是否为 null,并初始化为 1
+      int newCurPage = state.curPage ?? 1;
+      state = state.copyWith(curPage: ++newCurPage);
+      getListData();
+    }else {
+      refreshController.finishRefresh();
+    }
+  }
+
+
+  // 下拉刷新
+  Future onRefresh() async {
+    bool isShowing = await ref.read(communityVmProvider.notifier).isCurrentPageViewShowing(CommunityPageViewIdxData.following);
+    if(isShowing){
+      // 当前pageView 页面正处于显示状态
+      Log.d("----following_vm-----onRefresh ");
+      // await Future.delayed(const Duration(seconds: 2));
+      state = state.copyWith(curPage: 1);
+      getListData();
+    }else {
+      refreshController.finishRefresh();
+      // //更新展示的状态
+      changeLoadingState(LoadState.State_Success, null);
+    }
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    bool isShowing = await ref.read(communityVmProvider.notifier).isCurrentPageViewShowing(CommunityPageViewIdxData.following);
+    if(isShowing){
+      state = state.copyWith(curPage: 1);
+      _needShowPlaceholder = true;
+      getListData();
+    }
+  }
+
+
+  // 获取list 列表数据
+  Future getListData<T>() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading, null);
+    }
+
+    Log.d("for_sale加载listData数据---------------start--${state.curPage}---");
+    //   try {
+    //     //请求网络
+    //     Map<String, dynamic>  params = {
+    //       "curPage": state.curPage,
+    //       "pageSize": state.pageSize,
+    //     };
+    //     Log.d("请求参数------$params");
+    //     final result = await propertyNewsRepository.fetchPropertyNewsList(params);
+    //     Log.d("请求完成结果------${result.data}");
+    //     //校验成功失败
+    //     if (result.isSuccess) {
+    //       // state = state.copyWith(serverTime: result.data);
+    //       state = state;
+    //   handleList(listResult.data?.rows);
+    //       ToastEngine.show("获取数据成功");
+    //     } else {
+    //   errorMessage = listResult.errorMsg;
+    //   changeLoadingState(LoadState.State_Error);
+    //       ToastEngine.show(result.errorMsg ?? "Network Load Error");
+    //     }
+    //   } catch (e) {
+    //     ToastEngine.show("Error: $e");
+    //   }
+
+    await Future.delayed(const Duration(milliseconds: 1500));
+    final List<Map<String, dynamic>> listData = [
+      {
+        'id':1,
+        'avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500',
+        'title': 'William Jefferson',
+        'isFollow': false,
+        'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
+        'imageUrls': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'time': 'June 17,2016 at 7:23 p.m.',
+        'isLike': true,
+        'likeno': 12
+      },
+      {
+        'id':2,
+        'avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500',
+        'title': 'William fdsaf的飞洒发生的',
+        'isFollow': true,
+        'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
+        'imageUrls': [],
+        'time': 'June 17,2016 at 7:23 p.m.',
+        'isLike': true,
+        'likeno': 12
+      },
+      {
+        'id':3,
+        'avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500',
+        'title': 'Fsjfkds  dfsk',
+        'isFollow': false,
+        'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
+        'imageUrls': [],
+        'time': 'June 17,2016 at 7:23 p.m.',
+        'isLike': false,
+        'likeno': 12
+      },
+    ];
+
+    if (state.curPage == 1) {
+      //刷新的方式
+      state = state.copyWith(list: listData);
+      refreshController.finishRefresh();
+      // //更新展示的状态
+      changeLoadingState(LoadState.State_Success, null);
+    } else {
+      //加载更多
+      final allList = state.list;
+      allList?.addAll(listData);
+      state = state.copyWith(list: allList);
+      refreshController.finishLoad();
+
+    }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+  }
+
+
+  // 点击 like comments  share
+  void handlerClickActionBtn(String? actionStr, item){
+    final id = item['id'];
+    switch (actionStr) {
+      case 'like':
+        Log.d("点击了 点赞");
+        handlerGotoDetail(id);
+        break;
+      case 'comments':
+        Log.d("点击了 评论");
+        handlerGotoDetail(id);
+        break;
+      case 'share':
+        Log.d("点击了 分享");
+        handlerGotoDetail(id);
+        break;
+      default:
+        Log.d("点击了卡片");
+        handlerGotoDetail(id);
+        break;
+    }
+  }
+  // 去详情页面
+  void handlerGotoDetail(id){
+    Log.d("去详情页面");
+    appRouter.push(NewsfeedDetailPageRoute(id: id, type:'following'));
   }
 }

+ 1 - 1
packages/cpt_community/lib/modules/community/following/following_vm.g.dart

@@ -6,7 +6,7 @@ part of 'following_vm.dart';
 // RiverpodGenerator
 // **************************************************************************
 
-String _$followingVmHash() => r'f9d603ab33e1ad9ea677604484055c3072167f74';
+String _$followingVmHash() => r'd1095a72d4591c463b6a6ef58602c117149075cb';
 
 /// See also [FollowingVm].
 @ProviderFor(FollowingVm)

+ 141 - 17
packages/cpt_community/lib/modules/community/foryou/foryou_page.dart

@@ -6,6 +6,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:router/ext/auto_router_extensions.dart';
 import 'package:shared/utils/color_utils.dart';
 import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_button.dart';
 import 'package:widgets/my_load_image.dart';
 import 'package:widgets/ext/ex_widget.dart';
 import 'package:widgets/my_text_view.dart';
@@ -13,7 +15,11 @@ import 'package:widgets/my_appbar.dart';
 import 'package:cs_resources/theme/app_colors_theme.dart';
 import 'package:widgets/widget_export.dart';
 
+import '../../../components/newfeed_card_header.dart';
+import '../../../components/newsfeed_card_content.dart';
+import '../../../components/newsfeed_card_footer.dart';
 import '../../../router/page/community_page_router.dart';
+import '../community_vm.dart';
 import 'foryou_vm.dart';
 
 @RoutePage()
@@ -29,9 +35,18 @@ class ForyouPage extends HookConsumerWidget {
     }
   }
 
+
   @override
   Widget build(BuildContext context, WidgetRef ref) {
     final vm = ref.read(foryouVmProvider.notifier);
+    final state = ref.watch(foryouVmProvider);
+    useEffect((){
+      // 组件挂载时执行 - 执行接口请求
+      Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+      };
+    }, []);
     return Scaffold(
       // appBar: MyAppBar.appBar(
       //   context,
@@ -40,20 +55,37 @@ class ForyouPage extends HookConsumerWidget {
       // ),
       // backgroundColor: ColorUtils.string2Color("#F2F3F6"),
         body: Column(
+          mainAxisSize: MainAxisSize.max,
           children: [
             Expanded(
               child: EasyRefresh(
-                // 上拉加载
+                controller: vm.refreshController,
+                key: UniqueKey(),
                 onLoad: () async{
                   Log.d("----onLoad");
-                  vm.onLoadData();
+                  vm.loadMore();
                 },
-                // 下拉刷新
                 onRefresh: () async{
                   Log.d("----onRefresh");
-                  vm.refreshListData();
+                  vm.onRefresh();
                 },
-                child: _buildForyouList(context, ref, vm),
+                child: LoadStateLayout(
+                  state: state.loadingState,
+                  errorMessage: state.errorMessage,
+                  errorRetry: () {
+                    vm.retryRequest();
+                  },
+                  successSliverWidget: [
+                    SliverList(
+                      delegate: SliverChildBuilderDelegate(
+                              (context, index){
+                            return _buildNewsItem(context, ref, state.list![index], vm);
+                          },
+                          childCount: state.list!.length
+                      ),
+                    )
+                  ],
+                ),
               ),
             )
           ],
@@ -62,18 +94,110 @@ class ForyouPage extends HookConsumerWidget {
   }
 
 
-  Widget _buildForyouList(BuildContext context, WidgetRef ref, ForyouVm vm) {
-
-    return ListView.builder(
-      itemCount: 3,
-      itemBuilder: (context, index) {
-        return _buildListItem(context, ref, vm, index);
-      },
-    );
-  }
-
-  Widget _buildListItem(BuildContext context, WidgetRef ref, ForyouVm vm, int index) {
-    return Text("list-item");
+  Widget _buildNewsItem(BuildContext context, WidgetRef ref, item, vm){
+    return Container(
+      margin: const EdgeInsets.only(left: 15, right: 15,top: 14,bottom: 14),
+      padding: const EdgeInsets.only(left: 15, right: 15,top: 17,bottom: 0),
+      // height: 280,
+      decoration: BoxDecoration(
+          color: context.appColors.textWhite,
+          borderRadius: BorderRadius.circular(10),
+          boxShadow: [
+            BoxShadow(
+              color: ColorUtils.string2Color("#E4E7EB").withOpacity(0.5),
+              spreadRadius: 0,
+              blurRadius: 4,
+              offset: const Offset(0, 4), // changes position of shadow
+            ),
+          ]
+      ),
+      child: Stack(
+        children: [
+          Column(
+              mainAxisAlignment: MainAxisAlignment.center,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+                // 卡片头部(头像 标题 时间)
+                Container(
+                  child: NewsFeedCardHeader(
+                    key: UniqueKey(),
+                    title: item['title'],
+                    avator: item['avator'],
+                    time: item['time'],
+                  ),
+                ),
+                const SizedBox(height: 15),
+                // 卡片中间 (文字和图片)
+                NewsFeedCardContent(
+                  key: UniqueKey(),
+                  content: item['content'],
+                  imageUrls: item['imageUrls'],
+                ),
+                const SizedBox(height: 16),
+                // // 卡片底部 (点赞 评论 分享)
+                Container(
+                  decoration: BoxDecoration(
+                    // color: Colors.white,
+                      border: Border(
+                        top: BorderSide(color: context.appColors.dividerDefault, width: 0.5),
+                      )
+                  ),
+                  padding: const EdgeInsets.only(top: 10, bottom: 15),
+                  child: NewsFeedCardFooter(
+                    key: UniqueKey(),
+                    isLike: item['isLike'],
+                    onLike: (){
+                      vm.handlerClickActionBtn('like', item);
+                    },
+                    onComment: (){
+                      vm.handlerClickActionBtn('comments', item);
+                    },
+                    onShare: (){
+                      vm.handlerClickActionBtn('share', item);
+                    },
+                  ),
+                ),
+              ]
+          ),
+          // 右上角 关注/取消关注 按钮
+          Visibility(
+            visible: !item['isFollow'],
+            child: Positioned(
+                right: 10,
+                top: -5,
+                child: Container(
+                  width: 83.5,
+                  height: 45.5,
+                  alignment: Alignment.center,
+                  // decoration: BoxDecoration(
+                  //   color:  ColorUtils.string2Color('#4161D0'),
+                  //   borderRadius: BorderRadius.circular(5),
+                  // ),
+                  child: MyButton(
+                    text: '+Follow',
+                    textColor: Colors.white,
+                    backgroundColor: ColorUtils.string2Color('#4161D0'),
+                    radius: 8,
+                    minHeight: 27.5,
+                    padding: const EdgeInsets.only(left: 5, right: 5,top:9,bottom:9),
+                    fontWeight: FontWeight.w400,
+                    fontSize: 14,
+                    onPressed: (){
+                      // Navigator.pop(context);
+                    },
+                  ),
+                )
+            ),
+          )
+        ],
+      ),
+    ).constrained(
+      width: double.infinity,
+      // height: 580,
+      // minHeight: 300,
+    ).onTap((){
+      vm.handlerClickActionBtn(null, item);
+    });
   }
 
 }

+ 22 - 18
packages/cpt_community/lib/modules/community/foryou/foryou_state.dart

@@ -1,56 +1,60 @@
+import 'package:widgets/load_state_layout.dart';
+
 class ForyouState {
-  int? activeTabIndex =0;
-  int? curPage =0;
-  int? pageSize =10;
-  int? filterCount =0;
-  List<String>? tabsList = ['News', 'Following', 'For You'];
+  //页面 LoadView 状态的展示
+  LoadState loadingState;
+  String? errorMessage;
+  int? curPage;
+  int? pageSize = 10;
+  int? filterCount = 0;
+
   List<Map<String, dynamic>>? list = [];
 
   ForyouState({
-    this.activeTabIndex,
-    this.curPage,
-    this.pageSize,
-    this.filterCount,
-    this.tabsList,
+    this.loadingState = LoadState.State_Loading,
+    String? errorMessage,
+    this.curPage = 1,
+    this.pageSize = 10,
+    this.filterCount = 0,
     this.list,
   });
 
   ForyouState copyWith({
-    int? activeTabIndex,
+    LoadState? loadingState,
+    String? errorMessage,
     int? curPage,
     int? pageSize,
     int? filterCount,
-    List<String>? tabsList,
     List<Map<String, dynamic>>? list,
   }) {
     return ForyouState(
-      activeTabIndex: activeTabIndex ?? this.activeTabIndex,
+      loadingState: loadingState ?? this.loadingState,
+      errorMessage: errorMessage ?? this.errorMessage,
       curPage: curPage ?? this.curPage,
       pageSize: pageSize ?? this.pageSize,
       filterCount: filterCount ?? this.filterCount,
-      tabsList: tabsList ?? this.tabsList,
       list: list ?? this.list,
     );
   }
 
   Map<String, dynamic> toMap() {
     return {
-      'activeTabIndex': this.activeTabIndex,
+      'loadingState': this.loadingState,
+      'errorMessage': this.errorMessage,
       'curPage': this.curPage,
       'pageSize': this.pageSize,
       'filterCount': this.filterCount,
-      'tabsList': this.tabsList,
       'list': this.list,
     };
   }
 
   factory ForyouState.fromMap(Map<String, dynamic> map) {
     return ForyouState(
-      activeTabIndex: map['activeTabIndex'] as int,
+      loadingState: map['loadingState'] as LoadState,
+      errorMessage: map['errorMessage'] as String,
       curPage: map['curPage'] as int,
       pageSize: map['pageSize'] as int,
       filterCount: map['filterCount'] as int,
-      tabsList: map['tabsList'] as List<String>,
       list: map['list'] as List<Map<String, dynamic>>,
     );
   }

+ 142 - 46
packages/cpt_community/lib/modules/community/foryou/foryou_vm.dart

@@ -4,8 +4,12 @@ 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:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
 
 import '../../../router/page/community_page_router.dart';
+import '../community_pageview_idx_data.dart';
+import '../community_vm.dart';
 import '../newsfeed_detail/newsfeed_detail_page.dart';
 import 'foryou_respository.dart';
 import 'foryou_state.dart';
@@ -15,6 +19,14 @@ part 'foryou_vm.g.dart';
 @riverpod
 class ForyouVm extends _$ForyouVm {
   late ForyouRepository ForyouRepositoryInstance;
+  bool _needShowPlaceholder = true; //是否展示LoadingView
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,  //允许刷新
+    controlFinishLoad: true,   //允许加载
+  );
+
   ForyouState initState() {
     return ForyouState(
         list: [
@@ -24,7 +36,7 @@ class ForyouVm extends _$ForyouVm {
             'title': 'William Jefferson',
             'isFollow': false,
             'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-            'imageUrls': ['https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg'],
+            'imageUrls': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
             'time': 'June 17,2016 at 7:23 p.m.',
             'isLike': true,
             'likeno': 12
@@ -109,62 +121,144 @@ class ForyouVm extends _$ForyouVm {
     return state;
   }
 
-  // 设置当前的 tabsRouter 和 pageController
-  Future setTabsRouterAndPageController(dynamic tabsRouter, dynamic pageController) async{
-    Log.d("setTabsRouterAndPageController---:$tabsRouter");
+  //刷新页面状态
+  void changeLoadingState(LoadState loadState, String? errorMsg) {
+    state = state.copyWith(
+        loadingState: loadState,
+        errorMessage: errorMsg
+    );
   }
 
-// 上拉加载
-  Future onLoadData() async {
-    Log.d("----Foryou_vm-----initListData");
-    // await Future.delayed(const Duration(seconds: 2));
-    // if(state.list.length >= state.filterCount){
-    //   return;
-    // }else {
-    //   int curPage = state.curPage + 1;
-    //   state = state.copyWith(curPage: curPage,);
-    //   getListData();
-    // }
-    // getListData();
+  // 初始化页面数据
+  initPageData() {
+    Log.d("----foryou_vm-----initPageData   ${state.loadingState}");
+    onRefresh();
   }
 
-  // 获取list 列表数据
-  void getListData<T>() async {
-    Log.d("加载listData数据---------------start-----");
-    try {
-      //请求网络
-      Map<String, dynamic>  params = {
-        "curPage": state.curPage,
-        "pageSize": state.pageSize,
-      };
-      Log.d("请求参数------$params");
-      final result = await ForyouRepositoryInstance.fetchForyouList(params);
-      Log.d("请求完成结果------${result.data}");
-      //校验成功失败
-      if (result.isSuccess) {
-        // state = state.copyWith(serverTime: result.data);
-        state = state;
-        ToastEngine.show("获取数据成功");
-      } else {
-        ToastEngine.show(result.errorMsg ?? "Network Load Error");
-      }
-    } catch (e) {
-      ToastEngine.show("Error: $e");
+  // 上拉加载 更多
+  Future loadMore() async {
+    bool isShowing = await ref.read(communityVmProvider.notifier).isCurrentPageViewShowing(CommunityPageViewIdxData.forYou);
+    if(isShowing){
+      Log.d("----foryou_vm-----loadMore");
+      // await Future.delayed(const Duration(seconds: 2));
+      // if(state.list.length >= state.filterCount){
+      //   return;
+      // }else {
+      //   int curPage = state.curPage + 1;
+      //   state = state.copyWith(curPage: curPage,);
+      //   getListData();
+      // }
+      // 检查 curPage 是否为 null,并初始化为 1
+      int newCurPage = state.curPage ?? 1;
+      state = state.copyWith(curPage: ++newCurPage);
+      getListData();
+    }else {
+      refreshController.finishRefresh();
     }
   }
 
 
   // 下拉刷新
-  Future refreshListData() async {
-    Log.d("----property_foryou_vm-----refreshListData ");
+  Future onRefresh() async {
+    bool isShowing = await ref.read(communityVmProvider.notifier).isCurrentPageViewShowing(CommunityPageViewIdxData.forYou);
+    if(isShowing){
+      // 当前pageView 页面正处于显示状态
+      Log.d("----forsale_vm-----onRefresh ");
+      // await Future.delayed(const Duration(seconds: 2));
+      state = state.copyWith(curPage: 1);
+      getListData();
+    }else {
+      refreshController.finishRefresh();
+      // //更新展示的状态
+      changeLoadingState(LoadState.State_Success, null);
+    }
+  }
 
-    // await Future.delayed(const Duration(seconds: 2));
+  // 重试请求
+  Future retryRequest() async {
+    bool isShowing = await ref.read(communityVmProvider.notifier).isCurrentPageViewShowing(CommunityPageViewIdxData.forYou);
+    if(isShowing){
+      state = state.copyWith(curPage: 1);
+      _needShowPlaceholder = true;
+      getListData();
+    }
+  }
 
-    state = state.copyWith(curPage: 1, pageSize: 10);
-    // ref.invalidateSelf();
-    // ref.invalidate(ForyouVmProvider);
-    getListData();
 
+  // 获取list 列表数据
+  Future getListData<T>() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading, null);
+    }
+
+    Log.d("for_sale加载listData数据---------------start--${state.curPage}---");
+    //   try {
+    //     //请求网络
+    //     Map<String, dynamic>  params = {
+    //       "curPage": state.curPage,
+    //       "pageSize": state.pageSize,
+    //     };
+    //     Log.d("请求参数------$params");
+    //     final result = await propertyNewsRepository.fetchPropertyNewsList(params);
+    //     Log.d("请求完成结果------${result.data}");
+    //     //校验成功失败
+    //     if (result.isSuccess) {
+    //       // state = state.copyWith(serverTime: result.data);
+    //       state = state;
+    //   handleList(listResult.data?.rows);
+    //       ToastEngine.show("获取数据成功");
+    //     } else {
+    //   errorMessage = listResult.errorMsg;
+    //   changeLoadingState(LoadState.State_Error);
+    //       ToastEngine.show(result.errorMsg ?? "Network Load Error");
+    //     }
+    //   } catch (e) {
+    //     ToastEngine.show("Error: $e");
+    //   }
+
+    await Future.delayed(const Duration(milliseconds: 1500));
+    final List<Map<String, dynamic>> listData = [
+      {
+        'id':1,
+        'avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500',
+        'title': 'William Jefferson',
+        'isFollow': false,
+        'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
+        'imageUrls': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'time': 'June 17,2016 at 7:23 p.m.',
+        'isLike': true,
+        'likeno': 12
+      },
+      {
+        'id':2,
+        'avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500',
+        'title': 'William fdsaf的飞洒发生的',
+        'isFollow': true,
+        'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
+        'imageUrls': [],
+        'time': 'June 17,2016 at 7:23 p.m.',
+        'isLike': true,
+        'likeno': 12
+      },
+    ];
+
+    if (state.curPage == 1) {
+      //刷新的方式
+      state = state.copyWith(list: listData);
+      refreshController.finishRefresh();
+      // //更新展示的状态
+      changeLoadingState(LoadState.State_Success, null);
+    } else {
+      //加载更多
+      final allList = state.list;
+      allList?.addAll(listData);
+      state = state.copyWith(list: allList);
+      refreshController.finishLoad();
+
+    }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
   }
 
 
@@ -185,12 +279,14 @@ class ForyouVm extends _$ForyouVm {
         handlerGotoDetail(id);
         break;
       default:
+        Log.d("点击了卡片");
+        handlerGotoDetail(id);
         break;
     }
   }
   // 去详情页面
   void handlerGotoDetail(id){
     Log.d("去详情页面");
-    appRouter.push(NewsfeedDetailPageRoute(id: id, type: 'foryou'));
+    appRouter.push(NewsfeedDetailPageRoute(id: id, type:'foryou'));
   }
 }

+ 1 - 1
packages/cpt_community/lib/modules/community/foryou/foryou_vm.g.dart

@@ -6,7 +6,7 @@ part of 'foryou_vm.dart';
 // RiverpodGenerator
 // **************************************************************************
 
-String _$foryouVmHash() => r'69331ffc075d0cb4409a72d3a08782ad534ffa8e';
+String _$foryouVmHash() => r'af8a4c5962dea938d82be432aa28ec4e704971ad';
 
 /// See also [ForyouVm].
 @ProviderFor(ForyouVm)

+ 133 - 99
packages/cpt_community/lib/modules/community/news/news_page.dart

@@ -7,6 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:router/ext/auto_router_extensions.dart';
 import 'package:shared/utils/color_utils.dart';
 import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/load_state_layout.dart';
 import 'package:widgets/my_button.dart';
 import 'package:widgets/my_load_image.dart';
 import 'package:widgets/ext/ex_widget.dart';
@@ -19,6 +20,7 @@ import '../../../components/newfeed_card_header.dart';
 import '../../../components/newsfeed_card_content.dart';
 import '../../../components/newsfeed_card_footer.dart';
 import '../../../router/page/community_page_router.dart';
+import '../community_vm.dart';
 import 'news_vm.dart';
 
 @RoutePage()
@@ -34,33 +36,60 @@ class NewsPage extends HookConsumerWidget {
     }
   }
 
+
   @override
   Widget build(BuildContext context, WidgetRef ref) {
     final vm = ref.read(newsVmProvider.notifier);
+    final state = ref.watch(newsVmProvider);
+    useEffect((){
+      // 组件挂载时执行 - 执行接口请求
+      Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+      };
+    }, []);
+
     return Scaffold(
       // appBar: MyAppBar.appBar(
       //   context,
       //   "news",
       //   backgroundColor: context.appColors.whiteBG,
       // ),
-      // backgroundColor: ColorUtils.string2Color("#F2F3F6"),
+      backgroundColor: ColorUtils.string2Color("#F2F3F6"),
       body: Column(
+        mainAxisSize: MainAxisSize.max,
         children: [
-            Expanded(
-              child: EasyRefresh(
-                // 上拉加载
-                onLoad: () async{
-                  Log.d("----onLoad");
-                  vm.onLoadData();
+          Expanded(
+            child: EasyRefresh(
+              controller: vm.refreshController,
+              key: UniqueKey(),
+              onLoad: () async{
+                Log.d("----onLoad");
+                vm.loadMore();
+              },
+              onRefresh: () async{
+                Log.d("----onRefresh");
+                vm.onRefresh();
+              },
+              child: LoadStateLayout(
+                state: state.loadingState,
+                errorMessage: state.errorMessage,
+                errorRetry: () {
+                  vm.retryRequest();
                 },
-                // 下拉刷新
-                onRefresh: () async{
-                  Log.d("----onRefresh");
-                  vm.refreshListData();
-                },
-                child: _buildNewsFeedList(context, ref, vm),
+                successSliverWidget: [
+                  SliverList(
+                    delegate: SliverChildBuilderDelegate(
+                        (context, index){
+                          return _buildNewsItem(context, ref, state.list![index], vm);
+                        },
+                        childCount: state.list!.length
+                    ),
+                  )
+                ],
               ),
-            )
+            ),
+          )
         ],
       )
     );
@@ -68,102 +97,107 @@ class NewsPage extends HookConsumerWidget {
 
   Widget _buildNewsItem(BuildContext context, WidgetRef ref, item, vm){
     return Container(
-      width: double.infinity,
-      //   color: Colors.yellow,
-      child: Stack(
-        children: [
-          Container(
-            margin: const EdgeInsets.only(left: 15, right: 15,top: 14,bottom: 14),
-            color: Colors.white,
-            padding: const EdgeInsets.only(left: 15, right: 15,top: 17,bottom: 17),
-            height: 280,
-            child: Column(
+        margin: const EdgeInsets.only(left: 15, right: 15,top: 14,bottom: 14),
+        padding: const EdgeInsets.only(left: 15, right: 15,top: 17,bottom: 0),
+        decoration: BoxDecoration(
+          color: context.appColors.textWhite,
+          borderRadius: BorderRadius.circular(10),
+          boxShadow: [
+            BoxShadow(
+              color: ColorUtils.string2Color("#E4E7EB").withOpacity(0.5),
+              spreadRadius: 0,
+              blurRadius: 4,
+              offset: const Offset(0, 4), // changes position of shadow
+            ),
+          ]
+        ),
+        child: Stack(
+          children: [
+            Column(
                 mainAxisAlignment: MainAxisAlignment.center,
-                crossAxisAlignment: CrossAxisAlignment.start,
+                crossAxisAlignment: CrossAxisAlignment.center,
                 children: [
                   // 卡片头部(头像 标题 时间)
-                  NewsFeedCardHeader(
-                    key: UniqueKey(),
-                    title: item['title'],
-                    avator: item['avator'],
-                    time: item['time'],
+                  Container(
+                    child: NewsFeedCardHeader(
+                      key: UniqueKey(),
+                      title: item['title'],
+                      avator: item['avator'],
+                      time: item['time'],
+                    ),
                   ),
                   const SizedBox(height: 15),
                   // 卡片中间 (文字和图片)
-                  Expanded(
-                    child: NewsFeedCardContent(
-                      key: UniqueKey(),
-                      content: item['content'],
-                      imageUrls: item['imageUrls'],
-                    ),
+                  NewsFeedCardContent(
+                    key: UniqueKey(),
+                    content: item['content'],
+                    imageUrls: item['imageUrls'],
                   ),
-                  const SizedBox(height: 26),
+                  const SizedBox(height: 16),
                   // // 卡片底部 (点赞 评论 分享)
-                  NewsFeedCardFooter(
-                      key: UniqueKey(),
-                      isLike: item['isLike'],
-                      onLike: (){
-                        vm.handlerClickActionBtn('like', item);
-                      },
-                      onComment: (){
-                        vm.handlerClickActionBtn('comments', item);
-                      },
-                      onShare: (){
-                        vm.handlerClickActionBtn('share', item);
-                      },
+                  Container(
+                    padding: const EdgeInsets.only(top: 10, bottom: 15),
+                    decoration: BoxDecoration(
+                      // color: Colors.white,
+                      border: Border(
+                        top: BorderSide(color: context.appColors.dividerDefault, width: 0.5),
+                      )
+                    ),
+                    child: NewsFeedCardFooter(
+                        key: UniqueKey(),
+                        isLike: item['isLike'],
+                        onLike: (){
+                          vm.handlerClickActionBtn('like', item);
+                        },
+                        onComment: (){
+                          vm.handlerClickActionBtn('comments', item);
+                        },
+                        onShare: (){
+                          vm.handlerClickActionBtn('share', item);
+                        },
+                    ),
                   ),
                 ]
             ),
-          ),
-          // 右上角 关注/取消关注 按钮
-          Visibility(
-            visible: !item['isFollow'],
-            child: Positioned(
-              right: 40,
-              top: 35,
-              child: Container(
-                width: 83.5,
-                height: 45.5,
-                alignment: Alignment.center,
-                // decoration: BoxDecoration(
-                //   color:  ColorUtils.string2Color('#4161D0'),
-                //   borderRadius: BorderRadius.circular(5),
-                // ),
-                child: MyButton(
-                  text: '+Follow',
-                  textColor: Colors.white,
-                  backgroundColor: ColorUtils.string2Color('#4161D0'),
-                  radius: 8,
-                  minHeight: 27.5,
-                  padding: const EdgeInsets.only(left: 5, right: 5,top:9,bottom:9),
-                  fontWeight: FontWeight.w400,
-                  fontSize: 14,
-                  onPressed: (){
-                    // Navigator.pop(context);
-                  },
-                ),
-              )
-            ),
-          )
-        ],
-      ),
-    );
+            // 右上角 关注/取消关注 按钮
+            Visibility(
+              visible: !item['isFollow'],
+              child: Positioned(
+                  right: 10,
+                  top: -5,
+                  child: Container(
+                    width: 83.5,
+                    height: 45.5,
+                    alignment: Alignment.center,
+                    // decoration: BoxDecoration(
+                    //   color:  ColorUtils.string2Color('#4161D0'),
+                    //   borderRadius: BorderRadius.circular(5),
+                    // ),
+                    child: MyButton(
+                      text: '+Follow',
+                      textColor: Colors.white,
+                      backgroundColor: ColorUtils.string2Color('#4161D0'),
+                      radius: 8,
+                      minHeight: 27.5,
+                      padding: const EdgeInsets.only(left: 5, right: 5,top:9,bottom:9),
+                      fontWeight: FontWeight.w400,
+                      fontSize: 14,
+                      onPressed: (){
+                        // Navigator.pop(context);
+                      },
+                    ),
+                  )
+              ),
+            )
+          ],
+        ),
+      ).constrained(
+        width: double.infinity,
+        // height: 580,
+        // minHeight: 300,
+      ).onTap((){
+      vm.handlerClickActionBtn(null, item);
+    });
   }
 
-  Widget _buildNewsFeedList(BuildContext context, WidgetRef ref, vm){
-    final itemList = vm.state.list?? [];
-    if(itemList.isEmpty){
-      return const Center(child: Text('No Data'));
-    }else {
-      List itemsList = vm.state.list.toList();
-      return ListView.builder(
-        key: UniqueKey(),
-        itemCount: itemsList.length,
-        itemBuilder: (context, index) {
-          return _buildNewsItem(context, ref, itemsList[index], vm);
-        },
-      );
-    }
-    // return Text("879424");
-  }
 }

+ 23 - 18
packages/cpt_community/lib/modules/community/news/news_state.dart

@@ -1,56 +1,61 @@
+import 'package:widgets/load_state_layout.dart';
+
 class NewsState {
-  int? activeTabIndex =0;
-  int? curPage =0;
-  int? pageSize =10;
-  int? filterCount =0;
-  List<String>? tabsList = ['News', 'Following', 'For You'];
+  //页面 LoadView 状态的展示
+  LoadState loadingState;
+  String? errorMessage;
+  int? curPage;
+  int? pageSize = 10;
+  int? filterCount = 0;
   List<Map<String, dynamic>>? list = [];
 
+
   NewsState({
-    this.activeTabIndex,
-    this.curPage,
-    this.pageSize,
-    this.filterCount,
-    this.tabsList,
+    this.loadingState = LoadState.State_Loading,
+    String? errorMessage,
+    this.curPage = 1,
+    this.pageSize = 10,
+    this.filterCount = 0,
     this.list,
   });
 
   NewsState copyWith({
-    int? activeTabIndex,
+    LoadState? loadingState,
+    String? errorMessage,
     int? curPage,
     int? pageSize,
     int? filterCount,
-    List<String>? tabsList,
     List<Map<String, dynamic>>? list,
+    List<String>? tabsList,
   }) {
     return NewsState(
-      activeTabIndex: activeTabIndex ?? this.activeTabIndex,
+      loadingState: loadingState ?? this.loadingState,
+      errorMessage: errorMessage ?? this.errorMessage,
       curPage: curPage ?? this.curPage,
       pageSize: pageSize ?? this.pageSize,
       filterCount: filterCount ?? this.filterCount,
-      tabsList: tabsList ?? this.tabsList,
       list: list ?? this.list,
     );
   }
 
   Map<String, dynamic> toMap() {
     return {
-      'activeTabIndex': this.activeTabIndex,
+      'loadingState': this.loadingState,
+      'errorMessage': this.errorMessage,
       'curPage': this.curPage,
       'pageSize': this.pageSize,
       'filterCount': this.filterCount,
-      'tabsList': this.tabsList,
       'list': this.list,
     };
   }
 
   factory NewsState.fromMap(Map<String, dynamic> map) {
     return NewsState(
-      activeTabIndex: map['activeTabIndex'] as int,
+      loadingState: map['loadingState'] as LoadState,
+      errorMessage: map['errorMessage'] as String,
       curPage: map['curPage'] as int,
       pageSize: map['pageSize'] as int,
       filterCount: map['filterCount'] as int,
-      tabsList: map['tabsList'] as List<String>,
       list: map['list'] as List<Map<String, dynamic>>,
     );
   }

+ 196 - 123
packages/cpt_community/lib/modules/community/news/news_vm.dart

@@ -4,8 +4,12 @@ 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:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
 
 import '../../../router/page/community_page_router.dart';
+import '../community_pageview_idx_data.dart';
+import '../community_vm.dart';
 import '../newsfeed_detail/newsfeed_detail_page.dart';
 import 'news_respository.dart';
 import 'news_state.dart';
@@ -15,87 +19,17 @@ part 'news_vm.g.dart';
 @riverpod
 class NewsVm extends _$NewsVm {
   late NewsRepository NewsRepositoryInstance;
+  bool _needShowPlaceholder = true; //是否展示LoadingView
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,  //允许刷新
+    controlFinishLoad: true,   //允许加载
+  );
+
   NewsState initState() {
     return NewsState(
-      list: [
-          {
-            'id':1,
-            'avator': Assets.communityCamera,
-            'title': 'William Jefferson',
-            'isFollow': false,
-            'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-            'imageUrls': ['https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg'],
-            'time': 'June 17,2016 at 7:23 p.m.',
-              'isLike': true,
-            'likeno': 12
-          },
-          {
-            'id':2,
-            'avator': Assets.communityCamera,
-            'title': 'William fdsaf的飞洒发生的',
-            'isFollow': true,
-            'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-            'imageUrls': [],
-            'time': 'June 17,2016 at 7:23 p.m.',
-            'isLike': true,
-            'likeno': 12
-          },
-          {
-            'id':3,
-            'avator': Assets.communityCamera,
-            'title': 'Fsjfkds  dfsk',
-            'isFollow': false,
-            'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-            'imageUrls': [],
-            'time': 'June 17,2016 at 7:23 p.m.',
-            'isLike': false,
-            'likeno': 12
-          },
-          {
-            'id':4,
-            'avator': Assets.communityCamera,
-            'title': 'Fsjfkds  dfsk',
-            'isFollow': false,
-            'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-            'imageUrls': [],
-            'time': 'June 17,2016 at 7:23 p.m.',
-            'isLike': false,
-            'likeno': 12
-          },
-          {
-            'id':5,
-            'avator': Assets.communityCamera,
-            'title': 'Fsjfkds  dfsk',
-            'isFollow': false,
-            'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-            'imageUrls': [],
-            'time': 'June 17,2016 at 7:23 p.m.',
-            'isLike': false,
-            'likeno': 12
-          },
-          {
-            'id':6,
-            'avator': Assets.communityCamera,
-            'title': 'Fsjfkds  dfsk',
-            'isFollow': false,
-            'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-            'imageUrls': [],
-            'time': 'June 17,2016 at 7:23 p.m.',
-            'isLike': false,
-            'likeno': 12
-          },
-          {
-            'id':7,
-            'avator': Assets.communityCamera,
-            'title': '放大发大水',
-            'isFollow': false,
-            'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-            'imageUrls': [],
-            'time': 'June 17,2016 at 7:23 p.m.',
-            'isLike': false,
-            'likeno': 12
-          },
-      ]
+      list: []
     );
   }
 
@@ -109,62 +43,199 @@ class NewsVm extends _$NewsVm {
     return state;
   }
 
-   // 设置当前的 tabsRouter 和 pageController
-  Future setTabsRouterAndPageController(dynamic tabsRouter, dynamic pageController) async{
-    Log.d("setTabsRouterAndPageController---:$tabsRouter");
+  //刷新页面状态
+  void changeLoadingState(LoadState loadState, String? errorMsg) {
+    state = state.copyWith(
+        loadingState: loadState,
+        errorMessage: errorMsg
+    );
   }
 
-// 上拉加载
-  Future onLoadData() async {
-    Log.d("----News_vm-----initListData");
-    // await Future.delayed(const Duration(seconds: 2));
-    // if(state.list.length >= state.filterCount){
-    //   return;
-    // }else {
-    //   int curPage = state.curPage + 1;
-    //   state = state.copyWith(curPage: curPage,);
-    //   getListData();
-    // }
-    // getListData();
+  // 初始化页面数据
+  initPageData() {
+    Log.d("----news_vm-----initPageData   ${state.loadingState}");
+    onRefresh();
   }
 
-  // 获取list 列表数据
-  void getListData<T>() async {
-    Log.d("加载listData数据---------------start-----");
-    try {
-      //请求网络
-      Map<String, dynamic>  params = {
-        "curPage": state.curPage,
-        "pageSize": state.pageSize,
-      };
-      Log.d("请求参数------$params");
-      final result = await NewsRepositoryInstance.fetchNewsList(params);
-      Log.d("请求完成结果------${result.data}");
-      //校验成功失败
-      if (result.isSuccess) {
-        // state = state.copyWith(serverTime: result.data);
-        state = state;
-        ToastEngine.show("获取数据成功");
-      } else {
-        ToastEngine.show(result.errorMsg ?? "Network Load Error");
-      }
-    } catch (e) {
-      ToastEngine.show("Error: $e");
+  // 上拉加载 更多
+  Future loadMore() async {
+    bool isShowing = await ref.read(communityVmProvider.notifier).isCurrentPageViewShowing(CommunityPageViewIdxData.news);
+    if(isShowing){
+      Log.d("----news_vm-----loadMore");
+      // await Future.delayed(const Duration(seconds: 2));
+      // if(state.list.length >= state.filterCount){
+      //   return;
+      // }else {
+      //   int curPage = state.curPage + 1;
+      //   state = state.copyWith(curPage: curPage,);
+      //   getListData();
+      // }
+      // 检查 curPage 是否为 null,并初始化为 1
+      int newCurPage = state.curPage ?? 1;
+      state = state.copyWith(curPage: ++newCurPage);
+      getListData();
+    }else {
+      refreshController.finishRefresh();
     }
   }
 
 
   // 下拉刷新
-  Future refreshListData() async {
-    Log.d("----property_news_vm-----refreshListData ");
+  Future onRefresh() async {
+    bool isShowing = await ref.read(communityVmProvider.notifier).isCurrentPageViewShowing(CommunityPageViewIdxData.news);
+    if(isShowing){
+      // 当前pageView 页面正处于显示状态
+      Log.d("----forsale_vm-----onRefresh ");
+      // await Future.delayed(const Duration(seconds: 2));
+      state = state.copyWith(curPage: 1);
+      getListData();
+    }else {
+      refreshController.finishRefresh();
+      // //更新展示的状态
+      changeLoadingState(LoadState.State_Success, null);
+    }
+  }
 
-    // await Future.delayed(const Duration(seconds: 2));
+  // 重试请求
+  Future retryRequest() async {
+    bool isShowing = await ref.read(communityVmProvider.notifier).isCurrentPageViewShowing(CommunityPageViewIdxData.news);
+    if(isShowing){
+      state = state.copyWith(curPage: 1);
+      _needShowPlaceholder = true;
+      getListData();
+    }
+  }
 
-    state = state.copyWith(curPage: 1, pageSize: 10);
-    // ref.invalidateSelf();
-    // ref.invalidate(NewsVmProvider);
-    getListData();
 
+  // 获取list 列表数据
+  Future getListData<T>() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading, null);
+    }
+
+    Log.d("for_sale加载listData数据---------------start--${state.curPage}---");
+    //   try {
+    //     //请求网络
+    //     Map<String, dynamic>  params = {
+    //       "curPage": state.curPage,
+    //       "pageSize": state.pageSize,
+    //     };
+    //     Log.d("请求参数------$params");
+    //     final result = await propertyNewsRepository.fetchPropertyNewsList(params);
+    //     Log.d("请求完成结果------${result.data}");
+    //     //校验成功失败
+    //     if (result.isSuccess) {
+    //       // state = state.copyWith(serverTime: result.data);
+    //       state = state;
+    //   handleList(listResult.data?.rows);
+    //       ToastEngine.show("获取数据成功");
+    //     } else {
+    //   errorMessage = listResult.errorMsg;
+    //   changeLoadingState(LoadState.State_Error);
+    //       ToastEngine.show(result.errorMsg ?? "Network Load Error");
+    //     }
+    //   } catch (e) {
+    //     ToastEngine.show("Error: $e");
+    //   }
+
+    await Future.delayed(const Duration(milliseconds: 1500));
+    final List<Map<String, dynamic>> listData = [
+      {
+        'id':1,
+        'avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500',
+        'title': 'William Jefferson',
+        'isFollow': false,
+        'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
+        'imageUrls': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'time': 'June 17,2016 at 7:23 p.m.',
+        'isLike': true,
+        'likeno': 12
+      },
+      {
+        'id':2,
+        'avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500',
+        'title': 'William fdsaf的飞洒发生的',
+        'isFollow': true,
+        'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
+        'imageUrls': [],
+        'time': 'June 17,2016 at 7:23 p.m.',
+        'isLike': true,
+        'likeno': 12
+      },
+      {
+        'id':3,
+        'avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500',
+        'title': 'Fsjfkds  dfsk',
+        'isFollow': false,
+        'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
+        'imageUrls': [],
+        'time': 'June 17,2016 at 7:23 p.m.',
+        'isLike': false,
+        'likeno': 12
+      },
+      {
+        'id':4,
+        'avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500',
+        'title': 'Fsjfkds  dfsk',
+        'isFollow': false,
+        'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
+        'imageUrls': [],
+        'time': 'June 17,2016 at 7:23 p.m.',
+        'isLike': false,
+        'likeno': 12
+      },
+      {
+        'id':5,
+        'avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500',
+        'title': 'Fsjfkds  dfsk',
+        'isFollow': false,
+        'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
+        'imageUrls': [],
+        'time': 'June 17,2016 at 7:23 p.m.',
+        'isLike': false,
+        'likeno': 12
+      },
+      {
+        'id':6,
+        'avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500',
+        'title': 'Fsjfkds  dfsk',
+        'isFollow': false,
+        'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
+        'imageUrls': [],
+        'time': 'June 17,2016 at 7:23 p.m.',
+        'isLike': false,
+        'likeno': 12
+      },
+      {
+        'id':7,
+        'avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500',
+        'title': '放大发大水',
+        'isFollow': false,
+        'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
+        'imageUrls': [],
+        'time': 'June 17,2016 at 7:23 p.m.',
+        'isLike': false,
+        'likeno': 12
+      },
+    ];
+
+    if (state.curPage == 1) {
+      //刷新的方式
+      state = state.copyWith(list: listData);
+      refreshController.finishRefresh();
+      // //更新展示的状态
+      changeLoadingState(LoadState.State_Success, null);
+    } else {
+      //加载更多
+      final allList = state.list;
+      allList?.addAll(listData);
+      state = state.copyWith(list: allList);
+      refreshController.finishLoad();
+
+    }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
   }
 
 
@@ -185,6 +256,8 @@ class NewsVm extends _$NewsVm {
         handlerGotoDetail(id);
         break;
       default:
+        Log.d("点击了卡片");
+        handlerGotoDetail(id);
         break;
     }
   }

+ 1 - 1
packages/cpt_community/lib/modules/community/news/news_vm.g.dart

@@ -6,7 +6,7 @@ part of 'news_vm.dart';
 // RiverpodGenerator
 // **************************************************************************
 
-String _$newsVmHash() => r'f5a5a082bc85c87229ff78e9b20eef1acb230831';
+String _$newsVmHash() => r'ce792a3814b2a3da4d164c030346eca5e9d77798';
 
 /// See also [NewsVm].
 @ProviderFor(NewsVm)

+ 79 - 0
packages/cpt_community/lib/modules/community/newsfeed_detail/comments_input.dart

@@ -0,0 +1,79 @@
+import 'package:cpt_community/modules/community/newsfeed_detail/newsfeed_detail_vm.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_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/my_text_view.dart';
+
+/// 多行输入框
+
+class CommentsTextareaInput extends HookConsumerWidget {
+  bool showCounter = true;
+  bool autoFocus = false;
+  CommentsTextareaInput({Key? key,this.showCounter = true, this.autoFocus = false}): super(key: key);
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final state = ref.watch(newsfeedDetailVmProvider);
+    final vm = ref.read(newsfeedDetailVmProvider.notifier);
+    final noteCount = useState(0);
+
+    useEffect((){
+      return (){
+        vm.resetCommentField();
+        Log.d('dispose');
+      };
+    },[]);
+    return _buildTextAreaLayout(context, ref, state, vm, noteCount);
+  }
+
+  Widget _buildTextAreaLayout(BuildContext context, ref, state, vm , noteCount){
+    return Row(
+      mainAxisSize: MainAxisSize.max,
+      children: [
+        Expanded(
+          child: TextField(
+            cursorColor: context.appColors.authFiledText,
+            cursorWidth: 1.5,
+            autofocus: autoFocus,
+            textAlign: TextAlign.start,
+            enabled: true,
+            maxLines: null,
+            expands: true,
+            focusNode: state.commentFieldInfo['focusNode'],
+            controller: state.commentFieldInfo!['controller'],
+            decoration: InputDecoration(
+              isDense: true,
+              isCollapsed: true,
+              border: InputBorder.none,
+              hintText: state.commentFieldInfo!['hintText'],
+              hintStyle: TextStyle(
+                color: context.appColors.authFiledHint,
+                fontSize: 14.0,
+                fontWeight: FontWeight.w300,
+              ),
+            ),
+            style: TextStyle(
+              color: context.appColors.authFiledText,
+              fontSize: 14.0,
+              fontWeight: FontWeight.w300,
+            ),
+            textInputAction: TextInputAction.done,
+            onSubmitted: (value) {
+              // FocusScope.of(context).unfocus();
+            },
+            onChanged: (text) {
+              // 当文本改变时,更新字符数量
+              if(showCounter){
+                noteCount.value = text.length;
+              }
+            },
+          ),
+        ),
+      ],
+    );
+  }
+}
+

+ 348 - 96
packages/cpt_community/lib/modules/community/newsfeed_detail/newsfeed_detail_page.dart

@@ -1,16 +1,23 @@
 import 'package:cpt_community/router/page/community_page_router.dart';
+import 'package:cs_resources/generated/assets.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:plugin_basic/provider/app_config/app_config.dart';
 import 'package:router/ext/auto_router_extensions.dart';
 import 'package:shared/utils/color_utils.dart';
 import 'package:shared/utils/log_utils.dart';
 import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/load_state_layout.dart';
 import 'package:widgets/my_appbar.dart';
 import 'package:widgets/my_button.dart';
+import 'package:widgets/my_like_button.dart';
+import 'package:widgets/my_load_image.dart';
 import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+import 'package:widgets/my_button.dart';
 
 import '../../../components/newfeed_card_header.dart';
 import '../../../components/newsfeed_card_content.dart';
@@ -38,9 +45,11 @@ class NewsfeedDetailPage extends HookConsumerWidget {
   Widget build(BuildContext context, WidgetRef ref) {
     final vm = ref.read(newsfeedDetailVmProvider.notifier);
     final state = ref.watch(newsfeedDetailVmProvider);
+    GlobalKey _likeButtonKey = GlobalKey<MyLikeButtonState>();
+
     useEffect((){
       // 组件挂载时执行 - 执行接口请求
-      // Future.microtask(() => vm.fetchList());
+      Future.microtask(() => vm.initPageData(id: id));
       return () {
         // 组件卸载时执行
       };
@@ -51,113 +60,154 @@ class NewsfeedDetailPage extends HookConsumerWidget {
       appBar: MyAppBar.appBar(
         context,
         "News Feed Detail",
-        backgroundColor: context.appColors.whiteBG,
+        backgroundColor: context.appColors.backgroundWhite,
       ),
       backgroundColor: ColorUtils.string2Color("#F2F3F6"),
       body: Column(
-        children: [
-          _buildTopCard(context, ref),
-         // 评论区域
-          Expanded(child: _buildCommentListCard(context, ref)),
-         //  底部 like comments 按钮区
-          Container(
-            color: ColorUtils.string2Color("#2956B7"),
-            child: Row(
-              children: [
-                Expanded(
-                  child: MyButton(
-                    minHeight: 50,
-                    text: 'Like',
-                    textColor: Colors.white,
-                    onPressed: () {},
-                  )
-                )
-              ]
-            )
-          )
-        ],
-      ),
+          mainAxisSize: MainAxisSize.max,
+          children:[
+              Expanded(
+                child: EasyRefresh(
+                  controller: vm.refreshController,
+                  // 上拉加载
+                  onLoad: () async{
+                    Log.d("----onLoad");
+                    vm.loadMore();
+                  },
+                  // 下拉刷新
+                  onRefresh: () async{
+                    Log.d("----onRefresh");
+                    vm.onRefresh();
+                  },
+                  child: LoadStateLayout(
+                    state: state.loadingState,
+                    errorMessage: state.errorMessage,
+                    errorRetry: () {
+                      vm.retryRequest();
+                    },
+                    successSliverWidget: [
+                      SliverList(
+                        delegate: SliverChildListDelegate(
+                            [
+                            _buildTopCard(context, ref),
+                            ConstrainedBox(
+                              constraints: BoxConstraints(
+                                minHeight: MediaQuery.of(context).size.height - 250 - 150,
+                              ),
+                              child: _buildCommentListCard(context, ref),
+                            )
+                          ]
+                        ),
+                      )
+                    ],
+                  ),
+                ),
+              ),
+              _buildBottomActionSection(context, ref, _likeButtonKey),
+          ]
+      )
     );
   }
 
   Widget _buildTopCard(BuildContext context, WidgetRef ref){
     final vm = ref.read(newsfeedDetailVmProvider.notifier);
     final state = ref.watch(newsfeedDetailVmProvider);
-    return Container(
-      width: double.infinity,
-      //   color: Colors.yellow,
-      child: Stack(
-        children: [
-          Container(
-            margin: const EdgeInsets.only(left: 15, right: 15,top: 14,bottom: 14),
-            color: Colors.white,
-            padding: const EdgeInsets.only(left: 15, right: 15,top: 17,bottom: 17),
-            height: 280,
-            child: Column(
-                mainAxisAlignment: MainAxisAlignment.center,
-                crossAxisAlignment: CrossAxisAlignment.start,
-                children: [
-                  // 卡片头部(头像 标题 时间)
-                  NewsFeedCardHeader(
-                    key: UniqueKey(),
-                    title: state?.detailInfo!['title'],
-                    avator: state?.detailInfo['avator'],
-                    time: state?.detailInfo['time'],
-                  ),
-                  const SizedBox(height: 15),
-                  // 卡片中间 (文字和图片)
-                  Expanded(
-                    child: NewsFeedCardContent(
-                      key: UniqueKey(),
-                      content: state.detailInfo['content'],
-                      imageUrls: state.detailInfo['imageUrls'],
-                    ),
-                  ),
-                  const SizedBox(height: 26),
-                ]
-            ),
+    return Stack(
+      children: [
+        Container(
+          margin: const EdgeInsets.only(left: 15, right: 15,top: 14,bottom: 14),
+          padding: const EdgeInsets.only(left: 15, right: 15,top: 17,bottom: 17),
+          decoration: BoxDecoration(
+              color: context.appColors.textWhite,
+              borderRadius: BorderRadius.circular(10),
+              boxShadow: [
+                BoxShadow(
+                  color: ColorUtils.string2Color("#E4E7EB").withOpacity(0.5),
+                  spreadRadius: 0,
+                  blurRadius: 4,
+                  offset: const Offset(0, 4), // changes position of shadow
+                ),
+              ]
           ),
-          // 右上角 关注/取消关注 按钮
-          Visibility(
-            visible: !state.detailInfo['isFollow'],
-            child: Positioned(
-                right: 40,
-                top: 35,
-                child: Container(
-                  width: 83.5,
-                  height: 45.5,
-                  alignment: Alignment.center,
-                  // decoration: BoxDecoration(
-                  //   color:  ColorUtils.string2Color('#4161D0'),
-                  //   borderRadius: BorderRadius.circular(5),
-                  // ),
-                  child: MyButton(
-                    text: '+Follow',
-                    textColor: Colors.white,
-                    backgroundColor: ColorUtils.string2Color('#4161D0'),
-                    radius: 8,
-                    minHeight: 27.5,
-                    padding: const EdgeInsets.only(left: 5, right: 5,top:9,bottom:9),
-                    fontWeight: FontWeight.w400,
-                    fontSize: 14,
-                    onPressed: (){
-                      // Navigator.pop(context);
-                    },
-                  ),
-                )
-            ),
-          )
-        ],
-      ),
+          child: Column(
+              mainAxisAlignment: MainAxisAlignment.center,
+              crossAxisAlignment: CrossAxisAlignment.start,
+              children: [
+                // 卡片头部(头像 标题 时间)
+                NewsFeedCardHeader(
+                  key: UniqueKey(),
+                  title: state?.detailInfo!['title'],
+                  avator: state?.detailInfo['avator'],
+                  time: state?.detailInfo['time'],
+                ),
+                const SizedBox(height: 15),
+                // 卡片中间 (文字和图片)
+                NewsFeedCardContent(
+                  key: UniqueKey(),
+                  content: state.detailInfo['content'],
+                  imageUrls: state.detailInfo['imageUrls'],
+                    textMaxLines: 5000,
+                ),
+                const SizedBox(height: 26),
+              ]
+          ),
+        ),
+        // 右上角 关注/取消关注 按钮
+        Visibility(
+          visible: !state.detailInfo['isFollow'],
+          child: Positioned(
+              right: 40,
+              top: 35,
+              child: Container(
+                width: 83.5,
+                height: 45.5,
+                alignment: Alignment.center,
+                // decoration: BoxDecoration(
+                //   color:  ColorUtils.string2Color('#4161D0'),
+                //   borderRadius: BorderRadius.circular(5),
+                // ),
+                child: MyButton(
+                  text: '+Follow',
+                  textColor: Colors.white,
+                  backgroundColor: ColorUtils.string2Color('#4161D0'),
+                  radius: 8,
+                  minHeight: 27.5,
+                  padding: const EdgeInsets.only(left: 5, right: 5,top:9,bottom:9),
+                  fontWeight: FontWeight.w400,
+                  fontSize: 14,
+                  onPressed: (){
+                    // Navigator.pop(context);
+                  },
+                ),
+              )
+          ),
+        )
+      ],
     );
   }
 
   Widget _buildCommentListCard(BuildContext context,  WidgetRef ref){
+    final vm = ref.read(newsfeedDetailVmProvider.notifier);
     return Container(
-      color: Colors.white,
       margin: const EdgeInsets.only(left: 15, right: 15,top: 18,bottom: 18),
+      padding: const EdgeInsets.only(bottom: 30),
+      decoration: BoxDecoration(
+          color: context.appColors.textWhite,
+          borderRadius: BorderRadius.circular(10),
+          boxShadow: [
+            BoxShadow(
+              color: ColorUtils.string2Color("#E4E7EB").withOpacity(0.5),
+              spreadRadius: 0,
+              blurRadius: 4,
+              offset: const Offset(0, 4), // changes position of shadow
+            ),
+          ]
+      ),
       child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        mainAxisAlignment: MainAxisAlignment.start,
         children: [
+          // 总评论数 总收藏数等
           Row(
             children: [
               Expanded(
@@ -167,17 +217,58 @@ class NewsfeedDetailPage extends HookConsumerWidget {
                     children: [
                       MyTextView('Comments', textColor: ColorUtils.string2Color("#2956B7"), fontSize: 15, isFontLight: true,),
                       const SizedBox(width: 5,),
-                      MyTextView('95', textColor: ColorUtils.string2Color("#2956B7"), fontSize: 15, isFontLight: true,),
+                      MyTextView('(95)', textColor: ColorUtils.string2Color("#2956B7"), fontSize: 15, isFontLight: true,),
                     ]
                   )
                 )
-              )
+              ),
+             // 收藏数
+             Expanded(
+                 child: Container(
+                   padding: const EdgeInsets.only(right: 20),
+                   child: Row(
+                     mainAxisAlignment: MainAxisAlignment.end,
+                     children: [
+                       const MyAssetImage(
+                         Assets.communityLikeActive,
+                         width: 15,
+                         height: 15,
+                       ),
+                       const SizedBox(width: 5,),
+                       MyTextView(
+                         '105K',
+                         textColor: ColorUtils.string2Color("#767676"),
+                         fontSize: 14,
+                         isFontLight: true,
+                       ),
+                     ],
+                   ),
+                 ),
+             )
             ],
           ),
-          const Expanded(
-            child:  Row(
-              children: [
-                 Text("fsdf"),
+
+          // 分割线
+          Container(
+            height: 1,
+            color: context.appColors.dividerDefault,
+          ),
+
+          Padding(
+            padding: const EdgeInsets.only(top: 16),
+            child: Column(
+              children: (vm.state.list.isNotEmpty )? vm.state.list.asMap().entries.map((entry) {
+                final item = entry.value;
+                final index = entry.key;
+                final lastIdx = vm.state.list.length - 1;
+                return _buildCommentItem(context, ref, item, index, lastIdx);
+              }).toList(): [
+                Center(
+                  child: SizedBox(
+                    height: 200,
+                    child: MyTextView("NO DATA"),
+                  ),
+                )
               ],
             ),
           ),
@@ -185,4 +276,165 @@ class NewsfeedDetailPage extends HookConsumerWidget {
       ),
     ).borderRadius(all:8);
   }
+
+
+  Widget _buildCommentItem(BuildContext context, WidgetRef ref, item, index, lastIdx){
+    final vm = ref.read(newsfeedDetailVmProvider.notifier);
+    final state = ref.watch(newsfeedDetailVmProvider);
+    return Container(
+      padding: const EdgeInsets.only(left: 20,right: 20),
+      margin:  EdgeInsets.only(top: index> 0? 16: 0),
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.start,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        mainAxisSize: MainAxisSize.max,
+        children: [
+          MyLoadImage(
+            item['avator'],
+            width: 43,
+            height: 43,
+            isCircle: true,
+            fit: BoxFit.cover,
+          ).onTap(() {
+            // 点击头像
+            // onTap?.call();
+          }),
+          Expanded(
+            child: Container(
+              padding: const EdgeInsets.only(left:15, right: 0),
+              // color: Colors.red,
+              child: Column(
+                mainAxisAlignment: MainAxisAlignment.start,
+                crossAxisAlignment: CrossAxisAlignment.start,
+                children: [
+                  MyTextView(
+                    item['userName'],
+                    isFontRegular: true,
+                    fontSize: 14,
+                    textColor: ColorUtils.string2Color('#2956B7'),
+                    maxLines: 1,
+                    isTextEllipsis: true,
+                  ),
+                  MyTextView(
+                    item['time'],
+                    isFontLight: true,
+                    fontSize: 12,
+                    marginTop: 10,
+                    textColor: ColorUtils.string2Color('#767676'),
+                    maxLines: 1,
+                    isTextEllipsis: true,
+                  ),
+                  MyTextView(
+                    item['content'],
+                    isFontLight: true,
+                    fontSize: 15,
+                    marginTop: 10,
+                    textColor: context.appColors.textBlack,
+                    maxLines: null,
+                    isTextEllipsis: false,
+                  ),
+
+                 const SizedBox(height: 15,),
+                 // 分割线
+                  index!=lastIdx?Container(height: 1, color: context.appColors.dividerDefault):const SizedBox.shrink(),
+                ],
+              ),
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+
+
+  Widget _buildBottomActionSection(BuildContext context, WidgetRef ref , _likeButtonKey){
+    final state = ref.watch(newsfeedDetailVmProvider);
+    final vm = ref.read(newsfeedDetailVmProvider.notifier);
+    bool isLike = state.detailInfo['isLike'];
+    return SafeArea(
+      child: Visibility(
+        visible: state.loadingState == LoadState.State_Success,
+        child: Container(
+            height: 60,
+            width: MediaQuery.of(context).size.width,
+            color: context.appColors.textPrimary,
+            child: Row(
+                mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                crossAxisAlignment: CrossAxisAlignment.center,
+                children: [
+                  Expanded(
+                    child: Row(
+                      mainAxisAlignment: MainAxisAlignment.center,
+                      crossAxisAlignment: CrossAxisAlignment.center,
+                      children: [
+                        Container(
+                          alignment: Alignment.center,
+                          padding: const EdgeInsets.only(left: 10,right: 10),
+                          child: Wrap(
+                            children: [
+                              MyLikeButton(
+                                key: _likeButtonKey,
+                                isLiked: false,
+                                isCustomIcon: true,
+                                customIconWidth: 20,
+                                customIconHeight: 18,
+                                customIconActiveAssets: Assets.communityLikeActive,
+                                customIconUnActiveAssets: Assets.communityNewsfeedDetailLike,
+                                onLike: () {
+                                  Log.d('点击了like button');
+                                },
+                              ),
+                              const SizedBox(width: 10,),
+
+                              MyTextView(
+                                "Like",
+                                textColor: context.appColors.whiteBG,
+                                fontSize: 14,
+                                isFontRegular: true,
+                              )
+                            ],
+                          ),
+                        ).onTap((){
+                          final state = _likeButtonKey.currentState;
+                          state?.triggerTap();
+                        })
+                      ],
+                    ),
+                  ),
+                  Expanded(
+                    child: Row(
+                      mainAxisAlignment: MainAxisAlignment.center,
+                      crossAxisAlignment: CrossAxisAlignment.center,
+                      children: [
+                        Container(
+                          alignment: Alignment.center,
+                          padding: const EdgeInsets.only(left: 10,right: 10),
+                          child: Wrap(
+                            children: [
+                              MyLoadImage(
+                                Assets.communityNewsfeedDetailComment,
+                                width: 17.5,
+                                height: 16.5,
+                              ),
+                              const SizedBox(width: 10,),
+                              MyTextView(
+                                "Comment",
+                                textColor: context.appColors.whiteBG,
+                                fontSize: 14,
+                                isFontRegular: true,
+                              ),
+                            ],
+                          ),
+                        ).onTap((){
+                          vm.handlerCommentClick(context);
+                        })
+                      ],
+                    ),
+                  ),
+                ]
+            )
+        ),
+      ),
+    );
+  }
 }

+ 56 - 6
packages/cpt_community/lib/modules/community/newsfeed_detail/newsfeed_detail_state.dart

@@ -1,27 +1,77 @@
+import 'package:widgets/load_state_layout.dart';
+
 class NewsfeedDetailState {
+  //页面 LoadView 状态的展示
+  LoadState loadingState;
+  String? errorMessage;
+
   int? curPage;
-  int? pageSize;
-  int? filterCount;
+  int? pageSize = 10;
+  int? filterCount = 1;
+
+  // 评论框信息
+  Map<String, dynamic>? commentFieldInfo;
+
   Map<String, dynamic> detailInfo;
+  List<Map<String, dynamic>> list; // 评论列表
 
   NewsfeedDetailState({
-    this.curPage,
-    this.pageSize,
-    this.filterCount,
+    this.loadingState = LoadState.State_Loading,
+    String? errorMessage,
+    this.curPage = 1,
+    this.pageSize = 10,
+    this.filterCount = 1,
+    Map<String, dynamic>? commentFieldInfo,
     required this.detailInfo,
-  });
+    required this.list,
+  }): commentFieldInfo = commentFieldInfo ?? {},
+      errorMessage = errorMessage?? '';
 
   NewsfeedDetailState copyWith({
+    LoadState? loadingState,
+    String? errorMessage,
     int? curPage,
     int? pageSize,
     int? filterCount,
     Map<String, dynamic>? detailInfo,
+    Map<String, dynamic>? commentFieldInfo,
+    List<Map<String, dynamic>>? list,
   }) {
     return NewsfeedDetailState(
+      loadingState: loadingState ?? this.loadingState,
+      errorMessage: errorMessage ?? this.errorMessage,
       curPage: curPage ?? this.curPage,
       pageSize: pageSize ?? this.pageSize,
       filterCount: filterCount ?? this.filterCount,
+      commentFieldInfo: commentFieldInfo ?? this.commentFieldInfo,
       detailInfo: detailInfo ?? this.detailInfo,
+      list: list ?? this.list,
+    );
+  }
+
+  Map<String, dynamic> toMap() {
+    return {
+      'loadingState': this.loadingState,
+      'errorMessage': this.errorMessage,
+      'curPage': this.curPage,
+      'pageSize': this.pageSize,
+      'filterCount': this.filterCount,
+      'commentFieldInfo': this.commentFieldInfo,
+      'detailInfo': this.detailInfo,
+      'list': this.list,
+    };
+  }
+
+  factory NewsfeedDetailState.fromMap(Map<String, dynamic> map) {
+    return NewsfeedDetailState(
+      loadingState: map['loadingState'] as LoadState,
+      errorMessage: map['errorMessage'] as String,
+      curPage: map['curPage'] as int,
+      pageSize: map['pageSize'] as int,
+      filterCount: map['filterCount'] as int,
+      commentFieldInfo: map['commentFieldInfo'] as Map<String, dynamic>,
+      detailInfo: map['detailInfo'] as Map<String, dynamic>,
+      list: map['list'] as List<Map<String, dynamic>>,
     );
   }
 }

+ 239 - 18
packages/cpt_community/lib/modules/community/newsfeed_detail/newsfeed_detail_vm.dart

@@ -1,10 +1,20 @@
 
 import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
 import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.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:shared/utils/color_utils.dart';
 import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+import '../../../components/comments_dialog.dart';
 
+import 'comments_input.dart';
 import 'newsfeed_detail_state.dart';
 import 'newsfeed_detail_page.dart';
 
@@ -13,34 +23,53 @@ part 'newsfeed_detail_vm.g.dart';
 @riverpod
 class NewsfeedDetailVm extends _$NewsfeedDetailVm {
 
-   initState() {
+  // 评论框信息
+  final Map<String, dynamic> _textFieldInfo= {
+    'value': '',
+    'controller': TextEditingController(),
+    'hintText': 'Please enter your comment',
+    'focusNode': FocusNode(),
+    'obsecure': false,
+  };
+
+
+  bool _needShowPlaceholder = true; //是否展示LoadingView
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,  //允许刷新
+    controlFinishLoad: true,   //允许加载
+  );
+
+  NewsfeedDetailState initState() {
     return NewsfeedDetailState(
-      curPage: 1,
-      pageSize: 10,
-      filterCount: 0,
       detailInfo: {
         'id':1,
-        'avator': Assets.communityCamera,
+        'avator': 'https://p4.itc.cn/q_70/images03/20231121/ab826c4a7efe4c9aa93ca6e00f9e0986.jpeg',
         'title': 'William Jefferson',
         'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-        'imageUrls': ['https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg'],
+        'imageUrls': [
+          'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+          'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+          'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+          'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+          'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        ],
         'time': 'June 17,2016 at 7:23 p.m.',
         'isLike': true,
         'isFollow': false,
         'likeno': 12,
-        'commentsInfo': {
-          'total': 12,
-          'list': [
-            {
-                'userId': 1,
-                'userName': 'William Jefferson',
-                'avator': Assets.communityCamera,
-                'time': 'June 17,2016 at 7:23 p.m.',
-                'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]'
-            },
-          ]
-        }
       },
+      list: [
+        {
+          'userId': 1,
+          'userName': 'William Jefferson',
+          'avator': 'https://iknow-pic.cdn.bcebos.com/d439b6003af33a87f27f0584d45c10385343b519',
+          'time': 'June 17,2016 at 7:23 p.m.',
+          'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]'
+        },
+      ],
+      commentFieldInfo: _textFieldInfo
     );
   }
 
@@ -56,5 +85,197 @@ class NewsfeedDetailVm extends _$NewsfeedDetailVm {
     // Log.d("---------444-----------------build---------------${state.detailInfo}------");
     return state;
   }
+
+//刷新页面状态
+  void changeLoadingState(LoadState loadState, String? errorMsg) {
+    state = state.copyWith(
+        loadingState: loadState,
+        errorMessage: errorMsg
+    );
+  }
+
+  // 初始化页面数据
+  initPageData({int? id}) {
+    Log.d("----property_news_vm-----initPageData   ${state.loadingState}");
+    onRefresh(id: id);
+  }
+
+  // 上拉加载 更多
+  Future loadMore({int? id}) async {
+    Log.d("----property_news_vm-----loadMore");
+    // await Future.delayed(const Duration(seconds: 2));
+    // if(state.list.length >= state.filterCount){
+    //   return;
+    // }else {
+    //   int curPage = state.curPage + 1;
+    //   state = state.copyWith(curPage: curPage,);
+    //   getListData();
+    // }
+    // 检查 curPage 是否为 null,并初始化为 1
+    int newCurPage = state.curPage ?? 1;
+    state = state.copyWith(curPage: ++newCurPage);
+    getListData();
+  }
+
+
+  // 下拉刷新
+  Future onRefresh({int? id}) async {
+    Log.d("----property_news_vm-----onRefresh ");
+
+    // await Future.delayed(const Duration(seconds: 2));
+    state = state.copyWith(curPage: 1);
+    getListData();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    state = state.copyWith(curPage: 1);
+    _needShowPlaceholder = true;
+    getListData();
+  }
+
+
+  // 获取list 列表数据
+  Future getListData<T>({int? id}) async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading, null);
+    }
+
+    Log.d("加载listData数据---------------start--${state.curPage}---");
+    //   try {
+    //     //请求网络
+    //     Map<String, dynamic>  params = {
+    //       "curPage": state.curPage,
+    //       "pageSize": state.pageSize,
+    //     };
+    //     Log.d("请求参数------$params");
+    //     final result = await propertyNewsRepository.fetchPropertyNewsList(params);
+    //     Log.d("请求完成结果------${result.data}");
+    //     //校验成功失败
+    //     if (result.isSuccess) {
+    //       // state = state.copyWith(serverTime: result.data);
+    //       state = state;
+    //   handleList(listResult.data?.rows);
+    //       ToastEngine.show("获取数据成功");
+    //     } else {
+    //   errorMessage = listResult.errorMsg;
+    //   changeLoadingState(LoadState.State_Error);
+    //       ToastEngine.show(result.errorMsg ?? "Network Load Error");
+    //     }
+    //   } catch (e) {
+    //     ToastEngine.show("Error: $e");
+    //   }
+
+    await Future.delayed(const Duration(milliseconds: 1500));
+    final List<Map<String, dynamic>> listData = [
+      {
+        'userId': 1,
+        'userName': 'William Jefferson',
+        'avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500',
+        'time': 'June 17,2016 at 7:23 p.m.',
+        'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]'
+      },
+      {
+        'userId': 2,
+        'userName': 'Ander Jackson',
+        'avator': 'https://iknow-pic.cdn.bcebos.com/d439b6003af33a87f27f0584d45c10385343b519',
+        'time': 'June 17,2016 at 7:23 p.m.',
+        'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]'
+      },
+    ];
+
+
+    if (state.curPage == 1) {
+      //刷新的方式
+      state = state.copyWith(list: listData);
+      refreshController.finishRefresh();
+      // //更新展示的状态
+      changeLoadingState(LoadState.State_Success, null);
+    } else {
+      //加载更多
+      final allList = state.list;
+      allList.addAll(listData);
+      refreshController.finishLoad();
+      state = state.copyWith(list: allList);
+    }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+  }
+
+
+  // 点击了 comment 按钮
+  handlerCommentClick(BuildContext context) async{
+    BuildContext contextPage = context;
+    // double height = MediaQuery.of(contextPage).size.height;
+    double commentInputHeight = 150.0;
+    Log.d("点击了评论按钮");
+    await DialogEngine.show(
+      tag: "newsFeedComments",
+      position: DialogPosition.bottom,
+      widget: CommentsDialog(
+        isShowConfirmBtn: false,
+        isShowCancelBtn: false,
+        height: commentInputHeight,
+        confirmAction: (){},
+        cancelAction: null,
+        messageBuilder: (context) {
+          return Row(
+            mainAxisAlignment: MainAxisAlignment.start,
+            children: [
+              Expanded(
+                  child: Container(
+                    margin: const EdgeInsets.only(left:15, right:15,top: 20, bottom: 20),
+                    width: double.infinity,
+                    height: commentInputHeight,
+                    padding: const EdgeInsets.all(15),
+                    decoration: BoxDecoration(
+                        color: context.appColors.backgroundDark,
+                        borderRadius: BorderRadius.circular(5),
+                        boxShadow: [
+                          BoxShadow(
+                            color: Colors.grey.withOpacity(0.1),
+                            spreadRadius:1,
+                            blurRadius: 1,
+                            offset: const Offset(0.5, 0.5), // changes position of shadow
+                          ),
+                        ]
+                    ),
+                    child: CommentsTextareaInput( showCounter:false, autoFocus: true),
+                  ),
+              ),
+              Container(
+                margin: const EdgeInsets.only(right: 10),
+                height: commentInputHeight,
+                decoration: BoxDecoration(
+                  color: context.appColors.textPrimary,
+                  borderRadius: BorderRadius.circular(5),
+                ),
+                child: Center(
+                  child: IconButton(
+                    icon: Icon(Icons.send, color: context.appColors.textWhite, size: 25,),
+                    onPressed: (){
+                      Log.d("点击了发送按钮");
+                    },
+                  ),
+                ),
+              ),
+            ]
+          );
+        },
+      ),
+    );
+  }
+
+  // 点击了 Like 按钮
+  bool handlerLikeClick(){
+    Log.d("点击了点赞按钮");
+    return true;
+  }
+
+  // 清空评论
+  resetCommentField(){
+    state.commentFieldInfo!['controller']!.clear();
+  }
 }
 

+ 1 - 1
packages/cpt_community/lib/modules/community/newsfeed_detail/newsfeed_detail_vm.g.dart

@@ -6,7 +6,7 @@ part of 'newsfeed_detail_vm.dart';
 // RiverpodGenerator
 // **************************************************************************
 
-String _$newsfeedDetailVmHash() => r'058d92cd086572a5cc7e16f57db7d89405bf3c2f';
+String _$newsfeedDetailVmHash() => r'2a36483461d9e5ca76e5f65ac524f0a972325735';
 
 /// See also [NewsfeedDetailVm].
 @ProviderFor(NewsfeedDetailVm)

+ 0 - 38
packages/cpt_community/lib/modules/community/newsfeed_myposts/newsfeed_myposts_page.dart

@@ -1,38 +0,0 @@
-import 'package:cpt_community/router/page/community_page_router.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';
-
-@RoutePage()
-class NewsfeedMypostsPage extends HookConsumerWidget {
-  const NewsfeedMypostsPage({Key? key}) : super(key: key);
-
-  //启动当前页面
-  static void startInstance({BuildContext? context}) {
-    if (context != null) {
-      context.router.push(const NewsfeedMypostsPageRoute());
-    } else {
-      appRouter.push(const NewsfeedMypostsPageRoute());
-    }
-  }
-
-  @override
-  Widget build(BuildContext context, WidgetRef ref) {
-    // final viewModel = ref.watch(newsfeedPostVmProvider.notifier);
-
-    return Scaffold(
-      appBar: MyAppBar.appBar(
-        context,
-        "My Posts",
-        backgroundColor: context.appColors.whiteBG,
-      ),
-      backgroundColor: context.appColors.backgroundDefault,
-      body: Center(
-        child: Text("NewsfeedMyposts"),
-      ),
-    );
-  }
-}

+ 1 - 1
packages/cpt_community/lib/modules/community/newsfeed_post/newsfeed_post_page.dart

@@ -37,7 +37,7 @@ class NewsfeedPostPage extends HookConsumerWidget {
       appBar: MyAppBar.appBar(
         context,
         "Create Post",
-        backgroundColor: context.appColors.whiteBG,
+        backgroundColor: context.appColors.backgroundWhite,
       ),
       backgroundColor: ColorUtils.string2Color("#F2F3F6"),
       body: Column(

+ 69 - 14
packages/cpt_community/lib/modules/community/newsfeed_tabs.dart

@@ -11,14 +11,14 @@ import 'package:widgets/ext/ex_widget.dart';
 import 'package:widgets/my_text_view.dart';
 import 'package:widgets/my_appbar.dart';
 
+import 'community_page.dart';
 import 'community_vm.dart';
-import '../garage/garage_vm.dart';
 
 class NewsfeedTabs extends HookConsumerWidget {
-  List tabsList;
+  List<String> tabsList;
   Widget? Function(BuildContext)? tabItemBuilder;
   dynamic? tabsRouter;
-  void Function(int)? onClickAction;
+  void Function(Map<String, dynamic>)? onClickAction;
   NewsfeedTabs({
     Key? key,
     required this.tabsList,
@@ -28,20 +28,74 @@ class NewsfeedTabs extends HookConsumerWidget {
   }) : super(key: key);
 
   Widget _buildTabItem(BuildContext context, WidgetRef ref, vm, item, index) {
-    // Log.d("NewsfeedTabs _buildTabItem index: ${vm.state.tabsList}");
-    // Log.d("NewsfeedTabs _buildTabItem index: ${vm.state.activeTabIdx}");
+    final providerCommunityState = ref.watch(communityVmProvider);
+    int lastNewsFeedTabIdx = 0;
+    int lastGarageTabIdx = 0;
+    final newsfeedTabCount = ref.read(communityVmProvider.notifier).state!.newsFeedTabsList!.length;
     // 监听 activeTabIdx 的变化
-    // final activeTabIdx = ref.watch(communityVmProvider.select((state) => state.activeTabIdx));
+    final activePageIdx = ref.watch(communityVmProvider.select((state) => state.currentPageViewIdx));
+    int currentCatgoryIdx = activePageIdx >= newsfeedTabCount? 1: 0;
 
-    final activeTabIdx = tabsRouter!.activeIndex;
 
-    // Log.d("NewsfeedTabs _buildTabItem activeTabIdx: $activeTabIdx");
+    int curTabIdx = 0;
+    // final activeTabIdx = tabsRouter!.activeIndex;
+    // final activeTabIdx = tabsRouterKey.currentState?.controller?.activeIndex ?? 0;
+
+    // 监听 currentPageViewIdx 的变化
+    ref.listen(communityVmProvider.select((state) => state.currentPageViewIdx), (oldValue, newValue){
+      // Log.d("--111--oldValue: $oldValue, newValue: $newValue---------------");
+      // Log.d("--111--lastNewsFeedTabIdx: $lastNewsFeedTabIdx, lastGarageTabIdx: $lastGarageTabIdx---------------");
+
+
+      if(oldValue! < newsfeedTabCount){
+        if( newValue >= newsfeedTabCount ){
+          // 由 newsfeed 切换到 garage sale  记录newsFeed 最后的 tabidx
+          lastNewsFeedTabIdx = oldValue;
+          lastGarageTabIdx = newValue - newsfeedTabCount;
+          currentCatgoryIdx = 1;
+          Log.d("-从newsfeed 切换到 garagesale-----------currentCatgoryIdx  $currentCatgoryIdx-------lastNewsFeedTabIdx: $lastNewsFeedTabIdx, lastGarageTabIdx: $lastGarageTabIdx---------------");
+        }else {
+          lastNewsFeedTabIdx = newValue;
+          lastGarageTabIdx = providerCommunityState.lastGarageTabIdx;
+        }
+      }
+
+      if(oldValue! >= newsfeedTabCount){
+        if(newValue < newsfeedTabCount){
+          // 由 garage sale 切换到 newsfeed  记录garage sale 最后的 tabidx
+          lastGarageTabIdx = oldValue - newsfeedTabCount;
+          lastNewsFeedTabIdx = newValue;
+          currentCatgoryIdx = 0;
+          Log.d("-从garagesale  切换到 newsfeed---------------currentCatgoryIdx  $currentCatgoryIdx------lastNewsFeedTabIdx: $lastNewsFeedTabIdx, lastGarageTabIdx: $lastGarageTabIdx---------------");
+
+        }else {
+          lastGarageTabIdx = newValue - newsfeedTabCount;
+          lastNewsFeedTabIdx = providerCommunityState.lastNewsfeedTabIdx;
+        }
+      }
+
+      // Log.d("-666---currentCatgoryIdx----$currentCatgoryIdx-----lastNewsFeedTabIdx: $lastNewsFeedTabIdx, lastGarageTabIdx: $lastGarageTabIdx---------------");
+      ref.read(communityVmProvider.notifier).setCurrentCategoryIdx(
+        context,
+        currentCatgoryIdx,
+        lastNewsFeedTabIdx,
+        lastGarageTabIdx,
+      );
+    });
+
+    if(activePageIdx >= newsfeedTabCount){
+      // garage sale
+      curTabIdx = activePageIdx - newsfeedTabCount;
+    }else {
+      // news feed
+      curTabIdx = activePageIdx;
+    }
     return Container(
-      width: MediaQuery.of(context).size.width / vm.state.tabsList.length - 30,
+      width: MediaQuery.of(context).size.width / tabsList.length - 30,
       height: 43,
       padding: const EdgeInsets.only(top: 10, bottom: 10, left: 10, right: 10),
-      decoration: index==activeTabIdx? BoxDecoration(
-        color: index==activeTabIdx? context.appColors.btnBgDefault: ColorUtils.string2Color("#F2F3F6"),
+      decoration: index==curTabIdx? BoxDecoration(
+        color: index==curTabIdx? context.appColors.btnBgDefault: ColorUtils.string2Color("#F2F3F6"),
         borderRadius: BorderRadius.circular(20),
         boxShadow: [
           BoxShadow(
@@ -62,11 +116,11 @@ class NewsfeedTabs extends HookConsumerWidget {
                 fontSize: 16,
                 textAlign: TextAlign.center,
                 isFontMedium: true,
-                textColor: index == activeTabIdx ? Colors.white :ColorUtils.string2Color("#000000"),
+                textColor: index == curTabIdx ? Colors.white :ColorUtils.string2Color("#000000"),
               ),
             ).onTap((){
-              print("newsfeed_tabs 中点击的tab index:  $index");
-              onClickAction?.call(index);
+              // Log.d("----currentCatgoryIdx----$currentCatgoryIdx-------");
+              onClickAction?.call({'currentCatgoryIdx':currentCatgoryIdx, 'tabIdx': index});
             }),
           ),
         ],
@@ -108,6 +162,7 @@ class NewsfeedTabs extends HookConsumerWidget {
       child: Row(
         mainAxisSize: MainAxisSize.max,
         mainAxisAlignment: MainAxisAlignment.center,
+        crossAxisAlignment: CrossAxisAlignment.center,
         children: _buildTabs(context, ref, vm),
       ).constrained(
           maxWidth:  MediaQuery.of(context).size.width

+ 101 - 36
packages/cpt_community/lib/modules/garage/for_rent/for_rent_page.dart

@@ -1,11 +1,17 @@
+import 'package:cs_resources/generated/assets.dart';
 import 'package:flutter/material.dart';
 import 'package:auto_route/auto_route.dart';
 import 'package:flutter/rendering.dart';
 import 'package:flutter_hooks/flutter_hooks.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:shared/utils/color_utils.dart';
+import 'package:shared/utils/ext_dart.dart';
 import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_button.dart';
 import 'package:widgets/my_load_image.dart';
 import 'package:widgets/ext/ex_widget.dart';
 import 'package:widgets/my_text_view.dart';
@@ -13,6 +19,8 @@ import 'package:widgets/my_appbar.dart';
 import 'package:cs_resources/theme/app_colors_theme.dart';
 import 'package:widgets/widget_export.dart';
 
+import '../../../components/garage_card.dart';
+import '../../../components/newfeed_card_header.dart';
 import '../../../router/page/community_page_router.dart';
 import 'for_rent_vm.dart';
 
@@ -29,52 +37,109 @@ class ForrentPage extends HookConsumerWidget {
     }
   }
 
+
   @override
   Widget build(BuildContext context, WidgetRef ref) {
     final vm = ref.read(forrentVmProvider.notifier);
-    return const Scaffold(
+    final state = ref.watch(forrentVmProvider);
+    // final appConfigState = ref.watch(appConfigServiceProvider);
+
+    useEffect(() {
+      // 组件挂载时执行 - 执行接口请求
+      Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+        Log.d("garage_forsale_page 组件卸载时执行");
+      };
+    }, []);
+    return Scaffold(
       // appBar: MyAppBar.appBar(
       //   context,
-      //   "forrent",
+      //   "Forrent",
       //   backgroundColor: context.appColors.whiteBG,
       // ),
-      // backgroundColor: ColorUtils.string2Color("#F2F3F6"),
-        body: Column(
-          children: [
-            // Expanded(
-            //   child: EasyRefresh(
-            //     // 上拉加载
-            //     onLoad: () async{
-            //       Log.d("----onLoad");
-            //       vm.onLoadData();
-            //     },
-            //     // 下拉刷新
-            //     onRefresh: () async{
-            //       Log.d("----onRefresh");
-            //       vm.refreshListData();
-            //     },
-            //     child: _buildForrentList(context, ref, vm),
-            //   ),
-            // )
-            Text("FOR RENT PAGE"),
-          ],
-        )
+        backgroundColor: ColorUtils.string2Color("#F2F3F6"),
+      body: SizedBox(
+          width: double.infinity,
+          height: double.infinity,
+          child: EasyRefresh(
+            controller: vm.refreshController,
+            // 上拉加载
+            onLoad: () async{
+              Log.d("----onLoad");
+              vm.loadMore();
+            },
+            // 下拉刷新
+            onRefresh: () async{
+              Log.d("----onRefresh");
+              vm.onRefresh();
+            },
+            child: LoadStateLayout(
+              state: state.loadingState,
+              errorMessage: state.errorMessage,
+              errorRetry: () {
+                vm.retryRequest();
+              },
+              successSliverWidget:[
+                SliverGrid(
+                  gridDelegate:  SliverGridDelegateWithFixedCrossAxisCount(
+                    crossAxisCount: 2, // 每行显示两个项目
+                    mainAxisSpacing: 15,
+                    crossAxisSpacing: 15,
+                    // childAspectRatio: 166.5/214, // 宽高比
+                    // childAspectRatio:  166.5/214 * ScreenUtil.getAdapterSizeCtx(context, 166.5/214), // 宽高比
+                    childAspectRatio: 166.5/214 * ((166.5/214).ap), //
+                  ),
+                  delegate: SliverChildBuilderDelegate(
+                        (context, index) {
+                      return  _buildForrentItem(context, ref, state.list[index], vm).onTap((){
+                        vm.handlerGotoDetail(state.list[index]['id']);
+                      });
+                    },
+                    childCount: state.list.length,
+                  ),
+                ),
+              ],
+            ),
+          ).marginOnly(left: 15,right: 15,top: 15,bottom: 15)
+      ),
     );
   }
 
-
-  Widget _buildForrentList(BuildContext context, WidgetRef ref, ForrentVm vm) {
-
-    return ListView.builder(
-      itemCount: 3,
-      itemBuilder: (context, index) {
-        return _buildListItem(context, ref, vm, index);
-      },
+  Widget _buildForrentItem(BuildContext context, WidgetRef ref, item, vm){
+    return SizedBox(
+      width: double.infinity,
+      child: Container(
+        decoration: BoxDecoration(
+          color: context.appColors.whiteBG,
+          borderRadius: BorderRadius.circular(8),
+          boxShadow: [
+            BoxShadow(
+              color: ColorUtils.string2Color('#E5E5E5'),
+              offset: const Offset(0, 2),
+              blurRadius: 8,
+            ),
+          ],
+        ),
+        child: Column(
+            mainAxisAlignment: MainAxisAlignment.start,
+            crossAxisAlignment: CrossAxisAlignment.start,
+            mainAxisSize: MainAxisSize.max,
+            children: [
+              // 卡片头部(头像 标题 时间)
+              Expanded(
+                child: GarageCard(
+                    key: UniqueKey(),
+                    itemObj: item,
+                    onClickColleciotn: (dynamic collectionValue){
+                      Log.d("点击了收藏按钮  --${item['id']}-$collectionValue");
+                      return true;
+                    }
+                ),
+              ),
+            ]
+        ),
+      ),
     );
   }
-
-  Widget _buildListItem(BuildContext context, WidgetRef ref, ForrentVm vm, int index) {
-    return Text("list-item");
-  }
-
 }

+ 25 - 14
packages/cpt_community/lib/modules/garage/for_rent/for_rent_state.dart

@@ -1,34 +1,43 @@
+import 'package:widgets/load_state_layout.dart';
+
 class ForrentState {
   int? activeTabIndex =0;
-  int? curPage =0;
-  int? pageSize =10;
-  int? filterCount =0;
-  List<String>? tabsList = ['For Sale', 'For Rent'];
-  List<Map<String, dynamic>>? list = [];
+  //页面 LoadView 状态的展示
+  LoadState loadingState;
+  String? errorMessage;
+
+  int? curPage;
+  int? pageSize = 10;
+  int? filterCount = 0;
+  List<Map<String, dynamic>> list;
+
 
   ForrentState({
     this.activeTabIndex,
-    this.curPage,
-    this.pageSize,
-    this.filterCount,
-    this.tabsList,
-    this.list,
+    this.loadingState = LoadState.State_Loading,
+    String? errorMessage,
+    this.curPage = 1,
+    this.pageSize = 10,
+    this.filterCount = 0,
+    required this.list,
   });
 
   ForrentState copyWith({
     int? activeTabIndex,
+    LoadState? loadingState,
+    String? errorMessage,
     int? curPage,
     int? pageSize,
     int? filterCount,
-    List<String>? tabsList,
     List<Map<String, dynamic>>? list,
   }) {
     return ForrentState(
       activeTabIndex: activeTabIndex ?? this.activeTabIndex,
+      loadingState: loadingState ?? this.loadingState,
+      errorMessage: errorMessage ?? this.errorMessage,
       curPage: curPage ?? this.curPage,
       pageSize: pageSize ?? this.pageSize,
       filterCount: filterCount ?? this.filterCount,
-      tabsList: tabsList ?? this.tabsList,
       list: list ?? this.list,
     );
   }
@@ -36,10 +45,11 @@ class ForrentState {
   Map<String, dynamic> toMap() {
     return {
       'activeTabIndex': this.activeTabIndex,
+      'loadingState': this.loadingState,
+      'errorMessage': this.errorMessage,
       'curPage': this.curPage,
       'pageSize': this.pageSize,
       'filterCount': this.filterCount,
-      'tabsList': this.tabsList,
       'list': this.list,
     };
   }
@@ -47,10 +57,11 @@ class ForrentState {
   factory ForrentState.fromMap(Map<String, dynamic> map) {
     return ForrentState(
       activeTabIndex: map['activeTabIndex'] as int,
+      loadingState: map['loadingState'] as LoadState,
+      errorMessage: map['errorMessage'] as String,
       curPage: map['curPage'] as int,
       pageSize: map['pageSize'] as int,
       filterCount: map['filterCount'] as int,
-      tabsList: map['tabsList'] as List<String>,
       list: map['list'] as List<Map<String, dynamic>>,
     );
   }

+ 172 - 132
packages/cpt_community/lib/modules/garage/for_rent/for_rent_vm.dart

@@ -4,8 +4,12 @@ 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:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
 
 import '../../../router/page/community_page_router.dart';
+import '../../community/community_pageview_idx_data.dart';
+import '../../community/community_vm.dart';
 import '../garagesale_detail/garagesale_detail_page.dart';
 import 'for_rent_respository.dart';
 import 'for_rent_state.dart';
@@ -15,85 +19,49 @@ part 'for_rent_vm.g.dart';
 @riverpod
 class ForrentVm extends _$ForrentVm {
   late ForrentRepository ForrentRepositoryInstance;
+  bool _needShowPlaceholder = true; //是否展示LoadingView
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,  //允许刷新
+    controlFinishLoad: true,   //允许加载
+  );
+
   ForrentState initState() {
     return ForrentState(
         list: [
           {
             'id':1,
-            'avator': Assets.communityCamera,
-            'title': 'William Jefferson',
-            'isFollow': false,
-            'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-            'imageUrls': ['https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg'],
-            'time': 'June 17,2016 at 7:23 p.m.',
-            'isLike': true,
-            'likeno': 12
+            'goods_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+            'title': 'Electronic keyboard',
+            'price': '\$66',
+            'isCollection': true,
+            'collection_num': 12,
+            'publisher': 'William Jefferson',
+            'publish_time': 'June 17,2016 at 7:23 p.m.',
+            'publisrher_avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500'
           },
           {
             'id':2,
-            'avator': Assets.communityCamera,
-            'title': 'William fdsaf的飞洒发生的',
-            'isFollow': true,
-            'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-            'imageUrls': [],
-            'time': 'June 17,2016 at 7:23 p.m.',
-            'isLike': true,
-            'likeno': 12
+            'goods_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+            'title': 'Electronic keyboard',
+            'price': '\$88',
+            'isCollection': false,
+            'collection_num': 12,
+            'publisher': 'William Jefferson',
+            'publish_time': 'June 17,2016 at 7:23 p.m.',
+            'publisher_avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500'
           },
           {
             'id':3,
-            'avator': Assets.communityCamera,
-            'title': 'Fsjfkds  dfsk',
-            'isFollow': false,
-            'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-            'imageUrls': [],
-            'time': 'June 17,2016 at 7:23 p.m.',
-            'isLike': false,
-            'likeno': 12
-          },
-          {
-            'id':4,
-            'avator': Assets.communityCamera,
-            'title': 'Fsjfkds  dfsk',
-            'isFollow': false,
-            'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-            'imageUrls': [],
-            'time': 'June 17,2016 at 7:23 p.m.',
-            'isLike': false,
-            'likeno': 12
-          },
-          {
-            'id':5,
-            'avator': Assets.communityCamera,
-            'title': 'Fsjfkds  dfsk',
-            'isFollow': false,
-            'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-            'imageUrls': [],
-            'time': 'June 17,2016 at 7:23 p.m.',
-            'isLike': false,
-            'likeno': 12
-          },
-          {
-            'id':6,
-            'avator': Assets.communityCamera,
-            'title': 'Fsjfkds  dfsk',
-            'isFollow': false,
-            'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-            'imageUrls': [],
-            'time': 'June 17,2016 at 7:23 p.m.',
-            'isLike': false,
-            'likeno': 12
-          },
-          {
-            'id':7,
-            'avator': Assets.communityCamera,
-            'title': '放大发大水',
-            'isFollow': false,
-            'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-            'imageUrls': [],
-            'time': 'June 17,2016 at 7:23 p.m.',
-            'isLike': false,
-            'likeno': 12
+            'goods_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+            'title': 'Electronic keyboard',
+            'price': '\$66',
+            'isCollection': true,
+            'collection_num': 12,
+            'publisher': 'William Jefferson',
+            'publish_time': 'June 17,2016 at 7:23 p.m.',
+            'publisher_avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500'
           },
         ]
     );
@@ -109,88 +77,160 @@ class ForrentVm extends _$ForrentVm {
     return state;
   }
 
-  // 设置当前的 tabsRouter 和 pageController
-  Future setTabsRouterAndPageController(dynamic tabsRouter, dynamic pageController) async{
-    Log.d("setTabsRouterAndPageController---:$tabsRouter");
+  //刷新页面状态
+  void changeLoadingState(LoadState loadState, String? errorMsg) {
+    state = state.copyWith(
+        loadingState: loadState,
+        errorMessage: errorMsg
+    );
   }
 
-// 上拉加载
-  Future onLoadData() async {
-    Log.d("----Forrent_vm-----initListData");
-    // await Future.delayed(const Duration(seconds: 2));
-    // if(state.list.length >= state.filterCount){
-    //   return;
-    // }else {
-    //   int curPage = state.curPage + 1;
-    //   state = state.copyWith(curPage: curPage,);
-    //   getListData();
-    // }
-    // getListData();
+  // 初始化页面数据
+  initPageData() {
+    Log.d("----for_rent_vm-----initPageData   ${state.loadingState}");
+    onRefresh();
   }
 
-  // 获取list 列表数据
-  void getListData<T>() async {
-    Log.d("加载listData数据---------------start-----");
-    try {
-      //请求网络
-      Map<String, dynamic>  params = {
-        "curPage": state.curPage,
-        "pageSize": state.pageSize,
-      };
-      Log.d("请求参数------$params");
-      final result = await ForrentRepositoryInstance.fetchForrentList(params);
-      Log.d("请求完成结果------${result.data}");
-      //校验成功失败
-      if (result.isSuccess) {
-        // state = state.copyWith(serverTime: result.data);
-        state = state;
-        ToastEngine.show("获取数据成功");
-      } else {
-        ToastEngine.show(result.errorMsg ?? "Network Load Error");
-      }
-    } catch (e) {
-      ToastEngine.show("Error: $e");
+  // 上拉加载 更多
+  Future loadMore() async {
+    bool isShowing = await ref.read(communityVmProvider.notifier).isCurrentPageViewShowing(CommunityPageViewIdxData.forRent);
+    if(isShowing){
+      Log.d("----for_rent_vm-----loadMore");
+      // await Future.delayed(const Duration(seconds: 2));
+      // if(state.list.length >= state.filterCount){
+      //   return;
+      // }else {
+      //   int curPage = state.curPage + 1;
+      //   state = state.copyWith(curPage: curPage,);
+      //   getListData();
+      // }
+      // 检查 curPage 是否为 null,并初始化为 1
+      int newCurPage = state.curPage ?? 1;
+      state = state.copyWith(curPage: ++newCurPage);
+      getListData();
+    }else {
+      refreshController.finishRefresh();
     }
   }
 
 
   // 下拉刷新
-  Future refreshListData() async {
-    Log.d("----property_forrent_vm-----refreshListData ");
+  Future onRefresh() async {
+    bool isShowing = await ref.read(communityVmProvider.notifier).isCurrentPageViewShowing(CommunityPageViewIdxData.forRent);
+    if(isShowing){
+      // 当前pageView 页面正处于显示状态
+      Log.d("----for_rent_vm-----onRefresh ");
+      // await Future.delayed(const Duration(seconds: 2));
+      state = state.copyWith(curPage: 1);
+      getListData();
+    }else {
+      refreshController.finishRefresh();
+      // //更新展示的状态
+      changeLoadingState(LoadState.State_Success, null);
+    }
+  }
 
-    // await Future.delayed(const Duration(seconds: 2));
+  // 重试请求
+  Future retryRequest() async {
+    bool isShowing = await ref.read(communityVmProvider.notifier).isCurrentPageViewShowing(CommunityPageViewIdxData.forRent);
+    if(isShowing){
+      state = state.copyWith(curPage: 1);
+      _needShowPlaceholder = true;
+      getListData();
+    }
+  }
 
-    state = state.copyWith(curPage: 1, pageSize: 10);
-    // ref.invalidateSelf();
-    // ref.invalidate(ForrentVmProvider);
-    getListData();
 
-  }
+  // 获取list 列表数据
+  Future getListData<T>() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading, null);
+    }
+
+    Log.d("for_rent加载listData数据---------------start--${state.curPage}---");
+    //   try {
+    //     //请求网络
+    //     Map<String, dynamic>  params = {
+    //       "curPage": state.curPage,
+    //       "pageSize": state.pageSize,
+    //     };
+    //     Log.d("请求参数------$params");
+    //     final result = await propertyNewsRepository.fetchPropertyNewsList(params);
+    //     Log.d("请求完成结果------${result.data}");
+    //     //校验成功失败
+    //     if (result.isSuccess) {
+    //       // state = state.copyWith(serverTime: result.data);
+    //       state = state;
+    //   handleList(listResult.data?.rows);
+    //       ToastEngine.show("获取数据成功");
+    //     } else {
+    //   errorMessage = listResult.errorMsg;
+    //   changeLoadingState(LoadState.State_Error);
+    //       ToastEngine.show(result.errorMsg ?? "Network Load Error");
+    //     }
+    //   } catch (e) {
+    //     ToastEngine.show("Error: $e");
+    //   }
+
+    await Future.delayed(const Duration(milliseconds: 1500));
+    final List<Map<String, dynamic>> listData = [
+      {
+        'id':1,
+        'goods_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'title': 'Electronic keyboard',
+        'price': '\$66',
+        'isCollection': true,
+        'collection_num': 12,
+        'publisher': 'William Jefferson',
+        'publish_time': 'June 17,2016 at 7:23 p.m.',
+        'publisher_avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500'
+      },
+      {
+        'id':2,
+        'goods_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'title': 'Electronic keyboard',
+        'price': '\$88',
+        'isCollection': false,
+        'collection_num': 12,
+        'publisher': 'William Jefferson',
+        'publish_time': 'June 17,2016 at 7:23 p.m.',
+        'publisher_avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500'
+      },
+      {
+        'id':3,
+        'goods_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'title': 'Electronic keyboard',
+        'price': '\$66',
+        'isCollection': true,
+        'collection_num': 12,
+        'publisher': 'William Jefferson',
+        'publish_time': 'June 17,2016 at 7:23 p.m.',
+        'publisher_avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500'
+      },
+    ];
 
+    if (state.curPage == 1) {
+      //刷新的方式
+      state = state.copyWith(list: listData);
+      refreshController.finishRefresh();
+      // //更新展示的状态
+      changeLoadingState(LoadState.State_Success, null);
+    } else {
+      //加载更多
+      final allList = state.list;
+      allList?.addAll(listData);
+      state = state.copyWith(list: allList);
+      refreshController.finishLoad();
 
-  // 点击 like comments  share
-  void handlerClickActionBtn(String? actionStr, item){
-    final id = item['id'];
-    switch (actionStr) {
-      case 'like':
-        Log.d("点击了 点赞");
-        handlerGotoDetail(id);
-        break;
-      case 'comments':
-        Log.d("点击了 评论");
-        handlerGotoDetail(id);
-        break;
-      case 'share':
-        Log.d("点击了 分享");
-        handlerGotoDetail(id);
-        break;
-      default:
-        break;
     }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
   }
+
   // 去详情页面
   void handlerGotoDetail(id){
     Log.d("去详情页面");
-    appRouter.push(GaragesaleDetailPageRoute(id: id, type: 'forrent'));
+    appRouter.push(GaragesaleDetailPageRoute(id: id, type: 'forRent'));
   }
 }

+ 1 - 1
packages/cpt_community/lib/modules/garage/for_rent/for_rent_vm.g.dart

@@ -6,7 +6,7 @@ part of 'for_rent_vm.dart';
 // RiverpodGenerator
 // **************************************************************************
 
-String _$forrentVmHash() => r'be5b4f8c70608acb43dcdf0a5562a821168feaca';
+String _$forrentVmHash() => r'057fddba190bba20681aadf8965195e6ec1c54be';
 
 /// See also [ForrentVm].
 @ProviderFor(ForrentVm)

+ 94 - 84
packages/cpt_community/lib/modules/garage/for_sale/for_sale_page.dart

@@ -1,14 +1,17 @@
-import 'package:cpt_community/components/garage_card_content.dart';
-import 'package:cpt_community/components/garage_card_footer.dart';
+import 'package:cpt_community/modules/garage/for_rent/for_rent_page.dart';
 import 'package:cs_resources/generated/assets.dart';
 import 'package:flutter/material.dart';
 import 'package:auto_route/auto_route.dart';
 import 'package:flutter/rendering.dart';
 import 'package:flutter_hooks/flutter_hooks.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:shared/utils/color_utils.dart';
+import 'package:shared/utils/ext_dart.dart';
 import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/load_state_layout.dart';
 import 'package:widgets/my_button.dart';
 import 'package:widgets/my_load_image.dart';
 import 'package:widgets/ext/ex_widget.dart';
@@ -17,7 +20,7 @@ import 'package:widgets/my_appbar.dart';
 import 'package:cs_resources/theme/app_colors_theme.dart';
 import 'package:widgets/widget_export.dart';
 
-import '../../../components/garage_card_header.dart';
+import '../../../components/garage_card.dart';
 import '../../../components/newfeed_card_header.dart';
 import '../../../router/page/community_page_router.dart';
 import 'for_sale_vm.dart';
@@ -35,104 +38,111 @@ class ForsalePage extends HookConsumerWidget {
     }
   }
 
+
   @override
   Widget build(BuildContext context, WidgetRef ref) {
     final vm = ref.read(forsaleVmProvider.notifier);
+    final state = ref.watch(forsaleVmProvider);
+    // final appConfigState = ref.watch(appConfigServiceProvider)
+
+
+    useEffect(() {
+      // 组件挂载时执行 - 执行接口请求
+      Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+        Log.d("garage_forsale_page 组件卸载时执行");
+      };
+    }, []);
+
     return Scaffold(
       // appBar: MyAppBar.appBar(
       //   context,
       //   "Forsale",
       //   backgroundColor: context.appColors.whiteBG,
       // ),
-      // backgroundColor: ColorUtils.string2Color("#F2F3F6"),
-      body: Column(
-        children: [
-            Expanded(
-              child: EasyRefresh(
-                // 上拉加载
-                onLoad: () async{
-                  Log.d("----onLoad");
-                  vm.onLoadData();
-                },
-                // 下拉刷新
-                onRefresh: () async{
-                  Log.d("----onRefresh");
-                  vm.refreshListData();
-                },
-                child: _buildForsaleFeedList(context, ref, vm),
+      backgroundColor: ColorUtils.string2Color("#F2F3F6"),
+      body: SizedBox(
+        width: double.infinity,
+        height: double.infinity,
+        child: EasyRefresh(
+          controller: vm.refreshController,
+          // 上拉加载
+          onLoad: () async{
+            Log.d("----onLoad");
+            vm.loadMore();
+          },
+          // 下拉刷新
+          onRefresh: () async{
+            Log.d("----onRefresh");
+            vm.onRefresh();
+          },
+          child: LoadStateLayout(
+            state: state.loadingState,
+            errorMessage: state.errorMessage,
+            errorRetry: () {
+              vm.retryRequest();
+            },
+            successSliverWidget:[
+              SliverGrid(
+                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
+                  crossAxisCount: 2, // 每行显示两个项目
+                  mainAxisSpacing: 15,
+                  crossAxisSpacing: 15,
+                  // childAspectRatio: 166.5/214, // 宽高比
+                  // childAspectRatio:  166.5/214 * ScreenUtil.getAdapterSizeCtx(context, 166.5/214), // 宽高比
+                  childAspectRatio: 166.5/214 * ((166.5/214).ap), // 宽高比
+                ),
+                delegate: SliverChildBuilderDelegate(
+                      (context, index) {
+                    return  _buildForsaleItem(context, ref, state.list[index], vm).onTap((){
+                      vm.handlerGotoDetail(state.list[index]['id']);
+                    });
+                  },
+                  childCount: state.list.length,
+                ),
               ),
-            )
-        ],
-      )
+            ],
+          ),
+        ).marginOnly(left: 15,right: 15,top: 15,bottom: 15)
+      ),
     );
   }
 
   Widget _buildForsaleItem(BuildContext context, WidgetRef ref, item, vm){
-    return Container(
+    return SizedBox(
       width: double.infinity,
-      //   color: Colors.yellow,
-      child: Stack(
-        children: [
-          Container(
-            margin: const EdgeInsets.only(left: 15, right: 15,top: 14,bottom: 14),
-            color: Colors.white,
-            padding: const EdgeInsets.only(left: 15, right: 15,top: 17,bottom: 17),
-            height: 280,
-            child: Column(
-                mainAxisAlignment: MainAxisAlignment.center,
-                crossAxisAlignment: CrossAxisAlignment.start,
-                children: [
-                  // 卡片头部(头像 标题 时间)
-                  GarageCardHeader(
-                    key: UniqueKey(),
-                    itemObj: item,
-                  ),
-                  const SizedBox(height: 15),
-                  // 卡片中间 (文字和图片)
-                  // Expanded(
-                  //   child: GarageCardContent(
-                  //     key: UniqueKey(),
-                  //     content: item['content'],
-                  //     imageUrls: item['imageUrls'],
-                  //   ),
-                  // ),
-                  const SizedBox(height: 26),
-                  // // 卡片底部 (点赞 评论 分享)
-                  // GarageCardFooter(
-                  //     key: UniqueKey(),
-                  //     isLike: item['isLike'],
-                  //     onLike: (){
-                  //       vm.handlerClickActionBtn('like', item);
-                  //     },
-                  //     onComment: (){
-                  //       vm.handlerClickActionBtn('comments', item);
-                  //     },
-                  //     onShare: (){
-                  //       vm.handlerClickActionBtn('share', item);
-                  //     },
-                  // ),
-                ]
+      child: Container(
+        decoration: BoxDecoration(
+          color: context.appColors.whiteBG,
+          borderRadius: BorderRadius.circular(8),
+          boxShadow: [
+            BoxShadow(
+              color: ColorUtils.string2Color('#E5E5E5'),
+              offset: const Offset(0, 2),
+              blurRadius: 8,
             ),
-          ),
-        ],
+          ],
+        ),
+        child: Column(
+            mainAxisAlignment: MainAxisAlignment.start,
+            crossAxisAlignment: CrossAxisAlignment.start,
+            mainAxisSize: MainAxisSize.max,
+            children: [
+              // 卡片头部(头像 标题 时间)
+              Expanded(
+                child: GarageCard(
+                  key: UniqueKey(),
+                  itemObj: item,
+                  onClickColleciotn: (dynamic collectionValue){
+                    Log.d("点击了收藏按钮  --${item['id']}-$collectionValue");
+                    return true;
+                  }
+                ),
+              ),
+            ]
+        ),
       ),
     );
   }
-
-  Widget _buildForsaleFeedList(BuildContext context, WidgetRef ref, vm){
-    final itemList = vm.state.list?? [];
-    if(itemList.isEmpty){
-      return const Center(child: Text('No Data'));
-    }else {
-      List itemsList = vm.state.list.toList();
-      return ListView.builder(
-        key: UniqueKey(),
-        itemCount: itemsList.length,
-        itemBuilder: (context, index) {
-          return _buildForsaleItem(context, ref, itemsList[index], vm);
-        },
-      );
-    }
-    // return Text("879424");
-  }
 }

+ 26 - 14
packages/cpt_community/lib/modules/garage/for_sale/for_sale_state.dart

@@ -1,34 +1,44 @@
+import 'package:widgets/load_state_layout.dart';
+
 class ForsaleState {
   int? activeTabIndex =0;
-  int? curPage =0;
-  int? pageSize =10;
-  int? filterCount =0;
-  List<String>? tabsList = ['For Sale', 'For Rent'];
-  List<Map<String, dynamic>>? list = [];
+  //页面 LoadView 状态的展示
+  LoadState loadingState;
+  String? errorMessage;
+
+  int? curPage;
+  int? pageSize = 10;
+  int? filterCount = 0;
+  List<Map<String, dynamic>> list;
+
 
   ForsaleState({
     this.activeTabIndex,
-    this.curPage,
-    this.pageSize,
-    this.filterCount,
-    this.tabsList,
-    this.list,
+    this.loadingState = LoadState.State_Loading,
+    String? errorMessage,
+    this.curPage = 1,
+    this.pageSize = 10,
+    this.filterCount = 0,
+    required this.list,
   });
 
   ForsaleState copyWith({
     int? activeTabIndex,
+    LoadState? loadingState,
+    String? errorMessage,
     int? curPage,
     int? pageSize,
     int? filterCount,
-    List<String>? tabsList,
     List<Map<String, dynamic>>? list,
+    List<String>? tabsList,
   }) {
     return ForsaleState(
       activeTabIndex: activeTabIndex ?? this.activeTabIndex,
+      loadingState: loadingState ?? this.loadingState,
+      errorMessage: errorMessage ?? this.errorMessage,
       curPage: curPage ?? this.curPage,
       pageSize: pageSize ?? this.pageSize,
       filterCount: filterCount ?? this.filterCount,
-      tabsList: tabsList ?? this.tabsList,
       list: list ?? this.list,
     );
   }
@@ -36,10 +46,11 @@ class ForsaleState {
   Map<String, dynamic> toMap() {
     return {
       'activeTabIndex': this.activeTabIndex,
+      'loadingState': this.loadingState,
+      'errorMessage': this.errorMessage,
       'curPage': this.curPage,
       'pageSize': this.pageSize,
       'filterCount': this.filterCount,
-      'tabsList': this.tabsList,
       'list': this.list,
     };
   }
@@ -47,10 +58,11 @@ class ForsaleState {
   factory ForsaleState.fromMap(Map<String, dynamic> map) {
     return ForsaleState(
       activeTabIndex: map['activeTabIndex'] as int,
+      loadingState: map['loadingState'] as LoadState,
+      errorMessage: map['errorMessage'] as String,
       curPage: map['curPage'] as int,
       pageSize: map['pageSize'] as int,
       filterCount: map['filterCount'] as int,
-      tabsList: map['tabsList'] as List<String>,
       list: map['list'] as List<Map<String, dynamic>>,
     );
   }

+ 187 - 113
packages/cpt_community/lib/modules/garage/for_sale/for_sale_vm.dart

@@ -1,11 +1,16 @@
+import 'package:cpt_community/modules/community/community_vm.dart';
 import 'package:cs_resources/generated/assets.dart';
 import 'package:flutter/cupertino.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:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
 
 import '../../../router/page/community_page_router.dart';
+import '../../community/community_page.dart';
+import '../../community/community_pageview_idx_data.dart';
 import '../garagesale_detail/garagesale_detail_page.dart';
 import 'for_sale_respository.dart';
 import 'for_sale_state.dart';
@@ -15,76 +20,18 @@ part 'for_sale_vm.g.dart';
 @riverpod
 class ForsaleVm extends _$ForsaleVm {
   late ForsaleRepository ForsaleRepositoryInstance;
+  bool _needShowPlaceholder = true; //是否展示LoadingView
+
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,  //允许刷新
+    controlFinishLoad: true,   //允许加载
+  );
+
   ForsaleState initState() {
     return ForsaleState(
-      list: [
-          {
-            'id':1,
-            'goods_img':  'https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg',
-            'title': 'Electronic keyboard',
-            'price': '\$66',
-            'isCollection': true,
-            'collection_num': '12',
-            'publisher': 'William Jefferson',
-            'publish_time': 'June 17,2016 at 7:23 p.m.',
-            'publisher_avator': Assets.communityCamera
-          },
-          {
-            'id':2,
-            'goods_img':  'https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg',
-            'title': 'Electronic keyboard',
-            'price': '\$88',
-            'isCollection': false,
-            'collection_num': '12',
-            'publisher': 'William Jefferson',
-            'publish_time': 'June 17,2016 at 7:23 p.m.',
-            'publisher_avator': Assets.communityCamera
-          },
-          {
-            'id':3,
-            'goods_img':  'https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg',
-            'title': 'Electronic keyboard',
-            'price': '\$66',
-            'isCollection': true,
-            'collection_num': '12',
-            'publisher': 'William Jefferson',
-            'publish_time': 'June 17,2016 at 7:23 p.m.',
-            'publisher_avator': Assets.communityCamera
-          },
-          {
-            'id':4,
-            'goods_img':  'https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg',
-            'title': 'Electronic keyboard',
-            'price': '\$88',
-            'isCollection': false,
-            'collection_num': '12',
-            'publisher': 'William Jefferson',
-            'publish_time': 'June 17,2016 at 7:23 p.m.',
-            'publisher_avator': Assets.communityCamera
-          },
-          {
-            'id':5,
-            'goods_img':  'https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg',
-            'title': 'Electronic keyboard',
-            'price': '\$66',
-            'isCollection': true,
-            'collection_num': '12',
-            'publisher': 'William Jefferson',
-            'publish_time': 'June 17,2016 at 7:23 p.m.',
-            'publisher_avator': Assets.communityCamera
-          },
-          {
-            'id':6,
-            'goods_img':  'https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg',
-            'title': 'Electronic keyboard',
-            'price': '\$88',
-            'isCollection': false,
-            'collection_num': '12',
-            'publisher': 'William Jefferson',
-            'publish_time': 'June 17,2016 at 7:23 p.m.',
-            'publisher_avator': Assets.communityCamera
-          },
-      ]
+      list: []
     );
   }
 
@@ -98,62 +45,189 @@ class ForsaleVm extends _$ForsaleVm {
     return state;
   }
 
-   // 设置当前的 tabsRouter 和 pageController
-  Future setTabsRouterAndPageController(dynamic tabsRouter, dynamic pageController) async{
-    Log.d("setTabsRouterAndPageController---:$tabsRouter");
+
+ //刷新页面状态
+  void changeLoadingState(LoadState loadState, String? errorMsg) {
+    state = state.copyWith(
+        loadingState: loadState,
+        errorMessage: errorMsg
+    );
   }
 
-// 上拉加载
-  Future onLoadData() async {
-    Log.d("----Forsale_vm-----initListData");
-    // await Future.delayed(const Duration(seconds: 2));
-    // if(state.list.length >= state.filterCount){
-    //   return;
-    // }else {
-    //   int curPage = state.curPage + 1;
-    //   state = state.copyWith(curPage: curPage,);
-    //   getListData();
-    // }
-    // getListData();
+  // 初始化页面数据
+  initPageData() {
+    Log.d("----for_sale_vm-----initPageData   ${state.loadingState}");
+    onRefresh();
   }
 
-  // 获取list 列表数据
-  void getListData<T>() async {
-    Log.d("加载listData数据---------------start-----");
-    try {
-      //请求网络
-      Map<String, dynamic>  params = {
-        "curPage": state.curPage,
-        "pageSize": state.pageSize,
-      };
-      Log.d("请求参数------$params");
-      final result = await ForsaleRepositoryInstance.fetchForsaleList(params);
-      Log.d("请求完成结果------${result.data}");
-      //校验成功失败
-      if (result.isSuccess) {
-        // state = state.copyWith(serverTime: result.data);
-        state = state;
-        ToastEngine.show("获取数据成功");
-      } else {
-        ToastEngine.show(result.errorMsg ?? "Network Load Error");
-      }
-    } catch (e) {
-      ToastEngine.show("Error: $e");
+  // 上拉加载 更多
+  Future loadMore() async {
+    bool isShowing = await ref.read(communityVmProvider.notifier).isCurrentPageViewShowing(CommunityPageViewIdxData.forSale);
+    if(isShowing){
+      Log.d("----for_sale_vm-----loadMore");
+      // await Future.delayed(const Duration(seconds: 2));
+      // if(state.list.length >= state.filterCount){
+      //   return;
+      // }else {
+      //   int curPage = state.curPage + 1;
+      //   state = state.copyWith(curPage: curPage,);
+      //   getListData();
+      // }
+      // 检查 curPage 是否为 null,并初始化为 1
+      int newCurPage = state.curPage ?? 1;
+      state = state.copyWith(curPage: ++newCurPage);
+      getListData();
+    }else {
+      refreshController.finishRefresh();
     }
   }
 
 
   // 下拉刷新
-  Future refreshListData() async {
-    Log.d("----property_news_vm-----refreshListData ");
+  Future onRefresh() async {
+    bool isShowing = await ref.read(communityVmProvider.notifier).isCurrentPageViewShowing(CommunityPageViewIdxData.forSale);
+    if(isShowing){
+      // 当前pageView 页面正处于显示状态
+      Log.d("----forsale_vm-----onRefresh ");
+      // await Future.delayed(const Duration(seconds: 2));
+      state = state.copyWith(curPage: 1);
+      getListData();
+    }else {
+      refreshController.finishRefresh();
+      // //更新展示的状态
+      changeLoadingState(LoadState.State_Success, null);
+    }
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    bool isShowing = await ref.read(communityVmProvider.notifier).isCurrentPageViewShowing(CommunityPageViewIdxData.forSale);
+    if(isShowing){
+      state = state.copyWith(curPage: 1);
+      _needShowPlaceholder = true;
+      getListData();
+    }
+  }
+
 
-    // await Future.delayed(const Duration(seconds: 2));
+  // 获取list 列表数据
+  Future getListData<T>() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading, null);
+    }
 
-    state = state.copyWith(curPage: 1, pageSize: 10);
-    // ref.invalidateSelf();
-    // ref.invalidate(ForsaleVmProvider);
-    getListData();
+    Log.d("for_sale_vm加载listData数据---------------start--${state.curPage}---");
+    //   try {
+    //     //请求网络
+    //     Map<String, dynamic>  params = {
+    //       "curPage": state.curPage,
+    //       "pageSize": state.pageSize,
+    //     };
+    //     Log.d("请求参数------$params");
+    //     final result = await propertyNewsRepository.fetchPropertyNewsList(params);
+    //     Log.d("请求完成结果------${result.data}");
+    //     //校验成功失败
+    //     if (result.isSuccess) {
+    //       // state = state.copyWith(serverTime: result.data);
+    //       state = state;
+    //   handleList(listResult.data?.rows);
+    //       ToastEngine.show("获取数据成功");
+    //     } else {
+    //   errorMessage = listResult.errorMsg;
+    //   changeLoadingState(LoadState.State_Error);
+    //       ToastEngine.show(result.errorMsg ?? "Network Load Error");
+    //     }
+    //   } catch (e) {
+    //     ToastEngine.show("Error: $e");
+    //   }
+
+    await Future.delayed(const Duration(milliseconds: 1500));
+    final List<Map<String, dynamic>> listData = [
+      {
+        'id':1,
+        'goods_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'title': 'Electronic keyboard',
+        'price': '\$66',
+        'isCollection': true,
+        'collection_num': 12,
+        'publisher': 'William Jefferson',
+        'publish_time': 'June 17,2016 at 7:23 p.m.',
+        'publisher_avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500'
+      },
+      {
+        'id':2,
+        'goods_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'title': 'Electronic keyboard',
+        'price': '\$88',
+        'isCollection': false,
+        'collection_num': 12,
+        'publisher': 'William Jefferson',
+        'publish_time': 'June 17,2016 at 7:23 p.m.',
+        'publisher_avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500'
+      },
+      {
+        'id':3,
+        'goods_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'title': 'Electronic keyboard',
+        'price': '\$66',
+        'isCollection': true,
+        'collection_num': 12,
+        'publisher': 'William Jefferson',
+        'publish_time': 'June 17,2016 at 7:23 p.m.',
+        'publisher_avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500'
+      },
+      {
+        'id':4,
+        'goods_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'title': 'Electronic keyboard',
+        'price': '\$88',
+        'isCollection': false,
+        'collection_num': 12,
+        'publisher': 'William Jefferson',
+        'publish_time': 'June 17,2016 at 7:23 p.m.',
+        'publisher_avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500'
+      },
+      {
+        'id':5,
+        'goods_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'title': 'Electronic keyboard',
+        'price': '\$66',
+        'isCollection': true,
+        'collection_num': 12,
+        'publisher': 'William Jefferson',
+        'publish_time': 'June 17,2016 at 7:23 p.m.',
+        'publisher_avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500'
+      },
+      {
+        'id':6,
+        'goods_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'title': 'Electronic keyboard',
+        'price': '\$88',
+        'isCollection': false,
+        'collection_num': 12,
+        'publisher': 'William Jefferson',
+        'publish_time': 'June 17,2016 at 7:23 p.m.',
+        'publisher_avator': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500'
+      },
+    ];
+
+    if (state.curPage == 1) {
+      //刷新的方式
+      state = state.copyWith(list: listData);
+      refreshController.finishRefresh();
+      // //更新展示的状态
+      changeLoadingState(LoadState.State_Success, null);
+    } else {
+      //加载更多
+      final allList = state.list;
+      allList.addAll(listData);
+      state = state.copyWith(list: allList);
+      refreshController.finishLoad();
+
+    }
 
+    // 最后赋值
+    _needShowPlaceholder = false;
   }
 
 
@@ -180,6 +254,6 @@ class ForsaleVm extends _$ForsaleVm {
   // 去详情页面
   void handlerGotoDetail(id){
     Log.d("去详情页面");
-    appRouter.push(GaragesaleDetailPageRoute(id: id, type: 'forsale'));
+    appRouter.push(GaragesaleDetailPageRoute(id: id, type: 'forSale'));
   }
 }

+ 1 - 1
packages/cpt_community/lib/modules/garage/for_sale/for_sale_vm.g.dart

@@ -6,7 +6,7 @@ part of 'for_sale_vm.dart';
 // RiverpodGenerator
 // **************************************************************************
 
-String _$forsaleVmHash() => r'a63dd5772509b90fac8ede5ec692e01754dc6a22';
+String _$forsaleVmHash() => r'bbbdd29799a5d2bf561b9a247ec85768c3fc607b';
 
 /// See also [ForsaleVm].
 @ProviderFor(ForsaleVm)

+ 0 - 278
packages/cpt_community/lib/modules/garage/garage_page.dart

@@ -1,278 +0,0 @@
-
-import 'package:cpt_community/components/newsfeed_card_content.dart';
-import 'package:cpt_community/components/newsfeed_card_footer.dart';
-import 'package:cs_resources/generated/assets.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/rendering.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:router/ext/auto_router_extensions.dart';
-import 'package:shared/utils/color_utils.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_load_image.dart';
-import 'package:widgets/my_text_view.dart';
-import 'package:widgets/widget_export.dart';
-
-import '../../components/newfeed_card_header.dart';
-import '../../router/page/community_page_router.dart';
-import '../garage/garage_vm.dart';
-
-import 'garage_tabs.dart';
-
-import 'garage_vm.dart';
-
-
-@RoutePage()
-class GaragePage extends HookConsumerWidget {
-  const GaragePage({Key? key}) : super(key: key);
-
-  //启动当前页面
-  static void startInstance({BuildContext? context}) {
-    if (context != null) {
-      context.router.push(const GaragePageRoute());
-    } else {
-      appRouter.push(const GaragePageRoute());
-    }
-  }
-
-  Widget _buildTabsSection(BuildContext context, WidgetRef ref, tabsRouter){
-    final vm = ref.read(garageVmProvider.notifier);
-    return Container(
-      width: double.infinity,
-      padding: const EdgeInsets.only(left: 15, right: 15,top: 14,bottom: 14),
-      child: GarageTabs(
-        key: UniqueKey(),
-        tabsList: vm.state.tabsList,
-        tabsRouter: tabsRouter,
-        onClickAction:(activeTabIdx){
-          vm.handlerChangeTab(activeTabIdx, tabsRouter);
-        }
-      ),
-    );
-  }
-
-  Widget _buildPostSection(BuildContext context, WidgetRef ref, vm){
-    return Container(
-      height: 65.5,
-      width: double.infinity,
-      padding: const EdgeInsets.only(left: 20, right: 20),
-      color: Colors.white,
-      child: Row(
-        children: [
-          const MyAssetImage(Assets.communityNesFeed, width: 45,height: 45,),
-          Expanded(
-            child: Row(
-              children: [
-                Expanded(
-                  child: Container(
-                    // height: 65.5,
-                    // color: Colors.blue,
-                    child: MyTextView(
-                      "Sell Item",
-                      textColor: ColorUtils.string2Color('#000000'),
-                      fontSize: 15,
-                      marginLeft: 15,
-                      alignment: Alignment.centerLeft,
-                      textAlign: TextAlign.left,
-                      backgroundColor: ColorUtils.string2Color('#ffffff'),
-                      maxLines: 1,
-                      isFontMedium: true,
-                    ),
-                  ),
-                ),
-                const MyAssetImage(
-                  Assets.communityCamera,
-                  width: 21,
-                  height: 16.5,
-                ),
-              ],
-            ).onTap((){
-              vm.handlerGotoPost(context);
-            }),
-          ),
-        ],
-      ),
-    );
-  }
-
-  Widget _buildNewsItem(BuildContext context, WidgetRef ref, item, vm){
-    return Container(
-        width: double.infinity,
-        // padding: const EdgeInsets.only(left: 15, right: 15,top: 14,bottom: 14),
-        color: Colors.yellow,
-        child: Container(
-          margin: const EdgeInsets.only(left: 15, right: 15,top: 14,bottom: 14),
-          color: Colors.white,
-          padding: const EdgeInsets.only(left: 15, right: 15,top: 17,bottom: 17),
-          height: 280,
-          child: Column(
-              mainAxisAlignment: MainAxisAlignment.center,
-              crossAxisAlignment: CrossAxisAlignment.start,
-              children: [
-                // 卡片头部(头像 标题 时间)
-                NewsFeedCardHeader(
-                  key: UniqueKey(),
-                  title: item['title'],
-                  avator: item['avator'],
-                  time: item['time'],
-                ),
-                const SizedBox(height: 15),
-                // 卡片中间 (文字和图片)
-                Expanded(
-                  child: NewsFeedCardContent(
-                    key: UniqueKey(),
-                    content: item['content'],
-                    imageUrls: item['imageUrls'],
-                  ),
-                ),
-                const SizedBox(height: 26),
-                // // 卡片底部 (点赞 评论 分享)
-                NewsFeedCardFooter(
-                  key: UniqueKey(),
-                  isLike: item['isLike'],
-                ),
-              ]
-          ),
-        )
-    );
-  }
-
-  Widget _buildNewsFeedList(BuildContext context, WidgetRef ref, vm){
-    final itemList = vm.state.list?? [];
-    if(itemList.isEmpty){
-      return const Center(child: Text('No Data'));
-    }else {
-      List itemsList = vm.state.list.toList();
-      return ListView.builder(
-        key: UniqueKey(),
-        itemCount: itemsList.length,
-        itemBuilder: (context, index) {
-          return _buildNewsItem(context, ref, itemsList[index], vm);
-        },
-      );
-    }
-  }
-
-
-  @override
-  Widget build(BuildContext context, WidgetRef ref) {
-    final vm = ref.read(garageVmProvider.notifier);
-
-    return Scaffold(
-      appBar: MyAppBar.searchAppBar(
-        context,
-        backgroundColor: context.appColors.whiteBG,
-        actions: [
-          IconButton(
-            icon: const Icon(Icons.search),
-            onPressed: () {
-              // do something
-            },
-          ),
-        ],
-      ),
-      backgroundColor: ColorUtils.string2Color("#F2F3F6"),
-      body: NestedScrollView(
-        headerSliverBuilder: (context, innerBoxIsScrolled) {
-          return [
-            SliverToBoxAdapter(
-              child: Consumer(
-                builder: (context, ref, _) {
-                  return _buildTopSection(context, ref, vm);
-                },
-              ),
-            ),
-          ];
-        },
-        body: NotificationListener<ScrollNotification>(
-          onNotification: (ScrollNotification notification) {
-            // 是否拦截滚动  false 表示不拦截通知
-            // return _isPreventScroll(notification, ref);
-            return false;
-          },
-          child: AutoTabsRouter.pageView(
-            key: UniqueKey(),
-            routes: const [
-              ForsalePageRoute(),
-              ForrentPageRoute(),
-            ],
-            builder: (context, child, animation) {
-              final tabsRouter = AutoTabsRouter.of(context);
-              return Column(
-                children: [
-                  //  garagesale tab 分类 (For sale  For Rent)
-                  _buildTabsSection(context, ref, tabsRouter),
-                  // 发布 garage sale 组件
-                  _buildPostSection(context, ref, vm),
-                  Expanded(
-                    child: child,
-                  )
-                ],
-              );
-            },
-          ),
-        ),
-      ),
-    );
-  }
-
-  Widget _buildTopSection(BuildContext context, WidgetRef ref, vm) {
-    final topSectionsData = vm.topSectionsData;
-    int curTagIdx = 1;
-    return Container(
-      color: Colors.white,
-      padding: const EdgeInsets.only(top: 30, bottom: 30),
-      child: Center(
-        child: Row(
-          mainAxisAlignment: MainAxisAlignment.center,
-          crossAxisAlignment: CrossAxisAlignment.center,
-          children: List.generate(topSectionsData.length, (index) {
-            final item = topSectionsData[index];
-            return Flexible(
-              flex: 1,
-              child: Column(
-                children: [
-                  Container(
-                    width: MediaQuery.of(context).size.width / topSectionsData.length - 36,
-                    height: 70,
-                    decoration: BoxDecoration(
-                      // color: currentTabIdx == index ? ColorUtils.string2Color('#E6F2FF') : Colors.white,
-                      shape: BoxShape.circle, // 设置为圆形
-                      boxShadow: index == curTagIdx ? [
-                        BoxShadow(
-                          color: context.appColors.tabLightBlueShadow, // 设置阴影颜色
-                          blurRadius: 5, // 设置模糊半径
-                          spreadRadius: 0.05, // 控制阴影扩散
-                          offset: const Offset(0, 4), // 设置阴影偏移量
-                        ),                          ] : [],// 未选中时无阴影,
-                    ),
-                    child: MyAssetImage(
-                      item['icon'],
-                      width: MediaQuery.of(context).size.width / topSectionsData.length - 36,
-                      height: 70,
-                    ).onTap(() {
-                      vm.handlerChangeCommunityType(context,index);
-                    },
-                      type: ClickType.throttle,
-                    ),
-                  ),
-                  SizedBox.fromSize(size: const Size(0, 9)),
-                  MyTextView(
-                    item['title'],
-                    fontSize: 15,
-                    textColor: index == curTagIdx ? ColorUtils.string2Color('#4161D0'):Colors.black,
-                    textAlign: TextAlign.center,
-                    isFontMedium: true,
-                  ),
-                ],
-              ),
-            ).marginOnly(left: 18, right: 18, top: 10, bottom: 10);
-          }),
-        ),
-      ),
-    );
-  }
-}

+ 0 - 71
packages/cpt_community/lib/modules/garage/garage_repository.dart

@@ -1,71 +0,0 @@
-import 'package:domain/constants/api_constants.dart';
-import 'package:domain/entity/server_time.dart';
-import 'package:plugin_platform/platform_export.dart';
-import 'package:plugin_platform/http/dio_engine.dart';
-import 'package:plugin_platform/http/http_result.dart';
-import 'package:riverpod_annotation/riverpod_annotation.dart';
-import 'package:shared/utils/util.dart';
-
-import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:plugin_basic/provider/http_provider/http_provider.dart';
-
-import 'garage_state.dart';
-
-part 'garage_repository.g.dart';
-
-@Riverpod(keepAlive: true)
-GarageRepository garageRepository(Ref ref) {
-  final dioEngine = ref.watch(dioEngineProvider);
-  return GarageRepository(dioEngine: dioEngine);
-}
-
-/*
- * 数据仓库
- */
-class GarageRepository {
-  DioEngine dioEngine;
-
-  GarageRepository({required this.dioEngine});
-
-  Future<HttpResult<Object>> fetchGarageList(
-      Map<String, dynamic>? data, {
-        CancelToken? cancelToken,
-      }) async {
-    Map<String, dynamic> params = {};
-
-    // if (!Utils.isEmpty(type)) {
-    //   params["type"] = type!;
-    // }
-
-    params = data!;
-
-
-    Map<String, String> headers = {};
-
-    headers["Content-Type"] = "application/x-www-form-urlencoded";
-    headers["Accept"] = "application/x.yyjobs-api.v1+json";
-
-    final result = await dioEngine.requestNetResult(
-      // ApiConstants.apiServerTime, // api 地址
-      '/index.php/api/employee/extra/time', // api 地址
-      params: params,
-      headers: headers,
-      method: HttpMethod.GET,
-      isShowLoadingDialog: true,  //是否展示默认的Loading弹窗
-      networkDebounce: true,   //是否防抖防止重复请求
-      cancelToken: cancelToken,
-    );
-
-    //根据返回的结果,封装原始数据为Bean/Entity对象
-    if (result.isSuccess) {
-      //重新赋值data或list
-      final json = result.getDataJson();
-      var data = GarageState.fromMap(json!);
-      //重新赋值data或list
-      return result.convert<GarageState>(data: data);
-    }
-    return result.convert();
-  }
-
-
-}

+ 0 - 67
packages/cpt_community/lib/modules/garage/garage_state.dart

@@ -1,67 +0,0 @@
-import 'package:cs_resources/generated/assets.dart';
-import 'package:flutter/material.dart';
-import 'package:shared/utils/color_utils.dart';
-
-class GarageState {
-  List<Map<String, dynamic>>? topSectionsData;
-  int? curPage;
-  int? pageSize;
-  int? filterCount;
-  int? activeIndex = 0;
-  List tabsList = [];
-  List? list = [];
-
-  GarageState({
-    this.topSectionsData,
-    this.curPage,
-    this.pageSize,
-    this.filterCount,
-    this.activeIndex,
-    required this.tabsList,
-    this.list,
-  });
-
-  GarageState copyWith({
-    List<Map<String, dynamic>>? topSectionsData,
-    int? curPage,
-    int? pageSize,
-    int? filterCount,
-    int? activeIndex,
-    List? tabsList,
-    List? list,
-  }) {
-    return GarageState(
-      topSectionsData: topSectionsData ?? this.topSectionsData,
-      curPage: curPage ?? this.curPage,
-      pageSize: pageSize ?? this.pageSize,
-      filterCount: filterCount ?? this.filterCount,
-      activeIndex: activeIndex ?? this.activeIndex,
-      tabsList: tabsList ?? this.tabsList,
-      list: list ?? this.list,
-    );
-  }
-
-  Map<String, dynamic> toMap() {
-    return {
-      'topSectionsData': this.topSectionsData,
-      'curPage': this.curPage,
-      'pageSize': this.pageSize,
-      'filterCount': this.filterCount,
-      'activeIndex': this.activeIndex,
-      'tabsList': this.tabsList,
-      'list': this.list,
-    };
-  }
-
-  factory GarageState.fromMap(Map<String, dynamic> map) {
-    return GarageState(
-      topSectionsData: map['topSectionsData'] as List<Map<String, dynamic>>,
-      curPage: map['curPage'] as int,
-      pageSize: map['pageSize'] as int,
-      filterCount: map['filterCount'] as int,
-      activeIndex: map['activeIndex'] as int,
-      tabsList: map['tabsList'] as List,
-      list: map['list'] as List,
-    );
-  }
-}

+ 0 - 167
packages/cpt_community/lib/modules/garage/garage_vm.dart

@@ -1,167 +0,0 @@
-
-import 'package:cpt_community/modules/community/community_page.dart';
-import 'package:cs_resources/generated/assets.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/path/router_path.dart';
-import 'package:shared/utils/color_utils.dart';
-import 'package:shared/utils/log_utils.dart';
-import 'package:auto_route/auto_route.dart';
-
-import 'garage_page.dart';
-import 'garage_state.dart';
-import 'garage_repository.dart';
-
-part 'garage_vm.g.dart';
-
-@riverpod
-class GarageVm extends _$GarageVm {
-  late GarageRepository garageRepository;
-  get topSectionsData => state.topSectionsData;
-  GarageState initState() {
-    return GarageState(
-        activeIndex: 0,
-        topSectionsData: [
-          {
-            "title": "News Feed",
-            "icon": Assets.communityNesFeed,
-            "pageStartInstanceFn": CommunityPage.startInstance,
-            "page": const CommunityPage(),
-          },
-          {
-            "title": "Garage Sale",
-            "icon": Assets.communityGarageSale,
-            "pageStartInstanceFn": GaragePage.startInstance,
-            "page": const GaragePage(),
-          },
-        ],
-        tabsList: [
-          {
-            'title': 'For Sale',
-            'icon': null,
-            'active': true,
-            'activeTitleColor': Colors.white,
-            'activeTitleFontSize': 16,
-            'activeTitleBackgroundColor': ColorUtils.string2Color("#4161D0"),
-          },
-          {
-            'title': 'For Rent',
-            'icon': null,
-            'active': false,
-            'activeTitleColor': Colors.white,
-            'activeTitleFontSize': 16,
-            'activeTitleBackgroundColor': ColorUtils.string2Color("#4161D0"),
-          },
-        ],
-        list: [
-          {
-            'id':1,
-            'avator': Assets.communityCamera,
-            'title': '发动机上课士大夫',
-            'isFollow': false,
-            'content': '经典福克斯附件是的开飞机迪斯科封禁端口是否建立四道口附近 ……[More]',
-            'imageUrls': [],
-            'time': 'June 17,2016 at 7:23 p.m.',
-            'isLike': true,
-            'likeno': 12
-          },
-          {
-            'id':2,
-            'avator': Assets.communityCamera,
-            'title': '分等级付给的积分多少',
-            'isFollow': true,
-            'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
-            'imageUrls': ['https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg','https://img.alicdn.com/tfs/TB1h.o9O4MPMeJjy1XaXXcSsFXa-640-360.jpg'],
-            'time': 'June 17,2016 at 7:23 p.m.',
-            'isLike': true,
-            'likeno': 12
-          },
-        ]
-    );
-  }
-  @override
-  GarageState build(){
-    // 引入数据仓库
-    garageRepository = ref.read(garageRepositoryProvider);
-    // 初始化状态
-    GarageState state = initState();
-    return state;
-  }
-
-  // 上拉加载
-  Future onLoadData() async {
-    Log.d("----garage_vm-----initListData");
-    // await Future.delayed(const Duration(seconds: 2));
-    // if(state.list.length >= state.filterCount){
-    //   return;
-    // }else {
-    //   int curPage = state.curPage + 1;
-    //   state = state.copyWith(curPage: curPage,);
-    //   getListData();
-    // }
-    // getListData();
-  }
-
-  // 获取list 列表数据
-  void getListData<T>() async {
-    Log.d("加载listData数据---------------start-----");
-    try {
-      //请求网络
-      Map<String, dynamic>  params = {
-        "curPage": state.curPage,
-        "pageSize": state.pageSize,
-      };
-      Log.d("请求参数------$params");
-      final result = await garageRepository.fetchGarageList(params);
-      Log.d("请求完成结果------${result.data}");
-      //校验成功失败
-      if (result.isSuccess) {
-        // state = state.copyWith(serverTime: result.data);
-        state = state;
-        ToastEngine.show("获取数据成功");
-      } else {
-        ToastEngine.show(result.errorMsg ?? "Network Load Error");
-      }
-    } catch (e) {
-      ToastEngine.show("Error: $e");
-    }
-  }
-
-
-  // 下拉刷新
-  Future refreshListData() async {
-    Log.d("----garage_vm-----refreshListData ");
-
-    // await Future.delayed(const Duration(seconds: 2));
-
-    state = state.copyWith(curPage: 1, pageSize: 10);
-    // ref.invalidateSelf();
-    // ref.invalidate(garageVmProvider);
-    getListData();
-
-  }
-
-  // 切换tab
-  handlerChangeTab(int index, tabsRouter) {
-    tabsRouter.setActiveIndex(index);
-    // state = state.copyWith(tabsRouter: tabsRouter ,activeTabIdx: index);
-    Log.d("garage_vm handlerChangeTab--index:    $index");
-  }
-
-
-  // 点击发布的按钮 跳转到发布的页面
-  void handlerGotoPost(context){
-    // ComponentServiceManager().communityService.startCommunityPage();
-    AutoRouter.of(context).pushNamed(RouterPath.garageSalePost);
-  }
-
-
-  handlerChangeCommunityType(context, int index){
-    if(index == 0){
-      // 返回到 newsfeed 页面
-      Navigator.pop(context);
-    }
-  }
-
-}

+ 207 - 7
packages/cpt_community/lib/modules/garage/garagesale_detail/garagesale_detail_page.dart

@@ -1,19 +1,28 @@
+import 'package:cpt_community/modules/garage/garagesale_detail/garagesale_detail_vm.dart';
 import 'package:cpt_community/router/page/community_page_router.dart';
+import 'package:cs_resources/generated/assets.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/color_utils.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/load_state_layout.dart';
 import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_like_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
 
 @RoutePage()
 class GaragesaleDetailPage extends HookConsumerWidget {
   final int? id;
-  final String? type;  // forsale  forrent
+  final String? type;  // forSale  forRent
 
   const GaragesaleDetailPage({Key? key, required this.id, required this.type}) : super(key: key);
   // 启动当前页面
-  static void startInstance({BuildContext? context, int? id, String? type = 'forsale'}) {
+  static void startInstance({BuildContext? context, int? id, String? type = 'forSale'}) {
     if (context != null) {
       context.router.push(GaragesaleDetailPageRoute(id: id, type: type));
     } else {
@@ -24,17 +33,208 @@ class GaragesaleDetailPage extends HookConsumerWidget {
 
   @override
   Widget build(BuildContext context, WidgetRef ref) {
-    // final viewModel = ref.watch(newsfeedPostVmProvider.notifier);
+    final state = ref.watch(garagesaleDetailVmProvider);
+    final vm = ref.read(garagesaleDetailVmProvider.notifier);
+    final String pageTitle = (type == 'forSale') ? 'For Sale' :'For Rent';
+    GlobalKey _likeButtonKey = GlobalKey<MyLikeButtonState>();
+    useEffect((){
+      // 组件挂载时执行 - 执行接口请求
+      Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+      };
+    },[]);
 
     return Scaffold(
       appBar: MyAppBar.appBar(
         context,
-        "Garage Sale Detail",
-        backgroundColor: context.appColors.whiteBG,
+        pageTitle,
+        backgroundColor: context.appColors.backgroundWhite,
       ),
       backgroundColor: context.appColors.backgroundDefault,
-      body: Center(
-        child: Text("GaragesaleDetail"),
+      body: Column(
+        children: [
+          Expanded(
+            child: Stack(
+              children:[
+                LoadStateLayout(
+                  state: state.loadingState,
+                  errorMessage: state.errorMessage,
+                  errorRetry: () {
+                    vm.retryRequest();
+                  },
+                  successWidget: SingleChildScrollView(
+                    scrollDirection: Axis.vertical,
+                    physics: const BouncingScrollPhysics(),
+                    clipBehavior: Clip.none,
+                    child: _buildContentBox(context, ref),
+                  ),
+                ),
+                Visibility(
+                  visible: state.loadingState == LoadState.State_Success && state.datas!['contactType'] == 'WhatsApp',
+                  child: _buildWhatsApp(context, ref),
+                ),
+              ]
+            ),
+          ),
+          // 底部联系信息
+          Visibility(
+            visible: state.loadingState == LoadState.State_Success,
+            child: _buildBottomConcatInfo(context, ref, _likeButtonKey),
+          )
+        ],
+      ),
+    );
+  }
+
+  Widget _buildWhatsApp(BuildContext context, WidgetRef ref) {
+    final vm = ref.read(garagesaleDetailVmProvider.notifier);
+    return Positioned(
+      right: 15.5,
+      bottom: 42,
+      child: InkWell(
+        onTap: () {
+          // 跳转 WhatsApp
+          vm.gotoWhatsApp();
+        },
+        child: const MyAssetImage(Assets.communityWhatsAPP, width: 57,height: 57,),
+      ),
+    );
+  }
+
+  Widget _buildContentBox(BuildContext context, WidgetRef ref) {
+    final vm = ref.read(garagesaleDetailVmProvider.notifier);
+    final state = ref.watch(garagesaleDetailVmProvider);
+    String title = state.datas!['title'] ?? '';
+    String price = state.datas!['price'] ?? '';
+    String contactType = state.datas!['contactType'] ?? '';
+    String description = state.datas!['description'] ?? '';
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        // 图片
+        SizedBox(
+          width: double.infinity,
+          height: 173.5,
+          child: MyLoadImage(
+            state.datas!['goods_img'],
+          ),
+        ),
+        Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            // 标题
+            Padding(
+              padding: const EdgeInsets.only(left: 16, right: 16, top: 16,),
+              child: title.isNotEmpty? MyTextView( title, fontSize: 18, isFontMedium: true, textColor: context.appColors.textBlack,):const SizedBox.shrink(),
+            ),
+            // 价格
+            Padding(
+              padding: const EdgeInsets.only(left: 16, right: 16, top: 10),
+              child: price.isNotEmpty? MyTextView( price, fontSize: 24, isFontMedium: true, textColor: context.appColors.textBlack,):const SizedBox.shrink(),
+            ),
+           //  内容
+            Padding(
+              padding: const EdgeInsets.only(left: 16, right: 16, top: 16),
+              child: description.isNotEmpty? MyTextView( description, fontSize: 17, isFontRegular: true, textColor: context.appColors.textBlack,):const SizedBox.shrink(),
+            ),
+          ],
+        ),
+
+      ]
+    );
+  }
+
+  Widget _buildBottomConcatInfo(BuildContext context, WidgetRef ref, likeButtonKey) {
+    final state = ref.watch(garagesaleDetailVmProvider);
+    String publisher = state.datas!['publisher'] ?? '';
+    String contactType = state.datas!['contactType'] ?? '';
+    String contactInfo = state.datas!['contactInfo'] ?? '';
+    String publisherAvatar = state.datas!['publisher_avatar'] ?? '';
+    String publisherTime = state.datas!['publisher_time'] ?? '';
+    int collectionNum = state.datas!['collection_num'] ?? 0;
+    return Container(
+      height: 50,
+      color: ColorUtils.string2Color('#4161D0'),
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.center,
+        children: [
+          Expanded(
+            child: Row(
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+                SizedBox.fromSize(size: const Size(15, 0)),
+                MyLoadImage(
+                  publisherAvatar,
+                  width: 29,
+                  height: 29,
+                  isCircle: true,
+                ),
+                SizedBox.fromSize(size: const Size(10, 0)),
+                Column(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  mainAxisAlignment: MainAxisAlignment.center,
+                  children: [
+                    MyTextView(publisher, fontSize:12, textColor: context.appColors.textWhite, isFontMedium: true,),
+                    MyTextView(publisherTime, fontSize:10, textColor: context.appColors.textWhite, isFontRegular: true, marginTop: 4,)
+                  ],
+                ),
+              ],
+            ),
+          ),
+          Expanded(
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.end,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+                Container(
+                  alignment: Alignment.center,
+                  padding: const EdgeInsets.only(left:5, right: 0,top:5,bottom: 5),
+                  child: Row(
+                    mainAxisAlignment: MainAxisAlignment.center,
+                    children: [
+                      MyLikeButton(
+                        key: likeButtonKey,
+                        isLiked: false,
+                        isCustomIcon: true,
+                        customIconUnActiveAssets: Assets.communityCollection,
+                        customIconActiveAssets: Assets.communityLikeActive,
+                        // customIconWidth: 18,
+                        // customIconHeight: 18,
+                        onLike: () {
+                          Log.d('点击了like button');
+                        },
+                      ),
+                      MyTextView(
+                        "$collectionNum",
+                        fontSize: 18,
+                        textColor: Colors.white,
+                        isFontRegular: true,
+                        marginLeft: 5,
+                        marginRight: 15,
+                        onClick: (){
+                          final state = likeButtonKey.currentState;
+                          state?.triggerTap();
+                        },
+                      ),
+                    ],
+                  ),
+                ),
+                // const MyAssetImage(Assets.communityCollection, width: 18,height: 18,),
+                Visibility(
+                  visible: contactType == 'Mobile Phone',
+                  child: const Row(
+                    children: [
+                      SizedBox(width: 4,),
+                      MyAssetImage(Assets.communityPhone, width: 21.5,height: 18,),
+                      SizedBox(width: 15,),
+                    ],
+                  ),
+                ),
+              ],
+            ),
+          ),
+        ],
       ),
     );
   }

+ 36 - 0
packages/cpt_community/lib/modules/garage/garagesale_detail/garagesale_detail_state.dart

@@ -0,0 +1,36 @@
+import 'package:widgets/load_state_layout.dart';
+
+class GarageDetailState {
+  //页面 LoadView 状态的展示
+  LoadState loadingState;
+  String? errorMessage;
+
+  final int? id;
+  final String? type;  // forsale  forrent
+
+  Map<String, dynamic> datas;
+
+  GarageDetailState({
+    this.loadingState = LoadState.State_Loading,
+    this.errorMessage,
+    this.id,
+    this.type,
+    required this.datas,
+  });
+
+  GarageDetailState copyWith({
+    Map<String, dynamic>? datas,
+    LoadState? loadingState,
+    String? errorMessage,
+    int? id,
+    String? type,
+  }) {
+    return GarageDetailState(
+      loadingState: loadingState ?? this.loadingState,
+      errorMessage: errorMessage ?? this.errorMessage,
+      id: id ?? this.id,
+      type: type ?? this.type,
+      datas: datas?? this.datas,
+    );
+  }
+}

+ 109 - 0
packages/cpt_community/lib/modules/garage/garagesale_detail/garagesale_detail_vm.dart

@@ -0,0 +1,109 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:flutter/cupertino.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:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../../router/page/community_page_router.dart';
+// import 'garagesale_detail_respository.dart';
+import 'garagesale_detail_state.dart';
+
+part 'garagesale_detail_vm.g.dart';
+
+@riverpod
+class GaragesaleDetailVm extends _$GaragesaleDetailVm {
+  // late GaragesaleDetailRepository GaragesaleDetailRepositoryInstance;
+  bool _needShowPlaceholder = true; //是否展示LoadingView
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,  //允许刷新
+    controlFinishLoad: true,   //允许加载
+  );
+
+  GarageDetailState initState() {
+    return GarageDetailState(
+        datas: {}
+    );
+  }
+
+  @override
+  GarageDetailState build(){
+    // 引入数据仓库
+    // GaragesaleDetailRepositoryInstance = ref.read(newsRepositoryProvider);
+    final state = initState();
+    Log.d("--------------------------build---------------------");
+
+    return state;
+  }
+
+
+  //刷新页面状态
+  void changeLoadingState(LoadState loadState, String? errorMsg) {
+    state = state.copyWith(
+        loadingState: loadState,
+        errorMessage: errorMsg
+    );
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _needShowPlaceholder = true;
+    getDetailData();
+  }
+
+  // 初始化页面数据
+  initPageData() {
+    Log.d("----property_news_vm-----initPageData   ${state.loadingState}");
+    getDetailData();
+  }
+
+  Future getDetailData<T>() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading, null);
+    }
+
+    //   try {
+    //     //请求网络
+    //     Map<String, dynamic>  params = {
+    //       "curPage": state.curPage,
+    //       "pageSize": state.pageSize,
+    //     };
+    //     Log.d("请求参数------$params");
+
+    await Future.delayed(const Duration(milliseconds: 1500));
+
+    final Map<String, dynamic> detailData = {
+      'id':1,
+      'goods_img': 'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+      'title': 'Electronic keyboard',
+      'price': '\$66',
+      'description':'Electronic keyboards for sale. I will attend together with the booth.\$10 per day usage Negotiable usage',
+      'isCollection': true,
+      'contactType': 'WhatsApp',
+      'contactInfo': '+1 123456789',
+      'collection_num': 12,
+      'publisher': 'William Jefferson',
+      'publisher_avatar': 'https://img1.baidu.com/it/u=3890726495,1572750319&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500',
+      'publisher_time': 'June 17,2024 at 7:23 PM'
+    };
+
+    state = state.copyWith(datas: detailData);
+
+    changeLoadingState(LoadState.State_Success, null);
+
+    refreshController.finishRefresh();
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+  }
+
+
+  // 去whatsapp
+  gotoWhatsApp(){
+    // 跳转 WhatsApp
+  }
+}

+ 27 - 0
packages/cpt_community/lib/modules/garage/garagesale_detail/garagesale_detail_vm.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'garagesale_detail_vm.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$garagesaleDetailVmHash() =>
+    r'7ee0bd467ff952716fbe9a949c6bf072969c3932';
+
+/// See also [GaragesaleDetailVm].
+@ProviderFor(GaragesaleDetailVm)
+final garagesaleDetailVmProvider =
+    AutoDisposeNotifierProvider<GaragesaleDetailVm, GarageDetailState>.internal(
+  GaragesaleDetailVm.new,
+  name: r'garagesaleDetailVmProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$garagesaleDetailVmHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$GaragesaleDetailVm = AutoDisposeNotifier<GarageDetailState>;
+// 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 - 38
packages/cpt_community/lib/modules/garage/garagesale_myposts/garagesale_myposts_page.dart

@@ -1,38 +0,0 @@
-import 'package:cpt_community/router/page/community_page_router.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';
-
-@RoutePage()
-class GaragesaleMypostsPage extends HookConsumerWidget {
-  const GaragesaleMypostsPage({Key? key}) : super(key: key);
-
-  //启动当前页面
-  static void startInstance({BuildContext? context}) {
-    if (context != null) {
-      context.router.push(const GaragesaleMypostsPageRoute());
-    } else {
-      appRouter.push(const GaragesaleMypostsPageRoute());
-    }
-  }
-
-  @override
-  Widget build(BuildContext context, WidgetRef ref) {
-    // final viewModel = ref.watch(newsfeedPostVmProvider.notifier);
-
-    return Scaffold(
-      appBar: MyAppBar.appBar(
-        context,
-        "My Posts",
-        backgroundColor: context.appColors.whiteBG,
-      ),
-      backgroundColor: context.appColors.backgroundDefault,
-      body: Center(
-        child: Text("GaragesaleMyposts"),
-      ),
-    );
-  }
-}

+ 104 - 28
packages/cpt_community/lib/modules/garage/garagesale_post/garagesale_post_page.dart

@@ -36,13 +36,12 @@ class GaragesalePostPage extends HookConsumerWidget {
   Widget build(BuildContext context, WidgetRef ref) {
     final vm = ref.watch(garagesalePostVmProvider.notifier);
     final state = ref.watch(garagesalePostVmProvider);
-    // print("9999999999999999999 ${state.titleErrorText}");
 
     return Scaffold(
       appBar: MyAppBar.appBar(
         context,
         "Post New Item",
-        backgroundColor: context.appColors.whiteBG,
+        backgroundColor: context.appColors.backgroundWhite,
       ),
       backgroundColor: Colors.white,
       body: Column(
@@ -66,31 +65,97 @@ class GaragesalePostPage extends HookConsumerWidget {
                           ),
                           // title
                           Container(
-                            margin: const EdgeInsets.only(left:15, right:15, top: 15),
-                            child: Column(
-                              crossAxisAlignment: CrossAxisAlignment.start,
-                              children: [
-                                FormRequireText(
-                                  text: "Title",
-                                  textColor: context.appColors.textBlack,
-                                  fontSize: 17,
-                                ).marginOnly(bottom: 14.5),
-                                _buildInputLayout(
-                                  context,
-                                  ref,
-                                  state,
-                                  'title',
-                                  marginTop: 16,
-                                  textInputType: TextInputType.text,
-                                  textInputAction: TextInputAction.next,
-                                  errorText: state.titleErrorText,
-                                  onSubmit: (formKey, value) {
-                                    state!.formData[formKey]!['focusNode'].unfocus();
-                                    FocusScope.of(context).requestFocus(state.formData['description']!['focusNode']);
-                                  },
-                                ),
-                              ]
-                            )
+                              margin: const EdgeInsets.only(left:15, right:15, top: 15),
+                              child: Column(
+                                  crossAxisAlignment: CrossAxisAlignment.start,
+                                  children: [
+                                    FormRequireText(
+                                      text: "Title",
+                                      textColor: context.appColors.textBlack,
+                                      fontSize: 17,
+                                    ).marginOnly(bottom: 14.5),
+                                    _buildInputLayout(
+                                      context,
+                                      ref,
+                                      state,
+                                      'title',
+                                      textInputType: TextInputType.text,
+                                      textInputAction: TextInputAction.next,
+                                      errorText: state.titleErrorText,
+                                      onSubmit: (formKey, value) {
+                                        state!.formData[formKey]!['focusNode'].unfocus();
+                                        FocusScope.of(context).requestFocus(state.formData['contactInfo']!['focusNode']);
+                                      },
+                                    ),
+                                  ]
+                              )
+                          ),
+                          // contact information
+                          Container(
+                              margin: const EdgeInsets.only(left:15, right:15, top: 15),
+                              child: Column(
+                                  crossAxisAlignment: CrossAxisAlignment.start,
+                                  children: [
+                                    FormRequireText(
+                                      text: "Contact Information",
+                                      textColor: context.appColors.textBlack,
+                                      fontSize: 17,
+                                    ).marginOnly(bottom: 14.5),
+                                    Row(
+                                      mainAxisSize: MainAxisSize.max,
+                                      crossAxisAlignment: CrossAxisAlignment.center,
+                                      children: [
+                                        SizedBox(
+                                          width: 205,
+                                          child: _buildContactTypeSelectCmp(context, ref),
+                                        ),
+                                        const SizedBox(width: 15),
+                                        Expanded(
+                                          child: _buildInputLayout(
+                                            context,
+                                            ref,
+                                            state,
+                                            'contactInfo',
+                                            textInputType: TextInputType.text,
+                                            textInputAction: TextInputAction.next,
+                                            errorText: state.contactInfoErrorText,
+                                            onSubmit: (formKey, value) {
+                                              state!.formData[formKey]!['focusNode'].unfocus();
+                                              FocusScope.of(context).requestFocus(state.formData['price']!['focusNode']);
+                                            },
+                                          ),
+                                        ),
+                                      ],
+                                    ),
+                                  ]
+                              )
+                          ),
+                          // price
+                          Container(
+                              margin: const EdgeInsets.only(left:15, right:15, top: 15),
+                              child: Column(
+                                  crossAxisAlignment: CrossAxisAlignment.start,
+                                  children: [
+                                    FormRequireText(
+                                      text: "Price(\$)",
+                                      textColor: context.appColors.textBlack,
+                                      fontSize: 17,
+                                    ).marginOnly(bottom: 14.5),
+                                    _buildInputLayout(
+                                      context,
+                                      ref,
+                                      state,
+                                      'price',
+                                      textInputType: TextInputType.text,
+                                      textInputAction: TextInputAction.next,
+                                      errorText: state.priceErrorText,
+                                      onSubmit: (formKey, value) {
+                                        state!.formData[formKey]!['focusNode'].unfocus();
+                                        FocusScope.of(context).requestFocus(state.formData['description']!['focusNode']);
+                                      },
+                                    ),
+                                  ]
+                              )
                           ),
                           // description
                           Container(
@@ -239,6 +304,17 @@ class GaragesalePostPage extends HookConsumerWidget {
     );
   }
 
+  /// contact Information 的select 选择
+  Widget _buildContactTypeSelectCmp(BuildContext context, WidgetRef ref, ){
+    final vm = ref.read(garagesalePostVmProvider.notifier);
+    final state = ref.watch(garagesalePostVmProvider);
+    return PickerContainer(
+      content: state.contactTypeSelectedOption ?? "",
+      hint: S.current.choose_category,
+      onClick: vm.pickContactTypeCallback,
+    );
+  }
+
   /// sale 选择
   Widget _buildSaleInputSelectCmp(BuildContext context, WidgetRef ref, ){
     final vm = ref.read(garagesalePostVmProvider.notifier);
@@ -284,8 +360,8 @@ class GaragesalePostPage extends HookConsumerWidget {
     return IgnoreKeyboardDismiss(
       child: MyTextField(
         key,
-        fillBackgroundColor: context.appColors.authFiledBG,
         state.formData[key]!['value'],
+        fillBackgroundColor: context.appColors.authFiledBG,
         hintText: state.formData[key]!['hintText'],
         hintStyle: TextStyle(
           color: context.appColors.authFiledHint,

+ 33 - 1
packages/cpt_community/lib/modules/garage/garagesale_post/garagesale_post_state.dart

@@ -4,7 +4,13 @@ class GaragesalePostPageState {
   //title 字段的错误信息展示
   String? titleErrorText;
 
-  //title 字段的错误信息展示
+  //contactInfo 字段的错误信息展示
+  String? contactInfoErrorText;
+
+  //price 字段的错误信息展示
+  String? priceErrorText;
+
+  //description 字段的错误信息展示
   String? descriptionErrorText;
 
   //表单的校验与数据
@@ -15,6 +21,13 @@ class GaragesalePostPageState {
   // 描述
   final String? description;
 
+  // contact type 类型选项
+  final List<String> contactTypeOptionsList = ["Mobile Phone", "WhatsApp", "Email"];
+  String? contactTypeSelectedOption;
+
+  // contact information
+  final String? contactInformation;
+
   //sale 类型选项
   final List<String> saleOptionsList = ["For Sale", "For Rent",];
   String? saleSelectedOption;
@@ -30,9 +43,13 @@ class GaragesalePostPageState {
     formData,
     required this.imgList,
     this.titleErrorText,
+    this.contactInfoErrorText,
+    this.priceErrorText,
     this.descriptionErrorText,
     this.title,
     this.description,
+    this.contactInformation,
+    this.contactTypeSelectedOption,
     this.saleSelectedOption,
     this.categorySelectedOption,
   }): formData = formData ?? {
@@ -57,6 +74,13 @@ class GaragesalePostPageState {
       'focusNode': FocusNode(),
       'obsecure': false,
     },
+    'price': {
+      'value': '',
+      'controller': TextEditingController(),
+      'hintText': '',
+      'focusNode': FocusNode(),
+      'obsecure': false,
+    },
     'description': {
       'value': '',
       'controller': TextEditingController(),
@@ -70,7 +94,11 @@ class GaragesalePostPageState {
     Map<String, Map<String, dynamic>>? formData,
     List<String>? imgList,
     String? titleErrorText,
+    String? contactInfoErrorText,
+    String? priceErrorText,
     String? descriptionErrorText,
+    String? contactInformation,
+    String? contactTypeSelectedOption,
     String? saleSelectedOption,
     String? categorySelectedOption,
     String? title,
@@ -81,9 +109,13 @@ class GaragesalePostPageState {
       title: title ?? this.title,
       description: description ?? this.description,
       imgList: imgList ?? this.imgList,
+      contactInformation: contactInformation ?? this.contactInformation,
+      contactTypeSelectedOption: contactTypeSelectedOption ?? this.contactTypeSelectedOption,
       saleSelectedOption: saleSelectedOption ?? this.saleSelectedOption,
       categorySelectedOption: categorySelectedOption ?? this.categorySelectedOption,
       titleErrorText: titleErrorText ,
+      contactInfoErrorText: contactInfoErrorText,
+      priceErrorText: priceErrorText ,
       descriptionErrorText: descriptionErrorText,
     );
   }

+ 88 - 2
packages/cpt_community/lib/modules/garage/garagesale_post/garagesale_post_vm.dart

@@ -22,6 +22,8 @@ class GaragesalePostVm extends _$GaragesalePostVm {
   GaragesalePostPageState initState() {
     return GaragesalePostPageState(
       titleErrorText: null,
+      contactInfoErrorText: null,
+      priceErrorText: null,
       descriptionErrorText: '',
       formData: {
         'sale': {
@@ -45,6 +47,27 @@ class GaragesalePostVm extends _$GaragesalePostVm {
           'focusNode': FocusNode(),
           'obsecure': false,
         },
+        'contactInfoType': {
+          'value': '',
+          'controller': TextEditingController(),
+          'hintText': '',
+          'focusNode': FocusNode(),
+          'obsecure': false,
+        },
+        'contactInfo': {
+          'value': '',
+          'controller': TextEditingController(),
+          'hintText': '',
+          'focusNode': FocusNode(),
+          'obsecure': false,
+        },
+        'price': {
+          'value': '',
+          'controller': TextEditingController(),
+          'hintText': '',
+          'focusNode': FocusNode(),
+          'obsecure': false,
+        },
         'description': {
           'value': '',
           'controller': TextEditingController(),
@@ -77,6 +100,8 @@ class GaragesalePostVm extends _$GaragesalePostVm {
   //catogery 选择选项
   void pickCategoryCallback() {
     _dismissKeyboard(keyStr: 'title');
+    _dismissKeyboard(keyStr: 'price');
+    _dismissKeyboard(keyStr: 'contactInfo');
     _dismissKeyboard(keyStr: 'description');
 
     OptionPickerUtil.showCupertinoOptionPicker(
@@ -91,6 +116,8 @@ class GaragesalePostVm extends _$GaragesalePostVm {
   //sale 选择选项
   void pickSaleCallback() {
     _dismissKeyboard(keyStr: 'title');
+    _dismissKeyboard(keyStr: 'price');
+    _dismissKeyboard(keyStr: 'contactInfo');
     _dismissKeyboard(keyStr: 'description');
 
     OptionPickerUtil.showCupertinoOptionPicker(
@@ -102,6 +129,22 @@ class GaragesalePostVm extends _$GaragesalePostVm {
     );
   }
 
+  //sale 选择选项
+  void pickContactTypeCallback() {
+    _dismissKeyboard(keyStr: 'title');
+    _dismissKeyboard(keyStr: 'price');
+    _dismissKeyboard(keyStr: 'contactInfo');
+    _dismissKeyboard(keyStr: 'description');
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: state.contactTypeOptionsList,
+      initialSelectIndex: 0,
+      onPickerChanged: (_, index) {
+        state = state.copyWith(contactTypeSelectedOption: state.contactTypeOptionsList[index]);
+      },
+    );
+  }
+
   // 获取聚焦的node
   FocusNode getFocusNode(Map<String, dynamic> formData, String keyStr) {
     return formData![keyStr]!['focusNode'];
@@ -154,17 +197,28 @@ class GaragesalePostVm extends _$GaragesalePostVm {
     // 获取表单的值
     String  saleValue= state.saleSelectedOption ?? "";
     String  categoryValue= state.categorySelectedOption ?? "";
+    String  contactTypeValue= state.contactTypeSelectedOption ?? "";
 
     String  titleValue= _getFormFieldValue(keyStr: 'title');
+    String  contactInfoValue= _getFormFieldValue(keyStr: 'contactInfo');
+    String  priceValue= _getFormFieldValue(keyStr: 'price');
     String  descriptionValue= _getFormFieldValue(keyStr: 'description');
 
-    Log.d('当前待提交的 sale:$saleValue category:$categoryValue title:$titleValue description:$descriptionValue  imgList:${state.imgList}');
+    Log.d('当前待提交的 sale:$saleValue category:$categoryValue contactTypeValue:$contactTypeValue title:$titleValue  contactInfoValue:$contactInfoValue  price:$priceValue  description:$descriptionValue  imgList:${state.imgList}');
 
 
     if (Utils.isEmpty(saleValue)) {
       ToastEngine.show('Sale is empty');
       return;
     }
+    if (Utils.isEmpty(categoryValue)) {
+      ToastEngine.show('Category is empty');
+      return;
+    }
+    if (Utils.isEmpty(contactTypeValue)) {
+      ToastEngine.show('ContactType is empty');
+      return;
+    }
 
     if (Utils.isEmpty(titleValue)) {
       state = state.copyWith(titleErrorText: 'Title is empty',);
@@ -174,6 +228,22 @@ class GaragesalePostVm extends _$GaragesalePostVm {
       state = state.copyWith(titleErrorText: null);
     }
 
+    if (Utils.isEmpty(contactInfoValue)) {
+      state = state.copyWith(contactInfoErrorText: 'ContactInfo is empty',);
+      return;
+    }else {
+      state.contactInfoErrorText = null;
+      state = state.copyWith(contactInfoErrorText: null);
+    }
+
+    if (Utils.isEmpty(priceValue)) {
+      state = state.copyWith(priceErrorText: 'Price is empty',);
+      return;
+    }else {
+      // state.priceErrorText = null;
+      state = state.copyWith(priceErrorText: null);
+    }
+
     if (Utils.isEmpty(descriptionValue)) {
       state = state.copyWith(descriptionErrorText: 'Description is empty');
       return;
@@ -197,11 +267,25 @@ class GaragesalePostVm extends _$GaragesalePostVm {
 
     // 获取表单的焦点节点
     final FocusNode focusNode = getFocusNode(state.formData, 'title');
+    final FocusNode focusNodeContactInfo = getFocusNode(state.formData, 'contactInfo');
+    final FocusNode focusNodePrice = getFocusNode(state.formData, 'price');
 
     focusNode.addListener(() {
       // 获取焦点的时候清空错误文本
       if (focusNode.hasFocus) {
-        state = state.copyWith(titleErrorText: null, descriptionErrorText: null);
+        state = state.copyWith(contactInfoErrorText: null, titleErrorText: null, priceErrorText:null, descriptionErrorText: null);
+      }
+    });
+    focusNodeContactInfo.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (focusNodeContactInfo.hasFocus) {
+        state = state.copyWith(contactInfoErrorText: null, titleErrorText: null, priceErrorText:null, descriptionErrorText: null);
+      }
+    });
+    focusNodePrice.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if(focusNodePrice.hasFocus){
+        state = state.copyWith(contactInfoErrorText: null, titleErrorText: null, priceErrorText:null, descriptionErrorText: null);
       }
     });
   }
@@ -210,7 +294,9 @@ class GaragesalePostVm extends _$GaragesalePostVm {
   void onDispose(GaragesalePostPageState initState) {
     // 获取表单的焦点节点
     final FocusNode focusNode = getFocusNode(state.formData, 'title');
+    final FocusNode focusNodePrice = getFocusNode(state.formData, 'price');
     focusNode.dispose();
+    focusNodePrice.dispose();
 
     Log.d("GaragesalePostPageState 销毁 onDispose");
   }

+ 1 - 1
packages/cpt_community/lib/modules/garage/garagesale_post/garagesale_post_vm.g.dart

@@ -6,7 +6,7 @@ part of 'garagesale_post_vm.dart';
 // RiverpodGenerator
 // **************************************************************************
 
-String _$garagesalePostVmHash() => r'263c880d5ef88c9a3808b91d6448cbee51b8ff8e';
+String _$garagesalePostVmHash() => r'cabc484eddab1b8e5322750ea9eee463dd6d2290';
 
 /// See also [GaragesalePostVm].
 @ProviderFor(GaragesalePostVm)

+ 92 - 0
packages/cpt_community/lib/modules/my_following/components/item_following.dart

@@ -0,0 +1,92 @@
+import 'package:cpt_community/components/comments_dialog.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+enum MyFollowingType {
+  isFollowType,
+  isFollowerType,
+}
+
+class MyFollowingListItem extends StatelessWidget {
+  final MyFollowingType? itemType;
+  bool? hasBottomDriver = true;
+  Map<String, dynamic> itemObj;
+  void Function(dynamic)? onClickAction;
+  MyFollowingListItem({
+    super.key,
+    required this.itemObj,
+    this.itemType = MyFollowingType.isFollowType,
+    this.hasBottomDriver=true,
+    this.onClickAction
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      children: [
+        Container(
+          margin: const EdgeInsets.only(left: 15,right: 15,top: 15,bottom: 15),
+          child: Row(
+            mainAxisSize: MainAxisSize.max,
+            children: [
+              Expanded(
+                child: Row(
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyLoadImage(
+                      'https://img1.baidu.com/it/u=2743394743,692629981&fm=253&fmt=auto&app=120&f=JPEG?w=800&h=800',
+                      width: 65,
+                      height: 65,
+                      isCircle: true,
+                    ),
+                    MyTextView(
+                      'Chin Lan',
+                      fontSize: 17,
+                      isFontMedium: true,
+                      marginLeft: 17,
+                    )
+                  ],
+                ).onTap((){
+                  // onClickAction?.call(itemObj);
+                }),
+              ),
+              // _buildIsFollowButton(context),
+              HookConsumer(
+                builder: (context, ref, child) {
+                  final isFollow = useState(itemObj['isFollow']);
+                  return (!isFollow.value)? MyButton(
+                    text:'+Follow',
+                    onPressed: (){
+                      // todo 调用接口 然后更新状态
+                      isFollow.value = !isFollow.value;
+                    },
+                    textColor: context.appColors.textWhite,
+                    backgroundColor: context.appColors.textPrimary,
+                    fontSize: 14,
+                    fontWeight: FontWeight.w400,
+                    minWidth: 67.5,
+                    minHeight: 30.5,
+                  ): const SizedBox.shrink();
+                },
+              )
+
+            ],
+          ),
+        ),
+        if(hasBottomDriver!)
+          Container(
+            height: 1,
+            color: context.appColors.dividerDefault,
+          )
+      ],
+    );
+  }
+}

+ 64 - 0
packages/cpt_community/lib/modules/my_following/components/search_cmp.dart

@@ -0,0 +1,64 @@
+import 'package:flutter/material.dart';
+import 'package:widgets/search_app_bar.dart';
+
+class SearchCmp extends StatelessWidget {
+  String? value;
+  String? hintText;
+  Color backgroundColor = Colors.transparent;
+  ValueChanged<String>? onSearch;
+  ValueChanged<String>? onChanged;
+  TextEditingController? controller;
+  List<Widget>? actions;
+  void Function()? backCallback;
+
+  SearchCmp({
+    Key? key,
+    this.backCallback,
+    this.value,
+    this.hintText,
+    this.backgroundColor = Colors.transparent,
+    this.onSearch,
+    this.onChanged,
+    this.controller,
+    this.actions,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    // 检查当前主题是亮色还是暗色
+    final bool isDarkMode = Theme.of(context).brightness == Brightness.dark;
+
+
+    return Column(
+      children: [
+        Container(
+          height: kToolbarHeight,
+          color: backgroundColor,
+          child: Row(
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              // 搜索栏
+              Expanded(
+                child: SearchAppBar(
+                  onSearch: onSearch,
+                  onChanged: onChanged,
+                  value: value,
+                  hintText: hintText,
+                  controller: controller,
+                ),
+              ),
+              // 右侧操作按钮
+              Container(
+                alignment: Alignment.centerRight,
+                child: Row(
+                  mainAxisSize: MainAxisSize.min,
+                  children: actions ?? [],
+                ),
+              ),
+            ],
+          ),
+        ),
+      ],
+    );
+  }
+}

+ 167 - 0
packages/cpt_community/lib/modules/my_following/my_follow/my_follow_page.dart

@@ -0,0 +1,167 @@
+import 'package:cpt_community/modules/my_following/my_following_vm.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:flutter/rendering.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/color_utils.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:widgets/search_app_bar.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../../router/page/community_page_router.dart';
+import '../components/item_following.dart';
+import 'my_follow_vm.dart';
+
+@RoutePage()
+class MyFollowPage extends HookConsumerWidget {
+  const MyFollowPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(const MyFollowPageRoute());
+    } else {
+      appRouter.push(const MyFollowPageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final vm = ref.read(myFollowVmProvider.notifier);
+    final state = ref.watch(myFollowVmProvider);
+    final useSearchBar = useState(false);
+    // 用于存储前一次的滚动位置
+    final previousOffset = useState(0.0);
+
+    useEffect(() {
+      // 组件挂载时执行 - 执行接口请求
+      Future.microtask(() => vm.initPageData());
+
+      return () {
+        // 组件卸载时执行
+        // vm.refreshController.removeListener(() {});
+      };
+    }, []);
+
+    useEffect((){
+      Log.d("---useSearchBar.value 变化为:---${useSearchBar.value}");
+      ref.read(myFollowingVmProvider.notifier)!.handlerChangeFollowPageNavbar(useSearchBar.value);
+    }, [useSearchBar.value]);
+
+    return  Scaffold(
+        // appBar: MyAppBar.appBar(
+        //   context,
+        //   '${useSearchBar.value}',
+        //   backgroundColor: context.appColors.whiteBG,
+        // ),
+        backgroundColor: ColorUtils.string2Color("#F2F3F6"),
+        body: Container(
+          height: double.infinity,
+          width: double.infinity,
+          color: context.appColors.whiteBG,
+          child: NotificationListener<ScrollNotification>(
+            onNotification: (ScrollNotification notification) {
+              if (notification is ScrollUpdateNotification) {
+                final ScrollMetrics metrics = notification.metrics;
+                final double currentOffset = metrics.pixels;
+                final double prevOffset = previousOffset.value;
+                Log.d("---最大滚动距离-----${metrics.maxScrollExtent}-------");
+                if (currentOffset > prevOffset) {
+                  // 向上滚动
+                  // 日志输出当前滚动偏移量和前一次滚动偏移量
+                  Log.d('--向上滚动---当前currentOffset: $currentOffset, previousOffset: $prevOffset---------');
+                  if(useSearchBar.value == false && metrics.maxScrollExtent >=0 && currentOffset >= 110){
+                    // 滚动到110 时 将 titlebar 换成 searchbar
+                    useSearchBar.value = true;
+                  }
+                } else if (currentOffset < prevOffset) {
+                  // 向下滚动
+                  // 日志输出当前滚动偏移量和前一次滚动偏移量
+                  Log.d('--向下滚动---当前currentOffset: $currentOffset, previousOffset: $prevOffset---------');
+                  if(useSearchBar.value == true && metrics.maxScrollExtent >=0 && currentOffset <= 110){
+                    // 滚动到110 时 将 searchbar 换成 titlebar
+                    useSearchBar.value = false;
+                  }
+                }
+                // 更新前一次的滚动位置
+                previousOffset.value = currentOffset;
+              }
+
+              // 返回 true 表示通知被消耗
+              return false;
+            },
+            child: EasyRefresh(
+              controller: vm.refreshController,
+              // 上拉加载
+              onLoad: () async{
+                Log.d("----onLoad");
+                vm.loadMore();
+              },
+              // 下拉刷新
+              onRefresh: () async{
+                Log.d("----onRefresh");
+                vm.onRefresh();
+              },
+              child: LoadStateLayout(
+                state: state.loadingState,
+                errorMessage: state.errorMessage,
+                errorRetry: () {
+                  vm.retryRequest();
+                },
+                successSliverWidget:[
+                  SliverList(
+                      delegate: SliverChildBuilderDelegate(
+                              (context, index){
+                            return Padding(
+                              padding: const EdgeInsets.only(top: 10, bottom: 10, left: 10),
+                              child: SearchAppBar(
+                                hintText: 'Search',
+                                onChanged: (String value){
+                                  vm.handlerSearchChange(value);
+                                },
+                                onSearch: (String value){
+                                  vm.handlerSubmitSearch(value);
+                                },
+                              ),
+                            );
+                          },
+                          childCount: 1
+                      )
+                  ),
+                  SliverList(
+                      delegate: SliverChildBuilderDelegate(
+                              (context, index){
+                            return _buildFollowItem(context, ref, state.list![index], vm, index, state.list.length);
+                          },
+                          childCount: state.list!.length
+                      )
+                  )
+                ],
+              ),
+            ).marginOnly(left: 15,right: 15,top: 15,bottom: 15),
+          ),
+        )
+    );
+  }
+
+  Widget _buildFollowItem(BuildContext context,WidgetRef ref, item, vm, index, totalIndex) {
+    bool isLast = index == totalIndex - 1;
+    return MyFollowingListItem(
+        itemObj: item,
+        itemType: MyFollowingType.isFollowType,
+        hasBottomDriver: isLast? false : true,
+        onClickAction:(itemObj){
+          print("点击了  ${itemObj['name']}");
+        }
+    );
+  }
+}

+ 39 - 0
packages/cpt_community/lib/modules/my_following/my_follow/my_follow_state.dart

@@ -0,0 +1,39 @@
+import 'package:widgets/load_state_layout.dart';
+
+class MyFollowState {
+  //页面 LoadView 状态的展示
+  LoadState loadingState;
+  String? errorMessage;
+
+  int? curPage;
+  int? pageSize = 10;
+  int? filterCount = 0;
+  List<Map<String, dynamic>> list;
+
+  MyFollowState({
+    this.loadingState = LoadState.State_Loading,
+    String? errorMessage,
+    this.curPage = 1,
+    this.pageSize = 10,
+    this.filterCount = 0,
+    required this.list,
+  });
+
+  MyFollowState copyWith({
+    LoadState? loadingState,
+    String? errorMessage,
+    int? curPage,
+    int? pageSize,
+    int? filterCount,
+    List<Map<String, dynamic>>? list,
+  }) {
+    return MyFollowState(
+      loadingState: loadingState ?? this.loadingState,
+      errorMessage: errorMessage ?? this.errorMessage,
+      curPage: curPage ?? this.curPage,
+      pageSize: pageSize ?? this.pageSize,
+      filterCount: filterCount ?? this.filterCount,
+      list: list ?? this.list,
+    );
+  }
+}

+ 163 - 0
packages/cpt_community/lib/modules/my_following/my_follow/my_follow_vm.dart

@@ -0,0 +1,163 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:flutter/cupertino.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:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../my_following_vm.dart';
+import 'my_follow_state.dart';
+
+part 'my_follow_vm.g.dart';
+
+@riverpod
+class MyFollowVm extends _$MyFollowVm {
+
+  bool _needShowPlaceholder = true; //是否展示LoadingView
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,  //允许刷新
+    controlFinishLoad: true,   //允许加载
+  );
+
+  MyFollowState initState() {
+    return MyFollowState(
+        list: []
+    );
+  }
+
+  @override
+  MyFollowState build(){
+    final state = initState();
+    Log.d("--------------------------build---------------------");
+
+    return state;
+  }
+
+  //刷新页面状态
+  void changeLoadingState(LoadState loadState, String? errorMsg) {
+    state = state.copyWith(
+        loadingState: loadState,
+        errorMessage: errorMsg
+    );
+  }
+
+  // 初始化页面数据
+  initPageData() {
+    Log.d("----for_sale_vm-----initPageData   ${state.loadingState}");
+    onRefresh();
+  }
+
+  // 上拉加载 更多
+  Future loadMore() async {
+    Log.d("----for_sale_vm-----loadMore");
+    // await Future.delayed(const Duration(seconds: 2));
+    // if(state.list.length >= state.filterCount){
+    //   return;
+    // }else {
+    //   int curPage = state.curPage + 1;
+    //   state = state.copyWith(curPage: curPage,);
+    //   getListData();
+    // }
+    // 检查 curPage 是否为 null,并初始化为 1
+    int newCurPage = state.curPage ?? 1;
+    state = state.copyWith(curPage: ++newCurPage);
+    getListData();
+  }
+
+
+  // 下拉刷新
+  Future onRefresh() async {
+    Log.d("----forsale_vm-----onRefresh ");
+
+    // await Future.delayed(const Duration(seconds: 2));
+    state = state.copyWith(curPage: 1);
+    getListData();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    state = state.copyWith(curPage: 1);
+    _needShowPlaceholder = true;
+    getListData();
+  }
+
+
+  // 获取list 列表数据
+  Future getListData<T>() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading, null);
+    }
+
+    Log.d("for_sale加载listData数据---------------start--${state.curPage}---");
+    //   try {
+    //     //请求网络
+    //     Map<String, dynamic>  params = {
+    //       "curPage": state.curPage,
+    //       "pageSize": state.pageSize,
+    //     };
+    //     Log.d("请求参数------$params");
+    //     final result = await propertyNewsRepository.fetchPropertyNewsList(params);
+    //     Log.d("请求完成结果------${result.data}");
+    //     //校验成功失败
+    //     if (result.isSuccess) {
+    //       // state = state.copyWith(serverTime: result.data);
+    //       state = state;
+    //   handleList(listResult.data?.rows);
+    //       ToastEngine.show("获取数据成功");
+    //     } else {
+    //   errorMessage = listResult.errorMsg;
+    //   changeLoadingState(LoadState.State_Error);
+    //       ToastEngine.show(result.errorMsg ?? "Network Load Error");
+    //     }
+    //   } catch (e) {
+    //     ToastEngine.show("Error: $e");
+    //   }
+
+    await Future.delayed(const Duration(milliseconds: 1500));
+    final List<Map<String, dynamic>> listData = [
+      {
+        'id':1,
+        'avator': 'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'title': 'William Jefferson',
+        'isFollow': false,
+      },
+      {
+        'id':2,
+        'avator': 'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'title': 'William Jefferson',
+        'isFollow': true,
+      },
+    ];
+
+    if (state.curPage == 1) {
+      //刷新的方式
+      state = state.copyWith(list: listData);
+      refreshController.finishRefresh();
+      // //更新展示的状态
+      changeLoadingState(LoadState.State_Success, null);
+    } else {
+      //加载更多
+      final allList = state.list;
+      allList.addAll(listData);
+      state = state.copyWith(list: allList);
+      refreshController.finishLoad();
+
+    }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+
+    WidgetsBinding.instance.addPostFrameCallback((_) {
+      // 需要更新 my_posts_tabs 里面的数字
+      ref.read(myFollowingVmProvider.notifier)
+          .updateMyFollowingTabsNum( 'followCount', state.list!.length, 0);
+    });
+  }
+
+  handlerSearchChange(String value){}
+
+  handlerSubmitSearch(String value){}
+}

+ 25 - 0
packages/cpt_community/lib/modules/my_following/my_follow/my_follow_vm.g.dart

@@ -0,0 +1,25 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'my_follow_vm.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$myFollowVmHash() => r'8c6f9fbf7a522cfd47ee34c5de7cf24916587c36';
+
+/// See also [MyFollowVm].
+@ProviderFor(MyFollowVm)
+final myFollowVmProvider =
+    AutoDisposeNotifierProvider<MyFollowVm, MyFollowState>.internal(
+  MyFollowVm.new,
+  name: r'myFollowVmProvider',
+  debugGetCreateSourceHash:
+      const bool.fromEnvironment('dart.vm.product') ? null : _$myFollowVmHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$MyFollowVm = AutoDisposeNotifier<MyFollowState>;
+// 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

+ 167 - 0
packages/cpt_community/lib/modules/my_following/my_follower/my_follower_page.dart

@@ -0,0 +1,167 @@
+import 'package:cpt_community/modules/my_following/my_following_vm.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:flutter/rendering.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/color_utils.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:widgets/search_app_bar.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../../router/page/community_page_router.dart';
+import '../components/item_following.dart';
+import 'my_follower_vm.dart';
+
+@RoutePage()
+class MyFollowerPage extends HookConsumerWidget {
+  const MyFollowerPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(const MyFollowerPageRoute());
+    } else {
+      appRouter.push(const MyFollowerPageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final vm = ref.read(myFollowerVmProvider.notifier);
+    final state = ref.watch(myFollowerVmProvider);
+
+    final useSearchBar = useState(false);
+    // 用于存储前一次的滚动位置
+    final previousOffset = useState(0.0);
+
+    useEffect(() {
+      // 组件挂载时执行 - 执行接口请求
+      Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+      };
+    }, []);
+
+    useEffect((){
+      Log.d("---useSearchBar.value 变化为:---${useSearchBar.value}");
+      ref.read(myFollowingVmProvider.notifier)!.handlerChangeFollowerPageNavbar(useSearchBar.value);
+    }, [useSearchBar.value]);
+
+
+    return  Scaffold(
+      // appBar: MyAppBar.appBar(
+      //   context,
+      //   "following",
+      //   backgroundColor: context.appColors.whiteBG,
+      // ),
+        backgroundColor: ColorUtils.string2Color("#F2F3F6"),
+        body: Container(
+          height: double.infinity,
+          width: double.infinity,
+          color: context.appColors.whiteBG,
+          child: NotificationListener<ScrollNotification>(
+            onNotification: (ScrollNotification notification) {
+              if (notification is ScrollUpdateNotification) {
+                final ScrollMetrics metrics = notification.metrics;
+                final double currentOffset = metrics.pixels;
+                final double prevOffset = previousOffset.value;
+                Log.d("---最大滚动距离-----${metrics.maxScrollExtent}-------");
+                if (currentOffset > prevOffset) {
+                  // 向上滚动
+                  // 日志输出当前滚动偏移量和前一次滚动偏移量
+                  Log.d('--向上滚动---当前currentOffset: $currentOffset, previousOffset: $prevOffset---------');
+                  if(useSearchBar.value == false && metrics.maxScrollExtent >=0 && currentOffset >= 110){
+                    // 滚动到110 时 将 titlebar 换成 searchbar
+                    useSearchBar.value = true;
+                  }
+                } else if (currentOffset < prevOffset) {
+                  // 向下滚动
+                  // 日志输出当前滚动偏移量和前一次滚动偏移量
+                  Log.d('--向下滚动---当前currentOffset: $currentOffset, previousOffset: $prevOffset---------');
+                  if(useSearchBar.value == true && metrics.maxScrollExtent >=0 && currentOffset <= 110){
+                    // 滚动到110 时 将 searchbar 换成 titlebar
+                    useSearchBar.value = false;
+                  }
+                }
+                // 更新前一次的滚动位置
+                previousOffset.value = currentOffset;
+              }
+
+              // 返回 true 表示通知被消耗
+              return false;
+            },
+            child: EasyRefresh(
+              controller: vm.refreshController,
+              // 上拉加载
+              onLoad: () async{
+                Log.d("----onLoad");
+                vm.loadMore();
+              },
+              // 下拉刷新
+              onRefresh: () async{
+                Log.d("----onRefresh");
+                vm.onRefresh();
+              },
+              child: LoadStateLayout(
+                state: state.loadingState,
+                errorMessage: state.errorMessage,
+                errorRetry: () {
+                  vm.retryRequest();
+                },
+                successSliverWidget:[
+                  SliverList(
+                      delegate: SliverChildBuilderDelegate(
+                              (context, index){
+                            return Padding(
+                              padding: const EdgeInsets.only(top: 10, bottom: 10, left: 10),
+                              child: SearchAppBar(
+                                hintText: 'Search',
+                                onChanged: (String value){
+                                  vm.handlerSearchChange(value);
+                                },
+                                onSearch: (String value){
+                                  vm.handlerSubmitSearch(value);
+                                },
+                              ),
+                            );
+                          },
+                          childCount: 1
+                      )
+                  ),
+                  SliverList(
+                      delegate: SliverChildBuilderDelegate(
+                              (context, index){
+                            return _buildFollowItem(context, ref, state.list![index], vm, index, state.list.length);
+                          },
+                          childCount: state.list!.length
+                      )
+                  )
+                ],
+              ),
+            ).marginOnly(left: 15,right: 15,top: 15,bottom: 15),
+          ),
+        )
+    );
+  }
+
+  Widget _buildFollowItem(BuildContext context,WidgetRef ref, item, vm, index, totalIndex) {
+    bool isLast = index == totalIndex - 1;
+    return MyFollowingListItem(
+        itemObj: item,
+        itemType: MyFollowingType.isFollowerType,
+        hasBottomDriver: isLast? false : true,
+        onClickAction:(itemObj){
+          print("点击了  ${itemObj['name']}");
+        }
+    );
+  }
+}

+ 39 - 0
packages/cpt_community/lib/modules/my_following/my_follower/my_follower_state.dart

@@ -0,0 +1,39 @@
+import 'package:widgets/load_state_layout.dart';
+
+class MyFollowerState {
+  //页面 LoadView 状态的展示
+  LoadState loadingState;
+  String? errorMessage;
+
+  int? curPage;
+  int? pageSize = 10;
+  int? filterCount = 0;
+  List<Map<String, dynamic>> list;
+
+  MyFollowerState({
+    this.loadingState = LoadState.State_Loading,
+    String? errorMessage,
+    this.curPage = 1,
+    this.pageSize = 10,
+    this.filterCount = 0,
+    required this.list,
+  });
+
+  MyFollowerState copyWith({
+    LoadState? loadingState,
+    String? errorMessage,
+    int? curPage,
+    int? pageSize,
+    int? filterCount,
+    List<Map<String, dynamic>>? list,
+  }) {
+    return MyFollowerState(
+      loadingState: loadingState ?? this.loadingState,
+      errorMessage: errorMessage ?? this.errorMessage,
+      curPage: curPage ?? this.curPage,
+      pageSize: pageSize ?? this.pageSize,
+      filterCount: filterCount ?? this.filterCount,
+      list: list ?? this.list,
+    );
+  }
+}

+ 169 - 0
packages/cpt_community/lib/modules/my_following/my_follower/my_follower_vm.dart

@@ -0,0 +1,169 @@
+import 'package:cpt_community/modules/my_following/my_following_vm.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:flutter/cupertino.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:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'my_follower_state.dart';
+
+part 'my_follower_vm.g.dart';
+
+@riverpod
+class MyFollowerVm extends _$MyFollowerVm {
+
+  bool _needShowPlaceholder = true; //是否展示LoadingView
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,  //允许刷新
+    controlFinishLoad: true,   //允许加载
+  );
+
+  MyFollowerState initState() {
+    return MyFollowerState(
+        list: []
+    );
+  }
+
+  @override
+  MyFollowerState build(){
+    final state = initState();
+    Log.d("--------------------------build---------------------");
+
+    return state;
+  }
+
+  //刷新页面状态
+  void changeLoadingState(LoadState loadState, String? errorMsg) {
+    state = state.copyWith(
+        loadingState: loadState,
+        errorMessage: errorMsg
+    );
+  }
+
+  // 初始化页面数据
+  initPageData() {
+    Log.d("----for_sale_vm-----initPageData   ${state.loadingState}");
+    WidgetsBinding.instance.addPostFrameCallback((_){
+      final myFollowingPageVm = ref.read(myFollowingVmProvider.notifier);
+      myFollowingPageVm.tabsRouterChange();
+    });
+    onRefresh();
+  }
+
+  // 上拉加载 更多
+  Future loadMore() async {
+    Log.d("----for_sale_vm-----loadMore");
+    // await Future.delayed(const Duration(seconds: 2));
+    // if(state.list.length >= state.filterCount){
+    //   return;
+    // }else {
+    //   int curPage = state.curPage + 1;
+    //   state = state.copyWith(curPage: curPage,);
+    //   getListData();
+    // }
+    // 检查 curPage 是否为 null,并初始化为 1
+    int newCurPage = state.curPage ?? 1;
+    state = state.copyWith(curPage: ++newCurPage);
+    getListData();
+  }
+
+
+  // 下拉刷新
+  Future onRefresh() async {
+    Log.d("----forsale_vm-----onRefresh ");
+
+    // await Future.delayed(const Duration(seconds: 2));
+    state = state.copyWith(curPage: 1);
+    getListData();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    state = state.copyWith(curPage: 1);
+    _needShowPlaceholder = true;
+    getListData();
+  }
+
+
+  // 获取list 列表数据
+  Future getListData<T>() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading, null);
+    }
+
+    Log.d("for_sale加载listData数据---------------start--${state.curPage}---");
+    //   try {
+    //     //请求网络
+    //     Map<String, dynamic>  params = {
+    //       "curPage": state.curPage,
+    //       "pageSize": state.pageSize,
+    //     };
+    //     Log.d("请求参数------$params");
+    //     final result = await propertyNewsRepository.fetchPropertyNewsList(params);
+    //     Log.d("请求完成结果------${result.data}");
+    //     //校验成功失败
+    //     if (result.isSuccess) {
+    //       // state = state.copyWith(serverTime: result.data);
+    //       state = state;
+    //   handleList(listResult.data?.rows);
+    //       ToastEngine.show("获取数据成功");
+    //     } else {
+    //   errorMessage = listResult.errorMsg;
+    //   changeLoadingState(LoadState.State_Error);
+    //       ToastEngine.show(result.errorMsg ?? "Network Load Error");
+    //     }
+    //   } catch (e) {
+    //     ToastEngine.show("Error: $e");
+    //   }
+
+    await Future.delayed(const Duration(milliseconds: 1500));
+    final List<Map<String, dynamic>> listData = [
+      {
+        'id':1,
+        'avator': 'https://iknow-pic.cdn.bcebos.com/4afbfbedab64034f6c21389bbdc379310b551dba',
+        'title': 'Steve Jobs',
+        'isFollow': false,
+      },
+      {
+        'id':2,
+        'avator': 'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'title': 'William Jefferson',
+        'isFollow': true,
+      },
+    ];
+
+    if (state.curPage == 1) {
+      //刷新的方式
+      state = state.copyWith(list: listData);
+      refreshController.finishRefresh();
+      // //更新展示的状态
+      changeLoadingState(LoadState.State_Success, null);
+    } else {
+      //加载更多
+      final allList = state.list;
+      allList.addAll(listData);
+      state = state.copyWith(list: allList);
+      refreshController.finishLoad();
+
+    }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+
+    WidgetsBinding.instance.addPostFrameCallback((_) {
+      // 需要更新 my_posts_tabs 里面的数字
+      ref.read(myFollowingVmProvider.notifier)
+          .updateMyFollowingTabsNum( 'followerCount', state.list!.length, 1);
+    });
+
+  }
+
+  handlerSearchChange(String value){}
+
+  handlerSubmitSearch(String value){}
+
+}

+ 25 - 0
packages/cpt_community/lib/modules/my_following/my_follower/my_follower_vm.g.dart

@@ -0,0 +1,25 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'my_follower_vm.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$myFollowerVmHash() => r'3069f27c5676bb740990ead2ddf9041bec412ca0';
+
+/// See also [MyFollowerVm].
+@ProviderFor(MyFollowerVm)
+final myFollowerVmProvider =
+    AutoDisposeNotifierProvider<MyFollowerVm, MyFollowerState>.internal(
+  MyFollowerVm.new,
+  name: r'myFollowerVmProvider',
+  debugGetCreateSourceHash:
+      const bool.fromEnvironment('dart.vm.product') ? null : _$myFollowerVmHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$MyFollowerVm = AutoDisposeNotifier<MyFollowerState>;
+// 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

+ 131 - 0
packages/cpt_community/lib/modules/my_following/my_following_page.dart

@@ -0,0 +1,131 @@
+
+import 'package:cpt_community/components/newsfeed_card_content.dart';
+import 'package:cpt_community/components/newsfeed_card_footer.dart';
+import 'package:cs_resources/generated/assets.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/rendering.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/color_utils.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_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/search_app_bar.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../components/newfeed_card_header.dart';
+import '../../router/page/community_page_router.dart';
+import 'my_following_vm.dart';
+
+import 'my_following_tabs.dart';
+
+import 'my_following_vm.dart';
+
+
+final myFollowingPageTabsRouterKey = GlobalKey<AutoTabsRouterState>();
+
+@RoutePage()
+class MyFollowingPage extends HookConsumerWidget {
+  const MyFollowingPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(const MyFollowingPageRoute());
+    } else {
+      appRouter.push(const MyFollowingPageRoute());
+    }
+  }
+
+  Widget _buildTabsSection(BuildContext context, WidgetRef ref, tabsRouter, state){
+    final vm = ref.read(myFollowingVmProvider.notifier);
+    return Container(
+      width: double.infinity,
+      padding: const EdgeInsets.only(left: 15, right: 15,top: 14,bottom: 14),
+      child: MyFollowingTabs(
+          key: UniqueKey(),
+          tabsList: state.tabsList,
+          tabsRouter: tabsRouter,
+          onClickAction:(activeTabIdx){
+            vm.handlerChangeTab.call(activeTabIdx, tabsRouter);
+          }
+      ),
+    );
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final vm = ref.read(myFollowingVmProvider.notifier);
+    final state = ref.watch(myFollowingVmProvider);
+
+
+    useEffect((){
+      // 延迟监听
+      WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
+        if(myFollowingPageTabsRouterKey.currentState?.controller != null){
+          myFollowingPageTabsRouterKey.currentState?.controller?.addListener((){
+            vm.tabsRouterChange();
+          });
+        }
+      });
+
+      return (){
+        Log.d("My_followingPage dispose");
+        myFollowingPageTabsRouterKey.currentState?.controller?.removeListener(vm.tabsRouterChange);
+      };
+    },[]);
+
+
+    PreferredSizeWidget _buildPageAppBar(BuildContext context, WidgetRef ref, state){
+      if(state.currentPageViewIdx == 0){
+        return  state.followPageIsSearchBarStatus!? MyAppBar.searchAppBar(context, backgroundColor: context.appColors.backgroundWhite): MyAppBar.appBar(
+          context,
+          state.currentPageViewIdx == 0 ? "My Follow" : "My Follower",
+          backgroundColor: context.appColors.backgroundWhite,
+        );
+      }else {
+        return state.followerPageIsSearchBarStatus!? MyAppBar.searchAppBar(context, backgroundColor: context.appColors.backgroundWhite): MyAppBar.appBar(
+          context,
+          state.currentPageViewIdx == 0 ? "My Follow" : "My Follower",
+          backgroundColor: context.appColors.backgroundWhite,
+        );
+      }
+    }
+
+    return Scaffold(
+      appBar: _buildPageAppBar(context, ref, state),
+      // appBar: MyAppBar.searchAppBar(context),
+      backgroundColor: ColorUtils.string2Color("#F2F3F6"),
+      body: Column(
+        children:[
+          Expanded(
+            child: AutoTabsRouter.pageView(
+              key: myFollowingPageTabsRouterKey,
+              routes: const [
+                MyFollowPageRoute(),
+                MyFollowerPageRoute(),
+              ],
+              builder: (context, child, animation) {
+                final tabsRouter = AutoTabsRouter.of(context);
+                return Column(
+                  mainAxisSize: MainAxisSize.max,
+                  children: [
+                    _buildTabsSection(context, ref, tabsRouter, state),
+                    Expanded(
+                      child: child,
+                    )
+                  ],
+                );
+              },
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 39 - 0
packages/cpt_community/lib/modules/my_following/my_following_state.dart

@@ -0,0 +1,39 @@
+class MyFollowingState {
+  List<String>? tabsList;
+  int? currentPageViewIdx = 0;
+  int? followerCount;
+  int? followCount;
+  bool? followPageIsSearchBarStatus = false;
+  bool? followerPageIsSearchBarStatus = false;
+
+  MyFollowingState({
+    tabsList,
+    this.followerCount = 0,
+    this.followCount = 0,
+    this.currentPageViewIdx = 0,
+    this.followPageIsSearchBarStatus = false,
+    this.followerPageIsSearchBarStatus = false,
+  }) : tabsList = tabsList ?? [
+    'Follow',
+    'Follower',
+  ];
+
+  MyFollowingState copyWith({
+    List<String>? tabsList,
+    int? currentPageViewIdx,
+    int? activeTabIdx,
+    int? followerCount,
+    int? followCount,
+    bool? followPageIsSearchBarStatus,
+    bool? followerPageIsSearchBarStatus,
+  }) {
+    return MyFollowingState(
+      tabsList: tabsList ?? this.tabsList,
+      currentPageViewIdx: currentPageViewIdx ?? this.currentPageViewIdx,
+      followerCount: followerCount ?? this.followerCount,
+      followCount: followCount ?? this.followCount,
+      followPageIsSearchBarStatus: followPageIsSearchBarStatus ?? this.followPageIsSearchBarStatus,
+      followerPageIsSearchBarStatus: followerPageIsSearchBarStatus ?? this.followerPageIsSearchBarStatus,
+    );
+  }
+}

+ 127 - 110
packages/cpt_community/lib/modules/garage/garage_tabs.dart

@@ -1,110 +1,127 @@
-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/color_utils.dart';
-import 'package:shared/utils/log_utils.dart';
-import 'package:widgets/my_load_image.dart';
-import 'package:widgets/ext/ex_widget.dart';
-import 'package:widgets/my_text_view.dart';
-import 'package:widgets/my_appbar.dart';
-
-import '../community/community_vm.dart';
-import '../garage/garage_vm.dart';
-
-class GarageTabs extends HookConsumerWidget {
-  List tabsList;
-  Widget? Function(BuildContext)? tabItemBuilder;
-  dynamic? tabsRouter;
-  void Function(int)? onClickAction;
-  GarageTabs({
-    Key? key,
-    required this.tabsList,
-    this.tabsRouter,
-    this.onClickAction,
-    this.tabItemBuilder
-  }) : super(key: key);
-
-  Widget _buildTabItem(BuildContext context, WidgetRef ref, vm, item, index) {
-    // 监听 activeIndex 的变化
-    final activeTabIdx = tabsRouter!.activeIndex;
-
-    return Container(
-      width: MediaQuery.of(context).size.width / vm.state.tabsList.length - 30,
-      height: 43,
-      padding: const EdgeInsets.only(top: 10, bottom: 10, left: 10, right: 10),
-      decoration: index==activeTabIdx? BoxDecoration(
-        color: index==activeTabIdx? context.appColors.btnBgDefault: ColorUtils.string2Color("#F2F3F6"),
-        borderRadius: BorderRadius.circular(20),
-        boxShadow: [
-          BoxShadow(
-            color: Colors.grey.withOpacity(0.5),
-            spreadRadius: 1,
-            blurRadius: 5,
-            offset: const Offset(0, 2), // changes position of shadow
-          ),
-        ],
-      ): null,
-      child: Row(
-        children: [
-          Expanded(
-            child: Container(
-              alignment: Alignment.center,
-              child: MyTextView(
-                item['title'],
-                fontSize: 16,
-                textAlign: TextAlign.center,
-                isFontMedium: true,
-                textColor: index == activeTabIdx ? Colors.white :ColorUtils.string2Color("#000000"),
-              ),
-            ).onTap((){
-              print("garage_tabs 中点击的tab index:  $index");
-              onClickAction?.call(index);
-            }),
-          ),
-        ],
-      ),
-    );
-  }
-
-  List<Widget> _buildTabs(BuildContext context, WidgetRef ref, vm){
-
-
-    List tabsList = ref.read(garageVmProvider).tabsList;
-
-    int tabsLength = tabsList.length;
-
-    return List.generate(tabsLength, (index) {
-      return _buildTabItem(context, ref, vm, tabsList[index], index);
-    });
-  }
-
-  @override
-  Widget build(BuildContext context, WidgetRef ref) {
-    final vm = ref.read(garageVmProvider.notifier);
-    // 使用useEffect钩子
-    useEffect(() {
-      print('副作用函数执行');
-      // 这里是副作用逻辑
-      // 返回清理函数
-      return () {
-        print('清理函数执行');
-      };
-    }, []); // 空依赖列表意味着这个副作用只在组件挂载时执行一次
-
-    return SingleChildScrollView(
-      scrollDirection: Axis.horizontal,
-      physics: const BouncingScrollPhysics(),
-      clipBehavior: Clip.none,
-      child: Row(
-        mainAxisSize: MainAxisSize.max,
-        mainAxisAlignment: MainAxisAlignment.center,
-        children: _buildTabs(context, ref, vm),
-      ).constrained(
-          maxWidth:  MediaQuery.of(context).size.width
-      ),
-    );
-  }
-}
+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/color_utils.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/my_appbar.dart';
+
+import '../community/community_vm.dart';
+import 'my_following_vm.dart';
+
+class MyFollowingTabs extends HookConsumerWidget {
+  List tabsList;
+  Widget? Function(BuildContext)? tabItemBuilder;
+  dynamic? tabsRouter;
+  void Function(int)? onClickAction;
+  MyFollowingTabs({
+    Key? key,
+    required this.tabsList,
+    this.tabsRouter,
+    this.onClickAction,
+    this.tabItemBuilder
+  }) : super(key: key);
+
+  Widget _buildTabItem(BuildContext context, WidgetRef ref, vm,state, item, index, int activeTabCounter) {
+    final activeTabIdx = tabsRouter!.activeIndex;
+
+    // final activeTabCounter = ref.watch(myFollowingVmProvider.select((state) =>
+    //   state.tabsList![index] == 'follow'? state.followerCount: state.followerCount
+    // ));
+
+
+    return Container(
+      width: MediaQuery.of(context).size.width / vm.state.tabsList.length - 30,
+      height: 43,
+      padding: const EdgeInsets.only(top: 10, bottom: 10, left: 10, right: 10),
+      decoration: index==activeTabIdx? BoxDecoration(
+        color: index==activeTabIdx? context.appColors.btnBgDefault: ColorUtils.string2Color("#F2F3F6"),
+        borderRadius: BorderRadius.circular(20),
+        boxShadow: [
+          BoxShadow(
+            color: Colors.grey.withOpacity(0.5),
+            spreadRadius: 1,
+            blurRadius: 5,
+            offset: const Offset(0, 2), // changes position of shadow
+          ),
+        ],
+      ): null,
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.center,
+        children: [
+          Expanded(
+            child: Container(
+              alignment: Alignment.center,
+              child: Row(
+                mainAxisAlignment: MainAxisAlignment.center,
+                children: [
+                  MyTextView(
+                    item,
+                    fontSize: 16,
+                    textAlign: TextAlign.center,
+                    isFontMedium: true,
+                    textColor: index == activeTabIdx ? Colors.white :ColorUtils.string2Color("#000000"),
+                  ),
+                  MyTextView(
+                    // '(${activeTabCounter.toString()})',
+                    '(${activeTabCounter.toString()})',
+                    fontSize: 16,
+                    textAlign: TextAlign.center,
+                    isFontMedium: true,
+                    textColor: index == activeTabIdx ? Colors.white :ColorUtils.string2Color("#000000"),
+                  ),
+                ],
+              ),
+            ).onTap((){
+              onClickAction?.call(index);
+            }),
+          ),
+        ],
+      ),
+    );
+  }
+
+  List<Widget> _buildTabs(BuildContext context, WidgetRef ref, vm, state){
+    List tabsList = state.tabsList;
+    int tabsLength = tabsList.length;
+    return List.generate(tabsLength, (index) {
+      final followCount = state.followCount;
+      final followerCount = state.followerCount;
+      final activeTabCounter = (index == 0? followCount: followerCount);
+      return _buildTabItem(context, ref, vm, state, tabsList[index], index, activeTabCounter);
+    });
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final vm = ref.read(myFollowingVmProvider.notifier);
+    final state = ref.watch(myFollowingVmProvider);
+    // 使用useEffect钩子
+    // useEffect(() {
+      // print('副作用函数执行');
+      // 这里是副作用逻辑
+      // 返回清理函数
+      // return () {
+      //   print('清理函数执行');
+      // };
+    // }, []); // 空依赖列表意味着这个副作用只在组件挂载时执行一次
+
+    return SingleChildScrollView(
+      scrollDirection: Axis.horizontal,
+      physics: const BouncingScrollPhysics(),
+      clipBehavior: Clip.none,
+      child: Row(
+        mainAxisSize: MainAxisSize.max,
+        mainAxisAlignment: MainAxisAlignment.center,
+        children: _buildTabs(context, ref, vm, state),
+      ).constrained(
+          maxWidth:  MediaQuery.of(context).size.width
+      ),
+    );
+  }
+}

+ 90 - 0
packages/cpt_community/lib/modules/my_following/my_following_vm.dart

@@ -0,0 +1,90 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:flutter/cupertino.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:auto_route/auto_route.dart';
+
+import 'my_following_page.dart';
+import 'my_following_state.dart';
+
+part 'my_following_vm.g.dart';
+
+@riverpod
+class MyFollowingVm extends _$MyFollowingVm {
+
+  MyFollowingState initState() {
+    return MyFollowingState(
+      currentPageViewIdx: 0,
+    );
+  }
+
+  @override
+  MyFollowingState build(){
+    final state = initState();
+    Log.d("--------------------------build---------------------");
+    return state;
+  }
+
+  tabsRouterChange(){
+    Log.d("----tabsRouterChange---${myFollowingPageTabsRouterKey.currentState?.controller?.activeIndex}-");
+    state = state.copyWith(
+        currentPageViewIdx: myFollowingPageTabsRouterKey.currentState?.controller?.activeIndex ?? 0
+    );
+  }
+
+  // 切换tab
+  handlerChangeTab(int tabIndex, TabsRouter? tabsRouter,) {
+    tabsRouter = (tabsRouter?? myFollowingPageTabsRouterKey.currentState?.controller)!;
+    tabsRouter.setActiveIndex(tabIndex);
+  }
+
+  setCurrentTabIndex(int activeTabIdx){
+    state = state.copyWith(
+      activeTabIdx: activeTabIdx,
+    );
+  }
+  // 改变 follow 页面navigatorbar
+  handlerChangeFollowPageNavbar(bool showSearchBar){
+    WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
+      if(showSearchBar){
+        state = state.copyWith(
+          followPageIsSearchBarStatus: true,
+        );
+      } else {
+        state = state.copyWith(
+          followPageIsSearchBarStatus: false,
+        );
+      }
+    });
+  }
+  // 改变 follower 页面navigatorbar
+  handlerChangeFollowerPageNavbar(bool showSearchBar){
+    WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
+      if(showSearchBar){
+        state = state.copyWith(
+          followerPageIsSearchBarStatus: true,
+        );
+      } else {
+        state = state.copyWith(
+          followerPageIsSearchBarStatus: false,
+        );
+      }
+    });
+  }
+
+
+  updateMyFollowingTabsNum(String code, int num, int activeTabIdx){
+    if(code == 'followerCount'){
+      state = state.copyWith(
+        followerCount: num,
+        activeTabIdx: activeTabIdx,
+      );
+    } else if(code == 'followCount'){
+      state = state.copyWith(
+        followCount: num,
+        activeTabIdx: activeTabIdx,
+      );
+    }
+  }
+}

+ 26 - 0
packages/cpt_community/lib/modules/my_following/my_following_vm.g.dart

@@ -0,0 +1,26 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'my_following_vm.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$myFollowingVmHash() => r'062ebd721a307fa49519a8aedffa645d04a4ce68';
+
+/// See also [MyFollowingVm].
+@ProviderFor(MyFollowingVm)
+final myFollowingVmProvider =
+    AutoDisposeNotifierProvider<MyFollowingVm, MyFollowingState>.internal(
+  MyFollowingVm.new,
+  name: r'myFollowingVmProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$myFollowingVmHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$MyFollowingVm = AutoDisposeNotifier<MyFollowingState>;
+// 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

+ 139 - 0
packages/cpt_community/lib/modules/my_posts/my_posts_forrent/my_posts_forrent_page.dart

@@ -0,0 +1,139 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:flutter/rendering.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/color_utils.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../../components/garage_card.dart';
+import '../../../components/newfeed_card_header.dart';
+import '../../../router/page/community_page_router.dart';
+import 'my_posts_forrent_vm.dart';
+
+@RoutePage()
+class MyPostsForRentPage extends HookConsumerWidget {
+  const MyPostsForRentPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(const MyPostsForRentPageRoute());
+    } else {
+      appRouter.push(const MyPostsForRentPageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final vm = ref.read(myPostsForRentVmProvider.notifier);
+    final state = ref.watch(myPostsForRentVmProvider);
+    useEffect(() {
+      // 组件挂载时执行 - 执行接口请求
+      Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+        Log.d("garage_forrent_page 组件卸载时执行");
+      };
+    }, []);
+
+    return Scaffold(
+      // appBar: MyAppBar.appBar(
+      //   context,
+      //   "MyPostsForRent",
+      //   backgroundColor: context.appColors.whiteBG,
+      // ),
+      backgroundColor: ColorUtils.string2Color("#F2F3F6"),
+      body: SizedBox(
+          width: double.infinity,
+          height: double.infinity,
+          child: EasyRefresh(
+            controller: vm.refreshController,
+            // 上拉加载
+            onLoad: () async{
+              Log.d("----onLoad");
+              vm.loadMore();
+            },
+            // 下拉刷新
+            onRefresh: () async{
+              Log.d("----onRefresh");
+              vm.onRefresh();
+            },
+            child: LoadStateLayout(
+              state: state.loadingState,
+              errorMessage: state.errorMessage,
+              errorRetry: () {
+                vm.retryRequest();
+              },
+              successSliverWidget:[
+                SliverGrid(
+                  gridDelegate:  const SliverGridDelegateWithFixedCrossAxisCount(
+                    crossAxisCount: 2, // 每行显示两个项目
+                    mainAxisSpacing: 15,
+                    crossAxisSpacing: 15,
+                    childAspectRatio: 0.85, // 宽高比
+                  ),
+                  delegate: SliverChildBuilderDelegate(
+                        (context, index) {
+                      return  _buildMyPostsForRentItem(context, ref, state.list[index], vm).onTap((){
+                        vm.handlerGotoDetail(state.list[index]['id']);
+                      });
+                    },
+                    childCount: state.list.length,
+                  ),
+                ),
+              ],
+            ),
+          ).marginOnly(left: 15,right: 15,top: 15,bottom: 15)
+      ),
+    );
+  }
+
+  Widget _buildMyPostsForRentItem(BuildContext context, WidgetRef ref, item, vm){
+    return SizedBox(
+      width: double.infinity,
+      child: Container(
+        decoration: BoxDecoration(
+          color: context.appColors.whiteBG,
+          borderRadius: BorderRadius.circular(8),
+          boxShadow: [
+            BoxShadow(
+              color: ColorUtils.string2Color('#E5E5E5'),
+              offset: const Offset(0, 2),
+              blurRadius: 8,
+            ),
+          ],
+        ),
+        child: Column(
+            mainAxisAlignment: MainAxisAlignment.start,
+            crossAxisAlignment: CrossAxisAlignment.start,
+            mainAxisSize: MainAxisSize.max,
+            children: [
+              // 卡片头部(头像 标题 时间)
+              Expanded(
+                child: GarageCard(
+                    key: UniqueKey(),
+                    useType: GarageCardUseType.myPostsForRent,
+                    itemObj: item,
+                    onClickColleciotn: (dynamic collectionValue){
+                      Log.d("点击了收藏按钮  --${item['id']}-$collectionValue");
+                      return true;
+                    }
+                ),
+              ),
+            ]
+        ),
+      ),
+    );
+  }
+}

+ 0 - 0
packages/cpt_community/lib/modules/my_posts/my_posts_forrent/my_posts_forrent_respository.dart


+ 74 - 0
packages/cpt_community/lib/modules/my_posts/my_posts_forrent/my_posts_forrent_state.dart

@@ -0,0 +1,74 @@
+import 'package:widgets/load_state_layout.dart';
+
+class MyPostsForRentState {
+  int? activeTabIndex =0;
+  //页面 LoadView 状态的展示
+  LoadState loadingState;
+  String? errorMessage;
+
+  int? curPage;
+  int? pageSize = 10;
+  int? filterCount = 0;
+  List<Map<String, dynamic>> list;
+
+  List<String>? tabsList = ['For Sale', 'For Rent'];
+
+  MyPostsForRentState({
+    this.activeTabIndex,
+    this.loadingState = LoadState.State_Loading,
+    String? errorMessage,
+    this.curPage = 1,
+    this.pageSize = 10,
+    this.filterCount = 0,
+    required this.list,
+    this.tabsList,
+  });
+
+  MyPostsForRentState copyWith({
+    int? activeTabIndex,
+    LoadState? loadingState,
+    String? errorMessage,
+    int? curPage,
+    int? pageSize,
+    int? filterCount,
+    List<Map<String, dynamic>>? list,
+    List<String>? tabsList,
+  }) {
+    return MyPostsForRentState(
+      activeTabIndex: activeTabIndex ?? this.activeTabIndex,
+      loadingState: loadingState ?? this.loadingState,
+      errorMessage: errorMessage ?? this.errorMessage,
+      curPage: curPage ?? this.curPage,
+      pageSize: pageSize ?? this.pageSize,
+      filterCount: filterCount ?? this.filterCount,
+      tabsList: tabsList ?? this.tabsList,
+      list: list ?? this.list,
+    );
+  }
+
+  Map<String, dynamic> toMap() {
+    return {
+      'activeTabIndex': this.activeTabIndex,
+      'loadingState': this.loadingState,
+      'errorMessage': this.errorMessage,
+      'curPage': this.curPage,
+      'pageSize': this.pageSize,
+      'filterCount': this.filterCount,
+      'list': this.list,
+      'tabsList': this.tabsList,
+    };
+  }
+
+  factory MyPostsForRentState.fromMap(Map<String, dynamic> map) {
+    return MyPostsForRentState(
+      activeTabIndex: map['activeTabIndex'] as int,
+      loadingState: map['loadingState'] as LoadState,
+      errorMessage: map['errorMessage'] as String,
+      curPage: map['curPage'] as int,
+      pageSize: map['pageSize'] as int,
+      filterCount: map['filterCount'] as int,
+      list: map['list'] as List<Map<String, dynamic>>,
+      tabsList: map['tabsList'] as List<String>,
+    );
+  }
+}

+ 0 - 0
packages/cpt_community/lib/modules/my_posts/my_posts_forrent/my_posts_forrent_vm.dart


Неке датотеке нису приказане због велике количине промена