Przeglądaj źródła

批量添加预选员工的逻辑
其他的反馈修改

liukai 1 dzień temu
rodzic
commit
47c20d70f4

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

@@ -79,10 +79,10 @@ class _AppliedAddStaffState extends State<ErAppliedAddStaff> {
                 searchBarBgColor: Colors.white,
                 searchBarBorderRadius: 15,
                 searchBarBorder: Border.all(
-                  color: Color(0XFFC3C3C3), // 设置边框颜色为灰色
+                  color: const Color(0XFFC3C3C3), // 设置边框颜色为灰色
                   width: 0.5, // 设置边框宽度
                 ),
-                textHintColor: Color(0XFFAFB3B7),
+                textHintColor: const Color(0XFFAFB3B7),
                 textColor: ColorConstants.black33,
                 onSearch: (keyword) {
                   controller.doSearch(keyword);

+ 60 - 0
packages/cpt_th/lib/modules/labour/labour_request_list/dialog/batch_pre_selected_controller.dart

@@ -0,0 +1,60 @@
+import 'package:domain/entity/response/t_h_o_a_attachment_entity.dart';
+import 'package:domain/repository/th_er_repository.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';
+
+import '../../../job_er/applied_er/widget/er_applied_add_staff.dart';
+import '../../../job_er/job_list_er/widget/pre_add_staff.dart';
+import 'pre_add_request_dialog.dart';
+
+class BatchPreSelectedController extends GetxController with DioCancelableMixin {
+  final THERRepository _thRepository = Get.find();
+
+  String? selectedRequestIds;
+  String? selectedStaffIds;
+
+  @override
+  void onReady() {
+    super.onReady();
+  }
+
+  @override
+  void onClose() {
+    super.onClose();
+  }
+
+  //选择员工
+  void showStaffSelectedDialog() {
+    DialogEngine.show(
+      widget: PreAddStaff(
+        confirmAction: (selectedIds) {
+          Log.d("选中的selectedIds:$selectedIds");
+          selectedStaffIds = selectedIds;
+          update();
+        },
+      ),
+    );
+  }
+
+  //选择Labour Request的多选
+  void showRequestSelectedDialog() {
+    DialogEngine.show(
+      widget: PreAddRequestDialog(
+        confirmAction: (selectedIds) {
+          Log.d("选中的selectedIds:$selectedIds");
+          selectedRequestIds = selectedIds;
+          update();
+        },
+      ),
+    );
+  }
+}

+ 228 - 0
packages/cpt_th/lib/modules/labour/labour_request_list/dialog/batch_pre_selected_dialog.dart

@@ -0,0 +1,228 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/base/base_stateless_page.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:shared/utils/util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:cs_resources/constants/color_constants.dart';
+import 'dart:ui';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/widgets.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/shatter/form_require_text.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'batch_pre_selected_controller.dart';
+
+/*
+ * 批量预选的弹窗
+ */
+class BatchPreSelectedDialog extends BaseStatelessPage<BatchPreSelectedController> {
+  void Function(String? requestIds, String? staffIds) confirmAction;
+  VoidCallback? cancelAction;
+
+  BatchPreSelectedDialog({
+    required this.confirmAction,
+    this.cancelAction,
+  });
+
+  @override
+  BatchPreSelectedController createRawController() {
+    return BatchPreSelectedController();
+  }
+
+  @override
+  void initState() {}
+
+  @override
+  Widget buildWidget(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return Column(
+        crossAxisAlignment: CrossAxisAlignment.center,
+        mainAxisAlignment: MainAxisAlignment.center,
+        children: [
+          //Title (如果使用 Container 为最外层容器则默认为 match_parent 的效果,除非我们限制宽度和最大高度最小高度)
+          Container(
+            width: double.infinity,
+            decoration: const BoxDecoration(
+              color: Colors.white,
+              borderRadius: BorderRadius.all(Radius.circular(15)),
+            ),
+            child: Column(
+              crossAxisAlignment: CrossAxisAlignment.start,
+              children: [
+                MyTextView(
+                  'Pre Selected'.tr,
+                  fontSize: 19,
+                  isFontMedium: true,
+                  textColor: ColorConstants.black,
+                  marginTop: 15,
+                  marginBottom: 15,
+                ).alignCenter(),
+
+                //选择用工请求
+                FormRequireText(
+                  text: 'Request'.tr,
+                  textColor: ColorConstants.black33,
+                  fontSize: 14,
+                  fontWeight: FontWeight.w400,
+                ).marginOnly(left: 17),
+
+                //选择工请求的选择框
+                Container(
+                  padding: const EdgeInsets.only(left: 15),
+                  margin: const EdgeInsets.only(left: 17, right: 17, top: 8),
+                  height: 48,
+                  decoration: BoxDecoration(
+                    color: hexToColor("#ECECEC"),
+                    borderRadius: const BorderRadius.all(Radius.circular(5)),
+                  ),
+                  child: Row(
+                    mainAxisSize: MainAxisSize.max,
+                    crossAxisAlignment: CrossAxisAlignment.center,
+                    mainAxisAlignment: MainAxisAlignment.start,
+                    children: [
+                      MyTextView(
+                        controller.selectedRequestIds ?? "",
+                        hint: 'Choose Labour Request'.tr,
+                        fontSize: 14,
+                        textHintColor: ColorConstants.textBlackHint,
+                        isFontRegular: true,
+                        textColor: ColorConstants.black33,
+                      ).expanded(),
+                      MyTextView(
+                        'Select'.tr,
+                        fontSize: 14,
+                        boxWidth: 64,
+                        boxHeight: 48,
+                        textAlign: TextAlign.center,
+                        onClick: controller.showRequestSelectedDialog,
+                        isFontRegular: true,
+                        textColor: ColorConstants.white,
+                      ).decorated(
+                        color: ColorConstants.string2Color("#4AA3FF"),
+                        borderRadius: const BorderRadius.only(topRight: Radius.circular(5), bottomRight: Radius.circular(5)),
+                      )
+                    ],
+                  ),
+                ),
+
+                //选择员工
+                FormRequireText(
+                  text: 'Employee'.tr,
+                  textColor: ColorConstants.black33,
+                  fontSize: 14,
+                  fontWeight: FontWeight.w400,
+                ).marginOnly(left: 17, top: 15),
+
+                //选择员工的选择框
+                Container(
+                  padding: const EdgeInsets.only(left: 15),
+                  margin: const EdgeInsets.only(left: 17, right: 17, top: 8),
+                  height: 48,
+                  decoration: BoxDecoration(
+                    color: hexToColor("#ECECEC"),
+                    borderRadius: const BorderRadius.all(Radius.circular(5)),
+                  ),
+                  child: Row(
+                    mainAxisSize: MainAxisSize.max,
+                    crossAxisAlignment: CrossAxisAlignment.center,
+                    mainAxisAlignment: MainAxisAlignment.start,
+                    children: [
+                      MyTextView(
+                        controller.selectedStaffIds ?? "",
+                        hint: 'Choose Staff'.tr,
+                        fontSize: 14,
+                        textHintColor: ColorConstants.textBlackHint,
+                        isFontRegular: true,
+                        textColor: ColorConstants.black33,
+                      ).expanded(),
+                      MyTextView(
+                        'Select'.tr,
+                        fontSize: 14,
+                        boxWidth: 64,
+                        boxHeight: 48,
+                        textAlign: TextAlign.center,
+                        isFontRegular: true,
+                        onClick: controller.showStaffSelectedDialog,
+                        textColor: ColorConstants.white,
+                      ).decorated(
+                        color: ColorConstants.string2Color("#4AA3FF"),
+                        borderRadius: const BorderRadius.only(topRight: Radius.circular(5), bottomRight: Radius.circular(5)),
+                      )
+                    ],
+                  ),
+                ),
+
+                //分割线
+                Container(
+                  margin: const EdgeInsets.only(top: 25),
+                  color: const Color(0XFFCECECE),
+                  height: 0.5,
+                ),
+
+                //按钮组
+                Row(
+                  children: [
+                    Expanded(
+                        flex: 1,
+                        child: InkWell(
+                          onTap: () {
+                            onCancel();
+                            cancelAction?.call();
+                          },
+                          child: MyTextView(
+                            "Cancel".tr,
+                            fontSize: 17.5,
+                            isFontMedium: true,
+                            textAlign: TextAlign.center,
+                            textColor: const Color(0XFF0085C4),
+                            cornerRadius: 3,
+                            borderWidth: 1,
+                          ),
+                        )),
+                    Container(
+                      color: const Color(0xff09141F).withOpacity(0.13),
+                      width: 0.5,
+                    ),
+                    Expanded(
+                        flex: 1,
+                        child: InkWell(
+                          onTap: () async {
+                            if (Utils.isNotEmpty(controller.selectedRequestIds)) {
+                              if (Utils.isNotEmpty(controller.selectedStaffIds)) {
+                                onCancel();
+                                confirmAction(controller.selectedRequestIds, controller.selectedStaffIds);
+                              } else {
+                                ToastEngine.show("Select Employee");
+                              }
+                            } else {
+                              ToastEngine.show("Select Labour Request");
+                            }
+                          },
+                          child: MyTextView(
+                            "Confirm".tr,
+                            marginLeft: 10,
+                            fontSize: 17.5,
+                            isFontMedium: true,
+                            textAlign: TextAlign.center,
+                            textColor: const Color(0XFF0085C4),
+                            cornerRadius: 3,
+                          ),
+                        )),
+                  ],
+                ).constrained(height: 46),
+              ],
+            ),
+          ),
+        ],
+      ).constrained(width: 295);
+    });
+  }
+
+  //取消弹框
+  void onCancel() async {
+    SmartDialog.dismiss();
+  }
+}

+ 209 - 0
packages/cpt_th/lib/modules/labour/labour_request_list/dialog/pre_add_request_controller.dart

@@ -0,0 +1,209 @@
+import 'package:domain/entity/response/labour_request_index_entity.dart';
+import 'package:domain/entity/response/t_h_o_a_labour_table_entity.dart';
+import 'package:domain/repository/th_er_repository.dart';
+import 'package:domain/repository/th_oa_repository.dart';
+import 'package:flutter/material.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:plugin_platform/http/http_result.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/picker/date_picker_util.dart';
+import 'package:widgets/picker/option_pick_util.dart';
+import 'package:widgets/widget_export.dart';
+
+class PreAddRequestController extends GetxController with DioCancelableMixin {
+  final THOARepository _thOARepository = Get.find();
+
+  final TextEditingController searchController = TextEditingController();
+  DateTime? selectedStartDate;
+  DateTime? selectedEndDate;
+  String? selectedDepartmentId;
+  String? selectedDepartmentName;
+
+  //页面的列表数据
+  List<THOALabourTableRows> datas = [];
+  LabourRequestIndexEntity? indexOptions;
+
+  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;
+    fetchLabourRequestList();
+  }
+
+  // Refresh 加载事件
+  Future loadMore() async {
+    _curPage++;
+    fetchLabourRequestList();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _curPage = 1;
+    _needShowPlaceholder = true;
+    fetchLabourRequestList();
+  }
+
+  /// 获取列表数据
+  Future fetchLabourRequestList() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    }
+
+    // 并发执行两个请求
+    var futures = [
+      _thOARepository.fetchLabourRequestTable(
+        startDate: DateTimeUtils.formatDate(selectedStartDate, format: "yyyy-MM-dd"),
+        endDate: DateTimeUtils.formatDate(selectedEndDate, format: "yyyy-MM-dd"),
+        //只查询pending
+        status: '1',
+        outletId: selectedDepartmentId,
+        curPage: _curPage,
+        cancelToken: cancelToken,
+      ),
+      indexOptions == null
+          ? _thOARepository.fetchLabourRequestIndex(
+              cancelToken: cancelToken,
+            )
+          : Future(() => HttpResult(isSuccess: true).convert(data: indexOptions!)),
+    ];
+
+    //拿到结果
+    var results = await Future.wait(futures);
+    var listResult = results[0] as HttpResult<THOALabourTableEntity>;
+    var optionResult = results[1] as HttpResult<LabourRequestIndexEntity>;
+
+    //选项数据
+    if (indexOptions == null && optionResult.isSuccess) {
+      indexOptions = optionResult.data!;
+    }
+
+    // 处理数据
+    if (listResult.isSuccess) {
+      handleList(listResult.data?.rows);
+    } else {
+      errorMessage = listResult.errorMsg;
+      changeLoadingState(LoadState.State_Error);
+    }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void handleList(List<THOALabourTableRows>? list) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      if (_curPage == 1) {
+        //刷新的方式
+        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();
+    fetchLabourRequestList();
+  }
+
+  @override
+  void onClose() {
+    datas.clear();
+    super.onClose();
+  }
+
+  //根据筛选条件搜索
+  void doSearch() {
+    refreshController.callRefresh();
+  }
+
+  //筛选部门
+  void pickerOutlet() {
+    if (indexOptions == null) return;
+
+    int selectedDepartmentIndex;
+    if (selectedDepartmentId == null) {
+      selectedDepartmentIndex = 0;
+    } else {
+      selectedDepartmentIndex = indexOptions!.outletList!.indexWhere((department) => department.value.toString() == selectedDepartmentId);
+    }
+
+    if (selectedDepartmentIndex < 0) {
+      selectedDepartmentIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: indexOptions!.outletList!.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedDepartmentIndex,
+      onPickerChanged: (_, index) {
+        selectedDepartmentId = indexOptions!.outletList![index].value!.toString();
+        selectedDepartmentName = indexOptions!.outletList![index].txt!.toString();
+        update();
+      },
+    );
+  }
+
+  /// 筛选开始日期
+  void pickerStartDate() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: selectedStartDate,
+      onDateTimeChanged: (date) {
+        selectedStartDate = date;
+        update();
+      },
+      title: "Start Date".tr,
+    );
+  }
+
+  /// 筛选结束日期
+  void pickerEndDate() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: selectedEndDate ?? selectedStartDate,
+      onDateTimeChanged: (date) {
+        selectedEndDate = date;
+        update();
+      },
+      title: "End Date".tr,
+    );
+  }
+}

+ 427 - 0
packages/cpt_th/lib/modules/labour/labour_request_list/dialog/pre_add_request_dialog.dart

@@ -0,0 +1,427 @@
+import 'dart:ui';
+
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/response/t_h_applied_employee_entity.dart';
+import 'package:domain/entity/response/t_h_o_a_labour_table_entity.dart';
+import 'package:domain/entity/response/t_h_pre_selected_staff_table_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package: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/search_app_bar.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'pre_add_request_controller.dart';
+
+/*
+ * 添加多个用工请求的选择弹窗
+ */
+class PreAddRequestDialog extends StatefulWidget {
+  void Function(String selectIds)? confirmAction;
+
+  PreAddRequestDialog({this.confirmAction});
+
+  @override
+  State<PreAddRequestDialog> createState() => _AppliedAddStaffState();
+}
+
+class _AppliedAddStaffState extends State<PreAddRequestDialog> {
+  @override
+  void initState() {
+    super.initState();
+    Get.put(PreAddRequestController());
+  }
+
+  @override
+  void dispose() {
+    super.dispose();
+    Get.delete<PreAddRequestController>();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return GetBuilder<PreAddRequestController>(
+      assignId: true,
+      builder: (controller) {
+        return Container(
+          width: 300,
+          height: 600,
+          decoration: const BoxDecoration(
+            color: Color(0XFFF7F7F7),
+            borderRadius: BorderRadius.all(Radius.circular(15)),
+          ),
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              Center(
+                child: MyTextView(
+                  "Choose Request".tr,
+                  fontSize: 19,
+                  isFontMedium: true,
+                  textColor: ColorConstants.black,
+                  marginTop: 22,
+                  marginBottom: 15,
+                  marginLeft: 22,
+                  marginRight: 22,
+                ),
+              ),
+
+              MyTextView(
+                "Outlet".tr,
+                fontSize: 13,
+                marginLeft: 15,
+                isFontRegular: true,
+                textColor: ColorConstants.black33,
+              ),
+
+              Row(
+                children: [
+                  MyTextView(
+                    controller.selectedDepartmentName ?? "",
+                    hint: 'Outlet'.tr,
+                    textHintColor: ColorConstants.textBlackHint,
+                    fontSize: 13,
+                    isFontRegular: true,
+                    textColor: ColorConstants.black33,
+                  ).expanded(),
+                  const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6)
+                ],
+              )
+                  .constrained(height: 32)
+                  .paddingOnly(left: 15, right: 12)
+                  .decorated(
+                    color: hexToColor("#ECECEC"),
+                    borderRadius: const BorderRadius.all(Radius.circular(5)),
+                  )
+                  .onTap(controller.pickerOutlet)
+                  .marginOnly(left: 15, right: 15, top: 8),
+
+              Row(
+                children: [
+                  Column(
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    children: [
+                      MyTextView(
+                        "Start Date".tr,
+                        fontSize: 13,
+                        marginLeft: 15,
+                        marginTop: 8,
+                        isFontRegular: true,
+                        textColor: ColorConstants.black33,
+                      ),
+                      Row(
+                        children: [
+                          MyTextView(
+                            controller.selectedStartDate == null ? '' : DateTimeUtils.formatDate(controller.selectedStartDate, format: "yyyy-MM-dd"),
+                            hint: 'Start Date'.tr,
+                            textHintColor: ColorConstants.textBlackHint,
+                            fontSize: 13,
+                            isFontRegular: true,
+                            textColor: ColorConstants.black33,
+                          ).expanded(),
+                          const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6)
+                        ],
+                      )
+                          .constrained(height: 32)
+                          .paddingOnly(left: 15, right: 9)
+                          .decorated(
+                            color: hexToColor("#ECECEC"),
+                            borderRadius: const BorderRadius.all(Radius.circular(5)),
+                          )
+                          .onTap(controller.pickerStartDate)
+                          .marginOnly(left: 15, top: 8),
+                    ],
+                  ).expanded(),
+                  Column(
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    children: [
+                      MyTextView(
+                        "End Date".tr,
+                        fontSize: 13,
+                        marginLeft: 15,
+                        marginTop: 8,
+                        isFontRegular: true,
+                        textColor: ColorConstants.black33,
+                      ),
+                      Row(
+                        children: [
+                          MyTextView(
+                            controller.selectedEndDate == null ? '' : DateTimeUtils.formatDate(controller.selectedEndDate, format: "yyyy-MM-dd"),
+                            hint: 'End Date'.tr,
+                            textHintColor: ColorConstants.textBlackHint,
+                            fontSize: 13,
+                            isFontRegular: true,
+                            textColor: ColorConstants.black33,
+                          ).expanded(),
+                          const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6)
+                        ],
+                      )
+                          .constrained(height: 32)
+                          .paddingOnly(left: 15, right: 9)
+                          .decorated(
+                            color: hexToColor("#ECECEC"),
+                            borderRadius: const BorderRadius.all(Radius.circular(5)),
+                          )
+                          .onTap(controller.pickerEndDate)
+                          .marginOnly(left: 15, top: 8),
+                    ],
+                  ).expanded(),
+                ],
+              ),
+
+              MyButton(
+                type: ClickType.throttle,
+                milliseconds: 500,
+                onPressed: () {
+                  FocusScope.of(context).unfocus();
+                  controller.doSearch();
+                },
+                text: "Submit".tr,
+                textColor: ColorConstants.white,
+                fontSize: 13,
+                minHeight: 32,
+                radius: 5,
+                backgroundColor: hexToColor("#4AA3FF"),
+                fontWeight: FontWeight.w400,
+              ).marginOnly(left: 15, right: 15, top: 2, bottom: 2),
+
+              Container(
+                color: Colors.white,
+                child: EasyRefresh(
+                  header: ClassicHeader(
+                    dragText: 'Pull to refresh'.tr,
+                    armedText: 'Release ready'.tr,
+                    readyText: 'Refreshing...'.tr,
+                    processingText: 'Refreshing...'.tr,
+                    processedText: 'Succeeded'.tr,
+                    noMoreText: 'No more'.tr,
+                    failedText: 'Failed'.tr,
+                    messageText: 'Last updated at %T'.tr,
+                    textStyle: const TextStyle(color: ColorConstants.black66, fontSize: 14),
+                    messageStyle: const TextStyle(color: ColorConstants.black66, fontSize: 12),
+                    iconTheme: const IconThemeData(color: ColorConstants.black66),
+                    backgroundColor: Colors.transparent,
+                  ),
+                  footer: ClassicFooter(
+                    dragText: 'Pull to load'.tr,
+                    armedText: 'Release ready'.tr,
+                    readyText: 'Loading...'.tr,
+                    processingText: 'Loading...'.tr,
+                    processedText: 'Succeeded'.tr,
+                    noMoreText: 'No more'.tr,
+                    failedText: 'Failed'.tr,
+                    showMessage: false,
+                    triggerOffset: 50,
+                    iconDimension: 22,
+                    textStyle: const TextStyle(color: ColorConstants.black66, fontSize: 14),
+                    messageStyle: const TextStyle(color: ColorConstants.black66, fontSize: 12),
+                    iconTheme: const IconThemeData(color: ColorConstants.black66),
+                    backgroundColor: Colors.transparent,
+                  ),
+                  controller: controller.refreshController,
+                  onRefresh: controller.onRefresh,
+                  onLoad: controller.loadMore,
+                  child: LoadStateLayout(
+                    themeColor: ColorConstants.black66,
+                    state: controller.loadingState,
+                    errorMessage: controller.errorMessage,
+                    errorRetry: () {
+                      controller.retryRequest();
+                    },
+                    successSliverWidget: [
+                      SliverList(
+                          delegate: SliverChildBuilderDelegate(
+                        (context, index) {
+                          return _buildStaffItem(controller.datas[index], () {
+                            /// Item选中与未选中设置
+                            controller.datas[index].isSelected = !controller.datas[index].isSelected;
+                            controller.update();
+                          });
+                        },
+                        childCount: controller.datas.length,
+                      ))
+                    ],
+                  ),
+                ).paddingZero,
+              ).expanded(),
+
+              //按钮组
+              Row(
+                children: [
+                  //取消按钮
+                  Expanded(
+                      flex: 1,
+                      child: InkWell(
+                        onTap: () {
+                          onCancel();
+                        },
+                        child: MyTextView(
+                          "Cancel".tr,
+                          fontSize: 17.5,
+                          isFontMedium: true,
+                          textAlign: TextAlign.center,
+                          textColor: const Color(0XFF0085C4),
+                          cornerRadius: 3,
+                          borderWidth: 1,
+                        ),
+                      )),
+
+                  //垂直分割线
+                  Container(
+                    color: const Color(0xff09141F).withOpacity(0.13),
+                    width: 0.5,
+                  ),
+
+                  //同意按钮
+                  Expanded(
+                      flex: 1,
+                      child: InkWell(
+                        onTap: () {
+                          doCallbackAction(controller);
+                        },
+                        child: MyTextView(
+                          "Submit".tr,
+                          marginLeft: 10,
+                          fontSize: 17.5,
+                          isFontMedium: true,
+                          textAlign: TextAlign.center,
+                          textColor: Color(0XFF0085C4),
+                          cornerRadius: 3,
+                        ),
+                      )),
+                ],
+              ).constrained(height: 46),
+            ],
+          ),
+        );
+      },
+    );
+  }
+
+  //取消弹框
+  void onCancel() async {
+    SmartDialog.dismiss();
+  }
+
+  //执行回调
+  void doCallbackAction(PreAddRequestController controller) {
+    onCancel();
+
+    //找到当前选中的
+    var selectedList = controller.datas.where((element) => element.isSelected).toList(growable: false);
+    if (selectedList.isNotEmpty) {
+      var ids = selectedList.map((e) => e.id.toString()).toList(growable: false);
+      String separatedIds = ids.join(',');
+
+      widget.confirmAction?.call(separatedIds);
+    }
+  }
+
+  Widget _buildStaffItem(THOALabourTableRows item, VoidCallback callback) {
+    return Stack(
+      children: [
+        Column(
+          children: [
+            //时间
+            Row(
+              children: [
+                MyTextView(
+                  "Job Time:",
+                  textColor: ColorConstants.black66,
+                  fontSize: 13,
+                  marginRight: 3,
+                  isFontRegular: true,
+                ),
+                MyTextView(
+                  "${item.jobDate} ${item.startTime} ~ ${item.endTime}}",
+                  textColor: ColorConstants.black,
+                  fontSize: 13,
+                  isFontRegular: true,
+                ),
+              ],
+            ),
+
+            //部门
+            Row(
+              children: [
+                MyTextView(
+                  "Outlet:",
+                  marginRight: 3,
+                  textColor: ColorConstants.black66,
+                  fontSize: 13,
+                  isFontRegular: true,
+                ),
+                MyTextView(
+                  item.outletName ?? "-",
+                  textColor: ColorConstants.black,
+                  fontSize: 13,
+                  isFontRegular: true,
+                ),
+              ],
+            ).marginOnly(top: 5),
+
+            //标题
+            Row(
+              children: [
+                MyTextView(
+                  "Job Title:",
+                  marginRight: 3,
+                  textColor: ColorConstants.black66,
+                  fontSize: 13,
+                  isFontRegular: true,
+                ),
+                MyTextView(
+                  item.jobTitle ?? "-",
+                  textColor: ColorConstants.black,
+                  fontSize: 13,
+                  isFontRegular: true,
+                ).expanded(),
+              ],
+            ).marginOnly(top: 5),
+
+            //状态
+            Row(
+              children: [
+                MyTextView(
+                  "Status:",
+                  marginRight: 3,
+                  textColor: ColorConstants.black66,
+                  fontSize: 13,
+                  isFontRegular: true,
+                ),
+                MyTextView(
+                  item.statusShow ?? "-",
+                  textColor: ColorConstants.black,
+                  fontSize: 13,
+                  isFontRegular: true,
+                ),
+              ],
+            ).marginOnly(top: 5),
+
+            Container(
+              margin: const EdgeInsets.only(top: 19),
+              width: double.infinity,
+              height: 1,
+              color: const Color(0XFFF7F7F7),
+            )
+          ],
+        ).paddingOnly(left: 19, right: 20, top: 17),
+
+        //是否勾选
+        MyAssetImage(
+          item.isSelected ? Assets.baseServiceItemSelectedIcon : Assets.baseServiceItemUnselectedGrayIcon,
+          width: 20.5,
+          height: 20.5,
+        ).alignRight().marginOnly(right: 20, top: 17.5),
+      ],
+    ).onTap(callback);
+  }
+}

+ 23 - 1
packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_list_controller.dart

@@ -20,6 +20,7 @@ import 'package:widgets/load_state_layout.dart';
 import 'package:widgets/widget_export.dart';
 
 import '../labour_request_workflow/labour_request_workflow_page.dart';
+import 'dialog/batch_pre_selected_dialog.dart';
 import 'dialog/oa_attach_list_controller.dart';
 import 'dialog/oa_attach_list_dialog.dart';
 import 'labour_request_filter.dart';
@@ -350,9 +351,30 @@ class LabourRequestListController extends GetxController with DioCancelableMixin
     DialogEngine.show(
       widget: PreSelectedStaff(
         requestId: data.id,
-        jobStatus: data.status??0,
+        jobStatus: data.status ?? 0,
       ),
     );
   }
 
+  // 展示批量预选的弹窗
+  void showBatchPreSelectedDialog() {
+    DialogEngine.show(widget: BatchPreSelectedDialog(
+      confirmAction: (requestIds, staffIds) {
+        _requestBatchPreSelected(requestIds, staffIds);
+      },
+    ));
+  }
+
+  //请求接口发送用工请求
+  void _requestBatchPreSelected(String? requestIds, String? staffIds) async {
+    var result = await _thRepository.submitBatchPreSelectEmployees(reqIds: requestIds, eeIds: staffIds, cancelToken: cancelToken);
+
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess("Successful".tr);
+
+      refreshController.callRefresh();
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+  }
 }

+ 40 - 19
packages/cpt_th/lib/modules/labour/labour_request_list/labour_request_list_page.dart

@@ -105,21 +105,42 @@ class _LabourRequestListState extends BaseState<LabourRequestListPage, LabourReq
                   }).marginOnly(right: 15),
                 ],
               ),
-              // 添加按钮
-              MyButton(
-                type: ClickType.throttle,
-                milliseconds: 500,
-                onPressed: () {
-                  FocusScope.of(context).unfocus();
-                  controller.gotoAddLabourPage();
-                },
-                text: "Create New Job Request".tr,
-                textColor: ColorConstants.white,
-                fontSize: 16,
-                radius: 20,
-                backgroundColor: hexToColor("#FFBB1B"),
-                fontWeight: FontWeight.w500,
-              ).marginSymmetric(horizontal: 15, vertical: 15),
+
+              Row(
+                children: [
+                  // 添加按钮
+                  MyButton(
+                    type: ClickType.throttle,
+                    milliseconds: 500,
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      controller.gotoAddLabourPage();
+                    },
+                    text: "Create New Job Request".tr,
+                    textColor: ColorConstants.white,
+                    fontSize: 14,
+                    radius: 20,
+                    backgroundColor: hexToColor("#FFBB1B"),
+                    fontWeight: FontWeight.w500,
+                  ).expanded(),
+
+                  //批量预选
+                  MyButton(
+                    type: ClickType.throttle,
+                    milliseconds: 500,
+                    onPressed: () {
+                      FocusScope.of(context).unfocus();
+                      controller.showBatchPreSelectedDialog();
+                    },
+                    text: "Batch Pre selected".tr,
+                    textColor: ColorConstants.white,
+                    fontSize: 14,
+                    radius: 20,
+                    backgroundColor: hexToColor("#4AA3FF"),
+                    fontWeight: FontWeight.w500,
+                  ).marginOnly(left: 10).expanded(),
+                ],
+              ).marginOnly(left: 15, right: 15, top: 10, bottom: 5),
 
               //底部的列表
               EasyRefresh(
@@ -152,13 +173,13 @@ class _LabourRequestListState extends BaseState<LabourRequestListPage, LabourReq
                           onStatusAction: () {
                             controller.gotoWorkflowPage(state.datas[index]);
                           },
-                          onAttAction: (){
-                           controller.showAttachmentDialog(state.datas[index]);
+                          onAttAction: () {
+                            controller.showAttachmentDialog(state.datas[index]);
                           },
-                          onDeleteAction: (){
+                          onDeleteAction: () {
                             controller.doDeleteAction(state.datas[index].id!, index);
                           },
-                          onPreSelectedAction: (){
+                          onPreSelectedAction: () {
                             controller.doPreSelectedAction(state.datas[index]);
                           },
                         );

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

@@ -661,4 +661,7 @@ class ApiConstants {
   //OA 用工审核工作流
   static const apiOALabourReviewWorkflowTH = "/index.php/api/oa/lab-req/review/view-status";
 
+  //批量操作预选员工的添加
+  static const apiOABatchPreSelectAddEmployeeTH = "/index.php/api/er/selected/lab-batch";
+
 }

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

@@ -57,6 +57,8 @@ class THOALabourTableRows {
 	@JSONField(name: "position_name")
 	String? positionName;
 
+	bool isSelected = false;
+
 	THOALabourTableRows();
 
 	factory THOALabourTableRows.fromJson(Map<String, dynamic> json) => $THOALabourTableRowsFromJson(json);

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

@@ -611,4 +611,32 @@ class THOARepository extends GetxService {
     }
     return result.convert();
   }
+
+  /// 批量操作预选员工的添加
+  Future<HttpResult> submitBatchPreSelectEmployees({
+    required String? reqIds,
+    required String? eeIds,
+    String? auditMark,
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    params['req_ids'] = reqIds ?? "";
+    params['ee_ids'] = eeIds ?? "";
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiOABatchPreSelectAddEmployeeTH,
+      method: HttpMethod.POST,
+      params: params,
+      networkDebounce: true,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      return result.convert();
+    }
+    return result.convert();
+  }
+
 }

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

@@ -321,6 +321,7 @@ const Map<String, String> en_US = {
   'Avatar': 'Avatar',
   'Pre-selected Staff': 'Pre-selected Staff',
   'Are you sure you want to remove this employee from the pre-selected list?': 'Are you sure you want to remove this employee from the pre-selected list?',
+  'Batch Pre selected': 'Batch Pre selected',
 
   //插件的国际化
   'Pull to refresh': 'Pull to refresh',

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

@@ -321,6 +321,7 @@ const Map<String, String> th_TH = {
   'Avatar': 'อวาตาร์',
   'Pre-selected Staff': 'การคัดเลือกพนักงานล่วงหน้า',
   'Are you sure you want to remove this employee from the pre-selected list?': 'คุณแน่ใจว่าต้องการลบพนักงานคนนี้ออกจากรายการคัดเลือก?',
+  'Batch Pre selected': 'แบทช์เลือกล่วงหน้า',
 
   // การแปลของปลั๊กอิน
   'Pull to refresh': 'ดึงเพื่อรีเฟรช',

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

@@ -321,6 +321,7 @@ const Map<String, String> zh_CN = {
   'Avatar': '头像',
   'Pre-selected Staff': '预选员工',
   'Are you sure you want to remove this employee from the pre-selected list?': '你确定你想要从预选列表中删除此员工吗?',
+  '批量预选': '批量预选',
 
   //插件的国际化
   'Pull to refresh': '下拉刷新',

+ 1 - 0
packages/cs_widgets/lib/my_button.dart

@@ -106,6 +106,7 @@ class MyButton extends StatelessWidget {
         ),
         child: Text(
           text,
+          textAlign: TextAlign.center,
           style: TextStyle(fontSize: fontSize, fontWeight: fontWeight ?? FontWeight.w400),
         ));
   }