1 添加删除和编辑按钮
element官网
修改User.vue
删除需确认窗口
修改user_api.js
在user_api.js中新增一个删除接口
javascript
userDelete:function(id){
return http({
url:'/api/user/'+id,
method:'delete'
});
},
在User.vue中增加组件
java
<script setup>
import {ref,onMounted} from 'vue'
import user_api from '@/api/user_api.js'
import { ElMessageBox,ElMessage } from 'element-plus'
const tableData = ref([]);
onMounted(function(){
showTableData();
});
const showTableData=async()=>{
const result = await user_api.userList({current:1,size:10});
tableData.value=result.data.records;
}
//执行删除按钮执行的代码
const handleDelete = (index, row) => {
//row就是当前行的数据
//index就是当前数据的下表
ElMessageBox.confirm('是否要删除该行数据?')
.then(async() => {
//点击执行删除操作
const result = await user_api.userDelete(row.id);
showTableData();
if(result.code==200 && result.message){
ElMessage({
message: result.message,
type: 'success',
})
}
if(result.code!=200){
ElMessage({
message: '系统异常',
type: 'warning',
})
}
}).catch(()=>{
ElMessage.error('服务端出现异常')
});
};
</script>
<template>
<el-table :data="tableData" border style="width: 100%">
<el-table-column prop="username" label="账号" width="180" />
<el-table-column prop="nickname" label="昵称" width="180" />
<el-table-column label="状态" >
<template #default="scope">
<el-switch
v-model="scope.row.status"
inline-prompt
style="--el-switch-on-color:#13ce66; --el-switch-off-color: #ff4949"
active-text="正常"
inactive-text="封号"
:active-value="0"
:inactive-value="1"
/>
</template>
</el-table-column>
<el-table-column prop="last_login_time" label="最近登录时间" />
<el-table-column label="操作">
<template #default="scope">
<el-button
size="small"
@click="handleEdit(scope.$index, scope.row)"
>
修改
</el-button>
<el-button
size="small"
type="danger"
@click="handleDelete(scope.$index, scope.row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</template>
<style scoped></style>

2 添加"新增"按钮,并实现点击"新增"按钮,弹出"新增窗口"
2.1 在user_api.js中添加新增接口
java
// 新增:用户新增接口
userAdd:function(obj){
return http({
url:'/api/user/',
method:'post',
data: obj,
headers: {
'Content-Type': 'application/json'
}
});
},
在User.vue中添加"新增方法"
2.2 创建UserAdd.vue文件

java
<template>
<el-dialog
v-model="localVisible"
title="新增用户"
width="400px"
@close="handleClose"
>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="80px"
style="max-width: 360px"
>
<el-form-item label="账号" prop="username">
<el-input v-model="form.username" placeholder="请输入用户账号" />
</el-form-item>
<el-form-item label="昵称" prop="nickname">
<el-input v-model="form.nickname" placeholder="请输入用户昵称" />
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input
v-model="form.password"
type="password"
placeholder="请输入6-20位密码"
show-password
/>
</el-form-item>
<el-form-item label="状态">
<el-switch
v-model="form.status"
inline-prompt
style="--el-switch-on-color:#13ce66; --el-switch-off-color: #ff4949"
active-text="正常"
inactive-text="封号"
:active-value="0"
:inactive-value="1"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<!-- 新增:加载状态,防止重复提交 -->
<el-button type="primary" @click="handleSubmit" :loading="loading">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import user_api from '@/api/user_api.js'
// 接收父组件传参(弹窗显隐)
const props = defineProps({
visible: { type: Boolean, default: false }
})
// 向父组件传递事件
const emit = defineEmits(['close', 'refreshList'])
// 本地中转弹窗状态(避免直接修改props)
const localVisible = ref(props.visible)
watch(() => props.visible, (newVal) => {
localVisible.value = newVal
})
// 表单实例 + 加载状态 + 表单数据
const formRef = ref(null)
const loading = ref(false) // 新增:防止重复提交
const form = ref({
username: '',
nickname: '',
password: '', // 修复:补充密码字段,与表单v-model绑定对应
status: 0 // 默认正常
})
// 表单验证规则
const rules = ref({
// 保留原有账号、昵称的规则,新增密码规则
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, max: 20, message: '密码长度需在6-20位之间', trigger: 'blur' }
],
username: [
{ required: true, message: '请输入用户账号', trigger: 'blur' },
{ min: 3, max: 20, message: '账号长度3-20字符', trigger: 'blur' }
],
nickname: [
{ required: true, message: '请输入用户昵称', trigger: 'blur' },
{ min: 2, max: 30, message: '昵称长度2-30字符', trigger: 'blur' }
]
})
// 提交新增(核心逻辑)
const handleSubmit = async () => {
// 1. 防止重复提交
if (loading.value) return;
// 2. 表单验证
if (!formRef.value) return;
try {
await formRef.value.validate()
} catch (err) {
ElMessage.warning('请完善必填项');
return;
}
// 3. 调用接口写入数据库
try {
loading.value = true; // 开启加载
const res = await user_api.userAdd(form.value);
// 新增成功判断:严格校验返回值,确保提示准确性
if (res?.code === 200) {
// 优化:优先使用后端返回消息,无消息则兜底友好提示
const successMsg = res.message || '新增用户成功!'
ElMessage.success(successMsg)
emit('refreshList'); // 通知父组件刷新列表
handleClose(); // 关闭弹窗
} else {
ElMessage.warning(res?.message || '新增用户失败,请重试')
}
} catch (err) {
ElMessage.error(`服务端异常:${err.message || '未知错误,新增失败'}`);
console.error('新增用户报错:', err);
} finally {
loading.value = false; // 关闭加载,无论成功失败都重置
}
}
// 关闭弹窗(重置表单)
const handleClose = () => {
if (formRef.value) formRef.value.resetFields(); // 重置验证状态
form.value = {
username: '',
nickname: '',
password: '', // 修复:清空密码字段
status: 0
}; // 清空数据
emit('close'); // 通知父组件关闭弹窗
localVisible.value = false;
}
</script>
<style scoped>
.dialog-footer { text-align: right; }
</style>
2.3 修改User.vue
1

2

3

4


新增成功

3 编辑功能
3.1 创建UserEdit.vue文件

java
<template>
<el-dialog
v-model="localVisible"
title="编辑用户"
width="400px"
@close="handleClose"
>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="80px"
style="max-width: 360px"
>
<!-- 账号通常不允许修改,disabled 可以设置为禁用 -->
<el-form-item label="账号" prop="username">
<el-input v-model="form.username" placeholder="请输入用户账号" />
</el-form-item>
<el-form-item label="昵称" prop="nickname">
<el-input v-model="form.nickname" placeholder="请输入用户昵称" />
</el-form-item>
<el-form-item label="状态">
<el-switch
v-model="form.status"
inline-prompt
style="--el-switch-on-color:#13ce66; --el-switch-off-color: #ff4949"
active-text="正常"
inactive-text="封号"
:active-value="0"
:inactive-value="1"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSubmit">保存</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import user_api from '@/api/user_api.js'
// 接收父组件传递的参数:弹窗显隐、编辑行数据
const props = defineProps({
visible: {
type: Boolean,
default: false
},
editData: { // 编辑的用户数据
type: Object,
default: () => ({})
}
})
// 向父组件传递事件:关闭弹窗、刷新列表
const emit = defineEmits(['close', 'refreshList'])
// 本地维护弹窗显隐状态(避免直接修改props)
const localVisible = ref(props.visible)
watch(() => props.visible, (newVal) => {
localVisible.value = newVal
})
// 表单实例
const formRef = ref(null)
// 表单数据(编辑时从父组件回显)
const form = ref({
id: '', // 新增id字段,用于后端定位编辑对象
username: '',
nickname: '',
status: 0
})
// 监听编辑数据变化,自动回显到表单
watch(() => props.editData, (newVal) => {
if (newVal && newVal.id) {
form.value = { ...newVal } // 深拷贝,避免修改原数据
}
}, { immediate: true })
// 表单验证规则(和新增保持一致)
const rules = ref({
username: [
{ required: true, message: '请输入用户账号', trigger: 'blur' },
{ min: 3, max: 20, message: '账号长度在 3 到 20 个字符', trigger: 'blur' }
],
nickname: [
{ required: true, message: '请输入用户昵称', trigger: 'blur' },
{ min: 2, max: 30, message: '昵称长度在 2 到 30 个字符', trigger: 'blur' }
]
})
// 提交编辑
const handleSubmit = async () => {
// 表单验证
try {
await formRef.value.validate()
} catch (error) {
ElMessage.warning('请完善表单必填项')
return
}
// 调用编辑接口
try {
const result = await user_api.userEdit(form.value)
// 编辑成功判断:优先判断接口返回码,保障提示准确性
if (result.code === 200) {
// 弹窗成功提示:优先使用接口返回的消息,无消息则使用兜底默认提示
const successMsg = result.message || '编辑用户成功!'
ElMessage.success(successMsg)
emit('refreshList') // 通知父组件刷新列表
handleClose() // 关闭编辑弹窗
} else {
// 接口返回非成功码,弹出失败提示
ElMessage.warning(result.message || '编辑用户失败,请重试')
}
} catch (error) {
// 网络异常/服务端报错,弹出错误提示
ElMessage.error('服务端异常,编辑用户失败')
console.error('编辑用户报错:', error)
}
}
// 关闭弹窗(重置表单+通知父组件)
const handleClose = () => {
formRef.value?.resetFields() // 重置表单验证状态
form.value = { id: '', username: '', nickname: '', status: 0 } // 清空表单
emit('close') // 通知父组件关闭弹窗
localVisible.value = false
}
</script>
<style scoped>
.dialog-footer {
text-align: right;
}
</style>
3.2 相应的修改User.vue代码

编辑成功

4 搜索框(实现模糊搜索)
4.1 创建UserSearch.vue文件

java
<template>
<el-form
ref="searchFormRef"
:model="searchForm"
inline
style="margin-bottom: 0"
>
<!-- 昵称搜索框 -->
<el-form-item label="昵称" prop="nickname">
<el-input
v-model="searchForm.nickname"
placeholder="请输入用户昵称"
clearable
style="width: 200px"
@keyup.enter="handleSearch"
/>
</el-form-item>
<!-- 操作按钮 -->
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</template>
<script setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
// 定义事件:向父组件传递搜索/重置指令
const emit = defineEmits(['search', 'reset'])
// 搜索表单数据
const searchForm = ref({
nickname: '' // 昵称关键词
})
// 表单实例(用于重置)
const searchFormRef = ref(null)
// 搜索按钮/回车触发
const handleSearch = () => {
// 传递搜索参数(深拷贝避免引用问题)
emit('search', { ...searchForm.value })
// 添加查询成功的提示消息
ElMessage({
type: 'success',
message: '查询成功!',
duration: 2000 // 可选:设置提示自动关闭时间,默认3000毫秒
})
// 简化写法(若无需自定义配置,可直接使用)
// ElMessage.success('查询成功!')
}
// 重置按钮触发
const handleReset = () => {
// 重置表单状态+清空数据
searchFormRef.value?.resetFields()
searchForm.value = { nickname: '' }
// 通知父组件重置搜索
emit('reset')
}
</script>
<style scoped>
/* 调整表单间距,避免和页面其他元素冲突 */
.el-form-item {
margin-bottom: 0;
margin-right: 10px;
}
</style>
4.2 修改后端 Controller 查询逻辑(核心)
代码调整
1、替换原有的 searchtext 参数为 nickname,精准接收前端传递的昵称关键词;
2、增加分页参数 current/size 的默认值,避免分页异常;
3、仅针对 nickname 执行模糊查询(like),且做空值校验,避免无效查询。
前后端参数对齐
1、前端 UserSearch 组件传递 nickname 参数 → 父组件 User.vue 接收后拼接分页参数 → user_api.js 传递到后端;
2、后端接收 nickname 参数后,通过 MyBatis Plus 的 LambdaQueryWrapper.like 实现模糊搜索。
java
package com.easy.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.easy.service.UserService;
import com.easy.bean.User;
import com.easy.util.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
//允许跨域访问
@CrossOrigin
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
UserService userService;
@PostMapping("/")
public Result add(@RequestBody User user) {
userService.save(user);
user = userService.getById(user.getId());
return Result.success("新增数据成功", user);
}
@PutMapping("/")
public Result edit(@RequestBody User user) {
userService.updateById(user);
user = userService.getById(user.getId());
return Result.success("编辑数据成功", user);
}
@DeleteMapping("/{id}")
public Result delete(@PathVariable int id) {
userService.removeById(id);
return Result.success("删除数据成功");
}
@GetMapping("/{id}")
public Result getById(@PathVariable int id) {
User user = userService.getById(id);
return Result.success("", user);
}
/**
* 分页查询 + 昵称模糊搜索
* @param page MyBatis Plus分页对象
* @param nickname 昵称搜索关键词(非必传)
* @param current 当前页(默认1)
* @param size 每页条数(默认10)
* @return 分页结果
*/
@GetMapping("/")
public Result getPage(Page page,
@RequestParam(value = "nickname", required = false) String nickname,
@RequestParam(value = "current", defaultValue = "1") Long current,
@RequestParam(value = "size", defaultValue = "10") Long size) {
// 设置分页参数
page.setCurrent(current);
page.setSize(size);
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
// 昵称非空时,执行模糊查询
if (nickname != null && !nickname.trim().isEmpty()) {
queryWrapper.like(User::getNickname, nickname.trim());
}
// 执行分页查询
userService.page(page, queryWrapper);
return Result.success("", page);
}
}
支持模糊查询

5 最新的User.vue文件
java
<script setup>
import {ref,onMounted} from 'vue'
import user_api from '@/api/user_api.js'
import { ElMessageBox,ElMessage } from 'element-plus'
// 引入新增用户组件
import UserAdd from './UserAdd.vue'
import UserEdit from './UserEdit.vue' // 新增:引入编辑组件
// 1. 引入搜索组件
import UserSearch from './UserSearch.vue'
const tableData = ref([]);
// 控制新增弹窗显隐
const addDialogVisible = ref(false);
const editDialogVisible = ref(false); // 新增:编辑弹窗显隐
const editRowData = ref({}); // 新增:存储要编辑的行数据
// 2. 新增:存储搜索参数
const searchParams = ref({
username: '',
nickname: ''
})
onMounted(function(){
showTableData();
});
// 3. 改造:支持传入搜索参数
const showTableData=async(searchParams = {})=>{
try {
// 合并分页参数 + 搜索参数
const params = {
current:1, // 可扩展分页,此处先固定
size:10,
...searchParams
};
const result = await user_api.userList(params);
tableData.value=result.data.records;
} catch (error) {
ElMessage.error('加载数据失败:' + error.message)
console.error('加载用户列表报错:', error)
}
}
// 4. 新增:处理搜索事件(接收子组件参数)
const handleSearch = (params) => {
searchParams.value = params;
showTableData(params);
}
// 5. 新增:处理重置事件
const handleReset = () => {
searchParams.value = { username: '', nickname: '' };
showTableData();
}
// 打开新增弹窗
const openAddDialog = () => {
addDialogVisible.value = true
}
// 新增:打开编辑弹窗(传递行数据)
const handleEdit = (index, row) => {
editRowData.value = { ...row }; // 深拷贝行数据,避免直接修改表格
editDialogVisible.value = true;
};
//执行删除按钮执行的代码
const handleDelete = (index, row) => {
//row就是当前行的数据
//index就是当前数据的下表
ElMessageBox.confirm('是否要删除该行数据?')
.then(async() => {
//点击执行删除操作
const result = await user_api.userDelete(row.id);
showTableData();
if(result.code==200 && result.message){
ElMessage({
message: result.message,
type: 'success',
})
}
if(result.code!=200){
ElMessage({
message: '系统异常',
type: 'warning',
})
}
}).catch(()=>{
ElMessage.error('服务端出现异常')
});
};
</script>
<template>
<!-- 调整布局:新增按钮 + 搜索组件 同行展示 -->
<div style="margin-bottom: 10px; display: flex; align-items: center; gap: 20px;">
<el-button type="primary" @click="openAddDialog">新增用户</el-button>
<!-- 2. 挂载搜索组件,绑定事件 -->
<UserSearch
@search="handleSearch"
@reset="handleReset"
/>
</div>
<el-table :data="tableData" border style="width: 100%">
<el-table-column prop="username" label="账号" width="180" />
<el-table-column prop="nickname" label="昵称" width="180" />
<el-table-column label="状态" >
<template #default="scope">
<el-switch
v-model="scope.row.status"
inline-prompt
style="--el-switch-on-color:#13ce66; --el-switch-off-color: #ff4949"
active-text="正常"
inactive-text="封号"
:active-value="0"
:inactive-value="1"
/>
</template>
</el-table-column>
<el-table-column prop="last_login_time" label="最近登录时间" />
<el-table-column label="操作">
<template #default="scope">
<el-button
size="small"
@click="handleEdit(scope.$index, scope.row)"
>
修改
</el-button>
<el-button
size="small"
type="danger"
@click="handleDelete(scope.$index, scope.row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 用户弹窗组件 -->
<UserAdd
:visible="addDialogVisible"
@close="addDialogVisible = false"
@refreshList="showTableData"
/>
<!-- 编辑用户弹窗组件 -->
<UserEdit
:visible="editDialogVisible"
:edit-data="editRowData"
@close="editDialogVisible = false"
@refreshList="showTableData"
/>
</template>
<style scoped></style>