Browse Source

update services

glglove 2 months ago
parent
commit
3fb0d90909
100 changed files with 9534 additions and 392 deletions
  1. 4 0
      melos.yaml
  2. 1 0
      packages/cpt_community/lib/modules/community/community_vm.dart
  3. 0 130
      packages/cpt_property/lib/components/bottomDialog.dart
  4. 0 1
      packages/cpt_property/lib/modules/ioan/property_ioan_page.dart
  5. 2 2
      packages/cpt_property/lib/modules/ioan/property_ioan_vm.dart
  6. 1 1
      packages/cpt_property/lib/modules/news/page/property_news_page.dart
  7. 0 1
      packages/cpt_property/lib/modules/news_detail/property_news_detail_page.dart
  8. 76 0
      packages/cpt_services/lib/components/chooseAirConditionContent.dart
  9. 44 0
      packages/cpt_services/lib/components/chooseAirConditionContent_state.dart
  10. 75 0
      packages/cpt_services/lib/components/chooseAirConditionContent_vm.dart
  11. 28 0
      packages/cpt_services/lib/components/chooseAirConditionContent_vm.g.dart
  12. 73 0
      packages/cpt_services/lib/components/chooseAirConditionTitle.dart
  13. 126 0
      packages/cpt_services/lib/components/chooseHouseCleanContent.dart
  14. 57 0
      packages/cpt_services/lib/components/chooseHouseCleanContent_state.dart
  15. 89 0
      packages/cpt_services/lib/components/chooseHouseCleanContent_vm.dart
  16. 28 0
      packages/cpt_services/lib/components/chooseHouseCleanContent_vm.g.dart
  17. 73 0
      packages/cpt_services/lib/components/chooseHouseCleanTitle.dart
  18. 96 0
      packages/cpt_services/lib/components/chooseVisitTimeBottomFooter.dart
  19. 71 0
      packages/cpt_services/lib/components/chooseVisitTimeBottomFooter_vm.dart
  20. 27 0
      packages/cpt_services/lib/components/chooseVisitTimeBottomFooter_vm.g.dart
  21. 176 0
      packages/cpt_services/lib/components/chooseVisitTimeContent.dart
  22. 67 0
      packages/cpt_services/lib/components/chooseVisitTimeContent_state.dart
  23. 321 0
      packages/cpt_services/lib/components/chooseVisitTimeContent_vm.dart
  24. 28 0
      packages/cpt_services/lib/components/chooseVisitTimeContent_vm.g.dart
  25. 121 0
      packages/cpt_services/lib/components/chooseVisitTimeTitle.dart
  26. 44 0
      packages/cpt_services/lib/components/chooseVisitTimeTitle_state.dart
  27. 144 0
      packages/cpt_services/lib/components/chooseVisitTimeTitle_vm.dart
  28. 28 0
      packages/cpt_services/lib/components/chooseVisitTimeTitle_vm.g.dart
  29. 227 0
      packages/cpt_services/lib/components/status_card_item.dart
  30. 79 0
      packages/cpt_services/lib/components/status_card_item_vm.dart
  31. 26 0
      packages/cpt_services/lib/components/status_card_item_vm.g.dart
  32. 274 0
      packages/cpt_services/lib/components/user_evaluate_card_item.dart
  33. 35 0
      packages/cpt_services/lib/components/user_evaluate_card_item_state.dart
  34. 19 0
      packages/cpt_services/lib/components/visitTimeType.dart
  35. 90 3
      packages/cpt_services/lib/constants_services.dart
  36. 133 0
      packages/cpt_services/lib/modules/services/history/history_page.dart
  37. 38 0
      packages/cpt_services/lib/modules/services/history/history_state.dart
  38. 342 0
      packages/cpt_services/lib/modules/services/history/history_vm.dart
  39. 25 0
      packages/cpt_services/lib/modules/services/history/history_vm.g.dart
  40. 102 51
      packages/cpt_services/lib/modules/services/homeService/home_service_page.dart
  41. 172 28
      packages/cpt_services/lib/modules/services/homeService/home_service_vm.dart
  42. 1 1
      packages/cpt_services/lib/modules/services/homeService/home_service_vm.g.dart
  43. 73 69
      packages/cpt_services/lib/modules/services/homeService/service_card_item.dart
  44. 133 0
      packages/cpt_services/lib/modules/services/inProgress/in_progress_page.dart
  45. 38 0
      packages/cpt_services/lib/modules/services/inProgress/in_progress_state.dart
  46. 342 0
      packages/cpt_services/lib/modules/services/inProgress/in_progress_vm.dart
  47. 25 0
      packages/cpt_services/lib/modules/services/inProgress/in_progress_vm.g.dart
  48. 544 0
      packages/cpt_services/lib/modules/services/service_clean_detail/service_clean_detail_page.dart
  49. 50 0
      packages/cpt_services/lib/modules/services/service_clean_detail/service_clean_detail_state.dart
  50. 433 0
      packages/cpt_services/lib/modules/services/service_clean_detail/service_clean_detail_vm.dart
  51. 27 0
      packages/cpt_services/lib/modules/services/service_clean_detail/service_clean_detail_vm.g.dart
  52. 374 0
      packages/cpt_services/lib/modules/services/service_evaluate_create/service_evaluate_create_page.dart
  53. 66 0
      packages/cpt_services/lib/modules/services/service_evaluate_create/service_evaluate_create_state.dart
  54. 479 0
      packages/cpt_services/lib/modules/services/service_evaluate_create/service_evaluate_create_vm.dart
  55. 28 0
      packages/cpt_services/lib/modules/services/service_evaluate_create/service_evaluate_create_vm.g.dart
  56. 74 0
      packages/cpt_services/lib/modules/services/service_evaluate_create_success/evaluate_create_success_page.dart
  57. 146 0
      packages/cpt_services/lib/modules/services/service_evaluate_list/service_evaluate_list_page.dart
  58. 38 0
      packages/cpt_services/lib/modules/services/service_evaluate_list/service_evaluate_list_state.dart
  59. 342 0
      packages/cpt_services/lib/modules/services/service_evaluate_list/service_evaluate_list_vm.dart
  60. 27 0
      packages/cpt_services/lib/modules/services/service_evaluate_list/service_evaluate_list_vm.g.dart
  61. 629 0
      packages/cpt_services/lib/modules/services/service_order_confirm/service_order_confirm_page.dart
  62. 46 0
      packages/cpt_services/lib/modules/services/service_order_confirm/service_order_confirm_state.dart
  63. 367 0
      packages/cpt_services/lib/modules/services/service_order_confirm/service_order_confirm_vm.dart
  64. 27 0
      packages/cpt_services/lib/modules/services/service_order_confirm/service_order_confirm_vm.g.dart
  65. 99 0
      packages/cpt_services/lib/modules/services/service_pay_success/service_pay_success_page.dart
  66. 27 0
      packages/cpt_services/lib/modules/services/service_pay_success/service_pay_success_state.dart
  67. 232 0
      packages/cpt_services/lib/modules/services/service_pay_success/service_pay_success_vm.dart
  68. 27 0
      packages/cpt_services/lib/modules/services/service_pay_success/service_pay_success_vm.g.dart
  69. 362 0
      packages/cpt_services/lib/modules/services/service_repair_detail/service_repair_detail_page.dart
  70. 37 0
      packages/cpt_services/lib/modules/services/service_repair_detail/service_repair_detail_state.dart
  71. 248 0
      packages/cpt_services/lib/modules/services/service_repair_detail/service_repair_detail_vm.dart
  72. 27 0
      packages/cpt_services/lib/modules/services/service_repair_detail/service_repair_detail_vm.g.dart
  73. 19 99
      packages/cpt_services/lib/modules/services/services_page.dart
  74. 4 0
      packages/cpt_services/lib/modules/services/services_vm.dart
  75. 1 1
      packages/cpt_services/lib/modules/services/services_vm.g.dart
  76. 39 2
      packages/cpt_services/lib/respository/services_respository.dart
  77. 56 1
      packages/cpt_services/lib/router/page/services_page_router.dart
  78. 443 0
      packages/cpt_services/lib/router/page/services_page_router.gr.dart
  79. 1 0
      packages/cpt_services/pubspec.yaml
  80. 1 1
      packages/cs_plugin_basic/lib/provider/app_config/app_config_service.g.dart
  81. 93 0
      packages/cs_plugin_basic/lib/provider/shopping_cart/shopping_cart.dart
  82. 119 0
      packages/cs_plugin_basic/lib/provider/shopping_cart/shopping_cart_service.dart
  83. 27 0
      packages/cs_plugin_basic/lib/provider/shopping_cart/shopping_cart_service.g.dart
  84. 1 1
      packages/cs_plugin_basic/lib/provider/user_config/user_config_service.g.dart
  85. BIN
      packages/cs_resources/assets/service/detail01.png
  86. BIN
      packages/cs_resources/assets/service/detail02.png
  87. BIN
      packages/cs_resources/assets/service/detail03.png
  88. BIN
      packages/cs_resources/assets/service/detail04.png
  89. BIN
      packages/cs_resources/assets/service/detail05.png
  90. BIN
      packages/cs_resources/assets/service/detail06.png
  91. BIN
      packages/cs_resources/assets/service/service_icon_1.png
  92. BIN
      packages/cs_resources/assets/service/service_icon_10.png
  93. BIN
      packages/cs_resources/assets/service/service_icon_11.png
  94. BIN
      packages/cs_resources/assets/service/service_icon_12.png
  95. BIN
      packages/cs_resources/assets/service/service_icon_13.png
  96. BIN
      packages/cs_resources/assets/service/service_icon_14.png
  97. BIN
      packages/cs_resources/assets/service/service_icon_2.png
  98. BIN
      packages/cs_resources/assets/service/service_icon_3.png
  99. BIN
      packages/cs_resources/assets/service/service_icon_4.png
  100. 0 0
      packages/cs_resources/assets/service/service_icon_5.png

+ 4 - 0
melos.yaml

@@ -47,6 +47,10 @@ scripts:
     run: cd "$MELOS_ROOT_PATH/packages/cpt_auth" && dart run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in auth module
 
+  build_runner_plugin_basic:
+    run: cd "$MELOS_ROOT_PATH/packages/cs_plugin_basic" && flutter pub run build_runner build --delete-conflicting-outputs
+    description: Run `dart run build_runner build` in cs_plugin_basic module
+
   build_runner_community:
     run: cd "$MELOS_ROOT_PATH/packages/cpt_community" && flutter pub run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in community module

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

@@ -287,6 +287,7 @@ class CommunityVm extends _$CommunityVm {
         position: DialogPosition.center,
         widget: AppCustomDialog(
           message: '',
+          title: 'Choose a Category',
           dialogWidth: MediaQuery.of(context).size.width * 0.8,
           // contentBoxMaxHeight: 350,
           // contentBoxMinHeight: 300,

+ 0 - 130
packages/cpt_property/lib/components/bottomDialog.dart

@@ -1,130 +0,0 @@
-
-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 BottomDialog extends StatelessWidget {
-  String? title;
-  String? message;
-  Widget? Function(BuildContext)? messageBuilder;
-  VoidCallback confirmAction;
-  VoidCallback? cancelAction;
-  bool isShowCancelBtn;
-  String? confirmTxt;
-  String? cancelTxt;
-  final double? minHeight;
-  final double? maxHeight;
-
-  BottomDialog({
-    this.title,
-    this.message,
-    Widget Function(BuildContext)? this.messageBuilder,
-    required this.confirmAction,
-    this.cancelAction,
-    this.isShowCancelBtn = true,
-    this.confirmTxt,
-    this.cancelTxt,
-    minHeight,
-    maxHeight,
-    Key? key,
-  }): minHeight = minHeight?? 0,
-      maxHeight = maxHeight?? 300,
-        super(key:key);
-
-  @override
-  Widget build(BuildContext context) {
-    return Container(
-      width: double.infinity,
-      padding: const EdgeInsets.only(top: 30),
-      constraints: BoxConstraints(
-        minHeight: minHeight!,
-        maxHeight: maxHeight!,
-      ),
-      decoration: BoxDecoration(
-        color: context.appColors.whiteSecondBG,
-        borderRadius: const BorderRadius.only(
-          topLeft: Radius.circular(0),
-          topRight: Radius.circular(0),
-        ),
-      ),
-      child: Column(
-        children: [
-          Scrollbar(
-            child: ScrollConfiguration(
-              behavior: NoShadowScrollBehavior(),
-              child: SingleChildScrollView(
-                child: messageBuilder?.call(context) ??
-                  MyTextView(
-                    message!,
-                    fontSize: 18,
-                    textColor: context.appColors.textBlack,
-                    isFontRegular: true,
-                    textAlign: TextAlign.center,
-                    paddingLeft: 30,
-                    paddingRight: 30,
-                  ),
-              ),
-            ),
-          ).constrained(maxHeight: maxHeight! - 60),
-          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: 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),
-        ],
-      ),
-    );
-  }
-
-  //取消弹框
-  void onCancel() async {
-    SmartDialog.dismiss();
-  }
-}

+ 0 - 1
packages/cpt_property/lib/modules/ioan/property_ioan_page.dart

@@ -1,4 +1,3 @@
-import 'package:cpt_property/components/bottomDialog.dart';
 import 'package:cs_resources/generated/assets.dart';
 import 'package:cs_resources/theme/app_colors_theme.dart';
 import 'package:flutter/material.dart';

+ 2 - 2
packages/cpt_property/lib/modules/ioan/property_ioan_vm.dart

@@ -10,7 +10,7 @@ import 'package:shared/utils/log_utils.dart';
 import 'package:widgets/load_state_layout.dart';
 import 'package:widgets/my_text_view.dart';
 import 'package:widgets/widget_export.dart';
-import '../../components/bottomDialog.dart';
+import 'package:widgets/dialog/dialog_content_wrap.dart';
 import './property_ioan_state.dart';
 
 part 'property_ioan_vm.g.dart';
@@ -136,7 +136,7 @@ class PropertyIoanVm extends _$PropertyIoanVm {
     await DialogEngine.show(
       tag: "requestQuote",
       position: DialogPosition.bottom,
-      widget: BottomDialog(
+      widget: DialogContentWrap(
         confirmAction: requestQuoteConfirmAction,
         cancelAction: requestQuoteCancelAction,
         messageBuilder: (context) {

+ 1 - 1
packages/cpt_property/lib/modules/news/page/property_news_page.dart

@@ -1,4 +1,4 @@
-import 'package:cpt_property/components/bottomDialog.dart';
+
 import 'package:cpt_property/modules/property/page/property_page.dart';
 import 'package:cs_resources/theme/app_colors_theme.dart';
 import 'package:flutter/material.dart';

+ 0 - 1
packages/cpt_property/lib/modules/news_detail/property_news_detail_page.dart

@@ -1,5 +1,4 @@
 
-import 'package:cpt_property/components/bottomDialog.dart';
 import 'package:cpt_property/modules/news_detail/property_news_detail_vm.dart';
 import 'package:cpt_property/modules/rent/page/property_rent_page.dart';
 import 'package:cs_resources/generated/l10n.dart';

+ 76 - 0
packages/cpt_services/lib/components/chooseAirConditionContent.dart

@@ -0,0 +1,76 @@
+
+import 'package:cpt_services/components/chooseAirConditionContent_vm.dart';
+import 'package:cpt_services/components/status_card_item.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:shared/utils/log_utils.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+import 'package:widgets/my_cart_num.dart';
+import 'chooseAirConditionContent_state.dart';
+import 'chooseAirConditionContent_vm.dart';
+
+class ChooseAirConditionContent extends HookConsumerWidget {
+  final int id;
+  final int serviceTypeCode;
+
+  const ChooseAirConditionContent({Key? key,required this.id, required this.serviceTypeCode}) : super(key: key);
+  
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final vm = ref.read(chooseAirConditionContentVmProvider.notifier);
+    final state = ref.watch(chooseAirConditionContentVmProvider);
+
+    final totalPrice = ref.watch(chooseAirConditionContentVmProvider.select((state)=>state.totalPrice));
+
+    useEffect((){
+      // 组件挂载时执行 - 执行接口请求
+      // Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+      };
+    },[]);
+
+    return Column(
+        mainAxisAlignment: MainAxisAlignment.start,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: List.generate(state.airConditionList.length, (index){
+          return _buildItem(context, state.airConditionList[index], index, vm, totalPrice);
+        }),
+    );
+  }
+
+  Widget _buildItem(BuildContext context, AirConditionContentItem airConditionItem, int index, ChooseAirConditionContentVm vm,double totalPrice){
+    final title = airConditionItem.name??'';
+    final num = airConditionItem.num?? 1;
+    final price = airConditionItem.price?? 0;
+    return Container(
+      padding: EdgeInsets.only(left: 15, right: 15, top: 0, bottom: 0),
+      child: Column(
+        children: [
+          Row(
+            mainAxisAlignment: MainAxisAlignment.spaceBetween,
+            mainAxisSize: MainAxisSize.max,
+            children: [
+              Expanded(
+                child: MyTextView(
+                  title,
+                  fontSize: 15,
+                  isFontRegular: true,
+                  textColor: context.appColors.textDarkGray,
+                ),
+              ),
+              MyCartNum(onChange: (value){
+                vm.handlerChangeNum(context, value, index);
+              })
+            ],
+          ),
+          Divider(),
+        ],
+      ),
+    );
+  }
+}

+ 44 - 0
packages/cpt_services/lib/components/chooseAirConditionContent_state.dart

@@ -0,0 +1,44 @@
+class ChooseAirConditionContentState{
+  // 设置一个get  totalPrice
+  get totalPrice => airConditionList.map((item) => (item.num??0) * (item.price??0)).reduce((before, current) => before + current);
+  List<AirConditionContentItem> airConditionList = [];
+
+  ChooseAirConditionContentState({
+    required this.airConditionList,
+  });
+
+  ChooseAirConditionContentState copyWith({
+    List<AirConditionContentItem>? airConditionList,
+  }){
+    return ChooseAirConditionContentState(
+      airConditionList: airConditionList??this.airConditionList,
+    );
+  }
+}
+
+class AirConditionContentItem{
+  String? name;
+  int? id;
+  int? num;
+  double? price;
+  AirConditionContentItem({
+    this.name,
+    this.id,
+    this.num,
+    this.price
+  });
+
+  AirConditionContentItem copyWith({
+    String? name,
+    int? id,
+    int? num,
+    double? price
+  }){
+    return AirConditionContentItem(
+      name: name??this.name,
+      id: id??this.id,
+      num: num??this.num,
+      price: price??this.price
+    );
+  }
+}

+ 75 - 0
packages/cpt_services/lib/components/chooseAirConditionContent_vm.dart

@@ -0,0 +1,75 @@
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.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/dialog/dialog_content_wrap.dart';
+
+import '../../../respository/services_respository.dart';
+import 'chooseAirConditionContent_state.dart';
+
+part 'chooseAirConditionContent_vm.g.dart';
+
+
+List<Map<String, dynamic>> airConditionList = [
+  {
+    "name": "Hanging air conditioner (unit)",
+    "id": 1,
+    "price": 40.0,
+  },
+  {
+    "name": "Cabinet air conditioner (unit)",
+    "id": 2,
+    "price": 50.0,
+  },
+  {
+    "name": "Cylindrical air conditioner (unit)",
+    "id": 3,
+    "price": 60.0,
+  },
+  {
+    "name": "Central air conditioning (group)",
+    "id": 4,
+    "price": 70.0,
+  },
+];
+
+
+
+
+ChooseAirConditionContentState initState(){
+  Log.d("--------------------------initState---------------------");
+  List<AirConditionContentItem> airConditionListNew = [];
+  airConditionList.forEach((item) {
+    AirConditionContentItem newItem = AirConditionContentItem()..id = item['id']..price = item['price'] as double..name = item['name']..num = 1;
+    airConditionListNew.add(newItem);
+  });
+  return ChooseAirConditionContentState(
+    airConditionList: airConditionListNew,
+  );
+}
+
+@riverpod
+class ChooseAirConditionContentVm extends _$ChooseAirConditionContentVm {
+  late ServicesRespository serviceRespositoryInstance;
+
+  @override
+  ChooseAirConditionContentState build(){
+    // 引入数据仓库
+    serviceRespositoryInstance = ref.read(servicesRespositoryProvider);
+    ChooseAirConditionContentState state = initState();
+    Log.d("--------------------------build---------------------");
+
+    return state;
+  }
+
+
+  handlerChangeNum(BuildContext context, int num, int index){
+    state.airConditionList[index].num = num;
+    state = state.copyWith(
+      airConditionList: state.airConditionList,
+    );
+  }
+}

+ 28 - 0
packages/cpt_services/lib/components/chooseAirConditionContent_vm.g.dart

@@ -0,0 +1,28 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'chooseAirConditionContent_vm.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$chooseAirConditionContentVmHash() =>
+    r'fa5791b94a90ea6ee0aeab7895adf8b7e4ecb61a';
+
+/// See also [ChooseAirConditionContentVm].
+@ProviderFor(ChooseAirConditionContentVm)
+final chooseAirConditionContentVmProvider = AutoDisposeNotifierProvider<
+    ChooseAirConditionContentVm, ChooseAirConditionContentState>.internal(
+  ChooseAirConditionContentVm.new,
+  name: r'chooseAirConditionContentVmProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$chooseAirConditionContentVmHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$ChooseAirConditionContentVm
+    = AutoDisposeNotifier<ChooseAirConditionContentState>;
+// 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

+ 73 - 0
packages/cpt_services/lib/components/chooseAirConditionTitle.dart

@@ -0,0 +1,73 @@
+
+import 'package:cpt_services/modules/services/service_clean_detail/service_clean_detail_vm.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:shared/utils/log_utils.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'chooseAirConditionContent_vm.dart';
+
+// import 'chooseAirConditionTitle_vm.dart';
+
+class ChooseAirConditionTitle extends HookConsumerWidget {
+  final int id;
+  final int serviceTypeCode;
+
+  const ChooseAirConditionTitle(
+      {Key? key,required this.id, required this.serviceTypeCode,}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final totalPrice = ref.watch(chooseAirConditionContentVmProvider.select((state)=>state.totalPrice));
+
+    useEffect((){
+      // 组件挂载时执行 - 执行接口请求
+      // Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+      };
+    },[]);
+
+    return Container(
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.start,
+        children: [
+          Padding(
+            padding: const EdgeInsets.only(left: 20, right: 20, top: 10, bottom: 10),
+            child: Column(
+              children: [
+                MyTextView(
+                  "Select Air Conditioner",
+                  textColor: context.appColors.textBlack,
+                  textAlign: TextAlign.left,
+                  isFontBold: true,
+                  fontSize: 17,
+                  boxWidth: double.infinity,
+                  // maxLines: 5,
+                ),
+                MyTextView(
+                  "\$$totalPrice",
+                  textColor: context.appColors.textPrimary,
+                  textAlign: TextAlign.left,
+                  isFontMedium: true,
+                  fontSize: 17,
+                  // maxLines: 5,
+                  boxWidth: double.infinity,
+                ),
+              ],
+            ),
+          ),
+          // 分割线
+          Divider(
+            color: context.appColors.grayBgE9,
+            height: 1,
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 126 - 0
packages/cpt_services/lib/components/chooseHouseCleanContent.dart

@@ -0,0 +1,126 @@
+
+import 'package:cpt_services/components/status_card_item.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:shared/utils/color_utils.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+import 'package:widgets/my_cart_num.dart';
+import 'chooseHouseCleanContent_state.dart';
+import 'chooseHouseCleanContent_vm.dart';
+
+class ChooseHouseCleanContent extends HookConsumerWidget {
+  final int id;
+  final int serviceTypeCode;
+
+  const ChooseHouseCleanContent({Key? key,required this.id, required this.serviceTypeCode}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final vm = ref.read(chooseHouseCleanContentVmProvider.notifier);
+    final state = ref.watch(chooseHouseCleanContentVmProvider);
+
+    final totalPrice = ref.watch(chooseHouseCleanContentVmProvider.select((state)=>state.totalPrice));
+
+    useEffect((){
+      // 组件挂载时执行 - 执行接口请求
+      // Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+      };
+    },[]);
+
+    return Container(
+      padding: EdgeInsets.only(left: 0, right: 0, top: 10, bottom: 10),
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.start,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: List.generate(state.HouseCleanList.length, (index){
+          return _buildItem(context, state.HouseCleanList[index], index, vm);
+        }),
+      ),
+    );
+  }
+
+  Widget _buildItem(BuildContext context, HouseCleanContentItem HouseCleanItem, int index, ChooseHouseCleanContentVm vm){
+    final title = HouseCleanItem.name??'';
+    final areaSizeRange = HouseCleanItem.areaSizeRange??'';
+    final num = HouseCleanItem.num?? 1;
+    final price = HouseCleanItem.price?? 0;
+
+    bool isChecked = HouseCleanItem.isChecked??false;
+    bool isDisable = HouseCleanItem.isDisable??false;
+    return Container(
+      padding: EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 10),
+      margin: EdgeInsets.only(left: 15, right: 15, top: 0, bottom: 0),
+      color: ColorUtils.string2Color('#F8F8F8'),
+      child: Column(
+        children: [
+          Row(
+            mainAxisAlignment: MainAxisAlignment.spaceBetween,
+            mainAxisSize: MainAxisSize.max,
+            children: [
+              Expanded(
+                child: Column(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      title,
+                      fontSize: 16,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+                    MyTextView(
+                      areaSizeRange,
+                      fontSize: 15,
+                      isFontRegular: true,
+                      textColor: context.appColors.textDarkGray999,
+                      marginTop: 5,
+                    ),
+                  ],
+                ),
+              ),
+              // MyCartNum(onChange: (value){
+              //   vm.handlerChangeNum(context, value, index);
+              // })
+              // MyButton(
+              //   text: '\$$price',
+              //   onPressed: (){
+              //       // vm.handlerChangeNum(context, value, index);
+              //   },
+              //   fontSize: 19,
+              //   fontWeight: FontWeight.w500,
+              // )
+              Container(
+                child: MyButton(
+                  text: '\$$price',
+                  onPressed: (){
+                    if(!isDisable){
+                      isChecked = !isChecked;
+                      vm.handlerChangeNum(context, isChecked, index);
+                    }
+                  },
+                  minWidth: 80,
+                  minHeight: 40,
+                  fontSize: 19,
+                  fontWeight: FontWeight.w500,
+                  enable: !isDisable,
+                  textColor: isChecked? context.appColors.textWhite: context.appColors.textPrimary,
+                  backgroundColor: isChecked? context.appColors.textPrimary:context.appColors.textWhite,
+                  disabledBackgroundColor: context.appColors.disEnableGray,
+                  disabledTextColor: context.appColors.textWhite,
+                ),
+              ),
+            ],
+          ),
+          // Divider(),
+        ],
+      ),
+    );
+  }
+}

+ 57 - 0
packages/cpt_services/lib/components/chooseHouseCleanContent_state.dart

@@ -0,0 +1,57 @@
+class ChooseHouseCleanContentState{
+  // 设置一个get  totalPrice
+  // 设置一个get totalPrice
+  get totalPrice => HouseCleanList.map((item) => (item.isChecked ?? false) ? (item.num ?? 1) * (item.price ?? 0) : 0).reduce((before, current) => before + current);
+  List<HouseCleanContentItem> HouseCleanList = [];
+
+  ChooseHouseCleanContentState({
+    required this.HouseCleanList,
+  });
+
+  ChooseHouseCleanContentState copyWith({
+    List<HouseCleanContentItem>? HouseCleanList,
+  }){
+    return ChooseHouseCleanContentState(
+      HouseCleanList: HouseCleanList??this.HouseCleanList,
+    );
+  }
+}
+
+class HouseCleanContentItem{
+  String? name;
+  String? areaSizeRange;
+  int? id;
+  int? num;
+  double? price;
+  bool? isChecked;
+  bool? isDisable;
+  HouseCleanContentItem({
+    this.name,
+    this.areaSizeRange,
+    this.id,
+    this.num,
+    this.price,
+    this.isChecked = false,
+    this.isDisable = false,
+  });
+
+  HouseCleanContentItem copyWith({
+    String? name,
+    String? areaSizeRange,
+    int? id,
+    int? num,
+    double? price,
+    bool? isChecked,
+    bool? isDisable,
+  }){
+    return HouseCleanContentItem(
+        name: name??this.name,
+        areaSizeRange: areaSizeRange??this.areaSizeRange,
+        id: id??this.id,
+        num: num??this.num,
+        price: price??this.price,
+        isChecked: isChecked??this.isChecked,
+        isDisable: isDisable??this.isDisable
+    );
+  }
+}

+ 89 - 0
packages/cpt_services/lib/components/chooseHouseCleanContent_vm.dart

@@ -0,0 +1,89 @@
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.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/dialog/dialog_content_wrap.dart';
+
+import '../../../respository/services_respository.dart';
+import 'chooseHouseCleanContent_state.dart';
+
+part 'chooseHouseCleanContent_vm.g.dart';
+
+
+List<Map<String, dynamic>> HouseCleanList = [
+  {
+    "name": "House Clean 1(unit)",
+    "id": 1,
+    "price": 40.0,
+    "num":1,
+    "areaSizeRange": "≤600 sqft",
+  },
+  {
+    "name": "House Clean 2(unit)",
+    "id": 2,
+    "price": 57.0,
+    "num":1,
+    "areaSizeRange": "601-800 sqft",
+  },
+  {
+    "name": "House Clean 3(unit)",
+    "id": 3,
+    "price": 72.0,
+    "num":1,
+    "areaSizeRange": "801-1000 sqft",
+  },
+  {
+    "name": "House Clean 3(unit)",
+    "id": 4,
+    "price": 85.0,
+    "num":1,
+    "areaSizeRange": "1001-1200 sqft",
+  },
+];
+
+
+
+
+ChooseHouseCleanContentState initState(){
+  Log.d("--------------------------initState---------------------");
+  List<HouseCleanContentItem> HouseCleanListNew = [];
+  HouseCleanList.forEach((item) {
+    HouseCleanContentItem newItem = HouseCleanContentItem()
+      ..id = item['id']
+      ..price = item['price'] as double
+      ..name = item['name']
+      ..num = 1
+      ..areaSizeRange = item['areaSizeRange']
+      ..isChecked = false;
+    HouseCleanListNew.add(newItem);
+  });
+  return ChooseHouseCleanContentState(
+    HouseCleanList: HouseCleanListNew,
+  );
+}
+
+@riverpod
+class ChooseHouseCleanContentVm extends _$ChooseHouseCleanContentVm {
+  late ServicesRespository serviceRespositoryInstance;
+
+  @override
+  ChooseHouseCleanContentState build(){
+    // 引入数据仓库
+    serviceRespositoryInstance = ref.read(servicesRespositoryProvider);
+    ChooseHouseCleanContentState state = initState();
+    Log.d("--------------------------build---------------------");
+
+    return state;
+  }
+
+
+  handlerChangeNum(BuildContext context, bool isChecked, int index){
+    state.HouseCleanList[index].isChecked = isChecked;
+    state = state.copyWith(
+      HouseCleanList: state.HouseCleanList,
+    );
+  }
+}

+ 28 - 0
packages/cpt_services/lib/components/chooseHouseCleanContent_vm.g.dart

@@ -0,0 +1,28 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'chooseHouseCleanContent_vm.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$chooseHouseCleanContentVmHash() =>
+    r'f54a57b948bef3c2386dbebec3f70e1f50ace01b';
+
+/// See also [ChooseHouseCleanContentVm].
+@ProviderFor(ChooseHouseCleanContentVm)
+final chooseHouseCleanContentVmProvider = AutoDisposeNotifierProvider<
+    ChooseHouseCleanContentVm, ChooseHouseCleanContentState>.internal(
+  ChooseHouseCleanContentVm.new,
+  name: r'chooseHouseCleanContentVmProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$chooseHouseCleanContentVmHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$ChooseHouseCleanContentVm
+    = AutoDisposeNotifier<ChooseHouseCleanContentState>;
+// 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

+ 73 - 0
packages/cpt_services/lib/components/chooseHouseCleanTitle.dart

@@ -0,0 +1,73 @@
+
+import 'package:cpt_services/components/chooseHouseCleanContent_vm.dart';
+import 'package:cpt_services/modules/services/service_clean_detail/service_clean_detail_vm.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:shared/utils/log_utils.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+
+// import 'chooseHouseCleanTitle_vm.dart';
+
+class ChooseHouseCleanTitle extends HookConsumerWidget {
+  final int id;
+  final int serviceTypeCode;
+
+  const ChooseHouseCleanTitle(
+      {Key? key,required this.id, required this.serviceTypeCode,}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final totalPrice = ref.watch(chooseHouseCleanContentVmProvider.select((state)=>state.totalPrice));
+
+    useEffect((){
+      // 组件挂载时执行 - 执行接口请求
+      // Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+      };
+    },[]);
+
+    return Container(
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.start,
+        children: [
+          Padding(
+            padding: const EdgeInsets.only(left: 20, right: 20, top: 10, bottom: 10),
+            child: Column(
+              children: [
+                MyTextView(
+                  "Select House Clean",
+                  textColor: context.appColors.textBlack,
+                  textAlign: TextAlign.left,
+                  isFontBold: true,
+                  fontSize: 17,
+                  boxWidth: double.infinity,
+                  // maxLines: 5,
+                ),
+                MyTextView(
+                  "\$$totalPrice",
+                  textColor: context.appColors.textPrimary,
+                  textAlign: TextAlign.left,
+                  isFontMedium: true,
+                  fontSize: 17,
+                  // maxLines: 5,
+                  boxWidth: double.infinity,
+                ),
+              ],
+            ),
+          ),
+          // 分割线
+          Divider(
+            color: context.appColors.grayBgE9,
+            height: 1,
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 96 - 0
packages/cpt_services/lib/components/chooseVisitTimeBottomFooter.dart

@@ -0,0 +1,96 @@
+
+import 'package:cpt_services/components/chooseVisitTimeBottomFooter_vm.dart';
+import 'package:cpt_services/components/chooseVisitTimeContent_vm.dart';
+import 'package:cpt_services/components/status_card_item.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:shared/utils/log_utils.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+import 'package:widgets/my_cart_num.dart';
+
+class ChooseVisitTimeBottomFooter extends HookConsumerWidget {
+  final int id;
+  final int serviceTypeCode;
+
+  const ChooseVisitTimeBottomFooter({Key? key,required this.id, required this.serviceTypeCode}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final vm = ref.read(chooseVisitTimeBottomFooterVmProvider.notifier);
+    final state = ref.watch(chooseVisitTimeBottomFooterVmProvider);
+
+    final totalPrice = ref.watch(chooseVisitTimeContentVmProvider.select((state) => state.totalPrice));
+
+    useEffect((){
+      // 组件挂载时执行 - 执行接口请求
+      // Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+      };
+    },[]);
+
+    return Container(
+      height: 50,
+      width: double.infinity,
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.center,
+        mainAxisSize: MainAxisSize.max,
+        children: [
+          Container(
+            constraints: const BoxConstraints(
+              minWidth: 122,
+            ),
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.center,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              mainAxisSize: MainAxisSize.max,
+              children: [
+                Container(
+                  alignment: Alignment.center,
+                  padding: const EdgeInsets.only(left:5, right: 0,top:5,bottom: 5),
+                  child: Row(
+                    mainAxisAlignment: MainAxisAlignment.center,
+                    mainAxisSize: MainAxisSize.max,
+                    children: [
+                      MyTextView(
+                        "\$$totalPrice",
+                        fontSize: 18,
+                        textColor: Colors.white,
+                        isFontRegular: true,
+                        maxLines:2,
+                        isTextEllipsis: true,
+                        marginLeft: 5,
+                        marginRight: 15,
+                      ),
+                    ],
+                  ),
+                ),
+              ],
+            ).backgroundColor(context.appColors.textPrimary),
+          ),
+          Expanded(
+            child: Container(
+              color: context.appColors.redDefault,
+              child: Center(
+                child: MyTextView(
+                  'Confirm',
+                  textColor: context.appColors.textWhite,
+                  fontSize: 17,
+                  isFontMedium: true,
+                  textAlign: TextAlign.center,
+                ),
+              ),
+            ).onTap((){
+              vm.handlerClickVisitTimeConfirm(context, id: id , serviceTypeCode: serviceTypeCode);
+            }),
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 71 - 0
packages/cpt_services/lib/components/chooseVisitTimeBottomFooter_vm.dart

@@ -0,0 +1,71 @@
+
+import 'package:cpt_services/modules/services/service_order_confirm/service_order_confirm_vm.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.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/dialog/dialog_content_wrap.dart';
+
+import '../../../respository/services_respository.dart';
+
+part 'chooseVisitTimeBottomFooter_vm.g.dart';
+
+
+List<Map<String, dynamic>> airConditionList = [
+  {
+    "name": "Hanging air conditioner (unit)",
+    "id": 1,
+    "price": 40.0,
+  },
+  {
+    "name": "Cabinet air conditioner (unit)",
+    "id": 2,
+    "price": 50.0,
+  },
+  {
+    "name": "Cylindrical air conditioner (unit)",
+    "id": 3,
+    "price": 60.0,
+  },
+  {
+    "name": "Central air conditioning (group)",
+    "id": 4,
+    "price": 70.0,
+  },
+];
+
+
+
+
+
+@riverpod
+class ChooseVisitTimeBottomFooterVm extends _$ChooseVisitTimeBottomFooterVm {
+  // late ServicesRespository serviceRespositoryInstance;
+
+  @override
+   build(){
+    // 引入数据仓库
+    // serviceRespositoryInstance = ref.read(servicesRespositoryProvider);
+    final state = null;
+    Log.d("--------------------------build---------------------");
+
+    return state;
+  }
+
+
+  handlerChangeNum(BuildContext context, int num, int index){
+    // state.airConditionList[index].num = num;
+    // state = state.copyWith(
+    //   airConditionList: state.airConditionList,
+    // );
+  }
+
+  handlerClickVisitTimeConfirm(BuildContext context,{required int id, required int serviceTypeCode} ){
+    Log.d("handlerClickVisitTimeConfirm");
+    // 关闭 visitTime 弹框
+    final serviceOrderConfrimVm = ref.read(serviceOrderConfirmVmProvider.notifier);
+    serviceOrderConfrimVm.handlerHideVisitTimeDialog();
+  }
+}

+ 27 - 0
packages/cpt_services/lib/components/chooseVisitTimeBottomFooter_vm.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'chooseVisitTimeBottomFooter_vm.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$chooseVisitTimeBottomFooterVmHash() =>
+    r'7ef89e7901bf6761ee066d4ef93bbdf7a6ce59c5';
+
+/// See also [ChooseVisitTimeBottomFooterVm].
+@ProviderFor(ChooseVisitTimeBottomFooterVm)
+final chooseVisitTimeBottomFooterVmProvider = AutoDisposeNotifierProvider<
+    ChooseVisitTimeBottomFooterVm, Object?>.internal(
+  ChooseVisitTimeBottomFooterVm.new,
+  name: r'chooseVisitTimeBottomFooterVmProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$chooseVisitTimeBottomFooterVmHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$ChooseVisitTimeBottomFooterVm = AutoDisposeNotifier<Object?>;
+// 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

+ 176 - 0
packages/cpt_services/lib/components/chooseVisitTimeContent.dart

@@ -0,0 +1,176 @@
+
+import 'package:cpt_services/components/chooseHouseCleanContent_vm.dart';
+import 'package:cpt_services/components/chooseVisitTimeBottomFooter.dart';
+import 'package:cpt_services/components/chooseVisitTimeBottomFooter_vm.dart';
+import 'package:cpt_services/components/chooseVisitTimeContent_vm.dart';
+import 'package:cpt_services/components/status_card_item.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:shared/utils/color_utils.dart';
+import 'package:shared/utils/ext_dart.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_button.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+import 'package:widgets/my_cart_num.dart';
+
+import 'chooseVisitTimeContent_state.dart';
+
+// import 'chooseVisitTimeContent_vm.dart';
+
+class ChooseVisitTimeContent extends HookConsumerWidget {
+  final int id;
+  final int serviceTypeCode;
+  final double contentTopSectionHeight;
+  final double contentMaxHeight;
+  final double bottomBtnSectionHeight;
+
+  const ChooseVisitTimeContent({
+    Key? key,
+    required this.id,
+    required this.serviceTypeCode,
+    this.contentTopSectionHeight = 150,
+    this.contentMaxHeight = 480,
+    this.bottomBtnSectionHeight = 50,
+  }):super(key: key);
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final vm = ref.read(chooseVisitTimeContentVmProvider.notifier);
+    final state = ref.watch(chooseVisitTimeContentVmProvider);
+
+    useEffect((){
+      // 组件挂载时执行 - 执行接口请求
+      Future.microtask(() => vm.initPageData(context));
+      return () {
+        // 组件卸载时执行
+      };
+    },[]);
+
+    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--");
+        double scrollMaxHeight = contentMaxHeight - contentTopSectionHeight - bottomBtnSectionHeight ;
+        // Log.d("scrollMaxHeight  $scrollMaxHeight");
+        return Container(
+          width: double.infinity,
+          // width: 80,
+          color: ColorUtils.string2Color("#F5F5F7"),
+          padding: const EdgeInsets.only(left:20, right: 20, top: 12, bottom: 12),
+          constraints: const BoxConstraints(
+            minHeight: 0,
+            maxHeight: 280,
+          ),
+          child: LoadStateLayout(
+            state: state.loadingState,
+            successSliverWidget: [
+              SliverGrid(
+                gridDelegate:  const SliverGridDelegateWithFixedCrossAxisCount(
+                crossAxisCount: 4,
+                mainAxisSpacing: 5,
+                crossAxisSpacing: 5,
+                childAspectRatio: 80 / 38,
+                  // childAspectRatio:  166.5/214 * ScreenUtil.getAdapterSizeCtx(context, 166.5/214), // 宽高比
+                  // childAspectRatio: 166.5/214 * ((166.5/214).ap), //
+                ),
+                delegate: SliverChildBuilderDelegate(
+                      (context, index) {
+                    return  _buildTimeItem(context, index, state.visitTimeList[index], ref, vm);
+                  },
+                  childCount: state.visitTimeList.length,
+                ),
+
+                // GridView.builder(
+                //   gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
+                //     crossAxisCount: 4,
+                //     mainAxisSpacing: 5,
+                //     crossAxisSpacing: 5,
+                //     childAspectRatio: 80 / 38,
+                //   ),
+                //   itemCount: state.visitTimeList.length,
+                //   itemBuilder: (context, index) {
+                //     final item = state.visitTimeList[index];
+                //     return _buildTimeItem(context, index, item, ref, vm);
+                //   },
+                // )
+              )
+            ],
+          ),
+        );
+      }
+    );
+  }
+
+
+  Widget _buildTimeItem(BuildContext context,int index, VisitTimeContentItem item, WidgetRef ref, vm){
+    final name = item.name;
+    return HookBuilder(
+      builder: (context) {
+        final isChecked = useState<bool>(item.isChecked??false);
+        return SizedBox(
+          width: 80,
+          child: Stack(
+            children: [
+              Container(
+                decoration: BoxDecoration(
+                  color: isChecked.value? ColorUtils.string2Color("#D9DFF6"):context.appColors.whiteBG,
+                  borderRadius: BorderRadius.circular(8),
+                  boxShadow: [
+                    BoxShadow(
+                      color: ColorUtils.string2Color('#E5E5E5'),
+                      offset: const Offset(0, 2),
+                      blurRadius: 8,
+                    ),
+                  ],
+                ),
+                child:  MyButton(
+                  onPressed: (){
+                    isChecked.value = !isChecked.value;
+                    vm.handlerTimeItemClick(context, index, isChecked.value);
+                  },
+                  text: '$name',
+                  minHeight: 38,
+                  textColor: isChecked.value?context.appColors.textPrimary: context.appColors.textDarkGray999,
+                  fontSize: 12,
+                  fontWeight: FontWeight.w400,
+                )
+              ),
+              Positioned(
+                child: MyTextView(
+                  'Extreme speed',
+                  textColor: ColorUtils.string2Color("#DD6800"),
+                  fontSize: 8,
+                  isFontRegular: true,
+                  backgroundColor:ColorUtils.string2Color("#FFEBD9"),
+                  paddingTop: 2,
+                  paddingBottom: 2,
+                  paddingLeft: 5,
+                  paddingRight: 5,
+                ).clipRRect(
+                  topLeft: 10,
+                  bottomRight: 10
+                ).onTap((){
+
+                }),
+              ),
+            ],
+          ),
+        );
+      }
+    );
+  }
+
+}
+
+
+
+
+

+ 67 - 0
packages/cpt_services/lib/components/chooseVisitTimeContent_state.dart

@@ -0,0 +1,67 @@
+import 'package:widgets/load_state_layout.dart';
+
+import 'visitTimeType.dart';
+
+class ChooseVisitTimeContentState{
+  // 设置一个get  totalPrice
+  get totalPrice => visitTimeList.map((item) => (item.isChecked??false) ? item.price??0 : 0 ).reduce((before, current) => before + current);
+
+  //页面 LoadView 状态的展示
+  LoadState loadingState;
+  String? errorMessage;
+
+  List<VisitTimeContentItem> visitTimeList = [];
+
+  ChooseVisitTimeContentState({
+    this.loadingState = LoadState.State_Loading,
+    this.errorMessage,
+    required this.visitTimeList,
+  });
+
+  ChooseVisitTimeContentState copyWith({
+    LoadState? loadingState,
+    String? errorMessage,
+    List<VisitTimeContentItem>? visitTimeList,
+  }){
+    return ChooseVisitTimeContentState(
+      loadingState: loadingState??this.loadingState,
+      errorMessage: errorMessage??this.errorMessage,
+      visitTimeList: visitTimeList??this.visitTimeList,
+    );
+  }
+}
+
+class VisitTimeContentItem{
+  String? name;
+  int? id;
+  double? price;
+  Map<String, dynamic>? type;
+  bool? isChecked;
+  bool? isDisable;
+  VisitTimeContentItem({
+    this.name,
+    this.id,
+    this.price,
+    this.type,
+    this.isChecked = false,
+    this.isDisable = false,
+  });
+
+  VisitTimeContentItem copyWith({
+    String? name,
+    int? id,
+    double? price,
+    Map<String, dynamic>? type,
+    bool? isChecked,
+    bool? isDisable,
+  }){
+    return VisitTimeContentItem(
+        name: name??this.name,
+        id: id??this.id,
+        price: price??this.price,
+        type: type ?? this.type, // 在这里设置默认值
+        isChecked: isChecked??this.isChecked,
+        isDisable: isDisable??this.isDisable,
+    );
+  }
+}

+ 321 - 0
packages/cpt_services/lib/components/chooseVisitTimeContent_vm.dart

@@ -0,0 +1,321 @@
+
+import 'package:cpt_services/modules/services/service_order_confirm/service_order_confirm_vm.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.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/dialog/dialog_content_wrap.dart';
+import 'package:widgets/load_state_layout.dart';
+
+import '../../../respository/services_respository.dart';
+import 'chooseVisitTimeContent_state.dart';
+
+part 'chooseVisitTimeContent_vm.g.dart';
+
+
+List<Map<String, dynamic>> visitTimeList = [
+  {
+    "name": "08:00 AM",
+    "id": 1,
+    "price": 40.0,
+    "type": null
+  },
+  {
+    "name": "08:30 AM",
+    "id": 1,
+    "price": 40.0,
+    "type": null
+  },
+  {
+    "name": "09:00 AM",
+    "id": 1,
+    "price": 40.0,
+    "type": null
+  },
+  {
+    "name": "09:30 AM",
+    "id": 1,
+    "price": 40.0,
+    "type": null
+  },
+  {
+    "name": "10:00 AM",
+    "id": 1,
+    "price": 40.0,
+    "type": null
+  },
+  {
+    "name": "10:30 AM",
+    "id": 1,
+    "price": 40.0,
+    "type": null
+  },
+  {
+    "name": "11:00 AM",
+    "id": 1,
+    "price": 40.0,
+    "type": null
+  },
+  {
+    "name": "11:30 AM",
+    "id": 1,
+    "price": 40.0,
+    "type": null
+  },
+  {
+    "name": "12:00 AM",
+    "id": 1,
+    "price": 40.0,
+    "type": null
+  },
+  {
+    "name": "12:30 PM",
+    "id": 1,
+    "price": 40.0,
+    "type": null
+  },
+  {
+    "name": "13:00 PM",
+    "id": 1,
+    "price": 50.0,
+    "type": {
+      "text": "Extreme speed",
+      "type": "extremeSpeed",
+      'code': 1,
+    }
+  },
+  {
+    "name": "13:30 PM",
+    "id": 1,
+    "price": 50.0,
+    "type": {
+      "text": "Extreme speed",
+      "type": "extremeSpeed",
+      'code': 1,
+    }
+  },
+  {
+    "name": "14:00 PM",
+    "id": 1,
+    "price": 50.0,
+    "type": {
+      "text": "Extreme speed",
+      "type": "extremeSpeed",
+      'code': 1,
+    }
+  },
+  {
+    "name": "14:30 PM",
+    "id": 1,
+    "price": 50.0,
+    "type": {
+      "text": "Extreme speed",
+      "type": "extremeSpeed",
+      'code': 1,
+    }
+  },
+  {
+    "name": "15:00 PM",
+    "id": 1,
+    "price": 50.0,
+    "type": {
+      "text": "Extreme speed",
+      "type": "extremeSpeed",
+      'code': 1,
+    }
+  },
+];
+
+
+
+
+
+
+@riverpod
+class ChooseVisitTimeContentVm extends _$ChooseVisitTimeContentVm {
+  late ServicesRespository serviceRespositoryInstance;
+
+  ChooseVisitTimeContentState initState(){
+    Log.d("--------------------------initState---------------------");
+    List<VisitTimeContentItem> visitTimeListNew = [];
+    visitTimeList.forEach((item) {
+      VisitTimeContentItem newItem = VisitTimeContentItem()
+        ..id = item['id']
+        ..price = item['price'] as double
+        ..name = item['name']
+        ..type = null
+        ..isDisable = false
+        ..isChecked = false;
+      visitTimeListNew.add(newItem);
+    });
+
+    return ChooseVisitTimeContentState(
+      visitTimeList: visitTimeListNew,
+    );
+  }
+
+  @override
+  ChooseVisitTimeContentState build(){
+    // 引入数据仓库
+    serviceRespositoryInstance = ref.read(servicesRespositoryProvider);
+     state = initState();
+    Log.d("--------------------------build---------------------");
+
+    return state;
+  }
+
+  void changeLoadingState(LoadState loadState, String? errorMsg) {
+    state = state.copyWith(
+        loadingState: loadState,
+        errorMessage: errorMsg
+    );
+  }
+
+  initPageData(BuildContext context){
+    changeLoadingState(LoadState.State_Success, null);
+  }
+
+  Future<ChooseVisitTimeContentState> getVisitTimeList(BuildContext context) async{
+    Log.d("--------------------------getVisitTimeList---------------------");
+    // 获取数据
+    // state.visitTimeList = serviceRespositoryInstance.getVisitTimeList();
+    // state = state.copyWith(
+    //   visitTimeList: state.visitTimeList,
+    // );
+
+    changeLoadingState(LoadState.State_Loading, null);
+
+    List<Map<String, dynamic>> visitTimeList = [
+      {
+        "name": "10:00 AM",
+        "id": 1,
+        "price": 40.0,
+        "type": null
+      },
+      {
+        "name": "10:30 AM",
+        "id": 1,
+        "price": 40.0,
+        "type": null
+      },
+      {
+        "name": "11:00 AM",
+        "id": 1,
+        "price": 40.0,
+        "type": null
+      },
+      {
+        "name": "11:30 AM",
+        "id": 1,
+        "price": 40.0,
+        "type": null
+      },
+      {
+        "name": "12:00 AM",
+        "id": 1,
+        "price": 40.0,
+        "type": null
+      },
+      {
+        "name": "12:30 PM",
+        "id": 1,
+        "price": 40.0,
+        "type": null
+      },
+      {
+        "name": "13:00 PM",
+        "id": 1,
+        "price": 50.0,
+        "type": {
+          "text": "Extreme speed",
+          "type": "extremeSpeed",
+          'code': 1,
+        }
+      },
+      {
+        "name": "13:30 PM",
+        "id": 1,
+        "price": 50.0,
+        "type": {
+          "text": "Extreme speed",
+          "type": "extremeSpeed",
+          'code': 1,
+        }
+      },
+      {
+        "name": "14:00 PM",
+        "id": 1,
+        "price": 50.0,
+        "type": {
+          "text": "Extreme speed",
+          "type": "extremeSpeed",
+          'code': 1,
+        }
+      },
+      {
+        "name": "14:30 PM",
+        "id": 1,
+        "price": 50.0,
+        "type": {
+          "text": "Extreme speed",
+          "type": "extremeSpeed",
+          'code': 1,
+        }
+      },
+      {
+        "name": "15:00 PM",
+        "id": 1,
+        "price": 50.0,
+        "type": {
+          "text": "Extreme speed",
+          "type": "extremeSpeed",
+          'code': 1,
+        }
+      },
+    ];
+    await Future.delayed(Duration(seconds: 1), (){});
+
+    List<VisitTimeContentItem> visitTimeListNew = [];
+    visitTimeList.forEach((item) {
+      VisitTimeContentItem newItem = VisitTimeContentItem()
+        ..id = item['id']
+        ..price = item['price'] as double
+        ..name = item['name']
+        ..type = null
+        ..isDisable = false
+        ..isChecked = false;
+      visitTimeListNew.add(newItem);
+    });
+
+    state = state.copyWith(
+      visitTimeList: visitTimeListNew,
+    );
+    Log.d("--------------------------getVisitTimeList END   ${state.visitTimeList}---------------------");
+
+    changeLoadingState(LoadState.State_Success, null);
+
+    return ChooseVisitTimeContentState(
+      visitTimeList: visitTimeListNew,
+    );
+  }
+
+
+  handlerChangeNum(BuildContext context, int num, int index){
+    // state.visitTimeList[index].tu = num;
+    // state = state.copyWith(
+    //   visitTimeList: state.visitTimeList,
+    // );
+  }
+
+  handlerTimeItemClick(BuildContext context, int index, bool isSelected){
+    // 修改 状态
+    state.visitTimeList[index].isChecked = isSelected;
+    state = state.copyWith(
+      visitTimeList: state.visitTimeList,
+    );
+
+    ref.read(serviceOrderConfirmVmProvider.notifier).setConfirmOrderTotalPrice(context);
+  }
+}

+ 28 - 0
packages/cpt_services/lib/components/chooseVisitTimeContent_vm.g.dart

@@ -0,0 +1,28 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'chooseVisitTimeContent_vm.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$chooseVisitTimeContentVmHash() =>
+    r'f7465ee5636aac140e046ba07c02e64117890fe4';
+
+/// See also [ChooseVisitTimeContentVm].
+@ProviderFor(ChooseVisitTimeContentVm)
+final chooseVisitTimeContentVmProvider = AutoDisposeNotifierProvider<
+    ChooseVisitTimeContentVm, ChooseVisitTimeContentState>.internal(
+  ChooseVisitTimeContentVm.new,
+  name: r'chooseVisitTimeContentVmProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$chooseVisitTimeContentVmHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$ChooseVisitTimeContentVm
+    = AutoDisposeNotifier<ChooseVisitTimeContentState>;
+// 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

+ 121 - 0
packages/cpt_services/lib/components/chooseVisitTimeTitle.dart

@@ -0,0 +1,121 @@
+
+import 'package:cpt_services/components/chooseVisitTimeBottomFooter.dart';
+import 'package:cpt_services/components/chooseVisitTimeTitle_vm.dart';
+import 'package:cpt_services/modules/services/service_clean_detail/service_clean_detail_vm.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:shared/utils/color_utils.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'chooseVisitTimeTitle_state.dart';
+import 'chooseVisitTimeTitle_vm.dart';
+
+class ChooseVisitTimeTitle extends HookConsumerWidget {
+  final int id;
+  final int serviceTypeCode;
+
+  const ChooseVisitTimeTitle(
+      {Key? key,required this.id, required this.serviceTypeCode,}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final vm = ref.read(chooseVisitTimeTitleVmProvider.notifier);
+    final state = ref.watch(chooseVisitTimeTitleVmProvider);
+    List<DayInfoItem> dayInfoList = ref.watch(chooseVisitTimeTitleVmProvider.select((state) => state.dayInfoList))?? [];
+
+    useEffect((){
+      // 组件挂载时执行 - 执行接口请求
+      // Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+      };
+    },[]);
+
+    return Container(
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.start,
+        children: [
+          Padding(
+            padding: const EdgeInsets.only(left: 20, right: 20, top: 19, bottom: 16.5),
+            child: Column(
+              children: [
+                MyTextView(
+                  "Please choose a visit time",
+                  textColor: context.appColors.textBlack,
+                  textAlign: TextAlign.left,
+                  isFontBold: true,
+                  fontSize: 17,
+                  boxWidth: double.infinity,
+                  // maxLines: 5,
+                ),
+                MyTextView(
+                  '30 minutes before the start of the service, free time modification is available',
+                  textColor: context.appColors.textDarkGray999,
+                  textAlign: TextAlign.left,
+                  isFontRegular: true,
+                  fontSize: 14,
+                  // maxLines: 5,
+                  boxWidth: double.infinity,
+                ),
+              ],
+            ),
+          ),
+          Container(
+            width: double.infinity,
+            height: 65,
+            // color: Colors.red,
+            margin: const EdgeInsets.only(left: 20, right: 20, top: 0, bottom: 0),
+            child: ListView.builder(
+                itemCount: dayInfoList.length,
+                shrinkWrap: true,
+                scrollDirection: Axis.horizontal,
+                itemBuilder: (context, index){
+                  DayInfoItem item = dayInfoList[index];
+                  return Container(
+                      width: 100,
+                      height: 65,
+                      decoration: BoxDecoration(
+                        color: item.isSelected ? ColorUtils.string2Color('#F5F5F7'): context.appColors.whiteBG,
+                        borderRadius: const BorderRadius.only(topLeft: Radius.circular(10), topRight: Radius.circular(10)),
+                      ),
+                      child: Column(
+                          mainAxisAlignment: MainAxisAlignment.center,
+                          children: [
+                            MyTextView(
+                              "${item.day}",
+                              textColor: context.appColors.textBlack,
+                              fontSize: 12,
+                              isFontBold: true,
+                            ),
+                            MyTextView(
+                              "${item.date}",
+                              textColor: context.appColors.textBlack,
+                              fontSize: 12,
+                              isFontBold: true,
+                              marginTop: 5,
+                            ),
+                          ]
+                      )
+                  ).onTap((){
+                    // 点击某一天
+                    vm.handlerClicKSomeDay(context, item, index);
+                  });
+                }
+            ),
+          ),
+          // 分割线
+          Divider(
+            color: context.appColors.grayBgE9,
+            height: 1,
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 44 - 0
packages/cpt_services/lib/components/chooseVisitTimeTitle_state.dart

@@ -0,0 +1,44 @@
+class ChooseVisitTimeTitleState {
+  int? currentSelectIndex;
+  List<DayInfoItem>? dayInfoList;
+
+  ChooseVisitTimeTitleState({
+    this.currentSelectIndex = 0,
+    dayInfoList,
+  }): dayInfoList = dayInfoList ?? [
+    DayInfoItem(day: 'Tody', date: '2022-01-01', isSelected: true, isIntrady: true,),
+    DayInfoItem(day: 'Tomorrow', date: '2022-01-02', isSelected: false, isIntrady: false,),
+    DayInfoItem(day: 'Wendnesday', date: '2022-01-03', isSelected: false, isIntrady: false,),
+    DayInfoItem(day: 'Thursdy', date: '2022-01-04', isSelected: false, isIntrady: false,),
+    DayInfoItem(day: 'Friday', date: '2022-01-05', isSelected: false, isIntrady: false,),
+    DayInfoItem(day: 'Saturday', date: '2022-01-06', isSelected: false, isIntrady: false,),
+    DayInfoItem(day: 'Sunday', date: '2022-01-07', isSelected: false, isIntrady: false,),
+  ];
+
+  ChooseVisitTimeTitleState copyWith({
+    int? currentSelectIndex,
+    List<DayInfoItem>? dayInfoList,
+  }) {
+    return ChooseVisitTimeTitleState(
+      currentSelectIndex: currentSelectIndex ?? this.currentSelectIndex,
+      dayInfoList: dayInfoList ?? this.dayInfoList,
+    );
+  }
+}
+
+
+class DayInfoItem {
+  int? id;
+  String? day;
+  String? date;
+  bool isSelected;
+  bool isIntrady;
+  DayInfoItem({
+    this.id = 0,
+    this.day,
+    this.date,
+    this.isSelected = false,
+    this.isIntrady = false,
+  });
+}
+

+ 144 - 0
packages/cpt_services/lib/components/chooseVisitTimeTitle_vm.dart

@@ -0,0 +1,144 @@
+
+import 'package:cpt_services/components/chooseVisitTimeContent_vm.dart';
+import 'package:cpt_services/modules/services/service_order_confirm/service_order_confirm_vm.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.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/dialog/dialog_content_wrap.dart';
+
+import '../../../respository/services_respository.dart';
+import 'chooseVisitTimeTitle_state.dart';
+
+part 'chooseVisitTimeTitle_vm.g.dart';
+
+
+List<Map<String, dynamic>> dayInfoList = [
+  {
+    "id": 1,
+    "day": 'Tody',
+    "date": '2022-01-01',
+    "isSelected": true,
+    "isIntrady": true,
+  },
+  {
+    "id": 2,
+    "day": 'Tommorow',
+    "date": '2022-01-01',
+    "isSelected": false,
+    "isIntrady": false,
+  },
+  {
+    "id": 3,
+    "day": '周三',
+    "date": '2022-01-01',
+    "isSelected": false,
+    "isIntrady": false,
+  },
+  {
+    "id": 4,
+    "day": '周四',
+    "date": '2022-01-01',
+    "isSelected": false,
+    "isIntrady": false,
+  },
+  {
+    "id": 5,
+    "day": '周五',
+    "date": '2022-01-01',
+    "isSelected": false,
+    "isIntrady": false,
+  },
+  {
+    "id": 6,
+    "day": '周六',
+    "date": '2022-01-01',
+    "isSelected": false,
+    "isIntrady": false,
+  },
+  {
+    "id": 7,
+    "day": '周天',
+    "date": '2022-01-01',
+    "isSelected": false,
+    "isIntrady": false,
+  },
+];
+
+
+
+
+ChooseVisitTimeTitleState initState(){
+  Log.d("--------------------------initState---------------------");
+  List<DayInfoItem> dayInfoListNew = [];
+  dayInfoList.forEach((item) {
+    DayInfoItem newItem = DayInfoItem()
+      ..id = item['id']
+      ..day = item['day']
+      ..date = item['date']
+      ..isSelected = item['isSelected']
+      ..isIntrady = item['isIntrady'];
+    dayInfoListNew.add(newItem);
+  });
+  return ChooseVisitTimeTitleState(
+    dayInfoList: dayInfoListNew,
+  );
+}
+
+@riverpod
+class ChooseVisitTimeTitleVm extends _$ChooseVisitTimeTitleVm {
+  late ServicesRespository serviceRespositoryInstance;
+
+  @override
+  ChooseVisitTimeTitleState build(){
+    // 引入数据仓库
+    serviceRespositoryInstance = ref.read(servicesRespositoryProvider);
+    ChooseVisitTimeTitleState state = initState();
+    Log.d("--------------------------build---------------------");
+
+    return state;
+  }
+
+
+  handlerChangeNum(BuildContext context, int num, int index){
+    // state.dayInfoList[index].tu = num;
+    // state = state.copyWith(
+    //   dayInfoList: state.dayInfoList,
+    // );
+  }
+
+  // 点击某一天
+  handlerClicKSomeDay(BuildContext context, DayInfoItem dayInfoItem, int index){
+    state.currentSelectIndex = index;
+    // 循环 state.dayInfoList 需要索引值为index 的 设置isSelected 为true 其他设置为false
+    state.dayInfoList?.asMap().forEach((idx, value) {
+      if(idx == index){
+        value.isSelected = true;
+      }else{
+        value.isSelected = false;
+      }
+    });
+
+    state = state.copyWith(
+      currentSelectIndex: state.currentSelectIndex,
+      dayInfoList: state.dayInfoList,
+    );
+
+    // 触发 chooseVisitTimeContent 的刷新
+    final chooseVisitTimeContentVm = ref.read(chooseVisitTimeContentVmProvider.notifier);
+    chooseVisitTimeContentVm?.getVisitTimeList(context);
+  }
+
+  // 点击某一时间段
+  handlerTimeItemClick(BuildContext context, int index, bool isSelected){
+    // 修改 状态
+    // state.dayInfoList[index].isChecked = isSelected;
+    // state = state.copyWith(
+    //   dayInfoList: state.dayInfoList,
+    // );
+    //
+    // ref.read(serviceOrderConfirmVmProvider.notifier).setConfirmOrderTotalPrice(context);
+  }
+}

+ 28 - 0
packages/cpt_services/lib/components/chooseVisitTimeTitle_vm.g.dart

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

+ 227 - 0
packages/cpt_services/lib/components/status_card_item.dart

@@ -0,0 +1,227 @@
+import 'package:cpt_services/components/status_card_item_vm.dart';
+import 'package:cpt_services/modules/services/homeService/home_service_page.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:hooks_riverpod/hooks_riverpod.dart';
+import 'package:shared/utils/color_utils.dart';
+import 'package:shared/utils/ext_dart.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_text_view.dart';
+
+import '../constants_services.dart';
+
+class StausCardItem extends HookConsumerWidget {
+  Map<String, dynamic>? serviceType = servicesConstants.servicesType['houseCleaning'];
+  Map<String, dynamic> serviceStatus = servicesConstants.servicesStatus['pending'];
+  int serviceId;
+  Map<String, dynamic> itemObj;
+  double? cardHeight ;
+  final Function()? onTap;
+  final Function(dynamic)? onClickColleciotn;
+
+  StausCardItem({
+    Key? key,
+    required this.serviceId,
+    required this.serviceType,
+    required this.serviceStatus,
+    required this.itemObj,
+    this.onTap,
+    this.onClickColleciotn,
+    cardHeight,
+  }): super(key: key) {
+    this.cardHeight = cardHeight ?? 180.0;
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final vm = ref.read(statusCardItemVmProvider.notifier);
+
+    final title = itemObj.getValue("title", "House Cleaning Services");
+    final companyName = itemObj.getValue("company_name", "HONG YE GROUP PTE LTD");
+    final duration = itemObj.getValue("duration", "Daily cleaning for 2 hours");
+    final totalPrice = itemObj.getValue("totalPrice", '40');
+    final visit_time = itemObj.getValue("visit_time", '14 0ct 2024 15:00');
+    final order_time = itemObj.getValue("order_time", '13 0ct 2024 12:00');
+    final status_text = itemObj.getValue("status_text", 'In Progress');
+
+    List<Map<String, dynamic>>? actionBtnList = servicesConstants.servicesStatusActionBtnList[serviceStatus['code']];
+    Log.d("actionBtnList   $actionBtnList");
+
+    useEffect((){
+      vm.setInitData(context, serviceId, serviceType!, serviceStatus);
+      return () {
+      };
+    },[]);
+    return Container(
+      width: double.infinity,
+      height: cardHeight!,
+      child: Padding(
+        padding: const EdgeInsets.all(18.0),
+        child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            Expanded(
+              child: Column(
+                crossAxisAlignment: CrossAxisAlignment.start,
+                children: [
+                  Row(
+                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                    children: [
+                      // 标题
+                      Expanded(
+                        child: MyTextView(
+                          title,
+                          fontSize: 16,
+                          maxLines: 1,
+                          isFontMedium: true,
+                          textAlign: TextAlign.left,
+                        ),
+                      ),
+                      // 状态
+                      MyTextView(
+                        status_text,
+                        fontSize: 14,
+                        maxLines: 1,
+                        isFontRegular: true,
+                        textAlign: TextAlign.left,
+                        textColor: context.appColors.textPrimary,
+                      ),
+                    ],
+                  ),
+                  // 公司名称
+                  MyTextView(
+                    companyName,
+                    fontSize: 14,
+                    maxLines: 1,
+                    isFontRegular: true,
+                    textAlign: TextAlign.left,
+                    marginTop: 6,
+                  ),
+                  Row(
+                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                    children: [
+                      // 公司名称
+                      MyTextView(
+                        duration,
+                        fontSize: 14,
+                        maxLines: 1,
+                        isFontRegular: true,
+                        textAlign: TextAlign.left,
+                      ),
+                      // 公司名称
+                      MyTextView(
+                        '\$$totalPrice',
+                        fontSize: 17,
+                        maxLines: 1,
+                        isFontBold: true,
+                        textAlign: TextAlign.left,
+                        textColor: context.appColors.textPrimary,
+                      ),
+                    ],
+                  ),
+                  SizedBox(height: 7.5,),
+                  Row(
+                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                    children: [
+                      MyTextView(
+                        'Visit Time',
+                        fontSize: 14,
+                        maxLines: 1,
+                        isFontRegular: true,
+                        textAlign: TextAlign.left,
+                      ),
+                      MyTextView(
+                        visit_time,
+                        fontSize: 14,
+                        maxLines: 1,
+                        isFontRegular: true,
+                        textAlign: TextAlign.left,
+                        textColor: context.appColors.textBlack,
+                      ),
+                    ],
+                  ),
+                  SizedBox(height: 7.5,),
+                  Row(
+                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                    children: [
+                      MyTextView(
+                        'Order Time',
+                        fontSize: 14,
+                        maxLines: 1,
+                        isFontRegular: true,
+                        textAlign: TextAlign.left,
+                      ),
+                      MyTextView(
+                        order_time,
+                        fontSize: 14,
+                        maxLines: 1,
+                        isFontRegular: true,
+                        textAlign: TextAlign.left,
+                        textColor: context.appColors.textBlack,
+                      ),
+                    ],
+                  ),
+                ],
+              )
+            ),
+            Container(
+              margin: EdgeInsets.only(top: 15),
+              child: _buildActionSection(context,actionBtnList!, vm, ref),
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+
+  Widget _buildActionSection(BuildContext context,List<Map<String, dynamic>> actionBtnList, StatusCardItemVm vm, WidgetRef ref ) {
+    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(
+            height: 50,
+            // color: Colors.red,
+            width: maxWidth,
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.end,
+              mainAxisSize: MainAxisSize.max,
+              children: List.generate(actionBtnList.length, (index){
+                var item = actionBtnList[index];
+                // return Expanded(
+                //   child: _buildBtn(context, item),
+                // );
+                return _buildBtn(context, item, vm, ref);
+              })
+            ),
+          ).scrollable(
+            scrollDirection: Axis.horizontal,
+            physics: BouncingScrollPhysics(),
+          );
+      }
+    );
+  }
+
+  Widget _buildBtn(BuildContext context, Map<String, dynamic> btnItem, StatusCardItemVm vm, WidgetRef ref) {
+    // final btnCode = btnItem['code'];
+    return MyButton(
+      onPressed: (){
+        vm.handlerClickActionBtn(context, btnItem);
+      },
+      text: btnItem['text'],
+      radius: 10.0,
+      textColor: context.appColors.textWhite,
+      backgroundColor: ColorUtils.string2Color(btnItem['btnColor']),
+      fontSize: 12,
+      padding: EdgeInsets.symmetric(horizontal: 5),
+      minWidth: btnItem['btnWidth']?.toDouble(),
+      minHeight: btnItem['btnHeight']?.toDouble() + 5,
+    ).marginOnly(left: 5);
+  }
+}

+ 79 - 0
packages/cpt_services/lib/components/status_card_item_vm.dart

@@ -0,0 +1,79 @@
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:shared/utils/log_utils.dart';
+
+
+import '../../../respository/services_respository.dart';
+import '../constants_services.dart';
+import '../modules/services/service_evaluate_create/service_evaluate_create_page.dart';
+
+part 'status_card_item_vm.g.dart';
+
+
+@riverpod
+class StatusCardItemVm extends _$StatusCardItemVm {
+  late ServicesRespository serviceRespositoryInstance;
+
+  late int _serviceId;
+  late Map<String, dynamic> _serviceType;
+  late Map<String, dynamic> _serviceStatus;
+  late int _serviceTypeCode;
+
+
+  initState(){
+    Log.d("--------------------------initState---------------------");
+    return null;
+  }
+
+  @override
+  build(){
+    // 引入数据仓库
+    // serviceRespositoryInstance = ref.read(servicesRespositoryProvider);
+    final state = initState();
+    Log.d("--------------------------build---------------------");
+
+    return state;
+  }
+
+  // void changeLoadingState(LoadState loadState, String? errorMsg) {
+  //   state = state.copyWith(
+  //       loadingState: loadState,
+  //       errorMessage: errorMsg
+  //   );
+  // }
+
+  setInitData(BuildContext context,int serviceId, Map<String, dynamic> serviceType, Map<String, dynamic> serviceStatus){
+    _serviceId = serviceId;
+    _serviceType = serviceType;
+    _serviceStatus = serviceStatus;
+    _serviceTypeCode = serviceType['code']!;
+  }
+
+  handlerClickActionBtn(BuildContext context, Map<String, dynamic> actionBtn){
+    final actionBtnCode = actionBtn['code']!.toString();
+    switch (actionBtnCode) {
+      case '0':
+        // context.router.pushNamed('/detail');
+        break;
+      case '1':
+      // context.router.pushNamed('/detail');
+        break;
+      case '2':
+      // context.router.pushNamed('/detail');
+        break;
+      case '3':
+      // context.router.pushNamed('/detail');
+        break;
+      case '4':
+        // 去评价
+        // context.router.pushNamed('/detail');
+        ServiceEvaluateCreatePage.startInstance(id: _serviceId, serviceTypeCode: _serviceTypeCode,);
+        break;
+      default:
+        break;
+    }
+  }
+}

+ 26 - 0
packages/cpt_services/lib/components/status_card_item_vm.g.dart

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

+ 274 - 0
packages/cpt_services/lib/components/user_evaluate_card_item.dart

@@ -0,0 +1,274 @@
+import 'dart:math';
+
+import 'package:auto_route/src/route/page_route_info.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:plugin_basic/router/basic_page_router.dart';
+import 'package:plugin_platform/engine/image/image_preview.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/goto_page.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 'package:widgets/widget_export.dart';
+
+class UserEvaluateCardItem extends StatelessWidget {
+  final String? useInSence;  // 默认evaluateList 页面   可选值cleanDetailPage
+  final String? id;
+  final String name;
+  final String avator;
+  final String time;
+  final double? score;
+  final String content;
+  final List<dynamic>? imageUrls;
+  int? rowMaxImageNum;
+  int? contentTextMaxLines;
+  bool? isPreviewImage;
+
+  UserEvaluateCardItem({
+    Key? key,
+    this.useInSence = 'evaluateList',
+    this.id,
+    required this.name,
+    required this.avator,
+    required this.time,
+    this.score = 5.0,
+    required this.content,
+    this.imageUrls,
+    this.rowMaxImageNum = 3,
+    this.contentTextMaxLines = 3,
+    this.isPreviewImage = true,
+  }) : super(key: key);
+
+
+  @override
+  Widget build(BuildContext context) {
+    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;
+
+
+    double avatorWidth = useInSence == 'evaluateList'? 45: 35;
+    double avatorHeight = useInSence == 'evaluateList'? 45: 35;
+    double nameFontSize = useInSence == 'evaluateList'? 18: 15;
+    double starSize = useInSence == 'evaluateList'? 13.5: 12;
+    double timeFontSize = useInSence == 'evaluateList'? 13: 10;
+    double contentFontSize = useInSence == 'evaluateList'? 15: 11;
+    double imageHeight = useInSence == 'evaluateList'? 87: 70;
+    return Container(
+      child: Column(
+        children: [
+          Container(
+            width: double.infinity,
+            // color: Colors.blue,
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.start,
+              crossAxisAlignment: CrossAxisAlignment.start,
+              mainAxisSize: MainAxisSize.max,
+              children: [
+                MyLoadImage(
+                  avator,
+                  width: avatorWidth,
+                  height: avatorHeight,
+                  isCircle: true,
+                  fit: BoxFit.cover,
+                ).onTap(() {
+                  // 点击头像
+                }),
+                Expanded(
+                  child: Container(
+                    // color: Colors.yellow,
+                    margin: const EdgeInsets.only(left: 10),
+                    child: Column(
+                      mainAxisAlignment: MainAxisAlignment.start,
+                      crossAxisAlignment: CrossAxisAlignment.start,
+                      children: [
+                        MyTextView(
+                          name,
+                          isFontMedium: true,
+                          fontSize: nameFontSize,
+                          textColor: context.appColors.textBlack,
+                          maxLines: 1,
+                          isTextEllipsis: true,
+                        ),
+                        Row(
+                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          children: [
+                            // 评分显示
+                            Wrap(
+                              crossAxisAlignment: WrapCrossAlignment.center,
+                              spacing: 5,
+                              children: [
+                                AnimatedRatingStars(
+                                  initialRating: score!,
+                                  onChanged: (rating) {
+                                  },
+                                  readOnly: true,
+                                  displayRatingValue: true, // Display the rating value
+                                  interactiveTooltips: true, // Allow toggling half-star state
+                                  customFilledIcon: Icons.star,
+                                  customHalfFilledIcon: Icons.star_half,
+                                  customEmptyIcon: Icons.star_border,
+                                  filledColor: ColorUtils.string2Color("#FF0000"),
+                                  starSize: starSize,
+                                  animationDuration: const Duration(milliseconds: 0),
+                                  animationCurve: Curves.easeInOut,
+                                ),
+                                MyTextView(
+                                  '${score!}',
+                                  isFontRegular: true,
+                                  fontSize: 12,
+                                  textColor: ColorUtils.string2Color('#767676'),
+                                  maxLines: 1,
+                                  isTextEllipsis: true,
+                                ),
+                              ],
+                            ),
+                            MyTextView(
+                              time,
+                              isFontRegular: true,
+                              fontSize: timeFontSize,
+                              textColor: ColorUtils.string2Color('#767676'),
+                              maxLines: 1,
+                              isTextEllipsis: true,
+                            ),
+                          ],
+                        ),
+                      ],
+                    ),
+                  ),
+                ),
+              ],
+            ),
+          ),
+          const SizedBox(height: 12,),
+          Container(
+              width: double.infinity,
+              // color: Colors.red,
+              child: Column(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      content,
+                      textColor: context.appColors.textBlack,
+                      fontSize: contentFontSize,
+                      maxLines: contentTextMaxLines,
+                      textAlign: TextAlign.left,
+                      isTextEllipsis: true,
+                    ),
+                    const SizedBox(height: 12),
+                    // 图片
+                    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 _buildImageSection(
+                              context,
+                              isPreviewImage!,
+                              maxWidth,
+                              imageUrlsList,
+                              imageUrls,
+                              totalImageCount,
+                              otherImageCount,
+                              rowMaxImageNum!,
+                              imageHeight
+                          );
+                        }
+                    )
+                  ]
+              )
+          ),
+        ],
+      ),
+    );
+  }
+
+
+  Widget _buildImageSection(BuildContext context,bool isPreviewImage, double maxWidth, List? imageUrls,List? totalImageUrls, int totalImageCount, int otherImageCount, int rowMaxImageNum, double imageHeight) {
+    if (imageUrls != null && imageUrls!.isNotEmpty) {
+      final imageCount = imageUrls.length;
+      return SizedBox(
+          width: maxWidth,
+          height: imageHeight,
+          child: Row(
+              mainAxisAlignment: MainAxisAlignment.start,
+              children: List.generate(
+                imageUrls.length,
+                    (index) => Container(
+                    height: imageHeight,
+                    width: (totalImageCount < rowMaxImageNum)? maxWidth/rowMaxImageNum - 16 : maxWidth/imageCount - 16,
+                    margin: EdgeInsets.only(right: (index!=imageCount-1) ? 16 : 0 ),
+                    color: ColorUtils.string2Color("#F2F3F6"),
+                    child:Stack(
+                        children: [
+                          Hero(
+                            tag: imageUrls[index],
+                            child: MyLoadImage(
+                              imageUrls[index],
+                              height: 87,
+                              // width: maxWidth/imageCount - 16,
+                              width: (totalImageCount < rowMaxImageNum)? maxWidth/rowMaxImageNum - 16 : 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);
+                                  // context.appRouter.push(PreviewPhotoPageRoute(images: filteredImages, position: index));
+                                  // ImagePreviewEngine.multipleImagePreview(
+                                  //     context,
+                                  //     filteredImages,
+                                  //     heroes: List.generate(filteredImages.length, (index) => filteredImages[index]),
+                                  //     onLongPressAction: (url) {}
+                                  // );
+                                  GotoPage.pushPageByFade(
+                                    context: context,
+                                    targetPage:
+                                    PreviewPhotoPage(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();
+    }
+  }
+}

+ 35 - 0
packages/cpt_services/lib/components/user_evaluate_card_item_state.dart

@@ -0,0 +1,35 @@
+class UserEvaluateCardItemState {
+  final String name;
+  final String avator;
+  final double score;
+  final String time;
+  final String content;
+  final List<dynamic>? imageUrls;
+
+  const UserEvaluateCardItemState({
+    required this.name,
+    required this.avator,
+    required this.score,
+    required this.time,
+    required this.content,
+    this.imageUrls,
+  });
+
+  UserEvaluateCardItemState copyWith({
+    String? name,
+    String? avator,
+    double? score,
+    String? time,
+    String? content,
+    List<dynamic>? imageUrls,
+  }) {
+    return UserEvaluateCardItemState(
+      name: name ?? this.name,
+      avator: avator ?? this.avator,
+      score: score ?? this.score,
+      time: time ?? this.time,
+      content: content ?? this.content,
+      imageUrls: imageUrls ?? this.imageUrls,
+    );
+  }
+}

+ 19 - 0
packages/cpt_services/lib/components/visitTimeType.dart

@@ -0,0 +1,19 @@
+class VisitTimeType {
+  static Map<String, Map<String, dynamic>> type= {
+    "0": {
+      "text": null,
+      "type": null,
+      'code': 0,
+    },
+    "1": {
+      "text": "Extreme speed",
+      "type": "extremeSpeed",
+      'code': 1,
+    },
+    "2": {
+      "text": "Night",
+      "type": "night",
+      'code': 2,
+    }
+  };
+}

+ 90 - 3
packages/cpt_services/lib/constants_services.dart

@@ -1,19 +1,106 @@
 class servicesConstants {
    static Map<String, Map<String, dynamic>> servicesType = {
      "houseCleaning": {
-       "name": "房屋保洁",
+       "text": "houseCleaning",
        "type": "houseCleaning",
        'code': 0,
      },
      "airConditioner": {
-       "name": "空调保洁",
+       "text": "airConditioner",
        "type": "airConditioner",
        'code': 1,
      },
      "repaire": {
-       "name": "维修",
+       "text": "维修",
        "type": "repaire",
        'code': 2,
      }
   };
+
+   static Map<String, dynamic> servicesStatus = {
+     "pending": {
+       "text": "Pending",
+       "type": "pending",
+       'code': 0,
+     },
+     "inProgress": {
+       "text": "In Progress",
+       "type": "inProgress",
+       'code': 1,
+     },
+     "completed": {
+       "text": "completed",
+       "type": "completed",
+       'code': 2,
+     },
+     "canceled": {
+       "text": "canceled",
+       "type": "canceled",
+       'code': 3,
+     }
+  };
+
+   static Map<int, List<Map<String, dynamic>>> servicesStatusActionBtnList = {
+     // pending, inProgress, completed, canceled
+     servicesStatus['pending']['code']: [
+       actionBtn['0'],
+       actionBtn['1'],
+       actionBtn['2'],
+     ],
+     servicesStatus['inProgress']['code']: [
+       actionBtn['0'],
+       actionBtn['1'],
+       actionBtn['2'],
+     ],
+     servicesStatus['completed']['code']: [
+       actionBtn['3'],
+       actionBtn['4'],
+     ],
+     servicesStatus['canceled']['code']: [
+       actionBtn['3'],
+     ]
+   };
+
+  static Map<String, dynamic> actionBtn = {
+     "0": {
+       "text": "Cancel",
+       "type": "cancel",
+       'code': 0,
+       'btnColor': '#FFFE6C00',
+       'btnWidth': 65.0,
+       'btnHeight': 35.0,
+     },
+     "1": {
+       "text": "Contact Merchant",
+       "type": "contactMerchant",
+       'code': 1,
+       'btnColor': '#FF4161D0',
+       'btnWidth': 115.0,
+       'btnHeight': 35.0,
+     },
+    "2": {
+      "text": "Contact Service Personnel",
+      "type": "contactServicePersonnel",
+      'code': 2,
+      'btnColor': '#FF01CA48',
+      'btnWidth': 115.0,
+      'btnHeight': 35.0,
+    },
+    "3": {
+      "text": "Delete",
+      "type": "delete",
+      'code': 3,
+      'btnColor': '#FFFE4066',
+      'btnWidth': 98.0,
+      'btnHeight': 30.0,
+    },
+    "4": {
+      "text": "Evaluate",
+      "type": "evaluate",
+      'code': 4,
+      'btnColor': '#FF4161D0',
+      'btnWidth': 98.0,
+      'btnHeight': 30.0,
+    }
+  };
 }

+ 133 - 0
packages/cpt_services/lib/modules/services/history/history_page.dart

@@ -0,0 +1,133 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/garage_sale_rent_entity.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';
+import 'package:widgets/my_appbar.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../../constants_services.dart';
+import '../../../router/page/services_page_router.dart';
+import 'history_vm.dart';
+import '../../../components/status_card_item.dart';
+
+@RoutePage()
+class HistoryPage extends HookConsumerWidget {
+  const HistoryPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(const HistoryPageRoute());
+    } else {
+      appRouter.push(const HistoryPageRoute());
+    }
+  }
+
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final vm = ref.read(historyVmProvider.notifier);
+    final state = ref.watch(historyVmProvider);
+    // final appConfigState = ref.watch(appConfigServiceProvider)
+
+
+    useEffect(() {
+      // 组件挂载时执行 - 执行接口请求
+      Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+        Log.d("history_page 组件卸载时执行");
+      };
+    }, []);
+
+    return Scaffold(
+      // appBar: MyAppBar.appBar(
+      //   context,
+      //   "History",
+      //   backgroundColor: context.appColors.whiteBG,
+      // ),
+      backgroundColor: ColorUtils.string2Color("#F2F3F6"),
+      body: SizedBox(
+          width: double.infinity,
+          height: double.infinity,
+          child: EasyRefresh(
+            key: ValueKey('history'),
+            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  _buildHistoryItem(context, ref, state.list[index], vm,);
+                    },
+                    childCount: state.list.length,
+                  ),
+                )
+              ],
+            ),
+          ).marginOnly(left: 15,right: 15,top: 0,bottom: 15)
+      ),
+    );
+  }
+
+  Widget _buildHistoryItem(BuildContext context, WidgetRef ref, Map<String, dynamic> item, vm){
+    return Container(
+      margin: const EdgeInsets.only(top: 9),
+      width: double.infinity,
+      decoration: BoxDecoration(
+        color: context.appColors.whiteBG,
+        borderRadius: BorderRadius.circular(8),
+        boxShadow: [
+          BoxShadow(
+            color: ColorUtils.string2Color('#E5E5E5'),
+            offset: const Offset(0, 2),
+            blurRadius: 8,
+          ),
+        ],
+      ),
+      child: StausCardItem(
+        key: UniqueKey(),
+        cardHeight: 240.0,
+        serviceType: servicesConstants.servicesType['houseCleaning'],
+        serviceStatus: servicesConstants.servicesStatus['completed'],
+        serviceId: item['id'],
+        itemObj: item.cast<String, dynamic>(),
+        onClickColleciotn: (dynamic collectionValue) async {
+          Log.d("点击了喜欢按钮  --id:${item['id']}- $collectionValue");
+          int id = item['id'];
+          return await vm.handlerClickCollection(id, collectionValue);
+        },
+      ),
+    );
+  }
+}

+ 38 - 0
packages/cpt_services/lib/modules/services/history/history_state.dart

@@ -0,0 +1,38 @@
+import 'package:widgets/load_state_layout.dart';
+
+class HistoryState {
+  //页面 LoadView 状态的展示
+  LoadState loadingState;
+  String? errorMessage;
+
+  String? keyword;
+  bool? isLiked;
+  List<Map<String, dynamic>> list;
+
+
+  HistoryState({
+    this.loadingState = LoadState.State_Loading,
+    String? errorMessage,
+    this.keyword,
+    this.isLiked,
+    required this.list,
+  });
+
+  HistoryState copyWith({
+    LoadState? loadingState,
+    String? errorMessage,
+    String? keyword,
+    bool? isLiked,
+    Map<String, dynamic>? activeSortMap,
+    List<Map<String, dynamic>>? activeCateGoryList,
+    List<Map<String, dynamic>>? list,
+  }) {
+    return HistoryState(
+      loadingState: loadingState ?? this.loadingState,
+      errorMessage: errorMessage ?? this.errorMessage,
+      keyword: keyword ?? this.keyword,
+      isLiked: isLiked ?? this.isLiked,
+      list: list ?? this.list,
+    );
+  }
+}

+ 342 - 0
packages/cpt_services/lib/modules/services/history/history_vm.dart

@@ -0,0 +1,342 @@
+
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/garage_sale_rent_entity.dart';
+import 'package:domain/entity/newsfeed_detail_entity.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 '../../../respository/services_respository.dart';
+import '../../../router/page/services_page_router.dart';
+import 'history_state.dart';
+
+part 'history_vm.g.dart';
+
+@riverpod
+class HistoryVm extends _$HistoryVm {
+  late ServicesRespository servicesRespositoryInstance;
+  bool _needShowPlaceholder = false; //是否展示LoadingView
+  int _page = 1;  // 当前页
+  int _limit = 10; // 每页数量
+  int _count = 0; // 总条数
+
+  Map<String, dynamic> _queryParams = {
+    'category_id': null,
+    'keyword': null,
+    'is_liked': null,
+  };
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,  //允许刷新
+    controlFinishLoad: true,   //允许加载
+  );
+
+  HistoryState initState() {
+    return HistoryState(
+        list: []
+    );
+  }
+
+  @override
+  HistoryState build(){
+    // 引入数据仓库
+    // servicesRespositoryInstance = ref.read(commonGarageRespositoryProvider);
+    final state = initState();
+    Log.d("--------------------------build---------------------");
+
+    return state;
+  }
+
+
+  //刷新页面状态
+  void changeLoadingState(LoadState loadState, String? errorMsg) {
+    state = state.copyWith(
+        loadingState: loadState,
+        errorMessage: errorMsg
+    );
+  }
+
+  // 初始化页面数据
+  initPageData() {
+    Log.d("----home_service_vm-----initPageData   ${state.loadingState}");
+    onRefresh();
+  }
+
+  // 上拉加载 更多
+  Future loadMore() async {
+    Log.d("----home_service_vm-----loadMore");
+    _page++;
+    getListData();
+  }
+
+
+  // 下拉刷新
+  Future onRefresh() async {
+    // 当前pageView 页面正处于显示状态
+    Log.d("----forsale_vm-----onRefresh ");
+    // await Future.delayed(const Duration(seconds: 2));
+    _page = 1;
+    getListData();
+  }
+
+
+  // 手动进行刷新
+  Future triggerRefresh() async {
+    Log.d("trggerRefresh");
+    refreshController.callRefresh();
+  }
+
+  // 手动进行刷新
+  Future directRefresh() async {
+    state = state.copyWith(list:[]);
+    // 注意:由于 nestedscrollview 嵌套easyfresh 组件  refreshController.callRefresh() 自动刷新只能滚动顶部但是不会触发下拉刷新,这里调用是 用到了将其滚动到顶部的作用,进而刷新操作主动掉接口
+    // https://github.com/xuelongqy/flutter_easy_refresh/issues/692
+    refreshController.callRefresh();
+    refreshController.resetFooter();
+    _page = 1;
+    _needShowPlaceholder = true;
+    getListData();
+  }
+
+
+  // 重试请求
+  Future retryRequest() async {
+    _page = 1;
+    _needShowPlaceholder = true;
+    getListData();
+  }
+
+
+  // 获取list 列表数据
+  Future getListData<T>({bool? isLoadMore}) async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading, null);
+    }
+
+    List<Map<String, dynamic>> list = [
+      {
+        'id':1,
+        'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'title': 'House Cleaning Services',
+        'duration': 'Daily cleaning for 2 hours',
+        'totalPrice': 66,
+        'visit_time': '14 0ct 2024 15:00',
+        'order_time': '13 0ct 2024 12:00',
+        'status_text': 'In Progress',
+        'status_code': 2,
+        'company_name': 'HONG YE GROUP PTE LTD',
+      },
+      {
+        'id':2,
+        'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'title': 'House Cleaning Services',
+        'duration': 'Daily cleaning for 2 hours',
+        'totalPrice': 66,
+        'visit_time': '14 0ct 2024 15:00',
+        'order_time': '13 0ct 2024 12:00',
+        'status_text': 'In Progress',
+        'status_code': 2,
+        'company_name': 'HONG YE GROUP PTE LTD',
+      },
+      {
+        'id':3,
+        'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'title': 'House Cleaning Services',
+        'duration': 'Daily cleaning for 2 hours',
+        'totalPrice': 66,
+        'visit_time': '14 0ct 2024 15:00',
+        'order_time': '13 0ct 2024 12:00',
+        'status_text': 'In Progress',
+        'status_code': 3,
+        'company_name': 'HONG YE GROUP PTE LTD',
+      },
+    ];
+    handlerResultData(true, list:list);
+
+    // try {
+    //   //请求网络
+    //   Map<String, dynamic>  params = {
+    //     "type": 1,  // 类型(1=Sale,2=Rent)
+    //     "category_id": _queryParams['category_id'],
+    //     "keyword": _queryParams['keyword'],
+    //     "page": _page,
+    //     "limit": _limit,
+    //   };
+    //   Log.d("请求参数------$params");
+    //   final result = await servicesRespositoryInstance.fetchGarageDataList(params);
+    //   //校验成功失败
+    //   if (result.isSuccess) {
+    //     // handlerResultList((result.data as GarageSaleRentEntity).list as List<GarageSaleRentList>, isLoadMore ?? false);
+    //   } else {
+    //     String errorMessage = result.errorMsg!;
+    //     changeLoadingState(LoadState.State_Error, errorMessage);
+    //     ToastEngine.show(result.errorMsg ?? "Network Load Error");
+    //   }
+    // } catch (e) {
+    //   ToastEngine.show("Error: $e");
+    // }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+
+  }
+
+  handlerResultData(bool isList, {List<Map<String, dynamic>>? list, dynamic? data}){
+    Future.delayed(const Duration(seconds: 1)).then((value) {
+      if(isList){
+        // list 数据模式
+        if(list != null && list.isNotEmpty){
+          if(_page == 1){
+            state.list.clear();
+            state.list!.addAll(list);
+            refreshController.finishRefresh();
+            changeLoadingState(LoadState.State_Success, null);
+          }else {
+            final allList = state.list;
+            allList!.addAll(list);
+            state = state.copyWith(list: allList);
+            refreshController.finishLoad();
+          }
+        }else {
+          if(_page == 1){
+            state.list.clear();
+            changeLoadingState(LoadState.State_Empty, null);
+            refreshController.finishRefresh();
+          }else {
+            refreshController.finishLoad(IndicatorResult.noMore);
+          }
+        }
+      }else {
+        // 单个数据模式
+        if(data!=null){
+          if(_page == 1){
+            refreshController.finishRefresh();
+          }else{
+            refreshController.finishLoad();
+          }
+          changeLoadingState(LoadState.State_Success, null);
+        }else {
+          if(_page == 1){
+            refreshController.finishRefresh();
+          }else{
+            refreshController.finishLoad();
+          }
+          changeLoadingState(LoadState.State_Empty, null);
+        }
+      }
+    });
+  }
+
+  void handlerResultList(List<GarageSaleRentList>? list, bool isLoadMore) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      if (_page == 1) {
+        //刷新的方式
+        state.list!.clear();
+        state.list!.addAll(list.map((item) => item.toJson()).toList());
+        refreshController.finishRefresh();
+        //更新展示的状态
+        changeLoadingState(LoadState.State_Success, null);
+      } else {
+        //加载更多
+        final allList = state.list;
+        allList!.addAll(list.map((item) => item.toJson()).toList());
+        state = state.copyWith(list: allList);
+        refreshController.finishLoad();
+      }
+    } else {
+      if (_page == 1) {
+        //展示无数据的布局
+        state.list!.clear();
+        changeLoadingState(LoadState.State_Empty, null);
+        refreshController.finishRefresh();
+      } else {
+        //展示加载完成,没有更多数据了
+        if (_page == 1) {
+          //展示无数据的布局
+          state.list!.clear();
+          changeLoadingState(LoadState.State_Empty, null);
+          refreshController.finishRefresh();
+        } else {
+          //展示加载完成,没有更多数据了
+          if(state.list!.length == 0){
+            changeLoadingState(LoadState.State_Empty, null);
+            refreshController.finishLoad();
+          }else {
+            if(_needShowPlaceholder){
+              changeLoadingState(LoadState.State_Success, null);
+            }
+          }
+          //更新展示的状态
+          refreshController.finishLoad(IndicatorResult.noMore);
+        }
+      }
+    }
+  }
+
+
+  // 点击 收藏/取消收藏
+  Future<bool?> handlerClickCollection(int id, bool? isCollection) async{
+    try {
+      //请求网络
+      Map<String, dynamic>  params = {
+        "id": id,
+      };
+      Log.d("请求参数------$params");
+      final result = await servicesRespositoryInstance.fetchGarageColleciton(params);
+      //校验成功失败
+      if (result.isSuccess) {
+        // 修改 该id 的 liked 和 likes_count 字段
+        state.list!.forEach((Map<String, dynamic> element) {
+          GarageSaleRentList elementEntity = GarageSaleRentList.fromJson(element);
+
+          if(elementEntity.id == id){
+            elementEntity.liked = !elementEntity.liked!;
+            elementEntity.likesCount = elementEntity.liked! ? (elementEntity.likesCount! + 1) : (elementEntity.likesCount! - 1);
+          }
+
+          element = elementEntity.toJson();
+
+        });
+        return true;
+      } else {
+        String errorMessage = result.errorMsg!;
+        changeLoadingState(LoadState.State_Error, errorMessage);
+        ToastEngine.show(result.errorMsg ?? "Network Load Error");
+      }
+    } catch (e) {
+      Log.e("Error: $e");
+      ToastEngine.show("Error: $e");
+    }
+  }
+
+  // 设置当前的 _queryParams
+  setCurrentQueryParams(Map<String, dynamic> params){
+    _queryParams.addAll(params);
+  }
+
+  // 获取当前的 _queryParams
+  Map<String, dynamic> getCurrentQueryParams(String? key){
+    if(key!=null && key!.isNotEmpty){
+      return _queryParams[key];
+    }
+    return _queryParams;
+  }
+
+  // 去详情页面
+  void handlerGotoDetail({BuildContext? context, required int id, String type='forSale'}){
+    Log.d("去详情页面");
+    // appRouter.push(GaragesaleDetailPageRoute(id: id, type: 'forSale'));
+  }
+}

+ 25 - 0
packages/cpt_services/lib/modules/services/history/history_vm.g.dart

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

+ 102 - 51
packages/cpt_services/lib/modules/services/homeService/home_service_page.dart

@@ -11,6 +11,7 @@ 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:shared/utils/size_config.dart';
 import 'package:widgets/load_state_layout.dart';
 import 'package:widgets/my_button.dart';
 import 'package:widgets/my_load_image.dart';
@@ -22,6 +23,7 @@ import 'package:widgets/widget_export.dart';
 
 import '../../../router/page/services_page_router.dart';
 import 'home_service_vm.dart';
+import 'service_card_item.dart';
 
 @RoutePage()
 class HomeServicePage extends HookConsumerWidget {
@@ -62,48 +64,55 @@ class HomeServicePage extends HookConsumerWidget {
       backgroundColor: ColorUtils.string2Color("#F2F3F6"),
       body: SizedBox(
           width: double.infinity,
-          height: double.infinity,
-          child: EasyRefresh(
-            key: ValueKey('homeService'),
-            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  _buildHomeServiceItem(context, ref, state.list[index], vm).onTap((){
-                        vm.handlerGotoDetail(context: context, id: state.list[index]['id'], type: 'forSale');
-                      });
+          child: Column(
+            children: [
+              // sort and filter
+              _buildSortAndFilter(context, ref, vm, state),
+              Expanded(
+                child: EasyRefresh(
+                  key: ValueKey('homeService'),
+                  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();
                     },
-                    childCount: state.list.length,
+                    successSliverWidget:[
+                      SliverGrid(
+                        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
+                          crossAxisCount: 2, // 每行显示两个项目
+                          mainAxisSpacing: 15,
+                          crossAxisSpacing: 15,
+                          childAspectRatio: 172.5/170, // 宽高比
+                          // childAspectRatio:  172.5/170 * ScreenUtil.getAdapterSizeCtx(context, 172.5/170), // 宽高比
+                          // childAspectRatio:  172.5/170 * ((172.5/170).ap), // 宽高比
+                        ),
+                        delegate: SliverChildBuilderDelegate(
+                              (context, index) {
+                            return  _buildHomeServiceItem(context, ref, state.list[index], vm).onTap((){
+                              vm.handlerGotoDetail(context: context, id: state.list[index]['id'], serviceTypeCode: state.list[index]['service_type']);
+                            });
+                          },
+                          childCount: state.list.length,
+                        ),
+                      ),
+                    ],
                   ),
-                ),
-              ],
-            ),
-          ).marginOnly(left: 15,right: 15,top: 15,bottom: 15)
+                ).marginOnly(left: 15,right: 15,top: 0,bottom: 15),
+              ),
+            ],
+          )
       ),
     );
   }
@@ -111,6 +120,7 @@ class HomeServicePage extends HookConsumerWidget {
   Widget _buildHomeServiceItem(BuildContext context, WidgetRef ref, Map<String, dynamic> item, vm){
     return SizedBox(
       width: double.infinity,
+      // height: 170,
       child: Container(
         decoration: BoxDecoration(
           color: context.appColors.whiteBG,
@@ -129,20 +139,61 @@ class HomeServicePage extends HookConsumerWidget {
             mainAxisSize: MainAxisSize.max,
             children: [
               // 卡片头部(头像 标题 时间)
-              // Expanded(
-              //   child: GarageCard(
-              //       key: UniqueKey(),
-              //       itemObj: item.cast<String, dynamic>(),
-              //       onClickColleciotn: (dynamic collectionValue) async {
-              //         Log.d("点击了喜欢按钮  --id:${item['id']}- $collectionValue");
-              //         int id = item['id'];
-              //         return await vm.handlerClickCollection(id, collectionValue);
-              //       }
-              //   ),
-              // ),
+              Expanded(
+                child: HomeServiceCard(
+                    key: UniqueKey(),
+                    itemObj: item.cast<String, dynamic>(),
+                    onClickColleciotn: (dynamic collectionValue) async {
+                      Log.d("点击了喜欢按钮  --id:${item['id']}- $collectionValue");
+                      int id = item['id'];
+                      return await vm.handlerClickCollection(id, collectionValue);
+                    }
+                ),
+              ),
             ]
         ),
       ),
     );
   }
+
+  Widget _buildSortAndFilter(BuildContext context, WidgetRef ref, vm, state) {
+    String dropdownValue = 'Item1';
+    List<String> items = ['Item1', 'Item2', 'Item3'];
+
+    return Container(
+      padding: const EdgeInsets.only(left: 15, right: 15, top: 10, bottom: 10),
+      // color: Colors.red,
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.spaceBetween,
+        crossAxisAlignment: CrossAxisAlignment.center,
+        children: [
+          // 筛选
+          MyTextView(
+            "Sort",
+            fontSize: 15,
+            isFontRegular: true,
+            textAlign: TextAlign.center,
+          ).onTap(() {
+            Log.d("点击了筛选");
+
+          }),
+
+          // 排序
+          const MyAssetImage(
+            Assets.assetsYyHomeLogo,
+            width: 20.5,
+            height: 20.9,
+          ).onTap((){
+            Log.d("点击了filter");
+            vm.handlerClickFilterIcon(context);
+          }),
+        ]
+      ),
+    );
+  }
+
+  _buildCustomSort(BuildContext context, WidgetRef ref, vm, state) {
+    return Container();
+  }
+
 }

+ 172 - 28
packages/cpt_services/lib/modules/services/homeService/home_service_vm.dart

@@ -1,13 +1,18 @@
 
 import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
 import 'package:domain/entity/garage_sale_rent_entity.dart';
 import 'package:domain/entity/newsfeed_detail_entity.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:router/ext/auto_router_extensions.dart';
 import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/dialog/app_custom_dialog.dart';
 import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_checkbox_group.dart';
 import 'package:widgets/widget_export.dart';
 
 import '../../../respository/services_respository.dart';
@@ -24,6 +29,10 @@ class HomeServiceVm extends _$HomeServiceVm {
   int _limit = 10; // 每页数量
   int _count = 0; // 总条数
 
+
+  bool _isSingleSelect = true;
+  List<Map<String, dynamic>> _currentSelectedCategory = [];
+
   Map<String, dynamic> _queryParams = {
     'category_id': null,
     'keyword': null,
@@ -122,39 +131,38 @@ class HomeServiceVm extends _$HomeServiceVm {
       {
         'id':1,
         'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
-        'goods_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
         'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
         'title': 'House Cleaning Services',
-        'price': '\$66',
-        'isCollection': true,
-        'collection_num': '12',
-        'publisher': 'William Jefferson',
-        'publish_time': 'June 17,2016 at 7:23 p.m.',
-        'publisher_avator': Assets.communityCamera
+        'price': 66,
+        'unit': '/hr',
+        'liked': true,
+        'likes_count': 12,
+        'company_name': 'HONG YE GROUP PTE LTD',
       },
       {
-        'id':1,
+        'id':2,
         'service_type': 1,  // 0 房屋保洁 1 空调保洁  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': '\$66',
-        'isCollection': true,
-        'collection_num': '12',
-        'publisher': 'William Jefferson',
-        'publish_time': 'June 17,2016 at 7:23 p.m.',
-        'publisher_avator': Assets.communityCamera
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'title': 'Repair air Conditioner Services',
+        'price': 88,
+        'unit': '/unit',
+        'liked': false,
+        'likes_count': 10,
+        'company_name': 'HONG YE GROUP PTE LTD',
       },
       {
-        'id':1,
+        'id':3,
         'service_type': 2,  // 0 房屋保洁 1 空调保洁  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': '\$66',
-        'isCollection': true,
-        'collection_num': '12',
-        'publisher': 'William Jefferson',
-        'publish_time': 'June 17,2016 at 7:23 p.m.',
-        'publisher_avator': Assets.communityCamera
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'title': 'Air Conditioning Cleaning',
+        'price': 166,
+        'unit': '/hr',
+        'liked': true,
+        'likes_count': 1212,
+        'company_name': 'HONG YE GROUP PTE LTD',
       },
     ];
     handlerResultData(true, list:list);
@@ -280,6 +288,136 @@ class HomeServiceVm extends _$HomeServiceVm {
     }
   }
 
+  // 获取 homeservice 的分类选项
+  Future<List<Map<String, dynamic>>> getHomeServiceCategoryOptions() async{
+    ToastEngine.show("Loading...");
+    await Future.delayed(const Duration(seconds: 1));
+    ToastEngine.dismiss();
+    List<Map<String, dynamic>> serviceCategoryList = [
+      {
+        'id': 1,
+        'name': 'Part-Time Cleaning',
+      },
+      {
+        'id': 2,
+        'name': 'Move in/Out Cleaning',
+      },
+      {
+        'id': 3,
+        'name': 'Laundry Anddry Cleaning',
+      },
+      {
+        'id': 4,
+        'name': 'Sofa Cleaning',
+      },
+      {
+        'id': 5,
+        'name': 'Find BBQ Essentials',
+      },
+    ];
+
+    // 获取分类列表
+    // try {
+    //   // 加入有缓存 就取缓存
+    //   List<Map<String, dynamic>>? StorageCategoryList = SPUtil.getObjectList(
+    //       AppConstant.storageGarageCategoryList)?.cast<Map<String, dynamic>>();
+    //
+    //   if (StorageCategoryList != null && StorageCategoryList.isNotEmpty) {
+    //     Log.d("取StorageCategoryList 缓存: $StorageCategoryList ");
+    //     serviceCategoryList = StorageCategoryList;
+    //   } else {
+    //     Map<String, dynamic> params = {};
+    //     final result = await commonGarageRespositoryInstance
+    //         .fetchGarageCateGoryList(params);
+    //     if (result.isSuccess) {
+    //       final listJson = result.getListJson();
+    //       // 将 listJson 转换为 List<Map<String, dynamic>>
+    //       serviceCategoryList = (listJson as List?)
+    //           ?.map((item) => item as Map<String, dynamic>)
+    //           .toList() ?? [];
+    //       // 将 serviceCategoryList 存入缓存
+    //       Log.d("设置StorageCategoryList 缓存");
+    //       SPUtil.putObjectList(
+    //           AppConstant.storageGarageCategoryList, serviceCategoryList);
+    //     }
+    //   }
+    // } catch(error){
+    //
+    // }
+    return serviceCategoryList;
+  }
+
+  // 点击了filter icon
+  handlerClickFilterIcon(BuildContext context) async{
+    List<Map<String, dynamic>> serviceCategoryList = await getHomeServiceCategoryOptions();
+    // 显示弹框
+    handlerShowChooseGarageCategoryDialog(context, serviceCategoryList);
+
+  }
+
+  Future<void> handlerShowChooseGarageCategoryDialog(BuildContext context, List<Map<String, dynamic>> garageCategoryList) async{
+    await DialogEngine.show(
+        tag: "chooseGarageSaleCategory",
+        position: DialogPosition.center,
+        widget: AppCustomDialog(
+          message: '',
+          title: 'Choose a Category',
+          dialogWidth: MediaQuery.of(context).size.width * 0.8,
+          // contentBoxMaxHeight: 350,
+          // contentBoxMinHeight: 300,
+          isShowConfirmBtn: garageCategoryList!.length > 0 ? true: false,
+          confirmTxt: "Ok",
+          messageBuilder: (BuildContext context){
+            return Container(
+              color: context.appColors.textWhite,
+              child: Column(
+                mainAxisAlignment: MainAxisAlignment.start,
+                crossAxisAlignment: CrossAxisAlignment.start,
+                children: garageCategoryList!.length > 0 ? [
+                  MyCheckboxGroup(
+                      isSingleSelect: _isSingleSelect,
+                      labelStyle: const TextStyle(
+                        fontSize: 16,
+                        fontWeight: FontWeight.w500,
+                      ),
+                      items: garageCategoryList!,
+                      defaultSelectedItems: [],
+                      onChanged: (List<Map<String, dynamic>> selectedItems){
+                        Log.d("----MyCheckboxGroup onChanged  $selectedItems");
+                        _currentSelectedCategory = selectedItems;
+                      }
+                  )
+                ]: [
+                  Container(
+                    child: CircularProgressIndicator(
+                      strokeWidth: 3,
+                      valueColor: AlwaysStoppedAnimation(context.appColors.textDarkGray),
+                    ),
+                  )
+                ],
+              ),
+            );
+          },
+          isShowCancelBtn:false,
+          confirmAction: (){
+            // 点击了确定
+            Log.d("----点击了确定按钮");
+            int? categoryId;
+            if(_isSingleSelect){
+              if(_currentSelectedCategory.length > 0){
+                categoryId = _currentSelectedCategory[0]['id'];
+              }
+            }
+
+            setCurrentQueryParams({
+              "category_id": _queryParams?['categoryId'],
+            });
+            directRefresh();
+          },
+        )
+    );
+  }
+
 
   // 点击 收藏/取消收藏
   Future<bool?> handlerClickCollection(int id, bool? isCollection) async{
@@ -330,8 +468,14 @@ class HomeServiceVm extends _$HomeServiceVm {
   }
 
   // 去详情页面
-  void handlerGotoDetail({BuildContext? context, required int id, String type='forSale'}){
-    Log.d("去详情页面");
-    // appRouter.push(GaragesaleDetailPageRoute(id: id, type: 'forSale'));
+  void handlerGotoDetail({BuildContext? context, required int id, int serviceTypeCode= 0 }){
+    Log.d("去详情页面  $id  $serviceTypeCode");
+    if(serviceTypeCode == 0 || serviceTypeCode == 1){
+      // clean service  跳转到 clean 详情页
+      appRouter.push(ServiceCleanDetailPageRoute(id: id, serviceTypeCode: serviceTypeCode));
+    }else if(serviceTypeCode == 2){
+      // repair service 跳转到 repair 详情页
+      appRouter.push(ServiceRepairDetailPageRoute(id: id, serviceTypeCode: serviceTypeCode));
+    }
   }
 }

+ 1 - 1
packages/cpt_services/lib/modules/services/homeService/home_service_vm.g.dart

@@ -6,7 +6,7 @@ part of 'home_service_vm.dart';
 // RiverpodGenerator
 // **************************************************************************
 
-String _$homeServiceVmHash() => r'b0e883932798f7e0dfd413dd1cdadf758cd13173';
+String _$homeServiceVmHash() => r'183a53b6cf59798f91a9d58d32ac345427fe387c';
 
 /// See also [HomeServiceVm].
 @ProviderFor(HomeServiceVm)

+ 73 - 69
packages/cpt_services/lib/modules/services/homeService/service_card_item.dart

@@ -16,14 +16,15 @@ import '../../../constants_services.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,'
+// 'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
+// 'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+// 'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+// 'title': 'House Cleaning Services',
+// 'price': 66,
+// 'unit': '/hr',
+// 'liked': true,
+// 'likes_count': 1212,
+// 'company_name': 'HONG YE GROUP PTE LTD',
 
 
 class HomeServiceCard extends StatelessWidget {
@@ -41,7 +42,7 @@ class HomeServiceCard extends StatelessWidget {
     this.onClickColleciotn,
     double? cardHeight,
   }) : super(key: key) {
-    this.cardHeight ??= 214;
+    this.cardHeight ??= 170;
   }
 
   @override
@@ -49,13 +50,13 @@ class HomeServiceCard extends StatelessWidget {
     List? card_resources = itemObj.getValue<List>("resources", [])?? [];
     String card_img = card_resources.length>0? card_resources[0]:"";
     String card_title = itemObj.getValue("title", "");
+    final unit = itemObj.getValue("unit", "");
+
     int card_price = itemObj.getValue("price", "");
     String card_created_at = itemObj.getValue("created_at", "");
-    Map<String, dynamic>? card_account = itemObj.getValue<Map<String,dynamic>>("account", null);
-    String card_avatar = card_account?['avatar']?? "";
-    String card_publish_name = card_account?['name']?? "";
     bool card_liked = itemObj.getValue("liked", false);
     int card_likes_count = itemObj.getValue("likes_count", 0);
+    final company_name = itemObj.getValue("company_name", "");
     return Column(
       children: [
         // 图片
@@ -68,8 +69,8 @@ class HomeServiceCard extends StatelessWidget {
                 borderRadius: const BorderRadius.only(topLeft: Radius.circular(8), topRight: Radius.circular(8),),
                 child: MyLoadImage(
                   card_img,
-                  width: 166.5,
-                  height: 102.5.ap,
+                  width: 172.5,
+                  height: 80.5.ap,
                   isCircle: false,
                   fit: BoxFit.cover,
                 ),
@@ -79,19 +80,22 @@ class HomeServiceCard extends StatelessWidget {
         ),
         // 标题
         Padding(
-          padding: const EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 10),
+          padding: const EdgeInsets.only(left: 10, right: 10, top: 10,),
           child: Row(
             mainAxisAlignment: MainAxisAlignment.center,
             children: [
               Expanded(
-                child: MyTextView(
-                  card_title,
-                  maxLines: 1,
-                  isTextEllipsis: true,
-                  textAlign: TextAlign.left,
-                  textColor: context.appColors.textBlack,
-                  fontSize: 16,
-                  isFontRegular: true,
+                child: SizedBox(
+                  height: 30.ap,
+                  child: MyTextView(
+                    card_title,
+                    maxLines: 2,
+                    isTextEllipsis: true,
+                    textAlign: TextAlign.left,
+                    textColor: context.appColors.textBlack,
+                    fontSize: 15,
+                    isFontRegular: true,
+                  ),
                 ),
               ),
             ],
@@ -105,14 +109,39 @@ class HomeServiceCard extends StatelessWidget {
             crossAxisAlignment: CrossAxisAlignment.center,
             children: [
               Expanded(
-                child: MyTextView(
-                  '$card_price',
-                  maxLines: 1,
-                  isTextEllipsis: true,
-                  textAlign: TextAlign.start,
-                  textColor: ColorUtils.string2Color('#4161D0'),
-                  fontSize: 18,
-                  isFontMedium: true,
+                child: Row(
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  children: [
+                    MyTextView(
+                      'from',
+                      maxLines: 1,
+                      isTextEllipsis: true,
+                      textAlign: TextAlign.start,
+                      textColor: ColorUtils.string2Color('#666666'),
+                      fontSize: 11,
+                      isFontMedium: true,
+                    ),
+                    MyTextView(
+                      '\$$card_price',
+                      maxLines: 1,
+                      isTextEllipsis: true,
+                      textAlign: TextAlign.start,
+                      textColor: ColorUtils.string2Color('#4161D0'),
+                      fontSize: 15,
+                      isFontMedium: true,
+                      marginLeft: 2,
+                    ),
+                    MyTextView(
+                      '$unit',
+                      maxLines: 1,
+                      isTextEllipsis: true,
+                      textAlign: TextAlign.start,
+                      textColor: ColorUtils.string2Color('#666666'),
+                      fontSize: 11,
+                      isFontMedium: true,
+                    ),
+                  ],
                 ),
               ),
               // 动态的 收藏数
@@ -124,46 +153,21 @@ class HomeServiceCard extends StatelessWidget {
             ],
           ),
         ),
-        // 发布人信息
+        // 公司名称
         Expanded(
           child: Padding(
-              padding: EdgeInsets.only(left: 10, right: 10, bottom: 17.5),
-              child: Row(
-                  mainAxisAlignment: MainAxisAlignment.spaceAround,
-                  crossAxisAlignment: CrossAxisAlignment.center,
-                  children: [
-                    Expanded(
-                        child: Column(
-                            mainAxisAlignment: MainAxisAlignment.center,
-                            crossAxisAlignment: CrossAxisAlignment.start,
-                            mainAxisSize: MainAxisSize.max,
-                            children: [
-                              MyTextView(
-                                card_publish_name,
-                                maxLines: 1,
-                                isTextEllipsis: true,
-                                textAlign: TextAlign.start,
-                                marginLeft: 13,
-                                fontSize: 12,
-                                textColor: ColorUtils.string2Color('#2956B7'),
-                                isFontRegular: true,
-                              ),
-                              MyTextView(
-                                card_created_at,
-                                maxLines: 1,
-                                isTextEllipsis: true,
-                                textAlign: TextAlign.start,
-                                marginLeft: 13,
-                                marginTop: 5,
-                                fontSize: 10,
-                                textColor: context.appColors.textBlack,
-                                isFontRegular: true,
-                              ),
-                            ]
-                        )
-                    ),
-                  ]
-              )
+            padding: const EdgeInsets.only(bottom: 5),
+            child: MyTextView(
+              company_name,
+              maxLines: 1,
+              isTextEllipsis: true,
+              textAlign: TextAlign.start,
+              marginLeft: 13,
+              fontSize: 11,
+              textColor: ColorUtils.string2Color('#666666'),
+              alignment: Alignment.centerLeft,
+              isFontRegular: true,
+            ),
           ),
         )
       ],

+ 133 - 0
packages/cpt_services/lib/modules/services/inProgress/in_progress_page.dart

@@ -0,0 +1,133 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/garage_sale_rent_entity.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';
+import 'package:widgets/my_appbar.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../../constants_services.dart';
+import '../../../router/page/services_page_router.dart';
+import 'in_progress_vm.dart';
+import '../../../components/status_card_item.dart';
+
+@RoutePage()
+class InProgressPage extends HookConsumerWidget {
+  const InProgressPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(const InProgressPageRoute());
+    } else {
+      appRouter.push(const InProgressPageRoute());
+    }
+  }
+
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final vm = ref.read(inProgressVmProvider.notifier);
+    final state = ref.watch(inProgressVmProvider);
+    // final appConfigState = ref.watch(appConfigServiceProvider)
+
+
+    useEffect(() {
+      // 组件挂载时执行 - 执行接口请求
+      Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+        Log.d("inProgress_page 组件卸载时执行");
+      };
+    }, []);
+
+    return Scaffold(
+      // appBar: MyAppBar.appBar(
+      //   context,
+      //   "InProgress",
+      //   backgroundColor: context.appColors.whiteBG,
+      // ),
+      backgroundColor: ColorUtils.string2Color("#F2F3F6"),
+      body: SizedBox(
+          width: double.infinity,
+          height: double.infinity,
+          child: EasyRefresh(
+            key: ValueKey('inProgress'),
+            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  _buildInProgressItem(context, ref, state.list[index], vm,);
+                    },
+                    childCount: state.list.length,
+                  ),
+                )
+              ],
+            ),
+          ).marginOnly(left: 15,right: 15,top: 0,bottom: 15)
+      ),
+    );
+  }
+
+  Widget _buildInProgressItem(BuildContext context, WidgetRef ref, Map<String, dynamic> item, vm){
+    return Container(
+      margin: const EdgeInsets.only(top: 9),
+      width: double.infinity,
+      decoration: BoxDecoration(
+        color: context.appColors.whiteBG,
+        borderRadius: BorderRadius.circular(8),
+        boxShadow: [
+          BoxShadow(
+            color: ColorUtils.string2Color('#E5E5E5'),
+            offset: const Offset(0, 2),
+            blurRadius: 8,
+          ),
+        ],
+      ),
+      child: StausCardItem(
+          key: UniqueKey(),
+          cardHeight: 250.0,
+          serviceType: servicesConstants.servicesType['houseCleaning'],
+          serviceStatus: servicesConstants.servicesStatus['inProgress'],
+          serviceId: item['id'],
+          itemObj: item.cast<String, dynamic>(),
+          onClickColleciotn: (dynamic collectionValue) async {
+            // Log.d("点击了喜欢按钮  --id:${item['id']}- $collectionValue");
+            // int id = item['id'];
+            // return await vm.handlerClickCollection(id, collectionValue);
+          },
+      ),
+    );
+  }
+}

+ 38 - 0
packages/cpt_services/lib/modules/services/inProgress/in_progress_state.dart

@@ -0,0 +1,38 @@
+import 'package:widgets/load_state_layout.dart';
+
+class InProgressState {
+  //页面 LoadView 状态的展示
+  LoadState loadingState;
+  String? errorMessage;
+
+  String? keyword;
+  bool? isLiked;
+  List<Map<String, dynamic>> list;
+
+
+  InProgressState({
+    this.loadingState = LoadState.State_Loading,
+    String? errorMessage,
+    this.keyword,
+    this.isLiked,
+    required this.list,
+  });
+
+  InProgressState copyWith({
+    LoadState? loadingState,
+    String? errorMessage,
+    String? keyword,
+    bool? isLiked,
+    Map<String, dynamic>? activeSortMap,
+    List<Map<String, dynamic>>? activeCateGoryList,
+    List<Map<String, dynamic>>? list,
+  }) {
+    return InProgressState(
+      loadingState: loadingState ?? this.loadingState,
+      errorMessage: errorMessage ?? this.errorMessage,
+      keyword: keyword ?? this.keyword,
+      isLiked: isLiked ?? this.isLiked,
+      list: list ?? this.list,
+    );
+  }
+}

+ 342 - 0
packages/cpt_services/lib/modules/services/inProgress/in_progress_vm.dart

@@ -0,0 +1,342 @@
+
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/garage_sale_rent_entity.dart';
+import 'package:domain/entity/newsfeed_detail_entity.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 '../../../respository/services_respository.dart';
+import '../../../router/page/services_page_router.dart';
+import 'in_progress_state.dart';
+
+part 'in_progress_vm.g.dart';
+
+@riverpod
+class InProgressVm extends _$InProgressVm {
+  late ServicesRespository servicesRespositoryInstance;
+  bool _needShowPlaceholder = false; //是否展示LoadingView
+  int _page = 1;  // 当前页
+  int _limit = 10; // 每页数量
+  int _count = 0; // 总条数
+
+  Map<String, dynamic> _queryParams = {
+    'category_id': null,
+    'keyword': null,
+    'is_liked': null,
+  };
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,  //允许刷新
+    controlFinishLoad: true,   //允许加载
+  );
+
+  InProgressState initState() {
+    return InProgressState(
+        list: []
+    );
+  }
+
+  @override
+  InProgressState build(){
+    // 引入数据仓库
+    // servicesRespositoryInstance = ref.read(commonGarageRespositoryProvider);
+    final state = initState();
+    Log.d("--------------------------build---------------------");
+
+    return state;
+  }
+
+
+  //刷新页面状态
+  void changeLoadingState(LoadState loadState, String? errorMsg) {
+    state = state.copyWith(
+        loadingState: loadState,
+        errorMessage: errorMsg
+    );
+  }
+
+  // 初始化页面数据
+  initPageData() {
+    Log.d("----home_service_vm-----initPageData   ${state.loadingState}");
+    onRefresh();
+  }
+
+  // 上拉加载 更多
+  Future loadMore() async {
+    Log.d("----home_service_vm-----loadMore");
+    _page++;
+    getListData();
+  }
+
+
+  // 下拉刷新
+  Future onRefresh() async {
+    // 当前pageView 页面正处于显示状态
+    Log.d("----forsale_vm-----onRefresh ");
+    // await Future.delayed(const Duration(seconds: 2));
+    _page = 1;
+    getListData();
+  }
+
+
+  // 手动进行刷新
+  Future triggerRefresh() async {
+    Log.d("trggerRefresh");
+    refreshController.callRefresh();
+  }
+
+  // 手动进行刷新
+  Future directRefresh() async {
+    state = state.copyWith(list:[]);
+    // 注意:由于 nestedscrollview 嵌套easyfresh 组件  refreshController.callRefresh() 自动刷新只能滚动顶部但是不会触发下拉刷新,这里调用是 用到了将其滚动到顶部的作用,进而刷新操作主动掉接口
+    // https://github.com/xuelongqy/flutter_easy_refresh/issues/692
+    refreshController.callRefresh();
+    refreshController.resetFooter();
+    _page = 1;
+    _needShowPlaceholder = true;
+    getListData();
+  }
+
+
+  // 重试请求
+  Future retryRequest() async {
+    _page = 1;
+    _needShowPlaceholder = true;
+    getListData();
+  }
+
+
+  // 获取list 列表数据
+  Future getListData<T>({bool? isLoadMore}) async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading, null);
+    }
+
+    List<Map<String, dynamic>> list = [
+      {
+        'id':1,
+        'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'title': 'House Cleaning Services',
+        'duration': 'Daily cleaning for 2 hours',
+        'totalPrice': 66,
+        'visit_time': '14 0ct 2024 15:00',
+        'order_time': '13 0ct 2024 12:00',
+        'status_text': 'In Progress',
+        'status_code': 0,
+        'company_name': 'HONG YE GROUP PTE LTD',
+      },
+      {
+        'id':2,
+        'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'title': 'House Cleaning Services',
+        'duration': 'Daily cleaning for 2 hours',
+        'totalPrice': 66,
+        'visit_time': '14 0ct 2024 15:00',
+        'order_time': '13 0ct 2024 12:00',
+        'status_text': 'In Progress',
+        'status_code': 1,
+        'company_name': 'HONG YE GROUP PTE LTD',
+      },
+      {
+        'id':3,
+        'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'title': 'House Cleaning Services',
+        'duration': 'Daily cleaning for 2 hours',
+        'totalPrice': 66,
+        'visit_time': '14 0ct 2024 15:00',
+        'order_time': '13 0ct 2024 12:00',
+        'status_text': 'In Progress',
+        'status_code': 1,
+        'company_name': 'HONG YE GROUP PTE LTD',
+      },
+    ];
+    handlerResultData(true, list:list);
+
+    // try {
+    //   //请求网络
+    //   Map<String, dynamic>  params = {
+    //     "type": 1,  // 类型(1=Sale,2=Rent)
+    //     "category_id": _queryParams['category_id'],
+    //     "keyword": _queryParams['keyword'],
+    //     "page": _page,
+    //     "limit": _limit,
+    //   };
+    //   Log.d("请求参数------$params");
+    //   final result = await servicesRespositoryInstance.fetchGarageDataList(params);
+    //   //校验成功失败
+    //   if (result.isSuccess) {
+    //     // handlerResultList((result.data as GarageSaleRentEntity).list as List<GarageSaleRentList>, isLoadMore ?? false);
+    //   } else {
+    //     String errorMessage = result.errorMsg!;
+    //     changeLoadingState(LoadState.State_Error, errorMessage);
+    //     ToastEngine.show(result.errorMsg ?? "Network Load Error");
+    //   }
+    // } catch (e) {
+    //   ToastEngine.show("Error: $e");
+    // }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+
+  }
+
+  handlerResultData(bool isList, {List<Map<String, dynamic>>? list, dynamic? data}){
+    Future.delayed(const Duration(seconds: 1)).then((value) {
+      if(isList){
+        // list 数据模式
+        if(list != null && list.isNotEmpty){
+          if(_page == 1){
+            state.list.clear();
+            state.list!.addAll(list);
+            refreshController.finishRefresh();
+            changeLoadingState(LoadState.State_Success, null);
+          }else {
+            final allList = state.list;
+            allList!.addAll(list);
+            state = state.copyWith(list: allList);
+            refreshController.finishLoad();
+          }
+        }else {
+          if(_page == 1){
+            state.list.clear();
+            changeLoadingState(LoadState.State_Empty, null);
+            refreshController.finishRefresh();
+          }else {
+            refreshController.finishLoad(IndicatorResult.noMore);
+          }
+        }
+      }else {
+        // 单个数据模式
+        if(data!=null){
+          if(_page == 1){
+            refreshController.finishRefresh();
+          }else{
+            refreshController.finishLoad();
+          }
+          changeLoadingState(LoadState.State_Success, null);
+        }else {
+          if(_page == 1){
+            refreshController.finishRefresh();
+          }else{
+            refreshController.finishLoad();
+          }
+          changeLoadingState(LoadState.State_Empty, null);
+        }
+      }
+    });
+  }
+
+  void handlerResultList(List<GarageSaleRentList>? list, bool isLoadMore) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      if (_page == 1) {
+        //刷新的方式
+        state.list!.clear();
+        state.list!.addAll(list.map((item) => item.toJson()).toList());
+        refreshController.finishRefresh();
+        //更新展示的状态
+        changeLoadingState(LoadState.State_Success, null);
+      } else {
+        //加载更多
+        final allList = state.list;
+        allList!.addAll(list.map((item) => item.toJson()).toList());
+        state = state.copyWith(list: allList);
+        refreshController.finishLoad();
+      }
+    } else {
+      if (_page == 1) {
+        //展示无数据的布局
+        state.list!.clear();
+        changeLoadingState(LoadState.State_Empty, null);
+        refreshController.finishRefresh();
+      } else {
+        //展示加载完成,没有更多数据了
+        if (_page == 1) {
+          //展示无数据的布局
+          state.list!.clear();
+          changeLoadingState(LoadState.State_Empty, null);
+          refreshController.finishRefresh();
+        } else {
+          //展示加载完成,没有更多数据了
+          if(state.list!.length == 0){
+            changeLoadingState(LoadState.State_Empty, null);
+            refreshController.finishLoad();
+          }else {
+            if(_needShowPlaceholder){
+              changeLoadingState(LoadState.State_Success, null);
+            }
+          }
+          //更新展示的状态
+          refreshController.finishLoad(IndicatorResult.noMore);
+        }
+      }
+    }
+  }
+
+
+  // 点击 收藏/取消收藏
+  Future<bool?> handlerClickCollection(int id, bool? isCollection) async{
+    try {
+      //请求网络
+      Map<String, dynamic>  params = {
+        "id": id,
+      };
+      Log.d("请求参数------$params");
+      final result = await servicesRespositoryInstance.fetchGarageColleciton(params);
+      //校验成功失败
+      if (result.isSuccess) {
+        // 修改 该id 的 liked 和 likes_count 字段
+        state.list!.forEach((Map<String, dynamic> element) {
+          GarageSaleRentList elementEntity = GarageSaleRentList.fromJson(element);
+
+          if(elementEntity.id == id){
+            elementEntity.liked = !elementEntity.liked!;
+            elementEntity.likesCount = elementEntity.liked! ? (elementEntity.likesCount! + 1) : (elementEntity.likesCount! - 1);
+          }
+
+          element = elementEntity.toJson();
+
+        });
+        return true;
+      } else {
+        String errorMessage = result.errorMsg!;
+        changeLoadingState(LoadState.State_Error, errorMessage);
+        ToastEngine.show(result.errorMsg ?? "Network Load Error");
+      }
+    } catch (e) {
+      Log.e("Error: $e");
+      ToastEngine.show("Error: $e");
+    }
+  }
+
+  // 设置当前的 _queryParams
+  setCurrentQueryParams(Map<String, dynamic> params){
+    _queryParams.addAll(params);
+  }
+
+  // 获取当前的 _queryParams
+  Map<String, dynamic> getCurrentQueryParams(String? key){
+    if(key!=null && key!.isNotEmpty){
+      return _queryParams[key];
+    }
+    return _queryParams;
+  }
+
+  // 去详情页面
+  void handlerGotoDetail({BuildContext? context, required int id, String type='forSale'}){
+    Log.d("去详情页面");
+    // appRouter.push(GaragesaleDetailPageRoute(id: id, type: 'forSale'));
+  }
+}

+ 25 - 0
packages/cpt_services/lib/modules/services/inProgress/in_progress_vm.g.dart

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

+ 544 - 0
packages/cpt_services/lib/modules/services/service_clean_detail/service_clean_detail_page.dart

@@ -0,0 +1,544 @@
+import 'package:cpt_services/components/chooseAirConditionContent.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:domain/entity/garage_sale_rent_detail_entity.dart';
+import 'package:domain/entity/garage_sale_rent_entity.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:plugin_basic/provider/app_config/app_config_service.dart';
+import 'package:plugin_platform/engine/image/image_preview.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/size_config.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_like_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../../components/chooseHouseCleanContent.dart';
+import '../../../components/user_evaluate_card_item.dart';
+import '../../../constants_services.dart';
+import 'service_clean_detail_vm.dart';
+
+import '../../../router/page/services_page_router.dart';
+
+@RoutePage()
+class ServiceCleanDetailPage extends HookConsumerWidget {
+  final int id;
+  final int serviceTypeCode;
+
+  const ServiceCleanDetailPage({Key? key,@PathParam('id') required this.id, @PathParam('serviceTypeCode') required this.serviceTypeCode}) : super(key: key);
+  // 启动当前页面
+  static void startInstance({BuildContext? context, int? id, int? serviceTypeCode = 0}) {
+    if (context != null) {
+      context.router.push(ServiceCleanDetailPageRoute(id: id!, serviceTypeCode: serviceTypeCode!));
+    } else {
+      appRouter.push(ServiceCleanDetailPageRoute(id: id!, serviceTypeCode: serviceTypeCode!));
+    }
+  }
+
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final state = ref.watch(serviceCleanDetailVmProvider);
+    final vm = ref.read(serviceCleanDetailVmProvider.notifier);
+    final String pageTitle = 'Clean Details';
+    GlobalKey _likeButtonKey = GlobalKey<MyLikeButtonState>();
+
+    Map<String, dynamic>? detailInfo = state.datas?? null;
+    String title =  detailInfo?['title']?? '';
+
+    useEffect((){
+      vm.setInitPageData(id: id, serviceTypeCode: serviceTypeCode,);
+      // 组件挂载时执行 - 执行接口请求
+      Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+      };
+    },[]);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(
+        context,
+        pageTitle,
+        backgroundColor: context.appColors.backgroundWhite,
+      ),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: Column(
+        children: [
+          Expanded(
+            child: EasyRefresh(
+              controller: vm.refreshController,
+              // 上拉加载
+              onLoad: null,
+              // 下拉刷新
+              onRefresh: () async{
+                Log.d("----onRefresh");
+                vm.onRefresh();
+              },
+              // header: MaterialHeader(),
+              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, detailInfo,),
+                      // ),
+                      successSliverWidget: [
+                        SliverList(
+                          delegate: SliverChildBuilderDelegate(
+                                  (context, index){
+                                return detailInfo !=null ? _buildContentBox(context, ref, detailInfo): Container();
+                              },
+                              childCount: 1
+                          ),
+                        )
+                      ],
+                    ),
+                  ]
+              ),
+            ),
+          ),
+          // 底部联系信息
+          Visibility(
+            visible: state.loadingState == LoadState.State_Success,
+            child: detailInfo !=null ? _buildBottomConcatInfo(context, ref, _likeButtonKey,
+              detailInfo:detailInfo,
+            ): Container(),
+          )
+        ],
+      ),
+    );
+  }
+
+  Widget _buildContentBox(BuildContext context, WidgetRef ref, Map<String, dynamic> detailInfo) {
+    List<String> resources = detailInfo?['resources'].cast<String>()??[];
+    String title =  detailInfo?['title']??'';
+    double score = detailInfo?['score']?? 5.0;
+
+    final hilightStr = detailInfo?['hilightStr']??'On call, at the fastest 30 minutes to come to the door';
+
+
+    String description = detailInfo?['description']??'';
+    CarouselSliderController buttonCarouselController = CarouselSliderController();
+    return Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          // 图片
+          SizedBox(
+            width: SizeConfig().screenWidth,
+            height: 175,
+            child: CarouselSlider(
+              items: resources.map((resource) => MyLoadImage(
+                  resource,
+                  fit: BoxFit.cover, // 确保图片覆盖整个区域
+                  width: SizeConfig().screenWidth, // 确保图片宽度与屏幕宽度一致
+                  height: 175, // 确保图片高度与 CarouselSlider 高度一致
+                ).onTap((){
+                  ImagePreviewEngine.multipleImagePreview(
+                      context,
+                      resources,
+                      heroes: List.generate(resources.length, (index) => resources[index]),
+                      onLongPressAction: (url) {}
+                  );
+                })
+              ).toList(),
+              carouselController: buttonCarouselController,
+              options: CarouselOptions(
+                autoPlay: false,
+                enlargeCenterPage: true,
+                viewportFraction: 1,
+                initialPage: 0,
+              ),
+            ),
+          ),
+          Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              // 亮点
+              Padding(
+                padding: const EdgeInsets.only(left: 16, right: 16, top: 16,),
+                child: hilightStr.isNotEmpty? MyTextView( hilightStr, fontSize: 13, isFontRegular: true, textColor: context.appColors.textDarkGray,):const SizedBox.shrink(),
+              ),
+              //  评分
+              Column(
+                children: [
+                  Container(
+                    width: double.infinity,
+                    padding: const EdgeInsets.only(left: 16, right: 16, top: 16,bottom: 16),
+                    color: context.appColors.whiteBG,
+                    child: Column(
+                      crossAxisAlignment: CrossAxisAlignment.start,
+                      children: [
+                        MyTextView(
+                          "House Cleaning Services",
+                          textColor: context.appColors.textBlack,
+                          fontSize: 18,
+                          isFontBold: true,
+                          marginBottom: 5,
+                        ),
+                        Row(
+                          mainAxisAlignment: MainAxisAlignment.start,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          children: [
+                            AnimatedRatingStars(
+                              initialRating: score,
+                              onChanged: (rating) {
+                              },
+                              readOnly: true,
+                              displayRatingValue: true, // Display the rating value
+                              interactiveTooltips: true, // Allow toggling half-star state
+                              customFilledIcon: Icons.star,
+                              customHalfFilledIcon: Icons.star_half,
+                              customEmptyIcon: Icons.star_border,
+                              filledColor: ColorUtils.string2Color("#FF0000"),
+                              starSize: 16.5,
+                              animationDuration: const Duration(milliseconds: 0),
+                              animationCurve: Curves.easeInOut,
+                            ),
+                            MyTextView(
+                              "${score}",
+                              textColor: context.appColors.textBlack,
+                              fontSize: 16,
+                              isFontMedium: true,
+                              marginLeft: 15,
+                            ),
+                          ],
+                        ),
+                        MyTextView(
+                          "HONG YE GROUP PTE LTD",
+                          textColor: context.appColors.textDarkGray999,
+                          fontSize: 12,
+                          isFontRegular: true,
+                          marginTop: 10,
+                          marginBottom: 5,
+                        ),
+                      ],
+                    ),
+                  ),
+                ],
+              ),
+              //  室内清洁选择服务 或者 空调清洁选择空调选项
+              _buildSelectHouseCleanOrAirConditioner(context, ref, detailInfo),
+              // user Reviews
+              _buildUserReviews(context, ref, detailInfo),
+              // 介绍图
+              Padding(
+                padding: const EdgeInsets.only(left: 16, right: 16, top: 16),
+                child: Column(
+                  children: List.generate(6, (index) {
+                    // 创建一个映射来关联字符串和资源
+                    final Map<String, String> assetMap = {
+                      'serviceDetail01': Assets.serviceDetail01,
+                      'serviceDetail02': Assets.serviceDetail02,
+                      'serviceDetail03': Assets.serviceDetail03,
+                      'serviceDetail04': Assets.serviceDetail04,
+                      'serviceDetail05': Assets.serviceDetail05,
+                      'serviceDetail06': Assets.serviceDetail06,
+                    };
+                    String curDetailStr = 'serviceDetail0${(index + 1)}';
+                    return MyAssetImage(
+                      assetMap[curDetailStr]!,
+                      width: SizeConfig().screenWidth,
+                      fit: BoxFit.cover,
+                    );
+                  }).toList(),
+                )
+              ),
+            ],
+          ),
+        ]
+    );
+  }
+
+  Widget _buildUserReviews(BuildContext context, WidgetRef ref, Map<String, dynamic> detailInfo) {
+    final vm = ref.read(serviceCleanDetailVmProvider.notifier);
+    final state = ref.watch(serviceCleanDetailVmProvider);
+    int reviewScoreMaxNum = 3;
+    double reviewScoreItemCardHeight = 275;
+
+
+    Map<String, dynamic> item = {
+      'name': "User Sandy",
+      'avatar': "",
+      'score': 5.0,
+      'card_created_at': "14 Oct 2024 15:00",
+      'content': "This aunt is very careful because I haven't been home for three or four months. The house is",
+      'resources': [],
+    };
+    return Container(
+      width: context.screenSize.width! * reviewScoreMaxNum,
+      height: reviewScoreItemCardHeight,
+      child: Column(
+        children: [
+          Container(
+            padding: const EdgeInsets.only(left: 16, right: 16, top: 16,bottom: 0),
+            color: context.appColors.whiteBG,
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+                MyTextView(
+                  "User Reviews",
+                  textColor: context.appColors.textBlack,
+                  fontSize: 17,
+                  isFontBold: true,
+                ),
+                MyTextView(
+                  "All",
+                  textColor: context.appColors.textDarkGray999,
+                  fontSize: 14,
+                  isFontRegular: true,
+                ),
+              ],
+            ).onTap((){
+              vm.gotoUserReviewsPage(context, id, serviceTypeCode);
+            }),
+          ),
+          Expanded(
+            child: SingleChildScrollView(
+              scrollDirection: Axis.horizontal,
+              physics: const BouncingScrollPhysics(),
+              child: Container(
+                color: context.appColors.whiteBG,
+                padding: const EdgeInsets.only(left: 16, right: 16, top: 0,bottom: 16),
+                child: Row(
+                  children: List.generate(reviewScoreMaxNum, (index){
+                    return _buildserviceEvaluateListItem(context, ref, item, vm, 300);
+                  }),
+                ),
+              ),
+            ),
+          )
+        ],
+      ),
+    );
+  }
+
+  Widget _buildserviceEvaluateListItem(BuildContext context, WidgetRef ref, Map<String, dynamic> item, vm, double cardWidth){
+    String card_name = item?['name']?? "User Sandy";
+    String card_avatar = item?['avatar']?? "";
+    double card_score = item?['score']?? 5.0;
+    String card_created_at = item?['card_created_at']?? "14 Oct 2024 15:00";
+    String card_content = item.getValue("content", "This aunt is very careful because I haven't been home for three or four months. The house is");
+    List? card_resources =  ['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'];
+
+    // Log.d("card_resources  $card_resources");
+    return LayoutBuilder(
+      builder: (context, constraints) {
+        final maxWidth = constraints.maxWidth;
+        final maxHeight = constraints.maxHeight;
+        // Log.d("8933 $maxWidth  $maxHeight");
+        Log.d("55555 $cardWidth");
+        return Container(
+          margin: const EdgeInsets.only(top: 10),
+          width: cardWidth,
+          height: maxHeight,
+          child: Container(
+            margin: const EdgeInsets.only(right: 5),
+            decoration: BoxDecoration(
+              color: ColorUtils.string2Color("#F8F8F8"),
+              borderRadius: BorderRadius.circular(8),
+              // boxShadow: [
+              //   BoxShadow(
+              //     color: ColorUtils.string2Color('#E5E5E5'),
+              //     offset: const Offset(0, 2),
+              //     blurRadius: 8,
+              //   ),
+              // ],
+            ),
+            child: Padding(
+                padding: const EdgeInsets.only(left: 15, right:15,top: 17, bottom: 17),
+                child: UserEvaluateCardItem(
+                  key: UniqueKey(),
+                  useInSence: 'cleanDetailPage',
+                  name: card_name,
+                  avator: card_avatar,
+                  score: card_score,
+                  time: card_created_at,
+                  content: card_content,
+                  contentTextMaxLines: 2,
+                  imageUrls: card_resources,
+                ),
+              // child: Text("455"),
+            ),
+          ),
+        );
+      }
+    );
+  }
+
+  Widget _buildSelectHouseCleanOrAirConditioner(BuildContext context, WidgetRef ref, Map<String, dynamic> detailInfo) {
+    if(serviceTypeCode == servicesConstants.servicesType['houseCleaning']!['code']){
+      // 室内清洁
+      return Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Padding(
+            padding: const EdgeInsets.only(left: 16, right: 16,top: 10),
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.start,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+                const MyAssetImage(
+                  Assets.assetsYyHomeLogo,
+                  width: 15,
+                  height: 17.5,
+                ),
+                const SizedBox(
+                  width: 10,
+                ),
+                MyTextView(
+                  'Service',
+                  fontSize: 17,
+                  isFontBold: true,
+                  textColor: context.appColors.textBlack,
+                ),
+              ],
+            ),
+          ),
+          ChooseHouseCleanContent(id: id, serviceTypeCode: serviceTypeCode,),
+        ],
+      );
+    }else if(serviceTypeCode == servicesConstants.servicesType['airConditioner']!['code']){
+      // 空调清洁
+      // return ChooseAirConditionContent(id: id, serviceTypeCode: serviceTypeCode);
+    }else if(serviceTypeCode == servicesConstants.servicesType['repaire']!['code']){
+      // 维修
+      return SizedBox.shrink();
+    }
+    return SizedBox.shrink();
+  }
+
+  Widget _buildBottomConcatInfo(
+      BuildContext context,
+      WidgetRef ref,
+      likeButtonKey,
+      {
+        required Map<String, dynamic> detailInfo,
+      }
+      ) {
+    final vm = ref.read(serviceCleanDetailVmProvider.notifier);
+
+    int? likes_count =  detailInfo['likesCount']??0;
+    bool isLiked = detailInfo['isLiked']??false;
+    final contact = detailInfo['contact']??'+8613563656325';
+
+    final _likes_count = useState<int>(likes_count!);
+    final _isLiked = useState<bool>(isLiked);
+
+
+    return Container(
+      height: 50,
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.center,
+        mainAxisSize: MainAxisSize.max,
+        children: [
+          Container(
+            width: 122,
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.center,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              mainAxisSize: MainAxisSize.max,
+              children: [
+                Container(
+                  alignment: Alignment.center,
+                  padding: const EdgeInsets.only(left:5, right: 0,top:5,bottom: 5),
+                  child: Row(
+                    mainAxisAlignment: MainAxisAlignment.center,
+                    mainAxisSize: MainAxisSize.max,
+                    children: [
+                      MyLikeButton(
+                        key: likeButtonKey,
+                        isLiked: _isLiked.value,
+                        isCustomIcon: true,
+                        customIconUnActiveAssets: Assets.communityCollection,
+                        customIconActiveAssets: Assets.communityLikeActive,
+                        customIconWidth: 19,
+                        customIconHeight: 18,
+                        onLike: () async {
+                          Log.d('点击了like button');
+                          int id = detailInfo!['id'] as int;
+                          final isSuccess = await vm.handlerClickCollection(context, id, true);
+                          if(isSuccess!=null && isSuccess){
+                            // 成功
+                            if(_isLiked.value){
+                              Log.d("取消点赞");
+                              _likes_count.value--;
+                              _isLiked.value = false;
+                            }else {
+                              Log.d("点赞");
+                              _likes_count.value++;
+                              _isLiked.value = true;
+                            }
+                          }
+                        },
+                      ),
+                      MyTextView(
+                        "${_likes_count.value}",
+                        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,),
+                Row(
+                  children: [
+                    const SizedBox(width: 4,),
+                    const MyAssetImage(
+                      Assets.communityPhone,
+                      width: 21.5,
+                      height: 18,
+                    ).onTap((){
+                      vm.handlerClickMobile(context, contact);
+                    }),
+                    const SizedBox(width: 15,),
+                  ],
+                ),
+              ],
+            ).backgroundColor(context.appColors.textPrimary),
+          ),
+          Expanded(
+            child: Container(
+              color: context.appColors.redDefault,
+              child: Center(
+                child: MyTextView(
+                  'Book Now',
+                  textColor: context.appColors.textWhite,
+                  fontSize: 17,
+                  isFontMedium: true,
+                  textAlign: TextAlign.center,
+                ),
+              ),
+            ).onTap((){
+              vm.handlerClickBookNow(context, id: id , serviceTypeCode: serviceTypeCode);
+            }),
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 50 - 0
packages/cpt_services/lib/modules/services/service_clean_detail/service_clean_detail_state.dart

@@ -0,0 +1,50 @@
+import 'package:domain/entity/garage_sale_rent_detail_entity.dart';
+import 'package:widgets/load_state_layout.dart';
+
+class ServiceCleanDetailState {
+  //页面 LoadView 状态的展示
+  LoadState loadingState;
+  String? errorMessage;
+
+  // dialog 状态的展示
+  LoadState dialogLoadingState;
+  String? dialogErrorMessage;
+
+  final int? id;
+  final String? type;
+  double? totalPrice;
+  Map<String, dynamic>? datas;
+
+  ServiceCleanDetailState({
+    this.loadingState = LoadState.State_Loading,
+    this.errorMessage,
+    this.dialogLoadingState = LoadState.State_Loading,
+    this.dialogErrorMessage,
+    this.totalPrice = 0,
+    this.id,
+    this.type,
+    required this.datas,
+  });
+
+  ServiceCleanDetailState copyWith({
+    Map<String, dynamic>? datas,
+    LoadState? loadingState,
+    String? errorMessage,
+    LoadState? dialogLoadingState,
+    String? dialogErrorMessage,
+    double? totalPrice,
+    int? id,
+    String? type,
+  }) {
+    return ServiceCleanDetailState(
+      loadingState: loadingState ?? this.loadingState,
+      errorMessage: errorMessage ?? this.errorMessage,
+      dialogLoadingState: dialogLoadingState ?? this.dialogLoadingState,
+      dialogErrorMessage: dialogErrorMessage ?? this.dialogErrorMessage,
+      totalPrice: totalPrice?? this.totalPrice,
+      id: id ?? this.id,
+      type: type ?? this.type,
+      datas: datas?? this.datas,
+    );
+  }
+}

+ 433 - 0
packages/cpt_services/lib/modules/services/service_clean_detail/service_clean_detail_vm.dart

@@ -0,0 +1,433 @@
+
+import 'dart:async';
+
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:domain/entity/garage_sale_rent_detail_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:plugin_basic/basic_export.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/ext/auto_router_extensions.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+import 'package:widgets/dialog/dialog_content_wrap.dart';
+
+import '../../../components/chooseAirConditionContent.dart';
+import '../../../components/chooseAirConditionTitle.dart';
+import '../../../components/chooseHouseCleanContent.dart';
+import '../../../components/chooseHouseCleanTitle.dart';
+import '../../../constants_services.dart';
+import '../../../respository/services_respository.dart';
+import '../../../router/page/services_page_router.dart';
+import '../service_evaluate_list/service_evaluate_list_page.dart';
+import '../service_order_confirm/service_order_confirm_page.dart';
+import 'service_clean_detail_state.dart';
+
+part 'service_clean_detail_vm.g.dart';
+
+
+final _chooseHouseCleanDialogGlobalKey = GlobalKey<DialogContentWrapState>();
+final _chooseAirCleanDialogGlobalKey = GlobalKey<DialogContentWrapState>();
+
+
+@riverpod
+class ServiceCleanDetailVm extends _$ServiceCleanDetailVm {
+  late ServicesRespository serviceRespositoryInstance;
+
+  int _detailId = 0;
+  int _detailServiceTypeCode = 0;
+
+  bool _needShowPlaceholder = false; //是否展示LoadingView
+  int _page = 1;  // 当前页
+  int _limit = 10; // 每页数量
+  int _count = 0; // 总条数
+
+  int _totalnum = 130;
+
+  int totalnum = 50;
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,  //允许刷新
+    controlFinishLoad: true,   //允许加载
+  );
+
+  ServiceCleanDetailState initState() {
+    return ServiceCleanDetailState(
+      datas: null,
+    );
+  }
+
+  @override
+  ServiceCleanDetailState build(){
+    // 引入数据仓库
+    serviceRespositoryInstance = ref.read(servicesRespositoryProvider);
+    final state = initState();
+    Log.d("--------------------------build---------------------");
+
+    return state;
+  }
+
+
+  //刷新页面状态
+  void changeLoadingState(LoadState loadState, String? errorMsg) {
+    state = state.copyWith(
+        loadingState: loadState,
+        errorMessage: errorMsg
+    );
+  }
+
+  setInitPageData({required int id, required int serviceTypeCode}){
+    _detailId = id;
+    _detailServiceTypeCode = serviceTypeCode;
+  }
+  
+  // 初始化页面数据
+  initPageData() {
+    Log.d("----for_sale_vm-----initPageData   ${state.loadingState}");
+    onRefresh();
+  }
+
+  // 上拉加载 更多
+  Future loadMore() async {
+    _page++;
+    getDetailData();
+  }
+
+
+  // 下拉刷新
+  Future onRefresh() async {
+    _page = 1;
+    getDetailData();
+  }
+
+
+  // 手动进行刷新
+  Future triggerRefresh() async {
+    Log.d("trggerRefresh");
+    refreshController.callRefresh();
+  }
+
+  // 手动进行刷新
+  Future directRefresh() async {
+    state = state.copyWith(datas:null);
+    // 注意:由于 nestedscrollview 嵌套easyfresh 组件  refreshController.callRefresh() 自动刷新只能滚动顶部但是不会触发下拉刷新,这里调用是 用到了将其滚动到顶部的作用,进而刷新操作主动掉接口
+    // https://github.com/xuelongqy/flutter_easy_refresh/issues/692
+    refreshController.callRefresh();
+    refreshController.resetFooter();
+    _page = 1;
+    _needShowPlaceholder = true;
+    getDetailData();
+  }
+
+
+  // 重试请求
+  Future retryRequest() async {
+    _page = 1;
+    _needShowPlaceholder = true;
+    getDetailData();
+  }
+
+
+  Future getDetailData<T>() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading, null);
+    }
+
+
+    // 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: GarageSaleRentDetailEntity(
+    // ));
+    //
+    // changeLoadingState(LoadState.State_Success, null);
+    //
+    // refreshController.finishRefresh();
+    //
+    // // 最后赋值
+    // _needShowPlaceholder = false;
+
+
+    try {
+      Map<String, dynamic> params = {
+        "id": _detailId,
+      };
+      final result = await serviceRespositoryInstance.fetchServiceCleanDetailInfo(params);
+      if(result.isSuccess){
+        state = state.copyWith(datas: result.data as Map<String, dynamic>);
+        changeLoadingState(LoadState.State_Success, null);
+        refreshController.finishRefresh();
+      } else {
+        String errorMessage = result.errorMsg!;
+        changeLoadingState(LoadState.State_Error, errorMessage);
+        ToastEngine.show(result.errorMsg ?? "Network Load Error");
+      }
+    } catch (e) {
+      ToastEngine.show("Error: $e");
+    }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+  }
+
+
+  // 点击了底部的收藏按钮
+  Future<bool?> handlerClickCollection(BuildContext? context, int id, bool isCollection) async{
+    // var vm;
+    // if(_detailServiceTypeCode == "forSale"){
+    //   // vm = ref.read(forsaleVmProvider.notifier);
+    // }else if(_detailServiceTypeCode == "forRent"){
+    //   vm = ref.read(forrentVmProvider.notifier);
+    // }
+    // try {
+    //   final isSuccess = await vm.handlerClickCollection(id, isCollection);
+    //   return isSuccess;
+    // }catch(e){
+    //
+    // }
+  }
+
+  // 判断是whatsapp 还是 mobile  (通过 contact: whatsapp:+8617671757687)
+  String getContactType(String contactType){
+    // whatsapp:+8617671757687  截取:前面的字符串
+    String type = contactType.split(":")[0];
+    return type;
+  }
+
+  isContactWhatsApp(String contactType){
+    return contactType.contains("whatsapp");
+  }
+
+  isContactMobile(String contact){
+    return contact.contains("mobile");
+  }
+  String getContactNumber(String contact){
+    // whatsapp:+8617671757687     mobile:+8617671757687  截取:后面的字符串
+    String number = contact.split(":")[1];
+    return number;
+  }
+
+  // 点击 whatsapp
+  handlerClickWhatsapp(BuildContext? context,String contactType, String title, int price){
+    // 假设你有一个获取 WhatsApp 号码的方法
+    String whatsappNumber = getContactNumber(contactType); // 你需要实现这个方法
+    // 构建消息并进行编码
+    String message = Uri.encodeComponent("Hello, I am interested in your listing: $title for $price.");
+    // 打开WhatsApp
+    openWhatsApp(whatsappNumber, message);
+  }
+
+  // 打开whatsapp
+  openWhatsApp(String phoneNumber, String message) async {
+    final url = "https://wa.me/$phoneNumber/?text=${Uri.parse(message)}";
+    if (await  canLaunchUrl(Uri.parse(url))) {
+      await launchUrl(Uri.parse(url));
+    } else {
+      ToastEngine.show("Could not launch $url");
+      throw 'Could not launch $url';
+    }
+  }
+
+  // 点击 电话
+  handlerClickMobile(BuildContext? context,String contact){
+    String mobileNumber = getContactNumber(contact);
+    // 拨打电话
+    makePhoneCall(mobileNumber);
+  }
+
+  // 拨打电话
+  Future<void> makePhoneCall(String phoneNumber) async {
+    final Uri launchUri = Uri(
+      scheme: 'tel',
+      path: phoneNumber,
+    );
+
+    if (await canLaunchUrl(launchUri)) {
+      await launchUrl(launchUri);
+    } else {
+      ToastEngine.show("Could not launch $launchUri");
+      throw 'Could not launch $launchUri';
+    }
+  }
+
+  // 点击了 Book Now 按钮
+  handlerClickBookNow(BuildContext context, {int? id, int? serviceTypeCode} ){
+    final detailId = id ?? _detailId;
+    final detailServiceTypeCode = serviceTypeCode ?? _detailServiceTypeCode;   // 0 房屋清洁  1 空调清洁  2 维修
+    if(detailServiceTypeCode == servicesConstants.servicesType['houseCleaning']!['code']){
+      // 房屋清洁
+      handlerShowChooseCleanConditionerDialog(context);
+    }else if(detailServiceTypeCode == servicesConstants.servicesType['airConditioner']!['code']){
+      // 空调清洁
+      // 弹出选择 空调型号和数量的弹框
+      handlerShowChooseAirConditionerDialog(context);
+    }else if(detailServiceTypeCode == servicesConstants.servicesType['repaire']!['code']){
+      // 维修
+    }
+  }
+
+  // 显示选择 房屋清洁的弹框
+  handlerShowChooseCleanConditionerDialog(BuildContext context, ) async{
+    // 定时器
+    LoadState dialogState = LoadState.State_Loading;
+    DialogEngine.show(
+        tag: "chooseHouseClean",
+        position: DialogPosition.bottom,
+        widget: DialogContentWrap(
+          key: _chooseHouseCleanDialogGlobalKey,
+          loadingState: dialogState,
+          maxHeight: 315.5,
+          isShowConfirmBtn: true,
+          isShowCancelBtn: false,
+          title: "",
+          bottomBtnRadius:0,
+          bottomBtnSpace: 0,
+          topLeftRadius: 20.0,
+          topRightRadius: 20.0,
+          // bottomBtnSectionPadding: EdgeInsets.only(left: 20, right: 20, bottom: 20,top: 20),
+          yesBtnBg: context.appColors.redDefault,
+          noBtnBg: context.appColors.grayBgE9,
+          confirmTxt: "Confirm Selection",
+          cancelTxt: "Cancel",
+          // yesBtnTextStyle: TextStyle(
+          //   color: context.appColors.textWhite,
+          //   fontSize: 17,
+          //   fontWeight: FontWeight.w400
+          // ),
+          // noBtnTextStyle: TextStyle(
+          //   color: context.appColors.textBlack,
+          //   fontSize: 17,
+          //   fontWeight: FontWeight.w400
+          // ),
+          confirmAction: chooseHouseCleanConfirmFn,
+          cancelAction: chooseHouseCleanCancelFn,
+          titleBuilder: (context) {
+            return buildChooseHouseCleanTitle(context);
+          },
+          messageBuilder: (context) {
+            return buildChooseHouseCleanContent(context);
+          },
+        )
+    );
+
+    await Future.delayed(Duration(milliseconds: 1000));
+
+    _chooseHouseCleanDialogGlobalKey.currentState?.changeDialogLoadingState(LoadState.State_Success);    
+  }
+  // 显示 选择空调型号和数量的弹框
+  handlerShowChooseAirConditionerDialog(BuildContext context, ) async{
+    // 定时器
+    LoadState dialogState = LoadState.State_Loading;
+    DialogEngine.show(
+      tag: "chooseAirConditionClean",
+      position: DialogPosition.bottom,
+      widget: DialogContentWrap(
+        key: _chooseAirCleanDialogGlobalKey,
+        loadingState: dialogState,
+        maxHeight: 315.5,
+        isShowConfirmBtn: true,
+        isShowCancelBtn: false,
+        title: "",
+        bottomBtnRadius:0,
+        bottomBtnSpace: 0,
+        topLeftRadius: 20.0,
+        topRightRadius: 20.0,
+        // bottomBtnSectionPadding: EdgeInsets.only(left: 20, right: 20, bottom: 20,top: 20),
+        yesBtnBg: context.appColors.redDefault,
+        noBtnBg: context.appColors.grayBgE9,
+        confirmTxt: "Confirm Selection",
+        cancelTxt: "Cancel",
+        // yesBtnTextStyle: TextStyle(
+        //   color: context.appColors.textWhite,
+        //   fontSize: 17,
+        //   fontWeight: FontWeight.w400
+        // ),
+        // noBtnTextStyle: TextStyle(
+        //   color: context.appColors.textBlack,
+        //   fontSize: 17,
+        //   fontWeight: FontWeight.w400
+        // ),
+        confirmAction: chooseAirConditionConfirmFn,
+        cancelAction: chooseAirConditionCancelFn,
+        titleBuilder: (context) {
+          return buildChooseAirConditionTitle(context);
+        },
+        messageBuilder: (context) {
+          return buildChooseAirConditionContent(context);
+        },
+      )
+    );
+
+    await Future.delayed(Duration(milliseconds: 1000));
+
+    _chooseAirCleanDialogGlobalKey.currentState?.changeDialogLoadingState(LoadState.State_Success);
+  }
+
+  chooseHouseCleanConfirmFn(){
+    Log.d("点击了确定");
+    DialogEngine.dismiss(tag: "chooseHouseClean");
+    // 跳转到 订单确认页面
+    // appRouter.push(ServiceOrderConfirmPageRoute(id: _detailId, serviceTypeCode: _detailServiceTypeCode));
+    ServiceOrderConfirmPage.startInstance(id: _detailId, serviceTypeCode: _detailServiceTypeCode);
+  }
+
+  chooseHouseCleanCancelFn(){
+    Log.d("点击了取消");
+  }
+  
+  
+
+  chooseAirConditionConfirmFn(){
+    Log.d("点击了确定");
+    DialogEngine.dismiss(tag: "chooseAirConditionClean");
+    // 跳转到 订单确认页面
+    // appRouter.push(ServiceOrderConfirmPageRoute(id: _detailId, serviceTypeCode: _detailServiceTypeCode));
+    ServiceOrderConfirmPage.startInstance(id: _detailId, serviceTypeCode: _detailServiceTypeCode);
+  }
+
+  chooseAirConditionCancelFn(){
+    Log.d("点击了取消");
+  }
+
+  
+  Widget buildChooseHouseCleanTitle(BuildContext context){
+    return ChooseHouseCleanTitle(id: 0, serviceTypeCode: 0);
+  }
+
+  Widget buildChooseHouseCleanContent(BuildContext context){
+    return ChooseHouseCleanContent(id: 0, serviceTypeCode: 0,);
+  }
+
+  Widget buildChooseAirConditionTitle(BuildContext context){
+    return ChooseAirConditionTitle(id: 0, serviceTypeCode: 0);
+  }
+  
+  Widget buildChooseAirConditionContent(BuildContext context){
+    return ChooseAirConditionContent(id: 0, serviceTypeCode: 0,);
+  }
+
+  // 去 评价列表页面
+  gotoUserReviewsPage(BuildContext context, int id, int serviceTypeCode){
+    ServiceEvaluateListPage.startInstance(id:id, serviceTypeCode: serviceTypeCode);
+  }
+}

+ 27 - 0
packages/cpt_services/lib/modules/services/service_clean_detail/service_clean_detail_vm.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'service_clean_detail_vm.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$serviceCleanDetailVmHash() =>
+    r'b6fe1878337691f7676f1dff91046c4c7d9e266a';
+
+/// See also [ServiceCleanDetailVm].
+@ProviderFor(ServiceCleanDetailVm)
+final serviceCleanDetailVmProvider = AutoDisposeNotifierProvider<
+    ServiceCleanDetailVm, ServiceCleanDetailState>.internal(
+  ServiceCleanDetailVm.new,
+  name: r'serviceCleanDetailVmProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$serviceCleanDetailVmHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$ServiceCleanDetailVm = AutoDisposeNotifier<ServiceCleanDetailState>;
+// 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

+ 374 - 0
packages/cpt_services/lib/modules/services/service_evaluate_create/service_evaluate_create_page.dart

@@ -0,0 +1,374 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:domain/entity/garage_sale_rent_entity.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:plugin_platform/engine/image/image_nine_grid.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';
+import 'package:widgets/my_appbar.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:widgets/widget_export.dart';
+import 'package:widgets/shatter/form_require_text.dart';
+import 'package:widgets/my_text_field.dart';
+
+import '../../../constants_services.dart';
+import '../../../router/page/services_page_router.dart';
+import 'service_evaluate_create_vm.dart';
+
+@RoutePage()
+class ServiceEvaluateCreatePage extends HookConsumerWidget {
+  final int id;
+  final int serviceTypeCode;
+  const ServiceEvaluateCreatePage({
+    Key? key,
+    required this.id,
+    required this.serviceTypeCode,
+  }) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context, required int id, required int serviceTypeCode}) {
+    if (context != null) {
+      context.router.push(ServiceEvaluateCreatePageRoute(id: id, serviceTypeCode: serviceTypeCode));
+    } else {
+      appRouter.push(ServiceEvaluateCreatePageRoute(id:id, serviceTypeCode: serviceTypeCode));
+    }
+  }
+
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final vm = ref.read(serviceEvaluateCreateVmProvider.notifier);
+    final state = ref.watch(serviceEvaluateCreateVmProvider);
+    // final appConfigState = ref.watch(appConfigServiceProvider)
+
+    final _score = useState<double>(state.score?? 5.0);
+
+    useEffect(() {
+      // 组件挂载时执行 - 执行接口请求
+      Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+      };
+    }, []);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(
+        context,
+        "Evaluate",
+        backgroundColor: context.appColors.whiteBG,
+      ),
+      backgroundColor: ColorUtils.string2Color("#F2F3F6"),
+      body: Column(
+          children: [
+            Expanded(
+                child: SingleChildScrollView(
+                    scrollDirection: Axis.vertical,
+                    physics: const BouncingScrollPhysics(),
+                    clipBehavior: Clip.none,
+                    child: Column(
+                        children: [
+                          // notes
+                          Container(
+                              child: Column(
+                                  crossAxisAlignment: CrossAxisAlignment.start,
+                                  children: [
+                                    // 提示文字
+                                    Container(
+                                      child: MyTextView(
+                                        "Submit feedback or suggestions to encourage us to do better~",
+                                        textColor: context.appColors.textBlack,
+                                        fontSize: 15,
+                                        backgroundColor: context.appColors.whiteBG,
+                                        paddingTop: 16,
+                                        paddingBottom: 16,
+                                        paddingLeft: 10.5,
+                                        paddingRight: 10.5,
+                                      ),
+                                    ),
+                                    // 评价分
+                                    Container(
+                                      width: double.infinity,
+                                      padding: const EdgeInsets.only(left: 10.5, right: 10.5,top: 15,bottom: 15),
+                                      margin: const EdgeInsets.only(top: 5, bottom: 0),
+                                      color: context.appColors.whiteBG,
+                                      child: Column(
+                                        crossAxisAlignment: CrossAxisAlignment.start,
+                                        children: [
+                                          MyTextView(
+                                            "Score",
+                                            textColor: context.appColors.textBlack,
+                                            fontSize: 17,
+                                            isFontBold: true,
+                                            backgroundColor: context.appColors.whiteBG,
+                                            marginBottom: 10,
+                                          ),
+                                          Row(
+                                            mainAxisAlignment: MainAxisAlignment.start,
+                                            crossAxisAlignment: CrossAxisAlignment.center,
+                                            children: [
+                                              AnimatedRatingStars(
+                                                initialRating: _score.value,
+                                                onChanged: (rating) {
+                                                  Log.d("rating 变化 $rating");
+                                                  _score.value = rating;
+                                                  vm.setScore(context, _score.value);
+                                                },
+                                                readOnly: false,
+                                                displayRatingValue: true, // Display the rating value
+                                                interactiveTooltips: true, // Allow toggling half-star state
+                                                customFilledIcon: Icons.star,
+                                                customHalfFilledIcon: Icons.star_half,
+                                                customEmptyIcon: Icons.star_border,
+                                                starSize: 30.0,
+                                                animationDuration: const Duration(milliseconds: 300),
+                                                animationCurve: Curves.easeInOut,
+                                              ),
+                                              MyTextView(
+                                                "${_score.value}",
+                                                textColor: context.appColors.textBlack,
+                                                fontSize: 25,
+                                                isFontRegular: true,
+                                                backgroundColor: context.appColors.whiteBG,
+                                                marginLeft: 16,
+                                              ),
+                                            ],
+                                          ),
+                                        ],
+                                      ),
+                                    ),
+                                    // Notes
+                                    Container(
+                                      width: double.infinity,
+                                      padding: const EdgeInsets.only(left: 10.5, right: 10.5,top: 15,bottom: 15),
+                                      margin: const EdgeInsets.only(top: 5, bottom: 0),
+                                      color: context.appColors.whiteBG,
+                                      child: Column(
+                                        crossAxisAlignment: CrossAxisAlignment.start,
+                                        children: [
+                                          FormRequireText(
+                                            text: "Notes",
+                                            textColor: context.appColors.textBlack,
+                                            fontSize: 17,
+                                            fontWeight: FontWeight.bold,
+                                          ),
+                                          SizedBox(height: 16,),
+                                          _buildTextArea(context, ref, 'notes',),
+                                        ],
+                                      ),
+                                    ),
+                                    // upload Image
+                                    Container(
+                                      width: double.infinity,
+                                      padding: const EdgeInsets.only(left: 10.5, right: 10.5,top: 15,bottom: 15),
+                                      margin: const EdgeInsets.only(top: 5, bottom: 0),
+                                      color: context.appColors.whiteBG,
+                                      child: Column(
+                                        crossAxisAlignment: CrossAxisAlignment.start,
+                                        children: [
+                                          FormRequireText(
+                                            text: "Upload Image:",
+                                            textColor: context.appColors.textBlack,
+                                            fontSize: 17,
+                                            fontWeight: FontWeight.bold,
+                                          ),
+                                          MyTextView(
+                                            "(Up to 3 images can be uploaded)",
+                                            fontSize: 13,
+                                            isFontMedium: true,
+                                            textColor: context.appColors.textDarkGray999,
+                                            marginTop: 8.5,
+                                          ),
+                                          SizedBox(height: 16,),
+                                          // 选择图片上传 控件
+                                          _buildImageSelectCmp(context, ref,vm),
+                                        ],
+                                      ),
+                                    ),
+                                  ]
+                              )
+
+                          ),
+                        ]
+                    )
+                )
+            ),
+            // 底部 按钮
+            Container(
+              child: Row(
+                  children: [
+                    Expanded(
+                      child: MyButton(
+                        text: "Submit",
+                        radius: 0,
+                        minHeight: 50,
+                        backgroundColor: context.appColors.textPrimary,
+                        textColor: Colors.white,
+                        fontWeight: FontWeight.w500,
+                        fontSize: 16,
+                        onPressed: () {
+                          vm.handlerEvaluateSubmit(context);
+                        },
+                      ),
+                    ),
+                  ]
+              ),
+            )
+          ]
+      ),
+    );
+  }
+
+  /// textarea
+  Widget _buildTextArea(BuildContext context, WidgetRef ref, key){
+    return Container(
+      width: double.infinity,
+      height: 200,
+      padding: const EdgeInsets.all(15),
+      decoration: BoxDecoration(
+        color: context.appColors.authFiledBG,
+        borderRadius: BorderRadius.circular(5),
+      ),
+      child: _buildTextAreaLayout(
+        context,
+        ref,
+        key,
+      ),
+    );
+  }
+  /// 选择图片上传组件
+  Widget _buildImageSelectCmp(BuildContext context, WidgetRef ref,vm,){
+    final state = ref.watch(serviceEvaluateCreateVmProvider);
+    return ImageNineGrid(
+      isSelectEnable: true,
+      maxImages: 10,
+      spacing: 10,
+      aspectRatio: 108 / 80,
+      initialImages: state.imgList,
+      onImagesChanged: (list) {
+        vm.setImgList(list);
+      },
+    );
+  }
+
+  /// 多行输入框
+  Widget _buildTextAreaLayout(BuildContext context, ref, key){
+    final state = ref.watch(serviceEvaluateCreateVmProvider);
+    final vm = ref.read(serviceEvaluateCreateVmProvider.notifier);
+    final noteCount = useState(0);
+
+    return Stack(
+        children: [
+          TextField(
+            cursorColor: context.appColors.authFiledText,
+            cursorWidth: 1.5,
+            autofocus: false,
+            enabled: true,
+            maxLines: null,
+            focusNode: state.formData[key]!['focusNode'],
+            controller: state.formData[key]!['controller'],
+            decoration: InputDecoration(
+              isDense: true,
+              isCollapsed: true,
+              border: InputBorder.none,
+              hintText: state.formData[key]!['hintText'],
+              hintStyle: TextStyle(
+                color: context.appColors.authFiledHint,
+                fontSize: 16.0,
+                fontWeight: FontWeight.w500,
+              ),
+            ),
+            style: TextStyle(
+              color: context.appColors.authFiledText,
+              fontSize: 16.0,
+              fontWeight: FontWeight.w500,
+            ),
+            textInputAction: TextInputAction.done,
+            onSubmitted: (value) {
+              FocusScope.of(context).unfocus();
+            },
+            expands: true,
+            onChanged: (text) {
+              // 当文本改变时,更新字符数量
+              noteCount.value = text.length;
+            },
+          ),
+          Positioned(
+            bottom: 0.0,
+            right: 0.0,
+            child: Text(
+              S.current.characters(noteCount.value),
+              style: TextStyle(
+                color: context.appColors.textBlack,
+                fontSize: 15.0,
+              ),
+            ),
+          ),
+        ]
+    );
+  }
+
+  /// 单行输入框
+  Widget _buildInputLayout(
+      BuildContext context,
+      WidgetRef ref,
+      state,
+      String key,
+      {
+        double marginTop = 0,
+        bool? showRightIcon = false, //是否展示右侧的布局
+        Widget? rightWidget, //右侧的布局
+        TextInputType textInputType = TextInputType.text,
+        String? errorText,
+        bool obscureText = false,
+        bool enable = true,
+        TextInputAction textInputAction = TextInputAction.done,
+        Function? onSubmit,
+      }) {
+    // final state = ref.watch(garagesalePostVmProvider);
+    return IgnoreKeyboardDismiss(
+      child: MyTextField(
+        key,
+        state.formData[key]!['value'],
+        fillBackgroundColor: context.appColors.authFiledBG,
+        hintText: state.formData[key]!['hintText'],
+        hintStyle: TextStyle(
+          color: context.appColors.authFiledHint,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        controller: state.formData[key]!['controller'],
+        focusNode: state.formData[key]!['focusNode'],
+        margin: EdgeInsets.only(top: marginTop),
+        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
+        showDivider: false,
+        style: TextStyle(
+          color: context.appColors.authFiledText,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        inputType: textInputType,
+        textInputAction: textInputAction,
+        onSubmit: onSubmit,
+        cursorColor: context.appColors.authFiledText,
+        obscureText: obscureText,
+        errorText: errorText,
+        showLeftIcon: true,
+        showRightIcon: showRightIcon,
+        rightWidget: rightWidget,
+      ),
+    );
+  }
+} 

+ 66 - 0
packages/cpt_services/lib/modules/services/service_evaluate_create/service_evaluate_create_state.dart

@@ -0,0 +1,66 @@
+import 'package:flutter/cupertino.dart';
+import 'package:widgets/load_state_layout.dart';
+
+class ServiceEvaluateCreateState {
+  //页面 LoadView 状态的展示
+  LoadState loadingState;
+  String? errorMessage;
+
+  //notes 字段的错误信息展示
+  String? notesErrorText;
+
+  //表单的校验与数据
+  final Map<String, Map<String, dynamic>> formData;
+
+  // score 评分
+  final double? score;
+
+  // 描述
+  final String? notes;
+
+  // 选择的图片
+  final List<String> imgList;
+
+  Map<String, dynamic>? datas;
+
+  ServiceEvaluateCreateState({
+    required this.loadingState,
+    this.errorMessage,
+    this.notesErrorText,
+    formData,
+    this.notes,
+    this.score,
+    required this.imgList,
+    this.datas,
+  }): formData = formData ?? {
+    'notes': {
+      'value': '',
+      'controller': TextEditingController(),
+      'hintText': '',
+      'focusNode': FocusNode(),
+      'obsecure': false,
+    },
+  };
+
+  ServiceEvaluateCreateState copyWith({
+    LoadState? loadingState,
+    String? errorMessage,
+    String? notesErrorText,
+    Map<String, Map<String, dynamic>>? formData,
+    String? notes,
+    double? score,
+    List<String>? imgList,
+    Map<String, dynamic>? datas,
+  }) {
+    return ServiceEvaluateCreateState(
+      loadingState: loadingState ?? this.loadingState,
+      errorMessage: errorMessage ?? this.errorMessage,
+      notesErrorText: notesErrorText ?? this.notesErrorText,
+      formData: formData ?? this.formData,
+      notes: notes ?? this.notes,
+      score: score ?? this.score,
+      imgList: imgList ?? this.imgList,
+      datas: datas ?? this.datas,
+    );
+  }
+}

+ 479 - 0
packages/cpt_services/lib/modules/services/service_evaluate_create/service_evaluate_create_vm.dart

@@ -0,0 +1,479 @@
+
+import 'package:auto_route/src/route/page_route_info.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/garage_sale_rent_entity.dart';
+import 'package:domain/entity/newsfeed_detail_entity.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 '../../../respository/services_respository.dart';
+import '../../../router/page/services_page_router.dart';
+import '../service_evaluate_create_success/evaluate_create_success_page.dart';
+import 'service_evaluate_create_state.dart';
+
+part 'service_evaluate_create_vm.g.dart';
+
+@riverpod
+class ServiceEvaluateCreateVm extends _$ServiceEvaluateCreateVm {
+  late ServicesRespository servicesRespositoryInstance;
+  bool _needShowPlaceholder = false; //是否展示LoadingView
+  int _page = 1;  // 当前页
+  int _limit = 10; // 每页数量
+  int _count = 0; // 总条数
+
+  int? _id;
+  int? _serviceTypeCode;
+
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,  //允许刷新
+    controlFinishLoad: true,   //允许加载
+  );
+
+
+  ServiceEvaluateCreateState initState() {
+    return ServiceEvaluateCreateState(
+      loadingState: LoadState.State_Success,
+      notesErrorText: '',
+      formData: {
+        'notes': {
+          'value': '',
+          'controller': TextEditingController(),
+          'hintText': '',
+          'focusNode': FocusNode(),
+          'obsecure': false,
+        },
+      },
+      imgList: [],
+    );
+  }
+
+
+  @override
+  ServiceEvaluateCreateState build() {
+    // 初始化状态
+    ServiceEvaluateCreateState state = initState();
+
+    // 当前渲染完成后执行一次
+    WidgetsBinding.instance!.addPostFrameCallback((_) {
+      initListener(state);
+      ref.onDispose(() {
+        onDispose(state);
+      });
+    });
+
+    return state;
+  }
+
+
+  //刷新页面状态
+  void changeLoadingState(LoadState loadState, String? errorMsg) {
+    state = state.copyWith(
+        loadingState: loadState,
+        errorMessage: errorMsg
+    );
+  }
+
+  setInitPageData(BuildContext context, {int? id, int? serviceTypeCode,}) {
+    _id = id;
+    _serviceTypeCode = serviceTypeCode;
+  }
+  // 初始化页面数据
+  initPageData() {
+    // changeLoadingState(LoadState.State_Success, null);
+
+    // onRefresh();
+  }
+
+  // 上拉加载 更多
+  Future loadMore() async {
+    Log.d("----home_service_vm-----loadMore");
+    _page++;
+    getListData();
+  }
+
+
+  // 下拉刷新
+  Future onRefresh() async {
+    // 当前pageView 页面正处于显示状态
+    Log.d("----forsale_vm-----onRefresh ");
+    // await Future.delayed(const Duration(seconds: 2));
+    _page = 1;
+    getListData();
+  }
+
+
+  // 手动进行刷新
+  Future triggerRefresh() async {
+    Log.d("trggerRefresh");
+    refreshController.callRefresh();
+  }
+
+  // 手动进行刷新
+  Future directRefresh() async {
+    state = state.copyWith(datas:null);
+    // 注意:由于 nestedscrollview 嵌套easyfresh 组件  refreshController.callRefresh() 自动刷新只能滚动顶部但是不会触发下拉刷新,这里调用是 用到了将其滚动到顶部的作用,进而刷新操作主动掉接口
+    // https://github.com/xuelongqy/flutter_easy_refresh/issues/692
+    refreshController.callRefresh();
+    refreshController.resetFooter();
+    _page = 1;
+    _needShowPlaceholder = true;
+    getListData();
+  }
+
+
+  // 重试请求
+  Future retryRequest() async {
+    _page = 1;
+    _needShowPlaceholder = true;
+    getListData();
+  }
+
+
+  // 获取list 列表数据
+  Future getListData<T>({bool? isLoadMore}) async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading, null);
+    }
+
+    List<Map<String, dynamic>> list = [
+      {
+        'id':1,
+        'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'title': 'House Cleaning Services',
+        'duration': 'Daily cleaning for 2 hours',
+        'totalPrice': 66,
+        'visit_time': '14 0ct 2024 15:00',
+        'order_time': '13 0ct 2024 12:00',
+        'status_text': 'In Progress',
+        'status_code': 0,
+        'company_name': 'HONG YE GROUP PTE LTD',
+      },
+      {
+        'id':2,
+        'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'title': 'House Cleaning Services',
+        'duration': 'Daily cleaning for 2 hours',
+        'totalPrice': 66,
+        'visit_time': '14 0ct 2024 15:00',
+        'order_time': '13 0ct 2024 12:00',
+        'status_text': 'In Progress',
+        'status_code': 1,
+        'company_name': 'HONG YE GROUP PTE LTD',
+      },
+      {
+        'id':3,
+        'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'title': 'House Cleaning Services',
+        'duration': 'Daily cleaning for 2 hours',
+        'totalPrice': 66,
+        'visit_time': '14 0ct 2024 15:00',
+        'order_time': '13 0ct 2024 12:00',
+        'status_text': 'In Progress',
+        'status_code': 1,
+        'company_name': 'HONG YE GROUP PTE LTD',
+      },
+    ];
+    handlerResultData(true, list:list);
+
+    // try {
+    //   //请求网络
+    //   Map<String, dynamic>  params = {
+    //     "type": 1,  // 类型(1=Sale,2=Rent)
+    //     "category_id": _queryParams['category_id'],
+    //     "keyword": _queryParams['keyword'],
+    //     "page": _page,
+    //     "limit": _limit,
+    //   };
+    //   Log.d("请求参数------$params");
+    //   final result = await servicesRespositoryInstance.fetchGarageDataList(params);
+    //   //校验成功失败
+    //   if (result.isSuccess) {
+    //     // handlerResultList((result.data as GarageSaleRentEntity).list as List<GarageSaleRentList>, isLoadMore ?? false);
+    //   } else {
+    //     String errorMessage = result.errorMsg!;
+    //     changeLoadingState(LoadState.State_Error, errorMessage);
+    //     ToastEngine.show(result.errorMsg ?? "Network Load Error");
+    //   }
+    // } catch (e) {
+    //   ToastEngine.show("Error: $e");
+    // }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+
+  }
+
+  handlerResultData(bool isList, {List<Map<String, dynamic>>? list, dynamic? data}){
+    Future.delayed(const Duration(seconds: 1)).then((value) {
+      if(isList){
+        // list 数据模式
+        // if(list != null && list.isNotEmpty){
+        //   if(_page == 1){
+        //     state.list.clear();
+        //     state.list!.addAll(list);
+        //     refreshController.finishRefresh();
+        //     changeLoadingState(LoadState.State_Success, null);
+        //   }else {
+        //     final allList = state.list;
+        //     allList!.addAll(list);
+        //     state = state.copyWith(list: allList);
+        //     refreshController.finishLoad();
+        //   }
+        // }else {
+        //   if(_page == 1){
+        //     state.list.clear();
+        //     changeLoadingState(LoadState.State_Empty, null);
+        //     refreshController.finishRefresh();
+        //   }else {
+        //     refreshController.finishLoad(IndicatorResult.noMore);
+        //   }
+        // }
+      }else {
+        // 单个数据模式
+        if(data!=null){
+          if(_page == 1){
+            refreshController.finishRefresh();
+          }else{
+            refreshController.finishLoad();
+          }
+          changeLoadingState(LoadState.State_Success, null);
+        }else {
+          if(_page == 1){
+            refreshController.finishRefresh();
+          }else{
+            refreshController.finishLoad();
+          }
+          changeLoadingState(LoadState.State_Empty, null);
+        }
+      }
+    });
+  }
+
+
+  // 获取聚焦的node
+  FocusNode getFocusNode(Map<String, dynamic> formData, String keyStr) {
+    return formData![keyStr]!['focusNode'];
+  }
+
+  // 取消表单聚焦状态
+  void _dismissKeyboard({String? keyStr}) {
+    if(keyStr!.isNotEmpty){
+      Log.d("FeedbackCreateViewModel 取消单个表单 $keyStr 聚焦状态");
+      final FocusNode sigleItemFocusNode = state.formData[keyStr]!['focusNode'];
+      sigleItemFocusNode!.unfocus();
+    }else {
+      Log.d("FeedbackCreateViewModel 取消所有表单聚焦状态");
+      // 遍历 formData 的所有表单 然后逐一取消聚焦
+      Map<String, dynamic> formData = state.formData;
+      for(String key in formData.keys){
+        final FocusNode sigleItemFocusNode = formData[key]['focusNode'];
+        sigleItemFocusNode!.unfocus();
+      }
+    }
+  }
+
+  // 获取表单的值
+  dynamic _getFormFieldValue({List<String>? keys, String? keyStr}) {
+    if(keyStr!.isEmpty){
+      Log.d("获取指定集合的表单值");
+      if(keys!.isNotEmpty){
+        // 遍历keys获取指定多个 keys 的表单value
+        Map<String, dynamic> resultValueMap = {};
+        for (String itemStr in keys!) {
+          TextEditingController sigleItemController = state.formData[itemStr]!['controller'];
+          resultValueMap[itemStr] = sigleItemController!.text;
+        }
+        return resultValueMap;
+      }
+    }else if(keyStr!.isNotEmpty){
+      Log.d("获取单个表单值  ${keyStr}");
+      // 获取单个表单的value
+      final TextEditingController sigleItemController = state.formData[keyStr]!['controller'];
+      return sigleItemController!.text;
+    }
+  }
+
+  ///提交反馈
+  Future submitGaragesalePost(BuildContext context, {VoidCallback? sCallback, VoidCallback? fCallback}) async{
+    // Log.d("GaragesalePostPageState 提交表单");
+    // state = state.copyWith(titleErrorText: null, notesErrorText: null);
+    // _dismissKeyboard(keyStr: "");
+    //
+    // // 获取表单的值
+    // String  saleValue= state.saleSelectedOption ?? "";
+    // String  categoryValue= state.categorySelectedOption ?? "";
+    // int categoryId;
+    // String  contactTypeValue= state.contactTypeSelectedOption ?? "";
+    //
+    // String  titleValue= _getFormFieldValue(keyStr: 'title');
+    // String  contactInfoValue= _getFormFieldValue(keyStr: 'contactInfo');
+    // String  priceValue= _getFormFieldValue(keyStr: 'price');
+    // String  notesValue= _getFormFieldValue(keyStr: 'notes');
+    //
+    // Log.d('当前待提交的 sale:$saleValue category:$categoryValue contactTypeValue:$contactTypeValue title:$titleValue  contactInfoValue:$contactInfoValue  price:$priceValue  notes:$notesValue  imgList:${state.imgList}');
+    //
+    //
+    // if (Utils.isEmpty(saleValue)) {
+    //   ToastEngine.show('Sale is empty');
+    //   return;
+    // }
+    // if (Utils.isEmpty(categoryValue)) {
+    //   ToastEngine.show('Category is empty');
+    //   return;
+    // }else {
+    //   // 通过 categoryValue 值 来获取 categoryId
+    //   Log.d("_resCategoryOptionsList  $_resCategoryOptionsList");
+    //   try {
+    //     Map<String, dynamic>? categoryItem = _resCategoryOptionsList.firstWhere((element) => element['name'] == categoryValue, orElse: () => {'id': -1});
+    //     categoryId = categoryItem?['id'];
+    //   } catch (e) {
+    //     Log.d("未找到匹配的 categoryValue: $categoryValue");
+    //     ToastEngine.show('Category is invalid');
+    //     return;
+    //   }
+    // }
+    // if (Utils.isEmpty(contactTypeValue)) {
+    //   ToastEngine.show('ContactType is empty');
+    //   return;
+    // }
+    //
+    // if (Utils.isEmpty(titleValue)) {
+    //   state = state.copyWith(titleErrorText: 'Title is empty',);
+    //   return;
+    // }else {
+    //   state.titleErrorText = null;
+    //   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(notesValue)) {
+    //   state = state.copyWith(notesErrorText: 'notes is empty');
+    //   return;
+    // }else {
+    //   state = state.copyWith(notesErrorText: null);
+    // }
+    //
+    // String totalContactStr = '';
+    // if(contactTypeValue == 'Mobile Phone'){
+    //   totalContactStr = 'mobile:$contactInfoValue';
+    // }else if(contactTypeValue == 'WhatsApp'){
+    //   totalContactStr = 'whatsapp:$contactInfoValue';
+    // }else if(contactTypeValue == 'Email'){
+    //   totalContactStr = 'email:$contactInfoValue';
+    // }
+    //
+    // // 提交数据
+    // final resResult = await handlerSubmitPost(
+    //   type: saleValue == 'For Sale'? 1:2,
+    //   titleValue: titleValue,
+    //   categoryId: categoryId!,
+    //   priceValue: priceValue,
+    //   notesValue: notesValue,
+    //   contactValue: totalContactStr,
+    //   imgList: state.imgList,
+    // );
+
+    // if(resResult.isSuccess){
+      // // 更新用户信息
+      // UserConfigService.getInstance().refreshUserInfo();
+      //
+      // final communitVm = ref.read(communityVmProvider.notifier);
+      // communitVm.getCurrentPageViewVm(null).initPageData();
+      // // Navigator.pop(context);
+      // appRouter.maybePop();
+    // }
+  }
+
+  // Future<HttpResult<Object>> handlerSubmitPost({
+  //   required int type,
+  //   required String titleValue,
+  //   required int categoryId,
+  //   required String priceValue,
+  //   required String contactValue,
+  //   required String notesValue,
+  //   required List imgList,
+  // }) async{
+  //   final params = <String, dynamic>{
+  //     "type": type, // 1 = Sale  2 = Rent
+  //     "category_id": categoryId,
+  //     "title": titleValue,
+  //     "price": priceValue,
+  //     "notes": notesValue,
+  //     "contact": contactValue,  // string($例如:whatsapp:+8617671757687 或 mobile:+8617671757687)
+  //     "resources": imgList,
+  //   };
+  //   final result =  await commonGarageRespositoryInstance.fetchPublishGarage(params);
+  //   return result;
+  // }
+
+
+  //选中图片
+  void setImgList(List<String> list) {
+    state = state.copyWith(imgList: list);
+  }
+
+  //初始化监听
+  void initListener(ServiceEvaluateCreateState initState) {
+
+    // 获取表单的焦点节点
+    // final FocusNode focusNode = getFocusNode(state.formData, 'title');
+    //
+    // focusNode.addListener(() {
+    //   // 获取焦点的时候清空错误文本
+    //   if (focusNode.hasFocus) {
+    //     state = state.copyWith(contactInfoErrorText: null, titleErrorText: null, priceErrorText:null, notesErrorText: null);
+    //   }
+    // });
+  }
+
+  //销毁资源
+  void onDispose(ServiceEvaluateCreateState initState) {
+    // 获取表单的焦点节点
+    // final FocusNode focusNode = getFocusNode(state.formData, 'title');
+    // final FocusNode focusNodePrice = getFocusNode(state.formData, 'price');
+    // focusNode.dispose();
+    // focusNodePrice.dispose();
+
+    Log.d("GaragesalePostPageState 销毁 onDispose");
+  }
+
+  setScore(BuildContext context, double score){
+    state = state.copyWith(score: score);
+  }
+
+  handlerEvaluateSubmit(BuildContext context,){
+    Log.d("handlerEvaluateSubmit");
+    context.appRouter.pushAndPopUntil(EvaluateCreateSuccessPageRoute(id:0, serviceTypeCode: 0,), predicate: (Route<dynamic> route) {       // 根据具体条件返回 true 或 false
+      // Log.d("888 ${route.settings}");
+      return route.settings.name != 'ServiceEvaluateCreatePageRoute';
+    });
+  }
+}

+ 28 - 0
packages/cpt_services/lib/modules/services/service_evaluate_create/service_evaluate_create_vm.g.dart

@@ -0,0 +1,28 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'service_evaluate_create_vm.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$serviceEvaluateCreateVmHash() =>
+    r'14c7ca8f77a7d98040e48e69425c5f8fa1fdec7f';
+
+/// See also [ServiceEvaluateCreateVm].
+@ProviderFor(ServiceEvaluateCreateVm)
+final serviceEvaluateCreateVmProvider = AutoDisposeNotifierProvider<
+    ServiceEvaluateCreateVm, ServiceEvaluateCreateState>.internal(
+  ServiceEvaluateCreateVm.new,
+  name: r'serviceEvaluateCreateVmProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$serviceEvaluateCreateVmHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$ServiceEvaluateCreateVm
+    = AutoDisposeNotifier<ServiceEvaluateCreateState>;
+// 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

+ 74 - 0
packages/cpt_services/lib/modules/services/service_evaluate_create_success/evaluate_create_success_page.dart

@@ -0,0 +1,74 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/garage_sale_rent_entity.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';
+import 'package:widgets/my_appbar.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../../router/page/services_page_router.dart';
+
+@RoutePage()
+class EvaluateCreateSuccessPage extends HookConsumerWidget {
+  final int id;
+  final int serviceTypeCode;
+  const EvaluateCreateSuccessPage({
+    Key? key,
+    required this.id,
+    required this.serviceTypeCode,
+  }) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context, required int id, required int serviceTypeCode}) {
+    if (context != null) {
+      context.router.push( EvaluateCreateSuccessPageRoute(id:id, serviceTypeCode:serviceTypeCode));
+    } else {
+      appRouter.push(EvaluateCreateSuccessPageRoute(id:id, serviceTypeCode:serviceTypeCode));
+    }
+  }
+
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    // final appConfigState = ref.watch(appConfigServiceProvider)
+
+
+    useEffect(() {
+      // 组件挂载时执行 - 执行接口请求
+      // Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+        // Log.d("EvaluateCreateSuccess_page 组件卸载时执行");
+      };
+    }, []);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(
+        context,
+        "Evaluation",
+        backgroundColor: context.appColors.whiteBG,
+      ),
+      backgroundColor: ColorUtils.string2Color("#F2F3F6"),
+      body: Column(
+        children: [
+          MyTextView("Thank you"),
+        ],
+      )
+    );
+  }
+
+}

+ 146 - 0
packages/cpt_services/lib/modules/services/service_evaluate_list/service_evaluate_list_page.dart

@@ -0,0 +1,146 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/garage_sale_rent_entity.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';
+import 'package:widgets/my_appbar.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../../components/user_evaluate_card_item.dart';
+import '../../../constants_services.dart';
+import '../../../router/page/services_page_router.dart';
+import 'service_evaluate_list_vm.dart';
+import '../../../components/status_card_item.dart';
+
+@RoutePage()
+class ServiceEvaluateListPage extends HookConsumerWidget {
+  final int id;
+  final int serviceTypeCode;
+  const ServiceEvaluateListPage({
+    Key? key,
+    required this.id,
+    required this.serviceTypeCode,
+  }) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context, required int id, required int serviceTypeCode}) {
+    if (context != null) {
+      context.router.push( ServiceEvaluateListPageRoute(id:id, serviceTypeCode:serviceTypeCode));
+    } else {
+      appRouter.push(ServiceEvaluateListPageRoute(id:id, serviceTypeCode:serviceTypeCode));
+    }
+  }
+
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final vm = ref.read(serviceEvaluateListVmProvider.notifier);
+    final state = ref.watch(serviceEvaluateListVmProvider);
+    // final appConfigState = ref.watch(appConfigServiceProvider)
+
+
+    useEffect(() {
+      // 组件挂载时执行 - 执行接口请求
+      Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+        Log.d("serviceEvaluateList_page 组件卸载时执行");
+      };
+    }, []);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(
+        context,
+        "serviceEvaluateList",
+        backgroundColor: context.appColors.whiteBG,
+      ),
+      backgroundColor: ColorUtils.string2Color("#F2F3F6"),
+      body: SizedBox(
+          width: double.infinity,
+          height: double.infinity,
+          child: EasyRefresh(
+            key: ValueKey('serviceEvaluateList'),
+            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  _buildserviceEvaluateListItem(context, ref, state.list[index], vm,);
+                    },
+                    childCount: state.list.length,
+                  ),
+                )
+              ],
+            ),
+          ).marginOnly(left: 15,right: 15,top: 0,bottom: 15)
+      ),
+    );
+  }
+
+  Widget _buildserviceEvaluateListItem(BuildContext context, WidgetRef ref, Map<String, dynamic> item, vm){
+    String card_name = item?['name']?? "User Sandy";
+    String card_avatar = item?['avatar']?? "";
+    double card_score = item?['score']?? 5.0;
+    String card_created_at = item?['card_created_at']?? "14 Oct 2024 15:00";
+    String card_content = item.getValue("content", "This aunt is very careful because I haven't been home for three or four months. The house is");
+    List? card_resources = item.getValue<List>("resources", [])?? [];
+    return Container(
+      margin: const EdgeInsets.only(top: 10),
+      width: double.infinity,
+      decoration: BoxDecoration(
+        color: context.appColors.whiteBG,
+        borderRadius: BorderRadius.circular(8),
+        boxShadow: [
+          BoxShadow(
+            color: ColorUtils.string2Color('#E5E5E5'),
+            offset: const Offset(0, 2),
+            blurRadius: 8,
+          ),
+        ],
+      ),
+      child: Padding(
+        padding: const EdgeInsets.only(left: 15, right:15,top: 17, bottom: 17),
+        child: UserEvaluateCardItem(
+          key: UniqueKey(),
+          name: card_name,
+          avator: card_avatar,
+          score: card_score,
+          time: card_created_at,
+          content: card_content,
+          contentTextMaxLines: 9999,
+          imageUrls: card_resources,
+        ),
+      ),
+    );
+  }
+}

+ 38 - 0
packages/cpt_services/lib/modules/services/service_evaluate_list/service_evaluate_list_state.dart

@@ -0,0 +1,38 @@
+import 'package:widgets/load_state_layout.dart';
+
+class ServiceEvaluateListState {
+  //页面 LoadView 状态的展示
+  LoadState loadingState;
+  String? errorMessage;
+
+  String? keyword;
+  bool? isLiked;
+  List<Map<String, dynamic>> list;
+
+
+  ServiceEvaluateListState({
+    this.loadingState = LoadState.State_Loading,
+    String? errorMessage,
+    this.keyword,
+    this.isLiked,
+    required this.list,
+  });
+
+  ServiceEvaluateListState copyWith({
+    LoadState? loadingState,
+    String? errorMessage,
+    String? keyword,
+    bool? isLiked,
+    Map<String, dynamic>? activeSortMap,
+    List<Map<String, dynamic>>? activeCateGoryList,
+    List<Map<String, dynamic>>? list,
+  }) {
+    return ServiceEvaluateListState(
+      loadingState: loadingState ?? this.loadingState,
+      errorMessage: errorMessage ?? this.errorMessage,
+      keyword: keyword ?? this.keyword,
+      isLiked: isLiked ?? this.isLiked,
+      list: list ?? this.list,
+    );
+  }
+}

+ 342 - 0
packages/cpt_services/lib/modules/services/service_evaluate_list/service_evaluate_list_vm.dart

@@ -0,0 +1,342 @@
+
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/garage_sale_rent_entity.dart';
+import 'package:domain/entity/newsfeed_detail_entity.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 '../../../respository/services_respository.dart';
+import '../../../router/page/services_page_router.dart';
+import 'service_evaluate_list_state.dart';
+
+part 'service_evaluate_list_vm.g.dart';
+
+@riverpod
+class ServiceEvaluateListVm extends _$ServiceEvaluateListVm {
+  late ServicesRespository servicesRespositoryInstance;
+  bool _needShowPlaceholder = false; //是否展示LoadingView
+  int _page = 1;  // 当前页
+  int _limit = 10; // 每页数量
+  int _count = 0; // 总条数
+
+  Map<String, dynamic> _queryParams = {
+    'category_id': null,
+    'keyword': null,
+    'is_liked': null,
+  };
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,  //允许刷新
+    controlFinishLoad: true,   //允许加载
+  );
+
+  ServiceEvaluateListState initState() {
+    return ServiceEvaluateListState(
+        list: []
+    );
+  }
+
+  @override
+  ServiceEvaluateListState build(){
+    // 引入数据仓库
+    // servicesRespositoryInstance = ref.read(commonGarageRespositoryProvider);
+    final state = initState();
+    Log.d("--------------------------build---------------------");
+
+    return state;
+  }
+
+
+  //刷新页面状态
+  void changeLoadingState(LoadState loadState, String? errorMsg) {
+    state = state.copyWith(
+        loadingState: loadState,
+        errorMessage: errorMsg
+    );
+  }
+
+  // 初始化页面数据
+  initPageData() {
+    Log.d("----home_service_vm-----initPageData   ${state.loadingState}");
+    onRefresh();
+  }
+
+  // 上拉加载 更多
+  Future loadMore() async {
+    Log.d("----home_service_vm-----loadMore");
+    _page++;
+    getListData();
+  }
+
+
+  // 下拉刷新
+  Future onRefresh() async {
+    // 当前pageView 页面正处于显示状态
+    Log.d("----forsale_vm-----onRefresh ");
+    // await Future.delayed(const Duration(seconds: 2));
+    _page = 1;
+    getListData();
+  }
+
+
+  // 手动进行刷新
+  Future triggerRefresh() async {
+    Log.d("trggerRefresh");
+    refreshController.callRefresh();
+  }
+
+  // 手动进行刷新
+  Future directRefresh() async {
+    state = state.copyWith(list:[]);
+    // 注意:由于 nestedscrollview 嵌套easyfresh 组件  refreshController.callRefresh() 自动刷新只能滚动顶部但是不会触发下拉刷新,这里调用是 用到了将其滚动到顶部的作用,进而刷新操作主动掉接口
+    // https://github.com/xuelongqy/flutter_easy_refresh/issues/692
+    refreshController.callRefresh();
+    refreshController.resetFooter();
+    _page = 1;
+    _needShowPlaceholder = true;
+    getListData();
+  }
+
+
+  // 重试请求
+  Future retryRequest() async {
+    _page = 1;
+    _needShowPlaceholder = true;
+    getListData();
+  }
+
+
+  // 获取list 列表数据
+  Future getListData<T>({bool? isLoadMore}) async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading, null);
+    }
+
+    List<Map<String, dynamic>> list = [
+      {
+        'id':1,
+        'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['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'],
+        'title': 'House Cleaning Services',
+        'duration': 'Daily cleaning for 2 hours',
+        'totalPrice': 66,
+        'visit_time': '14 0ct 2024 15:00',
+        'order_time': '13 0ct 2024 12:00',
+        'status_text': 'In Progress',
+        'status_code': 2,
+        'company_name': 'HONG YE GROUP PTE LTD',
+      },
+      {
+        'id':2,
+        'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'title': 'House Cleaning Services',
+        'duration': 'Daily cleaning for 2 hours',
+        'totalPrice': 66,
+        'visit_time': '14 0ct 2024 15:00',
+        'order_time': '13 0ct 2024 12:00',
+        'status_text': 'In Progress',
+        'status_code': 2,
+        'company_name': 'HONG YE GROUP PTE LTD',
+      },
+      {
+        'id':3,
+        'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'title': 'House Cleaning Services',
+        'duration': 'Daily cleaning for 2 hours',
+        'totalPrice': 66,
+        'visit_time': '14 0ct 2024 15:00',
+        'order_time': '13 0ct 2024 12:00',
+        'status_text': 'In Progress',
+        'status_code': 3,
+        'company_name': 'HONG YE GROUP PTE LTD',
+      },
+    ];
+    handlerResultData(true, list:list);
+
+    // try {
+    //   //请求网络
+    //   Map<String, dynamic>  params = {
+    //     "type": 1,  // 类型(1=Sale,2=Rent)
+    //     "category_id": _queryParams['category_id'],
+    //     "keyword": _queryParams['keyword'],
+    //     "page": _page,
+    //     "limit": _limit,
+    //   };
+    //   Log.d("请求参数------$params");
+    //   final result = await servicesRespositoryInstance.fetchGarageDataList(params);
+    //   //校验成功失败
+    //   if (result.isSuccess) {
+    //     // handlerResultList((result.data as GarageSaleRentEntity).list as List<GarageSaleRentList>, isLoadMore ?? false);
+    //   } else {
+    //     String errorMessage = result.errorMsg!;
+    //     changeLoadingState(LoadState.State_Error, errorMessage);
+    //     ToastEngine.show(result.errorMsg ?? "Network Load Error");
+    //   }
+    // } catch (e) {
+    //   ToastEngine.show("Error: $e");
+    // }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+
+  }
+
+  handlerResultData(bool isList, {List<Map<String, dynamic>>? list, dynamic? data}){
+    Future.delayed(const Duration(seconds: 1)).then((value) {
+      if(isList){
+        // list 数据模式
+        if(list != null && list.isNotEmpty){
+          if(_page == 1){
+            state.list.clear();
+            state.list!.addAll(list);
+            refreshController.finishRefresh();
+            changeLoadingState(LoadState.State_Success, null);
+          }else {
+            final allList = state.list;
+            allList!.addAll(list);
+            state = state.copyWith(list: allList);
+            refreshController.finishLoad();
+          }
+        }else {
+          if(_page == 1){
+            state.list.clear();
+            changeLoadingState(LoadState.State_Empty, null);
+            refreshController.finishRefresh();
+          }else {
+            refreshController.finishLoad(IndicatorResult.noMore);
+          }
+        }
+      }else {
+        // 单个数据模式
+        if(data!=null){
+          if(_page == 1){
+            refreshController.finishRefresh();
+          }else{
+            refreshController.finishLoad();
+          }
+          changeLoadingState(LoadState.State_Success, null);
+        }else {
+          if(_page == 1){
+            refreshController.finishRefresh();
+          }else{
+            refreshController.finishLoad();
+          }
+          changeLoadingState(LoadState.State_Empty, null);
+        }
+      }
+    });
+  }
+
+  void handlerResultList(List<GarageSaleRentList>? list, bool isLoadMore) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      if (_page == 1) {
+        //刷新的方式
+        state.list!.clear();
+        state.list!.addAll(list.map((item) => item.toJson()).toList());
+        refreshController.finishRefresh();
+        //更新展示的状态
+        changeLoadingState(LoadState.State_Success, null);
+      } else {
+        //加载更多
+        final allList = state.list;
+        allList!.addAll(list.map((item) => item.toJson()).toList());
+        state = state.copyWith(list: allList);
+        refreshController.finishLoad();
+      }
+    } else {
+      if (_page == 1) {
+        //展示无数据的布局
+        state.list!.clear();
+        changeLoadingState(LoadState.State_Empty, null);
+        refreshController.finishRefresh();
+      } else {
+        //展示加载完成,没有更多数据了
+        if (_page == 1) {
+          //展示无数据的布局
+          state.list!.clear();
+          changeLoadingState(LoadState.State_Empty, null);
+          refreshController.finishRefresh();
+        } else {
+          //展示加载完成,没有更多数据了
+          if(state.list!.length == 0){
+            changeLoadingState(LoadState.State_Empty, null);
+            refreshController.finishLoad();
+          }else {
+            if(_needShowPlaceholder){
+              changeLoadingState(LoadState.State_Success, null);
+            }
+          }
+          //更新展示的状态
+          refreshController.finishLoad(IndicatorResult.noMore);
+        }
+      }
+    }
+  }
+
+
+  // 点击 收藏/取消收藏
+  Future<bool?> handlerClickCollection(int id, bool? isCollection) async{
+    try {
+      //请求网络
+      Map<String, dynamic>  params = {
+        "id": id,
+      };
+      Log.d("请求参数------$params");
+      final result = await servicesRespositoryInstance.fetchGarageColleciton(params);
+      //校验成功失败
+      if (result.isSuccess) {
+        // 修改 该id 的 liked 和 likes_count 字段
+        state.list!.forEach((Map<String, dynamic> element) {
+          GarageSaleRentList elementEntity = GarageSaleRentList.fromJson(element);
+
+          if(elementEntity.id == id){
+            elementEntity.liked = !elementEntity.liked!;
+            elementEntity.likesCount = elementEntity.liked! ? (elementEntity.likesCount! + 1) : (elementEntity.likesCount! - 1);
+          }
+
+          element = elementEntity.toJson();
+
+        });
+        return true;
+      } else {
+        String errorMessage = result.errorMsg!;
+        changeLoadingState(LoadState.State_Error, errorMessage);
+        ToastEngine.show(result.errorMsg ?? "Network Load Error");
+      }
+    } catch (e) {
+      Log.e("Error: $e");
+      ToastEngine.show("Error: $e");
+    }
+  }
+
+  // 设置当前的 _queryParams
+  setCurrentQueryParams(Map<String, dynamic> params){
+    _queryParams.addAll(params);
+  }
+
+  // 获取当前的 _queryParams
+  Map<String, dynamic> getCurrentQueryParams(String? key){
+    if(key!=null && key!.isNotEmpty){
+      return _queryParams[key];
+    }
+    return _queryParams;
+  }
+
+  // 去详情页面
+  void handlerGotoDetail({BuildContext? context, required int id, String type='forSale'}){
+    Log.d("去详情页面");
+    // appRouter.push(GaragesaleDetailPageRoute(id: id, type: 'forSale'));
+  }
+}

+ 27 - 0
packages/cpt_services/lib/modules/services/service_evaluate_list/service_evaluate_list_vm.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'service_evaluate_list_vm.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$serviceEvaluateListVmHash() =>
+    r'cf8b9b7369798db17804f2b5b7db1d4fdc4c8e08';
+
+/// See also [ServiceEvaluateListVm].
+@ProviderFor(ServiceEvaluateListVm)
+final serviceEvaluateListVmProvider = AutoDisposeNotifierProvider<
+    ServiceEvaluateListVm, ServiceEvaluateListState>.internal(
+  ServiceEvaluateListVm.new,
+  name: r'serviceEvaluateListVmProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$serviceEvaluateListVmHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$ServiceEvaluateListVm = AutoDisposeNotifier<ServiceEvaluateListState>;
+// 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

File diff suppressed because it is too large
+ 629 - 0
packages/cpt_services/lib/modules/services/service_order_confirm/service_order_confirm_page.dart


+ 46 - 0
packages/cpt_services/lib/modules/services/service_order_confirm/service_order_confirm_state.dart

@@ -0,0 +1,46 @@
+import 'package:flutter/cupertino.dart';
+import 'package:widgets/load_state_layout.dart';
+
+class ServiceOrderConfirmState {
+  //页面 LoadView 状态的展示
+  LoadState loadingState;
+  String? errorMessage;
+  double? totalPrice;
+  Map<String, Map<String, dynamic>> remarkInfo;
+  Map<String, dynamic>? datas;
+
+
+  ServiceOrderConfirmState({
+    this.loadingState = LoadState.State_Loading,
+    String? errorMessage,
+    double? totalPrice,
+
+    remarkInfo,
+    this.datas,
+  }): remarkInfo = remarkInfo ?? {
+    "remark": {
+      'value': '',
+      'controller': TextEditingController(),
+      'hintText': '',
+      'focusNode': FocusNode(),
+      'obsecure': false,
+    },
+  },
+  totalPrice = totalPrice ?? 0.0;
+
+  ServiceOrderConfirmState copyWith({
+    LoadState? loadingState,
+    String? errorMessage,
+    double? totalPrice,
+    Map<String, dynamic>? remarkInfo,
+    Map<String, dynamic>? datas,
+  }) {
+    return ServiceOrderConfirmState(
+      loadingState: loadingState ?? this.loadingState,
+      errorMessage: errorMessage ?? this.errorMessage,
+      totalPrice: totalPrice ?? this.totalPrice,
+      remarkInfo: remarkInfo ?? this.remarkInfo,
+      datas: datas ?? this.datas,
+    );
+  }
+}

+ 367 - 0
packages/cpt_services/lib/modules/services/service_order_confirm/service_order_confirm_vm.dart

@@ -0,0 +1,367 @@
+
+import 'package:cpt_services/components/chooseAirConditionContent_vm.dart';
+import 'package:cpt_services/components/chooseHouseCleanContent_vm.dart';
+import 'package:cpt_services/components/chooseVisitTimeContent.dart';
+import 'package:cpt_services/components/chooseVisitTimeContent_vm.dart';
+import 'package:cpt_services/components/chooseVisitTimeTitle.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:domain/entity/garage_sale_rent_entity.dart';
+import 'package:domain/entity/newsfeed_detail_entity.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:router/ext/auto_router_extensions.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/dialog/dialog_content_wrap.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../../components/chooseHouseCleanContent.dart';
+import '../../../components/chooseHouseCleanTitle.dart';
+import '../../../components/chooseVisitTimeBottomFooter.dart';
+import '../../../constants_services.dart';
+import '../../../respository/services_respository.dart';
+import '../../../router/page/services_page_router.dart';
+import 'service_order_confirm_state.dart';
+
+part 'service_order_confirm_vm.g.dart';
+
+
+final _chooseVisitTimeDialogGlobalKey = GlobalKey<DialogContentWrapState>();
+
+@riverpod
+class ServiceOrderConfirmVm extends _$ServiceOrderConfirmVm {
+  late ServicesRespository servicesRespositoryInstance;
+  bool _needShowPlaceholder = false; //是否展示LoadingView
+  int _page = 1;  // 当前页
+  int _limit = 10; // 每页数量
+  int _count = 0; // 总条数
+
+  int _detailId = 0;
+  int _detailServiceTypeCode = 0;
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,  //允许刷新
+    controlFinishLoad: true,   //允许加载
+  );
+
+  ServiceOrderConfirmState initState() {
+    return ServiceOrderConfirmState(
+      datas: {}
+    );
+  }
+
+  @override
+  ServiceOrderConfirmState build(){
+    // 引入数据仓库
+    // servicesRespositoryInstance = ref.read(commonGarageRespositoryProvider);
+    final state = initState();
+    Log.d("--------------------------build---------------------");
+
+    return state;
+  }
+
+
+  //刷新页面状态
+  void changeLoadingState(LoadState loadState, String? errorMsg) {
+    state = state.copyWith(
+        loadingState: loadState,
+        errorMessage: errorMsg
+    );
+  }
+
+
+  setInitPageData({required int id, required int serviceTypeCode}){
+    _detailId = id;
+    _detailServiceTypeCode = serviceTypeCode;
+  }
+  // 初始化页面数据
+  initPageData({detailId, detailServiceTypeCode}) {
+    _detailId = detailId??_detailId;
+    _detailServiceTypeCode = detailServiceTypeCode??_detailServiceTypeCode;
+    Log.d("--------------------------initPageData---------------------");
+    changeLoadingState(LoadState.State_Success, null);
+    setConfirmOrderTotalPrice(null);
+  }
+
+  // 上拉加载 更多
+  Future loadMore() async {
+    Log.d("----home_service_vm-----loadMore");
+    _page++;
+    getListData();
+  }
+
+
+  // 下拉刷新
+  Future onRefresh() async {
+    // 当前pageView 页面正处于显示状态
+    Log.d("----forsale_vm-----onRefresh ");
+    // await Future.delayed(const Duration(seconds: 2));
+    _page = 1;
+    getListData();
+  }
+
+
+  // 手动进行刷新
+  Future triggerRefresh() async {
+    Log.d("trggerRefresh");
+    refreshController.callRefresh();
+  }
+
+  // 手动进行刷新
+  Future directRefresh() async {
+    state = state.copyWith(datas:null);
+    // 注意:由于 nestedscrollview 嵌套easyfresh 组件  refreshController.callRefresh() 自动刷新只能滚动顶部但是不会触发下拉刷新,这里调用是 用到了将其滚动到顶部的作用,进而刷新操作主动掉接口
+    // https://github.com/xuelongqy/flutter_easy_refresh/issues/692
+    refreshController.callRefresh();
+    refreshController.resetFooter();
+    _page = 1;
+    _needShowPlaceholder = true;
+    getListData();
+  }
+
+
+  // 重试请求
+  Future retryRequest() async {
+    _page = 1;
+    _needShowPlaceholder = true;
+    getListData();
+  }
+
+
+  // 获取list 列表数据
+  Future getListData<T>({bool? isLoadMore}) async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading, null);
+    }
+
+    List<Map<String, dynamic>> list = [
+      {
+        'id':1,
+        'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'title': 'House Cleaning Services',
+        'duration': 'Daily cleaning for 2 hours',
+        'totalPrice': 66,
+        'visit_time': '14 0ct 2024 15:00',
+        'order_time': '13 0ct 2024 12:00',
+        'status_text': 'In Progress',
+        'status_code': 0,
+        'company_name': 'HONG YE GROUP PTE LTD',
+      },
+      {
+        'id':2,
+        'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'title': 'House Cleaning Services',
+        'duration': 'Daily cleaning for 2 hours',
+        'totalPrice': 66,
+        'visit_time': '14 0ct 2024 15:00',
+        'order_time': '13 0ct 2024 12:00',
+        'status_text': 'In Progress',
+        'status_code': 1,
+        'company_name': 'HONG YE GROUP PTE LTD',
+      },
+      {
+        'id':3,
+        'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'title': 'House Cleaning Services',
+        'duration': 'Daily cleaning for 2 hours',
+        'totalPrice': 66,
+        'visit_time': '14 0ct 2024 15:00',
+        'order_time': '13 0ct 2024 12:00',
+        'status_text': 'In Progress',
+        'status_code': 1,
+        'company_name': 'HONG YE GROUP PTE LTD',
+      },
+    ];
+    handlerResultData(true, list:list);
+
+    // try {
+    //   //请求网络
+    //   Map<String, dynamic>  params = {
+    //     "type": 1,  // 类型(1=Sale,2=Rent)
+    //     "category_id": _queryParams['category_id'],
+    //     "keyword": _queryParams['keyword'],
+    //     "page": _page,
+    //     "limit": _limit,
+    //   };
+    //   Log.d("请求参数------$params");
+    //   final result = await servicesRespositoryInstance.fetchGarageDataList(params);
+    //   //校验成功失败
+    //   if (result.isSuccess) {
+    //     // handlerResultList((result.data as GarageSaleRentEntity).list as List<GarageSaleRentList>, isLoadMore ?? false);
+    //   } else {
+    //     String errorMessage = result.errorMsg!;
+    //     changeLoadingState(LoadState.State_Error, errorMessage);
+    //     ToastEngine.show(result.errorMsg ?? "Network Load Error");
+    //   }
+    // } catch (e) {
+    //   ToastEngine.show("Error: $e");
+    // }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+
+  }
+
+  handlerResultData(bool isList, {List<Map<String, dynamic>>? list, dynamic? data}){
+    Future.delayed(const Duration(seconds: 1)).then((value) {
+      if(isList){
+        // list 数据模式
+        // if(list != null && list.isNotEmpty){
+        //   if(_page == 1){
+        //     state.list.clear();
+        //     state.list!.addAll(list);
+        //     refreshController.finishRefresh();
+        //     changeLoadingState(LoadState.State_Success, null);
+        //   }else {
+        //     final allList = state.list;
+        //     allList!.addAll(list);
+        //     state = state.copyWith(list: allList);
+        //     refreshController.finishLoad();
+        //   }
+        // }else {
+        //   if(_page == 1){
+        //     state.list.clear();
+        //     changeLoadingState(LoadState.State_Empty, null);
+        //     refreshController.finishRefresh();
+        //   }else {
+        //     refreshController.finishLoad(IndicatorResult.noMore);
+        //   }
+        // }
+      }else {
+        // 单个数据模式
+        if(data!=null){
+          if(_page == 1){
+            refreshController.finishRefresh();
+          }else{
+            refreshController.finishLoad();
+          }
+          changeLoadingState(LoadState.State_Success, null);
+        }else {
+          if(_page == 1){
+            refreshController.finishRefresh();
+          }else{
+            refreshController.finishLoad();
+          }
+          changeLoadingState(LoadState.State_Empty, null);
+        }
+      }
+    });
+  }
+
+
+  handlerClickVisitTime(BuildContext context){
+    // ToastEngine.show("Visit Time");
+    handlerShowChooseVisitTimeDialog(context);
+  }
+
+  // 显示 选择空调型号和数量的弹框
+  handlerShowChooseVisitTimeDialog(BuildContext context) async{
+    // 定时器
+    LoadState dialogState = LoadState.State_Loading;
+
+    DialogEngine.show(
+        tag: "chooseAirConditionClean",
+        position: DialogPosition.bottom,
+        widget: DialogContentWrap(
+          key: _chooseVisitTimeDialogGlobalKey,
+          loadingState: dialogState,
+          maxHeight: 480.0,
+          isShowConfirmBtn: false,
+          isShowCancelBtn: false,
+          title: "",
+          bottomBtnRadius:0,
+          bottomBtnSpace: 0,
+          topLeftRadius: 20.0,
+          topRightRadius: 20.0,
+          // bottomBtnSectionPadding: EdgeInsets.only(left: 20, right: 20, bottom: 20,top: 20),
+          yesBtnBg: context.appColors.redDefault,
+          noBtnBg: context.appColors.grayBgE9,
+          confirmTxt: "Confirm",
+          cancelTxt: "Cancel",
+          // yesBtnTextStyle: TextStyle(
+          //   color: context.appColors.textWhite,
+          //   fontSize: 17,
+          //   fontWeight: FontWeight.w400
+          // ),
+          // noBtnTextStyle: TextStyle(
+          //   color: context.appColors.textBlack,
+          //   fontSize: 17,
+          //   fontWeight: FontWeight.w400
+          // ),
+          confirmAction: chooseVisitTimeConfirmFn,
+          cancelAction: chooseVisitTimeCancelFn,
+          titleBuilder: (context) {
+            return buildChooseVisitTimeTitle(context);
+          },
+          messageBuilder: (context) {
+            return buildChooseVisitTimeContent(context);
+          },
+          bottomFooterBuilder: (context){
+            return  buildChooseVisitTimeBottomFooter(context);
+          },
+        )
+    );
+
+    await Future.delayed(Duration(milliseconds: 1000));
+
+    _chooseVisitTimeDialogGlobalKey.currentState?.changeDialogLoadingState(LoadState.State_Success);
+  }
+
+  chooseVisitTimeConfirmFn(){
+    Log.d("点击了确定");
+    // 跳转到 订单确认页面
+    // appRouter.push(ServiceOrderConfirmPageRoute(id: _detailId, serviceTypeCode: _detailServiceTypeCode));
+  }
+
+  chooseVisitTimeCancelFn(){
+    Log.d("点击了取消");
+  }
+
+  Widget buildChooseVisitTimeTitle(BuildContext context){
+    return ChooseVisitTimeTitle(id: 0, serviceTypeCode: 0);
+  }
+
+  Widget buildChooseVisitTimeContent(BuildContext context){
+    return ChooseVisitTimeContent(id: 0, serviceTypeCode: 0,);
+  }
+
+  Widget buildChooseVisitTimeBottomFooter(BuildContext context){
+    return ChooseVisitTimeBottomFooter(id: 0, serviceTypeCode: 0,);
+  }
+
+  // 关闭visitTime 选择的弹框
+  handlerHideVisitTimeDialog(){
+    DialogEngine.dismiss(tag: 'chooseAirConditionClean');
+  }
+  handlerClickPayNow(BuildContext context, {int? id, int? serviceTypeCode}){
+    ToastEngine.show("Pay Now");
+  }
+
+  setConfirmOrderTotalPrice(BuildContext? context){
+    final visitTimeTotalPrice = ref.read(chooseVisitTimeContentVmProvider).totalPrice?? 0.0;
+    final airConditionTotalPrice = ref.read(chooseAirConditionContentVmProvider).totalPrice?? 0.0;
+    final houseCleaningTotalPrice = ref.read(chooseHouseCleanContentVmProvider).totalPrice?? 0.0;
+    final totalPrice = visitTimeTotalPrice.toDouble() + airConditionTotalPrice.toDouble() + houseCleaningTotalPrice.toDouble();
+    // if( _detailServiceTypeCode == servicesConstants.servicesType['houseCleaning'] ){
+    //
+    // }else if(_detailServiceTypeCode == servicesConstants.servicesType['airConditioner']){
+    //
+    // }else {
+    //
+    // }
+    state = state.copyWith(
+        totalPrice: totalPrice
+    );
+  }
+}

+ 27 - 0
packages/cpt_services/lib/modules/services/service_order_confirm/service_order_confirm_vm.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'service_order_confirm_vm.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$serviceOrderConfirmVmHash() =>
+    r'be532359d0dafb1791899b22cd2af3492872a0df';
+
+/// See also [ServiceOrderConfirmVm].
+@ProviderFor(ServiceOrderConfirmVm)
+final serviceOrderConfirmVmProvider = AutoDisposeNotifierProvider<
+    ServiceOrderConfirmVm, ServiceOrderConfirmState>.internal(
+  ServiceOrderConfirmVm.new,
+  name: r'serviceOrderConfirmVmProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$serviceOrderConfirmVmHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$ServiceOrderConfirmVm = AutoDisposeNotifier<ServiceOrderConfirmState>;
+// 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

+ 99 - 0
packages/cpt_services/lib/modules/services/service_pay_success/service_pay_success_page.dart

@@ -0,0 +1,99 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/garage_sale_rent_entity.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';
+import 'package:widgets/my_appbar.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../../constants_services.dart';
+import '../../../router/page/services_page_router.dart';
+import 'service_pay_success_vm.dart';
+import '../../../components/status_card_item.dart';
+
+@RoutePage()
+class ServicePaySuccessPage extends HookConsumerWidget {
+  final int id;
+  final int serviceTypeCode;
+  const ServicePaySuccessPage({
+    Key? key,
+    required this.id,
+    required this.serviceTypeCode,
+  }) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context, required int id, required int serviceTypeCode}) {
+    if (context != null) {
+      context.router.push(ServicePaySuccessPageRoute(id: id, serviceTypeCode: serviceTypeCode));
+    } else {
+      appRouter.push(ServicePaySuccessPageRoute(id:id, serviceTypeCode: serviceTypeCode));
+    }
+  }
+
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final vm = ref.read(servicePaySuccessVmProvider.notifier);
+    final state = ref.watch(servicePaySuccessVmProvider);
+    // final appConfigState = ref.watch(appConfigServiceProvider)
+
+
+    useEffect(() {
+      // 组件挂载时执行 - 执行接口请求
+      Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+      };
+    }, []);
+
+    return Scaffold(
+      // appBar: MyAppBar.appBar(
+      //   context,
+      //   "ServicePaySuccess",
+      //   backgroundColor: context.appColors.whiteBG,
+      // ),
+      backgroundColor: ColorUtils.string2Color("#F2F3F6"),
+      body: SizedBox(
+          width: double.infinity,
+          height: double.infinity,
+          child: EasyRefresh(
+            key: ValueKey('ServicePaySuccess'),
+            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();
+              },
+              successWidget: Container(),
+            ),
+          ).marginOnly(left: 15,right: 15,top: 0,bottom: 15)
+      ),
+    );
+  }
+
+}

+ 27 - 0
packages/cpt_services/lib/modules/services/service_pay_success/service_pay_success_state.dart

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

+ 232 - 0
packages/cpt_services/lib/modules/services/service_pay_success/service_pay_success_vm.dart

@@ -0,0 +1,232 @@
+
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/garage_sale_rent_entity.dart';
+import 'package:domain/entity/newsfeed_detail_entity.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 '../../../respository/services_respository.dart';
+import '../../../router/page/services_page_router.dart';
+import 'service_pay_success_state.dart';
+
+part 'service_pay_success_vm.g.dart';
+
+@riverpod
+class ServicePaySuccessVm extends _$ServicePaySuccessVm {
+  late ServicesRespository servicesRespositoryInstance;
+  bool _needShowPlaceholder = false; //是否展示LoadingView
+  int _page = 1;  // 当前页
+  int _limit = 10; // 每页数量
+  int _count = 0; // 总条数
+
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,  //允许刷新
+    controlFinishLoad: true,   //允许加载
+  );
+
+  ServicePaySuccessState initState() {
+    return ServicePaySuccessState();
+  }
+
+  @override
+  ServicePaySuccessState build(){
+    // 引入数据仓库
+    // servicesRespositoryInstance = ref.read(commonGarageRespositoryProvider);
+    final state = initState();
+    Log.d("--------------------------build---------------------");
+
+    return state;
+  }
+
+
+  //刷新页面状态
+  void changeLoadingState(LoadState loadState, String? errorMsg) {
+    state = state.copyWith(
+        loadingState: loadState,
+        errorMessage: errorMsg
+    );
+  }
+
+  // 初始化页面数据
+  initPageData() {
+    onRefresh();
+  }
+
+  // 上拉加载 更多
+  Future loadMore() async {
+    Log.d("----home_service_vm-----loadMore");
+    _page++;
+    getListData();
+  }
+
+
+  // 下拉刷新
+  Future onRefresh() async {
+    // 当前pageView 页面正处于显示状态
+    Log.d("----forsale_vm-----onRefresh ");
+    // await Future.delayed(const Duration(seconds: 2));
+    _page = 1;
+    getListData();
+  }
+
+
+  // 手动进行刷新
+  Future triggerRefresh() async {
+    Log.d("trggerRefresh");
+    refreshController.callRefresh();
+  }
+
+  // 手动进行刷新
+  Future directRefresh() async {
+    state = state.copyWith(datas:null);
+    // 注意:由于 nestedscrollview 嵌套easyfresh 组件  refreshController.callRefresh() 自动刷新只能滚动顶部但是不会触发下拉刷新,这里调用是 用到了将其滚动到顶部的作用,进而刷新操作主动掉接口
+    // https://github.com/xuelongqy/flutter_easy_refresh/issues/692
+    refreshController.callRefresh();
+    refreshController.resetFooter();
+    _page = 1;
+    _needShowPlaceholder = true;
+    getListData();
+  }
+
+
+  // 重试请求
+  Future retryRequest() async {
+    _page = 1;
+    _needShowPlaceholder = true;
+    getListData();
+  }
+
+
+  // 获取list 列表数据
+  Future getListData<T>({bool? isLoadMore}) async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading, null);
+    }
+
+    List<Map<String, dynamic>> list = [
+      {
+        'id':1,
+        'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'title': 'House Cleaning Services',
+        'duration': 'Daily cleaning for 2 hours',
+        'totalPrice': 66,
+        'visit_time': '14 0ct 2024 15:00',
+        'order_time': '13 0ct 2024 12:00',
+        'status_text': 'In Progress',
+        'status_code': 0,
+        'company_name': 'HONG YE GROUP PTE LTD',
+      },
+      {
+        'id':2,
+        'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'title': 'House Cleaning Services',
+        'duration': 'Daily cleaning for 2 hours',
+        'totalPrice': 66,
+        'visit_time': '14 0ct 2024 15:00',
+        'order_time': '13 0ct 2024 12:00',
+        'status_text': 'In Progress',
+        'status_code': 1,
+        'company_name': 'HONG YE GROUP PTE LTD',
+      },
+      {
+        'id':3,
+        'service_type': 0,  // 0 房屋保洁 1 空调保洁  2 维修
+        'cover_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+        'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+        'title': 'House Cleaning Services',
+        'duration': 'Daily cleaning for 2 hours',
+        'totalPrice': 66,
+        'visit_time': '14 0ct 2024 15:00',
+        'order_time': '13 0ct 2024 12:00',
+        'status_text': 'In Progress',
+        'status_code': 1,
+        'company_name': 'HONG YE GROUP PTE LTD',
+      },
+    ];
+    handlerResultData(true, list:list);
+
+    // try {
+    //   //请求网络
+    //   Map<String, dynamic>  params = {
+    //     "type": 1,  // 类型(1=Sale,2=Rent)
+    //     "category_id": _queryParams['category_id'],
+    //     "keyword": _queryParams['keyword'],
+    //     "page": _page,
+    //     "limit": _limit,
+    //   };
+    //   Log.d("请求参数------$params");
+    //   final result = await servicesRespositoryInstance.fetchGarageDataList(params);
+    //   //校验成功失败
+    //   if (result.isSuccess) {
+    //     // handlerResultList((result.data as GarageSaleRentEntity).list as List<GarageSaleRentList>, isLoadMore ?? false);
+    //   } else {
+    //     String errorMessage = result.errorMsg!;
+    //     changeLoadingState(LoadState.State_Error, errorMessage);
+    //     ToastEngine.show(result.errorMsg ?? "Network Load Error");
+    //   }
+    // } catch (e) {
+    //   ToastEngine.show("Error: $e");
+    // }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+
+  }
+
+  handlerResultData(bool isList, {List<Map<String, dynamic>>? list, dynamic? data}){
+    Future.delayed(const Duration(seconds: 1)).then((value) {
+      if(isList){
+        // list 数据模式
+        // if(list != null && list.isNotEmpty){
+        //   if(_page == 1){
+        //     state.list.clear();
+        //     state.list!.addAll(list);
+        //     refreshController.finishRefresh();
+        //     changeLoadingState(LoadState.State_Success, null);
+        //   }else {
+        //     final allList = state.list;
+        //     allList!.addAll(list);
+        //     state = state.copyWith(list: allList);
+        //     refreshController.finishLoad();
+        //   }
+        // }else {
+        //   if(_page == 1){
+        //     state.list.clear();
+        //     changeLoadingState(LoadState.State_Empty, null);
+        //     refreshController.finishRefresh();
+        //   }else {
+        //     refreshController.finishLoad(IndicatorResult.noMore);
+        //   }
+        // }
+      }else {
+        // 单个数据模式
+        if(data!=null){
+          if(_page == 1){
+            refreshController.finishRefresh();
+          }else{
+            refreshController.finishLoad();
+          }
+          changeLoadingState(LoadState.State_Success, null);
+        }else {
+          if(_page == 1){
+            refreshController.finishRefresh();
+          }else{
+            refreshController.finishLoad();
+          }
+          changeLoadingState(LoadState.State_Empty, null);
+        }
+      }
+    });
+  }
+}

+ 27 - 0
packages/cpt_services/lib/modules/services/service_pay_success/service_pay_success_vm.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'service_pay_success_vm.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$servicePaySuccessVmHash() =>
+    r'459e97a97f9d2e878e216041d5d5714378cf8ad5';
+
+/// See also [ServicePaySuccessVm].
+@ProviderFor(ServicePaySuccessVm)
+final servicePaySuccessVmProvider = AutoDisposeNotifierProvider<
+    ServicePaySuccessVm, ServicePaySuccessState>.internal(
+  ServicePaySuccessVm.new,
+  name: r'servicePaySuccessVmProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$servicePaySuccessVmHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$ServicePaySuccessVm = AutoDisposeNotifier<ServicePaySuccessState>;
+// 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

+ 362 - 0
packages/cpt_services/lib/modules/services/service_repair_detail/service_repair_detail_page.dart

@@ -0,0 +1,362 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:domain/entity/garage_sale_rent_detail_entity.dart';
+import 'package:domain/entity/garage_sale_rent_entity.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:plugin_basic/provider/app_config/app_config_service.dart';
+import 'package:plugin_platform/engine/image/image_preview.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/size_config.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_like_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'service_repair_detail_vm.dart';
+
+import '../../../router/page/services_page_router.dart';
+
+@RoutePage()
+class ServiceRepairDetailPage extends HookConsumerWidget {
+  final int id;
+  final int serviceTypeCode;
+
+  const ServiceRepairDetailPage({Key? key,@PathParam('id') required this.id, @PathParam('serviceTypeCode') required this.serviceTypeCode}) : super(key: key);
+  // 启动当前页面
+  static void startInstance({BuildContext? context, int? id, int? serviceTypeCode = 0}) {
+    if (context != null) {
+      context.router.push(ServiceRepairDetailPageRoute(id: id!, serviceTypeCode: serviceTypeCode!));
+    } else {
+      appRouter.push(ServiceRepairDetailPageRoute(id: id!, serviceTypeCode: serviceTypeCode!));
+    }
+  }
+
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final state = ref.watch(serviceRepairDetailVmProvider);
+    final vm = ref.read(serviceRepairDetailVmProvider.notifier);
+    final String pageTitle = 'Repair Details';
+    GlobalKey _likeButtonKey = GlobalKey<MyLikeButtonState>();
+
+    GarageSaleRentDetailEntity? detailInfo = state.datas?? null;
+    String title =  detailInfo?.title?? '';
+    int price =  detailInfo?.price?? 0;
+
+    String contactType =  detailInfo?.contact??'';
+
+    String description = '';
+
+    useEffect((){
+      vm.setInitPageData(id: id, serviceTypeCode: serviceTypeCode,);
+      // 组件挂载时执行 - 执行接口请求
+      Future.microtask(() => vm.initPageData());
+      return () {
+        // 组件卸载时执行
+      };
+    },[]);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(
+        context,
+        pageTitle,
+        backgroundColor: context.appColors.backgroundWhite,
+      ),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: Column(
+        children: [
+          Expanded(
+            child: EasyRefresh(
+              controller: vm.refreshController,
+              // 上拉加载
+              onLoad: null,
+              // 下拉刷新
+              onRefresh: () async{
+                Log.d("----onRefresh");
+                vm.onRefresh();
+              },
+              // header: MaterialHeader(),
+              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, detailInfo,),
+                      // ),
+                      successSliverWidget: [
+                        SliverList(
+                          delegate: SliverChildBuilderDelegate(
+                                  (context, index){
+                                return detailInfo !=null? _buildContentBox(context, ref, detailInfo): Container();
+                              },
+                              childCount: 1
+                          ),
+                        )
+                      ],
+                    ),
+                    Visibility(
+                      visible: state.loadingState == LoadState.State_Success,
+                      child: Visibility(
+                        visible: vm.isContactWhatsApp(contactType),
+                        child: _buildWhatsApp(context, ref,
+                          title:title,
+                          price:price,
+                          contactType:contactType,
+                          description:description,
+                        ),
+                      ),
+                    ),
+                  ]
+              ),
+            ),
+          ),
+          // 底部联系信息
+          Visibility(
+            visible: state.loadingState == LoadState.State_Success,
+            child: detailInfo !=null ? _buildBottomConcatInfo(context, ref, _likeButtonKey,
+              title:title,
+              price:price,
+              contactType:contactType,
+              description:description,
+              detailInfo:detailInfo,
+            ): Container(),
+          )
+        ],
+      ),
+    );
+  }
+
+  Widget _buildWhatsApp(BuildContext context, WidgetRef ref, {required String title, required int price, required String contactType, required String description}) {
+    final vm = ref.read(serviceRepairDetailVmProvider.notifier);
+    return Positioned(
+      right: 15.5,
+      bottom: 42,
+      child: InkWell(
+        onTap: () {
+          // 点击了whatsapp
+          vm.handlerClickWhatsapp(context, contactType, title, price);
+        },
+        child: const MyAssetImage(Assets.communityWhatsAPP, width: 57,height: 57,),
+      ),
+    );
+  }
+
+  Widget _buildContentBox(BuildContext context, WidgetRef ref, GarageSaleRentDetailEntity detailInfo) {
+    List<String> resources = detailInfo.resources??[];
+    String goods_img = resources[0]??'';
+    String title =  detailInfo.title??'';
+    int price = detailInfo.price??0;
+    String contactType =  '';
+    String description = detailInfo.description??'';
+    CarouselSliderController buttonCarouselController = CarouselSliderController();
+    return Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          // 图片
+          SizedBox(
+            width: SizeConfig().screenWidth,
+            height: 173.5,
+            child: CarouselSlider(
+              // items: [MyLoadImage(goods_img)] ,
+              items: resources.map((resource) => MyLoadImage(
+                resource,
+                fit: BoxFit.cover, // 确保图片覆盖整个区域
+                width: SizeConfig().screenWidth, // 确保图片宽度与屏幕宽度一致
+                height: 173.5, // 确保图片高度与 CarouselSlider 高度一致
+              ).onTap((){
+                ImagePreviewEngine.multipleImagePreview(
+                    context,
+                    resources,
+                    heroes: List.generate(resources.length, (index) => resources[index]),
+                    onLongPressAction: (url) {}
+                );
+              })
+              ).toList(),
+              carouselController: buttonCarouselController,
+              options: CarouselOptions(
+                autoPlay: false,
+                enlargeCenterPage: true,
+                viewportFraction: 1,
+                initialPage: 0,
+              ),
+            ),
+          ),
+          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!=null? 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,
+      {
+        required String title,
+        required int price,
+        required String contactType,
+        required String description,
+        required GarageSaleRentDetailEntity detailInfo,
+      }
+      ) {
+    final vm = ref.read(serviceRepairDetailVmProvider.notifier);
+
+    GarageSaleRentDetailAccount  account = detailInfo.account?? GarageSaleRentDetailAccount();
+
+    Log.d("0000000 ${detailInfo.likesCount}");
+    String publisher =  account.name??'-';
+    String publisherAvatar = account.avatar??'-';
+
+    String contactType =  detailInfo.contact??'';
+    String publisherTime =  detailInfo.createdAt??'-';
+    int? likes_count =  detailInfo.likesCount??0;
+    Log.d("666666 ${likes_count}");
+
+
+    final _likes_count = useState<int>(likes_count!);
+    final _isLiked = useState<bool>(false);
+    Log.d("4344 ${_likes_count.value}");
+
+
+    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: _isLiked.value,
+                        isCustomIcon: true,
+                        customIconUnActiveAssets: Assets.communityCollection,
+                        customIconActiveAssets: Assets.communityLikeActive,
+                        // customIconWidth: 18,
+                        // customIconHeight: 18,
+                        onLike: () async {
+                          Log.d('点击了like button');
+                          int id = detailInfo!.id as int;
+                          final isSuccess = await vm.handlerClickCollection(context, id, true);
+                          if(isSuccess!=null && isSuccess){
+                            // 成功
+                            if(_isLiked.value){
+                              Log.d("取消点赞");
+                              _likes_count.value--;
+                              _isLiked.value = false;
+                            }else {
+                              Log.d("点赞");
+                              _likes_count.value++;
+                              _isLiked.value = true;
+                            }
+                          }
+                        },
+                      ),
+                      MyTextView(
+                        "${_likes_count.value}",
+                        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: vm.isContactMobile(contactType),
+                  child:  Row(
+                    children: [
+                      const SizedBox(width: 4,),
+                      const MyAssetImage(
+                        Assets.communityPhone,
+                        width: 21.5,
+                        height: 18,
+                      ).onTap((){
+                        vm.handlerClickMobile(context, contactType);
+                      }),
+                      const SizedBox(width: 15,),
+                    ],
+                  ),
+                ),
+              ],
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 37 - 0
packages/cpt_services/lib/modules/services/service_repair_detail/service_repair_detail_state.dart

@@ -0,0 +1,37 @@
+import 'package:domain/entity/garage_sale_rent_detail_entity.dart';
+import 'package:widgets/load_state_layout.dart';
+
+class ServiceRepairDetailState {
+  //页面 LoadView 状态的展示
+  LoadState loadingState;
+  String? errorMessage;
+
+  final int? id;
+  final String? type;
+
+  GarageSaleRentDetailEntity? datas;
+
+  ServiceRepairDetailState({
+    this.loadingState = LoadState.State_Loading,
+    this.errorMessage,
+    this.id,
+    this.type,
+    required this.datas,
+  });
+
+  ServiceRepairDetailState copyWith({
+    GarageSaleRentDetailEntity? datas,
+    LoadState? loadingState,
+    String? errorMessage,
+    int? id,
+    String? type,
+  }) {
+    return ServiceRepairDetailState(
+      loadingState: loadingState ?? this.loadingState,
+      errorMessage: errorMessage ?? this.errorMessage,
+      id: id ?? this.id,
+      type: type ?? this.type,
+      datas: datas?? this.datas,
+    );
+  }
+}

+ 248 - 0
packages/cpt_services/lib/modules/services/service_repair_detail/service_repair_detail_vm.dart

@@ -0,0 +1,248 @@
+
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/garage_sale_rent_detail_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:plugin_basic/basic_export.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 '../../../respository/services_respository.dart';
+import '../../../router/page/services_page_router.dart';
+import 'service_repair_detail_state.dart';
+
+part 'service_repair_detail_vm.g.dart';
+
+@riverpod
+class ServiceRepairDetailVm extends _$ServiceRepairDetailVm {
+  late ServicesRespository serviceRespositoryInstance;
+
+  int _detailId = 0;
+  int _detailServiceTypeCode = 0;
+
+  bool _needShowPlaceholder = false; //是否展示LoadingView
+  int _page = 1;  // 当前页
+  int _limit = 10; // 每页数量
+  int _count = 0; // 总条数
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,  //允许刷新
+    controlFinishLoad: true,   //允许加载
+  );
+
+  ServiceRepairDetailState initState() {
+    return ServiceRepairDetailState(
+      datas: null,
+    );
+  }
+
+  @override
+  ServiceRepairDetailState build(){
+    // 引入数据仓库
+    serviceRespositoryInstance = ref.read(servicesRespositoryProvider);
+    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 {
+    _page++;
+    getDetailData();
+  }
+
+
+  // 下拉刷新
+  Future onRefresh() async {
+    _page = 1;
+    getDetailData();
+  }
+
+
+  // 手动进行刷新
+  Future triggerRefresh() async {
+    Log.d("trggerRefresh");
+    refreshController.callRefresh();
+  }
+
+  // 手动进行刷新
+  Future directRefresh() async {
+    state = state.copyWith(datas:null);
+    // 注意:由于 nestedscrollview 嵌套easyfresh 组件  refreshController.callRefresh() 自动刷新只能滚动顶部但是不会触发下拉刷新,这里调用是 用到了将其滚动到顶部的作用,进而刷新操作主动掉接口
+    // https://github.com/xuelongqy/flutter_easy_refresh/issues/692
+    refreshController.callRefresh();
+    refreshController.resetFooter();
+    _page = 1;
+    _needShowPlaceholder = true;
+    getDetailData();
+  }
+
+
+  // 重试请求
+  Future retryRequest() async {
+    _page = 1;
+    _needShowPlaceholder = true;
+    getDetailData();
+  }
+
+
+  setInitPageData({required int id, required int serviceTypeCode}){
+    _detailId = id;
+    _detailServiceTypeCode = serviceTypeCode;
+  }
+
+  Future getDetailData<T>() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading, null);
+    }
+
+
+    // 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: GarageSaleRentDetailEntity(
+    // ));
+    //
+    // changeLoadingState(LoadState.State_Success, null);
+    //
+    // refreshController.finishRefresh();
+    //
+    // // 最后赋值
+    // _needShowPlaceholder = false;
+
+
+    try {
+      Map<String, dynamic> params = {
+        "id": _detailId,
+      };
+      final result = await serviceRespositoryInstance.fetchServiceRepairDetailInfo(params);
+      if(result.isSuccess){
+        state = state.copyWith(datas: result.data as GarageSaleRentDetailEntity);
+        changeLoadingState(LoadState.State_Success, null);
+        refreshController.finishRefresh();
+      } else {
+        String errorMessage = result.errorMsg!;
+        changeLoadingState(LoadState.State_Error, errorMessage);
+        ToastEngine.show(result.errorMsg ?? "Network Load Error");
+      }
+    } catch (e) {
+      ToastEngine.show("Error: $e");
+    }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+  }
+
+
+  // 点击了底部的收藏按钮
+  Future<bool?> handlerClickCollection(BuildContext? context, int id, bool isCollection) async{
+    // var vm;
+    // if(_detailServiceTypeCode == "forSale"){
+    //   // vm = ref.read(forsaleVmProvider.notifier);
+    // }else if(_detailServiceTypeCode == "forRent"){
+    //   vm = ref.read(forrentVmProvider.notifier);
+    // }
+    // try {
+    //   final isSuccess = await vm.handlerClickCollection(id, isCollection);
+    //   return isSuccess;
+    // }catch(e){
+    //
+    // }
+  }
+
+  // 判断是whatsapp 还是 mobile  (通过 contact: whatsapp:+8617671757687)
+  String getContactType(String contactType){
+    // whatsapp:+8617671757687  截取:前面的字符串
+    String type = contactType.split(":")[0];
+    return type;
+  }
+
+  isContactWhatsApp(String contactType){
+    return contactType.contains("whatsapp");
+  }
+
+  isContactMobile(String contactType){
+    return contactType.contains("mobile");
+  }
+  String getContactNumber(String contactType){
+    // whatsapp:+8617671757687     mobile:+8617671757687  截取:后面的字符串
+    String number = contactType.split(":")[1];
+    return number;
+  }
+  // 点击 whatsapp
+  handlerClickWhatsapp(BuildContext? context,String contactType, String title, int price){
+    // 假设你有一个获取 WhatsApp 号码的方法
+    String whatsappNumber = getContactNumber(contactType); // 你需要实现这个方法
+    // 构建消息并进行编码
+    String message = Uri.encodeComponent("Hello, I am interested in your listing: $title for $price.");
+    // 打开WhatsApp
+    openWhatsApp(whatsappNumber, message);
+  }
+
+  // 打开whatsapp
+  openWhatsApp(String phoneNumber, String message) async {
+    final url = "https://wa.me/$phoneNumber/?text=${Uri.parse(message)}";
+    if (await  canLaunchUrl(Uri.parse(url))) {
+      await launchUrl(Uri.parse(url));
+    } else {
+      ToastEngine.show("Could not launch $url");
+      throw 'Could not launch $url';
+    }
+  }
+
+  // 点击 电话
+  handlerClickMobile(BuildContext? context,String contactType){
+    String mobileNumber = getContactNumber(contactType);
+    // 拨打电话
+    makePhoneCall(mobileNumber);
+  }
+
+  // 拨打电话
+  Future<void> makePhoneCall(String phoneNumber) async {
+    final Uri launchUri = Uri(
+      scheme: 'tel',
+      path: phoneNumber,
+    );
+
+    if (await canLaunchUrl(launchUri)) {
+      await launchUrl(launchUri);
+    } else {
+      ToastEngine.show("Could not launch $launchUri");
+      throw 'Could not launch $launchUri';
+    }
+  }
+}

+ 27 - 0
packages/cpt_services/lib/modules/services/service_repair_detail/service_repair_detail_vm.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'service_repair_detail_vm.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$serviceRepairDetailVmHash() =>
+    r'd828d6b6e6677999dea7125508ba85448ebf7d00';
+
+/// See also [ServiceRepairDetailVm].
+@ProviderFor(ServiceRepairDetailVm)
+final serviceRepairDetailVmProvider = AutoDisposeNotifierProvider<
+    ServiceRepairDetailVm, ServiceRepairDetailState>.internal(
+  ServiceRepairDetailVm.new,
+  name: r'serviceRepairDetailVmProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$serviceRepairDetailVmHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$ServiceRepairDetailVm = AutoDisposeNotifier<ServiceRepairDetailState>;
+// 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

+ 19 - 99
packages/cpt_services/lib/modules/services/services_page.dart

@@ -10,6 +10,7 @@ import 'package:plugin_basic/provider/user_config/user_config_service.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/custom_sliver_persistent_header_delegate.dart';
 import 'package:widgets/my_load_image.dart';
 import 'package:widgets/ext/ex_widget.dart';
 import 'package:widgets/my_text_view.dart';
@@ -18,6 +19,7 @@ import 'package:cs_resources/theme/app_colors_theme.dart';
 import 'package:widgets/widget_export.dart';
 
 import '../../router/page/services_page_router.dart';
+import 'inProgress/in_progress_page.dart';
 import 'services_vm.dart';
 
 final tabsRouterKey = GlobalKey<AutoTabsRouterState>();
@@ -86,6 +88,7 @@ class ServicesPage extends HookConsumerWidget with WidgetsBindingObserver {
   Widget build(BuildContext context, WidgetRef ref) {
     final vm = ref.read(servicesVmProvider.notifier);
     final state = ref.watch(servicesVmProvider);
+    final currentPageIdx = tabsRouterKey.currentState?.controller?.activeIndex ?? 0;
 
     useEffect(() {
       // 监听窗口
@@ -118,7 +121,7 @@ class ServicesPage extends HookConsumerWidget with WidgetsBindingObserver {
             value: vm.getCurrentQueryParams('keyword'),
             actions: [
               const MyAssetImage(
-                Assets.serviceServiceIcon1,
+                Assets.serviceServiceScoreYes,
                 width: 21.5,
                 height: 21.5,
               ).onTap((){
@@ -137,18 +140,18 @@ class ServicesPage extends HookConsumerWidget with WidgetsBindingObserver {
             onlyOneScrollInBody: true,
             headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
               return [
-                // SliverPersistentHeader(
-                //   delegate: CustomSliverPersistentHeaderDelegate(
-                //     maxHeight: 180,
-                //     minHeight: 180,
-                //     child: _buildTopSection(context, ref, vm, state),
-                //   ),
-                //   pinned: false,
-                // ),
-                // top 组件,转换为 Sliver 组件
-                SliverToBoxAdapter(
-                  child: _buildTopSection(context, ref, vm, state),
+                SliverPersistentHeader(
+                  delegate: CustomSliverPersistentHeaderDelegate(
+                    maxHeight: 130,
+                    minHeight: 130,
+                    child: _buildTopSection(context, ref, vm, state),
+                  ),
+                  pinned: currentPageIdx !=0? true:false,
                 ),
+                // top 组件,转换为 Sliver 组件
+                // SliverToBoxAdapter(
+                //   child: _buildTopSection(context, ref, vm, state),
+                // ),
               ];
             },
             body: Column(
@@ -159,14 +162,14 @@ class ServicesPage extends HookConsumerWidget with WidgetsBindingObserver {
                         key: tabsRouterKey,
                         routes: const [
                           HomeServicePageRoute(),
+                          InProgressPageRoute(),
+                          HistoryPageRoute(),
                         ],
                         builder: (context, child, pageController) {
                           final tabsRouter = AutoTabsRouter.of(context);
                           return Column(
                             children: [
-                              Expanded(
-                                  child: child
-                              ),
+                              Expanded(child: child),
                             ],
                           );
                         },
@@ -184,7 +187,6 @@ class ServicesPage extends HookConsumerWidget with WidgetsBindingObserver {
     final currentPageIdx = tabsRouterKey.currentState?.controller?.activeIndex ?? 0;
     return Container(
       color: context.appColors.whiteBG,
-      padding: const EdgeInsets.only(top: 23, bottom: 30),
       child: Center(
         child: Row(
           mainAxisSize: MainAxisSize.max,
@@ -214,6 +216,7 @@ class ServicesPage extends HookConsumerWidget with WidgetsBindingObserver {
                     // width: 70,
                     height: 70,
                   ).onTap(() {
+                    vm.handlerSwitchPageView(context, index);
                   },
                     type: ClickType.throttle,
                   ),
@@ -233,88 +236,5 @@ class ServicesPage extends HookConsumerWidget with WidgetsBindingObserver {
       ),
     );
   }
-
-  Widget _buildNewsFeedPost(BuildContext context, WidgetRef ref, vm, state){
-    final userConfig = UserConfigService.getState(ref: ref);
-    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.servicesNewsFeed, width: 45,height: 45,),
-          MyLoadImage(
-            userConfig.user?.avatar,
-            width: 45,
-            height: 45,
-            isCircle: true,
-            fit: BoxFit.fill,
-          ),
-          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.serviceServiceIcon1,
-            width: 21,
-            height: 16.5,
-          ),
-        ],
-      ).onTap((){
-        vm.handlerGotoNewsfeedPost(context);
-      }),
-    );
-  }
-
-  Widget _buildGarageSalePost(BuildContext context, WidgetRef ref, vm, state){
-    final userConfig = UserConfigService.getState(ref: ref);
-    return Container(
-      height: 65.5,
-      width: double.infinity,
-      padding: const EdgeInsets.only(left: 20, right: 20),
-      color: Colors.white,
-      child: Row(
-        children: [
-          MyLoadImage(
-            userConfig.user?.avatar,
-            width: 45,
-            height: 45,
-            isCircle: true,
-            fit: BoxFit.fill,
-          ),
-          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.serviceServiceIcon1,
-            width: 21,
-            height: 16.5,
-          ),
-        ],
-      ).onTap((){
-        vm.handlerGotoGaragePost(context);
-      }),
-    );
-  }
 }
 

+ 4 - 0
packages/cpt_services/lib/modules/services/services_vm.dart

@@ -113,6 +113,10 @@ class ServicesVm extends _$ServicesVm {
     getPageViewVm();
   }
 
+  handlerSwitchPageView(BuildContext? context, int pageViewIdx){
+    tabsRouterKey.currentState?.controller?.setActiveIndex(pageViewIdx);
+  }
+
 
   // 获取当前pageView 的vm
   getCurrentPageViewVm(int? pageViewIdx){

+ 1 - 1
packages/cpt_services/lib/modules/services/services_vm.g.dart

@@ -6,7 +6,7 @@ part of 'services_vm.dart';
 // RiverpodGenerator
 // **************************************************************************
 
-String _$servicesVmHash() => r'1b1580e19b32511d053f424edbd3ca934e86cad7';
+String _$servicesVmHash() => r'7805f3c3765a93ec9b488e6e288a5baa346ba2ed';
 
 /// See also [ServicesVm].
 @ProviderFor(ServicesVm)

+ 39 - 2
packages/cpt_services/lib/respository/services_respository.dart

@@ -231,8 +231,45 @@ class ServicesRespository {
     return result.convert();
   }
 
-  // 获取 详情信息
-  Future<HttpResult<Object>> fetchGarageDetailInfo(
+  // 获取 clean 详情信息
+  Future<HttpResult<Object>> fetchServiceCleanDetailInfo(
+      Map<String, dynamic>? data, {
+        CancelToken? cancelToken,
+      }) async {
+    Map<String, dynamic> params = {};
+    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 地址
+      '/api/v1/user/garage-sale/index/detail', // api 地址
+      params: params,
+      headers: headers,
+      method: HttpMethod.GET,
+      isShowLoadingDialog: false,  //是否展示默认的Loading弹窗
+      networkDebounce: true,   //是否防抖防止重复请求
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      final json = result.getDataJson();
+      // var data = GarageSaleRentDetailEntity.fromJson(json!);
+      //重新赋值data或list
+      return result.convert(data: json);
+    }else {
+      if(result.errorMsg != null && result.errorMsg!.isNotEmpty){
+        ToastEngine.show("${result.errorMsg}");
+      }
+    }
+    return result.convert();
+  }
+
+  // 获取 repair 详情信息
+  Future<HttpResult<Object>> fetchServiceRepairDetailInfo(
       Map<String, dynamic>? data, {
         CancelToken? cancelToken,
       }) async {

+ 56 - 1
packages/cpt_services/lib/router/page/services_page_router.dart

@@ -1,9 +1,19 @@
 import 'package:auto_route/auto_route.dart';
+import 'package:cpt_services/modules/services/service_order_confirm/service_order_confirm_vm.dart';
 import 'package:flutter/material.dart';
 import 'package:router/ext/auto_router_extensions.dart';
 import 'package:router/path/router_path.dart';
 
 import '../../modules/services/homeService/home_service_page.dart';
+import '../../modules/services/inProgress/in_progress_page.dart';
+import '../../modules/services/history/history_page.dart';
+import '../../modules/services/service_clean_detail/service_clean_detail_page.dart';
+import '../../modules/services/service_evaluate_create/service_evaluate_create_page.dart';
+import '../../modules/services/service_evaluate_create_success/evaluate_create_success_page.dart';
+import '../../modules/services/service_evaluate_list/service_evaluate_list_page.dart';
+import '../../modules/services/service_order_confirm/service_order_confirm_page.dart';
+import '../../modules/services/service_pay_success/service_pay_success_page.dart';
+import '../../modules/services/service_repair_detail/service_repair_detail_page.dart';
 import '../../modules/services/services_page.dart';
 part 'services_page_router.gr.dart';
 
@@ -21,10 +31,55 @@ class ServicesPageRouter extends _$ServicesPageRouter {
         children: [
           CustomRoute(
               page: HomeServicePageRoute.page,
-              path: 'home_service',
+              path: RouterPath.servicesHomeService,
               transitionsBuilder: applySlideTransition
           ),
+          CustomRoute(
+              page: InProgressPageRoute.page,
+              path: RouterPath.servicesInProgress,
+              transitionsBuilder: applySlideTransition,
+          ),
+          CustomRoute(
+            page: HistoryPageRoute.page,
+            path: RouterPath.servicesHistory,
+            transitionsBuilder: applySlideTransition,
+          ),
         ]
     ),
+    CustomRoute(
+      page: ServiceCleanDetailPageRoute.page,
+      path: "${RouterPath.servicesCleanDetail}:id/:serviceTypeCode",
+      transitionsBuilder: applySlideTransition,
+    ),
+    CustomRoute(
+      page: ServiceRepairDetailPageRoute.page,
+      path: "${RouterPath.servicesRepairDetail}:id/:serviceTypeCode",
+      transitionsBuilder: applySlideTransition,
+    ),
+    CustomRoute(
+      page: ServiceOrderConfirmPageRoute.page,
+      path: "${RouterPath.servicesOrderConfirm}:id/:serviceTypeCode",
+      transitionsBuilder: applySlideTransition,
+    ),
+    CustomRoute(
+      page: ServicePaySuccessPageRoute.page,
+      path: "${RouterPath.servicesOrderPaySuccess}:id/:serviceTypeCode",
+      transitionsBuilder: applySlideTransition,
+    ),
+    CustomRoute(
+      page: ServiceEvaluateCreatePageRoute.page,
+      path: "${RouterPath.servicesEvaluateCreate}:id/:serviceTypeCode",
+      transitionsBuilder: applySlideTransition,
+    ),
+    CustomRoute(
+      page: EvaluateCreateSuccessPageRoute.page,
+      path: RouterPath.servicesEvaluateCreateSuccess,
+      transitionsBuilder: applySlideTransition,
+    ),
+    CustomRoute(
+      page: ServiceEvaluateListPageRoute.page,
+      path: "${RouterPath.servicesEvaluateList}:id/:serviceTypeCode",
+      transitionsBuilder: applySlideTransition,
+    ),
   ];
 }

+ 443 - 0
packages/cpt_services/lib/router/page/services_page_router.gr.dart

@@ -15,12 +15,111 @@ abstract class _$ServicesPageRouter extends RootStackRouter {
 
   @override
   final Map<String, PageFactory> pagesMap = {
+    EvaluateCreateSuccessPageRoute.name: (routeData) {
+      final args = routeData.argsAs<EvaluateCreateSuccessPageRouteArgs>();
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: EvaluateCreateSuccessPage(
+          key: args.key,
+          id: args.id,
+          serviceTypeCode: args.serviceTypeCode,
+        ),
+      );
+    },
+    HistoryPageRoute.name: (routeData) {
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: const HistoryPage(),
+      );
+    },
     HomeServicePageRoute.name: (routeData) {
       return AutoRoutePage<dynamic>(
         routeData: routeData,
         child: const HomeServicePage(),
       );
     },
+    InProgressPageRoute.name: (routeData) {
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: const InProgressPage(),
+      );
+    },
+    ServiceCleanDetailPageRoute.name: (routeData) {
+      final pathParams = routeData.inheritedPathParams;
+      final args = routeData.argsAs<ServiceCleanDetailPageRouteArgs>(
+          orElse: () => ServiceCleanDetailPageRouteArgs(
+                id: pathParams.getInt('id'),
+                serviceTypeCode: pathParams.getInt('serviceTypeCode'),
+              ));
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: ServiceCleanDetailPage(
+          key: args.key,
+          id: args.id,
+          serviceTypeCode: args.serviceTypeCode,
+        ),
+      );
+    },
+    ServiceEvaluateCreatePageRoute.name: (routeData) {
+      final args = routeData.argsAs<ServiceEvaluateCreatePageRouteArgs>();
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: ServiceEvaluateCreatePage(
+          key: args.key,
+          id: args.id,
+          serviceTypeCode: args.serviceTypeCode,
+        ),
+      );
+    },
+    ServiceEvaluateListPageRoute.name: (routeData) {
+      final args = routeData.argsAs<ServiceEvaluateListPageRouteArgs>();
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: ServiceEvaluateListPage(
+          key: args.key,
+          id: args.id,
+          serviceTypeCode: args.serviceTypeCode,
+        ),
+      );
+    },
+    ServiceOrderConfirmPageRoute.name: (routeData) {
+      final args = routeData.argsAs<ServiceOrderConfirmPageRouteArgs>();
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: ServiceOrderConfirmPage(
+          key: args.key,
+          id: args.id,
+          serviceTypeCode: args.serviceTypeCode,
+        ),
+      );
+    },
+    ServicePaySuccessPageRoute.name: (routeData) {
+      final args = routeData.argsAs<ServicePaySuccessPageRouteArgs>();
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: ServicePaySuccessPage(
+          key: args.key,
+          id: args.id,
+          serviceTypeCode: args.serviceTypeCode,
+        ),
+      );
+    },
+    ServiceRepairDetailPageRoute.name: (routeData) {
+      final pathParams = routeData.inheritedPathParams;
+      final args = routeData.argsAs<ServiceRepairDetailPageRouteArgs>(
+          orElse: () => ServiceRepairDetailPageRouteArgs(
+                id: pathParams.getInt('id'),
+                serviceTypeCode: pathParams.getInt('serviceTypeCode'),
+              ));
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: ServiceRepairDetailPage(
+          key: args.key,
+          id: args.id,
+          serviceTypeCode: args.serviceTypeCode,
+        ),
+      );
+    },
     ServicesPageRoute.name: (routeData) {
       final args = routeData.argsAs<ServicesPageRouteArgs>(
           orElse: () => const ServicesPageRouteArgs());
@@ -33,6 +132,64 @@ abstract class _$ServicesPageRouter extends RootStackRouter {
 }
 
 /// generated route for
+/// [EvaluateCreateSuccessPage]
+class EvaluateCreateSuccessPageRoute
+    extends PageRouteInfo<EvaluateCreateSuccessPageRouteArgs> {
+  EvaluateCreateSuccessPageRoute({
+    Key? key,
+    required int id,
+    required int serviceTypeCode,
+    List<PageRouteInfo>? children,
+  }) : super(
+          EvaluateCreateSuccessPageRoute.name,
+          args: EvaluateCreateSuccessPageRouteArgs(
+            key: key,
+            id: id,
+            serviceTypeCode: serviceTypeCode,
+          ),
+          initialChildren: children,
+        );
+
+  static const String name = 'EvaluateCreateSuccessPageRoute';
+
+  static const PageInfo<EvaluateCreateSuccessPageRouteArgs> page =
+      PageInfo<EvaluateCreateSuccessPageRouteArgs>(name);
+}
+
+class EvaluateCreateSuccessPageRouteArgs {
+  const EvaluateCreateSuccessPageRouteArgs({
+    this.key,
+    required this.id,
+    required this.serviceTypeCode,
+  });
+
+  final Key? key;
+
+  final int id;
+
+  final int serviceTypeCode;
+
+  @override
+  String toString() {
+    return 'EvaluateCreateSuccessPageRouteArgs{key: $key, id: $id, serviceTypeCode: $serviceTypeCode}';
+  }
+}
+
+/// generated route for
+/// [HistoryPage]
+class HistoryPageRoute extends PageRouteInfo<void> {
+  const HistoryPageRoute({List<PageRouteInfo>? children})
+      : super(
+          HistoryPageRoute.name,
+          initialChildren: children,
+        );
+
+  static const String name = 'HistoryPageRoute';
+
+  static const PageInfo<void> page = PageInfo<void>(name);
+}
+
+/// generated route for
 /// [HomeServicePage]
 class HomeServicePageRoute extends PageRouteInfo<void> {
   const HomeServicePageRoute({List<PageRouteInfo>? children})
@@ -47,6 +204,292 @@ class HomeServicePageRoute extends PageRouteInfo<void> {
 }
 
 /// generated route for
+/// [InProgressPage]
+class InProgressPageRoute extends PageRouteInfo<void> {
+  const InProgressPageRoute({List<PageRouteInfo>? children})
+      : super(
+          InProgressPageRoute.name,
+          initialChildren: children,
+        );
+
+  static const String name = 'InProgressPageRoute';
+
+  static const PageInfo<void> page = PageInfo<void>(name);
+}
+
+/// generated route for
+/// [ServiceCleanDetailPage]
+class ServiceCleanDetailPageRoute
+    extends PageRouteInfo<ServiceCleanDetailPageRouteArgs> {
+  ServiceCleanDetailPageRoute({
+    Key? key,
+    required int id,
+    required int serviceTypeCode,
+    List<PageRouteInfo>? children,
+  }) : super(
+          ServiceCleanDetailPageRoute.name,
+          args: ServiceCleanDetailPageRouteArgs(
+            key: key,
+            id: id,
+            serviceTypeCode: serviceTypeCode,
+          ),
+          rawPathParams: {
+            'id': id,
+            'serviceTypeCode': serviceTypeCode,
+          },
+          initialChildren: children,
+        );
+
+  static const String name = 'ServiceCleanDetailPageRoute';
+
+  static const PageInfo<ServiceCleanDetailPageRouteArgs> page =
+      PageInfo<ServiceCleanDetailPageRouteArgs>(name);
+}
+
+class ServiceCleanDetailPageRouteArgs {
+  const ServiceCleanDetailPageRouteArgs({
+    this.key,
+    required this.id,
+    required this.serviceTypeCode,
+  });
+
+  final Key? key;
+
+  final int id;
+
+  final int serviceTypeCode;
+
+  @override
+  String toString() {
+    return 'ServiceCleanDetailPageRouteArgs{key: $key, id: $id, serviceTypeCode: $serviceTypeCode}';
+  }
+}
+
+/// generated route for
+/// [ServiceEvaluateCreatePage]
+class ServiceEvaluateCreatePageRoute
+    extends PageRouteInfo<ServiceEvaluateCreatePageRouteArgs> {
+  ServiceEvaluateCreatePageRoute({
+    Key? key,
+    required int id,
+    required int serviceTypeCode,
+    List<PageRouteInfo>? children,
+  }) : super(
+          ServiceEvaluateCreatePageRoute.name,
+          args: ServiceEvaluateCreatePageRouteArgs(
+            key: key,
+            id: id,
+            serviceTypeCode: serviceTypeCode,
+          ),
+          initialChildren: children,
+        );
+
+  static const String name = 'ServiceEvaluateCreatePageRoute';
+
+  static const PageInfo<ServiceEvaluateCreatePageRouteArgs> page =
+      PageInfo<ServiceEvaluateCreatePageRouteArgs>(name);
+}
+
+class ServiceEvaluateCreatePageRouteArgs {
+  const ServiceEvaluateCreatePageRouteArgs({
+    this.key,
+    required this.id,
+    required this.serviceTypeCode,
+  });
+
+  final Key? key;
+
+  final int id;
+
+  final int serviceTypeCode;
+
+  @override
+  String toString() {
+    return 'ServiceEvaluateCreatePageRouteArgs{key: $key, id: $id, serviceTypeCode: $serviceTypeCode}';
+  }
+}
+
+/// generated route for
+/// [ServiceEvaluateListPage]
+class ServiceEvaluateListPageRoute
+    extends PageRouteInfo<ServiceEvaluateListPageRouteArgs> {
+  ServiceEvaluateListPageRoute({
+    Key? key,
+    required int id,
+    required int serviceTypeCode,
+    List<PageRouteInfo>? children,
+  }) : super(
+          ServiceEvaluateListPageRoute.name,
+          args: ServiceEvaluateListPageRouteArgs(
+            key: key,
+            id: id,
+            serviceTypeCode: serviceTypeCode,
+          ),
+          initialChildren: children,
+        );
+
+  static const String name = 'ServiceEvaluateListPageRoute';
+
+  static const PageInfo<ServiceEvaluateListPageRouteArgs> page =
+      PageInfo<ServiceEvaluateListPageRouteArgs>(name);
+}
+
+class ServiceEvaluateListPageRouteArgs {
+  const ServiceEvaluateListPageRouteArgs({
+    this.key,
+    required this.id,
+    required this.serviceTypeCode,
+  });
+
+  final Key? key;
+
+  final int id;
+
+  final int serviceTypeCode;
+
+  @override
+  String toString() {
+    return 'ServiceEvaluateListPageRouteArgs{key: $key, id: $id, serviceTypeCode: $serviceTypeCode}';
+  }
+}
+
+/// generated route for
+/// [ServiceOrderConfirmPage]
+class ServiceOrderConfirmPageRoute
+    extends PageRouteInfo<ServiceOrderConfirmPageRouteArgs> {
+  ServiceOrderConfirmPageRoute({
+    Key? key,
+    required int id,
+    required int serviceTypeCode,
+    List<PageRouteInfo>? children,
+  }) : super(
+          ServiceOrderConfirmPageRoute.name,
+          args: ServiceOrderConfirmPageRouteArgs(
+            key: key,
+            id: id,
+            serviceTypeCode: serviceTypeCode,
+          ),
+          initialChildren: children,
+        );
+
+  static const String name = 'ServiceOrderConfirmPageRoute';
+
+  static const PageInfo<ServiceOrderConfirmPageRouteArgs> page =
+      PageInfo<ServiceOrderConfirmPageRouteArgs>(name);
+}
+
+class ServiceOrderConfirmPageRouteArgs {
+  const ServiceOrderConfirmPageRouteArgs({
+    this.key,
+    required this.id,
+    required this.serviceTypeCode,
+  });
+
+  final Key? key;
+
+  final int id;
+
+  final int serviceTypeCode;
+
+  @override
+  String toString() {
+    return 'ServiceOrderConfirmPageRouteArgs{key: $key, id: $id, serviceTypeCode: $serviceTypeCode}';
+  }
+}
+
+/// generated route for
+/// [ServicePaySuccessPage]
+class ServicePaySuccessPageRoute
+    extends PageRouteInfo<ServicePaySuccessPageRouteArgs> {
+  ServicePaySuccessPageRoute({
+    Key? key,
+    required int id,
+    required int serviceTypeCode,
+    List<PageRouteInfo>? children,
+  }) : super(
+          ServicePaySuccessPageRoute.name,
+          args: ServicePaySuccessPageRouteArgs(
+            key: key,
+            id: id,
+            serviceTypeCode: serviceTypeCode,
+          ),
+          initialChildren: children,
+        );
+
+  static const String name = 'ServicePaySuccessPageRoute';
+
+  static const PageInfo<ServicePaySuccessPageRouteArgs> page =
+      PageInfo<ServicePaySuccessPageRouteArgs>(name);
+}
+
+class ServicePaySuccessPageRouteArgs {
+  const ServicePaySuccessPageRouteArgs({
+    this.key,
+    required this.id,
+    required this.serviceTypeCode,
+  });
+
+  final Key? key;
+
+  final int id;
+
+  final int serviceTypeCode;
+
+  @override
+  String toString() {
+    return 'ServicePaySuccessPageRouteArgs{key: $key, id: $id, serviceTypeCode: $serviceTypeCode}';
+  }
+}
+
+/// generated route for
+/// [ServiceRepairDetailPage]
+class ServiceRepairDetailPageRoute
+    extends PageRouteInfo<ServiceRepairDetailPageRouteArgs> {
+  ServiceRepairDetailPageRoute({
+    Key? key,
+    required int id,
+    required int serviceTypeCode,
+    List<PageRouteInfo>? children,
+  }) : super(
+          ServiceRepairDetailPageRoute.name,
+          args: ServiceRepairDetailPageRouteArgs(
+            key: key,
+            id: id,
+            serviceTypeCode: serviceTypeCode,
+          ),
+          rawPathParams: {
+            'id': id,
+            'serviceTypeCode': serviceTypeCode,
+          },
+          initialChildren: children,
+        );
+
+  static const String name = 'ServiceRepairDetailPageRoute';
+
+  static const PageInfo<ServiceRepairDetailPageRouteArgs> page =
+      PageInfo<ServiceRepairDetailPageRouteArgs>(name);
+}
+
+class ServiceRepairDetailPageRouteArgs {
+  const ServiceRepairDetailPageRouteArgs({
+    this.key,
+    required this.id,
+    required this.serviceTypeCode,
+  });
+
+  final Key? key;
+
+  final int id;
+
+  final int serviceTypeCode;
+
+  @override
+  String toString() {
+    return 'ServiceRepairDetailPageRouteArgs{key: $key, id: $id, serviceTypeCode: $serviceTypeCode}';
+  }
+}
+
+/// generated route for
 /// [ServicesPage]
 class ServicesPageRoute extends PageRouteInfo<ServicesPageRouteArgs> {
   ServicesPageRoute({

+ 1 - 0
packages/cpt_services/pubspec.yaml

@@ -48,6 +48,7 @@ dependencies:
   # Hooks 简化 Riverpod 获取
   hooks_riverpod: ^2.5.1
 
+
 dev_dependencies:
   flutter_test:
     sdk: flutter

+ 1 - 1
packages/cs_plugin_basic/lib/provider/app_config/app_config_service.g.dart

@@ -6,7 +6,7 @@ part of 'app_config_service.dart';
 // RiverpodGenerator
 // **************************************************************************
 
-String _$appConfigServiceHash() => r'7bf641d7421f160146c33fda60fcbf628069a6bc';
+String _$appConfigServiceHash() => r'a53ad3460c724025d59a4c233d4264f41bcd7b62';
 
 /// See also [AppConfigService].
 @ProviderFor(AppConfigService)

+ 93 - 0
packages/cs_plugin_basic/lib/provider/shopping_cart/shopping_cart.dart

@@ -0,0 +1,93 @@
+
+enum ProductItemType { goods, service }
+
+class ShoppingCart {
+  List<ServiceProviderItem>? serviceProviderItemList = []; // 服务商列表
+  ShoppingCart({this.serviceProviderItemList});
+
+  ShoppingCart copyWith({
+    List<ServiceProviderItem>? serviceProviderItemList,
+  }) {
+    return ShoppingCart(
+      serviceProviderItemList:
+          serviceProviderItemList ?? this.serviceProviderItemList,
+    );
+  }
+}
+
+// 卖家
+class ServiceProviderItem {
+  String? serviceProviderId;  // 服务商Id
+  String? serviceProviderName; // 服务商名称
+  String? serviceProviderImg;  // 服务商图片
+  List<ProductItem>? productItemList = []; // 商品/服务列表
+  ServiceProviderItem({
+    this.serviceProviderId,
+    this.serviceProviderName,
+    this.serviceProviderImg,
+    this.productItemList
+  });
+
+  ServiceProviderItem copyWith({
+    String? serviceProviderId,
+    String? serviceProviderName,
+    String? serviceProviderImg,
+    List<ProductItem>? productItemList,
+  }) {
+    return ServiceProviderItem(
+      serviceProviderId: serviceProviderId ?? this.serviceProviderId,
+      serviceProviderName: serviceProviderName ?? this.serviceProviderName,
+      serviceProviderImg: serviceProviderImg ?? this.serviceProviderImg,
+      productItemList: productItemList ?? this.productItemList,
+    );
+  }
+}
+
+// 商品或者服务
+class ProductItem {
+  ProductItemType type; // 标识是商品还是服务
+  String? productId;  // 商品id
+  String? productName; // 商品名称
+  String? productPrice; // 商品价格
+  String? productNum; // 商品数量
+  String? productImg; // 商品图片
+  double? productScore;  // 商品/服务 评分
+  String? serviceDate;  // 服务的日期
+  String? serviceTime;  // 服务的时间
+
+  ProductItem({
+    required this.type,
+    this.productId,
+    this.productName,
+    this.productPrice,
+    this.productNum,
+    this.productImg,
+    this.productScore,
+    this.serviceDate,
+    this.serviceTime,
+  });
+
+  ProductItem copyWith({
+    ProductItemType? type,
+    String? productId,
+    String? productName,
+    String? productPrice,
+    String? productNum,
+    String? productImg,
+    double? productScore,
+    String? serviceDate,
+    String? serviceTime,
+  }) {
+    return ProductItem(
+      type: type ?? this.type,
+      productId: productId ?? this.productId,
+      productName: productName ?? this.productName,
+      productPrice: productPrice ?? this.productPrice,
+      productNum: productNum ?? this.productNum,
+      productImg: productImg ?? this.productImg,
+      productScore: productScore ?? this.productScore,
+      serviceDate: serviceDate ?? this.serviceDate,
+      serviceTime: serviceTime ?? this.serviceTime,
+    );
+  }
+}

+ 119 - 0
packages/cs_plugin_basic/lib/provider/shopping_cart/shopping_cart_service.dart

@@ -0,0 +1,119 @@
+
+import 'package:domain/repository/profile_repository.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/util.dart';
+import '../global_provider_container.dart';
+import 'shopping_cart.dart';
+
+part 'shopping_cart_service.g.dart';
+
+/*
+ * 购物车信息与状态的服务 - 全局单例
+ */
+@Riverpod(keepAlive: true)
+class ShoppingCartService extends _$ShoppingCartService {
+  late final ProfileRepository profileRepository;
+
+  /// 静态方法获取当前单例对象
+  static ShoppingCartService getInstance({WidgetRef? ref}) {
+    if (ref != null) {
+      return ref.watch(shoppingCartServiceProvider.notifier);
+    } else {
+      return globalContainer.read(shoppingCartServiceProvider.notifier);
+    }
+  }
+
+  static ShoppingCart getState({WidgetRef? ref}) {
+    if (ref != null) {
+      return ref.watch(shoppingCartServiceProvider);
+    } else {
+      return globalContainer.read(shoppingCartServiceProvider);
+    }
+  }
+
+  @override
+  ShoppingCart build() {
+    final state = ShoppingCart();
+    return state;
+  }
+
+
+  // 根据服务商id 和商品id 添加/更新商品 到购物车(如果商品id 已经存在则更新,没有则新增)
+  void addToShoppingCart(String serviceProviderId, String productId, ProductItem productItem) {
+    Log.d('根据服务商id 添加商品 到购物车');
+    for (var serviceProviderItem in state.serviceProviderItemList!) {
+      if (serviceProviderItem.serviceProviderId == serviceProviderId) {
+        for (var productItem in serviceProviderItem.productItemList!) {
+          if (productItem.productId == productId) {
+            productItem = productItem;
+          }else {
+            serviceProviderItem.productItemList!.add(productItem);
+          }
+        }
+      }
+    }
+
+    state.copyWith(serviceProviderItemList: state.serviceProviderItemList);
+  }
+
+
+  // 根据服务商id 和 商品id 删除购物车(如果找到了商品则删除对应商品)
+  void deleteShoppingCart(String serviceProviderId, String productId) {
+    Log.d('根据服务商id 和 商品id 删除购物车');
+    for (var serviceProviderItem in state.serviceProviderItemList!) {
+      if (serviceProviderItem.serviceProviderId == serviceProviderId) {
+        for (var productItem in serviceProviderItem.productItemList!) {
+          if (productItem.productId == productId) {
+            serviceProviderItem.productItemList!.remove(productItem);
+          }
+        }
+      }
+    }
+
+    state = state.copyWith(serviceProviderItemList: state.serviceProviderItemList);
+  }
+
+  // 根据服务商id 和 商品id 更新购物车(如果找到对应商品则更新)
+  void updateShoppingCart(String serviceProviderId, String productId, ProductItem productItem) {
+    Log.d('根据服务商id 和 商品id 更新购物车');
+    for (var serviceProviderItem in state.serviceProviderItemList!) {
+      if (serviceProviderItem.serviceProviderId == serviceProviderId) {
+        for (var productItem in serviceProviderItem.productItemList!) {
+          if (productItem.productId == productId) {
+            productItem = productItem;
+          }
+        }
+      }
+    }
+
+    state = state.copyWith(serviceProviderItemList: state.serviceProviderItemList);
+  }
+
+
+  // 根据服务商id 和 商品id 获取商品
+  ProductItem? getProductItem(String serviceProviderId, String productId) {
+    Log.d('根据服务商id 和 商品id 获取商品');
+    for (var serviceProviderItem in state.serviceProviderItemList!) {
+      if (serviceProviderItem.serviceProviderId == serviceProviderId) {
+        for (var productItem in serviceProviderItem.productItemList!) {
+          if (productItem.productId == productId) {
+            return productItem;
+          }
+        }
+      }
+    }
+  }
+
+  // 根据服务商id 来获取商品列表
+  List<ProductItem>? getProductItemListByServiceProviderId(String serviceProviderId) {
+    Log.d('根据服务商id获取商品列表');
+    for (var serviceProviderItem in state.serviceProviderItemList!) {
+      if (serviceProviderItem.serviceProviderId == serviceProviderId) {
+        return serviceProviderItem.productItemList;
+      }
+    }
+    return null;
+  }
+}

+ 27 - 0
packages/cs_plugin_basic/lib/provider/shopping_cart/shopping_cart_service.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'shopping_cart_service.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$shoppingCartServiceHash() =>
+    r'35498f4945e50d89326279c459a790c22d9e4520';
+
+/// See also [ShoppingCartService].
+@ProviderFor(ShoppingCartService)
+final shoppingCartServiceProvider =
+    NotifierProvider<ShoppingCartService, ShoppingCart>.internal(
+  ShoppingCartService.new,
+  name: r'shoppingCartServiceProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$shoppingCartServiceHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$ShoppingCartService = Notifier<ShoppingCart>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 1 - 1
packages/cs_plugin_basic/lib/provider/user_config/user_config_service.g.dart

@@ -6,7 +6,7 @@ part of 'user_config_service.dart';
 // RiverpodGenerator
 // **************************************************************************
 
-String _$userConfigServiceHash() => r'9cb0a6024c41d061fe49b24ccf54dd8952279272';
+String _$userConfigServiceHash() => r'ed4cea1ce6a4b6de74408867602fd8f5efd3dbc2';
 
 /// See also [UserConfigService].
 @ProviderFor(UserConfigService)

BIN
packages/cs_resources/assets/service/detail01.png


BIN
packages/cs_resources/assets/service/detail02.png


BIN
packages/cs_resources/assets/service/detail03.png


BIN
packages/cs_resources/assets/service/detail04.png


BIN
packages/cs_resources/assets/service/detail05.png


BIN
packages/cs_resources/assets/service/detail06.png


BIN
packages/cs_resources/assets/service/service_icon_1.png


BIN
packages/cs_resources/assets/service/service_icon_10.png


BIN
packages/cs_resources/assets/service/service_icon_11.png


BIN
packages/cs_resources/assets/service/service_icon_12.png


BIN
packages/cs_resources/assets/service/service_icon_13.png


BIN
packages/cs_resources/assets/service/service_icon_14.png


BIN
packages/cs_resources/assets/service/service_icon_2.png


BIN
packages/cs_resources/assets/service/service_icon_3.png


BIN
packages/cs_resources/assets/service/service_icon_4.png


+ 0 - 0
packages/cs_resources/assets/service/service_icon_5.png


Some files were not shown because too many files changed in this diff