Complete main UI #37

Merged
FatttSnake merged 192 commits from FatttSnake into dev 2024-02-23 16:31:17 +08:00
4 changed files with 143 additions and 47 deletions
Showing only changes of commit edf5f08828 - Show all commits

View File

@@ -30,6 +30,7 @@ export const SYSTEM_TIMEOUT = 10051
export const SYSTEM_REQUEST_ILLEGAL = 10052 export const SYSTEM_REQUEST_ILLEGAL = 10052
export const SYSTEM_ARGUMENT_NOT_VALID = 10053 export const SYSTEM_ARGUMENT_NOT_VALID = 10053
export const SYSTEM_INVALID_CAPTCHA_CODE = 10054 export const SYSTEM_INVALID_CAPTCHA_CODE = 10054
export const SYSTEM_REQUEST_TOO_FREQUENT = 10055
export const PERMISSION_LOGIN_SUCCESS = 20000 export const PERMISSION_LOGIN_SUCCESS = 20000
export const PERMISSION_PASSWORD_CHANGE_SUCCESS = 20001 export const PERMISSION_PASSWORD_CHANGE_SUCCESS = 20001

3
src/global.d.ts vendored
View File

@@ -62,6 +62,7 @@ interface RegisterParam {
username: string username: string
email: string email: string
password: string password: string
captchaCode: string
} }
interface VerifyParam { interface VerifyParam {
@@ -72,11 +73,13 @@ interface VerifyParam {
interface ForgetParam { interface ForgetParam {
email: string email: string
captchaCode: string
} }
interface RetrieveParam { interface RetrieveParam {
code: string code: string
password: string password: string
captchaCode: string
} }
interface LoginParam { interface LoginParam {

View File

@@ -1,7 +1,7 @@
import React, { useState } from 'react' import React, { useState } from 'react'
import { useNavigate } from 'react-router' import { useNavigate } from 'react-router'
import Icon from '@ant-design/icons' import Icon from '@ant-design/icons'
import { Turnstile } from '@marsidev/react-turnstile' import { Turnstile, TurnstileInstance } from '@marsidev/react-turnstile'
import '@/assets/css/pages/sign.scss' import '@/assets/css/pages/sign.scss'
import { import {
COLOR_BACKGROUND, COLOR_BACKGROUND,
@@ -41,9 +41,11 @@ import { r_api_avatar_random_base64 } from '@/services/api/avatar.tsx'
const SignUp: React.FC = () => { const SignUp: React.FC = () => {
const location = useLocation() const location = useLocation()
const navigate = useNavigate() const navigate = useNavigate()
const turnstileRef = useRef<TurnstileInstance>()
const [isSigningUp, setIsSigningUp] = useState(false) const [isSigningUp, setIsSigningUp] = useState(false)
const [isFinish, setIsFinish] = useState(false) const [isFinish, setIsFinish] = useState(false)
const [isSending, setIsSending] = useState(false) const [isSending, setIsSending] = useState(false)
const [captchaCode, setCaptchaCode] = useState('')
useUpdatedEffect(() => { useUpdatedEffect(() => {
if (location.pathname !== '/register') { if (location.pathname !== '/register') {
@@ -54,6 +56,9 @@ const SignUp: React.FC = () => {
replace: true replace: true
}) })
} }
setTimeout(() => {
turnstileRef.current?.execute()
}, 200)
}, [location.pathname]) }, [location.pathname])
const handleOnFinish = (registerParam: RegisterParam) => { const handleOnFinish = (registerParam: RegisterParam) => {
@@ -61,10 +66,18 @@ const SignUp: React.FC = () => {
return return
} }
setIsSigningUp(true) setIsSigningUp(true)
if (!captchaCode) {
void message.warning('请先通过验证')
setIsSigningUp(false)
return
}
void r_auth_register({ void r_auth_register({
username: registerParam.username, username: registerParam.username,
email: registerParam.email, email: registerParam.email,
password: registerParam.password password: registerParam.password,
captchaCode
}) })
.then((res) => { .then((res) => {
const response = res.data const response = res.data
@@ -78,6 +91,9 @@ const SignUp: React.FC = () => {
void message.error('用户名或邮箱已被注册,请重试') void message.error('用户名或邮箱已被注册,请重试')
setIsSigningUp(false) setIsSigningUp(false)
break break
case SYSTEM_INVALID_CAPTCHA_CODE:
void message.error('验证码有误,请刷新重试')
break
default: default:
void message.error('服务器出错了,请稍后重试') void message.error('服务器出错了,请稍后重试')
setIsSigningUp(false) setIsSigningUp(false)
@@ -97,7 +113,6 @@ const SignUp: React.FC = () => {
void r_auth_resend() void r_auth_resend()
.then((res) => { .then((res) => {
const response = res.data const response = res.data
message.destroy('sending')
if (response.success) { if (response.success) {
void message.success('已发送验证邮件,请查收') void message.success('已发送验证邮件,请查收')
} else { } else {
@@ -105,6 +120,7 @@ const SignUp: React.FC = () => {
} }
}) })
.finally(() => { .finally(() => {
message.destroy('sending')
setIsSending(false) setIsSending(false)
}) })
} }
@@ -189,6 +205,15 @@ const SignUp: React.FC = () => {
disabled={isSigningUp} disabled={isSigningUp}
/> />
</AntdForm.Item> </AntdForm.Item>
<AntdForm.Item>
<Turnstile
id={'sign-up-turnstile'}
ref={turnstileRef}
siteKey={H_CAPTCHA_SITE_KEY}
options={{ theme: 'light', execution: 'execute' }}
onSuccess={setCaptchaCode}
/>
</AntdForm.Item>
<AntdForm.Item> <AntdForm.Item>
<AntdButton <AntdButton
style={{ width: '100%' }} style={{ width: '100%' }}
@@ -286,7 +311,6 @@ const Verify: React.FC = () => {
void r_auth_resend() void r_auth_resend()
.then((res) => { .then((res) => {
const response = res.data const response = res.data
message.destroy('sending')
if (response.success) { if (response.success) {
void message.success('已发送验证邮件,请查收') void message.success('已发送验证邮件,请查收')
} else { } else {
@@ -294,6 +318,7 @@ const Verify: React.FC = () => {
} }
}) })
.finally(() => { .finally(() => {
message.destroy('sending')
setIsSending(false) setIsSending(false)
}) })
} }
@@ -435,10 +460,28 @@ const Verify: React.FC = () => {
const Forget: React.FC = () => { const Forget: React.FC = () => {
const navigate = useNavigate() const navigate = useNavigate()
const [searchParams] = useSearchParams() const [searchParams] = useSearchParams()
const turnstileRef = useRef<TurnstileInstance>()
const retrieveTurnstileRef = useRef<TurnstileInstance>()
const [isSending, setIsSending] = useState(false) const [isSending, setIsSending] = useState(false)
const [isSent, setIsSent] = useState(false) const [isSent, setIsSent] = useState(false)
const [isChanging, setIsChanging] = useState(false) const [isChanging, setIsChanging] = useState(false)
const [isChanged, setIsChanged] = useState(false) const [isChanged, setIsChanged] = useState(false)
const [captchaCode, setCaptchaCode] = useState('')
const [retrieveCpatchaCode, setRetrieveCpatchaCode] = useState('')
useUpdatedEffect(() => {
if (location.pathname !== '/forget') {
return
}
setTimeout(() => {
if (!searchParams.get('code')) {
turnstileRef.current?.execute()
} else {
retrieveTurnstileRef.current?.execute()
}
}, 200)
}, [location.pathname])
const handleOnSend = (forgetParam: ForgetParam) => { const handleOnSend = (forgetParam: ForgetParam) => {
if (isSending) { if (isSending) {
@@ -446,28 +489,40 @@ const Forget: React.FC = () => {
} }
setIsSending(true) setIsSending(true)
void r_auth_forget(forgetParam) if (!captchaCode) {
.then((res) => { void message.warning('请先通过验证')
const response = res.data setIsSending(false)
switch (response.code) { return
case PERMISSION_FORGET_SUCCESS: }
void message.success('已发送验证邮件,请查收')
setIsSent(true) void r_auth_forget({ email: forgetParam.email, captchaCode }).then((res) => {
break const response = res.data
case PERMISSION_USER_NOT_FOUND: switch (response.code) {
void message.error('用户不存在') case PERMISSION_FORGET_SUCCESS:
break void message.success('已发送验证邮件,请查收')
default: setIsSent(true)
void message.error('出错了,请稍后重试') setIsSending(false)
} break
}) case PERMISSION_USER_NOT_FOUND:
.finally(() => { void message.error('用户不存在')
setIsSending(false) setIsSending(false)
}) break
case SYSTEM_INVALID_CAPTCHA_CODE:
void message.error('验证码有误,请刷新重试')
break
default:
void message.error('出错了,请稍后重试')
setIsSending(false)
}
})
} }
const handleOnRetry = () => { const handleOnRetry = () => {
setIsSent(false) setIsSent(false)
setTimeout(() => {
turnstileRef.current?.execute()
}, 200)
} }
const handleOnChange = (retrieveParam: RetrieveParam) => { const handleOnChange = (retrieveParam: RetrieveParam) => {
@@ -478,26 +533,29 @@ const Forget: React.FC = () => {
void r_auth_retrieve({ void r_auth_retrieve({
code: searchParams.get('code') ?? '', code: searchParams.get('code') ?? '',
password: retrieveParam.password password: retrieveParam.password,
}) captchaCode: retrieveCpatchaCode
.then((res) => { }).then((res) => {
const response = res.data const response = res.data
switch (response.code) { switch (response.code) {
case PERMISSION_RETRIEVE_SUCCESS: case PERMISSION_RETRIEVE_SUCCESS:
void message.success('密码已更新') void message.success('密码已更新')
setIsChanged(true) setIsChanged(true)
break setIsChanging(false)
case PERMISSION_RETRIEVE_CODE_ERROR_OR_EXPIRED: break
void message.error('验证码有误,请重新获取') case PERMISSION_RETRIEVE_CODE_ERROR_OR_EXPIRED:
break void message.error('请重新获取邮件')
default: setIsChanging(false)
void message.error('出错了,请稍后重试') break
} case SYSTEM_INVALID_CAPTCHA_CODE:
}) void message.error('验证码有误,请刷新重试')
.finally(() => { break
setIsChanging(false) default:
}) void message.error('出错了,请稍后重试')
setIsChanging(false)
}
})
} }
return ( return (
@@ -526,6 +584,15 @@ const Forget: React.FC = () => {
disabled={isSending} disabled={isSending}
/> />
</AntdForm.Item> </AntdForm.Item>
<AntdForm.Item>
<Turnstile
id={'forget-turnstile'}
ref={turnstileRef}
siteKey={H_CAPTCHA_SITE_KEY}
options={{ theme: 'light', execution: 'execute' }}
onSuccess={setCaptchaCode}
/>
</AntdForm.Item>
<AntdForm.Item> <AntdForm.Item>
<AntdButton <AntdButton
style={{ width: '100%' }} style={{ width: '100%' }}
@@ -589,6 +656,15 @@ const Forget: React.FC = () => {
disabled={isChanging} disabled={isChanging}
/> />
</AntdForm.Item> </AntdForm.Item>
<AntdForm.Item>
<Turnstile
id={'retrieve-turnstile'}
ref={retrieveTurnstileRef}
siteKey={H_CAPTCHA_SITE_KEY}
options={{ theme: 'light', execution: 'execute' }}
onSuccess={setRetrieveCpatchaCode}
/>
</AntdForm.Item>
<AntdForm.Item> <AntdForm.Item>
<AntdButton <AntdButton
style={{ width: '100%' }} style={{ width: '100%' }}
@@ -621,12 +697,18 @@ const SignIn: React.FC = () => {
const { refreshRouter } = useContext(AppContext) const { refreshRouter } = useContext(AppContext)
const navigate = useNavigate() const navigate = useNavigate()
const [searchParams] = useSearchParams() const [searchParams] = useSearchParams()
const turnstileRef = useRef<TurnstileInstance>()
const [isSigningIn, setIsSigningIn] = useState(false) const [isSigningIn, setIsSigningIn] = useState(false)
const [captchaCode, setCaptchaCode] = useState('') const [captchaCode, setCaptchaCode] = useState('')
const handleOnTurnstileSuccess = (token: string) => { useUpdatedEffect(() => {
setCaptchaCode(token) if (location.pathname !== '/login') {
} return
}
setTimeout(() => {
turnstileRef.current?.execute()
}, 200)
}, [location.pathname])
const handleOnFinish = (loginParam: LoginParam) => { const handleOnFinish = (loginParam: LoginParam) => {
if (isSigningIn) { if (isSigningIn) {
@@ -749,9 +831,11 @@ const SignIn: React.FC = () => {
</AntdForm.Item> </AntdForm.Item>
<AntdForm.Item> <AntdForm.Item>
<Turnstile <Turnstile
id={'sign-in-turnstile'}
ref={turnstileRef}
siteKey={H_CAPTCHA_SITE_KEY} siteKey={H_CAPTCHA_SITE_KEY}
options={{ theme: 'light' }} options={{ theme: 'light', execution: 'execute' }}
onSuccess={handleOnTurnstileSuccess} onSuccess={setCaptchaCode}
/> />
</AntdForm.Item> </AntdForm.Item>
<FlexBox direction={'horizontal'} className={'addition'}> <FlexBox direction={'horizontal'} className={'addition'}>

View File

@@ -5,7 +5,8 @@ import {
PERMISSION_TOKEN_HAS_EXPIRED, PERMISSION_TOKEN_HAS_EXPIRED,
PERMISSION_TOKEN_ILLEGAL, PERMISSION_TOKEN_ILLEGAL,
PERMISSION_TOKEN_RENEW_SUCCESS, PERMISSION_TOKEN_RENEW_SUCCESS,
PERMISSION_UNAUTHORIZED PERMISSION_UNAUTHORIZED,
SYSTEM_REQUEST_TOO_FREQUENT
} from '@/constants/common.constants' } from '@/constants/common.constants'
import { getRedirectUrl } from '@/util/route' import { getRedirectUrl } from '@/util/route'
import { getToken, setToken, removeToken } from '@/util/auth' import { getToken, setToken, removeToken } from '@/util/auth'
@@ -97,6 +98,13 @@ service.interceptors.response.use(
</> </>
) )
throw response?.data throw response?.data
case SYSTEM_REQUEST_TOO_FREQUENT:
void message.warning(
<>
<strong></strong>
</>
)
throw response?.data
} }
return response return response
}, },