style: 优化书本封面显示样式

This commit is contained in:
xiamuceer
2026-03-16 16:50:15 +08:00
parent 17ecf34565
commit 577b48285b
+450 -201
View File
@@ -60,8 +60,8 @@ export default function BookshelfPage({
const alphaColor = (color: string, alpha: number) => `color-mix(in srgb, ${color} ${(alpha * 100).toFixed(0)}%, transparent)`; const alphaColor = (color: string, alpha: number) => `color-mix(in srgb, ${color} ${(alpha * 100).toFixed(0)}%, transparent)`;
const [flippedProjectIds, setFlippedProjectIds] = useState<Record<string, boolean>>({}); const [flippedProjectIds, setFlippedProjectIds] = useState<Record<string, boolean>>({});
const [coverGeneratingIds, setCoverGeneratingIds] = useState<Record<string, boolean>>({}); const [coverGeneratingIds, setCoverGeneratingIds] = useState<Record<string, boolean>>({});
const mobileBookHeight = 460; const mobileBookHeight = 520;
const desktopBookHeight = 430; const desktopBookHeight = 520;
const mobileSpineWidth = 32; const mobileSpineWidth = 32;
const serialBookPalettes = [ const serialBookPalettes = [
@@ -365,13 +365,14 @@ export default function BookshelfPage({
const coverReady = project.cover_status === 'ready' && !!project.cover_image_url; const coverReady = project.cover_status === 'ready' && !!project.cover_image_url;
const coverGenerating = project.cover_status === 'generating' || coverActionLoading; const coverGenerating = project.cover_status === 'generating' || coverActionLoading;
const coverFailed = project.cover_status === 'failed' && !coverActionLoading; const coverFailed = project.cover_status === 'failed' && !coverActionLoading;
const showCoverFace = coverReady && !isFlipped;
return ( return (
<div key={project.id} style={{ position: 'relative', width: '100%', minWidth: 0, minHeight: isMobile ? mobileBookHeight : desktopBookHeight }}> <div key={project.id} style={{ position: 'relative', width: '100%', minWidth: 0, height: isMobile ? mobileBookHeight : desktopBookHeight }}>
<Card <Card
hoverable hoverable
style={{ ...bookshelfCardStyles.projectCard, minHeight: isMobile ? mobileBookHeight : desktopBookHeight }} style={{ ...bookshelfCardStyles.projectCard, height: isMobile ? mobileBookHeight : desktopBookHeight }}
styles={{ body: { padding: 0, flex: 1, display: 'flex', flexDirection: 'column' } }} styles={{ body: { padding: 0, height: '100%', flex: 1, display: 'flex', flexDirection: 'column' } }}
{...bookshelfCardHoverHandlers} {...bookshelfCardHoverHandlers}
onClick={() => !isFlipped && onEnterProject(project)} onClick={() => !isFlipped && onEnterProject(project)}
data-card-style="bookshelf-book" data-card-style="bookshelf-book"
@@ -414,110 +415,89 @@ export default function BookshelfPage({
zIndex: 2, zIndex: 2,
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
minHeight: isMobile ? mobileBookHeight : desktopBookHeight, flex: 1,
padding: isMobile ? '18px 16px 14px 38px' : '26px 24px 18px 42px', height: '100%',
padding: isMobile ? '16px 16px 14px 38px' : '20px 20px 18px 42px',
overflow: 'hidden',
width: '100%',
boxSizing: 'border-box',
}}> }}>
<div style={{ display: 'flex', justifyContent: 'flex-start', marginBottom: 8 }}> {showCoverFace && (
<>
<div style={{
position: 'absolute',
inset: 0,
backgroundImage: `url(${project.cover_image_url})`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
}} />
<div style={{
position: 'absolute',
inset: 0,
background: isDark
? 'linear-gradient(180deg, rgba(7,10,18,0.18) 0%, rgba(7,10,18,0.38) 24%, rgba(7,10,18,0.72) 62%, rgba(7,10,18,0.88) 100%)'
: 'linear-gradient(180deg, rgba(255,255,255,0.08) 0%, rgba(16,24,40,0.22) 24%, rgba(16,24,40,0.62) 62%, rgba(16,24,40,0.82) 100%)',
}} />
</>
)}
<div style={{ display: 'flex', justifyContent: 'flex-start', marginBottom: 8, position: 'relative', zIndex: 2 }}>
<Button <Button
type="text" type="text"
size="small" size="small"
icon={isFlipped ? <BookOutlined /> : <SwapOutlined />} icon={showCoverFace ? <BookOutlined /> : <SwapOutlined />}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
toggleProjectFace(project.id); toggleProjectFace(project.id);
}} }}
style={showCoverFace ? {
color: token.colorWhite,
borderRadius: 999,
background: 'rgba(255,255,255,0.18)',
backdropFilter: 'blur(10px)',
border: '1px solid rgba(255,255,255,0.22)',
} : undefined}
> >
{isFlipped ? '返回书本' : '查看封面'} {showCoverFace ? '切换回详情' : (coverReady ? '查看封面' : '封面操作')}
</Button> </Button>
</div> </div>
{isFlipped ? (
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 12, justifyContent: 'center' }}>
{coverReady ? (
<>
<div style={{ flex: 1, minHeight: 240, borderRadius: 12, overflow: 'hidden', background: alphaColor(token.colorText, 0.06), border: `1px solid ${alphaColor(token.colorText, 0.08)}` }}>
<img src={project.cover_image_url} alt={`${project.title} cover`} style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
</div>
<Space wrap>
<Button icon={<DownloadOutlined />} onClick={(e) => { e.stopPropagation(); onDownloadCover(project); }} disabled={coverActionLoading}></Button>
<Button
icon={<ReloadOutlined />}
loading={coverActionLoading}
onClick={(e) => void handleGenerateCoverClick(e, project, true)}
>
{coverActionLoading ? '重新生成中...' : '重新生成'}
</Button>
</Space>
</>
) : coverGenerating ? (
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 12 }}>
<LoadingOutlined spin style={{ fontSize: 28, color: token.colorPrimary }} />
<div style={{ color: token.colorTextSecondary }}>...</div>
</div>
) : (
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 12, textAlign: 'center' }}>
<PictureOutlined style={{ fontSize: 36, color: token.colorTextTertiary }} />
{coverFailed ? (
<>
<div style={{ color: token.colorError }}></div>
<div style={{ color: token.colorTextSecondary, fontSize: 12 }}>{project.cover_error || '请稍后重试'}</div>
<Button
type="primary"
icon={<ReloadOutlined />}
loading={coverActionLoading}
onClick={(e) => void handleGenerateCoverClick(e, project, true)}
>
{coverActionLoading ? '重新生成中...' : '重新生成'}
</Button>
</>
) : (
<Button
type="primary"
icon={<PictureOutlined />}
loading={coverActionLoading}
onClick={(e) => void handleGenerateCoverClick(e, project, true)}
>
{coverActionLoading ? '生成中...' : '生成封面'}
</Button>
)}
</div>
)}
</div>
) : (
<>
<div style={{ marginBottom: isMobile ? 10 : 12, paddingRight: isMobile ? 18 : 30 }}>
<div style={{
display: 'flex',
alignItems: 'flex-start',
gap: 8,
marginBottom: isMobile ? 8 : 10,
minHeight: isMobile ? 50 : 58,
}}>
<BookOutlined style={{
fontSize: isMobile ? 14 : 16,
color: alphaColor(token.colorText, 0.4),
marginTop: 2,
flexShrink: 0,
}} />
<div style={{
fontSize: isMobile ? 18 : 22,
fontWeight: 700,
color: token.colorText,
lineHeight: 1.3,
display: '-webkit-box',
WebkitLineClamp: 2,
WebkitBoxOrient: 'vertical',
overflow: 'hidden',
wordBreak: 'break-word',
fontFamily: 'Georgia, "Times New Roman", "Noto Serif SC", serif',
}}>
{project.title}
</div>
</div>
{showCoverFace ? (
<div style={{
position: 'relative',
zIndex: 2,
flex: 1,
display: 'flex',
flexDirection: 'column',
minHeight: 0,
width: '100%',
}}>
<div style={{
minHeight: isMobile ? 82 : 90,
width: '100%',
marginBottom: isMobile ? 10 : 12,
}}>
<div style={{
fontSize: isMobile ? 18 : 22,
fontWeight: 700,
color: token.colorWhite,
lineHeight: 1.3,
display: '-webkit-box',
WebkitLineClamp: 2,
WebkitBoxOrient: 'vertical',
overflow: 'hidden',
wordBreak: 'break-word',
fontFamily: 'Georgia, "Times New Roman", "Noto Serif SC", serif',
textShadow: '0 2px 12px rgba(0,0,0,0.35)',
}}>
{project.title}
</div>
<div style={{ <div style={{
display: 'flex', display: 'flex',
flexWrap: 'wrap', flexWrap: 'wrap',
gap: 6, gap: 6,
marginTop: 10,
minHeight: isMobile ? 20 : 22, minHeight: isMobile ? 20 : 22,
alignItems: 'flex-start', alignItems: 'flex-start',
}}> }}>
@@ -525,13 +505,14 @@ export default function BookshelfPage({
<Tag key={idx} style={{ <Tag key={idx} style={{
margin: 0, margin: 0,
padding: isMobile ? '0 7px' : '0 8px', padding: isMobile ? '0 7px' : '0 8px',
borderRadius: 4, borderRadius: 999,
border: `1px solid ${alphaColor(token.colorSuccess, 0.18)}`, border: '1px solid rgba(255,255,255,0.24)',
background: alphaColor(token.colorSuccess, 0.08), background: 'rgba(255,255,255,0.14)',
color: token.colorSuccess, color: token.colorWhite,
fontSize: isMobile ? 10 : 11, fontSize: isMobile ? 10 : 11,
lineHeight: isMobile ? '18px' : '20px', lineHeight: isMobile ? '18px' : '20px',
fontWeight: 500, fontWeight: 500,
backdropFilter: 'blur(8px)',
}}> }}>
{tag} {tag}
</Tag> </Tag>
@@ -539,13 +520,14 @@ export default function BookshelfPage({
<Tag style={{ <Tag style={{
margin: 0, margin: 0,
padding: isMobile ? '0 7px' : '0 8px', padding: isMobile ? '0 7px' : '0 8px',
borderRadius: 4, borderRadius: 999,
border: `1px solid ${alphaColor(token.colorSuccess, 0.18)}`, border: '1px solid rgba(255,255,255,0.24)',
background: alphaColor(token.colorSuccess, 0.08), background: 'rgba(255,255,255,0.14)',
color: token.colorSuccess, color: token.colorWhite,
fontSize: isMobile ? 10 : 11, fontSize: isMobile ? 10 : 11,
lineHeight: isMobile ? '18px' : '20px', lineHeight: isMobile ? '18px' : '20px',
fontWeight: 500, fontWeight: 500,
backdropFilter: 'blur(8px)',
}}> }}>
</Tag> </Tag>
@@ -553,130 +535,397 @@ export default function BookshelfPage({
</div> </div>
</div> </div>
<Paragraph <div style={{ flex: 1, minHeight: isMobile ? 54 : 72 }} />
ellipsis={{ rows: isMobile ? 3 : 3 }}
style={{ <div style={{
fontSize: isMobile ? 12 : 13, display: 'flex',
color: token.colorTextSecondary, flexDirection: 'column',
marginBottom: isMobile ? 12 : 16, gap: 10,
lineHeight: 1.7, width: '100%',
flexGrow: 1, }}>
}} <div style={{
> padding: isMobile ? '12px 10px' : '14px 12px',
{project.description || '暂无描述...'} borderRadius: 10,
</Paragraph> background: 'rgba(10,18,32,0.34)',
border: '1px solid rgba(255,255,255,0.16)',
boxShadow: '0 8px 24px rgba(0,0,0,0.18)',
backdropFilter: 'blur(12px)',
minHeight: isMobile ? 74 : 82,
}}>
<div style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 6,
fontSize: isMobile ? 11 : 12,
color: 'rgba(255,255,255,0.82)',
}}>
<span></span>
<span style={{ color: token.colorWhite, fontWeight: 700 }}>{progress}%</span>
</div>
<div style={{
height: 6,
width: '100%',
borderRadius: 999,
overflow: 'hidden',
background: 'rgba(255,255,255,0.18)',
marginBottom: 12,
}}>
<div style={{
width: `${progress}%`,
height: '100%',
borderRadius: 999,
background: progressColor,
transition: 'width 0.3s ease',
}} />
</div>
<div style={{ display: 'flex', alignItems: 'stretch', textAlign: 'center' }}>
<div style={{ flex: 1 }}>
<div style={{
fontSize: isMobile ? 22 : 26,
fontWeight: 700,
color: token.colorWhite,
lineHeight: 1.1,
fontFamily: 'Georgia, "Times New Roman", serif',
}}>
{formatWordCount(project.current_words || 0)}
</div>
<div style={{
fontSize: isMobile ? 10 : 11,
color: 'rgba(255,255,255,0.72)',
marginTop: 4,
}}>
</div>
</div>
<div style={{
width: 1,
margin: '0 12px',
background: 'rgba(255,255,255,0.16)',
}} />
<div style={{ flex: 1 }}>
<div style={{
fontSize: isMobile ? 22 : 26,
fontWeight: 700,
color: progress >= 100 ? '#7CFFB2' : token.colorWhite,
lineHeight: 1.1,
fontFamily: 'Georgia, "Times New Roman", serif',
}}>
{formatWordCount(project.target_words || 0)}
</div>
<div style={{
fontSize: isMobile ? 10 : 11,
color: 'rgba(255,255,255,0.72)',
marginTop: 4,
}}>
</div>
</div>
</div>
</div>
<div style={{ marginBottom: isMobile ? 14 : 18 }}>
<div style={{ <div style={{
display: 'flex', display: 'flex',
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center', alignItems: 'center',
marginBottom: 6, gap: 10,
fontSize: isMobile ? 11 : 12, flexWrap: 'wrap',
paddingTop: isMobile ? 10 : 12,
borderTop: '1px solid rgba(255,255,255,0.18)',
minHeight: isMobile ? 42 : 46,
}}> }}>
<span style={{ color: token.colorTextTertiary }}></span> <Space size={4} style={{ fontSize: isMobile ? 11 : 12, color: 'rgba(255,255,255,0.76)' }}>
<span style={{ color: progressColor, fontWeight: 700 }}>{progress}%</span> <CalendarOutlined style={{ fontSize: isMobile ? 10 : 12 }} />
</div> {formatDate(project.updated_at)}
<div style={{ </Space>
height: 6,
width: '100%', <Space wrap>
borderRadius: 999, <Button
overflow: 'hidden', size="small"
background: alphaColor(token.colorText, 0.06), icon={<DownloadOutlined />}
}}> onClick={(e) => { e.stopPropagation(); onDownloadCover(project); }}
<div style={{ disabled={coverActionLoading}
width: `${progress}%`, style={{
height: '100%', color: token.colorWhite,
borderRadius: 999, background: 'rgba(255,255,255,0.14)',
background: progressColor, borderColor: 'rgba(255,255,255,0.22)',
transition: 'width 0.3s ease', backdropFilter: 'blur(8px)',
}} /> }}
>
</Button>
<Button
size="small"
icon={<ReloadOutlined />}
loading={coverActionLoading}
onClick={(e) => void handleGenerateCoverClick(e, project, true)}
style={{
color: token.colorWhite,
background: 'rgba(255,255,255,0.14)',
borderColor: 'rgba(255,255,255,0.22)',
backdropFilter: 'blur(8px)',
}}
>
{coverActionLoading ? '重新生成中...' : '重新生成'}
</Button>
</Space>
</div> </div>
</div> </div>
</div>
<div style={{ ) : (
marginBottom: isMobile ? 12 : 16, <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 12, justifyContent: 'space-between', minHeight: 0, width: '100%' }}>
padding: isMobile ? '12px 10px' : '14px 12px', {isFlipped && coverGenerating ? (
background: `linear-gradient(180deg, ${alphaColor(token.colorBgContainer, 0.94)} 0%, ${alphaColor(token.colorFillSecondary, 0.78)} 100%)`, <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 12, minHeight: 0, width: '100%' }}>
borderRadius: 10, <LoadingOutlined spin style={{ fontSize: 28, color: token.colorPrimary }} />
border: `1px solid ${alphaColor(token.colorText, 0.06)}`, <div style={{ color: token.colorTextSecondary }}>...</div>
boxShadow: `inset 0 1px 2px ${alphaColor(token.colorText, 0.08)}`, </div>
}}> ) : isFlipped && coverFailed ? (
<div style={{ display: 'flex', alignItems: 'stretch', textAlign: 'center' }}> <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 12, textAlign: 'center', minHeight: 0 }}>
<div style={{ flex: 1 }}> <PictureOutlined style={{ fontSize: 36, color: token.colorTextTertiary }} />
<div style={{ color: token.colorError }}></div>
<div style={{ color: token.colorTextSecondary, fontSize: 12 }}>{project.cover_error || '请稍后重试'}</div>
<Button
type="primary"
icon={<ReloadOutlined />}
loading={coverActionLoading}
onClick={(e) => void handleGenerateCoverClick(e, project, true)}
>
{coverActionLoading ? '重新生成中...' : '重新生成'}
</Button>
</div>
) : isFlipped && !coverReady ? (
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 12, textAlign: 'center', minHeight: 0 }}>
<PictureOutlined style={{ fontSize: 36, color: token.colorTextTertiary }} />
<div style={{ color: token.colorTextSecondary }}></div>
<Button
type="primary"
icon={<PictureOutlined />}
loading={coverActionLoading}
onClick={(e) => void handleGenerateCoverClick(e, project, true)}
>
{coverActionLoading ? '生成中...' : '生成封面'}
</Button>
</div>
) : (
<>
<div style={{ marginBottom: isMobile ? 10 : 12, paddingRight: isMobile ? 18 : 30 }}>
<div style={{ <div style={{
fontSize: isMobile ? 22 : 26, display: 'flex',
fontWeight: 700, alignItems: 'flex-start',
color: token.colorText, gap: 8,
lineHeight: 1.1, marginBottom: isMobile ? 8 : 10,
fontFamily: 'Georgia, "Times New Roman", serif', minHeight: isMobile ? 50 : 58,
}}> }}>
{formatWordCount(project.current_words || 0)} <BookOutlined style={{
fontSize: isMobile ? 14 : 16,
color: alphaColor(token.colorText, 0.4),
marginTop: 2,
flexShrink: 0,
}} />
<div style={{
fontSize: isMobile ? 18 : 22,
fontWeight: 700,
color: token.colorText,
lineHeight: 1.3,
display: '-webkit-box',
WebkitLineClamp: 2,
WebkitBoxOrient: 'vertical',
overflow: 'hidden',
wordBreak: 'break-word',
fontFamily: 'Georgia, "Times New Roman", "Noto Serif SC", serif',
}}>
{project.title}
</div>
</div> </div>
<div style={{ <div style={{
fontSize: isMobile ? 10 : 11, display: 'flex',
color: token.colorTextTertiary, flexWrap: 'wrap',
marginTop: 4, gap: 6,
minHeight: isMobile ? 20 : 22,
alignItems: 'flex-start',
}}> }}>
{tags.length > 0 ? tags.slice(0, 3).map((tag: string, idx: number) => (
<Tag key={idx} style={{
margin: 0,
padding: isMobile ? '0 7px' : '0 8px',
borderRadius: 4,
border: `1px solid ${alphaColor(token.colorSuccess, 0.18)}`,
background: alphaColor(token.colorSuccess, 0.08),
color: token.colorSuccess,
fontSize: isMobile ? 10 : 11,
lineHeight: isMobile ? '18px' : '20px',
fontWeight: 500,
}}>
{tag}
</Tag>
)) : (
<Tag style={{
margin: 0,
padding: isMobile ? '0 7px' : '0 8px',
borderRadius: 4,
border: `1px solid ${alphaColor(token.colorSuccess, 0.18)}`,
background: alphaColor(token.colorSuccess, 0.08),
color: token.colorSuccess,
fontSize: isMobile ? 10 : 11,
lineHeight: isMobile ? '18px' : '20px',
fontWeight: 500,
}}>
</Tag>
)}
</div> </div>
</div> </div>
<div style={{
width: 1, <Paragraph
margin: '0 12px', ellipsis={{ rows: isMobile ? 3 : 3 }}
background: alphaColor(token.colorText, 0.1), style={{
}} /> fontSize: isMobile ? 12 : 13,
<div style={{ flex: 1 }}> color: token.colorTextSecondary,
marginBottom: isMobile ? 12 : 16,
lineHeight: 1.7,
flexGrow: 1,
}}
>
{project.description || '暂无描述...'}
</Paragraph>
<div style={{ marginBottom: isMobile ? 14 : 18 }}>
<div style={{ <div style={{
fontSize: isMobile ? 22 : 26, display: 'flex',
fontWeight: 700, justifyContent: 'space-between',
color: progress >= 100 ? token.colorSuccess : progressColor, alignItems: 'center',
lineHeight: 1.1, marginBottom: 6,
fontFamily: 'Georgia, "Times New Roman", serif', fontSize: isMobile ? 11 : 12,
}}> }}>
{formatWordCount(project.target_words || 0)} <span style={{ color: token.colorTextTertiary }}></span>
<span style={{ color: progressColor, fontWeight: 700 }}>{progress}%</span>
</div> </div>
<div style={{ <div style={{
fontSize: isMobile ? 10 : 11, height: 6,
color: token.colorTextTertiary, width: '100%',
marginTop: 4, borderRadius: 999,
overflow: 'hidden',
background: alphaColor(token.colorText, 0.06),
}}> }}>
<div style={{
width: `${progress}%`,
height: '100%',
borderRadius: 999,
background: progressColor,
transition: 'width 0.3s ease',
}} />
</div> </div>
</div> </div>
</div>
</div>
<div style={{ <div style={{
display: 'flex', marginBottom: isMobile ? 12 : 16,
justifyContent: 'space-between', padding: isMobile ? '12px 10px' : '14px 12px',
alignItems: 'center', background: `linear-gradient(180deg, ${alphaColor(token.colorBgContainer, 0.94)} 0%, ${alphaColor(token.colorFillSecondary, 0.78)} 100%)`,
paddingTop: isMobile ? 10 : 12, borderRadius: 10,
borderTop: `1px solid ${alphaColor(token.colorText, 0.06)}`, border: `1px solid ${alphaColor(token.colorText, 0.06)}`,
color: token.colorTextTertiary, boxShadow: `inset 0 1px 2px ${alphaColor(token.colorText, 0.08)}`,
marginTop: 'auto', }}>
}}> <div style={{ display: 'flex', alignItems: 'stretch', textAlign: 'center' }}>
<Space size={4} style={{ fontSize: isMobile ? 11 : 12, color: token.colorTextTertiary }}> <div style={{ flex: 1 }}>
<CalendarOutlined style={{ fontSize: isMobile ? 10 : 12 }} /> <div style={{
{formatDate(project.updated_at)} fontSize: isMobile ? 22 : 26,
</Space> fontWeight: 700,
color: token.colorText,
lineHeight: 1.1,
fontFamily: 'Georgia, "Times New Roman", serif',
}}>
{formatWordCount(project.current_words || 0)}
</div>
<div style={{
fontSize: isMobile ? 10 : 11,
color: token.colorTextTertiary,
marginTop: 4,
}}>
</div>
</div>
<div style={{
width: 1,
margin: '0 12px',
background: alphaColor(token.colorText, 0.1),
}} />
<div style={{ flex: 1 }}>
<div style={{
fontSize: isMobile ? 22 : 26,
fontWeight: 700,
color: progress >= 100 ? token.colorSuccess : progressColor,
lineHeight: 1.1,
fontFamily: 'Georgia, "Times New Roman", serif',
}}>
{formatWordCount(project.target_words || 0)}
</div>
<div style={{
fontSize: isMobile ? 10 : 11,
color: token.colorTextTertiary,
marginTop: 4,
}}>
</div>
</div>
</div>
</div>
<Button <div style={{
type="text" display: 'grid',
size="small" gridTemplateColumns: '1fr auto 1fr',
danger alignItems: 'center',
icon={<DeleteOutlined style={{ fontSize: isMobile ? 12 : 14 }} />} gap: 10,
onClick={(e) => { paddingTop: isMobile ? 10 : 12,
e.stopPropagation(); borderTop: `1px solid ${alphaColor(token.colorText, 0.06)}`,
onDeleteProject(project.id); color: token.colorTextTertiary,
}} marginTop: 'auto',
style={{ }}>
padding: isMobile ? '2px 4px' : '4px 8px', <Space size={4} style={{ fontSize: isMobile ? 11 : 12, color: token.colorTextTertiary, justifySelf: 'start' }}>
borderRadius: 8, <CalendarOutlined style={{ fontSize: isMobile ? 10 : 12 }} />
}} {formatDate(project.updated_at)}
/> </Space>
</div>
</> <div style={{ justifySelf: 'center' }}>
{!coverReady && (
<Button
size="small"
icon={<PictureOutlined />}
loading={coverActionLoading}
onClick={(e) => void handleGenerateCoverClick(e, project, true)}
style={{
color: token.colorWhite,
background: 'rgba(255,255,255,0.14)',
borderColor: 'rgba(255,255,255,0.22)',
backdropFilter: 'blur(8px)',
minWidth: isMobile ? 112 : 124,
}}
>
{coverActionLoading ? '生成中...' : '生成封面'}
</Button>
)}
</div>
<div style={{ justifySelf: 'end' }}>
<Button
type="text"
size="small"
danger
icon={<DeleteOutlined style={{ fontSize: isMobile ? 12 : 14 }} />}
onClick={(e) => {
e.stopPropagation();
onDeleteProject(project.id);
}}
style={{
padding: isMobile ? '2px 4px' : '4px 8px',
borderRadius: 8,
}}
/>
</div>
</div>
</>
)}
</div>
)} )}
</div> </div>
</Card> </Card>