夜猫子的知识栈 夜猫子的知识栈
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

夜猫子

前端练习生
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 任务管理日历

  • 无代码平台

  • 图书管理系统

    • 图书管理系统:需求分析和原型图
    • 图书管理系统:用户模块后端开发
    • 图书管理系统:图书模块后端开发
    • 图书管理系统:用户模块前端开发
      • 创建项目
      • 添加路由
      • 添加 tailwindcss
      • 注册页面
      • 登录页面
      • 总结
    • 图书管理系统:图书搜索前端开发
    • 图书管理系统:图书增删改前端开发
    • 图书管理系统:项目总结
  • 《全栈项目》
  • 图书管理系统
神说要有光
2025-03-10
目录

图书管理系统:用户模块前端开发

后端接口写完后,我们来写前端页面。

先写登录、注册页面:

# 创建项目

用 create-vite 新建个 react 项目:

npx create-vite book-management-system-frontend
1

进入项目目录,把开发服务跑起来:

npm install
npm run dev
1
2

浏览器访问下:

# 添加路由

然后我们添加 router:

npm install --save react-router-dom
1

在 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;

1
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
1

在 Login 组件引入 Button 组件:

import { Button } from "@arco-design/web-react";

export function Login(){
    return <div>
        login
        <Button type="primary">按钮</Button>
    </div>
}
1
2
3
4
5
6
7
8

没啥问题,说明 Arco 引入成功了。

# 添加 tailwindcss

npm install tailwindcss @tailwindcss/vite
1

引入 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)),
    },
  },
});
1
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);
1
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;

1
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
1

创建 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
    });
}
1
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");
    });
  };

1
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;

1
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
    });
}
1
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);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

试下效果:

至此,注册、登录的前后端都完成了。

案例代码上传了小册仓库 (opens new window)

# 总结

这节我们写了下注册、登录的前端页面。

通过 create-vite 创建项目,引入了 react-router-dom 实现了路由,然后使用 antd 作为组件库,引入了 axios 发请求。

在后端项目开启跨域之后,在前端项目里调用登录、注册接口来实现功能。

下节,我们继续写其他前端页面。

编辑 (opens new window)
上次更新: 2025/5/7 18:16:53
图书管理系统:图书模块后端开发
图书管理系统:图书搜索前端开发

← 图书管理系统:图书模块后端开发 图书管理系统:图书搜索前端开发→

最近更新
01
IoC 解决了什么痛点问题?
03-10
02
如何调试 Nest 项目
03-10
03
Provider注入对象
03-10
更多文章>
Copyright © 2019-2025 Study | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式