imeeting/frontend/src/pages/Login.tsx

206 lines
6.8 KiB
TypeScript
Raw Normal View History

import { Button, Checkbox, Form, Input, message, Typography } from "antd";
import { useEffect, useState, useCallback } from "react";
import { fetchCaptcha, login, type CaptchaResponse } from "../api/auth";
import { getCurrentUser, getSystemParamValue } from "../api";
import { UserOutlined, LockOutlined, SafetyOutlined, ReloadOutlined, ShopOutlined } from "@ant-design/icons";
import "./Login.css";
const { Title, Text, Link } = Typography;
export default function Login() {
const [captcha, setCaptcha] = useState<CaptchaResponse | null>(null);
const [captchaEnabled, setCaptchaEnabled] = useState(true);
const [loading, setLoading] = useState(false);
const [form] = Form.useForm();
const loadCaptcha = useCallback(async () => {
if (!captchaEnabled) {
return;
}
try {
const data = await fetchCaptcha();
setCaptcha(data);
} catch (e) {
message.error("加载验证码失败");
}
}, [captchaEnabled]);
useEffect(() => {
const init = async () => {
try {
const value = await getSystemParamValue("security.captcha.enabled", "true");
const enabled = value !== "false";
setCaptchaEnabled(enabled);
if (enabled) {
loadCaptcha();
}
} catch (e) {
setCaptchaEnabled(true);
loadCaptcha();
}
};
init();
}, [loadCaptcha]);
const onFinish = async (values: any) => {
setLoading(true);
try {
const data = await login({
username: values.username,
password: values.password,
tenantCode: values.tenantCode,
captchaId: captchaEnabled ? captcha?.captchaId : undefined,
captchaCode: captchaEnabled ? values.captchaCode : undefined
});
localStorage.setItem("accessToken", data.accessToken);
localStorage.setItem("refreshToken", data.refreshToken);
localStorage.setItem("username", values.username);
try {
const profile = await getCurrentUser();
sessionStorage.setItem("userProfile", JSON.stringify(profile));
} catch (e) {
sessionStorage.removeItem("userProfile");
}
message.success("登录成功");
window.location.href = "/";
} catch (e: any) {
message.error(e.message || "登录失败");
if (captchaEnabled) {
loadCaptcha();
}
} finally {
setLoading(false);
}
};
return (
<div className="login-page">
<div className="login-left">
<div className="login-brand">
<img src="/logo.svg" alt="MeetingAI Logo" className="brand-logo-img" />
<span className="brand-name">MeetingAI</span>
</div>
<div className="login-hero">
<h1 className="hero-title">
<br />
<span className="hero-accent"></span><br />
</h1>
<p className="hero-desc">
<br />
</p>
</div>
<div className="login-left-footer">
<div className="footer-item"></div>
<div className="footer-divider" aria-hidden="true" />
<div className="footer-item"></div>
</div>
</div>
<div className="login-right">
<div className="login-container">
<div className="login-header">
<Title level={2}></Title>
<Text type="secondary"></Text>
</div>
<Form
form={form}
layout="vertical"
onFinish={onFinish}
className="login-form"
requiredMark={false}
autoComplete="off"
>
<Form.Item
name="tenantCode"
rules={[{ required: false }]}
>
<Input
size="large"
prefix={<ShopOutlined className="text-gray-400" aria-hidden="true" />}
placeholder="租户编码 (平台管理可留空)"
aria-label="租户编码"
/>
</Form.Item>
<Form.Item
name="username"
rules={[{ required: true, message: "请输入用户名" }]}
>
<Input
size="large"
prefix={<UserOutlined className="text-gray-400" aria-hidden="true" />}
placeholder="用户名"
autoComplete="username"
spellCheck={false}
aria-label="用户名"
/>
</Form.Item>
<Form.Item
name="password"
rules={[{ required: true, message: "请输入密码" }]}
>
<Input.Password
size="large"
prefix={<LockOutlined className="text-gray-400" aria-hidden="true" />}
placeholder="密码"
autoComplete="current-password"
aria-label="密码"
/>
</Form.Item>
{captchaEnabled && (
<Form.Item
name="captchaCode"
rules={[{ required: true, message: "请输入验证码" }]}
>
<div className="captcha-wrapper">
<Input
size="large"
prefix={<SafetyOutlined className="text-gray-400" aria-hidden="true" />}
placeholder="验证码"
maxLength={6}
aria-label="验证码"
/>
<Button
className="captcha-image-btn"
onClick={loadCaptcha}
icon={!captcha ? <ReloadOutlined spin /> : null}
aria-label="点击刷新验证码"
>
{captcha && <img src={captcha.imageBase64} alt="验证码图片" />}
</Button>
</div>
</Form.Item>
)}
<div className="login-extra">
<Form.Item name="remember" valuePropName="checked" noStyle>
<Checkbox></Checkbox>
</Form.Item>
<Link className="forgot-password"></Link>
</div>
<Form.Item>
<Button type="primary" htmlType="submit" loading={loading} block size="large" className="login-submit-btn">
{loading ? "登录中…" : "立即登录"}
</Button>
</Form.Item>
</Form>
<div className="login-footer">
<Text type="secondary">
<Text strong className="tabular-nums">admin</Text> / <Text strong className="tabular-nums">123456</Text>
</Text>
</div>
</div>
</div>
</div>
);
}