update:1.更新灵感模式重试功能
This commit is contained in:
@@ -333,16 +333,43 @@ class PlotExpansionService:
|
|||||||
"""
|
"""
|
||||||
logger.info(f"根据规划创建 {len(chapter_plans)} 个章节记录")
|
logger.info(f"根据规划创建 {len(chapter_plans)} 个章节记录")
|
||||||
|
|
||||||
# 如果没有指定起始章节号,自动计算
|
# 如果没有指定起始章节号,根据大纲顺序自动计算
|
||||||
if start_chapter_number is None:
|
if start_chapter_number is None:
|
||||||
# 查询项目中已有章节的最大序号
|
# 1. 获取当前大纲信息
|
||||||
max_number_result = await db.execute(
|
outline_result = await db.execute(
|
||||||
select(func.max(Chapter.chapter_number))
|
select(Outline).where(Outline.id == outline_id)
|
||||||
.where(Chapter.project_id == project_id)
|
|
||||||
)
|
)
|
||||||
max_number = max_number_result.scalar()
|
current_outline = outline_result.scalar_one_or_none()
|
||||||
start_chapter_number = (max_number or 0) + 1
|
|
||||||
logger.info(f"自动计算起始章节号: {start_chapter_number} (当前最大序号: {max_number})")
|
if not current_outline:
|
||||||
|
raise ValueError(f"大纲 {outline_id} 不存在")
|
||||||
|
|
||||||
|
# 2. 查询所有在当前大纲之前的大纲(按order_index排序)
|
||||||
|
prev_outlines_result = await db.execute(
|
||||||
|
select(Outline)
|
||||||
|
.where(
|
||||||
|
Outline.project_id == project_id,
|
||||||
|
Outline.order_index < current_outline.order_index
|
||||||
|
)
|
||||||
|
.order_by(Outline.order_index)
|
||||||
|
)
|
||||||
|
prev_outlines = prev_outlines_result.scalars().all()
|
||||||
|
|
||||||
|
# 3. 计算前面所有大纲已展开的章节总数
|
||||||
|
total_prev_chapters = 0
|
||||||
|
for prev_outline in prev_outlines:
|
||||||
|
count_result = await db.execute(
|
||||||
|
select(func.count(Chapter.id))
|
||||||
|
.where(
|
||||||
|
Chapter.project_id == project_id,
|
||||||
|
Chapter.outline_id == prev_outline.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
total_prev_chapters += count_result.scalar() or 0
|
||||||
|
|
||||||
|
# 4. 起始章节号 = 前面所有大纲的章节数 + 1
|
||||||
|
start_chapter_number = total_prev_chapters + 1
|
||||||
|
logger.info(f"自动计算起始章节号: {start_chapter_number} (基于大纲order_index={current_outline.order_index}, 前置章节数={total_prev_chapters})")
|
||||||
|
|
||||||
chapters = []
|
chapters = []
|
||||||
for idx, plan in enumerate(chapter_plans):
|
for idx, plan in enumerate(chapter_plans):
|
||||||
@@ -376,6 +403,14 @@ class PlotExpansionService:
|
|||||||
await db.refresh(chapter)
|
await db.refresh(chapter)
|
||||||
|
|
||||||
logger.info(f"成功创建 {len(chapters)} 个章节记录(已保存展开规划数据)")
|
logger.info(f"成功创建 {len(chapters)} 个章节记录(已保存展开规划数据)")
|
||||||
|
|
||||||
|
# 重新排序当前大纲之后的所有章节
|
||||||
|
await self._renumber_subsequent_chapters(
|
||||||
|
project_id=project_id,
|
||||||
|
current_outline_id=outline_id,
|
||||||
|
db=db
|
||||||
|
)
|
||||||
|
|
||||||
return chapters
|
return chapters
|
||||||
|
|
||||||
async def _get_outline_context(
|
async def _get_outline_context(
|
||||||
@@ -717,6 +752,94 @@ class PlotExpansionService:
|
|||||||
}]
|
}]
|
||||||
|
|
||||||
|
|
||||||
|
async def _renumber_subsequent_chapters(
|
||||||
|
self,
|
||||||
|
project_id: str,
|
||||||
|
current_outline_id: str,
|
||||||
|
db: AsyncSession
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
重新计算当前大纲之后所有大纲的章节序号
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project_id: 项目ID
|
||||||
|
current_outline_id: 当前大纲ID
|
||||||
|
db: 数据库会话
|
||||||
|
"""
|
||||||
|
logger.info(f"开始重新排序大纲 {current_outline_id} 之后的所有章节")
|
||||||
|
|
||||||
|
# 1. 获取当前大纲信息
|
||||||
|
current_outline_result = await db.execute(
|
||||||
|
select(Outline).where(Outline.id == current_outline_id)
|
||||||
|
)
|
||||||
|
current_outline = current_outline_result.scalar_one_or_none()
|
||||||
|
|
||||||
|
if not current_outline:
|
||||||
|
logger.warning(f"大纲 {current_outline_id} 不存在,跳过重新排序")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 2. 获取当前大纲及之后的所有大纲(按order_index排序)
|
||||||
|
subsequent_outlines_result = await db.execute(
|
||||||
|
select(Outline)
|
||||||
|
.where(
|
||||||
|
Outline.project_id == project_id,
|
||||||
|
Outline.order_index >= current_outline.order_index
|
||||||
|
)
|
||||||
|
.order_by(Outline.order_index)
|
||||||
|
)
|
||||||
|
subsequent_outlines = subsequent_outlines_result.scalars().all()
|
||||||
|
|
||||||
|
# 3. 计算每个大纲的起始章节号
|
||||||
|
current_chapter_number = 1
|
||||||
|
|
||||||
|
# 先计算前面大纲的章节总数
|
||||||
|
prev_outlines_result = await db.execute(
|
||||||
|
select(Outline)
|
||||||
|
.where(
|
||||||
|
Outline.project_id == project_id,
|
||||||
|
Outline.order_index < current_outline.order_index
|
||||||
|
)
|
||||||
|
.order_by(Outline.order_index)
|
||||||
|
)
|
||||||
|
prev_outlines = prev_outlines_result.scalars().all()
|
||||||
|
|
||||||
|
for prev_outline in prev_outlines:
|
||||||
|
count_result = await db.execute(
|
||||||
|
select(func.count(Chapter.id))
|
||||||
|
.where(
|
||||||
|
Chapter.project_id == project_id,
|
||||||
|
Chapter.outline_id == prev_outline.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
current_chapter_number += count_result.scalar() or 0
|
||||||
|
|
||||||
|
# 4. 逐个大纲更新章节序号
|
||||||
|
updated_count = 0
|
||||||
|
for outline in subsequent_outlines:
|
||||||
|
# 获取该大纲的所有章节(按sub_index排序)
|
||||||
|
chapters_result = await db.execute(
|
||||||
|
select(Chapter)
|
||||||
|
.where(
|
||||||
|
Chapter.project_id == project_id,
|
||||||
|
Chapter.outline_id == outline.id
|
||||||
|
)
|
||||||
|
.order_by(Chapter.sub_index)
|
||||||
|
)
|
||||||
|
chapters = chapters_result.scalars().all()
|
||||||
|
|
||||||
|
# 更新每个章节的chapter_number
|
||||||
|
for chapter in chapters:
|
||||||
|
if chapter.chapter_number != current_chapter_number:
|
||||||
|
logger.debug(f"更新章节 {chapter.id}: {chapter.chapter_number} -> {current_chapter_number}")
|
||||||
|
chapter.chapter_number = current_chapter_number
|
||||||
|
updated_count += 1
|
||||||
|
current_chapter_number += 1
|
||||||
|
|
||||||
|
# 5. 提交更新
|
||||||
|
await db.commit()
|
||||||
|
logger.info(f"重新排序完成,共更新 {updated_count} 个章节的序号")
|
||||||
|
|
||||||
|
|
||||||
# 工厂函数
|
# 工厂函数
|
||||||
def create_plot_expansion_service(ai_service: AIService) -> PlotExpansionService:
|
def create_plot_expansion_service(ai_service: AIService) -> PlotExpansionService:
|
||||||
"""创建剧情展开服务实例"""
|
"""创建剧情展开服务实例"""
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ const Inspiration: React.FC = () => {
|
|||||||
const [projectTitle, setProjectTitle] = useState<string>('');
|
const [projectTitle, setProjectTitle] = useState<string>('');
|
||||||
const [progress, setProgress] = useState(0);
|
const [progress, setProgress] = useState(0);
|
||||||
const [progressMessage, setProgressMessage] = useState('');
|
const [progressMessage, setProgressMessage] = useState('');
|
||||||
|
const [errorDetails, setErrorDetails] = useState<string>(''); // 新增:错误详情
|
||||||
const [generationSteps, setGenerationSteps] = useState<{
|
const [generationSteps, setGenerationSteps] = useState<{
|
||||||
worldBuilding: 'pending' | 'processing' | 'completed' | 'error';
|
worldBuilding: 'pending' | 'processing' | 'completed' | 'error';
|
||||||
characters: 'pending' | 'processing' | 'completed' | 'error';
|
characters: 'pending' | 'processing' | 'completed' | 'error';
|
||||||
@@ -58,6 +59,10 @@ const Inspiration: React.FC = () => {
|
|||||||
outline: 'pending'
|
outline: 'pending'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 新增:保存生成数据,用于重试
|
||||||
|
const [generationData, setGenerationData] = useState<WizardData | null>(null);
|
||||||
|
const [worldBuildingResult, setWorldBuildingResult] = useState<any>(null);
|
||||||
|
|
||||||
// 滚动容器引用
|
// 滚动容器引用
|
||||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||||
const chatContainerRef = useRef<HTMLDivElement>(null);
|
const chatContainerRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -334,6 +339,8 @@ const Inspiration: React.FC = () => {
|
|||||||
setProjectTitle(data.title);
|
setProjectTitle(data.title);
|
||||||
setProgress(0);
|
setProgress(0);
|
||||||
setProgressMessage('开始创建项目...');
|
setProgressMessage('开始创建项目...');
|
||||||
|
setErrorDetails(''); // 清空错误详情
|
||||||
|
setGenerationData(data); // 保存数据用于重试
|
||||||
|
|
||||||
// 步骤1: 生成世界观并创建项目
|
// 步骤1: 生成世界观并创建项目
|
||||||
setGenerationSteps(prev => ({ ...prev, worldBuilding: 'processing' }));
|
setGenerationSteps(prev => ({ ...prev, worldBuilding: 'processing' }));
|
||||||
@@ -357,9 +364,12 @@ const Inspiration: React.FC = () => {
|
|||||||
},
|
},
|
||||||
onResult: (result) => {
|
onResult: (result) => {
|
||||||
setProjectId(result.project_id);
|
setProjectId(result.project_id);
|
||||||
|
setWorldBuildingResult(result); // 保存结果用于重试
|
||||||
setGenerationSteps(prev => ({ ...prev, worldBuilding: 'completed' }));
|
setGenerationSteps(prev => ({ ...prev, worldBuilding: 'completed' }));
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
|
console.error('世界观生成失败:', error);
|
||||||
|
setErrorDetails(`世界观生成失败: ${error}`);
|
||||||
setGenerationSteps(prev => ({ ...prev, worldBuilding: 'error' }));
|
setGenerationSteps(prev => ({ ...prev, worldBuilding: 'error' }));
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
},
|
},
|
||||||
@@ -370,11 +380,12 @@ const Inspiration: React.FC = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!worldResult?.project_id) {
|
if (!worldResult?.project_id) {
|
||||||
throw new Error('项目创建失败');
|
throw new Error('项目创建失败:未获取到项目ID');
|
||||||
}
|
}
|
||||||
|
|
||||||
const createdProjectId = worldResult.project_id;
|
const createdProjectId = worldResult.project_id;
|
||||||
setProjectId(createdProjectId);
|
setProjectId(createdProjectId);
|
||||||
|
setWorldBuildingResult(worldResult);
|
||||||
|
|
||||||
// 步骤2: 生成角色
|
// 步骤2: 生成角色
|
||||||
setGenerationSteps(prev => ({ ...prev, characters: 'processing' }));
|
setGenerationSteps(prev => ({ ...prev, characters: 'processing' }));
|
||||||
@@ -403,6 +414,8 @@ const Inspiration: React.FC = () => {
|
|||||||
setGenerationSteps(prev => ({ ...prev, characters: 'completed' }));
|
setGenerationSteps(prev => ({ ...prev, characters: 'completed' }));
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
|
console.error('角色生成失败:', error);
|
||||||
|
setErrorDetails(`角色生成失败: ${error}`);
|
||||||
setGenerationSteps(prev => ({ ...prev, characters: 'error' }));
|
setGenerationSteps(prev => ({ ...prev, characters: 'error' }));
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
},
|
},
|
||||||
@@ -433,6 +446,8 @@ const Inspiration: React.FC = () => {
|
|||||||
setGenerationSteps(prev => ({ ...prev, outline: 'completed' }));
|
setGenerationSteps(prev => ({ ...prev, outline: 'completed' }));
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
|
console.error('大纲生成失败:', error);
|
||||||
|
setErrorDetails(`大纲生成失败: ${error}`);
|
||||||
setGenerationSteps(prev => ({ ...prev, outline: 'error' }));
|
setGenerationSteps(prev => ({ ...prev, outline: 'error' }));
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
},
|
},
|
||||||
@@ -450,13 +465,171 @@ const Inspiration: React.FC = () => {
|
|||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const apiError = error as ApiError;
|
const apiError = error as ApiError;
|
||||||
message.error('创建项目失败:' + (apiError.response?.data?.detail || apiError.message || '未知错误'));
|
const errorMsg = apiError.response?.data?.detail || apiError.message || '未知错误';
|
||||||
setCurrentStep('genre');
|
console.error('创建项目失败:', errorMsg);
|
||||||
setGenerationSteps({
|
setErrorDetails(errorMsg);
|
||||||
worldBuilding: 'pending',
|
message.error('创建项目失败:' + errorMsg);
|
||||||
characters: 'pending',
|
// 不重置步骤,保持在generating状态以显示重试按钮
|
||||||
outline: 'pending'
|
} finally {
|
||||||
});
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重试世界观生成
|
||||||
|
const retryWorldBuilding = async () => {
|
||||||
|
if (!generationData) return;
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
setErrorDetails('');
|
||||||
|
setGenerationSteps(prev => ({ ...prev, worldBuilding: 'processing' }));
|
||||||
|
setProgressMessage('重新生成世界观...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const worldResult = await wizardStreamApi.generateWorldBuildingStream(
|
||||||
|
{
|
||||||
|
title: generationData.title,
|
||||||
|
description: generationData.description,
|
||||||
|
theme: generationData.theme,
|
||||||
|
genre: generationData.genre.join('、'),
|
||||||
|
narrative_perspective: generationData.narrative_perspective,
|
||||||
|
target_words: 100000,
|
||||||
|
chapter_count: 5,
|
||||||
|
character_count: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onProgress: (msg, prog) => {
|
||||||
|
setProgress(Math.floor(prog / 3));
|
||||||
|
setProgressMessage(msg);
|
||||||
|
},
|
||||||
|
onResult: (result) => {
|
||||||
|
setProjectId(result.project_id);
|
||||||
|
setWorldBuildingResult(result);
|
||||||
|
setGenerationSteps(prev => ({ ...prev, worldBuilding: 'completed' }));
|
||||||
|
message.success('世界观生成成功!');
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
console.error('世界观生成失败:', error);
|
||||||
|
setErrorDetails(`世界观生成失败: ${error}`);
|
||||||
|
setGenerationSteps(prev => ({ ...prev, worldBuilding: 'error' }));
|
||||||
|
},
|
||||||
|
onComplete: () => {
|
||||||
|
console.log('世界观重新生成完成');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (worldResult?.project_id) {
|
||||||
|
setProjectId(worldResult.project_id);
|
||||||
|
setWorldBuildingResult(worldResult);
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('重试世界观生成失败:', error);
|
||||||
|
setErrorDetails(error.message || '重试失败');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重试角色生成
|
||||||
|
const retryCharacters = async () => {
|
||||||
|
if (!generationData || !projectId || !worldBuildingResult) {
|
||||||
|
message.warning('请先完成世界观生成');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
setErrorDetails('');
|
||||||
|
setGenerationSteps(prev => ({ ...prev, characters: 'processing' }));
|
||||||
|
setProgressMessage('重新生成角色...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await wizardStreamApi.generateCharactersStream(
|
||||||
|
{
|
||||||
|
project_id: projectId,
|
||||||
|
count: 5,
|
||||||
|
world_context: {
|
||||||
|
time_period: worldBuildingResult.time_period || '',
|
||||||
|
location: worldBuildingResult.location || '',
|
||||||
|
atmosphere: worldBuildingResult.atmosphere || '',
|
||||||
|
rules: worldBuildingResult.rules || '',
|
||||||
|
},
|
||||||
|
theme: generationData.theme,
|
||||||
|
genre: generationData.genre.join('、'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onProgress: (msg, prog) => {
|
||||||
|
setProgress(33 + Math.floor(prog / 3));
|
||||||
|
setProgressMessage(msg);
|
||||||
|
},
|
||||||
|
onResult: (result) => {
|
||||||
|
console.log(`成功生成${result.characters?.length || 0}个角色`);
|
||||||
|
setGenerationSteps(prev => ({ ...prev, characters: 'completed' }));
|
||||||
|
message.success('角色生成成功!');
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
console.error('角色生成失败:', error);
|
||||||
|
setErrorDetails(`角色生成失败: ${error}`);
|
||||||
|
setGenerationSteps(prev => ({ ...prev, characters: 'error' }));
|
||||||
|
},
|
||||||
|
onComplete: () => {
|
||||||
|
console.log('角色重新生成完成');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('重试角色生成失败:', error);
|
||||||
|
setErrorDetails(error.message || '重试失败');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重试大纲生成
|
||||||
|
const retryOutline = async () => {
|
||||||
|
if (!generationData || !projectId) {
|
||||||
|
message.warning('请先完成世界观和角色生成');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
setErrorDetails('');
|
||||||
|
setGenerationSteps(prev => ({ ...prev, outline: 'processing' }));
|
||||||
|
setProgressMessage('重新生成大纲...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await wizardStreamApi.generateCompleteOutlineStream(
|
||||||
|
{
|
||||||
|
project_id: projectId,
|
||||||
|
chapter_count: 5,
|
||||||
|
narrative_perspective: generationData.narrative_perspective,
|
||||||
|
target_words: 100000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onProgress: (msg, prog) => {
|
||||||
|
setProgress(66 + Math.floor(prog / 3));
|
||||||
|
setProgressMessage(msg);
|
||||||
|
},
|
||||||
|
onResult: () => {
|
||||||
|
console.log('大纲生成完成');
|
||||||
|
setGenerationSteps(prev => ({ ...prev, outline: 'completed' }));
|
||||||
|
setProgress(100);
|
||||||
|
setProgressMessage('项目创建完成!');
|
||||||
|
setCurrentStep('complete');
|
||||||
|
message.success('大纲生成成功!项目创建完成!');
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
console.error('大纲生成失败:', error);
|
||||||
|
setErrorDetails(`大纲生成失败: ${error}`);
|
||||||
|
setGenerationSteps(prev => ({ ...prev, outline: 'error' }));
|
||||||
|
},
|
||||||
|
onComplete: () => {
|
||||||
|
console.log('大纲重新生成完成');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('重试大纲生成失败:', error);
|
||||||
|
setErrorDetails(error.message || '重试失败');
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -630,6 +803,10 @@ const Inspiration: React.FC = () => {
|
|||||||
return { icon: '○', color: '#d9d9d9' };
|
return { icon: '○', color: '#d9d9d9' };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const hasError = generationSteps.worldBuilding === 'error' ||
|
||||||
|
generationSteps.characters === 'error' ||
|
||||||
|
generationSteps.outline === 'error';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ textAlign: 'center', padding: '40px 20px' }}>
|
<div style={{ textAlign: 'center', padding: '40px 20px' }}>
|
||||||
<Title level={3} style={{ marginBottom: 32, color: '#fff' }}>
|
<Title level={3} style={{ marginBottom: 32, color: '#fff' }}>
|
||||||
@@ -639,7 +816,7 @@ const Inspiration: React.FC = () => {
|
|||||||
<Card style={{ marginBottom: 24 }}>
|
<Card style={{ marginBottom: 24 }}>
|
||||||
<Progress
|
<Progress
|
||||||
percent={progress}
|
percent={progress}
|
||||||
status={progress === 100 ? 'success' : 'active'}
|
status={hasError ? 'exception' : (progress === 100 ? 'success' : 'active')}
|
||||||
strokeColor={{
|
strokeColor={{
|
||||||
'0%': '#667eea',
|
'0%': '#667eea',
|
||||||
'100%': '#764ba2',
|
'100%': '#764ba2',
|
||||||
@@ -647,16 +824,33 @@ const Inspiration: React.FC = () => {
|
|||||||
style={{ marginBottom: 24 }}
|
style={{ marginBottom: 24 }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Paragraph style={{ fontSize: 16, marginBottom: 32, color: '#666' }}>
|
<Paragraph style={{ fontSize: 16, marginBottom: 32, color: hasError ? '#ff4d4f' : '#666' }}>
|
||||||
{progressMessage}
|
{progressMessage}
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
|
|
||||||
|
{/* 错误详情显示 */}
|
||||||
|
{errorDetails && (
|
||||||
|
<Card
|
||||||
|
size="small"
|
||||||
|
style={{
|
||||||
|
marginBottom: 24,
|
||||||
|
background: '#fff2f0',
|
||||||
|
borderColor: '#ffccc7',
|
||||||
|
textAlign: 'left'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text strong style={{ color: '#ff4d4f' }}>错误详情:</Text>
|
||||||
|
<br />
|
||||||
|
<Text style={{ color: '#666', fontSize: 14 }}>{errorDetails}</Text>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
<Space direction="vertical" size={16} style={{ width: '100%', maxWidth: 400, margin: '0 auto' }}>
|
<Space direction="vertical" size={16} style={{ width: '100%', maxWidth: 400, margin: '0 auto' }}>
|
||||||
{[
|
{[
|
||||||
{ key: 'worldBuilding', label: '生成世界观', step: generationSteps.worldBuilding },
|
{ key: 'worldBuilding', label: '生成世界观', step: generationSteps.worldBuilding, retry: retryWorldBuilding },
|
||||||
{ key: 'characters', label: '生成角色', step: generationSteps.characters },
|
{ key: 'characters', label: '生成角色', step: generationSteps.characters, retry: retryCharacters },
|
||||||
{ key: 'outline', label: '生成大纲', step: generationSteps.outline },
|
{ key: 'outline', label: '生成大纲', step: generationSteps.outline, retry: retryOutline },
|
||||||
].map(({ key, label, step }) => {
|
].map(({ key, label, step, retry }) => {
|
||||||
const status = getStepStatus(step);
|
const status = getStepStatus(step);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -666,17 +860,31 @@ const Inspiration: React.FC = () => {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
padding: '12px 20px',
|
padding: '12px 20px',
|
||||||
background: step === 'processing' ? '#f0f5ff' : '#fafafa',
|
background: step === 'processing' ? '#f0f5ff' : (step === 'error' ? '#fff2f0' : '#fafafa'),
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
border: `1px solid ${step === 'processing' ? '#d6e4ff' : '#f0f0f0'}`,
|
border: `1px solid ${step === 'processing' ? '#d6e4ff' : (step === 'error' ? '#ffccc7' : '#f0f0f0')}`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text style={{ fontSize: 16, fontWeight: step === 'processing' ? 600 : 400 }}>
|
<Text style={{ fontSize: 16, fontWeight: step === 'processing' ? 600 : 400 }}>
|
||||||
{label}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
<span style={{ fontSize: 20, color: status.color }}>
|
<Space>
|
||||||
{status.icon}
|
<span style={{ fontSize: 20, color: status.color }}>
|
||||||
</span>
|
{status.icon}
|
||||||
|
</span>
|
||||||
|
{step === 'error' && (
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
danger
|
||||||
|
onClick={retry}
|
||||||
|
loading={loading}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
重试
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -684,8 +892,27 @@ const Inspiration: React.FC = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Paragraph type="secondary" style={{ color: '#fff', opacity: 0.9 }}>
|
<Paragraph type="secondary" style={{ color: '#fff', opacity: 0.9 }}>
|
||||||
请耐心等待,AI正在为您精心创作...
|
{hasError ? '生成过程中出现错误,请点击重试按钮重新生成' : '请耐心等待,AI正在为您精心创作...'}
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
|
|
||||||
|
{hasError && (
|
||||||
|
<Space style={{ marginTop: 16 }}>
|
||||||
|
<Button
|
||||||
|
size="large"
|
||||||
|
onClick={() => {
|
||||||
|
setCurrentStep('confirm');
|
||||||
|
setGenerationSteps({
|
||||||
|
worldBuilding: 'pending',
|
||||||
|
characters: 'pending',
|
||||||
|
outline: 'pending'
|
||||||
|
});
|
||||||
|
setErrorDetails('');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
返回重新配置
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user