Skip to content

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

先写登录、注册页面:

用 create-vite 新建个 react 项目:

npx create-vite book-management-system-frontend

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

npm install
npm run dev

浏览器访问下:

然后我们添加 router:

npm install --save react-router-dom

在 main.tsx 加上路由的配置:

javascript
import ReactDOM from 'react-dom/client';
import { RouterProvider, createBrowserRouter} from 'react-router-dom';

function BookManage() {
  return <div>book</div>;
}

function Login(){
  return <div>login</div>;
}

function Register(){
  return <div>register</div>;
}

const routes = [
  {
    path: "/login",
    element: <Login/>,
  },
  {
    path: "/register",
    element: <Register/>,
  },
  {
    path: "/",
    element: <BookManage/>,
  },
];

const router = createBrowserRouter(routes);

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);

root.render(<RouterProvider router={router}/>);

配置了 3 个路由:

访问 / 的时候,渲染 BookManage 组件。

访问 /login 的时候,渲染 Login 组件。

访问 /register 的时候,渲染 Register 组件。

测试下:

都没问题。

然后在 src 下创建 3 个组件:Login、Register、BookManage,把其余无用文件去掉:

然后来写 Register 页面:

引入 Ant Design 组件库:

npm install antd --save

在 Login 组件引入 Button 组件:

javascript
import { Button } from "antd";

export function Login(){
    return <div>
        login
        <Button type="primary">按钮</Button>
    </div>
}

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

然后我们把注册页面写一下:

javascript
import { Button, Form, Input } from 'antd';
import './index.css';

interface RegisterUser {
    username: string;
    password: string;
    password2: string;
}

const onFinish = (values: RegisterUser) => {
    console.log(values);
};

const layout1 = {
    labelCol: { span: 4 },
    wrapperCol: { span: 20 }
}

const layout2 = {
    labelCol: { span: 0 },
    wrapperCol: { span: 24 }
}

export function Register() {
    return <div id="register-container">
        <h1>图书管理系统</h1>
        <Form
            {...layout1}
            onFinish={onFinish}
            colon={false}
            autoComplete="off"
        >
            <Form.Item
                label="用户名"
                name="username"
                rules={[{ required: true, message: '请输入用户名!' }]}
            >
                <Input />
            </Form.Item>

            <Form.Item
                label="密码"
                name="password"
                rules={[{ required: true, message: '请输入密码!' }]}
            >
                <Input.Password />
            </Form.Item>

            <Form.Item
                label="确认密码"
                name="password2"
                rules={[{ required: true, message: '请输入确认密码!' }]}
            >
                <Input.Password />
            </Form.Item>

            <Form.Item
                {...layout2}
            >
                <div className='links'>
                    <a href='/login'>已有账号?去登录</a>
                </div>
            </Form.Item>

            <Form.Item
                {...layout2}
            >
                <Button className='btn' type="primary" htmlType="submit">
                    注册
                </Button>
            </Form.Item>
        </Form>
    </div>   
}

layout 是指定 label 和 input 部分的比例分配的,总共是 24。

写下 index.css 的样式:

css
#register-container {
    width: 400px;
    margin: 100px auto 0 auto;
    text-align: center;
}

#register-container .links {
    display: flex;
    justify-content: center;
}

#register-container .btn {
    width: 100%;
}

看下现在的注册页面:

输入用户名、密码、确认密码,点击注册:

控制台打印了拿到的表单值。

然后我们调用下后端接口,安装下 axios:

npm install --save axios

创建 interfaces/index.ts

javascript
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
    });
}

在这里集中管理接口。

暴露 register 方法,里面调用 /user/register 接口。

然后在 Register 组件的 onFinish 里调用:

javascript
const onFinish = async (values: RegisterUser) => {

    if(values.password !== values.password2) {
        message.error('两次密码不一致');
        return;
    }

    try {
        const res = await register(values.username, values.password);

        if(res.status === 201 || res.status === 200) {
            message.success('注册成功');

            setTimeout(() => {
                window.location.href = '/login';
            }, 1000);
        }
    } catch(e: any) {
        message.error(e.response.data.message);
    }
}

两次密码不一致提示错误。

然后请求注册接口,如果有错误就提示错误,注册成功跳转登录页。

注册下:

提示跨域。

在后端项目支持下跨域访问:

再试下:

没啥问题。

这样,注册就完成了。

我们再来写下登录页面:

修改下 Login/index.tsx

javascript
import { Button, Form, Input, message } from 'antd';
import './index.css';

interface LoginUser {
    username: string;
    password: string;
}

const onFinish = async (values: LoginUser) => {
    console.log(values);
}

const layout1 = {
    labelCol: { span: 4 },
    wrapperCol: { span: 20 }
}

const layout2 = {
    labelCol: { span: 0 },
    wrapperCol: { span: 24 }
}

export function Login() {
    return <div id="login-container">
        <h1>图书管理系统</h1>
        <Form
            {...layout1}
            onFinish={onFinish}
            colon={false}
            autoComplete="off"
        >
            <Form.Item
                label="用户名"
                name="username"
                rules={[{ required: true, message: '请输入用户名!' }]}
            >
                <Input />
            </Form.Item>

            <Form.Item
                label="密码"
                name="password"
                rules={[{ required: true, message: '请输入密码!' }]}
            >
                <Input.Password />
            </Form.Item>

            <Form.Item
                {...layout2}
            >
                <div className='links'>
                    <a href='/register'>没有账号?去注册</a>
                </div>
            </Form.Item>

            <Form.Item
                {...layout2}
            >
                <Button className='btn' type="primary" htmlType="submit">
                    登录
                </Button>
            </Form.Item>
        </Form>
    </div>
}

还有样式 index.css

css
#login-container {
    width: 400px;
    margin: 100px auto 0 auto;
    text-align: center;
}

#login-container .links {
    display: flex;
    justify-content: center;
}

#login-container .btn {
    width: 100%;
}

试一下:

没啥问题。

然后在 interfaces/index.ts 里添加 login 接口:

javascript
export async function login(username: string, password: string) {
    return await axiosInstance.post('/user/login', {
        username, password
    });
}

在页面调用下:

javascript
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);
    }
}

试下效果:

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

案例代码上传了小册仓库

总结

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

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

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

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