注册
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 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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Layout />}></Route>
|
<Route path="/" element={<Layout />}></Route>
|
||||||
<Route path="/login" element={<Login />} />
|
<Route path="/login" element={<Login />} />
|
||||||
|
<Route path="/register" element={<Register />} />
|
||||||
</Routes>
|
</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 React from 'react'
|
||||||
import ReactDOM from 'react-dom/client'
|
import ReactDOM from 'react-dom/client'
|
||||||
import App from './App.tsx'
|
import App from './App.tsx'
|
||||||
import { BrowserRouter } from "react-router-dom";
|
|
||||||
import CssBaseline from '@mui/material/CssBaseline';
|
import CssBaseline from '@mui/material/CssBaseline';
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<BrowserRouter>
|
|
||||||
<CssBaseline>
|
<CssBaseline>
|
||||||
<App />
|
<App />
|
||||||
</CssBaseline>
|
</CssBaseline>
|
||||||
</BrowserRouter>
|
|
||||||
</React.StrictMode>
|
</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 CircularProgress from '@mui/material/CircularProgress';
|
||||||
import { useSetAtom } from 'jotai';
|
import { useSetAtom } from 'jotai';
|
||||||
import { token, username } from '@/store/store'
|
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() {
|
function Loading() {
|
||||||
return (
|
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() {
|
export default function SignIn() {
|
||||||
@ -67,13 +48,13 @@ export default function SignIn() {
|
|||||||
email: data.get('email')?.toString(),
|
email: data.get('email')?.toString(),
|
||||||
password: data.get('password')?.toString(),
|
password: data.get('password')?.toString(),
|
||||||
}
|
}
|
||||||
if (!postData.email?.includes("@")) {
|
if (!checkEmail(postData.email ?? "")) {
|
||||||
setEmailErr("需要为邮箱")
|
setEmailErr("需要为邮箱")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (loading) return
|
if (loading) return
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
tologin(postData.email, postData.password ?? "").
|
login(postData.email!, postData.password ?? "").
|
||||||
then(v => {
|
then(v => {
|
||||||
if (!v) return
|
if (!v) return
|
||||||
setToken(v.accessToken)
|
setToken(v.accessToken)
|
||||||
@ -137,7 +118,7 @@ export default function SignIn() {
|
|||||||
</Link>
|
</Link>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<Link href="#" variant="body2">
|
<Link component={RouterLink} to="/register" variant="body2">
|
||||||
{"注册"}
|
{"注册"}
|
||||||
</Link>
|
</Link>
|
||||||
</Grid>
|
</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