密码重置页

This commit is contained in:
秦秋旭 2023-02-24 17:01:32 +08:00
parent bd0332a582
commit 3aef983325
3 changed files with 160 additions and 0 deletions

View File

@ -3,6 +3,7 @@ import { type AxiosResponse } from 'axios'
import {
LoginInputDto,
RegisterInputDto,
ForgetPasswordInputDto,
Token,
TokenRefreshPayload,
User,
@ -16,6 +17,10 @@ export async function login(data: LoginInputDto) {
return axios.post<Token>('/api/users/token', data)
}
export async function forgetPassword(data: ForgetPasswordInputDto) {
return axios.patch<Token>('/api/users/password', data)
}
let refreshing: Promise<AxiosResponse<Token>> | null
export async function refreshToken(data: TokenRefreshPayload) {
if (!refreshing) {

View File

@ -12,6 +12,12 @@ export interface RegisterInputDto extends EmailVerifyDto {
username?: string
}
/** 重置密码入参 */
export interface ForgetPasswordInputDto extends EmailVerifyDto {
userId: string
password: string
}
export interface Token {
accessToken: string
refreshToken: string

View File

@ -0,0 +1,149 @@
import Avatar from '@mui/material/Avatar'
import Button from '@mui/material/Button'
import TextField from '@mui/material/TextField'
import Link from '@mui/material/Link'
import Grid from '@mui/material/Unstable_Grid2'
import Box from '@mui/material/Box'
import KeyIcon from '@mui/icons-material/Key'
import Typography from '@mui/material/Typography'
import Container from '@mui/material/Container'
import { useFormik } from 'formik'
import { useRouter } from 'next/router'
import * as yup from '@/utils/validation'
import * as api from '@/api'
import { ForgetPasswordInputDto } from '@/api/user.interface'
import { EmailVerifyCodeScene } from '@/api/email.interface'
import { useCountdown, COUNTDOWN_SECONDS } from '@/utils/useCountdown'
import { toast } from 'react-toastify'
export default function ForgetPassword() {
const router = useRouter()
const { countdown, setCountdown } = useCountdown()
const formik = useFormik<ForgetPasswordInputDto>({
initialValues: {
email: '',
password: '',
verifyCode: '',
token: '',
userId: '',
},
validateOnChange: false,
validationSchema: yup.object({
email: yup.emailSchema,
password: yup.passwordSchema,
verifyCode: yup.verifyCodeSchema,
}),
onSubmit: async (values) => {
const res = await api.user.forgetPassword(values)
localStorage.setItem('accessToken', res.data.accessToken)
localStorage.setItem('refreshToken', res.data.refreshToken)
toast.success('密码重置成功!', {
onClose: () => router.push('/login'),
})
},
})
async function sendVerifyCode() {
await formik.validateField('email')
if (formik.errors.email) return
setCountdown(COUNTDOWN_SECONDS)
try {
const res = await api.email.sendEmailVerifyCode({
email: formik.values.email,
scene: EmailVerifyCodeScene.forgetPassword,
})
formik.setFieldValue('token', res.data.token)
formik.setFieldValue('userId', res.data.userId)
} catch (err) {
setCountdown(0)
}
}
return (
<Container component="main" sx={{ width: 400 }}>
<Box
sx={{
marginTop: '20vh',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<Avatar sx={{ m: 1, bgcolor: 'primary.main' }}>
<KeyIcon />
</Avatar>
<Typography component="h1" variant="h5">
</Typography>
<Box
component="form"
onSubmit={formik.handleSubmit}
noValidate
sx={{ mt: 1, width: '100%' }}
>
<TextField
{...formik.getFieldProps('email')}
required
error={formik.touched.email && !!formik.errors.email}
helperText={(formik.touched.email && formik.errors.email) || ' '}
fullWidth
label="邮箱"
autoComplete="email"
autoFocus
sx={{ mt: 1 }}
/>
<Grid container spacing={2}>
<Grid>
<TextField
{...formik.getFieldProps('verifyCode')}
required
error={formik.touched.verifyCode && !!formik.errors.verifyCode}
helperText={
(formik.touched.verifyCode && formik.errors.verifyCode) || ' '
}
fullWidth
label="验证码"
autoComplete="verifyCode"
autoFocus
sx={{ mt: 1 }}
/>
</Grid>
<Grid mt={2} sx={{ flexGrow: 1 }}>
<Button
size="large"
fullWidth
variant="contained"
disabled={countdown > 0}
onClick={sendVerifyCode}
>
{countdown > 0 ? countdown : '发送验证码'}
</Button>
</Grid>
</Grid>
<TextField
{...formik.getFieldProps('password')}
required
error={formik.touched.password && !!formik.errors.password}
helperText={
(formik.touched.password && formik.errors.password) || ' '
}
fullWidth
label="新密码"
type="password"
autoComplete="current-password"
sx={{ mt: 1 }}
/>
<Button type="submit" fullWidth variant="contained" sx={{ mt: 1 }}>
</Button>
<Grid container justifyContent="flex-end" sx={{ mt: 2 }}>
<Grid>
<Link href="/login"></Link>
</Grid>
</Grid>
</Box>
</Box>
</Container>
)
}