Files
smart-cropx-ui/src/components/auth/CaptchaInput.tsx
peng dfc29ce01f fix: 修复系统模块TypeScript类型错误和组件功能问题
- 修复消息组件JSX.Element类型错误,改为React.ReactNode
- 完善审核历史页面类型定义和API接口调用
- 优化验证码组件,移除备用验证码逻辑避免无限循环
- 简化系统设置页面,仅保留基本设置和外观设置
- 修复用户管理页面编辑模态框数据加载和CRUD操作
- 移除废弃的作物推荐组件文件

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-12 17:28:11 +08:00

135 lines
4.4 KiB
TypeScript

'use client';
import { useState, useEffect, useCallback,useRef } from 'react';
import { RefreshCw } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { getCaptchaApiV1AuthCaptchaGet } from '@/lib/api/sdk.gen';
import type { CaptchaResponse } from '@/lib/api/types.gen';
interface CaptchaInputProps {
value: string;
onChange: (value: string) => void;
onCaptchaChange?: (captchaData: CaptchaResponse | null) => void;
className?: string;
instanceId?: string;
}
export function CaptchaInput({ value, onChange, onCaptchaChange, className = '', instanceId = 'default' }: CaptchaInputProps) {
const [captchaData, setCaptchaData] = useState<CaptchaResponse | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const isInitialized = useRef(false);
// 初始化验证码
useEffect(() => {
if (!isInitialized.current) {
isInitialized.current = true;
const initialFetch = async () => {
console.log(`[CaptchaInput-${instanceId}] 初始化获取验证码...`);
setLoading(true);
setError('');
try {
const response = await getCaptchaApiV1AuthCaptchaGet();
console.log(`[CaptchaInput-${instanceId}] API验证码获取成功:`, response);
setCaptchaData(response.data);
if (onCaptchaChange) {
onCaptchaChange(response.data);
}
} catch (err) {
console.error(`[CaptchaInput-${instanceId}] 验证码获取失败:`, err);
setError('获取验证码失败,请重试');
} finally {
setLoading(false);
}
};
initialFetch();
}
}, [instanceId, onCaptchaChange]);
const fetchCaptcha = useCallback(async () => {
console.log(`[CaptchaInput-${instanceId}] 刷新验证码...`);
setLoading(true);
setError('');
try {
const response = await getCaptchaApiV1AuthCaptchaGet();
console.log(`[CaptchaInput-${instanceId}] API验证码获取成功:`, response);
setCaptchaData(response.data);
if (onCaptchaChange) {
onCaptchaChange(response.data);
}
} catch (err) {
console.error(`[CaptchaInput-${instanceId}] 验证码获取失败:`, err);
setError('获取验证码失败,请重试');
} finally {
setLoading(false);
}
}, [instanceId, onCaptchaChange]);
const handleRefresh = () => {
onChange(''); // 清空验证码输入
fetchCaptcha();
};
const handleCaptchaChange = (inputValue: string) => {
onChange(inputValue);
};
return (
<div className={`flex gap-2 ${className}`}>
<Input
type="text"
placeholder="请输入验证码"
value={value}
onChange={(e) => handleCaptchaChange(e.target.value.toUpperCase())}
maxLength={4}
className="flex-1"
disabled={loading}
/>
<div className="flex gap-2 items-center">
{loading ? (
<div className="w-[120px] h-10 border border-gray-300 rounded flex items-center justify-center bg-gray-100">
<div className="w-4 h-4 border-2 border-gray-300 border-t-blue-500 rounded-full animate-spin"></div>
</div>
) : error ? (
<div className="w-[120px] h-10 border border-red-300 rounded flex items-center justify-center bg-red-50">
<span className="text-xs text-red-500"></span>
</div>
) : (
<div
className="h-10 w-[120px] border border-gray-300 rounded cursor-pointer hover:opacity-80 transition-opacity overflow-hidden"
onClick={handleRefresh}
title="点击刷新验证码"
>
{captchaData && (
<img
src={captchaData.image}
alt="验证码"
className="w-full h-full object-cover"
onError={(e) => {
// 如果图片加载失败,隐藏错误图片
e.currentTarget.style.display = 'none';
setError('图片加载失败');
}}
/>
)}
</div>
)}
<Button
type="button"
variant="outline"
size="icon"
onClick={handleRefresh}
disabled={loading}
title="刷新验证码"
>
<RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />
</Button>
</div>
</div>
);
}