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
+67
View File
@@ -0,0 +1,67 @@
import 'package:new_learn/core/api/user_collect/model.dart';
import 'package:new_learn/core/api/user_note/model.dart';
import 'package:new_learn/core/network/dio_client.dart';
/// ============================================================
/// 【user_note 模块接口】我的笔记 —— 对标 Vue3 端 api/userNote/index.ts
///
/// getSubjects —— 学科筛选项(顶部 Tab,无参)
/// GET /api/v1/UserNote/GetUserCollectSubject
///
/// getNoteList —— 笔记分页列表(游标分页)
/// GET /api/v1/UserNote/GetUserNotes
/// 参数:subjectId(可选) / pageIndex / pageSize / prevPageId
///
/// 列表不按笔记类型过滤;type(1题目/2讲义) 由每条数据自带,
/// 前端用卡片角标图标区分。
///
/// 注意:Vue 同文件里的 OSS 凭证/上传地址接口归属 OSS 模块
/// (Flutter 端见 core/network/oss_api.dart),不在此重复封装。
/// ============================================================
class UserNoteApi {
UserNoteApi._();
static final UserNoteApi instance = UserNoteApi._();
/// 学科筛选项
Future<List<CollectTab>> getSubjects() async {
final data = await DioClient.instance.get(
'/api/v1/UserNote/GetUserCollectSubject',
);
// 兼容裸数组 / {data: [...]} 响应壳
final List<dynamic> list = data is List
? data
: (data is Map<String, dynamic>
? (data['data'] as List<dynamic>? ?? const [])
: const []);
return list
.whereType<Map<String, dynamic>>()
.map(CollectTab.fromJson)
.toList();
}
/// 笔记分页列表
///
/// [subjectId] 不传 = 全部学科;[prevPageId] 游标:首页传 0,
/// 翻页传上一页最后一条 id。
Future<UserNotePage> getNoteList({
int? subjectId,
required int pageIndex,
int pageSize = 10,
int prevPageId = 0,
}) async {
final query = <String, dynamic>{
'pageIndex': pageIndex,
'pageSize': pageSize,
'prevPageId': prevPageId,
};
// 对齐 Vue useList:subjectId 为 null 时不带该参数
if (subjectId != null) query['subjectId'] = subjectId;
final data = await DioClient.instance.get(
'/api/v1/UserNote/GetUserNotes',
query: query,
);
return UserNotePage.fromJson(data as Map<String, dynamic>);
}
}
+90
View File
@@ -0,0 +1,90 @@
/// ============================================================
/// 【user_note 模块模型】我的笔记 —— 对标 Vue3 端 api/userNote/type.ts
///
/// UserNoteItem 笔记列表项(type 1=题目笔记 2=讲义笔记)
/// UserNotePage 分页响应(游标分页:pageIndex + prevPageId)
///
/// 学科筛选 Tab 复用收藏模块的 CollectTab(见 api.dart 的 import,
/// 与 Vue 端 api/userNote/index.ts 从 ../userCollect/type 引入一致)。
/// ============================================================
library;
/// 兼容后端 id 字段:雪花 id 后端可能以 String 返回(防 JS 精度丢失)
int? _noteToInt(dynamic v) {
if (v == null) return null;
if (v is num) return v.toInt();
if (v is String && v.isNotEmpty) return int.tryParse(v);
return null;
}
/// 我的笔记列表项
class UserNoteItem {
const UserNoteItem({
this.id = 0,
this.subjectName = '',
this.type = 0,
this.title = '',
this.content = '',
this.createTime = '',
});
/// 笔记 id(游标分页 prevPageId 取本页最后一条的 id;详情页参数 id)
final int id;
/// 学科名(SubjectTag 配色用)
final String subjectName;
/// 笔记类型:1=题目笔记 2=讲义笔记(卡片角标/详情页区分用)
final int type;
/// 笔记标题
final String title;
/// 笔记内容 HTML(RichTextContent 渲染)
final String content;
/// 记笔记时间字符串
final String createTime;
factory UserNoteItem.fromJson(Map<String, dynamic> json) {
return UserNoteItem(
id: _noteToInt(json['id']) ?? 0,
subjectName: json['subjectName'] as String? ?? '',
type: _noteToInt(json['type']) ?? 0,
title: json['title'] as String? ?? '',
content: json['content'] as String? ?? '',
createTime: json['createTime'] as String? ?? '',
);
}
}
/// 我的笔记分页响应
class UserNotePage {
const UserNotePage({
this.data = const [],
this.total = 0,
this.pageIndex = 1,
this.pageSize = 10,
});
final List<UserNoteItem> data;
final int total;
final int pageIndex;
final int pageSize;
/// 兼容业务响应壳 {code, message, data: {...}}:data 存在就取 data
factory UserNotePage.fromJson(Map<String, dynamic> json) {
final payload = (json['data'] is Map<String, dynamic>)
? json['data'] as Map<String, dynamic>
: json;
return UserNotePage(
data: (payload['data'] as List<dynamic>? ?? const [])
.whereType<Map<String, dynamic>>()
.map(UserNoteItem.fromJson)
.toList(),
total: _noteToInt(payload['total']) ?? 0,
pageIndex: _noteToInt(payload['pageIndex']) ?? 1,
pageSize: _noteToInt(payload['pageSize']) ?? 10,
);
}
}