马良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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,500 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:ainoval/models/novel_setting_item.dart';
import 'package:ainoval/models/ai_context_tracking.dart';
import 'package:ainoval/blocs/setting/setting_bloc.dart';
import 'package:ainoval/utils/web_theme.dart';
import 'package:ainoval/widgets/common/top_toast.dart';
/// 设定追踪配置标签页
class SettingTrackingTab extends StatefulWidget {
final NovelSettingItem settingItem;
final String novelId;
final Function(NovelSettingItem) onItemUpdated;
const SettingTrackingTab({
Key? key,
required this.settingItem,
required this.novelId,
required this.onItemUpdated,
}) : super(key: key);
@override
State<SettingTrackingTab> createState() => _SettingTrackingTabState();
}
class _SettingTrackingTabState extends State<SettingTrackingTab> {
late NameAliasTracking _nameAliasTracking;
late AIContextTracking _aiContextTracking;
late SettingReferenceUpdate _referenceUpdatePolicy;
bool _hasChanges = false;
bool _isSaving = false;
@override
void initState() {
super.initState();
_nameAliasTracking = widget.settingItem.nameAliasTracking;
_aiContextTracking = widget.settingItem.aiContextTracking;
_referenceUpdatePolicy = widget.settingItem.referenceUpdatePolicy;
}
@override
Widget build(BuildContext context) {
final isDark = WebTheme.isDarkMode(context);
return SingleChildScrollView(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 名称/别名追踪设置
_buildNameAliasTrackingSection(isDark),
const SizedBox(height: 16),
// AI上下文追踪设置
_buildAIContextTrackingSection(isDark),
const SizedBox(height: 16),
// 引用更新策略设置
_buildReferenceUpdateSection(isDark),
const SizedBox(height: 20),
// 保存按钮
if (_hasChanges) _buildSaveButton(isDark),
],
),
);
}
/// 构建名称/别名追踪设置区域
Widget _buildNameAliasTrackingSection(bool isDark) {
return _buildSettingSection(
title: '名称/别名追踪',
description: '控制是否通过名称和别名来追踪此设定条目',
icon: Icons.label,
iconColor: Colors.blue,
child: Column(
children: NameAliasTracking.values.map((option) {
return _buildRadioTile<NameAliasTracking>(
value: option,
groupValue: _nameAliasTracking,
title: option.displayName,
description: option.description,
onChanged: (value) {
if (value != null) {
setState(() {
_nameAliasTracking = value;
_hasChanges = true;
});
}
},
isDark: isDark,
);
}).toList(),
),
);
}
/// 构建AI上下文追踪设置区域
Widget _buildAIContextTrackingSection(bool isDark) {
return _buildSettingSection(
title: 'AI上下文',
description: '控制此设定条目如何包含在AI上下文中',
icon: Icons.psychology,
iconColor: Colors.purple,
child: Column(
children: AIContextTracking.values.map((option) {
return _buildRadioTile<AIContextTracking>(
value: option,
groupValue: _aiContextTracking,
title: option.displayName,
description: option.description,
onChanged: (value) {
if (value != null) {
setState(() {
_aiContextTracking = value;
_hasChanges = true;
});
}
},
isDark: isDark,
isRecommended: option == AIContextTracking.detected,
);
}).toList(),
),
);
}
/// 构建引用更新策略设置区域
Widget _buildReferenceUpdateSection(bool isDark) {
return _buildSettingSection(
title: '引用更新策略',
description: '当修改此设定时,如何处理引用此设定的其他内容',
icon: Icons.update,
iconColor: Colors.orange,
child: Column(
children: SettingReferenceUpdate.values.map((option) {
return _buildRadioTile<SettingReferenceUpdate>(
value: option,
groupValue: _referenceUpdatePolicy,
title: option.displayName,
description: option.description,
onChanged: (value) {
if (value != null) {
setState(() {
_referenceUpdatePolicy = value;
_hasChanges = true;
});
}
},
isDark: isDark,
isRecommended: option == SettingReferenceUpdate.ask,
);
}).toList(),
),
);
}
/// 构建设置区域的通用框架
Widget _buildSettingSection({
required String title,
required String description,
required IconData icon,
required Color iconColor,
required Widget child,
}) {
final isDark = WebTheme.isDarkMode(context);
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isDark ? WebTheme.darkGrey100 : WebTheme.grey50,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isDark ? WebTheme.darkGrey700 : WebTheme.grey200,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题区域
Row(
children: [
Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: iconColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
icon,
size: 18,
color: iconColor,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: WebTheme.getTextColor(context),
),
),
const SizedBox(height: 4),
Text(
description,
style: TextStyle(
fontSize: 12,
color: WebTheme.getSecondaryTextColor(context),
height: 1.4,
),
),
],
),
),
],
),
const SizedBox(height: 16),
// 选项内容
child,
],
),
);
}
/// 构建单选按钮瓦片
Widget _buildRadioTile<T>({
required T value,
required T groupValue,
required String title,
required String description,
required ValueChanged<T?> onChanged,
required bool isDark,
bool isRecommended = false,
}) {
final isSelected = value == groupValue;
return Container(
margin: const EdgeInsets.only(bottom: 8),
decoration: BoxDecoration(
color: isSelected
? (isDark ? WebTheme.darkGrey700 : Colors.blue.withOpacity(0.1))
: WebTheme.getSurfaceColor(context),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: isSelected
? Colors.blue
: (isDark ? WebTheme.darkGrey600 : WebTheme.grey300),
width: isSelected ? 2 : 1,
),
),
child: InkWell(
borderRadius: BorderRadius.circular(8),
onTap: () => onChanged(value),
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
// 单选按钮
Radio<T>(
value: value,
groupValue: groupValue,
onChanged: onChanged,
activeColor: Colors.blue,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
const SizedBox(width: 12),
// 内容区域
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
title,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: isSelected
? Colors.blue
: WebTheme.getTextColor(context),
),
),
if (isRecommended) ...[
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: Colors.green.withOpacity(0.1),
borderRadius: BorderRadius.circular(4),
border: Border.all(
color: Colors.green.withOpacity(0.3),
),
),
child: Text(
'推荐',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w500,
color: Colors.green,
),
),
),
],
],
),
const SizedBox(height: 4),
Text(
description,
style: TextStyle(
fontSize: 12,
color: WebTheme.getSecondaryTextColor(context),
height: 1.3,
),
),
],
),
),
],
),
),
),
);
}
/// 构建保存按钮
Widget _buildSaveButton(bool isDark) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isDark ? WebTheme.darkGrey800 : WebTheme.grey50,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isDark ? WebTheme.darkGrey700 : WebTheme.grey200,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'保存更改',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: WebTheme.getTextColor(context),
),
),
const SizedBox(height: 8),
Text(
'您的追踪配置已修改,点击保存以应用更改。',
style: TextStyle(
fontSize: 13,
color: WebTheme.getSecondaryTextColor(context),
),
),
const SizedBox(height: 16),
Row(
children: [
// 重置按钮
TextButton(
onPressed: _isSaving ? null : _resetChanges,
child: Text(
'重置',
style: TextStyle(
color: WebTheme.getSecondaryTextColor(context),
),
),
),
const SizedBox(width: 12),
// 保存按钮
Expanded(
child: ElevatedButton(
onPressed: _isSaving ? null : _saveChanges,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: _isSaving
? Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
const SizedBox(width: 8),
Text('保存中...'),
],
)
: Text('保存更改'),
),
),
],
),
],
),
);
}
/// 重置更改
void _resetChanges() {
setState(() {
_nameAliasTracking = widget.settingItem.nameAliasTracking;
_aiContextTracking = widget.settingItem.aiContextTracking;
_referenceUpdatePolicy = widget.settingItem.referenceUpdatePolicy;
_hasChanges = false;
});
TopToast.info(context, '已重置所有更改');
}
/// 保存更改
Future<void> _saveChanges() async {
if (widget.settingItem.id == null) return;
setState(() {
_isSaving = true;
});
try {
// 更新设定项目
final updatedItem = widget.settingItem.copyWith(
nameAliasTracking: _nameAliasTracking,
aiContextTracking: _aiContextTracking,
referenceUpdatePolicy: _referenceUpdatePolicy,
);
// 先更新本地状态
setState(() {
_hasChanges = false;
_isSaving = false;
});
// 立即通知父组件
widget.onItemUpdated(updatedItem);
// 显示成功提示
TopToast.success(context, '追踪配置已保存');
// 异步保存到后端不阻塞UI
_saveToBackendAsync(updatedItem);
} catch (e) {
setState(() {
_isSaving = false;
});
TopToast.error(context, '保存失败: ${e.toString()}');
}
}
/// 异步保存到后端
Future<void> _saveToBackendAsync(NovelSettingItem updatedItem) async {
try {
// 通过BLoC更新后端
context.read<SettingBloc>().add(UpdateSettingItem(
novelId: widget.novelId,
itemId: widget.settingItem.id!,
item: updatedItem,
));
} catch (e) {
// 静默处理错误,不干扰用户体验
}
}
}