Browse Source

Merge branch 'master-sg-v1'

# Conflicts:
#	packages/cpt_auth/lib/modules/main/main_controller.dart
liukai 3 months ago
parent
commit
490597e14e
97 changed files with 7339 additions and 165 deletions
  1. 1 1
      packages/cpt_auth/lib/modules/login/login_controller.dart
  2. 9 5
      packages/cpt_auth/lib/modules/login/login_page.dart
  3. 6 7
      packages/cpt_auth/lib/modules/main/main_controller.dart
  4. 9 5
      packages/cpt_auth/lib/modules/select_country/select_country_controller.dart
  5. 46 15
      packages/cpt_auth/lib/modules/select_country/select_country_page.dart
  6. 247 0
      packages/cpt_job/lib/modules/attendance_review_list/attendance_review_controller.dart
  7. 329 0
      packages/cpt_job/lib/modules/attendance_review_list/attendance_review_item.dart
  8. 173 0
      packages/cpt_job/lib/modules/attendance_review_list/attendance_review_page.dart
  9. 187 0
      packages/cpt_job/lib/modules/attendance_review_list/attendance_review_reject_dialog.dart
  10. 12 0
      packages/cpt_job/lib/modules/attendance_review_list/attendance_review_state.dart
  11. 6 0
      packages/cpt_job/lib/router/job_service_impl.dart
  12. 7 0
      packages/cpt_job/lib/router/page_router.dart
  13. 15 15
      packages/cpt_job/lib/widget/applied_butch_modify.dart
  14. 10 10
      packages/cpt_job/lib/widget/applied_staff_reviews.dart
  15. 16 14
      packages/cpt_job_sg/lib/modules/job_applied/applied_staff_reviews.dart
  16. 1 0
      packages/cpt_job_sg/lib/modules/job_applied/job_applied_controller.dart
  17. 1 2
      packages/cpt_labour/lib/modules/labour_request_list/labour_request_filter.dart
  18. 1 1
      packages/cpt_labour/lib/modules/labour_request_list/labour_request_item.dart
  19. 196 0
      packages/cpt_labour/lib/modules/labour_review_edit/labour_review_edit_controller.dart
  20. 357 0
      packages/cpt_labour/lib/modules/labour_review_edit/labour_review_edit_page.dart
  21. 41 0
      packages/cpt_labour/lib/modules/labour_review_edit/labour_review_edit_state.dart
  22. 346 0
      packages/cpt_labour/lib/modules/labour_review_list/labour_review_controller.dart
  23. 318 0
      packages/cpt_labour/lib/modules/labour_review_list/labour_review_filter.dart
  24. 292 0
      packages/cpt_labour/lib/modules/labour_review_list/labour_review_item.dart
  25. 189 0
      packages/cpt_labour/lib/modules/labour_review_list/labour_review_page.dart
  26. 190 0
      packages/cpt_labour/lib/modules/labour_review_list/labour_review_reject_dialog.dart
  27. 16 0
      packages/cpt_labour/lib/modules/labour_review_list/labour_review_state.dart
  28. 102 0
      packages/cpt_labour/lib/modules/labour_review_workflow/labour_request_workflow_page.dart
  29. 10 0
      packages/cpt_labour/lib/modules/labour_review_workflow/labour_request_workflow_state.dart
  30. 96 0
      packages/cpt_labour/lib/modules/labour_review_workflow/labour_review_workflow_controller.dart
  31. 228 0
      packages/cpt_labour/lib/modules/labour_review_workflow/labour_review_workflow_item.dart
  32. 126 0
      packages/cpt_labour/lib/modules/labour_template_add/labour_template_add_controller.dart
  33. 327 0
      packages/cpt_labour/lib/modules/labour_template_add/labour_template_add_page.dart
  34. 45 0
      packages/cpt_labour/lib/modules/labour_template_add/labour_template_add_state.dart
  35. 7 0
      packages/cpt_labour/lib/modules/labour_template_list/add_edit_template.dart
  36. 175 0
      packages/cpt_labour/lib/modules/labour_template_list/labour_template_item.dart
  37. 215 0
      packages/cpt_labour/lib/modules/labour_template_list/labour_template_list_controller.dart
  38. 155 0
      packages/cpt_labour/lib/modules/labour_template_list/labour_template_list_page.dart
  39. 13 0
      packages/cpt_labour/lib/modules/labour_template_list/labour_template_list_state.dart
  40. 15 2
      packages/cpt_labour/lib/router/labour_service_impl.dart
  41. 35 2
      packages/cpt_labour/lib/router/page_router.dart
  42. 48 2
      packages/cpt_labour_sg/lib/modules/job_list_edit/job_list_edit_controller.dart
  43. 41 2
      packages/cpt_labour_sg/lib/modules/job_list_edit/job_list_edit_page.dart
  44. 6 2
      packages/cpt_labour_sg/lib/modules/job_list_edit/job_list_edit_state.dart
  45. 1 1
      packages/cpt_labour_sg/lib/modules/job_template_add/job_template_add_page.dart
  46. 0 2
      packages/cpt_labour_sg/lib/modules/job_template_list/job_template_list_controller.dart
  47. 42 2
      packages/cpt_labour_sg/lib/modules/labour_request_add/labour_request_add_controller.dart
  48. 41 6
      packages/cpt_labour_sg/lib/modules/labour_request_add/labour_request_add_page.dart
  49. 5 0
      packages/cpt_labour_sg/lib/modules/labour_request_add/labour_request_add_state.dart
  50. 84 0
      packages/cpt_report/lib/modules/report_labour_vn/report_labour_vn_controller.dart
  51. 219 0
      packages/cpt_report/lib/modules/report_labour_vn/report_labour_vn_item.dart
  52. 128 0
      packages/cpt_report/lib/modules/report_labour_vn/report_labour_vn_page.dart
  53. 10 0
      packages/cpt_report/lib/modules/report_labour_vn/report_labour_vn_state.dart
  54. 10 1
      packages/cpt_report/lib/modules/report_list/report_list_controller.dart
  55. 37 15
      packages/cpt_report/lib/modules/report_staff_request/report_staff_request_controller.dart
  56. 3 0
      packages/cpt_report/lib/modules/report_staff_request/report_staff_request_page.dart
  57. 8 0
      packages/cpt_report/lib/router/page_router.dart
  58. 36 2
      packages/cs_domain/lib/constants/api_constants.dart
  59. 70 0
      packages/cs_domain/lib/entity/response/attendance_review_entity.dart
  60. 2 0
      packages/cs_domain/lib/entity/response/job_list_edit_index_s_g_entity.dart
  61. 7 5
      packages/cs_domain/lib/entity/response/job_template_edit_index_entity.dart
  62. 3 1
      packages/cs_domain/lib/entity/response/job_template_s_g_entity.dart
  63. 99 0
      packages/cs_domain/lib/entity/response/labour_report_v_n_entity.dart
  64. 2 0
      packages/cs_domain/lib/entity/response/labour_request_s_g_add_index_entity.dart
  65. 58 0
      packages/cs_domain/lib/entity/response/labour_review_list_entity.dart
  66. 55 0
      packages/cs_domain/lib/entity/response/labour_review_status_entity.dart
  67. 28 0
      packages/cs_domain/lib/entity/response/staff_report_v_n_entity.dart
  68. 170 0
      packages/cs_domain/lib/generated/json/attendance_review_entity.g.dart
  69. 51 1
      packages/cs_domain/lib/generated/json/base/json_convert_content.dart
  70. 8 0
      packages/cs_domain/lib/generated/json/job_list_edit_index_s_g_entity.g.dart
  71. 7 0
      packages/cs_domain/lib/generated/json/job_template_edit_index_entity.g.dart
  72. 15 8
      packages/cs_domain/lib/generated/json/job_template_s_g_entity.g.dart
  73. 264 0
      packages/cs_domain/lib/generated/json/labour_report_v_n_entity.g.dart
  74. 9 1
      packages/cs_domain/lib/generated/json/labour_request_s_g_add_index_entity.g.dart
  75. 129 0
      packages/cs_domain/lib/generated/json/labour_review_list_entity.g.dart
  76. 114 0
      packages/cs_domain/lib/generated/json/labour_review_status_entity.g.dart
  77. 50 0
      packages/cs_domain/lib/generated/json/staff_report_v_n_entity.g.dart
  78. 91 0
      packages/cs_domain/lib/repository/job_repository.dart
  79. 5 4
      packages/cs_domain/lib/repository/job_sg_repository.dart
  80. 406 0
      packages/cs_domain/lib/repository/labour_repository.dart
  81. 23 13
      packages/cs_domain/lib/repository/labour_sg_repository.dart
  82. 73 6
      packages/cs_domain/lib/repository/other_repository.dart
  83. 0 2
      packages/cs_plugin_basic/lib/constants/app_constant.dart
  84. 2 2
      packages/cs_plugin_basic/lib/service/app_config_service.dart
  85. 5 1
      packages/cs_plugin_basic/lib/service/http_provider_injection.dart
  86. BIN
      packages/cs_resources/assets/cpt_auth/korea_icon.webp
  87. 1 0
      packages/cs_resources/lib/generated/assets.dart
  88. 6 3
      packages/cs_resources/lib/local/language/en_US.dart
  89. 258 0
      packages/cs_resources/lib/local/language/ko_KR.dart
  90. 2 0
      packages/cs_resources/lib/local/language/translation_service.dart
  91. 3 0
      packages/cs_resources/lib/local/language/vi_VN.dart
  92. 3 0
      packages/cs_resources/lib/local/language/zh_CN.dart
  93. 2 1
      packages/cs_router/lib/componentRouter/job_service.dart
  94. 4 1
      packages/cs_router/lib/componentRouter/labour_service.dart
  95. 7 0
      packages/cs_router/lib/path/router_path.dart
  96. 17 1
      packages/cs_widgets/lib/shatter/custom_check_box.dart
  97. 1 1
      packages/cs_widgets/lib/shatter/custom_radio_check.dart

+ 1 - 1
packages/cpt_auth/lib/modules/login/login_controller.dart

@@ -81,7 +81,7 @@ class LoginController extends GetxController with DioCancelableMixin {
     UserService.to.setToken(token);
 
     //保存是否是管理员登录
-    if (ConfigService.to.curSelectCountry.value == 1) {
+    if (ConfigService.to.selectCountry.value == 1) {
       //如果是新加坡用户,强制是管理员登录
       SPUtil.putInt(AppConstant.storageIsAdmin, 1);
     }else{

+ 9 - 5
packages/cpt_auth/lib/modules/login/login_page.dart

@@ -102,7 +102,11 @@ class _LoginPageState extends BaseState<LoginPage, LoginController> with StateLi
                     children: [
                       Obx(() {
                         return MyTextView(
-                          ConfigService.to.curSelectCountry == 0 ? "Vietnam".tr : "Singapore".tr,
+                          ConfigService.to.selectCountry.value == 1
+                              ? "Singapore".tr
+                              : ConfigService.to.selectCountry.value == 2
+                                  ? "Korea".tr
+                                  : "Vietnam".tr,
                           textColor: ColorConstants.white,
                           isFontMedium: true,
                           marginRight: 5,
@@ -141,10 +145,10 @@ class _LoginPageState extends BaseState<LoginPage, LoginController> with StateLi
                         //中间的输入框布局
                         Container(
                           width: double.infinity,
-                          margin: EdgeInsets.symmetric(vertical: 45, horizontal: 15),
-                          padding: EdgeInsets.symmetric(vertical: 33, horizontal: 20),
+                          margin: const EdgeInsets.symmetric(vertical: 45, horizontal: 15),
+                          padding: const EdgeInsets.symmetric(vertical: 33, horizontal: 20),
                           decoration: BoxDecoration(
-                            color: Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+                            color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
                             borderRadius: BorderRadius.circular(5.0), // 设置圆角
                           ),
                           child: Center(
@@ -214,7 +218,7 @@ class _LoginPageState extends BaseState<LoginPage, LoginController> with StateLi
                                 //选择签到功能还是全功能
                                 Obx(() {
                                   return Visibility(
-                                    visible: ConfigService.to.curSelectCountry == 0,
+                                    visible: ConfigService.to.selectCountry.value != 1,
                                     child: CustomRadioCheck(
                                       options: state.loginOption,
                                       onOptionSelected: (index, text) {

+ 6 - 7
packages/cpt_auth/lib/modules/main/main_controller.dart

@@ -9,7 +9,6 @@ import 'package:plugin_basic/service/user_service.dart';
 import 'package:plugin_platform/engine/sp/sp_util.dart';
 import 'package:plugin_platform/engine/toast/toast_engine.dart';
 import 'package:router/componentRouter/component_router_service.dart';
-import 'package:shared/utils/log_utils.dart';
 import 'package:widgets/load_state_layout.dart';
 import 'package:widgets/widget_export.dart';
 
@@ -120,7 +119,7 @@ class MainController extends GetxController {
   void gotoModulePage(HomeModule module) {
     switch (module.key) {
       case 'labReq':
-        if (ConfigService.to.curSelectCountry.value == 1) {
+        if (ConfigService.to.selectCountry.value == 1) {
           //新加坡的用工请求
           ComponentRouterServices.labourSGService.startLabourRequestPage();
         } else {
@@ -129,7 +128,7 @@ class MainController extends GetxController {
         }
         break;
       case 'jobList':
-        if (ConfigService.to.curSelectCountry.value == 1) {
+        if (ConfigService.to.selectCountry.value == 1) {
           ComponentRouterServices.labourSGService.startJobListPage();
         } else {
           //越南的工作列表
@@ -146,14 +145,14 @@ class MainController extends GetxController {
         ComponentRouterServices.reportService.startDeviceListPage();
         break;
       case 'reqReview':
-        Log.d("进入 LabourRequest 模块");
+        ComponentRouterServices.labourService.startLabourReviewPage();
         break;
       case 'attReview':
-        Log.d("进入 Attendance Review 模块");
+        ComponentRouterServices.jobService.startAttendanceReviewPage();
         break;
       case 'template':
-        //越南的
-        Log.d("进入越南的用工请求模板页面");
+        //越南的用工请求
+        ComponentRouterServices.labourService.startLabourTemplatePage();
         break;
       case 'jobTitle':
         //新加坡的用工请求

+ 9 - 5
packages/cpt_auth/lib/modules/select_country/select_country_controller.dart

@@ -4,23 +4,27 @@ import 'package:plugin_basic/constants/app_constant.dart';
 import 'package:plugin_basic/service/app_config_service.dart';
 import 'package:plugin_basic/service/http_provider_injection.dart';
 import 'package:plugin_platform/engine/sp/sp_util.dart';
-
+import 'package:shared/utils/log_utils.dart';
 
 class SelectCountryController extends GetxController {
-
   //设置下一步
   void setupNext() {
-    int country = ConfigService.to.curSelectCountry.value;
+    int country = ConfigService.to.selectCountry.value;
     SPUtil.putInt(AppConstant.storageSelectedCountry, country);
 
+
     String baseUrl;
-    if (country == 1){
+    if (country == 1) {
       //新加坡
       baseUrl = ApiConstants.sgBaseUrl;
-    }else {
+    } else if (country == 2) {
+      //韩国
+      baseUrl = ApiConstants.koreaBaseUrl;
+    } else {
       baseUrl = ApiConstants.baseUrl;
     }
 
+    Log.d("当前选择的国家:$country 当前要切换的域名为:$baseUrl");
     HttpProviderInjection.switchBaseUrl(baseUrl);
 
     Get.back();

+ 46 - 15
packages/cpt_auth/lib/modules/select_country/select_country_page.dart

@@ -95,15 +95,15 @@ class SelectCountryPage extends BaseStatelessPage<SelectCountryController> {
                       //新加坡的选项
                       Container(
                         width: double.infinity,
-                        margin: EdgeInsets.only(top: 48, left: 20, right: 20),
-                        padding: EdgeInsets.symmetric(vertical: 13, horizontal: 17),
+                        margin: const EdgeInsets.only(top: 48, left: 20, right: 20),
+                        padding: const EdgeInsets.symmetric(vertical: 13, horizontal: 17),
                         decoration: BoxDecoration(
-                          color: Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+                          color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
                           borderRadius: BorderRadius.circular(5.0), // 设置圆角
                         ),
                         child: Row(
                           children: [
-                            MyAssetImage(Assets.cptAuthSgIcon, width: 50, height: 33),
+                            const MyAssetImage(Assets.cptAuthSgIcon, width: 50, height: 33),
                             MyTextView(
                               "Singapore".tr,
                               marginLeft: 17,
@@ -113,28 +113,28 @@ class SelectCountryPage extends BaseStatelessPage<SelectCountryController> {
                             ).expanded(),
                             Obx(() {
                               return Visibility(
-                                visible: ConfigService.to.curSelectCountry.value == 1,
-                                child: MyAssetImage(Assets.cptAuthCheckedIcon, width: 22, height: 22),
+                                visible: ConfigService.to.selectCountry.value == 1,
+                                child: const MyAssetImage(Assets.cptAuthCheckedIcon, width: 22, height: 22),
                               );
                             }),
                           ],
                         ),
                       ).onTap(() {
-                        ConfigService.to.curSelectCountry.value = 1;
+                        ConfigService.to.selectCountry.value = 1;
                       }),
 
                       //越南的选项
                       Container(
                         width: double.infinity,
-                        margin: EdgeInsets.only(top: 13.5, left: 20, right: 20),
-                        padding: EdgeInsets.symmetric(vertical: 13, horizontal: 17),
+                        margin: const EdgeInsets.only(top: 13.5, left: 20, right: 20),
+                        padding: const EdgeInsets.symmetric(vertical: 13, horizontal: 17),
                         decoration: BoxDecoration(
-                          color: Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+                          color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
                           borderRadius: BorderRadius.circular(5.0), // 设置圆角
                         ),
                         child: Row(
                           children: [
-                            MyAssetImage(Assets.cptAuthVnIcon, width: 50, height: 33),
+                            const MyAssetImage(Assets.cptAuthVnIcon, width: 50, height: 33),
                             MyTextView(
                               "Vietnam".tr,
                               marginLeft: 17,
@@ -144,16 +144,47 @@ class SelectCountryPage extends BaseStatelessPage<SelectCountryController> {
                             ).expanded(),
                             Obx(() {
                               return Visibility(
-                                visible: ConfigService.to.curSelectCountry.value == 0,
-                                child: MyAssetImage(Assets.cptAuthCheckedIcon, width: 22, height: 22),
+                                visible: ConfigService.to.selectCountry.value == 0,
+                                child: const MyAssetImage(Assets.cptAuthCheckedIcon, width: 22, height: 22),
                               );
                             }),
                           ],
                         ),
                       ).onTap(() {
-                        ConfigService.to.curSelectCountry.value = 0;
+                        ConfigService.to.selectCountry.value = 0;
                       }),
 
+                      //韩国的选项
+                      // 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.cptAuthKoreaIcon, width: 50, height: 33),
+                      //       MyTextView(
+                      //         "Korea".tr,
+                      //         marginLeft: 17,
+                      //         textColor: ColorConstants.white,
+                      //         isFontMedium: true,
+                      //         fontSize: 18,
+                      //       ).expanded(),
+                      //       Obx(() {
+                      //         return Visibility(
+                      //           visible: ConfigService.to.selectCountry.value == 2,
+                      //           child: const MyAssetImage(Assets.cptAuthCheckedIcon, width: 22, height: 22),
+                      //         );
+                      //       }),
+                      //     ],
+                      //   ),
+                      // ).onTap(() {
+                      //   ConfigService.to.selectCountry.value = 2;
+                      // }),
+
                       //Next按钮
                       Stack(
                         children: [
@@ -171,7 +202,7 @@ class SelectCountryPage extends BaseStatelessPage<SelectCountryController> {
                             backgroundColor: hexToColor("#FFBB1B"),
                             fontWeight: FontWeight.w500,
                           ),
-                          Positioned(
+                          const Positioned(
                             right: 28,
                             top: 0,
                             bottom: 0,

+ 247 - 0
packages/cpt_job/lib/modules/attendance_review_list/attendance_review_controller.dart

@@ -0,0 +1,247 @@
+import 'package:domain/entity/response/attendance_review_entity.dart';
+import 'package:domain/repository/job_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: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 'attendance_review_reject_dialog.dart';
+import 'attendance_review_state.dart';
+
+class AttendanceReviewController extends GetxController with DioCancelableMixin {
+  final JobRepository _jobRepository = Get.find();
+  final AttendanceReviewState state = AttendanceReviewState();
+
+  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);
+    }
+
+    // 并发执行两个请求
+    final listResult = await _jobRepository.fetchAttendanceReviewList(
+      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<AttendanceReviewRows>? 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 = "";
+
+    //赋值之后刷新
+    refreshController.callRefresh();
+  }
+
+  /// Item选中与未选中设置
+  void doSelectedOrNot(AttendanceReviewRows data) {
+    data.isSelected = !data.isSelected;
+    Log.d("isSelected:${data.isSelected}");
+    update();
+  }
+
+  /// 执行批量同意
+  void _requestBatchApprove(String recordIds) async {
+    //执行请求
+    var result = await _jobRepository.approveAttendanceReviews(
+      recordIds,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess("Successful".tr);
+
+      //调用接口刷新指定的Staff的信息
+      _removeItemsByList(recordIds);
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+      return;
+    }
+  }
+
+  /// 执行批量拒绝
+  void _requestBatchReject(String recordIds, String? reason) async {
+    //执行请求
+    var result = await _jobRepository.rejectLabourReviews(
+      recordIds,
+      reason,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess("Successful".tr);
+
+      //调用接口刷新指定的Staff的信息
+      _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: AttendaceReviewRejectDialog(
+          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 gotoStatusViewPage(AttendanceReviewRows data) {}
+}

+ 329 - 0
packages/cpt_job/lib/modules/attendance_review_list/attendance_review_item.dart

@@ -0,0 +1,329 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/response/attendance_review_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_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+/*
+ * 用工审核的列表Item
+ */
+class AttendanceReviewItem extends StatelessWidget {
+  final int index;
+  final AttendanceReviewRows item;
+  final VoidCallback? onStatusAction;
+  final VoidCallback? onItemAction;
+
+  AttendanceReviewItem({
+    required this.index,
+    required this.item,
+    this.onStatusAction,
+    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(
+                "Staff Name:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //姓名
+              MyTextView(
+                item.staffName ?? "-",
+                isFontRegular: true,
+                textColor: ColorConstants.white,
+                fontSize: 14,
+                marginLeft: 5,
+                marginRight: 5,
+              ).expanded(),
+
+              //是否选中
+              MyAssetImage(
+                item.isSelected ? Assets.baseServiceItemSelectedIcon : Assets.baseServiceItemUnselectedIcon,
+                width: 20.5,
+                height: 20.5,
+              ),
+            ],
+          ),
+
+          // 工作标题
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "${"Title".tr}:",
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              MyTextView(
+                item.jobTitle ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 12),
+
+          // 部门
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "${"Outlet".tr}:",
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              MyTextView(
+                item.departmentName ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 12),
+
+          // 工作时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "DateTime:".tr,
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              MyTextView(
+                item.jobTime ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 12),
+
+          //考勤时间
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "${"Clock Time".tr}:",
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //时间
+              MyTextView(
+                "${item.clockIn} - ${item.clockOut}",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: 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(
+                Utils.isNotEmpty(item.adjustShow) ? item.adjustShow! : "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 (Hrs/Rms)".tr}:",
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //小时
+              MyTextView(
+                item.totalShow.toString(),
+                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),
+
+          // 创建时间
+          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: 12),
+
+          // //按钮组
+          // Visibility(
+          //   visible: item.actionList?.isNotEmpty ?? false,
+          //   child: Row(
+          //     mainAxisSize: MainAxisSize.max,
+          //     mainAxisAlignment: MainAxisAlignment.end,
+          //     crossAxisAlignment: CrossAxisAlignment.center,
+          //     children: [
+          //       //编辑按钮
+          //       Visibility(
+          //         visible: item.actionList?.contains("edit") ?? false,
+          //         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: item.actionList?.contains("status") ?? false,
+          //         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: item.actionList?.contains("remarks") ?? false,
+          //         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();
+    });
+  }
+}

+ 173 - 0
packages/cpt_job/lib/modules/attendance_review_list/attendance_review_page.dart

@@ -0,0 +1,173 @@
+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_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'attendance_review_item.dart';
+import 'attendance_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 'attendance_review_state.dart';
+
+/*
+ * 工作考勤的审核列表
+ */
+class AttendanceReviewPage extends BaseStatefulPage<AttendanceReviewController> {
+  AttendanceReviewPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance() {
+    return Get.start(RouterPath.jobAttendanceReviewList);
+  }
+
+  @override
+  AttendanceReviewController createRawController() {
+    return AttendanceReviewController();
+  }
+
+  @override
+  State<AttendanceReviewPage> createState() => _LabourReviewState();
+}
+
+class _LabourReviewState extends BaseState<AttendanceReviewPage, AttendanceReviewController> {
+  late AttendanceReviewState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return SafeArea(
+        bottom: true,
+        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),
+
+                ],
+              ),
+
+              //底部的列表
+              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 AttendanceReviewItem(
+                          index: index,
+                          item: state.datas[index],
+                          onStatusAction: () {
+
+                          },
+                          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(),
+                ],
+              ),
+            ],
+          ),
+        ),
+      );
+    });
+  }
+}

+ 187 - 0
packages/cpt_job/lib/modules/attendance_review_list/attendance_review_reject_dialog.dart

@@ -0,0 +1,187 @@
+import 'dart:ui';
+
+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/widget_export.dart';
+
+/*
+ * 拒绝的弹窗
+ */
+class AttendaceReviewRejectDialog extends StatefulWidget {
+
+  void Function(String reason)? confirmAction;
+
+  AttendaceReviewRejectDialog({this.confirmAction});
+
+  @override
+  State<AttendaceReviewRejectDialog> createState() => _AttendaceReviewRejectDialogState();
+}
+
+class _AttendaceReviewRejectDialogState extends State<AttendaceReviewRejectDialog> {
+
+  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);
+  }
+}

+ 12 - 0
packages/cpt_job/lib/modules/attendance_review_list/attendance_review_state.dart

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

+ 6 - 0
packages/cpt_job/lib/router/job_service_impl.dart

@@ -3,6 +3,7 @@ import 'package:plugin_basic/basic_export.dart';
 import 'package:router/componentRouter/job_service.dart';
 import 'package:shared/utils/log_utils.dart';
 
+import '../modules/attendance_review_list/attendance_review_page.dart';
 import '../modules/sign_in_sign_out/sign_in_sign_out_page.dart';
 
 class JobServiceImpl extends GetxService implements JobService {
@@ -31,4 +32,9 @@ class JobServiceImpl extends GetxService implements JobService {
     JobListPage.startInstance();
   }
 
+  @override
+  void startAttendanceReviewPage() {
+    AttendanceReviewPage.startInstance();
+  }
+
 }

+ 7 - 0
packages/cpt_job/lib/router/page_router.dart

@@ -6,6 +6,7 @@ import 'package:cpt_job/modules/job_applied_edit/job_applied_edit_page.dart';
 import 'package:get/get.dart';
 import 'package:router/path/router_path.dart';
 
+import '../modules/attendance_review_list/attendance_review_page.dart';
 import '../modules/job_list/job_list_page.dart';
 import '../modules/job_list_detail/job_list_detail_page.dart';
 import '../modules/sign_in_sign_out/sign_in_sign_out_page.dart';
@@ -60,5 +61,11 @@ class JobPageRouter {
       page: () => AppliedStaffReviewsPage(),
     ),
 
+    //工作考勤的审核列表
+    GetPage(
+      name: RouterPath.jobAttendanceReviewList,
+      page: () => AttendanceReviewPage(),
+    ),
+
   ];
 }

+ 15 - 15
packages/cpt_job/lib/widget/applied_butch_modify.dart

@@ -17,7 +17,7 @@ import 'package:widgets/picker/date_picker_util.dart';
 import 'package:widgets/shatter/form_require_text.dart';
 import 'package:widgets/widget_export.dart';
 
-/**
+/*
  * 批量操作修改时间的弹窗
  */
 class AppliedButchModify extends StatefulWidget {
@@ -50,11 +50,11 @@ class _AppliedButchModifyState extends State<AppliedButchModify> {
       children: [
         //Title (如果使用 Container 为最外层容器则默认为 match_parent 的效果,除非我们限制宽度和最大高度最小高度)
         Container(
-          padding: EdgeInsets.symmetric(horizontal: 16.5),
+          padding: const EdgeInsets.symmetric(horizontal: 16.5),
           width: double.infinity,
-          decoration: BoxDecoration(
+          decoration: const BoxDecoration(
             color: Colors.white,
-            borderRadius: const BorderRadius.all(Radius.circular(15)),
+            borderRadius: BorderRadius.all(Radius.circular(15)),
           ),
           child: Column(
             crossAxisAlignment: CrossAxisAlignment.start,
@@ -79,12 +79,12 @@ class _AppliedButchModifyState extends State<AppliedButchModify> {
 
               //选择时间
               Container(
-                padding: EdgeInsets.only(left: 16, right: 10),
-                margin: EdgeInsets.only(top: 10),
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
                 height: 45,
-                decoration: BoxDecoration(
+                decoration: const BoxDecoration(
                   color: ColorConstants.grayECECEC,
-                  borderRadius: const BorderRadius.all(Radius.circular(5)),
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
                 ),
                 child: Row(
                   mainAxisSize: MainAxisSize.max,
@@ -116,12 +116,12 @@ class _AppliedButchModifyState extends State<AppliedButchModify> {
 
               //选择结束日期
               Container(
-                padding: EdgeInsets.only(left: 16, right: 10),
-                margin: EdgeInsets.only(top: 10),
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
                 height: 45,
-                decoration: BoxDecoration(
+                decoration: const BoxDecoration(
                   color: ColorConstants.grayECECEC,
-                  borderRadius: const BorderRadius.all(Radius.circular(5)),
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
                 ),
                 child: Row(
                   mainAxisSize: MainAxisSize.max,
@@ -144,8 +144,8 @@ class _AppliedButchModifyState extends State<AppliedButchModify> {
               }),
 
               Container(
-                margin: EdgeInsets.only(top: 25),
-                color: Color(0XFFCECECE),
+                margin: const EdgeInsets.only(top: 25),
+                color: const Color(0XFFCECECE),
                 height: 0.5,
               ),
               Row(
@@ -193,7 +193,7 @@ class _AppliedButchModifyState extends State<AppliedButchModify> {
                           fontSize: 17.5,
                           isFontMedium: true,
                           textAlign: TextAlign.center,
-                          textColor: Color(0XFF0085C4),
+                          textColor: const Color(0XFF0085C4),
                           cornerRadius: 3,
                         ),
                       )),

+ 10 - 10
packages/cpt_job/lib/widget/applied_staff_reviews.dart

@@ -15,7 +15,7 @@ import 'package:widgets/my_text_view.dart';
 import 'package:widgets/shatter/rating_widget.dart';
 import 'package:widgets/widget_export.dart';
 
-/**
+/*
  * 员工的评价弹窗
  */
 class AppliedStaffReviews extends StatefulWidget {
@@ -61,9 +61,9 @@ class _AppliedStaffReviewsState extends State<AppliedStaffReviews> {
         //Title (如果使用 Container 为最外层容器则默认为 match_parent 的效果,除非我们限制宽度和最大高度最小高度)
         Container(
           width: double.infinity,
-          decoration: BoxDecoration(
+          decoration: const BoxDecoration(
             color: Colors.white,
-            borderRadius: const BorderRadius.all(Radius.circular(15)),
+            borderRadius: BorderRadius.all(Radius.circular(15)),
           ),
           child: Column(
             crossAxisAlignment: CrossAxisAlignment.start,
@@ -189,12 +189,12 @@ class _AppliedStaffReviewsState extends State<AppliedStaffReviews> {
               IgnoreKeyboardDismiss(
                 child: Container(
                   height: 130,
-                  margin: EdgeInsets.symmetric(vertical: 19, horizontal: 22),
-                  padding: EdgeInsets.symmetric(vertical: 15, horizontal: 15),
+                  margin: const EdgeInsets.symmetric(vertical: 19, horizontal: 22),
+                  padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 15),
                   decoration: BoxDecoration(
-                    color: Color(0xFFF0F0F0),
+                    color: const Color(0xFFF0F0F0),
                     border: Border.all(
-                      color: Color(0xFFD8D8D8),
+                      color: const Color(0xFFD8D8D8),
                       width: 0.5,
                     ),
                   ),
@@ -211,13 +211,13 @@ class _AppliedStaffReviewsState extends State<AppliedStaffReviews> {
                       isCollapsed: true,
                       border: InputBorder.none,
                       hintText: "Enter...".tr,
-                      hintStyle: TextStyle(
+                      hintStyle: const TextStyle(
                         color: ColorConstants.black66,
                         fontSize: 15.0,
                         fontWeight: FontWeight.w400,
                       ),
                     ),
-                    style: TextStyle(
+                    style: const TextStyle(
                       color: ColorConstants.black,
                       fontSize: 15.0,
                       fontWeight: FontWeight.w400,
@@ -233,7 +233,7 @@ class _AppliedStaffReviewsState extends State<AppliedStaffReviews> {
 
               // 分割线
               Container(
-                color: Color(0XFFCECECE),
+                color: const Color(0XFFCECECE),
                 height: 0.5,
               ),
 

+ 16 - 14
packages/cpt_job_sg/lib/modules/job_applied/applied_staff_reviews.dart

@@ -15,7 +15,7 @@ import 'package:widgets/my_text_view.dart';
 import 'package:widgets/shatter/rating_widget.dart';
 import 'package:widgets/widget_export.dart';
 
-/**
+/*
  * 员工的评价弹窗
  */
 class AppliedStaffReviews extends StatefulWidget {
@@ -61,9 +61,9 @@ class _AppliedStaffReviewsState extends State<AppliedStaffReviews> {
         //Title (如果使用 Container 为最外层容器则默认为 match_parent 的效果,除非我们限制宽度和最大高度最小高度)
         Container(
           width: double.infinity,
-          decoration: BoxDecoration(
+          decoration: const BoxDecoration(
             color: Colors.white,
-            borderRadius: const BorderRadius.all(Radius.circular(15)),
+            borderRadius: BorderRadius.all(Radius.circular(15)),
           ),
           child: Column(
             crossAxisAlignment: CrossAxisAlignment.start,
@@ -189,12 +189,12 @@ class _AppliedStaffReviewsState extends State<AppliedStaffReviews> {
               IgnoreKeyboardDismiss(
                 child: Container(
                   height: 130,
-                  margin: EdgeInsets.symmetric(vertical: 19, horizontal: 22),
-                  padding: EdgeInsets.symmetric(vertical: 15, horizontal: 15),
+                  margin: const EdgeInsets.symmetric(vertical: 19, horizontal: 22),
+                  padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 15),
                   decoration: BoxDecoration(
-                    color: Color(0xFFF0F0F0),
+                    color: const Color(0xFFF0F0F0),
                     border: Border.all(
-                      color: Color(0xFFD8D8D8),
+                      color: const Color(0xFFD8D8D8),
                       width: 0.5,
                     ),
                   ),
@@ -211,13 +211,13 @@ class _AppliedStaffReviewsState extends State<AppliedStaffReviews> {
                       isCollapsed: true,
                       border: InputBorder.none,
                       hintText: "Enter...".tr,
-                      hintStyle: TextStyle(
+                      hintStyle: const TextStyle(
                         color: ColorConstants.black66,
                         fontSize: 15.0,
                         fontWeight: FontWeight.w400,
                       ),
                     ),
-                    style: TextStyle(
+                    style: const TextStyle(
                       color: ColorConstants.black,
                       fontSize: 15.0,
                       fontWeight: FontWeight.w400,
@@ -233,7 +233,7 @@ class _AppliedStaffReviewsState extends State<AppliedStaffReviews> {
 
               // 分割线
               Container(
-                color: Color(0XFFCECECE),
+                color: const Color(0XFFCECECE),
                 height: 0.5,
               ),
 
@@ -251,20 +251,22 @@ class _AppliedStaffReviewsState extends State<AppliedStaffReviews> {
                           fontSize: 17.5,
                           isFontMedium: true,
                           textAlign: TextAlign.center,
-                          textColor: Color(0XFF0085C4),
+                          textColor: const Color(0XFF0085C4),
                           cornerRadius: 3,
                           borderWidth: 1,
                         ),
                       )),
                   Container(
-                    color: Color(0xff09141F).withOpacity(0.13),
+                    color: const Color(0xff09141F).withOpacity(0.13),
                     width: 0.5,
                   ),
                   Expanded(
                       flex: 1,
                       child: InkWell(
                         onTap: () {
-                          doCallbackAction();
+                          if (Utils.isEmpty(reviews.feedback)) {
+                            doCallbackAction();
+                          }
                         },
                         child: MyTextView(
                           "Submit".tr,
@@ -272,7 +274,7 @@ class _AppliedStaffReviewsState extends State<AppliedStaffReviews> {
                           fontSize: 17.5,
                           isFontMedium: true,
                           textAlign: TextAlign.center,
-                          textColor: Color(0XFF0085C4),
+                          textColor: Utils.isEmpty(reviews.feedback) ? const Color(0XFF0085C4) : ColorConstants.gray88,
                           cornerRadius: 3,
                         ),
                       )),

+ 1 - 0
packages/cpt_job_sg/lib/modules/job_applied/job_applied_controller.dart

@@ -330,6 +330,7 @@ class JobAppliedController extends GetxController with DioCancelableMixin {
   void _requestBatchModify(DateTime? start, DateTime? end, String? hour, String? status, String separatedIds) async {
     //执行请求
     var result = await _jobRepository.batchEditJobApplied(
+      state.jobId,
       separatedIds,
       start == null ? "" : DateTimeUtils.formatDate(start, format: "HH:mm:ss"),
       end == null ? "" : DateTimeUtils.formatDate(end, format: "HH:mm:ss"),

+ 1 - 2
packages/cpt_labour/lib/modules/labour_request_list/labour_request_filter.dart

@@ -7,7 +7,6 @@ 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';
@@ -19,7 +18,7 @@ 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 {

+ 1 - 1
packages/cpt_labour/lib/modules/labour_request_list/labour_request_item.dart

@@ -8,7 +8,7 @@ import 'package:widgets/ext/ex_widget.dart';
 import 'package:widgets/my_button.dart';
 import 'package:widgets/my_text_view.dart';
 
-/**
+/*
  * 用工请求的主页面列表Item
  */
 class LabourRequestItem extends StatelessWidget {

+ 196 - 0
packages/cpt_labour/lib/modules/labour_review_edit/labour_review_edit_controller.dart

@@ -0,0 +1,196 @@
+import 'package:domain/entity/response/labour_request_edit_index_entity.dart';
+import 'package:domain/repository/labour_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: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/picker/date_picker_util.dart';
+import 'package:widgets/picker/option_pick_util.dart';
+
+import 'labour_review_edit_state.dart';
+
+class LabourReviewEditController extends GetxController with DioCancelableMixin {
+  final LabourRepository _labourRepository = Get.find();
+  final LabourReviewEditState state = LabourReviewEditState();
+
+  /// 获取首页的数据
+  void fetchRequestDetail() async {
+    //获取到数据
+
+    final taskFuture = _labourRepository.fetchLabourReviewDetail(state.requestId, cancelToken: cancelToken);
+
+    var result = await taskFuture;
+
+    //处理数据
+    if (result.isSuccess) {
+      state.labReqOption = result.data;
+      state.selectedStartTime = state.labReqOption?.jobStart == null ? null : DateTimeUtils.getDateTime(state.labReqOption!.jobStart!);
+      state.selectedEndTime = state.labReqOption?.jobEnd == null ? null : DateTimeUtils.getDateTime(state.labReqOption!.jobEnd!);
+      state.noStaff = state.labReqOption?.needNum.toString() == "0" ? "" : state.labReqOption?.needNum.toString();
+      var needNumController = state.formData['no_of_staff']!['controller'];
+      needNumController.text = state.noStaff;
+      var amountController = state.formData['amount']!['controller'];
+      amountController.text = state.labReqOption?.amount ?? "";
+      state.selectedTemplateId = state.labReqOption?.templateId;
+      state.selectedDepartmentId = state.labReqOption?.departmentId;
+
+      state.chargeOptionId = state.labReqOption?.chargeList.firstWhere((element) => element.checked == "checked").value;
+
+      update();
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    fetchRequestDetail();
+  }
+
+  /// 提交
+  void doSubmit() async {
+    var needNumController = state.formData['no_of_staff']!['controller'];
+    var amountController = state.formData['amount']!['controller'];
+    String needNum = needNumController.text.toString();
+    String amount = amountController.text.toString();
+
+    if (Utils.isEmpty(state.selectedTemplateId)) {
+      ToastEngine.show("Choose Job Title".tr);
+      return;
+    } else if (state.selectedStartTime == null) {
+      ToastEngine.show("Choose Start Date".tr);
+      return;
+    } else if (state.selectedEndTime == null) {
+      ToastEngine.show("Choose End Date".tr);
+      return;
+    } else if (Utils.isEmpty(state.selectedDepartmentId)) {
+      ToastEngine.show("Choose Outlet".tr);
+      return;
+    } else if (Utils.isEmpty(needNum)) {
+      ToastEngine.show("Enter No. of Staff".tr);
+      return;
+    } else if (state.labReqOption?.serviceType == 1 && Utils.isEmpty(amount)) {
+      ToastEngine.show("Enter Amount".tr);
+      return;
+    }
+
+
+    var result = await _labourRepository.editLabourReviewSubmit(
+      state.requestId,
+      state.selectedTemplateId,
+      DateTimeUtils.formatDate(state.selectedStartTime),
+      DateTimeUtils.formatDate(state.selectedEndTime),
+      state.selectedDepartmentId,
+      needNum,
+      state.chargeOptionId,
+      state.labReqOption?.serviceType == 1 ? amount : null,
+    );
+
+    //处理数据
+    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.labReqOption == null || state.pageType == 2) {
+      return;
+    }
+
+    int selectedTemplateIndex;
+    if (state.selectedTemplateId == null) {
+      selectedTemplateIndex = 0;
+    } else {
+      selectedTemplateIndex = state.labReqOption!.templateList.indexWhere((department) => department.value.toString() == state.selectedTemplateId);
+    }
+
+    if (selectedTemplateIndex < 0) {
+      selectedTemplateIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: state.labReqOption!.templateList.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedTemplateIndex,
+      onPickerChanged: (_, index) {
+        state.selectedTemplateId = state.labReqOption!.templateList[index].value!.toString();
+        update();
+      },
+    );
+  }
+
+  //选择开始时间
+  void pickStartTime() {
+    if (state.labReqOption == null || state.pageType == 2) {
+      return;
+    }
+
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.selectedStartTime,
+      mode: CupertinoDatePickerMode.dateAndTime,
+      onDateTimeChanged: (date) {
+        state.selectedStartTime = date;
+        update();
+      },
+      title: "Start Time".tr,
+    );
+  }
+
+  // 选择结束时间
+  void pickEndTime() {
+    if (state.labReqOption == null || state.pageType == 2) {
+      return;
+    }
+
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.selectedEndTime ?? state.selectedStartTime,
+      mode: CupertinoDatePickerMode.dateAndTime,
+      onDateTimeChanged: (date) {
+        state.selectedEndTime = date;
+        update();
+      },
+      title: "End Time".tr,
+    );
+  }
+
+  // 筛选部门
+  void pickDepartment() {
+    if (state.labReqOption == null || state.pageType == 2) {
+      return;
+    }
+
+    int selectedDepartmentIndex;
+    if (state.selectedDepartmentId == null) {
+      selectedDepartmentIndex = 0;
+    } else {
+      selectedDepartmentIndex = state.labReqOption!.departmentList.indexWhere((department) => department.value.toString() == state.selectedDepartmentId);
+    }
+
+    if (selectedDepartmentIndex < 0) {
+      selectedDepartmentIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: state.labReqOption!.departmentList.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedDepartmentIndex,
+      onPickerChanged: (_, index) {
+        state.selectedDepartmentId = state.labReqOption!.departmentList[index].value!.toString();
+        update();
+      },
+    );
+  }
+}

+ 357 - 0
packages/cpt_labour/lib/modules/labour_review_edit/labour_review_edit_page.dart

@@ -0,0 +1,357 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/response/labour_request_edit_index_entity.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/custom_radio_check.dart';
+import 'package:widgets/shatter/form_require_text.dart';
+import 'package:widgets/shatter/round_my_text_field.dart';
+
+import 'labour_review_edit_controller.dart';
+import 'labour_review_edit_state.dart';
+
+class LabourReviewEditPage extends BaseStatefulPage<LabourReviewEditController> {
+  LabourReviewEditPage({Key? key}) : super(key: key);
+
+  //启动当前页面,pageType  1是编辑  2是详情
+  static void startInstance(int pageType, String? requestId,void Function(dynamic value)? cb) {
+    return Get.start(RouterPath.labourReviewEdit, arguments: {'pageType': pageType, 'requestId': requestId,'cb': cb});
+  }
+
+  @override
+  LabourReviewEditController createRawController() {
+    return LabourReviewEditController();
+  }
+
+  @override
+  State<LabourReviewEditPage> createState() => _LabourReviewEditState();
+}
+
+class _LabourReviewEditState extends BaseState<LabourReviewEditPage, LabourReviewEditController> {
+  late LabourReviewEditState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+    state.pageType = Get.arguments['pageType'];
+    state.requestId = Get.arguments['requestId'];
+    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: true,
+          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.selectedTemplateId == null || state.selectedTemplateId == "0"
+                                  ? ""
+                                  : state.labReqOption!.templateList
+                                          .firstWhere((element) => element.value.toString() == state.selectedTemplateId,
+                                              orElse: () => LabourRequestEditIndexTemplateList())
+                                          .txt ??
+                                      "",
+                              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();
+                        controller.pickJobTitle();
+                      }),
+
+                      //开始时间
+                      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(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),
+                              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();
+                        controller.pickStartTime();
+                      }),
+
+                      //结束时间
+                      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(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),
+                              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();
+                        controller.pickEndTime();
+                      }),
+
+                      //工作选择部门
+                      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.selectedDepartmentId == null || state.selectedDepartmentId == "0"
+                                  ? ""
+                                  : state.labReqOption!.departmentList
+                                          .firstWhere((element) => element.value.toString() == state.selectedDepartmentId,
+                                              orElse: () => LabourRequestEditIndexDepartmentList())
+                                          .txt ??
+                                      "",
+                              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();
+                        controller.pickDepartment();
+                      }),
+
+                      //需要的人数
+                      FormRequireText(
+                        text: "No. of Staff".tr,
+                      ).marginOnly(top: 15),
+
+                      //输入框(只允许输入数字)
+                      CustomTextField(
+                        formKey: "no_of_staff",
+                        marginLeft: 0,
+                        marginRight: 0,
+                        paddingTop: 0,
+                        paddingBottom: 0,
+                        height: 45,
+                        fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                        enabled: state.pageType != 2,
+                        inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
+                        textInputType: TextInputType.number,
+                        formData: state.formData,
+                        textInputAction: TextInputAction.done,
+                        onSubmit: (key, value) {
+                          FocusScope.of(context).unfocus();
+                        },
+                        marginTop: 10,
+                      ),
+
+                      FormRequireText(
+                        text: "Salary By".tr,
+                      ).marginOnly(top: 15),
+
+                      //选择计费类型
+                      state.labReqOption != null
+                          ? CustomRadioCheck(
+                              options: state.labReqOption!.chargeList.map((e) => e.txt).whereType<String>().toList(), //后台返回的数据展示,并且根据后台的数据匹配索引
+                              selectedPosition: state.labReqOption!.chargeList.indexWhere((element) => element.checked == "checked"),
+                              onOptionSelected: (index, text) {
+                                //修改内存的值
+                                state.chargeOptionId = state.labReqOption!.chargeList[index].value;
+                              },
+                            ).marginOnly(top: 15)
+                          : const CircularProgressIndicator(),
+
+                      //选择是否需要输入金额
+                      Visibility(
+                        visible: state.labReqOption?.serviceType == 1,
+                        child: FormRequireText(
+                          text: "Amount".tr,
+                        ).marginOnly(top: 15),
+                      ),
+
+                      Visibility(
+                        visible: state.labReqOption?.serviceType == 1,
+                        child: CustomTextField(
+                          formKey: "amount",
+                          marginLeft: 0,
+                          marginRight: 0,
+                          paddingTop: 0,
+                          paddingBottom: 0,
+                          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();
+                          },
+                          marginTop: 10,
+                        ),
+                      ),
+
+                      //提交按钮
+                      Visibility(
+                        visible: state.pageType != 2,
+                        child: 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),
+                ),
+              ),
+            ),
+          ),
+        ),
+      );
+    });
+  }
+}

+ 41 - 0
packages/cpt_labour/lib/modules/labour_review_edit/labour_review_edit_state.dart

@@ -0,0 +1,41 @@
+import 'package:domain/entity/response/labour_request_edit_index_entity.dart';
+import 'package:flutter/material.dart';
+import 'package:plugin_basic/basic_export.dart';
+
+class LabourReviewEditState {
+
+  int pageType = 2;  //页面的状态 1是编辑  2是详情
+  String? requestId;  //编辑和详情需要用到ID
+  void Function(dynamic value)? cb;
+
+  //页面对应的详情数据
+  LabourRequestEditIndexEntity? labReqOption;
+
+  //页面对应的选择的条件
+  DateTime? selectedStartTime;
+  DateTime? selectedEndTime;
+  String? selectedTemplateId;
+  String? selectedDepartmentId;
+  String? noStaff;  //成员数量
+
+  String? chargeOptionId;
+
+  //表单的校验与数据
+  Map<String, Map<String, dynamic>> formData = {
+    'no_of_staff': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter No. of Staff'.tr,
+      'obsecure': false,
+    },
+    'amount': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter Amount'.tr,
+      'obsecure': false,
+    },
+  };
+
+}

+ 346 - 0
packages/cpt_labour/lib/modules/labour_review_list/labour_review_controller.dart

@@ -0,0 +1,346 @@
+import 'package:domain/entity/response/labour_request_index_entity.dart';
+import 'package:domain/entity/response/labour_review_list_entity.dart';
+import 'package:domain/repository/labour_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: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_review_edit/labour_review_edit_page.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 LabourRepository _labourRepository = 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 = [
+      _labourRepository.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
+          ? _labourRepository.fetchLabourReviewIndex(
+              cancelToken: cancelToken,
+            )
+          : Future(() => HttpResult(isSuccess: true).convert(data: state.indexOptions!)),
+    ];
+
+    //拿到结果
+    var results = await Future.wait(futures);
+    var listResult = results[0] as HttpResult<LabourReviewListEntity>;
+    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<LabourReviewListRows>? 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(LabourReviewListRows data) {
+    data.isSelected = !data.isSelected;
+    Log.d("isSelected:${data.isSelected}");
+    update();
+  }
+
+  /// 执行批量同意
+  void _requestBatchApprove(String recordIds) async {
+    //执行请求
+    var result = await _labourRepository.approveLabourReviews(
+      recordIds,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess("Successful".tr);
+
+      //调用接口刷新指定的Staff的信息
+      _removeItemsByList(recordIds);
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+      return;
+    }
+  }
+
+  /// 执行批量拒绝
+  void _requestBatchReject(String recordIds, String? reason) async {
+    //执行请求
+    var result = await _labourRepository.rejectLabourReviews(
+      recordIds,
+      reason,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess("Successful".tr);
+
+      //调用接口刷新指定的Staff的信息
+      _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(LabourReviewListRows data) {
+    LabourReviewEditPage.startInstance(2, data.requestId, null);
+  }
+
+  /// 去编辑页面
+  void gotoEditPage(LabourReviewListRows data) {
+    LabourReviewEditPage.startInstance(1, data.requestId, (result) {
+      if (Utils.isNotEmpty(result)) {
+        fetchItemByIdAndRefreshItem(data.recordId);
+      }
+    });
+  }
+
+  /// 去用工审核流程页面
+  void gotoStatusViewPage(LabourReviewListRows data) {
+    LabourReviewWorkflowPage.startInstance(data.requestId);
+  }
+
+  /// 根据ID获取Item对象,用于刷新
+  void fetchItemByIdAndRefreshItem(String? recordId) async {
+    var result = await _labourRepository.fetchItemByRecordId(
+      recordId,
+      cancelToken: cancelToken,
+    );
+
+    //处理数据
+    if (result.isSuccess) {
+      var data = result.data;
+      if (data != null && data.rows.isNotEmpty) {
+        List<LabourReviewListRows> newItem = data.rows;
+
+        // 创建一个 Map 来加速查找
+        Map<String, LabourReviewListRows> 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);
+    }
+  }
+}

+ 318 - 0
packages/cpt_labour/lib/modules/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.departmentList!.firstWhere((element) => element.value.toString() == selectedDepartmentId).txt!,
+                      hint: "Choose Outlet".tr,
+                      textHintColor: ColorConstants.textBlackHint,
+                      fontSize: 14,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black33,
+                    ).expanded(),
+                    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.departmentList!.indexWhere((department) => department.value.toString() == selectedDepartmentId);
+    }
+
+    if (selectedDepartmentIndex < 0) {
+      selectedDepartmentIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: widget.optionResult.departmentList!.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedDepartmentIndex,
+      onPickerChanged: (_, index) {
+        setState(() {
+          selectedDepartmentId = widget.optionResult.departmentList![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();
+        });
+      },
+    );
+  }
+}

+ 292 - 0
packages/cpt_labour/lib/modules/labour_review_list/labour_review_item.dart

@@ -0,0 +1,292 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/response/job_list_applied_info_entity.dart';
+import 'package:domain/entity/response/job_list_applied_staff_list_entity.dart';
+import 'package:domain/entity/response/labour_review_list_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_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+/*
+ * 用工审核的列表Item
+ */
+class LabourReviewItem extends StatelessWidget {
+  final int index;
+  final LabourReviewListRows item;
+  final VoidCallback? onStatusAction;
+  final VoidCallback? onEditAction;
+  final VoidCallback? onDetailAction;
+  final VoidCallback? onItemAction;
+
+  LabourReviewItem({
+    required this.index,
+    required this.item,
+    this.onStatusAction,
+    this.onEditAction,
+    this.onDetailAction,
+    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.departmentName ?? "-",
+                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.jobTime ?? "-",
+                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.needNum.toString(),
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Colors.white,
+                fontSize: 14,
+              ).expanded(),
+            ],
+          ).marginOnly(top: 15),
+
+          // 薪水
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "${"Salary".tr}:",
+                isFontRegular: true,
+                textColor: ColorConstants.textGrayAECAE5,
+                fontSize: 14,
+              ),
+
+              //发布状态
+              MyTextView(
+                item.salaryShow ?? "-",
+                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: item.btnList?.isNotEmpty ?? false,
+            child: Row(
+              mainAxisSize: MainAxisSize.max,
+              mainAxisAlignment: MainAxisAlignment.end,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+                //详情按钮
+                Visibility(
+                  visible: item.btnList?.contains("detail") ?? false,
+                  child: MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      onDetailAction?.call();
+                    },
+                    text: "Detail".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor(
+                      "#56AAFF",
+                    ),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 35,
+                  ).marginOnly(left: 12),
+                ),
+
+                //Edit按钮
+                Visibility(
+                  visible: item.btnList.contains("edit") ?? false,
+                  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: item.btnList.contains("status") ?? false,
+                  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_labour/lib/modules/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.labourReviewList);
+  }
+
+  @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: true,
+        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],
+                          onDetailAction: () {
+                            controller.gotoDetailPage(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_labour/lib/modules/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_labour/lib/modules/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/labour_review_list_entity.dart';
+import 'package:flutter/material.dart';
+
+class LabourReviewState {
+  //筛选条件
+  final TextEditingController searchController = TextEditingController();
+  String keyword = "";
+  DateTime? selectedStartDate;
+  DateTime? selectedEndDate;
+  String? selectedDepartmentId;
+
+  //页面的列表数据
+  List<LabourReviewListRows> datas = [];
+  LabourRequestIndexEntity? indexOptions;
+}

+ 102 - 0
packages/cpt_labour/lib/modules/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.labourReviewWorkflow,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: true,
+          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_labour/lib/modules/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_labour/lib/modules/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/labour_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 LabourRepository _labourRepository = 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 _labourRepository.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_labour/lib/modules/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_labour/lib/modules/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_labour/lib/modules/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: true,
+              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_labour/lib/modules/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_labour/lib/modules/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_labour/lib/modules/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_labour/lib/modules/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_labour/lib/modules/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: true,
+        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_labour/lib/modules/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 = [];
+
+}

+ 15 - 2
packages/cpt_labour/lib/router/labour_service_impl.dart

@@ -1,8 +1,10 @@
+import 'package:cpt_labour/modules/labour_review_list/labour_review_page.dart';
 import 'package:plugin_basic/basic_export.dart';
 import 'package:router/componentRouter/labour_service.dart';
 import 'package:shared/utils/log_utils.dart';
 
 import '../modules/labour_request_list/labour_request_list_page.dart';
+import '../modules/labour_template_list/labour_template_list_page.dart';
 
 class LabourServiceImpl extends GetxService implements LabourService {
 
@@ -12,16 +14,27 @@ class LabourServiceImpl extends GetxService implements LabourService {
   }
 
   @override
+  void startLabourTemplatePage() {
+    LabourTemplateListPage.startInstance();
+  }
+
+  @override
+  void startLabourReviewPage() {
+    LabourReviewPage.startInstance();
+  }
+
+  @override
   void onInit() {
     super.onInit();
     //初始化资源
-    Log.d("ProfileServiceImpl 初始化资源");
+    Log.d("LabourServiceImpl 初始化资源");
   }
 
   @override
   void onClose() {
     super.onClose();
     //销毁资源
-    Log.d("ProfileServiceImpl 销毁资源");
+    Log.d("LabourServiceImpl 销毁资源");
   }
+
 }

+ 35 - 2
packages/cpt_labour/lib/router/page_router.dart

@@ -1,11 +1,14 @@
 
-import 'package:flutter/material.dart';
 import 'package:get/get.dart';
 import 'package:router/path/router_path.dart';
-
 import '../modules/labour_request_add/labour_request_add_page.dart';
 import '../modules/labour_request_list/labour_request_list_page.dart';
 import '../modules/labour_request_workflow/labour_request_workflow_page.dart';
+import '../modules/labour_review_edit/labour_review_edit_page.dart';
+import '../modules/labour_review_list/labour_review_page.dart';
+import '../modules/labour_review_workflow/labour_request_workflow_page.dart';
+import '../modules/labour_template_add/labour_template_add_page.dart';
+import '../modules/labour_template_list/labour_template_list_page.dart';
 
 class LabourPageRouter {
 
@@ -29,5 +32,35 @@ class LabourPageRouter {
       page: () => LabourRequestWorkflowPage(),
     ),
 
+    //用工请求状态修改工作流
+    GetPage(
+      name: RouterPath.labourTemplateList,
+      page: () => LabourTemplateListPage(),
+    ),
+
+    //用工请求状态修改工作流
+    GetPage(
+      name: RouterPath.labourTemplateAdd,
+      page: () => LabourTemplateAddPage(),
+    ),
+
+    //用工请求审核列表
+    GetPage(
+      name: RouterPath.labourReviewList,
+      page: () => LabourReviewPage(),
+    ),
+
+    //用工请求审核编辑
+    GetPage(
+      name: RouterPath.labourReviewEdit,
+      page: () => LabourReviewEditPage(),
+    ),
+
+    //用工请求审核工作流
+    GetPage(
+      name: RouterPath.labourReviewWorkflow,
+      page: () => LabourReviewWorkflowPage(),
+    ),
+
   ];
 }

+ 48 - 2
packages/cpt_labour_sg/lib/modules/job_list_edit/job_list_edit_controller.dart

@@ -6,6 +6,7 @@ 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/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';
@@ -35,7 +36,11 @@ class JobListEditController extends GetxController with DioCancelableMixin {
       state.selectedOutlet = state.editEntity?.outletList.firstWhere((e) => e.selected == "selected", orElse: () => JobListEditIndexOption()).txt;
       state.selectedOutletId = state.editEntity?.outletList.firstWhere((e) => e.selected == "selected", orElse: () => JobListEditIndexOption()).value;
       state.genderOptionType = state.editEntity?.sexLimit ?? 0;
-      state.selectRequestTypeIndex = state.editEntity!.requestType.indexWhere((e) => e.checked == "checked");
+      state.selectRequestTypeId = state.editEntity?.requestType.firstWhere((e) => e.checked == "checked").value;
+      state.selectPositionName = state.editEntity?.positionList.firstWhere((e) => e.selected == "selected", orElse: () => JobListEditIndexOption()).txt;
+      state.selectPositionId = state.editEntity?.positionList.firstWhere((e) => e.selected == "selected", orElse: () => JobListEditIndexOption()).value;
+
+      Log.d("selectRequestTypeId:${state.selectRequestTypeId},selectPositionId:${state.selectPositionId}");
 
       //时间赋值
       state.selectedStartTime = state.editEntity?.startTime == null ? null : DateTimeUtils.getDateTime(state.editEntity?.startTime ?? "");
@@ -48,6 +53,7 @@ class JobListEditController extends GetxController with DioCancelableMixin {
       needNoController.text = state.editEntity?.needNum == 0 ? "" : state.editEntity?.needNum.toString() ?? "";
       remarkController.text = state.editEntity?.remark ?? "";
 
+
       update();
     } else {
       ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
@@ -122,6 +128,45 @@ class JobListEditController extends GetxController with DioCancelableMixin {
     );
   }
 
+  //选择常规职位
+  void pickRegularPosition() {
+    if (state.editEntity == null) {
+      return;
+    }
+
+    int selectedIndex;
+    if (state.selectPositionId == null) {
+      selectedIndex = 0;
+    } else {
+      selectedIndex = state.editEntity!.positionList.indexWhere((bean) => bean.value.toString() == state.selectPositionId);
+    }
+
+    if (selectedIndex < 0) {
+      selectedIndex = 0;
+    } else {
+      selectedIndex++;
+    }
+
+    List<String> list = ["Position"];
+    list.addAll(state.editEntity!.positionList.map((e) => e.txt!).toList(growable: false));
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: list,
+      initialSelectIndex: selectedIndex,
+      onPickerChanged: (_, index) {
+        if (index == 0) {
+          state.selectPositionId = null;
+          state.selectPositionName = null;
+        } else {
+          state.selectPositionId = state.editEntity!.positionList[index - 1].value!.toString();
+          state.selectPositionName = state.editEntity!.positionList[index - 1].txt!.toString();
+        }
+
+        update();
+      },
+    );
+  }
+
   // 提交表单
   void doSubmit() async {
     var maleNoController = state.formData['need_male']!['controller'];
@@ -170,7 +215,8 @@ class JobListEditController extends GetxController with DioCancelableMixin {
       maleNo,
       femaleNo,
       needNo,
-      state.editEntity?.requestType[state.selectRequestTypeIndex].value,
+      state.selectRequestTypeId,
+      state.selectPositionId,
       remark,
       cancelToken: cancelToken,
     );

+ 41 - 2
packages/cpt_labour_sg/lib/modules/job_list_edit/job_list_edit_page.dart

@@ -347,12 +347,51 @@ class JobListEditPage extends BaseStatelessPage<JobListEditController> {
                       CustomRadioCheck(
                         options: state.editEntity?.requestType.map((e) => e.txt!).toList() ?? [],
                         onOptionSelected: (index, text) {
-                          state.selectRequestTypeIndex = index;
+                          state.selectRequestTypeId = state.editEntity!.requestType[index].value;
+                          controller.update(["position"]);
                         },
                         enable: state.isEditType && state.isOtherFormDisEnable,
-                        selectedPosition: state.editEntity == null ? -1 : state.selectRequestTypeIndex,
+                        selectedPosition: state.editEntity == null ? -1 : state.editEntity!.requestType.indexWhere((e) => e.checked == "checked"),
                       ).marginOnly(top: 10),
 
+                      //当 Request Type 选择 Regular 的时候,需要展示 Position 的下拉选
+                      GetBuilder<JobListEditController>(
+                          id: "position",
+                          init: controller,
+                          builder: (controller) {
+                            return Visibility(
+                              visible: state.selectRequestTypeId == "1",
+                              child: 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.selectPositionName ?? "",
+                                      fontSize: 14,
+                                      hint: "Choose Position".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.pickRegularPosition();
+                              }),
+                            );
+                          }),
+
                       //输入Remark
                       MyTextView(
                         "Remark".tr,

+ 6 - 2
packages/cpt_labour_sg/lib/modules/job_list_edit/job_list_edit_state.dart

@@ -39,7 +39,7 @@ class JobListEditState {
   String? jobId;
   void Function(dynamic value)? cb;
 
-  int genderOptionType = 0;  //使用哪一种类型限制
+  int genderOptionType = 0; //使用哪一种类型限制
 
   JobListEditIndexSGEntity? editEntity;
 
@@ -51,5 +51,9 @@ class JobListEditState {
   String? selectedOutlet;
   String? selectedOutletId;
 
-  int selectRequestTypeIndex = 0;
+  String? selectRequestTypeId;
+
+  //Regular 对应的职位
+  String? selectPositionId;
+  String? selectPositionName;
 }

+ 1 - 1
packages/cpt_labour_sg/lib/modules/job_template_add/job_template_add_page.dart

@@ -358,7 +358,7 @@ class JobTemplateAddPage extends BaseStatelessPage<JobTemplateAddController> {
                   ),
                 ),
               ),
-            ).expanded(),
+            ),
           ),
         ),
       );

+ 0 - 2
packages/cpt_labour_sg/lib/modules/job_template_list/job_template_list_controller.dart

@@ -5,8 +5,6 @@ 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: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';

+ 42 - 2
packages/cpt_labour_sg/lib/modules/labour_request_add/labour_request_add_controller.dart

@@ -104,7 +104,7 @@ class LabourRequestAddController extends GetxController with DioCancelableMixin
 
     DatePickerUtil.showCupertinoDatePicker(
       selectedDateTime: state.selectedRepeatStartTime,
-      mode: CupertinoDatePickerMode.dateAndTime,
+      mode: CupertinoDatePickerMode.date,
       onDateTimeChanged: (date) {
         state.selectedRepeatStartTime = date;
         update();
@@ -120,7 +120,7 @@ class LabourRequestAddController extends GetxController with DioCancelableMixin
 
     DatePickerUtil.showCupertinoDatePicker(
       selectedDateTime: state.selectedRepeatEndTime ?? state.selectedRepeatStartTime,
-      mode: CupertinoDatePickerMode.dateAndTime,
+      mode: CupertinoDatePickerMode.date,
       onDateTimeChanged: (date) {
         state.selectedRepeatEndTime = date;
         update();
@@ -213,6 +213,7 @@ class LabourRequestAddController extends GetxController with DioCancelableMixin
       femaleNo,
       needNo,
       state.selectRequestTypeId,
+      state.selectPositionId,
       remark,
       cancelToken: cancelToken,
     );
@@ -229,4 +230,43 @@ class LabourRequestAddController extends GetxController with DioCancelableMixin
       ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
     }
   }
+
+  //选择常规职位
+  void pickRegularPosition() {
+    if (state.indexEntity == null) {
+      return;
+    }
+
+    int selectedIndex;
+    if (state.selectPositionId == null) {
+      selectedIndex = 0;
+    } else {
+      selectedIndex = state.indexEntity!.positionList.indexWhere((bean) => bean.value.toString() == state.selectPositionId);
+    }
+
+    if (selectedIndex < 0) {
+      selectedIndex = 0;
+    } else {
+      selectedIndex++;
+    }
+
+    List<String> list = ["Position"];
+    list.addAll(state.indexEntity!.positionList.map((e) => e.txt!).toList(growable: false));
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: list,
+      initialSelectIndex: selectedIndex,
+      onPickerChanged: (_, index) {
+        if (index == 0) {
+          state.selectPositionId = null;
+          state.selectPositionName = null;
+        } else {
+          state.selectPositionId = state.indexEntity!.positionList[index - 1].value!.toString();
+          state.selectPositionName = state.indexEntity!.positionList[index - 1].txt!.toString();
+        }
+
+        update();
+      },
+    );
+  }
 }

+ 41 - 6
packages/cpt_labour_sg/lib/modules/labour_request_add/labour_request_add_page.dart

@@ -222,9 +222,7 @@ class LabourRequestAddPage extends BaseStatelessPage<LabourRequestAddController>
                                 mainAxisAlignment: MainAxisAlignment.start,
                                 children: [
                                   MyTextView(
-                                    state.selectedRepeatStartTime == null
-                                        ? ""
-                                        : DateTimeUtils.formatDate(state.selectedRepeatStartTime, format: "yyyy-MM-dd"),
+                                    state.selectedRepeatStartTime == null ? "" : DateTimeUtils.formatDate(state.selectedRepeatStartTime, format: "yyyy-MM-dd"),
                                     fontSize: 14,
                                     hint: "Repeat Start Time".tr,
                                     textHintColor: ColorConstants.textGrayAECAE5,
@@ -257,9 +255,7 @@ class LabourRequestAddPage extends BaseStatelessPage<LabourRequestAddController>
                                 mainAxisAlignment: MainAxisAlignment.start,
                                 children: [
                                   MyTextView(
-                                    state.selectedRepeatEndTime == null
-                                        ? ""
-                                        : DateTimeUtils.formatDate(state.selectedRepeatEndTime, format: "yyyy-MM-dd"),
+                                    state.selectedRepeatEndTime == null ? "" : DateTimeUtils.formatDate(state.selectedRepeatEndTime, format: "yyyy-MM-dd"),
                                     fontSize: 14,
                                     hint: "Repeat End Time".tr,
                                     textHintColor: ColorConstants.textGrayAECAE5,
@@ -419,10 +415,49 @@ class LabourRequestAddPage extends BaseStatelessPage<LabourRequestAddController>
                         options: state.indexEntity?.requestType.map((e) => e.txt!).toList() ?? [],
                         onOptionSelected: (index, text) {
                           state.selectRequestTypeId = state.indexEntity!.requestType[index].value;
+                          controller.update(["position"]);
                         },
                         selectedPosition: state.indexEntity == null ? -1 : 0,
                       ).marginOnly(top: 10),
 
+                      //当 Request Type 选择 Regular 的时候,需要展示 Position 的下拉选
+                      GetBuilder<LabourRequestAddController>(
+                          id: "position",
+                          init: controller,
+                          builder: (controller) {
+                            return Visibility(
+                              visible: state.selectRequestTypeId == "1",
+                              child: 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.selectPositionName ?? "",
+                                      fontSize: 14,
+                                      hint: "Choose Position".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.pickRegularPosition();
+                              }),
+                            );
+                          }),
+
                       //输入Remark
                       MyTextView(
                         "Remark".tr,

+ 5 - 0
packages/cpt_labour_sg/lib/modules/labour_request_add/labour_request_add_state.dart

@@ -56,4 +56,9 @@ class LabourRequestAddState {
   String? selectedOutletId;
 
   String? selectRequestTypeId;
+
+  String? selectPositionId;
+  String? selectPositionName;
+
+
 }

+ 84 - 0
packages/cpt_report/lib/modules/report_labour_vn/report_labour_vn_controller.dart

@@ -0,0 +1,84 @@
+import 'package:domain/repository/other_repository.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/picker/date_picker_util.dart';
+
+import 'report_labour_vn_state.dart';
+
+class ReportLabourVNController extends GetxController with DioCancelableMixin {
+  final OtherRepository _otherRepository = Get.find();
+  final ReportLabourVNState state = ReportLabourVNState();
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Loading;
+  String? errorMessage;
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  //重试
+  void retryRequest() {
+    fetchReportData();
+  }
+
+  // 获取当前列表数据
+  void fetchReportData() async {
+    changeLoadingState(LoadState.State_Loading);
+
+    var result = await _otherRepository.fetchReportLabourVN(
+      DateTimeUtils.formatDate(state.startDateTime, format: "yyyy-MM-dd"),
+      DateTimeUtils.formatDate(state.endDateTime, format: "yyyy-MM-dd"),
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      state.entity = result.data;
+      state.datas = result.data?.rows ?? [];
+      if (state.datas.isNotEmpty) {
+        changeLoadingState(LoadState.State_Success);
+      } else {
+        changeLoadingState(LoadState.State_Empty);
+      }
+    } else {
+      errorMessage = result.errorMsg ?? "Network Load Error".tr;
+      changeLoadingState(LoadState.State_Error);
+    }
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    fetchReportData();
+  }
+
+  /// 筛选开始日期
+  void pickerStartDate() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.startDateTime,
+      onDateTimeChanged: (date) {
+        state.startDateTime = date;
+        update();
+        fetchReportData();
+      },
+      title: "Start Date".tr,
+    );
+  }
+
+  /// 筛选结束日期
+  void pickerEndDate() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.endDateTime ?? state.startDateTime,
+      onDateTimeChanged: (date) {
+        state.endDateTime = date;
+        update();
+        fetchReportData();
+      },
+      title: "End Date".tr,
+    );
+  }
+}

+ 219 - 0
packages/cpt_report/lib/modules/report_labour_vn/report_labour_vn_item.dart

@@ -0,0 +1,219 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:domain/entity/response/labour_report_v_n_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 ReportLabourVNItem extends StatelessWidget {
+  LabourReportVNEntity? entity; //主体数据
+  int type; //0  1  2
+
+  ReportLabourVNItem({
+    required this.entity,
+    required this.type,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      width: double.infinity,
+      padding: const EdgeInsets.only(top: 16, bottom: 20,left: 15,right: 15),
+      margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 15),
+      decoration: BoxDecoration(
+        color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+        borderRadius: BorderRadius.circular(5), // 设置圆角
+      ),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          MyTextView(
+            type == 0
+                ? "Completed".tr
+                : type == 1
+                    ? "Incomplete".tr
+                    : "Completed + Incomplete".tr,
+            fontSize: 14,
+            isFontBold: true,
+            marginBottom: 15,
+            marginLeft: 5,
+            marginRight: 20,
+            textColor: type == 0
+                ? ColorConstants.textGreen0AC074
+                : type == 1
+                    ? ColorConstants.textBlue06D9FF
+                    : ColorConstants.textRedFF6262,
+          ),
+          const Divider(height: 0.5, color: ColorConstants.dividerBar),
+          SingleChildScrollView(
+            scrollDirection: Axis.horizontal,
+            child: Row(
+              crossAxisAlignment: CrossAxisAlignment.start,
+              children: [
+                //第一列 部门
+                Column(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      "Outlet".tr,
+                      fontSize: 14,
+                      textColor: Colors.white,
+                      isFontRegular: true,
+                    ),
+                    MyTextView(
+                      "-",
+                      textColor: Colors.transparent,
+                      fontSize: 14,
+                      isFontRegular: true,
+                    ),
+                    ...(entity?.rows.map((item) {
+                          return MyTextView(
+                            item.outletName ?? "-",
+                            fontSize: 14,
+                            maxLines: 1,
+                            isTextEllipsis: true,
+                            textColor: Colors.white,
+                            isFontRegular: true,
+                            marginTop: 6,
+                            marginBottom: 6,
+                          );
+                        }).toList() ??
+                        []),
+                  ],
+                ).constrained(maxWidth: 100),
+
+                //第二列 小时
+                Column(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: [
+                    MyTextView("Hours".tr, textColor: Colors.white, fontSize: 14, isFontRegular: true),
+                    MyTextView(
+                      "(${entity?.total?.hrs})",
+                      textColor: type == 0
+                          ? ColorConstants.textGreen0AC074
+                          : type == 1
+                              ? ColorConstants.textBlue06D9FF
+                              : ColorConstants.textRedFF6262,
+                      fontSize: 14,
+                      isFontRegular: true,
+                    ),
+                    ...(entity?.rows.map((item) {
+                          return MyTextView(
+                            "${type == 0 ? item.hrs ?? "0" : type == 1 ? item.inHrs ?? "0" : item.totHrs ?? "0"} H",
+                            fontSize: 14,
+                            textColor: Colors.white,
+                            isFontRegular: true,
+                            marginTop: 6,
+                            marginBottom: 6,
+                          );
+                        }).toList() ??
+                        []),
+                  ],
+                ).constrained(maxWidth: 100).marginOnly(left: 25),
+
+                //第三列 房间
+                Column(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: [
+                    MyTextView("Rooms".tr, textColor: Colors.white, fontSize: 14, isFontRegular: true),
+                    MyTextView(
+                      "(${entity?.total?.rms})",
+                      textColor: type == 0
+                          ? ColorConstants.textGreen0AC074
+                          : type == 1
+                              ? ColorConstants.textBlue06D9FF
+                              : ColorConstants.textRedFF6262,
+                      fontSize: 14,
+                      isFontRegular: true,
+                    ),
+                    ...(entity?.rows.map((item) {
+                          return MyTextView(
+                            type == 0
+                                ? item.rms ?? ""
+                                : type == 1
+                                    ? item.inRms ?? ""
+                                    : item.totRms ?? "",
+                            fontSize: 14,
+                            textColor: Colors.white,
+                            isFontRegular: true,
+                            marginTop: 6,
+                            marginBottom: 6,
+                          );
+                        }).toList() ??
+                        []),
+                  ],
+                ).constrained(maxWidth: 100).marginOnly(left: 25),
+
+                //第四列 数量
+                Column(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: [
+                    MyTextView("Count".tr, textColor: Colors.white, fontSize: 14, isFontRegular: true),
+                    MyTextView(
+                      "(${entity?.total?.num})",
+                      textColor: type == 0
+                          ? ColorConstants.textGreen0AC074
+                          : type == 1
+                              ? ColorConstants.textBlue06D9FF
+                              : ColorConstants.textRedFF6262,
+                      fontSize: 14,
+                      isFontRegular: true,
+                    ),
+                    ...(entity?.rows.map((item) {
+                          return MyTextView(
+                            type == 0
+                                ? item.num ?? ""
+                                : type == 1
+                                    ? item.inNum ?? ""
+                                    : item.totNum ?? "",
+                            fontSize: 14,
+                            textColor: Colors.white,
+                            isFontRegular: true,
+                            marginTop: 6,
+                            marginBottom: 6,
+                          );
+                        }).toList() ??
+                        []),
+                  ],
+                ).constrained(maxWidth: 100).marginOnly(left: 25),
+
+                //第五列 总金额
+                Column(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: [
+                    MyTextView("TotalAmt".tr, textColor: Colors.white, fontSize: 14, isFontRegular: true),
+                    MyTextView(
+                      "(${entity?.total?.totAmt})",
+                      textColor: type == 0
+                          ? ColorConstants.textGreen0AC074
+                          : type == 1
+                              ? ColorConstants.textBlue06D9FF
+                              : ColorConstants.textRedFF6262,
+                      fontSize: 14,
+                      isFontRegular: true,
+                    ),
+                    ...(entity?.rows.map((item) {
+                          return MyTextView(
+                            "₫ ${type == 0 ? item.amt ?? "0" : type == 1 ? item.inAmt ?? "0" : item.totAmt ?? "0"}",
+                            fontSize: 14,
+                            textColor: Colors.white,
+                            isFontRegular: true,
+                            marginTop: 6,
+                            marginBottom: 6,
+                          );
+                        }).toList() ??
+                        []),
+                  ],
+                ).constrained(maxWidth: 100).marginOnly(left: 25),
+              ],
+            ).marginOnly(left: 5, right: 5, top: 18, bottom: 2),
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 128 - 0
packages/cpt_report/lib/modules/report_labour_vn/report_labour_vn_page.dart

@@ -0,0 +1,128 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:flutter/material.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:widgets/load_state_layout.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_text_view.dart';
+
+import 'report_labour_vn_controller.dart';
+
+import 'package:plugin_basic/base/base_stateless_page.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+
+import 'report_labour_vn_item.dart';
+import 'report_labour_vn_state.dart';
+
+/*
+ * 越南的用工报表,单独的页面
+ */
+class ReportLabourVNPage extends BaseStatelessPage<ReportLabourVNController> {
+  ReportLabourVNPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance() {
+    return Get.start(RouterPath.reportLabourVN);
+  }
+
+  late ReportLabourVNState state;
+
+  @override
+  void initState() {
+    state = controller.state;
+  }
+
+  @override
+  ReportLabourVNController createRawController() {
+    return ReportLabourVNController();
+  }
+
+  @override
+  Widget buildWidget(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return Scaffold(
+        extendBodyBehindAppBar: true,
+        appBar: MyAppBar.appBar(context, "YY Casual Labour Report".tr),
+        body: SafeArea(
+          bottom: true,
+          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: Column(
+              children: [
+                // 时间筛选
+                Container(
+                    width: double.infinity,
+                    height: 36,
+                    margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
+                    decoration: BoxDecoration(
+                      color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+                      borderRadius: BorderRadius.circular(20.0), // 设置圆角
+                    ),
+                    child: Row(
+                      children: [
+                        MyTextView(
+                          DateTimeUtils.formatDate(state.startDateTime, format: "yyyy-MM-dd"),
+                          hint: "Start Date".tr,
+                          textHintColor: ColorConstants.textGrayAECAE5,
+                          fontSize: 15,
+                          textAlign: TextAlign.center,
+                          isFontRegular: true,
+                          onClick: controller.pickerStartDate,
+                          textColor: ColorConstants.textGrayAECAE5,
+                        ).expanded(),
+                        Container(color: ColorConstants.dividerBar, height: 21.5, width: 0.5),
+                        MyTextView(
+                          DateTimeUtils.formatDate(state.endDateTime, format: "yyyy-MM-dd"),
+                          hint: "End Date".tr,
+                          textHintColor: ColorConstants.textGrayAECAE5,
+                          fontSize: 15,
+                          textAlign: TextAlign.center,
+                          isFontRegular: true,
+                          onClick: controller.pickerEndDate,
+                          textColor: ColorConstants.textGrayAECAE5,
+                        ).expanded(),
+                      ],
+                    )),
+
+                //动态列表
+                LoadStateLayout(
+                  state: controller.loadingState,
+                  errorMessage: controller.errorMessage,
+                  errorRetry: () {
+                    controller.retryRequest();
+                  },
+                  successSliverWidget: [
+                    SliverList(
+                        delegate: SliverChildBuilderDelegate(
+                      (context, index) {
+                        return ReportLabourVNItem(entity: state.entity, type: index);
+                      },
+                      childCount: state.datas.isNotEmpty ? 3 : 0,
+                    ))
+                  ],
+                ).expanded(),
+              ],
+            ),
+          ),
+        ),
+      );
+    });
+  }
+}

+ 10 - 0
packages/cpt_report/lib/modules/report_labour_vn/report_labour_vn_state.dart

@@ -0,0 +1,10 @@
+import 'package:domain/entity/response/labour_report_v_n_entity.dart';
+
+class ReportLabourVNState {
+
+  DateTime? startDateTime;
+  DateTime? endDateTime;
+
+  LabourReportVNEntity? entity;   //主体数据
+  List<LabourReportVNRows> datas = [];  //列表数据
+}

+ 10 - 1
packages/cpt_report/lib/modules/report_list/report_list_controller.dart

@@ -1,12 +1,14 @@
 import 'package:domain/entity/home_module.dart';
 import 'package:domain/entity/response/hotel_info_entity.dart';
 import 'package:get/get.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:shared/utils/log_utils.dart';
 
 import '../report_finance/report_finance_page.dart';
 import '../report_labour/report_labour_page.dart';
+import '../report_labour_vn/report_labour_vn_page.dart';
 import '../report_staff_request/report_staff_request_page.dart';
 import 'report_list_state.dart';
 
@@ -56,7 +58,14 @@ class ReportListController extends GetxController {
         ReportFinancePage.startInstance();
         break;
       case 'casLab':
-        ReportLabourPage.startInstance();
+        if (ConfigService.to.selectCountry.value ==1){
+          //新加坡
+          ReportLabourPage.startInstance();
+        }else{
+          //越南
+          ReportLabourVNPage.startInstance();
+        }
+
         break;
       case 'monReq':
         ReportStaffRequestPage.startInstance();

+ 37 - 15
packages/cpt_report/lib/modules/report_staff_request/report_staff_request_controller.dart

@@ -1,8 +1,10 @@
 import 'package:domain/entity/response/staff_request_report_entity.dart';
 import 'package:domain/repository/other_repository.dart';
 import 'package:get/get.dart';
+import 'package:plugin_basic/service/app_config_service.dart';
 import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
 import 'package:shared/utils/date_time_utils.dart';
+import 'package:shared/utils/log_utils.dart';
 import 'package:widgets/load_state_layout.dart';
 import 'package:widgets/picker/date_picker_util.dart';
 
@@ -31,20 +33,41 @@ class ReportStaffRequestController extends GetxController with DioCancelableMixi
   void fetchReportData() async {
     changeLoadingState(LoadState.State_Loading);
 
-    var result = await _otherRepository.fetchReportStaffRequest(
-      DateTimeUtils.formatDate(state.startDateTime, format: "yyyy-MM-dd"),
-      DateTimeUtils.formatDate(state.endDateTime, format: "yyyy-MM-dd"),
-      cancelToken: cancelToken,
-    );
-
-    if (result.isSuccess) {
-      state.datas = result.list
-          ?.whereType<StaffRequestReportEntity>() // 直接过滤出非 null 的 StaffRequestReportEntity
-          .toList() ?? [];
-      changeLoadingState(LoadState.State_Success);
+    //根据不同的国家,调用不同的接口
+    if (ConfigService.to.selectCountry.value == 1) {
+      //如果是新加坡版本的
+      var result = await _otherRepository.fetchReportStaffRequest(
+        DateTimeUtils.formatDate(state.startDateTime, format: "yyyy-MM-dd"),
+        DateTimeUtils.formatDate(state.endDateTime, format: "yyyy-MM-dd"),
+        cancelToken: cancelToken,
+      );
+      if (result.isSuccess) {
+        state.datas = result.list
+                ?.whereType<StaffRequestReportEntity>() // 直接过滤出非 null 的 StaffRequestReportEntity
+                .toList() ??
+            [];
+        changeLoadingState(LoadState.State_Success);
+      } else {
+        errorMessage = result.errorMsg ?? "Network Load Error".tr;
+        changeLoadingState(LoadState.State_Error);
+      }
     } else {
-      errorMessage = result.errorMsg ?? "Network Load Error".tr;
-      changeLoadingState(LoadState.State_Error);
+      //如果是越南的版本
+      var result = await _otherRepository.fetchStaffReportVN(
+        DateTimeUtils.formatDate(state.startDateTime, format: "yyyy-MM-dd"),
+        DateTimeUtils.formatDate(state.endDateTime, format: "yyyy-MM-dd"),
+        cancelToken: cancelToken,
+      );
+      if (result.isSuccess) {
+        state.datas = result.data?.requestList
+                .whereType<StaffRequestReportEntity>() // 直接过滤出非 null 的 StaffRequestReportEntity
+                .toList() ??
+            [];
+        changeLoadingState(LoadState.State_Success);
+      } else {
+        errorMessage = result.errorMsg ?? "Network Load Error".tr;
+        changeLoadingState(LoadState.State_Error);
+      }
     }
   }
 
@@ -70,7 +93,7 @@ class ReportStaffRequestController extends GetxController with DioCancelableMixi
   /// 筛选结束日期
   void pickerEndDate() {
     DatePickerUtil.showCupertinoDatePicker(
-      selectedDateTime: state.endDateTime??state.startDateTime,
+      selectedDateTime: state.endDateTime ?? state.startDateTime,
       onDateTimeChanged: (date) {
         state.endDateTime = date;
         update();
@@ -79,5 +102,4 @@ class ReportStaffRequestController extends GetxController with DioCancelableMixi
       title: "End Date".tr,
     );
   }
-
 }

+ 3 - 0
packages/cpt_report/lib/modules/report_staff_request/report_staff_request_page.dart

@@ -17,6 +17,9 @@ import 'package:router/path/router_path.dart';
 import 'report_staff_request_item.dart';
 import 'report_staff_request_state.dart';
 
+/*
+ * 兼容新加坡与越南的展示,不同的国家调用不同的接口统一展示一样的样式
+ */
 class ReportStaffRequestPage extends BaseStatelessPage<ReportStaffRequestController> {
   ReportStaffRequestPage({Key? key}) : super(key: key);
 

+ 8 - 0
packages/cpt_report/lib/router/page_router.dart

@@ -6,6 +6,8 @@ import 'package:cpt_report/modules/report_staff_request/report_staff_request_pag
 import 'package:get/get.dart';
 import 'package:router/path/router_path.dart';
 
+import '../modules/report_labour_vn/report_labour_vn_page.dart';
+
 class ReportPageRouter {
   static final routes = [
     //设备列表
@@ -38,5 +40,11 @@ class ReportPageRouter {
       page: () => ReportLabourPage(),
     ),
 
+    //用工统计(越南单独页面)
+    GetPage(
+      name: RouterPath.reportLabourVN,
+      page: () => ReportLabourVNPage(),
+    ),
+
   ];
 }

+ 36 - 2
packages/cs_domain/lib/constants/api_constants.dart

@@ -2,7 +2,7 @@
 
 class ApiConstants {
   //当前服务器环境
-  static const isServerRelease = true;
+  static const isServerRelease = false;
 
   //越南的域名
   static const baseUrl = isServerRelease ? 'https://vietnam.casualabour.com' : 'http://vietnam-dev.casualabour.com';
@@ -10,7 +10,10 @@ class ApiConstants {
   //新加坡的域名
   static const sgBaseUrl = isServerRelease ? 'https://www.casualabour.com' : 'http://singapore-dev.casualabour.com';
 
-// =========================== 用户相关 ↓=========================================
+  //韩国的域名
+  static const koreaBaseUrl = isServerRelease ? 'https://korea.casualabour.com' : 'http://korea-dev.casualabour.com';
+
+  // =========================== 用户相关 ↓=========================================
 
   // 酒店登录
   static const apiUserLogin = "/index.php/api/v1/hotel/login";
@@ -270,4 +273,35 @@ class ApiConstants {
   //员工申请报表
   static const apiReportStaffRequest = "/index.php/api/v1/hotel/report/staff";
 
+// =========================== 越南的审核 ↓=========================================
+
+  // 用工审核选项
+  static const apiLabourReviewIndex = "/index.php/api/v1/hotel/lab-review/index";
+
+  // 用工审核列表
+  static const apiLabourReviewTable = "/index.php/api/v1/hotel/lab-review/table";
+
+  // 用工审核的编辑详情
+  static const apiLabourReviewDetail = "/index.php/api/v1/hotel/lab-review/edit-view";
+
+  // 用工审核的编辑提交
+  static const apiLabourReviewEdit = "/index.php/api/v1/hotel/lab-review/edit-submit";
+
+  // 用工审核的审核流程列表
+  static const apiLabourReviewStatusView = "/index.php/api/v1/hotel/lab-review/status-view";
+
+  // 用工审核的批量同意
+  static const apiLabourReviewApprove = "/index.php/api/v1/hotel/lab-review/approve";
+
+  // 用工审核的批量拒绝
+  static const apiLabourReviewReject = "/index.php/api/v1/hotel/lab-review/reject";
+
+  // 考勤的审核列表
+  static const apiAttendanceReviewTable = "/index.php/api/v1/hotel/att-review/table";
+
+  // 考勤的批量同意
+  static const apiAttendanceReviewApprove = "/index.php/api/v1/hotel/att-review/approve";
+
+  // 考勤的批量拒绝
+  static const apiAttendanceReviewReject = "/index.php/api/v1/hotel/att-review/reject";
 }

+ 70 - 0
packages/cs_domain/lib/entity/response/attendance_review_entity.dart

@@ -0,0 +1,70 @@
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/attendance_review_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/attendance_review_entity.g.dart';
+
+@JsonSerializable()
+class AttendanceReviewEntity {
+	int total = 0;
+	List<AttendanceReviewRows> rows = [];
+
+	AttendanceReviewEntity();
+
+	factory AttendanceReviewEntity.fromJson(Map<String, dynamic> json) => $AttendanceReviewEntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $AttendanceReviewEntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class AttendanceReviewRows {
+	@JSONField(name: "record_id")
+	String? recordId;
+	@JSONField(name: "order_id")
+	String? orderId;
+	@JSONField(name: "applied_id")
+	String? appliedId;
+	@JSONField(name: "staff_name")
+	String? staffName;
+	@JSONField(name: "job_title")
+	String? jobTitle;
+	@JSONField(name: "department_name")
+	String? departmentName;
+	@JSONField(name: "job_time")
+	String? jobTime;
+	@JSONField(name: "status_show")
+	String? statusShow;
+	@JSONField(name: "adjust_show")
+	String? adjustShow;
+	@JSONField(name: "total_show")
+	String? totalShow;
+	@JSONField(name: "clock_in")
+	String? clockIn;
+	@JSONField(name: "in_class")
+	int? inClass;
+	@JSONField(name: "clock_out")
+	String? clockOut;
+	@JSONField(name: "out_class")
+	int? outClass;
+	@JSONField(name: "created_at")
+	String? createdAt;
+	@JSONField(name: "has_reason")
+	int? hasReason;
+
+	bool isSelected = false;
+
+	AttendanceReviewRows();
+
+	factory AttendanceReviewRows.fromJson(Map<String, dynamic> json) => $AttendanceReviewRowsFromJson(json);
+
+	Map<String, dynamic> toJson() => $AttendanceReviewRowsToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}

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

@@ -31,6 +31,8 @@ class JobListEditIndexSGEntity {
 	List<JobListEditIndexOption> outletList = [];
 	@JSONField(name: "request_type")
 	List<JobListEditIndexOption> requestType = [];
+	@JSONField(name: "position_list")
+	List<JobListEditIndexOption> positionList = [];
 	@JSONField(name: "limit_list")
 	List<JobListEditIndexOption> limitList = [];
 

+ 7 - 5
packages/cs_domain/lib/entity/response/job_template_edit_index_entity.dart

@@ -7,12 +7,14 @@ export 'package:domain/generated/json/job_template_edit_index_entity.g.dart';
 class JobTemplateEditIndexEntity {
 	@JSONField(name: "template_id")
 	int templateId = 0;
-	String? name = null;
-	String? contact = null;
+	String? name;  //新加坡用这个为标题
+	@JSONField(name: "job_title")
+	String? jobTitle;  //越南用这个为标题
+	String? contact;
 	@JSONField(name: "contact_no")
-	String? contactNo = null;
-	String? description = null;
-	String? note = null;
+	String? contactNo;
+	String? description;
+	String? note;
 	@JSONField(name: "with_food_cert")
 	int withFoodCert = 0;
 	@JSONField(name: "age_list")

+ 3 - 1
packages/cs_domain/lib/entity/response/job_template_s_g_entity.dart

@@ -25,15 +25,17 @@ class JobTemplateSGRows {
 	int id = 0;
 	String? name = null;
 	String? contact = null;
+	@JSONField(name: "created_by")
+	String? createdBy = null;
 	@JSONField(name: "contact_no")
 	String? contactNo = null;
-	String? note = null;
 	@JSONField(name: "created_at")
 	String? createdAt = null;
 	@JSONField(name: "updated_at")
 	String? updatedAt = null;
 	@JSONField(name: "action_list")
 	List<String> actionList = [];
+	String? note = null;
 
 	JobTemplateSGRows();
 

+ 99 - 0
packages/cs_domain/lib/entity/response/labour_report_v_n_entity.dart

@@ -0,0 +1,99 @@
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/labour_report_v_n_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/labour_report_v_n_entity.g.dart';
+
+@JsonSerializable()
+class LabourReportVNEntity {
+	@JSONField(name: "start_date")
+	String? startDate;
+	@JSONField(name: "end_date")
+	String? endDate;
+	List<LabourReportVNRows> rows = [];
+	LabourReportVNTotal? total;
+
+	LabourReportVNEntity();
+
+	factory LabourReportVNEntity.fromJson(Map<String, dynamic> json) => $LabourReportVNEntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $LabourReportVNEntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class LabourReportVNRows {
+	@JSONField(name: "outlet_id")
+	String? outletId;
+	@JSONField(name: "outlet_name")
+	String? outletName;
+	String? num;
+	String? hrs;
+	String? rms;
+	String? amt;
+	@JSONField(name: "in_num")
+	String? inNum;
+	@JSONField(name: "in_hrs")
+	String? inHrs;
+	@JSONField(name: "in_rms")
+	String? inRms;
+	@JSONField(name: "in_amt")
+	String? inAmt;
+	@JSONField(name: "tot_num")
+	String? totNum;
+	@JSONField(name: "tot_hrs")
+	String? totHrs;
+	@JSONField(name: "tot_rms")
+	String? totRms;
+	@JSONField(name: "tot_amt")
+	String? totAmt;
+
+	LabourReportVNRows();
+
+	factory LabourReportVNRows.fromJson(Map<String, dynamic> json) => $LabourReportVNRowsFromJson(json);
+
+	Map<String, dynamic> toJson() => $LabourReportVNRowsToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class LabourReportVNTotal {
+	String? num;
+	double? hrs;
+	String? rms;
+	String? amt;
+	@JSONField(name: "in_num")
+	String? inNum;
+	@JSONField(name: "in_hrs")
+	String? inHrs;
+	@JSONField(name: "in_rms")
+	String? inRms;
+	@JSONField(name: "in_amt")
+	String? inAmt;
+	@JSONField(name: "tot_num")
+	String? totNum;
+	@JSONField(name: "tot_hrs")
+	String? totHrs;
+	@JSONField(name: "tot_rms")
+	String? totRms;
+	@JSONField(name: "tot_amt")
+	String? totAmt;
+
+	LabourReportVNTotal();
+
+	factory LabourReportVNTotal.fromJson(Map<String, dynamic> json) => $LabourReportVNTotalFromJson(json);
+
+	Map<String, dynamic> toJson() => $LabourReportVNTotalToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}

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

@@ -11,6 +11,8 @@ class LabourRequestSGAddIndexEntity {
 	List<LabourRequestSGAddIndexOption> outletList = [];
 	@JSONField(name: "request_type")
 	List<LabourRequestSGAddIndexOption> requestType = [];
+	@JSONField(name: "position_list")
+	List<LabourRequestSGAddIndexOption> positionList = [];
 
 	LabourRequestSGAddIndexEntity();
 

+ 58 - 0
packages/cs_domain/lib/entity/response/labour_review_list_entity.dart

@@ -0,0 +1,58 @@
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/labour_review_list_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/labour_review_list_entity.g.dart';
+
+@JsonSerializable()
+class LabourReviewListEntity {
+	int total = 0;
+	List<LabourReviewListRows> rows = [];
+
+	LabourReviewListEntity();
+
+	factory LabourReviewListEntity.fromJson(Map<String, dynamic> json) => $LabourReviewListEntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $LabourReviewListEntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class LabourReviewListRows {
+	@JSONField(name: "record_id")
+	String? recordId;
+	@JSONField(name: "request_id")
+	String? requestId;
+	@JSONField(name: "job_title")
+	String? jobTitle;
+	@JSONField(name: "job_time")
+	String? jobTime;
+	@JSONField(name: "department_name")
+	String? departmentName;
+	@JSONField(name: "salary_show")
+	String? salaryShow;
+	@JSONField(name: "need_num")
+	String? needNum;
+	@JSONField(name: "status_show")
+	String? statusShow;
+	@JSONField(name: "created_at")
+	String? createdAt;
+	@JSONField(name: "btn_list")
+	List<String> btnList = [];
+
+	bool isSelected = false;  //自定义字段,是否选中
+
+	LabourReviewListRows();
+
+	factory LabourReviewListRows.fromJson(Map<String, dynamic> json) => $LabourReviewListRowsFromJson(json);
+
+	Map<String, dynamic> toJson() => $LabourReviewListRowsToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}

+ 55 - 0
packages/cs_domain/lib/entity/response/labour_review_status_entity.dart

@@ -0,0 +1,55 @@
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/labour_review_status_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/labour_review_status_entity.g.dart';
+
+@JsonSerializable()
+class LabourReviewStatusEntity {
+	@JSONField(name: "co_department_id")
+	int coDepartmentId = 0;
+	List<LabourReviewStatusRecords> records = [];
+
+	LabourReviewStatusEntity();
+
+	factory LabourReviewStatusEntity.fromJson(Map<String, dynamic> json) => $LabourReviewStatusEntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $LabourReviewStatusEntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class LabourReviewStatusRecords {
+	@JSONField(name: "serial_number")
+	String? serialNumber;
+	@JSONField(name: "node_name")
+	String? nodeName;
+	@JSONField(name: "status_show")
+	String? statusShow;
+	@JSONField(name: "audit_name")
+	String? auditName;
+	@JSONField(name: "audit_time")
+	String? auditTime;
+	@JSONField(name: "audit_mark")
+	dynamic auditMark;
+	@JSONField(name: "created_at")
+	String? createdAt;
+	@JSONField(name: "assignee_type_show")
+	String? assigneeTypeShow;
+	@JSONField(name: "designation_show")
+	String? designationShow;
+
+	LabourReviewStatusRecords();
+
+	factory LabourReviewStatusRecords.fromJson(Map<String, dynamic> json) => $LabourReviewStatusRecordsFromJson(json);
+
+	Map<String, dynamic> toJson() => $LabourReviewStatusRecordsToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}

+ 28 - 0
packages/cs_domain/lib/entity/response/staff_report_v_n_entity.dart

@@ -0,0 +1,28 @@
+import 'package:domain/entity/response/staff_request_report_entity.dart';
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/staff_report_v_n_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/staff_report_v_n_entity.g.dart';
+
+@JsonSerializable()
+class StaffReportVNEntity {
+	@JSONField(name: "start_date")
+	String? startDate;
+	@JSONField(name: "end_date")
+	String? endDate;
+	@JSONField(name: "request_list")
+	List<StaffRequestReportEntity> requestList = [];
+	@JSONField(name: "total_num")
+	int totalNum = 0;
+
+	StaffReportVNEntity();
+
+	factory StaffReportVNEntity.fromJson(Map<String, dynamic> json) => $StaffReportVNEntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $StaffReportVNEntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}

+ 170 - 0
packages/cs_domain/lib/generated/json/attendance_review_entity.g.dart

@@ -0,0 +1,170 @@
+import 'package:domain/generated/json/base/json_convert_content.dart';
+import 'package:domain/entity/response/attendance_review_entity.dart';
+
+AttendanceReviewEntity $AttendanceReviewEntityFromJson(Map<String, dynamic> json) {
+  final AttendanceReviewEntity attendanceReviewEntity = AttendanceReviewEntity();
+  final int? total = jsonConvert.convert<int>(json['total']);
+  if (total != null) {
+    attendanceReviewEntity.total = total;
+  }
+  final List<AttendanceReviewRows>? rows = (json['rows'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<AttendanceReviewRows>(e) as AttendanceReviewRows).toList();
+  if (rows != null) {
+    attendanceReviewEntity.rows = rows;
+  }
+  return attendanceReviewEntity;
+}
+
+Map<String, dynamic> $AttendanceReviewEntityToJson(AttendanceReviewEntity entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['total'] = entity.total;
+  data['rows'] = entity.rows.map((v) => v.toJson()).toList();
+  return data;
+}
+
+extension AttendanceReviewEntityExtension on AttendanceReviewEntity {
+  AttendanceReviewEntity copyWith({
+    int? total,
+    List<AttendanceReviewRows>? rows,
+  }) {
+    return AttendanceReviewEntity()
+      ..total = total ?? this.total
+      ..rows = rows ?? this.rows;
+  }
+}
+
+AttendanceReviewRows $AttendanceReviewRowsFromJson(Map<String, dynamic> json) {
+  final AttendanceReviewRows attendanceReviewRows = AttendanceReviewRows();
+  final String? recordId = jsonConvert.convert<String>(json['record_id']);
+  if (recordId != null) {
+    attendanceReviewRows.recordId = recordId;
+  }
+  final String? orderId = jsonConvert.convert<String>(json['order_id']);
+  if (orderId != null) {
+    attendanceReviewRows.orderId = orderId;
+  }
+  final String? appliedId = jsonConvert.convert<String>(json['applied_id']);
+  if (appliedId != null) {
+    attendanceReviewRows.appliedId = appliedId;
+  }
+  final String? staffName = jsonConvert.convert<String>(json['staff_name']);
+  if (staffName != null) {
+    attendanceReviewRows.staffName = staffName;
+  }
+  final String? jobTitle = jsonConvert.convert<String>(json['job_title']);
+  if (jobTitle != null) {
+    attendanceReviewRows.jobTitle = jobTitle;
+  }
+  final String? departmentName = jsonConvert.convert<String>(json['department_name']);
+  if (departmentName != null) {
+    attendanceReviewRows.departmentName = departmentName;
+  }
+  final String? jobTime = jsonConvert.convert<String>(json['job_time']);
+  if (jobTime != null) {
+    attendanceReviewRows.jobTime = jobTime;
+  }
+  final String? statusShow = jsonConvert.convert<String>(json['status_show']);
+  if (statusShow != null) {
+    attendanceReviewRows.statusShow = statusShow;
+  }
+  final String? adjustShow = jsonConvert.convert<String>(json['adjust_show']);
+  if (adjustShow != null) {
+    attendanceReviewRows.adjustShow = adjustShow;
+  }
+  final String? totalShow = jsonConvert.convert<String>(json['total_show']);
+  if (totalShow != null) {
+    attendanceReviewRows.totalShow = totalShow;
+  }
+  final String? clockIn = jsonConvert.convert<String>(json['clock_in']);
+  if (clockIn != null) {
+    attendanceReviewRows.clockIn = clockIn;
+  }
+  final int? inClass = jsonConvert.convert<int>(json['in_class']);
+  if (inClass != null) {
+    attendanceReviewRows.inClass = inClass;
+  }
+  final String? clockOut = jsonConvert.convert<String>(json['clock_out']);
+  if (clockOut != null) {
+    attendanceReviewRows.clockOut = clockOut;
+  }
+  final int? outClass = jsonConvert.convert<int>(json['out_class']);
+  if (outClass != null) {
+    attendanceReviewRows.outClass = outClass;
+  }
+  final String? createdAt = jsonConvert.convert<String>(json['created_at']);
+  if (createdAt != null) {
+    attendanceReviewRows.createdAt = createdAt;
+  }
+  final int? hasReason = jsonConvert.convert<int>(json['has_reason']);
+  if (hasReason != null) {
+    attendanceReviewRows.hasReason = hasReason;
+  }
+  final bool? isSelected = jsonConvert.convert<bool>(json['isSelected']);
+  if (isSelected != null) {
+    attendanceReviewRows.isSelected = isSelected;
+  }
+  return attendanceReviewRows;
+}
+
+Map<String, dynamic> $AttendanceReviewRowsToJson(AttendanceReviewRows entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['record_id'] = entity.recordId;
+  data['order_id'] = entity.orderId;
+  data['applied_id'] = entity.appliedId;
+  data['staff_name'] = entity.staffName;
+  data['job_title'] = entity.jobTitle;
+  data['department_name'] = entity.departmentName;
+  data['job_time'] = entity.jobTime;
+  data['status_show'] = entity.statusShow;
+  data['adjust_show'] = entity.adjustShow;
+  data['total_show'] = entity.totalShow;
+  data['clock_in'] = entity.clockIn;
+  data['in_class'] = entity.inClass;
+  data['clock_out'] = entity.clockOut;
+  data['out_class'] = entity.outClass;
+  data['created_at'] = entity.createdAt;
+  data['has_reason'] = entity.hasReason;
+  data['isSelected'] = entity.isSelected;
+  return data;
+}
+
+extension AttendanceReviewRowsExtension on AttendanceReviewRows {
+  AttendanceReviewRows copyWith({
+    String? recordId,
+    String? orderId,
+    String? appliedId,
+    String? staffName,
+    String? jobTitle,
+    String? departmentName,
+    String? jobTime,
+    String? statusShow,
+    String? adjustShow,
+    String? totalShow,
+    String? clockIn,
+    int? inClass,
+    String? clockOut,
+    int? outClass,
+    String? createdAt,
+    int? hasReason,
+    bool? isSelected,
+  }) {
+    return AttendanceReviewRows()
+      ..recordId = recordId ?? this.recordId
+      ..orderId = orderId ?? this.orderId
+      ..appliedId = appliedId ?? this.appliedId
+      ..staffName = staffName ?? this.staffName
+      ..jobTitle = jobTitle ?? this.jobTitle
+      ..departmentName = departmentName ?? this.departmentName
+      ..jobTime = jobTime ?? this.jobTime
+      ..statusShow = statusShow ?? this.statusShow
+      ..adjustShow = adjustShow ?? this.adjustShow
+      ..totalShow = totalShow ?? this.totalShow
+      ..clockIn = clockIn ?? this.clockIn
+      ..inClass = inClass ?? this.inClass
+      ..clockOut = clockOut ?? this.clockOut
+      ..outClass = outClass ?? this.outClass
+      ..createdAt = createdAt ?? this.createdAt
+      ..hasReason = hasReason ?? this.hasReason
+      ..isSelected = isSelected ?? this.isSelected;
+  }
+}

+ 51 - 1
packages/cs_domain/lib/generated/json/base/json_convert_content.dart

@@ -6,6 +6,7 @@
 import 'package:flutter/material.dart' show debugPrint;
 import 'package:domain/entity/response/add_edit_revise_view_s_g_entity.dart';
 import 'package:domain/entity/response/attendance_entity.dart';
+import 'package:domain/entity/response/attendance_review_entity.dart';
 import 'package:domain/entity/response/check_success_entity.dart';
 import 'package:domain/entity/response/device_list_entity.dart';
 import 'package:domain/entity/response/fiance_report_entity.dart';
@@ -34,12 +35,15 @@ import 'package:domain/entity/response/job_template_s_g_entity.dart';
 import 'package:domain/entity/response/job_title_edit_index_entity.dart';
 import 'package:domain/entity/response/job_title_s_g_entity.dart';
 import 'package:domain/entity/response/labour_report_entity.dart';
+import 'package:domain/entity/response/labour_report_v_n_entity.dart';
 import 'package:domain/entity/response/labour_request_edit_index_entity.dart';
 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/labour_request_s_g_add_index_entity.dart';
 import 'package:domain/entity/response/labour_request_s_g_entity.dart';
 import 'package:domain/entity/response/labour_request_work_flow_entity.dart';
+import 'package:domain/entity/response/labour_review_list_entity.dart';
+import 'package:domain/entity/response/labour_review_status_entity.dart';
 import 'package:domain/entity/response/revise_index_s_g_entity.dart';
 import 'package:domain/entity/response/revise_list_s_g_entity.dart';
 import 'package:domain/entity/response/revise_log_s_g_entity.dart';
@@ -48,6 +52,7 @@ import 'package:domain/entity/response/staff_detail_s_g_entity.dart';
 import 'package:domain/entity/response/staff_job_history_s_g_entity.dart';
 import 'package:domain/entity/response/staff_labour_history_entity.dart';
 import 'package:domain/entity/response/staff_remark_history_entity.dart';
+import 'package:domain/entity/response/staff_report_v_n_entity.dart';
 import 'package:domain/entity/response/staff_request_report_entity.dart';
 import 'package:domain/entity/response/staff_review_history_s_g_entity.dart';
 import 'package:domain/entity/server_time.dart';
@@ -169,7 +174,12 @@ class JsonConvert {
         if (value == null) {
           return null;
         }
-        return convertFuncMap[type]!(value as Map<String, dynamic>) as T;
+        var covertFunc = convertFuncMap[type]!;
+        if (covertFunc is Map<String, dynamic>) {
+          return covertFunc(value as Map<String, dynamic>) as T;
+        } else {
+          return covertFunc(Map<String, dynamic>.from(value)) as T;
+        }
       } else {
         throw UnimplementedError('$type unimplemented,you can try running the app again');
       }
@@ -187,6 +197,12 @@ class JsonConvert {
     if (<AttendanceList>[] is M) {
       return data.map<AttendanceList>((Map<String, dynamic> e) => AttendanceList.fromJson(e)).toList() as M;
     }
+    if (<AttendanceReviewEntity>[] is M) {
+      return data.map<AttendanceReviewEntity>((Map<String, dynamic> e) => AttendanceReviewEntity.fromJson(e)).toList() as M;
+    }
+    if (<AttendanceReviewRows>[] is M) {
+      return data.map<AttendanceReviewRows>((Map<String, dynamic> e) => AttendanceReviewRows.fromJson(e)).toList() as M;
+    }
     if (<CheckSuccessEntity>[] is M) {
       return data.map<CheckSuccessEntity>((Map<String, dynamic> e) => CheckSuccessEntity.fromJson(e)).toList() as M;
     }
@@ -373,6 +389,15 @@ class JsonConvert {
     if (<LabourReportTotal>[] is M) {
       return data.map<LabourReportTotal>((Map<String, dynamic> e) => LabourReportTotal.fromJson(e)).toList() as M;
     }
+    if (<LabourReportVNEntity>[] is M) {
+      return data.map<LabourReportVNEntity>((Map<String, dynamic> e) => LabourReportVNEntity.fromJson(e)).toList() as M;
+    }
+    if (<LabourReportVNRows>[] is M) {
+      return data.map<LabourReportVNRows>((Map<String, dynamic> e) => LabourReportVNRows.fromJson(e)).toList() as M;
+    }
+    if (<LabourReportVNTotal>[] is M) {
+      return data.map<LabourReportVNTotal>((Map<String, dynamic> e) => LabourReportVNTotal.fromJson(e)).toList() as M;
+    }
     if (<LabourRequestEditIndexEntity>[] is M) {
       return data.map<LabourRequestEditIndexEntity>((Map<String, dynamic> e) => LabourRequestEditIndexEntity.fromJson(e)).toList() as M;
     }
@@ -415,6 +440,18 @@ class JsonConvert {
     if (<LabourRequestWorkFlowRecords>[] is M) {
       return data.map<LabourRequestWorkFlowRecords>((Map<String, dynamic> e) => LabourRequestWorkFlowRecords.fromJson(e)).toList() as M;
     }
+    if (<LabourReviewListEntity>[] is M) {
+      return data.map<LabourReviewListEntity>((Map<String, dynamic> e) => LabourReviewListEntity.fromJson(e)).toList() as M;
+    }
+    if (<LabourReviewListRows>[] is M) {
+      return data.map<LabourReviewListRows>((Map<String, dynamic> e) => LabourReviewListRows.fromJson(e)).toList() as M;
+    }
+    if (<LabourReviewStatusEntity>[] is M) {
+      return data.map<LabourReviewStatusEntity>((Map<String, dynamic> e) => LabourReviewStatusEntity.fromJson(e)).toList() as M;
+    }
+    if (<LabourReviewStatusRecords>[] is M) {
+      return data.map<LabourReviewStatusRecords>((Map<String, dynamic> e) => LabourReviewStatusRecords.fromJson(e)).toList() as M;
+    }
     if (<ReviseIndexSGEntity>[] is M) {
       return data.map<ReviseIndexSGEntity>((Map<String, dynamic> e) => ReviseIndexSGEntity.fromJson(e)).toList() as M;
     }
@@ -481,6 +518,9 @@ class JsonConvert {
     if (<StaffRemarkHistoryRows>[] is M) {
       return data.map<StaffRemarkHistoryRows>((Map<String, dynamic> e) => StaffRemarkHistoryRows.fromJson(e)).toList() as M;
     }
+    if (<StaffReportVNEntity>[] is M) {
+      return data.map<StaffReportVNEntity>((Map<String, dynamic> e) => StaffReportVNEntity.fromJson(e)).toList() as M;
+    }
     if (<StaffRequestReportEntity>[] is M) {
       return data.map<StaffRequestReportEntity>((Map<String, dynamic> e) => StaffRequestReportEntity.fromJson(e)).toList() as M;
     }
@@ -516,6 +556,8 @@ class JsonConvertClassCollection {
     (AddEditReviseViewSGEntity).toString(): AddEditReviseViewSGEntity.fromJson,
     (AttendanceEntity).toString(): AttendanceEntity.fromJson,
     (AttendanceList).toString(): AttendanceList.fromJson,
+    (AttendanceReviewEntity).toString(): AttendanceReviewEntity.fromJson,
+    (AttendanceReviewRows).toString(): AttendanceReviewRows.fromJson,
     (CheckSuccessEntity).toString(): CheckSuccessEntity.fromJson,
     (DeviceListEntity).toString(): DeviceListEntity.fromJson,
     (DeviceListRows).toString(): DeviceListRows.fromJson,
@@ -578,6 +620,9 @@ class JsonConvertClassCollection {
     (LabourReportEntity).toString(): LabourReportEntity.fromJson,
     (LabourReportRows).toString(): LabourReportRows.fromJson,
     (LabourReportTotal).toString(): LabourReportTotal.fromJson,
+    (LabourReportVNEntity).toString(): LabourReportVNEntity.fromJson,
+    (LabourReportVNRows).toString(): LabourReportVNRows.fromJson,
+    (LabourReportVNTotal).toString(): LabourReportVNTotal.fromJson,
     (LabourRequestEditIndexEntity).toString(): LabourRequestEditIndexEntity.fromJson,
     (LabourRequestEditIndexTemplateList).toString(): LabourRequestEditIndexTemplateList.fromJson,
     (LabourRequestEditIndexDepartmentList).toString(): LabourRequestEditIndexDepartmentList.fromJson,
@@ -592,6 +637,10 @@ class JsonConvertClassCollection {
     (LabourRequestSGCountList).toString(): LabourRequestSGCountList.fromJson,
     (LabourRequestWorkFlowEntity).toString(): LabourRequestWorkFlowEntity.fromJson,
     (LabourRequestWorkFlowRecords).toString(): LabourRequestWorkFlowRecords.fromJson,
+    (LabourReviewListEntity).toString(): LabourReviewListEntity.fromJson,
+    (LabourReviewListRows).toString(): LabourReviewListRows.fromJson,
+    (LabourReviewStatusEntity).toString(): LabourReviewStatusEntity.fromJson,
+    (LabourReviewStatusRecords).toString(): LabourReviewStatusRecords.fromJson,
     (ReviseIndexSGEntity).toString(): ReviseIndexSGEntity.fromJson,
     (ReviseIndexSGStatusList).toString(): ReviseIndexSGStatusList.fromJson,
     (ReviseListSGEntity).toString(): ReviseListSGEntity.fromJson,
@@ -614,6 +663,7 @@ class JsonConvertClassCollection {
     (StaffLabourHistoryRowsWorkOut).toString(): StaffLabourHistoryRowsWorkOut.fromJson,
     (StaffRemarkHistoryEntity).toString(): StaffRemarkHistoryEntity.fromJson,
     (StaffRemarkHistoryRows).toString(): StaffRemarkHistoryRows.fromJson,
+    (StaffReportVNEntity).toString(): StaffReportVNEntity.fromJson,
     (StaffRequestReportEntity).toString(): StaffRequestReportEntity.fromJson,
     (StaffReviewHistorySGEntity).toString(): StaffReviewHistorySGEntity.fromJson,
     (StaffReviewHistorySGReviews).toString(): StaffReviewHistorySGReviews.fromJson,

+ 8 - 0
packages/cs_domain/lib/generated/json/job_list_edit_index_s_g_entity.g.dart

@@ -61,6 +61,11 @@ JobListEditIndexSGEntity $JobListEditIndexSGEntityFromJson(Map<String, dynamic>
   if (requestType != null) {
     jobListEditIndexSGEntity.requestType = requestType;
   }
+  final List<JobListEditIndexOption>? positionList = (json['position_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<JobListEditIndexOption>(e) as JobListEditIndexOption).toList();
+  if (positionList != null) {
+    jobListEditIndexSGEntity.positionList = positionList;
+  }
   final List<JobListEditIndexOption>? limitList = (json['limit_list'] as List<dynamic>?)?.map(
           (e) => jsonConvert.convert<JobListEditIndexOption>(e) as JobListEditIndexOption).toList();
   if (limitList != null) {
@@ -85,6 +90,7 @@ Map<String, dynamic> $JobListEditIndexSGEntityToJson(JobListEditIndexSGEntity en
   data['disabled'] = entity.disabled;
   data['outlet_list'] = entity.outletList.map((v) => v.toJson()).toList();
   data['request_type'] = entity.requestType.map((v) => v.toJson()).toList();
+  data['position_list'] = entity.positionList.map((v) => v.toJson()).toList();
   data['limit_list'] = entity.limitList.map((v) => v.toJson()).toList();
   return data;
 }
@@ -105,6 +111,7 @@ extension JobListEditIndexSGEntityExtension on JobListEditIndexSGEntity {
     String? disabled,
     List<JobListEditIndexOption>? outletList,
     List<JobListEditIndexOption>? requestType,
+    List<JobListEditIndexOption>? positionList,
     List<JobListEditIndexOption>? limitList,
   }) {
     return JobListEditIndexSGEntity()
@@ -122,6 +129,7 @@ extension JobListEditIndexSGEntityExtension on JobListEditIndexSGEntity {
       ..disabled = disabled ?? this.disabled
       ..outletList = outletList ?? this.outletList
       ..requestType = requestType ?? this.requestType
+      ..positionList = positionList ?? this.positionList
       ..limitList = limitList ?? this.limitList;
   }
 }

+ 7 - 0
packages/cs_domain/lib/generated/json/job_template_edit_index_entity.g.dart

@@ -11,6 +11,10 @@ JobTemplateEditIndexEntity $JobTemplateEditIndexEntityFromJson(Map<String, dynam
   if (name != null) {
     jobTemplateEditIndexEntity.name = name;
   }
+  final String? jobTitle = jsonConvert.convert<String>(json['job_title']);
+  if (jobTitle != null) {
+    jobTemplateEditIndexEntity.jobTitle = jobTitle;
+  }
   final String? contact = jsonConvert.convert<String>(json['contact']);
   if (contact != null) {
     jobTemplateEditIndexEntity.contact = contact;
@@ -53,6 +57,7 @@ Map<String, dynamic> $JobTemplateEditIndexEntityToJson(JobTemplateEditIndexEntit
   final Map<String, dynamic> data = <String, dynamic>{};
   data['template_id'] = entity.templateId;
   data['name'] = entity.name;
+  data['job_title'] = entity.jobTitle;
   data['contact'] = entity.contact;
   data['contact_no'] = entity.contactNo;
   data['description'] = entity.description;
@@ -68,6 +73,7 @@ extension JobTemplateEditIndexEntityExtension on JobTemplateEditIndexEntity {
   JobTemplateEditIndexEntity copyWith({
     int? templateId,
     String? name,
+    String? jobTitle,
     String? contact,
     String? contactNo,
     String? description,
@@ -80,6 +86,7 @@ extension JobTemplateEditIndexEntityExtension on JobTemplateEditIndexEntity {
     return JobTemplateEditIndexEntity()
       ..templateId = templateId ?? this.templateId
       ..name = name ?? this.name
+      ..jobTitle = jobTitle ?? this.jobTitle
       ..contact = contact ?? this.contact
       ..contactNo = contactNo ?? this.contactNo
       ..description = description ?? this.description

+ 15 - 8
packages/cs_domain/lib/generated/json/job_template_s_g_entity.g.dart

@@ -47,14 +47,14 @@ JobTemplateSGRows $JobTemplateSGRowsFromJson(Map<String, dynamic> json) {
   if (contact != null) {
     jobTemplateSGRows.contact = contact;
   }
+  final String? createdBy = jsonConvert.convert<String>(json['created_by']);
+  if (createdBy != null) {
+    jobTemplateSGRows.createdBy = createdBy;
+  }
   final String? contactNo = jsonConvert.convert<String>(json['contact_no']);
   if (contactNo != null) {
     jobTemplateSGRows.contactNo = contactNo;
   }
-  final String? note = jsonConvert.convert<String>(json['note']);
-  if (note != null) {
-    jobTemplateSGRows.note = note;
-  }
   final String? createdAt = jsonConvert.convert<String>(json['created_at']);
   if (createdAt != null) {
     jobTemplateSGRows.createdAt = createdAt;
@@ -68,6 +68,10 @@ JobTemplateSGRows $JobTemplateSGRowsFromJson(Map<String, dynamic> json) {
   if (actionList != null) {
     jobTemplateSGRows.actionList = actionList;
   }
+  final String? note = jsonConvert.convert<String>(json['note']);
+  if (note != null) {
+    jobTemplateSGRows.note = note;
+  }
   return jobTemplateSGRows;
 }
 
@@ -76,11 +80,12 @@ Map<String, dynamic> $JobTemplateSGRowsToJson(JobTemplateSGRows entity) {
   data['id'] = entity.id;
   data['name'] = entity.name;
   data['contact'] = entity.contact;
+  data['created_by'] = entity.createdBy;
   data['contact_no'] = entity.contactNo;
-  data['note'] = entity.note;
   data['created_at'] = entity.createdAt;
   data['updated_at'] = entity.updatedAt;
   data['action_list'] = entity.actionList;
+  data['note'] = entity.note;
   return data;
 }
 
@@ -89,20 +94,22 @@ extension JobTemplateSGRowsExtension on JobTemplateSGRows {
     int? id,
     String? name,
     String? contact,
+    String? createdBy,
     String? contactNo,
-    String? note,
     String? createdAt,
     String? updatedAt,
     List<String>? actionList,
+    String? note,
   }) {
     return JobTemplateSGRows()
       ..id = id ?? this.id
       ..name = name ?? this.name
       ..contact = contact ?? this.contact
+      ..createdBy = createdBy ?? this.createdBy
       ..contactNo = contactNo ?? this.contactNo
-      ..note = note ?? this.note
       ..createdAt = createdAt ?? this.createdAt
       ..updatedAt = updatedAt ?? this.updatedAt
-      ..actionList = actionList ?? this.actionList;
+      ..actionList = actionList ?? this.actionList
+      ..note = note ?? this.note;
   }
 }

+ 264 - 0
packages/cs_domain/lib/generated/json/labour_report_v_n_entity.g.dart

@@ -0,0 +1,264 @@
+import 'package:domain/generated/json/base/json_convert_content.dart';
+import 'package:domain/entity/response/labour_report_v_n_entity.dart';
+
+LabourReportVNEntity $LabourReportVNEntityFromJson(Map<String, dynamic> json) {
+  final LabourReportVNEntity labourReportVNEntity = LabourReportVNEntity();
+  final String? startDate = jsonConvert.convert<String>(json['start_date']);
+  if (startDate != null) {
+    labourReportVNEntity.startDate = startDate;
+  }
+  final String? endDate = jsonConvert.convert<String>(json['end_date']);
+  if (endDate != null) {
+    labourReportVNEntity.endDate = endDate;
+  }
+  final List<LabourReportVNRows>? rows = (json['rows'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<LabourReportVNRows>(e) as LabourReportVNRows).toList();
+  if (rows != null) {
+    labourReportVNEntity.rows = rows;
+  }
+  final LabourReportVNTotal? total = jsonConvert.convert<LabourReportVNTotal>(json['total']);
+  if (total != null) {
+    labourReportVNEntity.total = total;
+  }
+  return labourReportVNEntity;
+}
+
+Map<String, dynamic> $LabourReportVNEntityToJson(LabourReportVNEntity entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['start_date'] = entity.startDate;
+  data['end_date'] = entity.endDate;
+  data['rows'] = entity.rows.map((v) => v.toJson()).toList();
+  data['total'] = entity.total?.toJson();
+  return data;
+}
+
+extension LabourReportVNEntityExtension on LabourReportVNEntity {
+  LabourReportVNEntity copyWith({
+    String? startDate,
+    String? endDate,
+    List<LabourReportVNRows>? rows,
+    LabourReportVNTotal? total,
+  }) {
+    return LabourReportVNEntity()
+      ..startDate = startDate ?? this.startDate
+      ..endDate = endDate ?? this.endDate
+      ..rows = rows ?? this.rows
+      ..total = total ?? this.total;
+  }
+}
+
+LabourReportVNRows $LabourReportVNRowsFromJson(Map<String, dynamic> json) {
+  final LabourReportVNRows labourReportVNRows = LabourReportVNRows();
+  final String? outletId = jsonConvert.convert<String>(json['outlet_id']);
+  if (outletId != null) {
+    labourReportVNRows.outletId = outletId;
+  }
+  final String? outletName = jsonConvert.convert<String>(json['outlet_name']);
+  if (outletName != null) {
+    labourReportVNRows.outletName = outletName;
+  }
+  final String? num = jsonConvert.convert<String>(json['num']);
+  if (num != null) {
+    labourReportVNRows.num = num;
+  }
+  final String? hrs = jsonConvert.convert<String>(json['hrs']);
+  if (hrs != null) {
+    labourReportVNRows.hrs = hrs;
+  }
+  final String? rms = jsonConvert.convert<String>(json['rms']);
+  if (rms != null) {
+    labourReportVNRows.rms = rms;
+  }
+  final String? amt = jsonConvert.convert<String>(json['amt']);
+  if (amt != null) {
+    labourReportVNRows.amt = amt;
+  }
+  final String? inNum = jsonConvert.convert<String>(json['in_num']);
+  if (inNum != null) {
+    labourReportVNRows.inNum = inNum;
+  }
+  final String? inHrs = jsonConvert.convert<String>(json['in_hrs']);
+  if (inHrs != null) {
+    labourReportVNRows.inHrs = inHrs;
+  }
+  final String? inRms = jsonConvert.convert<String>(json['in_rms']);
+  if (inRms != null) {
+    labourReportVNRows.inRms = inRms;
+  }
+  final String? inAmt = jsonConvert.convert<String>(json['in_amt']);
+  if (inAmt != null) {
+    labourReportVNRows.inAmt = inAmt;
+  }
+  final String? totNum = jsonConvert.convert<String>(json['tot_num']);
+  if (totNum != null) {
+    labourReportVNRows.totNum = totNum;
+  }
+  final String? totHrs = jsonConvert.convert<String>(json['tot_hrs']);
+  if (totHrs != null) {
+    labourReportVNRows.totHrs = totHrs;
+  }
+  final String? totRms = jsonConvert.convert<String>(json['tot_rms']);
+  if (totRms != null) {
+    labourReportVNRows.totRms = totRms;
+  }
+  final String? totAmt = jsonConvert.convert<String>(json['tot_amt']);
+  if (totAmt != null) {
+    labourReportVNRows.totAmt = totAmt;
+  }
+  return labourReportVNRows;
+}
+
+Map<String, dynamic> $LabourReportVNRowsToJson(LabourReportVNRows entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['outlet_id'] = entity.outletId;
+  data['outlet_name'] = entity.outletName;
+  data['num'] = entity.num;
+  data['hrs'] = entity.hrs;
+  data['rms'] = entity.rms;
+  data['amt'] = entity.amt;
+  data['in_num'] = entity.inNum;
+  data['in_hrs'] = entity.inHrs;
+  data['in_rms'] = entity.inRms;
+  data['in_amt'] = entity.inAmt;
+  data['tot_num'] = entity.totNum;
+  data['tot_hrs'] = entity.totHrs;
+  data['tot_rms'] = entity.totRms;
+  data['tot_amt'] = entity.totAmt;
+  return data;
+}
+
+extension LabourReportVNRowsExtension on LabourReportVNRows {
+  LabourReportVNRows copyWith({
+    String? outletId,
+    String? outletName,
+    String? num,
+    String? hrs,
+    String? rms,
+    String? amt,
+    String? inNum,
+    String? inHrs,
+    String? inRms,
+    String? inAmt,
+    String? totNum,
+    String? totHrs,
+    String? totRms,
+    String? totAmt,
+  }) {
+    return LabourReportVNRows()
+      ..outletId = outletId ?? this.outletId
+      ..outletName = outletName ?? this.outletName
+      ..num = num ?? this.num
+      ..hrs = hrs ?? this.hrs
+      ..rms = rms ?? this.rms
+      ..amt = amt ?? this.amt
+      ..inNum = inNum ?? this.inNum
+      ..inHrs = inHrs ?? this.inHrs
+      ..inRms = inRms ?? this.inRms
+      ..inAmt = inAmt ?? this.inAmt
+      ..totNum = totNum ?? this.totNum
+      ..totHrs = totHrs ?? this.totHrs
+      ..totRms = totRms ?? this.totRms
+      ..totAmt = totAmt ?? this.totAmt;
+  }
+}
+
+LabourReportVNTotal $LabourReportVNTotalFromJson(Map<String, dynamic> json) {
+  final LabourReportVNTotal labourReportVNTotal = LabourReportVNTotal();
+  final String? num = jsonConvert.convert<String>(json['num']);
+  if (num != null) {
+    labourReportVNTotal.num = num;
+  }
+  final double? hrs = jsonConvert.convert<double>(json['hrs']);
+  if (hrs != null) {
+    labourReportVNTotal.hrs = hrs;
+  }
+  final String? rms = jsonConvert.convert<String>(json['rms']);
+  if (rms != null) {
+    labourReportVNTotal.rms = rms;
+  }
+  final String? amt = jsonConvert.convert<String>(json['amt']);
+  if (amt != null) {
+    labourReportVNTotal.amt = amt;
+  }
+  final String? inNum = jsonConvert.convert<String>(json['in_num']);
+  if (inNum != null) {
+    labourReportVNTotal.inNum = inNum;
+  }
+  final String? inHrs = jsonConvert.convert<String>(json['in_hrs']);
+  if (inHrs != null) {
+    labourReportVNTotal.inHrs = inHrs;
+  }
+  final String? inRms = jsonConvert.convert<String>(json['in_rms']);
+  if (inRms != null) {
+    labourReportVNTotal.inRms = inRms;
+  }
+  final String? inAmt = jsonConvert.convert<String>(json['in_amt']);
+  if (inAmt != null) {
+    labourReportVNTotal.inAmt = inAmt;
+  }
+  final String? totNum = jsonConvert.convert<String>(json['tot_num']);
+  if (totNum != null) {
+    labourReportVNTotal.totNum = totNum;
+  }
+  final String? totHrs = jsonConvert.convert<String>(json['tot_hrs']);
+  if (totHrs != null) {
+    labourReportVNTotal.totHrs = totHrs;
+  }
+  final String? totRms = jsonConvert.convert<String>(json['tot_rms']);
+  if (totRms != null) {
+    labourReportVNTotal.totRms = totRms;
+  }
+  final String? totAmt = jsonConvert.convert<String>(json['tot_amt']);
+  if (totAmt != null) {
+    labourReportVNTotal.totAmt = totAmt;
+  }
+  return labourReportVNTotal;
+}
+
+Map<String, dynamic> $LabourReportVNTotalToJson(LabourReportVNTotal entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['num'] = entity.num;
+  data['hrs'] = entity.hrs;
+  data['rms'] = entity.rms;
+  data['amt'] = entity.amt;
+  data['in_num'] = entity.inNum;
+  data['in_hrs'] = entity.inHrs;
+  data['in_rms'] = entity.inRms;
+  data['in_amt'] = entity.inAmt;
+  data['tot_num'] = entity.totNum;
+  data['tot_hrs'] = entity.totHrs;
+  data['tot_rms'] = entity.totRms;
+  data['tot_amt'] = entity.totAmt;
+  return data;
+}
+
+extension LabourReportVNTotalExtension on LabourReportVNTotal {
+  LabourReportVNTotal copyWith({
+    String? num,
+    double? hrs,
+    String? rms,
+    String? amt,
+    String? inNum,
+    String? inHrs,
+    String? inRms,
+    String? inAmt,
+    String? totNum,
+    String? totHrs,
+    String? totRms,
+    String? totAmt,
+  }) {
+    return LabourReportVNTotal()
+      ..num = num ?? this.num
+      ..hrs = hrs ?? this.hrs
+      ..rms = rms ?? this.rms
+      ..amt = amt ?? this.amt
+      ..inNum = inNum ?? this.inNum
+      ..inHrs = inHrs ?? this.inHrs
+      ..inRms = inRms ?? this.inRms
+      ..inAmt = inAmt ?? this.inAmt
+      ..totNum = totNum ?? this.totNum
+      ..totHrs = totHrs ?? this.totHrs
+      ..totRms = totRms ?? this.totRms
+      ..totAmt = totAmt ?? this.totAmt;
+  }
+}

+ 9 - 1
packages/cs_domain/lib/generated/json/labour_request_s_g_add_index_entity.g.dart

@@ -18,6 +18,11 @@ LabourRequestSGAddIndexEntity $LabourRequestSGAddIndexEntityFromJson(Map<String,
   if (requestType != null) {
     labourRequestSGAddIndexEntity.requestType = requestType;
   }
+  final List<LabourRequestSGAddIndexOption>? positionList = (json['position_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<LabourRequestSGAddIndexOption>(e) as LabourRequestSGAddIndexOption).toList();
+  if (positionList != null) {
+    labourRequestSGAddIndexEntity.positionList = positionList;
+  }
   return labourRequestSGAddIndexEntity;
 }
 
@@ -26,6 +31,7 @@ Map<String, dynamic> $LabourRequestSGAddIndexEntityToJson(LabourRequestSGAddInde
   data['title_list'] = entity.titleList.map((v) => v.toJson()).toList();
   data['outlet_list'] = entity.outletList.map((v) => v.toJson()).toList();
   data['request_type'] = entity.requestType.map((v) => v.toJson()).toList();
+  data['position_list'] = entity.positionList.map((v) => v.toJson()).toList();
   return data;
 }
 
@@ -34,11 +40,13 @@ extension LabourRequestSGAddIndexEntityExtension on LabourRequestSGAddIndexEntit
     List<LabourRequestSGAddIndexOption>? titleList,
     List<LabourRequestSGAddIndexOption>? outletList,
     List<LabourRequestSGAddIndexOption>? requestType,
+    List<LabourRequestSGAddIndexOption>? positionList,
   }) {
     return LabourRequestSGAddIndexEntity()
       ..titleList = titleList ?? this.titleList
       ..outletList = outletList ?? this.outletList
-      ..requestType = requestType ?? this.requestType;
+      ..requestType = requestType ?? this.requestType
+      ..positionList = positionList ?? this.positionList;
   }
 }
 

+ 129 - 0
packages/cs_domain/lib/generated/json/labour_review_list_entity.g.dart

@@ -0,0 +1,129 @@
+import 'package:domain/generated/json/base/json_convert_content.dart';
+import 'package:domain/entity/response/labour_review_list_entity.dart';
+
+LabourReviewListEntity $LabourReviewListEntityFromJson(Map<String, dynamic> json) {
+  final LabourReviewListEntity labourReviewListEntity = LabourReviewListEntity();
+  final int? total = jsonConvert.convert<int>(json['total']);
+  if (total != null) {
+    labourReviewListEntity.total = total;
+  }
+  final List<LabourReviewListRows>? rows = (json['rows'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<LabourReviewListRows>(e) as LabourReviewListRows).toList();
+  if (rows != null) {
+    labourReviewListEntity.rows = rows;
+  }
+  return labourReviewListEntity;
+}
+
+Map<String, dynamic> $LabourReviewListEntityToJson(LabourReviewListEntity entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['total'] = entity.total;
+  data['rows'] = entity.rows.map((v) => v.toJson()).toList();
+  return data;
+}
+
+extension LabourReviewListEntityExtension on LabourReviewListEntity {
+  LabourReviewListEntity copyWith({
+    int? total,
+    List<LabourReviewListRows>? rows,
+  }) {
+    return LabourReviewListEntity()
+      ..total = total ?? this.total
+      ..rows = rows ?? this.rows;
+  }
+}
+
+LabourReviewListRows $LabourReviewListRowsFromJson(Map<String, dynamic> json) {
+  final LabourReviewListRows labourReviewListRows = LabourReviewListRows();
+  final String? recordId = jsonConvert.convert<String>(json['record_id']);
+  if (recordId != null) {
+    labourReviewListRows.recordId = recordId;
+  }
+  final String? requestId = jsonConvert.convert<String>(json['request_id']);
+  if (requestId != null) {
+    labourReviewListRows.requestId = requestId;
+  }
+  final String? jobTitle = jsonConvert.convert<String>(json['job_title']);
+  if (jobTitle != null) {
+    labourReviewListRows.jobTitle = jobTitle;
+  }
+  final String? jobTime = jsonConvert.convert<String>(json['job_time']);
+  if (jobTime != null) {
+    labourReviewListRows.jobTime = jobTime;
+  }
+  final String? departmentName = jsonConvert.convert<String>(json['department_name']);
+  if (departmentName != null) {
+    labourReviewListRows.departmentName = departmentName;
+  }
+  final String? salaryShow = jsonConvert.convert<String>(json['salary_show']);
+  if (salaryShow != null) {
+    labourReviewListRows.salaryShow = salaryShow;
+  }
+  final String? needNum = jsonConvert.convert<String>(json['need_num']);
+  if (needNum != null) {
+    labourReviewListRows.needNum = needNum;
+  }
+  final String? statusShow = jsonConvert.convert<String>(json['status_show']);
+  if (statusShow != null) {
+    labourReviewListRows.statusShow = statusShow;
+  }
+  final String? createdAt = jsonConvert.convert<String>(json['created_at']);
+  if (createdAt != null) {
+    labourReviewListRows.createdAt = createdAt;
+  }
+  final List<String>? btnList = (json['btn_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<String>(e) as String).toList();
+  if (btnList != null) {
+    labourReviewListRows.btnList = btnList;
+  }
+  final bool? isSelected = jsonConvert.convert<bool>(json['isSelected']);
+  if (isSelected != null) {
+    labourReviewListRows.isSelected = isSelected;
+  }
+  return labourReviewListRows;
+}
+
+Map<String, dynamic> $LabourReviewListRowsToJson(LabourReviewListRows entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['record_id'] = entity.recordId;
+  data['request_id'] = entity.requestId;
+  data['job_title'] = entity.jobTitle;
+  data['job_time'] = entity.jobTime;
+  data['department_name'] = entity.departmentName;
+  data['salary_show'] = entity.salaryShow;
+  data['need_num'] = entity.needNum;
+  data['status_show'] = entity.statusShow;
+  data['created_at'] = entity.createdAt;
+  data['btn_list'] = entity.btnList;
+  data['isSelected'] = entity.isSelected;
+  return data;
+}
+
+extension LabourReviewListRowsExtension on LabourReviewListRows {
+  LabourReviewListRows copyWith({
+    String? recordId,
+    String? requestId,
+    String? jobTitle,
+    String? jobTime,
+    String? departmentName,
+    String? salaryShow,
+    String? needNum,
+    String? statusShow,
+    String? createdAt,
+    List<String>? btnList,
+    bool? isSelected,
+  }) {
+    return LabourReviewListRows()
+      ..recordId = recordId ?? this.recordId
+      ..requestId = requestId ?? this.requestId
+      ..jobTitle = jobTitle ?? this.jobTitle
+      ..jobTime = jobTime ?? this.jobTime
+      ..departmentName = departmentName ?? this.departmentName
+      ..salaryShow = salaryShow ?? this.salaryShow
+      ..needNum = needNum ?? this.needNum
+      ..statusShow = statusShow ?? this.statusShow
+      ..createdAt = createdAt ?? this.createdAt
+      ..btnList = btnList ?? this.btnList
+      ..isSelected = isSelected ?? this.isSelected;
+  }
+}

+ 114 - 0
packages/cs_domain/lib/generated/json/labour_review_status_entity.g.dart

@@ -0,0 +1,114 @@
+import 'package:domain/generated/json/base/json_convert_content.dart';
+import 'package:domain/entity/response/labour_review_status_entity.dart';
+
+LabourReviewStatusEntity $LabourReviewStatusEntityFromJson(Map<String, dynamic> json) {
+  final LabourReviewStatusEntity labourReviewStatusEntity = LabourReviewStatusEntity();
+  final int? coDepartmentId = jsonConvert.convert<int>(json['co_department_id']);
+  if (coDepartmentId != null) {
+    labourReviewStatusEntity.coDepartmentId = coDepartmentId;
+  }
+  final List<LabourReviewStatusRecords>? records = (json['records'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<LabourReviewStatusRecords>(e) as LabourReviewStatusRecords).toList();
+  if (records != null) {
+    labourReviewStatusEntity.records = records;
+  }
+  return labourReviewStatusEntity;
+}
+
+Map<String, dynamic> $LabourReviewStatusEntityToJson(LabourReviewStatusEntity entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['co_department_id'] = entity.coDepartmentId;
+  data['records'] = entity.records.map((v) => v.toJson()).toList();
+  return data;
+}
+
+extension LabourReviewStatusEntityExtension on LabourReviewStatusEntity {
+  LabourReviewStatusEntity copyWith({
+    int? coDepartmentId,
+    List<LabourReviewStatusRecords>? records,
+  }) {
+    return LabourReviewStatusEntity()
+      ..coDepartmentId = coDepartmentId ?? this.coDepartmentId
+      ..records = records ?? this.records;
+  }
+}
+
+LabourReviewStatusRecords $LabourReviewStatusRecordsFromJson(Map<String, dynamic> json) {
+  final LabourReviewStatusRecords labourReviewStatusRecords = LabourReviewStatusRecords();
+  final String? serialNumber = jsonConvert.convert<String>(json['serial_number']);
+  if (serialNumber != null) {
+    labourReviewStatusRecords.serialNumber = serialNumber;
+  }
+  final String? nodeName = jsonConvert.convert<String>(json['node_name']);
+  if (nodeName != null) {
+    labourReviewStatusRecords.nodeName = nodeName;
+  }
+  final String? statusShow = jsonConvert.convert<String>(json['status_show']);
+  if (statusShow != null) {
+    labourReviewStatusRecords.statusShow = statusShow;
+  }
+  final String? auditName = jsonConvert.convert<String>(json['audit_name']);
+  if (auditName != null) {
+    labourReviewStatusRecords.auditName = auditName;
+  }
+  final String? auditTime = jsonConvert.convert<String>(json['audit_time']);
+  if (auditTime != null) {
+    labourReviewStatusRecords.auditTime = auditTime;
+  }
+  final dynamic auditMark = json['audit_mark'];
+  if (auditMark != null) {
+    labourReviewStatusRecords.auditMark = auditMark;
+  }
+  final String? createdAt = jsonConvert.convert<String>(json['created_at']);
+  if (createdAt != null) {
+    labourReviewStatusRecords.createdAt = createdAt;
+  }
+  final String? assigneeTypeShow = jsonConvert.convert<String>(json['assignee_type_show']);
+  if (assigneeTypeShow != null) {
+    labourReviewStatusRecords.assigneeTypeShow = assigneeTypeShow;
+  }
+  final String? designationShow = jsonConvert.convert<String>(json['designation_show']);
+  if (designationShow != null) {
+    labourReviewStatusRecords.designationShow = designationShow;
+  }
+  return labourReviewStatusRecords;
+}
+
+Map<String, dynamic> $LabourReviewStatusRecordsToJson(LabourReviewStatusRecords entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['serial_number'] = entity.serialNumber;
+  data['node_name'] = entity.nodeName;
+  data['status_show'] = entity.statusShow;
+  data['audit_name'] = entity.auditName;
+  data['audit_time'] = entity.auditTime;
+  data['audit_mark'] = entity.auditMark;
+  data['created_at'] = entity.createdAt;
+  data['assignee_type_show'] = entity.assigneeTypeShow;
+  data['designation_show'] = entity.designationShow;
+  return data;
+}
+
+extension LabourReviewStatusRecordsExtension on LabourReviewStatusRecords {
+  LabourReviewStatusRecords copyWith({
+    String? serialNumber,
+    String? nodeName,
+    String? statusShow,
+    String? auditName,
+    String? auditTime,
+    dynamic auditMark,
+    String? createdAt,
+    String? assigneeTypeShow,
+    String? designationShow,
+  }) {
+    return LabourReviewStatusRecords()
+      ..serialNumber = serialNumber ?? this.serialNumber
+      ..nodeName = nodeName ?? this.nodeName
+      ..statusShow = statusShow ?? this.statusShow
+      ..auditName = auditName ?? this.auditName
+      ..auditTime = auditTime ?? this.auditTime
+      ..auditMark = auditMark ?? this.auditMark
+      ..createdAt = createdAt ?? this.createdAt
+      ..assigneeTypeShow = assigneeTypeShow ?? this.assigneeTypeShow
+      ..designationShow = designationShow ?? this.designationShow;
+  }
+}

+ 50 - 0
packages/cs_domain/lib/generated/json/staff_report_v_n_entity.g.dart

@@ -0,0 +1,50 @@
+import 'package:domain/generated/json/base/json_convert_content.dart';
+import 'package:domain/entity/response/staff_report_v_n_entity.dart';
+import 'package:domain/entity/response/staff_request_report_entity.dart';
+
+
+StaffReportVNEntity $StaffReportVNEntityFromJson(Map<String, dynamic> json) {
+  final StaffReportVNEntity staffReportVNEntity = StaffReportVNEntity();
+  final String? startDate = jsonConvert.convert<String>(json['start_date']);
+  if (startDate != null) {
+    staffReportVNEntity.startDate = startDate;
+  }
+  final String? endDate = jsonConvert.convert<String>(json['end_date']);
+  if (endDate != null) {
+    staffReportVNEntity.endDate = endDate;
+  }
+  final List<StaffRequestReportEntity>? requestList = (json['request_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<StaffRequestReportEntity>(e) as StaffRequestReportEntity).toList();
+  if (requestList != null) {
+    staffReportVNEntity.requestList = requestList;
+  }
+  final int? totalNum = jsonConvert.convert<int>(json['total_num']);
+  if (totalNum != null) {
+    staffReportVNEntity.totalNum = totalNum;
+  }
+  return staffReportVNEntity;
+}
+
+Map<String, dynamic> $StaffReportVNEntityToJson(StaffReportVNEntity entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['start_date'] = entity.startDate;
+  data['end_date'] = entity.endDate;
+  data['request_list'] = entity.requestList.map((v) => v.toJson()).toList();
+  data['total_num'] = entity.totalNum;
+  return data;
+}
+
+extension StaffReportVNEntityExtension on StaffReportVNEntity {
+  StaffReportVNEntity copyWith({
+    String? startDate,
+    String? endDate,
+    List<StaffRequestReportEntity>? requestList,
+    int? totalNum,
+  }) {
+    return StaffReportVNEntity()
+      ..startDate = startDate ?? this.startDate
+      ..endDate = endDate ?? this.endDate
+      ..requestList = requestList ?? this.requestList
+      ..totalNum = totalNum ?? this.totalNum;
+  }
+}

+ 91 - 0
packages/cs_domain/lib/repository/job_repository.dart

@@ -20,6 +20,7 @@ import 'package:shared/utils/util.dart';
 
 import '../constants/api_constants.dart';
 import '../entity/response/attendance_entity.dart';
+import '../entity/response/attendance_review_entity.dart';
 import '../entity/response/check_success_entity.dart';
 import '../entity/response/job_list_applied_work_flow_entity.dart';
 
@@ -730,4 +731,94 @@ class JobRepository extends GetxService {
     }
     return result.convert();
   }
+
+  /// 考勤审核列表
+  Future<HttpResult<AttendanceReviewEntity>> fetchAttendanceReviewList(
+      String? keyword, {
+        required int curPage,
+        CancelToken? cancelToken,
+      }) async {
+    //参数
+    Map<String, String> params = {};
+    params["cur_page"] = curPage.toString();
+    params["page_size"] = "20";
+
+    if (!Utils.isEmpty(keyword)) {
+      params["staff_name"] = keyword!;
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiAttendanceReviewTable,
+      params: params,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      final json = result.getDataJson();
+      var data = AttendanceReviewEntity.fromJson(json!);
+      //重新赋值data或list
+      return result.convert<AttendanceReviewEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 考勤的批量同意
+  Future<HttpResult> approveAttendanceReviews(
+      String? recordIds, {
+        CancelToken? cancelToken,
+      }) async {
+    //参数
+    Map<String, String> params = {};
+    params['record_ids'] = recordIds ?? "";
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiAttendanceReviewApprove,
+      method: HttpMethod.POST,
+      params: params,
+      networkDebounce: true,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      return result.convert();
+    }
+    return result.convert();
+  }
+
+  /// 考勤的批量拒绝
+  Future<HttpResult> rejectLabourReviews(
+      String? recordIds,
+      String? reason, {
+        CancelToken? cancelToken,
+      }) async {
+    //参数
+    Map<String, String> params = {};
+    params['record_ids'] = recordIds ?? "";
+
+    if (Utils.isNotEmpty(reason)) {
+      params['audit_mark'] = reason ?? "";
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiAttendanceReviewReject,
+      method: HttpMethod.POST,
+      params: params,
+      networkDebounce: true,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      return result.convert();
+    }
+    return result.convert();
+  }
+
 }

+ 5 - 4
packages/cs_domain/lib/repository/job_sg_repository.dart

@@ -207,6 +207,7 @@ class JobSGRepository extends GetxService {
 
   /// 批量修改员工信息或考勤
   Future<HttpResult> batchEditJobApplied(
+    String? jobId,
     String? appliedIds,
     String? startTime,
     String? endTime,
@@ -217,6 +218,7 @@ class JobSGRepository extends GetxService {
     //参数
     Map<String, String> params = {};
     params['applied_ids'] = appliedIds ?? "";
+    params['job_id'] = jobId ?? "";
 
     if (!Utils.isEmpty(startTime)) {
       params['start_time'] = startTime ?? "";
@@ -605,9 +607,9 @@ class JobSGRepository extends GetxService {
 
   /// 根据ID获取主列表的Item数据,用于刷新Item
   Future<HttpResult<ReviseListSGEntity>> fetchReviseListByIds(
-      String? reviseId, {
-        CancelToken? cancelToken,
-      }) async {
+    String? reviseId, {
+    CancelToken? cancelToken,
+  }) async {
     //参数
     Map<String, String> params = {};
     params["cur_page"] = "1";
@@ -834,5 +836,4 @@ class JobSGRepository extends GetxService {
     }
     return result.convert();
   }
-
 }

+ 406 - 0
packages/cs_domain/lib/repository/labour_repository.dart

@@ -9,6 +9,9 @@ import 'package:plugin_platform/http/http_result.dart';
 import 'package:shared/utils/util.dart';
 
 import '../constants/api_constants.dart';
+import '../entity/response/job_template_edit_index_entity.dart';
+import '../entity/response/labour_review_list_entity.dart';
+import '../entity/response/labour_review_status_entity.dart';
 
 /// 用工请求相关
 class LabourRepository extends GetxService {
@@ -307,4 +310,407 @@ class LabourRepository extends GetxService {
     }
     return result.convert();
   }
+
+  /// 添加工作模板的详情或选项数据
+  Future<HttpResult<JobTemplateEditIndexEntity>> fetchJobTemplateAddIndex({
+    CancelToken? cancelToken,
+  }) async {
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiJobTemplateAddIndexSG,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      final json = result.getDataJson();
+      var data = JobTemplateEditIndexEntity.fromJson(json!);
+      //重新赋值data或list
+      return result.convert<JobTemplateEditIndexEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 编辑工作模板的详情或选项数据
+  Future<HttpResult<JobTemplateEditIndexEntity>> fetchJobTemplateEditIndex(
+    String? templateId, {
+    CancelToken? cancelToken,
+  }) async {
+    Map<String, String> params = {};
+    params["template_id"] = templateId ?? "";
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiJobTemplateEditIndexSG,
+      params: params,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      final json = result.getDataJson();
+      var data = JobTemplateEditIndexEntity.fromJson(json!);
+      //重新赋值data或list
+      return result.convert<JobTemplateEditIndexEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 添加工作模板的提交
+  Future<HttpResult> addLabourTemplateSubmit(
+    String? name,
+    String? description,
+    String? note,
+    String? age,
+    String? sex,
+    String? language, {
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    params['job_title'] = name ?? "";
+
+    if (!Utils.isEmpty(description)) {
+      params["description"] = description!;
+    }
+
+    if (!Utils.isEmpty(note)) {
+      params["note"] = note!;
+    }
+
+    if (!Utils.isEmpty(age)) {
+      params["age"] = age!;
+    }
+
+    if (!Utils.isEmpty(sex)) {
+      params["gender"] = sex!;
+    }
+
+    if (!Utils.isEmpty(language)) {
+      params["language"] = language!;
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiJobTemplateAddSubmitSG,
+      method: HttpMethod.POST,
+      params: params,
+      networkDebounce: true,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      return result.convert();
+    }
+    return result.convert();
+  }
+
+  /// 编辑工作模板的提交
+  Future<HttpResult> editLabourTemplateSubmit(
+    String? templateId,
+    String? name,
+    String? description,
+    String? note,
+    String? age,
+    String? sex,
+    String? language, {
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    params['job_title'] = name ?? "";
+    params['template_id'] = templateId ?? "";
+
+    if (!Utils.isEmpty(description)) {
+      params["description"] = description!;
+    }
+
+    if (!Utils.isEmpty(note)) {
+      params["note"] = note!;
+    }
+
+    if (!Utils.isEmpty(age)) {
+      params["age"] = age!;
+    }
+
+    if (!Utils.isEmpty(sex)) {
+      params["gender"] = sex!;
+    }
+
+    if (!Utils.isEmpty(language)) {
+      params["language"] = language!;
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiJobTemplateEditSubmitSG,
+      method: HttpMethod.POST,
+      params: params,
+      networkDebounce: true,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      return result.convert();
+    }
+    return result.convert();
+  }
+
+  /// 用工审核选项
+  Future<HttpResult<LabourRequestIndexEntity>> fetchLabourReviewIndex({
+    CancelToken? cancelToken,
+  }) async {
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiLabourReviewIndex,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      final json = result.getDataJson();
+      var data = LabourRequestIndexEntity.fromJson(json!);
+      //重新赋值data或list
+      return result.convert<LabourRequestIndexEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 用工审核列表
+  Future<HttpResult<LabourReviewListEntity>> fetchLabourReviewList(
+    String? keyword,
+    String? startDate,
+    String? endDate,
+    String? departmentId, {
+    required int curPage,
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    params["cur_page"] = curPage.toString();
+    params["page_size"] = "20";
+
+    if (!Utils.isEmpty(keyword)) {
+      params["job_title"] = keyword!;
+    }
+    if (!Utils.isEmpty(startDate)) {
+      params["job_start"] = startDate!;
+    }
+    if (!Utils.isEmpty(endDate)) {
+      params["job_end"] = endDate!;
+    }
+    if (!Utils.isEmpty(departmentId)) {
+      params["department_id"] = departmentId!;
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiLabourReviewTable,
+      params: params,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      final json = result.getDataJson();
+      var data = LabourReviewListEntity.fromJson(json!);
+      //重新赋值data或list
+      return result.convert<LabourReviewListEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 根据ID获取主列表的Item数据,用于刷新审核列表Item
+  Future<HttpResult<LabourReviewListEntity>> fetchItemByRecordId(
+    String? recordId, {
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    params["cur_page"] = "1";
+    params["page_size"] = "1";
+
+    if (!Utils.isEmpty(recordId)) {
+      params["record_id"] = recordId!;
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiLabourReviewTable,
+      params: params,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      final json = result.getDataJson();
+      var data = LabourReviewListEntity.fromJson(json!);
+      //重新赋值data或list
+      return result.convert<LabourReviewListEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 用工审核的编辑详情
+  Future<HttpResult<LabourRequestEditIndexEntity>> fetchLabourReviewDetail(
+    String? requestId, {
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    params['request_id'] = requestId ?? "";
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiLabourReviewDetail,
+      params: params,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      final json = result.getDataJson();
+      var data = LabourRequestEditIndexEntity.fromJson(json!);
+      //重新赋值data或list
+      return result.convert<LabourRequestEditIndexEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 用工审核的编辑提交
+  Future<HttpResult> editLabourReviewSubmit(
+    String? requestId,
+    String? templateId,
+    String? jobStart,
+    String? jobEnd,
+    String? departmentId,
+    String? needNum,
+    String? salaryBy,
+    String? amount, {
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    params['request_id'] = requestId ?? "";
+    params['template_id'] = templateId ?? "";
+    params['job_start'] = jobStart ?? "";
+    params['job_end'] = jobEnd ?? "";
+    params['need_num'] = needNum ?? "";
+    if (!Utils.isEmpty(departmentId)) {
+      params["co_department_id"] = departmentId!;
+    }
+
+    params['salary_by'] = salaryBy ?? "";
+
+    if (!Utils.isEmpty(amount)) {
+      params["amount"] = amount!;
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiLabourReviewEdit,
+      method: HttpMethod.POST,
+      params: params,
+      networkDebounce: true,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      return result.convert();
+    }
+    return result.convert();
+  }
+
+  /// 用工审核的审核流程列表
+  Future<HttpResult<LabourReviewStatusEntity>> fetchLabourReviewStatusView(
+    String? requestId, {
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    params['request_id'] = requestId ?? "";
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiLabourReviewStatusView,
+      params: params,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      final json = result.getDataJson();
+      var data = LabourReviewStatusEntity.fromJson(json!);
+      //重新赋值data或list
+      return result.convert<LabourReviewStatusEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 用工审核的批量同意
+  Future<HttpResult> approveLabourReviews(
+    String? recordIds, {
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    params['record_ids'] = recordIds ?? "";
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiLabourReviewApprove,
+      method: HttpMethod.POST,
+      params: params,
+      networkDebounce: true,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      return result.convert();
+    }
+    return result.convert();
+  }
+
+  /// 用工审核的批量拒绝
+  Future<HttpResult> rejectLabourReviews(
+    String? recordIds,
+    String? reason, {
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    params['record_ids'] = recordIds ?? "";
+
+    if (Utils.isNotEmpty(reason)) {
+      params['audit_mark'] = reason ?? "";
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiLabourReviewReject,
+      method: HttpMethod.POST,
+      params: params,
+      networkDebounce: true,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      return result.convert();
+    }
+    return result.convert();
+  }
 }

+ 23 - 13
packages/cs_domain/lib/repository/labour_sg_repository.dart

@@ -34,7 +34,7 @@ class LabourSGRepository extends GetxService {
     params["cur_page"] = curPage.toString();
     params["page_size"] = "10";
 
-    if (!Utils.isEmpty(keyword)) {
+    if (Utils.isNotEmpty(keyword)) {
       params["keyword"] = keyword!;
     }
 
@@ -352,12 +352,12 @@ class LabourSGRepository extends GetxService {
   Future<HttpResult> addJobTemplateSubmit(
     String? name,
     String? contact,
-    String? contact_no,
+    String? contactNo,
     String? description,
     String? note,
     String? age,
     String? sex,
-    String? with_food_cert,
+    String? withFoodCert,
     String? language, {
     CancelToken? cancelToken,
   }) async {
@@ -369,8 +369,8 @@ class LabourSGRepository extends GetxService {
       params["contact"] = contact!;
     }
 
-    if (!Utils.isEmpty(contact_no)) {
-      params["contact_no"] = contact_no!;
+    if (!Utils.isEmpty(contactNo)) {
+      params["contact_no"] = contactNo!;
     }
 
     if (!Utils.isEmpty(description)) {
@@ -389,8 +389,8 @@ class LabourSGRepository extends GetxService {
       params["sex"] = sex!;
     }
 
-    if (!Utils.isEmpty(with_food_cert)) {
-      params["with_food_cert"] = with_food_cert!;
+    if (!Utils.isEmpty(withFoodCert)) {
+      params["with_food_cert"] = withFoodCert!;
     }
 
     if (!Utils.isEmpty(language)) {
@@ -419,12 +419,12 @@ class LabourSGRepository extends GetxService {
     String? templateId,
     String? name,
     String? contact,
-    String? contact_no,
+    String? contactNo,
     String? description,
     String? note,
     String? age,
     String? sex,
-    String? with_food_cert,
+    String? withFoodCert,
     String? language, {
     CancelToken? cancelToken,
   }) async {
@@ -437,8 +437,8 @@ class LabourSGRepository extends GetxService {
       params["contact"] = contact!;
     }
 
-    if (!Utils.isEmpty(contact_no)) {
-      params["contact_no"] = contact_no!;
+    if (!Utils.isEmpty(contactNo)) {
+      params["contact_no"] = contactNo!;
     }
 
     if (!Utils.isEmpty(description)) {
@@ -457,8 +457,8 @@ class LabourSGRepository extends GetxService {
       params["sex"] = sex!;
     }
 
-    if (!Utils.isEmpty(with_food_cert)) {
-      params["with_food_cert"] = with_food_cert!;
+    if (!Utils.isEmpty(withFoodCert)) {
+      params["with_food_cert"] = withFoodCert!;
     }
 
     if (!Utils.isEmpty(language)) {
@@ -571,6 +571,7 @@ class LabourSGRepository extends GetxService {
     String? femaleLimit,
     String? needNum,
     String? requestType,
+    String? positionId,
     String? remark, {
     CancelToken? cancelToken,
   }) async {
@@ -604,6 +605,10 @@ class LabourSGRepository extends GetxService {
       params['request_type'] = requestType ?? "";
     }
 
+    if (Utils.isNotEmpty(positionId) && requestType == "1") {
+      params['position_id'] = positionId ?? "";
+    }
+
     if (Utils.isNotEmpty(remark)) {
       params['remark'] = remark ?? "";
     }
@@ -760,6 +765,7 @@ class LabourSGRepository extends GetxService {
     String? femaleLimit,
     String? needNum,
     String? requestType,
+    String? positionId,
     String? remark, {
     CancelToken? cancelToken,
   }) async {
@@ -785,6 +791,10 @@ class LabourSGRepository extends GetxService {
       params['request_type'] = requestType ?? "";
     }
 
+    if (Utils.isNotEmpty(positionId) && requestType == "1") {
+      params['position_id'] = positionId ?? "";
+    }
+
     if (Utils.isNotEmpty(remark)) {
       params['remark'] = remark ?? "";
     }

+ 73 - 6
packages/cs_domain/lib/repository/other_repository.dart

@@ -6,9 +6,12 @@ import 'package:get/get.dart';
 import 'package:plugin_platform/dio_export.dart';
 import 'package:plugin_platform/http/http_provider.dart';
 import 'package:plugin_platform/http/http_result.dart';
+import 'package:shared/utils/log_utils.dart';
 import 'package:shared/utils/util.dart';
 
 import '../constants/api_constants.dart';
+import '../entity/response/labour_report_v_n_entity.dart';
+import '../entity/response/staff_report_v_n_entity.dart';
 
 /// 报表与其他的相关数据
 class OtherRepository extends GetxService {
@@ -51,10 +54,10 @@ class OtherRepository extends GetxService {
   }) async {
     //参数
     Map<String, String> params = {};
-    if (Utils.isEmpty(startDate)) {
+    if (Utils.isNotEmpty(startDate)) {
       params["start_date"] = startDate!;
     }
-    if (Utils.isEmpty(endDate)) {
+    if (Utils.isNotEmpty(endDate)) {
       params["end_date"] = endDate!;
     }
 
@@ -83,10 +86,10 @@ class OtherRepository extends GetxService {
   }) async {
     //参数
     Map<String, String> params = {};
-    if (Utils.isEmpty(startDate)) {
+    if (Utils.isNotEmpty(startDate)) {
       params["start_date"] = startDate!;
     }
-    if (Utils.isEmpty(endDate)) {
+    if (Utils.isNotEmpty(endDate)) {
       params["end_date"] = endDate!;
     }
 
@@ -115,10 +118,10 @@ class OtherRepository extends GetxService {
   }) async {
     //参数
     Map<String, String> params = {};
-    if (Utils.isEmpty(startDate)) {
+    if (Utils.isNotEmpty(startDate)) {
       params["start_date"] = startDate!;
     }
-    if (Utils.isEmpty(endDate)) {
+    if (Utils.isNotEmpty(endDate)) {
       params["end_date"] = endDate!;
     }
 
@@ -146,4 +149,68 @@ class OtherRepository extends GetxService {
 
   }
 
+  /// 越南的员工报表
+  Future<HttpResult<StaffReportVNEntity>> fetchStaffReportVN(
+      String? startDate,
+      String? endDate, {
+        CancelToken? cancelToken,
+      }) async {
+    //参数
+    Map<String, String> params = {};
+    if (Utils.isNotEmpty(startDate)) {
+      params["start_date"] = startDate!;
+    }
+    if (Utils.isNotEmpty(endDate)) {
+      params["end_date"] = endDate!;
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiReportStaffRequest,
+      params: params,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      final json = result.getDataJson();
+      var data = StaffReportVNEntity.fromJson(json!);
+      //重新赋值data或list
+      return result.convert<StaffReportVNEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 越南的用工报表
+  Future<HttpResult<LabourReportVNEntity>> fetchReportLabourVN(
+      String? startDate,
+      String? endDate, {
+        CancelToken? cancelToken,
+      }) async {
+    //参数
+    Map<String, String> params = {};
+    if (Utils.isNotEmpty(startDate)) {
+      params["start_date"] = startDate!;
+    }
+    if (Utils.isNotEmpty(endDate)) {
+      params["end_date"] = endDate!;
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiReportLabour,
+      params: params,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      final json = result.getDataJson();
+      var data = LabourReportVNEntity.fromJson(json!);
+      //重新赋值data或list
+      return result.convert<LabourReportVNEntity>(data: data);
+    }
+    return result.convert();
+  }
+
 }

+ 0 - 2
packages/cs_plugin_basic/lib/constants/app_constant.dart

@@ -7,8 +7,6 @@ class AppConstant {
   /// App运行在Release环境时,inProduction为true;当App运行在Debug和Profile环境时,inProduction为false
   static const bool inProduction = kReleaseMode; //本地打包的编译环境是 Debug 还是 Release (自动生成,不需手动修改)
 
-  static int selectCountry = 0;  //当前选中的国家,默认越南   0越南  1新加坡  2马来西亚
-
   static const String theme = 'AppTheme';
   static const String locale = 'locale';
 

+ 2 - 2
packages/cs_plugin_basic/lib/service/app_config_service.dart

@@ -22,7 +22,7 @@ class ConfigService extends GetxService {
   static ConfigService get to => Get.find();
 
   //选择的国家
-  RxInt curSelectCountry = 0.obs;
+  RxInt selectCountry = 0.obs;  //0 越南  1 新加坡  2 韩国
 
   // 设备信息
   /// android 设备信息
@@ -99,7 +99,7 @@ class ConfigService extends GetxService {
 
     //赋值选中的国家
     int country = SPUtil.getInt(AppConstant.storageSelectedCountry, defValue: 0) ?? 0;
-    curSelectCountry.value = country;
+    selectCountry.value = country;
 
     //打印应用与设备的信息
     getDeviceInfo();

+ 5 - 1
packages/cs_plugin_basic/lib/service/http_provider_injection.dart

@@ -14,8 +14,13 @@ class HttpProviderInjection {
     String baseUrl;
     int country = SPUtil.getInt(AppConstant.storageSelectedCountry, defValue: 0) ?? 0;
     if (country == 1) {
+      //新加坡
       baseUrl = ApiConstants.sgBaseUrl;
+    } else if (country == 2) {
+      //韩国
+      baseUrl = ApiConstants.koreaBaseUrl;
     } else {
+      //默认越南
       baseUrl = ApiConstants.baseUrl;
     }
 
@@ -33,6 +38,5 @@ class HttpProviderInjection {
     //找到单例对象 HttpProvider,切换域名
     HttpProvider httpProvider = Get.find();
     httpProvider.switchBaseUrl(baseUrl);
-
   }
 }

BIN
packages/cs_resources/assets/cpt_auth/korea_icon.webp


+ 1 - 0
packages/cs_resources/lib/generated/assets.dart

@@ -23,6 +23,7 @@ class Assets {
   static const String baseServiceTriangleDropDown = 'assets/base_service/triangle_drop_down.webp';
   static const String baseServiceTriangleDropDownIcon = 'assets/base_service/triangle_drop_down_icon.webp';
   static const String cptAuthCheckedIcon = 'assets/cpt_auth/checked_icon.webp';
+  static const String cptAuthKoreaIcon = 'assets/cpt_auth/korea_icon.webp';
   static const String cptAuthLoginRadioChecked = 'assets/cpt_auth/login_radio_checked.webp';
   static const String cptAuthLoginRadioUncheck = 'assets/cpt_auth/login_radio_uncheck.webp';
   static const String cptAuthNextIcon = 'assets/cpt_auth/next_icon.webp';

+ 6 - 3
packages/cs_resources/lib/local/language/en_US.dart

@@ -50,7 +50,7 @@ const Map<String, String> en_US = {
   'Notice': 'Notice',
   'Are you sure you need to exit the system?': 'Are you sure you need to exit the system?',
   'Are you sure you want to deactivate your account? You will not be able to login into the app once you proceed with the request.':
-  'Are you sure you want to deactivate your account? You will not be able to login into the app once you proceed with the request.',
+      'Are you sure you want to deactivate your account? You will not be able to login into the app once you proceed with the request.',
   'Welcome': 'Welcome',
   'Switch Projects': 'Switch Projects',
   'Account Deactivation': 'Account Deactivation',
@@ -234,7 +234,10 @@ const Map<String, String> en_US = {
   'TotalAmt': 'TotalAmt',
   'Hours': 'Hours',
   'Monthly Staff Request Report': 'Monthly Staff Request Report',
-  'Finance Report':'Finance Report',
+  'Finance Report': 'Finance Report',
+  'Created By': 'Created By',
+  'Language': 'Language',
+  'Korea': 'Korea',
 
   //插件的国际化
   'Pull to refresh': 'Pull to refresh',
@@ -252,4 +255,4 @@ const Map<String, String> en_US = {
   'Click again and exit the app': 'Click again and exit the app',
   'The login credential have expired, please log in again.': 'The login credentials have expired, please log in again.',
   'Successful': 'Successful',
-};
+};

+ 258 - 0
packages/cs_resources/lib/local/language/ko_KR.dart

@@ -0,0 +1,258 @@
+const Map<String, String> ko_KR = {
+  'YY Employer': 'YY Employer',
+  'Please enter your password': 'Please enter your password',
+  'The login code cannot be empty!': 'The login code cannot be empty!',
+  'The password cannot be empty!': 'The password cannot be empty!',
+  'The new password cannot be empty!': 'The new password cannot be empty!',
+  'Log in': 'Log in',
+  'Password': 'Password',
+  'Email': 'Email',
+  'Name/Mobile': 'Name/Mobile',
+  'Check In': 'Check In',
+  'Check Out': 'Check Out',
+  'Job Date': 'Job Date',
+  'Start Time:': 'Start Time:',
+  'End Time:': 'End Time:',
+  'Start Time': 'Start Time',
+  'End Time': 'End Time',
+  'Name:': 'Name:',
+  'Reset': 'Reset',
+  'Start Date': 'Start Date',
+  'End Date': 'End Date',
+  'Cancel': 'Cancel',
+  'Confirm': 'Confirm',
+  'Sign Here': 'Sign Here',
+  'Clean': 'Clean',
+  'Vietnam': 'Vietnam',
+  'Sign in/Sign out': 'Sign in/Sign out',
+  'Admin': 'Admin',
+  'Don’t have an account?': 'Don’t have an account?',
+  'Sign up': 'Sign up',
+  'Confirm Password': 'Confirm Password',
+  'Please enter your email': 'Please enter your email',
+  'Please enter confirm password': 'Please enter confirm password',
+  'Submit': 'Submit',
+  'The email cannot be empty!': 'The email cannot be empty!',
+  'The confirm password cannot be empty!': 'The confirm password cannot be empty!',
+  'Please confirm your confirmed password!': 'Please confirm your confirmed password!',
+  'Reset Password': 'Reset Password',
+  'New Password': 'New Password',
+  'Please enter your new password': 'Please enter your new password',
+  'Labour Request': 'Labour Request',
+  'Job List': 'Job List',
+  'Sign in Sign out': 'Sign in Sign out',
+  'Devices': 'Devices',
+  'Labour Request Review': 'Labour Request Review',
+  'Attendance Review': 'Attendance Review',
+  'Default Job Title': 'Default Job Title',
+  'Report': 'Report',
+  'Confirmation': 'Confirmation',
+  'Notice': 'Notice',
+  'Are you sure you need to exit the system?': 'Are you sure you need to exit the system?',
+  'Are you sure you want to deactivate your account? You will not be able to login into the app once you proceed with the request.':
+      'Are you sure you want to deactivate your account? You will not be able to login into the app once you proceed with the request.',
+  'Welcome': 'Welcome',
+  'Switch Projects': 'Switch Projects',
+  'Account Deactivation': 'Account Deactivation',
+  'Logout': 'Logout',
+  'Old Password': 'Old Password',
+  'Settings': 'Settings',
+  'Title': 'Title',
+  'Create New Job Request': 'Create New Job Request',
+  'Outlet': 'Outlet',
+  'Status': 'Status',
+  'Filter': 'Filter',
+  'Edit': 'Edit',
+  'Recall': 'Recall',
+  'Detail': 'Detail',
+  'Outlet:': 'Outlet:',
+  'DateTime:': 'DateTime:',
+  'No. of Staff:': 'No. of Staff:',
+  'Status:': 'Status:',
+  'Publish Status:': 'Publish Status:',
+  'Created At:': 'Created At:',
+  'Published': 'Published',
+  'Unpublished': 'Unpublished',
+  'Choose Outlet': 'Choose Outlet',
+  'Choose Status': 'Choose Status',
+  'Choose Job Title': 'Choose Job Title',
+  'Job Title': 'Job Title',
+  'Job Start Time': 'Job Start Time',
+  'Job End Time': 'Job End Time',
+  'No. of Staff': 'No. of Staff',
+  'Enter No. of Staff': 'Enter No. of Staff',
+  'Choose Start Date': 'Choose Start Date',
+  'Choose End Date': 'Choose End Date',
+  'Add Labour Requisition': 'Add Labour Requisition',
+  'Edit Labour Requisition': 'Edit Labour Requisition',
+  'Labour Requisition': 'Labour Requisition',
+  'Message': 'Message',
+  'Are you sure you want to recall?': 'Are you sure you want to recall?',
+  'Workflow': 'Workflow',
+  'Remark:': 'Remark:',
+  'Audit Time:': 'Audit Time:',
+  'Operator:': 'Operator:',
+  'Designation:': 'Designation:',
+  'Type:': 'Type:',
+  'Node:': 'Node:',
+  'Approved': 'Approved',
+  'Pending': 'Pending',
+  'Rejected': 'Rejected',
+  'Completed': 'Completed',
+  'Active': 'Active',
+  'Cancelled': 'Cancelled',
+  'Revised': 'Revised',
+  'Job Detail': 'Job Detail',
+  'Add Staff': 'Add Staff',
+  'Remarks': 'Remarks',
+  'Applied At:': 'Applied At:',
+  'Total Rooms:': 'Total Rooms:',
+  'Total Hours:': 'Total Hours:',
+  '+/- Hours:': '+/- Hours:',
+  'Security Out:': 'Security Out:',
+  'Work Out:': 'Work Out:',
+  'Work In:': 'Work In:',
+  'Security In:': 'Security In:',
+  'Staff Name:': 'Staff Name:',
+  'Staff Name': 'Staff Name',
+  'Work Out': 'Work Out',
+  'Work In': 'Work In',
+  'Security In': 'Security In',
+  'Security Out': 'Security Out',
+  '+/- Hours': '+/- Hours',
+  'Total Rooms': 'Total Rooms',
+  'Operation Approve': 'Operation Approve',
+  'Batch Modify': 'Batch Modify',
+  'Please select the applied record': 'Please select the applied record',
+  'Are you sure you want to setting approved?': 'Are you sure you want to setting approved?',
+  'Attitude': 'Attitude',
+  'Performance': 'Performance',
+  'Experience': 'Experience',
+  'Grooming': 'Grooming',
+  'Enter...': 'Enter...',
+  'Please Enter Remark': 'Please Enter Remark',
+  'Choose Staff': 'Choose Staff',
+  'Reason': 'Reason',
+  'None': 'None',
+  'Forgot to clock in/out': 'Forgot to clock in/out',
+  'Technical issue': 'Technical issue',
+  'Others': 'Others',
+  'Staff Detail': 'Staff Detail',
+  'Staff Name/ID/Phone': 'Staff Name/ID/Phone',
+  'Select Country': 'Select Country',
+  'Browse jobs available in your selected country.': 'Browse jobs available in your selected country.',
+  'Next': 'Next',
+  'Singapore': 'Singapore',
+  'Revise List': 'Revise List',
+  'Device List': 'Device List',
+  'Device MAC:': 'Device MAC:',
+  'Device Alias:': 'Device Alias:',
+  'Location:': 'Location:',
+  'Alive State:': 'Alive State:',
+  'Offline': 'Offline',
+  'Online': 'Online',
+  'Sort': 'Sort',
+  'Template': 'Template',
+  'Updated At': 'Updated At',
+  'Add New': 'Add New',
+  'Template Setting': 'Template Setting',
+  'Enter Job Title': 'Enter Job Title',
+  'Template Name': 'Template Name',
+  'Contact': 'Contact',
+  'Note': 'Note',
+  'Delete': 'Delete',
+  'Are you sure you want to delete this job title?': 'Are you sure you want to delete this job title?',
+  'Are you sure you want to delete this job template?': 'Are you sure you want to delete this job template?',
+  'Age': 'Age',
+  'Gender': 'Gender',
+  'Preferred Language': 'Preferred Language',
+  'Food Hygiene Cert': 'Food Hygiene Cert',
+  'Yes': 'Yes',
+  'No': 'No',
+  'Description': 'Description',
+  'Contact No': 'Contact No',
+  'Create Template': 'Create Template',
+  'Edit Template': 'Edit Template',
+  'Both': 'Both',
+  'Male': 'Male',
+  'Female': 'Female',
+  'English': 'English',
+  'Chinese': 'Chinese',
+  'Malay': 'Malay',
+  'Tamil': 'Tamil',
+  'Hindi': 'Hindi',
+  'Select Job Title': 'Select Job Title',
+  'Select Job Start Time': 'Select Job Start Time',
+  'Select Job End Time': 'Select Job End Time',
+  'Enter No. of Staff of The Corresponding Gender': 'Enter No. of Staff of The Corresponding Gender',
+  'Select Date': 'Select Date',
+  'Date': 'Date',
+  'Job Time': 'Job Time',
+  'Repeat': 'Repeat',
+  'Repeat Start Time': 'Repeat Start Time',
+  'Repeat End Time': 'Repeat End Time',
+  'Gender Unlimited': 'Gender Unlimited',
+  'Gender Limited': 'Gender Limited',
+  'Needs Num': 'Needs Num',
+  'Request Type': 'Request Type',
+  'Remark': 'Remark',
+  'Are you sure you want to cancel this job?': 'Are you sure you want to cancel this job?',
+  'Are you sure you want to delete this job?': 'Are you sure you want to delete this job?',
+  'Are you sure you want to confirm this job?': 'Are you sure you want to confirm this job?',
+  'Create New Job': 'Create New Job',
+  'Search': 'Search',
+  'Send E-Attendance': 'Send E-Attendance',
+  'Nric': 'Nric',
+  'Name / Nric': 'Name / Nric',
+  'Add Staff - Choose Staff': 'Add Staff - Choose Staff',
+  'Are you sure you want to send the e-attendance to agency?': 'Are you sure you want to send the e-attendance to agency?',
+  'Work Clock In': 'Work Clock In',
+  'Work Clock Out': 'Work Clock Out',
+  'Security Clock In': 'Security Clock In',
+  'Security Clock Out': 'Security Clock Out',
+  'Applied': 'Applied',
+  'No Show': 'No Show',
+  'Modify': 'Modify',
+  'Subtract Hours': 'Subtract Hours',
+  'Hourly Rate': 'Hourly Rate',
+  'Reviews': 'Reviews',
+  'Mobile': 'Mobile',
+  'Staff Reviews': 'Staff Reviews',
+  'Revise Hours': 'Revise Hours',
+  'Are you sure you want to recall this revise?': 'Are you sure you want to recall this revise?',
+  'Are you sure you want to delete this revise?': 'Are you sure you want to delete this revise?',
+  'Logs': 'Logs',
+  'Date Time': 'Date Time',
+  'Action': 'Action',
+  'Content': 'Content',
+  'Revise': 'Revise',
+  'Add Revise': 'Add Revise',
+  'Edit Revise': 'Edit Revise',
+  'Incomplete': 'Incomplete',
+  'Completed + Incomplete': 'Completed + Incomplete',
+  'YY Casual Labour Report': 'YY Casual Labour Report',
+  'TotalAmt': 'TotalAmt',
+  'Hours': 'Hours',
+  'Monthly Staff Request Report': 'Monthly Staff Request Report',
+  'Finance Report': 'Finance Report',
+  'Created By': 'Created By',
+  'Language': 'Language',
+  'Korea': 'Korea',
+
+  //插件的国际化
+  'Pull to refresh': 'Pull to refresh',
+  'Release ready': 'Release ready',
+  'Refreshing...': 'Refreshing...',
+  'Succeeded': 'Succeeded',
+  'No more': 'No more',
+  'Failed': 'Failed',
+  'Last updated at %T': 'Last updated at %T',
+  'Pull to load': 'Pull to load',
+  'Network Load Error': 'Network Load Error',
+  'Loading...': 'Loading...',
+  'Data loading failed! Please refresh and try again': 'Data loading failed! Please refresh and try again',
+  'There is currently no content available': 'There is currently no content available',
+  'Click again and exit the app': 'Click again and exit the app',
+  'The login credential have expired, please log in again.': 'The login credentials have expired, please log in again.',
+  'Successful': 'Successful',
+};

+ 2 - 0
packages/cs_resources/lib/local/language/translation_service.dart

@@ -3,6 +3,7 @@ import 'package:get/get.dart';
 import 'en_US.dart';
 import 'vi_VN.dart';
 import 'zh_CN.dart';
+import 'ko_KR.dart';
 
 class TranslationService extends Translations {
   static Locale? get locale => Get.deviceLocale;
@@ -13,5 +14,6 @@ class TranslationService extends Translations {
         'en_US': en_US,
         'zh_CN': zh_CN,
         'vi_VN': vi_VN,
+        'ko_KR': ko_KR,
       };
 }

+ 3 - 0
packages/cs_resources/lib/local/language/vi_VN.dart

@@ -235,6 +235,9 @@ const Map<String, String> vi_VN = {
   'Hours': 'Giờ',
   'Monthly Staff Request Report': 'Báo cáo nhu cầu nhân viên hàng tháng',
   'Finance Report':'Báo cáo tài chính',
+  'Created By': 'Trang chủ',
+  'Language': 'Ngôn ngữ',
+  'Korea': 'Hàn Quốc',
 
   //插件的国际化
   "Pull to refresh": "Kéo để làm mới",

+ 3 - 0
packages/cs_resources/lib/local/language/zh_CN.dart

@@ -235,6 +235,9 @@ const Map<String, String> zh_CN = {
   'Hours': '小时',
   'Monthly Staff Request Report': '月度员工请求报表',
   'Finance Report': '财务报表',
+  'Created By': '创建者',
+  'Language': '语言',
+  'Korea': '韩国',
 
   //插件的国际化
   'Pull to refresh': '下拉刷新',

+ 2 - 1
packages/cs_router/lib/componentRouter/job_service.dart

@@ -1,5 +1,5 @@
 
-/**
+/*
  * Job组件对应的路由抽象接口
  */
 abstract class JobService {
@@ -8,4 +8,5 @@ abstract class JobService {
 
   void startJobListPage();
 
+  void startAttendanceReviewPage();
 }

+ 4 - 1
packages/cs_router/lib/componentRouter/labour_service.dart

@@ -1,9 +1,12 @@
 
-/**
+/*
  * Labour组件对应的路由抽象接口
  */
 abstract class LabourService {
 
   void startLabourRequestPage();
 
+  void startLabourTemplatePage();
+
+  void startLabourReviewPage();
 }

+ 7 - 0
packages/cs_router/lib/path/router_path.dart

@@ -25,18 +25,25 @@ class RouterPath {
   static const jobAppliedStaffWorkflow = '/job/applied/workflow'; //指定工作中已申请的指定员工的信息与状态修改工作流
   static const jobAppliedStaffDetail = '/job/applied/staff/detail'; //工作中员工的详细信息
   static const jobAppliedStaffReviews = '/job/applied/staff/reviews'; //工作中员工的被评价列表
+  static const jobAttendanceReviewList = '/job/attendance/review/list'; //工作考勤的审核列表
 
   //用工请求
   static const jobLabourRequestList = '/labour/list'; //用工请求列表
   static const jobLabourRequestAdd = '/labour/add'; //用工请求添加
   static const jobLabourRequestDetail = '/labour/detail'; //用工请求详情
   static const jobLabourRequestWorkflow = '/labour/workflow'; //用工请求修改状态的工作流
+  static const labourTemplateList = '/labour/template/list'; //模板列表
+  static const labourTemplateAdd = '/labour/template/add'; //模板添加
+  static const labourReviewList = '/labour/review/list'; //用工请求审核列表
+  static const labourReviewEdit = '/labour/review/edit'; //用工请求审核编辑
+  static const labourReviewWorkflow = '/labour/review/workflow'; //用工请求审核工作流
 
   //其他模块和报表模块
   static const deviceList = '/device/list'; //雇主绑定的设备列表
   static const reportList = '/report/list'; //报表的选项
   static const reportFinance = '/report/finance'; //金额报表
   static const reportCasualLabour = '/report/labour'; //用工请求报表
+  static const reportLabourVN = '/report/labour/vn'; //用工请求报表(越南的单独页面)
   static const reportStaffRequest = '/report/staff/request'; //员工申请报表
 
   //新加坡的用工请求

+ 17 - 1
packages/cs_widgets/lib/shatter/custom_check_box.dart

@@ -41,11 +41,27 @@ class _CustomCheckBoxState extends State<CustomCheckBox> {
   @override
   void didUpdateWidget(CustomCheckBox oldWidget) {
     super.didUpdateWidget(oldWidget);
-    if (oldWidget.selectedOptions != widget.selectedOptions) {
+    Log.d("oldWidget - selectedOptions :${oldWidget.selectedOptions}  newWidget - selectedOptions:${widget.selectedOptions} ");
+
+    // 使用ListEquality来比较两个列表的内容
+    if (!_listEquals(oldWidget.selectedOptions, widget.selectedOptions)) {
       _initializeSelectedOptions();
     }
   }
 
+  bool _listEquals(List<String> list1, List<String> list2) {
+    if (list1.length != list2.length) {
+      return false;
+    }
+    for (int i = 0; i < list1.length; i++) {
+      if (list1[i] != list2[i]) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+
   @override
   Widget build(BuildContext context) {
     return Wrap(

+ 1 - 1
packages/cs_widgets/lib/shatter/custom_radio_check.dart

@@ -7,7 +7,7 @@ import 'package:widgets/ext/ex_widget.dart';
 import 'package:widgets/my_load_image.dart';
 import 'package:widgets/my_text_view.dart';
 
-/**
+/*
  * 条件单选 Radio
  */
 class CustomRadioCheck extends StatefulWidget {