import 'dart:async'; import 'package:ainoval/models/novel_structure.dart' as novel_models; import 'package:ainoval/services/api_service/repositories/impl/editor_repository_impl.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../utils/logger.dart'; part 'plan_event.dart'; part 'plan_state.dart'; class PlanBloc extends Bloc { PlanBloc({ required EditorRepositoryImpl repository, required this.novelId, }) : repository = repository, super(PlanInitial()) { on(_onLoadContent); on(_onUpdateActTitle); on(_onUpdateChapterTitle); on(_onUpdateSceneSummary); on(_onAddNewAct); on(_onAddNewChapter); on(_onAddNewScene); on(_onMoveScene); on(_onDeleteScene); } final EditorRepositoryImpl repository; final String novelId; Future _onLoadContent( LoadPlanContent event, Emitter emit) async { emit(PlanLoading()); try { AppLogger.i('PlanBloc/_onLoadContent', '开始加载小说大纲数据'); // 获取小说数据(带场景摘要) final novel = await repository.getNovelWithSceneSummaries(novelId); if (novel == null) { emit(const PlanError(message: '无法加载小说大纲数据')); return; } emit(PlanLoaded( novel: novel, isDirty: false, isSaving: false, )); } catch (e) { emit(PlanError(message: e.toString())); } } Future _onUpdateActTitle( UpdateActTitle event, Emitter emit) async { final currentState = state; if (currentState is PlanLoaded) { try { // 更新标题逻辑 final acts = currentState.novel.acts.map((act) { if (act.id == event.actId) { return act.copyWith(title: event.title); } return act; }).toList(); final updatedNovel = currentState.novel.copyWith(acts: acts); emit(currentState.copyWith( novel: updatedNovel, isDirty: true, )); // 保存到服务器 await repository.updateActTitle( novelId, event.actId, event.title, ); emit(currentState.copyWith(isDirty: false)); } catch (e) { emit(currentState.copyWith( errorMessage: '更新Act标题失败: ${e.toString()}', )); } } } Future _onUpdateChapterTitle( UpdateChapterTitle event, Emitter emit) async { final currentState = state; if (currentState is PlanLoaded) { try { // 更新标题逻辑 final acts = currentState.novel.acts.map((act) { if (act.id == event.actId) { final chapters = act.chapters.map((chapter) { if (chapter.id == event.chapterId) { return chapter.copyWith(title: event.title); } return chapter; }).toList(); return act.copyWith(chapters: chapters); } return act; }).toList(); final updatedNovel = currentState.novel.copyWith(acts: acts); emit(currentState.copyWith( novel: updatedNovel, isDirty: true, )); // 保存到服务器 await repository.updateChapterTitle( novelId, event.actId, event.chapterId, event.title, ); emit(currentState.copyWith(isDirty: false)); } catch (e) { emit(currentState.copyWith( errorMessage: '更新Chapter标题失败: ${e.toString()}', )); } } } Future _onUpdateSceneSummary( UpdateSceneSummary event, Emitter emit) async { final currentState = state; if (currentState is PlanLoaded) { try { // 更新摘要逻辑 bool updated = false; final acts = currentState.novel.acts.map((act) { if (act.id == event.actId) { final chapters = act.chapters.map((chapter) { if (chapter.id == event.chapterId) { final scenes = chapter.scenes.map((scene) { if (scene.id == event.sceneId) { updated = true; final updatedSummary = novel_models.Summary( id: scene.summary.id, content: event.summary, ); return scene.copyWith(summary: updatedSummary); } return scene; }).toList(); return chapter.copyWith(scenes: scenes); } return chapter; }).toList(); return act.copyWith(chapters: chapters); } return act; }).toList(); if (!updated) { emit(currentState.copyWith( errorMessage: '未找到对应的场景', )); return; } final updatedNovel = currentState.novel.copyWith(acts: acts); // 先更新UI以立即反映更改 emit(currentState.copyWith( novel: updatedNovel, isDirty: true, )); // 保存到服务器 await repository.updateSummary( novelId, event.actId, event.chapterId, event.sceneId, event.summary, ); // 只更新isDirty标志,保持更新后的novel对象 emit(currentState.copyWith( novel: updatedNovel, isDirty: false, )); } catch (e) { emit(currentState.copyWith( errorMessage: '更新场景摘要失败: ${e.toString()}', )); } } } Future _onAddNewAct( AddNewAct event, Emitter emit) async { final currentState = state; if (currentState is PlanLoaded) { try { emit(currentState.copyWith(isSaving: true)); // 调用API创建新Act final updatedNovel = await repository.addNewAct( novelId, event.title, ); if (updatedNovel == null) { emit(currentState.copyWith( isSaving: false, errorMessage: '添加新Act失败', )); return; } emit(currentState.copyWith( novel: updatedNovel, isSaving: false, )); } catch (e) { emit(currentState.copyWith( isSaving: false, errorMessage: '添加新Act失败: ${e.toString()}', )); } } } Future _onAddNewChapter( AddNewChapter event, Emitter emit) async { final currentState = state; if (currentState is PlanLoaded) { try { emit(currentState.copyWith(isSaving: true)); // 调用API创建新Chapter final updatedNovel = await repository.addNewChapter( novelId, event.actId, event.title, ); if (updatedNovel == null) { emit(currentState.copyWith( isSaving: false, errorMessage: '添加新Chapter失败', )); return; } emit(currentState.copyWith( novel: updatedNovel, isSaving: false, )); } catch (e) { emit(currentState.copyWith( isSaving: false, errorMessage: '添加新Chapter失败: ${e.toString()}', )); } } } Future _onAddNewScene( AddNewScene event, Emitter emit) async { final currentState = state; if (currentState is PlanLoaded) { try { emit(currentState.copyWith(isSaving: true)); // 调用API创建新Scene final updatedNovel = await repository.addNewScene( novelId, event.actId, event.chapterId, ); if (updatedNovel == null) { emit(currentState.copyWith( isSaving: false, errorMessage: '添加新Scene失败', )); return; } emit(currentState.copyWith( novel: updatedNovel, isSaving: false, )); } catch (e) { emit(currentState.copyWith( isSaving: false, errorMessage: '添加新Scene失败: ${e.toString()}', )); } } } Future _onMoveScene( MoveScene event, Emitter emit) async { final currentState = state; if (currentState is PlanLoaded) { try { emit(currentState.copyWith(isSaving: true)); // 调用API移动Scene final updatedNovel = await repository.moveScene( novelId, event.sourceActId, event.sourceChapterId, event.sourceSceneId, event.targetActId, event.targetChapterId, event.targetIndex, ); if (updatedNovel == null) { emit(currentState.copyWith( isSaving: false, errorMessage: '移动场景失败', )); return; } emit(currentState.copyWith( novel: updatedNovel, isSaving: false, )); } catch (e) { emit(currentState.copyWith( isSaving: false, errorMessage: '移动场景失败: ${e.toString()}', )); } } } Future _onDeleteScene( DeleteScene event, Emitter emit) async { final currentState = state; if (currentState is PlanLoaded) { try { emit(currentState.copyWith(isSaving: true)); // 调用API删除场景 final success = await repository.deleteScene( novelId, event.actId, event.chapterId, event.sceneId, ); if (!success) { emit(currentState.copyWith( isSaving: false, errorMessage: '删除场景失败', )); return; } // 从小说结构中删除场景 final updatedActs = currentState.novel.acts.map((act) { if (act.id == event.actId) { final updatedChapters = act.chapters.map((chapter) { if (chapter.id == event.chapterId) { final updatedScenes = chapter.scenes .where((scene) => scene.id != event.sceneId) .toList(); return chapter.copyWith(scenes: updatedScenes); } return chapter; }).toList(); return act.copyWith(chapters: updatedChapters); } return act; }).toList(); final updatedNovel = currentState.novel.copyWith(acts: updatedActs); emit(currentState.copyWith( novel: updatedNovel, isSaving: false, )); } catch (e) { emit(currentState.copyWith( isSaving: false, errorMessage: '删除场景失败: ${e.toString()}', )); } } } }