vue-element-admin整合spring-boot实现权限控制之用户管理篇
0 引言
距离笔者上次利用vue-element-admin项目整合后台spring-boot项目打通前后端权限控制首页左侧菜单功能过去一个半月了。最近换了项目组,用的都是华为的自研新技术,而且华为的IT系统集成了非常多的自研系统,很长一段时间竟然让笔者感觉摸不清门路,尤其是以灵雀系统为代表的低代码平台,前段都不需要怎么写代码了,只需配置模型和对服务进行编排,然后页面绑定模型和服务就OK了。代码量是少了很多,但是入门门口却更高了。所以这一个多月笔者因为加班太多,也没有太多时间开发自己的开源项目。但是最近总算腾出时间实现了之前承诺要实现的功能。本文笔者将带领大家一起实现如何使用element-ui 开源UI框架调用spring-boot 项目实现后台接口实现分页查询用户信息功能及查看用户下的角色等功能,末尾还会展示页面效果。
注意:本文的功能实现在上一篇文章vue-element-admin整合SpringBoot实现动态渲染基于角色的菜单资源踩坑录(前后端整合篇)
1 谈谈需求的原型设计
在IT行业,几乎所有的开发都是基于一定的需求开发出来的,产品经理一般会在与客户对接后形成一个需求文档,需求文档里不仅有详细需求规格说明和描述, 更少不了原型设计出来的低保真和高保真图。低保真图一般由产品尽力借助mockplus 和sketch 等原型设计工具来完成,而高保真则由专门的UCD 人员来完成。UCD 是User-Centered Design 的缩写,翻译成中文就是:以用户为中心的设计。
首先我们根据原型设计图需求来完成后台的两个接口,分别是分页查询用户信息数据接口和根据用户ID查询用户角色列表。第一个接口对应前端UI 功能为点击左侧菜单权限管理下的用户管理时显示默认的分页查询用户信息列表,同时 还可以通过form表单查询用户列表 ;第二个接口对应点击每行用户数据操作栏中的查看已有角色链接时弹框显示选中用户已有的角色列表。 图 1 用户管理界面
?
图 2 点击查看已有角色链接弹框显示选中用户已有的角色列表
说明:由于笔者对于产品设计工具的使用并不熟练,因此使用了截取部分效果图作为原型图
2 后端接口开发
根据原型界面拆分的需求完成两个后台接口的开发,按照分层设计的思想完成两个接口的编码。
2.1 完成分页查询用户信息接口
2.1.1 Dao 层代码
UserMapper.java
int queryUserCountsByCondition(@Param("userParam") QueryUserParam userParam);
List<User> queryPageUsersByCondition(@Param("startIndex") int startIndex,@Param("endIndex") int endIndex,
@Param("userParam") QueryUserParam userParam);
UserMapper.xml
<select id="queryUserCountsByCondition" resultType="int">
select count(*) from user
<include refid="userConditionSql" />
</select>
<sql id="userConditionSql">
where enabled=1
<if test="userParam.username!=null and userParam.username!=''">
and username like concat('%',#{userParam.username,jdbcType=VARCHAR},'%')
</if>
<if test="userParam.nickname!=null and userParam.nickname!=''">
and nickname like concat('%',#{userParam.nickname,jdbcType=VARCHAR},'%')
</if>
<if test="userParam.email!=null and userParam.email!=''">
and email like concat('%',#{userParam.email,jdbcType=VARCHAR},'%')
</if>
<if test="userParam.regStartTime!=null and userParam.regStartTime!=''">
and regTime <![CDATA[>=]]> #{userParam.regStartTime}
</if>
<if test="userParam.regEndTime!=null and userParam.regEndTime!=''">
and regTime <![CDATA[<=]]> #{userParam.regEndTime}
</if>
</sql>
<select id="queryPageUsersByCondition" resultType="org.sang.pojo.User">
select t.id,t.username,t.nickname,t.enabled,t.email,t.regTime
from
(select id, username,nickname,enabled,email,regTime
from user
<include refid="userConditionSql" />
) t limit #{startIndex,jdbcType=INTEGER},#{endIndex,jdbcType=INTEGER}
</select>
2.1.2 Service 层代码
UserService.java
public RespBean queryPageUsersByCondition(PageVo<User> pageVo, QueryUserParam userParam){
logger.info("currPage={},pageSize={},queryUserParam={}",
pageVo.getCurrPage(),pageVo.getPageSize(), JSON.toJSON(userParam));
int totalRows = userMapper.queryUserCountsByCondition(userParam);
pageVo.setTotalRows(totalRows);
if(pageVo.getEndIndex()>totalRows){
pageVo.setEndIndex(totalRows);
}
pageVo.setTotalPage((totalRows/pageVo.getPageSize())+1);
List<User> pageUsers = userMapper.queryPageUsersByCondition(pageVo.getStartIndex(),pageVo.getEndIndex(),userParam);
pageVo.setResult(pageUsers);
RespBean respBean = new RespBean(200,"success");
respBean.setData(pageVo);
return respBean;
}
用户对象日期属性添加@JsonFormat 注解,方面前台日期以字符串的格式展示
User.java
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date regTime;
2.1.3 Controller 层代码
UserController.java
@RequestMapping(value = "/pageQuery/users/{currPage}/{pageSize}",method = RequestMethod.POST)
public RespBean queryPageUsersByCondition(@PathVariable("currPage") int currPage, @PathVariable("pageSize") int pageSize,
@RequestBody QueryUserParam userParam){
PageVo<User> pageVo = new PageVo<>(currPage,pageSize);
RespBean respBean = userService.queryPageUsersByCondition(pageVo,userParam);
return respBean;
}
2 .2 完成根据用户ID查询用户角色列表接口
2.2.1 Dao 层代码
RoleMapper.java
List<Role> getRolesByUid(Long uid);
RoleMapper
<select id="getRolesByUid" parameterType="java.lang.Long" resultType="org.sang.pojo.Role">
SELECT r.id,
r.role_code as roleCode,
r.role_name as roleName
FROM roles r
where r.id in
(select rid
from roles_user
where uid = #{uid,jdbcType=BIGINT}
)
</select>
2.2.2 Service 层代码
RoleService.java
@Autowired
private RolesMapper rolesMapper;
public List<Role> getRolesByUid(Long uid){
List<Role> roles = rolesMapper.getRolesByUid(uid);
if(roles.size()>1){
roles.sort(Comparator.comparing(Role::getId));
}
return roles;
}
2.2.3 Controller 层代码
RoleController.java
@GetMapping("/getUserRoles")
public RespBean getUserRoles(@RequestParam("uid") Long uid) {
logger.info("uid={}",uid);
List<Role> roles = roleService.getRolesByUid(uid);
RespBean respBean = new RespBean(200, "success");
respBean.setData(roles);
return respBean;
}
接口开发完成后,启动后台服务后就可以通过postman 对接口进行测试,之前的文章中有过很多关于如何使用postman 这款接口UI 工具对开发出来的API 进行测试, 这里笔者就不演示了。
3 前端界面及按钮功能实现
3.1 增加两个后台接口的API导出配置
src/api/user.js
export function queryUserInfosByPage(queryParam, pageParam) {
return request({
url: `pageQuery/users/${pageParam.currPage}/${pageParam.pageSize}`,
method: 'post',
data: queryParam
})
}
src/api/role.js
export function getRolesByUid(uid) {
return request({
url: `/role/getUserRoles?uid=${uid}`,
method: 'get'
})
}
3.2 完成用户管理vue组件编码
根据原型设计图,我们需要开发一个用户管理的组件,我们可以在我们的前端项目vue-element-admin 的src/views/permission 目录下新建一个UserManage.vue 文件。笔者参考了element-ui 官网的组件demo源码完成了用户管理组件编码,其源码如下:
<!--组件html模板-->
<template>
<div id="userAccoutTemplate">
<!--查询表单-->
<el-form :model="searchForm" label-width="120px" ref="searchForm" class="search-ruleForm">
<div class="form-line">
<el-form-item label="账号:" class="user-account" prop="username">
<el-input v-model="searchForm.username"></el-input>
</el-form-item>
<el-form-item label="用户昵称:" class="nickname" prop="nickname">
<el-input v-model="searchForm.nickname"></el-input>
</el-form-item>
<el-form-item label="邮箱:" class="email" prop="email">
<el-input v-model="searchForm.email"></el-input>
</el-form-item>
</div>
<div class="date-time-line form-line">
<!--日期时间范围组件-->
<el-form-item label="时间范围" class="timeRange" prop="timeRange">
<el-date-picker v-model="searchForm.dateTimes" type="datetimerange"
range-separator="至" start-placeholder="开始时间" end-placeholde="结束时间" align="right"
format="yyyy-MM-dd HH:mm:ss" value-format="yyyy-MM-dd HH:mm:ss" :picker-options="pickOptions">
</el-date-picker>
</el-form-item>
</div>
<div class="button-line">
<el-button type="primary" @click="submitForm('searchForm')">查询</el-button>
<el-button @click="resetForm('searchForm')">重置</el-button>
</div>
</el-form>
<!--数据列表-->
<el-table border :data="userAccounts" style="width: 100%">
<el-table-column prop="username" label="用户账号" width="200"></el-table-column>
<el-table-column prop="nickname" label="用户名称" width="200"></el-table-column>
<el-table-column prop="email" label="邮箱" width="250"></el-table-column>
<el-table-column prop="regTime" label="注册时间" width="250"></el-table-column>
<el-table-column width="350"
label="操作">
<template slot-scope="scope">
<el-button @click="openQueryDialog(scope.row)" type="text" size="small">查看已有角色</el-button>
<el-button type="text" @click="openEditDialog(scope.row)" size="small">分配角色</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件-->
<el-pagination @size-change="hadleSizeChange" @current-change="handleCurrentChange"
:current-page="currentPage" :page-sizes="pageSizes" :page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper" :total="totalCounts">
</el-pagination>
<!--用于显示用户角色列表的弹框-->
<el-dialog class="query-dialog" title="用户已有角色" :visible.sync="queryDialogVisible"
width="35%">
<el-table ref="qeuryDialogTable" :data="userRoles" highlight-current-row
@current-change="handleCurrentRowChange" style="width: 100%"
>
<el-table-column property="id" label="角色ID" width="80">
</el-table-column>
<el-table-column property="roleCode" label="角色编码" width="120">
</el-table-column>
<el-table-column property="roleName" label="角色名称" width="120">
</el-table-column>
</el-table>
<span slot="footer" class="dialog-footer">
<el-button @click="closeQueryDialog">取 消</el-button>
<el-button type="primary" @click="closeQueryDialog">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<!--javascript脚本-->
<script>
import {queryUserInfosByPage} from '@/api/user'
import {getRolesByUid} from '@/api/role'
import { Message } from 'element-ui'
export default {
// 数据模型
data() {
let startDate = new Date()
// 默认选中最近一周的时间
startDate.setTime(startDate.getTime() - 3600 * 1000 * 24 * 7)
const endDate =new Date()
const startYear = startDate.getFullYear()
const startMonth = startDate.getMonth()>8?''+(startDate.getMonth()+1):'0'+(startDate.getMonth()+1)
const startMonthDay = startDate.getDate()>9?''+startDate.getDate():'0'+startDate.getDate()
const startHours = startDate.getHours()>9?''+startDate.getHours():'0'+startDate.getHours()
const startMinutes = startDate.getMinutes()>9?''+startDate.getMinutes():'0'+startDate.getMinutes()
const startSeconds = startDate.getSeconds()>9?''+startDate.getSeconds():'0'+startDate.getSeconds()
const startDateTimeStr = startYear+'-'+startMonth+'-'+startMonthDay+' '+startHours+':'+startMinutes+':'+startSeconds
const endYear = endDate.getFullYear()
const endMonth = endDate.getMonth()>8?''+(endDate.getMonth()+1):'0'+(endDate.getMonth()+1)
const endMonthDay = endDate.getDate()>9?''+endDate.getDate():'0'+endDate.getDate()
const endHours = endDate.getHours()>9?''+endDate.getHours():'0'+endDate.getHours()
const endMinutes = endDate.getMinutes()>9?''+endDate.getMinutes():'0'+endDate.getMinutes()
const endSeconds = endDate.getSeconds()>9?''+endDate.getSeconds():'0'+endDate.getSeconds()
const endDateTimeStr = endYear+'-'+endMonth+'-'+endMonthDay+' '+endHours+':'+endMinutes+':'+endSeconds
return {
searchForm: {
username: '',
nickname: '',
email: '',
dateTimes: [startDateTimeStr,endDateTimeStr]
},
userAccounts: [{ id: 1, username: 'zhangsan', nickname: '张三', email: 'zhangsan2021@163.com', regTime: '2021-06-20 23:58:48' },
{ id: 2, username: 'heshengfu', nickname: '程序员阿福', email: 'heshengfu2018@163.com', regTime: '2021-06-21 00:00:13' },
{ id: 3, username: 'lisi', nickname: '李四', email: 'lisi1998@163.com', regTime: '2021-08-04 00:45:38' }],
pickOptions: {
shortcuts: [{
text: '最近一周',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
picker.$emit('pick', [start, end]);
}
}]
},
currentPage: 1,
pageSizes: [10,20,50,100,500],
pageSize: 10,
totalCounts: 120,
queryDialogVisible: false,
currentRoleRow: 0,
userRoles: [
{id:1,roleCode:'admin',roleName:'管理员'},
{id:2,roleCode:'user',roleName:'普通用户'}
]
}
},
methods: {
// 提交查询表单
submitForm(formName) {
console.log(formName)
const formData = this.searchForm
const queyParam = {
username: formData.username,
nickname: formData.nickname,
email: formData.email,
regStartTime: formData.dateTimes[0],
regEndTime: formData.dateTimes[1]
}
console.log(queyParam)
// 分页参数
const pageParam = {
currPage: this.currentPage,
pageSize: this.pageSize
}
console.log(pageParam)
// 发送axios请求查询用户数据
queryUserInfosByPage(queyParam, pageParam).then(res=>{
if(res.status===200 && res.data.status===200){
const pageData = res.data.data
this.totalCounts = pageData.totalRows
this.userAccounts = pageData.result
}else{
Message.error('queryUserInfosByPage, status:'+res.data.status+', message:'+res.data.msg)
}
}).catch(err=>{
Message.error(err)
})
},
// 表单重置时间
resetForm(formName) {
this.$refs[formName].resetFields()
},
// 分页条数改变事件
hadleSizeChange(val) {
console.log(val);
this.pageSize = val
this.submitForm('searchForm')
},
// 当前页改变事件
handleCurrentChange(val){
console.log(val);
this.currentPage = val
this.submitForm('searchForm')
},
// 打开查询用户已有角色对话框事件
openQueryDialog(row) {
console.log(row)
this.queryDialogVisible = true
const uid = row.id
getRolesByUid(uid).then(res=>{
if(res.status===200 && res.data.status===200){
this.userRoles = res.data.data
}else{
this.userRoles = []
Message.error('getRolesByUid, status:'+res.data.status+', message:'+res.data.msg)
}
}).catch(err=>{
console.error(err)
})
},
openEditDialog(row){
console.log(row)
},
closeQueryDialog(){
this.queryDialogVisible = false
},
// 当前行改变事件
handleCurrentRowChange(row){
this.currentRoleRow = row
this.$refs['qeuryDialogTable'].setCurrentRow(row)
}
}
}
</script>
<!--页面样式,通过调试模式在浏览器中调试出效果后复制过来-->
<style lang="scss" scoped>
.search-ruleForm{
width: 100%;
height: 180px;
.el-form-item__label {
text-align: right;
}
.form-line {
height: 32px;
width: 100%;
margin-bottom: 20px;
.el-form-item {
width: 32%;
float: left;
}
}
.button-line {
height: 40px;
width: 100%;
margin-top: 25px;
}
}
</style>
3.3 修改权限管理入口组件源码
src/views/page.vue
<template>
<div class="app-container">
<user-manage></user-manage>
</div>
</template>
<script>
import UserManage from './components/UserManage.vue'
export default {
name: 'PagePermission',
components: { UserManage },
methods: {
}
}
</script>
这里需要在权限管理入口中加入用户管理组件,并屏蔽掉原来的角色切换组件
3.4 修改路由数组
src/router/index.js
export const asyncRoutes = [
{
id: '15',
path: '/permission',
component: Layout,
redirect: '/permission/page',
alwaysShow: true,
name: 'Permission',
meta: {
title: '权限管理',
icon: 'lock'
},
children: [
{
id: '16',
path: 'page',
component: () => import('@/views/permission/page'),
name: 'PagePermission',
meta: {
title: '用户管理'
}
},
{
id: '17',
path: 'directive',
component: () => import('@/views/permission/directive'),
name: 'DirectivePermission',
meta: {
title: 'Directive Permission'
}
},
{
id: '18',
path: 'role',
component: () => import('@/views/permission/role'),
name: 'RolePermission',
meta: {
title: '角色管理',
roles: ['admin']
}
}
]
},
]
为了让权限控制菜单以中文的形式显示,这里修改了路由组件数据对应权限相关数据的title 字段数据
4 效果体验
在vue-element-admin 项目根目录下通过git bash 或者cmd 的方式打开控制台,执行npm run dev 命令启动项目脚本。
控制台出现如下日志表示前端启动成功:
98% after emitting CopyPlugin DONE Compiled successfully in 1072ms上午8:04:51
App running at:
- Local: http://localhost:3000/
- Network: http://192.168.1.235:3000/
在谷歌浏览器中输入http://localhost:3000/ 进入登录界面,登录成功后进入项目首页,然后点击左侧菜单栏中的权限管理->用户管理可以看到下面的界面效果图
? 图 3 用户管理界面效果图
点击事件范围选则框中的快捷选择最近三个月,然后点击查询按钮,可以看到界面显示了从后台数据库查询出来的用户信息数据,并按每页10条显示。用户也可以输入账号、用户昵称和邮箱等信息作为查询条件查询符合搜索条件的数据,也可以点击切换当前页和页条数,从而在界面上看到自己想要的数据。
? 图 4 显示form表单分页查询数据
点击每条用户信息记录操作栏中的查看已有角色链接弹出一个对话框显示用户已经分配的角色
? 图 5 查看用户已有角色
5 小结
本文紧接我的上一篇原创文章vue-element-admin整合SpringBoot实现动态渲染基于角色的菜单资源踩坑录(前后端整合篇)开发了自定义权限设计模块中的用户管理界面功能,涉及到分页查看用户信息和弹框显示用户已有角色等两项功能。功能虽少,但却能帮助读者快速掌握前后端分离开发的技能,也能快速掌握element-ui 这款UI框架开发出优美简约的Web界面功能。在笔者的下一篇文章中将尽快推出给用户分配角色、以及给角色分配页面路由资源等功能,敬请期待!
6 参考链接
element-ui官网组件demo
本人首发笔者的个人微信公众号“阿福谈Web编程”,欢迎读者朋友们在微信公众号里搜索“阿福谈Web编程”,让我们一起在技术的路上共同成长!
|