springboot+vue前后端分离图书管理项目整合

本教程详细介绍了如何使用SpringBoot和Vue搭建一个前后端分离的项目,包括创建新项目、构建Book表格的前端和后端接口、使用ElementUI、实现图书的增删改查功能以及处理跨域问题。此外,还涉及了数据校验和分页功能的实现。
摘要由CSDN通过智能技术生成

1 创建新项目

打开视图操作

vue ui

一直点击往下,直到创建成功项目为止

启动项目:

npm run serve

2 构建Book表格Demo

2.1 创建vue文件

首先在views下创建vue文件,并创建表格,填写好假数据
注意:template下只能有一个div包裹,否则元素会溢出,显示不了且报错

views/Book.vue

<template>
    <div>
        <tr>
            <td>编号</td>
            <td>图书名称</td>
            <td>作者</td>
            <td>出版社</td>
            <td>页数</td>
            <td>价格</td>
        </tr>
        <tr v-for="book in books">
            <td>{{book.id}}</td>
            <td>{{book.name}}</td>
            <td>{{book.author}}</td>
            <td>{{book.publish}}</td>
            <td>{{book.pages}}</td>
            <td>{{book.price}}</td>
        </tr>
    </div>
</template>

<script>
    export default {
        name: "Book",
        data(){
            return{
                msg:'Hello Vue',
                books:[
                    {
                        id:1,
                        name:"追风筝的人",
                        author:'战三',
                        publish:'问心出版社',
                        pages:123,
                        price:44.3,
                    },
                    {
                        id:2,
                        name:"解忧杂货铺",
                        author:'李四',
                        publish:'问心出版社',
                        pages:113,
                        price:33.3,
                    },
                    {
                        id:3,
                        name:"暖暖心绘本",
                        author:'王五',
                        publish:'问心出版社',
                        pages:133,
                        price:23.1,
                    },
                ]
            }
        }
    }
</script>

2.2 注册router

在router下引入Book,并注册
routes下的数组的意思是当走/book的请求的时候,会返回Book组件

import Book from "../views/Book";
const routes = [
  {
    path: '/book',
    component: Book
  }]

2.3 前端测试

在这里插入图片描述
成功显示假数据,此时可将前端搁置一遍不管,等后端传输数据!

2.4 后端查找数据

2.4.1 配置yaml

application.yaml

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/mybatis?&useSSL=false&serverTimezone=UTC&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
# 整合mybatis
mybatis:
  type-aliases-package: com.cycyong.pojo
  mapper-locations: classpath:mybatis/mapper/*.xml
server:
  port: 8181

2.4.2 pojo

Book.java

package com.cycyong.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Book {
    private Integer id;// id
    private String name; // 书名
    private String author; //作者
    private String publish; // 出版社
    private int pages; // 页数
    private float price; // 价格
}

2.4.3 mapper

@Mapper表示这是一个Mybatis的Mapper类

BookMapper.java

package com.cycyong.Mapper;
import com.cycyong.pojo.Book;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;
// 表示这是一个Mybatis的Mapper类
@Mapper
@Repository
public interface BookMapper {
    public List<Book> getBookList();
}

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.cycyong.Mapper.BookMapper">
    <select id="getBookList" resultType="Book">
        select * from library.book
    </select>
</mapper>

2.4.4 service

BookService.java

package com.cycyong.service;
import com.cycyong.pojo.Book;
import org.springframework.stereotype.Service;
import java.util.List;
public interface BookService {
    public List<Book> getBookList();
}

BookServiceImpl.java

package com.cycyong.service;
import com.cycyong.Mapper.BookMapper;
import com.cycyong.pojo.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BookServiceImpl implements BookService {
    @Autowired
    BookMapper bookMapper;
    
    @Override
    public List<Book> getBookList() {
        return bookMapper.getBookList();
    }
}

2.4.5 controller

BookController.java

package com.cycyong.controller;
import com.cycyong.pojo.Book;
import com.cycyong.service.BookServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/book")
public class BookController {
    @Autowired
    BookServiceImpl bookService;

    @GetMapping("/getBookList")
    public List<Book>getBookList(){
        return bookService.getBookList();
    }
}

2.5 跨域传输数据

由于vue前端的端口是8080,而springBoot后端的端口为8181,两者传输数据会报传输数据问题,因此得配置参数文件CrosConfig

下面这段代码复制即可用!
config/CrosConfig.java

package com.cycyong.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CrosConfig {
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**")
                        .allowCredentials(false)
                        .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
                        .allowedOrigins("*");
            }
        };
    }
}

2.6 anxios前后端交互

2.6.1 安装axios

cnpm install axios --save

2.6.2 引入axios

在需要用到的界面引入axios

const axios = require('axios');

2.6.3 编写请求

created()是页面刚刚初始化的时候的函数,注意不要放错在methods方法中
即要做的就是在刚刚初始化时就把books数据加到table表格中!
注意:

  • 设置一个_this,把vue的this拿过来即可
  • 这里使用的this是指回调的this,不是上面的vue对象的
// 页面加载就初始化
created() {
    //这里设置一个_this,把vue的this拿过来即可
    const _this = this
    axios.get('http://localhost:8181/book/getBookList').then(function (response) {
        // 这里使用的this是指回调的this,不是上面的vue对象
        _this.books = response.data
    })
}

在这里插入图片描述
成功实现跨域请求数据!

3 简单使用Element UI

3.1 认识el-table

  • el-container:构建整个页面框架
  • el-aside:构建左侧菜单
  • el-menu:左侧菜单内容
    • default-openeds:默认展开菜单,通过菜单index值关联
    • default-active:默认选中的菜单,通过菜单的index值关联
    • el-submenu:可展开的菜单
      • index:菜单下标,文本类型,不能是数值类型
    • template:el-submenu的带单名,可设置图标i
      • i:用class属性设置,例如el-icon-message
    • el-menu-item:菜单的子节点,不可再展开
      • index:菜单下标,文本类型,不能是数值类型

3.2 构建左侧菜单

构建目标:

  • 导航1
    • 页面1
    • 页面2
  • 导航2
    • 页面3
    • 页面4
<template>
    <div>
        <el-container style="height: 500px; border: 1px solid #eee">
            <el-aside width="200px" style="background-color: rgb(238, 241, 246)">
                <el-menu router :default-openeds="['0','1']">
                    <el-submenu v-for="(item,index) in this.$router.options.routes" :index="index+''">
                        <template slot="title"><i class="el-icon-setting"></i>{{item.name}}</template>
                        <el-menu-item v-for="(item2,index2) in item.children" :index="item2.path" :class="$route.path===item2.path?'is-active':''">{{item2.name}}</el-menu-item>
                    </el-submenu>
                </el-menu>
            </el-aside>
                <el-main>
                    <router-view></router-view>
                </el-main>
        </el-container>
    </div>
</template>

代码讲解:

  1. default-openeds="['0','1']":默认展开1,2两个菜单
  2. router:menu和菜单绑定的条件
  3. 在el-submenu配置index是为了展开,否则点击两个菜单项同时展示
  4. 在el-menu-item总配置:index就是它的跳转路径
  5. 在el-menu-item总配置:class是为了选中时发光是否正确,与地址栏的route.path比较即可

3.3 menu和router绑定

  1. <el-menu>标签添加router属性
  2. 在页面中添加<router-view>标签,它是一个容器,动态渲染你选择的router
  3. <el-menu-item>标签的index值就是要跳转的router

3.4 表单数据校验

定义rules对象,在rules对象中设置各个选项的校验规则

rules: {
    name: [
        { required: true, message: '请输入活动名称', trigger: 'blur' },
        { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
    ]
}
  • require:是否为必填项
  • message:提示信息
  • trigger:blur 失去焦点触发事件

4 完善Book的CRUD

4.1 增加图书

4.1.1 前端逻辑

template

<template>
    <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
        <el-form-item label="书名" prop="name">
            <el-input v-model="ruleForm.name"></el-input>
        </el-form-item>
        <el-form-item label="作者" prop="author">
            <el-input v-model="ruleForm.author"></el-input>
        </el-form-item>
        <el-form-item label="出版社" prop="publish">
            <el-input v-model="ruleForm.publish"></el-input>
        </el-form-item>
        <el-form-item label="页数" prop="pages">
            <el-input v-model.number="ruleForm.pages" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="价格" prop="price">
            <el-input v-model.number="ruleForm.price" type="number"></el-input>
        </el-form-item>
        <el-form-item>
            <el-button type="primary" @click="submitForm('ruleForm')">立即添加</el-button>
            <el-button @click="resetForm('ruleForm')">重置</el-button>
        </el-form-item>
    </el-form>
</template>

代码解析:

  1. :model="ruleForm"是vue的双向绑定,即与data中return数据绑定
  2. rules="rules"绑定检验规则
  3. prop绑定具体的检验规则

script

<script>
    const axios = require('axios')
    export default {
        data() {
            return {
                // 绑定数据
                ruleForm: {
                    name: '',
                    author: '',
                    publish: '',
                    pages: null,
                    price: null,
                },
                // 绑定校验规则
                rules: {
                    name: [
                        { required: true, message: '图书名不能为空', trigger: 'blur' },
                    ],
                    author: [
                        { required: true, message: '作者不能为空', trigger: 'blur' },
                    ],
                    publish: [
                        { required: true, message: '出版社不能为空', trigger: 'blur' },
                    ],
                    pages: [
                        { required: true, message: '页数不能为空', trigger: 'blur' },
                        { type:'number',message: '请输入数字'}
                    ],
                    price: [
                        { required: true, message: '价格不能为空', trigger: 'blur' },
                        { type:'float',message: '请输入浮点数'},
                    ],
                },
            };
        },
        methods: {
            submitForm(formName) {
                const _this = this
                this.$refs[formName].validate((valid) => {
                    if (valid) {
                        axios.post("http://localhost:8181/book/addBook",this.ruleForm).then(function (response) {
                            if (response.data==='success'){
                                _this.$alert('添加成功',{
                                    confirmButtonText:'确定',
                                    callback: action => {
                                        _this.$router.push('/BookManager')
                                    }
                                })
                            }
                        })
                    } else {
                        return false;
                    }
                });
            },
            resetForm(formName) {
                this.$refs[formName].resetFields();
            }
        }
    }
</script>

代码讲解:

  1. ruleForm代表与上方进行数据绑定
  2. rules检验规则
  3. methods一些函数

axios提交请求

submitForm(formName) {
    const _this = this
    this.$refs[formName].validate((valid) => {
        if (valid) {
            axios.post("http://localhost:8181/book/addBook",this.ruleForm).then(function (response) {
                if (response.data==='success'){
                    _this.$alert('添加成功',{
                        confirmButtonText:'确定',
                        callback: action => {
                            _this.$router.push('/BookManager')
                        }
                    })
                }
            })
        } else {
            return false;
        }
    });
},

代码讲解:

  1. 这是一个提交事件
  2. 注意添加成功回调跳转到BookManager页面,否则不刷新
  3. _this.$router.push('/BookManager')这个是跳转

4.1.2 后端逻辑

就讲一点就是将前端数据给到后端需要添加@RequesstBody
@RequestBody:把JSON对象转换为Java对象

    @PostMapping("/addBook")
    // 通过@RequestBody 把JSON对象转换为Java对象
    public String addBook(@RequestBody Book book){
        System.out.println(book);
        int flag = bookService.addBook(book);
        if(flag>0){
            return "success";
        }
        else{
            return "error";
        }
    }

4.2 分页

4.2.1 前端模块

template

<template>
    <div>
        <el-table
                :data="tableData"
                border
                style="width: 60%">
            <el-table-column
                    fixed
                    prop="id"
                    label="编号"
                    width="100">
            </el-table-column>
            <el-table-column
                    prop="name"
                    label="书名"
                    width="120">
            </el-table-column>
            <el-table-column
                    prop="author"
                    label="作者"
                    width="120">
            </el-table-column>
            <el-table-column
                    prop="publish"
                    label="出版社"
                    width="120">
            </el-table-column>
            <el-table-column
                    prop="pages"
                    label="页数"
                    width="50">
            </el-table-column>
            <el-table-column
                    prop="price"
                    label="价格"
                    width="100">
            </el-table-column>
            <el-table-column
                    fixed="right"
                    label="操作"
                    width="100">
                <template slot-scope="scope">
                    <el-button @click="edit(scope.row)" type="text" size="small">修改</el-button>
                    <el-button @click="dele(scope.row)" type="text" size="small">删除</el-button>
                </template>
            </el-table-column>
        </el-table>
        <el-pagination
                background
                layout="prev, pager, next"
                :page-size=pageSize
                :total=total
                @current-change="page"
                >
        </el-pagination>
    </div>
</template>

代码讲解:

  1. prop属性可以作为传参数的桥梁(双向绑定)
  2. @click="edit(scope.row)点击事件,并将当行数据作为参数传递
  3. el-pagination为分页控件,需要传递两个参数:pageSize和totalSize
  4. @current-change="page"为点击页数时的点击切换页码函数

script

 data() {
     return {
         total:null,
         pageSize:null,
         tableData: null,
     }
 }
 page(currentPage) {
        const _this = this
        axios.get("http://localhost:8181/book/getBookList/"+currentPage+"/5").then(function (response) {
            _this.tableData = response.data.books
            _this.total = response.data.count
            _this.pageSize = response.data.pageSize
        })
    }
},
 created() {
     const _this = this
     axios.get("http://localhost:8181/book/getBookList/1/5").then(function (response) {
         _this.tableData = response.data.books
         _this.total = response.data.count
         _this.pageSize = response.data.pageSize
     })
 },
  1. currentPage获得当前选的的页码,5是pageSize
  2. 后端需给出三个参数:tableDatacountpageSize,接着赋给分页控件即可

4.2.2 后端模块

4.2.2.1 mapper
    public List<Book> getBookList(Map<String,Object> data);
    <!--结果集映射-->
    <resultMap id="BookMap" type="com.cycyong.pojo.Book">
        <!--column为数据库字段,property为实体类的属性-->
        <result column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="author" property="author"/>
        <result column="publish" property="publish"/>
        <result column="pages" property="pages"/>
        <result column="price" property="price"/>
    </resultMap>
    <select id="getBookList" parameterType="map" resultMap="BookMap">
        select * from library.book limit #{currPage},#{pageSize}
    </select>
4.2.2.2 service
    public List<Book> getBookList(int currPage, int pageSize);
    @Override
    public List<Book> getBookList(int currPage, int pageSize) {
        Map <String,Object> data = new HashMap<String,Object>();
        /*
            1 3 | 0 1 2
            2 3 | 3 4 5
            3 3 | 6 7 8
         */
        data.put("currPage", (currPage - 1) * pageSize);
        data.put("pageSize", pageSize);
        return bookMapper.getBookList(data);
    }
4.2.2.3 controller
    @GetMapping("/getBookList/{page}/{size}")
    public Map<String, Object>getBookList(@PathVariable("page") int page,@PathVariable("size") int size){
        Map<String, Object> map = new HashMap<>();
        map.put("books",bookService.getBookList(page,size));
        map.put("count",bookService.getBookCount());
        map.put("pageSize",size);
        return map;
    }

4.3 删除图书

4.3.1 前端模块

<template slot-scope="scope">
    <el-button @click="edit(scope.row)" type="text" size="small">修改</el-button>
    <el-button @click="dele(scope.row)" type="text" size="small">删除</el-button>
</template>
dele(row){
    const _this = this
    this.$confirm('此操作将永久删除该条记录, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
    }).then(() => {
        axios.delete("http://localhost:8181/book/deleteBook/"+row.id).then(function (response) {
            if(response.data==='success'){
                _this.$message({
                    type: 'success',
                    message: '删除成功!',
                });
                window.location.reload()
            }
        })
    }).catch(() => {
        _this.$message({
            type: 'info',
            message: '已取消删除'
        });
    });
},

代码讲解:

  1. window.location.reload()刷新界面

4.3.2 后端模块

@DeleteMapping:删除用DeleteMapping

    @DeleteMapping("/deleteBook/{id}")
    public String deleteBook(@PathVariable ("id") int id){
        int flag = bookService.deleteBook(id);
        if(flag>0){
            return "success";
        }else{
            return "error";
        }
    }

4.4 修改图书

4.4.1前端模块

<el-table-column
        fixed="right"
        label="操作"
        width="100">
    <template slot-scope="scope">
        <el-button @click="edit(scope.row)" type="text" size="small">修改</el-button>
        <el-button @click="dele(scope.row)" type="text" size="small">删除</el-button>
    </template>
</el-table-column>
edit(row) {
    //row得到当前行数据
    // console.log(row);
    this.$router.push({
        path:"/updateBook",
        query:{
            id:row.id
        }
    })
},

4.4.2后端模块

@PutMapping:修改用putMapping

    //更新信息用PutMapping
    @PutMapping("/updateBook")
    public String updateBook(@RequestBody Book book){
        int flag = bookService.updateBook(book);
        if(flag>0){
            return "success";
        }else{
            return "error";
        }
    }
一、项目简介 本项目是一套基于SpringBoot图书管理系统,主要针对计算机相关专业的正在做毕设的学生和需要项目实战练习的Java学习者。 包含:项目源码、数据库脚本、软件工具、项目说明等,该项目可以直接作为毕设使用。 项目都经过严格调试,确保可以运行! 二、技术实现 ​后台框架:SpringBoot ​数据库:MySQL 开发环境:JDK、IDEA、Tomcat 三、系统功能 本图书管理系统主要包含两种角色:读者和管理员。读者只能对个人信息的查阅、修改,图书的查询,而管理员则可以进行图书信息及借阅信息的管理。 具体实现功能如下: (1)系统登录。分为普通读者登录和管理员登录。 (2)系统管理。系统管理包括管理员设置,以及图书类别设置。管理员设置包括管理员信息的设置以及密码的设置。图书类别的设置只有管理员才可以对他进行新增,修改和删除。 (3)图书管理。包括图书信息管理图书信息查询。只有管理员才可以对图书进行管理,图书查询是帮助读者方便查找图书信息。 (4)读者管理。读者管理包括读者信息管理,以及读者信息的查询。读者信息查询可以根据读者的姓名和编号进行查询。读者信息管理只对管理员有用,只有管理员可以添加读者,修改和删除读者的信息。 (5)图书借阅管理。图书借阅包括图书的借阅,归还以及续借。图书的借阅以及归还只对管理员起作用,只有通过管理员才可以进行图书的借阅以及归还。读者只能对图书进行续借的操作。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cycyong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值