马良AI写作初始化仓库

This commit is contained in:
邓滨杰
2025-09-10 00:07:52 +08:00
parent 3c06bb1a03
commit 39c0f8840f
1309 changed files with 318528 additions and 0 deletions

View File

@@ -0,0 +1,400 @@
import 'package:ainoval/models/novel_summary.dart';
import 'package:ainoval/services/api_service/repositories/novel_repository.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
// 事件定义
abstract class NovelListEvent extends Equatable {
@override
List<Object?> get props => [];
}
class LoadNovels extends NovelListEvent {}
class SearchNovels extends NovelListEvent {
SearchNovels({required this.query});
final String query;
@override
List<Object?> get props => [query];
}
class FilterNovels extends NovelListEvent {
FilterNovels({required this.filterOption});
final FilterOption filterOption;
@override
List<Object?> get props => [filterOption];
}
class SortNovels extends NovelListEvent {
SortNovels({required this.sortOption});
final SortOption sortOption;
@override
List<Object?> get props => [sortOption];
}
class GroupNovels extends NovelListEvent {
GroupNovels({required this.groupOption});
final GroupOption groupOption;
@override
List<Object?> get props => [groupOption];
}
class DeleteNovel extends NovelListEvent {
DeleteNovel({required this.id});
final String id;
@override
List<Object?> get props => [id];
}
// 添加创建小说的事件
class CreateNovel extends NovelListEvent {
CreateNovel({
required this.title,
this.seriesName,
});
final String title;
final String? seriesName;
@override
List<Object?> get props => [title, seriesName];
}
// 状态定义
abstract class NovelListState extends Equatable {
@override
List<Object?> get props => [];
}
class NovelListInitial extends NovelListState {}
class NovelListLoading extends NovelListState {}
class NovelListLoaded extends NovelListState {
NovelListLoaded({
required List<NovelSummary> allNovels,
this.sortOption = SortOption.lastEdited,
this.filterOption = const FilterOption(),
this.groupOption = GroupOption.none,
this.searchQuery = '',
}) : _allNovels = allNovels,
novels = _applySearchAndFilterAndSort(allNovels, searchQuery, filterOption, sortOption);
final List<NovelSummary> _allNovels;
final List<NovelSummary> novels;
final SortOption sortOption;
final FilterOption filterOption;
final GroupOption groupOption;
final String searchQuery;
@override
List<Object?> get props => [_allNovels, novels, sortOption, filterOption, groupOption, searchQuery];
static List<NovelSummary> _applySearchAndFilterAndSort(
List<NovelSummary> novels,
String searchQuery,
FilterOption filterOption,
SortOption sortOption,
) {
List<NovelSummary> processedNovels = List.from(novels);
if (searchQuery.isNotEmpty) {
processedNovels = processedNovels.where((novel) {
final titleMatch = novel.title.toLowerCase().contains(searchQuery.toLowerCase());
final seriesMatch = novel.seriesName.toLowerCase().contains(searchQuery.toLowerCase());
return titleMatch || seriesMatch;
}).toList();
}
if (filterOption.series != null && filterOption.series!.isNotEmpty) {
processedNovels = processedNovels.where((novel) {
return novel.seriesName.toLowerCase() == filterOption.series!.toLowerCase();
}).toList();
}
switch (sortOption) {
case SortOption.lastEdited:
processedNovels.sort((a, b) => b.lastEditTime.compareTo(a.lastEditTime));
break;
case SortOption.title:
processedNovels.sort((a, b) => a.title.compareTo(b.title));
break;
case SortOption.wordCount:
processedNovels.sort((a, b) => b.wordCount.compareTo(a.wordCount));
break;
case SortOption.creationDate:
processedNovels.sort((a, b) => b.lastEditTime.compareTo(a.lastEditTime));
break;
case SortOption.actCount:
processedNovels.sort((a, b) => b.actCount.compareTo(a.actCount));
break;
case SortOption.chapterCount:
processedNovels.sort((a, b) => b.chapterCount.compareTo(a.chapterCount));
break;
case SortOption.sceneCount:
processedNovels.sort((a, b) => b.sceneCount.compareTo(a.sceneCount));
break;
}
return processedNovels;
}
NovelListLoaded copyWith({
List<NovelSummary>? allNovels,
SortOption? sortOption,
FilterOption? filterOption,
GroupOption? groupOption,
String? searchQuery,
}) {
return NovelListLoaded(
allNovels: allNovels ?? _allNovels,
sortOption: sortOption ?? this.sortOption,
filterOption: filterOption ?? this.filterOption,
groupOption: groupOption ?? this.groupOption,
searchQuery: searchQuery ?? this.searchQuery,
);
}
}
class NovelListError extends NovelListState {
NovelListError({required this.message});
final String message;
@override
List<Object?> get props => [message];
}
// 排序选项
enum SortOption {
lastEdited,
title,
wordCount,
creationDate,
actCount,
chapterCount,
sceneCount,
}
// 分组选项
enum GroupOption {
none,
series,
status,
}
// 过滤选项
class FilterOption extends Equatable {
const FilterOption({
this.showCompleted = true,
this.showInProgress = true,
this.showNotStarted = true,
this.minWordCount = 0,
this.maxWordCount,
this.series,
});
final bool showCompleted;
final bool showInProgress;
final bool showNotStarted;
final int minWordCount;
final int? maxWordCount;
final String? series;
@override
List<Object?> get props => [
showCompleted,
showInProgress,
showNotStarted,
minWordCount,
maxWordCount,
series,
];
}
// 添加强制刷新事件
class RefreshNovels extends NovelListEvent {
@override
List<Object?> get props => [];
}
// 清理状态事件(用于退出登录)
class ClearNovels extends NovelListEvent {
@override
List<Object?> get props => [];
}
// Bloc实现
class NovelListBloc extends Bloc<NovelListEvent, NovelListState> {
NovelListBloc({required this.repository}) : super(NovelListInitial()) {
on<LoadNovels>(_onLoadNovels);
on<RefreshNovels>(_onRefreshNovels);
on<ClearNovels>(_onClearNovels);
on<SearchNovels>(_onSearchNovels);
on<FilterNovels>(_onFilterNovels);
on<SortNovels>(_onSortNovels);
on<GroupNovels>(_onGroupNovels);
on<DeleteNovel>(_onDeleteNovel);
on<CreateNovel>(_onCreateNovel);
}
final NovelRepository repository;
// 防止重复加载标志
bool _isLoading = false;
// 数据是否已经加载过的标志
bool _hasLoadedData = false;
Future<void> _onLoadNovels(LoadNovels event, Emitter<NovelListState> emit) async {
// 如果数据已经加载过且当前不是错误状态,则不重复加载
if (_hasLoadedData && state is NovelListLoaded) return;
// 如果已经在加载中,则不重复加载
if (_isLoading || state is NovelListLoading) return;
_isLoading = true;
// 只有在没有数据时才显示加载状态
if (!_hasLoadedData) {
emit(NovelListLoading());
}
try {
final novels = await repository.fetchNovels();
// 转换为NovelSummary列表
final novelSummaries = novels.map((novel) => NovelSummary(
id: novel.id,
title: novel.title,
coverUrl: novel.coverUrl,
lastEditTime: novel.updatedAt,
wordCount: novel.wordCount,
readTime: novel.readTime,
version: novel.version,
completionPercentage: 0.0,
lastEditedChapterId: novel.lastEditedChapterId,
author: novel.author?.username,
contributors: novel.contributors,
actCount: novel.getActCount(),
chapterCount: novel.getChapterCount(),
sceneCount: novel.getSceneCount(),
serverUpdatedAt: novel.updatedAt,
)).toList();
_hasLoadedData = true;
emit(NovelListLoaded(allNovels: novelSummaries));
} catch (e) {
emit(NovelListError(message: e.toString()));
} finally {
_isLoading = false;
}
}
// 强制刷新数据(忽略缓存)
Future<void> _onRefreshNovels(RefreshNovels event, Emitter<NovelListState> emit) async {
// 重置缓存标志,强制重新加载
_hasLoadedData = false;
add(LoadNovels());
}
// 清理小说列表状态(用于退出登录)
void _onClearNovels(ClearNovels event, Emitter<NovelListState> emit) {
// 重置所有标志
_isLoading = false;
_hasLoadedData = false;
// 恢复到初始状态
emit(NovelListInitial());
}
Future<void> _onSearchNovels(SearchNovels event, Emitter<NovelListState> emit) async {
final currentState = state;
if (currentState is NovelListLoaded) {
emit(currentState.copyWith(searchQuery: event.query));
}
}
void _onFilterNovels(FilterNovels event, Emitter<NovelListState> emit) {
final currentState = state;
if (currentState is NovelListLoaded) {
emit(currentState.copyWith(filterOption: event.filterOption));
}
}
void _onSortNovels(SortNovels event, Emitter<NovelListState> emit) {
final currentState = state;
if (currentState is NovelListLoaded) {
emit(currentState.copyWith(sortOption: event.sortOption));
}
}
void _onGroupNovels(GroupNovels event, Emitter<NovelListState> emit) {
final currentState = state;
if (currentState is NovelListLoaded) {
emit(currentState.copyWith(groupOption: event.groupOption));
}
}
Future<void> _onDeleteNovel(DeleteNovel event, Emitter<NovelListState> emit) async {
final currentState = state;
if (currentState is NovelListLoaded) {
try {
await repository.deleteNovel(event.id);
final updatedNovels = currentState._allNovels.where((novel) => novel.id != event.id).toList();
emit(currentState.copyWith(allNovels: updatedNovels));
} catch (e) {
emit(NovelListError(message: e.toString()));
}
}
}
// 添加创建小说的处理方法
Future<void> _onCreateNovel(CreateNovel event, Emitter<NovelListState> emit) async {
try {
final newNovel = await repository.createNovel(event.title);
// 将Novel转换为NovelSummary
final novelSummary = NovelSummary(
id: newNovel.id,
title: newNovel.title,
coverUrl: newNovel.coverUrl,
lastEditTime: newNovel.updatedAt,
wordCount: newNovel.wordCount,
readTime: newNovel.readTime,
version: newNovel.version,
seriesName: event.seriesName ?? '',
completionPercentage: 0.0,
author: newNovel.author?.username,
contributors: newNovel.contributors,
actCount: newNovel.getActCount(),
chapterCount: newNovel.getChapterCount(),
sceneCount: newNovel.getSceneCount(),
serverUpdatedAt: newNovel.updatedAt,
);
// 直接更新状态,添加新创建的小说
final currentState = state;
if (currentState is NovelListLoaded) {
final updatedNovels = List<NovelSummary>.from(currentState._allNovels)..add(novelSummary);
emit(currentState.copyWith(allNovels: updatedNovels));
} else {
// 如果当前不是加载状态,则重新加载整个列表
add(LoadNovels());
}
} catch (e) {
emit(NovelListError(message: e.toString()));
}
}
}