Quellcode durchsuchen

马来的部分主要逻辑完善

liukai vor 6 Tagen
Ursprung
Commit
b99a63e68a
62 geänderte Dateien mit 3255 neuen und 14 gelöschten Zeilen
  1. 10 1
      app/lib/main.dart
  2. 6 0
      app/lib/modules/splash/splash_controller.dart
  3. 6 0
      app/pubspec.yaml
  4. 5 1
      app/pubspec_overrides.yaml
  5. 2 0
      melos.yaml
  6. 8 2
      packages/cpt_auth/lib/modules/login/login_controller.dart
  7. 6 2
      packages/cpt_auth/lib/modules/login/login_page.dart
  8. 6 0
      packages/cpt_auth/lib/modules/select_country/select_country_controller.dart
  9. 62 0
      packages/cpt_auth/lib/modules/select_country/select_country_page.dart
  10. 31 0
      packages/cpt_ms/.gitignore
  11. 19 0
      packages/cpt_ms/lib/modules/job/sign_camera/sign_camera_controller.dart
  12. 290 0
      packages/cpt_ms/lib/modules/job/sign_camera/sign_camera_page.dart
  13. 5 0
      packages/cpt_ms/lib/modules/job/sign_camera/sign_camera_state.dart
  14. 192 0
      packages/cpt_ms/lib/modules/job/sign_in_sign_out/sign_in_sign_out_controller.dart
  15. 440 0
      packages/cpt_ms/lib/modules/job/sign_in_sign_out/sign_in_sign_out_page.dart
  16. 10 0
      packages/cpt_ms/lib/modules/job/sign_in_sign_out/sign_in_sign_out_state.dart
  17. 110 0
      packages/cpt_ms/lib/modules/main/main_controller.dart
  18. 46 0
      packages/cpt_ms/lib/modules/main/main_item_module.dart
  19. 197 0
      packages/cpt_ms/lib/modules/main/main_page.dart
  20. 22 0
      packages/cpt_ms/lib/modules/main/main_state.dart
  21. 0 0
      packages/cpt_ms/lib/modules/ms_export.dart
  22. 33 0
      packages/cpt_ms/lib/router/ms_router.dart
  23. 23 0
      packages/cpt_ms/lib/router/ms_service_impl.dart
  24. 40 0
      packages/cpt_ms/pubspec.yaml
  25. 16 0
      packages/cpt_ms/pubspec_overrides.yaml
  26. 31 0
      packages/cpt_nl/.gitignore
  27. 203 0
      packages/cpt_nl/lib/modules/job/sign_in_sign_out/sign_in_sign_out_controller.dart
  28. 440 0
      packages/cpt_nl/lib/modules/job/sign_in_sign_out/sign_in_sign_out_page.dart
  29. 10 0
      packages/cpt_nl/lib/modules/job/sign_in_sign_out/sign_in_sign_out_state.dart
  30. 160 0
      packages/cpt_nl/lib/modules/job/widget/attendance_sign_in_out.dart
  31. 131 0
      packages/cpt_nl/lib/modules/main/main_controller.dart
  32. 46 0
      packages/cpt_nl/lib/modules/main/main_item_module.dart
  33. 198 0
      packages/cpt_nl/lib/modules/main/main_page.dart
  34. 22 0
      packages/cpt_nl/lib/modules/main/main_state.dart
  35. 0 0
      packages/cpt_nl/lib/modules/nl_export.dart
  36. 24 0
      packages/cpt_nl/lib/router/nl_router.dart
  37. 23 0
      packages/cpt_nl/lib/router/nl_service_impl.dart
  38. 40 0
      packages/cpt_nl/pubspec.yaml
  39. 16 0
      packages/cpt_nl/pubspec_overrides.yaml
  40. 1 1
      packages/cpt_sg/lib/modules/main/main_controller.dart
  41. 2 1
      packages/cpt_uk/lib/modules/main/main_controller.dart
  42. 1 1
      packages/cpt_vn/lib/modules/main/main_controller.dart
  43. 27 0
      packages/cs_domain/lib/constants/api_constants.dart
  44. 55 0
      packages/cs_domain/lib/entity/response/sign_in_sign_out_entity.dart
  45. 9 0
      packages/cs_domain/lib/generated/json/base/json_convert_content.dart
  46. 114 0
      packages/cs_domain/lib/generated/json/sign_in_sign_out_entity.g.dart
  47. 13 4
      packages/cs_domain/lib/repository/auth_repository.dart
  48. 51 0
      packages/cs_domain/lib/repository/ms_repository.dart
  49. 4 0
      packages/cs_initializer/lib/global_services_injection.dart
  50. 1 1
      packages/cs_plugin_basic/lib/service/app_config_service.dart
  51. 6 0
      packages/cs_plugin_basic/lib/service/http_provider_injection.dart
  52. 3 0
      packages/cs_plugin_platform/pubspec.yaml
  53. BIN
      packages/cs_resources/assets/cpt_auth/ms-icon.webp
  54. BIN
      packages/cs_resources/assets/cpt_auth/nl-icon.webp
  55. BIN
      packages/cs_resources/assets/cpt_job/sign_take_pic.webp
  56. 3 0
      packages/cs_resources/lib/generated/assets.dart
  57. 3 0
      packages/cs_resources/lib/local/language/en_US.dart
  58. 3 0
      packages/cs_resources/lib/local/language/zh_CN.dart
  59. 7 0
      packages/cs_router/lib/componentRouter/component_router_service.dart
  60. 6 0
      packages/cs_router/lib/componentRouter/ms_service.dart
  61. 6 0
      packages/cs_router/lib/componentRouter/nl_service.dart
  62. 11 0
      packages/cs_router/lib/path/router_path.dart

+ 10 - 1
app/lib/main.dart

@@ -1,5 +1,9 @@
 import 'package:cpt_auth/router/auth_service_impl.dart';
 import 'package:cpt_auth/router/page_router.dart';
+import 'package:cpt_ms/router/ms_router.dart';
+import 'package:cpt_ms/router/ms_service_impl.dart';
+import 'package:cpt_nl/router/nl_router.dart';
+import 'package:cpt_nl/router/nl_service_impl.dart';
 import 'package:cpt_sg/router/sg_router.dart';
 import 'package:cpt_sg/router/sg_service_impl.dart';
 import 'package:cpt_uk/router/uk_router.dart';
@@ -12,6 +16,8 @@ import 'package:initializer/global_services_injection.dart';
 import 'package:initializer/app_initializer.dart';
 import 'package:plugin_basic/basic_export.dart';
 import 'package:router/componentRouter/auth_service.dart';
+import 'package:router/componentRouter/ms_service.dart';
+import 'package:router/componentRouter/nl_service.dart';
 import 'package:router/componentRouter/sg_service.dart';
 import 'package:router/componentRouter/vn_service.dart';
 import 'package:cpt_vn/router/vn_service_impl.dart';
@@ -41,6 +47,8 @@ void main() async {
     Get.lazyPut<UKService>(() => UKServiceImpl());
     Get.lazyPut<VNService>(() => VNServiceImpl());
     Get.lazyPut<SGService>(() => SGServiceImpl());
+    Get.lazyPut<MSService>(() => MSServiceImpl());
+    Get.lazyPut<NLService>(() => NLServiceImpl());
   });
 
   runApp(MyApp());
@@ -117,7 +125,8 @@ class MyApp extends StatelessWidget {
           enableLog: true,
           //默认路由与路由表的加载
           initialRoute: RouterPath.splash,
-          getPages: PageRouter.routes + BasicPageRouter.routes + AuthPageRouter.routes + UKPageRouter.routes + VNPageRouter.routes + SGPageRouter.routes,
+          getPages: PageRouter.routes + BasicPageRouter.routes + AuthPageRouter.routes + UKPageRouter.routes
+              + VNPageRouter.routes + SGPageRouter.routes + NLPageRouter.routes + MSPageRouter.routes,
           //对原生导航的兼容;SmartDialog路由配置生命周期处理
           navigatorObservers: [GetXRouterObserver(), FlutterSmartDialog.observer, routeObserver],
           //默认页面动画

+ 6 - 0
app/lib/modules/splash/splash_controller.dart

@@ -52,6 +52,12 @@ void _gotoNextPage() async {
     } else if (ConfigService.to.selectCountry.value == 2) {
       //去英国首页
       ComponentRouterServices.ukService.startUKMainPage();
+    }else if (ConfigService.to.selectCountry.value == 3) {
+      //去马来首页
+      ComponentRouterServices.msService.startMSMainPage();
+    }else if (ConfigService.to.selectCountry.value == 4) {
+      //去荷兰首页
+      ComponentRouterServices.nlService.startNLMainPage();
     } else {
       //去越南首页
       ComponentRouterServices.vnService.startVNMainPage();

+ 6 - 0
app/pubspec.yaml

@@ -49,6 +49,12 @@ dependencies:
   cpt_sg:
     path: ../packages/cpt_sg
 
+  cpt_nl:
+    path: ../packages/cpt_nl
+
+  cpt_ms:
+    path: ../packages/cpt_ms
+
   initializer:
     path: ../packages/cs_initializer
 

+ 5 - 1
app/pubspec_overrides.yaml

@@ -1,7 +1,11 @@
-# melos_managed_dependency_overrides: cpt_auth,cs_resources,domain,initializer,plugin_basic,plugin_platform,router,shared,widgets,cpt_uk,cpt_vn,cpt_sg
+# melos_managed_dependency_overrides: cpt_auth,cs_resources,domain,initializer,plugin_basic,plugin_platform,router,shared,widgets,cpt_uk,cpt_vn,cpt_sg,cpt_ms,cpt_nl
 dependency_overrides:
   cpt_auth:
     path: ../packages/cpt_auth
+  cpt_ms:
+    path: ../packages/cpt_ms
+  cpt_nl:
+    path: ../packages/cpt_nl
   cpt_sg:
     path: ../packages/cpt_sg
   cpt_uk:

+ 2 - 0
melos.yaml

@@ -13,6 +13,8 @@ packages:
   - "packages/cpt_uk/"
   - "packages/cpt_vn/"
   - "packages/cpt_sg/"
+  - "packages/cpt_nl/"
+  - "packages/cpt_ms/"
 
 
 command:

+ 8 - 2
packages/cpt_auth/lib/modules/login/login_controller.dart

@@ -62,7 +62,7 @@ class LoginController extends GetxController with DioCancelableMixin {
     var result = await authRepository.userLogin(
       state.code,
       state.password,
-      version: ConfigService.to.selectCountry.value == 1 ? 2 : 1,  //新加坡用 V2版本
+      country: ConfigService.to.selectCountry.value,
       cancelToken: cancelToken,
     );
 
@@ -82,7 +82,7 @@ class LoginController extends GetxController with DioCancelableMixin {
     UserService.to.setToken(token);
 
     //保存是否是管理员登录
-    if (ConfigService.to.selectCountry.value == 1 || ConfigService.to.selectCountry.value == 2) {
+    if (ConfigService.to.selectCountry.value != 0) {
       //如果是新加坡、英国国户,强制是管理员登录
       SPUtil.putInt(AppConstant.storageIsAdmin, 1);
     } else {
@@ -95,6 +95,12 @@ class LoginController extends GetxController with DioCancelableMixin {
     } else if (ConfigService.to.selectCountry.value == 2) {
       //去英国首页
       ComponentRouterServices.ukService.startUKMainPage();
+    } else if (ConfigService.to.selectCountry.value == 3) {
+      //去马来首页
+      ComponentRouterServices.msService.startMSMainPage();
+    } else if (ConfigService.to.selectCountry.value == 4) {
+      //去荷兰首页
+      ComponentRouterServices.nlService.startNLMainPage();
     } else {
       //去越南首页
       ComponentRouterServices.vnService.startVNMainPage();

+ 6 - 2
packages/cpt_auth/lib/modules/login/login_page.dart

@@ -106,7 +106,11 @@ class _LoginPageState extends BaseState<LoginPage, LoginController> with StateLi
                               ? "Singapore".tr
                               : ConfigService.to.selectCountry.value == 2
                                   ? "United Kingdom".tr
-                                  : "Vietnam".tr,
+                                  : ConfigService.to.selectCountry.value == 3
+                                      ? "Malaysia".tr
+                                      : ConfigService.to.selectCountry.value == 4
+                                          ? "Netherlands".tr
+                                          : "Vietnam".tr,
                           textColor: ColorConstants.white,
                           isFontMedium: true,
                           marginRight: 5,
@@ -218,7 +222,7 @@ class _LoginPageState extends BaseState<LoginPage, LoginController> with StateLi
                                 //选择签到功能还是全功能
                                 Obx(() {
                                   return Visibility(
-                                    visible: ConfigService.to.selectCountry.value == 0 ,  //这是越南的逻辑,新加坡与英国不需要
+                                    visible: ConfigService.to.selectCountry.value == 0, //这是越南的逻辑,新加坡与英国不需要
                                     child: CustomRadioCheck(
                                       options: state.loginOption,
                                       onOptionSelected: (index, text) {

+ 6 - 0
packages/cpt_auth/lib/modules/select_country/select_country_controller.dart

@@ -19,6 +19,12 @@ class SelectCountryController extends GetxController {
     } else if (country == 2) {
       //英国
       baseUrl = ApiConstants.ukBaseUrl;
+    } else if (country == 3) {
+      //马来西亚
+      baseUrl = ApiConstants.msBaseUrl;
+    } else if (country == 4) {
+      //荷兰
+      baseUrl = ApiConstants.nlBaseUrl;
     } else {
       //默认是越南
       baseUrl = ApiConstants.vnBaseUrl;

+ 62 - 0
packages/cpt_auth/lib/modules/select_country/select_country_page.dart

@@ -154,6 +154,68 @@ class SelectCountryPage extends BaseStatelessPage<SelectCountryController> {
                         ConfigService.to.selectCountry.value = 2;
                       }),
 
+                      //马来西亚
+                      Container(
+                        width: double.infinity,
+                        margin: const EdgeInsets.only(top: 13.5, left: 20, right: 20),
+                        padding: const EdgeInsets.symmetric(vertical: 13, horizontal: 17),
+                        decoration: BoxDecoration(
+                          color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+                          borderRadius: BorderRadius.circular(5.0), // 设置圆角
+                        ),
+                        child: Row(
+                          children: [
+                            const MyAssetImage(Assets.cptAuthMsIcon, width: 50, height: 33),
+                            MyTextView(
+                              "Malaysia".tr,
+                              marginLeft: 17,
+                              textColor: ColorConstants.white,
+                              isFontMedium: true,
+                              fontSize: 18,
+                            ).expanded(),
+                            Obx(() {
+                              return Visibility(
+                                visible: ConfigService.to.selectCountry.value == 3,
+                                child: const MyAssetImage(Assets.cptAuthCheckedIcon, width: 22, height: 22),
+                              );
+                            }),
+                          ],
+                        ),
+                      ).onTap(() {
+                        ConfigService.to.selectCountry.value = 3;
+                      }),
+
+                      //荷兰
+                      Container(
+                        width: double.infinity,
+                        margin: const EdgeInsets.only(top: 13.5, left: 20, right: 20),
+                        padding: const EdgeInsets.symmetric(vertical: 13, horizontal: 17),
+                        decoration: BoxDecoration(
+                          color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+                          borderRadius: BorderRadius.circular(5.0), // 设置圆角
+                        ),
+                        child: Row(
+                          children: [
+                            const MyAssetImage(Assets.cptAuthNlIcon, width: 50, height: 33),
+                            MyTextView(
+                              "Netherlands".tr,
+                              marginLeft: 17,
+                              textColor: ColorConstants.white,
+                              isFontMedium: true,
+                              fontSize: 18,
+                            ).expanded(),
+                            Obx(() {
+                              return Visibility(
+                                visible: ConfigService.to.selectCountry.value == 4,
+                                child: const MyAssetImage(Assets.cptAuthCheckedIcon, width: 22, height: 22),
+                              );
+                            }),
+                          ],
+                        ),
+                      ).onTap(() {
+                        ConfigService.to.selectCountry.value = 4;
+                      }),
+
                       //越南
                       // Container(
                       //   width: double.infinity,

+ 31 - 0
packages/cpt_ms/.gitignore

@@ -0,0 +1,31 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+*.lock
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# Visual Studio Code related
+.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+proguardMapping.txt

+ 19 - 0
packages/cpt_ms/lib/modules/job/sign_camera/sign_camera_controller.dart

@@ -0,0 +1,19 @@
+import 'package:get/get.dart';
+
+import 'sign_camera_state.dart';
+
+class SignCameraController extends GetxController {
+  final SignCameraState state = SignCameraState();
+
+  /// 处理图片
+  void handleImage(String? watermarkedPath) {
+    state.watermarkedFile = watermarkedPath;
+    update();
+  }
+
+  /// 确认图片
+  void confirmImage() {
+
+  }
+
+}

+ 290 - 0
packages/cpt_ms/lib/modules/job/sign_camera/sign_camera_page.dart

@@ -0,0 +1,290 @@
+import 'dart:typed_data';
+
+import 'package:cpt_ms/modules/job/sign_in_sign_out/sign_in_sign_out_page.dart';
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/base/mixin_state_lifecycle.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/date_time_utils.dart';
+import 'package:shared/utils/log_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:plugin_basic/base/base_state.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'sign_camera_controller.dart';
+import 'sign_camera_state.dart';
+import 'package:camera/camera.dart';
+import 'package:image/image.dart' as img;
+import 'dart:io';
+import 'package:path/path.dart';
+
+/*
+   签到的自定义相机页面
+ */
+class SignCameraPage extends BaseStatefulPage<SignCameraController> {
+  SignCameraPage({super.key});
+
+  //启动当前页面
+  static void startInstance() {
+    return Get.start(RouterPath.MSSignCamera);
+  }
+
+  @override
+  State<SignCameraPage> createState() => _SignCameraPageState();
+
+  @override
+  SignCameraController createRawController() {
+    return SignCameraController();
+  }
+}
+
+class _SignCameraPageState extends BaseState<SignCameraPage, SignCameraController> with StateLifecycle {
+  late SignCameraState state;
+
+  //Camera的控制器
+  late CameraController _controller;
+  List<CameraDescription>? _cameras;
+  bool _isCameraInitialized = false;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+    _initCamera(); // 初始化摄像头
+  }
+
+  @override
+  void dispose() {
+    Get.delete<SignCameraController>();
+    super.dispose();
+    _controller.dispose(); // 页面销毁时释放摄像头资源
+  }
+
+  @override
+  void onResume() {
+    super.onResume();
+    Log.d("SignInSignOutPage Lifecycle - onResume");
+  }
+
+  @override
+  void onPause() {
+    super.onPause();
+    Log.d("SignInSignOutPage Lifecycle - onPause");
+  }
+
+  @override
+  void onStop() {
+    super.onStop();
+    Log.d("SignInSignOutPage Lifecycle - onStop");
+  }
+
+  @override
+  void onStart() {
+    super.onStart();
+    Log.d("SignInSignOutPage Lifecycle - onStart");
+  }
+
+  // 初始化摄像头(选择后置摄像头,配置分辨率)
+  Future<void> _initCamera() async {
+    // 1. 获取设备上所有可用摄像头
+    _cameras = await availableCameras();
+    if (_cameras == null || _cameras!.isEmpty) {
+      debugPrint("无可用摄像头");
+      return;
+    }
+    // 2. 选择摄像头(优先选前置镜头)
+    CameraDescription frontCamera = _cameras!.firstWhere((cam) => cam.lensDirection == CameraLensDirection.front);
+
+    // 3. 初始化控制器(分辨率选 medium,平衡性能和画质)
+    _controller = CameraController(
+      frontCamera,
+      ResolutionPreset.medium,
+    );
+
+    // 4. 等待控制器初始化完成
+    await _controller.initialize();
+
+    if (mounted) {
+      setState(() {
+        _isCameraInitialized = true;
+      });
+    }
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    Log.d("SignInSignOutPage Lifecycle - build走了一遍");
+    // 摄像头未初始化时,显示加载
+
+    return autoCtlGetBuilder(
+      builder: (controller) {
+        return SafeArea(
+          bottom: false,
+          top: false,
+          child: Container(
+            width: double.infinity,
+            height: double.infinity,
+            padding: EdgeInsets.only(top: ScreenUtil.getStatusBarH(context)),
+            decoration: const BoxDecoration(
+              gradient: LinearGradient(
+                colors: [
+                  Color(0xFF091D44),
+                  Color(0xFF245A8A),
+                  Color(0xFF7F7CEC),
+                ],
+                begin: Alignment.topCenter,
+                end: Alignment.bottomCenter,
+              ),
+            ),
+            child: Column(
+              children: [
+                //顶部的搜索标题布局
+                MyAppBar.titleBar(context, "Sign in / Sign out"),
+
+                SingleChildScrollView(
+                  child: Column(
+                    children: [
+                      // -------- 1:1 摄像头预览区域 --------
+                      AspectRatio(
+                        aspectRatio: 3 / 4, // 宽高比 1:1
+                        child: (!_isCameraInitialized || !_controller.value.isInitialized)
+                            ? const Center(child: CircularProgressIndicator(color: Colors.white))
+                            : Utils.isEmpty(state.watermarkedFile)
+                                ? CameraPreview(_controller)
+                                : MyLoadImage(state.watermarkedFile),
+                      ),
+
+                      Padding(
+                        padding: const EdgeInsets.only(top: 40, bottom: 40),
+                        child: Stack(
+                          children: [
+                            Visibility(
+                              visible: Utils.isEmpty(state.watermarkedFile),
+                              child: const MyAssetImage(Assets.cptJobSignTakePic, width: 93, height: 93).onTap(() {
+                                _takePicture();
+                              }),
+                            ),
+                            Visibility(
+                              visible: Utils.isNotEmpty(state.watermarkedFile),
+                              child: Row(
+                                mainAxisSize: MainAxisSize.max,
+                                children: [
+                                  MyButton(
+                                    onPressed: () {
+                                      controller.handleImage(null);
+                                    },
+                                    text: 'Retake'.tr,
+                                    textColor: Colors.white,
+                                    backgroundColor: ColorConstants.textYellowF8AE00,
+                                    radius: 25,
+                                    minHeight: 50,
+                                  ).marginOnly(left: 30, right: 7.5).expanded(),
+                                  MyButton(
+                                    onPressed: () {
+                                      controller.confirmImage();
+                                    },
+                                    text: 'Confirm'.tr,
+                                    textColor: Colors.white,
+                                    backgroundColor: ColorConstants.textGreen0AC074,
+                                    radius: 25,
+                                    minHeight: 50,
+                                  ).marginOnly(right: 30, left: 7.5).expanded(),
+                                ],
+                              ),
+                            ),
+                          ],
+                        ),
+                      ),
+                    ],
+                  ),
+                ).expanded(),
+              ],
+            ),
+          ),
+        );
+      },
+    );
+  }
+
+  /// 拍照逻辑:调用控制器拍摄照片
+  Future<void> _takePicture() async {
+    try {
+      if (_isCameraInitialized || _controller.value.isInitialized) {
+        XFile picture = await _controller.takePicture();
+        debugPrint("照片路径:${picture.path}");
+
+        String? watermarkedPath = await _addWatermark(picture.path);
+        // 后续可处理:上传服务器、保存到相册等
+        debugPrint("处理后带水印的图片路径:$watermarkedPath");
+
+        if (Utils.isNotEmpty(watermarkedPath)) {
+          controller.handleImage(watermarkedPath);
+        } else {
+          ToastEngine.show("Image processing failed !");
+        }
+      }
+    } catch (e) {
+      debugPrint("拍照失败:$e");
+      ToastEngine.show("Take Picture Failed");
+    }
+  }
+
+  Future<String?> _addWatermark(String imagePath) async {
+    try {
+      // -------- 步骤1:解码图片并修正旋转 --------
+      File originalFile = File(imagePath);
+      if (!await originalFile.exists()) {
+        debugPrint("错误:原始图片不存在 → $imagePath");
+        return null;
+      }
+
+      // 读取图片字节
+      Uint8List imageBytes = await originalFile.readAsBytes();
+      img.Image? originalImage = img.decodeImage(imageBytes);
+      if (originalImage == null) {
+        debugPrint("错误:图片解码失败");
+        return null;
+      }
+
+      //如果宽>高,则逆时针旋转90度
+      if (originalImage.width > originalImage.height) {
+        debugPrint("图片的宽>高,则逆时针旋转90度");
+        originalImage = img.copyRotate(originalImage, 270);
+      }
+
+      //由于是前置摄像头 → 水平镜像翻转
+      originalImage = img.flipHorizontal(originalImage);
+
+      // -------- 步骤2:生成水印文本(时间) --------
+      String watermarkText = DateTimeUtils.formatNowDateStr(format: 'yyyy-MM-dd HH:mm');
+      // (需确保 DateTimeUtils 工具类能正确返回时间字符串)
+
+      // -------- 步骤3:准备绘制字体 --------
+      img.drawString(originalImage, img.arial_24, 20, 20, watermarkText);
+
+      // -------- 步骤5:保存带水印的新图片(避免覆盖原始文件) --------
+      String watermarkedPath = join(dirname(imagePath), 'watermarked_${basename(imagePath)}');
+      File watermarkedFile = File(watermarkedPath);
+
+      // 编码为 JPG 保存到File
+      final processedBytes = img.encodeJpg(originalImage, quality: 95);
+      await watermarkedFile.writeAsBytes(processedBytes);
+
+      debugPrint("成功:带水印图片已保存 → $watermarkedPath");
+
+      return watermarkedPath;
+    } catch (e) {
+      debugPrint("添加水印失败:$e");
+      ToastEngine.show("Image processing failed");
+    }
+    return null;
+  }
+}

+ 5 - 0
packages/cpt_ms/lib/modules/job/sign_camera/sign_camera_state.dart

@@ -0,0 +1,5 @@
+class SignCameraState {
+
+ String? watermarkedFile;  //最终拍摄处理后的图片
+
+}

+ 192 - 0
packages/cpt_ms/lib/modules/job/sign_in_sign_out/sign_in_sign_out_controller.dart

@@ -0,0 +1,192 @@
+import 'dart:typed_data';
+
+import 'package:cpt_ms/modules/job/sign_camera/sign_camera_page.dart';
+import 'package:domain/entity/response/sign_in_sign_out_entity.dart';
+import 'package:domain/repository/ms_repository.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/engine/permission/permission_engine.dart';
+
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/picker/date_picker_util.dart';
+import 'package:widgets/widget_export.dart';
+import 'sign_in_sign_out_state.dart';
+
+class SignInSignOutController extends GetxController with DioCancelableMixin {
+  final MSRepository _msRepository = Get.find();
+  final SignInSignOutState state = SignInSignOutState();
+
+  var _needShowPlaceholder = true;
+
+  //页面的列表数据
+  List<SignInSignOutRows> datas = [];
+  String keyword = "";
+  Rx<DateTime> startDate = DateTime.now().obs;
+  Rx<DateTime> endDate = DateTime.now().obs;
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Success;
+  String? errorMessage;
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,
+    controlFinishLoad: false,
+  );
+
+  // Refresh 刷新事件
+  Future onRefresh() async {
+    fetchAttendanceList();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _needShowPlaceholder = true;
+    fetchAttendanceList();
+  }
+
+  /// 获取服务器数据,成员考勤列表
+  Future fetchAttendanceList() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    }
+
+    //获取到数据
+    var result = await _msRepository.fetchSignInSignOutList(
+      keyword,
+      DateTimeUtils.formatDate(startDate.value, format: "yyyy-MM-dd"),
+      DateTimeUtils.formatDate(endDate.value, format: "yyyy-MM-dd"),
+      cancelToken: cancelToken,
+    );
+
+    //处理数据
+    if (result.isSuccess) {
+      handleList(result.data?.rows);
+      refreshController.finishRefresh(IndicatorResult.success);
+    } else {
+      errorMessage = result.errorMsg;
+      changeLoadingState(LoadState.State_Error);
+      refreshController.finishRefresh(IndicatorResult.fail);
+    }
+
+    //最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void handleList(List<SignInSignOutRows>? list) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      datas.clear();
+      datas.addAll(list);
+      //更新状态
+      changeLoadingState(LoadState.State_Success);
+    } else {
+      //展示无数据的布局
+      datas.clear();
+      changeLoadingState(LoadState.State_Empty);
+    }
+  }
+
+  /// 签到签出
+  void requestCheckInCheckOut(bool isCheckIn, SignInSignOutRows item, ByteData byteData) async {
+    //请求接口
+
+    // var result = await _jobRepository.submitCheckInOut(
+    //   item.appliedId.toString(),
+    //   byteData,
+    //   isCheckIn: isCheckIn,
+    //   cancelToken: cancelToken,
+    // );
+    //
+    // if (result.isSuccess) {
+    //   CheckSuccessEntity entity = result.data!;
+    //   if (isCheckIn) {
+    //     item.checkInImg = entity.checkImg;
+    //     item.checkInTime = entity.checkTime;
+    //   } else {
+    //     item.checkOutImg = entity.checkImg;
+    //     item.checkOutTime = entity.checkTime;
+    //   }
+    //   //更新状态
+    //   update();
+    // } else {
+    //   errorMessage = result.errorMsg;
+    //   ToastEngine.show(errorMessage ?? "Network Load Error".tr);
+    // }
+  }
+
+  //执行搜索
+  void doSearch(String keyword) {
+    this.keyword = keyword;
+    refreshController.callRefresh();
+  }
+
+  @override
+  void onReady() async {
+    super.onReady();
+    fetchAttendanceList();
+  }
+
+  @override
+  void onClose() {
+    super.onClose();
+    datas.clear();
+  }
+
+  /// 用户签到
+  void userSignIn(SignInSignOutRows item) {
+    PermissionEngine().requestCameraPermission(() {
+      SignCameraPage.startInstance();
+    });
+  }
+
+  /// 用户签出
+  void userSignOut(SignInSignOutRows item) {
+    PermissionEngine().requestCameraPermission(() {
+      SignCameraPage.startInstance();
+    });
+  }
+
+  /// 重置筛选条件
+  void resetFiltering() {
+    keyword = "";
+    state.searchController.text = "";
+    startDate.value = DateTime.now();
+    endDate.value = DateTime.now();
+    update();
+
+    refreshController.callRefresh();
+  }
+
+  /// 筛选开始日期
+  void pickerStartDate() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: startDate.value,
+      onDateTimeChanged: (date) {
+        startDate.value = date;
+        refreshController.callRefresh();
+      },
+      title: "Start Date".tr,
+    );
+  }
+
+  /// 筛选结束日期
+  void pickerEndDate() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: endDate.value,
+      onDateTimeChanged: (date) {
+        endDate.value = date;
+        refreshController.callRefresh();
+      },
+      title: "End Date".tr,
+    );
+  }
+}

+ 440 - 0
packages/cpt_ms/lib/modules/job/sign_in_sign_out/sign_in_sign_out_page.dart

@@ -0,0 +1,440 @@
+import 'dart:ui';
+
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/src/services/predictive_back_event.dart';
+import 'package:flutter/widgets.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/mixin_state_lifecycle.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:shared/utils/util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+import 'sign_in_sign_out_controller.dart';
+import 'sign_in_sign_out_state.dart';
+
+/*
+   工作的签到页面
+ */
+class SignInSignOutPage extends BaseStatefulPage<SignInSignOutController> {
+  SignInSignOutPage({super.key});
+
+  //启动当前页面
+  static void startInstance() {
+    return Get.start(RouterPath.MSJobSignInSignOut);
+  }
+
+  @override
+  State<SignInSignOutPage> createState() => _SignInSignOutPageState();
+
+  @override
+  SignInSignOutController createRawController() {
+    return SignInSignOutController();
+  }
+}
+
+class _SignInSignOutPageState extends BaseState<SignInSignOutPage, SignInSignOutController> with StateLifecycle {
+  late SignInSignOutState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+  }
+
+  @override
+  void dispose() {
+    Get.delete<SignInSignOutController>();
+    super.dispose();
+  }
+
+  @override
+  void onResume() {
+    super.onResume();
+    Log.d("SignInSignOutPage Lifecycle - onResume");
+  }
+
+  @override
+  void onPause() {
+    super.onPause();
+    Log.d("SignInSignOutPage Lifecycle - onPause");
+  }
+
+  @override
+  void onStop() {
+    super.onStop();
+    Log.d("SignInSignOutPage Lifecycle - onStop");
+  }
+
+  @override
+  void onStart() {
+    super.onStart();
+    Log.d("SignInSignOutPage Lifecycle - onStart");
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    Log.d("SignInSignOutPage Lifecycle - build走了一遍");
+
+    return autoCtlGetBuilder(
+      builder: (controller) {
+        return SafeArea(
+          bottom: false,
+          top: false,
+          child: Container(
+            width: double.infinity,
+            height: double.infinity,
+            padding: EdgeInsets.only(top: ScreenUtil.getStatusBarH(context)),
+            decoration: const BoxDecoration(
+              gradient: LinearGradient(
+                colors: [
+                  Color(0xFF091D44),
+                  Color(0xFF245A8A),
+                  Color(0xFF7F7CEC),
+                ],
+                begin: Alignment.topCenter,
+                end: Alignment.bottomCenter,
+              ),
+            ),
+            child: Column(
+              children: [
+                //顶部的搜索标题布局
+                MyAppBar.searchTitleBar(
+                  context,
+                  value: controller.keyword,
+                  hintText: 'Name/Mobile'.tr,
+                  controller: state.searchController,
+                  onSearch: (keyword) {
+                    controller.doSearch(keyword);
+                  },
+                  actions: [
+                    MyButton(
+                      onPressed: () {
+                        controller.resetFiltering();
+                      },
+                      text: "Reset".tr,
+                      textColor: ColorConstants.white,
+                      backgroundColor: hexToColor("#2BA9F9", opacity: 0.5),
+                      radius: 17.25,
+                      minWidth: 60,
+                      minHeight: 35,
+                    ).marginOnly(right: 15)
+                  ],
+                ),
+
+                _buildFilterDateWidget(),
+
+                EasyRefresh(
+                  controller: controller.refreshController,
+                  onRefresh: controller.onRefresh,
+                  child: LoadStateLayout(
+                    state: controller.loadingState,
+                    errorMessage: controller.errorMessage,
+                    errorRetry: () {
+                      controller.retryRequest();
+                    },
+                    successSliverWidget: [
+                      SliverList(
+                          delegate: SliverChildBuilderDelegate(
+                        (context, index) {
+                          return _buildAttendanceItem(controller, index, () {});
+                        },
+                        childCount: controller.datas.length,
+                      ))
+                    ],
+                  ),
+                ).expanded(),
+              ],
+            ),
+          ),
+        );
+      },
+    );
+  }
+
+  /// 时间筛选布局
+  Widget _buildFilterDateWidget() {
+    return Container(
+      width: double.infinity,
+      height: 36,
+      margin: const EdgeInsets.only(left: 15, right: 15, top: 15, bottom: 10),
+      decoration: BoxDecoration(
+        color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+        borderRadius: BorderRadius.circular(17.25), // 设置圆角
+      ),
+      child: Row(
+        mainAxisSize: MainAxisSize.max,
+        crossAxisAlignment: CrossAxisAlignment.center,
+        children: [
+          Obx(() {
+            return MyTextView(
+              DateTimeUtils.formatDate(controller.startDate.value, format: "yyyy-MM-dd"),
+              fontSize: 16,
+              textAlign: TextAlign.center,
+              isFontRegular: true,
+              textColor: ColorConstants.white,
+              onClick: () {
+                controller.pickerStartDate();
+              },
+            );
+          }).expanded(),
+          Container(color: const Color(0XFF52739C), width: 0.5, height: 21.5),
+          Obx(() {
+            return MyTextView(
+              DateTimeUtils.formatDate(controller.endDate.value, format: "yyyy-MM-dd"),
+              fontSize: 16,
+              textAlign: TextAlign.center,
+              isFontRegular: true,
+              textColor: ColorConstants.white,
+              onClick: () {
+                controller.pickerEndDate();
+              },
+            );
+          }).expanded(),
+        ],
+      ),
+    );
+  }
+
+  /// Item列表
+  Widget _buildAttendanceItem(SignInSignOutController controller, int index, void Function() callback) {
+    final item = controller.datas[index];
+
+    return Container(
+      padding: EdgeInsets.only(left: 23, right: 12),
+      margin: EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 5),
+      decoration: BoxDecoration(
+        color: Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+        borderRadius: BorderRadius.circular(5), // 设置圆角
+      ),
+      child: Column(
+        children: [
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Name:".tr,
+                isFontRegular: true,
+                textColor: const Color(0XFFAECAE5),
+                fontSize: 14,
+              ),
+
+              //姓名
+              MyTextView(
+                item.staffName ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: const Color(0XFFAECAE5),
+                fontSize: 14,
+              ).expanded(),
+
+              MyAssetImage(item.isExpended ? Assets.cptJobArrawUpIcon : Assets.cptJobArrawDownIcon, width: 15, height: 7),
+            ],
+          ).paddingOnly(top: 19, bottom: 19),
+
+          //显示隐藏的布局
+          Visibility(
+            visible: item.isExpended,
+            child: Column(
+              children: [
+                //工作日期
+                Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  children: [
+                    MyTextView(
+                      "${"Job Date".tr}:",
+                      isFontRegular: true,
+                      textColor: const Color(0XFFAECAE5),
+                      fontSize: 14,
+                    ),
+
+                    //姓名
+                    MyTextView(
+                      item.jobDate ?? "-",
+                      marginLeft: 5,
+                      isFontRegular: true,
+                      textColor: const Color(0XFFAECAE5),
+                      fontSize: 14,
+                    ).expanded(),
+                  ],
+                ),
+
+                //开始时间
+                Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  children: [
+                    MyTextView(
+                      "Start Time:",
+                      isFontRegular: true,
+                      textColor: const Color(0XFFAECAE5),
+                      fontSize: 14,
+                    ),
+
+                    //姓名
+                    MyTextView(
+                      item.startTime ?? "-",
+                      marginLeft: 5,
+                      isFontRegular: true,
+                      textColor: const Color(0XFFAECAE5),
+                      fontSize: 14,
+                    ).expanded(),
+                  ],
+                ).marginOnly(top: 16),
+
+                //结束时间
+                Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  children: [
+                    MyTextView(
+                      "End Time:".tr,
+                      isFontRegular: true,
+                      textColor: const Color(0XFFAECAE5),
+                      fontSize: 14,
+                    ),
+
+                    //姓名
+                    MyTextView(
+                      item.endTime ?? "-",
+                      marginLeft: 5,
+                      isFontRegular: true,
+                      textColor: const Color(0XFFAECAE5),
+                      fontSize: 14,
+                    ).expanded(),
+                  ],
+                ).marginOnly(top: 16),
+
+                //签到的控件
+                Row(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    //开始签到
+                    Column(
+                      mainAxisAlignment: MainAxisAlignment.start,
+                      crossAxisAlignment: CrossAxisAlignment.center,
+                      children: [
+                        Container(
+                          width: 90,
+                          height: 35,
+                          margin: const EdgeInsets.only(top: 18),
+                          decoration: BoxDecoration(
+                            color: Utils.isEmpty(item.inPhoto) ? const Color(0xFF56AAFF) : ColorConstants.white,
+                            borderRadius: BorderRadius.circular(17.25), // 设置圆角
+                          ),
+                          child: Stack(
+                            alignment: Alignment.center,
+                            children: [
+                              Visibility(
+                                visible: Utils.isEmpty(item.inPhoto),
+                                child: MyTextView(
+                                  "Check In".tr,
+                                  isFontMedium: true,
+                                  textColor: ColorConstants.white,
+                                  fontSize: 14,
+                                ),
+                              ),
+                              Visibility(
+                                visible: !Utils.isEmpty(item.inPhoto),
+                                child: MyLoadImage(item.inPhoto, width: 71.5, height: 25, fit: BoxFit.cover),
+                              ),
+                            ],
+                          ).onTap(() {
+                            if (Utils.isEmpty(item.inPhoto)) {
+                              controller.userSignIn(item);
+                            }
+                          }),
+                        ),
+                        Visibility(
+                          visible: !Utils.isEmpty(item.inTime),
+                          child: MyTextView(
+                            marginTop: 9,
+                            item.inTime ?? "-",
+                            textColor: ColorConstants.white,
+                            fontSize: 14,
+                            isFontRegular: true,
+                          ),
+                        ),
+                      ],
+                    ),
+
+                    //结束签到
+                    Column(
+                      mainAxisAlignment: MainAxisAlignment.start,
+                      crossAxisAlignment: CrossAxisAlignment.center,
+                      children: [
+                        Container(
+                          width: 90,
+                          height: 35,
+                          margin: EdgeInsets.only(top: 18),
+                          decoration: BoxDecoration(
+                            color: Utils.isEmpty(item.outPhoto) ? Color(0xFF56AAFF) : ColorConstants.white,
+                            borderRadius: BorderRadius.circular(17.25), // 设置圆角
+                          ),
+                          child: Stack(
+                            alignment: Alignment.center,
+                            children: [
+                              Visibility(
+                                visible: Utils.isEmpty(item.outPhoto),
+                                child: MyTextView(
+                                  "Check Out".tr,
+                                  isFontMedium: true,
+                                  textColor: ColorConstants.white,
+                                  fontSize: 14,
+                                ),
+                              ),
+                              Visibility(
+                                visible: !Utils.isEmpty(item.outPhoto),
+                                child: MyLoadImage(item.outPhoto, width: 71.5, height: 25, fit: BoxFit.cover),
+                              ),
+                            ],
+                          ).onTap(() {
+                            if (Utils.isEmpty(item.outPhoto)) {
+                              controller.userSignOut(item);
+                            }
+                          }),
+                        ),
+                        Visibility(
+                          visible: !Utils.isEmpty(item.outTime),
+                          child: MyTextView(
+                            marginTop: 9,
+                            item.outTime ?? "-",
+                            textColor: ColorConstants.white,
+                            fontSize: 14,
+                            isFontRegular: true,
+                          ),
+                        ),
+                      ],
+                    ).marginOnly(left: 10),
+                  ],
+                ),
+
+                const SizedBox(height: 18),
+              ],
+            ),
+          ),
+        ],
+      ),
+    ).onTap(() {
+      //切换展开收起
+      item.isExpended = !item.isExpended;
+      controller.update();
+    });
+  }
+}

+ 10 - 0
packages/cpt_ms/lib/modules/job/sign_in_sign_out/sign_in_sign_out_state.dart

@@ -0,0 +1,10 @@
+
+
+import 'package:flutter/material.dart';
+import 'package:plugin_basic/basic_export.dart';
+
+class SignInSignOutState {
+
+  final TextEditingController searchController = TextEditingController();
+
+}

+ 110 - 0
packages/cpt_ms/lib/modules/main/main_controller.dart

@@ -0,0 +1,110 @@
+
+import 'package:domain/entity/home_module.dart';
+import 'package:domain/entity/response/hotel_info_entity.dart';
+import 'package:domain/repository/auth_repository.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/constants/app_constant.dart';
+import 'package:plugin_basic/service/app_config_service.dart';
+import 'package:plugin_basic/service/user_service.dart';
+import 'package:plugin_platform/engine/sp/sp_util.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:router/componentRouter/component_router_service.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../job/sign_in_sign_out/sign_in_sign_out_page.dart';
+import 'main_state.dart';
+
+class MainController extends GetxController {
+  final AuthRepository _authRepository = Get.find();
+  final MainState state = MainState();
+
+  var _needShowPlaceholder = true;
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Success;
+  String? errorMessage;
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,
+    controlFinishLoad: false,
+  );
+
+  // Refresh 刷新事件
+  Future onRefresh() async {
+    fetchHomeData();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _needShowPlaceholder = true;
+    fetchHomeData();
+  }
+
+  /// 获取首页的数据
+  void fetchHomeData() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    }
+
+    //获取到数据
+    var result = await _authRepository.fetchHotelInfo(country: ConfigService.to.selectCountry.value);
+
+    //处理数据
+    if (result.isSuccess) {
+      final hotelInfo = result.data;
+
+      if (hotelInfo != null) {
+        UserService.to.hotelInfo.value = hotelInfo;
+
+        _handleList();
+      } else {
+        ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+      }
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+
+    //停止刷新
+    refreshController.finishRefresh();
+    //最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void _handleList() {
+
+      state.datas.clear();
+      state.datas.addAll(state.modules);
+
+      changeLoadingState(LoadState.State_Success);
+  }
+
+
+  @override
+  void onReady() async {
+    super.onReady();
+    fetchHomeData();
+  }
+
+  /// 跳转到指定的模块中去
+  void gotoModulePage(HomeModule module) {
+    switch (module.key) {
+      case 'sign':
+        SignInSignOutPage.startInstance();
+        break;
+    }
+  }
+
+  /// 跳转到设置页面
+  void gotoSettingPage() {
+    ComponentRouterServices.authService.startSettingPage();
+  }
+}

+ 46 - 0
packages/cpt_ms/lib/modules/main/main_item_module.dart

@@ -0,0 +1,46 @@
+// 自定义的模块项 Widget
+import 'package:domain/entity/home_module.dart';
+import 'package:flutter/material.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+class ModuleItem extends StatelessWidget {
+  final HomeModule item;
+  final VoidCallback onTap;
+
+  ModuleItem(this.item, this.onTap);
+
+  @override
+  Widget build(BuildContext context) {
+    return GestureDetector(
+      onTap: onTap,
+      child: Container(
+        decoration: BoxDecoration(
+          color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+          borderRadius: BorderRadius.circular(7.5), // 设置圆角
+        ),
+        child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            // 模块的名称
+            MyTextView(
+              item.moduleName,
+              textColor: Colors.white,
+              isTextEllipsis: true,
+              maxLines: 2,
+              isFontBold: true,
+              marginLeft: 20,
+              marginRight: 20,
+              marginTop: 20,
+              fontSize: 17,
+            ),
+            Center(
+              child: MyAssetImage(item.moduleIconPath, width: item.iconWidth, height: item.iconHeight),
+            ).expanded(),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 197 - 0
packages/cpt_ms/lib/modules/main/main_page.dart

@@ -0,0 +1,197 @@
+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:get/get.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/mixin_state_lifecycle.dart';
+import 'package:plugin_basic/service/user_service.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:cs_resources/local/theme/theme_config.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/double_tap_back_exit_app.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'main_controller.dart';
+import 'main_item_module.dart';
+import 'main_state.dart';
+
+/*
+   App首页页面
+ */
+class MSMainPage extends BaseStatefulPage<MainController> {
+  MSMainPage({super.key});
+
+  //启动当前页面
+  static void startInstance() {
+    return Get.start(RouterPath.MSMain);
+  }
+
+  static void startWithPopAll() {
+    Get.offAllNamed(RouterPath.MSMain);
+  }
+
+  @override
+  State<MSMainPage> createState() => _MainPageState();
+
+  @override
+  MainController createRawController() {
+    return MainController();
+  }
+}
+
+class _MainPageState extends BaseState<MSMainPage, MainController> with StateLifecycle {
+  late MainState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+  }
+
+  @override
+  void dispose() {
+    Get.delete<MainController>();
+    super.dispose();
+  }
+
+  @override
+  void onResume() {
+    super.onResume();
+    Log.d("MainPage Lifecycle - onResume");
+  }
+
+  @override
+  void onPause() {
+    super.onPause();
+    Log.d("MainPage Lifecycle - onPause");
+  }
+
+  @override
+  void onStop() {
+    super.onStop();
+    Log.d("MainPage Lifecycle - onStop");
+  }
+
+  @override
+  void onStart() {
+    super.onStart();
+    Log.d("MainPage Lifecycle - onStart");
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    Log.d("MainPage Lifecycle - build走了一遍");
+
+    //双击退出应用
+    return DoubleTapBackExitApp(
+      child: AnnotatedRegion<SystemUiOverlayStyle>(
+        value: ThemeConfig.systemUiOverlayStyleLightThemeWhite,
+        child: autoCtlGetBuilder(builder: (controller) {
+          return Scaffold(
+            body: SafeArea(
+              bottom: true,
+              top: false,
+              //真正的 Content 布局,使用PageView保存状态
+              child: Container(
+                width: double.infinity,
+                height: double.infinity,
+                padding: EdgeInsets.only(top: ScreenUtil.getStatusBarH(context)),
+                decoration: const BoxDecoration(
+                  gradient: LinearGradient(
+                    colors: [
+                      Color(0xFF091D44),
+                      Color(0xFF245A8A),
+                      Color(0xFF7F7CEC),
+                    ],
+                    begin: Alignment.topCenter,
+                    end: Alignment.bottomCenter,
+                  ),
+                ),
+                child: Column(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: [
+                    Row(
+                      mainAxisSize: MainAxisSize.max,
+                      mainAxisAlignment: MainAxisAlignment.start,
+                      crossAxisAlignment: CrossAxisAlignment.center,
+                      children: [
+                        // 酒店的名字
+                        Obx(() {
+                          return MyTextView(
+                            UserService.to.getHotelInfo.hotelName ?? "-",
+                            textColor: ColorConstants.white,
+                            isTextEllipsis: true,
+                            maxLines: 2,
+                            isFontBold: true,
+                            fontSize: 21,
+                          );
+                        }).expanded(),
+
+                        //设置图标,点击进入设置页面
+                        const MyAssetImage(Assets.mainHomeSetting, width: 33, height: 33).onTap(() => controller.gotoSettingPage()),
+                      ],
+                    ),
+
+                    //欢迎的文本+姓名
+                    Obx(() {
+                      return MyTextView(
+                        "${"Welcome".tr} ${UserService.to.getHotelInfo.name ?? "-"}",
+                        textColor: ColorConstants.textGray9EB0C1,
+                        isTextEllipsis: true,
+                        isFontMedium: true,
+                        marginTop: 5,
+                        marginBottom: 15,
+                        fontSize: 18,
+                      );
+                    }),
+
+                    //底部的列表
+                    EasyRefresh(
+                      controller: controller.refreshController,
+                      onRefresh: controller.onRefresh,
+                      child: LoadStateLayout(
+                        state: controller.loadingState,
+                        errorMessage: controller.errorMessage,
+                        errorRetry: () {
+                          controller.retryRequest();
+                        },
+                        successSliverWidget: [
+                          //接口控制显示的模块
+                          SliverGrid(
+                            delegate: SliverChildBuilderDelegate(
+                              (context, index) {
+                                return ModuleItem(state.datas[index], () {
+                                  controller.gotoModulePage(state.datas[index]);
+                                });
+                              },
+                              childCount: state.datas.length,
+                            ),
+                            gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
+                              crossAxisCount: 2, // 每行2个项目
+                              mainAxisSpacing: 5, // 主轴方向的间距
+                              crossAxisSpacing: 5, // 交叉轴方向的间距
+                              childAspectRatio: 171 / 161, // 子项目的宽高比
+                            ),
+                          ),
+                        ],
+                      ),
+                    ).expanded(),
+                  ],
+                ).paddingSymmetric(horizontal: 15, vertical: 17),
+              ),
+            ),
+          );
+        }),
+      ),
+    );
+  }
+}

+ 22 - 0
packages/cpt_ms/lib/modules/main/main_state.dart

@@ -0,0 +1,22 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/home_module.dart';
+import 'package:plugin_basic/basic_export.dart';
+
+class MainState {
+  //当前显示的模块
+  List<HomeModule> datas = [];
+
+  //全部的模块
+  final List<HomeModule> modules = [
+    // HomeModule(key: 'labReq', moduleName: 'Labour Request'.tr, moduleIconPath: Assets.mainHomeLabourRequest, iconWidth: 50, iconHeight: 40.3),
+    // HomeModule(key: 'jobList', moduleName: 'Job List'.tr, moduleIconPath: Assets.mainHomeJobList, iconWidth: 45, iconHeight: 45),
+    HomeModule(key: 'sign', moduleName: 'Sign in Sign out'.tr, moduleIconPath: Assets.mainHomeSignInOut, iconWidth: 44.5, iconHeight: 44.5),  //越南的手动签到
+    // HomeModule(key: 'reviseList', moduleName: 'Revise List'.tr, moduleIconPath: Assets.mainHomeSignInOut, iconWidth: 44.5, iconHeight: 44.5),  //新加坡的修改列表
+    // HomeModule(key: 'device', moduleName: 'Devices'.tr, moduleIconPath: Assets.mainHomeDevices, iconWidth: 45.5, iconHeight: 45.5),
+    // HomeModule(key: 'reqReview', moduleName: 'Labour Request Review'.tr, moduleIconPath: Assets.mainHomeLabourRequestReview, iconWidth: 50.5, iconHeight: 43),
+    // HomeModule(key: 'attReview', moduleName: 'Attendance Review'.tr, moduleIconPath: Assets.mainHomeAttendanceReview, iconWidth: 47.5, iconHeight: 46),
+    // HomeModule(key: 'template', moduleName: 'Default Job Title'.tr, moduleIconPath: Assets.mainHomeJobTemplate, iconWidth: 48.5, iconHeight: 46.5), //越南的模版
+    // HomeModule(key: 'jobTitle', moduleName: 'Default Job Title'.tr, moduleIconPath: Assets.mainHomeJobTemplate, iconWidth: 48.5, iconHeight: 46.5), //新加坡的模板
+    // HomeModule(key: 'report', moduleName: 'Report'.tr, moduleIconPath: Assets.mainHomeReport, iconWidth: 50.5, iconHeight: 45.5),
+  ];
+}

+ 0 - 0
packages/cpt_ms/lib/modules/ms_export.dart


+ 33 - 0
packages/cpt_ms/lib/router/ms_router.dart

@@ -0,0 +1,33 @@
+import 'package:cpt_ms/modules/job/sign_camera/sign_camera_page.dart';
+import 'package:cpt_ms/modules/job/sign_in_sign_out/sign_in_sign_out_page.dart';
+import 'package:get/get.dart';
+import 'package:router/path/router_path.dart';
+
+import '../modules/main/main_page.dart';
+
+
+
+/// 模块路由配置
+class MSPageRouter {
+  static final routes = <GetPage<dynamic>>[
+
+    //首页
+    GetPage(
+      name: RouterPath.MSMain,
+      page: () => MSMainPage(),
+    ),
+
+    //签到签出
+    GetPage(
+      name: RouterPath.MSJobSignInSignOut,
+      page: () => SignInSignOutPage(),
+    ),
+
+    //签到签出的拍摄页面
+    GetPage(
+      name: RouterPath.MSSignCamera,
+      page: () => SignCameraPage(),
+    ),
+
+  ];
+}

+ 23 - 0
packages/cpt_ms/lib/router/ms_service_impl.dart

@@ -0,0 +1,23 @@
+import 'package:cpt_ms/modules/main/main_page.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:router/componentRouter/ms_service.dart';
+
+/// 马来组件对应的路由实现类
+class MSServiceImpl extends GetxService implements MSService {
+  @override
+  void onInit() {
+    super.onInit();
+    //初始化资源
+  }
+
+  @override
+  void onClose() {
+    super.onClose();
+    //销毁资源
+  }
+
+  @override
+  void startMSMainPage() {
+    MSMainPage.startWithPopAll();
+  }
+}

+ 40 - 0
packages/cpt_ms/pubspec.yaml

@@ -0,0 +1,40 @@
+name: cpt_ms
+description: 按照国家区分模块-马来西亚
+
+version: 1.0.0
+
+environment:
+  sdk: '>=3.0.2 <4.0.0'
+
+dependencies:
+
+  flutter_localizations:
+    sdk: flutter
+
+  flutter:
+    sdk: flutter
+
+  #基础组件的依赖
+  domain:
+    path: ../cs_domain
+
+  plugin_basic:
+    path: ../cs_plugin_basic
+
+  plugin_platform:
+    path: ../cs_plugin_platform
+
+  shared:
+    path: ../cs_shared
+
+  cs_resources:
+    path: ../cs_resources
+
+  router:
+    path: ../cs_router
+
+  widgets:
+    path: ../cs_widgets
+
+flutter:
+  uses-material-design: true

+ 16 - 0
packages/cpt_ms/pubspec_overrides.yaml

@@ -0,0 +1,16 @@
+# melos_managed_dependency_overrides: cs_resources,domain,plugin_basic,plugin_platform,router,shared,widgets
+dependency_overrides:
+  cs_resources:
+    path: ../cs_resources
+  domain:
+    path: ../cs_domain
+  plugin_basic:
+    path: ../cs_plugin_basic
+  plugin_platform:
+    path: ../cs_plugin_platform
+  router:
+    path: ../cs_router
+  shared:
+    path: ../cs_shared
+  widgets:
+    path: ../cs_widgets

+ 31 - 0
packages/cpt_nl/.gitignore

@@ -0,0 +1,31 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+*.lock
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# Visual Studio Code related
+.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+proguardMapping.txt

+ 203 - 0
packages/cpt_nl/lib/modules/job/sign_in_sign_out/sign_in_sign_out_controller.dart

@@ -0,0 +1,203 @@
+import 'dart:typed_data';
+
+import 'package:domain/entity/response/attendance_entity.dart';
+import 'package:domain/entity/response/check_success_entity.dart';
+import 'package:domain/repository/job_repository.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/service/user_service.dart';
+import 'package:plugin_platform/engine/dialog/dialog_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/picker/date_picker_util.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../widget/attendance_sign_in_out.dart';
+import 'sign_in_sign_out_state.dart';
+
+class SignInSignOutController extends GetxController with DioCancelableMixin {
+  final JobRepository _jobRepository = Get.find();
+  final SignInSignOutState state = SignInSignOutState();
+
+  var _needShowPlaceholder = true;
+
+  //页面的列表数据
+  List<AttendanceList> datas = [];
+  String keyword = "";
+  Rx<DateTime> startDate = DateTime.now().obs;
+  Rx<DateTime> endDate = DateTime.now().obs;
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Success;
+  String? errorMessage;
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,
+    controlFinishLoad: false,
+  );
+
+  // Refresh 刷新事件
+  Future onRefresh() async {
+    fetchAttendanceList();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _needShowPlaceholder = true;
+    fetchAttendanceList();
+  }
+
+  /// 获取服务器数据,成员考勤列表
+  Future fetchAttendanceList() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    }
+
+    //获取到数据
+    var result = await _jobRepository.fetchAttendanceList(
+      keyword,
+      DateTimeUtils.formatDate(startDate.value, format: "yyyy-MM-dd"),
+      DateTimeUtils.formatDate(endDate.value, format: "yyyy-MM-dd"),
+      cancelToken: cancelToken,
+    );
+
+    //处理数据
+    if (result.isSuccess) {
+      handleList(result.data?.rows);
+      refreshController.finishRefresh(IndicatorResult.success);
+    } else {
+      errorMessage = result.errorMsg;
+      changeLoadingState(LoadState.State_Error);
+      refreshController.finishRefresh(IndicatorResult.fail);
+    }
+
+    //最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void handleList(List<AttendanceList>? list) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      datas.clear();
+      datas.addAll(list);
+      //更新状态
+      changeLoadingState(LoadState.State_Success);
+    } else {
+      //展示无数据的布局
+      datas.clear();
+      changeLoadingState(LoadState.State_Empty);
+    }
+  }
+
+  /// 签到签出
+  void requestCheckInCheckOut(bool isCheckIn, AttendanceList item, ByteData byteData) async {
+    //请求接口
+
+    var result = await _jobRepository.submitCheckInOut(
+      item.appliedId.toString(),
+      byteData,
+      isCheckIn: isCheckIn,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      CheckSuccessEntity entity = result.data!;
+      if (isCheckIn) {
+        item.checkInImg = entity.checkImg;
+        item.checkInTime = entity.checkTime;
+      } else {
+        item.checkOutImg = entity.checkImg;
+        item.checkOutTime = entity.checkTime;
+      }
+      //更新状态
+      update();
+    } else {
+      errorMessage = result.errorMsg;
+      ToastEngine.show(errorMessage ?? "Network Load Error".tr);
+    }
+  }
+
+  //执行搜索
+  void doSearch(String keyword) {
+    this.keyword = keyword;
+    refreshController.callRefresh();
+  }
+
+  @override
+  void onReady() async {
+    super.onReady();
+    fetchAttendanceList();
+  }
+
+  @override
+  void onClose() {
+    super.onClose();
+    datas.clear();
+  }
+
+  /// 用户签到
+  void userSignIn(AttendanceList item) {
+    DialogEngine.show(
+      widget: AttendanceSignInOut(
+        confirmAction: (byteData) {
+          requestCheckInCheckOut(true, item, byteData);
+        },
+      ),
+    );
+  }
+
+  /// 用户签出
+  void userSignOut(AttendanceList item) {
+    DialogEngine.show(
+      widget: AttendanceSignInOut(
+        confirmAction: (byteData) {
+          requestCheckInCheckOut(false, item, byteData);
+        },
+      ),
+    );
+  }
+
+  /// 重置筛选条件
+  void resetFiltering() {
+    keyword = "";
+    state.searchController.text = "";
+    startDate.value = DateTime.now();
+    endDate.value = DateTime.now();
+    update();
+
+    refreshController.callRefresh();
+  }
+
+  /// 筛选开始日期
+  void pickerStartDate() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: startDate.value,
+      onDateTimeChanged: (date) {
+        startDate.value = date;
+        refreshController.callRefresh();
+      },
+      title: "Start Date".tr,
+    );
+  }
+
+  /// 筛选结束日期
+  void pickerEndDate() {
+    DatePickerUtil.showCupertinoDatePicker(
+      selectedDateTime: endDate.value,
+      onDateTimeChanged: (date) {
+        endDate.value = date;
+        refreshController.callRefresh();
+      },
+      title: "End Date".tr,
+    );
+  }
+}

+ 440 - 0
packages/cpt_nl/lib/modules/job/sign_in_sign_out/sign_in_sign_out_page.dart

@@ -0,0 +1,440 @@
+import 'dart:ui';
+
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/src/services/predictive_back_event.dart';
+import 'package:flutter/widgets.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/mixin_state_lifecycle.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/date_time_utils.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:shared/utils/util.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+import 'sign_in_sign_out_controller.dart';
+import 'sign_in_sign_out_state.dart';
+
+/*
+   工作的签到页面
+ */
+class SignInSignOutPage extends BaseStatefulPage<SignInSignOutController> {
+  SignInSignOutPage({super.key});
+
+  //启动当前页面
+  static void startInstance() {
+    return Get.start(RouterPath.NLJobSignInSignOut);
+  }
+
+  @override
+  State<SignInSignOutPage> createState() => _SignInSignOutPageState();
+
+  @override
+  SignInSignOutController createRawController() {
+    return SignInSignOutController();
+  }
+}
+
+class _SignInSignOutPageState extends BaseState<SignInSignOutPage, SignInSignOutController> with StateLifecycle {
+  late SignInSignOutState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+  }
+
+  @override
+  void dispose() {
+    Get.delete<SignInSignOutController>();
+    super.dispose();
+  }
+
+  @override
+  void onResume() {
+    super.onResume();
+    Log.d("SignInSignOutPage Lifecycle - onResume");
+  }
+
+  @override
+  void onPause() {
+    super.onPause();
+    Log.d("SignInSignOutPage Lifecycle - onPause");
+  }
+
+  @override
+  void onStop() {
+    super.onStop();
+    Log.d("SignInSignOutPage Lifecycle - onStop");
+  }
+
+  @override
+  void onStart() {
+    super.onStart();
+    Log.d("SignInSignOutPage Lifecycle - onStart");
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    Log.d("SignInSignOutPage Lifecycle - build走了一遍");
+
+    return autoCtlGetBuilder(
+      builder: (controller) {
+        return SafeArea(
+          bottom: false,
+          top: false,
+          child: Container(
+            width: double.infinity,
+            height: double.infinity,
+            padding: EdgeInsets.only(top: ScreenUtil.getStatusBarH(context)),
+            decoration: const BoxDecoration(
+              gradient: LinearGradient(
+                colors: [
+                  Color(0xFF091D44),
+                  Color(0xFF245A8A),
+                  Color(0xFF7F7CEC),
+                ],
+                begin: Alignment.topCenter,
+                end: Alignment.bottomCenter,
+              ),
+            ),
+            child: Column(
+              children: [
+                //顶部的搜索标题布局
+                MyAppBar.searchTitleBar(
+                  context,
+                  value: controller.keyword,
+                  hintText: 'Name/Mobile'.tr,
+                  controller: state.searchController,
+                  onSearch: (keyword) {
+                    controller.doSearch(keyword);
+                  },
+                  actions: [
+                    MyButton(
+                      onPressed: () {
+                        controller.resetFiltering();
+                      },
+                      text: "Reset".tr,
+                      textColor: ColorConstants.white,
+                      backgroundColor: hexToColor("#2BA9F9", opacity: 0.5),
+                      radius: 17.25,
+                      minWidth: 60,
+                      minHeight: 35,
+                    ).marginOnly(right: 15)
+                  ],
+                ),
+
+                _buildFilterDateWidget(),
+
+                EasyRefresh(
+                  controller: controller.refreshController,
+                  onRefresh: controller.onRefresh,
+                  child: LoadStateLayout(
+                    state: controller.loadingState,
+                    errorMessage: controller.errorMessage,
+                    errorRetry: () {
+                      controller.retryRequest();
+                    },
+                    successSliverWidget: [
+                      SliverList(
+                          delegate: SliverChildBuilderDelegate(
+                        (context, index) {
+                          return _buildAttendanceItem(controller, index, () {});
+                        },
+                        childCount: controller.datas.length,
+                      ))
+                    ],
+                  ),
+                ).expanded(),
+              ],
+            ),
+          ),
+        );
+      },
+    );
+  }
+
+  /// 时间筛选布局
+  Widget _buildFilterDateWidget() {
+    return Container(
+      width: double.infinity,
+      height: 36,
+      margin: EdgeInsets.only(left: 15, right: 15, top: 15, bottom: 10),
+      decoration: BoxDecoration(
+        color: Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+        borderRadius: BorderRadius.circular(17.25), // 设置圆角
+      ),
+      child: Row(
+        mainAxisSize: MainAxisSize.max,
+        crossAxisAlignment: CrossAxisAlignment.center,
+        children: [
+          Obx(() {
+            return MyTextView(
+              DateTimeUtils.formatDate(controller.startDate.value, format: "yyyy-MM-dd"),
+              fontSize: 16,
+              textAlign: TextAlign.center,
+              isFontRegular: true,
+              textColor: ColorConstants.white,
+              onClick: () {
+                controller.pickerStartDate();
+              },
+            );
+          }).expanded(),
+          Container(color: Color(0XFF52739C), width: 0.5, height: 21.5),
+          Obx(() {
+            return MyTextView(
+              DateTimeUtils.formatDate(controller.endDate.value, format: "yyyy-MM-dd"),
+              fontSize: 16,
+              textAlign: TextAlign.center,
+              isFontRegular: true,
+              textColor: ColorConstants.white,
+              onClick: () {
+                controller.pickerEndDate();
+              },
+            );
+          }).expanded(),
+        ],
+      ),
+    );
+  }
+
+  /// Item列表
+  Widget _buildAttendanceItem(SignInSignOutController controller, int index, void Function() callback) {
+    final item = controller.datas[index];
+
+    return Container(
+      padding: EdgeInsets.only(left: 23, right: 12),
+      margin: EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 5),
+      decoration: BoxDecoration(
+        color: Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+        borderRadius: BorderRadius.circular(5), // 设置圆角
+      ),
+      child: Column(
+        children: [
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              MyTextView(
+                "Name:".tr,
+                isFontRegular: true,
+                textColor: Color(0XFFAECAE5),
+                fontSize: 14,
+              ),
+
+              //姓名
+              MyTextView(
+                item.staffName ?? "-",
+                marginLeft: 5,
+                isFontRegular: true,
+                textColor: Color(0XFFAECAE5),
+                fontSize: 14,
+              ).expanded(),
+
+              MyAssetImage(item.isExpended ? Assets.cptJobArrawUpIcon : Assets.cptJobArrawDownIcon, width: 15, height: 7),
+            ],
+          ).paddingOnly(top: 19, bottom: 19),
+
+          //显示隐藏的布局
+          Visibility(
+            visible: item.isExpended,
+            child: Column(
+              children: [
+                //工作日期
+                Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  children: [
+                    MyTextView(
+                      "Job Date".tr+":",
+                      isFontRegular: true,
+                      textColor: Color(0XFFAECAE5),
+                      fontSize: 14,
+                    ),
+
+                    //姓名
+                    MyTextView(
+                      item.jobDate ?? "-",
+                      marginLeft: 5,
+                      isFontRegular: true,
+                      textColor: Color(0XFFAECAE5),
+                      fontSize: 14,
+                    ).expanded(),
+                  ],
+                ),
+
+                //开始时间
+                Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  children: [
+                    MyTextView(
+                      "Start Time:",
+                      isFontRegular: true,
+                      textColor: Color(0XFFAECAE5),
+                      fontSize: 14,
+                    ),
+
+                    //姓名
+                    MyTextView(
+                      item.startTime ?? "-",
+                      marginLeft: 5,
+                      isFontRegular: true,
+                      textColor: Color(0XFFAECAE5),
+                      fontSize: 14,
+                    ).expanded(),
+                  ],
+                ).marginOnly(top: 16),
+
+                //结束时间
+                Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  children: [
+                    MyTextView(
+                      "End Time:".tr,
+                      isFontRegular: true,
+                      textColor: Color(0XFFAECAE5),
+                      fontSize: 14,
+                    ),
+
+                    //姓名
+                    MyTextView(
+                      item.endTime ?? "-",
+                      marginLeft: 5,
+                      isFontRegular: true,
+                      textColor: Color(0XFFAECAE5),
+                      fontSize: 14,
+                    ).expanded(),
+                  ],
+                ).marginOnly(top: 16),
+
+                //签到的控件
+                Row(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    //开始签到
+                    Column(
+                      mainAxisAlignment: MainAxisAlignment.start,
+                      crossAxisAlignment: CrossAxisAlignment.center,
+                      children: [
+                        Container(
+                          width: 90,
+                          height: 35,
+                          margin: EdgeInsets.only(top: 18),
+                          decoration: BoxDecoration(
+                            color: Utils.isEmpty(item.checkInImg) ? Color(0xFF56AAFF) : ColorConstants.white,
+                            borderRadius: BorderRadius.circular(17.25), // 设置圆角
+                          ),
+                          child: Stack(
+                            alignment: Alignment.center,
+                            children: [
+                              Visibility(
+                                visible: Utils.isEmpty(item.checkInImg),
+                                child: MyTextView(
+                                  "Check In".tr,
+                                  isFontMedium: true,
+                                  textColor: ColorConstants.white,
+                                  fontSize: 14,
+                                ),
+                              ),
+                              Visibility(
+                                visible: !Utils.isEmpty(item.checkInImg),
+                                child: MyLoadImage(item.checkInImg, width: 71.5, height: 25, fit: BoxFit.cover),
+                              ),
+                            ],
+                          ).onTap(() {
+                            if (Utils.isEmpty(item.checkInImg)) {
+                              controller.userSignIn(item);
+                            }
+                          }),
+                        ),
+                        Visibility(
+                          visible: !Utils.isEmpty(item.checkInTime),
+                          child: MyTextView(
+                            marginTop: 9,
+                            item.checkInTime ?? "-",
+                            textColor: ColorConstants.white,
+                            fontSize: 14,
+                            isFontRegular: true,
+                          ),
+                        ),
+                      ],
+                    ),
+
+                    //结束签到
+                    Column(
+                      mainAxisAlignment: MainAxisAlignment.start,
+                      crossAxisAlignment: CrossAxisAlignment.center,
+                      children: [
+                        Container(
+                          width: 90,
+                          height: 35,
+                          margin: EdgeInsets.only(top: 18),
+                          decoration: BoxDecoration(
+                            color: Utils.isEmpty(item.checkOutImg) ? Color(0xFF56AAFF) : ColorConstants.white,
+                            borderRadius: BorderRadius.circular(17.25), // 设置圆角
+                          ),
+                          child: Stack(
+                            alignment: Alignment.center,
+                            children: [
+                              Visibility(
+                                visible: Utils.isEmpty(item.checkOutImg),
+                                child: MyTextView(
+                                  "Check Out".tr,
+                                  isFontMedium: true,
+                                  textColor: ColorConstants.white,
+                                  fontSize: 14,
+                                ),
+                              ),
+                              Visibility(
+                                visible: !Utils.isEmpty(item.checkOutImg),
+                                child: MyLoadImage(item.checkOutImg, width: 71.5, height: 25, fit: BoxFit.cover),
+                              ),
+                            ],
+                          ).onTap(() {
+                            if (Utils.isEmpty(item.checkOutImg)) {
+                              controller.userSignOut(item);
+                            }
+                          }),
+                        ),
+                        Visibility(
+                          visible: !Utils.isEmpty(item.checkOutTime),
+                          child: MyTextView(
+                            marginTop: 9,
+                            item.checkOutTime ?? "-",
+                            textColor: ColorConstants.white,
+                            fontSize: 14,
+                            isFontRegular: true,
+                          ),
+                        ),
+                      ],
+                    ).marginOnly(left: 10),
+                  ],
+                ),
+
+                SizedBox(height: 18),
+              ],
+            ),
+          ),
+        ],
+      ),
+    ).onTap(() {
+      //切换展开收起
+      item.isExpended = !item.isExpended;
+      controller.update();
+    });
+  }
+}

+ 10 - 0
packages/cpt_nl/lib/modules/job/sign_in_sign_out/sign_in_sign_out_state.dart

@@ -0,0 +1,10 @@
+
+
+import 'package:flutter/material.dart';
+import 'package:plugin_basic/basic_export.dart';
+
+class SignInSignOutState {
+
+  final TextEditingController searchController = TextEditingController();
+
+}

+ 160 - 0
packages/cpt_nl/lib/modules/job/widget/attendance_sign_in_out.dart

@@ -0,0 +1,160 @@
+import 'dart:typed_data';
+import 'dart:ui';
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+/**
+ * 签到签出的签名弹窗
+ */
+class AttendanceSignInOut extends StatelessWidget {
+  VoidCallback? cancelAction;
+  void Function(ByteData byteData)? confirmAction;
+
+  //签名版配置
+  HandSignatureControl handSignatureControl = HandSignatureControl(
+    threshold: 3.0,
+    smoothRatio: 200 / 240,
+    velocityRange: 2.0,
+  );
+
+  AttendanceSignInOut({this.cancelAction, this.confirmAction});
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      width: 283,
+      height: 320,
+      decoration: BoxDecoration(
+        color: Colors.white, // 设置背景颜色
+        borderRadius: BorderRadius.circular(15), // 设置圆角
+      ),
+      child: Column(
+        children: [
+          MyTextView(
+            "Sign Here".tr,
+            fontSize: 19,
+            isFontMedium: true,
+            textColor: ColorConstants.black,
+            marginTop: 15,
+            marginBottom: 12,
+          ),
+
+          Stack(
+            children: [
+              //签名
+              Center(
+                child: Container(
+                  width: 240,
+                  height: 200,
+                  color: Color(0XFFF0F0F0),
+                  child: HandSignature(
+                    control: handSignatureControl,
+                    color: ColorConstants.black404A5B,
+                    width: 1.0,
+                    maxWidth: 3.5,
+                    type: SignatureDrawType.shape,
+                  ),
+                ),
+              ),
+
+              //清除签名
+              Align(
+                alignment: Alignment.bottomLeft,
+                child: MyTextView(
+                  "Clean".tr,
+                  fontSize: 12,
+                  textColor: ColorConstants.white,
+                  cornerRadius: 10.37,
+                  backgroundColor: Color(0XFFFFBB1B),
+                  paddingTop: 4,
+                  paddingBottom: 4,
+                  paddingLeft: 11,
+                  paddingRight: 11,
+                  margin: 10,
+                  onClick: () {
+                    handSignatureControl.clear();
+                  },
+                ),
+              ),
+            ],
+          ).constrained(
+            width: 240,
+            height: 200,
+          ),
+
+          Container(
+            color: Color(0XFFCECECE),
+            height: 0.5,
+            margin: EdgeInsets.only(top: 18),
+          ),
+
+          //Buttons
+          Row(
+            children: [
+              Expanded(
+                  flex: 1,
+                  child: InkWell(
+                    onTap: () {
+                      onCancel();
+                      cancelAction?.call();
+                    },
+                    child: MyTextView(
+                      "Cancel".tr,
+                      fontSize: 17.5,
+                      isFontMedium: true,
+                      textAlign: TextAlign.center,
+                      textColor: Color(0XFF0085C4),
+                      cornerRadius: 3,
+                      borderWidth: 1,
+                    ),
+                  )),
+              Container(
+                color: Color(0XFFCECECE),
+                width: 0.5,
+              ),
+              Expanded(
+                  flex: 1,
+                  child: InkWell(
+                    onTap: () async {
+                      //签名数据
+                      var byteData = await handSignatureControl.toImage(
+                        format: ImageByteFormat.png,
+                        border: 0,
+                        width: 240,
+                        height: 200,
+                        background: Colors.white,
+                      ) as ByteData;
+
+                      onCancel();
+                      confirmAction?.call(byteData);
+                    },
+                    child: MyTextView(
+                      "Confirm".tr,
+                      marginLeft: 10,
+                      fontSize: 17.5,
+                      isFontMedium: true,
+                      textAlign: TextAlign.center,
+                      textColor: Color(0XFF0085C4),
+                      cornerRadius: 3,
+                    ),
+                  )),
+            ],
+          ).expanded(),
+        ],
+      ),
+    );
+  }
+
+  //取消弹框
+  void onCancel() async {
+    SmartDialog.dismiss();
+  }
+}

+ 131 - 0
packages/cpt_nl/lib/modules/main/main_controller.dart

@@ -0,0 +1,131 @@
+import 'package:cpt_nl/modules/job/sign_in_sign_out/sign_in_sign_out_page.dart';
+import 'package:domain/entity/home_module.dart';
+import 'package:domain/entity/response/hotel_info_entity.dart';
+import 'package:domain/repository/auth_repository.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/constants/app_constant.dart';
+import 'package:plugin_basic/service/app_config_service.dart';
+import 'package:plugin_basic/service/user_service.dart';
+import 'package:plugin_platform/engine/sp/sp_util.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:router/componentRouter/component_router_service.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'main_state.dart';
+
+class MainController extends GetxController {
+  final AuthRepository _authRepository = Get.find();
+  final MainState state = MainState();
+
+  var _needShowPlaceholder = true;
+
+  //页面PlaceHolder的展示
+  LoadState loadingState = LoadState.State_Success;
+  String? errorMessage;
+
+  //刷新页面状态
+  void changeLoadingState(LoadState state) {
+    loadingState = state;
+    update();
+  }
+
+  // Refresh 控制器
+  final EasyRefreshController refreshController = EasyRefreshController(
+    controlFinishRefresh: true,
+    controlFinishLoad: false,
+  );
+
+  // Refresh 刷新事件
+  Future onRefresh() async {
+    fetchHomeData();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _needShowPlaceholder = true;
+    fetchHomeData();
+  }
+
+  /// 获取首页的数据
+  void fetchHomeData() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    }
+
+    //获取到数据
+    var result = await _authRepository.fetchHotelInfo(country: ConfigService.to.selectCountry.value);
+
+    //处理数据
+    if (result.isSuccess) {
+      final hotelInfo = result.data;
+
+      if (hotelInfo != null) {
+        UserService.to.hotelInfo.value = hotelInfo;
+
+        _handleList(hotelInfo.menus);
+      } else {
+        ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+      }
+    } else {
+      ToastEngine.show(result.errorMsg ?? "Network Load Error".tr);
+    }
+
+    //停止刷新
+    refreshController.finishRefresh();
+    //最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void _handleList(List<HotelInfoMenus>? list) {
+    if (list != null && list.isNotEmpty) {
+      //有数据,判断是刷新还是加载更多的数据
+      state.datas.clear();
+
+      int? isAdmin = SPUtil.getInt(AppConstant.storageIsAdmin);
+      if (isAdmin == 1) {
+        //如果是管理员登录,根据Key筛选需要展示的模块
+        for (var hotelInfo in list) {
+          if (hotelInfo.key != null) {
+            state.datas.addAll(_filterModulesByKey(hotelInfo.key!));
+          }
+        }
+      } else {
+        //如果只是签到签出模式,手动的添加模块
+        state.datas.addAll(_filterModulesByKey("sign"));
+      }
+
+      //更新状态
+      changeLoadingState(LoadState.State_Success);
+    } else {
+      //展示无数据的布局
+      state.datas.clear();
+      changeLoadingState(LoadState.State_Empty);
+    }
+  }
+
+  List<HomeModule> _filterModulesByKey(String key) {
+    return state.modules.where((module) => module.key == key).toList();
+  }
+
+  @override
+  void onReady() async {
+    super.onReady();
+    fetchHomeData();
+  }
+
+  /// 跳转到指定的模块中去
+  void gotoModulePage(HomeModule module) {
+    switch (module.key) {
+      case 'sign':
+        SignInSignOutPage.startInstance();
+        break;
+    }
+  }
+
+  /// 跳转到设置页面
+  void gotoSettingPage() {
+    ComponentRouterServices.authService.startSettingPage();
+  }
+}

+ 46 - 0
packages/cpt_nl/lib/modules/main/main_item_module.dart

@@ -0,0 +1,46 @@
+// 自定义的模块项 Widget
+import 'package:domain/entity/home_module.dart';
+import 'package:flutter/material.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+class ModuleItem extends StatelessWidget {
+  final HomeModule item;
+  final VoidCallback onTap;
+
+  ModuleItem(this.item, this.onTap);
+
+  @override
+  Widget build(BuildContext context) {
+    return GestureDetector(
+      onTap: onTap,
+      child: Container(
+        decoration: BoxDecoration(
+          color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+          borderRadius: BorderRadius.circular(7.5), // 设置圆角
+        ),
+        child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            // 模块的名称
+            MyTextView(
+              item.moduleName,
+              textColor: Colors.white,
+              isTextEllipsis: true,
+              maxLines: 2,
+              isFontBold: true,
+              marginLeft: 20,
+              marginRight: 20,
+              marginTop: 20,
+              fontSize: 17,
+            ),
+            Center(
+              child: MyAssetImage(item.moduleIconPath, width: item.iconWidth, height: item.iconHeight),
+            ).expanded(),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 198 - 0
packages/cpt_nl/lib/modules/main/main_page.dart

@@ -0,0 +1,198 @@
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/home_module.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:get/get.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/mixin_state_lifecycle.dart';
+import 'package:plugin_basic/service/user_service.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:cs_resources/local/theme/theme_config.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/double_tap_back_exit_app.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'main_controller.dart';
+import 'main_item_module.dart';
+import 'main_state.dart';
+
+/*
+   App首页页面
+ */
+class NLMainPage extends BaseStatefulPage<MainController> {
+  NLMainPage({super.key});
+
+  //启动当前页面
+  static void startInstance() {
+    return Get.start(RouterPath.NLMain);
+  }
+
+  static void startWithPopAll() {
+    Get.offAllNamed(RouterPath.NLMain);
+  }
+
+  @override
+  State<NLMainPage> createState() => _MainPageState();
+
+  @override
+  MainController createRawController() {
+    return MainController();
+  }
+}
+
+class _MainPageState extends BaseState<NLMainPage, MainController> with StateLifecycle {
+  late MainState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+  }
+
+  @override
+  void dispose() {
+    Get.delete<MainController>();
+    super.dispose();
+  }
+
+  @override
+  void onResume() {
+    super.onResume();
+    Log.d("MainPage Lifecycle - onResume");
+  }
+
+  @override
+  void onPause() {
+    super.onPause();
+    Log.d("MainPage Lifecycle - onPause");
+  }
+
+  @override
+  void onStop() {
+    super.onStop();
+    Log.d("MainPage Lifecycle - onStop");
+  }
+
+  @override
+  void onStart() {
+    super.onStart();
+    Log.d("MainPage Lifecycle - onStart");
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    Log.d("MainPage Lifecycle - build走了一遍");
+
+    //双击退出应用
+    return DoubleTapBackExitApp(
+      child: AnnotatedRegion<SystemUiOverlayStyle>(
+        value: ThemeConfig.systemUiOverlayStyleLightThemeWhite,
+        child: autoCtlGetBuilder(builder: (controller) {
+          return Scaffold(
+            body: SafeArea(
+              bottom: true,
+              top: false,
+              //真正的 Content 布局,使用PageView保存状态
+              child: Container(
+                width: double.infinity,
+                height: double.infinity,
+                padding: EdgeInsets.only(top: ScreenUtil.getStatusBarH(context)),
+                decoration: const BoxDecoration(
+                  gradient: LinearGradient(
+                    colors: [
+                      Color(0xFF091D44),
+                      Color(0xFF245A8A),
+                      Color(0xFF7F7CEC),
+                    ],
+                    begin: Alignment.topCenter,
+                    end: Alignment.bottomCenter,
+                  ),
+                ),
+                child: Column(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: [
+                    Row(
+                      mainAxisSize: MainAxisSize.max,
+                      mainAxisAlignment: MainAxisAlignment.start,
+                      crossAxisAlignment: CrossAxisAlignment.center,
+                      children: [
+                        // 酒店的名字
+                        Obx(() {
+                          return MyTextView(
+                            UserService.to.getHotelInfo.hotelName ?? "-",
+                            textColor: ColorConstants.white,
+                            isTextEllipsis: true,
+                            maxLines: 2,
+                            isFontBold: true,
+                            fontSize: 21,
+                          );
+                        }).expanded(),
+
+                        //设置图标,点击进入设置页面
+                        const MyAssetImage(Assets.mainHomeSetting, width: 33, height: 33).onTap(() => controller.gotoSettingPage()),
+                      ],
+                    ),
+
+                    //欢迎的文本+姓名
+                    Obx(() {
+                      return MyTextView(
+                        "${"Welcome".tr} ${UserService.to.getHotelInfo.name ?? "-"}",
+                        textColor: ColorConstants.textGray9EB0C1,
+                        isTextEllipsis: true,
+                        isFontMedium: true,
+                        marginTop: 5,
+                        marginBottom: 15,
+                        fontSize: 18,
+                      );
+                    }),
+
+                    //底部的列表
+                    EasyRefresh(
+                      controller: controller.refreshController,
+                      onRefresh: controller.onRefresh,
+                      child: LoadStateLayout(
+                        state: controller.loadingState,
+                        errorMessage: controller.errorMessage,
+                        errorRetry: () {
+                          controller.retryRequest();
+                        },
+                        successSliverWidget: [
+                          //接口控制显示的模块
+                          SliverGrid(
+                            delegate: SliverChildBuilderDelegate(
+                              (context, index) {
+                                return ModuleItem(state.datas[index], () {
+                                  controller.gotoModulePage(state.datas[index]);
+                                });
+                              },
+                              childCount: state.datas.length,
+                            ),
+                            gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
+                              crossAxisCount: 2, // 每行2个项目
+                              mainAxisSpacing: 5, // 主轴方向的间距
+                              crossAxisSpacing: 5, // 交叉轴方向的间距
+                              childAspectRatio: 171 / 161, // 子项目的宽高比
+                            ),
+                          ),
+                        ],
+                      ),
+                    ).expanded(),
+                  ],
+                ).paddingSymmetric(horizontal: 15, vertical: 17),
+              ),
+            ),
+          );
+        }),
+      ),
+    );
+  }
+}

+ 22 - 0
packages/cpt_nl/lib/modules/main/main_state.dart

@@ -0,0 +1,22 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/home_module.dart';
+import 'package:plugin_basic/basic_export.dart';
+
+class MainState {
+  //当前显示的模块
+  List<HomeModule> datas = [];
+
+  //全部的模块
+  final List<HomeModule> modules = [
+    HomeModule(key: 'labReq', moduleName: 'Labour Request'.tr, moduleIconPath: Assets.mainHomeLabourRequest, iconWidth: 50, iconHeight: 40.3),
+    HomeModule(key: 'jobList', moduleName: 'Job List'.tr, moduleIconPath: Assets.mainHomeJobList, iconWidth: 45, iconHeight: 45),
+    HomeModule(key: 'sign', moduleName: 'Sign in Sign out'.tr, moduleIconPath: Assets.mainHomeSignInOut, iconWidth: 44.5, iconHeight: 44.5),  //越南的手动签到
+    HomeModule(key: 'reviseList', moduleName: 'Revise List'.tr, moduleIconPath: Assets.mainHomeSignInOut, iconWidth: 44.5, iconHeight: 44.5),  //新加坡的修改列表
+    HomeModule(key: 'device', moduleName: 'Devices'.tr, moduleIconPath: Assets.mainHomeDevices, iconWidth: 45.5, iconHeight: 45.5),
+    HomeModule(key: 'reqReview', moduleName: 'Labour Request Review'.tr, moduleIconPath: Assets.mainHomeLabourRequestReview, iconWidth: 50.5, iconHeight: 43),
+    HomeModule(key: 'attReview', moduleName: 'Attendance Review'.tr, moduleIconPath: Assets.mainHomeAttendanceReview, iconWidth: 47.5, iconHeight: 46),
+    HomeModule(key: 'template', moduleName: 'Default Job Title'.tr, moduleIconPath: Assets.mainHomeJobTemplate, iconWidth: 48.5, iconHeight: 46.5), //越南的模版
+    HomeModule(key: 'jobTitle', moduleName: 'Default Job Title'.tr, moduleIconPath: Assets.mainHomeJobTemplate, iconWidth: 48.5, iconHeight: 46.5), //新加坡的模板
+    HomeModule(key: 'report', moduleName: 'Report'.tr, moduleIconPath: Assets.mainHomeReport, iconWidth: 50.5, iconHeight: 45.5),
+  ];
+}

+ 0 - 0
packages/cpt_nl/lib/modules/nl_export.dart


+ 24 - 0
packages/cpt_nl/lib/router/nl_router.dart

@@ -0,0 +1,24 @@
+import 'package:cpt_nl/modules/job/sign_in_sign_out/sign_in_sign_out_page.dart';
+import 'package:cpt_nl/modules/main/main_page.dart';
+import 'package:get/get.dart';
+import 'package:router/path/router_path.dart';
+
+
+/// 模块路由配置
+class NLPageRouter {
+  static final routes = <GetPage<dynamic>>[
+
+    //首页
+    GetPage(
+      name: RouterPath.NLMain,
+      page: () => NLMainPage(),
+    ),
+
+    //签到签出
+    GetPage(
+      name: RouterPath.NLJobSignInSignOut,
+      page: () => SignInSignOutPage(),
+    ),
+
+  ];
+}

+ 23 - 0
packages/cpt_nl/lib/router/nl_service_impl.dart

@@ -0,0 +1,23 @@
+
+import 'package:plugin_basic/basic_export.dart';
+import 'package:router/componentRouter/nl_service.dart';
+
+/// 荷兰组件对应的路由实现类
+class NLServiceImpl extends GetxService implements NLService {
+  @override
+  void onInit() {
+    super.onInit();
+    //初始化资源
+  }
+
+  @override
+  void onClose() {
+    super.onClose();
+    //销毁资源
+  }
+
+  @override
+  void startNLMainPage() {
+
+  }
+}

+ 40 - 0
packages/cpt_nl/pubspec.yaml

@@ -0,0 +1,40 @@
+name: cpt_nl
+description: 按照国家区分模块-荷兰
+
+version: 1.0.0
+
+environment:
+  sdk: '>=3.0.2 <4.0.0'
+
+dependencies:
+
+  flutter_localizations:
+    sdk: flutter
+
+  flutter:
+    sdk: flutter
+
+  #基础组件的依赖
+  domain:
+    path: ../cs_domain
+
+  plugin_basic:
+    path: ../cs_plugin_basic
+
+  plugin_platform:
+    path: ../cs_plugin_platform
+
+  shared:
+    path: ../cs_shared
+
+  cs_resources:
+    path: ../cs_resources
+
+  router:
+    path: ../cs_router
+
+  widgets:
+    path: ../cs_widgets
+
+flutter:
+  uses-material-design: true

+ 16 - 0
packages/cpt_nl/pubspec_overrides.yaml

@@ -0,0 +1,16 @@
+# melos_managed_dependency_overrides: cs_resources,domain,plugin_basic,plugin_platform,router,shared,widgets
+dependency_overrides:
+  cs_resources:
+    path: ../cs_resources
+  domain:
+    path: ../cs_domain
+  plugin_basic:
+    path: ../cs_plugin_basic
+  plugin_platform:
+    path: ../cs_plugin_platform
+  router:
+    path: ../cs_router
+  shared:
+    path: ../cs_shared
+  widgets:
+    path: ../cs_widgets

+ 1 - 1
packages/cpt_sg/lib/modules/main/main_controller.dart

@@ -64,7 +64,7 @@ class MainController extends GetxController {
     }
 
     //获取到数据
-    var result = await _authRepository.fetchHotelInfo(version: 2);
+    var result = await _authRepository.fetchHotelInfo(country: ConfigService.to.selectCountry.value);
 
     //处理数据
     if (result.isSuccess) {

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

@@ -6,6 +6,7 @@ import 'package:cpt_uk/modules/report/report_list/report_list_page.dart';
 import 'package:domain/entity/home_module.dart';
 import 'package:domain/repository/auth_repository.dart';
 import 'package:get/get.dart';
+import 'package:plugin_basic/service/app_config_service.dart';
 import 'package:plugin_basic/service/user_service.dart';
 import 'package:plugin_platform/engine/toast/toast_engine.dart';
 import 'package:widgets/load_state_layout.dart';
@@ -54,7 +55,7 @@ class MainController extends GetxController {
     }
 
     //获取到数据
-    var result = await _authRepository.fetchHotelInfo();
+    var result = await _authRepository.fetchHotelInfo(country: ConfigService.to.selectCountry.value);
 
     //处理数据
     if (result.isSuccess) {

+ 1 - 1
packages/cpt_vn/lib/modules/main/main_controller.dart

@@ -61,7 +61,7 @@ class MainController extends GetxController {
     }
 
     //获取到数据
-    var result = await _authRepository.fetchHotelInfo();
+    var result = await _authRepository.fetchHotelInfo(country: ConfigService.to.selectCountry.value);
 
     //处理数据
     if (result.isSuccess) {

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

@@ -13,6 +13,12 @@ class ApiConstants {
   //越南的域名
   static const vnBaseUrl = isServerRelease ? 'https://vietnam.casualabour.com' : 'http://vietnam-dev.casualabour.com';
 
+  //马来的域名
+  static const msBaseUrl = isServerRelease ? 'https://malaysia.casualabour.com' : 'http://malaysia-dev.casualabour.com';
+
+  //荷兰的域名
+  static const nlBaseUrl = isServerRelease ? 'https://netherlands.casualabour.com' : 'http://netherlands-dev.casualabour.com';
+
   // =========================== 用户相关 ↓=========================================
 
   // 酒店登录
@@ -480,4 +486,25 @@ class ApiConstants {
 
   //新加坡 V2 Dashboard 的列表
   static const apiDashboardTableSG = "/index.php/api/v2/hotel/home";
+
+// ===================================  马来接口  ↓  ===================================
+
+  //登录
+  static const apiLoginMS = "/index.php/api/v1/er/login";
+
+  //账户信息
+  static const apiAccountInfoMS = "/index.php/api/v1/er/auth/info";
+
+  //账户列表
+  static const apiAccountListMS = "/index.php/api/v1/er/auth/account";
+
+  //切换账号
+  static const apiSwitchAccountMS = "/index.php/api/v1/er/auth/switch";
+
+  //签到员工列表
+  static const apiSignListMS = "/index.php/api/v1/er/sign/table";
+
+  //签到保存
+  static const apiSignSaveMS = "/index.php/api/v1/er/sing/save";
+
 }

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

@@ -0,0 +1,55 @@
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/sign_in_sign_out_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/sign_in_sign_out_entity.g.dart';
+
+@JsonSerializable()
+class SignInSignOutEntity {
+	List<SignInSignOutRows>? rows = [];
+
+	SignInSignOutEntity();
+
+	factory SignInSignOutEntity.fromJson(Map<String, dynamic> json) => $SignInSignOutEntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $SignInSignOutEntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class SignInSignOutRows {
+	@JSONField(name: "applied_id")
+	String? appliedId;
+	@JSONField(name: "staff_name")
+	String? staffName;
+	@JSONField(name: "job_date")
+	String? jobDate;
+	@JSONField(name: "start_time")
+	String? startTime;
+	@JSONField(name: "end_time")
+	String? endTime;
+	@JSONField(name: "in_time")
+	String? inTime;
+	@JSONField(name: "in_photo")
+	String? inPhoto;
+	@JSONField(name: "out_time")
+	String? outTime;
+	@JSONField(name: "out_photo")
+	String? outPhoto;
+
+	bool isExpended = false;  //是否展开了
+
+	SignInSignOutRows();
+
+	factory SignInSignOutRows.fromJson(Map<String, dynamic> json) => $SignInSignOutRowsFromJson(json);
+
+	Map<String, dynamic> toJson() => $SignInSignOutRowsToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}

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

@@ -70,6 +70,7 @@ import 'package:domain/entity/response/s_g_labour_request_table_entity.dart';
 import 'package:domain/entity/response/s_g_labour_request_worl_flow_entity.dart';
 import 'package:domain/entity/response/s_g_labour_review_option_entity.dart';
 import 'package:domain/entity/response/s_g_labour_review_table_entity.dart';
+import 'package:domain/entity/response/sign_in_sign_out_entity.dart';
 import 'package:domain/entity/response/staff_detail_entity.dart';
 import 'package:domain/entity/response/staff_detail_s_g_entity.dart';
 import 'package:domain/entity/response/staff_job_history_s_g_entity.dart';
@@ -627,6 +628,12 @@ class JsonConvert {
     if (<SGLabourReviewTableRows>[] is M) {
       return data.map<SGLabourReviewTableRows>((Map<String, dynamic> e) => SGLabourReviewTableRows.fromJson(e)).toList() as M;
     }
+    if (<SignInSignOutEntity>[] is M) {
+      return data.map<SignInSignOutEntity>((Map<String, dynamic> e) => SignInSignOutEntity.fromJson(e)).toList() as M;
+    }
+    if (<SignInSignOutRows>[] is M) {
+      return data.map<SignInSignOutRows>((Map<String, dynamic> e) => SignInSignOutRows.fromJson(e)).toList() as M;
+    }
     if (<StaffDetailEntity>[] is M) {
       return data.map<StaffDetailEntity>((Map<String, dynamic> e) => StaffDetailEntity.fromJson(e)).toList() as M;
     }
@@ -894,6 +901,8 @@ class JsonConvertClassCollection {
     (SGLabourReviewOptionEntity).toString(): SGLabourReviewOptionEntity.fromJson,
     (SGLabourReviewTableEntity).toString(): SGLabourReviewTableEntity.fromJson,
     (SGLabourReviewTableRows).toString(): SGLabourReviewTableRows.fromJson,
+    (SignInSignOutEntity).toString(): SignInSignOutEntity.fromJson,
+    (SignInSignOutRows).toString(): SignInSignOutRows.fromJson,
     (StaffDetailEntity).toString(): StaffDetailEntity.fromJson,
     (StaffDetailSGEntity).toString(): StaffDetailSGEntity.fromJson,
     (StaffDetailSGReviews).toString(): StaffDetailSGReviews.fromJson,

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

@@ -0,0 +1,114 @@
+import 'package:domain/generated/json/base/json_convert_content.dart';
+import 'package:domain/entity/response/sign_in_sign_out_entity.dart';
+
+SignInSignOutEntity $SignInSignOutEntityFromJson(Map<String, dynamic> json) {
+  final SignInSignOutEntity signInSignOutEntity = SignInSignOutEntity();
+  final List<SignInSignOutRows>? rows = (json['rows'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<SignInSignOutRows>(e) as SignInSignOutRows).toList();
+  if (rows != null) {
+    signInSignOutEntity.rows = rows;
+  }
+  return signInSignOutEntity;
+}
+
+Map<String, dynamic> $SignInSignOutEntityToJson(SignInSignOutEntity entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['rows'] = entity.rows?.map((v) => v.toJson()).toList();
+  return data;
+}
+
+extension SignInSignOutEntityExtension on SignInSignOutEntity {
+  SignInSignOutEntity copyWith({
+    List<SignInSignOutRows>? rows,
+  }) {
+    return SignInSignOutEntity()
+      ..rows = rows ?? this.rows;
+  }
+}
+
+SignInSignOutRows $SignInSignOutRowsFromJson(Map<String, dynamic> json) {
+  final SignInSignOutRows signInSignOutRows = SignInSignOutRows();
+  final String? appliedId = jsonConvert.convert<String>(json['applied_id']);
+  if (appliedId != null) {
+    signInSignOutRows.appliedId = appliedId;
+  }
+  final String? staffName = jsonConvert.convert<String>(json['staff_name']);
+  if (staffName != null) {
+    signInSignOutRows.staffName = staffName;
+  }
+  final String? jobDate = jsonConvert.convert<String>(json['job_date']);
+  if (jobDate != null) {
+    signInSignOutRows.jobDate = jobDate;
+  }
+  final String? startTime = jsonConvert.convert<String>(json['start_time']);
+  if (startTime != null) {
+    signInSignOutRows.startTime = startTime;
+  }
+  final String? endTime = jsonConvert.convert<String>(json['end_time']);
+  if (endTime != null) {
+    signInSignOutRows.endTime = endTime;
+  }
+  final String? inTime = jsonConvert.convert<String>(json['in_time']);
+  if (inTime != null) {
+    signInSignOutRows.inTime = inTime;
+  }
+  final String? inPhoto = jsonConvert.convert<String>(json['in_photo']);
+  if (inPhoto != null) {
+    signInSignOutRows.inPhoto = inPhoto;
+  }
+  final String? outTime = jsonConvert.convert<String>(json['out_time']);
+  if (outTime != null) {
+    signInSignOutRows.outTime = outTime;
+  }
+  final String? outPhoto = jsonConvert.convert<String>(json['out_photo']);
+  if (outPhoto != null) {
+    signInSignOutRows.outPhoto = outPhoto;
+  }
+  final bool? isExpended = jsonConvert.convert<bool>(json['isExpended']);
+  if (isExpended != null) {
+    signInSignOutRows.isExpended = isExpended;
+  }
+  return signInSignOutRows;
+}
+
+Map<String, dynamic> $SignInSignOutRowsToJson(SignInSignOutRows entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['applied_id'] = entity.appliedId;
+  data['staff_name'] = entity.staffName;
+  data['job_date'] = entity.jobDate;
+  data['start_time'] = entity.startTime;
+  data['end_time'] = entity.endTime;
+  data['in_time'] = entity.inTime;
+  data['in_photo'] = entity.inPhoto;
+  data['out_time'] = entity.outTime;
+  data['out_photo'] = entity.outPhoto;
+  data['isExpended'] = entity.isExpended;
+  return data;
+}
+
+extension SignInSignOutRowsExtension on SignInSignOutRows {
+  SignInSignOutRows copyWith({
+    String? appliedId,
+    String? staffName,
+    String? jobDate,
+    String? startTime,
+    String? endTime,
+    String? inTime,
+    String? inPhoto,
+    String? outTime,
+    String? outPhoto,
+    bool? isExpended,
+  }) {
+    return SignInSignOutRows()
+      ..appliedId = appliedId ?? this.appliedId
+      ..staffName = staffName ?? this.staffName
+      ..jobDate = jobDate ?? this.jobDate
+      ..startTime = startTime ?? this.startTime
+      ..endTime = endTime ?? this.endTime
+      ..inTime = inTime ?? this.inTime
+      ..inPhoto = inPhoto ?? this.inPhoto
+      ..outTime = outTime ?? this.outTime
+      ..outPhoto = outPhoto ?? this.outPhoto
+      ..isExpended = isExpended ?? this.isExpended;
+  }
+}

+ 13 - 4
packages/cs_domain/lib/repository/auth_repository.dart

@@ -16,7 +16,7 @@ class AuthRepository extends GetxService {
   Future<HttpResult<HotelInfoEntity>> userLogin(
     String? email,
     String? password, {
-    int version = 1,
+    int country = 1,
     CancelToken? cancelToken,
   }) async {
     //Post请求
@@ -26,7 +26,12 @@ class AuthRepository extends GetxService {
 
     final result = await httpProvider.requestNetResult(
       method: HttpMethod.POST,
-      version == 2 ? ApiConstants.apiAuthLoginV2 : ApiConstants.apiUserLogin,
+      country == 1
+          ? ApiConstants.apiAuthLoginV2 //新加坡
+          : country == 3
+              ? ApiConstants.apiLoginMS //马来
+              : ApiConstants.apiUserLogin,
+      //通用
       params: params,
       networkDebounce: true,
       isShowLoadingDialog: true,
@@ -93,9 +98,13 @@ class AuthRepository extends GetxService {
   }
 
   /// 获取酒店信息
-  Future<HttpResult<HotelInfoEntity>> fetchHotelInfo({int version = 1}) async {
+  Future<HttpResult<HotelInfoEntity>> fetchHotelInfo({required int country}) async {
     final result = await httpProvider.requestNetResult(
-      version == 2 ? ApiConstants.apiHotelInfoV2 : ApiConstants.apiHotelInfo,
+      country == 1
+          ? ApiConstants.apiHotelInfoV2 //新加坡
+          : country == 3
+              ? ApiConstants.apiAccountInfoMS //马来
+              : ApiConstants.apiHotelInfo,
       method: HttpMethod.GET,
       networkDebounce: true,
     );

+ 51 - 0
packages/cs_domain/lib/repository/ms_repository.dart

@@ -0,0 +1,51 @@
+import 'package:domain/entity/response/sign_in_sign_out_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';
+
+/// MS/NL等签到签出的功能单独的数据仓库
+class MSRepository extends GetxService {
+  HttpProvider httpProvider;
+
+  MSRepository({required this.httpProvider});
+
+  /// 获取当前酒店需要签到签出的考勤成员列表
+  Future<HttpResult<SignInSignOutEntity>> fetchSignInSignOutList(
+    String? keyword,
+    String? startDate,
+    String? endDate, {
+    CancelToken? cancelToken,
+  }) async {
+    Map<String, String> params = {};
+    if (!Utils.isEmpty(keyword)) {
+      params["keyword"] = keyword!;
+    }
+    if (!Utils.isEmpty(startDate)) {
+      params["start_date"] = startDate!;
+    }
+    if (!Utils.isEmpty(endDate)) {
+      params["end_date"] = endDate!;
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiSignListMS,
+      params: params,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      final json = result.getDataJson();
+      var data = SignInSignOutEntity.fromJson(json!);
+      //重新赋值data或list
+      return result.convert<SignInSignOutEntity>(data: data);
+    }
+    return result.convert();
+  }
+
+}

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

@@ -4,6 +4,7 @@ import 'package:domain/repository/job_repository.dart';
 import 'package:domain/repository/job_sg_repository.dart';
 import 'package:domain/repository/labour_repository.dart';
 import 'package:domain/repository/labour_sg_repository.dart';
+import 'package:domain/repository/ms_repository.dart';
 import 'package:domain/repository/other_repository.dart';
 import 'package:domain/repository/uk_attendance_repository.dart';
 import 'package:domain/repository/uk_job_repository.dart';
@@ -47,12 +48,15 @@ class GlobalServicesInjection {
     Get.lazyPut(() => LabourSGRepository(httpProvider: Get.find()));
     Get.lazyPut(() => OtherRepository(httpProvider: Get.find()));
     Get.lazyPut(() => SGAgencyRepository(httpProvider: Get.find()));
+
     //UK的数据仓库注入
     Get.lazyPut(() => UKJobRepository(httpProvider: Get.find()));
     Get.lazyPut(() => UKAttendanceRepository(httpProvider: Get.find()));
     Get.lazyPut(() => UKReviewRepository(httpProvider: Get.find()));
     Get.lazyPut(() => UKReportRepository(httpProvider: Get.find()));
 
+    //MS/NL的数据仓库注入
+    Get.lazyPut(() => MSRepository(httpProvider: Get.find()));
 
     // 调用额外的依赖注入逻辑(如果提供了)
     if (additionalDependencies != null) {

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

@@ -22,7 +22,7 @@ class ConfigService extends GetxService {
   static ConfigService get to => Get.find();
 
   //选择的国家,默认新加坡
-  RxInt selectCountry = 1.obs;  //0 越南  1 新加坡  2 英国
+  RxInt selectCountry = 1.obs;  //0 越南  1 新加坡  2 英国  3马来  4荷兰
 
   // 设备信息
   /// android 设备信息

+ 6 - 0
packages/cs_plugin_basic/lib/service/http_provider_injection.dart

@@ -19,6 +19,12 @@ class HttpProviderInjection {
     } else if (country == 2) {
       //英国
       baseUrl = ApiConstants.ukBaseUrl;
+    } else if (country == 3) {
+      //马来西亚
+      baseUrl = ApiConstants.msBaseUrl;
+    } else if (country == 4) {
+      //荷兰
+      baseUrl = ApiConstants.nlBaseUrl;
     } else {
       //默认越南
       baseUrl = ApiConstants.vnBaseUrl;

+ 3 - 0
packages/cs_plugin_platform/pubspec.yaml

@@ -60,6 +60,9 @@ dependencies:
   # 图片预览(单图与多图) https://github.com/xia-weiyang/image_preview
   image_preview: ^1.2.4
 
+  # 图片处理核心
+  image: ^3.2.2
+
   # SD卡管理,缓存管理
   path_provider: 2.1.4
   synchronized: ^3.1.0+1

BIN
packages/cs_resources/assets/cpt_auth/ms-icon.webp


BIN
packages/cs_resources/assets/cpt_auth/nl-icon.webp


BIN
packages/cs_resources/assets/cpt_job/sign_take_pic.webp


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

@@ -31,7 +31,9 @@ class Assets {
   static const String cptAuthKoreaIcon = 'assets/cpt_auth/korea_icon.webp';
   static const String cptAuthLoginRadioChecked = 'assets/cpt_auth/login_radio_checked.webp';
   static const String cptAuthLoginRadioUncheck = 'assets/cpt_auth/login_radio_uncheck.webp';
+  static const String cptAuthMsIcon = 'assets/cpt_auth/ms-icon.webp';
   static const String cptAuthNextIcon = 'assets/cpt_auth/next_icon.webp';
+  static const String cptAuthNlIcon = 'assets/cpt_auth/nl-icon.webp';
   static const String cptAuthPasswordHideIcon = 'assets/cpt_auth/password_hide_icon.webp';
   static const String cptAuthPasswordShowIcon = 'assets/cpt_auth/password_show_icon.webp';
   static const String cptAuthSgIcon = 'assets/cpt_auth/sg_icon.webp';
@@ -47,6 +49,7 @@ class Assets {
   static const String cptJobExportIcon = 'assets/cpt_job/export_icon.webp';
   static const String cptJobPickDateIcon = 'assets/cpt_job/pick_date_icon.png';
   static const String cptJobSearchIcon = 'assets/cpt_job/search_icon.webp';
+  static const String cptJobSignTakePic = 'assets/cpt_job/sign_take_pic.webp';
   static const String cptReportReportFinance = 'assets/cpt_report/report_finance.webp';
   static const String cptReportReportLabour = 'assets/cpt_report/report_labour.webp';
   static const String cptReportReportStaffRequest = 'assets/cpt_report/report_staff_request.webp';

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

@@ -296,6 +296,9 @@ const Map<String, String> en_US = {
   'Expired': 'Expired',
   'Certificate': 'Certificate',
   'Vehicle': 'Vehicle',
+  'Malaysia': 'Malaysia',
+  'Netherlands': 'Netherlands',
+  'Retake': 'Retake',
 
   //插件的国际化
   'Pull to refresh': 'Pull to refresh',

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

@@ -296,6 +296,9 @@ const Map<String, String> zh_CN = {
   'Expired': '已过期',
   'Certificate': '证书',
   'Vehicle': '交通工具',
+  'Malaysia': '马来西亚',
+  'Netherlands': '荷兰',
+  'Retake': '重新获取',
 
   //插件的国际化
   'Pull to refresh': '下拉刷新',

+ 7 - 0
packages/cs_router/lib/componentRouter/component_router_service.dart

@@ -3,6 +3,8 @@ import 'package:router/componentRouter/auth_service.dart';
 import 'package:router/componentRouter/uk_service.dart';
 import 'package:router/componentRouter/vn_service.dart';
 
+import 'ms_service.dart';
+import 'nl_service.dart';
 import 'sg_service.dart';
 
 /*
@@ -30,4 +32,9 @@ class ComponentRouterServices{
   //获取 VN 组件服务(越南)
   static VNService get vnService => Get.find();
 
+  //获取 MS 组件服务(马来)
+  static MSService get msService => Get.find();
+
+  //获取 NL 组件服务(荷兰)
+  static NLService get nlService => Get.find();
 }

+ 6 - 0
packages/cs_router/lib/componentRouter/ms_service.dart

@@ -0,0 +1,6 @@
+/// 马来组件对应的路由抽象接口
+abstract class MSService {
+
+  void startMSMainPage();
+
+}

+ 6 - 0
packages/cs_router/lib/componentRouter/nl_service.dart

@@ -0,0 +1,6 @@
+/// 荷兰组件对应的路由抽象接口
+abstract class NLService {
+
+  void startNLMainPage();
+
+}

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

@@ -134,6 +134,17 @@ class RouterPath {
   static const SGPositionList = '/sg/position/list';   //SG的中介的职位列表
   static const SGPositionAdd = '/sg/position/add';   //SG的中介的职位添加
 
+  // ===================================  MS马来  ↓  ===================================
+
+  static const MSMain = '/ms/main';   //马来的首页
+  static const MSJobSignInSignOut = '/ms/sign_in/sign_out';   //马来的签到签出页面
+  static const MSSignCamera = '/ms/sign/camera';   //马来的签到签出页面
+
+  // ===================================  NL荷兰  ↓  ===================================
+
+  static const NLMain = '/nl/main';   //荷兰的首页
+  static const NLJobSignInSignOut = '/nl/sign_in/sign_out';   //荷兰的签到签出页面
+
   //Runalone
   static const runAloneMain = '/runalone/main'; //独立运行的入口页面