Commit 4a9044d7eda186b25b03743dad481e5384be46c5

Authored by zichun
1 parent 24664be0

feat(usr): rewrite LoginPage visual to match prototype REQ-USR-004

frontend/src/pages/usr/LoginPage.tsx
1 import { useEffect, useState } from 'react' 1 import { useEffect, useState } from 'react'
2 import { useNavigate } from 'react-router-dom' 2 import { useNavigate } from 'react-router-dom'
3 -import { Button, Form, Input, message, Select } from 'antd'  
4 -import { getBrands, login, BrandVO } from '../../api/auth' 3 +import { Button, Form, Input, Select, message } from 'antd'
  4 +import { getBrands, login, type BrandVO } from '../../api/auth'
5 import { setCredentials } from '../../store/slices/authSlice' 5 import { setCredentials } from '../../store/slices/authSlice'
  6 +import { activateTab } from '../../store/slices/tabsSlice'
6 import { useAppDispatch } from '../../store/hooks' 7 import { useAppDispatch } from '../../store/hooks'
7 8
  9 +const ANTLER_PATHS = [
  10 + 'M14 10c2 4 1 8-1 11 3-1 7 0 10 3 1-4 4-7 8-7-3 3-4 7-3 11l4 1c-1 3 0 6 3 8-3 0-6 1-8 4-1-3-4-5-8-5 2-3 2-7 0-10-3 1-7 0-10-3 3 0 5-2 6-5l-1-8z',
  11 + 'M48 14c-2 3-2 6-1 9-2-2-5-2-8-1 1 3 1 6-1 9 3 0 5 2 6 5 1-3 4-5 7-5-2-3-2-6 0-9 2 1 5 1 7-1-2 0-4-1-5-3-1-2-3-4-5-4z',
  12 + 'M28 38c2 3 5 5 9 5 1 4 4 7 8 8-3 2-5 5-5 9-3-2-7-3-11-2 1-3 1-7-1-10-3 0-6-1-8-4 3-1 6-3 8-6z',
  13 +]
  14 +
8 export default function LoginPage() { 15 export default function LoginPage() {
9 const [brandOptions, setBrandOptions] = useState<BrandVO[]>([]) 16 const [brandOptions, setBrandOptions] = useState<BrandVO[]>([])
10 const [loading, setLoading] = useState(false) 17 const [loading, setLoading] = useState(false)
@@ -19,17 +26,14 @@ export default function LoginPage() { @@ -19,17 +26,14 @@ export default function LoginPage() {
19 const defaultNo = std ? std.sNo : brands[0]?.sNo 26 const defaultNo = std ? std.sNo : brands[0]?.sNo
20 if (defaultNo) form.setFieldValue('brandNo', defaultNo) 27 if (defaultNo) form.setFieldValue('brandNo', defaultNo)
21 }).catch(() => {}) 28 }).catch(() => {})
22 - }, []) 29 + }, [form])
23 30
24 const onFinish = async (values: { brandNo: string; username: string; password: string }) => { 31 const onFinish = async (values: { brandNo: string; username: string; password: string }) => {
25 setLoading(true) 32 setLoading(true)
26 try { 33 try {
27 const result = await login(values) 34 const result = await login(values)
28 - dispatch(setCredentials({  
29 - accessToken: result.accessToken,  
30 - refreshToken: result.refreshToken,  
31 - userInfo: result.userInfo  
32 - })) 35 + dispatch(setCredentials({ accessToken: result.accessToken, refreshToken: result.refreshToken, userInfo: result.userInfo }))
  36 + dispatch(activateTab('main'))
33 navigate('/') 37 navigate('/')
34 } catch (e: unknown) { 38 } catch (e: unknown) {
35 message.error(e instanceof Error ? e.message : '登录失败') 39 message.error(e instanceof Error ? e.message : '登录失败')
@@ -39,28 +43,49 @@ export default function LoginPage() { @@ -39,28 +43,49 @@ export default function LoginPage() {
39 } 43 }
40 44
41 return ( 45 return (
42 - <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '100vh' }}>  
43 - <div style={{ width: 360 }}>  
44 - <h2 style={{ textAlign: 'center', marginBottom: 24 }}>小羚羊 ERP</h2>  
45 - <Form form={form} layout="vertical" onFinish={onFinish}>  
46 - <Form.Item label="公司/版本" name="brandNo" rules={[{ required: true, message: '请选择公司/版本' }]}>  
47 - <Select  
48 - options={brandOptions.map(b => ({ value: b.sNo, label: b.sName }))}  
49 - placeholder="请选择公司/版本"  
50 - />  
51 - </Form.Item>  
52 - <Form.Item label="用户名" name="username" rules={[{ required: true, message: '请输入用户名' }]}>  
53 - <Input placeholder="请输入用户名" />  
54 - </Form.Item>  
55 - <Form.Item label="密码" name="password" rules={[{ required: true, message: '请输入密码' }]}>  
56 - <Input.Password placeholder="请输入密码" />  
57 - </Form.Item>  
58 - <Form.Item>  
59 - <Button type="primary" htmlType="submit" loading={loading} block>  
60 - 登录  
61 - </Button>  
62 - </Form.Item>  
63 - </Form> 46 + <div style={{ position: 'absolute', inset: 0, background: '#eaedf2', display: 'flex', flexDirection: 'column' }}>
  47 + {/* Header */}
  48 + <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '18px 36px' }}>
  49 + <div style={{ width: 42, height: 42, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
  50 + <svg viewBox="0 0 64 64" width={42} height={42} fill="#0e1216">
  51 + {ANTLER_PATHS.map((d, i) => <path key={i} d={d} />)}
  52 + </svg>
  53 + </div>
  54 + <span style={{ fontSize: 24, fontWeight: 700, color: '#e0a020', letterSpacing: 2 }}>Antler ERP</span>
  55 + <span style={{ color: '#444', fontSize: 14, marginLeft: 6 }}>欢迎登录EBC平台</span>
  56 + </div>
  57 + {/* Hero */}
  58 + <div style={{ flex: 1, position: 'relative', background: 'radial-gradient(ellipse at center, #1a4ea0 0%, #0a1d44 60%, #050d20 100%)', overflow: 'hidden' }}>
  59 + {/* Brand text */}
  60 + <div style={{ position: 'absolute', left: '8%', top: '35%', color: '#fff', zIndex: 2 }}>
  61 + <div style={{ fontSize: 30, fontWeight: 300, color: '#cfe1ff', marginBottom: 6 }}>Enterprise Business Capability</div>
  62 + <div style={{ fontSize: 54, fontWeight: 700, letterSpacing: 4, marginBottom: 4 }}>企业业务能力平台</div>
  63 + <div style={{ fontSize: 90, fontWeight: 800, letterSpacing: 8, lineHeight: 0.9 }}>ERP</div>
  64 + </div>
  65 + {/* Login card */}
  66 + <div style={{ position: 'absolute', right: '8%', top: '50%', transform: 'translateY(-50%)', background: '#fff', width: 380, padding: '36px 32px', borderRadius: 2, boxShadow: '0 12px 40px rgba(0,0,0,.3)', zIndex: 3 }}>
  67 + <h3 style={{ margin: '0 0 22px', fontSize: 18, color: '#333', fontWeight: 500 }}>用户登录</h3>
  68 + <Form form={form} layout="vertical" onFinish={onFinish}>
  69 + <Form.Item label="公司/版本" name="brandNo" rules={[{ required: true, message: '请选择公司/版本' }]}>
  70 + <Select options={brandOptions.map(b => ({ value: b.sNo, label: b.sName }))} placeholder="请选择公司/版本" />
  71 + </Form.Item>
  72 + <Form.Item label="用户名" name="username" rules={[{ required: true, message: '请输入用户名' }]}>
  73 + <Input placeholder="请输入用户名" />
  74 + </Form.Item>
  75 + <Form.Item label="密码" name="password" rules={[{ required: true, message: '请输入密码' }]}>
  76 + <Input.Password placeholder="请输入密码" />
  77 + </Form.Item>
  78 + <Form.Item>
  79 + <Button type="primary" htmlType="submit" loading={loading} block style={{ height: 42, fontSize: 15, letterSpacing: 8, borderRadius: 2 }}>
  80 + 登 录
  81 + </Button>
  82 + </Form.Item>
  83 + </Form>
  84 + </div>
  85 + </div>
  86 + {/* Footer */}
  87 + <div style={{ background: '#eaedf2', textAlign: 'center', padding: '14px 8px', color: '#666', fontSize: 12, borderTop: '1px solid #d8dce2' }}>
  88 + 🛠 ©Copyright Antler Software | 印刷ERP | 400-880-6237
64 </div> 89 </div>
65 </div> 90 </div>
66 ) 91 )