/// 时间: 2026-03-28 /// 功能: 答题记录页面 /// 介绍: 显示用户的诗词答题记录列表,包括题目、标签、是否答对等信息 /// 最新变化: 添加统计数据弹窗功能,支持主题色设置 import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import '../../../models/colors/app_colors.dart'; import '../../../models/colors/theme_colors.dart'; import '../../../controllers/shared_preferences_storage_controller.dart'; import '../../../controllers/history_controller.dart'; import '../../../services/network_listener_service.dart'; import '../../../services/get/theme_controller.dart'; class DistinguishPage extends StatefulWidget { const DistinguishPage({super.key}); @override State createState() => _DistinguishPageState(); } class _DistinguishPageState extends State { final ThemeController _themeController = Get.find(); List> _answerRecords = []; bool _isLoading = true; int _totalQuestions = 0; int _correctAnswers = 0; int _wrongAnswers = 0; double _correctRate = 0.0; double _wrongRate = 0.0; double _averageTime = 0.0; int _hintCount = 0; int _skipCount = 0; String _poetryLevel = '未知'; @override void initState() { super.initState(); _loadAnswerRecords(); } /// 加载答题记录 Future _loadAnswerRecords() async { try { // 获取答题记录列表 List records = await SharedPreferencesStorageController.getStringList( 'poetryAnswerRecords', defaultValue: [], ); // 解析记录 _answerRecords = records .map((record) { try { return jsonDecode(record) as Map; } catch (e) { return {}; } }) .where((record) => record.isNotEmpty) .toList(); // 按时间倒序排列 _answerRecords.sort((a, b) { final timeA = a['answerTime'] ?? ''; final timeB = b['answerTime'] ?? ''; return timeB.compareTo(timeA); }); // 加载统计数据 await _loadStatistics(); } catch (e) { // 加载失败 } finally { setState(() { _isLoading = false; }); } } /// 加载统计数据 Future _loadStatistics() async { try { _totalQuestions = await SharedPreferencesStorageController.getInt( 'totalQuestions', defaultValue: 0, ); _correctAnswers = await SharedPreferencesStorageController.getInt( 'correctAnswers', defaultValue: 0, ); _wrongAnswers = await SharedPreferencesStorageController.getInt( 'wrongAnswers', defaultValue: 0, ); int totalTime = await SharedPreferencesStorageController.getInt( 'totalTime', defaultValue: 0, ); _hintCount = await SharedPreferencesStorageController.getInt( 'hintCount', defaultValue: 0, ); _skipCount = await SharedPreferencesStorageController.getInt( 'skipCount', defaultValue: 0, ); // 计算正确率和错误率 if (_totalQuestions > 0) { _correctRate = (_correctAnswers / _totalQuestions) * 100; _wrongRate = (_wrongAnswers / _totalQuestions) * 100; _averageTime = totalTime / _totalQuestions; } // 计算诗词水平 _calculatePoetryLevel(); } catch (e) { // 加载失败 } } /// 计算诗词水平 void _calculatePoetryLevel() { if (_totalQuestions < 5) { _poetryLevel = '未知(答题数量不足)'; } else if (_correctRate >= 90) { _poetryLevel = '诗词大师 🏆'; } else if (_correctRate >= 70) { _poetryLevel = '诗词达人 ⭐'; } else if (_correctRate >= 50) { _poetryLevel = '诗词爱好者 📚'; } else { _poetryLevel = '诗词初学者 🌱'; } } void _showStatisticsDialog() { showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (context) => _buildStatisticsSheet(), ); } Widget _buildStatisticsSheet() { return Obx(() { final isDark = _themeController.isDarkMode; return Container( decoration: BoxDecoration( color: AppColors.surface, borderRadius: const BorderRadius.vertical(top: Radius.circular(24)), ), child: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Center( child: Container( width: 40, height: 4, decoration: BoxDecoration( color: AppColors.divider, borderRadius: BorderRadius.circular(2), ), ), ), const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: AppColors.primary.withAlpha(20), borderRadius: BorderRadius.circular(10), ), child: Icon( Icons.analytics_outlined, color: AppColors.primary, size: 24, ), ), const SizedBox(width: 12), Text( '本次答题记录', style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold, color: AppColors.primaryText, ), ), ], ), if (_answerRecords.isNotEmpty) Container( decoration: BoxDecoration( color: AppColors.primary.withAlpha(15), borderRadius: BorderRadius.circular(8), border: Border.all( color: AppColors.primary.withAlpha(50), width: 0.5, ), ), child: TextButton.icon( onPressed: _copyStatisticsContent, icon: Icon( Icons.copy, size: 16, color: Colors.orange[700], ), label: Text( '添加笔记', style: TextStyle( fontSize: 12, color: Colors.orange[700], ), ), style: TextButton.styleFrom( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 6, ), minimumSize: Size.zero, tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), ), ), ], ), const SizedBox(height: 24), Container( width: double.infinity, padding: const EdgeInsets.all(20), decoration: BoxDecoration( gradient: LinearGradient( colors: [ AppColors.primary.withAlpha(10), AppColors.surface, ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(16), border: Border.all( color: AppColors.primary.withAlpha(30), width: 1, ), ), child: Column( children: [ _buildStatRow('已答题', '$_totalQuestions 题'), _buildStatRow('正确', '$_correctAnswers 题', isGreen: true), _buildStatRow('错误', '$_wrongAnswers 题', isRed: true), _buildStatRow( '正确率', '${_correctRate.toStringAsFixed(1)}%', isGreen: _correctRate >= 60, ), _buildStatRow( '错误率', '${_wrongRate.toStringAsFixed(1)}%', isRed: _wrongRate > 40, ), _buildStatRow( '平均用时', '${_averageTime.toStringAsFixed(1)} 秒', ), _buildStatRow('提示次数', '$_hintCount 次'), _buildStatRow('跳过次数', '$_skipCount 次'), Divider(height: 24, color: AppColors.divider), _buildStatRow('诗词水平', _poetryLevel, isHighlight: true), ], ), ), const SizedBox(height: 24), SizedBox( width: double.infinity, child: Container( decoration: BoxDecoration( gradient: LinearGradient( colors: [ AppColors.primary, AppColors.primary.withAlpha(200), ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: AppColors.primary.withAlpha(80), blurRadius: 12, offset: const Offset(0, 4), ), ], ), child: ElevatedButton( onPressed: _copyStatisticsToClipboard, style: ElevatedButton.styleFrom( backgroundColor: Colors.transparent, shadowColor: Colors.transparent, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: const Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.copy, color: Colors.white, size: 20), SizedBox(width: 8), Text( '复制数据发送给AI评估', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white, ), ), ], ), ), ), ), const SizedBox(height: 16), ], ), ), ), ); }); } Widget _buildStatRow( String label, String value, { bool isGreen = false, bool isRed = false, bool isHighlight = false, }) { Color valueColor = AppColors.primaryText; if (isGreen) valueColor = AppColors.iosGreen; if (isRed) valueColor = AppColors.iosRed; if (isHighlight) valueColor = AppColors.primary; return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( label, style: TextStyle(fontSize: 15, color: AppColors.secondaryText), ), Text( value, style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600, color: valueColor, ), ), ], ), ); } /// 复制统计数据到剪贴板 Future _copyStatisticsToClipboard() async { String content = ''' 🎓 诗词答题记录评估报告 📊 基础数据统计 ━━━━━━━━━━━━━━━━ • 已答题数:$_totalQuestions 题 • 正确数量:$_correctAnswers 题 • 错误数量:$_wrongAnswers 题 • 正确率:${_correctRate.toStringAsFixed(1)}% • 错误率:${_wrongRate.toStringAsFixed(1)}% • 平均用时:${_averageTime.toStringAsFixed(1)} 秒/题 📈 辅助数据 ━━━━━━━━━━━━━━━━ • 提示次数:$_hintCount 次 • 跳过次数:$_skipCount 次 🏆 诗词水平评估 ━━━━━━━━━━━━━━━━ $_poetryLevel 💡 AI评估提示 ━━━━━━━━━━━━━━━━ 请根据以上答题数据,综合评估我的诗词水平,并给出: 1. 当前诗词水平的详细分析 2. 薄弱环节和改进建议 3. 推荐学习的诗词类型或朝代 4. 适合的诗词学习路径建议 感谢您的评估! '''; await Clipboard.setData(ClipboardData(text: content)); // 关闭弹窗 Navigator.pop(context); // 显示复制成功提示 ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('统计数据已复制,可发送给AI进行评估'), duration: Duration(seconds: 2), behavior: SnackBarBehavior.floating, ), ); } /// 清空答题记录 Future _clearRecords() async { final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( backgroundColor: AppColors.surface, title: Text('确认清空', style: TextStyle(color: AppColors.primaryText)), content: Text( '确定要清空所有答题记录吗?此操作不可恢复。', style: TextStyle(color: AppColors.secondaryText), ), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: Text('取消', style: TextStyle(color: AppColors.primary)), ), TextButton( onPressed: () => Navigator.pop(context, true), child: Text('确定', style: TextStyle(color: AppColors.iosRed)), ), ], ), ); if (confirmed == true) { try { await SharedPreferencesStorageController.setStringList( 'poetryAnswerRecords', [], ); setState(() { _answerRecords = []; }); ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('答题记录已清空'))); } catch (e) { // 清空失败 } } } /// 格式化时间 String _formatTime(String? isoTime) { if (isoTime == null || isoTime.isEmpty) return '未知时间'; try { final dateTime = DateTime.parse(isoTime); return DateFormat('MM-dd HH:mm').format(dateTime); } catch (e) { return '未知时间'; } } @override Widget build(BuildContext context) { return Obx(() { final isDark = _themeController.isDarkMode; final primaryColor = ThemeColors.getThemeColor( _themeController.themeColorIndexRx.value, ); return Scaffold( appBar: AppBar( title: Text( '答题记录', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 20, color: Colors.white, ), ), backgroundColor: primaryColor, foregroundColor: Colors.white, elevation: 0, flexibleSpace: Container( decoration: BoxDecoration( gradient: LinearGradient( colors: [primaryColor, primaryColor.withAlpha(180)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), ), ), actions: [ IconButton( onPressed: _showStatisticsDialog, icon: const Icon(Icons.analytics_outlined), tooltip: '查看统计', ), if (_answerRecords.isNotEmpty) IconButton( onPressed: _clearRecords, icon: const Icon(Icons.delete_outline), tooltip: '清空记录', ), ], ), body: Container( color: isDark ? const Color(0xFF1A1A1A) : AppColors.background, child: SafeArea( child: _isLoading ? const Center(child: CircularProgressIndicator()) : _answerRecords.isEmpty ? _buildEmptyView() : _buildRecordList(), ), ), ); }); } Widget _buildEmptyView() { return Obx(() { final isDark = _themeController.isDarkMode; return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.menu_book_outlined, size: 80, color: AppColors.tertiaryText, ), const SizedBox(height: 16), Text( '暂无答题记录', style: TextStyle( fontSize: 18, color: AppColors.secondaryText, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 8), Text( '快去答题吧!', style: TextStyle(fontSize: 14, color: AppColors.tertiaryText), ), ], ), ); }); } Widget _buildRecordList() { return Obx(() { final isDark = _themeController.isDarkMode; final primaryColor = ThemeColors.getThemeColor( _themeController.themeColorIndexRx.value, ); return ListView.builder( padding: const EdgeInsets.all(16), itemCount: _answerRecords.length, itemBuilder: (context, index) { final record = _answerRecords[index]; return _buildRecordCard(record, index, primaryColor, isDark); }, ); }); } Widget _buildRecordCard( Map record, int index, Color primaryColor, bool isDark, ) { final question = record['question'] ?? '未知题目'; final author = record['author'] ?? '未知作者'; final tags = (record['tags'] as List?)?.cast() ?? []; final isCorrect = record['isCorrect'] ?? false; final answerTime = _formatTime(record['answerTime']); return Container( margin: const EdgeInsets.only(bottom: 12), decoration: BoxDecoration( color: isDark ? const Color(0xFF2A2A2A) : AppColors.surface, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.black.withAlpha(isDark ? 30 : 10), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: 28, height: 28, decoration: BoxDecoration( color: primaryColor.withAlpha(20), borderRadius: BorderRadius.circular(8), ), child: Center( child: Text( '${index + 1}', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: primaryColor, ), ), ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( question, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: isDark ? Colors.grey[300] : AppColors.primaryText, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), Text( '—— $author', style: TextStyle( fontSize: 12, color: isDark ? Colors.grey[500] : AppColors.secondaryText, fontStyle: FontStyle.italic, ), ), ], ), ), const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 4, ), decoration: BoxDecoration( color: isCorrect ? AppColors.iosGreen.withAlpha(20) : AppColors.iosRed.withAlpha(20), borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( isCorrect ? Icons.check_circle : Icons.cancel, size: 14, color: isCorrect ? AppColors.iosGreen : AppColors.iosRed, ), const SizedBox(width: 4), Text( isCorrect ? '答对' : '答错', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: isCorrect ? AppColors.iosGreen : AppColors.iosRed, ), ), ], ), ), ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: tags.isNotEmpty ? Wrap( spacing: 6, runSpacing: 6, children: tags.map((tag) { return Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 3, ), decoration: BoxDecoration( color: primaryColor.withAlpha(15), borderRadius: BorderRadius.circular(6), border: Border.all( color: primaryColor.withAlpha(50), width: 0.5, ), ), child: Text( tag, style: TextStyle( fontSize: 11, color: primaryColor, fontWeight: FontWeight.w500, ), ), ); }).toList(), ) : Text( '暂无标签', style: TextStyle( fontSize: 12, color: isDark ? Colors.grey[500] : AppColors.tertiaryText, fontStyle: FontStyle.italic, ), ), ), Row( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.access_time, size: 12, color: isDark ? Colors.grey[500] : AppColors.tertiaryText, ), const SizedBox(width: 4), Text( answerTime, style: TextStyle( fontSize: 12, color: isDark ? Colors.grey[400] : AppColors.secondaryText, ), ), ], ), ], ), ], ), ), ); } // 写入统计数据到笔记 Future _copyStatisticsContent() async { try { // 生成完整的评估报告内容 final StringBuffer content = StringBuffer(); content.writeln('🎓 诗词答题记录评估报告'); content.writeln(''); content.writeln('📊 基础数据统计'); content.writeln('━━━━━━━━━━━━━━━━'); content.writeln('• 已答题数:$_totalQuestions 题'); content.writeln('• 正确数量:$_correctAnswers 题'); content.writeln('• 错误数量:$_wrongAnswers 题'); content.writeln('• 正确率:${_correctRate.toStringAsFixed(1)}%'); content.writeln('• 错误率:${_wrongRate.toStringAsFixed(1)}%'); content.writeln('• 平均用时:${_averageTime.toStringAsFixed(1)} 秒/题'); content.writeln(''); content.writeln('📈 辅助数据'); content.writeln('━━━━━━━━━━━━━━━━'); content.writeln('• 提示次数:$_hintCount 次'); content.writeln('• 跳过次数:$_skipCount 次'); content.writeln(''); content.writeln('🏆 诗词水平评估'); content.writeln('━━━━━━━━━━━━━━━━'); content.writeln('$_poetryLevel'); content.writeln(''); content.writeln('💡 AI评估提示'); content.writeln('━━━━━━━━━━━━━━━━'); content.writeln('请根据以上答题数据,综合评估我的诗词水平,并给出:'); content.writeln('1. 当前诗词水平的详细分析'); content.writeln('2. 薄弱环节和改进建议'); content.writeln('3. 推荐学习的诗词类型或朝代'); content.writeln('4. 适合的诗词学习路径建议'); content.writeln(''); content.writeln('感谢您的评估!'); // 保存到笔记 final noteId = await HistoryController.saveNote( title: '诗词答题评估_${DateFormat('yyyyMMdd_HHmmss').format(DateTime.now())}', content: content.toString(), category: '答题评估', ); if (noteId != null) { NetworkListenerService().sendSuccessEvent( NetworkEventType.noteUpdate, data: noteId, ); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('评估报告已保存到笔记'), duration: Duration(seconds: 2), ), ); } } } catch (e) { debugPrint('保存笔记失败: $e'); if (mounted) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('保存笔记失败: $e'))); } } } }