221 lines
7.0 KiB
TypeScript
221 lines
7.0 KiB
TypeScript
import React, { useState } from 'react'
|
|
import { useNavigate } from 'react-router-dom'
|
|
import {
|
|
Card,
|
|
Form,
|
|
Input,
|
|
Button,
|
|
Typography,
|
|
Space,
|
|
Alert,
|
|
Flex,
|
|
Divider
|
|
} from 'antd'
|
|
import {
|
|
UserOutlined,
|
|
LockOutlined,
|
|
DashboardOutlined,
|
|
DollarOutlined,
|
|
ProjectOutlined,
|
|
TeamOutlined
|
|
} from '@ant-design/icons'
|
|
import { useAuthStore } from '../../store/authStore'
|
|
import { useLanguageStore } from '../../store/languageStore'
|
|
import LanguageSelector from '../../components/common/LanguageSelector'
|
|
|
|
const { Title, Text } = Typography
|
|
|
|
const LoginPage: React.FC = () => {
|
|
const navigate = useNavigate()
|
|
const [form] = Form.useForm()
|
|
const [loading, setLoading] = useState(false)
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
const { login } = useAuthStore()
|
|
const { t } = useLanguageStore()
|
|
|
|
const handleSubmit = async (values: { username: string; password: string }) => {
|
|
setLoading(true)
|
|
setError(null)
|
|
|
|
try {
|
|
await login(values.username, values.password)
|
|
navigate('/dashboard')
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : t('login.loginFailed'))
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
// 测试账户
|
|
const testAccounts = [
|
|
{ username: 'admin', password: 'X123c321@', role: t('user.admin') },
|
|
{ username: 'finance', password: 'X123c321@', role: t('user.finance') },
|
|
{ username: 'manager', password: 'X123c321@', role: t('user.manager') },
|
|
{ username: 'employee', password: 'X123c321@', role: t('user.employee') }
|
|
]
|
|
|
|
const handleTestLogin = (username: string, password: string) => {
|
|
form.setFieldsValue({ username, password })
|
|
form.submit()
|
|
}
|
|
|
|
return (
|
|
<div style={{
|
|
minHeight: '100vh',
|
|
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
padding: '20px'
|
|
}}>
|
|
<Card
|
|
className="login-card"
|
|
style={{
|
|
width: '100%',
|
|
maxWidth: 480,
|
|
borderRadius: 16,
|
|
boxShadow: '0 20px 60px rgba(0,0,0,0.3)'
|
|
}}
|
|
styles={{ body: { padding: 40 } }}
|
|
>
|
|
<Space direction="vertical" size="large" style={{ width: '100%' }}>
|
|
{/* 标题 */}
|
|
<div style={{ textAlign: 'center' }}>
|
|
<Title level={2} style={{ marginBottom: 8 }}>
|
|
<DashboardOutlined style={{ marginRight: 12, color: '#1890ff' }} />
|
|
{t('login.title')}
|
|
</Title>
|
|
<Text type="secondary">{t('login.subtitle')}</Text>
|
|
</div>
|
|
|
|
{/* 语言选择器 V2.0 */}
|
|
<div style={{
|
|
textAlign: 'center',
|
|
padding: '12px',
|
|
background: '#f0f2f5',
|
|
borderRadius: '8px',
|
|
border: '2px solid #1890ff'
|
|
}}>
|
|
<div style={{ marginBottom: 8, color: '#1890ff', fontWeight: 'bold' }}>
|
|
🌍 选择语言 / Select Language
|
|
</div>
|
|
<LanguageSelector size="large" style={{ width: '200px' }} />
|
|
</div>
|
|
|
|
{/* 错误提示 */}
|
|
{error && (
|
|
<Alert
|
|
message={error}
|
|
type="error"
|
|
showIcon
|
|
closable
|
|
onClose={() => setError(null)}
|
|
/>
|
|
)}
|
|
|
|
{/* 登录表单 */}
|
|
<Form
|
|
form={form}
|
|
layout="vertical"
|
|
onFinish={handleSubmit}
|
|
autoComplete="off"
|
|
>
|
|
<Form.Item
|
|
name="username"
|
|
label={t('login.username')}
|
|
rules={[
|
|
{ required: true, message: t('login.usernameRequired') },
|
|
{ min: 3, message: t('login.usernameMin') }
|
|
]}
|
|
>
|
|
<Input
|
|
prefix={<UserOutlined />}
|
|
placeholder={t('login.usernamePlaceholder')}
|
|
size="large"
|
|
/>
|
|
</Form.Item>
|
|
|
|
<Form.Item
|
|
name="password"
|
|
label={t('login.password')}
|
|
rules={[
|
|
{ required: true, message: t('login.passwordRequired') },
|
|
{ min: 6, message: t('login.passwordMin') }
|
|
]}
|
|
>
|
|
<Input.Password
|
|
prefix={<LockOutlined />}
|
|
placeholder={t('login.passwordPlaceholder')}
|
|
size="large"
|
|
/>
|
|
</Form.Item>
|
|
|
|
<Form.Item>
|
|
<Button
|
|
type="primary"
|
|
htmlType="submit"
|
|
loading={loading}
|
|
size="large"
|
|
block
|
|
>
|
|
{t('login.loginButton')}
|
|
</Button>
|
|
</Form.Item>
|
|
</Form>
|
|
|
|
<Divider>{t('login.testAccounts')}</Divider>
|
|
|
|
{/* 测试账户 */}
|
|
<Space direction="vertical" style={{ width: '100%' }}>
|
|
{testAccounts.map((account, index) => (
|
|
<Card
|
|
key={index}
|
|
size="small"
|
|
hoverable
|
|
onClick={() => handleTestLogin(account.username, account.password)}
|
|
style={{ cursor: 'pointer' }}
|
|
>
|
|
<Flex justify="space-between" align="center">
|
|
<Space>
|
|
{account.role === t('user.admin') && <DashboardOutlined style={{ color: '#1890ff' }} />}
|
|
{account.role === t('user.finance') && <DollarOutlined style={{ color: '#52c41a' }} />}
|
|
{account.role === t('user.manager') && <ProjectOutlined style={{ color: '#fa8c16' }} />}
|
|
{account.role === t('user.employee') && <TeamOutlined style={{ color: '#722ed1' }} />}
|
|
<Text strong>{account.role}</Text>
|
|
</Space>
|
|
<Text type="secondary">
|
|
{t('login.username')}: {account.username} / {t('login.password')}: {account.password}
|
|
</Text>
|
|
</Flex>
|
|
</Card>
|
|
))}
|
|
</Space>
|
|
|
|
{/* 功能说明 */}
|
|
<Card size="small" type="inner">
|
|
<Space direction="vertical" size="small" style={{ width: '100%' }}>
|
|
<Text strong>{t('menu.dashboard')}:</Text>
|
|
<Text type="secondary">• {t('features.projectManage')}</Text>
|
|
<Text type="secondary">• {t('features.advanceManage')}</Text>
|
|
<Text type="secondary">• {t('features.reimburseManage')}</Text>
|
|
<Text type="secondary">• {t('features.financeReport')}</Text>
|
|
<Text type="secondary">• {t('features.mobileSupport')}</Text>
|
|
</Space>
|
|
</Card>
|
|
|
|
{/* 技术支持 */}
|
|
<div style={{ textAlign: 'center', marginTop: 20 }}>
|
|
<Text type="secondary">
|
|
{t('login.techSupport')}
|
|
</Text>
|
|
</div>
|
|
</Space>
|
|
</Card>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default LoginPage
|