feat(web): 验证码组件支持主题自动跟随

- 自动跟随系统主题(dark/light)
- 支持 theme prop 手动指定主题
- 代码格式化优化

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
charilezhou
2026-01-17 21:35:55 +08:00
parent ce5a96c622
commit f9563f886c

View File

@@ -1,7 +1,9 @@
'use client';
import type { CaptchaResponse, CaptchaScene } from '@seclusion/shared';
import type { CaptchaResponse, CaptchaScene, CaptchaTheme } from '@seclusion/shared';
import { CaptchaTheme as CaptchaThemeEnum } from '@seclusion/shared';
import { RefreshCw } from 'lucide-react';
import { useTheme } from 'next-themes';
import { useCallback, useEffect, useRef, useState } from 'react';
import { Button } from '@/components/ui/button';
@@ -10,7 +12,6 @@ import { Label } from '@/components/ui/label';
import { http } from '@/lib/http';
import { cn } from '@/lib/utils';
export interface CaptchaProps {
/** 验证码使用场景 */
scene: CaptchaScene;
@@ -18,6 +19,8 @@ export interface CaptchaProps {
onChange: (captchaId: string, captchaCode: string) => void;
/** 验证码输入值 */
value: string;
/** 验证码主题(可选,默认自动跟随系统主题) */
theme?: CaptchaTheme;
/** 是否禁用 */
disabled?: boolean;
/** 自定义类名 */
@@ -32,6 +35,7 @@ export function Captcha({
scene,
onChange,
value,
theme: themeProp,
disabled = false,
className,
}: CaptchaProps) {
@@ -40,6 +44,13 @@ export function Captcha({
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
// 获取当前系统主题
const { resolvedTheme } = useTheme();
// 计算实际使用的主题:优先使用 prop否则跟随系统主题
const captchaTheme: CaptchaTheme =
themeProp ?? (resolvedTheme === 'dark' ? CaptchaThemeEnum.DARK : CaptchaThemeEnum.LIGHT);
// 使用 ref 保存 onChange避免作为 useEffect 依赖导致无限循环
const onChangeRef = useRef(onChange);
onChangeRef.current = onChange;
@@ -50,7 +61,7 @@ export function Captcha({
setError(null);
try {
const response = await http.get<CaptchaResponse>(
`/captcha?scene=${scene}`,
`/captcha?scene=${scene}&theme=${captchaTheme}`,
{ skipAuth: true }
);
setCaptchaId(response.captchaId);
@@ -62,7 +73,7 @@ export function Captcha({
} finally {
setLoading(false);
}
}, [scene]);
}, [scene, captchaTheme]);
// 组件挂载时获取验证码
useEffect(() => {
@@ -104,11 +115,7 @@ export function Captcha({
) : error ? (
<span className="text-xs text-destructive px-2">{error}</span>
) : captchaImage ? (
<img
src={captchaImage}
alt="验证码"
className="h-full object-contain"
/>
<img src={captchaImage} alt="验证码" className="h-full object-contain" />
) : (
<span className="text-xs text-muted-foreground"></span>
)}
@@ -122,9 +129,7 @@ export function Captcha({
onClick={fetchCaptcha}
disabled={loading || disabled}
>
<RefreshCw
className={cn('h-4 w-4', loading && 'animate-spin')}
/>
<RefreshCw className={cn('h-4 w-4', loading && 'animate-spin')} />
<span className="sr-only"></span>
</Button>
</div>