注册
This commit is contained in:
parent
2febce1c3a
commit
0406612a5a
@ -1,13 +1,30 @@
|
||||
import { Routes, Route, Outlet } from "react-router-dom";
|
||||
import { Routes, Route, Outlet, createBrowserRouter, RouterProvider } from "react-router-dom";
|
||||
import Login from '@/views/Login'
|
||||
import Register from '@/views/Register'
|
||||
import { ScrollRestoration } from "react-router-dom";
|
||||
|
||||
export function PageRoute() {
|
||||
const router = createBrowserRouter([
|
||||
{ path: "*", Component: Root },
|
||||
]);
|
||||
|
||||
function Root() {
|
||||
return (
|
||||
<>
|
||||
<Routes>
|
||||
<Route path="/" element={<Layout />}></Route>
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/register" element={<Register />} />
|
||||
</Routes>
|
||||
<ScrollRestoration />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export function PageRoute() {
|
||||
return (
|
||||
<>
|
||||
<RouterProvider router={router} />;
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
21
frontend/src/apis/apis.ts
Normal file
21
frontend/src/apis/apis.ts
Normal file
@ -0,0 +1,21 @@
|
||||
interface tokenData {
|
||||
accessToken: string
|
||||
selectedProfile: {
|
||||
name: string
|
||||
}
|
||||
}
|
||||
|
||||
export async function login(username: string, password: string) {
|
||||
const v = await fetch(import.meta.env.VITE_APIADDR + "/api/yggdrasil/authserver/authenticate", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
"username": username,
|
||||
"password": password,
|
||||
})
|
||||
})
|
||||
const data = await v.json()
|
||||
if (!v.ok) {
|
||||
throw data?.errorMessage
|
||||
}
|
||||
return data as tokenData
|
||||
}
|
@ -1,15 +1,12 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.tsx'
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<BrowserRouter>
|
||||
<CssBaseline>
|
||||
<App />
|
||||
</CssBaseline>
|
||||
</BrowserRouter>
|
||||
</React.StrictMode>
|
||||
)
|
||||
|
3
frontend/src/utils/email.ts
Normal file
3
frontend/src/utils/email.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function checkEmail(email: string) {
|
||||
return /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email);
|
||||
}
|
@ -15,7 +15,9 @@ import Backdrop from '@mui/material/Backdrop';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { token, username } from '@/store/store'
|
||||
|
||||
import { login } from '@/apis/apis'
|
||||
import { checkEmail } from '@/utils/email'
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
|
||||
function Loading() {
|
||||
return (
|
||||
@ -30,27 +32,6 @@ function Loading() {
|
||||
)
|
||||
}
|
||||
|
||||
interface tokenData {
|
||||
accessToken: string
|
||||
selectedProfile: {
|
||||
name: string
|
||||
}
|
||||
}
|
||||
|
||||
async function tologin(username: string, password: string) {
|
||||
const v = await fetch(import.meta.env.VITE_APIADDR + "/api/yggdrasil/authserver/authenticate", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
"username": username,
|
||||
"password": password,
|
||||
})
|
||||
})
|
||||
const data = await v.json()
|
||||
if (!v.ok) {
|
||||
throw data?.errorMessage
|
||||
}
|
||||
return data as tokenData
|
||||
}
|
||||
|
||||
|
||||
export default function SignIn() {
|
||||
@ -67,13 +48,13 @@ export default function SignIn() {
|
||||
email: data.get('email')?.toString(),
|
||||
password: data.get('password')?.toString(),
|
||||
}
|
||||
if (!postData.email?.includes("@")) {
|
||||
if (!checkEmail(postData.email ?? "")) {
|
||||
setEmailErr("需要为邮箱")
|
||||
return
|
||||
}
|
||||
if (loading) return
|
||||
setLoading(true)
|
||||
tologin(postData.email, postData.password ?? "").
|
||||
login(postData.email!, postData.password ?? "").
|
||||
then(v => {
|
||||
if (!v) return
|
||||
setToken(v.accessToken)
|
||||
@ -137,7 +118,7 @@ export default function SignIn() {
|
||||
</Link>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Link href="#" variant="body2">
|
||||
<Link component={RouterLink} to="/register" variant="body2">
|
||||
{"注册"}
|
||||
</Link>
|
||||
</Grid>
|
||||
|
130
frontend/src/views/Register.tsx
Normal file
130
frontend/src/views/Register.tsx
Normal file
@ -0,0 +1,130 @@
|
||||
import * as React from 'react';
|
||||
import Avatar from '@mui/material/Avatar';
|
||||
import Button from '@mui/material/Button';
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import Link from '@mui/material/Link';
|
||||
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 { useState } from 'react';
|
||||
import { checkEmail } from '@/utils/email';
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
|
||||
|
||||
|
||||
export default function SignUp() {
|
||||
const [emailErr, setEmailErr] = useState("");
|
||||
const [passErr, setpassErr] = useState("");
|
||||
|
||||
|
||||
|
||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
setEmailErr("")
|
||||
setpassErr("")
|
||||
event.preventDefault();
|
||||
const data = new FormData(event.currentTarget);
|
||||
const d = {
|
||||
email: data.get('email')?.toString(),
|
||||
password: data.get('password')?.toString(),
|
||||
password1: data.get('password1')?.toString(),
|
||||
username: data.get("username")?.toString()
|
||||
}
|
||||
if (!checkEmail(d.email ?? "")) {
|
||||
setEmailErr("需要为邮箱")
|
||||
return
|
||||
}
|
||||
if (d.password != d.password1) {
|
||||
setpassErr("密码不一致")
|
||||
return
|
||||
}
|
||||
if (d.password == "") {
|
||||
setpassErr("密码为空")
|
||||
return
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Container component="main" maxWidth="xs">
|
||||
<CssBaseline />
|
||||
<Box
|
||||
sx={{
|
||||
marginTop: 8,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Avatar sx={{ m: 1, bgcolor: 'secondary.main' }}>
|
||||
<LockOutlinedIcon />
|
||||
</Avatar>
|
||||
<Typography component="h1" variant="h5">
|
||||
注册
|
||||
</Typography>
|
||||
<Box component="form" noValidate onSubmit={handleSubmit} sx={{ mt: 3 }}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
error={emailErr != ""}
|
||||
helperText={emailErr}
|
||||
required
|
||||
fullWidth
|
||||
name="email"
|
||||
label="邮箱"
|
||||
autoComplete="email"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
required
|
||||
fullWidth
|
||||
name="username"
|
||||
label="角色名"
|
||||
autoComplete="email"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
error={passErr != ""}
|
||||
helperText={passErr}
|
||||
required
|
||||
fullWidth
|
||||
label="密码"
|
||||
type="password"
|
||||
name="password"
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
required
|
||||
fullWidth
|
||||
label="确认密码"
|
||||
type="password"
|
||||
name="password1"
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Button
|
||||
type="submit"
|
||||
fullWidth
|
||||
variant="contained"
|
||||
sx={{ mt: 3, mb: 2 }}
|
||||
>
|
||||
注册
|
||||
</Button>
|
||||
<Grid container justifyContent="flex-end">
|
||||
<Grid item>
|
||||
<Link component={RouterLink} to={"/login"} variant="body2">
|
||||
登录
|
||||
</Link>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</Box>
|
||||
</Container>
|
||||
);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user