SpringBoot3实战(SpringBoot3+Vue3基本增删改查、前后端通信交互、配置后端跨域请求、数据批量删除(超详细))(3)
目录
一、从0快速搭建SpringBoot3工程、SpringBoot3集成MyBatis、PageHelper分页查询的详细教程。(博客链接)
二、实现前端与后端通信对接数据。(axios工具)
(1)安装axios。(vue工程目录)
(2)封装请求工具类。(request.js)
<1>src目录、utils目录下。
<2>request.js代码。(request拦截器、response拦截器)
<3>简单请求示例。
前端请求后端接口代码示例。
浏览器拦截非同源请求。(跨域请求问题)
<4>SpringBoot后端中配置统一跨域处理。
三、SpringBoot3+Vue3实现基本增删改查。
(1)查询。
<1>新建Employee.vue(员工信息页面)。
<2>配置Employee.vue路由。
<3>页面初级渲染效果。
<4>分页查询。("/employee/selectPage")
属性:show-overflow-tooltip。
ElementUI的Pagination分页事件(@size-change、@current-change)。
<5>条件查询。
后端接口使用Employee对象接收参数。
EmployeeMapper。(动态SQL条件查询)
<6>重置(reset)操作。
(2)新增。(Dialog对话框)
<1>新增对话框(弹窗)组件代码示例。
<2>新增按钮添加事件。
<3>对话框新增点击保存,调用save方法。("/employee/add")
<4>后端代码示例。
新增时页面渲染效果。
(3)更新(编辑)。(表格操作栏的“编辑”)
<1>替换操作栏“编辑”、“删除”样式。(链接文字按钮:link)
<2>“编辑”操作绑定事件handleUpdate(scope.row)函数。
行对象(scope.row)的浅拷贝问题。(错误!)
行对象的深拷贝。(JSON.stringify()、JSON.parse())
<3>区分新增操作的"保存"与编辑操作的"保存"。(post与put请求)
(4)删除。(表格操作栏的“删除”)
<1>单个删除。
“删除”的二次确认操作。(ElMessageBox.confirm())
<2>批量删除。(type="selection")
使用@selection-change="xxx函数"。(获取所选行对象)
使用map函数提取返回的数组中所选中行对象的id。
批量删除的后端接口代码。(controller)
service层。(依次调用deleteById)
前端代码示例。
一、从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-8config.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容器中*/@Beanpublic 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-paginationv-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.titlenext() //必须调用的方法 })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=xxxparams:{pageNum:data.pageNum,pageSize:data.pageSize}}).then(res=>{/*console.log(res.data)*/data.EmployeeList = res.data.listdata.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-paginationv-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=xxxparams:{pageNum:data.pageNum,pageSize:data.pageSize,}}).then(res=>{/*console.log(res.data)*/data.EmployeeList = res.data.listdata.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 mapperPUBLIC "-//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对话框)
- 简单教程:Vue3实战学习(Element-Plus常用组件的使用(轮播图、日期时间选择器、表格、分页组件、对话框)(超详细))(下)(6)_vuejs30 + elementplus自定义组件实战-CSDN博客
<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=falseElMessage.success('操作成功')//新增后重新加载最新数据load()}else {//操作失败,关闭弹窗data.formVisible=falseElMessage.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 {@Resourceprivate 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 {@Resourceprivate 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 mapperPUBLIC "-//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 = rowdata.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=xxxparams:{pageNum:data.pageNum,pageSize:data.pageSize,name:data.name}}).then(res=>{/*console.log(res.data)*/data.EmployeeList = res.data.listdata.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=falseElMessage.success('操作成功')//新增后重新加载最新数据load()}else {//操作失败,关闭弹窗data.formVisible=falseElMessage.error(res.msg)}}) }//编辑(更新)方法 。编辑的行对象存在id const update = () =>{request.put("/employee/update",data.form).then(res=>{if(res.code === '200'){//操作成功,关闭弹窗data.formVisible=falseElMessage.success('操作成功')//更新后重新加载最新数据load()}else {//操作失败,关闭弹窗data.formVisible=falseElMessage.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 mapperPUBLIC "-//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实现基本增删改查的完结。还需要自己再琢磨琢磨很多新的知识!
相关文章:
SpringBoot3实战(SpringBoot3+Vue3基本增删改查、前后端通信交互、配置后端跨域请求、数据批量删除(超详细))(3)
目录 一、从0快速搭建SpringBoot3工程、SpringBoot3集成MyBatis、PageHelper分页查询的详细教程。(博客链接) 二、实现前端与后端通信对接数据。(axios工具) (1)安装axios。(vue工程目录) (2)封装请求工具类。(request.js) <1&…...
LabVIEW发电平台数据采集系统
本文详细介绍了基于LabVIEW的摇臂式波浪发电平台数据采集系统的设计与实现。通过整合LabVIEW软件与多种传感器技术,本系统能够有效提升数据采集的准确性和效率,为波浪能的利用和发电设备的优化提供科学依据。 项目背景 随着全球能源需求增长和环境保…...
人工智能(AI)系统化学习路线
一、为什么需要系统化学习AI? 人工智能技术正在重塑各行各业,但许多初学者容易陷入误区: ❌ 盲目跟风:直接学习TensorFlow/PyTorch,忽视数学与算法基础。 ❌ 纸上谈兵:只看理论不写代码,无法解…...
oracle事务的组成
1)数据库事务由以下的部分组成: 一个或多个DML 语句 ; 一个 DDL(Data Definition Language – 数据定义语言) 语句; 一个 DCL(Data Control Language – 数据控制语言)语句; 2)事务的执行开始: 以第一个 DML 语句的执行作为开始 ,…...
第二十八篇 数据获取与数据分析:数仓体系下的专业化分工与协同
声明:文章内容仅供参考,需仔细甄别。文中技术名称属相关方商标,仅作技术描述;代码示例为交流学习用途,部分参考开源文档(Apache 2.0/GPLv3);案例数据已脱敏,技术推荐保持…...
SpringSecurity——前后端分离登录认证
SpringSecurity——前后端分离登录认证的整个过程 前端: 使用Axios向后端发送请求 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>登录</title><script src"https://cdn…...
【AI】Orin Nano+ubuntu22.04上移植YoloV11,并使用DeepStream测试成功
1、准备工作 使用 sdk-manager 烧写 OrinNano, JetPack版本为6.0 DP,对应操作系统为:Ubuntu22.04 参见博客:【NVIDIA】Jetson Orin Nano系列:烧写Ubuntu22.04 2、安装 PyTorch 2.1 下载依赖 1)安装onnx pip install onnx -i https://pypi.tuna.tsinghua.edu.cn/sim…...
RHCE 使用nginx搭建网站
一。准备工作 Windows dns映射 创建目录网页 vim 编辑内容 添加如下 重启nginx服务,在Windows浏览器进行测试...
arm linux下的读写信号量rw_semphore的实现
本文基于arm linux 5.10来介绍内核中使用的读写信号量rw remphore的实现代码。 内核中信号量结构体struct rw_semaphore的定义在include/linux/rwsem.h 32位architectures下,结构体struct rw_semaphore中的count的使用如下: 先来看信号量的定义和初始化…...
搭建主从DNS、nfs、nginx
任务需求: 客户端通过访问 www.nihao.com 后,能够通过 dns 域名解析,访问到 nginx 服务中由 nfs 共享的首页文件,内容为:Very good, you have successfully set up the system. 各个主机能够实现时间同步,…...
Qt6+QML实现Windows屏幕录制
前言 Qt6提供了更丰富的多媒体支持类,使用Qt6 QMediaCaptureSession、QScreenCapture、QMediaRecorder,来实现一个屏幕录制的demo,其中QScreenCapture 最低版本 Qt6.5。支持录制的清晰度设置,选择视频保存位置,UI使用…...
python二级每日十题(1)
\ 第一题,在Python中,变量名的命名规则:以字母或下划线开头,后面跟字母、下划线和数字;不能以数字开头.故选c项,博主正确 缩进:在逻辑行首的空白(空格和制表符)用来决定逻辑行的缩进层次&…...
记录一次truncate导致MySQL夯住的故障
目录 环境信息: 故障描述: 处理过程: 原理分析: show processlist结果中的system lock含义: truncate原理: 1. TRUNCATE 的执行流程 2、TRUNCATE 表导致数据库夯住的原因 3、 TRUNCATE 表导致…...
Java Web应用程序实现用户登录、学生信息管理和验证码验证以及页面跳转等基本功能(IDEA)含(Ajax、JSTL)
一、具体框架以及代码功能的展示: 1. 文件结构 web03: 项目根目录。 src: 包含Java源代码。 cn.lvb: 主包。 bean: 包含实体类,如 Book 和 Student。 controller: 包含处理HTTP请求的Servlet类,如 DoLogin, Index, StuList1, VerifyCode。 …...
【Mybatis-plus】在mybatis-plus中 if test标签如何判断 list不为空
博主介绍:✌全网粉丝22W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
MySQL 事务(Transaction)详解
MySQL 事务(Transaction)详解 1. 什么是事务? 事务(Transaction)是一组要么全部执行,要么全部回滚的 SQL 语句,用于保证数据一致性。事务一般用于银行转账、订单支付等操作,确保多个…...
Redis 知识点梳理
第一章 NoSQL数据库发展历史简介 1、 Web的历史发展历程 web1.0时代简介 web 1.0是以编辑为特征,网站提供给用户的内容是网站编辑进行编辑处理后提供的,用户阅读网站提供的内容这个过程是网站到用户的单向行为web1.0时代的代表站点为新浪,…...
github上传操作简单说明
前期准备 0.下载git(如果已经有了就不用了) 1.在GitHub上新建一个存储库 2.先在本地创建一个目录作为本地库目录,在目录里打开git bash进行上传 上传过程 echo "# Garbled_repair" >> README.md 作用:创建一个…...
在 ASP .NET Core 9.0 中使用 Scalar 创建漂亮的 API 文档
示例代码:https://download.csdn.net/download/hefeng_aspnet/90407900 Scalar 是一款可帮助我们为 API 创建精美文档的工具。与感觉有些过时的默认 Swagger 文档不同,Scalar 为 API 文档提供了全新而现代的 UI。其简洁的设计让开发人员可以轻松找到测试…...
针对 pdf.mjs 文件因 MIME 类型错误导致的 Failed to load module script 问题解决方案
Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of “application/octet-stream”. Strict MIME type checking is enforced for module scripts per HTML spec. pdf.mjs 这种问题该如何处理 nginx 针对 pdf.…...
Flink介绍与安装
Apache Flink是一个在有界数据流和无界数据流上进行有状态计算分布式处理引擎和框架。Flink 设计旨在所有常见的集群环境中运行,以任意规模和内存级速度执行计算。 一、主要特点和功能 1. 实时流处理: 低延迟: Flink 能够以亚秒级的延迟处理数据流,非常…...
《双指针算法指南:LeetCode 经典题解(C++实现)》
《双指针算法指南:LeetCode 经典题解(C实现)》 —— 从快慢指针到对撞指针,刷题效率提升 200%! 常⻅的双指针有两种形式,⼀种是对撞指针,⼀种是左右指针。 对撞指针: ⼀般⽤于顺…...
kaggle上经典泰坦尼克项目数据分析探索
之前了解在kaggle上这个项目很火,最近想要加强一下python数据分析,所以在kaggle上找到这个项目进行学习探索,下面是将一些学习资料以及过程整理出来。 一、首先我们了解一下项目背景以及如何找到这个项目。 kaggle项目地址: https://www.k…...
【深度学习】多目标融合算法(五):定制门控网络CGC(Customized Gate Control)
目录 一、引言 二、CGC(Customized Gate Control,定制门控网络) 2.1 技术原理 2.2 技术优缺点 2.3 业务代码实践 2.3.1 业务场景与建模 2.3.2 模型代码实现 2.3.3 模型训练与推理测试 2.3.4 打印模型结构 三、总结 一、引言 上一…...
Ubuntu上查看GPU使用情况并释放内存
先用nvidia-smi查看GPU当前使用情况 再用fuser 命令查找对应显卡上占用 GPU 的进程 最后查到了用kill -9强制杀掉进程(PID)即可...
大数据学习栈记——HBase安装
本文介绍大数据技术中流行的非关系型数据库HBase的安装,操作系统:Ubuntu24.04 安装Zookeeper 安装HBase前需要先安装Zookeeper,HBase使用Zookeeper作为其分布式协同服务,存储了HBase集群的元数据信息,并提供了分布式…...
[入门]NUC13配置Ubuntu20.04详细步骤
文章目录 1. 安装Ubuntu20.041.1 制作系统启动盘1.1.1 下载镜像文件1.1.2 配置启动盘 1.2 安装内存条、硬盘1.3 安装系统 2. 网卡驱动配置2.1 关闭安全启动2.2 安装intel官方网卡驱动backport2.2.1 第四步可能会出现问题 2.3 ubuntu官方的驱动2.4 重启 3. 软件安装3.1 录屏软件…...
【实战指南】用MongoDB存储文档和图片等大文件(Java实现)
一、前言 在现代应用开发中,经常需要处理和存储大量的文档、图片等大文件。传统的关系型数据库在处理这类大文件时,往往会面临性能瓶颈、存储成本高等问题。而 MongoDB 作为一款流行的 NoSQL 数据库,提供了 GridFS 规范,能够很好地解决大文件存储的问题。GridFS 可以将大文…...
使用Gitee Go流水线部署个人项目到服务器指南
使用Gitee Go流水线部署个人项目到服务器指南 前言!!! 本文解决的问题: 你有一台ECS服务器,你在上面部署了一个Java服务也就是一个jar,你觉着你每次手动本地打包,上传,在通过命令去…...
使用Three.js渲染器创建炫酷3D场景
引言 在当今数字化的时代,3D图形技术正以其独特的魅力在各个领域掀起波澜。从影视制作到游戏开发,从虚拟现实到网页交互,3D场景以其强烈的视觉冲击力和沉浸式的体验,成为了吸引用户、传达信息的重要手段。而Three.js,…...
Spring Boot 集成 Elasticsearch怎样在不启动es的情况下正常启动服务
解释 在spingboot 集成es客户端后,每当服务启动时,服务默认都会查看es中是否已经创建了对应的索引,如果没有索引则创建。基于上面的规则我们可以通过配置不自动创建索引来达到在没有es服务的情况下正常启动服务。 解决办法 在entity类的Docu…...
明远智睿SD2351核心板:多接口融合,破解边缘计算难题
在边缘计算领域,明讯智睿SD2351核心板凭借丰富的接口资源与异构计算架构,成为工业网关与智能终端的理想选择。硬件配置升级 :处理器:四核Cortex-A35,256KB L2缓存,动态调频降低功耗;存储性能:emMC 5.0 HS400模式读写速度提升40%&a…...
xampp安装教程与配置
一、安装 XAMPP (一)下载 访问官网:打开浏览器,访问 XAMPP 官方网站(https://www.apachefriends.org/zh_cn/index.html)。 选择版本:根据你的操作系统(Windows、macOS 或 Linux&am…...
设计模式之单例模式(Singleton Pattern)
单例模式(Singleton Pattern)是一种常用的设计模式,确保一个类只有一个实例,并提供一个全局访问点。单例模式在许多场景中非常有用,例如配置管理、日志记录、线程池等。 ### **1. 单例模式的特点** 1. **唯一实例**&a…...
Androidstudio实现引导页文字动画
文章目录 1. 功能需求2. 代码实现过程1. 编写布局文件2. 实现引导页GuideActivity 3. 总结4. 效果演示5. 关于作者其它项目视频教程介绍 1. 功能需求 1、引导页具有两行文字(“疫情在前”和“共克时艰”),和向上的 图标。 2、进入【引导页】…...
【linux】线程概念与控制
引言 当现代CPU的晶体管密度逼近物理极限,多核架构已成为突破性能瓶颈的必由之路。在这个计算密集型任务与异步IO需求并行的时代,多线程编程不再是可选项,而是开发者必须掌握的核心技能。Linux作为承载着全球90%云计算负载的操作系统…...
Cesium 自定义路径导航材质
cesium 自定义路径导航纹理图片随便更换,UI 提供设计图片即可达到效果; 打开小马的weix 关注下 搜索“技术链” 回复关键词《《路径》》获取原始代码; 拿到就能用轻松解决!帮忙点个关注吧!...
用 pytorch 从零开始创建大语言模型(五):预训练无标注数据
用 pytorch 从零开始创建大语言模型(五):预训练无标注数据 5 预训练无标注数据5.1 评估文本生成模型5.1.1 使用 GPT 生成文本5.1.2 计算文本生成损失5.1.3 计算训练集和验证集的损失 5.2 训练 LLM5.3 解码策略以控制随机性5.3.1 温度缩放&am…...
[AI速读]混合验证方案:如何高效解决RISC-V向量扩展的验证难题
RISC-V作为一种开源指令集架构,近年来在AI和高性能计算领域备受关注。其向量扩展(RVV)为处理并行数据提供了强大的支持,但复杂的异常处理和冒险检测机制也带来了验证上的巨大挑战。本文将结合一篇技术论文,解析一种混合验证方案,帮助开发者更高效地解决RVV的验证问题。 一…...
模型空间、图纸空间、布局(Layout)之间联系——CAD c#二次开发
在 AutoCAD 的二次开发中,**模型空间(Model Space)**、**图纸空间(Paper Space)** 和 **布局(Layout)** 是三个核心概念,它们的关系及开发中的操作逻辑如下: --- 1. 模…...
Linux 日常开发常用命令(解释-全)
帮助类 #查看cd命令的帮助信息 [rootcentos100 ~] help cd # 查看网卡配置信息 [rootcentos100 ~] ifconfig # 检测到目标主机是否连接正常 [rootcentos100 ~] ping IP地址[rootcentos100 ~] ssh [-p port] userremote #远程登录user 是在远程机器上的用户名,如果…...
数据库监控:确保业务连续性和用户体验
在数字化时代,数据库作为企业的数据心脏,其重要性不言而喻。无论是交易系统、客户关系管理系统,还是数据分析平台,都离不开数据库的支撑。然而,数据库的运行状态和性能直接影响着企业的业务连续性和用户体验。因此&…...
Java面试黄金宝典9
1. Redis 持久化机制 Redis 提供了两种主要的持久化机制:RDB(Redis Database)和 AOF(Append Only File),下面对这两种机制进行详细介绍。 RDB(Redis Database) 原理:RDB …...
【C#】WinForm自定义控件及窗体
前言 WinForm(Windows Forms)是Microsoft.NET框架中的技术,用于开发Windows桌面应用程序。它提供了一套丰富的控件和组件。通过拖放控件、编写事件处理程序等方式快速构建用户界面。 通过属性窗口定制这些控件的外观和行为。 通过数据绑定&am…...
VideoHelper 油猴脚本,重塑你的视频观看体验
VideoHelper 油猴脚本,重塑你的视频观看体验 在日常上网看视频时,你是否也被这些问题困扰:视频网站开头的广告又臭又长,找个合适的播放倍速要在一堆选项里翻半天,每次手动调音量、点全屏按钮繁琐又影响沉浸感…...
从收藏夹到知识图谱:书签管理器如何重塑你的信息获取方式?
在信息爆炸的今天,浏览器收藏夹早已沦为 “数字垃圾堆”—— 杂乱无章的网址、重复的标签、过期的链接,不仅降低效率,更成为信息焦虑的源头。智能书签管理器通过AI分类、跨平台同步、隐私保护等黑科技,重塑您的数字生活方式。书签…...
SOFABoot-07-版本查看
前言 大家好,我是老马。 sofastack 其实出来很久了,第一次应该是在 2022 年左右开始关注,但是一直没有深入研究。 最近想学习一下 SOFA 对于生态的设计和思考。 sofaboot 系列 SOFABoot-00-sofaboot 概览 SOFABoot-01-蚂蚁金服开源的 s…...
[极客大挑战 2019]Http_3.19BUUCTF练习day3(1)
BUUCTF练习day3(1)_[极客大挑战 2019]Http 打开靶场,查看源码(如果在做题时没有什么发现就查看源码) 打开Secret.php提示没有来自https://Sycsecret.buuoj.cn 添加Referer头为https://Sycsecret.buuoj.cn(提示说我们没有来自那个网址,那我们…...
golang Error的一些坑
golang Error的一些坑 golang error的设计可能是被人吐槽最多的golang设计了。 最经典的err!nil只影响代码风格设计,而有一些坑会导致我们的程序发生一些与我们预期不符的问题,开发过程中需要注意。 errors.Is判断error是否Wrap不符合预期 …...
弱网测试:全链路实战、高阶策略与自动化落地
在移动互联网时代,网络环境的不确定性成为用户体验的“隐形杀手”。弱网测试不仅是质量保障的必备环节,更是提升用户留存率和业务转化率的战略手段。 本文将从 工具链深度配置、复杂场景模拟、性能优化底层逻辑 和 自动化流水线集成 四个维度,彻底解析弱网测试的完整方法论…...