feat(conquerPackage, upload): 新增课前课中页面与多图上传组件,修复路由配置

- 实现攻克包模块的课前课中学习页面,包含学习计时、笔记上传、分页切换等完整逻辑
- 新增通用多图上传组件,支持编辑/查看两种业务模式
- 配置新页面的路由规则,并更新捕获列表页面的跳转逻辑为正式的go_router跳转
- 修复捕获列表页面跳转路径不匹配的问题
This commit is contained in:
chenshuainan
2026-09-11 09:37:37 +08:00
parent ee14373c6d
commit fe060ac974
11 changed files with 2230 additions and 20 deletions
+80
View File
@@ -0,0 +1,80 @@
import 'package:new_learn/core/api/public_upload/model.dart';
import 'package:new_learn/core/network/dio_client.dart';
/// ============================================================
/// 【笔记相关接口】讲义笔记 / 试题笔记
///
/// 对应 Vue 端 api/task/index.ts:
/// HandoutNoteAdd → POST /api/v1/HandoutNote/Add
/// HandoutNoteDel → DELETE /api/v1/HandoutNote/Del/{id}
/// QuestionNoteAdd → POST /api/v1/QuestionNote/Add
/// QuestionNoteDel → DELETE /api/v1/QuestionNote/Del/{id}
///
/// 注意:页面只调这里,不直接碰 Dio;异常不在这里 catch,交页面统一处理。
/// ============================================================
class NoteApi {
NoteApi._();
static final NoteApi instance = NoteApi._();
/// 添加讲义笔记 → POST /api/v1/HandoutNote/Add
///
/// [id] 讲义 id
/// [url] 原图 URL(OSS 直传后返回)
/// [thumbnailUrl] 缩略图 URL
Future<NoteImage> handoutNoteAdd({
required dynamic id,
required String url,
required String thumbnailUrl,
}) async {
final data = await DioClient.instance.post(
'/api/v1/HandoutNote/Add',
data: {
'id': id,
'url': url,
'thumbnailUrl': thumbnailUrl,
},
);
// 后端统一 {code, message, data} 包装:data 存在取 data,否则用顶层
final payload =
(data is Map<String, dynamic> && data.containsKey('data'))
? (data['data'] as Map<String, dynamic>?) ?? <String, dynamic>{}
: data as Map<String, dynamic>;
return NoteImage.fromJson(payload);
}
/// 删除讲义笔记 → DELETE /api/v1/HandoutNote/Del/{id}
///
/// 返回 true 表示删除成功
Future<bool> handoutNoteDel(dynamic id) async {
await DioClient.instance.delete('/api/v1/HandoutNote/Del/$id');
return true;
}
/// 添加试题笔记 → POST /api/v1/QuestionNote/Add
Future<NoteImage> questionNoteAdd({
required dynamic id,
required String url,
required String thumbnailUrl,
}) async {
final data = await DioClient.instance.post(
'/api/v1/QuestionNote/Add',
data: {
'id': id,
'url': url,
'thumbnailUrl': thumbnailUrl,
},
);
final payload =
(data is Map<String, dynamic> && data.containsKey('data'))
? (data['data'] as Map<String, dynamic>?) ?? <String, dynamic>{}
: data as Map<String, dynamic>;
return NoteImage.fromJson(payload);
}
/// 删除试题笔记 → DELETE /api/v1/QuestionNote/Del/{id}
Future<bool> questionNoteDel(dynamic id) async {
await DioClient.instance.delete('/api/v1/QuestionNote/Del/$id');
return true;
}
}
+40
View File
@@ -0,0 +1,40 @@
/// ============================================================
/// 【笔记图片数据模型】讲义笔记 / 试题笔记
///
/// NoteImage 对应 Vue 端上传笔记后返回的图片记录:
/// - id:笔记记录 id(删除接口用)
/// - url:原图 URL
/// - thumbnailUrl:缩略图 URL(列表预览用)
/// ============================================================
/// 笔记图片项(用于已上传图片列表)
class NoteImage {
const NoteImage({
this.id,
required this.url,
this.thumbnailUrl,
});
/// 服务器返回的笔记 id(删除接口用;本地添加的图片可能没有)
final dynamic id;
/// 图片原图 URL
final String url;
/// 缩略图 URL(为空时用 url 兜底)
final String? thumbnailUrl;
factory NoteImage.fromJson(Map<String, dynamic> json) {
return NoteImage(
id: json['id'],
url: (json['url'] as String?) ?? '',
thumbnailUrl: json['thumbnailUrl'] as String?,
);
}
Map<String, dynamic> toJson() => {
'id': id,
'url': url,
'thumbnailUrl': thumbnailUrl,
};
}