Spring Boot + Vue 图书增删改简单案例

目录

  • 修改图书

  • 删除图书

  • 添加图书

  1. 前端:
    - Vue.js
    - Element
    - Axios
  2. 后端:
    - Spring Boot
    - Mysql
    - JPA
    - Lombok
  3. 其他功能:
    - 模态框
    - Pageable分页

  • 后端Spring Boot

  • 数据库表


  • 前端Vue

  • Index.vue(菜单路由导航页)
<template>
  <el-container style="height: 100%; border: 1px solid #eee">
    <!-- 左边部分 菜单 -->
    <el-aside width="200px" style="background-color: rgb(238, 241, 246)">
      <el-menu router :default-openeds="['0','1']">
        <!-- 通过for循环遍历路由设置菜单 -->
        <el-submenu
          v-for="(item,index) in $router.options.routes"
          :key="item.name"
          :index="index+''"
        >
        <!-- 菜单标题 -->
          <template slot="title">
            <i class="el-icon-setting"></i>
            {{item.name}}
          </template>
        <!-- 子菜单 :class 如果当前路由路径与子菜单路由路径相等,则激活该样式-->
          <el-menu-item
            v-for="(item1) in item.children"
            :key="item1.name"
            :index="item1.path"
            :class="$route.path==item1.path?'is-active':''"
          >{{item1.name}}</el-menu-item>
        </el-submenu>
      </el-menu>
    </el-aside>

    <!-- main部分 路由 -->
    <el-container>
      <el-main>
        <router-view />
      </el-main>
    </el-container>
  </el-container>
</template>

<script>
export default {
  name: "Index",
  data() {
    return {
      msg: "Welcome to Your Vue.js App",
    };
  },
};
</script>
<style scoped>
</style>
  • PageOne.vue(图书表格页)
<template>
  <div>
    <!-- table表格 绑定数据tableData-->
    <el-table :data="tableData" border style="width: 100%">
      <el-table-column fixed prop="id" label="id"></el-table-column>
      <el-table-column prop="name" label="书名"></el-table-column>
      <el-table-column prop="author" label="作者"></el-table-column>
      <el-table-column fixed="right" label="操作">
        <template slot-scope="scope">
          <el-button @click="OpenModifyBookDialog(scope.row)" type="text" size="small">修改</el-button>
          <!-- 删除气泡确认框 -->
          <el-popconfirm title="确定删除吗?" style="margin-left:10px;" @onConfirm="delBook(scope.row)">
            <el-button type="text" slot="reference">删除</el-button>
          </el-popconfirm>
        </template>
      </el-table-column>
    </el-table>
    <!-- 
      分页导航 tatal是从axios请求中获取出来的resp.data.totalElements
      @current-change="page",绑定跳页按键事件,当我们点了其他页码之后,就会触发这个事件,去请求数据-->
    <el-pagination
      background
      layout="prev, pager, next"
      :page-size="pageSize"
      :total="total"
      @current-change="page"></el-pagination>
    <!-- 修改图书资料的模态框 -->
    <el-dialog
      :title="ModifyBookDialogTitle"
      :visible.sync="dialogModifyBookVisible"
      @close="closeDialogModifyBookVisible">
      <el-form :model="modifyForm" :rules="rules" ref="modifyForm">
        <el-form-item label="图书编号">
          <el-input v-model="modifyForm.id" readonly></el-input>
        </el-form-item>
        <el-form-item label="图书名称" prop="name">
          <el-input v-model="modifyForm.name" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="作者" prop="author">
          <el-input v-model="modifyForm.author"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogModifyBookVisible = false">取 消</el-button>
        <el-button type="primary" @click="saveModifyForm('modifyForm')">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
export default {
  inject: ["reload"],
  data() {
    return {
      total: null,    //图书条目的总数,由请求返回的resp.data.totalElements获得
      pageSize: null, //一页的大小,由分页请求的链接返回的resp.data.size获得
      tableData: null,//图书表格数据
      ModifyBookDialogTitle: null,  //修改图书信息模态框标题
      modifyForm: {    //修改图书信息模态框数据
        name: "",
        author: "",
      },
      dialogModifyBookVisible: null,  //模态框视图是否打开
      //修改图书模态框信息表单验证规则
      rules: {
        name: [
          { required: true, message: "图书名称不能为空!", trigger: "blur" },
          {
            min: 2,
            max: 10,
            message: "长度在 2 到 10 个字符",
            trigger: "blur",
          },
        ],
        author: [
          { required: true, message: "作者名称不能为空!", trigger: "blur" },
          { min: 2, max: 5, message: "长度在 2 到 5 个字符", trigger: "blur" },
        ],
      },
    };
  },
  created() {
    const _this = this; //这里要定义一个_this指向该组件对象
    //生命周期开始时,请求分页数据,参数/1/7的意思是查找第一页 7条数据
    this.$axios
      .get("http://localhost:8082/book/findAll/1/7")
      .then(function (resp) {
        _this.tableData = resp.data.content;  //图书表数据
        _this.total = resp.data.totalElements; //总数
        _this.pageSize = resp.data.size; //页面大小
      });
  },
  methods: {
    // 页码点击事件,获取要去的页码currentPage,重新请求数据,
    page(currentPage) {
      const _this = this; //这里要定义一个_this指向该组件对象
      this.$axios
        .get("http://localhost:8082/book/findAll/" + currentPage + "/7")
        .then(function (resp) {
          _this.tableData = resp.data.content;  
          _this.total = resp.data.totalElements; //总数
          _this.pageSize = resp.data.size; //一页的数据大小
        });
    },
    //打开修改图书信息模态框
    OpenModifyBookDialog(row) {
      const _this = this;
      this.dialogModifyBookVisible = true;
      this.ModifyBookDialogTitle = "修改图书信息";
      this.modifyForm.id = row.id;
      this.$axios
        .get("http://localhost:8082/book/findById/" + row.id)
        .then(function (resp) {
          _this.modifyForm.name = resp.data.name;
          _this.modifyForm.author = resp.data.author;
        });
    },
    //提交模态框的表单数据
    saveModifyForm(formName) {
      const _this = this;
      this.$refs[formName].validate((valid) => {
        //如果表单验证通过,则提交请求
        if (valid) {
          this.$axios
            .put("http://localhost:8082/book/updateBook/", this.modifyForm)
            .then(function (resp) {
              if (resp.data == "success update") {
                _this.$message({
                  message: "修改成功!",
                  type: "success",
                });
                _this.reload(); //数据修改成功后,自动刷新页面
              } else if (resp.data == "false update") {
                alert("添加失败!");
              }
            });
          this.dialogModifyBookVisible = false;
        } else {
          console.log("error submit!!");
          return false;
        }
      });
    },
    //关闭或取消模态框,清空值
    closeDialogModifyBookVisible() {
      this.$refs.modifyForm.resetFields(); //element封装的方法,清空模态框的值
      this.ModifyBookDialogTitle = ""; //初始化title的值
      this.dialogModifyBookVisible = false;
      this.modifyForm = {
        //初始化modifyForm中的值
        name: "",
        author: "",
      };
    },
    //确定删除图书
    delBook(row) {
      const _this = this;
      console.log(row.id);
      this.$axios
        .delete("http://localhost:8082/book/deleteById/" + row.id)
        .then(function (resp) {
          if (resp.status == "200") {
            _this.$message({
              message: "删除成功!",
              type: "success",
            });
          }
          _this.reload(); //数据修改成功后,自动刷新页面
        });
    },
  },
};
</script>
  • PageTwo.vue(添加图书页)
<template>
  <div>
    <el-form
      :model="ruleForm"
      :rules="rules"
      ref="ruleForm"
      label-width="100px"
      class="demo-ruleForm"
    >
      <el-form-item label="图书名称" prop="name">
        <el-input style="width:20%" v-model="ruleForm.name"></el-input>
      </el-form-item>
      <el-form-item label="作者" prop="author">
        <el-input style="width:20%" v-model="ruleForm.author"></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>
  </div>
</template>

<script>
export default {
  data() {
    return {
      ruleForm: {
        name: "",
        author: "",
      },
      //表单验证规则
      rules: {
        name: [
          { required: true, message: "图书名称不能为空!", trigger: "blur" },
          {
            min: 2,
            max: 10,
            message: "长度在 2 到 10 个字符",
            trigger: "blur",
          },
        ],
        author: [
          { required: true, message: "作者名称不能为空!", trigger: "blur" },
          { min: 2, max: 5, message: "长度在 2 到 5 个字符", trigger: "blur" },
        ],
      },
    };
  },
  methods: {
    //提交表单
    submitForm(formName) {
      const _this = this;
      this.$refs[formName].validate((valid) => {
        //根据rules规则进行表单验证,如果验证成功,则提交请求
        if (valid) {
          this.$axios
            .post("http://localhost:8082/book/saveBook/", this.ruleForm) //提交表单数据时只需要在post后面参数添加上就可以了
            .then(function (resp) {
              if (resp.data == "success save") {
                _this.$message({
                  message: "添加成功!",
                  type: "success",
                });
                _this.$router.push("/PageOne"); //路由跳转
              } else if (resp.data == "false save") {
                alert("添加失败!");
              }
            });
        } else {
          console.log("error submit!!");
          return false;
        }
      });
    },
    //重置表单
    resetForm(formName) {
      this.$refs[formName].resetFields();
    },
  },
};
</script>
  • index.js
import Vue from 'vue'
import Router from 'vue-router'
import PageOne from '@/components/PageOne'
import PageTwo from '@/components/PageTwo'
import Index from '@/components/Index'
Vue.use(Router)
export default new Router({
  routes: [
    {
      path: '/',
      name: '图书管理',
      component: Index,
      redirect:"/PageOne",
      show: true,
      children:[
        {
          path: '/PageOne',
          name: '查询图书',
          component: PageOne
        },
        {
          path: '/PageTwo',
          name: '添加图书',
          component: PageTwo
        },
      ]
    },
  ]
})
  • App.vue(通过provide和inject实现了页面刷新的功能)
<template>
  <div id="app">
    <router-view v-if="isRouterAlive" />
  </div>
</template>
<script>
export default {
  name: "App",
  provide() {
    //父组件中通过provide来提供变量,在子组件中通过inject来注入变量。
    return {
      reload: this.reload,
    };
  },
  data() {
    return {
      isRouterAlive: true, //控制视图是否显示的变量
    };
  },
  methods: {
    reload() {
      this.isRouterAlive = false; //先关闭,
      this.$nextTick(function () {
        this.isRouterAlive = true; //再打开
      });
    },
  },
};
</script>
<style>
</style>
  • 后端SpringBoot

  • pom.xml依赖
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>
  • application.yml配置文件
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mytest?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT
    username: root
    password: loveu0222
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    show-sql: true
    properties:
      hibernate:
        format_sql: true
server:
  port: 8082
  • CrosConfig.java(Vue与SpirngBoot跨域请求配置)
package com.example.demo.config;
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 implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        //解决Vue与SpringBoot通信跨域问题
        registry.addMapping("/**")  //设置允许跨域的路径
                .allowedOrigins("*")          //设置允许跨域请求的域名
                .allowedMethods("GET","HEAD","POST","PUT","DELETE","OPTIONS")   //设置允许的方法
                .allowCredentials(true)       //这里:是否允许证书 不再默认开启
                .maxAge(3600)                 //跨域允许时间
                .allowedHeaders("*");
    }
}
  • Book.java
package com.example.demo.entity;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
@Data
public class Book {
    /*
        @Data注解是lombok的注解,它会帮我们生成各种set、get方法
        @id是主键注解
        lombok插件要在idea里自己添加下载
    */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    private String author;
}

  • BookRepository.java (JPA)
package com.example.demo.repository;
import com.example.demo.entity.Book;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BookRepository extends JpaRepository<Book,Integer> {}
  • BookHander.java(Controller请求处理)
package com.example.demo.controller;
import com.example.demo.entity.Book;
import com.example.demo.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/book")
public class BookHander {
    //自动注入BookRepository,该接口继承了JpaRepository<Book,Integer>
    @Autowired
    private BookRepository bookRepository;

    //查询所有
    @GetMapping("/findAll")
    public List<Book> findAll(){
    //参数添加了排序规则,按照id排序
        return bookRepository.findAll(Sort.by(Sort.Direction.DESC, "id"));
     //return bookRepository.findAll();

    }
    //分页查询
    @GetMapping("/findAll/{page}/{size}")
    public Page<Book> findAll(@PathVariable("page") Integer page, @PathVariable("size") Integer size){
    //第三个参数为排序规则,按照id排序
        Pageable pageable = PageRequest.of(page-1,size,Sort.by(Sort.Direction.ASC, "id"));
        return bookRepository.findAll(pageable);
    }
    //保存图书信息
    @PostMapping("/saveBook")
    public String save(@RequestBody Book book){
        Book result =  bookRepository.save(book);
        if(result != null){
            return "success save";
        }else {
            return "false save";
        }
    }
    //通过id查找,请求时带id参数
    @GetMapping("/findById/{id}")
    public Book findById(@PathVariable("id") Integer id){
        return bookRepository.findById(id).get();
    }
    //更新图书信息,调用save方法保存即可
    @PutMapping("/updateBook")
    public String updateBook(@RequestBody Book book){
        Book result =  bookRepository.save(book);
        if(result != null){
            return "success update";
        }else {
            return "false update";
        }
    }
    //通过id删除,请求时带id参数,没有返回值,前端通过判断http状态码200判断删除是成功
    @DeleteMapping("/deleteById/{id}")
    public void DeleteById(@PathVariable("id") Integer id){
         bookRepository.deleteById(id);
    }
}

posted @ 2020-09-14 09:22  lodalo  阅读(935)  评论(0编辑  收藏  举报