目录
一、从0快速搭建SpringBoot3工程、SpringBoot3集成MyBatis、PageHelper分页查询的详细教程。(博客链接)
<2>request.js代码。(request拦截器、response拦截器)
<4>分页查询。("/employee/selectPage")
ElementUI的Pagination分页事件(@size-change、@current-change)。
<3>对话框新增点击保存,调用save方法。("/employee/add")
<1>替换操作栏“编辑”、“删除”样式。(链接文字按钮:link)
<2>“编辑”操作绑定事件handleUpdate(scope.row)函数。
行对象的深拷贝。(JSON.stringify()、JSON.parse())
<3>区分新增操作的"保存"与编辑操作的"保存"。(post与put请求)
“删除”的二次确认操作。(ElMessageBox.confirm())
使用@selection-change="xxx函数"。(获取所选行对象)
一、从0快速搭建SpringBoot3工程、SpringBoot3集成MyBatis、PageHelper分页查询的详细教程。(博客链接)
- SpringBoot3实战(从0快速搭建SpringBoot3工程、全局异常处理器、自定义封装结果类、自定义异常)(2025详细教程)(1)-CSDN博客
- SpringBoot3实战(SpringBoot3集成MyBatis。PageHelper分页查询。get(查)、post(增)、put(改)、delete(删)请求)(2)-CSDN博客
- 本篇博客的学习:实现SpringBoot3后端接口与Vue3前端页面的连接互通。
- 之前的《SpringBoot3集成MyBatis》学习是使用Postman工具完成接口的调用与测试。
- 所以需要实现前端页面数据的“分页查询”、“条件查询”、“模糊查询”、“新增”、“修改”、“删除”、“批量删除”等功能。
二、实现前端与后端通信对接数据。(axios工具)
(1)安装axios。(vue工程目录)
- 打开IDEA中的对应工程的终端。执行安装axios命令。
cd .\自己vue工程对应目录\ npm i axios -S
(2)封装请求工具类。(request.js)
- 实现前端向后端发起请求之前,需要使用一个封装好的工具类。通过request.js帮助发起请求。功能:添加统一的请求头、对后端服务器返回的数据进行统一处理等。
<1>src目录、utils目录下。
- 新建request.js文件。
<2>request.js代码。(request拦截器、response拦截器)
import { ElMessage } from 'element-plus' import axios from "axios"; const request = axios.create({ //设置后台请求地址 baseURL: 'http://localhost:9090', timeout: 30000 // 后台接口超时时间设置 }) // request 拦截器(数据请求) // 可以自请求发送前对请求做一些处理 request.interceptors.request.use(config => { //设置统一的数据传输格式json、数据传输编码utf-8 config.headers['Content-Type'] = 'application/json;charset=utf-8'; return config }, error => { return Promise.reject(error) }); // response 拦截器 // 可以在接口响应后统一处理结果 request.interceptors.response.use( response => { //响应对象response中提取实际数据部分,存储在变量res中 let res = response.data; // 兼容服务端返回的字符串数据 //如果res是字符串且不为空字符串,则使用JSON.parse方法将其解析为JavaScript对象; //如果 res 为空字符串,则保持原样。 if (typeof res === 'string') { res = res ? JSON.parse(res) : res } return res; }, error => { if(error.response.status === 404){ //404 状态码表示请求的资源未找到,通常意味着请求的接口不存在 ElMessage.error('未找到请求接口') }else if(error.response.status === 500){ //500:之前后端设置的全局系统异常处理捕获 //500 状态码表示服务器内部错误,通常是由于后端代码出现异常 ElMessage.error('系统异常,请查看后端控制台报错') }else{ //其它情况统一打印错误信息 console.error(error.message) } //将错误继续抛出,以便后续的代码可以继续处理该错误 return Promise.reject(error) } ) export default request
<3>简单请求示例。
- Manager.vue页面的嵌套子页面Home.vue进行简单请求演示操作。
- 在<script>标签下导入request.js中的request对象进行请求。
前端请求后端接口代码示例。
<script setup> import {reactive} from "vue"; import request from "@/utils/request.js"; //定义数据的常用方式 const data = reactive({ employeeList:[], }) request.get("employee/selectAll").then(res=>{ alert(res) data.employeeList = res.data }) </script>
浏览器拦截非同源请求。(跨域请求问题)
- 报错原因:由于CORS(跨域资源共享)策略。请求被阻止,被请求的资源上不存在xxx头 。
- 前端代码在http://localhost:5173,而请求后端接口地址http://localhost:9090/xxx/xxx。二者源不同,浏览器的同源策略限制了这种跨域请求。需要在后端配置允许跨域的规则。
<4>SpringBoot后端中配置统一跨域处理。
- 在后端中新建一个类。跨越处理代码示例如下。
- 配置完毕后,记得重启后端工程。
package com.hyl.common; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; /** * 跨域配置类。用于解决前端和后端由于不同源(协议、域名、端口不同)导致的跨域请求问题 * @Configuration 让该配置类注入到Spring容器中并能够扫描到其下类下注解 */ @Configuration public class CorsConfig { /** * 创建并注册CorsFilter bean,用于处理跨域请求 * @return CorsFilter实例,Spring会在请求处理过程中使用该过滤器处理跨域请求 * @Bean 注解会让该方法的返回值注入到Spring容器中 */ @Bean public CorsFilter corsFilter() { // 创建一个基于URL的跨域配置源对象,用于存储和管理不同URL路径的跨域配置 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); // 创建一个跨域配置对象,用于设置具体的跨域规则 CorsConfiguration corsConfiguration = new CorsConfiguration(); // 设置允许访问的源地址,这里使用通配符"*",表示允许所有源地址访问 corsConfiguration.addAllowedOrigin("*"); // 设置允许的请求头,"*"表示允许所有请求头,即前端可以在请求中携带任意请求头 corsConfiguration.addAllowedHeader("*"); // 设置允许的请求方法,"*"表示允许所有的HTTP请求方法,如GET、POST、PUT、DELETE等 corsConfiguration.addAllowedMethod("*"); // 将跨域配置应用到所有的接口路径上,"/**"表示匹配所有路径 source.registerCorsConfiguration("/**", corsConfiguration); // 创建并返回CorsFilter实例,传入配置源对象,Spring会使用该过滤器处理跨域请求 return new CorsFilter(source); } }
- 重新访问http://localhost:5173/manager/home。查看请求结果。
- 发现:从后端访问接口拿到数据库数据并转化成Java对象再以JSON格式返回给前端的数据无法通过弹窗在页面显示。
- 将alert()方法改为控制台打印console.log()方法。
request.get("employee/selectAll").then(res=>{ /*alert(res)*/ console.log(res) data.employeeList = res.data })
三、SpringBoot3+Vue3实现基本增删改查。
(1)查询。
<1>新建Employee.vue(员工信息页面)。
<template> <div> <div class="card" style="margin-bottom: 5px"> <el-input style="width: 240px" v-model="data.name" placeholder="请根据名称查询" :prefix-icon="Search"></el-input> <el-button type="primary" style="margin-left: 10px">查 询</el-button> <el-button type="warning" style="margin-left: 10px">重 置</el-button> </div> <div class="card" style="margin-bottom: 5px"> <el-button type="primary" style="margin-left: 10px">新 增</el-button> <el-button type="warning" style="margin-left: 10px">批量删除</el-button> <el-button type="info" style="margin-left: 10px">导 入</el-button> <el-button type="success" style="margin-left: 10px">导 出</el-button> </div> <div class="card" style="margin-bottom: 5px"> <div style="margin: 30px"> <el-table :data="data.EmployeeList" stripe style="width: 100%"> <el-table-column label="姓名" prop="name"/> <el-table-column label="性别" prop="sex"/> <el-table-column label="工号" prop="no"/> <el-table-column label="年龄" prop="age"/> <el-table-column label="个人简介" prop="description"/> <el-table-column label="部门" prop="departmentName"/> <el-table-column label="操作"> <template #default="scope"> <el-button type="primary" circle> <el-icon><Edit /></el-icon> </el-button> <el-button type="danger" circle> <el-icon><Delete /></el-icon> </el-button> </template> </el-table-column> </el-table> </div> <div style="margin-top: 10px"> <el-pagination v-model:current-page="data.pageNum" v-model:page-size="data.pageSize" :page-sizes="[5, 10, 15, 20]" layout="total, sizes, prev, pager, next, jumper" :total="data.total" /> </div> </div> </div> </template> <script setup> import {Delete, Edit, Search} from "@element-plus/icons-vue"; import {reactive} from "vue"; const data = reactive({ name:'', pageNum:1, pageSize:10, total:0, EmployeeList:[], }) </script>
<2>配置Employee.vue路由。
import { createRouter, createWebHistory } from 'vue-router' const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ {path:'/',redirect:'/manager/home'}, {path:'/manager',meta:{ title:'父级页面'},component: () => import('../views/Manager.vue'),children:[ {path: 'home', name: 'home', meta:{ title:'主页'}, component: () => import('../views/Home.vue')}, // url:/manager/home {path: 'test', name: 'test', meta:{ title:'测试数据页01'}, component: () => import('../views/Test.vue')}, // url:/manager/test {path: 'demo', name: 'demo', meta:{ title:'测试数据页02'}, component: () => import('../views/Demo.vue')}, // url:/manager/demo {path: 'data', name: 'data', meta:{ title:'数据展示页面'}, component: () => import('../views/Data.vue')}, // url:/manager/data {path: 'employee', name: 'employee', meta:{ title:'员工信息页面'}, component: () => import('../views/Employee.vue')}, // url:/manager/employee ]}, {path: '/404', name: 'NotFound', meta:{ title:'404找不到页面'}, component: () => import('../views/404.vue')}, {path:'/:pathMatch(.*)',redirect:'/404'} ], }) router.beforeEach((to,from,next)=>{ //设置即将跳转的路由页面的网页标题 document.title=to.meta.title next() //必须调用的方法 }) export default router
<3>页面初级渲染效果。
<4>分页查询。("/employee/selectPage")
- 自定义load()函数发起后端接口("/employee/selectPage")的分页请求。
- 后端代码。(selectPage、selectAll)
/** * 查询所有员工 * 查询使用get请求 */ @GetMapping("/selectAll") public Result selectAll(){ List<Employee> emList = employeeService.selectAll(); return Result.success(emList); } /** * 分页查询 * @param pageNum 当前页码。默认值1 * @param pageSize 每页数据个数。默认值10 * @return */ @GetMapping("/selectPage") public Result selectPage( @RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize) { PageInfo<Employee> employeePageInfo = employeeService.selectPage(pageNum, pageSize); return Result.success(employeePageInfo); }
public List<Employee> selectAll() { return employeeMapper.selectAll(); } /** *service层实现分页操作的代码。PageHelper分页插件使用。 */ public PageInfo<Employee> selectPage(Integer pageNum,Integer pageSize) { //开启分页查询(传参:当前页码、每页分页个数) PageHelper.startPage(pageNum,pageSize); //查询所有的数据 List<Employee> employeeList = employeeMapper.selectAll(); //PageInfo执行分页操作 return PageInfo.of(employeeList); }
- load()函数基础代码示例。
- 查看请求的控制台信息。
- 完善后的load()函数代码示例。
const load = () =>{ //给后端发起request请求 request.get("employee/selectPage",{ //参数示例:pageNum=xxx&pageSize=xxx params:{ pageNum:data.pageNum, pageSize:data.pageSize } }).then(res=>{ /*console.log(res.data)*/ data.EmployeeList = res.data.list data.total = res.data.total }) } load()
属性:show-overflow-tooltip。
- Element-UI的表格属性:允许内容在一行内显示,并在内容溢出时用省略号表示。同时当鼠标悬停时会显示所有完整内容。
- Employee.vue的代码示例。
<template> <div> <div class="card" style="margin-bottom: 5px"> <el-input style="width: 240px" v-model="data.name" placeholder="请根据名称查询" :prefix-icon="Search"></el-input> <el-button type="primary" style="margin-left: 10px" @click="load">查 询</el-button> <!--v-for事件绑定可以用"@"代替--> <el-button type="warning" style="margin-left: 10px">重 置</el-button> </div> <div class="card" style="margin-bottom: 5px"> <el-button type="primary" style="margin-left: 10px">新 增</el-button> <el-button type="warning" style="margin-left: 10px">批量删除</el-button> <el-button type="info" style="margin-left: 10px">导 入</el-button> <el-button type="success" style="margin-left: 10px">导 出</el-button> </div> <div class="card" style="margin-bottom: 5px"> <div style="margin: 30px"> <el-table :data="data.EmployeeList" stripe style="width: 100%"> <el-table-column label="姓名" prop="name"/> <el-table-column label="性别" prop="sex"/> <el-table-column label="工号" prop="no"/> <el-table-column label="年龄" prop="age"/> <el-table-column label="个人简介" prop="description" show-overflow-tooltip/> <el-table-column label="部门" prop="departmentName"/> <el-table-column label="操作"> <template #default="scope"> <el-button type="primary" circle> <el-icon><Edit /></el-icon> </el-button> <el-button type="danger" circle> <el-icon><Delete /></el-icon> </el-button> </template> </el-table-column> </el-table> </div> <div style="margin-top: 10px"> <el-pagination v-model:current-page="data.pageNum" v-model:page-size="data.pageSize" :page-sizes="[5, 10, 15, 20]" layout="total, sizes, prev, pager, next, jumper" :total="data.total" /> </div> </div> </div> </template> <script setup> import {Delete, Edit, Search} from "@element-plus/icons-vue"; import {reactive} from "vue"; import request from "@/utils/request.js"; const data = reactive({ name:'', pageNum:1, pageSize:10, total:0, EmployeeList:[], }) const load = () =>{ //给后端发起request请求 request.get("employee/selectPage",{ //参数示例:pageNum=xxx&pageSize=xxx params:{ pageNum:data.pageNum, pageSize:data.pageSize, } }).then(res=>{ /*console.log(res.data)*/ data.EmployeeList = res.data.list data.total = res.data.total }) } load() </script>
ElementUI的Pagination分页事件(@size-change、@current-change)。
<div style="margin-top: 10px"> <el-pagination @current-change="load" @size-change="load" v-model:current-page="data.pageNum" v-model:page-size="data.pageSize" :page-sizes="[5, 10, 15, 20]" layout="total, sizes, prev, pager, next, jumper" :total="data.total" /> </div>
- 分页查询的页面渲染效果。
<5>条件查询。
后端接口使用Employee对象接收参数。
/** * 查询所有员工 * 查询使用get请求 */ @GetMapping("/selectAll") public Result selectAll(Employee employee){ List<Employee> emList = employeeService.selectAll(employee); return Result.success(emList); } /** * 分页查询 * @param employee 对象。接收前端传递的属性值 * @param pageNum 当前页码。默认值1 * @param pageSize 每页数据个数。默认值10 * @return */ @GetMapping("/selectPage") public Result selectPage( Employee employee, @RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize) { PageInfo<Employee> employeePageInfo = employeeService.selectPage(employee,pageNum, pageSize); return Result.success(employeePageInfo); }
- service层。
public List<Employee> selectAll(Employee employee) { return employeeMapper.selectAll(employee); } /** *service层实现分页操作的代码 */ public PageInfo<Employee> selectPage(Employee employee,Integer pageNum,Integer pageSize) { //开启分页查询(传参:当前页码、每页分页个数) PageHelper.startPage(pageNum,pageSize); //查询所有的数据 List<Employee> employeeList = employeeMapper.selectAll(employee); //PageInfo执行分页操作 return PageInfo.of(employeeList); }
- EmployeeMapper。
package com.hyl.mapper; import com.hyl.entity.Employee; import java.util.List; public interface EmployeeMapper { List<Employee> selectAll(Employee employee); }
EmployeeMapper。(动态SQL条件查询)
<?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.hyl.mapper.EmployeeMapper"> <!--动态条件查询:如果有姓名就跟据姓名模糊查询,且根据id倒序展示数据(数据新增的放在zuiqm)--> <select id="selectAll" resultType="com.hyl.entity.Employee"> select * from `employee` <where> <if test="name != null">name like concat('%',#{name},'%')</if> </where> order by id desc </select> </mapper>
<6>重置(reset)操作。
<el-button type="warning" style="margin-left: 10px" @click="reset">重 置</el-button>
//重置输入框操作方法 const reset = () =>{ data.name = null //重新渲染数据 load() }
(2)新增。(Dialog对话框)
<1>新增对话框(弹窗)组件代码示例。
<el-dialog title="员工信息" v-model="data.formVisible" width="500"> <el-form :model="data.form" label-width="80px" style="padding-right: 50px;padding-top: 20px"> <el-form-item label="名称"> <el-input v-model="data.form.name" autocomplete="off" placeholder="请输入名称"/> </el-form-item> <el-form-item label="性别"> <el-radio-group v-model="data.form.sex"> <el-radio value="男">男</el-radio> <el-radio value="女">女</el-radio> </el-radio-group> </el-form-item> <el-form-item label="工号"> <el-input v-model="data.form.no" autocomplete="off" placeholder="请输入工号"/> </el-form-item> <!-- 设置最小年龄18 最大年龄100 --> <el-form-item label="年龄"> <el-input-number v-model="data.form.age" :min="18" :max="100" style="width: 250px" autocomplete="off" placeholder="年龄>=18与年龄<=100"/> </el-form-item> <!-- 设置类型:文本域 :rows设置默认显示三行 --> <el-form-item label="个人简介"> <el-input type="textarea" :rows="3" v-model="data.form.description" autocomplete="off" placeholder="请输入个人简介"/> </el-form-item> </el-form> <template #footer> <div class="dialog-footer"> <el-button @click="data.formVisible = false">取消</el-button> <el-button type="primary" @click="save"> 保存 </el-button> </div> </template> </el-dialog>
import {reactive} from "vue"; const data = reactive({ formVisible: false, form:{}, })
- 页面渲染效果。
<2>新增按钮添加事件。
<el-button type="primary" style="margin-left: 10px" @click="handleAdd">新 增</el-button>
import {reactive} from "vue"; const data = reactive({ name:'', pageNum:1, pageSize:10, total:0, EmployeeList:[], formVisible: false, form:{}, }) const handleAdd = () =>{ //显示新增对话框 data.formVisible = true //防止有脏数据 data.form ={} }
<3>对话框新增点击保存,调用save方法。("/employee/add")
- 代码示例。
import {reactive} from "vue"; import request from "@/utils/request.js"; import {ElMessage} from "element-plus"; const data = reactive({ formVisible: false, form:{}, }) const save = () =>{ request.post("/employee/add",data.form).then(res=>{ if(res.code === '200'){ //操作成功,关闭弹窗 data.formVisible=false ElMessage.success('操作成功') //新增后重新加载最新数据 load() }else { //操作失败,关闭弹窗 data.formVisible=false ElMessage.error(res.msg) } }) }
<4>后端代码示例。
- 新增接口。(controller)
package com.hyl.controller; import com.hyl.common.Result; import com.hyl.entity.Employee; import com.hyl.service.EmployeeService; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/employee") public class EmployeeController { @Resource private EmployeeService employeeService; /** *新增操作 */ @PostMapping("/add") public Result add(@RequestBody Employee employee){ employeeService.add(employee); return Result.success(); } }
- service层。
package com.hyl.service; import com.hyl.entity.Employee; import com.hyl.mapper.EmployeeMapper; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import java.util.List; @Service public class EmployeeService { @Resource private EmployeeMapper employeeMapper; public void add(Employee employee) { employeeMapper.insert(employee); } }
- EmployeeMapper。
package com.hyl.mapper; import com.hyl.entity.Employee; import java.util.List; public interface EmployeeMapper { void insert(Employee employee); }
- EmployeeMapper.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.hyl.mapper.EmployeeMapper"> <insert id="insert" parameterType="com.hyl.entity.Employee"> insert into `employee`(name,sex,no,age,description,department_id) values (#{name},#{sex},#{no},#{age},#{description},#{departmentId}) </insert> </mapper>
新增时页面渲染效果。
(3)更新(编辑)。(表格操作栏的“编辑”)
<1>替换操作栏“编辑”、“删除”样式。(链接文字按钮:link)
- 官方文档。
- 代码示例。
<el-table-column label="操作"> <template #default="scope"> <el-button link type="primary"> <el-icon><Edit /></el-icon>编辑 </el-button> <el-button link type="danger"> <el-icon><Delete /></el-icon>删除 </el-button> </template> </el-table-column>
- 页面渲染效果。
<2>“编辑”操作绑定事件handleUpdate(scope.row)函数。
- 将操作的行对象拿来渲染表单——用于更新员工信息。
<el-table-column label="操作"> <template #default="scope"> <el-button link type="primary" @click="handleUpdate(scope.row)"> <el-icon><Edit /></el-icon>编辑 </el-button> <el-button link type="danger"> <el-icon><Delete /></el-icon>删除 </el-button> </template> </el-table-column>
行对象(scope.row)的浅拷贝问题。(错误!)
//更新(编辑)操作 const handleUpdate = (row) =>{ //行对象的浅拷贝问题 data.form = row data.formVisible = true }
- 这样的设计写法法是有问题的。当我点击编辑按钮,页面渲染出员工信息编辑弹窗。
- 当我修改了某一个值,但没有点“保存”,点击“取消”。正常情况不会修改数据,但对象浅拷贝问题将当前的行对象也进行了修改。如下所示。
行对象的深拷贝。(JSON.stringify()、JSON.parse())
- JSON.stringify(row)会把row行对象转换为JSON 字符串,接着使用JSON.parse()再将这个JSON 字符串转换回一个新对象。
- 这个过程中会创建一个新的对象。新对象和原始的row行对象在内存中是相互独立的。其中对data.form进行修改不会影响原始的row行对象。
- 代码示例如下。
import {reactive} from "vue"; const data = reactive({ formVisible: false, form:{}, }) //更新(编辑)操作 const handleUpdate = (row) =>{ /*//行对象的浅拷贝问题 data.form = row*/ //深拷贝一个新的对象用于编辑。这样就不会影响行对象 data.form = JSON.parse(JSON.stringify(row)) data.formVisible = true }
- 再次测试。“取消”编辑时,行对象对应的数据并没有被影响或修改。
<3>区分新增操作的"保存"与编辑操作的"保存"。(post与put请求)
- 将保存按钮绑定事件的save函数添加条件判断。判断:操作的行对象是否有id。因为新增时员工没有id、编辑(更新)时员工已经存在id。
- 代码示例如下。
<script setup> import {Delete, Edit, Search} from "@element-plus/icons-vue"; import {reactive} from "vue"; import request from "@/utils/request.js"; import {ElMessage} from "element-plus"; const data = reactive({ name:'', pageNum:1, pageSize:10, total:0, EmployeeList:[], formVisible: false, form:{}, }) const load = () =>{ //给后端发起request请求 request.get("employee/selectPage",{ //参数示例:pageNum=xxx&pageSize=xxx params:{ pageNum:data.pageNum, pageSize:data.pageSize, name:data.name } }).then(res=>{ /*console.log(res.data)*/ data.EmployeeList = res.data.list data.total = res.data.total }) } //重置输入框操作方法 const reset = () =>{ data.name = null //重新渲染数据 load() } //新增操作 const handleAdd = () =>{ //显示新增对话框 data.formVisible = true //防止有脏数据 data.form ={} } //更新(编辑)操作 const handleUpdate = (row) =>{ /*//行对象的浅拷贝问题 data.form = row*/ //深拷贝一个新的对象用于编辑。这样就不会影响行对象 data.form = JSON.parse(JSON.stringify(row)) data.formVisible = true } //对话框的保存按钮事件 const save = () =>{ //有id进行更新操作。无id进行新增操作 data.form.id ? update() : add() } //新增方法。新增的行对象不存在id。 const add = () => { request.post("/employee/add",data.form).then(res=>{ if(res.code === '200'){ //操作成功,关闭弹窗 data.formVisible=false ElMessage.success('操作成功') //新增后重新加载最新数据 load() }else { //操作失败,关闭弹窗 data.formVisible=false ElMessage.error(res.msg) } }) } //编辑(更新)方法 。编辑的行对象存在id const update = () =>{ request.put("/employee/update",data.form).then(res=>{ if(res.code === '200'){ //操作成功,关闭弹窗 data.formVisible=false ElMessage.success('操作成功') //更新后重新加载最新数据 load() }else { //操作失败,关闭弹窗 data.formVisible=false ElMessage.error(res.msg) } }) } load() </script>
- 后端代码示例。
/** * 更新数据 */ @PutMapping("/update") public Result update(@RequestBody Employee employee){ employeeService.update(employee); return Result.success(); }
public void update(Employee employee) { //在数据库是根据id更新员工信息 employeeMapper.updateById(employee); }
void updateById(Employee employee);
<?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.hyl.mapper.EmployeeMapper"> <update id="updateById" parameterType="com.hyl.entity.Employee"> update `employee` set name = #{name},sex=#{sex},no=#{no},age=#{age},description=#{description},department_id=#{departmentId} where id = #{id} </update> </mapper>
- 编辑(更新)操作示例。(页面渲染效果)
(4)删除。(表格操作栏的“删除”)
<1>单个删除。
- 代码示例。
<el-table-column label="操作"> <template #default="scope"> <el-button link type="primary" @click="handleUpdate(scope.row)"> <el-icon><Edit /></el-icon>编辑 </el-button> <el-button link type="danger" @click="del(scope.row.id)"> <el-icon><Delete /></el-icon>删除 </el-button> </template> </el-table-column>
//删除方法(根据id删除) const del = (id) =>{ request.delete("/employee/deleteById/"+id).then(res=>{ if(res.code === '200'){ ElMessage.success('操作成功') //删除后重新加载最新数据 load() }else { ElMessage.error(res.msg) } }) }
“删除”的二次确认操作。(ElMessageBox.confirm())
import {ElMessage, ElMessageBox} from "element-plus"; //删除方法(根据id删除) const del = (id) =>{ ElMessageBox.confirm('删除数据后无法恢复,您确认删除吗?','删除确认',{type: 'warning'}).then(()=>{ request.delete("/employee/deleteById/"+id).then(res=>{ if(res.code === '200'){ ElMessage.success('操作成功') //删除后重新加载最新数据 load() }else { ElMessage.error(res.msg) } }) }).catch() }
- 后端删除接口与其它层代码示例。
/** *根据id删除员工 */ @DeleteMapping("/deleteById/{id}") public Result deleteById(@PathVariable Integer id){ employeeService.deleteById(id); return Result.success(); }
public void deleteById(Integer id) { employeeMapper.deleteById(id); }
@Delete("delete from `employee` where id = #{id}") void deleteById(Integer id);
- 页面删除操作时渲染效果。
<2>批量删除。(type="selection")
- 官方文档。
使用@selection-change="xxx函数"。(获取所选行对象)
<el-table :data="data.EmployeeList" stripe @selection-change="handleSelectionChange" style="width: 100%"> <el-table-column type="selection" width="55" /> <el-table-column label="名称" prop="name"/> <el-table-column label="性别" prop="sex"/> <el-table-column label="工号" prop="no"/> <el-table-column label="年龄" prop="age"/> <el-table-column label="个人简介" prop="description" show-overflow-tooltip/> <el-table-column label="部门" prop="departmentName"/> <el-table-column label="操作"> <template #default="scope"> <el-button link type="primary" @click="handleUpdate(scope.row)"> <el-icon><Edit /></el-icon>编辑 </el-button> <el-button link type="danger" @click="del(scope.row.id)"> <el-icon><Delete /></el-icon>删除 </el-button> </template> </el-table-column> </el-table>
//批量选择 const handleSelectionChange = (rows) =>{ //返回选中的行对象数组 console.log(rows) }
使用map函数提取返回的数组中所选中行对象的id。
import {reactive} from "vue"; //批量选择 const handleSelectionChange = (rows) =>{ //返回选中的行对象数组 console.log(rows) //从选中的行对象中取出所有的id,组成一个新数组! //使用map函数(数组内置函数) //遍历rows数组,将每个元素(行对象)的id属性值提取出来,组成一个新的数组。并赋值给存储id的数组中 data.ids = rows.map(row => row.id) console.log(data.ids) }
批量删除的后端接口代码。(controller)
- 后端接口必须使用注解@RequestBody接受前端数组传参。
/** *批量删除员工信息 */ @DeleteMapping("/deleteBatch") public Result deleteBatch(@RequestBody List<Integer> ids){ employeeService.deleteBatch(ids); return Result.success(); }
service层。(依次调用deleteById)
public void deleteById(Integer id) { employeeMapper.deleteById(id); } public void deleteBatch(List<Integer> ids) { for (Integer id : ids) { //依次根据员工id删除 this.deleteById(id); } }
前端代码示例。
<div class="card" style="margin-bottom: 5px"> <el-button type="primary" style="margin-left: 10px" @click="handleAdd">新 增</el-button> <el-button type="danger" style="margin-left: 10px" @click="delBatch">批量删除</el-button> <el-button type="info" style="margin-left: 10px">导 入</el-button> <el-button type="success" style="margin-left: 10px">导 出</el-button> </div>
import {reactive} from "vue"; import request from "@/utils/request.js"; import {ElMessage, ElMessageBox} from "element-plus"; const data = reactive({ ids:[] }) //批量删除 const delBatch = () => { //如果没有选中表格项,则提示错误 if (data.ids.length === 0) { ElMessage.warning('请选中需删除的数据') return } ElMessageBox.confirm('批量删除数据后无法恢复,您确认删除吗?','批量删除确认',{type:'warning'}).then(()=>{ request.delete("/employee/deleteBatch", {data: data.ids}).then(res => { if(res.code === '200'){ ElMessage.success('操作成功') //删除后重新加载最新数据 load() }else { ElMessage.error(res.msg) } }) }).catch() }
- 当没选中表格项,进行批量删除的页面渲染效果。
- 选中表格的某几个选单项。进行批量删除操作。
- 到这里就算SpringBoot3+Vue3实现基本增删改查的完结。还需要自己再琢磨琢磨很多新的知识!