diff --git a/frontend/src/Route.tsx b/frontend/src/Route.tsx
index 0d5597a..6ff6962 100644
--- a/frontend/src/Route.tsx
+++ b/frontend/src/Route.tsx
@@ -9,7 +9,9 @@ import Layout from '@/views/Layout'
import UserAdmin from "@/views/admin/UserAdmin";
import NeedLogin from "@/components/NeedLogin";
import Index from "@/views/Index";
-import SignUpEmail from "@/views/SignUpEmail";
+import SendEmail from "@/views/SendEmail";
+import { sendForgotEmail, sendRegEmail } from "@/apis/apis";
+import Forgot from "@/views/Forgot";
const router = createBrowserRouter([
{ path: "*", Component: Root },
@@ -24,7 +26,9 @@ function Root() {
404
} />
} />
} />
- } />
+ } />
+ } />
+ } />
}>
} />
diff --git a/frontend/src/apis/apis.ts b/frontend/src/apis/apis.ts
index fa53ba4..b996595 100644
--- a/frontend/src/apis/apis.ts
+++ b/frontend/src/apis/apis.ts
@@ -129,4 +129,28 @@ export async function sendRegEmail(email: string, captchaToken: string) {
})
})
return await apiGet(r)
+}
+
+export async function sendForgotEmail(email: string, captchaToken: string) {
+ const r = await fetch(root() + "/api/v1/user/forgot_email", {
+ method: "POST",
+ body: JSON.stringify({
+ "email": email,
+ "captchaToken": captchaToken
+ })
+ })
+ return await apiGet(r)
+}
+
+
+export async function forgotPassWord(email: string, emailJwt: string, password: string) {
+ const r = await fetch(root() + "/api/v1/user/forgot", {
+ method: "POST",
+ body: JSON.stringify({
+ "email": email,
+ "emailJwt": emailJwt,
+ "passWord": password,
+ })
+ })
+ return await apiGet(r)
}
\ No newline at end of file
diff --git a/frontend/src/views/Forgot.tsx b/frontend/src/views/Forgot.tsx
new file mode 100644
index 0000000..f78d79e
--- /dev/null
+++ b/frontend/src/views/Forgot.tsx
@@ -0,0 +1,120 @@
+import Avatar from '@mui/material/Avatar';
+import Button from '@mui/material/Button';
+import CssBaseline from '@mui/material/CssBaseline';
+import Grid from '@mui/material/Grid';
+import Box from '@mui/material/Box';
+import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
+import Typography from '@mui/material/Typography';
+import Container from '@mui/material/Container';
+import TextField from '@mui/material/TextField';
+import { useTitle } from 'ahooks';
+import { useEffect, useState } from 'react';
+import Snackbar from '@mui/material/Snackbar';
+import Alert from '@mui/material/Alert';
+import Loading from '@/components/Loading';
+import { produce } from 'immer';
+import { forgotPassWord } from '@/apis/apis';
+import { useNavigate } from 'react-router-dom';
+
+export default function Forgot() {
+ const [err, setErr] = useState("")
+ useTitle("找回密码")
+ const [passerr, setPasserr] = useState("")
+ const [pass, setPass] = useState({
+ pass1: "",
+ pass2: "",
+ })
+ const [load, setLoad] = useState(false)
+ const [email, setEmail] = useState("")
+ const [code, setCode] = useState("")
+ const navigate = useNavigate();
+
+ useEffect(() => {
+ if (pass.pass1 != pass.pass2 && pass.pass2 != "") {
+ setPasserr("密码不相等")
+ return
+ }
+ setPasserr("")
+ }, [pass.pass1, pass.pass2])
+
+ const u = new URL(location.href)
+
+ useEffect(() => {
+ setEmail(u.searchParams.get("email") ?? "")
+ setCode(u.searchParams.get("code") ?? "")
+ }, [u.searchParams])
+
+
+ const onSubmit = (e: React.FormEvent) => {
+ e.preventDefault()
+ setLoad(true)
+ forgotPassWord(email, code, pass.pass1).then(() => {
+ navigate("/")
+ }).catch(e => {
+ setErr(String(e))
+ }).finally(() => { setLoad(false) })
+ }
+
+
+ return (
+
+
+
+
+
+
+
+ 找回密码
+
+
+
+
+ setPass(produce(v => { v.pass1 = p.target.value }))}
+ autoComplete="new-password"
+ />
+
+
+ setPass(produce(v => { v.pass2 = p.target.value }))}
+ autoComplete="new-password"
+ />
+
+
+
+
+
+
+ setErr("")} severity="error">{err}
+
+
+ {load && }
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/views/Login.tsx b/frontend/src/views/Login.tsx
index a4fe5f0..8d1127d 100644
--- a/frontend/src/views/Login.tsx
+++ b/frontend/src/views/Login.tsx
@@ -13,7 +13,7 @@ import Snackbar from '@mui/material/Snackbar';
import Alert from '@mui/material/Alert';
import { useSetAtom } from 'jotai';
import { token, user } from '@/store/store'
-import { login } from '@/apis/apis'
+import { getConfig, login } from '@/apis/apis'
import { Link as RouterLink, useNavigate } from "react-router-dom";
import Loading from '@/components/Loading'
import CheckInput, { refType } from '@/components/CheckInput'
@@ -21,6 +21,7 @@ import useTitle from '@/hooks/useTitle';
import CaptchaWidget from '@/components/CaptchaWidget';
import type { refType as CaptchaWidgetRef } from '@/components/CaptchaWidget'
import { ApiErr } from '@/apis/error';
+import { useRequest } from 'ahooks';
@@ -35,6 +36,16 @@ export default function SignIn() {
const captchaRef = React.useRef(null)
const [captchaToken, setCaptchaToken] = useState("");
+ const server = useRequest(getConfig, {
+ cacheKey: "/api/v1/config",
+ staleTime: 60000,
+ onError: e => {
+ console.warn(e)
+ setErr(String(e))
+ }
+ })
+
+
const handleSubmit = (event: React.FormEvent) => {
event.preventDefault();
@@ -66,7 +77,7 @@ export default function SignIn() {
switch (v.code) {
case 10:
setErr("验证码错误")
- return
+ return
case 6:
setErr("密码或用户名错误")
return
@@ -137,9 +148,9 @@ export default function SignIn() {
- {/*
+ {server.data?.NeedEmail &&
忘记密码?
- */}
+ }
diff --git a/frontend/src/views/SignUpEmail.tsx b/frontend/src/views/SendEmail.tsx
similarity index 93%
rename from frontend/src/views/SignUpEmail.tsx
rename to frontend/src/views/SendEmail.tsx
index 203ff79..7201c54 100644
--- a/frontend/src/views/SignUpEmail.tsx
+++ b/frontend/src/views/SendEmail.tsx
@@ -12,7 +12,7 @@ import Select from '@mui/material/Select';
import MenuItem from '@mui/material/MenuItem';
import TextField from '@mui/material/TextField';
import { useRequest, useTitle } from 'ahooks';
-import { getConfig, sendRegEmail } from '@/apis/apis';
+import { getConfig } from '@/apis/apis';
import { useEffect, useRef, useState } from 'react';
import Snackbar from '@mui/material/Snackbar';
import Alert from '@mui/material/Alert';
@@ -26,14 +26,14 @@ import { useNavigate } from "react-router-dom";
import { ApiErr } from '@/apis/error';
import Loading from '@/components/Loading';
-export default function SignUpEmail() {
+export default function SendEmail({ title, anyEmail = false, sendService }: { title: string, anyEmail?: boolean, sendService: (email: string, captchaToken: string) => Promise }) {
const [err, setErr] = useState("");
const [domain, setDomain] = useState("");
const [email, setEmail] = useState("")
const captchaRef = useRef(null)
const [captchaToken, setCaptchaToken] = useState("");
const [open, setOpen] = useState(false);
- useTitle("注册")
+ useTitle(title)
const navigate = useNavigate();
const [helperText, setHelperText] = useState("")
const [loading, setLoading] = useState(false);
@@ -73,7 +73,7 @@ export default function SignUpEmail() {
return email
})()
- if (!/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(sendEmail)){
+ if (!/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(sendEmail)) {
setHelperText("邮箱格式错误")
return
}
@@ -82,7 +82,7 @@ export default function SignUpEmail() {
return
}
setLoading(true)
- sendRegEmail(sendEmail, captchaToken).then(() => setOpen(true)).catch(e => {
+ sendService(sendEmail, captchaToken).then(() => setOpen(true)).catch(e => {
captchaRef.current?.reload()
console.warn(e)
if (e instanceof ApiErr) {
@@ -120,7 +120,7 @@ export default function SignUpEmail() {
- 输入邮箱
+ {title}
@@ -135,7 +135,7 @@ export default function SignUpEmail() {
onChange={emailonChange}
/>
{
- server.data?.AllowDomain.length != 0 &&
+ server.data?.AllowDomain.length != 0 && !anyEmail &&
域名