fix(css): fix version

This commit is contained in:
2026-02-10 12:23:47 +08:00
parent 8c033066af
commit 2696ec8692
21 changed files with 658 additions and 194 deletions

View File

@@ -0,0 +1,150 @@
import * as React from "react"
import Link from "next/link"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { CaptchaField } from "@/components/auth/captcha-field"
export function LoginForm(props: {
clientId: string
tenantId: string
callback: string
initialEmail: string
prefillPassword?: string
disabled?: boolean
externalError?: string | null
onClearExternalError?: () => void
captcha: string
onCaptchaChange: (next: string) => void
captchaKey: string
onRefreshCaptcha: () => void
}) {
const [email, setEmail] = React.useState(props.initialEmail)
const [password, setPassword] = React.useState("")
const [rememberMe, setRememberMe] = React.useState(Boolean(props.initialEmail))
const [submitting, setSubmitting] = React.useState(false)
const [error, setError] = React.useState<string | null>(null)
React.useEffect(() => {
setEmail(props.initialEmail)
setRememberMe(Boolean(props.initialEmail))
}, [props.initialEmail])
React.useEffect(() => {
if (props.prefillPassword) {
setPassword(props.prefillPassword)
}
}, [props.prefillPassword])
const missingParams = !props.clientId || !props.tenantId || !props.callback
async function onSubmit(e: React.FormEvent) {
e.preventDefault()
setError(null)
props.onClearExternalError?.()
setSubmitting(true)
try {
const res = await fetch("/api/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
clientId: props.clientId,
tenantId: props.tenantId,
callback: props.callback,
email,
password,
captcha: props.captcha,
rememberMe,
}),
})
const json = (await res.json()) as { redirectTo?: string; message?: string }
if (!res.ok || !json.redirectTo) {
throw new Error(json.message || "登录失败")
}
window.location.href = json.redirectTo
} catch (err) {
setError(err instanceof Error ? err.message : "登录失败")
props.onCaptchaChange("")
props.onRefreshCaptcha()
} finally {
setSubmitting(false)
}
}
const disabled = Boolean(props.disabled) || submitting
return (
<form onSubmit={onSubmit} className="space-y-4">
{missingParams ? (
<div className="text-sm text-destructive">
clientIdtenantId callback
</div>
) : null}
{props.externalError ? (
<div className="text-sm text-destructive">{props.externalError}</div>
) : null}
{error ? <div className="text-sm text-destructive">{error}</div> : null}
<div className="space-y-2">
<Label htmlFor="email"></Label>
<Input
id="email"
autoComplete="username"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="user@example.com"
disabled={disabled}
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="password"></Label>
<Input
id="password"
type="password"
autoComplete="current-password"
value={password}
onChange={(e) => setPassword(e.target.value)}
disabled={disabled}
required
/>
</div>
<CaptchaField
captcha={props.captcha}
onCaptchaChange={props.onCaptchaChange}
captchaKey={props.captchaKey}
onRefresh={props.onRefreshCaptcha}
disabled={disabled}
/>
<div className="flex items-center justify-between">
<label className="flex items-center gap-2 text-sm">
<Checkbox
checked={rememberMe}
onCheckedChange={(v) => setRememberMe(Boolean(v))}
disabled={disabled}
/>
</label>
<Link
className="text-sm underline underline-offset-4"
href={`/forgot-password?tenantId=${encodeURIComponent(props.tenantId)}`}
>
</Link>
</div>
<Button
type="submit"
className="w-full"
disabled={disabled || missingParams}
>
{submitting ? "登录中..." : "登录"}
</Button>
</form>
)
}