From 7468be3190f9aa01e9494adac2245875a1cef1b0 Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Fri, 18 Mar 2022 16:11:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=BF=98=E8=AE=B0?= =?UTF-8?q?=E5=AF=86=E7=A0=81=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/index.tsx | 2 + shared/model/user.ts | 29 +++++ web/src/routes/Entry/ForgetPasswordView.tsx | 128 ++++++++++++++++++++ web/src/routes/Entry/LoginView.tsx | 14 ++- web/src/routes/Entry/index.tsx | 3 + 5 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 web/src/routes/Entry/ForgetPasswordView.tsx diff --git a/shared/index.tsx b/shared/index.tsx index dc109eed..13402cd8 100644 --- a/shared/index.tsx +++ b/shared/index.tsx @@ -141,6 +141,8 @@ export { loginWithToken, registerWithEmail, modifyUserPassword, + forgetPassword, + resetPassword, createTemporaryUser, claimTemporaryUser, searchUserWithUniqueName, diff --git a/shared/model/user.ts b/shared/model/user.ts index ef1b7d38..db07ee89 100644 --- a/shared/model/user.ts +++ b/shared/model/user.ts @@ -82,6 +82,9 @@ export async function registerWithEmail( return data; } +/** + * 修改密码 + */ export async function modifyUserPassword( oldPassword: string, newPassword: string @@ -92,6 +95,32 @@ export async function modifyUserPassword( }); } +/** + * 忘记密码 + * @param email 邮箱 + */ +export async function forgetPassword(email: string) { + await request.post('/api/user/forgetPassword', { + email, + }); +} + +/** + * 忘记密码 + * @param email 邮箱 + */ +export async function resetPassword( + email: string, + password: string, + otp: string +) { + await request.post('/api/user/resetPassword', { + email, + password, + otp, + }); +} + /** * 创建访客账号 * @param nickname 访客昵称 diff --git a/web/src/routes/Entry/ForgetPasswordView.tsx b/web/src/routes/Entry/ForgetPasswordView.tsx new file mode 100644 index 00000000..f5515ca8 --- /dev/null +++ b/web/src/routes/Entry/ForgetPasswordView.tsx @@ -0,0 +1,128 @@ +import { Icon } from '@/components/Icon'; +import { + forgetPassword, + resetPassword, + showToasts, + t, + useAsyncRequest, +} from 'tailchat-shared'; +import React, { useState } from 'react'; +import { Spinner } from '../../components/Spinner'; +import { string } from 'yup'; +import { useNavToView } from './utils'; + +/** + * 登录视图 + */ +export const ForgetPasswordView: React.FC = React.memo(() => { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [otp, setOtp] = useState(''); + const [sendedEmail, setSendedEmail] = useState(false); + + const navToView = useNavToView(); + + const [{ loading: sendEmailLoading }, handleSendEmail] = + useAsyncRequest(async () => { + await forgetPassword(email); + setSendedEmail(true); + showToasts(`已发送邮件到 ${email}`, 'success'); + }, [email]); + + const [{ loading }, handleResetPassword] = useAsyncRequest(async () => { + await string() + .email(t('邮箱格式不正确')) + .required(t('邮箱不能为空')) + .validate(email); + + await string() + .min(6, t('密码不能低于6位')) + .required(t('密码不能为空')) + .validate(password); + + await string().length(6, t('OTP为6位数字')).validate(otp); + + await resetPassword(email, password, otp); + + showToasts(t('密码重置成功,现在回到登录页'), 'success'); + navToView('/entry/login'); + }, [email, password, otp, navToView]); + + return ( +
+
{t('忘记密码')}
+ +
+
+
{t('邮箱')}
+ setEmail(e.target.value)} + /> +
+ + {!sendedEmail && ( + + )} + + {sendedEmail && ( + <> +
+
{t('OTP')}
+ setOtp(e.target.value)} + /> +
+ +
+
{t('新密码')}
+ setPassword(e.target.value)} + /> +
+ + + + )} + + +
+
+ ); +}); +ForgetPasswordView.displayName = 'ForgetPasswordView'; diff --git a/web/src/routes/Entry/LoginView.tsx b/web/src/routes/Entry/LoginView.tsx index 43c89d35..76414313 100644 --- a/web/src/routes/Entry/LoginView.tsx +++ b/web/src/routes/Entry/LoginView.tsx @@ -63,7 +63,7 @@ export const LoginView: React.FC = React.memo(() => { return (
-
{t('登录 Tail Chat')}
+
{t('登录 Tailchat')}
@@ -89,7 +89,17 @@ export const LoginView: React.FC = React.memo(() => { />
- {error &&

{error.message}

} + {error && ( +
+

{error.message}

+
navToView('/entry/forget')} + > + {t('忘记密码?')} +
+
+ )}