第六章 MyBatis-Plus+Vue+Element前后端分离实战

飞一样的编程
飞一样的编程
擅长邻域:Java,MySQL,Linux,nginx,springboot,mongodb,微信小程序,vue

分类: springboot 专栏: springboot3.0新教材 标签: mybatis-plus vue3

2024-01-03 22:58:21 1333浏览

MyBatis-Plus+Vue+Element前后端分离实战

前期回顾

  • mybatisplus带条件查询 :

QueryWrapper 和LambdaQueryWrapper

  • mybatisplus分页查询

实战项目展示

后端开发

依赖

 <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.4</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.16</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </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>
        </dependency>
    </dependencies>
spring.datasource.url=jdbc:mysql:///bookstore?serverTimezone=Asia/Shanghai&characterEncoding=utf8
spring.datasource.password=123456
spring.datasource.username=root
@Data
@TableName(value = "student")
public class Student {

    @TableId(value = "id",type = IdType.AUTO)
    private int id;
    private String studentname;
    private String gender;
    private int age;
    private String address;
}
@Data
@AllArgsConstructor
public class ResultVo<T> {
    private Integer code;
    private String mess;
    private T data;

    //success
    public static<T> ResultVo success(String mess,T data){
        return new ResultVo(200,mess,data);
    }
    //error
    public static<T> ResultVo error(String mess){
        return new ResultVo(500,mess,null);
    }
}

测试新增修改json

{
    "id":20,
    "studentname":"李四",
    "gender":"女",
    "age":30,
    "address":"山西太原"
}

控制层

/**
 * @author:xiaojie
 * @create: 2024-01-04 10:33
 * @Description:
 */

@RestController
@RequestMapping("/student")
public class StudentController {
    @Autowired
    StudentService studentService;

    //分页带条件查询
    @GetMapping
    public ResultVo page(String studentname, @RequestParam(defaultValue = "1") Integer pageNum ){

       Page<Student> studentPage= studentService.getPage(studentname,pageNum);
       return ResultVo.success("",studentPage);

    }

    //增 或者修改

    @PostMapping
    public ResultVo addOrUpdate(@RequestBody Student student){
        studentService.saveOrUpdate(student);
        return ResultVo.success("操作成功",null);
    }
    //删除
    @DeleteMapping("/{id}")
    public  ResultVo del(@PathVariable Integer id){
        studentService.removeById(id);
        return ResultVo.success("操作成功",null);
    }


}

前端开发

安装vue

先安装nvm ,然后通过nvm安装node和npm ,进而通过npm安装vue脚手架

这些之前都讲过了就不赘述了。

创建vue项目

前提:我用的node版本是14.17.5

  • 1.vue create 项目名称

选择手动选功能

然后选择如下选项

注意:空格是选中

然后回车

是否保存为将来创建项目的预设,建议第一次创建的时候保存下

  • 2.安装路由-这个前面创建项目的时候已经安装过了
npm install vue-router@next
  • 3.安装axios
npm install axios -save
  • 4.安装Element Plus
npm install element-plus --save
  • 5.安装Element Plus中要用到的icon图标
npm install @element-plus/icons
  • 6.运行项目
npm run serve

项目结构

  • (1) node_models目录:

项目的依赖包,包含了很多基础依赖,如本项目依赖了vue-router、vuex都可以在该目录下面找得到,也可以根据开发的需要安装其他依赖,方法是在Visual Studio Code中点击菜单Terminal New Terminal打开终端(相当于 window系统的命令提示符),默认路径就是当前项目路径下,安装命令:npm install [依赖包的名称]

  • (2) public目录:

公共资源目录,用于放置HTML文件和所需的图片(图标)。这里的index.html是程序的主页文件,一般只定义一个空的根节点<div id=“app”></div>用于挂载Vue实例。

  • (3) src目录:

是项目的核心文件目录,其下包括以下子目录或文件

    • assets:静态资源目录,存放css样式、图片、脚本和字体等等。
    • components:组件目录,存放项目的各种组件。
    • router:路由目录,在该目录下的index.js文件中配置路由。
    • store:容器目录,用于状态管理。
    • views:视图目录,用于存放路由配置中所用到的各种视图组件。
    • App.vue:项目的主组件,是页面入口文件。
    • main.js:入口javascript文件,由此文件创建Vue实例,串联起项目的各个部分。
  • package.json文件:项目的描述及依赖
  • package-lock.json:版本管理。

跨域问题解决方案

只需要在项目的vue.config.js文件中添加代码如下:

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave:false
})

//以上是自动生成的,下面是为解决跨域加的
module.exports = {
  publicPath:'/',
  devServer: {
    host:'localhost',
    port:8088, //前端项目端口号 选填
    proxy: {                 //设置代理,必须填
      '/api': {
          target: 'http://localhost:8080', // 配置访问的服务器地址
          pathRewrite: { '^/api': '' },  // 用于将请求中的/api字符串替换为空, 然后访问地址就能正确访问,若不添加此行配置,那么访问地址就变成了: http://localhost:8080/api/request_url,这样的请求就会出现 404 操作
          ws: true, // 是否支持 websocket, 默认是 true
          changeOrigin: true // 用于控制请求头中的 host 值, 默认是 true
      }
    }
  }
}

这里的意思是使用/api请求来代替真实的请求,浏览器代理会将/api请求重新替换成真实请求去访问,所以前端的所有请求前面要加上/api前缀(但前面不能有真实后台地址)。

前端核心代码

<template>
  <div>
    <el-form :inline="true" :model="formInline" class="demo-form-inline">
      <el-form-item label="学生姓名">
        <el-input v-model="formInline.studentname" placeholder="请输入学生姓名" clearable />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="onSearch">搜索</el-button>
        <el-button type="warning" @click="onAdd">新增</el-button>
      </el-form-item>
    </el-form>


    <el-table :data="tableData" style="width: 100%">
      <el-table-column prop="id" label="编号"   />
      <el-table-column prop="studentname" label="学生姓名"  />
      <el-table-column prop="gender" label="性别" />
      <el-table-column prop="age" label="年龄" />
      <el-table-column prop="address" label="家庭住址" />
      <el-table-column label="操作" >
        <template #default="scope">
          <el-button type="primary" size="small" @click="edit(scope.row)">编辑</el-button>
          <el-button type="danger" size="small" @click="del(scope.row.id)">删除</el-button>
        </template>
      </el-table-column >
    </el-table>

    <!--分页-->
    <el-pagination background
                   @current-change="changePage"
                   layout="total, prev, pager, next, jumper"
                   :total="total"
                   :page-count="pages" />

    <!--弹框-->
    <el-dialog
        v-model="dialogVisible"
        :destroy-on-close="true"
        title="学生信息"
        width="30%"
    >
      <el-form
          ref="ruleFormRef"
          :model="ruleForm"
          :rules="rules"
          label-width="120px"
          class="demo-ruleForm"
          status-icon
      >
        <el-form-item label="学生姓名" prop="studentname">
          <el-input v-model="ruleForm.studentname" />
        </el-form-item>
        <el-form-item label="性别" prop="gender">
          <el-radio-group v-model="ruleForm.gender">
            <el-radio label="男" />
            <el-radio label="女" />
          </el-radio-group>
        </el-form-item>
        <el-form-item label="年龄" prop="age">
          <el-input v-model="ruleForm.age" />
        </el-form-item>
        <el-form-item label="家庭住址" prop="address">
          <el-input v-model="ruleForm.address" type="textarea" />
        </el-form-item>

        <el-form-item>
          <el-button @click="resetForm('ruleFormRef')">重置</el-button>
          <el-button type="primary" @click="submitForm">
            提交
          </el-button>

        </el-form-item>
      </el-form>

    </el-dialog>


  </div>



</template>

<script >
import {onMounted,toRefs,reactive,ref} from 'vue'
import axios from 'axios'
import { ElMessage,ElMessageBox } from "element-plus"
export default {
  //setup () 是vue3新增加的组件。vue3采用了组合式 API ,
 // 为了使用组合式API,我们需要一个入口,在vue3组件中,称之为setup。
 // (简单点来说,就是vue2里面的data,method,computed,
 // 所有数据方法全写在setup里)
  setup(){
    //reactive函数可以把复合类型的变量变为响应式的引用 
    const data = reactive({
      formInline: {
        studentname:''
      },
      ruleForm:{
        studentname:'',
        gender:'',
        age:'',
        address:''
      },
      rules:{
        studentname: [
          { required: true, message: '请输入学生姓名', trigger: 'blur' },
        ],
        gender: [
          { required: true, message: '请选择性别', trigger: 'blur' },
        ],
        age: [
          { required: true, message: '请输入年龄', trigger: 'blur' },
          { pattern :/^(1[8-9]|[2-9]\d)$/,message: '必须是18-99之间的数字', trigger: 'blur' }
        ],
        address: [
          { required: true, message: '请输入家庭地址', trigger: 'blur' },
        ],
      },
      dialogVisible:false,
      tableData:[],
      pageNum:1,
      total:0,
      pages:1,
    })

    const edit = (row) => {
      data.ruleForm=row
      data.dialogVisible=true
    }

    const onAdd = () => {
      data.ruleForm={}
      data.dialogVisible=true
    }

    const del = (id) => {

      ElMessageBox.confirm(
          '你确定要删除该记录吗?',
          '警告',
          {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning',
          }
      )
          .then(() => {

            axios({
              url:'/api/student/'+id,
              method:'delete',
            }).then(function (res){
              if(res.data.code==200){
                ElMessage({
                  type: 'success',
                  message: '已删除',
                })
                getTableData()
              }else{
                ElMessage({
                  type: 'error',
                  message: '删除失败',
                })
              }
            })

          })
          .catch(() => {
            ElMessage({
              type: 'info',
              message: '已取消',
            })
          })

    }

    const ruleFormRef = ref();//把基本类型变量变为响应式引用 获取数据值需要加.value
    const submitForm = () => {
      console.log(ruleFormRef.value)
      ruleFormRef.value.validate(vaild => {
        if (vaild) {
          axios({
            url: '/api/student',
            method:"POST",
            data:data.ruleForm
          }).then(function (res) {
            if(res.data.code ==200){
              ElMessage.success("操作成功")
              data.dialogVisible=false
              getTableData()
            }else{
              ElMessage.error("系统异常")
            }
          })

        } else {
          console.log("数据校验失败")
          ElMessage.error("请按提示填写表单")
        }
      })
    }

    const resetForm = () => {

      ruleFormRef.value.resetFields();

    }

    const changePage = (pageNum) => {
      data.pageNum=pageNum
      getTableData()
    }
    const onSearch = () => {
      data.pageNum=1
      getTableData()
    }
    const getTableData=function () {


      axios({
        url:'/api/student?pageNum='+data.pageNum+'&stuName='+data.formInline.studentname,
        method:'get',

      }).then(function (res) {
        console.log(res)
        data.tableData=res.data.data.records
        data.pages=res.data.data.pages
        data.total=res.data.data.total
      })
    }
    onMounted(() =>{
      getTableData()
    })
    return{
      ...toRefs(data),//可以将reactive返回的对象中的属性都转成ref;
      edit,
      getTableData,
      changePage,
      onAdd,
      del,
      onSearch,
      submitForm,
      ruleFormRef,
      resetForm
    }
  }
}


</script>

<style scoped>

</style>

还有另外一种写法,vue3不能直接使用this,可以换成下面这种写法

<template>

  <div>
    <el-form :inline="true" :model="formInline" class="demo-form-inline">
      <el-form-item label="学生姓名">
        <el-input v-model="formInline.studentname" placeholder="请输入学生姓名" clearable />
      </el-form-item>

      <el-form-item>
        <el-button type="primary" @click="onSubmit">查询</el-button>
        <el-button type="danger" @click="onAdd">新增</el-button>
      </el-form-item>
    </el-form>

    <el-table :data="tableData" style="width: 100%">
      <el-table-column prop="id" label="编号"  />
      <el-table-column prop="studentname" label="姓名"  />
      <el-table-column prop="gender" label="性别" />
      <el-table-column prop="age" label="年龄" />
      <el-table-column prop="address" label="家庭住址" />
      <el-table-column  label="操作" >
        <template #default="scope">
          <el-button type="primary" size="small" @click="onEdit(scope.row)">编辑</el-button>
          <el-button type="danger" size="small" @click="onDel(scope.row.id)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>

    <el-pagination background
                   layout="total, prev, pager, next,jumper"
                   :total="total"
                   :page-count="pages"
                   :current-page="pageNum"
                   @current-change="changePageNum"
    />

    <el-dialog
        v-model="dialogVisible"
        title="学生信息"
        width="30%"
        :destroy-on-close="true"
    >

      <el-form
          ref="ruleFormRef"
          :model="ruleForm"
          :rules="rules"
          label-width="120px"
          class="demo-ruleForm"
          status-icon
      >
        <el-form-item label="学生姓名" prop="studentname">
          <el-input v-model="ruleForm.studentname" />
        </el-form-item>
        <el-form-item label="性别" prop="gender">
          <el-radio-group v-model="ruleForm.gender">
            <el-radio label="男" />
            <el-radio label="女" />
          </el-radio-group>
        </el-form-item>
        <el-form-item label="年龄" prop="age">
          <el-input v-model="ruleForm.age" />
        </el-form-item>
        <el-form-item label="家庭住址" prop="address">
          <el-input v-model="ruleForm.address" type="textarea" />
        </el-form-item>

        <el-form-item>
          <el-button type="primary" @click="submitForm">
            提交
          </el-button>
          <el-button @click="resetForm">重置</el-button>
        </el-form-item>

      </el-form>
    </el-dialog>

  </div>
</template>

<script>
import {reactive,toRefs,onMounted,getCurrentInstance} from 'vue'
import axios from 'axios'

import { ElMessage,ElMessageBox } from 'element-plus'

export default {
  name: "StuView",
  setup(){
    const {proxy}=getCurrentInstance()


    const data = reactive({
      dialogVisible:false,
      formInline:{
        studentname: ''
      },
      ruleForm:{
        studentname: '',
        gender:'',
        age:20,
        address: ''
      },
      rules:{
        studentname: [
          { required: true, message: '请输入学生名称', trigger: 'blur' },
        ],
        gender: [
          { required: true, message: '请选择性别', trigger: 'blur' },
        ],
        address: [
          { required: true, message: '请输入家庭地址', trigger: 'blur' },
        ],
        age: [
          { required: true, message: '请输入年龄', trigger: 'blur' },
          { pattern: /^(1[8-9]|[2-9]\d)$/,message: '请输入18-99的数字',trigger: 'blur' }
        ],
      },
      pageNum:1,
      tableData:[],
      total:0,
      pages:0,

    })

    const resetForm = () => {
      proxy.$refs['ruleFormRef'].resetFields();
    }
    const submitForm = () => {
      proxy.$refs['ruleFormRef'].validate((valid) => {
        if (valid) {
          axios({
            url:'/api/student',
            method: 'post',
            data:data.ruleForm
          }).then(function (res){
            if(res.data.code==200){
              ElMessage.success("操作成功")

              data.dialogVisible=false
              getBackData()
            }else{
              ElMessage.error("出现异常")
            }
          })
        } else {
          ElMessage.error("请按提示填写表单")
          return false;
        }
      });
    }
    const changePageNum = (pageNo) => {
      data.pageNum=pageNo
      getBackData()
    }
    const onDel = (id) => {
      ElMessageBox.confirm(
          '确定要删除该记录吗?',
          '警告',
          {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning',
          }
      )
          .then(() => {

            axios({
              url:'/api/student/'+id,
              method:'delete'
            }).then(function (res){
              if(res.data.code==200){
                ElMessage({
                  type: 'success',
                  message: '删除成功',
                })
                getBackData()
              }else{
                ElMessage({
                  type: 'error',
                  message: '删除失败',
                })
              }
            })

          })
          .catch(() => {
            ElMessage({
              type: 'info',
              message: '取消操作',
            })
          })
    }
    const onEdit = (row) => {
      data.dialogVisible=true
      data.ruleForm=row
    }
    const onAdd = () => {
      data.ruleForm={
        studentname: '',
        gender:'',
        age:20,
        address: ''
      }
      data.dialogVisible=true
    }
    const onSubmit = () => {
      console.log('submit!')
      data.pageNum=1
      getBackData()

    }
    onMounted(function () {
      getBackData()
    })

    const getBackData = () => {
      axios({
        url: '/api/student?stuName='+data.formInline.studentname+'&pageNum='+data.pageNum,
        method:'get'
      }).then(function (res){
        console.log(res)
        data.tableData=res.data.data.records
        data.pageNum=res.data.data.current
        data.total=res.data.data.total
        data.pages=res.data.data.pages
      })
    }
    return {
      ...toRefs(data),
      onDel,
      onEdit,
      resetForm,
      submitForm,
      getBackData,
      onSubmit,
      onAdd,
      changePageNum
    }
  }
}
</script>

<style scoped>

</style>

vue2和vue3的主要区别

Vue3组合式APl(Composition API)主要用于在大型组件中提高代码逻辑的可复用性。

传统的组件随着业务复杂度越来越高,代码量会不断的加大,整个代码逻辑都不易阅读和理解。Vue3使用组合式API的地方为setup 。

在setup中,我们可以按逻辑关注点对部分代码进行分组,然后提取逻辑片段并与其他组件共享代码。因此,组合式APl (Composition API)允许我们编写更有条理的代码。


好博客就要一起分享哦!分享海报

此处可发布评论

评论(0展开评论

暂无评论,快来写一下吧

展开评论

您可能感兴趣的博客

客服QQ 1913284695