import 'package:flutter/material.dart'; import '../models/reading_article.dart'; /// 阅读文章卡片组件 class ReadingArticleCard extends StatelessWidget { final ReadingArticle article; final VoidCallback? onTap; final VoidCallback? onFavorite; final bool showProgress; const ReadingArticleCard({ super.key, required this.article, this.onTap, this.onFavorite, this.showProgress = false, }); @override Widget build(BuildContext context) { return Card( margin: const EdgeInsets.only(bottom: 16), elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(12), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 标题和收藏按钮 Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Text( article.title, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black87, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ), if (onFavorite != null) IconButton( icon: const Icon( Icons.favorite_border, color: Colors.grey, size: 20, ), onPressed: onFavorite, padding: EdgeInsets.zero, constraints: const BoxConstraints( minWidth: 24, minHeight: 24, ), ), ], ), const SizedBox(height: 8), // 文章摘要 if (article.content.isNotEmpty) Text( _getExcerpt(article.content), style: TextStyle( fontSize: 14, color: Colors.grey[600], height: 1.4, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 12), // 标签行 Row( children: [ // 分类标签 Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), decoration: BoxDecoration( color: const Color(0xFF2196F3).withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Text( article.categoryLabel, style: const TextStyle( fontSize: 12, color: Color(0xFF2196F3), fontWeight: FontWeight.w500, ), ), ), const SizedBox(width: 8), // 难度标签 Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), decoration: BoxDecoration( color: _getDifficultyColor(article.difficulty).withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Text( article.difficultyLabel, style: TextStyle( fontSize: 12, color: _getDifficultyColor(article.difficulty), fontWeight: FontWeight.w500, ), ), ), const Spacer(), // 字数和阅读时间 Text( '${article.wordCount}词', style: TextStyle( fontSize: 12, color: Colors.grey[500], ), ), const SizedBox(width: 8), Icon( Icons.schedule, size: 12, color: Colors.grey[500], ), const SizedBox(width: 2), Text( '${article.readingTime}分钟', style: TextStyle( fontSize: 12, color: Colors.grey[500], ), ), ], ), // 进度条(如果需要显示) if (showProgress && article.isCompleted) Padding( padding: const EdgeInsets.only(top: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '已完成', style: TextStyle( fontSize: 12, color: Colors.grey[600], ), ), if (article.comprehensionScore != null) Text( '得分: ${article.comprehensionScore}分', style: TextStyle( fontSize: 12, color: _getScoreColor(article.comprehensionScore!.toInt()), fontWeight: FontWeight.w500, ), ), ], ), const SizedBox(height: 4), LinearProgressIndicator( value: 1.0, backgroundColor: Colors.grey[200], valueColor: AlwaysStoppedAnimation( _getScoreColor((article.comprehensionScore ?? 0).toInt()), ), ), ], ), ), // 标签(如果有) if (article.tags.isNotEmpty) Padding( padding: const EdgeInsets.only(top: 8), child: Wrap( spacing: 6, runSpacing: 4, children: article.tags.take(3).map((tag) { return Container( padding: const EdgeInsets.symmetric( horizontal: 6, vertical: 2, ), decoration: BoxDecoration( color: Colors.grey[100], borderRadius: BorderRadius.circular(8), ), child: Text( '#$tag', style: TextStyle( fontSize: 10, color: Colors.grey[600], ), ), ); }).toList(), ), ), ], ), ), ), ); } /// 获取文章摘要 String _getExcerpt(String content) { // 移除HTML标签和多余空白 String cleanContent = content .replaceAll(RegExp(r'<[^>]*>'), '') .replaceAll(RegExp(r'\s+'), ' ') .trim(); // 截取前100个字符作为摘要 if (cleanContent.length <= 100) { return cleanContent; } return '${cleanContent.substring(0, 100)}...'; } /// 获取难度颜色 Color _getDifficultyColor(String difficulty) { switch (difficulty.toLowerCase()) { case 'a1': case 'a2': return Colors.green; case 'b1': case 'b2': return Colors.orange; case 'c1': case 'c2': return Colors.red; default: return Colors.grey; } } /// 获取分数颜色 Color _getScoreColor(int score) { if (score >= 90) { return Colors.green; } else if (score >= 70) { return Colors.orange; } else { return Colors.red; } } }