1
This commit is contained in:
@@ -1,13 +1,39 @@
|
|||||||
import { useState } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Button, Card, Form, Input, message, Typography, Checkbox } from 'antd';
|
import { Button, Card, Form, Input, message, Typography, Checkbox } from 'antd';
|
||||||
import { UserOutlined, LockOutlined, ThunderboltOutlined } from '@ant-design/icons';
|
import { UserOutlined, LockOutlined, ThunderboltOutlined } from '@ant-design/icons';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useAdminStore } from '../store';
|
import { useAdminStore } from '../store';
|
||||||
|
import { getSiteInfo } from '../api';
|
||||||
|
|
||||||
const AdminLoginPage = () => {
|
const AdminLoginPage = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { login } = useAdminStore();
|
const { login } = useAdminStore();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const getInitialSiteInfo = () => {
|
||||||
|
try {
|
||||||
|
const cached = localStorage.getItem('siteInfo');
|
||||||
|
if (cached) {
|
||||||
|
const info = JSON.parse(cached);
|
||||||
|
return {
|
||||||
|
siteName: info.siteName || 'VideoGen.AI',
|
||||||
|
siteLogo: info.siteLogo || '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch { }
|
||||||
|
return { siteName: 'VideoGen.AI', siteLogo: '' };
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialInfo = getInitialSiteInfo();
|
||||||
|
const [siteName, setSiteName] = useState(initialInfo.siteName);
|
||||||
|
const [siteLogo, setSiteLogo] = useState(initialInfo.siteLogo);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getSiteInfo().then(info => {
|
||||||
|
setSiteName(info.siteName);
|
||||||
|
setSiteLogo(info.siteLogo);
|
||||||
|
}).catch(() => {});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleLogin = async (values: { username: string; password: string; rememberMe?: boolean }) => {
|
const handleLogin = async (values: { username: string; password: string; rememberMe?: boolean }) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -24,45 +50,113 @@ const AdminLoginPage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{
|
<div style={{
|
||||||
minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center',
|
minHeight: '100vh',
|
||||||
background: 'linear-gradient(135deg, #0f0f23 0%, #1a1a35 50%, #0f0f23 100%)',
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
background: 'linear-gradient(135deg, #f0f4ff 0%, #e8ecf8 40%, #f0f0ff 70%, #f8f9ff 100%)',
|
||||||
|
position: 'relative',
|
||||||
|
overflow: 'hidden',
|
||||||
}}>
|
}}>
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute',
|
||||||
|
width: 500, height: 500, borderRadius: '50%',
|
||||||
|
background: 'radial-gradient(circle, rgba(99,102,241,0.08) 0%, transparent 70%)',
|
||||||
|
top: -150, right: -100, filter: 'blur(40px)',
|
||||||
|
}} />
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute',
|
||||||
|
width: 400, height: 400, borderRadius: '50%',
|
||||||
|
background: 'radial-gradient(circle, rgba(139,92,246,0.06) 0%, transparent 70%)',
|
||||||
|
bottom: -100, left: -80, filter: 'blur(50px)',
|
||||||
|
}} />
|
||||||
|
|
||||||
<Card bordered={false} style={{
|
<Card bordered={false} style={{
|
||||||
width: 420, borderRadius: 16, boxShadow: '0 20px 60px rgba(0,0,0,0.3)',
|
width: 440,
|
||||||
}}>
|
borderRadius: 20,
|
||||||
{/* Logo */}
|
boxShadow: '0 20px 60px rgba(0,0,0,0.08)',
|
||||||
<div style={{ textAlign: 'center', marginBottom: 32 }}>
|
border: '1px solid #e2e8f0',
|
||||||
|
background: '#fff',
|
||||||
|
zIndex: 1,
|
||||||
|
}} styles={{ body: { padding: '36px 28px' } }}>
|
||||||
|
<div style={{ textAlign: 'center', marginBottom: 28 }}>
|
||||||
<div style={{
|
<div style={{
|
||||||
width: 56, height: 56, borderRadius: 14, margin: '0 auto 16px',
|
width: 52,
|
||||||
background: 'linear-gradient(135deg, #6366f1, #8b5cf6)',
|
height: 52,
|
||||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
borderRadius: 14,
|
||||||
boxShadow: '0 8px 24px rgba(99,102,241,0.3)',
|
margin: '0 auto 16px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
background: siteLogo
|
||||||
|
? 'transparent'
|
||||||
|
: 'linear-gradient(135deg, #6366f1, #8b5cf6)',
|
||||||
|
boxShadow: siteLogo ? 'none' : '0 8px 24px rgba(99,102,241,0.25)',
|
||||||
}}>
|
}}>
|
||||||
<ThunderboltOutlined style={{ fontSize: 26, color: '#fff' }} />
|
{siteLogo ? (
|
||||||
|
<img src={siteLogo} alt="logo" style={{ width: '100%', height: '100%', borderRadius: 14, objectFit: 'contain' }} />
|
||||||
|
) : (
|
||||||
|
<ThunderboltOutlined style={{ fontSize: 26, color: '#fff' }} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Typography.Title level={3} style={{ margin: 0 }}>
|
<Typography.Title level={3} style={{ margin: 0, color: '#1e293b', fontWeight: 700 }}>
|
||||||
VideoGen<span style={{ color: '#6366f1' }}>.AI</span>
|
{siteName}
|
||||||
|
<span style={{ color: '#6366f1' }}>.AI</span>
|
||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
<Typography.Text type="secondary">管理后台</Typography.Text>
|
<Typography.Text type="secondary" style={{ fontSize: 14, color: '#94a3b8' }}>管理后台</Typography.Text>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Form onFinish={handleLogin} layout="vertical">
|
<Form onFinish={handleLogin} layout="vertical">
|
||||||
<Form.Item name="username" rules={[{ required: true, message: '请输入用户名' }]}>
|
<Form.Item name="username" rules={[{ required: true, message: '请输入用户名' }]}>
|
||||||
<Input placeholder="用户名" size="large" prefix={<UserOutlined style={{ color: '#94a3b8', marginRight: 8 }} />} />
|
<Input
|
||||||
|
placeholder="用户名"
|
||||||
|
size="large"
|
||||||
|
prefix={<UserOutlined style={{ color: '#94a3b8', marginRight: 8 }} />}
|
||||||
|
style={{
|
||||||
|
background: '#fff',
|
||||||
|
border: '1.5px solid #e2e8f0',
|
||||||
|
color: '#1e293b',
|
||||||
|
height: 48,
|
||||||
|
borderRadius: 10,
|
||||||
|
fontSize: 14,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="password" rules={[{ required: true, message: '请输入密码' }]}>
|
<Form.Item name="password" rules={[{ required: true, message: '请输入密码' }]}>
|
||||||
<Input.Password placeholder="密码" size="large" prefix={<LockOutlined style={{ color: '#94a3b8', marginRight: 8 }} />} />
|
<Input.Password
|
||||||
|
placeholder="密码"
|
||||||
|
size="large"
|
||||||
|
prefix={<LockOutlined style={{ color: '#94a3b8', marginRight: 8 }} />}
|
||||||
|
style={{
|
||||||
|
background: '#fff',
|
||||||
|
border: '1.5px solid #e2e8f0',
|
||||||
|
color: '#1e293b',
|
||||||
|
height: 48,
|
||||||
|
borderRadius: 10,
|
||||||
|
fontSize: 14,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="rememberMe" valuePropName="checked">
|
<Form.Item name="rememberMe" valuePropName="checked" style={{ marginBottom: 12 }}>
|
||||||
<Checkbox>记住我的登录状态</Checkbox>
|
<Checkbox>记住我的登录状态</Checkbox>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item style={{ marginBottom: 8 }}>
|
<Form.Item style={{ marginBottom: 8 }}>
|
||||||
<Button type="primary" htmlType="submit" loading={loading} block size="large"
|
<Button
|
||||||
|
type="primary"
|
||||||
|
htmlType="submit"
|
||||||
|
loading={loading}
|
||||||
|
block
|
||||||
|
size="large"
|
||||||
style={{
|
style={{
|
||||||
borderRadius: 10, fontWeight: 600, height: 44,
|
height: 48,
|
||||||
background: 'linear-gradient(135deg, #6366f1, #8b5cf6)',
|
borderRadius: 10,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: 600,
|
||||||
|
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||||
border: 'none',
|
border: 'none',
|
||||||
}}>
|
boxShadow: '0 8px 24px rgba(99,102,241,0.25)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
登录管理后台
|
登录管理后台
|
||||||
</Button>
|
</Button>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@@ -25,10 +25,27 @@ const LoginPage: React.FC = () => {
|
|||||||
const [sliderKey, setSliderKey] = useState(0);
|
const [sliderKey, setSliderKey] = useState(0);
|
||||||
const [showResend, setShowResend] = useState(false);
|
const [showResend, setShowResend] = useState(false);
|
||||||
const [loginShowResend, setLoginShowResend] = useState(false);
|
const [loginShowResend, setLoginShowResend] = useState(false);
|
||||||
const [siteName, setSiteName] = useState('VideoGen.AI');
|
|
||||||
const [siteLogo, setSiteLogo] = useState('');
|
const getInitialSiteInfo = () => {
|
||||||
|
try {
|
||||||
|
const cached = localStorage.getItem('siteInfo');
|
||||||
|
if (cached) {
|
||||||
|
const info = JSON.parse(cached);
|
||||||
|
return {
|
||||||
|
siteName: info.siteName || 'VideoGen.AI',
|
||||||
|
siteLogo: info.siteLogo || '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch { }
|
||||||
|
return { siteName: 'VideoGen.AI', siteLogo: '' };
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialInfo = getInitialSiteInfo();
|
||||||
|
const [siteName, setSiteName] = useState(initialInfo.siteName);
|
||||||
|
const [siteLogo, setSiteLogo] = useState(initialInfo.siteLogo);
|
||||||
const [agreementUrl, setAgreementUrl] = useState('');
|
const [agreementUrl, setAgreementUrl] = useState('');
|
||||||
const [policyUrl, setPolicyUrl] = useState('');
|
const [policyUrl, setPolicyUrl] = useState('');
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { login } = useAuthStore();
|
const { login } = useAuthStore();
|
||||||
const [pwdForm] = Form.useForm();
|
const [pwdForm] = Form.useForm();
|
||||||
|
|||||||
Reference in New Issue
Block a user