Files
oxygen-ui/src/pages/sign/SignUp.tsx
2024-01-04 10:17:06 +08:00

245 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useCallback } from 'react'
import Icon from '@ant-design/icons'
import { Turnstile, TurnstileInstance } from '@marsidev/react-turnstile'
import {
DATABASE_DUPLICATE_KEY,
H_CAPTCHA_SITE_KEY,
PERMISSION_REGISTER_SUCCESS,
SYSTEM_INVALID_CAPTCHA_CODE
} from '@/constants/common.constants'
import { useUpdatedEffect } from '@/util/hooks'
import { getLoginStatus, setToken } from '@/util/auth'
import { r_auth_register, r_auth_resend } from '@/services/auth'
import FitCenter from '@/components/common/FitCenter'
import FlexBox from '@/components/common/FlexBox'
const SignUp: React.FC = () => {
const location = useLocation()
const navigate = useNavigate()
const turnstileRef = useRef<TurnstileInstance>()
const turnstileRefCallback = useCallback(
(node: TurnstileInstance) => {
turnstileRef.current = node
if (location.pathname === '/register') {
turnstileRef.current?.execute()
}
},
[location.pathname]
)
const [isSigningUp, setIsSigningUp] = useState(false)
const [isFinish, setIsFinish] = useState(false)
const [isSending, setIsSending] = useState(false)
const [captchaCode, setCaptchaCode] = useState('')
useUpdatedEffect(() => {
if (!isSigningUp) {
setCaptchaCode('')
turnstileRef.current?.reset()
turnstileRef.current?.execute()
}
}, [isSigningUp])
useUpdatedEffect(() => {
if (location.pathname !== '/register') {
return
}
if (getLoginStatus()) {
navigate(`/login${location.search}`, {
replace: true
})
}
}, [location.pathname])
const handleOnFinish = (registerParam: RegisterParam) => {
if (isSigningUp) {
return
}
setIsSigningUp(true)
if (!captchaCode) {
void message.warning('请先通过验证')
setIsSigningUp(false)
return
}
void r_auth_register({
username: registerParam.username,
email: registerParam.email,
password: registerParam.password,
captchaCode
})
.then((res) => {
const response = res.data
switch (response.code) {
case PERMISSION_REGISTER_SUCCESS:
setToken(response.data?.token ?? '')
void message.success('恭喜,您快要完成注册了')
setIsFinish(true)
break
case DATABASE_DUPLICATE_KEY:
void message.error('用户名或邮箱已被注册,请重试')
setIsSigningUp(false)
break
case SYSTEM_INVALID_CAPTCHA_CODE:
void message.error('验证码有误,请重试')
setIsSigningUp(false)
break
default:
void message.error('服务器出错了,请稍后重试')
setIsSigningUp(false)
}
})
.catch(() => {
setIsSigningUp(false)
})
}
const handleOnResend = () => {
if (isSending) {
return
}
setIsSending(true)
void message.loading({ content: '发送中', key: 'sending', duration: 0 })
void r_auth_resend()
.then((res) => {
const response = res.data
if (response.success) {
void message.success('已发送验证邮件,请查收')
} else {
void message.error('出错了,请稍后重试')
}
})
.finally(() => {
message.destroy('sending')
setIsSending(false)
})
}
return (
<div className={'sign-up'}>
<FitCenter>
<FlexBox>
<div className={'title'}>
<div className={'primary'}></div>
<div className={'secondary'}>Create account</div>
</div>
<AntdForm autoComplete={'on'} onFinish={handleOnFinish} className={'form'}>
{!isFinish ? (
<>
<AntdForm.Item
name={'username'}
rules={[
{ required: true, message: '请输入用户名' },
{
pattern: /^[a-zA-Z-_][0-9a-zA-Z-_]{2,38}$/,
message:
'只能包含字母、数字、连字符和下划线,不能以数字开头'
}
]}
>
<AntdInput
prefix={<Icon component={IconOxygenUser} />}
placeholder={'用户名'}
maxLength={39}
showCount={true}
disabled={isSigningUp}
/>
</AntdForm.Item>
<AntdForm.Item
name={'email'}
rules={[
{ required: true, message: '请输入邮箱' },
{ type: 'email', message: '不是有效的邮箱地址' }
]}
>
<AntdInput
type={'email'}
prefix={<Icon component={IconOxygenEmail} />}
placeholder={'邮箱'}
disabled={isSigningUp}
/>
</AntdForm.Item>
<AntdForm.Item
name={'password'}
rules={[
{ required: true, message: '请输入密码' },
{ min: 10, message: '密码至少为10位' },
{ max: 30, message: '密码最多为30位' }
]}
>
<AntdInput.Password
prefix={<Icon component={IconOxygenPassword} />}
placeholder={'密码'}
disabled={isSigningUp}
/>
</AntdForm.Item>
<AntdForm.Item
name={'passwordConfirm'}
rules={[
{ required: true, message: '请确认密码' },
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('password') === value) {
return Promise.resolve()
}
return Promise.reject(
new Error('两次密码输入必须一致')
)
}
})
]}
>
<AntdInput.Password
prefix={<Icon component={IconOxygenPassword} />}
placeholder={'确认密码'}
disabled={isSigningUp}
/>
</AntdForm.Item>
<AntdForm.Item>
<Turnstile
id={'sign-up-turnstile'}
ref={turnstileRefCallback}
siteKey={H_CAPTCHA_SITE_KEY}
options={{ theme: 'light', execution: 'execute' }}
onSuccess={setCaptchaCode}
/>
</AntdForm.Item>
<AntdForm.Item>
<AntdButton
style={{ width: '100%' }}
type={'primary'}
htmlType={'submit'}
disabled={isSigningUp}
loading={isSigningUp}
>
&ensp;&ensp;&ensp;&ensp;
</AntdButton>
</AntdForm.Item>
</>
) : (
<div className={'retry'}>
<a onClick={handleOnResend}></a>
</div>
)}
<div className={'footer'} hidden={isFinish}>
<a
onClick={() =>
navigate(`/login${location.search}`, { replace: true })
}
>
</a>
</div>
</AntdForm>
</FlexBox>
</FitCenter>
</div>
)
}
export default SignUp