diff --git a/web/public/locales/en/translation.json b/web/public/locales/en/translation.json index ba08e88298..bb87cb9a28 100644 --- a/web/public/locales/en/translation.json +++ b/web/public/locales/en/translation.json @@ -293,7 +293,7 @@ "LoginTip": "Please input your phone number and verification code", "LoginWithGithub": "Login with GitHub", "ValidationCodeTip": "Please input the verification code", - "ForgotPassword": "Forgot Password", + "ForgotPassword": "Forgot Password?", "Register": "Register", "ToRegister": "Go to Register", "ToLogin": "Go to Login", @@ -308,7 +308,10 @@ "PasswordNotMatch": "The two passwords you entered do not match", "SmsCodeSendSuccess": "Verification code sent successfully", "ValidationCodePlaceholder": "Please input the verification code", - "SignupSuccess": "Sign up successfully" + "SignupSuccess": "Sign up successfully", + "ResetPassword": "Reset Password", + "ResetPasswordSuccess": "Reset password successfully", + "NewPassword": "New Password" }, "Time": "Time", "CreateTime": "Created", diff --git a/web/public/locales/zh-CN/translation.json b/web/public/locales/zh-CN/translation.json index 4437bd4892..a8014f61dc 100644 --- a/web/public/locales/zh-CN/translation.json +++ b/web/public/locales/zh-CN/translation.json @@ -293,7 +293,7 @@ "LoginTip": "请输入手机号和验证码", "LoginWithGithub": "GitHub 登录", "ValidationCodeTip": "请输入验证码", - "ForgotPassword": "忘记密码", + "ForgotPassword": "忘记密码?", "Register": "注册", "ToRegister": "去注册", "ToLogin": "去登录", @@ -308,7 +308,10 @@ "PasswordNotMatch": "两次输入的密码不一致", "SmsCodeSendSuccess": "验证码发送成功", "ValidationCodePlaceholder": "请输入验证码", - "SignupSuccess": "注册成功" + "SignupSuccess": "注册成功", + "ResetPassword": "重置密码", + "ResetPasswordSuccess": "重置密码成功", + "NewPassword": "新密码" }, "Time": "时间", "CreateTime": "创建时间", @@ -327,4 +330,4 @@ "CreateFolder": "新建文件夹", "Or": "或", "BeforeunloadTip": "确定要离开当前编辑页面吗?" -} \ No newline at end of file +} diff --git a/web/public/locales/zh/translation.json b/web/public/locales/zh/translation.json index 8669c4b1bd..1921d762bb 100644 --- a/web/public/locales/zh/translation.json +++ b/web/public/locales/zh/translation.json @@ -293,7 +293,7 @@ "LoginTip": "请输入手机号和验证码", "LoginWithGithub": "GitHub 登录", "ValidationCodeTip": "请输入验证码", - "ForgotPassword": "忘记密码", + "ForgotPassword": "忘记密码?", "Register": "注册", "ToRegister": "去注册", "ToLogin": "去登录", @@ -308,7 +308,10 @@ "PasswordNotMatch": "两次输入的密码不一致", "SmsCodeSendSuccess": "验证码发送成功", "ValidationCodePlaceholder": "请输入验证码", - "SignupSuccess": "注册成功" + "SignupSuccess": "注册成功", + "ResetPassword": "重置密码", + "ResetPasswordSuccess": "重置密码成功", + "NewPassword": "新密码" }, "Time": "时间", "CreateTime": "创建时间", @@ -327,4 +330,4 @@ "CreateFolder": "新建文件夹", "Or": "或", "BeforeunloadTip": "确定要离开当前编辑页面吗?" -} \ No newline at end of file +} diff --git a/web/src/apis/v1/api-auto.d.ts b/web/src/apis/v1/api-auto.d.ts index d41f2105a6..def58b3677 100644 --- a/web/src/apis/v1/api-auto.d.ts +++ b/web/src/apis/v1/api-auto.d.ts @@ -602,6 +602,22 @@ declare namespace Paths { export type Responses = any; } + namespace AuthControllerBindPhone { + export type QueryParameters = any; + + export type BodyParameters = any; + + export type Responses = any; + } + + namespace AuthControllerBindUsername { + export type QueryParameters = any; + + export type BodyParameters = any; + + export type Responses = any; + } + namespace SubscriptionControllerCreate { export type QueryParameters = any; diff --git a/web/src/apis/v1/auth.ts b/web/src/apis/v1/auth.ts index 56e2d23477..dc00de5f52 100644 --- a/web/src/apis/v1/auth.ts +++ b/web/src/apis/v1/auth.ts @@ -89,3 +89,29 @@ export async function AuthControllerGetProviders( params: params, }); } + +/** + * bind phone + * @param {string} phone + * @param {string} code + */ +export async function AuthControllerBindPhone( + params: Paths.AuthControllerBindPhone.BodyParameters | any, +): Promise { + return request(`/v1/auth/bind/phone`, { + method: "POST", + data: params, + }); +} + +/** + * bind username + */ +export async function AuthControllerBindUsername( + params: Paths.AuthControllerBindUsername.BodyParameters | any, +): Promise { + return request(`/v1/auth/bind/username`, { + method: "POST", + data: params, + }); +} diff --git a/web/src/components/DependenceList/index.module.scss b/web/src/components/DependenceList/index.module.scss index 5cc2ddad77..d4f3e67bcc 100644 --- a/web/src/components/DependenceList/index.module.scss +++ b/web/src/components/DependenceList/index.module.scss @@ -14,4 +14,4 @@ vertical-align: middle; } } -} \ No newline at end of file +} diff --git a/web/src/pages/auth/reset-password/index.tsx b/web/src/pages/auth/reset-password/index.tsx new file mode 100644 index 0000000000..36ee923069 --- /dev/null +++ b/web/src/pages/auth/reset-password/index.tsx @@ -0,0 +1,224 @@ +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { useNavigate } from "react-router-dom"; +import { ViewIcon, ViewOffIcon } from "@chakra-ui/icons"; +import { + Button, + FormControl, + FormLabel, + Input, + InputGroup, + InputRightElement, +} from "@chakra-ui/react"; +import { t } from "i18next"; + +import { useResetPasswordMutation, useSendSmsCodeMutation } from "@/pages/auth/service"; +import useGlobalStore from "@/pages/globalStore"; + +type FormData = { + phone?: string; + validationCode?: string; + account: string; + password: string; + confirmPassword: string; +}; + +export default function ResetPassword() { + const resetPasswordMutation = useResetPasswordMutation(); + const sendSmsCodeMutation = useSendSmsCodeMutation(); + + const { showSuccess, showError } = useGlobalStore(); + const navigate = useNavigate(); + + const [isSendSmsCode, setIsSendSmsCode] = useState(false); + const [countdown, setCountdown] = useState(60); + const [isShowPassword, setIsShowPassword] = useState(false); + + const { + register, + getValues, + handleSubmit, + formState: { errors }, + } = useForm({ + defaultValues: { + phone: "", + validationCode: "", + password: "", + confirmPassword: "", + }, + }); + + const onSubmit = async (data: FormData) => { + if (data.password !== data.confirmPassword) { + showError(t("AuthPanel.PasswordNotMatch")); + return; + } + + const params = { + phone: data.phone, + code: data.validationCode, + password: data.password, + type: "ResetPassword", + }; + + const res = await resetPasswordMutation.mutateAsync(params); + + if (res?.data) { + showSuccess(t("AuthPanel.ResetPasswordSuccess")); + navigate("/login", { replace: true }); + } + }; + + const handleSendSmsCode = async () => { + if (isSendSmsCode) { + return; + } + + const phone = getValues("phone") || ""; + const isValidate = /^1[2-9]\d{9}$/.test(phone); + if (!isValidate) { + showError(t("AuthPanel.PhoneTip")); + return; + } + + switchSmsCodeStatus(); + + const res = await sendSmsCodeMutation.mutateAsync({ + phone, + type: "ResetPassword", + }); + + if (res?.data) { + showSuccess(t("AuthPanel.SmsCodeSendSuccess")); + } + }; + + const switchSmsCodeStatus = () => { + setIsSendSmsCode(true); + setCountdown(60); + const timer = setInterval(() => { + setCountdown((countdown) => { + if (countdown === 0) { + clearInterval(timer); + setIsSendSmsCode(false); + return 0; + } + return countdown - 1; + }); + }, 1000); + }; + + return ( +
+
+ logo +
+
+ + + {t("AuthPanel.Phone")} + + + + + + + + + + + {t("AuthPanel.ValidationCode")} + + + + + + {t("AuthPanel.NewPassword")} + + + + + {isShowPassword ? ( + setIsShowPassword(false)} /> + ) : ( + setIsShowPassword(true)} /> + )} + + + + + + {t("AuthPanel.ConfirmPassword")} + + + + + {isShowPassword ? ( + setIsShowPassword(false)} /> + ) : ( + setIsShowPassword(true)} /> + )} + + + +
+ +
+
+ +
+
+
+ ); +} diff --git a/web/src/pages/auth/signin/mods/LoginByPasswordPanel/index.tsx b/web/src/pages/auth/signin/mods/LoginByPasswordPanel/index.tsx index 00a0e7ae64..8935d64737 100644 --- a/web/src/pages/auth/signin/mods/LoginByPasswordPanel/index.tsx +++ b/web/src/pages/auth/signin/mods/LoginByPasswordPanel/index.tsx @@ -93,21 +93,33 @@ export default function LoginByPasswordPanel({ > {t("AuthPanel.Login")} -
- {showPhoneSigninBtn && ( - - )} - {showSignupBtn && ( +
+
- )} +
+
+ {showPhoneSigninBtn && ( + + )} + {showSignupBtn && ( + + )} +
diff --git a/web/src/routes/index.tsx b/web/src/routes/index.tsx index 80d1e060b3..a87733228c 100644 --- a/web/src/routes/index.tsx +++ b/web/src/routes/index.tsx @@ -32,6 +32,17 @@ const routes = [ }, ], }, + { + path: "/reset-password", + element: , + auth: false, + children: [ + { + path: "/reset-password", + element: () => import("@/pages/auth/reset-password"), + }, + ], + }, { path: "/", children: [