Explorar el Código

泰国的OA的LabourRequest

liukai hace 1 semana
padre
commit
706cefeb2f
Se han modificado 39 ficheros con 2946 adiciones y 613 borrados
  1. 0 247
      packages/cpt_th/lib/modules/labour/labour_request_add/labour_need_number_widget.dart
  2. 193 77
      packages/cpt_th/lib/modules/labour/labour_request_add/labour_request_add_controller.dart
  3. 408 131
      packages/cpt_th/lib/modules/labour/labour_request_add/labour_request_add_page.dart
  4. 55 7
      packages/cpt_th/lib/modules/labour/labour_request_add/labour_request_add_state.dart
  5. 190 0
      packages/cpt_th/lib/modules/labour/labour_request_list/dialog/oa_attach_list_controller.dart
  6. 266 0
      packages/cpt_th/lib/modules/labour/labour_request_list/dialog/oa_attach_list_dialog.dart
  7. 97 30
      packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_filter.dart
  8. 132 60
      packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_item.dart
  9. 81 30
      packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_list_controller.dart
  10. 8 0
      packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_list_page.dart
  11. 4 1
      packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_list_state.dart
  12. 3 3
      packages/cpt_th/lib/modules/labour/labour_request_workflow/labour_request_workflow_controller.dart
  13. 2 2
      packages/cpt_th/lib/modules/labour/labour_request_workflow/labour_request_workflow_page.dart
  14. 1 0
      packages/cpt_th/lib/modules/labour_er/labour_request_er_add/labour_request_er_add_page.dart
  15. 1 1
      packages/cpt_th/lib/router/th_router.dart
  16. 38 0
      packages/cs_domain/lib/constants/api_constants.dart
  17. 3 0
      packages/cs_domain/lib/entity/response/labour_request_index_entity.dart
  18. 41 0
      packages/cs_domain/lib/entity/response/t_h_o_a_attachment_entity.dart
  19. 129 0
      packages/cs_domain/lib/entity/response/t_h_o_a_labour_detail_entity.dart
  20. 70 0
      packages/cs_domain/lib/entity/response/t_h_o_a_labour_table_entity.dart
  21. 20 0
      packages/cs_domain/lib/entity/response/t_h_upload_file_entity.dart
  22. 32 0
      packages/cs_domain/lib/generated/json/base/json_convert_content.dart
  23. 11 1
      packages/cs_domain/lib/generated/json/labour_request_index_entity.g.dart
  24. 72 0
      packages/cs_domain/lib/generated/json/t_h_o_a_attachment_entity.g.dart
  25. 386 0
      packages/cs_domain/lib/generated/json/t_h_o_a_labour_detail_entity.g.dart
  26. 177 0
      packages/cs_domain/lib/generated/json/t_h_o_a_labour_table_entity.g.dart
  27. 26 0
      packages/cs_domain/lib/generated/json/t_h_upload_file_entity.g.dart
  28. 11 15
      packages/cs_domain/lib/repository/th_er_repository.dart
  29. 465 0
      packages/cs_domain/lib/repository/th_oa_repository.dart
  30. 2 4
      packages/cs_initializer/lib/global_services_injection.dart
  31. 9 1
      packages/cs_plugin_platform/lib/engine/network/network_engine.dart
  32. 4 1
      packages/cs_plugin_platform/pubspec.yaml
  33. BIN
      packages/cs_resources/assets/base_service/red_delete_icon.webp
  34. 1 0
      packages/cs_resources/lib/constants/color_constants.dart
  35. 1 0
      packages/cs_resources/lib/generated/assets.dart
  36. 2 0
      packages/cs_resources/lib/local/language/en_US.dart
  37. 2 0
      packages/cs_resources/lib/local/language/zh_CN.dart
  38. 1 0
      packages/cs_router/lib/path/router_path.dart
  39. 2 2
      packages/cs_widgets/lib/my_text_view.dart

+ 0 - 247
packages/cpt_th/lib/modules/labour/labour_request_add/labour_need_number_widget.dart

@@ -1,247 +0,0 @@
-import 'package:cs_resources/constants/color_constants.dart';
-import 'package:cs_resources/generated/assets.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
-import 'package:plugin_basic/basic_export.dart';
-import 'package:widgets/ext/ex_widget.dart';
-import 'package:widgets/my_load_image.dart';
-import 'package:widgets/my_text_view.dart';
-import 'package:widgets/picker/option_pick_util.dart';
-import 'package:widgets/shatter/form_require_text.dart';
-import 'package:widgets/shatter/round_my_text_field.dart';
-import 'package:domain/entity/agency_need_number.dart';
-
-/*
- * 需求数量的限制
- */
-class LabourNeedNumberWidget extends StatefulWidget {
-
-  int sexLimit = 0; //是否限制性别 0不限制  1限制
-  int needNum = 0;
-  int maleLimit = 0;
-  int femaleLimit = 0;
-
-  LabourNeedNumberWidget({
-    this.sexLimit = 0,
-    this.needNum = 0,
-    this.maleLimit = 0,
-    this.femaleLimit = 0,
-    Key? key,
-  }) : super(key: key ?? GlobalKey<AgencyNeedNumberState>()); // 使用新的 GlobalKey
-
-  @override
-  State<LabourNeedNumberWidget> createState() => AgencyNeedNumberState();
-}
-
-class AgencyNeedNumberState extends State<LabourNeedNumberWidget> {
-  int limitType = 0;
-  List<String> limitTypeList = [
-    "Gender Unlimited".tr,
-    "Gender Limited".tr,
-  ];
-
-  final Map<String, Map<String, dynamic>> formData = {
-    '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,
-    },
-  };
-
-  @override
-  void initState() {
-    super.initState();
-    limitType = 0;
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return Column(
-      crossAxisAlignment: CrossAxisAlignment.start,
-      children: [
-
-        FormRequireText(
-          text: "No. of Staff".tr,
-        ).marginOnly(top: 15),
-
-        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(
-                    limitTypeList[limitType],
-                    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();
-              pickLimitType();
-            }).expanded(flex: 55),
-
-            //输入框-不限制性别
-            Visibility(
-              visible: limitType == 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: formData,
-                textInputAction: TextInputAction.done,
-                onSubmit: (key, value) {
-                  FocusScope.of(context).unfocus();
-                },
-              ).expanded(flex: 55),
-            ),
-
-            //输入框组-限制性别
-            Visibility(
-              visible: limitType != 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: 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: 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),
-            ),
-          ],
-        ),
-      ],
-    );
-  }
-
-  //选择分类
-  void pickLimitType() {
-    OptionPickerUtil.showCupertinoOptionPicker(
-      items: limitTypeList,
-      initialSelectIndex: limitType,
-      onPickerChanged: (_, index) {
-        setState(() {
-          limitType = index;
-        });
-      },
-    );
-  }
-
-  // 返回 AgencyNeedNumberEntity 对象
-  AgencyNeedNumberEntity getAgencyNeedNumberEntity() {
-    AgencyNeedNumberEntity entity = AgencyNeedNumberEntity();
-    entity.sexLimit = limitType; // 设置性别限制
-    if (limitType == 0) {
-      // 不限制性别的总人数
-      entity.needNum = int.tryParse(formData['need_no']!['controller']!.text) ?? 0;
-    } else {
-      // 限制性别
-      entity.maleLimit = int.tryParse(formData['need_male']!['controller']!.text) ?? 0;
-      entity.femaleLimit = int.tryParse(formData['need_female']!['controller']!.text) ?? 0;
-    }
-    return entity;
-  }
-}

+ 193 - 77
packages/cpt_th/lib/modules/labour/labour_request_add/labour_request_add_controller.dart

@@ -1,66 +1,93 @@
-import 'package:domain/repository/labour_sg_repository.dart';
+import 'dart:io';
+import 'dart:typed_data';
+
+import 'package:domain/entity/response/t_h_o_a_labour_detail_entity.dart';
+import 'package:domain/repository/th_oa_repository.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:get/get.dart';
-import 'package:plugin_basic/constants/app_constant.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/event_bus.dart';
+import 'package:shared/utils/log_utils.dart';
 import 'package:shared/utils/util.dart';
 import 'package:widgets/picker/date_picker_util.dart';
 import 'package:widgets/picker/option_pick_util.dart';
-
+import 'package:file_picker/file_picker.dart';
 import 'labour_request_add_state.dart';
 
 class LabourRequestAddController extends GetxController with DioCancelableMixin {
-  final LabourSGRepository _labourRepository = Get.find();
+  final THOARepository _thRepository = Get.find();
   final LabourRequestAddState state = LabourRequestAddState();
 
   // 获取添加选项数据
   void fetchLabourRequestEditDetail() async {
-    var result = await _labourRepository.fetchLabourRequestEditDetail(
-      state.requestId,
-      cancelToken: cancelToken,
-    );
-
-    LoadingEngine.dismiss();
+    HttpResult<THOALabourDetailEntity> result;
+    if (Utils.isEmpty(state.requestId)) {
+      result = await _thRepository.fetchLabourRequestAddOption(
+        cancelToken: cancelToken,
+      );
+    } else {
+      result = await _thRepository.fetchLabourRequestEditDetail(
+        state.requestId,
+        cancelToken: cancelToken,
+      );
+    }
 
     if (result.isSuccess) {
       state.editDetailEntity = result.data;
-      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'];
-
-      int genderOptionType = 0; //使用哪一种类型限制
-      if (state.editDetailEntity?.labReq?.sexLimit == 1) {
-        state.genderOptionType = 1;
-        needMallController.text = state.editDetailEntity?.labReq?.maleLimit?.toString();
-        needFemaleController.text = state.editDetailEntity?.labReq?.femaleLimit?.toString();
-        needNumController.text = '';
-      } else {
-        state.genderOptionType = 0;
-        needMallController.text = '';
-        needFemaleController.text = '';
-        needNumController.text = state.editDetailEntity?.labReq?.hiringNum?.toString();
-      }
-
-      remarkController.text = state.editDetailEntity?.labReq?.description ?? '';
-
-      state.selectedStartTime = DateTimeUtils.getDateTime(state.editDetailEntity!.startTime!);
-      state.selectedEndTime = DateTimeUtils.getDateTime(state.editDetailEntity!.endTime!);
-
-      state.selectedOutlet = state.editDetailEntity?.labReq?.outletName;
-      state.selectedOutletId = state.editDetailEntity?.labReq?.outletId?.toString();
 
-      state.selectRequestTypeId = state.editDetailEntity?.labReq?.requestType?.toString();
-      state.selectPositionId = state.editDetailEntity?.labReq?.positionId?.toString();
-      state.selectPositionName = state.editDetailEntity?.labReq?.positionName?.toString();
+      if (state.editDetailEntity?.labReq != null) {
+        //有详情数据
+        var needMallController = state.formData['need_male']!['controller'];
+        var needFemaleController = state.formData['need_female']!['controller'];
+        var needNumController = state.formData['need_no']!['controller'];
+        var remarkController = state.formData['remark']!['controller'];
+        var eventController = state.formData['event']!['controller'];
+        var eventTypeController = state.formData['event_type']!['controller'];
+        var eventPaxController = state.formData['event_pax']!['controller'];
+        var revenueController = state.formData['revenue']!['controller'];
+        var positionController = state.formData['position']!['controller'];
+        var totalCostController = state.formData['total_cost']!['controller'];
+
+        if (state.editDetailEntity?.labReq?.sexLimit == 1) {
+          state.genderOptionType = 1;
+          needMallController.text = state.editDetailEntity?.labReq?.maleLimit?.toString();
+          needFemaleController.text = state.editDetailEntity?.labReq?.femaleLimit?.toString();
+          needNumController.text = '';
+        } else {
+          state.genderOptionType = 0;
+          needMallController.text = '';
+          needFemaleController.text = '';
+          needNumController.text = state.editDetailEntity?.labReq?.hiringNum?.toString();
+        }
+
+        remarkController.text = state.editDetailEntity?.labReq?.description ?? '';
+        eventController.text = state.editDetailEntity?.labReq?.eventName ?? "";
+        eventTypeController.text = state.editDetailEntity?.labReq?.eventType ?? "";
+        eventPaxController.text = state.editDetailEntity?.labReq?.passengers ?? "";
+        revenueController.text = state.editDetailEntity?.labReq?.estRevenue ?? "";
+        positionController.text = state.editDetailEntity?.labReq?.position ?? "";
+        totalCostController.text = state.editDetailEntity?.labReq?.estCost ?? "";
+
+        state.selectedStartTime = DateTimeUtils.getDateTime(state.editDetailEntity!.startTime!);
+        state.selectedEndTime = DateTimeUtils.getDateTime(state.editDetailEntity!.endTime!);
+
+        state.selectedOutlet = state.editDetailEntity?.labReq?.outletName;
+        state.selectedOutletId = state.editDetailEntity?.labReq?.outletId?.toString();
+
+        state.selectedJobTitleId = state.editDetailEntity?.labReq?.jobTitleId?.toString();
+        state.selectedJobTitleName = state.editDetailEntity?.labReq?.jobTitle;
+
+        state.selectedTypeId = state.editDetailEntity?.labReq?.employmentType?.toString();
+        state.selectedType = state.editDetailEntity?.employmentList.firstWhereOrNull((element) => element.value == state.selectedTypeId)?.txt;
+
+        state.attFilePath = state.editDetailEntity?.labReq?.attUrl;
 
-      update();
+        update();
+      }
     } else {
       ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
     }
@@ -74,10 +101,6 @@ class LabourRequestAddController extends GetxController with DioCancelableMixin
 
   //选择开始时间
   void pickStartTime() {
-    // if (state.editDetailEntity == null) {
-    //   return;
-    // }
-
     DatePickerUtil.showCupertinoDatePicker(
       selectedDateTime: state.selectedStartTime,
       mode: CupertinoDatePickerMode.dateAndTime,
@@ -91,10 +114,6 @@ class LabourRequestAddController extends GetxController with DioCancelableMixin
 
   // 选择结束时间
   void pickEndTime() {
-    if (state.editDetailEntity == null) {
-      return;
-    }
-
     DatePickerUtil.showCupertinoDatePicker(
       selectedDateTime: state.selectedEndTime ?? state.selectedStartTime,
       mode: CupertinoDatePickerMode.dateAndTime,
@@ -116,7 +135,7 @@ class LabourRequestAddController extends GetxController with DioCancelableMixin
     if (state.selectedOutletId == null) {
       selectedIndex = 0;
     } else {
-      selectedIndex = state.editDetailEntity!.outletList.indexWhere((department) => department.value.toString() == state.selectedOutletId);
+      selectedIndex = state.editDetailEntity!.outletList.indexWhere((entity) => entity.value.toString() == state.selectedOutletId);
     }
 
     if (selectedIndex < 0) {
@@ -140,11 +159,23 @@ class LabourRequestAddController extends GetxController with DioCancelableMixin
     var femaleNoController = state.formData['need_female']!['controller'];
     var needNoController = state.formData['need_no']!['controller'];
     var remarkController = state.formData['remark']!['controller'];
+    var eventController = state.formData['event']!['controller'];
+    var eventTypeController = state.formData['event_type']!['controller'];
+    var eventPaxController = state.formData['event_pax']!['controller'];
+    var revenueController = state.formData['revenue']!['controller'];
+    var positionController = state.formData['position']!['controller'];
+    var totalCostController = state.formData['total_cost']!['controller'];
 
     String maleNo = maleNoController.text.toString();
     String femaleNo = femaleNoController.text.toString();
     String needNo = needNoController.text.toString();
     String remark = remarkController.text.toString();
+    String event = eventController.text.toString();
+    String eventType = eventTypeController.text.toString();
+    String eventPax = eventPaxController.text.toString();
+    String revenue = revenueController.text.toString();
+    String position = positionController.text.toString();
+    String totalCost = totalCostController.text.toString();
 
     if (state.selectedStartTime == null) {
       ToastEngine.show("Select Job Start Time".tr);
@@ -173,43 +204,53 @@ class LabourRequestAddController extends GetxController with DioCancelableMixin
       }
     }
 
-    if (state.selectRequestTypeId == "1") {
-      if (Utils.isEmpty(state.selectPositionId)) {
-        ToastEngine.show("Select Position".tr);
-        return;
-      }
-    }
-
     HttpResult result;
-    if (state.pageType == 1) {
-      result = await _labourRepository.editLabourRequestSubmit(
+    if (state.pageType == 1 && Utils.isNotEmpty(state.requestId)) {
+      //编辑
+      result = await _thRepository.editLabourRequestSubmit(
         requestId: state.requestId,
+        jobTitleId: state.selectedJobTitleId,
         startTime: DateTimeUtils.formatDate(state.selectedStartTime),
         endTime: DateTimeUtils.formatDate(state.selectedEndTime),
+        repeatStart: DateTimeUtils.formatDate(state.selectedRepeatStartTime, format: "yyyy-MM-dd"),
+        repeatEnd: DateTimeUtils.formatDate(state.selectedRepeatEndTime, format: "yyyy-MM-dd"),
         outletId: state.selectedOutletId,
         sexLimit: state.genderOptionType,
         maleLimit: maleNo,
         femaleLimit: femaleNo,
         needNum: needNo,
-        requestType: state.selectRequestTypeId,
-        positionId: state.selectPositionId,
-        remark: remark,
+        description: remark,
+        employmentType: state.selectedTypeId,
+        eventName: event,
+        eventType: eventType,
+        passengers: eventPax,
+        estRevenue: revenue,
+        position: position,
+        estCost: totalCost,
         cancelToken: cancelToken,
       );
     } else {
-      // state.pageType == 3 这是 LabourRequestReview 的编辑提交
-      result = await _labourRepository.editLabourRequestReviewSubmit(
-        requestId: state.requestId,
+      //新增
+      result = await _thRepository.addLabourRequestSubmit(
+        jobTitleId: state.selectedJobTitleId,
         startTime: DateTimeUtils.formatDate(state.selectedStartTime),
         endTime: DateTimeUtils.formatDate(state.selectedEndTime),
+        repeatStart: DateTimeUtils.formatDate(state.selectedRepeatStartTime, format: "yyyy-MM-dd"),
+        repeatEnd: DateTimeUtils.formatDate(state.selectedRepeatEndTime, format: "yyyy-MM-dd"),
         outletId: state.selectedOutletId,
         sexLimit: state.genderOptionType,
         maleLimit: maleNo,
         femaleLimit: femaleNo,
         needNum: needNo,
-        requestType: state.selectRequestTypeId,
-        positionId: state.selectPositionId,
-        remark: remark,
+        description: remark,
+        employmentType: state.selectedTypeId,
+        eventName: event,
+        eventType: eventType,
+        passengers: eventPax,
+        estRevenue: revenue,
+        position: position,
+        estCost: totalCost,
+        attUrl: state.attFilePath,
         cancelToken: cancelToken,
       );
     }
@@ -227,17 +268,17 @@ class LabourRequestAddController extends GetxController with DioCancelableMixin
     }
   }
 
-  //选择常规职位
-  void pickRegularPosition() {
+  //选择工作标题
+  void pickJobTitle() {
     if (state.editDetailEntity == null) {
       return;
     }
 
     int selectedIndex;
-    if (state.selectPositionId == null) {
+    if (state.selectedJobTitleId == null) {
       selectedIndex = 0;
     } else {
-      selectedIndex = state.editDetailEntity!.positionList.indexWhere((bean) => bean.value.toString() == state.selectPositionId);
+      selectedIndex = state.editDetailEntity!.titleList.indexWhere((bean) => bean.value.toString() == state.selectedJobTitleId);
     }
 
     if (selectedIndex < 0) {
@@ -246,15 +287,12 @@ class LabourRequestAddController extends GetxController with DioCancelableMixin
       selectedIndex++;
     }
 
-    List<String> list = [];
-    list.addAll(state.editDetailEntity!.positionList.map((e) => e.txt!).toList(growable: false));
-
     OptionPickerUtil.showCupertinoOptionPicker(
-      items: list,
+      items: state.editDetailEntity!.titleList.map((e) => e.txt!).toList(growable: false),
       initialSelectIndex: selectedIndex,
       onPickerChanged: (_, index) {
-        state.selectPositionId = state.editDetailEntity!.positionList[index].value!.toString();
-        state.selectPositionName = state.editDetailEntity!.positionList[index].txt!.toString();
+        state.selectedJobTitleId = state.editDetailEntity!.titleList[index].value!.toString();
+        state.selectedJobTitleName = state.editDetailEntity!.titleList[index].txt!.toString();
         update();
       },
     );
@@ -271,4 +309,82 @@ class LabourRequestAddController extends GetxController with DioCancelableMixin
       },
     );
   }
+
+  void pickRepeatStartTime() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.selectedRepeatStartTime,
+      mode: CupertinoDatePickerMode.date,
+      onDateTimeChanged: (date) {
+        state.selectedRepeatStartTime = date;
+        update();
+      },
+      title: "Repeat Start Time".tr,
+    );
+  }
+
+  void pickRepeatEndTime() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: state.selectedRepeatEndTime ?? state.selectedRepeatStartTime,
+      mode: CupertinoDatePickerMode.date,
+      minimumDate: state.selectedRepeatStartTime,
+      onDateTimeChanged: (date) {
+        state.selectedRepeatEndTime = date;
+        update();
+      },
+      title: "Repeat End Time".tr,
+    );
+  }
+
+  //选择类型
+  void pickType() {
+    if (state.editDetailEntity == null) {
+      return;
+    }
+
+    int selectedIndex;
+    if (state.selectedTypeId == null) {
+      selectedIndex = 0;
+    } else {
+      selectedIndex = state.editDetailEntity!.employmentList.indexWhere((bean) => bean.value.toString() == state.selectedTypeId);
+    }
+
+    if (selectedIndex < 0) {
+      selectedIndex = 0;
+    } else {
+      selectedIndex++;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: state.editDetailEntity!.employmentList.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedIndex,
+      onPickerChanged: (_, index) {
+        state.selectedTypeId = state.editDetailEntity!.employmentList[index].value!.toString();
+        state.selectedType = state.editDetailEntity!.employmentList[index].txt!.toString();
+        update();
+      },
+    );
+  }
+
+  // 选择单个文件
+  void pickAttFile() async {
+    FilePickerResult? result = await FilePicker.platform.pickFiles(
+      allowMultiple: false,
+      type: FileType.custom,
+      allowedExtensions: ['jpg', 'png', 'jpeg', 'pdf', 'docx'],
+    );
+
+    if (result != null) {
+      PlatformFile file = result.files.first;
+      Log.d("path:${file.path} name:${file.name}");
+
+      final fileResult = await _thRepository.uploadFile(file.path);
+
+      if (fileResult.isSuccess) {
+        state.attFilePath = fileResult.data?.path;
+        update();
+      } else {
+        ToastEngine.show(fileResult.errorMsg ?? "Network Load Error".tr);
+      }
+    }
+  }
 }

+ 408 - 131
packages/cpt_th/lib/modules/labour/labour_request_add/labour_request_add_page.dart

@@ -11,18 +11,16 @@ import 'package:plugin_basic/utils/ext_get_nav.dart';
 import 'package:router/path/router_path.dart';
 import 'package:shared/utils/date_time_utils.dart';
 import 'package:shared/utils/screen_util.dart';
+import 'package:shared/utils/util.dart';
 import 'package:widgets/ext/ex_widget.dart';
 import 'package:widgets/my_appbar.dart';
 import 'package:widgets/my_button.dart';
 import 'package:widgets/my_load_image.dart';
 import 'package:widgets/my_text_view.dart';
 import 'package:widgets/no_shadow_scroll_behavior.dart';
-import 'package:widgets/shatter/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_need_number_widget.dart';
 import 'labour_request_add_controller.dart';
 import 'labour_request_add_state.dart';
 
@@ -30,8 +28,8 @@ class LabourRequestAddPage extends BaseStatefulPage<LabourRequestAddController>
   LabourRequestAddPage({Key? key}) : super(key: key);
 
   //启动当前页面,pageType 0 是新增  1是编辑  2是详情
-  static void startInstance(int pageType, String? appliedId) {
-    return Get.start(RouterPath.THLabourRequestAddOA, arguments: {'pageType': pageType, 'appliedId': appliedId});
+  static void startInstance(int pageType, String? appliedId, void Function(dynamic value)? cb) {
+    return Get.start(RouterPath.THLabourRequestAddOA, arguments: {'pageType': pageType, 'appliedId': appliedId, 'cb': cb});
   }
 
   @override
@@ -52,6 +50,7 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
     state = controller.state;
     state.pageType = Get.arguments['pageType'];
     state.requestId = Get.arguments['appliedId'];
+    state.cb = Get.arguments['cb'] as void Function(dynamic)?;
   }
 
   @override
@@ -59,7 +58,7 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
     return autoCtlGetBuilder(builder: (controller) {
       return Scaffold(
         extendBodyBehindAppBar: true,
-        appBar: MyAppBar.appBar(context, state.pageType == 1 || state.pageType == 3 ? "Edit Labour Requisition".tr : "Labour Requisition".tr),
+        appBar: MyAppBar.appBar(context, state.pageType == 1 ? "Edit Labour Requisition".tr : "Labour Requisition".tr),
         body: SafeArea(
           bottom: MediaQuery.of(context).padding.bottom > 38,
           top: false,
@@ -87,7 +86,6 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                   child: Column(
                     crossAxisAlignment: CrossAxisAlignment.start,
                     children: [
-
                       //工作标题,选择模板
                       FormRequireText(
                         text: "Job Title".tr,
@@ -99,7 +97,7 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                         margin: const EdgeInsets.only(top: 10),
                         height: 45,
                         decoration: BoxDecoration(
-                          color: const Color(0xFF4DCFF6).withOpacity(0.5),
+                          color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
                           borderRadius: const BorderRadius.all(Radius.circular(5)),
                         ),
                         child: Row(
@@ -108,15 +106,25 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                           mainAxisAlignment: MainAxisAlignment.start,
                           children: [
                             MyTextView(
-                              state.editDetailEntity?.labReq?.jobTitle ?? "",
+                              state.selectedJobTitleName ?? "",
                               fontSize: 14,
+                              hint: "Choose Job Title".tr,
                               textHintColor: ColorConstants.textGrayAECAE5,
                               isFontMedium: true,
                               textColor: ColorConstants.white,
                             ).expanded(),
+
+                            //下拉选图标
+                            Visibility(
+                              visible: state.pageType != 2,
+                              child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                            ),
                           ],
                         ),
-                      ),
+                      ).onTap(() {
+                        FocusScope.of(context).unfocus();
+                        if (state.pageType != 2) controller.pickJobTitle();
+                      }),
 
                       //选择工作时间
                       FormRequireText(
@@ -134,7 +142,7 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                               padding: const EdgeInsets.only(left: 16, right: 10),
                               height: 45,
                               decoration: BoxDecoration(
-                                color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 || state.pageType == 4 ? 0.5 : 0.2),
+                                color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
                                 borderRadius: const BorderRadius.all(Radius.circular(5)),
                               ),
                               child: Row(
@@ -152,14 +160,14 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                                   ).expanded(),
                                   //下拉选图标
                                   Visibility(
-                                    visible: state.pageType == 1 || state.pageType == 3,
+                                    visible: state.pageType != 2,
                                     child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
                                   ),
                                 ],
                               ),
                             ).onTap(() {
                               FocusScope.of(context).unfocus();
-                              if (state.pageType == 1 || state.pageType == 3) controller.pickStartTime();
+                              if (state.pageType != 2) controller.pickStartTime();
                             }),
                           ),
 
@@ -170,7 +178,7 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                               margin: const EdgeInsets.only(left: 10),
                               height: 45,
                               decoration: BoxDecoration(
-                                color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 || state.pageType == 4 ? 0.5 : 0.2),
+                                color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
                                 borderRadius: const BorderRadius.all(Radius.circular(5)),
                               ),
                               child: Row(
@@ -188,19 +196,108 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                                   ).expanded(),
                                   //下拉选图标
                                   Visibility(
-                                    visible: state.pageType == 1 || state.pageType == 3,
+                                    visible: state.pageType != 2,
                                     child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
                                   ),
                                 ],
                               ),
                             ).onTap(() {
                               FocusScope.of(context).unfocus();
-                              if (state.pageType == 1 || state.pageType == 3) controller.pickEndTime();
+                              if (state.pageType != 2) controller.pickEndTime();
                             }),
                           ),
                         ],
                       ).marginOnly(top: 10),
 
+                      //选择重复时间
+                      Visibility(
+                        visible: state.pageType == 0,
+                        child: MyTextView("Repeat".tr, fontSize: 15, textColor: Colors.white, isFontRegular: true, marginTop: 15),
+                      ),
+
+                      Visibility(
+                        visible: state.pageType == 0,
+                        child: Row(
+                          mainAxisSize: MainAxisSize.max,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          mainAxisAlignment: MainAxisAlignment.start,
+                          children: [
+                            //开始时间
+                            Expanded(
+                              child: Container(
+                                padding: const EdgeInsets.only(left: 16, right: 10),
+                                height: 45,
+                                decoration: BoxDecoration(
+                                  color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                                  borderRadius: const BorderRadius.all(Radius.circular(5)),
+                                ),
+                                child: Row(
+                                  mainAxisSize: MainAxisSize.max,
+                                  crossAxisAlignment: CrossAxisAlignment.center,
+                                  mainAxisAlignment: MainAxisAlignment.start,
+                                  children: [
+                                    MyTextView(
+                                      state.selectedRepeatStartTime == null
+                                          ? ""
+                                          : DateTimeUtils.formatDate(state.selectedRepeatStartTime, format: "yyyy-MM-dd"),
+                                      fontSize: 14,
+                                      hint: "Repeat Start Time".tr,
+                                      textHintColor: ColorConstants.textGrayAECAE5,
+                                      isFontMedium: true,
+                                      textColor: ColorConstants.white,
+                                    ).expanded(),
+                                    //下拉选图标
+                                    Visibility(
+                                      visible: state.pageType != 2,
+                                      child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                                    ),
+                                  ],
+                                ),
+                              ).onTap(() {
+                                FocusScope.of(context).unfocus();
+                                if (state.pageType != 2) controller.pickRepeatStartTime();
+                              }),
+                            ),
+
+                            //结束时间
+                            Expanded(
+                              child: Container(
+                                padding: const EdgeInsets.only(left: 16, right: 10),
+                                margin: const EdgeInsets.only(left: 10),
+                                height: 45,
+                                decoration: BoxDecoration(
+                                  color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                                  borderRadius: const BorderRadius.all(Radius.circular(5)),
+                                ),
+                                child: Row(
+                                  mainAxisSize: MainAxisSize.max,
+                                  crossAxisAlignment: CrossAxisAlignment.center,
+                                  mainAxisAlignment: MainAxisAlignment.start,
+                                  children: [
+                                    MyTextView(
+                                      state.selectedRepeatEndTime == null ? "" : DateTimeUtils.formatDate(state.selectedRepeatEndTime, format: "yyyy-MM-dd"),
+                                      fontSize: 14,
+                                      hint: "Repeat End Time".tr,
+                                      textHintColor: ColorConstants.textGrayAECAE5,
+                                      isFontMedium: true,
+                                      textColor: ColorConstants.white,
+                                    ).expanded(),
+                                    //下拉选图标
+                                    Visibility(
+                                      visible: state.pageType != 2,
+                                      child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                                    ),
+                                  ],
+                                ),
+                              ).onTap(() {
+                                FocusScope.of(context).unfocus();
+                                if (state.pageType != 2) controller.pickRepeatEndTime();
+                              }),
+                            ),
+                          ],
+                        ).marginOnly(top: 10),
+                      ),
+
                       //工作选择部门
                       FormRequireText(
                         text: "Outlet".tr,
@@ -212,7 +309,7 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                         margin: const EdgeInsets.only(top: 10),
                         height: 45,
                         decoration: BoxDecoration(
-                          color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 || state.pageType == 4 ? 0.5 : 0.2),
+                          color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
                           borderRadius: const BorderRadius.all(Radius.circular(5)),
                         ),
                         child: Row(
@@ -229,20 +326,20 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                               textColor: ColorConstants.white,
                             ).expanded(),
                             Visibility(
-                              visible: state.pageType == 1 || state.pageType == 3,
+                              visible: state.pageType != 2,
                               child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
                             ),
                           ],
                         ),
                       ).onTap(() {
                         FocusScope.of(context).unfocus();
-                        if (state.pageType == 1 || state.pageType == 3) controller.pickOutlet();
+                        if (state.pageType != 2) controller.pickOutlet();
                       }),
 
-
+                      //需求人数
                       FormRequireText(
                         text: "No. of Staff".tr,
-                      ).marginOnly(top: 15,bottom: 10),
+                      ).marginOnly(top: 15, bottom: 10),
 
                       Row(
                         children: [
@@ -251,7 +348,7 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                             margin: const EdgeInsets.only(right: 12),
                             height: 45,
                             decoration: BoxDecoration(
-                              color: const Color(0xFF4DCFF6).withOpacity(0.2),
+                              color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
                               borderRadius: const BorderRadius.all(Radius.circular(5)),
                             ),
                             child: Row(
@@ -267,12 +364,15 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                                   textColor: ColorConstants.white,
                                 ).expanded(),
                                 //下拉选图标
-                                const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                                Visibility(
+                                  visible: state.pageType != 2,
+                                  child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                                ),
                               ],
                             ),
                           ).onTap(() {
                             FocusScope.of(context).unfocus();
-                           controller.pickLimitType();
+                            if (state.pageType != 2) controller.pickLimitType();
                           }).expanded(flex: 55),
 
                           //输入框-不限制性别
@@ -285,7 +385,8 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                               paddingTop: 0,
                               paddingBottom: 0,
                               height: 45,
-                              fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(0.2),
+                              enabled: state.pageType != 2,
+                              fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
                               inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
                               textInputType: TextInputType.number,
                               formData: state.formData,
@@ -319,6 +420,7 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                                       paddingLeft: 10,
                                       paddingRight: 10,
                                       height: 45,
+                                      enabled: state.pageType != 2,
                                       cornerRadius: 0,
                                       fillBackgroundColor: Colors.transparent,
                                       inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
@@ -332,9 +434,9 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                                   ],
                                 )
                                     .decorated(
-                                  color: const Color(0xFF4DCFF6).withOpacity(0.2),
-                                  borderRadius: const BorderRadius.all(Radius.circular(5)),
-                                )
+                                      color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                                      borderRadius: const BorderRadius.all(Radius.circular(5)),
+                                    )
                                     .expanded(),
                                 const SizedBox(width: 12),
                                 Row(
@@ -355,6 +457,7 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                                       paddingLeft: 10,
                                       paddingRight: 10,
                                       height: 45,
+                                      enabled: state.pageType != 2,
                                       cornerRadius: 0,
                                       fillBackgroundColor: Colors.transparent,
                                       inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
@@ -368,9 +471,9 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                                   ],
                                 )
                                     .decorated(
-                                  color: const Color(0xFF4DCFF6).withOpacity(0.2),
-                                  borderRadius: const BorderRadius.all(Radius.circular(5)),
-                                )
+                                      color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                                      borderRadius: const BorderRadius.all(Radius.circular(5)),
+                                    )
                                     .expanded(),
                               ],
                             ).expanded(flex: 55),
@@ -378,102 +481,6 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                         ],
                       ),
 
-                      //需要的人数
-                      // FormRequireText(
-                      //   text: "No. of Staff".tr,
-                      // ).marginOnly(top: 15),
-                      //
-                      // //选择人数类型单选
-                      // CustomRadioCheck(
-                      //   options: state.genderOptions,
-                      //   enable: state.pageType == 1 || state.pageType == 3,
-                      //   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: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 || state.pageType == 4 ? 0.5 : 0.2),
-                      //     enabled: state.pageType == 1 || state.pageType == 3,
-                      //     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: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 || state.pageType == 4 ? 0.5 : 0.2),
-                      //         enabled: state.pageType == 1 || state.pageType == 3,
-                      //         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: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 || state.pageType == 4 ? 0.5 : 0.2),
-                      //         enabled: state.pageType == 1 || state.pageType == 3,
-                      //         inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
-                      //         textInputType: TextInputType.number,
-                      //         formData: state.formData,
-                      //         textInputAction: TextInputAction.done,
-                      //         onSubmit: (key, value) {
-                      //           FocusScope.of(context).unfocus();
-                      //         },
-                      //       ).expanded(),
-                      //     ],
-                      //   ).marginOnly(top: 10),
-                      // ),
-
                       //输入Remark
                       MyTextView(
                         "Remark".tr,
@@ -489,14 +496,14 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                           margin: const EdgeInsets.only(top: 10),
                           padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 15),
                           decoration: BoxDecoration(
-                            color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 || state.pageType == 4 ? 0.5 : 0.2),
+                            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 == 1 || state.pageType == 3,
+                            enabled: state.pageType == 0 || state.pageType == 1,
                             focusNode: state.formData["remark"]!['focusNode'],
                             controller: state.formData["remark"]!['controller'],
                             // 装饰
@@ -523,11 +530,280 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                             },
                           ),
                         ),
-                      ).marginOnly(bottom: 30),
+                      ),
+
+                      //OA的其他显示
+
+                      //Type
+                      MyTextView(
+                        "Type".tr,
+                        fontSize: 15,
+                        isFontRegular: true,
+                        textColor: Colors.white,
+                        marginTop: 15,
+                      ),
+
+                      //选择类型
+                      Container(
+                        padding: const EdgeInsets.only(left: 16, right: 10),
+                        margin: const EdgeInsets.only(top: 10),
+                        height: 45,
+                        decoration: BoxDecoration(
+                          color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                          borderRadius: const BorderRadius.all(Radius.circular(5)),
+                        ),
+                        child: Row(
+                          mainAxisSize: MainAxisSize.max,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          mainAxisAlignment: MainAxisAlignment.start,
+                          children: [
+                            MyTextView(
+                              state.selectedType ?? "",
+                              fontSize: 14,
+                              hint: "Choose Type".tr,
+                              textHintColor: ColorConstants.textGrayAECAE5,
+                              isFontMedium: true,
+                              textColor: ColorConstants.white,
+                            ).expanded(),
+
+                            //下拉选图标
+                            Visibility(
+                              visible: state.pageType != 2,
+                              child: const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                            ),
+                          ],
+                        ),
+                      ).onTap(() {
+                        FocusScope.of(context).unfocus();
+                        if (state.pageType != 2) controller.pickType();
+                      }),
+
+                      //Event
+                      MyTextView(
+                        "I.Event".tr,
+                        fontSize: 15,
+                        isFontRegular: true,
+                        textColor: Colors.white,
+                        marginTop: 15,
+                      ),
+
+                      CustomTextField(
+                        formKey: "event",
+                        marginLeft: 0,
+                        marginRight: 0,
+                        paddingTop: 0,
+                        paddingBottom: 0,
+                        marginTop: 10,
+                        height: 45,
+                        fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                        enabled: state.pageType != 2,
+                        textInputType: TextInputType.text,
+                        formData: state.formData,
+                        textInputAction: TextInputAction.next,
+                        onSubmit: (key, value) {
+                          state.formData[key]!['focusNode'].unfocus();
+                          FocusScope.of(context).requestFocus(state.formData['event_type']!['focusNode']);
+                        },
+                      ),
+
+                      //Event Type
+                      MyTextView(
+                        "II.Event Type".tr,
+                        fontSize: 15,
+                        isFontRegular: true,
+                        textColor: Colors.white,
+                        marginTop: 15,
+                      ),
+
+                      CustomTextField(
+                        formKey: "event_type",
+                        marginLeft: 0,
+                        marginRight: 0,
+                        paddingTop: 0,
+                        paddingBottom: 0,
+                        marginTop: 10,
+                        height: 45,
+                        fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                        enabled: state.pageType != 2,
+                        textInputType: TextInputType.text,
+                        formData: state.formData,
+                        textInputAction: TextInputAction.next,
+                        onSubmit: (key, value) {
+                          state.formData[key]!['focusNode'].unfocus();
+                          FocusScope.of(context).requestFocus(state.formData['event_pax']!['focusNode']);
+                        },
+                      ),
+
+                      //Event Pax
+                      MyTextView(
+                        "III.Event Pax".tr,
+                        fontSize: 15,
+                        isFontRegular: true,
+                        textColor: Colors.white,
+                        marginTop: 15,
+                      ),
+
+                      CustomTextField(
+                        formKey: "event_pax",
+                        marginLeft: 0,
+                        marginRight: 0,
+                        paddingTop: 0,
+                        paddingBottom: 0,
+                        marginTop: 10,
+                        height: 45,
+                        fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                        enabled: state.pageType != 2,
+                        textInputType: TextInputType.number,
+                        formData: state.formData,
+                        textInputAction: TextInputAction.next,
+                        onSubmit: (key, value) {
+                          state.formData[key]!['focusNode'].unfocus();
+                          FocusScope.of(context).requestFocus(state.formData['revenue']!['focusNode']);
+                        },
+                      ),
+
+                      //Revenue
+                      MyTextView(
+                        "IV.Estimated Revenue".tr,
+                        fontSize: 15,
+                        isFontRegular: true,
+                        textColor: Colors.white,
+                        marginTop: 15,
+                      ),
+
+                      CustomTextField(
+                        formKey: "revenue",
+                        marginLeft: 0,
+                        marginRight: 0,
+                        paddingTop: 0,
+                        paddingBottom: 0,
+                        marginTop: 10,
+                        height: 45,
+                        fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                        enabled: state.pageType != 2,
+                        textInputType: TextInputType.number,
+                        formData: state.formData,
+                        textInputAction: TextInputAction.next,
+                        onSubmit: (key, value) {
+                          state.formData[key]!['focusNode'].unfocus();
+                          FocusScope.of(context).requestFocus(state.formData['position']!['focusNode']);
+                        },
+                      ),
+
+                      //Position
+                      MyTextView(
+                        "V.Position".tr,
+                        fontSize: 15,
+                        isFontRegular: true,
+                        textColor: Colors.white,
+                        marginTop: 15,
+                      ),
+
+                      CustomTextField(
+                        formKey: "position",
+                        marginLeft: 0,
+                        marginRight: 0,
+                        paddingTop: 0,
+                        paddingBottom: 0,
+                        marginTop: 10,
+                        height: 45,
+                        fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                        enabled: state.pageType != 2,
+                        textInputType: TextInputType.text,
+                        formData: state.formData,
+                        textInputAction: TextInputAction.next,
+                        onSubmit: (key, value) {
+                          state.formData[key]!['focusNode'].unfocus();
+                          FocusScope.of(context).requestFocus(state.formData['total_cost']!['focusNode']);
+                        },
+                      ),
+
+                      //total_cost
+                      MyTextView(
+                        "VI.Estimated Total Cost".tr,
+                        fontSize: 15,
+                        isFontRegular: true,
+                        textColor: Colors.white,
+                        marginTop: 15,
+                      ),
+
+                      CustomTextField(
+                        formKey: "total_cost",
+                        marginLeft: 0,
+                        marginRight: 0,
+                        paddingTop: 0,
+                        paddingBottom: 0,
+                        marginTop: 10,
+                        height: 45,
+                        fillBackgroundColor: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                        enabled: state.pageType != 2,
+                        textInputType: TextInputType.number,
+                        formData: state.formData,
+                        textInputAction: TextInputAction.done,
+                        onSubmit: (key, value) {
+                          FocusScope.of(context).unfocus();
+                        },
+                      ),
+
+                      //附件文件
+                      Visibility(
+                        visible: state.pageType == 0,
+                        child: MyTextView(
+                          "Attachment".tr,
+                          fontSize: 15,
+                          isFontRegular: true,
+                          textColor: Colors.white,
+                          marginTop: 15,
+                        ),
+                      ),
+
+                      //选择类型
+                      Visibility(
+                          visible: state.pageType == 0,
+                          child: Container(
+                            padding: const EdgeInsets.only(left: 16),
+                            margin: const EdgeInsets.only(top: 10),
+                            height: 45,
+                            decoration: BoxDecoration(
+                              color: const Color(0xFF4DCFF6).withOpacity(state.pageType == 2 ? 0.5 : 0.2),
+                              borderRadius: const BorderRadius.all(Radius.circular(5)),
+                            ),
+                            child: Row(
+                              mainAxisSize: MainAxisSize.max,
+                              crossAxisAlignment: CrossAxisAlignment.center,
+                              mainAxisAlignment: MainAxisAlignment.start,
+                              children: [
+                                MyTextView(
+                                  Utils.isNotEmpty(state.attFilePath) ? Uri.parse(state.attFilePath!).pathSegments.last : "",
+                                  fontSize: 14,
+                                  hint: "Choose Attachment".tr,
+                                  textHintColor: ColorConstants.textGrayAECAE5,
+                                  isFontMedium: true,
+                                  textColor: ColorConstants.white,
+                                ).expanded(),
+
+                                //下拉选图标
+                                MyTextView(
+                                  'Upload'.tr,
+                                  boxHeight: 45,
+                                  textAlign: TextAlign.center,
+                                  boxWidth: 90,
+                                  cornerRadius: 5,
+                                  onClick: controller.pickAttFile,
+                                  textColor: Colors.white,
+                                  fontSize: 15,
+                                  fontWeight: FontWeight.w400,
+                                  backgroundColor: ColorConstants.textGreen0AC074,
+                                ),
+                              ],
+                            ),
+                          )),
+
+                      const SizedBox(height: 30),
 
                       //提交按钮
                       Visibility(
-                        visible: state.pageType == 1 || state.pageType == 3,
+                        visible: state.pageType == 1 || state.pageType == 0,
                         child: MyButton(
                           type: ClickType.throttle,
                           milliseconds: 500,
@@ -541,6 +817,7 @@ class _LabourRequestAddState extends BaseState<LabourRequestAddPage, LabourReque
                           radius: 22.5,
                           backgroundColor: hexToColor("#FFBB1B"),
                           fontWeight: FontWeight.w500,
+                          minHeight: 45,
                         ).marginOnly(bottom: 30),
                       ),
                     ],

+ 55 - 7
packages/cpt_th/lib/modules/labour/labour_request_add/labour_request_add_state.dart

@@ -1,4 +1,5 @@
 import 'package:domain/entity/response/s_g_labour_request_detail_entity.dart';
+import 'package:domain/entity/response/t_h_o_a_labour_detail_entity.dart';
 import 'package:flutter/material.dart';
 import 'package:plugin_basic/basic_export.dart';
 
@@ -34,26 +35,73 @@ class LabourRequestAddState {
       'hintText': 'Needs Num'.tr,
       'obsecure': false,
     },
+    'event': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter...'.tr,
+      'obsecure': false,
+    },
+    'event_type': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter...'.tr,
+      'obsecure': false,
+    },
+    'event_pax': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter...'.tr,
+      'obsecure': false,
+    },
+    'revenue': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter...'.tr,
+      'obsecure': false,
+    },
+    'position': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter...'.tr,
+      'obsecure': false,
+    },
+    'total_cost': {
+      'value': '',
+      'controller': TextEditingController(),
+      'focusNode': FocusNode(),
+      'hintText': 'Enter...'.tr,
+      'obsecure': false,
+    },
   };
 
-  int pageType = 2;  //pageType  1是编辑  2是详情  3是review编辑  4是review详情
+  int pageType = 0; // 0 是新增  1是编辑  2是详情
   String? requestId;  //编辑和详情需要用到ID
   void Function(dynamic value)? cb;
 
   List<String> genderOptions = ["Gender Unlimited".tr, "Gender Limited".tr];
   int genderOptionType = 0;  //使用哪一种类型限制
 
-  SGLabourRequestDetailEntity? editDetailEntity;
-
+  THOALabourDetailEntity? editDetailEntity;
 
   DateTime? selectedStartTime;
   DateTime? selectedEndTime;
 
-  String? selectedOutlet;
+  DateTime? selectedRepeatStartTime;
+  DateTime? selectedRepeatEndTime;
+
   String? selectedOutletId;
+  String? selectedOutlet;
+
+  String? selectedJobTitleId;
+  String? selectedJobTitleName;
 
-  String? selectRequestTypeId;
-  String? selectPositionId;
-  String? selectPositionName;
+  String? selectedTypeId;
+  String? selectedType;
 
+  String? attFilePath;
 }

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

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

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

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

+ 97 - 30
packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_filter.dart

@@ -22,12 +22,13 @@ import 'package:widgets/widget_export.dart';
  * 用工请求列表的筛选
  */
 class LabourRequestFilter extends StatefulWidget {
-  void Function(DateTime? selectedStartDate, DateTime? selectedEndDate, String? selectedStatusId, String? selectedDepartmentId)? onFilterAction;
+  void Function(DateTime? selectedStartDate, DateTime? selectedEndDate, String? selectedStatusId, String? selectedDepartmentId, String? selectedDivisionId)? onFilterAction;
   LabourRequestIndexEntity optionResult;
   DateTime? selectedStartDate;
   DateTime? selectedEndDate;
   String? selectedStatusId;
   String? selectedDepartmentId;
+  String? selectedDivisionId;
 
   LabourRequestFilter({
     required this.optionResult,
@@ -35,6 +36,7 @@ class LabourRequestFilter extends StatefulWidget {
     required this.selectedEndDate,
     required this.selectedStatusId,
     required this.selectedDepartmentId,
+    required this.selectedDivisionId,
     this.onFilterAction,
   });
 
@@ -47,6 +49,7 @@ class _LabourRequestFilterState extends State<LabourRequestFilter> {
   DateTime? selectedEndDate;
   String? selectedStatusId;
   String? selectedDepartmentId;
+  String? selectedDivisionId;
 
   @override
   void initState() {
@@ -55,6 +58,7 @@ class _LabourRequestFilterState extends State<LabourRequestFilter> {
     this.selectedEndDate = widget.selectedEndDate;
     this.selectedStatusId = widget.selectedStatusId;
     this.selectedDepartmentId = widget.selectedDepartmentId;
+    this.selectedDivisionId = widget.selectedDivisionId;
   }
 
   @override
@@ -67,30 +71,70 @@ class _LabourRequestFilterState extends State<LabourRequestFilter> {
           height: kToolbarHeight + ScreenUtil.getStatusBarH(context) + 1,
         ),
         Container(
-          padding: EdgeInsets.only(left: 15, right: 15, top: 17.5, bottom: 20),
+          padding: const EdgeInsets.only(left: 15, right: 15, top: 17.5, bottom: 20),
           width: double.infinity,
-          decoration: BoxDecoration(
+          decoration: const BoxDecoration(
             color: Colors.white,
           ),
           child: Column(
             crossAxisAlignment: CrossAxisAlignment.start,
             children: [
+              //Division
+              MyTextView(
+                "Division".tr,
+                fontSize: 14,
+                isFontMedium: true,
+                textColor: ColorConstants.black33,
+              ),
+
+              //选择部门
+              Container(
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
+                height: 45,
+                decoration: const BoxDecoration(
+                  color: ColorConstants.grayECECEC,
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      selectedDivisionId == null || selectedDivisionId == "0"
+                          ? ""
+                          : widget.optionResult.divisionList.firstWhere((element) => element.value.toString() == selectedDivisionId).txt!,
+                      hint: "Choose Outlet".tr,
+                      textHintColor: ColorConstants.textBlackHint,
+                      fontSize: 14,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black33,
+                    ).expanded(),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                  ],
+                ),
+              ).onTap(() {
+                pickerDivision();
+              }),
+
               //部门
               MyTextView(
                 "Outlet".tr,
                 fontSize: 14,
                 isFontMedium: true,
+                marginTop: 11,
                 textColor: ColorConstants.black33,
               ),
 
               //选择部门
               Container(
-                padding: EdgeInsets.only(left: 16, right: 10),
-                margin: EdgeInsets.only(top: 10),
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
                 height: 45,
-                decoration: BoxDecoration(
+                decoration: const BoxDecoration(
                   color: ColorConstants.grayECECEC,
-                  borderRadius: const BorderRadius.all(Radius.circular(5)),
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
                 ),
                 child: Row(
                   mainAxisSize: MainAxisSize.max,
@@ -100,14 +144,14 @@ class _LabourRequestFilterState extends State<LabourRequestFilter> {
                     MyTextView(
                       selectedDepartmentId == null || selectedDepartmentId == "0"
                           ? ""
-                          : widget.optionResult.departmentList!.firstWhere((element) => element.value.toString() == selectedDepartmentId).txt!,
+                          : widget.optionResult.outletList!.firstWhere((element) => element.value.toString() == selectedDepartmentId).txt!,
                       hint: "Choose Outlet".tr,
                       textHintColor: ColorConstants.textBlackHint,
                       fontSize: 14,
                       isFontMedium: true,
                       textColor: ColorConstants.black33,
                     ).expanded(),
-                    MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
                   ],
                 ),
               ).onTap(() {
@@ -125,12 +169,12 @@ class _LabourRequestFilterState extends State<LabourRequestFilter> {
 
               //选择状态
               Container(
-                padding: EdgeInsets.only(left: 16, right: 10),
-                margin: EdgeInsets.only(top: 10),
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
                 height: 45,
-                decoration: BoxDecoration(
+                decoration: const BoxDecoration(
                   color: ColorConstants.grayECECEC,
-                  borderRadius: const BorderRadius.all(Radius.circular(5)),
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
                 ),
                 child: Row(
                   mainAxisSize: MainAxisSize.max,
@@ -147,7 +191,7 @@ class _LabourRequestFilterState extends State<LabourRequestFilter> {
                       isFontMedium: true,
                       textColor: ColorConstants.black33,
                     ).expanded(),
-                    MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
                   ],
                 ),
               ).onTap(() {
@@ -165,12 +209,12 @@ class _LabourRequestFilterState extends State<LabourRequestFilter> {
 
               //选择时间
               Container(
-                padding: EdgeInsets.only(left: 16, right: 10),
-                margin: EdgeInsets.only(top: 10),
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
                 height: 45,
-                decoration: BoxDecoration(
+                decoration: const BoxDecoration(
                   color: ColorConstants.grayECECEC,
-                  borderRadius: const BorderRadius.all(Radius.circular(5)),
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
                 ),
                 child: Row(
                   mainAxisSize: MainAxisSize.max,
@@ -185,7 +229,7 @@ class _LabourRequestFilterState extends State<LabourRequestFilter> {
                       isFontMedium: true,
                       textColor: ColorConstants.black33,
                     ).expanded(),
-                    MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
                   ],
                 ),
               ).onTap(() {
@@ -203,12 +247,12 @@ class _LabourRequestFilterState extends State<LabourRequestFilter> {
 
               //选择结束日期
               Container(
-                padding: EdgeInsets.only(left: 16, right: 10),
-                margin: EdgeInsets.only(top: 10),
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
                 height: 45,
-                decoration: BoxDecoration(
+                decoration: const BoxDecoration(
                   color: ColorConstants.grayECECEC,
-                  borderRadius: const BorderRadius.all(Radius.circular(5)),
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
                 ),
                 child: Row(
                   mainAxisSize: MainAxisSize.max,
@@ -223,7 +267,7 @@ class _LabourRequestFilterState extends State<LabourRequestFilter> {
                       isFontMedium: true,
                       textColor: ColorConstants.black33,
                     ).expanded(),
-                    MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
                   ],
                 ),
               ).onTap(() {
@@ -255,11 +299,11 @@ class _LabourRequestFilterState extends State<LabourRequestFilter> {
                     minWidth: 60,
                     minHeight: 36,
                   ).expanded(),
-                  SizedBox(width: 15),
+                  const SizedBox(width: 15),
                   MyButton(
                     onPressed: () {
                       onCancel();
-                      widget.onFilterAction?.call(selectedStartDate, selectedEndDate, selectedStatusId, selectedDepartmentId);
+                      widget.onFilterAction?.call(selectedStartDate, selectedEndDate, selectedStatusId, selectedDepartmentId,selectedDivisionId);
                     },
                     text: "Filter".tr,
                     textColor: ColorConstants.white,
@@ -273,7 +317,7 @@ class _LabourRequestFilterState extends State<LabourRequestFilter> {
             ],
           ),
         ),
-        Center(child: MyAssetImage(Assets.baseServiceDialogDeleteIcon, width: 26.5, height: 26.5).marginOnly(top: 35)).onTap(() {
+        Center(child: const MyAssetImage(Assets.baseServiceDialogDeleteIcon, width: 26.5, height: 26.5).marginOnly(top: 35)).onTap(() {
           onCancel();
         }),
       ],
@@ -317,7 +361,7 @@ class _LabourRequestFilterState extends State<LabourRequestFilter> {
     if (selectedDepartmentId == null) {
       selectedDepartmentIndex = 0;
     } else {
-      selectedDepartmentIndex = widget.optionResult.departmentList!.indexWhere((department) => department.value.toString() == selectedDepartmentId);
+      selectedDepartmentIndex = widget.optionResult.outletList!.indexWhere((department) => department.value.toString() == selectedDepartmentId);
     }
 
     if (selectedDepartmentIndex < 0) {
@@ -325,11 +369,11 @@ class _LabourRequestFilterState extends State<LabourRequestFilter> {
     }
 
     OptionPickerUtil.showCupertinoOptionPicker(
-      items: widget.optionResult.departmentList!.map((e) => e.txt!).toList(growable: false),
+      items: widget.optionResult.outletList!.map((e) => e.txt!).toList(growable: false),
       initialSelectIndex: selectedDepartmentIndex,
       onPickerChanged: (_, index) {
         setState(() {
-          selectedDepartmentId = widget.optionResult.departmentList![index].value!.toString();
+          selectedDepartmentId = widget.optionResult.outletList![index].value!.toString();
         });
       },
     );
@@ -358,4 +402,27 @@ class _LabourRequestFilterState extends State<LabourRequestFilter> {
       },
     );
   }
+
+  void pickerDivision() {
+    int selectedDivisionIndex;
+    if (selectedDivisionId == null) {
+      selectedDivisionIndex = 0;
+    } else {
+      selectedDivisionIndex = widget.optionResult.divisionList.indexWhere((entity) => entity.value.toString() == selectedDivisionId);
+    }
+
+    if (selectedDivisionIndex < 0) {
+      selectedDivisionIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: widget.optionResult.divisionList.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedDivisionIndex,
+      onPickerChanged: (_, index) {
+        setState(() {
+          selectedDivisionId = widget.optionResult.divisionList[index].value!.toString();
+        });
+      },
+    );
+  }
 }

+ 132 - 60
packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_item.dart

@@ -1,40 +1,48 @@
 import 'package:cs_resources/constants/color_constants.dart';
-import 'package:domain/entity/response/labour_request_list_entity.dart';
+import 'package:domain/entity/response/t_h_o_a_labour_table_entity.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/widgets.dart';
 import 'package:plugin_basic/basic_export.dart';
+import 'package:shared/utils/util.dart';
 import 'package:widgets/ext/ex_widget.dart';
 import 'package:widgets/my_button.dart';
 import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
 
 /*
  * 用工请求的主页面列表Item
  */
 class LabourRequestItem extends StatelessWidget {
   final int index;
-  final LabourRequestListRows item;
+  final THOALabourTableRows item;
+  final int totalNum;
   final VoidCallback? onStatusAction;
   final VoidCallback? onRecallAction;
   final VoidCallback? onDetailAction;
   final VoidCallback? onEditAction;
+  final VoidCallback? onDeleteAction;
+  final VoidCallback? onAttAction;
 
   LabourRequestItem({
     required this.index,
     required this.item,
+    required this.totalNum,
     this.onStatusAction,
     this.onRecallAction,
     this.onDetailAction,
     this.onEditAction,
+    this.onDeleteAction,
+    this.onAttAction,
   });
 
   @override
   Widget build(BuildContext context) {
     return Container(
-      padding: EdgeInsets.symmetric(vertical: 23, horizontal: 21),
-      margin: EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 5),
+      padding: const EdgeInsets.symmetric(vertical: 23, horizontal: 21),
+      margin: const EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 5),
       decoration: BoxDecoration(
-        color: Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+        color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
         borderRadius: BorderRadius.circular(5), // 设置圆角
       ),
       child: Column(
@@ -69,14 +77,14 @@ class LabourRequestItem extends StatelessWidget {
 
               //部门
               MyTextView(
-                item.departmentName ?? "-",
+                item.outletName ?? "-",
                 marginLeft: 5,
                 isFontRegular: true,
                 textColor: Colors.white,
                 fontSize: 14,
               ).expanded(),
             ],
-          ).marginOnly(top: 15),
+          ).marginOnly(top: 13),
 
           // 工作日期时间
           Row(
@@ -84,7 +92,7 @@ class LabourRequestItem extends StatelessWidget {
             crossAxisAlignment: CrossAxisAlignment.center,
             children: [
               MyTextView(
-                "Datetime:".tr,
+                "${"Job Time".tr}:",
                 isFontRegular: true,
                 textColor: ColorConstants.textGrayAECAE5,
                 fontSize: 14,
@@ -92,116 +100,149 @@ class LabourRequestItem extends StatelessWidget {
 
               //日期时间
               MyTextView(
-                item.jobTime ?? "-",
+                "${item.jobDate} ${item.startTime}~${item.endTime}",
                 marginLeft: 5,
                 isFontRegular: true,
                 textColor: Colors.white,
                 fontSize: 14,
               ).expanded(),
             ],
-          ).marginOnly(top: 15),
+          ).marginOnly(top: 13),
 
           // 人数
           Row(
             mainAxisSize: MainAxisSize.max,
             crossAxisAlignment: CrossAxisAlignment.center,
             children: [
-              MyTextView(
-                "No. of Staff:".tr,
-                isFontRegular: true,
-                textColor: ColorConstants.textGrayAECAE5,
-                fontSize: 14,
+              RichText(
+                text: TextSpan(
+                  style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w400, color: ColorConstants.textGrayAECAE5),
+                  children: <TextSpan>[
+                    TextSpan(
+                      text: "Manpower Needed".tr,
+                    ),
+                    TextSpan(
+                      text: "($totalNum)",
+                      style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w400, color: ColorConstants.textYellowFFBB1B),
+                    ),
+                    const TextSpan(
+                      text: ":",
+                    ),
+                  ],
+                ),
               ),
 
               //人数
               MyTextView(
-                item.needNum.toString(),
+                item.hiringNum.toString(),
                 marginLeft: 5,
                 isFontRegular: true,
                 textColor: Colors.white,
                 fontSize: 14,
               ).expanded(),
             ],
-          ).marginOnly(top: 15),
+          ).marginOnly(top: 13),
 
-          // 状态
+          // 预选人数
           Row(
             mainAxisSize: MainAxisSize.max,
             crossAxisAlignment: CrossAxisAlignment.center,
             children: [
               MyTextView(
-                "Status:".tr,
+                "${"Pre Selected".tr}:",
                 isFontRegular: true,
                 textColor: ColorConstants.textGrayAECAE5,
                 fontSize: 14,
               ),
 
-              //状态
+              //文本
               MyTextView(
-                item.coStatusShow == null ? "" : item.coStatusShow!.tr,
+                item.preNum.toString(),
                 marginLeft: 5,
                 isFontRegular: true,
-                textColor: "Approved" == item.coStatusShow
-                    ? ColorConstants.textGreen05DC82
-                    : "Rejected" == item.coStatusShow
-                        ? ColorConstants.textRedFF6262
-                        : "Recall" == item.coStatusShow
-                            ? ColorConstants.textYellowFFBB1B
-                            : ColorConstants.textBlue06D9FF,
+                textColor: Colors.white,
                 fontSize: 14,
               ).expanded(),
             ],
-          ).marginOnly(top: 15),
+          ).marginOnly(top: 13),
 
-          // 发布状态
+          // 最后操作人员
           Row(
             mainAxisSize: MainAxisSize.max,
-            crossAxisAlignment: CrossAxisAlignment.center,
+            crossAxisAlignment: CrossAxisAlignment.start,
             children: [
               MyTextView(
-                "Publish Status:".tr,
+                "${"Latest Operated".tr}:",
                 isFontRegular: true,
                 textColor: ColorConstants.textGrayAECAE5,
                 fontSize: 14,
+                marginTop: 8,
               ),
 
-              //发布状态
-              MyTextView(
-                item.publishStatus ? "Published".tr : "Unpublished".tr,
-                marginLeft: 5,
-                isFontRegular: true,
-                textColor: Colors.white,
-                fontSize: 14,
+              //文本
+              Html(
+                data: item.lastOperator ?? "",
+                style: {
+                  "*": Style(
+                    color: Colors.white,
+                    fontSize: FontSize(14),
+                  ),
+                  "small": Style(
+                    color: ColorConstants.textGrayAECAE5,
+                    fontSize: FontSize(12),
+                  ),
+                  // 你可以为其他HTML标签设置样式
+                },
               ).expanded(),
             ],
-          ).marginOnly(top: 15),
+          ).marginOnly(top: 5),
 
-          // 创建时间
+          // 状态
           Row(
             mainAxisSize: MainAxisSize.max,
             crossAxisAlignment: CrossAxisAlignment.center,
             children: [
               MyTextView(
-                "Created At:".tr,
+                "Status:".tr,
                 isFontRegular: true,
                 textColor: ColorConstants.textGrayAECAE5,
                 fontSize: 14,
               ),
 
-              //发布状态
-              MyTextView(
-                item.createdAt ?? "-",
-                marginLeft: 5,
-                isFontRegular: true,
-                textColor: Colors.white,
-                fontSize: 14,
+              //文本
+              Column(
+                crossAxisAlignment: CrossAxisAlignment.start,
+                children: [
+                  MyTextView(
+                    item.statusShow == null ? "" : item.statusShow!.tr,
+                    marginLeft: 5,
+                    isFontRegular: true,
+                    textColor: "Approved" == item.statusShow
+                        ? ColorConstants.textGreen05DC82
+                        : "Rejected" == item.statusShow
+                            ? ColorConstants.textRedFF6262
+                            : "Recall" == item.statusShow
+                                ? ColorConstants.textYellowFFBB1B
+                                : ColorConstants.textBlue06D9FF,
+                    fontSize: 14,
+                  ),
+                  Visibility(
+                      visible: Utils.isNotEmpty(item.approveBy),
+                      child: MyTextView(
+                        item.approveBy ?? "-",
+                        marginLeft: 5,
+                        isFontRegular: true,
+                        textColor: ColorConstants.textGrayAECAE5,
+                        fontSize: 12,
+                      ))
+                ],
               ).expanded(),
             ],
-          ).marginOnly(top: 15),
+          ).marginOnly(top: 0),
 
           //按钮组
           Visibility(
-            visible: item.actionList?.isNotEmpty ?? false,
+            visible: true,
             child: Row(
               mainAxisSize: MainAxisSize.max,
               mainAxisAlignment: MainAxisAlignment.end,
@@ -209,7 +250,7 @@ class LabourRequestItem extends StatelessWidget {
               children: [
                 //详情按钮
                 Visibility(
-                  visible: item.actionList?.contains("detail") ?? false,
+                  visible: item.status != 4,
                   child: MyButton(
                     onPressed: () {
                       FocusScope.of(context).unfocus();
@@ -217,18 +258,49 @@ class LabourRequestItem extends StatelessWidget {
                     },
                     text: "Detail".tr,
                     textColor: ColorConstants.white,
-                    backgroundColor: hexToColor(
-                      "#56AAFF",
-                    ),
+                    backgroundColor: hexToColor("#56AAFF"),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 35,
+                  ).marginOnly(left: 12),
+                ),
+
+                Visibility(
+                  visible: item.status == 4,
+                  child: MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      onDeleteAction?.call();
+                    },
+                    text: "Delete".tr,
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#56AAFF"),
+                    radius: 17.25,
+                    minWidth: 60,
+                    minHeight: 35,
+                  ).marginOnly(left: 12),
+                ),
+
+                //Att 附件
+                Visibility(
+                  visible: true,
+                  child: MyButton(
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      onAttAction?.call();
+                    },
+                    text: "Att(${item.attNum})",
+                    textColor: ColorConstants.white,
+                    backgroundColor: hexToColor("#0AC074"),
                     radius: 17.25,
                     minWidth: 60,
                     minHeight: 35,
                   ).marginOnly(left: 12),
                 ),
 
-                //Recall按钮
+                //Recall按钮 (Pending)
                 Visibility(
-                  visible: item.actionList?.contains("recall") ?? false,
+                  visible: item.status == 1,
                   child: MyButton(
                     onPressed: () {
                       FocusScope.of(context).unfocus();
@@ -243,9 +315,9 @@ class LabourRequestItem extends StatelessWidget {
                   ).marginOnly(left: 12),
                 ),
 
-                //Edit按钮
+                //Edit按钮 (Recall)
                 Visibility(
-                  visible: item.actionList?.contains("edit") ?? false,
+                  visible: item.status == 4,
                   child: MyButton(
                     onPressed: () {
                       FocusScope.of(context).unfocus();
@@ -262,7 +334,7 @@ class LabourRequestItem extends StatelessWidget {
 
                 //状态工作流按钮
                 Visibility(
-                  visible: item.actionList?.contains("status") ?? false,
+                  visible: true,
                   child: MyButton(
                     onPressed: () {
                       FocusScope.of(context).unfocus();

+ 81 - 30
packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_list_controller.dart

@@ -1,12 +1,14 @@
+import 'package:domain/entity/response/t_h_o_a_labour_table_entity.dart';
+import 'package:plugin_platform/engine/notify/notify_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+
 import '../labour_request_add/labour_request_add_page.dart';
 import 'package:domain/entity/response/labour_request_index_entity.dart';
-import 'package:domain/entity/response/labour_request_list_entity.dart';
-import 'package:domain/repository/labour_repository.dart';
+import 'package:domain/repository/th_oa_repository.dart';
 import 'package:get/get.dart';
 import 'package:plugin_basic/constants/app_constant.dart';
 import 'package:plugin_platform/engine/dialog/dialog_engine.dart';
-import 'package:plugin_platform/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';
@@ -17,11 +19,13 @@ import 'package:widgets/load_state_layout.dart';
 import 'package:widgets/widget_export.dart';
 
 import '../labour_request_workflow/labour_request_workflow_page.dart';
+import 'dialog/oa_attach_list_controller.dart';
+import 'dialog/oa_attach_list_dialog.dart';
 import 'labour_request_filter.dart';
 import 'labour_request_list_state.dart';
 
 class LabourRequestListController extends GetxController with DioCancelableMixin {
-  final LabourRepository _labourRepository = Get.find();
+  final THOARepository _thRepository = Get.find();
   final LabourRequestListState state = LabourRequestListState();
 
   var _curPage = 1;
@@ -45,6 +49,7 @@ class LabourRequestListController extends GetxController with DioCancelableMixin
 
   // Refresh 刷新事件
   Future onRefresh() async {
+    state.totalNum = 0;
     _curPage = 1;
     fetchLabourRequestList();
   }
@@ -70,17 +75,17 @@ class LabourRequestListController extends GetxController with DioCancelableMixin
 
     // 并发执行两个请求
     var futures = [
-      _labourRepository.fetchLabourRequestList(
-        state.keyword,
-        DateTimeUtils.formatDate(state.selectedStartDate, format: "yyyy-MM-dd"),
-        DateTimeUtils.formatDate(state.selectedEndDate, format: "yyyy-MM-dd"),
-        state.selectedStatusId,
-        state.selectedDepartmentId,
+      _thRepository.fetchLabourRequestTable(
+        startDate: DateTimeUtils.formatDate(state.selectedStartDate, format: "yyyy-MM-dd"),
+        endDate: DateTimeUtils.formatDate(state.selectedEndDate, format: "yyyy-MM-dd"),
+        status: state.selectedStatusId,
+        outletId: state.selectedDepartmentId,
+        divisionId: state.selectedDivisionId,
         curPage: _curPage,
         cancelToken: cancelToken,
       ),
       state.indexOptions == null
-          ? _labourRepository.fetchLabourRequestIndex(
+          ? _thRepository.fetchLabourRequestIndex(
               cancelToken: cancelToken,
             )
           : Future(() => HttpResult(isSuccess: true).convert(data: state.indexOptions!)),
@@ -88,7 +93,7 @@ class LabourRequestListController extends GetxController with DioCancelableMixin
 
     //拿到结果
     var results = await Future.wait(futures);
-    var listResult = results[0] as HttpResult<LabourRequestListEntity>;
+    var listResult = results[0] as HttpResult<THOALabourTableEntity>;
     var optionResult = results[1] as HttpResult<LabourRequestIndexEntity>;
 
     //选项数据
@@ -98,6 +103,7 @@ class LabourRequestListController extends GetxController with DioCancelableMixin
 
     // 处理数据
     if (listResult.isSuccess) {
+      state.totalNum += listResult.data?.totNum ?? 0;
       handleList(listResult.data?.rows);
     } else {
       errorMessage = listResult.errorMsg;
@@ -109,7 +115,7 @@ class LabourRequestListController extends GetxController with DioCancelableMixin
   }
 
   // 处理数据与展示的逻辑
-  void handleList(List<LabourRequestListRows>? list) {
+  void handleList(List<THOALabourTableRows>? list) {
     if (list != null && list.isNotEmpty) {
       //有数据,判断是刷新还是加载更多的数据
       if (_curPage == 1) {
@@ -170,11 +176,13 @@ class LabourRequestListController extends GetxController with DioCancelableMixin
           selectedEndDate: state.selectedEndDate,
           selectedStatusId: state.selectedStatusId,
           selectedDepartmentId: state.selectedDepartmentId,
-          onFilterAction: (startDate, endDate, statusId, departmentId) {
+          selectedDivisionId: state.selectedDivisionId,
+          onFilterAction: (startDate, endDate, statusId, departmentId, divisionId) {
             state.selectedStartDate = startDate;
             state.selectedEndDate = endDate;
             state.selectedStatusId = statusId;
             state.selectedDepartmentId = departmentId;
+            state.selectedDivisionId = divisionId;
 
             //赋值之后刷新
             refreshController.callRefresh();
@@ -188,7 +196,7 @@ class LabourRequestListController extends GetxController with DioCancelableMixin
 
   /// 执行Recall请求
   void requestRecall(String requestId) async {
-    var recallResult = await _labourRepository.recallLabourRequest(
+    var recallResult = await _thRepository.recallLabourRequest(
       requestId,
       cancelToken: cancelToken,
     );
@@ -202,9 +210,39 @@ class LabourRequestListController extends GetxController with DioCancelableMixin
     }
   }
 
+  //执行删除操作
+  void doDeleteAction(String requestId, int index) {
+    DialogEngine.show(
+        widget: AppDefaultDialog(
+      title: "Confirmation".tr,
+      message: "Are you sure you want to delete this job?".tr,
+      confirmAction: () {
+        _requestDeactivate(requestId, index);
+      },
+    ));
+  }
+
+  // 请求接口删除Job
+  void _requestDeactivate(String requestId, int index) async {
+    var result = await _thRepository.deleteLabourRequest(requestId, cancelToken: cancelToken);
+
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess("Successful".tr);
+
+      if (state.datas.length <= 1) {
+        refreshController.callRefresh();
+      } else {
+        state.datas.removeAt(index);
+        update();
+      }
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+  }
+
   /// 根据ID获取Item对象,用于刷新
   void fetchItemByIdAndRefreshItem(String requestId) async {
-    var result = await _labourRepository.fetchItemByRequestId(
+    var result = await _thRepository.fetchItemByRequestId(
       requestId,
       cancelToken: cancelToken,
     );
@@ -212,11 +250,11 @@ class LabourRequestListController extends GetxController with DioCancelableMixin
     //处理数据
     if (result.isSuccess) {
       var data = result.data;
-      if (data != null && data.rows != null && data.rows!.isNotEmpty) {
-        final requestItem = data.rows![0];
+      if (data != null && data.rows.isNotEmpty) {
+        final requestItem = data.rows[0];
 
         //找到当前数据中的此 requestId,并替换对象,再刷新
-        var index = state.datas.indexWhere((element) => element.requestId == requestItem.requestId);
+        var index = state.datas.indexWhere((element) => element.id == requestItem.id);
         if (index >= 0) {
           state.datas[index] = requestItem;
           update();
@@ -262,34 +300,47 @@ class LabourRequestListController extends GetxController with DioCancelableMixin
 
   //跳转到添加页面
   void gotoAddLabourPage() {
-    LabourRequestAddPage.startInstance(1, "");
+    LabourRequestAddPage.startInstance(0, "", (result) {
+      refreshController.callRefresh();
+    });
   }
 
   //去详情页面
-  void gotoDetailPage(LabourRequestListRows data) {
-    LabourRequestAddPage.startInstance(2, data.requestId.toString());
+  void gotoDetailPage(THOALabourTableRows data) {
+    LabourRequestAddPage.startInstance(2, data.id.toString(), null);
   }
 
   //去编辑页面
-  void gotoEditPage(LabourRequestListRows data) {
-    LabourRequestAddPage.startInstance(1, data.requestId.toString());
+  void gotoEditPage(THOALabourTableRows data) {
+    LabourRequestAddPage.startInstance(1, data.id.toString(), (result) {
+      fetchItemByIdAndRefreshItem(data.id.toString());
+    });
   }
 
   //去状态工作流的页面
-  void gotoWorkflowPage(LabourRequestListRows data) {
-    LabourRequestWorkflowPage.startInstance(data.requestId.toString());
+  void gotoWorkflowPage(THOALabourTableRows data) {
+    LabourRequestWorkflowPage.startInstance(data.id.toString());
   }
 
-  void doRecall(LabourRequestListRows data) {
+  void doRecall(THOALabourTableRows data) {
     DialogEngine.show(
         widget: AppDefaultDialog(
       title: "Message".tr,
       message: "Are you sure you want to recall?".tr,
       confirmAction: () {
-        requestRecall(data.requestId.toString());
+        requestRecall(data.id.toString());
       },
     ));
   }
 
-
+  /// 展示附件弹窗
+  void showAttachmentDialog(THOALabourTableRows data) {
+    DialogEngine.show(
+      widget: OaAttachListDialog(
+        requestId: data.id.toString(),
+        jobTitle: data.jobTitle ?? "-",
+        jobDate: data.jobDate ?? "-",
+      ),
+    );
+  }
 }

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

@@ -5,6 +5,7 @@ import 'package:get/get.dart';
 import 'package:plugin_basic/base/base_state.dart';
 import 'package:plugin_basic/base/base_stateful_page.dart';
 import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
 import 'package:router/path/router_path.dart';
 import 'package:shared/utils/screen_util.dart';
 import 'package:widgets/ext/ex_widget.dart';
@@ -138,6 +139,7 @@ class _LabourRequestListState extends BaseState<LabourRequestListPage, LabourReq
                         return LabourRequestItem(
                           index: index,
                           item: state.datas[index],
+                          totalNum: state.totalNum,
                           onDetailAction: () {
                             controller.gotoDetailPage(state.datas[index]);
                           },
@@ -150,6 +152,12 @@ class _LabourRequestListState extends BaseState<LabourRequestListPage, LabourReq
                           onStatusAction: () {
                             controller.gotoWorkflowPage(state.datas[index]);
                           },
+                          onAttAction: (){
+                           controller.showAttachmentDialog(state.datas[index]);
+                          },
+                          onDeleteAction: (){
+                            controller.doDeleteAction(state.datas[index].id!, index);
+                          },
                         );
                       },
                       childCount: state.datas.length,

+ 4 - 1
packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_list_state.dart

@@ -1,5 +1,6 @@
 import 'package:domain/entity/response/labour_request_index_entity.dart';
 import 'package:domain/entity/response/labour_request_list_entity.dart';
+import 'package:domain/entity/response/t_h_o_a_labour_table_entity.dart';
 import 'package:flutter/material.dart';
 
 class LabourRequestListState {
@@ -10,8 +11,10 @@ class LabourRequestListState {
   DateTime? selectedEndDate;
   String? selectedStatusId;
   String? selectedDepartmentId;
+  String? selectedDivisionId;
 
   //页面的列表数据
-  List<LabourRequestListRows> datas = [];
+  List<THOALabourTableRows> datas = [];
+  int totalNum = 0;
   LabourRequestIndexEntity? indexOptions;
 }

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

@@ -1,5 +1,5 @@
 import 'package:domain/entity/response/labour_request_work_flow_entity.dart';
-import 'package:domain/repository/labour_repository.dart';
+import 'package:domain/repository/th_oa_repository.dart';
 import 'package:get/get.dart';
 import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
 import 'package:widgets/load_state_layout.dart';
@@ -9,7 +9,7 @@ import 'labour_request_workflow_state.dart';
 
 
 class LabourRequestWorkflowController extends GetxController with DioCancelableMixin{
-  final LabourRepository _labourRepository = Get.find();
+  final THOARepository _thRepository = Get.find();
   final LabourRequestWorkflowState state = LabourRequestWorkflowState();
 
   var _needShowPlaceholder = true;
@@ -48,7 +48,7 @@ class LabourRequestWorkflowController extends GetxController with DioCancelableM
     }
 
     //获取到数据
-    var result = await _labourRepository.fetchLabourRequestWorkFlow(
+    var result = await _thRepository.fetchLabourRequestWorkFlow(
       state.requestId,
       cancelToken: cancelToken,
     );

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

@@ -15,7 +15,7 @@ import 'labour_request_workflow_controller.dart';
 import 'labour_request_workflow_state.dart';
 import 'labour_workflow_item.dart';
 
-/**
+/*
  * 用工请求-状态工作流列表
  */
 class LabourRequestWorkflowPage extends BaseStatefulPage<LabourRequestWorkflowController> {
@@ -23,7 +23,7 @@ class LabourRequestWorkflowPage extends BaseStatefulPage<LabourRequestWorkflowCo
 
   //启动当前页面
   static void startInstance(String? requestId) {
-    return Get.start(RouterPath.jobLabourRequestWorkflow,arguments: {'requestId': requestId});
+    return Get.start(RouterPath.THLabourRequestWorkflowOA,arguments: {'requestId': requestId});
   }
 
   @override

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

@@ -336,6 +336,7 @@ class _LabourRequestAddState extends BaseState<LabourRequestERAddPage, LabourReq
                         if (state.pageType != 2) controller.pickOutlet();
                       }),
 
+                      //需求人数
                       FormRequireText(
                         text: "No. of Staff".tr,
                       ).marginOnly(top: 15, bottom: 10),

+ 1 - 1
packages/cpt_th/lib/router/th_router.dart

@@ -53,7 +53,7 @@ class THPageRouter {
 
     //用工请求状态修改工作流
     GetPage(
-      name: RouterPath.jobLabourRequestWorkflow,
+      name: RouterPath.THLabourRequestWorkflowOA,
       page: () => LabourRequestWorkflowPage(),
     ),
 

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

@@ -592,4 +592,42 @@ class ApiConstants {
   // 工作已申请员工列表-评价员工的提交
   static const apiERAppliedStaffStateWorkflowTH = "/index.php/api/er/applied/view-status";
 
+  // OA 用工请求的选项
+  static const apiOALabourRequestOptionTH = "/index.php/api/oa/lab-req/index";
+
+  // OA 用工请求的列表
+  static const apiOALabourRequestTableTH = "/index.php/api/oa/lab-req/table";
+
+  // OA 用工请求的撤回
+  static const apiOALabourRequestRecallTH = "/index.php/api/oa/lab-req/recall";
+
+  // OA 用工请求的删除
+  static const apiOALabourRequestDeleteTH = "/index.php/api/oa/lab-req/destroy";
+
+  // OA 用工请求的状态流
+  static const apiOALabourRequestWorkflowTH = "/index.php/api/oa/lab-req/view-status";
+
+  // OA 用工请求添加选项
+  static const apiOALabourRequestAddOptionTH = "/index.php/api/oa/lab-req/view-add";
+
+  // OA 用工请求详情
+  static const apiOALabourRequestDetailTH = "/index.php/api/oa/lab-req/view-edit";
+
+  // OA 用工请求添加提交
+  static const apiOALabourRequestAddSubmitTH = "/index.php/api/oa/lab-req/add-submit";
+
+  // OA 用工请求编辑提交
+  static const apiOALabourRequestEditSubmitTH = "/index.php/api/oa/lab-req/edit-submit";
+
+  // TH 泰国上传文件
+  static const apiUploadFileTH = "/index.php/api/er/upload/file";
+
+  //OA 附件列表
+  static const apiOAAttachmentListTH = "/index.php/api/oa/lab-req/att-table";
+
+  //OA 附件新增
+  static const apiOAAttachmentAddTH = "/index.php/api/oa/lab-req/att-submit";
+
+  //OA 附件删除
+  static const apiOAAttachmentDeleteTH = "/index.php/api/oa/lab-req/att-delete";
 }

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

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

+ 41 - 0
packages/cs_domain/lib/entity/response/t_h_o_a_attachment_entity.dart

@@ -0,0 +1,41 @@
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/t_h_o_a_attachment_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/t_h_o_a_attachment_entity.g.dart';
+
+@JsonSerializable()
+class THOAAttachmentEntity {
+	int total = 0;
+	List<THOAAttachmentRows> rows = [];
+
+	THOAAttachmentEntity();
+
+	factory THOAAttachmentEntity.fromJson(Map<String, dynamic> json) => $THOAAttachmentEntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $THOAAttachmentEntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class THOAAttachmentRows {
+	String? id;
+	@JSONField(name: "att_url")
+	String? attUrl;
+	@JSONField(name: "created_at")
+	String? createdAt;
+
+	THOAAttachmentRows();
+
+	factory THOAAttachmentRows.fromJson(Map<String, dynamic> json) => $THOAAttachmentRowsFromJson(json);
+
+	Map<String, dynamic> toJson() => $THOAAttachmentRowsToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}

+ 129 - 0
packages/cs_domain/lib/entity/response/t_h_o_a_labour_detail_entity.dart

@@ -0,0 +1,129 @@
+import 'package:domain/entity/response/index_option_entity.dart';
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/t_h_o_a_labour_detail_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/t_h_o_a_labour_detail_entity.g.dart';
+
+@JsonSerializable()
+class THOALabourDetailEntity {
+	@JSONField(name: "labour_request")
+	THOALabourDetailLabourRequest? labReq;
+	@JSONField(name: "from_view")
+	String? fromView;
+	@JSONField(name: "start_time")
+	String? startTime;
+	@JSONField(name: "end_time")
+	String? endTime;
+	@JSONField(name: "with_ot")
+	bool? withOt;
+
+	@JSONField(name: "title_list")
+	List<IndexOptionEntity> titleList = [];
+	@JSONField(name: "limit_list")
+	List<IndexOptionEntity> limitList = [];
+	@JSONField(name: "employment_list")
+	List<IndexOptionEntity> employmentList = [];
+	@JSONField(name: "outlet_list")
+	List<IndexOptionEntity> outletList = [];
+	@JSONField(name: "ot_radios")
+	List<IndexOptionEntity> otRadios = [];
+
+	String? view;
+
+	THOALabourDetailEntity();
+
+	factory THOALabourDetailEntity.fromJson(Map<String, dynamic> json) => $THOALabourDetailEntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $THOALabourDetailEntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class THOALabourDetailLabourRequest {
+	int? id;
+	@JSONField(name: "employer_id")
+	int? employerId;
+	@JSONField(name: "source_type")
+	int? sourceType;
+	@JSONField(name: "agency_id")
+	int? agencyId;
+	@JSONField(name: "agency_name")
+	String? agencyName;
+	@JSONField(name: "job_title")
+	String? jobTitle;
+	@JSONField(name: "outlet_id")
+	int? outletId;
+	@JSONField(name: "outlet_name")
+	String? outletName;
+	@JSONField(name: "job_date")
+	String? jobDate;
+	@JSONField(name: "start_time")
+	int? startTime;
+	@JSONField(name: "end_time")
+	int? endTime;
+	@JSONField(name: "working_hours")
+	String? workingHours;
+	@JSONField(name: "hourly_rate")
+	String? hourlyRate;
+	@JSONField(name: "hiring_num")
+	int? hiringNum;
+	@JSONField(name: "real_num")
+	int? realNum;
+	int? status;
+	String? description;
+	@JSONField(name: "event_name")
+	String? eventName;
+	@JSONField(name: "event_type")
+	String? eventType;
+	@JSONField(name: "with_typhoid")
+	int? withTyphoid;
+	@JSONField(name: "created_at")
+	String? createdAt;
+	@JSONField(name: "updated_at")
+	String? updatedAt;
+	@JSONField(name: "employment_type")
+	int? employmentType;
+	String? position;
+	String? passengers;
+	@JSONField(name: "est_revenue")
+	String? estRevenue;
+	@JSONField(name: "est_cost")
+	String? estCost;
+	@JSONField(name: "att_url")
+	String? attUrl;
+	@JSONField(name: "deleted_at")
+	String? deletedAt;
+	@JSONField(name: "job_title_id")
+	String? jobTitleId;
+	@JSONField(name: "pre_staff_ids")
+	String? preStaffIds;
+	@JSONField(name: "last_operator")
+	int? lastOperator;
+	@JSONField(name: "last_at")
+	String? lastAt;
+	@JSONField(name: "position_id")
+	String? positionId;
+	@JSONField(name: "sex_limit")
+	int? sexLimit;
+	@JSONField(name: "male_limit")
+	int? maleLimit;
+	@JSONField(name: "female_limit")
+	int? femaleLimit;
+	@JSONField(name: "is_ot")
+	int? isOt;
+
+	THOALabourDetailLabourRequest();
+
+	factory THOALabourDetailLabourRequest.fromJson(Map<String, dynamic> json) => $THOALabourDetailLabourRequestFromJson(json);
+
+	Map<String, dynamic> toJson() => $THOALabourDetailLabourRequestToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}

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

@@ -0,0 +1,70 @@
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/t_h_o_a_labour_table_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/t_h_o_a_labour_table_entity.g.dart';
+
+@JsonSerializable()
+class THOALabourTableEntity {
+	int total = 0;
+	List<THOALabourTableRows> rows = [];
+	@JSONField(name: "tot_num")
+	int totNum = 0;
+
+	THOALabourTableEntity();
+
+	factory THOALabourTableEntity.fromJson(Map<String, dynamic> json) => $THOALabourTableEntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $THOALabourTableEntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class THOALabourTableRows {
+	String? id;
+	@JSONField(name: "source_show")
+	String? sourceShow;
+	@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;
+	int? status;
+	@JSONField(name: "status_show")
+	String? statusShow;
+	@JSONField(name: "approve_by")
+	String? approveBy;
+	@JSONField(name: "hiring_num")
+	int? hiringNum;
+	@JSONField(name: "is_ot")
+	int? isOt;
+	@JSONField(name: "pre_num")
+	int? preNum;
+	@JSONField(name: "att_num")
+	int? attNum;
+	@JSONField(name: "last_operator")
+	String? lastOperator;
+	@JSONField(name: "created_at")
+	String? createdAt;
+	@JSONField(name: "position_name")
+	String? positionName;
+
+	THOALabourTableRows();
+
+	factory THOALabourTableRows.fromJson(Map<String, dynamic> json) => $THOALabourTableRowsFromJson(json);
+
+	Map<String, dynamic> toJson() => $THOALabourTableRowsToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}

+ 20 - 0
packages/cs_domain/lib/entity/response/t_h_upload_file_entity.dart

@@ -0,0 +1,20 @@
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/t_h_upload_file_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/t_h_upload_file_entity.g.dart';
+
+@JsonSerializable()
+class THUploadFileEntity {
+	String? path;
+
+	THUploadFileEntity();
+
+	factory THUploadFileEntity.fromJson(Map<String, dynamic> json) => $THUploadFileEntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $THUploadFileEntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}

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

@@ -91,6 +91,10 @@ import 'package:domain/entity/response/t_h_applied_index_entity.dart';
 import 'package:domain/entity/response/t_h_applied_table_entity.dart';
 import 'package:domain/entity/response/t_h_employee_detail_entity.dart';
 import 'package:domain/entity/response/t_h_employee_remarks_entity.dart';
+import 'package:domain/entity/response/t_h_o_a_attachment_entity.dart';
+import 'package:domain/entity/response/t_h_o_a_labour_detail_entity.dart';
+import 'package:domain/entity/response/t_h_o_a_labour_table_entity.dart';
+import 'package:domain/entity/response/t_h_upload_file_entity.dart';
 import 'package:domain/entity/response/u_k_attendance_entity.dart';
 import 'package:domain/entity/response/u_k_report_attendance_entity.dart';
 import 'package:domain/entity/response/u_k_report_labour_entity.dart';
@@ -781,6 +785,27 @@ class JsonConvert {
     if (<THEmployeeRemarksReviews>[] is M) {
       return data.map<THEmployeeRemarksReviews>((Map<String, dynamic> e) => THEmployeeRemarksReviews.fromJson(e)).toList() as M;
     }
+    if (<THOAAttachmentEntity>[] is M) {
+      return data.map<THOAAttachmentEntity>((Map<String, dynamic> e) => THOAAttachmentEntity.fromJson(e)).toList() as M;
+    }
+    if (<THOAAttachmentRows>[] is M) {
+      return data.map<THOAAttachmentRows>((Map<String, dynamic> e) => THOAAttachmentRows.fromJson(e)).toList() as M;
+    }
+    if (<THOALabourDetailEntity>[] is M) {
+      return data.map<THOALabourDetailEntity>((Map<String, dynamic> e) => THOALabourDetailEntity.fromJson(e)).toList() as M;
+    }
+    if (<THOALabourDetailLabourRequest>[] is M) {
+      return data.map<THOALabourDetailLabourRequest>((Map<String, dynamic> e) => THOALabourDetailLabourRequest.fromJson(e)).toList() as M;
+    }
+    if (<THOALabourTableEntity>[] is M) {
+      return data.map<THOALabourTableEntity>((Map<String, dynamic> e) => THOALabourTableEntity.fromJson(e)).toList() as M;
+    }
+    if (<THOALabourTableRows>[] is M) {
+      return data.map<THOALabourTableRows>((Map<String, dynamic> e) => THOALabourTableRows.fromJson(e)).toList() as M;
+    }
+    if (<THUploadFileEntity>[] is M) {
+      return data.map<THUploadFileEntity>((Map<String, dynamic> e) => THUploadFileEntity.fromJson(e)).toList() as M;
+    }
     if (<UKAttendanceEntity>[] is M) {
       return data.map<UKAttendanceEntity>((Map<String, dynamic> e) => UKAttendanceEntity.fromJson(e)).toList() as M;
     }
@@ -1041,6 +1066,13 @@ class JsonConvertClassCollection {
     (THEmployeeDetailReviews).toString(): THEmployeeDetailReviews.fromJson,
     (THEmployeeRemarksEntity).toString(): THEmployeeRemarksEntity.fromJson,
     (THEmployeeRemarksReviews).toString(): THEmployeeRemarksReviews.fromJson,
+    (THOAAttachmentEntity).toString(): THOAAttachmentEntity.fromJson,
+    (THOAAttachmentRows).toString(): THOAAttachmentRows.fromJson,
+    (THOALabourDetailEntity).toString(): THOALabourDetailEntity.fromJson,
+    (THOALabourDetailLabourRequest).toString(): THOALabourDetailLabourRequest.fromJson,
+    (THOALabourTableEntity).toString(): THOALabourTableEntity.fromJson,
+    (THOALabourTableRows).toString(): THOALabourTableRows.fromJson,
+    (THUploadFileEntity).toString(): THUploadFileEntity.fromJson,
     (UKAttendanceEntity).toString(): UKAttendanceEntity.fromJson,
     (UKAttendanceRows).toString(): UKAttendanceRows.fromJson,
     (UKAttendanceInOut).toString(): UKAttendanceInOut.fromJson,

+ 11 - 1
packages/cs_domain/lib/generated/json/labour_request_index_entity.g.dart

@@ -1,5 +1,7 @@
 import 'package:domain/generated/json/base/json_convert_content.dart';
 import 'package:domain/entity/response/labour_request_index_entity.dart';
+import 'package:domain/entity/response/index_option_entity.dart';
+
 
 LabourRequestIndexEntity $LabourRequestIndexEntityFromJson(Map<String, dynamic> json) {
   final LabourRequestIndexEntity labourRequestIndexEntity = LabourRequestIndexEntity();
@@ -18,6 +20,11 @@ LabourRequestIndexEntity $LabourRequestIndexEntityFromJson(Map<String, dynamic>
   if (statusList != null) {
     labourRequestIndexEntity.statusList = statusList;
   }
+  final List<IndexOptionEntity>? divisionList = (json['division_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<IndexOptionEntity>(e) as IndexOptionEntity).toList();
+  if (divisionList != null) {
+    labourRequestIndexEntity.divisionList = divisionList;
+  }
   return labourRequestIndexEntity;
 }
 
@@ -26,6 +33,7 @@ Map<String, dynamic> $LabourRequestIndexEntityToJson(LabourRequestIndexEntity en
   data['department_list'] = entity.departmentList?.map((v) => v.toJson()).toList();
   data['outlet_list'] = entity.outletList?.map((v) => v.toJson()).toList();
   data['status_list'] = entity.statusList?.map((v) => v.toJson()).toList();
+  data['division_list'] = entity.divisionList.map((v) => v.toJson()).toList();
   return data;
 }
 
@@ -34,11 +42,13 @@ extension LabourRequestIndexEntityExtension on LabourRequestIndexEntity {
     List<LabourRequestIndexDepartmentList>? departmentList,
     List<LabourRequestIndexDepartmentList>? outletList,
     List<LabourRequestIndexStatusList>? statusList,
+    List<IndexOptionEntity>? divisionList,
   }) {
     return LabourRequestIndexEntity()
       ..departmentList = departmentList ?? this.departmentList
       ..outletList = outletList ?? this.outletList
-      ..statusList = statusList ?? this.statusList;
+      ..statusList = statusList ?? this.statusList
+      ..divisionList = divisionList ?? this.divisionList;
   }
 }
 

+ 72 - 0
packages/cs_domain/lib/generated/json/t_h_o_a_attachment_entity.g.dart

@@ -0,0 +1,72 @@
+import 'package:domain/generated/json/base/json_convert_content.dart';
+import 'package:domain/entity/response/t_h_o_a_attachment_entity.dart';
+
+THOAAttachmentEntity $THOAAttachmentEntityFromJson(Map<String, dynamic> json) {
+  final THOAAttachmentEntity tHOAAttachmentEntity = THOAAttachmentEntity();
+  final int? total = jsonConvert.convert<int>(json['total']);
+  if (total != null) {
+    tHOAAttachmentEntity.total = total;
+  }
+  final List<THOAAttachmentRows>? rows = (json['rows'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<THOAAttachmentRows>(e) as THOAAttachmentRows).toList();
+  if (rows != null) {
+    tHOAAttachmentEntity.rows = rows;
+  }
+  return tHOAAttachmentEntity;
+}
+
+Map<String, dynamic> $THOAAttachmentEntityToJson(THOAAttachmentEntity entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['total'] = entity.total;
+  data['rows'] = entity.rows.map((v) => v.toJson()).toList();
+  return data;
+}
+
+extension THOAAttachmentEntityExtension on THOAAttachmentEntity {
+  THOAAttachmentEntity copyWith({
+    int? total,
+    List<THOAAttachmentRows>? rows,
+  }) {
+    return THOAAttachmentEntity()
+      ..total = total ?? this.total
+      ..rows = rows ?? this.rows;
+  }
+}
+
+THOAAttachmentRows $THOAAttachmentRowsFromJson(Map<String, dynamic> json) {
+  final THOAAttachmentRows tHOAAttachmentRows = THOAAttachmentRows();
+  final String? id = jsonConvert.convert<String>(json['id']);
+  if (id != null) {
+    tHOAAttachmentRows.id = id;
+  }
+  final String? attUrl = jsonConvert.convert<String>(json['att_url']);
+  if (attUrl != null) {
+    tHOAAttachmentRows.attUrl = attUrl;
+  }
+  final String? createdAt = jsonConvert.convert<String>(json['created_at']);
+  if (createdAt != null) {
+    tHOAAttachmentRows.createdAt = createdAt;
+  }
+  return tHOAAttachmentRows;
+}
+
+Map<String, dynamic> $THOAAttachmentRowsToJson(THOAAttachmentRows entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['id'] = entity.id;
+  data['att_url'] = entity.attUrl;
+  data['created_at'] = entity.createdAt;
+  return data;
+}
+
+extension THOAAttachmentRowsExtension on THOAAttachmentRows {
+  THOAAttachmentRows copyWith({
+    String? id,
+    String? attUrl,
+    String? createdAt,
+  }) {
+    return THOAAttachmentRows()
+      ..id = id ?? this.id
+      ..attUrl = attUrl ?? this.attUrl
+      ..createdAt = createdAt ?? this.createdAt;
+  }
+}

+ 386 - 0
packages/cs_domain/lib/generated/json/t_h_o_a_labour_detail_entity.g.dart

@@ -0,0 +1,386 @@
+import 'package:domain/generated/json/base/json_convert_content.dart';
+import 'package:domain/entity/response/t_h_o_a_labour_detail_entity.dart';
+import 'package:domain/entity/response/index_option_entity.dart';
+
+
+THOALabourDetailEntity $THOALabourDetailEntityFromJson(Map<String, dynamic> json) {
+  final THOALabourDetailEntity tHOALabourDetailEntity = THOALabourDetailEntity();
+  final THOALabourDetailLabourRequest? labReq = jsonConvert.convert<THOALabourDetailLabourRequest>(json['labour_request']);
+  if (labReq != null) {
+    tHOALabourDetailEntity.labReq = labReq;
+  }
+  final String? fromView = jsonConvert.convert<String>(json['from_view']);
+  if (fromView != null) {
+    tHOALabourDetailEntity.fromView = fromView;
+  }
+  final String? startTime = jsonConvert.convert<String>(json['start_time']);
+  if (startTime != null) {
+    tHOALabourDetailEntity.startTime = startTime;
+  }
+  final String? endTime = jsonConvert.convert<String>(json['end_time']);
+  if (endTime != null) {
+    tHOALabourDetailEntity.endTime = endTime;
+  }
+  final bool? withOt = jsonConvert.convert<bool>(json['with_ot']);
+  if (withOt != null) {
+    tHOALabourDetailEntity.withOt = withOt;
+  }
+  final List<IndexOptionEntity>? titleList = (json['title_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<IndexOptionEntity>(e) as IndexOptionEntity).toList();
+  if (titleList != null) {
+    tHOALabourDetailEntity.titleList = titleList;
+  }
+  final List<IndexOptionEntity>? limitList = (json['limit_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<IndexOptionEntity>(e) as IndexOptionEntity).toList();
+  if (limitList != null) {
+    tHOALabourDetailEntity.limitList = limitList;
+  }
+  final List<IndexOptionEntity>? employmentList = (json['employment_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<IndexOptionEntity>(e) as IndexOptionEntity).toList();
+  if (employmentList != null) {
+    tHOALabourDetailEntity.employmentList = employmentList;
+  }
+  final List<IndexOptionEntity>? outletList = (json['outlet_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<IndexOptionEntity>(e) as IndexOptionEntity).toList();
+  if (outletList != null) {
+    tHOALabourDetailEntity.outletList = outletList;
+  }
+  final List<IndexOptionEntity>? otRadios = (json['ot_radios'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<IndexOptionEntity>(e) as IndexOptionEntity).toList();
+  if (otRadios != null) {
+    tHOALabourDetailEntity.otRadios = otRadios;
+  }
+  final String? view = jsonConvert.convert<String>(json['view']);
+  if (view != null) {
+    tHOALabourDetailEntity.view = view;
+  }
+  return tHOALabourDetailEntity;
+}
+
+Map<String, dynamic> $THOALabourDetailEntityToJson(THOALabourDetailEntity entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['labour_request'] = entity.labReq?.toJson();
+  data['from_view'] = entity.fromView;
+  data['start_time'] = entity.startTime;
+  data['end_time'] = entity.endTime;
+  data['with_ot'] = entity.withOt;
+  data['title_list'] = entity.titleList.map((v) => v.toJson()).toList();
+  data['limit_list'] = entity.limitList.map((v) => v.toJson()).toList();
+  data['employment_list'] = entity.employmentList.map((v) => v.toJson()).toList();
+  data['outlet_list'] = entity.outletList.map((v) => v.toJson()).toList();
+  data['ot_radios'] = entity.otRadios.map((v) => v.toJson()).toList();
+  data['view'] = entity.view;
+  return data;
+}
+
+extension THOALabourDetailEntityExtension on THOALabourDetailEntity {
+  THOALabourDetailEntity copyWith({
+    THOALabourDetailLabourRequest? labReq,
+    String? fromView,
+    String? startTime,
+    String? endTime,
+    bool? withOt,
+    List<IndexOptionEntity>? titleList,
+    List<IndexOptionEntity>? limitList,
+    List<IndexOptionEntity>? employmentList,
+    List<IndexOptionEntity>? outletList,
+    List<IndexOptionEntity>? otRadios,
+    String? view,
+  }) {
+    return THOALabourDetailEntity()
+      ..labReq = labReq ?? this.labReq
+      ..fromView = fromView ?? this.fromView
+      ..startTime = startTime ?? this.startTime
+      ..endTime = endTime ?? this.endTime
+      ..withOt = withOt ?? this.withOt
+      ..titleList = titleList ?? this.titleList
+      ..limitList = limitList ?? this.limitList
+      ..employmentList = employmentList ?? this.employmentList
+      ..outletList = outletList ?? this.outletList
+      ..otRadios = otRadios ?? this.otRadios
+      ..view = view ?? this.view;
+  }
+}
+
+THOALabourDetailLabourRequest $THOALabourDetailLabourRequestFromJson(Map<String, dynamic> json) {
+  final THOALabourDetailLabourRequest tHOALabourDetailLabourRequest = THOALabourDetailLabourRequest();
+  final int? id = jsonConvert.convert<int>(json['id']);
+  if (id != null) {
+    tHOALabourDetailLabourRequest.id = id;
+  }
+  final int? employerId = jsonConvert.convert<int>(json['employer_id']);
+  if (employerId != null) {
+    tHOALabourDetailLabourRequest.employerId = employerId;
+  }
+  final int? sourceType = jsonConvert.convert<int>(json['source_type']);
+  if (sourceType != null) {
+    tHOALabourDetailLabourRequest.sourceType = sourceType;
+  }
+  final int? agencyId = jsonConvert.convert<int>(json['agency_id']);
+  if (agencyId != null) {
+    tHOALabourDetailLabourRequest.agencyId = agencyId;
+  }
+  final String? agencyName = jsonConvert.convert<String>(json['agency_name']);
+  if (agencyName != null) {
+    tHOALabourDetailLabourRequest.agencyName = agencyName;
+  }
+  final String? jobTitle = jsonConvert.convert<String>(json['job_title']);
+  if (jobTitle != null) {
+    tHOALabourDetailLabourRequest.jobTitle = jobTitle;
+  }
+  final int? outletId = jsonConvert.convert<int>(json['outlet_id']);
+  if (outletId != null) {
+    tHOALabourDetailLabourRequest.outletId = outletId;
+  }
+  final String? outletName = jsonConvert.convert<String>(json['outlet_name']);
+  if (outletName != null) {
+    tHOALabourDetailLabourRequest.outletName = outletName;
+  }
+  final String? jobDate = jsonConvert.convert<String>(json['job_date']);
+  if (jobDate != null) {
+    tHOALabourDetailLabourRequest.jobDate = jobDate;
+  }
+  final int? startTime = jsonConvert.convert<int>(json['start_time']);
+  if (startTime != null) {
+    tHOALabourDetailLabourRequest.startTime = startTime;
+  }
+  final int? endTime = jsonConvert.convert<int>(json['end_time']);
+  if (endTime != null) {
+    tHOALabourDetailLabourRequest.endTime = endTime;
+  }
+  final String? workingHours = jsonConvert.convert<String>(json['working_hours']);
+  if (workingHours != null) {
+    tHOALabourDetailLabourRequest.workingHours = workingHours;
+  }
+  final String? hourlyRate = jsonConvert.convert<String>(json['hourly_rate']);
+  if (hourlyRate != null) {
+    tHOALabourDetailLabourRequest.hourlyRate = hourlyRate;
+  }
+  final int? hiringNum = jsonConvert.convert<int>(json['hiring_num']);
+  if (hiringNum != null) {
+    tHOALabourDetailLabourRequest.hiringNum = hiringNum;
+  }
+  final int? realNum = jsonConvert.convert<int>(json['real_num']);
+  if (realNum != null) {
+    tHOALabourDetailLabourRequest.realNum = realNum;
+  }
+  final int? status = jsonConvert.convert<int>(json['status']);
+  if (status != null) {
+    tHOALabourDetailLabourRequest.status = status;
+  }
+  final String? description = jsonConvert.convert<String>(json['description']);
+  if (description != null) {
+    tHOALabourDetailLabourRequest.description = description;
+  }
+  final String? eventName = jsonConvert.convert<String>(json['event_name']);
+  if (eventName != null) {
+    tHOALabourDetailLabourRequest.eventName = eventName;
+  }
+  final String? eventType = jsonConvert.convert<String>(json['event_type']);
+  if (eventType != null) {
+    tHOALabourDetailLabourRequest.eventType = eventType;
+  }
+  final int? withTyphoid = jsonConvert.convert<int>(json['with_typhoid']);
+  if (withTyphoid != null) {
+    tHOALabourDetailLabourRequest.withTyphoid = withTyphoid;
+  }
+  final String? createdAt = jsonConvert.convert<String>(json['created_at']);
+  if (createdAt != null) {
+    tHOALabourDetailLabourRequest.createdAt = createdAt;
+  }
+  final String? updatedAt = jsonConvert.convert<String>(json['updated_at']);
+  if (updatedAt != null) {
+    tHOALabourDetailLabourRequest.updatedAt = updatedAt;
+  }
+  final int? employmentType = jsonConvert.convert<int>(json['employment_type']);
+  if (employmentType != null) {
+    tHOALabourDetailLabourRequest.employmentType = employmentType;
+  }
+  final String? position = jsonConvert.convert<String>(json['position']);
+  if (position != null) {
+    tHOALabourDetailLabourRequest.position = position;
+  }
+  final String? passengers = jsonConvert.convert<String>(json['passengers']);
+  if (passengers != null) {
+    tHOALabourDetailLabourRequest.passengers = passengers;
+  }
+  final String? estRevenue = jsonConvert.convert<String>(json['est_revenue']);
+  if (estRevenue != null) {
+    tHOALabourDetailLabourRequest.estRevenue = estRevenue;
+  }
+  final String? estCost = jsonConvert.convert<String>(json['est_cost']);
+  if (estCost != null) {
+    tHOALabourDetailLabourRequest.estCost = estCost;
+  }
+  final String? attUrl = jsonConvert.convert<String>(json['att_url']);
+  if (attUrl != null) {
+    tHOALabourDetailLabourRequest.attUrl = attUrl;
+  }
+  final String? deletedAt = jsonConvert.convert<String>(json['deleted_at']);
+  if (deletedAt != null) {
+    tHOALabourDetailLabourRequest.deletedAt = deletedAt;
+  }
+  final String? jobTitleId = jsonConvert.convert<String>(json['job_title_id']);
+  if (jobTitleId != null) {
+    tHOALabourDetailLabourRequest.jobTitleId = jobTitleId;
+  }
+  final String? preStaffIds = jsonConvert.convert<String>(json['pre_staff_ids']);
+  if (preStaffIds != null) {
+    tHOALabourDetailLabourRequest.preStaffIds = preStaffIds;
+  }
+  final int? lastOperator = jsonConvert.convert<int>(json['last_operator']);
+  if (lastOperator != null) {
+    tHOALabourDetailLabourRequest.lastOperator = lastOperator;
+  }
+  final String? lastAt = jsonConvert.convert<String>(json['last_at']);
+  if (lastAt != null) {
+    tHOALabourDetailLabourRequest.lastAt = lastAt;
+  }
+  final String? positionId = jsonConvert.convert<String>(json['position_id']);
+  if (positionId != null) {
+    tHOALabourDetailLabourRequest.positionId = positionId;
+  }
+  final int? sexLimit = jsonConvert.convert<int>(json['sex_limit']);
+  if (sexLimit != null) {
+    tHOALabourDetailLabourRequest.sexLimit = sexLimit;
+  }
+  final int? maleLimit = jsonConvert.convert<int>(json['male_limit']);
+  if (maleLimit != null) {
+    tHOALabourDetailLabourRequest.maleLimit = maleLimit;
+  }
+  final int? femaleLimit = jsonConvert.convert<int>(json['female_limit']);
+  if (femaleLimit != null) {
+    tHOALabourDetailLabourRequest.femaleLimit = femaleLimit;
+  }
+  final int? isOt = jsonConvert.convert<int>(json['is_ot']);
+  if (isOt != null) {
+    tHOALabourDetailLabourRequest.isOt = isOt;
+  }
+  return tHOALabourDetailLabourRequest;
+}
+
+Map<String, dynamic> $THOALabourDetailLabourRequestToJson(THOALabourDetailLabourRequest entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['id'] = entity.id;
+  data['employer_id'] = entity.employerId;
+  data['source_type'] = entity.sourceType;
+  data['agency_id'] = entity.agencyId;
+  data['agency_name'] = entity.agencyName;
+  data['job_title'] = entity.jobTitle;
+  data['outlet_id'] = entity.outletId;
+  data['outlet_name'] = entity.outletName;
+  data['job_date'] = entity.jobDate;
+  data['start_time'] = entity.startTime;
+  data['end_time'] = entity.endTime;
+  data['working_hours'] = entity.workingHours;
+  data['hourly_rate'] = entity.hourlyRate;
+  data['hiring_num'] = entity.hiringNum;
+  data['real_num'] = entity.realNum;
+  data['status'] = entity.status;
+  data['description'] = entity.description;
+  data['event_name'] = entity.eventName;
+  data['event_type'] = entity.eventType;
+  data['with_typhoid'] = entity.withTyphoid;
+  data['created_at'] = entity.createdAt;
+  data['updated_at'] = entity.updatedAt;
+  data['employment_type'] = entity.employmentType;
+  data['position'] = entity.position;
+  data['passengers'] = entity.passengers;
+  data['est_revenue'] = entity.estRevenue;
+  data['est_cost'] = entity.estCost;
+  data['att_url'] = entity.attUrl;
+  data['deleted_at'] = entity.deletedAt;
+  data['job_title_id'] = entity.jobTitleId;
+  data['pre_staff_ids'] = entity.preStaffIds;
+  data['last_operator'] = entity.lastOperator;
+  data['last_at'] = entity.lastAt;
+  data['position_id'] = entity.positionId;
+  data['sex_limit'] = entity.sexLimit;
+  data['male_limit'] = entity.maleLimit;
+  data['female_limit'] = entity.femaleLimit;
+  data['is_ot'] = entity.isOt;
+  return data;
+}
+
+extension THOALabourDetailLabourRequestExtension on THOALabourDetailLabourRequest {
+  THOALabourDetailLabourRequest copyWith({
+    int? id,
+    int? employerId,
+    int? sourceType,
+    int? agencyId,
+    String? agencyName,
+    String? jobTitle,
+    int? outletId,
+    String? outletName,
+    String? jobDate,
+    int? startTime,
+    int? endTime,
+    String? workingHours,
+    String? hourlyRate,
+    int? hiringNum,
+    int? realNum,
+    int? status,
+    String? description,
+    String? eventName,
+    String? eventType,
+    int? withTyphoid,
+    String? createdAt,
+    String? updatedAt,
+    int? employmentType,
+    String? position,
+    String? passengers,
+    String? estRevenue,
+    String? estCost,
+    String? attUrl,
+    String? deletedAt,
+    String? jobTitleId,
+    String? preStaffIds,
+    int? lastOperator,
+    String? lastAt,
+    String? positionId,
+    int? sexLimit,
+    int? maleLimit,
+    int? femaleLimit,
+    int? isOt,
+  }) {
+    return THOALabourDetailLabourRequest()
+      ..id = id ?? this.id
+      ..employerId = employerId ?? this.employerId
+      ..sourceType = sourceType ?? this.sourceType
+      ..agencyId = agencyId ?? this.agencyId
+      ..agencyName = agencyName ?? this.agencyName
+      ..jobTitle = jobTitle ?? this.jobTitle
+      ..outletId = outletId ?? this.outletId
+      ..outletName = outletName ?? this.outletName
+      ..jobDate = jobDate ?? this.jobDate
+      ..startTime = startTime ?? this.startTime
+      ..endTime = endTime ?? this.endTime
+      ..workingHours = workingHours ?? this.workingHours
+      ..hourlyRate = hourlyRate ?? this.hourlyRate
+      ..hiringNum = hiringNum ?? this.hiringNum
+      ..realNum = realNum ?? this.realNum
+      ..status = status ?? this.status
+      ..description = description ?? this.description
+      ..eventName = eventName ?? this.eventName
+      ..eventType = eventType ?? this.eventType
+      ..withTyphoid = withTyphoid ?? this.withTyphoid
+      ..createdAt = createdAt ?? this.createdAt
+      ..updatedAt = updatedAt ?? this.updatedAt
+      ..employmentType = employmentType ?? this.employmentType
+      ..position = position ?? this.position
+      ..passengers = passengers ?? this.passengers
+      ..estRevenue = estRevenue ?? this.estRevenue
+      ..estCost = estCost ?? this.estCost
+      ..attUrl = attUrl ?? this.attUrl
+      ..deletedAt = deletedAt ?? this.deletedAt
+      ..jobTitleId = jobTitleId ?? this.jobTitleId
+      ..preStaffIds = preStaffIds ?? this.preStaffIds
+      ..lastOperator = lastOperator ?? this.lastOperator
+      ..lastAt = lastAt ?? this.lastAt
+      ..positionId = positionId ?? this.positionId
+      ..sexLimit = sexLimit ?? this.sexLimit
+      ..maleLimit = maleLimit ?? this.maleLimit
+      ..femaleLimit = femaleLimit ?? this.femaleLimit
+      ..isOt = isOt ?? this.isOt;
+  }
+}

+ 177 - 0
packages/cs_domain/lib/generated/json/t_h_o_a_labour_table_entity.g.dart

@@ -0,0 +1,177 @@
+import 'package:domain/generated/json/base/json_convert_content.dart';
+import 'package:domain/entity/response/t_h_o_a_labour_table_entity.dart';
+
+THOALabourTableEntity $THOALabourTableEntityFromJson(Map<String, dynamic> json) {
+  final THOALabourTableEntity tHOALabourTableEntity = THOALabourTableEntity();
+  final int? total = jsonConvert.convert<int>(json['total']);
+  if (total != null) {
+    tHOALabourTableEntity.total = total;
+  }
+  final List<THOALabourTableRows>? rows = (json['rows'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<THOALabourTableRows>(e) as THOALabourTableRows).toList();
+  if (rows != null) {
+    tHOALabourTableEntity.rows = rows;
+  }
+  final int? totNum = jsonConvert.convert<int>(json['tot_num']);
+  if (totNum != null) {
+    tHOALabourTableEntity.totNum = totNum;
+  }
+  return tHOALabourTableEntity;
+}
+
+Map<String, dynamic> $THOALabourTableEntityToJson(THOALabourTableEntity entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['total'] = entity.total;
+  data['rows'] = entity.rows.map((v) => v.toJson()).toList();
+  data['tot_num'] = entity.totNum;
+  return data;
+}
+
+extension THOALabourTableEntityExtension on THOALabourTableEntity {
+  THOALabourTableEntity copyWith({
+    int? total,
+    List<THOALabourTableRows>? rows,
+    int? totNum,
+  }) {
+    return THOALabourTableEntity()
+      ..total = total ?? this.total
+      ..rows = rows ?? this.rows
+      ..totNum = totNum ?? this.totNum;
+  }
+}
+
+THOALabourTableRows $THOALabourTableRowsFromJson(Map<String, dynamic> json) {
+  final THOALabourTableRows tHOALabourTableRows = THOALabourTableRows();
+  final String? id = jsonConvert.convert<String>(json['id']);
+  if (id != null) {
+    tHOALabourTableRows.id = id;
+  }
+  final String? sourceShow = jsonConvert.convert<String>(json['source_show']);
+  if (sourceShow != null) {
+    tHOALabourTableRows.sourceShow = sourceShow;
+  }
+  final String? jobDate = jsonConvert.convert<String>(json['job_date']);
+  if (jobDate != null) {
+    tHOALabourTableRows.jobDate = jobDate;
+  }
+  final String? outletName = jsonConvert.convert<String>(json['outlet_name']);
+  if (outletName != null) {
+    tHOALabourTableRows.outletName = outletName;
+  }
+  final String? jobTitle = jsonConvert.convert<String>(json['job_title']);
+  if (jobTitle != null) {
+    tHOALabourTableRows.jobTitle = jobTitle;
+  }
+  final String? startTime = jsonConvert.convert<String>(json['start_time']);
+  if (startTime != null) {
+    tHOALabourTableRows.startTime = startTime;
+  }
+  final String? endTime = jsonConvert.convert<String>(json['end_time']);
+  if (endTime != null) {
+    tHOALabourTableRows.endTime = endTime;
+  }
+  final int? status = jsonConvert.convert<int>(json['status']);
+  if (status != null) {
+    tHOALabourTableRows.status = status;
+  }
+  final String? statusShow = jsonConvert.convert<String>(json['status_show']);
+  if (statusShow != null) {
+    tHOALabourTableRows.statusShow = statusShow;
+  }
+  final String? approveBy = jsonConvert.convert<String>(json['approve_by']);
+  if (approveBy != null) {
+    tHOALabourTableRows.approveBy = approveBy;
+  }
+  final int? hiringNum = jsonConvert.convert<int>(json['hiring_num']);
+  if (hiringNum != null) {
+    tHOALabourTableRows.hiringNum = hiringNum;
+  }
+  final int? isOt = jsonConvert.convert<int>(json['is_ot']);
+  if (isOt != null) {
+    tHOALabourTableRows.isOt = isOt;
+  }
+  final int? preNum = jsonConvert.convert<int>(json['pre_num']);
+  if (preNum != null) {
+    tHOALabourTableRows.preNum = preNum;
+  }
+  final int? attNum = jsonConvert.convert<int>(json['att_num']);
+  if (attNum != null) {
+    tHOALabourTableRows.attNum = attNum;
+  }
+  final String? lastOperator = jsonConvert.convert<String>(json['last_operator']);
+  if (lastOperator != null) {
+    tHOALabourTableRows.lastOperator = lastOperator;
+  }
+  final String? createdAt = jsonConvert.convert<String>(json['created_at']);
+  if (createdAt != null) {
+    tHOALabourTableRows.createdAt = createdAt;
+  }
+  final String? positionName = jsonConvert.convert<String>(json['position_name']);
+  if (positionName != null) {
+    tHOALabourTableRows.positionName = positionName;
+  }
+  return tHOALabourTableRows;
+}
+
+Map<String, dynamic> $THOALabourTableRowsToJson(THOALabourTableRows entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['id'] = entity.id;
+  data['source_show'] = entity.sourceShow;
+  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['status'] = entity.status;
+  data['status_show'] = entity.statusShow;
+  data['approve_by'] = entity.approveBy;
+  data['hiring_num'] = entity.hiringNum;
+  data['is_ot'] = entity.isOt;
+  data['pre_num'] = entity.preNum;
+  data['att_num'] = entity.attNum;
+  data['last_operator'] = entity.lastOperator;
+  data['created_at'] = entity.createdAt;
+  data['position_name'] = entity.positionName;
+  return data;
+}
+
+extension THOALabourTableRowsExtension on THOALabourTableRows {
+  THOALabourTableRows copyWith({
+    String? id,
+    String? sourceShow,
+    String? jobDate,
+    String? outletName,
+    String? jobTitle,
+    String? startTime,
+    String? endTime,
+    int? status,
+    String? statusShow,
+    String? approveBy,
+    int? hiringNum,
+    int? isOt,
+    int? preNum,
+    int? attNum,
+    String? lastOperator,
+    String? createdAt,
+    String? positionName,
+  }) {
+    return THOALabourTableRows()
+      ..id = id ?? this.id
+      ..sourceShow = sourceShow ?? this.sourceShow
+      ..jobDate = jobDate ?? this.jobDate
+      ..outletName = outletName ?? this.outletName
+      ..jobTitle = jobTitle ?? this.jobTitle
+      ..startTime = startTime ?? this.startTime
+      ..endTime = endTime ?? this.endTime
+      ..status = status ?? this.status
+      ..statusShow = statusShow ?? this.statusShow
+      ..approveBy = approveBy ?? this.approveBy
+      ..hiringNum = hiringNum ?? this.hiringNum
+      ..isOt = isOt ?? this.isOt
+      ..preNum = preNum ?? this.preNum
+      ..attNum = attNum ?? this.attNum
+      ..lastOperator = lastOperator ?? this.lastOperator
+      ..createdAt = createdAt ?? this.createdAt
+      ..positionName = positionName ?? this.positionName;
+  }
+}

+ 26 - 0
packages/cs_domain/lib/generated/json/t_h_upload_file_entity.g.dart

@@ -0,0 +1,26 @@
+import 'package:domain/generated/json/base/json_convert_content.dart';
+import 'package:domain/entity/response/t_h_upload_file_entity.dart';
+
+THUploadFileEntity $THUploadFileEntityFromJson(Map<String, dynamic> json) {
+  final THUploadFileEntity tHUploadFileEntity = THUploadFileEntity();
+  final String? path = jsonConvert.convert<String>(json['path']);
+  if (path != null) {
+    tHUploadFileEntity.path = path;
+  }
+  return tHUploadFileEntity;
+}
+
+Map<String, dynamic> $THUploadFileEntityToJson(THUploadFileEntity entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['path'] = entity.path;
+  return data;
+}
+
+extension THUploadFileEntityExtension on THUploadFileEntity {
+  THUploadFileEntity copyWith({
+    String? path,
+  }) {
+    return THUploadFileEntity()
+      ..path = path ?? this.path;
+  }
+}

+ 11 - 15
packages/cs_domain/lib/repository/th_er_repository.dart

@@ -102,18 +102,15 @@ class THERRepository extends GetxService {
     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!;
+
+    params['sex_limit'] = sexLimit ?? "0";
+    if (sexLimit == "1") {
+      params['male_limit'] = maleLimit ?? "0";
+      params['female_limit'] = femaleLimit ?? "0";
+    } else {
+      params['need_num'] = needNum ?? "0";
     }
+
     if (!Utils.isEmpty(eventName)) {
       params["event_name"] = eventName!;
     }
@@ -776,9 +773,9 @@ class THERRepository extends GetxService {
 
   // 员工状态的工作流
   Future<HttpResult<JobListAppliedWorkFlowEntity>> fetchAppliedWorkFlow(
-      String? appliedId, {
-        CancelToken? cancelToken,
-      }) async {
+    String? appliedId, {
+    CancelToken? cancelToken,
+  }) async {
     //参数
     Map<String, String> params = {};
     if (!Utils.isEmpty(appliedId)) {
@@ -801,5 +798,4 @@ class THERRepository extends GetxService {
     }
     return result.convert();
   }
-
 }

+ 465 - 0
packages/cs_domain/lib/repository/th_oa_repository.dart

@@ -0,0 +1,465 @@
+import 'package:domain/entity/response/t_h_o_a_attachment_entity.dart';
+import 'package:domain/entity/response/t_h_o_a_labour_detail_entity.dart';
+import 'package:domain/entity/response/t_h_o_a_labour_table_entity.dart';
+import 'package:domain/entity/response/t_h_upload_file_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_request_index_entity.dart';
+import '../entity/response/labour_request_work_flow_entity.dart';
+
+/// 泰国的 OA 的数据仓库
+class THOARepository extends GetxService {
+  HttpProvider httpProvider;
+
+  THOARepository({required this.httpProvider});
+
+  /// 获取OA用工请求的筛选选项
+  Future<HttpResult<LabourRequestIndexEntity>> fetchLabourRequestIndex({
+    CancelToken? cancelToken,
+  }) async {
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiOALabourRequestOptionTH,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      final json = result.getDataJson();
+      var data = LabourRequestIndexEntity.fromJson(json!);
+      //重新赋值data或list
+      return result.convert<LabourRequestIndexEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 获取OA用工请求的列表
+  Future<HttpResult<THOALabourTableEntity>> fetchLabourRequestTable({
+    required int curPage,
+    String? divisionId,
+    String? outletId,
+    String? status,
+    String? startDate,
+    String? endDate,
+    CancelToken? cancelToken,
+  }) async {
+    Map<String, String> params = {};
+    params['cur_page'] = curPage.toString();
+    params['cur_page'] = curPage.toString();
+    if (Utils.isNotEmpty(divisionId)) {
+      params['division_id'] = divisionId!;
+    }
+    if (Utils.isNotEmpty(outletId)) {
+      params['outlet_id'] = outletId!;
+    }
+    if (Utils.isNotEmpty(status)) {
+      params['status'] = status!;
+    }
+    if (Utils.isNotEmpty(startDate)) {
+      params['start_date'] = startDate!;
+    }
+    if (Utils.isNotEmpty(endDate)) {
+      params['end_date'] = endDate!;
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiOALabourRequestTableTH,
+      params: params,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      final json = result.getDataJson();
+      var data = THOALabourTableEntity.fromJson(json!);
+      return result.convert<THOALabourTableEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 根据ID获取主列表的Item数据,用于刷新Item
+  Future<HttpResult<THOALabourTableEntity>> fetchItemByRequestId(
+    String? requestId, {
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    params["cur_page"] = "1";
+    params["page_size"] = "1";
+
+    if (!Utils.isEmpty(requestId)) {
+      params["request_id"] = requestId!;
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiOALabourRequestTableTH,
+      params: params,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      final json = result.getDataJson();
+      var data = THOALabourTableEntity.fromJson(json!);
+      return result.convert<THOALabourTableEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 撤回用工请求
+  Future<HttpResult> recallLabourRequest(
+    String? requestId, {
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    params['request_id'] = requestId ?? "";
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiOALabourRequestRecallTH,
+      method: HttpMethod.POST,
+      params: params,
+      networkDebounce: true,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      return result.convert();
+    }
+    return result.convert();
+  }
+
+  /// 删除用工请求
+  Future<HttpResult> deleteLabourRequest(
+    String? requestId, {
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    params['request_id'] = requestId ?? "";
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiOALabourRequestDeleteTH,
+      method: HttpMethod.POST,
+      params: params,
+      networkDebounce: true,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      return result.convert();
+    }
+    return result.convert();
+  }
+
+  ///  用工的审核详情工作流列表
+  Future<HttpResult<LabourRequestWorkFlowEntity>> fetchLabourRequestWorkFlow(
+    String? requestId, {
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    if (!Utils.isEmpty(requestId)) {
+      params["request_id"] = requestId!;
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiOALabourRequestWorkflowTH,
+      params: params,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      final json = result.getDataJson();
+      var data = LabourRequestWorkFlowEntity.fromJson(json!);
+      return result.convert<LabourRequestWorkFlowEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 用工请求 Add Option
+  Future<HttpResult<THOALabourDetailEntity>> fetchLabourRequestAddOption({
+    CancelToken? cancelToken,
+  }) async {
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiOALabourRequestAddOptionTH,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      final json = result.getDataJson();
+      var data = THOALabourDetailEntity.fromJson(json!);
+      return result.convert<THOALabourDetailEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 用工请求 Edit Detail
+  Future<HttpResult<THOALabourDetailEntity>> fetchLabourRequestEditDetail(
+    String? requestId, {
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    params['request_id'] = requestId ?? "";
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiOALabourRequestDetailTH,
+      params: params,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      final json = result.getDataJson();
+      var data = THOALabourDetailEntity.fromJson(json!);
+      return result.convert<THOALabourDetailEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// 添加用工请求提交
+  Future<HttpResult> addLabourRequestSubmit({
+    required String? jobTitleId,
+    required String? startTime,
+    required String? endTime,
+    String? repeatStart,
+    String? repeatEnd,
+    required String? outletId,
+    int? sexLimit,
+    String? needNum,
+    String? maleLimit,
+    String? femaleLimit,
+    String? description,
+    String? employmentType,
+    String? eventName,
+    String? eventType,
+    String? passengers,
+    String? estRevenue,
+    String? position,
+    String? estCost,
+    String? attUrl,
+    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['repeat_start'] = repeatStart ?? "";
+    params['repeat_end'] = repeatEnd ?? "";
+
+    params['sex_limit'] = sexLimit?.toString() ?? "0";
+    if (sexLimit == 1) {
+      params['male_limit'] = maleLimit ?? "0";
+      params['female_limit'] = femaleLimit ?? "0";
+    } else {
+      params['need_num'] = needNum ?? "0";
+    }
+
+    params['description'] = description ?? "";
+    params['employment_type'] = employmentType ?? "";
+    params['event_name'] = eventName ?? "";
+    params['event_type'] = eventType ?? "";
+    params['passengers'] = passengers ?? "";
+    params['est_revenue'] = estRevenue ?? "";
+    params['position'] = position ?? "";
+    params['est_cost'] = estCost ?? "";
+    params['att_url'] = attUrl ?? "";
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiOALabourRequestAddSubmitTH,
+      method: HttpMethod.POST,
+      params: params,
+      networkDebounce: true,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      return result.convert();
+    }
+    return result.convert();
+  }
+
+  /// 编辑用工请求提交
+  Future<HttpResult> editLabourRequestSubmit({
+    required String? requestId,
+    required String? jobTitleId,
+    required String? startTime,
+    required String? endTime,
+    String? repeatStart,
+    String? repeatEnd,
+    required String? outletId,
+    int? sexLimit,
+    String? needNum,
+    String? maleLimit,
+    String? femaleLimit,
+    String? description,
+    String? employmentType,
+    String? eventName,
+    String? eventType,
+    String? passengers,
+    String? estRevenue,
+    String? position,
+    String? estCost,
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    params['request_id'] = requestId ?? "";
+    params['job_title_id'] = jobTitleId ?? "";
+    params['start_time'] = startTime ?? "";
+    params['end_time'] = endTime ?? "";
+    params['outlet_id'] = outletId ?? "";
+    params['repeat_start'] = repeatStart ?? "";
+    params['repeat_end'] = repeatEnd ?? "";
+
+    params['sex_limit'] = sexLimit?.toString() ?? "0";
+    if (sexLimit == 1) {
+      params['male_limit'] = maleLimit ?? "0";
+      params['female_limit'] = femaleLimit ?? "0";
+    } else {
+      params['need_num'] = needNum ?? "0";
+    }
+
+    params['description'] = description ?? "";
+    params['employment_type'] = employmentType ?? "";
+    params['event_name'] = eventName ?? "";
+    params['event_type'] = eventType ?? "";
+    params['passengers'] = passengers ?? "";
+    params['est_revenue'] = estRevenue ?? "";
+    params['position'] = position ?? "";
+    params['est_cost'] = estCost ?? "";
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiOALabourRequestEditSubmitTH,
+      method: HttpMethod.POST,
+      params: params,
+      networkDebounce: true,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      return result.convert();
+    }
+    return result.convert();
+  }
+
+  /// 泰国上传文件
+  Future<HttpResult<THUploadFileEntity>> uploadFile(
+    String? filePath, {
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+
+    //文件
+    Map<String, String> fileParams = {};
+    if (Utils.isNotEmpty(filePath)) {
+      fileParams['file'] = filePath!;
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiUploadFileTH,
+      method: HttpMethod.POST,
+      params: params,
+      paths: fileParams,
+      networkDebounce: true,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      final json = result.getDataJson();
+      var data = THUploadFileEntity.fromJson(json!);
+      return result.convert<THUploadFileEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// OA 附件列表
+  Future<HttpResult<THOAAttachmentEntity>> fetchAttachmentTable({
+    required String? requestId,
+    required int curPage,
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    params['request_id'] = requestId ?? "";
+    params['cur_page'] = curPage.toString();
+    params['page_size'] = "10";
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiOAAttachmentListTH,
+      params: params,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      final json = result.getDataJson();
+      var data = THOAAttachmentEntity.fromJson(json!);
+      return result.convert<THOAAttachmentEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+  /// OA附件添加
+  Future<HttpResult> addAttachmentSubmit({
+    required String? requestId,
+    required String? attUrl,
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    params['request_id'] = requestId ?? "";
+    params['att_url'] = attUrl ?? "";
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiOAAttachmentAddTH,
+      method: HttpMethod.POST,
+      params: params,
+      networkDebounce: true,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      return result.convert();
+    }
+    return result.convert();
+  }
+
+  /// OA附件删除
+  Future<HttpResult> deleteAttachmentSubmit({
+    required String? attId,
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    params['att_id'] = attId ?? "";
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiOAAttachmentDeleteTH,
+      method: HttpMethod.POST,
+      params: params,
+      networkDebounce: true,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      return result.convert();
+    }
+    return result.convert();
+  }
+
+}

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

@@ -1,4 +1,3 @@
-import 'package:domain/constants/api_constants.dart';
 import 'package:domain/repository/auth_repository.dart';
 import 'package:domain/repository/job_repository.dart';
 import 'package:domain/repository/job_sg_repository.dart';
@@ -7,17 +6,15 @@ 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/th_oa_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';
 import 'package:domain/repository/uk_review_repository.dart';
 import 'package:domain/repository/sg_agency_repository.dart';
 import 'package:plugin_basic/basic_export.dart';
-import 'package:plugin_basic/dio_interceptors/interceptor_auth_dio.dart';
-import 'package:plugin_basic/dio_interceptors/interceptor_status_code_dio.dart';
 import 'package:plugin_basic/service/http_provider_injection.dart';
 import 'package:plugin_basic/service/user_service.dart';
-import 'package:plugin_platform/http/http_provider.dart';
 import 'package:shared/utils/log_utils.dart';
 
 /*
@@ -59,6 +56,7 @@ class GlobalServicesInjection {
     //MS/NL的数据仓库注入
     Get.lazyPut(() => MSRepository(httpProvider: Get.find()));
     Get.lazyPut(() => THERRepository(httpProvider: Get.find()));
+    Get.lazyPut(() => THOARepository(httpProvider: Get.find()));
 
     // 调用额外的依赖注入逻辑(如果提供了)
     if (additionalDependencies != null) {

+ 9 - 1
packages/cs_plugin_platform/lib/engine/network/network_engine.dart

@@ -106,6 +106,9 @@ class NetworkEngine {
             //传入压缩之后的流对象
             if (stream != null) {
               map[key] = MultipartFile.fromBytes(stream, filename: "file");
+            }else {
+              //如果压缩失败使用原始路径File
+              map[key] = MultipartFile.fromFileSync(value, filename: "file");
             }
           }
         }
@@ -127,7 +130,12 @@ class NetworkEngine {
             );
 
             //传入压缩之后的流对象
-            map[key] = MultipartFile.fromBytes(stream, filename: "file_stream");
+            if (stream != null) {
+              map[key] = MultipartFile.fromBytes(stream, filename: "file_stream");
+            }else {
+              //如果压缩失败使用原始流
+              map[key] = MultipartFile.fromBytes(value, filename: "file_stream");
+            }
           }
         }
       }

+ 4 - 1
packages/cs_plugin_platform/pubspec.yaml

@@ -67,6 +67,9 @@ dependencies:
   path_provider: 2.1.4
   synchronized: ^3.1.0+1
 
+  # 文件选择器
+  file_picker: ^8.0.7
+
   # Hive本地存储
   hive: ^2.2.3
   hive_flutter: ^1.1.0
@@ -76,7 +79,7 @@ dependencies:
   shared_preferences: 2.3.1
 
   # 5.5.0 报错,这里强制指定版本适配 Flutter 版本 3.24.0
-  win32: 5.5.4
+  win32: ^5.10.1
 
 flutter:
   uses-material-design: true

BIN
packages/cs_resources/assets/base_service/red_delete_icon.webp


+ 1 - 0
packages/cs_resources/lib/constants/color_constants.dart

@@ -38,6 +38,7 @@ class ColorConstants {
   static const Color textBlue56AAFF = Color(0xFF56AAFF);
   static const Color textYellowF8AE00 = Color(0xFFF8AE00);
   static const Color textPurpleFF35EE = Color(0xFFFF35EE);
+  static const Color redFF4066 = Color(0xFFFE4066);
 
   //黑暗模式的一些色值
   static const Color darkBlackBg = Color(0xFF0F0F0F);

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

@@ -24,6 +24,7 @@ class Assets {
   static const String baseServicePageNoData = 'assets/base_service/page_no_data.webp';
   static const String baseServiceRatingSelected = 'assets/base_service/rating_selected.webp';
   static const String baseServiceRatingUnselected = 'assets/base_service/rating_unselected.webp';
+  static const String baseServiceRedDeleteIcon = 'assets/base_service/red_delete_icon.webp';
   static const String baseServiceTitleBarFilterIcon = 'assets/base_service/title_bar_filter_icon.webp';
   static const String baseServiceTriangleDropDown = 'assets/base_service/triangle_drop_down.webp';
   static const String baseServiceTriangleDropDownIcon = 'assets/base_service/triangle_drop_down_icon.webp';

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

@@ -305,6 +305,8 @@ const Map<String, String> en_US = {
   'ID Card No.': 'ID Card No.',
   'Select All': 'Select All',
   'Employer': 'Employer',
+  'Latest Operated': 'Latest Operated',
+  'Are you sure you want to delete this attachment?':'Are you sure you want to delete this attachment?',
 
   //插件的国际化
   'Pull to refresh': 'Pull to refresh',

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

@@ -305,6 +305,8 @@ const Map<String, String> zh_CN = {
   'ID Card No.': '身份证号码',
   'Select All': '选择全部',
   'Employer': '雇主',
+  'Latest Operated': '最后操作人员',
+  'Are you sure you want to delete this attachment?':'你确定要删除此附件吗?',
 
   //插件的国际化
   'Pull to refresh': '下拉刷新',

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

@@ -149,6 +149,7 @@ class RouterPath {
   static const THMain = '/th/main';   //泰国的首页
   static const THLabourRequestOA = '/th/labour/request/oa';  //泰国的用工请求页面
   static const THLabourRequestAddOA = '/th/labour/request/add/oa';  //泰国的用工请求添加
+  static const THLabourRequestWorkflowOA = '/th/labour/request/workflow/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

+ 2 - 2
packages/cs_widgets/lib/my_text_view.dart

@@ -91,7 +91,7 @@ class MyTextView extends StatelessWidget {
     this.fontSize,
     this.textDecoration,
     this.decorationColor,
-    this.decorationThickness,  //下划线
+    this.decorationThickness, //下划线
     this.decorationStyle,
     this.isTextEllipsis,
     this.isFontLight,
@@ -189,7 +189,7 @@ class MyTextView extends StatelessWidget {
         borderRadius: BorderRadius.all(Radius.circular(cornerRadius ?? 0)),
       ),
       padding: EdgeInsets.fromLTRB(paddingLeft ?? 0, paddingTop ?? 0, paddingRight ?? 0, paddingBottom ?? 0),
-      child: _childText(),
+      child: textAlign == TextAlign.center ? Center(child: _childText()) : _childText(),
     );
   }