diff --git a/assets/img/class/comment-icon.png b/assets/img/class/comment-icon.png new file mode 100644 index 0000000..77c1e6b Binary files /dev/null and b/assets/img/class/comment-icon.png differ diff --git a/assets/img/class/like-check-icon.png b/assets/img/class/like-check-icon.png new file mode 100644 index 0000000..774a835 Binary files /dev/null and b/assets/img/class/like-check-icon.png differ diff --git a/assets/img/class/like-icon.png b/assets/img/class/like-icon.png new file mode 100644 index 0000000..e3fd7c9 Binary files /dev/null and b/assets/img/class/like-icon.png differ diff --git a/lib/core/components/resolve_problem_box.dart b/lib/core/components/resolve_problem_box.dart new file mode 100644 index 0000000..6fa9a97 --- /dev/null +++ b/lib/core/components/resolve_problem_box.dart @@ -0,0 +1,117 @@ +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'; +import 'package:new_learn/core/utils/screen_adapt_template.dart'; + +/// ============================================================ +/// 【ResolveProblemBox】攻克任务卡(对标 Vue ResolveProblem.vue) +/// +/// 用于展示"刚刚攻克了 「xxx」 的所有高难考点"类提示。 +/// 由 class_update_widgets.dart 抽离,详情页也可复用。 +/// 背景图 + 云朵装饰走 CDN,与 Vue 版 $img('/StaticResource/...') 同源。 +/// ============================================================ +class ResolveProblemBox extends StatelessWidget { + const ResolveProblemBox({ + super.key, + this.title = '刚刚攻克了', + required this.desc, + }); + + /// 标题(默认 "刚刚攻克了") + final String title; + + /// 攻克的知识点描述(如 "二次函数最值问题") + final String desc; + + @override + Widget build(BuildContext context) { + final isPad = MediaQuery.sizeOf(context).width >= AppAdapt.tabletBreakpoint; + + final bgUrl = ImgUrl.build( + isPad + ? '/StaticResource/2026-06-02/resolve-bg2-pad1780388446390.png' + : '/StaticResource/2026-05-07/resolve-bg2.png', + ); + final cloudUrl = ImgUrl.build( + isPad + ? '/StaticResource/2026-06-02/cloud-pad1780388443051.png' + : '/StaticResource/2026-05-07/cloud.png', + ); + + final cloudHeight = isPad ? 107.h : 74.h; + + return Container( + width: double.infinity, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.r), + image: DecorationImage( + image: CachedNetworkImageProvider(bgUrl), + fit: BoxFit.fill, + alignment: Alignment.topCenter, + ), + ), + // 裁剪所有子元素到圆角内(对标 Vue overflow:hidden) + clipBehavior: Clip.antiAlias, + child: Stack( + // Stack 默认不裁剪 Positioned 子元素,必须显式设置 + clipBehavior: Clip.hardEdge, + children: [ + // 底部云朵装饰(贴底部,z-index 1) + Positioned( + left: 0, + right: 0, + bottom: 0, + child: CachedNetworkImage( + imageUrl: cloudUrl, + height: cloudHeight, + width: double.infinity, + fit: BoxFit.fitWidth, + ), + ), + // 文本层(padding 在云朵之上,z-index 2) + Padding( + padding: EdgeInsets.fromLTRB(19.w, 25.h, 19.w, 11.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + title, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w900, + color: Color(0xFF1D2B47), + ), + ), + SizedBox(height: 3.h), + Text.rich( + TextSpan( + children: [ + TextSpan( + text: '「$desc」', + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + color: Color(0xFF1D2B47), + ), + ), + const TextSpan( + text: ' 的所有高难考点!', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + color: Color(0xFF45597E), + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/class/widgets/class_update_widgets.dart b/lib/features/class/widgets/class_update_widgets.dart index 649c1cf..696fae0 100644 --- a/lib/features/class/widgets/class_update_widgets.dart +++ b/lib/features/class/widgets/class_update_widgets.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:new_learn/core/api/class/model.dart'; import 'package:new_learn/core/components/image_preview_viewer.dart'; +import 'package:new_learn/core/components/resolve_problem_box.dart'; import 'package:new_learn/core/components/user_circle_avatar.dart'; import 'package:new_learn/core/utils/img_url.dart'; @@ -12,7 +13,8 @@ import 'package:new_learn/core/utils/img_url.dart'; /// 【班级动态组件】由 class_update_view.dart 抽离的展示组件 /// /// PostCard(动态卡片,按 type 分发内容 + 相册 + 点赞评论栏) -/// CardHeader / SubjectTag / ResolveProblemBox / AlbumDesc / AlbumGrid +/// CardHeader / SubjectTag / AlbumDesc / AlbumGrid / LikeCommentBar / ... +/// ResolveProblemBox 已抽离到 core/components/resolve_problem_box.dart /// LikeCommentBar / MetaButton / CommentPreview / CommentInput /// /// 原为 class_update_view.dart 内的私有组件,重命名去下划线后放到本文件。 @@ -263,65 +265,6 @@ class SubjectTag extends StatelessWidget { } } -/// 攻克任务卡(对标 ResolveProblem) -class ResolveProblemBox extends StatelessWidget { - const ResolveProblemBox({super.key, required this.desc}); - - final String desc; - - @override - Widget build(BuildContext context) { - return Container( - width: double.infinity, - padding: EdgeInsets.fromLTRB(19.w, 25.h, 19.w, 11.h), - decoration: BoxDecoration( - gradient: const LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [Color(0xFFEAF4FF), Color(0xFFFFFFFF)], - ), - borderRadius: BorderRadius.circular(10.r), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - '刚刚攻克了', - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.w900, - color: Color(0xFF1D2B47), - ), - ), - SizedBox(height: 3.h), - Text.rich( - TextSpan( - children: [ - TextSpan( - text: '「$desc」', - style: const TextStyle( - fontSize: 13, - fontWeight: FontWeight.w500, - color: Color(0xFF1D2B47), - ), - ), - const TextSpan( - text: ' 的所有高难考点!', - style: TextStyle( - fontSize: 13, - fontWeight: FontWeight.w500, - color: Color(0xFF45597E), - ), - ), - ], - ), - ), - ], - ), - ); - } -} - /// 纯文字笔记(album-desc,最多 2 行) class AlbumDesc extends StatelessWidget { const AlbumDesc({super.key, required this.text}); @@ -441,15 +384,26 @@ class LikeCommentBar extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.end, children: [ MetaButton( - icon: post.userLikeStatus - ? Icons.favorite - : Icons.favorite_border, + icon: Image.asset( + post.userLikeStatus + ? 'assets/img/class/like-check-icon.png' + : 'assets/img/class/like-icon.png', + width: 13.w, + height: 13.w, + ), count: post.likeCount, active: post.userLikeStatus, onTap: onLike, ), SizedBox(width: 8.w), - MetaButton(icon: Icons.chat_bubble_outline, count: post.replyCount), + MetaButton( + icon: Image.asset( + 'assets/img/class/comment-icon.png', + width: 13.w, + height: 13.w, + ), + count: post.replyCount, + ), ], ), if (showComposer) ...[ @@ -474,7 +428,7 @@ class MetaButton extends StatelessWidget { this.onTap, }); - final IconData icon; + final Widget icon; final int count; final bool active; final VoidCallback? onTap; @@ -496,7 +450,7 @@ class MetaButton extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon(icon, size: 13.w, color: color), + icon, SizedBox(width: 4.w), Text('$count', style: TextStyle(fontSize: 13, color: color)), ], diff --git a/pubspec.yaml b/pubspec.yaml index 5101c6e..292c5b5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: new_learn description: "A new Flutter project." # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: "none" # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 @@ -222,7 +222,6 @@ dev_dependencies: # The following section is specific to Flutter packages. flutter: - # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. @@ -236,6 +235,7 @@ flutter: - assets/img/tabbar/ - assets/img/tabbar/default/ - assets/audio/ + - assets/img/class/ # To add assets to your application, add an assets section, like this: # assets: