/// ============================================================ /// 【ObjectiveAnswerView】客观题答案对比卡片 /// (1:1 迁移 Vue components/AnswerView.vue 的 type == 'objective' 分支; /// 本页只使用客观题形态) /// /// 视觉: /// 218rpx 卡片:上行作答状态图标+文案(10 正确 / 11 错误 / 12 部分正确 / /// 0 未作答),下行白色对比块:"正确答案:X / 你的答案:Y" /// ============================================================ library; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:new_learn/core/components/rich_text_content.dart'; import 'package:new_learn/core/utils/img_url.dart'; import 'package:new_learn/core/utils/question/question_parser.dart'; import 'exercise_styles.dart'; class ObjectiveAnswerView extends StatelessWidget { const ObjectiveAnswerView({ super.key, required this.answerStatus, this.userAnswer = '', this.correctAnswer = '', }); /// 0 未作答 / 10 正确 / 11 错误 / 12 部分正确 final dynamic answerStatus; final String userAnswer; final String correctAnswer; int get _status => int.tryParse('$answerStatus') ?? 0; bool get _isCorrect => _status == 10; /// 客观题文字展示清洗(对标 formatOptionText),仅用于判空与纯文本展示 String _formatOptionText(dynamic ans) { if (ans == null) return ''; String str; if (ans is List) { str = ans.join(); } else if (ans is Map) { str = ans.toString(); if (str == '{}' || str == '[]') return ''; } else { str = '$ans'; } final trimmed = str.trim(); if (trimmed.isEmpty || trimmed == '[]' || trimmed == '{}' || trimmed == '""' || trimmed == "''") { return ''; } var result = trimmed.replaceAll(RegExp(r'<[^>]+>'), ''); result = result.replaceAll(' ', ' '); result = result.replaceAll(RegExp(r'&[a-z]+;'), ''); result = result.replaceAll(RegExp(r'\s+'), ' '); result = result.replaceAll(RegExp(r'^(\d+[\.\s、)\s]+|第\d+题)'), ''); result = result.replaceAll(',', ''); return result.trim(); } /// 清洗后的答案 HTML(富文本渲染用) String _rendered(String html) => cleanRichText(html); bool _hasFormula(String html) => RegExp(r'math-formula|katex').hasMatch(html); @override Widget build(BuildContext context) { final correctText = _formatOptionText(correctAnswer); final userText = _formatOptionText(userAnswer); final correctDisplay = correctText.isEmpty ? '无' : correctText; final userDisplay = userText.isEmpty ? '未作答' : userText; final statusText = _status == 10 ? '回答正确' : _status == 11 ? '回答错误' : _status == 12 ? '部分正确' : '未作答'; final accent = _isCorrect ? ExerciseColors.correct : ExerciseColors.error; final correctHtml = _rendered(correctAnswer); final userHtml = _rendered(userAnswer); return Container( height: 109.h, // 218rpx decoration: BoxDecoration( // CSS 外层:实色 padding-box + 180deg 渐变 border-box gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: _isCorrect ? const [Color(0x8000CC96), Color(0x1A00CC96), Color(0x8000CC96)] : const [Color(0x80FF4C4C), Color(0x1AFF4C4C), Color(0x80FF4C4C)], ), borderRadius: BorderRadius.circular(12.r), ), child: Container( margin: EdgeInsets.all(1.w), decoration: BoxDecoration( color: _isCorrect ? const Color(0xFFEBFFFA) : const Color(0xFFFFF0F0), borderRadius: BorderRadius.circular(11.r), ), child: Column( children: [ // 第一行:状态提示 Padding( padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 14.h), child: Row( children: [ CachedNetworkImage( imageUrl: ImgUrl.build( _isCorrect ? '/StaticResource/2026-06-01/correct-icon1780284329572.png' : '/StaticResource/2026-06-01/error-icon1780284292405.png', ), width: 16.w, height: 16.w, ), SizedBox(width: 6.w), Text( statusText, style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, color: accent, ), ), ], ), ), // 第二行:答案对比 Expanded( child: Container( margin: EdgeInsets.fromLTRB(1.w, 0, 1.w, 1.w), padding: EdgeInsets.fromLTRB(14.w, 0, 26.w, 0), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10.r), ), child: Row( children: [ Expanded( child: _CompareItem( label: '正确答案:', child: _hasFormula(correctHtml) ? RichTextContent( html: correctHtml, textStyle: TextStyle( fontSize: 14.sp, color: const Color(0xFF333333), fontWeight: FontWeight.bold, ), ) : Text( correctDisplay, style: TextStyle( fontSize: 14.sp, color: const Color(0xFF333333), fontWeight: FontWeight.bold, ), ), ), ), Expanded( child: _CompareItem( alignEnd: true, label: '你的答案:', child: _hasFormula(userHtml) ? RichTextContent( html: userHtml, textStyle: TextStyle( fontSize: 14.sp, color: _isCorrect ? ExerciseColors.correct : (userText.isEmpty ? const Color(0xFF999999) : ExerciseColors.error), fontWeight: FontWeight.bold, ), ) : Text( userDisplay, style: TextStyle( fontSize: 14.sp, color: _isCorrect ? ExerciseColors.correct : (userText.isEmpty ? const Color(0xFF999999) : ExerciseColors.error), fontWeight: FontWeight.bold, ), ), ), ), ], ), ), ), ], ), ), ); } } class _CompareItem extends StatelessWidget { const _CompareItem({ required this.label, required this.child, this.alignEnd = false, }); final String label; final Widget child; final bool alignEnd; @override Widget build(BuildContext context) { final row = Row( mainAxisSize: MainAxisSize.min, children: [ Flexible( child: Text( label, style: TextStyle(fontSize: 14.sp, color: ExerciseColors.subtitle), ), ), Flexible(child: child), ], ); return alignEnd ? Align(alignment: Alignment.centerRight, child: row) : Align(alignment: Alignment.centerLeft, child: row); } }