|  |  | 
 |  |  |     useTranslate, | 
 |  |  |     useLogin, | 
 |  |  |     useNotify, | 
 |  |  |     email as validEmail, | 
 |  |  | } from 'react-admin'; | 
 |  |  | import { useForm, Controller } from 'react-hook-form'; | 
 |  |  | import ProviderChoices from "./ProviderChoices"; | 
 |  |  | import Visibility from '@mui/icons-material/Visibility'; | 
 |  |  | import VisibilityOff from '@mui/icons-material/VisibilityOff'; | 
 |  |  | import { sendEmailCode, register } from '@/api/auth'; | 
 |  |  |  | 
 |  |  | const Register = (props) => { | 
 |  |  |     const translate = useTranslate(); | 
 |  |  | 
 |  |  |     const location = useLocation(); | 
 |  |  |     const { systemInfo } = props; | 
 |  |  |  | 
 |  |  |     const { control, watch, handleSubmit, setValue } = useForm(); | 
 |  |  |     const { control, watch, handleSubmit, setValue, setError, clearErrors } = useForm(); | 
 |  |  |  | 
 |  |  |     const email = watch('email'); | 
 |  |  |     const username = watch('username'); | 
 |  |  |     const password = watch('password'); | 
 |  |  |     const confirmPassword = watch('confirmPassword'); | 
 |  |  |     const code = watch('code'); | 
 |  |  |  | 
 |  |  |     const [loading, setLoading] = useState(false); | 
 |  |  |     const [codeLoading, setCodeLoading] = useState(false); | 
 |  |  |     const [showPassword, setShowPassword] = useState(true); | 
 |  |  |  | 
 |  |  |  | 
 |  |  |     const [isCounting, setIsCounting] = useState(false); | 
 |  |  |     const [countdown, setCountdown] = useState(60); | 
 |  |  |  | 
 |  |  |     // 处理验证码按钮点击 | 
 |  |  |     // send code | 
 |  |  |     const handleSendCode = async () => { | 
 |  |  |         // 这里假设发送验证码的请求 | 
 |  |  |         const response = await fetch('/api/send-code'); | 
 |  |  |         if (response.ok) { | 
 |  |  |             setIsCounting(true); | 
 |  |  |             localStorage.setItem('codeCountdown', 60); // 存储倒计时到本地 | 
 |  |  |         if (!email) { | 
 |  |  |             setError("email", { | 
 |  |  |                 message: translate('ra.validation.required') | 
 |  |  |             }) | 
 |  |  |             return; | 
 |  |  |         } | 
 |  |  |         const emailError = validEmail()(email); | 
 |  |  |         if (emailError) { | 
 |  |  |             setError("email", { | 
 |  |  |                 message: translate("ra.validation.email") | 
 |  |  |             }) | 
 |  |  |             return; | 
 |  |  |         } | 
 |  |  |         clearErrors("email"); | 
 |  |  |         setCodeLoading(true); | 
 |  |  |         sendEmailCode({ email }).then(res => { | 
 |  |  |             setCodeLoading(false); | 
 |  |  |             const { code, msg, data } = res; | 
 |  |  |             if (code === 200) { | 
 |  |  |                 notify(msg, { type: 'success', messageArgs: { _: msg } }); | 
 |  |  |  | 
 |  |  |                 const timestamp = Math.floor(Date.now() / 1000); | 
 |  |  |                 const expirationTime = timestamp + 60; | 
 |  |  |                 localStorage.setItem('codeExpirationTime', expirationTime); | 
 |  |  |  | 
 |  |  |                 setIsCounting(true); | 
 |  |  |                 setCountdown(60); | 
 |  |  |             } else if (code === 10005 || code === 10006) { | 
 |  |  |                 setError('email', { | 
 |  |  |                     message: msg | 
 |  |  |                 }) | 
 |  |  |             } else { | 
 |  |  |                 notify(msg, { type: 'error', messageArgs: { _: msg } }); | 
 |  |  |             } | 
 |  |  |         }).catch((error) => { | 
 |  |  |             setCodeLoading(false); | 
 |  |  |             notify(error.message, { type: 'error', messageArgs: { _: error.message } }); | 
 |  |  |             console.error(error); | 
 |  |  |         }) | 
 |  |  |     }; | 
 |  |  |  | 
 |  |  |     // 倒计时功能 | 
 |  |  |     // countdown | 
 |  |  |     useEffect(() => { | 
 |  |  |         const savedCountdown = localStorage.getItem('codeCountdown'); | 
 |  |  |         if (savedCountdown && !isCounting) { | 
 |  |  |             setCountdown(Number(savedCountdown)); | 
 |  |  |             setIsCounting(true); | 
 |  |  |         const codeExpirationTime = localStorage.getItem('codeExpirationTime'); | 
 |  |  |         if (codeExpirationTime) { | 
 |  |  |             const currentTimestamp = Math.floor(Date.now() / 1000); | 
 |  |  |             const remainingTime = codeExpirationTime - currentTimestamp; | 
 |  |  |             if (remainingTime > 0) { | 
 |  |  |                 setCountdown(remainingTime); | 
 |  |  |                 setIsCounting(true); | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         const interval = setInterval(() => { | 
 |  |  |             if (isCounting && countdown > 0) { | 
 |  |  |                 setCountdown(prev => prev - 1); | 
 |  |  |                 localStorage.setItem('codeCountdown', countdown - 1); | 
 |  |  |             } else if (countdown <= 0) { | 
 |  |  |                 clearInterval(interval); | 
 |  |  |                 setIsCounting(false); | 
 |  |  |                 localStorage.removeItem('codeCountdown'); // 重置 | 
 |  |  |                 localStorage.removeItem('codeExpirationTime'); | 
 |  |  |             } | 
 |  |  |         }, 1000); | 
 |  |  |  | 
 |  |  |         return () => clearInterval(interval); | 
 |  |  |     }, [countdown, isCounting]); | 
 |  |  |  | 
 |  |  |  | 
 |  |  |     const onSubmit = (data) => { | 
 |  |  |         notify("Registration is not open yet"); | 
 |  |  |         return; | 
 |  |  |     // register | 
 |  |  |     const onSubmit = (params) => { | 
 |  |  |         setLoading(true); | 
 |  |  |         // js native confirm && root | 
 |  |  |         login( | 
 |  |  |             data, | 
 |  |  |             location.state ? (location.state).nextPathname : '/' | 
 |  |  |         ).catch((error) => { | 
 |  |  |         register(params).then(res => { | 
 |  |  |             setLoading(false); | 
 |  |  |             notify( | 
 |  |  |                 typeof error === 'string' | 
 |  |  |                     ? error | 
 |  |  |                     : typeof error === 'undefined' || !error.message | 
 |  |  |                         ? 'ra.auth.sign_in_error' | 
 |  |  |                         : error.message, | 
 |  |  |                 { | 
 |  |  |                     type: 'error', | 
 |  |  |                     messageArgs: { | 
 |  |  |                         _: | 
 |  |  |                             typeof error === 'string' | 
 |  |  |                                 ? error | 
 |  |  |                                 : error && error.message | 
 |  |  |                                     ? error.message | 
 |  |  |                                     : undefined, | 
 |  |  |                     }, | 
 |  |  |                 } | 
 |  |  |             ); | 
 |  |  |         }); | 
 |  |  |             const { code, msg, data } = res; | 
 |  |  |             if (code === 200) { | 
 |  |  |                 notify(msg, { type: 'success', messageArgs: { _: msg } }); | 
 |  |  |                 // to login | 
 |  |  |                 login( | 
 |  |  |                     params, | 
 |  |  |                     location.state ? (location.state).nextPathname : '/' | 
 |  |  |                 ).catch(({ code, msg }) => { | 
 |  |  |                     setLoading(false); | 
 |  |  |                     notify(msg, { type: 'error', messageArgs: { _: msg } }); | 
 |  |  |                 }); | 
 |  |  |             } else if (code === 10002) { | 
 |  |  |                 setError("username", { | 
 |  |  |                     message: msg | 
 |  |  |                 }) | 
 |  |  |             } else { | 
 |  |  |                 notify(msg, { type: 'error', messageArgs: { _: msg } }); | 
 |  |  |             } | 
 |  |  |         }).catch((error) => { | 
 |  |  |             setLoading(false); | 
 |  |  |             notify(error.message, { type: 'error', messageArgs: { _: error.message } }); | 
 |  |  |             console.error(error); | 
 |  |  |         }) | 
 |  |  |     }; | 
 |  |  |  | 
 |  |  |     return ( | 
 |  |  | 
 |  |  |                         control={control} | 
 |  |  |                         defaultValue="" | 
 |  |  |                         rules={{ required: true }} | 
 |  |  |                         render={({ field }) => ( | 
 |  |  |                         render={({ field, fieldState: { error } }) => ( | 
 |  |  |                             <TextField | 
 |  |  |                                 {...field} | 
 |  |  |                                 label={translate("page.login.username")} | 
 |  |  | 
 |  |  |                                 disabled={loading} | 
 |  |  |                                 autoFocus | 
 |  |  |                                 autoComplete="off" | 
 |  |  |                                 error={!!error} | 
 |  |  |                                 helperText={error?.message || ""} | 
 |  |  |                             /> | 
 |  |  |                         )} | 
 |  |  |                     /> | 
 |  |  | 
 |  |  |                         <Button | 
 |  |  |                             variant="outlined" | 
 |  |  |                             onClick={handleSendCode} | 
 |  |  |                             disabled={isCounting || loading} | 
 |  |  |                             disabled={codeLoading || isCounting} | 
 |  |  |                             sx={{ | 
 |  |  |                                 width: '35%', | 
 |  |  |                                 mt: 1, | 
 |  |  |                                 whiteSpace: 'nowrap', | 
 |  |  |                             }} | 
 |  |  |                         > | 
 |  |  |                             {isCounting ? ( | 
 |  |  |                                 <> | 
 |  |  |                                     <CircularProgress size={20} color="primary" sx={{ marginRight: 1 }} /> | 
 |  |  |                                     {`${countdown}s`} | 
 |  |  |                                 </> | 
 |  |  |                             ) : ( | 
 |  |  |                                 translate('page.login.button.code') | 
 |  |  |                             )} | 
 |  |  |                             {codeLoading ? ( | 
 |  |  |                                 <CircularProgress size={20} color="primary" sx={{ marginRight: 1 }} /> | 
 |  |  |                             ) : | 
 |  |  |                                 isCounting ? ( | 
 |  |  |                                     `${countdown}s` | 
 |  |  |                                 ) : ( | 
 |  |  |                                     translate('page.login.button.code') | 
 |  |  |                                 ) | 
 |  |  |                             } | 
 |  |  |                         </Button> | 
 |  |  |                     </Box> | 
 |  |  |  | 
 |  |  | 
 |  |  |                     <Button | 
 |  |  |                         type="submit" | 
 |  |  |                         variant="contained" | 
 |  |  |                         disabled={loading || !(email && username && password && confirmPassword)} | 
 |  |  |                         disabled={loading || !(email && username && password && confirmPassword && code)} | 
 |  |  |                         sx={{ | 
 |  |  |                             backgroundColor: "#3D4BA7" | 
 |  |  |                         }} |