import 'package:flutter/material.dart'; import '../../app/app_controller.dart'; import '../../app/app_theme.dart'; import '../../domain/schedule_engine.dart'; import '../../domain/school_identity.dart'; import '../../widgets/directional_week_transition.dart'; import '../profile/settings_page.dart'; import 'week_grid.dart'; class TimetablePage extends StatefulWidget { const TimetablePage({super.key, required this.controller, this.onSync}); final AppController controller; final VoidCallback? onSync; @override State createState() => _TimetablePageState(); } class _TimetablePageState extends State { int? _week; bool? _wasDemo; double _weekDrag = 0; @override Widget build(BuildContext context) { final c = widget.controller, term = c.visibleSettings.semester; final today = ScheduleEngine.schoolDate(c.clock()); if (_wasDemo != c.isDemo) { _week = null; _wasDemo = c.isDemo; } final current = term == null ? 1 : (ScheduleEngine.weekNumber(term, today) ?? 1); final week = (_week ?? current).clamp(1, term?.totalWeeks ?? 1); final monday = term == null ? today.subtract(Duration(days: today.weekday - 1)) : term.firstMonday.add(Duration(days: (week - 1) * 7)); final sunday = monday.add(const Duration(days: 6)); return GestureDetector( onHorizontalDragStart: term == null ? null : (_) => _weekDrag = 0, onHorizontalDragUpdate: term == null ? null : (details) => _weekDrag += details.delta.dx, onHorizontalDragCancel: term == null ? null : () => _weekDrag = 0, onHorizontalDragEnd: term == null ? null : (details) { final velocity = details.primaryVelocity ?? 0; if (_weekDrag.abs() < 48 && velocity.abs() < 400) return; final direction = _weekDrag.abs() >= 48 ? _weekDrag : velocity; final next = (week + (direction < 0 ? 1 : -1)).clamp( 1, term.totalWeeks, ); if (next != week) setState(() => _week = next); }, child: ListView( key: const PageStorageKey('timetable-scroll'), padding: EdgeInsets.fromLTRB( 8, 10, 8, 140 + MediaQuery.paddingOf(context).bottom, ), children: [ Padding( padding: EdgeInsets.symmetric(horizontal: 14), child: Row( children: [ const Expanded( child: Text( '课表', style: TextStyle(fontSize: 22, fontWeight: FontWeight.w700), ), ), IconButton( tooltip: '刷新课表', onPressed: widget.onSync, icon: const Icon(Icons.sync_rounded), ), ], ), ), if (!c.isDemo && c.snapshot.schoolTerm != null) Padding( padding: const EdgeInsets.fromLTRB(14, 0, 14, 8), child: Text( '${c.snapshot.schoolTerm!.school.label} · ${c.snapshot.schoolTerm!.label}', style: TextStyle( fontSize: 12, color: ink.withValues(alpha: 0.5), ), ), ), if (!c.isDemo && c.snapshot.schoolTerm != null && c.snapshot.selectedSchool != null && c.snapshot.selectedSchool != c.snapshot.schoolTerm!.school) Padding( padding: const EdgeInsets.fromLTRB(14, 0, 14, 8), child: Text( '当前仍为${c.snapshot.schoolTerm!.school.label}课表;新课表导入成功后才会替换。', style: const TextStyle(fontSize: 12, color: river), ), ), const SizedBox(height: 10), SurfaceCard( padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 0), child: Row( children: [ IconButton( onPressed: term != null && week > 1 ? () => setState(() => _week = week - 1) : null, icon: const Icon(Icons.chevron_left_rounded), ), Expanded( child: Column( children: [ Text( term == null ? '本周' : '第 $week 周', style: const TextStyle( fontSize: 17, fontWeight: FontWeight.w700, ), ), const SizedBox(height: 4), Text( '${monday.month}.${monday.day.toString().padLeft(2, '0')} – ${sunday.month}.${sunday.day.toString().padLeft(2, '0')}', style: TextStyle( fontSize: 12, color: ink.withValues(alpha: 0.5), ), ), ], ), ), IconButton( onPressed: term != null && week < term.totalWeeks ? () => setState(() => _week = week + 1) : null, icon: const Icon(Icons.chevron_right_rounded), ), TextButton( onPressed: () => setState(() => _week = null), child: const Text('本周'), ), ], ), ), const SizedBox(height: 4), if (c.isDemo) Padding( padding: const EdgeInsets.only(bottom: 12), child: Wrap( crossAxisAlignment: WrapCrossAlignment.center, spacing: 8, children: [ Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 7, ), decoration: BoxDecoration( color: const Color(0xFFE2EEFF), borderRadius: BorderRadius.circular(10), ), child: const Text( '示例课表', style: TextStyle( fontSize: 11, color: Color(0xFF356AA8), fontWeight: FontWeight.w600, ), ), ), const Text( '不会生成提醒或日历事件', style: TextStyle(fontSize: 10, color: Color(0xFF8193AB)), ), TextButton(onPressed: c.leaveDemo, child: const Text('退出示例')), ], ), ), if (term != null) ...[ DirectionalWeekTransition( key: ValueKey((c.isDemo, term.firstMonday, term.totalWeeks)), week: week, child: WeekGrid( events: ScheduleEngine.occurrences( c.visibleCourses, term, ).where((e) => e.week == week).toList(), monday: monday, today: today, settings: c.visibleSettings, periodCount: !c.isDemo && c.snapshot.schoolTerm?.school == SupportedSchool.uestcUndergraduate ? 12 : 11, ), ), const SizedBox(height: 12), Text( '点击课程查看完整信息', textAlign: TextAlign.center, style: TextStyle( fontSize: 11, color: ink.withValues(alpha: 0.35), ), ), ] else if (c.visibleCourses.isNotEmpty) ...[ const SizedBox(height: 36), Padding( padding: const EdgeInsets.symmetric(horizontal: 14), child: SurfaceCard( child: Column( children: [ const Icon( Icons.event_available_rounded, size: 44, color: river, ), const SizedBox(height: 18), const Text( '课表已导入,请设置学期开始日', textAlign: TextAlign.center, style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 10), Text( '${c.visibleCourses.length} 条课程安排已保存', style: const TextStyle(fontSize: 13, color: river), ), const SizedBox(height: 10), const Text( '课程按教学周保存。设置真实的学期开始日后,\n才能显示每周课表并安排课前提醒。', textAlign: TextAlign.center, style: TextStyle(fontSize: 12, height: 1.8), ), const SizedBox(height: 20), FilledButton.icon( onPressed: () => Navigator.push( context, MaterialPageRoute( builder: (_) => SettingsPage(controller: c, focusSemester: true), ), ), icon: const Icon(Icons.edit_calendar_rounded, size: 18), label: const Text('设置学期开始日'), ), ], ), ), ), ] else ...[ const SizedBox(height: 38), const Icon( Icons.calendar_month_outlined, size: 66, color: Color(0xFFB7CDF0), ), const SizedBox(height: 22), const Text( '让课表,陪你开启每一天', textAlign: TextAlign.center, style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600), ), const SizedBox(height: 12), Text( '在「个人设置」设置学期并导入课程,\n或先看看示例课表。', textAlign: TextAlign.center, style: TextStyle( fontSize: 13, height: 1.8, color: ink.withValues(alpha: 0.5), ), ), const SizedBox(height: 25), Center( child: FilledButton.icon( onPressed: c.showDemo, icon: const Icon(Icons.auto_awesome_outlined, size: 18), label: const Text('体验示例课表'), ), ), ], ], ), ); } }