refactor(Login): rewrite login page using Material-UI components

- Replace styled components with Material-UI's AppBar, Toolbar, and Card
- Update form fields to handle email and password instead of just username
- Improve error handling for both email and password fields
- Simplify layout and remove social login options
This commit is contained in:
2025-10-09 18:11:35 +08:00
parent 5f93573e57
commit a1e8f47ed4
3 changed files with 128 additions and 225 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 647 KiB

BIN
src/assets/logo_text.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 558 KiB

View File

@@ -9,253 +9,156 @@ import {
TextField,
Typography,
Link,
styled
AppBar,
Toolbar,
Card,
CardContent
} from '@mui/material';
const LoginContainer = styled(Box)({
height: '100vh',
display: 'flex',
flexDirection: 'column',
backgroundColor: '#f5f7fa',
});
const TopBar = styled(Box)({
height: '60px',
backgroundColor: '#1a1a2e',
display: 'flex',
alignItems: 'center',
padding: '0 20px',
});
const BrandLogo = styled(Box)({
width: '40px',
height: '40px',
backgroundColor: '#fff',
color: '#1a1a2e',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: '50%',
fontWeight: 'bold',
fontSize: '1.2rem',
});
const LoginMain = styled(Box)({
flex: 1,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
});
const LoginCard = styled(Box)({
width: '400px',
backgroundColor: '#fff',
borderRadius: '8px',
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
padding: '30px',
});
const ServiceName = styled(Typography)({
fontSize: '0.75rem',
color: '#666',
marginBottom: '10px',
});
const LoginTitle = styled(Typography)({
fontSize: '1.5rem',
fontWeight: 'bold',
marginBottom: '20px',
});
const LoginForm = styled('form')({
display: 'flex',
flexDirection: 'column',
gap: '20px',
});
const RememberRow = styled(Box)({
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
});
const ActionButtons = styled(Box)({
display: 'flex',
gap: '10px',
marginTop: '10px',
});
const PrimaryButton = styled(Button)({
backgroundColor: '#e20074',
'&:hover': {
backgroundColor: '#c10062',
},
});
const SecondaryButton = styled(Button)({
color: '#333',
backgroundColor: '#f5f5f5',
'&:hover': {
backgroundColor: '#e0e0e0',
},
});
const HelpSection = styled(Box)({
marginTop: '20px',
textAlign: 'center',
fontSize: '0.875rem',
});
const SocialLogin = styled(Box)({
display: 'flex',
justifyContent: 'center',
gap: '10px',
marginTop: '15px',
});
const SocialButton = styled(Box)({
width: '30px',
height: '30px',
borderRadius: '50%',
backgroundColor: '#e0e0e0',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
});
const Footer = styled(Box)({
height: '50px',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '0 20px',
borderTop: '1px solid #eee',
fontSize: '0.75rem',
color: '#666',
});
const LegalLinks = styled(Box)({
display: 'flex',
gap: '15px',
});
const Login = () => {
const [username, setUsername] = useState('');
const [rememberUser, setRememberUser] = useState(false);
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [rememberEmail, setRememberEmail] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState(false);
const [emailError, setEmailError] = useState(false);
const [passwordError, setPasswordError] = useState(false);
const navigate = useNavigate();
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!username.trim()) {
setError(true);
return;
}
const hasEmail = !!email.trim();
const hasPassword = !!password.trim();
setEmailError(!hasEmail);
setPasswordError(!hasPassword);
if (!hasEmail || !hasPassword) return;
setIsSubmitting(true);
setError(false);
// 模拟登录过程
setTimeout(() => {
navigate('/');
}, 800);
};
const handleCancel = () => {
navigate('/');
};
return (
<LoginContainer>
<TopBar>
<BrandLogo>T</BrandLogo>
</TopBar>
<LoginMain>
<LoginCard>
<ServiceName>Servicename</ServiceName>
<LoginTitle>
Enter Login <br/> Username
</LoginTitle>
<LoginForm onSubmit={handleSubmit} noValidate>
<TextField
fullWidth
id="username"
name="username"
placeholder="Username"
autoComplete="username"
value={username}
onChange={(e) => setUsername(e.target.value)}
error={error}
required
/>
<RememberRow>
<FormControlLabel
control={
<Checkbox
checked={rememberUser}
onChange={(e) => setRememberUser(e.target.checked)}
id="rememberUser"
/>
}
label="Remember username"
<Box sx={{ minHeight: '100vh', width: '100vw', bgcolor: 'background.default', display: 'flex', flexDirection: 'column' }}>
{/* 顶部栏使用主题主色 */}
<AppBar position="static" color="primary" enableColorOnDark>
<Toolbar sx={{ minHeight: 60 }}>
<Box
sx={{
width: 40,
height: 40,
bgcolor: 'common.white',
color: 'primary.main',
borderRadius: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontWeight: 700,
fontSize: '1.1rem',
}}
aria-label="Brand"
>
T
</Box>
</Toolbar>
</AppBar>
{/* 主体卡片 */}
<Container maxWidth="sm" sx={{ flex: 1, display: 'flex', alignItems: 'center' }}>
<Card sx={{ width: '100%' }}>
<CardContent sx={{ p: 4 }}>
<Typography variant="caption" color="text.secondary" sx={{ mb: 1, display: 'block' }}>
Servicename
</Typography>
<Typography variant="h5" fontWeight={700} sx={{ mb: 2 }}>
Enter Login
<br /> Email & Password
</Typography>
<Box component="form" onSubmit={handleSubmit} noValidate>
<TextField
fullWidth
id="email"
name="email"
type="email"
placeholder="Email"
autoComplete="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
error={emailError}
required
sx={{ mb: 2 }}
/>
<Link href="#" variant="caption">
Forgot your username or password?
</Link>
</RememberRow>
<ActionButtons>
<PrimaryButton
type="submit"
variant="contained"
<TextField
fullWidth
disabled={isSubmitting}
>
{isSubmitting ? 'Processing...' : 'Next'}
</PrimaryButton>
<SecondaryButton
type="button"
variant="contained"
fullWidth
onClick={handleCancel}
>
Cancel
</SecondaryButton>
</ActionButtons>
</LoginForm>
<HelpSection>
<Link href="#">Do you need help?</Link>
<Box mt={1}>
No account? <Link href="#">Sign up</Link> or log in with your social network account.
id="password"
name="password"
type="password"
placeholder="Password"
autoComplete="current-password"
value={password}
onChange={(e) => setPassword(e.target.value)}
error={passwordError}
required
sx={{ mb: 2 }}
/>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
<FormControlLabel
control={
<Checkbox
checked={rememberEmail}
onChange={(e) => setRememberEmail(e.target.checked)}
id="rememberEmail"
/>
}
label="Remember email"
/>
<Link href="#" variant="caption">
Forgot your username or password?
</Link>
</Box>
<Button type="submit" variant="contained" fullWidth disabled={isSubmitting}>
{isSubmitting ? 'Processing...' : 'Login'}
</Button>
</Box>
</HelpSection>
<SocialLogin>
<SocialButton aria-label="Login with Facebook">
<Link href="#" underline="none" color="inherit">f</Link>
</SocialButton>
<SocialButton aria-label="Login with Twitter">
<Link href="#" underline="none" color="inherit">t</Link>
</SocialButton>
</SocialLogin>
</LoginCard>
</LoginMain>
<Footer>
<Box sx={{ mt: 2, textAlign: 'center' }}>
<Link href="#">Do you need help?</Link>
<Box mt={0.5}>
{/* 保留说明文案,去掉社交登录 */}
No account? <Link href="#">Sign up</Link>.
</Box>
</Box>
</CardContent>
</Card>
</Container>
{/* 页脚 */}
<Box
sx={{
borderTop: '1px solid',
borderColor: 'divider',
height: 50,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
px: 2,
color: 'text.secondary',
bgcolor: 'background.paper',
}}
>
<Box>© Deutsche Telekom AG</Box>
<LegalLinks>
<Box sx={{ display: 'flex', gap: 2 }}>
<Link href="#" color="inherit">Imprint</Link>
<Link href="#" color="inherit">Data privacy</Link>
</LegalLinks>
</Footer>
</LoginContainer>
</Box>
</Box>
</Box>
);
};