liukai 1 неделя назад
Родитель
Сommit
76e06b2c27

+ 66 - 0
packages/cpt_facility/lib/modules/booking/facility_booking_page.dart

@@ -0,0 +1,66 @@
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../router/page/facility_page_router.dart';
+import 'facility_booking_view_model.dart';
+
+@RoutePage()
+class FacilityBookingPage extends HookConsumerWidget {
+  const FacilityBookingPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(const FacilityBookingPageRoute());
+    } else {
+      appRouter.push(const FacilityBookingPageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.watch(facilityBookingViewModelProvider.notifier);
+    final state = ref.watch(facilityBookingViewModelProvider);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, "Kids party room", backgroundColor: context.appColors.whiteBG),
+      backgroundColor: context.appColors.backgroundDark,
+      body: SingleChildScrollView(
+        scrollDirection: Axis.vertical,
+        physics: const BouncingScrollPhysics(),
+        child: Column(
+          mainAxisSize: MainAxisSize.max,
+          crossAxisAlignment: CrossAxisAlignment.center,
+          children: [
+            MyTextView(
+              "Friday,11 October 2024",
+              textColor: context.appColors.textBlack,
+              fontSize: 17,
+              marginTop: 18,
+              marginBottom: 16,
+              marginLeft: 15,
+              isFontMedium: true,
+            ),
+            WeeklyCalendar(
+              isAutoSelect: false,
+              selectedDate: DateTime.now().add(Duration(days: 1)),
+              onChangedSelectedDate: (dateTime) {
+                Log.d("onChangedSelectedDate选中 - ${dateTime}}");
+              },
+              onChangedPage: (dateTime, state) {
+                Log.d("onChangedPage - ${dateTime} state:${state}");
+              },
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 3 - 0
packages/cpt_facility/lib/modules/booking/facility_booking_state.dart

@@ -0,0 +1,3 @@
+class FacilityBookingState{
+
+}

+ 13 - 0
packages/cpt_facility/lib/modules/booking/facility_booking_view_model.dart

@@ -0,0 +1,13 @@
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+import 'facility_booking_state.dart';
+
+part 'facility_booking_view_model.g.dart';
+
+@riverpod
+class FacilityBookingViewModel extends _$FacilityBookingViewModel{
+  @override
+  FacilityBookingState build() {
+    return FacilityBookingState();
+  }
+}

+ 27 - 0
packages/cpt_facility/lib/modules/booking/facility_booking_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'facility_booking_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$facilityBookingViewModelHash() =>
+    r'd90fc41489be510e703686650b6027f6f6cdf77d';
+
+/// See also [FacilityBookingViewModel].
+@ProviderFor(FacilityBookingViewModel)
+final facilityBookingViewModelProvider = AutoDisposeNotifierProvider<
+    FacilityBookingViewModel, FacilityBookingState>.internal(
+  FacilityBookingViewModel.new,
+  name: r'facilityBookingViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$facilityBookingViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$FacilityBookingViewModel = AutoDisposeNotifier<FacilityBookingState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 8 - 7
packages/cpt_facility/lib/modules/facility/book/facility_book_screen.dart

@@ -6,11 +6,10 @@ import 'package:shared/utils/log_utils.dart';
 import 'package:widgets/ext/ex_widget.dart';
 import 'package:widgets/load_state_layout.dart';
 import 'package:widgets/widget_export.dart';
-
+import '../../booking/facility_booking_page.dart';
 import 'facility_book_view_model.dart';
 import 'item_facility_book.dart';
 
-
 @RoutePage()
 class FacilityBookScreen extends HookConsumerWidget {
   @override
@@ -42,11 +41,13 @@ class FacilityBookScreen extends HookConsumerWidget {
           successSliverWidget: [
             SliverList(
                 delegate: SliverChildBuilderDelegate(
-                      (context, index) {
-                    return FacilityBookItem(index: index, item: state.datas[index]);
-                  },
-                  childCount: state.datas.length,
-                ))
+              (context, index) {
+                return FacilityBookItem(index: index, item: state.datas[index]).onTap(() {
+                  FacilityBookingPage.startInstance(context: context);
+                });
+              },
+              childCount: state.datas.length,
+            ))
           ],
         ),
       ).marginOnly(top: 5, bottom: 5),

+ 3 - 1
packages/cpt_facility/lib/router/page/facility_page_router.dart

@@ -9,6 +9,7 @@ import '../../modules/facility/book/facility_book_screen.dart';
 import '../../modules/facility/deposit/facility_deposit_screen.dart';
 import '../../modules/facility/history/facility_history_screen.dart';
 import '../../modules/detail/facility_detail_page.dart';
+import '../../modules/booking/facility_booking_page.dart';
 
 part 'facility_page_router.gr.dart';
 
@@ -30,6 +31,7 @@ class FacilityPageRouter extends _$FacilityPageRouter {
             AutoRoute(page: FacilityHistoryPageRoute.page, path: 'history'),
           ],
         ),
-        CustomRoute(page: FacilityDetailPageRoute.page, path: RouterPath.facilityDetail, transitionsBuilder: applySlideTransition)
+        CustomRoute(page: FacilityDetailPageRoute.page, path: RouterPath.facilityDetail, transitionsBuilder: applySlideTransition),
+        CustomRoute(page: FacilityBookingPageRoute.page, path: RouterPath.facilityBook, transitionsBuilder: applySlideTransition),
       ];
 }

+ 20 - 0
packages/cpt_facility/lib/router/page/facility_page_router.gr.dart

@@ -27,6 +27,12 @@ abstract class _$FacilityPageRouter extends RootStackRouter {
         child: FacilityBookScreen(),
       );
     },
+    FacilityBookingPageRoute.name: (routeData) {
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: const FacilityBookingPage(),
+      );
+    },
     FacilityDepositPageRoute.name: (routeData) {
       return AutoRoutePage<dynamic>(
         routeData: routeData,
@@ -83,6 +89,20 @@ class FacilityBookPageRoute extends PageRouteInfo<void> {
 }
 
 /// generated route for
+/// [FacilityBookingPage]
+class FacilityBookingPageRoute extends PageRouteInfo<void> {
+  const FacilityBookingPageRoute({List<PageRouteInfo>? children})
+      : super(
+          FacilityBookingPageRoute.name,
+          initialChildren: children,
+        );
+
+  static const String name = 'FacilityBookingPageRoute';
+
+  static const PageInfo<void> page = PageInfo<void>(name);
+}
+
+/// generated route for
 /// [FacilityDepositScreen]
 class FacilityDepositPageRoute extends PageRouteInfo<void> {
   const FacilityDepositPageRoute({List<PageRouteInfo>? children})

+ 11 - 0
packages/cs_resources/lib/theme/app_colors_theme.dart

@@ -31,6 +31,7 @@ class AppColorsTheme extends ThemeExtension<AppColorsTheme> {
   static const _color8B96BA = Color(0xFF8B96BA);
   static const _colorB4C5FF = Color(0xFFB4C5FF);
   static const _colorFE4066 = Color(0xFFFE4066);
+  static const _colorE9E9E9 = Color(0xFFE9E9E9);
 
   //暗色主题的一些自定义颜色值
   static const _darkBlackBg = Color(0xFF0F0F0F);
@@ -70,6 +71,8 @@ class AppColorsTheme extends ThemeExtension<AppColorsTheme> {
   final Color textLightPurple; //文本淡紫色
   final Color avatarBg; //头像框的淡蓝色
   final Color deleteRed; //删除的红色
+  final Color grayBgE9; //e9灰色背景
+  final Color disEnableGray; //禁用的灰色
 
   // 私有的构造函数
   const AppColorsTheme._internal({
@@ -102,6 +105,8 @@ class AppColorsTheme extends ThemeExtension<AppColorsTheme> {
     required this.textLightPurple,
     required this.avatarBg,
     required this.deleteRed,
+    required this.grayBgE9,
+    required this.disEnableGray,
   });
 
   // 浅色主题工厂方法
@@ -136,6 +141,8 @@ class AppColorsTheme extends ThemeExtension<AppColorsTheme> {
       textLightPurple: _color8B96BA,
       avatarBg: _colorB4C5FF,
       deleteRed: _colorFE4066,
+      grayBgE9: _colorE9E9E9,
+      disEnableGray: _colorBDBDBD,
     );
   }
 
@@ -171,6 +178,8 @@ class AppColorsTheme extends ThemeExtension<AppColorsTheme> {
       textLightPurple: Colors.white,
       avatarBg: _darkBlackItemLight,
       deleteRed: Colors.white,
+      grayBgE9: _darkBlackItemLightMost,
+      disEnableGray: _darkBlackItemLight,
     );
   }
 
@@ -215,6 +224,8 @@ class AppColorsTheme extends ThemeExtension<AppColorsTheme> {
       textLightPurple: Color.lerp(textLightPurple, other.textLightPurple, t)!,
       avatarBg: Color.lerp(avatarBg, other.avatarBg, t)!,
       deleteRed: Color.lerp(deleteRed, other.deleteRed, t)!,
+      grayBgE9: Color.lerp(grayBgE9, other.grayBgE9, t)!,
+      disEnableGray: Color.lerp(disEnableGray, other.disEnableGray, t)!,
     );
   }
 }

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

@@ -52,6 +52,7 @@ class RouterPath {
   //设施
   static const facility = '/facility';
   static const facilityDetail = '/facility/detail';
+  static const facilityBook = '/facility/book';
 
   //表单
   static const form = '/form';

+ 68 - 0
packages/cs_widgets/lib/shatter/weekly_calendar/calendar_utils.dart

@@ -0,0 +1,68 @@
+DateTime addDay(DateTime date, int days) {
+  return date.add(Duration(days: days));
+}
+
+DateTime subtractDay(DateTime date, int days) {
+  return date.subtract(Duration(days: days));
+}
+
+DateTime firstDayOfWeek(DateTime date) {
+  if (date.weekday == DateTime.sunday) {
+    return date;
+  }
+  return date.subtract(Duration(days: date.weekday));
+}
+
+// ===================================  获取一周的数据  ↓  ===================================
+
+List<DateTime> getWeekdays(DateTime date, int at) {
+  final first = firstDayOfWeek(date);
+  if (at >= 0) {
+    return _addWeek(first, at);
+  } else {
+    return _subtractWeek(first, -at);
+  }
+}
+
+List<DateTime> _addWeek(DateTime date, int add) {
+  final day = addDay(date, 7 * add);
+  return _getWeekDaysAt(day);
+}
+
+List<DateTime> _subtractWeek(DateTime date, int subtract) {
+  final day = subtractDay(date, 7 * subtract);
+  return _getWeekDaysAt(day);
+}
+
+List<DateTime> _getWeekDaysAt(DateTime date) {
+  return List.generate(7, (index) => index)
+      .map((index) => date.add(Duration(days: index)))
+      .toList();
+}
+
+// ===================================  获取二周的数据  ↓  ===================================
+
+List<DateTime> get2Weekdays(DateTime date, int at) {
+  final first = firstDayOfWeek(date);
+  if (at >= 0) {
+    return _add2Week(first, at);
+  } else {
+    return _subtract2Week(first, -at);
+  }
+}
+
+List<DateTime> _add2Week(DateTime date, int add) {
+  final day = addDay(date, 14 * add);
+  return _get2WeekDaysAt(day);
+}
+
+List<DateTime> _subtract2Week(DateTime date, int subtract) {
+  final day = subtractDay(date, 14 * subtract);
+  return _get2WeekDaysAt(day);
+}
+
+List<DateTime> _get2WeekDaysAt(DateTime date) {
+  return List.generate(14, (index) => index)
+      .map((index) => date.add(Duration(days: index)))
+      .toList();
+}

+ 86 - 0
packages/cs_widgets/lib/shatter/weekly_calendar/day_cell.dart

@@ -0,0 +1,86 @@
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:shared/utils/date_time_utils.dart';
+
+class DayCell extends StatelessWidget {
+  const DayCell({
+    super.key,
+    required this.display,
+    required this.selected,
+    required this.current,
+  });
+
+  final DateTime display;
+  final DateTime selected;
+  final DateTime current;
+
+  //显示的日期文本
+  String get dayText {
+    return DateTimeUtils.formatDate(display, format: 'dd');
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return AnimatedContainer(
+      duration: const Duration(milliseconds: 300),
+      width: 40,
+      height: 40,
+      decoration: BoxDecoration(
+        color: _isPastDate(display)
+            ? context.appColors.disEnableGray // 小于今天的日期背景颜色,禁用
+            : _isSelected(display)
+                ? _isToday(display)
+                    ? context.appColors.btnBgDefault // 今天的日期背景颜色
+                    : context.appColors.btnBgDefault // 选中的日期背景颜色
+                : context.appColors.grayBgE9, // 默认的背景颜色
+        shape: BoxShape.rectangle,
+        borderRadius: BorderRadius.circular(2.5), // 圆角
+      ),
+      alignment: Alignment.center,
+      child: Text(
+        dayText,
+        style: TextStyle(
+          fontSize: 17,
+          fontWeight: FontWeight.w500,
+          color: _dayTextColor(context, display),
+        ),
+      ),
+    );
+  }
+
+  Color _dayTextColor(BuildContext context, DateTime date) {
+    if (_isSelected(date)) {
+      return Colors.white;
+    }
+
+    if (_isToday(date)) {
+      return context.appColors.textBlack;
+    }
+
+    if (_isWeekend(date)) {
+      return context.appColors.textBlack;
+    }
+
+    return context.appColors.textBlack;
+  }
+
+  //是否是周末
+  bool _isWeekend(DateTime date) {
+    return date.weekday == DateTime.saturday || date.weekday == DateTime.sunday;
+  }
+
+  //是否是选中的
+  bool _isSelected(DateTime date) {
+    return DateTimeUtils.formatDate(date, format: 'yyyyMMdd') == DateTimeUtils.formatDate(selected, format: 'yyyyMMdd');
+  }
+
+  //是否是今天
+  bool _isToday(DateTime date) {
+    return DateTimeUtils.formatDate(date, format: 'yyyyMMdd') == DateTimeUtils.formatDate(current, format: 'yyyyMMdd');
+  }
+
+  // 检查日期是否小于今天
+  bool _isPastDate(DateTime date) {
+    return date.isBefore(DateTime.now().subtract(const Duration(days: 1))); // 小于今天
+  }
+}

+ 35 - 0
packages/cs_widgets/lib/shatter/weekly_calendar/day_of_week_cell.dart

@@ -0,0 +1,35 @@
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:intl/intl.dart';
+
+class DayOfWeekCell extends StatelessWidget {
+  const DayOfWeekCell({
+    super.key,
+    required this.date,
+  });
+
+  final DateTime date;
+
+  String get locale => "en"; //国际化
+  String get text => DateFormat.E(locale).format(date); //星期显示
+
+  @override
+  Widget build(BuildContext context) {
+    return Align(
+      alignment: Alignment.bottomCenter,
+      child: Text(
+        text,
+        style: TextStyle(
+          fontSize: 13,
+          fontWeight: FontWeight.w500,
+          color: _isWeekend(date) ? context.appColors.textBlack : context.appColors.textBlack,
+        ),
+      ),
+    );
+  }
+
+  //是否是周末
+  bool _isWeekend(DateTime date) {
+    return date.weekday == DateTime.saturday || date.weekday == DateTime.sunday;
+  }
+}

+ 30 - 0
packages/cs_widgets/lib/shatter/weekly_calendar/day_of_week_view.dart

@@ -0,0 +1,30 @@
+import 'package:flutter/material.dart';
+
+import 'day_of_week_cell.dart';
+
+/// 根据7天生成对应没每一天的星期
+class DayOfWeekView extends StatelessWidget {
+  const DayOfWeekView({
+    super.key,
+    required this.weekdays,
+  });
+
+  final List<DateTime> weekdays;
+
+  @override
+  Widget build(BuildContext context) {
+    return Table(
+      children: [
+        TableRow(
+          children: weekdays
+              .map(
+                (date) => DayOfWeekCell(
+                  date: date,
+                ),
+              )
+              .toList(),
+        ),
+      ],
+    );
+  }
+}

+ 74 - 0
packages/cs_widgets/lib/shatter/weekly_calendar/day_table_view.dart

@@ -0,0 +1,74 @@
+import 'package:flutter/material.dart';
+import 'day_cell.dart';
+
+/// 一周的数据为一行,展示两周共两行
+class DayTableView extends StatelessWidget {
+  const DayTableView({
+    super.key,
+    required this.weekdays,
+    required this.onSelect,
+    required this.selectedDate,
+    required this.currentDate,
+  });
+
+  final List<DateTime> weekdays;
+  final Function(DateTime)? onSelect;
+  final DateTime selectedDate;
+  final DateTime currentDate;
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      children: [
+        // 第一行
+        Row(
+          mainAxisAlignment: MainAxisAlignment.spaceAround,
+          children: weekdays.sublist(0, 7).map(
+                (date) {
+              return GestureDetector(
+                onTap: _isPastDate(date) ? null : () => onSelect?.call(date),
+                child: SizedBox(
+                  width: 40,
+                  height: 40,
+                  child: DayCell(
+                    display: date,
+                    selected: selectedDate,
+                    current: currentDate,
+                  ),
+                ),
+              );
+            },
+          ).toList(),
+        ),
+
+        const SizedBox(height: 10),
+
+        // 第二行
+        Row(
+          mainAxisAlignment: MainAxisAlignment.spaceAround,
+          children: weekdays.sublist(7, 14).map(
+                (date) {
+              return GestureDetector(
+                onTap: _isPastDate(date) ? null : () => onSelect?.call(date),
+                child: SizedBox(
+                  width: 40,
+                  height: 40,
+                  child: DayCell(
+                    display: date,
+                    selected: selectedDate,
+                    current: currentDate,
+                  ),
+                ),
+              );
+            },
+          ).toList(),
+        ),
+      ],
+    );
+  }
+
+  // 检查日期是否小于今天
+  bool _isPastDate(DateTime date) {
+    return date.isBefore(DateTime.now().subtract(const Duration(days: 1))); // 小于今天
+  }
+}

+ 151 - 0
packages/cs_widgets/lib/shatter/weekly_calendar/week_page.dart

@@ -0,0 +1,151 @@
+import 'package:flutter/material.dart';
+import 'calendar_utils.dart' show get2Weekdays, addDay, subtractDay, firstDayOfWeek;
+import 'day_table_view.dart';
+
+enum PageState {
+  previous,
+  current,
+  next,
+}
+
+/// 一周的Days的PageView页面
+class WeekPage extends StatefulWidget {
+  const WeekPage({
+    super.key,
+    required this.selectedDate,
+    required this.now,
+    required this.isAutoSelect,
+    this.onChangedSelectedDate,
+    this.onChangedPage,
+  });
+
+  final DateTime now;
+  final DateTime selectedDate;
+  final bool isAutoSelect;
+
+  final Function(DateTime)? onChangedSelectedDate; //选择日期变化的监听
+  final Function(DateTime date, PageState state)? onChangedPage; //切换日期Page的监听
+
+  final double height = 40 + 40 + 10;
+
+  @override
+  State<StatefulWidget> createState() => _WeekPageState();
+}
+
+class _WeekPageState extends State<WeekPage> {
+  double get height => widget.height;
+  final int initialPage = 999;
+  List<int> pageCounts = [0, 1, 2];
+  int currentPage = 1;
+  late DateTime _currentPageDate;
+  late DateTime _slectedDate;
+
+  DateTime get now => widget.now;
+
+  DateTime get selectedDate => widget.selectedDate;
+
+  @override
+  void initState() {
+    super.initState();
+    _currentPageDate = selectedDate;
+    _slectedDate = selectedDate;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Flexible(
+      child: AnimatedSize(
+        duration: const Duration(milliseconds: 250),
+        curve: Curves.easeInOut,
+        alignment: Alignment.topCenter,
+        child: SizedBox(
+          height: height,
+          child: pageViewBuilder(),
+        ),
+      ),
+    );
+  }
+
+  //PageView的方式展示不同的星期,每周每一个Page
+  Widget pageViewBuilder() {
+    final PageController pageController = PageController(initialPage: initialPage);
+    return PageView.builder(
+      itemBuilder: (context, index) {
+        final idx = _getIndex(index);
+        return dayTable(idx);
+      },
+      onPageChanged: onPageChanged,
+      controller: pageController,
+    );
+  }
+
+  //两周的数据
+  Widget dayTable(int index) {
+    int at = pageCounts[index] - 1;
+    final weekdays = get2Weekdays(now, at);
+    return DayTableView(
+      weekdays: weekdays,
+      onSelect: (date) {
+        setState(() {
+          _slectedDate = date;
+        });
+        //主动点击之后回调
+        widget.onChangedSelectedDate?.call(date);
+      },
+      selectedDate: _slectedDate,
+      currentDate: now,
+    );
+  }
+
+  //是否需要切换页面的时候自动选中
+  void changeSelectedDate(int value) {
+    if (_pageState(value) == PageState.next) {
+      if (widget.isAutoSelect) {
+        _slectedDate = addDay(selectedDate, 14);
+        widget.onChangedSelectedDate?.call(_slectedDate);
+      }
+    } else {
+      if (widget.isAutoSelect) {
+        _slectedDate = subtractDay(selectedDate, 14);
+        widget.onChangedSelectedDate?.call(_slectedDate);
+      }
+    }
+  }
+
+  //当切换页面的时候更新当前页面数据,返回当前页面第一条数据的日期
+  void updateCurrentPageDate(int value) {
+    final first = firstDayOfWeek(_currentPageDate);
+
+    if (_pageState(value) == PageState.next) {
+      _currentPageDate = addDay(first, 14);
+      widget.onChangedPage?.call(_currentPageDate, PageState.next);
+    } else {
+      _currentPageDate = subtractDay(first, 14);
+      widget.onChangedPage?.call(_currentPageDate, PageState.previous);
+    }
+  }
+
+  void onPageChanged(int value) {
+    changeSelectedDate(value);
+    updateCurrentPageDate(value);
+
+    int currentIndex = _getIndex(value);
+    int leftIndex = (currentIndex - 1 < 0) ? pageCounts.length - 1 : currentIndex - 1;
+    int rightIndex = (currentIndex + 1 > pageCounts.length - 1) ? 0 : currentIndex + 1;
+
+    pageCounts[leftIndex] = pageCounts[currentIndex] - 1;
+    pageCounts[rightIndex] = pageCounts[currentIndex] + 1;
+
+    currentPage = pageCounts[currentIndex];
+  }
+
+  int _getIndex(int idx) {
+    return (idx + 1) % pageCounts.length;
+  }
+
+  PageState _pageState(int idx) {
+    final prePage = currentPage;
+    final current = pageCounts[_getIndex(idx)];
+    return (prePage < current) ? PageState.next : PageState.previous;
+  }
+}

+ 78 - 0
packages/cs_widgets/lib/shatter/weekly_calendar/weekly_calendar.dart

@@ -0,0 +1,78 @@
+import 'package:flutter/material.dart';
+import 'package:intl/date_symbol_data_local.dart';
+import 'package:widgets/shatter/weekly_calendar/week_page.dart';
+
+import 'calendar_utils.dart' show getWeekdays;
+import 'day_of_week_view.dart';
+
+/// 总入口,整合顶部的Week控件与底部的Days的PageView控件
+class WeeklyCalendar extends StatefulWidget {
+  const WeeklyCalendar({
+    super.key,
+    this.isAutoSelect = false,
+    this.onChangedSelectedDate,
+    this.onChangedPage,
+    this.selectedDate,
+  });
+
+
+  final DateTime? selectedDate;
+  final bool isAutoSelect;
+  final Function(DateTime)? onChangedSelectedDate;
+  final Function(DateTime date, PageState state)? onChangedPage;
+
+  @override
+  State<StatefulWidget> createState() => _WeeklyCalendarState();
+}
+
+class _WeeklyCalendarState extends State<WeeklyCalendar> {
+  final DateTime now = DateTime.now();
+  late DateTime selectedDate;
+  DateTime currentPageDate = DateTime.now();
+
+
+  @override
+  void initState() {
+    initializeDateFormatting("en");  //日期国际化指定
+    super.initState();
+    selectedDate = widget.selectedDate ?? DateTime.now();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      mainAxisSize: MainAxisSize.min,
+      children: [
+        //顶部是固定的星期布局
+        customDayOfWeek(),
+
+        const SizedBox(height: 12),
+
+        //底部是每个星期的Days的PageView布局
+        WeekPage(
+          selectedDate: selectedDate,  //默认选中的日期,如果没有填写默认是今天
+          now: now,                    //今天的日期
+          isAutoSelect: widget.isAutoSelect,  //翻页是否自动选中
+          onChangedPage: (date, state) {
+            setState(() {
+              currentPageDate = date;
+            });
+            widget.onChangedPage?.call(date, state);
+          },
+          onChangedSelectedDate: (date) {
+            setState(() {
+              selectedDate = date;
+            });
+            widget.onChangedSelectedDate?.call(date);
+          },
+        ),
+      ],
+    );
+  }
+
+  //星期的文本显示
+  Widget customDayOfWeek() {
+    final weekdays = getWeekdays(now, 0);
+    return DayOfWeekView(weekdays: weekdays);
+  }
+}

+ 2 - 1
packages/cs_widgets/lib/widget_export.dart

@@ -5,4 +5,5 @@ export 'package:sticky_headers/sticky_headers.dart';
 export 'package:easy_refresh/easy_refresh.dart';
 export 'package:flutter_html/flutter_html.dart';
 export 'package:webview_flutter/webview_flutter.dart';
-export 'package:hand_signature/signature.dart';
+export 'package:hand_signature/signature.dart';
+export 'shatter/weekly_calendar/weekly_calendar.dart';