Browse Source

用工请求的页面与逻辑

liukai 7 months ago
parent
commit
eb81ef86e6
24 changed files with 1563 additions and 9 deletions
  1. 2 1
      packages/cpt_auth/lib/modules/main/main_controller.dart
  2. 1 4
      packages/cpt_labour_sg/lib/modules/job_template_add/job_template_add_page.dart
  3. 75 0
      packages/cpt_labour_sg/lib/modules/labour_request/labour_request_controller.dart
  4. 51 0
      packages/cpt_labour_sg/lib/modules/labour_request/labour_request_item.dart
  5. 168 0
      packages/cpt_labour_sg/lib/modules/labour_request/labour_request_page.dart
  6. 7 0
      packages/cpt_labour_sg/lib/modules/labour_request/labour_request_state.dart
  7. 233 0
      packages/cpt_labour_sg/lib/modules/labour_request_add/labour_request_add_controller.dart
  8. 502 0
      packages/cpt_labour_sg/lib/modules/labour_request_add/labour_request_add_page.dart
  9. 59 0
      packages/cpt_labour_sg/lib/modules/labour_request_add/labour_request_add_state.dart
  10. 4 1
      packages/cpt_labour_sg/lib/router/labour_sg_service_impl.dart
  11. 15 0
      packages/cpt_labour_sg/lib/router/page_router.dart
  12. 13 0
      packages/cs_domain/lib/constants/api_constants.dart
  13. 43 0
      packages/cs_domain/lib/entity/response/labour_request_s_g_add_index_entity.dart
  14. 43 0
      packages/cs_domain/lib/entity/response/labour_request_s_g_entity.dart
  15. 18 0
      packages/cs_domain/lib/generated/json/base/json_convert_content.dart
  16. 81 0
      packages/cs_domain/lib/generated/json/labour_request_s_g_add_index_entity.g.dart
  17. 79 0
      packages/cs_domain/lib/generated/json/labour_request_s_g_entity.g.dart
  18. 121 3
      packages/cs_domain/lib/repository/labour_sg_repository.dart
  19. BIN
      packages/cs_resources/assets/cpt_job/pick_date_icon.png
  20. 1 0
      packages/cs_resources/lib/generated/assets.dart
  21. 15 0
      packages/cs_resources/lib/local/language/en_US.dart
  22. 15 0
      packages/cs_resources/lib/local/language/vi_VN.dart
  23. 15 0
      packages/cs_resources/lib/local/language/zh_CN.dart
  24. 2 0
      packages/cs_router/lib/path/router_path.dart

+ 2 - 1
packages/cpt_auth/lib/modules/main/main_controller.dart

@@ -120,7 +120,8 @@ class MainController extends GetxController {
     switch (module.key) {
       case 'labReq':
         if (ConfigService.to.curSelectCountry.value == 1) {
-          ToastEngine.show("进入新加坡的用工请求模块");
+          //新加坡的用工请求
+          ComponentRouterServices.labourSGService.startLabourRequestPage();
         } else {
           //越南的用工请求
           ComponentRouterServices.labourService.startLabourRequestPage();

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

@@ -186,10 +186,7 @@ class JobTemplateAddPage extends BaseStatelessPage<JobTemplateAddController> {
                           state.foodCert = index == 0 ? "1" : "0";
                         },
                         selectedPosition: state.indexEntity?.withFoodCert != null
-                            ? state.indexEntity?.withFoodCert == 0
-                                ? 1
-                                : 0
-                            : -1,
+                            ? state.indexEntity?.withFoodCert == 0 ? 1 : 0 : -1,
                       ).marginOnly(left: 15, right: 15, top: 10),
 
                       //模板详情

+ 75 - 0
packages/cpt_labour_sg/lib/modules/labour_request/labour_request_controller.dart

@@ -0,0 +1,75 @@
+import 'package:domain/repository/labour_sg_repository.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/picker/date_picker_util.dart';
+
+import '../labour_request_add/labour_request_add_page.dart';
+import 'labour_request_state.dart';
+
+class LabourRequestController extends GetxController with DioCancelableMixin {
+  final LabourSGRepository _labourRepository = Get.find();
+  final LabourRequestState state = LabourRequestState();
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Success;
+  String? errorMessage;
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  void retryRequest() {
+    fetchLabourRequestMain();
+  }
+
+  //去创建页面
+  void gotoLabourRequestAddPage() {
+    LabourRequestAddPage.startInstance((result) {
+      if (result is bool) {
+        //添加成功之后刷新
+        fetchLabourRequestMain();
+      }
+    });
+  }
+
+  // 获取首页数据
+  void fetchLabourRequestMain() async {
+    changeLoadingState(LoadState.State_Loading);
+
+    var result = await _labourRepository.fetchLabourRequestMain(
+      DateTimeUtils.formatDate(state.selectedDateTime, format: "yyyy-MM-dd"),
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      state.datas = result.data?.countList ?? [];
+      changeLoadingState(LoadState.State_Success);
+    } else {
+      errorMessage = result.errorMsg ?? "Network Load Error".tr;
+      changeLoadingState(LoadState.State_Error);
+    }
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    fetchLabourRequestMain();
+  }
+
+  /// 筛选开始日期
+  void pickerSelectDate() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.selectedDateTime,
+      onDateTimeChanged: (date) {
+        state.selectedDateTime = date;
+        update();
+        fetchLabourRequestMain();
+      },
+      title: "Select Date".tr,
+    );
+  }
+}

+ 51 - 0
packages/cpt_labour_sg/lib/modules/labour_request/labour_request_item.dart

@@ -0,0 +1,51 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:domain/entity/response/job_template_s_g_entity.dart';
+import 'package:domain/entity/response/job_title_s_g_entity.dart';
+import 'package:domain/entity/response/labour_request_list_entity.dart';
+import 'package:domain/entity/response/labour_request_s_g_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_text_view.dart';
+
+/**
+ * 用工请求Item(新加坡)
+ */
+class LabourRequestItem extends StatelessWidget {
+  final LabourRequestSGCountList item;
+
+  LabourRequestItem({
+    required this.item,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Row(
+      children: [
+        MyTextView(
+          "[${item.week}] ${item.date}",
+          isFontRegular: true,
+          fontSize: 16,
+          textAlign: TextAlign.start,
+          marginLeft: 34,
+          textColor: item.today ? ColorConstants.textGreen0AC074 : Colors.white,
+        ).expanded(flex: 6),
+        MyTextView(
+          "${item.realNum}/${item.hiringNum}",
+          isFontBold: true,
+          fontSize: 16,
+          textAlign: TextAlign.right,
+          marginRight: 50,
+          textDecoration: TextDecoration.underline,
+          decorationColor: ColorConstants.textYellowFFBB1B,
+          decorationThickness: 2.0,
+          decorationStyle: TextDecorationStyle.solid,
+          textColor: ColorConstants.textYellowFFBB1B,
+        ).expanded(flex: 4),
+      ],
+    ).paddingOnly(top: 7, bottom: 7);
+  }
+}

+ 168 - 0
packages/cpt_labour_sg/lib/modules/labour_request/labour_request_page.dart

@@ -0,0 +1,168 @@
+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: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 'labour_request_controller.dart';
+
+import 'package:plugin_basic/base/base_stateless_page.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+
+import 'labour_request_item.dart';
+import 'labour_request_state.dart';
+
+class LabourRequestPage extends BaseStatelessPage<LabourRequestController> {
+  LabourRequestPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance() {
+    return Get.start(RouterPath.LABOUR_REQUEST_SG);
+  }
+
+  late LabourRequestState state;
+
+  @override
+  void initState() {
+    state = controller.state;
+  }
+
+  @override
+  LabourRequestController createRawController() {
+    return LabourRequestController();
+  }
+
+  @override
+  Widget buildWidget(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return Scaffold(
+        extendBodyBehindAppBar: true,
+        appBar: MyAppBar.appBar(context, "Labour Request".tr),
+        body: SafeArea(
+          bottom: true,
+          top: false,
+          child: Container(
+            width: double.infinity,
+            height: double.infinity,
+            padding: EdgeInsets.only(top: kToolbarHeight + ScreenUtil.getStatusBarH(context) + 1),
+            decoration: const BoxDecoration(
+              gradient: LinearGradient(
+                colors: [
+                  Color(0xFF091D44),
+                  Color(0xFF245A8A),
+                  Color(0xFF7F7CEC),
+                ],
+                begin: Alignment.topCenter,
+                end: Alignment.bottomCenter,
+              ),
+            ),
+            child: Column(
+              children: [
+                // 搜索的布局
+                Container(
+                  width: double.infinity,
+                  height: 42,
+                  margin: EdgeInsets.symmetric(vertical: 15, horizontal: 15),
+                  padding: EdgeInsets.symmetric(vertical: 0, horizontal: 13.5),
+                  decoration: BoxDecoration(
+                    color: Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+                    borderRadius: BorderRadius.circular(20.0), // 设置圆角
+                  ),
+                  child: Row(
+                    children: [
+                      MyAssetImage(Assets.cptJobPickDateIcon, width: 15, height: 15),
+                      MyTextView(
+                        DateTimeUtils.formatDate(state.selectedDateTime, format: "yyyy-MM-dd"),
+                        hint: "Select Date".tr,
+                        textHintColor: ColorConstants.textGrayAECAE5,
+                        fontSize: 15,
+                        marginLeft: 9.5,
+                        isFontRegular: true,
+                        textColor: ColorConstants.textGrayAECAE5,
+                      ),
+                    ],
+                  ).onTap(() {
+                    controller.pickerSelectDate();
+                  }),
+                ),
+
+                //添加用工请求按钮
+                MyButton(
+                  type: ClickType.throttle,
+                  milliseconds: 500,
+                  onPressed: () {
+                    FocusScope.of(context).unfocus();
+                    controller.gotoLabourRequestAddPage();
+                  },
+                  text: "Create New Job Request".tr,
+                  textColor: ColorConstants.white,
+                  fontSize: 16,
+                  radius: 20,
+                  backgroundColor: hexToColor("#FFBB1B"),
+                  fontWeight: FontWeight.w500,
+                ).marginOnly(left: 15, right: 15),
+
+                Container(
+                  width: double.infinity,
+                  margin: EdgeInsets.symmetric(vertical: 15, horizontal: 15),
+                  decoration: BoxDecoration(
+                    color: Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+                    borderRadius: BorderRadius.circular(5.0), // 设置圆角
+                  ),
+                  child: Column(
+                    children: [
+                      Row(
+                        children: [
+                          MyTextView(
+                            "Date".tr,
+                            isFontBold: true,
+                            fontSize: 16,
+                            textAlign: TextAlign.center,
+                            textColor: Colors.white,
+                          ).expanded(flex: 5),
+                          MyTextView(
+                            "YY Circle".tr,
+                            isFontBold: true,
+                            fontSize: 16,
+                            textAlign: TextAlign.right,
+                            marginRight: 32,
+                            textColor: Colors.white,
+                          ).expanded(flex: 5),
+                        ],
+                      ).constrained(height: 45),
+
+                      //分割线
+                      Divider(color: ColorConstants.dividerBar, height: 0.5),
+
+                      //底部的数据
+                      LoadStateLayout(
+                        state: controller.loadingState,
+                        errorMessage: controller.errorMessage,
+                        errorRetry: () {
+                          controller.retryRequest();
+                        },
+                        successWidget: Column(
+                          children: state.datas.map((item) {
+                            return LabourRequestItem(item: item);
+                          }).toList(),
+                        ).paddingOnly(top: 14, bottom: 7),
+                      ).constrained(width: double.infinity, height: 291),
+                    ],
+                  ),
+                ).constrained(minHeight: 336.5),
+              ],
+            ),
+          ),
+        ),
+      );
+    });
+  }
+}

+ 7 - 0
packages/cpt_labour_sg/lib/modules/labour_request/labour_request_state.dart

@@ -0,0 +1,7 @@
+import 'package:domain/entity/response/labour_request_s_g_entity.dart';
+
+class LabourRequestState {
+
+  DateTime? selectedDateTime;
+  List<LabourRequestSGCountList> datas = [];
+}

+ 233 - 0
packages/cpt_labour_sg/lib/modules/labour_request_add/labour_request_add_controller.dart

@@ -0,0 +1,233 @@
+import 'package:domain/repository/labour_sg_repository.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/engine/notify/notify_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:shared/utils/date_time_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_add_state.dart';
+
+class LabourRequestAddController extends GetxController with DioCancelableMixin {
+  final LabourSGRepository _labourRepository = Get.find();
+  final LabourRequestAddState state = LabourRequestAddState();
+
+  // 获取添加选项数据
+  void fetchLabourRequestAddIndex() async {
+    var result = await _labourRepository.fetchLabourRequestAddIndex(
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      state.indexEntity = result.data;
+      update();
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    fetchLabourRequestAddIndex();
+  }
+
+  // 选择工标题
+  void pickJobTitle() {
+    if (state.indexEntity == null) {
+      return;
+    }
+
+    int selectedTemplateIndex;
+    if (state.selectedJobTitleId == null) {
+      selectedTemplateIndex = 0;
+    } else {
+      selectedTemplateIndex = state.indexEntity!.titleList.indexWhere((department) => department.value.toString() == state.selectedJobTitleId);
+    }
+
+    if (selectedTemplateIndex < 0) {
+      selectedTemplateIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: state.indexEntity!.titleList.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedTemplateIndex,
+      onPickerChanged: (_, index) {
+        state.selectedJobTitleId = state.indexEntity!.titleList[index].value!.toString();
+        state.selectedJobTitle = state.indexEntity!.titleList[index].txt!.toString();
+        update();
+      },
+    );
+  }
+
+  //选择开始时间
+  void pickStartTime() {
+    if (state.indexEntity == null) {
+      return;
+    }
+
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.selectedStartTime,
+      mode: CupertinoDatePickerMode.dateAndTime,
+      onDateTimeChanged: (date) {
+        state.selectedStartTime = date;
+        update();
+      },
+      title: "Start Time".tr,
+    );
+  }
+
+  // 选择结束时间
+  void pickEndTime() {
+    if (state.indexEntity == null) {
+      return;
+    }
+
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.selectedEndTime ?? state.selectedStartTime,
+      mode: CupertinoDatePickerMode.dateAndTime,
+      onDateTimeChanged: (date) {
+        state.selectedEndTime = date;
+        update();
+      },
+      title: "End Time".tr,
+    );
+  }
+
+  void pickRepeatStartTime() {
+    if (state.indexEntity == null) {
+      return;
+    }
+
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.selectedRepeatStartTime,
+      mode: CupertinoDatePickerMode.dateAndTime,
+      onDateTimeChanged: (date) {
+        state.selectedRepeatStartTime = date;
+        update();
+      },
+      title: "Start Time".tr,
+    );
+  }
+
+  void pickRepeatEndTime() {
+    if (state.indexEntity == null) {
+      return;
+    }
+
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.selectedRepeatEndTime ?? state.selectedRepeatStartTime,
+      mode: CupertinoDatePickerMode.dateAndTime,
+      onDateTimeChanged: (date) {
+        state.selectedRepeatEndTime = date;
+        update();
+      },
+      title: "End Time".tr,
+    );
+  }
+
+  //选择部门
+  void pickOutlet() {
+    if (state.indexEntity == null) {
+      return;
+    }
+
+    int selectedIndex;
+    if (state.selectedOutletId == null) {
+      selectedIndex = 0;
+    } else {
+      selectedIndex = state.indexEntity!.outletList.indexWhere((department) => department.value.toString() == state.selectedOutletId);
+    }
+
+    if (selectedIndex < 0) {
+      selectedIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: state.indexEntity!.outletList.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedIndex,
+      onPickerChanged: (_, index) {
+        state.selectedOutletId = state.indexEntity!.outletList[index].value!.toString();
+        state.selectedOutlet = state.indexEntity!.outletList[index].txt!.toString();
+        update();
+      },
+    );
+  }
+
+  // 提交表单
+  void doSubmit() 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'];
+
+    String maleNo = maleNoController.text.toString();
+    String femaleNo = femaleNoController.text.toString();
+    String needNo = needNoController.text.toString();
+    String remark = remarkController.text.toString();
+
+    if (Utils.isEmpty(state.selectedJobTitleId)) {
+      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.selectedOutletId)) {
+      ToastEngine.show("Choose Outlet".tr);
+      return;
+    }
+
+    if (state.genderOptionType == 0) {
+      if (Utils.isEmpty(needNo)) {
+        ToastEngine.show("Enter No. of Staff".tr);
+        return;
+      }
+    } else {
+      if (Utils.isEmpty(maleNo) || Utils.isEmpty(femaleNo)) {
+        ToastEngine.show("Enter No. of Staff of The Corresponding Gender".tr);
+        return;
+      }
+    }
+
+    var result = await _labourRepository.submitLabourRequestAdd(
+      state.selectedJobTitleId,
+      DateTimeUtils.formatDate(state.selectedStartTime),
+      DateTimeUtils.formatDate(state.selectedEndTime),
+      DateTimeUtils.formatDate(state.selectedRepeatStartTime),
+      DateTimeUtils.formatDate(state.selectedRepeatEndTime),
+      state.selectedOutletId,
+      state.genderOptionType,
+      maleNo,
+      femaleNo,
+      needNo,
+      state.selectRequestTypeId,
+      remark,
+      cancelToken: cancelToken,
+    );
+
+    //处理数据
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess("Successful".tr);
+
+      //根据类型刷新
+      state.cb?.call(true);
+
+      Get.back();
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+
+  }
+}

+ 502 - 0
packages/cpt_labour_sg/lib/modules/labour_request_add/labour_request_add_page.dart

@@ -0,0 +1,502 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:get/get.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/no_shadow_scroll_behavior.dart';
+import 'package:widgets/shatter/custom_radio_check.dart';
+import 'package:widgets/shatter/form_require_text.dart';
+import 'package:widgets/shatter/round_my_text_field.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'labour_request_add_controller.dart';
+
+import 'package:plugin_basic/base/base_stateless_page.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+
+import 'labour_request_add_state.dart';
+
+class LabourRequestAddPage extends BaseStatelessPage<LabourRequestAddController> {
+  LabourRequestAddPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance(void Function(dynamic value)? cb) {
+    return Get.start(RouterPath.LABOUR_REQUEST_ADD_SG, arguments: {'cb': cb});
+  }
+
+  late LabourRequestAddState state;
+
+  @override
+  void initState() {
+    state = controller.state;
+    state.cb = Get.arguments['cb'] as void Function(dynamic)?;
+  }
+
+  @override
+  LabourRequestAddController createRawController() {
+    return LabourRequestAddController();
+  }
+
+  @override
+  Widget buildWidget(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return Scaffold(
+        extendBodyBehindAppBar: true,
+        appBar: MyAppBar.appBar(context, "Add Labour Requisition".tr),
+        body: SafeArea(
+          bottom: true,
+          top: false,
+          child: Container(
+            width: double.infinity,
+            height: double.infinity,
+            padding: EdgeInsets.only(top: kToolbarHeight + ScreenUtil.getStatusBarH(context) + 1),
+            decoration: const BoxDecoration(
+              gradient: LinearGradient(
+                colors: [
+                  Color(0xFF091D44),
+                  Color(0xFF245A8A),
+                  Color(0xFF7F7CEC),
+                ],
+                begin: Alignment.topCenter,
+                end: Alignment.bottomCenter,
+              ),
+            ),
+            child: Scrollbar(
+              child: ScrollConfiguration(
+                behavior: NoShadowScrollBehavior(),
+                child: SingleChildScrollView(
+                  scrollDirection: Axis.vertical,
+                  physics: const BouncingScrollPhysics(),
+                  child: Column(
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    children: [
+                      //工作标题,选择模板
+                      FormRequireText(
+                        text: "Job Title".tr,
+                      ).marginOnly(top: 15),
+
+                      //工作标题
+                      Container(
+                        padding: EdgeInsets.only(left: 16, right: 10),
+                        margin: EdgeInsets.only(top: 10),
+                        height: 45,
+                        decoration: BoxDecoration(
+                          color: 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.selectedJobTitle ?? "",
+                              fontSize: 14,
+                              hint: "Choose Job Title".tr,
+                              textHintColor: ColorConstants.textGrayAECAE5,
+                              isFontMedium: true,
+                              textColor: ColorConstants.white,
+                            ).expanded(),
+
+                            //下拉选图标
+                            MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                          ],
+                        ),
+                      ).onTap(() {
+                        FocusScope.of(context).unfocus();
+                        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: EdgeInsets.only(left: 16, right: 10),
+                              height: 45,
+                              decoration: BoxDecoration(
+                                color: 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.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(),
+                                  //下拉选图标
+                                  MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                                ],
+                              ),
+                            ).onTap(() {
+                              FocusScope.of(context).unfocus();
+                              controller.pickStartTime();
+                            }),
+                          ),
+
+                          // //选择结束时间
+                          Expanded(
+                            child: Container(
+                              padding: EdgeInsets.only(left: 16, right: 10),
+                              margin: EdgeInsets.only(left: 10),
+                              height: 45,
+                              decoration: BoxDecoration(
+                                color: 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.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(),
+                                  //下拉选图标
+                                  MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                                ],
+                              ),
+                            ).onTap(() {
+                              FocusScope.of(context).unfocus();
+                              controller.pickEndTime();
+                            }),
+                          ),
+                        ],
+                      ).marginOnly(top: 10),
+
+                      //选择重复时间
+                      MyTextView(
+                        "Repeat".tr,
+                        fontSize: 15,
+                        isFontRegular: true,
+                        textColor: Colors.white,
+                        marginTop: 15,
+                      ),
+
+                      Row(
+                        mainAxisSize: MainAxisSize.max,
+                        crossAxisAlignment: CrossAxisAlignment.center,
+                        mainAxisAlignment: MainAxisAlignment.start,
+                        children: [
+                          //选择开始时间
+                          Expanded(
+                            child: Container(
+                              padding: EdgeInsets.only(left: 16, right: 10),
+                              height: 45,
+                              decoration: BoxDecoration(
+                                color: 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.selectedRepeatStartTime == null
+                                        ? ""
+                                        : DateTimeUtils.formatDate(state.selectedRepeatStartTime, format: "yyyy-MM-dd HH:mm"),
+                                    fontSize: 14,
+                                    hint: "Repeat Start Time".tr,
+                                    textHintColor: ColorConstants.textGrayAECAE5,
+                                    isFontMedium: true,
+                                    textColor: ColorConstants.white,
+                                  ).expanded(),
+                                  //下拉选图标
+                                  MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                                ],
+                              ),
+                            ).onTap(() {
+                              FocusScope.of(context).unfocus();
+                              controller.pickRepeatStartTime();
+                            }),
+                          ),
+
+                          // //选择结束时间
+                          Expanded(
+                            child: Container(
+                              padding: EdgeInsets.only(left: 16, right: 10),
+                              margin: EdgeInsets.only(left: 10),
+                              height: 45,
+                              decoration: BoxDecoration(
+                                color: 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.selectedRepeatEndTime == null
+                                        ? ""
+                                        : DateTimeUtils.formatDate(state.selectedRepeatEndTime, format: "yyyy-MM-dd HH:mm"),
+                                    fontSize: 14,
+                                    hint: "Repeat End Time".tr,
+                                    textHintColor: ColorConstants.textGrayAECAE5,
+                                    isFontMedium: true,
+                                    textColor: ColorConstants.white,
+                                  ).expanded(),
+                                  //下拉选图标
+                                  MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                                ],
+                              ),
+                            ).onTap(() {
+                              FocusScope.of(context).unfocus();
+                              controller.pickRepeatEndTime();
+                            }),
+                          ),
+                        ],
+                      ).marginOnly(top: 10),
+
+                      //工作选择部门
+                      FormRequireText(
+                        text: "Outlet".tr,
+                      ).marginOnly(top: 15),
+
+                      //选择部门
+                      Container(
+                        padding: EdgeInsets.only(left: 16, right: 10),
+                        margin: EdgeInsets.only(top: 10),
+                        height: 45,
+                        decoration: BoxDecoration(
+                          color: 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.selectedOutlet ?? "",
+                              fontSize: 14,
+                              hint: "Choose Outlet".tr,
+                              textHintColor: ColorConstants.textGrayAECAE5,
+                              isFontMedium: true,
+                              textColor: ColorConstants.white,
+                            ).expanded(),
+                            MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                          ],
+                        ),
+                      ).onTap(() {
+                        FocusScope.of(context).unfocus();
+                        controller.pickOutlet();
+                      }),
+
+                      //需要的人数
+                      FormRequireText(
+                        text: "No. of Staff".tr,
+                      ).marginOnly(top: 15),
+
+                      //选择人数类型单选
+                      CustomRadioCheck(
+                        options: state.genderOptions,
+                        onOptionSelected: (index, text) {
+                          state.genderOptionType = index;
+                          controller.update();
+                        },
+                        selectedPosition: state.genderOptionType,
+                      ).marginOnly(top: 10),
+
+                      //输入框(只允许输入数字)
+                      Visibility(
+                        visible: state.genderOptionType == 0,
+                        child: CustomTextField(
+                          formKey: "need_no",
+                          marginLeft: 0,
+                          marginRight: 0,
+                          paddingTop: 0,
+                          paddingBottom: 0,
+                          height: 45,
+                          fillBackgroundColor: 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();
+                          },
+                          marginTop: 10,
+                        ),
+                      ),
+
+                      Visibility(
+                        visible: state.genderOptionType != 0,
+                        child: Row(
+                          children: [
+                            MyTextView(
+                              "Male".tr,
+                              fontSize: 15,
+                              isFontRegular: true,
+                              marginRight: 10,
+                              textColor: ColorConstants.textGrayAECAE5,
+                            ),
+                            CustomTextField(
+                              formKey: "need_male",
+                              marginLeft: 0,
+                              marginRight: 0,
+                              paddingTop: 0,
+                              paddingBottom: 0,
+                              height: 45,
+                              fillBackgroundColor: 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(),
+                            MyTextView(
+                              "Female".tr,
+                              fontSize: 15,
+                              isFontRegular: true,
+                              marginLeft: 12,
+                              marginRight: 10,
+                              textColor: ColorConstants.textGrayAECAE5,
+                            ),
+                            CustomTextField(
+                              formKey: "need_female",
+                              marginLeft: 0,
+                              marginRight: 0,
+                              paddingTop: 0,
+                              paddingBottom: 0,
+                              height: 45,
+                              fillBackgroundColor: 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(),
+                          ],
+                        ).marginOnly(top: 10),
+                      ),
+
+                      //Request Type
+                      MyTextView(
+                        "Request Type".tr,
+                        fontSize: 15,
+                        isFontRegular: true,
+                        textColor: Colors.white,
+                        marginTop: 15,
+                      ),
+
+                      //Request Type单选
+                      CustomRadioCheck(
+                        options: state.indexEntity?.requestType.map((e) => e.txt!).toList() ?? [],
+                        onOptionSelected: (index, text) {
+                          state.selectRequestTypeId = state.indexEntity!.requestType[index].value;
+                        },
+                        selectedPosition: state.indexEntity == null ? -1 : 0,
+                      ).marginOnly(top: 10),
+
+                      //输入Remark
+                      MyTextView(
+                        "Remark".tr,
+                        fontSize: 15,
+                        isFontRegular: true,
+                        textColor: Colors.white,
+                        marginTop: 15,
+                      ),
+
+                      IgnoreKeyboardDismiss(
+                        child: Container(
+                          height: 160,
+                          margin: EdgeInsets.only(top: 10),
+                          padding: EdgeInsets.symmetric(vertical: 15, horizontal: 15),
+                          decoration: BoxDecoration(
+                            color: Color(0xFF4DCFF6).withOpacity(0.2),
+                            borderRadius: BorderRadius.all(Radius.circular(5)),
+                          ),
+                          child: TextField(
+                            cursorColor: ColorConstants.white,
+                            cursorWidth: 1.5,
+                            autofocus: false,
+                            enabled: true,
+                            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: TextStyle(
+                                color: ColorConstants.textGrayAECAE5,
+                                fontSize: 15.0,
+                                fontWeight: FontWeight.w400,
+                              ),
+                            ),
+                            style: TextStyle(
+                              color: ColorConstants.white,
+                              fontSize: 15.0,
+                              fontWeight: FontWeight.w400,
+                            ),
+                            // 键盘动作右下角图标
+                            textInputAction: TextInputAction.done,
+                            onSubmitted: (value) {
+                              FocusScope.of(context).unfocus();
+                            },
+                          ),
+                        ),
+                      ),
+
+                      //提交按钮
+                      MyButton(
+                        type: ClickType.throttle,
+                        milliseconds: 500,
+                        onPressed: () {
+                          FocusScope.of(context).unfocus();
+                          controller.doSubmit();
+                        },
+                        text: "Submit".tr,
+                        textColor: ColorConstants.white,
+                        fontSize: 16,
+                        radius: 22.5,
+                        backgroundColor: hexToColor("#FFBB1B"),
+                        fontWeight: FontWeight.w500,
+                      ).marginSymmetric(horizontal: 0, vertical: 30),
+                    ],
+                  ).paddingOnly(left: 15, right: 15),
+                ),
+              ),
+            ),
+          ),
+        ),
+      );
+    });
+  }
+}

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

@@ -0,0 +1,59 @@
+import 'package:domain/entity/response/labour_request_s_g_add_index_entity.dart';
+import 'package:flutter/material.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:shared/utils/date_time_utils.dart';
+
+class LabourRequestAddState {
+  //表单的校验与数据
+  Map<String, Map<String, dynamic>> formData = {
+    'need_male': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Needs Num'.tr,
+      'obsecure': false,
+    },
+    'need_female': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Needs Num'.tr,
+      'obsecure': false,
+    },
+    'need_no': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Needs Num'.tr,
+      'obsecure': false,
+    },
+    'remark': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter...'.tr,
+      'obsecure': false,
+    },
+  };
+
+  void Function(dynamic value)? cb;
+
+  List<String> genderOptions = ["Gender Unlimited".tr, "Gender Limited".tr];
+  int genderOptionType = 0;  //使用哪一种类型限制
+
+  LabourRequestSGAddIndexEntity? indexEntity;
+
+  String? selectedJobTitle;
+  String? selectedJobTitleId;
+
+  DateTime? selectedStartTime;
+  DateTime? selectedEndTime;
+
+  DateTime? selectedRepeatStartTime;
+  DateTime? selectedRepeatEndTime;
+
+  String? selectedOutlet;
+  String? selectedOutletId;
+
+  String? selectRequestTypeId;
+}

+ 4 - 1
packages/cpt_labour_sg/lib/router/labour_sg_service_impl.dart

@@ -1,4 +1,5 @@
 import 'package:cpt_labour_sg/modules/job_title_list/job_title_list_page.dart';
+import 'package:cpt_labour_sg/modules/labour_request/labour_request_page.dart';
 import 'package:plugin_basic/basic_export.dart';
 import 'package:router/componentRouter/labour_sg_service.dart';
 import 'package:shared/utils/log_utils.dart';
@@ -17,7 +18,9 @@ class LabourSGServiceImpl extends GetxService implements LabourSGService {
   }
 
   @override
-  void startLabourRequestPage() {}
+  void startLabourRequestPage() {
+    LabourRequestPage.startInstance();
+  }
 
   @override
   void startJobTitlePage() {

+ 15 - 0
packages/cpt_labour_sg/lib/router/page_router.dart

@@ -6,6 +6,9 @@ import 'package:flutter/material.dart';
 import 'package:get/get.dart';
 import 'package:router/path/router_path.dart';
 
+import '../modules/labour_request/labour_request_page.dart';
+import '../modules/labour_request_add/labour_request_add_page.dart';
+
 
 class LabourSGPageRouter {
 
@@ -29,5 +32,17 @@ class LabourSGPageRouter {
       page: () => JobTemplateAddPage(),
     ),
 
+    // 用工请求
+    GetPage(
+      name: RouterPath.LABOUR_REQUEST_SG,
+      page: () => LabourRequestPage(),
+    ),
+
+    // 用工请求添加
+    GetPage(
+      name: RouterPath.LABOUR_REQUEST_ADD_SG,
+      page: () => LabourRequestAddPage(),
+    ),
+
   ];
 }

+ 13 - 0
packages/cs_domain/lib/constants/api_constants.dart

@@ -155,8 +155,21 @@ class ApiConstants {
   // 删除工作模板
   static const apiJobTemplateDeleteSG = "/index.php/api/v1/hotel/temp/delete";
 
+  //用工请求首页数据
+  static const apiLabourRequestMainSG = "/index.php/api/v1/hotel/lab-req/index";
+
+  //用工请求的添加页面选项数据
+  static const apiLabourRequestAddIndexSG = "/index.php/api/v1/hotel/lab-req/add-view";
+
+  //用工请求的添加提交
+  static const apiLabourRequestAddSubmitSG = "/index.php/api/v1/hotel/lab-req/add-submit";
+
+  // =========================== 新加坡工作相关 ↓=========================================
+
+
   // =========================== 报表与其他 ↓=========================================
 
   // 设备列表
   static const apiDeviceList = "/index.php/api/v1/hotel/device/table";
+
 }

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

@@ -0,0 +1,43 @@
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/labour_request_s_g_add_index_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/labour_request_s_g_add_index_entity.g.dart';
+
+@JsonSerializable()
+class LabourRequestSGAddIndexEntity {
+	@JSONField(name: "title_list")
+	List<LabourRequestSGAddIndexOption> titleList = [];
+	@JSONField(name: "outlet_list")
+	List<LabourRequestSGAddIndexOption> outletList = [];
+	@JSONField(name: "request_type")
+	List<LabourRequestSGAddIndexOption> requestType = [];
+
+	LabourRequestSGAddIndexEntity();
+
+	factory LabourRequestSGAddIndexEntity.fromJson(Map<String, dynamic> json) => $LabourRequestSGAddIndexEntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $LabourRequestSGAddIndexEntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class LabourRequestSGAddIndexOption {
+	String? value = null;
+	String? txt = null;
+	String? selected = null;
+
+	LabourRequestSGAddIndexOption();
+
+	factory LabourRequestSGAddIndexOption.fromJson(Map<String, dynamic> json) => $LabourRequestSGAddIndexOptionFromJson(json);
+
+	Map<String, dynamic> toJson() => $LabourRequestSGAddIndexOptionToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}

+ 43 - 0
packages/cs_domain/lib/entity/response/labour_request_s_g_entity.dart

@@ -0,0 +1,43 @@
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/labour_request_s_g_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/labour_request_s_g_entity.g.dart';
+
+@JsonSerializable()
+class LabourRequestSGEntity {
+	@JSONField(name: "count_list")
+	List<LabourRequestSGCountList> countList = [];
+
+	LabourRequestSGEntity();
+
+	factory LabourRequestSGEntity.fromJson(Map<String, dynamic> json) => $LabourRequestSGEntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $LabourRequestSGEntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class LabourRequestSGCountList {
+	String? date = null;
+	String? week = null;
+	bool today = false;
+	@JSONField(name: "real_num")
+	int realNum = 0;
+	@JSONField(name: "hiring_num")
+	int hiringNum = 0;
+
+	LabourRequestSGCountList();
+
+	factory LabourRequestSGCountList.fromJson(Map<String, dynamic> json) => $LabourRequestSGCountListFromJson(json);
+
+	Map<String, dynamic> toJson() => $LabourRequestSGCountListToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}

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

@@ -26,6 +26,8 @@ import 'package:domain/entity/response/job_title_s_g_entity.dart';
 import 'package:domain/entity/response/labour_request_edit_index_entity.dart';
 import 'package:domain/entity/response/labour_request_index_entity.dart';
 import 'package:domain/entity/response/labour_request_list_entity.dart';
+import 'package:domain/entity/response/labour_request_s_g_add_index_entity.dart';
+import 'package:domain/entity/response/labour_request_s_g_entity.dart';
 import 'package:domain/entity/response/labour_request_work_flow_entity.dart';
 import 'package:domain/entity/response/staff_detail_entity.dart';
 import 'package:domain/entity/response/staff_labour_history_entity.dart';
@@ -308,6 +310,18 @@ class JsonConvert {
     if (<LabourRequestListRows>[] is M) {
       return data.map<LabourRequestListRows>((Map<String, dynamic> e) => LabourRequestListRows.fromJson(e)).toList() as M;
     }
+    if (<LabourRequestSGAddIndexEntity>[] is M) {
+      return data.map<LabourRequestSGAddIndexEntity>((Map<String, dynamic> e) => LabourRequestSGAddIndexEntity.fromJson(e)).toList() as M;
+    }
+    if (<LabourRequestSGAddIndexOption>[] is M) {
+      return data.map<LabourRequestSGAddIndexOption>((Map<String, dynamic> e) => LabourRequestSGAddIndexOption.fromJson(e)).toList() as M;
+    }
+    if (<LabourRequestSGEntity>[] is M) {
+      return data.map<LabourRequestSGEntity>((Map<String, dynamic> e) => LabourRequestSGEntity.fromJson(e)).toList() as M;
+    }
+    if (<LabourRequestSGCountList>[] is M) {
+      return data.map<LabourRequestSGCountList>((Map<String, dynamic> e) => LabourRequestSGCountList.fromJson(e)).toList() as M;
+    }
     if (<LabourRequestWorkFlowEntity>[] is M) {
       return data.map<LabourRequestWorkFlowEntity>((Map<String, dynamic> e) => LabourRequestWorkFlowEntity.fromJson(e)).toList() as M;
     }
@@ -414,6 +428,10 @@ class JsonConvertClassCollection {
     (LabourRequestIndexStatusList).toString(): LabourRequestIndexStatusList.fromJson,
     (LabourRequestListEntity).toString(): LabourRequestListEntity.fromJson,
     (LabourRequestListRows).toString(): LabourRequestListRows.fromJson,
+    (LabourRequestSGAddIndexEntity).toString(): LabourRequestSGAddIndexEntity.fromJson,
+    (LabourRequestSGAddIndexOption).toString(): LabourRequestSGAddIndexOption.fromJson,
+    (LabourRequestSGEntity).toString(): LabourRequestSGEntity.fromJson,
+    (LabourRequestSGCountList).toString(): LabourRequestSGCountList.fromJson,
     (LabourRequestWorkFlowEntity).toString(): LabourRequestWorkFlowEntity.fromJson,
     (LabourRequestWorkFlowRecords).toString(): LabourRequestWorkFlowRecords.fromJson,
     (StaffDetailEntity).toString(): StaffDetailEntity.fromJson,

+ 81 - 0
packages/cs_domain/lib/generated/json/labour_request_s_g_add_index_entity.g.dart

@@ -0,0 +1,81 @@
+import 'package:domain/generated/json/base/json_convert_content.dart';
+import 'package:domain/entity/response/labour_request_s_g_add_index_entity.dart';
+
+LabourRequestSGAddIndexEntity $LabourRequestSGAddIndexEntityFromJson(Map<String, dynamic> json) {
+  final LabourRequestSGAddIndexEntity labourRequestSGAddIndexEntity = LabourRequestSGAddIndexEntity();
+  final List<LabourRequestSGAddIndexOption>? titleList = (json['title_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<LabourRequestSGAddIndexOption>(e) as LabourRequestSGAddIndexOption).toList();
+  if (titleList != null) {
+    labourRequestSGAddIndexEntity.titleList = titleList;
+  }
+  final List<LabourRequestSGAddIndexOption>? outletList = (json['outlet_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<LabourRequestSGAddIndexOption>(e) as LabourRequestSGAddIndexOption).toList();
+  if (outletList != null) {
+    labourRequestSGAddIndexEntity.outletList = outletList;
+  }
+  final List<LabourRequestSGAddIndexOption>? requestType = (json['request_type'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<LabourRequestSGAddIndexOption>(e) as LabourRequestSGAddIndexOption).toList();
+  if (requestType != null) {
+    labourRequestSGAddIndexEntity.requestType = requestType;
+  }
+  return labourRequestSGAddIndexEntity;
+}
+
+Map<String, dynamic> $LabourRequestSGAddIndexEntityToJson(LabourRequestSGAddIndexEntity 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['request_type'] = entity.requestType.map((v) => v.toJson()).toList();
+  return data;
+}
+
+extension LabourRequestSGAddIndexEntityExtension on LabourRequestSGAddIndexEntity {
+  LabourRequestSGAddIndexEntity copyWith({
+    List<LabourRequestSGAddIndexOption>? titleList,
+    List<LabourRequestSGAddIndexOption>? outletList,
+    List<LabourRequestSGAddIndexOption>? requestType,
+  }) {
+    return LabourRequestSGAddIndexEntity()
+      ..titleList = titleList ?? this.titleList
+      ..outletList = outletList ?? this.outletList
+      ..requestType = requestType ?? this.requestType;
+  }
+}
+
+LabourRequestSGAddIndexOption $LabourRequestSGAddIndexOptionFromJson(Map<String, dynamic> json) {
+  final LabourRequestSGAddIndexOption labourRequestSGAddIndexOption = LabourRequestSGAddIndexOption();
+  final String? value = jsonConvert.convert<String>(json['value']);
+  if (value != null) {
+    labourRequestSGAddIndexOption.value = value;
+  }
+  final String? txt = jsonConvert.convert<String>(json['txt']);
+  if (txt != null) {
+    labourRequestSGAddIndexOption.txt = txt;
+  }
+  final String? selected = jsonConvert.convert<String>(json['selected']);
+  if (selected != null) {
+    labourRequestSGAddIndexOption.selected = selected;
+  }
+  return labourRequestSGAddIndexOption;
+}
+
+Map<String, dynamic> $LabourRequestSGAddIndexOptionToJson(LabourRequestSGAddIndexOption entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['value'] = entity.value;
+  data['txt'] = entity.txt;
+  data['selected'] = entity.selected;
+  return data;
+}
+
+extension LabourRequestSGAddIndexOptionExtension on LabourRequestSGAddIndexOption {
+  LabourRequestSGAddIndexOption copyWith({
+    String? value,
+    String? txt,
+    String? selected,
+  }) {
+    return LabourRequestSGAddIndexOption()
+      ..value = value ?? this.value
+      ..txt = txt ?? this.txt
+      ..selected = selected ?? this.selected;
+  }
+}

+ 79 - 0
packages/cs_domain/lib/generated/json/labour_request_s_g_entity.g.dart

@@ -0,0 +1,79 @@
+import 'package:domain/generated/json/base/json_convert_content.dart';
+import 'package:domain/entity/response/labour_request_s_g_entity.dart';
+
+LabourRequestSGEntity $LabourRequestSGEntityFromJson(Map<String, dynamic> json) {
+  final LabourRequestSGEntity labourRequestSGEntity = LabourRequestSGEntity();
+  final List<LabourRequestSGCountList>? countList = (json['count_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<LabourRequestSGCountList>(e) as LabourRequestSGCountList).toList();
+  if (countList != null) {
+    labourRequestSGEntity.countList = countList;
+  }
+  return labourRequestSGEntity;
+}
+
+Map<String, dynamic> $LabourRequestSGEntityToJson(LabourRequestSGEntity entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['count_list'] = entity.countList.map((v) => v.toJson()).toList();
+  return data;
+}
+
+extension LabourRequestSGEntityExtension on LabourRequestSGEntity {
+  LabourRequestSGEntity copyWith({
+    List<LabourRequestSGCountList>? countList,
+  }) {
+    return LabourRequestSGEntity()
+      ..countList = countList ?? this.countList;
+  }
+}
+
+LabourRequestSGCountList $LabourRequestSGCountListFromJson(Map<String, dynamic> json) {
+  final LabourRequestSGCountList labourRequestSGCountList = LabourRequestSGCountList();
+  final String? date = jsonConvert.convert<String>(json['date']);
+  if (date != null) {
+    labourRequestSGCountList.date = date;
+  }
+  final String? week = jsonConvert.convert<String>(json['week']);
+  if (week != null) {
+    labourRequestSGCountList.week = week;
+  }
+  final bool? today = jsonConvert.convert<bool>(json['today']);
+  if (today != null) {
+    labourRequestSGCountList.today = today;
+  }
+  final int? realNum = jsonConvert.convert<int>(json['real_num']);
+  if (realNum != null) {
+    labourRequestSGCountList.realNum = realNum;
+  }
+  final int? hiringNum = jsonConvert.convert<int>(json['hiring_num']);
+  if (hiringNum != null) {
+    labourRequestSGCountList.hiringNum = hiringNum;
+  }
+  return labourRequestSGCountList;
+}
+
+Map<String, dynamic> $LabourRequestSGCountListToJson(LabourRequestSGCountList entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['date'] = entity.date;
+  data['week'] = entity.week;
+  data['today'] = entity.today;
+  data['real_num'] = entity.realNum;
+  data['hiring_num'] = entity.hiringNum;
+  return data;
+}
+
+extension LabourRequestSGCountListExtension on LabourRequestSGCountList {
+  LabourRequestSGCountList copyWith({
+    String? date,
+    String? week,
+    bool? today,
+    int? realNum,
+    int? hiringNum,
+  }) {
+    return LabourRequestSGCountList()
+      ..date = date ?? this.date
+      ..week = week ?? this.week
+      ..today = today ?? this.today
+      ..realNum = realNum ?? this.realNum
+      ..hiringNum = hiringNum ?? this.hiringNum;
+  }
+}

+ 121 - 3
packages/cs_domain/lib/repository/labour_sg_repository.dart

@@ -4,6 +4,8 @@ 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_request_s_g_add_index_entity.dart';
+import 'package:domain/entity/response/labour_request_s_g_entity.dart';
 import 'package:get/get.dart';
 import 'package:plugin_platform/dio_export.dart';
 import 'package:plugin_platform/http/http_provider.dart';
@@ -266,9 +268,9 @@ class LabourSGRepository extends GetxService {
 
   /// 根据ID获取主列表的Item数据,用于刷新Item
   Future<HttpResult<JobTemplateSGEntity>> fetchJobTemplateListByIds(
-      String? ids, {
-        CancelToken? cancelToken,
-      }) async {
+    String? ids, {
+    CancelToken? cancelToken,
+  }) async {
     //参数
     Map<String, String> params = {};
     params["cur_page"] = "1";
@@ -503,4 +505,120 @@ class LabourSGRepository extends GetxService {
     }
     return result.convert();
   }
+
+  /// 用工请求的首页数据
+  Future<HttpResult<LabourRequestSGEntity>> fetchLabourRequestMain(
+    String? date, {
+    CancelToken? cancelToken,
+  }) async {
+    Map<String, String> params = {};
+
+    if (Utils.isNotEmpty(date)) {
+      params["date"] = date ?? "";
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiLabourRequestMainSG,
+      params: params,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      final json = result.getDataJson();
+      var data = LabourRequestSGEntity.fromJson(json!);
+      //重新赋值data或list
+      return result.convert<LabourRequestSGEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 添加用工请求的选项数据
+  Future<HttpResult<LabourRequestSGAddIndexEntity>> fetchLabourRequestAddIndex({
+    CancelToken? cancelToken,
+  }) async {
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiLabourRequestAddIndexSG,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      final json = result.getDataJson();
+      var data = LabourRequestSGAddIndexEntity.fromJson(json!);
+      //重新赋值data或list
+      return result.convert<LabourRequestSGAddIndexEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 删除工作标题的提交
+  Future<HttpResult> submitLabourRequestAdd(
+    String? jobTitleId,
+    String? startTime,
+    String? endTime,
+    String? repeatStart,
+    String? repeatEnd,
+    String? outletId,
+    int sexLimit,
+    String? maleLimit,
+    String? femaleLimit,
+    String? needNum,
+    String? requestType,
+    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 ?? "";
+
+    params['sexLimit'] = sexLimit.toString();
+
+    if (sexLimit == 1) {
+      params['male_limit'] = maleLimit ?? "0";
+      params['female_limit'] = femaleLimit ?? "0";
+      params['need_num'] = (int.parse(maleLimit ?? "0") + int.parse(femaleLimit ?? "0")).toString();
+    } else {
+      params['need_num'] = needNum ?? "0";
+    }
+
+    if (Utils.isNotEmpty(repeatStart)) {
+      params['repeat_start'] = repeatStart ?? "";
+    }
+
+    if (Utils.isNotEmpty(repeatEnd)) {
+      params['repeat_end'] = repeatEnd ?? "";
+    }
+
+    if (Utils.isNotEmpty(requestType)) {
+      params['request_type'] = requestType ?? "";
+    }
+
+    if (Utils.isNotEmpty(remark)) {
+      params['remark'] = remark ?? "";
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiLabourRequestAddSubmitSG,
+      method: HttpMethod.POST,
+      params: params,
+      networkDebounce: true,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      return result.convert();
+    }
+    return result.convert();
+  }
 }

BIN
packages/cs_resources/assets/cpt_job/pick_date_icon.png


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

@@ -32,6 +32,7 @@ class Assets {
   static const String cptJobArrawDownIcon = 'assets/cpt_job/arraw_down_icon.webp';
   static const String cptJobArrawUpIcon = 'assets/cpt_job/arraw_up_icon.webp';
   static const String cptJobExportIcon = 'assets/cpt_job/export_icon.webp';
+  static const String cptJobPickDateIcon = 'assets/cpt_job/pick_date_icon.png';
   static const String cptJobSearchIcon = 'assets/cpt_job/search_icon.webp';
   static const String mainHomeAttendanceReview = 'assets/main/home_attendance_review.webp';
   static const String mainHomeDevices = 'assets/main/home_devices.webp';

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

@@ -181,6 +181,21 @@ const Map<String, String> en_US = {
   'Malay': 'Malay',
   'Tamil': 'Tamil',
   'Hindi': 'Hindi',
+  'Select Job Title': 'Select Job Title',
+  'Select Job Start Time': 'Select Job Start Time',
+  'Select Job End Time': 'Select Job End Time',
+  'Enter No. of Staff of The Corresponding Gender': 'Enter No. of Staff of The Corresponding Gender',
+  'Select Date': 'Select Date',
+  'Date': 'Date',
+  'Job Time': 'Job Time',
+  'Repeat': 'Repeat',
+  'Repeat Start Time': 'Repeat Start Time',
+  'Repeat End Time': 'Repeat End Time',
+  'Gender Unlimited': 'Gender Unlimited',
+  'Gender Limited': 'Gender Limited',
+  'Needs Num': 'Needs Num',
+  'Request Type': 'Request Type',
+  'Remark': 'Remark',
 
   //插件的国际化
   'Pull to refresh': 'Pull to refresh',

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

@@ -181,6 +181,21 @@ const Map<String, String> vi_VN = {
   'Malay': 'Tiếng Việt',
   'Tamil': 'Thái Lan',
   'Hindi': 'Tiếng Hindi',
+  'Select Job Title': 'Chọn vị trí',
+  'Select Job Start Time': 'Chọn thời gian bắt đầu công việc',
+  'Select Job End Time': 'Chọn thời gian kết thúc công việc',
+  'Enter No. of Staff of The Corresponding Gender': 'Nhập số lượng nhân viên cần giới tính tương ứng',
+  'Select Date': 'Chọn ngày',
+  'Date': 'Ngày',
+  'Job Time': 'Thời gian làm việc',
+  'Repeat': 'Lặp lại',
+  'Repeat Start Time': 'Lặp lại thời gian bắt đầu',
+  'Repeat End Time': 'Lặp lại thời gian kết thúc',
+  'Gender Unlimited': 'Giới tính Không giới hạn',
+  'Gender Limited': 'Giới tính giới hạn',
+  'Needs Num': 'Số lượng cần thiết',
+  'Request Type': 'Loại yêu cầu',
+  'Remark': 'Ghi chú',
 
   //插件的国际化
   "Pull to refresh": "Kéo để làm mới",

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

@@ -181,6 +181,21 @@ const Map<String, String> zh_CN = {
   'Malay': '马来语',
   'Tamil': '泰米尔语',
   'Hindi': '印地语',
+  'Select Job Title': '请选择工作标题',
+  'Select Job Start Time': '请选择工作开始时间',
+  'Select Job End Time': '请选择工作结束时间',
+  'Enter No. of Staff of The Corresponding Gender': '输入需要对应性别的员工数量',
+  'Select Date': '选择日期',
+  'Date': '日期',
+  'Job Time': '工作时间',
+  'Repeat': '重复',
+  'Repeat Start Time': '重复的开始时间',
+  'Repeat End Time': '重复的结束时间',
+  'Gender Unlimited': '不限制性别',
+  'Gender Limited': '限制性别',
+  'Needs Num': '需要的数量',
+  'Request Type': '请求类型',
+  'Remark': '备注',
 
   //插件的国际化
   'Pull to refresh': '下拉刷新',

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

@@ -39,6 +39,8 @@ class RouterPath {
   static const JOB_TITLE_LIST_SG = '/job/title/list/sg'; //模板的工作标题列表
   static const JOB_TEMPLATE_LIST_SG = '/job/template/list/sg'; //模板列表
   static const JOB_TEMPLATE_ADD_SG = '/job/template/add/sg'; //模板添加
+  static const LABOUR_REQUEST_SG = '/labour/request/sg'; //用工首页
+  static const LABOUR_REQUEST_ADD_SG = '/labour/request/add/sg'; //用工请求添加
 
   //新加坡的工作列表
   static const JOB_LIST_SG = '/job/list/sg'; //工作列表(新加坡)