10. SpringBoot3+Vue3实现基本的增删改查功能
本节课要求
先学完本节课的内容,然后不看视频和笔记,自己能独立完成下面的这个页面的基本的增删改查
只有完成这个功能,你才能跟后面的课程

安装 axios 封装前后端对接数据工具
npm i axios -S
request.js
import axios from "axios";
import {ElMessage} from "element-plus";
const request = axios.create({
baseURL: 'http://localhost:9090',
timeout: 30000 // 后台接口超时时间
})
// request 拦截器
// 可以自请求发送前对请求做一些处理
request.interceptors.request.use(config => {
config.headers['Content-Type'] = 'application/json;charset=utf-8';
return config
}, error => {
return Promise.reject(error)
});
// response 拦截器
// 可以在接口响应后统一处理结果
request.interceptors.response.use(
response => {
let res = response.data;
// 兼容服务端返回的字符串数据
if (typeof res === 'string') {
res = res ? JSON.parse(res) : res
}
return res;
},
error => {
if (error.response.status === 404) {
ElMessage.error('未找到请求接口')
} else if (error.response.status === 500) {
ElMessage.error('系统异常,请查看后端控制台报错')
} else {
console.error(error.message)
}
return Promise.reject(error)
}
)
export default request
一个最简单的请求示例
import request from "@/utils/request.js";
request.get('/employee/selectAll').then(res => {
console.log(res) // 打印数据
data.employeeList = res.data // res.data就是员工的列表数据 是一个数组
console.log(data.employeeList)
})
遇到了跨域错误

在 Springboot 里面设置统一的跨域处理
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
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*"); // 1 设置访问源地址
corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头
corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法
source.registerCorsConfiguration("/**", corsConfiguration); // 4 对接口配置跨域设置
return new CorsFilter(source);
}
}
设置完后记得重启
🔴学会分析网络请求(以后排查问题的关键手段)


分页查询数据

当你发起请求的时候携带了参数,你可以在 **载荷 **这里查看
table
<el-table :data="data.tableData" stripe>
<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>
<div style="margin-top: 15px">
<el-pagination
@size-change="load"
@current-change="load"
v-model:current-page="data.pageNum"
v-model:page-size="data.pageSize"
:page-sizes="[5, 10, 15, 20]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="data.total"
/>
</div>
请求数据
<script setup>
import { reactive } from "vue";
import { Search } from "@element-plus/icons-vue"
import request from "@/utils/request.js";
const data = reactive({
name: null,
tableData: [],
pageNum: 1,
pageSize: 10,
total: 0
})
const load = () => {
request.get('/employee/selectPage', { // ?pageNum=1&pageSize=10
params: {
pageNum: data.pageNum,
pageSize: data.pageSize
}
}).then(res => {
data.tableData = res.data.list
data.total = res.data.total
})
}
load()
</script>
条件查询
动态条件查询

查询 sql

新增数据
- 新增弹窗组件 设置表单
<el-dialog title="员工信息" v-model="data.formVisible" width="500">
<el-form :model="data.form" label-width="80px" style="padding-right: 40px; 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="男" label="男"></el-radio>
<el-radio value="女" label="女"></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>
<el-form-item label="年龄">
<el-input-number style="width: 180px" :min="18" v-model="data.form.age" autocomplete="off" placeholder="请输入年龄" />
</el-form-item>
<el-form-item label="个人介绍">
<el-input :rows="3" type="textarea" 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>
通过 handleAdd 打开弹窗
const handleAdd = () => {
data.formVisible = true
data.form = {}
}
2.点击保存按钮 发起请求
const save = () => {
request.post('/employee/add', data.form).then(res => {
if (res.code === '200') {
data.formVisible = false
ElMessage.success('操作成功')
load() // 新增后一定要重新加载最新的数据
} else {
ElMessage.error(res.msg)
}
})
}


编辑数据
- 打开弹窗
- 设置弹窗数据
- 调用更新的接口
- 重新加载表格数据
const handleUpdate = (row) => {
data.form = JSON.parse(JSON.stringify(row)) // 深拷贝一个新的对象 用于编辑 这样就不会影响行对象
data.formVisible = true
}
const update = () => {
request.put('/employee/update', data.form).then(res => { // 编辑的对象里面包含id
if (res.code === '200') {
data.formVisible = false
ElMessage.success('操作成功')
load() // 更新后一定要重新加载最新的数据
} else {
ElMessage.error(res.msg)
}
})
}
const save = () => { // 在一个保存方法里面做2个操作 一个是新增 一个是编辑
data.form.id ? update() : add()
}

删除数据
单个删除
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()
}
批量删除
后端接口必须使用 @RequestBody 接受数组

批量删除的前端代码

const handleSelectionChange = (rows) => { // 返回所有选中的行对象数组
// 从选中的行数组里面取出所有行的id组成一个新的数组
data.ids = rows.map(row => row.id)
console.log(data.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()
}
本节课完整的页面代码 Employee.vue
<template>
<div>
<div class="card" style="margin-bottom: 5px">
<el-input style="width: 240px; margin-right: 10px" v-model="data.name" placeholder="请输入名称查询" prefix-icon="Search"></el-input>
<el-button type="primary" @click="load">查 询</el-button>
<el-button type="warning" @click="reset">重 置</el-button>
</div>
<div class="card" style="margin-bottom: 5px">
<el-button type="primary" @click="handleAdd">新 增</el-button>
<el-button type="danger" @click="delBatch">批量删除</el-button>
<!-- <el-button type="info">导入</el-button>-->
<!-- <el-button type="success">导出</el-button>-->
</div>
<div class="card" style="margin-bottom: 5px">
<el-table :data="data.tableData" stripe @selection-change="handleSelectionChange">
<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="操作" width="120">
<template #default="scope">
<el-button @click="handleUpdate(scope.row)" type="primary" :icon="Edit" circle></el-button>
<el-button @click="del(scope.row.id)" type="danger" :icon="Delete" circle></el-button>
</template>
</el-table-column>
</el-table>
<div style="margin-top: 15px">
<el-pagination
@size-change="load"
@current-change="load"
v-model:current-page="data.pageNum"
v-model:page-size="data.pageSize"
:page-sizes="[5, 10, 15, 20]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="data.total"
/>
</div>
</div>
<el-dialog title="员工信息" v-model="data.formVisible" width="500">
<el-form :model="data.form" label-width="80px" style="padding-right: 40px; 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="男" label="男"></el-radio>
<el-radio value="女" label="女"></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>
<el-form-item label="年龄">
<el-input-number style="width: 180px" :min="18" v-model="data.form.age" autocomplete="off" placeholder="请输入年龄" />
</el-form-item>
<el-form-item label="个人介绍">
<el-input :rows="3" type="textarea" 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>
</div>
</template>
<script setup>
import { reactive } from "vue";
import {Edit, Delete, Search} from "@element-plus/icons-vue"
import request from "@/utils/request.js";
import {ElMessage, ElMessageBox} from "element-plus";
const data = reactive({
name: null,
tableData: [],
pageNum: 1,
pageSize: 10,
total: 0,
formVisible: false,
form: {},
ids: []
})
const load = () => {
request.get('/employee/selectPage', { // ?pageNum=1&pageSize=10
params: {
pageNum: data.pageNum,
pageSize: data.pageSize,
name: data.name
}
}).then(res => {
data.tableData = res.data.list
data.total = res.data.total
})
}
load()
const reset = () => {
data.name = null
load()
}
const handleAdd = () => {
data.formVisible = true
data.form = {}
}
const save = () => { // 在一个保存方法里面做2个操作 一个是新增 一个是编辑
data.form.id ? update() : add()
}
const add = () => {
request.post('/employee/add', data.form).then(res => { // 新增的对象里面没有id
if (res.code === '200') {
data.formVisible = false
ElMessage.success('操作成功')
load() // 新增后一定要重新加载最新的数据
} else {
ElMessage.error(res.msg)
}
})
}
const handleUpdate = (row) => {
data.form = JSON.parse(JSON.stringify(row)) // 深拷贝一个新的对象 用于编辑 这样就不会影响行对象
data.formVisible = true
}
const update = () => {
request.put('/employee/update', data.form).then(res => { // 编辑的对象里面包含id
if (res.code === '200') {
data.formVisible = false
ElMessage.success('操作成功')
load() // 更新后一定要重新加载最新的数据
} else {
ElMessage.error(res.msg)
}
})
}
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()
}
const handleSelectionChange = (rows) => { // 返回所有选中的行对象数组
// 从选中的行数组里面取出所有行的id组成一个新的数组
data.ids = rows.map(row => row.id)
console.log(data.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()
}
</script>