import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/utils/responsive_utils.dart'; import '../../../core/routes/app_routes.dart'; import '../models/vocabulary_book_model.dart'; import '../models/vocabulary_book_category.dart'; import '../models/word_model.dart'; import '../data/vocabulary_book_factory.dart'; import '../providers/vocabulary_provider.dart'; import '../../../core/routes/app_routes.dart'; import '../../auth/providers/auth_provider.dart'; /// 词汇学习主页面 class VocabularyHomeScreen extends ConsumerStatefulWidget { const VocabularyHomeScreen({super.key}); @override ConsumerState createState() => _VocabularyHomeScreenState(); } class _VocabularyHomeScreenState extends ConsumerState { @override void initState() { super.initState(); // 初始化时加载统计数据 WidgetsBinding.instance.addPostFrameCallback((_) { _loadStats(); }); } // 移除 _loadVocabularyBooks 方法,因为 provider 已经自动加载} Future _loadStats() async { try { final vocabularyNotifier = ref.read(vocabularyProvider.notifier); // 获取当前用户ID以加载每日统计 final user = ref.read(currentUserProvider); final userId = user?.id; await vocabularyNotifier.loadUserVocabularyOverallStats(); await vocabularyNotifier.loadWeeklyStudyStats(); if (userId != null && userId.isNotEmpty) { await vocabularyNotifier.loadTodayStudyWords(userId: userId); } else { // 无用户时仍加载今日单词与默认统计 await vocabularyNotifier.loadTodayStudyWords(); } } catch (_) { // 忽略错误,在UI中以默认值显示 } } // 获取推荐词汇书数据 static List get _recommendedBooks => VocabularyBookFactory.getRecommendedBooks(limit: 8); @override Widget build(BuildContext context) { final vocabularyAsync = ref.watch(vocabularyProvider); return Scaffold( backgroundColor: const Color(0xFFF5F5F5), appBar: AppBar( backgroundColor: Colors.transparent, elevation: 0, leading: IconButton( icon: const Icon(Icons.arrow_back, color: Colors.black87), onPressed: () => Navigator.of(context).pop(), ), title: const Text( '词汇学习', style: TextStyle( color: Colors.black87, fontSize: 18, fontWeight: FontWeight.w600, ), ), centerTitle: true, ), body: SafeArea( child: SingleChildScrollView( physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), child: Column( children: [ const SizedBox(height: 12), _buildTitle(), const SizedBox(height: 12), _buildCategoryNavigation(), const SizedBox(height: 12), _buildTodayWords(), const SizedBox(height: 12), // _buildAIRecommendation(), // const SizedBox(height: 12), _buildLearningStats(), const SizedBox(height: 80), // 底部导航栏空间 ], ), ), ), ); } Widget _buildTitle() { return const Padding( padding: EdgeInsets.symmetric(horizontal: 16), child: Align( alignment: Alignment.centerLeft, child: Text( '选择词书开始学习', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), ), ); } Widget _buildCategoryNavigation() { // 从 API 加载分类数据 final state = ref.watch(vocabularyProvider); final categories = state.categories; print('📂 分类数据: ${categories.length} 个分类'); if (categories.isNotEmpty) { print('📂 第一个分类: ${categories.first}'); } // 如果API没有返回分类数据,使用本地分类 final displayCategories = categories.isEmpty ? _getLocalCategories() : categories; return ResponsiveBuilder( builder: (context, isMobile, isTablet, isDesktop) { return Container( margin: ResponsiveUtils.getResponsivePadding(context), child: GridView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 12, mainAxisSpacing: 12, mainAxisExtent: isMobile ? 110 : 130, ), itemCount: displayCategories.length, itemBuilder: (context, index) { final category = displayCategories[index]; final categoryName = category['name'] as String; final count = category['count'] as int; // 从中文名称映射到枚举(用于显示图标和颜色) final mainCategory = VocabularyBookCategoryHelper.getMainCategoryFromName(categoryName); if (mainCategory == null) return const SizedBox.shrink(); // 根据分类获取图标和颜色 final iconData = _getCategoryIcon(mainCategory); final color = _getCategoryColor(mainCategory); return _buildCategoryCard( mainCategory, iconData, color, count: count, ); }, ), ); }, ); } // 获取本地分类数据作为后备方案 List> _getLocalCategories() { return [ {'name': '学段基础词汇', 'count': 4}, {'name': '国内应试类词汇', 'count': 4}, {'name': '出国考试类词汇', 'count': 3}, {'name': '职业与专业类词汇', 'count': 3}, {'name': '功能型词库', 'count': 3}, ]; } IconData _getCategoryIcon(VocabularyBookMainCategory category) { switch (category) { case VocabularyBookMainCategory.academicStage: return Icons.school; case VocabularyBookMainCategory.domesticTest: return Icons.quiz; case VocabularyBookMainCategory.internationalTest: return Icons.public; case VocabularyBookMainCategory.professional: return Icons.work; case VocabularyBookMainCategory.functional: return Icons.functions; } } Color _getCategoryColor(VocabularyBookMainCategory category) { switch (category) { case VocabularyBookMainCategory.academicStage: return Colors.blue; case VocabularyBookMainCategory.domesticTest: return Colors.green; case VocabularyBookMainCategory.internationalTest: return Colors.orange; case VocabularyBookMainCategory.professional: return Colors.purple; case VocabularyBookMainCategory.functional: return Colors.teal; } } Widget _buildCategoryCard(VocabularyBookMainCategory category, IconData icon, Color color, {int? count}) { final categoryName = VocabularyBookCategoryHelper.getMainCategoryName(category); return GestureDetector( onTap: () => _navigateToCategory(category), child: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(20), ), child: Icon( icon, color: color, size: 22, ), ), const SizedBox(height: 8), Text( categoryName, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, ), textAlign: TextAlign.center, maxLines: 2, overflow: TextOverflow.ellipsis, ), if (count != null) ...[ const SizedBox(height: 4), Text( '$count 本', style: TextStyle( fontSize: 12, color: Colors.grey[600], ), ), ], ], ), ), ); } Widget _buildVocabularyBooks() { final systemBooks = ref.watch(systemVocabularyBooksProvider); final userBooks = ref.watch(userVocabularyBooksProvider); // 合并系统词汇书和用户词汇书 final allBooks = [...systemBooks, ...userBooks]; // 如果没有数据,显示推荐词书 final booksToShow = allBooks.isNotEmpty ? allBooks : _recommendedBooks; return ResponsiveBuilder( builder: (context, isMobile, isTablet, isDesktop) { final crossAxisCount = ResponsiveUtils.getGridColumns( context, mobileColumns: 3, tabletColumns: 4, desktopColumns: 5, ); final childAspectRatio = ResponsiveUtils.getValueForScreenSize( context, mobile: 1.3, tablet: 1.4, desktop: 1.5, ); return Container( margin: ResponsiveUtils.getResponsivePadding(context), child: GridView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: crossAxisCount, crossAxisSpacing: 8, mainAxisSpacing: 8, childAspectRatio: childAspectRatio, ), itemCount: booksToShow.length, itemBuilder: (context, index) { final book = booksToShow[index]; // 使用后端 Provider 获取词书学习进度 final progressAsync = ref.watch(vocabularyBookProgressProvider(book.id)); final progress = progressAsync.when( data: (p) => (p.progressPercentage / 100.0).clamp(0.0, 1.0), loading: () => null, error: (e, st) => 0.0, ); return _buildBookCard( book, progress, ); }, ), ); }, ); } Widget _buildBookCard(VocabularyBook book, double? progress) { return ResponsiveBuilder( builder: (context, isMobile, isTablet, isDesktop) { final iconSize = ResponsiveUtils.getValueForScreenSize( context, mobile: 64.0, tablet: 72.0, desktop: 80.0, ); final titleFontSize = ResponsiveUtils.getResponsiveFontSize( context, mobileSize: 14, tabletSize: 16, desktopSize: 18, ); final subtitleFontSize = ResponsiveUtils.getResponsiveFontSize( context, mobileSize: 12, tabletSize: 14, desktopSize: 16, ); final progressFontSize = ResponsiveUtils.getResponsiveFontSize( context, mobileSize: 10, tabletSize: 12, desktopSize: 14, ); final cardPadding = ResponsiveUtils.getValueForScreenSize( context, mobile: 8.0, tablet: 10.0, desktop: 12.0, ); return GestureDetector( onTap: () => _navigateToVocabularyBook(book), child: Container( padding: EdgeInsets.all(cardPadding), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( children: [ Container( width: iconSize, height: iconSize, decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), image: book.coverImageUrl != null ? DecorationImage( image: NetworkImage(book.coverImageUrl!), fit: BoxFit.cover, ) : null, color: book.coverImageUrl == null ? Colors.grey[300] : null, ), child: book.coverImageUrl == null ? Icon( Icons.book, color: Colors.grey, size: iconSize * 0.4, ) : null, ), const SizedBox(height: 4), Text( book.name, style: TextStyle( fontSize: titleFontSize, fontWeight: FontWeight.bold, ), textAlign: TextAlign.center, maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 1), Text( '${book.totalWords}词', style: TextStyle( fontSize: subtitleFontSize, color: Colors.grey, ), ), const SizedBox(height: 3), LinearProgressIndicator( value: progress, backgroundColor: Colors.grey[200], valueColor: const AlwaysStoppedAnimation(Color(0xFF2196F3)), minHeight: 4, ), const SizedBox(height: 1), Text( progress == null ? '加载中...' : '${(progress * 100).toInt()}%', style: TextStyle( fontSize: ResponsiveUtils.getValueForScreenSize( context, mobile: 9.0, tablet: 10.0, desktop: 12.0, ), color: Colors.grey[600], ), textAlign: TextAlign.center, ), ], ), ), ); }, ); } void _navigateToVocabularyBook(VocabularyBook book) { Navigator.of(context).pushNamed( Routes.vocabularyList, arguments: {'vocabularyBook': book}, ); } void _startTodayWordLearning() { // 导航到今日单词详情页面 Navigator.of(context).pushNamed(Routes.dailyWords); } void _showWordDetailFromModel(Word word) { final meaning = word.definitions.isNotEmpty ? word.definitions.map((d) => d.translation).join('; ') : '暂无释义'; final phonetic = word.phonetic ?? ''; _showWordDetail(word.word, meaning, phonetic, word); } void _showWordDetail(String wordText, String meaning, String phonetic, [Word? wordModel]) { showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (context) => Container( height: MediaQuery.of(context).size.height * 0.7, decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), child: Column( children: [ Container( width: 40, height: 4, margin: const EdgeInsets.only(top: 12), decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.circular(2), ), ), Expanded( child: Padding( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( wordText, style: const TextStyle( fontSize: 28, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Text( phonetic, style: const TextStyle( fontSize: 16, color: Color(0xFF2196F3), fontStyle: FontStyle.italic, ), ), ], ), ), IconButton( onPressed: () => _playWordPronunciation(wordText), icon: const Icon( Icons.volume_up, color: Color(0xFF2196F3), size: 32, ), ), ], ), const SizedBox(height: 24), const Text( '释义', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), Text( meaning, style: const TextStyle( fontSize: 16, color: Colors.black87, ), ), const SizedBox(height: 24), Row( children: [ Expanded( child: ElevatedButton( onPressed: () { Navigator.pop(context); if (wordModel != null) { _startWordLearningWithModel(wordModel); } else { _startWordLearning(wordText); } }, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF2196F3), foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: const Text('开始学习'), ), ), const SizedBox(width: 12), Expanded( child: OutlinedButton( onPressed: () { if (wordModel != null) { _addWordToFavorites(wordModel); } else { _addToFavorites(wordText); } }, style: OutlinedButton.styleFrom( foregroundColor: const Color(0xFF2196F3), side: const BorderSide(color: Color(0xFF2196F3)), padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: const Text('收藏'), ), ), ], ), ], ), ), ), ], ), ), ); } void _startWordLearningWithModel(Word word) { Navigator.of(context).pushNamed( Routes.wordLearning, arguments: { 'words': [word], 'mode': LearningMode.normal, 'dailyTarget': 1, }, ); } void _startWordLearning(String word) { // 为单个单词创建学习会话 final wordToLearn = Word( id: '1', word: word, phonetic: '/example/', difficulty: WordDifficulty.intermediate, frequency: 1000, definitions: [ WordDefinition( type: WordType.noun, definition: word, translation: '示例释义', ), ], examples: [ WordExample( sentence: 'This is an example sentence.', translation: '这是一个示例句子。', ), ], createdAt: DateTime.now(), updatedAt: DateTime.now(), ); Navigator.of(context).pushNamed( Routes.wordLearning, arguments: { 'words': [wordToLearn], 'mode': LearningMode.normal, 'dailyTarget': 1, }, ); } void _playWordAudio(Word word) async { if (word.audioUrl != null && word.audioUrl!.isNotEmpty) { // TODO: 集成音频服务播放 ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('正在播放 "${word.word}" 的发音'), duration: const Duration(seconds: 1), ), ); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('"${word.word}" 暂无音频'), duration: const Duration(seconds: 1), ), ); } } void _playWordPronunciation(String word) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('播放 "$word" 的发音'), duration: const Duration(seconds: 1), ), ); } void _addWordToFavorites(Word word) async { // TODO: 集成生词本服务 ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('已将 "${word.word}" 添加到生词本'), duration: const Duration(seconds: 2), backgroundColor: Colors.green, ), ); } void _addToFavorites(String word) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('已将 "$word" 添加到收藏夹'), duration: const Duration(seconds: 2), ), ); } Widget _buildTodayWords() { final vocabularyAsync = ref.watch(vocabularyProvider); return Container( margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '今日单词', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), TextButton( onPressed: _startTodayWordLearning, child: const Text( '开始学习', style: TextStyle( color: Color(0xFF2196F3), fontSize: 14, ), ), ), ], ), const SizedBox(height: 16), Builder( builder: (context) { final state = ref.watch(vocabularyProvider); final todayWords = state.todayWords; if (todayWords.isEmpty) { return const Center( child: Padding( padding: EdgeInsets.all(16.0), child: Text( '今日暂无学习单词', style: TextStyle( color: Colors.grey, fontSize: 14, ), ), ), ); } // 显示前3个单词 final displayWords = todayWords.take(3).toList(); return Column( children: displayWords.asMap().entries.map((entry) { final index = entry.key; final word = entry.value; return Column( children: [ if (index > 0) const SizedBox(height: 12), _buildWordItemFromModel(word), ], ); }).toList(), ); }, ), ], ), ); } Widget _buildWordItemFromModel(Word word) { final meaning = word.definitions.isNotEmpty ? word.definitions.first.translation : '暂无释义'; final phonetic = word.phonetic ?? ''; return GestureDetector( onTap: () => _showWordDetailFromModel(word), child: Container( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( word.word, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), if (phonetic.isNotEmpty) ...[ const SizedBox(height: 2), Text( phonetic, style: const TextStyle( fontSize: 12, color: Color(0xFF2196F3), fontStyle: FontStyle.italic, ), ), ], const SizedBox(height: 2), Text( meaning, style: const TextStyle( fontSize: 14, color: Colors.grey, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), ), Row( mainAxisSize: MainAxisSize.min, children: [ if (word.audioUrl != null && word.audioUrl!.isNotEmpty) IconButton( onPressed: () => _playWordAudio(word), icon: const Icon( Icons.volume_up, color: Color(0xFF2196F3), size: 20, ), ), // IconButton( // onPressed: () => _addWordToFavorites(word), // icon: const Icon( // Icons.favorite_border, // color: Colors.grey, // size: 20, // ), // ), ], ), ], ), ), ); } Widget _buildAIRecommendation() { final state = ref.watch(vocabularyProvider); final reviewWords = state.reviewWords; final todayWords = state.todayWords; return Container( margin: const EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Padding( padding: EdgeInsets.only(left: 4, bottom: 12), child: Text( '推荐功能', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), ), Row( children: [ // 智能复习卡片 Expanded( child: _buildFeatureCard( title: '智能复习', subtitle: reviewWords.isEmpty ? '暂无复习内容' : '${reviewWords.length} 个单词待复习', icon: Icons.psychology_outlined, color: const Color(0xFF9C27B0), onTap: () => _startSmartReview(), ), ), const SizedBox(width: 12), // 词汇测试卡片 Expanded( child: _buildFeatureCard( title: '词汇测试', subtitle: '测试你的词汇量', icon: Icons.quiz_outlined, color: const Color(0xFFFF9800), onTap: () => _startVocabularyTest(), ), ), ], ), ], ), ); } Widget _buildFeatureCard({ required String title, required String subtitle, required IconData icon, required Color color, required VoidCallback onTap, }) { return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: 48, height: 48, decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Icon( icon, color: color, size: 28, ), ), const SizedBox(height: 12), Text( title, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF2C3E50), ), ), const SizedBox(height: 4), Text( subtitle, style: TextStyle( fontSize: 13, color: Colors.grey[600], ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ), ), ); } // 启动智能复习 void _startSmartReview() async { final state = ref.read(vocabularyProvider); final reviewWords = state.reviewWords; if (reviewWords.isEmpty) { // 如果没有复习词汇,尝试加载 await ref.read(vocabularyProvider.notifier).loadReviewWords(); final updatedWords = ref.read(vocabularyProvider).reviewWords; if (updatedWords.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('暂无需要复习的单词,赶紧去学习新单词吧!'), duration: Duration(seconds: 2), ), ); return; } } // 导航到智能复习页面 Navigator.of(context).pushNamed( Routes.smartReview, arguments: { 'reviewMode': 'adaptive', 'dailyTarget': 20, }, ); } // 启动词汇测试 void _startVocabularyTest() { // 显示测试选项对话框 showModalBottomSheet( context: context, backgroundColor: Colors.transparent, builder: (context) => Container( decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '选择测试类型', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 20), _buildTestOption( title: '快速测试', subtitle: '10道题,约 3 分钟', icon: Icons.speed, color: const Color(0xFF4CAF50), onTap: () { Navigator.pop(context); _navigateToTest(10); }, ), const SizedBox(height: 12), _buildTestOption( title: '标准测试', subtitle: '20道题,约 6 分钟', icon: Icons.assessment, color: const Color(0xFF2196F3), onTap: () { Navigator.pop(context); _navigateToTest(20); }, ), const SizedBox(height: 12), _buildTestOption( title: '完整测试', subtitle: '50道题,约 15 分钟', icon: Icons.assignment, color: const Color(0xFFFF9800), onTap: () { Navigator.pop(context); _navigateToTest(50); }, ), const SizedBox(height: 16), TextButton( onPressed: () => Navigator.pop(context), child: const Center( child: Text('取消'), ), ), ], ), ), ); } Widget _buildTestOption({ required String title, required String subtitle, required IconData icon, required Color color, required VoidCallback onTap, }) { return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(12), child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( border: Border.all(color: Colors.grey[200]!), borderRadius: BorderRadius.circular(12), ), child: Row( children: [ Container( width: 48, height: 48, decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Icon( icon, color: color, size: 24, ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Text( subtitle, style: TextStyle( fontSize: 13, color: Colors.grey[600], ), ), ], ), ), Icon( Icons.arrow_forward_ios, size: 16, color: Colors.grey[400], ), ], ), ), ); } void _navigateToTest(int questionCount) { Navigator.of(context).pushNamed( Routes.vocabularyTest, arguments: { 'testType': 'vocabularyLevel', 'questionCount': questionCount, }, ); } Widget _buildLearningStats() { final dailyStats = ref.watch(dailyVocabularyStatsProvider); final todayStats = ref.watch(todayStatisticsProvider); final overallStats = ref.watch(overallVocabularyStatsProvider); final weeklyTotal = ref.watch(weeklyWordsStudiedProvider); final currentUser = ref.watch(currentUserProvider); final todayLearned = dailyStats?.wordsLearned ?? todayStats?.wordsStudied ?? 0; final totalStudied = (overallStats != null && overallStats['total_studied'] != null) ? (overallStats['total_studied'] as num).toInt() : 0; final dailyWordGoal = currentUser?.settings?.dailyWordGoal ?? 20; final weeklyTarget = (dailyWordGoal * 7).clamp(1, 100000); final weeklyProgress = weeklyTarget > 0 ? (weeklyTotal / weeklyTarget) : 0.0; final weeklyPercent = ((weeklyProgress * 100).clamp(0, 100)).round(); return Container( margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '学习统计', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), TextButton( onPressed: _showDetailedStats, child: const Text( '查看详情', style: TextStyle( color: Color(0xFF2196F3), fontSize: 14, ), ), ), ], ), const SizedBox(height: 16), Row( children: [ Expanded( child: GestureDetector( onTap: _showTodayProgress, child: _buildStatItem('今日学习', '$todayLearned', '个单词', Icons.today), ), ), Expanded( child: GestureDetector( onTap: _showWeeklyProgress, child: _buildStatItem('本周学习', '$weeklyTotal', '个单词', Icons.calendar_view_week), ), ), Expanded( child: GestureDetector( onTap: _showVocabularyTest, child: _buildStatItem('总词汇量', '$totalStudied', '个单词', Icons.library_books), ), ), ], ), const SizedBox(height: 16), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xFF2196F3).withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Row( children: [ const Icon( Icons.trending_up, color: Color(0xFF2196F3), size: 20, ), const SizedBox(width: 8), const Text( '本周学习进度:', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), const SizedBox(width: 8), Expanded( child: LinearProgressIndicator( value: weeklyProgress.clamp(0.0, 1.0), backgroundColor: Colors.grey[300], valueColor: const AlwaysStoppedAnimation(Color(0xFF2196F3)), ), ), const SizedBox(width: 8), Text( '$weeklyPercent%', style: const TextStyle( fontSize: 12, color: Color(0xFF2196F3), fontWeight: FontWeight.bold, ), ), ], ), ), ], ), ); } Widget _buildStatItem(String label, String value, String unit, IconData icon) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.grey[50], borderRadius: BorderRadius.circular(8), ), child: Column( children: [ Icon( icon, color: const Color(0xFF2196F3), size: 24, ), const SizedBox(height: 8), Text( value, style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Color(0xFF2196F3), ), ), const SizedBox(height: 2), Text( unit, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), const SizedBox(height: 4), Text( label, style: const TextStyle( fontSize: 12, color: Colors.black87, fontWeight: FontWeight.w500, ), textAlign: TextAlign.center, ), ], ), ); } void _showDetailedStats() { Navigator.of(context).pushNamed(Routes.learningStatsDetail); } void _showTodayProgress() { // 显示今日学习进度详情 final dailyStats = ref.read(dailyVocabularyStatsProvider); final todayStats = ref.read(todayStatisticsProvider); final todayLearned = dailyStats?.wordsLearned ?? todayStats?.wordsStudied ?? 0; final currentUser = ref.read(currentUserProvider); final dailyWordGoal = currentUser?.settings?.dailyWordGoal ?? 20; final progress = dailyWordGoal > 0 ? (todayLearned / dailyWordGoal) : 0.0; showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (context) => Container( height: MediaQuery.of(context).size.height * 0.6, decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), child: Column( children: [ // 拖拽条 Container( width: 40, height: 4, margin: const EdgeInsets.only(top: 12, bottom: 20), decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.circular(2), ), ), // 内容 Expanded( child: SingleChildScrollView( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 48, height: 48, decoration: BoxDecoration( color: const Color(0xFF2196F3).withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: const Icon( Icons.today, color: Color(0xFF2196F3), size: 28, ), ), const SizedBox(width: 16), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '今日学习进度', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, ), ), SizedBox(height: 4), Text( '保持每日学习,让进步成为习惯', style: TextStyle( fontSize: 14, color: Colors.grey, ), ), ], ), ), ], ), const SizedBox(height: 32), // 进度环 Center( child: SizedBox( width: 160, height: 160, child: Stack( alignment: Alignment.center, children: [ SizedBox( width: 160, height: 160, child: CircularProgressIndicator( value: progress.clamp(0.0, 1.0), strokeWidth: 12, backgroundColor: Colors.grey[200], valueColor: const AlwaysStoppedAnimation( Color(0xFF2196F3), ), ), ), Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( '$todayLearned', style: const TextStyle( fontSize: 48, fontWeight: FontWeight.bold, color: Color(0xFF2196F3), ), ), Text( '/ $dailyWordGoal 个单词', style: TextStyle( fontSize: 14, color: Colors.grey[600], ), ), ], ), ], ), ), ), const SizedBox(height: 32), // 统计信息 _buildProgressStatRow('学习进度', '${(progress * 100).clamp(0, 100).toInt()}%', Icons.trending_up), const SizedBox(height: 16), _buildProgressStatRow('已学单词', '$todayLearned 个', Icons.check_circle), const SizedBox(height: 16), _buildProgressStatRow('剩余目标', '${(dailyWordGoal - todayLearned).clamp(0, dailyWordGoal)} 个', Icons.flag), const SizedBox(height: 32), // 鼓励文字 Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF2196F3).withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Row( children: [ const Icon( Icons.lightbulb_outline, color: Color(0xFF2196F3), ), const SizedBox(width: 12), Expanded( child: Text( progress >= 1.0 ? '太棒了!今天的目标已经完成!' : progress >= 0.5 ? '再加把劲,马上就能完成今天的目标!' : '加油!坚持学习,每天进步一点点!', style: const TextStyle( fontSize: 14, color: Color(0xFF2196F3), ), ), ), ], ), ), ], ), ), ), ], ), ), ); } Widget _buildProgressStatRow(String label, String value, IconData icon) { return Row( children: [ Icon( icon, size: 20, color: Colors.grey[600], ), const SizedBox(width: 12), Text( label, style: TextStyle( fontSize: 16, color: Colors.grey[700], ), ), const Spacer(), Text( value, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF2196F3), ), ), ], ); } void _showWeeklyProgress() { // 显示本周学习进度详情 final weeklyTotal = ref.read(weeklyWordsStudiedProvider); final currentUser = ref.read(currentUserProvider); final dailyWordGoal = currentUser?.settings?.dailyWordGoal ?? 20; final weeklyTarget = (dailyWordGoal * 7).clamp(1, 100000); final weeklyProgress = weeklyTarget > 0 ? (weeklyTotal / weeklyTarget) : 0.0; showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (context) => Container( height: MediaQuery.of(context).size.height * 0.6, decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), child: Column( children: [ // 拖拽条 Container( width: 40, height: 4, margin: const EdgeInsets.only(top: 12, bottom: 20), decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.circular(2), ), ), // 内容 Expanded( child: SingleChildScrollView( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 48, height: 48, decoration: BoxDecoration( color: const Color(0xFF4CAF50).withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: const Icon( Icons.calendar_view_week, color: Color(0xFF4CAF50), size: 28, ), ), const SizedBox(width: 16), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '本周学习进度', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, ), ), SizedBox(height: 4), Text( '坑持七天学习,养成好习惯', style: TextStyle( fontSize: 14, color: Colors.grey, ), ), ], ), ), ], ), const SizedBox(height: 32), // 进度环 Center( child: SizedBox( width: 160, height: 160, child: Stack( alignment: Alignment.center, children: [ SizedBox( width: 160, height: 160, child: CircularProgressIndicator( value: weeklyProgress.clamp(0.0, 1.0), strokeWidth: 12, backgroundColor: Colors.grey[200], valueColor: const AlwaysStoppedAnimation( Color(0xFF4CAF50), ), ), ), Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( '$weeklyTotal', style: const TextStyle( fontSize: 48, fontWeight: FontWeight.bold, color: Color(0xFF4CAF50), ), ), Text( '/ $weeklyTarget 个单词', style: TextStyle( fontSize: 14, color: Colors.grey[600], ), ), ], ), ], ), ), ), const SizedBox(height: 32), // 统计信息 _buildProgressStatRow('周学习进度', '${(weeklyProgress * 100).clamp(0, 100).toInt()}%', Icons.trending_up), const SizedBox(height: 16), _buildProgressStatRow('已学单词', '$weeklyTotal 个', Icons.check_circle), const SizedBox(height: 16), _buildProgressStatRow('周目标', '$weeklyTarget 个', Icons.flag), const SizedBox(height: 16), _buildProgressStatRow('剩余目标', '${(weeklyTarget - weeklyTotal).clamp(0, weeklyTarget)} 个', Icons.timer), const SizedBox(height: 32), // 鼓励文字 Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF4CAF50).withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Row( children: [ const Icon( Icons.emoji_events, color: Color(0xFF4CAF50), ), const SizedBox(width: 12), Expanded( child: Text( weeklyProgress >= 1.0 ? '完美!本周目标已经达成!' : weeklyProgress >= 0.7 ? '太棒了!本周已经完成大部分目标!' : '加油!距离本周目标还差一点点!', style: const TextStyle( fontSize: 14, color: Color(0xFF4CAF50), ), ), ), ], ), ), const SizedBox(height: 16), // 查看详情按钮 SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () { Navigator.pop(context); _showDetailedStats(); }, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF4CAF50), padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: const Text( '查看详细统计', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ), ), ], ), ), ), ], ), ), ); } void _showVocabularyTest() { // TODO: 导航到词汇量测试页面 ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('开始词汇量测试'), duration: Duration(seconds: 1), ), ); } void _navigateToCategory(VocabularyBookMainCategory category) { // 将枚举转换为中文名称传递 final categoryName = VocabularyBookCategoryHelper.getMainCategoryName(category); Navigator.pushNamed( context, Routes.vocabularyCategory, arguments: { 'category': categoryName, }, ); } }