diff --git a/backend/.env.example b/backend/.env.example index 51afd11..7e88607 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -8,7 +8,7 @@ # 应用配置 # ========================================== APP_NAME=MuMuAINovel -APP_VERSION=1.1.0 +APP_VERSION=1.1.1 APP_HOST=0.0.0.0 APP_PORT=8000 DEBUG=false diff --git a/backend/app/api/chapters.py b/backend/app/api/chapters.py index ccafe99..8dc109c 100644 --- a/backend/app/api/chapters.py +++ b/backend/app/api/chapters.py @@ -1128,6 +1128,27 @@ async def generate_chapter_content_stream( previous_content += smart_context['recent_summary'] + "\n\n" if smart_context['recent_full']: previous_content += smart_context['recent_full'] + + # 🔧 修复1-n模式重复问题: 提取上一章结尾作为精确衔接点 + if current_chapter.chapter_number > 1: + recent_chapters_parts = smart_context['recent_full'].split('===') + if len(recent_chapters_parts) >= 2: + # 提取最后一章(recent_full包含最近3章,最后一个是上一章) + last_chapter_content = recent_chapters_parts[-1].strip() + # 提取结尾500字 + last_chapter_ending = last_chapter_content[-600:] if len(last_chapter_content) > 600 else last_chapter_content + + previous_content += f"\n\n{'='*50}\n" + previous_content += f"【⚠️ 上一章结尾内容(必读,用于衔接)】\n" + previous_content += f"以下是上一章(第{current_chapter.chapter_number-1}章)的结尾部分:\n\n" + previous_content += last_chapter_ending + "\n" + previous_content += f"\n{'='*50}\n" + previous_content += f"【本章({current_chapter.chapter_number}章)创作要求】\n" + previous_content += f"1. 必须自然承接上述结尾的场景/情节/对话\n" + previous_content += f"2. 不要重复叙述上一章已经发生的事件\n" + previous_content += f"3. 从新的情节点、新的场景或新的时间点开始\n" + previous_content += f"4. 角色状态要延续,不要重新介绍已出场角色\n" + previous_content += f"{'='*50}\n" # 日志输出统计信息 stats = smart_context['stats'] diff --git a/backend/app/services/prompt_service.py b/backend/app/services/prompt_service.py index ea9d48a..6bc92be 100644 --- a/backend/app/services/prompt_service.py +++ b/backend/app/services/prompt_service.py @@ -604,24 +604,32 @@ class PromptService: - 不能出现与前文矛盾的内容 - 自然过渡,避免突兀的跳跃 -2. **情节推进**: -- 严格按照本章大纲展开情节 -- 推动故事向前发展 -- 保持与全书大纲的一致性 +2. **🔴 防止内容重复(关键)**: +- ⚠️ 仔细阅读【上一章结尾内容】,绝对不要重复叙述已经发生的事件 +- ⚠️ 本章必须从新的情节点开始,不要重新描述上一章的场景或对话 +- ⚠️ 如果上一章以某个动作或对话结束,本章应该从紧接着的下一个动作或反应开始 +- ⚠️ 角色状态应该延续而非重置,不要让角色重新经历上一章已经经历的心理过程 +- ⚠️ 场景转换要明确,如果是同一场景的延续,要从不同的视角或新的细节切入 -3. **角色一致性**: +3. **情节推进**: +- 严格按照本章大纲(expansion_plan)展开情节 +- 推动故事向前发展,不要原地踏步 +- 保持与全书大纲的一致性 +- 确保本章有独特的叙事价值,而非前章内容的重复 + +4. **角色一致性**: - 符合角色性格设定 - 延续角色在前文中的成长和变化 - 保持角色关系的连贯性 -4. **写作风格**: +5. **写作风格**: - 使用{narrative_perspective}视角 - **字数要求:目标{target_word_count}字,不得低于{target_word_count}字,建议控制在{target_word_count}至{max_word_count}字之间** - 语言自然流畅,避免AI痕迹 - 体现世界观特色 -5. **承上启下**: - - 开头自然衔接上一章结尾 +6. **承上启下**: + - 开头自然衔接上一章结尾(但不重复上一章内容) - 结尾为下一章做好铺垫 6. **记忆系统使用指南**: @@ -1067,6 +1075,14 @@ class PromptService: - 不要直接写到"解决困境",除非原大纲明确包含解决过程 - 如果看到【后一节】的内容,那些是禁区,绝不提前展开 +4. **🔴 相邻章节差异化约束(重要 - 防止内容重复)**: + - 每个章节必须有独特的开场方式(不同的场景、时间点、角色状态) + - 每个章节必须有独特的结束方式(不同的悬念、转折、情感收尾) + - key_events在相邻章节间绝不允许重叠,每章的关键事件必须完全不同 + - plot_summary必须描述该章的独特内容,不能与其他章节雷同 + - 即使是同一事件的不同阶段,也要明确区分"前、中、后"的具体内容 + - 例如:第1章可以是"发现线索",第2章必须是"追踪调查"而非再次"发现线索" + 【任务要求】 1. 深度分析该大纲的剧情容量和叙事节奏 2. 识别关键剧情点、冲突点和情感转折点(仅限当前大纲范围内) @@ -1081,12 +1097,13 @@ class PromptService: - conflict_type: 冲突类型(如:内心挣扎、人际冲突、环境挑战等) - estimated_words: 预计字数(建议2000-5000字) {scene_instruction} -4. 确保章节间: - - 衔接自然流畅 +5. 确保章节间: + - 衔接自然流畅(每章从不同的起点开始) - 剧情递进合理(但不超出当前大纲边界) - 节奏张弛有度 - - 每章都有明确的叙事价值 + - 每章都有明确且独特的叙事价值(不重复前一章的内容) - 最后一章结束时,剧情发展程度应恰好完成当前大纲描述的内容,不多不少 + - **关键事件无重叠**:仔细检查相邻章节的key_events,确保没有任何重复或雷同 【输出格式】 请严格按照以下JSON数组格式输出,不要添加任何其他文字: @@ -1154,6 +1171,14 @@ class PromptService: - 放慢叙事节奏,让读者充分体验当前阶段的剧情 - 每个章节都应该是当前大纲内容的不同侧面或阶段 +4. **🔴 相邻章节差异化约束(重要 - 防止内容重复)**: + - 每个章节必须有独特的开场方式(不同的场景、时间点、角色状态) + - 每个章节必须有独特的结束方式(不同的悬念、转折、情感收尾) + - key_events在相邻章节间绝不允许重叠,每章的关键事件必须完全不同 + - plot_summary必须描述该章的独特内容,不能与其他章节雷同 + - 特别注意与【已生成的前序章节】的差异化,避免重复已有内容 + - 即使是同一事件的不同阶段,也要明确区分"前、中、后"的具体内容 + 【任务要求】 1. 深度分析该大纲的剧情容量和叙事节奏 2. 识别关键剧情点、冲突点和情感转折点(仅限当前大纲范围内) @@ -1168,11 +1193,12 @@ class PromptService: - conflict_type: 冲突类型(如:内心挣扎、人际冲突、环境挑战等) - estimated_words: 预计字数(建议2000-5000字) {scene_instruction} -4. 确保章节间: - - 与前面章节衔接自然流畅 +5. 确保章节间: + - 与前面章节衔接自然流畅(每章从不同的起点开始) - 剧情递进合理(但不超出当前大纲边界) - 节奏张弛有度 - - 每章都有明确的叙事价值 + - 每章都有明确且独特的叙事价值(不重复前面章节的内容) + - **关键事件无重叠**:仔细检查本批次章节的key_events,以及与前序章节的key_events,确保没有任何重复或雷同 【输出格式】 请严格按照以下JSON数组格式输出,不要添加任何其他文字: diff --git a/frontend/package.json b/frontend/package.json index a743087..53224c5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "1.1.0", + "version": "1.1.1", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/components/AnnouncementModal.tsx b/frontend/src/components/AnnouncementModal.tsx index 3086392..6c6b9d2 100644 --- a/frontend/src/components/AnnouncementModal.tsx +++ b/frontend/src/components/AnnouncementModal.tsx @@ -31,15 +31,44 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday, return ( + 🎉 欢迎使用 AI小说创作助手 + + } open={visible} onCancel={onClose} footer={ - - @@ -49,6 +78,17 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday, styles={{ body: { padding: '24px', + background: 'var(--color-bg-container)', + }, + header: { + background: 'linear-gradient(135deg, rgba(77, 128, 136, 0.08) 0%, rgba(248, 246, 241, 0.95) 100%)', + borderBottom: '1px solid var(--color-border-secondary)', + padding: '20px 24px', + }, + footer: { + background: 'var(--color-bg-container)', + borderTop: '1px solid var(--color-border-secondary)', + padding: '16px 24px', }, }} > diff --git a/frontend/src/index.css b/frontend/src/index.css index 389593d..7de084e 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -320,6 +320,16 @@ body { transform: scale(1.15); } +/* 折叠状态下图标去边距并隐藏文字容器,确保居中 */ +.modern-sider.ant-layout-sider-collapsed .ant-menu-item .anticon { + margin-right: 0 !important; + font-size: 22px !important; +} + +.modern-sider.ant-layout-sider-collapsed .ant-menu-item .ant-menu-title-content { + display: none !important; +} + /* 选中项左侧指示条 */ .modern-sider .ant-menu-item-selected::before { content: ''; diff --git a/frontend/src/pages/Outline.tsx b/frontend/src/pages/Outline.tsx index 5818c30..1e1e177 100644 --- a/frontend/src/pages/Outline.tsx +++ b/frontend/src/pages/Outline.tsx @@ -887,6 +887,7 @@ export default function Outline() { modalApi.confirm({ title: '确认删除', icon: , + centered: true, content: (

此操作将删除大纲《{outlineTitle}》展开的所有 {data.chapter_count} 个章节。

diff --git a/frontend/src/pages/ProjectDetail.tsx b/frontend/src/pages/ProjectDetail.tsx index 9dc02e7..9629f77 100644 --- a/frontend/src/pages/ProjectDetail.tsx +++ b/frontend/src/pages/ProjectDetail.tsx @@ -179,6 +179,7 @@ export default function ProjectDetail() { }}>
{/* 现代化头部区域 */} @@ -822,7 +822,7 @@ export default function ProjectList() { padding: `${isMobile ? 16 : 24}px ${sidePadding}px`, paddingBottom: footerHeight + (isMobile ? 24 : 32), }}> -
+
{!Array.isArray(projects) || projects.length === 0 ? (
- {/* 头部标题区域 */} -
- - 赞助 MuMuAINovel - - - SUPPORT AI NOVEL CREATION - - -
- - 📚 MuMuAINovel - 基于 AI 的智能小说创作助手 - - - 支持多AI模型、智能向导、角色管理、章节编辑等强大功能 - -
-
- - {/* 赞助专属权益 */} -
- - <CheckCircleOutlined style={{ color: 'var(--color-success)', marginRight: '8px' }} /> - 赞助专属权益 - - - - {benefits.map((benefit, index) => ( - - -
- {benefit.icon} -
- {benefit.title} - - {benefit.description} - -
- - ))} -
-
- - {/* 选择金额 */} -
- - <HeartOutlined style={{ color: '#f5222d', marginRight: '8px' }} /> - 选择金额 - - - - {sponsorOptions.map((option, index) => ( - - handleCardClick(option)} - style={{ - textAlign: 'center', - borderRadius: '10px', - boxShadow: 'var(--shadow-card)', - cursor: 'pointer', - transition: 'all 0.3s', - border: '2px solid var(--color-border)' - }} - styles={{ - body: { padding: '20px 12px' } - }} - onMouseEnter={(e) => { - e.currentTarget.style.transform = 'translateY(-8px)'; - e.currentTarget.style.boxShadow = 'var(--shadow-elevated)'; - e.currentTarget.style.borderColor = 'var(--color-primary)'; - }} - onMouseLeave={(e) => { - e.currentTarget.style.transform = 'translateY(0)'; - e.currentTarget.style.boxShadow = 'var(--shadow-card)'; - e.currentTarget.style.borderColor = 'var(--color-border)'; - }} - > - - {option.description} - - - {option.label} - - - - ))} - -
- - - - {/* 感谢文案 */}
- - 💖 感谢您对 MuMuAINovel 项目的支持 - - - 您的赞助将帮助我们持续改进产品,提供更好的AI小说创作体验 - -
- - - - - + {/* 头部标题区域 */} +
+ + 赞助 MuMuAINovel + + + SUPPORT AI NOVEL CREATION + + +
+ + 📚 MuMuAINovel - 基于 AI 的智能小说创作助手 + + + 支持多AI模型、智能向导、角色管理、章节编辑等强大功能 + +
+
+ + {/* 赞助专属权益 */} +
+ + <CheckCircleOutlined style={{ color: 'var(--color-success)', marginRight: '8px' }} /> + 赞助专属权益 + + + + {benefits.map((benefit, index) => ( + + +
+ {benefit.icon} +
+ {benefit.title} + + {benefit.description} + +
+ + ))} +
+
+ + {/* 选择金额 */} +
+ + <HeartOutlined style={{ color: '#f5222d', marginRight: '8px' }} /> + 选择金额 + + + + {sponsorOptions.map((option, index) => ( + + handleCardClick(option)} + style={{ + textAlign: 'center', + borderRadius: '10px', + boxShadow: 'var(--shadow-card)', + cursor: 'pointer', + transition: 'all 0.3s', + border: '2px solid var(--color-border)' + }} + styles={{ + body: { padding: 'clamp(16px, 3vh, 20px) clamp(10px, 2vw, 12px)' } + }} + onMouseEnter={(e) => { + e.currentTarget.style.transform = 'translateY(-8px)'; + e.currentTarget.style.boxShadow = 'var(--shadow-elevated)'; + e.currentTarget.style.borderColor = 'var(--color-primary)'; + }} + onMouseLeave={(e) => { + e.currentTarget.style.transform = 'translateY(0)'; + e.currentTarget.style.boxShadow = 'var(--shadow-card)'; + e.currentTarget.style.borderColor = 'var(--color-border)'; + }} + > + + {option.description} + + + {option.label} + + + + ))} + +
+ + + + {/* 感谢文案 */} +
+ + 💖 感谢您对 MuMuAINovel 项目的支持 + + + 您的赞助将帮助我们持续改进产品,提供更好的AI小说创作体验 + +
+ + + + + +