- 新增 DiscoverBloc (LoadData/Refresh) + DiscoverModels 4 个数据类 - DiscoverPage 改为 BlocBuilder 驱动: loading/loaded/error/empty 四态 - 替换全部硬编码占位数据为 API 实时数据 - 添加 RefreshIndicator 下拉刷新 - 离线异常时保留已有数据,友好错误提示
161 lines
4.6 KiB
Dart
161 lines
4.6 KiB
Dart
// 发现页数据模型 — 手写不可变类(避免 build_runner 依赖)
|
||
|
||
/// 发现页聚合响应 — 一次 API 返回全部板块数据
|
||
class DiscoverData {
|
||
final InspirationItem? dailyInspiration;
|
||
final List<TagCount> hotTopics;
|
||
final List<DiscoverTemplateItem> featuredTemplates;
|
||
final List<ExpertDiaryItem> expertDiaries;
|
||
|
||
const DiscoverData({
|
||
this.dailyInspiration,
|
||
this.hotTopics = const [],
|
||
this.featuredTemplates = const [],
|
||
this.expertDiaries = const [],
|
||
});
|
||
|
||
factory DiscoverData.fromJson(Map<String, dynamic> json) => DiscoverData(
|
||
dailyInspiration: json['daily_inspiration'] != null
|
||
? InspirationItem.fromJson(
|
||
json['daily_inspiration'] as Map<String, dynamic>)
|
||
: null,
|
||
hotTopics: (json['hot_topics'] as List? ?? [])
|
||
.map((e) => TagCount.fromJson(e as Map<String, dynamic>))
|
||
.toList(),
|
||
featuredTemplates: (json['featured_templates'] as List? ?? [])
|
||
.map(
|
||
(e) => DiscoverTemplateItem.fromJson(e as Map<String, dynamic>))
|
||
.toList(),
|
||
expertDiaries: (json['expert_diaries'] as List? ?? [])
|
||
.map((e) => ExpertDiaryItem.fromJson(e as Map<String, dynamic>))
|
||
.toList(),
|
||
);
|
||
|
||
/// 心情 → emoji 映射
|
||
static String moodToEmoji(String mood) => switch (mood) {
|
||
'happy' => '😊',
|
||
'calm' => '😌',
|
||
'sad' => '😢',
|
||
'angry' => '😤',
|
||
'thinking' => '🤔',
|
||
_ => '📝',
|
||
};
|
||
}
|
||
|
||
/// 每日推荐条目
|
||
class InspirationItem {
|
||
final String journalId;
|
||
final String title;
|
||
final String authorName;
|
||
final String mood;
|
||
final DateTime date;
|
||
|
||
const InspirationItem({
|
||
required this.journalId,
|
||
required this.title,
|
||
required this.authorName,
|
||
required this.mood,
|
||
required this.date,
|
||
});
|
||
|
||
factory InspirationItem.fromJson(Map<String, dynamic> json) =>
|
||
InspirationItem(
|
||
journalId: json['journal_id'] as String,
|
||
title: json['title'] as String,
|
||
authorName: json['author_name'] as String,
|
||
mood: json['mood'] as String,
|
||
date: DateTime.parse(json['date'] as String),
|
||
);
|
||
}
|
||
|
||
/// 热门话题
|
||
class TagCount {
|
||
final String tag;
|
||
final int count;
|
||
|
||
const TagCount({required this.tag, required this.count});
|
||
|
||
factory TagCount.fromJson(Map<String, dynamic> json) => TagCount(
|
||
tag: json['tag'] as String,
|
||
count: json['count'] as int,
|
||
);
|
||
}
|
||
|
||
/// 精选模板条目(轻量版,不含 layout_data)
|
||
class DiscoverTemplateItem {
|
||
final String id;
|
||
final String name;
|
||
final String? previewUrl;
|
||
final String? category;
|
||
final bool isFree;
|
||
|
||
const DiscoverTemplateItem({
|
||
required this.id,
|
||
required this.name,
|
||
this.previewUrl,
|
||
this.category,
|
||
this.isFree = true,
|
||
});
|
||
|
||
factory DiscoverTemplateItem.fromJson(Map<String, dynamic> json) =>
|
||
DiscoverTemplateItem(
|
||
id: json['id'] as String,
|
||
name: json['name'] as String,
|
||
previewUrl: json['preview_url'] as String?,
|
||
category: json['category'] as String?,
|
||
isFree: json['is_free'] as bool? ?? true,
|
||
);
|
||
|
||
/// 分类 → emoji 映射
|
||
String get emoji => switch (category) {
|
||
'日常' => '📖',
|
||
'旅行' => '✈️',
|
||
'校园' => '🎓',
|
||
'节日' => '🎄',
|
||
'创意' => '✨',
|
||
'心情' => '🌿',
|
||
_ => '📝',
|
||
};
|
||
|
||
/// 使用人数展示文本
|
||
String get usageText => isFree ? '免费模板' : '精品模板';
|
||
}
|
||
|
||
/// 达人日记条目
|
||
class ExpertDiaryItem {
|
||
final String journalId;
|
||
final String title;
|
||
final String authorId;
|
||
final String authorName;
|
||
final String authorEmoji;
|
||
final String contentPreview;
|
||
final int likeCount;
|
||
final DateTime createdAt;
|
||
|
||
const ExpertDiaryItem({
|
||
required this.journalId,
|
||
required this.title,
|
||
required this.authorId,
|
||
required this.authorName,
|
||
required this.authorEmoji,
|
||
required this.contentPreview,
|
||
required this.likeCount,
|
||
required this.createdAt,
|
||
});
|
||
|
||
factory ExpertDiaryItem.fromJson(Map<String, dynamic> json) =>
|
||
ExpertDiaryItem(
|
||
journalId: json['journal_id'] as String,
|
||
title: json['title'] as String,
|
||
authorId: json['author_id'] as String,
|
||
authorName: json['author_name'] as String,
|
||
authorEmoji: json['author_emoji'] as String,
|
||
contentPreview: json['content_preview'] as String? ?? '',
|
||
likeCount: json['like_count'] as int? ?? 0,
|
||
createdAt: DateTime.parse(json['created_at'] as String),
|
||
);
|
||
|
||
/// 点赞数展示文本
|
||
String get likeText => '$likeCount 赞';
|
||
}
|