From 46444bd0feacd7652522debda65ad4d88042b667 Mon Sep 17 00:00:00 2001 From: RuoYi <yzz_ivy@163.com> Date: 星期二, 08 十月 2019 09:14:38 +0800 Subject: [PATCH] RuoYi-Vue 1.0 --- ruoyi-ui/.eslintignore | 10 ruoyi-ui/src/components/ThemePicker/index.vue | 175 ruoyi-ui/src/utils/scroll-to.js | 58 ruoyi-ui/src/layout/components/index.js | 5 ruoyi-ui/src/assets/icons/svg/international.svg | 1 ruoyi-ui/src/assets/icons/svg/druid.svg | 1 ruoyi-ui/src/assets/icons/svg/eye.svg | 1 ruoyi-ui/src/assets/styles/index.scss | 191 ruoyi/src/main/java/com/ruoyi/project/system/service/ISysPostService.java | 83 ruoyi/src/main/resources/mybatis/system/SysRoleMapper.xml | 137 ruoyi-ui/package.json | 102 ruoyi-ui/src/api/system/dict/type.js | 44 ruoyi-ui/src/assets/icons/svg/system.svg | 2 ruoyi-ui/src/assets/icons/svg/tree.svg | 1 ruoyi-ui/src/assets/icons/svg/bug.svg | 1 ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/DataScope.java | 28 ruoyi-ui/src/layout/components/TagsView/ScrollPane.vue | 85 ruoyi/src/main/resources/mybatis/system/SysPostMapper.xml | 115 ruoyi-ui/src/assets/icons/svg/pdf.svg | 1 ruoyi-ui/src/views/system/dept/index.vue | 307 ruoyi/src/main/java/com/ruoyi/common/utils/DateUtils.java | 155 ruoyi-ui/build/index.js | 35 ruoyi/src/main/java/com/ruoyi/framework/security/service/PermissionService.java | 169 ruoyi-ui/src/assets/icons/svg/monitor.svg | 2 ruoyi/src/main/java/com/ruoyi/framework/config/FilterConfig.java | 46 ruoyi-ui/.env.development | 8 ruoyi-ui/src/assets/icons/svg/job.svg | 1 ruoyi/src/main/java/com/ruoyi/common/constant/Constants.java | 96 ruoyi-ui/src/store/modules/user.js | 96 ruoyi-ui/src/components/Editor/index.vue | 226 ruoyi-ui/src/store/index.js | 23 ruoyi/src/main/java/com/ruoyi/project/system/service/ISysMenuService.java | 127 ruoyi-ui/src/assets/icons/svg/date.svg | 1 ruoyi-ui/src/views/system/menu/index.vue | 337 ruoyi/src/main/resources/mybatis/system/SysDictDataMapper.xml | 116 ruoyi-ui/src/assets/icons/svg/guide.svg | 1 ruoyi-ui/src/api/system/user.js | 109 ruoyi-ui/src/views/system/log/index.vue | 3 ruoyi-ui/src/assets/icons/svg/validCode.svg | 1 ruoyi/src/main/java/com/ruoyi/common/core/text/StrFormatter.java | 92 ruoyi/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java | 34 ruoyi/src/main/java/com/ruoyi/project/system/controller/SysProfileController.java | 106 ruoyi/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java | 26 ruoyi-ui/src/assets/404_images/404.png | 0 ruoyi-ui/src/assets/icons/svg/lock.svg | 1 ruoyi/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java | 105 ruoyi-ui/src/assets/image/profile.jpg | 0 ruoyi-ui/src/api/menu.js | 9 ruoyi-ui/src/components/Screenfull/index.vue | 60 ruoyi/src/main/java/com/ruoyi/project/system/controller/SysUserController.java | 153 ruoyi/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java | 16 ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDictDataServiceImpl.java | 105 ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysPostServiceImpl.java | 141 ruoyi/src/main/java/com/ruoyi/framework/config/DruidConfig.java | 106 ruoyi-ui/src/assets/icons/svg/user.svg | 1 ruoyi-ui/src/assets/icons/svg/tree-table.svg | 1 ruoyi/src/main/java/com/ruoyi/project/common/CommonController.java | 46 ruoyi-ui/src/views/components/icons/element-icons.js | 74 ruoyi/src/main/java/com/ruoyi/framework/web/page/PageDomain.java | 69 ruoyi/src/main/java/com/ruoyi/project/system/domain/vo/MetaVo.java | 49 ruoyi-ui/src/assets/icons/svg/form.svg | 1 ruoyi/src/main/java/com/ruoyi/project/system/domain/SysDictData.java | 145 ruoyi-ui/src/assets/icons/svg/edit.svg | 1 ruoyi/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java | 66 ruoyi-ui/src/assets/image/login-background.jpg | 0 ruoyi-ui/src/assets/styles/element-variables.scss | 31 ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysDictTypeMapper.java | 77 ruoyi/src/main/java/com/ruoyi/project/system/service/ISysDictDataService.java | 69 LICENSE | 27 ruoyi-ui/src/views/system/user/profile/userInfo.vue | 80 ruoyi-ui/src/store/modules/tagsView.js | 165 ruoyi/src/main/java/com/ruoyi/common/utils/IdUtils.java | 51 ruoyi/src/main/java/com/ruoyi/project/common/CaptchaController.java | 62 ruoyi-ui/src/layout/components/Sidebar/index.vue | 54 ruoyi-ui/src/assets/icons/svg/drag.svg | 1 ruoyi/src/main/java/com/ruoyi/common/utils/Arith.java | 114 ruoyi/src/main/java/com/ruoyi/project/system/service/ISysNoticeService.java | 52 ruoyi-ui/bin/package.bat | 12 ruoyi-ui/src/layout/components/Navbar.vue | 181 ruoyi/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java | 16 ruoyi/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java | 248 ruoyi-ui/src/components/RightPanel/index.vue | 149 ruoyi-ui/src/layout/components/Sidebar/Logo.vue | 84 ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java | 84 ruoyi-ui/src/api/system/menu.js | 60 ruoyi-ui/src/views/monitor/online/index.vue | 5 ruoyi/src/main/java/com/ruoyi/framework/config/RuoYiConfig.java | 116 ruoyi/src/main/java/com/ruoyi/framework/security/service/SysPermissionService.java | 66 ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java | 61 ruoyi-ui/src/assets/icons/svg/theme.svg | 1 ruoyi-ui/src/assets/icons/svgo.yml | 22 ruoyi/src/main/java/com/ruoyi/common/constant/UserConstants.java | 39 ruoyi-ui/src/store/modules/permission.js | 53 ruoyi/src/main/resources/logback.xml | 93 ruoyi-ui/.gitignore | 23 ruoyi-ui/src/views/login/auth-redirect.vue | 15 ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysUserMapper.java | 105 ruoyi-ui/src/components/Hamburger/index.vue | 44 ruoyi-ui/src/assets/icons/svg/eye-open.svg | 1 ruoyi/src/main/java/com/ruoyi/project/system/service/ISysConfigService.java | 68 ruoyi/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java | 77 ruoyi-ui/src/assets/styles/btn.scss | 99 ruoyi-ui/src/api/monitor/server.js | 9 ruoyi-ui/src/assets/icons/svg/documentation.svg | 1 ruoyi-ui/src/assets/icons/svg/code.svg | 1 ruoyi/bin/clean.bat | 12 ruoyi-ui/src/assets/icons/index.js | 9 ruoyi/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java | 26 ruoyi/src/main/java/com/ruoyi/project/system/domain/SysUser.java | 270 ruoyi/src/main/java/com/ruoyi/common/enums/UserStatus.java | 30 ruoyi-ui/src/views/tool/build/index.vue | 5 ruoyi/src/main/java/com/ruoyi/project/system/domain/SysMenu.java | 184 ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysDictDataMapper.java | 87 ruoyi/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java | 44 ruoyi/src/main/java/com/ruoyi/project/monitor/service/ISysOperLogService.java | 48 ruoyi-ui/src/assets/icons/svg/phone.svg | 1 ruoyi-ui/src/layout/components/Settings/index.vue | 108 ruoyi-ui/.editorconfig | 22 ruoyi-ui/src/store/getters.js | 15 ruoyi-ui/src/views/tool/swagger/index.vue | 25 ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDictTypeController.java | 99 ruoyi/src/main/resources/mybatis/monitor/SysLogininforMapper.xml | 56 ruoyi/src/main/java/com/ruoyi/framework/manager/AsyncManager.java | 55 ruoyi-ui/src/assets/icons/svg/qq.svg | 1 ruoyi-ui/src/components/RuoYi/index.vue | 21 ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysLogininforController.java | 34 ruoyi-ui/src/views/system/user/profile/userAvatar.vue | 135 ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysRoleServiceImpl.java | 275 ruoyi-ui/src/assets/icons/svg/logininfor.svg | 1 ruoyi-ui/src/directive/permission/index.js | 15 ruoyi-ui/src/assets/logo/logo.png | 0 ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysDeptMapper.java | 109 ruoyi/src/main/java/com/ruoyi/project/system/service/ISysDeptService.java | 101 ruoyi-ui/src/utils/request.js | 65 ruoyi/src/main/java/com/ruoyi/common/utils/sign/Base64.java | 291 ruoyi/src/main/java/com/ruoyi/project/monitor/domain/SysOperLog.java | 235 ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysMenuServiceImpl.java | 359 ruoyi-ui/src/components/SvgIcon/index.vue | 61 ruoyi/src/main/java/com/ruoyi/project/system/domain/SysNotice.java | 78 ruoyi/src/main/java/com/ruoyi/common/exception/user/UserException.java | 18 ruoyi/src/main/java/com/ruoyi/project/system/controller/SysRoleController.java | 143 ruoyi/src/main/java/com/ruoyi/common/utils/ServletUtils.java | 136 ruoyi/src/main/java/com/ruoyi/project/system/domain/vo/RouterVo.java | 133 ruoyi-ui/src/store/modules/app.js | 56 ruoyi-ui/src/views/dashboard/RaddarChart.vue | 116 ruoyi-ui/src/views/system/user/index.vue | 515 ruoyi/src/main/java/com/ruoyi/framework/web/domain/Server.java | 241 ruoyi-ui/src/assets/icons/svg/online.svg | 1 ruoyi/ry.sh | 86 ruoyi-ui/src/views/components/icons/index.vue | 87 ruoyi/src/main/resources/banner.txt | 24 ruoyi/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java | 16 ruoyi-ui/src/assets/icons/svg/component.svg | 1 ruoyi-ui/src/assets/icons/svg/clipboard.svg | 1 ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/enums/BusinessType.java | 59 ruoyi-ui/src/components/IconSelect/requireIcons.js | 11 ruoyi-ui/src/layout/components/Sidebar/SidebarItem.vue | 93 ruoyi/src/main/resources/mybatis/system/SysUserMapper.xml | 174 ruoyi/src/main/java/com/ruoyi/framework/web/domain/AjaxResult.java | 148 ruoyi/src/main/java/com/ruoyi/common/exception/BaseException.java | 97 ruoyi/src/main/java/com/ruoyi/project/system/service/ISysRoleService.java | 115 ruoyi-ui/vue.config.js | 130 ruoyi-ui/src/assets/icons/svg/server.svg | 1 README.md | 107 ruoyi-ui/src/assets/styles/mixin.scss | 66 ruoyi/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java | 76 ruoyi-ui/src/layout/index.vue | 102 ruoyi/src/main/java/com/ruoyi/framework/config/ServerConfig.java | 32 ruoyi-ui/src/assets/icons/svg/message.svg | 1 ruoyi-ui/bin/run-web.bat | 12 ruoyi/src/main/java/com/ruoyi/framework/config/RedisConfig.java | 43 ruoyi-ui/src/views/dashboard/PieChart.vue | 79 ruoyi-ui/src/views/error/401.vue | 99 ruoyi-ui/src/store/modules/settings.js | 34 ruoyi-ui/README.md | 30 ruoyi-ui/src/layout/components/Sidebar/FixiOSBug.js | 25 ruoyi-ui/src/layout/components/Sidebar/Link.vue | 36 ruoyi-ui/src/assets/icons/svg/star.svg | 1 ruoyi-ui/src/views/system/dict/data.vue | 276 ruoyi-ui/src/settings.js | 31 ruoyi/src/main/java/com/ruoyi/common/core/lang/UUID.java | 481 ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysRoleMapper.java | 100 ruoyi-ui/public/favicon.ico | 0 ruoyi-ui/src/views/monitor/server/index.vue | 209 ruoyi/src/main/java/com/ruoyi/project/system/controller/SysPostController.java | 118 ruoyi-ui/src/views/components/icons/svg-icons.js | 10 ruoyi/src/main/resources/mybatis/system/SysDictTypeMapper.xml | 98 ruoyi/src/main/java/com/ruoyi/framework/security/service/TokenService.java | 179 ruoyi/src/main/java/com/ruoyi/project/system/domain/SysUserRole.java | 46 ruoyi/src/main/java/com/ruoyi/project/system/domain/SysDept.java | 171 ruoyi-ui/src/assets/styles/element-ui.scss | 79 ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDeptController.java | 133 ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysUserServiceImpl.java | 363 ruoyi-ui/src/views/dashboard/mixins/resize.js | 52 ruoyi/src/main/java/com/ruoyi/common/utils/security/Md5Utils.java | 66 ruoyi/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java | 160 ruoyi/src/main/java/com/ruoyi/project/monitor/domain/SysLogininfor.java | 131 ruoyi-ui/src/views/dashboard/LineChart.vue | 135 ruoyi/src/main/resources/mybatis/system/SysUserPostMapper.xml | 34 ruoyi-ui/src/views/system/dict/index.vue | 275 ruoyi-ui/src/assets/icons/svg/build.svg | 1 ruoyi-ui/src/views/system/user/profile/index.vue | 101 ruoyi/src/main/java/com/ruoyi/project/monitor/controller/ServerController.java | 28 ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDeptServiceImpl.java | 289 ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysNoticeMapper.java | 52 ruoyi-ui/src/api/system/dept.js | 60 ruoyi-ui/src/assets/icons/svg/icon.svg | 1 ruoyi-ui/src/layout/components/TagsView/index.vue | 286 ruoyi/src/main/resources/mybatis/monitor/SysOperLogMapper.xml | 82 ruoyi/src/main/resources/mybatis/system/SysDeptMapper.xml | 151 ruoyi-ui/src/assets/icons/svg/example.svg | 1 ruoyi-ui/src/views/system/post/index.vue | 252 ruoyi/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java | 69 ruoyi-ui/src/assets/styles/sidebar.scss | 209 ruoyi-ui/src/api/system/post.js | 44 ruoyi-ui/src/components/IconSelect/index.vue | 69 ruoyi/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java | 55 ruoyi-ui/src/assets/icons/svg/excel.svg | 1 ruoyi-ui/src/assets/401_images/401.gif | 0 ruoyi/src/main/java/com/ruoyi/project/system/domain/SysPost.java | 98 ruoyi/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java | 51 ruoyi/src/main/java/com/ruoyi/project/system/controller/SysNoticeController.java | 91 ruoyi-ui/src/components/PanThumb/index.vue | 142 ruoyi-ui/src/utils/index.js | 317 ruoyi-ui/src/assets/icons/svg/link.svg | 1 ruoyi-ui/src/assets/icons/svg/email.svg | 1 ruoyi-ui/src/views/system/role/index.vue | 478 ruoyi/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java | 45 ruoyi-ui/src/assets/icons/svg/people.svg | 1 ruoyi/src/main/java/com/ruoyi/common/exception/UtilException.java | 26 ruoyi/sql/ry_20191008.sql | 693 + ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java | 114 ruoyi/src/main/resources/mybatis/system/SysConfigMapper.xml | 105 ruoyi/src/main/java/com/ruoyi/framework/web/domain/BaseEntity.java | 131 ruoyi-ui/src/api/login.js | 40 ruoyi-ui/src/assets/icons/svg/log.svg | 1 ruoyi-ui/src/assets/icons/svg/nested.svg | 1 ruoyi/src/main/resources/mybatis/system/SysUserRoleMapper.xml | 44 ruoyi-ui/src/permission.js | 64 ruoyi-ui/src/views/monitor/druid/index.vue | 25 ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysMenuMapper.java | 108 ruoyi-ui/src/assets/icons/svg/chart.svg | 1 ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java | 122 ruoyi-ui/src/views/login.vue | 203 ruoyi-ui/src/api/system/config.js | 52 ruoyi-ui/src/views/redirect.vue | 12 ruoyi/src/main/resources/application.yml | 109 ruoyi/src/main/resources/mybatis/system/SysNoticeMapper.xml | 82 ruoyi/src/main/java/com/ruoyi/project/system/service/ISysUserService.java | 149 ruoyi/src/main/java/com/ruoyi/common/utils/SecurityUtils.java | 90 ruoyi/src/main/resources/mybatis/mybatis-config.xml | 15 ruoyi-ui/src/views/error/404.vue | 228 ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/enums/BusinessStatus.java | 20 ruoyi/src/main/java/com/ruoyi/project/monitor/service/ISysLogininforService.java | 40 ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysUserPostMapper.java | 44 ruoyi-ui/.env.production | 5 ruoyi-ui/.env.staging | 7 ruoyi-ui/src/main.js | 69 ruoyi-ui/src/assets/404_images/404_cloud.png | 0 ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysConfigServiceImpl.java | 117 ruoyi-ui/src/router/index.js | 101 ruoyi-ui/src/assets/icons/svg/education.svg | 1 ruoyi-ui/src/assets/styles/transition.scss | 48 ruoyi-ui/public/index.html | 207 ruoyi/src/main/java/com/ruoyi/common/exception/DemoModeException.java | 15 ruoyi-ui/src/assets/icons/svg/table.svg | 1 ruoyi-ui/src/layout/mixin/ResizeHandler.js | 45 ruoyi/src/main/java/com/ruoyi/RuoYiApplication.java | 30 ruoyi/src/main/java/com/ruoyi/framework/security/LoginUser.java | 168 ruoyi-ui/src/components/SizeSelect/index.vue | 57 ruoyi-ui/src/layout/components/AppMain.vue | 57 ruoyi-ui/src/assets/icons/svg/tool.svg | 1 ruoyi-ui/src/views/dashboard/PanelGroup.vue | 181 ruoyi/src/main/java/com/ruoyi/framework/web/page/TableDataInfo.java | 85 ruoyi/src/main/java/com/ruoyi/project/system/controller/SysMenuController.java | 130 ruoyi/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java | 39 ruoyi/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java | 71 ruoyi-ui/src/assets/icons/svg/money.svg | 1 ruoyi/src/main/java/com/ruoyi/project/system/domain/SysConfig.java | 86 ruoyi/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java | 20 ruoyi-ui/src/components/Breadcrumb/index.vue | 81 ruoyi-ui/src/assets/styles/variables.scss | 35 ruoyi/src/main/java/com/ruoyi/project/monitor/service/impl/SysLogininforServiceImpl.java | 66 ruoyi-ui/src/assets/icons/svg/skill.svg | 1 ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysOperlogController.java | 34 ruoyi/src/main/java/com/ruoyi/common/core/text/CharsetKit.java | 86 ruoyi/src/main/java/com/ruoyi/common/xss/XssHttpServletRequestWrapper.java | 105 ruoyi-ui/src/assets/icons/svg/tab.svg | 1 ruoyi/pom.xml | 267 ruoyi/src/main/java/com/ruoyi/common/utils/file/FileUtils.java | 142 ruoyi/src/main/java/com/ruoyi/framework/redis/RedisCache.java | 200 ruoyi/src/main/resources/i18n/messages.properties | 36 ruoyi-ui/src/assets/icons/svg/password.svg | 1 ruoyi/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java | 93 ruoyi/src/main/java/com/ruoyi/project/system/domain/SysUserPost.java | 46 ruoyi-ui/src/assets/icons/svg/wechat.svg | 1 ruoyi/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java | 16 ruoyi-ui/src/assets/icons/svg/dashboard.svg | 1 ruoyi/src/main/java/com/ruoyi/common/constant/HttpStatus.java | 89 ruoyi/src/main/java/com/ruoyi/framework/web/domain/TreeSelect.java | 77 ruoyi/src/main/java/com/ruoyi/common/utils/MessageUtils.java | 26 ruoyi/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java | 36 ruoyi-ui/src/assets/icons/svg/size.svg | 1 ruoyi/src/main/java/com/ruoyi/common/exception/CustomException.java | 43 ruoyi-ui/src/directive/permission/hasRole.js | 28 ruoyi-ui/src/assets/icons/svg/shopping.svg | 1 ruoyi-ui/src/utils/ruoyi.js | 76 ruoyi/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java | 114 ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysPostMapper.java | 91 ruoyi-ui/src/utils/permission.js | 24 ruoyi/src/main/java/com/ruoyi/framework/web/page/TableSupport.java | 49 ruoyi-ui/src/assets/icons/svg/dict.svg | 1 ruoyi/src/main/java/com/ruoyi/common/utils/VerifyCodeUtils.java | 226 ruoyi/src/main/java/com/ruoyi/framework/security/service/UserDetailsServiceImpl.java | 60 ruoyi/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java | 62 ruoyi-ui/src/utils/auth.js | 15 ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysUserRoleMapper.java | 62 ruoyi-ui/src/components/Pagination/index.vue | 101 ruoyi/src/main/java/com/ruoyi/common/xss/XssFilter.java | 97 ruoyi-ui/src/views/tool/gen/index.vue | 5 ruoyi/src/main/java/com/ruoyi/project/system/service/ISysDictTypeService.java | 75 ruoyi-ui/src/views/dashboard/BarChart.vue | 102 ruoyi-ui/src/api/system/notice.js | 44 ruoyi-ui/src/views/index.vue | 98 ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/enums/DataSourceType.java | 19 ruoyi-ui/src/assets/icons/svg/zip.svg | 1 ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDictTypeServiceImpl.java | 130 ruoyi-ui/src/assets/icons/svg/404.svg | 1 ruoyi/src/main/java/com/ruoyi/RuoYiServletInitializer.java | 18 ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysConfigMapper.java | 60 ruoyi/src/main/java/com/ruoyi/framework/security/service/SysLoginService.java | 88 ruoyi/src/main/resources/application-druid.yml | 57 ruoyi-ui/src/assets/icons/svg/exit-fullscreen.svg | 1 ruoyi/src/main/java/com/ruoyi/common/exception/file/FileException.java | 19 ruoyi-ui/src/utils/validate.js | 83 ruoyi/src/main/resources/mybatis/system/SysRoleDeptMapper.xml | 34 ruoyi-ui/src/api/monitor/operlog.js | 10 ruoyi/src/main/java/com/ruoyi/framework/web/controller/BaseController.java | 85 ruoyi/src/main/resources/mybatis/system/SysRoleMenuMapper.xml | 27 ruoyi-ui/src/assets/styles/ruoyi.scss | 126 ruoyi-ui/src/views/monitor/operlog/index.vue | 226 ruoyi/src/main/java/com/ruoyi/project/tool/swagger/TestController.java | 175 ruoyi/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java | 41 ruoyi-ui/src/layout/components/Sidebar/Item.vue | 29 ruoyi-ui/src/api/monitor/logininfor.js | 10 ruoyi/src/main/java/com/ruoyi/project/system/domain/SysRole.java | 163 ruoyi-ui/src/directive/permission/hasPermi.js | 28 ruoyi/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java | 204 ruoyi/src/main/java/com/ruoyi/framework/config/SecurityConfig.java | 133 ruoyi/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java | 189 ruoyi-ui/src/assets/icons/svg/fullscreen.svg | 1 ruoyi/src/main/java/com/ruoyi/common/utils/LogUtils.java | 18 ruoyi/src/main/java/com/ruoyi/project/system/domain/SysRoleMenu.java | 46 ruoyi-ui/src/assets/icons/svg/post.svg | 1 ruoyi/bin/run-tomcat.bat | 14 ruoyi-ui/src/assets/icons/svg/swagger.svg | 1 ruoyi/src/main/java/com/ruoyi/project/system/domain/SysRoleDept.java | 46 ruoyi/src/main/java/com/ruoyi/project/system/controller/SysLoginController.java | 94 ruoyi-ui/src/views/system/notice/index.vue | 292 ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDictDataController.java | 100 ruoyi-ui/.eslintrc.js | 199 ruoyi/.gitignore | 43 ruoyi/bin/package.bat | 12 ruoyi-ui/src/views/monitor/job/index.vue | 5 ruoyi-ui/src/views/system/config/index.vue | 270 ruoyi-ui/src/assets/icons/svg/peoples.svg | 1 ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/DataSource.java | 26 ruoyi/src/main/java/com/ruoyi/project/monitor/mapper/SysOperLogMapper.java | 48 ruoyi/src/main/java/com/ruoyi/project/monitor/service/impl/SysOperLogServiceImpl.java | 77 ruoyi/src/main/java/com/ruoyi/common/utils/StringUtils.java | 453 ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysRoleMenuMapper.java | 36 ruoyi/src/main/java/com/ruoyi/common/enums/HttpMethod.java | 36 ruoyi/src/main/java/com/ruoyi/project/system/domain/SysDictType.java | 71 ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysRoleDeptMapper.java | 44 ruoyi-ui/src/api/system/dict/data.js | 52 ruoyi/src/main/java/com/ruoyi/project/system/controller/SysConfigController.java | 110 ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java | 80 ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/enums/OperatorType.java | 24 ruoyi-ui/src/assets/icons/svg/search.svg | 1 ruoyi-ui/src/views/login/components/SocialSignin.vue | 72 ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Log.java | 41 ruoyi-ui/src/assets/icons/svg/language.svg | 1 ruoyi/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java | 101 ruoyi/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java | 152 ruoyi/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java | 243 ruoyi/src/main/java/com/ruoyi/common/core/text/Convert.java | 999 + ruoyi-ui/babel.config.js | 5 ruoyi-ui/src/views/monitor/logininfor/index.vue | 138 ruoyi-ui/src/assets/icons/svg/download.svg | 1 ruoyi-ui/src/assets/icons/svg/list.svg | 1 ruoyi/src/main/resources/mybatis/system/SysMenuMapper.xml | 158 ruoyi-ui/src/api/system/role.js | 66 ruoyi-ui/src/components/HeaderSearch/index.vue | 180 ruoyi-ui/src/views/system/user/profile/resetPwd.vue | 76 ruoyi-ui/src/App.vue | 11 /dev/null | 36 ruoyi/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java | 16 ruoyi/src/main/java/com/ruoyi/project/monitor/mapper/SysLogininforMapper.java | 42 ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java | 101 ruoyi/src/main/java/com/ruoyi/common/utils/Threads.java | 99 400 files changed, 32,457 insertions(+), 84 deletions(-) diff --git a/LICENSE b/LICENSE index 933dab9..8564f29 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,20 @@ -MIT License +The MIT License (MIT) -Copyright (c) 2019 若依 +Copyright (c) 2018 RuoYi -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.en.md b/README.en.md deleted file mode 100644 index e0dfdb1..0000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# RuoYi-Vue - -#### Description -{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) \ No newline at end of file diff --git a/README.md b/README.md index 244ef66..a894f90 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,78 @@ -# RuoYi-Vue +## 平台简介 -#### 介绍 -{**以下是码云平台说明,您可以替换此简介** -码云是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 -无论是个人、团队、或是企业,都能够用码云实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} +一直想做一款后台管理系统,看了很多优秀的开源项目但是发现没有合适的。于是利用空闲休息时间开始自己写了一套后台系统。如此有了若依。她可以用于所有的Web应用程序,如网站管理后台,网站会员中心,CMS,CRM,OA。所有前端后台代码封装过后十分精简易上手,出错概率低。同时支持移动客户端访问。系统会陆续更新一些实用功能。 -#### 软件架构 -软件架构说明 +性别男,若依是给还没有出生女儿取的名字(寓意:你若不离不弃,我必生死相依) + +参考后台模板[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) + +> 如需不分离应用,请移步 [RuoYi](https://gitee.com/y_project/RuoYi) `(保持同步更新)`,如需其他版本,请移步 [项目扩展](http://doc.ruoyi.vip/ruoyi/document/xmkz.html) `(不定时更新)` + +> 阿里云通用云产品1888优惠券 :[点我领取](https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=brki8iof) 腾讯云通用云产品2860优惠券 :[点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console) `(仅限新用户)` + +> 阿里云Hi拼购 限量爆款 低至199元/年 [点我进入](https://www.aliyun.com/acts/hi-group-buying?userCode=brki8iof) `(仅限新用户)` + +## 内置功能 + +1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。 +2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。 +3. 岗位管理:配置系统用户所属担任职务。 +4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。 +5. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。 +6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。 +7. 参数管理:对系统动态配置常用参数。 +8. 通知公告:系统通知公告信息发布维护。 +9. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 +10. 登录日志:系统登录日志记录查询包含登录异常。 +11. 在线用户:当前系统中活跃用户状态监控。 +12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。 +13. 代码生成:前后端代码的生成(java、html、xml、sql)支持CRUD下载 。 +14. 系统接口:根据业务代码自动生成相关的api接口文档。 +15. 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。 +16. 在线构建器:拖动表单元素生成相应的HTML代码。 +17. 连接池监视:监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。 +## 在线体验 +> admin/admin123 +> 陆陆续续收到一些打赏,为了更好的体验已用于演示服务器升级。谢谢各位小伙伴。 + +演示地址:http://vue.ruoyi.vip + +文档地址:http://doc.ruoyi.vip + +## 演示图 + +<table> + <tr> + <td><img src="https://oscimg.oschina.net/oscnet/cd1f90be5f2684f4560c9519c0f2a232ee8.jpg"/></td> + <td><img src="https://oscimg.oschina.net/oscnet/1cbcf0e6f257c7d3a063c0e3f2ff989e4b3.jpg"/></td> + </tr> + <tr> + <td><img src="https://oscimg.oschina.net/oscnet/97fcdc766fa04c03722aef4b3d77f71e8d2.jpg"/></td> + <td><img src="https://oscimg.oschina.net/oscnet/642858372da91853c39e2d4746f036ea171.jpg"/></td> + </tr> + <tr> + <td><img src="https://oscimg.oschina.net/oscnet/8678d5204148e2610c9d02822274a961dcf.jpg"/></td> + <td><img src="https://oscimg.oschina.net/oscnet/feb2b25a08bf9dd121b8f51274ae935ead6.jpg"/></td> + </tr> + <tr> + <td><img src="https://oscimg.oschina.net/oscnet/71c2d48905221a09a728df4aff4160b8607.jpg"/></td> + <td><img src="https://oscimg.oschina.net/oscnet/c14c1ee9a64a6a9c2c22f67d43198767dbe.jpg"/></td> + </tr> + <tr> + <td><img src="https://oscimg.oschina.net/oscnet/5e8c387724954459291aafd5eb52b456f53.jpg"/></td> + <td><img src="https://oscimg.oschina.net/oscnet/644e78da53c2e92a95dfda4f76e6d117c4b.jpg"/></td> + </tr> + <tr> + <td><img src="https://oscimg.oschina.net/oscnet/c162686bf3a39e3cd6b4fd6b5919f515ebf.jpg"/></td> + <td><img src="https://oscimg.oschina.net/oscnet/412fb931faa8b3e3de6f9cbbc5b7979cf36.jpg"/></td> + </tr> + <tr> + <td><img src="https://oscimg.oschina.net/oscnet/b6115bc8c31de52951982e509930b20684a.jpg"/></td> + <td><img src="https://oscimg.oschina.net/oscnet/5f3d39a141f21f81b90536f391b8408f1fa.jpg"/></td> + </tr> +</table> -#### 安装教程 +## 若依交流群 -1. xxxx -2. xxxx -3. xxxx - -#### 使用说明 - -1. xxxx -2. xxxx -3. xxxx - -#### 参与贡献 - -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request - - -#### 码云特技 - -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目 -5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) \ No newline at end of file +QQ群: [](https://jq.qq.com/?_wv=1027&k=5PjgQ8e) 点击按钮入群。 \ No newline at end of file diff --git a/ruoyi-ui/.editorconfig b/ruoyi-ui/.editorconfig new file mode 100644 index 0000000..22d302e --- /dev/null +++ b/ruoyi-ui/.editorconfig @@ -0,0 +1,22 @@ +# 告诉EditorConfig插件,这是根文件,不用继续往上查找 +root = true + +# 匹配全部文件 +[*] +# 设置字符集 +charset = utf-8 +# 缩进风格,可选space、tab +indent_style = space +# 缩进的空格数 +indent_size = 2 +# 结尾换行符,可选lf、cr、crlf +end_of_line = lf +# 在文件结尾插入新行 +insert_final_newline = true +# 删除一行中的前后空格 +trim_trailing_whitespace = true + +# 匹配md结尾的文件 +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/ruoyi-ui/.env.development b/ruoyi-ui/.env.development new file mode 100644 index 0000000..0cb1c61 --- /dev/null +++ b/ruoyi-ui/.env.development @@ -0,0 +1,8 @@ +# 开发环境配置 +ENV = 'development' + +# 若依管理系统/开发环境 +VUE_APP_BASE_API = '/dev-api' + +# 路由懒加载 +VUE_CLI_BABEL_TRANSPILE_MODULES = true diff --git a/ruoyi-ui/.env.production b/ruoyi-ui/.env.production new file mode 100644 index 0000000..a8e4af3 --- /dev/null +++ b/ruoyi-ui/.env.production @@ -0,0 +1,5 @@ +# 生产环境配置 +ENV = 'production' + +# 若依管理系统/生产环境 +VUE_APP_BASE_API = '/prod-api' diff --git a/ruoyi-ui/.env.staging b/ruoyi-ui/.env.staging new file mode 100644 index 0000000..41d4817 --- /dev/null +++ b/ruoyi-ui/.env.staging @@ -0,0 +1,7 @@ +NODE_ENV = production + +# 测试环境配置 +ENV = 'staging' + +# 若依管理系统/测试环境 +VUE_APP_BASE_API = '/stage-api' diff --git a/ruoyi-ui/.eslintignore b/ruoyi-ui/.eslintignore new file mode 100644 index 0000000..7dd2c2a --- /dev/null +++ b/ruoyi-ui/.eslintignore @@ -0,0 +1,10 @@ +# 忽略build目录下类型为js的文件的语法检查 +build/*.js +# 忽略src/assets目录下文件的语法检查 +src/assets +# 忽略public目录下文件的语法检查 +public +# 忽略当前目录下为js的文件的语法检查 +*.js +# 忽略当前目录下为vue的文件的语法检查 +*.vue \ No newline at end of file diff --git a/ruoyi-ui/.eslintrc.js b/ruoyi-ui/.eslintrc.js new file mode 100644 index 0000000..b321fbe --- /dev/null +++ b/ruoyi-ui/.eslintrc.js @@ -0,0 +1,199 @@ +// ESlint 检查配置 +module.exports = { + root: true, + parserOptions: { + parser: 'babel-eslint', + sourceType: 'module' + }, + env: { + browser: true, + node: true, + es6: true, + }, + extends: ['plugin:vue/recommended', 'eslint:recommended'], + + // add your custom rules here + //it is base on https://github.com/vuejs/eslint-config-vue + rules: { + "vue/max-attributes-per-line": [2, { + "singleline": 10, + "multiline": { + "max": 1, + "allowFirstLine": false + } + }], + "vue/singleline-html-element-content-newline": "off", + "vue/multiline-html-element-content-newline":"off", + "vue/name-property-casing": ["error", "PascalCase"], + "vue/no-v-html": "off", + 'accessor-pairs': 2, + 'arrow-spacing': [2, { + 'before': true, + 'after': true + }], + 'block-spacing': [2, 'always'], + 'brace-style': [2, '1tbs', { + 'allowSingleLine': true + }], + 'camelcase': [0, { + 'properties': 'always' + }], + 'comma-dangle': [2, 'never'], + 'comma-spacing': [2, { + 'before': false, + 'after': true + }], + 'comma-style': [2, 'last'], + 'constructor-super': 2, + 'curly': [2, 'multi-line'], + 'dot-location': [2, 'property'], + 'eol-last': 2, + 'eqeqeq': ["error", "always", {"null": "ignore"}], + 'generator-star-spacing': [2, { + 'before': true, + 'after': true + }], + 'handle-callback-err': [2, '^(err|error)$'], + 'indent': [2, 2, { + 'SwitchCase': 1 + }], + 'jsx-quotes': [2, 'prefer-single'], + 'key-spacing': [2, { + 'beforeColon': false, + 'afterColon': true + }], + 'keyword-spacing': [2, { + 'before': true, + 'after': true + }], + 'new-cap': [2, { + 'newIsCap': true, + 'capIsNew': false + }], + 'new-parens': 2, + 'no-array-constructor': 2, + 'no-caller': 2, + 'no-console': 'off', + 'no-class-assign': 2, + 'no-cond-assign': 2, + 'no-const-assign': 2, + 'no-control-regex': 0, + 'no-delete-var': 2, + 'no-dupe-args': 2, + 'no-dupe-class-members': 2, + 'no-dupe-keys': 2, + 'no-duplicate-case': 2, + 'no-empty-character-class': 2, + 'no-empty-pattern': 2, + 'no-eval': 2, + 'no-ex-assign': 2, + 'no-extend-native': 2, + 'no-extra-bind': 2, + 'no-extra-boolean-cast': 2, + 'no-extra-parens': [2, 'functions'], + 'no-fallthrough': 2, + 'no-floating-decimal': 2, + 'no-func-assign': 2, + 'no-implied-eval': 2, + 'no-inner-declarations': [2, 'functions'], + 'no-invalid-regexp': 2, + 'no-irregular-whitespace': 2, + 'no-iterator': 2, + 'no-label-var': 2, + 'no-labels': [2, { + 'allowLoop': false, + 'allowSwitch': false + }], + 'no-lone-blocks': 2, + 'no-mixed-spaces-and-tabs': 2, + 'no-multi-spaces': 2, + 'no-multi-str': 2, + 'no-multiple-empty-lines': [2, { + 'max': 1 + }], + 'no-native-reassign': 2, + 'no-negated-in-lhs': 2, + 'no-new-object': 2, + 'no-new-require': 2, + 'no-new-symbol': 2, + 'no-new-wrappers': 2, + 'no-obj-calls': 2, + 'no-octal': 2, + 'no-octal-escape': 2, + 'no-path-concat': 2, + 'no-proto': 2, + 'no-redeclare': 2, + 'no-regex-spaces': 2, + 'no-return-assign': [2, 'except-parens'], + 'no-self-assign': 2, + 'no-self-compare': 2, + 'no-sequences': 2, + 'no-shadow-restricted-names': 2, + 'no-spaced-func': 2, + 'no-sparse-arrays': 2, + 'no-this-before-super': 2, + 'no-throw-literal': 2, + 'no-trailing-spaces': 2, + 'no-undef': 2, + 'no-undef-init': 2, + 'no-unexpected-multiline': 2, + 'no-unmodified-loop-condition': 2, + 'no-unneeded-ternary': [2, { + 'defaultAssignment': false + }], + 'no-unreachable': 2, + 'no-unsafe-finally': 2, + 'no-unused-vars': [2, { + 'vars': 'all', + 'args': 'none' + }], + 'no-useless-call': 2, + 'no-useless-computed-key': 2, + 'no-useless-constructor': 2, + 'no-useless-escape': 0, + 'no-whitespace-before-property': 2, + 'no-with': 2, + 'one-var': [2, { + 'initialized': 'never' + }], + 'operator-linebreak': [2, 'after', { + 'overrides': { + '?': 'before', + ':': 'before' + } + }], + 'padded-blocks': [2, 'never'], + 'quotes': [2, 'single', { + 'avoidEscape': true, + 'allowTemplateLiterals': true + }], + 'semi': [2, 'never'], + 'semi-spacing': [2, { + 'before': false, + 'after': true + }], + 'space-before-blocks': [2, 'always'], + 'space-before-function-paren': [2, 'never'], + 'space-in-parens': [2, 'never'], + 'space-infix-ops': 2, + 'space-unary-ops': [2, { + 'words': true, + 'nonwords': false + }], + 'spaced-comment': [2, 'always', { + 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] + }], + 'template-curly-spacing': [2, 'never'], + 'use-isnan': 2, + 'valid-typeof': 2, + 'wrap-iife': [2, 'any'], + 'yield-star-spacing': [2, 'both'], + 'yoda': [2, 'never'], + 'prefer-const': 2, + 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, + 'object-curly-spacing': [2, 'always', { + objectsInObjects: false + }], + 'array-bracket-spacing': [2, 'never'] + } +} diff --git a/ruoyi-ui/.gitignore b/ruoyi-ui/.gitignore new file mode 100644 index 0000000..2c9b3a7 --- /dev/null +++ b/ruoyi-ui/.gitignore @@ -0,0 +1,23 @@ +.DS_Store +node_modules/ +dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +**/*.log + +tests/**/coverage/ +tests/e2e/reports +selenium-debug.log + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.local + +package-lock.json +yarn.lock diff --git a/ruoyi-ui/README.md b/ruoyi-ui/README.md new file mode 100644 index 0000000..1b108f3 --- /dev/null +++ b/ruoyi-ui/README.md @@ -0,0 +1,30 @@ +## ���� + +```bash +# ��¡��Ŀ +git clone https://gitee.com/y_project/RuoYi-Vue + +# ������ĿĿ¼ +cd ruoyi-ui + +# ��װ���� +npm install + +# ���鲻Ҫֱ��ʹ�� cnpm ��װ���������и��ֹ���� bug������ͨ�����²������ npm �����ٶ��������� +npm install --registry=https://registry.npm.taobao.org + +# �������� +npm run dev +``` + +��������� http://localhost:80 + +## ���� + +```bash +# �������Ի��� +npm run build:stage + +# ������������ +npm run build:prod +``` \ No newline at end of file diff --git a/ruoyi-ui/babel.config.js b/ruoyi-ui/babel.config.js new file mode 100644 index 0000000..c8e69f5 --- /dev/null +++ b/ruoyi-ui/babel.config.js @@ -0,0 +1,5 @@ +module.exports = { + presets: [ + '@vue/app' + ] +} diff --git a/ruoyi-ui/bin/package.bat b/ruoyi-ui/bin/package.bat new file mode 100644 index 0000000..ecc8db4 --- /dev/null +++ b/ruoyi-ui/bin/package.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [��Ϣ] ���Web���̣�����node_modules���ļ��� +echo. + +%~d0 +cd %~dp0 + +cd .. +npm install --registry=https://registry.npm.taobao.org + +pause \ No newline at end of file diff --git a/ruoyi-ui/bin/run-web.bat b/ruoyi-ui/bin/run-web.bat new file mode 100644 index 0000000..a696280 --- /dev/null +++ b/ruoyi-ui/bin/run-web.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [��Ϣ] ʹ�� Vue ���� Web ���̡� +echo. + +%~d0 +cd %~dp0 + +cd .. +npm run dev + +pause \ No newline at end of file diff --git a/ruoyi-ui/build/index.js b/ruoyi-ui/build/index.js new file mode 100644 index 0000000..385da08 --- /dev/null +++ b/ruoyi-ui/build/index.js @@ -0,0 +1,35 @@ +const { run } = require('runjs') +const chalk = require('chalk') +const config = require('../vue.config.js') +const rawArgv = process.argv.slice(2) +const args = rawArgv.join(' ') + +if (process.env.npm_config_preview || rawArgv.includes('--preview')) { + const report = rawArgv.includes('--report') + + run(`vue-cli-service build ${args}`) + + const port = 9526 + const publicPath = config.publicPath + + var connect = require('connect') + var serveStatic = require('serve-static') + const app = connect() + + app.use( + publicPath, + serveStatic('./dist', { + index: ['index.html', '/'] + }) + ) + + app.listen(port, function () { + console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`)) + if (report) { + console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`)) + } + + }) +} else { + run(`vue-cli-service build ${args}`) +} diff --git a/ruoyi-ui/package.json b/ruoyi-ui/package.json new file mode 100644 index 0000000..1122913 --- /dev/null +++ b/ruoyi-ui/package.json @@ -0,0 +1,102 @@ +{ + "name": "ruoyi", + "version": "1.0.0", + "description": "若依管理系统", + "author": "若依", + "license": "MIT", + "scripts": { + "dev": "vue-cli-service serve", + "build:prod": "vue-cli-service build", + "build:stage": "vue-cli-service build --mode staging", + "preview": "node build/index.js --preview", + "lint": "eslint --ext .js,.vue src", + "test:unit": "jest --clearCache && vue-cli-service test:unit", + "test:ci": "npm run lint && npm run test:unit", + "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml", + "new": "plop" + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "src/**/*.{js,vue}": [ + "eslint --fix", + "git add" + ] + }, + "keywords": [ + "vue", + "admin", + "dashboard", + "element-ui", + "boilerplate", + "admin-template", + "management-system" + ], + "repository": { + "type": "git", + "url": "https://gitee.com/y_project/RuoYi-Vue.git" + }, + "dependencies": { + "@riophae/vue-treeselect": "0.1.0", + "vue-quill-editor": "3.0.6", + "vue-cropper": "0.4.9", + "axios": "0.18.1", + "echarts": "4.2.1", + "element-ui": "2.11.1", + "fuse.js": "3.4.4", + "js-cookie": "2.2.0", + "normalize.css": "7.0.0", + "nprogress": "0.2.0", + "path-to-regexp": "2.4.0", + "screenfull": "4.2.0", + "vue": "2.6.10", + "vue-count-to": "1.0.13", + "vue-router": "3.0.2", + "vue-splitpane": "1.0.4", + "vuex": "3.1.0" + }, + "devDependencies": { + "@babel/core": "7.0.0", + "@babel/register": "7.0.0", + "@vue/cli-plugin-babel": "3.5.3", + "@vue/cli-plugin-eslint": "^3.9.1", + "@vue/cli-plugin-unit-jest": "3.5.3", + "@vue/cli-service": "3.5.3", + "@vue/test-utils": "1.0.0-beta.29", + "autoprefixer": "^9.5.1", + "babel-core": "7.0.0-bridge.0", + "babel-eslint": "10.0.1", + "babel-jest": "23.6.0", + "chalk": "2.4.2", + "chokidar": "2.1.5", + "connect": "3.6.6", + "eslint": "5.15.3", + "eslint-plugin-vue": "5.2.2", + "html-webpack-plugin": "3.2.0", + "http-proxy-middleware": "^0.19.1", + "husky": "1.3.1", + "lint-staged": "8.1.5", + "mockjs": "1.0.1-beta3", + "node-sass": "^4.9.0", + "plop": "2.3.0", + "runjs": "^4.3.2", + "sass-loader": "^7.1.0", + "script-ext-html-webpack-plugin": "2.1.3", + "script-loader": "0.7.2", + "serve-static": "^1.13.2", + "svg-sprite-loader": "4.1.3", + "svgo": "1.2.0", + "vue-template-compiler": "2.6.10" + }, + "engines": { + "node": ">=8.9", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions" + ] +} diff --git a/ruoyi-ui/public/favicon.ico b/ruoyi-ui/public/favicon.ico new file mode 100644 index 0000000..e263760 --- /dev/null +++ b/ruoyi-ui/public/favicon.ico Binary files differ diff --git a/ruoyi-ui/public/index.html b/ruoyi-ui/public/index.html new file mode 100644 index 0000000..525e2ac --- /dev/null +++ b/ruoyi-ui/public/index.html @@ -0,0 +1,207 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> + <meta name="renderer" content="webkit"> + <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> + <link rel="icon" href="<%= BASE_URL %>favicon.ico"> + <title><%= webpackConfig.name %></title> + <style> + html, + body, + #app { + height: 100%; + margin: 0px; + padding: 0px; + } + .chromeframe { + margin: 0.2em 0; + background: #ccc; + color: #000; + padding: 0.2em 0; + } + + #loader-wrapper { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 999999; + } + + #loader { + display: block; + position: relative; + left: 50%; + top: 50%; + width: 150px; + height: 150px; + margin: -75px 0 0 -75px; + border-radius: 50%; + border: 3px solid transparent; + border-top-color: #FFF; + -webkit-animation: spin 2s linear infinite; + -ms-animation: spin 2s linear infinite; + -moz-animation: spin 2s linear infinite; + -o-animation: spin 2s linear infinite; + animation: spin 2s linear infinite; + z-index: 1001; + } + + #loader:before { + content: ""; + position: absolute; + top: 5px; + left: 5px; + right: 5px; + bottom: 5px; + border-radius: 50%; + border: 3px solid transparent; + border-top-color: #FFF; + -webkit-animation: spin 3s linear infinite; + -moz-animation: spin 3s linear infinite; + -o-animation: spin 3s linear infinite; + -ms-animation: spin 3s linear infinite; + animation: spin 3s linear infinite; + } + + #loader:after { + content: ""; + position: absolute; + top: 15px; + left: 15px; + right: 15px; + bottom: 15px; + border-radius: 50%; + border: 3px solid transparent; + border-top-color: #FFF; + -moz-animation: spin 1.5s linear infinite; + -o-animation: spin 1.5s linear infinite; + -ms-animation: spin 1.5s linear infinite; + -webkit-animation: spin 1.5s linear infinite; + animation: spin 1.5s linear infinite; + } + + + @-webkit-keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + -ms-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + -ms-transform: rotate(360deg); + transform: rotate(360deg); + } + } + + @keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + -ms-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + -ms-transform: rotate(360deg); + transform: rotate(360deg); + } + } + + + #loader-wrapper .loader-section { + position: fixed; + top: 0; + width: 51%; + height: 100%; + background: #7171C6; + z-index: 1000; + -webkit-transform: translateX(0); + -ms-transform: translateX(0); + transform: translateX(0); + } + + #loader-wrapper .loader-section.section-left { + left: 0; + } + + #loader-wrapper .loader-section.section-right { + right: 0; + } + + + .loaded #loader-wrapper .loader-section.section-left { + -webkit-transform: translateX(-100%); + -ms-transform: translateX(-100%); + transform: translateX(-100%); + -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000); + transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000); + } + + .loaded #loader-wrapper .loader-section.section-right { + -webkit-transform: translateX(100%); + -ms-transform: translateX(100%); + transform: translateX(100%); + -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000); + transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000); + } + + .loaded #loader { + opacity: 0; + -webkit-transition: all 0.3s ease-out; + transition: all 0.3s ease-out; + } + + .loaded #loader-wrapper { + visibility: hidden; + -webkit-transform: translateY(-100%); + -ms-transform: translateY(-100%); + transform: translateY(-100%); + -webkit-transition: all 0.3s 1s ease-out; + transition: all 0.3s 1s ease-out; + } + + .no-js #loader-wrapper { + display: none; + } + + .no-js h1 { + color: #222222; + } + + #loader-wrapper .load_title { + font-family: 'Open Sans'; + color: #FFF; + font-size: 19px; + width: 100%; + text-align: center; + z-index: 9999999999999; + position: absolute; + top: 60%; + opacity: 1; + line-height: 30px; + } + + #loader-wrapper .load_title span { + font-weight: normal; + font-style: italic; + font-size: 13px; + color: #FFF; + opacity: 0.5; + } + </style> + </head> + <body> + <div id="app"> + <div id="loader-wrapper"> + <div id="loader"></div> + <div class="loader-section section-left"></div> + <div class="loader-section section-right"></div> + <div class="load_title">正在加载系统资源,请耐心等待</div> + </div> + </div> + </body> +</html> diff --git a/ruoyi-ui/src/App.vue b/ruoyi-ui/src/App.vue new file mode 100644 index 0000000..1d0634c --- /dev/null +++ b/ruoyi-ui/src/App.vue @@ -0,0 +1,11 @@ +<template> + <div id="app"> + <router-view /> + </div> +</template> + +<script> +export default { + name: 'App' +} +</script> diff --git a/ruoyi-ui/src/api/login.js b/ruoyi-ui/src/api/login.js new file mode 100644 index 0000000..faa7bbe --- /dev/null +++ b/ruoyi-ui/src/api/login.js @@ -0,0 +1,40 @@ +import request from '@/utils/request' + +// 登录方法 +export function login(username, password, code, uuid) { + const data = { + username, + password, + code, + uuid + } + return request({ + url: '/login', + method: 'post', + params: data + }) +} + +// 获取用户详细信息 +export function getInfo() { + return request({ + url: '/getInfo', + method: 'get' + }) +} + +// 退出方法 +export function logout() { + return request({ + url: '/logout', + method: 'post' + }) +} + +// 获取验证码 +export function getCodeImg() { + return request({ + url: '/captchaImage', + method: 'get' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/menu.js b/ruoyi-ui/src/api/menu.js new file mode 100644 index 0000000..6e52e6e --- /dev/null +++ b/ruoyi-ui/src/api/menu.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +// 获取路由 +export const getRouters = () => { + return request({ + url: '/getRouters', + method: 'get' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/monitor/logininfor.js b/ruoyi-ui/src/api/monitor/logininfor.js new file mode 100644 index 0000000..9914aaf --- /dev/null +++ b/ruoyi-ui/src/api/monitor/logininfor.js @@ -0,0 +1,10 @@ +import request from '@/utils/request' + +// 查询登录日志列表 +export function list(query) { + return request({ + url: '/monitor/logininfor/list', + method: 'get', + params: query + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/monitor/operlog.js b/ruoyi-ui/src/api/monitor/operlog.js new file mode 100644 index 0000000..a6088ad --- /dev/null +++ b/ruoyi-ui/src/api/monitor/operlog.js @@ -0,0 +1,10 @@ +import request from '@/utils/request' + +// 查询操作日志列表 +export function list(query) { + return request({ + url: '/monitor/operlog/list', + method: 'get', + params: query + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/monitor/server.js b/ruoyi-ui/src/api/monitor/server.js new file mode 100644 index 0000000..4991a44 --- /dev/null +++ b/ruoyi-ui/src/api/monitor/server.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +// 查询服务器详细 +export function getServer() { + return request({ + url: '/monitor/server', + method: 'get' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/system/config.js b/ruoyi-ui/src/api/system/config.js new file mode 100644 index 0000000..6ed1050 --- /dev/null +++ b/ruoyi-ui/src/api/system/config.js @@ -0,0 +1,52 @@ +import request from '@/utils/request' + +// 查询参数列表 +export function listConfig(query) { + return request({ + url: '/system/config/list', + method: 'get', + params: query + }) +} + +// 查询参数详细 +export function getConfig(configId) { + return request({ + url: '/system/config/' + configId, + method: 'get' + }) +} + +// 根据参数键名查询参数值 +export function getConfigKey(configKey) { + return request({ + url: '/system/config/configKey/' + configKey, + method: 'get' + }) +} + +// 新增参数配置 +export function addConfig(data) { + return request({ + url: '/system/config', + method: 'post', + data: data + }) +} + +// 修改参数配置 +export function updateConfig(data) { + return request({ + url: '/system/config', + method: 'put', + data: data + }) +} + +// 删除参数配置 +export function delConfig(configId) { + return request({ + url: '/system/config/' + configId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/system/dept.js b/ruoyi-ui/src/api/system/dept.js new file mode 100644 index 0000000..2debf5a --- /dev/null +++ b/ruoyi-ui/src/api/system/dept.js @@ -0,0 +1,60 @@ +import request from '@/utils/request' + +// 查询部门列表 +export function listDept(query) { + return request({ + url: '/system/dept/list', + method: 'get', + params: query + }) +} + +// 查询部门详细 +export function getDept(deptId) { + return request({ + url: '/system/dept/' + deptId, + method: 'get' + }) +} + +// 查询部门下拉树结构 +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({ + url: '/system/dept', + method: 'post', + data: data + }) +} + +// 修改部门 +export function updateDept(data) { + return request({ + url: '/system/dept', + method: 'put', + data: data + }) +} + +// 删除部门 +export function delDept(deptId) { + return request({ + url: '/system/dept/' + deptId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/system/dict/data.js b/ruoyi-ui/src/api/system/dict/data.js new file mode 100644 index 0000000..a0fb115 --- /dev/null +++ b/ruoyi-ui/src/api/system/dict/data.js @@ -0,0 +1,52 @@ +import request from '@/utils/request' + +// 查询字典数据列表 +export function listData(query) { + return request({ + url: '/system/dict/data/list', + method: 'get', + params: query + }) +} + +// 查询字典数据详细 +export function getData(dictCode) { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'get' + }) +} + +// 根据字典类型查询字典数据信息 +export function getDicts(dictType) { + return request({ + url: '/system/dict/data/dictType/' + dictType, + method: 'get' + }) +} + +// 新增字典数据 +export function addData(data) { + return request({ + url: '/system/dict/data', + method: 'post', + data: data + }) +} + +// 修改字典数据 +export function updateData(data) { + return request({ + url: '/system/dict/data', + method: 'put', + data: data + }) +} + +// 删除字典数据 +export function delData(dictCode) { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'delete' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/system/dict/type.js b/ruoyi-ui/src/api/system/dict/type.js new file mode 100644 index 0000000..749bfaa --- /dev/null +++ b/ruoyi-ui/src/api/system/dict/type.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询字典类型列表 +export function listType(query) { + return request({ + url: '/system/dict/type/list', + method: 'get', + params: query + }) +} + +// 查询字典类型详细 +export function getType(dictId) { + return request({ + url: '/system/dict/type/' + dictId, + method: 'get' + }) +} + +// 新增字典类型 +export function addType(data) { + return request({ + url: '/system/dict/type', + method: 'post', + data: data + }) +} + +// 修改字典类型 +export function updateType(data) { + return request({ + url: '/system/dict/type', + method: 'put', + data: data + }) +} + +// 删除字典类型 +export function delType(dictId) { + return request({ + url: '/system/dict/type/' + dictId, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/system/menu.js b/ruoyi-ui/src/api/system/menu.js new file mode 100644 index 0000000..97258ee --- /dev/null +++ b/ruoyi-ui/src/api/system/menu.js @@ -0,0 +1,60 @@ +import request from '@/utils/request' + +// 查询菜单列表 +export function listMenu(query) { + return request({ + url: '/system/menu/list', + method: 'get', + params: query + }) +} + +// 查询菜单详细 +export function getMenu(menuId) { + return request({ + url: '/system/menu/' + menuId, + method: 'get' + }) +} + +// 查询菜单下拉树结构 +export function treeselect() { + return request({ + url: '/system/menu/treeselect', + method: 'get' + }) +} + +// 根据角色ID查询菜单下拉树结构 +export function roleMenuTreeselect(roleId) { + return request({ + url: '/system/menu/roleMenuTreeselect/' + roleId, + method: 'get' + }) +} + +// 新增菜单 +export function addMenu(data) { + return request({ + url: '/system/menu', + method: 'post', + data: data + }) +} + +// 修改菜单 +export function updateMenu(data) { + return request({ + url: '/system/menu', + method: 'put', + data: data + }) +} + +// 删除菜单 +export function delMenu(menuId) { + return request({ + url: '/system/menu/' + menuId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/system/notice.js b/ruoyi-ui/src/api/system/notice.js new file mode 100644 index 0000000..737fc16 --- /dev/null +++ b/ruoyi-ui/src/api/system/notice.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询公告列表 +export function listNotice(query) { + return request({ + url: '/system/notice/list', + method: 'get', + params: query + }) +} + +// 查询公告详细 +export function getNotice(noticeId) { + return request({ + url: '/system/notice/' + noticeId, + method: 'get' + }) +} + +// 新增公告 +export function addNotice(data) { + return request({ + url: '/system/notice', + method: 'post', + data: data + }) +} + +// 修改公告 +export function updateNotice(data) { + return request({ + url: '/system/notice', + method: 'put', + data: data + }) +} + +// 删除公告 +export function delNotice(noticeId) { + return request({ + url: '/system/notice/' + noticeId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/system/post.js b/ruoyi-ui/src/api/system/post.js new file mode 100644 index 0000000..1bdf069 --- /dev/null +++ b/ruoyi-ui/src/api/system/post.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询岗位列表 +export function listPost(query) { + return request({ + url: '/system/post/list', + method: 'get', + params: query + }) +} + +// 查询岗位详细 +export function getPost(postId) { + return request({ + url: '/system/post/' + postId, + method: 'get' + }) +} + +// 新增岗位 +export function addPost(data) { + return request({ + url: '/system/post', + method: 'post', + data: data + }) +} + +// 修改岗位 +export function updatePost(data) { + return request({ + url: '/system/post', + method: 'put', + data: data + }) +} + +// 删除岗位 +export function delPost(postId) { + return request({ + url: '/system/post/' + postId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/system/role.js b/ruoyi-ui/src/api/system/role.js new file mode 100644 index 0000000..95a606d --- /dev/null +++ b/ruoyi-ui/src/api/system/role.js @@ -0,0 +1,66 @@ +import request from '@/utils/request' + +// 查询角色列表 +export function listRole(query) { + return request({ + url: '/system/role/list', + method: 'get', + params: query + }) +} + +// 查询角色详细 +export function getRole(roleId) { + return request({ + url: '/system/role/' + roleId, + method: 'get' + }) +} + +// 新增角色 +export function addRole(data) { + return request({ + url: '/system/role', + method: 'post', + data: data + }) +} + +// 修改角色 +export function updateRole(data) { + return request({ + url: '/system/role', + method: 'put', + data: data + }) +} + +// 角色数据权限 +export function dataScope(data) { + return request({ + url: '/system/role/dataScope', + method: 'put', + data: data + }) +} + +// 角色状态修改 +export function changeRoleStatus(roleId, status) { + const data = { + roleId, + status + } + return request({ + url: '/system/role/changeStatus', + method: 'put', + data: data + }) +} + +// 删除角色 +export function delRole(roleId) { + return request({ + url: '/system/role/' + roleId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/system/user.js b/ruoyi-ui/src/api/system/user.js new file mode 100644 index 0000000..55766af --- /dev/null +++ b/ruoyi-ui/src/api/system/user.js @@ -0,0 +1,109 @@ +import request from '@/utils/request' + +// 查询用户列表 +export function listUser(query) { + return request({ + url: '/system/user/list', + method: 'get', + params: query + }) +} + +// 查询用户详细 +export function getUser(userId) { + return request({ + url: '/system/user/' + userId, + method: 'get' + }) +} + +// 新增用户 +export function addUser(data) { + return request({ + url: '/system/user', + method: 'post', + data: data + }) +} + +// 修改用户 +export function updateUser(data) { + return request({ + url: '/system/user', + method: 'put', + data: data + }) +} + +// 删除用户 +export function delUser(userId) { + return request({ + url: '/system/user/' + userId, + method: 'delete' + }) +} + +// 用户密码重置 +export function resetUserPwd(userId, password) { + const data = { + userId, + password + } + return request({ + url: '/system/user/resetPwd', + method: 'put', + data: data + }) +} + +// 用户状态修改 +export function changeUserStatus(userId, status) { + const data = { + userId, + status + } + return request({ + url: '/system/user/changeStatus', + method: 'put', + data: data + }) +} + +// 查询用户个人信息 +export function getUserProfile() { + return request({ + url: '/system/user/profile', + method: 'get' + }) +} + +// 修改用户个人信息 +export function updateUserProfile(data) { + return request({ + url: '/system/user/profile', + method: 'put', + data: data + }) +} + +// 用户密码重置 +export function updateUserPwd(oldPassword, newPassword) { + const data = { + oldPassword, + newPassword + } + return request({ + url: '/system/user/profile/updatePwd', + method: 'put', + params: data + }) +} + +// 用户头像上传 +export function uploadAvatar(data) { + return request({ + url: '/system/user/profile/avatar', + method: 'post', + data: data + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/assets/401_images/401.gif b/ruoyi-ui/src/assets/401_images/401.gif new file mode 100644 index 0000000..cd6e0d9 --- /dev/null +++ b/ruoyi-ui/src/assets/401_images/401.gif Binary files differ diff --git a/ruoyi-ui/src/assets/404_images/404.png b/ruoyi-ui/src/assets/404_images/404.png new file mode 100644 index 0000000..3d8e230 --- /dev/null +++ b/ruoyi-ui/src/assets/404_images/404.png Binary files differ diff --git a/ruoyi-ui/src/assets/404_images/404_cloud.png b/ruoyi-ui/src/assets/404_images/404_cloud.png new file mode 100644 index 0000000..c6281d0 --- /dev/null +++ b/ruoyi-ui/src/assets/404_images/404_cloud.png Binary files differ diff --git a/ruoyi-ui/src/assets/icons/index.js b/ruoyi-ui/src/assets/icons/index.js new file mode 100644 index 0000000..d79e164 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/ruoyi-ui/src/assets/icons/svg/404.svg b/ruoyi-ui/src/assets/icons/svg/404.svg new file mode 100644 index 0000000..6df5019 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/404.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M121.718 73.272v9.953c3.957-7.584 6.199-16.05 6.199-24.995C127.917 26.079 99.273 0 63.958 0 28.644 0 0 26.079 0 58.23c0 .403.028.806.028 1.21l22.97-25.953h13.34l-19.76 27.187h6.42V53.77l13.728-19.477v49.361H22.998V73.272H2.158c5.951 20.284 23.608 36.208 45.998 41.399-1.44 3.3-5.618 11.263-12.565 12.674-8.607 1.764 23.358.428 46.163-13.178 17.519-4.611 31.938-15.849 39.77-30.513h-13.506V73.272H85.02V59.464l22.998-25.977h13.008l-19.429 27.187h6.421v-7.433l13.727-19.402v39.433h-.027zm-78.24 2.822a10.516 10.516 0 0 1-.996-4.535V44.548c0-1.613.332-3.124.996-4.535a11.66 11.66 0 0 1 2.713-3.68c1.134-1.032 2.49-1.864 4.04-2.468 1.55-.605 3.21-.908 4.982-.908h11.292c1.77 0 3.431.303 4.981.908 1.522.604 2.85 1.41 3.986 2.418l-12.26 16.303v-2.898a1.96 1.96 0 0 0-.665-1.512c-.443-.403-.996-.604-1.66-.604-.665 0-1.218.201-1.661.604a1.96 1.96 0 0 0-.664 1.512v9.071L44.364 77.606a10.556 10.556 0 0 1-.886-1.512zm35.73-4.535c0 1.613-.332 3.124-.997 4.535a11.66 11.66 0 0 1-2.712 3.68c-1.134 1.032-2.49 1.864-4.04 2.469-1.55.604-3.21.907-4.982.907H55.185c-1.77 0-3.431-.303-4.981-.907-1.55-.605-2.906-1.437-4.041-2.47a12.49 12.49 0 0 1-1.384-1.512l13.727-18.217v6.375c0 .605.222 1.109.665 1.512.442.403.996.604 1.66.604.664 0 1.218-.201 1.66-.604a1.96 1.96 0 0 0 .665-1.512V53.87L75.97 36.838c.913.932 1.66 1.99 2.214 3.175.664 1.41.996 2.922.996 4.535v27.011h.028z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/bug.svg b/ruoyi-ui/src/assets/icons/svg/bug.svg new file mode 100644 index 0000000..05a150d --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/bug.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M127.88 73.143c0 1.412-.506 2.635-1.518 3.669-1.011 1.033-2.209 1.55-3.592 1.55h-17.887c0 9.296-1.783 17.178-5.35 23.645l16.609 17.044c1.011 1.034 1.517 2.257 1.517 3.67 0 1.412-.506 2.635-1.517 3.668-.958 1.033-2.155 1.55-3.593 1.55-1.438 0-2.635-.517-3.593-1.55l-15.811-16.063a15.49 15.49 0 0 1-1.196 1.06c-.532.434-1.65 1.208-3.353 2.322a50.104 50.104 0 0 1-5.192 2.974c-1.758.87-3.94 1.658-6.546 2.364-2.607.706-5.189 1.06-7.748 1.06V47.044H58.89v73.062c-2.716 0-5.417-.367-8.106-1.102-2.688-.734-5.003-1.631-6.945-2.692a66.769 66.769 0 0 1-5.268-3.179c-1.571-1.057-2.73-1.94-3.476-2.65L33.9 109.34l-14.611 16.877c-1.066 1.14-2.344 1.711-3.833 1.711-1.277 0-2.422-.434-3.434-1.304-1.012-.978-1.557-2.187-1.635-3.627-.079-1.44.333-2.705 1.236-3.794l16.129-18.51c-3.087-6.197-4.63-13.644-4.63-22.342H5.235c-1.383 0-2.58-.517-3.592-1.55S.125 74.545.125 73.132c0-1.412.506-2.635 1.518-3.668 1.012-1.034 2.21-1.55 3.592-1.55h17.887V43.939L9.308 29.833c-1.012-1.033-1.517-2.256-1.517-3.669 0-1.412.505-2.635 1.517-3.668 1.012-1.034 2.21-1.55 3.593-1.55s2.58.516 3.593 1.55l13.813 14.106h67.396l13.814-14.106c1.012-1.034 2.21-1.55 3.592-1.55 1.384 0 2.581.516 3.593 1.55 1.012 1.033 1.518 2.256 1.518 3.668 0 1.413-.506 2.636-1.518 3.67l-13.814 14.105v23.975h17.887c1.383 0 2.58.516 3.593 1.55 1.011 1.033 1.517 2.256 1.517 3.668l-.005.01zM89.552 26.175H38.448c0-7.23 2.489-13.386 7.466-18.469C50.892 2.623 56.92.082 64 .082c7.08 0 13.108 2.541 18.086 7.624 4.977 5.083 7.466 11.24 7.466 18.469z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/build.svg b/ruoyi-ui/src/assets/icons/svg/build.svg new file mode 100644 index 0000000..97c4688 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/build.svg @@ -0,0 +1 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1568899741379" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2054" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M960 591.424V368.96c0-0.288 0.16-0.512 0.16-0.768S960 367.68 960 367.424V192a32 32 0 0 0-32-32H96a32 32 0 0 0-32 32v175.424c0 0.288-0.16 0.512-0.16 0.768s0.16 0.48 0.16 0.768v222.464c0 0.288-0.16 0.512-0.16 0.768s0.16 0.48 0.16 0.768V864a32 32 0 0 0 32 32h832a32 32 0 0 0 32-32v-271.04c0-0.288 0.16-0.512 0.16-0.768S960 591.68 960 591.424z m-560-31.232v-160H608v160h-208z m208 64V832h-208v-207.808H608z m-480-224h208v160H128v-160z m544 0h224v160h-224v-160zM896 224v112.192H128V224h768zM128 624.192h208V832H128v-207.808zM672 832v-207.808h224V832h-224z" p-id="2055"></path></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/chart.svg b/ruoyi-ui/src/assets/icons/svg/chart.svg new file mode 100644 index 0000000..27728fb --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/chart.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 54.857h36.571V128H0V54.857zM91.429 27.43H128V128H91.429V27.429zM45.714 0h36.572v128H45.714V0z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/clipboard.svg b/ruoyi-ui/src/assets/icons/svg/clipboard.svg new file mode 100644 index 0000000..90923ff --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/clipboard.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M54.857 118.857h64V73.143H89.143c-1.902 0-3.52-.668-4.855-2.002-1.335-1.335-2.002-2.954-2.002-4.855V36.57H54.857v82.286zM73.143 16v-4.571a2.2 2.2 0 0 0-.677-1.61 2.198 2.198 0 0 0-1.609-.676H20.571c-.621 0-1.158.225-1.609.676a2.198 2.198 0 0 0-.676 1.61V16a2.2 2.2 0 0 0 .676 1.61c.451.45.988.676 1.61.676h50.285c.622 0 1.158-.226 1.61-.677.45-.45.676-.987.676-1.609zm18.286 48h21.357L91.43 42.642V64zM128 73.143v48c0 1.902-.667 3.52-2.002 4.855-1.335 1.335-2.953 2.002-4.855 2.002H52.57c-1.901 0-3.52-.667-4.854-2.002-1.335-1.335-2.003-2.953-2.003-4.855v-11.429H6.857c-1.902 0-3.52-.667-4.855-2.002C.667 106.377 0 104.759 0 102.857v-96c0-1.902.667-3.52 2.002-4.855C3.337.667 4.955 0 6.857 0h77.714c1.902 0 3.52.667 4.855 2.002 1.335 1.335 2.003 2.953 2.003 4.855V30.29c1 .622 1.856 1.29 2.569 2.003l29.147 29.147c1.335 1.335 2.478 3.145 3.429 5.43.95 2.287 1.426 4.383 1.426 6.291v-.018z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/code.svg b/ruoyi-ui/src/assets/icons/svg/code.svg new file mode 100644 index 0000000..ed4d23c --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/code.svg @@ -0,0 +1 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1546567861908" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2422" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M318.577778 819.2L17.066667 512l301.511111-307.2 45.511111 45.511111L96.711111 512l267.377778 261.688889zM705.422222 819.2l-45.511111-45.511111L927.288889 512l-267.377778-261.688889 45.511111-45.511111L1006.933333 512zM540.785778 221.866667l55.751111 11.150222L483.157333 802.133333l-55.751111-11.093333z" fill="#bfbfbf" p-id="2423"></path></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/component.svg b/ruoyi-ui/src/assets/icons/svg/component.svg new file mode 100644 index 0000000..207ada3 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/component.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h54.857v54.857H0V0zm0 73.143h54.857V128H0V73.143zm73.143 0H128V128H73.143V73.143zm27.428-18.286C115.72 54.857 128 42.577 128 27.43 128 12.28 115.72 0 100.571 0 85.423 0 73.143 12.28 73.143 27.429c0 15.148 12.28 27.428 27.428 27.428z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/dashboard.svg b/ruoyi-ui/src/assets/icons/svg/dashboard.svg new file mode 100644 index 0000000..5317d37 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/dashboard.svg @@ -0,0 +1 @@ +<svg width="128" height="100" xmlns="http://www.w3.org/2000/svg"><path d="M27.429 63.638c0-2.508-.893-4.65-2.679-6.424-1.786-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.465 2.662-1.785 1.774-2.678 3.916-2.678 6.424 0 2.508.893 4.65 2.678 6.424 1.786 1.775 3.94 2.662 6.465 2.662 2.524 0 4.678-.887 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm13.714-31.801c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM71.714 65.98l7.215-27.116c.285-1.23.107-2.378-.536-3.443-.643-1.064-1.56-1.762-2.75-2.094-1.19-.33-2.333-.177-3.429.462-1.095.639-1.81 1.573-2.143 2.804l-7.214 27.116c-2.857.237-5.405 1.266-7.643 3.088-2.238 1.822-3.738 4.152-4.5 6.992-.952 3.644-.476 7.098 1.429 10.364 1.905 3.265 4.69 5.37 8.357 6.317 3.667.947 7.143.474 10.429-1.42 3.285-1.892 5.404-4.66 6.357-8.305.762-2.84.619-5.607-.429-8.305-1.047-2.697-2.762-4.85-5.143-6.46zm47.143-2.342c0-2.508-.893-4.65-2.678-6.424-1.786-1.775-3.94-2.662-6.465-2.662-2.524 0-4.678.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.786 1.775 3.94 2.662 6.464 2.662 2.524 0 4.679-.887 6.465-2.662 1.785-1.775 2.678-3.916 2.678-6.424zm-45.714-45.43c0-2.509-.893-4.65-2.679-6.425C68.68 10.01 66.524 9.122 64 9.122c-2.524 0-4.679.887-6.464 2.661-1.786 1.775-2.679 3.916-2.679 6.425 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm32 13.629c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM128 63.638c0 12.351-3.357 23.78-10.071 34.286-.905 1.372-2.19 2.058-3.858 2.058H13.93c-1.667 0-2.953-.686-3.858-2.058C3.357 87.465 0 76.037 0 63.638c0-8.613 1.69-16.847 5.071-24.703C8.452 31.08 13 24.312 18.714 18.634c5.715-5.68 12.524-10.199 20.429-13.559C47.048 1.715 55.333.035 64 .035c8.667 0 16.952 1.68 24.857 5.04 7.905 3.36 14.714 7.88 20.429 13.559 5.714 5.678 10.262 12.446 13.643 20.301 3.38 7.856 5.071 16.09 5.071 24.703z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/date.svg b/ruoyi-ui/src/assets/icons/svg/date.svg new file mode 100644 index 0000000..2a28112 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/date.svg @@ -0,0 +1 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1567417179372" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1155" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M947.2 1024 76.8 1024C38.4 1024 0 992 0 953.6L0 172.8c0-38.4 38.4-70.4 76.8-70.4l38.4 0 0 102.4c0 38.4 38.4 70.4 76.8 70.4l76.8 0c44.8 0 76.8-32 76.8-70.4L345.6 102.4l313.6 0 0 102.4c0 38.4 38.4 70.4 76.8 70.4l76.8 0c44.8 0 76.8-32 76.8-70.4L889.6 102.4l38.4 0c44.8 0 76.8 32 76.8 70.4l0 787.2C1024 992 985.6 1024 947.2 1024zM352 339.2 115.2 339.2 115.2 512l236.8 0L352 339.2zM352 544 115.2 544l0 172.8 236.8 0L352 544zM352 748.8 115.2 748.8l0 172.8 236.8 0L352 748.8zM627.2 339.2 396.8 339.2 396.8 512l236.8 0L633.6 339.2zM627.2 544 396.8 544l0 172.8 236.8 0L633.6 544zM627.2 748.8 396.8 748.8l0 172.8 236.8 0L633.6 748.8zM908.8 339.2l-236.8 0L672 512l236.8 0L908.8 339.2zM908.8 544l-236.8 0 0 172.8 236.8 0L908.8 544zM908.8 748.8l-236.8 0 0 172.8 236.8 0L908.8 748.8zM787.2 236.8c-44.8 0-76.8-32-76.8-70.4L710.4 70.4c0-38.4 38.4-70.4 76.8-70.4s76.8 32 76.8 70.4l0 102.4C864 211.2 832 236.8 787.2 236.8zM236.8 236.8C192 236.8 160 211.2 160 172.8L160 70.4C160 32 192 0 236.8 0s76.8 32 76.8 70.4l0 102.4C313.6 211.2 281.6 236.8 236.8 236.8z" p-id="1156"></path></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/dict.svg b/ruoyi-ui/src/assets/icons/svg/dict.svg new file mode 100644 index 0000000..4849377 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/dict.svg @@ -0,0 +1 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1566035680909" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3601" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M1002.0848 744.672l-33.568 10.368c0.96 7.264 2.144 14.304 2.144 21.76 0 7.328-1.184 14.432-2.368 21.568l33.792 10.56c7.936 2.24 14.496 7.616 18.336 14.752 3.84 7.328 4.672 15.808 1.952 23.552-5.376 16-23.168 24.672-39.936 19.68l-34.176-10.624c-7.136 12.8-15.776 24.672-26.208 35.2l20.8 27.488a28.96 28.96 0 0 1 5.824 22.816 29.696 29.696 0 0 1-12.704 19.616 32.544 32.544 0 0 1-44.416-6.752l-20.8-27.552c-13.696 6.56-28.192 11.2-43.008 13.888v33.632c0 16.736-14.112 30.432-31.648 30.432-17.6 0-31.872-13.696-31.872-30.432v-33.632a167.616 167.616 0 0 1-42.88-13.888l-20.928 27.552c-10.72 13.76-30.08 16.64-44.288 6.752a29.632 29.632 0 0 1-12.704-19.616 29.28 29.28 0 0 1 5.696-22.816l20.896-27.808a166.72 166.72 0 0 1-27.008-34.688l-33.376 10.432c-16.8 5.184-34.56-3.552-39.936-19.616a29.824 29.824 0 0 1 20.224-38.24l33.472-10.432c-0.8-7.264-2.016-14.304-2.016-21.824 0-7.36 1.184-14.496 2.304-21.632l-33.792-10.368c-16.672-5.376-25.632-22.496-20.224-38.432 5.376-16 23.136-24.672 39.936-19.68l34.016 10.752c7.328-12.672 15.84-24.8 26.336-35.328l-20.8-27.552a29.44 29.44 0 0 1 6.944-42.432 32.704 32.704 0 0 1 44.384 6.752l20.832 27.616c13.696-6.432 28.224-11.2 43.104-13.952v-33.568c0-16.736 14.048-30.432 31.648-30.432 17.536 0 31.808 13.568 31.808 30.432v33.504c15.072 2.688 29.344 7.808 42.848 14.016l20.992-27.616a32.48 32.48 0 0 1 44.224-6.752 29.568 29.568 0 0 1 7.136 42.432l-21.024 27.808c10.432 10.432 19.872 21.888 27.04 34.752l33.376-10.432c16.768-5.12 34.56 3.68 39.936 19.68 5.536 15.936-3.712 33.056-20.32 38.304z m-206.016-74.432c-61.344 0-111.136 47.808-111.136 106.56 0 58.88 49.792 106.496 111.136 106.496 61.312 0 111.104-47.616 111.104-106.496 0-58.752-49.792-106.56-111.104-106.56z" p-id="3602"></path><path d="M802.7888 57.152h-76.448c0-22.08-21.024-38.24-42.848-38.24H39.3968a39.68 39.68 0 0 0-39.36 40.032v795.616s41.888 120.192 110.752 120.192H673.2848a227.488 227.488 0 0 1-107.04-97.44H117.6368s-40.608-13.696-40.608-41.248l470.304-0.256 1.664 3.36a227.68 227.68 0 0 1-12.64-73.632c0-60.576 24-118.624 66.88-161.44a228.352 228.352 0 0 1 123.552-63.392l-3.2 0.288 2.144-424.672h38.208l0.576 421.024c27.04 0 52.672 4.8 76.64 13.344V101.536c0.032 0-6.304-44.384-38.368-44.384zM149.7648 514.336H72.3888v-77.408H149.7648v77.408z m0-144.32H72.3888v-77.44H149.7648v77.44z m0-137.248H72.3888v-77.44H149.7648v77.44z m501.856 281.568H206.0848v-77.408h445.536v77.408z m0-144.32H206.0848v-77.44h445.536v77.44z m0-137.248H206.0848v-77.44h445.536v77.44z" p-id="3603"></path></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/documentation.svg b/ruoyi-ui/src/assets/icons/svg/documentation.svg new file mode 100644 index 0000000..7043122 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/documentation.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M71.984 44.815H115.9L71.984 9.642v35.173zM16.094.05h63.875l47.906 38.37v76.74c0 3.392-1.682 6.645-4.677 9.044-2.995 2.399-7.056 3.746-11.292 3.746H16.094c-4.236 0-8.297-1.347-11.292-3.746-2.995-2.399-4.677-5.652-4.677-9.044V12.84C.125 5.742 7.23.05 16.094.05zm71.86 102.32V89.58h-71.86v12.79h71.86zm23.952-25.58V64H16.094v12.79h95.812z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/download.svg b/ruoyi-ui/src/assets/icons/svg/download.svg new file mode 100644 index 0000000..c896951 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/download.svg @@ -0,0 +1 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1569915748289" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3062" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M768.35456 416a256 256 0 1 0-512 0 192 192 0 1 0 0 384v64a256 256 0 0 1-58.88-505.216 320.128 320.128 0 0 1 629.76 0A256.128 256.128 0 0 1 768.35456 864v-64a192 192 0 0 0 0-384z m-512 384h64v64H256.35456v-64z m448 0h64v64h-64v-64z" fill="#333333" p-id="3063"></path><path d="M539.04256 845.248V512.192a32.448 32.448 0 0 0-32-32.192c-17.664 0-32 14.912-32 32.192v333.056l-36.096-36.096a32.192 32.192 0 0 0-45.056 0.192 31.616 31.616 0 0 0-0.192 45.056l90.88 90.944a31.36 31.36 0 0 0 22.528 9.088 30.08 30.08 0 0 0 22.4-9.088l90.88-90.88a32.192 32.192 0 0 0-0.192-45.12 31.616 31.616 0 0 0-45.056-0.192l-36.096 36.096z" fill="#333333" p-id="3064"></path></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/drag.svg b/ruoyi-ui/src/assets/icons/svg/drag.svg new file mode 100644 index 0000000..4185d3c --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/drag.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M73.137 29.08h-9.209 29.7L63.886.093 34.373 29.08h20.49v27.035H27.238v17.948h27.625v27.133h18.274V74.063h27.41V56.115h-27.41V29.08zm-9.245 98.827l27.518-26.711H36.59l27.302 26.71zM.042 64.982l27.196 27.029V38.167L.042 64.982zm100.505-26.815V92.01l27.41-27.029-27.41-26.815z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/druid.svg b/ruoyi-ui/src/assets/icons/svg/druid.svg new file mode 100644 index 0000000..a2b4b4e --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/druid.svg @@ -0,0 +1 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1566036347051" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5853" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M832 128H192a64.19 64.19 0 0 0-64 64v640a64.19 64.19 0 0 0 64 64h640a64.19 64.19 0 0 0 64-64V192a64.19 64.19 0 0 0-64-64z m0 703.89l-0.11 0.11H192.11l-0.11-0.11V768h640zM832 544H720L605.6 696.54 442.18 435.07 333.25 544H192v-64h114.75l147.07-147.07L610.4 583.46 688 480h144z m0-288H192v-63.89l0.11-0.11h639.78l0.11 0.11z" p-id="5854"></path></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/edit.svg b/ruoyi-ui/src/assets/icons/svg/edit.svg new file mode 100644 index 0000000..d26101f --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/edit.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M106.133 67.2a4.797 4.797 0 0 0-4.8 4.8c0 .187.014.36.027.533h-.027V118.4H9.6V26.667h50.133c2.654 0 4.8-2.147 4.8-4.8 0-2.654-2.146-4.8-4.8-4.8H9.6a9.594 9.594 0 0 0-9.6 9.6V118.4c0 5.307 4.293 9.6 9.6 9.6h91.733c5.307 0 9.6-4.293 9.6-9.6V72.533h-.026c.013-.173.026-.346.026-.533 0-2.653-2.146-4.8-4.8-4.8z"/><path d="M125.16 13.373L114.587 2.8c-3.747-3.747-9.854-3.72-13.6.027l-52.96 52.96a4.264 4.264 0 0 0-.907 1.36L33.813 88.533c-.746 1.76-.226 3.534.907 4.68 1.133 1.147 2.92 1.667 4.693.92l31.4-13.293c.507-.213.96-.52 1.36-.907l52.96-52.96c3.747-3.746 3.774-9.853.027-13.6zM66.107 72.4l-18.32 7.76 7.76-18.32L92.72 24.667l10.56 10.56L66.107 72.4zm52.226-52.227l-8.266 8.267-10.56-10.56 8.266-8.267.027-.026 10.56 10.56-.027.026z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/education.svg b/ruoyi-ui/src/assets/icons/svg/education.svg new file mode 100644 index 0000000..7bfb01d --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/education.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M88.883 119.565c-7.284 0-19.434 2.495-21.333 8.25v.127c-4.232.13-5.222 0-7.108 0-1.895-5.76-14.045-8.256-21.333-8.256H0V0h42.523c9.179 0 17.109 5.47 21.47 13.551C68.352 5.475 76.295 0 85.478 0H128v119.57l-39.113-.005h-.004zM60.442 24.763c0-9.651-8.978-16.507-17.777-16.507H7.108V111.43H39.11c7.054-.14 18.177.082 21.333 6.12v-4.628c-.134-5.722-.004-13.522 0-13.832V27.413l.004-2.655-.004.005zm60.442-16.517h-35.55c-8.802 0-17.78 6.856-17.78 16.493v74.259c.004.32.138 8.115 0 13.813v4.627c3.155-6.022 14.279-6.26 21.333-6.114h32V8.25l-.003-.005z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/email.svg b/ruoyi-ui/src/assets/icons/svg/email.svg new file mode 100644 index 0000000..74d25e2 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/email.svg @@ -0,0 +1 @@ +<svg width="128" height="96" xmlns="http://www.w3.org/2000/svg"><path d="M64.125 56.975L120.188.912A12.476 12.476 0 0 0 115.5 0h-103c-1.588 0-3.113.3-4.513.838l56.138 56.137z"/><path d="M64.125 68.287l-62.3-62.3A12.42 12.42 0 0 0 0 12.5v71C0 90.4 5.6 96 12.5 96h103c6.9 0 12.5-5.6 12.5-12.5v-71a12.47 12.47 0 0 0-1.737-6.35L64.125 68.287z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/example.svg b/ruoyi-ui/src/assets/icons/svg/example.svg new file mode 100644 index 0000000..46f42b5 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/example.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M96.258 57.462h31.421C124.794 27.323 100.426 2.956 70.287.07v31.422a32.856 32.856 0 0 1 25.971 25.97zm-38.796-25.97V.07C27.323 2.956 2.956 27.323.07 57.462h31.422a32.856 32.856 0 0 1 25.97-25.97zm12.825 64.766v31.421c30.46-2.885 54.507-27.253 57.713-57.712H96.579c-2.886 13.466-13.146 23.726-26.292 26.291zM31.492 70.287H.07c2.886 30.46 27.253 54.507 57.713 57.713V96.579c-13.466-2.886-23.726-13.146-26.291-26.292z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/excel.svg b/ruoyi-ui/src/assets/icons/svg/excel.svg new file mode 100644 index 0000000..74d97b8 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/excel.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M78.208 16.576v8.384h38.72v5.376h-38.72v8.704h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.512h38.72v5.376h-38.72v11.136H128v-94.72H78.208zM0 114.368L72.128 128V0L0 13.632v100.736z"/><path d="M28.672 82.56h-11.2l14.784-23.488-14.08-22.592h11.52l8.192 14.976 8.448-14.976h11.136l-14.08 22.208L58.368 82.56H46.656l-8.768-15.68z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/exit-fullscreen.svg b/ruoyi-ui/src/assets/icons/svg/exit-fullscreen.svg new file mode 100644 index 0000000..485c128 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/exit-fullscreen.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M49.217 41.329l-.136-35.24c-.06-2.715-2.302-4.345-5.022-4.405h-3.65c-2.712-.06-4.866 2.303-4.806 5.016l.152 19.164-24.151-23.79a6.698 6.698 0 0 0-9.499 0 6.76 6.76 0 0 0 0 9.526l23.93 23.713-18.345.074c-2.712-.069-5.228 1.813-5.64 5.02v3.462c.069 2.721 2.31 4.97 5.022 5.03l35.028-.207c.052.005.087.025.133.025l2.457.054a4.626 4.626 0 0 0 3.436-1.38c.88-.874 1.205-2.096 1.169-3.462l-.262-2.465c0-.048.182-.081.182-.136h.002zm52.523 51.212l18.32-.073c2.713.06 5.224-1.609 5.64-4.815v-3.462c-.068-2.722-2.317-4.97-5.021-5.04l-34.58.21c-.053 0-.086-.021-.138-.021l-2.451-.06a4.64 4.64 0 0 0-3.445 1.381c-.885.868-1.201 2.094-1.174 3.46l.27 2.46c.005.06-.177.095-.177.141l.141 34.697c.069 2.713 2.31 4.338 5.022 4.397l3.45.006c2.705.062 4.867-2.31 4.8-5.026l-.153-18.752 24.151 23.946a6.69 6.69 0 0 0 9.494 0 6.747 6.747 0 0 0 0-9.523L101.74 92.54v.001zM48.125 80.662a4.636 4.636 0 0 0-3.437-1.382l-2.457.06c-.05 0-.082.022-.137.022l-35.025-.21c-2.712.07-4.957 2.318-5.022 5.04v3.462c.409 3.206 2.925 4.874 5.633 4.814l18.554.06-24.132 23.928c-2.62 2.626-2.62 6.89 0 9.524a6.694 6.694 0 0 0 9.496 0l24.155-23.79-.155 18.866c-.06 2.722 2.094 5.093 4.801 5.025h3.65c2.72-.069 4.962-1.685 5.022-4.406l.141-34.956c0-.05-.182-.082-.182-.136l.262-2.46c.03-1.366-.286-2.592-1.166-3.46h-.001zM80.08 47.397a4.62 4.62 0 0 0 3.443 1.374l2.45-.054c.055 0 .088-.02.143-.028l35.08.21c2.712-.062 4.953-2.312 5.021-5.033l.009-3.463c-.417-3.211-2.937-5.084-5.64-5.025l-18.615-.073 23.917-23.715c2.63-2.623 2.63-6.879.008-9.513a6.691 6.691 0 0 0-9.494 0L92.251 26.016l.155-19.312c.065-2.713-2.097-5.085-4.802-5.025h-3.45c-2.713.069-4.954 1.693-5.022 4.406l-.139 35.247c0 .054.18.088.18.136l-.267 2.465c-.028 1.366.288 2.588 1.174 3.463v.001z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/eye-open.svg b/ruoyi-ui/src/assets/icons/svg/eye-open.svg new file mode 100644 index 0000000..88dcc98 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/eye-open.svg @@ -0,0 +1 @@ +<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="128" height="128"><defs><style/></defs><path d="M512 128q69.675 0 135.51 21.163t115.498 54.997 93.483 74.837 73.685 82.006 51.67 74.837 32.17 54.827L1024 512q-2.347 4.992-6.315 13.483T998.87 560.17t-31.658 51.669-44.331 59.99-56.832 64.34-69.504 60.16-82.347 51.5-94.848 34.687T512 896q-69.675 0-135.51-21.163t-115.498-54.826-93.483-74.326-73.685-81.493-51.67-74.496-32.17-54.997L0 513.707q2.347-4.992 6.315-13.483t18.816-34.816 31.658-51.84 44.331-60.33 56.832-64.683 69.504-60.331 82.347-51.84 94.848-34.816T512 128.085zm0 85.333q-46.677 0-91.648 12.331t-81.152 31.83-70.656 47.146-59.648 54.485-48.853 57.686-37.675 52.821-26.325 43.99q12.33 21.674 26.325 43.52t37.675 52.351 48.853 57.003 59.648 53.845T339.2 767.02t81.152 31.488T512 810.667t91.648-12.331 81.152-31.659 70.656-46.848 59.648-54.186 48.853-57.344 37.675-52.651T927.957 512q-12.33-21.675-26.325-43.648t-37.675-52.65-48.853-57.345-59.648-54.186-70.656-46.848-81.152-31.659T512 213.334zm0 128q70.656 0 120.661 50.006T682.667 512 632.66 632.661 512 682.667 391.339 632.66 341.333 512t50.006-120.661T512 341.333zm0 85.334q-35.328 0-60.33 25.002T426.666 512t25.002 60.33T512 597.334t60.33-25.002T597.334 512t-25.002-60.33T512 426.666z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/eye.svg b/ruoyi-ui/src/assets/icons/svg/eye.svg new file mode 100644 index 0000000..16ed2d8 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/eye.svg @@ -0,0 +1 @@ +<svg width="128" height="64" xmlns="http://www.w3.org/2000/svg"><path d="M127.072 7.994c1.37-2.208.914-5.152-.914-6.87-2.056-1.717-4.797-1.226-6.396.982-.229.245-25.586 32.382-55.74 32.382-29.24 0-55.74-32.382-55.968-32.627-1.6-1.963-4.57-2.208-6.397-.49C-.17 3.086-.399 6.275 1.2 8.238c.457.736 5.94 7.36 14.62 14.72L4.17 35.96c-1.828 1.963-1.6 5.152.228 6.87.457.98 1.6 1.471 2.742 1.471s2.284-.49 3.198-1.472l12.564-13.983c5.94 4.416 13.021 8.587 20.788 11.53l-4.797 17.418c-.685 2.699.686 5.397 3.198 6.133h1.37c2.057 0 3.884-1.472 4.341-3.68L52.6 42.83c3.655.736 7.538 1.227 11.422 1.227 3.883 0 7.767-.49 11.422-1.227l4.797 17.173c.457 2.208 2.513 3.68 4.34 3.68.457 0 .914 0 1.143-.246 2.513-.736 3.883-3.434 3.198-6.133l-4.797-17.172c7.767-2.944 14.848-7.114 20.788-11.53l12.336 13.738c.913.981 2.056 1.472 3.198 1.472s2.284-.49 3.198-1.472c1.828-1.963 1.828-4.906.228-6.87l-11.65-13.001c9.366-7.36 14.849-14.474 14.849-14.474z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/form.svg b/ruoyi-ui/src/assets/icons/svg/form.svg new file mode 100644 index 0000000..dcbaa18 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/form.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M84.068 23.784c-1.02 0-1.877-.32-2.572-.96a8.588 8.588 0 0 1-1.738-2.237 11.524 11.524 0 0 1-1.042-2.621c-.232-.895-.348-1.641-.348-2.238V0h.278c.834 0 1.622.085 2.363.256.742.17 1.645.575 2.711 1.214 1.066.64 2.363 1.535 3.892 2.686 1.53 1.15 3.453 2.664 5.77 4.54 2.502 2.045 4.494 3.771 5.977 5.178 1.483 1.406 2.618 2.6 3.406 3.58.787.98 1.274 1.812 1.46 2.494.185.682.277 1.278.277 1.79v2.046H84.068zM127.3 84.01c.278.682.464 1.535.556 2.558.093 1.023-.37 2.003-1.39 2.94-.463.427-.88.832-1.25 1.215-.372.384-.696.704-.974.96a6.69 6.69 0 0 1-.973.767l-11.816-10.741a44.331 44.331 0 0 0 1.877-1.535 31.028 31.028 0 0 1 1.737-1.406c1.112-.938 2.317-1.343 3.615-1.215 1.297.128 2.363.405 3.197.83.927.427 1.923 1.173 2.989 2.239 1.065 1.065 1.876 2.195 2.432 3.388zM78.23 95.902c2.038 0 3.752-.511 5.143-1.534l-26.969 25.83H18.037c-1.761 0-3.684-.47-5.77-1.407a24.549 24.549 0 0 1-5.838-3.709 21.373 21.373 0 0 1-4.518-5.306c-1.204-2.003-1.807-4.07-1.807-6.202V16.495c0-1.79.44-3.665 1.32-5.626A18.41 18.41 0 0 1 5.04 5.562a21.798 21.798 0 0 1 5.213-3.964C12.198.533 14.237 0 16.37 0h53.24v15.984c0 1.62.278 3.367.834 5.242a16.704 16.704 0 0 0 2.572 5.179c1.159 1.577 2.665 2.898 4.518 3.964 1.853 1.066 4.078 1.598 6.673 1.598h20.295v42.325L85.458 92.45c1.02-1.364 1.529-2.856 1.529-4.476 0-2.216-.857-4.113-2.572-5.69-1.714-1.577-3.776-2.366-6.186-2.366H26.1c-2.409 0-4.448.789-6.116 2.366-1.668 1.577-2.502 3.474-2.502 5.69 0 2.217.834 4.092 2.502 5.626 1.668 1.535 3.707 2.302 6.117 2.302h52.13zM26.1 47.951c-2.41 0-4.449.789-6.117 2.366-1.668 1.577-2.502 3.473-2.502 5.69 0 2.216.834 4.092 2.502 5.626 1.668 1.534 3.707 2.302 6.117 2.302h52.13c2.409 0 4.47-.768 6.185-2.302 1.715-1.534 2.572-3.41 2.572-5.626 0-2.217-.857-4.113-2.572-5.69-1.714-1.577-3.776-2.366-6.186-2.366H26.1zm52.407 64.063l1.807-1.663 3.476-3.196a479.75 479.75 0 0 0 4.587-4.284 500.757 500.757 0 0 1 5.004-4.667c3.985-3.666 8.48-7.758 13.485-12.276l11.677 10.741-13.485 12.404-5.004 4.603-4.587 4.22a179.46 179.46 0 0 0-3.267 3.068c-.88.853-1.367 1.322-1.46 1.407-.463.341-.973.703-1.529 1.087-.556.383-1.112.703-1.668.959-.556.256-1.413.575-2.572.959a83.5 83.5 0 0 1-3.545 1.087 72.2 72.2 0 0 1-3.475.895c-1.112.256-1.946.426-2.502.511-1.112.17-1.854.043-2.224-.383-.371-.426-.464-1.151-.278-2.174.092-.511.278-1.279.556-2.302.278-1.023.602-2.067.973-3.132l1.042-3.005c.325-.938.58-1.577.765-1.918a10.157 10.157 0 0 1 2.224-2.941z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/fullscreen.svg b/ruoyi-ui/src/assets/icons/svg/fullscreen.svg new file mode 100644 index 0000000..0e86b6f --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/fullscreen.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M38.47 52L52 38.462l-23.648-23.67L43.209 0H.035L0 43.137l14.757-14.865L38.47 52zm74.773 47.726L89.526 76 76 89.536l23.648 23.672L84.795 128h43.174L128 84.863l-14.757 14.863zM89.538 52l23.668-23.648L128 43.207V.038L84.866 0 99.73 14.76 76 38.472 89.538 52zM38.46 76L14.792 99.651 0 84.794v43.173l43.137.033-14.865-14.757L52 89.53 38.46 76z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/guide.svg b/ruoyi-ui/src/assets/icons/svg/guide.svg new file mode 100644 index 0000000..b271001 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/guide.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M1.482 70.131l36.204 16.18 69.932-65.485-61.38 70.594 46.435 18.735c1.119.425 2.397-.17 2.797-1.363v-.085L127.998.047 1.322 65.874c-1.12.597-1.519 1.959-1.04 3.151.32.511.72.937 1.2 1.107zm44.676 57.821L64.22 107.26l-18.062-7.834v28.527z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/icon.svg b/ruoyi-ui/src/assets/icons/svg/icon.svg new file mode 100644 index 0000000..82be8ee --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/icon.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M115.147.062a13 13 0 0 1 4.94.945c1.55.63 2.907 1.526 4.069 2.688a13.148 13.148 0 0 1 2.761 4.069c.678 1.55 1.017 3.245 1.017 5.086v102.3c0 3.681-1.187 6.733-3.56 9.155-2.373 2.422-5.352 3.633-8.937 3.633H12.992c-3.875 0-7-1.26-9.373-3.779-2.373-2.518-3.56-5.667-3.56-9.445V12.704c0-3.39 1.163-6.345 3.488-8.863C5.872 1.32 8.972.062 12.847.062h102.3zM81.434 109.047c1.744 0 3.003-.412 3.778-1.235.775-.824 1.163-1.914 1.163-3.27 0-1.26-.388-2.325-1.163-3.197-.775-.872-2.034-1.307-3.778-1.307H72.57c.097-.194.145-.485.145-.872V27.09h9.01c1.743 0 2.954-.436 3.633-1.308.678-.872 1.017-1.938 1.017-3.197 0-1.26-.34-2.325-1.017-3.197-.679-.872-1.89-1.308-3.633-1.308H46.268c-1.743 0-2.954.436-3.632 1.308-.678.872-1.018 1.938-1.018 3.197 0 1.26.34 2.325 1.018 3.197.678.872 1.889 1.308 3.632 1.308h8.138v72.075c0 .193.024.339.073.436.048.096.072.242.072.436H46.56c-1.744 0-3.003.435-3.778 1.307-.775.872-1.163 1.938-1.163 3.197 0 1.356.388 2.446 1.163 3.27.775.823 2.034 1.235 3.778 1.235h34.875z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/international.svg b/ruoyi-ui/src/assets/icons/svg/international.svg new file mode 100644 index 0000000..e9b56ee --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/international.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M83.287 103.01c-1.57-3.84-6.778-10.414-15.447-19.548-2.327-2.444-2.182-4.306-1.338-9.862v-.64c.553-3.81 1.513-6.05 14.313-8.087 6.516-1.018 8.203 1.57 10.589 5.178l.785 1.193a12.625 12.625 0 0 0 6.43 5.207c1.134.524 2.53 1.164 4.421 2.24 4.596 2.53 4.596 5.41 4.596 11.753v.727a26.91 26.91 0 0 1-5.178 17.454 59.055 59.055 0 0 1-19.025 11.026c3.49-6.546.814-14.313 0-16.553l-.146-.087zM64 5.12a58.502 58.502 0 0 1 25.484 5.818 54.313 54.313 0 0 0-12.859 10.327c-.93 1.28-1.716 2.473-2.472 3.579-2.444 3.694-3.637 5.352-5.818 5.614a25.105 25.105 0 0 1-4.219 0c-4.276-.29-10.094-.64-11.956 4.422-1.193 3.23-1.396 11.956 2.444 16.495.66 1.077.778 2.4.32 3.578a7.01 7.01 0 0 1-2.066 3.229 18.938 18.938 0 0 1-2.909-2.91 18.91 18.91 0 0 0-8.32-6.603c-1.25-.349-2.647-.64-3.985-.93-3.782-.786-8.03-1.688-9.019-3.812a14.895 14.895 0 0 1-.727-5.818 21.935 21.935 0 0 0-1.396-9.25 8.873 8.873 0 0 0-5.557-4.946A58.705 58.705 0 0 1 64 5.12zM0 64c0 35.346 28.654 64 64 64 35.346 0 64-28.654 64-64 0-35.346-28.654-64-64-64C28.654 0 0 28.654 0 64z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/job.svg b/ruoyi-ui/src/assets/icons/svg/job.svg new file mode 100644 index 0000000..2a93a25 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/job.svg @@ -0,0 +1 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1566036191400" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5472" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M934.912 1016.832H192c-14.336 0-25.6-11.264-25.6-25.6v-189.44c0-14.336 11.264-25.6 25.6-25.6s25.6 11.264 25.6 25.6v163.84h691.712V64H217.6v148.48c0 14.336-11.264 25.6-25.6 25.6s-25.6-11.264-25.6-25.6v-174.08c0-14.336 11.264-25.6 25.6-25.6h742.912c14.336 0 25.6 11.264 25.6 25.6v952.832c0 14.336-11.264 25.6-25.6 25.6z" p-id="5473"></path><path d="M232.96 371.2h-117.76c-14.336 0-25.6-11.264-25.6-25.6s11.264-25.6 25.6-25.6h117.76c14.336 0 25.6 11.264 25.6 25.6s-11.264 25.6-25.6 25.6zM232.96 540.16h-117.76c-14.336 0-25.6-11.264-25.6-25.6s11.264-25.6 25.6-25.6h117.76c14.336 0 25.6 11.264 25.6 25.6s-11.264 25.6-25.6 25.6zM232.96 698.88h-117.76c-14.336 0-25.6-11.264-25.6-25.6s11.264-25.6 25.6-25.6h117.76c14.336 0 25.6 11.264 25.6 25.6s-11.264 25.6-25.6 25.6zM574.464 762.88c-134.144 0-243.2-109.056-243.2-243.2S440.32 276.48 574.464 276.48s243.2 109.056 243.2 243.2-109.056 243.2-243.2 243.2z m0-435.2c-105.984 0-192 86.016-192 192S468.48 711.68 574.464 711.68s192-86.016 192-192S680.448 327.68 574.464 327.68z" p-id="5474"></path><path d="M663.04 545.28h-87.04c-14.336 0-25.6-11.264-25.6-25.6s11.264-25.6 25.6-25.6h87.04c14.336 0 25.6 11.264 25.6 25.6s-11.264 25.6-25.6 25.6z" p-id="5475"></path><path d="M576 545.28c-14.336 0-25.6-11.264-25.6-25.6v-87.04c0-14.336 11.264-25.6 25.6-25.6s25.6 11.264 25.6 25.6v87.04c0 14.336-11.264 25.6-25.6 25.6z" p-id="5476"></path></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/language.svg b/ruoyi-ui/src/assets/icons/svg/language.svg new file mode 100644 index 0000000..0082b57 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/language.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M84.742 36.8c2.398 7.2 5.595 12.8 11.19 18.4 4.795-4.8 7.992-11.2 10.39-18.4h-21.58zm-52.748 40h20.78l-10.39-28-10.39 28z"/><path d="M111.916 0H16.009C7.218 0 .025 7.2.025 16v96c0 8.8 7.193 16 15.984 16h95.907c8.791 0 15.984-7.2 15.984-16V16c0-8.8-6.394-16-15.984-16zM72.754 103.2c-1.598 1.6-3.197 1.6-4.795 1.6-.8 0-2.398 0-3.197-.8-.8-.8-1.599 0-1.599-.8s-.799-1.6-1.598-3.2c-.8-1.6-.8-2.4-1.599-4l-3.196-8.8H28.797L25.6 96c-1.598 3.2-2.398 5.6-3.197 7.2-.8 1.6-2.398 1.6-4.795 1.6-1.599 0-3.197-.8-4.796-1.6-1.598-1.6-2.397-2.4-2.397-4 0-.8 0-1.6.799-3.2.8-1.6.8-2.4 1.598-4l17.583-44.8c.8-1.6.8-3.2 1.599-4.8.799-1.6 1.598-3.2 2.397-4 .8-.8 1.599-2.4 3.197-3.2 1.599-.8 3.197-.8 4.796-.8 1.598 0 3.196 0 4.795.8 1.598.8 2.398 1.6 3.197 3.2.799.8 1.598 2.4 2.397 4 .8 1.6 1.599 3.2 2.398 5.6l17.583 44c1.598 3.2 2.398 5.6 2.398 7.2-.8.8-1.599 2.4-2.398 4zM116.711 72c-8.791-3.2-15.185-7.2-20.78-12-5.594 5.6-12.787 9.6-21.579 12l-2.397-4c8.791-2.4 15.984-5.6 21.579-11.2C87.939 51.2 83.144 44 81.545 36h-7.992v-3.2h21.58c-1.6-2.4-3.198-5.6-4.796-8l2.397-.8c1.599 2.4 3.997 5.6 5.595 8.8h19.98v4h-7.992c-2.397 8-6.393 15.2-11.189 20 5.595 4.8 11.988 8.8 20.78 11.2l-3.197 4z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/link.svg b/ruoyi-ui/src/assets/icons/svg/link.svg new file mode 100644 index 0000000..48197ba --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/link.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M115.625 127.937H.063V12.375h57.781v12.374H12.438v90.813h90.813V70.156h12.374z"/><path d="M116.426 2.821l8.753 8.753-56.734 56.734-8.753-8.745z"/><path d="M127.893 37.982h-12.375V12.375H88.706V0h39.187z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/list.svg b/ruoyi-ui/src/assets/icons/svg/list.svg new file mode 100644 index 0000000..20259ed --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/list.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M1.585 12.087c0 6.616 3.974 11.98 8.877 11.98 4.902 0 8.877-5.364 8.877-11.98 0-6.616-3.975-11.98-8.877-11.98-4.903 0-8.877 5.364-8.877 11.98zM125.86.107H35.613c-1.268 0-2.114 1.426-2.114 2.852v18.255c0 1.712 1.057 2.853 2.114 2.853h90.247c1.268 0 2.114-1.426 2.114-2.853V2.96c0-1.711-1.057-2.852-2.114-2.852zM.106 62.86c0 6.615 3.974 11.979 8.876 11.979 4.903 0 8.877-5.364 8.877-11.98 0-6.616-3.974-11.98-8.877-11.98-4.902 0-8.876 5.364-8.876 11.98zM124.17 50.88H33.921c-1.268 0-2.114 1.425-2.114 2.851v18.256c0 1.711 1.057 2.852 2.114 2.852h90.247c1.268 0 2.114-1.426 2.114-2.852V53.73c0-1.426-.846-2.852-2.114-2.852zM.106 115.913c0 6.616 3.974 11.98 8.876 11.98 4.903 0 8.877-5.364 8.877-11.98 0-6.616-3.974-11.98-8.877-11.98-4.902 0-8.876 5.364-8.876 11.98zm124.064-11.98H33.921c-1.268 0-2.114 1.426-2.114 2.853v18.255c0 1.711 1.057 2.852 2.114 2.852h90.247c1.268 0 2.114-1.426 2.114-2.852v-18.255c0-1.427-.846-2.853-2.114-2.853z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/lock.svg b/ruoyi-ui/src/assets/icons/svg/lock.svg new file mode 100644 index 0000000..74fee54 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/lock.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M119.88 49.674h-7.987V39.52C111.893 17.738 90.45.08 63.996.08 37.543.08 16.1 17.738 16.1 39.52v10.154H8.113c-4.408 0-7.987 2.94-7.987 6.577v65.13c0 3.637 3.57 6.577 7.987 6.577H119.88c4.407 0 7.987-2.94 7.987-6.577v-65.13c-.008-3.636-3.58-6.577-7.987-6.577zm-23.953 0H32.065V39.52c0-14.524 14.301-26.295 31.931-26.295 17.63 0 31.932 11.777 31.932 26.295v10.153z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/log.svg b/ruoyi-ui/src/assets/icons/svg/log.svg new file mode 100644 index 0000000..d879d33 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/log.svg @@ -0,0 +1 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1566035943711" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4805" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M208.736 566.336H64.384v59.328h144.352v-59.328z m0-336.096H165.44V74.592c0-7.968 4.896-14.848 10.464-14.848h502.016V0.448H175.936c-38.72 1.248-69.248 34.368-68.192 74.144v155.648H64.384V289.6h144.352V230.24z m0 168.096H64.384v59.328h144.352v-59.328z m714.656 76.576h-57.76v474.496c0 7.936-4.896 14.848-10.464 14.848H175.936c-5.568 0-10.464-6.912-10.464-14.848v-155.68h43.296v-59.296H64.384v59.296h43.328v155.68c-1.024 39.776 29.472 72.896 68.192 74.144h679.232c38.72-1.184 69.248-34.368 68.256-74.144V474.912z m14.944-290.336l-83.072-85.312a71.264 71.264 0 0 0-52.544-21.728 71.52 71.52 0 0 0-51.616 23.872L386.528 507.264a30.496 30.496 0 0 0-6.176 10.72L308.16 740.512a30.016 30.016 0 0 0 6.976 30.24c7.712 7.968 19.2 10.752 29.568 7.2l216.544-74.112a28.736 28.736 0 0 0 12.128-7.936L940.448 287.456a75.552 75.552 0 0 0-2.112-102.88z m-557.12 518.272l39.104-120.64 78.336 80.416-117.44 40.224z m170.048-70.016l-103.552-106.016 200.16-222.4 103.52 106.304-200.128 222.112zM897.952 247.072l-0.256 0.224-107.136 119.168-103.52-106.528 106.432-118.624a14.144 14.144 0 0 1 10.304-4.736 13.44 13.44 0 0 1 10.464 4.288l83.264 85.696c5.472 5.6 5.664 14.72 0.448 20.512z" p-id="4806"></path></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/logininfor.svg b/ruoyi-ui/src/assets/icons/svg/logininfor.svg new file mode 100644 index 0000000..267f844 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/logininfor.svg @@ -0,0 +1 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1566036016814" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5261" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M896 128h-85.333333a42.666667 42.666667 0 0 0 0 85.333333h42.666666v640H170.666667V213.333333h42.666666a42.666667 42.666667 0 0 0 0-85.333333H128a42.666667 42.666667 0 0 0-42.666667 42.666667v725.333333a42.666667 42.666667 0 0 0 42.666667 42.666667h768a42.666667 42.666667 0 0 0 42.666667-42.666667V170.666667a42.666667 42.666667 0 0 0-42.666667-42.666667z" p-id="5262"></path><path d="M341.333333 298.666667a42.666667 42.666667 0 0 0 42.666667-42.666667V128a42.666667 42.666667 0 0 0-85.333333 0v128a42.666667 42.666667 0 0 0 42.666666 42.666667zM512 298.666667a42.666667 42.666667 0 0 0 42.666667-42.666667V128a42.666667 42.666667 0 0 0-85.333334 0v128a42.666667 42.666667 0 0 0 42.666667 42.666667zM682.666667 298.666667a42.666667 42.666667 0 0 0 42.666666-42.666667V128a42.666667 42.666667 0 0 0-85.333333 0v128a42.666667 42.666667 0 0 0 42.666667 42.666667zM341.333333 768a42.666667 42.666667 0 0 0 42.666667-42.666667 128 128 0 0 1 256 0 42.666667 42.666667 0 0 0 85.333333 0 213.333333 213.333333 0 0 0-107.52-184.32A128 128 0 0 0 640 469.333333a128 128 0 0 0-256 0 128 128 0 0 0 22.186667 71.68A213.333333 213.333333 0 0 0 298.666667 725.333333a42.666667 42.666667 0 0 0 42.666666 42.666667z m128-298.666667a42.666667 42.666667 0 1 1 42.666667 42.666667 42.666667 42.666667 0 0 1-42.666667-42.666667z" p-id="5263"></path></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/message.svg b/ruoyi-ui/src/assets/icons/svg/message.svg new file mode 100644 index 0000000..14ca817 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/message.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 20.967v59.59c0 11.59 8.537 20.966 19.075 20.966h28.613l1 26.477L76.8 101.523h32.125c10.538 0 19.075-9.377 19.075-20.966v-59.59C128 9.377 119.463 0 108.925 0h-89.85C8.538 0 0 9.377 0 20.967zm82.325 33.1c0-5.524 4.013-9.935 9.037-9.935 5.026 0 9.038 4.41 9.038 9.934 0 5.524-4.025 9.934-9.038 9.934-5.024 0-9.037-4.41-9.037-9.934zm-27.613 0c0-5.524 4.013-9.935 9.038-9.935s9.037 4.41 9.037 9.934c0 5.524-4.025 9.934-9.037 9.934-5.025 0-9.038-4.41-9.038-9.934zm-27.1 0c0-5.524 4.013-9.935 9.038-9.935s9.038 4.41 9.038 9.934c0 5.524-4.026 9.934-9.05 9.934-5.013 0-9.025-4.41-9.025-9.934z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/money.svg b/ruoyi-ui/src/assets/icons/svg/money.svg new file mode 100644 index 0000000..c1580de --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/money.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M54.122 127.892v-28.68H7.513V87.274h46.609v-12.4H7.513v-12.86h38.003L.099 0h22.6l32.556 45.07c3.617 5.144 6.44 9.611 8.487 13.385 1.788-3.05 4.89-7.779 9.301-14.186L103.93 0h24.01L82.385 62.013h38.34v12.862h-46.41v12.4h46.41v11.937h-46.41v28.68H54.123z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/monitor.svg b/ruoyi-ui/src/assets/icons/svg/monitor.svg new file mode 100644 index 0000000..70db62b --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/monitor.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1543827393750" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4695" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: rbicon; src: url("chrome-extension://dipiagiiohfljcicegpgffpbnjmgjcnf/fonts/rbicon.woff2") format("woff2"); font-weight: normal; font-style: normal; } +</style></defs><path d="M64 64V640H896V64H64zM0 0h960v704H0V0z" p-id="4696"></path><path d="M192 896H768v64H192zM448 640H512v256h-64z" p-id="4697"></path><path d="M479.232 561.604267l309.9904-348.330667-47.803733-42.5472-259.566934 291.669333L303.957333 240.008533 163.208533 438.6048l52.224 37.009067 91.6224-129.28z" p-id="4698"></path></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/nested.svg b/ruoyi-ui/src/assets/icons/svg/nested.svg new file mode 100644 index 0000000..06713a8 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/nested.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M.002 9.2c0 5.044 3.58 9.133 7.998 9.133 4.417 0 7.997-4.089 7.997-9.133 0-5.043-3.58-9.132-7.997-9.132S.002 4.157.002 9.2zM31.997.066h95.981V18.33H31.997V.066zm0 45.669c0 5.044 3.58 9.132 7.998 9.132 4.417 0 7.997-4.088 7.997-9.132 0-3.263-1.524-6.278-3.998-7.91-2.475-1.63-5.524-1.63-7.998 0-2.475 1.632-4 4.647-4 7.91zM63.992 36.6h63.986v18.265H63.992V36.6zm-31.995 82.2c0 5.043 3.58 9.132 7.998 9.132 4.417 0 7.997-4.089 7.997-9.132 0-5.044-3.58-9.133-7.997-9.133s-7.998 4.089-7.998 9.133zm31.995-9.131h63.986v18.265H63.992V109.67zm0-27.404c0 5.044 3.58 9.133 7.998 9.133 4.417 0 7.997-4.089 7.997-9.133 0-3.263-1.524-6.277-3.998-7.909-2.475-1.631-5.524-1.631-7.998 0-2.475 1.632-4 4.646-4 7.91zm31.995-9.13h31.991V91.4H95.987V73.135z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/online.svg b/ruoyi-ui/src/assets/icons/svg/online.svg new file mode 100644 index 0000000..330a202 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/online.svg @@ -0,0 +1 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1568899557259" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="535" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M356.246145 681.56286c-68.156286-41.949414-107.246583-103.84102-107.246583-169.805384 0-65.966411 39.090297-127.860063 107.246583-169.809477 12.046361-7.414877 15.800871-23.190165 8.385994-35.236526-7.413853-12.046361-23.191188-15.801894-35.236526-8.387018-39.640836 24.399713-72.539106 56.044434-95.137801 91.515297-23.86657 37.461193-36.481889 79.620385-36.481889 121.917724 0 42.297338 12.615319 84.454484 36.481889 121.914654 22.598694 35.469839 55.496965 67.11456 95.137801 91.51325 4.185322 2.576685 8.821923 3.804652 13.400195 3.804652 8.598842 0 16.998139-4.329609 21.836331-12.190647C372.047016 704.752002 368.291482 688.976714 356.246145 681.56286zM263.943926 754.580874c-92.603071-61.111846-145.713686-149.623739-145.713686-242.840794 0-93.195565 53.094242-181.682899 145.667637-242.774279 11.805884-7.79043 15.061021-23.677259 7.269567-35.483142-7.79043-11.805884-23.677259-15.062044-35.483142-7.269567C128.487861 296.954249 67.006602 401.024489 67.006602 511.74008c0 110.73708 61.496609 214.830857 168.721703 285.593504 4.343935 2.867304 9.240455 4.238534 14.08274 4.238534 8.317433 0 16.476253-4.046153 21.400403-11.507078C279.003923 778.258133 275.748786 762.372328 263.943926 754.580874zM788.660552 226.213092c-11.80486-7.791453-27.692712-4.536316-35.483142 7.269567-7.79043 11.805884-4.536316 27.692712 7.269567 35.483142 92.575442 61.092403 145.670707 149.579737 145.670707 242.774279 0 93.216032-53.111638 181.727924-145.715733 242.840794-11.805884 7.79043-15.059997 23.678282-7.269567 35.484166 4.925173 7.461949 13.081946 11.507078 21.400403 11.507078 4.841262 0 9.739828-1.37123 14.083763-4.238534 107.22714-70.761624 168.724773-174.857447 168.724773-285.593504C957.341323 401.025513 895.860063 296.955272 788.660552 226.213092zM790.090111 633.67213c23.865547-37.459147 36.480866-79.617315 36.480866-121.914654 0-42.298362-12.615319-84.45653-36.480866-121.917724-22.598694-35.470863-55.496965-67.115584-95.139847-91.515297-12.047384-7.413853-27.821649-3.659343-35.236526 8.387018-7.414877 12.045337-3.659343 27.821649 8.385994 35.236526 68.156286 41.949414 107.247606 103.842043 107.247606 169.809477 0 65.964364-39.090297 127.85597-107.247606 169.804361-12.045337 7.414877-15.800871 23.190165-8.385994 35.237549 4.838192 7.861038 13.236466 12.190647 21.835308 12.190647 4.579295 0 9.215896-1.227967 13.400195-3.804652C734.591099 700.786691 767.490394 669.142993 790.090111 633.67213zM567.129086 518.274914c24.12342-17.150612 39.887452-45.305859 39.887452-77.07133 0-52.128241-42.452881-94.538143-94.634334-94.538143-52.18043 0-94.633311 42.408879-94.633311 94.538143 0 31.695886 15.696494 59.797921 39.730886 76.958766-49.875944 21.128203-84.917018 70.234621-84.917018 127.301338 0 2.366907 0.061398 4.762467 0.182149 7.119141l1.249457 24.296359 276.373515 0 1.238201-24.308639c0.119727-2.358721 0.181125-4.750187 0.181125-7.106862C651.786185 588.497255 616.865861 539.465538 567.129086 518.274914zM512.381182 397.889079c23.937179 0 43.411719 19.430538 43.411719 43.314505 0 23.882943-19.47454 43.313481-43.411719 43.313481-23.936155 0-43.409672-19.430538-43.409672-43.313481C468.971509 417.320641 488.445026 397.889079 512.381182 397.889079zM426.08884 625.656573c9.119705-38.542828 44.254923-67.337641 86.085634-67.337641s76.966952 28.794813 86.085634 67.337641L426.08884 625.656573z" p-id="536"></path></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/password.svg b/ruoyi-ui/src/assets/icons/svg/password.svg new file mode 100644 index 0000000..e291d85 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/password.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M108.8 44.322H89.6v-5.36c0-9.04-3.308-24.163-25.6-24.163-23.145 0-25.6 16.881-25.6 24.162v5.361H19.2v-5.36C19.2 15.281 36.798 0 64 0c27.202 0 44.8 15.281 44.8 38.961v5.361zm-32 39.356c0-5.44-5.763-9.832-12.8-9.832-7.037 0-12.8 4.392-12.8 9.832 0 3.682 2.567 6.808 6.407 8.477v11.205c0 2.718 2.875 4.962 6.4 4.962 3.524 0 6.4-2.244 6.4-4.962V92.155c3.833-1.669 6.393-4.795 6.393-8.477zM128 64v49.201c0 8.158-8.645 14.799-19.2 14.799H19.2C8.651 128 0 121.359 0 113.201V64c0-8.153 8.645-14.799 19.2-14.799h89.6c10.555 0 19.2 6.646 19.2 14.799z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/pdf.svg b/ruoyi-ui/src/assets/icons/svg/pdf.svg new file mode 100644 index 0000000..957aa0c --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/pdf.svg @@ -0,0 +1 @@ +<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="M869.073 277.307H657.111V65.344l211.962 211.963zm-238.232 26.27V65.344l-476.498-.054v416.957h714.73v-178.67H630.841zm-335.836 360.57c-5.07-3.064-10.944-5.133-17.61-6.201-6.67-1.064-13.603-1.6-20.81-1.6h-48.821v85.641h48.822c7.206 0 14.14-.532 20.81-1.6 6.665-1.065 12.54-3.133 17.609-6.202 5.064-3.063 9.134-7.406 12.208-13.007 3.065-5.602 4.6-12.937 4.6-22.011 0-9.07-1.535-16.408-4.6-22.01-3.074-5.603-7.144-9.94-12.208-13.01zM35.82 541.805v416.904h952.358V541.805H35.821zm331.421 191.179c-3.6 11.071-9.343 20.879-17.209 29.413-7.874 8.542-18.078 15.408-30.617 20.61-12.544 5.206-27.747 7.807-45.621 7.807h-66.036v102.45h-62.831V607.517h128.867c17.874 0 33.077 2.6 45.62 7.802 12.541 5.207 22.745 12.076 30.618 20.615 7.866 8.538 13.604 18.277 17.21 29.212 3.6 10.943 5.401 22.278 5.401 34.018 0 11.477-1.8 22.752-5.402 33.819zM644.9 806.417c-5.343 17.61-13.408 32.818-24.212 45.627-10.807 12.803-24.283 22.879-40.423 30.213-16.146 7.343-35.155 11.007-57.03 11.007h-123.26V607.518h123.26c18.41 0 35.552 2.941 51.428 8.808 15.873 5.869 29.618 14.671 41.22 26.412 11.608 11.744 20.674 26.411 27.217 44.02 6.535 17.61 9.803 38.288 9.803 62.035 0 20.81-2.67 40.02-8.003 57.624zm245.362-146.07h-138.07v66.03h119.66v48.829h-119.66v118.058h-62.83V607.518h200.9v52.829h-.001zm-318.2 25.611c-6.402-8.266-14.877-14.604-25.412-19.01-10.544-4.402-23.551-6.602-39.019-6.602h-44.825v180.088h56.029c9.07 0 17.872-1.463 26.415-4.401 8.535-2.932 16.14-7.802 22.812-14.609 6.665-6.8 12.007-15.667 16.007-26.61 4.003-10.94 6.003-24.275 6.003-40.021 0-14.408-1.4-27.416-4.202-39.019-2.8-11.607-7.406-21.542-13.808-29.816zm0 0"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/people.svg b/ruoyi-ui/src/assets/icons/svg/people.svg new file mode 100644 index 0000000..2bd54ae --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/people.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M104.185 95.254c8.161 7.574 13.145 17.441 13.145 28.28 0 1.508-.098 2.998-.285 4.466h-10.784c.238-1.465.403-2.948.403-4.465 0-8.983-4.36-17.115-11.419-23.216C86 104.66 75.355 107.162 64 107.162c-11.344 0-21.98-2.495-31.22-6.83-7.064 6.099-11.444 14.218-11.444 23.203 0 1.517.165 3 .403 4.465H10.955a35.444 35.444 0 0 1-.285-4.465c0-10.838 4.974-20.713 13.127-28.291C9.294 85.42.003 70.417.003 53.58.003 23.99 28.656.001 64 .001s63.997 23.988 63.997 53.58c0 16.842-9.299 31.85-23.812 41.673zM64 36.867c-29.454 0-53.33-10.077-53.33 15.342 0 25.418 23.876 46.023 53.33 46.023 29.454 0 53.33-20.605 53.33-46.023 0-25.419-23.876-15.342-53.33-15.342zm24.888 25.644c-3.927 0-7.111-2.665-7.111-5.953 0-3.288 3.184-5.954 7.11-5.954 3.928 0 7.111 2.666 7.111 5.954s-3.183 5.953-7.11 5.953zm-3.556 16.372c0 4.11-9.55 7.442-21.332 7.442-11.781 0-21.332-3.332-21.332-7.442 0-1.06.656-2.064 1.8-2.976 3.295 2.626 10.79 4.465 19.532 4.465 8.743 0 16.237-1.84 19.531-4.465 1.145.912 1.801 1.916 1.801 2.976zm-46.22-16.372c-3.927 0-7.11-2.665-7.11-5.953 0-3.288 3.183-5.954 7.11-5.954 3.927 0 7.111 2.666 7.111 5.954s-3.184 5.953-7.11 5.953z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/peoples.svg b/ruoyi-ui/src/assets/icons/svg/peoples.svg new file mode 100644 index 0000000..aab852e --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/peoples.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M95.648 118.762c0 5.035-3.563 9.121-7.979 9.121H7.98c-4.416 0-7.979-4.086-7.979-9.121C0 100.519 15.408 83.47 31.152 76.75c-9.099-6.43-15.216-17.863-15.216-30.987v-9.128c0-20.16 14.293-36.518 31.893-36.518s31.894 16.358 31.894 36.518v9.122c0 13.137-6.123 24.556-15.216 30.993 15.738 6.726 31.141 23.769 31.141 42.012z"/><path d="M106.032 118.252h15.867c3.376 0 6.101-3.125 6.101-6.972 0-13.957-11.787-26.984-23.819-32.123 6.955-4.919 11.638-13.66 11.638-23.704v-6.985c0-15.416-10.928-27.926-24.39-27.926-1.674 0-3.306.193-4.89.561 1.936 4.713 3.018 9.974 3.018 15.526v9.121c0 13.137-3.056 23.111-11.066 30.993 14.842 4.41 27.312 23.42 27.541 41.509z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/phone.svg b/ruoyi-ui/src/assets/icons/svg/phone.svg new file mode 100644 index 0000000..ab8e8c4 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/phone.svg @@ -0,0 +1 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1567417214476" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2266" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M761.503029 2.90619 242.121921 2.90619c-32.405037 0-58.932204 26.060539-58.932204 58.527998l0 902.302287c0 32.156374 26.217105 58.216913 58.932204 58.216913l519.381108 0c32.344662 0 58.591443-26.060539 58.591443-58.216913L820.094472 61.123103C820.094472 28.966729 793.847691 2.90619 761.503029 2.90619M452.878996 61.123103l98.147344 0c6.780427 0 12.31549 5.536087 12.31549 12.253068 0 6.748704-5.535063 12.253068-12.31549 12.253068l-98.147344 0c-6.779404 0-12.345166-5.504364-12.345166-12.253068C440.532807 66.659189 446.099592 61.123103 452.878996 61.123103M501.641583 980.593398c-29.636994 0-53.987588-23.946388-53.987588-53.677527 0-29.356608 24.039509-53.614082 53.987588-53.614082 29.91738 0 53.987588 23.883967 53.987588 53.614082C555.629171 956.647009 531.559986 980.593398 501.641583 980.593398M766.35657 803.142893c0 16.23373-13.186324 29.107945-29.233811 29.107945l-470.618521 0c-16.35755 0-29.325909-13.186324-29.325909-29.107945L237.178329 163.500794c0-16.232706 13.279445-29.138644 29.325909-29.138644l470.246037 0c16.420995 0 29.357632 13.1853 29.357632 29.138644l0 639.642099L766.35657 803.142893zM766.35657 803.142893" p-id="2267"></path></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/post.svg b/ruoyi-ui/src/assets/icons/svg/post.svg new file mode 100644 index 0000000..2922c61 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/post.svg @@ -0,0 +1 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1566035724641" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3998" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M136.4 434.3h77.7c21.5 0 38.9-17.4 38.9-38.9s-17.4-38.9-38.9-38.9h-77.7c-21.5 0-38.9 17.4-38.9 38.9s17.4 38.9 38.9 38.9zM252.9 628.6c0-21.5-17.4-38.9-38.9-38.9h-77.7c-21.5 0-38.9 17.4-38.9 38.9s17.4 38.9 38.9 38.9H214c21.5-0.1 38.9-17.5 38.9-38.9z" p-id="3999"></path><path d="M874.7 97.5H227c-28.6 0-51.8 23.2-51.8 51.8v194.3h38.9c28.6 0 51.8 23.2 51.8 51.8 0 28.6-23.2 51.8-51.8 51.8h-38.9v129.5h38.9c28.6 0 51.8 23.2 51.8 51.8 0 28.6-23.2 51.8-51.8 51.8h-38.9v194.3c0 28.6 23.2 51.8 51.8 51.8h647.7c28.6 0 51.8-23.2 51.8-51.8V149.3c0-28.6-23.2-51.8-51.8-51.8z m-311.3 723c-15.6 0-146.7-71.6-146.7-91 0-19.4 102-368.6 102-368.6l-83.6-104s-12.3-23.1 24.6-23.1h208.9c36.9 0 18.4 23.1 18.4 23.1l-79 104s102 351.3 102 368.6c0.1 17.3-131 91-146.6 91z m169.2-253.6l-27.9 40.2-74.5-240 103.4 171.7c4.6 7.9 4.2 20.6-1 28.1z" p-id="4000"></path></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/qq.svg b/ruoyi-ui/src/assets/icons/svg/qq.svg new file mode 100644 index 0000000..ee13d4e --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/qq.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M18.448 57.545l-.244-.744-.198-.968-.132-.53v-2.181l.236-.859.24-.908.317-.953.428-1.06.561-1.103.794-1.104v-.773l.077-.724.123-.984.34-1.106.313-1.194.25-.548.289-.511.371-.569.405-.423v-2.73l.234-1.407.236-1.633.42-1.955.577-2.035.43-1.118.426-1.217.468-1.135.559-1.216.57-1.332.655-1.247.737-1.331.929-1.33.43-.762.457-.624.995-1.406 1.025-1.403 1.163-1.444 1.246-1.405 1.352-1.384 1.41-1.423 1.708-1.536 1.083-.934 1.322-1.008 1.34-.89 1.448-.855 1.392-.76 1.57-.63 1.667-.775 1.657-.532 1.653-.552 1.787-.548 1.785-.417 1.876-.347L59.128.68l1.879-.245 1.876-.252 2.002-.106h5.912l1.97.243 1.981.231 2.019.207 1.874.441 1.979.413 1.857.475 2.035.53 1.862.646 1.782.738 1.904.78 1.736.853 1.689.95 1.655 1.044 1.425.971.662.548.693.401 1.323 1.1 1.115 1.064 1.112 1.1 1.083 1.214.894 1.178 1.064 1.217.74 1.306.752 1.162.798 1.352.661 1.175 1.113 2.489.546 1.286.428 1.192.428 1.294.384 1.217.267 1.047.347 1.231.607 2.198.388 1.924.253 1.861.217 1.497.342 2.28.077.362.274.41.737 1.18.473.8.42.832.534.892.472 1.07.307 1.093.334 1.2.252 1.232.115.605.106.746v.648l-.106.643v.8l-.192.774-.35 1.5-.403.76-.299.852v.213l.142.264.4.623 1.746 2.53 1.377 1.9.66 1.267.889 1.389.774 1.52.893 1.627.894 1.828 1.006 2.069.567 1.268.518 1.239.447 1.307.44 1.175.336 1.235.342 1.16.432 2.261.343 2.31.235 2.05v2.891l-.158 1.025-.226 1.768-.308 1.59-.48 1.44-.18.588-.336.707-.28.493-.375.607-.33.383-.42.494-.375.4-.401.34-.48.207-.432.207-.355.114h-.543l-.346-.114-.66-.32-.302-.212-.317-.223-.347-.304-.35-.342-.579-.63-.684-.89-.539-.917-.538-.734-.526-.855-.741-1.517-.833-1.579-.098-.055h-.138l-.338.247-.196.415-.326.516-.567 1.533-.856 2.182-1.096 2.626-.824 1.308-.864 1.366-1.027 1.536-1.09 1.503-.557.68-.676.743-1.555 1.497.136.135.21.214.777.446 3.235 1.524 1.41.779 1.347.756 1.332.953 1.187.982.574.443.432.511.445.593.367.643.198.533.242.64.105.554.115.647-.115.433v.44l-.105.454-.242.415-.092.325-.22.394-.587.784-.543.627-.42.47-.35.348-.893.638-1.01.556-1.077.532-1.155.511-1.287.495-.693.207-.608.167-1.496.342-1.545.325-1.552.323-1.689.27-1.74.072-1.785.21h-5.539l-1.998-.114-1.86-.168-2.005-.27-1.99-.209-2.095-.286-2.03-.495-1.981-.374-1.968-.552-2.019-.707-1.98-.585-1.044-.342-.927-.323-.586-.223-.582-.12h-1.647l-1.904-.131-.962-.096-1.24-.135-.795.705-1.085.665-1.471.701-1.628.875-.99.475-1.033.376-2.281.914-1.24.305-1.3.343-1.803.344-1.13.086-1.193.1-1.246.135-1.45.053h-5.926l-3.346-.053-3.25-.321-1.644-.23-1.589-.23-1.546-.227-1.547-.305-1.442-.456-1.434-.325-1.294-.51-1.223-.474-1.142-.533-.99-.583-.984-.71-.336-.343-.44-.415-.334-.362-.3-.417-.278-.415-.215-.42-.311-.89-.109-.46-.138-.51v-.473l.138-.533v-.53l.109-.53v-1.069l.052-.564.259-.647.215-.646.39-.779.286-.3.236-.348.615-.738.49-.38.464-.266.428-.338.676-.21.543-.324.676-.341.77-.227.775-.231.897-.192.85-.11 1.008-.13 1.093-.081.284-.092h.063l.137-.115v-.13l-.2-.266-.58-.27-1.45-1.231-.975-.761-1.127-.967-1.136-1.082-1.181-1.382-1.36-1.558-.508-.843-.672-.87-.58-1.007-.522-1.1-.704-1.047-.459-1.194-.547-1.192-.546-1.33-.397-1.273-.378-1.575-.112-.057h-.115l-.059-.113h-.14l-.23.113-.114.057-.158.264-.057.321-.119.286-.206.477-.664 1.157-.345.701-.546.612-.58.736-.641.816-.677.724-.795.701-.734.658-.814.524-.89.546-.855.325-1.008.247-.99.095h-.233l-.228-.095-.18-.384-.29-.188-.38-.912-.237-.493-.255-.707-.21-.734-.113-.724-.313-1.648-.12-.972v-3.185l.12-2.379.196-1.214.23-1.252.21-1.347.374-1.254.42-1.443.431-1.407.578-1.448.545-1.38.754-1.4.699-1.52.855-1.425 1.006-1.538 1.023-1.382 1.069-1.538.891-1.071 1.142-1.227 1.202-1.237.56-.59.678-.662.985-.836 1.012-.853 1.647-1.446 1.242-.889z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/search.svg b/ruoyi-ui/src/assets/icons/svg/search.svg new file mode 100644 index 0000000..84233dd --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/search.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M124.884 109.812L94.256 79.166c-.357-.357-.757-.629-1.129-.914a50.366 50.366 0 0 0 8.186-27.59C101.327 22.689 78.656 0 50.67 0 22.685 0 0 22.688 0 50.663c0 27.989 22.685 50.663 50.656 50.663 10.186 0 19.643-3.03 27.6-8.201.286.385.557.771.9 1.114l30.628 30.632a10.633 10.633 0 0 0 7.543 3.129c2.728 0 5.457-1.043 7.543-3.115 4.171-4.157 4.171-10.915.014-15.073M50.671 85.338C31.557 85.338 16 69.78 16 50.663c0-19.102 15.557-34.661 34.67-34.661 19.115 0 34.657 15.559 34.657 34.675 0 19.102-15.557 34.661-34.656 34.661"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/server.svg b/ruoyi-ui/src/assets/icons/svg/server.svg new file mode 100644 index 0000000..ca37b00 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/server.svg @@ -0,0 +1 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1547360688278" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6717" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M890 120H134a70 70 0 0 0-70 70v500a70 70 0 0 0 70 70h756a70 70 0 0 0 70-70V190a70 70 0 0 0-70-70z m-10 520a40 40 0 0 1-40 40H712V448a40 40 0 0 0-80 0v232h-80V368a40 40 0 0 0-80 0v312h-80V512a40 40 0 0 0-80 0v168H184a40 40 0 0 1-40-40V240a40 40 0 0 1 40-40h656a40 40 0 0 1 40 40zM696 824H328a40 40 0 0 0 0 80h368a40 40 0 0 0 0-80z" fill="#bfbfbf" p-id="6718"></path></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/shopping.svg b/ruoyi-ui/src/assets/icons/svg/shopping.svg new file mode 100644 index 0000000..87513e7 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/shopping.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M42.913 101.36c1.642 0 3.198.332 4.667.996a12.28 12.28 0 0 1 3.89 2.772c1.123 1.184 1.987 2.582 2.592 4.193.605 1.612.908 3.318.908 5.118 0 1.8-.303 3.507-.908 5.118-.605 1.611-1.469 3.01-2.593 4.194a13.3 13.3 0 0 1-3.889 2.843 10.582 10.582 0 0 1-4.667 1.066c-1.729 0-3.306-.355-4.732-1.066a13.604 13.604 0 0 1-3.825-2.843c-1.123-1.185-1.988-2.583-2.593-4.194a14.437 14.437 0 0 1-.907-5.118c0-1.8.302-3.506.907-5.118.605-1.61 1.47-3.009 2.593-4.193a12.515 12.515 0 0 1 3.825-2.772c1.426-.664 3.003-.996 4.732-.996zm53.932.285c1.643 0 3.22.331 4.733.995a11.386 11.386 0 0 1 3.889 2.772c1.08 1.185 1.945 2.583 2.593 4.194.648 1.61.972 3.317.972 5.118 0 1.8-.324 3.506-.972 5.117-.648 1.611-1.513 3.01-2.593 4.194a12.253 12.253 0 0 1-3.89 2.843 11 11 0 0 1-4.732 1.066 10.58 10.58 0 0 1-4.667-1.066 12.478 12.478 0 0 1-3.824-2.843c-1.08-1.185-1.945-2.583-2.593-4.194a13.581 13.581 0 0 1-.973-5.117c0-1.801.325-3.507.973-5.118.648-1.611 1.512-3.01 2.593-4.194a11.559 11.559 0 0 1 3.824-2.772 11.212 11.212 0 0 1 4.667-.995zm21.781-80.747c2.42 0 4.3.355 5.64 1.066 1.34.71 2.29 1.587 2.852 2.63a6.427 6.427 0 0 1 .778 3.34c-.044 1.185-.195 2.204-.454 3.057-.26.853-.8 2.606-1.62 5.26a589.268 589.268 0 0 1-2.788 8.743 1236.373 1236.373 0 0 0-3.047 9.453c-.994 3.128-1.75 5.592-2.269 7.393-1.123 3.79-2.55 6.42-4.278 7.89-1.728 1.469-3.846 2.203-6.352 2.203H39.023l1.945 12.795h65.342c4.148 0 6.223 1.943 6.223 5.828 0 1.896-.41 3.53-1.232 4.905-.821 1.374-2.442 2.061-4.862 2.061H38.505c-1.729 0-3.176-.426-4.343-1.28-1.167-.852-2.14-1.966-2.917-3.34a21.277 21.277 0 0 1-1.88-4.478 44.128 44.128 0 0 1-1.102-4.55c-.087-.568-.324-1.942-.713-4.122-.39-2.18-.865-4.904-1.426-8.174l-1.88-10.947c-.692-4.027-1.383-8.079-2.075-12.154-1.642-9.572-3.5-20.234-5.574-31.986H6.87c-1.296 0-2.377-.356-3.24-1.067a9.024 9.024 0 0 1-2.14-2.558 10.416 10.416 0 0 1-1.167-3.2C.108 8.53 0 7.488 0 6.54c0-1.896.583-3.46 1.75-4.69C2.917.615 4.494 0 6.482 0h13.095c1.728 0 3.111.284 4.148.853 1.037.569 1.858 1.28 2.463 2.132a8.548 8.548 0 0 1 1.297 2.701c.26.948.475 1.754.648 2.417.173.758.346 1.825.519 3.199.173 1.374.345 2.772.518 4.193.26 1.706.519 3.507.778 5.403h88.678z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/size.svg b/ruoyi-ui/src/assets/icons/svg/size.svg new file mode 100644 index 0000000..ddb25b8 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/size.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 54.857h54.796v18.286H36.531V128H18.265V73.143H0V54.857zm127.857-36.571H91.935V128H72.456V18.286H36.534V0h91.326l-.003 18.286z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/skill.svg b/ruoyi-ui/src/assets/icons/svg/skill.svg new file mode 100644 index 0000000..a3b7312 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/skill.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M31.652 93.206h33.401c1.44 2.418 3.077 4.663 4.93 6.692h-38.33v-6.692zm0-10.586h28.914a44.8 44.8 0 0 1-1.264-6.688h-27.65v6.688zm0-17.27H59.39c.288-2.286.714-4.532 1.34-6.687H31.65v6.687h.003zm53.913 44.84v5.85c0 2.798-2.095 5.075-4.667 5.075h-70.07c-2.576 0-4.663-2.277-4.663-5.075V31.26l23.22-20.96v22.25H17.16v6.688h18.39V6.688h45.348c2.576 0 4.667 2.277 4.667 5.066v20.009c1.987-.675 4.053-1.128 6.17-1.445v-18.56C91.738 5.28 86.874 0 80.902 0H31.15L0 28.118v87.917c0 6.48 4.859 11.759 10.832 11.759h70.07c5.974 0 10.837-5.27 10.837-11.759v-4.41c-2.117-.312-4.183-.765-6.17-1.435h-.004zM23.279 58.667h-7.96v6.688h7.96v-6.688zm-7.956 41.23h7.96v-6.691h-7.96v6.692zm7.956-23.96h-7.96v6.687h7.96v-6.688zm89.718-15.042l-4.896-4.07-12.447 17.613-11.19-9.305-3.762 5.311 16.091 13.38 16.204-22.929zM128 70.978c0-18.632-13.97-33.782-31.147-33.782-17.168 0-31.135 15.155-31.135 33.782 0 18.628 13.97 33.783 31.135 33.783 17.172 0 31.143-15.15 31.143-33.783H128zm-6.17 0c0 14.933-11.203 27.1-24.981 27.1-13.77 0-24.987-12.158-24.987-27.1 0-14.941 11.195-27.099 24.987-27.099 13.778 0 24.982 12.158 24.982 27.1z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/star.svg b/ruoyi-ui/src/assets/icons/svg/star.svg new file mode 100644 index 0000000..6cf86e6 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/star.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M70.66 4.328l14.01 29.693c1.088 2.29 3.177 3.882 5.603 4.25l31.347 4.76c6.087.926 8.528 8.756 4.117 13.247L103.05 79.395c-1.75 1.78-2.544 4.352-2.132 6.867l5.352 32.641c1.043 6.337-5.33 11.182-10.778 8.19l-28.039-15.409a7.13 7.13 0 0 0-6.91 0l-28.039 15.41c-5.448 2.99-11.821-1.854-10.777-8.19l5.352-32.642c.415-2.515-.387-5.088-2.136-6.867L2.264 56.278C-2.146 51.787.286 43.957 6.38 43.031l31.343-4.76c2.419-.368 4.51-1.96 5.595-4.25L57.334 4.328c2.728-5.77 10.605-5.77 13.325 0z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/swagger.svg b/ruoyi-ui/src/assets/icons/svg/swagger.svg new file mode 100644 index 0000000..05d4e7b --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/swagger.svg @@ -0,0 +1 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1566036776944" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6463" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M64 223.995345h168.001164v47.997673c0 26.428509 18.878836 47.997673 41.984 47.997673h140.036654c23.095855 0 41.984-21.569164 41.984-47.997673v-47.997673h504.003491a32.004655 32.004655 0 0 0 0-64.009309H455.996509V111.988364c0-26.428509-18.878836-47.997673-41.984-47.997673H273.985164c-23.095855 0-41.984 21.569164-41.984 47.997673v47.997672H64a32.004655 32.004655 0 0 0 0 64.009309zM288.004655 128h111.997672V256H288.004655V128zM960 479.995345H791.998836v-47.997672c0-26.372655-18.878836-47.997673-41.984-47.997673H609.978182c-23.095855 0-41.984 21.634327-41.984 47.997673v47.997672H64a32.004655 32.004655 0 0 0 0 64.00931h504.003491v47.997672c0 26.363345 18.878836 47.997673 41.984 47.997673h140.036654c23.095855 0 41.984-21.634327 41.984-47.997673v-47.997672h168.001164a32.004655 32.004655 0 1 0-0.009309-64.00931zM735.995345 576H623.997673v-128h111.997672v128zM960 800.293236v-0.288581H455.996509v-47.997673c0-26.363345-18.878836-47.997673-41.984-47.997673H274.050327c-23.105164 0-41.984 21.634327-41.984 47.997673v47.997673H64v0.288581a32.004655 32.004655 0 0 0 0 64.009309c0.986764 0 1.917673-0.195491 2.885818-0.288581h165.115346v47.997672c0 26.363345 18.878836 47.997673 41.984 47.997673h140.036654c23.095855 0 41.984-21.634327 41.984-47.997673v-47.997672h501.108364c0.968145 0.093091 1.899055 0.288582 2.895127 0.288581a32.004655 32.004655 0 1 0-0.009309-64.009309zM400.002327 896H288.004655V768h111.997672v128z" fill="" p-id="6464"></path></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/system.svg b/ruoyi-ui/src/assets/icons/svg/system.svg new file mode 100644 index 0000000..86cb938 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/system.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1543827724451" class="icon" style="" viewBox="0 0 1084 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10233" xmlns:xlink="http://www.w3.org/1999/xlink" width="211.71875" height="200"><defs><style type="text/css">@font-face { font-family: rbicon; src: url("chrome-extension://dipiagiiohfljcicegpgffpbnjmgjcnf/fonts/rbicon.woff2") format("woff2"); font-weight: normal; font-style: normal; } +</style></defs><path d="M1080.09609 434.500756c-4.216302-23.731757-26.9241-47.945376-50.595623-53.185637l-17.648235-4.095836a175.940257 175.940257 0 0 1-101.612877-80.832531 177.807476 177.807476 0 0 1-18.732427-129.801867l5.541425-16.684509c7.10748-23.129428-2.108151-54.992624-20.599646-70.833873 0 0-16.624276-14.094495-63.244529-41.199293-46.800951-26.984332-66.858502-34.513443-66.858502-34.513443-22.76803-8.372371-54.631227-0.361397-71.255503 17.407304l-12.287509 13.251234a173.470708 173.470708 0 0 1-120.465769 48.065842A174.13327 174.13327 0 0 1 421.329029 33.590675L409.583617 20.761071C393.140039 2.99237 361.096144-4.898138 338.267881 3.353767c0 0-20.358715 7.529111-67.099434 34.513443-46.800951 27.34573-63.244529 41.440225-63.244529 41.440225-18.431263 15.66055-27.646894 47.222582-20.539413 70.592941l5.059562 16.865207a178.048407 178.048407 0 0 1-18.672194 129.621169 174.916297 174.916297 0 0 1-102.275439 81.073463l-17.045906 3.854904c-23.310126 5.42096-46.258856 29.333415-50.595623 53.185637 0 0-3.854905 21.382674-3.854905 75.712737 0 54.330062 3.854905 75.712736 3.854905 75.712736 4.216302 23.972688 26.9241 47.945376 50.595623 53.185637l16.624276 3.854905a174.253736 174.253736 0 0 1 102.395904 81.314394c23.310126 40.837896 28.911785 87.337683 18.732427 129.801867l-4.81863 16.443578c-7.10748 23.129428 2.108151 54.992624 20.599646 70.833872 0 0 16.624276 14.094495 63.244529 41.199293 46.800951 27.104798 66.918735 34.513443 66.918735 34.513443 22.707798 8.372371 54.631227 0.361397 71.255503-17.407303l11.624947-12.588673a175.096996 175.096996 0 0 1 242.256662 0.120465l11.624947 12.648906c16.383345 17.708468 48.427239 25.598976 71.255503 17.347071 0 0 20.358715-7.529111 67.159666-34.513443 46.740719-27.104798 63.124063-41.199293 63.124064-41.199293 18.491496-15.600317 27.707127-47.463513 20.599646-70.833873l-5.059562-17.106139a176.723284 176.723284 0 0 1 18.672194-129.139305 176.060722 176.060722 0 0 1 102.395904-81.314394l16.68451-3.854905c23.310126-5.42096 46.258856-29.333415 50.595623-53.185637 0 0 3.854905-21.382674 3.854904-75.712737-0.240932-54.330062-4.095836-75.833202-4.095836-75.833202z m-537.819428 293.334149c-119.261112 0-216.175824-97.336342-216.175824-217.621412a216.657687 216.657687 0 0 1 216.236057-217.320249c119.200879 0 216.115591 97.276109 216.11559 217.56118-0.240932 120.044139-96.974945 217.320248-216.175823 217.320249z" p-id="10234" fill="#bfbfbf"></path></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/tab.svg b/ruoyi-ui/src/assets/icons/svg/tab.svg new file mode 100644 index 0000000..b4b48e4 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/tab.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M78.921.052H49.08c-1.865 0-3.198 1.599-3.198 3.464v6.661c0 1.865 1.6 3.464 3.198 3.464h29.84c1.865 0 3.198-1.599 3.198-3.464V3.516C82.385 1.65 80.786.052 78.92.052zm45.563 0H94.642c-1.865 0-3.464 1.599-3.464 3.464v6.661c0 1.865 1.599 3.464 3.464 3.464h29.842c1.865-.266 3.464-1.599 3.464-3.464V3.516c0-1.865-1.599-3.464-3.464-3.464zm0 22.382H40.02c-1.866 0-3.464-1.599-3.464-3.464V3.516c0-1.865-1.599-3.464-3.464-3.464H3.516C1.65.052.052 1.651.052 3.516V124.75c0 1.598 1.599 3.197 3.464 3.197h120.968c1.865 0 3.464-1.599 3.464-3.464V25.898c0-1.865-1.599-3.464-3.464-3.464z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/table.svg b/ruoyi-ui/src/assets/icons/svg/table.svg new file mode 100644 index 0000000..0e3dc9d --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/table.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M.006.064h127.988v31.104H.006V.064zm0 38.016h38.396v41.472H.006V38.08zm0 48.384h38.396v41.472H.006V86.464zM44.802 38.08h38.396v41.472H44.802V38.08zm0 48.384h38.396v41.472H44.802V86.464zM89.598 38.08h38.396v41.472H89.598zm0 48.384h38.396v41.472H89.598z"/><path d="M.006.064h127.988v31.104H.006V.064zm0 38.016h38.396v41.472H.006V38.08zm0 48.384h38.396v41.472H.006V86.464zM44.802 38.08h38.396v41.472H44.802V38.08zm0 48.384h38.396v41.472H44.802V86.464zM89.598 38.08h38.396v41.472H89.598zm0 48.384h38.396v41.472H89.598z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/theme.svg b/ruoyi-ui/src/assets/icons/svg/theme.svg new file mode 100644 index 0000000..5982a2f --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/theme.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M125.5 36.984L95.336 2.83C93.735 1.018 91.565 0 89.3 0c-2.263 0-4.433 1.018-6.033 2.83l-3.786 4.286c-1.6 1.812-3.77 2.83-6.032 2.831H54.553c-2.263 0-4.434-1.018-6.033-2.83L44.734 2.83C43.134 1.018 40.964 0 38.701 0c-2.263 0-4.434 1.018-6.034 2.83L2.5 36.984C.9 38.796 0 41.254 0 43.815c0 2.562.899 5.02 2.5 6.831L14.565 64.31c2.178 2.468 5.367 3.403 8.33 2.444 1.35-.435 2.709.592 2.709 2.18v49.407c0 5.313 3.84 9.66 8.532 9.66h59.726c4.693 0 8.532-4.347 8.532-9.66V68.934c0-1.59 1.36-2.616 2.71-2.181 2.962.96 6.15.024 8.329-2.444L125.5 50.646c1.6-1.811 2.499-4.269 2.499-6.83 0-2.563-.899-5.02-2.5-6.832z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/tool.svg b/ruoyi-ui/src/assets/icons/svg/tool.svg new file mode 100644 index 0000000..c813067 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/tool.svg @@ -0,0 +1 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1553828490559" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1684" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M898.831744 900.517641 103.816972 900.517641c-36.002982 0-65.363683-29.286-65.363683-65.313541l0-554.949184c0-36.041868 29.361725-65.326844 65.363683-65.326844l795.015795 0c36.002982 0 65.198931 29.284977 65.198931 65.326844l0 554.949184C964.030675 871.231641 934.834726 900.517641 898.831744 900.517641L898.831744 900.517641zM103.816972 255.593236c-13.576203 0-24.711821 11.085476-24.711821 24.662703l0 554.949184c0 13.576203 11.136641 24.662703 24.711821 24.662703l795.015795 0c13.577227 0 24.547069-11.086499 24.547069-24.662703l0-554.949184c0-13.577227-10.970866-24.662703-24.547069-24.662703L103.816972 255.593236 103.816972 255.593236zM664.346245 251.774257c-11.161201 0-20.332071-9.080819-20.332071-20.332071l0-101.278661c0-13.576203-11.047614-24.623817-24.699542-24.623817L383.181611 105.539708c-13.576203 0-24.712845 11.04659-24.712845 24.623817l0 101.278661c0 11.252275-9.041934 20.332071-20.332071 20.332071-11.20111 0-20.319791-9.080819-20.319791-20.332071l0-101.278661c0-35.989679 29.323862-65.275679 65.364707-65.275679l236.133022 0c36.06745 0 65.402569 29.284977 65.402569 65.275679l0 101.278661C684.717202 242.694461 675.636383 251.774257 664.346245 251.774257L664.346245 251.774257zM413.233044 521.725502 75.694471 521.725502c-11.163247 0-20.333094-9.117658-20.333094-20.35663 0-11.252275 9.169847-20.332071 20.333094-20.332071l337.538573 0c11.277858 0 20.319791 9.080819 20.319791 20.332071C433.552835 512.607844 424.510902 521.725502 413.233044 521.725502L413.233044 521.725502zM912.894018 521.725502 575.367725 521.725502c-11.213389 0-20.332071-9.117658-20.332071-20.35663 0-11.252275 9.118682-20.332071 20.332071-20.332071l337.526293 0c11.290137 0 20.332071 9.080819 20.332071 20.332071C933.226089 512.607844 924.184155 521.725502 912.894018 521.725502L912.894018 521.725502zM557.56322 634.217552 445.085496 634.217552c-11.213389 0-20.332071-9.079796-20.332071-20.331048l0-168.763658c0-11.251252 9.118682-20.332071 20.332071-20.332071l112.478747 0c11.290137 0 20.370956 9.080819 20.370956 20.332071l0 168.763658C577.934177 625.137757 568.853357 634.217552 557.56322 634.217552L557.56322 634.217552zM465.417567 593.514525l71.827909 0L537.245476 465.454918l-71.827909 0L465.417567 593.514525 465.417567 593.514525z" p-id="1685" fill="#bfbfbf"></path></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/tree-table.svg b/ruoyi-ui/src/assets/icons/svg/tree-table.svg new file mode 100644 index 0000000..8aafdb8 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/tree-table.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M44.8 0h79.543C126.78 0 128 1.422 128 4.267v23.466c0 2.845-1.219 4.267-3.657 4.267H44.8c-2.438 0-3.657-1.422-3.657-4.267V4.267C41.143 1.422 42.362 0 44.8 0zm22.857 48h56.686c2.438 0 3.657 1.422 3.657 4.267v23.466c0 2.845-1.219 4.267-3.657 4.267H67.657C65.22 80 64 78.578 64 75.733V52.267C64 49.422 65.219 48 67.657 48zm0 48h56.686c2.438 0 3.657 1.422 3.657 4.267v23.466c0 2.845-1.219 4.267-3.657 4.267H67.657C65.22 128 64 126.578 64 123.733v-23.466C64 97.422 65.219 96 67.657 96zM50.286 68.267c2.02 0 3.657-1.91 3.657-4.267 0-2.356-1.638-4.267-3.657-4.267H17.37V32h6.4c2.02 0 3.658-1.91 3.658-4.267V4.267C27.429 1.91 25.79 0 23.77 0H3.657C1.637 0 0 1.91 0 4.267v23.466C0 30.09 1.637 32 3.657 32h6.4v80c0 2.356 1.638 4.267 3.657 4.267h36.572c2.02 0 3.657-1.91 3.657-4.267 0-2.356-1.638-4.267-3.657-4.267H17.37V68.267h32.915z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/tree.svg b/ruoyi-ui/src/assets/icons/svg/tree.svg new file mode 100644 index 0000000..dd4b7dd --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/tree.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M126.713 90.023c.858.985 1.287 2.134 1.287 3.447v29.553c0 1.423-.429 2.6-1.287 3.53-.858.93-1.907 1.395-3.146 1.395H97.824c-1.145 0-2.146-.465-3.004-1.395-.858-.93-1.287-2.107-1.287-3.53V93.47c0-.875.19-1.696.572-2.462.382-.766.906-1.368 1.573-1.806a3.84 3.84 0 0 1 2.146-.657h9.725V69.007a3.84 3.84 0 0 0-.43-1.806 3.569 3.569 0 0 0-1.143-1.313 2.714 2.714 0 0 0-1.573-.492h-36.47v23.149h9.725c1.144 0 2.145.492 3.004 1.478.858.985 1.287 2.134 1.287 3.447v29.553c0 .876-.191 1.696-.573 2.463-.38.766-.905 1.368-1.573 1.806a3.84 3.84 0 0 1-2.145.656H51.915a3.84 3.84 0 0 1-2.145-.656c-.668-.438-1.216-1.04-1.645-1.806a4.96 4.96 0 0 1-.644-2.463V93.47c0-1.313.43-2.462 1.288-3.447.858-.986 1.907-1.478 3.146-1.478h9.582v-23.15h-37.9c-.953 0-1.74.356-2.359 1.068-.62.711-.93 1.56-.93 2.544v19.538h9.726c1.239 0 2.264.492 3.074 1.478.81.985 1.216 2.134 1.216 3.447v29.553c0 1.423-.405 2.6-1.216 3.53-.81.93-1.835 1.395-3.074 1.395H4.29c-.476 0-.93-.082-1.358-.246a4.1 4.1 0 0 1-1.144-.657 4.658 4.658 0 0 1-.93-1.067 5.186 5.186 0 0 1-.643-1.395 5.566 5.566 0 0 1-.215-1.56V93.47c0-.437.048-.875.143-1.313a3.95 3.95 0 0 1 .429-1.15c.19-.328.429-.656.715-.984.286-.329.572-.602.858-.821.286-.22.62-.383 1.001-.493.382-.11.763-.164 1.144-.164h9.726V61.619c0-.985.31-1.833.93-2.544.619-.712 1.358-1.068 2.216-1.068h44.335V39.62h-9.582c-1.24 0-2.288-.492-3.146-1.477a5.09 5.09 0 0 1-1.287-3.448V5.14c0-1.423.429-2.627 1.287-3.612.858-.985 1.907-1.477 3.146-1.477h25.743c.763 0 1.478.246 2.145.739a5.17 5.17 0 0 1 1.573 1.888c.382.766.573 1.587.573 2.462v29.553c0 1.313-.43 2.463-1.287 3.448-.859.985-1.86 1.477-3.004 1.477h-9.725v18.389h42.762c.954 0 1.74.355 2.36 1.067.62.711.93 1.56.93 2.545v26.925h9.582c1.239 0 2.288.492 3.146 1.478z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/user.svg b/ruoyi-ui/src/assets/icons/svg/user.svg new file mode 100644 index 0000000..0ba0716 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/user.svg @@ -0,0 +1 @@ +<svg width="130" height="130" xmlns="http://www.w3.org/2000/svg"><path d="M63.444 64.996c20.633 0 37.359-14.308 37.359-31.953 0-17.649-16.726-31.952-37.359-31.952-20.631 0-37.36 14.303-37.358 31.952 0 17.645 16.727 31.953 37.359 31.953zM80.57 75.65H49.434c-26.652 0-48.26 18.477-48.26 41.27v2.664c0 9.316 21.608 9.325 48.26 9.325H80.57c26.649 0 48.256-.344 48.256-9.325v-2.663c0-22.794-21.605-41.271-48.256-41.271z" stroke="#979797"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/validCode.svg b/ruoyi-ui/src/assets/icons/svg/validCode.svg new file mode 100644 index 0000000..cfb1021 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/validCode.svg @@ -0,0 +1 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1569580729849" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1939" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M513.3 958.5c-142.2 0-397.9-222.1-401.6-440.5V268c1.7-39.6 31.7-72.3 71.1-77.3 49-4.6 97.1-16.5 142.7-35.3 47.8-14 91.9-38.3 129.4-71.1 30.3-24.4 72.9-26.3 105.3-4.6 39.9 30.7 83.8 55.9 130.5 74.6 48.6 14.7 98.2 25.9 148.4 33.7 38.5 7.6 67.1 40.3 69.5 79.5 3.3 84.9 2.5 169.9-2.6 254.7-33.7 281.6-253.7 436.4-392.7 436.3z m-0.1-813.7c-7.2-0.2-14.3 2-20 6.4-39.7 35.2-86.8 61.1-137.7 75.7-46.8 19.2-96.2 31-146.6 35.2-11 3.2-18.8 13-19.5 24.4v230.1c3.5 180.3 223.3 361 323.9 361s287.3-120.2 317.6-360.5c7.3-142.7 0-228.6 0-229.6-1.3-13.3-11-24.3-24-27.3-49.6-7.7-98.6-19-146.5-33.7-46.3-19.5-89.7-45.3-129-76.7-5.8-3.8-12.7-5.5-19.5-4.9l1.3-0.1z" fill="#C6CCDA" p-id="1940"></path><path d="M750.1 428L490.7 673.2c-11.7 11.1-29.5 12.9-43.1 4.2l-6.8-5.8-141.2-149.4c-9.3-9.3-12.7-22.9-9-35.5 3.8-12.6 14.1-22.1 27-24.8 12.9-2.7 26.1 1.9 34.6 11.9L469 597.5l233.7-221c14.6-12.8 36.8-11.6 49.9 2.7 13.2 14.2 11.5 35.3-2.5 48.8" fill="#C6CCDA" p-id="1941"></path></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/wechat.svg b/ruoyi-ui/src/assets/icons/svg/wechat.svg new file mode 100644 index 0000000..c586e55 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/wechat.svg @@ -0,0 +1 @@ +<svg width="128" height="110" xmlns="http://www.w3.org/2000/svg"><path d="M86.635 33.334c1.467 0 2.917.113 4.358.283C87.078 14.392 67.58.111 45.321.111 20.44.111.055 17.987.055 40.687c0 13.104 6.781 23.863 18.115 32.209l-4.527 14.352 15.82-8.364c5.666 1.182 10.207 2.395 15.858 2.395 1.42 0 2.829-.073 4.227-.189-.886-3.19-1.398-6.53-1.398-9.996 0-20.845 16.98-37.76 38.485-37.76zm-24.34-12.936c3.407 0 5.665 2.363 5.665 5.954 0 3.576-2.258 5.97-5.666 5.97-3.392 0-6.795-2.395-6.795-5.97 0-3.591 3.403-5.954 6.795-5.954zM30.616 32.323c-3.393 0-6.818-2.395-6.818-5.971 0-3.591 3.425-5.954 6.818-5.954 3.392 0 5.65 2.363 5.65 5.954 0 3.576-2.258 5.97-5.65 5.97z"/><path d="M127.945 70.52c0-19.075-18.108-34.623-38.448-34.623-21.537 0-38.5 15.548-38.5 34.623 0 19.108 16.963 34.622 38.5 34.622 4.508 0 9.058-1.2 13.584-2.395l12.414 7.167-3.404-11.923c9.087-7.184 15.854-16.712 15.854-27.471zm-50.928-5.97c-2.254 0-4.53-2.362-4.53-4.773 0-2.378 2.276-4.771 4.53-4.771 3.422 0 5.665 2.393 5.665 4.771 0 2.41-2.243 4.773-5.665 4.773zm24.897 0c-2.24 0-4.498-2.362-4.498-4.773 0-2.378 2.258-4.771 4.498-4.771 3.392 0 5.665 2.393 5.665 4.771 0 2.41-2.273 4.773-5.665 4.773z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/zip.svg b/ruoyi-ui/src/assets/icons/svg/zip.svg new file mode 100644 index 0000000..f806fc4 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/zip.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M78.527 116.793c.178.008.348.024.527.024h40.233c4.711-.005 8.53-3.677 8.534-8.21V18.895c-.004-4.532-3.823-8.204-8.534-8.209H79.054c-.179 0-.353.016-.527.024V0L0 10.082v107.406l78.527 10.342v-11.037zm0-101.362c.174-.024.348-.052.527-.052h40.233c2.018 0 3.659 1.578 3.659 3.52v89.713c-.003 1.942-1.64 3.517-3.659 3.519H79.054c-.179 0-.353-.028-.527-.052V15.431zM30.262 75.757l-18.721-.46V72.37l11.3-16.673v-.148l-10.266.164v-4.51l17.504-.44v3.264L18.696 70.76v.144l11.566.176v4.678zm9.419.231l-5.823-.144V50.671l5.823-.144v25.461zm22.255-11.632c-2.168 1.922-5.353 2.76-9.02 2.736-.702.004-1.402-.04-2.097-.131v9.303l-5.997-.148V50.743c1.852-.352 4.473-.647 8.218-.743 3.838-.096 6.608.539 8.48 1.913 1.807 1.306 3.032 3.5 3.032 6.112s-.926 4.833-2.612 6.331h-.004zM53.36 54.45c-.856-.01-1.71.083-2.541.275v7.682c.523.116 1.167.152 2.06.152 3.301-.004 5.36-1.614 5.36-4.314 0-2.425-1.772-3.843-4.875-3.791l-.004-.004zm39.847-37.066h9.564v3.795h-9.564v-3.795zm-9.568 5.68h9.564v3.8h-9.564v-3.8zm9.568 6.216h9.564v3.799h-9.564V29.28zm0 12h9.564v3.794h-9.564V41.28zm-9.568-6.096h9.564v3.795h-9.564v-3.795zm9.472 47.064c2.512 0 4.921-.96 6.697-2.67 1.776-1.708 2.773-4.026 2.772-6.442l-1.748-15.263c0-5.033-2.492-9.112-7.725-9.112-5.232 0-7.72 4.079-7.72 9.112l-1.752 15.263c-.001 2.417.996 4.735 2.773 6.444 1.777 1.71 4.187 2.669 6.7 2.668h.003zm-3.135-16.75h6.27v12.743h-6.27V65.5z"/></svg> \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svgo.yml b/ruoyi-ui/src/assets/icons/svgo.yml new file mode 100644 index 0000000..14f8f5c --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svgo.yml @@ -0,0 +1,22 @@ +# replace default config + +# multipass: true +# full: true + +plugins: + + # - name + # + # or: + # - name: false + # - name: true + # + # or: + # - name: + # param1: 1 + # param2: 2 + +- removeAttrs: + attrs: + - 'fill' + - 'fill-rule' diff --git a/ruoyi-ui/src/assets/image/login-background.jpg b/ruoyi-ui/src/assets/image/login-background.jpg new file mode 100644 index 0000000..8a89eb8 --- /dev/null +++ b/ruoyi-ui/src/assets/image/login-background.jpg Binary files differ diff --git a/ruoyi-ui/src/assets/image/profile.jpg b/ruoyi-ui/src/assets/image/profile.jpg new file mode 100644 index 0000000..b3a940b --- /dev/null +++ b/ruoyi-ui/src/assets/image/profile.jpg Binary files differ diff --git a/ruoyi-ui/src/assets/logo/logo.png b/ruoyi-ui/src/assets/logo/logo.png new file mode 100644 index 0000000..e263760 --- /dev/null +++ b/ruoyi-ui/src/assets/logo/logo.png Binary files differ diff --git a/ruoyi-ui/src/assets/styles/btn.scss b/ruoyi-ui/src/assets/styles/btn.scss new file mode 100644 index 0000000..1ccb57d --- /dev/null +++ b/ruoyi-ui/src/assets/styles/btn.scss @@ -0,0 +1,99 @@ +@import './variables.scss'; + +@mixin colorBtn($color) { + background: $color; + + &:hover { + color: $color; + + &:before, + &:after { + background: $color; + } + } +} + +.blue-btn { + @include colorBtn($blue) +} + +.light-blue-btn { + @include colorBtn($light-blue) +} + +.red-btn { + @include colorBtn($red) +} + +.pink-btn { + @include colorBtn($pink) +} + +.green-btn { + @include colorBtn($green) +} + +.tiffany-btn { + @include colorBtn($tiffany) +} + +.yellow-btn { + @include colorBtn($yellow) +} + +.pan-btn { + font-size: 14px; + color: #fff; + padding: 14px 36px; + border-radius: 8px; + border: none; + outline: none; + transition: 600ms ease all; + position: relative; + display: inline-block; + + &:hover { + background: #fff; + + &:before, + &:after { + width: 100%; + transition: 600ms ease all; + } + } + + &:before, + &:after { + content: ''; + position: absolute; + top: 0; + right: 0; + height: 2px; + width: 0; + transition: 400ms ease all; + } + + &::after { + right: inherit; + top: inherit; + left: 0; + bottom: 0; + } +} + +.custom-button { + display: inline-block; + line-height: 1; + white-space: nowrap; + cursor: pointer; + background: #fff; + color: #fff; + -webkit-appearance: none; + text-align: center; + box-sizing: border-box; + outline: 0; + margin: 0; + padding: 10px 15px; + font-size: 14px; + border-radius: 4px; +} diff --git a/ruoyi-ui/src/assets/styles/element-ui.scss b/ruoyi-ui/src/assets/styles/element-ui.scss new file mode 100644 index 0000000..35c224e --- /dev/null +++ b/ruoyi-ui/src/assets/styles/element-ui.scss @@ -0,0 +1,79 @@ +// cover some element-ui styles + +.el-breadcrumb__inner, +.el-breadcrumb__inner a { + font-weight: 400 !important; +} + +.el-upload { + input[type="file"] { + display: none !important; + } +} + +.el-upload__input { + display: none; +} + +.cell { + .el-tag { + margin-right: 0px; + } +} + +.small-padding { + .cell { + padding-left: 5px; + padding-right: 5px; + } +} + +.fixed-width { + .el-button--mini { + padding: 7px 10px; + width: 60px; + } +} + +.status-col { + .cell { + padding: 0 10px; + text-align: center; + + .el-tag { + margin-right: 0px; + } + } +} + +// to fixed https://github.com/ElemeFE/element/issues/2461 +.el-dialog { + transform: none; + left: 0; + position: relative; + margin: 0 auto; +} + +// refine element ui upload +.upload-container { + .el-upload { + width: 100%; + + .el-upload-dragger { + width: 100%; + height: 200px; + } + } +} + +// dropdown +.el-dropdown-menu { + a { + display: block + } +} + +// fix date-picker ui bug in filter-item +.el-range-editor.el-input__inner { + display: inline-flex !important; +} diff --git a/ruoyi-ui/src/assets/styles/element-variables.scss b/ruoyi-ui/src/assets/styles/element-variables.scss new file mode 100644 index 0000000..bc7697e --- /dev/null +++ b/ruoyi-ui/src/assets/styles/element-variables.scss @@ -0,0 +1,31 @@ +/** +* I think element-ui's default theme color is too light for long-term use. +* So I modified the default color and you can modify it to your liking. +**/ + +/* theme color */ +$--color-primary: #1890ff; +$--color-success: #13ce66; +$--color-warning: #FFBA00; +$--color-danger: #ff4949; +// $--color-info: #1E1E1E; + +$--button-font-weight: 400; + +// $--color-text-regular: #1f2d3d; + +$--border-color-light: #dfe4ed; +$--border-color-lighter: #e6ebf5; + +$--table-border:1px solid#dfe6ec; + +/* icon font path, required */ +$--font-path: '~element-ui/lib/theme-chalk/fonts'; + +@import "~element-ui/packages/theme-chalk/src/index"; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + theme: $--color-primary; +} diff --git a/ruoyi-ui/src/assets/styles/index.scss b/ruoyi-ui/src/assets/styles/index.scss new file mode 100644 index 0000000..9f536ae --- /dev/null +++ b/ruoyi-ui/src/assets/styles/index.scss @@ -0,0 +1,191 @@ +@import './variables.scss'; +@import './mixin.scss'; +@import './transition.scss'; +@import './element-ui.scss'; +@import './sidebar.scss'; +@import './btn.scss'; + +body { + height: 100%; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; +} + +label { + font-weight: 700; +} + +html { + height: 100%; + box-sizing: border-box; +} + +#app { + height: 100%; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} + +.no-padding { + padding: 0px !important; +} + +.padding-content { + padding: 4px 0; +} + +a:focus, +a:active { + outline: none; +} + +a, +a:focus, +a:hover { + cursor: pointer; + color: inherit; + text-decoration: none; +} + +div:focus { + outline: none; +} + +.fr { + float: right; +} + +.fl { + float: left; +} + +.pr-5 { + padding-right: 5px; +} + +.pl-5 { + padding-left: 5px; +} + +.block { + display: block; +} + +.pointer { + cursor: pointer; +} + +.inlineBlock { + display: block; +} + +.clearfix { + &:after { + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; + } +} + +aside { + background: #eef1f6; + padding: 8px 24px; + margin-bottom: 20px; + border-radius: 2px; + display: block; + line-height: 32px; + font-size: 16px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + color: #2c3e50; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + a { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } + } +} + +//main-container全局样式 +.app-container { + padding: 20px; +} + +.components-container { + margin: 30px 50px; + position: relative; +} + +.pagination-container { + margin-top: 30px; +} + +.text-center { + text-align: center +} + +.sub-navbar { + height: 50px; + line-height: 50px; + position: relative; + width: 100%; + text-align: right; + padding-right: 20px; + transition: 600ms ease position; + background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%); + + .subtitle { + font-size: 20px; + color: #fff; + } + + &.draft { + background: #d0d0d0; + } + + &.deleted { + background: #d0d0d0; + } +} + +.link-type, +.link-type:focus { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } +} + +.filter-container { + padding-bottom: 10px; + + .filter-item { + display: inline-block; + vertical-align: middle; + margin-bottom: 10px; + } +} + +//refine vue-multiselect plugin +.multiselect { + line-height: 16px; +} + +.multiselect--active { + z-index: 1000 !important; +} diff --git a/ruoyi-ui/src/assets/styles/mixin.scss b/ruoyi-ui/src/assets/styles/mixin.scss new file mode 100644 index 0000000..64d9cf6 --- /dev/null +++ b/ruoyi-ui/src/assets/styles/mixin.scss @@ -0,0 +1,66 @@ +@mixin clearfix { + &:after { + content: ""; + display: table; + clear: both; + } +} + +@mixin scrollBar { + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } +} + +@mixin relative { + position: relative; + width: 100%; + height: 100%; +} + +@mixin pct($pct) { + width: #{$pct}; + position: relative; + margin: 0 auto; +} + +@mixin triangle($width, $height, $color, $direction) { + $width: $width/2; + $color-border-style: $height solid $color; + $transparent-border-style: $width solid transparent; + height: 0; + width: 0; + + @if $direction==up { + border-bottom: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==right { + border-left: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } + + @else if $direction==down { + border-top: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==left { + border-right: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } +} diff --git a/ruoyi-ui/src/assets/styles/ruoyi.scss b/ruoyi-ui/src/assets/styles/ruoyi.scss new file mode 100644 index 0000000..7c3aca1 --- /dev/null +++ b/ruoyi-ui/src/assets/styles/ruoyi.scss @@ -0,0 +1,126 @@ + /** + * 通用css样式布局处理 + * Copyright (c) 2019 ruoyi + */ + +.el-table .el-table__header-wrapper th { + word-break: break-word; + background-color: #f8f8f9; + color: #515a6e; + height: 40px; + font-size: 13px; +} + +.pagination-container { + position: relative; + height: 25px; + margin-bottom: 10px; + margin-top: 15px; + padding: 10px 20px !important; +} + +.pagination-container .el-pagination { + right: 0; + position: absolute; +} + +.el-table .fixed-width .el-button--mini { + color: #409EFF; + padding-left: 0; + padding-right: 0; + width: inherit; +} + +.el-tree-node__content > .el-checkbox { + margin-right: 8px; +} + +.list-group-striped > .list-group-item { + border-left: 0; + border-right: 0; + border-radius: 0; + padding-left: 0; + padding-right: 0; +} + +.list-group { + padding-left: 0px; + list-style: none; +} + +.list-group-item { + border-bottom: 1px solid #e7eaec; + border-top: 1px solid #e7eaec; + margin-bottom: -1px; + padding: 11px 0px; + font-size: 13px; +} + +.pull-right { + float: right !important; +} + +.el-card__header { + padding: 14px 15px 7px; + min-height: 40px; +} + +.el-card__body { + padding: 15px 20px 20px 20px; +} + +.card-box { + padding-right: 15px; + padding-left: 15px; + margin-bottom: 10px; +} + +/* text color */ +.text-navy { + color: #1ab394; +} + +.text-primary { + color: inherit; +} + +.text-success { + color: #1c84c6; +} + +.text-info { + color: #23c6c8; +} + +.text-warning { + color: #f8ac59; +} + +.text-danger { + color: #ed5565; +} + +.text-muted { + color: #888888; +} + +/* image */ +.img-circle { + border-radius: 50%; +} + +.img-lg { + width: 120px; + height: 120px; +} + +.avatar-upload-preview { + position: absolute; + top: 50%; + transform: translate(50%, -50%); + width: 180px; + height: 180px; + border-radius: 50%; + box-shadow: 0 0 4px #ccc; + overflow: hidden; +} \ No newline at end of file diff --git a/ruoyi-ui/src/assets/styles/sidebar.scss b/ruoyi-ui/src/assets/styles/sidebar.scss new file mode 100644 index 0000000..9fc13e2 --- /dev/null +++ b/ruoyi-ui/src/assets/styles/sidebar.scss @@ -0,0 +1,209 @@ +#app { + + .main-container { + min-height: 100%; + transition: margin-left .28s; + margin-left: $sideBarWidth; + position: relative; + } + + .sidebar-container { + transition: width 0.28s; + width: $sideBarWidth !important; + background-color: $menuBg; + height: 100%; + position: fixed; + font-size: 0px; + top: 0; + bottom: 0; + left: 0; + z-index: 1001; + overflow: hidden; + + // reset element-ui css + .horizontal-collapse-transition { + transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; + } + + .scrollbar-wrapper { + overflow-x: hidden !important; + } + + .el-scrollbar__bar.is-vertical { + right: 0px; + } + + .el-scrollbar { + height: 100%; + } + + &.has-logo { + .el-scrollbar { + height: calc(100% - 50px); + } + } + + .is-horizontal { + display: none; + } + + a { + display: inline-block; + width: 100%; + overflow: hidden; + } + + .svg-icon { + margin-right: 16px; + } + + .el-menu { + border: none; + height: 100%; + width: 100% !important; + } + + // menu hover + .submenu-title-noDropdown, + .el-submenu__title { + &:hover { + background-color: $menuHover !important; + } + } + + .is-active>.el-submenu__title { + color: $subMenuActiveText !important; + } + + & .nest-menu .el-submenu>.el-submenu__title, + & .el-submenu .el-menu-item { + min-width: $sideBarWidth !important; + background-color: $subMenuBg !important; + + &:hover { + background-color: $subMenuHover !important; + } + } + } + + .hideSidebar { + .sidebar-container { + width: 54px !important; + } + + .main-container { + margin-left: 54px; + } + + .submenu-title-noDropdown { + padding: 0 !important; + position: relative; + + .el-tooltip { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + } + } + + .el-submenu { + overflow: hidden; + + &>.el-submenu__title { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + + .el-submenu__icon-arrow { + display: none; + } + } + } + + .el-menu--collapse { + .el-submenu { + &>.el-submenu__title { + &>span { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + } + } + } + } + + .el-menu--collapse .el-menu .el-submenu { + min-width: $sideBarWidth !important; + } + + // mobile responsive + .mobile { + .main-container { + margin-left: 0px; + } + + .sidebar-container { + transition: transform .28s; + width: $sideBarWidth !important; + } + + &.hideSidebar { + .sidebar-container { + pointer-events: none; + transition-duration: 0.3s; + transform: translate3d(-$sideBarWidth, 0, 0); + } + } + } + + .withoutAnimation { + + .main-container, + .sidebar-container { + transition: none; + } + } +} + +// when menu collapsed +.el-menu--vertical { + &>.el-menu { + .svg-icon { + margin-right: 16px; + } + } + + .nest-menu .el-submenu>.el-submenu__title, + .el-menu-item { + &:hover { + // you can use $subMenuHover + background-color: $menuHover !important; + } + } + + // the scroll bar appears when the subMenu is too long + >.el-menu--popup { + max-height: 100vh; + overflow-y: auto; + + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } + } +} diff --git a/ruoyi-ui/src/assets/styles/transition.scss b/ruoyi-ui/src/assets/styles/transition.scss new file mode 100644 index 0000000..25e7e18 --- /dev/null +++ b/ruoyi-ui/src/assets/styles/transition.scss @@ -0,0 +1,48 @@ +// global transition css + +/* fade */ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.28s; +} + +.fade-enter, +.fade-leave-active { + opacity: 0; +} + +/* fade-transform */ +.fade-transform-leave-active, +.fade-transform-enter-active { + transition: all .5s; +} + +.fade-transform-enter { + opacity: 0; + transform: translateX(-30px); +} + +.fade-transform-leave-to { + opacity: 0; + transform: translateX(30px); +} + +/* breadcrumb transition */ +.breadcrumb-enter-active, +.breadcrumb-leave-active { + transition: all .5s; +} + +.breadcrumb-enter, +.breadcrumb-leave-active { + opacity: 0; + transform: translateX(20px); +} + +.breadcrumb-move { + transition: all .5s; +} + +.breadcrumb-leave-active { + position: absolute; +} diff --git a/ruoyi-ui/src/assets/styles/variables.scss b/ruoyi-ui/src/assets/styles/variables.scss new file mode 100644 index 0000000..d5f4e9f --- /dev/null +++ b/ruoyi-ui/src/assets/styles/variables.scss @@ -0,0 +1,35 @@ +// base color +$blue:#324157; +$light-blue:#3A71A8; +$red:#C03639; +$pink: #E65D6E; +$green: #30B08F; +$tiffany: #4AB7BD; +$yellow:#FEC171; +$panGreen: #30B08F; + +// sidebar +$menuText:#bfcbd9; +$menuActiveText:#409EFF; +$subMenuActiveText:#f4f4f5; // https://github.com/ElemeFE/element/issues/12951 + +$menuBg:#304156; +$menuHover:#263445; + +$subMenuBg:#1f2d3d; +$subMenuHover:#001528; + +$sideBarWidth: 200px; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + menuText: $menuText; + menuActiveText: $menuActiveText; + subMenuActiveText: $subMenuActiveText; + menuBg: $menuBg; + menuHover: $menuHover; + subMenuBg: $subMenuBg; + subMenuHover: $subMenuHover; + sideBarWidth: $sideBarWidth; +} diff --git a/ruoyi-ui/src/components/Breadcrumb/index.vue b/ruoyi-ui/src/components/Breadcrumb/index.vue new file mode 100644 index 0000000..dec8bfe --- /dev/null +++ b/ruoyi-ui/src/components/Breadcrumb/index.vue @@ -0,0 +1,81 @@ +<template> + <el-breadcrumb class="app-breadcrumb" separator="/"> + <transition-group name="breadcrumb"> + <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path"> + <span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span> + <a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a> + </el-breadcrumb-item> + </transition-group> + </el-breadcrumb> +</template> + +<script> +import pathToRegexp from 'path-to-regexp' + +export default { + data() { + return { + levelList: null + } + }, + watch: { + $route(route) { + // if you go to the redirect page, do not update the breadcrumbs + if (route.path.startsWith('/redirect/')) { + return + } + this.getBreadcrumb() + } + }, + created() { + this.getBreadcrumb() + }, + methods: { + getBreadcrumb() { + // only show routes with meta.title + let matched = this.$route.matched.filter(item => item.meta && item.meta.title) + const first = matched[0] + + if (!this.isDashboard(first)) { + matched = [{ path: '/index', meta: { title: '首页' }}].concat(matched) + } + + this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false) + }, + isDashboard(route) { + const name = route && route.name + if (!name) { + return false + } + return name.trim() === '首页' + }, + pathCompile(path) { + const { params } = this.$route + var toPath = pathToRegexp.compile(path) + return toPath(params) + }, + handleLink(item) { + const { redirect, path } = item + if (redirect) { + this.$router.push(redirect) + return + } + this.$router.push(this.pathCompile(path)) + } + } +} +</script> + +<style lang="scss" scoped> +.app-breadcrumb.el-breadcrumb { + display: inline-block; + font-size: 14px; + line-height: 50px; + margin-left: 8px; + + .no-redirect { + color: #97a8be; + cursor: text; + } +} +</style> diff --git a/ruoyi-ui/src/components/Editor/index.vue b/ruoyi-ui/src/components/Editor/index.vue new file mode 100644 index 0000000..2002908 --- /dev/null +++ b/ruoyi-ui/src/components/Editor/index.vue @@ -0,0 +1,226 @@ +<template> + <div> + <!-- 图片上传组件辅助 --> + <el-upload + class="avatar-uploader quill-img" + :action="uploadImgUrl" + name="file" + :headers="headers" + :show-file-list="false" + :on-success="quillImgSuccess" + :on-error="uploadError" + :before-upload="quillImgBefore" + accept='.jpg,.jpeg,.png,.gif' + ></el-upload> + + <!-- 富文本组件 --> + <quill-editor + class="editor" + v-model="content" + ref="quillEditor" + :options="editorOption" + @blur="onEditorBlur($event)" + @focus="onEditorFocus($event)" + @change="onEditorChange($event)" + ></quill-editor> + </div> +</template> + +<script> +import { getToken } from '@/utils/auth' + +// 工具栏配置 +const toolbarOptions = [ + ["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线 + ["blockquote", "code-block"], // 引用 代码块 + [{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表 + [{ indent: "-1" }, { indent: "+1" }], // 缩进 + [{ size: ["small", false, "large", "huge"] }], // 字体大小 + [{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题 + [{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色 + [{ align: [] }], // 对齐方式 + ["clean"], // 清除文本格式 + ["link", "image", "video"] // 链接、图片、视频 +]; + +import { quillEditor } from "vue-quill-editor"; +import "quill/dist/quill.core.css"; +import "quill/dist/quill.snow.css"; +import "quill/dist/quill.bubble.css"; + +export default { + props: { + /* 编辑器的内容 */ + value: { + type: String + }, + /* 图片大小 */ + maxSize: { + type: Number, + default: 4000 //kb + } + }, + components: { quillEditor }, + data() { + return { + content: this.value, + uploadImgUrl: "", + editorOption: { + placeholder: "", + theme: "snow", // or 'bubble' + placeholder: "请输入内容", + modules: { + toolbar: { + container: toolbarOptions, + handlers: { + image: function(value) { + if (value) { + // 触发input框选择图片文件 + document.querySelector(".quill-img input").click(); + } else { + this.quill.format("image", false); + } + } + } + } + } + }, + uploadImgUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上传的图片服务器地址 + headers: { + Authorization: 'Bearer ' + getToken() + } + }; + }, + watch: { + value: function() { + this.content = this.value; + } + }, + methods: { + onEditorBlur() { + //失去焦点事件 + }, + onEditorFocus() { + //获得焦点事件 + }, + onEditorChange() { + //内容改变事件 + this.$emit("input", this.content); + }, + + // 富文本图片上传前 + quillImgBefore(file) { + let fileType = file.type; + if(fileType === 'image/jpeg' || fileType === 'image/png'){ + return true; + }else { + this.$message.error('请插入图片类型文件(jpg/jpeg/png)'); + return false; + } + }, + + quillImgSuccess(res, file) { + // res为图片服务器返回的数据 + // 获取富文本组件实例 + let quill = this.$refs.quillEditor.quill; + // 如果上传成功 + if (res.code == 200) { + // 获取光标所在位置 + let length = quill.getSelection().index; + // 插入图片 res.url为服务器返回的图片地址 + quill.insertEmbed(length, "image", res.url); + // 调整光标到最后 + quill.setSelection(length + 1); + } else { + this.$message.error("图片插入失败"); + } + }, + // 富文本图片上传失败 + uploadError() { + // loading动画消失 + this.$message.error("图片插入失败"); + } + } +}; +</script> + +<style> +.editor { + line-height: normal !important; + height: 192px; +} +.el-upload { + display: none; +} +.ql-snow .ql-tooltip[data-mode="link"]::before { + content: "请输入链接地址:"; +} +.ql-snow .ql-tooltip.ql-editing a.ql-action::after { + border-right: 0px; + content: "保存"; + padding-right: 0px; +} + +.ql-snow .ql-tooltip[data-mode="video"]::before { + content: "请输入视频地址:"; +} + +.ql-snow .ql-picker.ql-size .ql-picker-label::before, +.ql-snow .ql-picker.ql-size .ql-picker-item::before { + content: "14px"; +} +.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before, +.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before { + content: "10px"; +} +.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before, +.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before { + content: "18px"; +} +.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before, +.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before { + content: "32px"; +} + +.ql-snow .ql-picker.ql-header .ql-picker-label::before, +.ql-snow .ql-picker.ql-header .ql-picker-item::before { + content: "文本"; +} +.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before, +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before { + content: "标题1"; +} +.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before, +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before { + content: "标题2"; +} +.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before, +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before { + content: "标题3"; +} +.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before, +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before { + content: "标题4"; +} +.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before, +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before { + content: "标题5"; +} +.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before, +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before { + content: "标题6"; +} + +.ql-snow .ql-picker.ql-font .ql-picker-label::before, +.ql-snow .ql-picker.ql-font .ql-picker-item::before { + content: "标准字体"; +} +.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before, +.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before { + content: "衬线字体"; +} +.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before, +.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before { + content: "等宽字体"; +} +</style> \ No newline at end of file diff --git a/ruoyi-ui/src/components/Hamburger/index.vue b/ruoyi-ui/src/components/Hamburger/index.vue new file mode 100644 index 0000000..c6a4359 --- /dev/null +++ b/ruoyi-ui/src/components/Hamburger/index.vue @@ -0,0 +1,44 @@ +<template> + <div style="padding: 0 15px;" @click="toggleClick"> + <svg + :class="{'is-active':isActive}" + class="hamburger" + viewBox="0 0 1024 1024" + xmlns="http://www.w3.org/2000/svg" + width="64" + height="64" + > + <path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" /> + </svg> + </div> +</template> + +<script> +export default { + name: 'Hamburger', + props: { + isActive: { + type: Boolean, + default: false + } + }, + methods: { + toggleClick() { + this.$emit('toggleClick') + } + } +} +</script> + +<style scoped> +.hamburger { + display: inline-block; + vertical-align: middle; + width: 20px; + height: 20px; +} + +.hamburger.is-active { + transform: rotate(180deg); +} +</style> diff --git a/ruoyi-ui/src/components/HeaderSearch/index.vue b/ruoyi-ui/src/components/HeaderSearch/index.vue new file mode 100644 index 0000000..8974171 --- /dev/null +++ b/ruoyi-ui/src/components/HeaderSearch/index.vue @@ -0,0 +1,180 @@ +<template> + <div :class="{'show':show}" class="header-search"> + <svg-icon class-name="search-icon" icon-class="search" @click.stop="click" /> + <el-select + ref="headerSearchSelect" + v-model="search" + :remote-method="querySearch" + filterable + default-first-option + remote + placeholder="Search" + class="header-search-select" + @change="change" + > + <el-option v-for="item in options" :key="item.path" :value="item" :label="item.title.join(' > ')" /> + </el-select> + </div> +</template> + +<script> +// fuse is a lightweight fuzzy-search module +// make search results more in line with expectations +import Fuse from 'fuse.js' +import path from 'path' + +export default { + name: 'HeaderSearch', + data() { + return { + search: '', + options: [], + searchPool: [], + show: false, + fuse: undefined + } + }, + computed: { + routes() { + return this.$store.getters.permission_routes + } + }, + watch: { + routes() { + this.searchPool = this.generateRoutes(this.routes) + }, + searchPool(list) { + this.initFuse(list) + }, + show(value) { + if (value) { + document.body.addEventListener('click', this.close) + } else { + document.body.removeEventListener('click', this.close) + } + } + }, + mounted() { + this.searchPool = this.generateRoutes(this.routes) + }, + methods: { + click() { + this.show = !this.show + if (this.show) { + this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus() + } + }, + close() { + this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur() + this.options = [] + this.show = false + }, + change(val) { + this.$router.push(val.path) + this.search = '' + this.options = [] + this.$nextTick(() => { + this.show = false + }) + }, + initFuse(list) { + this.fuse = new Fuse(list, { + shouldSort: true, + threshold: 0.4, + location: 0, + distance: 100, + maxPatternLength: 32, + minMatchCharLength: 1, + keys: [{ + name: 'title', + weight: 0.7 + }, { + name: 'path', + weight: 0.3 + }] + }) + }, + // Filter out the routes that can be displayed in the sidebar + // And generate the internationalized title + generateRoutes(routes, basePath = '/', prefixTitle = []) { + let res = [] + + for (const router of routes) { + // skip hidden router + if (router.hidden) { continue } + + const data = { + path: path.resolve(basePath, router.path), + title: [...prefixTitle] + } + + if (router.meta && router.meta.title) { + data.title = [...data.title, router.meta.title] + + if (router.redirect !== 'noRedirect') { + // only push the routes with title + // special case: need to exclude parent router without redirect + res.push(data) + } + } + + // recursive child routes + if (router.children) { + const tempRoutes = this.generateRoutes(router.children, data.path, data.title) + if (tempRoutes.length >= 1) { + res = [...res, ...tempRoutes] + } + } + } + return res + }, + querySearch(query) { + if (query !== '') { + this.options = this.fuse.search(query) + } else { + this.options = [] + } + } + } +} +</script> + +<style lang="scss" scoped> +.header-search { + font-size: 0 !important; + + .search-icon { + cursor: pointer; + font-size: 18px; + vertical-align: middle; + } + + .header-search-select { + font-size: 18px; + transition: width 0.2s; + width: 0; + overflow: hidden; + background: transparent; + border-radius: 0; + display: inline-block; + vertical-align: middle; + + /deep/ .el-input__inner { + border-radius: 0; + border: 0; + padding-left: 0; + padding-right: 0; + box-shadow: none !important; + border-bottom: 1px solid #d9d9d9; + vertical-align: middle; + } + } + + &.show { + .header-search-select { + width: 210px; + margin-left: 10px; + } + } +} +</style> diff --git a/ruoyi-ui/src/components/IconSelect/index.vue b/ruoyi-ui/src/components/IconSelect/index.vue new file mode 100644 index 0000000..ea4c1b6 --- /dev/null +++ b/ruoyi-ui/src/components/IconSelect/index.vue @@ -0,0 +1,69 @@ +<!-- @author zhengjie --> +<template> + <div class="icon-body"> + <el-input v-model="name" style="position: relative;" clearable placeholder="请输入图标名称" @clear="filterIcons" @input.native="filterIcons"> + <i slot="suffix" class="el-icon-search el-input__icon" /> + </el-input> + <div class="icon-list"> + <div v-for="(item, index) in iconList" :key="index" @click="selectedIcon(item)"> + <svg-icon :icon-class="item" style="height: 30px;width: 16px;" /> + <span>{{ item }}</span> + </div> + </div> + </div> +</template> + +<script> +import icons from './requireIcons' +export default { + name: 'IconSelect', + data() { + return { + name: '', + iconList: icons + } + }, + methods: { + filterIcons() { + if (this.name) { + this.iconList = this.iconList.filter(item => item.includes(this.name)) + } else { + this.iconList = icons + } + }, + selectedIcon(name) { + this.$emit('selected', name) + document.body.click() + }, + reset() { + this.name = '' + this.iconList = icons + } + } +} +</script> + +<style rel="stylesheet/scss" lang="scss" scoped> + .icon-body { + width: 100%; + padding: 10px; + .icon-list { + height: 200px; + overflow-y: scroll; + div { + height: 30px; + line-height: 30px; + margin-bottom: -5px; + cursor: pointer; + width: 33%; + float: left; + } + span { + display: inline-block; + vertical-align: -0.15em; + fill: currentColor; + overflow: hidden; + } + } + } +</style> diff --git a/ruoyi-ui/src/components/IconSelect/requireIcons.js b/ruoyi-ui/src/components/IconSelect/requireIcons.js new file mode 100644 index 0000000..5d3005d --- /dev/null +++ b/ruoyi-ui/src/components/IconSelect/requireIcons.js @@ -0,0 +1,11 @@ + +const req = require.context('../../assets/icons/svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys() + +const re = /\.\/(.*)\.svg/ + +const icons = requireAll(req).map(i => { + return i.match(re)[1] +}) + +export default icons diff --git a/ruoyi-ui/src/components/Pagination/index.vue b/ruoyi-ui/src/components/Pagination/index.vue new file mode 100644 index 0000000..08ff090 --- /dev/null +++ b/ruoyi-ui/src/components/Pagination/index.vue @@ -0,0 +1,101 @@ +<template> + <div :class="{'hidden':hidden}" class="pagination-container"> + <el-pagination + :background="background" + :current-page.sync="currentPage" + :page-size.sync="pageSize" + :layout="layout" + :page-sizes="pageSizes" + :total="total" + v-bind="$attrs" + @size-change="handleSizeChange" + @current-change="handleCurrentChange" + /> + </div> +</template> + +<script> +import { scrollTo } from '@/utils/scroll-to' + +export default { + name: 'Pagination', + props: { + total: { + required: true, + type: Number + }, + page: { + type: Number, + default: 1 + }, + limit: { + type: Number, + default: 20 + }, + pageSizes: { + type: Array, + default() { + return [10, 20, 30, 50] + } + }, + layout: { + type: String, + default: 'total, sizes, prev, pager, next, jumper' + }, + background: { + type: Boolean, + default: true + }, + autoScroll: { + type: Boolean, + default: true + }, + hidden: { + type: Boolean, + default: false + } + }, + computed: { + currentPage: { + get() { + return this.page + }, + set(val) { + this.$emit('update:page', val) + } + }, + pageSize: { + get() { + return this.limit + }, + set(val) { + this.$emit('update:limit', val) + } + } + }, + methods: { + handleSizeChange(val) { + this.$emit('pagination', { page: this.currentPage, limit: val }) + if (this.autoScroll) { + scrollTo(0, 800) + } + }, + handleCurrentChange(val) { + this.$emit('pagination', { page: val, limit: this.pageSize }) + if (this.autoScroll) { + scrollTo(0, 800) + } + } + } +} +</script> + +<style scoped> +.pagination-container { + background: #fff; + padding: 32px 16px; +} +.pagination-container.hidden { + display: none; +} +</style> diff --git a/ruoyi-ui/src/components/PanThumb/index.vue b/ruoyi-ui/src/components/PanThumb/index.vue new file mode 100644 index 0000000..796b01b --- /dev/null +++ b/ruoyi-ui/src/components/PanThumb/index.vue @@ -0,0 +1,142 @@ +<template> + <div :style="{zIndex:zIndex,height:height,width:width}" class="pan-item"> + <div class="pan-info"> + <div class="pan-info-roles-container"> + <slot /> + </div> + </div> + <!-- eslint-disable-next-line --> + <div :style="{backgroundImage: `url(${image})`}" class="pan-thumb"></div> + </div> +</template> + +<script> +export default { + name: 'PanThumb', + props: { + image: { + type: String, + required: true + }, + zIndex: { + type: Number, + default: 1 + }, + width: { + type: String, + default: '150px' + }, + height: { + type: String, + default: '150px' + } + } +} +</script> + +<style scoped> +.pan-item { + width: 200px; + height: 200px; + border-radius: 50%; + display: inline-block; + position: relative; + cursor: default; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); +} + +.pan-info-roles-container { + padding: 20px; + text-align: center; +} + +.pan-thumb { + width: 100%; + height: 100%; + background-position: center center; + background-size: cover; + border-radius: 50%; + overflow: hidden; + position: absolute; + transform-origin: 95% 40%; + transition: all 0.3s ease-in-out; +} + +/* .pan-thumb:after { + content: ''; + width: 8px; + height: 8px; + position: absolute; + border-radius: 50%; + top: 40%; + left: 95%; + margin: -4px 0 0 -4px; + background: radial-gradient(ellipse at center, rgba(14, 14, 14, 1) 0%, rgba(125, 126, 125, 1) 100%); + box-shadow: 0 0 1px rgba(255, 255, 255, 0.9); +} */ + +.pan-info { + position: absolute; + width: inherit; + height: inherit; + border-radius: 50%; + overflow: hidden; + box-shadow: inset 0 0 0 5px rgba(0, 0, 0, 0.05); +} + +.pan-info h3 { + color: #fff; + text-transform: uppercase; + position: relative; + letter-spacing: 2px; + font-size: 18px; + margin: 0 60px; + padding: 22px 0 0 0; + height: 85px; + font-family: 'Open Sans', Arial, sans-serif; + text-shadow: 0 0 1px #fff, 0 1px 2px rgba(0, 0, 0, 0.3); +} + +.pan-info p { + color: #fff; + padding: 10px 5px; + font-style: italic; + margin: 0 30px; + font-size: 12px; + border-top: 1px solid rgba(255, 255, 255, 0.5); +} + +.pan-info p a { + display: block; + color: #333; + width: 80px; + height: 80px; + background: rgba(255, 255, 255, 0.3); + border-radius: 50%; + color: #fff; + font-style: normal; + font-weight: 700; + text-transform: uppercase; + font-size: 9px; + letter-spacing: 1px; + padding-top: 24px; + margin: 7px auto 0; + font-family: 'Open Sans', Arial, sans-serif; + opacity: 0; + transition: transform 0.3s ease-in-out 0.2s, opacity 0.3s ease-in-out 0.2s, background 0.2s linear 0s; + transform: translateX(60px) rotate(90deg); +} + +.pan-info p a:hover { + background: rgba(255, 255, 255, 0.5); +} + +.pan-item:hover .pan-thumb { + transform: rotate(-110deg); +} + +.pan-item:hover .pan-info p a { + opacity: 1; + transform: translateX(0px) rotate(0deg); +} +</style> diff --git a/ruoyi-ui/src/components/RightPanel/index.vue b/ruoyi-ui/src/components/RightPanel/index.vue new file mode 100644 index 0000000..1534b9b --- /dev/null +++ b/ruoyi-ui/src/components/RightPanel/index.vue @@ -0,0 +1,149 @@ +<template> + <div ref="rightPanel" :class="{show:show}" class="rightPanel-container"> + <div class="rightPanel-background" /> + <div class="rightPanel"> + <div class="rightPanel-items"> + <slot /> + </div> + </div> + </div> +</template> + +<script> +import { addClass, removeClass } from '@/utils' + +export default { + name: 'RightPanel', + props: { + clickNotClose: { + default: false, + type: Boolean + }, + buttonTop: { + default: 250, + type: Number + } + }, + computed: { + show: { + get() { + return this.$store.state.settings.showSettings + }, + set(val) { + this.$store.dispatch('settings/changeSetting', { + key: 'showSettings', + value: val + }) + } + }, + theme() { + return this.$store.state.settings.theme + }, + }, + watch: { + show(value) { + if (value && !this.clickNotClose) { + this.addEventClick() + } + if (value) { + addClass(document.body, 'showRightPanel') + } else { + removeClass(document.body, 'showRightPanel') + } + } + }, + mounted() { + this.insertToBody() + this.addEventClick() + }, + beforeDestroy() { + const elx = this.$refs.rightPanel + elx.remove() + }, + methods: { + addEventClick() { + window.addEventListener('click', this.closeSidebar) + }, + closeSidebar(evt) { + const parent = evt.target.closest('.rightPanel') + if (!parent) { + this.show = false + window.removeEventListener('click', this.closeSidebar) + } + }, + insertToBody() { + const elx = this.$refs.rightPanel + const body = document.querySelector('body') + body.insertBefore(elx, body.firstChild) + } + } +} +</script> + +<style> +.showRightPanel { + overflow: hidden; + position: relative; + width: calc(100% - 15px); +} +</style> + +<style lang="scss" scoped> +.rightPanel-background { + position: fixed; + top: 0; + left: 0; + opacity: 0; + transition: opacity .3s cubic-bezier(.7, .3, .1, 1); + background: rgba(0, 0, 0, .2); + z-index: -1; +} + +.rightPanel { + width: 100%; + max-width: 260px; + height: 100vh; + position: fixed; + top: 0; + right: 0; + box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, .05); + transition: all .25s cubic-bezier(.7, .3, .1, 1); + transform: translate(100%); + background: #fff; + z-index: 40000; +} + +.show { + transition: all .3s cubic-bezier(.7, .3, .1, 1); + + .rightPanel-background { + z-index: 20000; + opacity: 1; + width: 100%; + height: 100%; + } + + .rightPanel { + transform: translate(0); + } +} + +.handle-button { + width: 48px; + height: 48px; + position: absolute; + left: -48px; + text-align: center; + font-size: 24px; + border-radius: 6px 0 0 6px !important; + z-index: 0; + pointer-events: auto; + cursor: pointer; + color: #fff; + line-height: 48px; + i { + font-size: 24px; + line-height: 48px; + } +} +</style> diff --git a/ruoyi-ui/src/components/RuoYi/index.vue b/ruoyi-ui/src/components/RuoYi/index.vue new file mode 100644 index 0000000..530d57f --- /dev/null +++ b/ruoyi-ui/src/components/RuoYi/index.vue @@ -0,0 +1,21 @@ +<template> + <div> + <svg-icon icon-class="download" @click="handleDownload"/> + </div> +</template> + +<script> +export default { + name: 'RuoYi', + data() { + return { + url: 'https://gitee.com/y_project/RuoYi-Vue' + } + }, + methods: { + handleDownload() { + window.open(this.url) + } + } +} +</script> \ No newline at end of file diff --git a/ruoyi-ui/src/components/Screenfull/index.vue b/ruoyi-ui/src/components/Screenfull/index.vue new file mode 100644 index 0000000..099d74d --- /dev/null +++ b/ruoyi-ui/src/components/Screenfull/index.vue @@ -0,0 +1,60 @@ +<template> + <div> + <svg-icon :icon-class="isFullscreen?'exit-fullscreen':'fullscreen'" @click="click" /> + </div> +</template> + +<script> +import screenfull from 'screenfull' + +export default { + name: 'Screenfull', + data() { + return { + isFullscreen: false + } + }, + mounted() { + this.init() + }, + beforeDestroy() { + this.destroy() + }, + methods: { + click() { + if (!screenfull.enabled) { + this.$message({ + message: 'you browser can not work', + type: 'warning' + }) + return false + } + screenfull.toggle() + }, + change() { + this.isFullscreen = screenfull.isFullscreen + }, + init() { + if (screenfull.enabled) { + screenfull.on('change', this.change) + } + }, + destroy() { + if (screenfull.enabled) { + screenfull.off('change', this.change) + } + } + } +} +</script> + +<style scoped> +.screenfull-svg { + display: inline-block; + cursor: pointer; + fill: #5a5e66;; + width: 20px; + height: 20px; + vertical-align: 10px; +} +</style> diff --git a/ruoyi-ui/src/components/SizeSelect/index.vue b/ruoyi-ui/src/components/SizeSelect/index.vue new file mode 100644 index 0000000..5503b97 --- /dev/null +++ b/ruoyi-ui/src/components/SizeSelect/index.vue @@ -0,0 +1,57 @@ +<template> + <el-dropdown trigger="click" @command="handleSetSize"> + <div> + <svg-icon class-name="size-icon" icon-class="size" /> + </div> + <el-dropdown-menu slot="dropdown"> + <el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size===item.value" :command="item.value"> + {{ + item.label }} + </el-dropdown-item> + </el-dropdown-menu> + </el-dropdown> +</template> + +<script> +export default { + data() { + return { + sizeOptions: [ + { label: 'Default', value: 'default' }, + { label: 'Medium', value: 'medium' }, + { label: 'Small', value: 'small' }, + { label: 'Mini', value: 'mini' } + ] + } + }, + computed: { + size() { + return this.$store.getters.size + } + }, + methods: { + handleSetSize(size) { + this.$ELEMENT.size = size + this.$store.dispatch('app/setSize', size) + this.refreshView() + this.$message({ + message: 'Switch Size Success', + type: 'success' + }) + }, + refreshView() { + // In order to make the cached page re-rendered + this.$store.dispatch('tagsView/delAllCachedViews', this.$route) + + const { fullPath } = this.$route + + this.$nextTick(() => { + this.$router.replace({ + path: '/redirect' + fullPath + }) + }) + } + } + +} +</script> diff --git a/ruoyi-ui/src/components/SvgIcon/index.vue b/ruoyi-ui/src/components/SvgIcon/index.vue new file mode 100644 index 0000000..340ace4 --- /dev/null +++ b/ruoyi-ui/src/components/SvgIcon/index.vue @@ -0,0 +1,61 @@ +<template> + <div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" /> + <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners"> + <use :xlink:href="iconName" /> + </svg> +</template> + +<script> +import { isExternal } from '@/utils/validate' + +export default { + name: 'SvgIcon', + props: { + iconClass: { + type: String, + required: true + }, + className: { + type: String, + default: '' + } + }, + computed: { + isExternal() { + return isExternal(this.iconClass) + }, + iconName() { + return `#icon-${this.iconClass}` + }, + svgClass() { + if (this.className) { + return 'svg-icon ' + this.className + } else { + return 'svg-icon' + } + }, + styleExternalIcon() { + return { + mask: `url(${this.iconClass}) no-repeat 50% 50%`, + '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%` + } + } + } +} +</script> + +<style scoped> +.svg-icon { + width: 1em; + height: 1em; + vertical-align: -0.15em; + fill: currentColor; + overflow: hidden; +} + +.svg-external-icon { + background-color: currentColor; + mask-size: cover!important; + display: inline-block; +} +</style> diff --git a/ruoyi-ui/src/components/ThemePicker/index.vue b/ruoyi-ui/src/components/ThemePicker/index.vue new file mode 100644 index 0000000..9a77cd3 --- /dev/null +++ b/ruoyi-ui/src/components/ThemePicker/index.vue @@ -0,0 +1,175 @@ +<template> + <el-color-picker + v-model="theme" + :predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]" + class="theme-picker" + popper-class="theme-picker-dropdown" + /> +</template> + +<script> +const version = require('element-ui/package.json').version // element-ui version from node_modules +const ORIGINAL_THEME = '#409EFF' // default color + +export default { + data() { + return { + chalk: '', // content of theme-chalk css + theme: '' + } + }, + computed: { + defaultTheme() { + return this.$store.state.settings.theme + } + }, + watch: { + defaultTheme: { + handler: function(val, oldVal) { + this.theme = val + }, + immediate: true + }, + async theme(val) { + const oldVal = this.chalk ? this.theme : ORIGINAL_THEME + if (typeof val !== 'string') return + const themeCluster = this.getThemeCluster(val.replace('#', '')) + const originalCluster = this.getThemeCluster(oldVal.replace('#', '')) + console.log(themeCluster, originalCluster) + + const $message = this.$message({ + message: ' Compiling the theme', + customClass: 'theme-message', + type: 'success', + duration: 0, + iconClass: 'el-icon-loading' + }) + + const getHandler = (variable, id) => { + return () => { + const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', '')) + const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster) + + let styleTag = document.getElementById(id) + if (!styleTag) { + styleTag = document.createElement('style') + styleTag.setAttribute('id', id) + document.head.appendChild(styleTag) + } + styleTag.innerText = newStyle + } + } + + if (!this.chalk) { + const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css` + await this.getCSSString(url, 'chalk') + } + + const chalkHandler = getHandler('chalk', 'chalk-style') + + chalkHandler() + + const styles = [].slice.call(document.querySelectorAll('style')) + .filter(style => { + const text = style.innerText + return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text) + }) + styles.forEach(style => { + const { innerText } = style + if (typeof innerText !== 'string') return + style.innerText = this.updateStyle(innerText, originalCluster, themeCluster) + }) + + this.$emit('change', val) + + $message.close() + } + }, + + methods: { + updateStyle(style, oldCluster, newCluster) { + let newStyle = style + oldCluster.forEach((color, index) => { + newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index]) + }) + return newStyle + }, + + getCSSString(url, variable) { + return new Promise(resolve => { + const xhr = new XMLHttpRequest() + xhr.onreadystatechange = () => { + if (xhr.readyState === 4 && xhr.status === 200) { + this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '') + resolve() + } + } + xhr.open('GET', url) + xhr.send() + }) + }, + + getThemeCluster(theme) { + const tintColor = (color, tint) => { + let red = parseInt(color.slice(0, 2), 16) + let green = parseInt(color.slice(2, 4), 16) + let blue = parseInt(color.slice(4, 6), 16) + + if (tint === 0) { // when primary color is in its rgb space + return [red, green, blue].join(',') + } else { + red += Math.round(tint * (255 - red)) + green += Math.round(tint * (255 - green)) + blue += Math.round(tint * (255 - blue)) + + red = red.toString(16) + green = green.toString(16) + blue = blue.toString(16) + + return `#${red}${green}${blue}` + } + } + + const shadeColor = (color, shade) => { + let red = parseInt(color.slice(0, 2), 16) + let green = parseInt(color.slice(2, 4), 16) + let blue = parseInt(color.slice(4, 6), 16) + + red = Math.round((1 - shade) * red) + green = Math.round((1 - shade) * green) + blue = Math.round((1 - shade) * blue) + + red = red.toString(16) + green = green.toString(16) + blue = blue.toString(16) + + return `#${red}${green}${blue}` + } + + const clusters = [theme] + for (let i = 0; i <= 9; i++) { + clusters.push(tintColor(theme, Number((i / 10).toFixed(2)))) + } + clusters.push(shadeColor(theme, 0.1)) + return clusters + } + } +} +</script> + +<style> +.theme-message, +.theme-picker-dropdown { + z-index: 99999 !important; +} + +.theme-picker .el-color-picker__trigger { + height: 26px !important; + width: 26px !important; + padding: 2px; +} + +.theme-picker-dropdown .el-color-dropdown__link-btn { + display: none; +} +</style> diff --git a/ruoyi-ui/src/directive/permission/hasPermi.js b/ruoyi-ui/src/directive/permission/hasPermi.js new file mode 100644 index 0000000..74f9d32 --- /dev/null +++ b/ruoyi-ui/src/directive/permission/hasPermi.js @@ -0,0 +1,28 @@ + /** + * 操作权限处理 + * Copyright (c) 2019 ruoyi + */ + +import store from '@/store' + +export default { + inserted(el, binding, vnode) { + const { value } = binding + const all_permission = "*:*:*"; + const permissions = store.getters && store.getters.permissions + + if (value && value instanceof Array && value.length > 0) { + const permissionFlag = value + + const hasPermissions = permissions.some(permission => { + return all_permission === permission || permissionFlag.includes(permission) + }) + + if (!hasPermissions) { + el.parentNode && el.parentNode.removeChild(el) + } + } else { + throw new Error(`请设置操作权限标签值`) + } + } +} diff --git a/ruoyi-ui/src/directive/permission/hasRole.js b/ruoyi-ui/src/directive/permission/hasRole.js new file mode 100644 index 0000000..ea966a3 --- /dev/null +++ b/ruoyi-ui/src/directive/permission/hasRole.js @@ -0,0 +1,28 @@ + /** + * 角色权限处理 + * Copyright (c) 2019 ruoyi + */ + +import store from '@/store' + +export default { + inserted(el, binding, vnode) { + const { value } = binding + const super_admin = "admin"; + const roles = store.getters && store.getters.roles + + if (value && value instanceof Array && value.length > 0) { + const roleFlag = value + + const hasRole = roles.some(role => { + return super_admin === role || roleFlag.includes(role) + }) + + if (!hasRole) { + el.parentNode && el.parentNode.removeChild(el) + } + } else { + throw new Error(`请设置角色权限标签值"`) + } + } +} diff --git a/ruoyi-ui/src/directive/permission/index.js b/ruoyi-ui/src/directive/permission/index.js new file mode 100644 index 0000000..d6f530d --- /dev/null +++ b/ruoyi-ui/src/directive/permission/index.js @@ -0,0 +1,15 @@ +import hasRole from './hasRole' +import hasPermi from './hasPermi' + +const install = function(Vue) { + Vue.directive('hasRole', hasRole) + Vue.directive('hasPermi', hasPermi) +} + +if (window.Vue) { + window['hasRole'] = hasRole + window['hasPermi'] = hasPermi + Vue.use(install); // eslint-disable-line +} + +export default install diff --git a/ruoyi-ui/src/layout/components/AppMain.vue b/ruoyi-ui/src/layout/components/AppMain.vue new file mode 100644 index 0000000..423437b --- /dev/null +++ b/ruoyi-ui/src/layout/components/AppMain.vue @@ -0,0 +1,57 @@ +<template> + <section class="app-main"> + <transition name="fade-transform" mode="out-in"> + <keep-alive :include="cachedViews"> + <router-view :key="key" /> + </keep-alive> + </transition> + </section> +</template> + +<script> +export default { + name: 'AppMain', + computed: { + cachedViews() { + return this.$store.state.tagsView.cachedViews + }, + key() { + return this.$route.path + } + } +} +</script> + +<style lang="scss" scoped> +.app-main { + /* 50= navbar 50 */ + min-height: calc(100vh - 50px); + width: 100%; + position: relative; + overflow: hidden; +} + +.fixed-header+.app-main { + padding-top: 50px; +} + +.hasTagsView { + .app-main { + /* 84 = navbar + tags-view = 50 + 34 */ + min-height: calc(100vh - 84px); + } + + .fixed-header+.app-main { + padding-top: 84px; + } +} +</style> + +<style lang="scss"> +// fix css style bug in open el-dialog +.el-popup-parent--hidden { + .fixed-header { + padding-right: 15px; + } +} +</style> diff --git a/ruoyi-ui/src/layout/components/Navbar.vue b/ruoyi-ui/src/layout/components/Navbar.vue new file mode 100644 index 0000000..bafb11a --- /dev/null +++ b/ruoyi-ui/src/layout/components/Navbar.vue @@ -0,0 +1,181 @@ +<template> + <div class="navbar"> + <hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" /> + + <breadcrumb id="breadcrumb-container" class="breadcrumb-container" /> + + <div class="right-menu"> + <template v-if="device!=='mobile'"> + <search id="header-search" class="right-menu-item" /> + + <el-tooltip content="下载源码" effect="dark" placement="bottom"> + <ruo-yi id="ruoyi" class="right-menu-item hover-effect" /> + </el-tooltip> + + <screenfull id="screenfull" class="right-menu-item hover-effect" /> + + <el-tooltip content="布局大小" effect="dark" placement="bottom"> + <size-select id="size-select" class="right-menu-item hover-effect" /> + </el-tooltip> + + </template> + + <el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click"> + <div class="avatar-wrapper"> + <img :src="avatar" class="user-avatar"> + <i class="el-icon-caret-bottom" /> + </div> + <el-dropdown-menu slot="dropdown"> + <router-link to="/user/profile"> + <el-dropdown-item>个人中心</el-dropdown-item> + </router-link> + <el-dropdown-item> + <span @click="setting = true">布局设置</span> + </el-dropdown-item> + <el-dropdown-item divided> + <span @click="logout">退出登录</span> + </el-dropdown-item> + </el-dropdown-menu> + </el-dropdown> + </div> + </div> +</template> + +<script> +import { mapGetters } from 'vuex' +import Breadcrumb from '@/components/Breadcrumb' +import Hamburger from '@/components/Hamburger' +import Screenfull from '@/components/Screenfull' +import SizeSelect from '@/components/SizeSelect' +import Search from '@/components/HeaderSearch' +import RuoYi from '@/components/RuoYi' + +export default { + components: { + Breadcrumb, + Hamburger, + Screenfull, + SizeSelect, + Search, + RuoYi + }, + computed: { + ...mapGetters([ + 'sidebar', + 'avatar', + 'device' + ]), + setting: { + get() { + return this.$store.state.settings.showSettings + }, + set(val) { + this.$store.dispatch('settings/changeSetting', { + key: 'showSettings', + value: val + }) + } + } + }, + methods: { + toggleSideBar() { + this.$store.dispatch('app/toggleSideBar') + }, + async logout() { + this.$confirm('确定注销并退出系统吗?', '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }).then(() => { + this.$store.dispatch('LogOut').then(() => { + location.reload() + }) + }) + } + } +} +</script> + +<style lang="scss" scoped> +.navbar { + height: 50px; + overflow: hidden; + position: relative; + background: #fff; + box-shadow: 0 1px 4px rgba(0,21,41,.08); + + .hamburger-container { + line-height: 46px; + height: 100%; + float: left; + cursor: pointer; + transition: background .3s; + -webkit-tap-highlight-color:transparent; + + &:hover { + background: rgba(0, 0, 0, .025) + } + } + + .breadcrumb-container { + float: left; + } + + .errLog-container { + display: inline-block; + vertical-align: top; + } + + .right-menu { + float: right; + height: 100%; + line-height: 50px; + + &:focus { + outline: none; + } + + .right-menu-item { + display: inline-block; + padding: 0 8px; + height: 100%; + font-size: 18px; + color: #5a5e66; + vertical-align: text-bottom; + + &.hover-effect { + cursor: pointer; + transition: background .3s; + + &:hover { + background: rgba(0, 0, 0, .025) + } + } + } + + .avatar-container { + margin-right: 30px; + + .avatar-wrapper { + margin-top: 5px; + position: relative; + + .user-avatar { + cursor: pointer; + width: 40px; + height: 40px; + border-radius: 10px; + } + + .el-icon-caret-bottom { + cursor: pointer; + position: absolute; + right: -20px; + top: 25px; + font-size: 12px; + } + } + } + } +} +</style> diff --git a/ruoyi-ui/src/layout/components/Settings/index.vue b/ruoyi-ui/src/layout/components/Settings/index.vue new file mode 100644 index 0000000..3a9ac4a --- /dev/null +++ b/ruoyi-ui/src/layout/components/Settings/index.vue @@ -0,0 +1,108 @@ +<template> + <div class="drawer-container"> + <div> + <h3 class="drawer-title">系统布局配置</h3> + + <div class="drawer-item"> + <span>主题颜色</span> + <theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" /> + </div> + + <div class="drawer-item"> + <span>开启 Tags-Views</span> + <el-switch v-model="tagsView" class="drawer-switch" /> + </div> + + <div class="drawer-item"> + <span>固定 Header</span> + <el-switch v-model="fixedHeader" class="drawer-switch" /> + </div> + + <div class="drawer-item"> + <span>显示 Logo</span> + <el-switch v-model="sidebarLogo" class="drawer-switch" /> + </div> + + </div> + </div> +</template> + +<script> +import ThemePicker from '@/components/ThemePicker' + +export default { + components: { ThemePicker }, + data() { + return {} + }, + computed: { + fixedHeader: { + get() { + return this.$store.state.settings.fixedHeader + }, + set(val) { + this.$store.dispatch('settings/changeSetting', { + key: 'fixedHeader', + value: val + }) + } + }, + tagsView: { + get() { + return this.$store.state.settings.tagsView + }, + set(val) { + this.$store.dispatch('settings/changeSetting', { + key: 'tagsView', + value: val + }) + } + }, + sidebarLogo: { + get() { + return this.$store.state.settings.sidebarLogo + }, + set(val) { + this.$store.dispatch('settings/changeSetting', { + key: 'sidebarLogo', + value: val + }) + } + }, + }, + methods: { + themeChange(val) { + this.$store.dispatch('settings/changeSetting', { + key: 'theme', + value: val + }) + } + } +} +</script> + +<style lang="scss" scoped> +.drawer-container { + padding: 24px; + font-size: 14px; + line-height: 1.5; + word-wrap: break-word; + + .drawer-title { + margin-bottom: 12px; + color: rgba(0, 0, 0, .85); + font-size: 14px; + line-height: 22px; + } + + .drawer-item { + color: rgba(0, 0, 0, .65); + font-size: 14px; + padding: 12px 0; + } + + .drawer-switch { + float: right + } +} +</style> diff --git a/ruoyi-ui/src/layout/components/Sidebar/FixiOSBug.js b/ruoyi-ui/src/layout/components/Sidebar/FixiOSBug.js new file mode 100644 index 0000000..3d8cc21 --- /dev/null +++ b/ruoyi-ui/src/layout/components/Sidebar/FixiOSBug.js @@ -0,0 +1,25 @@ +export default { + computed: { + device() { + return this.$store.state.app.device + } + }, + mounted() { + // In order to fix the click on menu on the ios device will trigger the mouseleave bug + this.fixBugIniOS() + }, + methods: { + fixBugIniOS() { + const $subMenu = this.$refs.subMenu + if ($subMenu) { + const handleMouseleave = $subMenu.handleMouseleave + $subMenu.handleMouseleave = (e) => { + if (this.device === 'mobile') { + return + } + handleMouseleave(e) + } + } + } + } +} diff --git a/ruoyi-ui/src/layout/components/Sidebar/Item.vue b/ruoyi-ui/src/layout/components/Sidebar/Item.vue new file mode 100644 index 0000000..1a99d63 --- /dev/null +++ b/ruoyi-ui/src/layout/components/Sidebar/Item.vue @@ -0,0 +1,29 @@ +<script> +export default { + name: 'MenuItem', + functional: true, + props: { + icon: { + type: String, + default: '' + }, + title: { + type: String, + default: '' + } + }, + render(h, context) { + const { icon, title } = context.props + const vnodes = [] + + if (icon) { + vnodes.push(<svg-icon icon-class={icon}/>) + } + + if (title) { + vnodes.push(<span slot='title'>{(title)}</span>) + } + return vnodes + } +} +</script> diff --git a/ruoyi-ui/src/layout/components/Sidebar/Link.vue b/ruoyi-ui/src/layout/components/Sidebar/Link.vue new file mode 100644 index 0000000..6cd6e2c --- /dev/null +++ b/ruoyi-ui/src/layout/components/Sidebar/Link.vue @@ -0,0 +1,36 @@ + +<template> + <!-- eslint-disable vue/require-component-is --> + <component v-bind="linkProps(to)"> + <slot /> + </component> +</template> + +<script> +import { isExternal } from '@/utils/validate' + +export default { + props: { + to: { + type: String, + required: true + } + }, + methods: { + linkProps(url) { + if (isExternal(url)) { + return { + is: 'a', + href: url, + target: '_blank', + rel: 'noopener' + } + } + return { + is: 'router-link', + to: url + } + } + } +} +</script> diff --git a/ruoyi-ui/src/layout/components/Sidebar/Logo.vue b/ruoyi-ui/src/layout/components/Sidebar/Logo.vue new file mode 100644 index 0000000..021de8e --- /dev/null +++ b/ruoyi-ui/src/layout/components/Sidebar/Logo.vue @@ -0,0 +1,84 @@ +<template> + <div class="sidebar-logo-container" :class="{'collapse':collapse}"> + <transition name="sidebarLogoFade"> + <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/"> + <img v-if="logo" :src="logo" class="sidebar-logo"> + <h1 v-else class="sidebar-title">{{ title }} </h1> + </router-link> + <router-link v-else key="expand" class="sidebar-logo-link" to="/"> + <img v-if="logo" :src="logo" class="sidebar-logo"> + <h1 class="sidebar-title">{{ title }} </h1> + </router-link> + </transition> + </div> +</template> + +<script> +import logoImg from '@/assets/logo/logo.png' + +export default { + name: 'SidebarLogo', + props: { + collapse: { + type: Boolean, + required: true + } + }, + data() { + return { + title: '若依管理系统', + logo: logoImg + } + } +} +</script> + +<style lang="scss" scoped> +.sidebarLogoFade-enter-active { + transition: opacity 1.5s; +} + +.sidebarLogoFade-enter, +.sidebarLogoFade-leave-to { + opacity: 0; +} + +.sidebar-logo-container { + position: relative; + width: 100%; + height: 50px; + line-height: 50px; + background: #2b2f3a; + text-align: center; + overflow: hidden; + + & .sidebar-logo-link { + height: 100%; + width: 100%; + + & .sidebar-logo { + width: 32px; + height: 32px; + vertical-align: middle; + margin-right: 12px; + } + + & .sidebar-title { + display: inline-block; + margin: 0; + color: #fff; + font-weight: 600; + line-height: 50px; + font-size: 14px; + font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif; + vertical-align: middle; + } + } + + &.collapse { + .sidebar-logo { + margin-right: 0px; + } + } +} +</style> diff --git a/ruoyi-ui/src/layout/components/Sidebar/SidebarItem.vue b/ruoyi-ui/src/layout/components/Sidebar/SidebarItem.vue new file mode 100644 index 0000000..1ca89d2 --- /dev/null +++ b/ruoyi-ui/src/layout/components/Sidebar/SidebarItem.vue @@ -0,0 +1,93 @@ +<template> + <div v-if="!item.hidden" class="menu-wrapper"> + <template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow"> + <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)"> + <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}"> + <item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" /> + </el-menu-item> + </app-link> + </template> + + <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body> + <template slot="title"> + <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" /> + </template> + <sidebar-item + v-for="child in item.children" + :key="child.path" + :is-nest="true" + :item="child" + :base-path="resolvePath(child.path)" + class="nest-menu" + /> + </el-submenu> + </div> +</template> + +<script> +import path from 'path' +import { isExternal } from '@/utils/validate' +import Item from './Item' +import AppLink from './Link' +import FixiOSBug from './FixiOSBug' + +export default { + name: 'SidebarItem', + components: { Item, AppLink }, + mixins: [FixiOSBug], + props: { + // route object + item: { + type: Object, + required: true + }, + isNest: { + type: Boolean, + default: false + }, + basePath: { + type: String, + default: '' + } + }, + data() { + this.onlyOneChild = null + return {} + }, + methods: { + hasOneShowingChild(children = [], parent) { + const showingChildren = children.filter(item => { + if (item.hidden) { + return false + } else { + // Temp set(will be used if only has one showing child) + this.onlyOneChild = item + return true + } + }) + + // When there is only one child router, the child router is displayed by default + if (showingChildren.length === 1) { + return true + } + + // Show parent if there are no child router to display + if (showingChildren.length === 0) { + this.onlyOneChild = { ... parent, path: '', noShowingChildren: true } + return true + } + + return false + }, + resolvePath(routePath) { + if (isExternal(routePath)) { + return routePath + } + if (isExternal(this.basePath)) { + return this.basePath + } + return path.resolve(this.basePath, routePath) + } + } +} +</script> diff --git a/ruoyi-ui/src/layout/components/Sidebar/index.vue b/ruoyi-ui/src/layout/components/Sidebar/index.vue new file mode 100644 index 0000000..4d0c6c9 --- /dev/null +++ b/ruoyi-ui/src/layout/components/Sidebar/index.vue @@ -0,0 +1,54 @@ +<template> + <div :class="{'has-logo':showLogo}"> + <logo v-if="showLogo" :collapse="isCollapse" /> + <el-scrollbar wrap-class="scrollbar-wrapper"> + <el-menu + :default-active="activeMenu" + :collapse="isCollapse" + :background-color="variables.menuBg" + :text-color="variables.menuText" + :unique-opened="true" + :active-text-color="variables.menuActiveText" + :collapse-transition="false" + mode="vertical" + > + <sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" /> + </el-menu> + </el-scrollbar> + </div> +</template> + +<script> +import { mapGetters } from 'vuex' +import Logo from './Logo' +import SidebarItem from './SidebarItem' +import variables from '@/assets/styles/variables.scss' + +export default { + components: { SidebarItem, Logo }, + computed: { + ...mapGetters([ + 'permission_routes', + 'sidebar' + ]), + activeMenu() { + const route = this.$route + const { meta, path } = route + // if set path, the sidebar will highlight the path you set + if (meta.activeMenu) { + return meta.activeMenu + } + return path + }, + showLogo() { + return this.$store.state.settings.sidebarLogo + }, + variables() { + return variables + }, + isCollapse() { + return !this.sidebar.opened + } + } +} +</script> diff --git a/ruoyi-ui/src/layout/components/TagsView/ScrollPane.vue b/ruoyi-ui/src/layout/components/TagsView/ScrollPane.vue new file mode 100644 index 0000000..8be9c01 --- /dev/null +++ b/ruoyi-ui/src/layout/components/TagsView/ScrollPane.vue @@ -0,0 +1,85 @@ +<template> + <el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll"> + <slot /> + </el-scrollbar> +</template> + +<script> +const tagAndTagSpacing = 4 // tagAndTagSpacing + +export default { + name: 'ScrollPane', + data() { + return { + left: 0 + } + }, + computed: { + scrollWrapper() { + return this.$refs.scrollContainer.$refs.wrap + } + }, + methods: { + handleScroll(e) { + const eventDelta = e.wheelDelta || -e.deltaY * 40 + const $scrollWrapper = this.scrollWrapper + $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4 + }, + moveToTarget(currentTag) { + const $container = this.$refs.scrollContainer.$el + const $containerWidth = $container.offsetWidth + const $scrollWrapper = this.scrollWrapper + const tagList = this.$parent.$refs.tag + + let firstTag = null + let lastTag = null + + // find first tag and last tag + if (tagList.length > 0) { + firstTag = tagList[0] + lastTag = tagList[tagList.length - 1] + } + + if (firstTag === currentTag) { + $scrollWrapper.scrollLeft = 0 + } else if (lastTag === currentTag) { + $scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth + } else { + // find preTag and nextTag + const currentIndex = tagList.findIndex(item => item === currentTag) + const prevTag = tagList[currentIndex - 1] + const nextTag = tagList[currentIndex + 1] + + // the tag's offsetLeft after of nextTag + const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing + + // the tag's offsetLeft before of prevTag + const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing + + if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) { + $scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth + } else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) { + $scrollWrapper.scrollLeft = beforePrevTagOffsetLeft + } + } + } + } +} +</script> + +<style lang="scss" scoped> +.scroll-container { + white-space: nowrap; + position: relative; + overflow: hidden; + width: 100%; + /deep/ { + .el-scrollbar__bar { + bottom: 0px; + } + .el-scrollbar__wrap { + height: 49px; + } + } +} +</style> diff --git a/ruoyi-ui/src/layout/components/TagsView/index.vue b/ruoyi-ui/src/layout/components/TagsView/index.vue new file mode 100644 index 0000000..9fc380f --- /dev/null +++ b/ruoyi-ui/src/layout/components/TagsView/index.vue @@ -0,0 +1,286 @@ +<template> + <div id="tags-view-container" class="tags-view-container"> + <scroll-pane ref="scrollPane" class="tags-view-wrapper"> + <router-link + v-for="tag in visitedViews" + ref="tag" + :key="tag.path" + :class="isActive(tag)?'active':''" + :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }" + tag="span" + class="tags-view-item" + @click.middle.native="closeSelectedTag(tag)" + @contextmenu.prevent.native="openMenu(tag,$event)" + > + {{ tag.title }} + <span v-if="!tag.meta.affix" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" /> + </router-link> + </scroll-pane> + <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu"> + <li @click="refreshSelectedTag(selectedTag)">刷新页面</li> + <li v-if="!(selectedTag.meta&&selectedTag.meta.affix)" @click="closeSelectedTag(selectedTag)">关闭当前</li> + <li @click="closeOthersTags">关闭其他</li> + <li @click="closeAllTags(selectedTag)">关闭所有</li> + </ul> + </div> +</template> + +<script> +import ScrollPane from './ScrollPane' +import path from 'path' + +export default { + components: { ScrollPane }, + data() { + return { + visible: false, + top: 0, + left: 0, + selectedTag: {}, + affixTags: [] + } + }, + computed: { + visitedViews() { + return this.$store.state.tagsView.visitedViews + }, + routes() { + return this.$store.state.permission.routes + } + }, + watch: { + $route() { + this.addTags() + this.moveToCurrentTag() + }, + visible(value) { + if (value) { + document.body.addEventListener('click', this.closeMenu) + } else { + document.body.removeEventListener('click', this.closeMenu) + } + } + }, + mounted() { + this.initTags() + this.addTags() + }, + methods: { + isActive(route) { + return route.path === this.$route.path + }, + filterAffixTags(routes, basePath = '/') { + let tags = [] + routes.forEach(route => { + if (route.meta && route.meta.affix) { + const tagPath = path.resolve(basePath, route.path) + tags.push({ + fullPath: tagPath, + path: tagPath, + name: route.name, + meta: { ...route.meta } + }) + } + if (route.children) { + const tempTags = this.filterAffixTags(route.children, route.path) + if (tempTags.length >= 1) { + tags = [...tags, ...tempTags] + } + } + }) + return tags + }, + initTags() { + const affixTags = this.affixTags = this.filterAffixTags(this.routes) + for (const tag of affixTags) { + // Must have tag name + if (tag.name) { + this.$store.dispatch('tagsView/addVisitedView', tag) + } + } + }, + addTags() { + const { name } = this.$route + if (name) { + this.$store.dispatch('tagsView/addView', this.$route) + } + return false + }, + moveToCurrentTag() { + const tags = this.$refs.tag + this.$nextTick(() => { + for (const tag of tags) { + if (tag.to.path === this.$route.path) { + this.$refs.scrollPane.moveToTarget(tag) + // when query is different then update + if (tag.to.fullPath !== this.$route.fullPath) { + this.$store.dispatch('tagsView/updateVisitedView', this.$route) + } + break + } + } + }) + }, + refreshSelectedTag(view) { + this.$store.dispatch('tagsView/delCachedView', view).then(() => { + const { fullPath } = view + this.$nextTick(() => { + this.$router.replace({ + path: '/redirect' + fullPath + }) + }) + }) + }, + closeSelectedTag(view) { + this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => { + if (this.isActive(view)) { + this.toLastView(visitedViews, view) + } + }) + }, + closeOthersTags() { + this.$router.push(this.selectedTag) + this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => { + this.moveToCurrentTag() + }) + }, + closeAllTags(view) { + this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => { + if (this.affixTags.some(tag => tag.path === view.path)) { + return + } + this.toLastView(visitedViews, view) + }) + }, + toLastView(visitedViews, view) { + const latestView = visitedViews.slice(-1)[0] + if (latestView) { + this.$router.push(latestView) + } else { + // now the default is to redirect to the home page if there is no tags-view, + // you can adjust it according to your needs. + if (view.name === 'Dashboard') { + // to reload home page + this.$router.replace({ path: '/redirect' + view.fullPath }) + } else { + this.$router.push('/') + } + } + }, + openMenu(tag, e) { + const menuMinWidth = 105 + const offsetLeft = this.$el.getBoundingClientRect().left // container margin left + const offsetWidth = this.$el.offsetWidth // container width + const maxLeft = offsetWidth - menuMinWidth // left boundary + const left = e.clientX - offsetLeft + 15 // 15: margin right + + if (left > maxLeft) { + this.left = maxLeft + } else { + this.left = left + } + + this.top = e.clientY + this.visible = true + this.selectedTag = tag + }, + closeMenu() { + this.visible = false + } + } +} +</script> + +<style lang="scss" scoped> +.tags-view-container { + height: 34px; + width: 100%; + background: #fff; + border-bottom: 1px solid #d8dce5; + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04); + .tags-view-wrapper { + .tags-view-item { + display: inline-block; + position: relative; + cursor: pointer; + height: 26px; + line-height: 26px; + border: 1px solid #d8dce5; + color: #495060; + background: #fff; + padding: 0 8px; + font-size: 12px; + margin-left: 5px; + margin-top: 4px; + &:first-of-type { + margin-left: 15px; + } + &:last-of-type { + margin-right: 15px; + } + &.active { + background-color: #42b983; + color: #fff; + border-color: #42b983; + &::before { + content: ''; + background: #fff; + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + position: relative; + margin-right: 2px; + } + } + } + } + .contextmenu { + margin: 0; + background: #fff; + z-index: 3000; + position: absolute; + list-style-type: none; + padding: 5px 0; + border-radius: 4px; + font-size: 12px; + font-weight: 400; + color: #333; + box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3); + li { + margin: 0; + padding: 7px 16px; + cursor: pointer; + &:hover { + background: #eee; + } + } + } +} +</style> + +<style lang="scss"> +//reset element css of el-icon-close +.tags-view-wrapper { + .tags-view-item { + .el-icon-close { + width: 16px; + height: 16px; + vertical-align: 2px; + border-radius: 50%; + text-align: center; + transition: all .3s cubic-bezier(.645, .045, .355, 1); + transform-origin: 100% 50%; + &:before { + transform: scale(.6); + display: inline-block; + vertical-align: -3px; + } + &:hover { + background-color: #b4bccc; + color: #fff; + } + } + } +} +</style> diff --git a/ruoyi-ui/src/layout/components/index.js b/ruoyi-ui/src/layout/components/index.js new file mode 100644 index 0000000..7f83004 --- /dev/null +++ b/ruoyi-ui/src/layout/components/index.js @@ -0,0 +1,5 @@ +export { default as AppMain } from './AppMain' +export { default as Navbar } from './Navbar' +export { default as Settings } from './Settings' +export { default as Sidebar } from './Sidebar/index.vue' +export { default as TagsView } from './TagsView/index.vue' diff --git a/ruoyi-ui/src/layout/index.vue b/ruoyi-ui/src/layout/index.vue new file mode 100644 index 0000000..4e761ea --- /dev/null +++ b/ruoyi-ui/src/layout/index.vue @@ -0,0 +1,102 @@ +<template> + <div :class="classObj" class="app-wrapper"> + <div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside" /> + <sidebar class="sidebar-container" /> + <div :class="{hasTagsView:needTagsView}" class="main-container"> + <div :class="{'fixed-header':fixedHeader}"> + <navbar /> + <tags-view v-if="needTagsView" /> + </div> + <app-main /> + <right-panel v-if="showSettings"> + <settings /> + </right-panel> + </div> + </div> +</template> + +<script> +import RightPanel from '@/components/RightPanel' +import { AppMain, Navbar, Settings, Sidebar, TagsView } from './components' +import ResizeMixin from './mixin/ResizeHandler' +import { mapState } from 'vuex' + +export default { + name: 'Layout', + components: { + AppMain, + Navbar, + RightPanel, + Settings, + Sidebar, + TagsView + }, + mixins: [ResizeMixin], + computed: { + ...mapState({ + sidebar: state => state.app.sidebar, + device: state => state.app.device, + showSettings: state => state.settings.showSettings, + needTagsView: state => state.settings.tagsView, + fixedHeader: state => state.settings.fixedHeader + }), + classObj() { + return { + hideSidebar: !this.sidebar.opened, + openSidebar: this.sidebar.opened, + withoutAnimation: this.sidebar.withoutAnimation, + mobile: this.device === 'mobile' + } + } + }, + methods: { + handleClickOutside() { + this.$store.dispatch('app/closeSideBar', { withoutAnimation: false }) + } + } +} +</script> + +<style lang="scss" scoped> + @import "~@/assets/styles/mixin.scss"; + @import "~@/assets/styles/variables.scss"; + + .app-wrapper { + @include clearfix; + position: relative; + height: 100%; + width: 100%; + + &.mobile.openSidebar { + position: fixed; + top: 0; + } + } + + .drawer-bg { + background: #000; + opacity: 0.3; + width: 100%; + top: 0; + height: 100%; + position: absolute; + z-index: 999; + } + + .fixed-header { + position: fixed; + top: 0; + right: 0; + z-index: 9; + width: calc(100% - #{$sideBarWidth}); + transition: width 0.28s; + } + + .hideSidebar .fixed-header { + width: calc(100% - 54px) + } + + .mobile .fixed-header { + width: 100%; + } +</style> diff --git a/ruoyi-ui/src/layout/mixin/ResizeHandler.js b/ruoyi-ui/src/layout/mixin/ResizeHandler.js new file mode 100644 index 0000000..b3fc8b2 --- /dev/null +++ b/ruoyi-ui/src/layout/mixin/ResizeHandler.js @@ -0,0 +1,45 @@ +import store from '@/store' + +const { body } = document +const WIDTH = 992 // refer to Bootstrap's responsive design + +export default { + watch: { + $route(route) { + if (this.device === 'mobile' && this.sidebar.opened) { + store.dispatch('app/closeSideBar', { withoutAnimation: false }) + } + } + }, + beforeMount() { + window.addEventListener('resize', this.$_resizeHandler) + }, + beforeDestroy() { + window.removeEventListener('resize', this.$_resizeHandler) + }, + mounted() { + const isMobile = this.$_isMobile() + if (isMobile) { + store.dispatch('app/toggleDevice', 'mobile') + store.dispatch('app/closeSideBar', { withoutAnimation: true }) + } + }, + methods: { + // use $_ for mixins properties + // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential + $_isMobile() { + const rect = body.getBoundingClientRect() + return rect.width - 1 < WIDTH + }, + $_resizeHandler() { + if (!document.hidden) { + const isMobile = this.$_isMobile() + store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop') + + if (isMobile) { + store.dispatch('app/closeSideBar', { withoutAnimation: true }) + } + } + } + } +} diff --git a/ruoyi-ui/src/main.js b/ruoyi-ui/src/main.js new file mode 100644 index 0000000..c0056ee --- /dev/null +++ b/ruoyi-ui/src/main.js @@ -0,0 +1,69 @@ +import Vue from 'vue' + +import Cookies from 'js-cookie' + +import 'normalize.css/normalize.css' // a modern alternative to CSS resets + +import Element from 'element-ui' +import './assets/styles/element-variables.scss' + +import '@/assets/styles/index.scss' // global css +import '@/assets/styles/ruoyi.scss' // ruoyi css +import App from './App' +import store from './store' +import router from './router' +import permission from './directive/permission' + +import './assets/icons' // icon +import './permission' // permission control +import { getDicts } from "@/api/system/dict/data"; +import { getConfigKey } from "@/api/system/config"; +import { dateFormat, resetForm, addDateRange, selectDictLabel } from "@/utils/ruoyi"; +import Pagination from "@/components/Pagination"; + +// 全局方法挂载 +Vue.prototype.getDicts = getDicts +Vue.prototype.getConfigKey = getConfigKey +Vue.prototype.dateFormat = dateFormat +Vue.prototype.resetForm = resetForm +Vue.prototype.addDateRange = addDateRange +Vue.prototype.selectDictLabel = selectDictLabel + +Vue.prototype.msgSuccess = function (msg) { + this.$message({ showClose: true, message: msg, type: "success" }); +} + +Vue.prototype.msgError = function (msg) { + this.$message({ showClose: true, message: msg, type: "error" }); +} + +Vue.prototype.msgInfo = function (msg) { + this.$message.info(msg); +} + +// 全局组件挂载 +Vue.component('Pagination', Pagination) + +Vue.use(permission) + +/** + * If you don't want to use mock-server + * you want to use MockJs for mock api + * you can execute: mockXHR() + * + * Currently MockJs will be used in the production environment, + * please remove it before going online! ! ! + */ + +Vue.use(Element, { + size: Cookies.get('size') || 'medium' // set element-ui default size +}) + +Vue.config.productionTip = false + +new Vue({ + el: '#app', + router, + store, + render: h => h(App) +}) diff --git a/ruoyi-ui/src/permission.js b/ruoyi-ui/src/permission.js new file mode 100644 index 0000000..1813f42 --- /dev/null +++ b/ruoyi-ui/src/permission.js @@ -0,0 +1,64 @@ +import router from './router' +import store from './store' +import { Message } from 'element-ui' +import NProgress from 'nprogress' +import 'nprogress/nprogress.css' +import { getToken } from '@/utils/auth' + +NProgress.configure({ showSpinner: false }) + +const whiteList = ['/login', '/auth-redirect', '/bind', '/register'] + +router.beforeEach((to, from, next) => { + NProgress.start() + if (getToken()) { + /* has token*/ + if (to.path === '/login') { + next({ path: '/' }) + NProgress.done() + } else { + if (store.getters.roles.length === 0) { + // 判断当前用户是否已拉取完user_info信息 + store.dispatch('GetInfo').then(res => { + // 拉取user_info + const roles = res.roles + store.dispatch('GenerateRoutes', { roles }).then(accessRoutes => { + // 测试 默认静态页面 + // store.dispatch('permission/generateRoutes', { roles }).then(accessRoutes => { + // 根据roles权限生成可访问的路由表 + router.addRoutes(accessRoutes) // 动态添加可访问路由表 + next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 + }) + }) + .catch(err => { + store.dispatch('FedLogOut').then(() => { + Message.error(err) + next({ path: '/' }) + }) + }) + } else { + next() + // 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓ + // if (hasPermission(store.getters.roles, to.meta.roles)) { + // next() + // } else { + // next({ path: '/401', replace: true, query: { noGoBack: true }}) + // } + // 可删 ↑ + } + } + } else { + // 没有token + if (whiteList.indexOf(to.path) !== -1) { + // 在免登录白名单,直接进入 + next() + } else { + next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页 + NProgress.done() + } + } +}) + +router.afterEach(() => { + NProgress.done() +}) diff --git a/ruoyi-ui/src/router/index.js b/ruoyi-ui/src/router/index.js new file mode 100644 index 0000000..526d755 --- /dev/null +++ b/ruoyi-ui/src/router/index.js @@ -0,0 +1,101 @@ +import Vue from 'vue' +import Router from 'vue-router' + +Vue.use(Router) + +/* Layout */ +import Layout from '@/layout' + +/** + * Note: 路由配置项 + * + * hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1 + * alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + * // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面 + * // 若你想不管路由下面的 children 声明的个数都显示你的根路由 + * // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由 + * redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + * name:'router-name' // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题 + * meta : { + roles: ['admin','editor'] // 设置该路由进入的权限,支持多个权限叠加 + title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字 + icon: 'svg-name' // 设置该路由的图标,对应路径src/icons/svg + breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示 + } + */ + +// 公共路由 +export const constantRoutes = [ + { + path: '/redirect', + component: Layout, + hidden: true, + children: [ + { + path: '/redirect/:path*', + component: () => import('@/views/redirect') + } + ] + }, + { + path: '/login', + component: () => import('@/views/login'), + hidden: true + }, + { + path: '/404', + component: () => import('@/views/error/404'), + hidden: true + }, + { + path: '/401', + component: () => import('@/views/error/401'), + hidden: true + }, + { + path: '', + component: Layout, + redirect: 'index', + children: [ + { + path: 'index', + component: () => import('@/views/index'), + name: '首页', + meta: { title: '首页', icon: 'dashboard', noCache: true, affix: true } + } + ] + }, + { + path: '/user', + component: Layout, + hidden: true, + redirect: 'noredirect', + children: [ + { + path: 'profile', + component: () => import('@/views/system/user/profile/index'), + name: '个人中心', + meta: { title: '个人中心', icon: 'user' } + } + ] + }, + { + path: '/dict', + component: Layout, + hidden: true, + children: [ + { + path: 'type/data/:dictId(\\d+)', + component: () => import('@/views/system/dict/data'), + name: '字典数据', + meta: { title: '字典数据', icon: '' } + } + ] + } +] + +export default new Router({ + mode: 'history', // 去掉url中的# + scrollBehavior: () => ({ y: 0 }), + routes: constantRoutes +}) diff --git a/ruoyi-ui/src/settings.js b/ruoyi-ui/src/settings.js new file mode 100644 index 0000000..ca6d00c --- /dev/null +++ b/ruoyi-ui/src/settings.js @@ -0,0 +1,31 @@ +module.exports = { + title: '若依管理系统', + + /** + * 是否系统布局配置 + */ + showSettings: false, + + /** + * 是否显示 tagsView + */ + tagsView: true, + + /** + * 是否固定头部 + */ + fixedHeader: false, + + /** + * 是否显示logo + */ + sidebarLogo: true, + + /** + * @type {string | array} 'production' | ['production', 'development'] + * @description Need show err logs component. + * The default is only used in the production env + * If you want to also use it in dev, you can pass ['production', 'development'] + */ + errorLog: 'production' +} diff --git a/ruoyi-ui/src/store/getters.js b/ruoyi-ui/src/store/getters.js new file mode 100644 index 0000000..b985521 --- /dev/null +++ b/ruoyi-ui/src/store/getters.js @@ -0,0 +1,15 @@ +const getters = { + sidebar: state => state.app.sidebar, + size: state => state.app.size, + device: state => state.app.device, + visitedViews: state => state.tagsView.visitedViews, + cachedViews: state => state.tagsView.cachedViews, + token: state => state.user.token, + avatar: state => state.user.avatar, + name: state => state.user.name, + introduction: state => state.user.introduction, + roles: state => state.user.roles, + permissions: state => state.user.permissions, + permission_routes: state => state.permission.routes +} +export default getters diff --git a/ruoyi-ui/src/store/index.js b/ruoyi-ui/src/store/index.js new file mode 100644 index 0000000..eceb2cd --- /dev/null +++ b/ruoyi-ui/src/store/index.js @@ -0,0 +1,23 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import app from './modules/app' +import user from './modules/user' +import tagsView from './modules/tagsView' +import permission from './modules/permission' +import settings from './modules/settings' +import getters from './getters' + +Vue.use(Vuex) + +const store = new Vuex.Store({ + modules: { + app, + user, + tagsView, + permission, + settings + }, + getters +}) + +export default store diff --git a/ruoyi-ui/src/store/modules/app.js b/ruoyi-ui/src/store/modules/app.js new file mode 100644 index 0000000..c8d8ee9 --- /dev/null +++ b/ruoyi-ui/src/store/modules/app.js @@ -0,0 +1,56 @@ +import Cookies from 'js-cookie' + +const state = { + sidebar: { + opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true, + withoutAnimation: false + }, + device: 'desktop', + size: Cookies.get('size') || 'medium' +} + +const mutations = { + TOGGLE_SIDEBAR: state => { + state.sidebar.opened = !state.sidebar.opened + state.sidebar.withoutAnimation = false + if (state.sidebar.opened) { + Cookies.set('sidebarStatus', 1) + } else { + Cookies.set('sidebarStatus', 0) + } + }, + CLOSE_SIDEBAR: (state, withoutAnimation) => { + Cookies.set('sidebarStatus', 0) + state.sidebar.opened = false + state.sidebar.withoutAnimation = withoutAnimation + }, + TOGGLE_DEVICE: (state, device) => { + state.device = device + }, + SET_SIZE: (state, size) => { + state.size = size + Cookies.set('size', size) + } +} + +const actions = { + toggleSideBar({ commit }) { + commit('TOGGLE_SIDEBAR') + }, + closeSideBar({ commit }, { withoutAnimation }) { + commit('CLOSE_SIDEBAR', withoutAnimation) + }, + toggleDevice({ commit }, device) { + commit('TOGGLE_DEVICE', device) + }, + setSize({ commit }, size) { + commit('SET_SIZE', size) + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} diff --git a/ruoyi-ui/src/store/modules/permission.js b/ruoyi-ui/src/store/modules/permission.js new file mode 100644 index 0000000..da937ae --- /dev/null +++ b/ruoyi-ui/src/store/modules/permission.js @@ -0,0 +1,53 @@ +import { constantRoutes } from '@/router' +import { getRouters } from '@/api/menu' +import Layout from '@/layout/index' + +const permission = { + state: { + routes: [], + addRoutes: [] + }, + mutations: { + SET_ROUTES: (state, routes) => { + state.addRoutes = routes + state.routes = constantRoutes.concat(routes) + } + }, + actions: { + // 生成路由 + GenerateRoutes({ commit }) { + return new Promise(resolve => { + // 向后端请求路由数据 + getRouters().then(res => { + const accessedRoutes = filterAsyncRouter(res.data) + commit('SET_ROUTES', accessedRoutes) + resolve(accessedRoutes) + }) + }) + } + } +} + +// 遍历后台传来的路由字符串,转换为组件对象 +function filterAsyncRouter(asyncRouterMap) { + return asyncRouterMap.filter(route => { + if (route.component) { + // Layout组件特殊处理 + if (route.component === 'Layout') { + route.component = Layout + } else { + route.component = loadView(route.component) + } + } + if (route.children != null && route.children && route.children.length) { + route.children = filterAsyncRouter(route.children) + } + return true + }) +} + +export const loadView = (view) => { // 路由懒加载 + return () => import(`@/views/${view}`) +} + +export default permission diff --git a/ruoyi-ui/src/store/modules/settings.js b/ruoyi-ui/src/store/modules/settings.js new file mode 100644 index 0000000..30685e6 --- /dev/null +++ b/ruoyi-ui/src/store/modules/settings.js @@ -0,0 +1,34 @@ +import variables from '@/assets/styles/element-variables.scss' +import defaultSettings from '@/settings' + +const { showSettings, tagsView, fixedHeader, sidebarLogo } = defaultSettings + +const state = { + theme: variables.theme, + showSettings: showSettings, + tagsView: tagsView, + fixedHeader: fixedHeader, + sidebarLogo: sidebarLogo +} + +const mutations = { + CHANGE_SETTING: (state, { key, value }) => { + if (state.hasOwnProperty(key)) { + state[key] = value + } + } +} + +const actions = { + changeSetting({ commit }, data) { + commit('CHANGE_SETTING', data) + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} + diff --git a/ruoyi-ui/src/store/modules/tagsView.js b/ruoyi-ui/src/store/modules/tagsView.js new file mode 100644 index 0000000..e128d2a --- /dev/null +++ b/ruoyi-ui/src/store/modules/tagsView.js @@ -0,0 +1,165 @@ +const state = { + visitedViews: [], + cachedViews: [] +} + +const mutations = { + ADD_VISITED_VIEW: (state, view) => { + if (state.visitedViews.some(v => v.path === view.path)) return + state.visitedViews.push( + Object.assign({}, view, { + title: view.meta.title || 'no-name' + }) + ) + }, + ADD_CACHED_VIEW: (state, view) => { + if (state.cachedViews.includes(view.name)) return + if (!view.meta.noCache) { + state.cachedViews.push(view.name) + } + }, + + DEL_VISITED_VIEW: (state, view) => { + for (const [i, v] of state.visitedViews.entries()) { + if (v.path === view.path) { + state.visitedViews.splice(i, 1) + break + } + } + }, + DEL_CACHED_VIEW: (state, view) => { + for (const i of state.cachedViews) { + if (i === view.name) { + const index = state.cachedViews.indexOf(i) + state.cachedViews.splice(index, 1) + break + } + } + }, + + DEL_OTHERS_VISITED_VIEWS: (state, view) => { + state.visitedViews = state.visitedViews.filter(v => { + return v.meta.affix || v.path === view.path + }) + }, + DEL_OTHERS_CACHED_VIEWS: (state, view) => { + for (const i of state.cachedViews) { + if (i === view.name) { + const index = state.cachedViews.indexOf(i) + state.cachedViews = state.cachedViews.slice(index, index + 1) + break + } + } + }, + + DEL_ALL_VISITED_VIEWS: state => { + // keep affix tags + const affixTags = state.visitedViews.filter(tag => tag.meta.affix) + state.visitedViews = affixTags + }, + DEL_ALL_CACHED_VIEWS: state => { + state.cachedViews = [] + }, + + UPDATE_VISITED_VIEW: (state, view) => { + for (let v of state.visitedViews) { + if (v.path === view.path) { + v = Object.assign(v, view) + break + } + } + } +} + +const actions = { + addView({ dispatch }, view) { + dispatch('addVisitedView', view) + dispatch('addCachedView', 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) + dispatch('delCachedView', view) + resolve({ + visitedViews: [...state.visitedViews], + cachedViews: [...state.cachedViews] + }) + }) + }, + delVisitedView({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_VISITED_VIEW', view) + resolve([...state.visitedViews]) + }) + }, + 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) + dispatch('delOthersCachedViews', view) + resolve({ + visitedViews: [...state.visitedViews], + cachedViews: [...state.cachedViews] + }) + }) + }, + delOthersVisitedViews({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_OTHERS_VISITED_VIEWS', view) + resolve([...state.visitedViews]) + }) + }, + delOthersCachedViews({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_OTHERS_CACHED_VIEWS', view) + resolve([...state.cachedViews]) + }) + }, + + delAllViews({ dispatch, state }, view) { + return new Promise(resolve => { + dispatch('delAllVisitedViews', view) + dispatch('delAllCachedViews', view) + resolve({ + visitedViews: [...state.visitedViews], + cachedViews: [...state.cachedViews] + }) + }) + }, + delAllVisitedViews({ commit, state }) { + return new Promise(resolve => { + commit('DEL_ALL_VISITED_VIEWS') + resolve([...state.visitedViews]) + }) + }, + delAllCachedViews({ commit, state }) { + return new Promise(resolve => { + commit('DEL_ALL_CACHED_VIEWS') + resolve([...state.cachedViews]) + }) + }, + + updateVisitedView({ commit }, view) { + commit('UPDATE_VISITED_VIEW', view) + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} diff --git a/ruoyi-ui/src/store/modules/user.js b/ruoyi-ui/src/store/modules/user.js new file mode 100644 index 0000000..20f44bf --- /dev/null +++ b/ruoyi-ui/src/store/modules/user.js @@ -0,0 +1,96 @@ +import { login, logout, getInfo } from '@/api/login' +import { getToken, setToken, removeToken } from '@/utils/auth' + +const user = { + state: { + token: getToken(), + name: '', + avatar: '', + roles: [], + permissions: [] + }, + + mutations: { + SET_TOKEN: (state, token) => { + state.token = token + }, + SET_NAME: (state, name) => { + state.name = name + }, + SET_AVATAR: (state, avatar) => { + state.avatar = avatar + }, + SET_ROLES: (state, roles) => { + state.roles = roles + }, + SET_PERMISSIONS: (state, permissions) => { + state.permissions = permissions + } + }, + + actions: { + // 登录 + Login({ commit }, userInfo) { + const username = userInfo.username.trim() + const password = userInfo.password + const code = userInfo.code + const uuid = userInfo.uuid + return new Promise((resolve, reject) => { + login(username, password, code, uuid).then(res => { + setToken(res.token) + commit('SET_TOKEN', res.token) + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + + // 获取用户信息 + GetInfo({ commit, state }) { + return new Promise((resolve, reject) => { + getInfo(state.token).then(res => { + const user = res.user + const avatar = user.avatar == "" ? require("@/assets/image/profile.jpg") : process.env.VUE_APP_BASE_API + user.avatar; + if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组 + commit('SET_ROLES', res.roles) + commit('SET_PERMISSIONS', res.permissions) + } else { + commit('SET_ROLES', ['ROLE_DEFAULT']) + } + commit('SET_NAME', user.username) + commit('SET_AVATAR', avatar) + resolve(res) + }).catch(error => { + reject(error) + }) + }) + }, + + // 退出系统 + LogOut({ commit, state }) { + return new Promise((resolve, reject) => { + logout(state.token).then(() => { + commit('SET_TOKEN', '') + commit('SET_ROLES', []) + commit('SET_PERMISSIONS', []) + removeToken() + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + + // 前端 登出 + FedLogOut({ commit }) { + return new Promise(resolve => { + commit('SET_TOKEN', '') + removeToken() + resolve() + }) + } + } +} + +export default user diff --git a/ruoyi-ui/src/utils/auth.js b/ruoyi-ui/src/utils/auth.js new file mode 100644 index 0000000..88d7b6c --- /dev/null +++ b/ruoyi-ui/src/utils/auth.js @@ -0,0 +1,15 @@ +import Cookies from 'js-cookie' + +const TokenKey = 'Admin-Token' + +export function getToken() { + return Cookies.get(TokenKey) +} + +export function setToken(token) { + return Cookies.set(TokenKey, token) +} + +export function removeToken() { + return Cookies.remove(TokenKey) +} diff --git a/ruoyi-ui/src/utils/index.js b/ruoyi-ui/src/utils/index.js new file mode 100644 index 0000000..9e171eb --- /dev/null +++ b/ruoyi-ui/src/utils/index.js @@ -0,0 +1,317 @@ +/** + * 表格时间格式化 + */ +export function formatDate(cellValue) { + if (cellValue == null || cellValue == "") return ""; + var date = new Date(cellValue) + var year = date.getFullYear() + var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1 + var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() + var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() + var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() + var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds() + return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds +} + +/** + * @param {number} time + * @param {string} option + * @returns {string} + */ +export function formatTime(time, option) { + if (('' + time).length === 10) { + time = parseInt(time) * 1000 + } else { + time = +time + } + const d = new Date(time) + const now = Date.now() + + const diff = (now - d) / 1000 + + if (diff < 30) { + return '刚刚' + } else if (diff < 3600) { + // less 1 hour + return Math.ceil(diff / 60) + '分钟前' + } else if (diff < 3600 * 24) { + return Math.ceil(diff / 3600) + '小时前' + } else if (diff < 3600 * 24 * 2) { + return '1天前' + } + if (option) { + return parseTime(time, option) + } else { + return ( + d.getMonth() + + 1 + + '月' + + d.getDate() + + '日' + + d.getHours() + + '时' + + d.getMinutes() + + '分' + ) + } +} + +/** + * @param {string} url + * @returns {Object} + */ +export function getQueryObject(url) { + url = url == null ? window.location.href : url + const search = url.substring(url.lastIndexOf('?') + 1) + const obj = {} + const reg = /([^?&=]+)=([^?&=]*)/g + search.replace(reg, (rs, $1, $2) => { + const name = decodeURIComponent($1) + let val = decodeURIComponent($2) + val = String(val) + obj[name] = val + return rs + }) + return obj +} + +/** + * @param {string} input value + * @returns {number} output value + */ +export function byteLength(str) { + // returns the byte length of an utf8 string + let s = str.length + for (var i = str.length - 1; i >= 0; i--) { + const code = str.charCodeAt(i) + if (code > 0x7f && code <= 0x7ff) s++ + else if (code > 0x7ff && code <= 0xffff) s += 2 + if (code >= 0xDC00 && code <= 0xDFFF) i-- + } + return s +} + +/** + * @param {Array} actual + * @returns {Array} + */ +export function cleanArray(actual) { + const newArray = [] + for (let i = 0; i < actual.length; i++) { + if (actual[i]) { + newArray.push(actual[i]) + } + } + return newArray +} + +/** + * @param {Object} json + * @returns {Array} + */ +export function param(json) { + if (!json) return '' + return cleanArray( + Object.keys(json).map(key => { + if (json[key] === undefined) return '' + return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]) + }) + ).join('&') +} + +/** + * @param {string} url + * @returns {Object} + */ +export function param2Obj(url) { + const search = url.split('?')[1] + if (!search) { + return {} + } + return JSON.parse( + '{"' + + decodeURIComponent(search) + .replace(/"/g, '\\"') + .replace(/&/g, '","') + .replace(/=/g, '":"') + .replace(/\+/g, ' ') + + '"}' + ) +} + +/** + * @param {string} val + * @returns {string} + */ +export function html2Text(val) { + const div = document.createElement('div') + div.innerHTML = val + return div.textContent || div.innerText +} + +/** + * Merges two objects, giving the last one precedence + * @param {Object} target + * @param {(Object|Array)} source + * @returns {Object} + */ +export function objectMerge(target, source) { + if (typeof target !== 'object') { + target = {} + } + if (Array.isArray(source)) { + return source.slice() + } + Object.keys(source).forEach(property => { + const sourceProperty = source[property] + if (typeof sourceProperty === 'object') { + target[property] = objectMerge(target[property], sourceProperty) + } else { + target[property] = sourceProperty + } + }) + return target +} + +/** + * @param {HTMLElement} element + * @param {string} className + */ +export function toggleClass(element, className) { + if (!element || !className) { + return + } + let classString = element.className + const nameIndex = classString.indexOf(className) + if (nameIndex === -1) { + classString += '' + className + } else { + classString = + classString.substr(0, nameIndex) + + classString.substr(nameIndex + className.length) + } + element.className = classString +} + +/** + * @param {string} type + * @returns {Date} + */ +export function getTime(type) { + if (type === 'start') { + return new Date().getTime() - 3600 * 1000 * 24 * 90 + } else { + return new Date(new Date().toDateString()) + } +} + +/** + * @param {Function} func + * @param {number} wait + * @param {boolean} immediate + * @return {*} + */ +export function debounce(func, wait, immediate) { + let timeout, args, context, timestamp, result + + const later = function() { + // 据上一次触发时间间隔 + const last = +new Date() - timestamp + + // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait + if (last < wait && last > 0) { + timeout = setTimeout(later, wait - last) + } else { + timeout = null + // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用 + if (!immediate) { + result = func.apply(context, args) + if (!timeout) context = args = null + } + } + } + + return function(...args) { + context = this + timestamp = +new Date() + const callNow = immediate && !timeout + // 如果延时不存在,重新设定延时 + if (!timeout) timeout = setTimeout(later, wait) + if (callNow) { + result = func.apply(context, args) + context = args = null + } + + return result + } +} + +/** + * This is just a simple version of deep copy + * Has a lot of edge cases bug + * If you want to use a perfect deep copy, use lodash's _.cloneDeep + * @param {Object} source + * @returns {Object} + */ +export function deepClone(source) { + if (!source && typeof source !== 'object') { + throw new Error('error arguments', 'deepClone') + } + const targetObj = source.constructor === Array ? [] : {} + Object.keys(source).forEach(keys => { + if (source[keys] && typeof source[keys] === 'object') { + targetObj[keys] = deepClone(source[keys]) + } else { + targetObj[keys] = source[keys] + } + }) + return targetObj +} + +/** + * @param {Array} arr + * @returns {Array} + */ +export function uniqueArr(arr) { + return Array.from(new Set(arr)) +} + +/** + * @returns {string} + */ +export function createUniqueString() { + const timestamp = +new Date() + '' + const randomNum = parseInt((1 + Math.random()) * 65536) + '' + return (+(randomNum + timestamp)).toString(32) +} + +/** + * Check if an element has a class + * @param {HTMLElement} elm + * @param {string} cls + * @returns {boolean} + */ +export function hasClass(ele, cls) { + return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')) +} + +/** + * Add class to element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function addClass(ele, cls) { + if (!hasClass(ele, cls)) ele.className += ' ' + cls +} + +/** + * Remove class from element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function removeClass(ele, cls) { + if (hasClass(ele, cls)) { + const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)') + ele.className = ele.className.replace(reg, ' ') + } +} diff --git a/ruoyi-ui/src/utils/permission.js b/ruoyi-ui/src/utils/permission.js new file mode 100644 index 0000000..efb4cc8 --- /dev/null +++ b/ruoyi-ui/src/utils/permission.js @@ -0,0 +1,24 @@ +import store from '@/store' + +/** + * @param {Array} value + * @returns {Boolean} + */ +export default function checkPermission(value) { + if (value && value instanceof Array && value.length > 0) { + const roles = store.getters && store.getters.roles + const permissionRoles = value + + const hasPermission = roles.some(role => { + return permissionRoles.includes(role) + }) + + if (!hasPermission) { + return false + } + return true + } else { + console.error(`need roles! Like v-permission="['admin','editor']"`) + return false + } +} diff --git a/ruoyi-ui/src/utils/request.js b/ruoyi-ui/src/utils/request.js new file mode 100644 index 0000000..fd6e5f0 --- /dev/null +++ b/ruoyi-ui/src/utils/request.js @@ -0,0 +1,65 @@ +import axios from 'axios' +import { Notification, MessageBox } from 'element-ui' +import store from '@/store' +import { getToken } from '@/utils/auth' + +axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8' +// 创建axios实例 +const service = axios.create({ + // axios中请求配置有baseURL选项,表示请求URL公共部分 + baseURL: process.env.VUE_APP_BASE_API, + // 超时 + timeout: 10000 +}) +// request拦截器 +service.interceptors.request.use( + config => { + if (getToken()) { + config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 + } + return config + }, + error => { + console.log(error) + Promise.reject(error) + } +) + +// 响应拦截器 +service.interceptors.response.use(res => { + const code = res.data.code + if (code === 401) { + MessageBox.confirm( + '登录状态已过期,您可以继续留在该页面,或者重新登录', + '系统提示', + { + confirmButtonText: '重新登录', + cancelButtonText: '取消', + type: 'warning' + } + ).then(() => { + store.dispatch('LogOut').then(() => { + location.reload() // 为了重新实例化vue-router对象 避免bug + }) + }) + } else if (code !== 200) { + Notification.error({ + title: res.data.msg + }) + return Promise.reject('error') + } else { + return res.data + } + }, + error => { + console.log('err' + error) + Message({ + message: error.message, + type: 'error', + duration: 5 * 1000 + }) + return Promise.reject(error) + } +) + +export default service diff --git a/ruoyi-ui/src/utils/ruoyi.js b/ruoyi-ui/src/utils/ruoyi.js new file mode 100644 index 0000000..acef7f9 --- /dev/null +++ b/ruoyi-ui/src/utils/ruoyi.js @@ -0,0 +1,76 @@ +/** + * 通用js方法封装处理 + * Copyright (c) 2019 ruoyi + */ + +// 日期格式化 +export function dateFormat(date, pattern) { + var d = new Date(date).Format("yyyy-MM-dd hh:mm:ss"); + if (pattern) { + d = new Date(date).Format(pattern); + } + return d.toLocaleString(); +} + +// 表单重置 +export function resetForm(refName) { + if (this.$refs[refName] !== undefined) { + this.$refs[refName].resetFields(); + } +} + +// 添加日期范围 +export function addDateRange(params, dateRange) { + var search = params; + if (null != dateRange) { + search.params = { + beginTime: this.dateRange[0], + endTime: this.dateRange[1] + }; + } + return search; +} + +// 回显数据字典 +export function selectDictLabel(datas, value) { + var actions = []; + Object.keys(datas).map((key) => { + if (datas[key].dictValue == ('' + value)) { + actions.push(datas[key].dictLabel); + return false; + } + }) + return actions.join(''); +} + +// 字符串格式化(%s ) +export function sprintf(str) { + var args = arguments, flag = true, i = 1; + str = str.replace(/%s/g, function () { + var arg = args[i++]; + if (typeof arg === 'undefined') { + flag = false; + return ''; + } + return arg; + }); + return flag ? str : ''; +} + +Date.prototype.Format = function (fmt) { + var o = { + "M+": this.getMonth() + 1, // 月份 + "d+": this.getDate(), // 日 + "h+": this.getHours(), // 小时 + "m+": this.getMinutes(), // 分 + "s+": this.getSeconds(), // 秒 + "q+": Math.floor((this.getMonth() + 3) / 3), // 季度 + "S": this.getMilliseconds() // 毫秒 + }; + if (/(y+)/.test(fmt)) + fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); + for (var k in o) + if (new RegExp("(" + k + ")").test(fmt)) + fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); + return fmt; +} \ No newline at end of file diff --git a/ruoyi-ui/src/utils/scroll-to.js b/ruoyi-ui/src/utils/scroll-to.js new file mode 100644 index 0000000..709fa57 --- /dev/null +++ b/ruoyi-ui/src/utils/scroll-to.js @@ -0,0 +1,58 @@ +Math.easeInOutQuad = function(t, b, c, d) { + t /= d / 2 + if (t < 1) { + return c / 2 * t * t + b + } + t-- + return -c / 2 * (t * (t - 2) - 1) + b +} + +// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts +var requestAnimFrame = (function() { + return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) } +})() + +/** + * Because it's so fucking difficult to detect the scrolling element, just move them all + * @param {number} amount + */ +function move(amount) { + document.documentElement.scrollTop = amount + document.body.parentNode.scrollTop = amount + document.body.scrollTop = amount +} + +function position() { + return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop +} + +/** + * @param {number} to + * @param {number} duration + * @param {Function} callback + */ +export function scrollTo(to, duration, callback) { + const start = position() + const change = to - start + const increment = 20 + let currentTime = 0 + duration = (typeof (duration) === 'undefined') ? 500 : duration + var animateScroll = function() { + // increment the time + currentTime += increment + // find the value with the quadratic in-out easing function + var val = Math.easeInOutQuad(currentTime, start, change, duration) + // move the document.body + move(val) + // do the animation unless its over + if (currentTime < duration) { + requestAnimFrame(animateScroll) + } else { + if (callback && typeof (callback) === 'function') { + // the animation is done so lets callback + callback() + } + } + } + animateScroll() +} diff --git a/ruoyi-ui/src/utils/validate.js b/ruoyi-ui/src/utils/validate.js new file mode 100644 index 0000000..9c00400 --- /dev/null +++ b/ruoyi-ui/src/utils/validate.js @@ -0,0 +1,83 @@ +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validUsername(str) { + const valid_map = ['admin', 'editor'] + return valid_map.indexOf(str.trim()) >= 0 +} + +/** + * @param {string} url + * @returns {Boolean} + */ +export function validURL(url) { + const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ + return reg.test(url) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validLowerCase(str) { + const reg = /^[a-z]+$/ + return reg.test(str) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validUpperCase(str) { + const reg = /^[A-Z]+$/ + return reg.test(str) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validAlphabets(str) { + const reg = /^[A-Za-z]+$/ + return reg.test(str) +} + +/** + * @param {string} email + * @returns {Boolean} + */ +export function validEmail(email) { + const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + return reg.test(email) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function isString(str) { + if (typeof str === 'string' || str instanceof String) { + return true + } + return false +} + +/** + * @param {Array} arg + * @returns {Boolean} + */ +export function isArray(arg) { + if (typeof Array.isArray === 'undefined') { + return Object.prototype.toString.call(arg) === '[object Array]' + } + return Array.isArray(arg) +} diff --git a/ruoyi-ui/src/views/components/icons/element-icons.js b/ruoyi-ui/src/views/components/icons/element-icons.js new file mode 100644 index 0000000..d9fd628 --- /dev/null +++ b/ruoyi-ui/src/views/components/icons/element-icons.js @@ -0,0 +1,74 @@ +const elementIcons = [ + 'info', + 'error', + 'success', + 'warning', + 'question', + 'back', + 'arrow-left', + 'arrow-down', + 'arrow-right', + 'arrow-up', + 'caret-left', + 'caret-bottom', + 'caret-top', + 'caret-right', + 'd-arrow-left', + 'd-arrow-right', + 'minus', + 'plus', + 'remove', + 'circle-plus', + 'remove-outline', + 'circle-plus-outline', + 'close', + 'check', + 'circle-close', + 'circle-check', + 'circle-close-outline', + 'circle-check-outline', + 'zoom-out', + 'zoom-in', + 'd-caret', + 'sort', + 'sort-down', + 'sort-up', + 'tickets', + 'document', + 'goods', + 'sold-out', + 'news', + 'message', + 'date', + 'printer', + 'time', + 'bell', + 'mobile-phone', + 'service', + 'view', + 'menu', + 'more', + 'more-outline', + 'star-on', + 'star-off', + 'location', + 'location-outline', + 'phone', + 'phone-outline', + 'picture', + 'picture-outline', + 'delete', + 'search', + 'edit', + 'edit-outline', + 'rank', + 'refresh', + 'share', + 'setting', + 'upload', + 'upload2', + 'download', + 'loading' +] + +export default elementIcons diff --git a/ruoyi-ui/src/views/components/icons/index.vue b/ruoyi-ui/src/views/components/icons/index.vue new file mode 100644 index 0000000..f422eee --- /dev/null +++ b/ruoyi-ui/src/views/components/icons/index.vue @@ -0,0 +1,87 @@ +<template> + <div class="icons-container"> + <aside> + <a href="#" target="_blank">Add and use + </a> + </aside> + <el-tabs type="border-card"> + <el-tab-pane label="Icons"> + <div v-for="item of svgIcons" :key="item"> + <el-tooltip placement="top"> + <div slot="content"> + {{ generateIconCode(item) }} + </div> + <div class="icon-item"> + <svg-icon :icon-class="item" class-name="disabled" /> + <span>{{ item }}</span> + </div> + </el-tooltip> + </div> + </el-tab-pane> + <el-tab-pane label="Element-UI Icons"> + <div v-for="item of elementIcons" :key="item"> + <el-tooltip placement="top"> + <div slot="content"> + {{ generateElementIconCode(item) }} + </div> + <div class="icon-item"> + <i :class="'el-icon-' + item" /> + <span>{{ item }}</span> + </div> + </el-tooltip> + </div> + </el-tab-pane> + </el-tabs> + </div> +</template> + +<script> +import svgIcons from './svg-icons' +import elementIcons from './element-icons' + +export default { + name: 'Icons', + data() { + return { + svgIcons, + elementIcons + } + }, + methods: { + generateIconCode(symbol) { + return `<svg-icon icon-class="${symbol}" />` + }, + generateElementIconCode(symbol) { + return `<i class="el-icon-${symbol}" />` + } + } +} +</script> + +<style lang="scss" scoped> +.icons-container { + margin: 10px 20px 0; + overflow: hidden; + + .icon-item { + margin: 20px; + height: 85px; + text-align: center; + width: 100px; + float: left; + font-size: 30px; + color: #24292e; + cursor: pointer; + } + + span { + display: block; + font-size: 16px; + margin-top: 10px; + } + + .disabled { + pointer-events: none; + } +} +</style> diff --git a/ruoyi-ui/src/views/components/icons/svg-icons.js b/ruoyi-ui/src/views/components/icons/svg-icons.js new file mode 100644 index 0000000..9713fd8 --- /dev/null +++ b/ruoyi-ui/src/views/components/icons/svg-icons.js @@ -0,0 +1,10 @@ +const req = require.context('../../../assets/icons/svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys() + +const re = /\.\/(.*)\.svg/ + +const svgIcons = requireAll(req).map(i => { + return i.match(re)[1] +}) + +export default svgIcons diff --git a/ruoyi-ui/src/views/dashboard/BarChart.vue b/ruoyi-ui/src/views/dashboard/BarChart.vue new file mode 100644 index 0000000..6b464e1 --- /dev/null +++ b/ruoyi-ui/src/views/dashboard/BarChart.vue @@ -0,0 +1,102 @@ +<template> + <div :class="className" :style="{height:height,width:width}" /> +</template> + +<script> +import echarts from 'echarts' +require('echarts/theme/macarons') // echarts theme +import resize from './mixins/resize' + +const animationDuration = 6000 + +export default { + mixins: [resize], + props: { + className: { + type: String, + default: 'chart' + }, + width: { + type: String, + default: '100%' + }, + height: { + type: String, + default: '300px' + } + }, + data() { + return { + chart: null + } + }, + mounted() { + this.$nextTick(() => { + this.initChart() + }) + }, + beforeDestroy() { + if (!this.chart) { + return + } + this.chart.dispose() + this.chart = null + }, + methods: { + initChart() { + this.chart = echarts.init(this.$el, 'macarons') + + this.chart.setOption({ + tooltip: { + trigger: 'axis', + axisPointer: { // 坐标轴指示器,坐标轴触发有效 + type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' + } + }, + grid: { + top: 10, + left: '2%', + right: '2%', + bottom: '3%', + containLabel: true + }, + xAxis: [{ + type: 'category', + data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], + axisTick: { + alignWithLabel: true + } + }], + yAxis: [{ + type: 'value', + axisTick: { + show: false + } + }], + series: [{ + name: 'pageA', + type: 'bar', + stack: 'vistors', + barWidth: '60%', + data: [79, 52, 200, 334, 390, 330, 220], + animationDuration + }, { + name: 'pageB', + type: 'bar', + stack: 'vistors', + barWidth: '60%', + data: [80, 52, 200, 334, 390, 330, 220], + animationDuration + }, { + name: 'pageC', + type: 'bar', + stack: 'vistors', + barWidth: '60%', + data: [30, 52, 200, 334, 390, 330, 220], + animationDuration + }] + }) + } + } +} +</script> diff --git a/ruoyi-ui/src/views/dashboard/LineChart.vue b/ruoyi-ui/src/views/dashboard/LineChart.vue new file mode 100644 index 0000000..4e30a37 --- /dev/null +++ b/ruoyi-ui/src/views/dashboard/LineChart.vue @@ -0,0 +1,135 @@ +<template> + <div :class="className" :style="{height:height,width:width}" /> +</template> + +<script> +import echarts from 'echarts' +require('echarts/theme/macarons') // echarts theme +import resize from './mixins/resize' + +export default { + mixins: [resize], + props: { + className: { + type: String, + default: 'chart' + }, + width: { + type: String, + default: '100%' + }, + height: { + type: String, + default: '350px' + }, + autoResize: { + type: Boolean, + default: true + }, + chartData: { + type: Object, + required: true + } + }, + data() { + return { + chart: null + } + }, + watch: { + chartData: { + deep: true, + handler(val) { + this.setOptions(val) + } + } + }, + mounted() { + this.$nextTick(() => { + this.initChart() + }) + }, + beforeDestroy() { + if (!this.chart) { + return + } + this.chart.dispose() + this.chart = null + }, + methods: { + initChart() { + this.chart = echarts.init(this.$el, 'macarons') + this.setOptions(this.chartData) + }, + setOptions({ expectedData, actualData } = {}) { + this.chart.setOption({ + xAxis: { + data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], + boundaryGap: false, + axisTick: { + show: false + } + }, + grid: { + left: 10, + right: 10, + bottom: 20, + top: 30, + containLabel: true + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'cross' + }, + padding: [5, 10] + }, + yAxis: { + axisTick: { + show: false + } + }, + legend: { + data: ['expected', 'actual'] + }, + series: [{ + name: 'expected', itemStyle: { + normal: { + color: '#FF005A', + lineStyle: { + color: '#FF005A', + width: 2 + } + } + }, + smooth: true, + type: 'line', + data: expectedData, + animationDuration: 2800, + animationEasing: 'cubicInOut' + }, + { + name: 'actual', + smooth: true, + type: 'line', + itemStyle: { + normal: { + color: '#3888fa', + lineStyle: { + color: '#3888fa', + width: 2 + }, + areaStyle: { + color: '#f3f8ff' + } + } + }, + data: actualData, + animationDuration: 2800, + animationEasing: 'quadraticOut' + }] + }) + } + } +} +</script> diff --git a/ruoyi-ui/src/views/dashboard/PanelGroup.vue b/ruoyi-ui/src/views/dashboard/PanelGroup.vue new file mode 100644 index 0000000..8d3a73b --- /dev/null +++ b/ruoyi-ui/src/views/dashboard/PanelGroup.vue @@ -0,0 +1,181 @@ +<template> + <el-row :gutter="40" class="panel-group"> + <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col"> + <div class="card-panel" @click="handleSetLineChartData('newVisitis')"> + <div class="card-panel-icon-wrapper icon-people"> + <svg-icon icon-class="peoples" class-name="card-panel-icon" /> + </div> + <div class="card-panel-description"> + <div class="card-panel-text"> + 访客 + </div> + <count-to :start-val="0" :end-val="102400" :duration="2600" class="card-panel-num" /> + </div> + </div> + </el-col> + <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col"> + <div class="card-panel" @click="handleSetLineChartData('messages')"> + <div class="card-panel-icon-wrapper icon-message"> + <svg-icon icon-class="message" class-name="card-panel-icon" /> + </div> + <div class="card-panel-description"> + <div class="card-panel-text"> + 消息 + </div> + <count-to :start-val="0" :end-val="81212" :duration="3000" class="card-panel-num" /> + </div> + </div> + </el-col> + <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col"> + <div class="card-panel" @click="handleSetLineChartData('purchases')"> + <div class="card-panel-icon-wrapper icon-money"> + <svg-icon icon-class="money" class-name="card-panel-icon" /> + </div> + <div class="card-panel-description"> + <div class="card-panel-text"> + 金额 + </div> + <count-to :start-val="0" :end-val="9280" :duration="3200" class="card-panel-num" /> + </div> + </div> + </el-col> + <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col"> + <div class="card-panel" @click="handleSetLineChartData('shoppings')"> + <div class="card-panel-icon-wrapper icon-shopping"> + <svg-icon icon-class="shopping" class-name="card-panel-icon" /> + </div> + <div class="card-panel-description"> + <div class="card-panel-text"> + 订单 + </div> + <count-to :start-val="0" :end-val="13600" :duration="3600" class="card-panel-num" /> + </div> + </div> + </el-col> + </el-row> +</template> + +<script> +import CountTo from 'vue-count-to' + +export default { + components: { + CountTo + }, + methods: { + handleSetLineChartData(type) { + this.$emit('handleSetLineChartData', type) + } + } +} +</script> + +<style lang="scss" scoped> +.panel-group { + margin-top: 18px; + + .card-panel-col { + margin-bottom: 32px; + } + + .card-panel { + height: 108px; + cursor: pointer; + font-size: 12px; + position: relative; + overflow: hidden; + color: #666; + background: #fff; + box-shadow: 4px 4px 40px rgba(0, 0, 0, .05); + border-color: rgba(0, 0, 0, .05); + + &:hover { + .card-panel-icon-wrapper { + color: #fff; + } + + .icon-people { + background: #40c9c6; + } + + .icon-message { + background: #36a3f7; + } + + .icon-money { + background: #f4516c; + } + + .icon-shopping { + background: #34bfa3 + } + } + + .icon-people { + color: #40c9c6; + } + + .icon-message { + color: #36a3f7; + } + + .icon-money { + color: #f4516c; + } + + .icon-shopping { + color: #34bfa3 + } + + .card-panel-icon-wrapper { + float: left; + margin: 14px 0 0 14px; + padding: 16px; + transition: all 0.38s ease-out; + border-radius: 6px; + } + + .card-panel-icon { + float: left; + font-size: 48px; + } + + .card-panel-description { + float: right; + font-weight: bold; + margin: 26px; + margin-left: 0px; + + .card-panel-text { + line-height: 18px; + color: rgba(0, 0, 0, 0.45); + font-size: 16px; + margin-bottom: 12px; + } + + .card-panel-num { + font-size: 20px; + } + } + } +} + +@media (max-width:550px) { + .card-panel-description { + display: none; + } + + .card-panel-icon-wrapper { + float: none !important; + width: 100%; + height: 100%; + margin: 0 !important; + + .svg-icon { + display: block; + margin: 14px auto !important; + float: none !important; + } + } +} +</style> diff --git a/ruoyi-ui/src/views/dashboard/PieChart.vue b/ruoyi-ui/src/views/dashboard/PieChart.vue new file mode 100644 index 0000000..d0e4f50 --- /dev/null +++ b/ruoyi-ui/src/views/dashboard/PieChart.vue @@ -0,0 +1,79 @@ +<template> + <div :class="className" :style="{height:height,width:width}" /> +</template> + +<script> +import echarts from 'echarts' +require('echarts/theme/macarons') // echarts theme +import resize from './mixins/resize' + +export default { + mixins: [resize], + props: { + className: { + type: String, + default: 'chart' + }, + width: { + type: String, + default: '100%' + }, + height: { + type: String, + default: '300px' + } + }, + data() { + return { + chart: null + } + }, + mounted() { + this.$nextTick(() => { + this.initChart() + }) + }, + beforeDestroy() { + if (!this.chart) { + return + } + this.chart.dispose() + this.chart = null + }, + methods: { + initChart() { + this.chart = echarts.init(this.$el, 'macarons') + + this.chart.setOption({ + tooltip: { + trigger: 'item', + formatter: '{a} <br/>{b} : {c} ({d}%)' + }, + legend: { + left: 'center', + bottom: '10', + data: ['Industries', 'Technology', 'Forex', 'Gold', 'Forecasts'] + }, + series: [ + { + name: 'WEEKLY WRITE ARTICLES', + type: 'pie', + roseType: 'radius', + radius: [15, 95], + center: ['50%', '38%'], + data: [ + { value: 320, name: 'Industries' }, + { value: 240, name: 'Technology' }, + { value: 149, name: 'Forex' }, + { value: 100, name: 'Gold' }, + { value: 59, name: 'Forecasts' } + ], + animationEasing: 'cubicInOut', + animationDuration: 2600 + } + ] + }) + } + } +} +</script> diff --git a/ruoyi-ui/src/views/dashboard/RaddarChart.vue b/ruoyi-ui/src/views/dashboard/RaddarChart.vue new file mode 100644 index 0000000..9831abf --- /dev/null +++ b/ruoyi-ui/src/views/dashboard/RaddarChart.vue @@ -0,0 +1,116 @@ +<template> + <div :class="className" :style="{height:height,width:width}" /> +</template> + +<script> +import echarts from 'echarts' +require('echarts/theme/macarons') // echarts theme +import resize from './mixins/resize' + +const animationDuration = 3000 + +export default { + mixins: [resize], + props: { + className: { + type: String, + default: 'chart' + }, + width: { + type: String, + default: '100%' + }, + height: { + type: String, + default: '300px' + } + }, + data() { + return { + chart: null + } + }, + mounted() { + this.$nextTick(() => { + this.initChart() + }) + }, + beforeDestroy() { + if (!this.chart) { + return + } + this.chart.dispose() + this.chart = null + }, + methods: { + initChart() { + this.chart = echarts.init(this.$el, 'macarons') + + this.chart.setOption({ + tooltip: { + trigger: 'axis', + axisPointer: { // 坐标轴指示器,坐标轴触发有效 + type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' + } + }, + radar: { + radius: '66%', + center: ['50%', '42%'], + splitNumber: 8, + splitArea: { + areaStyle: { + color: 'rgba(127,95,132,.3)', + opacity: 1, + shadowBlur: 45, + shadowColor: 'rgba(0,0,0,.5)', + shadowOffsetX: 0, + shadowOffsetY: 15 + } + }, + indicator: [ + { name: 'Sales', max: 10000 }, + { name: 'Administration', max: 20000 }, + { name: 'Information Techology', max: 20000 }, + { name: 'Customer Support', max: 20000 }, + { name: 'Development', max: 20000 }, + { name: 'Marketing', max: 20000 } + ] + }, + legend: { + left: 'center', + bottom: '10', + data: ['Allocated Budget', 'Expected Spending', 'Actual Spending'] + }, + series: [{ + type: 'radar', + symbolSize: 0, + areaStyle: { + normal: { + shadowBlur: 13, + shadowColor: 'rgba(0,0,0,.2)', + shadowOffsetX: 0, + shadowOffsetY: 10, + opacity: 1 + } + }, + data: [ + { + value: [5000, 7000, 12000, 11000, 15000, 14000], + name: 'Allocated Budget' + }, + { + value: [4000, 9000, 15000, 15000, 13000, 11000], + name: 'Expected Spending' + }, + { + value: [5500, 11000, 12000, 15000, 12000, 12000], + name: 'Actual Spending' + } + ], + animationDuration: animationDuration + }] + }) + } + } +} +</script> diff --git a/ruoyi-ui/src/views/dashboard/mixins/resize.js b/ruoyi-ui/src/views/dashboard/mixins/resize.js new file mode 100644 index 0000000..5516794 --- /dev/null +++ b/ruoyi-ui/src/views/dashboard/mixins/resize.js @@ -0,0 +1,52 @@ +import { debounce } from '@/utils' + +export default { + data() { + return { + $_sidebarElm: null + } + }, + mounted() { + this.$_initResizeEvent() + this.$_initSidebarResizeEvent() + }, + beforeDestroy() { + this.$_destroyResizeEvent() + this.$_destroySidebarResizeEvent() + }, + activated() { + this.$_initResizeEvent() + this.$_initSidebarResizeEvent() + }, + deactivated() { + this.$_destroyResizeEvent() + this.$_destroySidebarResizeEvent() + }, + methods: { + $_resizeHandler() { + return debounce(() => { + if (this.chart) { + this.chart.resize() + } + }, 100)() + }, + $_initResizeEvent() { + window.addEventListener('resize', this.$_resizeHandler) + }, + $_destroyResizeEvent() { + window.removeEventListener('resize', this.$_resizeHandler) + }, + $_sidebarResizeHandler(e) { + if (e.propertyName === 'width') { + this.$_resizeHandler() + } + }, + $_initSidebarResizeEvent() { + this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0] + this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler) + }, + $_destroySidebarResizeEvent() { + this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler) + } + } +} diff --git a/ruoyi-ui/src/views/error/401.vue b/ruoyi-ui/src/views/error/401.vue new file mode 100644 index 0000000..8561150 --- /dev/null +++ b/ruoyi-ui/src/views/error/401.vue @@ -0,0 +1,99 @@ +<template> + <div class="errPage-container"> + <el-button icon="arrow-left" class="pan-back-btn" @click="back"> + 返回 + </el-button> + <el-row> + <el-col :span="12"> + <h1 class="text-jumbo text-ginormous"> + Oops! + </h1> + gif来源<a href="https://zh.airbnb.com/" target="_blank">airbnb</a> 页面 + <h2>你没有权限去该页面</h2> + <h6>如有不满请联系你领导</h6> + <ul class="list-unstyled"> + <li>或者你可以去:</li> + <li class="link-type"> + <router-link to="/dashboard"> + 回首页 + </router-link> + </li> + <li class="link-type"> + <a href="https://www.taobao.com/">随便看看</a> + </li> + <li><a href="#" @click.prevent="dialogVisible=true">点我看图</a></li> + </ul> + </el-col> + <el-col :span="12"> + <img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream."> + </el-col> + </el-row> + <el-dialog :visible.sync="dialogVisible" title="随便看"> + <img :src="ewizardClap" class="pan-img"> + </el-dialog> + </div> +</template> + +<script> +import errGif from '@/assets/401_images/401.gif' + +export default { + name: 'Page401', + data() { + return { + errGif: errGif + '?' + +new Date(), + ewizardClap: 'https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646', + dialogVisible: false + } + }, + methods: { + back() { + if (this.$route.query.noGoBack) { + this.$router.push({ path: '/dashboard' }) + } else { + this.$router.go(-1) + } + } + } +} +</script> + +<style lang="scss" scoped> + .errPage-container { + width: 800px; + max-width: 100%; + margin: 100px auto; + .pan-back-btn { + background: #008489; + color: #fff; + border: none!important; + } + .pan-gif { + margin: 0 auto; + display: block; + } + .pan-img { + display: block; + margin: 0 auto; + width: 100%; + } + .text-jumbo { + font-size: 60px; + font-weight: 700; + color: #484848; + } + .list-unstyled { + font-size: 14px; + li { + padding-bottom: 5px; + } + a { + color: #008489; + text-decoration: none; + &:hover { + text-decoration: underline; + } + } + } + } +</style> diff --git a/ruoyi-ui/src/views/error/404.vue b/ruoyi-ui/src/views/error/404.vue new file mode 100644 index 0000000..c21ebce --- /dev/null +++ b/ruoyi-ui/src/views/error/404.vue @@ -0,0 +1,228 @@ +<template> + <div class="wscn-http404-container"> + <div class="wscn-http404"> + <div class="pic-404"> + <img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404"> + <img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404"> + <img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404"> + <img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404"> + </div> + <div class="bullshit"> + <div class="bullshit__oops">OOPS!</div> + <div class="bullshit__info">All rights reserved + <a style="color:#20a0ff" href="https://wallstreetcn.com" target="_blank">wallstreetcn</a> + </div> + <div class="bullshit__headline">{{ message }}</div> + <div class="bullshit__info">Please check that the URL you entered is correct, or click the button below to return to the homepage.</div> + <a href="" class="bullshit__return-home">Back to home</a> + </div> + </div> + </div> +</template> + +<script> + +export default { + name: 'Page404', + computed: { + message() { + return 'The webmaster said that you can not enter this page...' + } + } +} +</script> + +<style lang="scss" scoped> +.wscn-http404-container{ + transform: translate(-50%,-50%); + position: absolute; + top: 40%; + left: 50%; +} +.wscn-http404 { + position: relative; + width: 1200px; + padding: 0 50px; + overflow: hidden; + .pic-404 { + position: relative; + float: left; + width: 600px; + overflow: hidden; + &__parent { + width: 100%; + } + &__child { + position: absolute; + &.left { + width: 80px; + top: 17px; + left: 220px; + opacity: 0; + animation-name: cloudLeft; + animation-duration: 2s; + animation-timing-function: linear; + animation-fill-mode: forwards; + animation-delay: 1s; + } + &.mid { + width: 46px; + top: 10px; + left: 420px; + opacity: 0; + animation-name: cloudMid; + animation-duration: 2s; + animation-timing-function: linear; + animation-fill-mode: forwards; + animation-delay: 1.2s; + } + &.right { + width: 62px; + top: 100px; + left: 500px; + opacity: 0; + animation-name: cloudRight; + animation-duration: 2s; + animation-timing-function: linear; + animation-fill-mode: forwards; + animation-delay: 1s; + } + @keyframes cloudLeft { + 0% { + top: 17px; + left: 220px; + opacity: 0; + } + 20% { + top: 33px; + left: 188px; + opacity: 1; + } + 80% { + top: 81px; + left: 92px; + opacity: 1; + } + 100% { + top: 97px; + left: 60px; + opacity: 0; + } + } + @keyframes cloudMid { + 0% { + top: 10px; + left: 420px; + opacity: 0; + } + 20% { + top: 40px; + left: 360px; + opacity: 1; + } + 70% { + top: 130px; + left: 180px; + opacity: 1; + } + 100% { + top: 160px; + left: 120px; + opacity: 0; + } + } + @keyframes cloudRight { + 0% { + top: 100px; + left: 500px; + opacity: 0; + } + 20% { + top: 120px; + left: 460px; + opacity: 1; + } + 80% { + top: 180px; + left: 340px; + opacity: 1; + } + 100% { + top: 200px; + left: 300px; + opacity: 0; + } + } + } + } + .bullshit { + position: relative; + float: left; + width: 300px; + padding: 30px 0; + overflow: hidden; + &__oops { + font-size: 32px; + font-weight: bold; + line-height: 40px; + color: #1482f0; + opacity: 0; + margin-bottom: 20px; + animation-name: slideUp; + animation-duration: 0.5s; + animation-fill-mode: forwards; + } + &__headline { + font-size: 20px; + line-height: 24px; + color: #222; + font-weight: bold; + opacity: 0; + margin-bottom: 10px; + animation-name: slideUp; + animation-duration: 0.5s; + animation-delay: 0.1s; + animation-fill-mode: forwards; + } + &__info { + font-size: 13px; + line-height: 21px; + color: grey; + opacity: 0; + margin-bottom: 30px; + animation-name: slideUp; + animation-duration: 0.5s; + animation-delay: 0.2s; + animation-fill-mode: forwards; + } + &__return-home { + display: block; + float: left; + width: 110px; + height: 36px; + background: #1482f0; + border-radius: 100px; + text-align: center; + color: #ffffff; + opacity: 0; + font-size: 14px; + line-height: 36px; + cursor: pointer; + animation-name: slideUp; + animation-duration: 0.5s; + animation-delay: 0.3s; + animation-fill-mode: forwards; + } + @keyframes slideUp { + 0% { + transform: translateY(60px); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } + } + } +} +</style> diff --git a/ruoyi-ui/src/views/index.vue b/ruoyi-ui/src/views/index.vue new file mode 100644 index 0000000..4828d88 --- /dev/null +++ b/ruoyi-ui/src/views/index.vue @@ -0,0 +1,98 @@ +<template> + <div class="dashboard-editor-container"> + + <panel-group @handleSetLineChartData="handleSetLineChartData" /> + + <el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;"> + <line-chart :chart-data="lineChartData" /> + </el-row> + + <el-row :gutter="32"> + <el-col :xs="24" :sm="24" :lg="8"> + <div class="chart-wrapper"> + <raddar-chart /> + </div> + </el-col> + <el-col :xs="24" :sm="24" :lg="8"> + <div class="chart-wrapper"> + <pie-chart /> + </div> + </el-col> + <el-col :xs="24" :sm="24" :lg="8"> + <div class="chart-wrapper"> + <bar-chart /> + </div> + </el-col> + </el-row> + + + </div> +</template> + +<script> +import PanelGroup from './dashboard/PanelGroup' +import LineChart from './dashboard/LineChart' +import RaddarChart from './dashboard/RaddarChart' +import PieChart from './dashboard/PieChart' +import BarChart from './dashboard/BarChart' + +const lineChartData = { + newVisitis: { + expectedData: [100, 120, 161, 134, 105, 160, 165], + actualData: [120, 82, 91, 154, 162, 140, 145] + }, + messages: { + expectedData: [200, 192, 120, 144, 160, 130, 140], + actualData: [180, 160, 151, 106, 145, 150, 130] + }, + purchases: { + expectedData: [80, 100, 121, 104, 105, 90, 100], + actualData: [120, 90, 100, 138, 142, 130, 130] + }, + shoppings: { + expectedData: [130, 140, 141, 142, 145, 150, 160], + actualData: [120, 82, 91, 154, 162, 140, 130] + } +} + +export default { + name: 'Index', + components: { + PanelGroup, + LineChart, + RaddarChart, + PieChart, + BarChart + }, + data() { + return { + lineChartData: lineChartData.newVisitis + } + }, + methods: { + handleSetLineChartData(type) { + this.lineChartData = lineChartData[type] + } + } +} +</script> + +<style lang="scss" scoped> +.dashboard-editor-container { + padding: 32px; + background-color: rgb(240, 242, 245); + position: relative; + + .chart-wrapper { + background: #fff; + padding: 16px 16px 0; + margin-bottom: 32px; + } +} + +@media (max-width:1024px) { + .chart-wrapper { + padding: 8px; + } +} +</style> diff --git a/ruoyi-ui/src/views/login.vue b/ruoyi-ui/src/views/login.vue new file mode 100644 index 0000000..880af8d --- /dev/null +++ b/ruoyi-ui/src/views/login.vue @@ -0,0 +1,203 @@ +<template> + <div class="login"> + <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form"> + <h3 class="title">若依后台管理系统</h3> + <el-form-item prop="username"> + <el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="账号"> + <svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" /> + </el-input> + </el-form-item> + <el-form-item prop="password"> + <el-input + v-model="loginForm.password" + type="password" + auto-complete="off" + placeholder="密码" + @keyup.enter.native="handleLogin" + > + <svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" /> + </el-input> + </el-form-item> + <el-form-item prop="code"> + <el-input + v-model="loginForm.code" + auto-complete="off" + placeholder="验证码" + style="width: 63%" + @keyup.enter.native="handleLogin" + > + <svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" /> + </el-input> + <div class="login-code"> + <img :src="codeUrl" @click="getCode" /> + </div> + </el-form-item> + <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox> + <el-form-item style="width:100%;"> + <el-button + :loading="loading" + size="medium" + type="primary" + style="width:100%;" + @click.native.prevent="handleLogin" + > + <span v-if="!loading">登 录</span> + <span v-else>登 录 中...</span> + </el-button> + </el-form-item> + </el-form> + <!-- 底部 --> + <div class="el-login-footer"> + <span>Copyright © 2018-2019 ruoyi.vip All Rights Reserved.</span> + </div> + </div> +</template> + +<script> +import { getCodeImg } from "@/api/login"; +import Cookies from "js-cookie"; + +export default { + name: "Login", + data() { + return { + codeUrl: "", + cookiePassword: "", + loginForm: { + username: "admin", + password: "admin123", + rememberMe: false, + code: "", + uuid: "" + }, + loginRules: { + username: [ + { required: true, trigger: "blur", message: "用户名不能为空" } + ], + password: [ + { required: true, trigger: "blur", message: "密码不能为空" } + ], + code: [{ required: true, trigger: "change", message: "验证码不能为空" }] + }, + loading: false, + redirect: undefined + }; + }, + watch: { + $route: { + handler: function(route) { + this.redirect = route.query && route.query.redirect; + }, + immediate: true + } + }, + created() { + this.getCode(); + this.getCookie(); + }, + methods: { + getCode() { + getCodeImg().then(res => { + this.codeUrl = "data:image/gif;base64," + res.img; + this.loginForm.uuid = res.uuid; + }); + }, + getCookie() { + const username = Cookies.get("username"); + const password = Cookies.get("password"); + const rememberMe = Cookies.get('rememberMe') + this.loginForm = { + username: username === undefined ? this.loginForm.username : username, + password: password === undefined ? this.loginForm.password : password, + rememberMe: rememberMe === undefined ? false : Boolean(rememberMe) + }; + }, + handleLogin() { + this.$refs.loginForm.validate(valid => { + if (valid) { + this.loading = true; + if (this.loginForm.rememberMe) { + Cookies.set("username", this.loginForm.username, { expires: 30 }); + Cookies.set("password", this.loginForm.password, { expires: 30 }); + Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 }); + } else { + Cookies.remove("username"); + Cookies.remove("password"); + Cookies.remove('rememberMe'); + } + this.$store + .dispatch("Login", this.loginForm) + .then(() => { + this.loading = false; + this.$router.push({ path: this.redirect || "/" }); + }) + .catch(() => { + this.loading = false; + this.getCode(); + }); + } + }); + } + } +}; +</script> + +<style rel="stylesheet/scss" lang="scss"> +.login { + display: flex; + justify-content: center; + align-items: center; + height: 100%; + background-image: url("../assets/image/login-background.jpg"); + background-size: cover; +} +.title { + margin: 0px auto 30px auto; + text-align: center; + color: #707070; +} + +.login-form { + border-radius: 6px; + background: #ffffff; + width: 400px; + padding: 25px 25px 5px 25px; + .el-input { + height: 38px; + input { + height: 38px; + } + } + .input-icon { + height: 39px; + width: 14px; + margin-left: 2px; + } +} +.login-tip { + font-size: 13px; + text-align: center; + color: #bfbfbf; +} +.login-code { + width: 33%; + height: 38px; + float: right; + img { + cursor: pointer; + vertical-align: middle; + } +} +.el-login-footer { + height: 40px; + line-height: 40px; + position: fixed; + bottom: 0; + width: 100%; + text-align: center; + color: #fff; + font-family: Arial; + font-size: 12px; + letter-spacing: 1px; +} +</style> diff --git a/ruoyi-ui/src/views/login/auth-redirect.vue b/ruoyi-ui/src/views/login/auth-redirect.vue new file mode 100644 index 0000000..de7bbf3 --- /dev/null +++ b/ruoyi-ui/src/views/login/auth-redirect.vue @@ -0,0 +1,15 @@ +<script> +export default { + name: 'AuthRedirect', + created() { + const hash = window.location.search.slice(1) + if (window.localStorage) { + window.localStorage.setItem('x-admin-oauth-code', hash) + window.close() + } + }, + render: function(h) { + return h() // avoid warning message + } +} +</script> diff --git a/ruoyi-ui/src/views/login/components/SocialSignin.vue b/ruoyi-ui/src/views/login/components/SocialSignin.vue new file mode 100644 index 0000000..17537ed --- /dev/null +++ b/ruoyi-ui/src/views/login/components/SocialSignin.vue @@ -0,0 +1,72 @@ +<template> + <div class="social-signup-container"> + <div class="sign-btn" @click="wechatHandleClick('wechat')"> + <span class="wx-svg-container"><svg-icon icon-class="wechat" class="icon" /></span> + WeChat + </div> + <div class="sign-btn" @click="tencentHandleClick('tencent')"> + <span class="qq-svg-container"><svg-icon icon-class="qq" class="icon" /></span> + QQ + </div> + </div> +</template> + +<script> +// import openWindow from '@/utils/open-window' + +export default { + name: 'SocialSignin', + methods: { + wechatHandleClick(thirdpart) { + alert('ok') + // this.$store.commit('SET_AUTH_TYPE', thirdpart) + // const appid = 'xxxxx' + // const redirect_uri = encodeURIComponent('xxx/redirect?redirect=' + window.location.origin + '/auth-redirect') + // const url = 'https://open.weixin.qq.com/connect/qrconnect?appid=' + appid + '&redirect_uri=' + redirect_uri + '&response_type=code&scope=snsapi_login#wechat_redirect' + // openWindow(url, thirdpart, 540, 540) + }, + tencentHandleClick(thirdpart) { + alert('ok') + // this.$store.commit('SET_AUTH_TYPE', thirdpart) + // const client_id = 'xxxxx' + // const redirect_uri = encodeURIComponent('xxx/redirect?redirect=' + window.location.origin + '/auth-redirect') + // const url = 'https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=' + client_id + '&redirect_uri=' + redirect_uri + // openWindow(url, thirdpart, 540, 540) + } + } +} +</script> + +<style lang="scss" scoped> + .social-signup-container { + margin: 20px 0; + .sign-btn { + display: inline-block; + cursor: pointer; + } + .icon { + color: #fff; + font-size: 24px; + margin-top: 8px; + } + .wx-svg-container, + .qq-svg-container { + display: inline-block; + width: 40px; + height: 40px; + line-height: 40px; + text-align: center; + padding-top: 1px; + border-radius: 4px; + margin-bottom: 20px; + margin-right: 5px; + } + .wx-svg-container { + background-color: #24da70; + } + .qq-svg-container { + background-color: #6BA2D6; + margin-left: 50px; + } + } +</style> diff --git a/ruoyi-ui/src/views/monitor/druid/index.vue b/ruoyi-ui/src/views/monitor/druid/index.vue new file mode 100644 index 0000000..6ab35b2 --- /dev/null +++ b/ruoyi-ui/src/views/monitor/druid/index.vue @@ -0,0 +1,25 @@ +<template> + <div v-loading="loading" :style="'height:'+ height"> + <iframe :src="src" frameborder="no" style="width: 100%;height: 100%" scrolling="auto" /> + </div> +</template> +<script> +export default { + data() { + return { + src: process.env.VUE_APP_BASE_API + "/druid/index.html", + height: document.documentElement.clientHeight - 94.5 + "px;", + loading: true + }; + }, + mounted: function() { + setTimeout(() => { + this.loading = false; + }, 230); + const that = this; + window.onresize = function temp() { + that.height = document.documentElement.clientHeight - 94.5 + "px;"; + }; + } +}; +</script> diff --git a/ruoyi-ui/src/views/monitor/job/index.vue b/ruoyi-ui/src/views/monitor/job/index.vue new file mode 100644 index 0000000..2467772 --- /dev/null +++ b/ruoyi-ui/src/views/monitor/job/index.vue @@ -0,0 +1,5 @@ +<template> + <div class="app-container"> + 定时任务 + </div> +</template> \ No newline at end of file diff --git a/ruoyi-ui/src/views/monitor/logininfor/index.vue b/ruoyi-ui/src/views/monitor/logininfor/index.vue new file mode 100644 index 0000000..7bc42aa --- /dev/null +++ b/ruoyi-ui/src/views/monitor/logininfor/index.vue @@ -0,0 +1,138 @@ +<template> + <div class="app-container"> + <el-form :inline="true" label-width="68px"> + <el-form-item label="登录地址"> + <el-input + v-model="queryParams.ipaddr" + placeholder="请输入登录地址" + clearable + style="width: 240px;" + size="small" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="用户名称"> + <el-input + v-model="queryParams.userName" + placeholder="请输入用户名称" + clearable + style="width: 240px;" + size="small" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="状态"> + <el-select + v-model="queryParams.status" + placeholder="登录状态" + clearable + size="small" + style="width: 240px" + > + <el-option + v-for="dict in statusOptions" + :key="dict.dictValue" + :label="dict.dictLabel" + :value="dict.dictValue" + /> + </el-select> + </el-form-item> + <el-form-item label="登录时间"> + <el-date-picker + v-model="dateRange" + size="small" + style="width: 240px" + value-format="yyyy-MM-dd" + type="daterange" + range-separator="-" + start-placeholder="开始日期" + end-placeholder="结束日期" + ></el-date-picker> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> + </el-form-item> + </el-form> + + <el-table v-loading="loading" :data="list" style="width: 100%;"> + <el-table-column label="访问编号" align="center" prop="infoId" /> + <el-table-column label="用户名称" align="center" prop="userName" /> + <el-table-column label="登录地址" align="center" prop="ipaddr" width="130" :show-overflow-tooltip="true" /> + <el-table-column label="登录地点" align="center" prop="loginLocation" /> + <el-table-column label="浏览器" align="center" prop="browser" /> + <el-table-column label="操作系统" align="center" prop="os" /> + <el-table-column label="登录状态" align="center" prop="status" :formatter="statusFormat" /> + <el-table-column label="操作信息" align="center" prop="msg" /> + <el-table-column label="登录日期" align="center" prop="loginTime" width="180"> + <template slot-scope="scope"> + <span>{{ dateFormat(scope.row.loginTime) }}</span> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total>0" + :total="total" + :page.sync="queryParams.pageNum" + :limit.sync="queryParams.pageSize" + @pagination="getList" + /> + </div> +</template> + +<script> +import { list } from "@/api/monitor/logininfor"; + +export default { + data() { + return { + // 遮罩层 + loading: true, + // 总条数 + total: 0, + // 表格数据 + list: [], + // 状态数据字典 + statusOptions: [], + // 日期范围 + dateRange: [], + // 查询参数 + queryParams: { + pageNum: 1, + pageSize: 10, + ipaddr: undefined, + userName: undefined, + status: undefined + } + }; + }, + created() { + this.getList(); + this.getDicts("sys_common_status").then(response => { + this.statusOptions = response.data; + }); + }, + methods: { + /** 查询登录日志列表 */ + getList() { + this.loading = true; + list(this.addDateRange(this.queryParams, this.dateRange)).then(response => { + this.list = response.rows; + this.total = response.total; + this.loading = false; + } + ); + }, + // 登录状态字典翻译 + statusFormat(row, column) { + return this.selectDictLabel(this.statusOptions, row.status); + }, + /** 搜索按钮操作 */ + handleQuery() { + this.queryParams.pageNum = 1; + this.getList(); + } + } +}; +</script> + diff --git a/ruoyi-ui/src/views/monitor/online/index.vue b/ruoyi-ui/src/views/monitor/online/index.vue new file mode 100644 index 0000000..764613a --- /dev/null +++ b/ruoyi-ui/src/views/monitor/online/index.vue @@ -0,0 +1,5 @@ +<template> + <div class="app-container"> + 在线用户 + </div> +</template> \ No newline at end of file diff --git a/ruoyi-ui/src/views/monitor/operlog/index.vue b/ruoyi-ui/src/views/monitor/operlog/index.vue new file mode 100644 index 0000000..b7d4f99 --- /dev/null +++ b/ruoyi-ui/src/views/monitor/operlog/index.vue @@ -0,0 +1,226 @@ +<template> + <div class="app-container"> + <el-form :inline="true" label-width="68px"> + <el-form-item label="系统模块"> + <el-input + v-model="queryParams.title" + placeholder="请输入系统模块" + clearable + style="width: 240px;" + size="small" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="操作人员"> + <el-input + v-model="queryParams.operName" + placeholder="请输入操作人员" + clearable + style="width: 240px;" + size="small" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="类型"> + <el-select + v-model="queryParams.businessType" + placeholder="操作类型" + clearable + size="small" + style="width: 240px" + > + <el-option + v-for="dict in typeOptions" + :key="dict.dictValue" + :label="dict.dictLabel" + :value="dict.dictValue" + /> + </el-select> + </el-form-item> + <el-form-item label="状态"> + <el-select + v-model="queryParams.status" + placeholder="操作状态" + clearable + size="small" + style="width: 240px" + > + <el-option + v-for="dict in statusOptions" + :key="dict.dictValue" + :label="dict.dictLabel" + :value="dict.dictValue" + /> + </el-select> + </el-form-item> + <el-form-item label="操作时间"> + <el-date-picker + v-model="dateRange" + size="small" + style="width: 240px" + value-format="yyyy-MM-dd" + type="daterange" + range-separator="-" + start-placeholder="开始日期" + end-placeholder="结束日期" + ></el-date-picker> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> + </el-form-item> + </el-form> + + <el-table v-loading="loading" :data="list" style="width: 100%;"> + <el-table-column label="日志编号" align="center" prop="operId" /> + <el-table-column label="系统模块" align="center" prop="title" /> + <el-table-column label="操作类型" align="center" prop="businessType" :formatter="typeFormat" /> + <el-table-column label="请求方式" align="center" prop="requestMethod" /> + <el-table-column label="操作人员" align="center" prop="operName" /> + <el-table-column label="主机" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" /> + <el-table-column label="操作地点" align="center" prop="operLocation" /> + <el-table-column label="操作状态" align="center" prop="status" :formatter="statusFormat" /> + <el-table-column label="操作日期" align="center" prop="operTime" width="180"> + <template slot-scope="scope"> + <span>{{ dateFormat(scope.row.operTime) }}</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-view" + @click="handleView(scope.row,scope.index)" + v-hasPermi="['monitor:operlog:query']" + >详细</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" + /> + + <!-- 操作日志详细 --> + <el-dialog title="操作日志详细" :visible.sync="open" width="700px"> + <el-form ref="form" :model="form" label-width="100px" size="mini"> + <el-row> + <el-col :span="12"> + <el-form-item label="操作模块:">{{ form.title }} / 修改</el-form-item> + <el-form-item + label="登录信息:" + >{{ form.operName }} / {{ form.operIp }} / {{ form.operLocation }}</el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="请求地址:">{{ form.operUrl }}</el-form-item> + <el-form-item label="请求方式:">{{ form.requestMethod }}</el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="操作方法:">{{ form.method }}</el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="请求参数:">{{ form.operParam }}</el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="返回参数:">{{ form.jsonResult }}</el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="操作状态:"> + <div v-if="form.status === 0">正常</div> + <div v-else-if="form.status === 1">失败</div> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="操作时间:">{{ dateFormat(form.operTime) }}</el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="异常信息:" v-if="form.status === 1">{{ form.errorMsg }}</el-form-item> + </el-col> + </el-row> + </el-form> + <div slot="footer" class="dialog-footer"> + <el-button @click="open = false">关 闭</el-button> + </div> + </el-dialog> + </div> +</template> + +<script> +import { list } from "@/api/monitor/operlog"; + +export default { + data() { + return { + // 遮罩层 + loading: true, + // 总条数 + total: 0, + // 表格数据 + list: [], + // 是否显示弹出层 + open: false, + // 类型数据字典 + typeOptions: [], + // 类型数据字典 + statusOptions: [], + // 日期范围 + dateRange: [], + // 表单参数 + form: {}, + // 查询参数 + queryParams: { + pageNum: 1, + pageSize: 10, + title: undefined, + operName: undefined, + businessType: undefined, + status: undefined + } + }; + }, + created() { + this.getList(); + this.getDicts("sys_oper_type").then(response => { + this.typeOptions = response.data; + }); + this.getDicts("sys_common_status").then(response => { + this.statusOptions = response.data; + }); + }, + methods: { + /** 查询登录日志 */ + getList() { + this.loading = true; + list(this.addDateRange(this.queryParams, this.dateRange)).then( response => { + this.list = response.rows; + this.total = response.total; + this.loading = false; + } + ); + }, + // 操作日志状态字典翻译 + statusFormat(row, column) { + return this.selectDictLabel(this.statusOptions, row.status); + }, + // 操作日志类型字典翻译 + typeFormat(row, column) { + return this.selectDictLabel(this.typeOptions, row.businessType); + }, + /** 搜索按钮操作 */ + handleQuery() { + this.queryParams.pageNum = 1; + this.getList(); + }, + /** 详细按钮操作 */ + handleView(row) { + this.open = true; + this.form = row; + } + } +}; +</script> + diff --git a/ruoyi-ui/src/views/monitor/server/index.vue b/ruoyi-ui/src/views/monitor/server/index.vue new file mode 100644 index 0000000..36c95e3 --- /dev/null +++ b/ruoyi-ui/src/views/monitor/server/index.vue @@ -0,0 +1,209 @@ +<template> + <div class="app-container"> + <el-row> + <el-col :span="12" class="card-box"> + <el-card> + <div slot="header"><span>CPU</span></div> + <div class="el-table el-table--enable-row-hover el-table--medium"> + <table cellspacing="0" style="width: 100%;"> + <thead> + <tr> + <th class="is-leaf"><div class="cell">属性</div></th> + <th class="is-leaf"><div class="cell">值</div></th> + </tr> + </thead> + <tbody> + <tr> + <td><div class="cell">核心数</div></td> + <td><div class="cell" v-if="server.cpu">{{ server.cpu.cpuNum }}</div></td> + </tr> + <tr> + <td><div class="cell">用户使用率</div></td> + <td><div class="cell" v-if="server.cpu">{{ server.cpu.used }}%</div></td> + </tr> + <tr> + <td><div class="cell">系统使用率</div></td> + <td><div class="cell" v-if="server.cpu">{{ server.cpu.sys }}%</div></td> + </tr> + <tr> + <td><div class="cell">当前空闲率</div></td> + <td><div class="cell" v-if="server.cpu">{{ server.cpu.free }}%</div></td> + </tr> + </tbody> + </table> + </div> + </el-card> + </el-col> + + <el-col :span="12" class="card-box"> + <el-card> + <div slot="header"><span>内存</span></div> + <div class="el-table el-table--enable-row-hover el-table--medium"> + <table cellspacing="0" style="width: 100%;"> + <thead> + <tr> + <th class="is-leaf"><div class="cell">属性</div></th> + <th class="is-leaf"><div class="cell">内存</div></th> + <th class="is-leaf"><div class="cell">JVM</div></th> + </tr> + </thead> + <tbody> + <tr> + <td><div class="cell">总内存</div></td> + <td><div class="cell" v-if="server.mem">{{ server.mem.total }}G</div></td> + <td><div class="cell" v-if="server.jvm">{{ server.jvm.total }}M</div></td> + </tr> + <tr> + <td><div class="cell">已用内存</div></td> + <td><div class="cell" v-if="server.mem">{{ server.mem.used}}G</div></td> + <td><div class="cell" v-if="server.jvm">{{ server.jvm.used}}M</div></td> + </tr> + <tr> + <td><div class="cell">剩余内存</div></td> + <td><div class="cell" v-if="server.mem">{{ server.mem.free }}G</div></td> + <td><div class="cell" v-if="server.jvm">{{ server.jvm.free }}M</div></td> + </tr> + <tr> + <td><div class="cell">使用率</div></td> + <td><div class="cell" v-if="server.mem" :class="{'text-danger': server.mem.usage > 80}">{{ server.mem.usage }}%</div></td> + <td><div class="cell" v-if="server.jvm" :class="{'text-danger': server.jvm.usage > 80}">{{ server.jvm.usage }}%</div></td> + </tr> + </tbody> + </table> + </div> + </el-card> + </el-col> + + <el-col :span="24" class="card-box"> + <el-card> + <div slot="header"> + <span>服务器信息</span> + </div> + <div class="el-table el-table--enable-row-hover el-table--medium"> + <table cellspacing="0" style="width: 100%;"> + <tbody> + <tr> + <td><div class="cell">服务器名称</div></td> + <td><div class="cell" v-if="server.sys">{{ server.sys.computerName }}</div></td> + <td><div class="cell">操作系统</div></td> + <td><div class="cell" v-if="server.sys">{{ server.sys.osName }}</div></td> + </tr> + <tr> + <td><div class="cell">服务器IP</div></td> + <td><div class="cell" v-if="server.sys">{{ server.sys.computerIp }}</div></td> + <td><div class="cell">系统架构</div></td> + <td><div class="cell" v-if="server.sys">{{ server.sys.osArch }}</div></td> + </tr> + </tbody> + </table> + </div> + </el-card> + </el-col> + + <el-col :span="24" class="card-box"> + <el-card> + <div slot="header"> + <span>Java虚拟机信息</span> + </div> + <div class="el-table el-table--enable-row-hover el-table--medium"> + <table cellspacing="0" style="width: 100%;"> + <tbody> + <tr> + <td><div class="cell">Java名称</div></td> + <td><div class="cell" v-if="server.jvm">{{ server.jvm.name }}</div></td> + <td><div class="cell">Java版本</div></td> + <td><div class="cell" v-if="server.jvm">{{ server.jvm.version }}</div></td> + </tr> + <tr> + <td><div class="cell">启动时间</div></td> + <td><div class="cell" v-if="server.jvm">{{ server.jvm.startTime }}</div></td> + <td><div class="cell">运行时长</div></td> + <td><div class="cell" v-if="server.jvm">{{ server.jvm.runTime }}</div></td> + </tr> + <tr> + <td colspan="1"><div class="cell">安装路径</div></td> + <td colspan="3"><div class="cell" v-if="server.jvm">{{ server.jvm.home }}</div></td> + </tr> + <tr> + <td colspan="1"><div class="cell">项目路径</div></td> + <td colspan="3"><div class="cell" v-if="server.sys">{{ server.sys.userDir }}</div></td> + </tr> + </tbody> + </table> + </div> + </el-card> + </el-col> + + <el-col :span="24" class="card-box"> + <el-card> + <div slot="header"> + <span>磁盘状态</span> + </div> + <div class="el-table el-table--enable-row-hover el-table--medium"> + <table cellspacing="0" style="width: 100%;"> + <thead> + <tr> + <th class="is-leaf"><div class="cell">盘符路径</div></th> + <th class="is-leaf"><div class="cell">文件系统</div></th> + <th class="is-leaf"><div class="cell">盘符类型</div></th> + <th class="is-leaf"><div class="cell">总大小</div></th> + <th class="is-leaf"><div class="cell">可用大小</div></th> + <th class="is-leaf"><div class="cell">已用大小</div></th> + <th class="is-leaf"><div class="cell">已用百分比</div></th> + </tr> + </thead> + <tbody v-if="server.sysFiles"> + <tr v-for="sysFile in server.sysFiles"> + <td><div class="cell">{{ sysFile.dirName }}</div></td> + <td><div class="cell">{{ sysFile.sysTypeName }}</div></td> + <td><div class="cell">{{ sysFile.typeName }}</div></td> + <td><div class="cell">{{ sysFile.total }}</div></td> + <td><div class="cell">{{ sysFile.free }}</div></td> + <td><div class="cell">{{ sysFile.used }}</div></td> + <td><div class="cell" :class="{'text-danger': sysFile.usage > 80}">{{ sysFile.usage }}%</div></td> + </tr> + </tbody> + </table> + </div> + </el-card> + </el-col> + </el-row> + </div> +</template> + +<script> +import { getServer } from "@/api/monitor/server"; + +export default { + data() { + return { + // 加载层信息 + loading: [], + // 服务器信息 + server: [] + }; + }, + created() { + this.getList(); + this.openLoading(); + }, + methods: { + /** 查询服务器信息 */ + getList() { + getServer().then(response => { + this.server = response.data; + this.loading.close(); + }); + }, + // 打开加载层 + openLoading() { + this.loading = this.$loading({ + lock: true, + text: "拼命读取中", + spinner: "el-icon-loading", + background: "rgba(0, 0, 0, 0.7)" + }); + } + } +}; +</script> \ No newline at end of file diff --git a/ruoyi-ui/src/views/redirect.vue b/ruoyi-ui/src/views/redirect.vue new file mode 100644 index 0000000..6a62cb7 --- /dev/null +++ b/ruoyi-ui/src/views/redirect.vue @@ -0,0 +1,12 @@ +<script> +export default { + created() { + const { params, query } = this.$route + const { path } = params + this.$router.replace({ path: '/' + path, query }) + }, + render: function(h) { + return h() // avoid warning message + } +} +</script> diff --git a/ruoyi-ui/src/views/system/config/index.vue b/ruoyi-ui/src/views/system/config/index.vue new file mode 100644 index 0000000..5e0046d --- /dev/null +++ b/ruoyi-ui/src/views/system/config/index.vue @@ -0,0 +1,270 @@ +<template> + <div class="app-container"> + <el-form :inline="true" label-width="68px"> + <el-form-item label="参数名称"> + <el-input + v-model="queryParams.configName" + placeholder="请输入参数名称" + clearable + size="small" + style="width: 240px" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="参数键名"> + <el-input + v-model="queryParams.configKey" + placeholder="请输入参数键名" + clearable + size="small" + style="width: 240px" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="系统内置"> + <el-select v-model="queryParams.configType" placeholder="系统内置" clearable size="small"> + <el-option + v-for="dict in typeOptions" + :key="dict.dictValue" + :label="dict.dictLabel" + :value="dict.dictValue" + /> + </el-select> + </el-form-item> + <el-form-item label="创建时间"> + <el-date-picker + v-model="dateRange" + size="small" + style="width: 240px" + value-format="yyyy-MM-dd" + type="daterange" + range-separator="-" + start-placeholder="开始日期" + end-placeholder="结束日期" + ></el-date-picker> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> + <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['system:config:add']">新增</el-button> + </el-form-item> + </el-form> + + <el-table v-loading="loading" :data="configList"> + <el-table-column label="参数主键" align="center" prop="configId" /> + <el-table-column label="参数名称" align="center" prop="configName" :show-overflow-tooltip="true" /> + <el-table-column label="参数键名" align="center" prop="configKey" :show-overflow-tooltip="true" /> + <el-table-column label="参数键值" align="center" prop="configValue" /> + <el-table-column label="系统内置" align="center" prop="configType" :formatter="typeFormat" /> + <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"> + <span>{{ dateFormat(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-edit" + @click="handleUpdate(scope.row)" + v-hasPermi="['system:config:edit']" + >修改</el-button> + <el-button + size="mini" + type="text" + icon="el-icon-delete" + @click="handleDelete(scope.row)" + v-hasPermi="['system:config: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" + /> + + <!-- 添加或修改参数配置对话框 --> + <el-dialog :title="title" :visible.sync="open" width="500px"> + <el-form ref="form" :model="form" :rules="rules" label-width="80px"> + <el-form-item label="参数名称" prop="configName"> + <el-input v-model="form.configName" placeholder="请输入参数名称" /> + </el-form-item> + <el-form-item label="参数键名" prop="configKey"> + <el-input v-model="form.configKey" placeholder="请输入参数键名" /> + </el-form-item> + <el-form-item label="参数键值" prop="configValue"> + <el-input v-model="form.configValue" placeholder="请输入参数键值" /> + </el-form-item> + <el-form-item label="系统内置" prop="configType"> + <el-radio-group v-model="form.configType"> + <el-radio + v-for="dict in typeOptions" + :key="dict.dictValue" + :label="dict.dictValue" + >{{dict.dictLabel}}</el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="备注" prop="remark"> + <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" /> + </el-form-item> + </el-form> + <div slot="footer" class="dialog-footer"> + <el-button type="primary" @click="submitForm">确 定</el-button> + <el-button @click="cancel">取 消</el-button> + </div> + </el-dialog> + </div> +</template> + +<script> +import { listConfig, getConfig, delConfig, addConfig, updateConfig } from "@/api/system/config"; + +export default { + data() { + return { + // 遮罩层 + loading: true, + // 总条数 + total: 0, + // 参数表格数据 + configList: [], + // 弹出层标题 + title: "", + // 是否显示弹出层 + open: false, + // 类型数据字典 + typeOptions: [], + // 日期范围 + dateRange: [], + // 查询参数 + queryParams: { + pageNum: 1, + pageSize: 10, + configName: undefined, + configKey: undefined, + configType: undefined + }, + // 表单参数 + form: {}, + // 表单校验 + rules: { + configName: [ + { required: true, message: "参数名称不能为空", trigger: "blur" } + ], + configKey: [ + { required: true, message: "参数键名不能为空", trigger: "blur" } + ], + configValue: [ + { required: true, message: "参数键值不能为空", trigger: "blur" } + ] + } + }; + }, + created() { + this.getList(); + this.getDicts("sys_yes_no").then(response => { + this.typeOptions = response.data; + }); + }, + methods: { + /** 查询参数列表 */ + getList() { + this.loading = true; + listConfig(this.addDateRange(this.queryParams, this.dateRange)).then(response => { + this.configList = response.rows; + this.total = response.total; + this.loading = false; + } + ); + }, + // 参数系统内置字典翻译 + typeFormat(row, column) { + return this.selectDictLabel(this.typeOptions, row.configType); + }, + // 取消按钮 + cancel() { + this.open = false; + this.reset(); + }, + // 表单重置 + reset() { + this.form = { + configId: undefined, + configName: undefined, + configKey: undefined, + configValue: undefined, + configType: "Y", + remark: undefined + }; + this.resetForm("form"); + }, + /** 搜索按钮操作 */ + handleQuery() { + this.queryParams.pageNum = 1; + this.getList(); + }, + /** 新增按钮操作 */ + handleAdd() { + this.reset(); + this.open = true; + this.title = "添加参数"; + }, + /** 修改按钮操作 */ + handleUpdate(row) { + this.reset(); + getConfig(row.configId).then(response => { + this.form = response.data; + this.open = true; + this.title = "修改参数"; + }); + }, + /** 提交按钮 */ + submitForm: function() { + this.$refs["form"].validate(valid => { + if (valid) { + if (this.form.configId != undefined) { + updateConfig(this.form).then(response => { + if (response.code === 200) { + this.msgSuccess("修改成功"); + this.open = false; + this.getList(); + } else { + this.msgError(response.msg); + } + }); + } else { + addConfig(this.form).then(response => { + if (response.code === 200) { + this.msgSuccess("新增成功"); + this.open = false; + this.getList(); + } else { + this.msgError(response.msg); + } + }); + } + } + }); + }, + /** 删除按钮操作 */ + handleDelete(row) { + this.$confirm('是否确认删除名称为"' + row.configName + '"的数据项?', "警告", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + }).then(function() { + return delConfig(row.configId); + }).then(() => { + this.getList(); + this.msgSuccess("删除成功"); + }).catch(function() {}); + } + } +}; +</script> \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/dept/index.vue b/ruoyi-ui/src/views/system/dept/index.vue new file mode 100644 index 0000000..c5bb197 --- /dev/null +++ b/ruoyi-ui/src/views/system/dept/index.vue @@ -0,0 +1,307 @@ +<template> + <div class="app-container"> + <el-form :inline="true"> + <el-form-item label="部门名称"> + <el-input + v-model="queryParams.deptName" + placeholder="请输入部门名称" + clearable + size="small" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="状态"> + <el-select v-model="queryParams.status" placeholder="部门状态" clearable size="small"> + <el-option + v-for="dict in statusOptions" + :key="dict.dictValue" + :label="dict.dictLabel" + :value="dict.dictValue" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button + class="filter-item" + type="primary" + icon="el-icon-search" + size="mini" + @click="handleQuery" + >搜索</el-button> + <el-button + class="filter-item" + type="primary" + icon="el-icon-plus" + size="mini" + @click="handleAdd" + v-hasPermi="['system:dept:add']" + >新增</el-button> + </el-form-item> + </el-form> + + <el-table + v-loading="loading" + :data="deptList" + row-key="deptId" + default-expand-all + :tree-props="{children: 'children', hasChildren: 'hasChildren'}" + > + <el-table-column prop="deptName" label="部门名称" width="200"></el-table-column> + <el-table-column prop="orderNum" label="排序" width="200"></el-table-column> + <el-table-column prop="status" label="状态" :formatter="statusFormat" width="100"></el-table-column> + <el-table-column label="创建时间" align="center" prop="createTime" width="200"> + <template slot-scope="scope"> + <span>{{ dateFormat(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-edit" + @click="handleUpdate(scope.row)" + v-hasPermi="['system:dept:edit']" + >修改</el-button> + <el-button + size="mini" + type="text" + icon="el-icon-plus" + @click="handleAdd(scope.row)" + v-hasPermi="['system:dept:add']" + >新增</el-button> + <el-button + v-if="scope.row.parentId != 0" + size="mini" + type="text" + icon="el-icon-delete" + @click="handleDelete(scope.row)" + v-hasPermi="['system:dept:remove']" + >删除</el-button> + </template> + </el-table-column> + </el-table> + + <!-- 添加或修改部门对话框 --> + <el-dialog :title="title" :visible.sync="open" width="600px"> + <el-form ref="form" :model="form" :rules="rules" label-width="80px"> + <el-row> + <el-col :span="24" v-if="form.parentId !== 0"> + <el-form-item label="上级部门" prop="parentId"> + <treeselect v-model="form.parentId" :options="deptOptions" placeholder="选择上级部门" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="部门名称" prop="deptName"> + <el-input v-model="form.deptName" placeholder="请输入部门名称" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="显示排序" prop="orderNum"> + <el-input-number v-model="form.orderNum" controls-position="right" :min="0" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="负责人" prop="leader"> + <el-input v-model="form.leader" placeholder="请输入负责人" maxlength="20" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="联系电话" prop="phone"> + <el-input v-model="form.phone" placeholder="请输入联系电话" maxlength="11" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="邮箱" prop="email"> + <el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="部门状态"> + <el-radio-group v-model="form.status"> + <el-radio + v-for="dict in statusOptions" + :key="dict.dictValue" + :label="dict.dictValue" + >{{dict.dictLabel}}</el-radio> + </el-radio-group> + </el-form-item> + </el-col> + </el-row> + </el-form> + <div slot="footer" class="dialog-footer"> + <el-button type="primary" @click="submitForm">确 定</el-button> + <el-button @click="cancel">取 消</el-button> + </div> + </el-dialog> + </div> +</template> + +<script> +import { listDept, getDept, treeselect, delDept, addDept, updateDept } from "@/api/system/dept"; +import Treeselect from "@riophae/vue-treeselect"; +import "@riophae/vue-treeselect/dist/vue-treeselect.css"; + +export default { + components: { Treeselect }, + data() { + return { + // 遮罩层 + loading: true, + // 表格树数据 + deptList: [], + // 部门部门树选项 + deptOptions: undefined, + // 弹出层标题 + title: "", + // 是否显示弹出层 + open: false, + // 状态数据字典 + statusOptions: [], + // 查询参数 + queryParams: { + deptName: undefined, + status: undefined + }, + // 表单参数 + form: {}, + // 表单校验 + rules: { + parentId: [ + { required: true, message: "上级部门不能为空", trigger: "blur" } + ], + deptName: [ + { required: true, message: "部门名称不能为空", trigger: "blur" } + ], + orderNum: [ + { required: true, message: "菜单顺序不能为空", trigger: "blur" } + ], + email: [ + { + type: "email", + message: "'请输入正确的邮箱地址", + trigger: ["blur", "change"] + } + ], + phone: [ + { + pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, + message: "请输入正确的手机号码", + trigger: "blur" + } + ] + } + }; + }, + created() { + this.getList(); + this.getDicts("sys_normal_disable").then(response => { + this.statusOptions = response.data; + }); + }, + methods: { + /** 查询部门列表 */ + getList() { + this.loading = true; + listDept(this.queryParams).then(response => { + this.deptList = response.data; + this.loading = false; + }); + }, + /** 查询部门下拉树结构 */ + getTreeselect() { + treeselect().then(response => { + this.deptOptions = response.data; + }); + }, + // 字典状态字典翻译 + statusFormat(row, column) { + return this.selectDictLabel(this.statusOptions, row.status); + }, + // 取消按钮 + cancel() { + this.open = false; + this.reset(); + }, + // 表单重置 + reset() { + this.form = { + deptId: undefined, + parentId: 100, + deptName: undefined, + orderNum: undefined, + leader: undefined, + phone: undefined, + email: undefined, + status: "0" + }; + this.resetForm("form"); + }, + /** 搜索按钮操作 */ + handleQuery() { + this.getList(); + }, + /** 新增按钮操作 */ + handleAdd(row) { + this.reset(); + this.getTreeselect(); + if (row != undefined) { + this.form.parentId = row.deptId; + } + this.open = true; + this.title = "添加部门"; + }, + /** 修改按钮操作 */ + handleUpdate(row) { + this.reset(); + this.getTreeselect(); + getDept(row.deptId).then(response => { + this.form = response.data; + this.open = true; + this.title = "修改部门"; + }); + }, + /** 提交按钮 */ + submitForm: function() { + this.$refs["form"].validate(valid => { + if (valid) { + if (this.form.deptId != undefined) { + updateDept(this.form).then(response => { + if (response.code === 200) { + this.msgSuccess("修改成功"); + this.open = false; + this.getList(); + } else { + this.msgError(response.msg); + } + }); + } else { + addDept(this.form).then(response => { + if (response.code === 200) { + this.msgSuccess("新增成功"); + this.open = false; + this.getList(); + } else { + this.msgError(response.msg); + } + }); + } + } + }); + }, + /** 删除按钮操作 */ + handleDelete(row) { + this.$confirm('是否确认删除名称为"' + row.deptName + '"的数据项?', "警告", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + }).then(function() { + return delDept(row.deptId); + }).then(() => { + this.getList(); + this.msgSuccess("删除成功"); + }).catch(function() {}); + } + } +}; +</script> \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/dict/data.vue b/ruoyi-ui/src/views/system/dict/data.vue new file mode 100644 index 0000000..fe1b12d --- /dev/null +++ b/ruoyi-ui/src/views/system/dict/data.vue @@ -0,0 +1,276 @@ +<template> + <div class="app-container"> + <el-form :inline="true"> + <el-form-item label="字典名称"> + <el-select v-model="queryParams.dictType" size="small"> + <el-option + v-for="item in typeOptions" + :key="item.dictId" + :label="item.dictName" + :value="item.dictType" + /> + </el-select> + </el-form-item> + <el-form-item label="字典标签"> + <el-input + v-model="queryParams.dictLabel" + placeholder="请输入字典标签" + clearable + size="small" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="状态"> + <el-select v-model="queryParams.status" placeholder="数据状态" clearable size="small"> + <el-option + v-for="dict in statusOptions" + :key="dict.dictValue" + :label="dict.dictLabel" + :value="dict.dictValue" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> + <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['system:dict:add']">新增</el-button> + </el-form-item> + </el-form> + + <el-table v-loading="loading" :data="dataList" style="width: 100%;"> + <el-table-column label="字典编码" align="center" prop="dictCode" /> + <el-table-column label="字典标签" align="center" prop="dictLabel" /> + <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="remark" :show-overflow-tooltip="true" /> + <el-table-column label="创建时间" align="center" prop="createTime" width="180"> + <template slot-scope="scope"> + <span>{{ dateFormat(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-edit" + @click="handleUpdate(scope.row)" + v-hasPermi="['system:dict:edit']" + >修改</el-button> + <el-button + size="mini" + type="text" + icon="el-icon-delete" + @click="handleDelete(scope.row)" + v-hasPermi="['system:dict: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" + /> + + <!-- 添加或修改参数配置对话框 --> + <el-dialog :title="title" :visible.sync="open" width="500px"> + <el-form ref="form" :model="form" :rules="rules" label-width="80px"> + <el-form-item label="字典类型"> + <el-input v-model="form.dictType" :disabled="true" /> + </el-form-item> + <el-form-item label="数据标签" prop="dictLabel"> + <el-input v-model="form.dictLabel" placeholder="请输入数据标签" /> + </el-form-item> + <el-form-item label="数据键值" prop="dictValue"> + <el-input v-model="form.dictValue" 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="status"> + <el-radio-group v-model="form.status"> + <el-radio + v-for="dict in statusOptions" + :key="dict.dictValue" + :label="dict.dictValue" + >{{dict.dictLabel}}</el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="备注" prop="remark"> + <el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input> + </el-form-item> + </el-form> + <div slot="footer" class="dialog-footer"> + <el-button type="primary" @click="submitForm">确 定</el-button> + <el-button @click="cancel">取 消</el-button> + </div> + </el-dialog> + </div> +</template> + +<script> +import { listData, getData, delData, addData, updateData } from "@/api/system/dict/data"; +import { listType, getType } from "@/api/system/dict/type"; + +export default { + data() { + return { + // 遮罩层 + loading: true, + // 总条数 + total: 0, + // 字典表格数据 + dataList: [], + // 弹出层标题 + title: "", + // 是否显示弹出层 + open: false, + // 状态数据字典 + statusOptions: [], + // 类型数据字典 + typeOptions: [], + // 查询参数 + queryParams: { + pageNum: 1, + pageSize: 10, + dictName: undefined, + dictType: undefined, + status: undefined + }, + // 表单参数 + form: {}, + // 表单校验 + rules: { + dictLabel: [ + { required: true, message: "数据标签不能为空", trigger: "blur" } + ], + dictValue: [ + { required: true, message: "数据键值不能为空", trigger: "blur" } + ], + dictSort: [ + { required: true, message: "数据顺序不能为空", trigger: "blur" } + ] + } + }; + }, + created() { + const dictId = this.$route.params && this.$route.params.dictId; + this.getType(dictId); + this.getTypeList(); + this.getDicts("sys_normal_disable").then(response => { + this.statusOptions = response.data; + }); + }, + methods: { + /** 查询字典类型详细 */ + getType(dictId) { + getType(dictId).then(response => { + this.queryParams.dictType = response.data.dictType; + this.getList(); + }); + }, + /** 查询字典类型列表 */ + getTypeList() { + listType().then(response => { + this.typeOptions = response.rows; + }); + }, + /** 查询字典数据列表 */ + getList() { + this.loading = true; + listData(this.queryParams).then(response => { + this.dataList = response.rows; + this.total = response.total; + this.loading = false; + }); + }, + // 数据状态字典翻译 + statusFormat(row, column) { + return this.selectDictLabel(this.statusOptions, row.status); + }, + // 取消按钮 + cancel() { + this.open = false; + this.reset(); + }, + // 表单重置 + reset() { + this.form = { + dictCode: undefined, + dictLabel: undefined, + dictValue: undefined, + dictSort: 0, + status: "0", + remark: undefined + }; + this.resetForm("form"); + }, + /** 搜索按钮操作 */ + handleQuery() { + this.queryParams.pageNum = 1; + this.getList(); + }, + /** 新增按钮操作 */ + handleAdd() { + this.reset(); + this.open = true; + this.title = "添加字典数据"; + this.form.dictType = this.queryParams.dictType; + }, + /** 修改按钮操作 */ + handleUpdate(row) { + this.reset(); + getData(row.dictCode).then(response => { + this.form = response.data; + this.open = true; + this.title = "修改字典数据"; + }); + }, + /** 提交按钮 */ + submitForm: function() { + this.$refs["form"].validate(valid => { + if (valid) { + if (this.form.dictCode != undefined) { + updateData(this.form).then(response => { + if (response.code === 200) { + this.msgSuccess("修改成功"); + this.open = false; + this.getList(); + } else { + this.msgError(response.msg); + } + }); + } else { + addData(this.form).then(response => { + if (response.code === 200) { + this.msgSuccess("新增成功"); + this.open = false; + this.getList(); + } else { + this.msgError(response.msg); + } + }); + } + } + }); + }, + /** 删除按钮操作 */ + handleDelete(row) { + this.$confirm('是否确认删除名称为"' + row.dictLabel + '"的数据项?', "警告", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + }).then(function() { + return delData(row.dictCode); + }).then(() => { + this.getList(); + this.msgSuccess("删除成功"); + }).catch(function() {}); + } + } +}; +</script> \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/dict/index.vue b/ruoyi-ui/src/views/system/dict/index.vue new file mode 100644 index 0000000..f9e5d17 --- /dev/null +++ b/ruoyi-ui/src/views/system/dict/index.vue @@ -0,0 +1,275 @@ +<template> + <div class="app-container"> + <el-form :inline="true" label-width="68px"> + <el-form-item label="字典名称"> + <el-input + v-model="queryParams.dictName" + placeholder="请输入字典名称" + clearable + size="small" + style="width: 240px" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="字典类型"> + <el-input + v-model="queryParams.dictType" + placeholder="请输入字典类型" + clearable + size="small" + style="width: 240px" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="状态"> + <el-select + v-model="queryParams.status" + placeholder="字典状态" + clearable + size="small" + style="width: 240px" + > + <el-option + v-for="dict in statusOptions" + :key="dict.dictValue" + :label="dict.dictLabel" + :value="dict.dictValue" + /> + </el-select> + </el-form-item> + <el-form-item label="创建时间"> + <el-date-picker + v-model="queryParams.createTime" + size="small" + style="width: 240px" + value-format="yyyy-MM-dd" + type="daterange" + range-separator="-" + start-placeholder="开始日期" + end-placeholder="结束日期" + ></el-date-picker> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> + <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['system:dict:add']">新增</el-button> + </el-form-item> + </el-form> + + <el-table v-loading="loading" :data="typeList" style="width: 100%;"> + <el-table-column label="字典主键" align="center" prop="dictId" /> + <el-table-column label="字典名称" align="center" prop="dictName" :show-overflow-tooltip="true" /> + <el-table-column label="字典类型" align="center"> + <template slot-scope="scope"> + <router-link :to="'/dict/type/data/' + scope.row.dictId" class="link-type"> + <span>{{ scope.row.dictType }}</span> + </router-link> + </template> + </el-table-column> + <el-table-column label="状态" align="center" prop="status" :formatter="statusFormat" /> + <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"> + <span>{{ dateFormat(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-edit" + @click="handleUpdate(scope.row)" + v-hasPermi="['system:dict:edit']" + >修改</el-button> + <el-button + size="mini" + type="text" + icon="el-icon-delete" + @click="handleDelete(scope.row)" + v-hasPermi="['system:dict: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" + /> + + <!-- 添加或修改参数配置对话框 --> + <el-dialog :title="title" :visible.sync="open" width="500px"> + <el-form ref="form" :model="form" :rules="rules" label-width="80px"> + <el-form-item label="字典名称" prop="dictName"> + <el-input v-model="form.dictName" placeholder="请输入字典名称" /> + </el-form-item> + <el-form-item label="字典类型" prop="dictType"> + <el-input v-model="form.dictType" placeholder="请输入字典类型" /> + </el-form-item> + <el-form-item label="状态" prop="status"> + <el-radio-group v-model="form.status"> + <el-radio + v-for="dict in statusOptions" + :key="dict.dictValue" + :label="dict.dictValue" + >{{dict.dictLabel}}</el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="备注" prop="remark"> + <el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input> + </el-form-item> + </el-form> + <div slot="footer" class="dialog-footer"> + <el-button type="primary" @click="submitForm">确 定</el-button> + <el-button @click="cancel">取 消</el-button> + </div> + </el-dialog> + </div> +</template> + +<script> +import { listType, getType, delType, addType, updateType } from "@/api/system/dict/type"; + +export default { + data() { + return { + // 遮罩层 + loading: true, + // 总条数 + total: 0, + // 字典表格数据 + typeList: [], + // 弹出层标题 + title: "", + // 是否显示弹出层 + open: false, + // 状态数据字典 + statusOptions: [], + // 日期范围 + dateRange: [], + // 查询参数 + queryParams: { + pageNum: 1, + pageSize: 10, + dictName: undefined, + dictType: undefined, + status: undefined + }, + // 表单参数 + form: {}, + // 表单校验 + rules: { + dictName: [ + { required: true, message: "字典名称不能为空", trigger: "blur" } + ], + dictType: [ + { required: true, message: "字典类型不能为空", trigger: "blur" } + ] + } + }; + }, + created() { + this.getList(); + this.getDicts("sys_normal_disable").then(response => { + this.statusOptions = response.data; + }); + }, + methods: { + /** 查询字典类型列表 */ + getList() { + this.loading = true; + listType(this.addDateRange(this.queryParams, this.dateRange)).then( + response => { + this.typeList = response.rows; + this.total = response.total; + this.loading = false; + } + ); + }, + // 字典状态字典翻译 + statusFormat(row, column) { + return this.selectDictLabel(this.statusOptions, row.status); + }, + // 取消按钮 + cancel() { + this.open = false; + this.reset(); + }, + // 表单重置 + reset() { + this.form = { + dictId: undefined, + dictName: undefined, + dictType: undefined, + status: "0", + remark: undefined + }; + this.resetForm("form"); + }, + /** 搜索按钮操作 */ + handleQuery() { + this.queryParams.pageNum = 1; + this.getList(); + }, + /** 新增按钮操作 */ + handleAdd() { + this.reset(); + this.open = true; + this.title = "添加字典类型"; + }, + /** 修改按钮操作 */ + handleUpdate(row) { + this.reset(); + getType(row.dictId).then(response => { + this.form = response.data; + this.open = true; + this.title = "修改字典类型"; + }); + }, + /** 提交按钮 */ + submitForm: function() { + this.$refs["form"].validate(valid => { + if (valid) { + if (this.form.dictId != undefined) { + updateType(this.form).then(response => { + if (response.code === 200) { + this.msgSuccess("修改成功"); + this.open = false; + this.getList(); + } else { + this.msgError(response.msg); + } + }); + } else { + addType(this.form).then(response => { + if (response.code === 200) { + this.msgSuccess("新增成功"); + this.open = false; + this.getList(); + } else { + this.msgError(response.msg); + } + }); + } + } + }); + }, + /** 删除按钮操作 */ + handleDelete(row) { + this.$confirm('是否确认删除名称为"' + row.dictName + '"的数据项?', "警告", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + }).then(function() { + return delType(row.dictId); + }).then(() => { + this.getList(); + this.msgSuccess("删除成功"); + }).catch(function() {}); + } + } +}; +</script> \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/log/index.vue b/ruoyi-ui/src/views/system/log/index.vue new file mode 100644 index 0000000..ad6360c --- /dev/null +++ b/ruoyi-ui/src/views/system/log/index.vue @@ -0,0 +1,3 @@ +<template > + <router-view /> +</template> diff --git a/ruoyi-ui/src/views/system/menu/index.vue b/ruoyi-ui/src/views/system/menu/index.vue new file mode 100644 index 0000000..d8d163e --- /dev/null +++ b/ruoyi-ui/src/views/system/menu/index.vue @@ -0,0 +1,337 @@ +<template> + <div class="app-container"> + <el-form :inline="true"> + <el-form-item label="菜单名称"> + <el-input + v-model="queryParams.menuName" + placeholder="请输入菜单名称" + clearable + size="small" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="状态"> + <el-select v-model="queryParams.visible" placeholder="菜单状态" clearable size="small"> + <el-option + v-for="dict in visibleOptions" + :key="dict.dictValue" + :label="dict.dictLabel" + :value="dict.dictValue" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> + <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['system:menu:add']">新增</el-button> + </el-form-item> + </el-form> + + <el-table + v-loading="loading" + :data="menuList" + row-key="menuId" + :tree-props="{children: 'children', hasChildren: 'hasChildren'}" + > + <el-table-column prop="menuName" label="菜单名称" :show-overflow-tooltip="true" width="130px"></el-table-column> + <el-table-column prop="icon" label="图标" align="center" width="100px"> + <template slot-scope="scope"> + <svg-icon :icon-class="scope.row.icon" /> + </template> + </el-table-column> + <el-table-column prop="orderNum" label="排序" width="60px"></el-table-column> + <el-table-column prop="perms" label="权限标识" width="130px" :show-overflow-tooltip="true"></el-table-column> + <el-table-column prop="component" label="组件路径" width="180px" :show-overflow-tooltip="true"></el-table-column> + <el-table-column prop="visible" label=" 可见" :formatter="visibleFormat" width="80px"></el-table-column> + <el-table-column label="创建时间" align="center" prop="createTime" width="180"> + <template slot-scope="scope"> + <span>{{ dateFormat(scope.row.createTime) }}</span> + </template> + </el-table-column> + <el-table-column label="操作" align="center" width="180" class-name="small-padding fixed-width"> + <template slot-scope="scope"> + <el-button size="mini" + type="text" + icon="el-icon-edit" + @click="handleUpdate(scope.row)" + v-hasPermi="['system:menu:edit']" + >修改</el-button> + <el-button + size="mini" + type="text" + icon="el-icon-plus" + @click="handleAdd(scope.row)" + v-hasPermi="['system:menu:add']" + >新增</el-button> + <el-button + v-if="scope.row.parentId != 0" + size="mini" + type="text" + icon="el-icon-delete" + @click="handleDelete(scope.row)" + v-hasPermi="['system:menu:remove']" + >删除</el-button> + </template> + </el-table-column> + </el-table> + + <!-- 添加或修改菜单对话框 --> + <el-dialog :title="title" :visible.sync="open" width="600px"> + <el-form ref="form" :model="form" :rules="rules" label-width="80px"> + <el-row> + <el-col :span="24" v-if="form.parentId !== 0"> + <el-form-item label="上级菜单" prop="parentId"> + <treeselect + v-model="form.parentId" + :options="menuOptions" + :show-count="true" + placeholder="选择上级菜单" + /> + </el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="菜单类型" prop="menuType"> + <el-radio-group v-model="form.menuType"> + <el-radio label="M">目录</el-radio> + <el-radio label="C">菜单</el-radio> + <el-radio label="F">按钮</el-radio> + </el-radio-group> + </el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item v-if="form.menuType != 'F'" label="菜单图标"> + <el-popover + placement="bottom-start" + width="460" + trigger="click" + @show="$refs['iconSelect'].reset()" + > + <IconSelect ref="iconSelect" @selected="selected" /> + <el-input slot="reference" v-model="form.icon" placeholder="点击选择图标" readonly> + <svg-icon + v-if="form.icon" + slot="prefix" + :icon-class="form.icon" + class="el-input__icon" + style="height: 32px;width: 16px;" + /> + <i v-else slot="prefix" class="el-icon-search el-input__icon" /> + </el-input> + </el-popover> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="菜单名称" prop="menuName"> + <el-input v-model="form.menuName" placeholder="请输入菜单名称" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="显示排序" prop="orderNum"> + <el-input-number v-model="form.orderNum" controls-position="right" :min="0" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item v-if="form.menuType != 'F'" label="是否外链"> + <el-radio-group v-model="form.isFrame"> + <el-radio label="0">是</el-radio> + <el-radio label="1">否</el-radio> + </el-radio-group> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item v-if="form.menuType != 'F'" label="路由地址" prop="path"> + <el-input v-model="form.path" placeholder="请输入路由地址" /> + </el-form-item> + </el-col> + <el-col :span="12" v-if="form.menuType == 'C'"> + <el-form-item label="组件路径" prop="component"> + <el-input v-model="form.component" placeholder="请输入组件路径" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item v-if="form.menuType == 'C'" label="权限标识"> + <el-input v-model="form.perms" placeholder="请权限标识" maxlength="50" /> + </el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item v-if="form.menuType != 'F'" label="菜单状态"> + <el-radio-group v-model="form.visible"> + <el-radio + v-for="dict in visibleOptions" + :key="dict.dictValue" + :label="dict.dictValue" + >{{dict.dictLabel}}</el-radio> + </el-radio-group> + </el-form-item> + </el-col> + </el-row> + </el-form> + <div slot="footer" class="dialog-footer"> + <el-button type="primary" @click="submitForm">确 定</el-button> + <el-button @click="cancel">取 消</el-button> + </div> + </el-dialog> + </div> +</template> + +<script> +import { listMenu, getMenu, treeselect, delMenu, addMenu, updateMenu } from "@/api/system/menu"; +import Treeselect from "@riophae/vue-treeselect"; +import "@riophae/vue-treeselect/dist/vue-treeselect.css"; +import IconSelect from "@/components/IconSelect"; + +export default { + components: { Treeselect, IconSelect }, + data() { + return { + // 遮罩层 + loading: true, + // 菜单表格树数据 + menuList: [], + // 菜单树选项 + menuOptions: undefined, + // 弹出层标题 + title: "", + // 是否显示弹出层 + open: false, + // 菜单状态数据字典 + visibleOptions: [], + // 查询参数 + queryParams: { + menuName: undefined, + visible: undefined + }, + // 表单参数 + form: {}, + // 表单校验 + rules: { + parentId: [ + { required: true, message: "上级菜单不能为空", trigger: "blur" } + ], + menuName: [ + { required: true, message: "菜单名称不能为空", trigger: "blur" } + ], + orderNum: [ + { required: true, message: "菜单顺序不能为空", trigger: "blur" } + ] + } + }; + }, + created() { + this.getList(); + this.getDicts("sys_show_hide").then(response => { + this.visibleOptions = response.data; + }); + }, + methods: { + // 选择图标 + selected(name) { + this.form.icon = name; + }, + /** 查询菜单列表 */ + getList() { + this.loading = true; + listMenu(this.queryParams).then(response => { + this.menuList = response.data; + this.loading = false; + }); + }, + /** 查询菜单下拉树结构 */ + getTreeselect() { + treeselect().then(response => { + this.menuOptions = response.data; + }); + }, + // 菜单显示状态字典翻译 + visibleFormat(row, column) { + if (row.menuType == "F") { + return ""; + } + return this.selectDictLabel(this.visibleOptions, row.visible); + }, + // 取消按钮 + cancel() { + this.open = false; + this.reset(); + }, + // 表单重置 + reset() { + this.form = { + menuId: undefined, + parentId: 0, + menuName: undefined, + icon: undefined, + menuType: "M", + orderNum: undefined, + isFrame: "1", + visible: "0" + }; + this.resetForm("form"); + }, + /** 搜索按钮操作 */ + handleQuery() { + this.getList(); + }, + /** 新增按钮操作 */ + handleAdd(row) { + this.reset(); + this.getTreeselect(); + if (row != null) { + this.form.parentId = row.menuId; + } + this.open = true; + this.title = "添加菜单"; + }, + /** 修改按钮操作 */ + handleUpdate(row) { + this.reset(); + this.getTreeselect(); + getMenu(row.menuId).then(response => { + this.form = response.data; + this.open = true; + this.title = "修改菜单"; + }); + }, + /** 提交按钮 */ + submitForm: function() { + this.$refs["form"].validate(valid => { + if (valid) { + if (this.form.menuId != undefined) { + updateMenu(this.form).then(response => { + if (response.code === 200) { + this.msgSuccess("修改成功"); + this.open = false; + this.getList(); + } else { + this.msgError(response.msg); + } + }); + } else { + addMenu(this.form).then(response => { + if (response.code === 200) { + this.msgSuccess("新增成功"); + this.open = false; + this.getList(); + } else { + this.msgError(response.msg); + } + }); + } + } + }); + }, + /** 删除按钮操作 */ + handleDelete(row) { + this.$confirm('是否确认删除名称为"' + row.menuName + '"的数据项?', "警告", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + }).then(function() { + return delMenu(row.menuId); + }).then(() => { + this.getList(); + this.msgSuccess("删除成功"); + }).catch(function() {}); + } + } +}; +</script> \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/notice/index.vue b/ruoyi-ui/src/views/system/notice/index.vue new file mode 100644 index 0000000..6aa32bd --- /dev/null +++ b/ruoyi-ui/src/views/system/notice/index.vue @@ -0,0 +1,292 @@ +<template> + <div class="app-container"> + <el-form :inline="true" label-width="68px"> + <el-form-item label="公告标题"> + <el-input + v-model="queryParams.noticeTitle" + placeholder="请输入公告标题" + clearable + size="small" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="操作人员"> + <el-input + v-model="queryParams.createBy" + placeholder="请输入操作人员" + clearable + size="small" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="类型"> + <el-select v-model="queryParams.noticeType" placeholder="公告类型" clearable size="small"> + <el-option + v-for="dict in typeOptions" + :key="dict.dictValue" + :label="dict.dictLabel" + :value="dict.dictValue" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> + <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['system:notice:add']">新增</el-button> + </el-form-item> + </el-form> + + <el-table v-loading="loading" :data="noticeList"> + <el-table-column label="序号" align="center" prop="noticeId" width="100" /> + <el-table-column + label="公告标题" + align="center" + prop="noticeTitle" + :show-overflow-tooltip="true" + /> + <el-table-column + label="公告类型" + align="center" + prop="noticeType" + :formatter="typeFormat" + width="100" + /> + <el-table-column + label="状态" + align="center" + prop="status" + :formatter="statusFormat" + width="100" + /> + <el-table-column label="创建者" align="center" prop="createBy" width="100" /> + <el-table-column label="创建时间" align="center" prop="createTime" width="100"> + <template slot-scope="scope"> + <span>{{ dateFormat(scope.row.createTime, 'yyyy-MM-dd') }}</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-edit" + @click="handleUpdate(scope.row)" + v-hasPermi="['system:notice:edit']" + >修改</el-button> + <el-button + size="mini" + type="text" + icon="el-icon-delete" + @click="handleDelete(scope.row)" + v-hasPermi="['system:notice: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" + /> + + <!-- 添加或修改公告对话框 --> + <el-dialog :title="title" :visible.sync="open" width="780px"> + <el-form ref="form" :model="form" :rules="rules" label-width="80px"> + <el-row> + <el-col :span="12"> + <el-form-item label="公告标题" prop="noticeTitle"> + <el-input v-model="form.noticeTitle" placeholder="请输入公告标题" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="公告类型" prop="noticeType"> + <el-select v-model="form.noticeType" placeholder="请选择"> + <el-option + v-for="dict in typeOptions" + :key="dict.dictValue" + :label="dict.dictLabel" + :value="dict.dictValue" + ></el-option> + </el-select> + </el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="状态"> + <el-radio-group v-model="form.status"> + <el-radio + v-for="dict in statusOptions" + :key="dict.dictValue" + :label="dict.dictValue" + >{{dict.dictLabel}}</el-radio> + </el-radio-group> + </el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="内容"> + <Editor v-model="form.noticeContent"/> + </el-form-item> + </el-col> + </el-row> + </el-form> + <div slot="footer" class="dialog-footer" style="padding-top:20px"> + <el-button type="primary" @click="submitForm">确 定</el-button> + <el-button @click="cancel">取 消</el-button> + </div> + </el-dialog> + </div> +</template> + +<script> +import { listNotice, getNotice, delNotice, addNotice, updateNotice } from "@/api/system/notice"; +import Editor from '@/components/Editor'; + +export default { + components: { + Editor + }, + data() { + return { + // 遮罩层 + loading: true, + // 总条数 + total: 0, + // 公告表格数据 + noticeList: [], + // 弹出层标题 + title: "", + // 是否显示弹出层 + open: false, + // 类型数据字典 + statusOptions: [], + // 状态数据字典 + typeOptions: [], + // 查询参数 + queryParams: { + pageNum: 1, + pageSize: 10, + noticeTitle: undefined, + createBy: undefined, + status: undefined + }, + // 表单参数 + form: {}, + // 表单校验 + rules: { + noticeTitle: [ + { required: true, message: "公告标题不能为空", trigger: "blur" } + ], + noticeType: [ + { required: true, message: "公告类型不能为空", trigger: "blur" } + ] + } + }; + }, + created() { + this.getList(); + this.getDicts("sys_notice_status").then(response => { + this.statusOptions = response.data; + }); + this.getDicts("sys_notice_type").then(response => { + this.typeOptions = response.data; + }); + }, + methods: { + /** 查询公告列表 */ + getList() { + this.loading = true; + listNotice(this.queryParams).then(response => { + this.noticeList = response.rows; + this.total = response.total; + this.loading = false; + }); + }, + // 公告状态字典翻译 + statusFormat(row, column) { + return this.selectDictLabel(this.statusOptions, row.status); + }, + // 公告状态字典翻译 + typeFormat(row, column) { + return this.selectDictLabel(this.typeOptions, row.noticeType); + }, + // 取消按钮 + cancel() { + this.open = false; + this.reset(); + }, + // 表单重置 + reset() { + this.form = { + noticeId: undefined, + noticeTitle: undefined, + noticeType: undefined, + noticeContent: undefined, + status: "0" + }; + this.resetForm("form"); + }, + /** 搜索按钮操作 */ + handleQuery() { + this.queryParams.pageNum = 1; + this.getList(); + }, + /** 新增按钮操作 */ + handleAdd() { + this.reset(); + this.open = true; + this.title = "添加公告"; + }, + /** 修改按钮操作 */ + handleUpdate(row) { + this.reset(); + getNotice(row.noticeId).then(response => { + this.form = response.data; + this.open = true; + this.title = "修改公告"; + }); + }, + /** 提交按钮 */ + submitForm: function() { + this.$refs["form"].validate(valid => { + if (valid) { + if (this.form.noticeId != undefined) { + updateNotice(this.form).then(response => { + if (response.code === 200) { + this.msgSuccess("修改成功"); + this.open = false; + this.getList(); + } else { + this.msgError(response.msg); + } + }); + } else { + addNotice(this.form).then(response => { + if (response.code === 200) { + this.msgSuccess("新增成功"); + this.open = false; + this.getList(); + } else { + this.msgError(response.msg); + } + }); + } + } + }); + }, + /** 删除按钮操作 */ + handleDelete(row) { + this.$confirm('是否确认删除公告标题为"' + row.noticeTitle + '"的数据项?', "警告", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + }).then(function() { + return delNotice(row.noticeId); + }).then(() => { + this.getList(); + this.msgSuccess("删除成功"); + }).catch(function() {}); + } + } +}; +</script> \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/post/index.vue b/ruoyi-ui/src/views/system/post/index.vue new file mode 100644 index 0000000..b1f0c9f --- /dev/null +++ b/ruoyi-ui/src/views/system/post/index.vue @@ -0,0 +1,252 @@ +<template> + <div class="app-container"> + <el-form :inline="true" label-width="68px"> + <el-form-item label="岗位编码"> + <el-input + v-model="queryParams.postCode" + placeholder="请输入岗位编码" + clearable + size="small" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="岗位名称"> + <el-input + v-model="queryParams.postName" + placeholder="请输入岗位名称" + clearable + size="small" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="状态"> + <el-select v-model="queryParams.status" placeholder="岗位状态" clearable size="small"> + <el-option + v-for="dict in statusOptions" + :key="dict.dictValue" + :label="dict.dictLabel" + :value="dict.dictValue" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> + <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['system:post:add']">新增</el-button> + </el-form-item> + </el-form> + + <el-table v-loading="loading" :data="postList"> + <el-table-column label="岗位编号" align="center" prop="postId" /> + <el-table-column label="岗位编码" align="center" prop="postCode" /> + <el-table-column label="岗位名称" align="center" prop="postName" /> + <el-table-column label="岗位排序" align="center" prop="postSort" /> + <el-table-column label="状态" align="center" prop="status" :formatter="statusFormat" /> + <el-table-column label="创建时间" align="center" prop="createTime" width="180"> + <template slot-scope="scope"> + <span>{{ dateFormat(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-edit" + @click="handleUpdate(scope.row)" + v-hasPermi="['system:post:edit']" + >修改</el-button> + <el-button + size="mini" + type="text" + icon="el-icon-delete" + @click="handleDelete(scope.row)" + v-hasPermi="['system:post: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" + /> + + <!-- 添加或修改岗位对话框 --> + <el-dialog :title="title" :visible.sync="open" width="500px"> + <el-form ref="form" :model="form" :rules="rules" label-width="80px"> + <el-form-item label="岗位名称" prop="postName"> + <el-input v-model="form.postName" placeholder="请输入岗位名称" /> + </el-form-item> + <el-form-item label="岗位编码" prop="postCode"> + <el-input v-model="form.postCode" placeholder="请输入编码名称" /> + </el-form-item> + <el-form-item label="岗位顺序" prop="postSort"> + <el-input-number v-model="form.postSort" controls-position="right" :min="0" /> + </el-form-item> + <el-form-item label="岗位状态" prop="status"> + <el-radio-group v-model="form.status"> + <el-radio + v-for="dict in statusOptions" + :key="dict.dictValue" + :label="dict.dictValue" + >{{dict.dictLabel}}</el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="备注" prop="remark"> + <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" /> + </el-form-item> + </el-form> + <div slot="footer" class="dialog-footer"> + <el-button type="primary" @click="submitForm">确 定</el-button> + <el-button @click="cancel">取 消</el-button> + </div> + </el-dialog> + </div> +</template> + +<script> +import { listPost, getPost, delPost, addPost, updatePost } from "@/api/system/post"; + +export default { + data() { + return { + // 遮罩层 + loading: true, + // 总条数 + total: 0, + // 岗位表格数据 + postList: [], + // 弹出层标题 + title: "", + // 是否显示弹出层 + open: false, + // 状态数据字典 + statusOptions: [], + // 查询参数 + queryParams: { + pageNum: 1, + pageSize: 10, + postCode: undefined, + postName: undefined, + status: undefined + }, + // 表单参数 + form: {}, + // 表单校验 + rules: { + postName: [ + { required: true, message: "岗位名称不能为空", trigger: "blur" } + ], + postCode: [ + { required: true, message: "岗位编码不能为空", trigger: "blur" } + ], + postSort: [ + { required: true, message: "岗位顺序不能为空", trigger: "blur" } + ] + } + }; + }, + created() { + this.getList(); + this.getDicts("sys_normal_disable").then(response => { + this.statusOptions = response.data; + }); + }, + methods: { + /** 查询岗位列表 */ + getList() { + this.loading = true; + listPost(this.queryParams).then(response => { + this.postList = response.rows; + this.total = response.total; + this.loading = false; + }); + }, + // 岗位状态字典翻译 + statusFormat(row, column) { + return this.selectDictLabel(this.statusOptions, row.status); + }, + // 取消按钮 + cancel() { + this.open = false; + this.reset(); + }, + // 表单重置 + reset() { + this.form = { + postId: undefined, + postCode: undefined, + postName: undefined, + postSort: 0, + status: "0", + remark: undefined + }; + this.resetForm("form"); + }, + /** 搜索按钮操作 */ + handleQuery() { + this.queryParams.pageNum = 1; + this.getList(); + }, + /** 新增按钮操作 */ + handleAdd() { + this.reset(); + this.open = true; + this.title = "添加岗位"; + }, + /** 修改按钮操作 */ + handleUpdate(row) { + this.reset(); + getPost(row.postId).then(response => { + this.form = response.data; + this.open = true; + this.title = "修改岗位"; + }); + }, + /** 提交按钮 */ + submitForm: function() { + this.$refs["form"].validate(valid => { + if (valid) { + if (this.form.postId != undefined) { + updatePost(this.form).then(response => { + if (response.code === 200) { + this.msgSuccess("修改成功"); + this.open = false; + this.getList(); + } else { + this.msgError(response.msg); + } + }); + } else { + addPost(this.form).then(response => { + if (response.code === 200) { + this.msgSuccess("新增成功"); + this.open = false; + this.getList(); + } else { + this.msgError(response.msg); + } + }); + } + } + }); + }, + /** 删除按钮操作 */ + handleDelete(row) { + this.$confirm('是否确认删除岗位名称为"' + row.postName + '"的数据项?', "警告", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + }).then(function() { + return delPost(row.postId); + }).then(() => { + this.getList(); + this.msgSuccess("删除成功"); + }).catch(function() {}); + } + } +}; +</script> \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/role/index.vue b/ruoyi-ui/src/views/system/role/index.vue new file mode 100644 index 0000000..332dc8f --- /dev/null +++ b/ruoyi-ui/src/views/system/role/index.vue @@ -0,0 +1,478 @@ +<template> + <div class="app-container"> + <el-form :inline="true"> + <el-form-item label="角色名称"> + <el-input + v-model="queryParams.roleName" + placeholder="请输入角色名称" + clearable + size="small" + style="width: 240px" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="权限字符"> + <el-input + v-model="queryParams.roleKey" + placeholder="请输入权限字符" + clearable + size="small" + style="width: 240px" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="状态"> + <el-select + v-model="queryParams.status" + placeholder="角色状态" + clearable + size="small" + style="width: 240px" + > + <el-option + v-for="dict in statusOptions" + :key="dict.dictValue" + :label="dict.dictLabel" + :value="dict.dictValue" + /> + </el-select> + </el-form-item> + <el-form-item label="创建时间"> + <el-date-picker + v-model="dateRange" + size="small" + style="width: 240px" + value-format="yyyy-MM-dd" + type="daterange" + range-separator="-" + start-placeholder="开始日期" + end-placeholder="结束日期" + ></el-date-picker> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> + <el-button + type="primary" + icon="el-icon-plus" + size="mini" + @click="handleAdd" + v-hasPermi="['system:role:add']" + >新增</el-button> + </el-form-item> + </el-form> + + <el-table v-loading="loading" :data="roleList"> + <el-table-column label="角色编号" prop="roleId" width="120" /> + <el-table-column label="角色名称" prop="roleName" :show-overflow-tooltip="true" width="150" /> + <el-table-column label="权限字符" prop="roleKey" :show-overflow-tooltip="true" width="180" /> + <el-table-column label="显示顺序" prop="roleSort" width="120" /> + <el-table-column label="状态" align="center" width="120"> + <template slot-scope="scope"> + <el-switch + v-model="scope.row.status" + active-value="0" + inactive-value="1" + @change="handleStatusChange(scope.row)" + ></el-switch> + </template> + </el-table-column> + <el-table-column label="创建时间" align="center" prop="createTime" width="180"> + <template slot-scope="scope"> + <span>{{ dateFormat(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-edit" + @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" + icon="el-icon-delete" + @click="handleDelete(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" + /> + + <!-- 添加或修改角色配置对话框 --> + <el-dialog :title="title" :visible.sync="open" width="500px"> + <el-form ref="form" :model="form" :rules="rules" label-width="80px"> + <el-form-item label="角色名称" prop="roleName"> + <el-input v-model="form.roleName" placeholder="请输入角色名称" /> + </el-form-item> + <el-form-item label="权限字符" prop="roleKey"> + <el-input v-model="form.roleKey" placeholder="请输入权限字符" /> + </el-form-item> + <el-form-item label="角色顺序" prop="roleSort"> + <el-input-number v-model="form.roleSort" controls-position="right" :min="0" /> + </el-form-item> + <el-form-item label="状态"> + <el-radio-group v-model="form.status"> + <el-radio + v-for="dict in statusOptions" + :key="dict.dictValue" + :label="dict.dictValue" + >{{dict.dictLabel}}</el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="菜单权限"> + <el-tree + :data="menuOptions" + show-checkbox + ref="menu" + node-key="id" + empty-text="加载中,请稍后" + :props="defaultProps" + ></el-tree> + </el-form-item> + <el-form-item label="备注"> + <el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input> + </el-form-item> + </el-form> + <div slot="footer" class="dialog-footer"> + <el-button type="primary" @click="submitForm">确 定</el-button> + <el-button @click="cancel">取 消</el-button> + </div> + </el-dialog> + + <!-- 分配角色数据权限对话框 --> + <el-dialog :title="title" :visible.sync="openDataScope" width="500px"> + <el-form :model="form" label-width="80px"> + <el-form-item label="角色名称"> + <el-input v-model="form.roleName" :disabled="true" /> + </el-form-item> + <el-form-item label="权限字符"> + <el-input v-model="form.roleKey" :disabled="true" /> + </el-form-item> + <el-form-item label="权限范围"> + <el-select v-model="form.dataScope"> + <el-option + v-for="item in dataScopeOptions" + :key="item.value" + :label="item.label" + :value="item.value" + ></el-option> + </el-select> + </el-form-item> + <el-form-item label="数据权限" v-show="form.dataScope == 2"> + <el-tree + :data="deptOptions" + show-checkbox + default-expand-all + ref="dept" + node-key="id" + empty-text="加载中,请稍后" + :props="defaultProps" + ></el-tree> + </el-form-item> + </el-form> + <div slot="footer" class="dialog-footer"> + <el-button type="primary" @click="submitDataScope">确 定</el-button> + <el-button @click="cancelDataScope">取 消</el-button> + </div> + </el-dialog> + </div> +</template> + +<script> +import { listRole, getRole, delRole, addRole, updateRole, dataScope, changeRoleStatus } from "@/api/system/role"; +import { treeselect as menuTreeselect, roleMenuTreeselect } from "@/api/system/menu"; +import { treeselect as deptTreeselect, roleDeptTreeselect } from "@/api/system/dept"; + +export default { + data() { + return { + // 遮罩层 + loading: true, + // 总条数 + total: 0, + // 角色表格数据 + roleList: [], + // 弹出层标题 + title: "", + // 是否显示弹出层 + open: false, + // 是否显示弹出层(数据权限) + openDataScope: false, + // 日期范围 + dateRange: [], + // 状态数据字典 + statusOptions: [], + // 数据范围选项 + dataScopeOptions: [ + { + value: "1", + label: "全部数据权限" + }, + { + value: "2", + label: "自定数据权限" + }, + { + value: "3", + label: "本部门数据权限" + }, + { + value: "4", + label: "本部门及以下数据权限" + }, + { + value: "5", + label: "仅本人数据权限" + } + ], + // 菜单列表 + menuOptions: [], + // 部门列表 + deptOptions: [], + // 查询参数 + queryParams: { + pageNum: 1, + pageSize: 10, + roleName: undefined, + roleKey: undefined, + status: undefined + }, + // 表单参数 + form: {}, + defaultProps: { + children: "children", + label: "label" + }, + // 表单校验 + rules: { + roleName: [ + { required: true, message: "角色名称不能为空", trigger: "blur" } + ], + roleKey: [ + { required: true, message: "权限字符不能为空", trigger: "blur" } + ], + roleSort: [ + { required: true, message: "角色顺序不能为空", trigger: "blur" } + ] + } + }; + }, + created() { + this.getList(); + this.getDicts("sys_normal_disable").then(response => { + this.statusOptions = response.data; + }); + }, + methods: { + /** 查询角色列表 */ + getList() { + this.loading = true; + listRole(this.addDateRange(this.queryParams, this.dateRange)).then( + response => { + this.roleList = response.rows; + this.total = response.total; + this.loading = false; + } + ); + }, + /** 查询菜单树结构 */ + getMenuTreeselect() { + menuTreeselect().then(response => { + this.menuOptions = response.data; + }); + }, + /** 查询部门树结构 */ + getDeptTreeselect() { + deptTreeselect().then(response => { + this.deptOptions = response.data; + }); + }, + // 所有菜单节点数据 + getMenuAllCheckedKeys() { + // 目前被选中的菜单节点 + let checkedKeys = this.$refs.menu.getHalfCheckedKeys(); + // 半选中的菜单节点 + let halfCheckedKeys = this.$refs.menu.getCheckedKeys(); + checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys); + return checkedKeys; + }, + // 所有部门节点数据 + getDeptAllCheckedKeys() { + // 目前被选中的部门节点 + let checkedKeys = this.$refs.dept.getHalfCheckedKeys(); + // 半选中的部门节点 + let halfCheckedKeys = this.$refs.dept.getCheckedKeys(); + checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys); + return checkedKeys; + }, + /** 根据角色ID查询菜单树结构 */ + getRoleMenuTreeselect(roleId) { + roleMenuTreeselect(roleId).then(response => { + this.getMenuTreeselect(); + this.$refs.menu.setCheckedKeys(response.data); + }); + }, + /** 根据角色ID查询部门树结构 */ + getRoleDeptTreeselect(roleId) { + roleDeptTreeselect(roleId).then(response => { + this.getDeptTreeselect(); + this.$refs.dept.setCheckedKeys(response.data); + }); + }, + // 角色状态修改 + handleStatusChange(row) { + let text = row.status === "0" ? "启用" : "停用"; + this.$confirm('确认要"' + text + '""' + row.roleName + '"角色吗?', "警告", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + }).then(function() { + return changeRoleStatus(row.roleId, row.status); + }).then(() => { + this.msgSuccess(text + "成功"); + }).catch(function() { + row.status = row.status === "0" ? "1" : "0"; + }); + }, + // 取消按钮 + cancel() { + this.open = false; + this.reset(); + }, + // 取消按钮(数据权限) + cancelDataScope() { + this.openDataScope = false; + this.reset(); + }, + // 表单重置 + reset() { + if (this.$refs.tree != undefined) { + this.$refs.tree.setCheckedKeys([]); + } + this.form = { + roleId: undefined, + roleName: undefined, + roleKey: undefined, + roleSort: 0, + status: "0", + menuIds: [], + deptIds: [], + remark: undefined + }; + this.resetForm("form"); + }, + /** 搜索按钮操作 */ + handleQuery() { + this.queryParams.pageNum = 1; + this.getList(); + }, + /** 新增按钮操作 */ + handleAdd() { + this.reset(); + this.getMenuTreeselect(); + this.open = true; + this.title = "添加角色"; + }, + /** 修改按钮操作 */ + handleUpdate(row) { + this.reset(); + this.$nextTick(() => { + this.getRoleMenuTreeselect(row.roleId); + }); + getRole(row.roleId).then(response => { + this.form = response.data; + this.open = true; + this.title = "修改角色"; + }); + }, + /** 分配数据权限操作 */ + handleDataScope(row) { + this.reset(); + this.$nextTick(() => { + this.getRoleDeptTreeselect(row.roleId); + }); + getRole(row.roleId).then(response => { + this.form = response.data; + this.openDataScope = true; + this.title = "分配数据权限"; + }); + }, + /** 提交按钮 */ + submitForm: function() { + this.$refs["form"].validate(valid => { + if (valid) { + if (this.form.roleId != undefined) { + this.form.menuIds = this.getMenuAllCheckedKeys(); + updateRole(this.form).then(response => { + if (response.code === 200) { + this.msgSuccess("修改成功"); + this.open = false; + this.getList(); + } else { + this.msgError(response.msg); + } + }); + } else { + this.form.menuIds = this.getMenuAllCheckedKeys(); + addRole(this.form).then(response => { + if (response.code === 200) { + this.msgSuccess("新增成功"); + this.open = false; + this.getList(); + } else { + this.msgError(response.msg); + } + }); + } + } + }); + }, + /** 提交按钮(数据权限) */ + submitDataScope: function() { + if (this.form.roleId != undefined) { + this.form.deptIds = this.getDeptAllCheckedKeys(); + dataScope(this.form).then(response => { + if (response.code === 200) { + this.msgSuccess("修改成功"); + this.openDataScope = false; + this.getList(); + } else { + this.msgError(response.msg); + } + }); + } + }, + /** 删除按钮操作 */ + handleDelete(row) { + this.$confirm('是否确认删除名称为"' + row.roleName + '"的数据项?', "警告", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + }).then(function() { + return delRole(row.roleId); + }).then(() => { + this.getList(); + this.msgSuccess("删除成功"); + }).catch(function() {}); + } + } +}; +</script> \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/user/index.vue b/ruoyi-ui/src/views/system/user/index.vue new file mode 100644 index 0000000..c340d74 --- /dev/null +++ b/ruoyi-ui/src/views/system/user/index.vue @@ -0,0 +1,515 @@ +<template> + <div class="app-container"> + <el-row :gutter="20"> + <!--部门数据--> + <el-col :span="4" :xs="24"> + <div class="head-container"> + <el-input + v-model="deptName" + placeholder="请输入部门名称" + clearable + size="small" + prefix-icon="el-icon-search" + style="margin-bottom: 20px" + /> + </div> + <div class="head-container"> + <el-tree + :data="deptOptions" + :props="defaultProps" + :expand-on-click-node="false" + :filter-node-method="filterNode" + ref="tree" + default-expand-all + @node-click="handleNodeClick" + /> + </div> + </el-col> + <!--用户数据--> + <el-col :span="20" :xs="24"> + <el-form :inline="true" label-width="68px"> + <el-form-item label="用户名称"> + <el-input + v-model="queryParams.userName" + placeholder="请输入用户名称" + clearable + size="small" + style="width: 240px" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="手机号码"> + <el-input + v-model="queryParams.phonenumber" + placeholder="请输入手机号码" + clearable + size="small" + style="width: 240px" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="状态"> + <el-select + v-model="queryParams.status" + placeholder="用户状态" + clearable + size="small" + style="width: 240px" + > + <el-option + v-for="dict in statusOptions" + :key="dict.dictValue" + :label="dict.dictLabel" + :value="dict.dictValue" + /> + </el-select> + </el-form-item> + <el-form-item label="创建时间"> + <el-date-picker + v-model="dateRange" + size="small" + style="width: 240px" + value-format="yyyy-MM-dd" + type="daterange" + range-separator="-" + start-placeholder="开始日期" + end-placeholder="结束日期" + ></el-date-picker> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> + <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['system:user:add']">新增</el-button> + </el-form-item> + </el-form> + + <el-table v-loading="loading" :data="userList"> + <el-table-column label="用户编号" align="center" prop="userId" /> + <el-table-column label="用户名称" align="center" prop="userName" /> + <el-table-column label="用户昵称" align="center" prop="nickName" /> + <el-table-column label="部门" align="center" prop="dept.deptName" /> + <el-table-column label="手机号码" align="center" prop="phonenumber" width="120" /> + <el-table-column label="状态" align="center"> + <template slot-scope="scope"> + <el-switch + v-model="scope.row.status" + active-value="0" + inactive-value="1" + @change="handleStatusChange(scope.row)" + ></el-switch> + </template> + </el-table-column> + <el-table-column label="创建时间" align="center" prop="createTime" width="160"> + <template slot-scope="scope"> + <span>{{ dateFormat(scope.row.createTime) }}</span> + </template> + </el-table-column> + <el-table-column + label="操作" + align="center" + width="180" + class-name="small-padding fixed-width" + > + <template slot-scope="scope"> + <el-button + size="mini" + type="text" + icon="el-icon-edit" + @click="handleUpdate(scope.row)" + 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> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total>0" + :total="total" + :page.sync="queryParams.pageNum" + :limit.sync="queryParams.pageSize" + @pagination="getList" + /> + </el-col> + </el-row> + + <!-- 添加或修改参数配置对话框 --> + <el-dialog :title="title" :visible.sync="open" width="600px"> + <el-form ref="form" :model="form" :rules="rules" label-width="80px"> + <el-row> + <el-col :span="12"> + <el-form-item label="用户昵称" prop="nickName"> + <el-input v-model="form.nickName" placeholder="请输入用户昵称" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="归属部门" prop="deptId"> + <treeselect v-model="form.deptId" :options="deptOptions" placeholder="请选择归属部门" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="手机号码" prop="phonenumber"> + <el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="邮箱" prop="email"> + <el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="用户名称" prop="userName"> + <el-input v-model="form.userName" placeholder="请输入用户名称" /> + </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-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="用户性别"> + <el-select v-model="form.sex" placeholder="请选择"> + <el-option + v-for="dict in sexOptions" + :key="dict.dictValue" + :label="dict.dictLabel" + :value="dict.dictValue" + ></el-option> + </el-select> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="状态"> + <el-radio-group v-model="form.status"> + <el-radio + v-for="dict in statusOptions" + :key="dict.dictValue" + :label="dict.dictValue" + >{{dict.dictLabel}}</el-radio> + </el-radio-group> + </el-form-item> + </el-col> + + <el-col :span="12"> + <el-form-item label="岗位"> + <el-select v-model="form.postIds" multiple placeholder="请选择"> + <el-option + v-for="item in postOptions" + :key="item.postId" + :label="item.postName" + :value="item.postId" + :disabled="item.status == 1" + ></el-option> + </el-select> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="角色"> + <el-select v-model="form.roleIds" multiple placeholder="请选择"> + <el-option + v-for="item in roleOptions" + :key="item.roleId" + :label="item.roleName" + :value="item.roleId" + :disabled="item.status == 1" + ></el-option> + </el-select> + </el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="备注"> + <el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input> + </el-form-item> + </el-col> + </el-row> + </el-form> + <div slot="footer" class="dialog-footer"> + <el-button type="primary" @click="submitForm">确 定</el-button> + <el-button @click="cancel">取 消</el-button> + </div> + </el-dialog> + </div> +</template> + +<script> +import { listUser, getUser, delUser, addUser, updateUser, resetUserPwd, changeUserStatus } from "@/api/system/user"; +import { treeselect } from "@/api/system/dept"; +import { listPost } from "@/api/system/post"; +import { listRole } from "@/api/system/role"; +import Treeselect from "@riophae/vue-treeselect"; +import "@riophae/vue-treeselect/dist/vue-treeselect.css"; + +export default { + components: { Treeselect }, + data() { + return { + // 遮罩层 + loading: true, + // 总条数 + total: 0, + // 用户表格数据 + userList: null, + // 弹出层标题 + title: "", + // 部门树选项 + deptOptions: undefined, + // 是否显示弹出层 + open: false, + // 部门名称 + deptName: undefined, + // 默认密码 + initPassword: undefined, + // 日期范围 + dateRange: [], + // 状态数据字典 + statusOptions: [], + // 性别状态字典 + sexOptions: [], + // 岗位选项 + postOptions: [], + // 角色选项 + roleOptions: [], + // 表单参数 + form: {}, + defaultProps: { + children: "children", + label: "label" + }, + // 查询参数 + queryParams: { + pageNum: 1, + pageSize: 10, + userName: undefined, + phonenumber: undefined, + status: undefined, + deptId: undefined + }, + // 表单校验 + rules: { + userName: [ + { required: true, message: "用户名称不能为空", trigger: "blur" } + ], + nickName: [ + { required: true, message: "用户昵称不能为空", trigger: "blur" } + ], + deptId: [ + { required: true, message: "归属部门不能为空", trigger: "blur" } + ], + password: [ + { required: true, message: "用户密码不能为空", trigger: "blur" } + ], + email: [ + { + type: "email", + message: "'请输入正确的邮箱地址", + trigger: ["blur", "change"] + } + ], + phonenumber: [ + { + pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, + message: "请输入正确的手机号码", + trigger: "blur" + } + ] + } + }; + }, + watch: { + // 根据名称筛选部门树 + deptName(val) { + this.$refs.tree.filter(val); + } + }, + created() { + this.getList(); + this.getTreeselect(); + this.getDicts("sys_normal_disable").then(response => { + this.statusOptions = response.data; + }); + this.getDicts("sys_user_sex").then(response => { + this.sexOptions = response.data; + }); + this.getConfigKey("sys.user.initPassword").then(response => { + this.initPassword = response.data; + }); + }, + methods: { + /** 查询用户列表 */ + getList() { + this.loading = true; + listUser(this.addDateRange(this.queryParams, this.dateRange)).then(response => { + this.userList = response.rows; + this.total = response.total; + this.loading = false; + } + ); + }, + /** 查询部门下拉树结构 */ + getTreeselect() { + treeselect().then(response => { + this.deptOptions = response.data; + }); + }, + // 筛选节点 + filterNode(value, data) { + if (!value) return true; + return data.label.indexOf(value) !== -1; + }, + // 节点单击事件 + handleNodeClick(data) { + this.queryParams.deptId = data.id; + this.getList(); + }, + /** 查询岗位列表 */ + getPosts() { + listPost().then(response => { + this.postOptions = response.rows; + }); + }, + /** 查询角色列表 */ + getRoles() { + listRole().then(response => { + this.roleOptions = response.rows; + }); + }, + // 用户状态修改 + handleStatusChange(row) { + let text = row.status === "0" ? "启用" : "停用"; + this.$confirm('确认要"' + text + '""' + row.userName + '"用户吗?', "警告", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + }).then(function() { + return changeUserStatus(row.userId, row.status); + }).then(() => { + this.msgSuccess(text + "成功"); + }).catch(function() { + row.status = row.status === "0" ? "1" : "0"; + }); + }, + // 取消按钮 + cancel() { + this.open = false; + this.reset(); + }, + // 表单重置 + reset() { + this.form = { + userId: undefined, + deptId: 100, + userName: undefined, + nickName: undefined, + phonenumber: undefined, + email: undefined, + sex: undefined, + status: "0", + remark: undefined, + postIds: [], + roleIds: [] + }; + this.resetForm("form"); + }, + /** 搜索按钮操作 */ + handleQuery() { + this.queryParams.page = 1; + this.getList(); + }, + /** 新增按钮操作 */ + handleAdd() { + this.reset(); + this.getTreeselect(); + this.getPosts(); + this.getRoles(); + this.open = true; + this.title = "添加用户"; + this.form.password = this.initPassword; + }, + /** 修改按钮操作 */ + handleUpdate(row) { + this.reset(); + this.getTreeselect(); + this.getPosts(); + this.getRoles(); + getUser(row.userId).then(response => { + this.form = response.data; + this.form.postIds = response.postIds; + this.form.roleIds = response.roleIds; + this.open = true; + this.title = "修改用户"; + this.form.password = ""; + }); + }, + /** 重置密码按钮操作 */ + handleResetPwd(row) { + this.$prompt('请输入"' + row.userName + '"的新密码', "提示", { + confirmButtonText: "确定", + cancelButtonText: "取消" + }).then(({ value }) => { + resetUserPwd(row.userId, value).then(response => { + if (response.code === 200) { + this.msgSuccess("修改成功,新密码是:" + value); + } else { + this.msgError(response.msg); + } + }); + }).catch(() => {}); + }, + /** 提交按钮 */ + submitForm: function() { + this.$refs["form"].validate(valid => { + if (valid) { + if (this.form.userId != undefined) { + updateUser(this.form).then(response => { + if (response.code === 200) { + this.msgSuccess("修改成功"); + this.open = false; + this.getList(); + } else { + this.msgError(response.msg); + } + }); + } else { + addUser(this.form).then(response => { + if (response.code === 200) { + this.msgSuccess("新增成功"); + this.open = false; + this.getList(); + } else { + this.msgError(response.msg); + } + }); + } + } + }); + }, + /** 删除按钮操作 */ + handleDelete(row) { + this.$confirm('是否确认删除名称为"' + row.userName + '"的数据项?', "警告", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + }).then(function() { + return delUser(row.userId); + }).then(() => { + this.getList(); + this.msgSuccess("删除成功"); + }).catch(function() {}); + } + } +}; +</script> \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/user/profile/index.vue b/ruoyi-ui/src/views/system/user/profile/index.vue new file mode 100644 index 0000000..3b7a117 --- /dev/null +++ b/ruoyi-ui/src/views/system/user/profile/index.vue @@ -0,0 +1,101 @@ +<template> + <div class="app-container"> + <el-row :gutter="20"> + <el-col :span="6" :xs="24"> + <el-card class="box-card"> + <div slot="header" class="clearfix"> + <span>个人信息</span> + </div> + <div> + <div class="text-center"> + <userAvatar :user="user" /> + </div> + <ul class="list-group list-group-striped"> + <li class="list-group-item"> + <svg-icon icon-class="user" />用户名称 + <div class="pull-right">{{ user.userName }}</div> + </li> + <li class="list-group-item"> + <svg-icon icon-class="phone" />手机号码 + <div class="pull-right">{{ user.phonenumber }}</div> + </li> + <li class="list-group-item"> + <svg-icon icon-class="email" />用户邮箱 + <div class="pull-right">{{ user.email }}</div> + </li> + <li class="list-group-item"> + <svg-icon icon-class="tree" />所属部门 + <div class="pull-right" v-if="user.dept">{{ user.dept.deptName }} / {{ postGroup }}</div> + </li> + <li class="list-group-item"> + <svg-icon icon-class="peoples" />所属角色 + <div class="pull-right">{{ roleGroup }}</div> + </li> + <li class="list-group-item"> + <svg-icon icon-class="date" />创建日期 + <div class="pull-right">2018-08-23 09:11:56</div> + </li> + </ul> + </div> + </el-card> + </el-col> + <el-col :span="18" :xs="24"> + <el-card> + <div slot="header" class="clearfix"> + <span>基本资料</span> + </div> + <el-tabs v-model="activeTab"> + <el-tab-pane label="基本资料" name="userinfo"> + <userInfo :user="user" /> + </el-tab-pane> + <el-tab-pane label="修改密码" name="resetPwd"> + <resetPwd :user="user" /> + </el-tab-pane> + </el-tabs> + </el-card> + </el-col> + </el-row> + </div> +</template> + +<script> +import userAvatar from "./userAvatar"; +import userInfo from "./userInfo"; +import resetPwd from "./resetPwd"; +import { getUserProfile } from "@/api/system/user"; + +export default { + name: "Profile", + components: { userAvatar, userInfo, resetPwd }, + data() { + return { + user: {}, + roleGroup: {}, + postGroup: {}, + activeTab: "userinfo" + }; + }, + created() { + this.getUser(); + }, + methods: { + getUser() { + getUserProfile().then(response => { + this.user = response.data; + this.roleGroup = response.roleGroup; + this.postGroup = response.postGroup; + }); + } + } +}; +</script> + +<style rel="stylesheet/scss" lang="scss"> +.avatar-uploader-icon { + font-size: 28px; + width: 120px; + height: 120px; + line-height: 120px; + text-align: center; +} +</style> diff --git a/ruoyi-ui/src/views/system/user/profile/resetPwd.vue b/ruoyi-ui/src/views/system/user/profile/resetPwd.vue new file mode 100644 index 0000000..380e168 --- /dev/null +++ b/ruoyi-ui/src/views/system/user/profile/resetPwd.vue @@ -0,0 +1,76 @@ +<template> + <el-form ref="form" :model="user" :rules="rules" label-width="80px"> + <el-form-item label="旧密码" prop="oldPassword"> + <el-input v-model="user.oldPassword" placeholder="请输入旧密码" type="password" /> + </el-form-item> + <el-form-item label="新密码" prop="newPassword"> + <el-input v-model="user.newPassword" placeholder="请输入新密码" type="password" /> + </el-form-item> + <el-form-item label="确认密码" prop="confirmPassword"> + <el-input v-model="user.confirmPassword" placeholder="请确认密码" type="password" /> + </el-form-item> + <el-form-item> + <el-button type="primary" size="mini" @click="submit">保存</el-button> + <el-button type="danger" size="mini" @click="close">关闭</el-button> + </el-form-item> + </el-form> +</template> + +<script> +import { updateUserPwd } from "@/api/system/user"; + +export default { + data() { + const equalToPassword = (rule, value, callback) => { + if (this.user.newPassword !== value) { + callback(new Error("两次输入的密码不一致")); + } else { + callback(); + } + }; + return { + test: "1test", + user: { + oldPassword: undefined, + newPassword: undefined, + confirmPassword: undefined + }, + // 表单校验 + rules: { + oldPassword: [ + { required: true, message: "旧密码不能为空", trigger: "blur" } + ], + newPassword: [ + { required: true, message: "新密码不能为空", trigger: "blur" }, + { min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" } + ], + confirmPassword: [ + { required: true, message: "确认密码不能为空", trigger: "blur" }, + { required: true, validator: equalToPassword, trigger: "blur" } + ] + } + }; + }, + methods: { + submit() { + this.$refs["form"].validate(valid => { + if (valid) { + updateUserPwd(this.user.oldPassword, this.user.newPassword).then( + response => { + if (response.code === 200) { + this.msgSuccess("修改成功"); + } else { + this.msgError(response.msg); + } + } + ); + } + }); + }, + close() { + this.$store.dispatch("tagsView/delView", this.$route); + this.$router.push({ path: "/index" }); + } + } +}; +</script> diff --git a/ruoyi-ui/src/views/system/user/profile/userAvatar.vue b/ruoyi-ui/src/views/system/user/profile/userAvatar.vue new file mode 100644 index 0000000..bca0493 --- /dev/null +++ b/ruoyi-ui/src/views/system/user/profile/userAvatar.vue @@ -0,0 +1,135 @@ +<template> + <div> + <img v-bind:src="options.img" @click="editCropper()" title="点击上传头像" class="img-circle img-lg" /> + <el-dialog :title="title" :visible.sync="open" width="800px"> + <el-row> + <el-col :xs="24" :md="12" :style="{height: '350px'}"> + <vue-cropper + ref="cropper" + :img="options.img" + :info="true" + :autoCrop="options.autoCrop" + :autoCropWidth="options.autoCropWidth" + :autoCropHeight="options.autoCropHeight" + :fixedBox="options.fixedBox" + @realTime="realTime" + /> + </el-col> + <el-col :xs="24" :md="12" :style="{height: '350px'}"> + <div class="avatar-upload-preview"> + <img :src="previews.url" :style="previews.img" /> + </div> + </el-col> + </el-row> + <br /> + <el-row> + <el-col :lg="2" :md="2"> + <el-upload :show-file-list="false" :before-upload="beforeUpload"> + <el-button size="small"> + 上传 + <i class="el-icon-upload el-icon--right"></i> + </el-button> + </el-upload> + </el-col> + <el-col :lg="{span: 1, offset: 2}" :md="2"> + <el-button icon="el-icon-plus" size="small" @click="changeScale(1)"></el-button> + </el-col> + <el-col :lg="{span: 1, offset: 1}" :md="2"> + <el-button icon="el-icon-minus" size="small" @click="changeScale(-1)"></el-button> + </el-col> + <el-col :lg="{span: 1, offset: 1}" :md="2"> + <el-button icon="el-icon-refresh-left" size="small" @click="rotateLeft()"></el-button> + </el-col> + <el-col :lg="{span: 1, offset: 1}" :md="2"> + <el-button icon="el-icon-refresh-right" size="small" @click="rotateRight()"></el-button> + </el-col> + <el-col :lg="{span: 2, offset: 6}" :md="2"> + <el-button type="primary" size="small" @click="uploadImg()">上 传</el-button> + </el-col> + </el-row> + </el-dialog> + </div> +</template> + +<script> +import store from "@/store"; +import { VueCropper } from "vue-cropper"; +import { uploadAvatar } from "@/api/system/user"; + +export default { + components: { VueCropper }, + props: { + user: { + type: Object + } + }, + data() { + return { + // 是否显示弹出层 + open: false, + // 弹出层标题 + title: "修改头像", + options: { + img: store.getters.avatar, //裁剪图片的地址 + autoCrop: true, // 是否默认生成截图框 + autoCropWidth: 200, // 默认生成截图框宽度 + autoCropHeight: 200, // 默认生成截图框高度 + fixedBox: true // 固定截图框大小 不允许改变 + }, + previews: {} + }; + }, + methods: { + // 编辑头像 + editCropper() { + this.open = true; + }, + // 向左旋转 + rotateLeft() { + this.$refs.cropper.rotateLeft(); + }, + // 向右旋转 + rotateRight() { + this.$refs.cropper.rotateRight(); + }, + // 图片缩放 + changeScale(num) { + num = num || 1; + this.$refs.cropper.changeScale(num); + }, + // 上传预处理 + beforeUpload(file) { + if (file.type.indexOf("image/") == -1) { + this.msgError("文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件。"); + } else { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = () => { + this.options.img = reader.result; + }; + } + }, + // 上传图片 + uploadImg() { + this.$refs.cropper.getCropBlob(data => { + let formData = new FormData(); + formData.append("avatarfile", data); + uploadAvatar(formData).then(response => { + if (response.code === 200) { + this.open = false; + this.options.img = process.env.VUE_APP_BASE_API + response.imgUrl; + this.msgSuccess("修改成功"); + } else { + this.msgError(response.msg); + } + this.$refs.cropper.clearCrop(); + }); + }); + }, + // 实时预览 + realTime(data) { + this.previews = data; + } + } +}; +</script> \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/user/profile/userInfo.vue b/ruoyi-ui/src/views/system/user/profile/userInfo.vue new file mode 100644 index 0000000..2e8ef99 --- /dev/null +++ b/ruoyi-ui/src/views/system/user/profile/userInfo.vue @@ -0,0 +1,80 @@ +<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-form-item> + <el-form-item label="手机号码" prop="phonenumber"> + <el-input v-model="user.phonenumber" maxlength="11" /> + </el-form-item> + <el-form-item label="邮箱" prop="email"> + <el-input v-model="user.email" maxlength="50" /> + </el-form-item> + <el-form-item label="性别"> + <el-radio-group v-model="user.sex"> + <el-radio label="0">男</el-radio> + <el-radio label="1">女</el-radio> + </el-radio-group> + </el-form-item> + <el-form-item> + <el-button type="primary" size="mini" @click="submit">保存</el-button> + <el-button type="danger" size="mini" @click="close">关闭</el-button> + </el-form-item> + </el-form> +</template> + +<script> +import { updateUserProfile } from "@/api/system/user"; + +export default { + props: { + user: { + type: Object + } + }, + data() { + return { + // 表单校验 + rules: { + nickName: [ + { required: true, message: "用户昵称不能为空", trigger: "blur" } + ], + email: [ + { required: true, message: "邮箱地址不能为空", trigger: "blur" }, + { + type: "email", + message: "'请输入正确的邮箱地址", + trigger: ["blur", "change"] + } + ], + phonenumber: [ + { required: true, message: "手机号码不能为空", trigger: "blur" }, + { + pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, + message: "请输入正确的手机号码", + trigger: "blur" + } + ] + } + }; + }, + methods: { + submit() { + this.$refs["form"].validate(valid => { + if (valid) { + updateUserProfile(this.user).then(response => { + if (response.code === 200) { + this.msgSuccess("修改成功"); + } else { + this.msgError(response.msg); + } + }); + } + }); + }, + close() { + this.$store.dispatch("tagsView/delView", this.$route); + this.$router.push({ path: "/index" }); + } + } +}; +</script> diff --git a/ruoyi-ui/src/views/tool/build/index.vue b/ruoyi-ui/src/views/tool/build/index.vue new file mode 100644 index 0000000..9d4496e --- /dev/null +++ b/ruoyi-ui/src/views/tool/build/index.vue @@ -0,0 +1,5 @@ +<template> + <div class="app-container"> + 构建工具 + </div> +</template> \ No newline at end of file diff --git a/ruoyi-ui/src/views/tool/gen/index.vue b/ruoyi-ui/src/views/tool/gen/index.vue new file mode 100644 index 0000000..c0c0673 --- /dev/null +++ b/ruoyi-ui/src/views/tool/gen/index.vue @@ -0,0 +1,5 @@ +<template> + <div class="app-container"> + 代码生成 + </div> +</template> \ No newline at end of file diff --git a/ruoyi-ui/src/views/tool/swagger/index.vue b/ruoyi-ui/src/views/tool/swagger/index.vue new file mode 100644 index 0000000..00484e2 --- /dev/null +++ b/ruoyi-ui/src/views/tool/swagger/index.vue @@ -0,0 +1,25 @@ +<template> + <div v-loading="loading" :style="'height:'+ height"> + <iframe :src="src" frameborder="no" style="width: 100%;height: 100%" scrolling="auto" /> + </div> +</template> +<script> +export default { + data() { + return { + src: process.env.VUE_APP_BASE_API + "/swagger-ui.html", + height: document.documentElement.clientHeight - 94.5 + "px;", + loading: true + }; + }, + mounted: function() { + setTimeout(() => { + this.loading = false; + }, 230); + const that = this; + window.onresize = function temp() { + that.height = document.documentElement.clientHeight - 94.5 + "px;"; + }; + } +}; +</script> diff --git a/ruoyi-ui/vue.config.js b/ruoyi-ui/vue.config.js new file mode 100644 index 0000000..62589f0 --- /dev/null +++ b/ruoyi-ui/vue.config.js @@ -0,0 +1,130 @@ +'use strict' +const path = require('path') +const defaultSettings = require('./src/settings.js') + +function resolve(dir) { + return path.join(__dirname, dir) +} + +const name = defaultSettings.title || '若依管理系统' // 标题 + +const port = process.env.port || process.env.npm_config_port || 80 // 端口 + +// vue.config.js 配置说明 +//官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions +// 这里只列一部分,具体配置参考文档 +module.exports = { + // 部署生产环境和开发环境下的URL。 + // 默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上 + // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。 + publicPath: process.env.NODE_ENV === "production" ? "./" : "/", + // 在npm run build 或 yarn build 时 ,生成文件的目录名称(要和baseUrl的生产环境路径一致)(默认dist) + outputDir: 'dist', + // 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下) + assetsDir: 'static', + // 是否开启eslint保存检测,有效值:ture | false | 'error' + lintOnSave: process.env.NODE_ENV === 'development', + // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。 + productionSourceMap: false, + // webpack-dev-server 相关配置 + devServer: { + host: '0.0.0.0', + port: port, + proxy: { + // detail: https://cli.vuejs.org/config/#devserver-proxy + [process.env.VUE_APP_BASE_API]: { + target: `http://localhost:8080`, + changeOrigin: true, + pathRewrite: { + ['^' + process.env.VUE_APP_BASE_API]: '' + } + } + }, + disableHostCheck: true + }, + configureWebpack: { + name: name, + resolve: { + alias: { + '@': resolve('src') + } + } + }, + chainWebpack(config) { + config.plugins.delete('preload') // TODO: need test + config.plugins.delete('prefetch') // TODO: need test + + // set svg-sprite-loader + config.module + .rule('svg') + .exclude.add(resolve('src/assets/icons')) + .end() + config.module + .rule('icons') + .test(/\.svg$/) + .include.add(resolve('src/assets/icons')) + .end() + .use('svg-sprite-loader') + .loader('svg-sprite-loader') + .options({ + symbolId: 'icon-[name]' + }) + .end() + + // set preserveWhitespace + config.module + .rule('vue') + .use('vue-loader') + .loader('vue-loader') + .tap(options => { + options.compilerOptions.preserveWhitespace = true + return options + }) + .end() + + config + // https://webpack.js.org/configuration/devtool/#development + .when(process.env.NODE_ENV === 'development', + config => config.devtool('cheap-source-map') + ) + + config + .when(process.env.NODE_ENV !== 'development', + config => { + config + .plugin('ScriptExtHtmlWebpackPlugin') + .after('html') + .use('script-ext-html-webpack-plugin', [{ + // `runtime` must same as runtimeChunk name. default is `runtime` + inline: /runtime\..*\.js$/ + }]) + .end() + config + .optimization.splitChunks({ + chunks: 'all', + cacheGroups: { + libs: { + name: 'chunk-libs', + test: /[\\/]node_modules[\\/]/, + priority: 10, + chunks: 'initial' // only package third parties that are initially dependent + }, + elementUI: { + name: 'chunk-elementUI', // split elementUI into a single package + priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app + test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm + }, + commons: { + name: 'chunk-commons', + test: resolve('src/components'), // can customize your rules + minChunks: 3, // minimum common number + priority: 5, + reuseExistingChunk: true + } + } + }) + config.optimization.runtimeChunk('single') + } + ) + } +} diff --git a/ruoyi/.gitignore b/ruoyi/.gitignore new file mode 100644 index 0000000..4916f7a --- /dev/null +++ b/ruoyi/.gitignore @@ -0,0 +1,43 @@ +###################################################################### +# Build Tools + +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +target/ +!.mvn/wrapper/maven-wrapper.jar + +###################################################################### +# IDE + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +nbproject/private/ +build/* +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +###################################################################### +# Others +*.log +*.xml.versionsBackup + +!*/build/*.java +!*/build/*.html +!*/build/*.xml \ No newline at end of file diff --git a/ruoyi/bin/clean.bat b/ruoyi/bin/clean.bat new file mode 100644 index 0000000..e144025 --- /dev/null +++ b/ruoyi/bin/clean.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [��Ϣ] ��������·���� +echo. + +%~d0 +cd %~dp0 + +cd .. +call mvn clean + +pause \ No newline at end of file diff --git a/ruoyi/bin/package.bat b/ruoyi/bin/package.bat new file mode 100644 index 0000000..af8af8e --- /dev/null +++ b/ruoyi/bin/package.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [��Ϣ] ���Web���̣�����war/jar���ļ��� +echo. + +%~d0 +cd %~dp0 + +cd .. +call mvn clean package -Dmaven.test.skip=true + +pause \ No newline at end of file diff --git a/ruoyi/bin/run-tomcat.bat b/ruoyi/bin/run-tomcat.bat new file mode 100644 index 0000000..5d38ad0 --- /dev/null +++ b/ruoyi/bin/run-tomcat.bat @@ -0,0 +1,14 @@ +@echo off +echo. +echo [��Ϣ] ʹ�� Spring Boot Tomcat ���� Web ���̡� +echo. + +%~d0 +cd %~dp0 + +cd .. +title %cd% +set MAVEN_OPTS=%MAVEN_OPTS% -Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m +call mvn clean spring-boot:run -Dmaven.test.skip=true -U + +pause \ No newline at end of file diff --git a/ruoyi/pom.xml b/ruoyi/pom.xml new file mode 100644 index 0000000..0e04df7 --- /dev/null +++ b/ruoyi/pom.xml @@ -0,0 +1,267 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>com.ruoyi</groupId> + <artifactId>ruoyi</artifactId> + <version>1.0</version> + <packaging>jar</packaging> + + <name>ruoyi</name> + <url>http://www.ruoyi.vip</url> + <description>若依管理系统</description> + + <parent> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-parent</artifactId> + <version>2.1.1.RELEASE</version> + <relativePath /> + </parent> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> + <java.version>1.8</java.version> + <mybatis.spring.boot.starter.version>1.3.2</mybatis.spring.boot.starter.version> + <pagehelper.spring.boot.starter.version>1.2.5</pagehelper.spring.boot.starter.version> + <fastjson.version>1.2.47</fastjson.version> + <druid.version>1.1.14</druid.version> + <commons.io.version>2.5</commons.io.version> + <commons.fileupload.version>1.3.3</commons.fileupload.version> + <bitwalker.version>1.19</bitwalker.version> + <jwt.version>0.9.0</jwt.version> + <swagger.version>2.9.2</swagger.version> + <oshi.version>3.9.1</oshi.version> + </properties> + + <dependencies> + + <!-- SpringBoot 核心包 --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter</artifactId> + <!-- + <exclusions> + <exclusion> + <artifactId>spring-boot-starter-tomcat</artifactId> + <groupId>org.springframework.boot</groupId> + </exclusion> + </exclusions> + --> + </dependency> + + <!-- SpringBoot 测试 --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + </dependency> + + <!-- SpringBoot 拦截器 --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-aop</artifactId> + </dependency> + + <!-- SpringBoot Web容器 --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + </dependency> + + <!-- spring-boot-devtools --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-devtools</artifactId> + <optional>true</optional> <!-- 表示依赖不会传递 --> + </dependency> + + <!-- spring security 安全认证 --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-security</artifactId> + </dependency> + + <!-- redis 缓存操作 --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-data-redis</artifactId> + </dependency> + + <!-- pool 对象池 --> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-pool2</artifactId> + </dependency> + + <!-- Mysql驱动包 --> + <dependency> + <groupId>mysql</groupId> + <artifactId>mysql-connector-java</artifactId> + <scope>runtime</scope> + </dependency> + + <!-- SpringBoot集成mybatis框架 --> + <dependency> + <groupId>org.mybatis.spring.boot</groupId> + <artifactId>mybatis-spring-boot-starter</artifactId> + <version>${mybatis.spring.boot.starter.version}</version> + </dependency> + + <!-- pagehelper 分页插件 --> + <dependency> + <groupId>com.github.pagehelper</groupId> + <artifactId>pagehelper-spring-boot-starter</artifactId> + <version>${pagehelper.spring.boot.starter.version}</version> + </dependency> + + <!--阿里数据库连接池 --> + <dependency> + <groupId>com.alibaba</groupId> + <artifactId>druid-spring-boot-starter</artifactId> + <version>${druid.version}</version> + </dependency> + + <!--常用工具类 --> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + </dependency> + + <!--io常用工具类 --> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>${commons.io.version}</version> + </dependency> + + <!--文件上传工具类 --> + <dependency> + <groupId>commons-fileupload</groupId> + <artifactId>commons-fileupload</artifactId> + <version>${commons.fileupload.version}</version> + </dependency> + + <!-- 解析客户端操作系统、浏览器等 --> + <dependency> + <groupId>eu.bitwalker</groupId> + <artifactId>UserAgentUtils</artifactId> + <version>${bitwalker.version}</version> + </dependency> + + <!-- 阿里JSON解析器 --> + <dependency> + <groupId>com.alibaba</groupId> + <artifactId>fastjson</artifactId> + <version>${fastjson.version}</version> + </dependency> + + <!--Spring框架基本的核心工具--> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context-support</artifactId> + </dependency> + + <!--Token生成与解析--> + <dependency> + <groupId>io.jsonwebtoken</groupId> + <artifactId>jjwt</artifactId> + <version>${jwt.version}</version> + </dependency> + + <!-- swagger2--> + <dependency> + <groupId>io.springfox</groupId> + <artifactId>springfox-swagger2</artifactId> + <version>${swagger.version}</version> + <exclusions> + <exclusion> + <groupId>io.swagger</groupId> + <artifactId>swagger-annotations</artifactId> + </exclusion> + <exclusion> + <groupId>io.swagger</groupId> + <artifactId>swagger-models</artifactId> + </exclusion> + </exclusions> + </dependency> + + <!--防止进入swagger页面报类型转换错误,排除2.9.2中的引用,手动增加1.5.21版本--> + <dependency> + <groupId>io.swagger</groupId> + <artifactId>swagger-annotations</artifactId> + <version>1.5.21</version> + </dependency> + + <dependency> + <groupId>io.swagger</groupId> + <artifactId>swagger-models</artifactId> + <version>1.5.21</version> + </dependency> + + <!-- swagger2-UI--> + <dependency> + <groupId>io.springfox</groupId> + <artifactId>springfox-swagger-ui</artifactId> + <version>${swagger.version}</version> + </dependency> + + <!-- 获取系统信息 --> + <dependency> + <groupId>com.github.oshi</groupId> + <artifactId>oshi-core</artifactId> + <version>${oshi.version}</version> + </dependency> + + <dependency> + <groupId>net.java.dev.jna</groupId> + <artifactId>jna</artifactId> + </dependency> + + <dependency> + <groupId>net.java.dev.jna</groupId> + <artifactId>jna-platform</artifactId> + </dependency> + + </dependencies> + + <build> + <finalName>${project.artifactId}</finalName> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <configuration> + <fork>true</fork> <!-- 如果没有该配置,devtools不会生效 --> + </configuration> + </plugin> + </plugins> + </build> + + <repositories> + <repository> + <id>public</id> + <name>aliyun nexus</name> + <url>http://maven.aliyun.com/nexus/content/groups/public/</url> + <releases> + <enabled>true</enabled> + </releases> + </repository> + </repositories> + + <pluginRepositories> + <pluginRepository> + <id>public</id> + <name>aliyun nexus</name> + <url>http://maven.aliyun.com/nexus/content/groups/public/</url> + <releases> + <enabled>true</enabled> + </releases> + <snapshots> + <enabled>false</enabled> + </snapshots> + </pluginRepository> + </pluginRepositories> + +</project> \ No newline at end of file diff --git a/ruoyi/ry.sh b/ruoyi/ry.sh new file mode 100644 index 0000000..52f80bd --- /dev/null +++ b/ruoyi/ry.sh @@ -0,0 +1,86 @@ +#!/bin/bash + +AppName=ruoyi.jar + +#JVM参数 +JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512M -Xmx512M -XX:PermSize=256M -XX:MaxPermSize=512M -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC" +APP_HOME=`pwd` +LOG_PATH=$APP_HOME/logs/$AppName.log + +if [ "$1" = "" ]; +then + echo -e "\033[0;31m 未输入操作名 \033[0m \033[0;34m {start|stop|restart|status} \033[0m" + exit 1 +fi + +if [ "$AppName" = "" ]; +then + echo -e "\033[0;31m 未输入应用名 \033[0m" + exit 1 +fi + +function start() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + + if [ x"$PID" != x"" ]; then + echo "$AppName is running..." + else + nohup java -jar $JVM_OPTS target/$AppName > /dev/null 2>&1 & + echo "Start $AppName success..." + fi +} + +function stop() +{ + echo "Stop $AppName" + + PID="" + query(){ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + } + + query + if [ x"$PID" != x"" ]; then + kill -TERM $PID + echo "$AppName (pid:$PID) exiting..." + while [ x"$PID" != x"" ] + do + sleep 1 + query + done + echo "$AppName exited." + else + echo "$AppName already stopped." + fi +} + +function restart() +{ + stop + sleep 2 + start +} + +function status() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|wc -l` + if [ $PID != 0 ];then + echo "$AppName is running..." + else + echo "$AppName is not running..." + fi +} + +case $1 in + start) + start;; + stop) + stop;; + restart) + restart;; + status) + status;; + *) + +esac diff --git a/ruoyi/sql/ry_20191008.sql b/ruoyi/sql/ry_20191008.sql new file mode 100644 index 0000000..7d75501 --- /dev/null +++ b/ruoyi/sql/ry_20191008.sql @@ -0,0 +1,693 @@ +-- ---------------------------- +-- 1、部门表 +-- ---------------------------- +drop table if exists sys_dept; +create table sys_dept ( + dept_id bigint(20) not null auto_increment comment '部门id', + parent_id bigint(20) default 0 comment '父部门id', + ancestors varchar(50) default '' comment '祖级列表', + dept_name varchar(30) default '' comment '部门名称', + order_num int(4) default 0 comment '显示顺序', + leader varchar(20) default null comment '负责人', + phone varchar(11) default null comment '联系电话', + email varchar(50) default null comment '邮箱', + status char(1) default '0' comment '部门状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + primary key (dept_id) +) engine=innodb auto_increment=200 comment = '部门表'; + +-- ---------------------------- +-- 初始化-部门表数据 +-- ---------------------------- +insert into sys_dept values(100, 0, '0', '若依科技', 0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); +insert into sys_dept values(101, 100, '0,100', '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); +insert into sys_dept values(102, 100, '0,100', '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); +insert into sys_dept values(103, 101, '0,100,101', '研发部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); +insert into sys_dept values(104, 101, '0,100,101', '市场部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); +insert into sys_dept values(105, 101, '0,100,101', '测试部门', 3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); +insert into sys_dept values(106, 101, '0,100,101', '财务部门', 4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); +insert into sys_dept values(107, 101, '0,100,101', '运维部门', 5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); +insert into sys_dept values(108, 102, '0,100,102', '市场部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); +insert into sys_dept values(109, 102, '0,100,102', '财务部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); + + +-- ---------------------------- +-- 2、用户信息表 +-- ---------------------------- +drop table if exists sys_user; +create table sys_user ( + user_id bigint(20) not null auto_increment comment '用户ID', + dept_id bigint(20) default null comment '部门ID', + user_name varchar(30) not null comment '用户账号', + nick_name varchar(30) not null comment '用户昵称', + user_type varchar(2) default '00' comment '用户类型(00系统用户)', + email varchar(50) default '' comment '用户邮箱', + phonenumber varchar(11) default '' comment '手机号码', + sex char(1) default '0' comment '用户性别(0男 1女 2未知)', + avatar varchar(100) default '' comment '头像地址', + password varchar(100) default '' comment '密码', + status char(1) default '0' comment '帐号状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + login_ip varchar(50) default '' comment '最后登陆IP', + login_date datetime comment '最后登陆时间', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (user_id) +) engine=innodb auto_increment=100 comment = '用户信息表'; + +-- ---------------------------- +-- 初始化-用户信息表数据 +-- ---------------------------- +insert into sys_user values(1, 103, 'admin', '若依', '00', 'ry@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', '2018-03-16 11-33-00', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '管理员'); +insert into sys_user values(2, 105, 'ry', '若依', '00', 'ry@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', '2018-03-16 11-33-00', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '测试员'); + + +-- ---------------------------- +-- 3、岗位信息表 +-- ---------------------------- +drop table if exists sys_post; +create table sys_post +( + post_id bigint(20) not null auto_increment comment '岗位ID', + post_code varchar(64) not null comment '岗位编码', + post_name varchar(50) not null comment '岗位名称', + post_sort int(4) not null comment '显示顺序', + status char(1) not null comment '状态(0正常 1停用)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (post_id) +) engine=innodb comment = '岗位信息表'; + +-- ---------------------------- +-- 初始化-岗位信息表数据 +-- ---------------------------- +insert into sys_post values(1, 'ceo', '董事长', 1, '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_post values(2, 'se', '项目经理', 2, '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_post values(3, 'hr', '人力资源', 3, '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_post values(4, 'user', '普通员工', 4, '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); + + +-- ---------------------------- +-- 4、角色信息表 +-- ---------------------------- +drop table if exists sys_role; +create table sys_role ( + role_id bigint(20) not null auto_increment comment '角色ID', + role_name varchar(30) not null comment '角色名称', + role_key varchar(100) not null comment '角色权限字符串', + role_sort int(4) not null comment '显示顺序', + data_scope char(1) default '1' comment '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)', + status char(1) not null comment '角色状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (role_id) +) engine=innodb auto_increment=100 comment = '角色信息表'; + +-- ---------------------------- +-- 初始化-角色信息表数据 +-- ---------------------------- +insert into sys_role values('1', '管理员', 'admin', 1, 1, '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '管理员'); +insert into sys_role values('2', '普通角色', 'common', 2, 2, '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '普通角色'); + + +-- ---------------------------- +-- 5、菜单权限表 +-- ---------------------------- +drop table if exists sys_menu; +create table sys_menu ( + menu_id bigint(20) not null auto_increment comment '菜单ID', + menu_name varchar(50) not null comment '菜单名称', + parent_id bigint(20) default 0 comment '父菜单ID', + order_num int(4) default 0 comment '显示顺序', + path varchar(200) default '' comment '路由地址', + component varchar(255) default null comment '组件路径', + is_frame int(1) default 1 comment '是否为外链(0是 1否)', + menu_type char(1) default '' comment '菜单类型(0目录 1菜单 2按钮)', + visible char(1) default 0 comment '菜单状态(0显示 1隐藏)', + perms varchar(100) default null comment '权限标识', + icon varchar(100) default '#' comment '菜单图标', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default '' comment '备注', + primary key (menu_id) +) engine=innodb auto_increment=2000 comment = '菜单权限表'; + +-- ---------------------------- +-- 初始化-菜单信息表数据 +-- ---------------------------- +-- 一级菜单 +insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, 1, 'M', '0', '', 'system', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '系统管理目录'); +insert into sys_menu values('2', '系统监控', '0', '2', 'monitor', null, 1, 'M', '0', '', 'monitor', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '系统监控目录'); +insert into sys_menu values('3', '系统工具', '0', '3', 'tool', null, 1, 'M', '0', '', 'tool', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '系统工具目录'); +INSERT INTO sys_menu VALUES('4', '若依官网', '0', '4', 'http://ruoyi.vip', NULL , 0, 'M', '0', '', 'guide', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '若依官网'); +-- 二级菜单 +insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', 1, 'C', '0', 'system:user:list', 'user', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '用户管理菜单'); +insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', 1, 'C', '0', 'system:role:list', 'peoples', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '角色管理菜单'); +insert into sys_menu values('102', '菜单管理', '1', '3', 'menu', 'system/menu/index', 1, 'C', '0', 'system:menu:list', 'tree-table', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '菜单管理菜单'); +insert into sys_menu values('103', '部门管理', '1', '4', 'dept', 'system/dept/index', 1, 'C', '0', 'system:dept:list', 'tree', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '部门管理菜单'); +insert into sys_menu values('104', '岗位管理', '1', '5', 'post', 'system/post/index', 1, 'C', '0', 'system:post:list', 'post', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '岗位管理菜单'); +insert into sys_menu values('105', '字典管理', '1', '6', 'dict', 'system/dict/index', 1, 'C', '0', 'system:dict:list', 'dict', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '字典管理菜单'); +insert into sys_menu values('106', '参数设置', '1', '7', 'config', 'system/config/index', 1, 'C', '0', 'system:config:list', 'edit', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '参数设置菜单'); +insert into sys_menu values('107', '通知公告', '1', '8', 'notice', 'system/notice/index', 1, 'C', '0', 'system:notice:list', 'message', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '通知公告菜单'); +insert into sys_menu values('108', '日志管理', '1', '9', 'log', 'system/log/index', 1, 'M', '0', '', 'log', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '日志管理菜单'); +insert into sys_menu values('109', '在线用户', '2', '1', 'online', 'monitor/online/index', 1, 'C', '0', 'monitor:online:list', 'online', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '在线用户菜单'); +insert into sys_menu values('110', '定时任务', '2', '2', 'job', 'monitor/job/index', 1, 'C', '0', 'monitor:job:list', 'job', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '定时任务菜单'); +insert into sys_menu values('111', '数据监控', '2', '3', 'druid', 'monitor/druid/index', 1, 'C', '0', 'monitor:druid:list', 'druid', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '数据监控菜单'); +insert into sys_menu values('112', '服务监控', '2', '4', 'server', 'monitor/server/index', 1, 'C', '0', 'monitor:server:list', 'server', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '服务监控菜单'); +insert into sys_menu values('113', '表单构建', '3', '1', 'build', 'tool/build/index', 1 ,'C', '0', 'tool:build:list', 'build', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '表单构建菜单'); +insert into sys_menu values('114', '代码生成', '3', '2', 'server', 'tool/gen/index', 1, 'C', '0', 'tool:gen:list', 'code', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '代码生成菜单'); +insert into sys_menu values('115', '系统接口', '3', '3', 'swagger', 'tool/swagger/index', 1, 'C', '0', 'tool:swagger:list', 'swagger', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '系统接口菜单'); +-- 三级菜单 +insert into sys_menu values('500', '操作日志', '108', '1', 'operlog', 'monitor/operlog/index', 1, 'C', '0', 'monitor:operlog:list', 'form', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '操作日志菜单'); +insert into sys_menu values('501', '登录日志', '108', '2', 'logininfor', 'monitor/logininfor/index', 1, 'C', '0', 'monitor:logininfor:list', 'logininfor', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '登录日志菜单'); +-- 用户管理按钮 +insert into sys_menu values('1001', '用户查询', '100', '1', '', '', 1, 'F', '0', 'system:user:query', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1002', '用户新增', '100', '2', '', '', 1, 'F', '0', 'system:user:add', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1003', '用户修改', '100', '3', '', '', 1, 'F', '0', 'system:user:edit', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1004', '用户删除', '100', '4', '', '', 1, 'F', '0', 'system:user:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1005', '用户导出', '100', '5', '', '', 1, 'F', '0', 'system:user:export', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1006', '用户导入', '100', '6', '', '', 1, 'F', '0', 'system:user:import', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1007', '重置密码', '100', '7', '', '', 1, 'F', '0', 'system:user:resetPwd', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +-- 角色管理按钮 +insert into sys_menu values('1008', '角色查询', '101', '1', '', '', 1, 'F', '0', 'system:role:query', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1009', '角色新增', '101', '2', '', '', 1, 'F', '0', 'system:role:add', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1010', '角色修改', '101', '3', '', '', 1, 'F', '0', 'system:role:edit', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1011', '角色删除', '101', '4', '', '', 1, 'F', '0', 'system:role:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1012', '角色导出', '101', '5', '', '', 1, 'F', '0', 'system:role:export', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +-- 菜单管理按钮 +insert into sys_menu values('1013', '菜单查询', '102', '1', '', '', 1, 'F', '0', 'system:menu:query', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1014', '菜单新增', '102', '2', '', '', 1, 'F', '0', 'system:menu:add', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1015', '菜单修改', '102', '3', '', '', 1, 'F', '0', 'system:menu:edit', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1016', '菜单删除', '102', '4', '', '', 1, 'F', '0', 'system:menu:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +-- 部门管理按钮 +insert into sys_menu values('1017', '部门查询', '103', '1', '', '', 1, 'F', '0', 'system:dept:query', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1018', '部门新增', '103', '2', '', '', 1, 'F', '0', 'system:dept:add', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1019', '部门修改', '103', '3', '', '', 1, 'F', '0', 'system:dept:edit', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1020', '部门删除', '103', '4', '', '', 1, 'F', '0', 'system:dept:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +-- 岗位管理按钮 +insert into sys_menu values('1021', '岗位查询', '104', '1', '', '', 1, 'F', '0', 'system:post:query', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1022', '岗位新增', '104', '2', '', '', 1, 'F', '0', 'system:post:add', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1023', '岗位修改', '104', '3', '', '', 1, 'F', '0', 'system:post:edit', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1024', '岗位删除', '104', '4', '', '', 1, 'F', '0', 'system:post:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1025', '岗位导出', '104', '5', '', '', 1, 'F', '0', 'system:post:export', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +-- 字典管理按钮 +insert into sys_menu values('1026', '字典查询', '105', '1', '#', '', 1, 'F', '0', 'system:dict:query', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1027', '字典新增', '105', '2', '#', '', 1, 'F', '0', 'system:dict:add', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1028', '字典修改', '105', '3', '#', '', 1, 'F', '0', 'system:dict:edit', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1029', '字典删除', '105', '4', '#', '', 1, 'F', '0', 'system:dict:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1030', '字典导出', '105', '5', '#', '', 1, 'F', '0', 'system:dict:export', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +-- 参数设置按钮 +insert into sys_menu values('1031', '参数查询', '106', '1', '#', '', 1, 'F', '0', 'system:config:query', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1032', '参数新增', '106', '2', '#', '', 1, 'F', '0', 'system:config:add', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1033', '参数修改', '106', '3', '#', '', 1, 'F', '0', 'system:config:edit', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1034', '参数删除', '106', '4', '#', '', 1, 'F', '0', 'system:config:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1035', '参数导出', '106', '5', '#', '', 1, 'F', '0', 'system:config:export', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +-- 通知公告按钮 +insert into sys_menu values('1036', '公告查询', '107', '1', '#', '', 1, 'F', '0', 'system:notice:query', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1037', '公告新增', '107', '2', '#', '', 1, 'F', '0', 'system:notice:add', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1038', '公告修改', '107', '3', '#', '', 1, 'F', '0', 'system:notice:edit', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1039', '公告删除', '107', '4', '#', '', 1, 'F', '0', 'system:notice:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +-- 操作日志按钮 +insert into sys_menu values('1040', '操作查询', '500', '1', '#', '', 1, 'F', '0', 'monitor:operlog:query', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1041', '操作删除', '500', '2', '#', '', 1, 'F', '0', 'monitor:operlog:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', 1, 'F', '0', 'monitor:operlog:export', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +-- 登录日志按钮 +insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', 1, 'F', '0', 'monitor:logininfor:query', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', 1, 'F', '0', 'monitor:logininfor:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', 1, 'F', '0', 'monitor:logininfor:export', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +-- 在线用户按钮 +insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', 1, 'F', '0', 'monitor:online:query', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', 1, 'F', '0', 'monitor:online:batchLogout', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1048', '单条强退', '109', '3', '#', '', 1, 'F', '0', 'monitor:online:forceLogout', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +-- 定时任务按钮 +insert into sys_menu values('1049', '任务查询', '110', '1', '#', '', 1, 'F', '0', 'monitor:job:query', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1050', '任务新增', '110', '2', '#', '', 1, 'F', '0', 'monitor:job:add', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1051', '任务修改', '110', '3', '#', '', 1, 'F', '0', 'monitor:job:edit', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1052', '任务删除', '110', '4', '#', '', 1, 'F', '0', 'monitor:job:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1053', '状态修改', '110', '5', '#', '', 1, 'F', '0', 'monitor:job:changeStatus', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1054', '任务导出', '110', '7', '#', '', 1, 'F', '0', 'monitor:job:export', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +-- 代码生成按钮 +insert into sys_menu values('1055', '生成查询', '114', '1', '#', '', 1, 'F', '0', 'tool:gen:query', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1056', '生成修改', '114', '2', '#', '', 1, 'F', '0', 'tool:gen:edit', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1057', '生成删除', '114', '3', '#', '', 1, 'F', '0', 'tool:gen:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1058', '预览代码', '114', '4', '#', '', 1, 'F', '0', 'tool:gen:preview', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_menu values('1059', '生成代码', '114', '5', '#', '', 1, 'F', '0', 'tool:gen:code', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); + + +-- ---------------------------- +-- 6、用户和角色关联表 用户N-1角色 +-- ---------------------------- +drop table if exists sys_user_role; +create table sys_user_role ( + user_id bigint(20) not null comment '用户ID', + role_id bigint(20) not null comment '角色ID', + primary key(user_id, role_id) +) engine=innodb comment = '用户和角色关联表'; + +-- ---------------------------- +-- 初始化-用户和角色关联表数据 +-- ---------------------------- +insert into sys_user_role values ('1', '1'); +insert into sys_user_role values ('2', '2'); + + +-- ---------------------------- +-- 7、角色和菜单关联表 角色1-N菜单 +-- ---------------------------- +drop table if exists sys_role_menu; +create table sys_role_menu ( + role_id bigint(20) not null comment '角色ID', + menu_id bigint(20) not null comment '菜单ID', + primary key(role_id, menu_id) +) engine=innodb comment = '角色和菜单关联表'; + +-- ---------------------------- +-- 初始化-角色和菜单关联表数据 +-- ---------------------------- +insert into sys_role_menu values ('2', '1'); +insert into sys_role_menu values ('2', '2'); +insert into sys_role_menu values ('2', '3'); +insert into sys_role_menu values ('2', '4'); +insert into sys_role_menu values ('2', '100'); +insert into sys_role_menu values ('2', '101'); +insert into sys_role_menu values ('2', '102'); +insert into sys_role_menu values ('2', '103'); +insert into sys_role_menu values ('2', '104'); +insert into sys_role_menu values ('2', '105'); +insert into sys_role_menu values ('2', '106'); +insert into sys_role_menu values ('2', '107'); +insert into sys_role_menu values ('2', '108'); +insert into sys_role_menu values ('2', '109'); +insert into sys_role_menu values ('2', '110'); +insert into sys_role_menu values ('2', '111'); +insert into sys_role_menu values ('2', '112'); +insert into sys_role_menu values ('2', '113'); +insert into sys_role_menu values ('2', '114'); +insert into sys_role_menu values ('2', '115'); +insert into sys_role_menu values ('2', '500'); +insert into sys_role_menu values ('2', '501'); +insert into sys_role_menu values ('2', '1000'); +insert into sys_role_menu values ('2', '1001'); +insert into sys_role_menu values ('2', '1002'); +insert into sys_role_menu values ('2', '1003'); +insert into sys_role_menu values ('2', '1004'); +insert into sys_role_menu values ('2', '1005'); +insert into sys_role_menu values ('2', '1006'); +insert into sys_role_menu values ('2', '1007'); +insert into sys_role_menu values ('2', '1008'); +insert into sys_role_menu values ('2', '1009'); +insert into sys_role_menu values ('2', '1010'); +insert into sys_role_menu values ('2', '1011'); +insert into sys_role_menu values ('2', '1012'); +insert into sys_role_menu values ('2', '1013'); +insert into sys_role_menu values ('2', '1014'); +insert into sys_role_menu values ('2', '1015'); +insert into sys_role_menu values ('2', '1016'); +insert into sys_role_menu values ('2', '1017'); +insert into sys_role_menu values ('2', '1018'); +insert into sys_role_menu values ('2', '1019'); +insert into sys_role_menu values ('2', '1020'); +insert into sys_role_menu values ('2', '1021'); +insert into sys_role_menu values ('2', '1022'); +insert into sys_role_menu values ('2', '1023'); +insert into sys_role_menu values ('2', '1024'); +insert into sys_role_menu values ('2', '1025'); +insert into sys_role_menu values ('2', '1026'); +insert into sys_role_menu values ('2', '1027'); +insert into sys_role_menu values ('2', '1028'); +insert into sys_role_menu values ('2', '1029'); +insert into sys_role_menu values ('2', '1030'); +insert into sys_role_menu values ('2', '1031'); +insert into sys_role_menu values ('2', '1032'); +insert into sys_role_menu values ('2', '1033'); +insert into sys_role_menu values ('2', '1034'); +insert into sys_role_menu values ('2', '1035'); +insert into sys_role_menu values ('2', '1036'); +insert into sys_role_menu values ('2', '1037'); +insert into sys_role_menu values ('2', '1038'); +insert into sys_role_menu values ('2', '1039'); +insert into sys_role_menu values ('2', '1040'); +insert into sys_role_menu values ('2', '1041'); +insert into sys_role_menu values ('2', '1042'); +insert into sys_role_menu values ('2', '1043'); +insert into sys_role_menu values ('2', '1044'); +insert into sys_role_menu values ('2', '1045'); +insert into sys_role_menu values ('2', '1046'); +insert into sys_role_menu values ('2', '1047'); +insert into sys_role_menu values ('2', '1048'); +insert into sys_role_menu values ('2', '1049'); +insert into sys_role_menu values ('2', '1050'); +insert into sys_role_menu values ('2', '1051'); +insert into sys_role_menu values ('2', '1052'); +insert into sys_role_menu values ('2', '1053'); +insert into sys_role_menu values ('2', '1054'); +insert into sys_role_menu values ('2', '1055'); +insert into sys_role_menu values ('2', '1056'); +insert into sys_role_menu values ('2', '1057'); +insert into sys_role_menu values ('2', '1058'); +insert into sys_role_menu values ('2', '1059'); + +-- ---------------------------- +-- 8、角色和部门关联表 角色1-N部门 +-- ---------------------------- +drop table if exists sys_role_dept; +create table sys_role_dept ( + role_id bigint(20) not null comment '角色ID', + dept_id bigint(20) not null comment '部门ID', + primary key(role_id, dept_id) +) engine=innodb comment = '角色和部门关联表'; + +-- ---------------------------- +-- 初始化-角色和部门关联表数据 +-- ---------------------------- +insert into sys_role_dept values ('2', '100'); +insert into sys_role_dept values ('2', '101'); +insert into sys_role_dept values ('2', '105'); + + +-- ---------------------------- +-- 9、用户与岗位关联表 用户1-N岗位 +-- ---------------------------- +drop table if exists sys_user_post; +create table sys_user_post +( + user_id bigint(20) not null comment '用户ID', + post_id bigint(20) not null comment '岗位ID', + primary key (user_id, post_id) +) engine=innodb comment = '用户与岗位关联表'; + +-- ---------------------------- +-- 初始化-用户与岗位关联表数据 +-- ---------------------------- +insert into sys_user_post values ('1', '1'); +insert into sys_user_post values ('2', '2'); + + +-- ---------------------------- +-- 10、操作日志记录 +-- ---------------------------- +drop table if exists sys_oper_log; +create table sys_oper_log ( + oper_id bigint(20) not null auto_increment comment '日志主键', + title varchar(50) default '' comment '模块标题', + business_type int(2) default 0 comment '业务类型(0其它 1新增 2修改 3删除)', + method varchar(100) default '' comment '方法名称', + request_method varchar(10) default '' comment '请求方式', + operator_type int(1) default 0 comment '操作类别(0其它 1后台用户 2手机端用户)', + oper_name varchar(50) default '' comment '操作人员', + dept_name varchar(50) default '' comment '部门名称', + oper_url varchar(255) default '' comment '请求URL', + oper_ip varchar(50) default '' comment '主机地址', + oper_location varchar(255) default '' comment '操作地点', + oper_param varchar(2000) default '' comment '请求参数', + json_result varchar(2000) default '' comment '返回参数', + status int(1) default 0 comment '操作状态(0正常 1异常)', + error_msg varchar(2000) default '' comment '错误消息', + oper_time datetime comment '操作时间', + primary key (oper_id) +) engine=innodb auto_increment=100 comment = '操作日志记录'; + + +-- ---------------------------- +-- 11、字典类型表 +-- ---------------------------- +drop table if exists sys_dict_type; +create table sys_dict_type +( + dict_id bigint(20) not null auto_increment comment '字典主键', + dict_name varchar(100) default '' comment '字典名称', + dict_type varchar(100) default '' comment '字典类型', + status char(1) default '0' comment '状态(0正常 1停用)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (dict_id), + unique (dict_type) +) engine=innodb auto_increment=100 comment = '字典类型表'; + +insert into sys_dict_type values(1, '用户性别', 'sys_user_sex', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '用户性别列表'); +insert into sys_dict_type values(2, '菜单状态', 'sys_show_hide', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '菜单状态列表'); +insert into sys_dict_type values(3, '系统开关', 'sys_normal_disable', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '系统开关列表'); +insert into sys_dict_type values(4, '任务状态', 'sys_job_status', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '任务状态列表'); +insert into sys_dict_type values(5, '任务分组', 'sys_job_group', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '任务分组列表'); +insert into sys_dict_type values(6, '系统是否', 'sys_yes_no', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '系统是否列表'); +insert into sys_dict_type values(7, '通知类型', 'sys_notice_type', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '通知类型列表'); +insert into sys_dict_type values(8, '通知状态', 'sys_notice_status', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '通知状态列表'); +insert into sys_dict_type values(9, '操作类型', 'sys_oper_type', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '操作类型列表'); +insert into sys_dict_type values(10, '系统状态', 'sys_common_status', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '登录状态列表'); + + +-- ---------------------------- +-- 12、字典数据表 +-- ---------------------------- +drop table if exists sys_dict_data; +create table sys_dict_data +( + dict_code bigint(20) not null auto_increment comment '字典编码', + dict_sort int(4) default 0 comment '字典排序', + dict_label varchar(100) default '' comment '字典标签', + dict_value varchar(100) default '' comment '字典键值', + dict_type varchar(100) default '' comment '字典类型', + css_class varchar(100) default null comment '样式属性(其他样式扩展)', + list_class varchar(100) default null comment '表格回显样式', + is_default char(1) default 'N' comment '是否默认(Y是 N否)', + status char(1) default '0' comment '状态(0正常 1停用)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (dict_code) +) engine=innodb auto_increment=100 comment = '字典数据表'; + +insert into sys_dict_data values(1, 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '性别男'); +insert into sys_dict_data values(2, 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '性别女'); +insert into sys_dict_data values(3, 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '性别未知'); +insert into sys_dict_data values(4, 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '显示菜单'); +insert into sys_dict_data values(5, 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '隐藏菜单'); +insert into sys_dict_data values(6, 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '正常状态'); +insert into sys_dict_data values(7, 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '停用状态'); +insert into sys_dict_data values(8, 1, '正常', '0', 'sys_job_status', '', 'primary', 'Y', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '正常状态'); +insert into sys_dict_data values(9, 2, '暂停', '1', 'sys_job_status', '', 'danger', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '停用状态'); +insert into sys_dict_data values(10, 1, '默认', 'DEFAULT', 'sys_job_group', '', '', 'Y', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '默认分组'); +insert into sys_dict_data values(11, 2, '系统', 'SYSTEM', 'sys_job_group', '', '', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '系统分组'); +insert into sys_dict_data values(12, 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '系统默认是'); +insert into sys_dict_data values(13, 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '系统默认否'); +insert into sys_dict_data values(14, 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '通知'); +insert into sys_dict_data values(15, 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '公告'); +insert into sys_dict_data values(16, 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '正常状态'); +insert into sys_dict_data values(17, 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '关闭状态'); +insert into sys_dict_data values(18, 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '新增操作'); +insert into sys_dict_data values(19, 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '修改操作'); +insert into sys_dict_data values(20, 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '删除操作'); +insert into sys_dict_data values(21, 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '授权操作'); +insert into sys_dict_data values(22, 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '导出操作'); +insert into sys_dict_data values(23, 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '导入操作'); +insert into sys_dict_data values(24, 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '强退操作'); +insert into sys_dict_data values(25, 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '生成操作'); +insert into sys_dict_data values(26, 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '清空操作'); +insert into sys_dict_data values(27, 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '正常状态'); +insert into sys_dict_data values(28, 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '停用状态'); + + +-- ---------------------------- +-- 13、参数配置表 +-- ---------------------------- +drop table if exists sys_config; +create table sys_config ( + config_id int(5) not null auto_increment comment '参数主键', + config_name varchar(100) default '' comment '参数名称', + config_key varchar(100) default '' comment '参数键名', + config_value varchar(500) default '' comment '参数键值', + config_type char(1) default 'N' comment '系统内置(Y是 N否)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (config_id) +) engine=innodb auto_increment=100 comment = '参数配置表'; + +insert into sys_config values(1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' ); +insert into sys_config values(2, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '初始化密码 123456' ); +insert into sys_config values(3, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '深色主题theme-dark,浅色主题theme-light' ); + + +-- ---------------------------- +-- 14、系统访问记录 +-- ---------------------------- +drop table if exists sys_logininfor; +create table sys_logininfor ( + info_id bigint(20) not null auto_increment comment '访问ID', + user_name varchar(50) default '' comment '用户账号', + ipaddr varchar(50) default '' comment '登录IP地址', + login_location varchar(255) default '' comment '登录地点', + browser varchar(50) default '' comment '浏览器类型', + os varchar(50) default '' comment '操作系统', + status char(1) default '0' comment '登录状态(0成功 1失败)', + msg varchar(255) default '' comment '提示消息', + login_time datetime comment '访问时间', + primary key (info_id) +) engine=innodb auto_increment=100 comment = '系统访问记录'; + + +-- ---------------------------- +-- 15、在线用户记录 +-- ---------------------------- +drop table if exists sys_user_online; +create table sys_user_online ( + sessionId varchar(50) default '' comment '用户会话id', + user_name varchar(50) default '' comment '用户账号', + dept_name varchar(50) default '' comment '部门名称', + ipaddr varchar(50) default '' comment '登录IP地址', + login_location varchar(255) default '' comment '登录地点', + browser varchar(50) default '' comment '浏览器类型', + os varchar(50) default '' comment '操作系统', + status varchar(10) default '' comment '在线状态on_line在线off_line离线', + start_timestamp datetime comment 'session创建时间', + last_access_time datetime comment 'session最后访问时间', + expire_time int(5) default 0 comment '超时时间,单位为分钟', + primary key (sessionId) +) engine=innodb comment = '在线用户记录'; + + +-- ---------------------------- +-- 16、定时任务调度表 +-- ---------------------------- +drop table if exists sys_job; +create table sys_job ( + job_id bigint(20) not null auto_increment comment '任务ID', + job_name varchar(64) default '' comment '任务名称', + job_group varchar(64) default 'DEFAULT' comment '任务组名', + invoke_target varchar(500) not null comment '调用目标字符串', + cron_expression varchar(255) default '' comment 'cron执行表达式', + misfire_policy varchar(20) default '3' comment '计划执行错误策略(1立即执行 2执行一次 3放弃执行)', + concurrent char(1) default '1' comment '是否并发执行(0允许 1禁止)', + status char(1) default '0' comment '状态(0正常 1暂停)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default '' comment '备注信息', + primary key (job_id, job_name, job_group) +) engine=innodb auto_increment=100 comment = '定时任务调度表'; + +insert into sys_job values(1, '系统默认(无参)', 'DEFAULT', 'ryTask.ryNoParams', '0/10 * * * * ?', '3', '1', '1', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_job values(2, '系统默认(有参)', 'DEFAULT', 'ryTask.ryParams(\'ry\')', '0/15 * * * * ?', '3', '1', '1', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_job values(3, '系统默认(多参)', 'DEFAULT', 'ryTask.ryMultipleParams(\'ry\', true, 2000L, 316.50D, 100)', '0/20 * * * * ?', '3', '1', '1', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); + + +-- ---------------------------- +-- 17、定时任务调度日志表 +-- ---------------------------- +drop table if exists sys_job_log; +create table sys_job_log ( + job_log_id bigint(20) not null auto_increment comment '任务日志ID', + job_name varchar(64) not null comment '任务名称', + job_group varchar(64) not null comment '任务组名', + invoke_target varchar(500) not null comment '调用目标字符串', + job_message varchar(500) comment '日志信息', + status char(1) default '0' comment '执行状态(0正常 1失败)', + exception_info varchar(2000) default '' comment '异常信息', + create_time datetime comment '创建时间', + primary key (job_log_id) +) engine=innodb comment = '定时任务调度日志表'; + + +-- ---------------------------- +-- 18、通知公告表 +-- ---------------------------- +drop table if exists sys_notice; +create table sys_notice ( + notice_id int(4) not null auto_increment comment '公告ID', + notice_title varchar(50) not null comment '公告标题', + notice_type char(1) not null comment '公告类型(1通知 2公告)', + notice_content varchar(2000) default null comment '公告内容', + status char(1) default '0' comment '公告状态(0正常 1关闭)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(255) default null comment '备注', + primary key (notice_id) +) engine=innodb auto_increment=10 comment = '通知公告表'; + +-- ---------------------------- +-- 初始化-公告信息表数据 +-- ---------------------------- +insert into sys_notice values('1', '温馨提醒:2018-07-01 若依新版本发布啦', '2', '新版本内容', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '管理员'); +insert into sys_notice values('2', '维护通知:2018-07-01 若依系统凌晨维护', '1', '维护内容', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '管理员'); + + +-- ---------------------------- +-- 19、代码生成业务表 +-- ---------------------------- +drop table if exists gen_table; +create table gen_table ( + table_id bigint(20) not null auto_increment comment '编号', + table_name varchar(200) default '' comment '表名称', + table_comment varchar(500) default '' comment '表描述', + class_name varchar(100) default '' comment '实体类名称', + tpl_category varchar(200) default 'crud' comment '使用的模板(crud单表操作 tree树表操作)', + package_name varchar(100) comment '生成包路径', + module_name varchar(30) comment '生成模块名', + business_name varchar(30) comment '生成业务名', + function_name varchar(50) comment '生成功能名', + function_author varchar(50) comment '生成功能作者', + options varchar(1000) comment '其它生成选项', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (table_id) +) engine=innodb auto_increment=1 comment = '代码生成业务表'; + + +-- ---------------------------- +-- 20、代码生成业务表字段 +-- ---------------------------- +drop table if exists gen_table_column; +create table gen_table_column ( + column_id bigint(20) not null auto_increment comment '编号', + table_id varchar(64) comment '归属表编号', + column_name varchar(200) comment '列名称', + column_comment varchar(500) comment '列描述', + column_type varchar(100) comment '列类型', + java_type varchar(500) comment 'JAVA类型', + java_field varchar(200) comment 'JAVA字段名', + is_pk char(1) comment '是否主键(1是)', + is_increment char(1) comment '是否自增(1是)', + is_required char(1) comment '是否必填(1是)', + is_insert char(1) comment '是否为插入字段(1是)', + is_edit char(1) comment '是否编辑字段(1是)', + is_list char(1) comment '是否列表字段(1是)', + is_query char(1) comment '是否查询字段(1是)', + query_type varchar(200) default 'EQ' comment '查询方式(等于、不等于、大于、小于、范围)', + html_type varchar(200) comment '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)', + dict_type varchar(200) default '' comment '字典类型', + sort int comment '排序', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + primary key (column_id) +) engine=innodb auto_increment=1 comment = '代码生成业务表字段'; \ No newline at end of file diff --git a/ruoyi/src/main/java/com/ruoyi/RuoYiApplication.java b/ruoyi/src/main/java/com/ruoyi/RuoYiApplication.java new file mode 100644 index 0000000..e3c56ee --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/RuoYiApplication.java @@ -0,0 +1,30 @@ +package com.ruoyi; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +/** + * 启动程序 + * + * @author ruoyi + */ +@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) +public class RuoYiApplication +{ + public static void main(String[] args) + { + // System.setProperty("spring.devtools.restart.enabled", "false"); + SpringApplication.run(RuoYiApplication.class, args); + System.out.println("(♥◠‿◠)ノ゙ 若依启动成功 ლ(´ڡ`ლ)゙ \n" + + " .-------. ____ __ \n" + + " | _ _ \\ \\ \\ / / \n" + + " | ( ' ) | \\ _. / ' \n" + + " |(_ o _) / _( )_ .' \n" + + " | (_,_).' __ ___(_ o _)' \n" + + " | |\\ \\ | || |(_,_)' \n" + + " | | \\ `' /| `-' / \n" + + " | | \\ / \\ / \n" + + " ''-' `'-' `-..-' "); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/RuoYiServletInitializer.java b/ruoyi/src/main/java/com/ruoyi/RuoYiServletInitializer.java new file mode 100644 index 0000000..9d82a23 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/RuoYiServletInitializer.java @@ -0,0 +1,18 @@ +package com.ruoyi; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * web容器中进行部署 + * + * @author ruoyi + */ +public class RuoYiServletInitializer extends SpringBootServletInitializer +{ + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) + { + return application.sources(RuoYiApplication.class); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/constant/Constants.java b/ruoyi/src/main/java/com/ruoyi/common/constant/Constants.java new file mode 100644 index 0000000..ed4e471 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/constant/Constants.java @@ -0,0 +1,96 @@ +package com.ruoyi.common.constant; + +import io.jsonwebtoken.Claims; + +/** + * 通用常量信息 + * + * @author ruoyi + */ +public class Constants +{ + /** + * 通用成功标识 + */ + public static final String SUCCESS = "0"; + + /** + * 通用失败标识 + */ + public static final String FAIL = "1"; + + /** + * 登录成功 + */ + public static final String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + public static final String LOGOUT = "Logout"; + + /** + * 登录失败 + */ + public static final String LOGIN_FAIL = "Error"; + + /** + * 验证码 redis key + */ + public static final String CAPTCHA_CODE_KEY = "captcha_codes:"; + + /** + * 登录用户 redis key + */ + public static final String LOGIN_TOKEN_KEY = "login_tokens:"; + + /** + * 验证码有效期(分钟) + */ + public static final Integer CAPTCHA_EXPIRATION = 2; + + /** + * 令牌 + */ + public static final String TOKEN = "token"; + + /** + * 令牌前缀 + */ + public static final String TOKEN_PREFIX = "Bearer "; + + /** + * 令牌前缀 + */ + public static final String LOGIN_USER_KEY = "login_user_key"; + + /** + * 用户ID + */ + public static final String JWT_USERID = "userid"; + + /** + * 用户名称 + */ + public static final String JWT_USERNAME = Claims.SUBJECT; + + /** + * 用户头像 + */ + public static final String JWT_AVATAR = "avatar"; + + /** + * 创建时间 + */ + public static final String JWT_CREATED = "created"; + + /** + * 用户权限 + */ + public static final String JWT_AUTHORITIES = "authorities"; + + /** + * 资源映射路径 前缀 + */ + public static final String RESOURCE_PREFIX = "/profile"; +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/constant/HttpStatus.java b/ruoyi/src/main/java/com/ruoyi/common/constant/HttpStatus.java new file mode 100644 index 0000000..ffdb56d --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/constant/HttpStatus.java @@ -0,0 +1,89 @@ +package com.ruoyi.common.constant; + +/** + * 返回状态码 + * + * @author ruoyi + */ +public interface HttpStatus +{ + /** + * 操作成功 + */ + public static final int SUCCESS = 200; + + /** + * 对象创建成功 + */ + public static final int CREATED = 201; + + /** + * 请求已经被接受 + */ + public static final int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + public static final int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + public static final int MOVED_PERM = 301; + + /** + * 重定向 + */ + public static final int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + public static final int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + public static final int BAD_REQUEST = 400; + + /** + * 未授权 + */ + public static final int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + public static final int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + public static final int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + public static final int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + public static final int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + public static final int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + public static final int ERROR = 500; + + /** + * 接口未实现 + */ + public static final int NOT_IMPLEMENTED = 501; +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/constant/UserConstants.java b/ruoyi/src/main/java/com/ruoyi/common/constant/UserConstants.java new file mode 100644 index 0000000..9164cd2 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/constant/UserConstants.java @@ -0,0 +1,39 @@ +package com.ruoyi.common.constant; + +/** + * 用户常量信息 + * + * @author ruoyi + */ +public class UserConstants +{ + /** + * 平台内系统用户的唯一标志 + */ + public static final String SYS_USER = "SYS_USER"; + + /** 正常状态 */ + public static final String NORMAL = "0"; + + /** 异常状态 */ + public static final String EXCEPTION = "1"; + + /** 用户封禁状态 */ + public static final String USER_BLOCKED = "1"; + + /** 角色封禁状态 */ + public static final String ROLE_BLOCKED = "1"; + + /** 部门正常状态 */ + public static final String DEPT_NORMAL = "0"; + + /** 字典正常状态 */ + public static final String DICT_NORMAL = "0"; + + /** 是否为系统默认(是) */ + public static final String YES = "Y"; + + /** 校验返回结果码 */ + public final static String UNIQUE = "0"; + public final static String NOT_UNIQUE = "1"; +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/core/lang/UUID.java b/ruoyi/src/main/java/com/ruoyi/common/core/lang/UUID.java new file mode 100644 index 0000000..9e1bbeb --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/core/lang/UUID.java @@ -0,0 +1,481 @@ +package com.ruoyi.common.core.lang; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import com.ruoyi.common.exception.UtilException; + +/** + * 提供通用唯一识别码(universally unique identifier)(UUID)实现 + * + * @author ruoyi + */ +public final class UUID implements java.io.Serializable, Comparable<UUID> +{ + private static final long serialVersionUID = -1185015143654744140L; + + /** + * SecureRandom 的单例 + * + */ + private static class Holder + { + static final SecureRandom numberGenerator = getSecureRandom(); + } + + /** 此UUID的最高64有效位 */ + private final long mostSigBits; + + /** 此UUID的最低64有效位 */ + private final long leastSigBits; + + /** + * 私有构造 + * + * @param data 数据 + */ + private UUID(byte[] data) + { + long msb = 0; + long lsb = 0; + assert data.length == 16 : "data must be 16 bytes in length"; + for (int i = 0; i < 8; i++) + { + msb = (msb << 8) | (data[i] & 0xff); + } + for (int i = 8; i < 16; i++) + { + lsb = (lsb << 8) | (data[i] & 0xff); + } + this.mostSigBits = msb; + this.leastSigBits = lsb; + } + + /** + * 使用指定的数据构造新的 UUID。 + * + * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位 + * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位 + */ + public UUID(long mostSigBits, long leastSigBits) + { + this.mostSigBits = mostSigBits; + this.leastSigBits = leastSigBits; + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的本地线程伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID fastUUID() + { + return randomUUID(false); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID() + { + return randomUUID(true); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能 + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID(boolean isSecure) + { + final Random ng = isSecure ? Holder.numberGenerator : getRandom(); + + byte[] randomBytes = new byte[16]; + ng.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(randomBytes); + } + + /** + * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。 + * + * @param name 用于构造 UUID 的字节数组。 + * + * @return 根据指定数组生成的 {@code UUID} + */ + public static UUID nameUUIDFromBytes(byte[] name) + { + MessageDigest md; + try + { + md = MessageDigest.getInstance("MD5"); + } + catch (NoSuchAlgorithmException nsae) + { + throw new InternalError("MD5 not supported"); + } + byte[] md5Bytes = md.digest(name); + md5Bytes[6] &= 0x0f; /* clear version */ + md5Bytes[6] |= 0x30; /* set to version 3 */ + md5Bytes[8] &= 0x3f; /* clear variant */ + md5Bytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(md5Bytes); + } + + /** + * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。 + * + * @param name 指定 {@code UUID} 字符串 + * @return 具有指定值的 {@code UUID} + * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常 + * + */ + public static UUID fromString(String name) + { + String[] components = name.split("-"); + if (components.length != 5) + { + throw new IllegalArgumentException("Invalid UUID string: " + name); + } + for (int i = 0; i < 5; i++) + { + components[i] = "0x" + components[i]; + } + + long mostSigBits = Long.decode(components[0]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[1]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[2]).longValue(); + + long leastSigBits = Long.decode(components[3]).longValue(); + leastSigBits <<= 48; + leastSigBits |= Long.decode(components[4]).longValue(); + + return new UUID(mostSigBits, leastSigBits); + } + + /** + * 返回此 UUID 的 128 位值中的最低有效 64 位。 + * + * @return 此 UUID 的 128 位值中的最低有效 64 位。 + */ + public long getLeastSignificantBits() + { + return leastSigBits; + } + + /** + * 返回此 UUID 的 128 位值中的最高有效 64 位。 + * + * @return 此 UUID 的 128 位值中最高有效 64 位。 + */ + public long getMostSignificantBits() + { + return mostSigBits; + } + + /** + * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。 + * <p> + * 版本号具有以下含意: + * <ul> + * <li>1 基于时间的 UUID + * <li>2 DCE 安全 UUID + * <li>3 基于名称的 UUID + * <li>4 随机生成的 UUID + * </ul> + * + * @return 此 {@code UUID} 的版本号 + */ + public int version() + { + // Version is bits masked by 0x000000000000F000 in MS long + return (int) ((mostSigBits >> 12) & 0x0f); + } + + /** + * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。 + * <p> + * 变体号具有以下含意: + * <ul> + * <li>0 为 NCS 向后兼容保留 + * <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF RFC 4122</a>(Leach-Salz), 用于此类 + * <li>6 保留,微软向后兼容 + * <li>7 保留供以后定义使用 + * </ul> + * + * @return 此 {@code UUID} 相关联的变体号 + */ + public int variant() + { + // This field is composed of a varying number of bits. + // 0 - - Reserved for NCS backward compatibility + // 1 0 - The IETF aka Leach-Salz variant (used by this class) + // 1 1 0 Reserved, Microsoft backward compatibility + // 1 1 1 Reserved for future definition. + return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63)); + } + + /** + * 与此 UUID 相关联的时间戳值。 + * + * <p> + * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。<br> + * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。 + * + * <p> + * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。<br> + * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。 + */ + public long timestamp() throws UnsupportedOperationException + { + checkTimeBase(); + return (mostSigBits & 0x0FFFL) << 48// + | ((mostSigBits >> 16) & 0x0FFFFL) << 32// + | mostSigBits >>> 32; + } + + /** + * 与此 UUID 相关联的时钟序列值。 + * + * <p> + * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。 + * <p> + * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出 + * UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的时钟序列 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public int clockSequence() throws UnsupportedOperationException + { + checkTimeBase(); + return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48); + } + + /** + * 与此 UUID 相关的节点值。 + * + * <p> + * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。 + * <p> + * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。<br> + * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的节点值 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public long node() throws UnsupportedOperationException + { + checkTimeBase(); + return leastSigBits & 0x0000FFFFFFFFFFFFL; + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + * <p> + * UUID 的字符串表示形式由此 BNF 描述: + * + * <pre> + * {@code + * UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node> + * time_low = 4*<hexOctet> + * time_mid = 2*<hexOctet> + * time_high_and_version = 2*<hexOctet> + * variant_and_sequence = 2*<hexOctet> + * node = 6*<hexOctet> + * hexOctet = <hexDigit><hexDigit> + * hexDigit = [0-9a-fA-F] + * } + * </pre> + * + * </blockquote> + * + * @return 此{@code UUID} 的字符串表现形式 + * @see #toString(boolean) + */ + @Override + public String toString() + { + return toString(false); + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + * <p> + * UUID 的字符串表示形式由此 BNF 描述: + * + * <pre> + * {@code + * UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node> + * time_low = 4*<hexOctet> + * time_mid = 2*<hexOctet> + * time_high_and_version = 2*<hexOctet> + * variant_and_sequence = 2*<hexOctet> + * node = 6*<hexOctet> + * hexOctet = <hexDigit><hexDigit> + * hexDigit = [0-9a-fA-F] + * } + * </pre> + * + * </blockquote> + * + * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串 + * @return 此{@code UUID} 的字符串表现形式 + */ + public String toString(boolean isSimple) + { + final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36); + // time_low + builder.append(digits(mostSigBits >> 32, 8)); + if (false == isSimple) + { + builder.append('-'); + } + // time_mid + builder.append(digits(mostSigBits >> 16, 4)); + if (false == isSimple) + { + builder.append('-'); + } + // time_high_and_version + builder.append(digits(mostSigBits, 4)); + if (false == isSimple) + { + builder.append('-'); + } + // variant_and_sequence + builder.append(digits(leastSigBits >> 48, 4)); + if (false == isSimple) + { + builder.append('-'); + } + // node + builder.append(digits(leastSigBits, 12)); + + return builder.toString(); + } + + /** + * 返回此 UUID 的哈希码。 + * + * @return UUID 的哈希码值。 + */ + public int hashCode() + { + long hilo = mostSigBits ^ leastSigBits; + return ((int) (hilo >> 32)) ^ (int) hilo; + } + + /** + * 将此对象与指定对象比较。 + * <p> + * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。 + * + * @param obj 要与之比较的对象 + * + * @return 如果对象相同,则返回 {@code true};否则返回 {@code false} + */ + public boolean equals(Object obj) + { + if ((null == obj) || (obj.getClass() != UUID.class)) + { + return false; + } + UUID id = (UUID) obj; + return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits); + } + + // Comparison Operations + + /** + * 将此 UUID 与指定的 UUID 比较。 + * + * <p> + * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。 + * + * @param val 与此 UUID 比较的 UUID + * + * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。 + * + */ + public int compareTo(UUID val) + { + // The ordering is intentionally set up so that the UUIDs + // can simply be numerically compared as two numbers + return (this.mostSigBits < val.mostSigBits ? -1 : // + (this.mostSigBits > val.mostSigBits ? 1 : // + (this.leastSigBits < val.leastSigBits ? -1 : // + (this.leastSigBits > val.leastSigBits ? 1 : // + 0)))); + } + + // ------------------------------------------------------------------------------------------------------------------- + // Private method start + /** + * 返回指定数字对应的hex值 + * + * @param val 值 + * @param digits 位 + * @return 值 + */ + private static String digits(long val, int digits) + { + long hi = 1L << (digits * 4); + return Long.toHexString(hi | (val & (hi - 1))).substring(1); + } + + /** + * 检查是否为time-based版本UUID + */ + private void checkTimeBase() + { + if (version() != 1) + { + throw new UnsupportedOperationException("Not a time-based UUID"); + } + } + + /** + * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG) + * + * @return {@link SecureRandom} + */ + public static SecureRandom getSecureRandom() + { + try + { + return SecureRandom.getInstance("SHA1PRNG"); + } + catch (NoSuchAlgorithmException e) + { + throw new UtilException(e); + } + } + + /** + * 获取随机数生成器对象<br> + * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。 + * + * @return {@link ThreadLocalRandom} + */ + public static ThreadLocalRandom getRandom() + { + return ThreadLocalRandom.current(); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/core/text/CharsetKit.java b/ruoyi/src/main/java/com/ruoyi/common/core/text/CharsetKit.java new file mode 100644 index 0000000..8297661 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/core/text/CharsetKit.java @@ -0,0 +1,86 @@ +package com.ruoyi.common.core.text; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import com.ruoyi.common.utils.StringUtils; + +/** + * 字符集工具类 + * + * @author ruoyi + */ +public class CharsetKit +{ + /** ISO-8859-1 */ + public static final String ISO_8859_1 = "ISO-8859-1"; + /** UTF-8 */ + public static final String UTF_8 = "UTF-8"; + /** GBK */ + public static final String GBK = "GBK"; + + /** ISO-8859-1 */ + public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); + /** UTF-8 */ + public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); + /** GBK */ + public static final Charset CHARSET_GBK = Charset.forName(GBK); + + /** + * 转换为Charset对象 + * + * @param charset 字符集,为空则返回默认字符集 + * @return Charset + */ + public static Charset charset(String charset) + { + return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, String srcCharset, String destCharset) + { + return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, Charset srcCharset, Charset destCharset) + { + if (null == srcCharset) + { + srcCharset = StandardCharsets.ISO_8859_1; + } + + if (null == destCharset) + { + srcCharset = StandardCharsets.UTF_8; + } + + if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) + { + return source; + } + return new String(source.getBytes(srcCharset), destCharset); + } + + /** + * @return 系统字符集编码 + */ + public static String systemCharset() + { + return Charset.defaultCharset().name(); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/core/text/Convert.java b/ruoyi/src/main/java/com/ruoyi/common/core/text/Convert.java new file mode 100644 index 0000000..9dd076d --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/core/text/Convert.java @@ -0,0 +1,999 @@ +package com.ruoyi.common.core.text; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.text.NumberFormat; +import java.util.Set; +import com.ruoyi.common.utils.StringUtils; + +/** + * 类型转换器 + * + * @author ruoyi + */ +public class Convert +{ + /** + * 转换为字符串<br> + * 如果给定的值为null,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static String toStr(Object value, String defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof String) + { + return (String) value; + } + return value.toString(); + } + + /** + * 转换为字符串<br> + * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static String toStr(Object value) + { + return toStr(value, null); + } + + /** + * 转换为字符<br> + * 如果给定的值为null,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Character toChar(Object value, Character defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof Character) + { + return (Character) value; + } + + final String valueStr = toStr(value, null); + return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); + } + + /** + * 转换为字符<br> + * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Character toChar(Object value) + { + return toChar(value, null); + } + + /** + * 转换为byte<br> + * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Byte toByte(Object value, Byte defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Byte) + { + return (Byte) value; + } + if (value instanceof Number) + { + return ((Number) value).byteValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Byte.parseByte(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为byte<br> + * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Byte toByte(Object value) + { + return toByte(value, null); + } + + /** + * 转换为Short<br> + * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Short toShort(Object value, Short defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Short) + { + return (Short) value; + } + if (value instanceof Number) + { + return ((Number) value).shortValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Short.parseShort(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Short<br> + * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Short toShort(Object value) + { + return toShort(value, null); + } + + /** + * 转换为Number<br> + * 如果给定的值为空,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Number toNumber(Object value, Number defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Number) + { + return (Number) value; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return NumberFormat.getInstance().parse(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Number<br> + * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Number toNumber(Object value) + { + return toNumber(value, null); + } + + /** + * 转换为int<br> + * 如果给定的值为空,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Integer toInt(Object value, Integer defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Integer) + { + return (Integer) value; + } + if (value instanceof Number) + { + return ((Number) value).intValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Integer.parseInt(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为int<br> + * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Integer toInt(Object value) + { + return toInt(value, null); + } + + /** + * 转换为Integer数组<br> + * + * @param str 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String str) + { + return toIntArray(",", str); + } + + /** + * 转换为Long数组<br> + * + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String str) + { + return toLongArray(",", str); + } + + /** + * 转换为Integer数组<br> + * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Integer[] {}; + } + String[] arr = str.split(split); + final Integer[] ints = new Integer[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Integer v = toInt(arr[i], 0); + ints[i] = v; + } + return ints; + } + + /** + * 转换为Long数组<br> + * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Long[] {}; + } + String[] arr = str.split(split); + final Long[] longs = new Long[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Long v = toLong(arr[i], null); + longs[i] = v; + } + return longs; + } + + /** + * 转换为String数组<br> + * + * @param str 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String str) + { + return toStrArray(",", str); + } + + /** + * 转换为String数组<br> + * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String split, String str) + { + return str.split(split); + } + + /** + * 转换为long<br> + * 如果给定的值为空,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Long toLong(Object value, Long defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Long) + { + return (Long) value; + } + if (value instanceof Number) + { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).longValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为long<br> + * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Long toLong(Object value) + { + return toLong(value, null); + } + + /** + * 转换为double<br> + * 如果给定的值为空,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Double toDouble(Object value, Double defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Double) + { + return (Double) value; + } + if (value instanceof Number) + { + return ((Number) value).doubleValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).doubleValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为double<br> + * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Double toDouble(Object value) + { + return toDouble(value, null); + } + + /** + * 转换为Float<br> + * 如果给定的值为空,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Float toFloat(Object value, Float defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Float) + { + return (Float) value; + } + if (value instanceof Number) + { + return ((Number) value).floatValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Float.parseFloat(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Float<br> + * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Float toFloat(Object value) + { + return toFloat(value, null); + } + + /** + * 转换为boolean<br> + * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Boolean toBool(Object value, Boolean defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Boolean) + { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) + { + case "true": + return true; + case "false": + return false; + case "yes": + return true; + case "ok": + return true; + case "no": + return false; + case "1": + return true; + case "0": + return false; + default: + return defaultValue; + } + } + + /** + * 转换为boolean<br> + * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Boolean toBool(Object value) + { + return toBool(value, null); + } + + /** + * 转换为Enum对象<br> + * 如果给定的值为空,或者转换失败,返回默认值<br> + * + * @param clazz Enum的Class + * @param value 值 + * @param defaultValue 默认值 + * @return Enum + */ + public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value, E defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (clazz.isAssignableFrom(value.getClass())) + { + @SuppressWarnings("unchecked") + E myE = (E) value; + return myE; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Enum.valueOf(clazz, valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Enum对象<br> + * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> + * + * @param clazz Enum的Class + * @param value 值 + * @return Enum + */ + public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value) + { + return toEnum(clazz, value, null); + } + + /** + * 转换为BigInteger<br> + * 如果给定的值为空,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value, BigInteger defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigInteger) + { + return (BigInteger) value; + } + if (value instanceof Long) + { + return BigInteger.valueOf((Long) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigInteger(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigInteger<br> + * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value) + { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal<br> + * 如果给定的值为空,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigDecimal) + { + return (BigDecimal) value; + } + if (value instanceof Long) + { + return new BigDecimal((Long) value); + } + if (value instanceof Double) + { + return new BigDecimal((Double) value); + } + if (value instanceof Integer) + { + return new BigDecimal((Integer) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigDecimal(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigDecimal<br> + * 如果给定的值为空,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value) + { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串<br> + * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @return 字符串 + */ + public static String utf8Str(Object obj) + { + return str(obj, CharsetKit.CHARSET_UTF_8); + } + + /** + * 将对象转为字符串<br> + * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charsetName 字符集 + * @return 字符串 + */ + public static String str(Object obj, String charsetName) + { + return str(obj, Charset.forName(charsetName)); + } + + /** + * 将对象转为字符串<br> + * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(Object obj, Charset charset) + { + if (null == obj) + { + return null; + } + + if (obj instanceof String) + { + return (String) obj; + } + else if (obj instanceof byte[] || obj instanceof Byte[]) + { + return str((Byte[]) obj, charset); + } + else if (obj instanceof ByteBuffer) + { + return str((ByteBuffer) obj, charset); + } + return obj.toString(); + } + + /** + * 将byte数组转为字符串 + * + * @param bytes byte数组 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(byte[] bytes, String charset) + { + return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); + } + + /** + * 解码字节码 + * + * @param data 字符串 + * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 + * @return 解码后的字符串 + */ + public static String str(byte[] data, Charset charset) + { + if (data == null) + { + return null; + } + + if (null == charset) + { + return new String(data); + } + return new String(data, charset); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, String charset) + { + if (data == null) + { + return null; + } + + return str(data, Charset.forName(charset)); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, Charset charset) + { + if (null == charset) + { + charset = Charset.defaultCharset(); + } + return charset.decode(data).toString(); + } + + // ----------------------------------------------------------------------- 全角半角转换 + /** + * 半角转全角 + * + * @param input String. + * @return 全角字符串. + */ + public static String toSBC(String input) + { + return toSBC(input, null); + } + + /** + * 半角转全角 + * + * @param input String + * @param notConvertSet 不替换的字符集合 + * @return 全角字符串. + */ + public static String toSBC(String input, Set<Character> notConvertSet) + { + char c[] = input.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == ' ') + { + c[i] = '\u3000'; + } + else if (c[i] < '\177') + { + c[i] = (char) (c[i] + 65248); + + } + } + return new String(c); + } + + /** + * 全角转半角 + * + * @param input String. + * @return 半角字符串 + */ + public static String toDBC(String input) + { + return toDBC(input, null); + } + + /** + * 替换全角为半角 + * + * @param text 文本 + * @param notConvertSet 不替换的字符集合 + * @return 替换后的字符 + */ + public static String toDBC(String text, Set<Character> notConvertSet) + { + char c[] = text.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == '\u3000') + { + c[i] = ' '; + } + else if (c[i] > '\uFF00' && c[i] < '\uFF5F') + { + c[i] = (char) (c[i] - 65248); + } + } + String returnString = new String(c); + + return returnString; + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @param n 数字 + * @return 中文大写数字 + */ + public static String digitUppercase(double n) + { + String[] fraction = { "角", "分" }; + String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; + String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } }; + + String head = n < 0 ? "负" : ""; + n = Math.abs(n); + + String s = ""; + for (int i = 0; i < fraction.length; i++) + { + s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); + } + if (s.length() < 1) + { + s = "整"; + } + int integerPart = (int) Math.floor(n); + + for (int i = 0; i < unit[0].length && integerPart > 0; i++) + { + String p = ""; + for (int j = 0; j < unit[1].length && n > 0; j++) + { + p = digit[integerPart % 10] + unit[1][j] + p; + integerPart = integerPart / 10; + } + s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; + } + return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/core/text/StrFormatter.java b/ruoyi/src/main/java/com/ruoyi/common/core/text/StrFormatter.java new file mode 100644 index 0000000..dbf3e23 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/core/text/StrFormatter.java @@ -0,0 +1,92 @@ +package com.ruoyi.common.core.text; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 字符串格式化 + * + * @author ruoyi + */ +public class StrFormatter +{ + public static final String EMPTY_JSON = "{}"; + public static final char C_BACKSLASH = '\\'; + public static final char C_DELIM_START = '{'; + public static final char C_DELIM_END = '}'; + + /** + * 格式化字符串<br> + * 此方法只是简单将占位符 {} 按照顺序替换为参数<br> + * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br> + * 例:<br> + * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br> + * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br> + * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br> + * + * @param strPattern 字符串模板 + * @param argArray 参数列表 + * @return 结果 + */ + public static String format(final String strPattern, final Object... argArray) + { + if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) + { + return strPattern; + } + final int strPatternLength = strPattern.length(); + + // 初始化定义好的长度以获得更好的性能 + StringBuilder sbuf = new StringBuilder(strPatternLength + 50); + + int handledPosition = 0; + int delimIndex;// 占位符所在位置 + for (int argIndex = 0; argIndex < argArray.length; argIndex++) + { + delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition); + if (delimIndex == -1) + { + if (handledPosition == 0) + { + return strPattern; + } + else + { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果 + sbuf.append(strPattern, handledPosition, strPatternLength); + return sbuf.toString(); + } + } + else + { + if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) + { + if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) + { + // 转义符之前还有一个转义符,占位符依旧有效 + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + else + { + // 占位符被转义 + argIndex--; + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(C_DELIM_START); + handledPosition = delimIndex + 1; + } + } + else + { + // 正常占位符 + sbuf.append(strPattern, handledPosition, delimIndex); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + } + } + // 加入最后一个占位符后所有的字符 + sbuf.append(strPattern, handledPosition, strPattern.length()); + + return sbuf.toString(); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/enums/HttpMethod.java b/ruoyi/src/main/java/com/ruoyi/common/enums/HttpMethod.java new file mode 100644 index 0000000..2fa9f86 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/enums/HttpMethod.java @@ -0,0 +1,36 @@ +package com.ruoyi.common.enums; + +import java.util.HashMap; +import java.util.Map; +import org.springframework.lang.Nullable; + +/** + * 请求方式 + * + * @author ruoyi + */ +public enum HttpMethod +{ + GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; + + private static final Map<String, HttpMethod> mappings = new HashMap<>(16); + + static + { + for (HttpMethod httpMethod : values()) + { + mappings.put(httpMethod.name(), httpMethod); + } + } + + @Nullable + public static HttpMethod resolve(@Nullable String method) + { + return (method != null ? mappings.get(method) : null); + } + + public boolean matches(String method) + { + return (this == resolve(method)); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/enums/UserStatus.java b/ruoyi/src/main/java/com/ruoyi/common/enums/UserStatus.java new file mode 100644 index 0000000..a4613ff --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/enums/UserStatus.java @@ -0,0 +1,30 @@ +package com.ruoyi.common.enums; + +/** + * 用户状态 + * + * @author ruoyi + */ +public enum UserStatus +{ + OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除"); + + private final String code; + private final String info; + + UserStatus(String code, String info) + { + this.code = code; + this.info = info; + } + + public String getCode() + { + return code; + } + + public String getInfo() + { + return info; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/exception/BaseException.java b/ruoyi/src/main/java/com/ruoyi/common/exception/BaseException.java new file mode 100644 index 0000000..c1e55c1 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/exception/BaseException.java @@ -0,0 +1,97 @@ +package com.ruoyi.common.exception; + +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * 基础异常 + * + * @author ruoyi + */ +public class BaseException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 所属模块 + */ + private String module; + + /** + * 错误码 + */ + private String code; + + /** + * 错误码对应的参数 + */ + private Object[] args; + + /** + * 错误消息 + */ + private String defaultMessage; + + public BaseException(String module, String code, Object[] args, String defaultMessage) + { + this.module = module; + this.code = code; + this.args = args; + this.defaultMessage = defaultMessage; + } + + public BaseException(String module, String code, Object[] args) + { + this(module, code, args, null); + } + + public BaseException(String module, String defaultMessage) + { + this(module, null, null, defaultMessage); + } + + public BaseException(String code, Object[] args) + { + this(null, code, args, null); + } + + public BaseException(String defaultMessage) + { + this(null, null, null, defaultMessage); + } + + @Override + public String getMessage() + { + String message = null; + if (!StringUtils.isEmpty(code)) + { + message = MessageUtils.message(code, args); + } + if (message == null) + { + message = defaultMessage; + } + return message; + } + + public String getModule() + { + return module; + } + + public String getCode() + { + return code; + } + + public Object[] getArgs() + { + return args; + } + + public String getDefaultMessage() + { + return defaultMessage; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/exception/CustomException.java b/ruoyi/src/main/java/com/ruoyi/common/exception/CustomException.java new file mode 100644 index 0000000..a99a100 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/exception/CustomException.java @@ -0,0 +1,43 @@ +package com.ruoyi.common.exception; + +/** + * 自定义异常 + * + * @author ruoyi + */ +public class CustomException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + private Integer code; + + private String message; + + public CustomException(String message) + { + this.message = message; + } + + public CustomException(String message, Integer code) + { + this.message = message; + this.code = code; + } + + public CustomException(String message, Throwable e) + { + super(message, e); + this.message = message; + } + + @Override + public String getMessage() + { + return message; + } + + public Integer getCode() + { + return code; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/exception/DemoModeException.java b/ruoyi/src/main/java/com/ruoyi/common/exception/DemoModeException.java new file mode 100644 index 0000000..57bffc4 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/exception/DemoModeException.java @@ -0,0 +1,15 @@ +package com.ruoyi.common.exception; + +/** + * 演示模式异常 + * + * @author ruoyi + */ +public class DemoModeException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public DemoModeException() + { + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/exception/UtilException.java b/ruoyi/src/main/java/com/ruoyi/common/exception/UtilException.java new file mode 100644 index 0000000..a544fb6 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/exception/UtilException.java @@ -0,0 +1,26 @@ +package com.ruoyi.common.exception; + +/** + * 工具类异常 + * + * @author ruoyi + */ +public class UtilException extends RuntimeException +{ + private static final long serialVersionUID = 8247610319171014183L; + + public UtilException(Throwable e) + { + super(e.getMessage(), e); + } + + public UtilException(String message) + { + super(message); + } + + public UtilException(String message, Throwable throwable) + { + super(message, throwable); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/exception/file/FileException.java b/ruoyi/src/main/java/com/ruoyi/common/exception/file/FileException.java new file mode 100644 index 0000000..f0ae563 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/exception/file/FileException.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.exception.file; + +import com.ruoyi.common.exception.BaseException; + +/** + * 文件信息异常类 + * + * @author ruoyi + */ +public class FileException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public FileException(String code, Object[] args) + { + super("file", code, args, null); + } + +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java b/ruoyi/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 0000000..44941b8 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.file; + +/** + * 文件名称超长限制异常类 + * + * @author ruoyi + */ +public class FileNameLengthLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException(int defaultFileNameLength) + { + super("upload.filename.exceed.length", new Object[] { defaultFileNameLength }); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java b/ruoyi/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 0000000..f855e72 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.file; + +/** + * 文件名大小限制异常类 + * + * @author ruoyi + */ +public class FileSizeLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException(long defaultMaxSize) + { + super("upload.exceed.maxSize", new Object[] { defaultMaxSize }); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java b/ruoyi/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java new file mode 100644 index 0000000..81cd78b --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java @@ -0,0 +1,71 @@ +package com.ruoyi.common.exception.file; + +import java.util.Arrays; +import org.apache.commons.fileupload.FileUploadException; + +/** + * 文件上传 误异常类 + * + * @author ruoyi + */ +public class InvalidExtensionException extends FileUploadException +{ + private static final long serialVersionUID = 1L; + + private String[] allowedExtension; + private String extension; + private String filename; + + public InvalidExtensionException(String[] allowedExtension, String extension, String filename) + { + super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : [" + Arrays.toString(allowedExtension) + "]"); + this.allowedExtension = allowedExtension; + this.extension = extension; + this.filename = filename; + } + + public String[] getAllowedExtension() + { + return allowedExtension; + } + + public String getExtension() + { + return extension; + } + + public String getFilename() + { + return filename; + } + + public static class InvalidImageExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidFlashExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidMediaExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java b/ruoyi/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java new file mode 100644 index 0000000..e3334ae --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 验证码错误异常类 + * + * @author ruoyi + */ +public class CaptchaException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaException() + { + super("user.jcaptcha.error", null); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java b/ruoyi/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java new file mode 100644 index 0000000..5e6fd0f --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 验证码失效异常类 + * + * @author ruoyi + */ +public class CaptchaExpireException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaExpireException() + { + super("user.jcaptcha.expire", null); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/exception/user/UserException.java b/ruoyi/src/main/java/com/ruoyi/common/exception/user/UserException.java new file mode 100644 index 0000000..b85dab6 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/exception/user/UserException.java @@ -0,0 +1,18 @@ +package com.ruoyi.common.exception.user; + +import com.ruoyi.common.exception.BaseException; + +/** + * 用户信息异常类 + * + * @author ruoyi + */ +public class UserException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public UserException(String code, Object[] args) + { + super("user", code, args, null); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java b/ruoyi/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java new file mode 100644 index 0000000..71449e2 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 用户密码不正确或不符合规范异常类 + * + * @author ruoyi + */ +public class UserPasswordNotMatchException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserPasswordNotMatchException() + { + super("user.password.not.match", null); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/Arith.java b/ruoyi/src/main/java/com/ruoyi/common/utils/Arith.java new file mode 100644 index 0000000..8668319 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/Arith.java @@ -0,0 +1,114 @@ +package com.ruoyi.common.utils; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * 精确的浮点数运算 + * + * @author ruoyi + */ +public class Arith +{ + + /** 默认除法运算精度 */ + private static final int DEF_DIV_SCALE = 10; + + /** 这个类不能实例化 */ + private Arith() + { + } + + /** + * 提供精确的加法运算。 + * @param v1 被加数 + * @param v2 加数 + * @return 两个参数的和 + */ + public static double add(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.add(b2).doubleValue(); + } + + /** + * 提供精确的减法运算。 + * @param v1 被减数 + * @param v2 减数 + * @return 两个参数的差 + */ + public static double sub(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.subtract(b2).doubleValue(); + } + + /** + * 提供精确的乘法运算。 + * @param v1 被乘数 + * @param v2 乘数 + * @return 两个参数的积 + */ + public static double mul(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.multiply(b2).doubleValue(); + } + + /** + * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 + * 小数点以后10位,以后的数字四舍五入。 + * @param v1 被除数 + * @param v2 除数 + * @return 两个参数的商 + */ + public static double div(double v1, double v2) + { + return div(v1, v2, DEF_DIV_SCALE); + } + + /** + * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 + * 定精度,以后的数字四舍五入。 + * @param v1 被除数 + * @param v2 除数 + * @param scale 表示表示需要精确到小数点以后几位。 + * @return 两个参数的商 + */ + public static double div(double v1, double v2, int scale) + { + if (scale < 0) + { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + if (b1.compareTo(BigDecimal.ZERO) == 0) + { + return BigDecimal.ZERO.doubleValue(); + } + return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue(); + } + + /** + * 提供精确的小数位四舍五入处理。 + * @param v 需要四舍五入的数字 + * @param scale 小数点后保留几位 + * @return 四舍五入后的结果 + */ + public static double round(double v, int scale) + { + if (scale < 0) + { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b = new BigDecimal(Double.toString(v)); + BigDecimal one = new BigDecimal("1"); + return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue(); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/DateUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/DateUtils.java new file mode 100644 index 0000000..8ff95f1 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/DateUtils.java @@ -0,0 +1,155 @@ +package com.ruoyi.common.utils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import org.apache.commons.lang3.time.DateFormatUtils; + +/** + * 时间工具类 + * + * @author ruoyi + */ +public class DateUtils extends org.apache.commons.lang3.time.DateUtils +{ + public static String YYYY = "yyyy"; + + public static String YYYY_MM = "yyyy-MM"; + + public static String YYYY_MM_DD = "yyyy-MM-dd"; + + public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static String[] parsePatterns = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate() + { + return new Date(); + } + + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate() + { + return dateTimeNow(YYYY_MM_DD); + } + + public static final String getTime() + { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static final String dateTimeNow() + { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static final String dateTimeNow(final String format) + { + return parseDateToStr(format, new Date()); + } + + public static final String dateTime(final Date date) + { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static final String parseDateToStr(final String format, final Date date) + { + return new SimpleDateFormat(format).format(date); + } + + public static final Date dateTime(final String format, final String ts) + { + try + { + return new SimpleDateFormat(format).parse(ts); + } + catch (ParseException e) + { + throw new RuntimeException(e); + } + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static final String datePath() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static final String dateTime() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) + { + if (str == null) + { + return null; + } + try + { + return parseDate(str.toString(), parsePatterns); + } + catch (ParseException e) + { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() + { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算两个时间差 + */ + public static String getDatePoor(Date endDate, Date nowDate) + { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - nowDate.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/IdUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/IdUtils.java new file mode 100644 index 0000000..45d8ac9 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/IdUtils.java @@ -0,0 +1,51 @@ +package com.ruoyi.common.utils; + +import com.ruoyi.common.core.lang.UUID; + +/** + * ID生成器工具类 + * + * @author ruoyi + */ +public class IdUtils +{ + /** + * 获取随机UUID + * + * @return 随机UUID + */ + public static String randomUUID() + { + return UUID.randomUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线 + * + * @return 简化的UUID,去掉了横线 + */ + public static String simpleUUID() + { + return UUID.randomUUID().toString(true); + } + + /** + * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 随机UUID + */ + public static String fastUUID() + { + return UUID.fastUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 简化的UUID,去掉了横线 + */ + public static String fastSimpleUUID() + { + return UUID.fastUUID().toString(true); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/LogUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/LogUtils.java new file mode 100644 index 0000000..e415c9c --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/LogUtils.java @@ -0,0 +1,18 @@ +package com.ruoyi.common.utils; + +/** + * 处理并记录日志文件 + * + * @author ruoyi + */ +public class LogUtils +{ + public static String getBlock(Object msg) + { + if (msg == null) + { + msg = ""; + } + return "[" + msg.toString() + "]"; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/MessageUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/MessageUtils.java new file mode 100644 index 0000000..5f2d9ea --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/MessageUtils.java @@ -0,0 +1,26 @@ +package com.ruoyi.common.utils; + +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import com.ruoyi.common.utils.spring.SpringUtils; + +/** + * 获取i18n资源文件 + * + * @author ruoyi + */ +public class MessageUtils +{ + /** + * 根据消息键和参数 获取消息 委托给spring messageSource + * + * @param code 消息键 + * @param args 参数 + * @return 获取国际化翻译值 + */ + public static String message(String code, Object... args) + { + MessageSource messageSource = SpringUtils.getBean(MessageSource.class); + return messageSource.getMessage(code, args, LocaleContextHolder.getLocale()); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/SecurityUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/SecurityUtils.java new file mode 100644 index 0000000..c8d1783 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/SecurityUtils.java @@ -0,0 +1,90 @@ +package com.ruoyi.common.utils; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.exception.CustomException; +import com.ruoyi.framework.security.LoginUser; + +/** + * 安全服务工具类 + * + * @author ruoyi + */ +public class SecurityUtils +{ + /** + * 获取用户账户 + **/ + public static String getUsername() + { + try + { + return getLoginUser().getUsername(); + } + catch (Exception e) + { + throw new CustomException("获取用户账户异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取用户 + **/ + public static LoginUser getLoginUser() + { + try + { + return (LoginUser) getAuthentication().getPrincipal(); + } + catch (Exception e) + { + throw new CustomException("获取用户信息异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取Authentication + */ + public static Authentication getAuthentication() + { + return SecurityContextHolder.getContext().getAuthentication(); + } + + /** + * 生成BCryptPasswordEncoder密码 + * + * @param password 密码 + * @return 加密字符串 + */ + public static String encryptPassword(String password) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } + + /** + * 判断密码是否相同 + * + * @param rawPassword 真实密码 + * @param encodedPassword 加密后字符 + * @return 结果 + */ + public static boolean matchesPassword(String rawPassword, String encodedPassword) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.matches(rawPassword, encodedPassword); + } + + /** + * 是否为管理员 + * + * @param userId 用户ID + * @return 结果 + */ + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/ServletUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/ServletUtils.java new file mode 100644 index 0000000..9e6fd59 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/ServletUtils.java @@ -0,0 +1,136 @@ +package com.ruoyi.common.utils; + +import java.io.IOException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import com.ruoyi.common.core.text.Convert; + +/** + * 客户端工具类 + * + * @author ruoyi + */ +public class ServletUtils +{ + /** + * 获取String参数 + */ + public static String getParameter(String name) + { + return getRequest().getParameter(name); + } + + /** + * 获取String参数 + */ + public static String getParameter(String name, String defaultValue) + { + return Convert.toStr(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name) + { + return Convert.toInt(getRequest().getParameter(name)); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name, Integer defaultValue) + { + return Convert.toInt(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest() + { + return getRequestAttributes().getRequest(); + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse() + { + return getRequestAttributes().getResponse(); + } + + /** + * 获取session + */ + public static HttpSession getSession() + { + return getRequest().getSession(); + } + + public static ServletRequestAttributes getRequestAttributes() + { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } + + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + * @return null + */ + public static String renderString(HttpServletResponse response, String string) + { + try + { + response.setStatus(200); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().print(string); + } + catch (IOException e) + { + e.printStackTrace(); + } + return null; + } + + /** + * 是否是Ajax异步请求 + * + * @param request + */ + public static boolean isAjaxRequest(HttpServletRequest request) + { + String accept = request.getHeader("accept"); + if (accept != null && accept.indexOf("application/json") != -1) + { + return true; + } + + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1) + { + return true; + } + + String uri = request.getRequestURI(); + if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) + { + return true; + } + + String ajax = request.getParameter("__ajax"); + if (StringUtils.inStringIgnoreCase(ajax, "json", "xml")) + { + return true; + } + return false; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/StringUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/StringUtils.java new file mode 100644 index 0000000..a865c35 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/StringUtils.java @@ -0,0 +1,453 @@ +package com.ruoyi.common.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import com.ruoyi.common.core.text.StrFormatter; + +/** + * 字符串工具类 + * + * @author ruoyi + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils +{ + /** 空字符串 */ + private static final String NULLSTR = ""; + + /** 下划线 */ + private static final char SEPARATOR = '_'; + + /** + * 获取参数不为空值 + * + * @param value defaultValue 要判断的value + * @return value 返回值 + */ + public static <T> T nvl(T value, T defaultValue) + { + return value != null ? value : defaultValue; + } + + /** + * * 判断一个Collection是否为空, 包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Collection<?> coll) + { + return isNull(coll) || coll.isEmpty(); + } + + /** + * * 判断一个Collection是否非空,包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Collection<?> coll) + { + return !isEmpty(coll); + } + + /** + * * 判断一个对象数组是否为空 + * + * @param objects 要判断的对象数组 + ** @return true:为空 false:非空 + */ + public static boolean isEmpty(Object[] objects) + { + return isNull(objects) || (objects.length == 0); + } + + /** + * * 判断一个对象数组是否非空 + * + * @param objects 要判断的对象数组 + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Object[] objects) + { + return !isEmpty(objects); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Map<?, ?> map) + { + return isNull(map) || map.isEmpty(); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Map<?, ?> map) + { + return !isEmpty(map); + } + + /** + * * 判断一个字符串是否为空串 + * + * @param str String + * @return true:为空 false:非空 + */ + public static boolean isEmpty(String str) + { + return isNull(str) || NULLSTR.equals(str.trim()); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @param str String + * @return true:非空串 false:空串 + */ + public static boolean isNotEmpty(String str) + { + return !isEmpty(str); + } + + /** + * * 判断一个对象是否为空 + * + * @param object Object + * @return true:为空 false:非空 + */ + public static boolean isNull(Object object) + { + return object == null; + } + + /** + * * 判断一个对象是否非空 + * + * @param object Object + * @return true:非空 false:空 + */ + public static boolean isNotNull(Object object) + { + return !isNull(object); + } + + /** + * * 判断一个对象是否是数组类型(Java基本型别的数组) + * + * @param object 对象 + * @return true:是数组 false:不是数组 + */ + public static boolean isArray(Object object) + { + return isNotNull(object) && object.getClass().isArray(); + } + + /** + * 去空格 + */ + public static String trim(String str) + { + return (str == null ? "" : str.trim()); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @return 结果 + */ + public static String substring(final String str, int start) + { + if (str == null) + { + return NULLSTR; + } + + if (start < 0) + { + start = str.length() + start; + } + + if (start < 0) + { + start = 0; + } + if (start > str.length()) + { + return NULLSTR; + } + + return str.substring(start); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * @return 结果 + */ + public static String substring(final String str, int start, int end) + { + if (str == null) + { + return NULLSTR; + } + + if (end < 0) + { + end = str.length() + end; + } + if (start < 0) + { + start = str.length() + start; + } + + if (end > str.length()) + { + end = str.length(); + } + + if (start > end) + { + return NULLSTR; + } + + if (start < 0) + { + start = 0; + } + if (end < 0) + { + end = 0; + } + + return str.substring(start, end); + } + + /** + * 格式化文本, {} 表示占位符<br> + * 此方法只是简单将占位符 {} 按照顺序替换为参数<br> + * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br> + * 例:<br> + * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br> + * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br> + * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br> + * + * @param template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * @return 格式化后的文本 + */ + public static String format(String template, Object... params) + { + if (isEmpty(params) || isEmpty(template)) + { + return template; + } + return StrFormatter.format(template, params); + } + + /** + * 字符串转set + * + * @param str 字符串 + * @param sep 分隔符 + * @return set集合 + */ + public static final Set<String> str2Set(String str, String sep) + { + return new HashSet<String>(str2List(str, sep, true, false)); + } + + /** + * 字符串转list + * + * @param str 字符串 + * @param sep 分隔符 + * @param filterBlank 过滤纯空白 + * @param trim 去掉首尾空白 + * @return list集合 + */ + public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) + { + List<String> list = new ArrayList<String>(); + if (StringUtils.isEmpty(str)) + { + return list; + } + + // 过滤空白字符串 + if (filterBlank && StringUtils.isBlank(str)) + { + return list; + } + String[] split = str.split(sep); + for (String string : split) + { + if (filterBlank && StringUtils.isBlank(string)) + { + continue; + } + if (trim) + { + string = string.trim(); + } + list.add(string); + } + + return list; + } + + /** + * 下划线转驼峰命名 + */ + public static String toUnderScoreCase(String str) + { + if (str == null) + { + return null; + } + StringBuilder sb = new StringBuilder(); + // 前置字符是否大写 + boolean preCharIsUpperCase = true; + // 当前字符是否大写 + boolean curreCharIsUpperCase = true; + // 下一字符是否大写 + boolean nexteCharIsUpperCase = true; + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if (i > 0) + { + preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); + } + else + { + preCharIsUpperCase = false; + } + + curreCharIsUpperCase = Character.isUpperCase(c); + + if (i < (str.length() - 1)) + { + nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); + } + + if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) + { + sb.append(SEPARATOR); + } + else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) + { + sb.append(SEPARATOR); + } + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inStringIgnoreCase(String str, String... strs) + { + if (str != null && strs != null) + { + for (String s : strs) + { + if (str.equalsIgnoreCase(trim(s))) + { + return true; + } + } + } + return false; + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase(String name) + { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) + { + // 没必要转换 + return ""; + } + else if (!name.contains("_")) + { + // 不含下划线,仅将首字母大写 + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + // 用下划线将原始字符串分割 + String[] camels = name.split("_"); + for (String camel : camels) + { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) + { + continue; + } + // 首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + + /** + * 驼峰式命名法 例如:user_name->userName + */ + public static String toCamelCase(String s) + { + if (s == null) + { + return null; + } + s = s.toLowerCase(); + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) + { + char c = s.charAt(i); + + if (c == SEPARATOR) + { + upperCase = true; + } + else if (upperCase) + { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } + else + { + sb.append(c); + } + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/Threads.java b/ruoyi/src/main/java/com/ruoyi/common/utils/Threads.java new file mode 100644 index 0000000..395920a --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/Threads.java @@ -0,0 +1,99 @@ +package com.ruoyi.common.utils; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 线程相关工具类. + * + * @author ruoyi + */ +public class Threads +{ + private static final Logger logger = LoggerFactory.getLogger(Threads.class); + + /** + * sleep等待,单位为毫秒 + */ + public static void sleep(long milliseconds) + { + try + { + Thread.sleep(milliseconds); + } + catch (InterruptedException e) + { + return; + } + } + + /** + * 停止线程池 + * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. + * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. + * 如果仍人超時,則強制退出. + * 另对在shutdown时线程本身被调用中断做了处理. + */ + public static void shutdownAndAwaitTermination(ExecutorService pool) + { + if (pool != null && !pool.isShutdown()) + { + pool.shutdown(); + try + { + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) + { + pool.shutdownNow(); + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) + { + logger.info("Pool did not terminate"); + } + } + } + catch (InterruptedException ie) + { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } + + /** + * 打印线程异常信息 + */ + public static void printException(Runnable r, Throwable t) + { + if (t == null && r instanceof Future<?>) + { + try + { + Future<?> future = (Future<?>) r; + if (future.isDone()) + { + future.get(); + } + } + catch (CancellationException ce) + { + t = ce; + } + catch (ExecutionException ee) + { + t = ee.getCause(); + } + catch (InterruptedException ie) + { + Thread.currentThread().interrupt(); + } + } + if (t != null) + { + logger.error(t.getMessage(), t); + } + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/VerifyCodeUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/VerifyCodeUtils.java new file mode 100644 index 0000000..80b8c84 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/VerifyCodeUtils.java @@ -0,0 +1,226 @@ +package com.ruoyi.common.utils; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.OutputStream; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Random; +import javax.imageio.ImageIO; + +/** + * 验证码工具类 + * + * @author ruoyi + */ +public class VerifyCodeUtils +{ + // 使用到Algerian字体,系统里没有的话需要安装字体,字体只显示大写,去掉了1,0,i,o几个容易混淆的字符 + public static final String VERIFY_CODES = "123456789ABCDEFGHJKLMNPQRSTUVWXYZ"; + + private static Random random = new SecureRandom(); + + /** + * 使用系统默认字符源生成验证码 + * + * @param verifySize 验证码长度 + * @return + */ + public static String generateVerifyCode(int verifySize) + { + return generateVerifyCode(verifySize, VERIFY_CODES); + } + + /** + * 使用指定源生成验证码 + * + * @param verifySize 验证码长度 + * @param sources 验证码字符源 + * @return + */ + public static String generateVerifyCode(int verifySize, String sources) + { + if (sources == null || sources.length() == 0) + { + sources = VERIFY_CODES; + } + int codesLen = sources.length(); + Random rand = new Random(System.currentTimeMillis()); + StringBuilder verifyCode = new StringBuilder(verifySize); + for (int i = 0; i < verifySize; i++) + { + verifyCode.append(sources.charAt(rand.nextInt(codesLen - 1))); + } + return verifyCode.toString(); + } + + /** + * 输出指定验证码图片流 + * + * @param w + * @param h + * @param os + * @param code + * @throws IOException + */ + public static void outputImage(int w, int h, OutputStream os, String code) throws IOException + { + int verifySize = code.length(); + BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); + Random rand = new Random(); + Graphics2D g2 = image.createGraphics(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + Color[] colors = new Color[5]; + Color[] colorSpaces = new Color[] { Color.WHITE, Color.CYAN, Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA, + Color.ORANGE, Color.PINK, Color.YELLOW }; + float[] fractions = new float[colors.length]; + for (int i = 0; i < colors.length; i++) + { + colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)]; + fractions[i] = rand.nextFloat(); + } + Arrays.sort(fractions); + + g2.setColor(Color.GRAY);// 设置边框色 + g2.fillRect(0, 0, w, h); + + Color c = getRandColor(200, 250); + g2.setColor(c);// 设置背景色 + g2.fillRect(0, 2, w, h - 4); + + // 绘制干扰线 + Random random = new Random(); + g2.setColor(getRandColor(160, 200));// 设置线条的颜色 + for (int i = 0; i < 20; i++) + { + int x = random.nextInt(w - 1); + int y = random.nextInt(h - 1); + int xl = random.nextInt(6) + 1; + int yl = random.nextInt(12) + 1; + g2.drawLine(x, y, x + xl + 40, y + yl + 20); + } + + // 添加噪点 + float yawpRate = 0.05f;// 噪声率 + int area = (int) (yawpRate * w * h); + for (int i = 0; i < area; i++) + { + int x = random.nextInt(w); + int y = random.nextInt(h); + int rgb = getRandomIntColor(); + image.setRGB(x, y, rgb); + } + + shear(g2, w, h, c);// 使图片扭曲 + + g2.setColor(getRandColor(100, 160)); + int fontSize = h - 4; + Font font = new Font("Algerian", Font.ITALIC, fontSize); + g2.setFont(font); + char[] chars = code.toCharArray(); + for (int i = 0; i < verifySize; i++) + { + AffineTransform affine = new AffineTransform(); + affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1), + (w / verifySize) * i + fontSize / 2, h / 2); + g2.setTransform(affine); + g2.drawChars(chars, i, 1, ((w - 10) / verifySize) * i + 5, h / 2 + fontSize / 2 - 10); + } + + g2.dispose(); + ImageIO.write(image, "jpg", os); + } + + private static Color getRandColor(int fc, int bc) + { + if (fc > 255) + fc = 255; + if (bc > 255) + bc = 255; + int r = fc + random.nextInt(bc - fc); + int g = fc + random.nextInt(bc - fc); + int b = fc + random.nextInt(bc - fc); + return new Color(r, g, b); + } + + private static int getRandomIntColor() + { + int[] rgb = getRandomRgb(); + int color = 0; + for (int c : rgb) + { + color = color << 8; + color = color | c; + } + return color; + } + + private static int[] getRandomRgb() + { + int[] rgb = new int[3]; + for (int i = 0; i < 3; i++) + { + rgb[i] = random.nextInt(255); + } + return rgb; + } + + private static void shear(Graphics g, int w1, int h1, Color color) + { + shearX(g, w1, h1, color); + shearY(g, w1, h1, color); + } + + private static void shearX(Graphics g, int w1, int h1, Color color) + { + + int period = random.nextInt(2); + + boolean borderGap = true; + int frames = 1; + int phase = random.nextInt(2); + + for (int i = 0; i < h1; i++) + { + double d = (double) (period >> 1) + * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames); + g.copyArea(0, i, w1, 1, (int) d, 0); + if (borderGap) + { + g.setColor(color); + g.drawLine((int) d, i, 0, i); + g.drawLine((int) d + w1, i, w1, i); + } + } + + } + + private static void shearY(Graphics g, int w1, int h1, Color color) + { + + int period = random.nextInt(40) + 10; // 50; + + boolean borderGap = true; + int frames = 20; + int phase = 7; + for (int i = 0; i < w1; i++) + { + double d = (double) (period >> 1) + * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames); + g.copyArea(i, 0, 1, h1, 0, (int) d); + if (borderGap) + { + g.setColor(color); + g.drawLine(i, (int) d, i, 0); + g.drawLine(i, (int) d + h1, i, h1); + } + + } + } +} \ No newline at end of file diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java new file mode 100644 index 0000000..48d3ada --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java @@ -0,0 +1,243 @@ +package com.ruoyi.common.utils.file; + +import java.io.File; +import java.io.IOException; +import org.apache.commons.io.FilenameUtils; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException; +import com.ruoyi.common.exception.file.FileSizeLimitExceededException; +import com.ruoyi.common.exception.file.InvalidExtensionException; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.security.Md5Utils; +import com.ruoyi.framework.config.RuoYiConfig; + +/** + * 文件上传工具类 + * + * @author ruoyi + */ +public class FileUploadUtils +{ + /** + * 默认大小 50M + */ + public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024; + + /** + * 默认的文件名最大长度 100 + */ + public static final int DEFAULT_FILE_NAME_LENGTH = 100; + + /** + * 默认上传的地址 + */ + private static String defaultBaseDir = RuoYiConfig.getProfile(); + + private static int counter = 0; + + public static void setDefaultBaseDir(String defaultBaseDir) + { + FileUploadUtils.defaultBaseDir = defaultBaseDir; + } + + public static String getDefaultBaseDir() + { + return defaultBaseDir; + } + + /** + * 以默认配置进行文件上传 + * + * @param file 上传的文件 + * @return 文件名称 + * @throws Exception + */ + public static final String upload(MultipartFile file) throws IOException + { + try + { + return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 根据文件路径上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @return 文件名称 + * @throws IOException + */ + public static final String upload(String baseDir, MultipartFile file) throws IOException + { + try + { + return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 文件上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @param extension 上传文件类型 + * @return 返回上传成功的文件名 + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws FileNameLengthLimitExceededException 文件名太长 + * @throws IOException 比如读写文件出错时 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException + { + int fileNamelength = file.getOriginalFilename().length(); + if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) + { + throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); + } + + assertAllowed(file, allowedExtension); + + String fileName = extractFilename(file); + + File desc = getAbsoluteFile(baseDir, fileName); + file.transferTo(desc); + String pathFileName = getPathFileName(baseDir, fileName); + return pathFileName; + } + + /** + * 编码文件名 + */ + public static final String extractFilename(MultipartFile file) + { + String fileName = file.getOriginalFilename(); + String extension = getExtension(file); + fileName = DateUtils.datePath() + "/" + encodingFilename(fileName) + "." + extension; + return fileName; + } + + private static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException + { + File desc = new File(uploadDir + File.separator + fileName); + + if (!desc.getParentFile().exists()) + { + desc.getParentFile().mkdirs(); + } + if (!desc.exists()) + { + desc.createNewFile(); + } + return desc; + } + + private static final String getPathFileName(String uploadDir, String fileName) throws IOException + { + int dirLastIndex = uploadDir.lastIndexOf("/") + 1; + String currentDir = StringUtils.substring(uploadDir, dirLastIndex); + String pathFileName = Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName; + return pathFileName; + } + + /** + * 编码文件名 + */ + private static final String encodingFilename(String fileName) + { + fileName = fileName.replace("_", " "); + fileName = Md5Utils.hash(fileName + System.nanoTime() + counter++); + return fileName; + } + + /** + * 文件大小校验 + * + * @param file 上传的文件 + * @return + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws InvalidExtensionException + */ + public static final void assertAllowed(MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, InvalidExtensionException + { + long size = file.getSize(); + if (DEFAULT_MAX_SIZE != -1 && size > DEFAULT_MAX_SIZE) + { + throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); + } + + String fileName = file.getOriginalFilename(); + String extension = getExtension(file); + if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) + { + if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) + { + throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) + { + throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) + { + throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, + fileName); + } + else + { + throw new InvalidExtensionException(allowedExtension, extension, fileName); + } + } + + } + + /** + * 判断MIME类型是否是允许的MIME类型 + * + * @param extension + * @param allowedExtension + * @return + */ + public static final boolean isAllowedExtension(String extension, String[] allowedExtension) + { + for (String str : allowedExtension) + { + if (str.equalsIgnoreCase(extension)) + { + return true; + } + } + return false; + } + + /** + * 获取文件名的后缀 + * + * @param file 表单文件 + * @return 后缀名 + */ + public static final String getExtension(MultipartFile file) + { + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + if (StringUtils.isEmpty(extension)) + { + extension = MimeTypeUtils.getExtension(file.getContentType()); + } + return extension; + } +} \ No newline at end of file diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/file/FileUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/file/FileUtils.java new file mode 100644 index 0000000..03f5aa2 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/file/FileUtils.java @@ -0,0 +1,142 @@ +package com.ruoyi.common.utils.file; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import javax.servlet.http.HttpServletRequest; + +/** + * 文件处理工具类 + * + * @author ruoyi + */ +public class FileUtils +{ + public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; + + /** + * 输出指定文件的byte数组 + * + * @param filePath 文件路径 + * @param os 输出流 + * @return + */ + public static void writeBytes(String filePath, OutputStream os) throws IOException + { + FileInputStream fis = null; + try + { + File file = new File(filePath); + if (!file.exists()) + { + throw new FileNotFoundException(filePath); + } + fis = new FileInputStream(file); + byte[] b = new byte[1024]; + int length; + while ((length = fis.read(b)) > 0) + { + os.write(b, 0, length); + } + } + catch (IOException e) + { + throw e; + } + finally + { + if (os != null) + { + try + { + os.close(); + } + catch (IOException e1) + { + e1.printStackTrace(); + } + } + if (fis != null) + { + try + { + fis.close(); + } + catch (IOException e1) + { + e1.printStackTrace(); + } + } + } + } + + /** + * 删除文件 + * + * @param filePath 文件 + * @return + */ + public static boolean deleteFile(String filePath) + { + boolean flag = false; + File file = new File(filePath); + // 路径为文件且不为空则进行删除 + if (file.isFile() && file.exists()) + { + file.delete(); + flag = true; + } + return flag; + } + + /** + * 文件名称验证 + * + * @param filename 文件名称 + * @return true 正常 false 非法 + */ + public static boolean isValidFilename(String filename) + { + return filename.matches(FILENAME_PATTERN); + } + + /** + * 下载文件名重新编码 + * + * @param request 请求对象 + * @param fileName 文件名 + * @return 编码后的文件名 + */ + public static String setFileDownloadHeader(HttpServletRequest request, String fileName) + throws UnsupportedEncodingException + { + final String agent = request.getHeader("USER-AGENT"); + String filename = fileName; + if (agent.contains("MSIE")) + { + // IE浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + filename = filename.replace("+", " "); + } + else if (agent.contains("Firefox")) + { + // 火狐浏览器 + filename = new String(fileName.getBytes(), "ISO8859-1"); + } + else if (agent.contains("Chrome")) + { + // google浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + else + { + // 其它浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + return filename; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java new file mode 100644 index 0000000..d179a91 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java @@ -0,0 +1,55 @@ +package com.ruoyi.common.utils.file; + +/** + * 媒体类型工具类 + * + * @author ruoyi + */ +public class MimeTypeUtils +{ + public static final String IMAGE_PNG = "image/png"; + + public static final String IMAGE_JPG = "image/jpg"; + + public static final String IMAGE_JPEG = "image/jpeg"; + + public static final String IMAGE_BMP = "image/bmp"; + + public static final String IMAGE_GIF = "image/gif"; + + public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" }; + + public static final String[] FLASH_EXTENSION = { "swf", "flv" }; + + public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", + "asf", "rm", "rmvb" }; + + public static final String[] DEFAULT_ALLOWED_EXTENSION = { + // 图片 + "bmp", "gif", "jpg", "jpeg", "png", + // word excel powerpoint + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", + // 压缩文件 + "rar", "zip", "gz", "bz2", + // pdf + "pdf" }; + + public static String getExtension(String prefix) + { + switch (prefix) + { + case IMAGE_PNG: + return "png"; + case IMAGE_JPG: + return "jpg"; + case IMAGE_JPEG: + return "jpeg"; + case IMAGE_BMP: + return "bmp"; + case IMAGE_GIF: + return "gif"; + default: + return ""; + } + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java b/ruoyi/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java new file mode 100644 index 0000000..f6754c4 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java @@ -0,0 +1,152 @@ +package com.ruoyi.common.utils.html; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 转义和反转义工具类 + * + * @author ruoyi + */ +public class EscapeUtil +{ + public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; + + private static final char[][] TEXT = new char[64][]; + + static + { + for (int i = 0; i < 64; i++) + { + TEXT[i] = new char[] { (char) i }; + } + + // special HTML characters + TEXT['\''] = "'".toCharArray(); // 单引号 + TEXT['"'] = """.toCharArray(); // 单引号 + TEXT['&'] = "&".toCharArray(); // &符 + TEXT['<'] = "<".toCharArray(); // 小于号 + TEXT['>'] = ">".toCharArray(); // 大于号 + } + + /** + * 转义文本中的HTML字符为安全的字符 + * + * @param text 被转义的文本 + * @return 转义后的文本 + */ + public static String escape(String text) + { + return encode(text); + } + + /** + * 还原被转义的HTML特殊字符 + * + * @param content 包含转义符的HTML内容 + * @return 转换后的字符串 + */ + public static String unescape(String content) + { + return decode(content); + } + + /** + * 清除所有HTML标签,但是不删除标签内的内容 + * + * @param content 文本 + * @return 清除标签后的文本 + */ + public static String clean(String content) + { + return content.replaceAll(RE_HTML_MARK, ""); + } + + /** + * Escape编码 + * + * @param text 被编码的文本 + * @return 编码后的字符 + */ + private static String encode(String text) + { + int len; + if ((text == null) || ((len = text.length()) == 0)) + { + return StringUtils.EMPTY; + } + StringBuilder buffer = new StringBuilder(len + (len >> 2)); + char c; + for (int i = 0; i < len; i++) + { + c = text.charAt(i); + if (c < 64) + { + buffer.append(TEXT[c]); + } + else + { + buffer.append(c); + } + } + return buffer.toString(); + } + + /** + * Escape解码 + * + * @param content 被转义的内容 + * @return 解码后的字符串 + */ + public static String decode(String content) + { + if (StringUtils.isEmpty(content)) + { + return content; + } + + StringBuilder tmp = new StringBuilder(content.length()); + int lastPos = 0, pos = 0; + char ch; + while (lastPos < content.length()) + { + pos = content.indexOf("%", lastPos); + if (pos == lastPos) + { + if (content.charAt(pos + 1) == 'u') + { + ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); + tmp.append(ch); + lastPos = pos + 6; + } + else + { + ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); + tmp.append(ch); + lastPos = pos + 3; + } + } + else + { + if (pos == -1) + { + tmp.append(content.substring(lastPos)); + lastPos = content.length(); + } + else + { + tmp.append(content.substring(lastPos, pos)); + lastPos = pos; + } + } + } + return tmp.toString(); + } + + public static void main(String[] args) + { + String html = "<script>alert(1);</script>"; + System.out.println(EscapeUtil.clean(html)); + System.out.println(EscapeUtil.escape(html)); + System.out.println(EscapeUtil.unescape(html)); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java new file mode 100644 index 0000000..323dbc1 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java @@ -0,0 +1,248 @@ +package com.ruoyi.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLConnection; +import java.security.cert.X509Certificate; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 通用http发送方法 + * + * @author ruoyi + */ +public class HttpUtils +{ + private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param) + { + StringBuilder result = new StringBuilder(); + BufferedReader in = null; + try + { + String urlNameString = url + "?" + param; + log.info("sendGet - {}", urlNameString); + URL realUrl = new URL(urlNameString); + URLConnection connection = realUrl.openConnection(); + connection.setRequestProperty("accept", "*/*"); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + connection.connect(); + in = new BufferedReader(new InputStreamReader(connection.getInputStream())); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (in != null) + { + in.close(); + } + } + catch (Exception ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendPost(String url, String param) + { + PrintWriter out = null; + BufferedReader in = null; + StringBuilder result = new StringBuilder(); + try + { + String urlNameString = url + "?" + param; + log.info("sendPost - {}", urlNameString); + URL realUrl = new URL(urlNameString); + URLConnection conn = realUrl.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + out = new PrintWriter(conn.getOutputStream()); + out.print(param); + out.flush(); + in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8")); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (out != null) + { + out.close(); + } + if (in != null) + { + in.close(); + } + } + catch (IOException ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + public static String sendSSLPost(String url, String param) + { + StringBuilder result = new StringBuilder(); + String urlNameString = url + "?" + param; + try + { + log.info("sendSSLPost - {}", urlNameString); + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom()); + URL console = new URL(urlNameString); + HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + + conn.setSSLSocketFactory(sc.getSocketFactory()); + conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); + conn.connect(); + InputStream is = conn.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String ret = ""; + while ((ret = br.readLine()) != null) + { + if (ret != null && !ret.trim().equals("")) + { + result.append(new String(ret.getBytes("ISO-8859-1"), "utf-8")); + } + } + log.info("recv - {}", result); + conn.disconnect(); + br.close(); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); + } + return result.toString(); + } + + private static class TrustAnyTrustManager implements X509TrustManager + { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public X509Certificate[] getAcceptedIssuers() + { + return new X509Certificate[] {}; + } + } + + private static class TrustAnyHostnameVerifier implements HostnameVerifier + { + @Override + public boolean verify(String hostname, SSLSession session) + { + return true; + } + } +} \ No newline at end of file diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java new file mode 100644 index 0000000..d70351f --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java @@ -0,0 +1,41 @@ +package com.ruoyi.common.utils.ip; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.alibaba.fastjson.JSONObject; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.HttpUtils; + +/** + * 获取地址类 + * + * @author ruoyi + */ +public class AddressUtils +{ + private static final Logger log = LoggerFactory.getLogger(AddressUtils.class); + + public static final String IP_URL = "http://ip.taobao.com/service/getIpInfo.php"; + + public static String getRealAddressByIP(String ip) + { + String address = "XX XX"; + // 内网不查询 + if (IpUtils.internalIp(ip)) + { + return "内网IP"; + } + String rspStr = HttpUtils.sendPost(IP_URL, "ip=" + ip); + if (StringUtils.isEmpty(rspStr)) + { + log.error("获取地理位置异常 {}", ip); + return address; + } + JSONObject obj = JSONObject.parseObject(rspStr); + JSONObject data = obj.getObject("data", JSONObject.class); + String region = data.getString("region"); + String city = data.getString("city"); + address = region + " " + city; + return address; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java new file mode 100644 index 0000000..9e050e9 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java @@ -0,0 +1,189 @@ +package com.ruoyi.common.utils.ip; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import javax.servlet.http.HttpServletRequest; +import com.ruoyi.common.utils.StringUtils; + +/** + * 获取IP方法 + * + * @author ruoyi + */ +public class IpUtils +{ + public static String getIpAddr(HttpServletRequest request) + { + if (request == null) + { + return "unknown"; + } + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Forwarded-For"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Real-IP"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getRemoteAddr(); + } + + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip; + } + + public static boolean internalIp(String ip) + { + byte[] addr = textToNumericFormatV4(ip); + return internalIp(addr) || "127.0.0.1".equals(ip); + } + + private static boolean internalIp(byte[] addr) + { + if (StringUtils.isNull(addr) || addr.length < 2) + { + return true; + } + final byte b0 = addr[0]; + final byte b1 = addr[1]; + // 10.x.x.x/8 + final byte SECTION_1 = 0x0A; + // 172.16.x.x/12 + final byte SECTION_2 = (byte) 0xAC; + final byte SECTION_3 = (byte) 0x10; + final byte SECTION_4 = (byte) 0x1F; + // 192.168.x.x/16 + final byte SECTION_5 = (byte) 0xC0; + final byte SECTION_6 = (byte) 0xA8; + switch (b0) + { + case SECTION_1: + return true; + case SECTION_2: + if (b1 >= SECTION_3 && b1 <= SECTION_4) + { + return true; + } + case SECTION_5: + switch (b1) + { + case SECTION_6: + return true; + } + default: + return false; + } + } + + /** + * 将IPv4地址转换成字节 + * + * @param text IPv4地址 + * @return byte 字节 + */ + public static byte[] textToNumericFormatV4(String text) + { + if (text.length() == 0) + { + return null; + } + + byte[] bytes = new byte[4]; + String[] elements = text.split("\\.", -1); + try + { + long l; + int i; + switch (elements.length) + { + case 1: + l = Long.parseLong(elements[0]); + if ((l < 0L) || (l > 4294967295L)) + return null; + bytes[0] = (byte) (int) (l >> 24 & 0xFF); + bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 2: + l = Integer.parseInt(elements[0]); + if ((l < 0L) || (l > 255L)) + return null; + bytes[0] = (byte) (int) (l & 0xFF); + l = Integer.parseInt(elements[1]); + if ((l < 0L) || (l > 16777215L)) + return null; + bytes[1] = (byte) (int) (l >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 3: + for (i = 0; i < 2; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + return null; + bytes[i] = (byte) (int) (l & 0xFF); + } + l = Integer.parseInt(elements[2]); + if ((l < 0L) || (l > 65535L)) + return null; + bytes[2] = (byte) (int) (l >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 4: + for (i = 0; i < 4; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + return null; + bytes[i] = (byte) (int) (l & 0xFF); + } + break; + default: + return null; + } + } + catch (NumberFormatException e) + { + return null; + } + return bytes; + } + + public static String getHostIp() + { + try + { + return InetAddress.getLocalHost().getHostAddress(); + } + catch (UnknownHostException e) + { + } + return "127.0.0.1"; + } + + public static String getHostName() + { + try + { + return InetAddress.getLocalHost().getHostName(); + } + catch (UnknownHostException e) + { + } + return "未知"; + } +} \ No newline at end of file diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/security/Md5Utils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/security/Md5Utils.java new file mode 100644 index 0000000..40e8030 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/security/Md5Utils.java @@ -0,0 +1,66 @@ +package com.ruoyi.common.utils.security; + +import java.security.MessageDigest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Md5加密方法 + * + * @author ruoyi + */ +public class Md5Utils +{ + private static final Logger log = LoggerFactory.getLogger(Md5Utils.class); + + private static byte[] md5(String s) + { + MessageDigest algorithm; + try + { + algorithm = MessageDigest.getInstance("MD5"); + algorithm.reset(); + algorithm.update(s.getBytes("UTF-8")); + byte[] messageDigest = algorithm.digest(); + return messageDigest; + } + catch (Exception e) + { + log.error("MD5 Error...", e); + } + return null; + } + + private static final String toHex(byte hash[]) + { + if (hash == null) + { + return null; + } + StringBuffer buf = new StringBuffer(hash.length * 2); + int i; + + for (i = 0; i < hash.length; i++) + { + if ((hash[i] & 0xff) < 0x10) + { + buf.append("0"); + } + buf.append(Long.toString(hash[i] & 0xff, 16)); + } + return buf.toString(); + } + + public static String hash(String s) + { + try + { + return new String(toHex(md5(s)).getBytes("UTF-8"), "UTF-8"); + } + catch (Exception e) + { + log.error("not supported charset...{}", e); + return s; + } + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/sign/Base64.java b/ruoyi/src/main/java/com/ruoyi/common/utils/sign/Base64.java new file mode 100644 index 0000000..f2c1bfe --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/sign/Base64.java @@ -0,0 +1,291 @@ +package com.ruoyi.common.utils.sign; + +/** + * Base64工具类 + * + * @author ruoyi + */ +public final class Base64 +{ + static private final int BASELENGTH = 128; + static private final int LOOKUPLENGTH = 64; + static private final int TWENTYFOURBITGROUP = 24; + static private final int EIGHTBIT = 8; + static private final int SIXTEENBIT = 16; + static private final int FOURBYTE = 4; + static private final int SIGN = -128; + static private final char PAD = '='; + static final private byte[] base64Alphabet = new byte[BASELENGTH]; + static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; + + static + { + for (int i = 0; i < BASELENGTH; ++i) + { + base64Alphabet[i] = -1; + } + for (int i = 'Z'; i >= 'A'; i--) + { + base64Alphabet[i] = (byte) (i - 'A'); + } + for (int i = 'z'; i >= 'a'; i--) + { + base64Alphabet[i] = (byte) (i - 'a' + 26); + } + + for (int i = '9'; i >= '0'; i--) + { + base64Alphabet[i] = (byte) (i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + + for (int i = 0; i <= 25; i++) + { + lookUpBase64Alphabet[i] = (char) ('A' + i); + } + + for (int i = 26, j = 0; i <= 51; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('a' + j); + } + + for (int i = 52, j = 0; i <= 61; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('0' + j); + } + lookUpBase64Alphabet[62] = (char) '+'; + lookUpBase64Alphabet[63] = (char) '/'; + } + + private static boolean isWhiteSpace(char octect) + { + return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); + } + + private static boolean isPad(char octect) + { + return (octect == PAD); + } + + private static boolean isData(char octect) + { + return (octect < BASELENGTH && base64Alphabet[octect] != -1); + } + + /** + * Encodes hex octects into Base64 + * + * @param binaryData Array containing binaryData + * @return Encoded Base64 array + */ + public static String encode(byte[] binaryData) + { + if (binaryData == null) + { + return null; + } + + int lengthDataBits = binaryData.length * EIGHTBIT; + if (lengthDataBits == 0) + { + return ""; + } + + int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; + int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; + int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets; + char encodedData[] = null; + + encodedData = new char[numberQuartet * 4]; + + byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; + + int encodedIndex = 0; + int dataIndex = 0; + + for (int i = 0; i < numberTriplets; i++) + { + b1 = binaryData[dataIndex++]; + b2 = binaryData[dataIndex++]; + b3 = binaryData[dataIndex++]; + + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; + } + + // form integral number of 6-bit groups + if (fewerThan24bits == EIGHTBIT) + { + b1 = binaryData[dataIndex]; + k = (byte) (b1 & 0x03); + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; + encodedData[encodedIndex++] = PAD; + encodedData[encodedIndex++] = PAD; + } + else if (fewerThan24bits == SIXTEENBIT) + { + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; + encodedData[encodedIndex++] = PAD; + } + return new String(encodedData); + } + + /** + * Decodes Base64 data into octects + * + * @param encoded string containing Base64 data + * @return Array containind decoded data. + */ + public static byte[] decode(String encoded) + { + if (encoded == null) + { + return null; + } + + char[] base64Data = encoded.toCharArray(); + // remove white spaces + int len = removeWhiteSpace(base64Data); + + if (len % FOURBYTE != 0) + { + return null;// should be divisible by four + } + + int numberQuadruple = (len / FOURBYTE); + + if (numberQuadruple == 0) + { + return new byte[0]; + } + + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; + char d1 = 0, d2 = 0, d3 = 0, d4 = 0; + + int i = 0; + int encodedIndex = 0; + int dataIndex = 0; + decodedData = new byte[(numberQuadruple) * 3]; + + for (; i < numberQuadruple - 1; i++) + { + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) + || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) + { + return null; + } // if found "no data" just return null + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + } + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) + { + return null;// if found "no data" just return null + } + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + + d3 = base64Data[dataIndex++]; + d4 = base64Data[dataIndex++]; + if (!isData((d3)) || !isData((d4))) + {// Check if they are PAD characters + if (isPad(d3) && isPad(d4)) + { + if ((b2 & 0xf) != 0)// last 4 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 1]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + return tmp; + } + else if (!isPad(d3) && isPad(d4)) + { + b3 = base64Alphabet[d3]; + if ((b3 & 0x3) != 0)// last 2 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 2]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + return tmp; + } + else + { + return null; + } + } + else + { // No PAD e.g 3cQl + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + + } + return decodedData; + } + + /** + * remove WhiteSpace from MIME containing encoded Base64 data. + * + * @param data the byte array of base64 data (with WS) + * @return the new length + */ + private static int removeWhiteSpace(char[] data) + { + if (data == null) + { + return 0; + } + + // count characters that's not whitespace + int newSize = 0; + int len = data.length; + for (int i = 0; i < len; i++) + { + if (!isWhiteSpace(data[i])) + { + data[newSize++] = data[i]; + } + } + return newSize; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java new file mode 100644 index 0000000..e031159 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java @@ -0,0 +1,114 @@ +package com.ruoyi.common.utils.spring; + +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.stereotype.Component; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + * @author ruoyi + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor +{ + /** Spring应用上下文环境 */ + private static ConfigurableListableBeanFactory beanFactory; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException + { + SpringUtils.beanFactory = beanFactory; + } + + /** + * 获取对象 + * + * @param name + * @return Object 一个以所给名字注册的bean的实例 + * @throws org.springframework.beans.BeansException + * + */ + @SuppressWarnings("unchecked") + public static <T> T getBean(String name) throws BeansException + { + return (T) beanFactory.getBean(name); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * @return + * @throws org.springframework.beans.BeansException + * + */ + public static <T> T getBean(Class<T> clz) throws BeansException + { + T result = (T) beanFactory.getBean(clz); + return result; + } + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) + { + return beanFactory.containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * @return boolean + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.isSingleton(name); + } + + /** + * @param name + * @return Class 注册对象的类型 + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static Class<?> getType(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + * @return + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static <T> T getAopProxy(T invoker) + { + return (T) AopContext.currentProxy(); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java b/ruoyi/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java new file mode 100644 index 0000000..b8aeaa1 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java @@ -0,0 +1,36 @@ +package com.ruoyi.common.utils.sql; + +import com.ruoyi.common.utils.StringUtils; + +/** + * sql操作工具类 + * + * @author ruoyi + */ +public class SqlUtil +{ + /** + * 仅支持字母、数字、下划线、空格、逗号(支持多个字段排序) + */ + public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,]+"; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) + { + if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) + { + return StringUtils.EMPTY; + } + return value; + } + + /** + * 验证 order by 语法是否符合规范 + */ + public static boolean isValidOrderBySql(String value) + { + return value.matches(SQL_PATTERN); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/xss/XssFilter.java b/ruoyi/src/main/java/com/ruoyi/common/xss/XssFilter.java new file mode 100644 index 0000000..d307fbc --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/xss/XssFilter.java @@ -0,0 +1,97 @@ +package com.ruoyi.common.xss; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import com.ruoyi.common.utils.StringUtils; + +/** + * 防止XSS攻击的过滤器 + * + * @author ruoyi + */ +public class XssFilter implements Filter +{ + /** + * 排除链接 + */ + public List<String> excludes = new ArrayList<>(); + + /** + * xss过滤开关 + */ + public boolean enabled = false; + + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + String tempExcludes = filterConfig.getInitParameter("excludes"); + String tempEnabled = filterConfig.getInitParameter("enabled"); + if (StringUtils.isNotEmpty(tempExcludes)) + { + String[] url = tempExcludes.split(","); + for (int i = 0; url != null && i < url.length; i++) + { + excludes.add(url[i]); + } + } + if (StringUtils.isNotEmpty(tempEnabled)) + { + enabled = Boolean.valueOf(tempEnabled); + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + if (handleExcludeURL(req, resp)) + { + chain.doFilter(request, response); + return; + } + XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); + chain.doFilter(xssRequest, response); + } + + private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) + { + if (!enabled) + { + return true; + } + if (excludes == null || excludes.isEmpty()) + { + return false; + } + String url = request.getServletPath(); + for (String pattern : excludes) + { + Pattern p = Pattern.compile("^" + pattern); + Matcher m = p.matcher(url); + if (m.find()) + { + return true; + } + } + return false; + } + + @Override + public void destroy() + { + + } +} \ No newline at end of file diff --git a/ruoyi/src/main/java/com/ruoyi/common/xss/XssHttpServletRequestWrapper.java b/ruoyi/src/main/java/com/ruoyi/common/xss/XssHttpServletRequestWrapper.java new file mode 100644 index 0000000..1b90c88 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/xss/XssHttpServletRequestWrapper.java @@ -0,0 +1,105 @@ +package com.ruoyi.common.xss; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import org.apache.commons.io.IOUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.html.EscapeUtil; + +/** + * XSS过滤处理 + * + * @author ruoyi + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper +{ + /** + * @param request + */ + public XssHttpServletRequestWrapper(HttpServletRequest request) + { + super(request); + } + + @Override + public String[] getParameterValues(String name) + { + String[] values = super.getParameterValues(name); + if (values != null) + { + int length = values.length; + String[] escapseValues = new String[length]; + for (int i = 0; i < length; i++) + { + // 防xss攻击和过滤前后空格 + escapseValues[i] = EscapeUtil.clean(values[i]).trim(); + } + return escapseValues; + } + return super.getParameterValues(name); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + // 非json类型,直接返回 + if (!isJsonRequest()) + { + return super.getInputStream(); + } + + // 为空,直接返回 + String json = IOUtils.toString(super.getInputStream(), "utf-8"); + if (StringUtils.isEmpty(json)) + { + return super.getInputStream(); + } + + // xss过滤 + json = EscapeUtil.clean(json).trim(); + final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes("utf-8")); + return new ServletInputStream() + { + @Override + public boolean isFinished() + { + return true; + } + + @Override + public boolean isReady() + { + return true; + } + + @Override + public void setReadListener(ReadListener readListener) + { + } + + @Override + public int read() throws IOException + { + return bis.read(); + } + }; + } + + /** + * 是否是Json请求 + * + * @param request + */ + public boolean isJsonRequest() + { + String header = super.getHeader(HttpHeaders.CONTENT_TYPE); + return MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(header) + || MediaType.APPLICATION_JSON_UTF8_VALUE.equalsIgnoreCase(header); + } +} \ No newline at end of file diff --git a/ruoyi/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java new file mode 100644 index 0000000..634f0ed --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java @@ -0,0 +1,160 @@ +package com.ruoyi.framework.aspectj; + +import java.lang.reflect.Method; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.Signature; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.framework.aspectj.lang.annotation.DataScope; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.framework.security.service.TokenService; +import com.ruoyi.framework.web.domain.BaseEntity; +import com.ruoyi.project.system.domain.SysRole; +import com.ruoyi.project.system.domain.SysUser; + +/** + * 数据过滤处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class DataScopeAspect +{ + /** + * 全部数据权限 + */ + public static final String DATA_SCOPE_ALL = "1"; + + /** + * 自定数据权限 + */ + public static final String DATA_SCOPE_CUSTOM = "2"; + + /** + * 部门数据权限 + */ + public static final String DATA_SCOPE_DEPT = "3"; + + /** + * 部门及以下数据权限 + */ + public static final String DATA_SCOPE_DEPT_AND_CHILD = "4"; + + /** + * 仅本人数据权限 + */ + public static final String DATA_SCOPE_SELF = "5"; + + // 配置织入点 + @Pointcut("@annotation(com.ruoyi.framework.aspectj.lang.annotation.DataScope)") + public void dataScopePointCut() + { + } + + @Before("dataScopePointCut()") + public void doBefore(JoinPoint point) throws Throwable + { + handleDataScope(point); + } + + protected void handleDataScope(final JoinPoint joinPoint) + { + // 获得注解 + DataScope controllerDataScope = getAnnotationLog(joinPoint); + if (controllerDataScope == null) + { + return; + } + // 获取当前的用户 + LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest()); + SysUser currentUser = loginUser.getUser(); + if (currentUser != null) + { + // 如果是超级管理员,则不过滤数据 + if (!currentUser.isAdmin()) + { + dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), + controllerDataScope.userAlias()); + } + } + } + + /** + * 数据范围过滤 + * + * @param joinPoint 切点 + * @param user 用户 + * @param alias 别名 + */ + public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias) + { + StringBuilder sqlString = new StringBuilder(); + + for (SysRole role : user.getRoles()) + { + String dataScope = role.getDataScope(); + if (DATA_SCOPE_ALL.equals(dataScope)) + { + sqlString = new StringBuilder(); + break; + } + else if (DATA_SCOPE_CUSTOM.equals(dataScope)) + { + sqlString.append(StringUtils.format( + " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, + role.getRoleId())); + } + else if (DATA_SCOPE_DEPT.equals(dataScope)) + { + sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId())); + } + else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) + { + sqlString.append(StringUtils.format( + " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", + deptAlias, user.getDeptId(), user.getDeptId())); + } + else if (DATA_SCOPE_SELF.equals(dataScope)) + { + if (StringUtils.isNotBlank(userAlias)) + { + sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId())); + } + else + { + // 数据权限为仅本人且没有userAlias别名不查询任何数据 + sqlString.append(" OR 1=0 "); + } + } + } + + if (StringUtils.isNotBlank(sqlString.toString())) + { + BaseEntity baseEntity = (BaseEntity) joinPoint.getArgs()[0]; + baseEntity.setDataScope(" AND (" + sqlString.substring(4) + ")"); + } + } + + /** + * 是否存在注解,如果存在就获取 + */ + private DataScope getAnnotationLog(JoinPoint joinPoint) + { + Signature signature = joinPoint.getSignature(); + MethodSignature methodSignature = (MethodSignature) signature; + Method method = methodSignature.getMethod(); + + if (method != null) + { + return method.getAnnotation(DataScope.class); + } + return null; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java new file mode 100644 index 0000000..5bdff86 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java @@ -0,0 +1,76 @@ +package com.ruoyi.framework.aspectj; + +import java.lang.reflect.Method; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.aspectj.lang.annotation.DataSource; +import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder; + +/** + * 多数据源处理 + * + * @author ruoyi + */ +@Aspect +@Order(1) +@Component +public class DataSourceAspect +{ + protected Logger logger = LoggerFactory.getLogger(getClass()); + + @Pointcut("@annotation(com.ruoyi.framework.aspectj.lang.annotation.DataSource)" + + "|| @within(com.ruoyi.framework.aspectj.lang.annotation.DataSource)") + public void dsPointCut() + { + + } + + @Around("dsPointCut()") + public Object around(ProceedingJoinPoint point) throws Throwable + { + DataSource dataSource = getDataSource(point); + + if (StringUtils.isNotNull(dataSource)) + { + DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); + } + + try + { + return point.proceed(); + } + finally + { + // 销毁数据源 在执行方法之后 + DynamicDataSourceContextHolder.clearDataSourceType(); + } + } + + /** + * 获取需要切换的数据源 + */ + public DataSource getDataSource(ProceedingJoinPoint point) + { + MethodSignature signature = (MethodSignature) point.getSignature(); + Class<? extends Object> targetClass = point.getTarget().getClass(); + DataSource targetDataSource = targetClass.getAnnotation(DataSource.class); + if (StringUtils.isNotNull(targetDataSource)) + { + return targetDataSource; + } + else + { + Method method = signature.getMethod(); + DataSource dataSource = method.getAnnotation(DataSource.class); + return dataSource; + } + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java new file mode 100644 index 0000000..8bb2074 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java @@ -0,0 +1,204 @@ +package com.ruoyi.framework.aspectj; + +import java.lang.reflect.Method; +import java.util.Map; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.Signature; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.HandlerMapping; +import com.alibaba.fastjson.JSON; +import com.ruoyi.common.enums.HttpMethod; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessStatus; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.framework.security.service.TokenService; +import com.ruoyi.project.monitor.domain.SysOperLog; + +/** + * 操作日志记录处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class LogAspect +{ + private static final Logger log = LoggerFactory.getLogger(LogAspect.class); + + // 配置织入点 + @Pointcut("@annotation(com.ruoyi.framework.aspectj.lang.annotation.Log)") + public void logPointCut() + { + } + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) + { + handleLog(joinPoint, null, jsonResult); + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "logPointCut()", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Exception e) + { + handleLog(joinPoint, e, null); + } + + protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) + { + try + { + // 获得注解 + Log controllerLog = getAnnotationLog(joinPoint); + if (controllerLog == null) + { + return; + } + + // 获取当前的用户 + LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest()); + + // *========数据库日志=========*// + SysOperLog operLog = new SysOperLog(); + operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); + // 请求的地址 + String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + operLog.setOperIp(ip); + // 返回参数 + operLog.setJsonResult(JSON.toJSONString(jsonResult)); + + operLog.setOperUrl(ServletUtils.getRequest().getRequestURI()); + if (loginUser != null) + { + operLog.setOperName(loginUser.getUsername()); + } + + if (e != null) + { + operLog.setStatus(BusinessStatus.FAIL.ordinal()); + operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000)); + } + // 设置方法名称 + String className = joinPoint.getTarget().getClass().getName(); + String methodName = joinPoint.getSignature().getName(); + operLog.setMethod(className + "." + methodName + "()"); + // 设置请求方式 + operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); + // 处理设置注解上的参数 + getControllerMethodDescription(joinPoint, controllerLog, operLog); + // 保存数据库 + AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); + } + catch (Exception exp) + { + // 记录本地异常日志 + log.error("==前置通知异常=="); + log.error("异常信息:{}", exp.getMessage()); + exp.printStackTrace(); + } + } + + /** + * 获取注解中对方法的描述信息 用于Controller层注解 + * + * @param log 日志 + * @param operLog 操作日志 + * @throws Exception + */ + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog) throws Exception + { + // 设置action动作 + operLog.setBusinessType(log.businessType().ordinal()); + // 设置标题 + operLog.setTitle(log.title()); + // 设置操作人类别 + operLog.setOperatorType(log.operatorType().ordinal()); + // 是否需要保存request,参数和值 + if (log.isSaveRequestData()) + { + // 获取参数的信息,传入到数据库中。 + setRequestValue(joinPoint, operLog); + } + } + + /** + * 获取请求的参数,放到log中 + * + * @param operLog 操作日志 + * @throws Exception 异常 + */ + private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception + { + String requestMethod = operLog.getRequestMethod(); + if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) + { + String params = argsArrayToString(joinPoint.getArgs()); + operLog.setOperParam(StringUtils.substring(params, 0, 2000)); + } + else + { + Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); + operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000)); + } + } + + /** + * 是否存在注解,如果存在就获取 + */ + private Log getAnnotationLog(JoinPoint joinPoint) throws Exception + { + Signature signature = joinPoint.getSignature(); + MethodSignature methodSignature = (MethodSignature) signature; + Method method = methodSignature.getMethod(); + + if (method != null) + { + return method.getAnnotation(Log.class); + } + return null; + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray) + { + String params = ""; + if (paramsArray != null && paramsArray.length > 0) + { + for (int i = 0; i < paramsArray.length; i++) + { + if (!(paramsArray[i] instanceof MultipartFile)) + { + Object jsonObj = JSON.toJSON(paramsArray[i]); + params += jsonObj.toString() + " "; + } + } + } + return params.trim(); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/DataScope.java b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/DataScope.java new file mode 100644 index 0000000..5e4cc9c --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/DataScope.java @@ -0,0 +1,28 @@ +package com.ruoyi.framework.aspectj.lang.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 数据权限过滤注解 + * + * @author ruoyi + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataScope +{ + /** + * 部门表的别名 + */ + public String deptAlias() default ""; + + /** + * 用户表的别名 + */ + public String userAlias() default ""; +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/DataSource.java b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/DataSource.java new file mode 100644 index 0000000..4df29ff --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/DataSource.java @@ -0,0 +1,26 @@ +package com.ruoyi.framework.aspectj.lang.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.ruoyi.framework.aspectj.lang.enums.DataSourceType; + +/** + * 自定义多数据源切换注解 + * + * @author ruoyi + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface DataSource +{ + /** + * 切换数据源名称 + */ + public DataSourceType value() default DataSourceType.MASTER; +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Log.java b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Log.java new file mode 100644 index 0000000..548eace --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Log.java @@ -0,0 +1,41 @@ +package com.ruoyi.framework.aspectj.lang.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.aspectj.lang.enums.OperatorType; + +/** + * 自定义操作日志记录注解 + * + * @author ruoyi + * + */ +@Target({ ElementType.PARAMETER, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Log +{ + /** + * 模块 + */ + public String title() default ""; + + /** + * 功能 + */ + public BusinessType businessType() default BusinessType.OTHER; + + /** + * 操作人类别 + */ + public OperatorType operatorType() default OperatorType.MANAGE; + + /** + * 是否保存请求的参数 + */ + public boolean isSaveRequestData() default true; +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/enums/BusinessStatus.java b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/enums/BusinessStatus.java new file mode 100644 index 0000000..7e27f2e --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/enums/BusinessStatus.java @@ -0,0 +1,20 @@ +package com.ruoyi.framework.aspectj.lang.enums; + +/** + * 操作状态 + * + * @author ruoyi + * + */ +public enum BusinessStatus +{ + /** + * 成功 + */ + SUCCESS, + + /** + * 失败 + */ + FAIL, +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/enums/BusinessType.java b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/enums/BusinessType.java new file mode 100644 index 0000000..d974a64 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/enums/BusinessType.java @@ -0,0 +1,59 @@ +package com.ruoyi.framework.aspectj.lang.enums; + +/** + * 业务操作类型 + * + * @author ruoyi + */ +public enum BusinessType +{ + /** + * 其它 + */ + OTHER, + + /** + * 新增 + */ + INSERT, + + /** + * 修改 + */ + UPDATE, + + /** + * 删除 + */ + DELETE, + + /** + * 授权 + */ + GRANT, + + /** + * 导出 + */ + EXPORT, + + /** + * 导入 + */ + IMPORT, + + /** + * 强退 + */ + FORCE, + + /** + * 生成代码 + */ + GENCODE, + + /** + * 清空数据 + */ + CLEAN, +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/enums/DataSourceType.java b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/enums/DataSourceType.java new file mode 100644 index 0000000..274dd34 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/enums/DataSourceType.java @@ -0,0 +1,19 @@ +package com.ruoyi.framework.aspectj.lang.enums; + +/** + * 数据源 + * + * @author ruoyi + */ +public enum DataSourceType +{ + /** + * 主库 + */ + MASTER, + + /** + * 从库 + */ + SLAVE +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/enums/OperatorType.java b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/enums/OperatorType.java new file mode 100644 index 0000000..98685a6 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/enums/OperatorType.java @@ -0,0 +1,24 @@ +package com.ruoyi.framework.aspectj.lang.enums; + +/** + * 操作人类别 + * + * @author ruoyi + */ +public enum OperatorType +{ + /** + * 其它 + */ + OTHER, + + /** + * 后台用户 + */ + MANAGE, + + /** + * 手机端用户 + */ + MOBILE +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java new file mode 100644 index 0000000..4dd3338 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java @@ -0,0 +1,20 @@ +package com.ruoyi.framework.config; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +/** + * 程序注解配置 + * + * @author ruoyi + */ +@Configuration +// 表示通过aop框架暴露该代理对象,AopContext能够访问 +@EnableAspectJAutoProxy(exposeProxy = true) +// 指定要扫描的Mapper类的包的路径 +@MapperScan("com.ruoyi.project.**.mapper") +public class ApplicationConfig +{ + +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/DruidConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/DruidConfig.java new file mode 100644 index 0000000..26135c8 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/config/DruidConfig.java @@ -0,0 +1,106 @@ +package com.ruoyi.framework.config; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.sql.DataSource; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; +import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; +import com.alibaba.druid.util.Utils; +import com.ruoyi.framework.aspectj.lang.enums.DataSourceType; +import com.ruoyi.framework.config.properties.DruidProperties; +import com.ruoyi.framework.datasource.DynamicDataSource; + +/** + * druid 配置多数据源 + * + * @author ruoyi + */ +@Configuration +public class DruidConfig +{ + @Bean + @ConfigurationProperties("spring.datasource.druid.master") + public DataSource masterDataSource(DruidProperties druidProperties) + { + DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); + return druidProperties.dataSource(dataSource); + } + + @Bean + @ConfigurationProperties("spring.datasource.druid.slave") + @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true") + public DataSource slaveDataSource(DruidProperties druidProperties) + { + DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); + return druidProperties.dataSource(dataSource); + } + + @Bean(name = "dynamicDataSource") + @Primary + public DynamicDataSource dataSource(DataSource masterDataSource, DataSource slaveDataSource) + { + Map<Object, Object> targetDataSources = new HashMap<>(); + targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource); + targetDataSources.put(DataSourceType.SLAVE.name(), slaveDataSource); + return new DynamicDataSource(masterDataSource, targetDataSources); + } + + /** + * 去除监控页面底部的广告 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true") + public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) + { + // 获取web监控页面的参数 + DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); + // 提取common.js的配置路径 + String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; + String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); + final String filePath = "support/http/resources/js/common.js"; + // 创建filter进行过滤 + Filter filter = new Filter() + { + @Override + public void init(javax.servlet.FilterConfig filterConfig) throws ServletException + { + } + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + chain.doFilter(request, response); + // 重置缓冲区,响应头不会被重置 + response.resetBuffer(); + // 获取common.js + String text = Utils.readFromResource(filePath); + // 正则替换banner, 除去底部的广告信息 + text = text.replaceAll("<a.*?banner\"></a><br/>", ""); + text = text.replaceAll("powered.*?shrek.wang</a>", ""); + response.getWriter().write(text); + } + @Override + public void destroy() + { + } + }; + FilterRegistrationBean registrationBean = new FilterRegistrationBean(); + registrationBean.setFilter(filter); + registrationBean.addUrlPatterns(commonJsPattern); + return registrationBean; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java b/ruoyi/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java new file mode 100644 index 0000000..b623cae --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java @@ -0,0 +1,69 @@ +package com.ruoyi.framework.config; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; +import com.alibaba.fastjson.parser.ParserConfig; +import org.springframework.util.Assert; +import java.nio.charset.Charset; + +/** + * Redis使用FastJson序列化 + * + * @author ruoyi + */ +public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> +{ + @SuppressWarnings("unused") + private ObjectMapper objectMapper = new ObjectMapper(); + + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + private Class<T> clazz; + + static + { + ParserConfig.getGlobalInstance().setAutoTypeSupport(true); + } + + public FastJson2JsonRedisSerializer(Class<T> clazz) + { + super(); + this.clazz = clazz; + } + + public byte[] serialize(T t) throws SerializationException + { + if (t == null) + { + return new byte[0]; + } + return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + public T deserialize(byte[] bytes) throws SerializationException + { + if (bytes == null || bytes.length <= 0) + { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + + return JSON.parseObject(str, clazz); + } + + public void setObjectMapper(ObjectMapper objectMapper) + { + Assert.notNull(objectMapper, "'objectMapper' must not be null"); + this.objectMapper = objectMapper; + } + + protected JavaType getJavaType(Class<?> clazz) + { + return TypeFactory.defaultInstance().constructType(clazz); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/FilterConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/FilterConfig.java new file mode 100644 index 0000000..92d010e --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/config/FilterConfig.java @@ -0,0 +1,46 @@ +package com.ruoyi.framework.config; + +import java.util.HashMap; +import java.util.Map; +import javax.servlet.DispatcherType; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.xss.XssFilter; + +/** + * Filter配置 + * + * @author ruoyi + */ +@Configuration +public class FilterConfig +{ + @Value("${xss.enabled}") + private String enabled; + + @Value("${xss.excludes}") + private String excludes; + + @Value("${xss.urlPatterns}") + private String urlPatterns; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + public FilterRegistrationBean xssFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter()); + registration.addUrlPatterns(StringUtils.split(urlPatterns, ",")); + registration.setName("xssFilter"); + registration.setOrder(Integer.MAX_VALUE); + Map<String, String> initParameters = new HashMap<String, String>(); + initParameters.put("excludes", excludes); + initParameters.put("enabled", enabled); + registration.setInitParameters(initParameters); + return registration; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java new file mode 100644 index 0000000..69868f0 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java @@ -0,0 +1,105 @@ +package com.ruoyi.framework.config; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import javax.sql.DataSource; +import org.apache.ibatis.io.VFS; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.boot.autoconfigure.SpringBootVFS; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.core.type.classreading.CachingMetadataReaderFactory; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.util.ClassUtils; + +/** + * Mybatis支持*匹配扫描包 + * + * @author ruoyi + */ +@Configuration +public class MyBatisConfig +{ + @Autowired + private Environment env; + + static final String DEFAULT_RESOURCE_PATTERN = "**/*.class"; + + public static String setTypeAliasesPackage(String typeAliasesPackage) + { + ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver(); + MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver); + List<String> allResult = new ArrayList<String>(); + try + { + for (String aliasesPackage : typeAliasesPackage.split(",")) + { + List<String> result = new ArrayList<String>(); + aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN; + Resource[] resources = resolver.getResources(aliasesPackage); + if (resources != null && resources.length > 0) + { + MetadataReader metadataReader = null; + for (Resource resource : resources) + { + if (resource.isReadable()) + { + metadataReader = metadataReaderFactory.getMetadataReader(resource); + try + { + result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName()); + } + catch (ClassNotFoundException e) + { + e.printStackTrace(); + } + } + } + } + if (result.size() > 0) + { + HashSet<String> hashResult = new HashSet<String>(result); + allResult.addAll(hashResult); + } + } + if (allResult.size() > 0) + { + typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0])); + } + else + { + throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包"); + } + } + catch (IOException e) + { + e.printStackTrace(); + } + return typeAliasesPackage; + } + + @Bean + public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception + { + String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage"); + String mapperLocations = env.getProperty("mybatis.mapperLocations"); + typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage); + VFS.addImplClass(SpringBootVFS.class); + + final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); + sessionFactory.setDataSource(dataSource); + sessionFactory.setTypeAliasesPackage(typeAliasesPackage); + sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations)); + return sessionFactory.getObject(); + } +} \ No newline at end of file diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/RedisConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/RedisConfig.java new file mode 100644 index 0000000..6322686 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/config/RedisConfig.java @@ -0,0 +1,43 @@ +package com.ruoyi.framework.config; + +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * redis配置 + * + * @author ruoyi + */ +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport +{ + @Bean + @SuppressWarnings(value = { "unchecked", "rawtypes" }) + public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) + { + RedisTemplate<Object, Object> template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + + ObjectMapper mapper = new ObjectMapper(); + mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); + serializer.setObjectMapper(mapper); + + template.setValueSerializer(serializer); + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.afterPropertiesSet(); + return template; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java new file mode 100644 index 0000000..b836c21 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java @@ -0,0 +1,26 @@ +package com.ruoyi.framework.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import com.ruoyi.common.constant.Constants; + +/** + * 通用配置 + * + * @author ruoyi + */ +@Configuration +public class ResourcesConfig implements WebMvcConfigurer +{ + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) + { + /** 本地文件上传路径 */ + registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**").addResourceLocations("file:" + RuoYiConfig.getProfile() + "/"); + + /** swagger配置 */ + registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/"); + registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); + } +} \ No newline at end of file diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/RuoYiConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/RuoYiConfig.java new file mode 100644 index 0000000..1f6e20a --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/config/RuoYiConfig.java @@ -0,0 +1,116 @@ +package com.ruoyi.framework.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 读取项目相关配置 + * + * @author ruoyi + */ +@Component +@ConfigurationProperties(prefix = "ruoyi") +public class RuoYiConfig +{ + /** 项目名称 */ + private String name; + + /** 版本 */ + private String version; + + /** 版权年份 */ + private String copyrightYear; + + /** 实例演示开关 */ + private boolean demoEnabled; + + /** 上传路径 */ + private static String profile; + + /** 获取地址开关 */ + private static boolean addressEnabled; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getVersion() + { + return version; + } + + public void setVersion(String version) + { + this.version = version; + } + + public String getCopyrightYear() + { + return copyrightYear; + } + + public void setCopyrightYear(String copyrightYear) + { + this.copyrightYear = copyrightYear; + } + + public boolean isDemoEnabled() + { + return demoEnabled; + } + + public void setDemoEnabled(boolean demoEnabled) + { + this.demoEnabled = demoEnabled; + } + + public static String getProfile() + { + return profile; + } + + public void setProfile(String profile) + { + RuoYiConfig.profile = profile; + } + + public static boolean isAddressEnabled() + { + return addressEnabled; + } + + public void setAddressEnabled(boolean addressEnabled) + { + RuoYiConfig.addressEnabled = addressEnabled; + } + + /** + * 获取头像上传路径 + */ + public static String getAvatarPath() + { + return getProfile() + "/avatar"; + } + + /** + * 获取下载路径 + */ + public static String getDownloadPath() + { + return getProfile() + "/download/"; + } + + /** + * 获取上传路径 + */ + public static String getUploadPath() + { + return getProfile() + "/upload"; + } +} \ No newline at end of file diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/SecurityConfig.java new file mode 100644 index 0000000..45000b5 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/config/SecurityConfig.java @@ -0,0 +1,133 @@ +package com.ruoyi.framework.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter; +import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl; +import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl; + +/** + * spring security配置 + * + * @author ruoyi + */ +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) +public class SecurityConfig extends WebSecurityConfigurerAdapter +{ + /** + * 自定义用户认证逻辑 + */ + @Autowired + private UserDetailsService userDetailsService; + + /** + * 认证失败处理类 + */ + @Autowired + private AuthenticationEntryPointImpl unauthorizedHandler; + + /** + * 退出处理类 + */ + @Autowired + private LogoutSuccessHandlerImpl logoutSuccessHandler; + + /** + * token认证过滤器 + */ + @Autowired + private JwtAuthenticationTokenFilter authenticationTokenFilter; + + /** + * 解决 无法直接注入 AuthenticationManager + * + * @return + * @throws Exception + */ + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception + { + return super.authenticationManagerBean(); + } + + /** + * anyRequest | 匹配所有请求路径 + * access | SpringEl表达式结果为true时可以访问 + * anonymous | 匿名可以访问 + * denyAll | 用户不能访问 + * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) + * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 + * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 + * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 + * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 + * hasRole | 如果有参数,参数表示角色,则其角色可以访问 + * permitAll | 用户可以任意访问 + * rememberMe | 允许通过remember-me登录的用户访问 + * authenticated | 用户登录后可访问 + */ + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception + { + httpSecurity + // CRSF禁用,因为不使用session + .csrf().disable() + // 认证失败处理类 + .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() + // 基于token,所以不需要session + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() + // 过滤请求 + .authorizeRequests() + // 对于登录login 验证码captchaImage 允许匿名访问 + .antMatchers("/login", "/captchaImage").anonymous() + .antMatchers( + HttpMethod.GET, + "/*.html", + "/**/*.html", + "/**/*.css", + "/**/*.js" + ).permitAll() + .antMatchers("/profile/**").anonymous() + .antMatchers("/swagger-ui.html").anonymous() + .antMatchers("/swagger-resources/**").anonymous() + .antMatchers("/webjars/**").anonymous() + .antMatchers("/*/api-docs").anonymous() + .antMatchers("/druid/**").anonymous() + // 除上面外的所有请求全部需要鉴权认证 + .anyRequest().authenticated() + .and() + .headers().frameOptions().disable(); + httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); + // 添加JWT filter + httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); + } + + + /** + * 强散列哈希加密实现 + */ + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder() + { + return new BCryptPasswordEncoder(); + } + + /** + * 身份认证接口 + */ + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception + { + auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/ServerConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/ServerConfig.java new file mode 100644 index 0000000..157e9f4 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/config/ServerConfig.java @@ -0,0 +1,32 @@ +package com.ruoyi.framework.config; + +import javax.servlet.http.HttpServletRequest; +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.ServletUtils; + +/** + * 服务相关配置 + * + * @author ruoyi + */ +@Component +public class ServerConfig +{ + /** + * 获取完整的请求路径,包括:域名,端口,上下文访问路径 + * + * @return 服务地址 + */ + public String getUrl() + { + HttpServletRequest request = ServletUtils.getRequest(); + return getDomain(request); + } + + public static String getDomain(HttpServletRequest request) + { + StringBuffer url = request.getRequestURL(); + String contextPath = request.getServletContext().getContextPath(); + return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString(); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java new file mode 100644 index 0000000..a2da4e8 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java @@ -0,0 +1,66 @@ +package com.ruoyi.framework.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import io.swagger.annotations.ApiOperation; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * Swagger2的接口配置 + * + * @author ruoyi + */ +@Configuration +@EnableSwagger2 +public class SwaggerConfig +{ + /** 系统基础配置 */ + @Autowired + private RuoYiConfig ruoyiConfig; + + /** + * 创建API + */ + @Bean + public Docket createRestApi() + { + return new Docket(DocumentationType.SWAGGER_2) + // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息) + .apiInfo(apiInfo()) + // 设置哪些接口暴露给Swagger展示 + .select() + // 扫描所有有注解的api,用这种方式更灵活 + .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + // 扫描指定包中的swagger注解 + //.apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger")) + // 扫描所有 .apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.any()) + .build(); + } + + /** + * 添加摘要信息 + */ + private ApiInfo apiInfo() + { + // 用ApiInfoBuilder进行定制 + return new ApiInfoBuilder() + // 设置标题 + .title("标题:余心管理系统_接口文档") + // 描述 + .description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...") + // 作者信息 + .contact(new Contact(ruoyiConfig.getName(), null, null)) + // 版本 + .version("版本号:" + ruoyiConfig.getVersion()) + .build(); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java new file mode 100644 index 0000000..618a925 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java @@ -0,0 +1,62 @@ +package com.ruoyi.framework.config; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import com.ruoyi.common.utils.Threads; + +/** + * 线程池配置 + * + * @author ruoyi + **/ +@Configuration +public class ThreadPoolConfig +{ + // 核心线程池大小 + private int corePoolSize = 50; + + // 最大可创建的线程数 + private int maxPoolSize = 200; + + // 队列最大长度 + private int queueCapacity = 1000; + + // 线程池维护线程所允许的空闲时间 + private int keepAliveSeconds = 300; + + @Bean(name = "threadPoolTaskExecutor") + public ThreadPoolTaskExecutor threadPoolTaskExecutor() + { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setMaxPoolSize(maxPoolSize); + executor.setCorePoolSize(corePoolSize); + executor.setQueueCapacity(queueCapacity); + executor.setKeepAliveSeconds(keepAliveSeconds); + // 线程池对拒绝任务(无线程可用)的处理策略 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + return executor; + } + + /** + * 执行周期性或定时任务 + */ + @Bean(name = "scheduledExecutorService") + protected ScheduledExecutorService scheduledExecutorService() + { + return new ScheduledThreadPoolExecutor(corePoolSize, + new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build()) + { + @Override + protected void afterExecute(Runnable r, Throwable t) + { + super.afterExecute(r, t); + Threads.printException(r, t); + } + }; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java b/ruoyi/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java new file mode 100644 index 0000000..ae6e02f --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java @@ -0,0 +1,77 @@ +package com.ruoyi.framework.config.properties; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import com.alibaba.druid.pool.DruidDataSource; + +/** + * druid 配置属性 + * + * @author ruoyi + */ +@Configuration +public class DruidProperties +{ + @Value("${spring.datasource.druid.initialSize}") + private int initialSize; + + @Value("${spring.datasource.druid.minIdle}") + private int minIdle; + + @Value("${spring.datasource.druid.maxActive}") + private int maxActive; + + @Value("${spring.datasource.druid.maxWait}") + private int maxWait; + + @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}") + private int timeBetweenEvictionRunsMillis; + + @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}") + private int minEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}") + private int maxEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.validationQuery}") + private String validationQuery; + + @Value("${spring.datasource.druid.testWhileIdle}") + private boolean testWhileIdle; + + @Value("${spring.datasource.druid.testOnBorrow}") + private boolean testOnBorrow; + + @Value("${spring.datasource.druid.testOnReturn}") + private boolean testOnReturn; + + public DruidDataSource dataSource(DruidDataSource datasource) + { + /** 配置初始化大小、最小、最大 */ + datasource.setInitialSize(initialSize); + datasource.setMaxActive(maxActive); + datasource.setMinIdle(minIdle); + + /** 配置获取连接等待超时的时间 */ + datasource.setMaxWait(maxWait); + + /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */ + datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); + + /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */ + datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); + datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis); + + /** + * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 + */ + datasource.setValidationQuery(validationQuery); + /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */ + datasource.setTestWhileIdle(testWhileIdle); + /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnBorrow(testOnBorrow); + /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnReturn(testOnReturn); + return datasource; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java b/ruoyi/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java new file mode 100644 index 0000000..014ae38 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java @@ -0,0 +1,26 @@ +package com.ruoyi.framework.datasource; + +import java.util.Map; +import javax.sql.DataSource; +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +/** + * 动态数据源 + * + * @author ruoyi + */ +public class DynamicDataSource extends AbstractRoutingDataSource +{ + public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) + { + super.setDefaultTargetDataSource(defaultTargetDataSource); + super.setTargetDataSources(targetDataSources); + super.afterPropertiesSet(); + } + + @Override + protected Object determineCurrentLookupKey() + { + return DynamicDataSourceContextHolder.getDataSourceType(); + } +} \ No newline at end of file diff --git a/ruoyi/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java b/ruoyi/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java new file mode 100644 index 0000000..20a0dda --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java @@ -0,0 +1,45 @@ +package com.ruoyi.framework.datasource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 数据源切换处理 + * + * @author ruoyi + */ +public class DynamicDataSourceContextHolder +{ + public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); + + /** + * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本, + * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 + */ + private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); + + /** + * 设置数据源的变量 + */ + public static void setDataSourceType(String dsType) + { + log.info("切换到{}数据源", dsType); + CONTEXT_HOLDER.set(dsType); + } + + /** + * 获得数据源的变量 + */ + public static String getDataSourceType() + { + return CONTEXT_HOLDER.get(); + } + + /** + * 清空数据源变量 + */ + public static void clearDataSourceType() + { + CONTEXT_HOLDER.remove(); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/manager/AsyncManager.java b/ruoyi/src/main/java/com/ruoyi/framework/manager/AsyncManager.java new file mode 100644 index 0000000..6ed6d5b --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/manager/AsyncManager.java @@ -0,0 +1,55 @@ +package com.ruoyi.framework.manager; + +import java.util.TimerTask; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import com.ruoyi.common.utils.Threads; +import com.ruoyi.common.utils.spring.SpringUtils; + +/** + * 异步任务管理器 + * + * @author ruoyi + */ +public class AsyncManager +{ + /** + * 操作延迟10毫秒 + */ + private final int OPERATE_DELAY_TIME = 10; + + /** + * 异步操作任务调度线程池 + */ + private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService"); + + /** + * 单例模式 + */ + private AsyncManager(){} + + private static AsyncManager me = new AsyncManager(); + + public static AsyncManager me() + { + return me; + } + + /** + * 执行任务 + * + * @param task 任务 + */ + public void execute(TimerTask task) + { + executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); + } + + /** + * 停止任务线程池 + */ + public void shutdown() + { + Threads.shutdownAndAwaitTermination(executor); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java b/ruoyi/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java new file mode 100644 index 0000000..8d8fb5a --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java @@ -0,0 +1,39 @@ +package com.ruoyi.framework.manager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import javax.annotation.PreDestroy; + +/** + * 确保应用退出时能关闭后台线程 + * + * @author ruoyi + */ +@Component +public class ShutdownManager +{ + private static final Logger logger = LoggerFactory.getLogger("sys-user"); + + @PreDestroy + public void destroy() + { + shutdownAsyncManager(); + } + + /** + * 停止异步执行任务 + */ + private void shutdownAsyncManager() + { + try + { + logger.info("====关闭后台任务任务线程池===="); + AsyncManager.me().shutdown(); + } + catch (Exception e) + { + logger.error(e.getMessage(), e); + } + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java b/ruoyi/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java new file mode 100644 index 0000000..0be7a30 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java @@ -0,0 +1,101 @@ +package com.ruoyi.framework.manager.factory; + +import java.util.TimerTask; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.LogUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.ip.AddressUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.project.monitor.domain.SysLogininfor; +import com.ruoyi.project.monitor.domain.SysOperLog; +import com.ruoyi.project.monitor.service.ISysLogininforService; +import com.ruoyi.project.monitor.service.ISysOperLogService; +import eu.bitwalker.useragentutils.UserAgent; + +/** + * 异步工厂(产生任务用) + * + * @author ruoyi + */ +public class AsyncFactory +{ + private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user"); + + /** + * 记录登陆信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息 + * @param args 列表 + * @return 任务task + */ + public static TimerTask recordLogininfor(final String username, final String status, final String message, + final Object... args) + { + final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + final String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + return new TimerTask() + { + @Override + public void run() + { + String address = AddressUtils.getRealAddressByIP(ip); + StringBuilder s = new StringBuilder(); + s.append(LogUtils.getBlock(ip)); + s.append(address); + s.append(LogUtils.getBlock(username)); + s.append(LogUtils.getBlock(status)); + s.append(LogUtils.getBlock(message)); + // 打印信息到日志 + sys_user_logger.info(s.toString(), args); + // 获取客户端操作系统 + String os = userAgent.getOperatingSystem().getName(); + // 获取客户端浏览器 + String browser = userAgent.getBrowser().getName(); + // 封装对象 + SysLogininfor logininfor = new SysLogininfor(); + logininfor.setUserName(username); + logininfor.setIpaddr(ip); + logininfor.setLoginLocation(address); + logininfor.setBrowser(browser); + logininfor.setOs(os); + logininfor.setMsg(message); + // 日志状态 + if (Constants.LOGIN_SUCCESS.equals(status) || Constants.LOGOUT.equals(status)) + { + logininfor.setStatus(Constants.SUCCESS); + } + else if (Constants.LOGIN_FAIL.equals(status)) + { + logininfor.setStatus(Constants.FAIL); + } + // 插入数据 + SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor); + } + }; + } + + /** + * 操作日志记录 + * + * @param operLog 操作日志信息 + * @return 任务task + */ + public static TimerTask recordOper(final SysOperLog operLog) + { + return new TimerTask() + { + @Override + public void run() + { + // 远程查询操作地点 + operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); + SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog); + } + }; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/redis/RedisCache.java b/ruoyi/src/main/java/com/ruoyi/framework/redis/RedisCache.java new file mode 100644 index 0000000..a290494 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/redis/RedisCache.java @@ -0,0 +1,200 @@ +package com.ruoyi.framework.redis; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.ListOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; + +/** + * spring redis 工具类 + * + * @author ruoyi + **/ +@SuppressWarnings(value = { "unchecked", "rawtypes" }) +@Component +public class RedisCache +{ + @Autowired + public RedisTemplate redisTemplate; + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @return 缓存的对象 + */ + public <T> ValueOperations<String, T> setCacheObject(String key, T value) + { + ValueOperations<String, T> operation = redisTemplate.opsForValue(); + operation.set(key, value); + return operation; + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + * @return 缓存的对象 + */ + public <T> ValueOperations<String, T> setCacheObject(String key, T value, Integer timeout, TimeUnit timeUnit) + { + ValueOperations<String, T> operation = redisTemplate.opsForValue(); + operation.set(key, value, timeout, timeUnit); + return operation; + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public <T> T getCacheObject(String key) + { + ValueOperations<String, T> operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + /** + * 删除单个对象 + * + * @param key + */ + public void deleteObject(String key) + { + redisTemplate.delete(key); + } + + /** + * 删除集合对象 + * + * @param collection + */ + public void deleteObject(Collection collection) + { + redisTemplate.delete(collection); + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + */ + public <T> ListOperations<String, T> setCacheList(String key, List<T> dataList) + { + ListOperations listOperation = redisTemplate.opsForList(); + if (null != dataList) + { + int size = dataList.size(); + for (int i = 0; i < size; i++) + { + listOperation.leftPush(key, dataList.get(i)); + } + } + return listOperation; + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public <T> List<T> getCacheList(String key) + { + List<T> dataList = new ArrayList<T>(); + ListOperations<String, T> listOperation = redisTemplate.opsForList(); + Long size = listOperation.size(key); + + for (int i = 0; i < size; i++) + { + dataList.add(listOperation.index(key, i)); + } + return dataList; + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + */ + public <T> BoundSetOperations<String, T> setCacheSet(String key, Set<T> dataSet) + { + BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key); + Iterator<T> it = dataSet.iterator(); + while (it.hasNext()) + { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * 获得缓存的set + * + * @param key + * @return + */ + public <T> Set<T> getCacheSet(String key) + { + Set<T> dataSet = new HashSet<T>(); + BoundSetOperations<String, T> operation = redisTemplate.boundSetOps(key); + Long size = operation.size(); + for (int i = 0; i < size; i++) + { + dataSet.add(operation.pop()); + } + return dataSet; + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + * @return + */ + public <T> HashOperations<String, String, T> setCacheMap(String key, Map<String, T> dataMap) + { + HashOperations hashOperations = redisTemplate.opsForHash(); + if (null != dataMap) + { + for (Map.Entry<String, T> entry : dataMap.entrySet()) + { + hashOperations.put(key, entry.getKey(), entry.getValue()); + } + } + return hashOperations; + } + + /** + * 获得缓存的Map + * + * @param key + * @return + */ + public <T> Map<String, T> getCacheMap(String key) + { + Map<String, T> map = redisTemplate.opsForHash().entries(key); + return map; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/security/LoginUser.java b/ruoyi/src/main/java/com/ruoyi/framework/security/LoginUser.java new file mode 100644 index 0000000..46afd22 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/security/LoginUser.java @@ -0,0 +1,168 @@ +package com.ruoyi.framework.security; + +import java.util.Collection; +import java.util.Set; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.ruoyi.project.system.domain.SysUser; + +/** + * 登录用户身份权限 + * + * @author ruoyi + */ +public class LoginUser implements UserDetails +{ + private static final long serialVersionUID = 1L; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 登陆时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 权限列表 + */ + private Set<String> permissions; + + /** + * 用户信息 + */ + private SysUser user; + + public String getToken() + { + return token; + } + + public void setToken(String token) + { + this.token = token; + } + + public LoginUser() + { + } + + public LoginUser(SysUser user, Set<String> permissions) + { + this.user = user; + this.permissions = permissions; + } + + @JsonIgnore + @Override + public String getPassword() + { + return user.getPassword(); + } + + @Override + public String getUsername() + { + return user.getUserName(); + } + + /** + * 账户是否未过期,过期无法验证 + */ + @JsonIgnore + @Override + public boolean isAccountNonExpired() + { + return true; + } + + /** + * 指定用户是否解锁,锁定的用户无法进行身份验证 + * + * @return + */ + @JsonIgnore + @Override + public boolean isAccountNonLocked() + { + return true; + } + + /** + * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 + * + * @return + */ + @JsonIgnore + @Override + public boolean isCredentialsNonExpired() + { + return true; + } + + /** + * 是否可用 ,禁用的用户不能身份验证 + * + * @return + */ + @JsonIgnore + @Override + public boolean isEnabled() + { + return true; + } + + public Long getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Long loginTime) + { + this.loginTime = loginTime; + } + + public Long getExpireTime() + { + return expireTime; + } + + public void setExpireTime(Long expireTime) + { + this.expireTime = expireTime; + } + + public Set<String> getPermissions() + { + return permissions; + } + + public void setPermissions(Set<String> permissions) + { + this.permissions = permissions; + } + + public SysUser getUser() + { + return user; + } + + public void setUser(SysUser user) + { + this.user = user; + } + + @Override + public Collection<? extends GrantedAuthority> getAuthorities() + { + return null; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java b/ruoyi/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java new file mode 100644 index 0000000..ede50be --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java @@ -0,0 +1,44 @@ +package com.ruoyi.framework.security.filter; + +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.framework.security.service.TokenService; + +/** + * token过滤器 验证token有效性 + * + * @author ruoyi + */ +@Component +public class JwtAuthenticationTokenFilter extends OncePerRequestFilter +{ + @Autowired + private TokenService tokenService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException + { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) + { + tokenService.verifyToken(loginUser); + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); + authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + } + chain.doFilter(request, response); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java b/ruoyi/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java new file mode 100644 index 0000000..995b1f3 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java @@ -0,0 +1,34 @@ +package com.ruoyi.framework.security.handle; + +import java.io.IOException; +import java.io.Serializable; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; +import com.alibaba.fastjson.JSON; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.domain.AjaxResult; + +/** + * 认证失败处理类 返回未授权 + * + * @author ruoyi + */ +@Component +public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable +{ + private static final long serialVersionUID = -8970718410437077606L; + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) + throws IOException + { + int code = HttpStatus.UNAUTHORIZED; + String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI()); + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg))); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java b/ruoyi/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java new file mode 100644 index 0000000..e8883a5 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java @@ -0,0 +1,51 @@ +package com.ruoyi.framework.security.handle; + +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import com.alibaba.fastjson.JSON; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.framework.security.service.TokenService; +import com.ruoyi.framework.web.domain.AjaxResult; + +/** + * 自定义退出处理类 返回成功 + * + * @author ruoyi + */ +@Configuration +public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler +{ + @Autowired + private TokenService tokenService; + + /** + * 退出处理 + * + * @return + */ + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException + { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser)) + { + String userName = loginUser.getUsername(); + // 记录用户退出日志 + AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功")); + } + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(HttpStatus.SUCCESS, "退出成功"))); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/security/service/PermissionService.java b/ruoyi/src/main/java/com/ruoyi/framework/security/service/PermissionService.java new file mode 100644 index 0000000..d467282 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/security/service/PermissionService.java @@ -0,0 +1,169 @@ +package com.ruoyi.framework.security.service; + +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.project.system.domain.SysRole; + +/** + * RuoYi首创 自定义权限实现,se取自SpringSecurity首字母 + * + * @author ruoyi + */ +@Service("ss") +public class PermissionService +{ + /** 所有权限标识 */ + private static final String ALL_PERMISSION = "*:*:*"; + + /** 管理员角色权限标识 */ + private static final String SUPER_ADMIN = "admin"; + + private static final String ROLE_DELIMETER = ","; + + private static final String PERMISSION_DELIMETER = ","; + + @Autowired + private TokenService tokenService; + + /** + * 验证用户是否具备某权限 + * + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public boolean hasPermi(String permission) + { + if (StringUtils.isEmpty(permission)) + { + return false; + } + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) + { + return false; + } + return hasPermissions(loginUser.getPermissions(), permission); + } + + /** + * 验证用户是否不具备某权限,与 hasPermi逻辑相反 + * + * @param permission 权限字符串 + * @return 用户是否不具备某权限 + */ + public boolean lacksPermi(String permission) + { + return hasPermi(permission) != true; + } + + /** + * 验证用户是否具有以下任意一个权限 + * + * @param permissions 以 PERMISSION_NAMES_DELIMETER 为分隔符的权限列表 + * @return 用户是否具有以下任意一个权限 + */ + public boolean hasAnyPermi(String permissions) + { + if (StringUtils.isEmpty(permissions)) + { + return false; + } + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) + { + return false; + } + Set<String> authorities = loginUser.getPermissions(); + for (String permission : permissions.split(PERMISSION_DELIMETER)) + { + if (permission != null && hasPermissions(authorities, permission)) + { + return true; + } + } + return false; + } + + /** + * 判断用户是否拥有某个角色 + * + * @param role 角色字符串 + * @return 用户是否具备某角色 + */ + public boolean hasRole(String role) + { + if (StringUtils.isEmpty(role)) + { + return false; + } + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) + { + return false; + } + for (SysRole sysRole : loginUser.getUser().getRoles()) + { + String roleKey = sysRole.getRoleKey(); + if (SUPER_ADMIN.contains(roleKey) || roleKey.contains(StringUtils.trim(role))) + { + return true; + } + } + return false; + } + + /** + * 验证用户是否不具备某角色,与 isRole逻辑相反。 + * + * @param role 角色名称 + * @return 用户是否不具备某角色 + */ + public boolean lacksRole(String role) + { + return hasRole(role) != true; + } + + /** + * 验证用户是否具有以下任意一个角色 + * + * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表 + * @return 用户是否具有以下任意一个角色 + */ + public boolean hasAnyRoles(String roles) + { + if (StringUtils.isEmpty(roles)) + { + return false; + } + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) + { + return false; + } + for (String role : roles.split(ROLE_DELIMETER)) + { + if (hasRole(role)) + { + return true; + } + } + return false; + } + + /** + * 判断是否包含权限 + * + * @param permissions 权限列表 + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + private boolean hasPermissions(Set<String> permissions, String permission) + { + return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission)); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/security/service/SysLoginService.java b/ruoyi/src/main/java/com/ruoyi/framework/security/service/SysLoginService.java new file mode 100644 index 0000000..a5d97a2 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/security/service/SysLoginService.java @@ -0,0 +1,88 @@ +package com.ruoyi.framework.security.service; + +import javax.annotation.Resource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.exception.CustomException; +import com.ruoyi.common.exception.user.CaptchaException; +import com.ruoyi.common.exception.user.CaptchaExpireException; +import com.ruoyi.common.exception.user.UserPasswordNotMatchException; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.redis.RedisCache; +import com.ruoyi.framework.security.LoginUser; + +/** + * 登录校验方法 + * + * @author ruoyi + */ +@Component +public class SysLoginService +{ + @Autowired + private TokenService tokenService; + + @Resource + private AuthenticationManager authenticationManager; + + @Autowired + private RedisCache redisCache; + + /** + * 登录验证 + * + * @param username 用户名 + * @param password 密码 + * @param captcha 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public String login(String username, String password, String code, String uuid) + { + String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; + String captcha = redisCache.getCacheObject(verifyKey); + redisCache.deleteObject(verifyKey); + if (captcha == null) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"))); + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"))); + throw new CaptchaException(); + } + // 用户验证 + Authentication authentication = null; + try + { + // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername + authentication = authenticationManager + .authenticate(new UsernamePasswordAuthenticationToken(username, password)); + } + catch (Exception e) + { + if (e instanceof BadCredentialsException) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + throw new UserPasswordNotMatchException(); + } + else + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage())); + throw new CustomException(e.getMessage()); + } + } + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); + LoginUser loginUser = (LoginUser) authentication.getPrincipal(); + // 生成token + return tokenService.createToken(loginUser); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/security/service/SysPermissionService.java b/ruoyi/src/main/java/com/ruoyi/framework/security/service/SysPermissionService.java new file mode 100644 index 0000000..1bdb667 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/security/service/SysPermissionService.java @@ -0,0 +1,66 @@ +package com.ruoyi.framework.security.service; + +import java.util.HashSet; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import com.ruoyi.project.system.domain.SysUser; +import com.ruoyi.project.system.service.ISysMenuService; +import com.ruoyi.project.system.service.ISysRoleService; + +/** + * 用户权限处理 + * + * @author ruoyi + */ +@Component +public class SysPermissionService +{ + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysMenuService menuService; + + /** + * 获取角色数据权限 + * + * @param user 用户信息 + * @return 角色权限信息 + */ + public Set<String> getRolePermission(SysUser user) + { + Set<String> roles = new HashSet<String>(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + roles.add("admin"); + } + else + { + roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); + } + return roles; + } + + /** + * 获取菜单数据权限 + * + * @param user 用户信息 + * @return 菜单权限信息 + */ + public Set<String> getMenuPermission(SysUser user) + { + Set<String> roles = new HashSet<String>(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + roles.add("*:*:*"); + } + else + { + roles.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); + } + return roles; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/security/service/TokenService.java b/ruoyi/src/main/java/com/ruoyi/framework/security/service/TokenService.java new file mode 100644 index 0000000..5eafd02 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/security/service/TokenService.java @@ -0,0 +1,179 @@ +package com.ruoyi.framework.security.service; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import javax.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.IdUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.redis.RedisCache; +import com.ruoyi.framework.security.LoginUser; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +/** + * token验证处理 + * + * @author ruoyi + */ +@Component +public class TokenService +{ + // 令牌自定义标识 + @Value("${token.header}") + private String header; + + // 令牌秘钥 + @Value("${token.secret}") + private String secret; + + // 令牌有效期(默认30分钟) + @Value("${token.expireTime}") + private int expireTime; + + protected static final long MILLIS_SECOND = 1000; + + protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; + + private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L; + + @Autowired + private RedisCache redisCache; + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser(HttpServletRequest request) + { + // 获取请求携带的令牌 + String token = getToken(request); + if (StringUtils.isNotEmpty(token)) + { + Claims claims = parseToken(token); + // 解析对应的权限以及用户信息 + String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); + String userKey = getTokenKey(uuid); + LoginUser user = redisCache.getCacheObject(userKey); + return user; + } + return null; + } + + /** + * 创建令牌 + * + * @param loginUser 用户信息 + * @return 令牌 + */ + public String createToken(LoginUser loginUser) + { + String token = IdUtils.fastUUID(); + loginUser.setToken(token); + refreshToken(loginUser); + + Map<String, Object> claims = new HashMap<>(); + claims.put(Constants.LOGIN_USER_KEY, token); + return createToken(claims); + } + + /** + * 验证令牌有效期,相差不足20分钟,自动刷新缓存 + * + * @param token 令牌 + * @return 令牌 + */ + public void verifyToken(LoginUser loginUser) + { + long expireTime = loginUser.getExpireTime(); + long currentTime = System.currentTimeMillis(); + if (expireTime - currentTime <= MILLIS_MINUTE_TEN) + { + String token = loginUser.getToken(); + loginUser.setToken(token); + refreshToken(loginUser); + } + } + + /** + * 刷新令牌有效期 + * + * @param token 令牌 + * @return 令牌 + */ + public void refreshToken(LoginUser loginUser) + { + loginUser.setLoginTime(System.currentTimeMillis()); + loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); + // 根据uuid将loginUser缓存 + String userKey = getTokenKey(loginUser.getToken()); + redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); + } + + /** + * 从数据声明生成令牌 + * + * @param claims 数据声明 + * @return 令牌 + */ + private String createToken(Map<String, Object> claims) + { + String token = Jwts.builder() + .setClaims(claims) + .signWith(SignatureAlgorithm.HS512, secret).compact(); + return token; + } + + /** + * 从令牌中获取数据声明 + * + * @param token 令牌 + * @return 数据声明 + */ + private Claims parseToken(String token) + { + return Jwts.parser() + .setSigningKey(secret) + .parseClaimsJws(token) + .getBody(); + } + + /** + * 从令牌中获取用户名 + * + * @param token 令牌 + * @return 用户名 + */ + public String getUsernameFromToken(String token) + { + Claims claims = parseToken(token); + return claims.getSubject(); + } + + /** + * 获取请求token + * + * @param request + * @return token + */ + private String getToken(HttpServletRequest request) + { + String token = request.getHeader(header); + if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) + { + token = token.replace(Constants.TOKEN_PREFIX, ""); + } + return token; + } + + private String getTokenKey(String uuid) + { + return Constants.LOGIN_TOKEN_KEY + uuid; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/security/service/UserDetailsServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/framework/security/service/UserDetailsServiceImpl.java new file mode 100644 index 0000000..2a8c692 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/security/service/UserDetailsServiceImpl.java @@ -0,0 +1,60 @@ +package com.ruoyi.framework.security.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import com.ruoyi.common.enums.UserStatus; +import com.ruoyi.common.exception.BaseException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.project.system.domain.SysUser; +import com.ruoyi.project.system.service.ISysUserService; + +/** + * 用户验证处理 + * + * @author ruoyi + */ +@Service +public class UserDetailsServiceImpl implements UserDetailsService +{ + private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); + + @Autowired + private ISysUserService userService; + + @Autowired + private SysPermissionService permissionService; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException + { + SysUser user = userService.selectUserByUserName(username); + if (StringUtils.isNull(user)) + { + log.info("登录用户:{} 不存在.", username); + throw new UsernameNotFoundException("登录用户:" + username + " 不存在"); + } + else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) + { + log.info("登录用户:{} 已被删除.", username); + throw new BaseException("对不起,您的账号:" + username + " 已被删除"); + } + else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) + { + log.info("登录用户:{} 已被停用.", username); + throw new BaseException("对不起,您的账号:" + username + " 已停用"); + } + + return createLoginUser(user); + } + + public UserDetails createLoginUser(SysUser user) + { + return new LoginUser(user, permissionService.getMenuPermission(user)); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/web/controller/BaseController.java b/ruoyi/src/main/java/com/ruoyi/framework/web/controller/BaseController.java new file mode 100644 index 0000000..ccc60a3 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/web/controller/BaseController.java @@ -0,0 +1,85 @@ +package com.ruoyi.framework.web.controller; + +import java.beans.PropertyEditorSupport; +import java.util.Date; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.sql.SqlUtil; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.PageDomain; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.framework.web.page.TableSupport; + +/** + * web层通用数据处理 + * + * @author ruoyi + */ +public class BaseController +{ + protected final Logger logger = LoggerFactory.getLogger(BaseController.class); + + /** + * 将前台传递过来的日期格式的字符串,自动转化为Date类型 + */ + @InitBinder + public void initBinder(WebDataBinder binder) + { + // Date 类型转换 + binder.registerCustomEditor(Date.class, new PropertyEditorSupport() + { + @Override + public void setAsText(String text) + { + setValue(DateUtils.parseDate(text)); + } + }); + } + + /** + * 设置请求分页数据 + */ + protected void startPage() + { + PageDomain pageDomain = TableSupport.buildPageRequest(); + Integer pageNum = pageDomain.getPageNum(); + Integer pageSize = pageDomain.getPageSize(); + if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize)) + { + String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); + PageHelper.startPage(pageNum, pageSize, orderBy); + } + } + + /** + * 响应请求分页数据 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected TableDataInfo getDataTable(List<?> list) + { + TableDataInfo rspData = new TableDataInfo(); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setRows(list); + rspData.setTotal(new PageInfo(list).getTotal()); + return rspData; + } + + /** + * 响应返回结果 + * + * @param rows 影响行数 + * @return 操作结果 + */ + protected AjaxResult toAjax(int rows) + { + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/web/domain/AjaxResult.java b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/AjaxResult.java new file mode 100644 index 0000000..5c551bd --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/AjaxResult.java @@ -0,0 +1,148 @@ +package com.ruoyi.framework.web.domain; + +import java.util.HashMap; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.utils.StringUtils; + +/** + * 操作消息提醒 + * + * @author ruoyi + */ +public class AjaxResult extends HashMap<String, Object> +{ + private static final long serialVersionUID = 1L; + + /** 状态码 */ + public static final String CODE_TAG = "code"; + + /** 返回内容 */ + public static final String MSG_TAG = "msg"; + + /** 数据对象 */ + public static final String DATA_TAG = "data"; + + /** + * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。 + */ + public AjaxResult() + { + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + */ + public AjaxResult(int code, String msg) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + * @param data 数据对象 + */ + public AjaxResult(int code, String msg, Object data) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + if (StringUtils.isNotNull(data)) + { + super.put(DATA_TAG, data); + } + } + + /** + * 返回成功消息 + * + * @return 成功消息 + */ + public static AjaxResult success() + { + return AjaxResult.success("操作成功"); + } + + /** + * 返回成功数据 + * + * @return 成功消息 + */ + public static AjaxResult success(Object data) + { + return AjaxResult.success("操作成功", data); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @return 成功消息 + */ + public static AjaxResult success(String msg) + { + return AjaxResult.success(msg, null); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 成功消息 + */ + public static AjaxResult success(String msg, Object data) + { + return new AjaxResult(HttpStatus.SUCCESS, msg, data); + } + + /** + * 返回错误消息 + * + * @return + */ + public static AjaxResult error() + { + return AjaxResult.error("操作失败"); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult error(String msg) + { + return AjaxResult.error(msg, null); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static AjaxResult error(String msg, Object data) + { + return new AjaxResult(HttpStatus.ERROR, msg, data); + } + + /** + * 返回错误消息 + * + * @param code 状态码 + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult error(int code, String msg) + { + return new AjaxResult(code, msg, null); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/web/domain/BaseEntity.java b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/BaseEntity.java new file mode 100644 index 0000000..7548bc9 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/BaseEntity.java @@ -0,0 +1,131 @@ +package com.ruoyi.framework.web.domain; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import com.alibaba.fastjson.JSON; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** + * Entity基类 + * + * @author ruoyi + */ +public class BaseEntity implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 搜索值 */ + private String searchValue; + + /** 创建者 */ + private String createBy; + + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 更新者 */ + private String updateBy; + + /** 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** 备注 */ + private String remark; + + /** 数据权限 */ + private String dataScope; + + /** 请求参数 */ + @JsonIgnore + private String params; + + public String getSearchValue() + { + return searchValue; + } + + public void setSearchValue(String searchValue) + { + this.searchValue = searchValue; + } + + public String getCreateBy() + { + return createBy; + } + + public void setCreateBy(String createBy) + { + this.createBy = createBy; + } + + public Date getCreateTime() + { + return createTime; + } + + public void setCreateTime(Date createTime) + { + this.createTime = createTime; + } + + public String getUpdateBy() + { + return updateBy; + } + + public void setUpdateBy(String updateBy) + { + this.updateBy = updateBy; + } + + public Date getUpdateTime() + { + return updateTime; + } + + public void setUpdateTime(Date updateTime) + { + this.updateTime = updateTime; + } + + public String getRemark() + { + return remark; + } + + public void setRemark(String remark) + { + this.remark = remark; + } + + public String getDataScope() + { + return dataScope; + } + + public void setDataScope(String dataScope) + { + this.dataScope = dataScope; + } + + @SuppressWarnings("unchecked") + public Map<String, Object> getParams() + { + if (params == null) + { + return new HashMap<>(); + } + return JSON.parseObject(params, Map.class); + } + + public void setParams(String params) + { + this.params = params; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/web/domain/Server.java b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/Server.java new file mode 100644 index 0000000..79e174b --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/Server.java @@ -0,0 +1,241 @@ +package com.ruoyi.framework.web.domain; + +import java.net.UnknownHostException; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; +import com.ruoyi.common.utils.Arith; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.web.domain.server.Cpu; +import com.ruoyi.framework.web.domain.server.Jvm; +import com.ruoyi.framework.web.domain.server.Mem; +import com.ruoyi.framework.web.domain.server.Sys; +import com.ruoyi.framework.web.domain.server.SysFile; +import oshi.SystemInfo; +import oshi.hardware.CentralProcessor; +import oshi.hardware.CentralProcessor.TickType; +import oshi.hardware.GlobalMemory; +import oshi.hardware.HardwareAbstractionLayer; +import oshi.software.os.FileSystem; +import oshi.software.os.OSFileStore; +import oshi.software.os.OperatingSystem; +import oshi.util.Util; + +/** + * 服务器相关信息 + * + * @author ruoyi + */ +public class Server +{ + + private static final int OSHI_WAIT_SECOND = 1000; + + /** + * CPU相关信息 + */ + private Cpu cpu = new Cpu(); + + /** + * 內存相关信息 + */ + private Mem mem = new Mem(); + + /** + * JVM相关信息 + */ + private Jvm jvm = new Jvm(); + + /** + * 服务器相关信息 + */ + private Sys sys = new Sys(); + + /** + * 磁盘相关信息 + */ + private List<SysFile> sysFiles = new LinkedList<SysFile>(); + + public Cpu getCpu() + { + return cpu; + } + + public void setCpu(Cpu cpu) + { + this.cpu = cpu; + } + + public Mem getMem() + { + return mem; + } + + public void setMem(Mem mem) + { + this.mem = mem; + } + + public Jvm getJvm() + { + return jvm; + } + + public void setJvm(Jvm jvm) + { + this.jvm = jvm; + } + + public Sys getSys() + { + return sys; + } + + public void setSys(Sys sys) + { + this.sys = sys; + } + + public List<SysFile> getSysFiles() + { + return sysFiles; + } + + public void setSysFiles(List<SysFile> sysFiles) + { + this.sysFiles = sysFiles; + } + + public void copyTo() throws Exception + { + SystemInfo si = new SystemInfo(); + HardwareAbstractionLayer hal = si.getHardware(); + + setCpuInfo(hal.getProcessor()); + + setMemInfo(hal.getMemory()); + + setSysInfo(); + + setJvmInfo(); + + setSysFiles(si.getOperatingSystem()); + } + + /** + * 设置CPU信息 + */ + private void setCpuInfo(CentralProcessor processor) + { + // CPU信息 + long[] prevTicks = processor.getSystemCpuLoadTicks(); + Util.sleep(OSHI_WAIT_SECOND); + long[] ticks = processor.getSystemCpuLoadTicks(); + long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()]; + long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()]; + long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()]; + long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()]; + long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()]; + long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()]; + long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()]; + long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()]; + long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal; + cpu.setCpuNum(processor.getLogicalProcessorCount()); + cpu.setTotal(totalCpu); + cpu.setSys(cSys); + cpu.setUsed(user); + cpu.setWait(iowait); + cpu.setFree(idle); + } + + /** + * 设置内存信息 + */ + private void setMemInfo(GlobalMemory memory) + { + mem.setTotal(memory.getTotal()); + mem.setUsed(memory.getTotal() - memory.getAvailable()); + mem.setFree(memory.getAvailable()); + } + + /** + * 设置服务器信息 + */ + private void setSysInfo() + { + Properties props = System.getProperties(); + sys.setComputerName(IpUtils.getHostName()); + sys.setComputerIp(IpUtils.getHostIp()); + sys.setOsName(props.getProperty("os.name")); + sys.setOsArch(props.getProperty("os.arch")); + sys.setUserDir(props.getProperty("user.dir")); + } + + /** + * 设置Java虚拟机 + */ + private void setJvmInfo() throws UnknownHostException + { + Properties props = System.getProperties(); + jvm.setTotal(Runtime.getRuntime().totalMemory()); + jvm.setMax(Runtime.getRuntime().maxMemory()); + jvm.setFree(Runtime.getRuntime().freeMemory()); + jvm.setVersion(props.getProperty("java.version")); + jvm.setHome(props.getProperty("java.home")); + } + + /** + * 设置磁盘信息 + */ + private void setSysFiles(OperatingSystem os) + { + FileSystem fileSystem = os.getFileSystem(); + OSFileStore[] fsArray = fileSystem.getFileStores(); + for (OSFileStore fs : fsArray) + { + long free = fs.getUsableSpace(); + long total = fs.getTotalSpace(); + long used = total - free; + SysFile sysFile = new SysFile(); + sysFile.setDirName(fs.getMount()); + sysFile.setSysTypeName(fs.getType()); + sysFile.setTypeName(fs.getName()); + sysFile.setTotal(convertFileSize(total)); + sysFile.setFree(convertFileSize(free)); + sysFile.setUsed(convertFileSize(used)); + sysFile.setUsage(Arith.mul(Arith.div(used, total, 4), 100)); + sysFiles.add(sysFile); + } + } + + /** + * 字节转换 + * + * @param size 字节大小 + * @return 转换后值 + */ + public String convertFileSize(long size) + { + long kb = 1024; + long mb = kb * 1024; + long gb = mb * 1024; + if (size >= gb) + { + return String.format("%.1f GB", (float) size / gb); + } + else if (size >= mb) + { + float f = (float) size / mb; + return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f); + } + else if (size >= kb) + { + float f = (float) size / kb; + return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f); + } + else + { + return String.format("%d B", size); + } + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/web/domain/TreeSelect.java b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/TreeSelect.java new file mode 100644 index 0000000..2ace4e9 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/TreeSelect.java @@ -0,0 +1,77 @@ +package com.ruoyi.framework.web.domain; + +import java.io.Serializable; +import java.util.List; +import java.util.stream.Collectors; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.ruoyi.project.system.domain.SysDept; +import com.ruoyi.project.system.domain.SysMenu; + +/** + * Treeselect树结构实体类 + * + * @author ruoyi + */ +public class TreeSelect implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 节点ID */ + private Long id; + + /** 节点名称 */ + private String label; + + /** 子节点 */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List<TreeSelect> children; + + public TreeSelect() + { + + } + + public TreeSelect(SysDept dept) + { + this.id = dept.getDeptId(); + this.label = dept.getDeptName(); + 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 List<TreeSelect> getChildren() + { + return children; + } + + public void setChildren(List<TreeSelect> children) + { + this.children = children; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java new file mode 100644 index 0000000..cf7b46e --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java @@ -0,0 +1,101 @@ +package com.ruoyi.framework.web.domain.server; + +import com.ruoyi.common.utils.Arith; + +/** + * CPU相关信息 + * + * @author ruoyi + */ +public class Cpu +{ + /** + * 核心数 + */ + private int cpuNum; + + /** + * CPU总的使用率 + */ + private double total; + + /** + * CPU系统使用率 + */ + private double sys; + + /** + * CPU用户使用率 + */ + private double used; + + /** + * CPU当前等待率 + */ + private double wait; + + /** + * CPU当前空闲率 + */ + private double free; + + public int getCpuNum() + { + return cpuNum; + } + + public void setCpuNum(int cpuNum) + { + this.cpuNum = cpuNum; + } + + public double getTotal() + { + return Arith.round(Arith.mul(total, 100), 2); + } + + public void setTotal(double total) + { + this.total = total; + } + + public double getSys() + { + return Arith.round(Arith.mul(sys / total, 100), 2); + } + + public void setSys(double sys) + { + this.sys = sys; + } + + public double getUsed() + { + return Arith.round(Arith.mul(used / total, 100), 2); + } + + public void setUsed(double used) + { + this.used = used; + } + + public double getWait() + { + return Arith.round(Arith.mul(wait / total, 100), 2); + } + + public void setWait(double wait) + { + this.wait = wait; + } + + public double getFree() + { + return Arith.round(Arith.mul(free / total, 100), 2); + } + + public void setFree(double free) + { + this.free = free; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java new file mode 100644 index 0000000..f29d2cb --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java @@ -0,0 +1,122 @@ +package com.ruoyi.framework.web.domain.server; + +import java.lang.management.ManagementFactory; +import com.ruoyi.common.utils.Arith; +import com.ruoyi.common.utils.DateUtils; + +/** + * JVM相关信息 + * + * @author ruoyi + */ +public class Jvm +{ + /** + * 当前JVM占用的内存总数(M) + */ + private double total; + + /** + * JVM最大可用内存总数(M) + */ + private double max; + + /** + * JVM空闲内存(M) + */ + private double free; + + /** + * JDK版本 + */ + private String version; + + /** + * JDK路径 + */ + private String home; + + public double getTotal() + { + return Arith.div(total, (1024 * 1024), 2); + } + + public void setTotal(double total) + { + this.total = total; + } + + public double getMax() + { + return Arith.div(max, (1024 * 1024), 2); + } + + public void setMax(double max) + { + this.max = max; + } + + public double getFree() + { + return Arith.div(free, (1024 * 1024), 2); + } + + public void setFree(double free) + { + this.free = free; + } + + public double getUsed() + { + return Arith.div(total - free, (1024 * 1024), 2); + } + + public double getUsage() + { + return Arith.mul(Arith.div(total - free, total, 4), 100); + } + + /** + * 获取JDK名称 + */ + public String getName() + { + return ManagementFactory.getRuntimeMXBean().getVmName(); + } + + public String getVersion() + { + return version; + } + + public void setVersion(String version) + { + this.version = version; + } + + public String getHome() + { + return home; + } + + public void setHome(String home) + { + this.home = home; + } + + /** + * JDK启动时间 + */ + public String getStartTime() + { + return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate()); + } + + /** + * JDK运行时间 + */ + public String getRunTime() + { + return DateUtils.getDatePoor(DateUtils.getNowDate(), DateUtils.getServerStartDate()); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java new file mode 100644 index 0000000..ac71504 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java @@ -0,0 +1,61 @@ +package com.ruoyi.framework.web.domain.server; + +import com.ruoyi.common.utils.Arith; + +/** + * 內存相关信息 + * + * @author ruoyi + */ +public class Mem +{ + /** + * 内存总量 + */ + private double total; + + /** + * 已用内存 + */ + private double used; + + /** + * 剩余内存 + */ + private double free; + + public double getTotal() + { + return Arith.div(total, (1024 * 1024 * 1024), 2); + } + + public void setTotal(long total) + { + this.total = total; + } + + public double getUsed() + { + return Arith.div(used, (1024 * 1024 * 1024), 2); + } + + public void setUsed(long used) + { + this.used = used; + } + + public double getFree() + { + return Arith.div(free, (1024 * 1024 * 1024), 2); + } + + public void setFree(long free) + { + this.free = free; + } + + public double getUsage() + { + return Arith.mul(Arith.div(used, total, 4), 100); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java new file mode 100644 index 0000000..a5f65e4 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java @@ -0,0 +1,84 @@ +package com.ruoyi.framework.web.domain.server; + +/** + * 系统相关信息 + * + * @author ruoyi + */ +public class Sys +{ + /** + * 服务器名称 + */ + private String computerName; + + /** + * 服务器Ip + */ + private String computerIp; + + /** + * 项目路径 + */ + private String userDir; + + /** + * 操作系统 + */ + private String osName; + + /** + * 系统架构 + */ + private String osArch; + + public String getComputerName() + { + return computerName; + } + + public void setComputerName(String computerName) + { + this.computerName = computerName; + } + + public String getComputerIp() + { + return computerIp; + } + + public void setComputerIp(String computerIp) + { + this.computerIp = computerIp; + } + + public String getUserDir() + { + return userDir; + } + + public void setUserDir(String userDir) + { + this.userDir = userDir; + } + + public String getOsName() + { + return osName; + } + + public void setOsName(String osName) + { + this.osName = osName; + } + + public String getOsArch() + { + return osArch; + } + + public void setOsArch(String osArch) + { + this.osArch = osArch; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java new file mode 100644 index 0000000..47ecf3f --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java @@ -0,0 +1,114 @@ +package com.ruoyi.framework.web.domain.server; + +/** + * 系统文件相关信息 + * + * @author ruoyi + */ +public class SysFile +{ + /** + * 盘符路径 + */ + private String dirName; + + /** + * 盘符类型 + */ + private String sysTypeName; + + /** + * 文件类型 + */ + private String typeName; + + /** + * 总大小 + */ + private String total; + + /** + * 剩余大小 + */ + private String free; + + /** + * 已经使用量 + */ + private String used; + + /** + * 资源的使用率 + */ + private double usage; + + public String getDirName() + { + return dirName; + } + + public void setDirName(String dirName) + { + this.dirName = dirName; + } + + public String getSysTypeName() + { + return sysTypeName; + } + + public void setSysTypeName(String sysTypeName) + { + this.sysTypeName = sysTypeName; + } + + public String getTypeName() + { + return typeName; + } + + public void setTypeName(String typeName) + { + this.typeName = typeName; + } + + public String getTotal() + { + return total; + } + + public void setTotal(String total) + { + this.total = total; + } + + public String getFree() + { + return free; + } + + public void setFree(String free) + { + this.free = free; + } + + public String getUsed() + { + return used; + } + + public void setUsed(String used) + { + this.used = used; + } + + public double getUsage() + { + return usage; + } + + public void setUsage(double usage) + { + this.usage = usage; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java b/ruoyi/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..d5e4289 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java @@ -0,0 +1,93 @@ +package com.ruoyi.framework.web.exception; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authentication.AccountExpiredException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.NoHandlerFoundException; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.exception.BaseException; +import com.ruoyi.common.exception.CustomException; +import com.ruoyi.common.exception.DemoModeException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.domain.AjaxResult; + +/** + * 全局异常处理器 + * + * @author ruoyi + */ +@RestControllerAdvice +public class GlobalExceptionHandler +{ + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + /** + * 基础异常 + */ + @ExceptionHandler(BaseException.class) + public AjaxResult baseException(BaseException e) + { + return AjaxResult.error(e.getMessage()); + } + + /** + * 业务异常 + */ + @ExceptionHandler(CustomException.class) + public AjaxResult businessException(CustomException e) + { + if (StringUtils.isNull(e.getCode())) + { + return AjaxResult.error(e.getMessage()); + } + return AjaxResult.error(e.getCode(), e.getMessage()); + } + + @ExceptionHandler(NoHandlerFoundException.class) + public AjaxResult handlerNoFoundException(Exception e) + { + log.error(e.getMessage(), e); + return AjaxResult.error(HttpStatus.NOT_FOUND, "路径不存在,请检查路径是否正确"); + } + + @ExceptionHandler(AccessDeniedException.class) + public AjaxResult handleAuthorizationException(AccessDeniedException e) + { + log.error(e.getMessage()); + return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权"); + } + + @ExceptionHandler(AccountExpiredException.class) + public AjaxResult handleAccountExpiredException(AccountExpiredException e) + { + log.error(e.getMessage(), e); + return AjaxResult.error(e.getMessage()); + } + + @ExceptionHandler(UsernameNotFoundException.class) + public AjaxResult handleUsernameNotFoundException(UsernameNotFoundException e) + { + log.error(e.getMessage(), e); + return AjaxResult.error(e.getMessage()); + } + + @ExceptionHandler(Exception.class) + public AjaxResult handleException(Exception e) + { + log.error(e.getMessage(), e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 演示模式异常 + */ + @ExceptionHandler(DemoModeException.class) + public AjaxResult demoModeException(DemoModeException e) + { + return AjaxResult.error("演示模式,不允许操作"); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/web/page/PageDomain.java b/ruoyi/src/main/java/com/ruoyi/framework/web/page/PageDomain.java new file mode 100644 index 0000000..7e50472 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/web/page/PageDomain.java @@ -0,0 +1,69 @@ +package com.ruoyi.framework.web.page; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 分页数据 + * + * @author ruoyi + */ +public class PageDomain +{ + /** 当前记录起始索引 */ + private Integer pageNum; + /** 每页显示记录数 */ + private Integer pageSize; + /** 排序列 */ + private String orderByColumn; + /** 排序的方向 "desc" 或者 "asc". */ + private String isAsc; + + public String getOrderBy() + { + if (StringUtils.isEmpty(orderByColumn)) + { + return ""; + } + return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc; + } + + public Integer getPageNum() + { + return pageNum; + } + + public void setPageNum(Integer pageNum) + { + this.pageNum = pageNum; + } + + public Integer getPageSize() + { + return pageSize; + } + + public void setPageSize(Integer pageSize) + { + this.pageSize = pageSize; + } + + public String getOrderByColumn() + { + return orderByColumn; + } + + public void setOrderByColumn(String orderByColumn) + { + this.orderByColumn = orderByColumn; + } + + public String getIsAsc() + { + return isAsc; + } + + public void setIsAsc(String isAsc) + { + this.isAsc = isAsc; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/framework/web/page/TableDataInfo.java b/ruoyi/src/main/java/com/ruoyi/framework/web/page/TableDataInfo.java new file mode 100644 index 0000000..3a184c6 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/web/page/TableDataInfo.java @@ -0,0 +1,85 @@ +package com.ruoyi.framework.web.page; + +import java.io.Serializable; +import java.util.List; + +/** + * 表格分页数据对象 + * + * @author ruoyi + */ +public class TableDataInfo implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 总记录数 */ + private long total; + + /** 列表数据 */ + private List<?> rows; + + /** 消息状态码 */ + private int code; + + /** 消息内容 */ + private int msg; + + /** + * 表格数据对象 + */ + public TableDataInfo() + { + } + + /** + * 分页 + * + * @param list 列表数据 + * @param total 总记录数 + */ + public TableDataInfo(List<?> list, int total) + { + this.rows = list; + this.total = total; + } + + public long getTotal() + { + return total; + } + + public void setTotal(long total) + { + this.total = total; + } + + public List<?> getRows() + { + return rows; + } + + public void setRows(List<?> rows) + { + this.rows = rows; + } + + public int getCode() + { + return code; + } + + public void setCode(int code) + { + this.code = code; + } + + public int getMsg() + { + return msg; + } + + public void setMsg(int msg) + { + this.msg = msg; + } +} \ No newline at end of file diff --git a/ruoyi/src/main/java/com/ruoyi/framework/web/page/TableSupport.java b/ruoyi/src/main/java/com/ruoyi/framework/web/page/TableSupport.java new file mode 100644 index 0000000..fa6a9ee --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/framework/web/page/TableSupport.java @@ -0,0 +1,49 @@ +package com.ruoyi.framework.web.page; + +import com.ruoyi.common.utils.ServletUtils; + +/** + * 表格数据处理 + * + * @author ruoyi + */ +public class TableSupport +{ + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 封装分页对象 + */ + public static PageDomain getPageDomain() + { + PageDomain pageDomain = new PageDomain(); + pageDomain.setPageNum(ServletUtils.getParameterToInt(PAGE_NUM)); + pageDomain.setPageSize(ServletUtils.getParameterToInt(PAGE_SIZE)); + pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN)); + pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC)); + return pageDomain; + } + + public static PageDomain buildPageRequest() + { + return getPageDomain(); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/common/CaptchaController.java b/ruoyi/src/main/java/com/ruoyi/project/common/CaptchaController.java new file mode 100644 index 0000000..9a3af20 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/common/CaptchaController.java @@ -0,0 +1,62 @@ +package com.ruoyi.project.common; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.IdUtils; +import com.ruoyi.common.utils.VerifyCodeUtils; +import com.ruoyi.common.utils.sign.Base64; +import com.ruoyi.framework.redis.RedisCache; +import com.ruoyi.framework.web.domain.AjaxResult; + +/** + * 验证码操作处理 + * + * @author ruoyi + */ +@RestController +public class CaptchaController +{ + @Autowired + private RedisCache redisCache; + + /** + * 生成验证码 + */ + @GetMapping("/captchaImage") + public AjaxResult getCode(HttpServletResponse response) throws IOException + { + // 生成随机字串 + String verifyCode = VerifyCodeUtils.generateVerifyCode(4); + // 唯一标识 + String uuid = IdUtils.simpleUUID(); + String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; + + redisCache.setCacheObject(verifyKey, verifyCode, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); + // 生成图片 + int w = 111, h = 36; + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + VerifyCodeUtils.outputImage(w, h, stream, verifyCode); + try + { + AjaxResult ajax = AjaxResult.success(); + ajax.put("uuid", uuid); + ajax.put("img", Base64.encode(stream.toByteArray())); + return ajax; + } + catch (Exception e) + { + e.printStackTrace(); + return AjaxResult.error(e.getMessage()); + } + finally + { + stream.close(); + } + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/common/CommonController.java b/ruoyi/src/main/java/com/ruoyi/project/common/CommonController.java new file mode 100644 index 0000000..3362e00 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/common/CommonController.java @@ -0,0 +1,46 @@ +package com.ruoyi.project.common; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.utils.file.FileUploadUtils; +import com.ruoyi.framework.config.RuoYiConfig; +import com.ruoyi.framework.config.ServerConfig; +import com.ruoyi.framework.web.domain.AjaxResult; + +/** + * 通用请求处理 + * + * @author ruoyi + */ +@RestController +public class CommonController +{ + @Autowired + private ServerConfig serverConfig; + + /** + * 通用上传请求 + */ + @PostMapping("/common/upload") + public AjaxResult uploadFile(MultipartFile file) throws Exception + { + try + { + // 上传文件路径 + String filePath = RuoYiConfig.getUploadPath(); + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + String url = serverConfig.getUrl() + fileName; + AjaxResult ajax = AjaxResult.success(); + ajax.put("fileName", fileName); + ajax.put("url", url); + return ajax; + } + catch (Exception e) + { + return AjaxResult.error(e.getMessage()); + } + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/controller/ServerController.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/controller/ServerController.java new file mode 100644 index 0000000..a692226 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/controller/ServerController.java @@ -0,0 +1,28 @@ +package com.ruoyi.project.monitor.controller; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.domain.Server; + +/** + * 服务器监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/server") +public class ServerController extends BaseController +{ + @PreAuthorize("@ss.hasPermi('monitor:server:list')") + @GetMapping() + public AjaxResult getInfo() throws Exception + { + Server server = new Server(); + server.copyTo(); + return AjaxResult.success(server); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysLogininforController.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysLogininforController.java new file mode 100644 index 0000000..2915b84 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysLogininforController.java @@ -0,0 +1,34 @@ +package com.ruoyi.project.monitor.controller; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.monitor.domain.SysLogininfor; +import com.ruoyi.project.monitor.service.ISysLogininforService; + +/** + * 系统访问记录 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/logininfor") +public class SysLogininforController extends BaseController +{ + @Autowired + private ISysLogininforService logininforService; + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:list')") + @GetMapping("/list") + public TableDataInfo list(SysLogininfor logininfor) + { + startPage(); + List<SysLogininfor> list = logininforService.selectLogininforList(logininfor); + return getDataTable(list); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysOperlogController.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysOperlogController.java new file mode 100644 index 0000000..5d316ab --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysOperlogController.java @@ -0,0 +1,34 @@ +package com.ruoyi.project.monitor.controller; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.monitor.domain.SysOperLog; +import com.ruoyi.project.monitor.service.ISysOperLogService; + +/** + * 操作日志记录 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/operlog") +public class SysOperlogController extends BaseController +{ + @Autowired + private ISysOperLogService operLogService; + + @PreAuthorize("@ss.hasPermi('monitor:operlog:list')") + @GetMapping("/list") + public TableDataInfo list(SysOperLog operLog) + { + startPage(); + List<SysOperLog> list = operLogService.selectOperLogList(operLog); + return getDataTable(list); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/domain/SysLogininfor.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/domain/SysLogininfor.java new file mode 100644 index 0000000..ea0c40f --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/domain/SysLogininfor.java @@ -0,0 +1,131 @@ +package com.ruoyi.project.monitor.domain; + +import java.util.Date; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 系统访问记录表 sys_logininfor + * + * @author ruoyi + */ +public class SysLogininfor extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** ID */ + private Long infoId; + + /** 用户账号 */ + private String userName; + + /** 登录状态 0成功 1失败 */ + private String status; + + /** 登录IP地址 */ + private String ipaddr; + + /** 登录地点 */ + private String loginLocation; + + /** 浏览器类型 */ + private String browser; + + /** 操作系统 */ + private String os; + + /** 提示消息 */ + private String msg; + + /** 访问时间 */ + private Date loginTime; + + public Long getInfoId() + { + return infoId; + } + + public void setInfoId(Long infoId) + { + this.infoId = infoId; + } + + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } + + public Date getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Date loginTime) + { + this.loginTime = loginTime; + } +} \ No newline at end of file diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/domain/SysOperLog.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/domain/SysOperLog.java new file mode 100644 index 0000000..a111aca --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/domain/SysOperLog.java @@ -0,0 +1,235 @@ +package com.ruoyi.project.monitor.domain; + +import java.util.Date; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 操作日志记录表 oper_log + * + * @author ruoyi + */ +public class SysOperLog extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 日志主键 */ + private Long operId; + + /** 操作模块 */ + private String title; + + /** 业务类型(0其它 1新增 2修改 3删除) */ + private Integer businessType; + + /** 业务类型数组 */ + private Integer[] businessTypes; + + /** 请求方法 */ + private String method; + + /** 请求方式 */ + private String requestMethod; + + /** 操作类别(0其它 1后台用户 2手机端用户) */ + private Integer operatorType; + + /** 操作人员 */ + private String operName; + + /** 部门名称 */ + private String deptName; + + /** 请求url */ + private String operUrl; + + /** 操作地址 */ + private String operIp; + + /** 操作地点 */ + private String operLocation; + + /** 请求参数 */ + private String operParam; + + /** 返回参数 */ + private String jsonResult; + + /** 操作状态(0正常 1异常) */ + private Integer status; + + /** 错误消息 */ + private String errorMsg; + + /** 操作时间 */ + private Date operTime; + + public Long getOperId() + { + return operId; + } + + public void setOperId(Long operId) + { + this.operId = operId; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public Integer getBusinessType() + { + return businessType; + } + + public void setBusinessType(Integer businessType) + { + this.businessType = businessType; + } + + public Integer[] getBusinessTypes() + { + return businessTypes; + } + + public void setBusinessTypes(Integer[] businessTypes) + { + this.businessTypes = businessTypes; + } + + public String getMethod() + { + return method; + } + + public void setMethod(String method) + { + this.method = method; + } + + public String getRequestMethod() + { + return requestMethod; + } + + public void setRequestMethod(String requestMethod) + { + this.requestMethod = requestMethod; + } + + public Integer getOperatorType() + { + return operatorType; + } + + public void setOperatorType(Integer operatorType) + { + this.operatorType = operatorType; + } + + public String getOperName() + { + return operName; + } + + public void setOperName(String operName) + { + this.operName = operName; + } + + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + public String getOperUrl() + { + return operUrl; + } + + public void setOperUrl(String operUrl) + { + this.operUrl = operUrl; + } + + public String getOperIp() + { + return operIp; + } + + public void setOperIp(String operIp) + { + this.operIp = operIp; + } + + public String getOperLocation() + { + return operLocation; + } + + public void setOperLocation(String operLocation) + { + this.operLocation = operLocation; + } + + public String getOperParam() + { + return operParam; + } + + public void setOperParam(String operParam) + { + this.operParam = operParam; + } + + public String getJsonResult() + { + return jsonResult; + } + + public void setJsonResult(String jsonResult) + { + this.jsonResult = jsonResult; + } + + public Integer getStatus() + { + return status; + } + + public void setStatus(Integer status) + { + this.status = status; + } + + public String getErrorMsg() + { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) + { + this.errorMsg = errorMsg; + } + + public Date getOperTime() + { + return operTime; + } + + public void setOperTime(Date operTime) + { + this.operTime = operTime; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/mapper/SysLogininforMapper.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/mapper/SysLogininforMapper.java new file mode 100644 index 0000000..43cd82e --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/mapper/SysLogininforMapper.java @@ -0,0 +1,42 @@ +package com.ruoyi.project.monitor.mapper; + +import java.util.List; +import com.ruoyi.project.monitor.domain.SysLogininfor; + +/** + * 系统访问日志情况信息 数据层 + * + * @author ruoyi + */ +public interface SysLogininforMapper +{ + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public void insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + public List<SysLogininfor> selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param ids 需要删除的数据 + * @return 结果 + */ + public int deleteLogininforByIds(String[] ids); + + /** + * 清空系统登录日志 + * + * @return 结果 + */ + public int cleanLogininfor(); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/mapper/SysOperLogMapper.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/mapper/SysOperLogMapper.java new file mode 100644 index 0000000..2f7c2da --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/mapper/SysOperLogMapper.java @@ -0,0 +1,48 @@ +package com.ruoyi.project.monitor.mapper; + +import java.util.List; +import com.ruoyi.project.monitor.domain.SysOperLog; + +/** + * 操作日志 数据层 + * + * @author ruoyi + */ +public interface SysOperLogMapper +{ + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + public void insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + public List<SysOperLog> selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param ids 需要删除的数据 + * @return 结果 + */ + public int deleteOperLogByIds(String[] ids); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + public SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + public void cleanOperLog(); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/service/ISysLogininforService.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/service/ISysLogininforService.java new file mode 100644 index 0000000..c00bff7 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/service/ISysLogininforService.java @@ -0,0 +1,40 @@ +package com.ruoyi.project.monitor.service; + +import java.util.List; +import com.ruoyi.project.monitor.domain.SysLogininfor; + +/** + * 系统访问日志情况信息 服务层 + * + * @author ruoyi + */ +public interface ISysLogininforService +{ + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public void insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + public List<SysLogininfor> selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param ids 需要删除的数据 + * @return + */ + public int deleteLogininforByIds(String ids); + + /** + * 清空系统登录日志 + */ + public void cleanLogininfor(); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/service/ISysOperLogService.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/service/ISysOperLogService.java new file mode 100644 index 0000000..d99eaf0 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/service/ISysOperLogService.java @@ -0,0 +1,48 @@ +package com.ruoyi.project.monitor.service; + +import java.util.List; +import com.ruoyi.project.monitor.domain.SysOperLog; + +/** + * 操作日志 服务层 + * + * @author ruoyi + */ +public interface ISysOperLogService +{ + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + public void insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + public List<SysOperLog> selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param ids 需要删除的数据 + * @return 结果 + */ + public int deleteOperLogByIds(String ids); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + public SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + public void cleanOperLog(); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/service/impl/SysLogininforServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/service/impl/SysLogininforServiceImpl.java new file mode 100644 index 0000000..6de6159 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/service/impl/SysLogininforServiceImpl.java @@ -0,0 +1,66 @@ +package com.ruoyi.project.monitor.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.project.monitor.domain.SysLogininfor; +import com.ruoyi.project.monitor.mapper.SysLogininforMapper; +import com.ruoyi.project.monitor.service.ISysLogininforService; + +/** + * 系统访问日志情况信息 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysLogininforServiceImpl implements ISysLogininforService +{ + + @Autowired + private SysLogininforMapper logininforMapper; + + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + @Override + public void insertLogininfor(SysLogininfor logininfor) + { + logininforMapper.insertLogininfor(logininfor); + } + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + @Override + public List<SysLogininfor> selectLogininforList(SysLogininfor logininfor) + { + return logininforMapper.selectLogininforList(logininfor); + } + + /** + * 批量删除系统登录日志 + * + * @param ids 需要删除的数据 + * @return + */ + @Override + public int deleteLogininforByIds(String ids) + { + return logininforMapper.deleteLogininforByIds(Convert.toStrArray(ids)); + } + + /** + * 清空系统登录日志 + */ + @Override + public void cleanLogininfor() + { + logininforMapper.cleanLogininfor(); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/service/impl/SysOperLogServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/service/impl/SysOperLogServiceImpl.java new file mode 100644 index 0000000..fdf9684 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/service/impl/SysOperLogServiceImpl.java @@ -0,0 +1,77 @@ +package com.ruoyi.project.monitor.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.project.monitor.domain.SysOperLog; +import com.ruoyi.project.monitor.mapper.SysOperLogMapper; +import com.ruoyi.project.monitor.service.ISysOperLogService; + +/** + * 操作日志 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysOperLogServiceImpl implements ISysOperLogService +{ + @Autowired + private SysOperLogMapper operLogMapper; + + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + @Override + public void insertOperlog(SysOperLog operLog) + { + operLogMapper.insertOperlog(operLog); + } + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + @Override + public List<SysOperLog> selectOperLogList(SysOperLog operLog) + { + return operLogMapper.selectOperLogList(operLog); + } + + /** + * 批量删除系统操作日志 + * + * @param ids 需要删除的数据 + * @return + */ + @Override + public int deleteOperLogByIds(String ids) + { + return operLogMapper.deleteOperLogByIds(Convert.toStrArray(ids)); + } + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + @Override + public SysOperLog selectOperLogById(Long operId) + { + return operLogMapper.selectOperLogById(operId); + } + + /** + * 清空操作日志 + */ + @Override + public void cleanOperLog() + { + operLogMapper.cleanOperLog(); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysConfigController.java b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysConfigController.java new file mode 100644 index 0000000..13b9d7c --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysConfigController.java @@ -0,0 +1,110 @@ +package com.ruoyi.project.system.controller; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +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.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.system.domain.SysConfig; +import com.ruoyi.project.system.service.ISysConfigService; + +/** + * 参数配置 信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/config") +public class SysConfigController extends BaseController +{ + @Autowired + private ISysConfigService configService; + + /** + * 获取参数配置列表 + */ + @PreAuthorize("@ss.hasPermi('system:config:list')") + @GetMapping("/list") + public TableDataInfo list(SysConfig config) + { + startPage(); + List<SysConfig> list = configService.selectConfigList(config); + return getDataTable(list); + } + + /** + * 根据参数编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:config:query')") + @GetMapping(value = "/{configId}") + public AjaxResult getInfo(@PathVariable Long configId) + { + return AjaxResult.success(configService.selectConfigById(configId)); + } + + /** + * 根据参数键名查询参数值 + */ + @PreAuthorize("@ss.hasPermi('system:config:query')") + @GetMapping(value = "/configKey/{configKey}") + public AjaxResult getConfigKey(@PathVariable String configKey) + { + return AjaxResult.success(configService.selectConfigByKey(configKey)); + } + + /** + * 新增参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:add')") + @Log(title = "参数管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysConfig config) + { + if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config))) + { + return AjaxResult.error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setCreateBy(SecurityUtils.getUsername()); + return toAjax(configService.insertConfig(config)); + } + + /** + * 修改参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:edit')") + @Log(title = "参数管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysConfig config) + { + if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config))) + { + return AjaxResult.error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(configService.updateConfig(config)); + } + + /** + * 删除参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:remove')") + @Log(title = "参数管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{configId}") + public AjaxResult remove(@PathVariable Long configId) + { + return toAjax(configService.deleteConfigById(configId)); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDeptController.java b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDeptController.java new file mode 100644 index 0000000..00700b1 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDeptController.java @@ -0,0 +1,133 @@ +package com.ruoyi.project.system.controller; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +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.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.project.system.domain.SysDept; +import com.ruoyi.project.system.service.ISysDeptService; + +/** + * 部门信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dept") +public class SysDeptController extends BaseController +{ + @Autowired + private ISysDeptService deptService; + + /** + * 获取部门列表 + */ + @PreAuthorize("@ss.hasPermi('system:dept:list')") + @GetMapping("/list") + public AjaxResult list(SysDept dept) + { + List<SysDept> depts = deptService.selectDeptList(dept); + return AjaxResult.success(deptService.buildDeptTree(depts)); + } + + /** + * 根据部门编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:dept:query')") + @GetMapping(value = "/{deptId}") + public AjaxResult getInfo(@PathVariable Long deptId) + { + return AjaxResult.success(deptService.selectDeptById(deptId)); + } + + /** + * 获取部门下拉树列表 + */ + @PreAuthorize("@ss.hasPermi('system:dept:query')") + @GetMapping("/treeselect") + public AjaxResult treeselect(SysDept dept) + { + List<SysDept> depts = deptService.selectDeptList(dept); + return AjaxResult.success(deptService.buildDeptTreeSelect(depts)); + } + + /** + * 加载对应角色部门列表树 + */ + @PreAuthorize("@ss.hasPermi('system:dept:query')") + @GetMapping(value = "/roleDeptTreeselect/{roleId}") + @ResponseBody + public AjaxResult roleDeptTreeselect(@PathVariable("roleId") Long roleId) + { + return AjaxResult.success(deptService.selectDeptListByRoleId(roleId)); + } + + /** + * 新增部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:add')") + @Log(title = "部门管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysDept dept) + { + if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept))) + { + return AjaxResult.error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + dept.setCreateBy(SecurityUtils.getUsername()); + return toAjax(deptService.insertDept(dept)); + } + + /** + * 修改部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:edit')") + @Log(title = "部门管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysDept dept) + { + if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept))) + { + return AjaxResult.error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + else if (dept.getParentId().equals(dept.getDeptId())) + { + return AjaxResult.error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己"); + } + dept.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(deptService.updateDept(dept)); + } + + /** + * 删除部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:remove')") + @Log(title = "部门管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{deptId}") + public AjaxResult remove(@PathVariable Long deptId) + { + if (deptService.hasChildByDeptId(deptId)) + { + return AjaxResult.error("存在下级部门,不允许删除"); + } + if (deptService.checkDeptExistUser(deptId)) + { + return AjaxResult.error("部门存在用户,不允许删除"); + } + return toAjax(deptService.deleteDeptById(deptId)); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDictDataController.java b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDictDataController.java new file mode 100644 index 0000000..488eeb5 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDictDataController.java @@ -0,0 +1,100 @@ +package com.ruoyi.project.system.controller; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +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.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.system.domain.SysDictData; +import com.ruoyi.project.system.service.ISysDictDataService; + +/** + * 数据字典信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dict/data") +public class SysDictDataController extends BaseController +{ + @Autowired + private ISysDictDataService dictDataService; + + @PreAuthorize("@ss.hasPermi('system:dict:list')") + @GetMapping("/list") + @ResponseBody + public TableDataInfo list(SysDictData dictData) + { + startPage(); + List<SysDictData> list = dictDataService.selectDictDataList(dictData); + return getDataTable(list); + } + + /** + * 查询字典数据详细 + */ + @PreAuthorize("@ss.hasPermi('system:dict:query')") + @GetMapping(value = "/{dictCode}") + public AjaxResult getInfo(@PathVariable Long dictCode) + { + return AjaxResult.success(dictDataService.selectDictDataById(dictCode)); + } + + /** + * 根据字典类型查询字典数据信息 + */ + @PreAuthorize("@ss.hasPermi('system:dict:query')") + @GetMapping(value = "/dictType/{dictType}") + public AjaxResult dictType(@PathVariable String dictType) + { + return AjaxResult.success(dictDataService.selectDictDataByType(dictType)); + } + + /** + * 新增字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:add')") + @Log(title = "字典数据", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysDictData dict) + { + dict.setCreateBy(SecurityUtils.getUsername()); + return toAjax(dictDataService.insertDictData(dict)); + } + + /** + * 修改保存字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:edit')") + @Log(title = "字典数据", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysDictData dict) + { + dict.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(dictDataService.updateDictData(dict)); + } + + /** + * 删除字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictCode}") + public AjaxResult remove(@PathVariable Long dictCode) + { + return toAjax(dictDataService.deleteDictDataById(dictCode)); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDictTypeController.java b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDictTypeController.java new file mode 100644 index 0000000..e98abde --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDictTypeController.java @@ -0,0 +1,99 @@ +package com.ruoyi.project.system.controller; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +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.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.system.domain.SysDictType; +import com.ruoyi.project.system.service.ISysDictTypeService; + +/** + * 数据字典信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dict/type") +public class SysDictTypeController extends BaseController +{ + @Autowired + private ISysDictTypeService dictTypeService; + + @PreAuthorize("@ss.hasPermi('system:dict:list')") + @GetMapping("/list") + @ResponseBody + public TableDataInfo list(SysDictType dictType) + { + startPage(); + List<SysDictType> list = dictTypeService.selectDictTypeList(dictType); + return getDataTable(list); + } + + /** + * 查询字典类型详细 + */ + @PreAuthorize("@ss.hasPermi('system:dict:query')") + @GetMapping(value = "/{dictId}") + public AjaxResult getInfo(@PathVariable Long dictId) + { + return AjaxResult.success(dictTypeService.selectDictTypeById(dictId)); + } + + /** + * 新增字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:add')") + @Log(title = "字典类型", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysDictType dict) + { + if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) + { + return AjaxResult.error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setCreateBy(SecurityUtils.getUsername()); + return toAjax(dictTypeService.insertDictType(dict)); + } + + /** + * 修改字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:edit')") + @Log(title = "字典类型", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysDictType dict) + { + if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) + { + return AjaxResult.error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(dictTypeService.updateDictType(dict)); + } + + /** + * 删除字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictId}") + public AjaxResult remove(@PathVariable Long dictId) + { + return toAjax(dictTypeService.deleteDictTypeById(dictId)); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysLoginController.java b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysLoginController.java new file mode 100644 index 0000000..d7bf923 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysLoginController.java @@ -0,0 +1,94 @@ +package com.ruoyi.project.system.controller; + +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.framework.security.service.SysLoginService; +import com.ruoyi.framework.security.service.SysPermissionService; +import com.ruoyi.framework.security.service.TokenService; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.project.system.domain.SysMenu; +import com.ruoyi.project.system.domain.SysUser; +import com.ruoyi.project.system.service.ISysMenuService; + +/** + * 登录验证 + * + * @author ruoyi + */ +@RestController +public class SysLoginController +{ + @Autowired + private SysLoginService loginService; + + @Autowired + private ISysMenuService menuService; + + @Autowired + private SysPermissionService permissionService; + + @Autowired + private TokenService tokenService; + + /** + * 登录方法 + * + * @param username 用户名 + * @param password 密码 + * @param captcha 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + @PostMapping("/login") + public AjaxResult login(String username, String password, String code, String uuid) + { + AjaxResult ajax = AjaxResult.success(); + // 生成令牌 + String token = loginService.login(username, password, code, uuid); + ajax.put(Constants.TOKEN, token); + return ajax; + } + + /** + * 获取用户信息 + * + * @return 用户信息 + */ + @GetMapping("getInfo") + public AjaxResult getInfo() + { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + SysUser user = loginUser.getUser(); + // 角色集合 + Set<String> roles = permissionService.getRolePermission(user); + // 权限集合 + Set<String> permissions = permissionService.getMenuPermission(user); + AjaxResult ajax = AjaxResult.success(); + ajax.put("user", user); + ajax.put("roles", roles); + ajax.put("permissions", permissions); + return ajax; + } + + /** + * 获取路由信息 + * + * @return 路由信息 + */ + @GetMapping("getRouters") + public AjaxResult getRouters() + { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + // 用户信息 + SysUser user = loginUser.getUser(); + List<SysMenu> menus = menuService.selectMenuTreeByUserId(user.getUserId()); + return AjaxResult.success(menuService.buildMenus(menus)); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysMenuController.java b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysMenuController.java new file mode 100644 index 0000000..d47ff4d --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysMenuController.java @@ -0,0 +1,130 @@ +package com.ruoyi.project.system.controller; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +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.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.project.system.domain.SysMenu; +import com.ruoyi.project.system.service.ISysMenuService; + +/** + * 菜单信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/menu") +public class SysMenuController extends BaseController +{ + @Autowired + private ISysMenuService menuService; + + /** + * 获取菜单列表 + */ + @PreAuthorize("@ss.hasPermi('system:menu:list')") + @GetMapping("/list") + public AjaxResult list(SysMenu menu) + { + List<SysMenu> menus = menuService.selectMenuList(menu); + return AjaxResult.success(menuService.buildMenuTree(menus)); + } + + /** + * 根据菜单编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:menu:query')") + @GetMapping(value = "/{menuId}") + public AjaxResult getInfo(@PathVariable Long menuId) + { + return AjaxResult.success(menuService.selectMenuById(menuId)); + } + + /** + * 获取菜单下拉树列表 + */ + @PreAuthorize("@ss.hasPermi('system:menu:query')") + @GetMapping("/treeselect") + public AjaxResult treeselect(SysMenu dept) + { + List<SysMenu> menus = menuService.selectMenuList(dept); + return AjaxResult.success(menuService.buildMenuTreeSelect(menus)); + } + + /** + * 加载对应角色菜单列表树 + */ + @PreAuthorize("@ss.hasPermi('system:menu:query')") + @GetMapping(value = "/roleMenuTreeselect/{roleId}") + @ResponseBody + public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId) + { + return AjaxResult.success(menuService.selectMenuListByRoleId(roleId)); + } + + /** + * 新增菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:add')") + @Log(title = "菜单管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody @Validated SysMenu menu) + { + if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) + { + return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + menu.setCreateBy(SecurityUtils.getUsername()); + return toAjax(menuService.insertMenu(menu)); + } + + /** + * 修改菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:edit')") + @Log(title = "菜单管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysMenu menu) + { + if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) + { + return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + menu.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(menuService.updateMenu(menu)); + } + + /** + * 删除菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:remove')") + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{menuId}") + public AjaxResult remove(@PathVariable("menuId") Long menuId) + { + if (menuService.hasChildByMenuId(menuId)) + { + return AjaxResult.error("存在子菜单,不允许删除"); + } + if (menuService.checkMenuExistRole(menuId)) + { + return AjaxResult.error("菜单已分配,不允许删除"); + } + return toAjax(menuService.deleteMenuById(menuId)); + } +} \ No newline at end of file diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysNoticeController.java b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysNoticeController.java new file mode 100644 index 0000000..8bd3985 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysNoticeController.java @@ -0,0 +1,91 @@ +package com.ruoyi.project.system.controller; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +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.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.system.domain.SysNotice; +import com.ruoyi.project.system.service.ISysNoticeService; + +/** + * 公告 信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/notice") +public class SysNoticeController extends BaseController +{ + @Autowired + private ISysNoticeService noticeService; + + /** + * 获取通知公告列表 + */ + @PreAuthorize("@ss.hasPermi('system:notice:list')") + @GetMapping("/list") + public TableDataInfo list(SysNotice notice) + { + startPage(); + List<SysNotice> list = noticeService.selectNoticeList(notice); + return getDataTable(list); + } + + /** + * 根据通知公告编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:notice:query')") + @GetMapping(value = "/{noticeId}") + public AjaxResult getInfo(@PathVariable Long noticeId) + { + return AjaxResult.success(noticeService.selectNoticeById(noticeId)); + } + + /** + * 新增通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:add')") + @Log(title = "通知公告", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysNotice notice) + { + notice.setCreateBy(SecurityUtils.getUsername()); + return toAjax(noticeService.insertNotice(notice)); + } + + /** + * 修改通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:edit')") + @Log(title = "通知公告", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysNotice notice) + { + notice.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(noticeService.updateNotice(notice)); + } + + /** + * 删除通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:remove')") + @Log(title = "通知公告", businessType = BusinessType.DELETE) + @DeleteMapping("/{noticeId}") + public AjaxResult remove(@PathVariable Long noticeId) + { + return toAjax(noticeService.deleteNoticeById(noticeId)); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysPostController.java b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysPostController.java new file mode 100644 index 0000000..77c3b55 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysPostController.java @@ -0,0 +1,118 @@ +package com.ruoyi.project.system.controller; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +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.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.system.domain.SysPost; +import com.ruoyi.project.system.service.ISysPostService; + +/** + * 岗位信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/post") +public class SysPostController extends BaseController +{ + @Autowired + private ISysPostService postService; + + /** + * 获取岗位列表 + */ + @PreAuthorize("@ss.hasPermi('system:post:list')") + @GetMapping("/list") + public TableDataInfo list(SysPost post) + { + startPage(); + List<SysPost> list = postService.selectPostList(post); + return getDataTable(list); + } + + /** + * 根据岗位编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:post:query')") + @GetMapping(value = "/{postId}") + public AjaxResult getInfo(@PathVariable Long postId) + { + return AjaxResult.success(postService.selectPostById(postId)); + } + + /** + * 新增岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:add')") + @Log(title = "岗位管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysPost post) + { + if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post))) + { + return AjaxResult.error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } + else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post))) + { + return AjaxResult.error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setCreateBy(SecurityUtils.getUsername()); + return toAjax(postService.insertPost(post)); + } + + /** + * 修改岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:edit')") + @Log(title = "岗位管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysPost post) + { + if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post))) + { + return AjaxResult.error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } + else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post))) + { + return AjaxResult.error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(postService.updatePost(post)); + } + + /** + * 删除岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:remove')") + @Log(title = "岗位管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{postId}") + public AjaxResult remove(@PathVariable Long postId) + { + return toAjax(postService.deletePostById(postId)); + } + + /** + * 获取岗位选择框列表 + */ + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + List<SysPost> posts = postService.selectPostAll(); + return AjaxResult.success(posts); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysProfileController.java b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysProfileController.java new file mode 100644 index 0000000..fd8eee2 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysProfileController.java @@ -0,0 +1,106 @@ +package com.ruoyi.project.system.controller; + +import java.io.IOException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.file.FileUploadUtils; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.config.RuoYiConfig; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.framework.security.service.TokenService; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.project.system.domain.SysUser; +import com.ruoyi.project.system.service.ISysUserService; + +/** + * 个人信息 业务处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/user/profile") +public class SysProfileController extends BaseController +{ + @Autowired + private ISysUserService userService; + + @Autowired + private TokenService tokenService; + + /** + * 个人信息 + */ + @GetMapping + public AjaxResult profile() + { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + SysUser user = loginUser.getUser(); + AjaxResult ajax = AjaxResult.success(user); + ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername())); + ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername())); + return ajax; + } + + /** + * 修改用户 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult updateProfile(@RequestBody SysUser user) + { + return toAjax(userService.updateUserProfile(user)); + } + + /** + * 重置密码 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping("/updatePwd") + public AjaxResult updatePwd(String oldPassword, String newPassword) + { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + String userName = loginUser.getUsername(); + String password = loginUser.getPassword(); + if (!SecurityUtils.matchesPassword(oldPassword, password)) + { + return AjaxResult.error("修改密码失败,旧密码错误"); + } + if (SecurityUtils.matchesPassword(newPassword, password)) + { + return AjaxResult.error("新密码不能与旧密码相同"); + } + return toAjax(userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword))); + } + + /** + * 头像上传 + */ + @Log(title = "用户头像", businessType = BusinessType.UPDATE) + @PostMapping("/avatar") + public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws IOException + { + if (!file.isEmpty()) + { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file); + if (userService.updateUserAvatar(loginUser.getUsername(), avatar)) + { + AjaxResult ajax = AjaxResult.success(); + ajax.put("imgUrl", avatar); + return ajax; + } + } + return AjaxResult.error("上传图片异常,请联系管理员"); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysRoleController.java b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysRoleController.java new file mode 100644 index 0000000..9eae5b4 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysRoleController.java @@ -0,0 +1,143 @@ +package com.ruoyi.project.system.controller; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +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.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.system.domain.SysRole; +import com.ruoyi.project.system.service.ISysRoleService; + +/** + * 角色信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/role") +public class SysRoleController extends BaseController +{ + @Autowired + private ISysRoleService roleService; + + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/list") + public TableDataInfo list(SysRole role) + { + startPage(); + List<SysRole> list = roleService.selectRoleList(role); + return getDataTable(list); + } + + /** + * 根据角色编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping(value = "/{roleId}") + public AjaxResult getInfo(@PathVariable Long roleId) + { + return AjaxResult.success(roleService.selectRoleById(roleId)); + } + + /** + * 新增角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:add')") + @Log(title = "角色管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysRole role) + { + if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role))) + { + return AjaxResult.error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } + else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role))) + { + return AjaxResult.error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setCreateBy(SecurityUtils.getUsername()); + return toAjax(roleService.insertRole(role)); + + } + + /** + * 修改保存角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role))) + { + return AjaxResult.error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } + else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role))) + { + return AjaxResult.error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(roleService.updateRole(role)); + } + + /** + * 修改保存数据权限 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/dataScope") + public AjaxResult dataScope(@RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + return toAjax(roleService.authDataScope(role)); + } + + /** + * 状态修改 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + role.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(roleService.updateRoleStatus(role)); + } + + /** + * 删除岗位 + */ + @PreAuthorize("@ss.hasPermi('system:role:remove')") + @Log(title = "角色管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{roleId}") + public AjaxResult remove(@PathVariable Long roleId) + { + roleService.checkRoleAllowed(new SysRole(roleId)); + return toAjax(roleService.deleteRoleById(roleId)); + } + + /** + * 获取角色选择框列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + return AjaxResult.success(roleService.selectRoleAll()); + } +} \ No newline at end of file diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysUserController.java b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysUserController.java new file mode 100644 index 0000000..cd8cd89 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/controller/SysUserController.java @@ -0,0 +1,153 @@ +package com.ruoyi.project.system.controller; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +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.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.system.domain.SysUser; +import com.ruoyi.project.system.service.ISysPostService; +import com.ruoyi.project.system.service.ISysRoleService; +import com.ruoyi.project.system.service.ISysUserService; + +/** + * 用户信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/user") +public class SysUserController extends BaseController +{ + @Autowired + private ISysUserService userService; + + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysPostService postService; + + /** + * 获取用户列表 + */ + @PreAuthorize("@ss.hasPermi('system:user:list')") + @GetMapping("/list") + public TableDataInfo list(SysUser user) + { + startPage(); + List<SysUser> list = userService.selectUserList(user); + return getDataTable(list); + } + + /** + * 根据用户编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:user:query')") + @GetMapping(value = "/{userId}") + public AjaxResult getInfo(@PathVariable Long userId) + { + AjaxResult ajax = AjaxResult.success(userService.selectUserById(userId)); + ajax.put("postIds", postService.selectPostListByUserId(userId)); + ajax.put("roleIds", roleService.selectRoleListByUserId(userId)); + return ajax; + } + + /** + * 新增用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:add')") + @Log(title = "用户管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysUser user) + { + if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user.getUserName()))) + { + return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); + } + else if (UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) + { + return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + else if (UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) + { + return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setCreateBy(SecurityUtils.getUsername()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + return toAjax(userService.insertUser(user)); + } + + /** + * 修改用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysUser user) + { + userService.checkUserAllowed(user); + if (UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) + { + return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + else if (UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) + { + return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(userService.updateUser(user)); + } + + /** + * 删除用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:remove')") + @Log(title = "用户管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{userId}") + public AjaxResult remove(@PathVariable Long userId) + { + userService.checkUserAllowed(new SysUser(userId)); + return toAjax(userService.deleteUserById(userId)); + } + + /** + * 重置密码 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/resetPwd") + public AjaxResult resetPwd(@RequestBody SysUser user) + { + userService.checkUserAllowed(user); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + user.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(userService.resetPwd(user)); + } + + /** + * 状态修改 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysUser user) + { + userService.checkUserAllowed(user); + user.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(userService.updateUserStatus(user)); + } +} \ No newline at end of file diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysConfig.java b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysConfig.java new file mode 100644 index 0000000..9b4352a --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysConfig.java @@ -0,0 +1,86 @@ +package com.ruoyi.project.system.domain; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 参数配置表 sys_config + * + * @author ruoyi + */ +public class SysConfig extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 参数主键 */ + private Long configId; + + /** 参数名称 */ + private String configName; + + /** 参数键名 */ + private String configKey; + + /** 参数键值 */ + private String configValue; + + /** 系统内置(Y是 N否) */ + private String configType; + + public Long getConfigId() + { + return configId; + } + + public void setConfigId(Long configId) + { + this.configId = configId; + } + + @NotBlank(message = "参数名称不能为空") + @Size(min = 0, max = 100, message = "参数名称不能超过100个字符") + public String getConfigName() + { + return configName; + } + + public void setConfigName(String configName) + { + this.configName = configName; + } + + @NotBlank(message = "参数键名长度不能为空") + @Size(min = 0, max = 100, message = "参数键名长度不能超过100个字符") + public String getConfigKey() + { + return configKey; + } + + public void setConfigKey(String configKey) + { + this.configKey = configKey; + } + + @NotBlank(message = "参数键值不能为空") + @Size(min = 0, max = 500, message = "参数键值长度不能超过500个字符") + public String getConfigValue() + { + return configValue; + } + + public void setConfigValue(String configValue) + { + this.configValue = configValue; + } + + public String getConfigType() + { + return configType; + } + + public void setConfigType(String configType) + { + this.configType = configType; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysDept.java b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysDept.java new file mode 100644 index 0000000..8541660 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysDept.java @@ -0,0 +1,171 @@ +package com.ruoyi.project.system.domain; + +import java.util.ArrayList; +import java.util.List; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 部门表 sys_dept + * + * @author ruoyi + */ +public class SysDept extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 部门ID */ + private Long deptId; + + /** 父部门ID */ + private Long parentId; + + /** 祖级列表 */ + private String ancestors; + + /** 部门名称 */ + private String deptName; + + /** 显示顺序 */ + private String orderNum; + + /** 负责人 */ + private String leader; + + /** 联系电话 */ + private String phone; + + /** 邮箱 */ + private String email; + + /** 部门状态:0正常,1停用 */ + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 父部门名称 */ + private String parentName; + + /** 子部门 */ + private List<SysDept> children = new ArrayList<SysDept>(); + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + public String getAncestors() + { + return ancestors; + } + + public void setAncestors(String ancestors) + { + this.ancestors = ancestors; + } + + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + public String getOrderNum() + { + return orderNum; + } + + public void setOrderNum(String orderNum) + { + this.orderNum = orderNum; + } + + public String getLeader() + { + return leader; + } + + public void setLeader(String leader) + { + this.leader = leader; + } + + public String getPhone() + { + return phone; + } + + public void setPhone(String phone) + { + this.phone = phone; + } + + public String getEmail() + { + return email; + } + + public void setEmail(String email) + { + this.email = email; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public List<SysDept> getChildren() + { + return children; + } + + public void setChildren(List<SysDept> children) + { + this.children = children; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysDictData.java b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysDictData.java new file mode 100644 index 0000000..4c7efc6 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysDictData.java @@ -0,0 +1,145 @@ +package com.ruoyi.project.system.domain; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 字典数据表 sys_dict_data + * + * @author ruoyi + */ +public class SysDictData extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 字典编码 */ + private Long dictCode; + + /** 字典排序 */ + private Long dictSort; + + /** 字典标签 */ + private String dictLabel; + + /** 字典键值 */ + private String dictValue; + + /** 字典类型 */ + private String dictType; + + /** 样式属性(其他样式扩展) */ + private String cssClass; + + /** 表格字典样式 */ + private String listClass; + + /** 是否默认(Y是 N否) */ + private String isDefault; + + /** 状态(0正常 1停用) */ + private String status; + + public Long getDictCode() + { + return dictCode; + } + + public void setDictCode(Long dictCode) + { + this.dictCode = dictCode; + } + + public Long getDictSort() + { + return dictSort; + } + + public void setDictSort(Long dictSort) + { + this.dictSort = dictSort; + } + + @NotBlank(message = "字典标签不能为空") + @Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符") + public String getDictLabel() + { + return dictLabel; + } + + public void setDictLabel(String dictLabel) + { + this.dictLabel = dictLabel; + } + + @NotBlank(message = "字典键值不能为空") + @Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符") + public String getDictValue() + { + return dictValue; + } + + public void setDictValue(String dictValue) + { + this.dictValue = dictValue; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符") + public String getDictType() + { + return dictType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + @Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符") + public String getCssClass() + { + return cssClass; + } + + public void setCssClass(String cssClass) + { + this.cssClass = cssClass; + } + + public String getListClass() + { + return listClass; + } + + public void setListClass(String listClass) + { + this.listClass = listClass; + } + + public boolean getDefault() + { + return UserConstants.YES.equals(this.isDefault) ? true : false; + } + + public String getIsDefault() + { + return isDefault; + } + + public void setIsDefault(String isDefault) + { + this.isDefault = isDefault; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysDictType.java b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysDictType.java new file mode 100644 index 0000000..8d3021e --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysDictType.java @@ -0,0 +1,71 @@ +package com.ruoyi.project.system.domain; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 字典类型表 sys_dict_type + * + * @author ruoyi + */ +public class SysDictType extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 字典主键 */ + private Long dictId; + + /** 字典名称 */ + private String dictName; + + /** 字典类型 */ + private String dictType; + + /** 状态(0正常 1停用) */ + private String status; + + public Long getDictId() + { + return dictId; + } + + public void setDictId(Long dictId) + { + this.dictId = dictId; + } + + @NotBlank(message = "字典名称不能为空") + @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符") + public String getDictName() + { + return dictName; + } + + public void setDictName(String dictName) + { + this.dictName = dictName; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符") + public String getDictType() + { + return dictType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysMenu.java b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysMenu.java new file mode 100644 index 0000000..73303d2 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysMenu.java @@ -0,0 +1,184 @@ +package com.ruoyi.project.system.domain; + +import java.util.List; +import com.ruoyi.framework.web.domain.BaseEntity; +import java.util.ArrayList; + +/** + * 菜单权限表 sys_menu + * + * @author ruoyi + */ +public class SysMenu extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 菜单ID */ + private Long menuId; + + /** 菜单名称 */ + private String menuName; + + /** 父菜单名称 */ + private String parentName; + + /** 父菜单ID */ + private Long parentId; + + /** 显示顺序 */ + private String orderNum; + + /** 路由地址 */ + private String path; + + /** 组件路径 */ + private String component; + + /** 是否为外链(0是 1否) */ + private String isFrame; + + /** 类型(M目录 C菜单 F按钮) */ + private String menuType; + + /** 菜单状态:0显示,1隐藏 */ + private String visible; + + /** 权限字符串 */ + private String perms; + + /** 菜单图标 */ + private String icon; + + /** 子菜单 */ + private List<SysMenu> children = new ArrayList<SysMenu>(); + + public Long getMenuId() + { + return menuId; + } + + public void setMenuId(Long menuId) + { + this.menuId = menuId; + } + + public String getMenuName() + { + return menuName; + } + + public void setMenuName(String menuName) + { + this.menuName = menuName; + } + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + public String getOrderNum() + { + return orderNum; + } + + public void setOrderNum(String orderNum) + { + this.orderNum = orderNum; + } + + 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 getIsFrame() + { + return isFrame; + } + + public void setIsFrame(String isFrame) + { + this.isFrame = isFrame; + } + + public String getMenuType() + { + return menuType; + } + + public void setMenuType(String menuType) + { + this.menuType = menuType; + } + + public String getVisible() + { + return visible; + } + + public void setVisible(String visible) + { + this.visible = visible; + } + + public String getPerms() + { + return perms; + } + + public void setPerms(String perms) + { + this.perms = perms; + } + + public String getIcon() + { + return icon; + } + + public void setIcon(String icon) + { + this.icon = icon; + } + + public List<SysMenu> getChildren() + { + return children; + } + + public void setChildren(List<SysMenu> children) + { + this.children = children; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysNotice.java b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysNotice.java new file mode 100644 index 0000000..dbf5248 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysNotice.java @@ -0,0 +1,78 @@ +package com.ruoyi.project.system.domain; + +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 通知公告表 sys_notice + * + * @author ruoyi + */ +public class SysNotice extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 公告ID */ + private Long noticeId; + + /** 公告标题 */ + private String noticeTitle; + + /** 公告类型(1通知 2公告) */ + private String noticeType; + + /** 公告内容 */ + private String noticeContent; + + /** 公告状态(0正常 1关闭) */ + private String status; + + public Long getNoticeId() + { + return noticeId; + } + + public void setNoticeId(Long noticeId) + { + this.noticeId = noticeId; + } + + public void setNoticeTitle(String noticeTitle) + { + this.noticeTitle = noticeTitle; + } + + public String getNoticeTitle() + { + return noticeTitle; + } + + public void setNoticeType(String noticeType) + { + this.noticeType = noticeType; + } + + public String getNoticeType() + { + return noticeType; + } + + public void setNoticeContent(String noticeContent) + { + this.noticeContent = noticeContent; + } + + public String getNoticeContent() + { + return noticeContent; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getStatus() + { + return status; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysPost.java b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysPost.java new file mode 100644 index 0000000..0b1a937 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysPost.java @@ -0,0 +1,98 @@ +package com.ruoyi.project.system.domain; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 岗位表 sys_post + * + * @author ruoyi + */ +public class SysPost extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 岗位序号 */ + private Long postId; + + /** 岗位编码 */ + private String postCode; + + /** 岗位名称 */ + private String postName; + + /** 岗位排序 */ + private String postSort; + + /** 状态(0正常 1停用) */ + private String status; + + /** 用户是否存在此岗位标识 默认不存在 */ + private boolean flag = false; + + public Long getPostId() + { + return postId; + } + + public void setPostId(Long postId) + { + this.postId = postId; + } + + @NotBlank(message = "岗位编码不能为空") + @Size(min = 0, max = 64, message = "岗位编码长度不能超过64个字符") + public String getPostCode() + { + return postCode; + } + + public void setPostCode(String postCode) + { + this.postCode = postCode; + } + + @NotBlank(message = "岗位名称不能为空") + @Size(min = 0, max = 50, message = "岗位名称长度不能超过50个字符") + public String getPostName() + { + return postName; + } + + public void setPostName(String postName) + { + this.postName = postName; + } + + @NotBlank(message = "显示顺序不能为空") + public String getPostSort() + { + return postSort; + } + + public void setPostSort(String postSort) + { + this.postSort = postSort; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public boolean isFlag() + { + return flag; + } + + public void setFlag(boolean flag) + { + this.flag = flag; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysRole.java b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysRole.java new file mode 100644 index 0000000..27bed48 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysRole.java @@ -0,0 +1,163 @@ +package com.ruoyi.project.system.domain; + +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 角色表 sys_role + * + * @author ruoyi + */ +public class SysRole extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 角色ID */ + private Long roleId; + + /** 角色名称 */ + private String roleName; + + /** 角色权限 */ + private String roleKey; + + /** 角色排序 */ + private String roleSort; + + /** 数据范围 */ + private String dataScope; + + /** 角色状态(0正常 1停用) */ + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 用户是否存在此角色标识 默认不存在 */ + private boolean flag = false; + + /** 菜单组 */ + private Long[] menuIds; + + /** 部门组(数据权限) */ + private Long[] deptIds; + + public SysRole() + { + + } + + public SysRole(Long roleId) + { + this.roleId = roleId; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public boolean isAdmin() + { + return isAdmin(this.roleId); + } + + public static boolean isAdmin(Long roleId) + { + return roleId != null && 1L == roleId; + } + + public String getRoleName() + { + return roleName; + } + + public void setRoleName(String roleName) + { + this.roleName = roleName; + } + + public String getRoleKey() + { + return roleKey; + } + + public void setRoleKey(String roleKey) + { + this.roleKey = roleKey; + } + + public String getRoleSort() + { + return roleSort; + } + + public void setRoleSort(String roleSort) + { + this.roleSort = roleSort; + } + + public String getDataScope() + { + return dataScope; + } + + public void setDataScope(String dataScope) + { + this.dataScope = dataScope; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public boolean isFlag() + { + return flag; + } + + public void setFlag(boolean flag) + { + this.flag = flag; + } + + public Long[] getMenuIds() + { + return menuIds; + } + + public void setMenuIds(Long[] menuIds) + { + this.menuIds = menuIds; + } + + public Long[] getDeptIds() + { + return deptIds; + } + + public void setDeptIds(Long[] deptIds) + { + this.deptIds = deptIds; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysRoleDept.java b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysRoleDept.java new file mode 100644 index 0000000..f3dbd03 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysRoleDept.java @@ -0,0 +1,46 @@ +package com.ruoyi.project.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 角色和部门关联 sys_role_dept + * + * @author ruoyi + */ +public class SysRoleDept +{ + /** 角色ID */ + private Long roleId; + + /** 部门ID */ + private Long deptId; + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("deptId", getDeptId()) + .toString(); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysRoleMenu.java b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysRoleMenu.java new file mode 100644 index 0000000..5445229 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysRoleMenu.java @@ -0,0 +1,46 @@ +package com.ruoyi.project.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 角色和菜单关联 sys_role_menu + * + * @author ruoyi + */ +public class SysRoleMenu +{ + /** 角色ID */ + private Long roleId; + + /** 菜单ID */ + private Long menuId; + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public Long getMenuId() + { + return menuId; + } + + public void setMenuId(Long menuId) + { + this.menuId = menuId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("menuId", getMenuId()) + .toString(); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysUser.java b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysUser.java new file mode 100644 index 0000000..f6549bb --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysUser.java @@ -0,0 +1,270 @@ +package com.ruoyi.project.system.domain; + +import java.util.Date; +import java.util.List; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 用户对象 sys_user + * + * @author ruoyi + */ +public class SysUser extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 用户ID */ + private Long userId; + + /** 部门ID */ + private Long deptId; + + /** 用户账号 */ + private String userName; + + /** 用户昵称 */ + private String nickName; + + /** 用户邮箱 */ + private String email; + + /** 手机号码 */ + private String phonenumber; + + /** 用户性别 */ + private String sex; + + /** 用户头像 */ + private String avatar; + + /** 密码 */ + private String password; + + /** 盐加密 */ + private String salt; + + /** 帐号状态(0正常 1停用) */ + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 最后登陆IP */ + private String loginIp; + + /** 最后登陆时间 */ + private Date loginDate; + + /** 部门对象 */ + private SysDept dept; + + /** 角色对象 */ + private List<SysRole> roles; + + /** 角色组 */ + private Long[] roleIds; + + /** 岗位组 */ + private Long[] postIds; + + public SysUser() + { + + } + + public SysUser(Long userId) + { + this.userId = userId; + } + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public boolean isAdmin() + { + return isAdmin(this.userId); + } + + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + public String getNickName() + { + return nickName; + } + + public void setNickName(String nickName) + { + this.nickName = nickName; + } + + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + public String getEmail() + { + return email; + } + + public void setEmail(String email) + { + this.email = email; + } + + public String getPhonenumber() + { + return phonenumber; + } + + public void setPhonenumber(String phonenumber) + { + this.phonenumber = phonenumber; + } + + public String getSex() + { + return sex; + } + + public void setSex(String sex) + { + this.sex = sex; + } + + public String getAvatar() + { + return avatar; + } + + public void setAvatar(String avatar) + { + this.avatar = avatar; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getSalt() + { + return salt; + } + + public void setSalt(String salt) + { + this.salt = salt; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public String getLoginIp() + { + return loginIp; + } + + public void setLoginIp(String loginIp) + { + this.loginIp = loginIp; + } + + public Date getLoginDate() + { + return loginDate; + } + + public void setLoginDate(Date loginDate) + { + this.loginDate = loginDate; + } + + public SysDept getDept() + { + return dept; + } + + public void setDept(SysDept dept) + { + this.dept = dept; + } + + public List<SysRole> getRoles() + { + return roles; + } + + public void setRoles(List<SysRole> roles) + { + this.roles = roles; + } + + public Long[] getRoleIds() + { + return roleIds; + } + + public void setRoleIds(Long[] roleIds) + { + this.roleIds = roleIds; + } + + public Long[] getPostIds() + { + return postIds; + } + + public void setPostIds(Long[] postIds) + { + this.postIds = postIds; + } + +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysUserPost.java b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysUserPost.java new file mode 100644 index 0000000..1ac4bfc --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysUserPost.java @@ -0,0 +1,46 @@ +package com.ruoyi.project.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 用户和岗位关联 sys_user_post + * + * @author ruoyi + */ +public class SysUserPost +{ + /** 用户ID */ + private Long userId; + + /** 岗位ID */ + private Long postId; + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getPostId() + { + return postId; + } + + public void setPostId(Long postId) + { + this.postId = postId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("postId", getPostId()) + .toString(); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysUserRole.java b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysUserRole.java new file mode 100644 index 0000000..e951bf3 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/domain/SysUserRole.java @@ -0,0 +1,46 @@ +package com.ruoyi.project.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 用户和角色关联 sys_user_role + * + * @author ruoyi + */ +public class SysUserRole +{ + /** 用户ID */ + private Long userId; + + /** 角色ID */ + private Long roleId; + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("roleId", getRoleId()) + .toString(); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/domain/vo/MetaVo.java b/ruoyi/src/main/java/com/ruoyi/project/system/domain/vo/MetaVo.java new file mode 100644 index 0000000..118d2fa --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/domain/vo/MetaVo.java @@ -0,0 +1,49 @@ +package com.ruoyi.project.system.domain.vo; + +/** + * 路由显示信息 + * + * @author ruoyi + */ +public class MetaVo +{ + /** + * 设置该路由在侧边栏和面包屑中展示的名字 + */ + private String title; + + /** + * 设置该路由的图标,对应路径src/icons/svg + */ + private String icon; + + public MetaVo() + { + } + + public MetaVo(String title, String icon) + { + this.title = title; + this.icon = icon; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public String getIcon() + { + return icon; + } + + public void setIcon(String icon) + { + this.icon = icon; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/domain/vo/RouterVo.java b/ruoyi/src/main/java/com/ruoyi/project/system/domain/vo/RouterVo.java new file mode 100644 index 0000000..2177a7e --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/domain/vo/RouterVo.java @@ -0,0 +1,133 @@ +package com.ruoyi.project.system.domain.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import java.util.List; + +/** + * 路由配置信息 + * + * @author ruoyi + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class RouterVo +{ + /** + * 路由名字 + */ + private String name; + + /** + * 路由地址 + */ + private String path; + + /** + * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 + */ + private String hidden; + + /** + * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + */ + private String redirect; + + /** + * 组件地址 + */ + private String component; + + /** + * 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + */ + private Boolean alwaysShow; + + /** + * 其他元素 + */ + private MetaVo meta; + + /** + * 子路由 + */ + private List<RouterVo> children; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getPath() + { + return path; + } + + public void setPath(String path) + { + this.path = path; + } + + public String getHidden() + { + return hidden; + } + + public void setHidden(String hidden) + { + this.hidden = hidden; + } + + public String getRedirect() + { + return redirect; + } + + public void setRedirect(String redirect) + { + this.redirect = redirect; + } + + public String getComponent() + { + return component; + } + + public void setComponent(String component) + { + this.component = component; + } + + public Boolean getAlwaysShow() + { + return alwaysShow; + } + + public void setAlwaysShow(Boolean alwaysShow) + { + this.alwaysShow = alwaysShow; + } + + public MetaVo getMeta() + { + return meta; + } + + public void setMeta(MetaVo meta) + { + this.meta = meta; + } + + public List<RouterVo> getChildren() + { + return children; + } + + public void setChildren(List<RouterVo> children) + { + this.children = children; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysConfigMapper.java b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysConfigMapper.java new file mode 100644 index 0000000..02d9270 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysConfigMapper.java @@ -0,0 +1,60 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import com.ruoyi.project.system.domain.SysConfig; + +/** + * 参数配置 数据层 + * + * @author ruoyi + */ +public interface SysConfigMapper +{ + /** + * 查询参数配置信息 + * + * @param config 参数配置信息 + * @return 参数配置信息 + */ + public SysConfig selectConfig(SysConfig config); + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + public List<SysConfig> selectConfigList(SysConfig config); + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数键名 + * @return 参数配置信息 + */ + public SysConfig checkConfigKeyUnique(String configKey); + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int insertConfig(SysConfig config); + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int updateConfig(SysConfig config); + + /** + * 删除参数配置 + * + * @param configId 需要删除的数据ID + * @return 结果 + */ + public int deleteConfigById(Long configId); +} \ No newline at end of file diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysDeptMapper.java b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysDeptMapper.java new file mode 100644 index 0000000..7dac0cb --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysDeptMapper.java @@ -0,0 +1,109 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.project.system.domain.SysDept; + +/** + * 部门管理 数据层 + * + * @author ruoyi + */ +public interface SysDeptMapper +{ + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + public List<SysDept> selectDeptList(SysDept dept); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + public List<Integer> selectDeptListByRoleId(Long roleId); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + public SysDept selectDeptById(Long deptId); + + /** + * 根据ID查询所有子部门 + * + * @param deptId 部门ID + * @return 部门列表 + */ + public List<SysDept> selectChildrenDeptById(Long deptId); + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + public int hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 + */ + public int checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param deptName 部门名称 + * @param parentId 父部门ID + * @return 结果 + */ + public SysDept checkDeptNameUnique(@Param("deptName") String deptName, @Param("parentId") Long parentId); + + /** + * 新增部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int insertDept(SysDept dept); + + /** + * 修改部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int updateDept(SysDept dept); + + /** + * 修改所在部门的父级部门状态 + * + * @param dept 部门 + */ + public void updateDeptStatus(SysDept dept); + + /** + * 修改子元素关系 + * + * @param depts 子元素 + * @return 结果 + */ + public int updateDeptChildren(@Param("depts") List<SysDept> depts); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + public int deleteDeptById(Long deptId); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysDictDataMapper.java b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysDictDataMapper.java new file mode 100644 index 0000000..eaa25da --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysDictDataMapper.java @@ -0,0 +1,87 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.project.system.domain.SysDictData; + +/** + * 字典表 数据层 + * + * @author ruoyi + */ +public interface SysDictDataMapper +{ + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + public List<SysDictData> selectDictDataList(SysDictData dictData); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + public List<SysDictData> selectDictDataByType(String dictType); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + public String selectDictLabel(@Param("dictType") String dictType, @Param("dictValue") String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + public SysDictData selectDictDataById(Long dictCode); + + /** + * 查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据 + */ + public int countDictDataByType(String dictType); + + /** + * 通过字典ID删除字典数据信息 + * + * @param dictCode 字典数据ID + * @return 结果 + */ + public int deleteDictDataById(Long dictCode); + + /** + * 新增字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int insertDictData(SysDictData dictData); + + /** + * 修改字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int updateDictData(SysDictData dictData); + + /** + * 同步修改字典类型 + * + * @param oldDictType 旧字典类型 + * @param newDictType 新旧字典类型 + * @return 结果 + */ + public int updateDictDataType(@Param("oldDictType") String oldDictType, @Param("newDictType") String newDictType); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysDictTypeMapper.java b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysDictTypeMapper.java new file mode 100644 index 0000000..16ceb69 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysDictTypeMapper.java @@ -0,0 +1,77 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Mapper; +import com.ruoyi.project.system.domain.SysDictType; + +/** + * 字典表 数据层 + * + * @author ruoyi + */ +@Mapper +public interface SysDictTypeMapper +{ + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + public List<SysDictType> selectDictTypeList(SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + public List<SysDictType> selectDictTypeAll(); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + public SysDictType selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + public SysDictType selectDictTypeByType(String dictType); + + /** + * 通过字典ID删除字典信息 + * + * @param dictId 字典ID + * @return 结果 + */ + public int deleteDictTypeById(Long dictId); + + /** + * 新增字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int insertDictType(SysDictType dictType); + + /** + * 修改字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int updateDictType(SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + public SysDictType checkDictTypeUnique(String dictType); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysMenuMapper.java b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysMenuMapper.java new file mode 100644 index 0000000..d52a087 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysMenuMapper.java @@ -0,0 +1,108 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.project.system.domain.SysMenu; + +/** + * 菜单表 数据层 + * + * @author ruoyi + */ +public interface SysMenuMapper +{ + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + public List<SysMenu> selectMenuList(SysMenu menu); + + /** + * 根据用户所有权限 + * + * @return 权限列表 + */ + public List<String> selectMenuPerms(); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public List<String> selectMenuPermsByUserId(Long userId); + + /** + * 根据用户ID查询菜单 + * + * @return 菜单列表 + */ + public List<SysMenu> selectMenuTreeAll(); + + /** + * 根据用户ID查询菜单 + * + * @param username 用户ID + * @return 菜单列表 + */ + public List<SysMenu> selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + public List<Integer> selectMenuListByRoleId(Long roleId); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + public SysMenu selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int hasChildByMenuId(Long menuId); + + /** + * 新增菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int insertMenu(SysMenu menu); + + /** + * 修改菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int updateMenu(SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int deleteMenuById(Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menuName 菜单名称 + * @param parentId 父菜单ID + * @return 结果 + */ + public SysMenu checkMenuNameUnique(@Param("menuName") String menuName, @Param("parentId") Long parentId); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysNoticeMapper.java b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysNoticeMapper.java new file mode 100644 index 0000000..ed6bcc0 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysNoticeMapper.java @@ -0,0 +1,52 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import com.ruoyi.project.system.domain.SysNotice; + +/** + * 通知公告表 数据层 + * + * @author ruoyi + */ +public interface SysNoticeMapper +{ + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + public SysNotice selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + public List<SysNotice> selectNoticeList(SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int insertNotice(SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int updateNotice(SysNotice notice); + + /** + * 批量删除公告 + * + * @param noticeId 公告ID + * @return 结果 + */ + public int deleteNoticeById(Long noticeId); +} \ No newline at end of file diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysPostMapper.java b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysPostMapper.java new file mode 100644 index 0000000..4c0d41e --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysPostMapper.java @@ -0,0 +1,91 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import com.ruoyi.project.system.domain.SysPost; + +/** + * 岗位信息 数据层 + * + * @author ruoyi + */ +public interface SysPostMapper +{ + /** + * 查询岗位数据集合 + * + * @param post 岗位信息 + * @return 岗位数据集合 + */ + public List<SysPost> selectPostList(SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + public List<SysPost> selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + public SysPost selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + public List<Integer> selectPostListByUserId(Long userId); + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + public List<SysPost> selectPostsByUserName(String userName); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + public int deletePostById(Long postId); + + /** + * 修改岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int updatePost(SysPost post); + + /** + * 新增岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int insertPost(SysPost post); + + /** + * 校验岗位名称 + * + * @param postName 岗位名称 + * @return 结果 + */ + public SysPost checkPostNameUnique(String postName); + + /** + * 校验岗位编码 + * + * @param postCode 岗位编码 + * @return 结果 + */ + public SysPost checkPostCodeUnique(String postCode); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysRoleDeptMapper.java b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysRoleDeptMapper.java new file mode 100644 index 0000000..fd4d6bd --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysRoleDeptMapper.java @@ -0,0 +1,44 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import com.ruoyi.project.system.domain.SysRoleDept; + +/** + * 角色与部门关联表 数据层 + * + * @author ruoyi + */ +public interface SysRoleDeptMapper +{ + /** + * 通过角色ID删除角色和部门关联 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleDeptByRoleId(Long roleId); + + /** + * 批量删除角色部门关联信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteRoleDept(Long[] ids); + + /** + * 查询部门使用数量 + * + * @param deptId 部门ID + * @return 结果 + */ + public int selectCountRoleDeptByDeptId(Long deptId); + + /** + * 批量新增角色部门信息 + * + * @param roleDeptList 角色部门列表 + * @return 结果 + */ + public int batchRoleDept(List<SysRoleDept> roleDeptList); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysRoleMapper.java b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysRoleMapper.java new file mode 100644 index 0000000..2f8cbd0 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysRoleMapper.java @@ -0,0 +1,100 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import com.ruoyi.project.system.domain.SysRole; + +/** + * 角色表 数据层 + * + * @author ruoyi + */ +public interface SysRoleMapper +{ + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + public List<SysRole> selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + public List<SysRole> selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List<SysRole> selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + public List<Integer> selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + public SysRole selectRoleById(Long roleId); + + /** + * 根据用户ID查询角色 + * + * @param userName 用户名 + * @return 角色列表 + */ + public List<SysRole> selectRolesByUserName(String userName); + + /** + * 校验角色名称是否唯一 + * + * @param roleName 角色名称 + * @return 角色信息 + */ + public SysRole checkRoleNameUnique(String roleName); + + /** + * 校验角色权限是否唯一 + * + * @param roleKey 角色权限 + * @return 角色信息 + */ + public SysRole checkRoleKeyUnique(String roleKey); + + /** + * 修改角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRole(SysRole role); + + /** + * 新增角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int insertRole(SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleById(Long roleId); + +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysRoleMenuMapper.java b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysRoleMenuMapper.java new file mode 100644 index 0000000..8d2e376 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysRoleMenuMapper.java @@ -0,0 +1,36 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import com.ruoyi.project.system.domain.SysRoleMenu; + +/** + * 角色与菜单关联表 数据层 + * + * @author ruoyi + */ +public interface SysRoleMenuMapper +{ + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int checkMenuExistRole(Long menuId); + + /** + * 通过角色ID删除角色和菜单关联 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleMenuByRoleId(Long roleId); + + /** + * 批量新增角色菜单信息 + * + * @param roleMenuList 角色菜单列表 + * @return 结果 + */ + public int batchRoleMenu(List<SysRoleMenu> roleMenuList); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysUserMapper.java b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysUserMapper.java new file mode 100644 index 0000000..a93cb6f --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysUserMapper.java @@ -0,0 +1,105 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; + +import org.apache.ibatis.annotations.Param; + +import com.ruoyi.project.system.domain.SysUser; + +/** + * 用户表 数据层 + * + * @author ruoyi + */ +public interface SysUserMapper +{ + /** + * 根据条件分页查询用户列表 + * + * @param sysUser 用户信息 + * @return 用户信息集合信息 + */ + public List<SysUser> selectUserList(SysUser sysUser); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public SysUser selectUserByUserName(String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public SysUser selectUserById(Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int insertUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUser(SysUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + public int updateUserAvatar(@Param("userName") String userName, @Param("avatar") String avatar); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + public int resetUserPwd(@Param("userName") String userName, @Param("password") String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserById(Long userId); + + /** + * 校验用户名称是否唯一 + * + * @param userName 用户名称 + * @return 结果 + */ + public int checkUserNameUnique(String userName); + + /** + * 校验手机号码是否唯一 + * + * @param phonenumber 手机号码 + * @return 结果 + */ + public SysUser checkPhoneUnique(String phonenumber); + + /** + * 校验email是否唯一 + * + * @param email 用户邮箱 + * @return 结果 + */ + public SysUser checkEmailUnique(String email); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysUserPostMapper.java b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysUserPostMapper.java new file mode 100644 index 0000000..d67f403 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysUserPostMapper.java @@ -0,0 +1,44 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import com.ruoyi.project.system.domain.SysUserPost; + +/** + * 用户与岗位关联表 数据层 + * + * @author ruoyi + */ +public interface SysUserPostMapper +{ + /** + * 通过用户ID删除用户和岗位关联 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserPostByUserId(Long userId); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + public int countUserPostById(Long postId); + + /** + * 批量删除用户和岗位关联 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteUserPost(Long[] ids); + + /** + * 批量新增用户岗位信息 + * + * @param userPostList 用户角色列表 + * @return 结果 + */ + public int batchUserPost(List<SysUserPost> userPostList); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysUserRoleMapper.java b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysUserRoleMapper.java new file mode 100644 index 0000000..614538d --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/mapper/SysUserRoleMapper.java @@ -0,0 +1,62 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.project.system.domain.SysUserRole; + +/** + * 用户与角色关联表 数据层 + * + * @author ruoyi + */ +public interface SysUserRoleMapper +{ + /** + * 通过用户ID删除用户和角色关联 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserRoleByUserId(Long userId); + + /** + * 批量删除用户和角色关联 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteUserRole(Long[] ids); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + public int countUserRoleByRoleId(Long roleId); + + /** + * 批量新增用户角色信息 + * + * @param userRoleList 用户角色列表 + * @return 结果 + */ + public int batchUserRole(List<SysUserRole> userRoleList); + + /** + * 删除用户和角色关联信息 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + public int deleteUserRoleInfo(SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * @return 结果 + */ + public int deleteUserRoleInfos(@Param("roleId") Long roleId, @Param("userIds") Long[] userIds); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysConfigService.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysConfigService.java new file mode 100644 index 0000000..45353e9 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysConfigService.java @@ -0,0 +1,68 @@ +package com.ruoyi.project.system.service; + +import java.util.List; +import com.ruoyi.project.system.domain.SysConfig; + +/** + * 参数配置 服务层 + * + * @author ruoyi + */ +public interface ISysConfigService +{ + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + public SysConfig selectConfigById(Long configId); + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数键名 + * @return 参数键值 + */ + public Object selectConfigByKey(String configKey); + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + public List<SysConfig> selectConfigList(SysConfig config); + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int insertConfig(SysConfig config); + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int updateConfig(SysConfig config); + + /** + * 删除参数配置信息 + * + * @param configId 需要删除的数据ID + * @return 结果 + */ + public int deleteConfigById(Long configId); + + /** + * 校验参数键名是否唯一 + * + * @param config 参数信息 + * @return 结果 + */ + public String checkConfigKeyUnique(SysConfig config); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysDeptService.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysDeptService.java new file mode 100644 index 0000000..1d8a306 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysDeptService.java @@ -0,0 +1,101 @@ +package com.ruoyi.project.system.service; + +import java.util.List; +import com.ruoyi.framework.web.domain.TreeSelect; +import com.ruoyi.project.system.domain.SysDept; + +/** + * 部门管理 服务层 + * + * @author ruoyi + */ +public interface ISysDeptService +{ + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + public List<SysDept> selectDeptList(SysDept dept); + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * @return 树结构列表 + */ + public List<SysDept> buildDeptTree(List<SysDept> depts); + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + public List<TreeSelect> buildDeptTreeSelect(List<SysDept> depts); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + public List<Integer> selectDeptListByRoleId(Long roleId); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + public SysDept selectDeptById(Long deptId); + + /** + * 是否存在部门子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + public boolean hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + public boolean checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + public String checkDeptNameUnique(SysDept dept); + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int insertDept(SysDept dept); + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int updateDept(SysDept dept); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + public int deleteDeptById(Long deptId); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysDictDataService.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysDictDataService.java new file mode 100644 index 0000000..936bc44 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysDictDataService.java @@ -0,0 +1,69 @@ +package com.ruoyi.project.system.service; + +import java.util.List; +import com.ruoyi.project.system.domain.SysDictData; + +/** + * 字典 业务层 + * + * @author ruoyi + */ +public interface ISysDictDataService +{ + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + public List<SysDictData> selectDictDataList(SysDictData dictData); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + public List<SysDictData> selectDictDataByType(String dictType); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + public String selectDictLabel(String dictType, String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + public SysDictData selectDictDataById(Long dictCode); + + /** + * 通过字典ID删除字典数据信息 + * + * @param dictCode 字典数据ID + * @return 结果 + */ + public int deleteDictDataById(Long dictCode); + + /** + * 新增保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int insertDictData(SysDictData dictData); + + /** + * 修改保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int updateDictData(SysDictData dictData); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysDictTypeService.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysDictTypeService.java new file mode 100644 index 0000000..1e3e7cf --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysDictTypeService.java @@ -0,0 +1,75 @@ +package com.ruoyi.project.system.service; + +import java.util.List; +import com.ruoyi.project.system.domain.SysDictType; + +/** + * 字典 业务层 + * + * @author ruoyi + */ +public interface ISysDictTypeService +{ + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + public List<SysDictType> selectDictTypeList(SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + public List<SysDictType> selectDictTypeAll(); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + public SysDictType selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + public SysDictType selectDictTypeByType(String dictType); + + /** + * 通过字典ID删除字典信息 + * + * @param dictId 字典ID + * @return 结果 + */ + public int deleteDictTypeById(Long dictId); + + /** + * 新增保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int insertDictType(SysDictType dictType); + + /** + * 修改保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int updateDictType(SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + public String checkDictTypeUnique(SysDictType dictType); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysMenuService.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysMenuService.java new file mode 100644 index 0000000..8460554 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysMenuService.java @@ -0,0 +1,127 @@ +package com.ruoyi.project.system.service; + +import java.util.List; +import java.util.Set; +import com.ruoyi.framework.web.domain.TreeSelect; +import com.ruoyi.project.system.domain.SysMenu; +import com.ruoyi.project.system.domain.vo.RouterVo; + +/** + * 菜单 业务层 + * + * @author ruoyi + */ +public interface ISysMenuService +{ + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + public List<SysMenu> selectMenuList(SysMenu menu); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public Set<String> selectMenuPermsByUserId(Long userId); + + /** + * 根据用户ID查询菜单树信息 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List<SysMenu> selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + public List<Integer> selectMenuListByRoleId(Long roleId); + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + public List<RouterVo> buildMenus(List<SysMenu> menus); + + /** + * 构建前端所需要树结构 + * + * @param menus 菜单列表 + * @return 树结构列表 + */ + public List<SysMenu> buildMenuTree(List<SysMenu> menus); + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + public List<TreeSelect> buildMenuTreeSelect(List<SysMenu> menus); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + public SysMenu selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + public boolean hasChildByMenuId(Long menuId); + + /** + * 查询菜单是否存在角色 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + public boolean checkMenuExistRole(Long menuId); + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int insertMenu(SysMenu menu); + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int updateMenu(SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int deleteMenuById(Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + public String checkMenuNameUnique(SysMenu menu); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysNoticeService.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysNoticeService.java new file mode 100644 index 0000000..77c6722 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysNoticeService.java @@ -0,0 +1,52 @@ +package com.ruoyi.project.system.service; + +import java.util.List; +import com.ruoyi.project.system.domain.SysNotice; + +/** + * 公告 服务层 + * + * @author ruoyi + */ +public interface ISysNoticeService +{ + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + public SysNotice selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + public List<SysNotice> selectNoticeList(SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int insertNotice(SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int updateNotice(SysNotice notice); + + /** + * 删除公告信息 + * + * @param noticeId 公告ID + * @return 结果 + */ + public int deleteNoticeById(Long noticeId); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysPostService.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysPostService.java new file mode 100644 index 0000000..f5770f6 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysPostService.java @@ -0,0 +1,83 @@ +package com.ruoyi.project.system.service; + +import java.util.List; +import com.ruoyi.project.system.domain.SysPost; + +/** + * 岗位信息 服务层 + * + * @author ruoyi + */ +public interface ISysPostService +{ + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位列表 + */ + public List<SysPost> selectPostList(SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + public List<SysPost> selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + public SysPost selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + public List<Integer> selectPostListByUserId(Long userId); + + /** + * 校验岗位名称 + * + * @param post 岗位信息 + * @return 结果 + */ + public String checkPostNameUnique(SysPost post); + + /** + * 校验岗位编码 + * + * @param post 岗位信息 + * @return 结果 + */ + public String checkPostCodeUnique(SysPost post); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + public int deletePostById(Long postId); + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int insertPost(SysPost post); + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int updatePost(SysPost post); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysRoleService.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysRoleService.java new file mode 100644 index 0000000..1774f13 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysRoleService.java @@ -0,0 +1,115 @@ +package com.ruoyi.project.system.service; + +import java.util.List; +import java.util.Set; +import com.ruoyi.project.system.domain.SysRole; + +/** + * 角色业务层 + * + * @author ruoyi + */ +public interface ISysRoleService +{ + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + public List<SysRole> selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 权限列表 + */ + public Set<String> selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List<SysRole> selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + public List<Integer> selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + public SysRole selectRoleById(Long roleId); + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + public String checkRoleNameUnique(SysRole role); + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + public String checkRoleKeyUnique(SysRole role); + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + public void checkRoleAllowed(SysRole role); + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int insertRole(SysRole role); + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRole(SysRole role); + + /** + * 修改角色状态 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRoleStatus(SysRole role); + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int authDataScope(SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleById(Long roleId); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysUserService.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysUserService.java new file mode 100644 index 0000000..2136cc9 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysUserService.java @@ -0,0 +1,149 @@ +package com.ruoyi.project.system.service; + +import java.util.List; +import com.ruoyi.project.system.domain.SysUser; + +/** + * 用户 业务层 + * + * @author ruoyi + */ +public interface ISysUserService +{ + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List<SysUser> selectUserList(SysUser user); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public SysUser selectUserByUserName(String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public SysUser selectUserById(Long userId); + + /** + * 根据用户ID查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + public String selectUserRoleGroup(String userName); + + /** + * 根据用户ID查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + public String selectUserPostGroup(String userName); + + /** + * 校验用户名称是否唯一 + * + * @param userName 用户名称 + * @return 结果 + */ + public String checkUserNameUnique(String userName); + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public String checkPhoneUnique(SysUser user); + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public String checkEmailUnique(SysUser user); + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + public void checkUserAllowed(SysUser user); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int insertUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUser(SysUser user); + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserStatus(SysUser user); + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserProfile(SysUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + public boolean updateUserAvatar(String userName, String avatar); + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + public int resetPwd(SysUser user); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + public int resetUserPwd(String userName, String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserById(Long userId); +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysConfigServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysConfigServiceImpl.java new file mode 100644 index 0000000..de62e8d --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysConfigServiceImpl.java @@ -0,0 +1,117 @@ +package com.ruoyi.project.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.project.system.domain.SysConfig; +import com.ruoyi.project.system.mapper.SysConfigMapper; +import com.ruoyi.project.system.service.ISysConfigService; + +/** + * 参数配置 服务层实现 + * + * @author ruoyi + */ +@Service +public class SysConfigServiceImpl implements ISysConfigService +{ + @Autowired + private SysConfigMapper configMapper; + + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + @Override + public SysConfig selectConfigById(Long configId) + { + SysConfig config = new SysConfig(); + config.setConfigId(configId); + return configMapper.selectConfig(config); + } + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数key + * @return 参数键值 + */ + @Override + public Object selectConfigByKey(String configKey) + { + SysConfig config = new SysConfig(); + config.setConfigKey(configKey); + SysConfig retConfig = configMapper.selectConfig(config); + return StringUtils.isNotNull(retConfig) ? retConfig.getConfigValue() : ""; + } + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + @Override + public List<SysConfig> selectConfigList(SysConfig config) + { + return configMapper.selectConfigList(config); + } + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public int insertConfig(SysConfig config) + { + return configMapper.insertConfig(config); + } + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public int updateConfig(SysConfig config) + { + return configMapper.updateConfig(config); + } + + /** + * 删除参数配置信息 + * + * @param configId 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteConfigById(Long configId) + { + return configMapper.deleteConfigById(configId); + } + + /** + * 校验参数键名是否唯一 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public String checkConfigKeyUnique(SysConfig config) + { + Long configId = StringUtils.isNull(config.getConfigId()) ? -1L : config.getConfigId(); + SysConfig info = configMapper.checkConfigKeyUnique(config.getConfigKey()); + if (StringUtils.isNotNull(info) && info.getConfigId().longValue() != configId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDeptServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDeptServiceImpl.java new file mode 100644 index 0000000..f4e4dec --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDeptServiceImpl.java @@ -0,0 +1,289 @@ +package com.ruoyi.project.system.service.impl; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.exception.CustomException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.aspectj.lang.annotation.DataScope; +import com.ruoyi.framework.web.domain.TreeSelect; +import com.ruoyi.project.system.domain.SysDept; +import com.ruoyi.project.system.mapper.SysDeptMapper; +import com.ruoyi.project.system.service.ISysDeptService; + +/** + * 部门管理 服务实现 + * + * @author ruoyi + */ +@Service +public class SysDeptServiceImpl implements ISysDeptService +{ + @Autowired + private SysDeptMapper deptMapper; + + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + @Override + @DataScope(deptAlias = "d") + public List<SysDept> selectDeptList(SysDept dept) + { + return deptMapper.selectDeptList(dept); + } + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * @return 树结构列表 + */ + @Override + public List<SysDept> buildDeptTree(List<SysDept> depts) + { + List<SysDept> returnList = new ArrayList<SysDept>(); + for (Iterator<SysDept> iterator = depts.iterator(); iterator.hasNext();) + { + SysDept t = (SysDept) iterator.next(); + // 根据传入的某个父节点ID,遍历该父节点的所有子节点 + if (t.getParentId() == 0) + { + recursionFn(depts, t); + returnList.add(t); + } + } + if (returnList.isEmpty()) + { + returnList = depts; + } + return returnList; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + @Override + public List<TreeSelect> buildDeptTreeSelect(List<SysDept> depts) + { + List<SysDept> deptTrees = buildDeptTree(depts); + return deptTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + @Override + public List<Integer> selectDeptListByRoleId(Long roleId) + { + return deptMapper.selectDeptListByRoleId(roleId); + } + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + @Override + public SysDept selectDeptById(Long deptId) + { + return deptMapper.selectDeptById(deptId); + } + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public boolean hasChildByDeptId(Long deptId) + { + int result = deptMapper.hasChildByDeptId(deptId); + return result > 0 ? true : false; + } + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + @Override + public boolean checkDeptExistUser(Long deptId) + { + int result = deptMapper.checkDeptExistUser(deptId); + return result > 0 ? true : false; + } + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public String checkDeptNameUnique(SysDept dept) + { + Long deptId = StringUtils.isNull(dept.getDeptId()) ? -1L : dept.getDeptId(); + SysDept info = deptMapper.checkDeptNameUnique(dept.getDeptName(), dept.getParentId()); + if (StringUtils.isNotNull(info) && info.getDeptId().longValue() != deptId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public int insertDept(SysDept dept) + { + SysDept info = deptMapper.selectDeptById(dept.getParentId()); + // 如果父节点不为正常状态,则不允许新增子节点 + if (!UserConstants.DEPT_NORMAL.equals(info.getStatus())) + { + throw new CustomException("部门停用,不允许新增"); + } + dept.setAncestors(info.getAncestors() + "," + dept.getParentId()); + return deptMapper.insertDept(dept); + } + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public int updateDept(SysDept dept) + { + SysDept newParentDept = deptMapper.selectDeptById(dept.getParentId()); + SysDept oldDept = deptMapper.selectDeptById(dept.getDeptId()); + if (StringUtils.isNotNull(newParentDept) && StringUtils.isNotNull(oldDept)) + { + String newAncestors = newParentDept.getAncestors() + "," + newParentDept.getDeptId(); + String oldAncestors = oldDept.getAncestors(); + dept.setAncestors(newAncestors); + updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors); + } + int result = deptMapper.updateDept(dept); + if (UserConstants.DEPT_NORMAL.equals(dept.getStatus())) + { + // 如果该部门是启用状态,则启用该部门的所有上级部门 + updateParentDeptStatus(dept); + } + return result; + } + + /** + * 修改该部门的父级部门状态 + * + * @param dept 当前部门 + */ + private void updateParentDeptStatus(SysDept dept) + { + String updateBy = dept.getUpdateBy(); + dept = deptMapper.selectDeptById(dept.getDeptId()); + dept.setUpdateBy(updateBy); + deptMapper.updateDeptStatus(dept); + } + + /** + * 修改子元素关系 + * + * @param deptId 被修改的部门ID + * @param newAncestors 新的父ID集合 + * @param oldAncestors 旧的父ID集合 + */ + public void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors) + { + List<SysDept> children = deptMapper.selectChildrenDeptById(deptId); + for (SysDept child : children) + { + child.setAncestors(child.getAncestors().replace(oldAncestors, newAncestors)); + } + if (children.size() > 0) + { + deptMapper.updateDeptChildren(children); + } + } + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public int deleteDeptById(Long deptId) + { + return deptMapper.deleteDeptById(deptId); + } + + /** + * 递归列表 + */ + private void recursionFn(List<SysDept> list, SysDept t) + { + // 得到子节点列表 + List<SysDept> childList = getChildList(list, t); + t.setChildren(childList); + for (SysDept tChild : childList) + { + if (hasChild(list, tChild)) + { + // 判断是否有子节点 + Iterator<SysDept> it = childList.iterator(); + while (it.hasNext()) + { + SysDept n = (SysDept) it.next(); + recursionFn(list, n); + } + } + } + } + + /** + * 得到子节点列表 + */ + private List<SysDept> getChildList(List<SysDept> list, SysDept t) + { + List<SysDept> tlist = new ArrayList<SysDept>(); + Iterator<SysDept> it = list.iterator(); + while (it.hasNext()) + { + SysDept n = (SysDept) it.next(); + if (n.getParentId().longValue() == t.getDeptId().longValue()) + { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List<SysDept> list, SysDept t) + { + return getChildList(list, t).size() > 0 ? true : false; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDictDataServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDictDataServiceImpl.java new file mode 100644 index 0000000..d940743 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDictDataServiceImpl.java @@ -0,0 +1,105 @@ +package com.ruoyi.project.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.project.system.domain.SysDictData; +import com.ruoyi.project.system.mapper.SysDictDataMapper; +import com.ruoyi.project.system.service.ISysDictDataService; + +/** + * 字典 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysDictDataServiceImpl implements ISysDictDataService +{ + @Autowired + private SysDictDataMapper dictDataMapper; + + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + @Override + public List<SysDictData> selectDictDataList(SysDictData dictData) + { + return dictDataMapper.selectDictDataList(dictData); + } + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + @Override + public List<SysDictData> selectDictDataByType(String dictType) + { + return dictDataMapper.selectDictDataByType(dictType); + } + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + @Override + public String selectDictLabel(String dictType, String dictValue) + { + return dictDataMapper.selectDictLabel(dictType, dictValue); + } + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + @Override + public SysDictData selectDictDataById(Long dictCode) + { + return dictDataMapper.selectDictDataById(dictCode); + } + + /** + * 通过字典ID删除字典数据信息 + * + * @param dictCode 字典数据ID + * @return 结果 + */ + @Override + public int deleteDictDataById(Long dictCode) + { + return dictDataMapper.deleteDictDataById(dictCode); + } + + /** + * 新增保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + @Override + public int insertDictData(SysDictData dictData) + { + return dictDataMapper.insertDictData(dictData); + } + + /** + * 修改保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + @Override + public int updateDictData(SysDictData dictData) + { + return dictDataMapper.updateDictData(dictData); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDictTypeServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDictTypeServiceImpl.java new file mode 100644 index 0000000..de784de --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDictTypeServiceImpl.java @@ -0,0 +1,130 @@ +package com.ruoyi.project.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.project.system.domain.SysDictType; +import com.ruoyi.project.system.mapper.SysDictDataMapper; +import com.ruoyi.project.system.mapper.SysDictTypeMapper; +import com.ruoyi.project.system.service.ISysDictTypeService; + +/** + * 字典 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysDictTypeServiceImpl implements ISysDictTypeService +{ + @Autowired + private SysDictTypeMapper dictTypeMapper; + + @Autowired + private SysDictDataMapper dictDataMapper; + + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + @Override + public List<SysDictType> selectDictTypeList(SysDictType dictType) + { + return dictTypeMapper.selectDictTypeList(dictType); + } + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + @Override + public List<SysDictType> selectDictTypeAll() + { + return dictTypeMapper.selectDictTypeAll(); + } + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + @Override + public SysDictType selectDictTypeById(Long dictId) + { + return dictTypeMapper.selectDictTypeById(dictId); + } + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + public SysDictType selectDictTypeByType(String dictType) + { + return dictTypeMapper.selectDictTypeByType(dictType); + } + + /** + * 通过字典ID删除字典信息 + * + * @param dictId 字典ID + * @return 结果 + */ + @Override + public int deleteDictTypeById(Long dictId) + { + return dictTypeMapper.deleteDictTypeById(dictId); + } + + /** + * 新增保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + @Override + public int insertDictType(SysDictType dictType) + { + return dictTypeMapper.insertDictType(dictType); + } + + /** + * 修改保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + @Override + @Transactional + public int updateDictType(SysDictType dictType) + { + SysDictType oldDict = dictTypeMapper.selectDictTypeById(dictType.getDictId()); + dictDataMapper.updateDictDataType(oldDict.getDictType(), dictType.getDictType()); + return dictTypeMapper.updateDictType(dictType); + } + + /** + * 校验字典类型称是否唯一 + * + * @param dict 字典类型 + * @return 结果 + */ + @Override + public String checkDictTypeUnique(SysDictType dict) + { + Long dictId = StringUtils.isNull(dict.getDictId()) ? -1L : dict.getDictId(); + SysDictType dictType = dictTypeMapper.checkDictTypeUnique(dict.getDictType()); + if (StringUtils.isNotNull(dictType) && dictType.getDictId().longValue() != dictId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysMenuServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysMenuServiceImpl.java new file mode 100644 index 0000000..735faab --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,359 @@ +package com.ruoyi.project.system.service.impl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.domain.TreeSelect; +import com.ruoyi.project.system.domain.SysMenu; +import com.ruoyi.project.system.domain.vo.MetaVo; +import com.ruoyi.project.system.domain.vo.RouterVo; +import com.ruoyi.project.system.mapper.SysMenuMapper; +import com.ruoyi.project.system.mapper.SysRoleMenuMapper; +import com.ruoyi.project.system.service.ISysMenuService; + +/** + * 菜单 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysMenuServiceImpl implements ISysMenuService +{ + public static final String PREMISSION_STRING = "perms[\"{0}\"]"; + + @Autowired + private SysMenuMapper menuMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + @Override + public List<SysMenu> selectMenuList(SysMenu menu) + { + List<SysMenu> menuList = menuMapper.selectMenuList(menu); + return menuList; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set<String> selectMenuPermsByUserId(Long userId) + { + List<String> perms = menuMapper.selectMenuPermsByUserId(userId); + Set<String> permsSet = new HashSet<>(); + for (String perm : perms) + { + if (StringUtils.isNotEmpty(perm)) + { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + + /** + * 根据用户名称查询菜单 + * + * @param username 用户名称 + * @return 菜单列表 + */ + @Override + public List<SysMenu> selectMenuTreeByUserId(Long userId) + { + List<SysMenu> menus = null; + if (SecurityUtils.isAdmin(userId)) + { + menus = menuMapper.selectMenuTreeAll(); + } + else + { + menus = menuMapper.selectMenuTreeByUserId(userId); + } + return getChildPerms(menus, 0); + } + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + public List<Integer> selectMenuListByRoleId(Long roleId) + { + return menuMapper.selectMenuListByRoleId(roleId); + } + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + @Override + public List<RouterVo> buildMenus(List<SysMenu> menus) + { + List<RouterVo> routers = new LinkedList<RouterVo>(); + for (SysMenu menu : menus) + { + RouterVo router = new RouterVo(); + router.setName(menu.getMenuName()); + router.setPath(getRouterPath(menu)); + router.setComponent(StringUtils.isEmpty(menu.getComponent()) ? "Layout" : menu.getComponent()); + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon())); + router.setName(menu.getMenuName()); + List<SysMenu> cMenus = menu.getChildren(); + if (!cMenus.isEmpty() && cMenus.size() > 0 && "M".equals(menu.getMenuType())) + { + router.setAlwaysShow(true); + router.setRedirect("noRedirect"); + router.setChildren(buildMenus(cMenus)); + } + routers.add(router); + } + return routers; + } + + /** + * 构建前端所需要树结构 + * + * @param menus 菜单列表 + * @return 树结构列表 + */ + @Override + public List<SysMenu> buildMenuTree(List<SysMenu> menus) + { + List<SysMenu> returnList = new ArrayList<SysMenu>(); + for (Iterator<SysMenu> iterator = menus.iterator(); iterator.hasNext();) + { + SysMenu t = (SysMenu) iterator.next(); + // 根据传入的某个父节点ID,遍历该父节点的所有子节点 + if (t.getParentId() == 0) + { + recursionFn(menus, t); + returnList.add(t); + } + } + if (returnList.isEmpty()) + { + returnList = menus; + } + return returnList; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + @Override + public List<TreeSelect> buildMenuTreeSelect(List<SysMenu> menus) + { + List<SysMenu> menuTrees = buildMenuTree(menus); + return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + @Override + public SysMenu selectMenuById(Long menuId) + { + return menuMapper.selectMenuById(menuId); + } + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean hasChildByMenuId(Long menuId) + { + int result = menuMapper.hasChildByMenuId(menuId); + return result > 0 ? true : false; + } + + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean checkMenuExistRole(Long menuId) + { + int result = roleMenuMapper.checkMenuExistRole(menuId); + return result > 0 ? true : false; + } + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public int insertMenu(SysMenu menu) + { + return menuMapper.insertMenu(menu); + } + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public int updateMenu(SysMenu menu) + { + return menuMapper.updateMenu(menu); + } + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public int deleteMenuById(Long menuId) + { + return menuMapper.deleteMenuById(menuId); + } + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public String checkMenuNameUnique(SysMenu menu) + { + Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId(); + SysMenu info = menuMapper.checkMenuNameUnique(menu.getMenuName(), menu.getParentId()); + if (StringUtils.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 获取路由地址 + * + * @param menu 菜单信息 + * @return 路由地址 + */ + public String getRouterPath(SysMenu menu) + { + String routerPath = menu.getPath(); + // 非外链并且是一级目录 + if (0 == menu.getParentId() && "1".equals(menu.getIsFrame())) + { + routerPath = "/" + menu.getPath(); + } + return routerPath; + } + + /** + * 根据父节点的ID获取所有子节点 + * + * @param list 分类表 + * @param parentId 传入的父节点ID + * @return String + */ + public List<SysMenu> getChildPerms(List<SysMenu> list, int parentId) + { + List<SysMenu> returnList = new ArrayList<SysMenu>(); + for (Iterator<SysMenu> iterator = list.iterator(); iterator.hasNext();) + { + SysMenu t = (SysMenu) iterator.next(); + // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点 + if (t.getParentId() == parentId) + { + recursionFn(list, t); + returnList.add(t); + } + } + return returnList; + } + + /** + * 递归列表 + * + * @param list + * @param t + */ + private void recursionFn(List<SysMenu> list, SysMenu t) + { + // 得到子节点列表 + List<SysMenu> childList = getChildList(list, t); + t.setChildren(childList); + for (SysMenu tChild : childList) + { + if (hasChild(list, tChild)) + { + // 判断是否有子节点 + Iterator<SysMenu> it = childList.iterator(); + while (it.hasNext()) + { + SysMenu n = (SysMenu) it.next(); + recursionFn(list, n); + } + } + } + } + + /** + * 得到子节点列表 + */ + private List<SysMenu> getChildList(List<SysMenu> list, SysMenu t) + { + List<SysMenu> tlist = new ArrayList<SysMenu>(); + Iterator<SysMenu> it = list.iterator(); + while (it.hasNext()) + { + SysMenu n = (SysMenu) it.next(); + if (n.getParentId().longValue() == t.getMenuId().longValue()) + { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List<SysMenu> list, SysMenu t) + { + return getChildList(list, t).size() > 0 ? true : false; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java new file mode 100644 index 0000000..800f4d1 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java @@ -0,0 +1,80 @@ +package com.ruoyi.project.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.project.system.domain.SysNotice; +import com.ruoyi.project.system.mapper.SysNoticeMapper; +import com.ruoyi.project.system.service.ISysNoticeService; + +/** + * 公告 服务层实现 + * + * @author ruoyi + */ +@Service +public class SysNoticeServiceImpl implements ISysNoticeService +{ + @Autowired + private SysNoticeMapper noticeMapper; + + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + @Override + public SysNotice selectNoticeById(Long noticeId) + { + return noticeMapper.selectNoticeById(noticeId); + } + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + @Override + public List<SysNotice> selectNoticeList(SysNotice notice) + { + return noticeMapper.selectNoticeList(notice); + } + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + @Override + public int insertNotice(SysNotice notice) + { + return noticeMapper.insertNotice(notice); + } + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + @Override + public int updateNotice(SysNotice notice) + { + return noticeMapper.updateNotice(notice); + } + + /** + * 删除公告对象 + * + * @param noticeId 公告ID + * @return 结果 + */ + @Override + public int deleteNoticeById(Long noticeId) + { + return noticeMapper.deleteNoticeById(noticeId); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysPostServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysPostServiceImpl.java new file mode 100644 index 0000000..682d147 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysPostServiceImpl.java @@ -0,0 +1,141 @@ +package com.ruoyi.project.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.project.system.domain.SysPost; +import com.ruoyi.project.system.mapper.SysPostMapper; +import com.ruoyi.project.system.service.ISysPostService; + +/** + * 岗位信息 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysPostServiceImpl implements ISysPostService +{ + @Autowired + private SysPostMapper postMapper; + + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位信息集合 + */ + @Override + public List<SysPost> selectPostList(SysPost post) + { + return postMapper.selectPostList(post); + } + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + @Override + public List<SysPost> selectPostAll() + { + return postMapper.selectPostAll(); + } + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + @Override + public SysPost selectPostById(Long postId) + { + return postMapper.selectPostById(postId); + } + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + public List<Integer> selectPostListByUserId(Long userId) + { + return postMapper.selectPostListByUserId(userId); + } + + + /** + * 校验岗位名称是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public String checkPostNameUnique(SysPost post) + { + Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId(); + SysPost info = postMapper.checkPostNameUnique(post.getPostName()); + if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验岗位编码是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public String checkPostCodeUnique(SysPost post) + { + Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId(); + SysPost info = postMapper.checkPostCodeUnique(post.getPostCode()); + if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public int deletePostById(Long postId) + { + return postMapper.deletePostById(postId); + } + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public int insertPost(SysPost post) + { + return postMapper.insertPost(post); + } + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public int updatePost(SysPost post) + { + return postMapper.updatePost(post); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysRoleServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..82c458d --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,275 @@ +package com.ruoyi.project.system.service.impl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.exception.CustomException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.aspectj.lang.annotation.DataScope; +import com.ruoyi.project.system.domain.SysRole; +import com.ruoyi.project.system.domain.SysRoleDept; +import com.ruoyi.project.system.domain.SysRoleMenu; +import com.ruoyi.project.system.mapper.SysRoleDeptMapper; +import com.ruoyi.project.system.mapper.SysRoleMapper; +import com.ruoyi.project.system.mapper.SysRoleMenuMapper; +import com.ruoyi.project.system.service.ISysRoleService; + +/** + * 角色 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysRoleServiceImpl implements ISysRoleService +{ + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + @Autowired + private SysRoleDeptMapper roleDeptMapper; + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + @Override + @DataScope(deptAlias = "d") + public List<SysRole> selectRoleList(SysRole role) + { + return roleMapper.selectRoleList(role); + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set<String> selectRolePermissionByUserId(Long userId) + { + List<SysRole> perms = roleMapper.selectRolePermissionByUserId(userId); + Set<String> permsSet = new HashSet<>(); + for (SysRole perm : perms) + { + if (StringUtils.isNotNull(perm)) + { + permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(","))); + } + } + return permsSet; + } + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List<SysRole> selectRoleAll() + { + return roleMapper.selectRoleAll(); + } + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + public List<Integer> selectRoleListByUserId(Long userId) + { + return roleMapper.selectRoleListByUserId(userId); + } + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + public SysRole selectRoleById(Long roleId) + { + return roleMapper.selectRoleById(roleId); + } + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public String checkRoleNameUnique(SysRole role) + { + Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId(); + SysRole info = roleMapper.checkRoleNameUnique(role.getRoleName()); + if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public String checkRoleKeyUnique(SysRole role) + { + Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId(); + SysRole info = roleMapper.checkRoleKeyUnique(role.getRoleKey()); + if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + public void checkRoleAllowed(SysRole role) + { + if (StringUtils.isNotNull(role.getRoleId()) && role.isAdmin()) + { + throw new CustomException("不允许操作超级管理员角色"); + } + } + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int insertRole(SysRole role) + { + // 新增角色信息 + roleMapper.insertRole(role); + return insertRoleMenu(role); + } + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int updateRole(SysRole role) + { + // 修改角色信息 + roleMapper.updateRole(role); + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenuByRoleId(role.getRoleId()); + return insertRoleMenu(role); + } + + /** + * 修改角色状态 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRoleStatus(SysRole role) + { + return roleMapper.updateRole(role); + } + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int authDataScope(SysRole role) + { + // 修改角色信息 + roleMapper.updateRole(role); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDeptByRoleId(role.getRoleId()); + // 新增角色和部门信息(数据权限) + return insertRoleDept(role); + } + + /** + * 新增角色菜单信息 + * + * @param role 角色对象 + */ + public int insertRoleMenu(SysRole role) + { + int rows = 1; + // 新增用户与角色管理 + List<SysRoleMenu> list = new ArrayList<SysRoleMenu>(); + for (Long menuId : role.getMenuIds()) + { + SysRoleMenu rm = new SysRoleMenu(); + rm.setRoleId(role.getRoleId()); + rm.setMenuId(menuId); + list.add(rm); + } + if (list.size() > 0) + { + rows = roleMenuMapper.batchRoleMenu(list); + } + return rows; + } + + /** + * 新增角色部门信息(数据权限) + * + * @param role 角色对象 + */ + public int insertRoleDept(SysRole role) + { + int rows = 1; + // 新增角色与部门(数据权限)管理 + List<SysRoleDept> list = new ArrayList<SysRoleDept>(); + for (Long deptId : role.getDeptIds()) + { + SysRoleDept rd = new SysRoleDept(); + rd.setRoleId(role.getRoleId()); + rd.setDeptId(deptId); + list.add(rd); + } + if (list.size() > 0) + { + rows = roleDeptMapper.batchRoleDept(list); + } + return rows; + } + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + public int deleteRoleById(Long roleId) + { + return roleMapper.deleteRoleById(roleId); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysUserServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..0a62aca --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysUserServiceImpl.java @@ -0,0 +1,363 @@ +package com.ruoyi.project.system.service.impl; + +import java.util.ArrayList; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.exception.CustomException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.aspectj.lang.annotation.DataScope; +import com.ruoyi.project.system.domain.SysPost; +import com.ruoyi.project.system.domain.SysRole; +import com.ruoyi.project.system.domain.SysUser; +import com.ruoyi.project.system.domain.SysUserPost; +import com.ruoyi.project.system.domain.SysUserRole; +import com.ruoyi.project.system.mapper.SysPostMapper; +import com.ruoyi.project.system.mapper.SysRoleMapper; +import com.ruoyi.project.system.mapper.SysUserMapper; +import com.ruoyi.project.system.mapper.SysUserPostMapper; +import com.ruoyi.project.system.mapper.SysUserRoleMapper; +import com.ruoyi.project.system.service.ISysUserService; + +/** + * 用户 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysUserServiceImpl implements ISysUserService +{ + @Autowired + private SysUserMapper userMapper; + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysPostMapper postMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + @Autowired + private SysUserPostMapper userPostMapper; + + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List<SysUser> selectUserList(SysUser user) + { + return userMapper.selectUserList(user); + } + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + @Override + public SysUser selectUserByUserName(String userName) + { + return userMapper.selectUserByUserName(userName); + } + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + @Override + public SysUser selectUserById(Long userId) + { + return userMapper.selectUserById(userId); + } + + /** + * 查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserRoleGroup(String userName) + { + List<SysRole> list = roleMapper.selectRolesByUserName(userName); + StringBuffer idsStr = new StringBuffer(); + for (SysRole role : list) + { + idsStr.append(role.getRoleName()).append(","); + } + if (StringUtils.isNotEmpty(idsStr.toString())) + { + return idsStr.substring(0, idsStr.length() - 1); + } + return idsStr.toString(); + } + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserPostGroup(String userName) + { + List<SysPost> list = postMapper.selectPostsByUserName(userName); + StringBuffer idsStr = new StringBuffer(); + for (SysPost post : list) + { + idsStr.append(post.getPostName()).append(","); + } + if (StringUtils.isNotEmpty(idsStr.toString())) + { + return idsStr.substring(0, idsStr.length() - 1); + } + return idsStr.toString(); + } + + /** + * 校验用户名称是否唯一 + * + * @param userName 用户名称 + * @return 结果 + */ + @Override + public String checkUserNameUnique(String userName) + { + int count = userMapper.checkUserNameUnique(userName); + if (count > 0) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public String checkPhoneUnique(SysUser user) + { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkPhoneUnique(user.getPhonenumber()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public String checkEmailUnique(SysUser user) + { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkEmailUnique(user.getEmail()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + public void checkUserAllowed(SysUser user) + { + if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin()) + { + throw new CustomException("不允许操作超级管理员用户"); + } + } + + /** + * 新增保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional + public int insertUser(SysUser user) + { + // 新增用户信息 + int rows = userMapper.insertUser(user); + // 新增用户岗位关联 + insertUserPost(user); + // 新增用户与角色管理 + insertUserRole(user); + return rows; + } + + /** + * 修改保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional + public int updateUser(SysUser user) + { + Long userId = user.getUserId(); + // 删除用户与角色关联 + userRoleMapper.deleteUserRoleByUserId(userId); + // 新增用户与角色管理 + insertUserRole(user); + // 删除用户与岗位关联 + userPostMapper.deleteUserPostByUserId(userId); + // 新增用户与岗位管理 + insertUserPost(user); + return userMapper.updateUser(user); + } + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserStatus(SysUser user) + { + return userMapper.updateUser(user); + } + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserProfile(SysUser user) + { + return userMapper.updateUser(user); + } + + /** + * 修改用户头像 + * + * @param userId 用户ID + * @param avatar 头像地址 + * @return 结果 + */ + public boolean updateUserAvatar(String userName, String avatar) + { + return userMapper.updateUserAvatar(userName, avatar) > 0; + } + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int resetPwd(SysUser user) + { + return userMapper.updateUser(user); + } + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + @Override + public int resetUserPwd(String userName, String password) + { + return userMapper.resetUserPwd(userName, password); + } + + /** + * 新增用户角色信息 + * + * @param user 用户对象 + */ + public void insertUserRole(SysUser user) + { + Long[] roles = user.getRoleIds(); + if (StringUtils.isNotNull(roles)) + { + // 新增用户与角色管理 + List<SysUserRole> list = new ArrayList<SysUserRole>(); + for (Long roleId : roles) + { + SysUserRole ur = new SysUserRole(); + ur.setUserId(user.getUserId()); + ur.setRoleId(roleId); + list.add(ur); + } + if (list.size() > 0) + { + userRoleMapper.batchUserRole(list); + } + } + } + + /** + * 新增用户岗位信息 + * + * @param user 用户对象 + */ + public void insertUserPost(SysUser user) + { + Long[] posts = user.getPostIds(); + if (StringUtils.isNotNull(posts)) + { + // 新增用户与岗位管理 + List<SysUserPost> list = new ArrayList<SysUserPost>(); + for (Long postId : posts) + { + SysUserPost up = new SysUserPost(); + up.setUserId(user.getUserId()); + up.setPostId(postId); + list.add(up); + } + if (list.size() > 0) + { + userPostMapper.batchUserPost(list); + } + } + } + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + @Override + public int deleteUserById(Long userId) + { + // 删除用户与角色关联 + userRoleMapper.deleteUserRoleByUserId(userId); + // 删除用户与岗位表 + userPostMapper.deleteUserPostByUserId(userId); + return userMapper.deleteUserById(userId); + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/project/tool/swagger/TestController.java b/ruoyi/src/main/java/com/ruoyi/project/tool/swagger/TestController.java new file mode 100644 index 0000000..17f4c31 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/project/tool/swagger/TestController.java @@ -0,0 +1,175 @@ +package com.ruoyi.project.tool.swagger; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.springframework.web.bind.annotation.DeleteMapping; +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.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiOperation; + +/** + * swagger 用户测试方法 + * + * @author ruoyi + */ +@Api("用户信息管理") +@RestController +@RequestMapping("/test/user") +public class TestController extends BaseController +{ + private final static Map<Integer, UserEntity> users = new LinkedHashMap<Integer, UserEntity>(); + { + users.put(1, new UserEntity(1, "admin", "admin123", "15888888888")); + users.put(2, new UserEntity(2, "ry", "admin123", "15666666666")); + } + + @ApiOperation("获取用户列表") + @GetMapping("/list") + public AjaxResult userList() + { + List<UserEntity> userList = new ArrayList<UserEntity>(users.values()); + return AjaxResult.success(userList); + } + + @ApiOperation("获取用户详细") + @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path") + @GetMapping("/{userId}") + public AjaxResult getUser(@PathVariable Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + return AjaxResult.success(users.get(userId)); + } + else + { + return AjaxResult.error("用户不存在"); + } + } + + @ApiOperation("新增用户") + @ApiImplicitParam(name = "userEntity", value = "新增用户信息", dataType = "UserEntity") + @PostMapping("/save") + public AjaxResult save(UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return AjaxResult.error("用户ID不能为空"); + } + return AjaxResult.success(users.put(user.getUserId(), user)); + } + + @ApiOperation("更新用户") + @ApiImplicitParam(name = "userEntity", value = "新增用户信息", dataType = "UserEntity") + @PutMapping("/update") + public AjaxResult update(UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return AjaxResult.error("用户ID不能为空"); + } + if (users.isEmpty() || !users.containsKey(user.getUserId())) + { + return AjaxResult.error("用户不存在"); + } + users.remove(user.getUserId()); + return AjaxResult.success(users.put(user.getUserId(), user)); + } + + @ApiOperation("删除用户信息") + @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path") + @DeleteMapping("/{userId}") + public AjaxResult delete(@PathVariable Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + users.remove(userId); + return AjaxResult.success(); + } + else + { + return AjaxResult.error("用户不存在"); + } + } +} + +@ApiModel("用户实体") +class UserEntity +{ + @ApiModelProperty("用户ID") + private Integer userId; + + @ApiModelProperty("用户名称") + private String username; + + @ApiModelProperty("用户密码") + private String password; + + @ApiModelProperty("用户手机") + private String mobile; + + public UserEntity() + { + + } + + public UserEntity(Integer userId, String username, String password, String mobile) + { + this.userId = userId; + this.username = username; + this.password = password; + this.mobile = mobile; + } + + public Integer getUserId() + { + return userId; + } + + public void setUserId(Integer userId) + { + this.userId = userId; + } + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getMobile() + { + return mobile; + } + + public void setMobile(String mobile) + { + this.mobile = mobile; + } +} diff --git a/ruoyi/src/main/resources/application-druid.yml b/ruoyi/src/main/resources/application-druid.yml new file mode 100644 index 0000000..cdfb339 --- /dev/null +++ b/ruoyi/src/main/resources/application-druid.yml @@ -0,0 +1,57 @@ +# 数据源配置 +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + druid: + # 主库数据源 + master: + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: root + password: password + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: + login-password: + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true \ No newline at end of file diff --git a/ruoyi/src/main/resources/application.yml b/ruoyi/src/main/resources/application.yml new file mode 100644 index 0000000..4146e35 --- /dev/null +++ b/ruoyi/src/main/resources/application.yml @@ -0,0 +1,109 @@ +# 项目相关配置 +ruoyi: + # 名称 + name: RuoYi + # 版本 + version: 1.0.0 + # 版权年份 + copyrightYear: 2019 + # 实例演示开关 + demoEnabled: true + # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) + profile: D:/ruoyi/uploadPath + # 获取ip地址开关 + addressEnabled: true + +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8080 + port: 8080 + servlet: + # 应用的访问路径 + context-path: / + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # tomcat最大线程数,默认为200 + max-threads: 800 + # Tomcat启动初始化的线程数,默认值25 + min-spare-threads: 30 + + +# 日志配置 +logging: + level: + com.ruoyi: debug + org.springframework: warn + +# Spring配置 +spring: + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + profiles: + active: druid + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 10MB + # 设置总上传的文件大小 + max-request-size: 20MB + # 服务模块 + devtools: + restart: + # 热部署开关 + enabled: true + # redis 配置 + redis: + # 地址 + host: localhost + # 端口,默认为6379 + port: 6379 + # 连接超时时间 + timeout: 10s + lettuce: + pool: + # 连接池中的最小空闲连接 + min-idle: 0 + # 连接池中的最大空闲连接 + max-idle: 8 + # 连接池的最大数据库连接数 + max-active: 8 + # #连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + +# token配置 +token: + # 令牌自定义标识 + header: Authorization + # 令牌秘钥 + secret: abcdefghijklmnopqrstuvwxyz + # 令牌有效期(默认30分钟) + expireTime: 30 + +# MyBatis配置 +mybatis: + # 搜索指定包别名 + typeAliasesPackage: com.ruoyi.project.**.domain + # 配置mapper的扫描,找到所有的mapper.xml映射文件 + mapperLocations: classpath*:mybatis/**/*Mapper.xml + # 加载全局的配置文件 + configLocation: classpath:mybatis/mybatis-config.xml + +# PageHelper分页插件 +pagehelper: + helperDialect: mysql + reasonable: true + supportMethodsArguments: true + params: count=countSql + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludes: /system/notice/* + # 匹配链接 + urlPatterns: /system/*,/monitor/*,/tool/* \ No newline at end of file diff --git a/ruoyi/src/main/resources/banner.txt b/ruoyi/src/main/resources/banner.txt new file mode 100644 index 0000000..9466259 --- /dev/null +++ b/ruoyi/src/main/resources/banner.txt @@ -0,0 +1,24 @@ +Application Version: ${ruoyi.version} +Spring Boot Version: ${spring-boot.version} +//////////////////////////////////////////////////////////////////// +// _ooOoo_ // +// o8888888o // +// 88" . "88 // +// (| ^_^ |) // +// O\ = /O // +// ____/`---'\____ // +// .' \\| |// `. // +// / \\||| : |||// \ // +// / _||||| -:- |||||- \ // +// | | \\\ - /// | | // +// | \_| ''\---/'' | | // +// \ .-\__ `-` ___/-. / // +// ___`. .' /--.--\ `. . ___ // +// ."" '< `.___\_<|>_/___.' >'"". // +// | | : `- \`.;`\ _ /`;.`/ - ` : | | // +// \ \ `-. \_ __\ /__ _/ .-` / / // +// ========`-.____`-.___\_____/___.-`____.-'======== // +// `=---=' // +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // +// 佛祖保佑 永不宕机 永无BUG // +//////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/ruoyi/src/main/resources/i18n/messages.properties b/ruoyi/src/main/resources/i18n/messages.properties new file mode 100644 index 0000000..4baeb24 --- /dev/null +++ b/ruoyi/src/main/resources/i18n/messages.properties @@ -0,0 +1,36 @@ +#错误消息 +not.null=* 必须填写 +user.jcaptcha.error=验证码错误 +user.jcaptcha.expire=验证码已失效 +user.not.exists=用户不存在/密码错误 +user.password.not.match=用户不存在/密码错误 +user.password.retry.limit.count=密码输入错误{0}次 +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定10分钟 +user.password.delete=对不起,您的账号已被删除 +user.blocked=用户已封禁,请联系管理员 +role.blocked=角色已封禁,请联系管理员 +user.logout.success=退出成功 + +length.not.valid=长度必须在{min}到{max}个字符之间 + +user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 +user.password.not.valid=* 5-50个字符 + +user.email.not.valid=邮箱格式错误 +user.mobile.phone.number.not.valid=手机号格式错误 +user.login.success=登录成功 +user.notfound=请重新登录 +user.forcelogout=管理员强制退出,请重新登录 +user.unknown.error=未知错误,请重新登录 + +##文件上传消息 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB! +upload.filename.exceed.length=上传的文件名最长{0}个字符 + +##权限 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] diff --git a/ruoyi/src/main/resources/logback.xml b/ruoyi/src/main/resources/logback.xml new file mode 100644 index 0000000..d69a572 --- /dev/null +++ b/ruoyi/src/main/resources/logback.xml @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<configuration> + <!-- 日志存放路径 --> + <property name="log.path" value="/home/ruoyi/logs" /> + <!-- 日志输出格式 --> + <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" /> + + <!-- 控制台输出 --> + <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> + <encoder> + <pattern>${log.pattern}</pattern> + </encoder> + </appender> + + <!-- 系统日志输出 --> + <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${log.path}/sys-info.log</file> + <!-- 循环政策:基于时间创建日志文件 --> + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <!-- 日志文件名格式 --> + <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern> + <!-- 日志最大的历史 60天 --> + <maxHistory>60</maxHistory> + </rollingPolicy> + <encoder> + <pattern>${log.pattern}</pattern> + </encoder> + <filter class="ch.qos.logback.classic.filter.LevelFilter"> + <!-- 过滤的级别 --> + <level>INFO</level> + <!-- 匹配时的操作:接收(记录) --> + <onMatch>ACCEPT</onMatch> + <!-- 不匹配时的操作:拒绝(不记录) --> + <onMismatch>DENY</onMismatch> + </filter> + </appender> + + <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${log.path}/sys-error.log</file> + <!-- 循环政策:基于时间创建日志文件 --> + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <!-- 日志文件名格式 --> + <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern> + <!-- 日志最大的历史 60天 --> + <maxHistory>60</maxHistory> + </rollingPolicy> + <encoder> + <pattern>${log.pattern}</pattern> + </encoder> + <filter class="ch.qos.logback.classic.filter.LevelFilter"> + <!-- 过滤的级别 --> + <level>ERROR</level> + <!-- 匹配时的操作:接收(记录) --> + <onMatch>ACCEPT</onMatch> + <!-- 不匹配时的操作:拒绝(不记录) --> + <onMismatch>DENY</onMismatch> + </filter> + </appender> + + <!-- 用户访问日志输出 --> + <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${log.path}/sys-user.log</file> + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <!-- 按天回滚 daily --> + <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern> + <!-- 日志最大的历史 60天 --> + <maxHistory>60</maxHistory> + </rollingPolicy> + <encoder> + <pattern>${log.pattern}</pattern> + </encoder> + </appender> + + <!-- 系统模块日志级别控制 --> + <logger name="com.ruoyi" level="info" /> + <!-- Spring日志级别控制 --> + <logger name="org.springframework" level="warn" /> + + <root level="info"> + <appender-ref ref="console" /> + </root> + + <!--系统操作日志--> + <root level="info"> + <appender-ref ref="file_info" /> + <appender-ref ref="file_error" /> + </root> + + <!--系统用户操作日志--> + <logger name="sys-user" level="info"> + <appender-ref ref="sys-user"/> + </logger> +</configuration> \ No newline at end of file diff --git a/ruoyi/src/main/resources/mybatis/monitor/SysLogininforMapper.xml b/ruoyi/src/main/resources/mybatis/monitor/SysLogininforMapper.xml new file mode 100644 index 0000000..0301a3d --- /dev/null +++ b/ruoyi/src/main/resources/mybatis/monitor/SysLogininforMapper.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.project.monitor.mapper.SysLogininforMapper"> + + <resultMap type="SysLogininfor" id="SysLogininforResult"> + <id property="infoId" column="info_id" /> + <result property="userName" column="user_name" /> + <result property="status" column="status" /> + <result property="ipaddr" column="ipaddr" /> + <result property="loginLocation" column="login_location" /> + <result property="browser" column="browser" /> + <result property="os" column="os" /> + <result property="msg" column="msg" /> + <result property="loginTime" column="login_time" /> + </resultMap> + + <insert id="insertLogininfor" parameterType="SysLogininfor"> + insert into sys_logininfor (user_name, status, ipaddr, login_location, browser, os, msg, login_time) + values (#{userName}, #{status}, #{ipaddr}, #{loginLocation}, #{browser}, #{os}, #{msg}, sysdate()) + </insert> + + <select id="selectLogininforList" parameterType="SysLogininfor" resultMap="SysLogininforResult"> + select info_id, user_name, ipaddr, login_location, browser, os, status, msg, login_time from sys_logininfor + <where> + <if test="ipaddr != null and ipaddr != ''"> + AND ipaddr like concat('%', #{ipaddr}, '%') + </if> + <if test="status != null and status != ''"> + AND status = #{status} + </if> + <if test="userName != null and userName != ''"> + AND user_name like concat('%', #{userName}, '%') + </if> + <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 --> + and date_format(login_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d') + </if> + <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 --> + and date_format(login_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') + </if> + </where> + </select> + + <delete id="deleteLogininforByIds" parameterType="String"> + delete from sys_logininfor where info_id in + <foreach collection="array" item="infoId" open="(" separator="," close=")"> + #{infoId} + </foreach> + </delete> + + <update id="cleanLogininfor"> + truncate table sys_logininfor + </update> + +</mapper> \ No newline at end of file diff --git a/ruoyi/src/main/resources/mybatis/monitor/SysOperLogMapper.xml b/ruoyi/src/main/resources/mybatis/monitor/SysOperLogMapper.xml new file mode 100644 index 0000000..6517828 --- /dev/null +++ b/ruoyi/src/main/resources/mybatis/monitor/SysOperLogMapper.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.project.monitor.mapper.SysOperLogMapper"> + + <resultMap type="SysOperLog" id="SysOperLogResult"> + <id property="operId" column="oper_id" /> + <result property="title" column="title" /> + <result property="businessType" column="business_type" /> + <result property="method" column="method" /> + <result property="requestMethod" column="request_method" /> + <result property="operatorType" column="operator_type" /> + <result property="operName" column="oper_name" /> + <result property="deptName" column="dept_name" /> + <result property="operUrl" column="oper_url" /> + <result property="operIp" column="oper_ip" /> + <result property="operLocation" column="oper_location" /> + <result property="operParam" column="oper_param" /> + <result property="jsonResult" column="json_result" /> + <result property="status" column="status" /> + <result property="errorMsg" column="error_msg" /> + <result property="operTime" column="oper_time" /> + </resultMap> + + <sql id="selectOperLogVo"> + select oper_id, title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, oper_time + from sys_oper_log + </sql> + + <insert id="insertOperlog" parameterType="SysOperLog"> + insert into sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, oper_time) + values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, #{operUrl}, #{operIp}, #{operLocation}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, sysdate()) + </insert> + + <select id="selectOperLogList" parameterType="SysOperLog" resultMap="SysOperLogResult"> + <include refid="selectOperLogVo"/> + <where> + <if test="title != null and title != ''"> + AND title like concat('%', #{title}, '%') + </if> + <if test="businessType != null"> + AND business_type = #{businessType} + </if> + <if test="businessTypes != null and businessTypes.length > 0"> + AND business_type in + <foreach collection="businessTypes" item="businessType" open="(" separator="," close=")"> + #{businessType} + </foreach> + </if> + <if test="status != null"> + AND status = #{status} + </if> + <if test="operName != null and operName != ''"> + AND oper_name like concat('%', #{operName}, '%') + </if> + <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 --> + and date_format(oper_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d') + </if> + <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 --> + and date_format(oper_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') + </if> + </where> + </select> + + <delete id="deleteOperLogByIds" parameterType="String"> + delete from sys_oper_log where oper_id in + <foreach collection="array" item="operId" open="(" separator="," close=")"> + #{operId} + </foreach> + </delete> + + <select id="selectOperLogById" parameterType="Long" resultMap="SysOperLogResult"> + <include refid="selectOperLogVo"/> + where oper_id = #{operId} + </select> + + <update id="cleanOperLog"> + truncate table sys_oper_log + </update> + +</mapper> \ No newline at end of file diff --git a/ruoyi/src/main/resources/mybatis/mybatis-config.xml b/ruoyi/src/main/resources/mybatis/mybatis-config.xml new file mode 100644 index 0000000..c7c36e3 --- /dev/null +++ b/ruoyi/src/main/resources/mybatis/mybatis-config.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE configuration +PUBLIC "-//mybatis.org//DTD Config 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-config.dtd"> +<configuration> + + <settings> + <setting name="cacheEnabled" value="true" /> <!-- 全局映射器启用缓存 --> + <setting name="useGeneratedKeys" value="true" /> <!-- 允许 JDBC 支持自动生成主键 --> + <setting name="defaultExecutorType" value="REUSE" /> <!-- 配置默认的执行器 --> + <setting name="logImpl" value="SLF4J" /> <!-- 指定 MyBatis 所用日志的具体实现 --> + <!-- <setting name="mapUnderscoreToCamelCase" value="true"/> 驼峰式命名 --> + </settings> + +</configuration> diff --git a/ruoyi/src/main/resources/mybatis/system/SysConfigMapper.xml b/ruoyi/src/main/resources/mybatis/system/SysConfigMapper.xml new file mode 100644 index 0000000..2189371 --- /dev/null +++ b/ruoyi/src/main/resources/mybatis/system/SysConfigMapper.xml @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.project.system.mapper.SysConfigMapper"> + + <resultMap type="SysConfig" id="SysConfigResult"> + <id property="configId" column="config_id" /> + <result property="configName" column="config_name" /> + <result property="configKey" column="config_key" /> + <result property="configValue" column="config_value" /> + <result property="configType" column="config_type" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + </resultMap> + + <sql id="selectConfigVo"> + select config_id, config_name, config_key, config_value, config_type, create_by, create_time, update_by, update_time, remark + from sys_config + </sql> + + <!-- 查询条件 --> + <sql id="sqlwhereSearch"> + <where> + <if test="configId !=null"> + and config_id = #{configId} + </if> + <if test="configKey !=null and configKey != ''"> + and config_key = #{configKey} + </if> + </where> + </sql> + + <select id="selectConfig" parameterType="SysConfig" resultMap="SysConfigResult"> + <include refid="selectConfigVo"/> + <include refid="sqlwhereSearch"/> + </select> + + <select id="selectConfigList" parameterType="SysConfig" resultMap="SysConfigResult"> + <include refid="selectConfigVo"/> + <where> + <if test="configName != null and configName != ''"> + AND config_name like concat('%', #{configName}, '%') + </if> + <if test="configType != null and configType != ''"> + AND config_type = #{configType} + </if> + <if test="configKey != null and configKey != ''"> + AND config_key like concat('%', #{configKey}, '%') + </if> + <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 --> + and date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d') + </if> + <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 --> + and date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') + </if> + </where> + </select> + + <select id="checkConfigKeyUnique" parameterType="String" resultMap="SysConfigResult"> + <include refid="selectConfigVo"/> + where config_key = #{configKey} + </select> + + <insert id="insertConfig" parameterType="SysConfig"> + insert into sys_config ( + <if test="configName != null and configName != '' ">config_name,</if> + <if test="configKey != null and configKey != '' ">config_key,</if> + <if test="configValue != null and configValue != '' ">config_value,</if> + <if test="configType != null and configType != '' ">config_type,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + <if test="remark != null and remark != ''">remark,</if> + create_time + )values( + <if test="configName != null and configName != ''">#{configName},</if> + <if test="configKey != null and configKey != ''">#{configKey},</if> + <if test="configValue != null and configValue != ''">#{configValue},</if> + <if test="configType != null and configType != ''">#{configType},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + <if test="remark != null and remark != ''">#{remark},</if> + sysdate() + ) + </insert> + + <update id="updateConfig" parameterType="SysConfig"> + update sys_config + <set> + <if test="configName != null and configName != ''">config_name = #{configName},</if> + <if test="configKey != null and configKey != ''">config_key = #{configKey},</if> + <if test="configValue != null and configValue != ''">config_value = #{configValue},</if> + <if test="configType != null and configType != ''">config_type = #{configType},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + <if test="remark != null">remark = #{remark},</if> + update_time = sysdate() + </set> + where config_id = #{configId} + </update> + + <delete id="deleteConfigById" parameterType="Long"> + delete from sys_config where config_id = #{configId} + </delete> + +</mapper> \ No newline at end of file diff --git a/ruoyi/src/main/resources/mybatis/system/SysDeptMapper.xml b/ruoyi/src/main/resources/mybatis/system/SysDeptMapper.xml new file mode 100644 index 0000000..247bb69 --- /dev/null +++ b/ruoyi/src/main/resources/mybatis/system/SysDeptMapper.xml @@ -0,0 +1,151 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.project.system.mapper.SysDeptMapper"> + + <resultMap type="SysDept" id="SysDeptResult"> + <id property="deptId" column="dept_id" /> + <result property="parentId" column="parent_id" /> + <result property="ancestors" column="ancestors" /> + <result property="deptName" column="dept_name" /> + <result property="orderNum" column="order_num" /> + <result property="leader" column="leader" /> + <result property="phone" column="phone" /> + <result property="email" column="email" /> + <result property="status" column="status" /> + <result property="delFlag" column="del_flag" /> + <result property="parentName" column="parent_name" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + </resultMap> + + <sql id="selectDeptVo"> + select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time + from sys_dept d + </sql> + + <select id="selectDeptList" parameterType="SysDept" resultMap="SysDeptResult"> + <include refid="selectDeptVo"/> + where d.del_flag = '0' + <if test="parentId != null and parentId != 0"> + AND parent_id = #{parentId} + </if> + <if test="deptName != null and deptName != ''"> + AND dept_name like concat('%', #{deptName}, '%') + </if> + <if test="status != null and status != ''"> + AND status = #{status} + </if> + <!-- 数据范围过滤 --> + ${dataScope} + order by d.parent_id, d.order_num + </select> + + <select id="selectDeptListByRoleId" parameterType="Long" resultType="Integer"> + select d.dept_id, d.parent_id + from sys_dept d + left join sys_role_dept rd on d.dept_id = rd.dept_id + where rd.role_id = #{roleId} + and d.dept_id not in (select d.parent_id from sys_dept d inner join sys_role_dept rd on d.dept_id = rd.dept_id and rd.role_id = #{roleId}) + order by d.parent_id, d.order_num + </select> + + <select id="selectDeptById" parameterType="Long" resultMap="SysDeptResult"> + <include refid="selectDeptVo"/> + where dept_id = #{deptId} + </select> + + <select id="checkDeptExistUser" parameterType="Long" resultType="int"> + select count(1) from sys_user where dept_id = #{deptId} and del_flag = '0' + </select> + + <select id="hasChildByDeptId" parameterType="Long" resultType="int"> + select count(1) from sys_dept + where del_flag = '0' and parent_id = #{deptId} + </select> + + <select id="selectChildrenDeptById" parameterType="Long" resultMap="SysDeptResult"> + select * from sys_dept where find_in_set(#{deptId}, ancestors) + </select> + + <select id="checkDeptNameUnique" resultMap="SysDeptResult"> + <include refid="selectDeptVo"/> + where dept_name=#{deptName} and parent_id = #{parentId} + </select> + + <insert id="insertDept" parameterType="SysDept"> + insert into sys_dept( + <if test="deptId != null and deptId != 0">dept_id,</if> + <if test="parentId != null and parentId != 0">parent_id,</if> + <if test="deptName != null and deptName != ''">dept_name,</if> + <if test="ancestors != null and ancestors != ''">ancestors,</if> + <if test="orderNum != null and orderNum != ''">order_num,</if> + <if test="leader != null and leader != ''">leader,</if> + <if test="phone != null and phone != ''">phone,</if> + <if test="email != null and email != ''">email,</if> + <if test="status != null">status,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + create_time + )values( + <if test="deptId != null and deptId != 0">#{deptId},</if> + <if test="parentId != null and parentId != 0">#{parentId},</if> + <if test="deptName != null and deptName != ''">#{deptName},</if> + <if test="ancestors != null and ancestors != ''">#{ancestors},</if> + <if test="orderNum != null and orderNum != ''">#{orderNum},</if> + <if test="leader != null and leader != ''">#{leader},</if> + <if test="phone != null and phone != ''">#{phone},</if> + <if test="email != null and email != ''">#{email},</if> + <if test="status != null">#{status},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + sysdate() + ) + </insert> + + <update id="updateDept" parameterType="SysDept"> + update sys_dept + <set> + <if test="parentId != null and parentId != 0">parent_id = #{parentId},</if> + <if test="deptName != null and deptName != ''">dept_name = #{deptName},</if> + <if test="ancestors != null and ancestors != ''">ancestors = #{ancestors},</if> + <if test="orderNum != null and orderNum != ''">order_num = #{orderNum},</if> + <if test="leader != null">leader = #{leader},</if> + <if test="phone != null">phone = #{phone},</if> + <if test="email != null">email = #{email},</if> + <if test="status != null and status != ''">status = #{status},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + update_time = sysdate() + </set> + where dept_id = #{deptId} + </update> + + <update id="updateDeptChildren" parameterType="java.util.List"> + update sys_dept set ancestors = + <foreach collection="depts" item="item" index="index" + separator=" " open="case dept_id" close="end"> + when #{item.deptId} then #{item.ancestors} + </foreach> + where dept_id in + <foreach collection="depts" item="item" index="index" + separator="," open="(" close=")"> + #{item.deptId} + </foreach> + </update> + + <update id="updateDeptStatus" parameterType="SysDept"> + update sys_dept + <set> + <if test="status != null and status != ''">status = #{status},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + update_time = sysdate() + </set> + where dept_id in (${ancestors}) + </update> + + <delete id="deleteDeptById" parameterType="Long"> + update sys_dept set del_flag = '2' where dept_id = #{deptId} + </delete> + +</mapper> \ No newline at end of file diff --git a/ruoyi/src/main/resources/mybatis/system/SysDictDataMapper.xml b/ruoyi/src/main/resources/mybatis/system/SysDictDataMapper.xml new file mode 100644 index 0000000..57e53c7 --- /dev/null +++ b/ruoyi/src/main/resources/mybatis/system/SysDictDataMapper.xml @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.project.system.mapper.SysDictDataMapper"> + + <resultMap type="SysDictData" id="SysDictDataResult"> + <id property="dictCode" column="dict_code" /> + <result property="dictSort" column="dict_sort" /> + <result property="dictLabel" column="dict_label" /> + <result property="dictValue" column="dict_value" /> + <result property="dictType" column="dict_type" /> + <result property="cssClass" column="css_class" /> + <result property="listClass" column="list_class" /> + <result property="isDefault" column="is_default" /> + <result property="status" column="status" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + </resultMap> + + <sql id="selectDictDataVo"> + select dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark + from sys_dict_data + </sql> + + <select id="selectDictDataList" parameterType="SysDictData" resultMap="SysDictDataResult"> + <include refid="selectDictDataVo"/> + <where> + <if test="dictType != null and dictType != ''"> + AND dict_type = #{dictType} + </if> + <if test="dictLabel != null and dictLabel != ''"> + AND dict_label like concat('%', #{dictLabel}, '%') + </if> + <if test="status != null and status != ''"> + AND status = #{status} + </if> + </where> + </select> + + <select id="selectDictDataByType" parameterType="SysDictData" resultMap="SysDictDataResult"> + <include refid="selectDictDataVo"/> + where status = '0' and dict_type = #{dictType} order by dict_sort asc + </select> + + <select id="selectDictLabel" resultType="String"> + select dict_label from sys_dict_data + where dict_type = #{dictType} and dict_value = #{dictValue} + </select> + + <select id="selectDictDataById" parameterType="Long" resultMap="SysDictDataResult"> + <include refid="selectDictDataVo"/> + where dict_code = #{dictCode} + </select> + + <select id="countDictDataByType" resultType="Integer"> + select count(1) from sys_dict_data where dict_type=#{dictType} + </select> + + <delete id="deleteDictDataById" parameterType="Long"> + delete from sys_dict_data where dict_code = #{dictCode} + </delete> + + <update id="updateDictData" parameterType="SysDictData"> + update sys_dict_data + <set> + <if test="dictSort != null and dictSort != ''">dict_sort = #{dictSort},</if> + <if test="dictLabel != null and dictLabel != ''">dict_label = #{dictLabel},</if> + <if test="dictValue != null and dictValue != ''">dict_value = #{dictValue},</if> + <if test="dictType != null and dictType != ''">dict_type = #{dictType},</if> + <if test="cssClass != null">css_class = #{cssClass},</if> + <if test="listClass != null">list_class = #{listClass},</if> + <if test="isDefault != null and isDefault != ''">is_default = #{isDefault},</if> + <if test="status != null">status = #{status},</if> + <if test="remark != null">remark = #{remark},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + update_time = sysdate() + </set> + where dict_code = #{dictCode} + </update> + + <update id="updateDictDataType" parameterType="String"> + update sys_dict_data set dict_type = #{newDictType} where dict_type = #{oldDictType} + </update> + + <insert id="insertDictData" parameterType="SysDictData"> + insert into sys_dict_data( + <if test="dictSort != null and dictSort != ''">dict_sort,</if> + <if test="dictLabel != null and dictLabel != ''">dict_label,</if> + <if test="dictValue != null and dictValue != ''">dict_value,</if> + <if test="dictType != null and dictType != ''">dict_type,</if> + <if test="cssClass != null and cssClass != ''">css_class,</if> + <if test="listClass != null and listClass != ''">list_class,</if> + <if test="isDefault != null and isDefault != ''">is_default,</if> + <if test="status != null">status,</if> + <if test="remark != null and remark != ''">remark,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + create_time + )values( + <if test="dictSort != null and dictSort != ''">#{dictSort},</if> + <if test="dictLabel != null and dictLabel != ''">#{dictLabel},</if> + <if test="dictValue != null and dictValue != ''">#{dictValue},</if> + <if test="dictType != null and dictType != ''">#{dictType},</if> + <if test="cssClass != null and cssClass != ''">#{cssClass},</if> + <if test="listClass != null and listClass != ''">#{listClass},</if> + <if test="isDefault != null and isDefault != ''">#{isDefault},</if> + <if test="status != null">#{status},</if> + <if test="remark != null and remark != ''">#{remark},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + sysdate() + ) + </insert> + +</mapper> \ No newline at end of file diff --git a/ruoyi/src/main/resources/mybatis/system/SysDictTypeMapper.xml b/ruoyi/src/main/resources/mybatis/system/SysDictTypeMapper.xml new file mode 100644 index 0000000..0a8b26c --- /dev/null +++ b/ruoyi/src/main/resources/mybatis/system/SysDictTypeMapper.xml @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.project.system.mapper.SysDictTypeMapper"> + + <resultMap type="SysDictType" id="SysDictTypeResult"> + <id property="dictId" column="dict_id" /> + <result property="dictName" column="dict_name" /> + <result property="dictType" column="dict_type" /> + <result property="status" column="status" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + </resultMap> + + <sql id="selectDictTypeVo"> + select dict_id, dict_name, dict_type, status, create_by, create_time, remark + from sys_dict_type + </sql> + + <select id="selectDictTypeList" parameterType="SysDictType" resultMap="SysDictTypeResult"> + <include refid="selectDictTypeVo"/> + <where> + <if test="dictName != null and dictName != ''"> + AND dict_name like concat('%', #{dictName}, '%') + </if> + <if test="status != null and status != ''"> + AND status = #{status} + </if> + <if test="dictType != null and dictType != ''"> + AND dict_type like concat('%', #{dictType}, '%') + </if> + <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 --> + and date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d') + </if> + <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 --> + and date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') + </if> + </where> + </select> + + <select id="selectDictTypeAll" resultMap="SysDictTypeResult"> + <include refid="selectDictTypeVo"/> + </select> + + <select id="selectDictTypeById" parameterType="Long" resultMap="SysDictTypeResult"> + <include refid="selectDictTypeVo"/> + where dict_id = #{dictId} + </select> + + <select id="selectDictTypeByType" parameterType="String" resultMap="SysDictTypeResult"> + <include refid="selectDictTypeVo"/> + where dict_type = #{dictType} + </select> + + <select id="checkDictTypeUnique" parameterType="String" resultMap="SysDictTypeResult"> + <include refid="selectDictTypeVo"/> + where dict_type = #{dictType} + </select> + + <delete id="deleteDictTypeById" parameterType="Long"> + delete from sys_dict_type where dict_id = #{dictId} + </delete> + + <update id="updateDictType" parameterType="SysDictType"> + update sys_dict_type + <set> + <if test="dictName != null and dictName != ''">dict_name = #{dictName},</if> + <if test="dictType != null and dictType != ''">dict_type = #{dictType},</if> + <if test="status != null">status = #{status},</if> + <if test="remark != null">remark = #{remark},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + update_time = sysdate() + </set> + where dict_id = #{dictId} + </update> + + <insert id="insertDictType" parameterType="SysDictType"> + insert into sys_dict_type( + <if test="dictName != null and dictName != ''">dict_name,</if> + <if test="dictType != null and dictType != ''">dict_type,</if> + <if test="status != null">status,</if> + <if test="remark != null and remark != ''">remark,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + create_time + )values( + <if test="dictName != null and dictName != ''">#{dictName},</if> + <if test="dictType != null and dictType != ''">#{dictType},</if> + <if test="status != null">#{status},</if> + <if test="remark != null and remark != ''">#{remark},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + sysdate() + ) + </insert> + +</mapper> \ No newline at end of file diff --git a/ruoyi/src/main/resources/mybatis/system/SysMenuMapper.xml b/ruoyi/src/main/resources/mybatis/system/SysMenuMapper.xml new file mode 100644 index 0000000..d233602 --- /dev/null +++ b/ruoyi/src/main/resources/mybatis/system/SysMenuMapper.xml @@ -0,0 +1,158 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper + PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.project.system.mapper.SysMenuMapper"> + + <resultMap type="SysMenu" id="SysMenuResult"> + <id property="menuId" column="menu_id" /> + <result property="menuName" column="menu_name" /> + <result property="parentName" column="parent_name" /> + <result property="parentId" column="parent_id" /> + <result property="orderNum" column="order_num" /> + <result property="path" column="path" /> + <result property="component" column="component" /> + <result property="isFrame" column="is_frame" /> + <result property="menuType" column="menu_type" /> + <result property="visible" column="visible" /> + <result property="perms" column="perms" /> + <result property="icon" column="icon" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateTime" column="update_time" /> + <result property="updateBy" column="update_by" /> + <result property="remark" column="remark" /> + </resultMap> + + <sql id="selectMenuVo"> + select menu_id, menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, ifnull(perms,'') as perms, icon, create_time + from sys_menu + </sql> + + <select id="selectMenuList" parameterType="SysMenu" resultMap="SysMenuResult"> + <include refid="selectMenuVo"/> + <where> + <if test="menuName != null and menuName != ''"> + AND menu_name like concat('%', #{menuName}, '%') + </if> + <if test="visible != null and visible != ''"> + AND visible = #{visible} + </if> + </where> + order by parent_id, order_num + </select> + + <select id="selectMenuTreeAll" resultMap="SysMenuResult"> + select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.visible, ifnull(m.perms,'') as perms, m.is_frame, m.menu_type, m.icon, m.order_num, m.create_time + from sys_menu m where m.menu_type in ('M', 'C') and m.visible = 0 + order by m.parent_id, m.order_num + </select> + + <select id="selectMenuTreeByUserId" parameterType="Long" resultMap="SysMenuResult"> + select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.visible, ifnull(m.perms,'') as perms, m.is_frame, m.menu_type, m.icon, m.order_num, m.create_time + from sys_menu m + left join sys_role_menu rm on m.menu_id = rm.menu_id + left join sys_user_role ur on rm.role_id = ur.role_id + left join sys_role ro on ur.role_id = ro.role_id + left join sys_user u on ur.user_id = u.user_id + where u.user_id = #{userId} and m.menu_type in ('M', 'C') and m.visible = 0 AND ro.status = 0 + order by m.parent_id, m.order_num + </select> + + <select id="selectMenuListByRoleId" parameterType="Long" resultType="Integer"> + select m.menu_id, m.parent_id + from sys_menu m + left join sys_role_menu rm on m.menu_id = rm.menu_id + where rm.role_id = #{roleId} + and m.menu_id not in (select m.parent_id from sys_menu m inner join sys_role_menu rm on m.menu_id = rm.menu_id and rm.role_id = #{roleId}) + order by m.parent_id, m.order_num + </select> + + <select id="selectMenuPerms" resultType="String"> + select distinct m.perms + from sys_menu m + left join sys_role_menu rm on m.menu_id = rm.menu_id + left join sys_user_role ur on rm.role_id = ur.role_id + </select> + + <select id="selectMenuPermsByUserId" 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 + left join sys_user_role ur on rm.role_id = ur.role_id + where ur.user_id = #{userId} + </select> + + <select id="selectMenuById" parameterType="Long" resultMap="SysMenuResult"> + <include refid="selectMenuVo"/> + where menu_id = #{menuId} + </select> + + <select id="hasChildByMenuId" resultType="Integer"> + select count(1) from sys_menu where parent_id = #{menuId} + </select> + + <select id="checkMenuNameUnique" parameterType="SysMenu" resultMap="SysMenuResult"> + <include refid="selectMenuVo"/> + where menu_name=#{menuName} and parent_id = #{parentId} + </select> + + <update id="updateMenu" parameterType="SysMenu"> + update sys_menu + <set> + <if test="menuName != null and menuName != ''">menu_name = #{menuName},</if> + <if test="parentId != null and parentId != 0">parent_id = #{parentId},</if> + <if test="orderNum != null and orderNum != ''">order_num = #{orderNum},</if> + <if test="path != null and path != ''">path = #{path},</if> + <if test="component != null and component != ''">component = #{component},</if> + <if test="isFrame != null and isFrame != ''">is_frame = #{isFrame},</if> + <if test="menuType != null and menuType != ''">menu_type = #{menuType},</if> + <if test="visible != null">visible = #{visible},</if> + <if test="perms !=null">perms = #{perms},</if> + <if test="icon !=null and icon != ''">icon = #{icon},</if> + <if test="remark != null and remark != ''">remark = #{remark},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + update_time = sysdate() + </set> + where menu_id = #{menuId} + </update> + + <insert id="insertMenu" parameterType="SysMenu"> + insert into sys_menu( + <if test="menuId != null and menuId != 0">menu_id,</if> + <if test="parentId != null and parentId != 0">parent_id,</if> + <if test="menuName != null and menuName != ''">menu_name,</if> + <if test="orderNum != null and orderNum != ''">order_num,</if> + <if test="path != null and path != ''">path,</if> + <if test="component != null and component != ''">component,</if> + <if test="isFrame != null and isFrame != ''">is_frame,</if> + <if test="menuType != null and menuType != ''">menu_type,</if> + <if test="visible != null">visible,</if> + <if test="perms !=null and perms != ''">perms,</if> + <if test="icon != null and icon != ''">icon,</if> + <if test="remark != null and remark != ''">remark,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + create_time + )values( + <if test="menuId != null and menuId != 0">#{menuId},</if> + <if test="parentId != null and parentId != 0">#{parentId},</if> + <if test="menuName != null and menuName != ''">#{menuName},</if> + <if test="orderNum != null and orderNum != ''">#{orderNum},</if> + <if test="path != null and path != ''">#{path},</if> + <if test="component != null and component != ''">#{component},</if> + <if test="isFrame != null and isFrame != ''">#{isFrame},</if> + <if test="menuType != null and menuType != ''">#{menuType},</if> + <if test="visible != null">#{visible},</if> + <if test="perms !=null and perms != ''">#{perms},</if> + <if test="icon != null and icon != ''">#{icon},</if> + <if test="remark != null and remark != ''">#{remark},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + sysdate() + ) + </insert> + + <delete id="deleteMenuById" parameterType="Long"> + delete from sys_menu where menu_id = #{menuId} + </delete> + +</mapper> \ No newline at end of file diff --git a/ruoyi/src/main/resources/mybatis/system/SysNoticeMapper.xml b/ruoyi/src/main/resources/mybatis/system/SysNoticeMapper.xml new file mode 100644 index 0000000..952e92f --- /dev/null +++ b/ruoyi/src/main/resources/mybatis/system/SysNoticeMapper.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.project.system.mapper.SysNoticeMapper"> + + <resultMap type="SysNotice" id="SysNoticeResult"> + <result property="noticeId" column="notice_id" /> + <result property="noticeTitle" column="notice_title" /> + <result property="noticeType" column="notice_type" /> + <result property="noticeContent" column="notice_content" /> + <result property="status" column="status" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + <result property="remark" column="remark" /> + </resultMap> + + <sql id="selectNoticeVo"> + select notice_id, notice_title, notice_type, notice_content, status, create_by, create_time, update_by, update_time, remark + from sys_notice + </sql> + + <select id="selectNoticeById" parameterType="Long" resultMap="SysNoticeResult"> + <include refid="selectNoticeVo"/> + where notice_id = #{noticeId} + </select> + + <select id="selectNoticeList" parameterType="SysNotice" resultMap="SysNoticeResult"> + <include refid="selectNoticeVo"/> + <where> + <if test="noticeTitle != null and noticeTitle != ''"> + AND notice_title like concat('%', #{noticeTitle}, '%') + </if> + <if test="noticeType != null and noticeType != ''"> + AND notice_type = #{noticeType} + </if> + <if test="createBy != null and createBy != ''"> + AND create_by like concat('%', #{createBy}, '%') + </if> + </where> + </select> + + <insert id="insertNotice" parameterType="SysNotice"> + insert into sys_notice ( + <if test="noticeTitle != null and noticeTitle != '' ">notice_title, </if> + <if test="noticeType != null and noticeType != '' ">notice_type, </if> + <if test="noticeContent != null and noticeContent != '' ">notice_content, </if> + <if test="status != null and status != '' ">status, </if> + <if test="remark != null and remark != ''">remark,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + create_time + )values( + <if test="noticeTitle != null and noticeTitle != ''">#{noticeTitle}, </if> + <if test="noticeType != null and noticeType != ''">#{noticeType}, </if> + <if test="noticeContent != null and noticeContent != ''">#{noticeContent}, </if> + <if test="status != null and status != ''">#{status}, </if> + <if test="remark != null and remark != ''">#{remark},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + sysdate() + ) + </insert> + + <update id="updateNotice" parameterType="SysNotice"> + update sys_notice + <set> + <if test="noticeTitle != null and noticeTitle != ''">notice_title = #{noticeTitle}, </if> + <if test="noticeType != null and noticeType != ''">notice_type = #{noticeType}, </if> + <if test="noticeContent != null">notice_content = #{noticeContent}, </if> + <if test="status != null and status != ''">status = #{status}, </if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + update_time = sysdate() + </set> + where notice_id = #{noticeId} + </update> + + <delete id="deleteNoticeById" parameterType="Long"> + delete from sys_notice where notice_id = #{noticeId} + </delete> + +</mapper> \ No newline at end of file diff --git a/ruoyi/src/main/resources/mybatis/system/SysPostMapper.xml b/ruoyi/src/main/resources/mybatis/system/SysPostMapper.xml new file mode 100644 index 0000000..232136c --- /dev/null +++ b/ruoyi/src/main/resources/mybatis/system/SysPostMapper.xml @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.project.system.mapper.SysPostMapper"> + + <resultMap type="SysPost" id="SysPostResult"> + <id property="postId" column="post_id" /> + <result property="postCode" column="post_code" /> + <result property="postName" column="post_name" /> + <result property="postSort" column="post_sort" /> + <result property="status" column="status" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + <result property="remark" column="remark" /> + </resultMap> + + <sql id="selectPostVo"> + select post_id, post_code, post_name, post_sort, status, create_by, create_time, remark + from sys_post + </sql> + + <select id="selectPostList" parameterType="SysPost" resultMap="SysPostResult"> + <include refid="selectPostVo"/> + <where> + <if test="postCode != null and postCode != ''"> + AND post_code like concat('%', #{postCode}, '%') + </if> + <if test="status != null and status != ''"> + AND status = #{status} + </if> + <if test="postName != null and postName != ''"> + AND post_name like concat('%', #{postName}, '%') + </if> + </where> + </select> + + <select id="selectPostAll" resultMap="SysPostResult"> + <include refid="selectPostVo"/> + </select> + + <select id="selectPostById" parameterType="Long" resultMap="SysPostResult"> + <include refid="selectPostVo"/> + where post_id = #{postId} + </select> + + <select id="selectPostListByUserId" parameterType="Long" resultType="Integer"> + select p.post_id + from sys_post p + left join sys_user_post up on up.post_id = p.post_id + left join sys_user u on u.user_id = up.user_id + where u.user_id = #{userId} + </select> + + <select id="selectPostsByUserName" parameterType="String" resultMap="SysPostResult"> + select p.post_id, p.post_name, p.post_code + from sys_post p + left join sys_user_post up on up.post_id = p.post_id + left join sys_user u on u.user_id = up.user_id + where u.user_name = #{userName} + </select> + + <select id="checkPostNameUnique" parameterType="String" resultMap="SysPostResult"> + <include refid="selectPostVo"/> + where post_name=#{postName} + </select> + + <select id="checkPostCodeUnique" parameterType="String" resultMap="SysPostResult"> + <include refid="selectPostVo"/> + where post_code=#{postCode} + </select> + + <update id="updatePost" parameterType="SysPost"> + update sys_post + <set> + <if test="postCode != null and postCode != ''">post_code = #{postCode},</if> + <if test="postName != null and postName != ''">post_name = #{postName},</if> + <if test="postSort != null and postSort != ''">post_sort = #{postSort},</if> + <if test="status != null and status != ''">status = #{status},</if> + <if test="remark != null">remark = #{remark},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + update_time = sysdate() + </set> + where post_id = #{postId} + </update> + + <insert id="insertPost" parameterType="SysPost" useGeneratedKeys="true" keyProperty="postId"> + insert into sys_post( + <if test="postId != null and postId != 0">post_id,</if> + <if test="postCode != null and postCode != ''">post_code,</if> + <if test="postName != null and postName != ''">post_name,</if> + <if test="postSort != null and postSort != ''">post_sort,</if> + <if test="status != null and status != ''">status,</if> + <if test="remark != null and remark != ''">remark,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + create_time + )values( + <if test="postId != null and postId != 0">#{postId},</if> + <if test="postCode != null and postCode != ''">#{postCode},</if> + <if test="postName != null and postName != ''">#{postName},</if> + <if test="postSort != null and postSort != ''">#{postSort},</if> + <if test="status != null and status != ''">#{status},</if> + <if test="remark != null and remark != ''">#{remark},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + sysdate() + ) + </insert> + + <delete id="deletePostById" parameterType="Long"> + delete from sys_post where post_id = #{postId} + </delete> + +</mapper> \ No newline at end of file diff --git a/ruoyi/src/main/resources/mybatis/system/SysRoleDeptMapper.xml b/ruoyi/src/main/resources/mybatis/system/SysRoleDeptMapper.xml new file mode 100644 index 0000000..acda245 --- /dev/null +++ b/ruoyi/src/main/resources/mybatis/system/SysRoleDeptMapper.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.project.system.mapper.SysRoleDeptMapper"> + + <resultMap type="SysRoleDept" id="SysRoleDeptResult"> + <result property="roleId" column="role_id" /> + <result property="deptId" column="dept_id" /> + </resultMap> + + <delete id="deleteRoleDeptByRoleId" parameterType="Long"> + delete from sys_role_dept where role_id=#{roleId} + </delete> + + <select id="selectCountRoleDeptByDeptId" resultType="Integer"> + select count(1) from sys_role_dept where dept_id=#{deptId} + </select> + + <delete id="deleteRoleDept" parameterType="Long"> + delete from sys_role_dept where role_id in + <foreach collection="array" item="roleId" open="(" separator="," close=")"> + #{roleId} + </foreach> + </delete> + + <insert id="batchRoleDept"> + insert into sys_role_dept(role_id, dept_id) values + <foreach item="item" index="index" collection="list" separator=","> + (#{item.roleId},#{item.deptId}) + </foreach> + </insert> + +</mapper> \ No newline at end of file diff --git a/ruoyi/src/main/resources/mybatis/system/SysRoleMapper.xml b/ruoyi/src/main/resources/mybatis/system/SysRoleMapper.xml new file mode 100644 index 0000000..4d62760 --- /dev/null +++ b/ruoyi/src/main/resources/mybatis/system/SysRoleMapper.xml @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.project.system.mapper.SysRoleMapper"> + + <resultMap type="SysRole" id="SysRoleResult"> + <id property="roleId" column="role_id" /> + <result property="roleName" column="role_name" /> + <result property="roleKey" column="role_key" /> + <result property="roleSort" column="role_sort" /> + <result property="dataScope" column="data_scope" /> + <result property="status" column="status" /> + <result property="delFlag" column="del_flag" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + <result property="remark" column="remark" /> + </resultMap> + + <sql id="selectRoleVo"> + select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, + r.status, r.del_flag, r.create_time, r.remark + from sys_role r + left join sys_user_role ur on ur.role_id = r.role_id + left join sys_user u on u.user_id = ur.user_id + left join sys_dept d on u.dept_id = d.dept_id + </sql> + + <select id="selectRoleList" parameterType="SysRole" resultMap="SysRoleResult"> + <include refid="selectRoleVo"/> + where r.del_flag = '0' + <if test="roleName != null and roleName != ''"> + AND r.role_name like concat('%', #{roleName}, '%') + </if> + <if test="status != null and status != ''"> + AND r.status = #{status} + </if> + <if test="roleKey != null and roleKey != ''"> + AND r.role_key like concat('%', #{roleKey}, '%') + </if> + <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 --> + and date_format(r.create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d') + </if> + <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 --> + and date_format(r.create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') + </if> + <!-- 数据范围过滤 --> + ${dataScope} + </select> + + <select id="selectRolePermissionByUserId" parameterType="Long" resultMap="SysRoleResult"> + <include refid="selectRoleVo"/> + WHERE r.del_flag = '0' and ur.user_id = #{userId} + </select> + + <select id="selectRoleAll" resultMap="SysRoleResult"> + <include refid="selectRoleVo"/> + </select> + + <select id="selectRoleListByUserId" parameterType="Long" resultType="Integer"> + select r.role_id + from sys_role r + left join sys_user_role ur on ur.role_id = r.role_id + left join sys_user u on u.user_id = ur.user_id + where u.user_id = #{userId} + </select> + + <select id="selectRoleById" parameterType="Long" resultMap="SysRoleResult"> + <include refid="selectRoleVo"/> + where r.role_id = #{roleId} + </select> + + <select id="selectRolesByUserName" parameterType="String" resultMap="SysRoleResult"> + <include refid="selectRoleVo"/> + WHERE r.del_flag = '0' and u.user_name = #{userName} + </select> + + <select id="checkRoleNameUnique" parameterType="String" resultMap="SysRoleResult"> + <include refid="selectRoleVo"/> + where r.role_name=#{roleName} + </select> + + <select id="checkRoleKeyUnique" parameterType="String" resultMap="SysRoleResult"> + <include refid="selectRoleVo"/> + where r.role_key=#{roleKey} + </select> + + <insert id="insertRole" parameterType="SysRole" useGeneratedKeys="true" keyProperty="roleId"> + insert into sys_role( + <if test="roleId != null and roleId != 0">role_id,</if> + <if test="roleName != null and roleName != ''">role_name,</if> + <if test="roleKey != null and roleKey != ''">role_key,</if> + <if test="roleSort != null and roleSort != ''">role_sort,</if> + <if test="dataScope != null and dataScope != ''">data_scope,</if> + <if test="status != null and status != ''">status,</if> + <if test="remark != null and remark != ''">remark,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + create_time + )values( + <if test="roleId != null and roleId != 0">#{roleId},</if> + <if test="roleName != null and roleName != ''">#{roleName},</if> + <if test="roleKey != null and roleKey != ''">#{roleKey},</if> + <if test="roleSort != null and roleSort != ''">#{roleSort},</if> + <if test="dataScope != null and dataScope != ''">#{dataScope},</if> + <if test="status != null and status != ''">#{status},</if> + <if test="remark != null and remark != ''">#{remark},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + sysdate() + ) + </insert> + + <update id="updateRole" parameterType="SysRole"> + update sys_role + <set> + <if test="roleName != null and roleName != ''">role_name = #{roleName},</if> + <if test="roleKey != null and roleKey != ''">role_key = #{roleKey},</if> + <if test="roleSort != null and roleSort != ''">role_sort = #{roleSort},</if> + <if test="dataScope != null and dataScope != ''">data_scope = #{dataScope},</if> + <if test="status != null and status != ''">status = #{status},</if> + <if test="remark != null">remark = #{remark},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + update_time = sysdate() + </set> + where role_id = #{roleId} + </update> + + <update id="updateRoleStatus" parameterType="SysRole"> + update sys_user set status = #{status} where user_id = #{userId} + </update> + + <delete id="deleteRoleById" parameterType="Long"> + delete from sys_role where role_id = #{roleId} + </delete> + +</mapper> \ No newline at end of file diff --git a/ruoyi/src/main/resources/mybatis/system/SysRoleMenuMapper.xml b/ruoyi/src/main/resources/mybatis/system/SysRoleMenuMapper.xml new file mode 100644 index 0000000..b7c5b6b --- /dev/null +++ b/ruoyi/src/main/resources/mybatis/system/SysRoleMenuMapper.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.project.system.mapper.SysRoleMenuMapper"> + + <resultMap type="SysRoleMenu" id="SysRoleMenuResult"> + <result property="roleId" column="role_id" /> + <result property="menuId" column="menu_id" /> + </resultMap> + + <select id="checkMenuExistRole" resultType="Integer"> + select count(1) from sys_role_menu where menu_id = #{menuId} + </select> + + <delete id="deleteRoleMenuByRoleId" parameterType="Long"> + delete from sys_role_menu where role_id=#{roleId} + </delete> + + <insert id="batchRoleMenu"> + insert into sys_role_menu(role_id, menu_id) values + <foreach item="item" index="index" collection="list" separator=","> + (#{item.roleId},#{item.menuId}) + </foreach> + </insert> + +</mapper> \ No newline at end of file diff --git a/ruoyi/src/main/resources/mybatis/system/SysUserMapper.xml b/ruoyi/src/main/resources/mybatis/system/SysUserMapper.xml new file mode 100644 index 0000000..eaf27a0 --- /dev/null +++ b/ruoyi/src/main/resources/mybatis/system/SysUserMapper.xml @@ -0,0 +1,174 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.project.system.mapper.SysUserMapper"> + + <resultMap type="SysUser" id="SysUserResult"> + <id property="userId" column="user_id" /> + <result property="deptId" column="dept_id" /> + <result property="userName" column="user_name" /> + <result property="nickName" column="nick_name" /> + <result property="email" column="email" /> + <result property="phonenumber" column="phonenumber" /> + <result property="sex" column="sex" /> + <result property="avatar" column="avatar" /> + <result property="password" column="password" /> + <result property="status" column="status" /> + <result property="delFlag" column="del_flag" /> + <result property="loginIp" column="login_ip" /> + <result property="loginDate" column="login_date" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + <result property="remark" column="remark" /> + <association property="dept" column="dept_id" javaType="SysDept" resultMap="deptResult" /> + <collection property="roles" javaType="java.util.List" resultMap="RoleResult" /> + </resultMap> + + <resultMap id="deptResult" type="SysDept"> + <id property="deptId" column="dept_id" /> + <result property="parentId" column="parent_id" /> + <result property="deptName" column="dept_name" /> + <result property="orderNum" column="order_num" /> + <result property="leader" column="leader" /> + <result property="status" column="dept_status" /> + </resultMap> + + <resultMap id="RoleResult" type="SysRole"> + <id property="roleId" column="role_id" /> + <result property="roleName" column="role_name" /> + <result property="roleKey" column="role_key" /> + <result property="roleSort" column="role_sort" /> + <result property="dataScope" column="data_scope" /> + <result property="status" column="role_status" /> + </resultMap> + + <sql id="selectUserVo"> + select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, + d.dept_id, d.parent_id, d.dept_name, d.order_num, d.leader, d.status as dept_status, + r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status + 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 + </sql> + + <select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult"> + <include refid="selectUserVo"/> + where u.del_flag = '0' + <if test="userName != null and userName != ''"> + AND u.user_name like concat('%', #{userName}, '%') + </if> + <if test="status != null and status != ''"> + AND u.status = #{status} + </if> + <if test="phonenumber != null and phonenumber != ''"> + AND u.phonenumber like concat('%', #{phonenumber}, '%') + </if> + <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 --> + AND date_format(u.create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d') + </if> + <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 --> + AND date_format(u.create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') + </if> + <if test="deptId != null and deptId != 0"> + AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE FIND_IN_SET (#{deptId},ancestors) )) + </if> + <!-- 数据范围过滤 --> + ${dataScope} + </select> + + <select id="selectUserByUserName" parameterType="String" resultMap="SysUserResult"> + <include refid="selectUserVo"/> + where u.user_name = #{userName} + </select> + + <select id="selectUserById" parameterType="Long" resultMap="SysUserResult"> + <include refid="selectUserVo"/> + where u.user_id = #{userId} + </select> + + <select id="checkUserNameUnique" parameterType="String" resultType="int"> + select count(1) from sys_user where user_name = #{userName} + </select> + + <select id="checkPhoneUnique" parameterType="String" resultMap="SysUserResult"> + select user_id, phonenumber from sys_user where phonenumber = #{phonenumber} + </select> + + <select id="checkEmailUnique" parameterType="String" resultMap="SysUserResult"> + select user_id, email from sys_user where email = #{email} + </select> + + <insert id="insertUser" parameterType="SysUser" useGeneratedKeys="true" keyProperty="userId"> + insert into sys_user( + <if test="userId != null and userId != 0">user_id,</if> + <if test="deptId != null and deptId != 0">dept_id,</if> + <if test="userName != null and userName != ''">user_name,</if> + <if test="nickName != null and nickName != ''">nick_name,</if> + <if test="email != null and email != ''">email,</if> + <if test="avatar != null and avatar != ''">avatar,</if> + <if test="phonenumber != null and phonenumber != ''">phonenumber,</if> + <if test="sex != null and sex != ''">sex,</if> + <if test="password != null and password != ''">password,</if> + <if test="status != null and status != ''">status,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + <if test="remark != null and remark != ''">remark,</if> + create_time + )values( + <if test="userId != null and userId != ''">#{userId},</if> + <if test="deptId != null and deptId != ''">#{deptId},</if> + <if test="userName != null and userName != ''">#{userName},</if> + <if test="nickName != null and nickName != ''">#{nickName},</if> + <if test="email != null and email != ''">#{email},</if> + <if test="avatar != null and avatar != ''">#{avatar},</if> + <if test="phonenumber != null and phonenumber != ''">#{phonenumber},</if> + <if test="sex != null and sex != ''">#{sex},</if> + <if test="password != null and password != ''">#{password},</if> + <if test="status != null and status != ''">#{status},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + <if test="remark != null and remark != ''">#{remark},</if> + sysdate() + ) + </insert> + + <update id="updateUser" parameterType="SysUser"> + update sys_user + <set> + <if test="deptId != null and deptId != 0">dept_id = #{deptId},</if> + <if test="userName != null and userName != ''">user_name = #{userName},</if> + <if test="nickName != null and nickName != ''">nick_name = #{nickName},</if> + <if test="email != null and email != ''">email = #{email},</if> + <if test="phonenumber != null and phonenumber != ''">phonenumber = #{phonenumber},</if> + <if test="sex != null and sex != ''">sex = #{sex},</if> + <if test="avatar != null and avatar != ''">avatar = #{avatar},</if> + <if test="password != null and password != ''">password = #{password},</if> + <if test="status != null and status != ''">status = #{status},</if> + <if test="loginIp != null and loginIp != ''">login_ip = #{loginIp},</if> + <if test="loginDate != null">login_date = #{loginDate},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + <if test="remark != null">remark = #{remark},</if> + update_time = sysdate() + </set> + where user_id = #{userId} + </update> + + <update id="updateUserStatus" parameterType="SysUser"> + update sys_user set status = #{status} where user_id = #{userId} + </update> + + <update id="updateUserAvatar" parameterType="SysUser"> + update sys_user set avatar = #{avatar} where user_name = #{userName} + </update> + + <update id="resetUserPwd" parameterType="SysUser"> + update sys_user set password = #{password} where user_name = #{userName} + </update> + + <delete id="deleteUserById" parameterType="Long"> + delete from sys_user where user_id = #{userId} + </delete> + +</mapper> \ No newline at end of file diff --git a/ruoyi/src/main/resources/mybatis/system/SysUserPostMapper.xml b/ruoyi/src/main/resources/mybatis/system/SysUserPostMapper.xml new file mode 100644 index 0000000..d634c8b --- /dev/null +++ b/ruoyi/src/main/resources/mybatis/system/SysUserPostMapper.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.project.system.mapper.SysUserPostMapper"> + + <resultMap type="SysUserPost" id="SysUserPostResult"> + <result property="userId" column="user_id" /> + <result property="postId" column="post_id" /> + </resultMap> + + <delete id="deleteUserPostByUserId" parameterType="Long"> + delete from sys_user_post where user_id=#{userId} + </delete> + + <select id="countUserPostById" resultType="Integer"> + select count(1) from sys_user_post where post_id=#{postId} + </select> + + <delete id="deleteUserPost" parameterType="Long"> + delete from sys_user_post where user_id in + <foreach collection="array" item="userId" open="(" separator="," close=")"> + #{userId} + </foreach> + </delete> + + <insert id="batchUserPost"> + insert into sys_user_post(user_id, post_id) values + <foreach item="item" index="index" collection="list" separator=","> + (#{item.userId},#{item.postId}) + </foreach> + </insert> + +</mapper> \ No newline at end of file diff --git a/ruoyi/src/main/resources/mybatis/system/SysUserRoleMapper.xml b/ruoyi/src/main/resources/mybatis/system/SysUserRoleMapper.xml new file mode 100644 index 0000000..3422b1c --- /dev/null +++ b/ruoyi/src/main/resources/mybatis/system/SysUserRoleMapper.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.project.system.mapper.SysUserRoleMapper"> + + <resultMap type="SysUserRole" id="SysUserRoleResult"> + <result property="userId" column="user_id" /> + <result property="roleId" column="role_id" /> + </resultMap> + + <delete id="deleteUserRoleByUserId" parameterType="Long"> + delete from sys_user_role where user_id=#{userId} + </delete> + + <select id="countUserRoleByRoleId" resultType="Integer"> + select count(1) from sys_user_role where role_id=#{roleId} + </select> + + <delete id="deleteUserRole" parameterType="Long"> + delete from sys_user_role where user_id in + <foreach collection="array" item="userId" open="(" separator="," close=")"> + #{userId} + </foreach> + </delete> + + <insert id="batchUserRole"> + insert into sys_user_role(user_id, role_id) values + <foreach item="item" index="index" collection="list" separator=","> + (#{item.userId},#{item.roleId}) + </foreach> + </insert> + + <delete id="deleteUserRoleInfo" parameterType="SysUserRole"> + delete from sys_user_role where user_id=#{userId} and role_id=#{roleId} + </delete> + + <delete id="deleteUserRoleInfos"> + delete from sys_user_role where role_id=#{roleId} and user_id in + <foreach collection="userIds" item="userId" open="(" separator="," close=")"> + #{userId} + </foreach> + </delete> +</mapper> \ No newline at end of file -- Gitblit v1.9.2