Files
MaliangAINovalWriter/AINoval/lib/utils/context_selection_helper.dart
2025-09-10 00:07:52 +08:00

314 lines
11 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.

import 'dart:convert';
import 'package:ainoval/models/context_selection_models.dart';
import 'package:ainoval/models/novel_structure.dart';
import 'package:ainoval/models/novel_setting_item.dart';
import 'package:ainoval/models/setting_group.dart';
import 'package:ainoval/models/novel_snippet.dart';
import 'package:ainoval/utils/logger.dart';
/// 上下文选择助手类
///
/// 提供统一的上下文选择管理方法,避免在不同组件中重复实现相同逻辑
class ContextSelectionHelper {
/// 初始化上下文选择数据
///
/// 根据提供的小说、设定、片段数据构建完整的上下文选择结构
static ContextSelectionData initializeContextData({
Novel? novel,
List<NovelSettingItem>? settings,
List<SettingGroup>? settingGroups,
List<NovelSnippet>? snippets,
ContextSelectionData? initialSelections,
}) {
//AppLogger.d('ContextSelectionHelper', '🔧 初始化上下文选择数据');
ContextSelectionData contextData;
if (novel != null) {
// 🚀 使用小说数据构建完整的上下文选择结构
contextData = ContextSelectionDataBuilder.fromNovelWithContext(
novel,
settings: settings ?? [],
settingGroups: settingGroups ?? [],
snippets: snippets ?? [],
);
//AppLogger.d('ContextSelectionHelper', '✅ 从小说构建上下文数据成功: ${contextData.availableItems.length}个可选项');
} else {
// 🚀 创建演示数据作为回退
contextData = _createFallbackContextData();
//AppLogger.d('ContextSelectionHelper', '✅ 创建回退上下文数据: ${contextData.availableItems.length}个可选项');
}
// 🚀 如果有初始选择,应用到构建的数据中
if (initialSelections != null && initialSelections.selectedCount > 0) {
contextData = contextData.applyPresetSelections(initialSelections);
//AppLogger.d('ContextSelectionHelper', '✅ 应用初始选择: ${contextData.selectedCount}个已选项');
}
return contextData;
}
/// 处理上下文选择变化
///
/// 这是核心方法,用于正确处理级联菜单的选择变化
/// [currentData] 当前的上下文选择数据
/// [newData] 从下拉菜单组件返回的新选择数据
/// [isAddOperation] 是否为添加操作true=添加false=删除)
static ContextSelectionData handleSelectionChanged(
ContextSelectionData currentData,
ContextSelectionData newData, {
bool isAddOperation = true,
}) {
//AppLogger.d('ContextSelectionHelper', '🔄 处理上下文选择变化');
//AppLogger.d('ContextSelectionHelper', '当前选择数: ${currentData.selectedCount}');
//AppLogger.d('ContextSelectionHelper', '新数据选择数: ${newData.selectedCount}');
//AppLogger.d('ContextSelectionHelper', '操作类型: ${isAddOperation ? "添加" : "删除"}');
// 🚀 关键修复:直接使用新的选择数据,而不是合并
// 下拉菜单组件已经处理了选择/取消选择的逻辑,我们只需要接受结果
// 确保新数据具有完整的菜单结构
if (newData.availableItems.length < currentData.availableItems.length) {
// 如果新数据的菜单结构不完整,保持当前的菜单结构,只更新选择状态
//AppLogger.d('ContextSelectionHelper', '🔧 修复不完整的菜单结构');
// 重建具有完整结构的数据
final updatedData = currentData.copyWith(
selectedItems: {},
flatItems: currentData.flatItems.map(
(key, value) => MapEntry(key, value.copyWith(selectionState: SelectionState.unselected)),
),
);
// 应用新的选择
ContextSelectionData result = updatedData;
for (final selectedItem in newData.selectedItems.values) {
if (result.flatItems.containsKey(selectedItem.id)) {
result = result.selectItem(selectedItem.id);
}
}
//AppLogger.d('ContextSelectionHelper', '✅ 选择处理完成: ${result.selectedCount}个已选项');
return result;
} else {
// 菜单结构完整,直接使用新数据
//AppLogger.d('ContextSelectionHelper', '✅ 直接使用新选择数据: ${newData.selectedCount}个已选项');
return newData;
}
}
/// 从保存的上下文选择字符串恢复选择状态
///
/// [baseData] 基础的完整菜单结构数据
/// [savedContextSelectionsData] 保存的上下文选择JSON字符串
static ContextSelectionData restoreSelectionsFromSaved(
ContextSelectionData baseData,
String? savedContextSelectionsData,
) {
if (savedContextSelectionsData == null || savedContextSelectionsData.isEmpty) {
//AppLogger.d('ContextSelectionHelper', '📭 没有保存的上下文选择数据');
return baseData;
}
try {
// 🚀 解析保存的选择数据
final savedSelections = _parseSavedContextSelections(
savedContextSelectionsData,
baseData.novelId,
);
if (savedSelections.selectedCount > 0) {
// 应用保存的选择到基础数据
final restoredData = baseData.applyPresetSelections(savedSelections);
//AppLogger.d('ContextSelectionHelper', '✅ 恢复上下文选择: ${restoredData.selectedCount}个已选项');
return restoredData;
}
} catch (e) {
AppLogger.e('ContextSelectionHelper', '恢复上下文选择失败', e);
}
return baseData;
}
/// 解析保存的上下文选择数据
static ContextSelectionData _parseSavedContextSelections(String savedData, String novelId) {
try {
// 🚀 解析JSON数据
final jsonData = jsonDecode(savedData) as Map<String, dynamic>;
// 检查是否有selectedItems字段
if (!jsonData.containsKey('selectedItems')) {
AppLogger.w('ContextSelectionHelper', '保存的数据中没有selectedItems字段');
return ContextSelectionData(novelId: novelId, availableItems: [], flatItems: {});
}
final contextList = jsonData['selectedItems'] as List<dynamic>;
//AppLogger.d('ContextSelectionHelper', '解析保存的上下文选择: ${contextList.length}个项目');
// 将已选择的项目转换为ContextSelectionItem
final selectedItems = <String, ContextSelectionItem>{};
final availableItems = <ContextSelectionItem>[];
final flatItems = <String, ContextSelectionItem>{};
for (var itemData in contextList) {
final item = ContextSelectionItem(
id: itemData['id'] ?? '',
title: itemData['title'] ?? '',
type: ContextSelectionType.values.firstWhere(
(type) => type.displayName == itemData['type'],
orElse: () => ContextSelectionType.fullNovelText,
),
metadata: Map<String, dynamic>.from(itemData['metadata'] ?? {}),
parentId: itemData['parentId'],
selectionState: SelectionState.fullySelected, // 标记为已选择
);
selectedItems[item.id] = item;
availableItems.add(item);
flatItems[item.id] = item;
//AppLogger.d('ContextSelectionHelper', ' ✅ ${item.type.displayName}:${item.id} (${item.title})');
}
return ContextSelectionData(
novelId: novelId,
selectedItems: selectedItems,
availableItems: availableItems,
flatItems: flatItems,
);
} catch (e) {
AppLogger.e('ContextSelectionHelper', '解析保存的上下文选择数据失败', e);
return ContextSelectionData(novelId: novelId, availableItems: [], flatItems: {});
}
}
/// 获取用于保存的上下文选择字符串
///
/// [contextData] 当前的上下文选择数据
static String? getSelectionsForSave(ContextSelectionData? contextData) {
if (contextData == null || contextData.selectedCount == 0) {
return null;
}
try {
return contextData.toSaveString();
} catch (e) {
AppLogger.e('ContextSelectionHelper', '序列化上下文选择失败', e);
return null;
}
}
/// 清除所有选择
///
/// [currentData] 当前的上下文选择数据
static ContextSelectionData clearAllSelections(ContextSelectionData currentData) {
//AppLogger.d('ContextSelectionHelper', '🧹 清除所有上下文选择');
return currentData.copyWith(
selectedItems: {},
flatItems: currentData.flatItems.map(
(key, value) => MapEntry(key, value.copyWith(selectionState: SelectionState.unselected)),
),
);
}
/// 创建回退的上下文选择数据(用于没有小说数据的情况)
static ContextSelectionData _createFallbackContextData() {
final demoItems = [
ContextSelectionItem(
id: 'demo_full_novel',
title: 'Full Novel Text',
type: ContextSelectionType.fullNovelText,
subtitle: '包含所有小说文本,这将产生费用',
metadata: {'wordCount': 0},
),
ContextSelectionItem(
id: 'demo_full_outline',
title: 'Full Outline',
type: ContextSelectionType.fullOutline,
subtitle: '包含所有卷、章节和场景的完整大纲',
metadata: {'actCount': 0, 'chapterCount': 0, 'sceneCount': 0},
),
];
final flatItems = <String, ContextSelectionItem>{};
for (final item in demoItems) {
flatItems[item.id] = item;
}
return ContextSelectionData(
novelId: 'demo_novel',
availableItems: demoItems,
flatItems: flatItems,
);
}
/// 验证上下文选择数据的完整性
///
/// [contextData] 要验证的上下文选择数据
static bool validateContextData(ContextSelectionData? contextData) {
if (contextData == null) {
AppLogger.w('ContextSelectionHelper', '❌ 上下文数据为null');
return false;
}
if (contextData.availableItems.isEmpty) {
AppLogger.w('ContextSelectionHelper', '❌ 上下文数据无可用项目');
return false;
}
if (contextData.flatItems.isEmpty) {
AppLogger.w('ContextSelectionHelper', '❌ 上下文数据扁平化映射为空');
return false;
}
//AppLogger.d('ContextSelectionHelper', '✅ 上下文数据验证通过');
return true;
}
/// 获取上下文选择的统计信息
///
/// [contextData] 上下文选择数据
static Map<String, dynamic> getSelectionStats(ContextSelectionData? contextData) {
if (contextData == null) {
return {'totalItems': 0, 'selectedItems': 0, 'selectionTypes': []};
}
final selectedTypes = contextData.selectedItems.values
.map((item) => item.type.displayName)
.toSet()
.toList();
return {
'totalItems': contextData.availableItems.length,
'selectedItems': contextData.selectedCount,
'selectionTypes': selectedTypes,
'novelId': contextData.novelId,
};
}
}
/// 上下文选择数据扩展方法
extension ContextSelectionDataExt on ContextSelectionData {
/// 转换为保存字符串
String toSaveString() {
if (selectedCount == 0) return '';
final saveData = {
'novelId': novelId,
'selectedItems': selectedItems.values.map((item) => {
'id': item.id,
'title': item.title,
'type': item.type.displayName,
'metadata': item.metadata,
}).toList(),
};
return saveData.toString(); // 简化的序列化,可以根据需要使用 jsonEncode
}
}