liukai 1 месяц назад
Родитель
Сommit
fe64760ff0
32 измененных файлов с 3638 добавлено и 13 удалено
  1. 0 1
      app/android/app/build.gradle
  2. 237 0
      packages/cpt_th/lib/modules/job_er/job_list_er/job_list_er_controller.dart
  3. 510 0
      packages/cpt_th/lib/modules/job_er/job_list_er/job_list_er_filter.dart
  4. 250 0
      packages/cpt_th/lib/modules/job_er/job_list_er/job_list_er_item.dart
  5. 155 0
      packages/cpt_th/lib/modules/job_er/job_list_er/job_list_er_page.dart
  6. 22 0
      packages/cpt_th/lib/modules/job_er/job_list_er/job_list_er_state.dart
  7. 122 0
      packages/cpt_th/lib/modules/labour_er/labour_request_er/labour_request_item.dart
  8. 155 0
      packages/cpt_th/lib/modules/labour_er/labour_request_er/labour_request_list_controller.dart
  9. 158 0
      packages/cpt_th/lib/modules/labour_er/labour_request_er/labour_request_list_page.dart
  10. 10 0
      packages/cpt_th/lib/modules/labour_er/labour_request_er/labour_request_list_state.dart
  11. 302 0
      packages/cpt_th/lib/modules/labour_er/labour_request_er_add/labour_request_er_add_controller.dart
  12. 590 0
      packages/cpt_th/lib/modules/labour_er/labour_request_er_add/labour_request_er_add_page.dart
  13. 73 0
      packages/cpt_th/lib/modules/labour_er/labour_request_er_add/labour_request_er_add_state.dart
  14. 4 2
      packages/cpt_th/lib/modules/main/main_controller.dart
  15. 22 0
      packages/cpt_th/lib/router/th_router.dart
  16. 18 1
      packages/cs_domain/lib/constants/api_constants.dart
  17. 51 0
      packages/cs_domain/lib/entity/response/job_list_e_r_option_entity.dart
  18. 6 6
      packages/cs_domain/lib/entity/response/job_list_entity.dart
  19. 59 0
      packages/cs_domain/lib/entity/response/labour_list_e_r_entity.dart
  20. 80 0
      packages/cs_domain/lib/entity/response/labour_request_e_r_entity.dart
  21. 42 0
      packages/cs_domain/lib/entity/response/labour_request_e_r_option_entity.dart
  22. 40 0
      packages/cs_domain/lib/generated/json/base/json_convert_content.dart
  23. 104 0
      packages/cs_domain/lib/generated/json/job_list_e_r_option_entity.g.dart
  24. 136 0
      packages/cs_domain/lib/generated/json/labour_list_e_r_entity.g.dart
  25. 150 0
      packages/cs_domain/lib/generated/json/labour_request_e_r_entity.g.dart
  26. 102 0
      packages/cs_domain/lib/generated/json/labour_request_e_r_option_entity.g.dart
  27. 3 3
      packages/cs_domain/lib/repository/ms_repository.dart
  28. 230 0
      packages/cs_domain/lib/repository/th_er_repository.dart
  29. 2 0
      packages/cs_initializer/lib/global_services_injection.dart
  30. 1 0
      packages/cs_resources/lib/local/language/en_US.dart
  31. 1 0
      packages/cs_resources/lib/local/language/zh_CN.dart
  32. 3 0
      packages/cs_router/lib/path/router_path.dart

+ 0 - 1
app/android/app/build.gradle

@@ -43,7 +43,6 @@ android {
     }
 
     lintOptions {
-        // 如打包出现Failed to transform libs.jar to match attributes
         checkReleaseBuilds false
     }
 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

+ 4 - 2
packages/cpt_th/lib/modules/main/main_controller.dart

@@ -1,3 +1,4 @@
+import 'package:cpt_th/modules/job_er/job_list_er/job_list_er_page.dart';
 import 'package:domain/entity/home_module.dart';
 import 'package:domain/entity/response/hotel_info_entity.dart';
 import 'package:domain/repository/auth_repository.dart';
@@ -15,6 +16,7 @@ import '../job/job_list/job_list_page.dart';
 import '../labour/labour_request_list/labour_request_list_page.dart';
 import '../labour/labour_review_list/labour_review_page.dart';
 import '../labour/labour_template_list/labour_template_list_page.dart';
+import '../labour_er/labour_request_er/labour_request_list_page.dart';
 import 'main_state.dart';
 
 class MainController extends GetxController {
@@ -122,10 +124,10 @@ class MainController extends GetxController {
   void gotoModulePage(HomeModule module) {
     switch (module.key) {
       case 'er/lab-req':
-        ToastEngine.show("ER 的 Labour Request");
+        LabourRequestERListPage.startInstance();
         break;
       case 'er/jobs':
-        ToastEngine.show("ER 的 Job List");
+        JobListERPage.startInstance(null, null);
         break;
       case 'oa/lab-req':
         LabourRequestListPage.startInstance();

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

@@ -8,6 +8,9 @@ import '../modules/job/attendance_review_list/attendance_review_page.dart';
 import '../modules/job/job_applied/job_applied_page.dart';
 import '../modules/job/job_applied_edit/job_applied_edit_page.dart';
 import '../modules/job/job_list/job_list_page.dart';
+import '../modules/job_er/job_list_er/job_list_er_page.dart';
+import '../modules/labour_er/labour_request_er/labour_request_list_page.dart';
+import '../modules/labour_er/labour_request_er_add/labour_request_er_add_page.dart';
 import '../modules/main/main_page.dart';
 import '../modules/job/job_list_detail/job_list_detail_page.dart';
 import '../modules/job/sign_in_sign_out/sign_in_sign_out_page.dart';
@@ -133,5 +136,24 @@ class THPageRouter {
       page: () => AttendanceReviewPage(),
     ),
 
+    //用工请求列表 ER
+    GetPage(
+      name: RouterPath.THLabourRequestER,
+      page: () => LabourRequestERListPage(),
+    ),
+
+    //用工请求列表添加 ER
+    GetPage(
+      name: RouterPath.THLabourRequestERAdd,
+      page: () => LabourRequestERAddPage(),
+    ),
+
+    //用工请求列表添加 ER
+    GetPage(
+      name: RouterPath.THJobListER,
+      page: () => JobListERPage(),
+    ),
+
+
   ];
 }

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

@@ -215,7 +215,6 @@ class ApiConstants {
   //用工请求的考勤确认(Job List)现在是发送到 Attendance Review 列表中
   static const apiJobAppliedApproveSG = "/index.php/api/v2/hotel/applied/batch-approve";
 
-
   // =========================== 新加坡工作相关 ↓=========================================
 
   //新加坡工作已申请的选项
@@ -527,4 +526,22 @@ class ApiConstants {
   //切换账号
   static const apiSwitchAccountTH = "/index.php/api/er/auth/switch";
 
+  // ER 的用工请求列表
+  static const apiERLabourRequestTH = "/index.php/api/er/lab-req/index";
+
+  // ER 的用工请求添加选项
+  static const apiERLabourRequestAddOptionTH = "/index.php/api/er/lab-req/view-add";
+
+  // ER 的用工请求新增提交
+  static const apiERLabourRequestAddSubmitTH = "/index.php/api/er/lab-req/add-submit";
+
+  //ER 工作列表选项
+  static const apiERJobListOptionTH = "/index.php/api/er/job/index";
+
+  //ER 工作列表
+  static const apiERJobListTableTH = "/index.php/api/er/job/table";
+
+  //ER 工作列表删除
+  static const apiERJobListDeleteTH = "/index.php/api/er/job/action";
+
 }

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

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

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

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

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

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

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

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

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

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

+ 40 - 0
packages/cs_domain/lib/generated/json/base/json_convert_content.dart

@@ -37,6 +37,7 @@ import 'package:domain/entity/response/job_list_applied_staff_list_entity.dart';
 import 'package:domain/entity/response/job_list_applied_staff_search_entity.dart';
 import 'package:domain/entity/response/job_list_applied_work_flow_entity.dart';
 import 'package:domain/entity/response/job_list_detail_entity.dart';
+import 'package:domain/entity/response/job_list_e_r_option_entity.dart';
 import 'package:domain/entity/response/job_list_edit_index_s_g_entity.dart';
 import 'package:domain/entity/response/job_list_entity.dart';
 import 'package:domain/entity/response/job_list_index_entity.dart';
@@ -47,8 +48,11 @@ import 'package:domain/entity/response/job_template_edit_index_entity.dart';
 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_list_e_r_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_e_r_entity.dart';
+import 'package:domain/entity/response/labour_request_e_r_option_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';
@@ -404,6 +408,12 @@ class JsonConvert {
     if (<JobListDetailDepartmentList>[] is M) {
       return data.map<JobListDetailDepartmentList>((Map<String, dynamic> e) => JobListDetailDepartmentList.fromJson(e)).toList() as M;
     }
+    if (<JobListEROptionEntity>[] is M) {
+      return data.map<JobListEROptionEntity>((Map<String, dynamic> e) => JobListEROptionEntity.fromJson(e)).toList() as M;
+    }
+    if (<JobListEROptionFilter>[] is M) {
+      return data.map<JobListEROptionFilter>((Map<String, dynamic> e) => JobListEROptionFilter.fromJson(e)).toList() as M;
+    }
     if (<JobListEditIndexSGEntity>[] is M) {
       return data.map<JobListEditIndexSGEntity>((Map<String, dynamic> e) => JobListEditIndexSGEntity.fromJson(e)).toList() as M;
     }
@@ -470,6 +480,12 @@ class JsonConvert {
     if (<JobTitleSGRows>[] is M) {
       return data.map<JobTitleSGRows>((Map<String, dynamic> e) => JobTitleSGRows.fromJson(e)).toList() as M;
     }
+    if (<LabourListEREntity>[] is M) {
+      return data.map<LabourListEREntity>((Map<String, dynamic> e) => LabourListEREntity.fromJson(e)).toList() as M;
+    }
+    if (<LabourListERRows>[] is M) {
+      return data.map<LabourListERRows>((Map<String, dynamic> e) => LabourListERRows.fromJson(e)).toList() as M;
+    }
     if (<LabourReportEntity>[] is M) {
       return data.map<LabourReportEntity>((Map<String, dynamic> e) => LabourReportEntity.fromJson(e)).toList() as M;
     }
@@ -488,6 +504,21 @@ class JsonConvert {
     if (<LabourReportVNTotal>[] is M) {
       return data.map<LabourReportVNTotal>((Map<String, dynamic> e) => LabourReportVNTotal.fromJson(e)).toList() as M;
     }
+    if (<LabourRequestEREntity>[] is M) {
+      return data.map<LabourRequestEREntity>((Map<String, dynamic> e) => LabourRequestEREntity.fromJson(e)).toList() as M;
+    }
+    if (<LabourRequestERCountList>[] is M) {
+      return data.map<LabourRequestERCountList>((Map<String, dynamic> e) => LabourRequestERCountList.fromJson(e)).toList() as M;
+    }
+    if (<LabourRequestERSourceList>[] is M) {
+      return data.map<LabourRequestERSourceList>((Map<String, dynamic> e) => LabourRequestERSourceList.fromJson(e)).toList() as M;
+    }
+    if (<LabourRequestERSourceListRequestList>[] is M) {
+      return data.map<LabourRequestERSourceListRequestList>((Map<String, dynamic> e) => LabourRequestERSourceListRequestList.fromJson(e)).toList() as M;
+    }
+    if (<LabourRequestEROptionEntity>[] is M) {
+      return data.map<LabourRequestEROptionEntity>((Map<String, dynamic> e) => LabourRequestEROptionEntity.fromJson(e)).toList() as M;
+    }
     if (<LabourRequestEditIndexEntity>[] is M) {
       return data.map<LabourRequestEditIndexEntity>((Map<String, dynamic> e) => LabourRequestEditIndexEntity.fromJson(e)).toList() as M;
     }
@@ -833,6 +864,8 @@ class JsonConvertClassCollection {
     (JobListAppliedWorkFlowRecords).toString(): JobListAppliedWorkFlowRecords.fromJson,
     (JobListDetailEntity).toString(): JobListDetailEntity.fromJson,
     (JobListDetailDepartmentList).toString(): JobListDetailDepartmentList.fromJson,
+    (JobListEROptionEntity).toString(): JobListEROptionEntity.fromJson,
+    (JobListEROptionFilter).toString(): JobListEROptionFilter.fromJson,
     (JobListEditIndexSGEntity).toString(): JobListEditIndexSGEntity.fromJson,
     (JobListEditIndexOption).toString(): JobListEditIndexOption.fromJson,
     (JobListEntity).toString(): JobListEntity.fromJson,
@@ -855,12 +888,19 @@ class JsonConvertClassCollection {
     (JobTitleEditIndexTemplate).toString(): JobTitleEditIndexTemplate.fromJson,
     (JobTitleSGEntity).toString(): JobTitleSGEntity.fromJson,
     (JobTitleSGRows).toString(): JobTitleSGRows.fromJson,
+    (LabourListEREntity).toString(): LabourListEREntity.fromJson,
+    (LabourListERRows).toString(): LabourListERRows.fromJson,
     (LabourReportEntity).toString(): LabourReportEntity.fromJson,
     (LabourReportRows).toString(): LabourReportRows.fromJson,
     (LabourReportTotal).toString(): LabourReportTotal.fromJson,
     (LabourReportVNEntity).toString(): LabourReportVNEntity.fromJson,
     (LabourReportVNRows).toString(): LabourReportVNRows.fromJson,
     (LabourReportVNTotal).toString(): LabourReportVNTotal.fromJson,
+    (LabourRequestEREntity).toString(): LabourRequestEREntity.fromJson,
+    (LabourRequestERCountList).toString(): LabourRequestERCountList.fromJson,
+    (LabourRequestERSourceList).toString(): LabourRequestERSourceList.fromJson,
+    (LabourRequestERSourceListRequestList).toString(): LabourRequestERSourceListRequestList.fromJson,
+    (LabourRequestEROptionEntity).toString(): LabourRequestEROptionEntity.fromJson,
     (LabourRequestEditIndexEntity).toString(): LabourRequestEditIndexEntity.fromJson,
     (LabourRequestEditIndexTemplateList).toString(): LabourRequestEditIndexTemplateList.fromJson,
     (LabourRequestEditIndexDepartmentList).toString(): LabourRequestEditIndexDepartmentList.fromJson,

+ 104 - 0
packages/cs_domain/lib/generated/json/job_list_e_r_option_entity.g.dart

@@ -0,0 +1,104 @@
+import 'package:domain/generated/json/base/json_convert_content.dart';
+import 'package:domain/entity/response/job_list_e_r_option_entity.dart';
+import 'package:domain/entity/response/index_option_entity.dart';
+
+
+JobListEROptionEntity $JobListEROptionEntityFromJson(Map<String, dynamic> json) {
+  final JobListEROptionEntity jobListEROptionEntity = JobListEROptionEntity();
+  final List<IndexOptionEntity>? divisionList = (json['division_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<IndexOptionEntity>(e) as IndexOptionEntity).toList();
+  if (divisionList != null) {
+    jobListEROptionEntity.divisionList = divisionList;
+  }
+  final List<IndexOptionEntity>? outletList = (json['outlet_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<IndexOptionEntity>(e) as IndexOptionEntity).toList();
+  if (outletList != null) {
+    jobListEROptionEntity.outletList = outletList;
+  }
+  final bool? withOt = jsonConvert.convert<bool>(json['with_ot']);
+  if (withOt != null) {
+    jobListEROptionEntity.withOt = withOt;
+  }
+  final List<IndexOptionEntity>? statusList = (json['status_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<IndexOptionEntity>(e) as IndexOptionEntity).toList();
+  if (statusList != null) {
+    jobListEROptionEntity.statusList = statusList;
+  }
+  final bool? withOtJob = jsonConvert.convert<bool>(json['with_ot_job']);
+  if (withOtJob != null) {
+    jobListEROptionEntity.withOtJob = withOtJob;
+  }
+  final JobListEROptionFilter? filter = jsonConvert.convert<JobListEROptionFilter>(json['filter']);
+  if (filter != null) {
+    jobListEROptionEntity.filter = filter;
+  }
+  final String? view = jsonConvert.convert<String>(json['view']);
+  if (view != null) {
+    jobListEROptionEntity.view = view;
+  }
+  return jobListEROptionEntity;
+}
+
+Map<String, dynamic> $JobListEROptionEntityToJson(JobListEROptionEntity entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['division_list'] = entity.divisionList?.map((v) => v.toJson()).toList();
+  data['outlet_list'] = entity.outletList?.map((v) => v.toJson()).toList();
+  data['with_ot'] = entity.withOt;
+  data['status_list'] = entity.statusList?.map((v) => v.toJson()).toList();
+  data['with_ot_job'] = entity.withOtJob;
+  data['filter'] = entity.filter?.toJson();
+  data['view'] = entity.view;
+  return data;
+}
+
+extension JobListEROptionEntityExtension on JobListEROptionEntity {
+  JobListEROptionEntity copyWith({
+    List<IndexOptionEntity>? divisionList,
+    List<IndexOptionEntity>? outletList,
+    bool? withOt,
+    List<IndexOptionEntity>? statusList,
+    bool? withOtJob,
+    JobListEROptionFilter? filter,
+    String? view,
+  }) {
+    return JobListEROptionEntity()
+      ..divisionList = divisionList ?? this.divisionList
+      ..outletList = outletList ?? this.outletList
+      ..withOt = withOt ?? this.withOt
+      ..statusList = statusList ?? this.statusList
+      ..withOtJob = withOtJob ?? this.withOtJob
+      ..filter = filter ?? this.filter
+      ..view = view ?? this.view;
+  }
+}
+
+JobListEROptionFilter $JobListEROptionFilterFromJson(Map<String, dynamic> json) {
+  final JobListEROptionFilter jobListEROptionFilter = JobListEROptionFilter();
+  final String? startDate = jsonConvert.convert<String>(json['start_date']);
+  if (startDate != null) {
+    jobListEROptionFilter.startDate = startDate;
+  }
+  final String? endDate = jsonConvert.convert<String>(json['end_date']);
+  if (endDate != null) {
+    jobListEROptionFilter.endDate = endDate;
+  }
+  return jobListEROptionFilter;
+}
+
+Map<String, dynamic> $JobListEROptionFilterToJson(JobListEROptionFilter entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['start_date'] = entity.startDate;
+  data['end_date'] = entity.endDate;
+  return data;
+}
+
+extension JobListEROptionFilterExtension on JobListEROptionFilter {
+  JobListEROptionFilter copyWith({
+    String? startDate,
+    String? endDate,
+  }) {
+    return JobListEROptionFilter()
+      ..startDate = startDate ?? this.startDate
+      ..endDate = endDate ?? this.endDate;
+  }
+}

+ 136 - 0
packages/cs_domain/lib/generated/json/labour_list_e_r_entity.g.dart

@@ -0,0 +1,136 @@
+import 'package:domain/generated/json/base/json_convert_content.dart';
+import 'package:domain/entity/response/labour_list_e_r_entity.dart';
+
+LabourListEREntity $LabourListEREntityFromJson(Map<String, dynamic> json) {
+  final LabourListEREntity labourListEREntity = LabourListEREntity();
+  final int? total = jsonConvert.convert<int>(json['total']);
+  if (total != null) {
+    labourListEREntity.total = total;
+  }
+  final List<LabourListERRows>? rows = (json['rows'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<LabourListERRows>(e) as LabourListERRows).toList();
+  if (rows != null) {
+    labourListEREntity.rows = rows;
+  }
+  return labourListEREntity;
+}
+
+Map<String, dynamic> $LabourListEREntityToJson(LabourListEREntity entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['total'] = entity.total;
+  data['rows'] = entity.rows.map((v) => v.toJson()).toList();
+  return data;
+}
+
+extension LabourListEREntityExtension on LabourListEREntity {
+  LabourListEREntity copyWith({
+    int? total,
+    List<LabourListERRows>? rows,
+  }) {
+    return LabourListEREntity()
+      ..total = total ?? this.total
+      ..rows = rows ?? this.rows;
+  }
+}
+
+LabourListERRows $LabourListERRowsFromJson(Map<String, dynamic> json) {
+  final LabourListERRows labourListERRows = LabourListERRows();
+  final String? jobId = jsonConvert.convert<String>(json['job_id']);
+  if (jobId != null) {
+    labourListERRows.jobId = jobId;
+  }
+  final String? jobDate = jsonConvert.convert<String>(json['job_date']);
+  if (jobDate != null) {
+    labourListERRows.jobDate = jobDate;
+  }
+  final String? outletName = jsonConvert.convert<String>(json['outlet_name']);
+  if (outletName != null) {
+    labourListERRows.outletName = outletName;
+  }
+  final String? jobTitle = jsonConvert.convert<String>(json['job_title']);
+  if (jobTitle != null) {
+    labourListERRows.jobTitle = jobTitle;
+  }
+  final String? startTime = jsonConvert.convert<String>(json['start_time']);
+  if (startTime != null) {
+    labourListERRows.startTime = startTime;
+  }
+  final String? endTime = jsonConvert.convert<String>(json['end_time']);
+  if (endTime != null) {
+    labourListERRows.endTime = endTime;
+  }
+  final String? hiringNum = jsonConvert.convert<String>(json['hiring_num']);
+  if (hiringNum != null) {
+    labourListERRows.hiringNum = hiringNum;
+  }
+  final String? preNum = jsonConvert.convert<String>(json['pre_num']);
+  if (preNum != null) {
+    labourListERRows.preNum = preNum;
+  }
+  final int? status = jsonConvert.convert<int>(json['status']);
+  if (status != null) {
+    labourListERRows.status = status;
+  }
+  final String? statusShow = jsonConvert.convert<String>(json['status_show']);
+  if (statusShow != null) {
+    labourListERRows.statusShow = statusShow;
+  }
+  final int? isOt = jsonConvert.convert<int>(json['is_ot']);
+  if (isOt != null) {
+    labourListERRows.isOt = isOt;
+  }
+  final List<String>? actionList = (json['action_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<String>(e) as String).toList();
+  if (actionList != null) {
+    labourListERRows.actionList = actionList;
+  }
+  return labourListERRows;
+}
+
+Map<String, dynamic> $LabourListERRowsToJson(LabourListERRows entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['job_id'] = entity.jobId;
+  data['job_date'] = entity.jobDate;
+  data['outlet_name'] = entity.outletName;
+  data['job_title'] = entity.jobTitle;
+  data['start_time'] = entity.startTime;
+  data['end_time'] = entity.endTime;
+  data['hiring_num'] = entity.hiringNum;
+  data['pre_num'] = entity.preNum;
+  data['status'] = entity.status;
+  data['status_show'] = entity.statusShow;
+  data['is_ot'] = entity.isOt;
+  data['action_list'] = entity.actionList;
+  return data;
+}
+
+extension LabourListERRowsExtension on LabourListERRows {
+  LabourListERRows copyWith({
+    String? jobId,
+    String? jobDate,
+    String? outletName,
+    String? jobTitle,
+    String? startTime,
+    String? endTime,
+    String? hiringNum,
+    String? preNum,
+    int? status,
+    String? statusShow,
+    int? isOt,
+    List<String>? actionList,
+  }) {
+    return LabourListERRows()
+      ..jobId = jobId ?? this.jobId
+      ..jobDate = jobDate ?? this.jobDate
+      ..outletName = outletName ?? this.outletName
+      ..jobTitle = jobTitle ?? this.jobTitle
+      ..startTime = startTime ?? this.startTime
+      ..endTime = endTime ?? this.endTime
+      ..hiringNum = hiringNum ?? this.hiringNum
+      ..preNum = preNum ?? this.preNum
+      ..status = status ?? this.status
+      ..statusShow = statusShow ?? this.statusShow
+      ..isOt = isOt ?? this.isOt
+      ..actionList = actionList ?? this.actionList;
+  }
+}

+ 150 - 0
packages/cs_domain/lib/generated/json/labour_request_e_r_entity.g.dart

@@ -0,0 +1,150 @@
+import 'package:domain/generated/json/base/json_convert_content.dart';
+import 'package:domain/entity/response/labour_request_e_r_entity.dart';
+
+LabourRequestEREntity $LabourRequestEREntityFromJson(Map<String, dynamic> json) {
+  final LabourRequestEREntity labourRequestEREntity = LabourRequestEREntity();
+  final List<LabourRequestERCountList>? countList = (json['count_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<LabourRequestERCountList>(e) as LabourRequestERCountList).toList();
+  if (countList != null) {
+    labourRequestEREntity.countList = countList;
+  }
+  final List<LabourRequestERSourceList>? sourceList = (json['source_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<LabourRequestERSourceList>(e) as LabourRequestERSourceList).toList();
+  if (sourceList != null) {
+    labourRequestEREntity.sourceList = sourceList;
+  }
+  final String? view = jsonConvert.convert<String>(json['view']);
+  if (view != null) {
+    labourRequestEREntity.view = view;
+  }
+  return labourRequestEREntity;
+}
+
+Map<String, dynamic> $LabourRequestEREntityToJson(LabourRequestEREntity entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['count_list'] = entity.countList.map((v) => v.toJson()).toList();
+  data['source_list'] = entity.sourceList.map((v) => v.toJson()).toList();
+  data['view'] = entity.view;
+  return data;
+}
+
+extension LabourRequestEREntityExtension on LabourRequestEREntity {
+  LabourRequestEREntity copyWith({
+    List<LabourRequestERCountList>? countList,
+    List<LabourRequestERSourceList>? sourceList,
+    String? view,
+  }) {
+    return LabourRequestEREntity()
+      ..countList = countList ?? this.countList
+      ..sourceList = sourceList ?? this.sourceList
+      ..view = view ?? this.view;
+  }
+}
+
+LabourRequestERCountList $LabourRequestERCountListFromJson(Map<String, dynamic> json) {
+  final LabourRequestERCountList labourRequestERCountList = LabourRequestERCountList();
+  final String? date = jsonConvert.convert<String>(json['date']);
+  if (date != null) {
+    labourRequestERCountList.date = date;
+  }
+  final String? week = jsonConvert.convert<String>(json['week']);
+  if (week != null) {
+    labourRequestERCountList.week = week;
+  }
+  final String? num = jsonConvert.convert<String>(json['num']);
+  if (num != null) {
+    labourRequestERCountList.num = num;
+  }
+  return labourRequestERCountList;
+}
+
+Map<String, dynamic> $LabourRequestERCountListToJson(LabourRequestERCountList entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['date'] = entity.date;
+  data['week'] = entity.week;
+  data['num'] = entity.num;
+  return data;
+}
+
+extension LabourRequestERCountListExtension on LabourRequestERCountList {
+  LabourRequestERCountList copyWith({
+    String? date,
+    String? week,
+    String? num,
+  }) {
+    return LabourRequestERCountList()
+      ..date = date ?? this.date
+      ..week = week ?? this.week
+      ..num = num ?? this.num;
+  }
+}
+
+LabourRequestERSourceList $LabourRequestERSourceListFromJson(Map<String, dynamic> json) {
+  final LabourRequestERSourceList labourRequestERSourceList = LabourRequestERSourceList();
+  final String? agencyId = jsonConvert.convert<String>(json['agency_id']);
+  if (agencyId != null) {
+    labourRequestERSourceList.agencyId = agencyId;
+  }
+  final String? agencyName = jsonConvert.convert<String>(json['agency_name']);
+  if (agencyName != null) {
+    labourRequestERSourceList.agencyName = agencyName;
+  }
+  final List<LabourRequestERSourceListRequestList>? requestList = (json['request_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<LabourRequestERSourceListRequestList>(e) as LabourRequestERSourceListRequestList).toList();
+  if (requestList != null) {
+    labourRequestERSourceList.requestList = requestList;
+  }
+  return labourRequestERSourceList;
+}
+
+Map<String, dynamic> $LabourRequestERSourceListToJson(LabourRequestERSourceList entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['agency_id'] = entity.agencyId;
+  data['agency_name'] = entity.agencyName;
+  data['request_list'] = entity.requestList?.map((v) => v.toJson()).toList();
+  return data;
+}
+
+extension LabourRequestERSourceListExtension on LabourRequestERSourceList {
+  LabourRequestERSourceList copyWith({
+    String? agencyId,
+    String? agencyName,
+    List<LabourRequestERSourceListRequestList>? requestList,
+  }) {
+    return LabourRequestERSourceList()
+      ..agencyId = agencyId ?? this.agencyId
+      ..agencyName = agencyName ?? this.agencyName
+      ..requestList = requestList ?? this.requestList;
+  }
+}
+
+LabourRequestERSourceListRequestList $LabourRequestERSourceListRequestListFromJson(Map<String, dynamic> json) {
+  final LabourRequestERSourceListRequestList labourRequestERSourceListRequestList = LabourRequestERSourceListRequestList();
+  final String? date = jsonConvert.convert<String>(json['date']);
+  if (date != null) {
+    labourRequestERSourceListRequestList.date = date;
+  }
+  final String? num = jsonConvert.convert<String>(json['num']);
+  if (num != null) {
+    labourRequestERSourceListRequestList.num = num;
+  }
+  return labourRequestERSourceListRequestList;
+}
+
+Map<String, dynamic> $LabourRequestERSourceListRequestListToJson(LabourRequestERSourceListRequestList entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['date'] = entity.date;
+  data['num'] = entity.num;
+  return data;
+}
+
+extension LabourRequestERSourceListRequestListExtension on LabourRequestERSourceListRequestList {
+  LabourRequestERSourceListRequestList copyWith({
+    String? date,
+    String? num,
+  }) {
+    return LabourRequestERSourceListRequestList()
+      ..date = date ?? this.date
+      ..num = num ?? this.num;
+  }
+}

+ 102 - 0
packages/cs_domain/lib/generated/json/labour_request_e_r_option_entity.g.dart

@@ -0,0 +1,102 @@
+import 'package:domain/generated/json/base/json_convert_content.dart';
+import 'package:domain/entity/response/labour_request_e_r_option_entity.dart';
+import 'package:domain/entity/response/index_option_entity.dart';
+
+
+LabourRequestEROptionEntity $LabourRequestEROptionEntityFromJson(Map<String, dynamic> json) {
+  final LabourRequestEROptionEntity labourRequestEROptionEntity = LabourRequestEROptionEntity();
+  final List<IndexOptionEntity>? titleList = (json['title_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<IndexOptionEntity>(e) as IndexOptionEntity).toList();
+  if (titleList != null) {
+    labourRequestEROptionEntity.titleList = titleList;
+  }
+  final List<IndexOptionEntity>? outletList = (json['outlet_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<IndexOptionEntity>(e) as IndexOptionEntity).toList();
+  if (outletList != null) {
+    labourRequestEROptionEntity.outletList = outletList;
+  }
+  final List<IndexOptionEntity>? limitList = (json['limit_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<IndexOptionEntity>(e) as IndexOptionEntity).toList();
+  if (limitList != null) {
+    labourRequestEROptionEntity.limitList = limitList;
+  }
+  final List<IndexOptionEntity>? otRadios = (json['ot_radios'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<IndexOptionEntity>(e) as IndexOptionEntity).toList();
+  if (otRadios != null) {
+    labourRequestEROptionEntity.otRadios = otRadios;
+  }
+  final bool? outletManager = jsonConvert.convert<bool>(json['outlet_manager']);
+  if (outletManager != null) {
+    labourRequestEROptionEntity.outletManager = outletManager;
+  }
+  final String? hiringNum = jsonConvert.convert<String>(json['hiring_num']);
+  if (hiringNum != null) {
+    labourRequestEROptionEntity.hiringNum = hiringNum;
+  }
+  final int? sexLimit = jsonConvert.convert<int>(json['sex_limit']);
+  if (sexLimit != null) {
+    labourRequestEROptionEntity.sexLimit = sexLimit;
+  }
+  final String? maleLimit = jsonConvert.convert<String>(json['male_limit']);
+  if (maleLimit != null) {
+    labourRequestEROptionEntity.maleLimit = maleLimit;
+  }
+  final String? femaleLimit = jsonConvert.convert<String>(json['female_limit']);
+  if (femaleLimit != null) {
+    labourRequestEROptionEntity.femaleLimit = femaleLimit;
+  }
+  final bool? withOtJob = jsonConvert.convert<bool>(json['with_ot_job']);
+  if (withOtJob != null) {
+    labourRequestEROptionEntity.withOtJob = withOtJob;
+  }
+  final String? view = jsonConvert.convert<String>(json['view']);
+  if (view != null) {
+    labourRequestEROptionEntity.view = view;
+  }
+  return labourRequestEROptionEntity;
+}
+
+Map<String, dynamic> $LabourRequestEROptionEntityToJson(LabourRequestEROptionEntity entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['title_list'] = entity.titleList.map((v) => v.toJson()).toList();
+  data['outlet_list'] = entity.outletList.map((v) => v.toJson()).toList();
+  data['limit_list'] = entity.limitList.map((v) => v.toJson()).toList();
+  data['ot_radios'] = entity.otRadios.map((v) => v.toJson()).toList();
+  data['outlet_manager'] = entity.outletManager;
+  data['hiring_num'] = entity.hiringNum;
+  data['sex_limit'] = entity.sexLimit;
+  data['male_limit'] = entity.maleLimit;
+  data['female_limit'] = entity.femaleLimit;
+  data['with_ot_job'] = entity.withOtJob;
+  data['view'] = entity.view;
+  return data;
+}
+
+extension LabourRequestEROptionEntityExtension on LabourRequestEROptionEntity {
+  LabourRequestEROptionEntity copyWith({
+    List<IndexOptionEntity>? titleList,
+    List<IndexOptionEntity>? outletList,
+    List<IndexOptionEntity>? limitList,
+    List<IndexOptionEntity>? otRadios,
+    bool? outletManager,
+    String? hiringNum,
+    int? sexLimit,
+    String? maleLimit,
+    String? femaleLimit,
+    bool? withOtJob,
+    String? view,
+  }) {
+    return LabourRequestEROptionEntity()
+      ..titleList = titleList ?? this.titleList
+      ..outletList = outletList ?? this.outletList
+      ..limitList = limitList ?? this.limitList
+      ..otRadios = otRadios ?? this.otRadios
+      ..outletManager = outletManager ?? this.outletManager
+      ..hiringNum = hiringNum ?? this.hiringNum
+      ..sexLimit = sexLimit ?? this.sexLimit
+      ..maleLimit = maleLimit ?? this.maleLimit
+      ..femaleLimit = femaleLimit ?? this.femaleLimit
+      ..withOtJob = withOtJob ?? this.withOtJob
+      ..view = view ?? this.view;
+  }
+}

+ 3 - 3
packages/cs_domain/lib/repository/ms_repository.dart

@@ -92,7 +92,7 @@ class MSRepository extends GetxService {
     final result = await httpProvider.requestNetResult(
       country == 5
           ? ApiConstants.apiAccountListTH //泰国
-          : ApiConstants.apiAccountListMS, //默认马来
+          : ApiConstants.apiAccountListMS,  //默认马来
       isShowLoadingDialog: true,
       networkDebounce: true,
       cancelToken: cancelToken,
@@ -109,7 +109,7 @@ class MSRepository extends GetxService {
   /// 切换项目
   Future<HttpResult<HotelInfoEntity>> switchProjectSubmit(
     String? adminId, {
-    required int country,
+      required int country,
     CancelToken? cancelToken,
   }) async {
     //参数
@@ -119,7 +119,7 @@ class MSRepository extends GetxService {
     final result = await httpProvider.requestNetResult(
       country == 5
           ? ApiConstants.apiSwitchAccountTH //泰国
-          : ApiConstants.apiSwitchAccountMS, //默认马来
+          : ApiConstants.apiSwitchAccountMS,  //默认马来
       method: HttpMethod.POST,
       params: params,
       networkDebounce: true,

+ 230 - 0
packages/cs_domain/lib/repository/th_er_repository.dart

@@ -0,0 +1,230 @@
+import 'package:domain/entity/response/hotel_info_entity.dart';
+import 'package:domain/entity/response/job_list_e_r_option_entity.dart';
+import 'package:domain/entity/response/labour_request_e_r_entity.dart';
+import 'package:domain/entity/response/sign_in_sign_out_entity.dart';
+import 'package:domain/entity/response/switch_project_entity.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/http/http_provider.dart';
+import 'package:plugin_platform/http/http_result.dart';
+import 'package:plugin_platform/platform_export.dart';
+import 'package:shared/utils/util.dart';
+
+import '../constants/api_constants.dart';
+import '../entity/response/labour_list_e_r_entity.dart';
+import '../entity/response/labour_request_e_r_option_entity.dart';
+
+/// 泰国的 ER 的数据仓库
+class THERRepository extends GetxService {
+  HttpProvider httpProvider;
+
+  THERRepository({required this.httpProvider});
+
+  /// 获取 ER 用工请求列表
+  Future<HttpResult<LabourRequestEREntity>> fetchERLabourRequest(
+    String? date, {
+    CancelToken? cancelToken,
+  }) async {
+    Map<String, String> params = {};
+    if (!Utils.isEmpty(date)) {
+      params["day"] = date!;
+    }
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiERLabourRequestTH,
+      params: params,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      final json = result.getDataJson();
+      var data = LabourRequestEREntity.fromJson(json!);
+      return result.convert<LabourRequestEREntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 获取 ER 用工请求选项
+  Future<HttpResult<LabourRequestEROptionEntity>> fetchERLabourRequestAddOption({
+    CancelToken? cancelToken,
+  }) async {
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiERLabourRequestAddOptionTH,
+      networkDebounce: true,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      final json = result.getDataJson();
+      var data = LabourRequestEROptionEntity.fromJson(json!);
+      return result.convert<LabourRequestEROptionEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 新增 ER 的用工请求
+  Future<HttpResult> addLabourRequestERSubmit({
+    required String jobTitleId,
+    required String startTime,
+    required String endTime,
+    required String outletId,
+    String? repeatStart,
+    String? repeatEnd,
+    String? sexLimit,
+    String? needNum,
+    String? maleLimit,
+    String? femaleLimit,
+    String? eventName,
+    String? eventType,
+    String? remark,
+    CancelToken? cancelToken,
+  }) async {
+    Map<String, String> params = {};
+    params["job_title_id"] = jobTitleId;
+    params["start_time"] = startTime;
+    params["end_time"] = endTime;
+    params["outlet_id"] = outletId;
+    if (!Utils.isEmpty(repeatStart)) {
+      params["repeat_start"] = repeatStart!;
+    }
+    if (!Utils.isEmpty(repeatEnd)) {
+      params["repeat_end"] = repeatEnd!;
+    }
+    if (!Utils.isEmpty(sexLimit)) {
+      params["sex_limit"] = sexLimit!;
+    }
+    if (!Utils.isEmpty(needNum)) {
+      params["need_num"] = needNum!;
+    }
+    if (!Utils.isEmpty(maleLimit)) {
+      params["male_limit"] = maleLimit!;
+    }
+    if (!Utils.isEmpty(femaleLimit)) {
+      params["female_limit"] = femaleLimit!;
+    }
+    if (!Utils.isEmpty(eventName)) {
+      params["event_name"] = eventName!;
+    }
+    if (!Utils.isEmpty(eventType)) {
+      params["event_type"] = eventType!;
+    }
+    if (!Utils.isEmpty(remark)) {
+      params["remark"] = remark!;
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiERLabourRequestAddSubmitTH,
+      method: HttpMethod.POST,
+      params: params,
+      networkDebounce: true,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      return result.convert();
+    }
+    return result.convert();
+  }
+
+  /// 获取 ER 工作列表选项
+  Future<HttpResult<JobListEROptionEntity>> fetchERJobListOption({
+    String? date,
+    CancelToken? cancelToken,
+  }) async {
+    Map<String, String> params = {};
+    if (!Utils.isEmpty(date)) {
+      params["daily"] = date!;
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiERJobListOptionTH,
+      params: params,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      final json = result.getDataJson();
+      var data = JobListEROptionEntity.fromJson(json!);
+      return result.convert<JobListEROptionEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 获取 ER 工作列表
+  Future<HttpResult<LabourListEREntity>> fetchERJobListTable({
+    required int curPage,
+    String? keyword,
+    String? divisionId,
+    String? outletId,
+    String? status,
+    String? startDate,
+    String? endDate,
+    String? trending,
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    params["cur_page"] = curPage.toString();
+    params["page_size"] = "10";
+    if (!Utils.isEmpty(keyword)) {
+      params["job_title"] = keyword!;
+    }
+    if (!Utils.isEmpty(divisionId)) {
+      params["division_id"] = divisionId!;
+    }
+    if (!Utils.isEmpty(outletId)) {
+      params["outlet_id"] = outletId!;
+    }
+    if (!Utils.isEmpty(status)) {
+      params["status"] = status!;
+    }
+    if (!Utils.isEmpty(startDate)) {
+      params["start_date"] = startDate!;
+    }
+    if (!Utils.isEmpty(endDate)) {
+      params["end_date"] = endDate!;
+    }
+    if (!Utils.isEmpty(trending)) {
+      params["trending"] = trending!;
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiERJobListTableTH,
+      params: params,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      final json = result.getDataJson();
+      var data = LabourListEREntity.fromJson(json!);
+      return result.convert<LabourListEREntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  ///  ER 工作列表的删除
+  Future<HttpResult> deleteJobListERSubmit({
+    required String jobId,
+    required String type,
+    CancelToken? cancelToken,
+  }) async {
+    Map<String, String> params = {};
+    params["job_id"] = jobId;
+    params["type"] = type;
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiERJobListDeleteTH,
+      method: HttpMethod.POST,
+      params: params,
+      networkDebounce: true,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      return result.convert();
+    }
+    return result.convert();
+  }
+
+}

+ 2 - 0
packages/cs_initializer/lib/global_services_injection.dart

@@ -6,6 +6,7 @@ import 'package:domain/repository/labour_repository.dart';
 import 'package:domain/repository/labour_sg_repository.dart';
 import 'package:domain/repository/ms_repository.dart';
 import 'package:domain/repository/other_repository.dart';
+import 'package:domain/repository/th_er_repository.dart';
 import 'package:domain/repository/uk_attendance_repository.dart';
 import 'package:domain/repository/uk_job_repository.dart';
 import 'package:domain/repository/uk_report_repository.dart';
@@ -57,6 +58,7 @@ class GlobalServicesInjection {
 
     //MS/NL的数据仓库注入
     Get.lazyPut(() => MSRepository(httpProvider: Get.find()));
+    Get.lazyPut(() => THERRepository(httpProvider: Get.find()));
 
     // 调用额外的依赖注入逻辑(如果提供了)
     if (additionalDependencies != null) {

+ 1 - 0
packages/cs_resources/lib/local/language/en_US.dart

@@ -300,6 +300,7 @@ const Map<String, String> en_US = {
   'Netherlands': 'Netherlands',
   'Retake': 'Retake',
   'thailand': 'Thailand',
+  'Pre Selected': 'Pre Selected',
 
   //插件的国际化
   'Pull to refresh': 'Pull to refresh',

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

@@ -300,6 +300,7 @@ const Map<String, String> zh_CN = {
   'Netherlands': '荷兰',
   'Retake': '重新获取',
   'thailand': '泰国',
+  'Pre Selected': '预选',
 
   //插件的国际化
   'Pull to refresh': '下拉刷新',

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

@@ -149,6 +149,9 @@ class RouterPath {
   static const THMain = '/th/main';   //泰国的首页
   static const THLabourRequestOA = '/th/labour/request/oa';  //泰国的用工请求页面
   static const THLabourRequestAddOA = '/th/labour/request/add/oa';  //泰国的用工请求添加
+  static const THLabourRequestER = '/th/labour/request/er';  //泰国的用工请求 ER
+  static const THLabourRequestERAdd = '/th/labour/request/er/add';  //泰国的用工请求添加 ER
+  static const THJobListER = '/th/job/list/er';  //泰国的用工请求添加 ER
 
   //Runalone
   static const runAloneMain = '/runalone/main'; //独立运行的入口页面