From bbe760abf6f7dae26c7dd6f09db55a1e4095c855 Mon Sep 17 00:00:00 2001 From: chenhao Date: Tue, 11 Nov 2025 15:41:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(system):=20=E5=AE=9E=E7=8E=B0=E5=9F=BA?= =?UTF-8?q?=E4=BA=8ESession=E7=9A=84=E8=AE=A4=E8=AF=81=E4=B8=8E=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 菜单图标字段从icon改为vueIcon,适配前端图标组件 - 登录认证方式改为Session认证,使用FormData格式提交数据 - 验证码获取方式改为直接图片URL,防止缓存问题 - 权限验证改为基于Session的用户角色判断 - axios请求配置支持withCredentials,允许携带cookie - 部门管理新增树形结构构建方法 - 菜单管理新增路由地址、组件路径等字段 - 字典管理新增根据字典类型查询数据接口 - 系统配置新增根据配置键查询接口 - 首页控制器新增用户信息和路由信息获取接口- 菜单控制器新增菜单列表、详情、增删改查接口 - 数据库菜单表新增path、component、query、status、vue_icon、is_frame字段 --- oms_web/oms_vue/src/api/login.js | 32 +-- oms_web/oms_vue/src/permission.js | 63 +++--- oms_web/oms_vue/src/store/modules/user.js | 39 ++-- oms_web/oms_vue/src/utils/request.js | 16 +- oms_web/oms_vue/src/views/login.vue | 17 +- .../oms_vue/src/views/system/menu/index.vue | 16 +- .../oms_vue/src/views/system/user/index.vue | 2 +- oms_web/oms_vue/vue.config.js | 6 +- .../system/SysCaptchaController.java | 3 + .../system/SysConfigController.java | 7 + .../system/SysDictDataController.java | 23 ++- .../controller/system/SysIndexController.java | 185 ++++++++++++++++-- .../controller/system/SysMenuController.java | 83 +++++++- .../controller/system/SysUserController.java | 27 +++ .../ruoyi/common/core/domain/TreeSelect.java | 94 +++++++++ .../common/core/domain/entity/SysDept.java | 14 +- .../common/core/domain/entity/SysMenu.java | 95 +++++++-- .../ruoyi/system/service/ISysDeptService.java | 17 ++ .../service/impl/SysDeptServiceImpl.java | 90 +++++++++ .../service/impl/SysMenuServiceImpl.java | 3 + .../resources/mapper/system/SysMenuMapper.xml | 30 ++- 21 files changed, 707 insertions(+), 155 deletions(-) create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java diff --git a/oms_web/oms_vue/src/api/login.js b/oms_web/oms_vue/src/api/login.js index 7b7388fd..186420d9 100644 --- a/oms_web/oms_vue/src/api/login.js +++ b/oms_web/oms_vue/src/api/login.js @@ -1,21 +1,21 @@ import request from '@/utils/request' -// 登录方法 -export function login(username, password, code, uuid) { - const data = { - username, - password, - code, - uuid - } +// 登录方法 - 基于 Session 认证,使用 FormData 格式 +export function login(username, password, code, rememberMe) { + const formData = new FormData() + formData.append('username', username) + formData.append('password', password) + formData.append('validateCode', code) + formData.append('rememberMe', rememberMe || false) return request({ url: '/login', headers: { isToken: false, + 'Content-Type': 'multipart/form-data', repeatSubmit: false }, method: 'post', - data: data + data: formData }) } @@ -39,22 +39,10 @@ export function getInfo() { }) } -// 退出方法 +// 退出方法 - 基于 Session 认证 export function logout() { return request({ url: '/logout', method: 'post' }) } - -// 获取验证码 -export function getCodeImg() { - return request({ - url: '/captchaImage', - headers: { - isToken: false - }, - method: 'get', - timeout: 20000 - }) -} \ No newline at end of file diff --git a/oms_web/oms_vue/src/permission.js b/oms_web/oms_vue/src/permission.js index b66190b3..480e0445 100644 --- a/oms_web/oms_vue/src/permission.js +++ b/oms_web/oms_vue/src/permission.js @@ -3,7 +3,6 @@ import store from './store' import { Message } from 'element-ui' import NProgress from 'nprogress' import 'nprogress/nprogress.css' -import { getToken } from '@/utils/auth' import { isPathMatch } from '@/utils/validate' import { isRelogin } from '@/utils/request' @@ -17,43 +16,37 @@ const isWhiteList = (path) => { router.beforeEach((to, from, next) => { NProgress.start() - if (getToken()) { - to.meta.title && store.dispatch('settings/setTitle', to.meta.title) - /* has token*/ - if (to.path === '/login') { - next({ path: '/' }) - NProgress.done() - } else if (isWhiteList(to.path)) { - next() - } else { - if (store.getters.roles.length === 0) { - isRelogin.show = true - // 判断当前用户是否已拉取完user_info信息 - store.dispatch('GetInfo').then(() => { - isRelogin.show = false - store.dispatch('GenerateRoutes').then(accessRoutes => { - // 根据roles权限生成可访问的路由表 - router.addRoutes(accessRoutes) // 动态添加可访问路由表 - next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 - }) - }).catch(err => { - store.dispatch('LogOut').then(() => { - Message.error(err) - next({ path: '/' }) - }) - }) - } else { - next() - } - } + // 基于 Session 认证:通过是否有角色信息来判断登录状态 + const hasRoles = store.getters.roles && store.getters.roles.length > 0 + + to.meta.title && store.dispatch('settings/setTitle', to.meta.title) + + if (isWhiteList(to.path)) { + // 在免登录白名单,直接进入 + next() + NProgress.done() } else { - // 没有token - if (isWhiteList(to.path)) { - // 在免登录白名单,直接进入 + if (hasRoles) { + // 已有用户信息,放行 next() } else { - next(`/login?redirect=${encodeURIComponent(to.fullPath)}`) // 否则全部重定向到登录页 - NProgress.done() + // 没有用户信息,尝试获取 + isRelogin.show = true + store.dispatch('GetInfo').then(() => { + isRelogin.show = false + store.dispatch('GenerateRoutes').then(accessRoutes => { + // 根据roles权限生成可访问的路由表 + router.addRoutes(accessRoutes) // 动态添加可访问路由表 + next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 + }) + }).catch(err => { + // 获取用户信息失败,说明未登录或 Session 已过期 + store.dispatch('FedLogOut').then(() => { + Message.error(err || '未登录或登录超时,请重新登录') + next(`/login?redirect=${encodeURIComponent(to.fullPath)}`) + NProgress.done() + }) + }) } } }) diff --git a/oms_web/oms_vue/src/store/modules/user.js b/oms_web/oms_vue/src/store/modules/user.js index 6a7b710a..fb1ff782 100644 --- a/oms_web/oms_vue/src/store/modules/user.js +++ b/oms_web/oms_vue/src/store/modules/user.js @@ -1,13 +1,11 @@ import router from '@/router' import { MessageBox, } from 'element-ui' import { login, logout, getInfo } from '@/api/login' -import { getToken, setToken, removeToken } from '@/utils/auth' import { isHttp, isEmpty } from "@/utils/validate" import defAva from '@/assets/images/profile.jpg' const user = { state: { - token: getToken(), id: '', name: '', nickName: '', @@ -17,9 +15,6 @@ const user = { }, mutations: { - SET_TOKEN: (state, token) => { - state.token = token - }, SET_ID: (state, id) => { state.id = id }, @@ -41,16 +36,14 @@ const user = { }, actions: { - // 登录 + // 登录 - 基于 Session 认证,不需要保存 token Login({ commit }, userInfo) { const username = userInfo.username.trim() const password = userInfo.password const code = userInfo.code - const uuid = userInfo.uuid + const rememberMe = userInfo.rememberMe return new Promise((resolve, reject) => { - login(username, password, code, uuid).then(res => { - setToken(res.token) - commit('SET_TOKEN', res.token) + login(username, password, code, rememberMe).then(res => { resolve() }).catch(error => { reject(error) @@ -62,14 +55,16 @@ const user = { GetInfo({ commit, state }) { return new Promise((resolve, reject) => { getInfo().then(res => { - const user = res.user + // 后端返回的数据在 res.data 中 + const data = res.data || res + const user = data.user let avatar = user.avatar || "" if (!isHttp(avatar)) { avatar = (isEmpty(avatar)) ? defAva : process.env.VUE_APP_BASE_API + avatar } - if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组 - commit('SET_ROLES', res.roles) - commit('SET_PERMISSIONS', res.permissions) + if (data.roles && data.roles.length > 0) { // 验证返回的roles是否是一个非空数组 + commit('SET_ROLES', data.roles) + commit('SET_PERMISSIONS', data.permissions) } else { commit('SET_ROLES', ['ROLE_DEFAULT']) } @@ -78,13 +73,13 @@ const user = { commit('SET_NICK_NAME', user.nickName) commit('SET_AVATAR', avatar) /* 初始密码提示 */ - if(res.isDefaultModifyPwd) { + if(data.isDefaultModifyPwd) { MessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } }) }).catch(() => {}) } /* 过期密码提示 */ - if(!res.isDefaultModifyPwd && res.isPasswordExpired) { + if(!data.isDefaultModifyPwd && data.isPasswordExpired) { MessageBox.confirm('您的密码已过期,请尽快修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } }) }).catch(() => {}) @@ -96,14 +91,12 @@ const user = { }) }, - // 退出系统 + // 退出系统 - 基于 Session 认证 LogOut({ commit, state }) { return new Promise((resolve, reject) => { - logout(state.token).then(() => { - commit('SET_TOKEN', '') + logout().then(() => { commit('SET_ROLES', []) commit('SET_PERMISSIONS', []) - removeToken() resolve() }).catch(error => { reject(error) @@ -111,11 +104,11 @@ const user = { }) }, - // 前端 登出 + // 前端 登出 - 清除状态 FedLogOut({ commit }) { return new Promise(resolve => { - commit('SET_TOKEN', '') - removeToken() + commit('SET_ROLES', []) + commit('SET_PERMISSIONS', []) resolve() }) } diff --git a/oms_web/oms_vue/src/utils/request.js b/oms_web/oms_vue/src/utils/request.js index 7150ecb1..34ee6390 100644 --- a/oms_web/oms_vue/src/utils/request.js +++ b/oms_web/oms_vue/src/utils/request.js @@ -1,7 +1,6 @@ import axios from 'axios' import { Notification, MessageBox, Message, Loading } from 'element-ui' import store from '@/store' -import { getToken } from '@/utils/auth' import errorCode from '@/utils/errorCode' import { tansParams, blobValidate } from "@/utils/ruoyi" import cache from '@/plugins/cache' @@ -17,18 +16,17 @@ const service = axios.create({ // axios中请求配置有baseURL选项,表示请求URL公共部分 baseURL: process.env.VUE_APP_BASE_API, // 超时 - timeout: 10000 + timeout: 10000, + // 允许携带cookie(Session认证必需) + withCredentials: true }) // request拦截器 service.interceptors.request.use(config => { - // 是否需要设置 token - const isToken = (config.headers || {}).isToken === false // 是否需要防止数据重复提交 const isRepeatSubmit = (config.headers || {}).repeatSubmit === false - if (getToken() && !isToken) { - config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 - } + config.headers['X-Requested-With'] = 'H5' + // Session 认证依赖 Cookie,无需手动设置 token // get请求映射params参数 if (config.method === 'get' && config.params) { let url = config.url + '?' + tansParams(config.params) @@ -39,7 +37,7 @@ service.interceptors.request.use(config => { if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) { const requestObj = { url: config.url, - data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data, + data: (typeof config.data === 'object' && !(config.data instanceof FormData)) ? JSON.stringify(config.data) : config.data, time: new Date().getTime() } const requestSize = Object.keys(JSON.stringify(requestObj)).length // 请求数据大小 @@ -79,7 +77,7 @@ service.interceptors.response.use(res => { const msg = errorCode[code] || res.data.msg || errorCode['default'] // 二进制数据则直接返回 if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') { - return res.data + return res } if (code === 401) { if (!isRelogin.show) { diff --git a/oms_web/oms_vue/src/views/login.vue b/oms_web/oms_vue/src/views/login.vue index 5bdda11c..381f6dac 100644 --- a/oms_web/oms_vue/src/views/login.vue +++ b/oms_web/oms_vue/src/views/login.vue @@ -62,7 +62,6 @@ \ No newline at end of file + diff --git a/oms_web/oms_vue/vue.config.js b/oms_web/oms_vue/vue.config.js index 40e140d5..66d5cb64 100644 --- a/oms_web/oms_vue/vue.config.js +++ b/oms_web/oms_vue/vue.config.js @@ -9,7 +9,7 @@ const CompressionPlugin = require('compression-webpack-plugin') const name = process.env.VUE_APP_TITLE || '若依管理系统' // 网页标题 -const baseUrl = 'http://localhost:8080' // 后端接口 +const baseUrl = 'http://localhost:28080' // 后端接口 const port = process.env.port || process.env.npm_config_port || 80 // 端口 @@ -38,6 +38,10 @@ module.exports = { [process.env.VUE_APP_BASE_API]: { target: baseUrl, changeOrigin: true, + // Session 认证需要携带 Cookie + cookieDomainRewrite: { + '*': '' + }, pathRewrite: { ['^' + process.env.VUE_APP_BASE_API]: '' } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysCaptchaController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysCaptchaController.java index 230cc3b2..2d3b8128 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysCaptchaController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysCaptchaController.java @@ -8,6 +8,8 @@ import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; + +import com.ruoyi.common.utils.StringUtils; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -48,6 +50,7 @@ public class SysCaptchaController extends BaseController response.setContentType("image/jpeg"); String type = request.getParameter("type"); + type = StringUtils.isEmpty(type) ? "math" : type; String capStr = null; String code = null; BufferedImage bi = null; diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java index 0a43e3bc..6930db21 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java @@ -155,4 +155,11 @@ public class SysConfigController extends BaseController { return configService.checkConfigKeyUnique(config); } + @GetMapping("/configKey/{key}") + @ResponseBody + public AjaxResult queryKey(@PathVariable("key") String key) + { + return success(configService.selectConfigByKey(key)); + } + } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java index 770766fd..ab4d1de9 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java @@ -1,6 +1,10 @@ package com.ruoyi.web.controller.system; +import java.util.ArrayList; import java.util.List; + +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysDictTypeService; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @@ -33,7 +37,8 @@ public class SysDictDataController extends BaseController @Autowired private ISysDictDataService dictDataService; - + @Autowired + private ISysDictTypeService dictTypeService; @RequiresPermissions("system:dict:view") @GetMapping() public String dictData() @@ -72,6 +77,22 @@ public class SysDictDataController extends BaseController mmap.put("dictType", dictType); return prefix + "/add"; } + /** + * 新增字典类型 + */ + + @GetMapping("/type/{dictType}") + @ResponseBody + public AjaxResult add(@PathVariable("dictType") String dictType) + { + + List data = dictTypeService.selectDictDataByType(dictType); + if (StringUtils.isNull(data)) + { + data = new ArrayList(); + } + return success(data); + } /** * 新增保存字典类型 diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java index 5fe06998..d16e66d3 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java @@ -1,21 +1,11 @@ package com.ruoyi.web.controller.system; -import java.util.Date; -import java.util.List; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.ResponseBody; import com.ruoyi.common.config.RuoYiConfig; import com.ruoyi.common.constant.ShiroConstants; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.core.domain.entity.SysRole; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.text.Convert; import com.ruoyi.common.utils.CookieUtils; @@ -25,6 +15,18 @@ import com.ruoyi.common.utils.StringUtils; import com.ruoyi.framework.shiro.service.SysPasswordService; import com.ruoyi.system.service.ISysConfigService; import com.ruoyi.system.service.ISysMenuService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; +import java.util.*; +import java.util.stream.Collectors; /** * 首页 业务处理 @@ -39,7 +41,6 @@ public class SysIndexController extends BaseController @Autowired private ISysConfigService configService; - @Autowired private SysPasswordService passwordService; @GetMapping("/test") @@ -178,4 +179,164 @@ public class SysIndexController extends BaseController } return false; } + + /** + * 获取用户信息(供前后端分离的前端调用) + * + * @return 用户信息、角色、权限 + */ + @GetMapping("/getInfo") + @ResponseBody + public AjaxResult getInfo() + { + SysUser user = getSysUser(); + if (StringUtils.isNull(user)) + { + return AjaxResult.error("未登录或登录超时"); + } + // 角色集合 + Set roles = user.getRoles().stream().map(SysRole::getRoleKey).collect(Collectors.toSet()); + // 权限集合 + Set permissions = menuService.selectPermsByUserId(user.getUserId()); + + Map data = new HashMap<>(); + data.put("user", user); + data.put("roles", roles); + data.put("permissions", permissions); + data.put("isDefaultModifyPwd", initPasswordIsModify(user.getPwdUpdateDate())); + data.put("isPasswordExpired", passwordIsExpiration(user.getPwdUpdateDate())); + + return AjaxResult.success(data); + } + + /** + * 获取路由信息(供前后端分离的前端调用) + * + * @return 路由信息 + */ + @GetMapping("/getRouters") + @ResponseBody + public AjaxResult getRouters() + { + SysUser user = getSysUser(); + if (StringUtils.isNull(user)) + { + return AjaxResult.error("未登录或登录超时"); + } + + List menus = menuService.selectMenusByUser(user); + List> routerList = buildRouters(menus); + return AjaxResult.success(routerList); + } + + /** + * 构建前端路由所需格式(简化版,使用现有字段) + */ + private List> buildRouters(List menus) + { + List> routers = new ArrayList<>(); + for (SysMenu menu : menus) + { + Map router = new HashMap<>(); + + // 基本属性 + router.put("name", getRouteName(menu)); + router.put("path", getRouterPath(menu)); + router.put("hidden", "1".equals(menu.getVisible())); + router.put("component", getComponent(menu)); + + // 路由元信息 + router.put("meta", buildMeta(menu)); + + // 子路由 + List cMenus = menu.getChildren(); + if (cMenus != null && !cMenus.isEmpty()) + { + router.put("alwaysShow", true); + router.put("redirect", "noRedirect"); + router.put("children", buildRouters(cMenus)); + } + + routers.add(router); + } + return routers; + } + + /** + * 构建路由元信息 + */ + private Map buildMeta(SysMenu menu) + { + Map meta = new HashMap<>(); + meta.put("title", menu.getMenuName()); + meta.put("icon", menu.getVueIcon()); + meta.put("noCache", "1".equals(menu.getIsRefresh())); + return meta; + } + + /** + * 获取路由名称 + */ + private String getRouteName(SysMenu menu) + { + // 使用 path 字段作为路由名称,如果没有则使用 menuName + String routerName = menu.getPath(); + if (StringUtils.isEmpty(routerName)) + { + routerName = menu.getMenuName(); + } + // 首字母大写 + return StringUtils.isNotEmpty(routerName) ? StringUtils.capitalize(routerName) : ""; + } + + /** + * 获取路由地址 + */ + private String getRouterPath(SysMenu menu) + { + // 优先使用 path 字段,如果没有则使用 url 字段 + String routerPath = menu.getPath(); + if (StringUtils.isEmpty(routerPath)) + { + routerPath = menu.getUrl(); + } + + // 一级目录需要加 / + if (menu.getParentId() == 0 && "M".equals(menu.getMenuType())) + { + if (!routerPath.startsWith("/")) + { + routerPath = "/" + routerPath; + } + } + + return StringUtils.isNotEmpty(routerPath) ? routerPath : ""; + } + + /** + * 获取组件信息 + */ + private String getComponent(SysMenu menu) + { + // 目录使用 Layout + if ("M".equals(menu.getMenuType())) + { + if (menu.getParentId() == 0) + { + return "Layout"; + } + else + { + return "ParentView"; + } + } + // 菜单使用 url 字段作为组件路径 + else if ("C".equals(menu.getMenuType())) + { + String component = menu.getComponent(); + return StringUtils.isNotEmpty(component) ? component : menu.getPath(); + } + + return "Layout"; + } } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java index 1dd7b93b..35e4490d 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java @@ -6,11 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.*; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; @@ -52,6 +48,15 @@ public class SysMenuController extends BaseController List menuList = menuService.selectMenuList(menu, userId); return menuList; } + @RequiresPermissions("system:menu:list") + @GetMapping("/list") + @ResponseBody + public AjaxResult listMenu(SysMenu menu) + { + Long userId = ShiroUtils.getUserId(); + List menuList = menuService.selectMenuList(menu, userId); + return AjaxResult.success(menuList); + } /** * 删除菜单 @@ -73,6 +78,23 @@ public class SysMenuController extends BaseController AuthorizationUtils.clearAllCachedAuthorizationInfo(); return toAjax(menuService.deleteMenuById(menuId)); } + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @RequiresPermissions("system:menu:remove") + @DeleteMapping("/{menuId}") + @ResponseBody + public AjaxResult delete(@PathVariable("menuId") Long menuId) + { + if (menuService.selectCountMenuByParentId(menuId) > 0) + { + return AjaxResult.warn("存在子菜单,不允许删除"); + } + if (menuService.selectCountRoleMenuByMenuId(menuId) > 0) + { + return AjaxResult.warn("菜单已分配,不允许删除"); + } + AuthorizationUtils.clearAllCachedAuthorizationInfo(); + return toAjax(menuService.deleteMenuById(menuId)); + } /** * 新增 @@ -113,6 +135,20 @@ public class SysMenuController extends BaseController AuthorizationUtils.clearAllCachedAuthorizationInfo(); return toAjax(menuService.insertMenu(menu)); } + @Log(title = "菜单管理", businessType = BusinessType.INSERT) + @RequiresPermissions("system:menu:add") + @PostMapping() + @ResponseBody + public AjaxResult insert(@RequestBody @Validated SysMenu menu) + { + if (!menuService.checkMenuNameUnique(menu)) + { + return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + menu.setCreateBy(getLoginName()); + AuthorizationUtils.clearAllCachedAuthorizationInfo(); + return toAjax(menuService.insertMenu(menu)); + } /** * 修改菜单 @@ -124,6 +160,30 @@ public class SysMenuController extends BaseController mmap.put("menu", menuService.selectMenuById(menuId)); return prefix + "/edit"; } + @RequiresPermissions("system:menu:edit") + @GetMapping("/{menuId}") + @ResponseBody + public AjaxResult query(@PathVariable("menuId") Long menuId) + { + + return AjaxResult.success(menuService.selectMenuById(menuId)); + } + @Log(title = "菜单管理", businessType = BusinessType.UPDATE) + @RequiresPermissions("system:menu:edit") + @PutMapping() + @ResponseBody + public AjaxResult edit(@RequestBody @Validated SysMenu menu) + { + + if (!menuService.checkMenuNameUnique(menu)) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + menu.setUpdateBy(getLoginName()); + AuthorizationUtils.clearAllCachedAuthorizationInfo(); + return toAjax(menuService.updateMenu(menu)); + } + /** * 修改保存菜单 @@ -195,4 +255,17 @@ public class SysMenuController extends BaseController mmap.put("menu", menuService.selectMenuById(menuId)); return prefix + "/tree"; } + + /** + * 获取路由信息 + * + * @return 路由信息 + */ + @GetMapping("/getRouters") + @ResponseBody + public AjaxResult getRouters() + { + List menus = menuService.selectMenusByUser(getSysUser()); + return AjaxResult.success(menus); + } } \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java index 797f05f2..84c66b6c 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java @@ -363,4 +363,31 @@ public class SysUserController extends BaseController mmap.put("dept", value==null?new SysDept():value); return prefix + "/deptTree"; } + + + // ----------------------VUE页面使用--------------------------- + @RequiresPermissions("system:user:list") + @GetMapping("/list") + @ResponseBody + public TableDataInfo listPage(SysUser user) { + startPage(); + List list = userService.selectUserList(user); + return getDataTable(list); + } + @RequiresPermissions("system:user:list") + @GetMapping("/{userId}") + @ResponseBody + public AjaxResult query(@PathVariable("userId") Long userId) { + return success(userService.selectUserById(userId)); + } + + /** + * 获取部门树列表 + */ + @RequiresPermissions("system:user:list") + @GetMapping("/deptTree") + @ResponseBody + public AjaxResult deptTree(SysDept dept) { + return success(deptService.selectDeptTreeList(dept)); + } } \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java new file mode 100644 index 00000000..5d4cb0db --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java @@ -0,0 +1,94 @@ +package com.ruoyi.common.core.domain; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.utils.StringUtils; + +import java.io.Serializable; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Treeselect树结构实体类 + * + * @author ruoyi + */ +public class TreeSelect implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 节点ID */ + private Long id; + + /** 节点名称 */ + private String label; + + /** 节点禁用 */ + private boolean disabled = false; + + /** 子节点 */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List children; + + public TreeSelect() + { + + } + + public TreeSelect(SysDept dept) + { + this.id = dept.getDeptId(); + this.label = dept.getDeptName(); + this.disabled = StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()); + this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + public TreeSelect(SysMenu menu) + { + this.id = menu.getMenuId(); + this.label = menu.getMenuName(); + this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + public Long getId() + { + return id; + } + + public void setId(Long id) + { + this.id = id; + } + + public String getLabel() + { + return label; + } + + public void setLabel(String label) + { + this.label = label; + } + + public boolean isDisabled() + { + return disabled; + } + + public void setDisabled(boolean disabled) + { + this.disabled = disabled; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java index a3a25d09..1d945b3f 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java @@ -9,6 +9,9 @@ import org.apache.commons.lang3.builder.ToStringStyle; import com.fasterxml.jackson.annotation.JsonIgnore; import com.ruoyi.common.core.domain.BaseEntity; +import java.util.ArrayList; +import java.util.List; + /** * 部门表 sys_dept * @@ -53,7 +56,8 @@ public class SysDept extends BaseEntity /** 排除编号 */ private Long excludeId; - + /** 子部门 */ + private List children = new ArrayList(); public Long getDeptId() { return deptId; @@ -181,6 +185,14 @@ public class SysDept extends BaseEntity this.excludeId = excludeId; } + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + @Override public String toString() { return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java index da1869a5..83c9269c 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java @@ -3,6 +3,9 @@ package com.ruoyi.common.core.domain.entity; import java.util.List; import java.util.ArrayList; import javax.validation.constraints.*; + +import lombok.Data; +import lombok.ToString; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import com.ruoyi.common.core.domain.BaseEntity; @@ -12,6 +15,7 @@ import com.ruoyi.common.core.domain.BaseEntity; * * @author ruoyi */ +@ToString public class SysMenu extends BaseEntity { private static final long serialVersionUID = 1L; @@ -52,6 +56,23 @@ public class SysMenu extends BaseEntity /** 菜单图标 */ private String icon; + /** 路由地址 */ + private String path; + + + /** 组件路径 */ + private String component; + + /** 路由参数 */ + private String query; + + /** 是否为外链(0是 1否) */ + private String isFrame; + + /** 菜单状态(0正常 1停用) */ + private String status; + /** 菜单图标 */ + private String vueIcon; /** 子菜单 */ private List children = new ArrayList(); @@ -181,6 +202,59 @@ public class SysMenu extends BaseEntity this.icon = icon; } + public String getPath() + { + return path; + } + + public void setPath(String path) + { + this.path = path; + } + + + public String getComponent() { + return component; + } + + public void setComponent(String component) { + this.component = component; + } + + public String getQuery() { + return query; + } + + public void setQuery(String query) { + this.query = query; + } + + public String getIsFrame() { + return isFrame; + } + + public void setIsFrame(String isFrame) { + this.isFrame = isFrame; + } + + + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getVueIcon() { + return vueIcon; + } + + public void setVueIcon(String vueIcon) { + this.vueIcon = vueIcon; + } + public List getChildren() { return children; @@ -191,24 +265,5 @@ public class SysMenu extends BaseEntity this.children = children; } - @Override - public String toString() { - return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) - .append("menuId", getMenuId()) - .append("menuName", getMenuName()) - .append("parentId", getParentId()) - .append("orderNum", getOrderNum()) - .append("url", getUrl()) - .append("target", getTarget()) - .append("menuType", getMenuType()) - .append("visible", getVisible()) - .append("perms", getPerms()) - .append("icon", getIcon()) - .append("createBy", getCreateBy()) - .append("createTime", getCreateTime()) - .append("updateBy", getUpdateBy()) - .append("updateTime", getUpdateTime()) - .append("remark", getRemark()) - .toString(); - } + } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java index 0998732a..a6b825d0 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java @@ -1,6 +1,8 @@ package com.ruoyi.system.service; import java.util.List; + +import com.ruoyi.common.core.domain.TreeSelect; import com.ruoyi.common.core.domain.Ztree; import com.ruoyi.common.core.domain.entity.SysDept; import com.ruoyi.common.core.domain.entity.SysRole; @@ -114,4 +116,19 @@ public interface ISysDeptService * @param deptId 部门id */ public void checkDeptDataScope(Long deptId); + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * @return 树结构列表 + */ + public List buildDeptTree(List depts); + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + public List buildDeptTreeSelect(List depts); + List selectDeptTreeList(SysDept dept); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java index e7ee0c9d..33d941e2 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java @@ -1,7 +1,12 @@ package com.ruoyi.system.service.impl; import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; import java.util.List; +import java.util.stream.Collectors; + +import com.ruoyi.common.core.domain.TreeSelect; import org.apache.commons.lang3.ArrayUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -325,4 +330,89 @@ public class SysDeptServiceImpl implements ISysDeptService } } } + + @Override + public List selectDeptTreeList(SysDept dept) { + List depts = SpringUtils.getAopProxy(this).selectDeptList(dept); + return buildDeptTreeSelect(depts); + } + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + @Override + public List buildDeptTreeSelect(List depts) + { + List deptTrees = buildDeptTree(depts); + return deptTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * @return 树结构列表 + */ + @Override + public List buildDeptTree(List depts) + { + List returnList = new ArrayList(); + List tempList = depts.stream().map(SysDept::getDeptId).collect(Collectors.toList()); + for (SysDept dept : depts) + { + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.contains(dept.getParentId())) + { + recursionFn(depts, dept); + returnList.add(dept); + } + } + if (returnList.isEmpty()) + { + returnList = depts; + } + return returnList; + } + /** + * 递归列表 + */ + private void recursionFn(List list, SysDept t) + { + // 得到子节点列表 + List childList = getChildList(list, t); + t.setChildren(childList); + for (SysDept tChild : childList) + { + if (hasChild(list, tChild)) + { + recursionFn(list, tChild); + } + } + } + /** + * 得到子节点列表 + */ + private List getChildList(List list, SysDept t) + { + List tlist = new ArrayList(); + Iterator it = list.iterator(); + while (it.hasNext()) + { + SysDept n = (SysDept) it.next(); + if (StringUtils.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getDeptId().longValue()) + { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List list, SysDept t) + { + return getChildList(list, t).size() > 0; + } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java index 26020c2c..22e374bf 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java @@ -118,6 +118,9 @@ public class SysMenuServiceImpl implements ISysMenuService permsSet.addAll(Arrays.asList(perm.trim().split(","))); } } + if (userId==1){ + permsSet.add("*:*:*"); + } return permsSet; } diff --git a/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml index be4ef1a4..15ee4cff 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml @@ -25,12 +25,12 @@ - select menu_id, menu_name, parent_id, order_num, url, target, menu_type, visible, is_refresh, ifnull(perms,'') as perms, icon, create_by, create_time + select menu_id, menu_name, parent_id, order_num, url, target, menu_type, visible, is_refresh, ifnull(perms,'') as perms, icon, create_by, create_time ,path,component,query,status,vue_icon,is_frame from sys_menu - SELECT t.menu_id, t.parent_id, t.menu_name, t.order_num, t.url, t.target, t.menu_type, t.visible, t.is_refresh, t.perms, t.icon, t.remark, + SELECT t.menu_id, t.parent_id, t.menu_name, t.order_num, t.url, t.target, t.menu_type, t.visible, t.is_refresh, t.perms, t.icon, t.remark,t.path,t.component,t.query,t.status,t.vue_icon,t.is_frame, (SELECT menu_name FROM sys_menu WHERE menu_id = t.parent_id) parent_name FROM sys_menu t where t.menu_id = #{menuId} @@ -149,6 +149,12 @@ icon = #{icon}, remark = #{remark}, update_by = #{updateBy}, + path = #{path}, + component = #{component}, + is_frame = #{isFrame}, + query = #{query}, + status = #{status}, + vue_icon = #{vueIcon}, update_time = sysdate() where menu_id = #{menuId} @@ -169,6 +175,12 @@ icon, remark, create_by, + path , + component , + is_frame, + query , + status , + vue_icon, create_time )values( #{menuId}, @@ -184,6 +196,12 @@ #{icon}, #{remark}, #{createBy}, + #{path}, + #{component}, + #{isFrame}, + #{query}, + #{status}, + #{vueIcon}, sysdate() )