Initial commit: ERP system with advance verification fixes
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
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
|
||||
Reference in New Issue
Block a user