Solon + EasyQuery + ElementPlus 实现后台管理系统之 05-部门、角色、用户操作
AI 摘要
部门操作
后端 API
核心注意事项:
- 部门管理的后端 API,提供基础增删改查方法。
- 获取部门列表:可以根据部门名称条件进行模糊查询,返回分页结果。
- 删除部门:部门下有关联的用户,禁止删除。
编写部门控制器(cn.duozai.sadmin.controller.DeptController),提供基础方法:
/**
* 部门控制器
* @visduo
*/
@Valid
@Mapping("/dept")
@Controller
public class DeptController {
@Db
EasyEntityQuery easyEntityQuery;
/**
* 获取部门列表
* @visduo
*
* @param name 查询条件-部门名称
* @param pageIndex 查询条件-页码
* @param pageSize 查询条件-页大小
* @return 部门列表分页结果
*/
@Get
@Mapping("/list")
public ResponseResult list(@Param(required = false) String name,
@Param(required = false, defaultValue = "1") Integer pageIndex,
@Param(required = false, defaultValue = "10") Integer pageSize) {
EasyPageResult<DeptEntity> deptPageResult = easyEntityQuery.queryable(DeptEntity.class)
.where(d -> {
// 部门名称不为空时对部门名称进行模糊查询
d.name().like(StrUtil.isNotBlank(name), name);
})
// 查询分页数据
.toPageResult(pageIndex, pageSize);
return ResponseResult.success("查询成功", deptPageResult);
}
/**
* 添加部门
* @visduo
*
* @param name 部门名称
* @return 添加结果
*/
@Post
@Mapping("/add")
public ResponseResult add(@NotBlank(message = "部门名称不能为空") String name) {
DeptEntity deptEntity = new DeptEntity();
deptEntity.setName(name);
// 插入部门数据
easyEntityQuery.insertable(deptEntity).executeRows();
return ResponseResult.success("添加成功", null);
}
/**
* 修改部门
* @visduo
*
* @param id 部门id
* @param name 部门名称
* @return 修改结果
*/
@Put
@Mapping("/update/{id}")
public ResponseResult update(@Path int id,
@NotBlank(message = "部门名称不能为空") String name) {
DeptEntity deptEntity = new DeptEntity();
deptEntity.setId(id);
deptEntity.setName(name);
// 修改部门数据
easyEntityQuery.updatable(deptEntity).executeRows();
return ResponseResult.success("修改成功", null);
}
/**
* 删除部门
* @visduo
*
* @param id 部门id
* @return 删除结果
*/
@Delete
@Mapping("/delete/{id}")
public ResponseResult delete(@Path int id) {
// 该部门下有关联用户,禁止删除
long count = easyEntityQuery.queryable(UsersEntity.class)
.where(u -> {
u.deptId().eq(id);
}).count();
if (count > 0) {
return ResponseResult.failure("该部门下有关联用户,禁止删除", null);
}
// 删除部门数据
easyEntityQuery.deletable(DeptEntity.class)
.where(d -> {
d.id().eq(id);
}).executeRows();
return ResponseResult.success("删除成功", null);
}
/**
* 根据部门ID获取部门信息
* @visduo
*
* @param id 部门ID
* @return 部门信息
*/
@Get
@Mapping("/get/{id}")
public ResponseResult get(@Path int id) {
DeptEntity deptEntity = easyEntityQuery.queryable(DeptEntity.class)
.where(d -> {
d.id().eq(id);
}).firstOrNull();
return ResponseResult.success("查询成功", deptEntity);
}
}前端组件
核心注意事项:
- 部门管理的前端组件,提供基础增删改查表单。
- 增改表单,使用 ElementPlus 模态框进行展示。
- 修改部门表单需要实现表单数据回显,修改模态框展示时,需要根据修改的部门 ID 发送请求到后端获取最新的部门数据进行回显。
- 删除部门时,提供确认框,确认后发送请求进行删除。
AI 生成组件提示词:
基于ElementPlus,生成部门列表页面,要求如下:
1、提供搜索表单,搜索条件为部门名称name。
2、请求后端接口/dept/list获取部门分页列表数据并展示。
3、部门数据包含部门id、部门名称name,表格需要展示出来。
4、后端返回分页结果data、数据总数total,你需要自行计算其他分页所需参数,并显示分页组件。
5、点击添加按钮,弹出添加部门的模态框,并提供添加部门表单,表单项包括部门名称name。
6、新增部门表单提交前,需要进行表单数据校验。表单数据校验通过后,发送post请求给后端接口/dept/add。
7、点击修改按钮,弹出修改部门的模态框,并提供修改部门表单,表单要做数据回显,可修改项包括部门名称name。
8、修改部门的模态框出现后,需要发送get请求给后端接口/dept/get/xxx,获取到部门信息后进行回显。
9、修改部门表单提交前,需要进行表单数据校验。表单数据校验通过后,发送put请求给后端接口/dept/update/xxx。
10、新增部门和修改部门的模态框消失后,需要清除表单数据。
11、点击删除按钮,弹出删除确认提示,确定删除,发送delete请求给后端接口/dept/delete/xxx。编写部门组件(views/Dept.vue),提供部门管理视图:
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import qs from "qs";
// 分页数据
const tableData = ref([])
const total = ref(0)
// 搜索表单数据
const searchForm = reactive({
name: ''
})
// 分页参数
const pagination = reactive({
currentPage: 1,
pageSize: 10
})
// 添加部门对话框相关
const addDialogVisible = ref(false)
const addFormRef = ref()
const addFormData = reactive({
name: ''
})
const addFormRules = {
name: [
{ required: true, message: '请输入部门名称', trigger: 'blur' },
{ min: 1, max: 50, message: '长度应在1到50个字符之间', trigger: 'blur' }
]
}
// 编辑部门对话框相关
const editDialogVisible = ref(false)
const editFormRef = ref()
const editFormData = reactive({
id: '',
name: ''
})
const editFormRules = {
name: [
{ required: true, message: '请输入部门名称', trigger: 'blur' },
{ min: 1, max: 50, message: '长度应在1到50个字符之间', trigger: 'blur' }
]
}
// 获取部门列表
const fetchDeptList = () => {
const params = {
page: pagination.currentPage,
size: pagination.pageSize,
name: searchForm.name || undefined
}
axios.get('/dept/list', { params }).then((res) => {
tableData.value = res.data.data
total.value = res.data.total
})
}
// 搜索
const submitSearchForm = () => {
pagination.currentPage = 1
fetchDeptList()
}
// 重置搜索
const resetSearchForm = () => {
searchForm.name = ''
pagination.currentPage = 1
fetchDeptList()
}
// 分页变化
const handleSizeChange = (val) => {
pagination.pageSize = val
pagination.currentPage = 1
fetchDeptList()
}
const handleCurrentChange = (val) => {
pagination.currentPage = val
fetchDeptList()
}
// 显示添加部门对话框
const showAddDialog = () => {
addDialogVisible.value = true
}
// 提交添加表单
const submitAddForm = () => {
addFormRef.value.validate((valid) => {
if (valid) {
axios.post('/dept/add', qs.stringify(addFormData)).then((res) => {
ElMessage.success('添加成功')
addDialogVisible.value = false
resetAddForm()
fetchDeptList()
})
} else {
ElMessage.error('请正确填写表单信息')
return false
}
})
}
// 重置添加表单
const resetAddForm = () => {
addFormRef.value.resetFields()
}
// 显示编辑部门对话框
const showEditDialog = (row) => {
// 先设置ID,用于请求详细信息
editFormData.id = row.id
editDialogVisible.value = true
// 获取部门详细信息
axios.get(`/dept/get/${row.id}`).then((res) => {
editFormData.name = res.data.name
})
}
// 提交编辑表单
const submitEditForm = () => {
editFormRef.value.validate((valid) => {
if (valid) {
axios.put(`/dept/update/${editFormData.id}`, qs.stringify(editFormData)).then((res) => {
ElMessage.success('修改成功')
editDialogVisible.value = false
resetEditForm()
fetchDeptList()
})
} else {
ElMessage.error('请正确填写表单信息')
return false
}
})
}
// 重置编辑表单
const resetEditForm = () => {
editFormRef.value.resetFields()
}
// 删除部门
const handleDelete = (id) => {
ElMessageBox.confirm('确定要删除该部门吗?', '删除确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
axios.delete(`/dept/delete/${id}`)
.then((res) => {
ElMessage.success('删除成功')
fetchDeptList()
})
}).catch(() => {
// 用户取消删除
})
}
// 初始化加载数据
onMounted(() => {
fetchDeptList()
})
</script>
<template>
<el-main class="layout-main">
<!-- 搜索表单 -->
<el-card shadow="never" style="margin-bottom: 1rem;" :body-style="{paddingBottom: '2px'}">
<el-form :model="searchForm" inline>
<el-form-item label="部门名称">
<el-input v-model="searchForm.name" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitSearchForm">搜索</el-button>
<el-button @click="resetSearchForm">重置</el-button>
<el-button type="warning" @click="showAddDialog">添加部门</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 数据表格 -->
<el-card shadow="never">
<template #header>
<div class="card-header">
<span>部门列表</span>
</div>
</template>
<el-table :data="tableData" style="width: 100%" stripe>
<el-table-column prop="id" label="部门ID" width="80" />
<el-table-column prop="name" label="部门名称" />
<el-table-column label="操作">
<template #default="scope">
<el-button type="primary" size="small" @click="showEditDialog(scope.row)">编辑</el-button>
<el-button type="danger" size="small" @click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<el-pagination v-model:current-page="pagination.currentPage" v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]" :small="false" :disabled="false" :background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total" @size-change="handleSizeChange" @current-change="handleCurrentChange"
style="margin-top: 20px; justify-content: flex-end; display: flex;"
/>
</el-card>
<!-- 添加部门对话框 -->
<el-dialog v-model="addDialogVisible" title="添加部门" width="500px" @close="resetAddForm">
<el-form ref="addFormRef" :model="addFormData" :rules="addFormRules" label-width="80px" label-position="top">
<el-form-item label="部门名称" prop="name">
<el-input v-model="addFormData.name" placeholder="请输入部门名称"/>
</el-form-item>
</el-form>
<template #footer>
<div style="width: 100%; text-align: center">
<el-button @click="addDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitAddForm">确定</el-button>
</div>
</template>
</el-dialog>
<!-- 编辑部门对话框 -->
<el-dialog v-model="editDialogVisible" title="编辑部门" width="500px" @close="resetEditForm">
<el-form ref="editFormRef" :model="editFormData" :rules="editFormRules" label-width="80px" label-position="top">
<el-form-item label="部门名称" prop="name">
<el-input v-model="editFormData.name" placeholder="请输入部门名称"/>
</el-form-item>
</el-form>
<template #footer>
<div style="width: 100%; text-align: center">
<el-button @click="editDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitEditForm">确定</el-button>
</div>
</template>
</el-dialog>
</el-main>
</template>编写路由配置文件(router/index.js),提供部门管理路由信息:
{
// 部门管理
path: 'dept/list',
component: () => import('@/views/Dept.vue'),
},编写布局组件(views/Layouts.vue),提供部门管理菜单:
{
id: 13,
title: '部门管理',
path: '/dept/list'
},在浏览器中测试:
角色操作
后端 API
核心注意事项:
- 角色管理的后端 API,提供基础增删改查方法。
- 获取角色列表:可以根据角色名称条件、角色备注条件进行模糊查询,返回分页结果。
- 删除部门:角色下有关联的用户,禁止删除,角色 ID 为 1 即最高角色,禁止删除。
AI 生成控制器提示词:
基于Solon+EasyQuery,生成角色控制器,要求如下:
1、获取角色列表:可以根据角色名称name、角色备注remarks模糊查询,返回分页结果。
2、添加角色:前端提交角色名称name、角色备注remarks,使用注解进行数据校验,校验后插入数据到数据库。
3、修改角色:流程同添加角色。
4、删除角色:删除时校验角色下是否有关联用户,如有禁止删除,校验角色ID是否为1,如是禁止删除。
5、根据角色ID获取角色信息:根据角色ID查询角色信息后返回结果。
6、编码风格,严格参考部门控制器DeptController。编写角色控制器(cn.duozai.sadmin.controller.RoleController),提供基础方法:
/**
* 角色控制器
* @visduo
*/
@Valid
@Mapping("/role")
@Controller
public class RoleController {
@Db
EasyEntityQuery easyEntityQuery;
/**
* 获取角色列表
* @visduo
*
* @param name 查询条件-角色名称
* @param remarks 查询条件-角色备注
* @param pageIndex 查询条件-页码
* @param pageSize 查询条件-页大小
* @return 角色列表分页结果
*/
@Get
@Mapping("/list")
public ResponseResult list(@Param(required = false) String name,
@Param(required = false) String remarks,
@Param(required = false, defaultValue = "1") Integer pageIndex,
@Param(required = false, defaultValue = "10") Integer pageSize) {
EasyPageResult<RoleEntity> rolePageResult = easyEntityQuery.queryable(RoleEntity.class)
.where(r -> {
// 角色名称不为空时对角色名称进行模糊查询
r.name().like(StrUtil.isNotBlank(name), name);
// 角色备注不为空时对角色备注进行模糊查询
r.remarks().like(StrUtil.isNotBlank(remarks), remarks);
})
// 查询分页数据
.toPageResult(pageIndex, pageSize);
return ResponseResult.success("查询成功", rolePageResult);
}
/**
* 添加角色
* @visduo
*
* @param name 角色名称
* @param remarks 角色备注
* @return 添加结果
*/
@Post
@Mapping("/add")
public ResponseResult add(@NotBlank(message = "角色名称不能为空") String name,
@NotBlank(message = "角色备注不能为空") String remarks) {
RoleEntity roleEntity = new RoleEntity();
roleEntity.setName(name);
roleEntity.setRemarks(remarks);
// 插入角色数据
easyEntityQuery.insertable(roleEntity).executeRows();
return ResponseResult.success("添加成功", null);
}
/**
* 修改角色
* @visduo
*
* @param id 角色id
* @param name 角色名称
* @param remarks 角色备注
* @return 修改结果
*/
@Put
@Mapping("/update/{id}")
public ResponseResult update(@Path int id,
@NotBlank(message = "角色名称不能为空") String name,
@NotBlank(message = "角色备注不能为空") String remarks) {
RoleEntity roleEntity = new RoleEntity();
roleEntity.setId(id);
roleEntity.setName(name);
roleEntity.setRemarks(remarks);
// 修改角色数据
easyEntityQuery.updatable(roleEntity).executeRows();
return ResponseResult.success("修改成功", null);
}
/**
* 删除角色
* @visduo
*
* @param id 角色id
* @return 删除结果
*/
@Delete
@Mapping("/delete/{id}")
public ResponseResult delete(@Path int id) {
// 禁止删除ID为1的角色
if (id == 1) {
return ResponseResult.failure("默认角色禁止删除", null);
}
// 该角色下有关联用户,禁止删除
long count = easyEntityQuery.queryable(UsersEntity.class)
.where(u -> {
u.roleId().eq(id);
}).count();
if (count > 0) {
return ResponseResult.failure("该角色下有关联用户,禁止删除", null);
}
// 删除角色数据
easyEntityQuery.deletable(RoleEntity.class)
.where(r -> {
r.id().eq(id);
}).executeRows();
return ResponseResult.success("删除成功", null);
}
/**
* 根据角色ID获取角色信息
* @visduo
*
* @param id 角色ID
* @return 角色信息
*/
@Get
@Mapping("/get/{id}")
public ResponseResult get(@Path int id) {
RoleEntity roleEntity = easyEntityQuery.queryable(RoleEntity.class)
.where(r -> {
r.id().eq(id);
}).firstOrNull();
return ResponseResult.success("查询成功", roleEntity);
}
}前端组件
核心注意事项:
- 角色管理的前端组件,提供基础增删改查表单,表单相关注意事项和要求同前。
AI 生成组件提示词:
基于ElementPlus,生成角色列表页面,要求如下:
1、提供搜索表单,搜索条件为角色名称name、角色备注remarks。
2、请求后端接口/role/list获取角色分页列表数据并展示。
3、角色数据包含角色id、角色名称name、角色备注remarks,表格需要展示出来。
4、后端返回分页结果data、数据总数total,你需要自行计算其他分页所需参数,并显示分页组件。
5、点击添加按钮,弹出添加角色的模态框,并提供添加角色表单,表单项包括角色名称name、角色备注remarks。
6、新增角色表单提交前,需要进行表单数据校验。表单数据校验通过后,发送post请求给后端接口/role/add。
7、点击修改按钮,弹出修改角色的模态框,并提供修改角色表单,表单要做数据回显,可修改项包括角色名称name、角色备注remarks。
8、修改角色的模态框出现后,需要发送get请求给后端接口/role/get/xxx,获取到角色信息后进行回显。
9、修改角色表单提交前,需要进行表单数据校验。表单数据校验通过后,发送put请求给后端接口/role/update/xxx。
10、新增角色和修改角色的模态框消失后,需要清除表单数据。
11、点击删除按钮,弹出删除确认提示,确定删除,发送delete请求给后端接口/role/delete/xxx。
12、编码风格,严格参考部门组件Dept.vue。
13、注意:ID为1的角色是默认角色,需要禁用删除按钮。编写角色组件(views/Role.vue),提供角色管理视图:
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import qs from "qs";
// 分页数据
const tableData = ref([])
const total = ref(0)
// 搜索表单数据
const searchForm = reactive({
name: '',
remarks: ''
})
// 分页参数
const pagination = reactive({
currentPage: 1,
pageSize: 10
})
// 添加角色对话框相关
const addDialogVisible = ref(false)
const addFormRef = ref()
const addFormData = reactive({
name: '',
remarks: ''
})
const addFormRules = {
name: [
{ required: true, message: '请输入角色名称', trigger: 'blur' },
{ min: 1, max: 50, message: '长度应在1到50个字符之间', trigger: 'blur' }
],
remarks: [
{ required: true, message: '请输入角色备注', trigger: 'blur' },
{ min: 1, max: 200, message: '长度应在1到200个字符之间', trigger: 'blur' }
]
}
// 编辑角色对话框相关
const editDialogVisible = ref(false)
const editFormRef = ref()
const editFormData = reactive({
id: '',
name: '',
remarks: ''
})
const editFormRules = {
name: [
{ required: true, message: '请输入角色名称', trigger: 'blur' },
{ min: 1, max: 50, message: '长度应在1到50个字符之间', trigger: 'blur' }
],
remarks: [
{ required: true, message: '请输入角色备注', trigger: 'blur' },
{ min: 1, max: 200, message: '长度应在1到200个字符之间', trigger: 'blur' }
]
}
// 获取角色列表
const fetchRoleList = () => {
const params = {
page: pagination.currentPage,
size: pagination.pageSize,
name: searchForm.name || undefined,
remarks: searchForm.remarks || undefined
}
axios.get('/role/list', { params }).then((res) => {
tableData.value = res.data.data
total.value = res.data.total
})
}
// 搜索
const submitSearchForm = () => {
pagination.currentPage = 1
fetchRoleList()
}
// 重置搜索
const resetSearchForm = () => {
searchForm.name = ''
searchForm.remarks = ''
pagination.currentPage = 1
fetchRoleList()
}
// 分页变化
const handleSizeChange = (val) => {
pagination.pageSize = val
pagination.currentPage = 1
fetchRoleList()
}
const handleCurrentChange = (val) => {
pagination.currentPage = val
fetchRoleList()
}
// 显示添加角色对话框
const showAddDialog = () => {
addDialogVisible.value = true
}
// 提交添加表单
const submitAddForm = () => {
addFormRef.value.validate((valid) => {
if (valid) {
axios.post('/role/add', qs.stringify(addFormData)).then((res) => {
ElMessage.success('添加成功')
addDialogVisible.value = false
resetAddForm()
fetchRoleList()
})
} else {
ElMessage.error('请正确填写表单信息')
return false
}
})
}
// 重置添加表单
const resetAddForm = () => {
addFormRef.value.resetFields()
}
// 显示编辑角色对话框
const showEditDialog = (row) => {
// 先设置ID,用于请求详细信息
editFormData.id = row.id
editDialogVisible.value = true
// 获取角色详细信息
axios.get(`/role/get/${row.id}`).then((res) => {
editFormData.name = res.data.name
editFormData.remarks = res.data.remarks
})
}
// 提交编辑表单
const submitEditForm = () => {
editFormRef.value.validate((valid) => {
if (valid) {
axios.put(`/role/update/${editFormData.id}`, qs.stringify(editFormData)).then((res) => {
ElMessage.success('修改成功')
editDialogVisible.value = false
resetEditForm()
fetchRoleList()
})
} else {
ElMessage.error('请正确填写表单信息')
return false
}
})
}
// 重置编辑表单
const resetEditForm = () => {
editFormRef.value.resetFields()
}
// 删除角色
const handleDelete = (id) => {
ElMessageBox.confirm('确定要删除该角色吗?', '删除确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
axios.delete(`/role/delete/${id}`)
.then((res) => {
ElMessage.success('删除成功')
fetchRoleList()
})
}).catch(() => {
// 用户取消删除
})
}
// 初始化加载数据
onMounted(() => {
fetchRoleList()
})
</script>
<template>
<el-main class="layout-main">
<!-- 搜索表单 -->
<el-card shadow="never" style="margin-bottom: 1rem;" :body-style="{paddingBottom: '2px'}">
<el-form :model="searchForm" inline>
<el-form-item label="角色名称">
<el-input v-model="searchForm.name" />
</el-form-item>
<el-form-item label="角色备注">
<el-input v-model="searchForm.remarks" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitSearchForm">搜索</el-button>
<el-button @click="resetSearchForm">重置</el-button>
<el-button type="warning" @click="showAddDialog">添加角色</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 数据表格 -->
<el-card shadow="never">
<template #header>
<div class="card-header">
<span>角色列表</span>
</div>
</template>
<el-table :data="tableData" style="width: 100%" stripe>
<el-table-column prop="id" label="角色ID" width="80" />
<el-table-column prop="name" label="角色名称" />
<el-table-column prop="remarks" label="角色备注" />
<el-table-column label="操作">
<template #default="scope">
<el-button type="primary" size="small" @click="showEditDialog(scope.row)">编辑</el-button>
<el-button
type="danger"
size="small"
@click="handleDelete(scope.row.id)"
:disabled="scope.row.id === 1"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<el-pagination v-model:current-page="pagination.currentPage" v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]" :small="false" :disabled="false" :background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total" @size-change="handleSizeChange" @current-change="handleCurrentChange"
style="margin-top: 20px; justify-content: flex-end; display: flex;"
/>
</el-card>
<!-- 添加角色对话框 -->
<el-dialog v-model="addDialogVisible" title="添加角色" width="500px" @close="resetAddForm">
<el-form ref="addFormRef" :model="addFormData" :rules="addFormRules" label-width="80px" label-position="top">
<el-form-item label="角色名称" prop="name">
<el-input v-model="addFormData.name" placeholder="请输入角色名称"/>
</el-form-item>
<el-form-item label="角色备注" prop="remarks">
<el-input v-model="addFormData.remarks" placeholder="请输入角色备注" type="textarea"/>
</el-form-item>
</el-form>
<template #footer>
<div style="width: 100%; text-align: center">
<el-button @click="addDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitAddForm">确定</el-button>
</div>
</template>
</el-dialog>
<!-- 编辑角色对话框 -->
<el-dialog v-model="editDialogVisible" title="编辑角色" width="500px" @close="resetEditForm">
<el-form ref="editFormRef" :model="editFormData" :rules="editFormRules" label-width="80px" label-position="top">
<el-form-item label="角色名称" prop="name">
<el-input v-model="editFormData.name" placeholder="请输入角色名称"/>
</el-form-item>
<el-form-item label="角色备注" prop="remarks">
<el-input v-model="editFormData.remarks" placeholder="请输入角色备注" type="textarea"/>
</el-form-item>
</el-form>
<template #footer>
<div style="width: 100%; text-align: center">
<el-button @click="editDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitEditForm">确定</el-button>
</div>
</template>
</el-dialog>
</el-main>
</template>编写路由配置文件(router/index.js),提供角色管理路由信息:
{
// 角色管理
path: 'role/list',
component: () => import('@/views/Role.vue'),
},编写布局组件(views/Layouts.vue),提供角色管理菜单:
{
id: 12,
title: '角色管理',
path: '/role/list'
},在浏览器中测试:
用户操作
部门与角色后端 API 变动
在用户的基础增改查功能中,会涉及到:
- 根据关联部门和关联角色查询用户列表。
- 增改用户时选择用户关联的部门和关联的角色。
此时关联部门和关联角色以下拉框的形式存在,下拉框中的选项,应该从数据库中取出,在前端表单中渲染,由于部门、角色下拉框需要加载全量数据(无分页、无筛选)以支撑下拉选项的完整展示,所以需要新增两个查询接口。
编写部门控制器(cn.duozai.sadmin.controller.DeptController),提供获取部门列表(全量)方法:
/**
* 部门控制器
* @visduo
*/
@Valid
@Mapping("/dept")
@Controller
public class DeptController {
// ...
/**
* 获取部门列表
* @visduo
*
* @return 部门列表
*/
@Get
@Mapping("/optionList")
public ResponseResult optionList() {
List<DeptEntity> deptList = easyEntityQuery.queryable(DeptEntity.class)
.toList();
return ResponseResult.success("查询成功", deptList);
}
}编写角色控制器(cn.duozai.sadmin.controller.RoleController),提供获取角色列表(全量)方法:
/**
* 角色控制器
* @visduo
*/
@Valid
@Mapping("/role")
@Controller
public class RoleController {
// ...
/**
* 获取角色列表
* @visduo
*
* @return 角色列表
*/
@Get
@Mapping("/optionList")
public ResponseResult optionList() {
List<RoleEntity> roleList = easyEntityQuery.queryable(RoleEntity.class)
.toList();
return ResponseResult.success("查询成功", roleList);
}
}后端 API
核心注意事项:
- 用户管理的后端 API,提供基础增删改查方法。
- 获取用户列表:可以根据关联部门、关联角色、账户状态进行精确查询,根据账户账号、真实姓名、用户备注进行查询,返回分页结果。
- 修改用户:账户密码非必填项,如果修改了登录密码,需要重新生成盐值并加密得到密文密码,且让对应用户掉线,重新登录。禁止修改当前登录用户。禁止修改账户账号。
- 删除用户:用户 ID 为 1 即最高角色,禁止删除,删除前让对应用户掉线。
AI 生成控制器提示词:
基于Solon+EasyQuery,生成用户控制器,要求如下:
1、获取用户列表:可以根据关联部门ID-deptId、关联角色ID-roleId、账户状态status精确查询、根据账户账号username、真实姓名realname、用户备注remarks模糊查询,返回分页结果。
2、添加用户:前端提交关联部门ID-deptId、关联角色ID-roleId、账户账号username、账户密码password、真实姓名realname、用户备注remarks、账户状态status,使用注解进行数据校验,校验后插入数据到数据库。
3、添加用户时,账户账号username不能重复,前端传递的账户密码password是明文密码,你需要调用MD5盐值加密工具类生成盐值salts并加密得到密文密码。
4、修改用户:流程同添加用户,但是修改用户时不能修改用户账号username。
5、修改用户时,如果前端传递了密码,你需要重新生成盐值并进行加密得到新的密文密码,密码修改后,使用SaToken工具类将该用户踢下线。
6、删除角色:删除时校验用户ID是否为当前登录的用户ID,如是禁止删除,校验用户ID是否为1,如是禁止删除。
7、根据用户ID获取用户信息:根据用户ID查询用户信息后返回结果。
8、编码风格,严格参考部门控制器DeptController。编写用户控制器(cn.duozai.sadmin.controller.UserController),提供基础方法:
/**
* 用户控制器
* @visduo
*/
@Valid
@Mapping("/users")
@Controller
public class UsersController {
@Db
EasyEntityQuery easyEntityQuery;
/**
* 获取用户列表
* @visduo
*
* @param deptId 查询条件-关联部门ID
* @param roleId 查询条件-关联角色ID
* @param status 查询条件-账户状态
* @param username 查询条件-账户账号
* @param realname 查询条件-真实姓名
* @param remarks 查询条件-用户备注
* @param pageIndex 查询条件-页码
* @param pageSize 查询条件-页大小
* @return 用户列表分页结果
*/
@Get
@Mapping("/list")
public ResponseResult list(@Param(required = false) Integer deptId,
@Param(required = false) Integer roleId,
@Param(required = false) Integer status,
@Param(required = false) String username,
@Param(required = false) String realname,
@Param(required = false) String remarks,
@Param(required = false, defaultValue = "1") Integer pageIndex,
@Param(required = false, defaultValue = "10") Integer pageSize) {
EasyPageResult<UsersEntity> userPageResult = easyEntityQuery.queryable(UsersEntity.class)
.where(u -> {
// 关联部门ID不为空时进行精确查询
u.deptId().eq(deptId != null, deptId);
// 关联角色ID不为空时进行精确查询
u.roleId().eq(roleId != null, roleId);
// 账户状态不为空时进行精确查询
u.status().eq(status != null, status);
// 账户账号不为空时进行模糊查询
u.username().like(StrUtil.isNotBlank(username), username);
// 真实姓名不为空时进行模糊查询
u.realname().like(StrUtil.isNotBlank(realname), realname);
// 用户备注不为空时进行模糊查询
u.remarks().like(StrUtil.isNotBlank(remarks), remarks);
})
// 关联查询部门、角色实体
.include(UsersEntityProxy::dept)
.include(UsersEntityProxy::role)
// 查询分页数据
.toPageResult(pageIndex, pageSize);
return ResponseResult.success("查询成功", userPageResult);
}
/**
* 添加用户
* @visduo
*
* @param deptId 关联部门ID
* @param roleId 关联角色ID
* @param username 账户账号
* @param password 账户密码
* @param realname 真实姓名
* @param remarks 用户备注
* @param status 账户状态
* @return 添加结果
*/
@Post
@Mapping("/add")
public ResponseResult add(@NotNull(message = "关联部门ID不能为空") Integer deptId,
@NotNull(message = "关联角色ID不能为空") Integer roleId,
@NotBlank(message = "账户账号不能为空") String username,
@NotBlank(message = "账户密码不能为空") String password,
@NotBlank(message = "真实姓名不能为空") String realname,
@NotBlank(message = "用户备注不能为空") String remarks,
@NotNull(message = "账户状态不能为空") Integer status) {
// 检查账户账号是否已存在
long count = easyEntityQuery.queryable(UsersEntity.class)
.where(u -> {
u.username().eq(username);
}).count();
if (count > 0) {
return ResponseResult.failure("账户账号已存在", null);
}
UsersEntity usersEntity = new UsersEntity();
usersEntity.setDeptId(deptId);
usersEntity.setRoleId(roleId);
usersEntity.setUsername(username);
usersEntity.setRealname(realname);
usersEntity.setRemarks(remarks);
usersEntity.setStatus(status);
// 生成盐值并加密密码
String salts = MD5SaltsUtil.salts();
String md5Password = MD5SaltsUtil.md5(password, salts);
usersEntity.setPassword(md5Password);
usersEntity.setSalts(salts);
// 插入用户数据
easyEntityQuery.insertable(usersEntity).executeRows();
return ResponseResult.success("添加成功", null);
}
/**
* 修改用户
* @visduo
*
* @param id 用户id
* @param deptId 关联部门ID
* @param roleId 关联角色ID
* @param password 账户密码
* @param realname 真实姓名
* @param remarks 用户备注
* @param status 账户状态
* @return 修改结果
*/
@Put
@Mapping("/update/{id}")
public ResponseResult update(@Path int id,
@NotNull(message = "关联部门ID不能为空") Integer deptId,
@NotNull(message = "关联角色ID不能为空") Integer roleId,
@Param(required = false) String password,
@NotBlank(message = "真实姓名不能为空") String realname,
@NotBlank(message = "用户备注不能为空") String remarks,
@NotNull(message = "账户状态不能为空") Integer status) {
// 禁止修改当前登录的用户
if (StpUtil.getLoginIdAsInt() == id) {
return ResponseResult.failure("不能修改当前登录用户", null);
}
UsersEntity usersEntity = new UsersEntity();
usersEntity.setId(id);
usersEntity.setDeptId(deptId);
usersEntity.setRoleId(roleId);
usersEntity.setRealname(realname);
usersEntity.setRemarks(remarks);
usersEntity.setStatus(status);
// 如果传递了密码,则重新生成盐值并加密
if (StrUtil.isNotBlank(password)) {
String salts = MD5SaltsUtil.salts();
String md5Password = MD5SaltsUtil.md5(password, salts);
usersEntity.setPassword(md5Password);
usersEntity.setSalts(salts);
// 密码修改后,将该用户踢下线
StpUtil.kickout(id);
}
// 修改用户数据
easyEntityQuery.updatable(usersEntity).executeRows();
return ResponseResult.success("修改成功", null);
}
/**
* 删除用户
* @visduo
*
* @param id 用户id
* @return 删除结果
*/
@Delete
@Mapping("/delete/{id}")
public ResponseResult delete(@Path int id) {
// 禁止删除ID为1的用户
if (id == 1) {
return ResponseResult.failure("默认用户禁止删除", null);
}
// 禁止删除当前登录的用户
if (StpUtil.getLoginIdAsInt() == id) {
return ResponseResult.failure("不能删除当前登录用户", null);
}
// 删除前,将该用户踢下线
StpUtil.kickout(id);
// 删除用户数据
easyEntityQuery.deletable(UsersEntity.class)
.where(u -> {
u.id().eq(id);
}).executeRows();
return ResponseResult.success("删除成功", null);
}
/**
* 根据用户ID获取用户信息
* @visduo
*
* @param id 用户ID
* @return 用户信息
*/
@Get
@Mapping("/get/{id}")
public ResponseResult get(@Path int id) {
UsersEntity usersEntity = easyEntityQuery.queryable(UsersEntity.class)
.where(u -> {
u.id().eq(id);
})
.include(UsersEntityProxy::dept)
.include(UsersEntityProxy::role)
.firstOrNull();
return ResponseResult.success("查询成功", usersEntity);
}
}前端组件
核心注意事项:
- 用户管理的前端组件,提供基础增删改查表单,表单相关注意事项和要求同前。
- 增改查表单中涉及关联部门、关联角色,需要从后端 API 中获取对应数据并展示为下拉框。
AI 生成组件提示词:
基于ElementPlus,生成用户列表页面,要求如下:
1、编码风格,严格参考部门组件Dept.vue。
2、提供搜索表单,搜索条件为关联部门ID-deptId、关联角色ID-roleId、账户状态status、根据账户账号username、真实姓名realname、用户备注remarks。
3、搜索/新增/修改表单中,关联部门ID、关联角色ID显示下拉框,数据需要发送请求到后端接口/dept/optionList和/role/optionList获取。
4、获取用户分页列表:响应用户数据包含用户id、关联部门名称dept.name、关联角色role.name、账户账号username、真实姓名realname、用户备注remarks、账户状态status。
5、新增用户:表单项包括关联部门ID-deptId、关联角色ID-roleId、账户账号username、账户密码password、真实姓名realname、用户备注remarks、账户状态status。
6、修改用户:表单可修改项包括关联部门ID-deptId、关联角色ID-roleId、账户密码password(非必填)、真实姓名realname、用户备注remarks、账户状态status。
7、注意:ID为1的用户是默认用户,需要禁用删除按钮。ID为当前登录的用户ID,禁止修改和删除,需要禁用修改和删除按钮。编写用户组件(views/User.vue),提供用户管理视图:
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import qs from "qs";
import { useCurrentUserStore } from '@/stores/currentUser.js'
// 分页数据
const tableData = ref([])
const total = ref(0)
// 下拉选项数据
const deptOptions = ref([])
const roleOptions = ref([])
// 状态选项
const statusOptions = ref([
{ value: 0, label: '禁用' },
{ value: 1, label: '正常' }
])
// 搜索表单数据
const searchForm = reactive({
deptId: '',
roleId: '',
status: '',
username: '',
realname: '',
remarks: ''
})
// 分页参数
const pagination = reactive({
currentPage: 1,
pageSize: 10
})
// 添加用户对话框相关
const addDialogVisible = ref(false)
const addFormRef = ref()
const addFormData = reactive({
deptId: '',
roleId: '',
username: '',
password: '',
realname: '',
remarks: '',
status: 1
})
const addFormRules = {
deptId: [
{ required: true, message: '请选择关联部门', trigger: 'change' }
],
roleId: [
{ required: true, message: '请选择关联角色', trigger: 'change' }
],
username: [
{ required: true, message: '请输入账户账号', trigger: 'blur' },
{ min: 3, max: 20, message: '长度应在3到20个字符之间', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入账户密码', trigger: 'blur' },
{ min: 6, max: 20, message: '长度应在6到20个字符之间', trigger: 'blur' }
],
realname: [
{ required: true, message: '请输入真实姓名', trigger: 'blur' },
{ min: 1, max: 50, message: '长度应在1到50个字符之间', trigger: 'blur' }
],
remarks: [
{ required: true, message: '请输入用户备注', trigger: 'blur' },
{ min: 1, max: 200, message: '长度应在1到200个字符之间', trigger: 'blur' }
],
status: [
{ required: true, message: '请选择账户状态', trigger: 'change' }
]
}
// 编辑用户对话框相关
const editDialogVisible = ref(false)
const editFormRef = ref()
const editFormData = reactive({
id: '',
deptId: '',
roleId: '',
password: '',
realname: '',
remarks: '',
status: 1
})
const editFormRules = {
deptId: [
{ required: true, message: '请选择关联部门', trigger: 'change' }
],
roleId: [
{ required: true, message: '请选择关联角色', trigger: 'change' }
],
password: [
{ min: 6, max: 20, message: '长度应在6到20个字符之间', trigger: 'blur' }
],
realname: [
{ required: true, message: '请输入真实姓名', trigger: 'blur' },
{ min: 1, max: 50, message: '长度应在1到50个字符之间', trigger: 'blur' }
],
remarks: [
{ required: true, message: '请输入用户备注', trigger: 'blur' },
{ min: 1, max: 200, message: '长度应在1到200个字符之间', trigger: 'blur' }
],
status: [
{ required: true, message: '请选择账户状态', trigger: 'change' }
]
}
// 获取部门下拉选项
const fetchDeptOptions = () => {
axios.get('/dept/optionList').then((res) => {
deptOptions.value = res.data
})
}
// 获取角色下拉选项
const fetchRoleOptions = () => {
axios.get('/role/optionList').then((res) => {
roleOptions.value = res.data
})
}
// 获取用户列表
const fetchUserList = () => {
const params = {
page: pagination.currentPage,
size: pagination.pageSize,
deptId: searchForm.deptId || undefined,
roleId: searchForm.roleId || undefined,
status: searchForm.status !== '' ? searchForm.status : undefined,
username: searchForm.username || undefined,
realname: searchForm.realname || undefined,
remarks: searchForm.remarks || undefined
}
axios.get('/users/list', { params }).then((res) => {
tableData.value = res.data.data
total.value = res.data.total
})
}
// 搜索
const submitSearchForm = () => {
pagination.currentPage = 1
fetchUserList()
}
// 重置搜索
const resetSearchForm = () => {
searchForm.deptId = ''
searchForm.roleId = ''
searchForm.status = ''
searchForm.username = ''
searchForm.realname = ''
searchForm.remarks = ''
pagination.currentPage = 1
fetchUserList()
}
// 分页变化
const handleSizeChange = (val) => {
pagination.pageSize = val
pagination.currentPage = 1
fetchUserList()
}
const handleCurrentChange = (val) => {
pagination.currentPage = val
fetchUserList()
}
// 显示添加用户对话框
const showAddDialog = () => {
addDialogVisible.value = true
}
// 提交添加表单
const submitAddForm = () => {
addFormRef.value.validate((valid) => {
if (valid) {
axios.post('/users/add', qs.stringify(addFormData)).then((res) => {
ElMessage.success('添加成功')
addDialogVisible.value = false
resetAddForm()
fetchUserList()
})
} else {
ElMessage.error('请正确填写表单信息')
return false
}
})
}
// 重置添加表单
const resetAddForm = () => {
addFormRef.value.resetFields()
}
// 显示编辑用户对话框
const showEditDialog = (row) => {
// 先设置ID,用于请求详细信息
editFormData.id = row.id
editDialogVisible.value = true
// 获取用户详细信息
axios.get(`/users/get/${row.id}`).then((res) => {
editFormData.deptId = res.data.deptId
editFormData.roleId = res.data.roleId
editFormData.realname = res.data.realname
editFormData.remarks = res.data.remarks
editFormData.status = res.data.status
})
}
// 提交编辑表单
const submitEditForm = () => {
editFormRef.value.validate((valid) => {
if (valid) {
axios.put(`/users/update/${editFormData.id}`, qs.stringify(editFormData)).then((res) => {
ElMessage.success('修改成功')
editDialogVisible.value = false
resetEditForm()
fetchUserList()
})
} else {
ElMessage.error('请正确填写表单信息')
return false
}
})
}
// 重置编辑表单
const resetEditForm = () => {
editFormRef.value.resetFields()
}
// 删除用户
const handleDelete = (id) => {
ElMessageBox.confirm('确定要删除该用户吗?', '删除确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
axios.delete(`/users/delete/${id}`)
.then((res) => {
ElMessage.success('删除成功')
fetchUserList()
})
}).catch(() => {
// 用户取消删除
})
}
// 初始化加载数据
onMounted(() => {
fetchUserList()
fetchDeptOptions()
fetchRoleOptions()
})
</script>
<template>
<el-main class="layout-main">
<!-- 搜索表单 -->
<el-card shadow="never" style="margin-bottom: 1rem;" :body-style="{paddingBottom: '2px'}">
<el-form :model="searchForm" inline>
<el-form-item label="关联部门">
<el-select style="width: 150px" v-model="searchForm.deptId" placeholder="请选择关联部门" clearable>
<el-option v-for="item in deptOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="关联角色">
<el-select style="width: 150px" v-model="searchForm.roleId" placeholder="请选择关联角色" clearable>
<el-option v-for="item in roleOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="账户状态">
<el-select style="width: 150px" v-model="searchForm.status" placeholder="请选择账户状态" clearable>
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="账户账号">
<el-input v-model="searchForm.username" />
</el-form-item>
<el-form-item label="真实姓名">
<el-input v-model="searchForm.realname" />
</el-form-item>
<el-form-item label="用户备注">
<el-input v-model="searchForm.remarks" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitSearchForm">搜索</el-button>
<el-button @click="resetSearchForm">重置</el-button>
<el-button type="warning" @click="showAddDialog">添加用户</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 数据表格 -->
<el-card shadow="never">
<template #header>
<div class="card-header">
<span>用户列表</span>
</div>
</template>
<el-table :data="tableData" style="width: 100%" stripe>
<el-table-column prop="id" label="用户ID" width="80" />
<el-table-column prop="dept.name" label="关联部门" />
<el-table-column prop="role.name" label="关联角色" />
<el-table-column prop="username" label="账户账号" />
<el-table-column prop="realname" label="真实姓名" />
<el-table-column prop="remarks" label="用户备注" />
<el-table-column prop="status" label="账户状态">
<template #default="scope">
<el-tag :type="scope.row.status === 1 ? 'success' : 'danger'">
{{ scope.row.status === 1 ? '正常' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button type="primary" size="small" @click="showEditDialog(scope.row)" :disabled="scope.row.id === 1 || scope.row.id === useCurrentUserStore().currentUser.id">
编辑
</el-button>
<el-button type="danger" size="small" @click="handleDelete(scope.row.id)" :disabled="scope.row.id === 1">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<el-pagination v-model:current-page="pagination.currentPage" v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]" :small="false" :disabled="false" :background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total" @size-change="handleSizeChange" @current-change="handleCurrentChange"
style="margin-top: 20px; justify-content: flex-end; display: flex;"
/>
</el-card>
<!-- 添加用户对话框 -->
<el-dialog v-model="addDialogVisible" title="添加用户" width="500px" @close="resetAddForm">
<el-form ref="addFormRef" :model="addFormData" :rules="addFormRules" label-width="80px" label-position="top">
<el-form-item label="关联部门" prop="deptId">
<el-select v-model="addFormData.deptId" placeholder="请选择关联部门" style="width: 100%">
<el-option v-for="item in deptOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="关联角色" prop="roleId">
<el-select v-model="addFormData.roleId" placeholder="请选择关联角色" style="width: 100%">
<el-option v-for="item in roleOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="账户账号" prop="username">
<el-input v-model="addFormData.username" placeholder="请输入账户账号"/>
</el-form-item>
<el-form-item label="账户密码" prop="password">
<el-input v-model="addFormData.password" placeholder="请输入账户密码" type="password"/>
</el-form-item>
<el-form-item label="真实姓名" prop="realname">
<el-input v-model="addFormData.realname" placeholder="请输入真实姓名"/>
</el-form-item>
<el-form-item label="用户备注" prop="remarks">
<el-input v-model="addFormData.remarks" placeholder="请输入用户备注" type="textarea"/>
</el-form-item>
<el-form-item label="账户状态" prop="status">
<el-select v-model="addFormData.status" placeholder="请选择账户状态" style="width: 100%">
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div style="width: 100%; text-align: center">
<el-button @click="addDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitAddForm">确定</el-button>
</div>
</template>
</el-dialog>
<!-- 编辑用户对话框 -->
<el-dialog v-model="editDialogVisible" title="编辑用户" width="500px" @close="resetEditForm">
<el-form ref="editFormRef" :model="editFormData" :rules="editFormRules" label-width="80px" label-position="top">
<el-form-item label="关联部门" prop="deptId">
<el-select v-model="editFormData.deptId" placeholder="请选择关联部门" style="width: 100%">
<el-option v-for="item in deptOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="关联角色" prop="roleId">
<el-select v-model="editFormData.roleId" placeholder="请选择关联角色" style="width: 100%">
<el-option v-for="item in roleOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="账户密码" prop="password">
<el-input v-model="editFormData.password" placeholder="请输入账户密码(留空则不修改)" type="password"/>
</el-form-item>
<el-form-item label="真实姓名" prop="realname">
<el-input v-model="editFormData.realname" placeholder="请输入真实姓名"/>
</el-form-item>
<el-form-item label="用户备注" prop="remarks">
<el-input v-model="editFormData.remarks" placeholder="请输入用户备注" type="textarea"/>
</el-form-item>
<el-form-item label="账户状态" prop="status">
<el-select v-model="editFormData.status" placeholder="请选择账户状态" style="width: 100%">
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div style="width: 100%; text-align: center">
<el-button @click="editDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitEditForm">确定</el-button>
</div>
</template>
</el-dialog>
</el-main>
</template>编写路由配置文件(router/index.js),提供用户管理路由信息:
{
// 用户管理
path: 'users/list',
component: () => import('@/views/Users.vue'),
},编写布局组件(views/Layouts.vue),提供用户管理菜单:
{
id: 11,
title: '用户管理',
path: '/users/list'
},在浏览器中测试:
修改报错问题
前置后端 API、前端组件开发完成后,测试修改用户功能时,会报错:
报错的原因,是因为 EasyQuery 中默认 update(entity) 操作是更新对象全部列到数据库,不忽略 null 值,在修改用户时,禁止修改账户账号,因此没有传递账户账号,此时账户账号为 null,在数据库中账户账号字段却是 NOT NULL,因此会产生报错。
参考文档:http://www.easy-query.com/easy-query-doc/ability/update.html
解决该问题,可以通过修改修改策略 updateStrategy,忽略 null 值。
参考文档:http://www.easy-query.com/easy-query-doc/framework/config-option.html
编写 Solon 项目配置文件(classpath:/app.yml),配置 EasyQuery:
easy-query:
ds1: # 数据源-ds1
database: mysql # 数据库类型设置为mysql
print-sql: true # 打印SQL日志
update-strategy: only_not_null_columns # 更新策略,只更新NOT NULL的字段在浏览器中测试:
序列化问题
Solon 3.7.2 默认的序列化规则是 Snack4,Solon-SaToken 使用的序列化规则是 Snack3,在使用 @ONodeAttr 时容易导错包,导致序列化规则不生效。
在获取用户列表时,账户密码、账户盐值会被序列化返回给前端:
解决该问题,可以统一使用 Snack3 序列化方式,而不使用 Snack4 序列化方式。
编写 Maven 项目配置文件(pom.xml),加入 Snack3 依赖:
<dependencies>
<!-- Solon Web -->
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-web</artifactId>
<exclusions>
<!-- 排除Snack4依赖 -->
<exclusion>
<groupId>org.noear</groupId>
<artifactId>solon-serialization-snack4</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 重新引入Snack3序列化 -->
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-serialization-snack3</artifactId>
</dependency>
<!-- ... -->
</dependencies>在浏览器中测试:
文章中可能会存在些许错别字内容描述不完整、表述不准确、排版布局异常等问题,文章中提及的软件、依赖、框架等程序可能随其版本更新迭代而产生变化,文章中的相关代码片段、例图、文本等内容仅供参考。
如若转载,请注明出处:https://www.duox.dev/post/123.html