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 ( +
{error.message}
} + {error && ( +{error.message}
+