Skip to main content
Glama

MCPDemo - Visual SQL Chat Platform

by Ayi456
ForgotPassword.tsx10.7 kB
import { useState } from 'react' import { useNavigate } from 'react-router-dom' import { apiPost } from '@/api/client' import AuthLayout from '@/components/layout/AuthLayout' import { SimpleErrorMessage } from '@/components/ui/ErrorMessage' import LoadingSpinner from '@/components/ui/LoadingSpinner' type Step = 'phone' | 'code' | 'password' interface ForgotPasswordResponse { success: boolean message: string code?: string // 开发环境返回 } export default function ForgotPassword() { const navigate = useNavigate() const [step, setStep] = useState<Step>('phone') const [phoneOrEmail, setPhoneOrEmail] = useState('') const [code, setCode] = useState('') const [newPassword, setNewPassword] = useState('') const [confirmPassword, setConfirmPassword] = useState('') const [countdown, setCountdown] = useState(0) const [loading, setLoading] = useState(false) const [error, setError] = useState('') const [successMessage, setSuccessMessage] = useState('') // 发送验证码 const handleSendCode = async (e: React.FormEvent) => { e.preventDefault() if (!phoneOrEmail.trim()) { setError('请输入手机号或邮箱') return } setLoading(true) setError('') setSuccessMessage('') try { const response = await apiPost<ForgotPasswordResponse>( '/api/auth/forget/send-code', { phoneOrEmail } ) if (response.success) { setStep('code') setSuccessMessage(response.message || '验证码已发送') // 如果是开发环境,显示验证码 if (response.data?.code) { setSuccessMessage(`验证码已发送(开发环境:${response.data.code})`) } // 开始倒计时 setCountdown(60) const timer = setInterval(() => { setCountdown((prev) => { if (prev <= 1) { clearInterval(timer) return 0 } return prev - 1 }) }, 1000) } else { setError(response.message || '发送失败') } } catch (err: any) { setError(err.message || '网络错误,请稍后重试') } finally { setLoading(false) } } // 验证验证码 const handleVerifyCode = async (e: React.FormEvent) => { e.preventDefault() if (code.length !== 6) { setError('请输入6位验证码') return } setLoading(true) setError('') setSuccessMessage('') try { const response = await apiPost<ForgotPasswordResponse>( '/api/auth/forget/verify-code', { phoneOrEmail, code } ) if (response.success) { setStep('password') setSuccessMessage('验证成功,请设置新密码') } else { setError(response.message || '验证失败') } } catch (err: any) { setError(err.message || '网络错误,请稍后重试') } finally { setLoading(false) } } // 重置密码 const handleResetPassword = async (e: React.FormEvent) => { e.preventDefault() if (newPassword.length < 6) { setError('密码长度至少6位') return } if (newPassword !== confirmPassword) { setError('两次输入的密码不一致') return } setLoading(true) setError('') setSuccessMessage('') try { const response = await apiPost<ForgotPasswordResponse>( '/api/auth/forget/reset', { phoneOrEmail, code, newPassword } ) if (response.success) { setSuccessMessage('密码重置成功!即将跳转到登录页...') setTimeout(() => { navigate('/login') }, 2000) } else { setError(response.message || '重置失败') } } catch (err: any) { setError(err.message || '网络错误,请稍后重试') } finally { setLoading(false) } } return ( <AuthLayout title="忘记密码" subtitle="通过手机号或邮箱重置您的密码" > {/* 步骤指示器 */} <div className="flex justify-between mb-8"> <div className={`flex-1 text-center ${step === 'phone' ? 'text-blue-600 font-semibold' : 'text-gray-400'}`}> <div className={`w-8 h-8 mx-auto mb-2 rounded-full flex items-center justify-center ${step === 'phone' ? 'bg-blue-600 text-white' : 'bg-gray-300'}`}> 1 </div> <span className="text-xs">验证身份</span> </div> <div className={`flex-1 text-center ${step === 'code' ? 'text-blue-600 font-semibold' : 'text-gray-400'}`}> <div className={`w-8 h-8 mx-auto mb-2 rounded-full flex items-center justify-center ${step === 'code' ? 'bg-blue-600 text-white' : 'bg-gray-300'}`}> 2 </div> <span className="text-xs">输入验证码</span> </div> <div className={`flex-1 text-center ${step === 'password' ? 'text-blue-600 font-semibold' : 'text-gray-400'}`}> <div className={`w-8 h-8 mx-auto mb-2 rounded-full flex items-center justify-center ${step === 'password' ? 'bg-blue-600 text-white' : 'bg-gray-300'}`}> 3 </div> <span className="text-xs">设置新密码</span> </div> </div> {/* 错误和成功提示 */} {error && <SimpleErrorMessage message={error} />} {successMessage && ( <div className="mb-4 p-3 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg text-green-700 dark:text-green-300 text-sm"> {successMessage} </div> )} {/* 步骤 1: 输入手机号/邮箱 */} {step === 'phone' && ( <form onSubmit={handleSendCode} className="space-y-6"> <div> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"> 手机号 / 邮箱 </label> <input type="text" value={phoneOrEmail} onChange={(e) => setPhoneOrEmail(e.target.value)} className="input-modern" placeholder="请输入注册时使用的手机号或邮箱" required /> </div> <button type="submit" disabled={loading} className="btn-gradient w-full" > {loading ? ( <div className="flex items-center justify-center"> <LoadingSpinner size="sm" color="white" className="mr-3" /> 发送中... </div> ) : '发送验证码'} </button> </form> )} {/* 步骤 2: 输入验证码 */} {step === 'code' && ( <form onSubmit={handleVerifyCode} className="space-y-6"> <div> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"> 验证码 </label> <input type="text" value={code} onChange={(e) => setCode(e.target.value)} className="input-modern text-center text-2xl tracking-widest" placeholder="请输入6位验证码" maxLength={6} required /> <p className="mt-2 text-xs text-gray-500 dark:text-gray-400"> 验证码已发送至 {phoneOrEmail} </p> </div> <div className="flex space-x-3"> <button type="button" onClick={() => setStep('phone')} className="flex-1 px-4 py-3 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors" > 返回上一步 </button> <button type="submit" disabled={loading || code.length !== 6} className="flex-1 btn-gradient" > {loading ? ( <div className="flex items-center justify-center"> <LoadingSpinner size="sm" color="white" className="mr-3" /> 验证中... </div> ) : '下一步'} </button> </div> {countdown > 0 ? ( <p className="text-center text-sm text-gray-500"> {countdown}秒后可重新发送 </p> ) : ( <button type="button" onClick={() => { setStep('phone') setCode('') setError('') }} className="w-full text-center text-sm text-blue-600 hover:text-blue-700 transition-colors" > 重新发送验证码 </button> )} </form> )} {/* 步骤 3: 设置新密码 */} {step === 'password' && ( <form onSubmit={handleResetPassword} className="space-y-6"> <div> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"> 新密码 </label> <input type="password" value={newPassword} onChange={(e) => setNewPassword(e.target.value)} className="input-modern" placeholder="请输入新密码(至少6位)" required /> </div> <div> <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"> 确认密码 </label> <input type="password" value={confirmPassword} onChange={(e) => setConfirmPassword(e.target.value)} className="input-modern" placeholder="请再次输入新密码" required /> </div> <button type="submit" disabled={loading || newPassword.length < 6 || newPassword !== confirmPassword} className="btn-gradient w-full" > {loading ? ( <div className="flex items-center justify-center"> <LoadingSpinner size="sm" color="white" className="mr-3" /> 重置中... </div> ) : '重置密码'} </button> </form> )} {/* 返回登录链接 */} <div className="mt-8 text-center"> <button onClick={() => navigate('/login')} className="text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 transition-colors" > ← 返回登录 </button> </div> </AuthLayout> ) }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Ayi456/visual-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server