马良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,220 @@
import 'dart:async';
import 'package:ainoval/blocs/editor/editor_bloc.dart';
import 'package:ainoval/screens/editor/controllers/editor_screen_controller.dart';
import 'package:ainoval/components/editable_title.dart';
import 'package:ainoval/utils/debouncer.dart' as debouncer;
import 'package:ainoval/utils/logger.dart';
import 'package:ainoval/screens/editor/widgets/custom_dropdown.dart';
import 'package:ainoval/screens/editor/widgets/menu_builder.dart';
import 'package:ainoval/utils/web_theme.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:provider/provider.dart';
class ChapterSection extends StatefulWidget {
const ChapterSection({
super.key, // Will be replaced by chapterKey if passed
required this.title,
required this.scenes,
required this.actId,
required this.chapterId,
required this.editorBloc,
this.chapterIndex, // 添加章节序号参数
this.chapterKey, // New GlobalKey parameter
});
final String title;
final List<Widget> scenes;
final String actId;
final String chapterId;
final EditorBloc editorBloc;
final int? chapterIndex; // 章节在卷中的序号从1开始
final GlobalKey? chapterKey; // New GlobalKey parameter
@override
State<ChapterSection> createState() => _ChapterSectionState();
}
class _ChapterSectionState extends State<ChapterSection> {
late TextEditingController _chapterTitleController;
late debouncer.Debouncer _debouncer;
// 为章节创建一个ValueKey确保唯一性 - This will be overridden by widget.chapterKey if provided
// late final Key _chapterKey =
// ValueKey('chapter_${widget.actId}_${widget.chapterId}');
@override
void initState() {
super.initState();
_chapterTitleController = TextEditingController(text: widget.title);
_debouncer = debouncer.Debouncer();
}
@override
void didUpdateWidget(ChapterSection oldWidget) {
super.didUpdateWidget(oldWidget);
// 更新标题控制器
if (oldWidget.title != widget.title) {
_chapterTitleController.text = widget.title;
}
}
@override
void dispose() {
_debouncer.dispose();
_chapterTitleController.dispose();
super.dispose();
}
// 获取章节序号文本
String _getChapterIndexText() {
if (widget.chapterIndex == null) return '';
// 使用中文数字表示章节序号
final List<String> chineseNumbers = ['', '', '', '', '', '', '', '', '', '', ''];
if (widget.chapterIndex! <= 10) {
return '${chineseNumbers[widget.chapterIndex!]}章 · ';
} else if (widget.chapterIndex! < 20) {
return '第十${chineseNumbers[widget.chapterIndex! - 10]}章 · ';
} else {
// 对于更大的数字,直接使用阿拉伯数字
return '${widget.chapterIndex}章 · ';
}
}
// 手动触发加载场景的方法
void _loadScenes() {
AppLogger.i('ChapterSection', '手动触发加载章节场景: ${widget.actId} - ${widget.chapterId}');
try {
final controller = Provider.of<EditorScreenController>(context, listen: false);
controller.loadScenesForChapter(widget.actId, widget.chapterId);
} catch (e) {
// 如果无法获取控制器直接使用EditorBloc
widget.editorBloc.add(LoadMoreScenes(
fromChapterId: widget.chapterId,
direction: 'center',
actId: widget.actId,
chaptersLimit: 2,
preventFocusChange: true,
));
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Container(
key: widget.chapterKey, // Use the passed GlobalKey here
color: WebTheme.getBackgroundColor(context), // 使用动态背景色
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Chapter标题
Padding(
// 调整间距
padding: const EdgeInsets.fromLTRB(0, 8, 0, 24), // 调整上下间距
child: Row(
crossAxisAlignment: CrossAxisAlignment.center, // 垂直居中对齐
children: [
// 添加章节序号前缀
if (widget.chapterIndex != null)
Text(
_getChapterIndexText(),
style: WebTheme.getAlignedTextStyle(
baseStyle: theme.textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.w600,
color: WebTheme.getTextColor(context),
) ?? const TextStyle(),
),
),
// 可编辑的文本字段
Expanded(
child: EditableTitle(
// 保持 EditableTitle
initialText: widget.title,
style: WebTheme.getAlignedTextStyle(
baseStyle: theme.textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.w600,
color: WebTheme.getTextColor(context),
) ?? const TextStyle(),
),
onChanged: (value) {
// 使用防抖更新
_debouncer.run(() {
if (mounted) {
widget.editorBloc.add(UpdateChapterTitle(
actId: widget.actId,
chapterId: widget.chapterId,
title: value,
));
}
});
},
),
),
const SizedBox(width: 8), // 增加间距
// 替换为MenuBuilder
MenuBuilder.buildChapterMenu(
context: context,
editorBloc: widget.editorBloc,
actId: widget.actId,
chapterId: widget.chapterId,
onRenamePressed: () {
// 聚焦到标题编辑框
// 通过setState强制刷新使标题进入编辑状态
setState(() {});
},
),
],
),
),
// 场景列表
if (widget.scenes.isEmpty)
// 显示空章节的UI提供手动加载按钮
Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 16.0),
child: Center(
child: Column(
children: [
Icon(Icons.article_outlined,
size: 48, color: WebTheme.getSecondaryTextColor(context)),
const SizedBox(height: 16),
Text(
'章节 "${widget.title}" 暂无场景内容',
style: TextStyle(color: WebTheme.getSecondaryTextColor(context)),
),
const SizedBox(height: 8),
Text(
'请手动加载或等待自动加载',
style: TextStyle(color: WebTheme.getSecondaryTextColor(context), fontSize: 14),
),
const SizedBox(height: 24),
// 加载场景按钮
OutlinedButton.icon(
onPressed: _loadScenes,
icon: Icon(Icons.download, size: 18, color: WebTheme.getTextColor(context)),
label: Text('加载场景', style: TextStyle(color: WebTheme.getTextColor(context))),
style: OutlinedButton.styleFrom(
foregroundColor: WebTheme.getTextColor(context),
side: BorderSide.none, // 去掉边框
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
elevation: 0, // 去掉阴影
),
),
],
),
),
)
else
Column(children: widget.scenes),
// 移除添加新场景按钮 - 现在由EditorMainArea统一管理
],
),
);
}
}