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";
        }
    }
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

cycyong

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

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

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

打赏作者

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

抵扣说明:

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

余额充值