前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >vue-element-admin整合spring-boot实现权限控制之用户管理篇

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

作者头像
用户3587585
发布2021-09-08 15:09:52
3.2K0
发布2021-09-08 15:09:52
举报
文章被收录于专栏:阿福谈Web编程阿福谈Web编程
引言

距离笔者上次利用vue-element-admin项目整合后台spring-boot项目打通前后端权限控制首页左侧菜单功能过去一个半月了。最近换了项目组,用的都是华为的自研新技术,系统比较复杂,项目上手门槛也比较高,所以这一个多月笔者因为加班太多,也没有太多时间开发自己的开源项目。

但是最近总算腾出时间实现了之前承诺要实现的功能。本文笔者将带领大家一起实现如何使用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

代码语言:javascript
复制
// 查询符合条件的用户数量
int queryUserCountsByCondition(@Param("userParam") QueryUserParam userParam);
// 分页查询
    List<User> queryPageUsersByCondition(@Param("startIndex") int startIndex,@Param("endIndex") int endIndex,
                                         @Param("userParam") QueryUserParam userParam);

UserMapper.xml

代码语言:javascript
复制
<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

代码语言:javascript
复制
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

代码语言:javascript
复制
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date regTime;

2.1.3 Controller层代码

UserController.java

代码语言:javascript
复制
@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

代码语言:javascript
复制
List<Role> getRolesByUid(Long uid);

RoleMapper.xml

代码语言:javascript
复制
<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

代码语言:javascript
复制
 @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

代码语言:javascript
复制
@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

代码语言:javascript
复制
export function queryUserInfosByPage(queryParam, pageParam) {
  return request({
    url: `pageQuery/users/${pageParam.currPage}/${pageParam.pageSize}`,
    method: 'post',
    data: queryParam
  })
}

src/api/role.js

代码语言:javascript
复制
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源码完成了用户管理组件编码,其源码如下:

代码语言:javascript
复制
<!--组件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

代码语言:javascript
复制
<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

代码语言:javascript
复制
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命令启动项目脚本。

控制台出现如下日志表示前端启动成功:

代码语言:javascript
复制
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界面功能。在笔者的下一篇文章中将尽快推出给用户分配角色、以及给角色分配页面路由资源等功能,敬请期待!

本文的前后端代码均已提交到个人gitee仓库,感兴趣的读者可自行克隆下来研究

前端代码链接:https://gitee.com/heshengfu1211/vue-element-admin.git

后端代码链接:https://gitee.com/heshengfu1211/blogserver.git

6 参考链接

【1】 https://element.eleme.cn/#/zh-CN/component/table(element-ui官网组件demo)

往精彩推荐

【1】实现基于用户角色的页面路由资源权限控制(后端篇)

【2】vue-element-admin整合SpringBoot实现动态渲染基于角色的菜单资源踩坑录(前后端整合篇)

【3】介绍一个开源博客项目VBlog并打包部署到已存在运行项目的Nginx服务器下

【4】SpringBoot项目集成阿里云对象存储服务实现文件上传

【5】改造jeecg-boot项目,解决启动报错,跑通开发环境!

写文不易,觉得对你有帮助的小伙伴读完请点亮下方的在看

---END---

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-08-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 阿福谈Web编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言
  • 1 谈谈需求的原型设计
  • 2 后端接口开发
  • 3 前端界面及按钮功能实现
  • 4 效果体验
  • 5 小结
  • 6 参考链接
  • 往精彩推荐
相关产品与服务
访问管理
访问管理(Cloud Access Management,CAM)可以帮助您安全、便捷地管理对腾讯云服务和资源的访问。您可以使用CAM创建子用户、用户组和角色,并通过策略控制其访问范围。CAM支持用户和角色SSO能力,您可以根据具体管理场景针对性设置企业内用户和腾讯云的互通能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档