fix captcha show

This commit is contained in:
songtianlun 2025-06-14 21:34:28 +08:00
parent fe4fbf0ea1
commit f779a277a8
2 changed files with 113 additions and 77 deletions

View File

@ -58,7 +58,12 @@ function SignInContent() {
const handleEmailSignInClick = (e: React.FormEvent) => { const handleEmailSignInClick = (e: React.FormEvent) => {
e.preventDefault() e.preventDefault()
if (!email) return if (!email.trim()) return
// 清除之前的消息
setMessage("")
// 显示验证码弹窗
setShowCaptcha(true) setShowCaptcha(true)
} }
@ -68,7 +73,7 @@ function SignInContent() {
try { try {
const result = await signIn("nodemailer", { const result = await signIn("nodemailer", {
email, email: email.trim(),
redirect: false, redirect: false,
}) })
@ -78,6 +83,7 @@ function SignInContent() {
setMessage("登录链接已发送到您的邮箱,请查收") setMessage("登录链接已发送到您的邮箱,请查收")
} }
} catch (error) { } catch (error) {
console.error("Email sign in error:", error)
setMessage("登录失败,请重试") setMessage("登录失败,请重试")
} finally { } finally {
setIsLoading(false) setIsLoading(false)
@ -86,14 +92,24 @@ function SignInContent() {
const handleCaptchaCancel = () => { const handleCaptchaCancel = () => {
setMessage("") setMessage("")
setIsLoading(false)
}
const handleCaptchaOpenChange = (open: boolean) => {
setShowCaptcha(open)
if (!open) {
setIsLoading(false)
}
} }
const handleGoogleSignIn = async () => { const handleGoogleSignIn = async () => {
setIsLoading(true) setIsLoading(true)
setMessage("")
try { try {
const callbackUrl = searchParams.get("callbackUrl") || "/" const callbackUrl = searchParams.get("callbackUrl") || "/"
await signIn("google", { callbackUrl }) await signIn("google", { callbackUrl })
} catch (error) { } catch (error) {
console.error("Google sign in error:", error)
setMessage("Google登录失败请重试") setMessage("Google登录失败请重试")
setIsLoading(false) setIsLoading(false)
} }
@ -145,12 +161,13 @@ function SignInContent() {
onChange={(e) => setEmail(e.target.value)} onChange={(e) => setEmail(e.target.value)}
required required
className="bg-background border-input" className="bg-background border-input"
disabled={isLoading}
/> />
</div> </div>
<Button <Button
type="submit" type="submit"
className="w-full" className="w-full"
disabled={isLoading || !email} disabled={isLoading || !email.trim()}
> >
<Mail className="mr-2 h-4 w-4" /> <Mail className="mr-2 h-4 w-4" />
{isLoading ? "发送中..." : "发送登录链接"} {isLoading ? "发送中..." : "发送登录链接"}
@ -169,7 +186,7 @@ function SignInContent() {
{/* 验证码弹窗 */} {/* 验证码弹窗 */}
<CaptchaDialog <CaptchaDialog
open={showCaptcha} open={showCaptcha}
onOpenChange={setShowCaptcha} onOpenChange={handleCaptchaOpenChange}
onVerify={handleCaptchaVerify} onVerify={handleCaptchaVerify}
onCancel={handleCaptchaCancel} onCancel={handleCaptchaCancel}
/> />

View File

@ -8,12 +8,15 @@ import {
DialogFooter, DialogFooter,
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
DialogPortal,
DialogOverlay,
} from "@/components/ui/dialog" } from "@/components/ui/dialog"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input" import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label" import { Label } from "@/components/ui/label"
import { Alert, AlertDescription } from "@/components/ui/alert" import { Alert, AlertDescription } from "@/components/ui/alert"
import { Shield, RefreshCw, AlertCircle } from "lucide-react" import { Shield, RefreshCw, AlertCircle } from "lucide-react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
interface CaptchaDialogProps { interface CaptchaDialogProps {
open: boolean open: boolean
@ -76,88 +79,104 @@ export function CaptchaDialog({
} }
} }
// 防止第一次打开时的遮罩层问题
const handleOpenChange = (newOpen: boolean) => {
if (!newOpen) {
setUserAnswer("")
setError("")
onCancel()
}
onOpenChange(newOpen)
}
return ( return (
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={handleOpenChange}>
<DialogContent className="sm:max-w-md"> <DialogPortal>
<DialogHeader> <DialogOverlay className="fixed inset-0 z-[100] bg-black/80" />
<DialogTitle className="flex items-center gap-2 text-foreground"> <DialogPrimitive.Content
<Shield className="h-5 w-5 text-blue-500 dark:text-blue-400" /> className="fixed left-[50%] top-[50%] z-[101] grid w-full max-w-md translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg focus:outline-none"
onPointerDownOutside={(e) => e.preventDefault()}
</DialogTitle> >
<DialogDescription> <DialogHeader>
使 <DialogTitle className="flex items-center gap-2 text-foreground">
</DialogDescription> <Shield className="h-5 w-5 text-blue-500 dark:text-blue-400" />
</DialogHeader>
</DialogTitle>
<DialogDescription>
使
</DialogDescription>
</DialogHeader>
<div className="space-y-4"> <div className="space-y-4">
<Alert className="border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/50"> <Alert className="border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/50">
<AlertCircle className="h-4 w-4" /> <AlertCircle className="h-4 w-4" />
<AlertDescription> <AlertDescription>
<strong></strong> <strong></strong>
</AlertDescription> </AlertDescription>
</Alert> </Alert>
<div className="space-y-3"> <div className="space-y-3">
<div className="flex items-center justify-center p-6 bg-muted/50 rounded-lg border"> <div className="flex items-center justify-center p-6 bg-muted/50 rounded-lg border">
<div className="text-center"> <div className="text-center">
<div className="text-2xl font-bold text-foreground mb-2"> <div className="text-2xl font-bold text-foreground mb-2">
{num1} + {num2} = ? {num1} + {num2} = ?
</div> </div>
<div className="flex items-center justify-center gap-2"> <div className="flex items-center justify-center gap-2">
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={generateCaptcha} onClick={generateCaptcha}
className="text-muted-foreground hover:text-foreground" className="text-muted-foreground hover:text-foreground"
> >
<RefreshCw className="h-4 w-4 mr-1" /> <RefreshCw className="h-4 w-4 mr-1" />
</Button> </Button>
</div>
</div> </div>
</div> </div>
</div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="captcha-answer" className="text-foreground"> <Label htmlFor="captcha-answer" className="text-foreground">
</Label> </Label>
<Input <Input
id="captcha-answer" id="captcha-answer"
type="number" type="number"
placeholder="输入计算结果" placeholder="输入计算结果"
value={userAnswer} value={userAnswer}
onChange={(e) => setUserAnswer(e.target.value)} onChange={(e) => setUserAnswer(e.target.value)}
onKeyPress={handleKeyPress} onKeyDown={handleKeyPress}
className="text-center text-lg font-medium" className="text-center text-lg font-medium"
autoFocus autoFocus
/> />
</div> </div>
{error && ( {error && (
<Alert className="border-red-200 bg-red-50 text-red-800 dark:border-red-800 dark:bg-red-950 dark:text-red-200"> <Alert className="border-red-200 bg-red-50 text-red-800 dark:border-red-800 dark:bg-red-950 dark:text-red-200">
<AlertCircle className="h-4 w-4" /> <AlertCircle className="h-4 w-4" />
<AlertDescription>{error}</AlertDescription> <AlertDescription>{error}</AlertDescription>
</Alert> </Alert>
)} )}
</div>
</div> </div>
</div>
<DialogFooter className="flex justify-between"> <DialogFooter className="flex justify-between">
<Button <Button
variant="outline" variant="outline"
onClick={handleCancel} onClick={handleCancel}
> >
</Button> </Button>
<Button <Button
onClick={handleVerify} onClick={handleVerify}
disabled={!userAnswer.trim()} disabled={!userAnswer.trim()}
className="bg-blue-600 hover:bg-blue-700 dark:bg-blue-600 dark:hover:bg-blue-700" className="bg-blue-600 hover:bg-blue-700 dark:bg-blue-600 dark:hover:bg-blue-700"
> >
</Button> </Button>
</DialogFooter> </DialogFooter>
</DialogContent> </DialogPrimitive.Content>
</DialogPortal>
</Dialog> </Dialog>
) )
} }