ソースを参照

Dashboard 的列表页面,以及相关JobList的跳转

liukai 5 時間 前
コミット
ceafed30d3

+ 3 - 1
packages/cpt_sg/lib/modules/job/job_list/job_list_controller.dart

@@ -158,11 +158,13 @@ class JobListController extends GetxController with DioCancelableMixin {
           selectedEndDate: state.selectedEndDate,
           selectedStatusId: state.selectedStatusId,
           selectedOutletId: state.selectedOutletId,
-          onFilterAction: (startDate, endDate, statusId, outletId) {
+          selectedAgencyId: state.selectedAgencyId,
+          onFilterAction: (startDate, endDate, statusId, outletId,agencyId) {
             state.selectedStartDate = startDate;
             state.selectedEndDate = endDate;
             state.selectedStatusId = statusId;
             state.selectedOutletId = outletId;
+            state.selectedAgencyId = agencyId;
 
             //赋值之后刷新
             refreshController.callRefresh();

+ 98 - 28
packages/cpt_sg/lib/modules/job/job_list/job_list_filter.dart

@@ -18,16 +18,17 @@ import 'package:widgets/picker/date_picker_util.dart';
 import 'package:widgets/picker/option_pick_util.dart';
 import 'package:widgets/widget_export.dart';
 
-/**
- * 用工请求列表的筛选
+/*
+ * 工作列表筛选
  */
 class JobListFilter extends StatefulWidget {
-  void Function(DateTime? selectedStartDate, DateTime? selectedEndDate, String? selectedStatusId, String? selectedOutletId)? onFilterAction;
+  void Function(DateTime? selectedStartDate, DateTime? selectedEndDate, String? selectedStatusId, String? selectedOutletId, String? agencyId)? onFilterAction;
   JobListIndexSGEntity optionResult;
   DateTime? selectedStartDate;
   DateTime? selectedEndDate;
   String? selectedStatusId;
   String? selectedOutletId;
+  String? selectedAgencyId;
 
   JobListFilter({
     required this.optionResult,
@@ -35,6 +36,7 @@ class JobListFilter extends StatefulWidget {
     required this.selectedEndDate,
     required this.selectedStatusId,
     required this.selectedOutletId,
+    required this.selectedAgencyId,
     this.onFilterAction,
   });
 
@@ -47,6 +49,7 @@ class _JobListFilterState extends State<JobListFilter> {
   DateTime? selectedEndDate;
   String? selectedStatusId;
   String? selectedDepartmentId;
+  String? selectedAgencyId;
 
   @override
   void initState() {
@@ -55,6 +58,7 @@ class _JobListFilterState extends State<JobListFilter> {
     this.selectedEndDate = widget.selectedEndDate;
     this.selectedStatusId = widget.selectedStatusId;
     this.selectedDepartmentId = widget.selectedOutletId;
+    this.selectedAgencyId = widget.selectedAgencyId;
   }
 
   @override
@@ -67,30 +71,69 @@ class _JobListFilterState extends State<JobListFilter> {
           height: kToolbarHeight + ScreenUtil.getStatusBarH(context) + 1,
         ),
         Container(
-          padding: EdgeInsets.only(left: 15, right: 15, top: 17.5, bottom: 20),
+          padding: const EdgeInsets.only(left: 15, right: 15, top: 17.5, bottom: 20),
           width: double.infinity,
-          decoration: BoxDecoration(
+          decoration: const BoxDecoration(
             color: Colors.white,
           ),
           child: Column(
             crossAxisAlignment: CrossAxisAlignment.start,
             children: [
+              //Source选择
+              MyTextView(
+                "Source".tr,
+                fontSize: 14,
+                isFontMedium: true,
+                textColor: ColorConstants.black33,
+              ),
+
+              //选择Source
+              Container(
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
+                height: 45,
+                decoration: const BoxDecoration(
+                  color: ColorConstants.grayECECEC,
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      selectedAgencyId == null
+                          ? ""
+                          : widget.optionResult.agencyList.firstWhere((element) => element.value.toString() == selectedAgencyId).txt!,
+                      hint: "Choose Source".tr,
+                      textHintColor: ColorConstants.textBlackHint,
+                      fontSize: 14,
+                      isFontMedium: true,
+                      textColor: ColorConstants.black33,
+                    ).expanded(),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                  ],
+                ),
+              ).onTap(() {
+                pickerAgency();
+              }),
+
               //部门
               MyTextView(
                 "Outlet".tr,
                 fontSize: 14,
+                marginTop: 11,
                 isFontMedium: true,
                 textColor: ColorConstants.black33,
               ),
-
               //选择部门
               Container(
-                padding: EdgeInsets.only(left: 16, right: 10),
-                margin: EdgeInsets.only(top: 10),
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
                 height: 45,
-                decoration: BoxDecoration(
+                decoration: const BoxDecoration(
                   color: ColorConstants.grayECECEC,
-                  borderRadius: const BorderRadius.all(Radius.circular(5)),
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
                 ),
                 child: Row(
                   mainAxisSize: MainAxisSize.max,
@@ -107,7 +150,7 @@ class _JobListFilterState extends State<JobListFilter> {
                       isFontMedium: true,
                       textColor: ColorConstants.black33,
                     ).expanded(),
-                    MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
                   ],
                 ),
               ).onTap(() {
@@ -125,12 +168,12 @@ class _JobListFilterState extends State<JobListFilter> {
 
               //选择状态
               Container(
-                padding: EdgeInsets.only(left: 16, right: 10),
-                margin: EdgeInsets.only(top: 10),
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
                 height: 45,
-                decoration: BoxDecoration(
+                decoration: const BoxDecoration(
                   color: ColorConstants.grayECECEC,
-                  borderRadius: const BorderRadius.all(Radius.circular(5)),
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
                 ),
                 child: Row(
                   mainAxisSize: MainAxisSize.max,
@@ -147,7 +190,7 @@ class _JobListFilterState extends State<JobListFilter> {
                       isFontMedium: true,
                       textColor: ColorConstants.black33,
                     ).expanded(),
-                    MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
                   ],
                 ),
               ).onTap(() {
@@ -165,12 +208,12 @@ class _JobListFilterState extends State<JobListFilter> {
 
               //选择时间
               Container(
-                padding: EdgeInsets.only(left: 16, right: 10),
-                margin: EdgeInsets.only(top: 10),
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
                 height: 45,
-                decoration: BoxDecoration(
+                decoration: const BoxDecoration(
                   color: ColorConstants.grayECECEC,
-                  borderRadius: const BorderRadius.all(Radius.circular(5)),
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
                 ),
                 child: Row(
                   mainAxisSize: MainAxisSize.max,
@@ -185,7 +228,7 @@ class _JobListFilterState extends State<JobListFilter> {
                       isFontMedium: true,
                       textColor: ColorConstants.black33,
                     ).expanded(),
-                    MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
+                    const MyAssetImage(Assets.baseServiceTriangleDropDownIcon, width: 11.5, height: 6.28),
                   ],
                 ),
               ).onTap(() {
@@ -203,12 +246,12 @@ class _JobListFilterState extends State<JobListFilter> {
 
               //选择结束日期
               Container(
-                padding: EdgeInsets.only(left: 16, right: 10),
-                margin: EdgeInsets.only(top: 10),
+                padding: const EdgeInsets.only(left: 16, right: 10),
+                margin: const EdgeInsets.only(top: 10),
                 height: 45,
-                decoration: BoxDecoration(
+                decoration: const BoxDecoration(
                   color: ColorConstants.grayECECEC,
-                  borderRadius: const BorderRadius.all(Radius.circular(5)),
+                  borderRadius: BorderRadius.all(Radius.circular(5)),
                 ),
                 child: Row(
                   mainAxisSize: MainAxisSize.max,
@@ -240,12 +283,14 @@ class _JobListFilterState extends State<JobListFilter> {
                       widget.selectedEndDate = null;
                       widget.selectedStatusId = null;
                       widget.selectedOutletId = null;
+                      widget.selectedAgencyId = null;
 
                       setState(() {
                         selectedStartDate = null;
                         selectedEndDate = null;
                         selectedStatusId = null;
                         selectedDepartmentId = null;
+                        selectedAgencyId = null;
                       });
                     },
                     text: "Reset".tr,
@@ -255,11 +300,11 @@ class _JobListFilterState extends State<JobListFilter> {
                     minWidth: 60,
                     minHeight: 36,
                   ).expanded(),
-                  SizedBox(width: 15),
+                  const SizedBox(width: 15),
                   MyButton(
                     onPressed: () {
                       onCancel();
-                      widget.onFilterAction?.call(selectedStartDate, selectedEndDate, selectedStatusId, selectedDepartmentId);
+                      widget.onFilterAction?.call(selectedStartDate, selectedEndDate, selectedStatusId, selectedDepartmentId,selectedAgencyId);
                     },
                     text: "Filter".tr,
                     textColor: ColorConstants.white,
@@ -273,7 +318,7 @@ class _JobListFilterState extends State<JobListFilter> {
             ],
           ),
         ),
-        Center(child: MyAssetImage(Assets.baseServiceDialogDeleteIcon, width: 26.5, height: 26.5).marginOnly(top: 35)).onTap(() {
+        Center(child: const MyAssetImage(Assets.baseServiceDialogDeleteIcon, width: 26.5, height: 26.5).marginOnly(top: 35)).onTap(() {
           onCancel();
         }),
       ],
@@ -311,6 +356,30 @@ class _JobListFilterState extends State<JobListFilter> {
     );
   }
 
+  /// 赛选 Source
+  void pickerAgency() {
+    int selectedIndex;
+    if (selectedAgencyId == null) {
+      selectedIndex = 0;
+    } else {
+      selectedIndex = widget.optionResult.agencyList.indexWhere((department) => department.value.toString() == selectedAgencyId);
+    }
+
+    if (selectedIndex < 0) {
+      selectedIndex = 0;
+    }
+
+    OptionPickerUtil.showCupertinoOptionPicker(
+      items: widget.optionResult.agencyList.map((e) => e.txt!).toList(growable: false),
+      initialSelectIndex: selectedIndex,
+      onPickerChanged: (_, index) {
+        setState(() {
+          selectedAgencyId = widget.optionResult.agencyList[index].value!.toString();
+        });
+      },
+    );
+  }
+
   /// 筛选部门
   void pickerOutlet() {
     int selectedDepartmentIndex;
@@ -358,4 +427,5 @@ class _JobListFilterState extends State<JobListFilter> {
       },
     );
   }
+
 }

+ 11 - 2
packages/cpt_sg/lib/modules/job/job_list/job_list_page.dart

@@ -27,8 +27,8 @@ class JobListPage extends BaseStatefulPage<JobListController> {
   JobListPage({Key? key}) : super(key: key);
 
   //启动当前页面
-  static void startInstance({String? date}) {
-    return Get.start(RouterPath.jobListSG, arguments: {'date': date ?? ""});
+  static void startInstance({String? date, String? agencyId,String? outletId}) {
+    return Get.start(RouterPath.jobListSG, arguments: {'date': date ?? "", 'agencyId': agencyId ?? "", 'outletId': outletId ?? ""});
   }
 
   @override
@@ -50,11 +50,20 @@ class _JobListState extends BaseState<JobListPage, JobListController> {
 
     //如果有传递的Date,那就直接显示
     String date = Get.arguments['date'];
+    String agencyId = Get.arguments['agencyId'];
+    String outletId = Get.arguments['outletId'];
     if (Utils.isNotEmpty(date)) {
       DateTime? selectDate = DateTimeUtils.getDateTime(date);
       state.selectedStartDate = selectDate;
       state.selectedEndDate = selectDate;
     }
+    if (Utils.isNotEmpty(agencyId)) {
+      state.selectedAgencyId = agencyId;
+    }
+    if (Utils.isNotEmpty(outletId)) {
+      state.selectedOutletId = outletId;
+    }
+
   }
 
   @override

+ 1 - 0
packages/cpt_sg/lib/modules/job/job_list/job_list_state.dart

@@ -8,6 +8,7 @@ class JobListState {
   DateTime? selectedEndDate;
   String? selectedStatusId;
   String? selectedOutletId;
+  String? selectedAgencyId;
 
   //页面的列表数据
   List<JobListSGRows> datas = [];

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

@@ -3,6 +3,7 @@ import 'package:cpt_sg/modules/job/job_list/job_list_page.dart';
 import 'package:cpt_sg/modules/labour/job_template_list/job_template_list_page.dart';
 import 'package:cpt_sg/modules/labour/job_title_list/job_title_list_page.dart';
 import 'package:cpt_sg/modules/labour/labour_request_list/labour_request_list_page.dart';
+import 'package:cpt_sg/modules/report/dashboard/dashboard_page.dart';
 import 'package:cpt_sg/modules/report/device_list/device_list_page.dart';
 import 'package:cpt_sg/modules/report/report_list/report_list_page.dart';
 import 'package:cpt_sg/modules/review/attendance_review_list/attendance_review_page.dart';
@@ -128,7 +129,7 @@ class MainController extends GetxController {
   void gotoModulePage(HomeModule module) {
     switch (module.key) {
       case 'dash':
-        ToastEngine.show("进入 dashboard");
+        SGDashboardPage.startInstance();
         break;
       case 'agency':
         SGAgencyCategoryPage.startInstance();

+ 95 - 0
packages/cpt_sg/lib/modules/report/dashboard/dashboard_controller.dart

@@ -0,0 +1,95 @@
+import 'package:domain/entity/response/device_list_entity.dart';
+import 'package:domain/entity/response/s_g_dashboard_entity.dart';
+import 'package:domain/repository/job_sg_repository.dart';
+import 'package:get/get.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'dashboard_state.dart';
+
+class DashboardController extends GetxController with DioCancelableMixin {
+  final JobSGRepository _jobRepository = Get.find();
+  final DashboardState state = DashboardState();
+
+  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 {
+    fetchList();
+  }
+
+  // 重试请求
+  Future retryRequest() async {
+    _needShowPlaceholder = true;
+    fetchList();
+  }
+
+  /// 获取服务器数据,通知消息列表
+  Future fetchList() async {
+    if (_needShowPlaceholder) {
+      changeLoadingState(LoadState.State_Loading);
+    }
+
+    // 获取 Applied 列表
+    var listResult = await _jobRepository.fetchDashboardList(
+      cancelToken: cancelToken,
+    );
+
+    // 处理数据
+    if (listResult.isSuccess) {
+      handleList(listResult.data?.agencyList);
+    } else {
+      errorMessage = listResult.errorMsg;
+      changeLoadingState(LoadState.State_Error);
+    }
+
+    // 最后赋值
+    _needShowPlaceholder = false;
+  }
+
+  // 处理数据与展示的逻辑
+  void handleList(List<SGDashboardAgencyList>? list) {
+    if (list != null && list.isNotEmpty) {
+      //有数据
+      state.datas.clear();
+      state.datas.addAll(list);
+      refreshController.finishRefresh();
+      //更新展示的状态
+      changeLoadingState(LoadState.State_Success);
+    } else {
+      //展示无数据的布局
+      state.datas.clear();
+      changeLoadingState(LoadState.State_Empty);
+      refreshController.finishRefresh();
+    }
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    fetchList();
+  }
+
+  @override
+  void onClose() {
+    state.datas.clear();
+    super.onClose();
+  }
+}

+ 111 - 0
packages/cpt_sg/lib/modules/report/dashboard/dashboard_item.dart

@@ -0,0 +1,111 @@
+import 'package:cpt_sg/modules/agency/agency_add/agency_add_page.dart';
+import 'package:cs_resources/constants/color_constants.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:domain/entity/response/s_g_dashboard_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:plugin_basic/basic_export.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+/*
+ * 中介的各种统计数据
+ */
+class DashboardItem extends StatelessWidget {
+  final int index;
+  final SGDashboardAgencyList item;
+  void Function(String? agencyId, String? outletId, String? day)? OnItemClickAction;
+
+  DashboardItem({
+    required this.index,
+    required this.item,
+    this.OnItemClickAction,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      margin: const EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 5),
+      child: Column(
+        mainAxisSize: MainAxisSize.max,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          MyTextView(
+            "${item.agencyName} (${item.outletName})",
+            isFontMedium: true,
+            marginBottom: 9,
+            textColor: Colors.white,
+            fontSize: 14,
+          ),
+
+          // 星期中每一天的数据
+          GridView.builder(
+            padding: EdgeInsets.zero,
+            physics: const NeverScrollableScrollPhysics(),
+            // 禁用 GridView 的滚动
+            shrinkWrap: true,
+            // 适应内容高度
+            gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
+              crossAxisCount: 2, // 每行2个元素
+              crossAxisSpacing: 9,
+              mainAxisSpacing: 9,
+              childAspectRatio: 168 / 50, // 控制子项的宽高比
+            ),
+            itemCount: item.days.length,
+            // 每个 SliverList 项中的网格项数量
+            itemBuilder: (BuildContext context, int gridIndex) {
+              return Container(
+                decoration: BoxDecoration(
+                  color: const Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
+                  borderRadius: BorderRadius.circular(5), // 设置圆角
+                ),
+                child: Row(
+                  mainAxisSize: MainAxisSize.max,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: [
+                    MyAssetImage(
+                            item.days[gridIndex].week == "Mon"
+                                ? Assets.cptReportWeekNum1
+                                : item.days[gridIndex].week == "Tue"
+                                    ? Assets.cptReportWeekNum2
+                                    : item.days[gridIndex].week == "Wed"
+                                        ? Assets.cptReportWeekNum3
+                                        : item.days[gridIndex].week == "Thu"
+                                            ? Assets.cptReportWeekNum4
+                                            : item.days[gridIndex].week == "Fir"
+                                                ? Assets.cptReportWeekNum5
+                                                : item.days[gridIndex].week == "Sat"
+                                                    ? Assets.cptReportWeekNum6
+                                                    : Assets.cptReportWeekNum7,
+                            width: 25,
+                            height: 25)
+                        .marginOnly(left: 10),
+                    MyTextView(
+                      item.days[gridIndex].day ?? "-",
+                      fontSize: 12.5,
+                      marginLeft: 10,
+                      textColor: ColorConstants.textGrayAECAE5,
+                      isFontRegular: true,
+                    ).expanded(),
+                    MyTextView(
+                      item.days[gridIndex].num ?? "-",
+                      fontSize: 13.5,
+                      marginRight: 15,
+                      textColor: ColorConstants.textYellowF8AE00,
+                      isFontBold: true,
+                    ),
+                  ],
+                ).onTap(() {
+                  OnItemClickAction?.call(item.agencyId, item.outletId, item.days[gridIndex].day);
+                }),
+              );
+            },
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 104 - 0
packages/cpt_sg/lib/modules/report/dashboard/dashboard_page.dart

@@ -0,0 +1,104 @@
+import 'package:cpt_sg/modules/job/job_list/job_list_page.dart';
+import 'package:cpt_sg/modules/report/dashboard/dashboard_item.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/load_state_layout.dart';
+import 'package:widgets/widget_export.dart';
+
+import 'dashboard_controller.dart';
+
+import 'package:plugin_basic/base/base_state.dart';
+import 'package:plugin_basic/base/base_stateful_page.dart';
+import 'package:plugin_basic/utils/ext_get_nav.dart';
+import 'package:router/path/router_path.dart';
+import 'package:shared/utils/screen_util.dart';
+import 'package:widgets/my_appbar.dart';
+
+import 'dashboard_state.dart';
+
+class SGDashboardPage extends BaseStatefulPage<DashboardController> {
+  SGDashboardPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance() {
+    return Get.start(RouterPath.dashboardSG);
+  }
+
+  @override
+  DashboardController createRawController() {
+    return DashboardController();
+  }
+
+  @override
+  State<SGDashboardPage> createState() => _DeviceListState();
+}
+
+class _DeviceListState extends BaseState<SGDashboardPage, DashboardController> {
+  late DashboardState state;
+
+  @override
+  void initState() {
+    super.initState();
+    state = controller.state;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return autoCtlGetBuilder(builder: (controller) {
+      return SafeArea(
+        bottom: true,
+        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, "Dashboard".tr),
+
+              //底部的列表
+              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 DashboardItem(
+                          index: index,
+                          item: state.datas[index],
+                          OnItemClickAction: (String? agencyId, String? outletId, String? day) {
+                            JobListPage.startInstance(date: day, outletId: outletId, agencyId: agencyId);
+                          },
+                        );
+                      },
+                      childCount: state.datas.length,
+                    ))
+                  ],
+                ),
+              ).marginOnly(top: 5).expanded(),
+            ],
+          ),
+        ),
+      );
+    });
+  }
+}

+ 7 - 0
packages/cpt_sg/lib/modules/report/dashboard/dashboard_state.dart

@@ -0,0 +1,7 @@
+import 'package:domain/entity/response/s_g_dashboard_entity.dart';
+
+class DashboardState {
+
+  List<SGDashboardAgencyList> datas = []; //列表数据
+
+}

+ 8 - 0
packages/cpt_sg/lib/router/sg_router.dart

@@ -9,6 +9,7 @@ import 'package:cpt_sg/modules/agency/contract_rate_specific_day/contract_rate_s
 import 'package:cpt_sg/modules/agency/position_add/position_add_page.dart';
 import 'package:cpt_sg/modules/agency/position_list/position_list_page.dart';
 import 'package:cpt_sg/modules/main/main_page.dart';
+import 'package:cpt_sg/modules/report/dashboard/dashboard_page.dart';
 import 'package:cpt_sg/modules/review/attendance_review_workflow/attendance_review_workflow_page.dart';
 import 'package:get/get.dart';
 import 'package:router/path/router_path.dart';
@@ -262,5 +263,12 @@ class SGPageRouter {
       name: RouterPath.SGPositionAdd,
       page: () => SGPositionAddPage(),
     ),
+
+    //新加坡的 Dashboard 列表
+    GetPage(
+      name: RouterPath.dashboardSG,
+      page: () => SGDashboardPage(),
+    ),
+
   ];
 }

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

@@ -445,4 +445,7 @@ class ApiConstants {
 
   //新加坡 V2 Attendance Review 状态流
   static const apiAttendanceReviewWorkflowSG = "/index.php/api/v2/hotel/att-review/status-view";
+
+  //新加坡 V2 Dashboard 的列表
+  static const apiDashboardTableSG = "/index.php/api/v2/hotel/home";
 }

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

@@ -13,6 +13,8 @@ class JobListIndexSGEntity {
   List<JobListIndexSGOption> outletList = [];
   @JSONField(name: "status_list")
   List<JobListIndexSGOption> statusList = [];
+  @JSONField(name: "agency_list")
+  List<JobListIndexSGOption> agencyList = [];
 
   JobListIndexSGEntity();
 

+ 64 - 0
packages/cs_domain/lib/entity/response/s_g_dashboard_entity.dart

@@ -0,0 +1,64 @@
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/s_g_dashboard_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/s_g_dashboard_entity.g.dart';
+
+@JsonSerializable()
+class SGDashboardEntity {
+	String? day;
+	@JSONField(name: "agency_list")
+	List<SGDashboardAgencyList> agencyList = [];
+
+	SGDashboardEntity();
+
+	factory SGDashboardEntity.fromJson(Map<String, dynamic> json) => $SGDashboardEntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $SGDashboardEntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class SGDashboardAgencyList {
+	@JSONField(name: "agency_id")
+	String? agencyId;
+	@JSONField(name: "agency_name")
+	String? agencyName;
+	@JSONField(name: "outlet_id")
+	String? outletId;
+	@JSONField(name: "outlet_name")
+	String? outletName;
+	List<SGDashboardAgencyListDays> days = [];
+
+	SGDashboardAgencyList();
+
+	factory SGDashboardAgencyList.fromJson(Map<String, dynamic> json) => $SGDashboardAgencyListFromJson(json);
+
+	Map<String, dynamic> toJson() => $SGDashboardAgencyListToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+@JsonSerializable()
+class SGDashboardAgencyListDays {
+	String? day;
+	String? week;
+	String? num;
+
+	SGDashboardAgencyListDays();
+
+	factory SGDashboardAgencyListDays.fromJson(Map<String, dynamic> json) => $SGDashboardAgencyListDaysFromJson(json);
+
+	Map<String, dynamic> toJson() => $SGDashboardAgencyListDaysToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}

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

@@ -62,6 +62,7 @@ import 'package:domain/entity/response/revise_log_s_g_entity.dart';
 import 'package:domain/entity/response/s_g_attendance_review_option_entity.dart';
 import 'package:domain/entity/response/s_g_attendance_review_table_entity.dart';
 import 'package:domain/entity/response/s_g_attendance_review_workflow_entity.dart';
+import 'package:domain/entity/response/s_g_dashboard_entity.dart';
 import 'package:domain/entity/response/s_g_labour_request_add_option_entity.dart';
 import 'package:domain/entity/response/s_g_labour_request_detail_entity.dart';
 import 'package:domain/entity/response/s_g_labour_request_option_entity.dart';
@@ -570,6 +571,15 @@ class JsonConvert {
     if (<SGAttendanceReviewWorkflowRecords>[] is M) {
       return data.map<SGAttendanceReviewWorkflowRecords>((Map<String, dynamic> e) => SGAttendanceReviewWorkflowRecords.fromJson(e)).toList() as M;
     }
+    if (<SGDashboardEntity>[] is M) {
+      return data.map<SGDashboardEntity>((Map<String, dynamic> e) => SGDashboardEntity.fromJson(e)).toList() as M;
+    }
+    if (<SGDashboardAgencyList>[] is M) {
+      return data.map<SGDashboardAgencyList>((Map<String, dynamic> e) => SGDashboardAgencyList.fromJson(e)).toList() as M;
+    }
+    if (<SGDashboardAgencyListDays>[] is M) {
+      return data.map<SGDashboardAgencyListDays>((Map<String, dynamic> e) => SGDashboardAgencyListDays.fromJson(e)).toList() as M;
+    }
     if (<SGLabourRequestAddOptionEntity>[] is M) {
       return data.map<SGLabourRequestAddOptionEntity>((Map<String, dynamic> e) => SGLabourRequestAddOptionEntity.fromJson(e)).toList() as M;
     }
@@ -806,6 +816,9 @@ class JsonConvertClassCollection {
     (SGAttendanceReviewWorkflowEntity).toString(): SGAttendanceReviewWorkflowEntity.fromJson,
     (SGAttendanceReviewWorkflowRow).toString(): SGAttendanceReviewWorkflowRow.fromJson,
     (SGAttendanceReviewWorkflowRecords).toString(): SGAttendanceReviewWorkflowRecords.fromJson,
+    (SGDashboardEntity).toString(): SGDashboardEntity.fromJson,
+    (SGDashboardAgencyList).toString(): SGDashboardAgencyList.fromJson,
+    (SGDashboardAgencyListDays).toString(): SGDashboardAgencyListDays.fromJson,
     (SGLabourRequestAddOptionEntity).toString(): SGLabourRequestAddOptionEntity.fromJson,
     (LabourRequestAgencyEntity).toString(): LabourRequestAgencyEntity.fromJson,
     (SGLabourRequestDetailEntity).toString(): SGLabourRequestDetailEntity.fromJson,

+ 9 - 1
packages/cs_domain/lib/generated/json/job_list_index_s_g_entity.g.dart

@@ -21,6 +21,11 @@ JobListIndexSGEntity $JobListIndexSGEntityFromJson(Map<String, dynamic> json) {
   if (statusList != null) {
     jobListIndexSGEntity.statusList = statusList;
   }
+  final List<JobListIndexSGOption>? agencyList = (json['agency_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<JobListIndexSGOption>(e) as JobListIndexSGOption).toList();
+  if (agencyList != null) {
+    jobListIndexSGEntity.agencyList = agencyList;
+  }
   return jobListIndexSGEntity;
 }
 
@@ -30,6 +35,7 @@ Map<String, dynamic> $JobListIndexSGEntityToJson(JobListIndexSGEntity entity) {
   data['end_date'] = entity.endDate;
   data['outlet_list'] = entity.outletList.map((v) => v.toJson()).toList();
   data['status_list'] = entity.statusList.map((v) => v.toJson()).toList();
+  data['agency_list'] = entity.agencyList.map((v) => v.toJson()).toList();
   return data;
 }
 
@@ -39,12 +45,14 @@ extension JobListIndexSGEntityExtension on JobListIndexSGEntity {
     String? endDate,
     List<JobListIndexSGOption>? outletList,
     List<JobListIndexSGOption>? statusList,
+    List<JobListIndexSGOption>? agencyList,
   }) {
     return JobListIndexSGEntity()
       ..startDate = startDate ?? this.startDate
       ..endDate = endDate ?? this.endDate
       ..outletList = outletList ?? this.outletList
-      ..statusList = statusList ?? this.statusList;
+      ..statusList = statusList ?? this.statusList
+      ..agencyList = agencyList ?? this.agencyList;
   }
 }
 

+ 125 - 0
packages/cs_domain/lib/generated/json/s_g_dashboard_entity.g.dart

@@ -0,0 +1,125 @@
+import 'package:domain/generated/json/base/json_convert_content.dart';
+import 'package:domain/entity/response/s_g_dashboard_entity.dart';
+
+SGDashboardEntity $SGDashboardEntityFromJson(Map<String, dynamic> json) {
+  final SGDashboardEntity sGDashboardEntity = SGDashboardEntity();
+  final String? day = jsonConvert.convert<String>(json['day']);
+  if (day != null) {
+    sGDashboardEntity.day = day;
+  }
+  final List<SGDashboardAgencyList>? agencyList = (json['agency_list'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<SGDashboardAgencyList>(e) as SGDashboardAgencyList).toList();
+  if (agencyList != null) {
+    sGDashboardEntity.agencyList = agencyList;
+  }
+  return sGDashboardEntity;
+}
+
+Map<String, dynamic> $SGDashboardEntityToJson(SGDashboardEntity entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['day'] = entity.day;
+  data['agency_list'] = entity.agencyList.map((v) => v.toJson()).toList();
+  return data;
+}
+
+extension SGDashboardEntityExtension on SGDashboardEntity {
+  SGDashboardEntity copyWith({
+    String? day,
+    List<SGDashboardAgencyList>? agencyList,
+  }) {
+    return SGDashboardEntity()
+      ..day = day ?? this.day
+      ..agencyList = agencyList ?? this.agencyList;
+  }
+}
+
+SGDashboardAgencyList $SGDashboardAgencyListFromJson(Map<String, dynamic> json) {
+  final SGDashboardAgencyList sGDashboardAgencyList = SGDashboardAgencyList();
+  final String? agencyId = jsonConvert.convert<String>(json['agency_id']);
+  if (agencyId != null) {
+    sGDashboardAgencyList.agencyId = agencyId;
+  }
+  final String? agencyName = jsonConvert.convert<String>(json['agency_name']);
+  if (agencyName != null) {
+    sGDashboardAgencyList.agencyName = agencyName;
+  }
+  final String? outletId = jsonConvert.convert<String>(json['outlet_id']);
+  if (outletId != null) {
+    sGDashboardAgencyList.outletId = outletId;
+  }
+  final String? outletName = jsonConvert.convert<String>(json['outlet_name']);
+  if (outletName != null) {
+    sGDashboardAgencyList.outletName = outletName;
+  }
+  final List<SGDashboardAgencyListDays>? days = (json['days'] as List<dynamic>?)?.map(
+          (e) => jsonConvert.convert<SGDashboardAgencyListDays>(e) as SGDashboardAgencyListDays).toList();
+  if (days != null) {
+    sGDashboardAgencyList.days = days;
+  }
+  return sGDashboardAgencyList;
+}
+
+Map<String, dynamic> $SGDashboardAgencyListToJson(SGDashboardAgencyList entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['agency_id'] = entity.agencyId;
+  data['agency_name'] = entity.agencyName;
+  data['outlet_id'] = entity.outletId;
+  data['outlet_name'] = entity.outletName;
+  data['days'] = entity.days.map((v) => v.toJson()).toList();
+  return data;
+}
+
+extension SGDashboardAgencyListExtension on SGDashboardAgencyList {
+  SGDashboardAgencyList copyWith({
+    String? agencyId,
+    String? agencyName,
+    String? outletId,
+    String? outletName,
+    List<SGDashboardAgencyListDays>? days,
+  }) {
+    return SGDashboardAgencyList()
+      ..agencyId = agencyId ?? this.agencyId
+      ..agencyName = agencyName ?? this.agencyName
+      ..outletId = outletId ?? this.outletId
+      ..outletName = outletName ?? this.outletName
+      ..days = days ?? this.days;
+  }
+}
+
+SGDashboardAgencyListDays $SGDashboardAgencyListDaysFromJson(Map<String, dynamic> json) {
+  final SGDashboardAgencyListDays sGDashboardAgencyListDays = SGDashboardAgencyListDays();
+  final String? day = jsonConvert.convert<String>(json['day']);
+  if (day != null) {
+    sGDashboardAgencyListDays.day = day;
+  }
+  final String? week = jsonConvert.convert<String>(json['week']);
+  if (week != null) {
+    sGDashboardAgencyListDays.week = week;
+  }
+  final String? num = jsonConvert.convert<String>(json['num']);
+  if (num != null) {
+    sGDashboardAgencyListDays.num = num;
+  }
+  return sGDashboardAgencyListDays;
+}
+
+Map<String, dynamic> $SGDashboardAgencyListDaysToJson(SGDashboardAgencyListDays entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['day'] = entity.day;
+  data['week'] = entity.week;
+  data['num'] = entity.num;
+  return data;
+}
+
+extension SGDashboardAgencyListDaysExtension on SGDashboardAgencyListDays {
+  SGDashboardAgencyListDays copyWith({
+    String? day,
+    String? week,
+    String? num,
+  }) {
+    return SGDashboardAgencyListDays()
+      ..day = day ?? this.day
+      ..week = week ?? this.week
+      ..num = num ?? this.num;
+  }
+}

+ 32 - 3
packages/cs_domain/lib/repository/job_sg_repository.dart

@@ -6,6 +6,7 @@ import 'package:domain/entity/response/revise_log_s_g_entity.dart';
 import 'package:domain/entity/response/s_g_attendance_review_option_entity.dart';
 import 'package:domain/entity/response/s_g_attendance_review_table_entity.dart';
 import 'package:domain/entity/response/s_g_attendance_review_workflow_entity.dart';
+import 'package:domain/entity/response/s_g_dashboard_entity.dart';
 import 'package:domain/entity/response/staff_detail_s_g_entity.dart';
 import 'package:domain/entity/response/staff_review_history_s_g_entity.dart';
 import 'package:get/get.dart';
@@ -960,9 +961,9 @@ class JobSGRepository extends GetxService {
 
   /// 考勤审核的状态流
   Future<HttpResult<SGAttendanceReviewWorkflowEntity>> fetchAttendanceReviewWorkFlow(
-      String? appliedId, {
-        CancelToken? cancelToken,
-      }) async {
+    String? appliedId, {
+    CancelToken? cancelToken,
+  }) async {
     //参数
     Map<String, String> params = {};
     params['applied_id'] = appliedId ?? "";
@@ -985,4 +986,32 @@ class JobSGRepository extends GetxService {
     return result.convert();
   }
 
+  /// 获取 SG Dashboard 的列表数据
+  Future<HttpResult<SGDashboardEntity>> fetchDashboardList({
+    String? day,
+    CancelToken? cancelToken,
+  }) async {
+    //参数
+    Map<String, String> params = {};
+    if (Utils.isNotEmpty(day)) {
+      params['day'] = day ?? "";
+    }
+
+    final result = await httpProvider.requestNetResult(
+      ApiConstants.apiDashboardTableSG,
+      params: params,
+      isShowLoadingDialog: true,
+      cancelToken: cancelToken,
+    );
+
+    //根据返回的结果,封装原始数据为Bean/Entity对象
+    if (result.isSuccess) {
+      //重新赋值data或list
+      final json = result.getDataJson();
+      var data = SGDashboardEntity.fromJson(json!);
+      //重新赋值data或list
+      return result.convert<SGDashboardEntity>(data: data);
+    }
+    return result.convert();
+  }
 }

+ 14 - 14
packages/cs_domain/lib/repository/labour_sg_repository.dart

@@ -830,9 +830,9 @@ class LabourSGRepository extends GetxService {
 
   /// 用工请求的工作流列表
   Future<HttpResult<SGLabourRequestWorlFlowEntity>> fetchLabourRequestWorkFlow(
-      String? requestId, {
-        CancelToken? cancelToken,
-      }) async {
+    String? requestId, {
+    CancelToken? cancelToken,
+  }) async {
     //参数
     Map<String, String> params = {};
     params['request_id'] = requestId ?? "";
@@ -884,6 +884,7 @@ class LabourSGRepository extends GetxService {
     String? status,
     String? outletId, {
     required int curPage,
+    String? agencyId,
     CancelToken? cancelToken,
   }) async {
     Map<String, String> params = {};
@@ -891,18 +892,18 @@ class LabourSGRepository extends GetxService {
     if (Utils.isNotEmpty(startDate)) {
       params['start_date'] = startDate ?? "";
     }
-
     if (Utils.isNotEmpty(endDate)) {
       params['end_date'] = endDate ?? "";
     }
-
     if (Utils.isNotEmpty(status)) {
       params['status'] = status ?? "";
     }
-
     if (Utils.isNotEmpty(outletId)) {
       params['outlet_id'] = outletId ?? "";
     }
+    if (Utils.isNotEmpty(agencyId)) {
+      params['agency_id'] = agencyId ?? "";
+    }
 
     params['cur_page'] = curPage.toString();
     params['page_size'] = "10";
@@ -1200,9 +1201,9 @@ class LabourSGRepository extends GetxService {
 
   /// 用工审核的批量同意
   Future<HttpResult> approveLabourReviews(
-      String? recordIds, {
-        CancelToken? cancelToken,
-      }) async {
+    String? recordIds, {
+    CancelToken? cancelToken,
+  }) async {
     //参数
     Map<String, String> params = {};
     params['record_ids'] = recordIds ?? "";
@@ -1226,10 +1227,10 @@ class LabourSGRepository extends GetxService {
 
   /// 用工审核的批量拒绝
   Future<HttpResult> rejectLabourReviews(
-      String? recordIds,
-      String? reason, {
-        CancelToken? cancelToken,
-      }) async {
+    String? recordIds,
+    String? reason, {
+    CancelToken? cancelToken,
+  }) async {
     //参数
     Map<String, String> params = {};
     params['record_ids'] = recordIds ?? "";
@@ -1312,5 +1313,4 @@ class LabourSGRepository extends GetxService {
     }
     return result.convert();
   }
-
 }

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

@@ -106,6 +106,7 @@ class RouterPath {
   static const reviseAppliedSG = '/revise/applied'; //Applied的Revise(新加坡)
   static const reviseLogSG = '/revise/log'; //Revise 的日志(新加坡)
   static const reviseEditSG = '/revise/edit'; //Revise 的添加,编辑,详情(新加坡)
+  static const dashboardSG = '/dashboard/list'; //Dashboard的
 
   //新加坡报表
   static const deviceListSG = '/device/list'; //雇主绑定的设备列表