springboot+react 前后端分离 图书管理项目总结

前端:React

1.新建项目

2.组件Book.js Home.js User.js

3.路由模块化 router.js

cnpm install react-router-dom --save (安装)

要在根组件里引入这个,才可以用路由。

import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";

router.js

import User from '../components/User';
import Home from '../components/Home';
import User from '../components/User';

let routers = [
    {
        path: "/",
        component: Home,
        exact:true,
    },
    {
        path: "/user",
        component: User,
    },
    {
        path: "/book",
        component: Book
    },
];

export default routers;

在根组件引入router.js,之后根组件里这样写。

<Router>
      <div>
        <header className='title'>
            <Link to='/'>首页组件</Link>
            <Link to='/user'>用户首页</Link>
            <Link to='/book'>书籍组件</Link>
        </header>

        {

          routers.map((route,key)=>{

                if(route.exact){
                    return <Route key={key} exact path={route.path} 
                    component={route.component} routes={route.routes}
                    />
                }else{
                    return <Route key={key}  path={route.path} 
                    component={route.component} routes={route.routes}
                 />
                }
          })
        }

    </div>
</Router>

写到这里,项目应该可以运行,点击根组件的几个链接可以实现跳转。

接下来是引入css以及在Book.js引入html的页面 让页面变得能看。

index.css

.title{
  height: 44px;
  line-height: 44px;
  background: #000;
}

.title a{
  color: white;
  padding: 6px;
  text-decoration: none;
}

/*左右分栏*/

.content{
  width: 100%;
  height:500px;
  display: flex;
}

.content .left{
  width: 200px;
  height: 500px;
  background: #eeee;
  border: 1px solid #eeee;
}

.content .right{
  flex:1;
  height: 500px;
  border: 1px solid #eeee;
}
<div className='book'>
  <div className='content'>
       <div className='left'>
           <Link to='/book/'>书籍列表</Link>
           <br/> <br/>
           <Link to='/book/addBook'> 添加书籍</Link> 
       </div>

       <div className='right'>
           {

          
           }
           {/* <Route  exact path='/user/' component={UserList}/>
           <Route  path='/user/UserAdd' component={UserAdd}/> */}
       </div>
  </div>
</div>

接下来就是路由嵌套,

首先新建两个组件 BookList.js 和 AddBook.js

然后就要在router.js 把这两个子路由写进去。记得放进去别放错了,要放在Book的下面。

routes:[   /*嵌套路由*/
      {
          path: "/book/",
          component: BookList
      },
      {
          path: "/book/AddBook",
          component: AddBook
      },
  ]

接下来在根组件里写法要注意。因为路由嵌套,要把嵌套的路由的值传给子组件,这样子在组件里点击链接才能实现跳转。

{
  // 前面的js跳转还挺简单的
  // 然后是路由嵌套,整了个简单的页面,还是不错的
  // 实现了路由的模块化,的确挺不错的,整的怪方便的..也挺麻烦的,真的怪麻烦的,父子组件传值也要在这里面吗,要弄清楚逻辑呀
  // 这一节比较难,尤其是到了父子组件传值的那一部分
  routers.map((route,key)=>{

        if(route.exact){
            return <Route key={key} exact path={route.path} 
            render={props=>(
              <route.component {...props} routes={route.routes}/>
            )}
            />
        }else{
          return <Route key={key}  path={route.path} 
          // component={route.component} routes={route.routes}
          render={props=>(
            <route.component {...props} routes={route.routes}/>
          )}
          />
        }

  })
}

什么?看不懂?看不懂就对了。

 render={props=>(
      <route.component {...props} routes={route.routes}/>
  )}

这一部分真的看不懂,反正就cv吧,

!!!!!最后一步了,在子组件里获取父组件的传的值,然后写个循环,把路由弄出来。

<div className='right'>
    {
  
        this.props.routes.map((route,key)=>{
                return <Route key={key} exact path={route.path} component={route.component}/>
        })
    }
    {/* <Route  exact path='/user/' component={UserList}/>
    <Route  path='/user/UserAdd' component={UserAdd}/> */}
</div>

几个页面都没有问题,拿这个做图书管理系统的demo。

数据库设计(book 表):
在这里插入图片描述

后端 :springboot

参考教程:

https://www.bilibili.com/video/BV1PE411i7CV?p=33

https://blog.csdn.net/wogieni/article/details/88778969

搭项目结构,实现查询书籍列表的功能。
在这里插入图片描述

注意pom.xml 需要引入整合的依赖

<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter:整合-->
<dependency>
   <groupId>org.mybatis.spring.boot</groupId>
   <artifactId>mybatis-spring-boot-starter</artifactId>
   <version>2.2.0</version>
</dependency>

这个是整合框架要用到的。

在 pom.xml 还发现一些依赖报红了,可以通过降低它们的版本来避免报红。

BookMapper.xml 中的格式有一定要求

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo05.mapper.BookMapper">

</mapper>

然后在 application.yaml 中也要配置数据库,整合等。

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/bookmanage?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver

# 整合mybatis
mybatis:
  type-aliases-package: com.example.demo05.pojo
  mapper-locations: classpath:mybatis/mapper/*.xml

配置基本完成了,现在来写实体类,Book.java

然后写controllr层,BookController

service层,Bookservice BookserviceImpl

mapper层,BookMapper BookMapper.xml

在运行的时候找不到BookMapper.xml文件,原来是目录的问题。

在这里插入图片描述

controller层注解要这样写 @RestController 返回的值才对。

要解决 跨域 在后端项目 BookController.java 文件中 @RestController 后加入

@CrossOrigin(origins = {"http://localhost:3000"},allowCredentials = "true",allowedHeaders = {"X-Custom-Header"},        maxAge = 3600L, methods={RequestMethod.GET,RequestMethod.POST,RequestMethod.HEAD})

接着还是前端:

接下来用前端获取后端的数据,然后渲染出来,

在前端获取数据要一个插件,

https://github.com/axios/axios

因为后台已经允许跨域请求,所以使用axios就可以实现请求。

大概就三步:1.安装 2.引入 3.看文档

cnpm install axios --save 
import axios from 'axios';
getData=()=>{
        //通过axios获取服务器数据
        // Make a request for a user with a given ID
        var api='http://localhost:8080/booklist';

        axios.get(api)
        .then( (response)=> {
        // handle success
        //用到this,要注意this指向
            console.log(response.data);
            this.setState({
                list:response.data,
            })
        })
        .catch(function (error) {
        // handle error
        console.log(error);
        })
        .then(function () {
        // always executed
        });
    }

    //组件的生命周期函数,在页面刷新的时候触发
    componentDidMount(){
        this.getData();
    }

组件加载完成后就去请求数据,在生命周期函数里调用请求数据的方法。

4.接收数据,把数据渲染出来

定义一个list,在请求数据的函数里获得数据,

this.setState({
      list:response.data,
})

把数据渲染出来。设计布局,然后遍历。

<div >
   <table className='table'>
      <thead>
      <tr>
          <th>书名</th>
          <th>作者</th>
          <th>ISBN</th>
          <th>价格(元)</th>
          <th>操作</th>
      </tr>
      </thead>
      <tbody>
      {
          this.state.list.map((v,k)=>{
                  return (
                  <tr key={k}>	    
                          <td>{v.name}</td>
                          <td>{v.author}</td>
                          <td>{v.isbn}</td>
                          <td>{v.price}</td>
                          <td>
                          {/* 修改的话需要再写一个页面,然后父子组件传值,传书的id过去,在另一个页面查询书的信息,然后渲染,然后修改。 */}
                          <Link to={`/book/modifyBook/${v.id}`}>修改</Link>
                          <button onClick={this.deleteBook.bind(this,v.id)}>删除</button>
                          </td>		          
                  </tr> 
                  )
              })
          }     
      </tbody>
  </table>
</div>

到这里,展示图书列表暂时没有问题了。

现在写下一个功能,添加图书,

首先点一个链接(路由)跳到添加图书组件。

<Link to='/book/addBook'> 添加书籍</Link>

在AddBook组件里,

import React from "react";
import {
    BrowserRouter as Router,
    Switch,
    Route,
    Link,
    Redirect,
    useHistory,
    useLocation
    } from "react-router-dom";
    import axios from 'axios';

class addBook extends React.Component {
    constructor(props) {
        super(props);
        this.state = { 
            name:'',
            author:'',
            isbn:'',
            price:'',
            AddBookFlag:false,
         }
    }

    doAddBook=(e)=>{
        // e.preventDefault();
        let name = this.state.name;
        let author = this.state.author;
        let isbn = this.state.isbn;
        let price = this.state.price;
        console.log(name,author,isbn,price);

        this.AddBook(name,author,isbn,price);
        //正常情况需要请求接口
        if(true)
        {
            this.setState({
                AddBookFlag:true,
            })
            //登录成功,跳转到首页
        }else{
            alert('登录失败');
        }
    }

    AddBook=(name,author,isbn,price)=>{
        //通过axios获取服务器数据
        // Make a request for a user with a given ID
        // var api='http://localhost:8080/booklist';
        var api='http://localhost:8080/addBook'+'?name='+name+'&author='+author+'&isbn='+isbn+'&price='+price;

        axios.get(api)
        .then( (response)=> {
        // handle success
        //用到this,要注意this指向
            console.log(response);
            this.setState({
                AddBookFlag:true,
            })
        })
        .catch(function (error) {
        // handle error
        console.log(error);
        })
        .then(function () {
        // always executed
        });
    }

    
    componentWillUnmount = () => {
        this.setState = (state,callback)=>{
        return;
        };
    }

   
    inputChange1=(event)=>{
        this.setState({
            name:event.target.value,
        })
    }

    inputChange2=(event)=>{
        this.setState({
            author:event.target.value,
        })
    }

    inputChange3=(event)=>{
        this.setState({
            isbn:event.target.value,
        })
    }

    inputChange4=(event)=>{
        this.setState({
            price:event.target.value,
        })
    }

    render() { 
        if(this.state.AddBookFlag){
            // return <Redirect to={{pathname:'/'}}/>
            return <Redirect to='/book'/>
        }
        return ( 
            <div>
               <form onSubmit={this.doAddBook}>
                   书名:<input type='text' name='name' value={this.state.name} onChange={this.inputChange1}/>
                        <br/><br/>
                   作者:<input type='text' name='author' value={this.state.author} onChange={this.inputChange2}/>
                        <br/><br/>
                   ISBN:<input type='text' name='isbn' value={this.state.isbn} onChange={this.inputChange3} />
                        <br/><br/>
                   价格:<input type='text' name='price' value={this.state.price} onChange={this.inputChange4} />
                        <br/><br/>
                         <input type='submit' value='添加' />
               </form>
            </div>
         );
    }
}
 
export default addBook;

大致的逻辑就是获取表单里所有的值,然后调一个接口,把参数全部传过去,最后跳转页面。

axios,前端向后端发送请求,并且带上参数,然后在后端里获取参数。差不多难点就是这些。

发送请求,带上参数。在后端获取。

这个我当初一点头绪都没有,不知道咋带参数,经过尝试后,这样简单的两行代码就可以传递并接收数据,不过我相信还有更好的方法求传参,例如封装等。

var api='http://localhost:8080/addBook'+'?name='+name+'&author='+author+'&isbn='+isbn+'&price='+price;
public int addBook(String name,String author,String isbn,double price)

像修改和删除就是用的不同的方法写的,思路是什么,

删除的话就是拿到书籍的id,然后调用一个方法,方法里调用一个接口,记得要传值(书籍的id)。

这个是写在BookList组件里面的。

<button onClick={this.deleteBook.bind(this,v.id)}>删除</button>
deleteBook(id){
	  console.log(id);
	  //通过axios获取服务器数据
	  // Make a request for a user with a given ID
	  var api='http://localhost:8080/deleteBook?id='+id;
	
	  axios.get(api)
	  .then( (response)=> {
	  // handle success
	  //用到this,要注意this指向
	      console.log(response);
	  })
	  .catch(function (error) {
	  // handle error
	  console.log(error);
	  })
	  .then(function () {
	  // always executed
	  });
	
	  this.getData();
}

修改的话有点像添加书籍,都是用的路由,不过他这个是动态路由,要传值(传递书籍的id)

{
    path: "/book/modifyBook/:id",
    component: ModifyBook
},
<Link to={`/book/modifyBook/${v.id}`}>修改</Link>

跳到ModifyBook组件,在这个组件里拿到书籍的id,再调一个接口,查询到这本书的信息,然后渲染到前端,前端得到数据,就可以在页面上修改,然后再调一个接口,记得传书籍的id,修改书的信息。

import React from "react";
import {
    BrowserRouter as Router,
    Switch,
    Route,
    Link,
    Redirect,
    useHistory,
    useLocation
    } from "react-router-dom";
import axios from 'axios';

class ModifyBook extends React.Component {
    constructor(props) {
        super(props);
        this.state = { 
            id:'',
            name:'',
            author:'',
            isbn:'',
            price:'',
            ModifyBookFlag:false,
         }
    }

    getData=(id)=>{
        //通过axios获取服务器数据
        // Make a request for a user with a given ID
        var api='http://localhost:8080/getBook?id='+id;

        axios.get(api)
        .then( (response)=> {
        // handle success
        //用到this,要注意this指向
            console.log('OKOK');
            console.log(response.data);
            this.setState({
                id:response.data.id,
                name:response.data.name,
                author:response.data.author,
                isbn:response.data.isbn,
                price:response.data.price,
            })
        })
        .catch(function (error) {
        // handle error
        console.log(error);
        })
        .then(function () {
        // always executed
        });
    }

    // componentWillUnmount = () => {
    //     this.setState = (state,callback)=>{
    //     return;
    //     };
    // }

     //组件的生命周期函数,在页面刷新的时候触发
    componentDidMount(){
        console.log('OK');
        console.log(this.props.match.params.id);
        let id=this.props.match.params.id;
        this.getData(id);
    }

    doModifyBook=()=>{
        console.log('doModifyBook方法');
        // 进这个方法,然后提交了表单,很快就被刷新了
        let id = this.state.id;
        let name = this.state.name;
        let author = this.state.author;
        let isbn = this.state.isbn;
        let price = this.state.price;
        console.log(id,name,author,isbn,price);
        this.ModifyBook(id,name,author,isbn,price);
        this.setState({
            ModifyBookFlag:true,
        })
    }

    
    ModifyBook=(id,name,author,isbn,price)=>{
        //通过axios获取服务器数据
        // Make a request for a user with a given ID
        // var api='http://localhost:8080/booklist';
        var api='http://localhost:8080/modifyBook'+'?id='+id+'&name='+name+'&author='+author+'&isbn='+isbn+'&price='+price;

        axios.get(api)
        .then( (response)=> {
        // handle success
        //用到this,要注意this指向
            console.log(response);    
        })
        .catch(function (error) {
        // handle error
        console.log(error);
        })
        .then(function () {
        // always executed
        });
    }

    

    inputChange1=(event)=>{
        this.setState({
            name:event.target.value,
        })
    }

    inputChange2=(event)=>{
        this.setState({
            author:event.target.value,
        })
    }

    inputChange3=(event)=>{
        this.setState({
            isbn:event.target.value,
        })
    }

    inputChange4=(event)=>{
        this.setState({
            price:event.target.value,
        })
    }

    render() { 
          if(this.state.ModifyBookFlag){
            // return <Redirect to={{pathname:'/'}}/>
            return <Redirect to='/book'/>
        }
        return ( 
            <div>
               <form onSubmit={this.doModifyBook}>
                   书名:<input type='text' name='name' value={this.state.name} onChange={this.inputChange1}/>
                        <br/><br/>
                   作者:<input type='text' name='author' value={this.state.author} onChange={this.inputChange2}/>
                        <br/><br/>
                   ISBN:<input type='text' name='isbn' value={this.state.isbn} onChange={this.inputChange3} />
                        <br/><br/>
                   价格:<input type='text' name='price' value={this.state.price} onChange={this.inputChange4} />
                        <br/><br/>
                        <input type='submit' value='修改' />
                </form>
            </div>
         );
    }
}
 
export default ModifyBook;

目前的主要问题是

前后端没有数据校验,后端没有返回具体的信息,只是调了一个接口,没有返回类似于成功,失败(失败的类型)的信息给前端,然后让前端显示给用户。

第二个问题就是刷新的问题,不管是添加,删除,还是修改,最后都不会刷新书籍列表的数据,这个暂时也不知道咋解决。

代码,在github上。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值