IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> vue-element-admin整合spring-boot实现权限控制之用户管理篇 -> 正文阅读

[Java知识库]vue-element-admin整合spring-boot实现权限控制之用户管理篇

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.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);
        // 按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+':'+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 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 menu
    name: '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: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编程”,让我们一起在技术的路上共同成长!

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-21 15:13:47  更:2021-08-21 15:14:30 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 9:43:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码