后端接口写完后,我们来写前端页面。
先写登录、注册页面:
# 创建项目
用 create-vite 新建个 react 项目:
npx create-vite book-management-system-frontend
进入项目目录,把开发服务跑起来:
npm install
npm run dev
2
浏览器访问下:
# 添加路由
然后我们添加 router:
npm install --save react-router-dom
在 main.tsx 加上路由的配置:
// src/router/index.tsx
import { createBrowserRouter } from "react-router-dom";
import Book from "../views/book";
import Login from "../views/login";
import Register from "../views/register";
const routes = [
{
path: "/",
element: <Book />,
},
{
path: "/login",
element: <Login />,
},
{
path: "/register",
element: <Register />,
},
];
const router = createBrowserRouter(routes);
export default router;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
配置了 3 个路由:
访问 / 的时候,渲染 BookManage 组件。
访问 /login 的时候,渲染 Login 组件。
访问 /register 的时候,渲染 Register 组件。
测试下:
都没问题。
然后在 src 下创建 3 个组件:Login、Register、BookManage,把其余无用文件去掉:
然后来写 Register 页面:
引入 Arco Design 组件库:
npm install @arco-plugins/vite-react --save
在 Login 组件引入 Button 组件:
import { Button } from "@arco-design/web-react";
export function Login(){
return <div>
login
<Button type="primary">按钮</Button>
</div>
}
2
3
4
5
6
7
8
没啥问题,说明 Arco 引入成功了。
# 添加 tailwindcss
npm install tailwindcss @tailwindcss/vite
引入 tailwindcss
配置
// main.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
import { fileURLToPath, URL } from "node:url";
import { vitePluginForArco } from "@arco-plugins/vite-react";
// https://vite.dev/config/
export default defineConfig({
plugins: [
react(),
tailwindcss(),
vitePluginForArco({
style: "css",
}),
],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
引入样式
不使用预检查样式是因为会和组件库样式冲突
@layer theme, base, components, utilities;
@import "tailwindcss/theme.css" layer(theme);
/* @import "tailwindcss/preflight.css" layer(base); */
@import "tailwindcss/utilities.css" layer(utilities);
2
3
4
# 注册页面
然后我们把注册页面写一下:
import { Button, Form, Input, RulesProps } from "@arco-design/web-react";
import { useNavigate } from "react-router-dom";
import { register } from "./api";
const formLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 18 },
};
function Register() {
const [form] = Form.useForm();
const navigate = useNavigate();
const onSubmit = (values: any) => {
register({
username: values.username,
password: values.password,
}).then(() => {
navigate("/login");
});
};
const password2validator: RulesProps["validator"] = async (
value: any,
callback
) => {
if (value !== form.getFieldValue("password")) {
callback("两次密码不一致!");
} else {
callback();
}
};
return (
<div className="w-[400px] my-50 text-center mx-auto">
<h1>图书管理系统</h1>
<Form
form={form}
autoComplete="off"
{...formLayout}
colon={false}
onSubmit={onSubmit}
>
<Form.Item
label="用户名"
field="username"
rules={[{ required: true, message: "请输入用户名!" }]}
>
<Input />
</Form.Item>
<Form.Item
label="密码"
field="password"
rules={[{ required: true, message: "请输入密码!" }]}
>
<Input.Password />
</Form.Item>
<Form.Item
label="确认密码"
field="password2"
rules={[
{ required: true, message: "请输入确认密码!" },
{
validator: password2validator,
},
]}
>
<Input.Password />
</Form.Item>
<Form.Item wrapperCol={{ offset: 6, span: 18 }}>
<a href="/login">已有账号?去登录</a>
</Form.Item>
<Form.Item wrapperCol={{ offset: 6, span: 18 }}>
<Button className="w-full" type="primary" htmlType="submit">
注册
</Button>
</Form.Item>
</Form>
</div>
);
}
export default Register;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
layout 是指定 label 和 input 部分的比例分配的,总共是 24。
看下现在的注册页面:
输入用户名、密码、确认密码,点击注册:
控制台打印了拿到的表单值。
然后我们调用下后端接口,安装下 axios:
npm install --save axios
创建 request/index.ts
import axios from "axios";
const axiosInstance = axios.create({
baseURL: 'http://localhost:3000/',
timeout: 3000
});
export async function register(username: string, password: string) {
return await axiosInstance.post('/user/register', {
username, password
});
}
2
3
4
5
6
7
8
9
10
11
12
在这里集中管理接口。
暴露 register 方法,里面调用 /user/register 接口。
然后在 Register 组件的 onFinish 里调用:
const onSubmit = (values: any) => {
console.log(values, "values");
register({
username: values.username,
password: values.password,
}).then((res) => {
console.log(res, "res");
});
};
2
3
4
5
6
7
8
9
10
两次密码不一致提示错误。
然后请求注册接口,如果有错误就提示错误,注册成功跳转登录页。
注册下:
提示跨域。
在后端项目支持下跨域访问:
再试下:
没啥问题。
这样,注册就完成了。
# 登录页面
我们再来写下登录页面:
修改下 Login/index.tsx
import { Button, Form, Input, RulesProps } from "@arco-design/web-react";
import { useNavigate } from "react-router-dom";
import { register } from "./api";
const formLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 18 },
};
function Register() {
const [form] = Form.useForm();
const navigate = useNavigate();
const onSubmit = (values: any) => {
register({
username: values.username,
password: values.password,
}).then(() => {
navigate("/");
});
};
const password2validator: RulesProps["validator"] = async (
value: any,
callback
) => {
if (value !== form.getFieldValue("password")) {
callback("两次密码不一致!");
} else {
callback();
}
};
return (
<div className="w-[400px] my-50 text-center mx-auto">
<h1>图书管理系统</h1>
<Form
form={form}
autoComplete="off"
{...formLayout}
colon={false}
onSubmit={onSubmit}
>
<Form.Item
label="用户名"
field="username"
rules={[{ required: true, message: "请输入用户名!" }]}
>
<Input />
</Form.Item>
<Form.Item
label="密码"
field="password"
rules={[{ required: true, message: "请输入密码!" }]}
>
<Input.Password />
</Form.Item>
<Form.Item
label="确认密码"
field="password2"
rules={[
{ required: true, message: "请输入确认密码!" },
{
validator: password2validator,
},
]}
>
<Input.Password />
</Form.Item>
<Form.Item wrapperCol={{ offset: 6, span: 18 }}>
<a href="/login">已有账号?去登录</a>
</Form.Item>
<Form.Item wrapperCol={{ offset: 6, span: 18 }}>
<Button className="w-full" type="primary" htmlType="submit">
注册
</Button>
</Form.Item>
</Form>
</div>
);
}
export default Register;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
试一下:
没啥问题。
然后在 interfaces/index.ts 里添加 login 接口:
export async function login(username: string, password: string) {
return await axiosInstance.post('/user/login', {
username, password
});
}
2
3
4
5
在页面调用下:
const onFinish = async (values: LoginUser) => {
try {
const res = await login(values.username, values.password);
if(res.status === 201 || res.status === 200) {
message.success('登录成功');
setTimeout(() => {
window.location.href = '/';
}, 1000);
}
} catch(e: any) {
message.error(e.response.data.message);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
试下效果:
至此,注册、登录的前后端都完成了。
案例代码上传了小册仓库 (opens new window)
# 总结
这节我们写了下注册、登录的前端页面。
通过 create-vite 创建项目,引入了 react-router-dom 实现了路由,然后使用 antd 作为组件库,引入了 axios 发请求。
在后端项目开启跨域之后,在前端项目里调用登录、注册接口来实现功能。
下节,我们继续写其他前端页面。