将答题界面的导航和进度条整合并固定在顶部

主要更改:
- 整合导航和进度条:将两者放在同一个卡片中,避免重复的卡片边框,提升美观性
- 固定顶栏:顶部导航和进度条固定在页面顶部,滚动时始终可见
- 移动端优化:调整返回按钮位置和布局顺序,优化小屏幕显示效果
- 进度条组件简化:移除 Card 包裹,直接渲染 Progress 组件
- 响应式布局:为固定顶栏预留合适的空间,移动端、平板、PC端各自适配

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
yanlongqi 2025-11-05 11:23:06 +08:00
parent 3c97985af0
commit 18b92e5452
4 changed files with 133 additions and 80 deletions

View File

@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import { Progress, Card } from 'antd' import { Progress } from 'antd'
interface QuestionProgressProps { interface QuestionProgressProps {
currentIndex: number currentIndex: number
@ -17,34 +17,21 @@ const QuestionProgress: React.FC<QuestionProgressProps> = ({
const percent = Math.round(((currentIndex + 1) / totalQuestions) * 100) const percent = Math.round(((currentIndex + 1) / totalQuestions) * 100)
return ( return (
<Card <Progress
style={{ percent={percent}
marginBottom: 20, status="active"
borderRadius: 16, strokeColor={{
background: 'rgba(255, 255, 255, 0.9)', '0%': '#007aff',
backdropFilter: 'blur(30px) saturate(180%)', '100%': '#52c41a',
WebkitBackdropFilter: 'blur(30px) saturate(180%)',
boxShadow: '0 2px 12px rgba(0, 0, 0, 0.05), 0 1px 4px rgba(0, 0, 0, 0.03), 0 0 0 1px rgba(0, 0, 0, 0.03)',
border: '0.5px solid rgba(0, 0, 0, 0.04)',
}} }}
bodyStyle={{ padding: '16px 20px' }} trailColor="rgba(0, 0, 0, 0.06)"
> strokeWidth={12}
<Progress format={() => `${currentIndex + 1} / ${totalQuestions}`}
percent={percent} style={{
status="active" fontSize: '14px',
strokeColor={{ fontWeight: 600,
'0%': '#007aff', }}
'100%': '#52c41a', />
}}
trailColor="rgba(0, 0, 0, 0.06)"
strokeWidth={12}
format={() => `${currentIndex + 1} / ${totalQuestions}`}
style={{
fontSize: '14px',
fontWeight: 600,
}}
/>
</Card>
) )
} }

View File

@ -194,11 +194,14 @@ const Login: React.FC = () => {
<Form.Item <Form.Item
name="nickname" name="nickname"
rules={[{ max: 20, message: '昵称最多20个字符' }]} rules={[
{ required: true, message: '请输入姓名' },
{ max: 20, message: '姓名最多20个字符' }
]}
> >
<Input <Input
prefix={<IdcardOutlined />} prefix={<IdcardOutlined />}
placeholder="请输入昵称(可选)" placeholder="请输入姓名"
/> />
</Form.Item> </Form.Item>

View File

@ -1,20 +1,31 @@
.container { .container {
min-height: 100vh; min-height: 100vh;
background: #fafafa; background: #fafafa;
padding: 20px; padding: 0;
padding-bottom: 80px; padding-bottom: 80px;
} }
.content { // 固定顶栏
max-width: 900px; .fixedTopBar {
margin: 0 auto; position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
background: rgba(250, 250, 250, 0.85);
backdrop-filter: blur(40px) saturate(180%);
-webkit-backdrop-filter: blur(40px) saturate(180%);
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06);
border-bottom: 0.5px solid rgba(0, 0, 0, 0.04);
} }
.header { .topBarContent {
display: flex; max-width: 900px;
justify-content: space-between; margin: 0 auto;
align-items: center; padding: 16px 20px;
margin-bottom: 20px; }
.topBarCard {
background: rgba(255, 255, 255, 0.9); background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(30px) saturate(180%); backdrop-filter: blur(30px) saturate(180%);
-webkit-backdrop-filter: blur(30px) saturate(180%); -webkit-backdrop-filter: blur(30px) saturate(180%);
@ -25,6 +36,20 @@
0 1px 4px rgba(0, 0, 0, 0.03), 0 1px 4px rgba(0, 0, 0, 0.03),
0 0 0 1px rgba(0, 0, 0, 0.03); 0 0 0 1px rgba(0, 0, 0, 0.03);
border: 0.5px solid rgba(0, 0, 0, 0.04); border: 0.5px solid rgba(0, 0, 0, 0.04);
}
.content {
max-width: 900px;
margin: 0 auto;
padding: 0 20px;
padding-top: 200px; // 为固定顶栏留出空间
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
.backButton { .backButton {
color: #007aff; color: #007aff;
@ -74,6 +99,10 @@
} }
} }
.progressWrapper {
// 进度条容器
}
.questionCard { .questionCard {
border-radius: 20px; border-radius: 20px;
background: rgba(255, 255, 255, 0.95); background: rgba(255, 255, 255, 0.95);
@ -121,19 +150,33 @@
// 响应式设计 - 移动端 // 响应式设计 - 移动端
@media (max-width: 768px) { @media (max-width: 768px) {
.container { .container {
padding: 12px;
padding-bottom: 70px; padding-bottom: 70px;
} }
.topBarContent {
padding: 12px;
}
.topBarCard {
padding: 12px 16px;
border-radius: 12px;
}
.content {
padding: 0 12px;
padding-top: 240px; // 移动端顶栏更高
}
.header { .header {
flex-wrap: wrap; flex-wrap: wrap;
gap: 12px; gap: 10px;
padding: 12px 16px; margin-bottom: 12px;
border-radius: 12px;
.backButton { .backButton {
order: 1; order: 1;
flex: 0 0 auto; flex: 0 0 auto;
font-size: 14px;
padding: 4px 8px;
} }
.title { .title {
@ -141,18 +184,21 @@
flex: 1 1 100%; flex: 1 1 100%;
text-align: center; text-align: center;
font-size: 16px !important; font-size: 16px !important;
margin-top: 4px !important;
} }
.statsGroup { .statsGroup {
order: 3; order: 3;
flex: 1 1 100%; flex: 1 1 100%;
justify-content: center; justify-content: center;
gap: 24px; gap: 32px;
margin-top: 4px;
} }
.statItem { .statItem {
flex-direction: row; flex-direction: row;
gap: 6px; gap: 6px;
align-items: center;
} }
.statLabel { .statLabel {
@ -183,8 +229,13 @@
// 响应式设计 - 平板 // 响应式设计 - 平板
@media (min-width: 769px) and (max-width: 1024px) { @media (min-width: 769px) and (max-width: 1024px) {
.container { .topBarContent {
padding: 24px; padding: 14px 24px;
}
.content {
padding: 0 24px;
padding-top: 190px;
} }
.header { .header {
@ -196,14 +247,16 @@
// 响应式设计 - PC端 // 响应式设计 - PC端
@media (min-width: 1025px) { @media (min-width: 1025px) {
.container { .topBarContent {
padding: 32px; padding: 18px 32px;
}
.content {
padding: 0 32px;
padding-top: 190px;
} }
.header { .header {
margin-bottom: 24px;
padding: 18px 24px;
.title { .title {
font-size: 22px !important; font-size: 22px !important;
} }

View File

@ -250,40 +250,50 @@ const QuestionPage: React.FC = () => {
return ( return (
<div className={styles.container}> <div className={styles.container}>
<div className={styles.content}> {/* 固定顶栏:包含导航和进度 */}
{/* 头部 */} <div className={styles.fixedTopBar}>
<div className={styles.header}> <div className={styles.topBarContent}>
<Button <div className={styles.topBarCard}>
icon={<ArrowLeftOutlined />} {/* 头部导航 */}
onClick={() => navigate("/")} <div className={styles.header}>
className={styles.backButton} <Button
type="text" icon={<ArrowLeftOutlined />}
> onClick={() => navigate("/")}
className={styles.backButton}
</Button> type="text"
<Title level={3} className={styles.title}> >
AnKao
</Title> </Button>
<div className={styles.statsGroup}> <Title level={3} className={styles.title}>
<div className={styles.statItem}> AnKao
<span className={styles.statLabel}></span> </Title>
<span className={styles.statValue} style={{ color: '#52c41a' }}>{correctCount}</span> <div className={styles.statsGroup}>
<div className={styles.statItem}>
<span className={styles.statLabel}></span>
<span className={styles.statValue} style={{ color: '#52c41a' }}>{correctCount}</span>
</div>
<div className={styles.statItem}>
<span className={styles.statLabel}></span>
<span className={styles.statValue} style={{ color: '#ff4d4f' }}>{wrongCount}</span>
</div>
</div>
</div> </div>
<div className={styles.statItem}>
<span className={styles.statLabel}></span> {/* 进度条 */}
<span className={styles.statValue} style={{ color: '#ff4d4f' }}>{wrongCount}</span> <div className={styles.progressWrapper}>
<QuestionProgress
currentIndex={currentIndex}
totalQuestions={allQuestions.length}
correctCount={correctCount}
wrongCount={wrongCount}
/>
</div> </div>
</div> </div>
</div> </div>
</div>
{/* 进度条 */} {/* 主内容区 */}
<QuestionProgress <div className={styles.content}>
currentIndex={currentIndex}
totalQuestions={allQuestions.length}
correctCount={correctCount}
wrongCount={wrongCount}
/>
{/* 题目卡片 */} {/* 题目卡片 */}
{currentQuestion && ( {currentQuestion && (
<QuestionCard <QuestionCard