feat(my): add 我的笔记 page and related functions

1. 新增我的笔记页面,包含学科筛选Tab和游标分页列表
2. 新增user_note接口封装和对应数据模型
3. 在我的页面添加笔记入口跳转
4. 优化收藏/错题卡片的按压反馈效果
This commit is contained in:
崔宝新
2026-09-10 18:14:41 +08:00
parent 14b8e56bc3
commit c65df455f2
6 changed files with 597 additions and 42 deletions
@@ -326,58 +326,81 @@ class _QuestionListPageState extends State<QuestionListPage> {
}
/// 单条收藏/错题卡片:SubjectTag + 时间 + packName + 题干富文本
class _QuestionCard extends StatelessWidget {
///
/// 按压反馈:按下缩放到 0.97、抬起回弹,背景始终白色
/// (不用 InkWell 灰底,改用 GestureDetector 驱动 AnimatedScale)。
class _QuestionCard extends StatefulWidget {
const _QuestionCard({required this.item, required this.onTap});
final UserCollectItem item;
final VoidCallback onTap;
@override
State<_QuestionCard> createState() => _QuestionCardState();
}
class _QuestionCardState extends State<_QuestionCard> {
/// 是否处于按压状态(控制 0.97 缩放)
bool _pressed = false;
void _setPressed(bool v) {
if (_pressed != v) setState(() => _pressed = v);
}
@override
Widget build(BuildContext context) {
final item = widget.item;
return Padding(
padding: EdgeInsets.only(bottom: 14.h), // Vue listItem margin-bottom: 28rpx
child: Material(
color: Colors.white,
borderRadius: BorderRadius.circular(16.r),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(16.r),
child: Padding(
// Vue padding: t-32 l/r-28 b-28
padding: EdgeInsets.fromLTRB(14.w, 16.h, 14.w, 14.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 第一行:学科标签 + 时间
Row(
children: [
SubjectTag(subject: item.subjectName),
const Spacer(),
Text(
item.createTime,
style: TextStyle(
fontSize: 12.sp, // Vue font-24
color: const Color(0xFFB4C2E0),
child: GestureDetector(
onTapDown: (_) => _setPressed(true),
onTapUp: (_) => _setPressed(false),
onTapCancel: () => _setPressed(false),
onTap: widget.onTap,
child: AnimatedScale(
scale: _pressed ? 0.97 : 1.0,
duration: const Duration(milliseconds: 100),
curve: Curves.easeOut,
child: Material(
color: Colors.white,
borderRadius: BorderRadius.circular(16.r),
child: Padding(
// Vue padding: t-32 l/r-28 b-28
padding: EdgeInsets.fromLTRB(14.w, 16.h, 14.w, 14.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 第一行:学科标签 + 时间
Row(
children: [
SubjectTag(subject: item.subjectName),
const Spacer(),
Text(
item.createTime,
style: TextStyle(
fontSize: 12.sp, // Vue font-24
color: const Color(0xFFB4C2E0),
),
),
),
],
),
SizedBox(height: 14.h), // Vue pack-name margin-top: 28rpx
// 试卷/礼包名
Text(
item.packName,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 16.sp, // Vue font-32
fontWeight: FontWeight.w600,
color: const Color(0xFF1D2B47),
],
),
),
SizedBox(height: 16.h), // Vue question-stem-wrap margin-top: 32rpx
// 题干富文本(限高渐隐)
_StemBox(html: item.questionStem),
],
SizedBox(height: 14.h), // Vue pack-name margin-top: 28rpx
// 试卷/礼包名
Text(
item.packName,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 16.sp, // Vue font-32
fontWeight: FontWeight.w600,
color: const Color(0xFF1D2B47),
),
),
SizedBox(height: 16.h), // Vue question-stem-wrap margin-top: 32rpx
// 题干富文本(限高渐隐)
_StemBox(html: item.questionStem),
],
),
),
),
),