import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; import '../../app/app_controller.dart'; import '../../domain/school_term.dart'; import 'school_session.dart'; class SchoolAuthPage extends StatefulWidget { const SchoolAuthPage({ super.key, required this.session, required this.controller, }); final SchoolSession session; final AppController controller; @override State createState() => _SchoolAuthPageState(); } class _SchoolAuthPageState extends State { bool _saving = false; SchoolTerm? _termChoice; @override void initState() { super.initState(); widget.session.addListener(_changed); WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) return; widget.session.setReadingActive(true); _changed(); }); } void _changed() { if (!mounted) return; setState(() {}); final s = widget.session; final courses = s.pendingDraft, exams = s.pendingExams; final generation = s.pageGeneration; final route = ModalRoute.of(context); if (s.needsTermSelection || _saving || (courses == null && exams == null)) { return; } _saving = true; WidgetsBinding.instance.addPostFrameCallback((_) async { if (!mounted) { _saving = false; return; } // The session draft must still belong to the same page after navigation. if (generation != s.pageGeneration || courses != s.pendingDraft || exams != s.pendingExams) { _saving = false; _changed(); return; } if (courses != null) { await widget.controller.applyImport(courses, confirmedEmpty: false); } if (exams != null) await widget.controller.applyExams(exams); final success = widget.controller.lastError == null; final acknowledged = s.saved( success: success, generation: generation, draft: courses, exams: exams, ); _saving = false; if (!mounted) return; if (!acknowledged) { _changed(); return; } if (success && route?.isCurrent == true) { Navigator.of(context).pop(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(exams == null ? '课表已导入;请核对学期开始日。' : '考试信息已更新'), ), ); } else { setState(() {}); } }); } @override void dispose() { widget.session.removeListener(_changed); widget.session.setReadingActive(false); super.dispose(); } @override Widget build(BuildContext context) { final s = widget.session; return Scaffold( appBar: AppBar( title: const Text('学校认证'), actions: [ IconButton( tooltip: '重新认证', onPressed: _saving ? null : s.openAuthentication, icon: const Icon(Icons.refresh_rounded), ), IconButton( tooltip: '读取当前页面', onPressed: _saving || s.needsTermSelection ? null : s.readCurrentPage, icon: const Icon(Icons.sync_rounded), ), ], ), body: Column( children: [ if (s.busy || _saving) const LinearProgressIndicator(minHeight: 2), Container( width: double.infinity, padding: const EdgeInsets.all(12), color: const Color(0xFFEFF6FF), child: Text( s.message, style: const TextStyle(fontSize: 12, height: 1.5), ), ), Expanded( child: Stack( children: [ WebViewWidget(controller: s.web), if (s.needsTermSelection) Positioned.fill(child: _termPicker(s)), if (!s.needsTermSelection && (s.finding || _saving)) Positioned.fill( child: ColoredBox( color: const Color(0xFFF5F9FE), child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const CircularProgressIndicator(), const SizedBox(height: 20), Text( _saving ? '正在保存,请稍候' : (s.requestingExams ? '正在查找考试安排,请稍候' : '正在查找课表,请稍候'), ), ], ), ), ), ), ], ), ), ], ), ); } Widget _termPicker(SchoolSession session) { final choices = session.termChoices; final preferred = _termChoice ?? session.selectedTerm ?? widget.controller.snapshot.schoolTerm ?? choices.first; final selected = choices.contains(preferred) ? preferred : choices.first; return Material( color: const Color(0xFFF5F9FE), child: SafeArea( child: Padding( padding: const EdgeInsets.fromLTRB(20, 16, 20, 20), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Text( '选择学校学期', style: TextStyle(fontSize: 22, fontWeight: FontWeight.w700), ), const SizedBox(height: 8), Text(session.requestingExams ? '将读取这个学期的考试安排。' : '将读取这个学期的课程表。'), const SizedBox(height: 12), Expanded( child: ListView.builder( itemCount: choices.length, itemBuilder: (context, index) { final term = choices[index]; final isSelected = term == selected; return ListTile( title: Text(term.label), selected: isSelected, leading: Icon( isSelected ? Icons.radio_button_checked : Icons.radio_button_off, ), onTap: () => setState(() => _termChoice = term), ); }, ), ), const SizedBox(height: 12), FilledButton( onPressed: () => session.selectTerm(selected), child: const Text('导入所选学期'), ), TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('取消'), ), ], ), ), ), ); } }