Parcourir la source

Merge branch 'master' of https://gitee.com/y_project/RuoYi-Cloud

 Conflicts:
	ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java
疯狂的狮子li il y a 5 ans
Parent
commit
8059655012
25 fichiers modifiés avec 1114 ajouts et 53 suppressions
  1. 1 1
      pom.xml
  2. 5 3
      ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java
  3. 11 8
      ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/PageDomain.java
  4. 62 0
      ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysRoleController.java
  5. 27 0
      ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java
  6. 15 2
      ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
  7. 35 2
      ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java
  8. 24 1
      ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
  9. 73 0
      ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java
  10. 65 0
      ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
  11. 35 0
      ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
  12. 44 0
      ruoyi-ui/src/api/system/role.js
  13. 17 0
      ruoyi-ui/src/api/system/user.js
  14. 18 0
      ruoyi-ui/src/assets/styles/ruoyi.scss
  15. 51 0
      ruoyi-ui/src/components/DictTag/index.vue
  16. 5 2
      ruoyi-ui/src/main.js
  17. 26 0
      ruoyi-ui/src/router/index.js
  18. 53 6
      ruoyi-ui/src/views/system/dict/data.vue
  19. 5 5
      ruoyi-ui/src/views/system/dict/index.vue
  20. 213 0
      ruoyi-ui/src/views/system/role/authUser.vue
  21. 30 8
      ruoyi-ui/src/views/system/role/index.vue
  22. 142 0
      ruoyi-ui/src/views/system/role/selectUser.vue
  23. 117 0
      ruoyi-ui/src/views/system/user/authRole.vue
  24. 39 14
      ruoyi-ui/src/views/system/user/index.vue
  25. 1 1
      ruoyi-ui/src/views/system/user/profile/userInfo.vue

+ 1 - 1
pom.xml

@@ -27,7 +27,7 @@
         <swagger.core.version>1.6.2</swagger.core.version>
         <tobato.version>1.26.5</tobato.version>
         <kaptcha.version>2.3.2</kaptcha.version>
-        <pagehelper.boot.version>1.3.0</pagehelper.boot.version>
+        <pagehelper.boot.version>1.3.1</pagehelper.boot.version>
         <druid.version>1.2.6</druid.version>
         <dynamic-ds.version>3.4.0</dynamic-ds.version>
         <commons.io.version>2.10.0</commons.io.version>

+ 5 - 3
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java

@@ -139,9 +139,11 @@ public class SysUser extends BaseEntity {
      */
     private Long[] postIds;
 
-    public SysUser(Long userId) {
-        this.userId = userId;
-    }
+    /**
+     * 角色ID
+     */
+    private Long roleId;
+
 
     public boolean isAdmin() {
         return isAdmin(this.userId);

+ 11 - 8
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/PageDomain.java

@@ -67,15 +67,18 @@ public class PageDomain
 
     public void setIsAsc(String isAsc)
     {
-        // 兼容前端排序类型
-        if ("ascending".equals(isAsc))
+        if (StringUtils.isNotEmpty(isAsc))
         {
-            isAsc = "asc";
+            // 兼容前端排序类型
+            if ("ascending".equals(isAsc))
+            {
+                isAsc = "asc";
+            }
+            else if ("descending".equals(isAsc))
+            {
+                isAsc = "desc";
+            }
+            this.isAsc = isAsc;
         }
-        else if ("descending".equals(isAsc))
-        {
-            isAsc = "desc";
-        }
-        this.isAsc = isAsc;
     }
 }

+ 62 - 0
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysRoleController.java

@@ -23,7 +23,10 @@ import com.ruoyi.common.log.annotation.Log;
 import com.ruoyi.common.log.enums.BusinessType;
 import com.ruoyi.common.security.annotation.PreAuthorize;
 import com.ruoyi.system.api.domain.SysRole;
+import com.ruoyi.system.api.domain.SysUser;
+import com.ruoyi.system.domain.SysUserRole;
 import com.ruoyi.system.service.ISysRoleService;
+import com.ruoyi.system.service.ISysUserService;
 
 /**
  * 角色信息
@@ -37,6 +40,9 @@ public class SysRoleController extends BaseController
     @Autowired
     private ISysRoleService roleService;
 
+    @Autowired
+    private ISysUserService userService;
+
     @PreAuthorize(hasPermi = "system:role:list")
     @GetMapping("/list")
     public TableDataInfo list(SysRole role)
@@ -153,4 +159,60 @@ public class SysRoleController extends BaseController
     {
         return AjaxResult.success(roleService.selectRoleAll());
     }
+    /**
+     * 查询已分配用户角色列表
+     */
+    @PreAuthorize(hasPermi = "system:role:list")
+    @GetMapping("/authUser/allocatedList")
+    public TableDataInfo allocatedList(SysUser user)
+    {
+        startPage();
+        List<SysUser> list = userService.selectAllocatedList(user);
+        return getDataTable(list);
+    }
+
+    /**
+     * 查询未分配用户角色列表
+     */
+    @PreAuthorize(hasPermi = "system:role:list")
+    @GetMapping("/authUser/unallocatedList")
+    public TableDataInfo unallocatedList(SysUser user)
+    {
+        startPage();
+        List<SysUser> list = userService.selectUnallocatedList(user);
+        return getDataTable(list);
+    }
+
+    /**
+     * 取消授权用户
+     */
+    @PreAuthorize(hasPermi = "system:role:edit")
+    @Log(title = "角色管理", businessType = BusinessType.GRANT)
+    @PutMapping("/authUser/cancel")
+    public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole)
+    {
+        return toAjax(roleService.deleteAuthUser(userRole));
+    }
+
+    /**
+     * 批量取消授权用户
+     */
+    @PreAuthorize(hasPermi = "system:role:edit")
+    @Log(title = "角色管理", businessType = BusinessType.GRANT)
+    @PutMapping("/authUser/cancelAll")
+    public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds)
+    {
+        return toAjax(roleService.deleteAuthUsers(roleId, userIds));
+    }
+
+    /**
+     * 批量选择用户授权
+     */
+    @PreAuthorize(hasPermi = "system:role:edit")
+    @Log(title = "角色管理", businessType = BusinessType.GRANT)
+    @PutMapping("/authUser/selectAll")
+    public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds)
+    {
+        return toAjax(roleService.insertAuthUsers(roleId, userIds));
+    }
 }

+ 27 - 0
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java

@@ -246,4 +246,31 @@ public class SysUserController extends BaseController
         user.setUpdateBy(SecurityUtils.getUsername());
         return toAjax(userService.updateUserStatus(user));
     }
+
+    /**
+     * 根据用户编号获取授权角色
+     */
+    @PreAuthorize(hasPermi = "system:user:query")
+    @GetMapping("/authRole/{userId}")
+    public AjaxResult authRole(@PathVariable("userId") Long userId)
+    {
+        AjaxResult ajax = AjaxResult.success();
+        SysUser user = userService.selectUserById(userId);
+        List<SysRole> roles = roleService.selectRolesByUserId(userId);
+        ajax.put("user", user);
+        ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
+        return ajax;
+    }
+
+    /**
+     * 用户授权角色
+     */
+    @PreAuthorize(hasPermi = "system:user:edit")
+    @Log(title = "用户管理", businessType = BusinessType.GRANT)
+    @PutMapping("/authRole")
+    public AjaxResult insertAuthRole(Long userId, Long[] roleIds)
+    {
+        userService.insertUserAuth(userId, roleIds);
+        return success();
+    }
 }

+ 15 - 2
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java

@@ -1,9 +1,7 @@
 package com.ruoyi.system.mapper;
 
 import java.util.List;
-
 import org.apache.ibatis.annotations.Param;
-
 import com.ruoyi.system.api.domain.SysUser;
 
 /**
@@ -20,6 +18,21 @@ public interface SysUserMapper
      * @return 用户信息集合信息
      */
     public List<SysUser> selectUserList(SysUser sysUser);
+    /**
+     * 根据条件分页查询未已配用户角色列表
+     * 
+     * @param user 用户信息
+     * @return 用户信息集合信息
+     */
+    public List<SysUser> selectAllocatedList(SysUser user);
+
+    /**
+     * 根据条件分页查询未分配用户角色列表
+     * 
+     * @param user 用户信息
+     * @return 用户信息集合信息
+     */
+    public List<SysUser> selectUnallocatedList(SysUser user);
 
     /**
      * 通过用户名查询用户

+ 35 - 2
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java

@@ -2,8 +2,8 @@ package com.ruoyi.system.service;
 
 import java.util.List;
 import java.util.Set;
-
 import com.ruoyi.system.api.domain.SysRole;
+import com.ruoyi.system.domain.SysUserRole;
 
 /**
  * 角色业务层
@@ -21,7 +21,15 @@ public interface ISysRoleService
     public List<SysRole> selectRoleList(SysRole role);
 
     /**
-     * 根据用户ID查询角色
+     * 根据用户ID查询角色列表
+     * 
+     * @param userId 用户ID
+     * @return 角色列表
+     */
+    public List<SysRole> selectRolesByUserId(Long userId);
+
+    /**
+     * 根据用户ID查询角色权限
      * 
      * @param userId 用户ID
      * @return 权限列表
@@ -129,4 +137,29 @@ public interface ISysRoleService
      * @return 结果
      */
     public int deleteRoleByIds(Long[] roleIds);
+    /**
+     * 取消授权用户角色
+     * 
+     * @param userRole 用户和角色关联信息
+     * @return 结果
+     */
+    public int deleteAuthUser(SysUserRole userRole);
+
+    /**
+     * 批量取消授权用户角色
+     * 
+     * @param roleId 角色ID
+     * @param userIds 需要取消授权的用户数据ID
+     * @return 结果
+     */
+    public int deleteAuthUsers(Long roleId, Long[] userIds);
+
+    /**
+     * 批量选择授权用户角色
+     * 
+     * @param roleId 角色ID
+     * @param userIds 需要删除的用户数据ID
+     * @return 结果
+     */
+    public int insertAuthUsers(Long roleId, Long[] userIds);
 }

+ 24 - 1
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java

@@ -1,7 +1,6 @@
 package com.ruoyi.system.service;
 
 import java.util.List;
-
 import com.ruoyi.system.api.domain.SysUser;
 
 /**
@@ -19,6 +18,22 @@ public interface ISysUserService
      */
     public List<SysUser> selectUserList(SysUser user);
 
+    /**
+     * 根据条件分页查询已分配用户角色列表
+     * 
+     * @param user 用户信息
+     * @return 用户信息集合信息
+     */
+    public List<SysUser> selectAllocatedList(SysUser user);
+
+    /**
+     * 根据条件分页查询未分配用户角色列表
+     * 
+     * @param user 用户信息
+     * @return 用户信息集合信息
+     */
+    public List<SysUser> selectUnallocatedList(SysUser user);
+
     /**
      * 通过用户名查询用户
      * 
@@ -97,6 +112,14 @@ public interface ISysUserService
      * @return 结果
      */
     public int updateUser(SysUser user);
+    
+    /**
+     * 用户授权角色
+     * 
+     * @param userId 用户ID
+     * @param roleIds 角色组
+     */
+    public void insertUserAuth(Long userId, Long[] roleIds);
 
     /**
      * 修改用户状态

+ 73 - 0
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java

@@ -16,6 +16,7 @@ import com.ruoyi.common.datascope.annotation.DataScope;
 import com.ruoyi.system.api.domain.SysRole;
 import com.ruoyi.system.domain.SysRoleDept;
 import com.ruoyi.system.domain.SysRoleMenu;
+import com.ruoyi.system.domain.SysUserRole;
 import com.ruoyi.system.mapper.SysRoleDeptMapper;
 import com.ruoyi.system.mapper.SysRoleMapper;
 import com.ruoyi.system.mapper.SysRoleMenuMapper;
@@ -55,6 +56,31 @@ public class SysRoleServiceImpl implements ISysRoleService
         return roleMapper.selectRoleList(role);
     }
 
+    /**
+     * 根据用户ID查询角色
+     * 
+     * @param userId 用户ID
+     * @return 角色列表
+     */
+    @Override
+    public List<SysRole> selectRolesByUserId(Long userId)
+    {
+        List<SysRole> userRoles = roleMapper.selectRolePermissionByUserId(userId);
+        List<SysRole> roles = selectRoleAll();
+        for (SysRole role : roles)
+        {
+            for (SysRole userRole : userRoles)
+            {
+                if (role.getRoleId().longValue() == userRole.getRoleId().longValue())
+                {
+                    role.setFlag(true);
+                    break;
+                }
+            }
+        }
+        return roles;
+    }
+
     /**
      * 根据用户ID查询权限
      * 
@@ -325,4 +351,51 @@ public class SysRoleServiceImpl implements ISysRoleService
         roleDeptMapper.deleteRoleDept(roleIds);
         return roleMapper.deleteRoleByIds(roleIds);
     }
+
+    /**
+     * 取消授权用户角色
+     * 
+     * @param userRole 用户和角色关联信息
+     * @return 结果
+     */
+    @Override
+    public int deleteAuthUser(SysUserRole userRole)
+    {
+        return userRoleMapper.deleteUserRoleInfo(userRole);
+    }
+
+    /**
+     * 批量取消授权用户角色
+     * 
+     * @param roleId 角色ID
+     * @param userIds 需要取消授权的用户数据ID
+     * @return 结果
+     */
+    @Override
+    public int deleteAuthUsers(Long roleId, Long[] userIds)
+    {
+        return userRoleMapper.deleteUserRoleInfos(roleId, userIds);
+    }
+
+    /**
+     * 批量选择授权用户角色
+     * 
+     * @param roleId 角色ID
+     * @param userIds 需要删除的用户数据ID
+     * @return 结果
+     */
+    @Override
+    public int insertAuthUsers(Long roleId, Long[] userIds)
+    {
+        // 新增用户与角色管理
+        List<SysUserRole> list = new ArrayList<SysUserRole>();
+        for (Long userId : userIds)
+        {
+            SysUserRole ur = new SysUserRole();
+            ur.setUserId(userId);
+            ur.setRoleId(roleId);
+            list.add(ur);
+        }
+        return userRoleMapper.batchUserRole(list);
+    }
 }

+ 65 - 0
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java

@@ -66,6 +66,32 @@ public class SysUserServiceImpl implements ISysUserService
         return userMapper.selectUserList(user);
     }
 
+    /**
+     * 根据条件分页查询已分配用户角色列表
+     * 
+     * @param user 用户信息
+     * @return 用户信息集合信息
+     */
+    @Override
+    @DataScope(deptAlias = "d", userAlias = "u")
+    public List<SysUser> selectAllocatedList(SysUser user)
+    {
+        return userMapper.selectAllocatedList(user);
+    }
+
+    /**
+     * 根据条件分页查询未分配用户角色列表
+     * 
+     * @param user 用户信息
+     * @return 用户信息集合信息
+     */
+    @Override
+    @DataScope(deptAlias = "d", userAlias = "u")
+    public List<SysUser> selectUnallocatedList(SysUser user)
+    {
+        return userMapper.selectUnallocatedList(user);
+    }
+
     /**
      * 通过用户名查询用户
      * 
@@ -242,6 +268,19 @@ public class SysUserServiceImpl implements ISysUserService
         return userMapper.updateUser(user);
     }
 
+    /**
+     * 用户授权角色
+     * 
+     * @param userId 用户ID
+     * @param roleIds 角色组
+     */
+    @Override
+    public void insertUserAuth(Long userId, Long[] roleIds)
+    {
+        userRoleMapper.deleteUserRoleByUserId(userId);
+        insertUserRole(userId, roleIds);
+    }
+
     /**
      * 修改用户状态
      * 
@@ -356,6 +395,32 @@ public class SysUserServiceImpl implements ISysUserService
         }
     }
 
+    /**
+     * 新增用户角色信息
+     * 
+     * @param userId 用户ID
+     * @param roleIds 角色组
+     */
+    public void insertUserRole(Long userId, Long[] roleIds)
+    {
+        if (StringUtils.isNotNull(roleIds))
+        {
+            // 新增用户与角色管理
+            List<SysUserRole> list = new ArrayList<SysUserRole>();
+            for (Long roleId : roleIds)
+            {
+                SysUserRole ur = new SysUserRole();
+                ur.setUserId(userId);
+                ur.setRoleId(roleId);
+                list.add(ur);
+            }
+            if (list.size() > 0)
+            {
+                userRoleMapper.batchUserRole(list);
+            }
+        }
+    }
+
     /**
      * 通过用户ID删除用户
      * 

+ 35 - 0
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml

@@ -81,6 +81,41 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 		${params.dataScope}
 	</select>
 	
+	<select id="selectAllocatedList" parameterType="SysUser" resultMap="SysUserResult">
+	    select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time
+	    from sys_user u
+			 left join sys_dept d on u.dept_id = d.dept_id
+			 left join sys_user_role ur on u.user_id = ur.user_id
+			 left join sys_role r on r.role_id = ur.role_id
+	    where u.del_flag = '0' and r.role_id = #{roleId}
+	    <if test="userName != null and userName != ''">
+			AND u.user_name like concat('%', #{userName}, '%')
+		</if>
+		<if test="phonenumber != null and phonenumber != ''">
+			AND u.phonenumber like concat('%', #{phonenumber}, '%')
+		</if>
+		<!-- 数据范围过滤 -->
+		${params.dataScope}
+	</select>
+	
+	<select id="selectUnallocatedList" parameterType="SysUser" resultMap="SysUserResult">
+	    select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time
+	    from sys_user u
+			 left join sys_dept d on u.dept_id = d.dept_id
+			 left join sys_user_role ur on u.user_id = ur.user_id
+			 left join sys_role r on r.role_id = ur.role_id
+	    where u.del_flag = '0' and (r.role_id != #{roleId} or r.role_id IS NULL)
+	    and u.user_id not in (select u.user_id from sys_user u inner join sys_user_role ur on u.user_id = ur.user_id and ur.role_id = #{roleId})
+	    <if test="userName != null and userName != ''">
+			AND u.user_name like concat('%', #{userName}, '%')
+		</if>
+		<if test="phonenumber != null and phonenumber != ''">
+			AND u.phonenumber like concat('%', #{phonenumber}, '%')
+		</if>
+		<!-- 数据范围过滤 -->
+		${params.dataScope}
+	</select>
+	
 	<select id="selectUserByUserName" parameterType="String" resultMap="SysUserResult">
 	    <include refid="selectUserVo"/>
 		where u.user_name = #{userName}

+ 44 - 0
ruoyi-ui/src/api/system/role.js

@@ -64,3 +64,47 @@ export function delRole(roleId) {
     method: 'delete'
   })
 }
+// 查询角色已授权用户列表
+export function allocatedUserList(query) {
+  return request({
+    url: '/system/role/authUser/allocatedList',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询角色未授权用户列表
+export function unallocatedUserList(query) {
+  return request({
+    url: '/system/role/authUser/unallocatedList',
+    method: 'get',
+    params: query
+  })
+}
+
+// 取消用户授权角色
+export function authUserCancel(data) {
+  return request({
+    url: '/system/role/authUser/cancel',
+    method: 'put',
+    data: data
+  })
+}
+
+// 批量取消用户授权角色
+export function authUserCancelAll(data) {
+  return request({
+    url: '/system/role/authUser/cancelAll',
+    method: 'put',
+    params: data
+  })
+}
+
+// 授权用户选择
+export function authUserSelectAll(data) {
+  return request({
+    url: '/system/role/authUser/selectAll',
+    method: 'put',
+    params: data
+  })
+}

+ 17 - 0
ruoyi-ui/src/api/system/user.js

@@ -108,3 +108,20 @@ export function uploadAvatar(data) {
     data: data
   })
 }
+
+// 查询授权角色
+export function getAuthRole(userId) {
+  return request({
+    url: '/system/user/authRole/' + userId,
+    method: 'get'
+  })
+}
+
+// 保存授权角色
+export function updateAuthRole(data) {
+  return request({
+    url: '/system/user/authRole',
+    method: 'put',
+    params: data
+  })
+}

+ 18 - 0
ruoyi-ui/src/assets/styles/ruoyi.scss

@@ -53,6 +53,13 @@
 	margin-left: 20px;
 }
 
+.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
+	font-family: inherit;
+	font-weight: 500;
+	line-height: 1.1;
+	color: inherit;
+}
+
 .el-dialog:not(.is-fullscreen){
 	margin-top: 6vh !important;
 }
@@ -120,6 +127,17 @@
 	width: inherit;
 }
 
+/** 表格更多操作下拉样式 */
+.el-table .el-dropdown-link {
+	cursor: pointer;
+	color: #1890ff;
+	margin-left: 5px;
+}
+
+.el-table .el-dropdown, .el-icon-arrow-down {
+	font-size: 12px;
+}
+
 .el-tree-node__content > .el-checkbox {
 	margin-right: 8px;
 }

+ 51 - 0
ruoyi-ui/src/components/DictTag/index.vue

@@ -0,0 +1,51 @@
+<template>
+  <div>
+    <template v-for="(item, index) in options">
+      <template v-if="values.includes(item.dictValue)">
+        <span
+          v-if="item.listClass == 'default' || item.listClass == ''"
+          :key="item.dictValue"
+          :index="index"
+          :class="item.cssClass"
+          >{{ item.dictLabel }}</span
+        >
+        <el-tag
+          v-else
+          :key="item.dictValue"
+          :index="index"
+          :type="item.listClass == 'primary' ? '' : item.listClass"
+          :class="item.cssClass"
+        >
+          {{ item.dictLabel }}
+        </el-tag>
+      </template>
+    </template>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "DictTag",
+  props: {
+    options: {
+      type: Array,
+      default: null,
+    },
+    value: [String, Array],
+  },
+  computed: {
+    values() {
+      if (this.value) {
+        return Array.isArray(this.value) ? this.value : [this.value];
+      } else {
+        return [];
+      }
+    },
+  },
+};
+</script>
+<style scoped>
+.el-tag + .el-tag {
+  margin-left: 10px;
+}
+</style>

+ 5 - 2
ruoyi-ui/src/main.js

@@ -19,9 +19,11 @@ import { getDicts } from "@/api/system/dict/data";
 import { getConfigKey } from "@/api/system/config";
 import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi";
 import Pagination from "@/components/Pagination";
-// 自定义表格工具扩展
+// 自定义表格工具组件
 import RightToolbar from "@/components/RightToolbar"
-// 头部标签插件
+// 字典标签组件
+import DictTag from '@/components/DictTag'
+// 头部标签组件
 import VueMeta from 'vue-meta'
 
 // 全局方法挂载
@@ -48,6 +50,7 @@ Vue.prototype.msgInfo = function (msg) {
 }
 
 // 全局组件挂载
+Vue.component('DictTag', DictTag)
 Vue.component('Pagination', Pagination)
 Vue.component('RightToolbar', RightToolbar)
 

+ 26 - 0
ruoyi-ui/src/router/index.js

@@ -80,6 +80,32 @@ export const constantRoutes = [
       }
     ]
   },
+  {
+    path: '/auth',
+    component: Layout,
+    hidden: true,
+    children: [
+      {
+        path: 'role/:userId(\\d+)',
+        component: (resolve) => require(['@/views/system/user/authRole'], resolve),
+        name: 'AuthRole',
+        meta: { title: '分配角色'}
+      }
+    ]
+  },
+  {
+    path: '/auth',
+    component: Layout,
+    hidden: true,
+    children: [
+      {
+        path: 'user/:roleId(\\d+)',
+        component: (resolve) => require(['@/views/system/role/authUser'], resolve),
+        name: 'AuthUser',
+        meta: { title: '分配用户'}
+      }
+    ]
+  },
   {
     path: '/dict',
     component: Layout,

+ 53 - 6
ruoyi-ui/src/views/system/dict/data.vue

@@ -85,10 +85,19 @@
     <el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="字典编码" align="center" prop="dictCode" />
-      <el-table-column label="字典标签" align="center" prop="dictLabel" />
+      <el-table-column label="字典标签" align="center" prop="dictLabel">
+        <template slot-scope="scope">
+          <span v-if="scope.row.listClass == '' || scope.row.listClass == 'default'">{{scope.row.dictLabel}}</span>
+          <el-tag v-else :type="scope.row.listClass == 'primary' ? '' : scope.row.listClass">{{scope.row.dictLabel}}</el-tag>
+        </template>
+      </el-table-column>
       <el-table-column label="字典键值" align="center" prop="dictValue" />
       <el-table-column label="字典排序" align="center" prop="dictSort" />
-      <el-table-column label="状态" align="center" prop="status" :formatter="statusFormat" />
+      <el-table-column label="状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :options="statusOptions" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
       <el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
       <el-table-column label="创建时间" align="center" prop="createTime" width="180">
         <template slot-scope="scope">
@@ -135,9 +144,22 @@
         <el-form-item label="数据键值" prop="dictValue">
           <el-input v-model="form.dictValue" placeholder="请输入数据键值" />
         </el-form-item>
+        <el-form-item label="样式属性" prop="cssClass">
+          <el-input v-model="form.cssClass" placeholder="请输入样式属性" />
+        </el-form-item>
         <el-form-item label="显示排序" prop="dictSort">
           <el-input-number v-model="form.dictSort" controls-position="right" :min="0" />
         </el-form-item>
+        <el-form-item label="回显样式" prop="listClass">
+          <el-select v-model="form.listClass">
+            <el-option
+              v-for="item in listClassOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            ></el-option>
+          </el-select>
+        </el-form-item>
         <el-form-item label="状态" prop="status">
           <el-radio-group v-model="form.status">
             <el-radio
@@ -187,6 +209,33 @@ export default {
       title: "",
       // 是否显示弹出层
       open: false,
+      // 数据标签回显样式
+      listClassOptions: [
+        {
+          value: "default",
+          label: "默认"
+        },
+        {
+          value: "primary",
+          label: "主要"
+        },
+        {
+          value: "success",
+          label: "成功"
+        },
+        {
+          value: "info",
+          label: "信息"
+        },
+        {
+          value: "warning",
+          label: "警告"
+        },
+        {
+          value: "danger",
+          label: "危险"
+        }
+      ],
       // 状态数据字典
       statusOptions: [],
       // 类型数据字典
@@ -247,10 +296,6 @@ export default {
         this.loading = false;
       });
     },
-    // 数据状态字典翻译
-    statusFormat(row, column) {
-      return this.selectDictLabel(this.statusOptions, row.status);
-    },
     // 取消按钮
     cancel() {
       this.open = false;
@@ -262,6 +307,8 @@ export default {
         dictCode: undefined,
         dictLabel: undefined,
         dictValue: undefined,
+        cssClass: undefined,
+        listClass: 'default',
         dictSort: 0,
         status: "0",
         remark: undefined

+ 5 - 5
ruoyi-ui/src/views/system/dict/index.vue

@@ -122,7 +122,11 @@
           </router-link>
         </template>
       </el-table-column>
-      <el-table-column label="状态" align="center" prop="status" :formatter="statusFormat" />
+      <el-table-column label="状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :options="statusOptions" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
       <el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
       <el-table-column label="创建时间" align="center" prop="createTime" width="180">
         <template slot-scope="scope">
@@ -254,10 +258,6 @@ export default {
         }
       );
     },
-    // 字典状态字典翻译
-    statusFormat(row, column) {
-      return this.selectDictLabel(this.statusOptions, row.status);
-    },
     // 取消按钮
     cancel() {
       this.open = false;

+ 213 - 0
ruoyi-ui/src/views/system/role/authUser.vue

@@ -0,0 +1,213 @@
+<template>
+  <div class="app-container">
+     <el-form :model="queryParams" ref="queryForm" v-show="showSearch" :inline="true">
+      <el-form-item label="用户名称" prop="userName">
+        <el-input
+          v-model="queryParams.userName"
+          placeholder="请输入用户名称"
+          clearable
+          size="small"
+          style="width: 240px"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="手机号码" prop="phonenumber">
+        <el-input
+          v-model="queryParams.phonenumber"
+          placeholder="请输入手机号码"
+          clearable
+          size="small"
+          style="width: 240px"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="openSelectUser"
+          v-hasPermi="['system:role:add']"
+        >添加用户</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-circle-close"
+          size="mini"
+          :disabled="multiple"
+          @click="cancelAuthUserAll"
+          v-hasPermi="['system:role:remove']"
+        >批量取消授权</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-close"
+          size="mini"
+          @click="handleClose"
+        >关闭</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
+      <el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
+      <el-table-column label="邮箱" prop="email" :show-overflow-tooltip="true" />
+      <el-table-column label="手机" prop="phonenumber" :show-overflow-tooltip="true" />
+      <el-table-column label="状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :options="statusOptions" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-circle-close"
+            @click="cancelAuthUser(scope.row)"
+            v-hasPermi="['system:role:remove']"
+          >取消授权</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+    <select-user ref="select" :roleId="queryParams.roleId" @ok="handleQuery" />
+  </div>
+</template>
+
+<script>
+import { allocatedUserList, authUserCancel, authUserCancelAll } from "@/api/system/role";
+import selectUser from "./selectUser";
+
+export default {
+  name: "AuthUser",
+  components: { selectUser },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中用户组
+      userIds: [],
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 用户表格数据
+      userList: [],
+      // 状态数据字典
+      statusOptions: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        roleId: undefined,
+        userName: undefined,
+        phonenumber: undefined
+      }
+    };
+  },
+  created() {
+    const roleId = this.$route.params && this.$route.params.roleId;
+    if (roleId) {
+      this.queryParams.roleId = roleId;
+      this.getList();
+      this.getDicts("sys_normal_disable").then(response => {
+        this.statusOptions = response.data;
+      });
+    }
+  },
+  methods: {
+    /** 查询授权用户列表 */
+    getList() {
+      this.loading = true;
+      allocatedUserList(this.queryParams).then(response => {
+          this.userList = response.rows;
+          this.total = response.total;
+          this.loading = false;
+        }
+      );
+    },
+    // 返回按钮
+    handleClose() {
+      this.$store.dispatch("tagsView/delView", this.$route);
+      this.$router.push({ path: "/system/role" });
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.userIds = selection.map(item => item.userId)
+      this.multiple = !selection.length
+    },
+    /** 打开授权用户表弹窗 */
+    openSelectUser() {
+      this.$refs.select.show();
+    },
+    /** 取消授权按钮操作 */
+    cancelAuthUser(row) {
+      const roleId = this.queryParams.roleId;
+      this.$confirm('确认要取消该用户"' + row.userName + '"角色吗?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        return authUserCancel({ userId: row.userId, roleId: roleId });
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("取消授权成功");
+      }).catch(() => {});
+    },
+    /** 批量取消授权按钮操作 */
+    cancelAuthUserAll(row) {
+      const roleId = this.queryParams.roleId;
+      const userIds = this.userIds.join(",");
+      this.$confirm('是否取消选中用户授权数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+      }).then(() => {
+          return authUserCancelAll({ roleId: roleId, userIds: userIds });
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("取消授权成功");
+      }).catch(() => {});
+    }
+  }
+};
+</script>

+ 30 - 8
ruoyi-ui/src/views/system/role/index.vue

@@ -123,7 +123,7 @@
         </template>
       </el-table-column>
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
-        <template slot-scope="scope">
+        <template slot-scope="scope" v-if="scope.row.roleId !== 1">
           <el-button
             size="mini"
             type="text"
@@ -131,13 +131,6 @@
             @click="handleUpdate(scope.row)"
             v-hasPermi="['system:role:edit']"
           >修改</el-button>
-          <el-button
-            size="mini"
-            type="text"
-            icon="el-icon-circle-check"
-            @click="handleDataScope(scope.row)"
-            v-hasPermi="['system:role:edit']"
-          >数据权限</el-button>
           <el-button
             size="mini"
             type="text"
@@ -145,6 +138,17 @@
             @click="handleDelete(scope.row)"
             v-hasPermi="['system:role:remove']"
           >删除</el-button>
+          <el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)">
+            <span class="el-dropdown-link">
+              <i class="el-icon-d-arrow-right el-icon--right"></i>更多
+            </span>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item command="handleDataScope" icon="el-icon-circle-check"
+                v-hasPermi="['system:role:edit']">数据权限</el-dropdown-item>
+              <el-dropdown-item command="handleAuthUser" icon="el-icon-user"
+                v-hasPermi="['system:role:edit']">分配用户</el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
         </template>
       </el-table-column>
     </el-table>
@@ -466,6 +470,19 @@ export default {
       this.single = selection.length!=1
       this.multiple = !selection.length
     },
+    // 更多操作触发
+    handleCommand(command, row) {
+      switch (command) {
+        case "handleDataScope":
+          this.handleDataScope(row);
+          break;
+        case "handleAuthUser":
+          this.handleAuthUser(row);
+          break;
+        default:
+          break;
+      }
+    },
     // 树权限(展开/折叠)
     handleCheckedTreeExpand(value, type) {
       if (type == 'menu') {
@@ -545,6 +562,11 @@ export default {
         this.title = "分配数据权限";
       });
     },
+    /** 分配用户操作 */
+    handleAuthUser: function(row) {
+      const roleId = row.roleId;
+      this.$router.push("/auth/user/" + roleId);
+    },
     /** 提交按钮 */
     submitForm: function() {
       this.$refs["form"].validate(valid => {

+ 142 - 0
ruoyi-ui/src/views/system/role/selectUser.vue

@@ -0,0 +1,142 @@
+<template>
+  <!-- 授权用户 -->
+  <el-dialog title="选择用户" :visible.sync="visible" width="800px" top="5vh" append-to-body>
+    <el-form :model="queryParams" ref="queryForm" :inline="true">
+      <el-form-item label="用户名称" prop="userName">
+        <el-input
+          v-model="queryParams.userName"
+          placeholder="请输入用户名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="手机号码" prop="phonenumber">
+        <el-input
+          v-model="queryParams.phonenumber"
+          placeholder="请输入手机号码"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+    <el-row>
+      <el-table @row-click="clickRow" ref="table" :data="userList" @selection-change="handleSelectionChange" height="260px">
+        <el-table-column type="selection" width="55"></el-table-column>
+        <el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
+        <el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
+        <el-table-column label="邮箱" prop="email" :show-overflow-tooltip="true" />
+        <el-table-column label="手机" prop="phonenumber" :show-overflow-tooltip="true" />
+        <el-table-column label="状态" align="center" prop="status">
+          <template slot-scope="scope">
+            <dict-tag :options="statusOptions" :value="scope.row.status"/>
+          </template>
+        </el-table-column>
+        <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+          <template slot-scope="scope">
+            <span>{{ parseTime(scope.row.createTime) }}</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination
+        v-show="total>0"
+        :total="total"
+        :page.sync="queryParams.pageNum"
+        :limit.sync="queryParams.pageSize"
+        @pagination="getList"
+      />
+    </el-row>
+    <div slot="footer" class="dialog-footer">
+      <el-button type="primary" @click="handleSelectUser">确 定</el-button>
+      <el-button @click="visible = false">取 消</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { unallocatedUserList, authUserSelectAll } from "@/api/system/role";
+export default {
+  props: {
+    // 角色编号
+    roleId: {
+      type: Number
+    }
+  },
+  data() {
+    return {
+      // 遮罩层
+      visible: false,
+      // 选中数组值
+      userIds: [],
+      // 总条数
+      total: 0,
+      // 未授权用户数据
+      userList: [],
+      // 状态数据字典
+      statusOptions: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        roleId: undefined,
+        userName: undefined,
+        phonenumber: undefined
+      }
+    };
+  },
+  created() {
+    this.getDicts("sys_normal_disable").then(response => {
+      this.statusOptions = response.data;
+    });
+  },
+  methods: {
+    // 显示弹框
+    show() {
+      this.queryParams.roleId = this.roleId;
+      this.getList();
+      this.visible = true;
+    },
+    clickRow(row) {
+      this.$refs.table.toggleRowSelection(row);
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.userIds = selection.map(item => item.userId);
+    },
+    // 查询表数据
+    getList() {
+      unallocatedUserList(this.queryParams).then(res => {
+        this.userList = res.rows;
+        this.total = res.total;
+      });
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    /** 选择授权用户操作 */
+    handleSelectUser() {
+      const roleId = this.queryParams.roleId;
+      const userIds = this.userIds.join(",");
+      authUserSelectAll({ roleId: roleId, userIds: userIds }).then(res => {
+        this.msgSuccess(res.msg);
+        if (res.code === 200) {
+          this.visible = false;
+          this.$emit("ok");
+        }
+      });
+    }
+  }
+};
+</script>

+ 117 - 0
ruoyi-ui/src/views/system/user/authRole.vue

@@ -0,0 +1,117 @@
+<template>
+  <div class="app-container">
+    <h4 class="form-header h4">基本信息</h4>
+    <el-form ref="form" :model="form" label-width="80px">
+      <el-row>
+        <el-col :span="8" :offset="2">
+          <el-form-item label="用户昵称" prop="nickName">
+            <el-input v-model="form.nickName" disabled />
+          </el-form-item>
+        </el-col>
+        <el-col :span="8" :offset="2">
+          <el-form-item label="登录账号" prop="phonenumber">
+            <el-input  v-model="form.userName" disabled />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <h4 class="form-header h4">角色信息</h4>
+    <el-table v-loading="loading" :row-key="getRowKey" @row-click="clickRow" ref="table" @selection-change="handleSelectionChange" :data="roles.slice((pageNum-1)*pageSize,pageNum*pageSize)">
+      <el-table-column label="序号" type="index" align="center">
+        <template slot-scope="scope">
+          <span>{{(pageNum - 1) * pageSize + scope.$index + 1}}</span>
+        </template>
+      </el-table-column>
+      <el-table-column type="selection" :reserve-selection="true" width="55"></el-table-column>
+      <el-table-column label="角色编号" align="center" prop="roleId" />
+      <el-table-column label="角色名称" align="center" prop="roleName" />
+      <el-table-column label="权限字符" align="center" prop="roleKey" />
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+    </el-table>
+    
+    <pagination v-show="total>0" :total="total" :page.sync="pageNum" :limit.sync="pageSize" />
+
+    <el-form label-width="100px">
+      <el-form-item style="text-align: center;margin-left:-120px;margin-top:30px;">
+        <el-button type="primary" @click="submitForm()">提交</el-button>
+        <el-button @click="close()">返回</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script>
+import { getAuthRole, updateAuthRole } from "@/api/system/user";
+
+export default {
+  name: "AuthRole",
+  data() {
+    return {
+       // 遮罩层
+      loading: true,
+      // 分页信息
+      total: 0,
+      pageNum: 1,
+      pageSize: 10,
+      // 选中角色编号
+      roleIds:[],
+      // 角色信息
+      roles: [],
+      // 用户信息
+      form: {}
+    };
+  },
+  created() {
+    const userId = this.$route.params && this.$route.params.userId;
+    if (userId) {
+      this.loading = true;
+      getAuthRole(userId).then((response) => {
+        this.form = response.user;
+        this.roles = response.roles;
+        this.total = this.roles.length;
+        this.$nextTick(() => {
+          this.roles.forEach((row) => {
+            if (row.flag) {
+              this.$refs.table.toggleRowSelection(row);
+            }
+          });
+        });
+        this.loading = false;
+      });
+    }
+  },
+  methods: {
+    /** 单击选中行数据 */
+    clickRow(row) {
+      this.$refs.table.toggleRowSelection(row);
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.roleIds = selection.map((item) => item.roleId);
+    },
+    // 保存选中的数据编号
+    getRowKey(row) {
+      return row.roleId;
+    },
+    /** 提交按钮 */
+    submitForm() {
+      const userId = this.form.userId;
+      const roleIds = this.roleIds.join(",");
+      updateAuthRole({ userId: userId, roleIds: roleIds }).then((response) => {
+        this.msgSuccess("授权成功");
+        this.close();
+      });
+    },
+    /** 关闭按钮 */
+    close() {
+      this.$store.dispatch("tagsView/delView", this.$route);
+      this.$router.push({ path: "/system/user" });
+    },
+  },
+};
+</script>

+ 39 - 14
ruoyi-ui/src/views/system/user/index.vue

@@ -166,7 +166,7 @@
             width="160"
             class-name="small-padding fixed-width"
           >
-            <template slot-scope="scope">
+            <template slot-scope="scope" v-if="scope.row.userId !== 1">
               <el-button
                 size="mini"
                 type="text"
@@ -175,20 +175,23 @@
                 v-hasPermi="['system:user:edit']"
               >修改</el-button>
               <el-button
-                v-if="scope.row.userId !== 1"
                 size="mini"
                 type="text"
                 icon="el-icon-delete"
                 @click="handleDelete(scope.row)"
                 v-hasPermi="['system:user:remove']"
               >删除</el-button>
-              <el-button
-                size="mini"
-                type="text"
-                icon="el-icon-key"
-                @click="handleResetPwd(scope.row)"
-                v-hasPermi="['system:user:resetPwd']"
-              >重置</el-button>
+              <el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)">
+                <span class="el-dropdown-link">
+                  <i class="el-icon-d-arrow-right el-icon--right"></i>更多
+                </span>
+                <el-dropdown-menu slot="dropdown">
+                  <el-dropdown-item command="handleResetPwd" icon="el-icon-key"
+                    v-hasPermi="['system:user:resetPwd']">重置密码</el-dropdown-item>
+                  <el-dropdown-item command="handleAuthRole" icon="el-icon-circle-check"
+                    v-hasPermi="['system:user:edit']">分配角色</el-dropdown-item>
+                </el-dropdown-menu>
+              </el-dropdown>
             </template>
           </el-table-column>
         </el-table>
@@ -209,7 +212,7 @@
         <el-row>
           <el-col :span="12">
             <el-form-item label="用户昵称" prop="nickName">
-              <el-input v-model="form.nickName" placeholder="请输入用户昵称" />
+              <el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
             </el-form-item>
           </el-col>
           <el-col :span="12">
@@ -233,12 +236,12 @@
         <el-row>
           <el-col :span="12">
             <el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
-              <el-input v-model="form.userName" placeholder="请输入用户名称" />
+              <el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30" />
             </el-form-item>
           </el-col>
           <el-col :span="12">
             <el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
-              <el-input v-model="form.password" placeholder="请输入用户密码" type="password" />
+              <el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" />
             </el-form-item>
           </el-col>
         </el-row>
@@ -437,7 +440,8 @@ export default {
           { required: true, message: "用户昵称不能为空", trigger: "blur" }
         ],
         password: [
-          { required: true, message: "用户密码不能为空", trigger: "blur" }
+          { required: true, message: "用户密码不能为空", trigger: "blur" },
+          { min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' }
         ],
         email: [
           {
@@ -557,6 +561,19 @@ export default {
       this.single = selection.length != 1;
       this.multiple = !selection.length;
     },
+    // 更多操作触发
+    handleCommand(command, row) {
+      switch (command) {
+        case "handleResetPwd":
+          this.handleResetPwd(row);
+          break;
+        case "handleAuthRole":
+          this.handleAuthRole(row);
+          break;
+        default:
+          break;
+      }
+    },
     /** 新增按钮操作 */
     handleAdd() {
       this.reset();
@@ -589,13 +606,21 @@ export default {
     handleResetPwd(row) {
       this.$prompt('请输入"' + row.userName + '"的新密码', "提示", {
         confirmButtonText: "确定",
-        cancelButtonText: "取消"
+        cancelButtonText: "取消",
+        closeOnClickModal: false,
+        inputPattern: /^.{5,20}$/,
+        inputErrorMessage: "用户密码长度必须介于 5 和 20 之间",
       }).then(({ value }) => {
           resetUserPwd(row.userId, value).then(response => {
             this.msgSuccess("修改成功,新密码是:" + value);
           });
         }).catch(() => {});
     },
+    /** 分配角色操作 */
+    handleAuthRole: function(row) {
+      const userId = row.userId;
+      this.$router.push("/auth/role/" + userId);
+    },
     /** 提交按钮 */
     submitForm: function() {
       this.$refs["form"].validate(valid => {

+ 1 - 1
ruoyi-ui/src/views/system/user/profile/userInfo.vue

@@ -1,7 +1,7 @@
 <template>
   <el-form ref="form" :model="user" :rules="rules" label-width="80px">
     <el-form-item label="用户昵称" prop="nickName">
-      <el-input v-model="user.nickName" />
+      <el-input v-model="user.nickName" maxlength="30" />
     </el-form-item> 
     <el-form-item label="手机号码" prop="phonenumber">
       <el-input v-model="user.phonenumber" maxlength="11" />