style:1.重构整个项目的主题颜色,样式风格采用中国风元素 2.优化更新日志逻辑,不再间隔1h自动刷新过于频繁触发403响应
This commit is contained in:
@@ -44,11 +44,11 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
resumeProjectId
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
|
||||
// 状态管理
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [projectId, setProjectId] = useState<string>('');
|
||||
|
||||
|
||||
// SSE流式进度状态
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [progressMessage, setProgressMessage] = useState('');
|
||||
@@ -58,7 +58,7 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
characters: 'pending',
|
||||
outline: 'pending'
|
||||
});
|
||||
|
||||
|
||||
// 保存生成数据,用于重试
|
||||
const [generationData, setGenerationData] = useState<GenerationConfig | null>(null);
|
||||
// 保存世界观生成结果,用于后续步骤
|
||||
@@ -132,7 +132,7 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
// 世界观已完成,从角色开始
|
||||
message.info('世界观已完成,从角色步骤继续...');
|
||||
setGenerationSteps({ worldBuilding: 'completed', characters: 'processing', outline: 'pending' });
|
||||
|
||||
|
||||
// 获取世界观数据
|
||||
const worldResult = {
|
||||
project_id: projectIdParam,
|
||||
@@ -143,7 +143,7 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
};
|
||||
setWorldBuildingResult(worldResult);
|
||||
setProgress(33);
|
||||
|
||||
|
||||
await resumeFromCharacters(data, worldResult);
|
||||
} else if (wizardStep === 2) {
|
||||
// 世界观和角色已完成,从大纲开始
|
||||
@@ -297,7 +297,7 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
message.success('项目创建成功!正在进入项目...');
|
||||
clearStorage();
|
||||
setLoading(false);
|
||||
|
||||
|
||||
onComplete(pid);
|
||||
setTimeout(() => {
|
||||
navigate(`/project/${pid}`);
|
||||
@@ -319,7 +319,7 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
// 步骤1: 生成世界观并创建项目
|
||||
setGenerationSteps(prev => ({ ...prev, worldBuilding: 'processing' }));
|
||||
setProgressMessage('正在生成世界观...');
|
||||
|
||||
|
||||
const worldResult = await wizardStreamApi.generateWorldBuildingStream(
|
||||
{
|
||||
title: data.title,
|
||||
@@ -367,7 +367,7 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
// 步骤2: 生成角色
|
||||
setGenerationSteps(prev => ({ ...prev, characters: 'processing' }));
|
||||
setProgressMessage('正在生成角色...');
|
||||
|
||||
|
||||
await wizardStreamApi.generateCharactersStream(
|
||||
{
|
||||
project_id: createdProjectId,
|
||||
@@ -406,7 +406,7 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
// 步骤3: 生成大纲
|
||||
setGenerationSteps(prev => ({ ...prev, outline: 'processing' }));
|
||||
setProgressMessage('正在生成大纲...');
|
||||
|
||||
|
||||
await wizardStreamApi.generateCompleteOutlineStream(
|
||||
{
|
||||
project_id: createdProjectId,
|
||||
@@ -441,15 +441,15 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
setProgressMessage('项目创建完成!正在跳转...');
|
||||
message.success('项目创建成功!正在进入项目...');
|
||||
clearStorage();
|
||||
|
||||
|
||||
// 调用完成回调
|
||||
onComplete(createdProjectId);
|
||||
|
||||
|
||||
// 延迟1秒后自动跳转到项目详情页
|
||||
setTimeout(() => {
|
||||
navigate(`/project/${createdProjectId}`);
|
||||
}, 1000);
|
||||
|
||||
|
||||
} catch (error) {
|
||||
const apiError = error as ApiError;
|
||||
const errorMsg = apiError.response?.data?.detail || apiError.message || '未知错误';
|
||||
@@ -634,11 +634,11 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
setProgressMessage('项目创建完成!正在跳转...');
|
||||
message.success('项目创建成功!正在进入项目...');
|
||||
setLoading(false);
|
||||
|
||||
|
||||
// 调用完成回调
|
||||
if (projectId) {
|
||||
onComplete(projectId);
|
||||
|
||||
|
||||
// 延迟1秒后自动跳转到项目详情页
|
||||
setTimeout(() => {
|
||||
navigate(`/project/${projectId}`);
|
||||
@@ -733,11 +733,11 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
setProgressMessage('项目创建完成!正在跳转...');
|
||||
message.success('项目创建成功!正在进入项目...');
|
||||
setLoading(false);
|
||||
|
||||
|
||||
// 调用完成回调
|
||||
if (projectId) {
|
||||
onComplete(projectId);
|
||||
|
||||
|
||||
// 延迟1秒后自动跳转到项目详情页
|
||||
setTimeout(() => {
|
||||
navigate(`/project/${projectId}`);
|
||||
@@ -748,15 +748,15 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
|
||||
// 获取步骤状态图标和颜色
|
||||
const getStepStatus = (step: GenerationStep) => {
|
||||
if (step === 'completed') return { icon: <CheckCircleOutlined />, color: '#52c41a' };
|
||||
if (step === 'processing') return { icon: <LoadingOutlined />, color: '#1890ff' };
|
||||
if (step === 'error') return { icon: '✗', color: '#ff4d4f' };
|
||||
return { icon: '○', color: '#d9d9d9' };
|
||||
if (step === 'completed') return { icon: <CheckCircleOutlined />, color: 'var(--color-success)' };
|
||||
if (step === 'processing') return { icon: <LoadingOutlined />, color: 'var(--color-primary)' };
|
||||
if (step === 'error') return { icon: '✗', color: 'var(--color-error)' };
|
||||
return { icon: '○', color: 'var(--color-text-quaternary)' };
|
||||
};
|
||||
|
||||
const hasError = generationSteps.worldBuilding === 'error' ||
|
||||
generationSteps.characters === 'error' ||
|
||||
generationSteps.outline === 'error';
|
||||
generationSteps.characters === 'error' ||
|
||||
generationSteps.outline === 'error';
|
||||
|
||||
// 渲染生成进度页面
|
||||
const renderGenerating = () => (
|
||||
@@ -770,7 +770,7 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
level={isMobile ? 4 : 3}
|
||||
style={{
|
||||
marginBottom: 32,
|
||||
color: '#fff',
|
||||
color: 'var(--color-text-primary)',
|
||||
wordBreak: 'break-word',
|
||||
whiteSpace: 'normal',
|
||||
overflowWrap: 'break-word'
|
||||
@@ -784,8 +784,8 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
percent={progress}
|
||||
status={hasError ? 'exception' : (progress === 100 ? 'success' : 'active')}
|
||||
strokeColor={{
|
||||
'0%': '#667eea',
|
||||
'100%': '#764ba2',
|
||||
'0%': 'var(--color-primary)',
|
||||
'100%': 'var(--color-primary-active)',
|
||||
}}
|
||||
style={{ marginBottom: 24 }}
|
||||
/>
|
||||
@@ -794,7 +794,7 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
style={{
|
||||
fontSize: isMobile ? 14 : 16,
|
||||
marginBottom: 32,
|
||||
color: hasError ? '#ff4d4f' : '#666',
|
||||
color: hasError ? 'var(--color-error)' : 'var(--color-text-secondary)',
|
||||
wordBreak: 'break-word',
|
||||
whiteSpace: 'normal',
|
||||
overflowWrap: 'break-word'
|
||||
@@ -808,18 +808,18 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
size="small"
|
||||
style={{
|
||||
marginBottom: 24,
|
||||
background: '#fff2f0',
|
||||
borderColor: '#ffccc7',
|
||||
background: 'var(--color-error-bg)',
|
||||
borderColor: 'var(--color-error-border)',
|
||||
textAlign: 'left',
|
||||
maxWidth: '100%',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
<Text strong style={{ color: '#ff4d4f' }}>错误详情:</Text>
|
||||
<Text strong style={{ color: 'var(--color-error)' }}>错误详情:</Text>
|
||||
<br />
|
||||
<Text
|
||||
style={{
|
||||
color: '#666',
|
||||
color: 'var(--color-text-secondary)',
|
||||
fontSize: 14,
|
||||
wordBreak: 'break-word',
|
||||
whiteSpace: 'normal',
|
||||
@@ -855,9 +855,9 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
padding: isMobile ? '10px 12px' : '12px 20px',
|
||||
background: step === 'processing' ? '#f0f5ff' : (step === 'error' ? '#fff2f0' : '#fafafa'),
|
||||
background: step === 'processing' ? 'var(--color-info-bg)' : (step === 'error' ? 'var(--color-error-bg)' : 'var(--color-bg-layout)'),
|
||||
borderRadius: 8,
|
||||
border: `1px solid ${step === 'processing' ? '#d6e4ff' : (step === 'error' ? '#ffccc7' : '#f0f0f0')}`,
|
||||
border: `1px solid ${step === 'processing' ? 'var(--color-info-border)' : (step === 'error' ? 'var(--color-error-border)' : 'var(--color-border-secondary)')}`,
|
||||
gap: '8px',
|
||||
maxWidth: '100%',
|
||||
overflow: 'hidden'
|
||||
@@ -894,7 +894,7 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
<Paragraph
|
||||
type="secondary"
|
||||
style={{
|
||||
color: '#fff',
|
||||
color: 'var(--color-text-secondary)',
|
||||
opacity: 0.9,
|
||||
wordBreak: 'break-word',
|
||||
whiteSpace: 'normal',
|
||||
@@ -904,7 +904,7 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
>
|
||||
{hasError ? '生成过程中出现错误,请点击重试按钮重新生成' : '请耐心等待,AI正在为您精心创作...'}
|
||||
</Paragraph>
|
||||
|
||||
|
||||
{hasError && (
|
||||
<Space style={{ marginTop: 16 }}>
|
||||
<Button
|
||||
@@ -918,7 +918,7 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
|
||||
</Button>
|
||||
</Space>
|
||||
)}
|
||||
|
||||
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
|
||||
<div style={{
|
||||
marginBottom: '16px',
|
||||
fontSize: '16px',
|
||||
color: '#666',
|
||||
color: 'var(--color-text-secondary)',
|
||||
lineHeight: '1.6',
|
||||
}}>
|
||||
<p>👋 欢迎加入我们的交流群!</p>
|
||||
@@ -72,18 +72,18 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
|
||||
<li>🐛 反馈问题和建议</li>
|
||||
<li>📚 分享创作经验和灵感</li>
|
||||
</ul>
|
||||
<p style={{ fontWeight: 600, color: '#333', marginBottom: '16px' }}>
|
||||
<p style={{ fontWeight: 600, color: 'var(--color-text-primary)', marginBottom: '16px' }}>
|
||||
扫描下方二维码加入交流群:
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'flex-start',
|
||||
gap: '24px',
|
||||
padding: '20px',
|
||||
background: '#f5f5f5',
|
||||
background: 'var(--color-bg-layout)',
|
||||
borderRadius: '8px',
|
||||
flexWrap: 'wrap',
|
||||
}}>
|
||||
@@ -94,7 +94,7 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
|
||||
alignItems: 'center',
|
||||
minWidth: '280px',
|
||||
}}>
|
||||
<p style={{ fontWeight: 600, color: '#333', marginBottom: '12px', fontSize: '15px' }}>
|
||||
<p style={{ fontWeight: 600, color: 'var(--color-text-primary)', marginBottom: '12px', fontSize: '15px' }}>
|
||||
QQ交流群
|
||||
</p>
|
||||
{!qqImageError ? (
|
||||
@@ -102,7 +102,7 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
background: '#fff',
|
||||
background: 'var(--color-bg-container)',
|
||||
borderRadius: '8px',
|
||||
padding: '8px',
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
|
||||
@@ -128,7 +128,7 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
background: '#fff',
|
||||
background: 'var(--color-bg-container)',
|
||||
borderRadius: '8px',
|
||||
color: '#999',
|
||||
}}>
|
||||
@@ -144,7 +144,7 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
|
||||
alignItems: 'center',
|
||||
minWidth: '280px',
|
||||
}}>
|
||||
<p style={{ fontWeight: 600, color: '#333', marginBottom: '12px', fontSize: '15px' }}>
|
||||
<p style={{ fontWeight: 600, color: 'var(--color-text-primary)', marginBottom: '12px', fontSize: '15px' }}>
|
||||
微信交流群
|
||||
</p>
|
||||
{!wxImageError ? (
|
||||
@@ -152,7 +152,7 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
background: '#fff',
|
||||
background: 'var(--color-bg-container)',
|
||||
borderRadius: '8px',
|
||||
padding: '8px',
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
|
||||
@@ -178,7 +178,7 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
background: '#fff',
|
||||
background: 'var(--color-bg-container)',
|
||||
borderRadius: '8px',
|
||||
color: '#999',
|
||||
}}>
|
||||
@@ -187,15 +187,15 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div style={{
|
||||
marginTop: '20px',
|
||||
padding: '12px',
|
||||
background: '#fff7e6',
|
||||
background: 'var(--color-warning-bg)',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid #ffd591',
|
||||
border: '1px solid var(--color-warning-border)',
|
||||
fontSize: '14px',
|
||||
color: '#ad6800',
|
||||
color: 'var(--color-warning)',
|
||||
}}>
|
||||
💡 提示:选择"今日内不再展示"当天不再显示,选择"永不再展示"将永久隐藏此公告
|
||||
</div>
|
||||
|
||||
@@ -46,10 +46,11 @@ export default function AppFooter() {
|
||||
right: 0,
|
||||
backdropFilter: 'blur(20px) saturate(180%)',
|
||||
WebkitBackdropFilter: 'blur(20px) saturate(180%)',
|
||||
borderTop: '1px solid rgba(255, 255, 255, 0.2)',
|
||||
borderTop: '1px solid var(--color-border)',
|
||||
padding: isMobile ? '8px 12px' : '10px 16px',
|
||||
zIndex: 100,
|
||||
boxShadow: '0 -4px 16px rgba(0, 0, 0, 0.1), 0 -1px 4px rgba(0, 0, 0, 0.06)',
|
||||
boxShadow: 'var(--shadow-card)',
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.8)', // 半透明背景以支持 backdrop-filter
|
||||
}}
|
||||
>
|
||||
<div
|
||||
@@ -77,29 +78,26 @@ export default function AppFooter() {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 4,
|
||||
color: '#fff',
|
||||
textShadow: '0 1px 3px rgba(0, 0, 0, 0.3)',
|
||||
color: 'var(--color-primary)',
|
||||
cursor: hasUpdate ? 'pointer' : 'default',
|
||||
}}
|
||||
>
|
||||
<strong style={{ color: '#fff' }}>{VERSION_INFO.projectName}</strong>
|
||||
<strong style={{ color: 'var(--color-text-primary)' }}>{VERSION_INFO.projectName}</strong>
|
||||
<span>{getVersionString()}</span>
|
||||
</Text>
|
||||
</Tooltip>
|
||||
</Badge>
|
||||
<Divider type="vertical" style={{ margin: '0 4px', borderColor: 'rgba(255, 255, 255, 0.3)' }} />
|
||||
<Divider type="vertical" style={{ margin: '0 4px', borderColor: 'var(--color-border)' }} />
|
||||
<Button
|
||||
type="primary"
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<GiftOutlined />}
|
||||
onClick={() => window.open('https://mumuverse.space:1588/', '_blank')}
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
||||
border: 'none',
|
||||
boxShadow: '0 2px 8px rgba(102, 126, 234, 0.4)',
|
||||
color: 'var(--color-text-secondary)',
|
||||
fontSize: 11,
|
||||
height: 24,
|
||||
padding: '0 8px',
|
||||
padding: '0 4px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 4,
|
||||
@@ -107,7 +105,7 @@ export default function AppFooter() {
|
||||
>
|
||||
赞助
|
||||
</Button>
|
||||
<Divider type="vertical" style={{ margin: '0 4px', borderColor: 'rgba(255, 255, 255, 0.3)' }} />
|
||||
<Divider type="vertical" style={{ margin: '0 4px', borderColor: 'var(--color-border)' }} />
|
||||
<Link
|
||||
href={VERSION_INFO.githubUrl}
|
||||
target="_blank"
|
||||
@@ -117,8 +115,7 @@ export default function AppFooter() {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 4,
|
||||
color: '#fff',
|
||||
textShadow: '0 1px 3px rgba(0, 0, 0, 0.3)',
|
||||
color: 'var(--color-text-secondary)',
|
||||
}}
|
||||
>
|
||||
<GithubOutlined style={{ fontSize: 12 }} />
|
||||
@@ -126,8 +123,7 @@ export default function AppFooter() {
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 10,
|
||||
color: 'rgba(255, 255, 255, 0.8)',
|
||||
textShadow: '0 1px 2px rgba(0, 0, 0, 0.3)',
|
||||
color: 'var(--color-text-tertiary)',
|
||||
}}
|
||||
>
|
||||
<ClockCircleOutlined style={{ fontSize: 10, marginRight: 4 }} />
|
||||
@@ -139,7 +135,7 @@ export default function AppFooter() {
|
||||
<Space
|
||||
direction="horizontal"
|
||||
size={12}
|
||||
split={<Divider type="vertical" style={{ borderColor: 'rgba(255, 255, 255, 0.3)' }} />}
|
||||
split={<Divider type="vertical" style={{ borderColor: 'var(--color-border)' }} />}
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
@@ -156,8 +152,8 @@ export default function AppFooter() {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
color: '#fff',
|
||||
textShadow: '0 1px 3px rgba(0, 0, 0, 0.3)',
|
||||
color: 'var(--color-text-secondary)',
|
||||
textShadow: 'none',
|
||||
cursor: hasUpdate ? 'pointer' : 'default',
|
||||
transition: 'all 0.3s',
|
||||
}}
|
||||
@@ -172,7 +168,7 @@ export default function AppFooter() {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<strong style={{ color: '#fff' }}>{VERSION_INFO.projectName}</strong>
|
||||
<strong style={{ color: 'var(--color-text-primary)' }}>{VERSION_INFO.projectName}</strong>
|
||||
<span>{getVersionString()}</span>
|
||||
</Text>
|
||||
</Tooltip>
|
||||
@@ -188,8 +184,7 @@ export default function AppFooter() {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
color: '#fff',
|
||||
textShadow: '0 1px 3px rgba(0, 0, 0, 0.3)',
|
||||
color: 'var(--color-text-secondary)',
|
||||
}}
|
||||
>
|
||||
<GithubOutlined style={{ fontSize: 13 }} />
|
||||
@@ -203,8 +198,7 @@ export default function AppFooter() {
|
||||
rel="noopener noreferrer"
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: '#fff',
|
||||
textShadow: '0 1px 3px rgba(0, 0, 0, 0.3)',
|
||||
color: 'var(--color-text-secondary)',
|
||||
}}
|
||||
>
|
||||
LinuxDO 社区
|
||||
@@ -216,9 +210,9 @@ export default function AppFooter() {
|
||||
icon={<GiftOutlined style={{ fontSize: 14 }} />}
|
||||
onClick={() => window.open('https://mumuverse.space:1588/', '_blank')}
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
||||
background: 'var(--color-primary)',
|
||||
border: 'none',
|
||||
boxShadow: '0 4px 12px rgba(102, 126, 234, 0.5)',
|
||||
boxShadow: '0 4px 12px rgba(77, 128, 136, 0.3)',
|
||||
fontSize: 13,
|
||||
height: 32,
|
||||
padding: '0 20px',
|
||||
@@ -250,8 +244,7 @@ export default function AppFooter() {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
color: '#fff',
|
||||
textShadow: '0 1px 3px rgba(0, 0, 0, 0.3)',
|
||||
color: 'var(--color-text-secondary)',
|
||||
}}
|
||||
>
|
||||
<CopyrightOutlined style={{ fontSize: 11 }} />
|
||||
@@ -265,8 +258,7 @@ export default function AppFooter() {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 4,
|
||||
color: 'rgba(255, 255, 255, 0.9)',
|
||||
textShadow: '0 1px 3px rgba(0, 0, 0, 0.3)',
|
||||
color: 'var(--color-text-tertiary)',
|
||||
}}
|
||||
>
|
||||
<ClockCircleOutlined style={{ fontSize: 12 }} />
|
||||
@@ -280,12 +272,12 @@ export default function AppFooter() {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 4,
|
||||
color: '#fff',
|
||||
textShadow: '0 1px 3px rgba(0, 0, 0, 0.3)',
|
||||
color: 'var(--color-text-secondary)',
|
||||
textShadow: '0 1px 3px rgba(0, 0, 0, 0.05)',
|
||||
}}
|
||||
>
|
||||
<span>Made with</span>
|
||||
<HeartFilled style={{ color: '#ff4d4f', fontSize: 11 }} />
|
||||
<HeartFilled style={{ color: 'var(--color-error)', fontSize: 11 }} />
|
||||
<span>by {VERSION_INFO.author}</span>
|
||||
</Text>
|
||||
</Space>
|
||||
|
||||
@@ -18,7 +18,7 @@ export const cardStyles = {
|
||||
// height: 320,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
borderColor: '#1890ff',
|
||||
borderColor: 'var(--color-info)',
|
||||
borderRadius: 12,
|
||||
} as CSSProperties,
|
||||
|
||||
@@ -27,19 +27,20 @@ export const cardStyles = {
|
||||
// height: 320,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
borderColor: '#52c41a',
|
||||
backgroundColor: '#f6ffed',
|
||||
borderColor: 'var(--color-success)',
|
||||
backgroundColor: 'var(--color-bg-base)', // 使用柔和的背景色
|
||||
borderRadius: 12,
|
||||
} as CSSProperties,
|
||||
|
||||
// 项目卡片样式
|
||||
// 项目卡片样式 - 现代化设计
|
||||
project: {
|
||||
height: '100%',
|
||||
borderRadius: 16,
|
||||
borderRadius: 20,
|
||||
overflow: 'hidden',
|
||||
background: '#fff',
|
||||
boxShadow: '0 4px 16px rgba(0, 0, 0, 0.08)',
|
||||
transition: 'all 0.3s ease',
|
||||
background: 'var(--color-bg-container)',
|
||||
boxShadow: '0 4px 20px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.04)',
|
||||
transition: 'all 0.35s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
border: '1px solid rgba(0, 0, 0, 0.04)',
|
||||
} as CSSProperties,
|
||||
|
||||
// 卡片内容区域样式
|
||||
@@ -73,17 +74,17 @@ export const cardStyles = {
|
||||
} as CSSProperties),
|
||||
};
|
||||
|
||||
// 卡片悬浮动画
|
||||
// 卡片悬浮动画 - 增强版
|
||||
export const cardHoverHandlers = {
|
||||
onMouseEnter: (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
const target = e.currentTarget;
|
||||
target.style.transform = 'translateY(-8px)';
|
||||
target.style.boxShadow = '0 12px 32px rgba(0, 0, 0, 0.15)';
|
||||
target.style.transform = 'translateY(-10px) scale(1.01)';
|
||||
target.style.boxShadow = '0 20px 40px rgba(77, 128, 136, 0.2), 0 8px 16px rgba(0, 0, 0, 0.08)';
|
||||
},
|
||||
onMouseLeave: (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
const target = e.currentTarget;
|
||||
target.style.transform = 'translateY(0)';
|
||||
target.style.boxShadow = '0 4px 16px rgba(0, 0, 0, 0.08)';
|
||||
target.style.transform = 'translateY(0) scale(1)';
|
||||
target.style.boxShadow = '0 4px 20px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.04)';
|
||||
},
|
||||
};
|
||||
|
||||
@@ -113,12 +114,12 @@ export const textStyles = {
|
||||
fontSize: 12,
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
} as CSSProperties,
|
||||
|
||||
|
||||
value: {
|
||||
fontSize: 14,
|
||||
color: 'rgba(0, 0, 0, 0.85)',
|
||||
} as CSSProperties,
|
||||
|
||||
|
||||
description: {
|
||||
fontSize: 12,
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
|
||||
@@ -16,10 +16,7 @@ import {
|
||||
import {
|
||||
fetchChangelog,
|
||||
groupChangelogByDate,
|
||||
getCachedChangelog,
|
||||
cacheChangelog,
|
||||
markChangelogFetched,
|
||||
shouldFetchChangelog,
|
||||
clearChangelogCache,
|
||||
type ChangelogEntry,
|
||||
} from '../services/changelogService';
|
||||
@@ -50,35 +47,15 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
|
||||
// 加载更新日志
|
||||
// 每次用户打开窗口时才同步获取最新数据,不自动刷新
|
||||
const loadChangelog = async (pageNum: number = 1, append: boolean = false) => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
// 如果是第一页,先尝试使用缓存
|
||||
if (pageNum === 1 && !append) {
|
||||
const cached = getCachedChangelog();
|
||||
if (cached && cached.length > 0) {
|
||||
setChangelog(cached);
|
||||
|
||||
// 后台刷新
|
||||
if (shouldFetchChangelog()) {
|
||||
fetchChangelog(pageNum, 30)
|
||||
.then(entries => {
|
||||
setChangelog(entries);
|
||||
cacheChangelog(entries);
|
||||
markChangelogFetched();
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 每次打开都从网络获取最新数据
|
||||
const entries = await fetchChangelog(pageNum, 30);
|
||||
|
||||
|
||||
if (entries.length === 0) {
|
||||
setHasMore(false);
|
||||
} else {
|
||||
@@ -86,10 +63,9 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
|
||||
setChangelog(prev => [...prev, ...entries]);
|
||||
} else {
|
||||
setChangelog(entries);
|
||||
// 缓存第一页数据
|
||||
// 缓存第一页数据(用于分页加载时的数据持久化)
|
||||
if (pageNum === 1) {
|
||||
cacheChangelog(entries);
|
||||
markChangelogFetched();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,7 +113,7 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
|
||||
if (diffDays === 0) return '今天';
|
||||
if (diffDays === 1) return '昨天';
|
||||
if (diffDays < 7) return `${diffDays} 天前`;
|
||||
|
||||
|
||||
return date.toLocaleDateString('zh-CN', { year: 'numeric', month: 'long', day: 'numeric' });
|
||||
};
|
||||
|
||||
@@ -180,10 +156,10 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
|
||||
<div style={{
|
||||
padding: '16px',
|
||||
marginBottom: '16px',
|
||||
background: '#fff2e8',
|
||||
border: '1px solid #ffbb96',
|
||||
background: 'var(--color-error-bg)',
|
||||
border: '1px solid var(--color-error-border)',
|
||||
borderRadius: '4px',
|
||||
color: '#d4380d',
|
||||
color: 'var(--color-error)',
|
||||
}}>
|
||||
{error}
|
||||
</div>
|
||||
@@ -199,16 +175,16 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
|
||||
<>
|
||||
{sortedDates.map(date => {
|
||||
const entries = groupedChangelog.get(date) || [];
|
||||
|
||||
|
||||
return (
|
||||
<div key={date} style={{ marginBottom: '32px' }}>
|
||||
<div style={{
|
||||
fontSize: '16px',
|
||||
fontWeight: 600,
|
||||
color: '#1890ff',
|
||||
color: 'var(--color-primary)',
|
||||
marginBottom: '16px',
|
||||
paddingBottom: '8px',
|
||||
borderBottom: '2px solid #e8e8e8',
|
||||
borderBottom: '2px solid var(--color-border-secondary)',
|
||||
}}>
|
||||
<ClockCircleOutlined style={{ marginRight: '8px' }} />
|
||||
{formatDate(date)}
|
||||
@@ -217,7 +193,7 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
|
||||
<Timeline>
|
||||
{entries.map(entry => {
|
||||
const config = typeConfig[entry.type] || typeConfig.other;
|
||||
|
||||
|
||||
return (
|
||||
<Timeline.Item
|
||||
key={entry.id}
|
||||
@@ -226,8 +202,8 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
|
||||
width: '24px',
|
||||
height: '24px',
|
||||
borderRadius: '50%',
|
||||
background: '#fff',
|
||||
border: `2px solid ${config.color === 'default' ? '#d9d9d9' : config.color}`,
|
||||
background: 'var(--color-bg-container)',
|
||||
border: `2px solid ${config.color === 'default' ? 'var(--color-border)' : config.color}`,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
@@ -245,7 +221,7 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
|
||||
{entry.scope && (
|
||||
<Tag color="blue">{entry.scope}</Tag>
|
||||
)}
|
||||
<span style={{ color: '#999', fontSize: '12px' }}>
|
||||
<span style={{ color: 'var(--color-text-tertiary)', fontSize: '12px' }}>
|
||||
{formatTime(entry.date)}
|
||||
</span>
|
||||
</Space>
|
||||
@@ -254,7 +230,7 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
|
||||
marginTop: '8px',
|
||||
fontSize: '14px',
|
||||
lineHeight: '1.6',
|
||||
color: '#333',
|
||||
color: 'var(--color-text-primary)',
|
||||
}}>
|
||||
{entry.message}
|
||||
</div>
|
||||
@@ -263,7 +239,7 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
|
||||
{entry.author.avatar && (
|
||||
<Avatar size="small" src={entry.author.avatar} />
|
||||
)}
|
||||
<span style={{ color: '#666', fontSize: '13px' }}>
|
||||
<span style={{ color: 'var(--color-text-secondary)', fontSize: '13px' }}>
|
||||
{entry.author.username || entry.author.name}
|
||||
</span>
|
||||
<a
|
||||
@@ -284,42 +260,46 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
|
||||
);
|
||||
})}
|
||||
|
||||
{hasMore && (
|
||||
<div style={{ textAlign: 'center', marginTop: '24px' }}>
|
||||
<Button
|
||||
type="default"
|
||||
onClick={handleLoadMore}
|
||||
loading={loading}
|
||||
>
|
||||
加载更多
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{
|
||||
hasMore && (
|
||||
<div style={{ textAlign: 'center', marginTop: '24px' }}>
|
||||
<Button
|
||||
type="default"
|
||||
onClick={handleLoadMore}
|
||||
loading={loading}
|
||||
>
|
||||
加载更多
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{!hasMore && changelog.length > 0 && (
|
||||
<div style={{
|
||||
textAlign: 'center',
|
||||
color: '#999',
|
||||
padding: '16px 0',
|
||||
fontSize: '14px',
|
||||
}}>
|
||||
已显示所有更新日志
|
||||
</div>
|
||||
)}
|
||||
{
|
||||
!hasMore && changelog.length > 0 && (
|
||||
<div style={{
|
||||
textAlign: 'center',
|
||||
color: 'var(--color-text-tertiary)',
|
||||
padding: '16px 0',
|
||||
fontSize: '14px',
|
||||
}}>
|
||||
已显示所有更新日志
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)}
|
||||
|
||||
<div style={{
|
||||
marginTop: '24px',
|
||||
padding: '12px',
|
||||
background: '#f0f5ff',
|
||||
background: 'var(--color-info-bg)',
|
||||
borderRadius: '4px',
|
||||
border: '1px solid #adc6ff',
|
||||
border: '1px solid var(--color-info-border)',
|
||||
fontSize: '13px',
|
||||
color: '#1d39c4',
|
||||
color: 'var(--color-primary)',
|
||||
}}>
|
||||
💡 提示:更新日志每小时自动刷新一次,数据来源于 GitHub 提交历史
|
||||
💡 提示:每次打开窗口时自动获取最新更新日志,数据来源于 GitHub 提交历史
|
||||
</div>
|
||||
</Modal>
|
||||
</Modal >
|
||||
);
|
||||
}
|
||||
@@ -42,14 +42,14 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
if (visible && chapterId) {
|
||||
fetchAnalysisStatus();
|
||||
}
|
||||
|
||||
|
||||
// 监听窗口大小变化
|
||||
const handleResize = () => {
|
||||
setIsMobile(isMobileDevice());
|
||||
};
|
||||
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
|
||||
// 清理函数:组件卸载或关闭时清除轮询
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
@@ -79,33 +79,33 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
|
||||
// 🔧 使用独立的章节加载函数
|
||||
await loadChapterInfo();
|
||||
|
||||
|
||||
const response = await fetch(`/api/chapters/${chapterId}/analysis/status`);
|
||||
|
||||
|
||||
if (response.status === 404) {
|
||||
setTask(null);
|
||||
setError('该章节还未进行分析');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('获取分析状态失败');
|
||||
}
|
||||
|
||||
|
||||
const taskData: AnalysisTask = await response.json();
|
||||
|
||||
|
||||
// 如果状态为 none(无任务),设置 task 为 null,让前端显示"开始分析"按钮
|
||||
if (taskData.status === 'none' || !taskData.has_task) {
|
||||
setTask(null);
|
||||
setError(null); // 清除错误,这不是错误状态
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
setTask(taskData);
|
||||
|
||||
|
||||
if (taskData.status === 'completed') {
|
||||
await fetchAnalysisResult();
|
||||
} else if (taskData.status === 'running' || taskData.status === 'pending') {
|
||||
@@ -137,10 +137,10 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
try {
|
||||
const response = await fetch(`/api/chapters/${chapterId}/analysis/status`);
|
||||
if (!response.ok) return;
|
||||
|
||||
|
||||
const taskData: AnalysisTask = await response.json();
|
||||
setTask(taskData);
|
||||
|
||||
|
||||
if (taskData.status === 'completed') {
|
||||
clearInterval(pollInterval);
|
||||
await fetchAnalysisResult();
|
||||
@@ -163,19 +163,19 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
|
||||
// 🔧 触发分析前先刷新章节内容,确保分析的是最新内容
|
||||
await loadChapterInfo();
|
||||
|
||||
|
||||
const response = await fetch(`/api/chapters/${chapterId}/analyze`, {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.detail || '触发分析失败');
|
||||
}
|
||||
|
||||
|
||||
// 触发成功后立即关闭Modal,让父组件的状态管理接管
|
||||
onClose();
|
||||
} catch (err) {
|
||||
@@ -188,16 +188,16 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
|
||||
const renderStatusIcon = () => {
|
||||
if (!task) return null;
|
||||
|
||||
|
||||
switch (task.status) {
|
||||
case 'pending':
|
||||
return <ClockCircleOutlined style={{ color: '#faad14' }} />;
|
||||
return <ClockCircleOutlined style={{ color: 'var(--color-warning)' }} />;
|
||||
case 'running':
|
||||
return <Spin />;
|
||||
case 'completed':
|
||||
return <CheckCircleOutlined style={{ color: '#52c41a' }} />;
|
||||
return <CheckCircleOutlined style={{ color: 'var(--color-success)' }} />;
|
||||
case 'failed':
|
||||
return <CloseCircleOutlined style={{ color: '#ff4d4f' }} />;
|
||||
return <CloseCircleOutlined style={{ color: 'var(--color-error)' }} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -205,7 +205,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
|
||||
const renderProgress = () => {
|
||||
if (!task || task.status === 'completed') return null;
|
||||
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
padding: '40px',
|
||||
@@ -225,7 +225,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
marginTop: 16,
|
||||
color: task.status === 'failed' ? '#ff4d4f' : '#262626'
|
||||
color: task.status === 'failed' ? 'var(--color-error)' : 'var(--color-text-primary)'
|
||||
}}>
|
||||
{task.status === 'pending' && '等待分析...'}
|
||||
{task.status === 'running' && 'AI正在分析中...'}
|
||||
@@ -241,7 +241,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
}}>
|
||||
<div style={{
|
||||
height: 12,
|
||||
background: '#f0f0f0',
|
||||
background: 'var(--color-bg-layout)',
|
||||
borderRadius: 6,
|
||||
overflow: 'hidden',
|
||||
marginBottom: 12
|
||||
@@ -249,10 +249,10 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
<div style={{
|
||||
height: '100%',
|
||||
background: task.status === 'failed'
|
||||
? 'linear-gradient(90deg, #ff4d4f 0%, #ff7875 100%)'
|
||||
? 'var(--color-error)'
|
||||
: task.progress === 100
|
||||
? 'linear-gradient(90deg, #52c41a 0%, #73d13d 100%)'
|
||||
: 'linear-gradient(90deg, #1890ff 0%, #40a9ff 100%)',
|
||||
? 'var(--color-success)'
|
||||
: 'var(--color-primary)',
|
||||
width: `${task.progress}%`,
|
||||
transition: 'all 0.3s ease',
|
||||
borderRadius: 6,
|
||||
@@ -261,14 +261,14 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
: 'none'
|
||||
}} />
|
||||
</div>
|
||||
|
||||
|
||||
{/* 进度百分比 */}
|
||||
<div style={{
|
||||
textAlign: 'center',
|
||||
fontSize: 32,
|
||||
fontWeight: 'bold',
|
||||
color: task.status === 'failed' ? '#ff4d4f' :
|
||||
task.progress === 100 ? '#52c41a' : '#1890ff',
|
||||
color: task.status === 'failed' ? 'var(--color-error)' :
|
||||
task.progress === 100 ? 'var(--color-success)' : 'var(--color-primary)',
|
||||
marginBottom: 8
|
||||
}}>
|
||||
{task.progress}%
|
||||
@@ -279,7 +279,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
<div style={{
|
||||
textAlign: 'center',
|
||||
fontSize: 16,
|
||||
color: '#595959',
|
||||
color: 'var(--color-text-secondary)',
|
||||
minHeight: 24,
|
||||
marginBottom: 16
|
||||
}}>
|
||||
@@ -307,7 +307,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
<div style={{
|
||||
textAlign: 'center',
|
||||
fontSize: 13,
|
||||
color: '#8c8c8c',
|
||||
color: 'var(--color-text-tertiary)',
|
||||
marginTop: 16
|
||||
}}>
|
||||
分析过程需要一定时间,请耐心等待
|
||||
@@ -320,7 +320,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
// 将分析建议转换为重新生成组件需要的格式
|
||||
const convertSuggestionsForRegeneration = () => {
|
||||
if (!analysis?.analysis?.suggestions) return [];
|
||||
|
||||
|
||||
return analysis.analysis.suggestions.map((suggestion, index) => ({
|
||||
category: '改进建议',
|
||||
content: suggestion,
|
||||
@@ -330,9 +330,9 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
|
||||
const renderAnalysisResult = () => {
|
||||
if (!analysis) return null;
|
||||
|
||||
|
||||
const { analysis: analysis_data, memories } = analysis;
|
||||
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
defaultActiveKey="overview"
|
||||
@@ -366,7 +366,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
style={{ marginBottom: 16 }}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
<Card title="整体评分" style={{ marginBottom: 16 }} size={isMobile ? 'small' : 'default'}>
|
||||
<Row gutter={isMobile ? 8 : 16}>
|
||||
<Col span={isMobile ? 12 : 6}>
|
||||
@@ -374,7 +374,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
title="整体质量"
|
||||
value={analysis_data.overall_quality_score || 0}
|
||||
suffix="/ 10"
|
||||
valueStyle={{ color: '#3f8600' }}
|
||||
valueStyle={{ color: 'var(--color-success)' }}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={isMobile ? 12 : 6}>
|
||||
@@ -400,7 +400,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
|
||||
|
||||
{analysis_data.analysis_report && (
|
||||
<Card title="分析摘要" style={{ marginBottom: 16 }} size={isMobile ? 'small' : 'default'}>
|
||||
<pre style={{ whiteSpace: 'pre-wrap', fontFamily: 'inherit', fontSize: isMobile ? 13 : 14 }}>
|
||||
@@ -408,7 +408,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
</pre>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
|
||||
{analysis_data.suggestions && analysis_data.suggestions.length > 0 && (
|
||||
<Card title={<><BulbOutlined /> 改进建议</>} size={isMobile ? 'small' : 'default'}>
|
||||
<List
|
||||
@@ -431,27 +431,27 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
children: (
|
||||
<div style={{ height: isMobile ? 'calc(80vh - 180px)' : 'calc(90vh - 220px)', overflowY: 'auto', paddingRight: '8px' }}>
|
||||
<Card size={isMobile ? 'small' : 'default'}>
|
||||
{analysis_data.hooks && analysis_data.hooks.length > 0 ? (
|
||||
<List
|
||||
dataSource={analysis_data.hooks}
|
||||
renderItem={(hook) => (
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
title={
|
||||
<div>
|
||||
<Tag color="blue">{hook.type}</Tag>
|
||||
<Tag color="orange">{hook.position}</Tag>
|
||||
<Tag color="red">强度: {hook.strength}/10</Tag>
|
||||
</div>
|
||||
}
|
||||
description={hook.content}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<Empty description="暂无钩子" />
|
||||
)}
|
||||
{analysis_data.hooks && analysis_data.hooks.length > 0 ? (
|
||||
<List
|
||||
dataSource={analysis_data.hooks}
|
||||
renderItem={(hook) => (
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
title={
|
||||
<div>
|
||||
<Tag color="blue">{hook.type}</Tag>
|
||||
<Tag color="orange">{hook.position}</Tag>
|
||||
<Tag color="red">强度: {hook.strength}/10</Tag>
|
||||
</div>
|
||||
}
|
||||
description={hook.content}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<Empty description="暂无钩子" />
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
@@ -463,32 +463,32 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
children: (
|
||||
<div style={{ height: isMobile ? 'calc(80vh - 180px)' : 'calc(90vh - 220px)', overflowY: 'auto', paddingRight: '8px' }}>
|
||||
<Card size={isMobile ? 'small' : 'default'}>
|
||||
{analysis_data.foreshadows && analysis_data.foreshadows.length > 0 ? (
|
||||
<List
|
||||
dataSource={analysis_data.foreshadows}
|
||||
renderItem={(foreshadow) => (
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
title={
|
||||
<div>
|
||||
<Tag color={foreshadow.type === 'planted' ? 'green' : 'purple'}>
|
||||
{foreshadow.type === 'planted' ? '已埋下' : '已回收'}
|
||||
</Tag>
|
||||
<Tag>强度: {foreshadow.strength}/10</Tag>
|
||||
<Tag>隐藏度: {foreshadow.subtlety}/10</Tag>
|
||||
{foreshadow.reference_chapter && (
|
||||
<Tag color="cyan">呼应第{foreshadow.reference_chapter}章</Tag>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
description={foreshadow.content}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<Empty description="暂无伏笔" />
|
||||
)}
|
||||
{analysis_data.foreshadows && analysis_data.foreshadows.length > 0 ? (
|
||||
<List
|
||||
dataSource={analysis_data.foreshadows}
|
||||
renderItem={(foreshadow) => (
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
title={
|
||||
<div>
|
||||
<Tag color={foreshadow.type === 'planted' ? 'green' : 'purple'}>
|
||||
{foreshadow.type === 'planted' ? '已埋下' : '已回收'}
|
||||
</Tag>
|
||||
<Tag>强度: {foreshadow.strength}/10</Tag>
|
||||
<Tag>隐藏度: {foreshadow.subtlety}/10</Tag>
|
||||
{foreshadow.reference_chapter && (
|
||||
<Tag color="cyan">呼应第{foreshadow.reference_chapter}章</Tag>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
description={foreshadow.content}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<Empty description="暂无伏笔" />
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
@@ -500,41 +500,41 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
children: (
|
||||
<div style={{ height: isMobile ? 'calc(80vh - 180px)' : 'calc(90vh - 220px)', overflowY: 'auto', paddingRight: '8px' }}>
|
||||
<Card size={isMobile ? 'small' : 'default'}>
|
||||
{analysis_data.emotional_tone ? (
|
||||
<div>
|
||||
<Row gutter={isMobile ? 8 : 16} style={{ marginBottom: isMobile ? 16 : 24 }}>
|
||||
<Col span={isMobile ? 24 : 12}>
|
||||
<Statistic
|
||||
title="主导情绪"
|
||||
value={analysis_data.emotional_tone}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={isMobile ? 24 : 12}>
|
||||
<Statistic
|
||||
title="情感强度"
|
||||
value={(analysis_data.emotional_intensity * 10).toFixed(1)}
|
||||
suffix="/ 10"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Card type="inner" title="剧情阶段" size="small">
|
||||
<p><strong>阶段:</strong>{analysis_data.plot_stage}</p>
|
||||
<p><strong>冲突等级:</strong>{analysis_data.conflict_level} / 10</p>
|
||||
{analysis_data.conflict_types && analysis_data.conflict_types.length > 0 && (
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<strong>冲突类型:</strong>
|
||||
{analysis_data.conflict_types.map((type, idx) => (
|
||||
<Tag key={idx} color="red" style={{ margin: 4 }}>
|
||||
{type}
|
||||
</Tag>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
) : (
|
||||
<Empty description="暂无情感分析" />
|
||||
)}
|
||||
{analysis_data.emotional_tone ? (
|
||||
<div>
|
||||
<Row gutter={isMobile ? 8 : 16} style={{ marginBottom: isMobile ? 16 : 24 }}>
|
||||
<Col span={isMobile ? 24 : 12}>
|
||||
<Statistic
|
||||
title="主导情绪"
|
||||
value={analysis_data.emotional_tone}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={isMobile ? 24 : 12}>
|
||||
<Statistic
|
||||
title="情感强度"
|
||||
value={(analysis_data.emotional_intensity * 10).toFixed(1)}
|
||||
suffix="/ 10"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Card type="inner" title="剧情阶段" size="small">
|
||||
<p><strong>阶段:</strong>{analysis_data.plot_stage}</p>
|
||||
<p><strong>冲突等级:</strong>{analysis_data.conflict_level} / 10</p>
|
||||
{analysis_data.conflict_types && analysis_data.conflict_types.length > 0 && (
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<strong>冲突类型:</strong>
|
||||
{analysis_data.conflict_types.map((type, idx) => (
|
||||
<Tag key={idx} color="red" style={{ margin: 4 }}>
|
||||
{type}
|
||||
</Tag>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
) : (
|
||||
<Empty description="暂无情感分析" />
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
@@ -546,37 +546,37 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
children: (
|
||||
<div style={{ height: isMobile ? 'calc(80vh - 180px)' : 'calc(90vh - 220px)', overflowY: 'auto', paddingRight: '8px' }}>
|
||||
<Card size={isMobile ? 'small' : 'default'}>
|
||||
{analysis_data.character_states && analysis_data.character_states.length > 0 ? (
|
||||
<List
|
||||
dataSource={analysis_data.character_states}
|
||||
renderItem={(char) => (
|
||||
<List.Item>
|
||||
<Card
|
||||
type="inner"
|
||||
title={char.character_name}
|
||||
size="small"
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<p><strong>状态变化:</strong>{char.state_before} → {char.state_after}</p>
|
||||
<p><strong>心理变化:</strong>{char.psychological_change}</p>
|
||||
<p><strong>关键事件:</strong>{char.key_event}</p>
|
||||
{char.relationship_changes && Object.keys(char.relationship_changes).length > 0 && (
|
||||
<div>
|
||||
<strong>关系变化:</strong>
|
||||
{Object.entries(char.relationship_changes).map(([name, change]) => (
|
||||
<Tag key={name} color="blue" style={{ margin: 4 }}>
|
||||
与{name}: {change}
|
||||
</Tag>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<Empty description="暂无角色分析" />
|
||||
)}
|
||||
{analysis_data.character_states && analysis_data.character_states.length > 0 ? (
|
||||
<List
|
||||
dataSource={analysis_data.character_states}
|
||||
renderItem={(char) => (
|
||||
<List.Item>
|
||||
<Card
|
||||
type="inner"
|
||||
title={char.character_name}
|
||||
size="small"
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<p><strong>状态变化:</strong>{char.state_before} → {char.state_after}</p>
|
||||
<p><strong>心理变化:</strong>{char.psychological_change}</p>
|
||||
<p><strong>关键事件:</strong>{char.key_event}</p>
|
||||
{char.relationship_changes && Object.keys(char.relationship_changes).length > 0 && (
|
||||
<div>
|
||||
<strong>关系变化:</strong>
|
||||
{Object.entries(char.relationship_changes).map(([name, change]) => (
|
||||
<Tag key={name} color="blue" style={{ margin: 4 }}>
|
||||
与{name}: {change}
|
||||
</Tag>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<Empty description="暂无角色分析" />
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
@@ -588,38 +588,38 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
children: (
|
||||
<div style={{ height: isMobile ? 'calc(80vh - 180px)' : 'calc(90vh - 220px)', overflowY: 'auto', paddingRight: '8px' }}>
|
||||
<Card size={isMobile ? 'small' : 'default'}>
|
||||
{memories && memories.length > 0 ? (
|
||||
<List
|
||||
dataSource={memories}
|
||||
renderItem={(memory) => (
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
title={
|
||||
<div>
|
||||
<Tag color="blue">{memory.type}</Tag>
|
||||
<Tag color="orange">重要性: {memory.importance.toFixed(1)}</Tag>
|
||||
{memory.is_foreshadow === 1 && <Tag color="green">已埋下伏笔</Tag>}
|
||||
{memory.is_foreshadow === 2 && <Tag color="purple">已回收伏笔</Tag>}
|
||||
<span style={{ marginLeft: 8 }}>{memory.title}</span>
|
||||
</div>
|
||||
}
|
||||
description={
|
||||
<div>
|
||||
<p>{memory.content}</p>
|
||||
{memories && memories.length > 0 ? (
|
||||
<List
|
||||
dataSource={memories}
|
||||
renderItem={(memory) => (
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
title={
|
||||
<div>
|
||||
{memory.tags.map((tag, idx) => (
|
||||
<Tag key={idx} style={{ margin: 2 }}>{tag}</Tag>
|
||||
))}
|
||||
<Tag color="blue">{memory.type}</Tag>
|
||||
<Tag color="orange">重要性: {memory.importance.toFixed(1)}</Tag>
|
||||
{memory.is_foreshadow === 1 && <Tag color="green">已埋下伏笔</Tag>}
|
||||
{memory.is_foreshadow === 2 && <Tag color="purple">已回收伏笔</Tag>}
|
||||
<span style={{ marginLeft: 8 }}>{memory.title}</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<Empty description="暂无记忆片段" />
|
||||
)}
|
||||
}
|
||||
description={
|
||||
<div>
|
||||
<p>{memory.content}</p>
|
||||
<div>
|
||||
{memory.tags.map((tag, idx) => (
|
||||
<Tag key={idx} style={{ margin: 2 }}>{tag}</Tag>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<Empty description="暂无记忆片段" />
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
@@ -700,7 +700,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
<p style={{ marginTop: 16 }}>加载中...</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{error && (
|
||||
<Alert
|
||||
message="错误"
|
||||
@@ -709,10 +709,10 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
showIcon
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
{task && task.status !== 'completed' && renderProgress()}
|
||||
{task && task.status === 'completed' && analysis && renderAnalysisResult()}
|
||||
|
||||
|
||||
{/* 重新生成Modal */}
|
||||
{chapterInfo && (
|
||||
<ChapterRegenerationModal
|
||||
@@ -734,7 +734,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
hasAnalysis={true}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
{/* 内容对比组件 */}
|
||||
{chapterInfo && comparisonModalVisible && (
|
||||
<ChapterContentComparison
|
||||
@@ -749,7 +749,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
// 应用新内容后刷新章节信息和分析
|
||||
setChapterInfo(null);
|
||||
setAnalysis(null);
|
||||
|
||||
|
||||
// 重新加载章节内容
|
||||
try {
|
||||
const chapterResponse = await fetch(`/api/chapters/${chapterId}`);
|
||||
@@ -764,7 +764,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
} catch (error) {
|
||||
console.error('重新加载章节失败:', error);
|
||||
}
|
||||
|
||||
|
||||
// 刷新分析状态
|
||||
await fetchAnalysisStatus();
|
||||
}}
|
||||
|
||||
@@ -51,10 +51,10 @@ const ChapterContentComparison: React.FC<ChapterContentComparisonProps> = ({
|
||||
}
|
||||
|
||||
message.success('新内容已应用!');
|
||||
|
||||
|
||||
// 先调用 onApply 通知父组件刷新
|
||||
onApply();
|
||||
|
||||
|
||||
// 延迟触发章节分析,给父组件时间刷新
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
@@ -75,7 +75,7 @@ const ChapterContentComparison: React.FC<ChapterContentComparisonProps> = ({
|
||||
message.warning('章节分析触发失败,您可以手动触发分析');
|
||||
}
|
||||
}, 500);
|
||||
|
||||
|
||||
onClose();
|
||||
} catch (error: any) {
|
||||
message.error(error.message || '应用失败');
|
||||
@@ -108,9 +108,9 @@ const ChapterContentComparison: React.FC<ChapterContentComparisonProps> = ({
|
||||
centered
|
||||
style={{ maxWidth: 1600 }}
|
||||
footer={[
|
||||
<Button
|
||||
key="discard"
|
||||
danger
|
||||
<Button
|
||||
key="discard"
|
||||
danger
|
||||
icon={<CloseOutlined />}
|
||||
onClick={handleDiscard}
|
||||
>
|
||||
@@ -123,9 +123,9 @@ const ChapterContentComparison: React.FC<ChapterContentComparisonProps> = ({
|
||||
>
|
||||
切换视图
|
||||
</Button>,
|
||||
<Button
|
||||
key="apply"
|
||||
type="primary"
|
||||
<Button
|
||||
key="apply"
|
||||
type="primary"
|
||||
icon={<CheckOutlined />}
|
||||
loading={applying}
|
||||
onClick={handleApply}
|
||||
@@ -156,7 +156,7 @@ const ChapterContentComparison: React.FC<ChapterContentComparisonProps> = ({
|
||||
title="字数变化"
|
||||
value={wordCountDiff}
|
||||
suffix="字"
|
||||
valueStyle={{ color: wordCountDiff > 0 ? '#3f8600' : '#cf1322' }}
|
||||
valueStyle={{ color: wordCountDiff > 0 ? 'var(--color-success)' : 'var(--color-error)' }}
|
||||
prefix={wordCountDiff > 0 ? '+' : ''}
|
||||
/>
|
||||
</Col>
|
||||
@@ -165,7 +165,7 @@ const ChapterContentComparison: React.FC<ChapterContentComparisonProps> = ({
|
||||
title="变化比例"
|
||||
value={wordCountDiffPercent}
|
||||
suffix="%"
|
||||
valueStyle={{ color: Math.abs(parseFloat(wordCountDiffPercent)) < 10 ? '#1890ff' : '#faad14' }}
|
||||
valueStyle={{ color: Math.abs(parseFloat(wordCountDiffPercent)) < 10 ? 'var(--color-primary)' : 'var(--color-warning)' }}
|
||||
prefix={wordCountDiff > 0 ? '+' : ''}
|
||||
/>
|
||||
</Col>
|
||||
@@ -173,10 +173,10 @@ const ChapterContentComparison: React.FC<ChapterContentComparisonProps> = ({
|
||||
</Card>
|
||||
|
||||
{/* 内容对比 */}
|
||||
<div style={{
|
||||
maxHeight: 'calc(90vh - 300px)',
|
||||
<div style={{
|
||||
maxHeight: 'calc(90vh - 300px)',
|
||||
overflow: 'auto',
|
||||
border: '1px solid #d9d9d9',
|
||||
border: '1px solid var(--color-border)',
|
||||
borderRadius: 4
|
||||
}}>
|
||||
<ReactDiffViewer
|
||||
@@ -190,19 +190,19 @@ const ChapterContentComparison: React.FC<ChapterContentComparisonProps> = ({
|
||||
styles={{
|
||||
variables: {
|
||||
light: {
|
||||
diffViewerBackground: '#fff',
|
||||
addedBackground: '#e6ffed',
|
||||
addedColor: '#24292e',
|
||||
removedBackground: '#ffeef0',
|
||||
removedColor: '#24292e',
|
||||
wordAddedBackground: '#acf2bd',
|
||||
wordRemovedBackground: '#fdb8c0',
|
||||
addedGutterBackground: '#cdffd8',
|
||||
removedGutterBackground: '#ffdce0',
|
||||
gutterBackground: '#f6f8fa',
|
||||
gutterBackgroundDark: '#f3f4f6',
|
||||
highlightBackground: '#fffbdd',
|
||||
highlightGutterBackground: '#fff5b1',
|
||||
diffViewerBackground: '#fff', // Keep white for diff viewer readability
|
||||
addedBackground: 'var(--color-success-bg)',
|
||||
addedColor: 'var(--color-text-primary)',
|
||||
removedBackground: 'var(--color-error-bg)',
|
||||
removedColor: 'var(--color-text-primary)',
|
||||
wordAddedBackground: 'var(--color-success-border)',
|
||||
wordRemovedBackground: 'var(--color-error-border)',
|
||||
addedGutterBackground: 'var(--color-success-bg)',
|
||||
removedGutterBackground: 'var(--color-error-bg)',
|
||||
gutterBackground: 'var(--color-bg-layout)',
|
||||
gutterBackgroundDark: 'var(--color-bg-container)',
|
||||
highlightBackground: 'var(--color-warning-bg)',
|
||||
highlightGutterBackground: 'var(--color-warning-border)',
|
||||
},
|
||||
},
|
||||
line: {
|
||||
|
||||
@@ -41,14 +41,14 @@ export const SSELoadingOverlay: React.FC<SSELoadingOverlayProps> = ({
|
||||
textAlign: 'center',
|
||||
marginBottom: 24
|
||||
}}>
|
||||
<Spin
|
||||
indicator={<LoadingOutlined style={{ fontSize: 48, color: '#1890ff' }} spin />}
|
||||
<Spin
|
||||
indicator={<LoadingOutlined style={{ fontSize: 48, color: 'var(--color-primary)' }} spin />}
|
||||
/>
|
||||
<div style={{
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
marginTop: 16,
|
||||
color: '#262626'
|
||||
color: 'var(--color-text-primary)'
|
||||
}}>
|
||||
AI生成中...
|
||||
</div>
|
||||
@@ -60,29 +60,29 @@ export const SSELoadingOverlay: React.FC<SSELoadingOverlayProps> = ({
|
||||
}}>
|
||||
<div style={{
|
||||
height: 12,
|
||||
background: '#f0f0f0',
|
||||
background: 'var(--color-bg-layout)',
|
||||
borderRadius: 6,
|
||||
overflow: 'hidden',
|
||||
marginBottom: 12
|
||||
}}>
|
||||
<div style={{
|
||||
height: '100%',
|
||||
background: progress === 100
|
||||
? 'linear-gradient(90deg, #52c41a 0%, #73d13d 100%)'
|
||||
: 'linear-gradient(90deg, #1890ff 0%, #40a9ff 100%)',
|
||||
background: progress === 100
|
||||
? 'linear-gradient(90deg, var(--color-success) 0%, var(--color-success-active) 100%)'
|
||||
: 'linear-gradient(90deg, var(--color-primary) 0%, var(--color-primary-active) 100%)',
|
||||
width: `${progress}%`,
|
||||
transition: 'all 0.3s ease',
|
||||
borderRadius: 6,
|
||||
boxShadow: progress > 0 ? '0 0 10px rgba(24, 144, 255, 0.3)' : 'none'
|
||||
boxShadow: progress > 0 ? 'var(--shadow-card)' : 'none'
|
||||
}} />
|
||||
</div>
|
||||
|
||||
|
||||
{/* 进度百分比 */}
|
||||
<div style={{
|
||||
textAlign: 'center',
|
||||
fontSize: 32,
|
||||
fontWeight: 'bold',
|
||||
color: progress === 100 ? '#52c41a' : '#1890ff',
|
||||
color: progress === 100 ? 'var(--color-success)' : 'var(--color-primary)',
|
||||
marginBottom: 8
|
||||
}}>
|
||||
{progress}%
|
||||
|
||||
@@ -52,14 +52,14 @@ export const SSEProgressModal: React.FC<SSEProgressModalProps> = ({
|
||||
textAlign: 'center',
|
||||
marginBottom: 24
|
||||
}}>
|
||||
<Spin
|
||||
indicator={<LoadingOutlined style={{ fontSize: 48, color: '#1890ff' }} spin />}
|
||||
<Spin
|
||||
indicator={<LoadingOutlined style={{ fontSize: 48, color: 'var(--color-primary)' }} spin />}
|
||||
/>
|
||||
<div style={{
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
marginTop: 16,
|
||||
color: '#262626'
|
||||
color: 'var(--color-text-primary)'
|
||||
}}>
|
||||
{title}
|
||||
</div>
|
||||
@@ -72,30 +72,30 @@ export const SSEProgressModal: React.FC<SSEProgressModalProps> = ({
|
||||
}}>
|
||||
<div style={{
|
||||
height: 12,
|
||||
background: '#f0f0f0',
|
||||
background: 'var(--color-bg-layout)',
|
||||
borderRadius: 6,
|
||||
overflow: 'hidden',
|
||||
marginBottom: showPercentage ? 12 : 0
|
||||
}}>
|
||||
<div style={{
|
||||
height: '100%',
|
||||
background: progress === 100
|
||||
? 'linear-gradient(90deg, #52c41a 0%, #73d13d 100%)'
|
||||
: 'linear-gradient(90deg, #1890ff 0%, #40a9ff 100%)',
|
||||
background: progress === 100
|
||||
? 'linear-gradient(90deg, var(--color-success) 0%, var(--color-success-active) 100%)'
|
||||
: 'linear-gradient(90deg, var(--color-primary) 0%, var(--color-primary-active) 100%)',
|
||||
width: `${progress}%`,
|
||||
transition: 'all 0.3s ease',
|
||||
borderRadius: 6,
|
||||
boxShadow: progress > 0 ? '0 0 10px rgba(24, 144, 255, 0.3)' : 'none'
|
||||
boxShadow: progress > 0 ? 'var(--shadow-card)' : 'none'
|
||||
}} />
|
||||
</div>
|
||||
|
||||
|
||||
{/* 进度百分比 */}
|
||||
{showPercentage && (
|
||||
<div style={{
|
||||
textAlign: 'center',
|
||||
fontSize: 32,
|
||||
fontWeight: 'bold',
|
||||
color: progress === 100 ? '#52c41a' : '#1890ff',
|
||||
color: progress === 100 ? 'var(--color-success)' : 'var(--color-primary)',
|
||||
marginBottom: 8
|
||||
}}>
|
||||
{progress}%
|
||||
@@ -107,7 +107,7 @@ export const SSEProgressModal: React.FC<SSEProgressModalProps> = ({
|
||||
<div style={{
|
||||
textAlign: 'center',
|
||||
fontSize: 16,
|
||||
color: '#595959',
|
||||
color: 'var(--color-text-secondary)',
|
||||
minHeight: 24,
|
||||
padding: '0 20px',
|
||||
marginBottom: 16
|
||||
@@ -119,7 +119,7 @@ export const SSEProgressModal: React.FC<SSEProgressModalProps> = ({
|
||||
<div style={{
|
||||
textAlign: 'center',
|
||||
fontSize: 13,
|
||||
color: '#8c8c8c',
|
||||
color: 'var(--color-text-tertiary)',
|
||||
marginBottom: onCancel ? 16 : 0
|
||||
}}>
|
||||
请勿关闭页面,生成过程需要一定时间
|
||||
|
||||
@@ -122,23 +122,23 @@ export default function UserMenu() {
|
||||
alignItems: 'center',
|
||||
gap: 12,
|
||||
padding: '8px 16px',
|
||||
background: 'rgba(255, 255, 255, 0.95)',
|
||||
background: 'rgba(255, 255, 255, 0.6)', // 保持半透明以配合 Backdrop
|
||||
backdropFilter: 'blur(10px)',
|
||||
WebkitBackdropFilter: 'blur(10px)',
|
||||
borderRadius: 24,
|
||||
border: '1px solid rgba(102, 126, 234, 0.2)',
|
||||
border: '1px solid var(--color-border)',
|
||||
transition: 'all 0.3s ease',
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
|
||||
boxShadow: 'var(--shadow-card)',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.background = 'rgba(255, 255, 255, 1)';
|
||||
e.currentTarget.style.background = 'var(--color-bg-container)'; // 悬浮时变实
|
||||
e.currentTarget.style.transform = 'translateY(-2px)';
|
||||
e.currentTarget.style.boxShadow = '0 4px 12px rgba(102, 126, 234, 0.3)';
|
||||
e.currentTarget.style.boxShadow = 'var(--shadow-elevated)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.background = 'rgba(255, 255, 255, 0.95)';
|
||||
e.currentTarget.style.background = 'rgba(255, 255, 255, 0.6)';
|
||||
e.currentTarget.style.transform = 'translateY(0)';
|
||||
e.currentTarget.style.boxShadow = '0 2px 8px rgba(0, 0, 0, 0.1)';
|
||||
e.currentTarget.style.boxShadow = 'var(--shadow-card)';
|
||||
}}
|
||||
>
|
||||
<div style={{ position: 'relative' }}>
|
||||
@@ -147,9 +147,9 @@ export default function UserMenu() {
|
||||
icon={<UserOutlined />}
|
||||
size={40}
|
||||
style={{
|
||||
backgroundColor: '#1890ff',
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
border: '3px solid #fff',
|
||||
boxShadow: '0 2px 8px rgba(102, 126, 234, 0.3)',
|
||||
boxShadow: 'var(--shadow-card)',
|
||||
}}
|
||||
/>
|
||||
{currentUser.is_admin && (
|
||||
@@ -173,14 +173,14 @@ export default function UserMenu() {
|
||||
</div>
|
||||
<Space direction="vertical" size={0} style={{ display: window.innerWidth <= 768 ? 'none' : 'flex' }}>
|
||||
<Text strong style={{
|
||||
color: '#262626',
|
||||
color: 'var(--color-text-primary)',
|
||||
fontSize: 14,
|
||||
lineHeight: '20px',
|
||||
}}>
|
||||
{currentUser.display_name || currentUser.username}
|
||||
</Text>
|
||||
<Text style={{
|
||||
color: '#8c8c8c',
|
||||
color: 'var(--color-text-secondary)',
|
||||
fontSize: 12,
|
||||
lineHeight: '18px',
|
||||
}}>
|
||||
|
||||
Reference in New Issue
Block a user