vue-element-admin整合spring-boot实现权限控制之用户管理篇

0 引言

距离笔者上次利用vue-element-admin项目整合后台spring-boot项目打通前后端权限控制首页左侧菜单功能过去一个半月了。最近换了项目组,用的都是华为的自研新技术,而且华为的IT系统集成了非常多的自研系统,很长一段时间竟然让笔者感觉摸不清门路,尤其是以灵雀系统为代表的低代码平台,前段都不需要怎么写代码了,只需配置模型和对服务进行编排,然后页面绑定模型和服务就OK了。代码量是少了很多,但是入门门口却更高了。所以这一个多月笔者因为加班太多,也没有太多时间开发自己的开源项目。但是最近总算腾出时间实现了之前承诺要实现的功能。本文笔者将带领大家一起实现如何使用element-ui开源UI框架调用spring-boot项目实现后台接口实现分页查询用户信息功能及查看用户下的角色等功能,末尾还会展示页面效果。

注意:本文的功能实现在上一篇文章vue-element-admin整合SpringBoot实现动态渲染基于角色的菜单资源踩坑录(前后端整合篇)

1 谈谈需求的原型设计

在IT行业,几乎所有的开发都是基于一定的需求开发出来的,产品经理一般会在与客户对接后形成一个需求文档,需求文档里不仅有详细需求规格说明和描述, 更少不了原型设计出来的低保真高保真图。低保真图一般由产品尽力借助mockplussketch等原型设计工具来完成,而高保真则由专门的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片段--><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><!--分页用户查询sql--><select id="queryPageUsersByCondition" resultType="org.sang.pojo.User">select t.id,t.username,t.nickname,t.enabled,t.email,t.regTimefrom(select id, username,nickname,enabled,email,regTimefrom 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 roleNameFROM roles rwhere r.id in(select ridfrom roles_userwhere uid = #{uid,jdbcType=BIGINT})</select>

2.2.2 Service层代码

RoleService.java

    @Autowiredprivate RolesMapper rolesMapper;public List<Role> getRolesByUid(Long uid){List<Role> roles = rolesMapper.getRolesByUid(uid);// 按ID升序排列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-adminsrc/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+':'+startSecondsconst 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+':'+endSecondsreturn {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.searchFormconst 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.datathis.totalCounts = pageData.totalRowsthis.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 = valthis.submitForm('searchForm')},// 当前页改变事件  handleCurrentChange(val){console.log(val);this.currentPage = valthis.submitForm('searchForm')},// 打开查询用户已有角色对话框事件openQueryDialog(row) {console.log(row)this.queryDialogVisible = trueconst uid = row.idgetRolesByUid(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 = rowthis.$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 SwitchRoles from './components/SwitchRoles'
import UserManage from './components/UserManage.vue'export default {name: 'PagePermission',components: { UserManage },methods: {// handleRolesChange() {//   this.$router.push({ path: '/permission/index' })// }}
}
</script>

这里需要在权限管理入口中加入用户管理组件,并屏蔽掉原来的角色切换组件

3.4 修改路由数组

src/router/index.js

export const asyncRoutes = [{id: '15',path: '/permission',component: Layout,redirect: '/permission/page',alwaysShow: true, // will always show the root menuname: 'Permission',meta: {title: '权限管理',icon: 'lock'//  roles: ['admin', 'editor']},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'// if do not set roles, means: this page does not require 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:51App 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编程”,让我们一起在技术的路上共同成长!

vue-element-admin整合spring-boot实现权限控制之用户管理篇相关推荐

  1. 39 Spring Boot Shiro权限管理【从零开始学Spring Boot】

    [视频 & 交流平台] à SpringBoot视频 http://study.163.com/course/introduction.htm?courseId=1004329008& ...

  2. spring boot security 权限用postman测试_Spring Security(五):前后端权限控制详解

    文章回顾: Spring Security(一):整合JWT实现登录功能 Spring Security(二):获取用户权限菜单树 Spring Security(三):与Vue.js整合 Sprin ...

  3. Spring Boot Shiro 权限管理

    Spring Boot Shiro 权限管理 标签: springshiro 2016-01-14 23:44 94587人阅读 评论(60) 收藏 举报 本来是打算接着写关于数据库方面,集成MyBa ...

  4. (39.3) Spring Boot Shiro权限管理【从零开始学Spring Boot】

    在学习此小节之前您可能还需要学习: (39.1) Spring Boot Shiro权限管理[从零开始学Spring Boot] http://412887952-qq-com.iteye.com/b ...

  5. springboot 创建地址_使用 SpringBoot Admin监控Spring Boot 服务

    简介 SpringBoot-Amind是什么?Spring Boot Admin 是一个管理和监控 Spring Boot 应用程序的开源软件.,可监控的信息包含:应用状态.内存.线程.堆栈等等,比较 ...

  6. 高级版的 jvisualvm :Spring Boot Admin 监控 Spring Boot 微服务项目

    前奏:先说一下 Java VisualVM Java VisualVM 是一个能够监控 JVM 的 jdk 自带的图形化工具: 在 $JAVA_HOME/bin 目录下,可直接运行它. 要想监控远程服 ...

  7. Vue Element Admin 使用mock模块模拟数据

    Mock 数据是前端开发过程中必不可少的一环,是分离前后端开发的关键链路.通过预先跟服务器端约定好的接口,模拟请求数据甚至逻辑,能够让前端开发更加独立自主,不会被服务端的开发所阻塞. vue-elem ...

  8. vue+element+admin(初始化项目)

    2022.10.17我接触到了vue+element+admin的使用,首先要安装node.jshttps://nodejs.org/zh-cn/download/.和githttps://git-s ...

  9. vue element admin登录方式切换(密码登录或短信登录)

    显示结果: 具体代码可以根据vue element admin源码进行更改,下面是页面代码 <el-form ref="loginForm" :model="log ...

最新文章

  1. Spring IDE 1.2.4发布
  2. socket编程中的异常处理
  3. sublime-text-2相关快捷键
  4. 有关OCS监控软件安装在windows上, 服务端显示乱码的问题
  5. share-Nothing原理
  6. python量化交易策略实例_Python量化实例 – 基于股票的金融数据量化分析
  7. java char character_Java char 与 Character
  8. 唐山大地震 昨夜万人首映 尚雯婕演唱《23秒,32年》使观众情绪得以第二次温暖爆发...
  9. java excel 操作方式_java excel兩種操作方式
  10. 腾讯云— LAMP 架构个人实践分享
  11. Ubuntu下逻辑坏道解决方案
  12. 驾驶习惯也能识人?基于时空孪生神经网络的轨迹识别
  13. 什么是IDC?IDC数据中心什么意思?
  14. 解决电脑“自动修复 电脑未正确启动/无法修复你的电脑”
  15. 主祷文第二、第三条求什么
  16. 解析肖特基二极管的优缺点及应用
  17. Android各种版本概述
  18. SSM整合项目人事管理系统改(2)——需求分析和数据库表结构设计
  19. 如何在虚拟主机上开发PHP定时任务
  20. [nodemon] app crashed - waiting for file changes before starting...报错

热门文章

  1. matlab幼苗识别,基于MATLAB的植物幼苗识别
  2. 【原创】赋值法写基础解系中解向量
  3. 以nba球员数据学习聚类算法
  4. php 汉字、字母验证码 例子
  5. CAD填充无法修剪的解决办法
  6. 数据结构---线性表
  7. python一元二次方程组
  8. 设定快搜Caption时注意
  9. 来啊,一起来智障啊:国外大火游戏人类一败涂地究竟有多好玩?
  10. 这键盘得有磨损多厉害?网友:简直就是骨灰级程序员