feat(auth): 将 Register/ForgotPassword/ResetPassword 页面样式对齐 V21 设计系统

- 使用统一的 xx-auth-page/xx-auth-card 布局结构
- 统一的渐变背景: rgba(79, 70, 229, 0.08) -> rgba(16, 185, 129, 0.06)
- 白色卡片 28px 圆角 + 大阴影效果
- Input 12px 圆角,focus 时 Indigo 边框
- 使用 btn primary class 样式按钮
- 添加 xx-auth-header/footer 统一头部和底部样式
- Result 页面使用 xx-result-page/xx-result-card 布局
This commit is contained in:
CI Test
2026-06-26 21:34:15 +08:00
parent e740ac6429
commit 0d232b1588
6 changed files with 362 additions and 92 deletions
+91 -11
View File
@@ -1,18 +1,98 @@
.forgot-password-container {
/* V21 忘记密码页 - 与 Login 保持一致 */
.xx-auth-page {
display: grid;
place-items: center;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
background: linear-gradient(135deg, rgba(79, 70, 229, 0.08), rgba(16, 185, 129, 0.06));
padding: 24px;
}
.forgot-password-card {
width: 400px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
.xx-auth-card {
background: rgba(255, 255, 255, 0.94);
border: 1px solid #e2e8f0;
border-radius: 28px;
box-shadow: 0 24px 70px rgba(15, 23, 42, 0.09);
padding: 40px;
width: 100%;
max-width: 400px;
}
.forgot-password-card .ant-card-head-title {
.xx-auth-header {
text-align: center;
margin-bottom: 32px;
}
.xx-logo-big {
font-size: 64px;
margin-bottom: 16px;
}
.xx-auth-header h1 {
font-size: 28px;
margin: 0 0 8px;
color: #0f172a;
font-weight: 900;
}
.xx-auth-header p {
color: #64748b;
margin: 0;
font-size: 14px;
}
.xx-auth-footer {
text-align: center;
font-size: 14px;
color: #64748b;
margin-top: 20px;
}
.xx-auth-footer a {
color: #4f46e5;
text-decoration: none;
font-weight: 850;
}
.xx-auth-footer a:hover {
color: #4338ca;
}
/* Form 样式覆盖 */
.ant-form-item-label > label {
font-weight: 850;
color: #0f172a;
font-size: 14px;
}
.ant-input,
.ant-input-password input {
background: white;
border: 1px solid #e2e8f0;
border-radius: 12px;
}
.ant-input:focus,
.ant-input-password input:focus {
border-color: #4f46e5;
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
}
/* Result 成功页样式 */
.xx-result-page {
display: grid;
place-items: center;
min-height: 100vh;
background: linear-gradient(135deg, rgba(79, 70, 229, 0.08), rgba(16, 185, 129, 0.06));
padding: 24px;
}
.xx-result-card {
background: rgba(255, 255, 255, 0.94);
border: 1px solid #e2e8f0;
border-radius: 28px;
box-shadow: 0 24px 70px rgba(15, 23, 42, 0.09);
padding: 48px 40px;
width: 100%;
max-width: 400px;
text-align: center;
font-size: 24px;
font-weight: 600;
}
+31 -20
View File
@@ -1,8 +1,8 @@
/**
* 忘记密码页面
* 忘记密码页面 - V21 完全对标
*/
import React, { useState } from 'react';
import { Form, Input, Button, Card, message, Result } from 'antd';
import { Form, Input, Button, Result, message } from 'antd';
import { MailOutlined } from '@ant-design/icons';
import { Link } from 'react-router-dom';
import { useMutation } from '@tanstack/react-query';
@@ -30,8 +30,8 @@ const ForgotPassword: React.FC = () => {
if (emailSent) {
return (
<div className="forgot-password-container">
<Card className="forgot-password-card">
<div className="xx-result-page">
<div className="xx-result-card">
<Result
status="success"
title="重置邮件已发送"
@@ -42,45 +42,56 @@ const ForgotPassword: React.FC = () => {
</Link>,
]}
/>
</Card>
</div>
</div>
);
}
return (
<div className="forgot-password-container">
<Card className="forgot-password-card" title="重置密码">
<p style={{ marginBottom: '24px', color: '#666' }}>
<div className="xx-auth-page">
<div className="xx-auth-card">
<div className="xx-auth-header">
<div className="xx-logo-big">🦐</div>
<h1></h1>
<p></p>
</div>
<p style={{ marginBottom: '24px', color: '#64748b', fontSize: '14px', textAlign: 'center' }}>
</p>
<Form form={form} name="forgot-password" onFinish={onFinish} size="large">
<Form form={form} name="forgot-password" onFinish={onFinish} layout="vertical">
<Form.Item
name="email"
label="邮箱"
rules={[
{ required: true, message: '请输入邮箱' },
{ type: 'email', message: '请输入有效的邮箱地址' },
]}
>
<Input prefix={<MailOutlined />} placeholder="邮箱" />
<Input
prefix={<MailOutlined />}
placeholder="name@example.com"
size="large"
/>
</Form.Item>
<Form.Item>
<Button
type="primary"
htmlType="submit"
loading={resetMutation.isPending}
block
<button
type="submit"
className="btn primary"
style={{ width: '100%' }}
disabled={resetMutation.isPending}
>
</Button>
{resetMutation.isPending ? '发送中...' : '发送重置邮件'}
</button>
</Form.Item>
<div style={{ textAlign: 'center' }}>
<div className="xx-auth-footer">
<Link to="/login"></Link>
</div>
</Form>
</Card>
</div>
</div>
);
};
+80 -11
View File
@@ -1,18 +1,87 @@
.register-container {
/* V21 注册页 - 与 Login 保持一致 */
.xx-auth-page {
display: grid;
place-items: center;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
background: linear-gradient(135deg, rgba(79, 70, 229, 0.08), rgba(16, 185, 129, 0.06));
padding: 24px;
}
.register-card {
width: 400px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
.xx-auth-card {
background: rgba(255, 255, 255, 0.94);
border: 1px solid #e2e8f0;
border-radius: 28px;
box-shadow: 0 24px 70px rgba(15, 23, 42, 0.09);
padding: 40px;
width: 100%;
max-width: 400px;
}
.register-card .ant-card-head-title {
.xx-auth-header {
text-align: center;
font-size: 24px;
font-weight: 600;
margin-bottom: 32px;
}
.xx-logo-big {
font-size: 64px;
margin-bottom: 16px;
}
.xx-auth-header h1 {
font-size: 28px;
margin: 0 0 8px;
color: #0f172a;
font-weight: 900;
}
.xx-auth-header p {
color: #64748b;
margin: 0;
font-size: 14px;
}
.xx-auth-footer {
text-align: center;
font-size: 14px;
color: #64748b;
margin-top: 20px;
}
.xx-auth-footer a {
color: #4f46e5;
text-decoration: none;
font-weight: 850;
}
.xx-auth-footer a:hover {
color: #4338ca;
}
/* Form 样式覆盖 */
.ant-form-item-label > label {
font-weight: 850;
color: #0f172a;
font-size: 14px;
}
.ant-input,
.ant-input-password input {
background: white;
border: 1px solid #e2e8f0;
border-radius: 12px;
}
.ant-input:focus,
.ant-input-password input:focus {
border-color: #4f46e5;
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
}
.ant-checkbox-wrapper {
font-size: 14px;
color: #64748b;
}
.ant-checkbox-inner {
border-radius: 6px;
}
+31 -17
View File
@@ -1,8 +1,8 @@
/**
* 注册页面
* 注册页面 - V21 完全对标
*/
import React from 'react';
import { Form, Input, Button, Card, message } from 'antd';
import { Form, Input, message } from 'antd';
import { UserOutlined, LockOutlined, MailOutlined } from '@ant-design/icons';
import { Link } from 'react-router-dom';
import { useRegister } from '@/hooks/useAuth';
@@ -34,17 +34,24 @@ const Register: React.FC = () => {
};
return (
<div className="register-container">
<Card className="register-card" title="注册小虾 SaaS">
<div className="xx-auth-page">
<div className="xx-auth-card">
<div className="xx-auth-header">
<div className="xx-logo-big">🦐</div>
<h1></h1>
<p></p>
</div>
<Form
form={form}
name="register"
onFinish={onFinish}
autoComplete="off"
size="large"
layout="vertical"
>
<Form.Item
name="email"
label="邮箱"
rules={[
{ required: true, message: '请输入邮箱' },
{ type: 'email', message: '请输入有效的邮箱地址' },
@@ -52,12 +59,14 @@ const Register: React.FC = () => {
>
<Input
prefix={<MailOutlined />}
placeholder="邮箱"
placeholder="name@example.com"
size="large"
/>
</Form.Item>
<Form.Item
name="username"
label="用户名"
rules={[
{ required: true, message: '请输入用户名' },
{ min: 3, message: '用户名至少 3 个字符' },
@@ -71,11 +80,13 @@ const Register: React.FC = () => {
<Input
prefix={<UserOutlined />}
placeholder="用户名"
size="large"
/>
</Form.Item>
<Form.Item
name="password"
label="密码"
rules={[
{ required: true, message: '请输入密码' },
{ min: 8, message: '密码至少 8 个字符' },
@@ -87,12 +98,14 @@ const Register: React.FC = () => {
>
<Input.Password
prefix={<LockOutlined />}
placeholder="密码"
placeholder="•••••••••"
size="large"
/>
</Form.Item>
<Form.Item
name="confirmPassword"
label="确认密码"
dependencies={['password']}
rules={[
{ required: true, message: '请确认密码' },
@@ -108,26 +121,27 @@ const Register: React.FC = () => {
>
<Input.Password
prefix={<LockOutlined />}
placeholder="确认密码"
placeholder="•••••••••"
size="large"
/>
</Form.Item>
<Form.Item>
<Button
type="primary"
htmlType="submit"
loading={registerMutation.isPending}
block
<button
type="submit"
className="btn primary"
style={{ width: '100%' }}
disabled={registerMutation.isPending}
>
</Button>
{registerMutation.isPending ? '注册中...' : '注册'}
</button>
</Form.Item>
<div style={{ textAlign: 'center' }}>
<div className="xx-auth-footer">
<Link to="/login"></Link>
</div>
</Form>
</Card>
</div>
</div>
);
};
+91 -11
View File
@@ -1,18 +1,98 @@
.reset-password-container {
/* V21 重置密码页 - 与 Login 保持一致 */
.xx-auth-page {
display: grid;
place-items: center;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
background: linear-gradient(135deg, rgba(79, 70, 229, 0.08), rgba(16, 185, 129, 0.06));
padding: 24px;
}
.reset-password-card {
width: 400px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
.xx-auth-card {
background: rgba(255, 255, 255, 0.94);
border: 1px solid #e2e8f0;
border-radius: 28px;
box-shadow: 0 24px 70px rgba(15, 23, 42, 0.09);
padding: 40px;
width: 100%;
max-width: 400px;
}
.reset-password-card .ant-card-head-title {
.xx-auth-header {
text-align: center;
margin-bottom: 32px;
}
.xx-logo-big {
font-size: 64px;
margin-bottom: 16px;
}
.xx-auth-header h1 {
font-size: 28px;
margin: 0 0 8px;
color: #0f172a;
font-weight: 900;
}
.xx-auth-header p {
color: #64748b;
margin: 0;
font-size: 14px;
}
.xx-auth-footer {
text-align: center;
font-size: 14px;
color: #64748b;
margin-top: 20px;
}
.xx-auth-footer a {
color: #4f46e5;
text-decoration: none;
font-weight: 850;
}
.xx-auth-footer a:hover {
color: #4338ca;
}
/* Form 样式覆盖 */
.ant-form-item-label > label {
font-weight: 850;
color: #0f172a;
font-size: 14px;
}
.ant-input,
.ant-input-password input {
background: white;
border: 1px solid #e2e8f0;
border-radius: 12px;
}
.ant-input:focus,
.ant-input-password input:focus {
border-color: #4f46e5;
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
}
/* Result 结果页样式 */
.xx-result-page {
display: grid;
place-items: center;
min-height: 100vh;
background: linear-gradient(135deg, rgba(79, 70, 229, 0.08), rgba(16, 185, 129, 0.06));
padding: 24px;
}
.xx-result-card {
background: rgba(255, 255, 255, 0.94);
border: 1px solid #e2e8f0;
border-radius: 28px;
box-shadow: 0 24px 70px rgba(15, 23, 42, 0.09);
padding: 48px 40px;
width: 100%;
max-width: 400px;
text-align: center;
font-size: 24px;
font-weight: 600;
}
+38 -22
View File
@@ -1,8 +1,8 @@
/**
* 重置密码页面
* 重置密码页面 - V21 完全对标
*/
import React from 'react';
import { Form, Input, Button, Card, message, Result } from 'antd';
import { Form, Input, Button, Result, message } from 'antd';
import { LockOutlined } from '@ant-design/icons';
import { Link, useSearchParams, useNavigate } from 'react-router-dom';
import { useMutation } from '@tanstack/react-query';
@@ -32,8 +32,8 @@ const ResetPassword: React.FC = () => {
if (!token) {
return (
<div className="reset-password-container">
<Card className="reset-password-card">
<div className="xx-result-page">
<div className="xx-result-card">
<Result
status="error"
title="无效的重置链接"
@@ -44,15 +44,15 @@ const ResetPassword: React.FC = () => {
</Link>,
]}
/>
</Card>
</div>
</div>
);
}
if (resetMutation.isSuccess) {
return (
<div className="reset-password-container">
<Card className="reset-password-card">
<div className="xx-result-page">
<div className="xx-result-card">
<Result
status="success"
title="密码重置成功"
@@ -63,17 +63,24 @@ const ResetPassword: React.FC = () => {
</Link>,
]}
/>
</Card>
</div>
</div>
);
}
return (
<div className="reset-password-container">
<Card className="reset-password-card" title="设置新密码">
<Form form={form} name="reset-password" onFinish={onFinish} size="large">
<div className="xx-auth-page">
<div className="xx-auth-card">
<div className="xx-auth-header">
<div className="xx-logo-big">🦐</div>
<h1></h1>
<p></p>
</div>
<Form form={form} name="reset-password" onFinish={onFinish} layout="vertical">
<Form.Item
name="password"
label="新密码"
rules={[
{ required: true, message: '请输入新密码' },
{ min: 8, message: '密码至少 8 个字符' },
@@ -83,11 +90,16 @@ const ResetPassword: React.FC = () => {
},
]}
>
<Input.Password prefix={<LockOutlined />} placeholder="新密码" />
<Input.Password
prefix={<LockOutlined />}
placeholder="•••••••••"
size="large"
/>
</Form.Item>
<Form.Item
name="confirmPassword"
label="确认新密码"
dependencies={['password']}
rules={[
{ required: true, message: '请确认新密码' },
@@ -101,25 +113,29 @@ const ResetPassword: React.FC = () => {
}),
]}
>
<Input.Password prefix={<LockOutlined />} placeholder="确认新密码" />
<Input.Password
prefix={<LockOutlined />}
placeholder="•••••••••"
size="large"
/>
</Form.Item>
<Form.Item>
<Button
type="primary"
htmlType="submit"
loading={resetMutation.isPending}
block
<button
type="submit"
className="btn primary"
style={{ width: '100%' }}
disabled={resetMutation.isPending}
>
</Button>
{resetMutation.isPending ? '重置中...' : '重置密码'}
</button>
</Form.Item>
<div style={{ textAlign: 'center' }}>
<div className="xx-auth-footer">
<Link to="/login"></Link>
</div>
</Form>
</Card>
</div>
</div>
);
};