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/utils/img_url.dart'; /// 科目计算状态(对标 Vue SubjectStatus) enum ComputationSubjectStatus { notStarted, inProgress, finished } /// ============================================================ /// 【ComputationSubjectItem】AI 计算科目卡片 /// —— 对标 reportComputation.vue 的 .subject-item /// /// 右上角标(subscript):未开始/进行中蓝色闪电底,已完成绿色对勾底; /// 标题行:科目名 + 科目图标 + ai-plan「AI学习计划+」图; /// 知识点行 + 底部渐变进度条(进行中蓝 / 已完成绿)。 /// ============================================================ class ComputationSubjectItem extends StatelessWidget { const ComputationSubjectItem({ super.key, required this.subjectName, required this.iconUrl, required this.knowCount, required this.progress, required this.status, }); final String subjectName; final String iconUrl; final int knowCount; /// 0-100 final int progress; final ComputationSubjectStatus status; // ---- 图片资源(default.json ai-plan + Vue 内联 $img 角标图)---- static const String _aiPlanImg = '/StaticResource/2026-06-11/aiPlan1781157211012.png'; static const String _subscriptBgBlue = '/StaticResource/2026-06-11/subscript-bg11781162055363.png'; static const String _subscriptBgGreen = '/StaticResource/2026-06-11/subscript-bg21781162055358.png'; static const String _iconFlash = '/StaticResource/2026-06-11/subscript-cion1-1781162401643.png'; static const String _iconCheck = '/StaticResource/2026-06-11/subscript-cion2-1781162401644.png'; bool get _isFinished => status == ComputationSubjectStatus.finished; @override Widget build(BuildContext context) { return Opacity( // Vue: 未开始 opacity 0.8 opacity: status == ComputationSubjectStatus.notStarted ? 0.8 : 1, child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(16.r), // 16rpx gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, stops: const [0, 0.3, 1], colors: _isFinished ? const [Color(0xFFE3FCF4), Color(0xFFF7FCFA), Colors.white] : const [Color(0xFFE3F7FC), Color(0xFFF7FBFC), Colors.white], ), ), child: Stack( clipBehavior: Clip.none, children: [ Padding( padding: EdgeInsets.fromLTRB(14.w, 14.w, 14.w, 20.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildTitle(), SizedBox(height: 16.h), // 知识点行 margin 32rpx 0 _buildKnowledge(), SizedBox(height: 16.h), _buildProgressBar(), ], ), ), // 右上角标 Positioned(top: 0, right: 0, child: _buildSubscript()), ], ), ), ); } // ===== 标题行:科目名 + 科目图标 + AI学习计划图 ===== Widget _buildTitle() { return Row( children: [ Text( subjectName, style: TextStyle( fontSize: 18.spMin, // 36rpx fontWeight: FontWeight.w600, color: const Color(0xFF1D2B47), ), ), SizedBox(width: 6.w), if (iconUrl.isNotEmpty) CachedNetworkImage( imageUrl: ImgUrl.build(iconUrl), width: 26.w, // 52rpx,widthFix fit: BoxFit.fitWidth, errorWidget: (_, _, _) => Icon( Icons.menu_book, size: 26.w, color: const Color(0xFF3D7DF5), ), ) else Icon(Icons.menu_book, size: 26.w, color: const Color(0xFF3D7DF5)), SizedBox(width: 6.w), CachedNetworkImage( imageUrl: ImgUrl.build(_aiPlanImg), width: 77.w, // 154rpx,widthFix fit: BoxFit.fitWidth, errorWidget: (_, _, _) => const SizedBox.shrink(), ), ], ); } // ===== 知识点行(Vue 预计时间已注释,只保留知识点)===== Widget _buildKnowledge() { return Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 6.h), decoration: BoxDecoration( color: const Color(0xFFF3F8FE), borderRadius: BorderRadius.circular(4.r), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( '知识点', style: TextStyle( fontSize: 12.spMin, color: const Color(0xFF495A7A), ), ), SizedBox(width: 6.w), Container( padding: EdgeInsets.symmetric(horizontal: 3.5.w), decoration: BoxDecoration( color: const Color(0xFFE1EAFA), borderRadius: BorderRadius.circular(4.r), ), child: Text( '$knowCount', style: TextStyle( fontSize: 12.spMin, color: const Color(0xFF495A7A), ), ), ), ], ), ); } // ===== 底部进度条:蓝渐变 / 绿渐变 ===== Widget _buildProgressBar() { return SizedBox( height: 6.h, // 12rpx child: LayoutBuilder( builder: (_, constraints) { final fillWidth = constraints.maxWidth * (progress.clamp(0, 100) / 100); return Stack( children: [ Positioned.fill( child: DecoratedBox( decoration: BoxDecoration( color: const Color(0xFFE9EDF5), borderRadius: BorderRadius.circular(3.h), ), ), ), Positioned( left: 0, top: 0, bottom: 0, child: Container( width: fillWidth, decoration: BoxDecoration( borderRadius: BorderRadius.circular(3.h), gradient: LinearGradient( colors: _isFinished ? const [Color(0xFFB4EADA), Color(0xFF19CE92)] : const [ Color(0xFF8AD1FD), Color(0xFF3C84FF), Color(0xFF3D7DF5), ], stops: _isFinished ? null : const [0, 0.64, 1], ), ), ), ), ], ); }, ), ); } // ===== 右上角标:162x66rpx 背景图 + 状态图标 + 百分比 ===== Widget _buildSubscript() { final bgImg = _isFinished ? _subscriptBgGreen : _subscriptBgBlue; return SizedBox( width: 81.w, height: 33.h, child: Stack( children: [ Positioned.fill( child: CachedNetworkImage( imageUrl: ImgUrl.build(bgImg), fit: BoxFit.fill, errorWidget: (_, _, _) => DecoratedBox( decoration: BoxDecoration( color: _isFinished ? const Color(0xFF19CE92) : const Color(0xFF3D7DF5), borderRadius: BorderRadius.only( topRight: Radius.circular(8.r), ), ), ), ), ), Padding( padding: EdgeInsets.only(bottom: 5.h), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ if (status != ComputationSubjectStatus.notStarted) Padding( padding: EdgeInsets.only(right: 4.w), child: CachedNetworkImage( imageUrl: ImgUrl.build( _isFinished ? _iconCheck : _iconFlash, ), width: 18.w, height: 18.w, errorWidget: (_, _, _) => SizedBox(width: 18.w, height: 18.w), ), ), Text( '$progress%', style: TextStyle( fontSize: 16.spMin, // 32rpx color: Colors.white, ), ), ], ), ), ], ), ); } }