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:
BIN
src/assets/logo_bg_white.png
Normal file
BIN
src/assets/logo_bg_white.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 647 KiB |
BIN
src/assets/logo_text.png
Normal file
BIN
src/assets/logo_text.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 558 KiB |
@@ -9,253 +9,156 @@ import {
|
|||||||
TextField,
|
TextField,
|
||||||
Typography,
|
Typography,
|
||||||
Link,
|
Link,
|
||||||
styled
|
AppBar,
|
||||||
|
Toolbar,
|
||||||
|
Card,
|
||||||
|
CardContent
|
||||||
} from '@mui/material';
|
} 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 Login = () => {
|
||||||
const [username, setUsername] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
const [rememberUser, setRememberUser] = useState(false);
|
const [password, setPassword] = useState('');
|
||||||
|
const [rememberEmail, setRememberEmail] = useState(false);
|
||||||
const [isSubmitting, setIsSubmitting] = 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 navigate = useNavigate();
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (!username.trim()) {
|
const hasEmail = !!email.trim();
|
||||||
setError(true);
|
const hasPassword = !!password.trim();
|
||||||
return;
|
setEmailError(!hasEmail);
|
||||||
}
|
setPasswordError(!hasPassword);
|
||||||
|
if (!hasEmail || !hasPassword) return;
|
||||||
|
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
setError(false);
|
|
||||||
|
|
||||||
// 模拟登录过程
|
// 模拟登录过程
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
navigate('/');
|
navigate('/');
|
||||||
}, 800);
|
}, 800);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
|
||||||
navigate('/');
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoginContainer>
|
<Box sx={{ minHeight: '100vh', width: '100vw', bgcolor: 'background.default', display: 'flex', flexDirection: 'column' }}>
|
||||||
<TopBar>
|
{/* 顶部栏使用主题主色 */}
|
||||||
<BrandLogo>T</BrandLogo>
|
<AppBar position="static" color="primary" enableColorOnDark>
|
||||||
</TopBar>
|
<Toolbar sx={{ minHeight: 60 }}>
|
||||||
|
<Box
|
||||||
<LoginMain>
|
sx={{
|
||||||
<LoginCard>
|
width: 40,
|
||||||
<ServiceName>Servicename</ServiceName>
|
height: 40,
|
||||||
<LoginTitle>
|
bgcolor: 'common.white',
|
||||||
Enter Login <br/> Username
|
color: 'primary.main',
|
||||||
</LoginTitle>
|
borderRadius: '50%',
|
||||||
|
display: 'flex',
|
||||||
<LoginForm onSubmit={handleSubmit} noValidate>
|
alignItems: 'center',
|
||||||
<TextField
|
justifyContent: 'center',
|
||||||
fullWidth
|
fontWeight: 700,
|
||||||
id="username"
|
fontSize: '1.1rem',
|
||||||
name="username"
|
}}
|
||||||
placeholder="Username"
|
aria-label="Brand"
|
||||||
autoComplete="username"
|
>
|
||||||
value={username}
|
T
|
||||||
onChange={(e) => setUsername(e.target.value)}
|
</Box>
|
||||||
error={error}
|
</Toolbar>
|
||||||
required
|
</AppBar>
|
||||||
/>
|
|
||||||
|
{/* 主体卡片 */}
|
||||||
<RememberRow>
|
<Container maxWidth="sm" sx={{ flex: 1, display: 'flex', alignItems: 'center' }}>
|
||||||
<FormControlLabel
|
<Card sx={{ width: '100%' }}>
|
||||||
control={
|
<CardContent sx={{ p: 4 }}>
|
||||||
<Checkbox
|
<Typography variant="caption" color="text.secondary" sx={{ mb: 1, display: 'block' }}>
|
||||||
checked={rememberUser}
|
Servicename
|
||||||
onChange={(e) => setRememberUser(e.target.checked)}
|
</Typography>
|
||||||
id="rememberUser"
|
<Typography variant="h5" fontWeight={700} sx={{ mb: 2 }}>
|
||||||
/>
|
Enter Login
|
||||||
}
|
<br /> Email & Password
|
||||||
label="Remember username"
|
</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?
|
<TextField
|
||||||
</Link>
|
|
||||||
</RememberRow>
|
|
||||||
|
|
||||||
<ActionButtons>
|
|
||||||
<PrimaryButton
|
|
||||||
type="submit"
|
|
||||||
variant="contained"
|
|
||||||
fullWidth
|
fullWidth
|
||||||
disabled={isSubmitting}
|
id="password"
|
||||||
>
|
name="password"
|
||||||
{isSubmitting ? 'Processing...' : 'Next'}
|
type="password"
|
||||||
</PrimaryButton>
|
placeholder="Password"
|
||||||
<SecondaryButton
|
autoComplete="current-password"
|
||||||
type="button"
|
value={password}
|
||||||
variant="contained"
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
fullWidth
|
error={passwordError}
|
||||||
onClick={handleCancel}
|
required
|
||||||
>
|
sx={{ mb: 2 }}
|
||||||
Cancel
|
/>
|
||||||
</SecondaryButton>
|
|
||||||
</ActionButtons>
|
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
|
||||||
</LoginForm>
|
<FormControlLabel
|
||||||
|
control={
|
||||||
<HelpSection>
|
<Checkbox
|
||||||
<Link href="#">Do you need help?</Link>
|
checked={rememberEmail}
|
||||||
<Box mt={1}>
|
onChange={(e) => setRememberEmail(e.target.checked)}
|
||||||
No account? <Link href="#">Sign up</Link> or log in with your social network account.
|
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>
|
</Box>
|
||||||
</HelpSection>
|
|
||||||
|
<Box sx={{ mt: 2, textAlign: 'center' }}>
|
||||||
<SocialLogin>
|
<Link href="#">Do you need help?</Link>
|
||||||
<SocialButton aria-label="Login with Facebook">
|
<Box mt={0.5}>
|
||||||
<Link href="#" underline="none" color="inherit">f</Link>
|
{/* 保留说明文案,去掉社交登录 */}
|
||||||
</SocialButton>
|
No account? <Link href="#">Sign up</Link>.
|
||||||
<SocialButton aria-label="Login with Twitter">
|
</Box>
|
||||||
<Link href="#" underline="none" color="inherit">t</Link>
|
</Box>
|
||||||
</SocialButton>
|
</CardContent>
|
||||||
</SocialLogin>
|
</Card>
|
||||||
</LoginCard>
|
</Container>
|
||||||
</LoginMain>
|
|
||||||
|
{/* 页脚 */}
|
||||||
<Footer>
|
<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>
|
<Box>© Deutsche Telekom AG</Box>
|
||||||
<LegalLinks>
|
<Box sx={{ display: 'flex', gap: 2 }}>
|
||||||
<Link href="#" color="inherit">Imprint</Link>
|
<Link href="#" color="inherit">Imprint</Link>
|
||||||
<Link href="#" color="inherit">Data privacy</Link>
|
<Link href="#" color="inherit">Data privacy</Link>
|
||||||
</LegalLinks>
|
</Box>
|
||||||
</Footer>
|
</Box>
|
||||||
</LoginContainer>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user