update:1.优化灵感模式重试功能

This commit is contained in:
xiamuceer
2025-11-20 14:04:41 +08:00
parent 80989271e7
commit 697cfc48bb
+187 -76
View File
@@ -61,6 +61,7 @@ const Inspiration: React.FC = () => {
// 新增:保存生成数据,用于重试
const [generationData, setGenerationData] = useState<WizardData | null>(null);
// 保存世界观生成结果,用于后续步骤
const [worldBuildingResult, setWorldBuildingResult] = useState<any>(null);
// 滚动容器引用
@@ -364,13 +365,14 @@ const Inspiration: React.FC = () => {
},
onResult: (result) => {
setProjectId(result.project_id);
setWorldBuildingResult(result); // 保存结果用于重试
setWorldBuildingResult(result); // 保存世界观结果
setGenerationSteps(prev => ({ ...prev, worldBuilding: 'completed' }));
},
onError: (error) => {
console.error('世界观生成失败:', error);
setErrorDetails(`世界观生成失败: ${error}`);
setGenerationSteps(prev => ({ ...prev, worldBuilding: 'error' }));
setLoading(false); // 确保错误时解除加载状态
throw new Error(error);
},
onComplete: () => {
@@ -385,7 +387,7 @@ const Inspiration: React.FC = () => {
const createdProjectId = worldResult.project_id;
setProjectId(createdProjectId);
setWorldBuildingResult(worldResult);
setWorldBuildingResult(worldResult); // 保存世界观结果
// 步骤2: 生成角色
setGenerationSteps(prev => ({ ...prev, characters: 'processing' }));
@@ -417,6 +419,7 @@ const Inspiration: React.FC = () => {
console.error('角色生成失败:', error);
setErrorDetails(`角色生成失败: ${error}`);
setGenerationSteps(prev => ({ ...prev, characters: 'error' }));
setLoading(false); // 确保错误时解除加载状态
throw new Error(error);
},
onComplete: () => {
@@ -449,6 +452,7 @@ const Inspiration: React.FC = () => {
console.error('大纲生成失败:', error);
setErrorDetails(`大纲生成失败: ${error}`);
setGenerationSteps(prev => ({ ...prev, outline: 'error' }));
setLoading(false); // 确保错误时解除加载状态
throw new Error(error);
},
onComplete: () => {
@@ -470,20 +474,49 @@ const Inspiration: React.FC = () => {
setErrorDetails(errorMsg);
message.error('创建项目失败:' + errorMsg);
// 不重置步骤,保持在generating状态以显示重试按钮
} finally {
setLoading(false); // 确保在错误时也设置loading为false
}
};
// 智能重试:从失败的步骤继续生成
const handleSmartRetry = async () => {
if (!generationData) {
message.warning('缺少生成数据');
return;
}
setLoading(true);
setErrorDetails('');
try {
// 判断从哪个步骤开始重试
if (generationSteps.worldBuilding === 'error') {
// 世界观失败,从世界观开始重新生成
message.info('从世界观步骤开始重新生成...');
await retryFromWorldBuilding();
} else if (generationSteps.characters === 'error') {
// 角色失败,从角色开始生成
message.info('从角色步骤继续生成...');
await retryFromCharacters();
} else if (generationSteps.outline === 'error') {
// 大纲失败,从大纲开始生成
message.info('从大纲步骤继续生成...');
await retryFromOutline();
}
} catch (error: any) {
console.error('智能重试失败:', error);
message.error('重试失败:' + (error.message || '未知错误'));
setLoading(false);
}
};
// 重试世界观生成
const retryWorldBuilding = async () => {
// 世界观步骤重新开始
const retryFromWorldBuilding = async () => {
if (!generationData) return;
setLoading(true);
setErrorDetails('');
setGenerationSteps(prev => ({ ...prev, worldBuilding: 'processing' }));
setProgressMessage('重新生成世界观...');
try {
const worldResult = await wizardStreamApi.generateWorldBuildingStream(
{
@@ -505,43 +538,42 @@ const Inspiration: React.FC = () => {
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' }));
setLoading(false);
throw new Error(error);
},
onComplete: () => {
console.log('世界观重新生成完成');
}
}
);
if (worldResult?.project_id) {
setProjectId(worldResult.project_id);
setWorldBuildingResult(worldResult);
if (!worldResult?.project_id) {
throw new Error('项目创建失败:未获取到项目ID');
}
// 继续生成角色和大纲
await continueFromCharacters(worldResult);
} catch (error: any) {
console.error('重试世界观生成失败:', error);
setErrorDetails(error.message || '重试失败');
} finally {
setLoading(false);
throw error;
}
};
// 重试角色生成
const retryCharacters = async () => {
// 从角色步骤继续
const retryFromCharacters = async () => {
if (!generationData || !projectId || !worldBuildingResult) {
message.warning('请先完成世界观生成');
message.warning('缺少必要数据,无法从角色步骤继续');
setLoading(false);
return;
}
setLoading(true);
setErrorDetails('');
setGenerationSteps(prev => ({ ...prev, characters: 'processing' }));
setProgressMessage('重新生成角色...');
try {
await wizardStreamApi.generateCharactersStream(
{
@@ -564,38 +596,38 @@ const Inspiration: React.FC = () => {
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' }));
setLoading(false);
throw new Error(error);
},
onComplete: () => {
console.log('角色重新生成完成');
}
}
);
// 继续生成大纲
await continueFromOutline();
} catch (error: any) {
console.error('重试角色生成失败:', error);
setErrorDetails(error.message || '重试失败');
} finally {
setLoading(false);
throw error;
}
};
// 重试大纲生成
const retryOutline = async () => {
// 从大纲步骤继续
const retryFromOutline = async () => {
if (!generationData || !projectId) {
message.warning('请先完成世界观和角色生成');
message.warning('缺少必要数据,无法从大纲步骤继续');
setLoading(false);
return;
}
setLoading(true);
setErrorDetails('');
setGenerationSteps(prev => ({ ...prev, outline: 'processing' }));
setProgressMessage('重新生成大纲...');
try {
await wizardStreamApi.generateCompleteOutlineStream(
{
@@ -612,29 +644,127 @@ const Inspiration: React.FC = () => {
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' }));
setLoading(false);
throw new Error(error);
},
onComplete: () => {
console.log('大纲重新生成完成');
}
}
);
} catch (error: any) {
console.error('重试大纲生成失败:', error);
setErrorDetails(error.message || '重试失败');
} finally {
// 全部完成
setProgress(100);
setProgressMessage('项目创建完成!');
setCurrentStep('complete');
message.success('项目创建成功!');
setLoading(false);
} catch (error: any) {
throw error;
}
};
// 从角色步骤开始的完整流程(世界观成功后调用)
const continueFromCharacters = async (worldResult: any) => {
if (!generationData || !worldResult?.project_id) return;
try {
// 生成角色
setGenerationSteps(prev => ({ ...prev, characters: 'processing' }));
setProgressMessage('正在生成角色...');
await wizardStreamApi.generateCharactersStream(
{
project_id: worldResult.project_id,
count: 5,
world_context: {
time_period: worldResult.time_period || '',
location: worldResult.location || '',
atmosphere: worldResult.atmosphere || '',
rules: worldResult.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' }));
},
onError: (error) => {
console.error('角色生成失败:', error);
setErrorDetails(`角色生成失败: ${error}`);
setGenerationSteps(prev => ({ ...prev, characters: 'error' }));
setLoading(false);
throw new Error(error);
},
onComplete: () => {
console.log('角色生成完成');
}
}
);
// 生成大纲
await continueFromOutline();
} catch (error: any) {
console.error('继续生成失败:', error);
throw error;
}
};
// 从大纲步骤开始的完整流程(角色成功后调用)
const continueFromOutline = async () => {
if (!generationData || !projectId) return;
setGenerationSteps(prev => ({ ...prev, outline: 'processing' }));
setProgressMessage('正在生成大纲...');
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' }));
},
onError: (error) => {
console.error('大纲生成失败:', error);
setErrorDetails(`大纲生成失败: ${error}`);
setGenerationSteps(prev => ({ ...prev, outline: 'error' }));
setLoading(false);
throw new Error(error);
},
onComplete: () => {
console.log('大纲生成完成');
}
}
);
// 全部完成
setProgress(100);
setProgressMessage('项目创建完成!');
setCurrentStep('complete');
message.success('项目创建成功!');
setLoading(false);
};
const handleConfirmGenres = async () => {
if (selectedOptions.length === 0) {
message.warning('请至少选择一个类型');
@@ -847,10 +977,10 @@ const Inspiration: React.FC = () => {
<Space direction="vertical" size={16} style={{ width: '100%', maxWidth: 400, margin: '0 auto' }}>
{[
{ key: 'worldBuilding', label: '生成世界观', step: generationSteps.worldBuilding, retry: retryWorldBuilding },
{ key: 'characters', label: '生成角色', step: generationSteps.characters, retry: retryCharacters },
{ key: 'outline', label: '生成大纲', step: generationSteps.outline, retry: retryOutline },
].map(({ key, label, step, retry }) => {
{ key: 'worldBuilding', label: '生成世界观', step: generationSteps.worldBuilding },
{ key: 'characters', label: '生成角色', step: generationSteps.characters },
{ key: 'outline', label: '生成大纲', step: generationSteps.outline },
].map(({ key, label, step }) => {
const status = getStepStatus(step);
return (
<div
@@ -868,23 +998,9 @@ const Inspiration: React.FC = () => {
<Text style={{ fontSize: 16, fontWeight: step === 'processing' ? 600 : 400 }}>
{label}
</Text>
<Space>
<span style={{ fontSize: 20, color: status.color }}>
{status.icon}
</span>
{step === 'error' && (
<Button
type="primary"
size="small"
danger
onClick={retry}
loading={loading}
disabled={loading}
>
</Button>
)}
</Space>
<span style={{ fontSize: 20, color: status.color }}>
{status.icon}
</span>
</div>
);
})}
@@ -898,18 +1014,13 @@ const Inspiration: React.FC = () => {
{hasError && (
<Space style={{ marginTop: 16 }}>
<Button
type="primary"
size="large"
onClick={() => {
setCurrentStep('confirm');
setGenerationSteps({
worldBuilding: 'pending',
characters: 'pending',
outline: 'pending'
});
setErrorDetails('');
}}
onClick={handleSmartRetry}
loading={loading}
disabled={loading}
>
</Button>
</Space>
)}