diff --git a/src/api/user.api.ts b/src/api/user.api.ts index c2e9730..da5c92b 100644 --- a/src/api/user.api.ts +++ b/src/api/user.api.ts @@ -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('/api/users/token', data) } +export async function forgetPassword(data: ForgetPasswordInputDto) { + return axios.patch('/api/users/password', data) +} + let refreshing: Promise> | null export async function refreshToken(data: TokenRefreshPayload) { if (!refreshing) { diff --git a/src/api/user.interface.ts b/src/api/user.interface.ts index 684727b..4a4ed43 100644 --- a/src/api/user.interface.ts +++ b/src/api/user.interface.ts @@ -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 diff --git a/src/pages/forget-password.tsx b/src/pages/forget-password.tsx new file mode 100644 index 0000000..52d2694 --- /dev/null +++ b/src/pages/forget-password.tsx @@ -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({ + 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 ( + + + + + + + 找回密码 + + + + + + + + + + + + + + + + 登录 + + + + + + ) +}