97 lines
3.2 KiB
TypeScript
97 lines
3.2 KiB
TypeScript
import { useState } from 'react'
|
||
import { ShieldCheck, LogIn, Loader2 } from 'lucide-react'
|
||
import { useRole } from '../../context'
|
||
import { api } from '../../api'
|
||
|
||
/**
|
||
* 登录页面 — 支持工号/姓名登录,演示模式可切换角色
|
||
*/
|
||
export function LoginPage() {
|
||
const { login, setRole } = useRole()
|
||
const [code, setCode] = useState('')
|
||
const [loading, setLoading] = useState(false)
|
||
const [error, setError] = useState('')
|
||
|
||
const handleLogin = async (e: React.FormEvent) => {
|
||
e.preventDefault()
|
||
if (!code.trim()) { setError('请输入工号或姓名'); return }
|
||
setLoading(true)
|
||
setError('')
|
||
try {
|
||
const res = await api.auth.login(code.trim())
|
||
login(res.token, res.role, res.name, res.station)
|
||
} catch (err) {
|
||
setError(err instanceof Error ? err.message : '登录失败')
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
|
||
const handleDemo = (role: 'headquarters' | 'station' | 'reporter') => {
|
||
const demoNames: Record<typeof role, { name: string; station: string | null }> = {
|
||
headquarters: { name: '林致远', station: null },
|
||
station: { name: '苏明远', station: '北京记者站' },
|
||
reporter: { name: '林晓', station: '北京记者站' },
|
||
}
|
||
const demo = demoNames[role]
|
||
localStorage.setItem('auth_role', role)
|
||
localStorage.setItem('auth_name', demo.name)
|
||
localStorage.setItem('auth_station', String(demo.station))
|
||
setRole(role)
|
||
}
|
||
|
||
return (
|
||
<div className="login-page">
|
||
<div className="login-card">
|
||
<div className="login-header">
|
||
<div className="login-logo">
|
||
<ShieldCheck size={32} />
|
||
</div>
|
||
<h1>全国记者站管理系统</h1>
|
||
<p>请输入工号或姓名登录</p>
|
||
</div>
|
||
|
||
<form onSubmit={handleLogin} className="login-form">
|
||
<div className="form-field">
|
||
<label htmlFor="code">工号 / 姓名</label>
|
||
<input
|
||
id="code"
|
||
type="text"
|
||
value={code}
|
||
onChange={e => setCode(e.target.value)}
|
||
placeholder="如:PERSON_001 或 林晓"
|
||
disabled={loading}
|
||
autoFocus
|
||
/>
|
||
</div>
|
||
{error && <div className="login-error">{error}</div>}
|
||
<button type="submit" className="login-btn" disabled={loading}>
|
||
{loading ? <Loader2 size={18} className="spin" /> : <LogIn size={18} />}
|
||
<span>{loading ? '登录中...' : '登 录'}</span>
|
||
</button>
|
||
</form>
|
||
|
||
<div className="login-divider">
|
||
<span>或使用演示模式</span>
|
||
</div>
|
||
|
||
<div className="demo-buttons">
|
||
<button onClick={() => handleDemo('reporter')} className="demo-btn">
|
||
记者视角
|
||
</button>
|
||
<button onClick={() => handleDemo('station')} className="demo-btn">
|
||
分站负责人
|
||
</button>
|
||
<button onClick={() => handleDemo('headquarters')} className="demo-btn">
|
||
总部管理员
|
||
</button>
|
||
</div>
|
||
|
||
<div className="login-hint">
|
||
<p>可尝试登录:PERSON_001(记者)、PERSON_006(分站负责人)、PERSON_007(总部管理员)</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|