update:1.修复1-N模式下,手动创建大纲后,再手动创建章节无法找到大纲问题 2.更新章节管理-编辑内容/批量生成 增加默认字数缓存

This commit is contained in:
xiamuceer
2026-01-05 14:26:58 +08:00
parent 3fa23a1698
commit 6e603ee1a9
+56 -31
View File
@@ -14,8 +14,37 @@ import ChapterReader from '../components/ChapterReader';
const { TextArea } = Input; const { TextArea } = Input;
// localStorage 缓存键名
const WORD_COUNT_CACHE_KEY = 'chapter_default_word_count';
const DEFAULT_WORD_COUNT = 3000;
// 从 localStorage 读取缓存的字数
const getCachedWordCount = (): number => {
try {
const cached = localStorage.getItem(WORD_COUNT_CACHE_KEY);
if (cached) {
const value = parseInt(cached, 10);
if (!isNaN(value) && value >= 500 && value <= 10000) {
return value;
}
}
} catch (error) {
console.warn('读取字数缓存失败:', error);
}
return DEFAULT_WORD_COUNT;
};
// 保存字数到 localStorage
const setCachedWordCount = (value: number): void => {
try {
localStorage.setItem(WORD_COUNT_CACHE_KEY, String(value));
} catch (error) {
console.warn('保存字数缓存失败:', error);
}
};
export default function Chapters() { export default function Chapters() {
const { currentProject, chapters, setCurrentChapter, setCurrentProject } = useStore(); const { currentProject, chapters, outlines, setCurrentChapter, setCurrentProject } = useStore();
const [modal, contextHolder] = Modal.useModal(); const [modal, contextHolder] = Modal.useModal();
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
const [isEditorOpen, setIsEditorOpen] = useState(false); const [isEditorOpen, setIsEditorOpen] = useState(false);
@@ -28,7 +57,7 @@ export default function Chapters() {
const contentTextAreaRef = useRef<any>(null); const contentTextAreaRef = useRef<any>(null);
const [writingStyles, setWritingStyles] = useState<WritingStyle[]>([]); const [writingStyles, setWritingStyles] = useState<WritingStyle[]>([]);
const [selectedStyleId, setSelectedStyleId] = useState<number | undefined>(); const [selectedStyleId, setSelectedStyleId] = useState<number | undefined>();
const [targetWordCount, setTargetWordCount] = useState<number>(3000); const [targetWordCount, setTargetWordCount] = useState<number>(getCachedWordCount);
const [availableModels, setAvailableModels] = useState<Array<{ value: string, label: string }>>([]); const [availableModels, setAvailableModels] = useState<Array<{ value: string, label: string }>>([]);
const [selectedModel, setSelectedModel] = useState<string | undefined>(); const [selectedModel, setSelectedModel] = useState<string | undefined>();
const [batchSelectedModel, setBatchSelectedModel] = useState<string | undefined>(); // 批量生成的模型选择 const [batchSelectedModel, setBatchSelectedModel] = useState<string | undefined>(); // 批量生成的模型选择
@@ -940,13 +969,13 @@ export default function Chapters() {
// 设置批量生成的模型选择状态 // 设置批量生成的模型选择状态
setBatchSelectedModel(defaultModel || undefined); setBatchSelectedModel(defaultModel || undefined);
// 重置表单并设置初始值 // 重置表单并设置初始值(使用缓存的字数)
batchForm.setFieldsValue({ batchForm.setFieldsValue({
startChapterNumber: firstIncompleteChapter.chapter_number, startChapterNumber: firstIncompleteChapter.chapter_number,
count: 5, count: 5,
enableAnalysis: false, enableAnalysis: false,
styleId: selectedStyleId, styleId: selectedStyleId,
targetWordCount: 3000, targetWordCount: getCachedWordCount(),
}); });
setBatchGenerateVisible(true); setBatchGenerateVisible(true);
@@ -997,27 +1026,14 @@ export default function Chapters() {
tooltip="one-to-many模式下,章节必须关联到大纲" tooltip="one-to-many模式下,章节必须关联到大纲"
> >
<Select placeholder="请选择所属大纲"> <Select placeholder="请选择所属大纲">
{sortedChapters.length > 0 && (() => { {/* 直接使用 store 中的 outlines 数据,而不是从现有章节中提取 */}
// 从现有章节中提取大纲信息 {[...outlines]
const outlineMap = new Map(); .sort((a, b) => a.order_index - b.order_index)
sortedChapters.forEach(ch => { .map(outline => (
if (ch.outline_id && ch.outline_title) {
outlineMap.set(ch.outline_id, {
id: ch.outline_id,
title: ch.outline_title,
order: ch.outline_order || 0
});
}
});
const uniqueOutlines = Array.from(outlineMap.values())
.sort((a, b) => a.order - b.order);
return uniqueOutlines.map(outline => (
<Select.Option key={outline.id} value={outline.id}> <Select.Option key={outline.id} value={outline.id}>
{outline.order}{outline.title} {outline.order_index}{outline.title}
</Select.Option> </Select.Option>
)); ))}
})()}
</Select> </Select>
</Form.Item> </Form.Item>
@@ -1555,7 +1571,7 @@ export default function Chapters() {
{!isMobile && ( {!isMobile && (
<Tag color="blue"> <Tag color="blue">
{currentProject.outline_mode === 'one-to-one' {currentProject.outline_mode === 'one-to-one'
? '传统模式:章节由大纲一对一管理,请在大纲页面操作' ? '传统模式:章节由大纲管理,请在大纲页面操作'
: '细化模式:章节可在大纲页面展开'} : '细化模式:章节可在大纲页面展开'}
</Tag> </Tag>
)} )}
@@ -1993,7 +2009,7 @@ export default function Chapters() {
name="title" name="title"
tooltip={ tooltip={
currentProject.outline_mode === 'one-to-one' currentProject.outline_mode === 'one-to-one'
? "章节标题由大纲管理,建议在大纲页面统一修改" ? "章节标题由大纲管理,在大纲页面修改"
: "一对多模式下可以修改章节标题" : "一对多模式下可以修改章节标题"
} }
rules={ rules={
@@ -2011,7 +2027,7 @@ export default function Chapters() {
<Form.Item <Form.Item
label="章节序号" label="章节序号"
name="chapter_number" name="chapter_number"
tooltip="章节序号由大纲的顺序决定,无法修改。请在大纲页面使用上移/下移功能调整顺序" tooltip="章节序号不允许修改,请删除对应大纲,重新生成"
> >
<Input type="number" placeholder="章节排序序号" disabled /> <Input type="number" placeholder="章节排序序号" disabled />
</Form.Item> </Form.Item>
@@ -2161,7 +2177,7 @@ export default function Chapters() {
}}> }}>
<Form.Item <Form.Item
label="目标字数" label="目标字数"
tooltip="AI生成章节时的目标字数,实际可能略有偏差" tooltip="AI生成章节时的目标字数,实际可能略有偏差(修改后会自动记住)"
style={{ flex: 1, marginBottom: isMobile ? 16 : 0 }} style={{ flex: 1, marginBottom: isMobile ? 16 : 0 }}
> >
<InputNumber <InputNumber
@@ -2169,7 +2185,11 @@ export default function Chapters() {
max={10000} max={10000}
step={100} step={100}
value={targetWordCount} value={targetWordCount}
onChange={(value) => setTargetWordCount(value || 3000)} onChange={(value) => {
const newValue = value || DEFAULT_WORD_COUNT;
setTargetWordCount(newValue);
setCachedWordCount(newValue);
}}
disabled={isGenerating} disabled={isGenerating}
style={{ width: '100%' }} style={{ width: '100%' }}
formatter={(value) => `${value}`} formatter={(value) => `${value}`}
@@ -2353,7 +2373,7 @@ export default function Chapters() {
count: 5, count: 5,
enableAnalysis: true, // 强制启用同步分析 enableAnalysis: true, // 强制启用同步分析
styleId: selectedStyleId, styleId: selectedStyleId,
targetWordCount: 3000, targetWordCount: getCachedWordCount(),
model: selectedModel, model: selectedModel,
}} }}
> >
@@ -2425,7 +2445,7 @@ export default function Chapters() {
<Form.Item <Form.Item
label="目标字数" label="目标字数"
tooltip="AI生成章节时的目标字数,实际生成字数可能略有偏差" tooltip="AI生成章节时的目标字数,实际生成字数可能略有偏差(修改后会自动记住)"
> >
<Form.Item <Form.Item
name="targetWordCount" name="targetWordCount"
@@ -2440,10 +2460,15 @@ export default function Chapters() {
style={{ width: '100%' }} style={{ width: '100%' }}
formatter={(value) => `${value}`} formatter={(value) => `${value}`}
parser={(value) => value?.replace(' 字', '') as any} parser={(value) => value?.replace(' 字', '') as any}
onChange={(value) => {
if (value) {
setCachedWordCount(value);
}
}}
/> />
</Form.Item> </Form.Item>
<div style={{ color: '#666', fontSize: 12, marginTop: 4 }}> <div style={{ color: '#666', fontSize: 12, marginTop: 4 }}>
500-100003000 500-10000
</div> </div>
</Form.Item> </Form.Item>