原生epub阅读

This commit is contained in:
DelLevin-Home
2026-06-27 14:06:44 +08:00
parent 5acbc3a602
commit d54e33e4cf
177 changed files with 6500 additions and 93933 deletions

View File

@@ -1,92 +0,0 @@
/// 书籍批注数据模型 — 高亮、下划线、书签
class BookAnnotation {
final int? id;
final String bookId;
final String content; // 选中的文字内容
final String cfi; // EPUB CFI 位置
final String chapter; // 章节标题
final String type; // 'highlight' | 'underline' | 'bookmark'
final String color; // 颜色 hex如 'FFEB3B'
final String? readerNote; // 用户附注
final DateTime createdAt;
final DateTime updatedAt;
BookAnnotation({
this.id,
required this.bookId,
required this.content,
required this.cfi,
this.chapter = '',
required this.type,
required this.color,
this.readerNote,
DateTime? createdAt,
DateTime? updatedAt,
}) : createdAt = createdAt ?? DateTime.now(),
updatedAt = updatedAt ?? DateTime.now();
Map<String, dynamic> toMap() {
return {
if (id != null) 'id': id,
'book_id': bookId,
'content': content,
'cfi': cfi,
'chapter': chapter,
'type': type,
'color': color,
'reader_note': readerNote ?? '',
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(),
};
}
factory BookAnnotation.fromMap(Map<String, dynamic> map) {
return BookAnnotation(
id: map['id'] as int?,
bookId: map['book_id'] as String,
content: map['content'] as String? ?? '',
cfi: map['cfi'] as String? ?? '',
chapter: map['chapter'] as String? ?? '',
type: map['type'] as String? ?? 'highlight',
color: map['color'] as String? ?? 'FFEB3B',
readerNote: map['reader_note'] as String?,
createdAt: DateTime.tryParse(map['created_at'] as String? ?? '') ?? DateTime.now(),
updatedAt: DateTime.tryParse(map['updated_at'] as String? ?? '') ?? DateTime.now(),
);
}
/// 用于 JS bridge 的 JSON传给 foliate-js renderAnnotations
Map<String, dynamic> toJson() {
return {
'id': id ?? 0,
'type': type,
'value': cfi,
'color': '#$color',
'note': content,
};
}
BookAnnotation copyWith({
int? id,
String? bookId,
String? content,
String? cfi,
String? chapter,
String? type,
String? color,
String? readerNote,
}) {
return BookAnnotation(
id: id ?? this.id,
bookId: bookId ?? this.bookId,
content: content ?? this.content,
cfi: cfi ?? this.cfi,
chapter: chapter ?? this.chapter,
type: type ?? this.type,
color: color ?? this.color,
readerNote: readerNote ?? this.readerNote,
createdAt: createdAt,
updatedAt: DateTime.now(),
);
}
}

View File

@@ -1,104 +0,0 @@
/// 阅读器书籍模型
class ReaderBook {
final String id;
final String title;
final String author;
final String? coverPath;
final String filePath; // 相对路径
final String fileName;
final String fileExtension;
final String lastReadCfi;
final double readingPercentage;
final DateTime createdAt;
final DateTime updatedAt;
final bool isDeleted;
ReaderBook({
required this.id,
required this.title,
this.author = '',
this.coverPath,
required this.filePath,
required this.fileName,
required this.fileExtension,
this.lastReadCfi = '',
this.readingPercentage = 0.0,
required this.createdAt,
required this.updatedAt,
this.isDeleted = false,
});
factory ReaderBook.fromJson(Map<String, dynamic> json) {
return ReaderBook(
id: json['id'] ?? '',
title: json['title'] ?? '',
author: json['author'] ?? '',
coverPath: json['cover_path'],
filePath: json['file_path'] ?? '',
fileName: json['file_name'] ?? '',
fileExtension: json['file_extension'] ?? 'epub',
lastReadCfi: json['last_read_cfi'] ?? '',
readingPercentage: (json['reading_percentage'] as num?)?.toDouble() ?? 0.0,
createdAt: json['created_at'] != null
? DateTime.parse(json['created_at'])
: DateTime.now(),
updatedAt: json['updated_at'] != null
? DateTime.parse(json['updated_at'])
: DateTime.now(),
isDeleted: json['is_deleted'] == 1 || json['is_deleted'] == true,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'title': title,
'author': author,
'cover_path': coverPath,
'file_path': filePath,
'file_name': fileName,
'file_extension': fileExtension,
'last_read_cfi': lastReadCfi,
'reading_percentage': readingPercentage,
'created_at': createdAt.toUtc().toIso8601String(),
'updated_at': updatedAt.toUtc().toIso8601String(),
'is_deleted': isDeleted ? 1 : 0,
};
}
ReaderBook copyWith({
String? id,
String? title,
String? author,
Object? coverPath = _readerBookCopyWithNull,
String? filePath,
String? fileName,
String? fileExtension,
String? lastReadCfi,
double? readingPercentage,
DateTime? createdAt,
DateTime? updatedAt,
bool? isDeleted,
}) {
return ReaderBook(
id: id ?? this.id,
title: title ?? this.title,
author: author ?? this.author,
coverPath: coverPath is _ReaderBookCopyWithNullSentinel ? this.coverPath : (coverPath as String?),
filePath: filePath ?? this.filePath,
fileName: fileName ?? this.fileName,
fileExtension: fileExtension ?? this.fileExtension,
lastReadCfi: lastReadCfi ?? this.lastReadCfi,
readingPercentage: readingPercentage ?? this.readingPercentage,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
isDeleted: isDeleted ?? this.isDeleted,
);
}
}
class _ReaderBookCopyWithNullSentinel {
const _ReaderBookCopyWithNullSentinel();
}
const _readerBookCopyWithNull = _ReaderBookCopyWithNullSentinel();