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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user