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

Merge branch 'master' of http://git.wmzhubo.com/guadoutech/YY-Hotel-Business-ER

glglove дней назад: 3
Родитель
Сommit
77e1633bb5
100 измененных файлов с 15164 добавлено и 27 удалено
  1. 3 0
      app/pubspec.yaml
  2. 1 0
      melos.yaml
  3. 3 0
      packages/cpt_auth/lib/modules/login/login_controller.dart
  4. 3 1
      packages/cpt_auth/lib/modules/login/login_page.dart
  5. 3 0
      packages/cpt_auth/lib/modules/select_country/select_country_controller.dart
  6. 31 0
      packages/cpt_auth/lib/modules/select_country/select_country_page.dart
  7. 17 15
      packages/cpt_auth/lib/modules/setting/setting_controller.dart
  8. 3 3
      packages/cpt_auth/lib/modules/setting/setting_page.dart
  9. 31 0
      packages/cpt_th/.gitignore
  10. 436 0
      packages/cpt_th/lib/modules/job_er/applied_er/applied_er_controller.dart
  11. 493 0
      packages/cpt_th/lib/modules/job_er/applied_er/applied_er_item.dart
  12. 255 0
      packages/cpt_th/lib/modules/job_er/applied_er/applied_er_page.dart
  13. 18 0
      packages/cpt_th/lib/modules/job_er/applied_er/applied_er_state.dart
  14. 344 0
      packages/cpt_th/lib/modules/job_er/applied_er/widget/er_applied_add_staff.dart
  15. 130 0
      packages/cpt_th/lib/modules/job_er/applied_er/widget/er_applied_add_staff_controller.dart
  16. 241 0
      packages/cpt_th/lib/modules/job_er/applied_er/widget/er_applied_butch_modify.dart
  17. 313 0
      packages/cpt_th/lib/modules/job_er/applied_er/widget/er_applied_staff_reviews.dart
  18. 195 0
      packages/cpt_th/lib/modules/job_er/applied_er_edit/applied_er_edit_controller.dart
  19. 564 0
      packages/cpt_th/lib/modules/job_er/applied_er_edit/applied_er_edit_page.dart
  20. 44 0
      packages/cpt_th/lib/modules/job_er/applied_er_edit/applied_er_edit_state.dart
  21. 162 0
      packages/cpt_th/lib/modules/job_er/applied_er_staff_detail/applied_er_staff_detail_controller.dart
  22. 138 0
      packages/cpt_th/lib/modules/job_er/applied_er_staff_detail/applied_er_staff_detail_page.dart
  23. 15 0
      packages/cpt_th/lib/modules/job_er/applied_er_staff_detail/applied_er_staff_detail_state.dart
  24. 292 0
      packages/cpt_th/lib/modules/job_er/applied_er_staff_detail/staff_detail_widget.dart
  25. 215 0
      packages/cpt_th/lib/modules/job_er/applied_er_staff_detail/staff_labour_history_item.dart
  26. 108 0
      packages/cpt_th/lib/modules/job_er/applied_er_staff_reviews/applied_er_staff_reviews_controller.dart
  27. 162 0
      packages/cpt_th/lib/modules/job_er/applied_er_staff_reviews/applied_er_staff_reviews_page.dart
  28. 10 0
      packages/cpt_th/lib/modules/job_er/applied_er_staff_reviews/applied_er_staff_reviews_state.dart
  29. 151 0
      packages/cpt_th/lib/modules/job_er/applied_er_staff_reviews/applied_staff_reviews_item.dart
  30. 98 0
      packages/cpt_th/lib/modules/job_er/applied_er_workflow/applied_er_workflow_controller.dart
  31. 102 0
      packages/cpt_th/lib/modules/job_er/applied_er_workflow/applied_er_workflow_page.dart
  32. 10 0
      packages/cpt_th/lib/modules/job_er/applied_er_workflow/applied_er_workflow_state.dart
  33. 228 0
      packages/cpt_th/lib/modules/job_er/applied_er_workflow/applied_workflow_item.dart
  34. 245 0
      packages/cpt_th/lib/modules/job_er/job_list_er/job_list_er_controller.dart
  35. 510 0
      packages/cpt_th/lib/modules/job_er/job_list_er/job_list_er_filter.dart
  36. 250 0
      packages/cpt_th/lib/modules/job_er/job_list_er/job_list_er_item.dart
  37. 155 0
      packages/cpt_th/lib/modules/job_er/job_list_er/job_list_er_page.dart
  38. 22 0
      packages/cpt_th/lib/modules/job_er/job_list_er/job_list_er_state.dart
  39. 391 0
      packages/cpt_th/lib/modules/labour/labour_request_add/labour_request_add_controller.dart
  40. 834 0
      packages/cpt_th/lib/modules/labour/labour_request_add/labour_request_add_page.dart
  41. 108 0
      packages/cpt_th/lib/modules/labour/labour_request_add/labour_request_add_state.dart
  42. 190 0
      packages/cpt_th/lib/modules/labour/labour_request_list/dialog/oa_attach_list_controller.dart
  43. 266 0
      packages/cpt_th/lib/modules/labour/labour_request_list/dialog/oa_attach_list_dialog.dart
  44. 428 0
      packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_filter.dart
  45. 364 0
      packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_item.dart
  46. 346 0
      packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_list_controller.dart
  47. 174 0
      packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_list_page.dart
  48. 20 0
      packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_list_state.dart
  49. 96 0
      packages/cpt_th/lib/modules/labour/labour_request_workflow/labour_request_workflow_controller.dart
  50. 102 0
      packages/cpt_th/lib/modules/labour/labour_request_workflow/labour_request_workflow_page.dart
  51. 10 0
      packages/cpt_th/lib/modules/labour/labour_request_workflow/labour_request_workflow_state.dart
  52. 230 0
      packages/cpt_th/lib/modules/labour/labour_request_workflow/labour_workflow_item.dart
  53. 354 0
      packages/cpt_th/lib/modules/labour/labour_review_list/labour_review_controller.dart
  54. 318 0
      packages/cpt_th/lib/modules/labour/labour_review_list/labour_review_filter.dart
  55. 288 0
      packages/cpt_th/lib/modules/labour/labour_review_list/labour_review_item.dart
  56. 189 0
      packages/cpt_th/lib/modules/labour/labour_review_list/labour_review_page.dart
  57. 190 0
      packages/cpt_th/lib/modules/labour/labour_review_list/labour_review_reject_dialog.dart
  58. 16 0
      packages/cpt_th/lib/modules/labour/labour_review_list/labour_review_state.dart
  59. 102 0
      packages/cpt_th/lib/modules/labour/labour_review_workflow/labour_request_workflow_page.dart
  60. 10 0
      packages/cpt_th/lib/modules/labour/labour_review_workflow/labour_request_workflow_state.dart
  61. 96 0
      packages/cpt_th/lib/modules/labour/labour_review_workflow/labour_review_workflow_controller.dart
  62. 228 0
      packages/cpt_th/lib/modules/labour/labour_review_workflow/labour_review_workflow_item.dart
  63. 126 0
      packages/cpt_th/lib/modules/labour/labour_template_add/labour_template_add_controller.dart
  64. 327 0
      packages/cpt_th/lib/modules/labour/labour_template_add/labour_template_add_page.dart
  65. 45 0
      packages/cpt_th/lib/modules/labour/labour_template_add/labour_template_add_state.dart
  66. 7 0
      packages/cpt_th/lib/modules/labour/labour_template_list/add_edit_template.dart
  67. 175 0
      packages/cpt_th/lib/modules/labour/labour_template_list/labour_template_item.dart
  68. 215 0
      packages/cpt_th/lib/modules/labour/labour_template_list/labour_template_list_controller.dart
  69. 155 0
      packages/cpt_th/lib/modules/labour/labour_template_list/labour_template_list_page.dart
  70. 13 0
      packages/cpt_th/lib/modules/labour/labour_template_list/labour_template_list_state.dart
  71. 122 0
      packages/cpt_th/lib/modules/labour_er/labour_request_er/labour_request_item.dart
  72. 150 0
      packages/cpt_th/lib/modules/labour_er/labour_request_er/labour_request_list_controller.dart
  73. 158 0
      packages/cpt_th/lib/modules/labour_er/labour_request_er/labour_request_list_page.dart
  74. 10 0
      packages/cpt_th/lib/modules/labour_er/labour_request_er/labour_request_list_state.dart
  75. 311 0
      packages/cpt_th/lib/modules/labour_er/labour_request_er_add/labour_request_er_add_controller.dart
  76. 599 0
      packages/cpt_th/lib/modules/labour_er/labour_request_er_add/labour_request_er_add_page.dart
  77. 74 0
      packages/cpt_th/lib/modules/labour_er/labour_request_er_add/labour_request_er_add_state.dart
  78. 164 0
      packages/cpt_th/lib/modules/main/main_controller.dart
  79. 46 0
      packages/cpt_th/lib/modules/main/main_item_module.dart
  80. 198 0
      packages/cpt_th/lib/modules/main/main_page.dart
  81. 21 0
      packages/cpt_th/lib/modules/main/main_state.dart
  82. 0 0
      packages/cpt_th/lib/modules/th_export.dart
  83. 122 0
      packages/cpt_th/lib/router/th_router.dart
  84. 23 0
      packages/cpt_th/lib/router/th_service_impl.dart
  85. 40 0
      packages/cpt_th/pubspec.yaml
  86. 16 0
      packages/cpt_th/pubspec_overrides.yaml
  87. 137 1
      packages/cs_domain/lib/constants/api_constants.dart
  88. 5 0
      packages/cs_domain/lib/entity/response/hotel_info_entity.dart
  89. 1 1
      packages/cs_domain/lib/entity/response/job_list_applied_work_flow_entity.dart
  90. 117 0
      packages/cs_domain/lib/entity/response/job_list_e_r_detail_entity.dart
  91. 51 0
      packages/cs_domain/lib/entity/response/job_list_e_r_option_entity.dart
  92. 6 6
      packages/cs_domain/lib/entity/response/job_list_entity.dart
  93. 3 0
      packages/cs_domain/lib/entity/response/job_list_remark_view_entity.dart
  94. 59 0
      packages/cs_domain/lib/entity/response/labour_list_e_r_entity.dart
  95. 80 0
      packages/cs_domain/lib/entity/response/labour_request_e_r_entity.dart
  96. 42 0
      packages/cs_domain/lib/entity/response/labour_request_e_r_option_entity.dart
  97. 3 0
      packages/cs_domain/lib/entity/response/labour_request_index_entity.dart
  98. 2 0
      packages/cs_domain/lib/entity/response/staff_labour_history_entity.dart
  99. 190 0
      packages/cs_domain/lib/entity/response/t_h_applied_edit_entity.dart
  100. 0 0
      packages/cs_domain/lib/entity/response/t_h_applied_employee_entity.dart

+ 3 - 0
app/pubspec.yaml

@@ -55,6 +55,9 @@ dependencies:
   cpt_ms:
     path: ../packages/cpt_ms
 
+  cpt_th:
+    path: ../packages/cpt_th
+
   initializer:
     path: ../packages/cs_initializer
 

+ 1 - 0
melos.yaml

@@ -15,6 +15,7 @@ packages:
   - "packages/cpt_sg/"
   - "packages/cpt_nl/"
   - "packages/cpt_ms/"
+  - "packages/cpt_th/"
 
 
 command:

+ 3 - 0
packages/cpt_auth/lib/modules/login/login_controller.dart

@@ -101,6 +101,9 @@ class LoginController extends GetxController with DioCancelableMixin {
     } else if (ConfigService.to.selectCountry.value == 4) {
       //去荷兰首页
       ComponentRouterServices.nlService.startNLMainPage();
+    } else if (ConfigService.to.selectCountry.value == 5) {
+      //去泰国首页
+      ComponentRouterServices.thService.startTHMainPage();
     } else {
       //去越南首页
       ComponentRouterServices.vnService.startVNMainPage();

+ 3 - 1
packages/cpt_auth/lib/modules/login/login_page.dart

@@ -110,7 +110,9 @@ class _LoginPageState extends BaseState<LoginPage, LoginController> with StateLi
                                       ? "Malaysia".tr
                                       : ConfigService.to.selectCountry.value == 4
                                           ? "Netherlands".tr
-                                          : "Vietnam".tr,
+                                          : ConfigService.to.selectCountry.value == 5
+                                              ? "thailand".tr
+                                              : "Vietnam".tr,
                           textColor: ColorConstants.white,
                           isFontMedium: true,
                           marginRight: 5,

+ 3 - 0
packages/cpt_auth/lib/modules/select_country/select_country_controller.dart

@@ -25,6 +25,9 @@ class SelectCountryController extends GetxController {
     } else if (country == 4) {
       //荷兰
       baseUrl = ApiConstants.nlBaseUrl;
+    } else if (country == 5) {
+      //泰国
+      baseUrl = ApiConstants.thBaseUrl;
     } else {
       //默认是越南
       baseUrl = ApiConstants.vnBaseUrl;

+ 31 - 0
packages/cpt_auth/lib/modules/select_country/select_country_page.dart

@@ -185,6 +185,37 @@ class SelectCountryPage extends BaseStatelessPage<SelectCountryController> {
                         ConfigService.to.selectCountry.value = 3;
                       }),
 
+                      //泰国
+                      Container(
+                        width: double.infinity,
+                        margin: const EdgeInsets.only(top: 13.5, left: 20, right: 20),
+                        padding: const EdgeInsets.symmetric(vertical: 13, horizontal: 17),
+                        decoration: BoxDecoration(
+                          color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+                          borderRadius: BorderRadius.circular(5.0), // 设置圆角
+                        ),
+                        child: Row(
+                          children: [
+                            const MyAssetImage(Assets.cptAuthThIcon, width: 50, height: 33),
+                            MyTextView(
+                              "thailand".tr,
+                              marginLeft: 17,
+                              textColor: ColorConstants.white,
+                              isFontMedium: true,
+                              fontSize: 18,
+                            ).expanded(),
+                            Obx(() {
+                              return Visibility(
+                                visible: ConfigService.to.selectCountry.value == 5,
+                                child: const MyAssetImage(Assets.cptAuthCheckedIcon, width: 22, height: 22),
+                              );
+                            }),
+                          ],
+                        ),
+                      ).onTap(() {
+                        ConfigService.to.selectCountry.value = 5;
+                      }),
+
                       //荷兰
                       Container(
                         width: double.infinity,

+ 17 - 15
packages/cpt_auth/lib/modules/setting/setting_controller.dart

@@ -13,7 +13,6 @@ import 'package:plugin_platform/engine/notify/notify_engine.dart';
 import 'package:plugin_platform/engine/toast/toast_engine.dart';
 import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
 import 'package:shared/utils/event_bus.dart';
-import 'package:shared/utils/util.dart';
 import 'package:widgets/dialog/app_default_dialog.dart';
 
 import 'setting_state.dart';
@@ -25,8 +24,7 @@ class SettingController extends GetxController with DioCancelableMixin {
 
   //切换账号
   void switchProjects() async {
-
-    final result = await _msRepository.fetchSwitchProjectList(cancelToken: cancelToken);
+    final result = await _msRepository.fetchSwitchProjectList(country: ConfigService.to.selectCountry.value, cancelToken: cancelToken);
 
     if (result.isSuccess) {
       final projects = result.data?.cutUser;
@@ -45,25 +43,22 @@ class SettingController extends GetxController with DioCancelableMixin {
         widget: SwitchProjectDialog(
       options: projects,
       confirmAction: (entity) {
-
         _requestSwitchProject(entity);
       },
     ));
   }
 
-  void _requestSwitchProject(IndexOptionEntity entity) async{
-    final result = await _msRepository.switchProjectSubmit(entity.value,cancelToken: cancelToken);
+  void _requestSwitchProject(IndexOptionEntity entity) async {
+    final result = await _msRepository.switchProjectSubmit(entity.value, country: ConfigService.to.selectCountry.value, cancelToken: cancelToken);
 
     if (result.isSuccess) {
-
       NotifyEngine.showSuccess('successful'.tr);
 
-     final token =  result.data?.token;
+      final token = result.data?.token;
       UserService.to.setToken(token);
 
       //发送通知刷新首页
       bus.emit(AppConstant.eventMainRefresh, true);
-
     } else {
       final errorMessage = result.errorMsg;
       ToastEngine.show(errorMessage ?? "Network Load Error".tr);
@@ -114,16 +109,23 @@ class SettingController extends GetxController with DioCancelableMixin {
 
   /// 请求接口退出账号
   void _requestLogout() async {
-    var result = await _authRepository.userLogout(country: ConfigService.to.selectCountry.value, cancelToken: cancelToken);
-
-    //处理数据
-    if (result.isSuccess) {
+    if (ConfigService.to.selectCountry.value == 5) {
+      //直接退出
       //清除数据,去首页
       UserService.to.handleLogoutParams();
       LoginPage.startWithPopAll();
     } else {
-      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+      //请求接口
+      var result = await _authRepository.userLogout(country: ConfigService.to.selectCountry.value, cancelToken: cancelToken);
+
+      //处理数据
+      if (result.isSuccess) {
+        //清除数据,去首页
+        UserService.to.handleLogoutParams();
+        LoginPage.startWithPopAll();
+      } else {
+        ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+      }
     }
   }
-
 }

+ 3 - 3
packages/cpt_auth/lib/modules/setting/setting_page.dart

@@ -59,16 +59,16 @@ class SettingPage extends BaseStatelessPage<SettingController> {
           child: Column(children: [
             const SizedBox(height: 10),
 
-          ConfigService.to.selectCountry.value == 3 ?
+          ConfigService.to.selectCountry.value == 3  ||  ConfigService.to.selectCountry.value == 5 ?
           SettingItemContainer(iconPath: Assets.mainSettingSwitchProject, title: "Switch Projects".tr).onTap(() {
               controller.switchProjects();
             }) : const SizedBox(),
 
-            ConfigService.to.selectCountry.value != 3 ? SettingItemContainer(iconPath: Assets.mainSettingResetPassword, title: "Reset Password".tr).onTap(() {
+            ConfigService.to.selectCountry.value != 3 &&  ConfigService.to.selectCountry.value != 5 ? SettingItemContainer(iconPath: Assets.mainSettingResetPassword, title: "Reset Password".tr).onTap(() {
               controller.gotoResetPasswordPage();
             }): const SizedBox(),
 
-            ConfigService.to.selectCountry.value != 3 ? SettingItemContainer(iconPath: Assets.mainSettingAccountDelectivation, title: "Account Deactivation".tr).onTap(() {
+            ConfigService.to.selectCountry.value != 3 &&  ConfigService.to.selectCountry.value != 5 ? SettingItemContainer(iconPath: Assets.mainSettingAccountDelectivation, title: "Account Deactivation".tr).onTap(() {
               controller.doAccountDelete();
             }): const SizedBox(),
 

+ 31 - 0
packages/cpt_th/.gitignore

@@ -0,0 +1,31 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+*.lock
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# Visual Studio Code related
+.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+proguardMapping.txt

+ 436 - 0
packages/cpt_th/lib/modules/job_er/applied_er/applied_er_controller.dart

@@ -0,0 +1,436 @@
+import 'package:cpt_th/modules/job_er/applied_er_edit/applied_er_edit_page.dart';
+import 'package:domain/entity/response/t_h_applied_index_entity.dart';
+import 'package:domain/entity/response/t_h_applied_table_entity.dart';
+import 'package:domain/repository/th_er_repository.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/constants/app_constant.dart';
+import 'package:plugin_platform/engine/dialog/dialog_engine.dart';
+import 'package:plugin_platform/engine/notify/notify_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:plugin_platform/http/http_result.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:shared/utils/event_bus.dart';
+import 'package:shared/utils/util.dart';
+import 'package:widgets/dialog/app_default_dialog.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/picker/option_pick_util.dart';
+import 'package:widgets/widget_export.dart';
+import '../applied_er_staff_detail/applied_er_staff_detail_page.dart';
+import '../applied_er_workflow/applied_er_workflow_page.dart';
+import 'applied_er_state.dart';
+import 'widget/er_applied_add_staff.dart';
+import 'widget/er_applied_butch_modify.dart';
+import 'widget/er_applied_staff_reviews.dart';
+
+class AppliedErController extends GetxController with DioCancelableMixin {
+  final THERRepository _erRepository = Get.find();
+  final AppliedErState state = AppliedErState();
+
+  var _curPage = 1;
+  var _needShowPlaceholder = true;
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Success;
+  String? errorMessage;
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,
+    controlFinishLoad: true,
+  );
+
+  // Refresh 刷新事件
+  Future onRefresh() async {
+    _curPage = 1;
+    fetchAppliedStaffList();
+  }
+
+  // Refresh 加载事件
+  Future loadMore() async {
+    _curPage++;
+    fetchAppliedStaffList();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _curPage = 1;
+    _needShowPlaceholder = true;
+    fetchAppliedStaffList();
+  }
+
+  /// 获取服务器数据,通知消息列表
+  Future fetchAppliedStaffList() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    }
+
+    // 并发执行两个请求
+    var futures = [
+      _erRepository.fetchERAppliedTable(
+        jobId: state.jobId,
+        curPage: _curPage,
+        status: state.selectedStatusId,
+        keyword: state.keyword,
+        cancelToken: cancelToken,
+      ),
+      state.jobInfo == null
+          ? _erRepository.fetchERAppliedOption(
+              jobId: state.jobId,
+              cancelToken: cancelToken,
+            )
+          : Future(() => HttpResult(isSuccess: true).convert(data: state.jobInfo!)),
+    ];
+
+    //拿到结果
+    var results = await Future.wait(futures);
+    var listResult = results[0] as HttpResult<THAppliedTableEntity>;
+    var optionResult = results[1] as HttpResult<THAppliedIndexEntity>;
+
+    //选项数据
+    if (state.jobInfo == null && optionResult.isSuccess) {
+      state.jobInfo = optionResult.data!;
+    }
+
+    // 处理数据
+    if (listResult.isSuccess) {
+      handleList(listResult.data?.rows);
+    } else {
+      errorMessage = listResult.errorMsg;
+      changeLoadingState(LoadState.State_Error);
+    }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void handleList(List<THAppliedTableRows>? list) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      if (_curPage == 1) {
+        //刷新的方式
+        state.datas.clear();
+        state.datas.addAll(list);
+        refreshController.finishRefresh();
+
+        //更新展示的状态
+        changeLoadingState(LoadState.State_Success);
+      } else {
+        //加载更多
+        state.datas.addAll(list);
+        refreshController.finishLoad();
+        update();
+      }
+    } else {
+      if (_curPage == 1) {
+        //展示无数据的布局
+        state.datas.clear();
+        changeLoadingState(LoadState.State_Empty);
+        refreshController.finishRefresh();
+      } else {
+        //展示加载完成,没有更多数据了
+        refreshController.finishLoad(IndicatorResult.noMore);
+      }
+    }
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    fetchAppliedStaffList();
+    registerEventBus();
+  }
+
+  @override
+  void onClose() {
+    unregisterEventBus();
+    state.datas.clear();
+    super.onClose();
+  }
+
+  // EventBus 的事件接收
+  Subscription? subscribe;
+
+  void registerEventBus() {
+    subscribe = bus.on(AppConstant.eventAppliedListRefresh, (arg) {
+      var appliedId = arg as String;
+      if (Utils.isNotEmpty(appliedId)) {
+        fetchItemByIdAndRefreshItem(appliedId);
+      } else {
+        refreshController.callRefresh();
+      }
+    });
+  }
+
+  void unregisterEventBus() {
+    bus.off(AppConstant.eventAppliedListRefresh, subscribe);
+  }
+
+  /// 搜索员工
+  void doSearch(String keyword) {
+    state.keyword = keyword;
+    //赋值之后刷新
+    refreshController.callRefresh();
+  }
+
+  /// 清空筛选条件
+  void resetFiltering() {
+    state.keyword = "";
+    state.searchController.text = "";
+    state.selectedStatusId = null;
+    state.selectedStatusName = null;
+    //赋值之后刷新
+    refreshController.callRefresh();
+  }
+
+  /// 展示添加员工的弹窗
+  void showAddStaffDialog() {
+    DialogEngine.show(
+      widget: ErAppliedAddStaff(
+        jobId: state.jobId!,
+        confirmAction: (selectedIds) {
+          //调用接口添加员工
+          _requestAddStaff2Applied(selectedIds);
+        },
+      ),
+    );
+  }
+
+  //调用接口添加员工
+  void _requestAddStaff2Applied(String selectedIds) async {
+    var result = await _erRepository.addStaff2Job(state.jobId, selectedIds, cancelToken: cancelToken);
+
+    if (result.isSuccess) {
+      var addStaffEntity = result.data;
+      List<String> filteredMessages = [];
+      if (addStaffEntity != null && addStaffEntity.resultList?.isNotEmpty == true) {
+        filteredMessages = addStaffEntity.resultList!
+            .where((resultList) => resultList.result == false && Utils.isNotEmpty(resultList.msg))
+            .map((resultList) => '${resultList.name} : ${resultList.msg!}')
+            .toList();
+      }
+
+      if (filteredMessages.isNotEmpty) {
+        //有错误信息
+        NotifyEngine.showFailure(filteredMessages.join(" , "));
+      } else {
+        //无错误信息
+        NotifyEngine.showSuccess("Successful".tr);
+        //添加成功之后刷新页面
+        refreshController.callRefresh();
+      }
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+      return;
+    }
+  }
+
+  /// 去编辑员工信息页面
+  void gotoAppliedEditPage(THAppliedTableRows data) {
+    AppliedErEditPage.startInstance(data.id.toString());
+  }
+
+  /// 展示评论的弹窗
+  void showRemarkDialog(THAppliedTableRows data) async {
+    // 请求接口获取到已评论的数据
+    var result = await _erRepository.fetchAppliedStaffReviewView(data.id.toString());
+
+    if (result.isSuccess) {
+      //接口数据获取成功,展示弹窗
+      DialogEngine.show(
+        widget: ErAppliedStaffReviews(
+          appliedReviews: result.data!,
+          confirmAction: (attitudeRate, performanceRate, experienceRate, groomingRate, content) async {
+            //请求接口,提交评论
+            var submitResult = await _erRepository.remarkAppliedSingleStaffSubmit(
+              data.id,
+              attitudeRate,
+              groomingRate,
+              performanceRate,
+              experienceRate,
+              content,
+            );
+
+            if (submitResult.isSuccess) {
+              NotifyEngine.showSuccess("Successful".tr);
+            } else {
+              ToastEngine.show(submitResult.errorMsg ?? "Network Load Error".tr);
+            }
+          },
+        ),
+      );
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+      return;
+    }
+  }
+
+  /// 去展示员工状态的审核流程页面
+  void gotoAppliedWorkflowPage(THAppliedTableRows data) {
+    AppliedErWorkflowPage.startInstance(data.id.toString());
+  }
+
+  /// 去查看员工详情页面
+  void gotoStaffDetailPage(THAppliedTableRows data) {
+    AppliedErStaffDetailPage.startInstance(data.memberId);
+  }
+
+  /// Item选中与未选中设置
+  void doSelectedOrNot(THAppliedTableRows data) {
+    //只有 Approve = 3 的状态才能选中
+    // if (data.status == 3) {
+    data.isSelected = !data.isSelected;
+    update();
+    // }
+  }
+
+  /// 批量修改的弹窗
+  void showBatchModifyDialog() async {
+    // if (state.jobInfo == null) return;
+    //找出已经选中的员工(只有状态为3 Approve的状态才能修改)
+    var selectedList = state.datas.where((element) => element.isSelected /*&& element.status == 3*/).toList(growable: false);
+    if (selectedList.isNotEmpty) {
+      var ids = selectedList.map((e) => e.id.toString()).toList(growable: false);
+      var separatedIds = ids.join(',');
+
+      DialogEngine.show(
+          widget: ErAppliedButchModify(
+              selectedStartDate: DateTimeUtils.getDateTimeByMs((state.jobInfo?.job?.startTime ?? 0) * 1000),
+              selectedEndDate: DateTimeUtils.getDateTimeByMs((state.jobInfo?.job?.endTime ?? 0) * 1000),
+              confirmAction: (start, end) {
+                _requestBatchModify(start, end, separatedIds);
+              }));
+    } else {
+      ToastEngine.show("Please select the applied record".tr);
+    }
+  }
+
+  /// 执行批量修改的请求
+  void _requestBatchModify(DateTime start, DateTime end, String separatedIds) async {
+    //执行请求
+    var result = await _erRepository.batchEditStaffCheckTime(
+      separatedIds,
+      DateTimeUtils.formatDate(start, format: 'HH:mm:ss'),
+      DateTimeUtils.formatDate(end, format: 'HH:mm:ss'),
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess("Successful".tr);
+
+      //调用接口刷新指定的Staff的信息
+      fetchItemByIdAndRefreshItem(separatedIds);
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+      return;
+    }
+  }
+
+  /// 批准的操作
+  void operationApprove() async {
+    // 找出已经选中的员工(只有状态为3 Approve的状态才能修改)
+    var selectedList = state.datas.where((element) => element.isSelected /*&& element.status == 3*/).toList(growable: false);
+    if (selectedList.isNotEmpty) {
+      var ids = selectedList.map((e) => e.id.toString()).toList(growable: false);
+      var separatedIds = ids.join(',');
+
+      // Are you sure 的弹窗
+      DialogEngine.show(
+        widget: AppDefaultDialog(
+          title: "Message".tr,
+          message: "Are you sure you want to setting approved?".tr,
+          confirmAction: () async {
+            //执行请求
+            var result = await _erRepository.submitBatchStaffApprove(separatedIds, cancelToken: cancelToken);
+
+            if (result.isSuccess) {
+              NotifyEngine.showSuccess("Successful".tr);
+
+              //调用接口刷新指定的Staff的信息
+              fetchItemByIdAndRefreshItem(separatedIds);
+            } else {
+              ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+              return;
+            }
+          },
+        ),
+      );
+    } else {
+      ToastEngine.show("Please select the applied record".tr);
+    }
+  }
+
+  /// 根据ID获取Item对象,用于刷新
+  void fetchItemByIdAndRefreshItem(String appliedIds) async {
+    var result = await _erRepository.fetchItemByAppliedIds(
+      state.jobId,
+      appliedIds,
+      cancelToken: cancelToken,
+    );
+
+    //处理数据
+    if (result.isSuccess) {
+      var data = result.data;
+      if (data != null && data.rows.isNotEmpty) {
+        List<THAppliedTableRows> newItem = data.rows;
+
+        // 创建一个 Map 来加速查找
+        Map<String, THAppliedTableRows> newItemMap = {for (var item in newItem) item.id ?? "": item};
+
+        // 遍历 state.datas 进行替换
+        for (int i = 0; i < state.datas.length; i++) {
+          if (newItemMap.containsKey(state.datas[i].id)) {
+            state.datas[i] = newItemMap[state.datas[i].id]!;
+          }
+        }
+
+        //刷新
+        update();
+      }
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+  }
+
+  //全部选中
+  void operationSelectAll() {
+    for (var element in state.datas) {
+      element.isSelected = true;
+    }
+    update();
+  }
+
+  /// 筛选状态
+  void pickerStatus() {
+    if (state.jobInfo == null) return;
+
+    int selectedStatusIndex;
+    if (state.selectedStatusId == null) {
+      selectedStatusIndex = 0;
+    } else {
+      selectedStatusIndex = state.jobInfo!.statusList.indexWhere((department) => department.value.toString() == state.selectedStatusId);
+    }
+
+    if (selectedStatusIndex < 0) {
+      selectedStatusIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: state.jobInfo!.statusList.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedStatusIndex,
+      onPickerChanged: (_, index) {
+        state.selectedStatusId = state.jobInfo!.statusList[index].value!.toString();
+        state.selectedStatusName = state.jobInfo!.statusList[index].txt!.toString();
+        update();
+        refreshController.callRefresh();
+      },
+    );
+  }
+}

+ 493 - 0
packages/cpt_th/lib/modules/job_er/applied_er/applied_er_item.dart

@@ -0,0 +1,493 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/response/t_h_applied_index_entity.dart';
+import 'package:domain/entity/response/t_h_applied_table_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:plugin_basic/service/app_config_service.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+/*
+ * 已申请的员工列表Item
+ */
+class AppliedErItem extends StatelessWidget {
+  final int index;
+  final THAppliedIndexEntity? jobInfo;
+  final THAppliedTableRows item;
+  final VoidCallback? onStatusAction;
+  final VoidCallback? onEditAction;
+  final VoidCallback? onRemarkAction;
+  final VoidCallback? onItemAction;
+  final VoidCallback? onMemberAction;
+
+  AppliedErItem({
+    required this.index,
+    required this.item,
+    required this.jobInfo,
+    this.onStatusAction,
+    this.onRemarkAction,
+    this.onEditAction,
+    this.onItemAction,
+    this.onMemberAction,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      padding: const EdgeInsets.symmetric(vertical: 23, horizontal: 21),
+      margin: const EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 5),
+      decoration: BoxDecoration(
+        color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+        borderRadius: BorderRadius.circular(5), // 设置圆角
+      ),
+      child: Column(
+        mainAxisSize: MainAxisSize.max,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          //员工姓名
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Staff Name:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //姓名
+              MyTextView(
+                item.labourerName ?? "-",
+                isFontMedium: true,
+                textColor: ColorConstants.textYellowFFBB1B,
+                fontSize: 14,
+                marginLeft: 5,
+                marginRight: 5,
+                textDecoration: TextDecoration.underline,
+                decorationColor: ColorConstants.textYellowFFBB1B,
+                // 可选,设置下划线的颜色
+                decorationThickness: 2.0,
+                // 可选,设置下划线的粗细
+                decorationStyle: TextDecorationStyle.solid,
+                onClick: onMemberAction,
+              ).expanded(),
+
+              //是否选中
+              MyAssetImage(
+                item.isSelected ? Assets.baseServiceItemSelectedIcon : Assets.baseServiceItemUnselectedIcon,
+                width: 20.5,
+                height: 20.5,
+              ),
+            ],
+          ),
+
+          //头像
+          // Visibility(
+          //   visible: ConfigService.to.isTHOAType,
+          //   child: Row(
+          //     mainAxisSize: MainAxisSize.max,
+          //     crossAxisAlignment: CrossAxisAlignment.center,
+          //     children: [
+          //       MyTextView(
+          //         "Avatar:",
+          //         isFontRegular: true,
+          //         textColor: ColorConstants.textGrayAECAE5,
+          //         fontSize: 14,
+          //       ),
+          //
+          //     ],
+          //   ).marginOnly(top: 12),
+          // ),
+
+          // 服装大小
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "${"Clothing Size".tr}:",
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //姓名
+              MyTextView(
+                item.clothingSize ?? "-",
+                isFontMedium: true,
+                textColor: ColorConstants.textYellowFFBB1B,
+                fontSize: 14,
+                marginLeft: 5,
+                marginRight: 5,
+                textDecoration: TextDecoration.underline,
+                decorationColor: ColorConstants.textYellowFFBB1B,
+                // 可选,设置下划线的颜色
+                decorationThickness: 2.0,
+                // 可选,设置下划线的粗细
+                decorationStyle: TextDecorationStyle.solid,
+                onClick: onMemberAction,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 12),
+
+          // 工作开始时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "${"ID Card No.".tr}:",
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //时间
+              MyTextView(
+                item.labourerNric ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 12),
+
+          // 工作开始时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Start Time:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //时间
+              MyTextView(
+                item.startTime ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 12),
+
+          // 门卫签到时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Security In:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //时间
+              MyTextView(
+                item.securityIn?.time ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: item.securityIn?.changed == 1 ? ColorConstants.textRedFF6262 : Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 12),
+
+          // 工作地签到时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Work In:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //时间
+              MyTextView(
+                item.workIn?.time ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: item.workIn?.changed == 1 ? ColorConstants.textRedFF6262 : Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 12),
+
+          //工作结束时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "End Time:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //时间
+              MyTextView(
+                item.endTime ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 12),
+
+          // 工作地签出时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Work Out:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //时间
+              MyTextView(
+                item.workOut?.time ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: item.workOut?.changed == 1 ? ColorConstants.textRedFF6262 : Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 12),
+
+          // 门卫签出时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Security Out:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //时间
+              MyTextView(
+                item.securityOut?.time ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: item.securityOut?.changed == 1 ? ColorConstants.textRedFF6262 : Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 12),
+
+          // + - Hours
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "+/- Hours:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //小时
+              MyTextView(
+                item.adjustHours ?? "0",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 12),
+
+          // Total Hours
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Total Hours:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //小时
+              MyTextView(
+                item.totalHours ?? "0",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 12),
+
+          // // Total Rooms
+          // Visibility(
+          //   visible: jobInfo?.jobUnit != "hour",
+          //   child: Row(
+          //     mainAxisSize: MainAxisSize.max,
+          //     crossAxisAlignment: CrossAxisAlignment.center,
+          //     children: [
+          //       MyTextView(
+          //         "Total Rooms:".tr,
+          //         isFontRegular: true,
+          //         textColor: ColorConstants.textGrayAECAE5,
+          //         fontSize: 14,
+          //       ),
+          //
+          //       //小时
+          //       MyTextView(
+          //         item.totalRooms.toString(),
+          //         marginLeft: 5,
+          //         isFontRegular: true,
+          //         textColor: Colors.white,
+          //         fontSize: 14,
+          //       ).expanded(),
+          //     ],
+          //   ).marginOnly(top: 12),
+          // ),
+
+          // // 申请时间
+          // Row(
+          //   mainAxisSize: MainAxisSize.max,
+          //   crossAxisAlignment: CrossAxisAlignment.center,
+          //   children: [
+          //     MyTextView(
+          //       "Applied At:".tr,
+          //       isFontRegular: true,
+          //       textColor: ColorConstants.textGrayAECAE5,
+          //       fontSize: 14,
+          //     ),
+          //
+          //     //发布状态
+          //     MyTextView(
+          //       item.appliedAt ?? "-",
+          //       marginLeft: 5,
+          //       isFontRegular: true,
+          //       textColor: Colors.white,
+          //       fontSize: 14,
+          //     ).expanded(),
+          //   ],
+          // ).marginOnly(top: 12),
+
+          // 状态
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Status:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //发布状态
+              MyTextView(
+                item.statusShow ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: "Completed" == item.statusShow
+                    ? ColorConstants.textGreen05DC82
+                    : "Cancelled" == item.statusShow || "Rejected" == item.statusShow
+                        ? ColorConstants.textRedFF6262
+                        : "Revised" == item.statusShow || "Pending" == item.statusShow || "Approve" == item.statusShow
+                            ? ColorConstants.textYellowFFBB1B
+                            : ColorConstants.textBlue06D9FF, //默认蓝色
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 12),
+
+          //按钮组
+          Visibility(
+            visible: true,
+            child: Row(
+              mainAxisSize: MainAxisSize.max,
+              mainAxisAlignment: MainAxisAlignment.end,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+                //编辑按钮
+                Visibility(
+                  visible: true,
+                  child: MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      onEditAction?.call();
+                    },
+                    text: "Edit".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor(
+                      "#FFBB1B",
+                    ),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 35,
+                  ).marginOnly(left: 12),
+                ),
+
+                //状态工作流按钮
+                Visibility(
+                  visible: true,
+                  child: MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      onStatusAction?.call();
+                    },
+                    text: "Status".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#0AC074"),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 35,
+                  ).marginOnly(left: 12),
+                ),
+
+                //Remark按钮
+                Visibility(
+                  visible: true,
+                  child: MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      onRemarkAction?.call();
+                    },
+                    text: "Remarks".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#56AAFF"),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 35,
+                  ).marginOnly(left: 12),
+                ),
+              ],
+            ).marginOnly(top: 15),
+          ),
+        ],
+      ).onTap(() {
+        onItemAction?.call();
+      }),
+    );
+  }
+}

+ 255 - 0
packages/cpt_th/lib/modules/job_er/applied_er/applied_er_page.dart

@@ -0,0 +1,255 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.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_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/search_app_bar.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'applied_er_item.dart';
+import 'applied_er_controller.dart';
+
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/my_appbar.dart';
+import 'applied_er_state.dart';
+
+/*
+ * 已申请的页面,可以签到签出、加人、修改状态、评论等操作
+ */
+class AppliedErPage extends BaseStatefulPage<AppliedErController> {
+  AppliedErPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance(String? jobId) {
+    return Get.start(RouterPath.THJobAppliedER, arguments: {'jobId': jobId});
+  }
+
+  @override
+  AppliedErController createRawController() {
+    return AppliedErController();
+  }
+
+  @override
+  State<AppliedErPage> createState() => _JobAppliedState();
+}
+
+class _JobAppliedState extends BaseState<AppliedErPage, AppliedErController> {
+  late AppliedErState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+    state.jobId = Get.arguments['jobId'];
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return SafeArea(
+        bottom: MediaQuery.of(context).padding.bottom > 38,
+        top: false,
+        child: Container(
+          width: double.infinity,
+          height: double.infinity,
+          padding: EdgeInsets.only(top: ScreenUtil.getStatusBarH(context)),
+          decoration: const BoxDecoration(
+            gradient: LinearGradient(
+              colors: [
+                Color(0xFF091D44),
+                Color(0xFF245A8A),
+                Color(0xFF7F7CEC),
+              ],
+              begin: Alignment.topCenter,
+              end: Alignment.bottomCenter,
+            ),
+          ),
+          child: Column(
+            children: [
+              MyAppBar.titleBar(
+                context,
+                state.jobInfo?.job?.jobTitle ?? "Title".tr,
+                subTitle: "(${state.jobInfo?.jobDate ?? "-"})",
+                subTitleColor: ColorConstants.textGrayAECAE5,
+              ),
+
+              //搜索的条件
+              Row(
+                children: [
+                  Container(
+                    padding: const EdgeInsets.symmetric(vertical: 7.5, horizontal: 10),
+                    margin: const EdgeInsets.only(left: 0, right: 10),
+                    decoration: BoxDecoration(
+                      color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+                      borderRadius: BorderRadius.circular(20), // 设置圆角
+                    ),
+                    child: Row(
+                      children: [
+                        MyTextView(
+                          state.selectedStatusName ?? "",
+                          hint: "Status".tr,
+                          marginLeft: 10,
+                          textColor: ColorConstants.white,
+                          textHintColor: ColorConstants.textGrayAECAE5,
+                          fontSize: 15,
+                          isFontRegular: true,
+                        ).expanded(),
+                        const MyAssetImage(
+                          Assets.baseServiceTriangleDropDown,
+                          width: 8,
+                          height: 5,
+                        ),
+                      ],
+                    ).onTap(controller.pickerStatus),
+                  ).expanded(),
+                  SearchAppBar(
+                    value: state.keyword,
+                    onSearch: (keyword) {
+                      controller.doSearch(keyword);
+                    },
+                    hintText: "Staff Name".tr,
+                    controller: state.searchController,
+                  ).expanded(),
+                  MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      controller.resetFiltering();
+                    },
+                    text: "Reset".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#2BA9F9", opacity: 0.5),
+                    radius: 20,
+                    minWidth: 60,
+                    minHeight: 35,
+                  ),
+                ],
+              ).marginOnly(top: 10, left: 15, right: 15, bottom: 5),
+
+              // 添加按钮
+              Visibility(
+                visible: state.jobInfo?.canAppend == true,
+                child: MyButton(
+                  type: ClickType.throttle,
+                  milliseconds: 500,
+                  onPressed: () {
+                    FocusScope.of(context).unfocus();
+                    controller.showAddStaffDialog();
+                  },
+                  text: "Add Staff".tr,
+                  textColor: ColorConstants.white,
+                  fontSize: 16,
+                  radius: 20,
+                  backgroundColor: hexToColor("#FFBB1B"),
+                  fontWeight: FontWeight.w500,
+                ).marginOnly(left: 15, right: 15, top: 5, bottom: 10),
+              ),
+
+              //底部的列表
+              EasyRefresh(
+                controller: controller.refreshController,
+                onRefresh: controller.onRefresh,
+                onLoad: controller.loadMore,
+                child: LoadStateLayout(
+                  state: controller.loadingState,
+                  errorMessage: controller.errorMessage,
+                  errorRetry: () {
+                    controller.retryRequest();
+                  },
+                  successSliverWidget: [
+                    SliverList(
+                        delegate: SliverChildBuilderDelegate(
+                      (context, index) {
+                        return AppliedErItem(
+                          index: index,
+                          jobInfo: state.jobInfo,
+                          item: state.datas[index],
+                          onEditAction: () {
+                            controller.gotoAppliedEditPage(state.datas[index]);
+                          },
+                          onRemarkAction: () {
+                            controller.showRemarkDialog(state.datas[index]);
+                          },
+                          onStatusAction: () {
+                            controller.gotoAppliedWorkflowPage(state.datas[index]);
+                          },
+                          onItemAction: () {
+                            controller.doSelectedOrNot(state.datas[index]);
+                          },
+                          onMemberAction: () {
+                            controller.gotoStaffDetailPage(state.datas[index]);
+                          },
+                        );
+                      },
+                      childCount: state.datas.length,
+                    ))
+                  ],
+                ),
+              ).expanded(),
+
+              Visibility(
+                visible: state.datas.isNotEmpty,
+                child: MyTextView(
+                  "Select All".tr,
+                  fontSize: 17,
+                  isFontMedium: true,
+                  boxHeight: 48,
+                  onClick: () {
+                    controller.operationSelectAll();
+                  },
+                  alignment: Alignment.center,
+                  textAlign: TextAlign.center,
+                  textColor: Colors.white,
+                  backgroundColor: const Color(0XFFCA28E4),
+                ),
+              ),
+
+              Row(
+                mainAxisSize: MainAxisSize.max,
+                children: [
+                  //批量Approve
+                  MyTextView(
+                    "Operation Approve".tr,
+                    fontSize: 17,
+                    isFontMedium: true,
+                    boxHeight: 48,
+                    onClick: () {
+                      controller.operationApprove();
+                    },
+                    alignment: Alignment.center,
+                    textAlign: TextAlign.center,
+                    textColor: Colors.white,
+                    backgroundColor: Color(0XFF0AC074),
+                  ).expanded(),
+
+                  //批量修改时间
+                  MyTextView(
+                    "Batch Modify".tr,
+                    fontSize: 17,
+                    isFontMedium: true,
+                    boxHeight: 48,
+                    onClick: () {
+                      controller.showBatchModifyDialog();
+                    },
+                    alignment: Alignment.center,
+                    textAlign: TextAlign.center,
+                    textColor: Colors.white,
+                    backgroundColor: Color(0XFFFFBB1B),
+                  ).expanded(),
+                ],
+              ),
+            ],
+          ),
+        ),
+      );
+    });
+  }
+}

+ 18 - 0
packages/cpt_th/lib/modules/job_er/applied_er/applied_er_state.dart

@@ -0,0 +1,18 @@
+
+import 'package:domain/entity/response/t_h_applied_index_entity.dart';
+import 'package:domain/entity/response/t_h_applied_table_entity.dart';
+import 'package:flutter/material.dart';
+
+class AppliedErState {
+  //筛选条件
+  final TextEditingController searchController = TextEditingController();
+  String keyword = "";
+
+  String? jobId;
+
+  THAppliedIndexEntity? jobInfo; //指定工作的简短信息
+  List<THAppliedTableRows> datas = []; //Applied的员工列表
+
+  String? selectedStatusId;
+  String? selectedStatusName;
+}

+ 344 - 0
packages/cpt_th/lib/modules/job_er/applied_er/widget/er_applied_add_staff.dart

@@ -0,0 +1,344 @@
+import 'dart:ui';
+
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/response/job_list_applied_staff_search_entity.dart';
+import 'package:domain/entity/response/t_h_applied_employee_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/search_app_bar.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'er_applied_add_staff_controller.dart';
+
+/*
+ * 添加员工的弹窗
+ */
+class ErAppliedAddStaff extends StatefulWidget {
+  String jobId;
+  void Function(String selectIds)? confirmAction;
+
+  ErAppliedAddStaff({required this.jobId, this.confirmAction});
+
+  @override
+  State<ErAppliedAddStaff> createState() => _AppliedAddStaffState();
+}
+
+class _AppliedAddStaffState extends State<ErAppliedAddStaff> {
+  @override
+  void initState() {
+    super.initState();
+    ErAppliedAddStaffController controller =  Get.put(ErAppliedAddStaffController());
+    controller.jobId = widget.jobId;
+  }
+
+  @override
+  void dispose() {
+    super.dispose();
+    Get.delete<ErAppliedAddStaffController>();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return GetBuilder<ErAppliedAddStaffController>(
+      assignId: true,
+      builder: (controller) {
+        return Container(
+          width: 300,
+          height: 555,
+          decoration: const BoxDecoration(
+            color: Color(0XFFF7F7F7),
+            borderRadius: BorderRadius.all(Radius.circular(15)),
+          ),
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              Center(
+                child: MyTextView(
+                  "Choose Staff".tr,
+                  fontSize: 19,
+                  isFontMedium: true,
+                  textColor: ColorConstants.black,
+                  marginTop: 22,
+                  marginBottom: 15,
+                  marginLeft: 22,
+                  marginRight: 22,
+                ),
+              ),
+
+              SearchAppBar(
+                value: controller.keyword,
+                searchBarHeight: 38,
+                searchBarBgColor: Colors.white,
+                searchBarBorderRadius: 15,
+                searchBarBorder: Border.all(
+                  color: Color(0XFFC3C3C3), // 设置边框颜色为灰色
+                  width: 0.5, // 设置边框宽度
+                ),
+                textHintColor: Color(0XFFAFB3B7),
+                textColor: ColorConstants.black33,
+                onSearch: (keyword) {
+                  controller.doSearch(keyword);
+                },
+                hintText: "Staff Name/ID/Phone".tr,
+                controller: controller.searchController,
+              ).marginOnly(left: 16.5, right: 1.5, bottom: 15.5),
+
+              Container(
+                color: Colors.white,
+                child: EasyRefresh(
+                  header: ClassicHeader(
+                    dragText: 'Pull to refresh'.tr,
+                    armedText: 'Release ready'.tr,
+                    readyText: 'Refreshing...'.tr,
+                    processingText: 'Refreshing...'.tr,
+                    processedText: 'Succeeded'.tr,
+                    noMoreText: 'No more'.tr,
+                    failedText: 'Failed'.tr,
+                    messageText: 'Last updated at %T'.tr,
+                    textStyle: const TextStyle(color: ColorConstants.black66, fontSize: 14),
+                    messageStyle: const TextStyle(color: ColorConstants.black66, fontSize: 12),
+                    iconTheme: const IconThemeData(color: ColorConstants.black66),
+                    backgroundColor: Colors.transparent,
+                  ),
+                  footer: ClassicFooter(
+                    dragText: 'Pull to load'.tr,
+                    armedText: 'Release ready'.tr,
+                    readyText: 'Loading...'.tr,
+                    processingText: 'Loading...'.tr,
+                    processedText: 'Succeeded'.tr,
+                    noMoreText: 'No more'.tr,
+                    failedText: 'Failed'.tr,
+                    showMessage: false,
+                    triggerOffset: 50,
+                    iconDimension: 22,
+                    textStyle: const TextStyle(color: ColorConstants.black66, fontSize: 14),
+                    messageStyle: const TextStyle(color: ColorConstants.black66, fontSize: 12),
+                    iconTheme: const IconThemeData(color: ColorConstants.black66),
+                    backgroundColor: Colors.transparent,
+                  ),
+                  controller: controller.refreshController,
+                  onRefresh: controller.onRefresh,
+                  onLoad: controller.loadMore,
+                  child: LoadStateLayout(
+                    themeColor: ColorConstants.black66,
+                    state: controller.loadingState,
+                    errorMessage: controller.errorMessage,
+                    errorRetry: () {
+                      controller.retryRequest();
+                    },
+                    successSliverWidget: [
+                      SliverList(
+                          delegate: SliverChildBuilderDelegate(
+                        (context, index) {
+                          return _buildStaffItem(controller.datas[index], () {
+                            /// Item选中与未选中设置
+                            controller.datas[index].isSelected = !controller.datas[index].isSelected;
+                            controller.update();
+                          });
+                        },
+                        childCount: controller.datas.length,
+                      ))
+                    ],
+                  ),
+                ),
+              ).expanded(),
+
+              //按钮组
+              Row(
+                children: [
+                  //取消按钮
+                  Expanded(
+                      flex: 1,
+                      child: InkWell(
+                        onTap: () {
+                          onCancel();
+                        },
+                        child: MyTextView(
+                          "Cancel".tr,
+                          fontSize: 17.5,
+                          isFontMedium: true,
+                          textAlign: TextAlign.center,
+                          textColor: Color(0XFF0085C4),
+                          cornerRadius: 3,
+                          borderWidth: 1,
+                        ),
+                      )),
+
+                  //垂直分割线
+                  Container(
+                    color: Color(0xff09141F).withOpacity(0.13),
+                    width: 0.5,
+                  ),
+
+                  //同意按钮
+                  Expanded(
+                      flex: 1,
+                      child: InkWell(
+                        onTap: () {
+                          doCallbackAction(controller);
+                        },
+                        child: MyTextView(
+                          "Submit".tr,
+                          marginLeft: 10,
+                          fontSize: 17.5,
+                          isFontMedium: true,
+                          textAlign: TextAlign.center,
+                          textColor: Color(0XFF0085C4),
+                          cornerRadius: 3,
+                        ),
+                      )),
+                ],
+              ).constrained(height: 46),
+            ],
+          ),
+        );
+      },
+    );
+  }
+
+  //取消弹框
+  void onCancel() async {
+    SmartDialog.dismiss();
+  }
+
+  //执行回调
+  void doCallbackAction(ErAppliedAddStaffController controller) {
+    onCancel();
+
+    //找到当前选中的
+    var selectedList = controller.datas.where((element) => element.isSelected).toList(growable: false);
+    if (selectedList.isNotEmpty) {
+      var ids = selectedList.map((e) => e.id.toString()).toList(growable: false);
+      String separatedIds = ids.join(',');
+
+      widget.confirmAction?.call(separatedIds);
+    }
+
+  }
+
+  Widget _buildStaffItem(THAppliedEmployeeRows item, VoidCallback callback) {
+    return Stack(
+      children: [
+        Column(
+          children: [
+            //姓名
+            Row(
+              children: [
+                MyTextView(
+                  "Staff:",
+                  textColor: ColorConstants.black66,
+                  fontSize: 13,
+                  marginRight: 3,
+                  isFontRegular: true,
+                ),
+                MyTextView(
+                  item.name ?? "-",
+                  textColor: ColorConstants.black66,
+                  fontSize: 13,
+                  isFontRegular: true,
+                ),
+              ],
+            ),
+
+            //头像
+            Row(
+              children: [
+                MyTextView(
+                  "Avatar:",
+                  textColor: ColorConstants.black66,
+                  fontSize: 13,
+                  marginRight: 3,
+                  isFontRegular: true,
+                ),
+                MyLoadImage(
+                  item.profilePicture ?? "",
+                  width: 25,
+                  height: 25,
+                ),
+              ],
+            ).marginOnly(top: 5),
+
+            //性别
+            Row(
+              children: [
+                MyTextView(
+                  "Gender:",
+                  marginRight: 3,
+                  textColor: ColorConstants.black66,
+                  fontSize: 13,
+                  isFontRegular: true,
+                ),
+                MyTextView(
+                  item.gender ?? "-",
+                  textColor: ColorConstants.black66,
+                  fontSize: 13,
+                  isFontRegular: true,
+                ),
+              ],
+            ).marginOnly(top: 5),
+
+            //身份证
+            Row(
+              children: [
+                MyTextView(
+                  "NRIC:",
+                  marginRight: 3,
+                  textColor: ColorConstants.black66,
+                  fontSize: 13,
+                  isFontRegular: true,
+                ),
+                MyTextView(
+                  item.nric ?? "-",
+                  textColor: ColorConstants.black66,
+                  fontSize: 13,
+                  isFontRegular: true,
+                ),
+              ],
+            ).marginOnly(top: 5),
+
+            //电话
+            Row(
+              children: [
+                MyTextView(
+                  "Phone:",
+                  marginRight: 3,
+                  textColor: ColorConstants.black66,
+                  fontSize: 13,
+                  isFontRegular: true,
+                ),
+                MyTextView(
+                  item.mobile ?? "-",
+                  textColor: ColorConstants.black66,
+                  fontSize: 13,
+                  isFontRegular: true,
+                ),
+              ],
+            ).marginOnly(top: 5),
+
+            Container(
+              margin: const EdgeInsets.only(top: 19),
+              width: double.infinity,
+              height: 1,
+              color: const Color(0XFFF7F7F7),
+            )
+          ],
+        ).paddingOnly(left: 19, right: 20, top: 17),
+
+        //是否勾选
+        MyAssetImage(
+          item.isSelected ? Assets.baseServiceItemSelectedIcon : Assets.baseServiceItemUnselectedGrayIcon,
+          width: 20.5,
+          height: 20.5,
+        ).alignRight().marginOnly(right: 20, top: 17.5),
+      ],
+    ).onTap(callback);
+  }
+}

+ 130 - 0
packages/cpt_th/lib/modules/job_er/applied_er/widget/er_applied_add_staff_controller.dart

@@ -0,0 +1,130 @@
+import 'package:domain/entity/response/t_h_applied_employee_entity.dart';
+import 'package:domain/repository/th_er_repository.dart';
+import 'package:flutter/material.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+
+class ErAppliedAddStaffController extends GetxController with DioCancelableMixin {
+  final THERRepository _erRepository = Get.find();
+
+  TextEditingController searchController = TextEditingController();
+
+  var _curPage = 1;
+  var _needShowPlaceholder = true;
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Success;
+  String? errorMessage;
+
+  String keyword = "";
+  String? jobId;
+  List<THAppliedEmployeeRows> datas = [];
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,
+    controlFinishLoad: true,
+  );
+
+  // Refresh 刷新事件
+  Future onRefresh() async {
+    _curPage = 1;
+    fetchAllStaffList();
+  }
+
+  // Refresh 加载事件
+  Future loadMore() async {
+    _curPage++;
+    fetchAllStaffList();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _curPage = 1;
+    _needShowPlaceholder = true;
+    fetchAllStaffList();
+  }
+
+  /// 获取列表数据
+  Future fetchAllStaffList() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    }
+
+    // 获取 Staff 列表
+    var listResult = await _erRepository.searchStaffList(
+      jobId,
+      keyword,
+      curPage: _curPage,
+      cancelToken: cancelToken,
+    );
+
+    // 处理数据
+    if (listResult.isSuccess) {
+      handleList(listResult.data?.rows);
+    } else {
+      errorMessage = listResult.errorMsg;
+      changeLoadingState(LoadState.State_Error);
+    }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void handleList(List<THAppliedEmployeeRows>? list) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      if (_curPage == 1) {
+        //刷新的方式
+        datas.clear();
+        datas.addAll(list);
+        refreshController.finishRefresh();
+
+        //更新展示的状态
+        changeLoadingState(LoadState.State_Success);
+      } else {
+        //加载更多
+        datas.addAll(list);
+        refreshController.finishLoad();
+        update();
+      }
+    } else {
+      if (_curPage == 1) {
+        //展示无数据的布局
+        datas.clear();
+        changeLoadingState(LoadState.State_Empty);
+        refreshController.finishRefresh();
+      } else {
+        //展示加载完成,没有更多数据了
+        refreshController.finishLoad(IndicatorResult.noMore);
+      }
+    }
+  }
+
+  //搜索
+  void doSearch(String keyword) {
+    this.keyword = keyword;
+    refreshController.callRefresh();
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    fetchAllStaffList();
+  }
+
+  @override
+  void onClose() {
+    datas.clear();
+    super.onClose();
+  }
+}

+ 241 - 0
packages/cpt_th/lib/modules/job_er/applied_er/widget/er_applied_butch_modify.dart

@@ -0,0 +1,241 @@
+import 'dart:typed_data';
+import 'dart:ui';
+
+import 'package:cs_resources/generated/assets.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/picker/date_picker_util.dart';
+import 'package:widgets/shatter/form_require_text.dart';
+import 'package:widgets/widget_export.dart';
+
+/*
+ * 批量操作修改时间的弹窗
+ */
+class ErAppliedButchModify extends StatefulWidget {
+  DateTime? selectedStartDate;
+  DateTime? selectedEndDate;
+  void Function(DateTime startTime, DateTime endTime)? confirmAction;
+
+  ErAppliedButchModify({this.selectedStartDate, this.selectedEndDate, this.confirmAction});
+
+  @override
+  State<ErAppliedButchModify> createState() => _AppliedButchModifyState();
+}
+
+class _AppliedButchModifyState extends State<ErAppliedButchModify> {
+  DateTime? selectedStartDate;
+  DateTime? selectedEndDate;
+
+  @override
+  void initState() {
+    super.initState();
+    selectedStartDate = widget.selectedStartDate;
+    selectedEndDate = widget.selectedEndDate;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.center,
+      mainAxisAlignment: MainAxisAlignment.center,
+      children: [
+        //Title (如果使用 Container 为最外层容器则默认为 match_parent 的效果,除非我们限制宽度和最大高度最小高度)
+        Container(
+          padding: const EdgeInsets.symmetric(horizontal: 16.5),
+          width: double.infinity,
+          decoration: const BoxDecoration(
+            color: Colors.white,
+            borderRadius: BorderRadius.all(Radius.circular(15)),
+          ),
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              Center(
+                child: MyTextView(
+                  "Batch Modify".tr,
+                  fontSize: 19,
+                  isFontMedium: true,
+                  textColor: ColorConstants.black,
+                  marginTop: 22,
+                  marginBottom: 20,
+                ),
+              ),
+
+              FormRequireText(
+                fontSize: 14,
+                textColor: ColorConstants.black33,
+                fontWeight: FontWeight.w400,
+                text: "Job Start Time".tr,
+              ),
+
+              //选择时间
+              Container(
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
+                height: 45,
+                decoration: const BoxDecoration(
+                  color: ColorConstants.grayECECEC,
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      selectedStartDate == null ? "" : DateTimeUtils.formatDate(selectedStartDate,format: 'HH:mm'),
+                      fontSize: 14,
+                      hint: "Choose Start Date".tr,
+                      textHintColor: ColorConstants.textBlackHint,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black33,
+                    ).expanded(),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                  ],
+                ),
+              ).onTap(() {
+                pickerStartDate();
+              }),
+
+              //结束日期
+              FormRequireText(
+                fontSize: 14,
+                textColor: ColorConstants.black33,
+                fontWeight: FontWeight.w400,
+                text: "Job End Time".tr,
+              ).marginOnly(top: 11),
+
+              //选择结束日期
+              Container(
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
+                height: 45,
+                decoration: const BoxDecoration(
+                  color: ColorConstants.grayECECEC,
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      selectedEndDate == null ? "" : DateTimeUtils.formatDate(selectedEndDate,format: 'HH:mm'),
+                      fontSize: 14,
+                      hint: "Choose End Date".tr,
+                      textHintColor: ColorConstants.textBlackHint,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black33,
+                    ).expanded(),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                  ],
+                ),
+              ).onTap(() {
+                pickerEndDate();
+              }),
+
+              Container(
+                margin: const EdgeInsets.only(top: 25),
+                color: const Color(0XFFCECECE),
+                height: 0.5,
+              ),
+              Row(
+                children: [
+                  Expanded(
+                      flex: 1,
+                      child: InkWell(
+                        onTap: () {
+                          onCancel();
+                        },
+                        child: MyTextView(
+                          "Cancel".tr,
+                          fontSize: 17.5,
+                          isFontMedium: true,
+                          textAlign: TextAlign.center,
+                          textColor: Color(0XFF0085C4),
+                          cornerRadius: 3,
+                          borderWidth: 1,
+                        ),
+                      )),
+                  Container(
+                    color: Color(0xff09141F).withOpacity(0.13),
+                    width: 0.5,
+                  ),
+                  Expanded(
+                      flex: 1,
+                      child: InkWell(
+                        onTap: () async {
+                          if (selectedStartDate == null) {
+                            ToastEngine.show("Choose Start Date".tr);
+                            return;
+                          }
+
+                          if (selectedEndDate == null) {
+                            ToastEngine.show("Choose End Date".tr);
+                            return;
+                          }
+
+                          widget.confirmAction?.call(selectedStartDate!, selectedEndDate!);
+                          onCancel();
+                        },
+                        child: MyTextView(
+                          "Submit".tr,
+                          marginLeft: 10,
+                          fontSize: 17.5,
+                          isFontMedium: true,
+                          textAlign: TextAlign.center,
+                          textColor: const Color(0XFF0085C4),
+                          cornerRadius: 3,
+                        ),
+                      )),
+                ],
+              ).constrained(height: 46),
+            ],
+          ),
+        ),
+      ],
+    ).constrained(width: 285);
+  }
+
+  /// 筛选开始日期
+  void pickerStartDate() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: selectedStartDate,
+      mode: CupertinoDatePickerMode.time,
+      onDateTimeChanged: (date) {
+        setState(() {
+          selectedStartDate = date;
+        });
+      },
+      title: "Start Date".tr,
+    );
+  }
+
+  /// 筛选结束日期
+  void pickerEndDate() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: selectedEndDate ?? selectedStartDate,
+      mode: CupertinoDatePickerMode.time,
+      onDateTimeChanged: (date) {
+        setState(() {
+          selectedEndDate = date;
+        });
+      },
+      title: "End Date".tr,
+    );
+  }
+
+  //取消弹框
+  void onCancel() async {
+    SmartDialog.dismiss();
+  }
+}

+ 313 - 0
packages/cpt_th/lib/modules/job_er/applied_er/widget/er_applied_staff_reviews.dart

@@ -0,0 +1,313 @@
+import 'dart:ui';
+
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/response/job_list_remark_view_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:shared/utils/util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/shatter/rating_widget.dart';
+import 'package:widgets/widget_export.dart';
+
+/*
+ * 员工的评价弹窗
+ */
+class ErAppliedStaffReviews extends StatefulWidget {
+  JobListRemarkViewEntity appliedReviews;
+  void Function(String attitudeRate, String performanceRate, String experienceRate, String groomingRate, String content)? confirmAction;
+
+  ErAppliedStaffReviews({required this.appliedReviews, this.confirmAction});
+
+  @override
+  State<ErAppliedStaffReviews> createState() => _AppliedStaffReviewsState();
+}
+
+class _AppliedStaffReviewsState extends State<ErAppliedStaffReviews> {
+  late JobListRemarkViewEntity reviews;
+  late String attitudeRate;
+  late String groomingRate;
+  late String performanceRate;
+  late String experienceRate;
+  late String content;
+  late TextEditingController _controller;
+  late FocusNode _focusNode;
+
+  @override
+  void initState() {
+    super.initState();
+    _controller = TextEditingController();
+    _focusNode = FocusNode();
+
+    reviews = widget.appliedReviews;
+    attitudeRate = reviews.attitudeRate.toString();
+    groomingRate = reviews.groomingRate.toString();
+    performanceRate = reviews.performanceRate.toString();
+    experienceRate = reviews.experienceRate.toString();
+    _controller.text = reviews.feedback ?? "";
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.center,
+      mainAxisAlignment: MainAxisAlignment.center,
+      children: [
+        //Title (如果使用 Container 为最外层容器则默认为 match_parent 的效果,除非我们限制宽度和最大高度最小高度)
+        Container(
+          width: double.infinity,
+          decoration: const BoxDecoration(
+            color: Colors.white,
+            borderRadius: BorderRadius.all(Radius.circular(15)),
+          ),
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              Center(
+                child: MyTextView(
+                  "Remarks".tr,
+                  fontSize: 19,
+                  isFontMedium: true,
+                  textColor: ColorConstants.black,
+                  marginTop: 23,
+                  marginBottom: 19,
+                  marginLeft: 22,
+                  marginRight: 22,
+                ),
+              ),
+
+              MyTextView(
+                reviews.labourerName ?? "",
+                isFontRegular: true,
+                textColor: Colors.black,
+                marginLeft: 22,
+                marginRight: 22,
+                fontSize: 17,
+              ),
+
+              //态度评分
+              Row(
+                children: [
+                  MyTextView(
+                    "Attitude".tr,
+                    isFontRegular: true,
+                    textColor: ColorConstants.black66,
+                    fontSize: 15,
+                  ).expanded(),
+                  RatingWidget(
+                    nomalImage: Assets.baseServiceRatingUnselected,
+                    selectImage: Assets.baseServiceRatingSelected,
+                    size: 21,
+                    padding: 5,
+                    selectAble: Utils.isEmpty(reviews.content),
+                    integerOnly: true,
+                    value: reviews.attitudeRate,
+                    onRatingUpdate: (value) {
+                      attitudeRate = value;
+                    },
+                  )
+                ],
+              ).marginOnly(top: 15, left: 22, right: 22),
+
+              //表现评分
+              Row(
+                children: [
+                  MyTextView(
+                    "Performance".tr,
+                    isFontRegular: true,
+                    textColor: ColorConstants.black66,
+                    fontSize: 15,
+                  ).expanded(),
+                  RatingWidget(
+                    nomalImage: Assets.baseServiceRatingUnselected,
+                    selectImage: Assets.baseServiceRatingSelected,
+                    size: 21,
+                    padding: 5,
+                    selectAble: Utils.isEmpty(reviews.content),
+                    integerOnly: true,
+                    value: reviews.performanceRate,
+                    onRatingUpdate: (value) {
+                      performanceRate = value;
+                    },
+                  )
+                ],
+              ).marginOnly(top: 15, left: 22, right: 22),
+
+              //经验评分
+              Row(
+                children: [
+                  MyTextView(
+                    "Experience".tr,
+                    isFontRegular: true,
+                    textColor: ColorConstants.black66,
+                    fontSize: 15,
+                  ).expanded(),
+                  RatingWidget(
+                    nomalImage: Assets.baseServiceRatingUnselected,
+                    selectImage: Assets.baseServiceRatingSelected,
+                    size: 21,
+                    padding: 5,
+                    selectAble: Utils.isEmpty(reviews.content),
+                    integerOnly: true,
+                    value: reviews.experienceRate,
+                    onRatingUpdate: (value) {
+                      experienceRate = value;
+                    },
+                  )
+                ],
+              ).marginOnly(top: 15, left: 22, right: 22),
+
+              //着装评分
+              Row(
+                children: [
+                  MyTextView(
+                    "Grooming".tr,
+                    isFontRegular: true,
+                    textColor: ColorConstants.black66,
+                    fontSize: 15,
+                  ).expanded(),
+                  RatingWidget(
+                    nomalImage: Assets.baseServiceRatingUnselected,
+                    selectImage: Assets.baseServiceRatingSelected,
+                    size: 21,
+                    padding: 5,
+                    selectAble: Utils.isEmpty(reviews.content),
+                    integerOnly: true,
+                    value: reviews.groomingRate,
+                    onRatingUpdate: (value) {
+                      groomingRate = value;
+                    },
+                  )
+                ],
+              ).marginOnly(top: 15, left: 22, right: 22),
+
+              IgnoreKeyboardDismiss(
+                child: Container(
+                  height: 130,
+                  margin: const EdgeInsets.symmetric(vertical: 19, horizontal: 22),
+                  padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 15),
+                  decoration: BoxDecoration(
+                    color: const Color(0xFFF0F0F0),
+                    border: Border.all(
+                      color: const Color(0xFFD8D8D8),
+                      width: 0.5,
+                    ),
+                  ),
+                  child: TextField(
+                    cursorColor: ColorConstants.black66,
+                    cursorWidth: 1.5,
+                    autofocus: false,
+                    enabled: Utils.isEmpty(reviews.content),
+                    focusNode: _focusNode,
+                    controller: _controller,
+                    // 装饰
+                    decoration: InputDecoration(
+                      isDense: true,
+                      isCollapsed: true,
+                      border: InputBorder.none,
+                      hintText: "Enter...".tr,
+                      hintStyle: const TextStyle(
+                        color: ColorConstants.black66,
+                        fontSize: 15.0,
+                        fontWeight: FontWeight.w400,
+                      ),
+                    ),
+                    style: const TextStyle(
+                      color: ColorConstants.black,
+                      fontSize: 15.0,
+                      fontWeight: FontWeight.w400,
+                    ),
+                    // 键盘动作右下角图标
+                    textInputAction: TextInputAction.done,
+                    onSubmitted: (value) {
+                      doCallbackAction();
+                    },
+                  ),
+                ),
+              ),
+
+              // 分割线
+              Container(
+                color: const Color(0XFFCECECE),
+                height: 0.5,
+              ),
+
+              //按钮组
+              Row(
+                children: [
+                  Expanded(
+                      flex: 1,
+                      child: InkWell(
+                        onTap: () {
+                          onCancel();
+                        },
+                        child: MyTextView(
+                          "Cancel".tr,
+                          fontSize: 17.5,
+                          isFontMedium: true,
+                          textAlign: TextAlign.center,
+                          textColor: Color(0XFF0085C4),
+                          cornerRadius: 3,
+                          borderWidth: 1,
+                        ),
+                      )),
+                  Container(
+                    color: Color(0xff09141F).withOpacity(0.13),
+                    width: 0.5,
+                  ),
+                  Expanded(
+                      flex: 1,
+                      child: InkWell(
+                        onTap: () {
+                          doCallbackAction();
+                        },
+                        child: MyTextView(
+                          "Submit".tr,
+                          marginLeft: 10,
+                          fontSize: 17.5,
+                          isFontMedium: true,
+                          textAlign: TextAlign.center,
+                          textColor: Color(0XFF0085C4),
+                          cornerRadius: 3,
+                        ),
+                      )),
+                ],
+              ).constrained(height: 46),
+            ],
+          ),
+        ),
+      ],
+    ).constrained(width: 285);
+  }
+
+  //取消弹框
+  void onCancel() async {
+    SmartDialog.dismiss();
+  }
+
+  //执行回调
+  void doCallbackAction() {
+    _focusNode.unfocus();
+
+    content = _controller.text.toString();
+
+    if (attitudeRate == "0" || experienceRate == "0" || performanceRate == "0" || groomingRate == "0") {
+      ToastEngine.show("Rate First");
+      return;
+    }
+
+    if (Utils.isEmpty(content)) {
+      ToastEngine.show("Please Enter Remark".tr);
+      return;
+    }
+
+    onCancel();
+
+    widget.confirmAction?.call(attitudeRate, performanceRate, experienceRate, groomingRate, content);
+  }
+}

+ 195 - 0
packages/cpt_th/lib/modules/job_er/applied_er_edit/applied_er_edit_controller.dart

@@ -0,0 +1,195 @@
+import 'package:domain/repository/job_repository.dart';
+import 'package:domain/repository/th_er_repository.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/constants/app_constant.dart';
+import 'package:plugin_platform/engine/notify/notify_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:shared/utils/event_bus.dart';
+import 'package:widgets/picker/date_picker_util.dart';
+
+import 'applied_er_edit_state.dart';
+
+class AppliedErEditController extends GetxController with DioCancelableMixin {
+  final THERRepository _erRepository = Get.find();
+  final AppliedErEditState state = AppliedErEditState();
+
+  // 获取编辑的详情
+  void fetchAppliedEditView() async {
+    var result = await _erRepository.fetchERAppliedEditDetail(appliedId: state.appliedId, cancelToken: cancelToken);
+
+    if (result.isSuccess) {
+      state.appliedEditView = result.data;
+
+      var addHourController = state.formData['add_hours']!['controller'];
+      var totalRoomController = state.formData['total_room']!['controller'];
+      var reasonController = state.formData['reason']!['controller'];
+
+      addHourController.text = state.appliedEditView?.subtractHours ?? "0";
+      // totalRoomController.text = state.appliedEditView?.totalRooms.toString();
+      reasonController.text = state.appliedEditView?.remark ?? "";
+
+      state.selectedReasonType = state.appliedEditView?.applied?.reasonType;
+      state.selectedStatusId = state.appliedEditView?.applied?.status;
+
+      update();
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+      return;
+    }
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    fetchAppliedEditView();
+  }
+
+  /// 选择时间
+  void pickStartTime() {
+    if (state.appliedEditView == null) return;
+
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: DateTimeUtils.getDateTimeByMs((state.appliedEditView?.applied?.startTime ?? 0) * 1000),
+      mode: CupertinoDatePickerMode.time,
+      onDateTimeChanged: (date) {
+        state.appliedEditView!.startTime = DateTimeUtils.formatDate(date, format: 'HH:mm:ss');
+        update();
+      },
+      title: "Start Time".tr,
+    );
+  }
+
+  /// 选择时间
+  void pickEndTime() {
+    if (state.appliedEditView == null) return;
+
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: DateTimeUtils.getDateTimeByMs((state.appliedEditView?.applied?.endTime ?? 0) * 1000),
+      mode: CupertinoDatePickerMode.time,
+      onDateTimeChanged: (date) {
+        state.appliedEditView!.endTime = DateTimeUtils.formatDate(date, format: 'HH:mm:ss');
+        update();
+      },
+      title: "End Time".tr,
+    );
+  }
+
+  /// 选择时间
+  void pickSecurityInTime() {
+    if (state.appliedEditView == null) return;
+
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: DateTimeUtils.getDateTime(state.appliedEditView!.securityIn ?? ""),
+      mode: CupertinoDatePickerMode.dateAndTime,
+      onDateTimeChanged: (date) {
+        state.appliedEditView!.securityIn = DateTimeUtils.formatDate(date);
+        update();
+      },
+      title: "Security In".tr,
+    );
+  }
+
+  /// 选择时间
+  void pickSecurityOutTime() {
+    if (state.appliedEditView == null) return;
+
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: DateTimeUtils.getDateTime(state.appliedEditView!.securityOut ?? ""),
+      mode: CupertinoDatePickerMode.dateAndTime,
+      onDateTimeChanged: (date) {
+        state.appliedEditView!.securityOut = DateTimeUtils.formatDate(date);
+        update();
+      },
+      title: "Security Out".tr,
+    );
+  }
+
+  /// 选择时间
+  void pickWorkInTime() {
+    if (state.appliedEditView == null) return;
+
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: DateTimeUtils.getDateTime(state.appliedEditView!.workIn ?? ""),
+      mode: CupertinoDatePickerMode.dateAndTime,
+      onDateTimeChanged: (date) {
+        state.appliedEditView!.workIn = DateTimeUtils.formatDate(date);
+        update();
+      },
+      title: "Work In".tr,
+    );
+  }
+
+  /// 选择时间
+  void pickWorkOutTime() {
+    if (state.appliedEditView == null) return;
+
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: DateTimeUtils.getDateTime(state.appliedEditView!.workOut ?? ""),
+      mode: CupertinoDatePickerMode.dateAndTime,
+      onDateTimeChanged: (date) {
+        state.appliedEditView!.workOut = DateTimeUtils.formatDate(date);
+        update();
+      },
+      title: "Work Out".tr,
+    );
+  }
+
+  /// 提交修改
+  void doSubmit() async {
+    if (state.appliedEditView == null) return;
+
+    //输入框的数据赋值
+    // TextEditingController totalRoomController = state.formData['total_room']!['controller'];
+    TextEditingController addHourController = state.formData['add_hours']!['controller'];
+    TextEditingController reasonController = state.formData['reason']!['controller'];
+    // 获取文本并尝试转换为整数
+    // String totalRoomText = totalRoomController.text;
+    // if (totalRoomText.isNotEmpty) {
+    //   try {
+    //     // 尝试将文本转换为整数
+    //     int totalRooms = int.parse(totalRoomText);
+    //     // 赋值给 state.appliedEditView!.totalRooms
+    //     state.appliedEditView!.totalRooms = totalRooms;
+    //   } catch (e) {
+    //     // 如果转换失败,可以进行错误处理
+    //     print('Error parsing totalRooms: $e');
+    //   }
+    // } else {
+    //   // 文本为空时的处理逻辑,例如设置默认值或提示用户
+    //   print('Total rooms input is empty');
+    // }
+
+    state.appliedEditView!.subtractHours = addHourController.text.toString();
+    state.appliedEditView!.remark = reasonController.text.toString();
+
+    //请求网络提交修改
+    var result = await _erRepository.editAppliedDetailSubmit(
+      appliedId: state.appliedId,
+      startTime: state.appliedEditView?.startTime,
+      endTime: state.appliedEditView?.endTime,
+      securityIn: state.appliedEditView?.securityIn,
+      securityOut: state.appliedEditView?.securityOut,
+      workIn: state.appliedEditView?.workIn,
+      workOut: state.appliedEditView?.workOut,
+      subtractHours: state.appliedEditView?.subtractHours,
+      reasonType: state.selectedReasonType,
+      status: state.selectedStatusId,
+      remark: state.appliedEditView?.remark,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess("Successful".tr);
+
+      bus.emit(AppConstant.eventAppliedListRefresh, state.appliedId);
+
+      Get.back();
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+      return;
+    }
+  }
+}

+ 564 - 0
packages/cpt_th/lib/modules/job_er/applied_er_edit/applied_er_edit_page.dart

@@ -0,0 +1,564 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:get/get.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/no_shadow_scroll_behavior.dart';
+import 'package:widgets/shatter/custom_radio_check.dart';
+import 'package:widgets/shatter/form_require_text.dart';
+import 'package:widgets/shatter/round_my_text_field.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'applied_er_edit_controller.dart';
+
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/my_appbar.dart';
+
+import 'applied_er_edit_state.dart';
+
+/*
+ * Applied列表中,编辑用户的此次工作打卡信息
+ */
+class AppliedErEditPage extends BaseStatefulPage<AppliedErEditController> {
+  AppliedErEditPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance(String? appliedId) {
+    return Get.start(RouterPath.THJobAppliedEditER, arguments: {'appliedId': appliedId});
+  }
+
+  @override
+  AppliedErEditController createRawController() {
+    return AppliedErEditController();
+  }
+
+  @override
+  State<AppliedErEditPage> createState() => _JobAppliedEditState();
+}
+
+class _JobAppliedEditState extends BaseState<AppliedErEditPage, AppliedErEditController> {
+  late AppliedErEditState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+    state.appliedId = Get.arguments['appliedId'];
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return Scaffold(
+        extendBodyBehindAppBar: true,
+        appBar: MyAppBar.appBar(context, "Edit".tr),
+        body: SafeArea(
+          bottom: MediaQuery.of(context).padding.bottom > 38,
+          top: false,
+          child: Container(
+            width: double.infinity,
+            height: double.infinity,
+            padding: EdgeInsets.only(top: kToolbarHeight + ScreenUtil.getStatusBarH(context) + 1),
+            decoration: const BoxDecoration(
+              gradient: LinearGradient(
+                colors: [
+                  Color(0xFF091D44),
+                  Color(0xFF245A8A),
+                  Color(0xFF7F7CEC),
+                ],
+                begin: Alignment.topCenter,
+                end: Alignment.bottomCenter,
+              ),
+            ),
+            child: Scrollbar(
+              child: ScrollConfiguration(
+                behavior: NoShadowScrollBehavior(),
+                child: SingleChildScrollView(
+                  scrollDirection: Axis.vertical,
+                  physics: const BouncingScrollPhysics(),
+                  child: Column(
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    children: [
+                      MyTextView(
+                        "Job Date".tr,
+                        textColor: Colors.white,
+                        fontSize: 15,
+                        marginTop: 15,
+                        isFontRegular: true,
+                      ),
+
+                      //工作时间
+                      Container(
+                        padding: const EdgeInsets.only(left: 16, right: 10),
+                        margin: const EdgeInsets.only(top: 10),
+                        height: 45,
+                        decoration: BoxDecoration(
+                          color: const Color(0xFF4DCFF6).withOpacity(0.5),
+                          borderRadius: const BorderRadius.all(Radius.circular(5)),
+                        ),
+                        child: Row(
+                          mainAxisSize: MainAxisSize.max,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          mainAxisAlignment: MainAxisAlignment.start,
+                          children: [
+                            MyTextView(
+                              state.appliedEditView?.applied?.jobDate ?? "-",
+                              fontSize: 15,
+                              hint: "Choose Job Title".tr,
+                              textHintColor: ColorConstants.textGrayAECAE5,
+                              isFontMedium: true,
+                              textColor: ColorConstants.white,
+                            ).expanded(),
+                          ],
+                        ),
+                      ),
+
+                      //工作标题,选择模板
+                      MyTextView(
+                        "Staff Name".tr,
+                        textColor: Colors.white,
+                        fontSize: 15,
+                        marginTop: 15,
+                        isFontRegular: true,
+                      ),
+
+                      //工作标题
+                      Container(
+                        padding: const EdgeInsets.only(left: 16, right: 10),
+                        margin: const EdgeInsets.only(top: 10),
+                        height: 45,
+                        decoration: BoxDecoration(
+                          color: const Color(0xFF4DCFF6).withOpacity(0.5),
+                          borderRadius: const BorderRadius.all(Radius.circular(5)),
+                        ),
+                        child: Row(
+                          mainAxisSize: MainAxisSize.max,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          mainAxisAlignment: MainAxisAlignment.start,
+                          children: [
+                            MyTextView(
+                              state.appliedEditView?.applied?.labourerName ?? "-",
+                              fontSize: 15,
+                              hint: "Choose Job Title".tr,
+                              textHintColor: ColorConstants.textGrayAECAE5,
+                              isFontMedium: true,
+                              textColor: ColorConstants.white,
+                            ).expanded(),
+                          ],
+                        ),
+                      ),
+
+                      //开始时间
+                      FormRequireText(
+                        text: "Start Time".tr,
+                      ).marginOnly(top: 15),
+
+                      //选择时间
+                      Container(
+                        padding: const EdgeInsets.only(left: 16, right: 10),
+                        margin: const EdgeInsets.only(top: 10),
+                        height: 45,
+                        decoration: BoxDecoration(
+                          color: const Color(0xFF4DCFF6).withOpacity(0.2),
+                          borderRadius: const BorderRadius.all(Radius.circular(5)),
+                        ),
+                        child: Row(
+                          mainAxisSize: MainAxisSize.max,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          mainAxisAlignment: MainAxisAlignment.start,
+                          children: [
+                            MyTextView(
+                              state.appliedEditView?.startTime ?? "-",
+                              fontSize: 14,
+                              hint: "Job Start Time".tr,
+                              textHintColor: ColorConstants.textGrayAECAE5,
+                              isFontMedium: true,
+                              textColor: ColorConstants.white,
+                            ).expanded(),
+                            //下拉选图标
+                            const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                          ],
+                        ),
+                      ).onTap(() {
+                        FocusScope.of(context).unfocus();
+                        controller.pickStartTime();
+                      }),
+
+                      //门卫签到时间
+                      MyTextView(
+                        "Security In".tr,
+                        textColor: Colors.white,
+                        fontSize: 15,
+                        marginTop: 15,
+                        isFontRegular: true,
+                      ),
+
+                      //门卫签到时间
+                      Container(
+                        padding: const EdgeInsets.only(left: 16, right: 10),
+                        margin: const EdgeInsets.only(top: 10),
+                        height: 45,
+                        decoration: BoxDecoration(
+                          color: const Color(0xFF4DCFF6).withOpacity(0.2),
+                          borderRadius: const BorderRadius.all(Radius.circular(5)),
+                        ),
+                        child: Row(
+                          mainAxisSize: MainAxisSize.max,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          mainAxisAlignment: MainAxisAlignment.start,
+                          children: [
+                            MyTextView(
+                              state.appliedEditView?.securityIn ?? "-",
+                              fontSize: 14,
+                              hint: "Security In".tr,
+                              textHintColor: ColorConstants.textGrayAECAE5,
+                              isFontMedium: true,
+                              textColor: ColorConstants.white,
+                            ).expanded(),
+                            //下拉选图标
+                            const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                          ],
+                        ),
+                      ).onTap(() {
+                        FocusScope.of(context).unfocus();
+                        controller.pickSecurityInTime();
+                      }),
+
+                      //工作地签到时间
+                      MyTextView(
+                        "Work In".tr,
+                        textColor: Colors.white,
+                        fontSize: 15,
+                        marginTop: 15,
+                        isFontRegular: true,
+                      ),
+
+                      //工作地签到时间
+                      Container(
+                        padding: const EdgeInsets.only(left: 16, right: 10),
+                        margin: const EdgeInsets.only(top: 10),
+                        height: 45,
+                        decoration: BoxDecoration(
+                          color: const Color(0xFF4DCFF6).withOpacity(0.2),
+                          borderRadius: const BorderRadius.all(Radius.circular(5)),
+                        ),
+                        child: Row(
+                          mainAxisSize: MainAxisSize.max,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          mainAxisAlignment: MainAxisAlignment.start,
+                          children: [
+                            MyTextView(
+                              state.appliedEditView?.workIn ?? "-",
+                              fontSize: 14,
+                              hint: "Work In".tr,
+                              textHintColor: ColorConstants.textGrayAECAE5,
+                              isFontMedium: true,
+                              textColor: ColorConstants.white,
+                            ).expanded(),
+                            //下拉选图标
+                            const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                          ],
+                        ),
+                      ).onTap(() {
+                        FocusScope.of(context).unfocus();
+                        controller.pickWorkInTime();
+                      }),
+
+                      //结束时间
+                      FormRequireText(
+                        text: "End Time".tr,
+                      ).marginOnly(top: 15),
+
+                      //结束时间
+                      Container(
+                        padding: const EdgeInsets.only(left: 16, right: 10),
+                        margin: const EdgeInsets.only(top: 10),
+                        height: 45,
+                        decoration: BoxDecoration(
+                          color: const Color(0xFF4DCFF6).withOpacity(0.2),
+                          borderRadius: const BorderRadius.all(Radius.circular(5)),
+                        ),
+                        child: Row(
+                          mainAxisSize: MainAxisSize.max,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          mainAxisAlignment: MainAxisAlignment.start,
+                          children: [
+                            MyTextView(
+                              state.appliedEditView?.endTime ?? "-",
+                              fontSize: 14,
+                              hint: "Job End Time".tr,
+                              textHintColor: ColorConstants.textGrayAECAE5,
+                              isFontMedium: true,
+                              textColor: ColorConstants.white,
+                            ).expanded(),
+
+                            //下拉选图标
+                            const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                          ],
+                        ),
+                      ).onTap(() {
+                        FocusScope.of(context).unfocus();
+                        controller.pickEndTime();
+                      }),
+
+                      //门卫签出时间
+                      MyTextView(
+                        "Security Out".tr,
+                        textColor: Colors.white,
+                        fontSize: 15,
+                        marginTop: 15,
+                        isFontRegular: true,
+                      ),
+
+                      //门卫签出时间
+                      Container(
+                        padding: const EdgeInsets.only(left: 16, right: 10),
+                        margin: const EdgeInsets.only(top: 10),
+                        height: 45,
+                        decoration: BoxDecoration(
+                          color: const Color(0xFF4DCFF6).withOpacity(0.2),
+                          borderRadius: const BorderRadius.all(Radius.circular(5)),
+                        ),
+                        child: Row(
+                          mainAxisSize: MainAxisSize.max,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          mainAxisAlignment: MainAxisAlignment.start,
+                          children: [
+                            MyTextView(
+                              state.appliedEditView?.securityOut ?? "-",
+                              fontSize: 14,
+                              hint: "Security Out".tr,
+                              textHintColor: ColorConstants.textGrayAECAE5,
+                              isFontMedium: true,
+                              textColor: ColorConstants.white,
+                            ).expanded(),
+                            //下拉选图标
+                            const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                          ],
+                        ),
+                      ).onTap(() {
+                        FocusScope.of(context).unfocus();
+                        controller.pickSecurityOutTime();
+                      }),
+
+                      //工作地签出时间
+                      MyTextView(
+                        "Work Out".tr,
+                        textColor: Colors.white,
+                        fontSize: 15,
+                        marginTop: 15,
+                        isFontRegular: true,
+                      ),
+
+                      //工作地签出时间
+                      Container(
+                        padding: const EdgeInsets.only(left: 16, right: 10),
+                        margin: const EdgeInsets.only(top: 10),
+                        height: 45,
+                        decoration: BoxDecoration(
+                          color: const Color(0xFF4DCFF6).withOpacity(0.2),
+                          borderRadius: const BorderRadius.all(Radius.circular(5)),
+                        ),
+                        child: Row(
+                          mainAxisSize: MainAxisSize.max,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          mainAxisAlignment: MainAxisAlignment.start,
+                          children: [
+                            MyTextView(
+                              state.appliedEditView?.workOut ?? "-",
+                              fontSize: 14,
+                              hint: "Work Out".tr,
+                              textHintColor: ColorConstants.textGrayAECAE5,
+                              isFontMedium: true,
+                              textColor: ColorConstants.white,
+                            ).expanded(),
+                            //下拉选图标
+                            const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                          ],
+                        ),
+                      ).onTap(() {
+                        FocusScope.of(context).unfocus();
+                        controller.pickWorkOutTime();
+                      }),
+
+                      // + - 小时
+                      MyTextView(
+                        "Subtract Hours".tr,
+                        textColor: Colors.white,
+                        fontSize: 15,
+                        marginTop: 15,
+                        isFontRegular: true,
+                      ),
+
+                      //+ - 小时输入框
+                      CustomTextField(
+                        formKey: "add_hours",
+                        marginLeft: 0,
+                        marginRight: 0,
+                        paddingTop: 0,
+                        paddingBottom: 0,
+                        height: 45,
+                        fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(0.2),
+                        enabled: true,
+                        textInputType: TextInputType.number,
+                        formData: state.formData,
+                        textInputAction: TextInputAction.done,
+                        onSubmit: (key, value) {
+                          FocusScope.of(context).unfocus();
+                        },
+                        marginTop: 10,
+                      ),
+
+                      // // 房间计费,房间数量
+                      // Visibility(
+                      //   visible: state.appliedEditView?.jobUnit != "hour",
+                      //   child: MyTextView(
+                      //     "Total Rooms".tr,
+                      //     textColor: Colors.white,
+                      //     fontSize: 15,
+                      //     marginTop: 15,
+                      //     isFontRegular: true,
+                      //   ),
+                      // ),
+                      //
+                      // // 房间计费,房间数量输入框
+                      // Visibility(
+                      //   visible: state.appliedEditView?.jobUnit != "hour",
+                      //   child: CustomTextField(
+                      //     formKey: "total_room",
+                      //     marginLeft: 0,
+                      //     marginRight: 0,
+                      //     paddingTop: 0,
+                      //     paddingBottom: 0,
+                      //     height: 45,
+                      //     fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(0.2),
+                      //     enabled: true,
+                      //     inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
+                      //     textInputType: TextInputType.number,
+                      //     formData: state.formData,
+                      //     textInputAction: TextInputAction.done,
+                      //     onSubmit: (key, value) {
+                      //       FocusScope.of(context).unfocus();
+                      //     },
+                      //     marginTop: 10,
+                      //   ),
+                      // ),
+
+                      // 状态
+                      MyTextView(
+                        "Status".tr,
+                        textColor: Colors.white,
+                        fontSize: 15,
+                        marginTop: 15,
+                        isFontRegular: true,
+                      ),
+
+                      //状态的单选
+                      state.appliedEditView != null
+                          ? CustomRadioCheck(
+                        options: state.appliedEditView!.statusList.map((e) => e.txt).whereType<String>().toList(),
+                        selectedPosition: state.appliedEditView!.statusList.indexWhere((element) => element.value == state.appliedEditView?.applied?.status),
+                        onOptionSelected: (index, text) {
+                          //修改内存的值
+                          state.selectedStatusId = state.appliedEditView!.statusList[index].value;
+                        },
+                      ).marginOnly(top: 15)
+                          : const CircularProgressIndicator(),
+
+                      // 修改的理由
+                      MyTextView(
+                        "Reason".tr,
+                        textColor: Colors.white,
+                        fontSize: 15,
+                        marginTop: 15,
+                        isFontRegular: true,
+                      ),
+
+                      //理由的单选
+                      state.appliedEditView != null
+                          ? CustomRadioCheck(
+                              options: state.appliedEditView!.reasonList.map((e) => e.txt).whereType<String>().toList(), //后台返回的数据展示,并且根据后台的数据匹配索引
+                              selectedPosition: state.appliedEditView!.reasonList.indexWhere((element) => element.value == state.appliedEditView?.applied?.reasonType),
+                              onOptionSelected: (index, text) {
+                                //修改内存的值
+                                state.selectedReasonType = state.appliedEditView!.reasonList[index].value;
+                              },
+                            ).marginOnly(top: 15, bottom: 20)
+                          : const CircularProgressIndicator(),
+
+                      IgnoreKeyboardDismiss(
+                        child: Container(
+                          height: 130,
+                          padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 15),
+                          decoration: BoxDecoration(
+                            color: const Color(0xFF4DCFF6).withOpacity(0.2),
+                            borderRadius: const BorderRadius.all(Radius.circular(5)),
+                          ),
+                          child: TextField(
+                            cursorColor: ColorConstants.white,
+                            cursorWidth: 1.5,
+                            autofocus: false,
+                            controller: state.formData["reason"]!['controller'],
+                            focusNode: state.formData["reason"]!['focusNode'],
+                            obscureText: state.formData["reason"]!['obsecure'],
+                            // 装饰
+                            decoration: InputDecoration(
+                              isDense: true,
+                              isCollapsed: true,
+                              border: InputBorder.none,
+                              hintText: state.formData["reason"]!['hintText'],
+                              hintStyle: const TextStyle(
+                                color: ColorConstants.white,
+                                fontSize: 15.0,
+                                fontWeight: FontWeight.w400,
+                              ),
+                            ),
+                            style: const TextStyle(
+                              color: ColorConstants.white,
+                              fontSize: 15.0,
+                              fontWeight: FontWeight.w400,
+                            ),
+                            // 键盘动作右下角图标
+                            textInputAction: TextInputAction.done,
+                            onSubmitted: (value) {
+                              FocusScope.of(context).unfocus();
+                            },
+                          ),
+                        ),
+                      ),
+
+                      //提交按钮
+                      MyButton(
+                        type: ClickType.throttle,
+                        milliseconds: 500,
+                        onPressed: () {
+                          FocusScope.of(context).unfocus();
+                          controller.doSubmit();
+                        },
+                        text: "Submit".tr,
+                        textColor: ColorConstants.white,
+                        fontSize: 16,
+                        radius: 22.5,
+                        backgroundColor: hexToColor("#FFBB1B"),
+                        fontWeight: FontWeight.w500,
+                      ).marginSymmetric(horizontal: 0, vertical: 30),
+                    ],
+                  ).paddingOnly(left: 15, right: 15),
+                ),
+              ),
+            ),
+          ),
+        ),
+      );
+    });
+  }
+}

+ 44 - 0
packages/cpt_th/lib/modules/job_er/applied_er_edit/applied_er_edit_state.dart

@@ -0,0 +1,44 @@
+import 'package:domain/entity/response/job_list_applied_edit_entity.dart';
+import 'package:domain/entity/response/t_h_applied_edit_entity.dart';
+import 'package:flutter/material.dart';
+import 'package:plugin_basic/basic_export.dart';
+
+class AppliedErEditState {
+  //表单的校验与数据
+  Map<String, Map<String, dynamic>> formData = {
+    'add_hours': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': '0',
+      'obsecure': false,
+    },
+    'total_room': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': '0',
+      'obsecure': false,
+    },
+    'reason': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter...'.tr,
+      'obsecure': false,
+    },
+  };
+
+  THAppliedEditEntity? appliedEditView;
+
+  String? appliedId;
+
+  //功能选项
+  final editOption = ['None'.tr, 'Forgot to clock in/out'.tr, 'Technical issue'.tr, 'Others'.tr];
+
+  //选项默认选中索引为0
+  int editOptionPosition = 0;
+
+  String? selectedReasonType;
+  String? selectedStatusId;
+}

+ 162 - 0
packages/cpt_th/lib/modules/job_er/applied_er_staff_detail/applied_er_staff_detail_controller.dart

@@ -0,0 +1,162 @@
+import 'package:domain/entity/response/staff_labour_history_entity.dart';
+import 'package:domain/entity/response/t_h_employee_detail_entity.dart';
+import 'package:domain/repository/th_er_repository.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/engine/loading/loading_engine.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:plugin_platform/http/http_result.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../applied_er_staff_reviews/applied_er_staff_reviews_page.dart';
+import 'applied_er_staff_detail_state.dart';
+
+class AppliedErStaffDetailController extends GetxController with DioCancelableMixin {
+  final THERRepository _erRepository = Get.find();
+  final AppliedErStaffDetailState state = AppliedErStaffDetailState();
+
+  var _curPage = 1;
+  var _isSearch = false;
+  var _needShowPlaceholder = true;
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Success;
+  String? errorMessage;
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,
+    controlFinishLoad: true,
+  );
+
+  // Refresh 刷新事件
+  Future onRefresh() async {
+    _curPage = 1;
+    fetchStaffDetail();
+  }
+
+  // Refresh 加载事件
+  Future loadMore() async {
+    _curPage++;
+    fetchStaffDetail();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _curPage = 1;
+    _needShowPlaceholder = true;
+    fetchStaffDetail();
+  }
+
+  /// 获取列表数据
+  Future fetchStaffDetail() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    } else if (_isSearch) {
+      LoadingEngine.show();
+    }
+
+    // 并发执行两个请求
+    var futures = [
+      _erRepository.fetchStaffLabourHistory(
+        state.memberId,
+        state.keyword,
+        curPage: _curPage,
+        cancelToken: cancelToken,
+      ),
+      state.detail == null
+          ? _erRepository.fetchStaffDetail(state.memberId, cancelToken: cancelToken)
+          : Future(() => HttpResult(isSuccess: true).convert(data: state.detail!)),
+    ];
+
+    //拿到结果
+    var results = await Future.wait(futures);
+    var listResult = results[0] as HttpResult<StaffLabourHistoryEntity>;
+    var detailResult = results[1] as HttpResult<THEmployeeDetailEntity>;
+
+    //详情数据
+    if (state.detail == null && detailResult.isSuccess) {
+      state.detail = detailResult.data!;
+    }
+
+    // 处理数据
+    if (listResult.isSuccess) {
+      handleList(listResult.data?.rows);
+    } else {
+      errorMessage = listResult.errorMsg;
+      changeLoadingState(LoadState.State_Error);
+    }
+
+    _isSearch = false;
+    LoadingEngine.dismiss();
+    // 最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void handleList(List<StaffLabourHistoryRows>? list) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      if (_curPage == 1) {
+        //刷新的方式
+        state.datas.clear();
+        state.datas.addAll(list);
+        refreshController.finishRefresh();
+
+        //更新展示的状态
+        changeLoadingState(LoadState.State_Success);
+      } else {
+        //加载更多
+        state.datas.addAll(list);
+        refreshController.finishLoad();
+        update();
+      }
+    } else {
+      if (_curPage == 1) {
+        //展示无数据的布局
+        state.datas.clear();
+        changeLoadingState(LoadState.State_Empty);
+        refreshController.finishRefresh();
+      } else {
+        //展示加载完成,没有更多数据了
+        refreshController.finishLoad(IndicatorResult.noMore);
+      }
+    }
+  }
+
+  // 执行搜索
+  void doSearch(String keyword) {
+    state.keyword = keyword;
+    //赋值之后刷新
+    // refreshController.callRefresh();
+    _isSearch = true;
+    onRefresh();
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    fetchStaffDetail();
+  }
+
+  @override
+  void onClose() {
+    super.onClose();
+  }
+
+  // 去员工的评论列表页面
+  void gotoRemarkHistoryPage() {
+    AppliedErStaffReviewsPage.startInstance(
+      state.memberId,
+      state.detail?.workerName,
+      double.parse(state.detail?.reviews?.avgScore ?? "0"),
+      state.detail?.reviews?.count,
+    );
+  }
+}

+ 138 - 0
packages/cpt_th/lib/modules/job_er/applied_er_staff_detail/applied_er_staff_detail_page.dart

@@ -0,0 +1,138 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/search_app_bar.dart';
+import 'package:widgets/widget_export.dart';
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/my_appbar.dart';
+
+import 'applied_er_staff_detail_controller.dart';
+import 'applied_er_staff_detail_state.dart';
+import 'staff_detail_widget.dart';
+import 'staff_labour_history_item.dart';
+
+class AppliedErStaffDetailPage extends BaseStatefulPage<AppliedErStaffDetailController> {
+  AppliedErStaffDetailPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance(String? memberId) {
+    return Get.start(RouterPath.THJobAppliedEmployeeER, arguments: {'memberId': memberId});
+  }
+
+  @override
+  AppliedErStaffDetailController createRawController() {
+    return AppliedErStaffDetailController();
+  }
+
+  @override
+  State<AppliedErStaffDetailPage> createState() => _AppliedStaffDetailState();
+}
+
+class _AppliedStaffDetailState extends BaseState<AppliedErStaffDetailPage, AppliedErStaffDetailController> {
+  late AppliedErStaffDetailState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+    state.memberId = Get.arguments['memberId'];
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return SafeArea(
+        bottom: MediaQuery.of(context).padding.bottom > 38,
+          top: false,
+          child: Container(
+            width: double.infinity,
+            height: double.infinity,
+            padding: EdgeInsets.only(top:ScreenUtil.getStatusBarH(context)),
+            decoration: const BoxDecoration(
+              gradient: LinearGradient(
+                colors: [
+                  Color(0xFF091D44),
+                  Color(0xFF245A8A),
+                  Color(0xFF7F7CEC),
+                ],
+                begin: Alignment.topCenter,
+                end: Alignment.bottomCenter,
+              ),
+            ),
+            child: Column(
+              children: [
+                MyAppBar.titleBar(context, "Staff Detail".tr),
+
+                EasyRefresh(
+                  controller: controller.refreshController,
+                  onRefresh: controller.onRefresh,
+                  onLoad: controller.loadMore,
+                  child: LoadStateLayout(
+                    state: controller.loadingState,
+                    errorMessage: controller.errorMessage,
+                    errorRetry: () {
+                      controller.retryRequest();
+                    },
+                    successSliverWidget: [
+                      //顶部用户信息
+                      SliverToBoxAdapter(
+                        child: StaffDetailWidget(detail: state.detail,onRemarkAction: (){
+                          controller.gotoRemarkHistoryPage();
+                        }),
+                      ),
+
+                      //中间搜索布局
+                      SliverToBoxAdapter(
+                        child: _buildSearchWidget(),
+                      ),
+
+                      //底部工作历史列表
+                      SliverList(
+                          delegate: SliverChildBuilderDelegate(
+                        (context, index) {
+                          return StaffLabourHistoryItem(index: index, item: state.datas[index]);
+                        },
+                        childCount: state.datas.length,
+                      ))
+                    ],
+                  ),
+                ).marginOnly(top: 5,bottom: 5).expanded(),
+              ],
+            ),
+          ),
+        );
+    });
+  }
+
+  // 搜索的布局
+  Widget _buildSearchWidget() {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        MyTextView(
+          "Job History",
+          marginLeft: 15,
+          marginTop: 10,
+          fontSize: 17,
+          textColor: ColorConstants.white,
+          isFontMedium: true,
+        ),
+        SearchAppBar(
+          value: state.keyword,
+          searchBarHeight: 38,
+          onSearch: (keyword) {
+            controller.doSearch(keyword);
+          },
+          hintText: "Job Title".tr,
+        ).marginOnly(left: 16.5, right: 1.5, bottom: 10, top: 10),
+      ],
+    );
+  }
+}

+ 15 - 0
packages/cpt_th/lib/modules/job_er/applied_er_staff_detail/applied_er_staff_detail_state.dart

@@ -0,0 +1,15 @@
+import 'package:domain/entity/response/staff_labour_history_entity.dart';
+import 'package:domain/entity/response/t_h_employee_detail_entity.dart';
+import 'package:flutter/cupertino.dart';
+
+class AppliedErStaffDetailState {
+
+  TextEditingController? searchController;
+
+  String? memberId;
+  String keyword = "";
+
+  THEmployeeDetailEntity? detail;
+  List<StaffLabourHistoryRows> datas = [];
+
+}

+ 292 - 0
packages/cpt_th/lib/modules/job_er/applied_er_staff_detail/staff_detail_widget.dart

@@ -0,0 +1,292 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:domain/entity/response/staff_detail_entity.dart';
+import 'package:domain/entity/response/t_h_employee_detail_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:plugin_platform/engine/image/image_preview.dart';
+import 'package:shared/utils/util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+/*
+ * 员工的信息
+ */
+class StaffDetailWidget extends StatelessWidget {
+  final THEmployeeDetailEntity? detail;
+  final void Function() onRemarkAction;
+
+  StaffDetailWidget({
+    required this.detail,
+    required this.onRemarkAction,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      margin: const EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 10),
+      decoration: BoxDecoration(
+        color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+        borderRadius: BorderRadius.circular(5), // 设置圆角
+      ),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          //头像
+          Center(
+              child: Hero(
+            tag: '112cc8a34e13',
+            child: MyLoadImage(
+              detail?.icon ?? "",
+              width: 100,
+              height: 100,
+            ),
+          )).onTap(() {
+            if (Utils.isNotEmpty(detail?.icon)) {
+              ImagePreviewEngine.singleImagePreview(context, detail!.icon!, heroTag: '112cc8a34e13');
+            }
+          }).marginOnly(top: 25, bottom: 15),
+
+          //姓名
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            children: [
+              MyTextView(
+                "Name:".tr,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+                isFontRegular: true,
+              ),
+              MyTextView(
+                detail?.workerName ?? "-",
+                marginLeft: 5,
+                textColor: ColorConstants.white,
+                fontSize: 14,
+                isFontMedium: true,
+              ).expanded(),
+            ],
+          ).marginOnly(left: 22, right: 22),
+
+          //性别
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            children: [
+              MyTextView(
+                "${"Gender".tr}:",
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+                isFontRegular: true,
+              ),
+              MyTextView(
+                detail?.sex ?? "-",
+                marginLeft: 5,
+                textColor: ColorConstants.white,
+                fontSize: 14,
+                isFontMedium: true,
+              ).expanded(),
+            ],
+          ).marginOnly(left: 22, right: 22, top: 10),
+
+          //电话
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            children: [
+              MyTextView(
+                "Mobile:",
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+                isFontRegular: true,
+              ),
+              MyTextView(
+                detail?.staff?.mobile ?? "-",
+                marginLeft: 5,
+                textColor: ColorConstants.white,
+                fontSize: 14,
+                isFontMedium: true,
+              ).expanded(),
+            ],
+          ).marginOnly(left: 22, right: 22, top: 10),
+
+          //身份证
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            children: [
+              MyTextView(
+                "${"ID Card No.".tr}:",
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+                isFontRegular: true,
+              ),
+              MyTextView(
+                detail?.staff?.nric ?? "-",
+                marginLeft: 5,
+                textColor: ColorConstants.white,
+                fontSize: 14,
+                isFontMedium: true,
+              ).expanded(),
+            ],
+          ).marginOnly(left: 22, right: 22, top: 10),
+
+          //评分
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            children: [
+              MyTextView(
+                "${"Reviews".tr}:",
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+                isFontRegular: true,
+              ),
+              MyTextView(
+                "${detail?.reviews?.avgScore ?? "-"} (${detail?.reviews?.count ?? "0"} Reviews)",
+                marginLeft: 5,
+                textColor: ColorConstants.textGreen0AC074,
+                fontSize: 14,
+                onClick: onRemarkAction,
+                isFontMedium: true,
+              ).expanded(),
+            ],
+          ).marginOnly(left: 22, right: 22, top: 10),
+
+          //时薪
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            children: [
+              MyTextView(
+                "${"Hourly Rate".tr}:",
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+                isFontRegular: true,
+              ),
+              MyTextView(
+                detail?.hourlyRate ?? "-",
+                marginLeft: 5,
+                textColor: ColorConstants.white,
+                fontSize: 14,
+                isFontMedium: true,
+              ).expanded(),
+            ],
+          ).marginOnly(left: 22, right: 22, top: 10),
+
+          //Typhoid
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            children: [
+              MyTextView(
+                "Typhoid:",
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+                isFontRegular: true,
+              ),
+              MyTextView(
+                detail?.staff?.typhoidDate ?? "-",
+                marginLeft: 5,
+                textColor: ColorConstants.white,
+                fontSize: 14,
+                isFontMedium: true,
+              ).expanded(),
+            ],
+          ).marginOnly(left: 22, right: 22, top: 10),
+
+          //Food hy
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            children: [
+              MyTextView(
+                "Food hy:",
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+                isFontRegular: true,
+              ),
+              MyTextView(
+                detail?.staff?.foodHygiene ?? "-",
+                marginLeft: 5,
+                textColor: ColorConstants.white,
+                fontSize: 14,
+                isFontMedium: true,
+              ).expanded(),
+            ],
+          ).marginOnly(left: 22, right: 22, top: 10),
+
+          //Criminal check:
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            children: [
+              MyTextView(
+                "Criminal check:",
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+                isFontRegular: true,
+              ),
+              MyTextView(
+                Utils.isNotEmpty(detail?.staff?.criminalFile) ? "criminal check file" : "",
+                marginLeft: 5,
+                textColor: ColorConstants.textYellowFFBB1B,
+                textDecoration: TextDecoration.underline,
+                decorationColor: ColorConstants.textYellowFFBB1B,
+                decorationThickness: 2.0,
+                decorationStyle: TextDecorationStyle.solid,
+                fontSize: 14,
+                onClick: () {
+                  if (Utils.isNotEmpty(detail?.staff?.criminalFile)) {
+                    ImagePreviewEngine.singleImagePreview(context, detail!.staff!.criminalFile!);
+                  }
+                },
+                isFontMedium: true,
+              ).expanded(),
+            ],
+          ).marginOnly(left: 22, right: 22, top: 10),
+
+          //Criminal check time:
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            children: [
+              MyTextView(
+                "Criminal check time:",
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+                isFontRegular: true,
+              ),
+              MyTextView(
+                detail?.staff?.criminalDate ?? "-",
+                marginLeft: 5,
+                textColor: ColorConstants.white,
+                fontSize: 14,
+                isFontMedium: true,
+              ).expanded(),
+            ],
+          ).marginOnly(left: 22, right: 22, top: 10),
+
+          //紧急联系人电话
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            children: [
+              MyTextView(
+                "Clothing Size: ",
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+                isFontRegular: true,
+              ),
+              MyTextView(
+                detail?.clothingSize ?? "-",
+                isFontMedium: true,
+                textColor: ColorConstants.textYellowFFBB1B,
+                fontSize: 14,
+                marginLeft: 5,
+                marginRight: 5,
+                textDecoration: TextDecoration.underline,
+                decorationColor: ColorConstants.textYellowFFBB1B,
+                // 可选,设置下划线的颜色
+                decorationThickness: 2.0,
+                // 可选,设置下划线的粗细
+                decorationStyle: TextDecorationStyle.solid,
+              ).expanded(),
+            ],
+          ).marginOnly(left: 22, right: 22, top: 10, bottom: 26),
+        ],
+      ),
+    );
+  }
+}

+ 215 - 0
packages/cpt_th/lib/modules/job_er/applied_er_staff_detail/staff_labour_history_item.dart

@@ -0,0 +1,215 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:domain/entity/response/staff_labour_history_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_text_view.dart';
+
+/*
+ * 员工的做工记录
+ */
+class StaffLabourHistoryItem extends StatelessWidget {
+  final int index;
+  final StaffLabourHistoryRows item;
+
+  StaffLabourHistoryItem({
+    required this.index,
+    required this.item,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      padding: const EdgeInsets.symmetric(vertical: 23, horizontal: 21),
+      margin: const EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 5),
+      decoration: BoxDecoration(
+        color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+        borderRadius: BorderRadius.circular(5), // 设置圆角
+      ),
+      child: Column(
+        mainAxisSize: MainAxisSize.max,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          //工作日期
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "${"Job Date".tr}:",
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //日期
+              MyTextView(
+                item.jobDate ?? "-",
+                isFontRegular: true,
+                textColor: ColorConstants.white,
+                fontSize: 14,
+                marginLeft: 5,
+                marginRight: 5,
+              ).expanded(),
+
+            ],
+          ),
+
+          // 工作开始时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Start Time:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //时间
+              MyTextView(
+                item.startTime ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 12),
+
+          //工作结束时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "End Time:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //时间
+              MyTextView(
+                item.endTime ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 12),
+
+          //雇主
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "${"Employer".tr}:",
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //标题
+              MyTextView(
+                item.employerName ?? "-",
+                isFontRegular: true,
+                textColor: ColorConstants.white,
+                fontSize: 14,
+                marginLeft: 5,
+                marginRight: 5,
+              ).expanded(),
+
+            ],
+          ).marginOnly(top: 12),
+
+          //工作标题(模板)
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Job Title:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //标题
+              MyTextView(
+                item.jobTitle ?? "-",
+                isFontRegular: true,
+                textColor: ColorConstants.white,
+                fontSize: 14,
+                marginLeft: 5,
+                marginRight: 5,
+              ).expanded(),
+
+            ],
+          ).marginOnly(top: 12),
+
+          //部门
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Outlet:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //部门
+              MyTextView(
+                item.outletName ?? "-",
+                isFontRegular: true,
+                textColor: ColorConstants.white,
+                fontSize: 14,
+                marginLeft: 5,
+                marginRight: 5,
+              ).expanded(),
+
+            ],
+          ).marginOnly(top: 12),
+
+
+          // 状态
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Status:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //发布状态
+              MyTextView(
+                item.statusShow ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: "Completed" == item.statusShow
+                    ? ColorConstants.textGreen05DC82
+                    : "Cancelled" == item.statusShow || "Rejected" == item.statusShow
+                        ? ColorConstants.textRedFF6262
+                        : "Revised" == item.statusShow || "Pending" == item.statusShow || "Approve" == item.statusShow
+                            ? ColorConstants.textYellowFFBB1B
+                            : ColorConstants.textBlue06D9FF,  //默认蓝色
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 12),
+
+        ],
+      ),
+    );
+  }
+}

+ 108 - 0
packages/cpt_th/lib/modules/job_er/applied_er_staff_reviews/applied_er_staff_reviews_controller.dart

@@ -0,0 +1,108 @@
+import 'package:domain/entity/response/t_h_employee_remarks_entity.dart';
+import 'package:domain/repository/th_er_repository.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'applied_er_staff_reviews_state.dart';
+
+class AppliedErStaffReviewsController extends GetxController with DioCancelableMixin {
+  final THERRepository _erRepository = Get.find();
+  final AppliedErStaffReviewsState state = AppliedErStaffReviewsState();
+
+  var _curPage = 1;
+  var _needShowPlaceholder = true;
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Success;
+  String? errorMessage;
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,
+    controlFinishLoad: true,
+  );
+
+  // Refresh 刷新事件
+  Future onRefresh() async {
+    _curPage = 1;
+    fetchStaffReviews();
+  }
+
+  // Refresh 加载事件
+  Future loadMore() async {
+    _curPage++;
+    fetchStaffReviews();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _curPage = 1;
+    _needShowPlaceholder = true;
+    fetchStaffReviews();
+  }
+
+  /// 获取列表数据
+  Future fetchStaffReviews() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    }
+
+    var listResult = await _erRepository.fetchStaffRemarkHistory(state.memberId, curPage: _curPage, cancelToken: cancelToken);
+
+    // 处理数据
+    if (listResult.isSuccess) {
+      handleList(listResult.data?.reviews);
+    } else {
+      errorMessage = listResult.errorMsg;
+      changeLoadingState(LoadState.State_Error);
+    }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void handleList(List<THEmployeeRemarksReviews>? list) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      if (_curPage == 1) {
+        //刷新的方式
+        state.datas.clear();
+        state.datas.addAll(list);
+        refreshController.finishRefresh();
+
+        //更新展示的状态
+        changeLoadingState(LoadState.State_Success);
+      } else {
+        //加载更多
+        state.datas.addAll(list);
+        refreshController.finishLoad();
+        update();
+      }
+    } else {
+      if (_curPage == 1) {
+        //展示无数据的布局
+        state.datas.clear();
+        changeLoadingState(LoadState.State_Empty);
+        refreshController.finishRefresh();
+      } else {
+        //展示加载完成,没有更多数据了
+        refreshController.finishLoad(IndicatorResult.noMore);
+      }
+    }
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    fetchStaffReviews();
+  }
+}

+ 162 - 0
packages/cpt_th/lib/modules/job_er/applied_er_staff_reviews/applied_er_staff_reviews_page.dart

@@ -0,0 +1,162 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/shatter/rating_widget.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'applied_er_staff_reviews_controller.dart';
+
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/my_appbar.dart';
+
+import 'applied_staff_reviews_item.dart';
+import 'applied_er_staff_reviews_state.dart';
+
+/*
+ * 员工的评论页面
+ */
+class AppliedErStaffReviewsPage extends BaseStatefulPage<AppliedErStaffReviewsController> {
+  AppliedErStaffReviewsPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance(String? memberId, String? staffName, double? reviews, String? reviewCount) {
+    return Get.start(
+      RouterPath.THJobAppliedEmployeeRemarkER,
+      arguments: {'memberId': memberId, 'staffName': staffName, 'reviews': reviews, 'reviewCount': reviewCount},
+    );
+  }
+
+  @override
+  AppliedErStaffReviewsController createRawController() {
+    return AppliedErStaffReviewsController();
+  }
+
+  @override
+  State<AppliedErStaffReviewsPage> createState() => _AppliedStaffReviewsState();
+}
+
+class _AppliedStaffReviewsState extends BaseState<AppliedErStaffReviewsPage, AppliedErStaffReviewsController> {
+  late AppliedErStaffReviewsState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+    state.memberId = Get.arguments['memberId'];
+    state.staffName = Get.arguments['staffName'];
+    state.reviews = Get.arguments['reviews'];
+    state.reviewCount = Get.arguments['reviewCount'];
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return SafeArea(
+        bottom: MediaQuery.of(context).padding.bottom > 38,
+        top: false,
+        child: Container(
+          width: double.infinity,
+          height: double.infinity,
+          padding: EdgeInsets.only(top: ScreenUtil.getStatusBarH(context)),
+          decoration: const BoxDecoration(
+            gradient: LinearGradient(
+              colors: [
+                Color(0xFF091D44),
+                Color(0xFF245A8A),
+                Color(0xFF7F7CEC),
+              ],
+              begin: Alignment.topCenter,
+              end: Alignment.bottomCenter,
+            ),
+          ),
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            mainAxisSize: MainAxisSize.max,
+            children: [
+              MyAppBar.titleBar(context, "Staff Detail".tr),
+              EasyRefresh(
+                controller: controller.refreshController,
+                onRefresh: controller.onRefresh,
+                onLoad: controller.loadMore,
+                child: LoadStateLayout(
+                  state: controller.loadingState,
+                  errorMessage: controller.errorMessage,
+                  errorRetry: () {
+                    controller.retryRequest();
+                  },
+                  successSliverWidget: [
+                    //顶部用户信息
+                    SliverToBoxAdapter(
+                      child: _buildRemarkWidget(),
+                    ),
+
+                    //底部工作历史列表
+                    SliverList(
+                        delegate: SliverChildBuilderDelegate(
+                      (context, index) {
+                        return StaffReviewsItem(index: index, item: state.datas[index]);
+                      },
+                      childCount: state.datas.length,
+                    ))
+                  ],
+                ),
+              ).expanded(),
+            ],
+          ),
+        ),
+      );
+    });
+  }
+
+  Widget _buildRemarkWidget() {
+    return Container(
+      padding: EdgeInsets.only(bottom: 22, left: 21, right: 21, top: 18.5),
+      margin: EdgeInsets.only(left: 15, right: 15, top: 14.5, bottom: 5),
+      decoration: BoxDecoration(
+        color: Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+        borderRadius: BorderRadius.circular(5), // 设置圆角
+      ),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          MyTextView(
+            state.staffName ?? "-",
+            fontSize: 17,
+            textColor: ColorConstants.white,
+            isFontMedium: true,
+            marginBottom: 8,
+          ),
+          Row(
+            children: [
+              RatingWidget(
+                nomalImage: Assets.baseServiceRatingUnselected,
+                selectImage: Assets.baseServiceRatingSelected,
+                size: 21,
+                padding: 5,
+                selectAble: false,
+                integerOnly: false,
+                value: state.reviews ?? 0,
+                onRatingUpdate: (value) {},
+              ),
+              MyTextView(
+                "${state.reviews.toString()} (${state.reviewCount} Reviews)",
+                fontSize: 14,
+                marginLeft: 12,
+                textColor: ColorConstants.textYellowF8AE00,
+                isFontMedium: true,
+              ).expanded(),
+            ],
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 10 - 0
packages/cpt_th/lib/modules/job_er/applied_er_staff_reviews/applied_er_staff_reviews_state.dart

@@ -0,0 +1,10 @@
+import 'package:domain/entity/response/t_h_employee_remarks_entity.dart';
+
+class AppliedErStaffReviewsState {
+  String? memberId;
+  String? staffName;
+  double? reviews;
+  String? reviewCount;
+
+  List<THEmployeeRemarksReviews> datas = [];
+}

+ 151 - 0
packages/cpt_th/lib/modules/job_er/applied_er_staff_reviews/applied_staff_reviews_item.dart

@@ -0,0 +1,151 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/response/t_h_employee_remarks_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/shatter/rating_widget.dart';
+
+/*
+ * 员工的评价记录
+ */
+class StaffReviewsItem extends StatelessWidget {
+  final int index;
+  final THEmployeeRemarksReviews item;
+
+  StaffReviewsItem({
+    required this.index,
+    required this.item,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      padding: const EdgeInsets.only(bottom: 25, left: 21,right: 21,top: 18.5),
+      margin: const EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 5),
+      decoration: BoxDecoration(
+        color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+        borderRadius: BorderRadius.circular(5), // 设置圆角
+      ),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          MyTextView(
+            item.employerName ?? "",
+            isFontRegular: true,
+            textColor: Colors.white,
+            fontSize: 17,
+          ),
+
+          //态度评分
+          Row(
+            children: [
+              MyTextView(
+                "Attitude".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 15,
+              ).expanded(flex: 2),
+              RatingWidget(
+                nomalImage: Assets.baseServiceRatingUnselected,
+                selectImage: Assets.baseServiceRatingSelected,
+                size: 21,
+                padding: 5,
+                selectAble: false,
+                integerOnly: true,
+                value: item.attitudeRate,
+                onRatingUpdate: (value) {},
+              ).expanded(flex: 3)
+            ],
+          ).marginOnly(top: 15),
+
+          //表现评分
+          Row(
+            children: [
+              MyTextView(
+                "Performance".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 15,
+              ).expanded(flex: 2),
+              RatingWidget(
+                nomalImage: Assets.baseServiceRatingUnselected,
+                selectImage: Assets.baseServiceRatingSelected,
+                size: 21,
+                padding: 5,
+                selectAble: false,
+                integerOnly: true,
+                value: item.performanceRate,
+                onRatingUpdate: (value) {},
+              ).expanded(flex: 3)
+            ],
+          ).marginOnly(top: 15),
+
+          //经验评分
+          Row(
+            children: [
+              MyTextView(
+                "Experience".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 15,
+              ).expanded(flex: 2),
+              RatingWidget(
+                nomalImage: Assets.baseServiceRatingUnselected,
+                selectImage: Assets.baseServiceRatingSelected,
+                size: 21,
+                padding: 5,
+                selectAble: false,
+                integerOnly: true,
+                value: item.experienceRate,
+                onRatingUpdate: (value) {},
+              ).expanded(flex: 3)
+            ],
+          ).marginOnly(top: 15),
+
+          //着装评分
+          Row(
+            children: [
+              MyTextView(
+                "Grooming".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 15,
+              ).expanded(flex: 2),
+              RatingWidget(
+                nomalImage: Assets.baseServiceRatingUnselected,
+                selectImage: Assets.baseServiceRatingSelected,
+                size: 21,
+                padding: 5,
+                selectAble: false,
+                integerOnly: true,
+                value: item.groomingRate,
+                onRatingUpdate: (value) {},
+              ).expanded(flex: 3)
+            ],
+          ).marginOnly(top: 15),
+
+          MyTextView(
+            item.feedback ?? "",
+            textColor: Colors.white,
+            fontSize: 15,
+            isFontRegular: true,
+            borderColor: Color(0XFF6D92BA),
+            borderWidth: 0.5,
+            boxHeight: 130,
+            boxWidth: double.infinity,
+            paddingTop: 15,
+            paddingLeft: 12.5,
+            paddingRight: 12.5,
+            paddingBottom: 15,
+            marginTop: 18,
+          ),
+
+        ],
+      ),
+    );
+  }
+}

+ 98 - 0
packages/cpt_th/lib/modules/job_er/applied_er_workflow/applied_er_workflow_controller.dart

@@ -0,0 +1,98 @@
+import 'package:domain/entity/response/job_list_applied_work_flow_entity.dart';
+import 'package:domain/repository/th_er_repository.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'applied_er_workflow_state.dart';
+
+/*
+ * 已申请的员工状态审核流
+ */
+class  AppliedErWorkflowController extends GetxController with DioCancelableMixin{
+  final THERRepository _erRepository = Get.find();
+  final AppliedErWorkflowState state = AppliedErWorkflowState();
+
+  var _needShowPlaceholder = true;
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Success;
+  String? errorMessage;
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,
+    controlFinishLoad: false,
+  );
+
+  // Refresh 刷新事件
+  Future onRefresh() async {
+    fetchWorkFlowList();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _needShowPlaceholder = true;
+    fetchWorkFlowList();
+  }
+
+  /// 获取服务器数据,成员考勤列表
+  Future fetchWorkFlowList() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    }
+
+    //获取到数据
+    var result = await _erRepository.fetchAppliedWorkFlow(
+      state.appliedId,
+      cancelToken: cancelToken,
+    );
+
+    //处理数据
+    if (result.isSuccess) {
+      handleList(result.data?.records);
+      refreshController.finishRefresh(IndicatorResult.success);
+    } else {
+      errorMessage = result.errorMsg;
+      changeLoadingState(LoadState.State_Error);
+      refreshController.finishRefresh(IndicatorResult.fail);
+    }
+
+    //最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void handleList(List<JobListAppliedWorkFlowRecords>? list) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      state.datas.clear();
+      state.datas.addAll(list);
+      //更新状态
+      changeLoadingState(LoadState.State_Success);
+    } else {
+      //展示无数据的布局
+      state.datas.clear();
+      changeLoadingState(LoadState.State_Empty);
+    }
+  }
+
+  @override
+  void onReady() async {
+    super.onReady();
+    fetchWorkFlowList();
+  }
+
+  @override
+  void onClose() {
+    super.onClose();
+    state.datas.clear();
+  }
+}

+ 102 - 0
packages/cpt_th/lib/modules/job_er/applied_er_workflow/applied_er_workflow_page.dart

@@ -0,0 +1,102 @@
+
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'applied_er_workflow_controller.dart';
+import 'applied_er_workflow_state.dart';
+import 'applied_workflow_item.dart';
+
+/*
+ * 已申请的员工-状态工作流列表
+ */
+class AppliedErWorkflowPage extends BaseStatefulPage<AppliedErWorkflowController> {
+  AppliedErWorkflowPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance(String? appliedId) {
+    return Get.start(RouterPath.THJobAppliedStaffWorkflowER,arguments: {'appliedId': appliedId});
+  }
+
+  @override
+  AppliedErWorkflowController createRawController() {
+    return AppliedErWorkflowController();
+  }
+
+  @override
+  State<AppliedErWorkflowPage> createState() => _AppliedWorkflowState();
+
+}
+
+class _AppliedWorkflowState extends BaseState<AppliedErWorkflowPage, AppliedErWorkflowController> {
+
+  late AppliedErWorkflowState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+    state.appliedId = Get.arguments['appliedId'];
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return  SafeArea(
+        bottom: MediaQuery.of(context).padding.bottom > 38,
+          top: false,
+          child: Container(
+            width: double.infinity,
+            height: double.infinity,
+            padding: EdgeInsets.only(top:ScreenUtil.getStatusBarH(context)),
+            decoration: const BoxDecoration(
+              gradient: LinearGradient(
+                colors: [
+                  Color(0xFF091D44),
+                  Color(0xFF245A8A),
+                  Color(0xFF7F7CEC),
+                ],
+                begin: Alignment.topCenter,
+                end: Alignment.bottomCenter,
+              ),
+            ),
+            child: Column(
+              children: [
+                MyAppBar.titleBar(context, "Workflow".tr),
+
+                EasyRefresh(
+                  controller: controller.refreshController,
+                  onRefresh: controller.onRefresh,
+                  child: LoadStateLayout(
+                    state: controller.loadingState,
+                    errorMessage: controller.errorMessage,
+                    errorRetry: () {
+                      controller.retryRequest();
+                    },
+                    successSliverWidget: [
+                      SliverList(
+                          delegate: SliverChildBuilderDelegate((context, index) {
+                              return AppliedWorkFlowItem(index: index,item: state.datas[index]);
+                            },
+                            childCount: state.datas.length,
+                          ))
+                    ],
+                  ),
+                ).marginOnly(top: 5,bottom: 5).expanded(),
+              ],
+            ).marginOnly(top: 10),
+          ),
+        );
+    });
+  }
+}
+
+

+ 10 - 0
packages/cpt_th/lib/modules/job_er/applied_er_workflow/applied_er_workflow_state.dart

@@ -0,0 +1,10 @@
+import 'package:domain/entity/response/job_list_applied_work_flow_entity.dart';
+
+class AppliedErWorkflowState {
+
+  String? appliedId;
+
+  //页面的列表数据
+  List<JobListAppliedWorkFlowRecords> datas = [];
+
+}

+ 228 - 0
packages/cpt_th/lib/modules/job_er/applied_er_workflow/applied_workflow_item.dart

@@ -0,0 +1,228 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:domain/entity/response/job_list_applied_work_flow_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_text_view.dart';
+
+/**
+ * 用工请求的工作流列表Item
+ */
+class AppliedWorkFlowItem extends StatelessWidget {
+  final int index;
+  final JobListAppliedWorkFlowRecords item;
+
+  AppliedWorkFlowItem({
+    required this.index,
+    required this.item,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      padding: EdgeInsets.symmetric(vertical: 23, horizontal: 21),
+      margin: EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 5),
+      decoration: BoxDecoration(
+        color: Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+        borderRadius: BorderRadius.circular(5), // 设置圆角
+      ),
+      child: Column(
+        mainAxisSize: MainAxisSize.max,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          //Node
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Node:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              MyTextView(
+                item.nodeName ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ),
+
+          // assigneeType
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Type:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //日期时间
+              MyTextView(
+                item.assigneeTypeShow ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // Designation
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Designation:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //人数
+              MyTextView(
+                item.designationShow ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // 状态
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Status:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //状态
+              MyTextView(
+                item.statusShow == null ? "" : item.statusShow!.tr,
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: "Approved" == item.statusShow
+                    ? ColorConstants.textGreen05DC82
+                    : "Rejected" == item.statusShow
+                    ? ColorConstants.textRedFF6262
+                    : "Recall" == item.statusShow
+                    ? ColorConstants.textYellowFFBB1B
+                    : ColorConstants.textBlue06D9FF,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // Operator
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Operator:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //发布状态
+              MyTextView(
+                item.auditName ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // 操作时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Audit Time:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //时间
+              MyTextView(
+                item.auditTime ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // 创建时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Created At:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //时间
+              MyTextView(
+                item.createdAt ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // Remark
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Remark:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //时间
+              MyTextView(
+                item.auditMark ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+        ],
+      ),
+    );
+  }
+}

+ 245 - 0
packages/cpt_th/lib/modules/job_er/job_list_er/job_list_er_controller.dart

@@ -0,0 +1,245 @@
+import 'package:cpt_th/modules/job_er/applied_er/applied_er_page.dart';
+import 'package:cpt_th/modules/labour_er/labour_request_er_add/labour_request_er_add_page.dart';
+import 'package:domain/entity/response/job_list_e_r_option_entity.dart';
+import 'package:domain/entity/response/labour_list_e_r_entity.dart';
+import 'package:domain/repository/th_er_repository.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/constants/app_constant.dart';
+import 'package:plugin_platform/engine/dialog/dialog_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:plugin_platform/http/http_result.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:shared/utils/event_bus.dart';
+import 'package:widgets/dialog/app_default_dialog.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'job_list_er_filter.dart';
+import 'job_list_er_state.dart';
+
+class JobListController extends GetxController with DioCancelableMixin {
+  final THERRepository _erRepository = Get.find();
+  final JobListErState state = JobListErState();
+
+  var _curPage = 1;
+  var _needShowPlaceholder = true;
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Success;
+  String? errorMessage;
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,
+    controlFinishLoad: true,
+  );
+
+  // Refresh 刷新事件
+  Future onRefresh() async {
+    _curPage = 1;
+    fetchNotifyList();
+  }
+
+  // Refresh 加载事件
+  Future loadMore() async {
+    _curPage++;
+    fetchNotifyList();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _curPage = 1;
+    _needShowPlaceholder = true;
+    fetchNotifyList();
+  }
+
+  /// 获取服务器数据,通知消息列表
+  Future fetchNotifyList() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    }
+
+    // 并发执行两个请求
+    var futures = [
+      _erRepository.fetchERJobListTable(
+        keyword: state.keyword,
+        startDate: DateTimeUtils.formatDate(state.selectedStartDate, format: "yyyy-MM-dd"),
+        endDate: DateTimeUtils.formatDate(state.selectedEndDate, format: "yyyy-MM-dd"),
+        status: state.selectedStatusId,
+        outletId: state.selectedOutletId,
+        trending: state.selectedTrending,
+        curPage: _curPage,
+        cancelToken: cancelToken,
+      ),
+      state.indexOptions == null
+          ? _erRepository.fetchERJobListOption(
+              date: DateTimeUtils.formatDate(state.selectedStartDate, format: "yyyy-MM-dd"),
+              cancelToken: cancelToken,
+            )
+          : Future(() => HttpResult(isSuccess: true).convert(data: state.indexOptions!)),
+    ];
+
+    //拿到结果
+    var results = await Future.wait(futures);
+    var listResult = results[0] as HttpResult<LabourListEREntity>;
+    var optionResult = results[1] as HttpResult<JobListEROptionEntity>;
+
+    //选项数据
+    if (state.indexOptions == null && optionResult.isSuccess) {
+      state.indexOptions = optionResult.data!;
+    }
+
+    // 处理数据
+    if (listResult.isSuccess) {
+      handleList(listResult.data?.rows);
+    } else {
+      errorMessage = listResult.errorMsg;
+      changeLoadingState(LoadState.State_Error);
+    }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void handleList(List<LabourListERRows>? list) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      if (_curPage == 1) {
+        //刷新的方式
+        state.datas.clear();
+        state.datas.addAll(list);
+        refreshController.finishRefresh();
+
+        //更新展示的状态
+        changeLoadingState(LoadState.State_Success);
+      } else {
+        //加载更多
+        state.datas.addAll(list);
+        refreshController.finishLoad();
+        update();
+      }
+    } else {
+      if (_curPage == 1) {
+        //展示无数据的布局
+        state.datas.clear();
+        changeLoadingState(LoadState.State_Empty);
+        refreshController.finishRefresh();
+      } else {
+        //展示加载完成,没有更多数据了
+        refreshController.finishLoad(IndicatorResult.noMore);
+      }
+    }
+  }
+
+  // 执行搜索
+  void doSearch(String keyword) {
+    state.keyword = keyword;
+    //赋值之后刷新
+    refreshController.callRefresh();
+  }
+
+  // 清空搜索条件
+  void resetFiltering() {
+    state.keyword = "";
+    state.searchController.text = "";
+
+    state.selectedStartDate = null;
+    state.selectedEndDate = null;
+    state.selectedStatusId = null;
+    state.selectedOutletId = null;
+
+    //赋值之后刷新
+    refreshController.callRefresh();
+  }
+
+  //展示筛选的弹窗
+  void showFilterDialog() {
+    if (state.indexOptions != null) {
+      DialogEngine.show(
+        widget: JobListERFilter(
+          optionResult: state.indexOptions!,
+          selectedStartDate: state.selectedStartDate,
+          selectedEndDate: state.selectedEndDate,
+          selectedStatusId: state.selectedStatusId,
+          selectedOutletId: state.selectedOutletId,
+          selectedDivisionId: state.selectedDivisionId,
+          selectedTrending: state.selectedTrending,
+          onFilterAction: (startDate, endDate, statusId, outletId, divisionId,trending) {
+            state.selectedStartDate = startDate;
+            state.selectedEndDate = endDate;
+            state.selectedStatusId = statusId;
+            state.selectedOutletId = outletId;
+            state.selectedDivisionId = divisionId;
+            state.selectedTrending = trending;
+
+            //赋值之后刷新
+            refreshController.callRefresh();
+          },
+        ),
+        position: DialogPosition.top,
+        animType: DialogAnimation.fade,
+      );
+    }
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    fetchNotifyList();
+  }
+
+  @override
+  void onClose() {
+    state.datas.clear();
+    super.onClose();
+  }
+
+  /// 去编辑页面
+  void gotoJobEditPage(LabourListERRows data) {
+    LabourRequestERAddPage.startInstance(1, data.jobId, (result){
+      refreshController.callRefresh();
+    });
+  }
+
+  /// 去已申请的成员列表
+  void gotoJobAppliedPage(LabourListERRows data) {
+    AppliedErPage.startInstance(data.jobId);
+  }
+
+  /// 删除当前的工作
+  void doDeleteAction(LabourListERRows data) {
+    DialogEngine.show(
+        widget: AppDefaultDialog(
+      title: "Confirmation".tr,
+      message: "Are you sure you want to delete this job?".tr,
+      confirmAction: () {
+        _requestDelete(data);
+      },
+    ));
+  }
+
+  /// 请求接口确认删除
+  void _requestDelete(LabourListERRows data) async {
+    //请求接口
+    var result = await _erRepository.deleteJobListERSubmit(jobId: data.jobId!, type: 'delete', cancelToken: cancelToken);
+
+    //处理数据
+    if (result.isSuccess) {
+      //清除数据,去首页
+      state.datas.removeWhere((element) => element.jobId == data.jobId);
+      update();
+      //发送通知Labour Request 列表刷新
+      bus.emit(AppConstant.eventLabourRequestRefresh, data.jobId);
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+  }
+}

+ 510 - 0
packages/cpt_th/lib/modules/job_er/job_list_er/job_list_er_filter.dart

@@ -0,0 +1,510 @@
+import 'dart:ui';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/response/index_option_entity.dart';
+import 'package:domain/entity/response/job_list_e_r_option_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/picker/date_picker_util.dart';
+import 'package:widgets/picker/option_pick_util.dart';
+import 'package:widgets/widget_export.dart';
+
+/*
+ * 工作列表的筛选
+ */
+class JobListERFilter extends StatefulWidget {
+  void Function(DateTime? selectedStartDate, DateTime? selectedEndDate, String? selectedStatusId, String? selectedOutletId,
+      String? selectedDivisionId,String? selectedTrending)?
+      onFilterAction;
+  JobListEROptionEntity optionResult;
+  DateTime? selectedStartDate;
+  DateTime? selectedEndDate;
+  String? selectedStatusId;
+  String? selectedOutletId;
+  String? selectedDivisionId;
+  String? selectedTrending;
+
+  JobListERFilter({
+    required this.optionResult,
+    required this.selectedStartDate,
+    required this.selectedEndDate,
+    required this.selectedStatusId,
+    required this.selectedOutletId,
+    required this.selectedDivisionId,
+    required this.selectedTrending,
+    this.onFilterAction,
+  });
+
+  @override
+  State<JobListERFilter> createState() => _JobListFilterState();
+}
+
+class _JobListFilterState extends State<JobListERFilter> {
+  DateTime? selectedStartDate;
+  DateTime? selectedEndDate;
+  String? selectedStatusId;
+  String? selectedOutletId;
+  String? selectedDivisionId;
+  String? selectedTrending;
+  
+  List<IndexOptionEntity> trendingList = [];
+
+  @override
+  void initState() {
+    super.initState();
+    this.selectedStartDate = widget.selectedStartDate;
+    this.selectedEndDate = widget.selectedEndDate;
+    this.selectedStatusId = widget.selectedStatusId;
+    this.selectedOutletId = widget.selectedOutletId;
+    this.selectedDivisionId = widget.selectedDivisionId;
+    this.selectedTrending = widget.selectedTrending;
+    trendingList.add(IndexOptionEntity()..value = 'part_comp'..txt = 'Partly Complete');
+    trendingList.add(IndexOptionEntity()..value = 'ot_job'..txt = 'OT Job');
+    trendingList.add(IndexOptionEntity()..value = 'without_position'..txt = 'Without Position');
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      mainAxisAlignment: MainAxisAlignment.start,
+      children: [
+        SizedBox(
+          height: kToolbarHeight + ScreenUtil.getStatusBarH(context) + 1,
+        ),
+        Container(
+          padding: const EdgeInsets.only(left: 15, right: 15, top: 17.5, bottom: 20),
+          width: double.infinity,
+          decoration: const BoxDecoration(
+            color: Colors.white,
+          ),
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              //Division
+              MyTextView(
+                "Division".tr,
+                fontSize: 14,
+                isFontMedium: true,
+                textColor: ColorConstants.black33,
+              ),
+
+              //选择部门
+              Container(
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
+                height: 45,
+                decoration: const BoxDecoration(
+                  color: ColorConstants.grayECECEC,
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      selectedDivisionId == null || selectedDivisionId == "0"
+                          ? ""
+                          : widget.optionResult.divisionList!.firstWhere((element) => element.value.toString() == selectedDivisionId).txt!,
+                      hint: "Choose Division".tr,
+                      textHintColor: ColorConstants.textBlackHint,
+                      fontSize: 14,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black33,
+                    ).expanded(),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                  ],
+                ),
+              ).onTap(() {
+                pickerDivision();
+              }),
+
+              //部门
+              MyTextView(
+                "Outlet".tr,
+                fontSize: 14,
+                marginTop: 11,
+                isFontMedium: true,
+                textColor: ColorConstants.black33,
+              ),
+
+              //选择部门
+              Container(
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
+                height: 45,
+                decoration: const BoxDecoration(
+                  color: ColorConstants.grayECECEC,
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      selectedOutletId == null || selectedOutletId == "0"
+                          ? ""
+                          : widget.optionResult.outletList!.firstWhere((element) => element.value.toString() == selectedOutletId).txt!,
+                      hint: "Choose Outlet".tr,
+                      textHintColor: ColorConstants.textBlackHint,
+                      fontSize: 14,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black33,
+                    ).expanded(),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                  ],
+                ),
+              ).onTap(() {
+                pickerOutlet();
+              }),
+
+              //Trending
+              MyTextView(
+                "Trending".tr,
+                fontSize: 14,
+                marginTop: 11,
+                isFontMedium: true,
+                textColor: ColorConstants.black33,
+              ),
+
+              //选择Trending
+              Container(
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
+                height: 45,
+                decoration: const BoxDecoration(
+                  color: ColorConstants.grayECECEC,
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      selectedTrending == null
+                          ? ""
+                          : trendingList.firstWhere((element) => element.value.toString() == selectedTrending).txt!,
+                      hint: "Choose Trending".tr,
+                      textHintColor: ColorConstants.textBlackHint,
+                      fontSize: 14,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black33,
+                    ).expanded(),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                  ],
+                ),
+              ).onTap(() {
+                pickerTrending();
+              }),
+
+              //状态
+              MyTextView(
+                "Status".tr,
+                fontSize: 14,
+                isFontMedium: true,
+                marginTop: 11,
+                textColor: ColorConstants.black33,
+              ),
+
+              //选择状态
+              Container(
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
+                height: 45,
+                decoration: const BoxDecoration(
+                  color: ColorConstants.grayECECEC,
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      selectedStatusId == null || selectedStatusId == "0"
+                          ? ""
+                          : widget.optionResult.statusList!.firstWhere((element) => element.value.toString() == selectedStatusId).txt!,
+                      fontSize: 14,
+                      hint: "Choose Status".tr,
+                      textHintColor: ColorConstants.textBlackHint,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black33,
+                    ).expanded(),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                  ],
+                ),
+              ).onTap(() {
+                pickerStatus();
+              }),
+
+              //开始时间
+              MyTextView(
+                "Start Date".tr,
+                fontSize: 14,
+                isFontMedium: true,
+                marginTop: 11,
+                textColor: ColorConstants.black33,
+              ),
+
+              //选择时间
+              Container(
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
+                height: 45,
+                decoration: const BoxDecoration(
+                  color: ColorConstants.grayECECEC,
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      selectedStartDate == null ? "" : DateTimeUtils.formatDate(selectedStartDate, format: "yyyy-MM-dd"),
+                      fontSize: 14,
+                      hint: "Choose Start Date".tr,
+                      textHintColor: ColorConstants.textBlackHint,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black33,
+                    ).expanded(),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                  ],
+                ),
+              ).onTap(() {
+                pickerStartDate();
+              }),
+
+              //结束日期
+              MyTextView(
+                "End Date".tr,
+                fontSize: 14,
+                marginTop: 11,
+                isFontMedium: true,
+                textColor: ColorConstants.black33,
+              ),
+
+              //选择结束日期
+              Container(
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
+                height: 45,
+                decoration: const BoxDecoration(
+                  color: ColorConstants.grayECECEC,
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      selectedEndDate == null ? "" : DateTimeUtils.formatDate(selectedEndDate, format: "yyyy-MM-dd"),
+                      fontSize: 14,
+                      hint: "Choose End Date".tr,
+                      textHintColor: ColorConstants.textBlackHint,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black33,
+                    ).expanded(),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                  ],
+                ),
+              ).onTap(() {
+                pickerEndDate();
+              }),
+
+              //按钮组
+              Row(
+                children: [
+                  MyButton(
+                    onPressed: () {
+                      //只是Reset当前的弹窗筛选选项
+                      widget.selectedStartDate = null;
+                      widget.selectedEndDate = null;
+                      widget.selectedStatusId = null;
+                      widget.selectedOutletId = null;
+                      widget.selectedDivisionId = null;
+                      widget.selectedTrending = null;
+
+                      setState(() {
+                        selectedStartDate = null;
+                        selectedEndDate = null;
+                        selectedStatusId = null;
+                        selectedOutletId = null;
+                        selectedDivisionId = null;
+                        selectedTrending = null;
+                      });
+                    },
+                    text: "Reset".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#FFBB1B"),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 36,
+                  ).expanded(),
+                  const SizedBox(width: 15),
+                  MyButton(
+                    onPressed: () {
+                      onCancel();
+                      widget.onFilterAction?.call(selectedStartDate, selectedEndDate, selectedStatusId, selectedOutletId,selectedDivisionId,selectedTrending);
+                    },
+                    text: "Filter".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#0AC074"),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 36,
+                  ).expanded(),
+                ],
+              ).marginOnly(top: 20),
+            ],
+          ),
+        ),
+        Center(child: const MyAssetImage(Assets.baseServiceDialogDeleteIcon, width: 26.5, height: 26.5).marginOnly(top: 35)).onTap(() {
+          onCancel();
+        }),
+      ],
+    );
+  }
+
+  //取消弹框
+  void onCancel() async {
+    SmartDialog.dismiss();
+  }
+
+  /// 筛选开始日期
+  void pickerStartDate() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: selectedStartDate,
+      onDateTimeChanged: (date) {
+        setState(() {
+          selectedStartDate = date;
+        });
+      },
+      title: "Start Date".tr,
+    );
+  }
+
+  /// 筛选结束日期
+  void pickerEndDate() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: selectedEndDate ?? selectedStartDate,
+      onDateTimeChanged: (date) {
+        setState(() {
+          selectedEndDate = date;
+        });
+      },
+      title: "End Date".tr,
+    );
+  }
+
+  /// 筛选部门
+  void pickerOutlet() {
+    int selectedOutletIndex;
+    if (selectedOutletId == null) {
+      selectedOutletIndex = 0;
+    } else {
+      selectedOutletIndex = widget.optionResult.outletList!.indexWhere((department) => department.value.toString() == selectedOutletId);
+    }
+
+    if (selectedOutletIndex < 0) {
+      selectedOutletIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: widget.optionResult.outletList!.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedOutletIndex,
+      onPickerChanged: (_, index) {
+        setState(() {
+          selectedOutletId = widget.optionResult.outletList![index].value!.toString();
+        });
+      },
+    );
+  }
+
+  /// 筛选状态
+  void pickerStatus() {
+    int selectedStatusIndex;
+    if (selectedStatusId == null) {
+      selectedStatusIndex = 0;
+    } else {
+      selectedStatusIndex = widget.optionResult.statusList!.indexWhere((department) => department.value.toString() == selectedStatusId);
+    }
+
+    if (selectedStatusIndex < 0) {
+      selectedStatusIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: widget.optionResult.statusList!.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedStatusIndex,
+      onPickerChanged: (_, index) {
+        setState(() {
+          selectedStatusId = widget.optionResult.statusList![index].value!.toString();
+        });
+      },
+    );
+  }
+
+  //筛选大部门
+  void pickerDivision() {
+    int selectedDivisionIndex;
+    if (selectedDivisionId == null) {
+      selectedDivisionIndex = 0;
+    } else {
+      selectedDivisionIndex = widget.optionResult.divisionList!.indexWhere((department) => department.value.toString() == selectedDivisionId);
+    }
+
+    if (selectedDivisionIndex < 0) {
+      selectedDivisionIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: widget.optionResult.divisionList!.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedDivisionIndex,
+      onPickerChanged: (_, index) {
+        setState(() {
+          selectedDivisionId = widget.optionResult.divisionList![index].value!.toString();
+        });
+      },
+    );
+  }
+
+  /// 选择 Trending
+  void pickerTrending() {
+    int selectedTrendingIndex;
+    if (selectedTrending == null) {
+      selectedTrendingIndex = 0;
+    } else {
+      selectedTrendingIndex = trendingList.indexWhere((department) => department.value.toString() == selectedTrending);
+    }
+
+    if (selectedTrendingIndex < 0) {
+      selectedTrendingIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: trendingList.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedTrendingIndex,
+      onPickerChanged: (_, index) {
+        setState(() {
+          selectedTrending = trendingList[index].value!.toString();
+        });
+      },
+    );
+  }
+
+}

+ 250 - 0
packages/cpt_th/lib/modules/job_er/job_list_er/job_list_er_item.dart

@@ -0,0 +1,250 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:domain/entity/response/labour_list_e_r_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_text_view.dart';
+
+/*
+ * 用工请求的主页面列表Item
+ */
+class JobListErItem extends StatelessWidget {
+  final int index;
+  final LabourListERRows item;
+  final VoidCallback? onEditAction;
+  final VoidCallback? onDeleteAction;
+  final VoidCallback? onAppliedAction;
+
+  JobListErItem({
+    required this.index,
+    required this.item,
+    this.onEditAction,
+    this.onDeleteAction,
+    this.onAppliedAction,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      padding: const EdgeInsets.symmetric(vertical: 23, horizontal: 21),
+      margin: const EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 5),
+      decoration: BoxDecoration(
+        color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+        borderRadius: BorderRadius.circular(5), // 设置圆角
+      ),
+      child: Column(
+        mainAxisSize: MainAxisSize.max,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          //工作标题
+          MyTextView(
+            item.jobTitle ?? "-",
+            isFontMedium: true,
+            textColor: ColorConstants.textYellowFFBB1B,
+            fontSize: 14,
+          ),
+
+          // 部门
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Outlet:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //部门
+              MyTextView(
+                item.outletName ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // 工作日期时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Datetime:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //日期时间
+              MyTextView(
+                item.jobDate ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // 人数
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "No. of Staff:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //人数
+              MyTextView(
+                item.hiringNum ?? "",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: ColorConstants.textYellowFFBB1B,
+                onClick: onAppliedAction,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // 预选
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "${"Pre Selected".tr}:",
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //文本
+              MyTextView(
+                item.preNum ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // 时薪
+          // Row(
+          //   mainAxisSize: MainAxisSize.max,
+          //   crossAxisAlignment: CrossAxisAlignment.center,
+          //   children: [
+          //     MyTextView(
+          //       "${"Hourly Rate".tr}:",
+          //       isFontRegular: true,
+          //       textColor: ColorConstants.textGrayAECAE5,
+          //       fontSize: 14,
+          //     ),
+          //
+          //     //文本
+          //     MyTextView(
+          //       item.hiringNum ?? "-",
+          //       marginLeft: 5,
+          //       isFontRegular: true,
+          //       textColor: Colors.white,
+          //       fontSize: 14,
+          //     ).expanded(),
+          //   ],
+          // ).marginOnly(top: 15),
+
+          // 状态
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Status:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //状态
+              MyTextView(
+                item.statusShow == null ? "" : item.statusShow!.tr,
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: "Completed" == item.statusShow
+                    ? ColorConstants.textGreen05DC82
+                    : "Cancelled" == item.statusShow
+                        ? ColorConstants.textRedFF6262
+                        : "Revised" == item.statusShow || "Pending" == item.statusShow
+                            ? ColorConstants.textYellowFFBB1B
+                            : ColorConstants.textBlue06D9FF,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          //按钮组
+          Visibility(
+            visible: item.actionList.isNotEmpty,
+            child: Row(
+              mainAxisSize: MainAxisSize.max,
+              mainAxisAlignment: MainAxisAlignment.end,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+                //详情按钮
+                Visibility(
+                  visible: item.actionList.contains("edit"),
+                  child: MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      onEditAction?.call();
+                    },
+                    text: "Edit".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor(
+                      "#56AAFF",
+                    ),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 35,
+                  ).marginOnly(left: 12),
+                ),
+
+                //删除按钮
+                Visibility(
+                  visible: item.actionList.contains("delete"),
+                  child: MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      onDeleteAction?.call();
+                    },
+                    text: "Delete".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor(
+                      "#DC4462",
+                    ),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 35,
+                  ).marginOnly(left: 12),
+                ),
+
+              ],
+            ).marginOnly(top: 18, bottom: 2),
+          ),
+        ],
+      ),
+    ).onTap(() {
+      onAppliedAction?.call();
+    });
+  }
+}

+ 155 - 0
packages/cpt_th/lib/modules/job_er/job_list_er/job_list_er_page.dart

@@ -0,0 +1,155 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:shared/utils/util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'job_list_er_controller.dart';
+import 'job_list_er_item.dart';
+import 'job_list_er_state.dart';
+
+/*
+ * 工作列表
+ */
+class JobListERPage extends BaseStatefulPage<JobListController> {
+  JobListERPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance(String? selectedDate, void Function(dynamic value)? cb) {
+    return Get.start(RouterPath.THJobListER, arguments: {'selectedDate': selectedDate, 'cb': cb});
+  }
+
+  @override
+  JobListController createRawController() {
+    return JobListController();
+  }
+
+  @override
+  State<JobListERPage> createState() => _JobListState();
+}
+
+class _JobListState extends BaseState<JobListERPage, JobListController> {
+  late JobListErState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+    String? dateFormat = Get.arguments['selectedDate'];
+    if (Utils.isNotEmpty(dateFormat)) {
+      state.selectedStartDate =  DateTimeUtils.getDateTime(dateFormat!);
+      state.selectedEndDate =  DateTimeUtils.getDateTime(dateFormat);
+    }
+    state.cb = Get.arguments['cb'] as void Function(dynamic)?;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return SafeArea(
+        bottom: MediaQuery.of(context).padding.bottom > 38,
+        top: false,
+        child: Container(
+          width: double.infinity,
+          height: double.infinity,
+          padding: EdgeInsets.only(top: ScreenUtil.getStatusBarH(context)),
+          decoration: const BoxDecoration(
+            gradient: LinearGradient(
+              colors: [
+                Color(0xFF091D44),
+                Color(0xFF245A8A),
+                Color(0xFF7F7CEC),
+              ],
+              begin: Alignment.topCenter,
+              end: Alignment.bottomCenter,
+            ),
+          ),
+          child: Column(
+            children: [
+              MyAppBar.searchTitleBar(
+                context,
+                value: state.keyword,
+                hintText: 'Title'.tr,
+                controller: state.searchController,
+                onSearch: (keyword) {
+                  controller.doSearch(keyword);
+                },
+                actions: [
+                  //重置按钮
+                  MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      controller.resetFiltering();
+                    },
+                    text: "Reset".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#2BA9F9", opacity: 0.5),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 35,
+                  ).marginOnly(right: 15),
+
+                  //筛选图标
+                  const MyAssetImage(
+                    Assets.baseServiceTitleBarFilterIcon,
+                    width: 24,
+                    height: 16.5,
+                  ).onTap(() {
+                    FocusScope.of(context).unfocus();
+                    controller.showFilterDialog();
+                  }).marginOnly(right: 15),
+                ],
+              ),
+              //底部的列表
+              EasyRefresh(
+                controller: controller.refreshController,
+                onRefresh: controller.onRefresh,
+                onLoad: controller.loadMore,
+                child: LoadStateLayout(
+                  state: controller.loadingState,
+                  errorMessage: controller.errorMessage,
+                  errorRetry: () {
+                    controller.retryRequest();
+                  },
+                  successSliverWidget: [
+                    SliverList(
+                        delegate: SliverChildBuilderDelegate(
+                      (context, index) {
+                        return JobListErItem(
+                          index: index,
+                          item: state.datas[index],
+                          onEditAction: () {
+                            controller.gotoJobEditPage(state.datas[index]);
+                          },
+                          onAppliedAction: () {
+                            controller.gotoJobAppliedPage(state.datas[index]);
+                          },
+                          onDeleteAction: (){
+                            controller.doDeleteAction(state.datas[index]);
+                          },
+                        );
+                      },
+                      childCount: state.datas.length,
+                    ))
+                  ],
+                ),
+              ).marginOnly(top: 5, bottom: 5).expanded(),
+            ],
+          ),
+        ),
+      );
+    });
+  }
+}

+ 22 - 0
packages/cpt_th/lib/modules/job_er/job_list_er/job_list_er_state.dart

@@ -0,0 +1,22 @@
+import 'package:domain/entity/response/job_list_e_r_option_entity.dart';
+import 'package:domain/entity/response/labour_list_e_r_entity.dart';
+
+import 'package:flutter/material.dart';
+
+class JobListErState {
+  //筛选条件
+  final TextEditingController searchController = TextEditingController();
+  String keyword = "";
+  DateTime? selectedStartDate = DateTime.now();
+  DateTime? selectedEndDate;
+  String? selectedStatusId;
+  String? selectedOutletId;
+  String? selectedDivisionId;
+  String? selectedTrending;
+
+  //页面的列表数据
+  List<LabourListERRows> datas = [];
+  JobListEROptionEntity? indexOptions;
+
+  void Function(dynamic value)? cb;
+}

+ 391 - 0
packages/cpt_th/lib/modules/labour/labour_request_add/labour_request_add_controller.dart

@@ -0,0 +1,391 @@
+import 'dart:io';
+import 'dart:typed_data';
+
+import 'package:domain/entity/response/t_h_o_a_labour_detail_entity.dart';
+import 'package:domain/repository/th_oa_repository.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/engine/loading/loading_engine.dart';
+import 'package:plugin_platform/engine/notify/notify_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:plugin_platform/http/http_result.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/util.dart';
+import 'package:widgets/picker/date_picker_util.dart';
+import 'package:widgets/picker/option_pick_util.dart';
+import 'package:file_picker/file_picker.dart';
+import 'labour_request_add_state.dart';
+
+class LabourRequestAddController extends GetxController with DioCancelableMixin {
+  final THOARepository _thRepository = Get.find();
+  final LabourRequestAddState state = LabourRequestAddState();
+
+  // 获取添加选项数据
+  void fetchLabourRequestEditDetail() async {
+    HttpResult<THOALabourDetailEntity> result;
+    if (Utils.isEmpty(state.requestId)) {
+      result = await _thRepository.fetchLabourRequestAddOption(
+        cancelToken: cancelToken,
+      );
+    } else {
+      result = await _thRepository.fetchLabourRequestEditDetail(
+        state.requestId,
+        cancelToken: cancelToken,
+      );
+    }
+
+    if (result.isSuccess) {
+      state.editDetailEntity = result.data;
+
+      if (state.editDetailEntity?.labReq != null) {
+        //有详情数据
+        var needMallController = state.formData['need_male']!['controller'];
+        var needFemaleController = state.formData['need_female']!['controller'];
+        var needNumController = state.formData['need_no']!['controller'];
+        var remarkController = state.formData['remark']!['controller'];
+        var eventController = state.formData['event']!['controller'];
+        var eventTypeController = state.formData['event_type']!['controller'];
+        var eventPaxController = state.formData['event_pax']!['controller'];
+        var revenueController = state.formData['revenue']!['controller'];
+        var positionController = state.formData['position']!['controller'];
+        var totalCostController = state.formData['total_cost']!['controller'];
+
+        if (state.editDetailEntity?.labReq?.sexLimit == 1) {
+          state.genderOptionType = 1;
+          needMallController.text = state.editDetailEntity?.labReq?.maleLimit?.toString();
+          needFemaleController.text = state.editDetailEntity?.labReq?.femaleLimit?.toString();
+          needNumController.text = '';
+        } else {
+          state.genderOptionType = 0;
+          needMallController.text = '';
+          needFemaleController.text = '';
+          needNumController.text = state.editDetailEntity?.labReq?.hiringNum?.toString();
+        }
+
+        remarkController.text = state.editDetailEntity?.labReq?.description ?? '';
+        eventController.text = state.editDetailEntity?.labReq?.eventName ?? "";
+        eventTypeController.text = state.editDetailEntity?.labReq?.eventType ?? "";
+        eventPaxController.text = state.editDetailEntity?.labReq?.passengers ?? "";
+        revenueController.text = state.editDetailEntity?.labReq?.estRevenue ?? "";
+        positionController.text = state.editDetailEntity?.labReq?.position ?? "";
+        totalCostController.text = state.editDetailEntity?.labReq?.estCost ?? "";
+
+        state.selectedStartTime = DateTimeUtils.getDateTime(state.editDetailEntity!.startTime!);
+        state.selectedEndTime = DateTimeUtils.getDateTime(state.editDetailEntity!.endTime!);
+
+        state.selectedOutlet = state.editDetailEntity?.labReq?.outletName;
+        state.selectedOutletId = state.editDetailEntity?.labReq?.outletId?.toString();
+
+        state.selectedJobTitleId = state.editDetailEntity?.labReq?.jobTitleId?.toString();
+        state.selectedJobTitleName = state.editDetailEntity?.labReq?.jobTitle;
+
+        state.selectedTypeId = state.editDetailEntity?.labReq?.employmentType?.toString();
+        state.selectedType = state.editDetailEntity?.employmentList.firstWhereOrNull((element) => element.value == state.selectedTypeId)?.txt;
+
+        state.attFilePath = state.editDetailEntity?.labReq?.attUrl;
+
+        update();
+      }
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    fetchLabourRequestEditDetail();
+  }
+
+  //选择开始时间
+  void pickStartTime() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.selectedStartTime,
+      mode: CupertinoDatePickerMode.dateAndTime,
+      onDateTimeChanged: (date) {
+        state.selectedStartTime = date;
+        update();
+      },
+      title: "Start Time".tr,
+    );
+  }
+
+  // 选择结束时间
+  void pickEndTime() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.selectedEndTime ?? state.selectedStartTime,
+      mode: CupertinoDatePickerMode.dateAndTime,
+      onDateTimeChanged: (date) {
+        state.selectedEndTime = date;
+        update();
+      },
+      title: "End Time".tr,
+    );
+  }
+
+  //选择部门
+  void pickOutlet() {
+    if (state.editDetailEntity == null) {
+      return;
+    }
+
+    int selectedIndex;
+    if (state.selectedOutletId == null) {
+      selectedIndex = 0;
+    } else {
+      selectedIndex = state.editDetailEntity!.outletList.indexWhere((entity) => entity.value.toString() == state.selectedOutletId);
+    }
+
+    if (selectedIndex < 0) {
+      selectedIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: state.editDetailEntity!.outletList.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedIndex,
+      onPickerChanged: (_, index) {
+        state.selectedOutletId = state.editDetailEntity!.outletList[index].value!.toString();
+        state.selectedOutlet = state.editDetailEntity!.outletList[index].txt!.toString();
+        update();
+      },
+    );
+  }
+
+  // 提交LabourRequest表单
+  void doEditSubmit() async {
+    var maleNoController = state.formData['need_male']!['controller'];
+    var femaleNoController = state.formData['need_female']!['controller'];
+    var needNoController = state.formData['need_no']!['controller'];
+    var remarkController = state.formData['remark']!['controller'];
+    var eventController = state.formData['event']!['controller'];
+    var eventTypeController = state.formData['event_type']!['controller'];
+    var eventPaxController = state.formData['event_pax']!['controller'];
+    var revenueController = state.formData['revenue']!['controller'];
+    var positionController = state.formData['position']!['controller'];
+    var totalCostController = state.formData['total_cost']!['controller'];
+
+    String maleNo = maleNoController.text.toString();
+    String femaleNo = femaleNoController.text.toString();
+    String needNo = needNoController.text.toString();
+    String remark = remarkController.text.toString();
+    String event = eventController.text.toString();
+    String eventType = eventTypeController.text.toString();
+    String eventPax = eventPaxController.text.toString();
+    String revenue = revenueController.text.toString();
+    String position = positionController.text.toString();
+    String totalCost = totalCostController.text.toString();
+
+    if (state.selectedStartTime == null) {
+      ToastEngine.show("Select Job Start Time".tr);
+      return;
+    }
+
+    if (state.selectedEndTime == null) {
+      ToastEngine.show("Select Job End Time".tr);
+      return;
+    }
+
+    if (Utils.isEmpty(state.selectedOutletId)) {
+      ToastEngine.show("Choose Outlet".tr);
+      return;
+    }
+
+    if (state.genderOptionType == 0) {
+      if (Utils.isEmpty(needNo)) {
+        ToastEngine.show("Enter No. of Staff".tr);
+        return;
+      }
+    } else {
+      if (Utils.isEmpty(maleNo) || Utils.isEmpty(femaleNo)) {
+        ToastEngine.show("Enter No. of Staff of The Corresponding Gender".tr);
+        return;
+      }
+    }
+
+    HttpResult result;
+    if (state.pageType == 1 && Utils.isNotEmpty(state.requestId)) {
+      //编辑
+      result = await _thRepository.editLabourRequestSubmit(
+        isReviewEdit: state.isReviewEdit,
+        requestId: state.requestId,
+        jobTitleId: state.selectedJobTitleId,
+        startTime: DateTimeUtils.formatDate(state.selectedStartTime),
+        endTime: DateTimeUtils.formatDate(state.selectedEndTime),
+        repeatStart: DateTimeUtils.formatDate(state.selectedRepeatStartTime, format: "yyyy-MM-dd"),
+        repeatEnd: DateTimeUtils.formatDate(state.selectedRepeatEndTime, format: "yyyy-MM-dd"),
+        outletId: state.selectedOutletId,
+        sexLimit: state.genderOptionType,
+        maleLimit: maleNo,
+        femaleLimit: femaleNo,
+        needNum: needNo,
+        description: remark,
+        employmentType: state.selectedTypeId,
+        eventName: event,
+        eventType: eventType,
+        passengers: eventPax,
+        estRevenue: revenue,
+        position: position,
+        estCost: totalCost,
+        cancelToken: cancelToken,
+      );
+    } else {
+      //新增
+      result = await _thRepository.addLabourRequestSubmit(
+        jobTitleId: state.selectedJobTitleId,
+        startTime: DateTimeUtils.formatDate(state.selectedStartTime),
+        endTime: DateTimeUtils.formatDate(state.selectedEndTime),
+        repeatStart: DateTimeUtils.formatDate(state.selectedRepeatStartTime, format: "yyyy-MM-dd"),
+        repeatEnd: DateTimeUtils.formatDate(state.selectedRepeatEndTime, format: "yyyy-MM-dd"),
+        outletId: state.selectedOutletId,
+        sexLimit: state.genderOptionType,
+        maleLimit: maleNo,
+        femaleLimit: femaleNo,
+        needNum: needNo,
+        description: remark,
+        employmentType: state.selectedTypeId,
+        eventName: event,
+        eventType: eventType,
+        passengers: eventPax,
+        estRevenue: revenue,
+        position: position,
+        estCost: totalCost,
+        attUrl: state.attFilePath,
+        cancelToken: cancelToken,
+      );
+    }
+
+    //处理数据
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess("Successful".tr);
+
+      //根据类型刷新
+      state.cb?.call(state.requestId);
+
+      Get.back();
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+  }
+
+  //选择工作标题
+  void pickJobTitle() {
+    if (state.editDetailEntity == null) {
+      return;
+    }
+
+    int selectedIndex;
+    if (state.selectedJobTitleId == null) {
+      selectedIndex = 0;
+    } else {
+      selectedIndex = state.editDetailEntity!.titleList.indexWhere((bean) => bean.value.toString() == state.selectedJobTitleId);
+    }
+
+    if (selectedIndex < 0) {
+      selectedIndex = 0;
+    } else {
+      selectedIndex++;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: state.editDetailEntity!.titleList.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedIndex,
+      onPickerChanged: (_, index) {
+        state.selectedJobTitleId = state.editDetailEntity!.titleList[index].value!.toString();
+        state.selectedJobTitleName = state.editDetailEntity!.titleList[index].txt!.toString();
+        update();
+      },
+    );
+  }
+
+  //选择性别限制类型
+  void pickLimitType() {
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: state.genderOptions,
+      initialSelectIndex: state.genderOptionType,
+      onPickerChanged: (_, index) {
+        state.genderOptionType = index;
+        update();
+      },
+    );
+  }
+
+  void pickRepeatStartTime() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.selectedRepeatStartTime,
+      mode: CupertinoDatePickerMode.date,
+      onDateTimeChanged: (date) {
+        state.selectedRepeatStartTime = date;
+        update();
+      },
+      title: "Repeat Start Time".tr,
+    );
+  }
+
+  void pickRepeatEndTime() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.selectedRepeatEndTime ?? state.selectedRepeatStartTime,
+      mode: CupertinoDatePickerMode.date,
+      minimumDate: state.selectedRepeatStartTime,
+      onDateTimeChanged: (date) {
+        state.selectedRepeatEndTime = date;
+        update();
+      },
+      title: "Repeat End Time".tr,
+    );
+  }
+
+  //选择类型
+  void pickType() {
+    if (state.editDetailEntity == null) {
+      return;
+    }
+
+    int selectedIndex;
+    if (state.selectedTypeId == null) {
+      selectedIndex = 0;
+    } else {
+      selectedIndex = state.editDetailEntity!.employmentList.indexWhere((bean) => bean.value.toString() == state.selectedTypeId);
+    }
+
+    if (selectedIndex < 0) {
+      selectedIndex = 0;
+    } else {
+      selectedIndex++;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: state.editDetailEntity!.employmentList.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedIndex,
+      onPickerChanged: (_, index) {
+        state.selectedTypeId = state.editDetailEntity!.employmentList[index].value!.toString();
+        state.selectedType = state.editDetailEntity!.employmentList[index].txt!.toString();
+        update();
+      },
+    );
+  }
+
+  // 选择单个文件
+  void pickAttFile() async {
+    FilePickerResult? result = await FilePicker.platform.pickFiles(
+      allowMultiple: false,
+      type: FileType.custom,
+      allowedExtensions: ['jpg', 'png', 'jpeg', 'pdf', 'docx'],
+    );
+
+    if (result != null) {
+      PlatformFile file = result.files.first;
+      Log.d("path:${file.path} name:${file.name}");
+
+      final fileResult = await _thRepository.uploadFile(file.path);
+
+      if (fileResult.isSuccess) {
+        state.attFilePath = fileResult.data?.path;
+        update();
+      } else {
+        ToastEngine.show(fileResult.errorMsg ?? "Network Load Error".tr);
+      }
+    }
+  }
+}

+ 834 - 0
packages/cpt_th/lib/modules/labour/labour_request_add/labour_request_add_page.dart

@@ -0,0 +1,834 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter/widgets.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:shared/utils/util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/no_shadow_scroll_behavior.dart';
+import 'package:widgets/shatter/form_require_text.dart';
+import 'package:widgets/shatter/round_my_text_field.dart';
+import 'package:widgets/widget_export.dart';
+import 'labour_request_add_controller.dart';
+import 'labour_request_add_state.dart';
+
+class LabourRequestAddPage extends BaseStatefulPage<LabourRequestAddController> {
+  LabourRequestAddPage({Key? key}) : super(key: key);
+
+  //启动当前页面,pageType 0 是新增  1是编辑  2是详情
+  static void startInstance(int pageType, String? appliedId, bool isReviewEdit, void Function(dynamic value)? cb) {
+    return Get.start(RouterPath.THLabourRequestAddOA, arguments: {'pageType': pageType, 'appliedId': appliedId, 'isReviewEdit': isReviewEdit, 'cb': cb});
+  }
+
+  @override
+  LabourRequestAddController createRawController() {
+    return LabourRequestAddController();
+  }
+
+  @override
+  State<LabourRequestAddPage> createState() => _LabourRequestAddState();
+}
+
+class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourRequestAddController> {
+  late LabourRequestAddState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+    state.pageType = Get.arguments['pageType'];
+    state.requestId = Get.arguments['appliedId'];
+    state.isReviewEdit = Get.arguments['isReviewEdit'];
+    state.cb = Get.arguments['cb'] as void Function(dynamic)?;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return Scaffold(
+        extendBodyBehindAppBar: true,
+        appBar: MyAppBar.appBar(context, state.pageType == 1 ? "Edit Labour Requisition".tr : "Labour Requisition".tr),
+        body: SafeArea(
+          bottom: MediaQuery.of(context).padding.bottom > 38,
+          top: false,
+          child: Container(
+            width: double.infinity,
+            height: double.infinity,
+            padding: EdgeInsets.only(top: kToolbarHeight + ScreenUtil.getStatusBarH(context) + 1),
+            decoration: const BoxDecoration(
+              gradient: LinearGradient(
+                colors: [
+                  Color(0xFF091D44),
+                  Color(0xFF245A8A),
+                  Color(0xFF7F7CEC),
+                ],
+                begin: Alignment.topCenter,
+                end: Alignment.bottomCenter,
+              ),
+            ),
+            child: Scrollbar(
+              child: ScrollConfiguration(
+                behavior: NoShadowScrollBehavior(),
+                child: SingleChildScrollView(
+                  scrollDirection: Axis.vertical,
+                  physics: const BouncingScrollPhysics(),
+                  child: Column(
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    children: [
+                      //工作标题,选择模板
+                      FormRequireText(
+                        text: "Job Title".tr,
+                      ).marginOnly(top: 15),
+
+                      //工作标题
+                      Container(
+                        padding: const EdgeInsets.only(left: 16, right: 10),
+                        margin: const EdgeInsets.only(top: 10),
+                        height: 45,
+                        decoration: BoxDecoration(
+                          color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                          borderRadius: const BorderRadius.all(Radius.circular(5)),
+                        ),
+                        child: Row(
+                          mainAxisSize: MainAxisSize.max,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          mainAxisAlignment: MainAxisAlignment.start,
+                          children: [
+                            MyTextView(
+                              state.selectedJobTitleName ?? "",
+                              fontSize: 14,
+                              hint: "Choose Job Title".tr,
+                              textHintColor: ColorConstants.textGrayAECAE5,
+                              isFontMedium: true,
+                              textColor: ColorConstants.white,
+                            ).expanded(),
+
+                            //下拉选图标
+                            Visibility(
+                              visible: state.pageType != 2,
+                              child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                            ),
+                          ],
+                        ),
+                      ).onTap(() {
+                        FocusScope.of(context).unfocus();
+                        if (state.pageType != 2) controller.pickJobTitle();
+                      }),
+
+                      //选择工作时间
+                      FormRequireText(
+                        text: "Job Time".tr,
+                      ).marginOnly(top: 15),
+
+                      Row(
+                        mainAxisSize: MainAxisSize.max,
+                        crossAxisAlignment: CrossAxisAlignment.center,
+                        mainAxisAlignment: MainAxisAlignment.start,
+                        children: [
+                          //选择工作开始时间
+                          Expanded(
+                            child: Container(
+                              padding: const EdgeInsets.only(left: 16, right: 10),
+                              height: 45,
+                              decoration: BoxDecoration(
+                                color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                                borderRadius: const BorderRadius.all(Radius.circular(5)),
+                              ),
+                              child: Row(
+                                mainAxisSize: MainAxisSize.max,
+                                crossAxisAlignment: CrossAxisAlignment.center,
+                                mainAxisAlignment: MainAxisAlignment.start,
+                                children: [
+                                  MyTextView(
+                                    state.selectedStartTime == null ? "" : DateTimeUtils.formatDate(state.selectedStartTime, format: "yyyy-MM-dd HH:mm"),
+                                    fontSize: 14,
+                                    hint: "Job Start Time".tr,
+                                    textHintColor: ColorConstants.textGrayAECAE5,
+                                    isFontMedium: true,
+                                    textColor: ColorConstants.white,
+                                  ).expanded(),
+                                  //下拉选图标
+                                  Visibility(
+                                    visible: state.pageType != 2,
+                                    child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                                  ),
+                                ],
+                              ),
+                            ).onTap(() {
+                              FocusScope.of(context).unfocus();
+                              if (state.pageType != 2) controller.pickStartTime();
+                            }),
+                          ),
+
+                          //选择工作结束时间
+                          Expanded(
+                            child: Container(
+                              padding: const EdgeInsets.only(left: 16, right: 10),
+                              margin: const EdgeInsets.only(left: 10),
+                              height: 45,
+                              decoration: BoxDecoration(
+                                color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                                borderRadius: const BorderRadius.all(Radius.circular(5)),
+                              ),
+                              child: Row(
+                                mainAxisSize: MainAxisSize.max,
+                                crossAxisAlignment: CrossAxisAlignment.center,
+                                mainAxisAlignment: MainAxisAlignment.start,
+                                children: [
+                                  MyTextView(
+                                    state.selectedEndTime == null ? "" : DateTimeUtils.formatDate(state.selectedEndTime, format: "yyyy-MM-dd HH:mm"),
+                                    fontSize: 14,
+                                    hint: "Job End Time".tr,
+                                    textHintColor: ColorConstants.textGrayAECAE5,
+                                    isFontMedium: true,
+                                    textColor: ColorConstants.white,
+                                  ).expanded(),
+                                  //下拉选图标
+                                  Visibility(
+                                    visible: state.pageType != 2,
+                                    child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                                  ),
+                                ],
+                              ),
+                            ).onTap(() {
+                              FocusScope.of(context).unfocus();
+                              if (state.pageType != 2) controller.pickEndTime();
+                            }),
+                          ),
+                        ],
+                      ).marginOnly(top: 10),
+
+                      //选择重复时间
+                      Visibility(
+                        visible: state.pageType == 0,
+                        child: MyTextView("Repeat".tr, fontSize: 15, textColor: Colors.white, isFontRegular: true, marginTop: 15),
+                      ),
+
+                      Visibility(
+                        visible: state.pageType == 0,
+                        child: Row(
+                          mainAxisSize: MainAxisSize.max,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          mainAxisAlignment: MainAxisAlignment.start,
+                          children: [
+                            //开始时间
+                            Expanded(
+                              child: Container(
+                                padding: const EdgeInsets.only(left: 16, right: 10),
+                                height: 45,
+                                decoration: BoxDecoration(
+                                  color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                                  borderRadius: const BorderRadius.all(Radius.circular(5)),
+                                ),
+                                child: Row(
+                                  mainAxisSize: MainAxisSize.max,
+                                  crossAxisAlignment: CrossAxisAlignment.center,
+                                  mainAxisAlignment: MainAxisAlignment.start,
+                                  children: [
+                                    MyTextView(
+                                      state.selectedRepeatStartTime == null
+                                          ? ""
+                                          : DateTimeUtils.formatDate(state.selectedRepeatStartTime, format: "yyyy-MM-dd"),
+                                      fontSize: 14,
+                                      hint: "Repeat Start Time".tr,
+                                      textHintColor: ColorConstants.textGrayAECAE5,
+                                      isFontMedium: true,
+                                      textColor: ColorConstants.white,
+                                    ).expanded(),
+                                    //下拉选图标
+                                    Visibility(
+                                      visible: state.pageType != 2,
+                                      child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                                    ),
+                                  ],
+                                ),
+                              ).onTap(() {
+                                FocusScope.of(context).unfocus();
+                                if (state.pageType != 2) controller.pickRepeatStartTime();
+                              }),
+                            ),
+
+                            //结束时间
+                            Expanded(
+                              child: Container(
+                                padding: const EdgeInsets.only(left: 16, right: 10),
+                                margin: const EdgeInsets.only(left: 10),
+                                height: 45,
+                                decoration: BoxDecoration(
+                                  color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                                  borderRadius: const BorderRadius.all(Radius.circular(5)),
+                                ),
+                                child: Row(
+                                  mainAxisSize: MainAxisSize.max,
+                                  crossAxisAlignment: CrossAxisAlignment.center,
+                                  mainAxisAlignment: MainAxisAlignment.start,
+                                  children: [
+                                    MyTextView(
+                                      state.selectedRepeatEndTime == null ? "" : DateTimeUtils.formatDate(state.selectedRepeatEndTime, format: "yyyy-MM-dd"),
+                                      fontSize: 14,
+                                      hint: "Repeat End Time".tr,
+                                      textHintColor: ColorConstants.textGrayAECAE5,
+                                      isFontMedium: true,
+                                      textColor: ColorConstants.white,
+                                    ).expanded(),
+                                    //下拉选图标
+                                    Visibility(
+                                      visible: state.pageType != 2,
+                                      child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                                    ),
+                                  ],
+                                ),
+                              ).onTap(() {
+                                FocusScope.of(context).unfocus();
+                                if (state.pageType != 2) controller.pickRepeatEndTime();
+                              }),
+                            ),
+                          ],
+                        ).marginOnly(top: 10),
+                      ),
+
+                      //工作选择部门
+                      FormRequireText(
+                        text: "Outlet".tr,
+                      ).marginOnly(top: 15),
+
+                      //选择部门
+                      Container(
+                        padding: const EdgeInsets.only(left: 16, right: 10),
+                        margin: const EdgeInsets.only(top: 10),
+                        height: 45,
+                        decoration: BoxDecoration(
+                          color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                          borderRadius: const BorderRadius.all(Radius.circular(5)),
+                        ),
+                        child: Row(
+                          mainAxisSize: MainAxisSize.max,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          mainAxisAlignment: MainAxisAlignment.start,
+                          children: [
+                            MyTextView(
+                              state.selectedOutlet ?? "",
+                              fontSize: 14,
+                              hint: "Choose Outlet".tr,
+                              textHintColor: ColorConstants.textGrayAECAE5,
+                              isFontMedium: true,
+                              textColor: ColorConstants.white,
+                            ).expanded(),
+                            Visibility(
+                              visible: state.pageType != 2,
+                              child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                            ),
+                          ],
+                        ),
+                      ).onTap(() {
+                        FocusScope.of(context).unfocus();
+                        if (state.pageType != 2) controller.pickOutlet();
+                      }),
+
+                      //需求人数
+                      FormRequireText(
+                        text: "No. of Staff".tr,
+                      ).marginOnly(top: 15, bottom: 10),
+
+                      Row(
+                        children: [
+                          Container(
+                            padding: const EdgeInsets.only(left: 16, right: 10),
+                            margin: const EdgeInsets.only(right: 12),
+                            height: 45,
+                            decoration: BoxDecoration(
+                              color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                              borderRadius: const BorderRadius.all(Radius.circular(5)),
+                            ),
+                            child: Row(
+                              mainAxisSize: MainAxisSize.max,
+                              crossAxisAlignment: CrossAxisAlignment.center,
+                              mainAxisAlignment: MainAxisAlignment.start,
+                              children: [
+                                MyTextView(
+                                  state.genderOptions[state.genderOptionType],
+                                  fontSize: 14,
+                                  textHintColor: ColorConstants.textGrayAECAE5,
+                                  isFontRegular: true,
+                                  textColor: ColorConstants.white,
+                                ).expanded(),
+                                //下拉选图标
+                                Visibility(
+                                  visible: state.pageType != 2,
+                                  child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                                ),
+                              ],
+                            ),
+                          ).onTap(() {
+                            FocusScope.of(context).unfocus();
+                            if (state.pageType != 2) controller.pickLimitType();
+                          }).expanded(flex: 55),
+
+                          //输入框-不限制性别
+                          Visibility(
+                            visible: state.genderOptionType == 0,
+                            child: CustomTextField(
+                              formKey: "need_no",
+                              marginLeft: 0,
+                              marginRight: 0,
+                              paddingTop: 0,
+                              paddingBottom: 0,
+                              height: 45,
+                              enabled: state.pageType != 2,
+                              fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                              inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
+                              textInputType: TextInputType.number,
+                              formData: state.formData,
+                              textInputAction: TextInputAction.done,
+                              onSubmit: (key, value) {
+                                FocusScope.of(context).unfocus();
+                              },
+                            ).expanded(flex: 55),
+                          ),
+
+                          //输入框组-限制性别
+                          Visibility(
+                            visible: state.genderOptionType != 0,
+                            child: Row(
+                              children: [
+                                Row(
+                                  children: [
+                                    MyTextView(
+                                      "M",
+                                      fontSize: 15,
+                                      paddingLeft: 10,
+                                      isFontRegular: true,
+                                      textColor: ColorConstants.white,
+                                    ),
+                                    CustomTextField(
+                                      formKey: "need_male",
+                                      marginLeft: 0,
+                                      marginRight: 0,
+                                      paddingTop: 0,
+                                      paddingBottom: 0,
+                                      paddingLeft: 10,
+                                      paddingRight: 10,
+                                      height: 45,
+                                      enabled: state.pageType != 2,
+                                      cornerRadius: 0,
+                                      fillBackgroundColor: Colors.transparent,
+                                      inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
+                                      textInputType: TextInputType.number,
+                                      formData: state.formData,
+                                      textInputAction: TextInputAction.done,
+                                      onSubmit: (key, value) {
+                                        FocusScope.of(context).unfocus();
+                                      },
+                                    ).expanded(),
+                                  ],
+                                )
+                                    .decorated(
+                                      color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                                      borderRadius: const BorderRadius.all(Radius.circular(5)),
+                                    )
+                                    .expanded(),
+                                const SizedBox(width: 12),
+                                Row(
+                                  children: [
+                                    MyTextView(
+                                      "F",
+                                      fontSize: 15,
+                                      paddingLeft: 10,
+                                      isFontRegular: true,
+                                      textColor: ColorConstants.white,
+                                    ),
+                                    CustomTextField(
+                                      formKey: "need_female",
+                                      marginLeft: 0,
+                                      marginRight: 0,
+                                      paddingTop: 0,
+                                      paddingBottom: 0,
+                                      paddingLeft: 10,
+                                      paddingRight: 10,
+                                      height: 45,
+                                      enabled: state.pageType != 2,
+                                      cornerRadius: 0,
+                                      fillBackgroundColor: Colors.transparent,
+                                      inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
+                                      textInputType: TextInputType.number,
+                                      formData: state.formData,
+                                      textInputAction: TextInputAction.done,
+                                      onSubmit: (key, value) {
+                                        FocusScope.of(context).unfocus();
+                                      },
+                                    ).expanded(),
+                                  ],
+                                )
+                                    .decorated(
+                                      color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                                      borderRadius: const BorderRadius.all(Radius.circular(5)),
+                                    )
+                                    .expanded(),
+                              ],
+                            ).expanded(flex: 55),
+                          ),
+                        ],
+                      ),
+
+                      //输入Remark
+                      MyTextView(
+                        "Remark".tr,
+                        fontSize: 15,
+                        isFontRegular: true,
+                        textColor: Colors.white,
+                        marginTop: 15,
+                      ),
+
+                      IgnoreKeyboardDismiss(
+                        child: Container(
+                          height: 160,
+                          margin: const EdgeInsets.only(top: 10),
+                          padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 15),
+                          decoration: BoxDecoration(
+                            color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                            borderRadius: const BorderRadius.all(Radius.circular(5)),
+                          ),
+                          child: TextField(
+                            cursorColor: ColorConstants.white,
+                            cursorWidth: 1.5,
+                            autofocus: false,
+                            enabled: state.pageType == 0 || state.pageType == 1,
+                            focusNode: state.formData["remark"]!['focusNode'],
+                            controller: state.formData["remark"]!['controller'],
+                            // 装饰
+                            decoration: InputDecoration(
+                              isDense: true,
+                              isCollapsed: true,
+                              border: InputBorder.none,
+                              hintText: state.formData["remark"]!['hintText'],
+                              hintStyle: const TextStyle(
+                                color: ColorConstants.textGrayAECAE5,
+                                fontSize: 15.0,
+                                fontWeight: FontWeight.w400,
+                              ),
+                            ),
+                            style: const TextStyle(
+                              color: ColorConstants.white,
+                              fontSize: 15.0,
+                              fontWeight: FontWeight.w400,
+                            ),
+                            // 键盘动作右下角图标
+                            textInputAction: TextInputAction.done,
+                            onSubmitted: (value) {
+                              FocusScope.of(context).unfocus();
+                            },
+                          ),
+                        ),
+                      ),
+
+                      //OA的其他显示
+
+                      //Type
+                      MyTextView(
+                        "Type".tr,
+                        fontSize: 15,
+                        isFontRegular: true,
+                        textColor: Colors.white,
+                        marginTop: 15,
+                      ),
+
+                      //选择类型
+                      Container(
+                        padding: const EdgeInsets.only(left: 16, right: 10),
+                        margin: const EdgeInsets.only(top: 10),
+                        height: 45,
+                        decoration: BoxDecoration(
+                          color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                          borderRadius: const BorderRadius.all(Radius.circular(5)),
+                        ),
+                        child: Row(
+                          mainAxisSize: MainAxisSize.max,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          mainAxisAlignment: MainAxisAlignment.start,
+                          children: [
+                            MyTextView(
+                              state.selectedType ?? "",
+                              fontSize: 14,
+                              hint: "Choose Type".tr,
+                              textHintColor: ColorConstants.textGrayAECAE5,
+                              isFontMedium: true,
+                              textColor: ColorConstants.white,
+                            ).expanded(),
+
+                            //下拉选图标
+                            Visibility(
+                              visible: state.pageType != 2,
+                              child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                            ),
+                          ],
+                        ),
+                      ).onTap(() {
+                        FocusScope.of(context).unfocus();
+                        if (state.pageType != 2) controller.pickType();
+                      }),
+
+                      //Event
+                      MyTextView(
+                        "I.Event".tr,
+                        fontSize: 15,
+                        isFontRegular: true,
+                        textColor: Colors.white,
+                        marginTop: 15,
+                      ),
+
+                      CustomTextField(
+                        formKey: "event",
+                        marginLeft: 0,
+                        marginRight: 0,
+                        paddingTop: 0,
+                        paddingBottom: 0,
+                        marginTop: 10,
+                        height: 45,
+                        fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                        enabled: state.pageType != 2,
+                        textInputType: TextInputType.text,
+                        formData: state.formData,
+                        textInputAction: TextInputAction.next,
+                        onSubmit: (key, value) {
+                          state.formData[key]!['focusNode'].unfocus();
+                          FocusScope.of(context).requestFocus(state.formData['event_type']!['focusNode']);
+                        },
+                      ),
+
+                      //Event Type
+                      MyTextView(
+                        "II.Event Type".tr,
+                        fontSize: 15,
+                        isFontRegular: true,
+                        textColor: Colors.white,
+                        marginTop: 15,
+                      ),
+
+                      CustomTextField(
+                        formKey: "event_type",
+                        marginLeft: 0,
+                        marginRight: 0,
+                        paddingTop: 0,
+                        paddingBottom: 0,
+                        marginTop: 10,
+                        height: 45,
+                        fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                        enabled: state.pageType != 2,
+                        textInputType: TextInputType.text,
+                        formData: state.formData,
+                        textInputAction: TextInputAction.next,
+                        onSubmit: (key, value) {
+                          state.formData[key]!['focusNode'].unfocus();
+                          FocusScope.of(context).requestFocus(state.formData['event_pax']!['focusNode']);
+                        },
+                      ),
+
+                      //Event Pax
+                      MyTextView(
+                        "III.Event Pax".tr,
+                        fontSize: 15,
+                        isFontRegular: true,
+                        textColor: Colors.white,
+                        marginTop: 15,
+                      ),
+
+                      CustomTextField(
+                        formKey: "event_pax",
+                        marginLeft: 0,
+                        marginRight: 0,
+                        paddingTop: 0,
+                        paddingBottom: 0,
+                        marginTop: 10,
+                        height: 45,
+                        fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                        enabled: state.pageType != 2,
+                        textInputType: TextInputType.number,
+                        formData: state.formData,
+                        textInputAction: TextInputAction.next,
+                        onSubmit: (key, value) {
+                          state.formData[key]!['focusNode'].unfocus();
+                          FocusScope.of(context).requestFocus(state.formData['revenue']!['focusNode']);
+                        },
+                      ),
+
+                      //Revenue
+                      MyTextView(
+                        "IV.Estimated Revenue".tr,
+                        fontSize: 15,
+                        isFontRegular: true,
+                        textColor: Colors.white,
+                        marginTop: 15,
+                      ),
+
+                      CustomTextField(
+                        formKey: "revenue",
+                        marginLeft: 0,
+                        marginRight: 0,
+                        paddingTop: 0,
+                        paddingBottom: 0,
+                        marginTop: 10,
+                        height: 45,
+                        fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                        enabled: state.pageType != 2,
+                        textInputType: TextInputType.number,
+                        formData: state.formData,
+                        textInputAction: TextInputAction.next,
+                        onSubmit: (key, value) {
+                          state.formData[key]!['focusNode'].unfocus();
+                          FocusScope.of(context).requestFocus(state.formData['position']!['focusNode']);
+                        },
+                      ),
+
+                      //Position
+                      MyTextView(
+                        "V.Position".tr,
+                        fontSize: 15,
+                        isFontRegular: true,
+                        textColor: Colors.white,
+                        marginTop: 15,
+                      ),
+
+                      CustomTextField(
+                        formKey: "position",
+                        marginLeft: 0,
+                        marginRight: 0,
+                        paddingTop: 0,
+                        paddingBottom: 0,
+                        marginTop: 10,
+                        height: 45,
+                        fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                        enabled: state.pageType != 2,
+                        textInputType: TextInputType.text,
+                        formData: state.formData,
+                        textInputAction: TextInputAction.next,
+                        onSubmit: (key, value) {
+                          state.formData[key]!['focusNode'].unfocus();
+                          FocusScope.of(context).requestFocus(state.formData['total_cost']!['focusNode']);
+                        },
+                      ),
+
+                      //total_cost
+                      MyTextView(
+                        "VI.Estimated Total Cost".tr,
+                        fontSize: 15,
+                        isFontRegular: true,
+                        textColor: Colors.white,
+                        marginTop: 15,
+                      ),
+
+                      CustomTextField(
+                        formKey: "total_cost",
+                        marginLeft: 0,
+                        marginRight: 0,
+                        paddingTop: 0,
+                        paddingBottom: 0,
+                        marginTop: 10,
+                        height: 45,
+                        fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                        enabled: state.pageType != 2,
+                        textInputType: TextInputType.number,
+                        formData: state.formData,
+                        textInputAction: TextInputAction.done,
+                        onSubmit: (key, value) {
+                          FocusScope.of(context).unfocus();
+                        },
+                      ),
+
+                      //附件文件
+                      Visibility(
+                        visible: state.pageType == 0,
+                        child: MyTextView(
+                          "Attachment".tr,
+                          fontSize: 15,
+                          isFontRegular: true,
+                          textColor: Colors.white,
+                          marginTop: 15,
+                        ),
+                      ),
+
+                      //选择类型
+                      Visibility(
+                          visible: state.pageType == 0,
+                          child: Container(
+                            padding: const EdgeInsets.only(left: 16),
+                            margin: const EdgeInsets.only(top: 10),
+                            height: 45,
+                            decoration: BoxDecoration(
+                              color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                              borderRadius: const BorderRadius.all(Radius.circular(5)),
+                            ),
+                            child: Row(
+                              mainAxisSize: MainAxisSize.max,
+                              crossAxisAlignment: CrossAxisAlignment.center,
+                              mainAxisAlignment: MainAxisAlignment.start,
+                              children: [
+                                MyTextView(
+                                  Utils.isNotEmpty(state.attFilePath) ? Uri.parse(state.attFilePath!).pathSegments.last : "",
+                                  fontSize: 14,
+                                  hint: "Choose Attachment".tr,
+                                  textHintColor: ColorConstants.textGrayAECAE5,
+                                  isFontMedium: true,
+                                  textColor: ColorConstants.white,
+                                ).expanded(),
+
+                                //下拉选图标
+                                MyTextView(
+                                  'Upload'.tr,
+                                  boxHeight: 45,
+                                  textAlign: TextAlign.center,
+                                  boxWidth: 90,
+                                  cornerRadius: 5,
+                                  onClick: controller.pickAttFile,
+                                  textColor: Colors.white,
+                                  fontSize: 15,
+                                  fontWeight: FontWeight.w400,
+                                  backgroundColor: ColorConstants.textGreen0AC074,
+                                ),
+                              ],
+                            ),
+                          )),
+
+                      const SizedBox(height: 30),
+
+                      //提交按钮
+                      Visibility(
+                        visible: state.pageType == 1 || state.pageType == 0,
+                        child: MyButton(
+                          type: ClickType.throttle,
+                          milliseconds: 500,
+                          onPressed: () {
+                            FocusScope.of(context).unfocus();
+                            controller.doEditSubmit();
+                          },
+                          text: "Submit".tr,
+                          textColor: ColorConstants.white,
+                          fontSize: 16,
+                          radius: 22.5,
+                          backgroundColor: hexToColor("#FFBB1B"),
+                          fontWeight: FontWeight.w500,
+                          minHeight: 45,
+                        ).marginOnly(bottom: 30),
+                      ),
+                    ],
+                  ).paddingOnly(left: 15, right: 15),
+                ),
+              ),
+            ),
+          ),
+        ),
+      );
+    });
+  }
+}

+ 108 - 0
packages/cpt_th/lib/modules/labour/labour_request_add/labour_request_add_state.dart

@@ -0,0 +1,108 @@
+import 'package:domain/entity/response/s_g_labour_request_detail_entity.dart';
+import 'package:domain/entity/response/t_h_o_a_labour_detail_entity.dart';
+import 'package:flutter/material.dart';
+import 'package:plugin_basic/basic_export.dart';
+
+class LabourRequestAddState {
+
+  //表单的校验与数据
+  Map<String, Map<String, dynamic>> formData = {
+    'remark': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter...'.tr,
+      'obsecure': false,
+    },
+    'need_male': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Male'.tr,
+      'obsecure': false,
+    },
+    'need_female': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Female'.tr,
+      'obsecure': false,
+    },
+    'need_no': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Needs Num'.tr,
+      'obsecure': false,
+    },
+    'event': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter...'.tr,
+      'obsecure': false,
+    },
+    'event_type': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter...'.tr,
+      'obsecure': false,
+    },
+    'event_pax': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter...'.tr,
+      'obsecure': false,
+    },
+    'revenue': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter...'.tr,
+      'obsecure': false,
+    },
+    'position': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter...'.tr,
+      'obsecure': false,
+    },
+    'total_cost': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter...'.tr,
+      'obsecure': false,
+    },
+  };
+
+  int pageType = 0; // 0 是新增  1是编辑  2是详情
+  String? requestId;  //编辑和详情需要用到ID
+  bool isReviewEdit = false;  //编辑状态下判断是否是Review中跳转过来的
+  void Function(dynamic value)? cb;
+
+  List<String> genderOptions = ["Gender Unlimited".tr, "Gender Limited".tr];
+  int genderOptionType = 0;  //使用哪一种类型限制
+
+  THOALabourDetailEntity? editDetailEntity;
+
+  DateTime? selectedStartTime;
+  DateTime? selectedEndTime;
+
+  DateTime? selectedRepeatStartTime;
+  DateTime? selectedRepeatEndTime;
+
+  String? selectedOutletId;
+  String? selectedOutlet;
+
+  String? selectedJobTitleId;
+  String? selectedJobTitleName;
+
+  String? selectedTypeId;
+  String? selectedType;
+
+  String? attFilePath;
+}

+ 190 - 0
packages/cpt_th/lib/modules/labour/labour_request_list/dialog/oa_attach_list_controller.dart

@@ -0,0 +1,190 @@
+import 'package:domain/entity/response/t_h_o_a_attachment_entity.dart';
+import 'package:domain/repository/th_oa_repository.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:plugin_platform/engine/dialog/dialog_engine.dart';
+import 'package:plugin_platform/engine/notify/notify_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/dialog/app_default_dialog.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+import 'package:file_picker/file_picker.dart';
+
+class OaAttachListController extends GetxController with DioCancelableMixin {
+  final THOARepository _thRepository = Get.find();
+
+  String? requestId;
+  List<THOAAttachmentRows> datas = [];
+  var _curPage = 1;
+  var _needShowPlaceholder = true;
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Success;
+  String? errorMessage;
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,
+    controlFinishLoad: true,
+  );
+
+  // Refresh 刷新事件
+  Future onRefresh() async {
+    _curPage = 1;
+    fetchAllStaffList();
+  }
+
+  // Refresh 加载事件
+  Future loadMore() async {
+    _curPage++;
+    fetchAllStaffList();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _curPage = 1;
+    _needShowPlaceholder = true;
+    fetchAllStaffList();
+  }
+
+  /// 获取列表数据
+  Future fetchAllStaffList() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    }
+
+    // 获取 Staff 列表
+    var listResult = await _thRepository.fetchAttachmentTable(
+      requestId: requestId,
+      curPage: _curPage,
+      cancelToken: cancelToken,
+    );
+
+    // 处理数据
+    if (listResult.isSuccess) {
+      handleList(listResult.data?.rows);
+    } else {
+      errorMessage = listResult.errorMsg;
+      changeLoadingState(LoadState.State_Error);
+    }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void handleList(List<THOAAttachmentRows>? list) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      if (_curPage == 1) {
+        //刷新的方式
+        datas.clear();
+        datas.addAll(list);
+        refreshController.finishRefresh();
+
+        //更新展示的状态
+        changeLoadingState(LoadState.State_Success);
+      } else {
+        //加载更多
+        datas.addAll(list);
+        refreshController.finishLoad();
+        update();
+      }
+    } else {
+      if (_curPage == 1) {
+        //展示无数据的布局
+        datas.clear();
+        changeLoadingState(LoadState.State_Empty);
+        refreshController.finishRefresh();
+      } else {
+        //展示加载完成,没有更多数据了
+        refreshController.finishLoad(IndicatorResult.noMore);
+      }
+    }
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    fetchAllStaffList();
+  }
+
+  @override
+  void onClose() {
+    datas.clear();
+    super.onClose();
+  }
+
+  void pickAttFile() async {
+    FilePickerResult? result = await FilePicker.platform.pickFiles(
+      allowMultiple: false,
+      type: FileType.custom,
+      allowedExtensions: ['jpg', 'png', 'jpeg', 'pdf', 'docx'],
+    );
+
+    if (result != null) {
+      PlatformFile file = result.files.first;
+      Log.d("path:${file.path} name:${file.name}");
+
+      final fileResult = await _thRepository.uploadFile(file.path);
+
+      if (fileResult.isSuccess) {
+        String? filePath = fileResult.data?.path;
+
+        _requestAddAttachment(filePath);
+      } else {
+        ToastEngine.show(fileResult.errorMsg ?? "Network Load Error".tr);
+      }
+    }
+  }
+
+  //调用接口添加附件,成功之后刷新列表
+  void _requestAddAttachment(String? filePath) async {
+    final result = await _thRepository.addAttachmentSubmit(requestId: requestId, attUrl: filePath);
+
+    if (result.isSuccess) {
+      refreshController.callRefresh();
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+  }
+
+  //执行删除操作
+  void doDeleteAction(String attachmentId, int index) {
+    DialogEngine.show(
+        widget: AppDefaultDialog(
+          title: "Confirmation".tr,
+          message: "Are you sure you want to delete this attachment?".tr,
+          confirmAction: () {
+            _requestDeactivate(attachmentId, index);
+          },
+        ));
+  }
+
+  // 请求接口删除Job
+  void _requestDeactivate(String attachmentId, int index) async {
+    var result = await _thRepository.deleteAttachmentSubmit(attId: attachmentId, cancelToken: cancelToken);
+
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess("Successful".tr);
+
+      if (datas.length <= 1) {
+        refreshController.callRefresh();
+      } else {
+        datas.removeAt(index);
+        update();
+      }
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+  }
+
+
+}

+ 266 - 0
packages/cpt_th/lib/modules/labour/labour_request_list/dialog/oa_attach_list_dialog.dart

@@ -0,0 +1,266 @@
+import 'dart:ui';
+
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/response/t_h_o_a_attachment_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'oa_attach_list_controller.dart';
+
+/*
+ * OA 的附件列表弹窗
+ */
+class OaAttachListDialog extends StatefulWidget {
+  String requestId;
+  String jobTitle;
+  String jobDate;
+
+  OaAttachListDialog({required this.requestId, required this.jobTitle, required this.jobDate});
+
+  @override
+  State<OaAttachListDialog> createState() => _AppliedAddStaffState();
+}
+
+class _AppliedAddStaffState extends State<OaAttachListDialog> {
+  @override
+  void initState() {
+    super.initState();
+    OaAttachListController controller = Get.put(OaAttachListController());
+    controller.requestId = widget.requestId;
+  }
+
+  @override
+  void dispose() {
+    super.dispose();
+    Get.delete<OaAttachListController>();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return GetBuilder<OaAttachListController>(
+      assignId: true,
+      builder: (controller) {
+        return Container(
+          width: 300,
+          height: 555,
+          decoration: const BoxDecoration(
+            color: Color(0XFFF7F7F7),
+            borderRadius: BorderRadius.all(Radius.circular(15)),
+          ),
+          child: Column(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              Stack(
+                children: [
+                  Center(
+                    child: MyTextView(
+                      "Attachment".tr,
+                      fontSize: 19,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black,
+                      marginTop: 22,
+                      marginLeft: 22,
+                      marginRight: 22,
+                    ),
+                  ),
+                  const MyAssetImage(
+                    Assets.baseServiceRedDeleteIcon,
+                    width: 20,
+                    height: 20,
+                  ).marginOnly(top: 15, right: 15).onTap(() {
+                    onCancel();
+                  }).alignRight(),
+                ],
+              ),
+              MyButton(
+                type: ClickType.throttle,
+                milliseconds: 500,
+                onPressed: () {
+                  controller.pickAttFile();
+                },
+                text: "Add File".tr,
+                minHeight: 35,
+                textColor: ColorConstants.white,
+                fontSize: 12,
+                radius: 20,
+                backgroundColor: hexToColor("#FFBB1B"),
+                fontWeight: FontWeight.w500,
+              ).marginOnly(left: 15, right: 15, top: 5),
+
+              //工作信息
+              MyTextView(
+                "${widget.jobTitle} ${widget.jobDate}",
+                isFontRegular: true,
+                fontSize: 14,
+                textColor: ColorConstants.black,
+                marginLeft: 10,
+                marginRight: 10,
+                textAlign: TextAlign.center,
+                marginBottom: 10,
+              ),
+
+              //列表容器
+              Container(
+                decoration: const BoxDecoration(
+                  color: Colors.white,
+                  borderRadius: BorderRadius.only(bottomLeft: Radius.circular(15), bottomRight: Radius.circular(15)),
+                ),
+                child: EasyRefresh(
+                  header: ClassicHeader(
+                    dragText: 'Pull to refresh'.tr,
+                    armedText: 'Release ready'.tr,
+                    readyText: 'Refreshing...'.tr,
+                    processingText: 'Refreshing...'.tr,
+                    processedText: 'Succeeded'.tr,
+                    noMoreText: 'No more'.tr,
+                    failedText: 'Failed'.tr,
+                    messageText: 'Last updated at %T'.tr,
+                    textStyle: const TextStyle(color: ColorConstants.black66, fontSize: 14),
+                    messageStyle: const TextStyle(color: ColorConstants.black66, fontSize: 12),
+                    iconTheme: const IconThemeData(color: ColorConstants.black66),
+                    backgroundColor: Colors.transparent,
+                  ),
+                  footer: ClassicFooter(
+                    dragText: 'Pull to load'.tr,
+                    armedText: 'Release ready'.tr,
+                    readyText: 'Loading...'.tr,
+                    processingText: 'Loading...'.tr,
+                    processedText: 'Succeeded'.tr,
+                    noMoreText: 'No more'.tr,
+                    failedText: 'Failed'.tr,
+                    showMessage: false,
+                    triggerOffset: 50,
+                    iconDimension: 22,
+                    textStyle: const TextStyle(color: ColorConstants.black66, fontSize: 14),
+                    messageStyle: const TextStyle(color: ColorConstants.black66, fontSize: 12),
+                    iconTheme: const IconThemeData(color: ColorConstants.black66),
+                    backgroundColor: Colors.transparent,
+                  ),
+                  controller: controller.refreshController,
+                  onRefresh: controller.onRefresh,
+                  onLoad: controller.loadMore,
+                  child: LoadStateLayout(
+                    themeColor: ColorConstants.black66,
+                    state: controller.loadingState,
+                    errorMessage: controller.errorMessage,
+                    errorRetry: () {
+                      controller.retryRequest();
+                    },
+                    successSliverWidget: [
+                      SliverList(
+                          delegate: SliverChildBuilderDelegate(
+                                (context, index) {
+                              return _buildStaffItem(controller.datas[index], () {
+                                //删除
+                                controller.doDeleteAction(controller.datas[index].id??"", index);
+                              });
+                            },
+                            childCount: controller.datas.length,
+                          ))
+                    ],
+                  ),
+                ),
+              ).expanded(),
+            ],
+          ),
+        );
+      },
+    );
+  }
+
+  //取消弹框
+  void onCancel() async {
+    SmartDialog.dismiss();
+  }
+
+  Widget _buildStaffItem(THOAAttachmentRows item, VoidCallback deleteAction) {
+    return Column(
+      children: [
+        //姓名
+        Row(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            MyTextView(
+              "${"Url".tr}:",
+              textColor: ColorConstants.black66,
+              fontSize: 13,
+              marginRight: 3,
+              isFontRegular: true,
+            ),
+            MyTextView(
+              item.attUrl ?? "-",
+              textColor: ColorConstants.string2Color("#06A962"),
+              fontSize: 12,
+              onClick: () {
+                _openFile(item.attUrl??"");
+              },
+              marginLeft: 10,
+              isFontRegular: true,
+            ).expanded(),
+            MyButton(
+              onPressed: () {
+                deleteAction.call();
+              },
+              text: "Delete".tr,
+              textColor: ColorConstants.white,
+              backgroundColor: ColorConstants.redFF4066,
+              radius: 17.25,
+              fontSize: 12,
+              minWidth: 48,
+              minHeight: 30,
+            ).marginOnly(left: 10)
+          ],
+        ).marginOnly(top: 15),
+
+        Row(
+          children: [
+            MyTextView(
+              "Created At:".tr,
+              textColor: ColorConstants.black66,
+              fontSize: 13,
+              marginRight: 3,
+              isFontRegular: true,
+            ),
+            MyTextView(
+              item.createdAt ?? "-",
+              textColor: ColorConstants.black,
+              fontSize: 12,
+              isFontRegular: true,
+            ),
+          ],
+        ).marginOnly(top: 10),
+
+        Container(
+          margin: const EdgeInsets.only(top: 16),
+          width: double.infinity,
+          height: 1,
+          color: const Color(0XFFF7F7F7),
+        )
+      ],
+    ).paddingOnly(left: 19, right: 11);
+  }
+
+  ///尝试预览文件
+  void _openFile(String attUrl) async {
+    // 创建一个 Uri 对象
+    final Uri url = Uri.parse(attUrl);
+    if (await canLaunchUrl(url)) {
+      await launchUrl(url);
+    } else {
+      ToastEngine.show("无法打开链接");
+    }
+  }
+}

+ 428 - 0
packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_filter.dart

@@ -0,0 +1,428 @@
+import 'dart:typed_data';
+import 'dart:ui';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/response/labour_request_index_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/picker/date_picker_util.dart';
+import 'package:widgets/picker/option_pick_util.dart';
+import 'package:widgets/widget_export.dart';
+
+/*
+ * 用工请求列表的筛选
+ */
+class LabourRequestFilter extends StatefulWidget {
+  void Function(DateTime? selectedStartDate, DateTime? selectedEndDate, String? selectedStatusId, String? selectedDepartmentId, String? selectedDivisionId)? onFilterAction;
+  LabourRequestIndexEntity optionResult;
+  DateTime? selectedStartDate;
+  DateTime? selectedEndDate;
+  String? selectedStatusId;
+  String? selectedDepartmentId;
+  String? selectedDivisionId;
+
+  LabourRequestFilter({
+    required this.optionResult,
+    required this.selectedStartDate,
+    required this.selectedEndDate,
+    required this.selectedStatusId,
+    required this.selectedDepartmentId,
+    required this.selectedDivisionId,
+    this.onFilterAction,
+  });
+
+  @override
+  State<LabourRequestFilter> createState() => _LabourRequestFilterState();
+}
+
+class _LabourRequestFilterState extends State<LabourRequestFilter> {
+  DateTime? selectedStartDate;
+  DateTime? selectedEndDate;
+  String? selectedStatusId;
+  String? selectedDepartmentId;
+  String? selectedDivisionId;
+
+  @override
+  void initState() {
+    super.initState();
+    this.selectedStartDate = widget.selectedStartDate;
+    this.selectedEndDate = widget.selectedEndDate;
+    this.selectedStatusId = widget.selectedStatusId;
+    this.selectedDepartmentId = widget.selectedDepartmentId;
+    this.selectedDivisionId = widget.selectedDivisionId;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      mainAxisAlignment: MainAxisAlignment.start,
+      children: [
+        SizedBox(
+          height: kToolbarHeight + ScreenUtil.getStatusBarH(context) + 1,
+        ),
+        Container(
+          padding: const EdgeInsets.only(left: 15, right: 15, top: 17.5, bottom: 20),
+          width: double.infinity,
+          decoration: const BoxDecoration(
+            color: Colors.white,
+          ),
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              //Division
+              MyTextView(
+                "Division".tr,
+                fontSize: 14,
+                isFontMedium: true,
+                textColor: ColorConstants.black33,
+              ),
+
+              //选择部门
+              Container(
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
+                height: 45,
+                decoration: const BoxDecoration(
+                  color: ColorConstants.grayECECEC,
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      selectedDivisionId == null || selectedDivisionId == "0"
+                          ? ""
+                          : widget.optionResult.divisionList.firstWhere((element) => element.value.toString() == selectedDivisionId).txt!,
+                      hint: "Choose Outlet".tr,
+                      textHintColor: ColorConstants.textBlackHint,
+                      fontSize: 14,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black33,
+                    ).expanded(),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                  ],
+                ),
+              ).onTap(() {
+                pickerDivision();
+              }),
+
+              //部门
+              MyTextView(
+                "Outlet".tr,
+                fontSize: 14,
+                isFontMedium: true,
+                marginTop: 11,
+                textColor: ColorConstants.black33,
+              ),
+
+              //选择部门
+              Container(
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
+                height: 45,
+                decoration: const BoxDecoration(
+                  color: ColorConstants.grayECECEC,
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      selectedDepartmentId == null || selectedDepartmentId == "0"
+                          ? ""
+                          : widget.optionResult.outletList!.firstWhere((element) => element.value.toString() == selectedDepartmentId).txt!,
+                      hint: "Choose Outlet".tr,
+                      textHintColor: ColorConstants.textBlackHint,
+                      fontSize: 14,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black33,
+                    ).expanded(),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                  ],
+                ),
+              ).onTap(() {
+                pickerOutlet();
+              }),
+
+              //状态
+              MyTextView(
+                "Status".tr,
+                fontSize: 14,
+                isFontMedium: true,
+                marginTop: 11,
+                textColor: ColorConstants.black33,
+              ),
+
+              //选择状态
+              Container(
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
+                height: 45,
+                decoration: const BoxDecoration(
+                  color: ColorConstants.grayECECEC,
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      selectedStatusId == null || selectedStatusId == "0"
+                          ? ""
+                          : widget.optionResult.statusList!.firstWhere((element) => element.value.toString() == selectedStatusId).txt!,
+                      fontSize: 14,
+                      hint: "Choose Status".tr,
+                      textHintColor: ColorConstants.textBlackHint,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black33,
+                    ).expanded(),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                  ],
+                ),
+              ).onTap(() {
+                pickerStatus();
+              }),
+
+              //开始时间
+              MyTextView(
+                "Start Date".tr,
+                fontSize: 14,
+                isFontMedium: true,
+                marginTop: 11,
+                textColor: ColorConstants.black33,
+              ),
+
+              //选择时间
+              Container(
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
+                height: 45,
+                decoration: const BoxDecoration(
+                  color: ColorConstants.grayECECEC,
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      selectedStartDate == null ? "" : DateTimeUtils.formatDate(selectedStartDate, format: "yyyy-MM-dd"),
+                      fontSize: 14,
+                      hint: "Choose Start Date".tr,
+                      textHintColor: ColorConstants.textBlackHint,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black33,
+                    ).expanded(),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                  ],
+                ),
+              ).onTap(() {
+                pickerStartDate();
+              }),
+
+              //结束日期
+              MyTextView(
+                "End Date".tr,
+                fontSize: 14,
+                marginTop: 11,
+                isFontMedium: true,
+                textColor: ColorConstants.black33,
+              ),
+
+              //选择结束日期
+              Container(
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
+                height: 45,
+                decoration: const BoxDecoration(
+                  color: ColorConstants.grayECECEC,
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      selectedEndDate == null ? "" : DateTimeUtils.formatDate(selectedEndDate, format: "yyyy-MM-dd"),
+                      fontSize: 14,
+                      hint: "Choose End Date".tr,
+                      textHintColor: ColorConstants.textBlackHint,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black33,
+                    ).expanded(),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                  ],
+                ),
+              ).onTap(() {
+                pickerEndDate();
+              }),
+
+              //按钮组
+              Row(
+                children: [
+                  MyButton(
+                    onPressed: () {
+                      //只是Reset当前的弹窗筛选选项
+                      widget.selectedStartDate = null;
+                      widget.selectedEndDate = null;
+                      widget.selectedStatusId = null;
+                      widget.selectedDepartmentId = null;
+
+                      setState(() {
+                        selectedStartDate = null;
+                        selectedEndDate = null;
+                        selectedStatusId = null;
+                        selectedDepartmentId = null;
+                      });
+                    },
+                    text: "Reset".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#FFBB1B"),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 36,
+                  ).expanded(),
+                  const SizedBox(width: 15),
+                  MyButton(
+                    onPressed: () {
+                      onCancel();
+                      widget.onFilterAction?.call(selectedStartDate, selectedEndDate, selectedStatusId, selectedDepartmentId,selectedDivisionId);
+                    },
+                    text: "Filter".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#0AC074"),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 36,
+                  ).expanded(),
+                ],
+              ).marginOnly(top: 20),
+            ],
+          ),
+        ),
+        Center(child: const MyAssetImage(Assets.baseServiceDialogDeleteIcon, width: 26.5, height: 26.5).marginOnly(top: 35)).onTap(() {
+          onCancel();
+        }),
+      ],
+    );
+  }
+
+  //取消弹框
+  void onCancel() async {
+    SmartDialog.dismiss();
+  }
+
+  /// 筛选开始日期
+  void pickerStartDate() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: selectedStartDate,
+      onDateTimeChanged: (date) {
+        setState(() {
+          selectedStartDate = date;
+        });
+      },
+      title: "Start Date".tr,
+    );
+  }
+
+  /// 筛选结束日期
+  void pickerEndDate() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: selectedEndDate ?? selectedStartDate,
+      onDateTimeChanged: (date) {
+        setState(() {
+          selectedEndDate = date;
+        });
+      },
+      title: "End Date".tr,
+    );
+  }
+
+  /// 筛选部门
+  void pickerOutlet() {
+    int selectedDepartmentIndex;
+    if (selectedDepartmentId == null) {
+      selectedDepartmentIndex = 0;
+    } else {
+      selectedDepartmentIndex = widget.optionResult.outletList!.indexWhere((department) => department.value.toString() == selectedDepartmentId);
+    }
+
+    if (selectedDepartmentIndex < 0) {
+      selectedDepartmentIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: widget.optionResult.outletList!.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedDepartmentIndex,
+      onPickerChanged: (_, index) {
+        setState(() {
+          selectedDepartmentId = widget.optionResult.outletList![index].value!.toString();
+        });
+      },
+    );
+  }
+
+  /// 筛选状态
+  void pickerStatus() {
+    int selectedStatusIndex;
+    if (selectedStatusId == null) {
+      selectedStatusIndex = 0;
+    } else {
+      selectedStatusIndex = widget.optionResult.statusList!.indexWhere((department) => department.value.toString() == selectedStatusId);
+    }
+
+    if (selectedStatusIndex < 0) {
+      selectedStatusIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: widget.optionResult.statusList!.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedStatusIndex,
+      onPickerChanged: (_, index) {
+        setState(() {
+          selectedStatusId = widget.optionResult.statusList![index].value!.toString();
+        });
+      },
+    );
+  }
+
+  void pickerDivision() {
+    int selectedDivisionIndex;
+    if (selectedDivisionId == null) {
+      selectedDivisionIndex = 0;
+    } else {
+      selectedDivisionIndex = widget.optionResult.divisionList.indexWhere((entity) => entity.value.toString() == selectedDivisionId);
+    }
+
+    if (selectedDivisionIndex < 0) {
+      selectedDivisionIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: widget.optionResult.divisionList.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedDivisionIndex,
+      onPickerChanged: (_, index) {
+        setState(() {
+          selectedDivisionId = widget.optionResult.divisionList[index].value!.toString();
+        });
+      },
+    );
+  }
+}

+ 364 - 0
packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_item.dart

@@ -0,0 +1,364 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:domain/entity/response/t_h_o_a_labour_table_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:shared/utils/util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+/*
+ * 用工请求的主页面列表Item
+ */
+class LabourRequestItem extends StatelessWidget {
+  final int index;
+  final THOALabourTableRows item;
+  final int totalNum;
+  final VoidCallback? onStatusAction;
+  final VoidCallback? onRecallAction;
+  final VoidCallback? onDetailAction;
+  final VoidCallback? onEditAction;
+  final VoidCallback? onDeleteAction;
+  final VoidCallback? onAttAction;
+
+  LabourRequestItem({
+    required this.index,
+    required this.item,
+    required this.totalNum,
+    this.onStatusAction,
+    this.onRecallAction,
+    this.onDetailAction,
+    this.onEditAction,
+    this.onDeleteAction,
+    this.onAttAction,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      padding: const EdgeInsets.symmetric(vertical: 23, horizontal: 21),
+      margin: const EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 5),
+      decoration: BoxDecoration(
+        color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+        borderRadius: BorderRadius.circular(5), // 设置圆角
+      ),
+      child: Column(
+        mainAxisSize: MainAxisSize.max,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          //工作标题
+          MyTextView(
+            item.jobTitle ?? "-",
+            isFontMedium: true,
+            textColor: ColorConstants.textYellowFFBB1B,
+            fontSize: 14,
+            textDecoration: TextDecoration.underline,
+            decorationColor: ColorConstants.textYellowFFBB1B,
+            // 可选,设置下划线的颜色
+            decorationThickness: 2.0,
+            // 可选,设置下划线的粗细
+            decorationStyle: TextDecorationStyle.solid, // 可选,设置下划线的样式
+          ),
+
+          // 部门
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Outlet:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //部门
+              MyTextView(
+                item.outletName ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 13),
+
+          // 工作日期时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "${"Job Time".tr}:",
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //日期时间
+              MyTextView(
+                "${item.jobDate} ${item.startTime}~${item.endTime}",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 13),
+
+          // 人数
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              RichText(
+                text: TextSpan(
+                  style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w400, color: ColorConstants.textGrayAECAE5),
+                  children: <TextSpan>[
+                    TextSpan(
+                      text: "Manpower Needed".tr,
+                    ),
+                    TextSpan(
+                      text: "($totalNum)",
+                      style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w400, color: ColorConstants.textYellowFFBB1B),
+                    ),
+                    const TextSpan(
+                      text: ":",
+                    ),
+                  ],
+                ),
+              ),
+
+              //人数
+              MyTextView(
+                item.hiringNum.toString(),
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 13),
+
+          // 预选人数
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "${"Pre Selected".tr}:",
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //文本
+              MyTextView(
+                item.preNum.toString(),
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 13),
+
+          // 最后操作人员
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              MyTextView(
+                "${"Latest Operated".tr}:",
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+                marginTop: 8,
+              ),
+
+              //文本
+              Html(
+                data: item.lastOperator ?? "",
+                style: {
+                  "*": Style(
+                    color: Colors.white,
+                    fontSize: FontSize(14),
+                  ),
+                  "small": Style(
+                    color: ColorConstants.textGrayAECAE5,
+                    fontSize: FontSize(12),
+                  ),
+                  // 你可以为其他HTML标签设置样式
+                },
+              ).expanded(),
+            ],
+          ).marginOnly(top: 5),
+
+          // 状态
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Status:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //文本
+              Column(
+                crossAxisAlignment: CrossAxisAlignment.start,
+                children: [
+                  MyTextView(
+                    item.statusShow == null ? "" : item.statusShow!.tr,
+                    marginLeft: 5,
+                    isFontRegular: true,
+                    textColor: "Approved" == item.statusShow
+                        ? ColorConstants.textGreen05DC82
+                        : "Rejected" == item.statusShow
+                            ? ColorConstants.textRedFF6262
+                            : "Recall" == item.statusShow
+                                ? ColorConstants.textYellowFFBB1B
+                                : ColorConstants.textBlue06D9FF,
+                    fontSize: 14,
+                  ),
+                  Visibility(
+                      visible: Utils.isNotEmpty(item.approveBy),
+                      child: MyTextView(
+                        item.approveBy ?? "-",
+                        marginLeft: 5,
+                        isFontRegular: true,
+                        textColor: ColorConstants.textGrayAECAE5,
+                        fontSize: 12,
+                      ))
+                ],
+              ).expanded(),
+            ],
+          ).marginOnly(top: 0),
+
+          //按钮组
+          Visibility(
+            visible: true,
+            child: Row(
+              mainAxisSize: MainAxisSize.max,
+              mainAxisAlignment: MainAxisAlignment.end,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+                //详情按钮
+                Visibility(
+                  visible: item.status != 4,
+                  child: MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      onDetailAction?.call();
+                    },
+                    text: "Detail".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#56AAFF"),
+                    radius: 17.25,
+                    padding: const EdgeInsets.symmetric(horizontal: 10),
+                    minWidth: 50,
+                    minHeight: 35,
+                  ).marginOnly(left: 10),
+                ),
+
+                Visibility(
+                  visible: item.status == 4,
+                  child: MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      onDeleteAction?.call();
+                    },
+                    text: "Delete".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#56AAFF"),
+                    radius: 17.25,
+                    padding: const EdgeInsets.symmetric(horizontal: 10),
+                    minWidth: 50,
+                    minHeight: 35,
+                  ).marginOnly(left: 10),
+                ),
+
+                //Att 附件
+                Visibility(
+                  visible: true,
+                  child: MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      onAttAction?.call();
+                    },
+                    text: "Att(${item.attNum})",
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#CA28E4"),
+                    radius: 17.25,
+                    padding: const EdgeInsets.symmetric(horizontal: 10),
+                    minWidth: 50,
+                    minHeight: 35,
+                  ).marginOnly(left: 10),
+                ),
+
+                //Recall按钮 (Pending)
+                Visibility(
+                  visible: item.status == 1,
+                  child: MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      onRecallAction?.call();
+                    },
+                    text: "Recall".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#FFBB1B"),
+                    radius: 17.25,
+                    padding: const EdgeInsets.symmetric(horizontal: 10),
+                    minWidth: 50,
+                    minHeight: 35,
+                  ).marginOnly(left: 10),
+                ),
+
+                //Edit按钮 (Recall)
+                Visibility(
+                  visible: item.status == 4 || item.status == 3,
+                  child: MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      onEditAction?.call();
+                    },
+                    text: "Edit".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#FFBB1B"),
+                    radius: 17.25,
+                    padding: const EdgeInsets.symmetric(horizontal: 10),
+                    minWidth: 50,
+                    minHeight: 35,
+                  ).marginOnly(left: 10),
+                ),
+
+                //状态工作流按钮
+                Visibility(
+                  visible: true,
+                  child: MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      onStatusAction?.call();
+                    },
+                    text: "Status".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#0AC074"),
+                    radius: 17.25,
+                    padding: const EdgeInsets.symmetric(horizontal: 10),
+                    minWidth: 50,
+                    minHeight: 35,
+                  ).marginOnly(left: 10),
+                ),
+              ],
+            ).marginOnly(top: 18, bottom: 2),
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 346 - 0
packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_list_controller.dart

@@ -0,0 +1,346 @@
+import 'package:domain/entity/response/t_h_o_a_labour_table_entity.dart';
+import 'package:plugin_platform/engine/notify/notify_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+
+import '../labour_request_add/labour_request_add_page.dart';
+import 'package:domain/entity/response/labour_request_index_entity.dart';
+import 'package:domain/repository/th_oa_repository.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/constants/app_constant.dart';
+import 'package:plugin_platform/engine/dialog/dialog_engine.dart';
+
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:plugin_platform/http/http_result.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:shared/utils/event_bus.dart';
+import 'package:shared/utils/util.dart';
+import 'package:widgets/dialog/app_default_dialog.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../labour_request_workflow/labour_request_workflow_page.dart';
+import 'dialog/oa_attach_list_controller.dart';
+import 'dialog/oa_attach_list_dialog.dart';
+import 'labour_request_filter.dart';
+import 'labour_request_list_state.dart';
+
+class LabourRequestListController extends GetxController with DioCancelableMixin {
+  final THOARepository _thRepository = Get.find();
+  final LabourRequestListState state = LabourRequestListState();
+
+  var _curPage = 1;
+  var _needShowPlaceholder = true;
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Success;
+  String? errorMessage;
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,
+    controlFinishLoad: true,
+  );
+
+  // Refresh 刷新事件
+  Future onRefresh() async {
+    state.totalNum = 0;
+    _curPage = 1;
+    fetchLabourRequestList();
+  }
+
+  // Refresh 加载事件
+  Future loadMore() async {
+    _curPage++;
+    fetchLabourRequestList();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _curPage = 1;
+    _needShowPlaceholder = true;
+    fetchLabourRequestList();
+  }
+
+  /// 获取列表数据
+  Future fetchLabourRequestList() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    }
+
+    // 并发执行两个请求
+    var futures = [
+      _thRepository.fetchLabourRequestTable(
+        startDate: DateTimeUtils.formatDate(state.selectedStartDate, format: "yyyy-MM-dd"),
+        endDate: DateTimeUtils.formatDate(state.selectedEndDate, format: "yyyy-MM-dd"),
+        status: state.selectedStatusId,
+        outletId: state.selectedDepartmentId,
+        divisionId: state.selectedDivisionId,
+        curPage: _curPage,
+        cancelToken: cancelToken,
+      ),
+      state.indexOptions == null
+          ? _thRepository.fetchLabourRequestIndex(
+              cancelToken: cancelToken,
+            )
+          : Future(() => HttpResult(isSuccess: true).convert(data: state.indexOptions!)),
+    ];
+
+    //拿到结果
+    var results = await Future.wait(futures);
+    var listResult = results[0] as HttpResult<THOALabourTableEntity>;
+    var optionResult = results[1] as HttpResult<LabourRequestIndexEntity>;
+
+    //选项数据
+    if (state.indexOptions == null && optionResult.isSuccess) {
+      state.indexOptions = optionResult.data!;
+    }
+
+    // 处理数据
+    if (listResult.isSuccess) {
+      state.totalNum += listResult.data?.totNum ?? 0;
+      handleList(listResult.data?.rows);
+    } else {
+      errorMessage = listResult.errorMsg;
+      changeLoadingState(LoadState.State_Error);
+    }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void handleList(List<THOALabourTableRows>? list) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      if (_curPage == 1) {
+        //刷新的方式
+        state.datas.clear();
+        state.datas.addAll(list);
+        refreshController.finishRefresh();
+
+        //更新展示的状态
+        changeLoadingState(LoadState.State_Success);
+      } else {
+        //加载更多
+        state.datas.addAll(list);
+        refreshController.finishLoad();
+        update();
+      }
+    } else {
+      if (_curPage == 1) {
+        //展示无数据的布局
+        state.datas.clear();
+        changeLoadingState(LoadState.State_Empty);
+        refreshController.finishRefresh();
+      } else {
+        //展示加载完成,没有更多数据了
+        refreshController.finishLoad(IndicatorResult.noMore);
+      }
+    }
+  }
+
+  // 执行搜索
+  void doSearch(String keyword) {
+    state.keyword = keyword;
+    //赋值之后刷新
+    refreshController.callRefresh();
+  }
+
+  // 清空搜索条件
+  void resetFiltering() {
+    state.keyword = "";
+    state.searchController.text = "";
+
+    state.selectedStartDate = null;
+    state.selectedEndDate = null;
+    state.selectedStatusId = null;
+    state.selectedDepartmentId = null;
+
+    //赋值之后刷新
+    refreshController.callRefresh();
+  }
+
+  //展示筛选的弹窗
+  void showFilterDialog() {
+    if (state.indexOptions != null) {
+      DialogEngine.show(
+        widget: LabourRequestFilter(
+          optionResult: state.indexOptions!,
+          selectedStartDate: state.selectedStartDate,
+          selectedEndDate: state.selectedEndDate,
+          selectedStatusId: state.selectedStatusId,
+          selectedDepartmentId: state.selectedDepartmentId,
+          selectedDivisionId: state.selectedDivisionId,
+          onFilterAction: (startDate, endDate, statusId, departmentId, divisionId) {
+            state.selectedStartDate = startDate;
+            state.selectedEndDate = endDate;
+            state.selectedStatusId = statusId;
+            state.selectedDepartmentId = departmentId;
+            state.selectedDivisionId = divisionId;
+
+            //赋值之后刷新
+            refreshController.callRefresh();
+          },
+        ),
+        position: DialogPosition.top,
+        animType: DialogAnimation.fade,
+      );
+    }
+  }
+
+  /// 执行Recall请求
+  void requestRecall(String requestId) async {
+    var recallResult = await _thRepository.recallLabourRequest(
+      requestId,
+      cancelToken: cancelToken,
+    );
+
+    //处理数据
+    if (recallResult.isSuccess) {
+      NotifyEngine.showSuccess("Successful".tr);
+      fetchItemByIdAndRefreshItem(requestId);
+    } else {
+      ToastEngine.show(recallResult.errorMsg ?? "Network Load Error".tr);
+    }
+  }
+
+  //执行删除操作
+  void doDeleteAction(String requestId, int index) {
+    DialogEngine.show(
+        widget: AppDefaultDialog(
+      title: "Confirmation".tr,
+      message: "Are you sure you want to delete this job?".tr,
+      confirmAction: () {
+        _requestDeactivate(requestId, index);
+      },
+    ));
+  }
+
+  // 请求接口删除Job
+  void _requestDeactivate(String requestId, int index) async {
+    var result = await _thRepository.deleteLabourRequest(requestId, cancelToken: cancelToken);
+
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess("Successful".tr);
+
+      if (state.datas.length <= 1) {
+        refreshController.callRefresh();
+      } else {
+        state.datas.removeAt(index);
+        update();
+      }
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+  }
+
+  /// 根据ID获取Item对象,用于刷新
+  void fetchItemByIdAndRefreshItem(String requestId) async {
+    var result = await _thRepository.fetchItemByRequestId(
+      requestId,
+      cancelToken: cancelToken,
+    );
+
+    //处理数据
+    if (result.isSuccess) {
+      var data = result.data;
+      if (data != null && data.rows.isNotEmpty) {
+        final requestItem = data.rows[0];
+
+        //找到当前数据中的此 requestId,并替换对象,再刷新
+        var index = state.datas.indexWhere((element) => element.id == requestItem.id);
+        if (index >= 0) {
+          state.datas[index] = requestItem;
+          update();
+        }
+      }
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    fetchLabourRequestList();
+
+    registerEventBus();
+  }
+
+  @override
+  void onClose() {
+    unregisterEventBus();
+    state.datas.clear();
+    super.onClose();
+  }
+
+  // EventBus 的事件接收
+  Subscription? subscribe;
+
+  void registerEventBus() {
+    subscribe = bus.on(AppConstant.eventLabourRequestRefresh, (arg) {
+      var requestId = arg as String;
+      if (Utils.isNotEmpty(requestId)) {
+        fetchItemByIdAndRefreshItem(requestId);
+      } else {
+        refreshController.callRefresh();
+      }
+    });
+  }
+
+  void unregisterEventBus() {
+    bus.off(AppConstant.eventLabourRequestRefresh, subscribe);
+  }
+
+  //跳转到添加页面
+  void gotoAddLabourPage() {
+    LabourRequestAddPage.startInstance(0, "", false, (result) {
+      refreshController.callRefresh();
+    });
+  }
+
+  //去详情页面
+  void gotoDetailPage(THOALabourTableRows data) {
+    LabourRequestAddPage.startInstance(2, data.id.toString(), false, null);
+  }
+
+  //去编辑页面
+  void gotoEditPage(THOALabourTableRows data) {
+    LabourRequestAddPage.startInstance(1, data.id.toString(), false, (result) {
+      fetchItemByIdAndRefreshItem(data.id.toString());
+    });
+  }
+
+  //去状态工作流的页面
+  void gotoWorkflowPage(THOALabourTableRows data) {
+    LabourRequestWorkflowPage.startInstance(data.id.toString());
+  }
+
+  void doRecall(THOALabourTableRows data) {
+    DialogEngine.show(
+        widget: AppDefaultDialog(
+      title: "Message".tr,
+      message: "Are you sure you want to recall?".tr,
+      confirmAction: () {
+        requestRecall(data.id.toString());
+      },
+    ));
+  }
+
+  /// 展示附件弹窗
+  void showAttachmentDialog(THOALabourTableRows data) {
+    DialogEngine.show(
+      widget: OaAttachListDialog(
+        requestId: data.id.toString(),
+        jobTitle: data.jobTitle ?? "-",
+        jobDate: data.jobDate ?? "-",
+      ),
+    );
+  }
+}

+ 174 - 0
packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_list_page.dart

@@ -0,0 +1,174 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'labour_request_item.dart';
+import 'labour_request_list_controller.dart';
+import 'labour_request_list_state.dart';
+
+/*
+ * 用工请求的主页列表
+ */
+class LabourRequestListPage extends BaseStatefulPage<LabourRequestListController> {
+  LabourRequestListPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance() {
+    return Get.start(RouterPath.THLabourRequestOA);
+  }
+
+  @override
+  LabourRequestListController createRawController() {
+    return LabourRequestListController();
+  }
+
+  @override
+  State<LabourRequestListPage> createState() => _LabourRequestListState();
+}
+
+class _LabourRequestListState extends BaseState<LabourRequestListPage, LabourRequestListController> {
+  late LabourRequestListState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return SafeArea(
+        bottom: MediaQuery.of(context).padding.bottom > 38,
+        top: false,
+        child: Container(
+          width: double.infinity,
+          height: double.infinity,
+          padding: EdgeInsets.only(top: ScreenUtil.getStatusBarH(context)),
+          decoration: const BoxDecoration(
+            gradient: LinearGradient(
+              colors: [
+                Color(0xFF091D44),
+                Color(0xFF245A8A),
+                Color(0xFF7F7CEC),
+              ],
+              begin: Alignment.topCenter,
+              end: Alignment.bottomCenter,
+            ),
+          ),
+          child: Column(
+            children: [
+              MyAppBar.searchTitleBar(
+                context,
+                value: state.keyword,
+                hintText: 'Title'.tr,
+                controller: state.searchController,
+                onSearch: (keyword) {
+                  controller.doSearch(keyword);
+                },
+                actions: [
+                  //重置按钮
+                  MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      controller.resetFiltering();
+                    },
+                    text: "Reset".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#2BA9F9", opacity: 0.5),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 35,
+                  ).marginOnly(right: 15),
+
+                  //筛选图标
+                  const MyAssetImage(
+                    Assets.baseServiceTitleBarFilterIcon,
+                    width: 24,
+                    height: 16.5,
+                  ).onTap(() {
+                    FocusScope.of(context).unfocus();
+                    controller.showFilterDialog();
+                  }).marginOnly(right: 15),
+                ],
+              ),
+              // 添加按钮
+              MyButton(
+                type: ClickType.throttle,
+                milliseconds: 500,
+                onPressed: () {
+                  FocusScope.of(context).unfocus();
+                  controller.gotoAddLabourPage();
+                },
+                text: "Create New Job Request".tr,
+                textColor: ColorConstants.white,
+                fontSize: 16,
+                radius: 20,
+                backgroundColor: hexToColor("#FFBB1B"),
+                fontWeight: FontWeight.w500,
+              ).marginSymmetric(horizontal: 15, vertical: 15),
+
+              //底部的列表
+              EasyRefresh(
+                controller: controller.refreshController,
+                onRefresh: controller.onRefresh,
+                onLoad: controller.loadMore,
+                child: LoadStateLayout(
+                  state: controller.loadingState,
+                  errorMessage: controller.errorMessage,
+                  errorRetry: () {
+                    controller.retryRequest();
+                  },
+                  successSliverWidget: [
+                    SliverList(
+                        delegate: SliverChildBuilderDelegate(
+                      (context, index) {
+                        return LabourRequestItem(
+                          index: index,
+                          item: state.datas[index],
+                          totalNum: state.totalNum,
+                          onDetailAction: () {
+                            controller.gotoDetailPage(state.datas[index]);
+                          },
+                          onRecallAction: () {
+                            controller.doRecall(state.datas[index]);
+                          },
+                          onEditAction: () {
+                            controller.gotoEditPage(state.datas[index]);
+                          },
+                          onStatusAction: () {
+                            controller.gotoWorkflowPage(state.datas[index]);
+                          },
+                          onAttAction: (){
+                           controller.showAttachmentDialog(state.datas[index]);
+                          },
+                          onDeleteAction: (){
+                            controller.doDeleteAction(state.datas[index].id!, index);
+                          },
+                        );
+                      },
+                      childCount: state.datas.length,
+                    ))
+                  ],
+                ),
+              ).expanded(),
+            ],
+          ),
+        ),
+      );
+    });
+  }
+}

+ 20 - 0
packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_list_state.dart

@@ -0,0 +1,20 @@
+import 'package:domain/entity/response/labour_request_index_entity.dart';
+import 'package:domain/entity/response/labour_request_list_entity.dart';
+import 'package:domain/entity/response/t_h_o_a_labour_table_entity.dart';
+import 'package:flutter/material.dart';
+
+class LabourRequestListState {
+  //筛选条件
+  final TextEditingController searchController = TextEditingController();
+  String keyword = "";
+  DateTime? selectedStartDate;
+  DateTime? selectedEndDate;
+  String? selectedStatusId;
+  String? selectedDepartmentId;
+  String? selectedDivisionId;
+
+  //页面的列表数据
+  List<THOALabourTableRows> datas = [];
+  int totalNum = 0;
+  LabourRequestIndexEntity? indexOptions;
+}

+ 96 - 0
packages/cpt_th/lib/modules/labour/labour_request_workflow/labour_request_workflow_controller.dart

@@ -0,0 +1,96 @@
+import 'package:domain/entity/response/labour_request_work_flow_entity.dart';
+import 'package:domain/repository/th_oa_repository.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'labour_request_workflow_state.dart';
+
+
+class LabourRequestWorkflowController extends GetxController with DioCancelableMixin{
+  final THOARepository _thRepository = Get.find();
+  final LabourRequestWorkflowState state = LabourRequestWorkflowState();
+
+  var _needShowPlaceholder = true;
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Success;
+  String? errorMessage;
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,
+    controlFinishLoad: false,
+  );
+
+  // Refresh 刷新事件
+  Future onRefresh() async {
+    fetchWorkFlowList();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _needShowPlaceholder = true;
+    fetchWorkFlowList();
+  }
+
+  /// 获取服务器数据,成员考勤列表
+  Future fetchWorkFlowList() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    }
+
+    //获取到数据
+    var result = await _thRepository.fetchLabourRequestWorkFlow(
+      state.requestId,
+      cancelToken: cancelToken,
+    );
+
+    //处理数据
+    if (result.isSuccess) {
+      handleList(result.data?.records);
+      refreshController.finishRefresh(IndicatorResult.success);
+    } else {
+      errorMessage = result.errorMsg;
+      changeLoadingState(LoadState.State_Error);
+      refreshController.finishRefresh(IndicatorResult.fail);
+    }
+
+    //最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void handleList(List<LabourRequestWorkFlowRecords>? list) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      state.datas.clear();
+      state.datas.addAll(list);
+      //更新状态
+      changeLoadingState(LoadState.State_Success);
+    } else {
+      //展示无数据的布局
+      state.datas.clear();
+      changeLoadingState(LoadState.State_Empty);
+    }
+  }
+
+  @override
+  void onReady() async {
+    super.onReady();
+    fetchWorkFlowList();
+  }
+
+  @override
+  void onClose() {
+    super.onClose();
+    state.datas.clear();
+  }
+}

+ 102 - 0
packages/cpt_th/lib/modules/labour/labour_request_workflow/labour_request_workflow_page.dart

@@ -0,0 +1,102 @@
+
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'labour_request_workflow_controller.dart';
+import 'labour_request_workflow_state.dart';
+import 'labour_workflow_item.dart';
+
+/*
+ * 用工请求-状态工作流列表
+ */
+class LabourRequestWorkflowPage extends BaseStatefulPage<LabourRequestWorkflowController> {
+  LabourRequestWorkflowPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance(String? requestId) {
+    return Get.start(RouterPath.THLabourRequestWorkflowOA,arguments: {'requestId': requestId});
+  }
+
+  @override
+  LabourRequestWorkflowController createRawController() {
+    return LabourRequestWorkflowController();
+  }
+
+  @override
+  State<LabourRequestWorkflowPage> createState() => _LabourRequestWorkflowState();
+
+}
+
+class _LabourRequestWorkflowState extends BaseState<LabourRequestWorkflowPage, LabourRequestWorkflowController> {
+
+  late LabourRequestWorkflowState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+    state.requestId = Get.arguments['requestId'];
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return  SafeArea(
+        bottom: MediaQuery.of(context).padding.bottom > 38,
+          top: false,
+          child: Container(
+            width: double.infinity,
+            height: double.infinity,
+            padding: EdgeInsets.only(top: ScreenUtil.getStatusBarH(context)),
+            decoration: const BoxDecoration(
+              gradient: LinearGradient(
+                colors: [
+                  Color(0xFF091D44),
+                  Color(0xFF245A8A),
+                  Color(0xFF7F7CEC),
+                ],
+                begin: Alignment.topCenter,
+                end: Alignment.bottomCenter,
+              ),
+            ),
+            child: Column(
+              children: [
+                MyAppBar.titleBar(context, "Workflow".tr),
+
+                EasyRefresh(
+                  controller: controller.refreshController,
+                  onRefresh: controller.onRefresh,
+                  child: LoadStateLayout(
+                    state: controller.loadingState,
+                    errorMessage: controller.errorMessage,
+                    errorRetry: () {
+                      controller.retryRequest();
+                    },
+                    successSliverWidget: [
+                      SliverList(
+                          delegate: SliverChildBuilderDelegate((context, index) {
+                              return LabourWorkFlowItem(index: index,item: state.datas[index]);
+                            },
+                            childCount: state.datas.length,
+                          ))
+                    ],
+                  ),
+                ).expanded(),
+              ],
+            ).marginOnly(top: 10),
+          ),
+        );
+    });
+  }
+}
+
+

+ 10 - 0
packages/cpt_th/lib/modules/labour/labour_request_workflow/labour_request_workflow_state.dart

@@ -0,0 +1,10 @@
+import 'package:domain/entity/response/labour_request_work_flow_entity.dart';
+
+class LabourRequestWorkflowState {
+
+  String? requestId;
+
+  //页面的列表数据
+  List<LabourRequestWorkFlowRecords> datas = [];
+
+}

+ 230 - 0
packages/cpt_th/lib/modules/labour/labour_request_workflow/labour_workflow_item.dart

@@ -0,0 +1,230 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:domain/entity/response/labour_request_list_entity.dart';
+import 'package:domain/entity/response/labour_request_work_flow_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_text_view.dart';
+
+/**
+ * 用工请求的工作流列表Item
+ */
+class LabourWorkFlowItem extends StatelessWidget {
+  final int index;
+  final LabourRequestWorkFlowRecords item;
+
+  LabourWorkFlowItem({
+    required this.index,
+    required this.item,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      padding: EdgeInsets.symmetric(vertical: 23, horizontal: 21),
+      margin: EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 5),
+      decoration: BoxDecoration(
+        color: Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+        borderRadius: BorderRadius.circular(5), // 设置圆角
+      ),
+      child: Column(
+        mainAxisSize: MainAxisSize.max,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          //Node
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Node:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              MyTextView(
+                item.nodeName ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ),
+
+          // assigneeType
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Type:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //日期时间
+              MyTextView(
+                item.assigneeTypeShow ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // Designation
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Designation:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //人数
+              MyTextView(
+                item.designationShow ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // 状态
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Status:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //状态
+              MyTextView(
+                item.statusShow == null ? "" : item.statusShow!.tr,
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: "Approved" == item.statusShow
+                    ? ColorConstants.textGreen05DC82
+                    : "Rejected" == item.statusShow
+                    ? ColorConstants.textRedFF6262
+                    : "Recall" == item.statusShow
+                    ? ColorConstants.textYellowFFBB1B
+                    : ColorConstants.textBlue06D9FF,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // Operator
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Operator:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //发布状态
+              MyTextView(
+                item.auditName ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // 操作时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Audit Time:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //时间
+              MyTextView(
+                item.auditTime ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // 创建时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Created At:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //时间
+              MyTextView(
+                item.createdAt ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // Remark
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Remark:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //时间
+              MyTextView(
+                item.auditMark ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+        ],
+      ),
+    );
+  }
+}

+ 354 - 0
packages/cpt_th/lib/modules/labour/labour_review_list/labour_review_controller.dart

@@ -0,0 +1,354 @@
+import 'package:domain/entity/response/labour_request_index_entity.dart';
+import 'package:domain/entity/response/t_h_o_a_labour_review_table_entity.dart';
+import 'package:domain/repository/th_oa_repository.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/engine/dialog/dialog_engine.dart';
+import 'package:plugin_platform/engine/notify/notify_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:plugin_platform/http/http_result.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/dialog/app_default_dialog.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+import '../labour_request_add/labour_request_add_page.dart';
+import '../labour_request_list/dialog/oa_attach_list_dialog.dart';
+import '../labour_review_workflow/labour_request_workflow_page.dart';
+import 'labour_review_filter.dart';
+import 'labour_review_reject_dialog.dart';
+import 'labour_review_state.dart';
+
+class LabourReviewController extends GetxController with DioCancelableMixin {
+  final THOARepository _thRepository = Get.find();
+  final LabourReviewState state = LabourReviewState();
+
+  var _curPage = 1;
+  var _needShowPlaceholder = true;
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Success;
+  String? errorMessage;
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,
+    controlFinishLoad: true,
+  );
+
+  // Refresh 刷新事件
+  Future onRefresh() async {
+    _curPage = 1;
+    fetchAppliedStaffList();
+  }
+
+  // Refresh 加载事件
+  Future loadMore() async {
+    _curPage++;
+    fetchAppliedStaffList();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _curPage = 1;
+    _needShowPlaceholder = true;
+    fetchAppliedStaffList();
+  }
+
+  /// 获取服务器数据,通知消息列表
+  Future fetchAppliedStaffList() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    }
+
+    // 并发执行两个请求
+    var futures = [
+      _thRepository.fetchLabourReviewList(
+        state.keyword,
+        DateTimeUtils.formatDate(state.selectedStartDate, format: "yyyy-MM-dd"),
+        DateTimeUtils.formatDate(state.selectedEndDate, format: "yyyy-MM-dd"),
+        state.selectedDepartmentId,
+        curPage: _curPage,
+        cancelToken: cancelToken,
+      ),
+      state.indexOptions == null
+          ? _thRepository.fetchLabourReviewIndex(
+              cancelToken: cancelToken,
+            )
+          : Future(() => HttpResult(isSuccess: true).convert(data: state.indexOptions!)),
+    ];
+
+    //拿到结果
+    var results = await Future.wait(futures);
+    var listResult = results[0] as HttpResult<THOALabourReviewTableEntity>;
+    var optionResult = results[1] as HttpResult<LabourRequestIndexEntity>;
+
+    //选项数据
+    if (state.indexOptions == null && optionResult.isSuccess) {
+      state.indexOptions = optionResult.data!;
+    }
+
+    // 处理数据
+    if (listResult.isSuccess) {
+      handleList(listResult.data?.rows);
+    } else {
+      errorMessage = listResult.errorMsg;
+      changeLoadingState(LoadState.State_Error);
+    }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void handleList(List<THOALabourReviewTableRows>? list) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      if (_curPage == 1) {
+        //刷新的方式
+        state.datas.clear();
+        state.datas.addAll(list);
+        refreshController.finishRefresh();
+
+        //更新展示的状态
+        changeLoadingState(LoadState.State_Success);
+      } else {
+        //加载更多
+        state.datas.addAll(list);
+        refreshController.finishLoad();
+        update();
+      }
+    } else {
+      if (_curPage == 1) {
+        //展示无数据的布局
+        state.datas.clear();
+        changeLoadingState(LoadState.State_Empty);
+        refreshController.finishRefresh();
+      } else {
+        //展示加载完成,没有更多数据了
+        refreshController.finishLoad(IndicatorResult.noMore);
+      }
+    }
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    fetchAppliedStaffList();
+  }
+
+  @override
+  void onClose() {
+    state.datas.clear();
+    super.onClose();
+  }
+
+  /// 搜索
+  void doSearch(String keyword) {
+    state.keyword = keyword;
+    //赋值之后刷新
+    refreshController.callRefresh();
+  }
+
+  /// 清空筛选条件
+  void resetFiltering() {
+    state.keyword = "";
+    state.searchController.text = "";
+    state.selectedStartDate = null;
+    state.selectedEndDate = null;
+    state.selectedDepartmentId = null;
+    //赋值之后刷新
+    refreshController.callRefresh();
+  }
+
+  /// Item选中与未选中设置
+  void doSelectedOrNot(THOALabourReviewTableRows data) {
+    data.isSelected = !data.isSelected;
+    Log.d("isSelected:${data.isSelected}");
+    update();
+  }
+
+  /// 执行批量同意
+  void _requestBatchApprove(String recordIds) async {
+    // //执行请求
+    var result = await _thRepository.batchActionLabourReviews(
+      recordIds: recordIds,
+      type: 'approve',
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess("Successful".tr);
+
+      _removeItemsByList(recordIds);
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+      return;
+    }
+  }
+
+  /// 执行批量拒绝
+  void _requestBatchReject(String recordIds, String? reason) async {
+    // //执行请求
+    var result = await _thRepository.batchActionLabourReviews(
+      recordIds: recordIds,
+      type: 'reject',
+      auditMark: reason,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess("Successful".tr);
+
+      _removeItemsByList(recordIds);
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+      return;
+    }
+  }
+
+  /// 批准的操作
+  void operationApprove() async {
+    //找出已经选中的员工(只有状态为3 Approve的状态才能修改)
+    var selectedList = state.datas.where((element) => element.isSelected).toList(growable: false);
+    if (selectedList.isNotEmpty) {
+      var ids = selectedList.map((e) => e.recordId.toString()).toList(growable: false);
+      var recordIds = ids.join(',');
+
+      // Are you sure 的弹窗
+      DialogEngine.show(
+        widget: AppDefaultDialog(
+          title: "Message".tr,
+          message: "Are you sure you want to setting approved?".tr,
+          confirmAction: () {
+            _requestBatchApprove(recordIds);
+          },
+        ),
+      );
+    } else {
+      ToastEngine.show("Please select the record".tr);
+    }
+  }
+
+  /// 拒绝的操作
+  void operationReject() async {
+    //找出已经选中的员工(只有状态为3 Approve的状态才能修改)
+    var selectedList = state.datas.where((element) => element.isSelected).toList(growable: false);
+    if (selectedList.isNotEmpty) {
+      var ids = selectedList.map((e) => e.recordId.toString()).toList(growable: false);
+      var recordIds = ids.join(',');
+
+      // Are you sure 的弹窗
+      DialogEngine.show(
+        widget: LabourReviewRejectDialog(
+          confirmAction: (reason) {
+            //请求接口,提交评论
+            _requestBatchReject(recordIds, reason);
+          },
+        ),
+      );
+    } else {
+      ToastEngine.show("Please select the record".tr);
+    }
+  }
+
+  /// 删除对应的recordId的Item数据
+  void _removeItemsByList(String recordIds) {
+    // 将逗号分隔的字符串转换为数组
+    List<String> recordIdList = recordIds.split(',');
+
+    // 移除列表中符合条件的项
+    state.datas.removeWhere((e) => recordIdList.contains(e.recordId));
+
+    update();
+  }
+
+  /// 展示顶部的筛选
+  void showFilterDialog() {
+    if (state.indexOptions != null) {
+      DialogEngine.show(
+        widget: LabourReviewFilter(
+          optionResult: state.indexOptions!,
+          selectedStartDate: state.selectedStartDate,
+          selectedEndDate: state.selectedEndDate,
+          selectedDepartmentId: state.selectedDepartmentId,
+          onFilterAction: (startDate, endDate, departmentId) {
+            state.selectedStartDate = startDate;
+            state.selectedEndDate = endDate;
+            state.selectedDepartmentId = departmentId;
+
+            //赋值之后刷新
+            refreshController.callRefresh();
+          },
+        ),
+        position: DialogPosition.top,
+        animType: DialogAnimation.fade,
+      );
+    }
+  }
+
+  /// 去详情页面
+  void gotoDetailPage(THOALabourReviewTableRows data) {
+    LabourRequestAddPage.startInstance(2, data.requestId.toString(), true, null);
+  }
+
+  /// 去编辑页面
+  void gotoEditPage(THOALabourReviewTableRows data) {
+    LabourRequestAddPage.startInstance(1, data.requestId.toString(), true, (result) {
+      fetchItemByIdAndRefreshItem(data.requestId.toString());
+    });
+  }
+
+  /// 去用工审核流程页面
+  void gotoStatusViewPage(THOALabourReviewTableRows data) {
+    LabourReviewWorkflowPage.startInstance(data.orderId);
+  }
+
+  /// 根据ID获取Item对象,用于刷新
+  void fetchItemByIdAndRefreshItem(String? requestId) async {
+    var result = await _thRepository.fetchItemByRecordId(
+      requestId,
+      cancelToken: cancelToken,
+    );
+
+    //处理数据
+    if (result.isSuccess) {
+      var data = result.data;
+      if (data != null && data.rows.isNotEmpty) {
+        List<THOALabourReviewTableRows> newItem = data.rows;
+
+        // 创建一个 Map 来加速查找
+        Map<String, THOALabourReviewTableRows> newItemMap = {for (var item in newItem) item.recordId ?? "": item};
+
+        // 遍历 state.datas 进行替换
+        for (int i = 0; i < state.datas.length; i++) {
+          if (newItemMap.containsKey(state.datas[i].recordId)) {
+            state.datas[i] = newItemMap[state.datas[i].recordId]!;
+          }
+        }
+
+        //刷新
+        update();
+      }
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+  }
+
+  /// 展示附件弹窗
+  void showAttachmentDialog(THOALabourReviewTableRows data) {
+    DialogEngine.show(
+      widget: OaAttachListDialog(
+        requestId: data.requestId.toString(),
+        jobTitle: data.jobTitle ?? "-",
+        jobDate: data.jobDate ?? "-",
+      ),
+    );
+  }
+}

+ 318 - 0
packages/cpt_th/lib/modules/labour/labour_review_list/labour_review_filter.dart

@@ -0,0 +1,318 @@
+import 'dart:typed_data';
+import 'dart:ui';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/response/labour_request_index_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/picker/date_picker_util.dart';
+import 'package:widgets/picker/option_pick_util.dart';
+import 'package:widgets/widget_export.dart';
+
+/*
+ * 用工请求列表的筛选
+ */
+class LabourReviewFilter extends StatefulWidget {
+  void Function(DateTime? selectedStartDate, DateTime? selectedEndDate, String? selectedDepartmentId)? onFilterAction;
+  LabourRequestIndexEntity optionResult;
+  DateTime? selectedStartDate;
+  DateTime? selectedEndDate;
+  String? selectedDepartmentId;
+
+  LabourReviewFilter({
+    required this.optionResult,
+    required this.selectedStartDate,
+    required this.selectedEndDate,
+    required this.selectedDepartmentId,
+    this.onFilterAction,
+  });
+
+  @override
+  State<LabourReviewFilter> createState() => _LabourReviewFilterState();
+}
+
+class _LabourReviewFilterState extends State<LabourReviewFilter> {
+  DateTime? selectedStartDate;
+  DateTime? selectedEndDate;
+  String? selectedStatusId;
+  String? selectedDepartmentId;
+
+  @override
+  void initState() {
+    super.initState();
+    this.selectedStartDate = widget.selectedStartDate;
+    this.selectedEndDate = widget.selectedEndDate;
+    this.selectedDepartmentId = widget.selectedDepartmentId;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      mainAxisAlignment: MainAxisAlignment.start,
+      children: [
+        SizedBox(
+          height: kToolbarHeight + ScreenUtil.getStatusBarH(context) + 1,
+        ),
+        Container(
+          padding: const EdgeInsets.only(left: 15, right: 15, top: 17.5, bottom: 20),
+          width: double.infinity,
+          decoration: const BoxDecoration(
+            color: Colors.white,
+          ),
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              //部门
+              MyTextView(
+                "Outlet".tr,
+                fontSize: 14,
+                isFontMedium: true,
+                textColor: ColorConstants.black33,
+              ),
+
+              //选择部门
+              Container(
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
+                height: 45,
+                decoration: const BoxDecoration(
+                  color: ColorConstants.grayECECEC,
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      selectedDepartmentId == null || selectedDepartmentId == "0"
+                          ? ""
+                          : widget.optionResult.outletList!.firstWhere((element) => element.value.toString() == selectedDepartmentId).txt!,
+                      hint: "Choose Outlet".tr,
+                      textHintColor: ColorConstants.textBlackHint,
+                      fontSize: 14,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black33,
+                    ).expanded(),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                  ],
+                ),
+              ).onTap(() {
+                pickerOutlet();
+              }),
+
+              //开始时间
+              MyTextView(
+                "Start Date".tr,
+                fontSize: 14,
+                isFontMedium: true,
+                marginTop: 11,
+                textColor: ColorConstants.black33,
+              ),
+
+              //选择时间
+              Container(
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
+                height: 45,
+                decoration: const BoxDecoration(
+                  color: ColorConstants.grayECECEC,
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      selectedStartDate == null ? "" : DateTimeUtils.formatDate(selectedStartDate, format: "yyyy-MM-dd"),
+                      fontSize: 14,
+                      hint: "Choose Start Date".tr,
+                      textHintColor: ColorConstants.textBlackHint,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black33,
+                    ).expanded(),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                  ],
+                ),
+              ).onTap(() {
+                pickerStartDate();
+              }),
+
+              //结束日期
+              MyTextView(
+                "End Date".tr,
+                fontSize: 14,
+                marginTop: 11,
+                isFontMedium: true,
+                textColor: ColorConstants.black33,
+              ),
+
+              //选择结束日期
+              Container(
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
+                height: 45,
+                decoration: const BoxDecoration(
+                  color: ColorConstants.grayECECEC,
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      selectedEndDate == null ? "" : DateTimeUtils.formatDate(selectedEndDate, format: "yyyy-MM-dd"),
+                      fontSize: 14,
+                      hint: "Choose End Date".tr,
+                      textHintColor: ColorConstants.textBlackHint,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black33,
+                    ).expanded(),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                  ],
+                ),
+              ).onTap(() {
+                pickerEndDate();
+              }),
+
+              //按钮组
+              Row(
+                children: [
+                  MyButton(
+                    onPressed: () {
+                      //只是Reset当前的弹窗筛选选项
+                      widget.selectedStartDate = null;
+                      widget.selectedEndDate = null;
+                      widget.selectedDepartmentId = null;
+
+                      setState(() {
+                        selectedStartDate = null;
+                        selectedEndDate = null;
+                        selectedStatusId = null;
+                        selectedDepartmentId = null;
+                      });
+                    },
+                    text: "Reset".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#FFBB1B"),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 36,
+                  ).expanded(),
+                  SizedBox(width: 15),
+                  MyButton(
+                    onPressed: () {
+                      onCancel();
+                      widget.onFilterAction?.call(selectedStartDate, selectedEndDate, selectedDepartmentId);
+                    },
+                    text: "Filter".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#0AC074"),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 36,
+                  ).expanded(),
+                ],
+              ).marginOnly(top: 20),
+            ],
+          ),
+        ),
+        Center(child: const MyAssetImage(Assets.baseServiceDialogDeleteIcon, width: 26.5, height: 26.5).marginOnly(top: 35)).onTap(() {
+          onCancel();
+        }),
+      ],
+    );
+  }
+
+  //取消弹框
+  void onCancel() async {
+    SmartDialog.dismiss();
+  }
+
+  /// 筛选开始日期
+  void pickerStartDate() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: selectedStartDate,
+      onDateTimeChanged: (date) {
+        setState(() {
+          selectedStartDate = date;
+        });
+      },
+      title: "Start Date".tr,
+    );
+  }
+
+  /// 筛选结束日期
+  void pickerEndDate() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: selectedEndDate ?? selectedStartDate,
+      onDateTimeChanged: (date) {
+        setState(() {
+          selectedEndDate = date;
+        });
+      },
+      title: "End Date".tr,
+    );
+  }
+
+  /// 筛选部门
+  void pickerOutlet() {
+    int selectedDepartmentIndex;
+    if (selectedDepartmentId == null) {
+      selectedDepartmentIndex = 0;
+    } else {
+      selectedDepartmentIndex = widget.optionResult.outletList!.indexWhere((department) => department.value.toString() == selectedDepartmentId);
+    }
+
+    if (selectedDepartmentIndex < 0) {
+      selectedDepartmentIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: widget.optionResult.outletList!.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedDepartmentIndex,
+      onPickerChanged: (_, index) {
+        setState(() {
+          selectedDepartmentId = widget.optionResult.outletList![index].value!.toString();
+        });
+      },
+    );
+  }
+
+  /// 筛选状态
+  void pickerStatus() {
+    int selectedStatusIndex;
+    if (selectedStatusId == null) {
+      selectedStatusIndex = 0;
+    } else {
+      selectedStatusIndex = widget.optionResult.statusList!.indexWhere((department) => department.value.toString() == selectedStatusId);
+    }
+
+    if (selectedStatusIndex < 0) {
+      selectedStatusIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: widget.optionResult.statusList!.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedStatusIndex,
+      onPickerChanged: (_, index) {
+        setState(() {
+          selectedStatusId = widget.optionResult.statusList![index].value!.toString();
+        });
+      },
+    );
+  }
+}

+ 288 - 0
packages/cpt_th/lib/modules/labour/labour_review_list/labour_review_item.dart

@@ -0,0 +1,288 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/response/t_h_o_a_labour_review_table_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+/*
+ * 用工审核的列表Item
+ */
+class LabourReviewItem extends StatelessWidget {
+  final int index;
+  final THOALabourReviewTableRows item;
+  final VoidCallback? onStatusAction;
+  final VoidCallback? onEditAction;
+  final VoidCallback? onAttAction;
+  final VoidCallback? onItemAction;
+
+  LabourReviewItem({
+    required this.index,
+    required this.item,
+    this.onStatusAction,
+    this.onEditAction,
+    this.onAttAction,
+    this.onItemAction,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      padding: const EdgeInsets.symmetric(vertical: 23, horizontal: 21),
+      margin: const EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 5),
+      decoration: BoxDecoration(
+        color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+        borderRadius: BorderRadius.circular(5), // 设置圆角
+      ),
+      child: Column(
+        mainAxisSize: MainAxisSize.max,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          // 用工请求名称
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              //名称
+              MyTextView(
+                item.jobTitle ?? "-",
+                isFontMedium: true,
+                textColor: ColorConstants.textYellowFFBB1B,
+                fontSize: 14,
+                marginLeft: 5,
+                marginRight: 5,
+                textDecoration: TextDecoration.underline,
+                decorationColor: ColorConstants.textYellowFFBB1B,
+                // 可选,设置下划线的颜色
+                decorationThickness: 2.0,
+                // 可选,设置下划线的粗细
+                decorationStyle: TextDecorationStyle.solid,
+              ).expanded(),
+
+              //是否选中
+              MyAssetImage(
+                item.isSelected ? Assets.baseServiceItemSelectedIcon : Assets.baseServiceItemUnselectedIcon,
+                width: 20.5,
+                height: 20.5,
+              ),
+            ],
+          ),
+
+          // 部门
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Outlet:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //部门
+              MyTextView(
+                item.outletName ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // 工作日期时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Job Time:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //日期时间
+              MyTextView(
+                "${item.jobDate} ${item.startTime}~${item.endTime}",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // 人数
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "${"Manpower Needed".tr}:",
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //人数
+              MyTextView(
+                item.hiringNum.toString(),
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // 薪水
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "${"Hourly Rate".tr}:",
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //发布状态
+              MyTextView(
+                item.hourlyRate ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // 状态
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Status:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //状态
+              MyTextView(
+                item.statusShow == null ? "" : item.statusShow!.tr,
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: "Approved" == item.statusShow
+                    ? ColorConstants.textGreen05DC82
+                    : "Rejected" == item.statusShow
+                        ? ColorConstants.textRedFF6262
+                        : "Recall" == item.statusShow
+                            ? ColorConstants.textYellowFFBB1B
+                            : ColorConstants.textBlue06D9FF,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // 创建时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Created At:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //发布状态
+              MyTextView(
+                item.createdAt ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          //按钮组
+          Visibility(
+            visible: true,
+            child: Row(
+              mainAxisSize: MainAxisSize.max,
+              mainAxisAlignment: MainAxisAlignment.end,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+
+                //Edit按钮
+                Visibility(
+                  visible: true,
+                  child: MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      onEditAction?.call();
+                    },
+                    text: "Edit".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#FFBB1B"),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 35,
+                  ).marginOnly(left: 12),
+                ),
+
+                //Att 附件
+                Visibility(
+                  visible: true,
+                  child: MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      onAttAction?.call();
+                    },
+                    text: "Att(${item.attNum})",
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#CA28E4"),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 35,
+                  ).marginOnly(left: 12),
+                ),
+
+                //状态工作流按钮
+                Visibility(
+                  visible: true,
+                  child: MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      onStatusAction?.call();
+                    },
+                    text: "Status".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#0AC074"),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 35,
+                  ).marginOnly(left: 12),
+                ),
+              ],
+            ).marginOnly(top: 18, bottom: 2),
+          ),
+        ],
+      ),
+    ).onTap(() {
+      onItemAction?.call();
+    });
+  }
+}

+ 189 - 0
packages/cpt_th/lib/modules/labour/labour_review_list/labour_review_page.dart

@@ -0,0 +1,189 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.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_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'labour_review_item.dart';
+import 'labour_review_controller.dart';
+
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/my_appbar.dart';
+import 'labour_review_state.dart';
+
+/*
+ * 用工请求审核的列表
+ */
+class LabourReviewPage extends BaseStatefulPage<LabourReviewController> {
+  LabourReviewPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance() {
+    return Get.start(RouterPath.THLabourReviewOA);
+  }
+
+  @override
+  LabourReviewController createRawController() {
+    return LabourReviewController();
+  }
+
+  @override
+  State<LabourReviewPage> createState() => _LabourReviewState();
+}
+
+class _LabourReviewState extends BaseState<LabourReviewPage, LabourReviewController> {
+  late LabourReviewState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return SafeArea(
+        bottom: MediaQuery.of(context).padding.bottom > 38,
+        top: false,
+        child: Container(
+          width: double.infinity,
+          height: double.infinity,
+          padding: EdgeInsets.only(top: ScreenUtil.getStatusBarH(context)),
+          decoration: const BoxDecoration(
+            gradient: LinearGradient(
+              colors: [
+                Color(0xFF091D44),
+                Color(0xFF245A8A),
+                Color(0xFF7F7CEC),
+              ],
+              begin: Alignment.topCenter,
+              end: Alignment.bottomCenter,
+            ),
+          ),
+          child: Column(
+            children: [
+              MyAppBar.searchTitleBar(
+                context,
+                value: state.keyword,
+                hintText: 'Title'.tr,
+                controller: state.searchController,
+                onSearch: (keyword) {
+                  controller.doSearch(keyword);
+                },
+                actions: [
+                  //重置按钮
+                  MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      controller.resetFiltering();
+                    },
+                    text: "Reset".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#2BA9F9", opacity: 0.5),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 35,
+                  ).marginOnly(right: 15),
+
+                  //筛选图标
+                  const MyAssetImage(
+                    Assets.baseServiceTitleBarFilterIcon,
+                    width: 24,
+                    height: 16.5,
+                  ).onTap(() {
+                    FocusScope.of(context).unfocus();
+                    controller.showFilterDialog();
+                  }).marginOnly(right: 15),
+                ],
+              ),
+
+              //底部的列表
+              EasyRefresh(
+                controller: controller.refreshController,
+                onRefresh: controller.onRefresh,
+                onLoad: controller.loadMore,
+                child: LoadStateLayout(
+                  state: controller.loadingState,
+                  errorMessage: controller.errorMessage,
+                  errorRetry: () {
+                    controller.retryRequest();
+                  },
+                  successSliverWidget: [
+                    SliverList(
+                        delegate: SliverChildBuilderDelegate(
+                      (context, index) {
+                        return LabourReviewItem(
+                          index: index,
+                          item: state.datas[index],
+                          onAttAction: () {
+                            controller.showAttachmentDialog(state.datas[index]);
+                          },
+                          onEditAction: () {
+                            controller.gotoEditPage(state.datas[index]);
+                          },
+                          onStatusAction: () {
+                            controller.gotoStatusViewPage(state.datas[index]);
+                          },
+                          onItemAction: () {
+                            controller.doSelectedOrNot(state.datas[index]);
+                          },
+                        );
+                      },
+                      childCount: state.datas.length,
+                    ))
+                  ],
+                ),
+              ).expanded(),
+
+              Row(
+                mainAxisSize: MainAxisSize.max,
+                children: [
+                  //批量Approve
+                  MyTextView(
+                    "Batch Confirm".tr,
+                    fontSize: 17,
+                    isFontMedium: true,
+                    boxHeight: 48,
+                    onClick: () {
+                      controller.operationApprove();
+                    },
+                    alignment: Alignment.center,
+                    textAlign: TextAlign.center,
+                    textColor: Colors.white,
+                    backgroundColor: ColorConstants.textGreen0AC074,
+                  ).expanded(),
+
+                  //批量修改时间
+                  MyTextView(
+                    "Batch Reject".tr,
+                    fontSize: 17,
+                    isFontMedium: true,
+                    boxHeight: 48,
+                    onClick: () {
+                      controller.operationReject();
+                    },
+                    alignment: Alignment.center,
+                    textAlign: TextAlign.center,
+                    textColor: Colors.white,
+                    backgroundColor: ColorConstants.textRedFF6262,
+                  ).expanded(),
+                ],
+              ),
+            ],
+          ),
+        ),
+      );
+    });
+  }
+}

+ 190 - 0
packages/cpt_th/lib/modules/labour/labour_review_list/labour_review_reject_dialog.dart

@@ -0,0 +1,190 @@
+import 'dart:ui';
+
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/response/job_list_remark_view_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:shared/utils/util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/shatter/rating_widget.dart';
+import 'package:widgets/widget_export.dart';
+
+/*
+ * 拒绝的弹窗
+ */
+class LabourReviewRejectDialog extends StatefulWidget {
+
+  void Function(String reason)? confirmAction;
+
+  LabourReviewRejectDialog({this.confirmAction});
+
+  @override
+  State<LabourReviewRejectDialog> createState() => _LabourReviewRejectDialogState();
+}
+
+class _LabourReviewRejectDialogState extends State<LabourReviewRejectDialog> {
+
+  late TextEditingController _controller;
+  late FocusNode _focusNode;
+
+  @override
+  void initState() {
+    super.initState();
+    _controller = TextEditingController();
+    _focusNode = FocusNode();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.center,
+      mainAxisAlignment: MainAxisAlignment.center,
+      children: [
+        //Title (如果使用 Container 为最外层容器则默认为 match_parent 的效果,除非我们限制宽度和最大高度最小高度)
+        Container(
+          width: double.infinity,
+          decoration: const BoxDecoration(
+            color: Colors.white,
+            borderRadius: BorderRadius.all(Radius.circular(15)),
+          ),
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              Center(
+                child: MyTextView(
+                  "Reason for refusal".tr,
+                  fontSize: 19,
+                  isFontMedium: true,
+                  textColor: ColorConstants.black,
+                  marginTop: 23,
+                  marginLeft: 22,
+                  marginRight: 22,
+                ),
+              ),
+
+              IgnoreKeyboardDismiss(
+                child: Container(
+                  height: 130,
+                  margin: const EdgeInsets.symmetric(vertical: 19, horizontal: 22),
+                  padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 15),
+                  decoration: BoxDecoration(
+                    color: const Color(0xFFF0F0F0),
+                    border: Border.all(
+                      color: const Color(0xFFD8D8D8),
+                      width: 0.5,
+                    ),
+                  ),
+                  child: TextField(
+                    cursorColor: ColorConstants.black66,
+                    cursorWidth: 1.5,
+                    autofocus: false,
+                    enabled: true,
+                    focusNode: _focusNode,
+                    controller: _controller,
+                    // 装饰
+                    decoration: InputDecoration(
+                      isDense: true,
+                      isCollapsed: true,
+                      border: InputBorder.none,
+                      hintText: "Enter...".tr,
+                      hintStyle: const TextStyle(
+                        color: ColorConstants.black66,
+                        fontSize: 15.0,
+                        fontWeight: FontWeight.w400,
+                      ),
+                    ),
+                    style: const TextStyle(
+                      color: ColorConstants.black,
+                      fontSize: 15.0,
+                      fontWeight: FontWeight.w400,
+                    ),
+                    // 键盘动作右下角图标
+                    textInputAction: TextInputAction.done,
+                    onSubmitted: (value) {
+                      doCallbackAction();
+                    },
+                  ),
+                ),
+              ),
+
+              // 分割线
+              Container(
+                color: const Color(0XFFCECECE),
+                height: 0.5,
+              ),
+
+              //按钮组
+              Row(
+                children: [
+                  Expanded(
+                      flex: 1,
+                      child: InkWell(
+                        onTap: () {
+                          onCancel();
+                        },
+                        child: MyTextView(
+                          "Cancel".tr,
+                          fontSize: 17.5,
+                          isFontMedium: true,
+                          textAlign: TextAlign.center,
+                          textColor: const Color(0XFF0085C4),
+                          cornerRadius: 3,
+                          borderWidth: 1,
+                        ),
+                      )),
+                  Container(
+                    color: const Color(0xff09141F).withOpacity(0.13),
+                    width: 0.5,
+                  ),
+                  Expanded(
+                      flex: 1,
+                      child: InkWell(
+                        onTap: () {
+                          doCallbackAction();
+                        },
+                        child: MyTextView(
+                          "Submit".tr,
+                          marginLeft: 10,
+                          fontSize: 17.5,
+                          isFontMedium: true,
+                          textAlign: TextAlign.center,
+                          textColor: const Color(0XFF0085C4),
+                          cornerRadius: 3,
+                        ),
+                      )),
+                ],
+              ).constrained(height: 46),
+            ],
+          ),
+        ),
+      ],
+    ).constrained(width: 285);
+  }
+
+  //取消弹框
+  void onCancel() async {
+    SmartDialog.dismiss();
+  }
+
+  //执行回调
+  void doCallbackAction() {
+    _focusNode.unfocus();
+
+    final content = _controller.text.toString();
+
+    if (Utils.isEmpty(content)) {
+      ToastEngine.show("Please Enter Reason".tr);
+      return;
+    }
+
+    onCancel();
+
+    widget.confirmAction?.call(content);
+  }
+}

+ 16 - 0
packages/cpt_th/lib/modules/labour/labour_review_list/labour_review_state.dart

@@ -0,0 +1,16 @@
+import 'package:domain/entity/response/labour_request_index_entity.dart';
+import 'package:domain/entity/response/t_h_o_a_labour_review_table_entity.dart';
+import 'package:flutter/material.dart';
+
+class LabourReviewState {
+  //筛选条件
+  final TextEditingController searchController = TextEditingController();
+  String keyword = "";
+  DateTime? selectedStartDate;
+  DateTime? selectedEndDate;
+  String? selectedDepartmentId;
+
+  //页面的列表数据
+  List<THOALabourReviewTableRows> datas = [];
+  LabourRequestIndexEntity? indexOptions;
+}

+ 102 - 0
packages/cpt_th/lib/modules/labour/labour_review_workflow/labour_request_workflow_page.dart

@@ -0,0 +1,102 @@
+
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'labour_review_workflow_controller.dart';
+import 'labour_request_workflow_state.dart';
+import 'labour_review_workflow_item.dart';
+
+/*
+ * 用工请求审核-状态工作流列表
+ */
+class LabourReviewWorkflowPage extends BaseStatefulPage<LabourReviewWorkflowController> {
+  LabourReviewWorkflowPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance(String? requestId) {
+    return Get.start(RouterPath.THLabourReviewWorkflowOA,arguments: {'requestId': requestId});
+  }
+
+  @override
+  LabourReviewWorkflowController createRawController() {
+    return LabourReviewWorkflowController();
+  }
+
+  @override
+  State<LabourReviewWorkflowPage> createState() => _LabourReviewWorkflowState();
+
+}
+
+class _LabourReviewWorkflowState extends BaseState<LabourReviewWorkflowPage, LabourReviewWorkflowController> {
+
+  late LabourReviewWorkflowState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+    state.requestId = Get.arguments['requestId'];
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return  SafeArea(
+        bottom: MediaQuery.of(context).padding.bottom > 38,
+          top: false,
+          child: Container(
+            width: double.infinity,
+            height: double.infinity,
+            padding: EdgeInsets.only(top: ScreenUtil.getStatusBarH(context)),
+            decoration: const BoxDecoration(
+              gradient: LinearGradient(
+                colors: [
+                  Color(0xFF091D44),
+                  Color(0xFF245A8A),
+                  Color(0xFF7F7CEC),
+                ],
+                begin: Alignment.topCenter,
+                end: Alignment.bottomCenter,
+              ),
+            ),
+            child: Column(
+              children: [
+                MyAppBar.titleBar(context, "Workflow".tr),
+
+                EasyRefresh(
+                  controller: controller.refreshController,
+                  onRefresh: controller.onRefresh,
+                  child: LoadStateLayout(
+                    state: controller.loadingState,
+                    errorMessage: controller.errorMessage,
+                    errorRetry: () {
+                      controller.retryRequest();
+                    },
+                    successSliverWidget: [
+                      SliverList(
+                          delegate: SliverChildBuilderDelegate((context, index) {
+                              return LabourReviewWorkFlowItem(index: index,item: state.datas[index]);
+                            },
+                            childCount: state.datas.length,
+                          ))
+                    ],
+                  ),
+                ).expanded(),
+              ],
+            ).marginOnly(top: 10),
+          ),
+        );
+    });
+  }
+}
+
+

+ 10 - 0
packages/cpt_th/lib/modules/labour/labour_review_workflow/labour_request_workflow_state.dart

@@ -0,0 +1,10 @@
+import 'package:domain/entity/response/labour_review_status_entity.dart';
+
+class LabourReviewWorkflowState {
+
+  String? requestId;
+
+  //页面的列表数据
+  List<LabourReviewStatusRecords> datas = [];
+
+}

+ 96 - 0
packages/cpt_th/lib/modules/labour/labour_review_workflow/labour_review_workflow_controller.dart

@@ -0,0 +1,96 @@
+import 'package:domain/entity/response/labour_review_status_entity.dart';
+import 'package:domain/repository/th_oa_repository.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'labour_request_workflow_state.dart';
+
+
+class LabourReviewWorkflowController extends GetxController with DioCancelableMixin{
+  final THOARepository _thRepository = Get.find();
+  final LabourReviewWorkflowState state = LabourReviewWorkflowState();
+
+  var _needShowPlaceholder = true;
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Success;
+  String? errorMessage;
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,
+    controlFinishLoad: false,
+  );
+
+  // Refresh 刷新事件
+  Future onRefresh() async {
+    fetchWorkFlowList();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _needShowPlaceholder = true;
+    fetchWorkFlowList();
+  }
+
+  /// 获取服务器数据,成员考勤列表
+  Future fetchWorkFlowList() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    }
+
+    //获取到数据
+    var result = await _thRepository.fetchLabourReviewStatusView(
+      state.requestId,
+      cancelToken: cancelToken,
+    );
+
+    //处理数据
+    if (result.isSuccess) {
+      handleList(result.data?.records);
+      refreshController.finishRefresh(IndicatorResult.success);
+    } else {
+      errorMessage = result.errorMsg;
+      changeLoadingState(LoadState.State_Error);
+      refreshController.finishRefresh(IndicatorResult.fail);
+    }
+
+    //最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void handleList(List<LabourReviewStatusRecords>? list) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      state.datas.clear();
+      state.datas.addAll(list);
+      //更新状态
+      changeLoadingState(LoadState.State_Success);
+    } else {
+      //展示无数据的布局
+      state.datas.clear();
+      changeLoadingState(LoadState.State_Empty);
+    }
+  }
+
+  @override
+  void onReady() async {
+    super.onReady();
+    fetchWorkFlowList();
+  }
+
+  @override
+  void onClose() {
+    super.onClose();
+    state.datas.clear();
+  }
+}

+ 228 - 0
packages/cpt_th/lib/modules/labour/labour_review_workflow/labour_review_workflow_item.dart

@@ -0,0 +1,228 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:domain/entity/response/labour_review_status_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_text_view.dart';
+
+/*
+ * 用请求的审核工作流Item
+ */
+class LabourReviewWorkFlowItem extends StatelessWidget {
+  final int index;
+  final LabourReviewStatusRecords item;
+
+  LabourReviewWorkFlowItem({
+    required this.index,
+    required this.item,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      padding: const EdgeInsets.symmetric(vertical: 23, horizontal: 21),
+      margin: const EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 5),
+      decoration: BoxDecoration(
+        color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+        borderRadius: BorderRadius.circular(5), // 设置圆角
+      ),
+      child: Column(
+        mainAxisSize: MainAxisSize.max,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          //Node
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Node:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              MyTextView(
+                item.nodeName ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ),
+
+          // assigneeType
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Type:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //日期时间
+              MyTextView(
+                item.assigneeTypeShow ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // Designation
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Designation:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //人数
+              MyTextView(
+                item.designationShow ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // 状态
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Status:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //状态
+              MyTextView(
+                item.statusShow == null ? "" : item.statusShow!.tr,
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: "Approved" == item.statusShow
+                    ? ColorConstants.textGreen05DC82
+                    : "Rejected" == item.statusShow
+                    ? ColorConstants.textRedFF6262
+                    : "Recall" == item.statusShow
+                    ? ColorConstants.textYellowFFBB1B
+                    : ColorConstants.textBlue06D9FF,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // Operator
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Operator:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //发布状态
+              MyTextView(
+                item.auditName ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // 操作时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Audit Time:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //时间
+              MyTextView(
+                item.auditTime ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // 创建时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Created At:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //时间
+              MyTextView(
+                item.createdAt ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // Remark
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Remark:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //时间
+              MyTextView(
+                item.auditMark ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+        ],
+      ),
+    );
+  }
+}

+ 126 - 0
packages/cpt_th/lib/modules/labour/labour_template_add/labour_template_add_controller.dart

@@ -0,0 +1,126 @@
+import 'package:domain/entity/response/job_template_edit_index_entity.dart';
+import 'package:domain/repository/labour_repository.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/engine/notify/notify_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:plugin_platform/http/http_result.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/util.dart';
+
+import 'labour_template_add_state.dart';
+
+class LabourTemplateAddController extends GetxController with DioCancelableMixin {
+  final LabourRepository _labourRepository = Get.find();
+  final LabourTemplateAddState state = LabourTemplateAddState();
+
+  // 获取添加或者编辑的详情
+  void _fetchAddEditIndexDetail() async {
+    //获取到数据
+    Future<HttpResult<JobTemplateEditIndexEntity>> taskFuture;
+    if (Utils.isNotEmpty(state.templateId) && state.templateId != "0") {
+      //编辑
+      taskFuture = _labourRepository.fetchJobTemplateEditIndex(state.templateId, cancelToken: cancelToken);
+    } else {
+      //新增
+      taskFuture = _labourRepository.fetchJobTemplateAddIndex(cancelToken: cancelToken);
+    }
+
+    var result = await taskFuture;
+
+    //处理数据
+    if (result.isSuccess) {
+      state.indexEntity = result.data;
+
+      var templateNameController = state.formData['template_name']!['controller'];
+      var descController = state.formData['desc']!['controller'];
+      var noteController = state.formData['note']!['controller'];
+      templateNameController.text = state.indexEntity?.jobTitle ?? "";
+      descController.text = state.indexEntity?.description ?? "";
+      noteController.text = state.indexEntity?.note ?? "";
+
+      //默认赋值
+      state.optionLanguageList = state.indexEntity?.languageList.map((e) => e.txt!).toList() ?? [];
+      Log.d("当前语言的选项为:${state.optionLanguageList}");
+
+      state.selectedAgeList = state.indexEntity?.ageList.where((e) => e.checked == "checked").map((e) => e.value!).toList() ?? [];
+      Log.d("当前选中的年龄:${state.selectedAgeList}");
+      state.selectedLanguageList = state.indexEntity?.languageList.where((e) => e.checked == "checked").map((e) => e.value!).toList() ?? [];
+      Log.d("当前选中的语言:${state.selectedLanguageList}");
+
+      if (state.indexEntity?.sexList != null) {
+        state.gender = state.indexEntity!.sexList.firstWhere((e) => e.checked == "checked", orElse: () => JobTemplateEditIndexSexList(),).value;
+      } else {
+        state.gender = null;
+      }
+      Log.d("当前选中的性别:${state.gender}");
+
+      //刷新
+      update(["language","all"]);
+
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+  }
+
+  /// 提交
+  void doSubmit() async {
+    var templateNameController = state.formData['template_name']!['controller'];
+    var descController = state.formData['desc']!['controller'];
+    var noteController = state.formData['note']!['controller'];
+
+    String templateName = templateNameController.text.toString();
+    String desc = descController.text.toString();
+    String note = noteController.text.toString();
+
+    //只校验模版的名称
+    if (Utils.isEmpty(templateName)) {
+      ToastEngine.show("Enter Job Template Name".tr);
+      return;
+    }
+
+    Future<HttpResult> taskFuture;
+    if (Utils.isNotEmpty(state.templateId) && state.templateId != "0") {
+      taskFuture = _labourRepository.editLabourTemplateSubmit(
+        state.templateId,
+        templateName,
+        desc,
+        note,
+        state.selectedAgeList.join(","),
+        state.gender,
+        state.selectedLanguageList.join(","),
+        cancelToken: cancelToken,
+      );
+    } else {
+      taskFuture = _labourRepository.addLabourTemplateSubmit(
+        templateName,
+        desc,
+        note,
+        state.selectedAgeList.join(","),
+        state.gender,
+        state.selectedLanguageList.join(","),
+        cancelToken: cancelToken,
+      );
+    }
+
+    var result = await taskFuture;
+
+    //处理数据
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess("Successful".tr);
+
+      //根据类型刷新
+      state.cb?.call(state.templateId);
+
+      Get.back();
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    _fetchAddEditIndexDetail();
+  }
+}

+ 327 - 0
packages/cpt_th/lib/modules/labour/labour_template_add/labour_template_add_page.dart

@@ -0,0 +1,327 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:shared/utils/util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/no_shadow_scroll_behavior.dart';
+import 'package:widgets/shatter/custom_check_box.dart';
+import 'package:widgets/shatter/custom_radio_check.dart';
+import 'package:widgets/shatter/form_require_text.dart';
+import 'package:widgets/shatter/round_my_text_field.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'labour_template_add_controller.dart';
+
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+
+import 'labour_template_add_state.dart';
+
+/*
+ * 模板的添加与编辑
+ */
+class LabourTemplateAddPage extends BaseStatefulPage<LabourTemplateAddController> {
+  LabourTemplateAddPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance(
+    String templateId,
+    void Function(dynamic value)? cb,
+  ) {
+    return Get.start(RouterPath.labourTemplateAdd, arguments: {'templateId': templateId, 'cb': cb});
+  }
+
+  @override
+  LabourTemplateAddController createRawController() {
+    return LabourTemplateAddController();
+  }
+
+  @override
+  State<LabourTemplateAddPage> createState() => _LabourTemplateAddState();
+}
+
+class _LabourTemplateAddState extends BaseState<LabourTemplateAddPage, LabourTemplateAddController> {
+  late LabourTemplateAddState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+    state.templateId = Get.arguments['templateId'];
+    state.cb = Get.arguments['cb'] as void Function(dynamic)?;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return autoCtlGetBuilder(
+        id: "all",
+        builder: (controller) {
+          return Scaffold(
+            extendBodyBehindAppBar: true,
+            appBar: MyAppBar.appBar(context, Utils.isEmpty(state.templateId) ? "Create Template".tr : "Edit Template".tr),
+            body: SafeArea(
+        bottom: MediaQuery.of(context).padding.bottom > 38,
+              top: false,
+              child: Container(
+                width: double.infinity,
+                height: double.infinity,
+                padding: EdgeInsets.only(top: kToolbarHeight + ScreenUtil.getStatusBarH(context) + 1),
+                decoration: const BoxDecoration(
+                  gradient: LinearGradient(
+                    colors: [
+                      Color(0xFF091D44),
+                      Color(0xFF245A8A),
+                      Color(0xFF7F7CEC),
+                    ],
+                    begin: Alignment.topCenter,
+                    end: Alignment.bottomCenter,
+                  ),
+                ),
+                child: Scrollbar(
+                  child: ScrollConfiguration(
+                    behavior: NoShadowScrollBehavior(),
+                    child: SingleChildScrollView(
+                      scrollDirection: Axis.vertical,
+                      physics: const BouncingScrollPhysics(),
+                      child: Column(
+                        crossAxisAlignment: CrossAxisAlignment.start,
+                        children: [
+                          //模板名称
+                          FormRequireText(text: "Template Name".tr).marginOnly(left: 15, top: 19),
+
+                          CustomTextField(
+                            formKey: "template_name",
+                            formData: state.formData,
+                            height: 46,
+                            fontSize: 14,
+                            onSubmit: (key, value) {
+                              state.formData[key]!['focusNode'].unfocus();
+                              FocusScope.of(context).requestFocus(state.formData['desc']!['focusNode']);
+                            },
+                            marginTop: 10,
+                          ),
+
+                          //年龄
+                          Visibility(
+                            visible: state.indexEntity?.ageList.isNotEmpty == true,
+                            child: MyTextView(
+                              "Age".tr,
+                              textColor: Colors.white,
+                              fontSize: 14,
+                              isFontRegular: true,
+                              marginLeft: 15,
+                              marginTop: 15,
+                            ),
+                          ),
+
+                          // 年龄的多选
+                          Visibility(
+                            visible: state.indexEntity?.ageList.isNotEmpty == true,
+                            child: CustomCheckBox(
+                              options: state.indexEntity?.ageList.map((e) => e.txt!).toList() ?? [],
+                              onOptionsSelected: (selected) {
+                                // 转换选中的索引为对应的 value
+                                state.selectedAgeList = selected
+                                    .map((index) {
+                                      return state.indexEntity?.ageList[index].value; // 获取对应的 value
+                                    })
+                                    .whereType<String>()
+                                    .toList();
+                              },
+                              selectedOptions: state.indexEntity?.ageList.where((e) => e.checked == "checked").map((e) => e.txt!).toList() ?? [],
+                            ).marginOnly(left: 15, right: 15, top: 10),
+                          ),
+
+                          //性别
+                          Visibility(
+                            visible: state.indexEntity?.sexList.isNotEmpty == true,
+                            child: MyTextView(
+                              "Gender".tr,
+                              textColor: Colors.white,
+                              fontSize: 14,
+                              isFontRegular: true,
+                              marginLeft: 15,
+                              marginTop: 15,
+                            ),
+                          ),
+
+                          //性别单选
+                          Visibility(
+                            visible: state.indexEntity?.sexList.isNotEmpty == true,
+                            child: CustomRadioCheck(
+                              options: state.indexEntity?.sexList.map((e) => e.txt!).toList() ?? [],
+                              onOptionSelected: (index, text) {
+                                state.gender = state.indexEntity!.sexList[index].value;
+                              },
+                              selectedPosition: state.indexEntity?.sexList.indexWhere((e) => e.checked == "checked") ?? -1,
+                            ).marginOnly(left: 15, right: 15, top: 10),
+                          ),
+
+                          //语言
+                          MyTextView(
+                            "Language".tr,
+                            textColor: Colors.white,
+                            fontSize: 14,
+                            isFontRegular: true,
+                            marginLeft: 15,
+                            marginTop: 15,
+                          ),
+
+                          // 语言的多选
+                          GetBuilder<LabourTemplateAddController>(
+                            builder: (controller) {
+                              return CustomCheckBox(
+                                options: state.optionLanguageList,
+                                onOptionsSelected: (selected) {
+                                  // 转换选中的索引为对应的 value
+                                  state.selectedLanguageList = selected
+                                      .map((index) {
+                                        return state.indexEntity?.languageList[index].value; // 获取对应的 value
+                                      })
+                                      .whereType<String>()
+                                      .toList();
+                                },
+                                selectedOptions: state.indexEntity?.languageList.where((e) => e.checked == "checked").map((e) => e.txt!).toList() ?? [],
+                              );
+                            },
+                            id: "language",
+                            init: controller,
+                          ).marginOnly(left: 15, right: 15, top: 10),
+
+                          //模板详情
+                          MyTextView(
+                            "Description".tr,
+                            textColor: Colors.white,
+                            fontSize: 14,
+                            isFontRegular: true,
+                            marginLeft: 15,
+                            marginTop: 15,
+                          ),
+
+                          IgnoreKeyboardDismiss(
+                            child: Container(
+                              height: 130,
+                              margin: const EdgeInsets.only(left: 15, right: 15, top: 10),
+                              padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 15),
+                              decoration: BoxDecoration(
+                                color: const Color(0xFF4DCFF6).withOpacity(0.2),
+                                borderRadius: const BorderRadius.all(Radius.circular(5)),
+                              ),
+                              child: TextField(
+                                cursorColor: ColorConstants.white,
+                                cursorWidth: 1.5,
+                                autofocus: false,
+                                enabled: true,
+                                focusNode: state.formData["desc"]!['focusNode'],
+                                controller: state.formData["desc"]!['controller'],
+                                // 装饰
+                                decoration: InputDecoration(
+                                  isDense: true,
+                                  isCollapsed: true,
+                                  border: InputBorder.none,
+                                  hintText: state.formData["desc"]!['hintText'],
+                                  hintStyle: const TextStyle(
+                                    color: ColorConstants.textGrayAECAE5,
+                                    fontSize: 15.0,
+                                    fontWeight: FontWeight.w400,
+                                  ),
+                                ),
+                                style: const TextStyle(
+                                  color: ColorConstants.white,
+                                  fontSize: 15.0,
+                                  fontWeight: FontWeight.w400,
+                                ),
+                                // 键盘动作右下角图标
+                                textInputAction: TextInputAction.next,
+                                onSubmitted: (value) {
+                                  state.formData['desc']!['focusNode'].unfocus();
+                                  FocusScope.of(context).requestFocus(state.formData['note']!['focusNode']);
+                                },
+                              ),
+                            ),
+                          ),
+
+                          //备注
+                          MyTextView(
+                            "Note".tr,
+                            textColor: Colors.white,
+                            fontSize: 14,
+                            isFontRegular: true,
+                            marginLeft: 15,
+                            marginTop: 18,
+                          ),
+
+                          IgnoreKeyboardDismiss(
+                            child: Container(
+                              height: 130,
+                              margin: const EdgeInsets.only(left: 15, right: 15, top: 10),
+                              padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 15),
+                              decoration: BoxDecoration(
+                                color: const Color(0xFF4DCFF6).withOpacity(0.2),
+                                borderRadius: const BorderRadius.all(Radius.circular(5)),
+                              ),
+                              child: TextField(
+                                cursorColor: ColorConstants.white,
+                                cursorWidth: 1.5,
+                                autofocus: false,
+                                enabled: true,
+                                focusNode: state.formData["note"]!['focusNode'],
+                                controller: state.formData["note"]!['controller'],
+                                // 装饰
+                                decoration: InputDecoration(
+                                  isDense: true,
+                                  isCollapsed: true,
+                                  border: InputBorder.none,
+                                  hintText: state.formData["note"]!['hintText'],
+                                  hintStyle: const TextStyle(
+                                    color: ColorConstants.textGrayAECAE5,
+                                    fontSize: 14.0,
+                                    fontWeight: FontWeight.w400,
+                                  ),
+                                ),
+                                style: const TextStyle(
+                                  color: ColorConstants.white,
+                                  fontSize: 14.0,
+                                  fontWeight: FontWeight.w400,
+                                ),
+                                // 键盘动作右下角图标
+                                textInputAction: TextInputAction.next,
+                                onSubmitted: (value) {
+                                  state.formData['note']!['focusNode'].unfocus();
+                                },
+                              ),
+                            ),
+                          ),
+
+                          //提交按钮
+                          MyButton(
+                            type: ClickType.throttle,
+                            milliseconds: 500,
+                            onPressed: () {
+                              FocusScope.of(context).unfocus();
+                              controller.doSubmit();
+                            },
+                            text: "Submit".tr,
+                            textColor: ColorConstants.white,
+                            fontSize: 16,
+                            radius: 20,
+                            backgroundColor: ColorConstants.textYellowFFBB1B,
+                            fontWeight: FontWeight.w500,
+                          ).marginSymmetric(vertical: 25, horizontal: 15),
+                        ],
+                      ),
+                    ),
+                  ),
+                ),
+              ),
+            ),
+          );
+        });
+  }
+}

+ 45 - 0
packages/cpt_th/lib/modules/labour/labour_template_add/labour_template_add_state.dart

@@ -0,0 +1,45 @@
+import 'package:domain/entity/response/job_template_edit_index_entity.dart';
+import 'package:flutter/material.dart';
+import 'package:plugin_basic/basic_export.dart';
+
+class LabourTemplateAddState {
+  //表单的校验与数据
+  Map<String, Map<String, dynamic>> formData = {
+    'template_name': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter...'.tr,
+      'obsecure': false,
+    },
+    'desc': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter...'.tr,
+      'obsecure': false,
+    },
+    'note': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter...'.tr,
+      'obsecure': false,
+    },
+  };
+
+
+  final foodHygieneCertOption = ["Yes".tr, "No".tr];
+
+  String templateId = "";
+  void Function(dynamic value)? cb;
+
+  JobTemplateEditIndexEntity? indexEntity;  //新增或者编辑的详情
+
+
+  List<String> selectedAgeList = [];   //选中的 age 的 id
+  List<String> selectedLanguageList = [];   //选中的 language 的 id
+  String? gender;
+
+  List<String> optionLanguageList =[];  // language 的选项字符串
+}

+ 7 - 0
packages/cpt_th/lib/modules/labour/labour_template_list/add_edit_template.dart

@@ -0,0 +1,7 @@
+//编辑和新增页面返回的对象
+class AddEditTemplate{
+   bool isEdit = false;
+   String? templateId;
+
+   AddEditTemplate(this.isEdit, this.templateId);
+}

+ 175 - 0
packages/cpt_th/lib/modules/labour/labour_template_list/labour_template_item.dart

@@ -0,0 +1,175 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:domain/entity/response/job_template_s_g_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_text_view.dart';
+
+/*
+ * 用工请求的主页面列表Item
+ */
+class LabourTemplateItem extends StatelessWidget {
+  final int index;
+  final JobTemplateSGRows item;
+  final VoidCallback? onDeleteAction;
+  final VoidCallback? onEditAction;
+
+  LabourTemplateItem({
+    required this.index,
+    required this.item,
+    this.onDeleteAction,
+    this.onEditAction,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      padding: const EdgeInsets.symmetric(vertical: 23, horizontal: 21),
+      margin: const EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 5),
+      decoration: BoxDecoration(
+        color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+        borderRadius: BorderRadius.circular(5), // 设置圆角
+      ),
+      child: Column(
+        mainAxisSize: MainAxisSize.max,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          // 标题
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "${"Template Name".tr}:",
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+              MyTextView(
+                item.name ?? "-",
+                marginLeft: 5,
+                isFontMedium: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ),
+
+          // 创建人
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "${"Created By".tr}:",
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+              MyTextView(
+                item.createdBy ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 14),
+
+          // 创建时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Created At:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //状态
+              MyTextView(
+                item.createdAt ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 14),
+
+          // 更新时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "${"Updated At".tr}:",
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+              MyTextView(
+                item.updatedAt ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 14),
+
+          //按钮组
+          Visibility(
+            visible: item.actionList.isNotEmpty,
+            child: Row(
+              mainAxisSize: MainAxisSize.max,
+              mainAxisAlignment: MainAxisAlignment.end,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+                //编辑按钮
+                Visibility(
+                  visible: item.actionList.contains("edit"),
+                  child: MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      onEditAction?.call();
+                    },
+                    text: "Edit".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: ColorConstants.textGreen0AC074,
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 35,
+                  ).marginOnly(left: 12),
+                ),
+
+                //删除按钮
+                Visibility(
+                  visible: item.actionList.contains("delete"),
+                  child: MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      onDeleteAction?.call();
+                    },
+                    text: "Delete".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: ColorConstants.textRedFF6262,
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 35,
+                  ).marginOnly(left: 12),
+                ),
+              ],
+            ).marginOnly(top: 18, bottom: 2),
+
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 215 - 0
packages/cpt_th/lib/modules/labour/labour_template_list/labour_template_list_controller.dart

@@ -0,0 +1,215 @@
+import 'package:domain/entity/response/job_template_s_g_entity.dart';
+import 'package:domain/repository/labour_sg_repository.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/engine/dialog/dialog_engine.dart';
+import 'package:plugin_platform/engine/notify/notify_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:widgets/dialog/app_default_dialog.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../labour_template_add/labour_template_add_page.dart';
+import 'labour_template_list_state.dart';
+
+class LabourTemplateListController extends GetxController with DioCancelableMixin {
+  final LabourSGRepository _labourRepository = Get.find();
+  final LabourTemplateListState state = LabourTemplateListState();
+
+  var _curPage = 1;
+  var _needShowPlaceholder = true;
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Success;
+  String? errorMessage;
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,
+    controlFinishLoad: true,
+  );
+
+  // Refresh 刷新事件
+  Future onRefresh() async {
+    _curPage = 1;
+    fetchNotifyList();
+  }
+
+  // Refresh 加载事件
+  Future loadMore() async {
+    _curPage++;
+    fetchNotifyList();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _curPage = 1;
+    _needShowPlaceholder = true;
+    fetchNotifyList();
+  }
+
+  /// 获取服务器数据,通知消息列表
+  Future fetchNotifyList() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    }
+
+    var listResult = await _labourRepository.fetchJobTemplateList(state.keyword, curPage: _curPage, cancelToken: cancelToken);
+
+    // 处理数据
+    if (listResult.isSuccess) {
+      handleList(listResult.data?.rows);
+    } else {
+      errorMessage = listResult.errorMsg;
+      changeLoadingState(LoadState.State_Error);
+    }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void handleList(List<JobTemplateSGRows>? list) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      if (_curPage == 1) {
+        //刷新的方式
+        state.datas.clear();
+        state.datas.addAll(list);
+        refreshController.finishRefresh();
+
+        //更新展示的状态
+        changeLoadingState(LoadState.State_Success);
+      } else {
+        //加载更多
+        state.datas.addAll(list);
+        refreshController.finishLoad();
+        update();
+      }
+    } else {
+      if (_curPage == 1) {
+        //展示无数据的布局
+        state.datas.clear();
+        changeLoadingState(LoadState.State_Empty);
+        refreshController.finishRefresh();
+      } else {
+        //展示加载完成,没有更多数据了
+        refreshController.finishLoad(IndicatorResult.noMore);
+      }
+    }
+  }
+
+  // 执行搜索
+  void doSearch(String keyword) {
+    state.keyword = keyword;
+    //赋值之后刷新
+    refreshController.callRefresh();
+  }
+
+  // 清空搜索条件
+  void resetFiltering() {
+    state.keyword = "";
+    state.searchController.text = "";
+
+    //赋值之后刷新
+    refreshController.callRefresh();
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    fetchNotifyList();
+  }
+
+  @override
+  void onClose() {
+    state.datas.clear();
+    super.onClose();
+  }
+
+  //去添加模板的页面
+  void gotoTemplateAddPage() {
+    LabourTemplateAddPage.startInstance("", (result) {
+      if (result is String) {
+        refreshController.callRefresh();
+      }
+    });
+  }
+
+  //去编辑的页面
+  void gotoEditPage(String templateId) {
+    LabourTemplateAddPage.startInstance(templateId, (result) {
+      if (result is String) {
+        fetchItemByIdAndRefreshItem(templateId);
+      }
+    });
+  }
+
+  //删除工作模板
+  void deleteJobTitle(int index) {
+    DialogEngine.show(
+        widget: AppDefaultDialog(
+      title: "Confirmation".tr,
+      message: "Are you sure you want to delete this job template?".tr,
+      confirmAction: () {
+        _requestDeactivate(index);
+      },
+    ));
+  }
+
+  // 请求接口删除JobTitle
+  void _requestDeactivate(int index) async {
+    final item = state.datas[index];
+    var result = await _labourRepository.deleteJobTemplateSubmit(item.id.toString(), cancelToken: cancelToken);
+
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess("Successful".tr);
+
+      if (state.datas.length <= 1) {
+        refreshController.callRefresh();
+      } else {
+        state.datas.removeAt(index);
+        update();
+      }
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+  }
+
+  /// 根据ID获取Item对象,用于刷新
+  void fetchItemByIdAndRefreshItem(String ids) async {
+    var result = await _labourRepository.fetchJobTemplateListByIds(
+      ids,
+      cancelToken: cancelToken,
+    );
+
+    //处理数据
+    if (result.isSuccess) {
+      var data = result.data;
+      if (data != null && data.rows.isNotEmpty) {
+        List<JobTemplateSGRows> newItem = data.rows;
+
+        // 创建一个 Map 来加速查找
+        Map<int, JobTemplateSGRows> newItemMap = {for (var item in newItem) item.id: item};
+
+        // 遍历 state.datas 进行替换
+        for (int i = 0; i < state.datas.length; i++) {
+          if (newItemMap.containsKey(state.datas[i].id)) {
+            state.datas[i] = newItemMap[state.datas[i].id]!;
+          }
+        }
+
+        //刷新
+        update();
+      }
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+  }
+}

+ 155 - 0
packages/cpt_th/lib/modules/labour/labour_template_list/labour_template_list_page.dart

@@ -0,0 +1,155 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'labour_template_item.dart';
+import '../labour_template_list/labour_template_list_controller.dart';
+
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/my_appbar.dart';
+
+import '../labour_template_list/labour_template_list_state.dart';
+
+/// 新加坡的模板列表页面
+class LabourTemplateListPage extends BaseStatefulPage<LabourTemplateListController> {
+  LabourTemplateListPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance() {
+    return Get.start(RouterPath.labourTemplateList);
+  }
+
+  @override
+  LabourTemplateListController createRawController() {
+    return LabourTemplateListController();
+  }
+
+  @override
+  State<LabourTemplateListPage> createState() => _LabourTemplateListState();
+
+}
+
+class _LabourTemplateListState extends BaseState<LabourTemplateListPage, LabourTemplateListController> {
+
+  late LabourTemplateListState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return SafeArea(
+        bottom: MediaQuery.of(context).padding.bottom > 38,
+        top: false,
+        child: Container(
+          width: double.infinity,
+          height: double.infinity,
+          padding: EdgeInsets.only(top: ScreenUtil.getStatusBarH(context)),
+          decoration: const BoxDecoration(
+            gradient: LinearGradient(
+              colors: [
+                Color(0xFF091D44),
+                Color(0xFF245A8A),
+                Color(0xFF7F7CEC),
+              ],
+              begin: Alignment.topCenter,
+              end: Alignment.bottomCenter,
+            ),
+          ),
+          child: Column(
+            children: [
+              MyAppBar.searchTitleBar(
+                context,
+                value: state.keyword,
+                hintText: 'Template Name'.tr,
+                controller: state.searchController,
+                onSearch: (keyword) {
+                  controller.doSearch(keyword);
+                },
+                actions: [
+                  //重置按钮
+                  MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      controller.resetFiltering();
+                    },
+                    text: "Reset".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#2BA9F9", opacity: 0.5),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 35,
+                  ).marginOnly(right: 15),
+                ],
+              ),
+
+              Row(
+                children: [
+                  MyButton(
+                    type: ClickType.throttle,
+                    milliseconds: 500,
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      controller.gotoTemplateAddPage();
+                    },
+                    text: "Add New".tr,
+                    textColor: ColorConstants.white,
+                    fontSize: 16,
+                    radius: 20,
+                    backgroundColor: hexToColor("#FFBB1B"),
+                    fontWeight: FontWeight.w500,
+                  ).expanded(),
+                ],
+              ).marginOnly(left: 15,right: 15,top: 15,bottom: 10),
+
+              //底部的列表
+              EasyRefresh(
+                controller: controller.refreshController,
+                onRefresh: controller.onRefresh,
+                onLoad: controller.loadMore,
+                child: LoadStateLayout(
+                  state: controller.loadingState,
+                  errorMessage: controller.errorMessage,
+                  errorRetry: () {
+                    controller.retryRequest();
+                  },
+                  successSliverWidget: [
+                    SliverList(
+                        delegate: SliverChildBuilderDelegate(
+                              (context, index) {
+                            return LabourTemplateItem(
+                              index: index,
+                              item: state.datas[index],
+                              onEditAction: () {
+                                controller.gotoEditPage(state.datas[index].id.toString());
+                              },
+                              onDeleteAction: () {
+                                controller.deleteJobTitle(index);
+                              },
+                            );
+                          },
+                          childCount: state.datas.length,
+                        ))
+                  ],
+                ),
+              ).expanded(),
+            ],
+          ),
+        ),
+      );
+    });
+  }
+}
+

+ 13 - 0
packages/cpt_th/lib/modules/labour/labour_template_list/labour_template_list_state.dart

@@ -0,0 +1,13 @@
+import 'package:domain/entity/response/job_template_s_g_entity.dart';
+import 'package:flutter/material.dart';
+
+class LabourTemplateListState {
+
+  //筛选条件
+  final TextEditingController searchController = TextEditingController();
+  String keyword = "";
+
+  //页面的列表数据
+  List<JobTemplateSGRows> datas = [];
+
+}

+ 122 - 0
packages/cpt_th/lib/modules/labour_er/labour_request_er/labour_request_item.dart

@@ -0,0 +1,122 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:domain/entity/response/labour_request_e_r_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_text_view.dart';
+
+/*
+ * 用工请求的主页面列表Item
+ */
+class LabourRequestItem extends StatelessWidget {
+  final int index;
+  final String? selectedDate;
+  final List<LabourRequestERCountList>? countList;
+  final LabourRequestERSourceList item;
+  final void Function(String? date)? gotoJobAction;
+
+  LabourRequestItem({
+    required this.index,
+    required this.selectedDate,
+    required this.countList,
+    required this.item,
+    this.gotoJobAction,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    final countMap = countList?.fold<Map<String, LabourRequestERCountList>>({}, (map, item) {
+        map[item.date??""] = item;
+        return map;
+      },
+    ) ?? {};
+
+    return Container(
+      padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 21),
+      margin: const EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 5),
+      decoration: BoxDecoration(
+        color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+        borderRadius: BorderRadius.circular(5), // 设置圆角
+      ),
+      child: Column(
+        mainAxisSize: MainAxisSize.max,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Row(
+            children: [
+              //日期
+              MyTextView(
+                'Date'.tr,
+                fontSize: 15,
+                textColor: Colors.white,
+                paddingTop: 15,
+                textAlign: TextAlign.center,
+                paddingBottom: 15,
+                isFontBold: true,
+              ).expanded(flex: 55),
+
+              //类型
+              MyTextView(
+                item.agencyName ?? "-",
+                fontSize: 15,
+                textColor: Colors.white,
+                isFontBold: true,
+                textAlign: TextAlign.center,
+                paddingTop: 15,
+                paddingBottom: 15,
+              ).expanded(flex: 45),
+            ],
+          ),
+
+          const Divider(height: 0.5, color: ColorConstants.dividerItem).marginOnly(bottom: 6),
+
+          //在这里根据Item内部的数组转换为对应的Widget
+          if (item.requestList != null && item.requestList!.isNotEmpty)
+            ...item.requestList!.map((e) {
+              final countItem = countMap[e.date]; // 使用 Map 进行查找
+              return Padding(
+                padding: const EdgeInsets.symmetric(vertical: 5), // 每项的间距
+                child: Row(
+                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                  children: [
+                    MyTextView(
+                      "[${countItem?.week ?? '-'}]${e.date}(${countItem?.num ?? '-'})",
+                      fontSize: 15,
+                      textColor: selectedDate == e.date ? ColorConstants.textGreen00FB92 : Colors.white,
+                      paddingTop: 6,
+                      textAlign: TextAlign.center,
+                      paddingBottom: 6,
+                      isFontRegular: true,
+                    ).expanded(flex: 55),
+
+                    //类型
+                    MyTextView(
+                      e.num ?? "-",
+                      fontSize: 15,
+                      textColor: ColorConstants.textYellowFFBB1B,
+                      isFontRegular: true,
+                      textAlign: TextAlign.center,
+                      paddingTop: 6,
+                      paddingBottom: 6,
+                      onClick: (){
+                        gotoJobAction?.call(e.date);
+                      },
+                      //下划线的设置
+                      textDecoration: TextDecoration.underline,
+                      decorationColor: ColorConstants.textYellowFFBB1B,
+                      decorationThickness: 2.0,
+                      decorationStyle: TextDecorationStyle.solid, // 可选,设置下划线的样式
+                    ).expanded(flex: 45),
+                  ],
+                ),
+              );
+            }).toList(),
+
+          const SizedBox(height: 10),
+        ],
+      ),
+    );
+  }
+}

+ 150 - 0
packages/cpt_th/lib/modules/labour_er/labour_request_er/labour_request_list_controller.dart

@@ -0,0 +1,150 @@
+import 'package:cpt_th/modules/job_er/job_list_er/job_list_er_page.dart';
+import 'package:cpt_th/modules/labour_er/labour_request_er_add/labour_request_er_add_page.dart';
+import 'package:domain/entity/response/labour_request_e_r_entity.dart';
+import 'package:domain/repository/th_er_repository.dart';
+import 'package:domain/entity/response/labour_request_list_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/constants/app_constant.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:shared/utils/event_bus.dart';
+import 'package:shared/utils/util.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/picker/date_picker_util.dart';
+import 'package:widgets/widget_export.dart';
+import 'labour_request_list_state.dart';
+
+class LabourRequestListController extends GetxController with DioCancelableMixin {
+  final THERRepository _erRepository = Get.find();
+  final LabourRequestListState state = LabourRequestListState();
+
+  var _needShowPlaceholder = true;
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Success;
+  String? errorMessage;
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,
+    controlFinishLoad: false,
+  );
+
+  // Refresh 刷新事件
+  Future onRefresh() async {
+    fetchLabourRequestList();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _needShowPlaceholder = true;
+    fetchLabourRequestList();
+  }
+
+  /// 获取列表数据
+  Future fetchLabourRequestList() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    }
+
+    final listResult = await _erRepository.fetchERLabourRequest(
+      DateTimeUtils.formatDate(state.selectedDate, format: "yyyy-MM-dd"),
+      cancelToken: cancelToken,
+    );
+
+    // 处理数据
+    if (listResult.isSuccess) {
+      state.labourRequestEREntity = listResult.data;
+      handleList(listResult.data?.sourceList);
+    } else {
+      errorMessage = listResult.errorMsg;
+      changeLoadingState(LoadState.State_Error);
+    }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void handleList(List<LabourRequestERSourceList>? list) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+
+      //刷新的方式
+      state.datas.clear();
+      state.datas.addAll(list);
+      refreshController.finishRefresh();
+
+      //更新展示的状态
+      changeLoadingState(LoadState.State_Success);
+    } else {
+      //展示无数据的布局
+      state.datas.clear();
+      changeLoadingState(LoadState.State_Empty);
+      refreshController.finishRefresh();
+    }
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    fetchLabourRequestList();
+
+    registerEventBus();
+  }
+
+  @override
+  void onClose() {
+    unregisterEventBus();
+    state.datas.clear();
+    super.onClose();
+  }
+
+  // EventBus 的事件接收
+  Subscription? subscribe;
+
+  void registerEventBus() {
+    subscribe = bus.on(AppConstant.eventLabourRequestRefresh, (arg) {
+        refreshController.callRefresh();
+    });
+  }
+
+  void unregisterEventBus() {
+    bus.off(AppConstant.eventLabourRequestRefresh, subscribe);
+  }
+
+  //跳转到添加页面
+  void gotoAddLabourPage() {
+    LabourRequestERAddPage.startInstance(0, "", (result) {
+      //添加成功之后的刷新
+      refreshController.callRefresh();
+    });
+  }
+
+  // 去工作列表的指定日期
+  void gotoJobPage(String? date) {
+    JobListERPage.startInstance(date, (result) {
+      refreshController.callRefresh();
+    });
+  }
+
+  void showDatePicker() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.selectedDate,
+      mode: CupertinoDatePickerMode.date,
+      onDateTimeChanged: (date) {
+        state.selectedDate = date;
+        update();
+        refreshController.callRefresh();
+      },
+      title: "Select Date".tr,
+    );
+  }
+}

+ 158 - 0
packages/cpt_th/lib/modules/labour_er/labour_request_er/labour_request_list_page.dart

@@ -0,0 +1,158 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'labour_request_item.dart';
+import 'labour_request_list_controller.dart';
+import 'labour_request_list_state.dart';
+
+/*
+ * 用工请求的主页列表
+ */
+class LabourRequestERListPage extends BaseStatefulPage<LabourRequestListController> {
+  LabourRequestERListPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance() {
+    return Get.start(RouterPath.THLabourRequestER);
+  }
+
+  @override
+  LabourRequestListController createRawController() {
+    return LabourRequestListController();
+  }
+
+  @override
+  State<LabourRequestERListPage> createState() => _LabourRequestListState();
+}
+
+class _LabourRequestListState extends BaseState<LabourRequestERListPage, LabourRequestListController> {
+  late LabourRequestListState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return SafeArea(
+        bottom: MediaQuery.of(context).padding.bottom > 38,
+        top: false,
+        child: Container(
+          width: double.infinity,
+          height: double.infinity,
+          padding: EdgeInsets.only(top: ScreenUtil.getStatusBarH(context)),
+          decoration: const BoxDecoration(
+            gradient: LinearGradient(
+              colors: [
+                Color(0xFF091D44),
+                Color(0xFF245A8A),
+                Color(0xFF7F7CEC),
+              ],
+              begin: Alignment.topCenter,
+              end: Alignment.bottomCenter,
+            ),
+          ),
+          child: Column(
+            children: [
+              MyAppBar.titleBar(context, 'Labour Request'.tr),
+
+              //日期的筛选
+              Container(
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(left: 15, right: 15, top: 15),
+                height: 42,
+                decoration: BoxDecoration(
+                  color: const Color(0xFF4DCFF6).withOpacity(0.2),
+                  borderRadius: const BorderRadius.all(Radius.circular(20)),
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    const MyAssetImage(Assets.cptJobPickDateIcon, width: 14, height: 14),
+                    MyTextView(
+                      state.selectedDate == null ? "" : DateTimeUtils.formatDate(state.selectedDate, format: "yyyy-MM-dd"),
+                      fontSize: 14,
+                      hint: "Select Date".tr,
+                      marginLeft: 12,
+                      textHintColor: ColorConstants.textGrayAECAE5,
+                      isFontMedium: true,
+                      textColor: ColorConstants.white,
+                    ).expanded(),
+                  ],
+                ),
+              ).onTap(() {
+                controller.showDatePicker();
+              }),
+
+              // 添加按钮
+              MyButton(
+                type: ClickType.throttle,
+                milliseconds: 500,
+                onPressed: () {
+                  FocusScope.of(context).unfocus();
+                  controller.gotoAddLabourPage();
+                },
+                text: "Create New Job Request".tr,
+                textColor: ColorConstants.white,
+                fontSize: 16,
+                radius: 20,
+                backgroundColor: hexToColor("#FFBB1B"),
+                fontWeight: FontWeight.w500,
+              ).marginSymmetric(horizontal: 15, vertical: 15),
+
+              //底部的列表
+              EasyRefresh(
+                controller: controller.refreshController,
+                onRefresh: controller.onRefresh,
+                child: LoadStateLayout(
+                  state: controller.loadingState,
+                  errorMessage: controller.errorMessage,
+                  errorRetry: () {
+                    controller.retryRequest();
+                  },
+                  successSliverWidget: [
+                    SliverList(
+                        delegate: SliverChildBuilderDelegate(
+                      (context, index) {
+                        return LabourRequestItem(
+                          index: index,
+                          selectedDate: DateTimeUtils.formatDate(state.selectedDate, format: "yyyy-MM-dd"),
+                          countList: state.labourRequestEREntity?.countList,
+                          item: state.datas[index],
+                          gotoJobAction: (date) {
+                            controller.gotoJobPage(date);
+                          },
+                        );
+                      },
+                      childCount: state.datas.length,
+                    ))
+                  ],
+                ),
+              ).expanded(),
+            ],
+          ),
+        ),
+      );
+    });
+  }
+}

+ 10 - 0
packages/cpt_th/lib/modules/labour_er/labour_request_er/labour_request_list_state.dart

@@ -0,0 +1,10 @@
+import 'package:domain/entity/response/labour_request_e_r_entity.dart';
+
+class LabourRequestListState {
+
+  DateTime? selectedDate = DateTime.now();  //选择的日期,默认是今天
+
+  LabourRequestEREntity? labourRequestEREntity;
+  //页面的列表数据
+  List<LabourRequestERSourceList> datas = [];
+}

+ 311 - 0
packages/cpt_th/lib/modules/labour_er/labour_request_er_add/labour_request_er_add_controller.dart

@@ -0,0 +1,311 @@
+import 'package:domain/repository/labour_sg_repository.dart';
+import 'package:domain/repository/th_er_repository.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/engine/loading/loading_engine.dart';
+import 'package:plugin_platform/engine/notify/notify_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:plugin_platform/http/http_result.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/util.dart';
+import 'package:widgets/picker/date_picker_util.dart';
+import 'package:widgets/picker/option_pick_util.dart';
+
+import 'labour_request_er_add_state.dart';
+
+class LabourRequestERAddController extends GetxController with DioCancelableMixin {
+  final THERRepository _erRepository = Get.find();
+  final LabourRequestERAddState state = LabourRequestERAddState();
+
+  // 获取添加选项数据
+  void fetchLabourRequestEditDetail() async {
+    if (Utils.isNotEmpty(state.jobId)) {
+      //编辑
+      var result = await _erRepository.fetchERJobListDetail(
+        jobId: state.jobId!,
+        cancelToken: cancelToken,
+      );
+
+      if (result.isSuccess) {
+        state.editDetailEntity = result.data;
+        var needMallController = state.formData['need_male']!['controller'];
+        var needFemaleController = state.formData['need_female']!['controller'];
+        var needNumController = state.formData['need_no']!['controller'];
+        var remarkController = state.formData['remark']!['controller'];
+        var eventNameController = state.formData['event_name']!['controller'];
+        var eventTypeController = state.formData['event_type']!['controller'];
+
+        if (state.editDetailEntity?.job?.sexLimit == '1') {
+          state.genderOptionType = 1;
+          needMallController.text = state.editDetailEntity?.job?.maleLimit?.toString();
+          needFemaleController.text = state.editDetailEntity?.job?.femaleLimit?.toString();
+          needNumController.text = '';
+        } else {
+          state.genderOptionType = 0;
+          needMallController.text = '';
+          needFemaleController.text = '';
+          needNumController.text = state.editDetailEntity?.job?.hiringNum?.toString();
+        }
+
+        state.selectTitleName = state.editDetailEntity?.job?.jobTitle;
+        state.selectTitleId = state.editDetailEntity?.job?.jobTitleId;
+
+        state.selectOutletName = state.editDetailEntity?.job?.outletName;
+        state.selectOutletId = state.editDetailEntity?.job?.outletId;
+
+        state.selectedStartTime = DateTimeUtils.getDateTime(state.editDetailEntity!.startTime!);
+        state.selectedEndTime = DateTimeUtils.getDateTime(state.editDetailEntity!.endTime!);
+
+        remarkController.text = state.editDetailEntity?.job?.description ?? '';
+        eventNameController.text = state.editDetailEntity?.job?.eventName ?? '';
+        eventTypeController.text = state.editDetailEntity?.job?.eventType ?? '';
+      } else {
+        ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+      }
+    } else {
+      //新增
+      var result = await _erRepository.fetchERLabourRequestAddOption(
+        cancelToken: cancelToken,
+      );
+
+      if (result.isSuccess) {
+        state.editDetailEntity = result.data;
+      } else {
+        ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+      }
+    }
+
+    update();
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    fetchLabourRequestEditDetail();
+  }
+
+  //选择开始时间
+  void pickStartTime() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.selectedStartTime,
+      mode: CupertinoDatePickerMode.dateAndTime,
+      onDateTimeChanged: (date) {
+        state.selectedStartTime = date;
+        update();
+      },
+      title: "Start Time".tr,
+    );
+  }
+
+  // 选择结束时间
+  void pickEndTime() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.selectedEndTime ?? state.selectedStartTime,
+      mode: CupertinoDatePickerMode.dateAndTime,
+      onDateTimeChanged: (date) {
+        state.selectedEndTime = date;
+        update();
+      },
+      title: "End Time".tr,
+    );
+  }
+
+  // 提交LabourRequest表单
+  void doEditSubmit() async {
+    var maleNoController = state.formData['need_male']!['controller'];
+    var femaleNoController = state.formData['need_female']!['controller'];
+    var needNoController = state.formData['need_no']!['controller'];
+    var remarkController = state.formData['remark']!['controller'];
+    var eventNameController = state.formData['event_name']!['controller'];
+    var eventTypeController = state.formData['event_type']!['controller'];
+
+    String maleNo = maleNoController.text.toString();
+    String femaleNo = femaleNoController.text.toString();
+    String needNo = needNoController.text.toString();
+    String remark = remarkController.text.toString();
+    String eventName = eventNameController.text.toString();
+    String evenType = eventTypeController.text.toString();
+
+    if (state.selectTitleId == null) {
+      ToastEngine.show("Select Job Title".tr);
+      return;
+    }
+
+    if (state.selectedStartTime == null) {
+      ToastEngine.show("Select Job Start Time".tr);
+      return;
+    }
+
+    if (state.selectedEndTime == null) {
+      ToastEngine.show("Select Job End Time".tr);
+      return;
+    }
+
+    if (Utils.isEmpty(state.selectOutletId)) {
+      ToastEngine.show("Choose Outlet".tr);
+      return;
+    }
+
+    if (state.genderOptionType == 0) {
+      if (Utils.isEmpty(needNo)) {
+        ToastEngine.show("Enter No. of Staff".tr);
+        return;
+      }
+    } else {
+      if (Utils.isEmpty(maleNo) || Utils.isEmpty(femaleNo)) {
+        ToastEngine.show("Enter No. of Staff of The Corresponding Gender".tr);
+        return;
+      }
+    }
+
+    HttpResult result;
+    if (state.pageType == 1 && state.jobId != null) {
+      //编辑
+      result = await _erRepository.editLabourRequestERSubmit(
+        jobId: state.jobId!,
+        jobTitleId: state.selectTitleId!,
+        startTime: DateTimeUtils.formatDate(state.selectedStartTime),
+        endTime: DateTimeUtils.formatDate(state.selectedEndTime),
+        outletId: state.selectOutletId!,
+        sexLimit: state.genderOptionType.toString(),
+        maleLimit: maleNo,
+        femaleLimit: femaleNo,
+        needNum: needNo,
+        eventName: eventName,
+        eventType: evenType,
+        remark: remark,
+        cancelToken: cancelToken,
+      );
+    } else {
+      //新增
+      result = await _erRepository.addLabourRequestERSubmit(
+        jobTitleId: state.selectTitleId!,
+        startTime: DateTimeUtils.formatDate(state.selectedStartTime),
+        endTime: DateTimeUtils.formatDate(state.selectedEndTime),
+        outletId: state.selectOutletId!,
+        repeatStart: DateTimeUtils.formatDate(state.selectedRepeatStartTime, format: 'yyyy-MM-dd'),
+        repeatEnd: DateTimeUtils.formatDate(state.selectedRepeatEndTime, format: 'yyyy-MM-dd'),
+        sexLimit: state.genderOptionType.toString(),
+        maleLimit: maleNo,
+        femaleLimit: femaleNo,
+        needNum: needNo,
+        eventName: eventName,
+        eventType: evenType,
+        remark: remark,
+        cancelToken: cancelToken,
+      );
+    }
+
+    //处理数据
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess("Successful".tr);
+
+      //根据类型刷新
+      state.cb?.call(state.jobId);
+
+      Get.back();
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+  }
+
+  //选择性别限制类型
+  void pickLimitType() {
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: state.genderOptions,
+      initialSelectIndex: state.genderOptionType,
+      onPickerChanged: (_, index) {
+        state.genderOptionType = index;
+        update();
+      },
+    );
+  }
+
+  // 选择工作模板标题
+  void pickJobTitle() {
+    if (state.editDetailEntity == null) {
+      return;
+    }
+
+    int selectedIndex;
+    if (state.selectTitleId == null) {
+      selectedIndex = 0;
+    } else {
+      selectedIndex = state.editDetailEntity!.titleList.indexWhere((bean) => bean.value.toString() == state.selectTitleId);
+    }
+
+    if (selectedIndex < 0) {
+      selectedIndex = 0;
+    } else {
+      selectedIndex++;
+    }
+
+    List<String> list = [];
+    list.addAll(state.editDetailEntity!.titleList.map((e) => e.txt!).toList(growable: false));
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: list,
+      initialSelectIndex: selectedIndex,
+      onPickerChanged: (_, index) {
+        state.selectTitleId = state.editDetailEntity!.titleList[index].value!.toString();
+        state.selectTitleName = state.editDetailEntity!.titleList[index].txt!.toString();
+        update();
+      },
+    );
+  }
+
+  //选择部门
+  void pickOutlet() {
+    if (state.editDetailEntity == null) {
+      return;
+    }
+
+    int selectedIndex;
+    if (state.selectOutletId == null) {
+      selectedIndex = 0;
+    } else {
+      selectedIndex = state.editDetailEntity!.outletList.indexWhere((department) => department.value.toString() == state.selectOutletId);
+    }
+
+    if (selectedIndex < 0) {
+      selectedIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: state.editDetailEntity!.outletList.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedIndex,
+      onPickerChanged: (_, index) {
+        state.selectOutletId = state.editDetailEntity!.outletList[index].value!.toString();
+        state.selectOutletName = state.editDetailEntity!.outletList[index].txt!.toString();
+        update();
+      },
+    );
+  }
+
+  void pickRepeatStartTime() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.selectedRepeatStartTime,
+      mode: CupertinoDatePickerMode.date,
+      onDateTimeChanged: (date) {
+        state.selectedRepeatStartTime = date;
+        update();
+      },
+      title: "Repeat Start Time".tr,
+    );
+  }
+
+  void pickRepeatEndTime() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.selectedRepeatEndTime ?? state.selectedRepeatStartTime,
+      mode: CupertinoDatePickerMode.date,
+      minimumDate: state.selectedRepeatStartTime,
+      onDateTimeChanged: (date) {
+        state.selectedRepeatEndTime = date;
+        update();
+      },
+      title: "Repeat End Time".tr,
+    );
+  }
+}

+ 599 - 0
packages/cpt_th/lib/modules/labour_er/labour_request_er_add/labour_request_er_add_page.dart

@@ -0,0 +1,599 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter/widgets.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/no_shadow_scroll_behavior.dart';
+import 'package:widgets/shatter/form_require_text.dart';
+import 'package:widgets/shatter/round_my_text_field.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'labour_request_er_add_controller.dart';
+import 'labour_request_er_add_state.dart';
+
+class LabourRequestERAddPage extends BaseStatefulPage<LabourRequestERAddController> {
+  LabourRequestERAddPage({Key? key}) : super(key: key);
+
+  //启动当前页面,pageType 0 是新增  1是编辑  2是详情
+  static void startInstance(int pageType, String? appliedId, void Function(dynamic value)? cb) {
+    return Get.start(RouterPath.THLabourRequestERAdd, arguments: {'pageType': pageType, 'appliedId': appliedId, 'cb': cb});
+  }
+
+  @override
+  LabourRequestERAddController createRawController() {
+    return LabourRequestERAddController();
+  }
+
+  @override
+  State<LabourRequestERAddPage> createState() => _LabourRequestAddState();
+}
+
+class _LabourRequestAddState extends BaseState<LabourRequestERAddPage, LabourRequestERAddController> {
+  late LabourRequestERAddState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+    state.pageType = Get.arguments['pageType'];
+    state.jobId = Get.arguments['appliedId'];
+    state.cb = Get.arguments['cb'] as void Function(dynamic)?;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return Scaffold(
+        extendBodyBehindAppBar: true,
+        appBar: MyAppBar.appBar(context, state.pageType == 2 ? "Edit Labour Requisition".tr : "Labour Requisition".tr),
+        body: SafeArea(
+          bottom: MediaQuery.of(context).padding.bottom > 38,
+          top: false,
+          child: Container(
+            width: double.infinity,
+            height: double.infinity,
+            padding: EdgeInsets.only(top: kToolbarHeight + ScreenUtil.getStatusBarH(context) + 1),
+            decoration: const BoxDecoration(
+              gradient: LinearGradient(
+                colors: [
+                  Color(0xFF091D44),
+                  Color(0xFF245A8A),
+                  Color(0xFF7F7CEC),
+                ],
+                begin: Alignment.topCenter,
+                end: Alignment.bottomCenter,
+              ),
+            ),
+            child: Scrollbar(
+              child: ScrollConfiguration(
+                behavior: NoShadowScrollBehavior(),
+                child: SingleChildScrollView(
+                  scrollDirection: Axis.vertical,
+                  physics: const BouncingScrollPhysics(),
+                  child: Column(
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    children: [
+                      //工作标题,选择模板
+                      FormRequireText(
+                        text: "Job Title".tr,
+                      ).marginOnly(top: 15),
+
+                      //工作标题
+                      Container(
+                        padding: const EdgeInsets.only(left: 16, right: 10),
+                        margin: const EdgeInsets.only(top: 10),
+                        height: 45,
+                        decoration: BoxDecoration(
+                          color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                          borderRadius: const BorderRadius.all(Radius.circular(5)),
+                        ),
+                        child: Row(
+                          mainAxisSize: MainAxisSize.max,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          mainAxisAlignment: MainAxisAlignment.start,
+                          children: [
+                            MyTextView(
+                              state.selectTitleName ?? "",
+                              fontSize: 14,
+                              hint: "Choose Job Title".tr,
+                              textHintColor: ColorConstants.textGrayAECAE5,
+                              isFontMedium: true,
+                              textColor: ColorConstants.white,
+                            ).expanded(),
+
+                            //下拉选图标
+                            Visibility(
+                              visible: state.pageType != 2,
+                              child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                            ),
+                          ],
+                        ),
+                      ).onTap(() {
+                        FocusScope.of(context).unfocus();
+                        if (state.pageType != 2) controller.pickJobTitle();
+                      }),
+
+                      //选择工作时间
+                      FormRequireText(
+                        text: "Job Time".tr,
+                      ).marginOnly(top: 15),
+
+                      Row(
+                        mainAxisSize: MainAxisSize.max,
+                        crossAxisAlignment: CrossAxisAlignment.center,
+                        mainAxisAlignment: MainAxisAlignment.start,
+                        children: [
+                          //选择工作开始时间
+                          Expanded(
+                            child: Container(
+                              padding: const EdgeInsets.only(left: 16, right: 10),
+                              height: 45,
+                              decoration: BoxDecoration(
+                                color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                                borderRadius: const BorderRadius.all(Radius.circular(5)),
+                              ),
+                              child: Row(
+                                mainAxisSize: MainAxisSize.max,
+                                crossAxisAlignment: CrossAxisAlignment.center,
+                                mainAxisAlignment: MainAxisAlignment.start,
+                                children: [
+                                  MyTextView(
+                                    state.selectedStartTime == null ? "" : DateTimeUtils.formatDate(state.selectedStartTime, format: "yyyy-MM-dd HH:mm"),
+                                    fontSize: 14,
+                                    hint: "Job Start Time".tr,
+                                    textHintColor: ColorConstants.textGrayAECAE5,
+                                    isFontMedium: true,
+                                    textColor: ColorConstants.white,
+                                  ).expanded(),
+                                  //下拉选图标
+                                  Visibility(
+                                    visible: state.pageType != 2,
+                                    child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                                  ),
+                                ],
+                              ),
+                            ).onTap(() {
+                              FocusScope.of(context).unfocus();
+                              if (state.pageType != 2) controller.pickStartTime();
+                            }),
+                          ),
+
+                          //选择工作结束时间
+                          Expanded(
+                            child: Container(
+                              padding: const EdgeInsets.only(left: 16, right: 10),
+                              margin: const EdgeInsets.only(left: 10),
+                              height: 45,
+                              decoration: BoxDecoration(
+                                color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                                borderRadius: const BorderRadius.all(Radius.circular(5)),
+                              ),
+                              child: Row(
+                                mainAxisSize: MainAxisSize.max,
+                                crossAxisAlignment: CrossAxisAlignment.center,
+                                mainAxisAlignment: MainAxisAlignment.start,
+                                children: [
+                                  MyTextView(
+                                    state.selectedEndTime == null ? "" : DateTimeUtils.formatDate(state.selectedEndTime, format: "yyyy-MM-dd HH:mm"),
+                                    fontSize: 14,
+                                    hint: "Job End Time".tr,
+                                    textHintColor: ColorConstants.textGrayAECAE5,
+                                    isFontMedium: true,
+                                    textColor: ColorConstants.white,
+                                  ).expanded(),
+                                  //下拉选图标
+                                  Visibility(
+                                    visible: state.pageType != 2,
+                                    child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                                  ),
+                                ],
+                              ),
+                            ).onTap(() {
+                              FocusScope.of(context).unfocus();
+                              if (state.pageType != 2) controller.pickEndTime();
+                            }),
+                          ),
+                        ],
+                      ).marginOnly(top: 10),
+
+                      //选择重复时间
+                      Visibility(
+                        visible: state.pageType == 0,
+                        child: MyTextView("Repeat".tr, fontSize: 15, textColor: Colors.white, isFontRegular: true, marginTop: 15),
+                      ),
+
+                      Visibility(
+                        visible: state.pageType == 0,
+                        child: Row(
+                          mainAxisSize: MainAxisSize.max,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          mainAxisAlignment: MainAxisAlignment.start,
+                          children: [
+                            //开始时间
+                            Expanded(
+                              child: Container(
+                                padding: const EdgeInsets.only(left: 16, right: 10),
+                                height: 45,
+                                decoration: BoxDecoration(
+                                  color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                                  borderRadius: const BorderRadius.all(Radius.circular(5)),
+                                ),
+                                child: Row(
+                                  mainAxisSize: MainAxisSize.max,
+                                  crossAxisAlignment: CrossAxisAlignment.center,
+                                  mainAxisAlignment: MainAxisAlignment.start,
+                                  children: [
+                                    MyTextView(
+                                      state.selectedRepeatStartTime == null
+                                          ? ""
+                                          : DateTimeUtils.formatDate(state.selectedRepeatStartTime, format: "yyyy-MM-dd"),
+                                      fontSize: 14,
+                                      hint: "Repeat Start Time".tr,
+                                      textHintColor: ColorConstants.textGrayAECAE5,
+                                      isFontMedium: true,
+                                      textColor: ColorConstants.white,
+                                    ).expanded(),
+                                    //下拉选图标
+                                    Visibility(
+                                      visible: state.pageType != 2,
+                                      child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                                    ),
+                                  ],
+                                ),
+                              ).onTap(() {
+                                FocusScope.of(context).unfocus();
+                                if (state.pageType != 2) controller.pickRepeatStartTime();
+                              }),
+                            ),
+
+                            //结束时间
+                            Expanded(
+                              child: Container(
+                                padding: const EdgeInsets.only(left: 16, right: 10),
+                                margin: const EdgeInsets.only(left: 10),
+                                height: 45,
+                                decoration: BoxDecoration(
+                                  color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                                  borderRadius: const BorderRadius.all(Radius.circular(5)),
+                                ),
+                                child: Row(
+                                  mainAxisSize: MainAxisSize.max,
+                                  crossAxisAlignment: CrossAxisAlignment.center,
+                                  mainAxisAlignment: MainAxisAlignment.start,
+                                  children: [
+                                    MyTextView(
+                                      state.selectedRepeatEndTime == null ? "" : DateTimeUtils.formatDate(state.selectedRepeatEndTime, format: "yyyy-MM-dd"),
+                                      fontSize: 14,
+                                      hint: "Repeat End Time".tr,
+                                      textHintColor: ColorConstants.textGrayAECAE5,
+                                      isFontMedium: true,
+                                      textColor: ColorConstants.white,
+                                    ).expanded(),
+                                    //下拉选图标
+                                    Visibility(
+                                      visible: state.pageType != 2,
+                                      child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                                    ),
+                                  ],
+                                ),
+                              ).onTap(() {
+                                FocusScope.of(context).unfocus();
+                                if (state.pageType != 2) controller.pickRepeatEndTime();
+                              }),
+                            ),
+                          ],
+                        ).marginOnly(top: 10),
+                      ),
+
+                      //工作选择部门
+                      FormRequireText(
+                        text: "Outlet".tr,
+                      ).marginOnly(top: 15),
+
+                      //选择部门
+                      Container(
+                        padding: const EdgeInsets.only(left: 16, right: 10),
+                        margin: const EdgeInsets.only(top: 10),
+                        height: 45,
+                        decoration: BoxDecoration(
+                          color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                          borderRadius: const BorderRadius.all(Radius.circular(5)),
+                        ),
+                        child: Row(
+                          mainAxisSize: MainAxisSize.max,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          mainAxisAlignment: MainAxisAlignment.start,
+                          children: [
+                            MyTextView(
+                              state.selectOutletName ?? "",
+                              fontSize: 14,
+                              hint: "Choose Outlet".tr,
+                              textHintColor: ColorConstants.textGrayAECAE5,
+                              isFontMedium: true,
+                              textColor: ColorConstants.white,
+                            ).expanded(),
+                            Visibility(
+                              visible: state.pageType != 2,
+                              child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                            ),
+                          ],
+                        ),
+                      ).onTap(() {
+                        FocusScope.of(context).unfocus();
+                        if (state.pageType != 2) controller.pickOutlet();
+                      }),
+
+                      //需求人数
+                      FormRequireText(
+                        text: "No. of Staff".tr,
+                      ).marginOnly(top: 15, bottom: 10),
+
+                      Row(
+                        children: [
+                          Container(
+                            padding: const EdgeInsets.only(left: 16, right: 10),
+                            margin: const EdgeInsets.only(right: 12),
+                            height: 45,
+                            decoration: BoxDecoration(
+                              color: const Color(0xFF4DCFF6).withOpacity(0.2),
+                              borderRadius: const BorderRadius.all(Radius.circular(5)),
+                            ),
+                            child: Row(
+                              mainAxisSize: MainAxisSize.max,
+                              crossAxisAlignment: CrossAxisAlignment.center,
+                              mainAxisAlignment: MainAxisAlignment.start,
+                              children: [
+                                MyTextView(
+                                  state.genderOptions[state.genderOptionType],
+                                  fontSize: 14,
+                                  textHintColor: ColorConstants.textGrayAECAE5,
+                                  isFontRegular: true,
+                                  textColor: ColorConstants.white,
+                                ).expanded(),
+                                //下拉选图标
+                                const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                              ],
+                            ),
+                          ).onTap(() {
+                            FocusScope.of(context).unfocus();
+                            controller.pickLimitType();
+                          }).expanded(flex: 55),
+
+                          //输入框-不限制性别
+                          Visibility(
+                            visible: state.genderOptionType == 0,
+                            child: CustomTextField(
+                              formKey: "need_no",
+                              marginLeft: 0,
+                              marginRight: 0,
+                              paddingTop: 0,
+                              paddingBottom: 0,
+                              height: 45,
+                              fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(0.2),
+                              inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
+                              textInputType: TextInputType.number,
+                              formData: state.formData,
+                              textInputAction: TextInputAction.done,
+                              onSubmit: (key, value) {
+                                FocusScope.of(context).unfocus();
+                              },
+                            ).expanded(flex: 55),
+                          ),
+
+                          //输入框组-限制性别
+                          Visibility(
+                            visible: state.genderOptionType != 0,
+                            child: Row(
+                              children: [
+                                Row(
+                                  children: [
+                                    MyTextView(
+                                      "M",
+                                      fontSize: 15,
+                                      paddingLeft: 10,
+                                      isFontRegular: true,
+                                      textColor: ColorConstants.white,
+                                    ),
+                                    CustomTextField(
+                                      formKey: "need_male",
+                                      marginLeft: 0,
+                                      marginRight: 0,
+                                      paddingTop: 0,
+                                      paddingBottom: 0,
+                                      paddingLeft: 10,
+                                      paddingRight: 10,
+                                      height: 45,
+                                      cornerRadius: 0,
+                                      fillBackgroundColor: Colors.transparent,
+                                      inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
+                                      textInputType: TextInputType.number,
+                                      formData: state.formData,
+                                      textInputAction: TextInputAction.done,
+                                      onSubmit: (key, value) {
+                                        FocusScope.of(context).unfocus();
+                                      },
+                                    ).expanded(),
+                                  ],
+                                )
+                                    .decorated(
+                                      color: const Color(0xFF4DCFF6).withOpacity(0.2),
+                                      borderRadius: const BorderRadius.all(Radius.circular(5)),
+                                    )
+                                    .expanded(),
+                                const SizedBox(width: 12),
+                                Row(
+                                  children: [
+                                    MyTextView(
+                                      "F",
+                                      fontSize: 15,
+                                      paddingLeft: 10,
+                                      isFontRegular: true,
+                                      textColor: ColorConstants.white,
+                                    ),
+                                    CustomTextField(
+                                      formKey: "need_female",
+                                      marginLeft: 0,
+                                      marginRight: 0,
+                                      paddingTop: 0,
+                                      paddingBottom: 0,
+                                      paddingLeft: 10,
+                                      paddingRight: 10,
+                                      height: 45,
+                                      cornerRadius: 0,
+                                      fillBackgroundColor: Colors.transparent,
+                                      inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
+                                      textInputType: TextInputType.number,
+                                      formData: state.formData,
+                                      textInputAction: TextInputAction.done,
+                                      onSubmit: (key, value) {
+                                        FocusScope.of(context).unfocus();
+                                      },
+                                    ).expanded(),
+                                  ],
+                                )
+                                    .decorated(
+                                      color: const Color(0xFF4DCFF6).withOpacity(0.2),
+                                      borderRadius: const BorderRadius.all(Radius.circular(5)),
+                                    )
+                                    .expanded(),
+                              ],
+                            ).expanded(flex: 55),
+                          ),
+                        ],
+                      ),
+
+                      //输入类型
+                      MyTextView("Event".tr, fontSize: 15, textColor: Colors.white, isFontRegular: true, marginTop: 15, marginBottom: 10),
+
+                      Row(
+                        children: [
+                          CustomTextField(
+                            formKey: "event_name",
+                            marginLeft: 0,
+                            marginRight: 15,
+                            paddingTop: 0,
+                            paddingBottom: 0,
+                            height: 45,
+                            fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(0.2),
+                            inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
+                            textInputType: TextInputType.number,
+                            formData: state.formData,
+                            textInputAction: TextInputAction.done,
+                            onSubmit: (key, value) {
+                              FocusScope.of(context).unfocus();
+                            },
+                          ).expanded(),
+
+                          //类型
+                          CustomTextField(
+                            formKey: "event_type",
+                            marginLeft: 0,
+                            marginRight: 0,
+                            paddingTop: 0,
+                            paddingBottom: 0,
+                            height: 45,
+                            fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(0.2),
+                            inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
+                            textInputType: TextInputType.number,
+                            formData: state.formData,
+                            textInputAction: TextInputAction.done,
+                            onSubmit: (key, value) {
+                              FocusScope.of(context).unfocus();
+                            },
+                          ).expanded(),
+                        ],
+                      ),
+
+                      //输入Remark
+                      MyTextView(
+                        "Remark".tr,
+                        fontSize: 15,
+                        isFontRegular: true,
+                        textColor: Colors.white,
+                        marginTop: 15,
+                      ),
+
+                      IgnoreKeyboardDismiss(
+                        child: Container(
+                          height: 160,
+                          margin: const EdgeInsets.only(top: 10),
+                          padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 15),
+                          decoration: BoxDecoration(
+                            color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                            borderRadius: const BorderRadius.all(Radius.circular(5)),
+                          ),
+                          child: TextField(
+                            cursorColor: ColorConstants.white,
+                            cursorWidth: 1.5,
+                            autofocus: false,
+                            enabled: state.pageType != 2,
+                            focusNode: state.formData["remark"]!['focusNode'],
+                            controller: state.formData["remark"]!['controller'],
+                            // 装饰
+                            decoration: InputDecoration(
+                              isDense: true,
+                              isCollapsed: true,
+                              border: InputBorder.none,
+                              hintText: state.formData["remark"]!['hintText'],
+                              hintStyle: const TextStyle(
+                                color: ColorConstants.textGrayAECAE5,
+                                fontSize: 15.0,
+                                fontWeight: FontWeight.w400,
+                              ),
+                            ),
+                            style: const TextStyle(
+                              color: ColorConstants.white,
+                              fontSize: 15.0,
+                              fontWeight: FontWeight.w400,
+                            ),
+                            // 键盘动作右下角图标
+                            textInputAction: TextInputAction.done,
+                            onSubmitted: (value) {
+                              FocusScope.of(context).unfocus();
+                            },
+                          ),
+                        ),
+                      ).marginOnly(bottom: 30),
+
+                      //提交按钮
+                      Visibility(
+                        visible: state.pageType != 2,
+                        child: MyButton(
+                          type: ClickType.throttle,
+                          milliseconds: 500,
+                          onPressed: () {
+                            FocusScope.of(context).unfocus();
+                            controller.doEditSubmit();
+                          },
+                          text: "Submit".tr,
+                          textColor: ColorConstants.white,
+                          fontSize: 16,
+                          radius: 22.5,
+                          backgroundColor: hexToColor("#FFBB1B"),
+                          fontWeight: FontWeight.w500,
+                        ).marginOnly(bottom: 30),
+                      ),
+                    ],
+                  ).paddingOnly(left: 15, right: 15),
+                ),
+              ),
+            ),
+          ),
+        ),
+      );
+    });
+  }
+}

+ 74 - 0
packages/cpt_th/lib/modules/labour_er/labour_request_er_add/labour_request_er_add_state.dart

@@ -0,0 +1,74 @@
+import 'package:domain/entity/response/job_list_e_r_detail_entity.dart';
+import 'package:domain/entity/response/labour_request_e_r_option_entity.dart';
+import 'package:flutter/material.dart';
+import 'package:plugin_basic/basic_export.dart';
+
+class LabourRequestERAddState {
+
+  //表单的校验与数据
+  Map<String, Map<String, dynamic>> formData = {
+    'remark': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter...'.tr,
+      'obsecure': false,
+    },
+    'need_male': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Male'.tr,
+      'obsecure': false,
+    },
+    'need_female': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Female'.tr,
+      'obsecure': false,
+    },
+    'need_no': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Needs Num'.tr,
+      'obsecure': false,
+    },
+    'event_name': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Event Name'.tr,
+      'obsecure': false,
+    },
+    'event_type': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Event Type'.tr,
+      'obsecure': false,
+    },
+  };
+
+  int pageType = 0;  //pageType 0新增  1是编辑  2是详情
+  String? jobId;  //编辑和详情需要用到ID
+  void Function(dynamic value)? cb;
+
+  List<String> genderOptions = ["Gender Unlimited".tr, "Gender Limited".tr];
+  int genderOptionType = 0;  //使用哪一种类型限制
+
+  JobListERDetailEntity? editDetailEntity;
+
+  DateTime? selectedStartTime;
+  DateTime? selectedEndTime;
+
+  DateTime? selectedRepeatStartTime;
+  DateTime? selectedRepeatEndTime;
+
+  String? selectTitleId;
+  String? selectTitleName;
+
+  String? selectOutletId;
+  String? selectOutletName;
+}

+ 164 - 0
packages/cpt_th/lib/modules/main/main_controller.dart

@@ -0,0 +1,164 @@
+import 'package:cpt_th/modules/job_er/job_list_er/job_list_er_page.dart';
+import 'package:domain/entity/home_module.dart';
+import 'package:domain/entity/response/hotel_info_entity.dart';
+import 'package:domain/repository/auth_repository.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/constants/app_constant.dart';
+import 'package:plugin_basic/service/app_config_service.dart';
+import 'package:plugin_basic/service/user_service.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:router/componentRouter/component_router_service.dart';
+import 'package:shared/utils/event_bus.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+import '../labour/labour_request_list/labour_request_list_page.dart';
+import '../labour/labour_review_list/labour_review_page.dart';
+import '../labour/labour_template_list/labour_template_list_page.dart';
+import '../labour_er/labour_request_er/labour_request_list_page.dart';
+import 'main_state.dart';
+
+class MainController extends GetxController {
+  final AuthRepository _authRepository = Get.find();
+  final MainState state = MainState();
+
+  var _needShowPlaceholder = true;
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Success;
+  String? errorMessage;
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,
+    controlFinishLoad: false,
+  );
+
+  // Refresh 刷新事件
+  Future onRefresh() async {
+    fetchHomeData();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _needShowPlaceholder = true;
+    fetchHomeData();
+  }
+
+  /// 获取首页的数据
+  void fetchHomeData() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    }
+
+    //获取到数据
+    var result = await _authRepository.fetchHotelInfo(country: ConfigService.to.selectCountry.value);
+
+    //处理数据
+    if (result.isSuccess) {
+      final hotelInfo = result.data;
+
+      if (hotelInfo != null) {
+        UserService.to.hotelInfo.value = hotelInfo;
+
+        _handleList(hotelInfo.menus);
+      } else {
+        ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+      }
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+
+    //停止刷新
+    refreshController.finishRefresh();
+    //最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void _handleList(List<HotelInfoMenus>? list) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      state.datas.clear();
+
+      //如果是管理员登录,根据Key筛选需要展示的模块
+      for (var hotelInfo in list) {
+        if (hotelInfo.key != null) {
+          state.datas.addAll(_filterModulesByKey(hotelInfo.route!));
+        }
+      }
+      //更新状态
+      changeLoadingState(LoadState.State_Success);
+    } else {
+      //展示无数据的布局
+      state.datas.clear();
+      changeLoadingState(LoadState.State_Empty);
+    }
+  }
+
+  List<HomeModule> _filterModulesByKey(String key) {
+    return state.modules.where((module) => module.key == key).toList();
+  }
+
+  @override
+  void onReady() async {
+    super.onReady();
+    fetchHomeData();
+    registerEventBus();
+  }
+
+  @override
+  void onClose() {
+    unregisterEventBus();
+    super.onClose();
+  }
+
+  /// 跳转到指定的模块中去
+  void gotoModulePage(HomeModule module) {
+    switch (module.key) {
+      case 'er/lab-req':
+        LabourRequestERListPage.startInstance();
+        break;
+      case 'er/jobs':
+        ConfigService.to.isTHOAType = false;
+        JobListERPage.startInstance(null, null);
+        break;
+      case 'oa/lab-req':
+        LabourRequestListPage.startInstance();
+        break;
+      case 'oa/jobs':
+        ConfigService.to.isTHOAType = true;
+        JobListERPage.startInstance(null, null);
+        break;
+      case 'oa/lab-req/review':
+        LabourReviewPage.startInstance();
+        break;
+      case 'template':
+        LabourTemplateListPage.startInstance();
+        break;
+    }
+  }
+
+  /// 跳转到设置页面
+  void gotoSettingPage() {
+    ComponentRouterServices.authService.startSettingPage();
+  }
+
+  // EventBus 的事件接收
+  Subscription? subscribe;
+
+  void registerEventBus() {
+    subscribe = bus.on(AppConstant.eventMainRefresh, (arg) {
+      refreshController.callRefresh();
+    });
+  }
+
+  void unregisterEventBus() {
+    bus.off(AppConstant.eventMainRefresh, subscribe);
+  }
+}

+ 46 - 0
packages/cpt_th/lib/modules/main/main_item_module.dart

@@ -0,0 +1,46 @@
+// 自定义的模块项 Widget
+import 'package:domain/entity/home_module.dart';
+import 'package:flutter/material.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+class ModuleItem extends StatelessWidget {
+  final HomeModule item;
+  final VoidCallback onTap;
+
+  ModuleItem(this.item, this.onTap);
+
+  @override
+  Widget build(BuildContext context) {
+    return GestureDetector(
+      onTap: onTap,
+      child: Container(
+        decoration: BoxDecoration(
+          color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+          borderRadius: BorderRadius.circular(7.5), // 设置圆角
+        ),
+        child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            // 模块的名称
+            MyTextView(
+              item.moduleName,
+              textColor: Colors.white,
+              isTextEllipsis: true,
+              maxLines: 2,
+              isFontBold: true,
+              marginLeft: 20,
+              marginRight: 10,
+              marginTop: 20,
+              fontSize: 17,
+            ),
+            Center(
+              child: MyAssetImage(item.moduleIconPath, width: item.iconWidth, height: item.iconHeight),
+            ).expanded(),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 198 - 0
packages/cpt_th/lib/modules/main/main_page.dart

@@ -0,0 +1,198 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/home_module.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/mixin_state_lifecycle.dart';
+import 'package:plugin_basic/service/user_service.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:cs_resources/local/theme/theme_config.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/double_tap_back_exit_app.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'main_controller.dart';
+import 'main_item_module.dart';
+import 'main_state.dart';
+
+/*
+   App首页页面
+ */
+class THMainPage extends BaseStatefulPage<MainController> {
+  THMainPage({super.key});
+
+  //启动当前页面
+  static void startInstance() {
+    return Get.start(RouterPath.THMain);
+  }
+
+  static void startWithPopAll() {
+    Get.offAllNamed(RouterPath.THMain);
+  }
+
+  @override
+  State<THMainPage> createState() => _MainPageState();
+
+  @override
+  MainController createRawController() {
+    return MainController();
+  }
+}
+
+class _MainPageState extends BaseState<THMainPage, MainController> with StateLifecycle {
+  late MainState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+  }
+
+  @override
+  void dispose() {
+    Get.delete<MainController>();
+    super.dispose();
+  }
+
+  @override
+  void onResume() {
+    super.onResume();
+    Log.d("MainPage Lifecycle - onResume");
+  }
+
+  @override
+  void onPause() {
+    super.onPause();
+    Log.d("MainPage Lifecycle - onPause");
+  }
+
+  @override
+  void onStop() {
+    super.onStop();
+    Log.d("MainPage Lifecycle - onStop");
+  }
+
+  @override
+  void onStart() {
+    super.onStart();
+    Log.d("MainPage Lifecycle - onStart");
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    Log.d("MainPage Lifecycle - build走了一遍");
+
+    //双击退出应用
+    return DoubleTapBackExitApp(
+      child: AnnotatedRegion<SystemUiOverlayStyle>(
+        value: ThemeConfig.systemUiOverlayStyleLightThemeWhite,
+        child: autoCtlGetBuilder(builder: (controller) {
+          return Scaffold(
+            body: SafeArea(
+        bottom: MediaQuery.of(context).padding.bottom > 38,
+              top: false,
+              //真正的 Content 布局,使用PageView保存状态
+              child: Container(
+                width: double.infinity,
+                height: double.infinity,
+                padding: EdgeInsets.only(top: ScreenUtil.getStatusBarH(context)),
+                decoration: const BoxDecoration(
+                  gradient: LinearGradient(
+                    colors: [
+                      Color(0xFF091D44),
+                      Color(0xFF245A8A),
+                      Color(0xFF7F7CEC),
+                    ],
+                    begin: Alignment.topCenter,
+                    end: Alignment.bottomCenter,
+                  ),
+                ),
+                child: Column(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: [
+                    Row(
+                      mainAxisSize: MainAxisSize.max,
+                      mainAxisAlignment: MainAxisAlignment.start,
+                      crossAxisAlignment: CrossAxisAlignment.center,
+                      children: [
+                        // 酒店的名字
+                        Obx(() {
+                          return MyTextView(
+                            UserService.to.getHotelInfo.hotelName ?? "-",
+                            textColor: ColorConstants.white,
+                            isTextEllipsis: true,
+                            maxLines: 2,
+                            isFontBold: true,
+                            fontSize: 21,
+                          );
+                        }).expanded(),
+
+                        //设置图标,点击进入设置页面
+                        const MyAssetImage(Assets.mainHomeSetting, width: 33, height: 33).onTap(() => controller.gotoSettingPage()),
+                      ],
+                    ),
+
+                    //欢迎的文本+姓名
+                    Obx(() {
+                      return MyTextView(
+                        "${"Welcome".tr} ${UserService.to.getHotelInfo.name ?? "-"}",
+                        textColor: ColorConstants.textGray9EB0C1,
+                        isTextEllipsis: true,
+                        isFontMedium: true,
+                        marginTop: 5,
+                        marginBottom: 15,
+                        fontSize: 18,
+                      );
+                    }),
+
+                    //底部的列表
+                    EasyRefresh(
+                      controller: controller.refreshController,
+                      onRefresh: controller.onRefresh,
+                      child: LoadStateLayout(
+                        state: controller.loadingState,
+                        errorMessage: controller.errorMessage,
+                        errorRetry: () {
+                          controller.retryRequest();
+                        },
+                        successSliverWidget: [
+                          //接口控制显示的模块
+                          SliverGrid(
+                            delegate: SliverChildBuilderDelegate(
+                              (context, index) {
+                                return ModuleItem(state.datas[index], () {
+                                  controller.gotoModulePage(state.datas[index]);
+                                });
+                              },
+                              childCount: state.datas.length,
+                            ),
+                            gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
+                              crossAxisCount: 2, // 每行2个项目
+                              mainAxisSpacing: 5, // 主轴方向的间距
+                              crossAxisSpacing: 5, // 交叉轴方向的间距
+                              childAspectRatio: 171 / 161, // 子项目的宽高比
+                            ),
+                          ),
+                        ],
+                      ),
+                    ).expanded(),
+                  ],
+                ).paddingSymmetric(horizontal: 15, vertical: 17),
+              ),
+            ),
+          );
+        }),
+      ),
+    );
+  }
+}

+ 21 - 0
packages/cpt_th/lib/modules/main/main_state.dart

@@ -0,0 +1,21 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/home_module.dart';
+import 'package:plugin_basic/basic_export.dart';
+
+class MainState {
+  //当前显示的模块
+  List<HomeModule> datas = [];
+
+  //全部的模块
+  final List<HomeModule> modules = [
+    //ER类型
+    HomeModule(key: 'er/lab-req', moduleName: 'Labour Request'.tr, moduleIconPath: Assets.mainHomeLabourRequest, iconWidth: 50, iconHeight: 40.3),
+    HomeModule(key: 'er/jobs', moduleName: 'Job List'.tr, moduleIconPath: Assets.mainHomeJobList, iconWidth: 45, iconHeight: 45),
+    //OA类型
+    HomeModule(key: 'oa/lab-req', moduleName: 'Labour Request'.tr, moduleIconPath: Assets.mainHomeLabourRequest, iconWidth: 50, iconHeight: 40.3),
+    HomeModule(key: 'oa/jobs', moduleName: 'Job List'.tr, moduleIconPath: Assets.mainHomeJobList, iconWidth: 45, iconHeight: 45),
+    HomeModule(key: 'oa/lab-req/review', moduleName: 'Labour Request Review'.tr, moduleIconPath: Assets.mainHomeLabourRequestReview, iconWidth: 50.5, iconHeight: 43),
+    HomeModule(key: 'template', moduleName: 'Default Job Title'.tr, moduleIconPath: Assets.mainHomeJobTemplate, iconWidth: 48.5, iconHeight: 46.5), //越南的模版
+    HomeModule(key: 'jobTitle', moduleName: 'Default Job Title'.tr, moduleIconPath: Assets.mainHomeJobTemplate, iconWidth: 48.5, iconHeight: 46.5), //新加坡的模板
+  ];
+}

+ 0 - 0
packages/cpt_th/lib/modules/th_export.dart


+ 122 - 0
packages/cpt_th/lib/router/th_router.dart

@@ -0,0 +1,122 @@
+import 'package:cpt_th/modules/job_er/applied_er_edit/applied_er_edit_page.dart';
+import 'package:get/get.dart';
+import 'package:router/path/router_path.dart';
+import '../modules/job_er/applied_er/applied_er_page.dart';
+import '../modules/job_er/applied_er_staff_detail/applied_er_staff_detail_page.dart';
+import '../modules/job_er/applied_er_staff_reviews/applied_er_staff_reviews_page.dart';
+import '../modules/job_er/applied_er_workflow/applied_er_workflow_page.dart';
+import '../modules/job_er/job_list_er/job_list_er_page.dart';
+import '../modules/labour_er/labour_request_er/labour_request_list_page.dart';
+import '../modules/labour_er/labour_request_er_add/labour_request_er_add_page.dart';
+import '../modules/main/main_page.dart';
+import '../modules/labour/labour_request_add/labour_request_add_page.dart';
+import '../modules/labour/labour_request_list/labour_request_list_page.dart';
+import '../modules/labour/labour_request_workflow/labour_request_workflow_page.dart';
+import '../modules/labour/labour_review_list/labour_review_page.dart';
+import '../modules/labour/labour_review_workflow/labour_request_workflow_page.dart';
+import '../modules/labour/labour_template_add/labour_template_add_page.dart';
+import '../modules/labour/labour_template_list/labour_template_list_page.dart';
+
+
+/// TH模块路由配置
+class THPageRouter {
+  static final routes = <GetPage<dynamic>>[
+
+    //首页
+    GetPage(
+      name: RouterPath.THMain,
+      page: () => THMainPage(),
+    ),
+
+    //用工请求列表
+    GetPage(
+      name: RouterPath.THLabourRequestOA,
+      page: () => LabourRequestListPage(),
+    ),
+
+    //用工请求添加
+    GetPage(
+      name: RouterPath.THLabourRequestAddOA,
+      page: () => LabourRequestAddPage(),
+    ),
+
+    //用工请求状态修改工作流
+    GetPage(
+      name: RouterPath.THLabourRequestWorkflowOA,
+      page: () => LabourRequestWorkflowPage(),
+    ),
+
+    //用工请求审核列表
+    GetPage(
+      name: RouterPath.THLabourReviewOA,
+      page: () => LabourReviewPage(),
+    ),
+
+    //用工请求审核工作流
+    GetPage(
+      name: RouterPath.THLabourReviewWorkflowOA,
+      page: () => LabourReviewWorkflowPage(),
+    ),
+
+    //用工请求列表 ER
+    GetPage(
+      name: RouterPath.THLabourRequestER,
+      page: () => LabourRequestERListPage(),
+    ),
+
+    //用工请求列表添加 ER
+    GetPage(
+      name: RouterPath.THLabourRequestERAdd,
+      page: () => LabourRequestERAddPage(),
+    ),
+
+    //用工请求列表添加 ER
+    GetPage(
+      name: RouterPath.THJobListER,
+      page: () => JobListERPage(),
+    ),
+
+    //ER 已申请列表
+    GetPage(
+      name: RouterPath.THJobAppliedER,
+      page: () => AppliedErPage(),
+    ),
+
+    //ER 已申请列表,修改员工信息
+    GetPage(
+      name: RouterPath.THJobAppliedEditER,
+      page: () => AppliedErEditPage(),
+    ),
+
+    //ER 已申请列表,员工详情页面
+    GetPage(
+      name: RouterPath.THJobAppliedEmployeeER,
+      page: () => AppliedErStaffDetailPage(),
+    ),
+
+    //ER 已申请列表,员工详情评价页面
+    GetPage(
+      name: RouterPath.THJobAppliedEmployeeRemarkER,
+      page: () => AppliedErStaffReviewsPage(),
+    ),
+
+    //ER 已申请列表,员工详情评价页面
+    GetPage(
+      name: RouterPath.THJobAppliedStaffWorkflowER,
+      page: () => AppliedErWorkflowPage(),
+    ),
+
+    //用工请求状态修改工作流
+    GetPage(
+      name: RouterPath.labourTemplateList,
+      page: () => LabourTemplateListPage(),
+    ),
+
+    //用工请求状态修改工作流
+    GetPage(
+      name: RouterPath.labourTemplateAdd,
+      page: () => LabourTemplateAddPage(),
+    ),
+
+  ];
+}

+ 23 - 0
packages/cpt_th/lib/router/th_service_impl.dart

@@ -0,0 +1,23 @@
+import 'package:plugin_basic/basic_export.dart';
+import 'package:router/componentRouter/th_service.dart';
+import '../modules/main/main_page.dart';
+
+/// TH组件对应的路由实现类
+class THServiceImpl extends GetxService implements THService {
+  @override
+  void onInit() {
+    super.onInit();
+    //初始化资源
+  }
+
+  @override
+  void onClose() {
+    super.onClose();
+    //销毁资源
+  }
+
+  @override
+  void startTHMainPage() {
+    THMainPage.startWithPopAll();
+  }
+}

+ 40 - 0
packages/cpt_th/pubspec.yaml

@@ -0,0 +1,40 @@
+name: cpt_th
+description: 按照国家区分模块-泰国
+
+version: 1.0.0
+
+environment:
+  sdk: '>=3.0.2 <4.0.0'
+
+dependencies:
+
+  flutter_localizations:
+    sdk: flutter
+
+  flutter:
+    sdk: flutter
+
+  #基础组件的依赖
+  domain:
+    path: ../cs_domain
+
+  plugin_basic:
+    path: ../cs_plugin_basic
+
+  plugin_platform:
+    path: ../cs_plugin_platform
+
+  shared:
+    path: ../cs_shared
+
+  cs_resources:
+    path: ../cs_resources
+
+  router:
+    path: ../cs_router
+
+  widgets:
+    path: ../cs_widgets
+
+flutter:
+  uses-material-design: true

+ 16 - 0
packages/cpt_th/pubspec_overrides.yaml

@@ -0,0 +1,16 @@
+# melos_managed_dependency_overrides: cs_resources,domain,plugin_basic,plugin_platform,router,shared,widgets
+dependency_overrides:
+  cs_resources:
+    path: ../cs_resources
+  domain:
+    path: ../cs_domain
+  plugin_basic:
+    path: ../cs_plugin_basic
+  plugin_platform:
+    path: ../cs_plugin_platform
+  router:
+    path: ../cs_router
+  shared:
+    path: ../cs_shared
+  widgets:
+    path: ../cs_widgets

+ 137 - 1
packages/cs_domain/lib/constants/api_constants.dart

@@ -19,6 +19,9 @@ class ApiConstants {
   //荷兰的域名
   static const nlBaseUrl = isServerRelease ? 'https://netherlands.casualabour.com' : 'http://netherlands-dev.casualabour.com';
 
+  //泰国的域名
+  static const thBaseUrl = isServerRelease ? 'https://thailand.casualabour.com' : 'http://thailand-dev.casualabour.com';
+
   // =========================== 用户相关 ↓=========================================
 
   // 酒店登录
@@ -212,7 +215,6 @@ class ApiConstants {
   //用工请求的考勤确认(Job List)现在是发送到 Attendance Review 列表中
   static const apiJobAppliedApproveSG = "/index.php/api/v2/hotel/applied/batch-approve";
 
-
   // =========================== 新加坡工作相关 ↓=========================================
 
   //新加坡工作已申请的选项
@@ -510,4 +512,138 @@ class ApiConstants {
   // 用户登出系统
   static const apiUserLogoutMS = "/index.php/api/v1/er/logout";
 
+// ===================================  泰国接口  ↓  ===================================
+
+  //登录
+  static const apiLoginTH = "/index.php/api/er/login";
+
+  //账户信息
+  static const apiAccountInfoTH = "/index.php/api/er/auth/info";
+
+  //账户列表
+  static const apiAccountListTH = "/index.php/api/er/auth/account";
+
+  //切换账号
+  static const apiSwitchAccountTH = "/index.php/api/er/auth/switch";
+
+  // ER 的用工请求列表
+  static const apiERLabourRequestTH = "/index.php/api/er/lab-req/index";
+
+  // ER 的用工请求添加选项
+  static const apiERLabourRequestAddOptionTH = "/index.php/api/er/lab-req/view-add";
+
+  // ER 的用工请求新增提交
+  static const apiERLabourRequestAddSubmitTH = "/index.php/api/er/lab-req/add-submit";
+
+  //ER 工作列表选项
+  static const apiERJobListOptionTH = "/index.php/api/er/job/index";
+
+  //ER 工作列表
+  static const apiERJobListTableTH = "/index.php/api/er/job/table";
+
+  //ER 工作列表删除
+  static const apiERJobListDeleteTH = "/index.php/api/er/job/action";
+
+  //ER 工作列表详情
+  static const apiERJobListDetailTH = "/index.php/api/er/job/view-edit";
+
+  // ER 的用工请求编辑提交
+  static const apiERLabourRequestEditSubmitTH = "/index.php/api/er/job/edit-submit";
+
+  //ER 工作 Applied Index
+  static const apiERAppliedOptionTH = "/index.php/api/er/applied/index";
+
+  //ER 工作 Applied List
+  static const apiERAppliedListTH = "/index.php/api/er/applied/table";
+
+  //ER 工作 Applied 批量批准
+  static const apiERAppliedApproveTH = "/index.php/api/er/applied/batch-approve";
+
+  //ER 工作 Applied 批量修改
+  static const apiERAppliedEditTH = "/index.php/api/er/applied/batch-modify";
+
+  //ER 工作 Applied 编辑个人考勤
+  static const apiERAppliedEditAttTH = "/index.php/api/er/applied/view-edit";
+
+  //ER 工作 Applied 编辑个人考勤的提交
+  static const apiERAppliedEditAttSubmitTH = "/index.php/api/er/applied/edit-submit";
+
+  //ER 工作 Applied 添加员工列表
+  static const apiERAppliedAddEmployeeTH = "/index.php/api/er/applied/staff-table";
+
+  //ER 工作 Applied 添加员工提交
+  static const apiERAppliedAddEmployeeSubmitTH = "/index.php/api/er/applied/staff-submit";
+
+  //ER 员工信息
+  static const apiEREmployeeInfoTH = "/index.php/api/er/member/detail";
+
+  //ER 员工工作历史
+  static const apiEREmployeeHistoryTH = "/index.php/api/er/member/history";
+
+  //ER 员工评价列表
+  static const apiEREmployeeEvaluationTH = "/index.php/api/er/member/remarks";
+
+  // 工作已申请员工列表-评价员工的详情数据
+  static const apiERAppliedStaffReviewIndexTH = "/index.php/api/er/applied/view-remark";
+
+  // 工作已申请员工列表-评价员工的提交
+  static const apiERAppliedStaffReviewSubmitTH = "/index.php/api/er/applied/remark-submit";
+
+  // 工作已申请员工列表-评价员工的提交
+  static const apiERAppliedStaffStateWorkflowTH = "/index.php/api/er/applied/view-status";
+
+  // OA 用工请求的选项
+  static const apiOALabourRequestOptionTH = "/index.php/api/oa/lab-req/index";
+
+  // OA 用工请求的列表
+  static const apiOALabourRequestTableTH = "/index.php/api/oa/lab-req/table";
+
+  // OA 用工请求的撤回
+  static const apiOALabourRequestRecallTH = "/index.php/api/oa/lab-req/recall";
+
+  // OA 用工请求的删除
+  static const apiOALabourRequestDeleteTH = "/index.php/api/oa/lab-req/destroy";
+
+  // OA 用工请求的状态流
+  static const apiOALabourRequestWorkflowTH = "/index.php/api/oa/lab-req/view-status";
+
+  // OA 用工请求添加选项
+  static const apiOALabourRequestAddOptionTH = "/index.php/api/oa/lab-req/view-add";
+
+  // OA 用工请求详情
+  static const apiOALabourRequestDetailTH = "/index.php/api/oa/lab-req/view-edit";
+
+  // OA 用工请求添加提交
+  static const apiOALabourRequestAddSubmitTH = "/index.php/api/oa/lab-req/add-submit";
+
+  // OA 用工请求编辑提交
+  static const apiOALabourRequestEditSubmitTH = "/index.php/api/oa/lab-req/edit-submit";
+
+  // TH 泰国上传文件
+  static const apiUploadFileTH = "/index.php/api/er/upload/file";
+
+  //OA 附件列表
+  static const apiOAAttachmentListTH = "/index.php/api/oa/lab-req/att-table";
+
+  //OA 附件新增
+  static const apiOAAttachmentAddTH = "/index.php/api/oa/lab-req/att-submit";
+
+  //OA 附件删除
+  static const apiOAAttachmentDeleteTH = "/index.php/api/oa/lab-req/att-delete";
+
+  //OA 用工审核列表选项
+  static const apiOALabourReviewOptionTH = "/index.php/api/oa/lab-req/review/index";
+
+  //OA 用工审核列表
+  static const apiOALabourReviewTableTH = "/index.php/api/oa/lab-req/review/table";
+
+  //OA 用工审核编辑提交
+  static const apiOALabourReviewEditSubmitTH = "/index.php/api/oa/lab-req/review/edit-submit";
+
+  //OA 用工审核的批量操作
+  static const apiOALabourReviewBatchTH = "/index.php/api/oa/lab-req/review/batch-audit";
+
+  //OA 用工审核工作流
+  static const apiOALabourReviewWorkflowTH = "/index.php/api/oa/lab-req/review/view-status";
+
 }

+ 5 - 0
packages/cs_domain/lib/entity/response/hotel_info_entity.dart

@@ -31,6 +31,11 @@ class HotelInfoEntity {
 class HotelInfoMenus {
 	String? key = '';
 	String? name = '';
+	String? route = '';  //泰国的根据这个判断类型
+	@JSONField(name: "red_dot")
+	String? redDot = '';
+	bool own = false;
+
 	List<HotelInfoMenusChildren>? children = [];
 
 	HotelInfoMenus();

+ 1 - 1
packages/cs_domain/lib/entity/response/job_list_applied_work_flow_entity.dart

@@ -34,7 +34,7 @@ class JobListAppliedWorkFlowRecords {
 	@JSONField(name: "audit_time")
 	String? auditTime = null;
 	@JSONField(name: "audit_mark")
-	dynamic auditMark;
+	String? auditMark;
 	@JSONField(name: "created_at")
 	String? createdAt = null;
 	@JSONField(name: "assignee_type_show")

+ 117 - 0
packages/cs_domain/lib/entity/response/job_list_e_r_detail_entity.dart

@@ -0,0 +1,117 @@
+import 'package:domain/entity/response/index_option_entity.dart';
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/job_list_e_r_detail_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/job_list_e_r_detail_entity.g.dart';
+
+@JsonSerializable()
+class JobListERDetailEntity {
+	JobListERDetailJob? job;
+	@JSONField(name: "start_time")
+	String? startTime;
+	@JSONField(name: "end_time")
+	String? endTime;
+
+	@JSONField(name: "title_list")
+	List<IndexOptionEntity> titleList = [];
+	@JSONField(name: "limit_list")
+	List<IndexOptionEntity> limitList = [];
+	@JSONField(name: "outlet_list")
+	List<IndexOptionEntity> outletList = [];
+	String? view;
+
+	JobListERDetailEntity();
+
+	factory JobListERDetailEntity.fromJson(Map<String, dynamic> json) => $JobListERDetailEntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $JobListERDetailEntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class JobListERDetailJob {
+	String? id;
+	@JSONField(name: "employer_id")
+	String? employerId;
+	@JSONField(name: "employer_name")
+	String? employerName;
+	@JSONField(name: "e_admin_id")
+	String? eAdminId;
+	@JSONField(name: "yy_job_id")
+	String? yyJobId;
+	@JSONField(name: "source_type")
+	int sourceType = 0;
+	@JSONField(name: "agency_id")
+	String? agencyId;
+	@JSONField(name: "agency_name")
+	String? agencyName;
+	@JSONField(name: "job_title")
+	String? jobTitle;
+	@JSONField(name: "outlet_id")
+	String? outletId;
+	@JSONField(name: "outlet_name")
+	String? outletName;
+	@JSONField(name: "job_date")
+	String? jobDate;
+	@JSONField(name: "start_time")
+	String? startTime;
+	@JSONField(name: "end_time")
+	String? endTime;
+	@JSONField(name: "working_hours")
+	String? workingHours;
+	@JSONField(name: "hourly_rate")
+	String? hourlyRate;
+	@JSONField(name: "hiring_num")
+	String? hiringNum;
+	@JSONField(name: "real_num")
+	String? realNum;
+	int status = 0;
+	String? description;
+	@JSONField(name: "confirm_status")
+	int confirmStatus = 0;
+	@JSONField(name: "created_at")
+	String? createdAt;
+	@JSONField(name: "updated_at")
+	String? updatedAt;
+	@JSONField(name: "deleted_at")
+	String? deletedAt;
+	@JSONField(name: "event_name")
+	String? eventName;
+	@JSONField(name: "event_type")
+	String? eventType;
+	@JSONField(name: "with_typhoid")
+	int withTyphoid = 0;
+	@JSONField(name: "sync_payroll")
+	int syncPayroll = 0;
+	@JSONField(name: "agency_rate")
+	String? agencyRate;
+	@JSONField(name: "request_id")
+	String? requestId;
+	@JSONField(name: "job_title_id")
+	String? jobTitleId;
+	@JSONField(name: "is_ot")
+	int isOt = 0;
+	@JSONField(name: "position_id")
+	String? positionId;
+	@JSONField(name: "sex_limit")
+	String? sexLimit;
+	@JSONField(name: "male_limit")
+	String? maleLimit;
+	@JSONField(name: "female_limit")
+	String? femaleLimit;
+
+	JobListERDetailJob();
+
+	factory JobListERDetailJob.fromJson(Map<String, dynamic> json) => $JobListERDetailJobFromJson(json);
+
+	Map<String, dynamic> toJson() => $JobListERDetailJobToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}

+ 51 - 0
packages/cs_domain/lib/entity/response/job_list_e_r_option_entity.dart

@@ -0,0 +1,51 @@
+import 'package:domain/entity/response/index_option_entity.dart';
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/job_list_e_r_option_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/job_list_e_r_option_entity.g.dart';
+
+@JsonSerializable()
+class JobListEROptionEntity {
+	@JSONField(name: "division_list")
+	List<IndexOptionEntity>? divisionList = [];
+	@JSONField(name: "outlet_list")
+	List<IndexOptionEntity>? outletList = [];
+	@JSONField(name: "with_ot")
+	bool? withOt;
+	@JSONField(name: "status_list")
+	List<IndexOptionEntity>? statusList = [];
+	@JSONField(name: "with_ot_job")
+	bool? withOtJob;
+	JobListEROptionFilter? filter;
+	String? view;
+
+	JobListEROptionEntity();
+
+	factory JobListEROptionEntity.fromJson(Map<String, dynamic> json) => $JobListEROptionEntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $JobListEROptionEntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class JobListEROptionFilter {
+	@JSONField(name: "start_date")
+	String? startDate;
+	@JSONField(name: "end_date")
+	String? endDate;
+
+	JobListEROptionFilter();
+
+	factory JobListEROptionFilter.fromJson(Map<String, dynamic> json) => $JobListEROptionFilterFromJson(json);
+
+	Map<String, dynamic> toJson() => $JobListEROptionFilterToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}

+ 6 - 6
packages/cs_domain/lib/entity/response/job_list_entity.dart

@@ -25,17 +25,17 @@ class JobListRows {
 	@JSONField(name: "job_id")
 	int jobId = 0;
 	@JSONField(name: "job_title")
-	String? jobTitle = null;
+	String? jobTitle;
 	@JSONField(name: "job_time")
-	String? jobTime = null;
+	String? jobTime;
 	@JSONField(name: "department_name")
-	String? departmentName = null;
+	String? departmentName;
 	@JSONField(name: "staff_show")
-	String? staffShow = null;
+	String? staffShow;
 	@JSONField(name: "status_show")
-	String? statusShow = null;
+	String? statusShow;
 	@JSONField(name: "created_at")
-	String? createdAt = null;
+	String? createdAt;
 	@JSONField(name: "action_list")
 	List<String>? actionList = [];
 

+ 3 - 0
packages/cs_domain/lib/entity/response/job_list_remark_view_entity.dart

@@ -9,6 +9,8 @@ class JobListRemarkViewEntity {
   int appliedId = 0;
   @JSONField(name: "member_name")
   String? memberName = null;
+  @JSONField(name: "labourer_name")
+  String? labourerName = null;
   @JSONField(name: "attitude_rate")
   double attitudeRate = 5.0;
   @JSONField(name: "grooming_rate")
@@ -18,6 +20,7 @@ class JobListRemarkViewEntity {
   @JSONField(name: "experience_rate")
   double experienceRate = 5.0;
   String? content = null;
+  String? feedback = null;
   String? disabled = null;
 
   JobListRemarkViewEntity();

+ 59 - 0
packages/cs_domain/lib/entity/response/labour_list_e_r_entity.dart

@@ -0,0 +1,59 @@
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/labour_list_e_r_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/labour_list_e_r_entity.g.dart';
+
+@JsonSerializable()
+class LabourListEREntity {
+	int total = 0;
+	List<LabourListERRows> rows = [];
+
+	LabourListEREntity();
+
+	factory LabourListEREntity.fromJson(Map<String, dynamic> json) => $LabourListEREntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $LabourListEREntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class LabourListERRows {
+	@JSONField(name: "job_id")
+	String? jobId;
+	@JSONField(name: "job_date")
+	String? jobDate;
+	@JSONField(name: "outlet_name")
+	String? outletName;
+	@JSONField(name: "job_title")
+	String? jobTitle;
+	@JSONField(name: "start_time")
+	String? startTime;
+	@JSONField(name: "end_time")
+	String? endTime;
+	@JSONField(name: "hiring_num")
+	String? hiringNum;
+	@JSONField(name: "pre_num")
+	String? preNum;
+	int status = 0;
+	@JSONField(name: "status_show")
+	String? statusShow;
+	@JSONField(name: "is_ot")
+	int isOt = 0;
+	@JSONField(name: "action_list")
+	List<String> actionList = [];
+
+	LabourListERRows();
+
+	factory LabourListERRows.fromJson(Map<String, dynamic> json) => $LabourListERRowsFromJson(json);
+
+	Map<String, dynamic> toJson() => $LabourListERRowsToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}

+ 80 - 0
packages/cs_domain/lib/entity/response/labour_request_e_r_entity.dart

@@ -0,0 +1,80 @@
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/labour_request_e_r_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/labour_request_e_r_entity.g.dart';
+
+@JsonSerializable()
+class LabourRequestEREntity {
+	@JSONField(name: "count_list")
+	List<LabourRequestERCountList> countList = [];
+	@JSONField(name: "source_list")
+	List<LabourRequestERSourceList> sourceList = [];
+	String? view;
+
+	LabourRequestEREntity();
+
+	factory LabourRequestEREntity.fromJson(Map<String, dynamic> json) => $LabourRequestEREntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $LabourRequestEREntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class LabourRequestERCountList {
+	String? date;
+	String? week;
+	String? num;
+
+	LabourRequestERCountList();
+
+	factory LabourRequestERCountList.fromJson(Map<String, dynamic> json) => $LabourRequestERCountListFromJson(json);
+
+	Map<String, dynamic> toJson() => $LabourRequestERCountListToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class LabourRequestERSourceList {
+	@JSONField(name: "agency_id")
+	String? agencyId;
+	@JSONField(name: "agency_name")
+	String? agencyName;
+	@JSONField(name: "request_list")
+	List<LabourRequestERSourceListRequestList>? requestList = [];
+
+	LabourRequestERSourceList();
+
+	factory LabourRequestERSourceList.fromJson(Map<String, dynamic> json) => $LabourRequestERSourceListFromJson(json);
+
+	Map<String, dynamic> toJson() => $LabourRequestERSourceListToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class LabourRequestERSourceListRequestList {
+	String? date;
+	String? num;
+
+	LabourRequestERSourceListRequestList();
+
+	factory LabourRequestERSourceListRequestList.fromJson(Map<String, dynamic> json) => $LabourRequestERSourceListRequestListFromJson(json);
+
+	Map<String, dynamic> toJson() => $LabourRequestERSourceListRequestListToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}

+ 42 - 0
packages/cs_domain/lib/entity/response/labour_request_e_r_option_entity.dart

@@ -0,0 +1,42 @@
+import 'package:domain/entity/response/index_option_entity.dart';
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/labour_request_e_r_option_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/labour_request_e_r_option_entity.g.dart';
+
+@JsonSerializable()
+class LabourRequestEROptionEntity {
+  @JSONField(name: "title_list")
+  List<IndexOptionEntity> titleList = [];
+  @JSONField(name: "outlet_list")
+  List<IndexOptionEntity> outletList = [];
+  @JSONField(name: "limit_list")
+  List<IndexOptionEntity> limitList = [];
+  @JSONField(name: "ot_radios")
+  List<IndexOptionEntity> otRadios = [];
+
+  @JSONField(name: "outlet_manager")
+  bool? outletManager;
+  @JSONField(name: "hiring_num")
+  String? hiringNum;
+  @JSONField(name: "sex_limit")
+  int? sexLimit;
+  @JSONField(name: "male_limit")
+  String? maleLimit;
+  @JSONField(name: "female_limit")
+  String? femaleLimit;
+  @JSONField(name: "with_ot_job")
+  bool? withOtJob;
+  String? view;
+
+  LabourRequestEROptionEntity();
+
+  factory LabourRequestEROptionEntity.fromJson(Map<String, dynamic> json) => $LabourRequestEROptionEntityFromJson(json);
+
+  Map<String, dynamic> toJson() => $LabourRequestEROptionEntityToJson(this);
+
+  @override
+  String toString() {
+    return jsonEncode(this);
+  }
+}

+ 3 - 0
packages/cs_domain/lib/entity/response/labour_request_index_entity.dart

@@ -1,3 +1,4 @@
+import 'package:domain/entity/response/index_option_entity.dart';
 import 'package:domain/generated/json/base/json_field.dart';
 import 'package:domain/generated/json/labour_request_index_entity.g.dart';
 import 'dart:convert';
@@ -11,6 +12,8 @@ class LabourRequestIndexEntity {
 	List<LabourRequestIndexDepartmentList>? outletList = [];
 	@JSONField(name: "status_list")
 	List<LabourRequestIndexStatusList>? statusList = [];
+	@JSONField(name: "division_list")
+	List<IndexOptionEntity> divisionList = [];
 
 	LabourRequestIndexEntity();
 

+ 2 - 0
packages/cs_domain/lib/entity/response/staff_labour_history_entity.dart

@@ -47,6 +47,8 @@ class StaffLabourHistoryRows {
 	int status = 0;
 	@JSONField(name: "status_show")
 	String? statusShow = null;
+	@JSONField(name: "employer_name")
+	String? employerName = null;
 	@JSONField(name: "applied_at")
 	String? appliedAt = null;
 	@JSONField(name: "security_in")

+ 190 - 0
packages/cs_domain/lib/entity/response/t_h_applied_edit_entity.dart

@@ -0,0 +1,190 @@
+import 'package:domain/entity/response/index_option_entity.dart';
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/t_h_applied_edit_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/t_h_applied_edit_entity.g.dart';
+
+@JsonSerializable()
+class THAppliedEditEntity {
+	THAppliedEditApplied? applied;
+	@JSONField(name: "applied_id")
+	String? appliedId;
+	@JSONField(name: "member_id")
+	String? memberId;
+	String? remark;
+	@JSONField(name: "start_time")
+	String? startTime;
+	@JSONField(name: "end_time")
+	String? endTime;
+	@JSONField(name: "default_start")
+	String? defaultStart;
+	@JSONField(name: "default_end")
+	String? defaultEnd;
+	@JSONField(name: "security_in")
+	String? securityIn;
+	@JSONField(name: "def_security_in")
+	String? defSecurityIn;
+	@JSONField(name: "security_out")
+	String? securityOut;
+	@JSONField(name: "def_security_out")
+	String? defSecurityOut;
+	@JSONField(name: "work_in")
+	String? workIn;
+	@JSONField(name: "def_work_in")
+	String? defWorkIn;
+	@JSONField(name: "work_out")
+	String? workOut;
+	@JSONField(name: "def_work_out")
+	String? defWorkOut;
+	@JSONField(name: "adjust_hours")
+	String? adjustHours;
+	@JSONField(name: "subtract_hours")
+	String? subtractHours;
+	@JSONField(name: "ballroom_list")
+	List<String> ballroomList = [];
+	@JSONField(name: "status_list")
+	List<IndexOptionEntity> statusList = [];
+	@JSONField(name: "reason_list")
+	List<IndexOptionEntity> reasonList = [];
+
+	THAppliedEditEntity();
+
+	factory THAppliedEditEntity.fromJson(Map<String, dynamic> json) => $THAppliedEditEntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $THAppliedEditEntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class THAppliedEditApplied {
+	String? id;
+	@JSONField(name: "job_id")
+	String? jobId;
+	@JSONField(name: "s_id")
+	String? sId;
+	@JSONField(name: "member_id")
+	String? memberId;
+	@JSONField(name: "labourer_id")
+	String? labourerId;
+	@JSONField(name: "labourer_name")
+	String? labourerName;
+	@JSONField(name: "labourer_nric")
+	String? labourerNric;
+	@JSONField(name: "source_type")
+	int sourceType = 0;
+	@JSONField(name: "employer_id")
+	String? employerId;
+	@JSONField(name: "employer_name")
+	String? employerName;
+	@JSONField(name: "agency_id")
+	String? agencyId;
+	@JSONField(name: "agency_name")
+	String? agencyName;
+	@JSONField(name: "job_title")
+	String? jobTitle;
+	@JSONField(name: "outlet_id")
+	String? outletId;
+	@JSONField(name: "outlet_name")
+	String? outletName;
+	@JSONField(name: "ballroom_id")
+	String? ballroomId;
+	@JSONField(name: "ballroom_name")
+	String? ballroomName;
+	@JSONField(name: "job_date")
+	String? jobDate;
+	@JSONField(name: "start_time")
+	int? startTime;
+	@JSONField(name: "end_time")
+	int? endTime;
+	@JSONField(name: "working_hours")
+	String? workingHours;
+	@JSONField(name: "adjust_hours")
+	String? adjustHours;
+	@JSONField(name: "total_hours")
+	String? totalHours;
+	@JSONField(name: "hourly_rate")
+	String? hourlyRate;
+	@JSONField(name: "gross_wage")
+	String? grossWage;
+	@JSONField(name: "security_clock_in")
+	int? securityClockIn;
+	@JSONField(name: "security_clock_out")
+	int? securityClockOut;
+	@JSONField(name: "work_clock_in")
+	int? workClockIn;
+	@JSONField(name: "work_clock_out")
+	int? workClockOut;
+	@JSONField(name: "security_in")
+	int? securityIn;
+	@JSONField(name: "security_out")
+	int? securityOut;
+	@JSONField(name: "work_in")
+	int? workIn;
+	@JSONField(name: "work_out")
+	int? workOut;
+	String? status ;
+	@JSONField(name: "remark_status")
+	int? remarkStatus;
+	@JSONField(name: "created_at")
+	String? createdAt;
+	@JSONField(name: "updated_at")
+	String? updatedAt;
+	@JSONField(name: "sync_payroll")
+	int? syncPayroll;
+	@JSONField(name: "sign_image")
+	String? signImage;
+	@JSONField(name: "event_name")
+	String? eventName;
+	@JSONField(name: "event_type")
+	String? eventType;
+	@JSONField(name: "working_type")
+	int? workingType;
+	@JSONField(name: "epf_employer")
+	String? epfEmployer;
+	@JSONField(name: "epf_employee")
+	String? epfEmployee;
+	@JSONField(name: "epf_total")
+	String? epfTotal;
+	@JSONField(name: "net_pay")
+	String? netPay;
+	String? socso;
+	@JSONField(name: "payment_status")
+	int? paymentStatus;
+	@JSONField(name: "paid_time")
+	String? paidTime;
+	@JSONField(name: "agency_rate")
+	String? agencyRate;
+	@JSONField(name: "agency_wage")
+	String? agencyWage;
+	String? remark;
+	@JSONField(name: "reason_type")
+	String? reasonType;
+	@JSONField(name: "audit_reject")
+	int? auditReject;
+	@JSONField(name: "revise_hours")
+	String? reviseHours;
+	@JSONField(name: "revise_gross_wage")
+	String? reviseGrossWage;
+	@JSONField(name: "revise_agency_wage")
+	String? reviseAgencyWage;
+	@JSONField(name: "is_ot")
+	int? isOt;
+	@JSONField(name: "reject_msg")
+	String? rejectMsg;
+
+	THAppliedEditApplied();
+
+	factory THAppliedEditApplied.fromJson(Map<String, dynamic> json) => $THAppliedEditAppliedFromJson(json);
+
+	Map<String, dynamic> toJson() => $THAppliedEditAppliedToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+

+ 0 - 0
packages/cs_domain/lib/entity/response/t_h_applied_employee_entity.dart


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