import { Button, Checkbox, Form, Input, message, Typography, Space } from "antd"; import { useEffect, useState, useCallback } from "react"; import { useTranslation } from "react-i18next"; import { fetchCaptcha, login, type CaptchaResponse } from "../api/auth"; import { getCurrentUser, getSystemParamValue, getOpenPlatformConfig } from "../api"; import { UserOutlined, LockOutlined, SafetyOutlined, ReloadOutlined, ShopOutlined } from "@ant-design/icons"; import type { SysPlatformConfig } from "../types"; import "./Login.css"; const { Title, Text, Link } = Typography; export default function Login() { const { t } = useTranslation(); const [captcha, setCaptcha] = useState(null); const [captchaEnabled, setCaptchaEnabled] = useState(true); const [loading, setLoading] = useState(false); const [platformConfig, setPlatformConfig] = useState(null); const [form] = Form.useForm(); const parseJwtPayload = (token: string) => { try { const payloadPart = token.split(".")[1]; if (!payloadPart) return null; const normalized = payloadPart.replace(/-/g, "+").replace(/_/g, "/"); const padded = normalized + "=".repeat((4 - (normalized.length % 4)) % 4); return JSON.parse(atob(padded)); } catch (e) { return null; } }; const loadCaptcha = useCallback(async () => { if (!captchaEnabled) { return; } try { const data = await fetchCaptcha(); setCaptcha(data); } catch (e) { // Handled by interceptor } }, [captchaEnabled]); useEffect(() => { const init = async () => { try { const [captchaVal, pConfig] = await Promise.all([ getSystemParamValue("security.captcha.enabled", "true"), getOpenPlatformConfig() ]); setPlatformConfig(pConfig); const enabled = captchaVal !== "false"; setCaptchaEnabled(enabled); if (enabled) { loadCaptcha(); } } catch (e) { setCaptchaEnabled(true); loadCaptcha(); } }; init(); }, [loadCaptcha]); useEffect(() => { const searchParams = new URLSearchParams(window.location.search); if (searchParams.get("timeout") === "1") { message.warning(t('login.loginTimeout')); // Clean up the URL to avoid repeated messages on refresh window.history.replaceState({}, document.title, window.location.pathname); } }, [t]); 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); if (data.availableTenants) { localStorage.setItem("availableTenants", JSON.stringify(data.availableTenants)); } const payload = parseJwtPayload(data.accessToken); if (payload?.tenantId !== undefined && payload?.tenantId !== null) { localStorage.setItem("activeTenantId", String(payload.tenantId)); } try { const profile = await getCurrentUser(); sessionStorage.setItem("userProfile", JSON.stringify(profile)); } catch (e) { if (data.pwdResetRequired === 0 || data.pwdResetRequired === 1) { sessionStorage.setItem("userProfile", JSON.stringify({ pwdResetRequired: data.pwdResetRequired })); } else { sessionStorage.removeItem("userProfile"); } } message.success(t('common.success')); window.location.href = "/"; } catch (e: any) { if (captchaEnabled) { loadCaptcha(); } } finally { setLoading(false); } }; const loginStyle = platformConfig?.loginBgUrl ? { backgroundImage: `url(${platformConfig.loginBgUrl})`, backgroundSize: 'cover', backgroundPosition: 'center', position: 'relative' as const } : {}; // 如果设置了背景图,左侧和右侧的背景应该透明或者半透明 const leftStyle = platformConfig?.loginBgUrl ? { ...loginStyle, background: 'rgba(255, 255, 255, 0.2)', backdropFilter: 'blur(10px)', } : {}; const rightStyle = platformConfig?.loginBgUrl ? { background: 'rgba(255, 255, 255, 0.85)', backdropFilter: 'blur(20px)', } : {}; return (
Logo {platformConfig?.projectName || "MeetingAI"}

{t('login.heroTitle1')}
{t('login.heroTitle2')}
{t('login.heroTitle3')}

{platformConfig?.systemDescription || t('login.heroDesc')}

{t('login.enterpriseSecurity')}
{t('login.welcome')} {t('login.subtitle')}
} placeholder={t('login.username')} autoComplete="username" spellCheck={false} aria-label={t('login.username')} /> {captchaEnabled && (
} placeholder={t('login.captcha')} maxLength={6} aria-label={t('login.captcha')} />
)}
{t('login.rememberMe')} {t('login.forgotPassword')}
{t('login.demoAccount')}:admin / {t('login.password')}:123456
); }