73 lines
1.9 KiB
JavaScript
73 lines
1.9 KiB
JavaScript
import React, { useState } from 'react'
|
||
import { api } from '../services/api'
|
||
import './Login.css'
|
||
|
||
const Login = ({ onLoggedIn }) => {
|
||
const [username, setUsername] = useState('')
|
||
const [password, setPassword] = useState('')
|
||
const [busy, setBusy] = useState(false)
|
||
const [error, setError] = useState('')
|
||
|
||
const doLogin = async () => {
|
||
setBusy(true)
|
||
setError('')
|
||
try {
|
||
await api.login(username, password)
|
||
if (typeof onLoggedIn === 'function') await onLoggedIn()
|
||
} catch (e) {
|
||
setError(e?.message || '登录失败')
|
||
} finally {
|
||
setBusy(false)
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div className="login-page">
|
||
<div className="login-card">
|
||
<div className="login-title">自动交易系统</div>
|
||
<div className="login-subtitle">请先登录</div>
|
||
|
||
<label className="login-field">
|
||
<div className="login-label">用户名</div>
|
||
<input
|
||
className="login-input"
|
||
value={username}
|
||
onChange={(e) => setUsername(e.target.value)}
|
||
placeholder="例如:admin"
|
||
autoComplete="username"
|
||
/>
|
||
</label>
|
||
|
||
<label className="login-field">
|
||
<div className="login-label">密码</div>
|
||
<input
|
||
className="login-input"
|
||
type="password"
|
||
value={password}
|
||
onChange={(e) => setPassword(e.target.value)}
|
||
placeholder="请输入密码"
|
||
autoComplete="current-password"
|
||
onKeyDown={(e) => {
|
||
if (e.key === 'Enter') doLogin()
|
||
}}
|
||
/>
|
||
</label>
|
||
|
||
{error ? <div className="login-error">{error}</div> : null}
|
||
|
||
<button
|
||
type="button"
|
||
className="login-btn"
|
||
disabled={busy || !username.trim() || !password}
|
||
onClick={doLogin}
|
||
>
|
||
{busy ? '登录中...' : '登录'}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default Login
|
||
|