diff --git a/web/src/App.tsx b/web/src/App.tsx index 3924fdc..55c579a 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -3,7 +3,6 @@ import { BrowserRouter as Router, Routes, Route } from 'react-router-dom' import TabBarLayout from './components/TabBarLayout' import ProtectedRoute from './components/ProtectedRoute' import QuestionPage from './pages/Question' -import Profile from './pages/Profile' import Login from './pages/Login' import Home from './pages/Home' import About from './pages/About' @@ -17,7 +16,6 @@ const App: React.FC = () => { }> } /> } /> - } /> {/* 不带TabBar的页面,但需要登录保护 */} diff --git a/web/src/components/TabBarLayout.tsx b/web/src/components/TabBarLayout.tsx index 988e76e..d83af37 100644 --- a/web/src/components/TabBarLayout.tsx +++ b/web/src/components/TabBarLayout.tsx @@ -4,7 +4,6 @@ import { Layout, Menu } from 'antd' import { HomeOutlined, FileTextOutlined, - UserOutlined, } from '@ant-design/icons' import styles from './TabBarLayout.module.less' @@ -35,11 +34,6 @@ const TabBarLayout: React.FC = () => { icon: , label: '答题', }, - { - key: '/profile', - icon: , - label: '我的', - }, ] const handleMenuClick = (key: string) => { diff --git a/web/src/pages/Home.module.less b/web/src/pages/Home.module.less index db9ef94..406881a 100644 --- a/web/src/pages/Home.module.less +++ b/web/src/pages/Home.module.less @@ -6,10 +6,16 @@ } .header { - text-align: center; + display: flex; + justify-content: space-between; + align-items: flex-start; margin-bottom: 32px; color: white; + .headerLeft { + flex: 1; + } + .title { color: white !important; margin-bottom: 8px !important; @@ -19,6 +25,30 @@ .subtitle { color: rgba(255, 255, 255, 0.9); } + + .userInfo { + background: rgba(255, 255, 255, 0.15); + padding: 12px 20px; + border-radius: 12px; + backdrop-filter: blur(10px); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); + + .userDetails { + display: flex; + flex-direction: column; + gap: 2px; + + .userNickname { + color: white !important; + font-size: 16px; + } + + .userUsername { + color: rgba(255, 255, 255, 0.8) !important; + font-size: 12px; + } + } + } } .statsCard { @@ -85,11 +115,21 @@ } .header { + flex-direction: column; + align-items: stretch; margin-bottom: 24px; + .headerLeft { + margin-bottom: 16px; + } + .title { font-size: 24px !important; } + + .userInfo { + width: 100%; + } } .statsCard { diff --git a/web/src/pages/Home.tsx b/web/src/pages/Home.tsx index 89a2cf4..a7f9552 100644 --- a/web/src/pages/Home.tsx +++ b/web/src/pages/Home.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' -import { Card, Statistic, Row, Col, Typography, message, Space } from 'antd' +import { Card, Statistic, Row, Col, Typography, message, Space, Avatar, Button, Modal } from 'antd' import { FileTextOutlined, CheckCircleOutlined, @@ -8,12 +8,14 @@ import { EditOutlined, RocketOutlined, BookOutlined, + UserOutlined, + LogoutOutlined, } from '@ant-design/icons' import * as questionApi from '../api/question' import type { Statistics } from '../types/question' import styles from './Home.module.less' -const { Title, Paragraph } = Typography +const { Title, Paragraph, Text } = Typography // 题型配置 const questionTypes = [ @@ -54,8 +56,15 @@ const questionTypes = [ }, ] +interface UserInfo { + username: string + nickname: string + avatar: string +} + const Home: React.FC = () => { const navigate = useNavigate() + const [userInfo, setUserInfo] = useState(null) const [statistics, setStatistics] = useState({ total_questions: 0, answered_questions: 0, @@ -75,6 +84,20 @@ const Home: React.FC = () => { } } + // 加载用户信息 + useEffect(() => { + const token = localStorage.getItem('token') + const savedUserInfo = localStorage.getItem('user') + + if (token && savedUserInfo) { + try { + setUserInfo(JSON.parse(savedUserInfo)) + } catch (e) { + console.error('解析用户信息失败', e) + } + } + }, []) + useEffect(() => { loadStatistics() }, []) @@ -96,12 +119,52 @@ const Home: React.FC = () => { } } + // 退出登录 + const handleLogout = () => { + Modal.confirm({ + title: '确定要退出登录吗?', + onOk: () => { + localStorage.removeItem('token') + localStorage.removeItem('user') + setUserInfo(null) + message.success('已退出登录') + navigate('/login') + }, + }) + } + return (
{/* 头部 */}
- AnKao 刷题 - 选择题型开始练习 +
+ AnKao 刷题 + 选择题型开始练习 +
+ {/* 用户信息 */} + {userInfo && ( +
+ + } + /> +
+ {userInfo.nickname} + @{userInfo.username} +
+ +
+
+ )}
{/* 统计卡片 */} diff --git a/web/src/pages/Profile.module.less b/web/src/pages/Profile.module.less deleted file mode 100644 index 5970243..0000000 --- a/web/src/pages/Profile.module.less +++ /dev/null @@ -1,79 +0,0 @@ -.container { - min-height: 100vh; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - padding: 24px; - padding-bottom: 80px; -} - -.content { - max-width: 600px; - margin: 0 auto; -} - -.userCard { - border-radius: 16px; - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); - margin-bottom: 24px; -} - -.userInfo { - display: flex; - align-items: center; - gap: 20px; -} - -.userDetails { - flex: 1; -} - -.userNickname { - margin: 0 !important; -} - -.userUsername { - display: block; - margin-top: 4px; -} - -.menuCard { - border-radius: 16px; - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); - margin-bottom: 24px; -} - -.logoutButton { - height: 48px; - font-size: 16px; - font-weight: 500; - border-radius: 12px; -} - -// 响应式设计 - 移动端 -@media (max-width: 768px) { - .container { - padding: 16px; - padding-bottom: 70px; - } - - .userCard, - .menuCard { - border-radius: 12px; - margin-bottom: 16px; - } - - .userInfo { - gap: 16px; - } - - .logoutButton { - height: 44px; - font-size: 15px; - } -} - -// 响应式设计 - PC端 -@media (min-width: 769px) { - .container { - padding: 32px; - } -} diff --git a/web/src/pages/Profile.tsx b/web/src/pages/Profile.tsx deleted file mode 100644 index 7711777..0000000 --- a/web/src/pages/Profile.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import React, { useState, useEffect } from 'react' -import { useNavigate } from 'react-router-dom' -import { - Card, - Avatar, - List, - Button, - Modal, - message, - Typography, - Space, -} from 'antd' -import { - RightOutlined, - SettingOutlined, - FileTextOutlined, - UserOutlined, - BookOutlined, -} from '@ant-design/icons' -import styles from './Profile.module.less' - -const { Title, Text } = Typography - -interface UserInfo { - username: string - nickname: string - avatar: string -} - -const Profile: React.FC = () => { - const navigate = useNavigate() - const [userInfo, setUserInfo] = useState(null) - - useEffect(() => { - // 从 localStorage 获取用户信息 - const token = localStorage.getItem('token') - const savedUserInfo = localStorage.getItem('user') - - if (token && savedUserInfo) { - try { - setUserInfo(JSON.parse(savedUserInfo)) - } catch (e) { - console.error('解析用户信息失败', e) - } - } - }, []) - - const handleLogout = () => { - Modal.confirm({ - title: '确定要退出登录吗?', - onOk: () => { - localStorage.removeItem('token') - localStorage.removeItem('user') - setUserInfo(null) - message.success('已退出登录') - navigate('/login') - }, - }) - } - - const handleLogin = () => { - navigate('/login') - } - - return ( -
-
- {/* 用户信息卡片 */} - - {userInfo ? ( -
- } - /> -
- {userInfo.nickname} - @{userInfo.username} -
-
- ) : ( -
- } /> -
- 未登录 - -
-
- )} -
- - {/* 功能列表 */} - - - navigate('/wrong-questions')} - style={{ cursor: 'pointer' }} - > - - - 错题本 - - - - message.info('功能开发中')} - style={{ cursor: 'pointer' }} - > - - - 我的题目 - - - - message.info('功能开发中')} - style={{ cursor: 'pointer' }} - > - - - 设置 - - - - - - - {/* 退出登录按钮 */} - {userInfo && ( - - )} -
-
- ) -} - -export default Profile diff --git a/web/src/pages/Question.tsx b/web/src/pages/Question.tsx index 26ab357..1c2f671 100644 --- a/web/src/pages/Question.tsx +++ b/web/src/pages/Question.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react' -import { useSearchParams } from 'react-router-dom' +import { useSearchParams, useNavigate } from 'react-router-dom' import { Button, Card, @@ -17,6 +17,7 @@ import { Typography, Row, Col, + Spin, } from 'antd' import { CheckOutlined, @@ -25,6 +26,7 @@ import { UnorderedListOutlined, FilterOutlined, ReloadOutlined, + ArrowLeftOutlined, } from '@ant-design/icons' import type { Question, AnswerResult } from '../types/question' import * as questionApi from '../api/question' @@ -35,11 +37,13 @@ const { Title, Text } = Typography const QuestionPage: React.FC = () => { const [searchParams] = useSearchParams() + const navigate = useNavigate() const [currentQuestion, setCurrentQuestion] = useState(null) const [selectedAnswer, setSelectedAnswer] = useState('') const [showResult, setShowResult] = useState(false) const [answerResult, setAnswerResult] = useState(null) const [loading, setLoading] = useState(false) + const [autoNextLoading, setAutoNextLoading] = useState(false) const [allQuestions, setAllQuestions] = useState([]) const [currentIndex, setCurrentIndex] = useState(0) const [fillAnswers, setFillAnswers] = useState([]) @@ -148,10 +152,13 @@ const QuestionPage: React.FC = () => { setAnswerResult(res.data) setShowResult(true) + // 如果答案正确,1秒后自动进入下一题 if (res.data.correct) { - message.success('回答正确!') - } else { - message.error('回答错误') + setAutoNextLoading(true) + setTimeout(() => { + setAutoNextLoading(false) + handleNext() + }, 1000) } } } catch (error) { @@ -357,6 +364,12 @@ const QuestionPage: React.FC = () => {
{/* 头部 */}
+ AnKao 刷题 ) : ( - )}