feat(web): 验证码组件支持主题自动跟随
- 自动跟随系统主题(dark/light) - 支持 theme prop 手动指定主题 - 代码格式化优化 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user