From 6883e31331af4633d1b7d74ea7deb5f972afa05d Mon Sep 17 00:00:00 2001
From: skyouc
Date: 星期五, 23 五月 2025 20:02:19 +0800
Subject: [PATCH] 新增移库功能

---
 rsf-admin/src/page/login/Register.jsx |  151 ++++++++++++++++++++++++++++++++------------------
 1 files changed, 96 insertions(+), 55 deletions(-)

diff --git a/rsf-admin/src/page/login/Register.jsx b/rsf-admin/src/page/login/Register.jsx
index 881f5be..c4169db 100644
--- a/rsf-admin/src/page/login/Register.jsx
+++ b/rsf-admin/src/page/login/Register.jsx
@@ -15,11 +15,13 @@
     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();
@@ -28,82 +30,116 @@
     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 (
@@ -120,7 +156,7 @@
                         control={control}
                         defaultValue=""
                         rules={{ required: true }}
-                        render={({ field }) => (
+                        render={({ field, fieldState: { error } }) => (
                             <TextField
                                 {...field}
                                 label={translate("page.login.username")}
@@ -128,6 +164,8 @@
                                 disabled={loading}
                                 autoFocus
                                 autoComplete="off"
+                                error={!!error}
+                                helperText={error?.message || ""}
                             />
                         )}
                     />
@@ -260,19 +298,22 @@
                         <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>
 
@@ -281,7 +322,7 @@
                     <Button
                         type="submit"
                         variant="contained"
-                        disabled={loading || !(email && username && password && confirmPassword)}
+                        disabled={loading || !(email && username && password && confirmPassword && code)}
                         sx={{
                             backgroundColor: "#3D4BA7"
                         }}

--
Gitblit v1.9.1