This repository has been archived on 2026-09-13. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
BPOS-TMP-REPO/apps/web/src/features/auth/internal/LoginPage.tsx
T

86 lines
2.8 KiB
TypeScript

/**
* 登录页(ADR-010)—— 两份原型都没有这一屏,是新造的。
*
* 它在两个台面**之前**渲染,拿不到任一台面的皮肤(工作台补充样式通篇作用域 `.proto-root`,
* 治理台压根不加载它),所以视觉全部由 `login.css` 自带 —— 类名一律 `jc-login-*`,
* 不蹭原型的类,免得在另一台面上落空。
*/
import { useState } from 'react';
import type { Session } from '@contracts';
import { loginRoute } from '@contracts';
import { Ico } from '@web-shared/Ico.tsx';
import { ApiCallError, call } from '@web-shared/typed-fetch.ts';
export function LoginPage({ onLogin }: { onLogin(session: Session): void }) {
const [account, setAccount] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState<string>();
const [busy, setBusy] = useState(false);
const ready = account.trim() !== '' && password !== '';
const submit = async () => {
if (!ready || busy) return;
setBusy(true);
setError(undefined);
try {
onLogin(await call<Session>(loginRoute, { body: { account: account.trim(), password } }));
} catch (e) {
// 后端只回一句话(不区分账号不存在 / 已停用 / 口令错),这里照实呈现,不替它猜
setError(e instanceof ApiCallError ? e.message : '登录失败,请稍后再试');
setBusy(false);
}
};
return (
<div className="jc-login">
<form
className="jc-login-card"
onSubmit={(e) => {
e.preventDefault();
void submit();
}}
>
<span className="jc-login-mark">
<Ico name="bot" s={24} />
</span>
<h1 className="jc-login-title">JCAI 任务操作系统</h1>
<p className="jc-login-sub">登录后进入你的工位</p>
<label className="jc-login-field">
<span>账号</span>
<input
value={account}
autoFocus
autoComplete="username"
placeholder="如 fengbing"
onChange={(e) => setAccount(e.target.value)}
/>
</label>
<label className="jc-login-field">
<span>口令</span>
<input
type="password"
value={password}
autoComplete="current-password"
placeholder="口令"
onChange={(e) => setPassword(e.target.value)}
/>
</label>
{error !== undefined && (
<div className="jc-login-err" role="alert">
<Ico name="alert-circle" s={15} />
<p>{error}</p>
</div>
)}
<button className="jc-login-go" type="submit" disabled={busy || !ready}>
{busy ? '登录中…' : '登录'}
</button>
<p className="jc-login-hint">初始口令由部署时的 JCAI_SEED_PASSWORD 决定</p>
</form>
</div>
);
}