import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; import '../../../constants/app_constants.dart'; import '../../../controllers/sqlite_storage_controller.dart'; import '../../../controllers/history_controller.dart'; import '../../../services/network_listener_service.dart'; /// 时间: 2026-03-28 /// 功能: 答题记录页面 /// 介绍: 显示用户的诗词答题记录列表,包括题目、标签、是否答对等信息 /// 最新变化: 添加统计数据弹窗功能 class DistinguishPage extends StatefulWidget { const DistinguishPage({super.key}); @override State createState() => _DistinguishPageState(); } class _DistinguishPageState extends State { // 答题记录列表 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 SQLiteStorageController.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 SQLiteStorageController.getInt( 'totalQuestions', defaultValue: 0, ); _correctAnswers = await SQLiteStorageController.getInt( 'correctAnswers', defaultValue: 0, ); _wrongAnswers = await SQLiteStorageController.getInt( 'wrongAnswers', defaultValue: 0, ); int totalTime = await SQLiteStorageController.getInt( 'totalTime', defaultValue: 0, ); _hintCount = await SQLiteStorageController.getInt( 'hintCount', defaultValue: 0, ); _skipCount = await SQLiteStorageController.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 Container( decoration: const BoxDecoration( color: Colors.white, borderRadius: 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: Colors.grey[300], borderRadius: BorderRadius.circular(2), ), ), ), const SizedBox(height: 20), // 标题 Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: AppConstants.primaryColor.withAlpha(20), borderRadius: BorderRadius.circular(10), ), child: Icon( Icons.analytics_outlined, color: AppConstants.primaryColor, size: 24, ), ), const SizedBox(width: 12), const Text( '本次答题记录', style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold, color: Colors.black87, ), ), ], ), // 复制内容按钮 if (_answerRecords.isNotEmpty) Container( decoration: BoxDecoration( color: AppConstants.primaryColor.withAlpha(15), borderRadius: BorderRadius.circular(8), border: Border.all( color: AppConstants.primaryColor.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: [ AppConstants.primaryColor.withAlpha(10), Colors.white, ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(16), border: Border.all( color: AppConstants.primaryColor.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 次'), const Divider(height: 24), _buildStatRow('诗词水平', _poetryLevel, isHighlight: true), ], ), ), const SizedBox(height: 24), // 复制按钮 SizedBox( width: double.infinity, child: Container( decoration: BoxDecoration( gradient: LinearGradient( colors: [ AppConstants.primaryColor, AppConstants.primaryColor.withAlpha(200), ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: AppConstants.primaryColor.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 = Colors.black87; if (isGreen) valueColor = Colors.green; if (isRed) valueColor = Colors.red; if (isHighlight) valueColor = AppConstants.primaryColor; return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(label, style: TextStyle(fontSize: 15, color: Colors.grey[700])), 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( title: const Text('确认清空'), content: const Text('确定要清空所有答题记录吗?此操作不可恢复。'), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('取消'), ), TextButton( onPressed: () => Navigator.pop(context, true), child: const Text('确定', style: TextStyle(color: Colors.red)), ), ], ), ); if (confirmed == true) { try { await SQLiteStorageController.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 Scaffold( appBar: AppBar( title: const Text( '答题记录', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20), ), backgroundColor: AppConstants.primaryColor, foregroundColor: Colors.white, elevation: 0, flexibleSpace: Container( decoration: BoxDecoration( gradient: LinearGradient( colors: [ AppConstants.primaryColor, AppConstants.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: Colors.grey[50], child: SafeArea( child: _isLoading ? const Center(child: CircularProgressIndicator()) : _answerRecords.isEmpty ? _buildEmptyView() : _buildRecordList(), ), ), ); } /// 构建空记录视图 Widget _buildEmptyView() { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.menu_book_outlined, size: 80, color: Colors.grey[300]), const SizedBox(height: 16), Text( '暂无答题记录', style: TextStyle( fontSize: 18, color: Colors.grey[500], fontWeight: FontWeight.w500, ), ), const SizedBox(height: 8), Text( '快去答题吧!', style: TextStyle(fontSize: 14, color: Colors.grey[400]), ), ], ), ); } /// 构建记录列表 Widget _buildRecordList() { return ListView.builder( padding: const EdgeInsets.all(16), itemCount: _answerRecords.length, itemBuilder: (context, index) { final record = _answerRecords[index]; return _buildRecordCard(record, index); }, ); } /// 构建记录卡片 Widget _buildRecordCard(Map record, int index) { 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: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.black.withAlpha(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: AppConstants.primaryColor.withAlpha(20), borderRadius: BorderRadius.circular(8), ), child: Center( child: Text( '${index + 1}', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: AppConstants.primaryColor, ), ), ), ), const SizedBox(width: 12), // 题目 Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( question, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.black87, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), Text( '—— $author', style: TextStyle( fontSize: 12, color: Colors.grey[600], fontStyle: FontStyle.italic, ), ), ], ), ), const SizedBox(width: 8), // 答对/答错标识 Container( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 4, ), decoration: BoxDecoration( color: isCorrect ? Colors.green.withAlpha(20) : Colors.red.withAlpha(20), borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( isCorrect ? Icons.check_circle : Icons.cancel, size: 14, color: isCorrect ? Colors.green : Colors.red, ), const SizedBox(width: 4), Text( isCorrect ? '答对' : '答错', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: isCorrect ? Colors.green : Colors.red, ), ), ], ), ), ], ), 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: AppConstants.primaryColor.withAlpha(15), borderRadius: BorderRadius.circular(6), border: Border.all( color: AppConstants.primaryColor.withAlpha( 50, ), width: 0.5, ), ), child: Text( tag, style: TextStyle( fontSize: 11, color: AppConstants.primaryColor, fontWeight: FontWeight.w500, ), ), ); }).toList(), ) : Text( '暂无标签', style: TextStyle( fontSize: 12, color: Colors.grey[400], fontStyle: FontStyle.italic, ), ), ), // 时间 Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.access_time, size: 12, color: Colors.grey[400]), const SizedBox(width: 4), Text( answerTime, style: TextStyle(fontSize: 12, color: Colors.grey[500]), ), ], ), ], ), ], ), ), ); } // 写入统计数据到笔记 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'))); } } } // 从答题记录创建笔记 Future _createNoteFromRecords() async { try { // 生成答题记录的详细内容 final StringBuffer content = StringBuffer(); content.writeln('## 答题记录汇总'); content.writeln( '生成时间: ${DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now())}', ); content.writeln('总记录数: ${_answerRecords.length}'); content.writeln(''); // 统计信息 int correctCount = 0; int totalCount = _answerRecords.length; for (final record in _answerRecords) { if (record['isCorrect'] == true) { correctCount++; } } content.writeln('### 统计信息'); content.writeln('- 总题数: $totalCount'); content.writeln('- 答对数: $correctCount'); content.writeln('- 答错数: ${totalCount - correctCount}'); content.writeln( '- 正确率: ${totalCount > 0 ? (correctCount / totalCount * 100).toStringAsFixed(1) : 0}%', ); content.writeln(''); content.writeln('### 详细记录'); content.writeln(''); for (int i = 0; i < _answerRecords.length; i++) { final record = _answerRecords[i]; final question = record['question'] ?? '未知题目'; final userAnswer = record['userAnswer'] ?? '未知答案'; final correctAnswer = record['correctAnswer'] ?? '未知答案'; final isCorrect = record['isCorrect'] == true; final answerTime = record['answerTime'] ?? '未知时间'; final tags = record['tags'] as List? ?? []; content.writeln('#### ${i + 1}. $question'); content.writeln('- **你的答案**: $userAnswer'); content.writeln('- **正确答案**: $correctAnswer'); content.writeln('- **答题结果**: ${isCorrect ? '✅ 正确' : '❌ 错误'}'); content.writeln('- **答题时间**: ${_formatTime(answerTime)}'); if (tags.isNotEmpty) { content.writeln('- **标签**: ${tags.join(', ')}'); } 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('答题记录已保存到笔记'))); } } } catch (e) { debugPrint('创建笔记失败: $e'); if (mounted) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('创建笔记失败: $e'))); } } } }