diff --git a/frontend/src/components/AIProjectGenerator.tsx b/frontend/src/components/AIProjectGenerator.tsx index c1b5538..c08a0b8 100644 --- a/frontend/src/components/AIProjectGenerator.tsx +++ b/frontend/src/components/AIProjectGenerator.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; -import { Card, Button, Space, Typography, message, Progress } from 'antd'; +import { Card, Button, Space, Typography, message, Progress, theme } from 'antd'; import { CheckCircleOutlined, LoadingOutlined } from '@ant-design/icons'; import { wizardStreamApi } from '../services/api'; import type { ApiError } from '../types'; @@ -53,6 +53,9 @@ export const AIProjectGenerator: React.FC = ({ resumeProjectId }) => { const navigate = useNavigate(); + const { token } = theme.useToken(); + const alphaColor = (color: string, alpha: number) => + `color-mix(in srgb, ${color} ${(alpha * 100).toFixed(0)}%, transparent)`; // 状态管理 const [loading, setLoading] = useState(false); @@ -947,12 +950,45 @@ export const AIProjectGenerator: React.FC = ({ }; - // 获取步骤状态图标和颜色 + // 获取步骤状态图标和样式 const getStepStatus = (step: GenerationStep) => { - if (step === 'completed') return { icon: , color: 'var(--color-success)' }; - if (step === 'processing') return { icon: , color: 'var(--color-primary)' }; - if (step === 'error') return { icon: '✗', color: 'var(--color-error)' }; - return { icon: '○', color: 'var(--color-text-quaternary)' }; + if (step === 'completed') { + return { + icon: , + color: token.colorSuccess, + text: '已完成', + background: `linear-gradient(135deg, ${alphaColor(token.colorSuccess, 0.12)} 0%, ${token.colorBgContainer} 100%)`, + borderColor: alphaColor(token.colorSuccess, 0.28), + }; + } + + if (step === 'processing') { + return { + icon: , + color: token.colorPrimary, + text: '进行中', + background: `linear-gradient(135deg, ${alphaColor(token.colorPrimary, 0.14)} 0%, ${token.colorBgContainer} 100%)`, + borderColor: alphaColor(token.colorPrimary, 0.32), + }; + } + + if (step === 'error') { + return { + icon: '✕', + color: token.colorError, + text: '失败', + background: `linear-gradient(135deg, ${alphaColor(token.colorError, 0.12)} 0%, ${token.colorBgContainer} 100%)`, + borderColor: alphaColor(token.colorError, 0.32), + }; + } + + return { + icon: '○', + color: token.colorTextQuaternary, + text: '等待中', + background: token.colorFillQuaternary, + borderColor: token.colorBorderSecondary, + }; }; const hasError = generationSteps.worldBuilding === 'error' || @@ -960,95 +996,215 @@ export const AIProjectGenerator: React.FC = ({ generationSteps.characters === 'error' || generationSteps.outline === 'error'; + const progressAccentColor = hasError + ? token.colorError + : progress === 100 + ? token.colorSuccess + : token.colorPrimary; + + const stepItems = [ + { key: 'worldBuilding', label: '生成世界观', step: generationSteps.worldBuilding }, + { key: 'careers', label: '生成职业体系', step: generationSteps.careers }, + { key: 'characters', label: '生成角色', step: generationSteps.characters }, + { key: 'outline', label: '生成大纲', step: generationSteps.outline }, + ]; + + const availableViewportHeight = isMobile + ? 'calc(100dvh - 96px)' + : 'calc(100dvh - 128px)'; + // 渲染生成进度页面 const renderGenerating = () => ( -
- + <div style={{ - marginBottom: 32, - color: 'var(--color-text-primary)', - wordBreak: 'break-word', - whiteSpace: 'normal', - overflowWrap: 'break-word' + marginBottom: 14, + padding: isMobile ? '18px 16px' : '24px 24px 20px', + borderRadius: 18, + border: `1px solid ${alphaColor(hasError ? token.colorError : token.colorPrimary, 0.18)}`, + background: `linear-gradient(135deg, ${alphaColor(token.colorPrimary, 0.12)} 0%, ${token.colorBgContainer} 48%, ${alphaColor(hasError ? token.colorError : token.colorSuccess, hasError ? 0.08 : 0.04)} 100%)`, + boxShadow: `0 12px 28px ${alphaColor(token.colorText, 0.06)}`, + textAlign: 'center', }} > - 正在为《{config.title}》生成内容 - - - - + > + 正在为《{config.title}》生成内容 + - {progressMessage} + {hasError + ? '生成流程中断,已保留当前进度与上下文信息,可从失败步骤继续重试。' + : '系统会依次生成世界观、职业体系、角色与大纲,请耐心等待。'} +
- {errorDetails && ( - +
+
- 错误详情: -
+
+ + 当前进度 + + + {progressMessage || '准备生成...'} + +
+ +
+ + {progress}% + +
+
+ + +
+ + {errorDetails && ( +
+ + 错误详情 + {errorDetails} - +
)} - - {[ - { key: 'worldBuilding', label: '生成世界观', step: generationSteps.worldBuilding }, - { key: 'careers', label: '生成职业体系', step: generationSteps.careers }, - { key: 'characters', label: '生成角色', step: generationSteps.characters }, - { key: 'outline', label: '生成大纲', step: generationSteps.outline }, - ].map(({ key, label, step }) => { + {stepItems.map(({ key, label, step }) => { const status = getStepStatus(step); return (
= ({ display: 'flex', alignItems: 'center', justifyContent: 'space-between', - padding: isMobile ? '10px 12px' : '12px 20px', - background: step === 'processing' ? 'var(--color-info-bg)' : (step === 'error' ? 'var(--color-error-bg)' : 'var(--color-bg-layout)'), - borderRadius: 8, - border: `1px solid ${step === 'processing' ? 'var(--color-info-border)' : (step === 'error' ? 'var(--color-error-border)' : 'var(--color-border-secondary)')}`, - gap: '8px', + padding: isMobile ? '10px 12px' : '12px 14px', + background: status.background, + borderRadius: 14, + border: `1px solid ${status.borderColor}`, + gap: 12, maxWidth: '100%', - overflow: 'hidden' + overflow: 'hidden', }} > +
+ + {status.icon} + + +
+ + {label} + + + {status.text} + +
+
+ - {label} - - - {status.icon} - + {status.text} +
); })} -
+
- {hasError ? '生成过程中出现错误,请点击重试按钮重新生成' : '请耐心等待,AI正在为您精心创作...'} + {hasError ? '可点击下方智能重试,从失败节点继续生成,避免重复执行已完成步骤。' : '请勿关闭页面,生成完成后将自动进入项目详情页。'} {hasError && ( - + )} - );