From 831dca6a550341e5b003a05e1a8da7d2ce1dab39 Mon Sep 17 00:00:00 2001
From: zjj <3272660260@qq.com>
Date: 星期一, 09 六月 2025 15:31:01 +0800
Subject: [PATCH] #
---
rsf-admin/src/page/login/Register.jsx | 274 ++++++++++++++++++++++++++++++++++++++++--------------
1 files changed, 200 insertions(+), 74 deletions(-)
diff --git a/rsf-admin/src/page/login/Register.jsx b/rsf-admin/src/page/login/Register.jsx
index 940663b..c4169db 100644
--- a/rsf-admin/src/page/login/Register.jsx
+++ b/rsf-admin/src/page/login/Register.jsx
@@ -15,64 +15,131 @@
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 notify = useNotify();
const login = useLogin();
const location = useLocation();
- const { tab, tenantList } = props;
+ const { systemInfo } = props;
- const { control, watch, handleSubmit, setValue } = useForm();
+ const { control, watch, handleSubmit, setValue, setError, clearErrors } = useForm();
- const [loading, setLoading] = useState(false);
- const [showPassword, setShowPassword] = useState(true);
-
+ const email = watch('email');
const username = watch('username');
const password = watch('password');
const confirmPassword = watch('confirmPassword');
- const tenantId = watch('tenantId');
+ const code = watch('code');
- useEffect(() => {
- if (tenantList.length > 0 && !tenantId) {
- setValue('tenantId', tenantList[0].id);
+ 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 () => {
+ if (!email) {
+ setError("email", {
+ message: translate('ra.validation.required')
+ })
+ return;
}
- }, [tenantList, setValue]);
+ 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 onSubmit = (data) => {
- notify("Registration is not open yet");
- return;
+ 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 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);
+ } else if (countdown <= 0) {
+ clearInterval(interval);
+ setIsCounting(false);
+ localStorage.removeItem('codeExpirationTime');
+ }
+ }, 1000);
+
+ return () => clearInterval(interval);
+ }, [countdown, isCounting]);
+
+ // 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 (
@@ -85,46 +152,20 @@
>
<Stack spacing={2}>
<Controller
- name="tenantId"
- control={control}
- rules={{ required: true }}
- defaultValue={tenantList.length > 0 ? tenantList[0].id : ''}
- render={({ field: { onChange, value, ref } }) => {
- const selectedTenant = tenantList.find(tenant => tenant.id === value) || null;
- return (
- <Autocomplete
- options={tenantList}
- getOptionLabel={(option) => option.name}
- value={selectedTenant}
- onChange={(_, newValue) => {
- onChange(newValue ? newValue.id : '');
- }}
- renderInput={(params) => (
- <TextField
- {...params}
- label={translate("page.login.tenant")}
- variant="standard"
- inputRef={ref}
- />
- )}
- />
- );
- }}
- />
-
- <Controller
name="username"
control={control}
defaultValue=""
rules={{ required: true }}
- render={({ field }) => (
+ render={({ field, fieldState: { error } }) => (
<TextField
{...field}
- label={translate('ra.auth.username')}
+ label={translate("page.login.username")}
variant="standard"
disabled={loading}
autoFocus
autoComplete="off"
+ error={!!error}
+ helperText={error?.message || ""}
/>
)}
/>
@@ -133,15 +174,23 @@
name="password"
control={control}
defaultValue=""
- rules={{ required: true }}
- render={({ field }) => (
+ rules={{
+ required: translate('ra.validation.required'),
+ pattern: {
+ value: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d\.]{6,13}$/,
+ message: translate('page.settings.resetPwd.tip.pwdInputLimit'),
+ },
+ }}
+ render={({ field, fieldState: { error } }) => (
<TextField
{...field}
- label={translate('ra.auth.password')}
+ label={translate("page.login.password")}
type={showPassword ? 'text' : 'password'}
variant="standard"
disabled={loading}
autoComplete="off"
+ error={!!error}
+ helperText={error?.message || ""}
InputProps={{
endAdornment: (
<InputAdornment position="end">
@@ -164,8 +213,12 @@
name="confirmPassword"
control={control}
defaultValue=""
- rules={{ required: true }}
- render={({ field }) => (
+ rules={{
+ required: translate('ra.validation.required'),
+ validate: value =>
+ value === password || translate('page.settings.resetPwd.tip.pwdNotMatch'),
+ }}
+ render={({ field, fieldState: { error } }) => (
<TextField
{...field}
label={translate('page.login.confirmPwd')}
@@ -173,6 +226,8 @@
variant="standard"
disabled={loading}
autoComplete="off"
+ error={!!error}
+ helperText={error?.message || ""}
InputProps={{
endAdornment: (
<InputAdornment position="end">
@@ -191,12 +246,83 @@
)}
/>
- <Box mt={10}></Box>
+ <Controller
+ name="email"
+ control={control}
+ defaultValue=""
+ rules={{
+ required: translate('ra.validation.required'),
+ pattern: {
+ value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
+ message: translate("ra.validation.email"),
+ },
+ }}
+ render={({ field, fieldState: { error } }) => (
+ <TextField
+ {...field}
+ label={translate('page.login.email')}
+ variant="standard"
+ disabled={loading}
+ // autoComplete="off"
+ error={!!error}
+ helperText={error ? error.message : ""}
+ />
+ )}
+ />
+
+ <Box display="flex" alignItems="center" justifyContent='center' width="100%">
+ <Controller
+ name="code"
+ control={control}
+ defaultValue=""
+ rules={{
+ required: translate('ra.validation.required'),
+ }}
+ render={({ field, fieldState: { error } }) => (
+ <TextField
+ {...field}
+ label={translate('page.login.code')}
+ variant="standard"
+ disabled={loading}
+ autoComplete="off"
+ error={!!error}
+ helperText={error ? error.message : ""}
+ sx={{
+ width: '65%',
+ mr: 2,
+ }}
+ />
+ )}
+ />
+
+ <Button
+ variant="outlined"
+ onClick={handleSendCode}
+ disabled={codeLoading || isCounting}
+ sx={{
+ width: '35%',
+ mt: 1,
+ whiteSpace: 'nowrap',
+ }}
+ >
+ {codeLoading ? (
+ <CircularProgress size={20} color="primary" sx={{ marginRight: 1 }} />
+ ) :
+ isCounting ? (
+ `${countdown}s`
+ ) : (
+ translate('page.login.button.code')
+ )
+ }
+ </Button>
+ </Box>
+
+ <Box />
<Button
type="submit"
variant="contained"
- disabled={loading || !(tenantId && username && password && confirmPassword)}
+ disabled={loading || !(email && username && password && confirmPassword && code)}
sx={{
backgroundColor: "#3D4BA7"
}}
--
Gitblit v1.9.1