readful/lib/models/highlight.dart
ddshi 7d7618401a feat: 完成数据模型设计阶段和学习文档整理
主要功能:
-  完成4个核心数据模型(Book、Highlight、Bookmark、Bookshelf)
-  实现7个枚举类型,提供类型安全的数据分类
-  建立完整的数据模型设计标准和最佳实践
-  创建5篇详细学习文档,涵盖Flutter数据模型开发

技术成就:
- 不可变对象设计模式 mastered
- 空值安全语法熟练应用
- 工厂构造函数模式实践
- 序列化/反序列化机制完整实现
- 计算属性和便利方法优化

业务价值:
- 完整支持电子书管理需求
- 高亮+批注功能集成设计
- 灵活的书架分类系统
- 可扩展的数据架构设计

文档体系:
- 📚 项目概览和技术架构(CLAUDE.md)
- 📓 数据模型完成度检查报告
- 📖 阶段性学习成果总结
- 💡 Flutter开发最佳实践指导

代码统计:
- 4个核心模型文件,约1200行代码
- 7个枚举类型,45个数据字段
- 80+个方法,完整的对象生命周期管理

下一阶段:Hive数据库集成和Repository模式实现

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 19:33:17 +08:00

224 lines
6.5 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

///高亮模型
/// 不同颜色的高亮
enum HighlightColor {
yellow, // 黄色 - 默认高亮
orange, // 橙色 - 重要内容
green, // 绿色 - 已理解内容
blue, // 蓝色 - 需要复习
pink // 粉色 - 个人感想
}
/// 批注类型枚举
/// 用于分类不同类型的批注内容
enum AnnotationType {
note, // 笔记 - 记录知识点
thought, // 感想 - 个人想法
summary, // 摘要 - 内容总结
question // 问题 - 疑问记录
}
///高亮模型
// 关联书籍和章节
// 记录选中的文本范围
// 支持不同颜色的高亮
// 记录创建时间
class Highlight {
//唯一标识符 - 使用UUID确保全局唯一
final String id;
//对应的书籍id
final String bookId;
// 对应章节id
final String? chapterId;
// 选中文本
final String selectedText;
// 开始索引
final int startIndex;
// 结尾索引
final int endIndex;
// 高亮颜色
final HighlightColor color;
// 创建时间
final DateTime createdTime;
/// 批注内容 - 可选字段,用于存储用户对高亮的注释
final String? annotation;
/// 批注类型 - 可选字段,用于分类批注
final AnnotationType? annotationType;
/// 批注添加时间 - 可选字段,记录批注的最后修改时间
final DateTime? annotationTime;
const Highlight({
required this.id,
required this.bookId,
this.chapterId,
required this.selectedText,
required this.startIndex,
required this.endIndex,
required this.color,
required this.createdTime,
this.annotation, // 批注内容
this.annotationType, // 批注类型
this.annotationTime, // 批注时间
});
/// copyWith方法 - 创建对象的副本
Highlight copyWith({
String? id,
String? bookId,
String? chapterId,
String? selectedText,
int? startIndex,
int? endIndex,
HighlightColor? color,
DateTime? createdTime,
String? annotation,
AnnotationType? annotationType,
DateTime? annotationTime,
}) {
return Highlight(
id: id ?? this.id,
bookId: bookId ?? this.bookId,
chapterId: chapterId ?? this.chapterId,
selectedText: selectedText ?? this.selectedText,
startIndex: startIndex ?? this.startIndex,
endIndex: endIndex ?? this.endIndex,
color: color ?? this.color,
createdTime: createdTime ?? this.createdTime,
annotation: annotation ?? this.annotation,
annotationType: annotationType ?? this.annotationType,
annotationTime: annotationTime ?? this.annotationTime,
);
}
/// toMap方法 - 将对象转换为Map
Map<String, dynamic> toMap() {
return {
'id': id,
'bookId': bookId,
'chapterId': chapterId,
'selectedText': selectedText,
'startIndex': startIndex,
'endIndex': endIndex,
'color': color.name, // 枚举值转换为字符串
'createdTime': createdTime.toIso8601String(), // DateTime转换为字符串
'annotation': annotation,
'annotationType': annotationType?.name, // 枚举转换为字符串注意可能是null
'annotationTime': annotationTime?.toIso8601String(), // DateTime转换为字符串注意可能是null
};
}
/// fromMap构造函数 - 从Map创建Highlight对象
factory Highlight.fromMap(Map<String, dynamic> map) {
return Highlight(
id: map['id'],
bookId: map['bookId'],
chapterId: map['chapterId'],
selectedText: map['selectedText'],
startIndex: map['startIndex'],
endIndex: map['endIndex'],
color: HighlightColor.values.firstWhere((e) => e.name == map['color']),
createdTime: DateTime.parse(map['createdTime']),
annotation: map['annotation'], // 批注内容直接取值
annotationType: map['annotationType'] != null
? AnnotationType.values.firstWhere((e) => e.name == map['annotationType'])
: null, // 处理可选的枚举字段
annotationTime: map['annotationTime'] != null
? DateTime.parse(map['annotationTime'])
: null, // 处理可选的DateTime字段
);
}
/// 创建一个新Highlight实例的工厂方法
/// 这是一个便利方法,用于创建基本的高亮对象
/// 通常在用户选中文本时使用
factory Highlight.create({
required String bookId,
required String selectedText,
required int startIndex,
required int endIndex,
HighlightColor color = HighlightColor.yellow, // 默认颜色
String? chapterId,
}) {
return Highlight(
id: _generateId(), // 生成唯一ID
bookId: bookId,
chapterId: chapterId,
selectedText: selectedText,
startIndex: startIndex,
endIndex: endIndex,
color: color,
createdTime: DateTime.now(), // 当前时间作为创建时间
);
}
/// 生成唯一ID的私有方法
static String _generateId() {
return '${DateTime.now().millisecondsSinceEpoch}_${(DateTime.now().microsecond).toString()}';
}
/// 重写equals和hashCode用于对象比较
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is Highlight && other.id == id;
}
@override
int get hashCode => id.hashCode;
/// 计算属性:检查是否有批注
bool get hasAnnotation => annotation != null && annotation!.isNotEmpty;
/// 计算属性:获取批注文本长度
int get annotationLength => annotation?.length ?? 0;
/// 添加批注的方法
/// 返回一个新的Highlight对象包含指定的批注内容
Highlight addAnnotation(String content, AnnotationType type) {
return copyWith(
annotation: content,
annotationType: type,
annotationTime: DateTime.now(), // 设置当前时间为批注时间
);
}
/// 更新批注内容的方法
/// 返回一个新的Highlight对象包含更新的批注内容
Highlight updateAnnotation(String content, AnnotationType type) {
return copyWith(
annotation: content,
annotationType: type,
annotationTime: DateTime.now(), // 更新批注时间
);
}
/// 移除批注的方法
/// 返回一个新的Highlight对象移除所有批注相关字段
Highlight removeAnnotation() {
return copyWith(
annotation: null,
annotationType: null,
annotationTime: null,
);
}
/// 重写toString方法增加批注信息显示
@override
String toString() {
final annotationInfo = hasAnnotation
? ' [批注: $annotation]'
: '';
return 'Highlight(id: $id, bookId: $bookId, text: "$selectedText"$annotationInfo)';
}
}