style:1.重构整个项目的主题颜色,样式风格采用中国风元素 2.优化更新日志逻辑,不再间隔1h自动刷新过于频繁触发403响应

This commit is contained in:
xiamuceer
2025-12-11 17:01:25 +08:00
parent 02bd2a2529
commit 46d56d9fd8
27 changed files with 2892 additions and 2329 deletions
+15 -15
View File
@@ -748,10 +748,10 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
// 获取步骤状态图标和颜色 // 获取步骤状态图标和颜色
const getStepStatus = (step: GenerationStep) => { const getStepStatus = (step: GenerationStep) => {
if (step === 'completed') return { icon: <CheckCircleOutlined />, color: '#52c41a' }; if (step === 'completed') return { icon: <CheckCircleOutlined />, color: 'var(--color-success)' };
if (step === 'processing') return { icon: <LoadingOutlined />, color: '#1890ff' }; if (step === 'processing') return { icon: <LoadingOutlined />, color: 'var(--color-primary)' };
if (step === 'error') return { icon: '✗', color: '#ff4d4f' }; if (step === 'error') return { icon: '✗', color: 'var(--color-error)' };
return { icon: '○', color: '#d9d9d9' }; return { icon: '○', color: 'var(--color-text-quaternary)' };
}; };
const hasError = generationSteps.worldBuilding === 'error' || const hasError = generationSteps.worldBuilding === 'error' ||
@@ -770,7 +770,7 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
level={isMobile ? 4 : 3} level={isMobile ? 4 : 3}
style={{ style={{
marginBottom: 32, marginBottom: 32,
color: '#fff', color: 'var(--color-text-primary)',
wordBreak: 'break-word', wordBreak: 'break-word',
whiteSpace: 'normal', whiteSpace: 'normal',
overflowWrap: 'break-word' overflowWrap: 'break-word'
@@ -784,8 +784,8 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
percent={progress} percent={progress}
status={hasError ? 'exception' : (progress === 100 ? 'success' : 'active')} status={hasError ? 'exception' : (progress === 100 ? 'success' : 'active')}
strokeColor={{ strokeColor={{
'0%': '#667eea', '0%': 'var(--color-primary)',
'100%': '#764ba2', '100%': 'var(--color-primary-active)',
}} }}
style={{ marginBottom: 24 }} style={{ marginBottom: 24 }}
/> />
@@ -794,7 +794,7 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
style={{ style={{
fontSize: isMobile ? 14 : 16, fontSize: isMobile ? 14 : 16,
marginBottom: 32, marginBottom: 32,
color: hasError ? '#ff4d4f' : '#666', color: hasError ? 'var(--color-error)' : 'var(--color-text-secondary)',
wordBreak: 'break-word', wordBreak: 'break-word',
whiteSpace: 'normal', whiteSpace: 'normal',
overflowWrap: 'break-word' overflowWrap: 'break-word'
@@ -808,18 +808,18 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
size="small" size="small"
style={{ style={{
marginBottom: 24, marginBottom: 24,
background: '#fff2f0', background: 'var(--color-error-bg)',
borderColor: '#ffccc7', borderColor: 'var(--color-error-border)',
textAlign: 'left', textAlign: 'left',
maxWidth: '100%', maxWidth: '100%',
overflow: 'hidden' overflow: 'hidden'
}} }}
> >
<Text strong style={{ color: '#ff4d4f' }}></Text> <Text strong style={{ color: 'var(--color-error)' }}></Text>
<br /> <br />
<Text <Text
style={{ style={{
color: '#666', color: 'var(--color-text-secondary)',
fontSize: 14, fontSize: 14,
wordBreak: 'break-word', wordBreak: 'break-word',
whiteSpace: 'normal', whiteSpace: 'normal',
@@ -855,9 +855,9 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-between', justifyContent: 'space-between',
padding: isMobile ? '10px 12px' : '12px 20px', 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, 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', gap: '8px',
maxWidth: '100%', maxWidth: '100%',
overflow: 'hidden' overflow: 'hidden'
@@ -894,7 +894,7 @@ export const AIProjectGenerator: React.FC<AIProjectGeneratorProps> = ({
<Paragraph <Paragraph
type="secondary" type="secondary"
style={{ style={{
color: '#fff', color: 'var(--color-text-secondary)',
opacity: 0.9, opacity: 0.9,
wordBreak: 'break-word', wordBreak: 'break-word',
whiteSpace: 'normal', whiteSpace: 'normal',
+12 -12
View File
@@ -56,7 +56,7 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
<div style={{ <div style={{
marginBottom: '16px', marginBottom: '16px',
fontSize: '16px', fontSize: '16px',
color: '#666', color: 'var(--color-text-secondary)',
lineHeight: '1.6', lineHeight: '1.6',
}}> }}>
<p>👋 </p> <p>👋 </p>
@@ -72,7 +72,7 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
<li>🐛 </li> <li>🐛 </li>
<li>📚 </li> <li>📚 </li>
</ul> </ul>
<p style={{ fontWeight: 600, color: '#333', marginBottom: '16px' }}> <p style={{ fontWeight: 600, color: 'var(--color-text-primary)', marginBottom: '16px' }}>
</p> </p>
</div> </div>
@@ -83,7 +83,7 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
alignItems: 'flex-start', alignItems: 'flex-start',
gap: '24px', gap: '24px',
padding: '20px', padding: '20px',
background: '#f5f5f5', background: 'var(--color-bg-layout)',
borderRadius: '8px', borderRadius: '8px',
flexWrap: 'wrap', flexWrap: 'wrap',
}}> }}>
@@ -94,7 +94,7 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
alignItems: 'center', alignItems: 'center',
minWidth: '280px', 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交流群 QQ交流群
</p> </p>
{!qqImageError ? ( {!qqImageError ? (
@@ -102,7 +102,7 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
display: 'flex', display: 'flex',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
background: '#fff', background: 'var(--color-bg-container)',
borderRadius: '8px', borderRadius: '8px',
padding: '8px', padding: '8px',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)', boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
@@ -128,7 +128,7 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
display: 'flex', display: 'flex',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
background: '#fff', background: 'var(--color-bg-container)',
borderRadius: '8px', borderRadius: '8px',
color: '#999', color: '#999',
}}> }}>
@@ -144,7 +144,7 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
alignItems: 'center', alignItems: 'center',
minWidth: '280px', 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> </p>
{!wxImageError ? ( {!wxImageError ? (
@@ -152,7 +152,7 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
display: 'flex', display: 'flex',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
background: '#fff', background: 'var(--color-bg-container)',
borderRadius: '8px', borderRadius: '8px',
padding: '8px', padding: '8px',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)', boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
@@ -178,7 +178,7 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
display: 'flex', display: 'flex',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
background: '#fff', background: 'var(--color-bg-container)',
borderRadius: '8px', borderRadius: '8px',
color: '#999', color: '#999',
}}> }}>
@@ -191,11 +191,11 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
<div style={{ <div style={{
marginTop: '20px', marginTop: '20px',
padding: '12px', padding: '12px',
background: '#fff7e6', background: 'var(--color-warning-bg)',
borderRadius: '8px', borderRadius: '8px',
border: '1px solid #ffd591', border: '1px solid var(--color-warning-border)',
fontSize: '14px', fontSize: '14px',
color: '#ad6800', color: 'var(--color-warning)',
}}> }}>
💡 "今日内不再展示""永不再展示" 💡 "今日内不再展示""永不再展示"
</div> </div>
+25 -33
View File
@@ -46,10 +46,11 @@ export default function AppFooter() {
right: 0, right: 0,
backdropFilter: 'blur(20px) saturate(180%)', backdropFilter: 'blur(20px) saturate(180%)',
WebkitBackdropFilter: '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', padding: isMobile ? '8px 12px' : '10px 16px',
zIndex: 100, 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 <div
@@ -77,29 +78,26 @@ export default function AppFooter() {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: 4, gap: 4,
color: '#fff', color: 'var(--color-primary)',
textShadow: '0 1px 3px rgba(0, 0, 0, 0.3)',
cursor: hasUpdate ? 'pointer' : 'default', 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> <span>{getVersionString()}</span>
</Text> </Text>
</Tooltip> </Tooltip>
</Badge> </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 <Button
type="primary" type="text"
size="small" size="small"
icon={<GiftOutlined />} icon={<GiftOutlined />}
onClick={() => window.open('https://mumuverse.space:1588/', '_blank')} onClick={() => window.open('https://mumuverse.space:1588/', '_blank')}
style={{ style={{
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', color: 'var(--color-text-secondary)',
border: 'none',
boxShadow: '0 2px 8px rgba(102, 126, 234, 0.4)',
fontSize: 11, fontSize: 11,
height: 24, height: 24,
padding: '0 8px', padding: '0 4px',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: 4, gap: 4,
@@ -107,7 +105,7 @@ export default function AppFooter() {
> >
</Button> </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 <Link
href={VERSION_INFO.githubUrl} href={VERSION_INFO.githubUrl}
target="_blank" target="_blank"
@@ -117,8 +115,7 @@ export default function AppFooter() {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: 4, gap: 4,
color: '#fff', color: 'var(--color-text-secondary)',
textShadow: '0 1px 3px rgba(0, 0, 0, 0.3)',
}} }}
> >
<GithubOutlined style={{ fontSize: 12 }} /> <GithubOutlined style={{ fontSize: 12 }} />
@@ -126,8 +123,7 @@ export default function AppFooter() {
<Text <Text
style={{ style={{
fontSize: 10, fontSize: 10,
color: 'rgba(255, 255, 255, 0.8)', color: 'var(--color-text-tertiary)',
textShadow: '0 1px 2px rgba(0, 0, 0, 0.3)',
}} }}
> >
<ClockCircleOutlined style={{ fontSize: 10, marginRight: 4 }} /> <ClockCircleOutlined style={{ fontSize: 10, marginRight: 4 }} />
@@ -139,7 +135,7 @@ export default function AppFooter() {
<Space <Space
direction="horizontal" direction="horizontal"
size={12} size={12}
split={<Divider type="vertical" style={{ borderColor: 'rgba(255, 255, 255, 0.3)' }} />} split={<Divider type="vertical" style={{ borderColor: 'var(--color-border)' }} />}
style={{ style={{
display: 'flex', display: 'flex',
justifyContent: 'center', justifyContent: 'center',
@@ -156,8 +152,8 @@ export default function AppFooter() {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: 6, gap: 6,
color: '#fff', color: 'var(--color-text-secondary)',
textShadow: '0 1px 3px rgba(0, 0, 0, 0.3)', textShadow: 'none',
cursor: hasUpdate ? 'pointer' : 'default', cursor: hasUpdate ? 'pointer' : 'default',
transition: 'all 0.3s', 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> <span>{getVersionString()}</span>
</Text> </Text>
</Tooltip> </Tooltip>
@@ -188,8 +184,7 @@ export default function AppFooter() {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: 6, gap: 6,
color: '#fff', color: 'var(--color-text-secondary)',
textShadow: '0 1px 3px rgba(0, 0, 0, 0.3)',
}} }}
> >
<GithubOutlined style={{ fontSize: 13 }} /> <GithubOutlined style={{ fontSize: 13 }} />
@@ -203,8 +198,7 @@ export default function AppFooter() {
rel="noopener noreferrer" rel="noopener noreferrer"
style={{ style={{
fontSize: 12, fontSize: 12,
color: '#fff', color: 'var(--color-text-secondary)',
textShadow: '0 1px 3px rgba(0, 0, 0, 0.3)',
}} }}
> >
LinuxDO LinuxDO
@@ -216,9 +210,9 @@ export default function AppFooter() {
icon={<GiftOutlined style={{ fontSize: 14 }} />} icon={<GiftOutlined style={{ fontSize: 14 }} />}
onClick={() => window.open('https://mumuverse.space:1588/', '_blank')} onClick={() => window.open('https://mumuverse.space:1588/', '_blank')}
style={{ style={{
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'var(--color-primary)',
border: 'none', border: 'none',
boxShadow: '0 4px 12px rgba(102, 126, 234, 0.5)', boxShadow: '0 4px 12px rgba(77, 128, 136, 0.3)',
fontSize: 13, fontSize: 13,
height: 32, height: 32,
padding: '0 20px', padding: '0 20px',
@@ -250,8 +244,7 @@ export default function AppFooter() {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: 6, gap: 6,
color: '#fff', color: 'var(--color-text-secondary)',
textShadow: '0 1px 3px rgba(0, 0, 0, 0.3)',
}} }}
> >
<CopyrightOutlined style={{ fontSize: 11 }} /> <CopyrightOutlined style={{ fontSize: 11 }} />
@@ -265,8 +258,7 @@ export default function AppFooter() {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: 4, gap: 4,
color: 'rgba(255, 255, 255, 0.9)', color: 'var(--color-text-tertiary)',
textShadow: '0 1px 3px rgba(0, 0, 0, 0.3)',
}} }}
> >
<ClockCircleOutlined style={{ fontSize: 12 }} /> <ClockCircleOutlined style={{ fontSize: 12 }} />
@@ -280,12 +272,12 @@ export default function AppFooter() {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: 4, gap: 4,
color: '#fff', color: 'var(--color-text-secondary)',
textShadow: '0 1px 3px rgba(0, 0, 0, 0.3)', textShadow: '0 1px 3px rgba(0, 0, 0, 0.05)',
}} }}
> >
<span>Made with</span> <span>Made with</span>
<HeartFilled style={{ color: '#ff4d4f', fontSize: 11 }} /> <HeartFilled style={{ color: 'var(--color-error)', fontSize: 11 }} />
<span>by {VERSION_INFO.author}</span> <span>by {VERSION_INFO.author}</span>
</Text> </Text>
</Space> </Space>
+14 -13
View File
@@ -18,7 +18,7 @@ export const cardStyles = {
// height: 320, // height: 320,
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
borderColor: '#1890ff', borderColor: 'var(--color-info)',
borderRadius: 12, borderRadius: 12,
} as CSSProperties, } as CSSProperties,
@@ -27,19 +27,20 @@ export const cardStyles = {
// height: 320, // height: 320,
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
borderColor: '#52c41a', borderColor: 'var(--color-success)',
backgroundColor: '#f6ffed', backgroundColor: 'var(--color-bg-base)', // 使用柔和的背景色
borderRadius: 12, borderRadius: 12,
} as CSSProperties, } as CSSProperties,
// 项目卡片样式 // 项目卡片样式 - 现代化设计
project: { project: {
height: '100%', height: '100%',
borderRadius: 16, borderRadius: 20,
overflow: 'hidden', overflow: 'hidden',
background: '#fff', background: 'var(--color-bg-container)',
boxShadow: '0 4px 16px rgba(0, 0, 0, 0.08)', boxShadow: '0 4px 20px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.04)',
transition: 'all 0.3s ease', transition: 'all 0.35s cubic-bezier(0.4, 0, 0.2, 1)',
border: '1px solid rgba(0, 0, 0, 0.04)',
} as CSSProperties, } as CSSProperties,
// 卡片内容区域样式 // 卡片内容区域样式
@@ -73,17 +74,17 @@ export const cardStyles = {
} as CSSProperties), } as CSSProperties),
}; };
// 卡片悬浮动画 // 卡片悬浮动画 - 增强版
export const cardHoverHandlers = { export const cardHoverHandlers = {
onMouseEnter: (e: React.MouseEvent<HTMLDivElement>) => { onMouseEnter: (e: React.MouseEvent<HTMLDivElement>) => {
const target = e.currentTarget; const target = e.currentTarget;
target.style.transform = 'translateY(-8px)'; target.style.transform = 'translateY(-10px) scale(1.01)';
target.style.boxShadow = '0 12px 32px rgba(0, 0, 0, 0.15)'; 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>) => { onMouseLeave: (e: React.MouseEvent<HTMLDivElement>) => {
const target = e.currentTarget; const target = e.currentTarget;
target.style.transform = 'translateY(0)'; target.style.transform = 'translateY(0) scale(1)';
target.style.boxShadow = '0 4px 16px rgba(0, 0, 0, 0.08)'; target.style.boxShadow = '0 4px 20px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.04)';
}, },
}; };
+26 -46
View File
@@ -16,10 +16,7 @@ import {
import { import {
fetchChangelog, fetchChangelog,
groupChangelogByDate, groupChangelogByDate,
getCachedChangelog,
cacheChangelog, cacheChangelog,
markChangelogFetched,
shouldFetchChangelog,
clearChangelogCache, clearChangelogCache,
type ChangelogEntry, type ChangelogEntry,
} from '../services/changelogService'; } from '../services/changelogService';
@@ -50,33 +47,13 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
const [hasMore, setHasMore] = useState(true); const [hasMore, setHasMore] = useState(true);
// 加载更新日志 // 加载更新日志
// 每次用户打开窗口时才同步获取最新数据,不自动刷新
const loadChangelog = async (pageNum: number = 1, append: boolean = false) => { const loadChangelog = async (pageNum: number = 1, append: boolean = false) => {
setLoading(true); setLoading(true);
setError(null); setError(null);
try { 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); const entries = await fetchChangelog(pageNum, 30);
if (entries.length === 0) { if (entries.length === 0) {
@@ -86,10 +63,9 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
setChangelog(prev => [...prev, ...entries]); setChangelog(prev => [...prev, ...entries]);
} else { } else {
setChangelog(entries); setChangelog(entries);
// 缓存第一页数据 // 缓存第一页数据(用于分页加载时的数据持久化)
if (pageNum === 1) { if (pageNum === 1) {
cacheChangelog(entries); cacheChangelog(entries);
markChangelogFetched();
} }
} }
} }
@@ -180,10 +156,10 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
<div style={{ <div style={{
padding: '16px', padding: '16px',
marginBottom: '16px', marginBottom: '16px',
background: '#fff2e8', background: 'var(--color-error-bg)',
border: '1px solid #ffbb96', border: '1px solid var(--color-error-border)',
borderRadius: '4px', borderRadius: '4px',
color: '#d4380d', color: 'var(--color-error)',
}}> }}>
{error} {error}
</div> </div>
@@ -205,10 +181,10 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
<div style={{ <div style={{
fontSize: '16px', fontSize: '16px',
fontWeight: 600, fontWeight: 600,
color: '#1890ff', color: 'var(--color-primary)',
marginBottom: '16px', marginBottom: '16px',
paddingBottom: '8px', paddingBottom: '8px',
borderBottom: '2px solid #e8e8e8', borderBottom: '2px solid var(--color-border-secondary)',
}}> }}>
<ClockCircleOutlined style={{ marginRight: '8px' }} /> <ClockCircleOutlined style={{ marginRight: '8px' }} />
{formatDate(date)} {formatDate(date)}
@@ -226,8 +202,8 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
width: '24px', width: '24px',
height: '24px', height: '24px',
borderRadius: '50%', borderRadius: '50%',
background: '#fff', background: 'var(--color-bg-container)',
border: `2px solid ${config.color === 'default' ? '#d9d9d9' : config.color}`, border: `2px solid ${config.color === 'default' ? 'var(--color-border)' : config.color}`,
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
@@ -245,7 +221,7 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
{entry.scope && ( {entry.scope && (
<Tag color="blue">{entry.scope}</Tag> <Tag color="blue">{entry.scope}</Tag>
)} )}
<span style={{ color: '#999', fontSize: '12px' }}> <span style={{ color: 'var(--color-text-tertiary)', fontSize: '12px' }}>
{formatTime(entry.date)} {formatTime(entry.date)}
</span> </span>
</Space> </Space>
@@ -254,7 +230,7 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
marginTop: '8px', marginTop: '8px',
fontSize: '14px', fontSize: '14px',
lineHeight: '1.6', lineHeight: '1.6',
color: '#333', color: 'var(--color-text-primary)',
}}> }}>
{entry.message} {entry.message}
</div> </div>
@@ -263,7 +239,7 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
{entry.author.avatar && ( {entry.author.avatar && (
<Avatar size="small" src={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} {entry.author.username || entry.author.name}
</span> </span>
<a <a
@@ -284,7 +260,8 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
); );
})} })}
{hasMore && ( {
hasMore && (
<div style={{ textAlign: 'center', marginTop: '24px' }}> <div style={{ textAlign: 'center', marginTop: '24px' }}>
<Button <Button
type="default" type="default"
@@ -294,31 +271,34 @@ export default function ChangelogModal({ visible, onClose }: ChangelogModalProps
</Button> </Button>
</div> </div>
)} )
}
{!hasMore && changelog.length > 0 && ( {
!hasMore && changelog.length > 0 && (
<div style={{ <div style={{
textAlign: 'center', textAlign: 'center',
color: '#999', color: 'var(--color-text-tertiary)',
padding: '16px 0', padding: '16px 0',
fontSize: '14px', fontSize: '14px',
}}> }}>
</div> </div>
)} )
}
</> </>
)} )}
<div style={{ <div style={{
marginTop: '24px', marginTop: '24px',
padding: '12px', padding: '12px',
background: '#f0f5ff', background: 'var(--color-info-bg)',
borderRadius: '4px', borderRadius: '4px',
border: '1px solid #adc6ff', border: '1px solid var(--color-info-border)',
fontSize: '13px', fontSize: '13px',
color: '#1d39c4', color: 'var(--color-primary)',
}}> }}>
💡 GitHub 💡 GitHub
</div> </div>
</Modal > </Modal >
); );
+13 -13
View File
@@ -191,13 +191,13 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
switch (task.status) { switch (task.status) {
case 'pending': case 'pending':
return <ClockCircleOutlined style={{ color: '#faad14' }} />; return <ClockCircleOutlined style={{ color: 'var(--color-warning)' }} />;
case 'running': case 'running':
return <Spin />; return <Spin />;
case 'completed': case 'completed':
return <CheckCircleOutlined style={{ color: '#52c41a' }} />; return <CheckCircleOutlined style={{ color: 'var(--color-success)' }} />;
case 'failed': case 'failed':
return <CloseCircleOutlined style={{ color: '#ff4d4f' }} />; return <CloseCircleOutlined style={{ color: 'var(--color-error)' }} />;
default: default:
return null; return null;
} }
@@ -225,7 +225,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
fontSize: 20, fontSize: 20,
fontWeight: 'bold', fontWeight: 'bold',
marginTop: 16, marginTop: 16,
color: task.status === 'failed' ? '#ff4d4f' : '#262626' color: task.status === 'failed' ? 'var(--color-error)' : 'var(--color-text-primary)'
}}> }}>
{task.status === 'pending' && '等待分析...'} {task.status === 'pending' && '等待分析...'}
{task.status === 'running' && 'AI正在分析中...'} {task.status === 'running' && 'AI正在分析中...'}
@@ -241,7 +241,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
}}> }}>
<div style={{ <div style={{
height: 12, height: 12,
background: '#f0f0f0', background: 'var(--color-bg-layout)',
borderRadius: 6, borderRadius: 6,
overflow: 'hidden', overflow: 'hidden',
marginBottom: 12 marginBottom: 12
@@ -249,10 +249,10 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
<div style={{ <div style={{
height: '100%', height: '100%',
background: task.status === 'failed' background: task.status === 'failed'
? 'linear-gradient(90deg, #ff4d4f 0%, #ff7875 100%)' ? 'var(--color-error)'
: task.progress === 100 : task.progress === 100
? 'linear-gradient(90deg, #52c41a 0%, #73d13d 100%)' ? 'var(--color-success)'
: 'linear-gradient(90deg, #1890ff 0%, #40a9ff 100%)', : 'var(--color-primary)',
width: `${task.progress}%`, width: `${task.progress}%`,
transition: 'all 0.3s ease', transition: 'all 0.3s ease',
borderRadius: 6, borderRadius: 6,
@@ -267,8 +267,8 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
textAlign: 'center', textAlign: 'center',
fontSize: 32, fontSize: 32,
fontWeight: 'bold', fontWeight: 'bold',
color: task.status === 'failed' ? '#ff4d4f' : color: task.status === 'failed' ? 'var(--color-error)' :
task.progress === 100 ? '#52c41a' : '#1890ff', task.progress === 100 ? 'var(--color-success)' : 'var(--color-primary)',
marginBottom: 8 marginBottom: 8
}}> }}>
{task.progress}% {task.progress}%
@@ -279,7 +279,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
<div style={{ <div style={{
textAlign: 'center', textAlign: 'center',
fontSize: 16, fontSize: 16,
color: '#595959', color: 'var(--color-text-secondary)',
minHeight: 24, minHeight: 24,
marginBottom: 16 marginBottom: 16
}}> }}>
@@ -307,7 +307,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
<div style={{ <div style={{
textAlign: 'center', textAlign: 'center',
fontSize: 13, fontSize: 13,
color: '#8c8c8c', color: 'var(--color-text-tertiary)',
marginTop: 16 marginTop: 16
}}> }}>
@@ -374,7 +374,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
title="整体质量" title="整体质量"
value={analysis_data.overall_quality_score || 0} value={analysis_data.overall_quality_score || 0}
suffix="/ 10" suffix="/ 10"
valueStyle={{ color: '#3f8600' }} valueStyle={{ color: 'var(--color-success)' }}
/> />
</Col> </Col>
<Col span={isMobile ? 12 : 6}> <Col span={isMobile ? 12 : 6}>
@@ -156,7 +156,7 @@ const ChapterContentComparison: React.FC<ChapterContentComparisonProps> = ({
title="字数变化" title="字数变化"
value={wordCountDiff} value={wordCountDiff}
suffix="字" suffix="字"
valueStyle={{ color: wordCountDiff > 0 ? '#3f8600' : '#cf1322' }} valueStyle={{ color: wordCountDiff > 0 ? 'var(--color-success)' : 'var(--color-error)' }}
prefix={wordCountDiff > 0 ? '+' : ''} prefix={wordCountDiff > 0 ? '+' : ''}
/> />
</Col> </Col>
@@ -165,7 +165,7 @@ const ChapterContentComparison: React.FC<ChapterContentComparisonProps> = ({
title="变化比例" title="变化比例"
value={wordCountDiffPercent} value={wordCountDiffPercent}
suffix="%" 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 ? '+' : ''} prefix={wordCountDiff > 0 ? '+' : ''}
/> />
</Col> </Col>
@@ -176,7 +176,7 @@ const ChapterContentComparison: React.FC<ChapterContentComparisonProps> = ({
<div style={{ <div style={{
maxHeight: 'calc(90vh - 300px)', maxHeight: 'calc(90vh - 300px)',
overflow: 'auto', overflow: 'auto',
border: '1px solid #d9d9d9', border: '1px solid var(--color-border)',
borderRadius: 4 borderRadius: 4
}}> }}>
<ReactDiffViewer <ReactDiffViewer
@@ -190,19 +190,19 @@ const ChapterContentComparison: React.FC<ChapterContentComparisonProps> = ({
styles={{ styles={{
variables: { variables: {
light: { light: {
diffViewerBackground: '#fff', diffViewerBackground: '#fff', // Keep white for diff viewer readability
addedBackground: '#e6ffed', addedBackground: 'var(--color-success-bg)',
addedColor: '#24292e', addedColor: 'var(--color-text-primary)',
removedBackground: '#ffeef0', removedBackground: 'var(--color-error-bg)',
removedColor: '#24292e', removedColor: 'var(--color-text-primary)',
wordAddedBackground: '#acf2bd', wordAddedBackground: 'var(--color-success-border)',
wordRemovedBackground: '#fdb8c0', wordRemovedBackground: 'var(--color-error-border)',
addedGutterBackground: '#cdffd8', addedGutterBackground: 'var(--color-success-bg)',
removedGutterBackground: '#ffdce0', removedGutterBackground: 'var(--color-error-bg)',
gutterBackground: '#f6f8fa', gutterBackground: 'var(--color-bg-layout)',
gutterBackgroundDark: '#f3f4f6', gutterBackgroundDark: 'var(--color-bg-container)',
highlightBackground: '#fffbdd', highlightBackground: 'var(--color-warning-bg)',
highlightGutterBackground: '#fff5b1', highlightGutterBackground: 'var(--color-warning-border)',
}, },
}, },
line: { line: {
@@ -42,13 +42,13 @@ export const SSELoadingOverlay: React.FC<SSELoadingOverlayProps> = ({
marginBottom: 24 marginBottom: 24
}}> }}>
<Spin <Spin
indicator={<LoadingOutlined style={{ fontSize: 48, color: '#1890ff' }} spin />} indicator={<LoadingOutlined style={{ fontSize: 48, color: 'var(--color-primary)' }} spin />}
/> />
<div style={{ <div style={{
fontSize: 20, fontSize: 20,
fontWeight: 'bold', fontWeight: 'bold',
marginTop: 16, marginTop: 16,
color: '#262626' color: 'var(--color-text-primary)'
}}> }}>
AI生成中... AI生成中...
</div> </div>
@@ -60,7 +60,7 @@ export const SSELoadingOverlay: React.FC<SSELoadingOverlayProps> = ({
}}> }}>
<div style={{ <div style={{
height: 12, height: 12,
background: '#f0f0f0', background: 'var(--color-bg-layout)',
borderRadius: 6, borderRadius: 6,
overflow: 'hidden', overflow: 'hidden',
marginBottom: 12 marginBottom: 12
@@ -68,12 +68,12 @@ export const SSELoadingOverlay: React.FC<SSELoadingOverlayProps> = ({
<div style={{ <div style={{
height: '100%', height: '100%',
background: progress === 100 background: progress === 100
? 'linear-gradient(90deg, #52c41a 0%, #73d13d 100%)' ? 'linear-gradient(90deg, var(--color-success) 0%, var(--color-success-active) 100%)'
: 'linear-gradient(90deg, #1890ff 0%, #40a9ff 100%)', : 'linear-gradient(90deg, var(--color-primary) 0%, var(--color-primary-active) 100%)',
width: `${progress}%`, width: `${progress}%`,
transition: 'all 0.3s ease', transition: 'all 0.3s ease',
borderRadius: 6, borderRadius: 6,
boxShadow: progress > 0 ? '0 0 10px rgba(24, 144, 255, 0.3)' : 'none' boxShadow: progress > 0 ? 'var(--shadow-card)' : 'none'
}} /> }} />
</div> </div>
@@ -82,7 +82,7 @@ export const SSELoadingOverlay: React.FC<SSELoadingOverlayProps> = ({
textAlign: 'center', textAlign: 'center',
fontSize: 32, fontSize: 32,
fontWeight: 'bold', fontWeight: 'bold',
color: progress === 100 ? '#52c41a' : '#1890ff', color: progress === 100 ? 'var(--color-success)' : 'var(--color-primary)',
marginBottom: 8 marginBottom: 8
}}> }}>
{progress}% {progress}%
+9 -9
View File
@@ -53,13 +53,13 @@ export const SSEProgressModal: React.FC<SSEProgressModalProps> = ({
marginBottom: 24 marginBottom: 24
}}> }}>
<Spin <Spin
indicator={<LoadingOutlined style={{ fontSize: 48, color: '#1890ff' }} spin />} indicator={<LoadingOutlined style={{ fontSize: 48, color: 'var(--color-primary)' }} spin />}
/> />
<div style={{ <div style={{
fontSize: 20, fontSize: 20,
fontWeight: 'bold', fontWeight: 'bold',
marginTop: 16, marginTop: 16,
color: '#262626' color: 'var(--color-text-primary)'
}}> }}>
{title} {title}
</div> </div>
@@ -72,7 +72,7 @@ export const SSEProgressModal: React.FC<SSEProgressModalProps> = ({
}}> }}>
<div style={{ <div style={{
height: 12, height: 12,
background: '#f0f0f0', background: 'var(--color-bg-layout)',
borderRadius: 6, borderRadius: 6,
overflow: 'hidden', overflow: 'hidden',
marginBottom: showPercentage ? 12 : 0 marginBottom: showPercentage ? 12 : 0
@@ -80,12 +80,12 @@ export const SSEProgressModal: React.FC<SSEProgressModalProps> = ({
<div style={{ <div style={{
height: '100%', height: '100%',
background: progress === 100 background: progress === 100
? 'linear-gradient(90deg, #52c41a 0%, #73d13d 100%)' ? 'linear-gradient(90deg, var(--color-success) 0%, var(--color-success-active) 100%)'
: 'linear-gradient(90deg, #1890ff 0%, #40a9ff 100%)', : 'linear-gradient(90deg, var(--color-primary) 0%, var(--color-primary-active) 100%)',
width: `${progress}%`, width: `${progress}%`,
transition: 'all 0.3s ease', transition: 'all 0.3s ease',
borderRadius: 6, borderRadius: 6,
boxShadow: progress > 0 ? '0 0 10px rgba(24, 144, 255, 0.3)' : 'none' boxShadow: progress > 0 ? 'var(--shadow-card)' : 'none'
}} /> }} />
</div> </div>
@@ -95,7 +95,7 @@ export const SSEProgressModal: React.FC<SSEProgressModalProps> = ({
textAlign: 'center', textAlign: 'center',
fontSize: 32, fontSize: 32,
fontWeight: 'bold', fontWeight: 'bold',
color: progress === 100 ? '#52c41a' : '#1890ff', color: progress === 100 ? 'var(--color-success)' : 'var(--color-primary)',
marginBottom: 8 marginBottom: 8
}}> }}>
{progress}% {progress}%
@@ -107,7 +107,7 @@ export const SSEProgressModal: React.FC<SSEProgressModalProps> = ({
<div style={{ <div style={{
textAlign: 'center', textAlign: 'center',
fontSize: 16, fontSize: 16,
color: '#595959', color: 'var(--color-text-secondary)',
minHeight: 24, minHeight: 24,
padding: '0 20px', padding: '0 20px',
marginBottom: 16 marginBottom: 16
@@ -119,7 +119,7 @@ export const SSEProgressModal: React.FC<SSEProgressModalProps> = ({
<div style={{ <div style={{
textAlign: 'center', textAlign: 'center',
fontSize: 13, fontSize: 13,
color: '#8c8c8c', color: 'var(--color-text-tertiary)',
marginBottom: onCancel ? 16 : 0 marginBottom: onCancel ? 16 : 0
}}> }}>
+11 -11
View File
@@ -122,23 +122,23 @@ export default function UserMenu() {
alignItems: 'center', alignItems: 'center',
gap: 12, gap: 12,
padding: '8px 16px', padding: '8px 16px',
background: 'rgba(255, 255, 255, 0.95)', background: 'rgba(255, 255, 255, 0.6)', // 保持半透明以配合 Backdrop
backdropFilter: 'blur(10px)', backdropFilter: 'blur(10px)',
WebkitBackdropFilter: 'blur(10px)', WebkitBackdropFilter: 'blur(10px)',
borderRadius: 24, borderRadius: 24,
border: '1px solid rgba(102, 126, 234, 0.2)', border: '1px solid var(--color-border)',
transition: 'all 0.3s ease', transition: 'all 0.3s ease',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)', boxShadow: 'var(--shadow-card)',
}} }}
onMouseEnter={(e) => { 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.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) => { 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.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' }}> <div style={{ position: 'relative' }}>
@@ -147,9 +147,9 @@ export default function UserMenu() {
icon={<UserOutlined />} icon={<UserOutlined />}
size={40} size={40}
style={{ style={{
backgroundColor: '#1890ff', backgroundColor: 'var(--color-primary)',
border: '3px solid #fff', border: '3px solid #fff',
boxShadow: '0 2px 8px rgba(102, 126, 234, 0.3)', boxShadow: 'var(--shadow-card)',
}} }}
/> />
{currentUser.is_admin && ( {currentUser.is_admin && (
@@ -173,14 +173,14 @@ export default function UserMenu() {
</div> </div>
<Space direction="vertical" size={0} style={{ display: window.innerWidth <= 768 ? 'none' : 'flex' }}> <Space direction="vertical" size={0} style={{ display: window.innerWidth <= 768 ? 'none' : 'flex' }}>
<Text strong style={{ <Text strong style={{
color: '#262626', color: 'var(--color-text-primary)',
fontSize: 14, fontSize: 14,
lineHeight: '20px', lineHeight: '20px',
}}> }}>
{currentUser.display_name || currentUser.username} {currentUser.display_name || currentUser.username}
</Text> </Text>
<Text style={{ <Text style={{
color: '#8c8c8c', color: 'var(--color-text-secondary)',
fontSize: 12, fontSize: 12,
lineHeight: '18px', lineHeight: '18px',
}}> }}>
+276 -7
View File
@@ -1,11 +1,63 @@
:root { :root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; /* --- 中国风配色方案 (Chinese Style Palette) --- */
line-height: 1.5;
/* 主色调:天青 (Cerulean / Azure) - 类似汝窑 */
--color-primary: #4D8088;
--color-primary-hover: #5F9EA8;
--color-primary-active: #3A666C;
/* 辅助色 */
--color-success: #52C41A;
--color-warning: #FAAD14;
--color-error: #FF4D4F;
--color-info: #1890FF;
/* 功能色背景/边框 */
--color-success-bg: #F6FFED;
--color-success-border: #B7EB8F;
--color-warning-bg: #FFFBE6;
--color-warning-border: #FFE58F;
--color-error-bg: #FFF2F0;
--color-error-border: #FFCCC7;
--color-info-bg: #E6F7FF;
--color-info-border: #91D5FF;
/* 背景色 */
--color-bg-base: #F8F6F1;
/* 米汤色 (Rice Soup / Cream) - 用于页面背景 */
--color-bg-container: #FFFFFF;
/* 纯白 - 用于卡片/容器 */
--color-bg-layout: #F0F2F5;
--color-bg-spotlight: #3A666C;
--color-bg-mask: rgba(0, 0, 0, 0.45);
/* 文本色 */
--color-text-base: #2B2B2B;
/* 墨色 (Ink) */
--color-text-primary: #2B2B2B;
--color-text-secondary: #595959;
/* 此时 (Secondary Text) */
--color-text-tertiary: #8C8C8C;
--color-text-quaternary: #BFBFBF;
/* 边框色 */
--color-border: #D9D9D9;
--color-border-secondary: #F0F0F0;
/* 阴影 */
--shadow-card: 0 2px 8px rgba(0, 0, 0, 0.06);
--shadow-elevated: 0 8px 24px rgba(77, 128, 136, 0.15);
/* 带一点主色调的阴影 */
--shadow-primary: 0 4px 16px rgba(77, 128, 136, 0.25);
--shadow-header: 0 2px 8px rgba(0, 0, 0, 0.05);
font-family: "PingFang SC", "Microsoft YaHei", "Heiti SC", Inter, system-ui, sans-serif;
line-height: 1.5715;
font-weight: 400; font-weight: 400;
color-scheme: light dark; color-scheme: light;
color: rgba(0, 0, 0, 0.87); color: var(--color-text-base);
background-color: #f0f2f5; background-color: var(--color-bg-base);
font-synthesis: none; font-synthesis: none;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
@@ -72,7 +124,9 @@ body {
} }
/* 移动端优化触摸区域 */ /* 移动端优化触摸区域 */
button, a, [role="button"] { button,
a,
[role="button"] {
min-height: 44px; min-height: 44px;
min-width: 44px; min-width: 44px;
} }
@@ -95,7 +149,9 @@ body {
/* 移动端禁止长按选择 */ /* 移动端禁止长按选择 */
@media (max-width: 768px) { @media (max-width: 768px) {
img, button {
img,
button {
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; -ms-user-select: none;
@@ -131,3 +187,216 @@ body {
.ant-tabs-dropdown { .ant-tabs-dropdown {
z-index: 2000 !important; z-index: 2000 !important;
} }
/* ===== 现代化侧边栏样式 (Modern Sidebar Styles) ===== */
/* 侧边栏容器 - 毛玻璃效果 */
.modern-sider {
background: linear-gradient(180deg,
rgba(255, 255, 255, 0.95) 0%,
rgba(248, 246, 241, 0.98) 100%) !important;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-right: 1px solid rgba(77, 128, 136, 0.12);
box-shadow:
4px 0 24px rgba(77, 128, 136, 0.08),
1px 0 0 rgba(255, 255, 255, 0.8) inset;
}
/* 侧边栏菜单整体样式 */
.modern-sider .ant-menu {
background: transparent !important;
border-right: none !important;
}
/* 菜单项基础样式 */
.modern-sider .ant-menu-item {
margin: 6px 12px !important;
padding: 0 16px !important;
border-radius: 12px !important;
height: 48px !important;
line-height: 48px !important;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
position: relative;
overflow: hidden;
}
/* 折叠状态下的菜单项 */
.modern-sider.ant-layout-sider-collapsed .ant-menu-item {
margin: 6px 8px !important;
padding: 0 !important;
display: flex;
align-items: center;
justify-content: center;
}
/* 菜单项悬停效果 - 仅未选中项 */
.modern-sider .ant-menu-item:not(.ant-menu-item-selected):hover {
background: linear-gradient(135deg,
rgba(77, 128, 136, 0.15) 0%,
rgba(95, 158, 168, 0.22) 100%) !important;
transform: translateX(6px);
box-shadow:
0 2px 8px rgba(77, 128, 136, 0.15),
inset 0 0 0 1px rgba(77, 128, 136, 0.2);
}
/* 折叠状态悬停效果 - 仅未选中项 */
.modern-sider.ant-layout-sider-collapsed .ant-menu-item:not(.ant-menu-item-selected):hover {
transform: translateX(0) scale(1.08);
box-shadow:
0 4px 12px rgba(77, 128, 136, 0.2),
inset 0 0 0 1px rgba(77, 128, 136, 0.25);
}
/* 菜单项选中状态 - 增强版 */
.modern-sider .ant-menu-item-selected {
background: linear-gradient(135deg,
var(--color-primary) 0%,
#5A9BA5 50%,
var(--color-primary-hover) 100%) !important;
box-shadow:
0 6px 20px rgba(77, 128, 136, 0.45),
0 3px 8px rgba(77, 128, 136, 0.25),
inset 0 1px 0 rgba(255, 255, 255, 0.2);
border: 1px solid rgba(255, 255, 255, 0.15);
}
/* 选中项悬停 - 微发光效果 */
.modern-sider .ant-menu-item-selected:hover {
transform: translateX(0) !important;
box-shadow:
0 8px 24px rgba(77, 128, 136, 0.5),
0 4px 10px rgba(77, 128, 136, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.25);
filter: brightness(1.05);
}
.modern-sider.ant-layout-sider-collapsed .ant-menu-item-selected:hover {
transform: scale(1.05) !important;
}
/* 选中状态文字和图标颜色 */
.modern-sider .ant-menu-item-selected,
.modern-sider .ant-menu-item-selected a,
.modern-sider .ant-menu-item-selected .anticon {
color: #fff !important;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
}
/* 未选中状态颜色 */
.modern-sider .ant-menu-item:not(.ant-menu-item-selected),
.modern-sider .ant-menu-item:not(.ant-menu-item-selected) a {
color: var(--color-text-secondary) !important;
}
.modern-sider .ant-menu-item:not(.ant-menu-item-selected) .anticon {
color: var(--color-primary) !important;
}
/* 菜单项悬停时文字和图标颜色 */
.modern-sider .ant-menu-item:not(.ant-menu-item-selected):hover,
.modern-sider .ant-menu-item:not(.ant-menu-item-selected):hover a {
color: var(--color-primary-active) !important;
font-weight: 600;
}
.modern-sider .ant-menu-item:not(.ant-menu-item-selected):hover .anticon {
color: var(--color-primary-active) !important;
}
/* 图标样式优化 */
.modern-sider .ant-menu-item .anticon {
font-size: 18px !important;
transition: all 0.25s cubic-bezier(0.34, 1.56, 0.64, 1) !important;
}
.modern-sider.ant-layout-sider-collapsed .ant-menu-item .anticon {
font-size: 22px !important;
}
/* 悬停时图标微动效 */
.modern-sider .ant-menu-item:not(.ant-menu-item-selected):hover .anticon {
transform: scale(1.15);
}
/* 选中项左侧指示条 */
.modern-sider .ant-menu-item-selected::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 24px;
background: rgba(255, 255, 255, 0.9);
border-radius: 0 4px 4px 0;
opacity: 0;
}
/* 折叠状态隐藏指示条 */
.modern-sider.ant-layout-sider-collapsed .ant-menu-item-selected::before {
display: none;
}
/* 链接文字样式 */
.modern-sider .ant-menu-item a {
font-weight: 500;
letter-spacing: 0.5px;
transition: all 0.3s ease;
}
/* 侧边栏顶部装饰线 */
.modern-sider::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg,
var(--color-primary) 0%,
var(--color-primary-hover) 50%,
var(--color-primary) 100%);
opacity: 0.8;
}
/* 侧边栏底部渐变遮罩 */
.modern-sider::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 60px;
background: linear-gradient(to top,
rgba(248, 246, 241, 0.95) 0%,
transparent 100%);
pointer-events: none;
}
/* 菜单项波纹效果 */
.modern-sider .ant-menu-item::after {
display: none !important;
}
/* 菜单标题样式 (如果有分组) */
.modern-sider .ant-menu-item-group-title {
color: var(--color-text-tertiary) !important;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
padding: 16px 24px 8px !important;
}
/* Tooltip 样式优化 (折叠状态) */
.ant-tooltip .ant-tooltip-inner {
background: linear-gradient(135deg,
var(--color-primary) 0%,
var(--color-primary-hover) 100%);
border-radius: 8px;
padding: 8px 16px;
font-weight: 500;
box-shadow: 0 4px 12px rgba(77, 128, 136, 0.3);
}
+28 -1
View File
@@ -8,7 +8,34 @@ import App from './App.tsx'
createRoot(document.getElementById('root')!).render( createRoot(document.getElementById('root')!).render(
<StrictMode> <StrictMode>
<ConfigProvider locale={zhCN}> <ConfigProvider
locale={zhCN}
theme={{
token: {
colorPrimary: '#4D8088', // 天青
colorBgBase: '#F8F6F1', // 米汤色
colorTextBase: '#2B2B2B', // 墨色
borderRadius: 6,
wireframe: false,
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif",
},
components: {
Layout: {
bodyBg: '#F8F6F1',
headerBg: '#FFFFFF',
siderBg: '#FFFFFF',
},
Card: {
colorBgContainer: '#FFFFFF',
boxShadowTertiary: '0 4px 12px rgba(0, 0, 0, 0.05)', // 更柔和的阴影
},
Button: {
borderRadius: 6,
controlHeight: 36,
}
}
}}
>
<App /> <App />
</ConfigProvider> </ConfigProvider>
</StrictMode>, </StrictMode>,
+28 -26
View File
@@ -15,6 +15,7 @@ const { TextArea } = Input;
export default function Chapters() { export default function Chapters() {
const { currentProject, chapters, setCurrentChapter, setCurrentProject } = useStore(); const { currentProject, chapters, setCurrentChapter, setCurrentProject } = useStore();
const [modal, contextHolder] = Modal.useModal();
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
const [isEditorOpen, setIsEditorOpen] = useState(false); const [isEditorOpen, setIsEditorOpen] = useState(false);
const [isContinuing, setIsContinuing] = useState(false); const [isContinuing, setIsContinuing] = useState(false);
@@ -433,7 +434,7 @@ export default function Chapters() {
const selectedStyle = writingStyles.find(s => s.id === selectedStyleId); const selectedStyle = writingStyles.find(s => s.id === selectedStyleId);
const modal = Modal.confirm({ const instance = modal.confirm({
title: 'AI创作章节内容', title: 'AI创作章节内容',
width: 700, width: 700,
centered: true, centered: true,
@@ -455,11 +456,11 @@ export default function Chapters() {
<div style={{ <div style={{
marginTop: 16, marginTop: 16,
padding: 12, padding: 12,
background: '#f0f5ff', background: 'var(--color-info-bg)',
borderRadius: 4, borderRadius: 4,
border: '1px solid #adc6ff' border: '1px solid var(--color-info-border)'
}}> }}>
<div style={{ marginBottom: 8, fontWeight: 500, color: '#1890ff' }}> <div style={{ marginBottom: 8, fontWeight: 500, color: 'var(--color-primary)' }}>
📚 {previousChapters.length} 📚 {previousChapters.length}
</div> </div>
<div style={{ maxHeight: 150, overflowY: 'auto' }}> <div style={{ maxHeight: 150, overflowY: 'auto' }}>
@@ -484,7 +485,7 @@ export default function Chapters() {
okButtonProps: { danger: true }, okButtonProps: { danger: true },
cancelText: '取消', cancelText: '取消',
onOk: async () => { onOk: async () => {
modal.update({ instance.update({
okButtonProps: { danger: true, loading: true }, okButtonProps: { danger: true, loading: true },
cancelButtonProps: { disabled: true }, cancelButtonProps: { disabled: true },
closable: false, closable: false,
@@ -495,7 +496,7 @@ export default function Chapters() {
try { try {
if (!selectedStyleId) { if (!selectedStyleId) {
message.error('请先选择写作风格'); message.error('请先选择写作风格');
modal.update({ instance.update({
okButtonProps: { danger: true, loading: false }, okButtonProps: { danger: true, loading: false },
cancelButtonProps: { disabled: false }, cancelButtonProps: { disabled: false },
closable: true, closable: true,
@@ -505,9 +506,9 @@ export default function Chapters() {
return; return;
} }
await handleGenerate(); await handleGenerate();
modal.destroy(); instance.destroy();
} catch (error) { } catch (error) {
modal.update({ instance.update({
okButtonProps: { danger: true, loading: false }, okButtonProps: { danger: true, loading: false },
cancelButtonProps: { disabled: false }, cancelButtonProps: { disabled: false },
closable: true, closable: true,
@@ -579,7 +580,7 @@ export default function Chapters() {
return; return;
} }
Modal.confirm({ modal.confirm({
title: '导出项目章节', title: '导出项目章节',
content: `确定要将《${currentProject.title}》的所有章节导出为TXT文件吗?`, content: `确定要将《${currentProject.title}》的所有章节导出为TXT文件吗?`,
centered: true, centered: true,
@@ -840,7 +841,7 @@ export default function Chapters() {
? Math.max(...chapters.map(c => c.chapter_number)) + 1 ? Math.max(...chapters.map(c => c.chapter_number)) + 1
: 1; : 1;
Modal.confirm({ modal.confirm({
title: '手动创建章节', title: '手动创建章节',
width: 600, width: 600,
centered: true, centered: true,
@@ -937,7 +938,7 @@ export default function Chapters() {
if (conflictChapter) { if (conflictChapter) {
// 显示冲突提示Modal // 显示冲突提示Modal
Modal.confirm({ modal.confirm({
title: '章节序号冲突', title: '章节序号冲突',
icon: <InfoCircleOutlined style={{ color: '#ff4d4f' }} />, icon: <InfoCircleOutlined style={{ color: '#ff4d4f' }} />,
width: 500, width: 500,
@@ -1074,10 +1075,10 @@ export default function Chapters() {
try { try {
const planData: ExpansionPlanData = JSON.parse(chapter.expansion_plan); const planData: ExpansionPlanData = JSON.parse(chapter.expansion_plan);
Modal.info({ modal.info({
title: ( title: (
<Space style={{ flexWrap: 'wrap' }}> <Space style={{ flexWrap: 'wrap' }}>
<InfoCircleOutlined style={{ color: '#1890ff' }} /> <InfoCircleOutlined style={{ color: 'var(--color-primary)' }} />
<span style={{ wordBreak: 'break-word' }}>{chapter.chapter_number}</span> <span style={{ wordBreak: 'break-word' }}>{chapter.chapter_number}</span>
</Space> </Space>
), ),
@@ -1366,11 +1367,12 @@ export default function Chapters() {
return ( return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}> <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
{contextHolder}
<div style={{ <div style={{
position: 'sticky', position: 'sticky',
top: 0, top: 0,
zIndex: 10, zIndex: 10,
backgroundColor: '#fff', backgroundColor: 'var(--color-bg-container)',
padding: isMobile ? '12px 0' : '16px 0', padding: isMobile ? '12px 0' : '16px 0',
marginBottom: isMobile ? 12 : 16, marginBottom: isMobile ? 12 : 16,
borderBottom: '1px solid #f0f0f0', borderBottom: '1px solid #f0f0f0',
@@ -1486,7 +1488,7 @@ export default function Chapters() {
> >
<div style={{ width: '100%' }}> <div style={{ width: '100%' }}>
<List.Item.Meta <List.Item.Meta
avatar={!isMobile && <FileTextOutlined style={{ fontSize: 32, color: '#1890ff' }} />} avatar={!isMobile && <FileTextOutlined style={{ fontSize: 32, color: 'var(--color-primary)' }} />}
title={ title={
<div style={{ <div style={{
display: 'flex', display: 'flex',
@@ -1500,7 +1502,7 @@ export default function Chapters() {
</span> </span>
<Space wrap size={isMobile ? 4 : 8}> <Space wrap size={isMobile ? 4 : 8}>
<Tag color={getStatusColor(item.status)}>{getStatusText(item.status)}</Tag> <Tag color={getStatusColor(item.status)}>{getStatusText(item.status)}</Tag>
<Badge count={`${item.word_count || 0}`} style={{ backgroundColor: '#52c41a' }} /> <Badge count={`${item.word_count || 0}`} style={{ backgroundColor: 'var(--color-success)' }} />
{renderAnalysisStatus(item.id)} {renderAnalysisStatus(item.id)}
{!canGenerateChapter(item) && ( {!canGenerateChapter(item) && (
<Tooltip title={getGenerateDisabledReason(item)}> <Tooltip title={getGenerateDisabledReason(item)}>
@@ -1591,11 +1593,11 @@ export default function Chapters() {
</span> </span>
<Badge <Badge
count={`${group.chapters.length}`} count={`${group.chapters.length}`}
style={{ backgroundColor: '#52c41a' }} style={{ backgroundColor: 'var(--color-success)' }}
/> />
<Badge <Badge
count={`${group.chapters.reduce((sum, ch) => sum + (ch.word_count || 0), 0)}`} count={`${group.chapters.reduce((sum, ch) => sum + (ch.word_count || 0), 0)}`}
style={{ backgroundColor: '#1890ff' }} style={{ backgroundColor: 'var(--color-primary)' }}
/> />
</div> </div>
} }
@@ -1681,7 +1683,7 @@ export default function Chapters() {
> >
<div style={{ width: '100%' }}> <div style={{ width: '100%' }}>
<List.Item.Meta <List.Item.Meta
avatar={!isMobile && <FileTextOutlined style={{ fontSize: 32, color: '#1890ff' }} />} avatar={!isMobile && <FileTextOutlined style={{ fontSize: 32, color: 'var(--color-primary)' }} />}
title={ title={
<div style={{ <div style={{
display: 'flex', display: 'flex',
@@ -1695,7 +1697,7 @@ export default function Chapters() {
</span> </span>
<Space wrap size={isMobile ? 4 : 8}> <Space wrap size={isMobile ? 4 : 8}>
<Tag color={getStatusColor(item.status)}>{getStatusText(item.status)}</Tag> <Tag color={getStatusColor(item.status)}>{getStatusText(item.status)}</Tag>
<Badge count={`${item.word_count || 0}`} style={{ backgroundColor: '#52c41a' }} /> <Badge count={`${item.word_count || 0}`} style={{ backgroundColor: 'var(--color-success)' }} />
{renderAnalysisStatus(item.id)} {renderAnalysisStatus(item.id)}
{!canGenerateChapter(item) && ( {!canGenerateChapter(item) && (
<Tooltip title={getGenerateDisabledReason(item)}> <Tooltip title={getGenerateDisabledReason(item)}>
@@ -1708,7 +1710,7 @@ export default function Chapters() {
{item.expansion_plan && ( {item.expansion_plan && (
<Tooltip title="查看展开详情"> <Tooltip title="查看展开详情">
<InfoCircleOutlined <InfoCircleOutlined
style={{ color: '#1890ff', cursor: 'pointer', fontSize: 16 }} style={{ color: 'var(--color-primary)', cursor: 'pointer', fontSize: 16 }}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
showExpansionPlanModal(item); showExpansionPlanModal(item);
@@ -1718,7 +1720,7 @@ export default function Chapters() {
)} )}
<Tooltip title={item.expansion_plan ? "编辑规划信息" : "创建规划信息"}> <Tooltip title={item.expansion_plan ? "编辑规划信息" : "创建规划信息"}>
<FormOutlined <FormOutlined
style={{ color: '#52c41a', cursor: 'pointer', fontSize: 16 }} style={{ color: 'var(--color-success)', cursor: 'pointer', fontSize: 16 }}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
handleOpenPlanEditor(item); handleOpenPlanEditor(item);
@@ -1992,7 +1994,7 @@ export default function Chapters() {
<Select.Option value="omniscient"></Select.Option> <Select.Option value="omniscient"></Select.Option>
</Select> </Select>
{temporaryNarrativePerspective && ( {temporaryNarrativePerspective && (
<div style={{ color: '#52c41a', fontSize: 12, marginTop: 4 }}> <div style={{ color: 'var(--color-success)', fontSize: 12, marginTop: 4 }}>
{getNarrativePerspectiveText(temporaryNarrativePerspective)} {getNarrativePerspectiveText(temporaryNarrativePerspective)}
</div> </div>
)} )}
@@ -2169,7 +2171,7 @@ export default function Chapters() {
open={batchGenerateVisible} open={batchGenerateVisible}
onCancel={() => { onCancel={() => {
if (batchGenerating) { if (batchGenerating) {
Modal.confirm({ modal.confirm({
title: '确认取消', title: '确认取消',
content: '批量生成正在进行中,确定要取消吗?', content: '批量生成正在进行中,确定要取消吗?',
okText: '确定取消', okText: '确定取消',
@@ -2374,7 +2376,7 @@ export default function Chapters() {
danger danger
icon={<StopOutlined />} icon={<StopOutlined />}
onClick={() => { onClick={() => {
Modal.confirm({ modal.confirm({
title: '确认取消', title: '确认取消',
content: '确定要取消批量生成吗?已生成的章节将保留。', content: '确定要取消批量生成吗?已生成的章节将保留。',
okText: '确定取消', okText: '确定取消',
@@ -2409,7 +2411,7 @@ export default function Chapters() {
} }
title="批量生成章节" title="批量生成章节"
onCancel={() => { onCancel={() => {
Modal.confirm({ modal.confirm({
title: '确认取消', title: '确认取消',
content: '确定要取消批量生成吗?已生成的章节将保留。', content: '确定要取消批量生成吗?已生成的章节将保留。',
okText: '确定取消', okText: '确定取消',
+8 -6
View File
@@ -40,6 +40,7 @@ export default function Characters() {
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentProject?.id]); }, [currentProject?.id]);
const [modal, contextHolder] = Modal.useModal();
if (!currentProject) return null; if (!currentProject) return null;
@@ -218,7 +219,7 @@ export default function Characters() {
}; };
const showGenerateModal = () => { const showGenerateModal = () => {
Modal.confirm({ modal.confirm({
title: 'AI生成角色', title: 'AI生成角色',
width: 600, width: 600,
centered: true, centered: true,
@@ -256,7 +257,7 @@ export default function Characters() {
}; };
const showGenerateOrgModal = () => { const showGenerateOrgModal = () => {
Modal.confirm({ modal.confirm({
title: 'AI生成组织', title: 'AI生成组织',
width: 600, width: 600,
centered: true, centered: true,
@@ -306,14 +307,15 @@ export default function Characters() {
return ( return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}> <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
{contextHolder}
<div style={{ <div style={{
position: 'sticky', position: 'sticky',
top: 0, top: 0,
zIndex: 10, zIndex: 10,
backgroundColor: '#fff', backgroundColor: 'var(--color-bg-container)',
padding: isMobile ? '12px 0' : '16px 0', padding: isMobile ? '12px 0' : '16px 0',
marginBottom: isMobile ? 12 : 16, marginBottom: isMobile ? 12 : 16,
borderBottom: '1px solid #f0f0f0', borderBottom: '1px solid var(--color-border-secondary)',
display: 'flex', display: 'flex',
flexDirection: isMobile ? 'column' : 'row', flexDirection: isMobile ? 'column' : 'row',
gap: isMobile ? 12 : 0, gap: isMobile ? 12 : 0,
@@ -370,9 +372,9 @@ export default function Characters() {
position: 'sticky', position: 'sticky',
top: isMobile ? 60 : 72, top: isMobile ? 60 : 72,
zIndex: 9, zIndex: 9,
backgroundColor: '#fff', backgroundColor: 'var(--color-bg-container)',
paddingBottom: 8, paddingBottom: 8,
borderBottom: '1px solid #f0f0f0', borderBottom: '1px solid var(--color-border-secondary)',
}}> }}>
<Tabs <Tabs
activeKey={activeTab} activeKey={activeTab}
+59 -39
View File
@@ -45,6 +45,16 @@ const CACHE_EXPIRY = 24 * 60 * 60 * 1000;
const Inspiration: React.FC = () => { const Inspiration: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [currentStep, setCurrentStep] = useState<Step>('idea'); const [currentStep, setCurrentStep] = useState<Step>('idea');
const [isMobile, setIsMobile] = useState(window.innerWidth <= 768);
useEffect(() => {
const handleResize = () => {
setIsMobile(window.innerWidth <= 768);
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
const [messages, setMessages] = useState<Message[]>([ const [messages, setMessages] = useState<Message[]>([
{ {
type: 'ai', type: 'ai',
@@ -697,7 +707,7 @@ const Inspiration: React.FC = () => {
<Card <Card
ref={chatContainerRef} ref={chatContainerRef}
style={{ style={{
height: window.innerWidth <= 768 ? 'calc(100vh - 280px)' : 600, height: isMobile ? 'calc(100vh - 280px)' : 600,
overflowY: 'auto', overflowY: 'auto',
marginBottom: 16, marginBottom: 16,
boxShadow: '0 8px 24px rgba(0,0,0,0.15)', boxShadow: '0 8px 24px rgba(0,0,0,0.15)',
@@ -721,16 +731,16 @@ const Inspiration: React.FC = () => {
maxWidth: '80%', maxWidth: '80%',
padding: '12px 16px', padding: '12px 16px',
borderRadius: 12, borderRadius: 12,
background: msg.type === 'ai' ? '#f0f0f0' : '#1890ff', background: msg.type === 'ai' ? 'var(--color-bg-container)' : 'var(--color-primary)',
color: msg.type === 'ai' ? '#000' : '#fff', color: msg.type === 'ai' ? 'var(--color-text-primary)' : '#fff',
boxShadow: msg.type === 'ai' boxShadow: msg.type === 'ai'
? '0 2px 8px rgba(0,0,0,0.08)' ? 'var(--shadow-card)'
: '0 2px 8px rgba(24,144,255,0.3)', : 'var(--shadow-primary)',
}}> }}>
<Paragraph <Paragraph
style={{ style={{
margin: 0, margin: 0,
color: msg.type === 'ai' ? '#000' : '#fff', color: msg.type === 'ai' ? 'var(--color-text-primary)' : '#fff',
whiteSpace: 'pre-wrap' whiteSpace: 'pre-wrap'
}} }}
> >
@@ -752,13 +762,13 @@ const Inspiration: React.FC = () => {
style={{ style={{
cursor: msg.optionsDisabled ? 'not-allowed' : 'pointer', cursor: msg.optionsDisabled ? 'not-allowed' : 'pointer',
border: msg.isMultiSelect && selectedOptions.includes(option) border: msg.isMultiSelect && selectedOptions.includes(option)
? '2px solid #1890ff' ? '2px solid var(--color-primary)'
: '1px solid #d9d9d9', : '1px solid var(--color-border)',
background: msg.optionsDisabled background: msg.optionsDisabled
? '#f5f5f5' ? 'var(--color-bg-layout)'
: msg.isMultiSelect && selectedOptions.includes(option) : msg.isMultiSelect && selectedOptions.includes(option)
? '#e6f7ff' ? 'var(--color-bg-spotlight)' // Need to ensure this exists or use safe fallback
: '#fff', : 'var(--color-bg-container)',
opacity: msg.optionsDisabled ? 0.6 : 1, opacity: msg.optionsDisabled ? 0.6 : 1,
animation: 'floatIn 0.6s ease-out', animation: 'floatIn 0.6s ease-out',
animationDelay: `${optIndex * 0.1}s`, animationDelay: `${optIndex * 0.1}s`,
@@ -768,7 +778,7 @@ const Inspiration: React.FC = () => {
onMouseEnter={(e) => { onMouseEnter={(e) => {
if (!msg.optionsDisabled) { if (!msg.optionsDisabled) {
e.currentTarget.style.transform = 'translateY(-2px) scale(1.02)'; e.currentTarget.style.transform = 'translateY(-2px) scale(1.02)';
e.currentTarget.style.boxShadow = '0 4px 12px rgba(24,144,255,0.2)'; e.currentTarget.style.boxShadow = 'var(--shadow-elevated)';
} }
}} }}
onMouseLeave={(e) => { onMouseLeave={(e) => {
@@ -854,8 +864,7 @@ const Inspiration: React.FC = () => {
return ( return (
<div style={{ <div style={{
minHeight: '100vh', minHeight: '100vh',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'var(--color-bg-base)',
padding: window.innerWidth <= 768 ? '12px' : '24px'
}}> }}>
<style> <style>
{` {`
@@ -894,53 +903,65 @@ const Inspiration: React.FC = () => {
} }
`} `}
</style> </style>
<div style={{ maxWidth: 800, margin: '0 auto' }}>
{/* 顶部标题栏 - 固定不滚动 */}
<div style={{ <div style={{
marginBottom: window.innerWidth <= 768 ? 12 : 24, position: 'sticky',
position: 'relative' top: 0,
zIndex: 100,
background: 'var(--color-primary)',
boxShadow: 'var(--shadow-header)',
}}>
<div style={{
maxWidth: 1200,
margin: '0 auto',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: isMobile ? '12px 16px' : '16px 24px',
}}> }}>
<Button <Button
icon={<ArrowLeftOutlined />} icon={<ArrowLeftOutlined />}
onClick={handleBack} onClick={handleBack}
type="text" size={isMobile ? 'middle' : 'large'}
size={window.innerWidth <= 768 ? 'small' : 'middle'}
style={{ style={{
background: 'rgba(255,255,255,0.2)',
borderColor: 'rgba(255,255,255,0.3)',
color: '#fff', color: '#fff',
padding: window.innerWidth <= 768 ? '4px 8px' : '4px 15px',
height: window.innerWidth <= 768 ? 32 : 'auto',
position: window.innerWidth <= 768 ? 'absolute' : 'static',
left: 0,
top: 0,
zIndex: 1
}} }}
> >
{window.innerWidth <= 768 ? '返回' : '返回项目列表'} {isMobile ? '返回' : '返回项目列表'}
</Button> </Button>
<div style={{
textAlign: 'center', <div style={{ textAlign: 'center' }}>
paddingTop: window.innerWidth <= 768 ? 0 : 0
}}>
<Title <Title
level={window.innerWidth <= 768 ? 4 : 2} level={isMobile ? 4 : 2}
style={{ style={{
color: '#fff',
margin: 0, margin: 0,
marginBottom: window.innerWidth <= 768 ? 4 : 8 color: '#fff',
textShadow: '0 2px 4px rgba(0,0,0,0.1)',
lineHeight: 1.2
}} }}
> >
</Title> </Title>
<Text style={{ <Text style={{
color: '#fff', color: 'rgba(255,255,255,0.85)',
display: 'block', fontSize: isMobile ? 12 : 14,
fontSize: window.innerWidth <= 768 ? 12 : 14,
opacity: 0.9
}}> }}>
</Text> </Text>
</div> </div>
<div style={{ width: isMobile ? 60 : 120 }}></div>
</div>
</div> </div>
<div style={{
maxWidth: 800,
margin: '0 auto',
padding: isMobile ? '16px 12px' : '24px 24px',
}}>
{(currentStep === 'idea' || currentStep === 'title' || currentStep === 'description' || {(currentStep === 'idea' || currentStep === 'title' || currentStep === 'description' ||
currentStep === 'theme' || currentStep === 'genre' || currentStep === 'perspective' || currentStep === 'theme' || currentStep === 'genre' || currentStep === 'perspective' ||
currentStep === 'outline_mode' || currentStep === 'confirm') && renderChat()} currentStep === 'outline_mode' || currentStep === 'confirm') && renderChat()}
@@ -950,7 +971,7 @@ const Inspiration: React.FC = () => {
storagePrefix="inspiration" storagePrefix="inspiration"
onComplete={handleComplete} onComplete={handleComplete}
onBack={handleBackToChat} onBack={handleBackToChat}
isMobile={window.innerWidth <= 768} isMobile={isMobile}
/> />
)} )}
</div> </div>
@@ -959,4 +980,3 @@ const Inspiration: React.FC = () => {
}; };
export default Inspiration; export default Inspiration;
+23 -24
View File
@@ -96,9 +96,9 @@ export default function Login() {
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
minHeight: '100vh', minHeight: '100vh',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'var(--color-bg-base)',
}}> }}>
<Spin size="large" style={{ color: '#fff' }} /> <Spin size="large" style={{ color: 'var(--color-primary)' }} />
</div> </div>
); );
} }
@@ -141,10 +141,10 @@ export default function Login() {
height: 48, height: 48,
fontSize: 16, fontSize: 16,
fontWeight: 600, fontWeight: 600,
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'var(--color-primary)',
border: 'none', border: 'none',
borderRadius: '12px', borderRadius: '12px',
boxShadow: '0 4px 16px rgba(102, 126, 234, 0.4)', boxShadow: 'var(--shadow-primary)',
}} }}
> >
@@ -178,19 +178,19 @@ export default function Login() {
height: 52, height: 52,
fontSize: 16, fontSize: 16,
fontWeight: 600, fontWeight: 600,
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'var(--color-primary)',
border: 'none', border: 'none',
borderRadius: '12px', borderRadius: '12px',
boxShadow: '0 4px 16px rgba(102, 126, 234, 0.4)', boxShadow: 'var(--shadow-primary)',
transition: 'all 0.3s ease', transition: 'all 0.3s ease',
}} }}
onMouseEnter={(e) => { onMouseEnter={(e) => {
e.currentTarget.style.transform = 'translateY(-2px)'; e.currentTarget.style.transform = 'translateY(-2px)';
e.currentTarget.style.boxShadow = '0 6px 24px rgba(102, 126, 234, 0.5)'; e.currentTarget.style.boxShadow = 'var(--shadow-elevated)';
}} }}
onMouseLeave={(e) => { onMouseLeave={(e) => {
e.currentTarget.style.transform = 'translateY(0)'; e.currentTarget.style.transform = 'translateY(0)';
e.currentTarget.style.boxShadow = '0 4px 16px rgba(102, 126, 234, 0.4)'; e.currentTarget.style.boxShadow = 'var(--shadow-primary)';
}} }}
> >
使 LinuxDO 使 LinuxDO
@@ -228,7 +228,7 @@ export default function Login() {
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
minHeight: '100vh', minHeight: '100vh',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'var(--color-bg-base)',
padding: '20px', padding: '20px',
position: 'relative', position: 'relative',
overflow: 'hidden', overflow: 'hidden',
@@ -240,7 +240,8 @@ export default function Login() {
right: '-5%', right: '-5%',
width: '400px', width: '400px',
height: '400px', height: '400px',
background: 'rgba(255, 255, 255, 0.1)', background: 'var(--color-primary)',
opacity: 0.1,
borderRadius: '50%', borderRadius: '50%',
filter: 'blur(60px)', filter: 'blur(60px)',
}} /> }} />
@@ -250,7 +251,8 @@ export default function Login() {
left: '-5%', left: '-5%',
width: '350px', width: '350px',
height: '350px', height: '350px',
background: 'rgba(255, 255, 255, 0.08)', background: 'var(--color-success)',
opacity: 0.08,
borderRadius: '50%', borderRadius: '50%',
filter: 'blur(60px)', filter: 'blur(60px)',
}} /> }} />
@@ -259,11 +261,11 @@ export default function Login() {
style={{ style={{
width: '100%', width: '100%',
maxWidth: 420, maxWidth: 420,
background: 'rgba(255, 255, 255, 0.95)', background: 'var(--color-bg-container)',
backdropFilter: 'blur(20px)', backdropFilter: 'blur(20px)',
WebkitBackdropFilter: 'blur(20px)', WebkitBackdropFilter: 'blur(20px)',
boxShadow: '0 20px 60px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.2)', boxShadow: 'var(--shadow-card)',
border: '1px solid rgba(255, 255, 255, 0.3)', border: '1px solid var(--color-border)',
borderRadius: '16px', borderRadius: '16px',
position: 'relative', position: 'relative',
zIndex: 1, zIndex: 1,
@@ -279,12 +281,12 @@ export default function Login() {
width: '72px', width: '72px',
height: '72px', height: '72px',
margin: '0 auto 20px', margin: '0 auto 20px',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'var(--color-primary)',
borderRadius: '20px', borderRadius: '20px',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
boxShadow: '0 8px 24px rgba(102, 126, 234, 0.4)', boxShadow: 'var(--shadow-primary)',
}}> }}>
<img <img
src="/logo.svg" src="/logo.svg"
@@ -298,16 +300,13 @@ export default function Login() {
</div> </div>
<Title level={2} style={{ <Title level={2} style={{
marginBottom: 8, marginBottom: 8,
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', color: 'var(--color-primary)',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
backgroundClip: 'text',
fontWeight: 700, fontWeight: 700,
}}> }}>
AI小说创作助手 AI小说创作助手
</Title> </Title>
<Paragraph style={{ <Paragraph style={{
color: '#666', color: 'var(--color-text-secondary)',
fontSize: '14px', fontSize: '14px',
marginBottom: 0, marginBottom: 0,
}}> }}>
@@ -344,13 +343,13 @@ export default function Login() {
{/* 提示信息 */} {/* 提示信息 */}
<div style={{ <div style={{
padding: '16px', padding: '16px',
background: 'rgba(102, 126, 234, 0.08)', background: 'rgba(77, 128, 136, 0.08)',
borderRadius: '12px', borderRadius: '12px',
border: '1px solid rgba(102, 126, 234, 0.1)', border: '1px solid var(--color-border)',
}}> }}>
<Paragraph style={{ <Paragraph style={{
fontSize: 13, fontSize: 13,
color: '#666', color: 'var(--color-text-secondary)',
marginBottom: 0, marginBottom: 0,
lineHeight: 1.6, lineHeight: 1.6,
}}> }}>
+104 -78
View File
@@ -17,7 +17,8 @@ import {
Empty, Empty,
Alert, Alert,
Descriptions, Descriptions,
Layout, Row,
Col,
} from 'antd'; } from 'antd';
import { import {
PlusOutlined, PlusOutlined,
@@ -35,7 +36,6 @@ import type { MCPPlugin, MCPTool } from '../types';
const { Paragraph, Text, Title } = Typography; const { Paragraph, Text, Title } = Typography;
const { TextArea } = Input; const { TextArea } = Input;
const { Header, Content } = Layout;
export default function MCPPluginsPage() { export default function MCPPluginsPage() {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -228,9 +228,9 @@ export default function MCPPluginsPage() {
<div style={{ <div style={{
marginTop: 8, marginTop: 8,
padding: 12, padding: 12,
background: '#fff2f0', background: 'var(--color-error-bg)',
borderRadius: 4, borderRadius: 4,
color: '#cf1322', color: 'var(--color-error)',
fontSize: '13px', fontSize: '13px',
fontFamily: 'monospace', fontFamily: 'monospace',
whiteSpace: 'pre-wrap', whiteSpace: 'pre-wrap',
@@ -260,8 +260,8 @@ export default function MCPPluginsPage() {
<div style={{ <div style={{
marginTop: 16, marginTop: 16,
padding: 12, padding: 12,
background: '#fff7e6', background: 'var(--color-warning-bg)',
border: '1px solid #ffd591', border: '1px solid var(--color-warning-border)',
borderRadius: 4 borderRadius: 4
}}> }}>
<Text style={{ fontSize: '13px', color: '#ad6800' }}> <Text style={{ fontSize: '13px', color: '#ad6800' }}>
@@ -340,105 +340,130 @@ export default function MCPPluginsPage() {
}; };
return ( return (
<Layout style={{ minHeight: '100vh', background: '#f0f2f5' }}> <div style={{
{/* 顶部导航栏 */} minHeight: '100vh',
<Header style={{ background: 'linear-gradient(180deg, var(--color-bg-base) 0%, #EEF2F3 100%)',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', padding: isMobile ? '20px 16px' : '40px 24px',
padding: isMobile ? '0 16px' : '0 24px',
display: 'flex', display: 'flex',
alignItems: 'center', flexDirection: 'column',
justifyContent: 'space-between',
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
position: 'fixed',
top: 0,
left: 0,
right: 0,
zIndex: 1000,
height: isMobile ? 56 : 64
}}> }}>
<Space size={isMobile ? 12 : 16}> <div style={{
<Button
type="text"
icon={<ArrowLeftOutlined />}
onClick={() => navigate('/')}
style={{
color: '#fff',
fontSize: isMobile ? 16 : 18,
display: 'flex',
alignItems: 'center'
}}
>
{!isMobile && '返回'}
</Button>
<Title level={isMobile ? 4 : 3} style={{
margin: 0,
color: '#fff',
fontSize: isMobile ? 18 : 24
}}>
MCP插件管理
</Title>
</Space>
</Header>
{/* 主内容区 */}
<Content style={{
marginTop: isMobile ? 56 : 64,
padding: isMobile ? '16px' : '24px',
maxWidth: 1400, maxWidth: 1400,
margin: '0 auto',
width: '100%', width: '100%',
margin: `${isMobile ? 56 : 64}px auto 0`, flex: 1,
display: 'flex',
flexDirection: 'column',
}}> }}>
{/* 顶部导航卡片 */}
<Card <Card
variant="borderless" variant="borderless"
style={{ style={{
borderRadius: isMobile ? 8 : 12, background: 'linear-gradient(135deg, var(--color-primary) 0%, #5A9BA5 50%, var(--color-primary-hover) 100%)',
boxShadow: '0 2px 8px rgba(0,0,0,0.06)', borderRadius: isMobile ? 16 : 24,
marginBottom: isMobile ? 16 : 24 boxShadow: '0 12px 40px rgba(77, 128, 136, 0.25), 0 4px 12px rgba(0, 0, 0, 0.06)',
marginBottom: isMobile ? 20 : 24,
border: 'none',
position: 'relative',
overflow: 'hidden'
}} }}
> >
<div style={{ {/* 装饰性背景元素 */}
display: 'flex', <div style={{ position: 'absolute', top: -60, right: -60, width: 200, height: 200, borderRadius: '50%', background: 'rgba(255, 255, 255, 0.08)', pointerEvents: 'none' }} />
justifyContent: 'space-between', <div style={{ position: 'absolute', bottom: -40, left: '30%', width: 120, height: 120, borderRadius: '50%', background: 'rgba(255, 255, 255, 0.05)', pointerEvents: 'none' }} />
alignItems: 'center', <div style={{ position: 'absolute', top: '50%', right: '15%', width: 80, height: 80, borderRadius: '50%', background: 'rgba(255, 255, 255, 0.06)', pointerEvents: 'none' }} />
marginBottom: isMobile ? 16 : 20
}}> <Row align="middle" justify="space-between" gutter={[16, 16]} style={{ position: 'relative', zIndex: 1 }}>
<Title level={isMobile ? 5 : 4} style={{ margin: 0 }}> <Col xs={24} sm={12}>
<Space direction="vertical" size={4}>
<Space align="center">
<Title level={isMobile ? 3 : 2} style={{ margin: 0, color: '#fff', textShadow: '0 2px 4px rgba(0,0,0,0.1)' }}>
<ToolOutlined style={{ color: 'rgba(255,255,255,0.9)', marginRight: 8 }} />
MCP插件管理
</Title> </Title>
</Space>
<Text style={{ fontSize: isMobile ? 12 : 14, color: 'rgba(255,255,255,0.85)', marginLeft: isMobile ? 40 : 48 }}>
AI能力
</Text>
</Space>
</Col>
<Col xs={24} sm={12}>
<Space size={12} style={{ display: 'flex', justifyContent: isMobile ? 'flex-start' : 'flex-end', width: '100%' }}>
<Button
icon={<ArrowLeftOutlined />}
onClick={() => navigate('/')}
style={{
borderRadius: 12,
background: 'rgba(255, 255, 255, 0.15)',
border: '1px solid rgba(255, 255, 255, 0.3)',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
color: '#fff',
backdropFilter: 'blur(10px)',
transition: 'all 0.3s ease'
}}
onMouseEnter={(e) => {
e.currentTarget.style.background = 'rgba(255, 255, 255, 0.25)';
e.currentTarget.style.transform = 'translateY(-1px)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = 'rgba(255, 255, 255, 0.15)';
e.currentTarget.style.transform = 'none';
}}
>
</Button>
<Button <Button
type="primary" type="primary"
icon={<PlusOutlined />} icon={<PlusOutlined />}
onClick={handleCreate} onClick={handleCreate}
size={isMobile ? 'middle' : 'large'}
style={{ style={{
borderRadius: 8, borderRadius: 12,
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'rgba(255, 193, 7, 0.95)',
border: 'none' border: '1px solid rgba(255, 255, 255, 0.3)',
boxShadow: '0 4px 16px rgba(255, 193, 7, 0.4)',
color: '#fff',
fontWeight: 600
}} }}
> >
</Button> </Button>
</div> </Space>
</Col>
</Row>
{/* 使用提示 */}
<Alert <Alert
message="什么是 MCP 插件?" message={
<Space align="center">
<InfoCircleOutlined style={{ fontSize: 16, color: 'var(--color-primary)' }} />
<Text strong style={{ fontSize: isMobile ? 13 : 14, color: 'var(--color-text-primary)' }}> MCP </Text>
</Space>
}
description={ description={
<div style={{ fontSize: isMobile ? '12px' : '14px' }}> <div>
<p style={{ margin: '8px 0' }}> <Text style={{ fontSize: isMobile ? 12 : 13, display: 'block', marginBottom: 8 }}>
MCP (Model Context Protocol) AI <strong>MCP (Model Context Protocol)</strong> AI
</p> </Text>
<p style={{ margin: '8px 0 0 0' }}> <Text style={{ fontSize: isMobile ? 12 : 13, display: 'block' }}>
MCP AI 访API MCP AI 访API
</p> </Text>
</div> </div>
} }
type="info" type="info"
showIcon showIcon={false}
icon={<InfoCircleOutlined />} style={{
style={{ marginBottom: isMobile ? 16 : 20 }} marginTop: isMobile ? 16 : 24,
borderRadius: 12,
background: 'rgba(230, 247, 255, 0.6)',
border: '1px solid rgba(145, 213, 255, 0.6)',
backdropFilter: 'blur(5px)'
}}
/> />
</Card> </Card>
{/* 主内容区 */}
<div style={{ flex: 1 }}>
{/* 插件列表 */} {/* 插件列表 */}
<Spin spinning={loading}> <Spin spinning={loading}>
{plugins.length === 0 ? ( {plugins.length === 0 ? (
@@ -600,7 +625,8 @@ export default function MCPPluginsPage() {
</Space> </Space>
)} )}
</Spin> </Spin>
</Content> </div>
</div>
{/* 创建/编辑插件模态框 */} {/* 创建/编辑插件模态框 */}
<Modal <Modal
@@ -710,6 +736,6 @@ export default function MCPPluginsPage() {
</Space> </Space>
)} )}
</Modal> </Modal>
</Layout> </div>
); );
} }
+4 -2
View File
@@ -54,6 +54,7 @@ export default function Organizations() {
const [editMemberForm] = Form.useForm(); const [editMemberForm] = Form.useForm();
const [editOrgForm] = Form.useForm(); const [editOrgForm] = Form.useForm();
const [isMobile, setIsMobile] = useState(window.innerWidth <= 768); const [isMobile, setIsMobile] = useState(window.innerWidth <= 768);
const [modal, contextHolder] = Modal.useModal();
useEffect(() => { useEffect(() => {
const handleResize = () => { const handleResize = () => {
@@ -129,7 +130,7 @@ export default function Organizations() {
}; };
const handleRemoveMember = async (memberId: string) => { const handleRemoveMember = async (memberId: string) => {
Modal.confirm({ modal.confirm({
title: '确认移除', title: '确认移除',
content: '确定要移除该成员吗?', content: '确定要移除该成员吗?',
centered: true, centered: true,
@@ -294,6 +295,7 @@ export default function Organizations() {
return ( return (
<div> <div>
{contextHolder}
<Card <Card
title={ title={
<Space wrap> <Space wrap>
@@ -326,7 +328,7 @@ export default function Organizations() {
hoverable hoverable
style={{ style={{
cursor: 'pointer', cursor: 'pointer',
border: selectedOrg?.id === org.id ? '2px solid #1890ff' : '1px solid #d9d9d9' border: selectedOrg?.id === org.id ? '2px solid var(--color-primary)' : '1px solid var(--color-border-secondary)'
}} }}
onClick={() => handleSelectOrganization(org)} onClick={() => handleSelectOrganization(org)}
> >
+44 -41
View File
@@ -1,4 +1,4 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { Button, List, Modal, Form, Input, message, Empty, Space, Popconfirm, Card, Select, Radio, Tag, InputNumber, Tooltip, Tabs } from 'antd'; import { Button, List, Modal, Form, Input, message, Empty, Space, Popconfirm, Card, Select, Radio, Tag, InputNumber, Tooltip, Tabs } from 'antd';
import { EditOutlined, DeleteOutlined, ThunderboltOutlined, BranchesOutlined, AppstoreAddOutlined, CheckCircleOutlined, ExclamationCircleOutlined, PlusOutlined } from '@ant-design/icons'; import { EditOutlined, DeleteOutlined, ThunderboltOutlined, BranchesOutlined, AppstoreAddOutlined, CheckCircleOutlined, ExclamationCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { useStore } from '../store'; import { useStore } from '../store';
@@ -41,6 +41,7 @@ export default function Outline() {
const [editForm] = Form.useForm(); const [editForm] = Form.useForm();
const [generateForm] = Form.useForm(); const [generateForm] = Form.useForm();
const [expansionForm] = Form.useForm(); const [expansionForm] = Form.useForm();
const [modalApi, contextHolder] = Modal.useModal();
const [batchExpansionForm] = Form.useForm(); const [batchExpansionForm] = Form.useForm();
const [manualCreateForm] = Form.useForm(); const [manualCreateForm] = Form.useForm();
const [isMobile, setIsMobile] = useState(window.innerWidth <= 768); const [isMobile, setIsMobile] = useState(window.innerWidth <= 768);
@@ -135,7 +136,7 @@ export default function Outline() {
const outline = outlines.find(o => o.id === id); const outline = outlines.find(o => o.id === id);
if (outline) { if (outline) {
editForm.setFieldsValue(outline); editForm.setFieldsValue(outline);
Modal.confirm({ modalApi.confirm({
title: '编辑大纲', title: '编辑大纲',
width: 600, width: 600,
centered: true, centered: true,
@@ -335,7 +336,7 @@ export default function Outline() {
} }
} }
Modal.confirm({ modalApi.confirm({
title: hasOutlines ? ( title: hasOutlines ? (
<Space> <Space>
<span>AI生成/</span> <span>AI生成/</span>
@@ -504,7 +505,7 @@ export default function Outline() {
console.log('已同步到Form,当前Form值:', generateForm.getFieldsValue()); console.log('已同步到Form,当前Form值:', generateForm.getFieldsValue());
}} }}
/> />
<div style={{ color: '#666', fontSize: 12, marginTop: 4 }}> <div style={{ color: 'var(--color-text-tertiary)', fontSize: 12, marginTop: 4 }}>
{defaultModel ? `当前默认模型: ${loadedModels.find(m => m.value === defaultModel)?.label || defaultModel}` : '未配置默认模型'} {defaultModel ? `当前默认模型: ${loadedModels.find(m => m.value === defaultModel)?.label || defaultModel}` : '未配置默认模型'}
</div> </div>
</Form.Item> </Form.Item>
@@ -526,7 +527,7 @@ export default function Outline() {
? Math.max(...outlines.map(o => o.order_index)) + 1 ? Math.max(...outlines.map(o => o.order_index)) + 1
: 1; : 1;
Modal.confirm({ modalApi.confirm({
title: '手动创建大纲', title: '手动创建大纲',
width: 600, width: 600,
centered: true, centered: true,
@@ -574,26 +575,26 @@ export default function Outline() {
// 校验序号是否重复 // 校验序号是否重复
const existingOutline = outlines.find(o => o.order_index === values.order_index); const existingOutline = outlines.find(o => o.order_index === values.order_index);
if (existingOutline) { if (existingOutline) {
Modal.warning({ modalApi.warning({
title: '序号冲突', title: '序号冲突',
content: ( content: (
<div> <div>
<p> <strong>{values.order_index}</strong> 使</p> <p> <strong>{values.order_index}</strong> 使</p>
<div style={{ <div style={{
padding: 12, padding: 12,
background: '#fff7e6', background: 'var(--color-warning-bg)',
borderRadius: 4, borderRadius: 4,
border: '1px solid #ffd591', border: '1px solid var(--color-warning-border)',
marginTop: 8 marginTop: 8
}}> }}>
<div style={{ fontWeight: 500, color: '#fa8c16' }}> <div style={{ fontWeight: 500, color: 'var(--color-warning)' }}>
{currentProject?.outline_mode === 'one-to-one' {currentProject?.outline_mode === 'one-to-one'
? `${existingOutline.order_index}` ? `${existingOutline.order_index}`
: `${existingOutline.order_index}` : `${existingOutline.order_index}`
}{existingOutline.title} }{existingOutline.title}
</div> </div>
</div> </div>
<p style={{ marginTop: 12, color: '#666' }}> <p style={{ marginTop: 12, color: 'var(--color-text-secondary)' }}>
💡 使 <strong>{nextOrderIndex}</strong>使 💡 使 <strong>{nextOrderIndex}</strong>使
</p> </p>
</div> </div>
@@ -644,7 +645,7 @@ export default function Outline() {
if (!prevChapters.has_chapters) { if (!prevChapters.has_chapters) {
// 如果前面有未展开的大纲,显示提示并阻止操作 // 如果前面有未展开的大纲,显示提示并阻止操作
setIsExpanding(false); setIsExpanding(false);
Modal.warning({ modalApi.warning({
title: '请按顺序展开大纲', title: '请按顺序展开大纲',
width: 600, width: 600,
centered: true, centered: true,
@@ -655,18 +656,18 @@ export default function Outline() {
</p> </p>
<div style={{ <div style={{
padding: 12, padding: 12,
background: '#fff7e6', background: 'var(--color-warning-bg)',
borderRadius: 4, borderRadius: 4,
border: '1px solid #ffd591' border: '1px solid var(--color-warning-border)'
}}> }}>
<div style={{ fontWeight: 500, marginBottom: 8, color: '#fa8c16' }}> <div style={{ fontWeight: 500, marginBottom: 8, color: 'var(--color-warning)' }}>
</div> </div>
<div style={{ color: '#666' }}> <div style={{ color: 'var(--color-text-secondary)' }}>
{prevOutline.order_index}{prevOutline.title} {prevOutline.order_index}{prevOutline.title}
</div> </div>
</div> </div>
<p style={{ marginTop: 12, color: '#666', fontSize: 13 }}> <p style={{ marginTop: 12, color: 'var(--color-text-secondary)', fontSize: 13 }}>
💡 使 💡 使
</p> </p>
</div> </div>
@@ -694,7 +695,7 @@ export default function Outline() {
// 如果没有章节,显示展开表单 // 如果没有章节,显示展开表单
setIsExpanding(false); setIsExpanding(false);
Modal.confirm({ modalApi.confirm({
title: ( title: (
<Space> <Space>
<BranchesOutlined /> <BranchesOutlined />
@@ -705,9 +706,9 @@ export default function Outline() {
centered: true, centered: true,
content: ( content: (
<div> <div>
<div style={{ marginBottom: 16, padding: 12, background: '#f5f5f5', borderRadius: 4 }}> <div style={{ marginBottom: 16, padding: 12, background: 'var(--color-bg-layout)', borderRadius: 4 }}>
<div style={{ fontWeight: 500, marginBottom: 4 }}></div> <div style={{ fontWeight: 500, marginBottom: 4 }}></div>
<div style={{ color: '#666' }}>{outlineTitle}</div> <div style={{ color: 'var(--color-text-secondary)' }}>{outlineTitle}</div>
</div> </div>
<Form <Form
form={expansionForm} form={expansionForm}
@@ -855,10 +856,10 @@ export default function Outline() {
}> | null; }> | null;
} }
) => { ) => {
const modal = Modal.info({ modalApi.info({
title: ( title: (
<Space style={{ flexWrap: 'wrap' }}> <Space style={{ flexWrap: 'wrap' }}>
<CheckCircleOutlined style={{ color: '#52c41a' }} /> <CheckCircleOutlined style={{ color: 'var(--color-success)' }} />
<span></span> <span></span>
</Space> </Space>
), ),
@@ -876,20 +877,20 @@ export default function Outline() {
overflowY: 'auto' overflowY: 'auto'
} }
}, },
footer: (_, { OkBtn }) => ( footer: (_: any, { OkBtn }: any) => (
<Space wrap style={{ width: '100%', justifyContent: isMobile ? 'center' : 'flex-end' }}> <Space wrap style={{ width: '100%', justifyContent: isMobile ? 'center' : 'flex-end' }}>
<Button <Button
danger danger
icon={<DeleteOutlined />} icon={<DeleteOutlined />}
onClick={() => { onClick={() => {
modal.destroy(); Modal.destroyAll();
Modal.confirm({ modalApi.confirm({
title: '确认删除', title: '确认删除',
icon: <ExclamationCircleOutlined />, icon: <ExclamationCircleOutlined />,
content: ( content: (
<div> <div>
<p>{outlineTitle} <strong>{data.chapter_count}</strong> </p> <p>{outlineTitle} <strong>{data.chapter_count}</strong> </p>
<p style={{ color: '#1890ff', marginTop: 8 }}> <p style={{ color: 'var(--color-primary)', marginTop: 8 }}>
📝 📝
</p> </p>
<p style={{ color: '#ff4d4f', marginTop: 8 }}> <p style={{ color: '#ff4d4f', marginTop: 8 }}>
@@ -1077,7 +1078,8 @@ export default function Outline() {
))} ))}
</Space> </Space>
</Card> </Card>
)} )
}
</Space> </Space>
</div > </div >
) )
@@ -1093,10 +1095,10 @@ export default function Outline() {
// 缓存AI生成的规划数据 // 缓存AI生成的规划数据
const cachedPlans = response.chapter_plans; const cachedPlans = response.chapter_plans;
Modal.confirm({ modalApi.confirm({
title: ( title: (
<Space> <Space>
<CheckCircleOutlined style={{ color: '#52c41a' }} /> <CheckCircleOutlined style={{ color: 'var(--color-success)' }} />
<span></span> <span></span>
</Space> </Space>
), ),
@@ -1222,7 +1224,7 @@ export default function Outline() {
return; return;
} }
Modal.confirm({ modalApi.confirm({
title: ( title: (
<Space> <Space>
<AppstoreAddOutlined /> <AppstoreAddOutlined />
@@ -1233,7 +1235,7 @@ export default function Outline() {
centered: true, centered: true,
content: ( content: (
<div> <div>
<div style={{ marginBottom: 16, padding: 12, background: '#fff3cd', borderRadius: 4 }}> <div style={{ marginBottom: 16, padding: 12, background: 'var(--color-warning-bg)', borderRadius: 4 }}>
<div style={{ color: '#856404' }}> <div style={{ color: '#856404' }}>
{outlines.length} {outlines.length}
</div> </div>
@@ -1361,11 +1363,11 @@ export default function Outline() {
<div style={{ <div style={{
marginBottom: 16, marginBottom: 16,
padding: 12, padding: 12,
background: '#fffbe6', background: 'var(--color-warning-bg)',
borderRadius: 4, borderRadius: 4,
border: '1px solid #ffe58f' border: '1px solid #ffe58f'
}}> }}>
<div style={{ fontWeight: 500, marginBottom: 8, color: '#faad14' }}> <div style={{ fontWeight: 500, marginBottom: 8, color: 'var(--color-warning)' }}>
</div> </div>
<Space direction="vertical" size="small" style={{ width: '100%' }}> <Space direction="vertical" size="small" style={{ width: '100%' }}>
@@ -1404,7 +1406,7 @@ export default function Outline() {
background: selectedOutlineIdx === idx ? '#e6f7ff' : 'transparent', background: selectedOutlineIdx === idx ? '#e6f7ff' : 'transparent',
borderRadius: 4, borderRadius: 4,
marginBottom: 4, marginBottom: 4,
border: selectedOutlineIdx === idx ? '1px solid #1890ff' : '1px solid transparent' border: selectedOutlineIdx === idx ? '1px solid var(--color-primary)' : '1px solid transparent'
}} }}
> >
<div style={{ width: '100%' }}> <div style={{ width: '100%' }}>
@@ -1445,7 +1447,7 @@ export default function Outline() {
background: selectedChapterIdx === idx ? '#e6f7ff' : 'transparent', background: selectedChapterIdx === idx ? '#e6f7ff' : 'transparent',
borderRadius: 4, borderRadius: 4,
marginBottom: 4, marginBottom: 4,
border: selectedChapterIdx === idx ? '1px solid #1890ff' : '1px solid transparent' border: selectedChapterIdx === idx ? '1px solid var(--color-primary)' : '1px solid transparent'
}} }}
> >
<div style={{ width: '100%' }}> <div style={{ width: '100%' }}>
@@ -1719,7 +1721,7 @@ export default function Outline() {
<Modal <Modal
title={ title={
<Space> <Space>
<ExclamationCircleOutlined style={{ color: '#faad14' }} /> <ExclamationCircleOutlined style={{ color: 'var(--color-warning)' }} />
<span></span> <span></span>
</Space> </Space>
} }
@@ -1731,7 +1733,7 @@ export default function Outline() {
handleConfirmCharacters(selectedCharacters); handleConfirmCharacters(selectedCharacters);
}} }}
onCancel={() => { onCancel={() => {
Modal.confirm({ modalApi.confirm({
title: '确认操作', title: '确认操作',
content: '是否跳过角色创建,直接续写大纲?', content: '是否跳过角色创建,直接续写大纲?',
okText: '跳过角色,继续续写', okText: '跳过角色,继续续写',
@@ -1745,7 +1747,7 @@ export default function Outline() {
cancelText="跳过角色创建" cancelText="跳过角色创建"
> >
<div> <div>
<div style={{ marginBottom: 16, padding: 12, background: '#fffbe6', borderRadius: 4, border: '1px solid #ffe58f' }}> <div style={{ marginBottom: 16, padding: 12, background: 'var(--color-warning-bg)', borderRadius: 4, border: '1px solid var(--color-warning-border)' }}>
<div style={{ fontWeight: 500, marginBottom: 8, color: '#d48806' }}> <div style={{ fontWeight: 500, marginBottom: 8, color: '#d48806' }}>
AI AI
</div> </div>
@@ -1785,7 +1787,7 @@ export default function Outline() {
padding: 12, padding: 12,
borderRadius: 4, borderRadius: 4,
marginBottom: 8, marginBottom: 8,
border: selectedCharacterIndices.includes(index) ? '1px solid #1890ff' : '1px solid #f0f0f0', border: selectedCharacterIndices.includes(index) ? '1px solid var(--color-primary)' : '1px solid var(--color-border-secondary)',
cursor: 'pointer' cursor: 'pointer'
}} }}
onClick={() => { onClick={() => {
@@ -1859,7 +1861,7 @@ export default function Outline() {
<Modal <Modal
title={ title={
<Space> <Space>
<CheckCircleOutlined style={{ color: '#52c41a' }} /> <CheckCircleOutlined style={{ color: 'var(--color-success)' }} />
<span></span> <span></span>
</Space> </Space>
} }
@@ -1875,6 +1877,7 @@ export default function Outline() {
{renderBatchPreviewContent()} {renderBatchPreviewContent()}
</Modal> </Modal>
{contextHolder}
{/* SSE进度Modal - 使用统一组件 */} {/* SSE进度Modal - 使用统一组件 */}
<SSEProgressModal <SSEProgressModal
visible={sseModalVisible} visible={sseModalVisible}
@@ -1889,7 +1892,7 @@ export default function Outline() {
position: 'sticky', position: 'sticky',
top: 0, top: 0,
zIndex: 10, zIndex: 10,
backgroundColor: '#fff', backgroundColor: 'var(--color-bg-container)',
padding: isMobile ? '12px 0' : '16px 0', padding: isMobile ? '12px 0' : '16px 0',
marginBottom: isMobile ? 12 : 16, marginBottom: isMobile ? 12 : 16,
borderBottom: '1px solid #f0f0f0', borderBottom: '1px solid #f0f0f0',
@@ -1992,7 +1995,7 @@ export default function Outline() {
<List.Item.Meta <List.Item.Meta
title={ title={
<Space size="small" style={{ fontSize: isMobile ? 14 : 16, flexWrap: 'wrap' }}> <Space size="small" style={{ fontSize: isMobile ? 14 : 16, flexWrap: 'wrap' }}>
<span style={{ color: '#1890ff', fontWeight: 'bold' }}> <span style={{ color: 'var(--color-primary)', fontWeight: 'bold' }}>
{currentProject?.outline_mode === 'one-to-one' {currentProject?.outline_mode === 'one-to-one'
? `${item.order_index || '?'}` ? `${item.order_index || '?'}`
: `${item.order_index || '?'}` : `${item.order_index || '?'}`
+19 -20
View File
@@ -193,7 +193,7 @@ export default function ProjectDetail() {
return ( return (
<Layout style={{ minHeight: '100vh', height: '100vh', overflow: 'hidden' }}> <Layout style={{ minHeight: '100vh', height: '100vh', overflow: 'hidden' }}>
<Header style={{ <Header style={{
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'var(--color-primary)',
padding: mobile ? '0 12px' : '0 24px', padding: mobile ? '0 12px' : '0 24px',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
@@ -203,7 +203,7 @@ export default function ProjectDetail() {
left: 0, left: 0,
right: 0, right: 0,
zIndex: 1000, zIndex: 1000,
boxShadow: '0 4px 12px rgba(0,0,0,0.15)', boxShadow: 'var(--shadow-header)',
height: mobile ? 56 : 70 height: mobile ? 56 : 70
}}> }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', zIndex: 1 }}> <div style={{ display: 'flex', alignItems: 'center', gap: '8px', zIndex: 1 }}>
@@ -279,7 +279,7 @@ export default function ProjectDetail() {
<Card <Card
size="small" size="small"
style={{ style={{
background: 'rgba(255,255,255,0.95)', background: 'var(--color-bg-container)',
borderRadius: '6px', borderRadius: '6px',
border: 'none', border: 'none',
minWidth: '80px', minWidth: '80px',
@@ -289,10 +289,10 @@ export default function ProjectDetail() {
styles={{ body: { padding: '8px' } }} styles={{ body: { padding: '8px' } }}
> >
<Statistic <Statistic
title={<span style={{ fontSize: '11px', color: '#666' }}></span>} title={<span style={{ fontSize: '11px', color: 'var(--color-text-secondary)' }}></span>}
value={outlines.length} value={outlines.length}
suffix="条" suffix="条"
valueStyle={{ fontSize: '16px', fontWeight: 600, color: '#667eea' }} valueStyle={{ fontSize: '16px', fontWeight: 600, color: 'var(--color-primary)' }}
/> />
</Card> </Card>
</Col> </Col>
@@ -300,7 +300,7 @@ export default function ProjectDetail() {
<Card <Card
size="small" size="small"
style={{ style={{
background: 'rgba(255,255,255,0.95)', background: 'var(--color-bg-container)',
borderRadius: '6px', borderRadius: '6px',
border: 'none', border: 'none',
minWidth: '80px', minWidth: '80px',
@@ -310,10 +310,10 @@ export default function ProjectDetail() {
styles={{ body: { padding: '8px' } }} styles={{ body: { padding: '8px' } }}
> >
<Statistic <Statistic
title={<span style={{ fontSize: '11px', color: '#666' }}></span>} title={<span style={{ fontSize: '11px', color: 'var(--color-text-secondary)' }}></span>}
value={characters.length} value={characters.length}
suffix="个" suffix="个"
valueStyle={{ fontSize: '16px', fontWeight: 600, color: '#52c41a' }} valueStyle={{ fontSize: '16px', fontWeight: 600, color: 'var(--color-success)' }}
/> />
</Card> </Card>
</Col> </Col>
@@ -321,7 +321,7 @@ export default function ProjectDetail() {
<Card <Card
size="small" size="small"
style={{ style={{
background: 'rgba(255,255,255,0.95)', background: 'var(--color-bg-container)',
borderRadius: '6px', borderRadius: '6px',
border: 'none', border: 'none',
minWidth: '80px', minWidth: '80px',
@@ -331,10 +331,10 @@ export default function ProjectDetail() {
styles={{ body: { padding: '8px' } }} styles={{ body: { padding: '8px' } }}
> >
<Statistic <Statistic
title={<span style={{ fontSize: '11px', color: '#666' }}></span>} title={<span style={{ fontSize: '11px', color: 'var(--color-text-secondary)' }}></span>}
value={chapters.length} value={chapters.length}
suffix="章" suffix="章"
valueStyle={{ fontSize: '16px', fontWeight: 600, color: '#1890ff' }} valueStyle={{ fontSize: '16px', fontWeight: 600, color: 'var(--color-info)' }}
/> />
</Card> </Card>
</Col> </Col>
@@ -342,7 +342,7 @@ export default function ProjectDetail() {
<Card <Card
size="small" size="small"
style={{ style={{
background: 'rgba(255,255,255,0.95)', background: 'var(--color-bg-container)',
borderRadius: '6px', borderRadius: '6px',
border: 'none', border: 'none',
minWidth: '80px', minWidth: '80px',
@@ -352,10 +352,10 @@ export default function ProjectDetail() {
styles={{ body: { padding: '8px' } }} styles={{ body: { padding: '8px' } }}
> >
<Statistic <Statistic
title={<span style={{ fontSize: '11px', color: '#666' }}></span>} title={<span style={{ fontSize: '11px', color: 'var(--color-text-secondary)' }}></span>}
value={currentProject.current_words} value={currentProject.current_words}
suffix="字" suffix="字"
valueStyle={{ fontSize: '16px', fontWeight: 600, color: '#fa8c16' }} valueStyle={{ fontSize: '16px', fontWeight: 600, color: 'var(--color-warning)' }}
/> />
</Card> </Card>
</Col> </Col>
@@ -384,15 +384,14 @@ export default function ProjectDetail() {
trigger={null} trigger={null}
width={220} width={220}
collapsedWidth={60} collapsedWidth={60}
className="modern-sider"
style={{ style={{
background: '#fff',
position: 'fixed', position: 'fixed',
left: 0, left: 0,
top: 70, top: 70,
bottom: 0, bottom: 0,
overflow: 'hidden', overflow: 'hidden',
boxShadow: '2px 0 12px rgba(0,0,0,0.08)', transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
transition: 'all 0.2s',
height: 'calc(100vh - 70px)' height: 'calc(100vh - 70px)'
}} }}
> >
@@ -412,7 +411,7 @@ export default function ProjectDetail() {
}}> }}>
<Content <Content
style={{ style={{
background: '#f5f7fa', background: 'var(--color-bg-base)',
padding: mobile ? 12 : 24, padding: mobile ? 12 : 24,
height: mobile ? 'calc(100vh - 56px)' : 'calc(100vh - 70px)', height: mobile ? 'calc(100vh - 56px)' : 'calc(100vh - 70px)',
overflow: 'hidden', overflow: 'hidden',
@@ -421,10 +420,10 @@ export default function ProjectDetail() {
}} }}
> >
<div style={{ <div style={{
background: '#fff', background: 'var(--color-bg-container)',
padding: mobile ? 12 : 24, padding: mobile ? 12 : 24,
borderRadius: mobile ? '8px' : '12px', borderRadius: mobile ? '8px' : '12px',
boxShadow: '0 2px 8px rgba(0,0,0,0.06)', boxShadow: 'var(--shadow-card)',
height: '100%', height: '100%',
overflow: 'hidden', overflow: 'hidden',
display: 'flex', display: 'flex',
+293 -111
View File
@@ -15,6 +15,7 @@ const { Title, Text, Paragraph } = Typography;
export default function ProjectList() { export default function ProjectList() {
const navigate = useNavigate(); const navigate = useNavigate();
const { projects, loading } = useStore(); const { projects, loading } = useStore();
const [modal, contextHolder] = Modal.useModal();
const [showApiTip, setShowApiTip] = useState(true); const [showApiTip, setShowApiTip] = useState(true);
const [importModalVisible, setImportModalVisible] = useState(false); const [importModalVisible, setImportModalVisible] = useState(false);
const [exportModalVisible, setExportModalVisible] = useState(false); const [exportModalVisible, setExportModalVisible] = useState(false);
@@ -56,7 +57,7 @@ export default function ProjectList() {
const handleDelete = (id: string) => { const handleDelete = (id: string) => {
const isMobile = window.innerWidth <= 768; const isMobile = window.innerWidth <= 768;
Modal.confirm({ modal.confirm({
title: '确认删除', title: '确认删除',
content: '删除项目将同时删除所有相关数据,此操作不可恢复。确定要删除吗?', content: '删除项目将同时删除所有相关数据,此操作不可恢复。确定要删除吗?',
okText: '确定', okText: '确定',
@@ -324,34 +325,83 @@ export default function ProjectList() {
} }
}; };
// 计算页脚高度和页面内边距
const isMobile = window.innerWidth <= 768;
const footerHeight = isMobile ? 48 : 52;
const topPadding = isMobile ? 20 : 32;
const sidePadding = isMobile ? 16 : 24;
return ( return (
<div style={{ <div style={{
minHeight: '100vh', height: '100vh',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', display: 'flex',
padding: window.innerWidth <= 768 ? '20px 16px' : '40px 24px' flexDirection: 'column',
background: 'linear-gradient(180deg, var(--color-bg-base) 0%, #EEF2F3 100%)',
overflow: 'hidden'
}}>
{contextHolder}
{/* 固定头部区域 */}
<div style={{
flexShrink: 0,
padding: `${topPadding}px ${sidePadding}px 0`,
}}> }}>
<div style={{ <div style={{
maxWidth: 1400, maxWidth: 1400,
margin: '0 auto', margin: '0 auto'
marginBottom: window.innerWidth <= 768 ? 20 : 40
}}> }}>
{/* 现代化头部区域 */}
<Card <Card
variant="borderless" variant="borderless"
style={{ style={{
background: 'rgba(255, 255, 255, 0.95)', background: 'linear-gradient(135deg, var(--color-primary) 0%, #5A9BA5 50%, var(--color-primary-hover) 100%)',
borderRadius: window.innerWidth <= 768 ? 12 : 16, borderRadius: window.innerWidth <= 768 ? 16 : 24,
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.1)', boxShadow: '0 12px 40px rgba(77, 128, 136, 0.25), 0 4px 12px rgba(0, 0, 0, 0.06)',
border: 'none',
position: 'relative',
overflow: 'hidden'
}} }}
> >
<Row align="middle" justify="space-between" gutter={[16, 16]}> {/* 装饰性背景元素 */}
<div style={{
position: 'absolute',
top: -60,
right: -60,
width: 200,
height: 200,
borderRadius: '50%',
background: 'rgba(255, 255, 255, 0.08)',
pointerEvents: 'none'
}} />
<div style={{
position: 'absolute',
bottom: -40,
left: '30%',
width: 120,
height: 120,
borderRadius: '50%',
background: 'rgba(255, 255, 255, 0.05)',
pointerEvents: 'none'
}} />
<div style={{
position: 'absolute',
top: '50%',
right: '15%',
width: 80,
height: 80,
borderRadius: '50%',
background: 'rgba(255, 255, 255, 0.06)',
pointerEvents: 'none'
}} />
<Row align="middle" justify="space-between" gutter={[16, 16]} style={{ position: 'relative', zIndex: 1 }}>
<Col xs={24} sm={12} md={10}> <Col xs={24} sm={12} md={10}>
<Space direction="vertical" size={4}> <Space direction="vertical" size={8}>
<Title level={window.innerWidth <= 768 ? 3 : 2} style={{ margin: 0 }}> <Title level={window.innerWidth <= 768 ? 3 : 2} style={{ margin: 0, color: '#fff', textShadow: '0 2px 4px rgba(0,0,0,0.1)' }}>
<FireOutlined style={{ color: '#ff4d4f', marginRight: 8 }} /> <FireOutlined style={{ color: 'rgba(255,255,255,0.9)', marginRight: 12 }} />
</Title> </Title>
<Text type="secondary" style={{ fontSize: window.innerWidth <= 768 ? 12 : 14 }}> <Text style={{ fontSize: window.innerWidth <= 768 ? 13 : 15, color: 'rgba(255,255,255,0.85)' }}>
</Text> </Text>
</Space> </Space>
</Col> </Col>
@@ -369,12 +419,13 @@ export default function ProjectList() {
onClick={() => navigate('/inspiration')} onClick={() => navigate('/inspiration')}
block block
style={{ style={{
borderRadius: 8, borderRadius: 10,
background: 'linear-gradient(135deg, #ffd700 0%, #ff8c00 100%)', background: 'rgba(255, 193, 7, 0.95)',
border: 'none', border: '1px solid rgba(255, 255, 255, 0.3)',
boxShadow: '0 2px 8px rgba(255, 215, 0, 0.4)', boxShadow: '0 4px 12px rgba(255, 193, 7, 0.35)',
color: '#fff', color: '#fff',
height: 40 height: 42,
fontWeight: 500
}} }}
> >
@@ -388,11 +439,14 @@ export default function ProjectList() {
onClick={() => navigate('/wizard')} onClick={() => navigate('/wizard')}
block block
style={{ style={{
borderRadius: 8, borderRadius: 10,
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'rgba(255, 255, 255, 0.2)',
border: 'none', border: '1px solid rgba(255, 255, 255, 0.4)',
boxShadow: '0 2px 8px rgba(102, 126, 234, 0.4)', boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
height: 40 color: '#fff',
height: 42,
fontWeight: 500,
backdropFilter: 'blur(8px)'
}} }}
> >
@@ -409,11 +463,14 @@ export default function ProjectList() {
onClick={() => navigate('/settings')} onClick={() => navigate('/settings')}
block block
style={{ style={{
borderRadius: 8, borderRadius: 10,
borderColor: '#d9d9d9', background: 'rgba(255, 255, 255, 0.15)',
border: '1px solid rgba(255, 255, 255, 0.3)',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)', boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)',
height: 36, height: 38,
padding: '0 8px' padding: '0 8px',
color: '#fff',
backdropFilter: 'blur(8px)'
}} }}
> >
@@ -461,10 +518,13 @@ export default function ProjectList() {
icon={<MoreOutlined />} icon={<MoreOutlined />}
block block
style={{ style={{
borderRadius: 8, borderRadius: 10,
borderColor: '#d9d9d9', background: 'rgba(255, 255, 255, 0.15)',
border: '1px solid rgba(255, 255, 255, 0.3)',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)', boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)',
height: 36 height: 38,
color: '#fff',
backdropFilter: 'blur(8px)'
}} }}
> >
@@ -487,11 +547,14 @@ export default function ProjectList() {
icon={<BulbOutlined />} icon={<BulbOutlined />}
onClick={() => navigate('/inspiration')} onClick={() => navigate('/inspiration')}
style={{ style={{
borderRadius: 8, borderRadius: 12,
background: 'linear-gradient(135deg, #ffd700 0%, #ff8c00 100%)', background: 'rgba(255, 193, 7, 0.95)',
border: 'none', border: '1px solid rgba(255, 255, 255, 0.3)',
boxShadow: '0 2px 8px rgba(255, 215, 0, 0.4)', boxShadow: '0 4px 16px rgba(255, 193, 7, 0.4)',
color: '#fff' color: '#fff',
fontWeight: 600,
height: 44,
transition: 'all 0.3s ease'
}} }}
> >
@@ -502,10 +565,15 @@ export default function ProjectList() {
icon={<RocketOutlined />} icon={<RocketOutlined />}
onClick={() => navigate('/wizard')} onClick={() => navigate('/wizard')}
style={{ style={{
borderRadius: 8, borderRadius: 12,
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'rgba(255, 255, 255, 0.2)',
border: 'none', border: '1px solid rgba(255, 255, 255, 0.4)',
boxShadow: '0 2px 8px rgba(102, 126, 234, 0.4)' boxShadow: '0 4px 16px rgba(0, 0, 0, 0.15)',
color: '#fff',
fontWeight: 500,
height: 44,
backdropFilter: 'blur(10px)',
transition: 'all 0.3s ease'
}} }}
> >
@@ -516,21 +584,15 @@ export default function ProjectList() {
icon={<SettingOutlined />} icon={<SettingOutlined />}
onClick={() => navigate('/settings')} onClick={() => navigate('/settings')}
style={{ style={{
borderRadius: 8, borderRadius: 12,
borderColor: '#d9d9d9', background: 'rgba(255, 255, 255, 0.15)',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)', border: '1px solid rgba(255, 255, 255, 0.3)',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
color: '#fff',
height: 44,
backdropFilter: 'blur(10px)',
transition: 'all 0.3s ease' transition: 'all 0.3s ease'
}} }}
onMouseEnter={(e) => {
e.currentTarget.style.borderColor = '#667eea';
e.currentTarget.style.color = '#667eea';
e.currentTarget.style.boxShadow = '0 2px 12px rgba(102, 126, 234, 0.3)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.borderColor = '#d9d9d9';
e.currentTarget.style.color = 'rgba(0, 0, 0, 0.88)';
e.currentTarget.style.boxShadow = '0 2px 8px rgba(0, 0, 0, 0.08)';
}}
> >
API设置 API设置
</Button> </Button>
@@ -573,21 +635,15 @@ export default function ProjectList() {
size="large" size="large"
icon={<MoreOutlined />} icon={<MoreOutlined />}
style={{ style={{
borderRadius: 8, borderRadius: 12,
borderColor: '#d9d9d9', background: 'rgba(255, 255, 255, 0.15)',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)', border: '1px solid rgba(255, 255, 255, 0.3)',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
color: '#fff',
height: 44,
backdropFilter: 'blur(10px)',
transition: 'all 0.3s ease' transition: 'all 0.3s ease'
}} }}
onMouseEnter={(e) => {
e.currentTarget.style.borderColor = '#1890ff';
e.currentTarget.style.color = '#1890ff';
e.currentTarget.style.boxShadow = '0 2px 12px rgba(24, 144, 255, 0.3)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.borderColor = '#d9d9d9';
e.currentTarget.style.color = 'rgba(0, 0, 0, 0.88)';
e.currentTarget.style.boxShadow = '0 2px 8px rgba(0, 0, 0, 0.08)';
}}
> >
</Button> </Button>
@@ -652,37 +708,104 @@ export default function ProjectList() {
)} )}
{projects.length > 0 && ( {projects.length > 0 && (
<Row gutter={[16, 16]} style={{ marginTop: window.innerWidth <= 768 ? 16 : 24 }}> <Row gutter={[isMobile ? 8 : 16, 16]} style={{ marginTop: isMobile ? 16 : 28, position: 'relative', zIndex: 1 }}>
<Col xs={24} sm={8}> <Col xs={8} sm={8}>
<Card variant="borderless" style={{ background: '#f0f5ff', borderRadius: 12 }}> <Card
variant="borderless"
style={{
background: 'rgba(255, 255, 255, 0.2)',
borderRadius: isMobile ? 12 : 16,
border: '1px solid rgba(255, 255, 255, 0.3)',
backdropFilter: 'blur(10px)',
boxShadow: '0 4px 16px rgba(0, 0, 0, 0.08)',
padding: isMobile ? '8px 4px' : '24px'
}}
styles={{ body: { padding: isMobile ? '4px' : '24px' } }}
>
<Statistic <Statistic
title={<span style={{ fontSize: window.innerWidth <= 768 ? 12 : 14, color: '#595959' }}></span>} title={
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: isMobile ? 4 : 8 }}>
<span style={{ fontSize: isMobile ? 16 : 14, color: 'rgba(255,255,255,0.9)', marginRight: isMobile ? 0 : 8 }}>📚</span>
{!isMobile && <span style={{ color: 'rgba(255,255,255,0.8)' }}></span>}
{isMobile && <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.8)', marginTop: 2 }}></div>}
</div>
}
value={projects.length} value={projects.length}
prefix={<BookOutlined style={{ color: '#1890ff' }} />} valueStyle={{
suffix="个" color: '#fff',
valueStyle={{ color: '#1890ff', fontSize: window.innerWidth <= 768 ? 20 : 28, fontWeight: 'bold' }} fontSize: isMobile ? 18 : 32,
fontWeight: 'bold',
textShadow: '0 1px 2px rgba(0,0,0,0.1)',
textAlign: 'center'
}}
/> />
</Card> </Card>
</Col> </Col>
<Col xs={24} sm={8}> <Col xs={8} sm={8}>
<Card variant="borderless" style={{ background: '#f6ffed', borderRadius: 12 }}> <Card
variant="borderless"
style={{
background: 'rgba(255, 255, 255, 0.2)',
borderRadius: isMobile ? 12 : 16,
border: '1px solid rgba(255, 255, 255, 0.3)',
backdropFilter: 'blur(10px)',
boxShadow: '0 4px 16px rgba(0, 0, 0, 0.08)',
padding: isMobile ? '8px 4px' : '24px'
}}
styles={{ body: { padding: isMobile ? '4px' : '24px' } }}
>
<Statistic <Statistic
title={<span style={{ fontSize: window.innerWidth <= 768 ? 12 : 14, color: '#595959' }}></span>} title={
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: isMobile ? 4 : 8 }}>
<span style={{ fontSize: isMobile ? 16 : 14, color: 'rgba(255,255,255,0.9)', marginRight: isMobile ? 0 : 8 }}></span>
{!isMobile && <span style={{ color: 'rgba(255,255,255,0.8)' }}></span>}
{isMobile && <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.8)', marginTop: 2 }}></div>}
</div>
}
value={activeProjects} value={activeProjects}
prefix={<EditOutlined style={{ color: '#52c41a' }} />} valueStyle={{
suffix="个" color: '#fff',
valueStyle={{ color: '#52c41a', fontSize: window.innerWidth <= 768 ? 20 : 28, fontWeight: 'bold' }} fontSize: isMobile ? 18 : 32,
fontWeight: 'bold',
textShadow: '0 1px 2px rgba(0,0,0,0.1)',
textAlign: 'center'
}}
/> />
</Card> </Card>
</Col> </Col>
<Col xs={24} sm={8}> <Col xs={8} sm={8}>
<Card variant="borderless" style={{ background: '#fff7e6', borderRadius: 12 }}> <Card
variant="borderless"
style={{
background: 'rgba(255, 255, 255, 0.2)',
borderRadius: isMobile ? 12 : 16,
border: '1px solid rgba(255, 255, 255, 0.3)',
backdropFilter: 'blur(10px)',
boxShadow: '0 4px 16px rgba(0, 0, 0, 0.08)',
padding: isMobile ? '8px 4px' : '24px'
}}
styles={{ body: { padding: isMobile ? '4px' : '24px' } }}
>
<Statistic <Statistic
title={<span style={{ fontSize: window.innerWidth <= 768 ? 12 : 14, color: '#595959' }}></span>} title={
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: isMobile ? 4 : 8 }}>
<span style={{ fontSize: isMobile ? 16 : 14, color: 'rgba(255,255,255,0.9)', marginRight: isMobile ? 0 : 8 }}>📝</span>
{!isMobile && <span style={{ color: 'rgba(255,255,255,0.8)' }}></span>}
{isMobile && <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.8)', marginTop: 2 }}></div>}
</div>
}
value={totalWords} value={totalWords}
prefix={<FileTextOutlined style={{ color: '#faad14' }} />} formatter={(value) => {
suffix="字" const val = Number(value);
valueStyle={{ color: '#faad14', fontSize: window.innerWidth <= 768 ? 20 : 28, fontWeight: 'bold' }} return isMobile && val > 10000 ? `${(val / 10000).toFixed(1)}w` : val;
}}
valueStyle={{
color: '#fff',
fontSize: isMobile ? 18 : 32,
fontWeight: 'bold',
textShadow: '0 1px 2px rgba(0,0,0,0.1)',
textAlign: 'center'
}}
/> />
</Card> </Card>
</Col> </Col>
@@ -690,16 +813,25 @@ export default function ProjectList() {
)} )}
</Card> </Card>
</div> </div>
</div>
{/* 可滚动的项目列表区域 */}
<div style={{
flex: 1,
overflow: 'auto',
padding: `${isMobile ? 16 : 24}px ${sidePadding}px`,
paddingBottom: footerHeight + (isMobile ? 24 : 32),
}}>
<div style={{ maxWidth: 1400, margin: '0 auto' }}> <div style={{ maxWidth: 1400, margin: '0 auto' }}>
<Spin spinning={loading}> <Spin spinning={loading}>
{!Array.isArray(projects) || projects.length === 0 ? ( {!Array.isArray(projects) || projects.length === 0 ? (
<Card <Card
variant="borderless" variant="borderless"
style={{ style={{
background: 'rgba(255, 255, 255, 0.95)', background: 'var(--color-bg-container)',
borderRadius: 16, borderRadius: 16,
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.1)', boxShadow: 'var(--shadow-card)',
border: '1px solid var(--color-border-secondary)'
}} }}
> >
<Empty <Empty
@@ -715,9 +847,10 @@ export default function ProjectList() {
icon={<BulbOutlined />} icon={<BulbOutlined />}
onClick={() => navigate('/inspiration')} onClick={() => navigate('/inspiration')}
style={{ style={{
background: 'linear-gradient(135deg, #ffd700 0%, #ff8c00 100%)', background: 'var(--color-warning)',
border: 'none', border: 'none',
color: '#fff' color: '#fff',
boxShadow: '0 2px 8px rgba(227, 173, 54, 0.2)'
}} }}
> >
@@ -727,6 +860,11 @@ export default function ProjectList() {
size="large" size="large"
icon={<RocketOutlined />} icon={<RocketOutlined />}
onClick={() => navigate('/wizard')} onClick={() => navigate('/wizard')}
style={{
background: 'var(--color-primary)',
border: 'none',
boxShadow: '0 2px 8px rgba(77, 128, 136, 0.2)'
}}
> >
</Button> </Button>
@@ -758,20 +896,52 @@ export default function ProjectList() {
styles={{ body: { padding: 0, overflow: 'hidden' } }} styles={{ body: { padding: 0, overflow: 'hidden' } }}
{...cardHoverHandlers} {...cardHoverHandlers}
> >
{/* 项目卡片头部 - 添加装饰元素 */}
<div style={{ <div style={{
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'linear-gradient(135deg, var(--color-primary) 0%, #5A9BA5 60%, var(--color-primary-hover) 100%)',
padding: window.innerWidth <= 768 ? '16px' : '24px', padding: window.innerWidth <= 768 ? '18px 16px' : '24px',
position: 'relative' position: 'relative',
overflow: 'hidden'
}}> }}>
<Space direction="vertical" size={8} style={{ width: '100%' }}> {/* 装饰性圆圈 */}
<div style={{ display: 'flex', alignItems: 'center', gap: window.innerWidth <= 768 ? 8 : 12 }}> <div style={{
<BookOutlined style={{ fontSize: window.innerWidth <= 768 ? 20 : 28, color: '#fff' }} /> position: 'absolute',
<Title level={window.innerWidth <= 768 ? 5 : 4} style={{ margin: 0, color: '#fff', flex: 1 }} ellipsis> top: -20,
right: -20,
width: 80,
height: 80,
borderRadius: '50%',
background: 'rgba(255, 255, 255, 0.1)',
pointerEvents: 'none'
}} />
<Space direction="vertical" size={8} style={{ width: '100%', position: 'relative', zIndex: 1 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: window.innerWidth <= 768 ? 10 : 12 }}>
<div style={{
width: window.innerWidth <= 768 ? 36 : 44,
height: window.innerWidth <= 768 ? 36 : 44,
borderRadius: 12,
background: 'rgba(255, 255, 255, 0.2)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backdropFilter: 'blur(4px)'
}}>
<BookOutlined style={{ fontSize: window.innerWidth <= 768 ? 18 : 22, color: '#fff' }} />
</div>
<Title level={window.innerWidth <= 768 ? 5 : 4} style={{ margin: 0, color: '#fff', flex: 1, textShadow: '0 1px 2px rgba(0,0,0,0.1)' }} ellipsis>
{project.title} {project.title}
</Title> </Title>
</div> </div>
{project.genre && ( {project.genre && (
<Tag color="rgba(255,255,255,0.3)" style={{ color: '#fff', border: 'none' }}> <Tag
color="rgba(255,255,255,0.2)"
style={{
color: '#fff',
border: '1px solid rgba(255,255,255,0.3)',
borderRadius: 6,
backdropFilter: 'blur(4px)'
}}
>
{project.genre} {project.genre}
</Tag> </Tag>
)} )}
@@ -809,11 +979,12 @@ export default function ProjectList() {
<Col span={12}> <Col span={12}>
<div style={{ <div style={{
textAlign: 'center', textAlign: 'center',
padding: '12px 0', padding: '14px 0',
background: '#f5f5f5', background: 'linear-gradient(135deg, #E8F4FC 0%, #F0F9FF 100%)',
borderRadius: 8 borderRadius: 12,
border: '1px solid rgba(24, 144, 255, 0.15)'
}}> }}>
<div style={{ fontSize: 20, fontWeight: 'bold', color: '#1890ff' }}> <div style={{ fontSize: 22, fontWeight: 'bold', color: 'var(--color-primary)' }}>
{project.current_words >= 1000000 {project.current_words >= 1000000
? (project.current_words / 1000000).toFixed(1) + 'M' ? (project.current_words / 1000000).toFixed(1) + 'M'
: project.current_words >= 1000 : project.current_words >= 1000
@@ -827,11 +998,12 @@ export default function ProjectList() {
<Col span={12}> <Col span={12}>
<div style={{ <div style={{
textAlign: 'center', textAlign: 'center',
padding: '12px 0', padding: '14px 0',
background: '#f5f5f5', background: 'linear-gradient(135deg, #F0FDF4 0%, #ECFDF5 100%)',
borderRadius: 8 borderRadius: 12,
border: '1px solid rgba(82, 196, 26, 0.15)'
}}> }}>
<div style={{ fontSize: 20, fontWeight: 'bold', color: '#52c41a' }}> <div style={{ fontSize: 22, fontWeight: 'bold', color: 'var(--color-success)' }}>
{project.target_words {project.target_words
? (project.target_words >= 1000000 ? (project.target_words >= 1000000
? (project.target_words / 1000000).toFixed(1) + 'M' ? (project.target_words / 1000000).toFixed(1) + 'M'
@@ -847,18 +1019,18 @@ export default function ProjectList() {
</Row> </Row>
<div style={{ <div style={{
marginTop: 16, marginTop: 18,
paddingTop: 16, paddingTop: 16,
borderTop: '1px solid #f0f0f0', borderTop: '1px solid rgba(0, 0, 0, 0.06)',
display: 'flex', display: 'flex',
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center' alignItems: 'center'
}}> }}>
<Text type="secondary" style={{ fontSize: 12 }}> <Text type="secondary" style={{ fontSize: 12, display: 'flex', alignItems: 'center', gap: 4 }}>
<CalendarOutlined style={{ marginRight: 4 }} /> <CalendarOutlined style={{ color: 'var(--color-primary)' }} />
{formatDate(project.updated_at)} {formatDate(project.updated_at)}
</Text> </Text>
<Space size={8}> <Space size={4}>
<Tooltip title="编辑"> <Tooltip title="编辑">
<Button <Button
type="text" type="text"
@@ -868,6 +1040,11 @@ export default function ProjectList() {
e.stopPropagation(); e.stopPropagation();
handleEditProject(project); handleEditProject(project);
}} }}
style={{
borderRadius: 8,
color: 'var(--color-primary)',
transition: 'all 0.2s ease'
}}
/> />
</Tooltip> </Tooltip>
<Tooltip title="删除"> <Tooltip title="删除">
@@ -880,6 +1057,10 @@ export default function ProjectList() {
e.stopPropagation(); e.stopPropagation();
handleDelete(project.id); handleDelete(project.id);
}} }}
style={{
borderRadius: 8,
transition: 'all 0.2s ease'
}}
/> />
</Tooltip> </Tooltip>
</Space> </Space>
@@ -1235,5 +1416,6 @@ export default function ProjectList() {
<ChangelogFloatingButton /> <ChangelogFloatingButton />
</div> </div>
</div>
); );
} }
+7 -9
View File
@@ -195,7 +195,7 @@ export default function ProjectWizardNew() {
<Card <Card
hoverable hoverable
style={{ style={{
borderColor: form.getFieldValue('outline_mode') === 'one-to-one' ? '#1890ff' : '#d9d9d9', borderColor: form.getFieldValue('outline_mode') === 'one-to-one' ? 'var(--color-primary)' : 'var(--color-border)',
borderWidth: 2, borderWidth: 2,
height: '100%', height: '100%',
}} }}
@@ -204,7 +204,7 @@ export default function ProjectWizardNew() {
<Radio value="one-to-one" style={{ width: '100%' }}> <Radio value="one-to-one" style={{ width: '100%' }}>
<Space direction="vertical" size={4} style={{ width: '100%' }}> <Space direction="vertical" size={4} style={{ width: '100%' }}>
<div style={{ fontSize: 16, fontWeight: 'bold' }}> <div style={{ fontSize: 16, fontWeight: 'bold' }}>
<CheckCircleOutlined style={{ marginRight: 8, color: '#52c41a' }} /> <CheckCircleOutlined style={{ marginRight: 8, color: 'var(--color-success)' }} />
(11) (11)
</div> </div>
<div style={{ fontSize: 12, color: '#666' }}> <div style={{ fontSize: 12, color: '#666' }}>
@@ -222,7 +222,7 @@ export default function ProjectWizardNew() {
<Card <Card
hoverable hoverable
style={{ style={{
borderColor: form.getFieldValue('outline_mode') === 'one-to-many' ? '#1890ff' : '#d9d9d9', borderColor: form.getFieldValue('outline_mode') === 'one-to-many' ? 'var(--color-primary)' : 'var(--color-border)',
borderWidth: 2, borderWidth: 2,
height: '100%', height: '100%',
}} }}
@@ -231,7 +231,7 @@ export default function ProjectWizardNew() {
<Radio value="one-to-many" style={{ width: '100%' }}> <Radio value="one-to-many" style={{ width: '100%' }}>
<Space direction="vertical" size={4} style={{ width: '100%' }}> <Space direction="vertical" size={4} style={{ width: '100%' }}>
<div style={{ fontSize: 16, fontWeight: 'bold' }}> <div style={{ fontSize: 16, fontWeight: 'bold' }}>
<CheckCircleOutlined style={{ marginRight: 8, color: '#52c41a' }} /> <CheckCircleOutlined style={{ marginRight: 8, color: 'var(--color-success)' }} />
(1N) (1N)
</div> </div>
<div style={{ fontSize: 12, color: '#666' }}> <div style={{ fontSize: 12, color: '#666' }}>
@@ -321,17 +321,15 @@ export default function ProjectWizardNew() {
return ( return (
<div style={{ <div style={{
minHeight: '100vh', minHeight: '100vh',
background: currentStep === 'generating' background: 'var(--color-bg-base)',
? 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
: '#f5f7fa',
}}> }}>
{/* 顶部标题栏 - 固定不滚动 */} {/* 顶部标题栏 - 固定不滚动 */}
<div style={{ <div style={{
position: 'sticky', position: 'sticky',
top: 0, top: 0,
zIndex: 100, zIndex: 100,
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'var(--color-primary)',
boxShadow: '0 4px 12px rgba(0,0,0,0.15)', boxShadow: 'var(--shadow-header)',
}}> }}>
<div style={{ <div style={{
maxWidth: 1200, maxWidth: 1200,
+76 -32
View File
@@ -193,50 +193,87 @@ export default function PromptTemplates() {
return ( return (
<div style={{ <div style={{
minHeight: '100vh', minHeight: '100vh',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'linear-gradient(180deg, var(--color-bg-base) 0%, #EEF2F3 100%)',
padding: isMobile ? '20px 16px' : '40px 24px' padding: isMobile ? '20px 16px' : '40px 24px',
display: 'flex',
flexDirection: 'column',
}}> }}>
{/* 头部卡片 */}
<div style={{ <div style={{
maxWidth: 1400, maxWidth: 1400,
margin: '0 auto', margin: '0 auto',
marginBottom: isMobile ? 20 : 40 width: '100%',
flex: 1,
display: 'flex',
flexDirection: 'column',
}}> }}>
{/* 顶部导航卡片 */}
<Card <Card
variant="borderless" variant="borderless"
style={{ style={{
background: 'rgba(255, 255, 255, 0.95)', background: 'linear-gradient(135deg, var(--color-primary) 0%, #5A9BA5 50%, var(--color-primary-hover) 100%)',
borderRadius: isMobile ? 12 : 16, borderRadius: isMobile ? 16 : 24,
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.1)', boxShadow: '0 12px 40px rgba(77, 128, 136, 0.25), 0 4px 12px rgba(0, 0, 0, 0.06)',
marginBottom: isMobile ? 20 : 24,
border: 'none',
position: 'relative',
overflow: 'hidden'
}} }}
> >
<Row align="middle" justify="space-between" gutter={[16, 16]}> {/* 装饰性背景元素 */}
<div style={{ position: 'absolute', top: -60, right: -60, width: 200, height: 200, borderRadius: '50%', background: 'rgba(255, 255, 255, 0.08)', pointerEvents: 'none' }} />
<div style={{ position: 'absolute', bottom: -40, left: '30%', width: 120, height: 120, borderRadius: '50%', background: 'rgba(255, 255, 255, 0.05)', pointerEvents: 'none' }} />
<div style={{ position: 'absolute', top: '50%', right: '15%', width: 80, height: 80, borderRadius: '50%', background: 'rgba(255, 255, 255, 0.06)', pointerEvents: 'none' }} />
<Row align="middle" justify="space-between" gutter={[16, 16]} style={{ position: 'relative', zIndex: 1 }}>
<Col xs={24} sm={12} md={14}> <Col xs={24} sm={12} md={14}>
<Space direction="vertical" size={4}> <Space direction="vertical" size={4}>
<Space align="center"> <Title level={isMobile ? 3 : 2} style={{ margin: 0, color: '#fff', textShadow: '0 2px 4px rgba(0,0,0,0.1)' }}>
<Button <FileSearchOutlined style={{ color: 'rgba(255,255,255,0.9)', marginRight: 8 }} />
type="text"
icon={<ArrowLeftOutlined />}
onClick={() => navigate('/projects')}
size={isMobile ? 'small' : 'middle'}
/>
<Title level={isMobile ? 3 : 2} style={{ margin: 0 }}>
<FileSearchOutlined style={{ color: '#667eea', marginRight: 8 }} />
</Title> </Title>
</Space> <Text style={{ fontSize: isMobile ? 12 : 14, color: 'rgba(255,255,255,0.85)', marginLeft: isMobile ? 40 : 48 }}>
<Text type="secondary" style={{ fontSize: isMobile ? 12 : 14, marginLeft: isMobile ? 40 : 48 }}>
AI生成提示词 AI生成提示词
</Text> </Text>
</Space> </Space>
</Col> </Col>
<Col xs={24} sm={12} md={10}> <Col xs={24} sm={12} md={10}>
<Space wrap style={{ justifyContent: isMobile ? 'flex-start' : 'flex-end', width: '100%' }}> <Space wrap style={{ justifyContent: isMobile ? 'flex-start' : 'flex-end', width: '100%' }}>
<Button
icon={<ArrowLeftOutlined />}
onClick={() => navigate('/projects')}
style={{
borderRadius: 12,
background: 'rgba(255, 255, 255, 0.15)',
border: '1px solid rgba(255, 255, 255, 0.3)',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
color: '#fff',
backdropFilter: 'blur(10px)',
transition: 'all 0.3s ease'
}}
onMouseEnter={(e) => {
e.currentTarget.style.background = 'rgba(255, 255, 255, 0.25)';
e.currentTarget.style.transform = 'translateY(-1px)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = 'rgba(255, 255, 255, 0.15)';
e.currentTarget.style.transform = 'none';
}}
>
</Button>
<Button <Button
icon={<DownloadOutlined />} icon={<DownloadOutlined />}
onClick={handleExport} onClick={handleExport}
size={isMobile ? 'small' : 'middle'} size={isMobile ? 'small' : 'middle'}
style={{ borderRadius: 8 }} style={{
borderRadius: 12,
background: 'rgba(255, 255, 255, 0.15)',
border: '1px solid rgba(255, 255, 255, 0.3)',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
color: '#fff',
backdropFilter: 'blur(10px)',
transition: 'all 0.3s ease'
}}
> >
</Button> </Button>
@@ -248,7 +285,14 @@ export default function PromptTemplates() {
<Button <Button
icon={<UploadOutlined />} icon={<UploadOutlined />}
size={isMobile ? 'small' : 'middle'} size={isMobile ? 'small' : 'middle'}
style={{ borderRadius: 8 }} style={{
borderRadius: 12,
background: 'rgba(255, 255, 255, 0.15)',
border: '1px solid rgba(255, 255, 255, 0.3)',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
color: '#fff',
backdropFilter: 'blur(10px)',
}}
> >
</Button> </Button>
@@ -261,7 +305,7 @@ export default function PromptTemplates() {
<Alert <Alert
message={ message={
<Space align="center"> <Space align="center">
<InfoCircleOutlined style={{ fontSize: 16, color: '#1890ff' }} /> <InfoCircleOutlined style={{ fontSize: 16, color: 'var(--color-primary)' }} />
<Text strong style={{ fontSize: isMobile ? 13 : 14 }}>使</Text> <Text strong style={{ fontSize: isMobile ? 13 : 14 }}>使</Text>
</Space> </Space>
} }
@@ -280,15 +324,14 @@ export default function PromptTemplates() {
style={{ style={{
marginTop: isMobile ? 16 : 24, marginTop: isMobile ? 16 : 24,
borderRadius: 12, borderRadius: 12,
background: 'linear-gradient(135deg, #e6f7ff 0%, #f0f5ff 100%)', background: 'var(--color-info-bg)',
border: '1px solid #91d5ff' border: '1px solid var(--color-info-border)'
}} }}
/> />
</Card> </Card>
</div>
{/* 主内容区 */} {/* 主内容区 */}
<div style={{ maxWidth: 1400, margin: '0 auto' }}> <div style={{ flex: 1 }}>
<Spin spinning={loading}> <Spin spinning={loading}>
{/* 分类标签 */} {/* 分类标签 */}
{categories.length > 0 && ( {categories.length > 0 && (
@@ -322,7 +365,7 @@ export default function PromptTemplates() {
variant="borderless" variant="borderless"
style={{ style={{
background: 'rgba(255, 255, 255, 0.95)', background: 'rgba(255, 255, 255, 0.95)',
borderRadius: 16, borderRadius: isMobile ? 12 : 16,
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.1)', boxShadow: '0 8px 32px rgba(0, 0, 0, 0.1)',
}} }}
> >
@@ -345,14 +388,14 @@ export default function PromptTemplates() {
{/* 头部 */} {/* 头部 */}
<div style={{ <div style={{
background: template.is_system_default background: template.is_system_default
? 'linear-gradient(135deg, #a8a8a8 0%, #636363 100%)' ? 'var(--color-bg-layout)'
: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', : 'var(--color-primary)',
padding: isMobile ? '16px' : '20px', padding: isMobile ? '16px' : '20px',
position: 'relative' position: 'relative'
}}> }}>
<Space direction="vertical" size={8} style={{ width: '100%' }}> <Space direction="vertical" size={8} style={{ width: '100%' }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<Title level={isMobile ? 5 : 4} style={{ margin: 0, color: '#fff', flex: 1 }} ellipsis> <Title level={isMobile ? 5 : 4} style={{ margin: 0, color: template.is_system_default ? 'var(--color-text-primary)' : '#fff', flex: 1 }} ellipsis>
{template.template_name} {template.template_name}
</Title> </Title>
{!template.is_system_default && ( {!template.is_system_default && (
@@ -365,10 +408,10 @@ export default function PromptTemplates() {
)} )}
</div> </div>
<Space wrap> <Space wrap>
<Tag color="rgba(255,255,255,0.3)" style={{ color: '#fff', border: 'none' }}> <Tag color={template.is_system_default ? 'default' : 'rgba(255,255,255,0.3)'} style={{ color: template.is_system_default ? 'var(--color-text-secondary)' : '#fff', border: 'none' }}>
{template.category} {template.category}
</Tag> </Tag>
<Tag color="rgba(255,255,255,0.3)" style={{ color: '#fff', border: 'none' }}> <Tag color={template.is_system_default ? 'default' : 'rgba(255,255,255,0.3)'} style={{ color: template.is_system_default ? 'var(--color-text-secondary)' : '#fff', border: 'none' }}>
{template.is_system_default ? '系统默认' : '已自定义'} {template.is_system_default ? '系统默认' : '已自定义'}
</Tag> </Tag>
</Space> </Space>
@@ -426,6 +469,7 @@ export default function PromptTemplates() {
)} )}
</Spin> </Spin>
</div> </div>
</div>
{/* 编辑对话框 */} {/* 编辑对话框 */}
<Modal <Modal
+23 -23
View File
@@ -238,7 +238,7 @@ export default function SettingsPage() {
return ( return (
<div style={{ <div style={{
minHeight: '100vh', minHeight: '100vh',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'var(--color-bg-base)',
padding: isMobile ? '16px 12px' : '40px 24px' padding: isMobile ? '16px 12px' : '40px 24px'
}}> }}>
<div style={{ <div style={{
@@ -248,9 +248,9 @@ export default function SettingsPage() {
<Card <Card
variant="borderless" variant="borderless"
style={{ style={{
background: 'rgba(255, 255, 255, 0.95)', background: 'var(--color-bg-container)',
borderRadius: isMobile ? 12 : 16, borderRadius: isMobile ? 12 : 16,
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.1)', boxShadow: 'var(--shadow-card)',
}} }}
styles={{ styles={{
body: { body: {
@@ -281,7 +281,7 @@ export default function SettingsPage() {
fontSize: isMobile ? '18px' : undefined fontSize: isMobile ? '18px' : undefined
}} }}
> >
<SettingOutlined style={{ marginRight: 8, color: '#667eea' }} /> <SettingOutlined style={{ marginRight: 8, color: 'var(--color-primary)' }} />
{isMobile ? 'API 设置' : 'AI API 设置'} {isMobile ? 'API 设置' : 'AI API 设置'}
</Title> </Title>
</Space> </Space>
@@ -341,7 +341,7 @@ export default function SettingsPage() {
<Space size={4}> <Space size={4}>
<span>API </span> <span>API </span>
<Tooltip title="选择你的AI服务提供商"> <Tooltip title="选择你的AI服务提供商">
<InfoCircleOutlined style={{ color: '#8c8c8c', fontSize: isMobile ? '12px' : '14px' }} /> <InfoCircleOutlined style={{ color: 'var(--color-text-secondary)', fontSize: isMobile ? '12px' : '14px' }} />
</Tooltip> </Tooltip>
</Space> </Space>
} }
@@ -362,7 +362,7 @@ export default function SettingsPage() {
<Space size={4}> <Space size={4}>
<span>API </span> <span>API </span>
<Tooltip title="你的API密钥,将加密存储"> <Tooltip title="你的API密钥,将加密存储">
<InfoCircleOutlined style={{ color: '#8c8c8c', fontSize: isMobile ? '12px' : '14px' }} /> <InfoCircleOutlined style={{ color: 'var(--color-text-secondary)', fontSize: isMobile ? '12px' : '14px' }} />
</Tooltip> </Tooltip>
</Space> </Space>
} }
@@ -381,7 +381,7 @@ export default function SettingsPage() {
<Space size={4}> <Space size={4}>
<span>API </span> <span>API </span>
<Tooltip title="API的基础URL地址"> <Tooltip title="API的基础URL地址">
<InfoCircleOutlined style={{ color: '#8c8c8c', fontSize: isMobile ? '12px' : '14px' }} /> <InfoCircleOutlined style={{ color: 'var(--color-text-secondary)', fontSize: isMobile ? '12px' : '14px' }} />
</Tooltip> </Tooltip>
</Space> </Space>
} }
@@ -402,7 +402,7 @@ export default function SettingsPage() {
<Space size={4}> <Space size={4}>
<span></span> <span></span>
<Tooltip title="AI模型的名称,如 gpt-4, gpt-3.5-turbo"> <Tooltip title="AI模型的名称,如 gpt-4, gpt-3.5-turbo">
<InfoCircleOutlined style={{ color: '#8c8c8c', fontSize: isMobile ? '12px' : '14px' }} /> <InfoCircleOutlined style={{ color: 'var(--color-text-secondary)', fontSize: isMobile ? '12px' : '14px' }} />
</Tooltip> </Tooltip>
</Space> </Space>
} }
@@ -424,7 +424,7 @@ export default function SettingsPage() {
<> <>
{menu} {menu}
{fetchingModels && ( {fetchingModels && (
<div style={{ padding: '8px 12px', color: '#8c8c8c', textAlign: 'center', fontSize: isMobile ? '12px' : '14px' }}> <div style={{ padding: '8px 12px', color: 'var(--color-text-secondary)', textAlign: 'center', fontSize: isMobile ? '12px' : '14px' }}>
<Spin size="small" /> ... <Spin size="small" /> ...
</div> </div>
)} )}
@@ -434,7 +434,7 @@ export default function SettingsPage() {
</div> </div>
)} )}
{!fetchingModels && modelOptions.length === 0 && !modelsFetched && ( {!fetchingModels && modelOptions.length === 0 && !modelsFetched && (
<div style={{ padding: '8px 12px', color: '#8c8c8c', textAlign: 'center', fontSize: isMobile ? '12px' : '14px' }}> <div style={{ padding: '8px 12px', color: 'var(--color-text-secondary)', textAlign: 'center', fontSize: isMobile ? '12px' : '14px' }}>
</div> </div>
)} )}
@@ -446,7 +446,7 @@ export default function SettingsPage() {
<Spin size="small" /> ... <Spin size="small" /> ...
</div> </div>
) : ( ) : (
<div style={{ padding: '8px 12px', color: '#8c8c8c', textAlign: 'center', fontSize: isMobile ? '12px' : '14px' }}> <div style={{ padding: '8px 12px', color: 'var(--color-text-secondary)', textAlign: 'center', fontSize: isMobile ? '12px' : '14px' }}>
</div> </div>
) )
@@ -506,7 +506,7 @@ export default function SettingsPage() {
<Space size={4}> <Space size={4}>
<span></span> <span></span>
<Tooltip title="控制输出的随机性,值越高越随机(0.0-2.0)"> <Tooltip title="控制输出的随机性,值越高越随机(0.0-2.0)">
<InfoCircleOutlined style={{ color: '#8c8c8c', fontSize: isMobile ? '12px' : '14px' }} /> <InfoCircleOutlined style={{ color: 'var(--color-text-secondary)', fontSize: isMobile ? '12px' : '14px' }} />
</Tooltip> </Tooltip>
</Space> </Space>
} }
@@ -530,7 +530,7 @@ export default function SettingsPage() {
<Space size={4}> <Space size={4}>
<span> Token </span> <span> Token </span>
<Tooltip title="单次请求的最大token数量"> <Tooltip title="单次请求的最大token数量">
<InfoCircleOutlined style={{ color: '#8c8c8c', fontSize: isMobile ? '12px' : '14px' }} /> <InfoCircleOutlined style={{ color: 'var(--color-text-secondary)', fontSize: isMobile ? '12px' : '14px' }} />
</Tooltip> </Tooltip>
</Space> </Space>
} }
@@ -554,9 +554,9 @@ export default function SettingsPage() {
message={ message={
<Space> <Space>
{testResult.success ? ( {testResult.success ? (
<CheckCircleOutlined style={{ color: '#52c41a', fontSize: isMobile ? '16px' : '18px' }} /> <CheckCircleOutlined style={{ color: 'var(--color-success)', fontSize: isMobile ? '16px' : '18px' }} />
) : ( ) : (
<CloseCircleOutlined style={{ color: '#ff4d4f', fontSize: isMobile ? '16px' : '18px' }} /> <CloseCircleOutlined style={{ color: 'var(--color-error)', fontSize: isMobile ? '16px' : '18px' }} />
)} )}
<span style={{ fontSize: isMobile ? '14px' : '16px', fontWeight: 500 }}> <span style={{ fontSize: isMobile ? '14px' : '16px', fontWeight: 500 }}>
{testResult.message} {testResult.message}
@@ -585,7 +585,7 @@ export default function SettingsPage() {
<div style={{ color: '#595959' }}>{testResult.response_preview}</div> <div style={{ color: '#595959' }}>{testResult.response_preview}</div>
</div> </div>
)} )}
<div style={{ color: '#52c41a', fontSize: isMobile ? '12px' : '13px', marginTop: '4px' }}> <div style={{ color: 'var(--color-success)', fontSize: isMobile ? '12px' : '13px', marginTop: '4px' }}>
API 使 API 使
</div> </div>
</Space> </Space>
@@ -604,7 +604,7 @@ export default function SettingsPage() {
</div> </div>
)} )}
{testResult.error_type && ( {testResult.error_type && (
<div style={{ fontSize: isMobile ? '11px' : '12px', color: '#8c8c8c' }}> <div style={{ fontSize: isMobile ? '11px' : '12px', color: 'var(--color-text-secondary)' }}>
: {testResult.error_type} : {testResult.error_type}
</div> </div>
)} )}
@@ -649,7 +649,7 @@ export default function SettingsPage() {
loading={loading} loading={loading}
block block
style={{ style={{
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'var(--color-primary)',
border: 'none', border: 'none',
height: '44px' height: '44px'
}} }}
@@ -663,8 +663,8 @@ export default function SettingsPage() {
loading={testingApi} loading={testingApi}
block block
style={{ style={{
borderColor: '#52c41a', borderColor: 'var(--color-success)',
color: '#52c41a', color: 'var(--color-success)',
fontWeight: 500, fontWeight: 500,
height: '44px' height: '44px'
}} }}
@@ -729,8 +729,8 @@ export default function SettingsPage() {
onClick={handleTestConnection} onClick={handleTestConnection}
loading={testingApi} loading={testingApi}
style={{ style={{
borderColor: '#52c41a', borderColor: 'var(--color-success)',
color: '#52c41a', color: 'var(--color-success)',
fontWeight: 500, fontWeight: 500,
minWidth: '100px' minWidth: '100px'
}} }}
@@ -754,7 +754,7 @@ export default function SettingsPage() {
htmlType="submit" htmlType="submit"
loading={loading} loading={loading}
style={{ style={{
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'var(--color-primary)',
border: 'none', border: 'none',
minWidth: '120px', minWidth: '120px',
fontWeight: 500 fontWeight: 500
+12 -12
View File
@@ -28,17 +28,17 @@ const sponsorOptions: SponsorOption[] = [
const benefits = [ const benefits = [
{ {
icon: <FileTextOutlined style={{ fontSize: '32px', color: '#1890ff' }} />, icon: <FileTextOutlined style={{ fontSize: '32px', color: 'var(--color-primary)' }} />,
title: '优先需求响应', title: '优先需求响应',
description: '您的功能需求和问题反馈将获得优先处理' description: '您的功能需求和问题反馈将获得优先处理'
}, },
{ {
icon: <RocketOutlined style={{ fontSize: '32px', color: '#52c41a' }} />, icon: <RocketOutlined style={{ fontSize: '32px', color: 'var(--color-success)' }} />,
title: 'Windows一键启动', title: 'Windows一键启动',
description: '获取免安装EXE程序,双击即可使用' description: '获取免安装EXE程序,双击即可使用'
}, },
{ {
icon: <MessageOutlined style={{ fontSize: '32px', color: '#fa8c16' }} />, icon: <MessageOutlined style={{ fontSize: '32px', color: 'var(--color-warning)' }} />,
title: '专属技术支持', title: '专属技术支持',
description: '加入赞助者群,获得远程协助和配置指导' description: '加入赞助者群,获得远程协助和配置指导'
} }
@@ -75,7 +75,7 @@ export default function Sponsor() {
<div style={{ <div style={{
marginTop: '16px', marginTop: '16px',
padding: '16px', padding: '16px',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'var(--color-primary)',
borderRadius: '12px', borderRadius: '12px',
color: '#fff' color: '#fff'
}}> }}>
@@ -91,7 +91,7 @@ export default function Sponsor() {
{/* 赞助专属权益 */} {/* 赞助专属权益 */}
<div style={{ marginBottom: '32px' }}> <div style={{ marginBottom: '32px' }}>
<Title level={3} style={{ textAlign: 'center', marginBottom: '20px' }}> <Title level={3} style={{ textAlign: 'center', marginBottom: '20px' }}>
<CheckCircleOutlined style={{ color: '#52c41a', marginRight: '8px' }} /> <CheckCircleOutlined style={{ color: 'var(--color-success)', marginRight: '8px' }} />
</Title> </Title>
@@ -139,27 +139,27 @@ export default function Sponsor() {
style={{ style={{
textAlign: 'center', textAlign: 'center',
borderRadius: '10px', borderRadius: '10px',
boxShadow: '0 2px 8px rgba(0,0,0,0.08)', boxShadow: 'var(--shadow-card)',
cursor: 'pointer', cursor: 'pointer',
transition: 'all 0.3s', transition: 'all 0.3s',
border: '2px solid #f0f0f0' border: '2px solid var(--color-border)'
}} }}
styles={{ styles={{
body: { padding: '20px 12px' } body: { padding: '20px 12px' }
}} }}
onMouseEnter={(e) => { onMouseEnter={(e) => {
e.currentTarget.style.transform = 'translateY(-8px)'; e.currentTarget.style.transform = 'translateY(-8px)';
e.currentTarget.style.boxShadow = '0 8px 24px rgba(0,0,0,0.15)'; e.currentTarget.style.boxShadow = 'var(--shadow-elevated)';
e.currentTarget.style.borderColor = '#1890ff'; e.currentTarget.style.borderColor = 'var(--color-primary)';
}} }}
onMouseLeave={(e) => { onMouseLeave={(e) => {
e.currentTarget.style.transform = 'translateY(0)'; e.currentTarget.style.transform = 'translateY(0)';
e.currentTarget.style.boxShadow = '0 4px 12px rgba(0,0,0,0.08)'; e.currentTarget.style.boxShadow = 'var(--shadow-card)';
e.currentTarget.style.borderColor = '#f0f0f0'; e.currentTarget.style.borderColor = 'var(--color-border)';
}} }}
> >
<Title level={3} style={{ <Title level={3} style={{
color: '#1890ff', color: 'var(--color-primary)',
marginBottom: '4px', marginBottom: '4px',
fontSize: '28px', fontSize: '28px',
fontWeight: 'bold' fontWeight: 'bold'
+47 -32
View File
@@ -59,6 +59,7 @@ export default function UserManagement() {
const [form] = Form.useForm(); const [form] = Form.useForm();
const [editForm] = Form.useForm(); const [editForm] = Form.useForm();
const [modal, contextHolder] = Modal.useModal();
// 过滤用户列表 // 过滤用户列表
const filteredUsers = users.filter(user => { const filteredUsers = users.filter(user => {
@@ -97,7 +98,7 @@ export default function UserManagement() {
// 如果有默认密码,显示给管理员 // 如果有默认密码,显示给管理员
if (res.default_password) { if (res.default_password) {
Modal.info({ modal.info({
title: '用户创建成功', title: '用户创建成功',
content: ( content: (
<div> <div>
@@ -180,7 +181,7 @@ export default function UserManagement() {
newPassword || undefined newPassword || undefined
); );
Modal.info({ modal.info({
title: '密码重置成功', title: '密码重置成功',
content: ( content: (
<div> <div>
@@ -226,7 +227,7 @@ export default function UserManagement() {
width: 150, width: 150,
render: (text: string) => ( render: (text: string) => (
<Space> <Space>
<UserOutlined style={{ color: '#1890ff' }} /> <UserOutlined style={{ color: 'var(--color-primary)' }} />
<Text strong>{text}</Text> <Text strong>{text}</Text>
</Space> </Space>
), ),
@@ -314,7 +315,7 @@ export default function UserManagement() {
icon: isActive ? <StopOutlined /> : <CheckCircleOutlined />, icon: isActive ? <StopOutlined /> : <CheckCircleOutlined />,
danger: isActive, danger: isActive,
onClick: () => { onClick: () => {
Modal.confirm({ modal.confirm({
title: `确定${isActive ? '禁用' : '启用'}该用户吗?`, title: `确定${isActive ? '禁用' : '启用'}该用户吗?`,
onOk: () => handleToggleStatus(record), onOk: () => handleToggleStatus(record),
okText: '确定', okText: '确定',
@@ -328,7 +329,7 @@ export default function UserManagement() {
icon: <DeleteOutlined />, icon: <DeleteOutlined />,
danger: true, danger: true,
onClick: () => { onClick: () => {
Modal.confirm({ modal.confirm({
title: '确定删除该用户吗?此操作不可恢复!', title: '确定删除该用户吗?此操作不可恢复!',
onOk: () => handleDelete(record), onOk: () => handleDelete(record),
okText: '确定', okText: '确定',
@@ -418,12 +419,13 @@ export default function UserManagement() {
return ( return (
<div style={{ <div style={{
height: '100vh', height: '100vh',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'linear-gradient(180deg, var(--color-bg-base) 0%, #EEF2F3 100%)',
padding: isMobile ? '20px 16px' : '40px 24px', padding: isMobile ? '20px 16px' : '40px 24px',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
overflow: 'hidden', overflow: 'hidden',
}}> }}>
{contextHolder}
<div style={{ <div style={{
maxWidth: 1400, maxWidth: 1400,
margin: '0 auto', margin: '0 auto',
@@ -437,20 +439,28 @@ export default function UserManagement() {
<Card <Card
variant="borderless" variant="borderless"
style={{ style={{
background: 'rgba(255, 255, 255, 0.95)', background: 'linear-gradient(135deg, var(--color-primary) 0%, #5A9BA5 50%, var(--color-primary-hover) 100%)',
borderRadius: isMobile ? 12 : 16, borderRadius: isMobile ? 16 : 24,
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.1)', boxShadow: '0 12px 40px rgba(77, 128, 136, 0.25), 0 4px 12px rgba(0, 0, 0, 0.06)',
marginBottom: isMobile ? 20 : 24, marginBottom: isMobile ? 20 : 24,
border: 'none',
position: 'relative',
overflow: 'hidden'
}} }}
> >
<Row align="middle" justify="space-between" gutter={[16, 16]}> {/* 装饰性背景元素 */}
<div style={{ position: 'absolute', top: -60, right: -60, width: 200, height: 200, borderRadius: '50%', background: 'rgba(255, 255, 255, 0.08)', pointerEvents: 'none' }} />
<div style={{ position: 'absolute', bottom: -40, left: '30%', width: 120, height: 120, borderRadius: '50%', background: 'rgba(255, 255, 255, 0.05)', pointerEvents: 'none' }} />
<div style={{ position: 'absolute', top: '50%', right: '15%', width: 80, height: 80, borderRadius: '50%', background: 'rgba(255, 255, 255, 0.06)', pointerEvents: 'none' }} />
<Row align="middle" justify="space-between" gutter={[16, 16]} style={{ position: 'relative', zIndex: 1 }}>
<Col xs={24} sm={12}> <Col xs={24} sm={12}>
<Space direction="vertical" size={4}> <Space direction="vertical" size={4}>
<Title level={isMobile ? 3 : 2} style={{ margin: 0 }}> <Title level={isMobile ? 3 : 2} style={{ margin: 0, color: '#fff', textShadow: '0 2px 4px rgba(0,0,0,0.1)' }}>
<TeamOutlined style={{ color: '#fa8c16', marginRight: 8 }} /> <TeamOutlined style={{ color: 'rgba(255,255,255,0.9)', marginRight: 12 }} />
</Title> </Title>
<Text type="secondary" style={{ fontSize: isMobile ? 12 : 14 }}> <Text style={{ fontSize: isMobile ? 12 : 14, color: 'rgba(255,255,255,0.85)' }}>
</Text> </Text>
</Space> </Space>
@@ -461,20 +471,21 @@ export default function UserManagement() {
icon={<ArrowLeftOutlined />} icon={<ArrowLeftOutlined />}
onClick={() => navigate('/')} onClick={() => navigate('/')}
style={{ style={{
borderRadius: 8, borderRadius: 12,
borderColor: '#d9d9d9', background: 'rgba(255, 255, 255, 0.15)',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)', border: '1px solid rgba(255, 255, 255, 0.3)',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
color: '#fff',
backdropFilter: 'blur(10px)',
transition: 'all 0.3s ease' transition: 'all 0.3s ease'
}} }}
onMouseEnter={(e) => { onMouseEnter={(e) => {
e.currentTarget.style.borderColor = '#667eea'; e.currentTarget.style.background = 'rgba(255, 255, 255, 0.25)';
e.currentTarget.style.color = '#667eea'; e.currentTarget.style.transform = 'translateY(-1px)';
e.currentTarget.style.boxShadow = '0 2px 12px rgba(102, 126, 234, 0.3)';
}} }}
onMouseLeave={(e) => { onMouseLeave={(e) => {
e.currentTarget.style.borderColor = '#d9d9d9'; e.currentTarget.style.background = 'rgba(255, 255, 255, 0.15)';
e.currentTarget.style.color = 'rgba(0, 0, 0, 0.88)'; e.currentTarget.style.transform = 'none';
e.currentTarget.style.boxShadow = '0 2px 8px rgba(0, 0, 0, 0.08)';
}} }}
> >
@@ -484,10 +495,12 @@ export default function UserManagement() {
icon={<PlusOutlined />} icon={<PlusOutlined />}
onClick={() => setModalVisible(true)} onClick={() => setModalVisible(true)}
style={{ style={{
borderRadius: 8, borderRadius: 12,
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', background: 'rgba(255, 193, 7, 0.95)',
border: 'none', border: '1px solid rgba(255, 255, 255, 0.3)',
boxShadow: '0 2px 8px rgba(102, 126, 234, 0.4)' boxShadow: '0 4px 16px rgba(255, 193, 7, 0.4)',
color: '#fff',
fontWeight: 600
}} }}
> >
@@ -502,9 +515,11 @@ export default function UserManagement() {
<Card <Card
variant="borderless" variant="borderless"
style={{ style={{
background: 'rgba(255, 255, 255, 0.95)', background: 'rgba(255, 255, 255, 0.7)',
borderRadius: isMobile ? 12 : 16, borderRadius: isMobile ? 16 : 24,
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.1)', border: '1px solid rgba(255, 255, 255, 0.4)',
backdropFilter: 'blur(20px)',
boxShadow: '0 4px 24px rgba(0, 0, 0, 0.04)',
flex: 1, flex: 1,
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
@@ -521,7 +536,7 @@ export default function UserManagement() {
{/* 搜索栏 */} {/* 搜索栏 */}
<div style={{ <div style={{
padding: '16px 24px 0 24px', padding: '16px 24px 0 24px',
borderBottom: '1px solid #f0f0f0', borderBottom: '1px solid rgba(0, 0, 0, 0.03)',
}}> }}>
<Input <Input
placeholder="搜索用户名、显示名称或用户ID" placeholder="搜索用户名、显示名称或用户ID"
@@ -560,8 +575,8 @@ export default function UserManagement() {
{/* 固定分页控件 */} {/* 固定分页控件 */}
<div style={{ <div style={{
padding: '16px 24px 24px 24px', padding: '16px 24px 24px 24px',
borderTop: '1px solid #f0f0f0', borderTop: '1px solid rgba(0, 0, 0, 0.03)',
background: 'rgba(255, 255, 255, 0.95)', background: 'transparent',
display: 'flex', display: 'flex',
justifyContent: 'center', justifyContent: 'center',
}}> }}>
+20 -18
View File
@@ -25,12 +25,13 @@ export default function WorldSetting() {
rules: string; rules: string;
} | null>(null); } | null>(null);
const [isSavingPreview, setIsSavingPreview] = useState(false); const [isSavingPreview, setIsSavingPreview] = useState(false);
const [modal, contextHolder] = Modal.useModal();
// AI重新生成世界观 // AI重新生成世界观
const handleRegenerate = async () => { const handleRegenerate = async () => {
if (!currentProject) return; if (!currentProject) return;
Modal.confirm({ modal.confirm({
title: '确认重新生成', title: '确认重新生成',
content: '确定要使用AI重新生成世界观设定吗?这将替换当前的世界观内容。', content: '确定要使用AI重新生成世界观设定吗?这将替换当前的世界观内容。',
centered: true, centered: true,
@@ -139,11 +140,11 @@ export default function WorldSetting() {
backgroundColor: '#fff', backgroundColor: '#fff',
padding: '16px 0', padding: '16px 0',
marginBottom: 16, marginBottom: 16,
borderBottom: '1px solid #f0f0f0', borderBottom: '1px solid var(--color-border-secondary)',
display: 'flex', display: 'flex',
alignItems: 'center' alignItems: 'center'
}}> }}>
<GlobalOutlined style={{ fontSize: 24, marginRight: 12, color: '#1890ff' }} /> <GlobalOutlined style={{ fontSize: 24, marginRight: 12, color: 'var(--color-primary)' }} />
<h2 style={{ margin: 0 }}></h2> <h2 style={{ margin: 0 }}></h2>
</div> </div>
@@ -164,6 +165,7 @@ export default function WorldSetting() {
return ( return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}> <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
{contextHolder}
{/* 固定头部 */} {/* 固定头部 */}
<div style={{ <div style={{
position: 'sticky', position: 'sticky',
@@ -178,7 +180,7 @@ export default function WorldSetting() {
justifyContent: 'space-between' justifyContent: 'space-between'
}}> }}>
<div style={{ display: 'flex', alignItems: 'center' }}> <div style={{ display: 'flex', alignItems: 'center' }}>
<GlobalOutlined style={{ fontSize: 24, marginRight: 12, color: '#1890ff' }} /> <GlobalOutlined style={{ fontSize: 24, marginRight: 12, color: 'var(--color-primary)' }} />
<h2 style={{ margin: 0 }}></h2> <h2 style={{ margin: 0 }}></h2>
</div> </div>
<Space> <Space>
@@ -249,16 +251,16 @@ export default function WorldSetting() {
<div style={{ padding: '16px 0' }}> <div style={{ padding: '16px 0' }}>
{currentProject.world_time_period && ( {currentProject.world_time_period && (
<div style={{ marginBottom: 24 }}> <div style={{ marginBottom: 24 }}>
<Title level={5} style={{ color: '#1890ff', marginBottom: 12 }}> <Title level={5} style={{ color: 'var(--color-primary)', marginBottom: 12 }}>
</Title> </Title>
<Paragraph style={{ <Paragraph style={{
fontSize: 15, fontSize: 15,
lineHeight: 1.8, lineHeight: 1.8,
padding: 16, padding: 16,
background: '#f5f5f5', background: 'var(--color-bg-layout)',
borderRadius: 8, borderRadius: 8,
borderLeft: '4px solid #1890ff' borderLeft: '4px solid var(--color-primary)'
}}> }}>
{currentProject.world_time_period} {currentProject.world_time_period}
</Paragraph> </Paragraph>
@@ -267,16 +269,16 @@ export default function WorldSetting() {
{currentProject.world_location && ( {currentProject.world_location && (
<div style={{ marginBottom: 24 }}> <div style={{ marginBottom: 24 }}>
<Title level={5} style={{ color: '#52c41a', marginBottom: 12 }}> <Title level={5} style={{ color: 'var(--color-success)', marginBottom: 12 }}>
</Title> </Title>
<Paragraph style={{ <Paragraph style={{
fontSize: 15, fontSize: 15,
lineHeight: 1.8, lineHeight: 1.8,
padding: 16, padding: 16,
background: '#f5f5f5', background: 'var(--color-bg-layout)',
borderRadius: 8, borderRadius: 8,
borderLeft: '4px solid #52c41a' borderLeft: '4px solid var(--color-success)'
}}> }}>
{currentProject.world_location} {currentProject.world_location}
</Paragraph> </Paragraph>
@@ -285,16 +287,16 @@ export default function WorldSetting() {
{currentProject.world_atmosphere && ( {currentProject.world_atmosphere && (
<div style={{ marginBottom: 24 }}> <div style={{ marginBottom: 24 }}>
<Title level={5} style={{ color: '#faad14', marginBottom: 12 }}> <Title level={5} style={{ color: 'var(--color-warning)', marginBottom: 12 }}>
</Title> </Title>
<Paragraph style={{ <Paragraph style={{
fontSize: 15, fontSize: 15,
lineHeight: 1.8, lineHeight: 1.8,
padding: 16, padding: 16,
background: '#f5f5f5', background: 'var(--color-bg-layout)',
borderRadius: 8, borderRadius: 8,
borderLeft: '4px solid #faad14' borderLeft: '4px solid var(--color-warning)'
}}> }}>
{currentProject.world_atmosphere} {currentProject.world_atmosphere}
</Paragraph> </Paragraph>
@@ -303,16 +305,16 @@ export default function WorldSetting() {
{currentProject.world_rules && ( {currentProject.world_rules && (
<div style={{ marginBottom: 0 }}> <div style={{ marginBottom: 0 }}>
<Title level={5} style={{ color: '#f5222d', marginBottom: 12 }}> <Title level={5} style={{ color: 'var(--color-error)', marginBottom: 12 }}>
</Title> </Title>
<Paragraph style={{ <Paragraph style={{
fontSize: 15, fontSize: 15,
lineHeight: 1.8, lineHeight: 1.8,
padding: 16, padding: 16,
background: '#f5f5f5', background: 'var(--color-bg-layout)',
borderRadius: 8, borderRadius: 8,
borderLeft: '4px solid #f5222d' borderLeft: '4px solid var(--color-error)'
}}> }}>
{currentProject.world_rules} {currentProject.world_rules}
</Paragraph> </Paragraph>
@@ -440,14 +442,14 @@ export default function WorldSetting() {
> >
{newWorldData && ( {newWorldData && (
<div style={{ maxHeight: '60vh', overflowY: 'auto' }}> <div style={{ maxHeight: '60vh', overflowY: 'auto' }}>
<div style={{ marginBottom: 24, padding: 16, background: '#fff7e6', border: '1px solid #ffd591', borderRadius: 8 }}> <div style={{ marginBottom: 24, padding: 16, background: 'var(--color-warning-bg)', border: '1px solid var(--color-warning-border)', borderRadius: 8 }}>
<Typography.Text type="warning" strong> <Typography.Text type="warning" strong>
"确认替换" "确认替换"
</Typography.Text> </Typography.Text>
</div> </div>
<div style={{ marginBottom: 24 }}> <div style={{ marginBottom: 24 }}>
<Title level={5} style={{ color: '#1890ff', marginBottom: 12 }}> <Title level={5} style={{ color: 'var(--color-primary)', marginBottom: 12 }}>
</Title> </Title>
<Paragraph style={{ <Paragraph style={{