疯狂的狮子li 3 лет назад
Родитель
Сommit
2cc7ea4bd8
32 измененных файлов с 428 добавлено и 221 удалено
  1. 6 0
      ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysRole.java
  2. 5 0
      ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java
  3. 43 43
      ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/SecurityConstants.java
  4. 8 0
      ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/aspect/LogAspect.java
  5. 1 25
      ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysDeptController.java
  6. 2 0
      ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysProfileController.java
  7. 23 1
      ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysRoleController.java
  8. 11 1
      ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java
  9. 8 0
      ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java
  10. 8 0
      ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java
  11. 8 0
      ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java
  12. 28 21
      ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPermissionService.java
  13. 12 0
      ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java
  14. 22 1
      ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java
  15. 21 6
      ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPermissionServiceImpl.java
  16. 6 0
      ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml
  17. 0 16
      ruoyi-ui/src/api/system/dept.js
  18. 9 1
      ruoyi-ui/src/api/system/role.js
  19. 8 0
      ruoyi-ui/src/api/system/user.js
  20. 5 0
      ruoyi-ui/src/assets/styles/transition.scss
  21. 20 13
      ruoyi-ui/src/components/FileUpload/index.vue
  22. 7 1
      ruoyi-ui/src/components/ImagePreview/index.vue
  23. 38 30
      ruoyi-ui/src/components/ImageUpload/index.vue
  24. 7 3
      ruoyi-ui/src/layout/components/AppMain.vue
  25. 24 0
      ruoyi-ui/src/layout/components/IframeToggle/index.vue
  26. 40 20
      ruoyi-ui/src/layout/components/InnerLink/index.vue
  27. 6 0
      ruoyi-ui/src/layout/components/TagsView/index.vue
  28. 33 12
      ruoyi-ui/src/store/modules/tagsView.js
  29. 7 7
      ruoyi-ui/src/views/system/menu/index.vue
  30. 6 12
      ruoyi-ui/src/views/system/role/index.vue
  31. 5 7
      ruoyi-ui/src/views/system/user/index.vue
  32. 1 1
      ruoyi-ui/src/views/system/user/profile/index.vue

+ 6 - 0
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysRole.java

@@ -17,6 +17,7 @@ import lombok.NoArgsConstructor;
 import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Size;
+import java.util.Set;
 
 /**
  * 角色表 sys_role
@@ -114,6 +115,11 @@ public class SysRole extends BaseEntity {
     @TableField(exist = false)
     private Long[] deptIds;
 
+    /**
+     * 角色菜单权限
+     */
+    private Set<String> permissions;
+
     public SysRole(Long roleId) {
         this.roleId = roleId;
     }

+ 5 - 0
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java

@@ -16,6 +16,11 @@ public interface Constants {
      */
     String GBK = "GBK";
 
+    /**
+     * www主域
+     */
+    String WWW = "www.";
+
     /**
      * http请求
      */

+ 43 - 43
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/SecurityConstants.java

@@ -1,43 +1,43 @@
-package com.ruoyi.common.core.constant;
-
-/**
- * 权限相关通用常量
- *
- * @author Lion Li
- */
-public interface SecurityConstants {
-    /**
-     * 用户ID字段
-     */
-    String DETAILS_USER_ID = "user_id";
-
-    /**
-     * 用户名字段
-     */
-    String DETAILS_USERNAME = "username";
-
-    /**
-     * 授权信息字段
-     */
-    String AUTHORIZATION_HEADER = "authorization";
-
-    /**
-     * 请求来源
-     */
-    String FROM_SOURCE = "from-source";
-
-    /**
-     * 内部请求
-     */
-    String INNER = "inner";
-
-    /**
-     * 用户标识
-     */
-    String USER_KEY = "user_key";
-
-    /**
-     * 登录用户
-     */
-    String LOGIN_USER = "login_user";
-}
+package com.ruoyi.common.core.constant;
+
+/**
+ * 权限相关通用常量
+ *
+ * @author Lion Li
+ */
+public interface SecurityConstants {
+    /**
+     * 用户ID字段
+     */
+    String DETAILS_USER_ID = "user_id";
+
+    /**
+     * 用户名字段
+     */
+    String DETAILS_USERNAME = "username";
+
+    /**
+     * 授权信息字段
+     */
+    String AUTHORIZATION_HEADER = "authorization";
+
+    /**
+     * 请求来源
+     */
+    String FROM_SOURCE = "from-source";
+
+    /**
+     * 内部请求
+     */
+    String INNER = "inner";
+
+    /**
+     * 用户标识
+     */
+    String USER_KEY = "user_key";
+
+    /**
+     * 登录用户
+     */
+    String LOGIN_USER = "login_user";
+}

+ 8 - 0
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/aspect/LogAspect.java

@@ -151,6 +151,14 @@ public class LogAspect {
         return params.toString().trim();
     }
 
+    /**
+     * 忽略敏感属性
+     */
+    public PropertyPreExcludeFilter excludePropertyPreFilter()
+    {
+        return new PropertyPreExcludeFilter().addExcludes(EXCLUDE_PROPERTIES);
+    }
+
     /**
      * 判断是否需要过滤的对象。
      *

+ 1 - 25
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysDeptController.java

@@ -51,8 +51,7 @@ public class SysDeptController extends BaseController {
     @GetMapping("/list/exclude/{deptId}")
     public R<List<SysDept>> excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) {
         List<SysDept> depts = deptService.selectDeptList(new SysDept());
-        depts.removeIf(d -> d.getDeptId().equals(deptId)
-            || ArrayUtil.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""));
+        depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtil.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""));
         return R.ok(depts);
     }
 
@@ -68,29 +67,6 @@ public class SysDeptController extends BaseController {
         return R.ok(deptService.selectDeptById(deptId));
     }
 
-    /**
-     * 获取部门下拉树列表
-     */
-    @GetMapping("/treeselect")
-    public R<List<Tree<Long>>> treeselect(SysDept dept) {
-        List<SysDept> depts = deptService.selectDeptList(dept);
-        return R.ok(deptService.buildDeptTreeSelect(depts));
-    }
-
-    /**
-     * 加载对应角色部门列表树
-     *
-     * @param roleId 角色ID
-     */
-    @GetMapping(value = "/roleDeptTreeselect/{roleId}")
-    public R<Map<String, Object>> roleDeptTreeselect(@PathVariable("roleId") Long roleId) {
-        List<SysDept> depts = deptService.selectDeptList(new SysDept());
-        Map<String, Object> ajax = new HashMap<>();
-        ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId));
-        ajax.put("depts", deptService.buildDeptTreeSelect(depts));
-        return R.ok(ajax);
-    }
-
     /**
      * 新增部门
      */

+ 2 - 0
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysProfileController.java

@@ -74,6 +74,8 @@ public class SysProfileController extends BaseController {
         user.setUserId(LoginHelper.getUserId());
         user.setUserName(null);
         user.setPassword(null);
+        user.setAvatar(null);
+        user.setDeptId(null);
         if (userService.updateUserProfile(user) > 0) {
             return R.ok();
         }

+ 23 - 1
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysRoleController.java

@@ -10,10 +10,12 @@ import com.ruoyi.common.log.enums.BusinessType;
 import com.ruoyi.common.mybatis.core.page.PageQuery;
 import com.ruoyi.common.mybatis.core.page.TableDataInfo;
 import com.ruoyi.common.satoken.utils.LoginHelper;
+import com.ruoyi.system.api.domain.SysDept;
 import com.ruoyi.system.api.domain.SysRole;
 import com.ruoyi.system.api.domain.SysUser;
 import com.ruoyi.system.api.model.LoginUser;
 import com.ruoyi.system.domain.SysUserRole;
+import com.ruoyi.system.service.ISysDeptService;
 import com.ruoyi.system.service.ISysPermissionService;
 import com.ruoyi.system.service.ISysRoleService;
 import com.ruoyi.system.service.ISysUserService;
@@ -22,7 +24,9 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletResponse;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * 角色信息
@@ -37,6 +41,7 @@ public class SysRoleController extends BaseController {
 
     private final ISysRoleService roleService;
     private final ISysUserService userService;
+    private final ISysDeptService deptService;
     private final ISysPermissionService permissionService;
 
     /**
@@ -106,7 +111,9 @@ public class SysRoleController extends BaseController {
             LoginUser loginUser = LoginHelper.getLoginUser();
             Long userId = loginUser.getUserId();
             if (!LoginHelper.isAdmin(userId)) {
-                loginUser.setMenuPermission(permissionService.getMenuPermission(userId));
+                SysUser sysUser = new SysUser();
+                sysUser.setUserId(userId);
+                loginUser.setMenuPermission(permissionService.getMenuPermission(sysUser));
                 LoginHelper.setLoginUser(loginUser);
             }
             return R.ok();
@@ -213,4 +220,19 @@ public class SysRoleController extends BaseController {
         roleService.checkRoleDataScope(roleId);
         return toAjax(roleService.insertAuthUsers(roleId, userIds));
     }
+
+    /**
+     * 获取对应角色部门树列表
+     *
+     * @param roleId 角色ID
+     */
+    @SaCheckPermission("system:role:query")
+    @GetMapping(value = "/deptTree/{roleId}")
+    public R<Map<String, Object>> deptTree(@PathVariable("roleId") Long roleId) {
+        Map<String, Object> ajax = new HashMap<>();
+        ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId));
+        ajax.put("depts", deptService.selectDeptTreeList(new SysDept()));
+        return R.ok(ajax);
+    }
 }
+

+ 11 - 1
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java

@@ -52,6 +52,7 @@ public class SysUserController extends BaseController {
     private final ISysRoleService roleService;
     private final ISysPostService postService;
     private final ISysPermissionService permissionService;
+    private final ISysDeptService deptService;
 
     /**
      * 获取用户列表
@@ -113,7 +114,7 @@ public class SysUserController extends BaseController {
     public R<Map<String, Object>> getInfo() {
         Long userId = LoginHelper.getUserId();
         // 角色集合
-        Set<String> roles = permissionService.getRolePermission(userId);
+        Set<String> roles = permissionService.getRolePermission(user);
         // 权限集合
         Set<String> permissions = permissionService.getMenuPermission(userId);
         Map<String, Object> ajax = new HashMap<>();
@@ -254,4 +255,13 @@ public class SysUserController extends BaseController {
         userService.insertUserAuth(userId, roleIds);
         return R.ok();
     }
+
+    /**
+     * 获取部门树列表
+     */
+    @SaCheckPermission("system:user:list")
+    @GetMapping("/deptTree")
+    public AjaxResult deptTree(SysDept dept) {
+        return R.ok(deptService.selectDeptTreeList(dept));
+    }
 }

+ 8 - 0
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java

@@ -32,6 +32,14 @@ public interface SysMenuMapper extends BaseMapperPlus<SysMenuMapper, SysMenu, Sy
      */
     List<SysMenu> selectMenuListByUserId(@Param(Constants.WRAPPER) Wrapper<SysMenu> queryWrapper);
 
+    /**
+     * 根据角色ID查询权限
+     *
+     * @param roleId 角色ID
+     * @return 权限列表
+     */
+    public List<String> selectMenuPermsByRoleId(Long roleId);
+
     /**
      * 根据用户ID查询权限
      *

+ 8 - 0
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java

@@ -19,6 +19,14 @@ public interface ISysDeptService {
      */
     List<SysDept> selectDeptList(SysDept dept);
 
+    /**
+     * 查询部门树结构信息
+     *
+     * @param dept 部门信息
+     * @return 部门树信息集合
+     */
+    public List<Tree<Long>> selectDeptTreeList(SysDept dept);
+
     /**
      * 构建前端所需要下拉树结构
      *

+ 8 - 0
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java

@@ -38,6 +38,14 @@ public interface ISysMenuService {
      */
     Set<String> selectMenuPermsByUserId(Long userId);
 
+    /**
+     * 根据角色ID查询权限
+     *
+     * @param roleId 角色ID
+     * @return 权限列表
+     */
+    public Set<String> selectMenuPermsByRoleId(Long roleId);
+
     /**
      * 根据用户ID查询菜单树信息
      *

+ 28 - 21
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPermissionService.java

@@ -1,21 +1,28 @@
-package com.ruoyi.system.service;
-
-import java.util.Set;
-
-public interface ISysPermissionService {
-    /**
-     * 获取角色数据权限
-     *
-     * @param userId 用户Id
-     * @return 角色权限信息
-     */
-    Set<String> getRolePermission(Long userId);
-
-    /**
-     * 获取菜单数据权限
-     *
-     * @param userId 用户Id
-     * @return 菜单权限信息
-     */
-    Set<String> getMenuPermission(Long userId);
-}
+package com.ruoyi.system.service;
+
+import java.util.Set;
+
+import com.ruoyi.system.api.domain.SysUser;
+
+/**
+ * 权限信息 服务层
+ *
+ * @author ruoyi
+ */
+public interface ISysPermissionService {
+    /**
+     * 获取角色数据权限
+     *
+     * @param userId 用户Id
+     * @return 角色权限信息
+     */
+    Set<String> getRolePermission(SysUser user);
+
+    /**
+     * 获取菜单数据权限
+     *
+     * @param userId 用户Id
+     * @return 菜单权限信息
+     */
+    Set<String> getMenuPermission(SysUser user);
+}

+ 12 - 0
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java

@@ -57,6 +57,18 @@ public class SysDeptServiceImpl implements ISysDeptService {
         return baseMapper.selectDeptList(lqw);
     }
 
+    /**
+     * 查询部门树结构信息
+     *
+     * @param dept 部门信息
+     * @return 部门树信息集合
+     */
+    @Override
+    public List<Tree<Long>> selectDeptTreeList(SysDept dept) {
+        List<SysDept> depts = this.selectDeptList(dept);
+        return buildDeptTreeSelect(depts);
+    }
+
     /**
      * 构建前端所需要下拉树结构
      *

+ 22 - 1
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java

@@ -97,6 +97,27 @@ public class SysMenuServiceImpl implements ISysMenuService {
         return permsSet;
     }
 
+    /**
+     * 根据角色ID查询权限
+     *
+     * @param roleId 角色ID
+     * @return 权限列表
+     */
+    @Override
+    public Set<String> selectMenuPermsByRoleId(Long roleId)
+    {
+        List<String> perms = menuMapper.selectMenuPermsByRoleId(roleId);
+        Set<String> permsSet = new HashSet<>();
+        for (String perm : perms)
+        {
+            if (StringUtils.isNotEmpty(perm))
+            {
+                permsSet.addAll(Arrays.asList(perm.trim().split(",")));
+            }
+        }
+        return permsSet;
+    }
+
     /**
      * 根据用户ID查询菜单
      *
@@ -430,7 +451,7 @@ public class SysMenuServiceImpl implements ISysMenuService {
      * @return
      */
     public String innerLinkReplaceEach(String path) {
-        return StringUtils.replaceEach(path, new String[]{Constants.HTTP, Constants.HTTPS},
+        return StringUtils.replaceEach(path, new String[]{Constants.HTTP, Constants.HTTPS, Constants.WWW, "." },
             new String[]{"", ""});
     }
 }

+ 21 - 6
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPermissionServiceImpl.java

@@ -10,6 +10,11 @@ import org.springframework.stereotype.Service;
 import java.util.HashSet;
 import java.util.Set;
 
+/**
+ * 用户权限处理
+ *
+ * @author ruoyi
+ */
 @RequiredArgsConstructor
 @Service
 public class SysPermissionServiceImpl implements ISysPermissionService {
@@ -24,13 +29,13 @@ public class SysPermissionServiceImpl implements ISysPermissionService {
      * @return 角色权限信息
      */
     @Override
-    public Set<String> getRolePermission(Long userId) {
+    public Set<String> getRolePermission(SysUser user) {
         Set<String> roles = new HashSet<String>();
         // 管理员拥有所有权限
-        if (LoginHelper.isAdmin(userId)) {
+        if (LoginHelper.isAdmin(user.getUserId())) {
             roles.add("admin");
         } else {
-            roles.addAll(roleService.selectRolePermissionByUserId(userId));
+            roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId()));
         }
         return roles;
     }
@@ -42,13 +47,23 @@ public class SysPermissionServiceImpl implements ISysPermissionService {
      * @return 菜单权限信息
      */
     @Override
-    public Set<String> getMenuPermission(Long userId) {
+    public Set<String> getMenuPermission(SysUser user) {
         Set<String> perms = new HashSet<String>();
         // 管理员拥有所有权限
-        if (LoginHelper.isAdmin(userId)) {
+        if (LoginHelper.isAdmin(user.getUserId())) {
             perms.add("*:*:*");
         } else {
-            perms.addAll(menuService.selectMenuPermsByUserId(userId));
+            List<SysRole> roles = user.getRoles();
+            if (!roles.isEmpty() && roles.size() > 1) {
+                // 多角色设置permissions属性,以便数据权限匹配权限
+                for (SysRole role : roles) {
+                    Set<String> rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId());
+                    role.setPermissions(rolePerms);
+                    perms.addAll(rolePerms);
+                }
+            } else {
+                perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId()));
+            }
         }
         return perms;
     }

+ 6 - 0
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml

@@ -92,4 +92,10 @@
 		where m.status = '0' and r.status = '0' and sur.user_id = #{userId}
 	</select>
 
+    <select id="selectMenuPermsByRoleId" parameterType="Long" resultType="String">
+        select distinct m.perms
+        from sys_menu m
+             left join sys_role_menu rm on m.menu_id = rm.menu_id
+        where m.status = '0' and rm.role_id = #{roleId}
+    </select>
 </mapper>

+ 0 - 16
ruoyi-ui/src/api/system/dept.js

@@ -25,22 +25,6 @@ export function getDept(deptId) {
   })
 }
 
-// 查询部门下拉树结构
-export function treeselect() {
-  return request({
-    url: '/system/dept/treeselect',
-    method: 'get'
-  })
-}
-
-// 根据角色ID查询部门树结构
-export function roleDeptTreeselect(roleId) {
-  return request({
-    url: '/system/dept/roleDeptTreeselect/' + roleId,
-    method: 'get'
-  })
-}
-
 // 新增部门
 export function addDept(data) {
   return request({

+ 9 - 1
ruoyi-ui/src/api/system/role.js

@@ -108,4 +108,12 @@ export function authUserSelectAll(data) {
     method: 'put',
     params: data
   })
-}
+}
+
+// 根据角色ID查询部门树结构
+export function deptTreeSelect(roleId) {
+  return request({
+    url: '/system/role/deptTree/' + roleId,
+    method: 'get'
+  })
+}

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

@@ -125,3 +125,11 @@ export function updateAuthRole(data) {
     params: data
   })
 }
+
+// 查询部门下拉树结构
+export function deptTreeSelect() {
+  return request({
+    url: '/system/user/deptTree',
+    method: 'get'
+  })
+}

+ 5 - 0
ruoyi-ui/src/assets/styles/transition.scss

@@ -12,11 +12,16 @@
 }
 
 /* fade-transform */
+.fade-transform--move,
 .fade-transform-leave-active,
 .fade-transform-enter-active {
   transition: all .5s;
 }
 
+.fade-transform-leave-active {
+  position: absolute;
+}
+
 .fade-transform-enter {
   opacity: 0;
   transform: translateX(-30px);

+ 20 - 13
ruoyi-ui/src/components/FileUpload/index.vue

@@ -12,7 +12,7 @@
       :show-file-list="false"
       :headers="headers"
       class="upload-file-uploader"
-      ref="upload"
+      ref="fileUpload"
     >
       <!-- 上传按钮 -->
       <el-button size="mini" type="primary">选取文件</el-button>
@@ -73,7 +73,7 @@ export default {
     return {
       number: 0,
       uploadList: [],
-      uploadFileUrl: process.env.VUE_APP_BASE_API + "/resource/oss/upload", // 上传的图片服务器地址
+      uploadFileUrl: process.env.VUE_APP_BASE_API + "/resource/oss/upload", // 上传文件服务器地址
       headers: {
         Authorization: "Bearer " + getToken(),
       },
@@ -155,24 +155,20 @@ export default {
     },
     // 上传失败
     handleUploadError(err) {
-      this.$modal.msgError("上传图片失败,请重试");
+      this.$modal.msgError("上传文件失败,请重试");
       this.$modal.closeLoading();
     },
     // 上传成功回调
-    handleUploadSuccess(res) {
+    handleUploadSuccess(res, file) {
       if (res.code === 200) {
         this.uploadList.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });
-        if (this.uploadList.length === this.number) {
-          this.fileList = this.fileList.concat(this.uploadList);
-          this.uploadList = [];
-          this.number = 0;
-          this.$emit("input", this.listToString(this.fileList));
-          this.$modal.closeLoading();
-        }
+        this.uploadedSuccessfully();
       } else {
-        this.$modal.msgError(res.msg);
-        this.$modal.closeLoading();
         this.number--;
+        this.$modal.closeLoading();
+        this.$modal.msgError(res.msg);
+        this.$refs.fileUpload.handleRemove(file);
+        this.uploadedSuccessfully();
       }
     },
     // 删除文件
@@ -182,6 +178,16 @@ export default {
       this.fileList.splice(index, 1);
       this.$emit("input", this.listToString(this.fileList));
     },
+    // 上传结束处理
+    uploadedSuccessfully() {
+      if (this.number > 0 && this.uploadList.length === this.number) {
+        this.fileList = this.fileList.concat(this.uploadList);
+        this.uploadList = [];
+        this.number = 0;
+        this.$emit("input", this.listToString(this.fileList));
+        this.$modal.closeLoading();
+      }
+    },
     // 获取文件名称
     getFileName(name) {
       if (name.lastIndexOf("/") > -1) {
@@ -223,3 +229,4 @@ export default {
   margin-right: 10px;
 }
 </style>
+

+ 7 - 1
ruoyi-ui/src/components/ImagePreview/index.vue

@@ -17,7 +17,7 @@ export default {
   props: {
     src: {
       type: String,
-      required: true
+      default: ""
     },
     width: {
       type: [Number, String],
@@ -30,10 +30,16 @@ export default {
   },
   computed: {
     realSrc() {
+      if (!this.src) {
+        return;
+      }
       let real_src = this.src.split(",")[0];
       return real_src;
     },
     realSrcList() {
+      if (!this.src) {
+        return;
+      }
       let real_src_list = this.src.split(",");
       let srcList = [];
       real_src_list.forEach(item => {

+ 38 - 30
ruoyi-ui/src/components/ImageUpload/index.vue

@@ -9,8 +9,8 @@
       :limit="limit"
       :on-error="handleUploadError"
       :on-exceed="handleExceed"
-      name="file"
-      :on-remove="handleRemove"
+      ref="imageUpload"
+      :on-remove="handleDelete"
       :show-file-list="true"
       :headers="headers"
       :file-list="fileList"
@@ -119,33 +119,6 @@ export default {
     },
   },
   methods: {
-    // 删除图片
-    handleRemove(file, fileList) {
-      const findex = this.fileList.map(f => f.name).indexOf(file.name);
-      if(findex > -1) {
-        let ossId = this.fileList[findex].ossId;
-        delOss(ossId);
-        this.fileList.splice(findex, 1);
-        this.$emit("input", this.listToString(this.fileList));
-      }
-    },
-    // 上传成功回调
-    handleUploadSuccess(res) {
-      if (res.code == 200) {
-        this.uploadList.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });
-        if (this.uploadList.length === this.number) {
-          this.fileList = this.fileList.concat(this.uploadList);
-          this.uploadList = [];
-          this.number = 0;
-          this.$emit("input", this.listToString(this.fileList));
-          this.$modal.closeLoading();
-        }
-      } else {
-        this.$modal.msgError(res.msg);
-        this.$modal.closeLoading();
-        this.number--;
-      }
-    },
     // 上传前loading加载
     handleBeforeUpload(file) {
       let isImg = false;
@@ -181,11 +154,44 @@ export default {
     handleExceed() {
       this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`);
     },
+    // 上传成功回调
+    handleUploadSuccess(res, file) {
+      if (res.code === 200) {
+        this.uploadList.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });
+        this.uploadedSuccessfully();
+      } else {
+        this.number--;
+        this.$modal.closeLoading();
+        this.$modal.msgError(res.msg);
+        this.$refs.imageUpload.handleRemove(file);
+        this.uploadedSuccessfully();
+      }
+    },
+    // 删除图片
+    handleDelete(file) {
+      const findex = this.fileList.map(f => f.name).indexOf(file.name);
+      if (findex > -1) {
+        let ossId = this.fileList[findex].ossId;
+        delOss(ossId);
+        this.fileList.splice(findex, 1);
+        this.$emit("input", this.listToString(this.fileList));
+      }
+    },
     // 上传失败
     handleUploadError() {
       this.$modal.msgError("上传图片失败,请重试");
       this.$modal.closeLoading();
     },
+    // 上传结束处理
+    uploadedSuccessfully() {
+      if (this.number > 0 && this.uploadList.length === this.number) {
+        this.fileList = this.fileList.concat(this.uploadList);
+        this.uploadList = [];
+        this.number = 0;
+        this.$emit("input", this.listToString(this.fileList));
+        this.$modal.closeLoading();
+      }
+    },
     // 预览
     handlePictureCardPreview(file) {
       this.dialogImageUrl = file.url;
@@ -196,7 +202,9 @@ export default {
       let strs = "";
       separator = separator || ",";
       for (let i in list) {
-        strs += list[i].ossId + separator;
+        if (list[i].ossId) {
+          strs += list[i].ossId + separator;
+        }
       }
       return strs != "" ? strs.substr(0, strs.length - 1) : "";
     },

+ 7 - 3
ruoyi-ui/src/layout/components/AppMain.vue

@@ -2,15 +2,19 @@
   <section class="app-main">
     <transition name="fade-transform" mode="out-in">
       <keep-alive :include="cachedViews">
-        <router-view :key="key" />
+        <router-view v-if="!$route.meta.link" :key="key" />
       </keep-alive>
     </transition>
+    <iframe-toggle />
   </section>
 </template>
 
 <script>
+import iframeToggle from "./IframeToggle/index"
+
 export default {
   name: 'AppMain',
+  components: { iframeToggle },
   computed: {
     cachedViews() {
       return this.$store.state.tagsView.cachedViews
@@ -31,7 +35,7 @@ export default {
   overflow: hidden;
 }
 
-.fixed-header+.app-main {
+.fixed-header + .app-main {
   padding-top: 50px;
 }
 
@@ -41,7 +45,7 @@ export default {
     min-height: calc(100vh - 84px);
   }
 
-  .fixed-header+.app-main {
+  .fixed-header + .app-main {
     padding-top: 84px;
   }
 }

+ 24 - 0
ruoyi-ui/src/layout/components/IframeToggle/index.vue

@@ -0,0 +1,24 @@
+<template>
+  <transition-group name="fade-transform" mode="out-in">
+    <inner-link
+      v-for="(item, index) in iframeViews"
+      :key="item.path"
+      :iframeId="'iframe' + index"
+      v-show="$route.path === item.path"
+      :src="item.meta.link"
+    ></inner-link>
+  </transition-group>
+</template>
+
+<script>
+import InnerLink from "../InnerLink/index"
+
+export default {
+  components: { InnerLink },
+  computed: {
+    iframeViews() {
+      return this.$store.state.tagsView.iframeViews
+    }
+  }
+}
+</script>

+ 40 - 20
ruoyi-ui/src/layout/components/InnerLink/index.vue

@@ -1,27 +1,47 @@
+<template>
+  <div :style="'height:' + height" v-loading="loading" element-loading-text="正在加载页面,请稍候!">
+    <iframe
+      :id="iframeId"
+      style="width: 100%; height: 100%"
+      :src="src"
+      frameborder="no"
+    ></iframe>
+  </div>
+</template>
+
 <script>
 export default {
+  props: {
+    src: {
+      type: String,
+      default: "/"
+    },
+    iframeId: {
+      type: String
+    }
+  },
   data() {
-    return {};
+    return {
+      loading: false,
+      height: document.documentElement.clientHeight - 94.5 + "px;"
+    };
   },
-  render() {
-    const { $route: { meta: { link } }, } = this;
-    if ({ link }.link === "") {
-      return "404";
+  mounted() {
+    var _this = this;
+    const iframeId = ("#" + this.iframeId).replace(/\//g, "\\/");
+    const iframe = document.querySelector(iframeId);
+    // iframe页面loading控制
+    if (iframe.attachEvent) {
+      this.loading = true;
+      iframe.attachEvent("onload", function () {
+        _this.loading = false;
+      });
+    } else {
+      this.loading = true;
+      iframe.onload = function () {
+        _this.loading = false;
+      };
     }
-    let url = { link }.link;
-    const height = document.documentElement.clientHeight - 94.5 + "px";
-    const style = { height: height };
-
-    return (
-      <div style={style}>
-        <iframe
-          src={url}
-          frameborder="no"
-          style="width: 100%; height: 100%"
-          scrolling="auto"
-        ></iframe>
-      </div>
-    );
-  },
+  }
 };
 </script>

+ 6 - 0
ruoyi-ui/src/layout/components/TagsView/index.vue

@@ -133,6 +133,9 @@ export default {
       const { name } = this.$route
       if (name) {
         this.$store.dispatch('tagsView/addView', this.$route)
+        if (this.$route.meta.link) {
+          this.$store.dispatch('tagsView/addIframeView', this.$route)
+        }
       }
       return false
     },
@@ -153,6 +156,9 @@ export default {
     },
     refreshSelectedTag(view) {
       this.$tab.refreshPage(view);
+      if (this.$route.meta.link) {
+        this.$store.dispatch('tagsView/delIframeView', this.$route)
+      }
     },
     closeSelectedTag(view) {
       this.$tab.closePage(view).then(({ visitedViews }) => {

+ 33 - 12
ruoyi-ui/src/store/modules/tagsView.js

@@ -1,9 +1,18 @@
 const state = {
   visitedViews: [],
-  cachedViews: []
+  cachedViews: [],
+  iframeViews: []
 }
 
 const mutations = {
+  ADD_IFRAME_VIEW: (state, view) => {
+    if (state.iframeViews.some(v => v.path === view.path)) return
+    state.iframeViews.push(
+      Object.assign({}, view, {
+        title: view.meta.title || 'no-name'
+      })
+    )
+  },
   ADD_VISITED_VIEW: (state, view) => {
     if (state.visitedViews.some(v => v.path === view.path)) return
     state.visitedViews.push(
@@ -18,7 +27,6 @@ const mutations = {
       state.cachedViews.push(view.name)
     }
   },
-
   DEL_VISITED_VIEW: (state, view) => {
     for (const [i, v] of state.visitedViews.entries()) {
       if (v.path === view.path) {
@@ -26,6 +34,10 @@ const mutations = {
         break
       }
     }
+    state.iframeViews = state.iframeViews.filter(item => item.path !== view.path)
+  },
+  DEL_IFRAME_VIEW: (state, view) => {
+    state.iframeViews = state.iframeViews.filter(item => item.path !== view.path)
   },
   DEL_CACHED_VIEW: (state, view) => {
     const index = state.cachedViews.indexOf(view.name)
@@ -36,6 +48,7 @@ const mutations = {
     state.visitedViews = state.visitedViews.filter(v => {
       return v.meta.affix || v.path === view.path
     })
+    state.iframeViews = state.iframeViews.filter(item => item.path === view.path)
   },
   DEL_OTHERS_CACHED_VIEWS: (state, view) => {
     const index = state.cachedViews.indexOf(view.name)
@@ -45,16 +58,15 @@ const mutations = {
       state.cachedViews = []
     }
   },
-
   DEL_ALL_VISITED_VIEWS: state => {
     // keep affix tags
     const affixTags = state.visitedViews.filter(tag => tag.meta.affix)
     state.visitedViews = affixTags
+    state.iframeViews = []
   },
   DEL_ALL_CACHED_VIEWS: state => {
     state.cachedViews = []
   },
-
   UPDATE_VISITED_VIEW: (state, view) => {
     for (let v of state.visitedViews) {
       if (v.path === view.path) {
@@ -63,7 +75,6 @@ const mutations = {
       }
     }
   },
-  
   DEL_RIGHT_VIEWS: (state, view) => {
     const index = state.visitedViews.findIndex(v => v.path === view.path)
     if (index === -1) {
@@ -77,10 +88,13 @@ const mutations = {
       if (i > -1) {
         state.cachedViews.splice(i, 1)
       }
+      if(item.meta.link) {
+        const fi = state.iframeViews.findIndex(v => v.path === item.path)
+        state.iframeViews.splice(fi, 1)
+      }
       return false
     })
   },
-
   DEL_LEFT_VIEWS: (state, view) => {
     const index = state.visitedViews.findIndex(v => v.path === view.path)
     if (index === -1) {
@@ -94,6 +108,10 @@ const mutations = {
       if (i > -1) {
         state.cachedViews.splice(i, 1)
       }
+      if(item.meta.link) {
+        const fi = state.iframeViews.findIndex(v => v.path === item.path)
+        state.iframeViews.splice(fi, 1)
+      }
       return false
     })
   }
@@ -104,13 +122,15 @@ const actions = {
     dispatch('addVisitedView', view)
     dispatch('addCachedView', view)
   },
+  addIframeView({ commit }, view) {
+    commit('ADD_IFRAME_VIEW', view)
+  },
   addVisitedView({ commit }, view) {
     commit('ADD_VISITED_VIEW', view)
   },
   addCachedView({ commit }, view) {
     commit('ADD_CACHED_VIEW', view)
   },
-
   delView({ dispatch, state }, view) {
     return new Promise(resolve => {
       dispatch('delVisitedView', view)
@@ -127,13 +147,18 @@ const actions = {
       resolve([...state.visitedViews])
     })
   },
+  delIframeView({ commit, state }, view) {
+    return new Promise(resolve => {
+      commit('DEL_IFRAME_VIEW', view)
+      resolve([...state.iframeViews])
+    })
+  },
   delCachedView({ commit, state }, view) {
     return new Promise(resolve => {
       commit('DEL_CACHED_VIEW', view)
       resolve([...state.cachedViews])
     })
   },
-
   delOthersViews({ dispatch, state }, view) {
     return new Promise(resolve => {
       dispatch('delOthersVisitedViews', view)
@@ -156,7 +181,6 @@ const actions = {
       resolve([...state.cachedViews])
     })
   },
-
   delAllViews({ dispatch, state }, view) {
     return new Promise(resolve => {
       dispatch('delAllVisitedViews', view)
@@ -179,18 +203,15 @@ const actions = {
       resolve([...state.cachedViews])
     })
   },
-
   updateVisitedView({ commit }, view) {
     commit('UPDATE_VISITED_VIEW', view)
   },
-
   delRightTags({ commit }, view) {
     return new Promise(resolve => {
       commit('DEL_RIGHT_VIEWS', view)
       resolve([...state.visitedViews])
     })
   },
-
   delLeftTags({ commit }, view) {
     return new Promise(resolve => {
       commit('DEL_LEFT_VIEWS', view)

+ 7 - 7
ruoyi-ui/src/views/system/menu/index.vue

@@ -107,7 +107,7 @@
       <el-form ref="form" :model="form" :rules="rules" label-width="100px">
         <el-row>
           <el-col :span="24">
-            <el-form-item label="上级菜单">
+            <el-form-item label="上级菜单" prop="parentId">
               <treeselect
                 v-model="form.parentId"
                 :options="menuOptions"
@@ -159,7 +159,7 @@
             </el-form-item>
           </el-col>
           <el-col :span="12" v-if="form.menuType != 'F'">
-            <el-form-item>
+            <el-form-item prop="isFrame">
               <span slot="label">
                 <el-tooltip content="选择是外链则路由地址需要以`http(s)://`开头" placement="top">
                 <i class="el-icon-question"></i>
@@ -195,7 +195,7 @@
             </el-form-item>
           </el-col>
           <el-col :span="12" v-if="form.menuType != 'M'">
-            <el-form-item>
+            <el-form-item prop="perms">
               <el-input v-model="form.perms" placeholder="请输入权限标识" maxlength="100" />
               <span slot="label">
                 <el-tooltip content="控制器中定义的权限字符,如:@SaCheckPermission('system:user:list')" placement="top">
@@ -206,7 +206,7 @@
             </el-form-item>
           </el-col>
           <el-col :span="12" v-if="form.menuType == 'C'">
-            <el-form-item>
+            <el-form-item prop="queryParam">
               <el-input v-model="form.queryParam" placeholder="请输入路由参数" maxlength="255" />
               <span slot="label">
                 <el-tooltip content='访问路由的默认传递参数,如:`{"id": 1, "name": "ry"}`' placement="top">
@@ -217,7 +217,7 @@
             </el-form-item>
           </el-col>
           <el-col :span="12" v-if="form.menuType == 'C'">
-            <el-form-item>
+            <el-form-item prop="isCache">
               <span slot="label">
                 <el-tooltip content="选择是则会被`keep-alive`缓存,需要匹配组件的`name`和地址保持一致" placement="top">
                 <i class="el-icon-question"></i>
@@ -231,7 +231,7 @@
             </el-form-item>
           </el-col>
           <el-col :span="12" v-if="form.menuType != 'F'">
-            <el-form-item>
+            <el-form-item prop="visible">
               <span slot="label">
                 <el-tooltip content="选择隐藏则路由将不会出现在侧边栏,但仍然可以访问" placement="top">
                 <i class="el-icon-question"></i>
@@ -248,7 +248,7 @@
             </el-form-item>
           </el-col>
           <el-col :span="12" v-if="form.menuType != 'F'">
-            <el-form-item>
+            <el-form-item prop="status">
               <span slot="label">
                 <el-tooltip content="选择停用则路由将不会出现在侧边栏,也不能被访问" placement="top">
                 <i class="el-icon-question"></i>

+ 6 - 12
ruoyi-ui/src/views/system/role/index.vue

@@ -254,9 +254,8 @@
 </template>
 
 <script>
-import { listRole, getRole, delRole, addRole, updateRole, dataScope, changeRoleStatus } from "@/api/system/role";
+import { listRole, getRole, delRole, addRole, updateRole, dataScope, changeRoleStatus, deptTreeSelect } from "@/api/system/role";
 import { treeselect as menuTreeselect, roleMenuTreeselect } from "@/api/system/menu";
-import { treeselect as deptTreeselect, roleDeptTreeselect } from "@/api/system/dept";
 
 export default {
   name: "Role",
@@ -364,12 +363,6 @@ export default {
         this.menuOptions = response.data;
       });
     },
-    /** 查询部门树结构 */
-    getDeptTreeselect() {
-      deptTreeselect().then(response => {
-        this.deptOptions = response.data;
-      });
-    },
     // 所有菜单节点数据
     getMenuAllCheckedKeys() {
       // 目前被选中的菜单节点
@@ -396,8 +389,8 @@ export default {
       });
     },
     /** 根据角色ID查询部门树结构 */
-    getRoleDeptTreeselect(roleId) {
-      return roleDeptTreeselect(roleId).then(response => {
+    getDeptTree(roleId) {
+      return deptTreeSelect(roleId).then(response => {
         this.deptOptions = response.data.depts;
         return response;
       });
@@ -543,12 +536,12 @@ export default {
     /** 分配数据权限操作 */
     handleDataScope(row) {
       this.reset();
-      const roleDeptTreeselect = this.getRoleDeptTreeselect(row.roleId);
+      const deptTreeSelect = this.getDeptTree(row.roleId);
       getRole(row.roleId).then(response => {
         this.form = response.data;
         this.openDataScope = true;
         this.$nextTick(() => {
-          roleDeptTreeselect.then(res => {
+          deptTreeSelect.then(res => {
             this.$refs.dept.setCheckedKeys(res.data.checkedKeys);
           });
         });
@@ -612,3 +605,4 @@ export default {
   }
 };
 </script>
+

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

@@ -342,9 +342,8 @@
 </template>
 
 <script>
-import { listUser, getUser, delUser, addUser, updateUser, resetUserPwd, changeUserStatus } from "@/api/system/user";
+import { listUser, getUser, delUser, addUser, updateUser, resetUserPwd, changeUserStatus, deptTreeSelect } from "@/api/system/user";
 import { getToken } from "@/utils/auth";
-import { treeselect } from "@/api/system/dept";
 import Treeselect from "@riophae/vue-treeselect";
 import "@riophae/vue-treeselect/dist/vue-treeselect.css";
 
@@ -462,7 +461,7 @@ export default {
   },
   created() {
     this.getList();
-    this.getTreeselect();
+    this.getDeptTree();
     this.getConfigKey("sys.user.initPassword").then(response => {
       this.initPassword = response.msg;
     });
@@ -479,8 +478,8 @@ export default {
       );
     },
     /** 查询部门下拉树结构 */
-    getTreeselect() {
-      treeselect().then(response => {
+    getDeptTree() {
+      deptTreeSelect().then(response => {
         this.deptOptions = response.data;
       });
     },
@@ -561,7 +560,6 @@ export default {
     /** 新增按钮操作 */
     handleAdd() {
       this.reset();
-      this.getTreeselect();
       getUser().then(response => {
         this.postOptions = response.data.posts;
         this.roleOptions = response.data.roles;
@@ -573,7 +571,6 @@ export default {
     /** 修改按钮操作 */
     handleUpdate(row) {
       this.reset();
-      this.getTreeselect();
       const userId = row.userId || this.ids;
       getUser(userId).then(response => {
         this.form = response.data.user;
@@ -670,3 +667,4 @@ export default {
   }
 };
 </script>
+

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

@@ -49,7 +49,7 @@
               <userInfo :user="user" />
             </el-tab-pane>
             <el-tab-pane label="修改密码" name="resetPwd">
-              <resetPwd :user="user" />
+              <resetPwd />
             </el-tab-pane>
           </el-tabs>
         </el-card>