fix(app): 修复 P2~P4 共 10 项前端问题
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled

P2 必须修复:
- 教师布置主题 classId 从硬编码改为班级下拉选择器
- 班级日记墙使用服务端 classId 过滤替代前端过滤
- Profile 统计栏接入 JournalRepository 真实数据
- WeeklyPage 从全硬编码改为 JournalRepository 数据驱动

P3 建议改进:
- 提取 mood_utils.dart 公共函数,消除 4 处重复定义
- 贴纸库搜索框连接 StickerBloc 按名称过滤

P4 细节打磨:
- 家长页多孩子时显示 DropdownButton 选择器
- 搜索结果日记卡片点击跳转 /editor?id=
- MonthlyPage 照片数量从 JournalElement 统计
- calendar_page/mood_page/search_page 统一使用 moodToEmoji/moodToLabel
This commit is contained in:
iven
2026-06-02 20:21:51 +08:00
parent 75db6a7eb7
commit 7e928ae1e1
17 changed files with 2537 additions and 799 deletions

View File

@@ -34,6 +34,7 @@ class IsarJournalRepository implements JournalRepository {
int? pageSize,
String? mood,
String? tag,
String? classId,
}) async {
var query = _isar.journalEntryCollections
.where()
@@ -58,6 +59,11 @@ class IsarJournalRepository implements JournalRepository {
query = query.and().tagsJsonContains(tag);
}
// 班级过滤
if (classId != null) {
query = query.and().classIdEqualTo(classId);
}
// 按日期降序排列
var results = await query
.sortByDateEpochDesc()
@@ -74,6 +80,15 @@ class IsarJournalRepository implements JournalRepository {
return results.map(_fromCollection).toList();
}
@override
Future<int> getJournalCount() async {
return _isar.journalEntryCollections
.where()
.filter()
.isDeletedEqualTo(false)
.count();
}
@override
Future<JournalEntry?> getJournal(String id) async {
final col = await _isar.journalEntryCollections
@@ -262,6 +277,7 @@ class IsarJournalRepository implements JournalRepository {
..isPrivate = entry.isPrivate
..sharedToClass = entry.sharedToClass
..assignedTopicId = entry.assignedTopicId
..contentExcerpt = entry.contentExcerpt
..version = entry.version
..createdAtEpoch = entry.createdAt.millisecondsSinceEpoch
..updatedAtEpoch = entry.updatedAt.millisecondsSinceEpoch
@@ -290,6 +306,7 @@ class IsarJournalRepository implements JournalRepository {
isPrivate: col.isPrivate,
sharedToClass: col.sharedToClass,
assignedTopicId: col.assignedTopicId,
contentExcerpt: col.contentExcerpt,
version: col.version,
createdAt: DateTime.fromMillisecondsSinceEpoch(col.createdAtEpoch),
updatedAt: DateTime.fromMillisecondsSinceEpoch(col.updatedAtEpoch),

View File

@@ -15,7 +15,7 @@ import '../models/journal_element.dart';
/// - [dateFrom]/[dateTo]: 日期范围过滤(闭区间)
/// - [page]/[pageSize]: 分页参数,从 1 开始
abstract class JournalRepository {
/// 获取日记列表(支持日期范围、心情、标签过滤和分页)
/// 获取日记列表(支持日期范围、心情、标签、班级过滤和分页)
Future<List<JournalEntry>> getJournals({
DateTime? dateFrom,
DateTime? dateTo,
@@ -23,8 +23,12 @@ abstract class JournalRepository {
int? pageSize,
String? mood,
String? tag,
String? classId,
});
/// 获取日记总数
Future<int> getJournalCount();
/// 获取单篇日记(返回 null 表示不存在)
Future<JournalEntry?> getJournal(String id);
@@ -66,6 +70,7 @@ class InMemoryJournalRepository implements JournalRepository {
int? pageSize,
String? mood,
String? tag,
String? classId,
}) async {
var results = _journals.values.toList();
@@ -87,6 +92,11 @@ class InMemoryJournalRepository implements JournalRepository {
results = results.where((j) => j.tags.contains(tag)).toList();
}
// 班级过滤
if (classId != null) {
results = results.where((j) => j.classId == classId).toList();
}
// 按日期降序排列(最新在前)
results.sort((a, b) => b.date.compareTo(a.date));
@@ -101,6 +111,9 @@ class InMemoryJournalRepository implements JournalRepository {
return results;
}
@override
Future<int> getJournalCount() async => _journals.length;
@override
Future<JournalEntry?> getJournal(String id) async {
return _journals[id];

View File

@@ -21,6 +21,7 @@ class RemoteJournalRepository implements JournalRepository {
int? pageSize,
String? mood,
String? tag,
String? classId,
}) async {
final queryParams = <String, dynamic>{};
// 后端 NaiveDateTime 格式: "2026-06-01T00:00:00"(不带毫秒)
@@ -34,6 +35,7 @@ class RemoteJournalRepository implements JournalRepository {
if (pageSize != null) queryParams['page_size'] = pageSize;
if (mood != null) queryParams['mood'] = mood;
if (tag != null) queryParams['tag'] = tag;
if (classId != null) queryParams['class_id'] = classId;
final response = await _api.get('/diary/journals', queryParams: queryParams);
final body = response.data as Map<String, dynamic>;
@@ -43,6 +45,16 @@ class RemoteJournalRepository implements JournalRepository {
.toList();
}
@override
Future<int> getJournalCount() async {
final response = await _api.get('/diary/journals', queryParams: {
'page': 1,
'page_size': 1,
});
final body = response.data as Map<String, dynamic>;
return (body['total'] as int?) ?? 0;
}
@override
Future<JournalEntry?> getJournal(String id) async {
try {